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 #ifdef HAVE_INTTYPES_H
     5         kx #include <inttypes.h>
     5         kx #else
     5         kx #include <stdint.h>
     5         kx #endif
     5         kx #include <stddef.h>   /* offsetof(3)   */
     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 <strings.h>  /* strcasecmp(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 <locale.h>
     5         kx #include <unistd.h>
     5         kx 
     5         kx #include <git2.h>
     5         kx 
     5         kx #include <nls.h>
     5         kx 
     5         kx #include <defs.h>
     5         kx 
     5         kx #include <fatal.h>
     5         kx #include <http.h>
     5         kx #include <html.h>
     5         kx 
     5         kx #include <dlist.h>
     5         kx #include <strbuf.h>
     5         kx #include <repolist.h>
     5         kx #include <wrapper.h>
     5         kx #include <system.h>
     5         kx #include <date.h>
     5         kx 
     5         kx #include <ctx.h>
     5         kx 
     5         kx #include <git-shared.h>
     5         kx #include <ui-shared.h>
     5         kx 
     5         kx 
     5         kx /* Allocate memory in .bss segment: */
     5         kx static struct __context mctx;
     5         kx 
     5         kx /* Share address of __context: */
     5         kx struct __context *pmctx = &mctx;
     5         kx 
     5         kx void __mctx_init( void )
     5         kx {
     5         kx   pmctx->_cur_brk = (void *)&((pmctx->_mem)[0]);
     5         kx }
     5         kx 
     5         kx static int
     5         kx __brk( void *end_d )
     5         kx {
     5         kx   void *ptr = __mem;
     5         kx 
     5         kx   if( (unsigned char *)end_d < (unsigned char *)ptr ||
     5         kx       (unsigned char *)end_d > (unsigned char *)ptr + CONTEXT_MEM_SIZE )
     5         kx   {
     5         kx     errno = ENOMEM;
     5         kx     return( -1 );
     5         kx   }
     5         kx 
     5         kx  /*
     5         kx    __cur_brk = (unsigned char *)end_d;
     5         kx 
     5         kx    Функция  __brk()  лишь проверяет границы
     5         kx    области памяти, значение __cur_brk выставляется
     5         kx    в __sbrk() .
     5         kx   *************************************************/
     5         kx 
     5         kx   return( 0 );
     5         kx 
     5         kx } /* End of __brk() */
     5         kx 
     5         kx void *
     5         kx __sbrk( int incr )
     5         kx {
     5         kx   void         *ptr = __cur_brk;
     5         kx   register int  rc;
     5         kx 
     5         kx   if( incr == 0 ) return( ptr );
     5         kx 
     5         kx   rc = __brk( ptr + incr );
     5         kx   if( rc == -1 )
     5         kx   {
     5         kx     /* errno is set into __brk() */
     5         kx     return( (void *)0 );
     5         kx   }
     5         kx 
     5         kx   __cur_brk = ((unsigned char *)ptr) + (int)incr;
     5         kx 
     5         kx   return( ptr );
     5         kx 
     5         kx } /* End of __sbrk() */
     5         kx 
     5         kx 
     5         kx struct cgit_context ctx;
     5         kx 
     5         kx const char *ptype_repolist   = "repolist";
     5         kx const char *ptype_repo       = "repo";
     5         kx 
     5         kx void cgit_prepare_context( void )
     5         kx {
     5         kx   memset( &ctx, 0, sizeof(ctx) );
     5         kx 
     5         kx   ctx.env.http_host = getenv("HTTP_HOST");
     5         kx   ctx.env.https = getenv("HTTPS");
     5         kx   ctx.env.no_http = getenv("NO_HTTP");
     5         kx   ctx.env.path_info = getenv("PATH_INFO");
     5         kx   ctx.env.query_string = getenv("QUERY_STRING");
     5         kx   ctx.env.request_uri = getenv("REQUEST_URI");
     5         kx 
     5         kx   if( ctx.env.request_uri == NULL )
     5         kx     ctx.env.request_uri = "/";
     5         kx 
     5         kx   ctx.env.request_scheme = getenv("REQUEST_SCHEME");
     5         kx   ctx.env.request_method = getenv("REQUEST_METHOD");
     5         kx   ctx.env.script_name = getenv("SCRIPT_NAME");
     5         kx   ctx.env.server_name = getenv("SERVER_NAME");
     5         kx   ctx.env.server_port = getenv("SERVER_PORT");
     5         kx   ctx.env.http_cookie = getenv("HTTP_COOKIE");
     5         kx   ctx.env.content_lenght = getenv("CONTENT_LENGTH") ? strtoul(getenv("CONTENT_LENGTH"), NULL, 10) : 0;
     5         kx   ctx.env.http_root = NULL;
     5         kx   ctx.env.authenticated = 0;
     5         kx 
     5         kx   ctx.query.ofs = 0;
     5         kx   ctx.query.rev = "0";
     5         kx   ctx.query.revision = NULL;
     5         kx   ctx.query.operation = NULL;
     5         kx   ctx.query.search = NULL;
     5         kx 
     5         kx   ctx.page.mimetype = "text/html";
     5         kx   ctx.page.charset = "UTF-8";
     5         kx   ctx.page.size = 0;
     5         kx   ctx.page.modified = time(NULL);
     5         kx   ctx.page.expires = ctx.page.modified + 5 * 60;
     5         kx   ctx.page.status = 200;
     5         kx   ctx.page.status_message = "OK";
     5         kx   ctx.page.header = "/.cgit/html/header.html";
     5         kx   ctx.page.footer = "/.cgit/html/footer.html";
     5         kx 
     5         kx   ctx.vars.css = "/.cgit/css/cgit.css";
     5         kx   ctx.vars.owner = "Andrey V.Kosteltsev";
     5         kx   ctx.vars.author = "Andrey V.Kosteltsev";
     5         kx   ctx.vars.description = "Git repositories hosted at Solar System, Earth";
     5         kx   ctx.vars.keywords = "cGit repositories";
     5         kx   ctx.vars.title = "Git Repositories";
     5         kx   ctx.vars.favicon_path = "/.cgit/pixmaps/favicon";
     5         kx   ctx.vars.syntax_highlight_css = "_cgit.css";
     5         kx   ctx.vars.logo = "/.cgit/pixmaps/cgit-banner-280x280.png";
     5         kx   ctx.vars.logo_alt = "Example.org";
     5         kx   ctx.vars.logo_link = "https://example.org";
     5         kx   ctx.vars.home_page = "https://example.org";
     5         kx   ctx.vars.snapshots = "tar.xz";
     5         kx   ctx.vars.status_line = "Git Repositories";
     5         kx   ctx.vars.main_menu_logo = "/.cgit/pixmaps/logo/git-logo-white-256x256.svg";
     5         kx   ctx.vars.main_menu_item = "<a href='/'>Index</a>";
     5         kx   ctx.vars.left_menu_items = "";
     5         kx   ctx.vars.popup_menu_items = "<div class='item'><span class='icon las la-home'></span><a href='https://example.org/' target='_blank'>Home page</a></div>";
     5         kx   ctx.vars.right_menu_items = "<div class='item'><a href='https://example.org/' target='_blank'>Home page</a></div>";
     5         kx   ctx.vars.copyright_notice = "By using any website materials you agree to indicate source.";
     5         kx   ctx.vars.copyright = "&#169; 2022 Andrey V.Kosteltsev. All Rights Reserved.";
     5         kx   ctx.vars.page_type = ptype_repolist;
     5         kx   ctx.vars.page_size = "200";
     5         kx   ctx.vars.num_of_repos = "0";
     5         kx 
     5         kx   ctx.repo.name = NULL;
     5         kx   ctx.repo.info = CGIT_INFO_INIT;
     5         kx   ctx.repo.git_root = NULL;
     5         kx   ctx.repo.repo_root = NULL;
     5         kx   ctx.repo.relative_path = NULL;
     5         kx   ctx.repo.relative_info = CGIT_INFO_INIT;
     5         kx   ctx.repo.relative_html = NULL;
     5         kx   ctx.repo.relative_href = "/";
     5         kx   ctx.repo.search_placeholder = "repository...";
     5         kx   ctx.repo.trunk = "master";
     5         kx   ctx.repo.clone_prefix = NULL;
     5         kx   ctx.repo.clone_ro_prefix = NULL;
     5         kx   ctx.repo.nbranches = 0;
     5         kx   ctx.repo.ncommits = 0;
     5         kx   ctx.repo.ntags = 0;
     5         kx 
     5         kx   ctx.promo.analytic_links   = NULL;
     5         kx   ctx.promo.analytic_scripts = NULL;
     5         kx   ctx.promo.donate           = 0;
     5         kx   ctx.promo.donate_css       = NULL;
     5         kx   ctx.promo.donate_html      = NULL;
     5         kx   ctx.promo.donate_js        = NULL;
     5         kx   ctx.promo.donate_header    = NULL;
     5         kx   ctx.promo.donate_purpose   = NULL;
     5         kx 
     5         kx   ctx.vers.git   = NULL;
     5         kx   ctx.vers.nginx = NULL;
     5         kx   ctx.vers.cgit  = PROGRAM_VERSION;
     5         kx }
     5         kx 
     5         kx void cgit_prepare_template_variables( void )
     5         kx {
     5         kx   envtab_install( &strbuf_envtab, "css", ctx.vars.css, fatal_html );
     5         kx   envtab_install( &strbuf_envtab, "owner", ctx.vars.owner, fatal_html );
     5         kx   envtab_install( &strbuf_envtab, "author", ctx.vars.author, fatal_html );
     5         kx   envtab_install( &strbuf_envtab, "description", ctx.vars.description, fatal_html );
     5         kx   envtab_install( &strbuf_envtab, "keywords", ctx.vars.keywords, fatal_html );
     5         kx   envtab_install( &strbuf_envtab, "title", ctx.vars.title, fatal_html );
     5         kx   envtab_install( &strbuf_envtab, "favicon-path", ctx.vars.favicon_path, fatal_html );
     5         kx   envtab_install( &strbuf_envtab, "syntax-highlight-css", ctx.vars.syntax_highlight_css, fatal_html );
     5         kx   envtab_install( &strbuf_envtab, "logo", ctx.vars.logo, fatal_html );
     5         kx   envtab_install( &strbuf_envtab, "logo-alt", ctx.vars.logo_alt, fatal_html );
     5         kx   envtab_install( &strbuf_envtab, "logo-link", ctx.vars.logo_link, fatal_html );
     5         kx   envtab_install( &strbuf_envtab, "home-page", ctx.vars.home_page, fatal_html );
     5         kx   envtab_install( &strbuf_envtab, "snapshots", ctx.vars.snapshots, fatal_html );
     5         kx   envtab_install( &strbuf_envtab, "status-line", ctx.vars.status_line, fatal_html );
     5         kx   envtab_install( &strbuf_envtab, "main-menu-logo", ctx.vars.main_menu_logo, fatal_html );
     5         kx   envtab_install( &strbuf_envtab, "main-menu-item", ctx.vars.main_menu_item, fatal_html );
     5         kx   envtab_install( &strbuf_envtab, "left-menu-items", ctx.vars.left_menu_items, fatal_html );
     5         kx   envtab_install( &strbuf_envtab, "popup-menu-items", ctx.vars.popup_menu_items, fatal_html );
     5         kx   envtab_install( &strbuf_envtab, "right-menu-items", ctx.vars.right_menu_items, fatal_html );
     5         kx 
     5         kx   envtab_install( &strbuf_envtab, "relative-html", ctx.repo.relative_html, fatal_html );
     5         kx   envtab_install( &strbuf_envtab, "search-placeholder", ctx.repo.search_placeholder, fatal_html );
     5         kx 
     5         kx   envtab_install( &strbuf_envtab, "analytic-links", ctx.promo.analytic_links, fatal_html );
     5         kx   envtab_install( &strbuf_envtab, "analytic-scripts", ctx.promo.analytic_scripts, fatal_html );
     5         kx   envtab_install( &strbuf_envtab, "donate-css", ctx.promo.donate_css, fatal_html );
     5         kx   envtab_install( &strbuf_envtab, "donate-html", ctx.promo.donate_html, fatal_html );
     5         kx   envtab_install( &strbuf_envtab, "donate-js", ctx.promo.donate_js, fatal_html );
     5         kx   envtab_install( &strbuf_envtab, "donate-header", ctx.promo.donate_header, fatal_html );
     5         kx   envtab_install( &strbuf_envtab, "donate-purpose", ctx.promo.donate_purpose, fatal_html );
     5         kx 
     5         kx   envtab_install( &strbuf_envtab, "git-version", ctx.vers.git, fatal_html );
     5         kx   envtab_install( &strbuf_envtab, "nginx-version", ctx.vers.nginx, fatal_html );
     5         kx   envtab_install( &strbuf_envtab, "cgit-version", ctx.vers.cgit, fatal_html );
     5         kx 
     5         kx   envtab_install( &strbuf_envtab, "copyright-notice", ctx.vars.copyright_notice, fatal_html );
     5         kx   envtab_install( &strbuf_envtab, "copyright", ctx.vars.copyright, fatal_html );
     5         kx 
     5         kx   envtab_install( &strbuf_envtab, "page-type", ctx.vars.page_type, fatal_html );
     5         kx   envtab_install( &strbuf_envtab, "page-size", ctx.vars.page_size, fatal_html );
     5         kx   envtab_install( &strbuf_envtab, "num-of-repos", ctx.vars.num_of_repos, fatal_html );
     5         kx }
     5         kx 
     5         kx void cgit_release_template_variables( void )
     5         kx {
     5         kx   envtab_release( &strbuf_envtab );
     5         kx }
     5         kx 
     5         kx static void get_selfdir( void )
     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     char *selfdir = NULL;
     5         kx 
     5         kx     selfdir = (char *)__sbrk( (int)len + 1 );
     5         kx     strcpy( selfdir, (const char *)dirname( (char *)&path[0] ) );
     5         kx     ctx.env.http_root = selfdir;
     5         kx   }
     5         kx   else
     5         kx     fatal_html( "cannot get selfdir" );
     5         kx }
     5         kx 
     5         kx static size_t read_http_root_raw_file( struct strbuf *sb, const char *fname )
     5         kx {
     5         kx   char    path[PATH_MAX];
     5         kx   int     fd = -1;
     5         kx   size_t  len = 0;
     5         kx 
     5         kx   if( !sb || !fname || !*fname ) return len;
     5         kx 
     5         kx   bzero( (void *)path, PATH_MAX );
     5         kx   strcpy( (char *)&path[0], ctx.env.http_root );
     5         kx   if( *fname != '/' )
     5         kx     strcat( (char *)&path[0], "/" );
     5         kx 
     5         kx   strcat( (char *)&path[0], fname );
     5         kx 
     5         kx   if( (fd = open( (const char *)&path[0], O_RDONLY )) == -1 )
     5         kx    fatal_html( "Cannot open %s file: %s", fname, strerror( errno ) );
     5         kx 
     5         kx   len = strbuf_read( sb, fd, 0 );
     5         kx   close( fd );
     5         kx 
     5         kx   return len;
     5         kx }
     5         kx 
     5         kx static size_t read_http_root_env_file( struct strbuf *sb, const char *fname )
     5         kx {
     5         kx   char    path[PATH_MAX];
     5         kx   FILE   *fp = NULL;
     5         kx   size_t  len = 0;
     5         kx 
     5         kx   if( !sb || !fname || !*fname ) return len;
     5         kx 
     5         kx   bzero( (void *)path, PATH_MAX );
     5         kx   strcpy( (char *)&path[0], ctx.env.http_root );
     5         kx   if( *fname != '/' )
     5         kx     strcat( (char *)&path[0], "/" );
     5         kx 
     5         kx   strcat( (char *)&path[0], fname );
     5         kx 
     5         kx   if( (fp = fopen( (const char *)&path[0], "r" )) == NULL )
     5         kx    fatal_html( "Cannot open %s file: %s", fname, strerror( errno ) );
     5         kx 
     5         kx   len = strbuf_env_fread( sb, fp );
     5         kx   fclose( fp );
     5         kx 
     5         kx   if( len > STRBUF_MAXLINE )
     5         kx     fatal_html( "The '%s' file is too large. No more than 8192 bytes is allowed for includes", fname );
     5         kx 
     5         kx   return len;
     5         kx }
     5         kx 
     5         kx static void read_donate_html( void )
     5         kx {
     5         kx   struct strbuf buf = STRBUF_INIT;
     5         kx   char  *html = NULL;
     5         kx 
     5         kx   if( !ctx.promo.donate ) return;
     5         kx   if( !ctx.promo.donate_html || !*ctx.promo.donate_html ) return;
     5         kx 
     5         kx   (void)read_http_root_env_file( &buf, ctx.promo.donate_html );
     5         kx 
     5         kx   html = (char *)__sbrk( (int)buf.len + 1 );
     5         kx   memcpy( (void *)html, (const void *)buf.buf, buf.len + 1 );
     5         kx   ctx.promo.donate_html = (const char *)html;
     5         kx   strbuf_release( &buf );
     5         kx }
     5         kx 
     5         kx 
     5         kx static char psize[4] = { 0, 0, 0, 0 };
     5         kx 
     5         kx static void ctx_page_size_from_global( void )
     5         kx {
     5         kx   struct variable f = { (unsigned char *)"page-size", { 0 }, DT_NUMERICAL }, *var = NULL;
     5         kx   var = lookup_global( lookup_global_section( config ), &f );
     5         kx   if( var )
     5         kx   {
     5         kx     if( var->type == DT_NUMERICAL )
     5         kx     {
     5         kx       if( var->_v.val > 9 && var->_v.val < 201 )
     5         kx         snprintf( &psize[0], 4, "%d", var->_v.val );
     5         kx       else
     5         kx         snprintf( &psize[0], 4, "%d", 200 );
     5         kx       ctx.vars.page_size = &psize[0];
     5         kx     }
     5         kx     else
     5         kx     {
     5         kx       int size = 0;
     5         kx       sscanf( (const char *)var->_v.vptr, "%d", &size );
     5         kx       if( size > 9 && size < 201 )
     5         kx         ctx.vars.page_size = (const char *)var->_v.vptr;
     5         kx       else
     5         kx       {
     5         kx         snprintf( &psize[0], 4, "%d", 200 );
     5         kx         ctx.vars.page_size = &psize[0];
     5         kx       }
     5         kx     }
     5         kx   }
     5         kx }
     5         kx 
     5         kx static void ctx_page_size_from_repo( struct repo *repo )
     5         kx {
     5         kx   struct variable f = { (unsigned char *)"page-size", { 0 }, DT_NUMERICAL }, *var = NULL;
     5         kx   var = lookup( repo, &f );
     5         kx   if( var )
     5         kx   {
     5         kx     if( var->type == DT_NUMERICAL )
     5         kx     {
     5         kx       if( var->_v.val > 9 && var->_v.val < 201 )
     5         kx         snprintf( &psize[0], 4, "%d", var->_v.val );
     5         kx       else
     5         kx         snprintf( &psize[0], 4, "%d", 200 );
     5         kx       ctx.vars.page_size = &psize[0];
     5         kx     }
     5         kx     else
     5         kx     {
     5         kx       int size = 0;
     5         kx       sscanf( (const char *)var->_v.vptr, "%d", &size );
     5         kx       if( size > 9 && size < 201 )
     5         kx         ctx.vars.page_size = (const char *)var->_v.vptr;
     5         kx       else
     5         kx       {
     5         kx         snprintf( &psize[0], 4, "%d", 200 );
     5         kx         ctx.vars.page_size = &psize[0];
     5         kx       }
     5         kx     }
     5         kx   }
     5         kx }
     5         kx 
     5         kx static void ctx_repo_dirs_from_global( void )
     5         kx {
     5         kx   struct variable  git_root  = { (unsigned char *)"git-root",  { 0 }, DT_PATH };
     5         kx   struct variable  repo_root = { (unsigned char *)"repo-root", { 0 }, DT_PATH };
     5         kx   struct variable  trunk     = { (unsigned char *)"trunk",     { 0 }, DT_PATH };
     5         kx   struct variable  ro_prefix = { (unsigned char *)"clone-prefix-readonly", { 0 }, DT_PATH };
     5         kx   struct variable  rw_prefix = { (unsigned char *)"clone-prefix",          { 0 }, DT_PATH };
     5         kx   struct variable *var = NULL;
     5         kx 
     5         kx   var = lookup_global( lookup_global_section( config ), &git_root );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.repo.git_root = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup_global( lookup_global_section( config ), &repo_root );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.repo.repo_root = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup_global( lookup_global_section( config ), &trunk );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.repo.trunk = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup_global( lookup_global_section( config ), &rw_prefix );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.repo.clone_prefix = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup_global( lookup_global_section( config ), &ro_prefix );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.repo.clone_ro_prefix = (const char *)var->_v.vptr;
     5         kx   }
     5         kx }
     5         kx 
     5         kx static void ctx_site_vars_from_global( void )
     5         kx {
     5         kx   struct variable                   css = { (unsigned char *)"css",                  { 0 }, DT_PATH };
     5         kx   struct variable                 owner = { (unsigned char *)"owner",                { 0 }, DT_STRING };
     5         kx   struct variable                author = { (unsigned char *)"author",               { 0 }, DT_STRING };
     5         kx   struct variable                 title = { (unsigned char *)"title",                { 0 }, DT_STRING };
     5         kx   struct variable           description = { (unsigned char *)"description",          { 0 }, DT_STRING };
     5         kx   struct variable              keywords = { (unsigned char *)"keywords",             { 0 }, DT_STRING };
     5         kx   struct variable      copyright_notice = { (unsigned char *)"copyright-notice",     { 0 }, DT_STRING };
     5         kx   struct variable             copyright = { (unsigned char *)"copyright",            { 0 }, DT_STRING };
     5         kx   struct variable          favicon_path = { (unsigned char *)"favicon-path",         { 0 }, DT_PATH   };
     5         kx   struct variable                  logo = { (unsigned char *)"logo",                 { 0 }, DT_PATH   };
     5         kx   struct variable              logo_alt = { (unsigned char *)"logo-alt",             { 0 }, DT_STRING };
     5         kx   struct variable             logo_link = { (unsigned char *)"logo-link",            { 0 }, DT_STRING };
     5         kx   struct variable             home_page = { (unsigned char *)"home-page",            { 0 }, DT_STRING };
     5         kx   struct variable             snapshots = { (unsigned char *)"snapshots",            { 0 }, DT_PATH   };
     5         kx   struct variable        main_menu_logo = { (unsigned char *)"main-menu-logo",       { 0 }, DT_PATH   };
     5         kx   struct variable  syntax_highlight_css = { (unsigned char *)"syntax-highlight-css", { 0 }, DT_PATH   };
     5         kx   struct variable *var = NULL;
     5         kx 
     5         kx   var = lookup_global( lookup_global_section( config ), &css );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.css = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup_global( lookup_global_section( config ), &owner );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.owner = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup_global( lookup_global_section( config ), &author );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.author = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup_global( lookup_global_section( config ), &title );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.title = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup_global( lookup_global_section( config ), &description );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.description = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup_global( lookup_global_section( config ), &keywords );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.keywords = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup_global( lookup_global_section( config ), &copyright_notice );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.copyright_notice = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup_global( lookup_global_section( config ), &copyright );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.copyright = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup_global( lookup_global_section( config ), &favicon_path );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.favicon_path = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup_global( lookup_global_section( config ), &logo );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.logo = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup_global( lookup_global_section( config ), &logo_alt );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.logo_alt = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup_global( lookup_global_section( config ), &logo_link );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.logo_link = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup_global( lookup_global_section( config ), &home_page );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.home_page = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup_global( lookup_global_section( config ), &snapshots );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.snapshots = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup_global( lookup_global_section( config ), &main_menu_logo );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.main_menu_logo = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup_global( lookup_global_section( config ), &syntax_highlight_css );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.syntax_highlight_css = (const char *)var->_v.vptr;
     5         kx   }
     5         kx }
     5         kx 
     5         kx static void ctx_promo_vars_from_global( void )
     5         kx {
     5         kx   struct variable  analytic_links   = { (unsigned char *)"analytic-links",   { 0 }, DT_PATH      };
     5         kx   struct variable  analytic_scripts = { (unsigned char *)"analytic-scripts", { 0 }, DT_PATH      };
     5         kx   struct variable  donate           = { (unsigned char *)"donate",           { 0 }, DT_NUMERICAL };
     5         kx   struct variable  donate_css       = { (unsigned char *)"donate-css",       { 0 }, DT_PATH      };
     5         kx   struct variable  donate_html      = { (unsigned char *)"donate-html",      { 0 }, DT_PATH      };
     5         kx   struct variable  donate_js        = { (unsigned char *)"donate-js",        { 0 }, DT_PATH      };
     5         kx   struct variable  donate_header    = { (unsigned char *)"donate-header",    { 0 }, DT_STRING    };
     5         kx   struct variable  donate_purpose   = { (unsigned char *)"donate-purpose",   { 0 }, DT_STRING    };
     5         kx   struct variable *var = NULL;
     5         kx 
     5         kx   var = lookup_global( lookup_global_section( config ), &analytic_links );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.promo.analytic_links = (const char *)var->_v.vptr;
     5         kx 
     5         kx     if( ctx.promo.analytic_links && *ctx.promo.analytic_links )
     5         kx     {
     5         kx       struct strbuf buf = STRBUF_INIT;
     5         kx       char  *links = NULL;
     5         kx 
     5         kx       (void)read_http_root_raw_file( &buf, ctx.promo.analytic_links );
     5         kx 
     5         kx       links = (char *)__sbrk( (int)buf.len + 1 );
     5         kx       memcpy( (void *)links, (const void *)buf.buf, buf.len + 1 );
     5         kx       ctx.promo.analytic_links = (const char *)links;
     5         kx       strbuf_release( &buf );
     5         kx     }
     5         kx   }
     5         kx   var = lookup_global( lookup_global_section( config ), &analytic_scripts );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.promo.analytic_scripts = (const char *)var->_v.vptr;
     5         kx 
     5         kx     if( ctx.promo.analytic_scripts && *ctx.promo.analytic_scripts )
     5         kx     {
     5         kx       struct strbuf buf = STRBUF_INIT;
     5         kx       char  *scripts = NULL;
     5         kx 
     5         kx       (void)read_http_root_raw_file( &buf, ctx.promo.analytic_scripts );
     5         kx 
     5         kx       scripts = (char *)__sbrk( (int)buf.len + 1 );
     5         kx       memcpy( (void *)scripts, (const void *)buf.buf, buf.len + 1 );
     5         kx       ctx.promo.analytic_scripts = (const char *)scripts;
     5         kx       strbuf_release( &buf );
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   var = lookup_global( lookup_global_section( config ), &donate );
     5         kx   if( var )
     5         kx   {
     5         kx     if( var->type == DT_NUMERICAL )
     5         kx     {
     5         kx       ctx.promo.donate = var->_v.val;
     5         kx       if( ctx.promo.donate )
     5         kx         ctx.promo.donate = 1;
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   if( ctx.promo.donate )
     5         kx   {
     5         kx     var = lookup_global( lookup_global_section( config ), &donate_css );
     5         kx     if( var )
     5         kx     {
     5         kx       ctx.promo.donate_css = (const char *)var->_v.vptr;
     5         kx 
     5         kx       if( ctx.promo.donate_css && *ctx.promo.donate_css )
     5         kx       {
     5         kx         struct strbuf buf = STRBUF_INIT;
     5         kx         char  *css = NULL;
     5         kx 
     5         kx         strbuf_addf( &buf, "<link rel=\"stylesheet\" href=\"%s\">", ctx.promo.donate_css );
     5         kx         strbuf_trim( &buf );
     5         kx         css = (char *)__sbrk( (int)buf.len + 1 );
     5         kx         memcpy( (void *)css, (const void *)buf.buf, buf.len + 1 );
     5         kx         ctx.promo.donate_css = (const char *)css;
     5         kx         strbuf_release( &buf );
     5         kx       }
     5         kx     }
     5         kx     var = lookup_global( lookup_global_section( config ), &donate_js );
     5         kx     if( var )
     5         kx     {
     5         kx       ctx.promo.donate_js = (const char *)var->_v.vptr;
     5         kx 
     5         kx       if( ctx.promo.donate_js && *ctx.promo.donate_js )
     5         kx       {
     5         kx         struct strbuf buf = STRBUF_INIT;
     5         kx         char  *js = NULL;
     5         kx 
     5         kx         strbuf_addf( &buf, "<script src=\"%s\"></script>", ctx.promo.donate_js );
     5         kx         strbuf_trim( &buf );
     5         kx         js = (char *)__sbrk( (int)buf.len + 1 );
     5         kx         memcpy( (void *)js, (const void *)buf.buf, buf.len + 1 );
     5         kx         ctx.promo.donate_js = (const char *)js;
     5         kx         strbuf_release( &buf );
     5         kx       }
     5         kx     }
     5         kx     var = lookup_global( lookup_global_section( config ), &donate_header );
     5         kx     if( var )
     5         kx     {
     5         kx       ctx.promo.donate_header = (const char *)var->_v.vptr;
     5         kx     }
     5         kx     var = lookup_global( lookup_global_section( config ), &donate_purpose );
     5         kx     if( var )
     5         kx     {
     5         kx       ctx.promo.donate_purpose = (const char *)var->_v.vptr;
     5         kx     }
     5         kx 
     5         kx     var = lookup_global( lookup_global_section( config ), &donate_html );
     5         kx     if( var )
     5         kx     {
     5         kx       ctx.promo.donate_html = (const char *)var->_v.vptr;
     5         kx 
     5         kx       if( ctx.promo.donate_html && *ctx.promo.donate_html )
     5         kx       {
     5         kx         envtab_install( &strbuf_envtab, "donate-header", ctx.promo.donate_header, fatal_html );
     5         kx         envtab_install( &strbuf_envtab, "donate-purpose", ctx.promo.donate_purpose, fatal_html );
     5         kx         read_donate_html();
     5         kx         envtab_release( &strbuf_envtab );
     5         kx       }
     5         kx     }
     5         kx   }
     5         kx }
     5         kx 
     5         kx static void ctx_repo_dirs_from_repo( struct repo *repo )
     5         kx {
     5         kx   struct variable  git_root  = { (unsigned char *)"git-root",  { 0 }, DT_PATH };
     5         kx   struct variable  repo_root = { (unsigned char *)"repo-root", { 0 }, DT_PATH };
     5         kx   struct variable  trunk     = { (unsigned char *)"trunk",     { 0 }, DT_PATH };
     5         kx   struct variable  ro_prefix = { (unsigned char *)"clone-prefix-readonly", { 0 }, DT_PATH };
     5         kx   struct variable  rw_prefix = { (unsigned char *)"clone-prefix",          { 0 }, DT_PATH };
     5         kx   struct variable *var = NULL;
     5         kx 
     5         kx   if( !repo ) return;
     5         kx 
     5         kx   var = lookup( repo, &git_root );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.repo.git_root = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup( repo, &repo_root );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.repo.repo_root = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup( repo, &trunk );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.repo.trunk = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup( repo, &rw_prefix );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.repo.clone_prefix = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup( repo, &ro_prefix );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.repo.clone_ro_prefix = (const char *)var->_v.vptr;
     5         kx   }
     5         kx }
     5         kx 
     5         kx static void ctx_site_vars_from_repo( struct repo *repo )
     5         kx {
     5         kx   struct variable                   css = { (unsigned char *)"css",                  { 0 }, DT_PATH   };
     5         kx   struct variable                 owner = { (unsigned char *)"owner",                { 0 }, DT_STRING };
     5         kx   struct variable                author = { (unsigned char *)"author",               { 0 }, DT_STRING };
     5         kx   struct variable                 title = { (unsigned char *)"title",                { 0 }, DT_STRING };
     5         kx   struct variable           description = { (unsigned char *)"description",          { 0 }, DT_STRING };
     5         kx   struct variable              keywords = { (unsigned char *)"keywords",             { 0 }, DT_STRING };
     5         kx   struct variable      copyright_notice = { (unsigned char *)"copyright-notice",     { 0 }, DT_STRING };
     5         kx   struct variable             copyright = { (unsigned char *)"copyright",            { 0 }, DT_STRING };
     5         kx   struct variable          favicon_path = { (unsigned char *)"favicon-path",         { 0 }, DT_PATH   };
     5         kx   struct variable                  logo = { (unsigned char *)"logo",                 { 0 }, DT_PATH   };
     5         kx   struct variable              logo_alt = { (unsigned char *)"logo-alt",             { 0 }, DT_STRING };
     5         kx   struct variable             logo_link = { (unsigned char *)"logo-link",            { 0 }, DT_STRING };
     5         kx   struct variable             home_page = { (unsigned char *)"home-page",            { 0 }, DT_STRING };
     5         kx   struct variable             snapshots = { (unsigned char *)"snapshots",            { 0 }, DT_PATH   };
     5         kx   struct variable        main_menu_logo = { (unsigned char *)"main-menu-logo",       { 0 }, DT_PATH   };
     5         kx   struct variable  syntax_highlight_css = { (unsigned char *)"syntax-highlight-css", { 0 }, DT_PATH   };
     5         kx   struct variable *var = NULL;
     5         kx 
     5         kx   if( !repo ) return;
     5         kx 
     5         kx   var = lookup( repo, &css );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.css = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup( repo, &owner );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.owner = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup( repo, &author );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.author = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup( repo, &title );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.title = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup( repo, &description );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.description = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup( repo, &keywords );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.keywords = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup( repo, &copyright_notice );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.copyright_notice = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup( repo, &copyright );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.copyright = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup( repo, &favicon_path );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.favicon_path = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup( repo, &logo );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.logo = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup( repo, &logo_alt );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.logo_alt = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup( repo, &logo_link );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.logo_link = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup( repo, &home_page );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.home_page = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup( repo, &snapshots );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.snapshots = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup( repo, &main_menu_logo );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.main_menu_logo = (const char *)var->_v.vptr;
     5         kx   }
     5         kx   var = lookup( repo, &syntax_highlight_css );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.vars.syntax_highlight_css = (const char *)var->_v.vptr;
     5         kx   }
     5         kx }
     5         kx 
     5         kx static void ctx_promo_vars_from_repo( struct repo *repo )
     5         kx {
     5         kx   struct variable  analytic_links   = { (unsigned char *)"analytic-links",   { 0 }, DT_PATH      };
     5         kx   struct variable  analytic_scripts = { (unsigned char *)"analytic-scripts", { 0 }, DT_PATH      };
     5         kx   struct variable  donate           = { (unsigned char *)"donate",           { 0 }, DT_NUMERICAL };
     5         kx   struct variable  donate_css       = { (unsigned char *)"donate-css",       { 0 }, DT_PATH      };
     5         kx   struct variable  donate_html      = { (unsigned char *)"donate-html",      { 0 }, DT_PATH      };
     5         kx   struct variable  donate_js        = { (unsigned char *)"donate-js",        { 0 }, DT_PATH      };
     5         kx   struct variable  donate_header    = { (unsigned char *)"donate-header",    { 0 }, DT_STRING    };
     5         kx   struct variable  donate_purpose   = { (unsigned char *)"donate-purpose",   { 0 }, DT_STRING    };
     5         kx   struct variable *var = NULL;
     5         kx 
     5         kx   /*
     5         kx     Analytics reads from global section only:
     5         kx    */
     5         kx   var = lookup_global( lookup_global_section( config ), &analytic_links );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.promo.analytic_links = (const char *)var->_v.vptr;
     5         kx 
     5         kx     if( ctx.promo.analytic_links && *ctx.promo.analytic_links )
     5         kx     {
     5         kx       struct strbuf buf = STRBUF_INIT;
     5         kx       char  *links = NULL;
     5         kx 
     5         kx       (void)read_http_root_raw_file( &buf, ctx.promo.analytic_links );
     5         kx 
     5         kx       links = (char *)__sbrk( (int)buf.len + 1 );
     5         kx       memcpy( (void *)links, (const void *)buf.buf, buf.len + 1 );
     5         kx       ctx.promo.analytic_links = (const char *)links;
     5         kx       strbuf_release( &buf );
     5         kx     }
     5         kx   }
     5         kx   var = lookup_global( lookup_global_section( config ), &analytic_scripts );
     5         kx   if( var )
     5         kx   {
     5         kx     ctx.promo.analytic_scripts = (const char *)var->_v.vptr;
     5         kx 
     5         kx     if( ctx.promo.analytic_scripts && *ctx.promo.analytic_scripts )
     5         kx     {
     5         kx       struct strbuf buf = STRBUF_INIT;
     5         kx       char  *scripts = NULL;
     5         kx 
     5         kx       (void)read_http_root_raw_file( &buf, ctx.promo.analytic_scripts );
     5         kx 
     5         kx       scripts = (char *)__sbrk( (int)buf.len + 1 );
     5         kx       memcpy( (void *)scripts, (const void *)buf.buf, buf.len + 1 );
     5         kx       ctx.promo.analytic_scripts = (const char *)scripts;
     5         kx       strbuf_release( &buf );
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   var = lookup( repo, &donate );
     5         kx   if( var )
     5         kx   {
     5         kx     if( var->type == DT_NUMERICAL )
     5         kx     {
     5         kx       ctx.promo.donate = var->_v.val;
     5         kx       if( ctx.promo.donate )
     5         kx         ctx.promo.donate = 1;
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   if( ctx.promo.donate )
     5         kx   {
     5         kx     var = lookup( repo, &donate_css );
     5         kx     if( var )
     5         kx     {
     5         kx       ctx.promo.donate_css = (const char *)var->_v.vptr;
     5         kx 
     5         kx       if( ctx.promo.donate_css && *ctx.promo.donate_css )
     5         kx       {
     5         kx         struct strbuf buf = STRBUF_INIT;
     5         kx         char  *css = NULL;
     5         kx 
     5         kx         strbuf_addf( &buf, "<link rel=\"stylesheet\" href=\"%s\">", ctx.promo.donate_css );
     5         kx         strbuf_trim( &buf );
     5         kx         css = (char *)__sbrk( (int)buf.len + 1 );
     5         kx         memcpy( (void *)css, (const void *)buf.buf, buf.len + 1 );
     5         kx         ctx.promo.donate_css = (const char *)css;
     5         kx         strbuf_release( &buf );
     5         kx       }
     5         kx     }
     5         kx     var = lookup( repo, &donate_js );
     5         kx     if( var )
     5         kx     {
     5         kx       ctx.promo.donate_js = (const char *)var->_v.vptr;
     5         kx 
     5         kx       if( ctx.promo.donate_js && *ctx.promo.donate_js )
     5         kx       {
     5         kx         struct strbuf buf = STRBUF_INIT;
     5         kx         char  *js = NULL;
     5         kx 
     5         kx         strbuf_addf( &buf, "<script src=\"%s\"></script>", ctx.promo.donate_js );
     5         kx         strbuf_trim( &buf );
     5         kx         js = (char *)__sbrk( (int)buf.len + 1 );
     5         kx         memcpy( (void *)js, (const void *)buf.buf, buf.len + 1 );
     5         kx         ctx.promo.donate_js = (const char *)js;
     5         kx         strbuf_release( &buf );
     5         kx       }
     5         kx     }
     5         kx     var = lookup( repo, &donate_header );
     5         kx     if( var )
     5         kx     {
     5         kx       ctx.promo.donate_header = (const char *)var->_v.vptr;
     5         kx     }
     5         kx     var = lookup( repo, &donate_purpose );
     5         kx     if( var )
     5         kx     {
     5         kx       ctx.promo.donate_purpose = (const char *)var->_v.vptr;
     5         kx     }
     5         kx     var = lookup( repo, &donate_html );
     5         kx     if( var )
     5         kx     {
     5         kx       ctx.promo.donate_html = (const char *)var->_v.vptr;
     5         kx 
     5         kx       if( ctx.promo.donate_html && *ctx.promo.donate_html )
     5         kx       {
     5         kx         envtab_install( &strbuf_envtab, "donate-header", ctx.promo.donate_header, fatal_html );
     5         kx         envtab_install( &strbuf_envtab, "donate-purpose", ctx.promo.donate_purpose, fatal_html );
     5         kx         read_donate_html();
     5         kx         envtab_release( &strbuf_envtab );
     5         kx       }
     5         kx     }
     5         kx   }
     5         kx }
     5         kx 
     5         kx 
     5         kx 
     5         kx static char nrepos[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
     5         kx 
     5         kx static void ctx_num_of_repos( void )
     5         kx {
     5         kx   int32_t  num = repolist_length( config );
     5         kx   num &= 0x7fffffff;
     5         kx   if( num > 0 )
     5         kx   {
     5         kx     snprintf( &nrepos[0], 11, "%d", num );
     5         kx     ctx.vars.num_of_repos = &nrepos[0];
     5         kx   }
     5         kx }
     5         kx 
     5         kx static void ctx_repolist_status_line( void )
     5         kx {
     5         kx   struct strbuf buf = STRBUF_INIT;
     5         kx   char    *nrepos_string = NULL;
     5         kx   char    *status_line   = NULL;
     5         kx   int32_t  num = 0;
     5         kx 
     5         kx   sscanf( ctx.vars.num_of_repos, "%d", &num );
     5         kx 
     5         kx   strbuf_addf( &buf, Q_("%d Git Repository", "%d Git Repositories", num), num );
     5         kx   nrepos_string = (char *)__sbrk( (int)buf.len + 1 );
     5         kx   memcpy( (void *)nrepos_string, (const void *)buf.buf, buf.len + 1 );
     5         kx   strbuf_release( &buf );
     5         kx 
     5         kx   buf.alloc = 0, buf.len = 0, buf.fatal = strbuf_fatal, buf.buf = strbuf_slopbuf;
     5         kx 
     5         kx   strbuf_addf( &buf, "<span class=\"las la-cloud-upload\"></span> %s\n", nrepos_string );
     5         kx   if( ctx.promo.donate )
     5         kx     strbuf_addf( &buf, "&nbsp; | <a class=\"donate\"><span class=\"icon las la-credit-card\"></span> Donate</a>\n" );
     5         kx 
     5         kx   status_line = (char *)__sbrk( (int)buf.len + 1 );
     5         kx   memcpy( (void *)status_line, (const void *)buf.buf, buf.len + 1 );
     5         kx   strbuf_release( &buf );
     5         kx 
     5         kx   ctx.vars.status_line = (const char *)status_line;
     5         kx }
     5         kx 
     5         kx static void ctx_repo_status_line( struct cgit_repository *rctx )
     5         kx {
     5         kx   struct strbuf buf      = STRBUF_INIT;
     5         kx   struct strbuf branches = STRBUF_INIT;
     5         kx   struct strbuf commits  = STRBUF_INIT;
     5         kx   struct strbuf tags     = STRBUF_INIT;
     5         kx   char  *status_line = NULL;
     5         kx 
     5         kx   if( !rctx ) return;
     5         kx 
     5         kx   strbuf_addf( &commits, Q_("%d Commit", "%d Commits", rctx->ncommits), rctx->ncommits );
     5         kx   strbuf_addf( &branches, Q_("%d Branch", "%d Branches", rctx->nbranches), rctx->nbranches );
     5         kx   strbuf_addf( &tags, Q_("%d Tag", "%d Tags", rctx->ntags), rctx->ntags );
     5         kx 
     5         kx   if( rctx->ncommits == 9999 )
     5         kx     strbuf_addf( &buf, "<span class=\"las la-code-commit\"></span> More than %s &nbsp;\n", commits.buf );
     5         kx   else
     5         kx     strbuf_addf( &buf, "<span class=\"las la-code-commit\"></span> %s &nbsp;\n", commits.buf );
     5         kx   strbuf_addf( &buf, "<span class=\"las la-code-branch\"></span> %s &nbsp;\n", branches.buf );
     5         kx   strbuf_addf( &buf, "<span class=\"las la-tags\"></span> %s\n", tags.buf );
     5         kx   if( ctx.promo.donate )
     5         kx     strbuf_addf( &buf, "&nbsp; | <a class=\"donate\"><span class=\"icon las la-credit-card\"></span> Donate</a>\n" );
     5         kx 
     5         kx   strbuf_release( &branches );
     5         kx   strbuf_release( &commits );
     5         kx   strbuf_release( &tags );
     5         kx 
     5         kx   status_line = (char *)__sbrk( (int)buf.len + 1 );
     5         kx   memcpy( (void *)status_line, (const void *)buf.buf, buf.len + 1 );
     5         kx   strbuf_release( &buf );
     5         kx 
     5         kx   ctx.vars.status_line = (const char *)status_line;
     5         kx }
     5         kx 
     5         kx static void ctx_repolist_menu( void )
     5         kx {
     5         kx   struct strbuf mmenu = STRBUF_INIT;
     5         kx   struct strbuf popup = STRBUF_INIT;
     5         kx   struct strbuf right = STRBUF_INIT;
     5         kx 
     5         kx   char *main_menu  = NULL;
     5         kx   char *popup_menu = NULL;
     5         kx   char *right_menu = NULL;
     5         kx 
     5         kx   strbuf_addf( &mmenu, "<a href=\"/\">Index</a>\n" );
     5         kx   strbuf_addf( &popup, "<div class=\"item\"><span class=\"icon las la-home\"></span><a href=\"%s\" target=\"_blank\">Home page</a></div>\n", ctx.vars.home_page );
     5         kx   strbuf_addf( &right, "<div class=\"item\"><a href=\"%s\" target=\"_blank\">Home page</a></div>\n", ctx.vars.home_page );
     5         kx 
     5         kx   main_menu = (char *)__sbrk( (int)mmenu.len + 1 );
     5         kx   memcpy( (void *)main_menu, (const void *)mmenu.buf, mmenu.len + 1 );
     5         kx   strbuf_release( &mmenu );
     5         kx   ctx.vars.main_menu_item = (const char *)main_menu;
     5         kx 
     5         kx   popup_menu = (char *)__sbrk( (int)popup.len + 1 );
     5         kx   memcpy( (void *)popup_menu, (const void *)popup.buf, popup.len + 1 );
     5         kx   strbuf_release( &popup );
     5         kx   ctx.vars.popup_menu_items = (const char *)popup_menu;
     5         kx 
     5         kx   right_menu = (char *)__sbrk( (int)right.len + 1 );
     5         kx   memcpy( (void *)right_menu, (const void *)right.buf, right.len + 1 );
     5         kx   strbuf_release( &right );
     5         kx   ctx.vars.right_menu_items = (const char *)right_menu;
     5         kx }
     5         kx 
     5         kx static void ctx_repo_menu( struct cgit_repository *rctx )
     5         kx {
     5         kx   struct strbuf mmenu = STRBUF_INIT;
     5         kx   struct strbuf left  = STRBUF_INIT;
     5         kx   struct strbuf popup = STRBUF_INIT;
     5         kx   struct strbuf right = STRBUF_INIT;
     5         kx 
     5         kx   char *main_menu  = NULL;
     5         kx   char *left_menu  = NULL;
     5         kx   char *popup_menu = NULL;
     5         kx   char *right_menu = NULL;
     5         kx 
     5         kx   if( !rctx ) return;
     5         kx 
     5         kx   strbuf_addf( &mmenu, "<a href=\"/\">Index</a>\n" );
     5         kx 
     5         kx   strbuf_addf( &left, "<div class=\"item\"><a href=\"/%s/%s/\">Trunk</a></div>\n", rctx->name, "trunk" );
     5         kx   strbuf_addf( &left, "<div class=\"item\"><a href=\"/%s/%s/\">Branches</a></div>\n", rctx->name, "branches" );
     5         kx   strbuf_addf( &left, "<div class=\"item\"><a href=\"/%s/%s/\">Tags</a></div>\n", rctx->name, "tags" );
     5         kx 
     5         kx   strbuf_addf( &popup, "<div class=\"item\"><span class=\"icon las la-road\"></span><a href=\"/%s/%s/\">Trunk</a></div>\n", rctx->name, "trunk" );
     5         kx   strbuf_addf( &popup, "<div class=\"item\"><span class=\"icon las la-code-branch\"></span><a href=\"/%s/%s/\">Branches</a></div>\n", rctx->name, "branches" );
     5         kx   strbuf_addf( &popup, "<div class=\"item\"><span class=\"icon las la-tags\"></span><a href=\"/%s/%s/\">Tags</a></div>\n", rctx->name, "tags" );
     5         kx   strbuf_addf( &popup, "<div class=\"divider\"></div>\n" );
     5         kx   strbuf_addf( &popup, "<div class=\"item\"><span class=\"icon las la-home\"></span><a href=\"%s\" target=\"_blank\">Home page</a></div>\n", ctx.vars.home_page );
     5         kx 
     5         kx   strbuf_addf( &right, "<div class=\"item\"><a href=\"%s\" target=\"_blank\">Home page</a></div>\n", ctx.vars.home_page );
     5         kx 
     5         kx   main_menu = (char *)__sbrk( (int)mmenu.len + 1 );
     5         kx   memcpy( (void *)main_menu, (const void *)mmenu.buf, mmenu.len + 1 );
     5         kx   strbuf_release( &mmenu );
     5         kx   ctx.vars.main_menu_item = (const char *)main_menu;
     5         kx 
     5         kx   left_menu = (char *)__sbrk( (int)left.len + 1 );
     5         kx   memcpy( (void *)left_menu, (const void *)left.buf, left.len + 1 );
     5         kx   strbuf_release( &left );
     5         kx   ctx.vars.left_menu_items = (const char *)left_menu;
     5         kx 
     5         kx   popup_menu = (char *)__sbrk( (int)popup.len + 1 );
     5         kx   memcpy( (void *)popup_menu, (const void *)popup.buf, popup.len + 1 );
     5         kx   strbuf_release( &popup );
     5         kx   ctx.vars.popup_menu_items = (const char *)popup_menu;
     5         kx 
     5         kx   right_menu = (char *)__sbrk( (int)right.len + 1 );
     5         kx   memcpy( (void *)right_menu, (const void *)right.buf, right.len + 1 );
     5         kx   strbuf_release( &right );
     5         kx   ctx.vars.right_menu_items = (const char *)right_menu;
     5         kx }
     5         kx 
     5         kx 
     5         kx static void ctx_header_from_global( void )
     5         kx {
     5         kx   struct strbuf   buf  = STRBUF_INIT;
     5         kx   struct variable head = { (unsigned char *)"header", { 0 }, DT_PATH }, *var = NULL;
     5         kx   char  *pheader = NULL;
     5         kx 
     5         kx   var = lookup_global( lookup_global_section( config ), &head );
     5         kx   strbuf_selfdir( &buf );
     5         kx 
     5         kx   if( var ) { strbuf_addf( &buf, "%s", (const char *)var->_v.vptr ); }
     5         kx   else      { strbuf_addf( &buf, "%s", ctx.page.header );            }
     5         kx 
     5         kx   pheader = (char *)__sbrk( (int)buf.len + 1 );
     5         kx   sprintf( pheader, "%s", buf.buf );
     5         kx   strbuf_release( &buf );
     5         kx   ctx.page.header = (const char *)pheader;
     5         kx }
     5         kx 
     5         kx static void ctx_header_from_repo( struct repo *repo )
     5         kx {
     5         kx   struct strbuf   buf  = STRBUF_INIT;
     5         kx   struct variable head = { (unsigned char *)"header", { 0 }, DT_PATH }, *var = NULL;
     5         kx   char  *pheader = NULL;
     5         kx 
     5         kx   var = lookup( repo, &head );
     5         kx   strbuf_selfdir( &buf );
     5         kx 
     5         kx   if( var ) { strbuf_addf( &buf, "%s", (const char *)var->_v.vptr ); }
     5         kx   else      { strbuf_addf( &buf, "%s", ctx.page.header );            }
     5         kx 
     5         kx   pheader = (char *)__sbrk( (int)buf.len + 1 );
     5         kx   sprintf( pheader, "%s", buf.buf );
     5         kx   strbuf_release( &buf );
     5         kx   ctx.page.header = (const char *)pheader;
     5         kx }
     5         kx 
     5         kx 
     5         kx static void ctx_footer_from_global( void )
     5         kx {
     5         kx   struct strbuf   buf  = STRBUF_INIT;
     5         kx   struct variable foot = { (unsigned char *)"footer", { 0 }, DT_PATH }, *var = NULL;
     5         kx   char  *pfooter = NULL;
     5         kx 
     5         kx   var = lookup_global( lookup_global_section( config ), &foot );
     5         kx   strbuf_selfdir( &buf );
     5         kx 
     5         kx   if( var ) { strbuf_addf( &buf, "%s", (const char *)var->_v.vptr ); }
     5         kx   else      { strbuf_addf( &buf, "%s", ctx.page.footer );            }
     5         kx 
     5         kx   pfooter = (char *)__sbrk( (int)buf.len + 1 );
     5         kx   sprintf( pfooter, "%s", buf.buf );
     5         kx   strbuf_release( &buf );
     5         kx   ctx.page.footer = (const char *)pfooter;
     5         kx }
     5         kx 
     5         kx static void ctx_footer_from_repo( struct repo *repo )
     5         kx {
     5         kx   struct strbuf   buf  = STRBUF_INIT;
     5         kx   struct variable foot = { (unsigned char *)"footer", { 0 }, DT_PATH }, *var = NULL;
     5         kx   char  *pfooter = NULL;
     5         kx 
     5         kx   var = lookup( repo, &foot );
     5         kx   strbuf_selfdir( &buf );
     5         kx 
     5         kx   if( var ) { strbuf_addf( &buf, "%s", (const char *)var->_v.vptr ); }
     5         kx   else      { strbuf_addf( &buf, "%s", ctx.page.footer );            }
     5         kx 
     5         kx   pfooter = (char *)__sbrk( (int)buf.len + 1 );
     5         kx   sprintf( pfooter, "%s", buf.buf );
     5         kx   strbuf_release( &buf );
     5         kx   ctx.page.footer = (const char *)pfooter;
     5         kx }
     5         kx 
     5         kx 
     5         kx static char name[PATH_MAX] = { 0 };
     5         kx static char repo_root[PATH_MAX] = { 0 };
     5         kx static char relative_path[PATH_MAX] = { 0 };
     5         kx 
     5         kx static char try[PATH_MAX] = { 0 };
     5         kx 
     5         kx static struct repo *ctx_repo_name( void )
     5         kx {
     5         kx   struct repo *repo = NULL;
     5         kx   char *s, *p = NULL, *n, *path, *path_info;
     5         kx   int   len = 0;
     5         kx 
     5         kx   if( strcmp( "/", ctx.env.path_info ) )
     5         kx   {
     5         kx     path_info = xstrdup( ctx.env.path_info );
     5         kx     s = path_info;
     5         kx 
     5         kx     while( *s )
     5         kx     {
     5         kx       while( *s && is_dir_sep( *s ) )
     5         kx         ++s;
     5         kx       n = p = s;
     5         kx 
     5         kx       while( *p && !is_dir_sep( *p ) )
     5         kx         ++p;
     5         kx       if( *p )
     5         kx       {
     5         kx         *p = '\0'; s = ++p;
     5         kx 
     5         kx         /****************************************************************
     5         kx           The repo name can be given as a relative path in the git-root:
     5         kx          */
     5         kx         if( repo_root[0] )
     5         kx         {
     5         kx           sprintf( try, "%s", repo_root );
     5         kx           strcat( try, "/" );
     5         kx           strcat( try, n );
     5         kx         }
     5         kx         else
     5         kx         {
     5         kx           sprintf( try, "%s", n );
     5         kx         }
     5         kx 
     5         kx         if( (repo = lookup_repo( config, try )) )
     5         kx         {
     5         kx           sprintf( name, "%s", try );
     5         kx           {
     5         kx             char *rr = strstr( repo_root, try );
     5         kx             if( rr )
     5         kx               *rr = '\0';
     5         kx             else
     5         kx               repo_root[0] = '\0';
     5         kx           }
     5         kx           break;
     5         kx         }
     5         kx         else
     5         kx         {
     5         kx           if( repo_root[0] )
     5         kx             strcat( repo_root, "/" );
     5         kx           strcat( repo_root, n );
     5         kx         }
     5         kx       }
     5         kx       else
     5         kx         s = p;
     5         kx     }
     5         kx 
     5         kx     ctx.repo.name = (const char *)&name[0];
     5         kx     ctx.repo.repo_root = (const char *)&repo_root[0];
     5         kx 
     5         kx     path = p;
     5         kx 
     5         kx     if( *path )
     5         kx     {
     5         kx       len = (int)strlen( path );
     5         kx 
     5         kx       if( path[len-1] =='/' ) { path[len-1] = '\0'; --len; }
     5         kx       len += 1;
     5         kx 
     5         kx       sprintf( relative_path, "%s", path );
     5         kx       ctx.repo.relative_path = (const char *)&relative_path[0];
     5         kx     }
     5         kx 
     5         kx     free( path_info );
     5         kx   }
     5         kx 
     5         kx   return repo;
     5         kx }
     5         kx 
     5         kx 
     5         kx /*************************************************************
     5         kx   Get integer value of query parameter 'name' and remove this
     5         kx   parameter 'name=..' from ctx.env.query_string.
     5         kx  */
     5         kx int ctx_grab_int_query_param( const char *name )
     5         kx {
     5         kx   char *pofs = NULL, *query_string, *s = (char *)ctx.env.query_string;
     5         kx   int   ret = 0, len = 0;
     5         kx 
     5         kx   if( !name || !*name ) return ret;
     5         kx 
     5         kx   if( s && *s && (pofs = strstr( s, name )) )
     5         kx   {
     5         kx     char *rem, *p, *val = NULL;
     5         kx     struct strbuf buf = STRBUF_INIT;
     5         kx 
     5         kx     p = pofs;
     5         kx 
     5         kx     /* move to end of 'ofs=val' declaration: */
     5         kx     while( *p && *p != '&' && *p != '=' ) ++p;
     5         kx     if( *p == '=' ) { val = ++p; while( *p && *p != '&' ) ++p; }
     5         kx     if( *p ) { *p = '\0'; rem = ++p; }
     5         kx     else
     5         kx     {
     5         kx       rem = p;
     5         kx       if( s < pofs && (pofs[-1] == '&' || pofs[-1] == '=') ) --pofs;
     5         kx     }
     5         kx 
     5         kx     /* fill buffer: */
     5         kx     while( s < pofs ) { strbuf_addch( &buf, *s ); ++s; }
     5         kx     s = rem;
     5         kx     while( *s ) { strbuf_addch( &buf, *s ); ++s; }
     5         kx     strbuf_addch( &buf, '\0' );
     5         kx 
     5         kx     /* get value: */
     5         kx     if( val && *val ) { ret = atoi( val ); }
     5         kx     else              { ret = 0;           }
     5         kx 
     5         kx     len = (int)buf.len;
     5         kx     query_string = (char *)__sbrk( len );
     5         kx     memcpy( (void *)query_string, (const void *)buf.buf, (size_t)len );
     5         kx     strbuf_release( &buf );
     5         kx     ctx.env.query_string = (const char *)query_string;
     5         kx   }
     5         kx 
     5         kx   return ret;
     5         kx }
     5         kx 
     5         kx /*************************************************************
     5         kx   Get string value of query parameter 'name' and remove this
     5         kx   parameter 'name=..' from ctx.env.query_string.
     5         kx  */
     5         kx const char *ctx_grab_str_query_param( const char *name )
     5         kx {
     5         kx   const char *ret = NULL;
     5         kx   char *pofs = NULL, *query_string, *s = (char *)ctx.env.query_string;
     5         kx   int   len = 0;
     5         kx 
     5         kx   if( !name || !*name ) return ret;
     5         kx 
     5         kx   if( s && *s && (pofs = strstr( s, name )) )
     5         kx   {
     5         kx     char *rem, *p, *val = NULL;
     5         kx     struct strbuf buf = STRBUF_INIT;
     5         kx 
     5         kx     p = pofs;
     5         kx 
     5         kx     /* move to end of 'ofs=val' declaration: */
     5         kx     while( *p && *p != '&' && *p != '=' ) ++p;
     5         kx     if( *p == '=' ) { val = ++p; while( *p && *p != '&' ) ++p; }
     5         kx     if( *p ) { *p = '\0'; rem = ++p; }
     5         kx     else
     5         kx     {
     5         kx       rem = p;
     5         kx       if( s < pofs && (pofs[-1] == '&' || pofs[-1] == '=') ) --pofs;
     5         kx     }
     5         kx 
     5         kx     /* fill buffer: */
     5         kx     while( s < pofs ) { strbuf_addch( &buf, *s ); ++s; }
     5         kx     s = rem;
     5         kx     while( *s ) { strbuf_addch( &buf, *s ); ++s; }
     5         kx     strbuf_addch( &buf, '\0' );
     5         kx 
     5         kx     /* get value: */
     5         kx     if( val && *val )
     5         kx     {
     5         kx       char *value = NULL;
     5         kx 
     5         kx       len = (int)strlen( val ) + 1;
     5         kx       value = (char *)__sbrk( len );
     5         kx       memcpy( (void *)value, (const void *)val, (size_t)len );
     5         kx 
     5         kx       ret = (const char *)value;
     5         kx     }
     5         kx 
     5         kx     len = (int)buf.len;
     5         kx     query_string = (char *)__sbrk( len );
     5         kx     memcpy( (void *)query_string, (const void *)buf.buf, (size_t)len );
     5         kx     strbuf_release( &buf );
     5         kx     ctx.env.query_string = (const char *)query_string;
     5         kx   }
     5         kx 
     5         kx   return ret;
     5         kx }
     5         kx 
     5         kx /***************************************************************
     5         kx   Rmove parametr with specified 'name=...' fom query string and
     5         kx   return modified query string allocated in context _mem[].
     5         kx  */
     5         kx const char *ctx_remove_query_param( const char *query_string, const char *name )
     5         kx {
     5         kx   char *pofs = NULL, *query = NULL, *s = (char *)query_string;
     5         kx   int len = 0;
     5         kx 
     5         kx   if( !name || !*name ) return (const char *)query;
     5         kx 
     5         kx   if( s && *s && (pofs = strstr( s, name )) )
     5         kx   {
     5         kx     char *rem, *p;
     5         kx     struct strbuf buf = STRBUF_INIT;
     5         kx 
     5         kx     p = pofs;
     5         kx 
     5         kx     /* move to end of 'ofs=val' declaration: */
     5         kx     while( *p && *p != '&' && *p != '=' ) ++p;
     5         kx     if( *p == '=' ) { while( *p && *p != '&' ) ++p; }
     5         kx     if( *p ) { *p = '\0'; rem = ++p; }
     5         kx     else
     5         kx     {
     5         kx       rem = p;
     5         kx       if( s < pofs && (pofs[-1] == '&' || pofs[-1] == '=') ) --pofs;
     5         kx     }
     5         kx 
     5         kx     /* fill buffer: */
     5         kx     while( s < pofs ) { strbuf_addch( &buf, *s ); ++s; }
     5         kx     s = rem;
     5         kx     while( *s ) { strbuf_addch( &buf, *s ); ++s; }
     5         kx     strbuf_addch( &buf, '\0' );
     5         kx 
     5         kx     len = (int)buf.len;
     5         kx     query = (char *)__sbrk( len );
     5         kx     memcpy( (void *)query, (const void *)buf.buf, (size_t)len );
     5         kx     strbuf_release( &buf );
     5         kx     return (const char *)query;
     5         kx   }
     5         kx 
     5         kx   return (const char *)query;
     5         kx }
     5         kx 
     5         kx 
     5         kx static void ctx_search_placeholder( void )
     5         kx {
     5         kx   struct strbuf buf = STRBUF_INIT;
     5         kx   char  *revision;
     5         kx   int    len;
     5         kx 
     5         kx   if( !strcmp( ctx.vars.page_type, ptype_repo ) )
     5         kx   {
     5         kx     if( ctx.query.rev && strcmp( ctx.query.rev, "0" ) )
     5         kx       strbuf_addf( &buf, "%0.12s...", ctx.query.rev );
     5         kx     else
     5         kx       strbuf_addf( &buf, "%0.12s...", ctx.repo.info.revision );
     5         kx 
     5         kx     len = (int)strlen( buf.buf ) + 1;
     5         kx     revision = (char *)__sbrk( len );
     5         kx     memcpy( (void *)revision, (const void *)buf.buf, (size_t)len );
     5         kx     strbuf_release( &buf );
     5         kx 
     5         kx     ctx.repo.search_placeholder = (const char *)revision;
     5         kx     /* ctx.repo.search_placeholder = "HEAD"; */
     5         kx   }
     5         kx }
     5         kx 
     5         kx static void ctx_relative_html( void )
     5         kx {
     5         kx   struct strbuf buf  = STRBUF_INIT;
     5         kx   char  *relative_html;
     5         kx   int    len;
     5         kx 
     5         kx   if( !strcmp( ctx.vars.page_type, ptype_repolist ) )
     5         kx   {
     5         kx     if( ctx.env.http_host && *ctx.env.http_host )
     5         kx     {
     5         kx       strbuf_addf( &buf, "<a class='base' href='/'>%s</a>/", ctx.env.http_host );
     5         kx     }
     5         kx     else if( ctx.env.server_name && *ctx.env.server_name )
     5         kx     {
     5         kx       strbuf_addf( &buf, "<a class='base' href='/'>%s</a>/", ctx.env.server_name );
     5         kx     }
     5         kx   }
     5         kx   else if( !strcmp( ctx.vars.page_type, ptype_repo ) )
     5         kx   {
     5         kx     if( ctx.repo.name && *ctx.repo.name )
     5         kx     {
     5         kx       if( ctx.repo.repo_root && *ctx.repo.repo_root )
     5         kx         strbuf_addf( &buf, "<a class='base' href='/%s/%s/'>%s/%s</a>/", ctx.repo.repo_root, ctx.repo.name, ctx.repo.repo_root, ctx.repo.name );
     5         kx       else
     5         kx         strbuf_addf( &buf, "<a class='base' href='/%s/'>%s</a>/", ctx.repo.name, ctx.repo.name );
     5         kx     }
     5         kx     if( ctx.repo.relative_path && *ctx.repo.relative_path )
     5         kx     {
     5         kx       char   *p, *s, *rem;
     5         kx       int     msize;
     5         kx       char   *path, *href;
     5         kx       size_t  length;
     5         kx 
     5         kx       length = strlen( ctx.repo.relative_path ) + 1;
     5         kx       msize = (int)strlen(ctx.repo.name) + (int)strlen(ctx.repo.relative_path) + 4; /* '/' repo.name '/' relative_path '/' + '\0' */
     5         kx 
     5         kx       href = (char *)__sbrk( msize );
     5         kx       sprintf( href, "/%s/", ctx.repo.name );
     5         kx 
     5         kx       path = (char *)__sbrk( msize );
     5         kx       memcpy( (void *)path, (const void *)ctx.repo.relative_path, length );
     5         kx 
     5         kx       p = s = path;
     5         kx       while( *p && !is_dir_sep( *p ) ) ++p;
     5         kx       if( *p ) { *p = '\0'; rem = ++p; }
     5         kx       else     { rem = p; }
     5         kx       strcat( href, s ); strcat( href, "/" );
     5         kx 
     5         kx       if( !strcmp( s, "trunk" )    ||
     5         kx           !strcmp( s, "branches" ) ||
     5         kx           !strcmp( s, "tags" )       )
     5         kx       {
     5         kx         strbuf_addf( &buf, "<a class='base' href='%s'>%s</a>/", href, s );
     5         kx 
     5         kx         /**********************************************
     5         kx           Searching the real name of branch or tag:
     5         kx          */
     5         kx         if( !strcmp( s, "branches" ) || !strcmp( s, "tags" ) )
     5         kx         {
     5         kx           char  reminder[PATH_MAX] = { 0 };
     5         kx           char *n, *r = (char *)&reminder[0];
     5         kx 
     5         kx           sprintf( reminder, "%s", rem );
     5         kx           n = r;
     5         kx 
     5         kx           while( *r && !is_dir_sep( *r ) )
     5         kx             ++r;
     5         kx           if( *r )
     5         kx           {
     5         kx             *r = '\0'; ++r;
     5         kx           }
     5         kx 
     5         kx           if( n && *n )
     5         kx           {
     5         kx             int   found = 0;
     5         kx             char  ref[PATH_MAX] = { 0 };
     5         kx             char *b;
     5         kx 
     5         kx             struct cgit_ref_names *names = NULL;
     5         kx 
     5         kx             sprintf( ref, "%s", n );
     5         kx 
     5         kx             if( !strcmp( s, "branches" ) )
     5         kx               lookup_branches_by_prefix( &names, n );
     5         kx             else
     5         kx               lookup_tags_by_prefix( &names, n );
     5         kx 
     5         kx             while( *r )
     5         kx             {
     5         kx               b = r;
     5         kx               while( *r && *r != '/' )
     5         kx                 ++r;
     5         kx               if( *r )
     5         kx               {
     5         kx                 *r = '\0'; ++r;
     5         kx               }
     5         kx 
     5         kx               if( b && *b )
     5         kx               {
     5         kx                 char probe[PATH_MAX] = { 0 };
     5         kx                 sprintf( probe, "%s/%s", ref, b );
     5         kx 
     5         kx                 found = 0;
     5         kx                 {
     5         kx                   size_t i, len = strlen( probe );
     5         kx                   for( i = 0; i < names->len; ++i )
     5         kx                   {
     5         kx                     if( !strncmp( probe, names->name[i], len ) )
     5         kx                     {
     5         kx                       ++found;
     5         kx                     }
     5         kx                   }
     5         kx                 }
     5         kx                 if( found == 1 )
     5         kx                 {
     5         kx                   strcat( ref, "/" );
     5         kx                   strcat( ref, b );
     5         kx                   break;
     5         kx                 }
     5         kx                 if( found )
     5         kx                 {
     5         kx                   strcat( ref, "/" );
     5         kx                   strcat( ref, b );
     5         kx                 }
     5         kx               }
     5         kx             }
     5         kx             cgit_ref_names_free( names );
     5         kx 
     5         kx             if( found )
     5         kx             {
     5         kx               strcat( href, ref ); strcat( href, "/" );
     5         kx               strbuf_addf( &buf, "<a class='relative' href='%s'>%s</a>/", href, ref );
     5         kx               rem += r - &reminder[0];
     5         kx             }
     5         kx           }
     5         kx         }
     5         kx         /*
     5         kx           End of searching real name of branch or tag.
     5         kx          **********************************************/
     5         kx       }
     5         kx       else
     5         kx       {
     5         kx         strbuf_addf( &buf, "<a class='relative' href='%s'>%s</a>/", href, s );
     5         kx       }
     5         kx 
     5         kx       s = rem;
     5         kx       while( *s )
     5         kx       {
     5         kx         p = s;
     5         kx         while( *p && !is_dir_sep( *p ) ) ++p;
     5         kx         if( *p ) { *p = '\0'; rem = ++p; }
     5         kx         else     { rem = p; }
     5         kx         strcat( href, s ); strcat( href, "/" );
     5         kx         strbuf_addf( &buf, "<a class='relative' href='%s'>%s</a>/", href, s );
     5         kx         s = rem;
     5         kx       }
     5         kx 
     5         kx       if( ctx.repo.relative_info.kind == GIT_OBJECT_BLOB )
     5         kx         strbuf_trim_trailing_dir_sep( &buf );
     5         kx 
     5         kx       ctx.repo.relative_href = (const char *)href;
     5         kx 
     5         kx       (void)__sbrk( - msize ); /* free ctx memory alocated for path only */
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   len = (int)strlen( buf.buf ) + 1;
     5         kx   relative_html = (char *)__sbrk( len );
     5         kx   memcpy( (void *)relative_html, (const void *)buf.buf, (size_t)len );
     5         kx   strbuf_release( &buf );
     5         kx   ctx.repo.relative_html = (const char *)relative_html;
     5         kx }
     5         kx 
     5         kx static void ctx_check_query_uri( int replace_query_string )
     5         kx {
     5         kx   /*
     5         kx     Nginx passed QUERY_STRING for all cgi engines by: {fastcgi, scgi, uwsgi}_params.
     5         kx     But PATH_INFO passed onlyfor UWSGI by uwsgi_params.
     5         kx    */
     5         kx   if( !ctx.env.path_info && ctx.env.request_uri && *ctx.env.request_uri )
     5         kx   {
     5         kx     char *path_info = NULL, *query_string = NULL;
     5         kx     char *p, *path, *query = NULL;
     5         kx     int   len;
     5         kx 
     5         kx     p = path = (char *)ctx.env.request_uri;
     5         kx 
     5         kx     while( *p && *p != '?' ) ++p;
     5         kx     if( *p == '?' ) { *p = '\0'; query = ++p; }
     5         kx 
     5         kx     len = strlen( path ) + 1;
     5         kx     path_info = (char *)__sbrk( len );
     5         kx     memcpy( (void *)path_info, (const void *)path, (size_t)len );
     5         kx     ctx.env.path_info = (const char *)path_info;
     5         kx 
     5         kx     if( query && *query && replace_query_string )
     5         kx     {
     5         kx       len = strlen( query ) + 1;
     5         kx       query_string = (char *)__sbrk( len );
     5         kx       memcpy( (void *)query_string, (const void *)query, (size_t)len );
     5         kx       ctx.env.query_string = (const char *)query_string;
     5         kx     }
     5         kx   }
     5         kx }
     5         kx 
     5         kx 
     5         kx static void querystring_cb( const char *name, const char *value )
     5         kx {
     5         kx   char *val;
     5         kx   int   len;
     5         kx 
     5         kx   if( !value )
     5         kx     value = "";
     5         kx 
     5         kx   if( !strcmp( name, "rev" ) )
     5         kx   {
     5         kx     len = (int)strlen( value ) + 1;
     5         kx     val = (char *)__sbrk( len );
     5         kx     memcpy( (void *)val, (const void *)value, (size_t)len );
     5         kx     ctx.query.revision = (const char *)val;
     5         kx     if( !strcasecmp( val, "HEAD" ) )
     5         kx       ctx.query.rev = "0";
     5         kx     else
     5         kx       ctx.query.rev = ctx.query.revision;
     5         kx     /*****************************************************************************
     5         kx       NOTE:
     5         kx       ====
     5         kx         We have to call http_parse_querystring() strongly after calling
     5         kx         cgit_rpath_info() or ctx_relative_html() because the structs
     5         kx         ctx.repo.info and ctx.repo.relative_info should be completed before
     5         kx         parsing special Git revision names.
     5         kx 
     5         kx         Revision at start of the date '{' DATE '}' we will not support.
     5         kx      *****************************************************************************/
     5         kx   }
     5         kx   else if( !strcmp( name, "op" ) )
     5         kx   {
     5         kx     len = (int)strlen( value ) + 1;
     5         kx     val = (char *)__sbrk( len );
     5         kx     memcpy( (void *)val, (const void *)value, (size_t)len );
     5         kx     ctx.query.operation = (const char *)val;
     5         kx   }
     5         kx   else if( !strcmp( name, "search" ) )
     5         kx   {
     5         kx     len = (int)strlen( value ) + 1;
     5         kx     val = (char *)__sbrk( len );
     5         kx     memcpy( (void *)val, (const void *)value, (size_t)len );
     5         kx     ctx.query.search = (const char *)val;
     5         kx   }
     5         kx /*
     5         kx   else if( another parameter )
     5         kx   {
     5         kx   }
     5         kx   ...
     5         kx  */
     5         kx   /* NOTE: Do not parse 'ofs=' parameter! */
     5         kx }
     5         kx 
     5         kx 
     5         kx void cgit_parse_query( void )
     5         kx {
     5         kx   char *path_info = NULL;
     5         kx 
     5         kx   get_selfdir();
     5         kx 
     5         kx   ctx_check_query_uri( 1 );
     5         kx 
     5         kx   path_info = xstrdup( ctx.env.path_info );
     5         kx 
     5         kx   ctx.query.ofs = ctx_grab_int_query_param( "ofs" );
     5         kx   http_parse_querystring( ctx.env.query_string, querystring_cb );
     5         kx   if( !strcmp( ctx.query.rev, "0" ) )
     5         kx   {
     5         kx     (void)ctx_grab_str_query_param( "rev" );
     5         kx     ctx.query.rev = "0";
     5         kx   }
     5         kx 
     5         kx   if( path_info )
     5         kx   {
     5         kx     if( !strcmp( "/", path_info ) )
     5         kx     {
     5         kx       ctx_page_size_from_global();
     5         kx       ctx_repo_dirs_from_global();
     5         kx       ctx_site_vars_from_global();
     5         kx       ctx_promo_vars_from_global();
     5         kx       ctx_header_from_global();
     5         kx       ctx_footer_from_global();
     5         kx       ctx_num_of_repos();
     5         kx       ctx_repolist_status_line();
     5         kx       ctx_repolist_menu();
     5         kx     }
     5         kx     else
     5         kx     {
     5         kx       struct repo *repo = NULL;
     5         kx 
     5         kx       repo = ctx_repo_name();
     5         kx       if( repo )
     5         kx       {
     5         kx         ctx_page_size_from_repo( repo );
     5         kx         ctx_repo_dirs_from_repo( repo );
     5         kx         ctx_site_vars_from_repo( repo );
     5         kx         ctx_promo_vars_from_repo( repo );
     5         kx         ctx_header_from_repo( repo );
     5         kx         ctx_footer_from_repo( repo );
     5         kx         ctx.vars.page_type = ptype_repo;
     5         kx 
     5         kx         cgit_repo_info( &ctx.repo.info, NULL );
     5         kx         if( ctx.repo.relative_path )
     5         kx         {
     5         kx           cgit_rpath_info( &ctx.repo.relative_info, ctx.repo.relative_path, (!strcmp( ctx.query.rev, "0" )) ? NULL : ctx.query.rev );
     5         kx           if( ctx.repo.relative_info.kind != GIT_OBJECT_TREE && ctx.repo.relative_info.kind != GIT_OBJECT_BLOB )
     5         kx           {
     5         kx             /* Query string requested invalid revision: */
     5         kx             memcpy( (void *)&ctx.repo.relative_info, (const void *)&ctx.repo.info, sizeof( struct cgit_info ) );
     5         kx             ctx.query.revision = NULL;
     5         kx             (void)ctx_grab_str_query_param( "rev" );
     5         kx             ctx.query.rev = "0";
     5         kx           }
     5         kx         }
     5         kx         else
     5         kx           memcpy( (void *)&ctx.repo.relative_info, (const void *)&ctx.repo.info, sizeof( struct cgit_info ) );
     5         kx 
     5         kx         cgit_repo_branches_number( &ctx.repo );
     5         kx         cgit_repo_commits_number( &ctx.repo );
     5         kx         cgit_repo_tags_number( &ctx.repo );
     5         kx         ctx_repo_status_line( &ctx.repo );
     5         kx         ctx_repo_menu( &ctx.repo );
     5         kx       }
     5         kx       else
     5         kx       {
     5         kx         ctx_page_size_from_global();
     5         kx         ctx_repo_dirs_from_global();
     5         kx         ctx_header_from_global();
     5         kx         ctx_footer_from_global();
     5         kx 
     5         kx         ctx.page.status = 404;
     5         kx         ctx.page.status_message = "Page not found";
     5         kx 
     5         kx         ctx.vars.title = "404";
     5         kx         {
     5         kx           struct strbuf  buf = STRBUF_INIT;
     5         kx           char  *stmsg = NULL;
     5         kx 
     5         kx           /**************************************************************************
     5         kx             We slipped to the end of PATH_INFO when we were looking for a repository
     5         kx             and now we printout the repo-root:
     5         kx            */
     5         kx           strbuf_addf( &buf, "Git Repository '%s' not found.", ctx.repo.repo_root );
     5         kx           stmsg = (char *)__sbrk( (int)buf.len + 1 );
     5         kx           memcpy( (void *)stmsg, (const void *)buf.buf, buf.len + 1 );
     5         kx           strbuf_release( &buf );
     5         kx           ctx.vars.description = (const char *)stmsg;
     5         kx         }
     5         kx       }
     5         kx 
     5         kx       ctx_num_of_repos();
     5         kx     }
     5         kx 
     5         kx     ctx_relative_html();
     5         kx     ctx_search_placeholder();
     5         kx 
     5         kx     cgit_git_version( &ctx.vers );
     5         kx     cgit_nginx_version( &ctx.vers );
     5         kx 
     5         kx     free( path_info );
     5         kx   }
     5         kx }