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 <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 #include <nls.h>
     5         kx 
     5         kx #include <defs.h>
     5         kx 
     5         kx #include <strbuf.h>
     5         kx #include <date.h>
     5         kx 
     5         kx 
     5         kx /* Valid rule actions */
     5         kx enum rule_action
     5         kx {
     5         kx   ACCUM,    /* Accumulate a decimal value */
     5         kx   MICRO,    /* Accumulate microseconds */
     5         kx   TZIND,    /* Handle +, -, Z */
     5         kx   NOOP,     /* Do nothing */
     5         kx   SKIPFROM, /* If at end-of-value, accept the match.  Otherwise,
     5         kx                if the next template character matches the current
     5         kx                value character, continue processing as normal.
     5         kx                Otherwise, attempt to complete matching starting
     5         kx                immediately after the first subsequent occurrance of
     5         kx                ']' in the template. */
     5         kx   SKIP,     /* Ignore this template character */
     5         kx   ACCEPT    /* Accept the value */
     5         kx };
     5         kx 
     5         kx /* How to handle a particular character in a template */
     5         kx struct rule
     5         kx {
     5         kx   char              key;    /* The template char that this rule matches */
     5         kx   const char       *valid;  /* String of valid chars for this rule */
     5         kx   enum rule_action  action; /* What action to take when the rule is matched */
     5         kx   int               offset; /* Where to store the any results of the action,
     5         kx                                expressed in terms of bytes relative to the
     5         kx                                base of a match_state object. */
     5         kx };
     5         kx 
     5         kx struct match_state
     5         kx {
     5         kx   struct tm       base;
     5         kx   struct timeval  tv;
     5         kx   int             gmtoff;
     5         kx   int             gmtoff_hours;
     5         kx   int             gmtoff_minutes;
     5         kx };
     5         kx 
     5         kx 
     5         kx #define DIGITS "0123456789"
     5         kx 
     5         kx /*
     5         kx   A declarative specification of how each template character
     5         kx   should be processed, using a rule for each valid symbol.
     5         kx  */
     5         kx static const struct rule rules[] =
     5         kx {
     5         kx   { 'Y', DIGITS,    ACCUM, offsetof(struct match_state, base.tm_year)   },
     5         kx   { 'M', DIGITS,    ACCUM, offsetof(struct match_state, base.tm_mon)    },
     5         kx   { 'D', DIGITS,    ACCUM, offsetof(struct match_state, base.tm_mday)   },
     5         kx   { 'h', DIGITS,    ACCUM, offsetof(struct match_state, base.tm_hour)   },
     5         kx   { 'm', DIGITS,    ACCUM, offsetof(struct match_state, base.tm_min)    },
     5         kx   { 's', DIGITS,    ACCUM, offsetof(struct match_state, base.tm_sec)    },
     5         kx   { 'u', DIGITS,    MICRO, offsetof(struct match_state, tv.tv_usec)     },
     5         kx   { 'O', DIGITS,    ACCUM, offsetof(struct match_state, gmtoff_hours)   },
     5         kx   { 'o', DIGITS,    ACCUM, offsetof(struct match_state, gmtoff_minutes) },
     5         kx   { '+',   "-+",    TZIND, 0 },
     5         kx   { 'Z',    "Z",    TZIND, 0 },
     5         kx   { ':',    ":",     NOOP, 0 },
     5         kx   { '-',    "-",     NOOP, 0 },
     5         kx   { 'T',    "T",     NOOP, 0 },
     5         kx   { ' ',    " ",     NOOP, 0 },
     5         kx   { '.',   ".,",     NOOP, 0 },
     5         kx   { '[',   NULL, SKIPFROM, 0 },
     5         kx   { ']',   NULL,     SKIP, 0 },
     5         kx   { '\0',  NULL,   ACCEPT, 0 },
     5         kx };
     5         kx 
     5         kx /* Return the rule associated with TCHAR, or NULL if there is no such rule. */
     5         kx static const struct rule *find_rule( char tchar )
     5         kx {
     5         kx   int i = sizeof(rules)/sizeof(rules[0]);
     5         kx   while( i-- )
     5         kx     if( rules[i].key == tchar )
     5         kx       return &rules[i];
     5         kx   return NULL;
     5         kx }
     5         kx 
     5         kx /*
     5         kx   Attempt to match the date-string in VALUE to the provided TEMPLATE,
     5         kx   using the rules defined above.  Return TRUE on successful match,
     5         kx   FALSE otherwise.  On successful match, fill in *TM with the
     5         kx   matched values and set *LOCALTZ to GMT-offset if the local time zone
     5         kx   should be used to interpret the match.
     5         kx  */
     5         kx static int template_match( struct tm *tm, int *localtz, const char *template, const char *value )
     5         kx {
     5         kx   int    multiplier = 100000;
     5         kx   int    tzind = 0;
     5         kx   struct match_state  ms;
     5         kx   char  *base = (char *)&ms;
     5         kx 
     5         kx   memset( &ms, 0, sizeof(ms) );
     5         kx 
     5         kx   for( ;; )
     5         kx   {
     5         kx     const struct rule *match = find_rule(*template++);
     5         kx     char vchar = *value++;
     5         kx     int *place;
     5         kx 
     5         kx     if( !match || (match->valid && (!vchar || !strchr(match->valid, vchar))) )
     5         kx       return FALSE;
     5         kx 
     5         kx     /* Compute the address of memory location affected by this
     5         kx        rule by adding match->offset bytes to the address of ms.
     5         kx        Because this is a byte-quantity, it is necessary to cast
     5         kx        &ms to char *. */
     5         kx     place = (int *)(base + match->offset);
     5         kx     switch( match->action )
     5         kx     {
     5         kx       case ACCUM:
     5         kx         *place = *place * 10 + vchar - '0';
     5         kx         continue;
     5         kx       case MICRO:
     5         kx         *place += (vchar - '0') * multiplier;
     5         kx         multiplier /= 10;
     5         kx         continue;
     5         kx       case TZIND:
     5         kx         tzind = vchar;
     5         kx         continue;
     5         kx       case SKIP:
     5         kx         value--;
     5         kx         continue;
     5         kx       case NOOP:
     5         kx         continue;
     5         kx       case SKIPFROM:
     5         kx         if( !vchar )
     5         kx           break;
     5         kx         match = find_rule(*template);
     5         kx         if (!strchr(match->valid, vchar))
     5         kx           template = strchr(template, ']') + 1;
     5         kx         value--;
     5         kx         continue;
     5         kx       case ACCEPT:
     5         kx         if( vchar )
     5         kx           return FALSE;
     5         kx         break;
     5         kx     }
     5         kx 
     5         kx     break;
     5         kx   }
     5         kx 
     5         kx   /* Validate gmt offset here, since we can't reliably do it later. */
     5         kx   if( ms.gmtoff_hours > 23 || ms.gmtoff_minutes > 59 )
     5         kx     return FALSE;
     5         kx 
     5         kx   /*
     5         kx     tzind will be '+' or '-' for an explicit time zone,
     5         kx     'Z' to indicate UTC, or 0 to indicate local time.
     5         kx    */
     5         kx   switch( tzind )
     5         kx   {
     5         kx     case '+':
     5         kx       ms.gmtoff =   ms.gmtoff_hours * 3600 + ms.gmtoff_minutes * 60;
     5         kx       break;
     5         kx     case '-':
     5         kx       ms.gmtoff = -(ms.gmtoff_hours * 3600 + ms.gmtoff_minutes * 60);
     5         kx       break;
     5         kx   }
     5         kx 
     5         kx   *tm = ms.base;
     5         kx   *localtz = ms.gmtoff;
     5         kx   return TRUE;
     5         kx }
     5         kx 
     5         kx static int valid_days_by_month[] =
     5         kx {
     5         kx   31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
     5         kx };
     5         kx 
     5         kx /*
     5         kx   Returns -1 on error,
     5         kx   time_t as the number of seconds since Epoch, 1970-01-01 00:00:00 +0000 (UTC)
     5         kx   on success.
     5         kx  */
     5         kx time_t parse_date( struct tm *tm, const char *text )
     5         kx {
     5         kx   time_t     n, ret = (time_t)-1;
     5         kx   struct tm  pt, *now;
     5         kx   int        localtz;
     5         kx 
     5         kx   n = time( NULL ); /* current UTC time */
     5         kx   now = gmtime( &n );
     5         kx 
     5         kx 
     5         kx   if( /* ISO-8601 extended, date only: */
     5         kx       template_match( &pt, &localtz, "YYYY-M[M]-D[D]", text ) ||
     5         kx       /* ISO-8601 extended, UTC: */
     5         kx       template_match( &pt, &localtz, "YYYY-M[M]-D[D]Th[h]:mm[:ss[.u[u[u[u[u[u][Z]", text ) ||
     5         kx       /* ISO-8601 extended, with offset: */
     5         kx       template_match( &pt, &localtz, "YYYY-M[M]-D[D]Th[h]:mm[:ss[.u[u[u[u[u[u]+OO[:oo]", text ) ||
     5         kx       /* ISO-8601 basic, date only */
     5         kx       template_match( &pt, &localtz, "YYYYMMDD", text ) ||
     5         kx       /* ISO-8601 basic, UTC: */
     5         kx       template_match( &pt, &localtz, "YYYYMMDDThhmm[ss[.u[u[u[u[u[u][Z]", text ) ||
     5         kx       /* ISO-8601 basic, with offset: */
     5         kx       template_match( &pt, &localtz, "YYYYMMDDThhmm[ss[.u[u[u[u[u[u]+OO[oo]", text ) ||
     5         kx       /* "git log" format: */
     5         kx       template_match( &pt, &localtz, "YYYY-M[M]-D[D] h[h]:mm[:ss[.u[u[u[u[u[u][ +OO[oo]", text ) ||
     5         kx       /* GNU date's iso-8601: */
     5         kx       template_match( &pt, &localtz, "YYYY-M[M]-D[D]Th[h]:mm[:ss[.u[u[u[u[u[u]+OO[oo]", text ) )
     5         kx   {
     5         kx     pt.tm_year -= 1900;
     5         kx     pt.tm_mon -= 1;
     5         kx   }
     5         kx   else if( template_match( &pt, &localtz, "h[h]:mm[:ss[.u[u[u[u[u[u]", text) ) /* Just a time */
     5         kx   {
     5         kx     pt.tm_year = now->tm_year;
     5         kx     pt.tm_mon  = now->tm_mon;
     5         kx     pt.tm_mday = now->tm_mday;
     5         kx   }
     5         kx 
     5         kx   /* Range validation, allowing for leap seconds */
     5         kx   if( pt.tm_mon  <  0 ||
     5         kx       pt.tm_mon  > 11 ||
     5         kx       pt.tm_mday > valid_days_by_month[pt.tm_mon] ||
     5         kx       pt.tm_mday <  1 ||
     5         kx       pt.tm_hour > 23 ||
     5         kx       pt.tm_min  > 59 ||
     5         kx       pt.tm_sec  > 60   )
     5         kx     return ret;
     5         kx 
     5         kx   /*
     5         kx     february/leap-year day checking. tm_year is bias-1900, so
     5         kx     centuries that equal 100 (mod 400) are multiples of 400.
     5         kx    */
     5         kx   if( pt.tm_mon  ==  1 &&
     5         kx       pt.tm_mday == 29 &&
     5         kx      (pt.tm_year % 4 != 0 || (pt.tm_year % 100 == 0 && pt.tm_year % 400 != 100)) )
     5         kx     return ret;
     5         kx 
     5         kx   if( localtz )
     5         kx   {
     5         kx     struct tm *gmt = NULL;
     5         kx     time_t     time;
     5         kx 
     5         kx     time = mktime( &pt ); /* brocken-down tm asumed as localtime */
     5         kx     if( time == -1 )
     5         kx       return ret;
     5         kx 
     5         kx     time -= (time_t)localtz;
     5         kx 
     5         kx     gmt = localtime( &time );
     5         kx     if( !gmt )
     5         kx       return ret;
     5         kx 
     5         kx     memcpy( (void *)&pt, (const void *)gmt, sizeof(struct tm) );
     5         kx   }
     5         kx 
     5         kx   memcpy( (void *)tm, (const void *)&pt, sizeof(struct tm) );
     5         kx 
     5         kx   return mktime( &pt );
     5         kx }
     5         kx 
     5         kx 
     5         kx void show_date_relative( struct strbuf *sb, time_t t )
     5         kx {
     5         kx   time_t now, diff;
     5         kx 
     5         kx   if( !sb || !t ) return;
     5         kx 
     5         kx   now = time( NULL );
     5         kx   if( now < t )
     5         kx   {
     5         kx     strbuf_addstr( sb, _("in the future") );
     5         kx     return;
     5         kx   }
     5         kx   diff = now - t;
     5         kx   if( diff < 90 )
     5         kx   {
     5         kx     strbuf_addf( sb, Q_("%"PRIdMAX" second ago", "%"PRIdMAX" seconds ago", diff), diff );
     5         kx     return;
     5         kx   }
     5         kx   /* Turn it into minutes */
     5         kx   diff = (diff + 30) / 60;
     5         kx   if( diff < 90 )
     5         kx   {
     5         kx     strbuf_addf( sb, Q_("%"PRIdMAX" minute ago", "%"PRIdMAX" minutes ago", diff), diff );
     5         kx     return;
     5         kx   }
     5         kx   /* Turn it into hours */
     5         kx   diff = (diff + 30) / 60;
     5         kx   if( diff < 36 )
     5         kx   {
     5         kx     strbuf_addf( sb, Q_("%"PRIdMAX" hour ago", "%"PRIdMAX" hours ago", diff), diff );
     5         kx     return;
     5         kx   }
     5         kx   /* We deal with number of days from here on */
     5         kx   diff = (diff + 12) / 24;
     5         kx   if( diff < 14 )
     5         kx   {
     5         kx     strbuf_addf( sb, Q_("%"PRIdMAX" day ago", "%"PRIdMAX" days ago", diff), diff );
     5         kx     return;
     5         kx   }
     5         kx   /* Say weeks for the past 10 weeks or so */
     5         kx   if( diff < 70 )
     5         kx   {
     5         kx     strbuf_addf( sb, Q_("%"PRIdMAX" week ago", "%"PRIdMAX" weeks ago", (diff+3)/7), (diff+3)/7 );
     5         kx     return;
     5         kx   }
     5         kx   /* Say months for the past 12 months or so */
     5         kx   if( diff < 365 )
     5         kx   {
     5         kx     strbuf_addf( sb, Q_("%"PRIdMAX" month ago", "%"PRIdMAX" months ago", (diff+15)/30), (diff+15)/30 );
     5         kx     return;
     5         kx   }
     5         kx   /* Give years and months for 5 years or so */
     5         kx   if( diff < 1825 )
     5         kx   {
     5         kx     time_t totalmonths = (diff * 12 * 2 + 365) / (365 * 2);
     5         kx     time_t years = totalmonths / 12;
     5         kx     time_t months = totalmonths % 12;
     5         kx     if( months )
     5         kx     {
     5         kx       struct strbuf buf = STRBUF_INIT;
     5         kx       strbuf_addf( &buf, Q_("%"PRIdMAX" year", "%"PRIdMAX" years", years), years );
     5         kx       strbuf_addf( sb,
     5         kx          /* TRANSLATORS: "%s" is "<n> months" */
     5         kx          Q_("%s, %"PRIdMAX" month ago", "%s, %"PRIdMAX" months ago", months), buf.buf, months );
     5         kx       strbuf_release( &buf );
     5         kx     }
     5         kx     else
     5         kx       strbuf_addf( sb, Q_("%"PRIdMAX" year ago", "%"PRIdMAX" years ago", years), years );
     5         kx     return;
     5         kx   }
     5         kx   /* Otherwise, just years. Centuries is probably overkill. */
     5         kx   strbuf_addf( sb, Q_("%"PRIdMAX" year ago", "%"PRIdMAX" years ago", (diff+183)/365), (diff+183)/365 );
     5         kx }
     5         kx 
     5         kx struct date_mode *date_mode_from_type( enum date_mode_type type )
     5         kx {
     5         kx   static struct date_mode mode;
     5         kx   mode.type = type;
     5         kx   mode.local = 0;
     5         kx   return &mode;
     5         kx }
     5         kx 
     5         kx static const char *month_names[] = {
     5         kx   "January", "February", "March", "April", "May", "June",
     5         kx   "July", "August", "September", "October", "November", "December"
     5         kx };
     5         kx 
     5         kx static const char *weekday_names[] = {
     5         kx   "Sundays", "Mondays", "Tuesdays", "Wednesdays", "Thursdays", "Fridays", "Saturdays"
     5         kx };
     5         kx 
     5         kx static time_t gm_time_t( time_t time, int tz )
     5         kx {
     5         kx   int minutes;
     5         kx 
     5         kx   minutes = tz < 0 ? -tz : tz;
     5         kx   minutes = (minutes / 100)*60 + (minutes % 100);
     5         kx   minutes = tz < 0 ? -minutes : minutes;
     5         kx 
     5         kx   time += minutes * 60;
     5         kx 
     5         kx   return time;
     5         kx }
     5         kx 
     5         kx static struct tm *time_to_tm( time_t time, int tz, struct tm *tm )
     5         kx {
     5         kx   time_t t = gm_time_t( time, tz );
     5         kx   return gmtime_r( &t, tm );
     5         kx }
     5         kx 
     5         kx static struct tm *time_to_tm_local( time_t time, struct tm *tm )
     5         kx {
     5         kx   time_t t = time;
     5         kx   return localtime_r( &t, tm );
     5         kx }
     5         kx 
     5         kx /**********************************************************
     5         kx   Fill in the localtime 'struct tm' for the supplied time,
     5         kx   and return the local tz.
     5         kx  */
     5         kx static int local_time_tzoffset( time_t t, struct tm *tm )
     5         kx {
     5         kx   time_t t_local;
     5         kx   int offset, eastwest;
     5         kx 
     5         kx   localtime_r( &t, tm );
     5         kx   t_local = mktime( tm );
     5         kx   if( t_local == -1 )
     5         kx     return 0; /* error; just use +0000 */
     5         kx   if( t_local < t )
     5         kx   {
     5         kx     eastwest = -1;
     5         kx     offset = t - t_local;
     5         kx   }
     5         kx   else
     5         kx   {
     5         kx     eastwest = 1;
     5         kx     offset = t_local - t;
     5         kx   }
     5         kx   offset /= 60; /* in minutes */
     5         kx   offset = (offset % 60) + ((offset / 60) * 100);
     5         kx   return offset * eastwest;
     5         kx }
     5         kx 
     5         kx static int local_tzoffset( time_t time )
     5         kx {
     5         kx   struct tm tm;
     5         kx   return local_time_tzoffset( time, &tm );
     5         kx }
     5         kx 
     5         kx 
     5         kx static void show_date_normal( struct strbuf *sb,
     5         kx                               time_t time, struct tm *tm, int tz,
     5         kx                               struct tm *human_tm, int human_tz, int local )
     5         kx {
     5         kx   struct
     5         kx   {
     5         kx     unsigned int year:1,
     5         kx                  date:1,
     5         kx                  wday:1,
     5         kx                  time:1,
     5         kx                  seconds:1,
     5         kx                  tz:1;
     5         kx   } hide = { 0 };
     5         kx 
     5         kx   hide.tz = local || tz == human_tz;
     5         kx   hide.year = tm->tm_year == human_tm->tm_year;
     5         kx   if( hide.year )
     5         kx   {
     5         kx     if( tm->tm_mon == human_tm->tm_mon )
     5         kx     {
     5         kx       if( tm->tm_mday > human_tm->tm_mday )
     5         kx       {
     5         kx         /* Future date: think timezones */
     5         kx       }
     5         kx       else if( tm->tm_mday == human_tm->tm_mday )
     5         kx       {
     5         kx         hide.date = hide.wday = 1;
     5         kx       }
     5         kx       else if( tm->tm_mday + 5 > human_tm->tm_mday )
     5         kx       {
     5         kx         /* Leave just weekday if it was a few days ago */
     5         kx         hide.date = 1;
     5         kx       }
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   /* Show "today" times as just relative times */
     5         kx   if( hide.wday )
     5         kx   {
     5         kx     show_date_relative( sb, time );
     5         kx     return;
     5         kx   }
     5         kx 
     5         kx   /******************************************************
     5         kx     Always hide seconds for human-readable.
     5         kx     Hide timezone if showing date.
     5         kx     Hide weekday and time if showing year.
     5         kx 
     5         kx     The logic here is two-fold:
     5         kx      (a) only show details when recent enough to matter
     5         kx      (b) keep the maximum length "similar", and in check
     5         kx    ******************************************************/
     5         kx   if( human_tm->tm_year )
     5         kx   {
     5         kx     hide.seconds = 1;
     5         kx     hide.tz |= !hide.date;
     5         kx     hide.wday = hide.time = !hide.year;
     5         kx   }
     5         kx 
     5         kx   if( !hide.wday )
     5         kx     strbuf_addf( sb, "%.3s ", weekday_names[tm->tm_wday] );
     5         kx   if( !hide.date )
     5         kx     strbuf_addf( sb, "%.3s %d ", month_names[tm->tm_mon], tm->tm_mday );
     5         kx 
     5         kx   /* Do we want AM/PM depending on locale? */
     5         kx   if( !hide.time )
     5         kx   {
     5         kx     strbuf_addf( sb, "%02d:%02d", tm->tm_hour, tm->tm_min );
     5         kx     if( !hide.seconds )
     5         kx       strbuf_addf( sb, ":%02d", tm->tm_sec );
     5         kx   }
     5         kx   else
     5         kx     strbuf_rtrim( sb );
     5         kx 
     5         kx   if( !hide.year )
     5         kx     strbuf_addf( sb, " %d", tm->tm_year + 1900 );
     5         kx 
     5         kx   if( !hide.tz )
     5         kx     strbuf_addf( sb, " %+05d", tz );
     5         kx }
     5         kx 
     5         kx void show_date( struct strbuf *sb, time_t t, int tz, const struct date_mode *mode )
     5         kx {
     5         kx   struct tm *tm;
     5         kx   struct tm  tmbuf = { 0 };
     5         kx   struct tm  human_tm = { 0 };
     5         kx   int        human_tz = -1;
     5         kx 
     5         kx   if( mode->type == DATE_UNIX )
     5         kx   {
     5         kx     strbuf_addf( sb, "%"PRIdMAX, t );
     5         kx   }
     5         kx 
     5         kx   if( mode->type == DATE_HUMAN )
     5         kx   {
     5         kx     time_t now = time( NULL );
     5         kx 
     5         kx     /* Fill in the data for "current time" in human_tz and human_tm */
     5         kx     human_tz = local_time_tzoffset( now, &human_tm );
     5         kx   }
     5         kx 
     5         kx   if( mode->local )
     5         kx     tz = local_tzoffset( t );
     5         kx 
     5         kx   if( mode->type == DATE_RAW )
     5         kx   {
     5         kx     strbuf_addf( sb, "%"PRIdMAX" %+05d", t, tz );
     5         kx   }
     5         kx 
     5         kx   if( mode->type == DATE_RELATIVE )
     5         kx   {
     5         kx     show_date_relative( sb, t );
     5         kx   }
     5         kx 
     5         kx   if( mode->local )
     5         kx     tm = time_to_tm_local( t, &tmbuf );
     5         kx   else
     5         kx     tm = time_to_tm( t, tz, &tmbuf );
     5         kx   if (!tm) {
     5         kx     tm = time_to_tm( 0, 0, &tmbuf );
     5         kx     tz = 0;
     5         kx   }
     5         kx 
     5         kx   if( mode->type == DATE_SHORT )
     5         kx     strbuf_addf( sb, "%04d-%02d-%02d", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday );
     5         kx   else if( mode->type == DATE_ISO8601 )
     5         kx     strbuf_addf( sb, "%04d-%02d-%02d %02d:%02d:%02d %+05d",
     5         kx                       tm->tm_year + 1900,
     5         kx                       tm->tm_mon + 1,
     5         kx                       tm->tm_mday,
     5         kx                       tm->tm_hour, tm->tm_min, tm->tm_sec,
     5         kx                       tz );
     5         kx   else if( mode->type == DATE_ISO8601_STRICT )
     5         kx   {
     5         kx     char sign = (tz >= 0) ? '+' : '-';
     5         kx     tz = abs( tz );
     5         kx     strbuf_addf( sb, "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
     5         kx                       tm->tm_year + 1900,
     5         kx                       tm->tm_mon + 1,
     5         kx                       tm->tm_mday,
     5         kx                       tm->tm_hour, tm->tm_min, tm->tm_sec,
     5         kx                       sign, tz / 100, tz % 100 );
     5         kx   }
     5         kx   else if( mode->type == DATE_RFC2822 )
     5         kx     strbuf_addf( sb, "%.3s, %d %.3s %d %02d:%02d:%02d %+05d",
     5         kx                       weekday_names[tm->tm_wday], tm->tm_mday,
     5         kx                       month_names[tm->tm_mon], tm->tm_year + 1900,
     5         kx                       tm->tm_hour, tm->tm_min, tm->tm_sec, tz );
     5         kx   else
     5         kx     show_date_normal( sb, t, tm, tz, &human_tm, human_tz, mode->local );
     5         kx }