1 /*
2  *  DATE.C
3  *
4  *  Written on 30-Jul-90 by jim nutt.  Changes on 10-Jul-94 by John Dennis.
5  *  Released to the public domain.
6  *
7  *  Parse various string date formats into a UNIX style timestamp.
8  */
9 
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <ctype.h>
14 #include <time.h>
15 #include "addr.h"
16 #include "nedit.h"
17 #include "msged.h"
18 #include "memextra.h"
19 #include "strextra.h"
20 #include "date.h"
21 #include "mctype.h"
22 
23 static char *month[] =
24 {
25     "Jan", "Feb", "Mar", "Apr", "May", "Jun",
26     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
27 };
28 
29 static char *day[] =
30 {
31     "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
32 };
33 
34 static char *attr_tokens[] =
35 {
36     "yms",  /* year of message creation */
37     "yno",  /* current year */
38     "mms",  /* month of message creation */
39     "mno",  /* current month */
40     "dms",  /* day of message creation */
41     "dno",  /* current day */
42     "wms",  /* weekday of message creation */
43     "wno",  /* current weekday */
44     "tnm",  /* (normal) time of message creation */
45     "tnn",  /* (normal) current time */
46     "tam",  /* (atime) time of message creation */
47     "tan",  /* (atime) current time */
48 
49     "ofn",  /* orginal from name */
50     "off",  /* original from first name */
51     "otn",  /* original to name */
52     "otf",  /* original to first name */
53     "osu",  /* original subject */
54     "ooa",  /* original originination address */
55     "oda",  /* orginal destination address */
56 
57     "fna",  /* from name */
58     "ffn",  /* from first name */
59     "fad",  /* from address */
60     "tna",  /* to name */
61     "tfn",  /* to first name */
62     "tad",  /* to address */
63     "sub",  /* subject */
64 
65     "una",  /* user name */
66     "ufn",  /* user first name */
67     "uad",  /* user address */
68     "ceh",  /* current conference name */
69     "oeh",  /* original conference name */
70 
71     "ims",  /* (iso) date of message creation */
72     "ino",  /* (iso) current date */
73     "cms",  /* four-digit year of message creation */
74     "cno",  /* current four-digit year */
75 
76     NULL
77 };
78 
79 #define ATTR_TOK_YMS  0
80 #define ATTR_TOK_YMO  1
81 #define ATTR_TOK_MMS  2
82 #define ATTR_TOK_MNO  3
83 #define ATTR_TOK_DMS  4
84 #define ATTR_TOK_DNO  5
85 #define ATTR_TOK_WMS  6
86 #define ATTR_TOK_WNO  7
87 #define ATTR_TOK_TNM  8
88 #define ATTR_TOK_TNN  9
89 #define ATTR_TOK_TAM  10
90 #define ATTR_TOK_TAN  11
91 #define ATTR_TOK_OFN  12
92 #define ATTR_TOK_OFF  13
93 #define ATTR_TOK_OTN  14
94 #define ATTR_TOK_OTF  15
95 #define ATTR_TOK_OSU  16
96 #define ATTR_TOK_OOA  17
97 #define ATTR_TOK_ODA  18
98 #define ATTR_TOK_FNA  19
99 #define ATTR_TOK_FFN  20
100 #define ATTR_TOK_FAD  21
101 #define ATTR_TOK_TNA  22
102 #define ATTR_TOK_TFN  23
103 #define ATTR_TOK_TAD  24
104 #define ATTR_TOK_SUB  25
105 #define ATTR_TOK_UNA  26
106 #define ATTR_TOK_UFN  27
107 #define ATTR_TOK_UAD  28
108 #define ATTR_TOK_CEH  29
109 #define ATTR_TOK_OEH  30
110 #define ATTR_TOK_IMS  31
111 #define ATTR_TOK_INO  32
112 #define ATTR_TOK_CMS  33
113 #define ATTR_TOK_CNO  34
114 
valid_date(struct tm * tms)115 static int valid_date(struct tm *tms)
116 {
117     return !(tms->tm_wday > 6 || tms->tm_wday < 0 || tms->tm_mon > 11 ||
118       tms->tm_mon < 0 || tms->tm_mday > 31 || tms->tm_mday < 0 ||
119       tms->tm_hour > 23 || tms->tm_hour < 0 || tms->tm_min > 59 ||
120       tms->tm_min < 0 || tms->tm_sec > 59 || tms->tm_sec < 0);
121 }
122 
parsedate(char * ds)123 time_t parsedate(char *ds)
124 {
125     int t, absnow, absyear;
126     struct tm tm, *now;
127     char work[80], *s;
128     time_t n;
129 
130     if (ds == NULL || *ds == '\0')
131     {
132         return 0;
133     }
134 
135     memset(&tm, 0, sizeof tm);
136     strcpy(work, ds);
137 
138     if (strchr(ds, '-') != NULL)
139     {
140         /* quickbbs style date */
141 
142         s = strtok(work, "-");
143         if (s != NULL)
144         {
145             tm.tm_mon = atoi(s) - 1;
146         }
147         s = strtok(NULL, "-");
148         if (s != NULL)
149         {
150             tm.tm_mday = atoi(s);
151         }
152         s = strtok(NULL, " ");
153         if (s != NULL)
154         {
155             tm.tm_year = atoi(s);
156         }
157         s = strtok(NULL, ":");
158         if (s != NULL)
159         {
160             while (m_isspace(*s))
161             {
162                 s++;
163             }
164             tm.tm_hour = atoi(s);
165         }
166         s = strtok(NULL, " ");
167         if (s != NULL)
168         {
169             tm.tm_min = atoi(s);
170         }
171         tm.tm_sec = 0;
172     }
173     else
174     {
175         /* fido style date */
176 
177         s = strtok(work, " ");
178 
179         if (s == NULL)
180         {
181             return 0;
182         }
183 
184         t = atoi(s);
185         if (t == 0)
186         {
187             /* a usenet date */
188             s = strtok(NULL, " ");
189             if (s == NULL)
190             {
191                 return 0;
192             }
193             t = atoi(s);
194         }
195         tm.tm_mday = t;
196         s = strtok(NULL, " ");
197         if (s == NULL)
198         {
199             return 0;
200         }
201         for (t = 0; t < 12; t++)
202         {
203             if (stricmp(s, month[t]) == 0)
204             {
205                 break;
206             }
207         }
208         if (t == 12)
209         {
210             t = 1;
211         }
212         tm.tm_mon = t;
213         s = strtok(NULL, " ");
214         if (s == NULL)
215         {
216             return 0;
217         }
218         tm.tm_year = atoi(s);
219         s = strtok(NULL, ":");
220         if (s == NULL)
221         {
222             return 0;
223         }
224         while (m_isspace(*s))
225         {
226             s++;
227         }
228         tm.tm_hour = atoi(s);
229         s = strtok(NULL, ": \0");
230         if (s == NULL)
231         {
232             return 0;
233         }
234         tm.tm_min = atoi(s);
235         s = strtok(NULL, " ");
236         if (s != NULL)
237         {
238             tm.tm_sec = atoi(s);
239         }
240         tm.tm_isdst = -1;
241     }
242 
243     /* Now try to find out which century we're in and fix the year */
244 
245     n = time(NULL);
246     now = localtime(&n);
247 
248     absnow  = 1900 + now->tm_year;
249     absyear = 1900 + tm.tm_year;
250     while (absyear <= absnow-50)
251       absyear += 100;
252     while (absyear > absnow+50)
253       absyear -= 100;
254 
255     tm.tm_year = absyear - 1900;
256 
257     return mktime(&tm);
258 }
259 
itime(time_t now)260 char *itime(time_t now)
261 {
262     struct tm *tm;
263     static char tmp[40];
264 
265     tm = localtime(&now);
266 
267     if (!tm || !valid_date(tm))
268     {
269         sprintf(tmp, "%04d-%02d-%02d %02d:%02d:%02d", 1970, 1, 1, 0, 0, 0);
270     }
271     else
272     {
273         sprintf(tmp, "%04d-%02d-%02d %02d:%02d:%02d", tm->tm_year + 1900,
274           tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
275     }
276 
277     return tmp;
278 }
279 
atime(time_t now)280 char *atime(time_t now)
281 {
282     struct tm *tm;
283     static char tmp[40];
284 
285     tm = localtime(&now);
286 
287     if (!tm || !valid_date(tm))
288     {
289         sprintf(tmp, "%s %s %02d %04d %02d:%02d:%02d", day[4], month[0],
290           1, 1970, 0, 0, 0);
291     }
292     else
293     {
294         sprintf(tmp, "%s %s %02d %04d %02d:%02d:%02d", day[tm->tm_wday],
295           month[tm->tm_mon], tm->tm_mday, tm->tm_year + 1900, tm->tm_hour,
296           tm->tm_min, tm->tm_sec);
297     }
298 
299     return tmp;
300 }
301 
mtime(time_t now)302 char *mtime(time_t now)
303 {
304     struct tm *tm;
305     static char tmp[21];
306 
307     tm = localtime(&now);
308 
309     if (!tm || !valid_date(tm))
310     {
311         sprintf(tmp, "%02d %s %02d  %02d:%02d:%02d", 1, month[0], 70, 0, 0, 0);
312     }
313     else
314     {
315         sprintf(tmp, "%02d %s %02d  %02d:%02d:%02d", tm->tm_mday,
316           month[tm->tm_mon], tm->tm_year % 100, tm->tm_hour, tm->tm_min,
317           tm->tm_sec);
318     }
319 
320     return tmp;
321 }
322 
qtime(time_t now)323 char *qtime(time_t now)
324 {
325     struct tm *tm;
326     static char tmp[20];
327 
328     tm = localtime(&now);
329 
330     if (!tm || !valid_date(tm))
331     {
332         sprintf(tmp, "%s %02d %02d:%02d", month[0], 1, 0, 0);
333     }
334     else
335     {
336         sprintf(tmp, "%s %02d %02d:%02d", month[tm->tm_mon], tm->tm_mday,
337           tm->tm_hour, tm->tm_min);
338     }
339 
340     return tmp;
341 }
342 
343 /* find_token - returns the token number or -1 if not found */
344 
find_token(char * token)345 static int find_token(char *token)
346 {
347     int i;
348     i = 0;
349     while (attr_tokens[i] != NULL)
350     {
351         if (stricmp(attr_tokens[i], token) == 0)
352         {
353             return i;
354         }
355         i++;
356     }
357     return -1;
358 }
359 
360 /* Returns a pointer to the first name. Note: uses static memory. */
361 
firstname(char * name)362 char *firstname(char *name)
363 {
364     char *s;
365     static char work[40];
366 
367     memset(work, 0, sizeof work);
368     if (name == NULL)
369     {
370 	return work;
371     }
372     s = strchr(name, ' ');
373     if (s == NULL)
374     {
375         sprintf(work, "%-.39s", name);
376     }
377     else
378     {
379         *s = '\0';
380         sprintf(work, "%-.39s", name);
381         *s = ' ';
382     }
383     return work;
384 }
385 
386 /* Returns a pointer to the last name. Note: uses static memory. */
387 
lastname(char * name)388 char *lastname(char *name)
389 {
390     char *s;
391     static char work[40];
392 
393     memset(work, 0, sizeof work);
394     if (name == NULL)
395     {
396 	return work;
397     }
398 
399     s = strchr(name, ' ');
400     if (s == NULL)
401     {
402         sprintf(work, "%-.39s", name);
403     }
404     else
405     {
406         sprintf(work, "%-.39s", s + 1);
407     }
408     return work;
409 }
410 
411 
412 /* attrib_line - builds an attribution line */
413 
attrib_line(msg * m,msg * old,int olda,char * format,char ** days,char ** months)414 char *attrib_line(msg * m, msg * old, int olda, char *format,
415                   char **days, char **months)
416 {
417     struct tm now, *tm;
418     char work[256], token[5], *t;
419     time_t n;
420     int num;
421 
422     if (days == NULL) days = day;
423     if (months == NULL) months = month;
424 
425     if (format == NULL)
426     {
427         return NULL;
428     }
429 
430     memset(work, 0, sizeof work);
431     t = work;
432     n = time(NULL);
433     tm = localtime(&n);
434     now = *tm;
435 
436     if (old)
437     {
438         tm = localtime(&(old->timestamp));
439     }
440 
441     while (*format)
442     {
443         if (*format == '%')
444         {
445             format++;
446             switch (*format)
447             {
448             case '%':
449                 *t = *format;
450                 break;
451 
452             case '_':
453                 *t = ' ';
454                 break;
455 
456             default:
457                 memset(token, 0, sizeof token);
458                 strncpy(token, format, 3);
459                 num = find_token(token);
460 
461                 switch (num)
462                 {
463                 case ATTR_TOK_YMS:
464                     if (old)
465                     {
466                         sprintf(t, "%02d", tm->tm_year % 100);
467                     }
468                     break;
469 
470                 case ATTR_TOK_YMO:
471                     sprintf(t, "%02d", now.tm_year % 100);
472                     break;
473 
474                 case ATTR_TOK_MMS:
475                     if (old)
476                     {
477                         strcpy(t, months[tm->tm_mon]);
478                     }
479                     break;
480 
481                 case ATTR_TOK_MNO:
482                     strcpy(t, months[now.tm_mon]);
483                     break;
484 
485                 case ATTR_TOK_DMS:
486                     if (old)
487                     {
488                         sprintf(t, "%02d", tm->tm_mday);
489                     }
490                     break;
491 
492                 case ATTR_TOK_DNO:
493                     sprintf(t, "%02d", now.tm_mday);
494                     break;
495 
496                 case ATTR_TOK_WMS:
497                     if (old)
498                     {
499                         strcpy(t, days[tm->tm_wday]);
500                     }
501                     break;
502 
503                 case ATTR_TOK_WNO:
504                     strcpy(t, days[now.tm_wday]);
505                     break;
506 
507                 case ATTR_TOK_TNM:
508                     if (old)
509                     {
510                         sprintf(t, "%02d:%02d", tm->tm_hour, tm->tm_min);
511                     }
512                     break;
513 
514                 case ATTR_TOK_TNN:
515                     sprintf(t, "%02d:%02d", now.tm_hour, now.tm_min);
516                     break;
517 
518                 case ATTR_TOK_TAM:
519                     if (old)
520                     {
521                         strcpy(t, atime(old->timestamp));
522                     }
523                     break;
524 
525                 case ATTR_TOK_TAN:
526                     strcpy(t, atime(n));
527                     break;
528 
529                 case ATTR_TOK_OFN:
530                     if (old && old->isfrom)
531                     {
532                         strcpy(t, old->isfrom);
533                     }
534                     break;
535 
536                 case ATTR_TOK_OFF:
537                     if (old && old->isfrom)
538                     {
539                         strcpy(t, firstname(old->isfrom));
540                     }
541                     break;
542 
543                 case ATTR_TOK_OTN:
544                     if (old && old->isto)
545                     {
546                         strcpy(t, old->isto);
547                     }
548                     break;
549 
550                 case ATTR_TOK_OTF:
551                     if (old && old->isto)
552                     {
553                         strcpy(t, firstname(old->isto));
554                     }
555                     break;
556 
557                 case ATTR_TOK_OSU:
558                     if (old && old->subj)
559                     {
560                         strcpy(t, old->subj);
561                     }
562                     break;
563 
564                 case ATTR_TOK_OOA:
565                     if (old)
566                     {
567                         strcpy(t, show_address(&old->from));
568                     }
569                     break;
570 
571                 case ATTR_TOK_ODA:
572                     if (old)
573                     {
574                         strcpy(t, show_address(&old->to));
575                     }
576                     break;
577 
578                 case ATTR_TOK_FNA:
579                     if (m->isfrom)
580                     {
581                         strcpy(t, m->isfrom);
582                     }
583                     break;
584 
585                 case ATTR_TOK_FFN:
586                     if (m->isfrom)
587                     {
588                         strcpy(t, firstname(m->isfrom));
589                     }
590                     break;
591 
592                 case ATTR_TOK_FAD:
593                     strcpy(t, show_address(&m->from));
594                     break;
595 
596                 case ATTR_TOK_TNA:
597                     if (m->isto)
598                     {
599                         strcpy(t, m->isto);
600                     }
601                     break;
602 
603                 case ATTR_TOK_TFN:
604                     if (m->isto)
605                     {
606                         strcpy(t, firstname(m->isto));
607                     }
608                     break;
609 
610                 case ATTR_TOK_TAD:
611                     strcpy(t, show_address(&m->to));
612                     break;
613 
614                 case ATTR_TOK_SUB:
615                     strcpy(t, m->subj);
616                     break;
617 
618                 case ATTR_TOK_UNA:
619                     if (ST->username)
620                     {
621                         strcpy(t, ST->username);
622                     }
623                     break;
624 
625                 case ATTR_TOK_UFN:
626                     if (ST->username)
627                     {
628                         strcpy(t, firstname(ST->username));
629                     }
630                     break;
631 
632                 case ATTR_TOK_UAD:
633                     strcpy(t, show_address(&CurArea.addr));
634                     break;
635 
636                 case ATTR_TOK_CEH:
637                     strcpy(t, CurArea.tag);
638                     break;
639 
640                 case ATTR_TOK_OEH:
641                     if (olda != -1)
642                     {
643                         strcpy(t, arealist[olda].tag);
644                     }
645                     break;
646 
647                 case ATTR_TOK_IMS:
648                     if (old != NULL)
649                     {
650                         sprintf(t, "%04d-%02d-%02d", tm->tm_year + 1900,
651                           tm->tm_mon + 1, tm->tm_mday);
652                     }
653                     break;
654 
655                 case ATTR_TOK_INO:
656                     sprintf(t, "%04d-%02d-%02d", now.tm_year + 1900,
657                       now.tm_mon + 1, now.tm_mday);
658                     break;
659 
660                 case ATTR_TOK_CMS:
661                     if (old != NULL)
662                     {
663                         sprintf(t, "%04d", tm->tm_year + 1900);
664                     }
665                     break;
666 
667                 case ATTR_TOK_CNO:
668                     sprintf(t, "%04d", now.tm_year + 1900);
669                     break;
670 
671                 default:
672                     break;
673                 }
674                 break;
675             }
676             t = work + strlen(work);
677             format += 3;
678         }
679         else
680         {
681             *t++ = *format++;
682         }
683     }
684     return xstrdup(work);
685 }
686