root/src/vfs/smbfs/helpers/lib/time.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. GetTimeOfDay
  2. tm_diff
  3. TimeZone
  4. TimeInit
  5. TimeZoneFaster
  6. TimeDiff
  7. LocTimeDiff
  8. LocalTime
  9. nt_time_to_unix
  10. interpret_long_date
  11. unix_to_nt_time
  12. put_long_date
  13. null_mtime
  14. make_dos_date1
  15. make_dos_time1
  16. make_dos_date
  17. put_dos_date
  18. put_dos_date2
  19. put_dos_date3
  20. interpret_dos_date
  21. make_unix_date
  22. make_unix_date2
  23. make_unix_date3
  24. http_timestring
  25. timestring
  26. get_create_time

   1 /*
   2    Unix SMB/Netbios implementation.
   3    Version 1.9.
   4    time handling functions
   5 
   6    Copyright (C) Andrew Tridgell 1992-1998
   7 
   8    Copyright (C) 2011-2019
   9    Free Software Foundation, Inc.
  10 
  11    This file is part of the Midnight Commander.
  12 
  13    The Midnight Commander is free software: you can redistribute it
  14    and/or modify it under the terms of the GNU General Public License as
  15    published by the Free Software Foundation, either version 3 of the License,
  16    or (at your option) any later version.
  17 
  18    The Midnight Commander is distributed in the hope that it will be useful,
  19    but WITHOUT ANY WARRANTY; without even the implied warranty of
  20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21    GNU General Public License for more details.
  22 
  23    You should have received a copy of the GNU General Public License
  24    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  25  */
  26 
  27 #include "includes.h"
  28 
  29 /*
  30    This stuff was largely rewritten by Paul Eggert <eggert@twinsun.com>
  31    in May 1996 
  32  */
  33 
  34 
  35 int serverzone = 0;
  36 int extra_time_offset = 0;
  37 
  38 extern int DEBUGLEVEL;
  39 
  40 #ifndef CHAR_BIT
  41 #define CHAR_BIT 8
  42 #endif
  43 
  44 #ifndef TIME_T_MIN
  45 #define TIME_T_MIN ((time_t)0 < (time_t) -1 ? (time_t) 0 \
  46                     : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1))
  47 #endif
  48 #ifndef TIME_T_MAX
  49 #define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
  50 #endif
  51 
  52 
  53 
  54 /*******************************************************************
  55 a gettimeofday wrapper
  56 ********************************************************************/
  57 void
  58 GetTimeOfDay (struct timeval *tval)
     /* [previous][next][first][last][top][bottom][index][help]  */
  59 {
  60 #ifdef HAVE_GETTIMEOFDAY_TZ
  61     gettimeofday (tval, NULL);
  62 #else
  63     gettimeofday (tval);
  64 #endif
  65 }
  66 
  67 #define TM_YEAR_BASE 1900
  68 
  69 /*******************************************************************
  70 yield the difference between *A and *B, in seconds, ignoring leap seconds
  71 ********************************************************************/
  72 static int
  73 tm_diff (struct tm *a, struct tm *b)
     /* [previous][next][first][last][top][bottom][index][help]  */
  74 {
  75     int ay = a->tm_year + (TM_YEAR_BASE - 1);
  76     int by = b->tm_year + (TM_YEAR_BASE - 1);
  77     int intervening_leap_days = (ay / 4 - by / 4) - (ay / 100 - by / 100) + (ay / 400 - by / 400);
  78     int years = ay - by;
  79     int days = 365 * years + intervening_leap_days + (a->tm_yday - b->tm_yday);
  80     int hours = 24 * days + (a->tm_hour - b->tm_hour);
  81     int minutes = 60 * hours + (a->tm_min - b->tm_min);
  82     int seconds = 60 * minutes + (a->tm_sec - b->tm_sec);
  83 
  84     return seconds;
  85 }
  86 
  87 /*******************************************************************
  88   return the UTC offset in seconds west of UTC, or 0 if it cannot be determined
  89   ******************************************************************/
  90 static int
  91 TimeZone (time_t t)
     /* [previous][next][first][last][top][bottom][index][help]  */
  92 {
  93     struct tm *tm = gmtime (&t);
  94     struct tm tm_utc;
  95     if (!tm)
  96         return 0;
  97     tm_utc = *tm;
  98     tm = localtime (&t);
  99     if (!tm)
 100         return 0;
 101     return tm_diff (&tm_utc, tm);
 102 
 103 }
 104 
 105 
 106 /*******************************************************************
 107 init the time differences
 108 ********************************************************************/
 109 void
 110 TimeInit (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 111 {
 112     serverzone = TimeZone (time (NULL));
 113 
 114     if ((serverzone % 60) != 0)
 115     {
 116         DEBUG (1, ("WARNING: Your timezone is not a multiple of 1 minute.\n"));
 117     }
 118 
 119     DEBUG (4, ("Serverzone is %d\n", serverzone));
 120 }
 121 
 122 
 123 /*******************************************************************
 124 return the same value as TimeZone, but it should be more efficient.
 125 
 126 We keep a table of DST offsets to prevent calling localtime() on each 
 127 call of this function. This saves a LOT of time on many unixes.
 128 
 129 Updated by Paul Eggert <eggert@twinsun.com>
 130 ********************************************************************/
 131 static int
 132 TimeZoneFaster (time_t t)
     /* [previous][next][first][last][top][bottom][index][help]  */
 133 {
 134     static struct dst_table
 135     {
 136         time_t start, end;
 137         int zone;
 138     } *dst_table = NULL;
 139     static int table_size = 0;
 140     int i;
 141     int zone = 0;
 142 
 143     if (t == 0)
 144         t = time (NULL);
 145 
 146     /* Tunis has a 8 day DST region, we need to be careful ... */
 147 #define MAX_DST_WIDTH (365*24*60*60)
 148 #define MAX_DST_SKIP (7*24*60*60)
 149 
 150     for (i = 0; i < table_size; i++)
 151         if (t >= dst_table[i].start && t <= dst_table[i].end)
 152             break;
 153 
 154     if (i < table_size)
 155     {
 156         zone = dst_table[i].zone;
 157     }
 158     else
 159     {
 160         time_t low, high;
 161 
 162         zone = TimeZone (t);
 163         dst_table = (struct dst_table *) Realloc (dst_table, sizeof (dst_table[0]) * (i + 1));
 164         if (!dst_table)
 165         {
 166             table_size = 0;
 167         }
 168         else
 169         {
 170             table_size++;
 171 
 172             dst_table[i].zone = zone;
 173             dst_table[i].start = dst_table[i].end = t;
 174 
 175             /* no entry will cover more than 6 months */
 176             low = t - MAX_DST_WIDTH / 2;
 177             if (t < low)
 178                 low = TIME_T_MIN;
 179 
 180             high = t + MAX_DST_WIDTH / 2;
 181             if (high < t)
 182                 high = TIME_T_MAX;
 183 
 184             /* widen the new entry using two bisection searches */
 185             while (low + 60 * 60 < dst_table[i].start)
 186             {
 187                 if (dst_table[i].start - low > MAX_DST_SKIP * 2)
 188                     t = dst_table[i].start - MAX_DST_SKIP;
 189                 else
 190                     t = low + (dst_table[i].start - low) / 2;
 191                 if (TimeZone (t) == zone)
 192                     dst_table[i].start = t;
 193                 else
 194                     low = t;
 195             }
 196 
 197             while (high - 60 * 60 > dst_table[i].end)
 198             {
 199                 if (high - dst_table[i].end > MAX_DST_SKIP * 2)
 200                     t = dst_table[i].end + MAX_DST_SKIP;
 201                 else
 202                     t = high - (high - dst_table[i].end) / 2;
 203                 if (TimeZone (t) == zone)
 204                     dst_table[i].end = t;
 205                 else
 206                     high = t;
 207             }
 208 #if 0
 209             DEBUG (1, ("Added DST entry from %s ", asctime (localtime (&dst_table[i].start))));
 210             DEBUG (1, ("to %s (%d)\n", asctime (localtime (&dst_table[i].end)), dst_table[i].zone));
 211 #endif
 212         }
 213     }
 214     return zone;
 215 }
 216 
 217 /****************************************************************************
 218   return the UTC offset in seconds west of UTC, adjusted for extra time offset
 219   **************************************************************************/
 220 int
 221 TimeDiff (time_t t)
     /* [previous][next][first][last][top][bottom][index][help]  */
 222 {
 223     return TimeZoneFaster (t) + 60 * extra_time_offset;
 224 }
 225 
 226 
 227 /****************************************************************************
 228   return the UTC offset in seconds west of UTC, adjusted for extra time
 229   offset, for a local time value.  If ut = lt + LocTimeDiff(lt), then
 230   lt = ut - TimeDiff(ut), but the converse does not necessarily hold near
 231   daylight savings transitions because some local times are ambiguous.
 232   LocTimeDiff(t) equals TimeDiff(t) except near daylight savings transitions.
 233   +**************************************************************************/
 234 static int
 235 LocTimeDiff (time_t lte)
     /* [previous][next][first][last][top][bottom][index][help]  */
 236 {
 237     time_t lt = lte - 60 * extra_time_offset;
 238     int d = TimeZoneFaster (lt);
 239     time_t t = lt + d;
 240 
 241     /* if overflow occurred, ignore all the adjustments so far */
 242     if (((lte < lt) ^ (extra_time_offset < 0)) | ((t < lt) ^ (d < 0)))
 243         t = lte;
 244 
 245     /* now t should be close enough to the true UTC to yield the right answer */
 246     return TimeDiff (t);
 247 }
 248 
 249 
 250 /****************************************************************************
 251 try to optimise the localtime call, it can be quite expensive on some machines
 252 ****************************************************************************/
 253 struct tm *
 254 LocalTime (time_t * t)
     /* [previous][next][first][last][top][bottom][index][help]  */
 255 {
 256     time_t t2 = *t;
 257 
 258     t2 -= TimeDiff (t2);
 259 
 260     return (gmtime (&t2));
 261 }
 262 
 263 #define TIME_FIXUP_CONSTANT (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60))
 264 
 265 /****************************************************************************
 266 interpret an 8 byte "filetime" structure to a time_t
 267 It's originally in "100ns units since jan 1st 1601"
 268 
 269 It appears to be kludge-GMT (at least for file listings). This means
 270 its the GMT you get by taking a localtime and adding the
 271 serverzone. This is NOT the same as GMT in some cases. This routine
 272 converts this to real GMT.
 273 ****************************************************************************/
 274 time_t
 275 nt_time_to_unix (NTTIME * nt)
     /* [previous][next][first][last][top][bottom][index][help]  */
 276 {
 277     double d;
 278     time_t ret;
 279     /* The next two lines are a fix needed for the 
 280        broken SCO compiler. JRA. */
 281     time_t l_time_min = TIME_T_MIN;
 282     time_t l_time_max = TIME_T_MAX;
 283 
 284     if (nt->high == 0)
 285         return (0);
 286 
 287     d = ((double) nt->high) * 4.0 * (double) (1 << 30);
 288     d += (nt->low & 0xFFF00000);
 289     d *= 1.0e-7;
 290 
 291     /* now adjust by 369 years to make the secs since 1970 */
 292     d -= TIME_FIXUP_CONSTANT;
 293 
 294     if (!(l_time_min <= d && d <= l_time_max))
 295         return (0);
 296 
 297     ret = (time_t) (d + 0.5);
 298 
 299     /* this takes us from kludge-GMT to real GMT */
 300     ret -= serverzone;
 301     ret += LocTimeDiff (ret);
 302 
 303     return (ret);
 304 }
 305 
 306 
 307 
 308 /****************************************************************************
 309 interprets an nt time into a unix time_t
 310 ****************************************************************************/
 311 time_t
 312 interpret_long_date (char *p)
     /* [previous][next][first][last][top][bottom][index][help]  */
 313 {
 314     NTTIME nt;
 315     nt.low = IVAL (p, 0);
 316     nt.high = IVAL (p, 4);
 317     return nt_time_to_unix (&nt);
 318 }
 319 
 320 /****************************************************************************
 321 put a 8 byte filetime from a time_t
 322 This takes real GMT as input and converts to kludge-GMT
 323 ****************************************************************************/
 324 void
 325 unix_to_nt_time (NTTIME * nt, time_t t)
     /* [previous][next][first][last][top][bottom][index][help]  */
 326 {
 327     double d;
 328 
 329     if (t == 0)
 330     {
 331         nt->low = 0;
 332         nt->high = 0;
 333         return;
 334     }
 335 
 336     /* this converts GMT to kludge-GMT */
 337     t -= LocTimeDiff (t) - serverzone;
 338 
 339     d = (double) (t);
 340     d += TIME_FIXUP_CONSTANT;
 341     d *= 1.0e7;
 342 
 343     nt->high = (uint32) (d * (1.0 / (4.0 * (double) (1 << 30))));
 344     nt->low = (uint32) (d - ((double) nt->high) * 4.0 * (double) (1 << 30));
 345 }
 346 
 347 
 348 /****************************************************************************
 349 take an NTTIME structure, containing high / low time.  convert to unix time.
 350 lkclXXXX this may need 2 SIVALs not a memcpy.  we'll see...
 351 ****************************************************************************/
 352 void
 353 put_long_date (char *p, time_t t)
     /* [previous][next][first][last][top][bottom][index][help]  */
 354 {
 355     NTTIME nt;
 356     unix_to_nt_time (&nt, t);
 357     SIVAL (p, 0, nt.low);
 358     SIVAL (p, 4, nt.high);
 359 }
 360 
 361 /****************************************************************************
 362 check if it's a null mtime
 363 ****************************************************************************/
 364 BOOL
 365 null_mtime (time_t mtime)
     /* [previous][next][first][last][top][bottom][index][help]  */
 366 {
 367     if (mtime == (time_t) 0 || mtime == (time_t) 0xFFFFFFFF || mtime == (time_t) - 1)
 368         return (True);
 369     return (False);
 370 }
 371 
 372 /*******************************************************************
 373   create a 16 bit dos packed date
 374 ********************************************************************/
 375 static uint16
 376 make_dos_date1 (struct tm *t)
     /* [previous][next][first][last][top][bottom][index][help]  */
 377 {
 378     uint16 ret = 0;
 379     ret = (((unsigned) (t->tm_mon + 1)) >> 3) | ((t->tm_year - 80) << 1);
 380     ret = ((ret & 0xFF) << 8) | (t->tm_mday | (((t->tm_mon + 1) & 0x7) << 5));
 381     return (ret);
 382 }
 383 
 384 /*******************************************************************
 385   create a 16 bit dos packed time
 386 ********************************************************************/
 387 static uint16
 388 make_dos_time1 (struct tm *t)
     /* [previous][next][first][last][top][bottom][index][help]  */
 389 {
 390     uint16 ret = 0;
 391     ret = ((((unsigned) t->tm_min >> 3) & 0x7) | (((unsigned) t->tm_hour) << 3));
 392     ret = ((ret & 0xFF) << 8) | ((t->tm_sec / 2) | ((t->tm_min & 0x7) << 5));
 393     return (ret);
 394 }
 395 
 396 /*******************************************************************
 397   create a 32 bit dos packed date/time from some parameters
 398   This takes a GMT time and returns a packed localtime structure
 399 ********************************************************************/
 400 static uint32
 401 make_dos_date (time_t unixdate)
     /* [previous][next][first][last][top][bottom][index][help]  */
 402 {
 403     struct tm *t;
 404     uint32 ret = 0;
 405 
 406     t = LocalTime (&unixdate);
 407     if (!t)
 408         return 0xFFFFFFFF;
 409 
 410     ret = make_dos_date1 (t);
 411     ret = ((ret & 0xFFFF) << 16) | make_dos_time1 (t);
 412 
 413     return (ret);
 414 }
 415 
 416 /*******************************************************************
 417 put a dos date into a buffer (time/date format)
 418 This takes GMT time and puts local time in the buffer
 419 ********************************************************************/
 420 void
 421 put_dos_date (char *buf, int offset, time_t unixdate)
     /* [previous][next][first][last][top][bottom][index][help]  */
 422 {
 423     uint32 x = make_dos_date (unixdate);
 424     SIVAL (buf, offset, x);
 425 }
 426 
 427 #if 0
 428 /*******************************************************************
 429 put a dos date into a buffer (date/time format)
 430 This takes GMT time and puts local time in the buffer
 431 ********************************************************************/
 432 void
 433 put_dos_date2 (char *buf, int offset, time_t unixdate)
     /* [previous][next][first][last][top][bottom][index][help]  */
 434 {
 435     uint32 x = make_dos_date (unixdate);
 436     x = ((x & 0xFFFF) << 16) | ((x & 0xFFFF0000) >> 16);
 437     SIVAL (buf, offset, x);
 438 }
 439 #endif /* 0 */
 440 
 441 /*******************************************************************
 442 put a dos 32 bit "unix like" date into a buffer. This routine takes
 443 GMT and converts it to LOCAL time before putting it (most SMBs assume
 444 localtime for this sort of date)
 445 ********************************************************************/
 446 void
 447 put_dos_date3 (char *buf, int offset, time_t unixdate)
     /* [previous][next][first][last][top][bottom][index][help]  */
 448 {
 449     if (!null_mtime (unixdate))
 450         unixdate -= TimeDiff (unixdate);
 451     SIVAL (buf, offset, unixdate);
 452 }
 453 
 454 /*******************************************************************
 455   interpret a 32 bit dos packed date/time to some parameters
 456 ********************************************************************/
 457 static void
 458 interpret_dos_date (uint32 date, int *year, int *month, int *day, int *hour, int *minute,
     /* [previous][next][first][last][top][bottom][index][help]  */
 459                     int *second)
 460 {
 461     uint32 p0, p1, p2, p3;
 462 
 463     p0 = date & 0xFF;
 464     p1 = ((date & 0xFF00) >> 8) & 0xFF;
 465     p2 = ((date & 0xFF0000) >> 16) & 0xFF;
 466     p3 = ((date & 0xFF000000) >> 24) & 0xFF;
 467 
 468     *second = 2 * (p0 & 0x1F);
 469     *minute = ((p0 >> 5) & 0xFF) + ((p1 & 0x7) << 3);
 470     *hour = (p1 >> 3) & 0xFF;
 471     *day = (p2 & 0x1F);
 472     *month = ((p2 >> 5) & 0xFF) + ((p3 & 0x1) << 3) - 1;
 473     *year = ((p3 >> 1) & 0xFF) + 80;
 474 }
 475 
 476 /*******************************************************************
 477   create a unix date (int GMT) from a dos date (which is actually in
 478   localtime)
 479 ********************************************************************/
 480 time_t
 481 make_unix_date (void *date_ptr)
     /* [previous][next][first][last][top][bottom][index][help]  */
 482 {
 483     uint32 dos_date = 0;
 484     struct tm t;
 485     time_t ret;
 486 
 487     dos_date = IVAL (date_ptr, 0);
 488 
 489     if (dos_date == 0)
 490         return (0);
 491 
 492     interpret_dos_date (dos_date, &t.tm_year, &t.tm_mon,
 493                         &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec);
 494     t.tm_isdst = -1;
 495 
 496     /* mktime() also does the local to GMT time conversion for us */
 497     ret = mktime (&t);
 498 
 499     return (ret);
 500 }
 501 
 502 /*******************************************************************
 503 like make_unix_date() but the words are reversed
 504 ********************************************************************/
 505 time_t
 506 make_unix_date2 (void *date_ptr)
     /* [previous][next][first][last][top][bottom][index][help]  */
 507 {
 508     uint32 x, x2;
 509 
 510     x = IVAL (date_ptr, 0);
 511     x2 = ((x & 0xFFFF) << 16) | ((x & 0xFFFF0000) >> 16);
 512     SIVAL (&x, 0, x2);
 513 
 514     return (make_unix_date ((void *) &x));
 515 }
 516 
 517 /*******************************************************************
 518   create a unix GMT date from a dos date in 32 bit "unix like" format
 519   these generally arrive as localtimes, with corresponding DST
 520   ******************************************************************/
 521 time_t
 522 make_unix_date3 (void *date_ptr)
     /* [previous][next][first][last][top][bottom][index][help]  */
 523 {
 524     time_t t = (time_t) IVAL (date_ptr, 0);
 525     if (!null_mtime (t))
 526         t += LocTimeDiff (t);
 527     return (t);
 528 }
 529 
 530 #if 0
 531 /***************************************************************************
 532 return a HTTP/1.0 time string
 533   ***************************************************************************/
 534 char *
 535 http_timestring (time_t t)
     /* [previous][next][first][last][top][bottom][index][help]  */
 536 {
 537     static fstring buf;
 538     struct tm *tm = LocalTime (&t);
 539 
 540     if (!tm)
 541         slprintf (buf, sizeof (buf) - 1, "%ld seconds since the Epoch", (long) t);
 542     else
 543         strftime (buf, sizeof (buf) - 1, "%a, %d %b %Y %H:%M:%S %Z", tm);
 544     return buf;
 545 }
 546 #endif /*0 */
 547 
 548 
 549 /****************************************************************************
 550   return the date and time as a string
 551 ****************************************************************************/
 552 char *
 553 timestring (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 554 {
 555     static fstring TimeBuf;
 556     time_t t = time (NULL);
 557     struct tm *tm = LocalTime (&t);
 558 
 559     if (!tm)
 560     {
 561         slprintf (TimeBuf, sizeof (TimeBuf) - 1, "%ld seconds since the Epoch", (long) t);
 562     }
 563     else
 564     {
 565         strftime (TimeBuf, 100, "%Y/%m/%d %H:%M:%S", tm);
 566     }
 567     return (TimeBuf);
 568 }
 569 
 570 /****************************************************************************
 571   return the best approximation to a 'create time' under UNIX from a stat
 572   structure.
 573 ****************************************************************************/
 574 
 575 time_t
 576 get_create_time (SMB_STRUCT_STAT * st, BOOL fake_dirs)
     /* [previous][next][first][last][top][bottom][index][help]  */
 577 {
 578     time_t ret, ret1;
 579 
 580     if (S_ISDIR (st->st_mode) && fake_dirs)
 581         return (time_t) 315493200L;     /* 1/1/1980 */
 582 
 583     ret = MIN (st->st_ctime, st->st_mtime);
 584     ret1 = MIN (ret, st->st_atime);
 585 
 586     if (ret1 != (time_t) 0)
 587         return ret1;
 588 
 589     /*
 590      * One of ctime, mtime or atime was zero (probably atime).
 591      * Just return MIN(ctime, mtime).
 592      */
 593     return ret;
 594 }

/* [previous][next][first][last][top][bottom][index][help]  */