2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
2N/A/* All Rights Reserved */
2N/A
2N/A
2N/A/*
2N/A * Copyright (c) 1989, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <stdio.h>
2N/A#include <string.h>
2N/A#include <ctype.h>
2N/A#include <sys/types.h>
2N/A#include <stdlib.h>
2N/A#include <limits.h>
2N/A#include "libadm.h"
2N/A
2N/Astatic int fmtcheck(char *);
2N/A
2N/A#define MSGSIZ 64
2N/A#define PROMPT "Enter the date"
2N/A#define MESG "Please enter a date"
2N/A#define DEFAULT "%m/%d/%y"
2N/A
2N/Astatic char *p_ndigit(char *, int *, int);
2N/Astatic char *p_date(char *, int, int, int);
2N/Astatic char *p_eday(char *, int, int);
2N/Astatic char *p_dlm(char *, char);
2N/A
2N/A#define MLIM 10
2N/A#define STDIG 2
2N/A#define LD2 10
2N/A#define LD 01
2N/A#define UD 31
2N/A#define LM 01
2N/A#define UM 12
2N/A/*
2N/A * All digits are valid for a YY year format
2N/A * 70-99 refer to the 20th Century
2N/A * 00-69 refer to the 21st Century
2N/A */
2N/A#define LY 00
2N/A#define UY 99
2N/A#define LCY 1970
2N/A#define UCY 9999
2N/A#define CCYY 4
2N/A#define DELIM1 '/'
2N/A#define DELIM2 '-'
2N/A#define BLANK ' '
2N/A#define TAB ' '
2N/A
2N/Astatic void
2N/Asetmsg(char *msg, size_t msgsize, char *fmt)
2N/A{
2N/A if ((fmt == NULL) || strcmp(fmt, "%D") == 0)
2N/A fmt = "%m/%d/%y";
2N/A (void) snprintf(msg, msgsize, "%s. Format is <%s>.", MESG, fmt);
2N/A}
2N/A
2N/Astatic char *
2N/Ap_ndigit(char *string, int *value, int n)
2N/A{
2N/A char *ptr;
2N/A int accum = 0;
2N/A
2N/A if (!string)
2N/A return (NULL);
2N/A for (ptr = string; *ptr && n > 0; n--, ptr++) {
2N/A if (! isdigit((unsigned char)*ptr))
2N/A return (NULL);
2N/A accum = (10 * accum) + (*ptr - '0');
2N/A }
2N/A if (n)
2N/A return (NULL);
2N/A *value = accum;
2N/A return (ptr);
2N/A}
2N/A
2N/Astatic char *
2N/Ap_date(char *string, int llim, int ulim, int ndig)
2N/A{
2N/A char *ptr;
2N/A int begin = -1;
2N/A
2N/A if (!(ptr = p_ndigit(string, &begin, ndig)))
2N/A return (NULL);
2N/A if (begin >= llim && begin <= ulim)
2N/A return (ptr);
2N/A else
2N/A return (NULL);
2N/A}
2N/A
2N/Astatic char *
2N/Ap_eday(char *string, int llim, int ulim)
2N/A{
2N/A char *ptr, *copy;
2N/A char daynum[3];
2N/A int begin = -1;
2N/A int iday = 0;
2N/A int idaymax = 2;
2N/A
2N/A daynum[0] = '\0';
2N/A if (*string == BLANK) {
2N/A string++;
2N/A idaymax--;
2N/A }
2N/A copy = string;
2N/A while (isdigit((unsigned char)*copy) && (iday < idaymax)) {
2N/A daynum[iday] = *copy++;
2N/A iday++;
2N/A }
2N/A daynum[iday] = '\0';
2N/A if (iday == 1) {
2N/A llim = 1;
2N/A ulim = 9;
2N/A } else if (iday == 2) {
2N/A llim = 10;
2N/A ulim = 31;
2N/A }
2N/A if (iday == 0)
2N/A return (NULL);
2N/A
2N/A if (!(ptr = p_ndigit(string, &begin, iday)))
2N/A return (NULL);
2N/A
2N/A if (begin >= llim && begin <= ulim)
2N/A return (ptr);
2N/A else
2N/A return (NULL);
2N/A}
2N/A
2N/A/* p_month will parse the string for the month - abbr. form i.e. JAN - DEC */
2N/A
2N/Astatic char *
2N/Ap_month(char *string, char mnabr)
2N/A{
2N/A static char *fmonth[] = {
2N/A "JANUARY", "FEBRUARY", "MARCH", "APRIL",
2N/A "MAY", "JUNE", "JULY", "AUGUST",
2N/A "SEPTEMBER", "OCTOBER", "NOVEMBER", "DECEMBER"
2N/A };
2N/A static char *amonth[] = {
2N/A "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
2N/A "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"
2N/A };
2N/A int ichng, icnt;
2N/A char *mnth[12];
2N/A char *copy;
2N/A char mletter[MLIM];
2N/A int mlen;
2N/A int imnth = 0;
2N/A int legit = 0;
2N/A int n = 0;
2N/A
2N/A if (mnabr == 'a') {
2N/A mlen = 3;
2N/A for (icnt = 0; icnt < 12; icnt++)
2N/A mnth[icnt] = amonth[icnt];
2N/A } else {
2N/A mlen = 9;
2N/A for (icnt = 0; icnt < 12; icnt++)
2N/A mnth[icnt] = fmonth[icnt];
2N/A }
2N/A
2N/A copy = string;
2N/A
2N/A while (((islower((unsigned char)*copy)) ||
2N/A (isupper((unsigned char)*copy))) && (imnth < mlen)) {
2N/A mletter[imnth] = toupper((unsigned char)*copy++);
2N/A imnth++;
2N/A }
2N/A mletter[imnth] = '\0';
2N/A while (!(legit) && (n < 12)) {
2N/A if (strncmp(mletter, mnth[n],
2N/A (imnth = (int)strlen(mnth[n]))) == 0)
2N/A legit = 1; /* found legitimate string */
2N/A n++;
2N/A }
2N/A if (legit) {
2N/A for (ichng = 0; ichng < imnth; ichng++) {
2N/A *string = toupper((unsigned char)*string);
2N/A string++;
2N/A }
2N/A
2N/A return (string);
2N/A /*
2N/A * I know this causes side effects, but it's less
2N/A * code than adding in a copy for string and using that
2N/A */
2N/A } else
2N/A return (NULL);
2N/A}
2N/A
2N/Astatic char *
2N/Ap_dlm(char *string, char dchoice)
2N/A{
2N/A char dlm;
2N/A
2N/A
2N/A if (! string)
2N/A return (NULL);
2N/A (void) sscanf(string, "%1c", &dlm);
2N/A if (dchoice == '/')
2N/A return (((dlm == DELIM1) || (dlm == DELIM2)) ? string+1 : NULL);
2N/A else
2N/A return ((dlm == dchoice) ? string + 1 : NULL);
2N/A}
2N/A
2N/Aint
2N/Ackdate_err(char *fmt, char *error)
2N/A{
2N/A char defmesg[MSGSIZ];
2N/A
2N/A if ((fmt != NULL) && (fmtcheck(fmt) == 1))
2N/A return (4);
2N/A setmsg(defmesg, sizeof (defmesg), fmt);
2N/A puterror(stdout, defmesg, error);
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Ackdate_hlp(char *fmt, char *help)
2N/A{
2N/A char defmesg[MSGSIZ];
2N/A
2N/A if ((fmt != NULL) && (fmtcheck(fmt) == 1))
2N/A return (4);
2N/A setmsg(defmesg, sizeof (defmesg), fmt);
2N/A puthelp(stdout, defmesg, help);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * A little state machine that checks out the format to
2N/A * make sure it is acceptable.
2N/A * return value 1: NG
2N/A * return value 0: OK
2N/A */
2N/Astatic int
2N/Afmtcheck(char *fmt)
2N/A{
2N/A int percent = 0;
2N/A
2N/A while (*fmt) {
2N/A switch (*fmt++) {
2N/A case '%': /* previous state must be start or letter */
2N/A if (percent == 0)
2N/A percent = 1;
2N/A else
2N/A return (1);
2N/A break;
2N/A case 'd': /* previous state must be "%" */
2N/A case 'e':
2N/A case 'm':
2N/A case 'y':
2N/A case 'Y':
2N/A case 'D':
2N/A case 'h':
2N/A case 'b':
2N/A case 'B':
2N/A if (percent == 1)
2N/A percent = 0;
2N/A else
2N/A return (1);
2N/A break;
2N/A case TAB: /* previous state must be start or letter */
2N/A case BLANK:
2N/A case DELIM1:
2N/A case DELIM2:
2N/A if (percent == 1)
2N/A return (1);
2N/A break;
2N/A default:
2N/A return (1);
2N/A }
2N/A }
2N/A return (percent);
2N/A}
2N/A
2N/Aint
2N/Ackdate_val(char *fmt, char *input)
2N/A{
2N/A char ltrl, dfl;
2N/A int valid = 1; /* time of day string is valid for format */
2N/A
2N/A if ((fmt != NULL) && (fmtcheck(fmt) == 1))
2N/A return (4);
2N/A
2N/A if (fmt == NULL)
2N/A fmt = DEFAULT;
2N/A ltrl = '\0';
2N/A while (*fmt && valid) {
2N/A if ((*fmt) == '%') {
2N/A fmt++;
2N/A switch (*fmt) {
2N/A case 'd':
2N/A input = p_date(input, LD, UD, STDIG);
2N/A if (!input)
2N/A valid = 0;
2N/A break;
2N/A
2N/A case 'e':
2N/A input = p_eday(input, LD2, UD);
2N/A if (!input)
2N/A valid = 0;
2N/A break;
2N/A
2N/A case 'm':
2N/A input = p_date(input, LM, UM, STDIG);
2N/A if (!input)
2N/A valid = 0;
2N/A break;
2N/A
2N/A case 'y':
2N/A input = p_date(input, LY, UY, STDIG);
2N/A if (!input)
2N/A valid = 0;
2N/A break;
2N/A
2N/A case 'Y':
2N/A input = p_date(input, LCY, UCY, CCYY);
2N/A if (!input)
2N/A valid = 0;
2N/A break;
2N/A
2N/A case 'D':
2N/A input = p_date(input, LM, UM, STDIG);
2N/A if (!input) {
2N/A valid = 0;
2N/A break;
2N/A }
2N/A input = p_dlm(input, DELIM1);
2N/A if (!input) {
2N/A valid = 0;
2N/A break;
2N/A }
2N/A input = p_date(input, LD, UD, STDIG);
2N/A if (!input) {
2N/A valid = 0;
2N/A break;
2N/A }
2N/A input = p_dlm(input, DELIM1);
2N/A if (!input) {
2N/A valid = 0;
2N/A break;
2N/A }
2N/A input = p_date(input, LY, UY, STDIG);
2N/A if (!input)
2N/A valid = 0;
2N/A break;
2N/A
2N/A case 'h':
2N/A case 'b':
2N/A input = p_month(input, 'a');
2N/A if (!input)
2N/A valid = 0;
2N/A break;
2N/A
2N/A case 'B':
2N/A input = p_month(input, 'f');
2N/A if (!input)
2N/A valid = 0;
2N/A break;
2N/A
2N/A default:
2N/A (void) sscanf(input, "%1c", &ltrl);
2N/A input++;
2N/A }
2N/A } else {
2N/A dfl = '\0';
2N/A (void) sscanf(input, "%1c", &dfl);
2N/A input++;
2N/A }
2N/A fmt++;
2N/A } /* end of while fmt and valid */
2N/A
2N/A if ((*fmt == NULL) && ((input != NULL) && *input != 0)) {
2N/A if (*input != NULL)
2N/A valid = 0;
2N/A }
2N/A return ((valid == 0));
2N/A}
2N/A
2N/Aint
2N/Ackdate(char *date, char *fmt, char *defstr, char *error, char *help,
2N/A char *prompt)
2N/A{
2N/A char defmesg[MSGSIZ];
2N/A char input[MAX_INPUT];
2N/A char *ept, end[128];
2N/A
2N/A ept = end;
2N/A *ept = '\0';
2N/A
2N/A if ((fmt != NULL) && (fmtcheck(fmt) == 1))
2N/A return (4);
2N/A
2N/A setmsg(defmesg, sizeof (defmesg), fmt);
2N/A (void) sprintf(ept, "[?,q]");
2N/A
2N/A if (!prompt)
2N/A prompt = PROMPT;
2N/A
2N/Astart:
2N/A putprmpt(stderr, prompt, NULL, defstr);
2N/A if (getinput(input))
2N/A return (1);
2N/A
2N/A if (!strlen(input)) {
2N/A if (defstr) {
2N/A (void) strcpy(date, defstr);
2N/A return (0);
2N/A }
2N/A puterror(stderr, defmesg, error);
2N/A goto start;
2N/A } else if (strcmp(input, "?") == 0) {
2N/A puthelp(stderr, defmesg, help);
2N/A goto start;
2N/A } else if (ckquit && strcmp(input, "q") == 0) {
2N/A return (3);
2N/A } else if (ckdate_val(fmt, input)) {
2N/A puterror(stderr, defmesg, error);
2N/A goto start;
2N/A }
2N/A (void) strcpy(date, input);
2N/A return (0);
2N/A}