xref: /illumos-gate/usr/src/lib/libadm/common/ckdate.c (revision 410cfc1c29e3c504b79366ddfd3584e9e69a33d5)
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)
75 {
76 	if ((fmt == NULL) || strcmp(fmt, "%D") == 0)
77 		fmt = "%m/%d/%y";
78 	(void) sprintf(msg, "%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 	char daynum[3];
119 	int begin = -1;
120 	int iday = 0;
121 	int idaymax = 2;
122 
123 	daynum[0] = '\0';
124 	if (*string == BLANK) {
125 		string++;
126 		idaymax--;
127 	}
128 	copy = string;
129 	while (isdigit((unsigned char)*copy) && (iday < idaymax)) {
130 		daynum[iday] = *copy++;
131 		iday++;
132 	}
133 	daynum[iday] = '\0';
134 	if (iday == 1) {
135 		llim = 1;
136 		ulim = 9;
137 	} else if (iday == 2) {
138 		llim = 10;
139 		ulim = 31;
140 	}
141 	if (iday == 0)
142 		return (NULL);
143 
144 	if (!(ptr = p_ndigit(string, &begin, iday)))
145 		return (NULL);
146 
147 	if (begin >= llim && begin <= ulim)
148 		return (ptr);
149 	else
150 		return (NULL);
151 }
152 
153 /* p_month will parse the string for the month - abbr. form i.e. JAN - DEC */
154 
155 static char *
156 p_month(char *string, char mnabr)
157 {
158 	static char *fmonth[] = {
159 		    "JANUARY", "FEBRUARY", "MARCH", "APRIL",
160 		    "MAY", "JUNE", "JULY", "AUGUST",
161 		    "SEPTEMBER", "OCTOBER", "NOVEMBER", "DECEMBER"
162 	};
163 	static char *amonth[] = {
164 		    "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
165 		    "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"
166 	};
167 	int ichng, icnt;
168 	char *mnth[12];
169 	char *copy;
170 	char mletter[MLIM];
171 	int mlen;
172 	int imnth = 0;
173 	int legit = 0;
174 	int n = 0;
175 
176 	if (mnabr == 'a') {
177 		mlen = 3;
178 		for (icnt = 0; icnt < 12; icnt++)
179 			mnth[icnt] = amonth[icnt];
180 	} else {
181 		mlen = 9;
182 		for (icnt = 0; icnt < 12; icnt++)
183 			mnth[icnt] = fmonth[icnt];
184 	}
185 
186 	copy = string;
187 
188 	while (((islower((unsigned char)*copy)) ||
189 	    (isupper((unsigned char)*copy))) && (imnth < mlen)) {
190 		mletter[imnth] = toupper((unsigned char)*copy++);
191 		imnth++;
192 	}
193 	mletter[imnth] = '\0';
194 	while (!(legit) && (n < 12)) {
195 		if (strncmp(mletter, mnth[n],
196 		    (imnth = (int)strlen(mnth[n]))) == 0)
197 			legit = 1;	/* found legitimate string */
198 		n++;
199 	}
200 	if (legit) {
201 		for (ichng = 0; ichng < imnth; ichng++) {
202 			*string = toupper((unsigned char)*string);
203 			string++;
204 		}
205 
206 		return (string);
207 		/*
208 		 * I know this causes side effects, but it's less
209 		 * code  than adding in a copy for string and using that
210 		 */
211 	} else
212 		return (NULL);
213 }
214 
215 static char *
216 p_dlm(char *string, char dchoice)
217 {
218 	char dlm;
219 
220 
221 	if (! string)
222 		return (NULL);
223 	(void) sscanf(string, "%1c", &dlm);
224 	if (dchoice == '/')
225 		return (((dlm == DELIM1) || (dlm == DELIM2)) ? string+1 : NULL);
226 	else
227 		return ((dlm == dchoice) ? string + 1 : NULL);
228 }
229 
230 int
231 ckdate_err(char	*fmt, char *error)
232 {
233 	char	defmesg[MSGSIZ];
234 
235 	if ((fmt != NULL) && (fmtcheck(fmt) == 1))
236 		return (4);
237 	setmsg(defmesg, fmt);
238 	puterror(stdout, defmesg, error);
239 	return (0);
240 }
241 
242 int
243 ckdate_hlp(char *fmt, char *help)
244 {
245 	char	defmesg[MSGSIZ];
246 
247 	if ((fmt != NULL) && (fmtcheck(fmt) == 1))
248 		return (4);
249 	setmsg(defmesg, fmt);
250 	puthelp(stdout, defmesg, help);
251 	return (0);
252 }
253 
254 /*
255  *	A little state machine that checks out the format to
256  *	make sure it is acceptable.
257  *		return value 1: NG
258  *		return value 0: OK
259  */
260 static int
261 fmtcheck(char *fmt)
262 {
263 	int	percent = 0;
264 
265 	while (*fmt) {
266 		switch (*fmt++) {
267 			case '%': /* previous state must be start or letter */
268 				if (percent == 0)
269 					percent = 1;
270 				else
271 					return (1);
272 				break;
273 			case 'd': /* previous state must be "%" */
274 			case 'e':
275 			case 'm':
276 			case 'y':
277 			case 'Y':
278 			case 'D':
279 			case 'h':
280 			case 'b':
281 			case 'B':
282 				if (percent == 1)
283 					percent = 0;
284 				else
285 					return (1);
286 				break;
287 			case TAB: /* previous state must be start or letter */
288 			case BLANK:
289 			case DELIM1:
290 			case DELIM2:
291 				if (percent == 1)
292 					return (1);
293 				break;
294 			default:
295 				return (1);
296 		}
297 	}
298 	return (percent);
299 }
300 
301 int
302 ckdate_val(char *fmt, char *input)
303 {
304 	char ltrl, dfl;
305 	int valid = 1; 	/* time of day string is valid for format */
306 
307 	if ((fmt != NULL) && (fmtcheck(fmt) == 1))
308 		return (4);
309 
310 	if (fmt == NULL)
311 		fmt = DEFAULT;
312 	ltrl = '\0';
313 	while (*fmt && valid) {
314 		if ((*fmt) == '%') {
315 			fmt++;
316 			switch (*fmt) {
317 				case 'd':
318 					input = p_date(input, LD, UD, STDIG);
319 					if (!input)
320 						valid = 0;
321 					break;
322 
323 				case 'e':
324 					input = p_eday(input, LD2, UD);
325 					if (!input)
326 						valid = 0;
327 					break;
328 
329 				case 'm':
330 					input = p_date(input, LM, UM, STDIG);
331 					if (!input)
332 						valid = 0;
333 					break;
334 
335 				case 'y':
336 					input = p_date(input, LY, UY, STDIG);
337 					if (!input)
338 						valid = 0;
339 					break;
340 
341 				case 'Y':
342 					input = p_date(input, LCY, UCY, CCYY);
343 					if (!input)
344 						valid = 0;
345 					break;
346 
347 				case 'D':
348 					input = p_date(input, LM, UM, STDIG);
349 					if (!input) {
350 						valid = 0;
351 					break;
352 				}
353 				input = p_dlm(input, DELIM1);
354 				if (!input) {
355 					valid = 0;
356 					break;
357 				}
358 				input = p_date(input, LD, UD, STDIG);
359 				if (!input) {
360 					valid = 0;
361 					break;
362 				}
363 				input = p_dlm(input, DELIM1);
364 				if (!input) {
365 					valid = 0;
366 					break;
367 				}
368 				input = p_date(input, LY, UY, STDIG);
369 				if (!input)
370 					valid = 0;
371 				break;
372 
373 				case 'h':
374 				case 'b':
375 					input = p_month(input, 'a');
376 					if (!input)
377 						valid = 0;
378 					break;
379 
380 				case 'B':
381 					input = p_month(input, 'f');
382 					if (!input)
383 						valid = 0;
384 					break;
385 
386 				default:
387 					(void) sscanf(input, "%1c", &ltrl);
388 					input++;
389 			}
390 		} else {
391 			dfl = '\0';
392 			(void) sscanf(input, "%1c", &dfl);
393 			input++;
394 		}
395 		fmt++;
396 	}	 /* end of while fmt and valid */
397 
398 	if ((*fmt == NULL) && ((input != NULL) && *input != 0)) {
399 		if (*input != NULL)
400 			valid = 0;
401 	}
402 	return ((valid == 0));
403 }
404 
405 int
406 ckdate(char *date, char *fmt, char *defstr, char *error, char *help,
407     char *prompt)
408 {
409 	char	defmesg[MSGSIZ];
410 	char	input[MAX_INPUT];
411 	char	*ept, end[128];
412 
413 	ept = end;
414 	*ept = '\0';
415 
416 	if ((fmt != NULL) && (fmtcheck(fmt) == 1))
417 		return (4);
418 
419 	setmsg(defmesg, fmt);
420 	(void) sprintf(ept, "[?,q]");
421 
422 	if (!prompt)
423 		prompt = PROMPT;
424 
425 start:
426 	putprmpt(stderr, prompt, NULL, defstr);
427 	if (getinput(input))
428 		return (1);
429 
430 	if (!strlen(input)) {
431 		if (defstr) {
432 			(void) strcpy(date, defstr);
433 			return (0);
434 		}
435 		puterror(stderr, defmesg, error);
436 		goto start;
437 	} else if (strcmp(input, "?") == 0) {
438 		puthelp(stderr, defmesg, help);
439 		goto start;
440 	} else if (ckquit && strcmp(input, "q") == 0) {
441 		return (3);
442 	} else if (ckdate_val(fmt, input)) {
443 		puterror(stderr, defmesg, error);
444 		goto start;
445 	}
446 	(void) strcpy(date, input);
447 	return (0);
448 }
449