cSvn-UI for SVN Repositories

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

6 Commits   0 Branches   2 Tags
bfc1508d (kx 2023-03-24 03:55:33 +0300   1) 
bfc1508d (kx 2023-03-24 03:55:33 +0300   2) #ifdef HAVE_CONFIG_H
bfc1508d (kx 2023-03-24 03:55:33 +0300   3) #include <config.h>
bfc1508d (kx 2023-03-24 03:55:33 +0300   4) #endif
bfc1508d (kx 2023-03-24 03:55:33 +0300   5) 
bfc1508d (kx 2023-03-24 03:55:33 +0300   6) #include <stdlib.h>
bfc1508d (kx 2023-03-24 03:55:33 +0300   7) #include <stdio.h>
bfc1508d (kx 2023-03-24 03:55:33 +0300   8) #include <sys/sysinfo.h>
bfc1508d (kx 2023-03-24 03:55:33 +0300   9) #include <sys/types.h>
bfc1508d (kx 2023-03-24 03:55:33 +0300  10) #ifdef HAVE_INTTYPES_H
bfc1508d (kx 2023-03-24 03:55:33 +0300  11) #include <inttypes.h>
bfc1508d (kx 2023-03-24 03:55:33 +0300  12) #else
bfc1508d (kx 2023-03-24 03:55:33 +0300  13) #include <stdint.h>
bfc1508d (kx 2023-03-24 03:55:33 +0300  14) #endif
bfc1508d (kx 2023-03-24 03:55:33 +0300  15) #include <stddef.h>   /* offsetof(3) */
bfc1508d (kx 2023-03-24 03:55:33 +0300  16) #include <dirent.h>
bfc1508d (kx 2023-03-24 03:55:33 +0300  17) #include <sys/stat.h> /* chmod(2)    */
bfc1508d (kx 2023-03-24 03:55:33 +0300  18) #include <sys/file.h>
bfc1508d (kx 2023-03-24 03:55:33 +0300  19) #include <sys/mman.h>
bfc1508d (kx 2023-03-24 03:55:33 +0300  20) #include <fcntl.h>
bfc1508d (kx 2023-03-24 03:55:33 +0300  21) #include <limits.h>
bfc1508d (kx 2023-03-24 03:55:33 +0300  22) #include <string.h>   /* strdup(3)   */
bfc1508d (kx 2023-03-24 03:55:33 +0300  23) #include <libgen.h>   /* basename(3) */
bfc1508d (kx 2023-03-24 03:55:33 +0300  24) #include <ctype.h>    /* tolower(3)  */
bfc1508d (kx 2023-03-24 03:55:33 +0300  25) #include <errno.h>
bfc1508d (kx 2023-03-24 03:55:33 +0300  26) #include <time.h>
bfc1508d (kx 2023-03-24 03:55:33 +0300  27) #include <sys/time.h>
bfc1508d (kx 2023-03-24 03:55:33 +0300  28) #include <pwd.h>
bfc1508d (kx 2023-03-24 03:55:33 +0300  29) #include <grp.h>
bfc1508d (kx 2023-03-24 03:55:33 +0300  30) #include <stdarg.h>
bfc1508d (kx 2023-03-24 03:55:33 +0300  31) #include <unistd.h>
bfc1508d (kx 2023-03-24 03:55:33 +0300  32) 
bfc1508d (kx 2023-03-24 03:55:33 +0300  33) #include <nls.h>
bfc1508d (kx 2023-03-24 03:55:33 +0300  34) 
bfc1508d (kx 2023-03-24 03:55:33 +0300  35) #include <defs.h>
bfc1508d (kx 2023-03-24 03:55:33 +0300  36) 
bfc1508d (kx 2023-03-24 03:55:33 +0300  37) #include <strbuf.h>
bfc1508d (kx 2023-03-24 03:55:33 +0300  38) #include <date.h>
bfc1508d (kx 2023-03-24 03:55:33 +0300  39) 
bfc1508d (kx 2023-03-24 03:55:33 +0300  40) 
bfc1508d (kx 2023-03-24 03:55:33 +0300  41) /* Valid rule actions */
bfc1508d (kx 2023-03-24 03:55:33 +0300  42) enum rule_action
bfc1508d (kx 2023-03-24 03:55:33 +0300  43) {
bfc1508d (kx 2023-03-24 03:55:33 +0300  44)   ACCUM,    /* Accumulate a decimal value */
bfc1508d (kx 2023-03-24 03:55:33 +0300  45)   MICRO,    /* Accumulate microseconds */
bfc1508d (kx 2023-03-24 03:55:33 +0300  46)   TZIND,    /* Handle +, -, Z */
bfc1508d (kx 2023-03-24 03:55:33 +0300  47)   NOOP,     /* Do nothing */
bfc1508d (kx 2023-03-24 03:55:33 +0300  48)   SKIPFROM, /* If at end-of-value, accept the match.  Otherwise,
bfc1508d (kx 2023-03-24 03:55:33 +0300  49)                if the next template character matches the current
bfc1508d (kx 2023-03-24 03:55:33 +0300  50)                value character, continue processing as normal.
bfc1508d (kx 2023-03-24 03:55:33 +0300  51)                Otherwise, attempt to complete matching starting
bfc1508d (kx 2023-03-24 03:55:33 +0300  52)                immediately after the first subsequent occurrance of
bfc1508d (kx 2023-03-24 03:55:33 +0300  53)                ']' in the template. */
bfc1508d (kx 2023-03-24 03:55:33 +0300  54)   SKIP,     /* Ignore this template character */
bfc1508d (kx 2023-03-24 03:55:33 +0300  55)   ACCEPT    /* Accept the value */
bfc1508d (kx 2023-03-24 03:55:33 +0300  56) };
bfc1508d (kx 2023-03-24 03:55:33 +0300  57) 
bfc1508d (kx 2023-03-24 03:55:33 +0300  58) /* How to handle a particular character in a template */
bfc1508d (kx 2023-03-24 03:55:33 +0300  59) struct rule
bfc1508d (kx 2023-03-24 03:55:33 +0300  60) {
bfc1508d (kx 2023-03-24 03:55:33 +0300  61)   char              key;    /* The template char that this rule matches */
bfc1508d (kx 2023-03-24 03:55:33 +0300  62)   const char       *valid;  /* String of valid chars for this rule */
bfc1508d (kx 2023-03-24 03:55:33 +0300  63)   enum rule_action  action; /* What action to take when the rule is matched */
bfc1508d (kx 2023-03-24 03:55:33 +0300  64)   int               offset; /* Where to store the any results of the action,
bfc1508d (kx 2023-03-24 03:55:33 +0300  65)                                expressed in terms of bytes relative to the
bfc1508d (kx 2023-03-24 03:55:33 +0300  66)                                base of a match_state object. */
bfc1508d (kx 2023-03-24 03:55:33 +0300  67) };
bfc1508d (kx 2023-03-24 03:55:33 +0300  68) 
bfc1508d (kx 2023-03-24 03:55:33 +0300  69) struct match_state
bfc1508d (kx 2023-03-24 03:55:33 +0300  70) {
bfc1508d (kx 2023-03-24 03:55:33 +0300  71)   struct tm       base;
bfc1508d (kx 2023-03-24 03:55:33 +0300  72)   struct timeval  tv;
bfc1508d (kx 2023-03-24 03:55:33 +0300  73)   int             gmtoff;
bfc1508d (kx 2023-03-24 03:55:33 +0300  74)   int             gmtoff_hours;
bfc1508d (kx 2023-03-24 03:55:33 +0300  75)   int             gmtoff_minutes;
bfc1508d (kx 2023-03-24 03:55:33 +0300  76) };
bfc1508d (kx 2023-03-24 03:55:33 +0300  77) 
bfc1508d (kx 2023-03-24 03:55:33 +0300  78) 
bfc1508d (kx 2023-03-24 03:55:33 +0300  79) #define DIGITS "0123456789"
bfc1508d (kx 2023-03-24 03:55:33 +0300  80) 
bfc1508d (kx 2023-03-24 03:55:33 +0300  81) /*
bfc1508d (kx 2023-03-24 03:55:33 +0300  82)   A declarative specification of how each template character
bfc1508d (kx 2023-03-24 03:55:33 +0300  83)   should be processed, using a rule for each valid symbol.
bfc1508d (kx 2023-03-24 03:55:33 +0300  84)  */
bfc1508d (kx 2023-03-24 03:55:33 +0300  85) static const struct rule rules[] =
bfc1508d (kx 2023-03-24 03:55:33 +0300  86) {
bfc1508d (kx 2023-03-24 03:55:33 +0300  87)   { 'Y', DIGITS,    ACCUM, offsetof(struct match_state, base.tm_year)   },
bfc1508d (kx 2023-03-24 03:55:33 +0300  88)   { 'M', DIGITS,    ACCUM, offsetof(struct match_state, base.tm_mon)    },
bfc1508d (kx 2023-03-24 03:55:33 +0300  89)   { 'D', DIGITS,    ACCUM, offsetof(struct match_state, base.tm_mday)   },
bfc1508d (kx 2023-03-24 03:55:33 +0300  90)   { 'h', DIGITS,    ACCUM, offsetof(struct match_state, base.tm_hour)   },
bfc1508d (kx 2023-03-24 03:55:33 +0300  91)   { 'm', DIGITS,    ACCUM, offsetof(struct match_state, base.tm_min)    },
bfc1508d (kx 2023-03-24 03:55:33 +0300  92)   { 's', DIGITS,    ACCUM, offsetof(struct match_state, base.tm_sec)    },
bfc1508d (kx 2023-03-24 03:55:33 +0300  93)   { 'u', DIGITS,    MICRO, offsetof(struct match_state, tv.tv_usec)     },
bfc1508d (kx 2023-03-24 03:55:33 +0300  94)   { 'O', DIGITS,    ACCUM, offsetof(struct match_state, gmtoff_hours)   },
bfc1508d (kx 2023-03-24 03:55:33 +0300  95)   { 'o', DIGITS,    ACCUM, offsetof(struct match_state, gmtoff_minutes) },
bfc1508d (kx 2023-03-24 03:55:33 +0300  96)   { '+',   "-+",    TZIND, 0 },
bfc1508d (kx 2023-03-24 03:55:33 +0300  97)   { 'Z',    "Z",    TZIND, 0 },
bfc1508d (kx 2023-03-24 03:55:33 +0300  98)   { ':',    ":",     NOOP, 0 },
bfc1508d (kx 2023-03-24 03:55:33 +0300  99)   { '-',    "-",     NOOP, 0 },
bfc1508d (kx 2023-03-24 03:55:33 +0300 100)   { 'T',    "T",     NOOP, 0 },
bfc1508d (kx 2023-03-24 03:55:33 +0300 101)   { ' ',    " ",     NOOP, 0 },
bfc1508d (kx 2023-03-24 03:55:33 +0300 102)   { '.',   ".,",     NOOP, 0 },
bfc1508d (kx 2023-03-24 03:55:33 +0300 103)   { '[',   NULL, SKIPFROM, 0 },
bfc1508d (kx 2023-03-24 03:55:33 +0300 104)   { ']',   NULL,     SKIP, 0 },
bfc1508d (kx 2023-03-24 03:55:33 +0300 105)   { '\0',  NULL,   ACCEPT, 0 },
bfc1508d (kx 2023-03-24 03:55:33 +0300 106) };
bfc1508d (kx 2023-03-24 03:55:33 +0300 107) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 108) /* Return the rule associated with TCHAR, or NULL if there is no such rule. */
bfc1508d (kx 2023-03-24 03:55:33 +0300 109) static const struct rule *find_rule( char tchar )
bfc1508d (kx 2023-03-24 03:55:33 +0300 110) {
bfc1508d (kx 2023-03-24 03:55:33 +0300 111)   int i = sizeof(rules)/sizeof(rules[0]);
bfc1508d (kx 2023-03-24 03:55:33 +0300 112)   while( i-- )
bfc1508d (kx 2023-03-24 03:55:33 +0300 113)     if( rules[i].key == tchar )
bfc1508d (kx 2023-03-24 03:55:33 +0300 114)       return &rules[i];
bfc1508d (kx 2023-03-24 03:55:33 +0300 115)   return NULL;
bfc1508d (kx 2023-03-24 03:55:33 +0300 116) }
bfc1508d (kx 2023-03-24 03:55:33 +0300 117) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 118) /*
bfc1508d (kx 2023-03-24 03:55:33 +0300 119)   Attempt to match the date-string in VALUE to the provided TEMPLATE,
bfc1508d (kx 2023-03-24 03:55:33 +0300 120)   using the rules defined above.  Return TRUE on successful match,
bfc1508d (kx 2023-03-24 03:55:33 +0300 121)   FALSE otherwise.  On successful match, fill in *TM with the
bfc1508d (kx 2023-03-24 03:55:33 +0300 122)   matched values and set *LOCALTZ to GMT-offset if the local time zone
bfc1508d (kx 2023-03-24 03:55:33 +0300 123)   should be used to interpret the match.
bfc1508d (kx 2023-03-24 03:55:33 +0300 124)  */
bfc1508d (kx 2023-03-24 03:55:33 +0300 125) static int template_match( struct tm *tm, int *localtz, const char *template, const char *value )
bfc1508d (kx 2023-03-24 03:55:33 +0300 126) {
bfc1508d (kx 2023-03-24 03:55:33 +0300 127)   int    multiplier = 100000;
bfc1508d (kx 2023-03-24 03:55:33 +0300 128)   int    tzind = 0;
bfc1508d (kx 2023-03-24 03:55:33 +0300 129)   struct match_state  ms;
bfc1508d (kx 2023-03-24 03:55:33 +0300 130)   char  *base = (char *)&ms;
bfc1508d (kx 2023-03-24 03:55:33 +0300 131) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 132)   memset( &ms, 0, sizeof(ms) );
bfc1508d (kx 2023-03-24 03:55:33 +0300 133) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 134)   for( ;; )
bfc1508d (kx 2023-03-24 03:55:33 +0300 135)   {
bfc1508d (kx 2023-03-24 03:55:33 +0300 136)     const struct rule *match = find_rule(*template++);
bfc1508d (kx 2023-03-24 03:55:33 +0300 137)     char vchar = *value++;
bfc1508d (kx 2023-03-24 03:55:33 +0300 138)     int *place;
bfc1508d (kx 2023-03-24 03:55:33 +0300 139) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 140)     if( !match || (match->valid && (!vchar || !strchr(match->valid, vchar))) )
bfc1508d (kx 2023-03-24 03:55:33 +0300 141)       return FALSE;
bfc1508d (kx 2023-03-24 03:55:33 +0300 142) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 143)     /* Compute the address of memory location affected by this
bfc1508d (kx 2023-03-24 03:55:33 +0300 144)        rule by adding match->offset bytes to the address of ms.
bfc1508d (kx 2023-03-24 03:55:33 +0300 145)        Because this is a byte-quantity, it is necessary to cast
bfc1508d (kx 2023-03-24 03:55:33 +0300 146)        &ms to char *. */
bfc1508d (kx 2023-03-24 03:55:33 +0300 147)     place = (int *)(base + match->offset);
bfc1508d (kx 2023-03-24 03:55:33 +0300 148)     switch( match->action )
bfc1508d (kx 2023-03-24 03:55:33 +0300 149)     {
bfc1508d (kx 2023-03-24 03:55:33 +0300 150)       case ACCUM:
bfc1508d (kx 2023-03-24 03:55:33 +0300 151)         *place = *place * 10 + vchar - '0';
bfc1508d (kx 2023-03-24 03:55:33 +0300 152)         continue;
bfc1508d (kx 2023-03-24 03:55:33 +0300 153)       case MICRO:
bfc1508d (kx 2023-03-24 03:55:33 +0300 154)         *place += (vchar - '0') * multiplier;
bfc1508d (kx 2023-03-24 03:55:33 +0300 155)         multiplier /= 10;
bfc1508d (kx 2023-03-24 03:55:33 +0300 156)         continue;
bfc1508d (kx 2023-03-24 03:55:33 +0300 157)       case TZIND:
bfc1508d (kx 2023-03-24 03:55:33 +0300 158)         tzind = vchar;
bfc1508d (kx 2023-03-24 03:55:33 +0300 159)         continue;
bfc1508d (kx 2023-03-24 03:55:33 +0300 160)       case SKIP:
bfc1508d (kx 2023-03-24 03:55:33 +0300 161)         value--;
bfc1508d (kx 2023-03-24 03:55:33 +0300 162)         continue;
bfc1508d (kx 2023-03-24 03:55:33 +0300 163)       case NOOP:
bfc1508d (kx 2023-03-24 03:55:33 +0300 164)         continue;
bfc1508d (kx 2023-03-24 03:55:33 +0300 165)       case SKIPFROM:
bfc1508d (kx 2023-03-24 03:55:33 +0300 166)         if( !vchar )
bfc1508d (kx 2023-03-24 03:55:33 +0300 167)           break;
bfc1508d (kx 2023-03-24 03:55:33 +0300 168)         match = find_rule(*template);
bfc1508d (kx 2023-03-24 03:55:33 +0300 169)         if (!strchr(match->valid, vchar))
bfc1508d (kx 2023-03-24 03:55:33 +0300 170)           template = strchr(template, ']') + 1;
bfc1508d (kx 2023-03-24 03:55:33 +0300 171)         value--;
bfc1508d (kx 2023-03-24 03:55:33 +0300 172)         continue;
bfc1508d (kx 2023-03-24 03:55:33 +0300 173)       case ACCEPT:
bfc1508d (kx 2023-03-24 03:55:33 +0300 174)         if( vchar )
bfc1508d (kx 2023-03-24 03:55:33 +0300 175)           return FALSE;
bfc1508d (kx 2023-03-24 03:55:33 +0300 176)         break;
bfc1508d (kx 2023-03-24 03:55:33 +0300 177)     }
bfc1508d (kx 2023-03-24 03:55:33 +0300 178) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 179)     break;
bfc1508d (kx 2023-03-24 03:55:33 +0300 180)   }
bfc1508d (kx 2023-03-24 03:55:33 +0300 181) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 182)   /* Validate gmt offset here, since we can't reliably do it later. */
bfc1508d (kx 2023-03-24 03:55:33 +0300 183)   if( ms.gmtoff_hours > 23 || ms.gmtoff_minutes > 59 )
bfc1508d (kx 2023-03-24 03:55:33 +0300 184)     return FALSE;
bfc1508d (kx 2023-03-24 03:55:33 +0300 185) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 186)   /*
bfc1508d (kx 2023-03-24 03:55:33 +0300 187)     tzind will be '+' or '-' for an explicit time zone,
bfc1508d (kx 2023-03-24 03:55:33 +0300 188)     'Z' to indicate UTC, or 0 to indicate local time.
bfc1508d (kx 2023-03-24 03:55:33 +0300 189)    */
bfc1508d (kx 2023-03-24 03:55:33 +0300 190)   switch( tzind )
bfc1508d (kx 2023-03-24 03:55:33 +0300 191)   {
bfc1508d (kx 2023-03-24 03:55:33 +0300 192)     case '+':
bfc1508d (kx 2023-03-24 03:55:33 +0300 193)       ms.gmtoff =   ms.gmtoff_hours * 3600 + ms.gmtoff_minutes * 60;
bfc1508d (kx 2023-03-24 03:55:33 +0300 194)       break;
bfc1508d (kx 2023-03-24 03:55:33 +0300 195)     case '-':
bfc1508d (kx 2023-03-24 03:55:33 +0300 196)       ms.gmtoff = -(ms.gmtoff_hours * 3600 + ms.gmtoff_minutes * 60);
bfc1508d (kx 2023-03-24 03:55:33 +0300 197)       break;
bfc1508d (kx 2023-03-24 03:55:33 +0300 198)   }
bfc1508d (kx 2023-03-24 03:55:33 +0300 199) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 200)   *tm = ms.base;
bfc1508d (kx 2023-03-24 03:55:33 +0300 201)   *localtz = ms.gmtoff;
bfc1508d (kx 2023-03-24 03:55:33 +0300 202)   return TRUE;
bfc1508d (kx 2023-03-24 03:55:33 +0300 203) }
bfc1508d (kx 2023-03-24 03:55:33 +0300 204) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 205) static int valid_days_by_month[] =
bfc1508d (kx 2023-03-24 03:55:33 +0300 206) {
bfc1508d (kx 2023-03-24 03:55:33 +0300 207)   31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
bfc1508d (kx 2023-03-24 03:55:33 +0300 208) };
bfc1508d (kx 2023-03-24 03:55:33 +0300 209) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 210) /*
bfc1508d (kx 2023-03-24 03:55:33 +0300 211)   Returns -1 on error,
bfc1508d (kx 2023-03-24 03:55:33 +0300 212)   time_t as the number of seconds since Epoch, 1970-01-01 00:00:00 +0000 (UTC)
bfc1508d (kx 2023-03-24 03:55:33 +0300 213)   on success.
bfc1508d (kx 2023-03-24 03:55:33 +0300 214)  */
bfc1508d (kx 2023-03-24 03:55:33 +0300 215) time_t parse_date( struct tm *tm, const char *text )
bfc1508d (kx 2023-03-24 03:55:33 +0300 216) {
bfc1508d (kx 2023-03-24 03:55:33 +0300 217)   time_t     n, ret = (time_t)-1;
bfc1508d (kx 2023-03-24 03:55:33 +0300 218)   struct tm  pt, *now;
bfc1508d (kx 2023-03-24 03:55:33 +0300 219)   int        localtz;
bfc1508d (kx 2023-03-24 03:55:33 +0300 220) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 221)   n = time( NULL ); /* current UTC time */
bfc1508d (kx 2023-03-24 03:55:33 +0300 222)   now = gmtime( &n );
bfc1508d (kx 2023-03-24 03:55:33 +0300 223) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 224) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 225)   if( /* ISO-8601 extended, date only: */
bfc1508d (kx 2023-03-24 03:55:33 +0300 226)       template_match( &pt, &localtz, "YYYY-M[M]-D[D]", text ) ||
bfc1508d (kx 2023-03-24 03:55:33 +0300 227)       /* ISO-8601 extended, UTC: */
bfc1508d (kx 2023-03-24 03:55:33 +0300 228)       template_match( &pt, &localtz, "YYYY-M[M]-D[D]Th[h]:mm[:ss[.u[u[u[u[u[u][Z]", text ) ||
bfc1508d (kx 2023-03-24 03:55:33 +0300 229)       /* ISO-8601 extended, with offset: */
bfc1508d (kx 2023-03-24 03:55:33 +0300 230)       template_match( &pt, &localtz, "YYYY-M[M]-D[D]Th[h]:mm[:ss[.u[u[u[u[u[u]+OO[:oo]", text ) ||
bfc1508d (kx 2023-03-24 03:55:33 +0300 231)       /* ISO-8601 basic, date only */
bfc1508d (kx 2023-03-24 03:55:33 +0300 232)       template_match( &pt, &localtz, "YYYYMMDD", text ) ||
bfc1508d (kx 2023-03-24 03:55:33 +0300 233)       /* ISO-8601 basic, UTC: */
bfc1508d (kx 2023-03-24 03:55:33 +0300 234)       template_match( &pt, &localtz, "YYYYMMDDThhmm[ss[.u[u[u[u[u[u][Z]", text ) ||
bfc1508d (kx 2023-03-24 03:55:33 +0300 235)       /* ISO-8601 basic, with offset: */
bfc1508d (kx 2023-03-24 03:55:33 +0300 236)       template_match( &pt, &localtz, "YYYYMMDDThhmm[ss[.u[u[u[u[u[u]+OO[oo]", text ) ||
bfc1508d (kx 2023-03-24 03:55:33 +0300 237)       /* "svn log" format: */
bfc1508d (kx 2023-03-24 03:55:33 +0300 238)       template_match( &pt, &localtz, "YYYY-M[M]-D[D] h[h]:mm[:ss[.u[u[u[u[u[u][ +OO[oo]", text ) ||
bfc1508d (kx 2023-03-24 03:55:33 +0300 239)       /* GNU date's iso-8601: */
bfc1508d (kx 2023-03-24 03:55:33 +0300 240)       template_match( &pt, &localtz, "YYYY-M[M]-D[D]Th[h]:mm[:ss[.u[u[u[u[u[u]+OO[oo]", text ) )
bfc1508d (kx 2023-03-24 03:55:33 +0300 241)   {
bfc1508d (kx 2023-03-24 03:55:33 +0300 242)     pt.tm_year -= 1900;
bfc1508d (kx 2023-03-24 03:55:33 +0300 243)     pt.tm_mon -= 1;
bfc1508d (kx 2023-03-24 03:55:33 +0300 244)   }
bfc1508d (kx 2023-03-24 03:55:33 +0300 245)   else if( template_match( &pt, &localtz, "h[h]:mm[:ss[.u[u[u[u[u[u]", text) ) /* Just a time */
bfc1508d (kx 2023-03-24 03:55:33 +0300 246)   {
bfc1508d (kx 2023-03-24 03:55:33 +0300 247)     pt.tm_year = now->tm_year;
bfc1508d (kx 2023-03-24 03:55:33 +0300 248)     pt.tm_mon  = now->tm_mon;
bfc1508d (kx 2023-03-24 03:55:33 +0300 249)     pt.tm_mday = now->tm_mday;
bfc1508d (kx 2023-03-24 03:55:33 +0300 250)   }
bfc1508d (kx 2023-03-24 03:55:33 +0300 251) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 252)   /* Range validation, allowing for leap seconds */
bfc1508d (kx 2023-03-24 03:55:33 +0300 253)   if( pt.tm_mon  <  0 ||
bfc1508d (kx 2023-03-24 03:55:33 +0300 254)       pt.tm_mon  > 11 ||
bfc1508d (kx 2023-03-24 03:55:33 +0300 255)       pt.tm_mday > valid_days_by_month[pt.tm_mon] ||
bfc1508d (kx 2023-03-24 03:55:33 +0300 256)       pt.tm_mday <  1 ||
bfc1508d (kx 2023-03-24 03:55:33 +0300 257)       pt.tm_hour > 23 ||
bfc1508d (kx 2023-03-24 03:55:33 +0300 258)       pt.tm_min  > 59 ||
bfc1508d (kx 2023-03-24 03:55:33 +0300 259)       pt.tm_sec  > 60   )
bfc1508d (kx 2023-03-24 03:55:33 +0300 260)     return ret;
bfc1508d (kx 2023-03-24 03:55:33 +0300 261) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 262)   /*
bfc1508d (kx 2023-03-24 03:55:33 +0300 263)     february/leap-year day checking. tm_year is bias-1900, so
bfc1508d (kx 2023-03-24 03:55:33 +0300 264)     centuries that equal 100 (mod 400) are multiples of 400.
bfc1508d (kx 2023-03-24 03:55:33 +0300 265)    */
bfc1508d (kx 2023-03-24 03:55:33 +0300 266)   if( pt.tm_mon  ==  1 &&
bfc1508d (kx 2023-03-24 03:55:33 +0300 267)       pt.tm_mday == 29 &&
bfc1508d (kx 2023-03-24 03:55:33 +0300 268)      (pt.tm_year % 4 != 0 || (pt.tm_year % 100 == 0 && pt.tm_year % 400 != 100)) )
bfc1508d (kx 2023-03-24 03:55:33 +0300 269)     return ret;
bfc1508d (kx 2023-03-24 03:55:33 +0300 270) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 271)   if( localtz )
bfc1508d (kx 2023-03-24 03:55:33 +0300 272)   {
bfc1508d (kx 2023-03-24 03:55:33 +0300 273)     struct tm *gmt = NULL;
bfc1508d (kx 2023-03-24 03:55:33 +0300 274)     time_t     time;
bfc1508d (kx 2023-03-24 03:55:33 +0300 275) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 276)     time = mktime( &pt ); /* brocken-down tm asumed as localtime */
bfc1508d (kx 2023-03-24 03:55:33 +0300 277)     if( time == -1 )
bfc1508d (kx 2023-03-24 03:55:33 +0300 278)       return ret;
bfc1508d (kx 2023-03-24 03:55:33 +0300 279) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 280)     time -= (time_t)localtz;
bfc1508d (kx 2023-03-24 03:55:33 +0300 281) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 282)     gmt = localtime( &time );
bfc1508d (kx 2023-03-24 03:55:33 +0300 283)     if( !gmt )
bfc1508d (kx 2023-03-24 03:55:33 +0300 284)       return ret;
bfc1508d (kx 2023-03-24 03:55:33 +0300 285) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 286)     memcpy( (void *)&pt, (const void *)gmt, sizeof(struct tm) );
bfc1508d (kx 2023-03-24 03:55:33 +0300 287)   }
bfc1508d (kx 2023-03-24 03:55:33 +0300 288) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 289)   memcpy( (void *)tm, (const void *)&pt, sizeof(struct tm) );
bfc1508d (kx 2023-03-24 03:55:33 +0300 290) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 291)   return mktime( &pt );
bfc1508d (kx 2023-03-24 03:55:33 +0300 292) }
bfc1508d (kx 2023-03-24 03:55:33 +0300 293) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 294) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 295) void show_date_relative( struct strbuf *sb, time_t t )
bfc1508d (kx 2023-03-24 03:55:33 +0300 296) {
bfc1508d (kx 2023-03-24 03:55:33 +0300 297)   time_t now, diff;
bfc1508d (kx 2023-03-24 03:55:33 +0300 298) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 299)   if( !sb || !t ) return;
bfc1508d (kx 2023-03-24 03:55:33 +0300 300) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 301)   now = time( NULL );
bfc1508d (kx 2023-03-24 03:55:33 +0300 302)   if( now < t )
bfc1508d (kx 2023-03-24 03:55:33 +0300 303)   {
bfc1508d (kx 2023-03-24 03:55:33 +0300 304)     strbuf_addstr( sb, _("in the future") );
bfc1508d (kx 2023-03-24 03:55:33 +0300 305)     return;
bfc1508d (kx 2023-03-24 03:55:33 +0300 306)   }
bfc1508d (kx 2023-03-24 03:55:33 +0300 307)   diff = now - t;
bfc1508d (kx 2023-03-24 03:55:33 +0300 308)   if( diff < 90 )
bfc1508d (kx 2023-03-24 03:55:33 +0300 309)   {
bfc1508d (kx 2023-03-24 03:55:33 +0300 310)     strbuf_addf( sb, Q_("%"PRIdMAX" second ago", "%"PRIdMAX" seconds ago", diff), diff );
bfc1508d (kx 2023-03-24 03:55:33 +0300 311)     return;
bfc1508d (kx 2023-03-24 03:55:33 +0300 312)   }
bfc1508d (kx 2023-03-24 03:55:33 +0300 313)   /* Turn it into minutes */
bfc1508d (kx 2023-03-24 03:55:33 +0300 314)   diff = (diff + 30) / 60;
bfc1508d (kx 2023-03-24 03:55:33 +0300 315)   if( diff < 90 )
bfc1508d (kx 2023-03-24 03:55:33 +0300 316)   {
bfc1508d (kx 2023-03-24 03:55:33 +0300 317)     strbuf_addf( sb, Q_("%"PRIdMAX" minute ago", "%"PRIdMAX" minutes ago", diff), diff );
bfc1508d (kx 2023-03-24 03:55:33 +0300 318)     return;
bfc1508d (kx 2023-03-24 03:55:33 +0300 319)   }
bfc1508d (kx 2023-03-24 03:55:33 +0300 320)   /* Turn it into hours */
bfc1508d (kx 2023-03-24 03:55:33 +0300 321)   diff = (diff + 30) / 60;
bfc1508d (kx 2023-03-24 03:55:33 +0300 322)   if( diff < 36 )
bfc1508d (kx 2023-03-24 03:55:33 +0300 323)   {
bfc1508d (kx 2023-03-24 03:55:33 +0300 324)     strbuf_addf( sb, Q_("%"PRIdMAX" hour ago", "%"PRIdMAX" hours ago", diff), diff );
bfc1508d (kx 2023-03-24 03:55:33 +0300 325)     return;
bfc1508d (kx 2023-03-24 03:55:33 +0300 326)   }
bfc1508d (kx 2023-03-24 03:55:33 +0300 327)   /* We deal with number of days from here on */
bfc1508d (kx 2023-03-24 03:55:33 +0300 328)   diff = (diff + 12) / 24;
bfc1508d (kx 2023-03-24 03:55:33 +0300 329)   if( diff < 14 )
bfc1508d (kx 2023-03-24 03:55:33 +0300 330)   {
bfc1508d (kx 2023-03-24 03:55:33 +0300 331)     strbuf_addf( sb, Q_("%"PRIdMAX" day ago", "%"PRIdMAX" days ago", diff), diff );
bfc1508d (kx 2023-03-24 03:55:33 +0300 332)     return;
bfc1508d (kx 2023-03-24 03:55:33 +0300 333)   }
bfc1508d (kx 2023-03-24 03:55:33 +0300 334)   /* Say weeks for the past 10 weeks or so */
bfc1508d (kx 2023-03-24 03:55:33 +0300 335)   if( diff < 70 )
bfc1508d (kx 2023-03-24 03:55:33 +0300 336)   {
bfc1508d (kx 2023-03-24 03:55:33 +0300 337)     strbuf_addf( sb, Q_("%"PRIdMAX" week ago", "%"PRIdMAX" weeks ago", (diff+3)/7), (diff+3)/7 );
bfc1508d (kx 2023-03-24 03:55:33 +0300 338)     return;
bfc1508d (kx 2023-03-24 03:55:33 +0300 339)   }
bfc1508d (kx 2023-03-24 03:55:33 +0300 340)   /* Say months for the past 12 months or so */
bfc1508d (kx 2023-03-24 03:55:33 +0300 341)   if( diff < 365 )
bfc1508d (kx 2023-03-24 03:55:33 +0300 342)   {
bfc1508d (kx 2023-03-24 03:55:33 +0300 343)     strbuf_addf( sb, Q_("%"PRIdMAX" month ago", "%"PRIdMAX" months ago", (diff+15)/30), (diff+15)/30 );
bfc1508d (kx 2023-03-24 03:55:33 +0300 344)     return;
bfc1508d (kx 2023-03-24 03:55:33 +0300 345)   }
bfc1508d (kx 2023-03-24 03:55:33 +0300 346)   /* Give years and months for 5 years or so */
bfc1508d (kx 2023-03-24 03:55:33 +0300 347)   if( diff < 1825 )
bfc1508d (kx 2023-03-24 03:55:33 +0300 348)   {
bfc1508d (kx 2023-03-24 03:55:33 +0300 349)     time_t totalmonths = (diff * 12 * 2 + 365) / (365 * 2);
bfc1508d (kx 2023-03-24 03:55:33 +0300 350)     time_t years = totalmonths / 12;
bfc1508d (kx 2023-03-24 03:55:33 +0300 351)     time_t months = totalmonths % 12;
bfc1508d (kx 2023-03-24 03:55:33 +0300 352)     if( months )
bfc1508d (kx 2023-03-24 03:55:33 +0300 353)     {
bfc1508d (kx 2023-03-24 03:55:33 +0300 354)       struct strbuf buf = STRBUF_INIT;
bfc1508d (kx 2023-03-24 03:55:33 +0300 355)       strbuf_addf( &buf, Q_("%"PRIdMAX" year", "%"PRIdMAX" years", years), years );
bfc1508d (kx 2023-03-24 03:55:33 +0300 356)       strbuf_addf( sb,
bfc1508d (kx 2023-03-24 03:55:33 +0300 357)          /* TRANSLATORS: "%s" is "<n> months" */
bfc1508d (kx 2023-03-24 03:55:33 +0300 358)          Q_("%s, %"PRIdMAX" month ago", "%s, %"PRIdMAX" months ago", months), buf.buf, months );
bfc1508d (kx 2023-03-24 03:55:33 +0300 359)       strbuf_release( &buf );
bfc1508d (kx 2023-03-24 03:55:33 +0300 360)     }
bfc1508d (kx 2023-03-24 03:55:33 +0300 361)     else
bfc1508d (kx 2023-03-24 03:55:33 +0300 362)       strbuf_addf( sb, Q_("%"PRIdMAX" year ago", "%"PRIdMAX" years ago", years), years );
bfc1508d (kx 2023-03-24 03:55:33 +0300 363)     return;
bfc1508d (kx 2023-03-24 03:55:33 +0300 364)   }
bfc1508d (kx 2023-03-24 03:55:33 +0300 365)   /* Otherwise, just years. Centuries is probably overkill. */
bfc1508d (kx 2023-03-24 03:55:33 +0300 366)   strbuf_addf( sb, Q_("%"PRIdMAX" year ago", "%"PRIdMAX" years ago", (diff+183)/365), (diff+183)/365 );
bfc1508d (kx 2023-03-24 03:55:33 +0300 367) }
bfc1508d (kx 2023-03-24 03:55:33 +0300 368) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 369) struct date_mode *date_mode_from_type( enum date_mode_type type )
bfc1508d (kx 2023-03-24 03:55:33 +0300 370) {
bfc1508d (kx 2023-03-24 03:55:33 +0300 371)   static struct date_mode mode;
bfc1508d (kx 2023-03-24 03:55:33 +0300 372)   mode.type = type;
bfc1508d (kx 2023-03-24 03:55:33 +0300 373)   mode.local = 0;
bfc1508d (kx 2023-03-24 03:55:33 +0300 374)   return &mode;
bfc1508d (kx 2023-03-24 03:55:33 +0300 375) }
bfc1508d (kx 2023-03-24 03:55:33 +0300 376) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 377) static const char *month_names[] = {
bfc1508d (kx 2023-03-24 03:55:33 +0300 378)   "January", "February", "March", "April", "May", "June",
bfc1508d (kx 2023-03-24 03:55:33 +0300 379)   "July", "August", "September", "October", "November", "December"
bfc1508d (kx 2023-03-24 03:55:33 +0300 380) };
bfc1508d (kx 2023-03-24 03:55:33 +0300 381) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 382) static const char *weekday_names[] = {
bfc1508d (kx 2023-03-24 03:55:33 +0300 383)   "Sundays", "Mondays", "Tuesdays", "Wednesdays", "Thursdays", "Fridays", "Saturdays"
bfc1508d (kx 2023-03-24 03:55:33 +0300 384) };
bfc1508d (kx 2023-03-24 03:55:33 +0300 385) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 386) static time_t gm_time_t( time_t time, int tz )
bfc1508d (kx 2023-03-24 03:55:33 +0300 387) {
bfc1508d (kx 2023-03-24 03:55:33 +0300 388)   int minutes;
bfc1508d (kx 2023-03-24 03:55:33 +0300 389) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 390)   minutes = tz < 0 ? -tz : tz;
bfc1508d (kx 2023-03-24 03:55:33 +0300 391)   minutes = (minutes / 100)*60 + (minutes % 100);
bfc1508d (kx 2023-03-24 03:55:33 +0300 392)   minutes = tz < 0 ? -minutes : minutes;
bfc1508d (kx 2023-03-24 03:55:33 +0300 393) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 394)   time += minutes * 60;
bfc1508d (kx 2023-03-24 03:55:33 +0300 395) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 396)   return time;
bfc1508d (kx 2023-03-24 03:55:33 +0300 397) }
bfc1508d (kx 2023-03-24 03:55:33 +0300 398) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 399) static struct tm *time_to_tm( time_t time, int tz, struct tm *tm )
bfc1508d (kx 2023-03-24 03:55:33 +0300 400) {
bfc1508d (kx 2023-03-24 03:55:33 +0300 401)   time_t t = gm_time_t( time, tz );
bfc1508d (kx 2023-03-24 03:55:33 +0300 402)   return gmtime_r( &t, tm );
bfc1508d (kx 2023-03-24 03:55:33 +0300 403) }
bfc1508d (kx 2023-03-24 03:55:33 +0300 404) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 405) static struct tm *time_to_tm_local( time_t time, struct tm *tm )
bfc1508d (kx 2023-03-24 03:55:33 +0300 406) {
bfc1508d (kx 2023-03-24 03:55:33 +0300 407)   time_t t = time;
bfc1508d (kx 2023-03-24 03:55:33 +0300 408)   return localtime_r( &t, tm );
bfc1508d (kx 2023-03-24 03:55:33 +0300 409) }
bfc1508d (kx 2023-03-24 03:55:33 +0300 410) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 411) /**********************************************************
bfc1508d (kx 2023-03-24 03:55:33 +0300 412)   Fill in the localtime 'struct tm' for the supplied time,
bfc1508d (kx 2023-03-24 03:55:33 +0300 413)   and return the local tz.
bfc1508d (kx 2023-03-24 03:55:33 +0300 414)  */
bfc1508d (kx 2023-03-24 03:55:33 +0300 415) static int local_time_tzoffset( time_t t, struct tm *tm )
bfc1508d (kx 2023-03-24 03:55:33 +0300 416) {
bfc1508d (kx 2023-03-24 03:55:33 +0300 417)   time_t t_local;
bfc1508d (kx 2023-03-24 03:55:33 +0300 418)   int offset, eastwest;
bfc1508d (kx 2023-03-24 03:55:33 +0300 419) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 420)   localtime_r( &t, tm );
bfc1508d (kx 2023-03-24 03:55:33 +0300 421)   t_local = mktime( tm );
bfc1508d (kx 2023-03-24 03:55:33 +0300 422)   if( t_local == -1 )
bfc1508d (kx 2023-03-24 03:55:33 +0300 423)     return 0; /* error; just use +0000 */
bfc1508d (kx 2023-03-24 03:55:33 +0300 424)   if( t_local < t )
bfc1508d (kx 2023-03-24 03:55:33 +0300 425)   {
bfc1508d (kx 2023-03-24 03:55:33 +0300 426)     eastwest = -1;
bfc1508d (kx 2023-03-24 03:55:33 +0300 427)     offset = t - t_local;
bfc1508d (kx 2023-03-24 03:55:33 +0300 428)   }
bfc1508d (kx 2023-03-24 03:55:33 +0300 429)   else
bfc1508d (kx 2023-03-24 03:55:33 +0300 430)   {
bfc1508d (kx 2023-03-24 03:55:33 +0300 431)     eastwest = 1;
bfc1508d (kx 2023-03-24 03:55:33 +0300 432)     offset = t_local - t;
bfc1508d (kx 2023-03-24 03:55:33 +0300 433)   }
bfc1508d (kx 2023-03-24 03:55:33 +0300 434)   offset /= 60; /* in minutes */
bfc1508d (kx 2023-03-24 03:55:33 +0300 435)   offset = (offset % 60) + ((offset / 60) * 100);
bfc1508d (kx 2023-03-24 03:55:33 +0300 436)   return offset * eastwest;
bfc1508d (kx 2023-03-24 03:55:33 +0300 437) }
bfc1508d (kx 2023-03-24 03:55:33 +0300 438) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 439) static int local_tzoffset( time_t time )
bfc1508d (kx 2023-03-24 03:55:33 +0300 440) {
bfc1508d (kx 2023-03-24 03:55:33 +0300 441)   struct tm tm;
bfc1508d (kx 2023-03-24 03:55:33 +0300 442)   return local_time_tzoffset( time, &tm );
bfc1508d (kx 2023-03-24 03:55:33 +0300 443) }
bfc1508d (kx 2023-03-24 03:55:33 +0300 444) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 445) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 446) static void show_date_normal( struct strbuf *sb,
bfc1508d (kx 2023-03-24 03:55:33 +0300 447)                               time_t time, struct tm *tm, int tz,
bfc1508d (kx 2023-03-24 03:55:33 +0300 448)                               struct tm *human_tm, int human_tz, int local )
bfc1508d (kx 2023-03-24 03:55:33 +0300 449) {
bfc1508d (kx 2023-03-24 03:55:33 +0300 450)   struct
bfc1508d (kx 2023-03-24 03:55:33 +0300 451)   {
bfc1508d (kx 2023-03-24 03:55:33 +0300 452)     unsigned int year:1,
bfc1508d (kx 2023-03-24 03:55:33 +0300 453)                  date:1,
bfc1508d (kx 2023-03-24 03:55:33 +0300 454)                  wday:1,
bfc1508d (kx 2023-03-24 03:55:33 +0300 455)                  time:1,
bfc1508d (kx 2023-03-24 03:55:33 +0300 456)                  seconds:1,
bfc1508d (kx 2023-03-24 03:55:33 +0300 457)                  tz:1;
bfc1508d (kx 2023-03-24 03:55:33 +0300 458)   } hide = { 0 };
bfc1508d (kx 2023-03-24 03:55:33 +0300 459) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 460)   hide.tz = local || tz == human_tz;
bfc1508d (kx 2023-03-24 03:55:33 +0300 461)   hide.year = tm->tm_year == human_tm->tm_year;
bfc1508d (kx 2023-03-24 03:55:33 +0300 462)   if( hide.year )
bfc1508d (kx 2023-03-24 03:55:33 +0300 463)   {
bfc1508d (kx 2023-03-24 03:55:33 +0300 464)     if( tm->tm_mon == human_tm->tm_mon )
bfc1508d (kx 2023-03-24 03:55:33 +0300 465)     {
bfc1508d (kx 2023-03-24 03:55:33 +0300 466)       if( tm->tm_mday > human_tm->tm_mday )
bfc1508d (kx 2023-03-24 03:55:33 +0300 467)       {
bfc1508d (kx 2023-03-24 03:55:33 +0300 468)         /* Future date: think timezones */
bfc1508d (kx 2023-03-24 03:55:33 +0300 469)       }
bfc1508d (kx 2023-03-24 03:55:33 +0300 470)       else if( tm->tm_mday == human_tm->tm_mday )
bfc1508d (kx 2023-03-24 03:55:33 +0300 471)       {
bfc1508d (kx 2023-03-24 03:55:33 +0300 472)         hide.date = hide.wday = 1;
bfc1508d (kx 2023-03-24 03:55:33 +0300 473)       }
bfc1508d (kx 2023-03-24 03:55:33 +0300 474)       else if( tm->tm_mday + 5 > human_tm->tm_mday )
bfc1508d (kx 2023-03-24 03:55:33 +0300 475)       {
bfc1508d (kx 2023-03-24 03:55:33 +0300 476)         /* Leave just weekday if it was a few days ago */
bfc1508d (kx 2023-03-24 03:55:33 +0300 477)         hide.date = 1;
bfc1508d (kx 2023-03-24 03:55:33 +0300 478)       }
bfc1508d (kx 2023-03-24 03:55:33 +0300 479)     }
bfc1508d (kx 2023-03-24 03:55:33 +0300 480)   }
bfc1508d (kx 2023-03-24 03:55:33 +0300 481) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 482)   /* Show "today" times as just relative times */
bfc1508d (kx 2023-03-24 03:55:33 +0300 483)   if( hide.wday )
bfc1508d (kx 2023-03-24 03:55:33 +0300 484)   {
bfc1508d (kx 2023-03-24 03:55:33 +0300 485)     show_date_relative( sb, time );
bfc1508d (kx 2023-03-24 03:55:33 +0300 486)     return;
bfc1508d (kx 2023-03-24 03:55:33 +0300 487)   }
bfc1508d (kx 2023-03-24 03:55:33 +0300 488) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 489)   /******************************************************
bfc1508d (kx 2023-03-24 03:55:33 +0300 490)     Always hide seconds for human-readable.
bfc1508d (kx 2023-03-24 03:55:33 +0300 491)     Hide timezone if showing date.
bfc1508d (kx 2023-03-24 03:55:33 +0300 492)     Hide weekday and time if showing year.
bfc1508d (kx 2023-03-24 03:55:33 +0300 493) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 494)     The logic here is two-fold:
bfc1508d (kx 2023-03-24 03:55:33 +0300 495)      (a) only show details when recent enough to matter
bfc1508d (kx 2023-03-24 03:55:33 +0300 496)      (b) keep the maximum length "similar", and in check
bfc1508d (kx 2023-03-24 03:55:33 +0300 497)    ******************************************************/
bfc1508d (kx 2023-03-24 03:55:33 +0300 498)   if( human_tm->tm_year )
bfc1508d (kx 2023-03-24 03:55:33 +0300 499)   {
bfc1508d (kx 2023-03-24 03:55:33 +0300 500)     hide.seconds = 1;
bfc1508d (kx 2023-03-24 03:55:33 +0300 501)     hide.tz |= !hide.date;
bfc1508d (kx 2023-03-24 03:55:33 +0300 502)     hide.wday = hide.time = !hide.year;
bfc1508d (kx 2023-03-24 03:55:33 +0300 503)   }
bfc1508d (kx 2023-03-24 03:55:33 +0300 504) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 505)   if( !hide.wday )
bfc1508d (kx 2023-03-24 03:55:33 +0300 506)     strbuf_addf( sb, "%.3s ", weekday_names[tm->tm_wday] );
bfc1508d (kx 2023-03-24 03:55:33 +0300 507)   if( !hide.date )
bfc1508d (kx 2023-03-24 03:55:33 +0300 508)     strbuf_addf( sb, "%.3s %d ", month_names[tm->tm_mon], tm->tm_mday );
bfc1508d (kx 2023-03-24 03:55:33 +0300 509) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 510)   /* Do we want AM/PM depending on locale? */
bfc1508d (kx 2023-03-24 03:55:33 +0300 511)   if( !hide.time )
bfc1508d (kx 2023-03-24 03:55:33 +0300 512)   {
bfc1508d (kx 2023-03-24 03:55:33 +0300 513)     strbuf_addf( sb, "%02d:%02d", tm->tm_hour, tm->tm_min );
bfc1508d (kx 2023-03-24 03:55:33 +0300 514)     if( !hide.seconds )
bfc1508d (kx 2023-03-24 03:55:33 +0300 515)       strbuf_addf( sb, ":%02d", tm->tm_sec );
bfc1508d (kx 2023-03-24 03:55:33 +0300 516)   }
bfc1508d (kx 2023-03-24 03:55:33 +0300 517)   else
bfc1508d (kx 2023-03-24 03:55:33 +0300 518)     strbuf_rtrim( sb );
bfc1508d (kx 2023-03-24 03:55:33 +0300 519) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 520)   if( !hide.year )
bfc1508d (kx 2023-03-24 03:55:33 +0300 521)     strbuf_addf( sb, " %d", tm->tm_year + 1900 );
bfc1508d (kx 2023-03-24 03:55:33 +0300 522) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 523)   if( !hide.tz )
bfc1508d (kx 2023-03-24 03:55:33 +0300 524)     strbuf_addf( sb, " %+05d", tz );
bfc1508d (kx 2023-03-24 03:55:33 +0300 525) }
bfc1508d (kx 2023-03-24 03:55:33 +0300 526) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 527) void show_date( struct strbuf *sb, time_t t, int tz, const struct date_mode *mode )
bfc1508d (kx 2023-03-24 03:55:33 +0300 528) {
bfc1508d (kx 2023-03-24 03:55:33 +0300 529)   struct tm *tm;
bfc1508d (kx 2023-03-24 03:55:33 +0300 530)   struct tm  tmbuf = { 0 };
bfc1508d (kx 2023-03-24 03:55:33 +0300 531)   struct tm  human_tm = { 0 };
bfc1508d (kx 2023-03-24 03:55:33 +0300 532)   int        human_tz = -1;
bfc1508d (kx 2023-03-24 03:55:33 +0300 533) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 534)   if( mode->type == DATE_UNIX )
bfc1508d (kx 2023-03-24 03:55:33 +0300 535)   {
bfc1508d (kx 2023-03-24 03:55:33 +0300 536)     strbuf_addf( sb, "%"PRIdMAX, t );
bfc1508d (kx 2023-03-24 03:55:33 +0300 537)   }
bfc1508d (kx 2023-03-24 03:55:33 +0300 538) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 539)   if( mode->type == DATE_HUMAN )
bfc1508d (kx 2023-03-24 03:55:33 +0300 540)   {
bfc1508d (kx 2023-03-24 03:55:33 +0300 541)     time_t now = time( NULL );
bfc1508d (kx 2023-03-24 03:55:33 +0300 542) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 543)     /* Fill in the data for "current time" in human_tz and human_tm */
bfc1508d (kx 2023-03-24 03:55:33 +0300 544)     human_tz = local_time_tzoffset( now, &human_tm );
bfc1508d (kx 2023-03-24 03:55:33 +0300 545)   }
bfc1508d (kx 2023-03-24 03:55:33 +0300 546) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 547)   if( mode->local )
bfc1508d (kx 2023-03-24 03:55:33 +0300 548)     tz = local_tzoffset( t );
bfc1508d (kx 2023-03-24 03:55:33 +0300 549) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 550)   if( mode->type == DATE_RAW )
bfc1508d (kx 2023-03-24 03:55:33 +0300 551)   {
bfc1508d (kx 2023-03-24 03:55:33 +0300 552)     strbuf_addf( sb, "%"PRIdMAX" %+05d", t, tz );
bfc1508d (kx 2023-03-24 03:55:33 +0300 553)   }
bfc1508d (kx 2023-03-24 03:55:33 +0300 554) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 555)   if( mode->type == DATE_RELATIVE )
bfc1508d (kx 2023-03-24 03:55:33 +0300 556)   {
bfc1508d (kx 2023-03-24 03:55:33 +0300 557)     show_date_relative( sb, t );
bfc1508d (kx 2023-03-24 03:55:33 +0300 558)   }
bfc1508d (kx 2023-03-24 03:55:33 +0300 559) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 560)   if( mode->local )
bfc1508d (kx 2023-03-24 03:55:33 +0300 561)     tm = time_to_tm_local( t, &tmbuf );
bfc1508d (kx 2023-03-24 03:55:33 +0300 562)   else
bfc1508d (kx 2023-03-24 03:55:33 +0300 563)     tm = time_to_tm( t, tz, &tmbuf );
bfc1508d (kx 2023-03-24 03:55:33 +0300 564)   if (!tm) {
bfc1508d (kx 2023-03-24 03:55:33 +0300 565)     tm = time_to_tm( 0, 0, &tmbuf );
bfc1508d (kx 2023-03-24 03:55:33 +0300 566)     tz = 0;
bfc1508d (kx 2023-03-24 03:55:33 +0300 567)   }
bfc1508d (kx 2023-03-24 03:55:33 +0300 568) 
bfc1508d (kx 2023-03-24 03:55:33 +0300 569)   if( mode->type == DATE_SHORT )
bfc1508d (kx 2023-03-24 03:55:33 +0300 570)     strbuf_addf( sb, "%04d-%02d-%02d", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday );
bfc1508d (kx 2023-03-24 03:55:33 +0300 571)   else if( mode->type == DATE_ISO8601 )
bfc1508d (kx 2023-03-24 03:55:33 +0300 572)     strbuf_addf( sb, "%04d-%02d-%02d %02d:%02d:%02d %+05d",
bfc1508d (kx 2023-03-24 03:55:33 +0300 573)                       tm->tm_year + 1900,
bfc1508d (kx 2023-03-24 03:55:33 +0300 574)                       tm->tm_mon + 1,
bfc1508d (kx 2023-03-24 03:55:33 +0300 575)                       tm->tm_mday,
bfc1508d (kx 2023-03-24 03:55:33 +0300 576)                       tm->tm_hour, tm->tm_min, tm->tm_sec,
bfc1508d (kx 2023-03-24 03:55:33 +0300 577)                       tz );
bfc1508d (kx 2023-03-24 03:55:33 +0300 578)   else if( mode->type == DATE_ISO8601_STRICT )
bfc1508d (kx 2023-03-24 03:55:33 +0300 579)   {
bfc1508d (kx 2023-03-24 03:55:33 +0300 580)     char sign = (tz >= 0) ? '+' : '-';
bfc1508d (kx 2023-03-24 03:55:33 +0300 581)     tz = abs( tz );
bfc1508d (kx 2023-03-24 03:55:33 +0300 582)     strbuf_addf( sb, "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
bfc1508d (kx 2023-03-24 03:55:33 +0300 583)                       tm->tm_year + 1900,
bfc1508d (kx 2023-03-24 03:55:33 +0300 584)                       tm->tm_mon + 1,
bfc1508d (kx 2023-03-24 03:55:33 +0300 585)                       tm->tm_mday,
bfc1508d (kx 2023-03-24 03:55:33 +0300 586)                       tm->tm_hour, tm->tm_min, tm->tm_sec,
bfc1508d (kx 2023-03-24 03:55:33 +0300 587)                       sign, tz / 100, tz % 100 );
bfc1508d (kx 2023-03-24 03:55:33 +0300 588)   }
bfc1508d (kx 2023-03-24 03:55:33 +0300 589)   else if( mode->type == DATE_RFC2822 )
bfc1508d (kx 2023-03-24 03:55:33 +0300 590)     strbuf_addf( sb, "%.3s, %d %.3s %d %02d:%02d:%02d %+05d",
bfc1508d (kx 2023-03-24 03:55:33 +0300 591)                       weekday_names[tm->tm_wday], tm->tm_mday,
bfc1508d (kx 2023-03-24 03:55:33 +0300 592)                       month_names[tm->tm_mon], tm->tm_year + 1900,
bfc1508d (kx 2023-03-24 03:55:33 +0300 593)                       tm->tm_hour, tm->tm_min, tm->tm_sec, tz );
bfc1508d (kx 2023-03-24 03:55:33 +0300 594)   else
bfc1508d (kx 2023-03-24 03:55:33 +0300 595)     show_date_normal( sb, t, tm, tz, &human_tm, human_tz, mode->local );
bfc1508d (kx 2023-03-24 03:55:33 +0300 596) }