File: sccsdate.cc

package info (click to toggle)
cssc 1.0.1-4
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k, lenny
  • size: 3,620 kB
  • ctags: 1,424
  • sloc: cpp: 13,500; sh: 4,759; ansic: 2,971; perl: 342; makefile: 342; awk: 11
file content (491 lines) | stat: -rw-r--r-- 11,728 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
/*
 * sccsdate.cc: Part of GNU CSSC.
 * 
 * 
 *    Copyright (C) 1997, Free Software Foundation, Inc. 
 * 
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 * 
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 * 
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
 * 
 * CSSC was originally Based on MySC, by Ross Ridge, which was 
 * placed in the Public Domain.
 *
 *
 * Members of the class sccs_date.
 *
 */

#ifdef __GNUC__
#pragma implementation "sccsdate.h"
#endif

#include "cssc.h"
#include "sccsdate.h"

#include <time.h>

#include <ctype.h>

#ifdef CONFIG_SCCS_IDS
static const char rcs_id[] = "CSSC $Id: sccsdate.cc,v 1.16 2002/04/04 19:32:05 james_youngman Exp $";
#endif

// The MySC code used to just check for (year % 4) and (year == 0).
// This implementation is "right", but in any case it won't make a
// difference until 2100AD.
static int
is_leapyear(int year)
{
  if (year % 4)
    {
      return 0;                 // not a leapyear.
    }
  else 
    {
      if (year % 100)
        {
          return 1;             // non-century year
        }
      else
        {
          if (year % 400)
            return 0;           // century years are not leap-years exceot
          else
            return 1;           // every fourth one, which IS a leap year.
        }
    }
}

static int
days_in_month(int mon, int year)
{
  switch(mon)
    {
    case 1:
    case 3:
    case 5:
    case 7:
    case 8:
    case 10:
    case 12:
      return 31;
      
    case 4:
    case 6:
    case 9:
    case 11:
      return 30;
      
    case 2:
      if (is_leapyear(year))
        return 29;
      else
        return 28;
    }
  return -1;
}

static inline int
is_digit(char ch)
{
  return isdigit( (unsigned char) ch );
}

static inline int 
get_digit(char ch)
{
  ASSERT(isdigit( (unsigned char) ch ));
  return ch - '0';
}

static int
get_two_digits(const char *s)
{
  return get_digit(s[0]) * 10 + get_digit(s[1]);  
}

static int
get_part(const char *&s, int def)
{
  char c = *s;
  while (c)
    {
      if (is_digit(c))
        {
          s++;
          if (is_digit(*s))
            {
              return get_digit(c) * 10 + get_digit(*s++);
            }
          else
            {
              return get_digit(c);
            }
        }
      c = *++s;
    }
  return def;
}

static int 
count_digits(const char *s)
{
  int count;
  
  for(count=0; *s; s++)
    {
      if (is_digit((unsigned char)*s))
        count++;
    }
  return count;
}

// Construct a date as specified on the command line.
sccs_date::sccs_date(const char *s)
{
  ASSERT(s != 0);
  /* if (s == 0) return; */

  // A fully-qualified YYmmDDhhMMss specification contains
  // twelve (12) digits.  If we have more than that, assume that
  // the first two digits are the century.  We have to make this
  // count before the first call to get_part(), since that
  // actually advances the pointer.
  const int n_digits = count_digits(s);
  
  // Get and check the year part.
  if ( (year = get_part(s, -1)) == -1)
    return;

#if 1
  // Here be Year-2000 code.
  //
  // This code checks if the year is 19 or 20 AND the next two
  // characters are ALSO digits.  If so, we assume that we've
  // just got the century.  This is an extension with respect to
  // "real" SCCS.
  //
  // To prevent this simply breaking down in the year 2019 (with
  // a two digit year part), we only activate this measure if we
  // have more than the regulation number of digits.
  //
  // The version of MySC which I inherited assumed the first 2
  // digits to be the century part if they were 19 or 20.  This
  // approach breaks down rather unexpectedly for the user in
  // the year 2019.
  // 
  if (n_digits > 12)
    {
      // If we actually have a 4-digit year, the next two
      // characters must be digits (we have already consumed the
      // first two).
      if (isdigit((unsigned char)s[0]) &&
          isdigit((unsigned char)s[1]))
        {
          const int century_field_val = year;
          year =  (century_field_val * 100) + get_two_digits(&s[0]);
          s += 2;               // this consumes exactly two characters.
        }
    }
  else
    {
      // In the X/Open Commands and Utilities Issue 5 standard,
      // it is specified that yy/mm/dd type dates with values
      // for "yy" which range from 69-99 are to be interpreted
      // as 1969-1999 and dates with year values 00-68 are to be
      // interpreted as 2000-2068.
      //
      // For more information about this, please see that
      // document itself and also the X/Open Year-2000 FAQ,
      // which can be obtained from the URL
      // http://www.xopen.org/public/tech/base/year2000.html
      //
      if (year < 69)
        year += 2000;
      else
        year += 1900;
    }
#endif
                        
  month     = get_part(s, 12);
  month_day = get_part(s, days_in_month(month, year));
  hour      = get_part(s, 23);
  minute    = get_part(s, 59);
  second    = get_part(s, 59);
  
  update_yearday();
}

// Construct a date as specified in an SCCS file.
sccs_date::sccs_date(const char *date, const char *time)
{
  char buf[11];
  int century;
  
  // Peter Kjellerstedt writes:-
  // 
  // This is a gross hack to handle that some old implementation of SCCS
  // has a Y2K bug that results in that dates are written incorrectly as
  // :0/01/01 instead of 00/01/01 (the colon is of course the next
  // character after '9' in the ASCII table). The following should handle
  // this correctly for years up to 2069 (after which the time format
  // used is not valid anyway).
  strncpy(buf, date, 11);

  /* Check for the symtoms of SourceForge bug ID 513800, where 
   * the Data General version of Unix puts a four-digit year
   * into the p-file. 
   */
  if (strlen(buf) > 4
      && is_digit(buf[0])
      && is_digit(buf[1])
      && is_digit(buf[2])
      && is_digit(buf[3])
      && ('/' == buf[4]))
  {
      warning("this file has been written by a version of SCCS"
	      " which uses four-digit years, which is contrary to the"
	      " common SCCS file format (though it might have been a "
	      " good idea in the first place)\n");
      century = get_two_digits(&buf[0]);
      date = buf + 2;
  }
  else
  {
      // this is a normal two-digit date.
      century = 0;              // decide by windowing.
      date = buf;
  }
  
  
  if (buf[0] >= ':' && buf[0] <= '@')
    {
      warning("date in SCCS file contains character '%c': "
	      "a version of SCCS which is not Year 2000 compliant "
	      "has probably been used on this file.\n",
	      buf[0]);
      
      buf[0] -= 10;
    }
  
  // The "1" in the if() is just there to make Emacs align the columns.
  if (1
      && is_digit(date[0]) && is_digit(date[1]) && date[2] == '/'
      && is_digit(date[3]) && is_digit(date[4]) && date[5] == '/'
      && is_digit(date[6]) && is_digit(date[7]) && date[8] == 0

      && is_digit(time[0]) && is_digit(time[1]) && time[2] == ':'
      && is_digit(time[3]) && is_digit(time[4]) && time[5] == ':'
      && is_digit(time[6]) && is_digit(time[7]) && time[8] == '\0')
    {
      year      = get_two_digits(&date[0]);
      month     = get_two_digits(&date[3]);
      month_day = get_two_digits(&date[6]);
      
      hour      = get_two_digits(&time[0]);
      minute    = get_two_digits(&time[3]);
      second    = get_two_digits(&time[6]);
      
      // Year 2000 fix (mandated by X/Open white paper, see above
      // for more details).
      if (century)
      {
          // SourceForge bug ID 513800 - Data General Unix uses 4-digit year
          // in the p-file.
          year = century * 100 + year;
      }
      else
      {
          
          if (year < 69)
              year += 2000;
          else
              year += 1900;
      }
      
      update_yearday();

      ASSERT(year >= 1969);
      ASSERT(year <  2069);
    }
}

int
sccs_date::printf(FILE *f, char fmt) const
{
  const int yy = year % 100;
  
  switch(fmt)
    {
    case 'D':
      return printf_failed(fprintf(f, "%02d/%02d/%02d",
                                   yy, month, month_day));
    case 'H':
      return printf_failed(fprintf(f, "%02d/%02d/%02d",
                                   month, month_day, yy));

    case 'T':
      return printf_failed(fprintf(f, "%02d:%02d:%02d",
                                   hour, minute, second));
    }

  int value = 0;
  switch (fmt)
    {
    case 'y':
      value = yy;
      break;

    case 'o':
      value = month;
      break;
                
    case 'd':
      value = month_day;
      break;

    case 'h':
      value = hour;
      break;

    case 'm':
      value = minute;
      break;
      
    case 's':
      value = second;
      break;

    default:
      ASSERT(!"sccs_date::printf: Invalid format");
      // This code IS reached, when ASSERT() expands to nothing.
      // TODO: throw exception here
    }
  return printf_failed(fprintf(f, "%02d", value));
}

int
sccs_date::print(FILE *f) const
{
  return this->printf(f, 'D')
    || putc_failed(putc(' ', f))
    || this->printf(f, 'T');
}


mystring
sccs_date::as_string() const
{
  char buf[18];
  const int yy = year % 100;
  
  sprintf(buf, "%02d/%02d/%02d %02d:%02d:%02d",
          yy, month, month_day,
          hour, minute, second);
  
  return mystring(buf);
}

sccs_date::sccs_date(int yr, int mth, int day,
                     int hr, int min, int sec)
  : year(yr), month(mth), month_day(day),
    hour(hr), minute(min), second(sec)
{
  update_yearday();
}

sccs_date::sccs_date()
  : year(-1), month(-1), month_day(-1),
    hour(-1), minute(-1), second(-1)
{
}


sccs_date
sccs_date::now()                // static member.
{
  time_t tt;
  time(&tt);                    // TODO: throw exception if this fails.
  struct tm *ptm = localtime(&tt);

  return sccs_date(ptm->tm_year+1900, ptm->tm_mon+1, ptm->tm_mday,
                   ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
}

void
sccs_date::update_yearday()
{
  int m=1, d=1;

  yearday = 1;
  
  while (m < month)
    yearday += days_in_month(m++, year);

  while (d++ < month_day)
    yearday++;

  daysecond = ((hour * 60) + minute) * 60 + second;
}


int sccs_date::compare(const sccs_date& d) const
{
  int diff;
  
  if ( (diff = year - d.year) != 0 )
    return diff;
  else if ( (diff = yearday - d.yearday) != 0)
    return diff;
  else
    return daysecond - d.daysecond;
}



int sccs_date::operator >(sccs_date const & d) const
{
  return compare(d) > 0;
}

int sccs_date::operator <(sccs_date const &d) const
{
  return compare(d) < 0;
}

int sccs_date::operator <=(sccs_date const &d) const
{
  return compare(d) <= 0;
}


int
sccs_date::valid() const
{
  // Allow the seconds field to get as high as 61, since that is what
  // the ANSI C spec for struct tm says, and we have to use a struct
  // tm with localtime().
  return year >= 0
    && month > 0 && month < 13
    && month_day > 0 && month_day <= days_in_month(month, year)
    && hour >= 0 && hour < 24
    && minute >= 0 && minute < 60
    && second >= 0 && second <= 61;
    
}


/* Local variables: */
/* mode: c++ */
/* End: */