cSvn-UI for SVN Repositories

cGit-UI – is a web interface for Subversion (SVN) Repositories. cSvn CGI script is writen in C and therefore it's fast enough

6 Commits   0 Branches   2 Tags
author: kx <kx@radix.pro> 2023-03-24 03:55:33 +0300 committer: kx <kx@radix.pro> 2023-03-24 03:55:33 +0300 commit: bfc1508d26c89c9a36d2d9a827fe2c4ed128884d parent: c836ae3775cf72f17e0b7e3792d156fdb389bee3
Commit Summary:
Version 0.1.4
Diffstat:
1 file changed, 255 insertions, 0 deletions
diff --git a/csvncgi/wrapper.c b/csvncgi/wrapper.c
new file mode 100644
index 0000000..60c184a
--- /dev/null
+++ b/csvncgi/wrapper.c
@@ -0,0 +1,305 @@
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/sysinfo.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <dirent.h>
+#include <sys/stat.h> /* chmod(2)    */
+#include <sys/file.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <string.h>   /* strdup(3)   */
+#include <libgen.h>   /* basename(3) */
+#include <ctype.h>    /* tolower(3)  */
+#include <errno.h>
+#include <time.h>
+#include <sys/time.h>
+#include <pwd.h>
+#include <grp.h>
+#include <stdarg.h>
+#include <poll.h>
+#include <unistd.h>
+
+#include <defs.h>
+#include <wrapper.h>
+
+
+#define WRAPPER_ERRMSG_SIZE 4096
+
+void wrapper_error( const char *fmt, ... )
+{
+  va_list arg_ptr;
+  char  buf[WRAPPER_ERRMSG_SIZE];
+  char  msg[WRAPPER_ERRMSG_SIZE];
+  char *format = "%s: %s\n";
+
+  va_start( arg_ptr, fmt );
+
+  vsnprintf( msg, WRAPPER_ERRMSG_SIZE, (const void *)fmt, arg_ptr );
+
+  va_end( arg_ptr ); /* Reset variable arguments. */
+
+  snprintf( buf, WRAPPER_ERRMSG_SIZE, format, "wrapper", msg );
+
+  (void)write( STDERR_FILENO, buf, strlen( buf ) );
+
+  exit( 1 );
+}
+
+wrapper_errfunc wrapper_fatal = wrapper_error;
+
+
+char *xstrdup( const char *str )
+{
+  char *ret;
+
+  ret = strdup( str );
+  if( !ret )
+    wrapper_fatal( "Out of memory, strdup failed" );
+  return ret;
+}
+
+void *xmalloc( size_t size )
+{
+  void *ret;
+
+  ret = malloc( size );
+  if( !ret )
+  {
+    wrapper_fatal( "Out of memory, malloc failed (tried to allocate %lu bytes)", (unsigned long)size );
+    return NULL;
+  }
+  memset( ret, 0, size );
+  return ret;
+}
+
+void *xrealloc( void *ptr, size_t size )
+{
+  void *ret = NULL;
+
+  ret = realloc( ptr, size );
+  if( !ret && !size )
+    ret = realloc( ptr, 1 );
+  if( !ret )
+    wrapper_fatal( "Out of memory, realloc failed" );
+  return ret;
+}
+
+/************************************************************************
+  Limit size of IO chunks, because huge chunks only cause pain.
+  OS X 64-bit is buggy, returning EINVAL if len >= INT_MAX; and even in
+  the absence of bugs, large chunks can result in bad latencies when
+  you decide to kill the process.
+
+  We pick 8 MiB as our default, but if the platform defines SSIZE_MAX
+  that is smaller than that, clip it to SSIZE_MAX, as a call to read(2)
+  or write(2) larger than that is allowed to fail. As the last resort,
+  we allow a port to pass via CFLAGS e.g. "-DMAX_IO_SIZE=value" to
+  override this, if the definition of SSIZE_MAX given by the platform
+  is broken.
+ ************************************************************************/
+#ifndef MAX_IO_SIZE
+# define MAX_IO_SIZE_DEFAULT (8*1024*1024)
+# if defined(SSIZE_MAX) && (SSIZE_MAX < MAX_IO_SIZE_DEFAULT)
+#  define MAX_IO_SIZE SSIZE_MAX
+# else
+#  define MAX_IO_SIZE MAX_IO_SIZE_DEFAULT
+# endif
+#endif
+
+/*
+  xopen() is the same as open(), but it die()s if the open() fails.
+ */
+int xopen( const char *path, int oflag, ... )
+{
+  mode_t mode = 0;
+  va_list ap;
+
+  /*
+    va_arg() will have undefined behavior if the specified type is not
+    compatible with the argument type. Since integers are promoted to
+    ints, we fetch the next argument as an int, and then cast it to a
+    mode_t to avoid undefined behavior.
+   */
+  va_start( ap, oflag );
+  if( oflag & O_CREAT )
+    mode = va_arg( ap, int );
+  va_end( ap );
+
+  for( ;; )
+  {
+    int fd = open( path, oflag, mode );
+    if( fd >= 0 )
+      return fd;
+    if( errno == EINTR )
+      continue;
+
+    if( (oflag & O_RDWR) == O_RDWR )
+      wrapper_fatal( "could not open '%s' for reading and writing", path );
+    else if( (oflag & O_WRONLY) == O_WRONLY )
+      wrapper_fatal( "could not open '%s' for writing", path );
+    else
+      wrapper_fatal( "could not open '%s' for reading", path );
+  }
+}
+
+static int handle_nonblock( int fd, short poll_events, int err )
+{
+  struct pollfd pfd;
+
+  if( err != EAGAIN && err != EWOULDBLOCK )
+    return 0;
+
+  pfd.fd = fd;
+  pfd.events = poll_events;
+
+  /*
+    no need to check for errors, here;
+    a subsequent read/write will detect unrecoverable errors
+   */
+  poll( &pfd, 1, -1 );
+  return 1;
+}
+
+/*
+  xread() is the same a read(), but it automatically restarts read()
+  operations with a recoverable error (EAGAIN and EINTR). xread()
+  DOES NOT GUARANTEE that "len" bytes is read even if the data is available.
+ */
+ssize_t xread(int fd, void *buf, size_t len)
+{
+  ssize_t nr;
+
+  if( len > MAX_IO_SIZE )
+    len = MAX_IO_SIZE;
+
+  while( 1 )
+  {
+    nr = read( fd, buf, len );
+    if( nr < 0 )
+    {
+      if( errno == EINTR )
+        continue;
+      if( handle_nonblock(fd, POLLIN, errno) )
+        continue;
+    }
+    return nr;
+  }
+}
+
+/*
+  xwrite() is the same a write(), but it automatically restarts write()
+  operations with a recoverable error (EAGAIN and EINTR). xwrite() DOES NOT
+  GUARANTEE that "len" bytes is written even if the operation is successful.
+ */
+ssize_t xwrite( int fd, const void *buf, size_t len )
+{
+  ssize_t nr;
+
+  if( len > MAX_IO_SIZE )
+    len = MAX_IO_SIZE;
+
+  while( 1 )
+  {
+    nr = write( fd, buf, len );
+    if( nr < 0 )
+    {
+      if( errno == EINTR )
+        continue;
+      if( handle_nonblock(fd, POLLOUT, errno) )
+        continue;
+    }
+    return nr;
+  }
+}
+
+ssize_t read_in_full( int fd, void *buf, size_t count )
+{
+  char *p = buf;
+  ssize_t total = 0;
+
+  while( count > 0 )
+  {
+    ssize_t loaded = xread( fd, p, count );
+
+    if (loaded < 0)
+      return -1;
+
+    if (loaded == 0)
+      return total;
+
+    count -= loaded;
+        p += loaded;
+    total += loaded;
+  }
+
+  return total;
+}
+
+ssize_t write_in_full( int fd, const void *buf, size_t count )
+{
+  const char *p = buf;
+  ssize_t total = 0;
+
+  while( count > 0 )
+  {
+    ssize_t written = xwrite( fd, p, count );
+    if( written < 0 )
+       return -1;
+
+    if( !written )
+    {
+      errno = ENOSPC;
+      return -1;
+    }
+    count -= written;
+        p += written;
+    total += written;
+  }
+
+  return total;
+}
+
+int xdup( int fd )
+{
+  int ret = dup( fd );
+  if( ret < 0 )
+    wrapper_fatal( "dup failed" );
+  return ret;
+}
+
+/*
+  xfopen() is the same as fopen(), but it die()s if the fopen() fails.
+ */
+FILE *xfopen( const char *path, const char *mode )
+{
+  for( ;;  )
+  {
+    FILE *fp = fopen( path, mode );
+    if (fp)
+      return fp;
+    if (errno == EINTR)
+      continue;
+
+    if( *mode && mode[1] == '+' )
+      wrapper_fatal( "could not open '%s' for reading and writing", path );
+    else if( *mode == 'w' || *mode == 'a' )
+      wrapper_fatal( "could not open '%s' for writing", path );
+    else
+      wrapper_fatal( "could not open '%s' for reading", path );
+  }
+}
+
+FILE *xfdopen( int fd, const char *mode )
+{
+  FILE *stream = fdopen( fd, mode );
+  if( stream == NULL )
+    wrapper_fatal( "Out of memory? fdopen failed" );
+  return stream;
+}