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