xref: /illumos-gate/usr/src/lib/libadm/common/ckdate.c (revision d2f7972d81337947df76c36b8c2a5f290829fa7a)
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
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 *
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 *
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 *
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 *
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 *
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
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
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
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
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", &ltrl);
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
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