mirror of
				https://github.com/espressif/esp-idf.git
				synced 2025-10-30 20:51:41 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			576 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			576 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * SPDX-FileCopyrightText: 1998-2001,2003-2011,2013 Stewart Heitmann
 | |
|  *
 | |
|  * SPDX-License-Identifier: BSD-3-Clause
 | |
|  */
 | |
| /*******************************************************************************
 | |
|  * arg_date: Implements the date command-line option
 | |
|  *
 | |
|  * This file is part of the argtable3 library.
 | |
|  *
 | |
|  * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
 | |
|  * <sheitmann@users.sourceforge.net>
 | |
|  * All rights reserved.
 | |
|  *
 | |
|  * Redistribution and use in source and binary forms, with or without
 | |
|  * modification, are permitted provided that the following conditions are met:
 | |
|  *     * Redistributions of source code must retain the above copyright
 | |
|  *       notice, this list of conditions and the following disclaimer.
 | |
|  *     * Redistributions in binary form must reproduce the above copyright
 | |
|  *       notice, this list of conditions and the following disclaimer in the
 | |
|  *       documentation and/or other materials provided with the distribution.
 | |
|  *     * Neither the name of STEWART HEITMANN nor the  names of its contributors
 | |
|  *       may be used to endorse or promote products derived from this software
 | |
|  *       without specific prior written permission.
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | |
|  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | |
|  * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
 | |
|  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 | |
|  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 | |
|  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 | |
|  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | |
|  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | |
|  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | |
|  ******************************************************************************/
 | |
| 
 | |
| #include "argtable3.h"
 | |
| 
 | |
| #ifndef ARG_AMALGAMATION
 | |
| #include "argtable3_private.h"
 | |
| #endif
 | |
| 
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| 
 | |
| char* arg_strptime(const char* buf, const char* fmt, struct tm* tm);
 | |
| 
 | |
| static void arg_date_resetfn(struct arg_date* parent) {
 | |
|     ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
 | |
|     parent->count = 0;
 | |
| }
 | |
| 
 | |
| static int arg_date_scanfn(struct arg_date* parent, const char* argval) {
 | |
|     int errorcode = 0;
 | |
| 
 | |
|     if (parent->count == parent->hdr.maxcount) {
 | |
|         errorcode = ARG_ERR_MAXCOUNT;
 | |
|     } else if (!argval) {
 | |
|         /* no argument value was given, leave parent->tmval[] unaltered but still count it */
 | |
|         parent->count++;
 | |
|     } else {
 | |
|         const char* pend;
 | |
|         struct tm tm = parent->tmval[parent->count];
 | |
| 
 | |
|         /* parse the given argument value, store result in parent->tmval[] */
 | |
|         pend = arg_strptime(argval, parent->format, &tm);
 | |
|         if (pend && pend[0] == '\0')
 | |
|             parent->tmval[parent->count++] = tm;
 | |
|         else
 | |
|             errorcode = ARG_ERR_BADDATE;
 | |
|     }
 | |
| 
 | |
|     ARG_TRACE(("%s:scanfn(%p) returns %d\n", __FILE__, parent, errorcode));
 | |
|     return errorcode;
 | |
| }
 | |
| 
 | |
| static int arg_date_checkfn(struct arg_date* parent) {
 | |
|     int errorcode = (parent->count < parent->hdr.mincount) ? ARG_ERR_MINCOUNT : 0;
 | |
| 
 | |
|     ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode));
 | |
|     return errorcode;
 | |
| }
 | |
| 
 | |
| static void arg_date_errorfn(struct arg_date* parent, arg_dstr_t ds, int errorcode, const char* argval, const char* progname) {
 | |
|     const char* shortopts = parent->hdr.shortopts;
 | |
|     const char* longopts = parent->hdr.longopts;
 | |
|     const char* datatype = parent->hdr.datatype;
 | |
| 
 | |
|     /* make argval NULL safe */
 | |
|     argval = argval ? argval : "";
 | |
| 
 | |
|     arg_dstr_catf(ds, "%s: ", progname);
 | |
|     switch (errorcode) {
 | |
|         case ARG_ERR_MINCOUNT:
 | |
|             arg_dstr_cat(ds, "missing option ");
 | |
|             arg_print_option_ds(ds, shortopts, longopts, datatype, "\n");
 | |
|             break;
 | |
| 
 | |
|         case ARG_ERR_MAXCOUNT:
 | |
|             arg_dstr_cat(ds, "excess option ");
 | |
|             arg_print_option_ds(ds, shortopts, longopts, argval, "\n");
 | |
|             break;
 | |
| 
 | |
|         case ARG_ERR_BADDATE: {
 | |
|             struct tm tm;
 | |
|             char buff[200];
 | |
| 
 | |
|             arg_dstr_catf(ds, "illegal timestamp format \"%s\"\n", argval);
 | |
|             memset(&tm, 0, sizeof(tm));
 | |
|             arg_strptime("1999-12-31 23:59:59", "%F %H:%M:%S", &tm);
 | |
|             strftime(buff, sizeof(buff), parent->format, &tm);
 | |
|             arg_dstr_catf(ds, "correct format is \"%s\"\n", buff);
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| struct arg_date* arg_date0(const char* shortopts, const char* longopts, const char* format, const char* datatype, const char* glossary) {
 | |
|     return arg_daten(shortopts, longopts, format, datatype, 0, 1, glossary);
 | |
| }
 | |
| 
 | |
| struct arg_date* arg_date1(const char* shortopts, const char* longopts, const char* format, const char* datatype, const char* glossary) {
 | |
|     return arg_daten(shortopts, longopts, format, datatype, 1, 1, glossary);
 | |
| }
 | |
| 
 | |
| struct arg_date*
 | |
| arg_daten(const char* shortopts, const char* longopts, const char* format, const char* datatype, int mincount, int maxcount, const char* glossary) {
 | |
|     size_t nbytes;
 | |
|     struct arg_date* result;
 | |
| 
 | |
|     /* foolproof things by ensuring maxcount is not less than mincount */
 | |
|     maxcount = (maxcount < mincount) ? mincount : maxcount;
 | |
| 
 | |
|     /* default time format is the national date format for the locale */
 | |
|     if (!format)
 | |
|         format = "%x";
 | |
| 
 | |
|     nbytes = sizeof(struct arg_date)         /* storage for struct arg_date */
 | |
|              + (size_t)maxcount * sizeof(struct tm); /* storage for tmval[maxcount] array */
 | |
| 
 | |
|     /* allocate storage for the arg_date struct + tmval[] array.    */
 | |
|     /* we use calloc because we want the tmval[] array zero filled. */
 | |
|     result = (struct arg_date*)xcalloc(1, nbytes);
 | |
| 
 | |
|     /* init the arg_hdr struct */
 | |
|     result->hdr.flag = ARG_HASVALUE;
 | |
|     result->hdr.shortopts = shortopts;
 | |
|     result->hdr.longopts = longopts;
 | |
|     result->hdr.datatype = datatype ? datatype : format;
 | |
|     result->hdr.glossary = glossary;
 | |
|     result->hdr.mincount = mincount;
 | |
|     result->hdr.maxcount = maxcount;
 | |
|     result->hdr.parent = result;
 | |
|     result->hdr.resetfn = (arg_resetfn*)arg_date_resetfn;
 | |
|     result->hdr.scanfn = (arg_scanfn*)arg_date_scanfn;
 | |
|     result->hdr.checkfn = (arg_checkfn*)arg_date_checkfn;
 | |
|     result->hdr.errorfn = (arg_errorfn*)arg_date_errorfn;
 | |
| 
 | |
|     /* store the tmval[maxcount] array immediately after the arg_date struct */
 | |
|     result->tmval = (struct tm*)(result + 1);
 | |
| 
 | |
|     /* init the remaining arg_date member variables */
 | |
|     result->count = 0;
 | |
|     result->format = format;
 | |
| 
 | |
|     ARG_TRACE(("arg_daten() returns %p\n", result));
 | |
|     return result;
 | |
| }
 | |
| 
 | |
| /*-
 | |
|  * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc.
 | |
|  * All rights reserved.
 | |
|  *
 | |
|  * This code was contributed to The NetBSD Foundation by Klaus Klein.
 | |
|  * Heavily optimised by David Laight
 | |
|  *
 | |
|  * Redistribution and use in source and binary forms, with or without
 | |
|  * modification, are permitted provided that the following conditions
 | |
|  * are met:
 | |
|  * 1. Redistributions of source code must retain the above copyright
 | |
|  *    notice, this list of conditions and the following disclaimer.
 | |
|  * 2. Redistributions in binary form must reproduce the above copyright
 | |
|  *    notice, this list of conditions and the following disclaimer in the
 | |
|  *    documentation and/or other materials provided with the distribution.
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 | |
|  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 | |
|  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | |
|  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 | |
|  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | |
|  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | |
|  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | |
|  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | |
|  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | |
|  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 | |
|  * POSSIBILITY OF SUCH DAMAGE.
 | |
|  */
 | |
| 
 | |
| #include <ctype.h>
 | |
| #include <string.h>
 | |
| #include <time.h>
 | |
| 
 | |
| /*
 | |
|  * We do not implement alternate representations. However, we always
 | |
|  * check whether a given modifier is allowed for a certain conversion.
 | |
|  */
 | |
| #define ALT_E 0x01
 | |
| #define ALT_O 0x02
 | |
| #define LEGAL_ALT(x)           \
 | |
|     {                          \
 | |
|         if (alt_format & ~(x)) \
 | |
|             return (0);        \
 | |
|     }
 | |
| #define TM_YEAR_BASE (1900)
 | |
| 
 | |
| static int conv_num(const char**, int*, int, int);
 | |
| 
 | |
| static const char* day[7] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
 | |
| 
 | |
| static const char* abday[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
 | |
| 
 | |
| static const char* mon[12] = {"January", "February", "March",     "April",   "May",      "June",
 | |
|                               "July",    "August",   "September", "October", "November", "December"};
 | |
| 
 | |
| static const char* abmon[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
 | |
| 
 | |
| static const char* am_pm[2] = {"AM", "PM"};
 | |
| 
 | |
| static int arg_strcasecmp(const char* s1, const char* s2) {
 | |
|     const unsigned char* us1 = (const unsigned char*)s1;
 | |
|     const unsigned char* us2 = (const unsigned char*)s2;
 | |
|     while (tolower(*us1) == tolower(*us2++))
 | |
|         if (*us1++ == '\0')
 | |
|             return 0;
 | |
| 
 | |
|     return tolower(*us1) - tolower(*--us2);
 | |
| }
 | |
| 
 | |
| static int arg_strncasecmp(const char* s1, const char* s2, size_t n) {
 | |
|     if (n != 0) {
 | |
|         const unsigned char* us1 = (const unsigned char*)s1;
 | |
|         const unsigned char* us2 = (const unsigned char*)s2;
 | |
|         do {
 | |
|             if (tolower(*us1) != tolower(*us2++))
 | |
|                 return tolower(*us1) - tolower(*--us2);
 | |
| 
 | |
|             if (*us1++ == '\0')
 | |
|                 break;
 | |
|         } while (--n != 0);
 | |
|     }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| char* arg_strptime(const char* buf, const char* fmt, struct tm* tm) {
 | |
|     char c;
 | |
|     const char* bp;
 | |
|     size_t len = 0;
 | |
|     int alt_format, i, split_year = 0;
 | |
| 
 | |
|     bp = buf;
 | |
| 
 | |
|     while ((c = *fmt) != '\0') {
 | |
|         /* Clear `alternate' modifier prior to new conversion. */
 | |
|         alt_format = 0;
 | |
| 
 | |
|         /* Eat up white-space. */
 | |
|         if (isspace(c)) {
 | |
|             while (isspace((int)(*bp)))
 | |
|                 bp++;
 | |
| 
 | |
|             fmt++;
 | |
|             continue;
 | |
|         }
 | |
| 
 | |
|         if ((c = *fmt++) != '%')
 | |
|             goto literal;
 | |
| 
 | |
|     again:
 | |
|         switch (c = *fmt++) {
 | |
|             case '%': /* "%%" is converted to "%". */
 | |
|             literal:
 | |
|                 if (c != *bp++)
 | |
|                     return (0);
 | |
|                 break;
 | |
| 
 | |
|             /*
 | |
|              * "Alternative" modifiers. Just set the appropriate flag
 | |
|              * and start over again.
 | |
|              */
 | |
|             case 'E': /* "%E?" alternative conversion modifier. */
 | |
|                 LEGAL_ALT(0);
 | |
|                 alt_format |= ALT_E;
 | |
|                 goto again;
 | |
| 
 | |
|             case 'O': /* "%O?" alternative conversion modifier. */
 | |
|                 LEGAL_ALT(0);
 | |
|                 alt_format |= ALT_O;
 | |
|                 goto again;
 | |
| 
 | |
|             /*
 | |
|              * "Complex" conversion rules, implemented through recursion.
 | |
|              */
 | |
|             case 'c': /* Date and time, using the locale's format. */
 | |
|                 LEGAL_ALT(ALT_E);
 | |
|                 bp = arg_strptime(bp, "%x %X", tm);
 | |
|                 if (!bp)
 | |
|                     return (0);
 | |
|                 break;
 | |
| 
 | |
|             case 'D': /* The date as "%m/%d/%y". */
 | |
|                 LEGAL_ALT(0);
 | |
|                 bp = arg_strptime(bp, "%m/%d/%y", tm);
 | |
|                 if (!bp)
 | |
|                     return (0);
 | |
|                 break;
 | |
| 
 | |
|             case 'R': /* The time as "%H:%M". */
 | |
|                 LEGAL_ALT(0);
 | |
|                 bp = arg_strptime(bp, "%H:%M", tm);
 | |
|                 if (!bp)
 | |
|                     return (0);
 | |
|                 break;
 | |
| 
 | |
|             case 'r': /* The time in 12-hour clock representation. */
 | |
|                 LEGAL_ALT(0);
 | |
|                 bp = arg_strptime(bp, "%I:%M:%S %p", tm);
 | |
|                 if (!bp)
 | |
|                     return (0);
 | |
|                 break;
 | |
| 
 | |
|             case 'T': /* The time as "%H:%M:%S". */
 | |
|                 LEGAL_ALT(0);
 | |
|                 bp = arg_strptime(bp, "%H:%M:%S", tm);
 | |
|                 if (!bp)
 | |
|                     return (0);
 | |
|                 break;
 | |
| 
 | |
|             case 'X': /* The time, using the locale's format. */
 | |
|                 LEGAL_ALT(ALT_E);
 | |
|                 bp = arg_strptime(bp, "%H:%M:%S", tm);
 | |
|                 if (!bp)
 | |
|                     return (0);
 | |
|                 break;
 | |
| 
 | |
|             case 'x': /* The date, using the locale's format. */
 | |
|                 LEGAL_ALT(ALT_E);
 | |
|                 bp = arg_strptime(bp, "%m/%d/%y", tm);
 | |
|                 if (!bp)
 | |
|                     return (0);
 | |
|                 break;
 | |
| 
 | |
|             /*
 | |
|              * "Elementary" conversion rules.
 | |
|              */
 | |
|             case 'A': /* The day of week, using the locale's form. */
 | |
|             case 'a':
 | |
|                 LEGAL_ALT(0);
 | |
|                 for (i = 0; i < 7; i++) {
 | |
|                     /* Full name. */
 | |
|                     len = strlen(day[i]);
 | |
|                     if (arg_strncasecmp(day[i], bp, len) == 0)
 | |
|                         break;
 | |
| 
 | |
|                     /* Abbreviated name. */
 | |
|                     len = strlen(abday[i]);
 | |
|                     if (arg_strncasecmp(abday[i], bp, len) == 0)
 | |
|                         break;
 | |
|                 }
 | |
| 
 | |
|                 /* Nothing matched. */
 | |
|                 if (i == 7)
 | |
|                     return (0);
 | |
| 
 | |
|                 tm->tm_wday = i;
 | |
|                 bp += len;
 | |
|                 break;
 | |
| 
 | |
|             case 'B': /* The month, using the locale's form. */
 | |
|             case 'b':
 | |
|             case 'h':
 | |
|                 LEGAL_ALT(0);
 | |
|                 for (i = 0; i < 12; i++) {
 | |
|                     /* Full name. */
 | |
|                     len = strlen(mon[i]);
 | |
|                     if (arg_strncasecmp(mon[i], bp, len) == 0)
 | |
|                         break;
 | |
| 
 | |
|                     /* Abbreviated name. */
 | |
|                     len = strlen(abmon[i]);
 | |
|                     if (arg_strncasecmp(abmon[i], bp, len) == 0)
 | |
|                         break;
 | |
|                 }
 | |
| 
 | |
|                 /* Nothing matched. */
 | |
|                 if (i == 12)
 | |
|                     return (0);
 | |
| 
 | |
|                 tm->tm_mon = i;
 | |
|                 bp += len;
 | |
|                 break;
 | |
| 
 | |
|             case 'C': /* The century number. */
 | |
|                 LEGAL_ALT(ALT_E);
 | |
|                 if (!(conv_num(&bp, &i, 0, 99)))
 | |
|                     return (0);
 | |
| 
 | |
|                 if (split_year) {
 | |
|                     tm->tm_year = (tm->tm_year % 100) + (i * 100);
 | |
|                 } else {
 | |
|                     tm->tm_year = i * 100;
 | |
|                     split_year = 1;
 | |
|                 }
 | |
|                 break;
 | |
| 
 | |
|             case 'd': /* The day of month. */
 | |
|             case 'e':
 | |
|                 LEGAL_ALT(ALT_O);
 | |
|                 if (!(conv_num(&bp, &tm->tm_mday, 1, 31)))
 | |
|                     return (0);
 | |
|                 break;
 | |
| 
 | |
|             case 'k': /* The hour (24-hour clock representation). */
 | |
|                 LEGAL_ALT(0);
 | |
|             /* FALLTHROUGH */
 | |
|             case 'H':
 | |
|                 LEGAL_ALT(ALT_O);
 | |
|                 if (!(conv_num(&bp, &tm->tm_hour, 0, 23)))
 | |
|                     return (0);
 | |
|                 break;
 | |
| 
 | |
|             case 'l': /* The hour (12-hour clock representation). */
 | |
|                 LEGAL_ALT(0);
 | |
|             /* FALLTHROUGH */
 | |
|             case 'I':
 | |
|                 LEGAL_ALT(ALT_O);
 | |
|                 if (!(conv_num(&bp, &tm->tm_hour, 1, 12)))
 | |
|                     return (0);
 | |
|                 if (tm->tm_hour == 12)
 | |
|                     tm->tm_hour = 0;
 | |
|                 break;
 | |
| 
 | |
|             case 'j': /* The day of year. */
 | |
|                 LEGAL_ALT(0);
 | |
|                 if (!(conv_num(&bp, &i, 1, 366)))
 | |
|                     return (0);
 | |
|                 tm->tm_yday = i - 1;
 | |
|                 break;
 | |
| 
 | |
|             case 'M': /* The minute. */
 | |
|                 LEGAL_ALT(ALT_O);
 | |
|                 if (!(conv_num(&bp, &tm->tm_min, 0, 59)))
 | |
|                     return (0);
 | |
|                 break;
 | |
| 
 | |
|             case 'm': /* The month. */
 | |
|                 LEGAL_ALT(ALT_O);
 | |
|                 if (!(conv_num(&bp, &i, 1, 12)))
 | |
|                     return (0);
 | |
|                 tm->tm_mon = i - 1;
 | |
|                 break;
 | |
| 
 | |
|             case 'p': /* The locale's equivalent of AM/PM. */
 | |
|                 LEGAL_ALT(0);
 | |
|                 /* AM? */
 | |
|                 if (arg_strcasecmp(am_pm[0], bp) == 0) {
 | |
|                     if (tm->tm_hour > 11)
 | |
|                         return (0);
 | |
| 
 | |
|                     bp += strlen(am_pm[0]);
 | |
|                     break;
 | |
|                 }
 | |
|                 /* PM? */
 | |
|                 else if (arg_strcasecmp(am_pm[1], bp) == 0) {
 | |
|                     if (tm->tm_hour > 11)
 | |
|                         return (0);
 | |
| 
 | |
|                     tm->tm_hour += 12;
 | |
|                     bp += strlen(am_pm[1]);
 | |
|                     break;
 | |
|                 }
 | |
| 
 | |
|                 /* Nothing matched. */
 | |
|                 return (0);
 | |
| 
 | |
|             case 'S': /* The seconds. */
 | |
|                 LEGAL_ALT(ALT_O);
 | |
|                 if (!(conv_num(&bp, &tm->tm_sec, 0, 61)))
 | |
|                     return (0);
 | |
|                 break;
 | |
| 
 | |
|             case 'U': /* The week of year, beginning on sunday. */
 | |
|             case 'W': /* The week of year, beginning on monday. */
 | |
|                 LEGAL_ALT(ALT_O);
 | |
|                 /*
 | |
|                  * XXX This is bogus, as we can not assume any valid
 | |
|                  * information present in the tm structure at this
 | |
|                  * point to calculate a real value, so just check the
 | |
|                  * range for now.
 | |
|                  */
 | |
|                 if (!(conv_num(&bp, &i, 0, 53)))
 | |
|                     return (0);
 | |
|                 break;
 | |
| 
 | |
|             case 'w': /* The day of week, beginning on sunday. */
 | |
|                 LEGAL_ALT(ALT_O);
 | |
|                 if (!(conv_num(&bp, &tm->tm_wday, 0, 6)))
 | |
|                     return (0);
 | |
|                 break;
 | |
| 
 | |
|             case 'Y': /* The year. */
 | |
|                 LEGAL_ALT(ALT_E);
 | |
|                 if (!(conv_num(&bp, &i, 0, 9999)))
 | |
|                     return (0);
 | |
| 
 | |
|                 tm->tm_year = i - TM_YEAR_BASE;
 | |
|                 break;
 | |
| 
 | |
|             case 'y': /* The year within 100 years of the epoch. */
 | |
|                 LEGAL_ALT(ALT_E | ALT_O);
 | |
|                 if (!(conv_num(&bp, &i, 0, 99)))
 | |
|                     return (0);
 | |
| 
 | |
|                 if (split_year) {
 | |
|                     tm->tm_year = ((tm->tm_year / 100) * 100) + i;
 | |
|                     break;
 | |
|                 }
 | |
|                 split_year = 1;
 | |
|                 if (i <= 68)
 | |
|                     tm->tm_year = i + 2000 - TM_YEAR_BASE;
 | |
|                 else
 | |
|                     tm->tm_year = i + 1900 - TM_YEAR_BASE;
 | |
|                 break;
 | |
| 
 | |
|             /*
 | |
|              * Miscellaneous conversions.
 | |
|              */
 | |
|             case 'n': /* Any kind of white-space. */
 | |
|             case 't':
 | |
|                 LEGAL_ALT(0);
 | |
|                 while (isspace((int)(*bp)))
 | |
|                     bp++;
 | |
|                 break;
 | |
| 
 | |
|             default: /* Unknown/unsupported conversion. */
 | |
|                 return (0);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* LINTED functional specification */
 | |
|     return ((char*)bp);
 | |
| }
 | |
| 
 | |
| static int conv_num(const char** buf, int* dest, int llim, int ulim) {
 | |
|     int result = 0;
 | |
| 
 | |
|     /* The limit also determines the number of valid digits. */
 | |
|     int rulim = ulim;
 | |
| 
 | |
|     if (**buf < '0' || **buf > '9')
 | |
|         return (0);
 | |
| 
 | |
|     do {
 | |
|         result *= 10;
 | |
|         result += *(*buf)++ - '0';
 | |
|         rulim /= 10;
 | |
|     } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9');
 | |
| 
 | |
|     if (result < llim || result > ulim)
 | |
|         return (0);
 | |
| 
 | |
|     *dest = result;
 | |
|     return (1);
 | |
| }
 | 
