More stuff.
[oweals/busybox.git] / date.c
1 /*
2  * Mini date implementation for busybox
3  *
4  * Copyright (C) 1999 by Erik Andersen <andersee@debian.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20 */
21
22 #include "internal.h"
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <sys/time.h>
26 #include <unistd.h>
27 #include <time.h>
28 #include <stdio.h>
29
30
31 /* This 'date' command supports only 2 time setting formats, 
32    all the GNU strftime stuff (its in libc, lets use it),
33    setting time using UTC and displaying int, as well as
34    an RFC 822 complient date output for shell scripting
35    mail commands */
36
37 const char      date_usage[] = "date [OPTION]... [+FORMAT]\n"
38 "  or:  date [OPTION] [MMDDhhmm[[CC]YY][.ss]]\n"
39 "Display the current time in the given FORMAT, or set the system date.\n"
40 "\nOptions:\n\t-R\t\toutput RFC-822 compliant date string\n"
41 "\t-s\t\tset time described by STRING\n"
42 "\t-u\t\tprint or set Coordinated Universal Time\n";
43
44
45 /* Input parsing code is always bulky - used heavy duty libc stuff as
46    much as possible, missed out a lot of bounds checking */
47
48 /* Default input handling to save suprising some people */
49
50 struct tm *
51 date_conv_time(struct tm *tm_time, const char *t_string) {
52   int nr;
53
54   nr = sscanf(t_string, "%2d%2d%2d%2d%d", 
55              &(tm_time->tm_mon),
56              &(tm_time->tm_mday),
57              &(tm_time->tm_hour),
58              &(tm_time->tm_min),
59              &(tm_time->tm_year));
60
61   if(nr < 4 || nr > 5) {
62     fprintf(stderr, "date: invalid date `%s'\n", t_string);
63     exit( FALSE);
64   }
65
66   /* correct for century  - minor Y2K problem here? */
67   if(tm_time->tm_year >= 1900)
68     tm_time->tm_year -= 1900;
69   /* adjust date */
70   tm_time->tm_mon -= 1;
71              
72   return(tm_time);
73
74 }
75
76
77 /* The new stuff for LRP */
78
79 struct tm *
80 date_conv_ftime(struct tm *tm_time, const char *t_string) {
81   struct tm itm_time, jtm_time, ktm_time, \
82     ltm_time, mtm_time, ntm_time;
83   
84   itm_time = *tm_time;
85   jtm_time = *tm_time;
86   ktm_time = *tm_time;
87   ltm_time = *tm_time;
88   mtm_time = *tm_time;
89   ntm_time = *tm_time;
90
91   /* Parse input and assign appropriately to tm_time */
92   
93   if(sscanf(t_string, "%d:%d:%d",
94             &itm_time.tm_hour, 
95             &itm_time.tm_min, 
96             &itm_time.tm_sec) == 3 ) {
97
98     *tm_time = itm_time;
99     return(tm_time);
100
101   } else if (sscanf(t_string, "%d:%d", 
102                     &jtm_time.tm_hour, 
103                     &jtm_time.tm_min) == 2) {
104
105     *tm_time = jtm_time;
106     return(tm_time);
107
108   } else if (sscanf(t_string, "%d.%d-%d:%d:%d",
109                     &ktm_time.tm_mon, 
110                     &ktm_time.tm_mday, 
111                     &ktm_time.tm_hour,
112                     &ktm_time.tm_min,
113                     &ktm_time.tm_sec) == 5) {
114
115     ktm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */
116     *tm_time = ktm_time;
117     return(tm_time);
118
119   } else if (sscanf(t_string, "%d.%d-%d:%d",
120                     &ltm_time.tm_mon, 
121                     &ltm_time.tm_mday, 
122                     &ltm_time.tm_hour,
123                     &ltm_time.tm_min) == 4) {
124
125     ltm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */
126     *tm_time = ltm_time;
127     return(tm_time);
128
129   } else if (sscanf(t_string, "%d.%d.%d-%d:%d:%d",
130                     &mtm_time.tm_year,
131                     &mtm_time.tm_mon, 
132                     &mtm_time.tm_mday, 
133                     &mtm_time.tm_hour,
134                     &mtm_time.tm_min,
135                     &mtm_time.tm_sec) == 6) {
136
137     mtm_time.tm_year -= 1900; /* Adjust years */
138     mtm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */
139     *tm_time = mtm_time;
140     return(tm_time);
141
142   } else if (sscanf(t_string, "%d.%d.%d-%d:%d",
143                     &ntm_time.tm_year,
144                     &ntm_time.tm_mon, 
145                     &ntm_time.tm_mday, 
146                     &ntm_time.tm_hour,
147                     &ntm_time.tm_min) == 5) { 
148     ntm_time.tm_year -= 1900; /* Adjust years */
149     ntm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */
150     *tm_time = ntm_time;
151     return(tm_time);
152
153   }
154
155   fprintf(stderr, "date: invalid date `%s'\n", t_string);
156
157   exit( FALSE);
158
159 }
160
161
162 int
163 date_main(int argc, char * * argv)
164 {
165   char *date_str = NULL;
166   char *date_fmt = NULL;
167   char *t_buff;
168   int i;
169   int set_time = 0;
170   int rfc822 = 0;
171   int utc = 0;
172   int use_arg = 0;
173   time_t tm; 
174   struct tm tm_time;
175   
176   /* Interpret command line args */
177     i = --argc;
178     argv++;
179     while (i > 0 && **argv) {
180         if (**argv == '-') {
181             while (i>0 && *++(*argv)) switch (**argv) {
182             case 'R':
183                 rfc822 = 1;
184                 break;
185             case 's':
186                 set_time = 1;
187                 if(date_str != NULL) usage ( date_usage);
188                 date_str = optarg;
189                 break;
190             case 'u':
191                 utc = 1;
192                 if (putenv ("TZ=UTC0") != 0) {
193                     fprintf(stderr,"date: memory exhausted\n");
194                     exit( FALSE);
195                 }
196                 /* Look ma, no break.  Don't fix it either. */
197             case 'd':
198                 use_arg = 1;
199                 if(date_str != NULL) usage ( date_usage);
200                 date_str = optarg;
201                 break;
202             case '-':
203                 usage ( date_usage);
204             }
205         } else {
206             if ( (date_fmt == NULL) && (strcmp(*argv, "+")==0) )
207                 date_fmt=*argv;
208             else if (date_str == NULL) {
209                 set_time = 1;
210                 date_str=*argv;
211             } else { 
212                 usage ( date_usage);
213             }
214         }
215         i--;
216         argv++;
217     }
218
219
220   /* Now we have parsed all the information except the date format
221    which depends on whether the clock is being set or read */
222
223   time(&tm);
224   memcpy(&tm_time, localtime(&tm), sizeof(tm_time));
225   /* Zero out fields - take her back to midnight!*/
226   if(date_str != NULL) {
227     tm_time.tm_sec = 0;
228     tm_time.tm_min = 0;
229     tm_time.tm_hour = 0;
230   }
231
232   /* Process any date input to UNIX time since 1 Jan 1970 */
233   if(date_str != NULL) {
234
235     if(strchr(date_str, ':') != NULL) {
236       date_conv_ftime(&tm_time, date_str);
237     } else {
238       date_conv_time(&tm_time, date_str);
239     }
240
241     /* Correct any day of week and day of year etc fields */
242     tm = mktime(&tm_time);
243     if (tm < 0 ) {
244       fprintf(stderr, "date: invalid date `%s'\n", date_str);
245       exit( FALSE);
246     }
247
248     /* if setting time, set it */
249     if(set_time) {
250       if( stime(&tm) < 0) {
251         fprintf(stderr, "date: can't set date.\n");
252         exit( FALSE);
253       }
254     }
255   }
256   
257   /* Display output */
258
259   /* Deal with format string */
260   if(date_fmt == NULL) {
261     date_fmt = (rfc822
262                 ? (utc
263                    ? "%a, %_d %b %Y %H:%M:%S GMT"
264                    : "%a, %_d %b %Y %H:%M:%S %z")
265                 : "%a %b %e %H:%M:%S %Z %Y");
266
267   } else if ( *date_fmt == '\0' ) {
268     /* Imitate what GNU 'date' does with NO format string! */
269     printf ("\n");
270     exit( TRUE);
271   }
272
273   /* Handle special conversions */
274
275   if( strncmp( date_fmt, "%f", 2) == 0 ) {
276     date_fmt = "%Y.%m.%d-%H:%M:%S";
277   }
278
279   /* Print OUTPUT (after ALL that!) */
280   t_buff = malloc(201);
281   strftime(t_buff, 200, date_fmt, &tm_time);
282   printf("%s\n", t_buff);
283
284   exit( TRUE);
285
286 }
287