cGit-UI for Git Repositories

cGit-UI – is a web interface for Git Repositories. cGit CGI script is writen in C and therefore it's fast enough

12 Commits   0 Branches   1 Tag
     5         kx 
     5         kx #ifdef HAVE_CONFIG_H
     5         kx #include <config.h>
     5         kx #endif
     5         kx 
     5         kx #include <stdlib.h>
     5         kx #include <stdio.h>
     5         kx #include <sys/sysinfo.h>
     5         kx #include <sys/types.h>
     5         kx #include <stdint.h>
     5         kx #include <dirent.h>
     5         kx #include <sys/stat.h> /* chmod(2)    */
     5         kx #include <sys/file.h>
     5         kx #include <sys/mman.h>
     5         kx #include <fcntl.h>
     5         kx #include <limits.h>
     5         kx #include <string.h>   /* strdup(3)   */
     5         kx #include <libgen.h>   /* basename(3) */
     5         kx #include <ctype.h>    /* tolower(3)  */
     5         kx #include <errno.h>
     5         kx #include <time.h>
     5         kx #include <sys/time.h>
     5         kx #include <pwd.h>
     5         kx #include <grp.h>
     5         kx #include <stdarg.h>
     5         kx #include <unistd.h>
     5         kx 
     5         kx #define PCRE2_CODE_UNIT_WIDTH 8
     5         kx #include <pcre2.h>
     5         kx 
     5         kx #include <nls.h>
     5         kx 
     5         kx #include <defs.h>
     5         kx #include <wrapper.h>
     5         kx #include <strbuf.h>
     5         kx 
     5         kx 
     5         kx struct symbol *strbuf_envtab = (struct symbol *)0;
     5         kx 
     5         kx struct symbol *envtab_install( struct symbol **sym, const char *n, const char *v, envtab_error fatal )
     5         kx {
     5         kx   struct symbol *ep = NULL;
     5         kx   size_t len;
     5         kx 
     5         kx   if( !sym || !n || !v || !fatal )
     5         kx     return (struct symbol *)NULL;
     5         kx 
     5         kx   ep = (struct symbol *)malloc( sizeof(struct symbol) );
     5         kx   if( !ep )
     5         kx   {
     5         kx     fatal( "cannot allocate memory for symbol '%s'", n );
     5         kx   }
     5         kx   bzero( (void *)ep, sizeof(struct symbol) );
     5         kx 
     5         kx   len = strlen( n ) + 1;
     5         kx   ep->name = (char *)malloc( len );
     5         kx   if( !ep->name )
     5         kx   {
     5         kx     fatal( "cannot allocate memory for symbol name '%s'", n );
     5         kx   }
     5         kx   bzero( (void *)ep->name, len );
     5         kx   strncpy( ep->name, n, len );
     5         kx 
     5         kx   len = strlen( v ) + 1;
     5         kx   ep->value = (char *)malloc( len );
     5         kx   if( !ep->value )
     5         kx   {
     5         kx     fatal( "cannot allocate memory for symbol value '%s'", v );
     5         kx   }
     5         kx   bzero( (void *)ep->value, len );
     5         kx   strncpy( ep->value, v, len );
     5         kx 
     5         kx   ep->next = *sym;
     5         kx   *sym = ep;
     5         kx 
     5         kx   return ep;
     5         kx }
     5         kx 
     5         kx struct symbol *envtab_lookup( struct symbol **sym, const char *n )
     5         kx {
     5         kx   struct symbol *ep = NULL;
     5         kx 
     5         kx   if( !sym || !n )
     5         kx     return (struct symbol *)NULL;
     5         kx 
     5         kx   for( ep = *sym; ep != (struct symbol *)0; ep = ep->next )
     5         kx       if( strcmp( ep->name, n ) == 0 ) return( ep );
     5         kx 
     5         kx   return (struct symbol *)NULL;
     5         kx }
     5         kx 
     5         kx void envtab_release( struct symbol **sym )
     5         kx {
     5         kx   struct symbol *tab = *sym;
     5         kx 
     5         kx   while( tab )
     5         kx   {
     5         kx     struct symbol *ep = tab;
     5         kx     tab = ep->next;
     5         kx     if( ep->name )  free( ep->name );
     5         kx     if( ep->value ) free( ep->value );
     5         kx     free( ep );
     5         kx   }
     5         kx   *sym = (struct symbol *)NULL;
     5         kx }
     5         kx 
     5         kx 
     5         kx #define STRBUF_ERRMSG_SIZE 4096
     5         kx 
     5         kx void strbuf_fatal( const char *fmt, ... )
     5         kx {
     5         kx   va_list arg_ptr;
     5         kx   char  buf[STRBUF_ERRMSG_SIZE];
     5         kx   char  msg[STRBUF_ERRMSG_SIZE];
     5         kx   char *format = "%s: %s\n";
     5         kx 
     5         kx   va_start( arg_ptr, fmt );
     5         kx 
     5         kx   vsnprintf( msg, STRBUF_ERRMSG_SIZE, (const void *)fmt, arg_ptr );
     5         kx 
     5         kx   va_end( arg_ptr ); /* Reset variable arguments. */
     5         kx 
     5         kx   snprintf( buf, STRBUF_ERRMSG_SIZE, format, "strbuf", msg );
     5         kx 
     5         kx   (void)write( STDERR_FILENO, buf, strlen( buf ) );
     5         kx 
     5         kx   exit( 1 );
     5         kx }
     5         kx 
     5         kx char strbuf_slopbuf[1];
     5         kx 
     5         kx #define alloc_nz(x) (((x)+16)*3/2)
     5         kx 
     5         kx static void alloc_grow( struct strbuf *sb, size_t size )
     5         kx {
     5         kx   if( size > sb->alloc )
     5         kx   {
     5         kx     if( alloc_nz(sb->alloc) < (size) )
     5         kx       sb->alloc = size;
     5         kx     else
     5         kx       sb->alloc = alloc_nz(sb->alloc);
     5         kx 
     5         kx     sb->buf = xrealloc( (void *)sb->buf, sb->alloc );
     5         kx     memset( (void *)(sb->buf + sb->len), 0, (size_t)(sb->alloc - sb->len) );
     5         kx   }
     5         kx }
     5         kx 
     5         kx void strbuf_grow( struct strbuf *sb, size_t extra )
     5         kx {
     5         kx   int new_buf = !sb->alloc;
     5         kx 
     5         kx   if( new_buf )
     5         kx     sb->buf = NULL;
     5         kx 
     5         kx   alloc_grow( sb, sb->len + extra + 1 );
     5         kx 
     5         kx   if( new_buf )
     5         kx     sb->buf[0] = '\0';
     5         kx }
     5         kx 
     5         kx /*
     5         kx    Функцию strbuf_init() надо вызывать в том случае, когда необходимо
     5         kx    задать внешнюю функцию обработки фатальной ошибки. Функция обработки
     5         kx    фатальной ошибки, в отличие от стандартной, может выводить как JSON
     5         kx    ответы, так и файлы 404.html, 50x.html, используя собственный буфер.
     5         kx  */
     5         kx void strbuf_init( struct strbuf *sb, strbuf_error fatal, size_t hint )
     5         kx {
     5         kx   sb->alloc = sb->len = 0;
     5         kx   sb->fatal = strbuf_fatal;
     5         kx   sb->buf   = strbuf_slopbuf;
     5         kx 
     5         kx   if( fatal )
     5         kx     sb->fatal = fatal;
     5         kx 
     5         kx   if( hint )
     5         kx     strbuf_grow( sb, hint );
     5         kx }
     5         kx 
     5         kx void strbuf_release( struct strbuf *sb )
     5         kx {
     5         kx   if( sb->alloc )
     5         kx   {
     5         kx     free( sb->buf );
     5         kx     strbuf_init( sb, (strbuf_error)0, 0 );
     5         kx   }
     5         kx }
     5         kx 
     5         kx char *strbuf_detach( struct strbuf *sb, size_t *sz )
     5         kx {
     5         kx   char *ret;
     5         kx   strbuf_grow( sb, 0 );
     5         kx   ret = sb->buf;
     5         kx   if( sz )
     5         kx     *sz = sb->len;
     5         kx   strbuf_init( sb, sb->fatal, 0 );
     5         kx   return ret;
     5         kx }
     5         kx 
     5         kx void strbuf_attach( struct strbuf *sb, void *buf, size_t len, size_t alloc )
     5         kx {
     5         kx   strbuf_release( sb );
     5         kx   sb->buf   = buf;
     5         kx   sb->len   = len;
     5         kx   sb->alloc = alloc;
     5         kx   strbuf_grow( sb, 0 );
     5         kx   sb->buf[sb->len] = '\0';
     5         kx }
     5         kx 
     5         kx void strbuf_ltrim( struct strbuf *sb )
     5         kx {
     5         kx   char *b = sb->buf;
     5         kx   while( sb->len > 0 && isspace(*b) )
     5         kx   {
     5         kx     b++;
     5         kx     sb->len--;
     5         kx   }
     5         kx   memmove( sb->buf, b, sb->len );
     5         kx   sb->buf[sb->len] = '\0';
     5         kx }
     5         kx 
     5         kx void strbuf_rtrim( struct strbuf *sb )
     5         kx {
     5         kx   while( sb->len > 0 && isspace((unsigned char)sb->buf[sb->len - 1]) )
     5         kx     sb->len--;
     5         kx   sb->buf[sb->len] = '\0';
     5         kx }
     5         kx 
     5         kx void strbuf_trim( struct strbuf *sb )
     5         kx {
     5         kx   strbuf_rtrim( sb );
     5         kx   strbuf_ltrim( sb );
     5         kx }
     5         kx 
     5         kx 
     5         kx void strbuf_trim_trailing_dir_sep( struct strbuf *sb )
     5         kx {
     5         kx   while( sb->len > 0 && is_dir_sep((unsigned char)sb->buf[sb->len - 1]) )
     5         kx     sb->len--;
     5         kx   sb->buf[sb->len] = '\0';
     5         kx }
     5         kx 
     5         kx void strbuf_trim_trailing_newline( struct strbuf *sb )
     5         kx {
     5         kx   if( sb->len > 0 && sb->buf[sb->len - 1] == '\n' )
     5         kx   {
     5         kx     if( --sb->len > 0 && sb->buf[sb->len - 1] == '\r' )
     5         kx       --sb->len;
     5         kx     sb->buf[sb->len] = '\0';
     5         kx   }
     5         kx }
     5         kx 
     5         kx int strbuf_cmp( const struct strbuf *a, const struct strbuf *b )
     5         kx {
     5         kx   size_t len = a->len < b->len ? a->len: b->len;
     5         kx   int    cmp = memcmp( a->buf, b->buf, len );
     5         kx   if( cmp )
     5         kx     return cmp;
     5         kx   return a->len < b->len ? -1: a->len != b->len;
     5         kx }
     5         kx 
     5         kx 
     5         kx /* Adding data to the buffer */
     5         kx 
     5         kx void strbuf_add( struct strbuf *sb, const void *data, size_t len )
     5         kx {
     5         kx   strbuf_grow( sb, len );
     5         kx   memcpy( sb->buf + sb->len, data, len );
     5         kx   strbuf_setlen( sb, sb->len + len );
     5         kx }
     5         kx 
     5         kx void strbuf_addbuf( struct strbuf *sb, const struct strbuf *sb2 )
     5         kx {
     5         kx   strbuf_grow( sb, sb2->len );
     5         kx   memcpy( sb->buf + sb->len, sb2->buf, sb2->len );
     5         kx   strbuf_setlen( sb, sb->len + sb2->len );
     5         kx }
     5         kx 
     5         kx void strbuf_addbuf_percentquote( struct strbuf *dst, const struct strbuf *src )
     5         kx {
     5         kx   size_t i, len = src->len;
     5         kx 
     5         kx   for (i = 0; i < len; i++) {
     5         kx     if( src->buf[i] == '%' )
     5         kx       strbuf_addch( dst, '%' );
     5         kx     strbuf_addch( dst, src->buf[i] );
     5         kx   }
     5         kx }
     5         kx 
     5         kx void strbuf_addchars( struct strbuf *sb, int c, size_t n )
     5         kx {
     5         kx   strbuf_grow( sb, n );
     5         kx   memset( sb->buf + sb->len, c, n );
     5         kx   strbuf_setlen( sb, sb->len + n );
     5         kx }
     5         kx 
     5         kx void strbuf_vaddf( struct strbuf *sb, const char *fmt, va_list ap )
     5         kx {
     5         kx   int len;
     5         kx   va_list cp;
     5         kx 
     5         kx   if( !strbuf_avail( sb ) )
     5         kx     strbuf_grow( sb, 64 );
     5         kx   va_copy( cp, ap );
     5         kx   len = vsnprintf( sb->buf + sb->len, sb->alloc - sb->len, fmt, cp );
     5         kx   va_end( cp );
     5         kx   if( len < 0 )
     5         kx     sb->fatal( "your vsnprintf is broken (returned %d)", len );
     5         kx   if( len > strbuf_avail( sb ) )
     5         kx   {
     5         kx     strbuf_grow( sb, len );
     5         kx     len = vsnprintf( sb->buf + sb->len, sb->alloc - sb->len, fmt, ap );
     5         kx     if( len > strbuf_avail( sb ) )
     5         kx       sb->fatal( "your vsnprintf is broken (insatiable)" );
     5         kx   }
     5         kx   strbuf_setlen( sb, sb->len + len );
     5         kx }
     5         kx 
     5         kx void strbuf_addf( struct strbuf *sb, const char *fmt, ... )
     5         kx {
     5         kx   va_list ap;
     5         kx   va_start( ap, fmt );
     5         kx   strbuf_vaddf( sb, fmt, ap );
     5         kx   va_end( ap );
     5         kx }
     5         kx 
     5         kx 
     5         kx size_t strbuf_fread( struct strbuf *sb, FILE *fp )
     5         kx {
     5         kx   size_t ret, nb = 64, read = 0;
     5         kx   size_t oldalloc = sb->alloc;
     5         kx 
     5         kx   if( !sb || !fp ) return read;
     5         kx 
     5         kx   do
     5         kx   {
     5         kx     strbuf_grow( sb, nb );
     5         kx     ret = fread( sb->buf + sb->len, 1, nb, fp );
     5         kx     if( ret > 0 )
     5         kx     {
     5         kx       strbuf_setlen( sb, sb->len + ret );
     5         kx       read += ret;
     5         kx     }
     5         kx     else if( oldalloc == 0 )
     5         kx     {
     5         kx       strbuf_release( sb );
     5         kx       return ret;
     5         kx     }
     5         kx 
     5         kx   } while( ret == nb );
     5         kx 
     5         kx   return read;
     5         kx }
     5         kx 
     5         kx 
     5         kx #define NAMELEN_MAX  128
     5         kx 
     5         kx size_t strbuf_env_fread( struct strbuf *sb, FILE *fp )
     5         kx {
     5         kx   size_t  read = 0;
     5         kx   char   *ln, line[STRBUF_MAXLINE], retline[STRBUF_MAXLINE];
     5         kx 
     5         kx   if( !sb || !fp ) return read;
     5         kx 
     5         kx   bzero( (void *)line, STRBUF_MAXLINE );
     5         kx   bzero( (void *)retline, STRBUF_MAXLINE );
     5         kx 
     5         kx   while( (ln = fgets( line, STRBUF_MAXLINE, fp )) )
     5         kx   {
     5         kx     char *start = NULL, *stop = NULL;
     5         kx     char *sp = ln;
     5         kx 
     5         kx     if( (start = strstr( sp, "${" )) && (stop = strstr( sp, "}" )) && ((stop - start) > 1) )
     5         kx     {
     5         kx       struct symbol *sym = NULL;
     5         kx       char  *lp = retline;
     5         kx 
     5         kx       do
     5         kx       {
     5         kx         /* may be multiple variables on a single line: */
     5         kx 
     5         kx         *start = '\0'; *stop++ = '\0';
     5         kx         if( (sym = envtab_lookup( &strbuf_envtab, start+2 )) && sym->value && sym->value[0] )
     5         kx         {
     5         kx           strncpy( lp, (const char *)sp, (size_t)(start - sp + 1) );
     5         kx           lp += (start - sp);
     5         kx           strcpy( lp, (const char *)sym->value );
     5         kx           lp += strlen( sym->value );
     5         kx           strcpy( lp, (const char *)stop );
     5         kx           sp = stop;
     5         kx         }
     5         kx         else
     5         kx         {
     5         kx           strncpy( lp, (const char *)sp, (size_t)(start - sp + 1) );
     5         kx           lp += (start - sp);
     5         kx           strcpy( lp, (const char *)stop );
     5         kx           sp = stop;
     5         kx         }
     5         kx 
     5         kx       } while( (start = strstr( sp, "${" )) && (stop = strstr( sp, "}" )) && ((stop - start) > 1) );
     5         kx 
     5         kx       strbuf_addstr( sb, retline );
     5         kx       read += strlen( retline );;
     5         kx     }
     5         kx     else
     5         kx     {
     5         kx       strbuf_addstr( sb, line );
     5         kx       read += strlen( line );;
     5         kx     }
     5         kx 
     5         kx   } /* End of while( ln ) */
     5         kx 
     5         kx   return read;
     5         kx }
     5         kx 
     5         kx ssize_t strbuf_read( struct strbuf *sb, int fd, size_t hint )
     5         kx {
     5         kx   size_t oldlen   = sb->len;
     5         kx   size_t oldalloc = sb->alloc;
     5         kx 
     5         kx   strbuf_grow(sb, hint ? hint : 8192);
     5         kx   for( ;; )
     5         kx   {
     5         kx     ssize_t want = sb->alloc - sb->len - 1;
     5         kx     ssize_t got  = read_in_full( fd, sb->buf + sb->len, want );
     5         kx 
     5         kx     if( got < 0 )
     5         kx     {
     5         kx       if( oldalloc == 0 )
     5         kx         strbuf_release( sb );
     5         kx       else
     5         kx         strbuf_setlen( sb, oldlen );
     5         kx       return -1;
     5         kx     }
     5         kx     sb->len += got;
     5         kx     if( got < want )
     5         kx       break;
     5         kx     strbuf_grow( sb, 8192 );
     5         kx   }
     5         kx 
     5         kx   sb->buf[sb->len] = '\0';
     5         kx   return sb->len - oldlen;
     5         kx }
     5         kx 
     5         kx 
     5         kx size_t strbuf_fwrite( struct strbuf *sb, FILE *fp )
     5         kx {
     5         kx   return sb->len ? fwrite( sb->buf, 1, sb->len, fp ) : 0;
     5         kx }
     5         kx 
     5         kx ssize_t strbuf_write( struct strbuf *sb, int fd )
     5         kx {
     5         kx   return sb->len ? write( fd, (const void *)sb->buf, sb->len ) : 0;
     5         kx }
     5         kx 
     5         kx 
     5         kx /* XML quoted: */
     5         kx 
     5         kx void strbuf_addstr_xml_quoted( struct strbuf *sb, const char *s )
     5         kx {
     5         kx   while( *s )
     5         kx   {
     5         kx     size_t len = strcspn( s, "\"<>&" );
     5         kx     strbuf_add( sb, s, len );
     5         kx     s += len;
     5         kx 
     5         kx     switch( *s )
     5         kx     {
     5         kx       case '"':
     5         kx         strbuf_addstr( sb, "&quot;" );
     5         kx         break;
     5         kx       case '<':
     5         kx         strbuf_addstr( sb, "&lt;" );
     5         kx         break;
     5         kx       case '>':
     5         kx         strbuf_addstr( sb, "&gt;" );
     5         kx         break;
     5         kx       case '&':
     5         kx         strbuf_addstr( sb, "&amp;" );
     5         kx         break;
     5         kx       case 0:
     5         kx         return;
     5         kx     }
     5         kx     s++;
     5         kx   }
     5         kx }
     5         kx 
     5         kx static int is_html_quoted( const char *str )
     5         kx {
     5         kx   int         rc = 0, error = 0;
     5         kx   PCRE2_SIZE  offset = 0;
     5         kx   const char  pattern[] = "^(&[#A-Za-z0-9]*;)";
     5         kx 
     5         kx   pcre2_match_data *match;
     5         kx 
     5         kx   pcre2_code *regexp = pcre2_compile( (PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, 0, &error, &offset, NULL );
     5         kx   if( regexp == NULL )
     5         kx   {
     5         kx     return 0; /* PCRE compilation failed */
     5         kx   }
     5         kx 
     5         kx   match = pcre2_match_data_create_from_pattern( regexp, NULL );
     5         kx 
     5         kx   rc = pcre2_match( regexp, (PCRE2_SPTR)str, (PCRE2_SIZE)strlen(str), 0, 0, match, NULL ); /* sizeof(match)/sizeof(match[0]) */
     5         kx   if( rc < 0 )
     5         kx   {
     5         kx     /* not match */
     5         kx     pcre2_match_data_free( match );
     5         kx     pcre2_code_free( regexp );
     5         kx     return 0;
     5         kx   }
     5         kx   else
     5         kx   {
     5         kx     /* match */
     5         kx     pcre2_match_data_free( match );
     5         kx     pcre2_code_free( regexp );
     5         kx     return 1;
     5         kx   }
     5         kx }
     5         kx 
     5         kx void strbuf_addstr_html_quoted( struct strbuf *sb, const char *s )
     5         kx {
     5         kx   while( *s )
     5         kx   {
     5         kx     size_t len = strcspn( s, "\"<>&" );
     5         kx     strbuf_add( sb, s, len );
     5         kx     s += len;
     5         kx 
     5         kx     switch( *s )
     5         kx     {
     5         kx       case '"':
     5         kx         strbuf_addstr( sb, "&quot;" );
     5         kx         break;
     5         kx       case '<':
     5         kx         strbuf_addstr( sb, "&lt;" );
     5         kx         break;
     5         kx       case '>':
     5         kx         strbuf_addstr( sb, "&gt;" );
     5         kx         break;
     5         kx       case '&':
     5         kx         if( !is_html_quoted( s ) )
     5         kx           strbuf_addstr( sb, "&amp;" );
     5         kx         else
     5         kx           strbuf_addch( sb, *s );
     5         kx         break;
     5         kx       case 0:
     5         kx         return;
     5         kx     }
     5         kx     s++;
     5         kx   }
     5         kx }
     5         kx 
     5         kx 
     5         kx /* urlencode: */
     5         kx 
     5         kx int is_rfc3986_reserved_or_unreserved( char ch )
     5         kx {
     5         kx   if( is_rfc3986_unreserved(ch) )
     5         kx     return 1;
     5         kx   switch( ch )
     5         kx   {
     5         kx     case '!': case '*': case '\'': case '(': case ')': case ';':
     5         kx     case ':': case '@': case '&': case '=': case '+': case '$':
     5         kx     case ',': case '/': case '?': case '#': case '[': case ']':
     5         kx       return 1;
     5         kx   }
     5         kx   return 0;
     5         kx }
     5         kx 
     5         kx int is_rfc3986_unreserved( char ch )
     5         kx {
     5         kx   return isalnum(ch) || ch == '-' || ch == '_' || ch == '.' || ch == '~';
     5         kx }
     5         kx 
     5         kx static void
     5         kx strbuf_add_urlencode( struct strbuf *sb, const char *s, size_t len,
     5         kx                       char_predicate allow_unencoded_fn )
     5         kx {
     5         kx   strbuf_grow( sb, len );
     5         kx   while( len-- )
     5         kx   {
     5         kx     char ch = *s++;
     5         kx     if( allow_unencoded_fn(ch) )
     5         kx       strbuf_addch( sb, ch );
     5         kx     else
     5         kx       strbuf_addf( sb, "%%%02x", (unsigned char)ch );
     5         kx   }
     5         kx }
     5         kx 
     5         kx void strbuf_addstr_urlencode( struct strbuf *sb, const char *s, char_predicate allow_unencoded_fn )
     5         kx {
     5         kx   strbuf_add_urlencode( sb, s, strlen(s), allow_unencoded_fn );
     5         kx }
     5         kx 
     5         kx 
     5         kx /* humanise: */
     5         kx 
     5         kx static void strbuf_humanise( struct strbuf *sb, off_t bytes, int humanise_rate )
     5         kx {
     5         kx   if( bytes > 1 << 30 )
     5         kx   {
     5         kx     strbuf_addf( sb,
     5         kx       humanise_rate == 0 ?
     5         kx         /* TRANSLATORS: IEC 80000-13:2008 gibibyte */
     5         kx         _("%u.%2.2u GiB") :
     5         kx         /* TRANSLATORS: IEC 80000-13:2008 gibibyte/second */
     5         kx         _("%u.%2.2u GiB/s"),
     5         kx         (unsigned)(bytes >> 30),
     5         kx         (unsigned)(bytes & ((1 << 30) - 1)) / 10737419 );
     5         kx   }
     5         kx   else if( bytes > 1 << 20 )
     5         kx   {
     5         kx     unsigned x = bytes + 5243;  /* for rounding */
     5         kx     strbuf_addf( sb,
     5         kx       humanise_rate == 0 ?
     5         kx         /* TRANSLATORS: IEC 80000-13:2008 mebibyte */
     5         kx         _("%u.%2.2u MiB") :
     5         kx         /* TRANSLATORS: IEC 80000-13:2008 mebibyte/second */
     5         kx         _("%u.%2.2u MiB/s"),
     5         kx         x >> 20, ((x & ((1 << 20) - 1)) * 100) >> 20 );
     5         kx   }
     5         kx   else if( bytes > 1 << 10 )
     5         kx   {
     5         kx     unsigned x = bytes + 5;  /* for rounding */
     5         kx     strbuf_addf( sb,
     5         kx       humanise_rate == 0 ?
     5         kx         /* TRANSLATORS: IEC 80000-13:2008 kibibyte */
     5         kx         _("%u.%2.2u KiB") :
     5         kx         /* TRANSLATORS: IEC 80000-13:2008 kibibyte/second */
     5         kx         _("%u.%2.2u KiB/s"),
     5         kx         x >> 10, ((x & ((1 << 10) - 1)) * 100) >> 10 );
     5         kx   }
     5         kx   else
     5         kx   {
     5         kx     strbuf_addf( sb,
     5         kx       humanise_rate == 0 ?
     5         kx         /* TRANSLATORS: IEC 80000-13:2008 byte */
     5         kx         Q_("%u byte", "%u bytes", (unsigned)bytes) :
     5         kx         /* TRANSLATORS: IEC 80000-13:2008 byte/second */
     5         kx         Q_("%u byte/s", "%u bytes/s", (unsigned)bytes),
     5         kx       (unsigned)bytes );
     5         kx   }
     5         kx }
     5         kx 
     5         kx void strbuf_humanise_bytes( struct strbuf *sb, off_t bytes )
     5         kx {
     5         kx   strbuf_humanise( sb, bytes, 0 );
     5         kx }
     5         kx 
     5         kx void strbuf_humanise_rate( struct strbuf *sb, off_t bytes )
     5         kx {
     5         kx   strbuf_humanise( sb, bytes, 1 );
     5         kx }
     5         kx 
     5         kx 
     5         kx int is_directory( const char *path )
     5         kx {
     5         kx   struct stat st;
     5         kx   return ( !stat( path, &st ) && S_ISDIR(st.st_mode) );
     5         kx }
     5         kx 
     5         kx void strbuf_selfdir( struct strbuf *sb )
     5         kx {
     5         kx   char    path[PATH_MAX];
     5         kx   ssize_t len;
     5         kx 
     5         kx   bzero( (void *)path, PATH_MAX );
     5         kx 
     5         kx   len = readlink( "/proc/self/exe", &path[0], (size_t)PATH_MAX );
     5         kx   if( len > 0 && len < PATH_MAX )
     5         kx   {
     5         kx     strbuf_addstr( sb, (const char *)dirname( (char *)&path[0] ) );
     5         kx   }
     5         kx   else
     5         kx     sb->fatal( "cannot get selfdir" );
     5         kx }
     5         kx 
     5         kx void strbuf_relpath( struct strbuf *sb, const char *path )
     5         kx {
     5         kx   struct strbuf self = STRBUF_INIT;
     5         kx   char   p[PATH_MAX];
     5         kx 
     5         kx   bzero( (void *)p, PATH_MAX );
     5         kx 
     5         kx   if( realpath( path, (char *)&p[0] ) == NULL )
     5         kx   {
     5         kx     sb->fatal( "cannot get relative path of '%s'", path );
     5         kx   }
     5         kx 
     5         kx   strbuf_init( &self, sb->fatal, 0 );
     5         kx   strbuf_selfdir( &self );
     5         kx 
     5         kx   strbuf_addstr( sb, (const char *)&p[self.len] );
     5         kx 
     5         kx   strbuf_release( &self );
     5         kx }
     5         kx 
     5         kx void strbuf_abspath( struct strbuf *sb, const char *path )
     5         kx {
     5         kx   char p[PATH_MAX];
     5         kx 
     5         kx   if( realpath( path, (char *)&p[0] ) == NULL )
     5         kx   {
     5         kx     sb->fatal( "cannot get absolute path of '%s'", path );
     5         kx   }
     5         kx 
     5         kx   strbuf_addstr( sb, (char *)&p[0] );
     5         kx }