1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
22 /* All Rights Reserved */
23
24
25 /*
26 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
27 */
28
29 /*LINTLIBRARY*/
30
31 #include <stdio.h>
32 #include <string.h>
33 #include <ctype.h>
34 #include <sys/types.h>
35 #include <stdlib.h>
36 #include <limits.h>
37 #include "libadm.h"
38
39 static int fmtcheck(char *);
40
41 #define MSGSIZ 64
42 #define PROMPT "Enter the date"
43 #define MESG "Please enter a date"
44 #define DEFAULT "%m/%d/%y"
45
46 static char *p_ndigit(char *, int *, int);
47 static char *p_date(char *, int, int, int);
48 static char *p_eday(char *, int, int);
49 static char *p_dlm(char *, char);
50
51 #define MLIM 10
52 #define STDIG 2
53 #define LD2 10
54 #define LD 01
55 #define UD 31
56 #define LM 01
57 #define UM 12
58 /*
59 * All digits are valid for a YY year format
60 * 70-99 refer to the 20th Century
61 * 00-69 refer to the 21st Century
62 */
63 #define LY 00
64 #define UY 99
65 #define LCY 1970
66 #define UCY 9999
67 #define CCYY 4
68 #define DELIM1 '/'
69 #define DELIM2 '-'
70 #define BLANK ' '
71 #define TAB ' '
72
73 static void
setmsg(char * msg,char * fmt,size_t sz)74 setmsg(char *msg, char *fmt, size_t sz)
75 {
76 if ((fmt == NULL) || strcmp(fmt, "%D") == 0)
77 fmt = "%m/%d/%y";
78 (void) snprintf(msg, sz, "%s. Format is <%s>.", MESG, fmt);
79 }
80
81 static char *
p_ndigit(char * string,int * value,int n)82 p_ndigit(char *string, int *value, int n)
83 {
84 char *ptr;
85 int accum = 0;
86
87 if (!string)
88 return (NULL);
89 for (ptr = string; *ptr && n > 0; n--, ptr++) {
90 if (! isdigit((unsigned char)*ptr))
91 return (NULL);
92 accum = (10 * accum) + (*ptr - '0');
93 }
94 if (n)
95 return (NULL);
96 *value = accum;
97 return (ptr);
98 }
99
100 static char *
p_date(char * string,int llim,int ulim,int ndig)101 p_date(char *string, int llim, int ulim, int ndig)
102 {
103 char *ptr;
104 int begin = -1;
105
106 if (!(ptr = p_ndigit(string, &begin, ndig)))
107 return (NULL);
108 if (begin >= llim && begin <= ulim)
109 return (ptr);
110 else
111 return (NULL);
112 }
113
114 static char *
p_eday(char * string,int llim,int ulim)115 p_eday(char *string, int llim, int ulim)
116 {
117 char *ptr, *copy;
118 int begin = -1;
119 int iday = 0;
120 int idaymax = 2;
121
122 if (*string == BLANK) {
123 string++;
124 idaymax--;
125 }
126 copy = string;
127 while (isdigit((unsigned char)*copy) && (iday < idaymax)) {
128 copy++;
129 iday++;
130 }
131 if (iday == 1) {
132 llim = 1;
133 ulim = 9;
134 } else if (iday == 2) {
135 llim = 10;
136 ulim = 31;
137 }
138 if (iday == 0)
139 return (NULL);
140
141 if (!(ptr = p_ndigit(string, &begin, iday)))
142 return (NULL);
143
144 if (begin >= llim && begin <= ulim)
145 return (ptr);
146 else
147 return (NULL);
148 }
149
150 /* p_month will parse the string for the month - abbr. form i.e. JAN - DEC */
151
152 static char *
p_month(char * string,char mnabr)153 p_month(char *string, char mnabr)
154 {
155 static char *fmonth[] = {
156 "JANUARY", "FEBRUARY", "MARCH", "APRIL",
157 "MAY", "JUNE", "JULY", "AUGUST",
158 "SEPTEMBER", "OCTOBER", "NOVEMBER", "DECEMBER"
159 };
160 static char *amonth[] = {
161 "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
162 "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"
163 };
164 int ichng, icnt;
165 char *mnth[12];
166 char *copy;
167 char mletter[MLIM];
168 int mlen;
169 int imnth = 0;
170 int legit = 0;
171 int n = 0;
172
173 if (mnabr == 'a') {
174 mlen = 3;
175 for (icnt = 0; icnt < 12; icnt++)
176 mnth[icnt] = amonth[icnt];
177 } else {
178 mlen = 9;
179 for (icnt = 0; icnt < 12; icnt++)
180 mnth[icnt] = fmonth[icnt];
181 }
182
183 copy = string;
184
185 while (((islower((unsigned char)*copy)) ||
186 (isupper((unsigned char)*copy))) && (imnth < mlen)) {
187 mletter[imnth] = toupper((unsigned char)*copy++);
188 imnth++;
189 }
190 mletter[imnth] = '\0';
191 while (!(legit) && (n < 12)) {
192 if (strncmp(mletter, mnth[n],
193 (imnth = (int)strlen(mnth[n]))) == 0)
194 legit = 1; /* found legitimate string */
195 n++;
196 }
197 if (legit) {
198 for (ichng = 0; ichng < imnth; ichng++) {
199 *string = toupper((unsigned char)*string);
200 string++;
201 }
202
203 return (string);
204 /*
205 * I know this causes side effects, but it's less
206 * code than adding in a copy for string and using that
207 */
208 } else
209 return (NULL);
210 }
211
212 static char *
p_dlm(char * string,char dchoice)213 p_dlm(char *string, char dchoice)
214 {
215 char dlm;
216
217
218 if (! string)
219 return (NULL);
220 (void) sscanf(string, "%1c", &dlm);
221 if (dchoice == '/')
222 return (((dlm == DELIM1) || (dlm == DELIM2)) ? string+1 : NULL);
223 else
224 return ((dlm == dchoice) ? string + 1 : NULL);
225 }
226
227 int
ckdate_err(char * fmt,char * error)228 ckdate_err(char *fmt, char *error)
229 {
230 char defmesg[MSGSIZ];
231
232 if ((fmt != NULL) && (fmtcheck(fmt) == 1))
233 return (4);
234 setmsg(defmesg, fmt, MSGSIZ);
235 puterror(stdout, defmesg, error);
236 return (0);
237 }
238
239 int
ckdate_hlp(char * fmt,char * help)240 ckdate_hlp(char *fmt, char *help)
241 {
242 char defmesg[MSGSIZ];
243
244 if ((fmt != NULL) && (fmtcheck(fmt) == 1))
245 return (4);
246 setmsg(defmesg, fmt, MSGSIZ);
247 puthelp(stdout, defmesg, help);
248 return (0);
249 }
250
251 /*
252 * A little state machine that checks out the format to
253 * make sure it is acceptable.
254 * return value 1: NG
255 * return value 0: OK
256 */
257 static int
fmtcheck(char * fmt)258 fmtcheck(char *fmt)
259 {
260 int percent = 0;
261
262 while (*fmt) {
263 switch (*fmt++) {
264 case '%': /* previous state must be start or letter */
265 if (percent == 0)
266 percent = 1;
267 else
268 return (1);
269 break;
270 case 'd': /* previous state must be "%" */
271 case 'e':
272 case 'm':
273 case 'y':
274 case 'Y':
275 case 'D':
276 case 'h':
277 case 'b':
278 case 'B':
279 if (percent == 1)
280 percent = 0;
281 else
282 return (1);
283 break;
284 case TAB: /* previous state must be start or letter */
285 case BLANK:
286 case DELIM1:
287 case DELIM2:
288 if (percent == 1)
289 return (1);
290 break;
291 default:
292 return (1);
293 }
294 }
295 return (percent);
296 }
297
298 int
ckdate_val(char * fmt,char * input)299 ckdate_val(char *fmt, char *input)
300 {
301 char ltrl, dfl;
302 int valid = 1; /* time of day string is valid for format */
303
304 if ((fmt != NULL) && (fmtcheck(fmt) == 1))
305 return (4);
306
307 if (fmt == NULL)
308 fmt = DEFAULT;
309 ltrl = '\0';
310 while (*fmt && valid) {
311 if ((*fmt) == '%') {
312 fmt++;
313 switch (*fmt) {
314 case 'd':
315 input = p_date(input, LD, UD, STDIG);
316 if (!input)
317 valid = 0;
318 break;
319
320 case 'e':
321 input = p_eday(input, LD2, UD);
322 if (!input)
323 valid = 0;
324 break;
325
326 case 'm':
327 input = p_date(input, LM, UM, STDIG);
328 if (!input)
329 valid = 0;
330 break;
331
332 case 'y':
333 input = p_date(input, LY, UY, STDIG);
334 if (!input)
335 valid = 0;
336 break;
337
338 case 'Y':
339 input = p_date(input, LCY, UCY, CCYY);
340 if (!input)
341 valid = 0;
342 break;
343
344 case 'D':
345 input = p_date(input, LM, UM, STDIG);
346 if (!input) {
347 valid = 0;
348 break;
349 }
350 input = p_dlm(input, DELIM1);
351 if (!input) {
352 valid = 0;
353 break;
354 }
355 input = p_date(input, LD, UD, STDIG);
356 if (!input) {
357 valid = 0;
358 break;
359 }
360 input = p_dlm(input, DELIM1);
361 if (!input) {
362 valid = 0;
363 break;
364 }
365 input = p_date(input, LY, UY, STDIG);
366 if (!input)
367 valid = 0;
368 break;
369
370 case 'h':
371 case 'b':
372 input = p_month(input, 'a');
373 if (!input)
374 valid = 0;
375 break;
376
377 case 'B':
378 input = p_month(input, 'f');
379 if (!input)
380 valid = 0;
381 break;
382
383 default:
384 (void) sscanf(input, "%1c", <rl);
385 input++;
386 }
387 } else {
388 dfl = '\0';
389 (void) sscanf(input, "%1c", &dfl);
390 input++;
391 }
392 fmt++;
393 } /* end of while fmt and valid */
394
395 if ((*fmt == '\0') && ((input != NULL) && *input != '\0')) {
396 valid = 0;
397 }
398 return ((valid == 0));
399 }
400
401 int
ckdate(char * date,char * fmt,char * defstr,char * error,char * help,char * prompt)402 ckdate(char *date, char *fmt, char *defstr, char *error, char *help,
403 char *prompt)
404 {
405 char defmesg[MSGSIZ];
406 char input[MAX_INPUT];
407 char *ept, end[128];
408
409 ept = end;
410 *ept = '\0';
411
412 if ((fmt != NULL) && (fmtcheck(fmt) == 1))
413 return (4);
414
415 setmsg(defmesg, fmt, MSGSIZ);
416 (void) sprintf(ept, "[?,q]");
417
418 if (!prompt)
419 prompt = PROMPT;
420
421 start:
422 putprmpt(stderr, prompt, NULL, defstr);
423 if (getinput(input))
424 return (1);
425
426 if (!strlen(input)) {
427 if (defstr) {
428 (void) strcpy(date, defstr);
429 return (0);
430 }
431 puterror(stderr, defmesg, error);
432 goto start;
433 } else if (strcmp(input, "?") == 0) {
434 puthelp(stderr, defmesg, help);
435 goto start;
436 } else if (ckquit && strcmp(input, "q") == 0) {
437 return (3);
438 } else if (ckdate_val(fmt, input)) {
439 puterror(stderr, defmesg, error);
440 goto start;
441 }
442 (void) strcpy(date, input);
443 return (0);
444 }
445