char * __attribute_warn_unused_result__ anytostr (inttype i, char *buf) { verify (TYPE_SIGNED (inttype) == inttype_is_signed); char *p = buf + INT_STRLEN_BOUND (inttype); *p = 0; #if inttype_is_signed if (i < 0) { do *--p = '0' - i % 10; while ((i /= 10) != 0); *--p = '-'; } else #endif { do *--p = '0' + i % 10; while ((i /= 10) != 0); } return p; }
void write_directory_file (void) { FILE *fp = listed_incremental_stream; char buf[UINTMAX_STRSIZE_BOUND]; char *s; if (! fp) return; if (fseeko (fp, 0L, SEEK_SET) != 0) seek_error (listed_incremental_option); if (sys_truncate (fileno (fp)) != 0) truncate_error (listed_incremental_option); fprintf (fp, "%s-%s-%d\n", PACKAGE_NAME, PACKAGE_VERSION, TAR_INCREMENTAL_VERSION); s = (TYPE_SIGNED (time_t) ? imaxtostr (start_time.tv_sec, buf) : umaxtostr (start_time.tv_sec, buf)); fwrite (s, strlen (s) + 1, 1, fp); s = umaxtostr (start_time.tv_nsec, buf); fwrite (s, strlen (s) + 1, 1, fp); if (! ferror (fp) && directory_table) hash_do_for_each (directory_table, write_directory_file_entry, fp); if (ferror (fp)) write_error (listed_incremental_option); if (fclose (fp) != 0) close_error (listed_incremental_option); }
static int differ_by_repeat(pg_time_t t1, pg_time_t t0) { if (TYPE_INTEGRAL(pg_time_t) && TYPE_BIT(pg_time_t) -TYPE_SIGNED(pg_time_t) <SECSPERREPEAT_BITS) return 0; return t1 - t0 == SECSPERREPEAT; }
extern char * gid_to_name (gid_t gid) { char buf[INT_BUFSIZE_BOUND (intmax_t)]; struct group *grp = getgrgid (gid); return xstrdup (grp ? grp->gr_name : TYPE_SIGNED (gid_t) ? imaxtostr (gid, buf) : umaxtostr (gid, buf)); }
extern char * uid_to_name (uid_t uid) { char buf[INT_BUFSIZE_BOUND (intmax_t)]; struct passwd *pwd = getpwuid (uid); return xstrdup (pwd ? pwd->pw_name : TYPE_SIGNED (uid_t) ? imaxtostr (uid, buf) : umaxtostr (uid, buf)); }
static int differ_by_repeat(const time_t t1, const time_t t0) { int_fast64_t _t0 = t0; int_fast64_t _t1 = t1; if (TYPE_INTEGRAL(time_t) && TYPE_BIT(time_t) - TYPE_SIGNED(time_t) < SECSPERREPEAT_BITS) return 0; return _t1 - _t0 == SECSPERREPEAT; }
double ATTRIBUTE_CONST difftime(const time_t time1, const time_t time0) { /* ** If (sizeof (double) > sizeof (time_t)) simply convert and subtract ** (assuming that the larger type has more precision). */ /*CONSTCOND*/ if (sizeof (double) > sizeof (time_t)) return (double) time1 - (double) time0; /*LINTED const not */ if (!TYPE_INTEGRAL(time_t)) { /* ** time_t is floating. */ return time1 - time0; } /*LINTED const not */ if (!TYPE_SIGNED(time_t)) { /* ** time_t is integral and unsigned. ** The difference of two unsigned values can't overflow ** if the minuend is greater than or equal to the subtrahend. */ if (time1 >= time0) return time1 - time0; else return -(double) (time0 - time1); } /* ** time_t is integral and signed. ** Handle cases where both time1 and time0 have the same sign ** (meaning that their difference cannot overflow). */ if ((time1 < 0) == (time0 < 0)) return time1 - time0; /* ** time1 and time0 have opposite signs. ** Punt if uintmax_t is too narrow. ** This suffers from double rounding; attempt to lessen that ** by using long double temporaries. */ /* CONSTCOND */ if (sizeof (uintmax_t) < sizeof (time_t)) return (double) time1 - (double) time0; /* ** Stay calm...decent optimizers will eliminate the complexity below. */ if (time1 >= 0 /* && time0 < 0 */) return (uintmax_t) time1 + (uintmax_t) (-(time0 + 1)) + 1; return -(double) ((uintmax_t) time0 + (uintmax_t) (-(time1 + 1)) + 1); }
/* Hash some device info. */ static size_t dev_info_hash (void const *x, size_t table_size) { struct fs_res const *p = x; /* Beware signed arithmetic gotchas. */ if (TYPE_SIGNED (dev_t) && SIZE_MAX < MAX (INT_MAX, TYPE_MAXIMUM (dev_t))) { uintmax_t dev = p->dev; return dev % table_size; } return p->dev % table_size; }
double difftime(const time_t time1, const time_t time0) { /* ** If (sizeof (double) > sizeof (time_t)) simply convert and subtract ** (assuming that the larger type has more precision). ** This is the common real-world case circa 2004. */ if (sizeof (double) > sizeof (time_t)) return (double) time1 - (double) time0; if (!TYPE_INTEGRAL(time_t)) { /* ** time_t is floating. */ return time1 - time0; } if (!TYPE_SIGNED(time_t)) { /* ** time_t is integral and unsigned. ** The difference of two unsigned values can't overflow ** if the minuend is greater than or equal to the subtrahend. */ if (time1 >= time0) return time1 - time0; else return -((double) (time0 - time1)); } /* ** time_t is integral and signed. ** Handle cases where both time1 and time0 have the same sign ** (meaning that their difference cannot overflow). */ if ((time1 < 0) == (time0 < 0)) return time1 - time0; /* ** time1 and time0 have opposite signs. ** Punt if unsigned long is too narrow. */ if (sizeof (unsigned long) < sizeof (time_t)) return (double) time1 - (double) time0; /* ** Stay calm...decent optimizers will eliminate the complexity below. */ if (time1 >= 0 /* && time0 < 0 */) return (unsigned long) time1 + (unsigned long) (-(time0 + 1)) + 1; return -(double) ((unsigned long) time0 + (unsigned long) (-(time1 + 1)) + 1); }
static strtol_error bkm_scale (__strtol_t *x, int scale_factor) { if (TYPE_SIGNED (__strtol_t) && *x < STRTOL_T_MINIMUM / scale_factor) { *x = STRTOL_T_MINIMUM; return LONGINT_OVERFLOW; } if (STRTOL_T_MAXIMUM / scale_factor < *x) { *x = STRTOL_T_MAXIMUM; return LONGINT_OVERFLOW; } *x *= scale_factor; return LONGINT_OK; }
/* Return 1 if A + B does not overflow. If time_t is unsigned and if B's top bit is set, assume that the sum represents A - -B, and return 1 if the subtraction does not wrap around. */ static int time_t_add_ok (time_t a, time_t b) { if (! TYPE_SIGNED (time_t)) { time_t sum = a + b; return (sum < a) == (TIME_T_MIDPOINT <= b); } else if (WRAPV) { time_t sum = a + b; return (sum < a) == (b < 0); } else { time_t avg = time_t_avg (a, b); return TIME_T_MIN / 2 <= avg && avg <= TIME_T_MAX / 2; } }
/* Return 1 if A + B does not overflow. If time_t is unsigned and if B's top bit is set, assume that the sum represents A - -B, and return 1 if the subtraction does not wrap around. */ static int time_t_add_ok(time_t a, time_t b) { if (! TYPE_SIGNED(time_t)) { time_t sum = (a + b); return ((sum < a) == (TIME_T_MIDPOINT <= b)); } else if (WRAPV) { time_t sum = (a + b); return ((sum < a) == (b < 0)); } else { time_t avg = time_t_avg(a, b); return (((TIME_T_MIN / 2) <= avg) && (avg <= (TIME_T_MAX / 2))); } }
/* Output incremental data for the directory ENTRY to the file DATA. Return nonzero if successful, preserving errno on write failure. */ static bool write_directory_file_entry (void *entry, void *data) { struct directory const *directory = entry; FILE *fp = data; if (DIR_IS_FOUND (directory)) { char buf[UINTMAX_STRSIZE_BOUND]; char *s; s = DIR_IS_NFS (directory) ? "1" : "0"; fwrite (s, 2, 1, fp); s = (TYPE_SIGNED (time_t) ? imaxtostr (directory->mtime.tv_sec, buf) : umaxtostr (directory->mtime.tv_sec, buf)); fwrite (s, strlen (s) + 1, 1, fp); s = umaxtostr (directory->mtime.tv_nsec, buf); fwrite (s, strlen (s) + 1, 1, fp); s = umaxtostr (directory->device_number, buf); fwrite (s, strlen (s) + 1, 1, fp); s = umaxtostr (directory->inode_number, buf); fwrite (s, strlen (s) + 1, 1, fp); fwrite (directory->name, strlen (directory->name) + 1, 1, fp); if (directory->dump) { const char *p; dumpdir_iter_t itr; for (p = dumpdir_first (directory->dump, 0, &itr); p; p = dumpdir_next (itr)) fwrite (p, strlen (p) + 1, 1, fp); free (itr); } fwrite ("\0\0", 2, 1, fp); } return ! ferror (fp); }
struct timespec decode_timespec (char const *arg, char **arg_lim, bool parse_fraction) { time_t s = TYPE_MINIMUM (time_t); int ns = -1; char const *p = arg; bool negative = *arg == '-'; struct timespec r; if (! ISDIGIT (arg[negative])) errno = EINVAL; else { errno = 0; if (negative) { intmax_t i = strtoimax (arg, arg_lim, 10); if (TYPE_SIGNED (time_t) ? TYPE_MINIMUM (time_t) <= i : 0 <= i) s = i; else errno = ERANGE; } else { uintmax_t i = strtoumax (arg, arg_lim, 10); if (i <= TYPE_MAXIMUM (time_t)) s = i; else errno = ERANGE; } p = *arg_lim; ns = 0; if (parse_fraction && *p == '.') { int digits = 0; bool trailing_nonzero = false; while (ISDIGIT (*++p)) if (digits < LOG10_BILLION) digits++, ns = 10 * ns + (*p - '0'); else trailing_nonzero |= *p != '0'; while (digits < LOG10_BILLION) digits++, ns *= 10; if (negative) { /* Convert "-1.10000000000001" to s == -2, ns == 89999999. I.e., truncate time stamps towards minus infinity while converting them to internal form. */ ns += trailing_nonzero; if (ns != 0) { if (s == TYPE_MINIMUM (time_t)) ns = -1; else { s--; ns = BILLION - ns; } } } } if (errno == ERANGE) ns = -1; } *arg_lim = (char *) p; r.tv_sec = s; r.tv_nsec = ns; return r; }
static int tzload(const char *name, struct state * const sp, const int doextend) { const char * p; int i; int fid; int stored; int nread; typedef union { struct tzhead tzhead; char buf[2 * sizeof(struct tzhead) + 2 * sizeof *sp + 4 * TZ_MAX_TIMES]; } u_t; u_t u; u_t * const up = &u; sp->goback = sp->goahead = FALSE; /* XXX The following is from OpenBSD, and I'm not sure it is correct */ if (name != NULL && issetugid() != 0) if ((name[0] == ':' && name[1] == '/') || name[0] == '/' || strchr(name, '.')) name = NULL; if (name == NULL && (name = TZDEFAULT) == NULL) goto oops; { int doaccess; struct stat stab; /* ** Section 4.9.1 of the C standard says that ** "FILENAME_MAX expands to an integral constant expression ** that is the size needed for an array of char large enough ** to hold the longest file name string that the implementation ** guarantees can be opened." */ char fullname[FILENAME_MAX + 1]; if (name[0] == ':') ++name; doaccess = name[0] == '/'; if (!doaccess) { if ((p = TZDIR) == NULL) goto oops; if ((strlen(p) + 1 + strlen(name) + 1) >= sizeof fullname) goto oops; strcpy(fullname, p); strcat(fullname, "/"); strcat(fullname, name); /* ** Set doaccess if '.' (as in "../") shows up in name. */ if (strchr(name, '.') != NULL) doaccess = TRUE; name = fullname; } if (doaccess && access(name, R_OK) != 0) goto oops; if ((fid = _open(name, O_RDONLY)) == -1) goto oops; if ((_fstat(fid, &stab) < 0) || !S_ISREG(stab.st_mode)) { _close(fid); return -1; } } nread = _read(fid, up->buf, sizeof up->buf); if (_close(fid) < 0 || nread <= 0) goto oops; for (stored = 4; stored <= 8; stored *= 2) { int ttisstdcnt; int ttisgmtcnt; ttisstdcnt = (int) detzcode(up->tzhead.tzh_ttisstdcnt); ttisgmtcnt = (int) detzcode(up->tzhead.tzh_ttisgmtcnt); sp->leapcnt = (int) detzcode(up->tzhead.tzh_leapcnt); sp->timecnt = (int) detzcode(up->tzhead.tzh_timecnt); sp->typecnt = (int) detzcode(up->tzhead.tzh_typecnt); sp->charcnt = (int) detzcode(up->tzhead.tzh_charcnt); p = up->tzhead.tzh_charcnt + sizeof up->tzhead.tzh_charcnt; if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS || sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES || sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES || sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS || (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) || (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0)) goto oops; if (nread - (p - up->buf) < sp->timecnt * stored + /* ats */ sp->timecnt + /* types */ sp->typecnt * 6 + /* ttinfos */ sp->charcnt + /* chars */ sp->leapcnt * (stored + 4) + /* lsinfos */ ttisstdcnt + /* ttisstds */ ttisgmtcnt) /* ttisgmts */ goto oops; for (i = 0; i < sp->timecnt; ++i) { sp->ats[i] = (stored == 4) ? detzcode(p) : detzcode64(p); p += stored; } for (i = 0; i < sp->timecnt; ++i) { sp->types[i] = (unsigned char) *p++; if (sp->types[i] >= sp->typecnt) goto oops; } for (i = 0; i < sp->typecnt; ++i) { struct ttinfo * ttisp; ttisp = &sp->ttis[i]; ttisp->tt_gmtoff = detzcode(p); p += 4; ttisp->tt_isdst = (unsigned char) *p++; if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1) goto oops; ttisp->tt_abbrind = (unsigned char) *p++; if (ttisp->tt_abbrind < 0 || ttisp->tt_abbrind > sp->charcnt) goto oops; } for (i = 0; i < sp->charcnt; ++i) sp->chars[i] = *p++; sp->chars[i] = '\0'; /* ensure '\0' at end */ for (i = 0; i < sp->leapcnt; ++i) { struct lsinfo * lsisp; lsisp = &sp->lsis[i]; lsisp->ls_trans = (stored == 4) ? detzcode(p) : detzcode64(p); p += stored; lsisp->ls_corr = detzcode(p); p += 4; } for (i = 0; i < sp->typecnt; ++i) { struct ttinfo * ttisp; ttisp = &sp->ttis[i]; if (ttisstdcnt == 0) ttisp->tt_ttisstd = FALSE; else { ttisp->tt_ttisstd = *p++; if (ttisp->tt_ttisstd != TRUE && ttisp->tt_ttisstd != FALSE) goto oops; } } for (i = 0; i < sp->typecnt; ++i) { struct ttinfo * ttisp; ttisp = &sp->ttis[i]; if (ttisgmtcnt == 0) ttisp->tt_ttisgmt = FALSE; else { ttisp->tt_ttisgmt = *p++; if (ttisp->tt_ttisgmt != TRUE && ttisp->tt_ttisgmt != FALSE) goto oops; } } /* ** Out-of-sort ats should mean we're running on a ** signed time_t system but using a data file with ** unsigned values (or vice versa). */ for (i = 0; i < sp->timecnt - 2; ++i) if (sp->ats[i] > sp->ats[i + 1]) { ++i; if (TYPE_SIGNED(time_t)) { /* ** Ignore the end (easy). */ sp->timecnt = i; } else { /* ** Ignore the beginning (harder). */ int j; for (j = 0; j + i < sp->timecnt; ++j) { sp->ats[j] = sp->ats[j + i]; sp->types[j] = sp->types[j + i]; } sp->timecnt = j; } break; } /* ** If this is an old file, we're done. */ if (up->tzhead.tzh_version[0] == '\0') break; nread -= p - up->buf; for (i = 0; i < nread; ++i) up->buf[i] = p[i]; /* ** If this is a narrow integer time_t system, we're done. */ if (stored >= (int) sizeof(time_t) && TYPE_INTEGRAL(time_t)) break; } if (doextend && nread > 2 && up->buf[0] == '\n' && up->buf[nread - 1] == '\n' && sp->typecnt + 2 <= TZ_MAX_TYPES) { struct state ts; int result; up->buf[nread - 1] = '\0'; result = tzparse(&up->buf[1], &ts, FALSE); if (result == 0 && ts.typecnt == 2 && sp->charcnt + ts.charcnt <= TZ_MAX_CHARS) { for (i = 0; i < 2; ++i) ts.ttis[i].tt_abbrind += sp->charcnt; for (i = 0; i < ts.charcnt; ++i) sp->chars[sp->charcnt++] = ts.chars[i]; i = 0; while (i < ts.timecnt && ts.ats[i] <= sp->ats[sp->timecnt - 1]) ++i; while (i < ts.timecnt && sp->timecnt < TZ_MAX_TIMES) { sp->ats[sp->timecnt] = ts.ats[i]; sp->types[sp->timecnt] = sp->typecnt + ts.types[i]; ++sp->timecnt; ++i; } sp->ttis[sp->typecnt++] = ts.ttis[0]; sp->ttis[sp->typecnt++] = ts.ttis[1]; } } if (sp->timecnt > 1) { for (i = 1; i < sp->timecnt; ++i) if (typesequiv(sp, sp->types[i], sp->types[0]) && differ_by_repeat(sp->ats[i], sp->ats[0])) { sp->goback = TRUE; break; } for (i = sp->timecnt - 2; i >= 0; --i) if (typesequiv(sp, sp->types[sp->timecnt - 1], sp->types[i]) && differ_by_repeat(sp->ats[sp->timecnt - 1], sp->ats[i])) { sp->goahead = TRUE; break; } } return 0; oops: return -1; }
#endif /* !defined TYPE_BIT */ #ifndef TYPE_SIGNED #define TYPE_SIGNED(type) (((type) -1) < 0) #endif /* !defined TYPE_SIGNED */ #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunknown-pragmas" #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wshift-negative-value" #endif /* The minimum and maximum finite time values. */ static time_t const time_t_min = (TYPE_SIGNED (time_t) ? (time_t) -1 << (CHAR_BIT * sizeof (time_t) - 1) : 0); static time_t const time_t_max = (TYPE_SIGNED (time_t) ? -(~0 < 0) - ((time_t) -1 << (CHAR_BIT * sizeof (time_t) - 1)) : -1); #ifdef __clang__ #pragma clang diagnostic pop #pragma clang diagnostic pop #endif #ifndef TZ_MAX_TIMES #define TZ_MAX_TIMES 2000 #endif /* !defined TZ_MAX_TIMES */ #ifndef TZ_MAX_TYPES
#include "diff.h" #include <cmpbuf.h> #include <file-type.h> #include <setmode.h> #include <xalloc.h> /* Rotate an unsigned value to the left. */ #define ROL(v, n) ((v) << (n) | (v) >> (sizeof (v) * CHAR_BIT - (n))) /* Given a hash value and a new character, return a new hash value. */ #define HASH(h, c) ((c) + ROL (h, 7)) /* The type of a hash value. */ typedef size_t hash_value; verify (hash_value_is_unsigned, ! TYPE_SIGNED (hash_value)); /* Lines are put into equivalence classes of lines that match in lines_differ. Each equivalence class is represented by one of these structures, but only while the classes are being computed. Afterward, each class is represented by a number. */ struct equivclass { lin next; /* Next item in this bucket. */ hash_value hash; /* Hash of lines in this class. */ char const *line; /* A line that fits this class. */ size_t length; /* That line's length, not counting its newline. */ }; /* Hash-table: array of buckets, each being a chain of equivalence classes. buckets[-1] is reserved for incomplete lines. */
static enum decode_time_status _decode_time (struct timespec *ts, char const *arg, char const *keyword) { time_t s; unsigned long int ns = 0; char *p; char *arg_lim; bool negative = *arg == '-'; errno = 0; if (ISDIGIT (arg[negative])) { if (negative) { intmax_t i = strtoimax (arg, &arg_lim, 10); if (TYPE_SIGNED (time_t) ? i < TYPE_MINIMUM (time_t) : i < 0) return decode_time_range; s = i; } else { uintmax_t i = strtoumax (arg, &arg_lim, 10); if (TYPE_MAXIMUM (time_t) < i) return decode_time_range; s = i; } p = arg_lim; if (errno == ERANGE) return decode_time_range; if (*p == '.') { int digits = 0; bool trailing_nonzero = false; while (ISDIGIT (*++p)) if (digits < LOG10_BILLION) { ns = 10 * ns + (*p - '0'); digits++; } else trailing_nonzero |= *p != '0'; while (digits++ < LOG10_BILLION) ns *= 10; if (negative) { /* Convert "-1.10000000000001" to s == -2, ns == 89999999. I.e., truncate time stamps towards minus infinity while converting them to internal form. */ ns += trailing_nonzero; if (ns != 0) { if (s == TYPE_MINIMUM (time_t)) return decode_time_range; s--; ns = BILLION - ns; } } } if (! *p) { ts->tv_sec = s; ts->tv_nsec = ns; return decode_time_success; } } return decode_time_bad_header; }
#include "diff.h" #include <binary-io.h> #include <cmpbuf.h> #include <file-type.h> #include <xalloc.h> /* Rotate an unsigned value to the left. */ #define ROL(v, n) ((v) << (n) | (v) >> (sizeof (v) * CHAR_BIT - (n))) /* Given a hash value and a new character, return a new hash value. */ #define HASH(h, c) ((c) + ROL (h, 7)) /* The type of a hash value. */ typedef size_t hash_value; verify (! TYPE_SIGNED (hash_value)); /* Lines are put into equivalence classes of lines that match in lines_differ. Each equivalence class is represented by one of these structures, but only while the classes are being computed. Afterward, each class is represented by a number. */ struct equivclass { lin next; /* Next item in this bucket. */ hash_value hash; /* Hash of lines in this class. */ char const *line; /* A line that fits this class. */ size_t length; /* That line's length, not counting its newline. */ }; /* Hash-table: array of buckets, each being a chain of equivalence classes. buckets[-1] is reserved for incomplete lines. */
strtol_error __xstrtol (const char *s, char **ptr, int strtol_base, __strtol_t *val, const char *valid_suffixes) { char *t_ptr; char **p; __strtol_t tmp; strtol_error err = LONGINT_OK; assert (0 <= strtol_base && strtol_base <= 36); p = (ptr ? ptr : &t_ptr); if (! TYPE_SIGNED (__strtol_t)) { const char *q = s; unsigned char ch = *q; while (isspace (ch)) ch = *++q; if (ch == '-') return LONGINT_INVALID; } errno = 0; tmp = __strtol (s, p, strtol_base); if (*p == s) { /* If there is no number but there is a valid suffix, assume the number is 1. The string is invalid otherwise. */ if (valid_suffixes && **p && strchr (valid_suffixes, **p)) tmp = 1; else return LONGINT_INVALID; } else if (errno != 0) { if (errno != ERANGE) return LONGINT_INVALID; err = LONGINT_OVERFLOW; } /* Let valid_suffixes == NULL mean `allow any suffix'. */ /* FIXME: update all callers except the ones that allow suffixes after the number, changing last parameter NULL to `""'. */ if (!valid_suffixes) { *val = tmp; return err; } if (**p != '\0') { int base = 1024; int suffixes = 1; strtol_error overflow; if (!strchr (valid_suffixes, **p)) { *val = tmp; return err | LONGINT_INVALID_SUFFIX_CHAR; } if (strchr (valid_suffixes, '0')) { /* The ``valid suffix'' '0' is a special flag meaning that an optional second suffix is allowed, which can change the base. A suffix "B" (e.g. "100MB") stands for a power of 1000, whereas a suffix "iB" (e.g. "100MiB") stands for a power of 1024. If no suffix (e.g. "100M"), assume power-of-1024. */ switch (p[0][1]) { case 'i': if (p[0][2] == 'B') suffixes += 2; break; case 'B': case 'D': /* 'D' is obsolescent */ base = 1000; suffixes++; break; } } switch (**p) { case 'b': overflow = bkm_scale (&tmp, 512); break; case 'B': overflow = bkm_scale (&tmp, 1024); break; case 'c': overflow = 0; break; case 'E': /* exa or exbi */ overflow = bkm_scale_by_power (&tmp, base, 6); break; case 'G': /* giga or gibi */ case 'g': /* 'g' is undocumented; for compatibility only */ overflow = bkm_scale_by_power (&tmp, base, 3); break; case 'k': /* kilo */ case 'K': /* kibi */ overflow = bkm_scale_by_power (&tmp, base, 1); break; case 'M': /* mega or mebi */ case 'm': /* 'm' is undocumented; for compatibility only */ overflow = bkm_scale_by_power (&tmp, base, 2); break; case 'P': /* peta or pebi */ overflow = bkm_scale_by_power (&tmp, base, 5); break; case 'T': /* tera or tebi */ case 't': /* 't' is undocumented; for compatibility only */ overflow = bkm_scale_by_power (&tmp, base, 4); break; case 'w': overflow = bkm_scale (&tmp, 2); break; case 'Y': /* yotta or 2**80 */ overflow = bkm_scale_by_power (&tmp, base, 8); break; case 'Z': /* zetta or 2**70 */ overflow = bkm_scale_by_power (&tmp, base, 7); break; default: *val = tmp; return err | LONGINT_INVALID_SUFFIX_CHAR; } err |= overflow; *p += suffixes; if (**p) err |= LONGINT_INVALID_SUFFIX_CHAR; } *val = tmp; return err; }
strtol_error __xstrtol (const char *s, char **ptr, int strtol_base, __strtol_t *val, const char *valid_suffixes) { char *t_ptr; char **p; __strtol_t tmp; assert (0 <= strtol_base && strtol_base <= 36); p = (ptr ? ptr : &t_ptr); if (! TYPE_SIGNED (__strtol_t)) { const char *q = s; while (ISSPACE ((unsigned char) *q)) ++q; if (*q == '-') return LONGINT_INVALID; } errno = 0; tmp = __strtol (s, p, strtol_base); if (errno != 0) return LONGINT_OVERFLOW; if (*p == s) return LONGINT_INVALID; /* Let valid_suffixes == NULL mean `allow any suffix'. */ /* FIXME: update all callers except the ones that allow suffixes after the number, changing last parameter NULL to `""'. */ if (!valid_suffixes) { *val = tmp; return LONGINT_OK; } if (**p != '\0') { int base = 1024; int suffixes = 1; int overflow; if (!strchr (valid_suffixes, **p)) { *val = tmp; return LONGINT_INVALID_SUFFIX_CHAR; } if (strchr (valid_suffixes, '0')) { /* The ``valid suffix'' '0' is a special flag meaning that an optional second suffix is allowed, which can change the base, e.g. "100MD" for 100 megabytes decimal. */ switch (p[0][1]) { case 'B': suffixes++; break; case 'D': base = 1000; suffixes++; break; } } switch (**p) { case 'b': overflow = bkm_scale (&tmp, 512); break; case 'B': overflow = bkm_scale (&tmp, 1024); break; case 'c': overflow = 0; break; case 'E': /* Exa */ overflow = bkm_scale_by_power (&tmp, base, 6); break; case 'G': /* Giga */ overflow = bkm_scale_by_power (&tmp, base, 3); break; case 'k': /* kilo */ overflow = bkm_scale_by_power (&tmp, base, 1); break; case 'M': /* Mega */ case 'm': /* 'm' is undocumented; for backward compatibility only */ overflow = bkm_scale_by_power (&tmp, base, 2); break; case 'P': /* Peta */ overflow = bkm_scale_by_power (&tmp, base, 5); break; case 'T': /* Tera */ overflow = bkm_scale_by_power (&tmp, base, 4); break; case 'w': overflow = bkm_scale (&tmp, 2); break; case 'Y': /* Yotta */ overflow = bkm_scale_by_power (&tmp, base, 8); break; case 'Z': /* Zetta */ overflow = bkm_scale_by_power (&tmp, base, 7); break; default: *val = tmp; return LONGINT_INVALID_SUFFIX_CHAR; break; } if (overflow) return LONGINT_OVERFLOW; (*p) += suffixes; } *val = tmp; return LONGINT_OK; }
#if (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901) && !defined restrict # define restrict /* empty */ #endif #ifndef TYPE_BIT #define TYPE_BIT(type) (sizeof (type) * CHAR_BIT) #endif /* !defined TYPE_BIT */ #ifndef TYPE_SIGNED #define TYPE_SIGNED(type) (((type) -1) < 0) #endif /* !defined TYPE_SIGNED */ /* The minimum and maximum finite time values. */ static time_t const time_t_min = (TYPE_SIGNED(time_t) ? (time_t) -1 << (CHAR_BIT * sizeof (time_t) - 1) : 0); static time_t const time_t_max = (TYPE_SIGNED(time_t) ? - (~ 0 < 0) - ((time_t) -1 << (CHAR_BIT * sizeof (time_t) - 1)) : -1); #ifndef TZ_MAX_TIMES #define TZ_MAX_TIMES 2000 #endif /* !defined TZ_MAX_TIMES */ #ifndef TZ_MAX_TYPES /* This must be at least 17 for Europe/Samara and Europe/Vilnius. */ #define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
static char * _fmt(const char *format, const struct tm *const t, char * pt, const char *const ptlim, int *warnp) { for ( ; *format; ++format) { if (*format == '%') { label: switch (*++format) { case '\0': --format; break; case 'A': pt = _add((t->tm_wday < 0 || t->tm_wday >= DAYSPERWEEK) ? "?" : Locale->weekday[t->tm_wday], pt, ptlim); continue; case 'a': pt = _add((t->tm_wday < 0 || t->tm_wday >= DAYSPERWEEK) ? "?" : Locale->wday[t->tm_wday], pt, ptlim); continue; case 'B': pt = _add((t->tm_mon < 0 || t->tm_mon >= MONSPERYEAR) ? "?" : Locale->month[t->tm_mon], pt, ptlim); continue; case 'b': case 'h': pt = _add((t->tm_mon < 0 || t->tm_mon >= MONSPERYEAR) ? "?" : Locale->mon[t->tm_mon], pt, ptlim); continue; case 'C': /* ** %C used to do a... ** _fmt("%a %b %e %X %Y", t); ** ...whereas now POSIX 1003.2 calls for ** something completely different. ** (ado, 1993-05-24) */ pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 0, pt, ptlim); continue; case 'c': { int warn2 = IN_SOME; pt = _fmt(Locale->c_fmt, t, pt, ptlim, &warn2); if (warn2 == IN_ALL) warn2 = IN_THIS; if (warn2 > *warnp) *warnp = warn2; } continue; case 'D': pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp); continue; case 'd': pt = _conv(t->tm_mday, "%02d", pt, ptlim); continue; case 'E': case 'O': /* ** C99 locale modifiers. ** The sequences ** %Ec %EC %Ex %EX %Ey %EY ** %Od %oe %OH %OI %Om %OM ** %OS %Ou %OU %OV %Ow %OW %Oy ** are supposed to provide alternate ** representations. */ goto label; case 'e': pt = _conv(t->tm_mday, "%2d", pt, ptlim); continue; case 'F': pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp); continue; case 'H': pt = _conv(t->tm_hour, "%02d", pt, ptlim); continue; case 'I': pt = _conv((t->tm_hour % 12) ? (t->tm_hour % 12) : 12, "%02d", pt, ptlim); continue; case 'j': pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim); continue; case 'k': /* ** This used to be... ** _conv(t->tm_hour % 12 ? ** t->tm_hour % 12 : 12, 2, ' '); ** ...and has been changed to the below to ** match SunOS 4.1.1 and Arnold Robbins' ** strftime version 3.0. That is, "%k" and ** "%l" have been swapped. ** (ado, 1993-05-24) */ pt = _conv(t->tm_hour, "%2d", pt, ptlim); continue; #ifdef KITCHEN_SINK case 'K': /* ** After all this time, still unclaimed! */ pt = _add("kitchen sink", pt, ptlim); continue; #endif /* defined KITCHEN_SINK */ case 'l': /* ** This used to be... ** _conv(t->tm_hour, 2, ' '); ** ...and has been changed to the below to ** match SunOS 4.1.1 and Arnold Robbin's ** strftime version 3.0. That is, "%k" and ** "%l" have been swapped. ** (ado, 1993-05-24) */ pt = _conv((t->tm_hour % 12) ? (t->tm_hour % 12) : 12, "%2d", pt, ptlim); continue; case 'M': pt = _conv(t->tm_min, "%02d", pt, ptlim); continue; case 'm': pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim); continue; case 'n': pt = _add("\n", pt, ptlim); continue; case 'p': pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ? Locale->pm : Locale->am, pt, ptlim); continue; case 'R': pt = _fmt("%H:%M", t, pt, ptlim, warnp); continue; case 'r': pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp); continue; case 'S': pt = _conv(t->tm_sec, "%02d", pt, ptlim); continue; case 's': { struct tm tm; char buf[INT_STRLEN_MAXIMUM( time_t) + 1]; time_t mkt; tm = *t; mkt = mktime(&tm); if (TYPE_SIGNED(time_t)) (void) sprintf(buf, "%"PRIdMAX, (intmax_t) mkt); else (void) sprintf(buf, "%"PRIuMAX, (uintmax_t) mkt); pt = _add(buf, pt, ptlim); } continue; case 'T': pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp); continue; case 't': pt = _add("\t", pt, ptlim); continue; case 'U': pt = _conv((t->tm_yday + DAYSPERWEEK - t->tm_wday) / DAYSPERWEEK, "%02d", pt, ptlim); continue; case 'u': /* ** From Arnold Robbins' strftime version 3.0: ** "ISO 8601: Weekday as a decimal number ** [1 (Monday) - 7]" ** (ado, 1993-05-24) */ pt = _conv((t->tm_wday == 0) ? DAYSPERWEEK : t->tm_wday, "%d", pt, ptlim); continue; case 'V': /* ISO 8601 week number */ case 'G': /* ISO 8601 year (four digits) */ case 'g': /* ISO 8601 year (two digits) */ /* ** From Arnold Robbins' strftime version 3.0: "the week number of the ** year (the first Monday as the first day of week 1) as a decimal number ** (01-53)." ** (ado, 1993-05-24) ** ** From "http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html" by Markus Kuhn: ** "Week 01 of a year is per definition the first week which has the ** Thursday in this year, which is equivalent to the week which contains ** the fourth day of January. In other words, the first week of a new year ** is the week which has the majority of its days in the new year. Week 01 ** might also contain days from the previous year and the week before week ** 01 of a year is the last week (52 or 53) of the previous year even if ** it contains days from the new year. A week starts with Monday (day 1) ** and ends with Sunday (day 7). For example, the first week of the year ** 1997 lasts from 1996-12-30 to 1997-01-05..." ** (ado, 1996-01-02) */ { int year; int base; int yday; int wday; int w; year = t->tm_year; base = TM_YEAR_BASE; yday = t->tm_yday; wday = t->tm_wday; for ( ; ; ) { int len; int bot; int top; len = isleap_sum(year, base) ? DAYSPERLYEAR : DAYSPERNYEAR; /* ** What yday (-3 ... 3) does ** the ISO year begin on? */ bot = ((yday + 11 - wday) % DAYSPERWEEK) - 3; /* ** What yday does the NEXT ** ISO year begin on? */ top = bot - (len % DAYSPERWEEK); if (top < -3) top += DAYSPERWEEK; top += len; if (yday >= top) { ++base; w = 1; break; } if (yday >= bot) { w = 1 + ((yday - bot) / DAYSPERWEEK); break; } --base; yday += isleap_sum(year, base) ? DAYSPERLYEAR : DAYSPERNYEAR; } #ifdef XPG4_1994_04_09 if ((w == 52 && t->tm_mon == TM_JANUARY) || (w == 1 && t->tm_mon == TM_DECEMBER)) w = 53; #endif /* defined XPG4_1994_04_09 */ if (*format == 'V') pt = _conv(w, "%02d", pt, ptlim); else if (*format == 'g') { *warnp = IN_ALL; pt = _yconv(year, base, 0, 1, pt, ptlim); } else pt = _yconv(year, base, 1, 1, pt, ptlim); } continue; case 'v': /* ** From Arnold Robbins' strftime version 3.0: ** "date as dd-bbb-YYYY" ** (ado, 1993-05-24) */ pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp); continue; case 'W': pt = _conv((t->tm_yday + DAYSPERWEEK - (t->tm_wday ? (t->tm_wday - 1) : (DAYSPERWEEK - 1))) / DAYSPERWEEK, "%02d", pt, ptlim); continue; case 'w': pt = _conv(t->tm_wday, "%d", pt, ptlim); continue; case 'X': pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp); continue; case 'x': { int warn2 = IN_SOME; pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2); if (warn2 == IN_ALL) warn2 = IN_THIS; if (warn2 > *warnp) *warnp = warn2; } continue; case 'y': *warnp = IN_ALL; pt = _yconv(t->tm_year, TM_YEAR_BASE, 0, 1, pt, ptlim); continue; case 'Y': pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 1, pt, ptlim); continue; case 'Z': #ifdef TM_ZONE if (t->TM_ZONE != NULL) pt = _add(t->TM_ZONE, pt, ptlim); else #endif /* defined TM_ZONE */ if (t->tm_isdst >= 0) pt = _add(tzname[t->tm_isdst != 0], pt, ptlim); /* ** C99 says that %Z must be replaced by the ** empty string if the time zone is not ** determinable. */ continue; case 'z': { long diff; char const * sign; if (t->tm_isdst < 0) continue; #ifdef TM_GMTOFF diff = t->TM_GMTOFF; #else /* !defined TM_GMTOFF */ /* ** C99 says that the UT offset must ** be computed by looking only at ** tm_isdst. This requirement is ** incorrect, since it means the code ** must rely on magic (in this case ** altzone and timezone), and the ** magic might not have the correct ** offset. Doing things correctly is ** tricky and requires disobeying C99; ** see GNU C strftime for details. ** For now, punt and conform to the ** standard, even though it's incorrect. ** ** C99 says that %z must be replaced by the ** empty string if the time zone is not ** determinable, so output nothing if the ** appropriate variables are not available. */ if (t->tm_isdst == 0) #ifdef USG_COMPAT diff = -timezone; #else /* !defined USG_COMPAT */ continue; #endif /* !defined USG_COMPAT */ else #ifdef ALTZONE diff = -altzone; #else /* !defined ALTZONE */ continue; #endif /* !defined ALTZONE */ #endif /* !defined TM_GMTOFF */ if (diff < 0) { sign = "-"; diff = -diff; } else sign = "+"; pt = _add(sign, pt, ptlim); diff /= SECSPERMIN; diff = (diff / MINSPERHOUR) * 100 + (diff % MINSPERHOUR); pt = _conv(diff, "%04d", pt, ptlim); } continue; case '+': pt = _fmt(Locale->date_fmt, t, pt, ptlim, warnp); continue; case '%': /* ** X311J/88-090 (4.12.3.5): if conversion char is ** undefined, behavior is undefined. Print out the ** character itself as printf(3) also does. */ default: break; } }
TzTransitionList parseTimeZone(const char* tzName) { static const char* zoneInfoDir = "/usr/share/zoneinfo/"; static const char* etcZoneInfoDir = "/usr/share/zoneinfo/Etc/"; std::string filePath = zoneInfoDir; filePath += tzName; struct stat stBuf; FILE* fp = fopen(filePath.c_str(), "r"); if (!fp && errno == ENOENT) { // if file not found - try alternative filePath printf("Failed to find file: %s\n", filePath.c_str()); filePath = etcZoneInfoDir; filePath += tzName; fp = fopen(filePath.c_str(), "r"); if (!fp && errno == ENOENT) { printf("Failed to find second try file: %s\n", filePath.c_str()); return TzTransitionList(); } } if (fp) { if (fstat(fileno(fp), &stBuf) != 0) { printf("Failed to stat opened file: %s\n", filePath.c_str()); fclose(fp); return TzTransitionList(); } if (stBuf.st_size <= (int) sizeof(tzhead)) { printf("file too short to be a tz file: %s\n", filePath.c_str()); fclose(fp); return TzTransitionList(); } } else { printf("Failed to open file: %s\n", filePath.c_str()); return TzTransitionList(); } char* buf = (char*) malloc(stBuf.st_size); size_t size = fread(buf, 1, stBuf.st_size, fp); fclose(fp); if (size != (size_t) stBuf.st_size) { printf("Short read on file: %s\n", filePath.c_str()); free(buf); return TzTransitionList(); } ttentrylist ttEntryList; ttinfolist ttInfoList; std::vector<char> ttAbbrList; /* The time zone information files used by tzset(3) begin with the magic characters "TZif" to identify then as time zone information files, followed by sixteen bytes reserved for future use, followed by six four-byte values of type long, written in a "standard" byte order (the high-order byte of the value is written first). These values are, in order: tzh_ttisgmtcnt The number of UTC/local indicators stored in the file. tzh_ttisstdcnt The number of standard/wall indicators stored in the file. tzh_leapcnt The number of leap seconds for which data is stored in the file. tzh_timecnt The number of "transition times" for which data is stored in the file. tzh_typecnt The number of "local time types" for which data is stored in the file (must not be zero). tzh_charcnt The number of characters of "time zone abbreviation strings" stored in the file. */ long isGmt = 0; long isStd = 0; long leapCnt = 0; long timeCnt = 0; long typeCnt = 0; long charCnt = 0; (void) isGmt; (void) isStd; (void) leapCnt; (void) timeCnt; (void) typeCnt; (void) charCnt; int index = 0; for (int stored = 4; stored <= 8; stored *= 2) { DBG("-----------------------------------------------------\n"); if (memcmp(buf, TZ_MAGIC, 4) != 0) { printf("Not a tz file. Header signature mismatch: %s\n", filePath.c_str()); free(buf); return TzTransitionList(); } struct tzhead* head = (struct tzhead*) (buf + index); isGmt = detzcode(head->tzh_ttisgmtcnt); isStd = detzcode(head->tzh_ttisstdcnt); leapCnt = detzcode(head->tzh_leapcnt); timeCnt = detzcode(head->tzh_timecnt); typeCnt = detzcode(head->tzh_typecnt); charCnt = detzcode(head->tzh_charcnt); ttInfoList.clear(); ttInfoList.reserve(timeCnt); ttEntryList.clear(); ttEntryList.reserve(typeCnt); ttAbbrList.clear(); ttAbbrList.reserve(charCnt + 1); index += sizeof(struct tzhead); DBG("tzh_ttisgmtcnt: %ld\n", isGmt); DBG("tzh_ttisstdcnt: %ld\n", isStd); DBG("tzh_leapcnt: %ld\n", leapCnt); DBG("tzh_timecnt: %ld\n", timeCnt); DBG("tzh_typecnt: %ld\n", typeCnt); DBG("tzh_charcnt: %ld\n", charCnt); /* The above header is followed by tzh_timecnt four-byte values of type long, sorted in ascending order. These values are written in "standard" byte order. Each is used as a transition time (as returned by time(2)) at which the rules for computing local time change. */ for (long i = 0; i < timeCnt; i++) { time_t time; time = (stored == 4) ? detzcode(buf + index) : detzcode64(buf + index); index += stored; ttentry e; e.time = time; ttEntryList.push_back(e); DBG("tzh_timecnt: Time: %ld, %s", time, asctime(gmtime(&time))); } /* Next come tzh_timecnt one-byte values of type unsigned char; each one tells which of the different types of "local time" types described in the file is associated with the same-indexed transition time. */ for (long i = 0; i < timeCnt; i++) { unsigned char indexToLocalTime = (unsigned char) buf[index]; index++; ttEntryList[i].indexToLocalTime = indexToLocalTime; DBG("tzh_timecnt: Index: %d\n", indexToLocalTime); } /* These values serve as indices into an array of ttinfo structures that appears next in the file; these structures are defined as follows: struct ttinfo { long tt_gmtoff; int tt_isdst; unsigned int tt_abbrind; }; Each structure is written as a four-byte value for tt_gmtoff of type long, in a standard byte order, followed by a one-byte value for tt_isdst and a one-byte value for tt_abbrind. In each structure, tt_gmtoff gives the number of seconds to be added to UTC, tt_isdst tells whether tm_isdst should be set by localtime(3), and tt_abbrind serves as an index into the array of time zone abbreviation characters that follow the ttinfo structure(s) in the file. */ for (long i = 0; i < typeCnt; i++) { ttinfo ttInfo; ttInfo.gmtOffset = detzcode(buf + index); index += 4; ttInfo.isDst = (unsigned char) buf[index]; index++; ttInfo.abbrIndex = (unsigned char) buf[index]; index++; ttInfoList.push_back(ttInfo); DBG("gmtOff: %ld, isDst: %d, abbrind: %d\n", ttInfo.gmtOffset, ttInfo.isDst, ttInfo.abbrIndex); } DBG("abbrev: "); for (long i = 0; i < charCnt; i++) { DBG("%c", buf[index]); ttAbbrList.push_back(buf[index]); index++; } ttAbbrList.push_back(0); DBG("\n"); /* Then there are tzh_leapcnt pairs of four-byte values, written in standard byte order; the first value of each pair gives the time (as returned by time(2)) at which a leap second occurs; the second gives the total number of leap seconds to be applied after the given time. The pairs of values are sorted in ascending order by time. */ for (long i = 0; i < leapCnt; i++) { long leapTime = (stored == 4) ? detzcode(buf + index) : detzcode64(buf + index); index += stored; long leapSeconds = detzcode(buf + index); index += 4; (void) leapTime; (void) leapSeconds; DBG("leapTime: %ld, leapSeconds: %ld\n", leapTime, leapSeconds); } /* Then there are tzh_ttisstdcnt standard/wall indicators, each stored as a one-byte value; they tell whether the transition times associated with local time types were specified as standard time or wall clock time, and are used when a time zone file is used in handling POSIX-style time zone environment variables. */ for (long i = 0; i < typeCnt; i++) { int standardOrWallClock = (unsigned char) buf[index]; index++; (void) standardOrWallClock; DBG("standardOrWallClock: %d\n", standardOrWallClock); } /* Finally, there are tzh_ttisgmtcnt UTC/local indicators, each stored as a one-byte value; they tell whether the transition times associated with local time types were specified as UTC or local time, and are used when a time zone file is used in handling POSIX-style time zone environment variables. */ for (long i = 0; i < typeCnt; i++) { int utcOrLocalTime = (unsigned char) buf[index]; index++; (void) utcOrLocalTime; DBG("utcOrLocalTime: %d\n", utcOrLocalTime); } /* Localtime uses the first standard-time ttinfo structure in the file (or simply the first ttinfo structure in the absence of a standard-time structure) if either tzh_timecnt is zero or the time argument is less than the first transition time recorded in the file. */ /* * Out-of-sort ats should mean we're running on a * signed time_t system but using a data file with * unsigned values (or vice versa). */ for (int i = 0; i < timeCnt - 2; ++i) { if (ttEntryList[i].time > ttEntryList[i+1].time) { ++i; if (TYPE_SIGNED(time_t)) { // Ignore the end (easy). timeCnt = i; } else { // Ignore the beginning (harder). int j; for (j = 0; (j + i) < timeCnt; ++j) { ttEntryList[j] = ttEntryList[j+i]; } timeCnt = j; } break; } } /* * If this is an old file, we're done. */ if (head->tzh_version[0] == '\0') { DBG("Version 0 file. breaking\n"); break; } /* * If this is a narrow integer time_t system, we're done. */ if ((stored >= (int) sizeof(time_t)) && (TYPE_INTEGRAL(time_t))) { DBG("narrow integer time_t system. breaking\n"); break; } } DBG("Total Buffer size parsed: %d\n", index); free(buf); // Dummy entry for standardized timezones which never had // a transition time if (ttEntryList.empty() && !ttInfoList.empty()) { ttentry e; timeCnt = 1; e.time = -2147483648UL; e.indexToLocalTime = 0; ttEntryList.push_back(e); } TzTransitionList result; for (int i = 0; i < timeCnt; i++) { const ttentry& entry = ttEntryList[i]; const ttinfo& info = ttInfoList[entry.indexToLocalTime]; struct tm* gmTime = gmtime(&entry.time); TzTransition trans; trans.time = entry.time; trans.utcOffset = info.gmtOffset; trans.isDst = info.isDst; trans.year = gmTime->tm_year + 1900; trans.abbrName[0] = 0; int j = info.abbrIndex; int k = 0; for (; (j < (int) ttAbbrList.size()) && (k < TZ_ABBR_MAX_LEN); j++, k++) { trans.abbrName[k] = ttAbbrList[j]; if (ttAbbrList[j] == 0) break; } trans.abbrName[TZ_ABBR_MAX_LEN-1] = 0; result.push_back(trans); } return result; }
int main (void) { unsigned int i; int fail = 0; char curr_year_str[30]; struct tm *tm; time_t t_now; int err; size_t n_bytes; /* The above test data requires Universal Time, e.g., TZ="UTC0". */ err = setenv ("TZ", "UTC0", 1); ASSERT (err == 0); t_now = time (NULL); ASSERT (t_now != (time_t) -1); tm = localtime (&t_now); ASSERT (tm); n_bytes = strftime (curr_year_str, sizeof curr_year_str, "%Y", tm); ASSERT (0 < n_bytes); for (i = 0; T[i].in; i++) { time_t t_out; time_t t_exp = T[i].t_expected; bool ok; /* Some tests assume that time_t is signed. If it is unsigned and the result is negative, skip the test. */ if (T[i].t_expected < 0 && ! TYPE_SIGNED (time_t)) { printf ("skipping %s: result is negative, " "but your time_t is unsigned\n", T[i].in); continue; } if (T[i].valid && t_exp != T[i].t_expected) { printf ("skipping %s: result is out of range of your time_t\n", T[i].in); continue; } /* If an input string does not specify the year number, determine the expected output by calling posixtime with an otherwise equivalent string that starts with the current year. */ if (8 <= strlen (T[i].in) && (T[i].in[8] == '.' || T[i].in[8] == '\0')) { char tmp_buf[20]; stpcpy (stpcpy (tmp_buf, curr_year_str), T[i].in); ASSERT (posixtime (&t_exp, tmp_buf, T[i].syntax_bits)); } ok = posixtime (&t_out, T[i].in, T[i].syntax_bits); if (ok != !!T[i].valid) { printf ("%s return value mismatch: got %d, expected %d\n", T[i].in, !!ok, T[i].valid); fail = 1; continue; } if (!ok) continue; if (t_out != t_exp) { printf ("%s mismatch (-: actual; +:expected)\n-%12ld\n+%12ld\n", T[i].in, t_out, t_exp); fail = 1; } } return fail; }
static char * _fmt(const char * format, const struct tm * const t, char * pt, const char * const ptlim, int * warnp) { for ( ; *format; ++format) { if (*format == '%') { label: switch (*++format) { case '\0': --format; break; case 'A': pt = _add((t->tm_wday < 0 || t->tm_wday >= DAYSPERWEEK) ? "?" : Locale->weekday[t->tm_wday], pt, ptlim); continue; case 'a': pt = _add((t->tm_wday < 0 || t->tm_wday >= DAYSPERWEEK) ? "?" : Locale->wday[t->tm_wday], pt, ptlim); continue; case 'B': pt = _add((t->tm_mon < 0 || t->tm_mon >= MONSPERYEAR) ? "?" : Locale->month[t->tm_mon], pt, ptlim); continue; case 'b': case 'h': pt = _add((t->tm_mon < 0 || t->tm_mon >= MONSPERYEAR) ? "?" : Locale->mon[t->tm_mon], pt, ptlim); continue; case 'C': /* ** %C used to do a... ** _fmt("%a %b %e %X %Y", t); ** ...whereas now POSIX 1003.2 calls for ** something completely different. ** (ado, 1993-05-24) */ pt = _conv((t->tm_year + TM_YEAR_BASE) / 100, "%02d", pt, ptlim); continue; case 'c': { int warn2 = IN_SOME; pt = _fmt(Locale->c_fmt, t, pt, ptlim, warnp); if (warn2 == IN_ALL) warn2 = IN_THIS; if (warn2 > *warnp) *warnp = warn2; } continue; case 'D': pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp); continue; case 'd': pt = _conv(t->tm_mday, "%02d", pt, ptlim); continue; case 'E': case 'O': /* ** C99 locale modifiers. ** The sequences ** %Ec %EC %Ex %EX %Ey %EY ** %Od %oe %OH %OI %Om %OM ** %OS %Ou %OU %OV %Ow %OW %Oy ** are supposed to provide alternate ** representations. */ goto label; case 'e': pt = _conv(t->tm_mday, "%2d", pt, ptlim); continue; case 'F': pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp); continue; case 'H': pt = _conv(t->tm_hour, "%02d", pt, ptlim); continue; case 'I': pt = _conv((t->tm_hour % 12) ? (t->tm_hour % 12) : 12, "%02d", pt, ptlim); continue; case 'j': pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim); continue; case 'k': /* ** This used to be... ** _conv(t->tm_hour % 12 ? ** t->tm_hour % 12 : 12, 2, ' '); ** ...and has been changed to the below to ** match SunOS 4.1.1 and Arnold Robbins' ** strftime version 3.0. That is, "%k" and ** "%l" have been swapped. ** (ado, 1993-05-24) */ pt = _conv(t->tm_hour, "%2d", pt, ptlim); continue; #ifdef KITCHEN_SINK case 'K': /* ** After all this time, still unclaimed! */ pt = _add("kitchen sink", pt, ptlim); continue; #endif /* defined KITCHEN_SINK */ case 'l': /* ** This used to be... ** _conv(t->tm_hour, 2, ' '); ** ...and has been changed to the below to ** match SunOS 4.1.1 and Arnold Robbin's ** strftime version 3.0. That is, "%k" and ** "%l" have been swapped. ** (ado, 1993-05-24) */ pt = _conv((t->tm_hour % 12) ? (t->tm_hour % 12) : 12, "%2d", pt, ptlim); continue; case 'M': pt = _conv(t->tm_min, "%02d", pt, ptlim); continue; case 'm': pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim); continue; case 'n': pt = _add("\n", pt, ptlim); continue; case 'p': pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ? Locale->pm : Locale->am, pt, ptlim); continue; case 'R': pt = _fmt("%H:%M", t, pt, ptlim, warnp); continue; case 'r': pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp); continue; case 'S': pt = _conv(t->tm_sec, "%02d", pt, ptlim); continue; case 's': { struct tm tm; char buf[INT_STRLEN_MAXIMUM( time_t) + 1]; time_t mkt; tm = *t; mkt = mktime(&tm); if (TYPE_SIGNED(time_t)) (void) _snprintf(buf, sizeof buf, "%ld", (long) mkt); else (void) _snprintf(buf, sizeof buf, "%lu", (unsigned long) mkt); pt = _add(buf, pt, ptlim); } continue; case 'T': pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp); continue; case 't': pt = _add("\t", pt, ptlim); continue; case 'U': pt = _conv((t->tm_yday + DAYSPERWEEK - t->tm_wday) / DAYSPERWEEK, "%02d", pt, ptlim); continue; case 'u': /* ** From Arnold Robbins' strftime version 3.0: ** "ISO 8601: Weekday as a decimal number ** [1 (Monday) - 7]" ** (ado, 1993-05-24) */ pt = _conv((t->tm_wday == 0) ? DAYSPERWEEK : t->tm_wday, "%d", pt, ptlim); continue; case 'V': /* ISO 8601 week number */ case 'G': /* ISO 8601 year (four digits) */ case 'g': /* ISO 8601 year (two digits) */ { int year; int yday; int wday; int w; year = t->tm_year + TM_YEAR_BASE; yday = t->tm_yday; wday = t->tm_wday; for ( ; ; ) { int len; int bot; int top; len = isleap(year) ? DAYSPERLYEAR : DAYSPERNYEAR; /* ** What yday (-3 ... 3) does ** the ISO year begin on? */ bot = ((yday + 11 - wday) % DAYSPERWEEK) - 3; /* ** What yday does the NEXT ** ISO year begin on? */ top = bot - (len % DAYSPERWEEK); if (top < -3) top += DAYSPERWEEK; top += len; if (yday >= top) { ++year; w = 1; break; } if (yday >= bot) { w = 1 + ((yday - bot) / DAYSPERWEEK); break; } --year; yday += isleap(year) ? DAYSPERLYEAR : DAYSPERNYEAR; } if (*format == 'V') pt = _conv(w, "%02d", pt, ptlim); else if (*format == 'g') { *warnp = IN_ALL; pt = _conv(year % 100, "%02d", pt, ptlim); } else pt = _conv(year, "%04d", pt, ptlim); } continue; case 'v': pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp); continue; case 'W': pt = _conv((t->tm_yday + DAYSPERWEEK - (t->tm_wday ? (t->tm_wday - 1) : (DAYSPERWEEK - 1))) / DAYSPERWEEK, "%02d", pt, ptlim); continue; case 'w': pt = _conv(t->tm_wday, "%d", pt, ptlim); continue; case 'X': pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp); continue; case 'x': { int warn2 = IN_SOME; pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2); if (warn2 == IN_ALL) warn2 = IN_THIS; if (warn2 > *warnp) *warnp = warn2; } continue; case 'y': *warnp = IN_ALL; pt = _conv((t->tm_year + TM_YEAR_BASE) % 100, "%02d", pt, ptlim); continue; case 'Y': pt = _conv(t->tm_year + TM_YEAR_BASE, "%04d", pt, ptlim); continue; case 'Z': if (t->tm_isdst >= 0) pt = _add(tzname[t->tm_isdst != 0], pt, ptlim); /* ** C99 says that %Z must be replaced by the ** empty string if the time zone is not ** determinable. */ continue; case 'z': { int diff; char const * sign; if (t->tm_isdst < 0) continue; continue; if (diff < 0) { sign = "-"; diff = -diff; } else sign = "+"; pt = _add(sign, pt, ptlim); diff /= 60; pt = _conv((diff/60)*100 + diff%60, "%04d", pt, ptlim); } continue; case '+': pt = _fmt(Locale->date_fmt, t, pt, ptlim, warnp); continue; case '%': default: break; } } if (pt == ptlim) break; *pt++ = *format; } return pt; }
#ifdef O_BINARY #define OPEN_MODE (O_RDONLY | O_BINARY) #endif /* defined O_BINARY */ #ifndef O_BINARY #define OPEN_MODE O_RDONLY #endif /* !defined O_BINARY */ /* Complex computations to determine the min/max of time_t depending * on TYPE_BIT / TYPE_SIGNED / TYPE_INTEGRAL. * These macros cannot be used in pre-processor directives, so we * let the C compiler do the work, which makes things a bit funky. */ static const time_t TIME_T_MAX = TYPE_INTEGRAL(time_t) ? ( TYPE_SIGNED(time_t) ? ~((time_t)1 << (TYPE_BIT(time_t)-1)) : ~(time_t)0 ) : /* if time_t is a floating point number */ ( sizeof(time_t) > sizeof(float) ? (time_t)DBL_MAX : (time_t)FLT_MAX ); static const time_t TIME_T_MIN = TYPE_INTEGRAL(time_t) ? ( TYPE_SIGNED(time_t) ? ((time_t)1 << (TYPE_BIT(time_t)-1)) : 0 ) :
int tzload(const char *name, char *canonname, struct state * sp, int doextend) { const char *p; int i; int fid; int stored; int nread; union { struct tzhead tzhead; char buf[2 * sizeof(struct tzhead) + 2 * sizeof *sp + 4 * TZ_MAX_TIMES]; } u; sp->goback = sp->goahead = FALSE; if (name == NULL && (name = TZDEFAULT) == NULL) return -1; if (name[0] == ':') ++name; fid = pg_open_tzfile(name, canonname); if (fid < 0) return -1; nread = read(fid, u.buf, sizeof u.buf); if (close(fid) != 0 || nread <= 0) return -1; for (stored = 4; stored <= 8; stored *= 2) { int ttisstdcnt; int ttisgmtcnt; ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt); ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt); sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt); sp->timecnt = (int) detzcode(u.tzhead.tzh_timecnt); sp->typecnt = (int) detzcode(u.tzhead.tzh_typecnt); sp->charcnt = (int) detzcode(u.tzhead.tzh_charcnt); p = u.tzhead.tzh_charcnt + sizeof u.tzhead.tzh_charcnt; if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS || sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES || sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES || sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS || (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) || (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0)) return -1; if (nread - (p - u.buf) < sp->timecnt * stored + /* ats */ sp->timecnt + /* types */ sp->typecnt * 6 + /* ttinfos */ sp->charcnt + /* chars */ sp->leapcnt * (stored + 4) + /* lsinfos */ ttisstdcnt + /* ttisstds */ ttisgmtcnt) /* ttisgmts */ return -1; for (i = 0; i < sp->timecnt; ++i) { sp->ats[i] = (stored == 4) ? detzcode(p) : detzcode64(p); p += stored; } for (i = 0; i < sp->timecnt; ++i) { sp->types[i] = (unsigned char) *p++; if (sp->types[i] >= sp->typecnt) return -1; } for (i = 0; i < sp->typecnt; ++i) { struct ttinfo *ttisp; ttisp = &sp->ttis[i]; ttisp->tt_gmtoff = detzcode(p); p += 4; ttisp->tt_isdst = (unsigned char) *p++; if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1) return -1; ttisp->tt_abbrind = (unsigned char) *p++; if (ttisp->tt_abbrind < 0 || ttisp->tt_abbrind > sp->charcnt) return -1; } for (i = 0; i < sp->charcnt; ++i) sp->chars[i] = *p++; sp->chars[i] = '\0'; /* ensure '\0' at end */ for (i = 0; i < sp->leapcnt; ++i) { struct lsinfo *lsisp; lsisp = &sp->lsis[i]; lsisp->ls_trans = (stored == 4) ? detzcode(p) : detzcode64(p); p += stored; lsisp->ls_corr = detzcode(p); p += 4; } for (i = 0; i < sp->typecnt; ++i) { struct ttinfo *ttisp; ttisp = &sp->ttis[i]; if (ttisstdcnt == 0) ttisp->tt_ttisstd = FALSE; else { ttisp->tt_ttisstd = *p++; if (ttisp->tt_ttisstd != TRUE && ttisp->tt_ttisstd != FALSE) return -1; } } for (i = 0; i < sp->typecnt; ++i) { struct ttinfo *ttisp; ttisp = &sp->ttis[i]; if (ttisgmtcnt == 0) ttisp->tt_ttisgmt = FALSE; else { ttisp->tt_ttisgmt = *p++; if (ttisp->tt_ttisgmt != TRUE && ttisp->tt_ttisgmt != FALSE) return -1; } } /* * Out-of-sort ats should mean we're running on a signed time_t system * but using a data file with unsigned values (or vice versa). */ for (i = 0; i < sp->timecnt - 2; ++i) if (sp->ats[i] > sp->ats[i + 1]) { ++i; if (TYPE_SIGNED(pg_time_t)) { /* * Ignore the end (easy). */ sp->timecnt = i; } else { /* * Ignore the beginning (harder). */ int j; for (j = 0; j + i < sp->timecnt; ++j) { sp->ats[j] = sp->ats[j + i]; sp->types[j] = sp->types[j + i]; } sp->timecnt = j; } break; } /* * If this is an old file, we're done. */ if (u.tzhead.tzh_version[0] == '\0') break; nread -= p - u.buf; for (i = 0; i < nread; ++i) u.buf[i] = p[i]; /* * If this is a narrow integer time_t system, we're done. */ if (stored >= (int) sizeof(pg_time_t) && TYPE_INTEGRAL(pg_time_t)) break; } if (doextend && nread > 2 && u.buf[0] == '\n' && u.buf[nread - 1] == '\n' && sp->typecnt + 2 <= TZ_MAX_TYPES) { struct state ts; int result; u.buf[nread - 1] = '\0'; result = tzparse(&u.buf[1], &ts, FALSE); if (result == 0 && ts.typecnt == 2 && sp->charcnt + ts.charcnt <= TZ_MAX_CHARS) { for (i = 0; i < 2; ++i) ts.ttis[i].tt_abbrind += sp->charcnt; for (i = 0; i < ts.charcnt; ++i) sp->chars[sp->charcnt++] = ts.chars[i]; i = 0; while (i < ts.timecnt && ts.ats[i] <= sp->ats[sp->timecnt - 1]) ++i; while (i < ts.timecnt && sp->timecnt < TZ_MAX_TIMES) { sp->ats[sp->timecnt] = ts.ats[i]; sp->types[sp->timecnt] = sp->typecnt + ts.types[i]; ++sp->timecnt; ++i; } sp->ttis[sp->typecnt++] = ts.ttis[0]; sp->ttis[sp->typecnt++] = ts.ttis[1]; } } if (sp->timecnt > 1) { for (i = 1; i < sp->timecnt; ++i) if (typesequiv(sp, sp->types[i], sp->types[0]) && differ_by_repeat(sp->ats[i], sp->ats[0])) { sp->goback = TRUE; break; } for (i = sp->timecnt - 2; i >= 0; --i) if (typesequiv(sp, sp->types[sp->timecnt - 1], sp->types[i]) && differ_by_repeat(sp->ats[sp->timecnt - 1], sp->ats[i])) { sp->goahead = TRUE; break; } } return 0; }
static time_t time2sub(struct tm * const tmp, struct tm * (* const funcp)(const time_t *, long, struct tm *), const long offset, int * const okayp, const int do_norm_secs) { const struct state * sp; int dir; int i, j; int saved_seconds; long li; time_t lo; time_t hi; long y; time_t newt; time_t t; struct tm yourtm, mytm; *okayp = FALSE; yourtm = *tmp; if (do_norm_secs) { if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec, SECSPERMIN)) return WRONG; } if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR)) return WRONG; if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY)) return WRONG; y = yourtm.tm_year; if (long_normalize_overflow(&y, &yourtm.tm_mon, MONSPERYEAR)) return WRONG; /* ** Turn y into an actual year number for now. ** It is converted back to an offset from TM_YEAR_BASE later. */ if (long_increment_overflow(&y, TM_YEAR_BASE)) return WRONG; while (yourtm.tm_mday <= 0) { if (long_increment_overflow(&y, -1)) return WRONG; li = y + (1 < yourtm.tm_mon); yourtm.tm_mday += year_lengths[isleap(li)]; } while (yourtm.tm_mday > DAYSPERLYEAR) { li = y + (1 < yourtm.tm_mon); yourtm.tm_mday -= year_lengths[isleap(li)]; if (long_increment_overflow(&y, 1)) return WRONG; } for ( ; ; ) { i = mon_lengths[isleap(y)][yourtm.tm_mon]; if (yourtm.tm_mday <= i) break; yourtm.tm_mday -= i; if (++yourtm.tm_mon >= MONSPERYEAR) { yourtm.tm_mon = 0; if (long_increment_overflow(&y, 1)) return WRONG; } } if (long_increment_overflow(&y, -TM_YEAR_BASE)) return WRONG; yourtm.tm_year = y; if (yourtm.tm_year != y) return WRONG; if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN) saved_seconds = 0; else if (y + TM_YEAR_BASE < EPOCH_YEAR) { /* ** We can't set tm_sec to 0, because that might push the ** time below the minimum representable time. ** Set tm_sec to 59 instead. ** This assumes that the minimum representable time is ** not in the same minute that a leap second was deleted from, ** which is a safer assumption than using 58 would be. */ if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN)) return WRONG; saved_seconds = yourtm.tm_sec; yourtm.tm_sec = SECSPERMIN - 1; } else { saved_seconds = yourtm.tm_sec; yourtm.tm_sec = 0; } /* ** Do a binary search (this works whatever time_t's type is). */ if (!TYPE_SIGNED(time_t)) { lo = 0; hi = lo - 1; } else if (!TYPE_INTEGRAL(time_t)) { if (sizeof(time_t) > sizeof(float)) hi = (time_t) DBL_MAX; else hi = (time_t) FLT_MAX; lo = -hi; } else { lo = 1; for (i = 0; i < (int) TYPE_BIT(time_t) - 1; ++i) lo *= 2; hi = -(lo + 1); } for ( ; ; ) { t = lo / 2 + hi / 2; if (t < lo) t = lo; else if (t > hi) t = hi; if ((*funcp)(&t, offset, &mytm) == NULL) { /* ** Assume that t is too extreme to be represented in ** a struct tm; arrange things so that it is less ** extreme on the next pass. */ dir = (t > 0) ? 1 : -1; } else dir = tmcomp(&mytm, &yourtm); if (dir != 0) { if (t == lo) { ++t; if (t <= lo) return WRONG; ++lo; } else if (t == hi) { --t; if (t >= hi) return WRONG; --hi; } if (lo > hi) return WRONG; if (dir > 0) hi = t; else lo = t; continue; } if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst) break; /* ** Right time, wrong type. ** Hunt for right time, right type. ** It's okay to guess wrong since the guess ** gets checked. */ sp = (const struct state *) ((funcp == localsub) ? lclptr : gmtptr); for (i = sp->typecnt - 1; i >= 0; --i) { if (sp->ttis[i].tt_isdst != yourtm.tm_isdst) continue; for (j = sp->typecnt - 1; j >= 0; --j) { if (sp->ttis[j].tt_isdst == yourtm.tm_isdst) continue; newt = t + sp->ttis[j].tt_gmtoff - sp->ttis[i].tt_gmtoff; if ((*funcp)(&newt, offset, &mytm) == NULL) continue; if (tmcomp(&mytm, &yourtm) != 0) continue; if (mytm.tm_isdst != yourtm.tm_isdst) continue; /* ** We have a match. */ t = newt; goto label; } } return WRONG; } label: newt = t + saved_seconds; if ((newt < t) != (saved_seconds < 0)) return WRONG; t = newt; if ((*funcp)(&t, offset, tmp)) *okayp = TRUE; return t; }