1 /*
2  * lib/krb5/krb/deltat.y
3  *
4  * Copyright 1999 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  *
26  *
27  * krb5_string_to_deltat()
28  */
29 
30 /* For a clean, thread-safe interface, we must use the "pure parser"
31    facility of GNU Bison.  Unfortunately, standard YACC has no such
32    option.  */
33 
34 /* N.B.: For simplicity in dealing with the distribution, the
35    Makefile.in listing for deltat.c does *not* normally list this
36    file.  If you change this file, tweak the Makefile so it'll rebuild
37    deltat.c, or do it manually.  */
38 %{
39 
40 /*
41  * GCC optimizer will detect a variable used without being set in a YYERROR
42  * path.  As this is generated code, suppress the complaint.
43  */
44 #ifdef __GNUC__
45 #pragma GCC diagnostic push
46 #pragma GCC diagnostic ignored "-Wuninitialized"
47 #endif
48 
49 #include "k5-int.h"
50 #include <ctype.h>
51 
52 struct param {
53     krb5_int32 delta;
54     char *p;
55 };
56 
57 #define MAX_TIME KRB5_INT32_MAX
58 #define MIN_TIME KRB5_INT32_MIN
59 
60 #define DAY (24 * 3600)
61 #define HOUR 3600
62 
63 #define MAX_DAY (MAX_TIME / DAY)
64 #define MIN_DAY (MIN_TIME / DAY)
65 #define MAX_HOUR (MAX_TIME / HOUR)
66 #define MIN_HOUR (MIN_TIME / HOUR)
67 #define MAX_MIN (MAX_TIME / 60)
68 #define MIN_MIN (MIN_TIME / 60)
69 
70 /* An explanation of the tests being performed.
71    We do not want to overflow a 32 bit integer with out manipulations,
72    even for testing for overflow. Therefore we rely on the following:
73 
74    The lex parser will not return a number > MAX_TIME (which is out 32
75    bit limit).
76 
77    Therefore, seconds (s) will require
78        MIN_TIME < s < MAX_TIME
79 
80    For subsequent tests, the logic is as follows:
81 
82       If A < MAX_TIME and  B < MAX_TIME
83 
84       If we want to test if A+B < MAX_TIME, there are two cases
85         if (A > 0)
86          then A + B < MAX_TIME if B < MAX_TIME - A
87 	else A + B < MAX_TIME  always.
88 
89       if we want to test if MIN_TIME < A + B
90           if A > 0 - then nothing to test
91           otherwise, we test if MIN_TIME - A < B.
92 
93    We of course are testing for:
94           MIN_TIME < A + B < MAX_TIME
95 */
96 
97 
98 #define DAY_NOT_OK(d) (d) > MAX_DAY || (d) < MIN_DAY
99 #define HOUR_NOT_OK(h) (h) > MAX_HOUR || (h) < MIN_HOUR
100 #define MIN_NOT_OK(m) (m) > MAX_MIN || (m) < MIN_MIN
101 #define SUM_OK(a, b) (((a) > 0) ? ( (b) <= MAX_TIME - (a)) : (MIN_TIME - (a) <= (b)))
102 #define DO_SUM(res, a, b) if (!SUM_OK((a), (b))) YYERROR; \
103                           res = (a) + (b)
104 
105 
106 #define OUT_D tmv->delta
107 #define DO(D,H,M,S) \
108  { \
109      /* Overflow testing - this does not handle negative values well.. */ \
110      if (DAY_NOT_OK(D) || HOUR_NOT_OK(H) || MIN_NOT_OK(M)) YYERROR; \
111      OUT_D = D * DAY; \
112      DO_SUM(OUT_D, OUT_D, H * HOUR); \
113      DO_SUM(OUT_D, OUT_D, M * 60); \
114      DO_SUM(OUT_D, OUT_D, S); \
115  }
116 
117 static int mylex(int *intp, struct param *tmv);
118 #undef yylex
119 #define yylex(U, P)    mylex (&(U)->val, (P))
120 
121 #undef yyerror
122 #define yyerror(tmv, msg)
123 
124 static int yyparse(struct param *);
125 
126 %}
127 
128 %union {int val;}
129 %parse-param {struct param *tmv}
130 %lex-param {struct param *tmv}
131 %define api.pure
132 
133 %token <val> tok_NUM tok_LONGNUM tok_OVERFLOW
134 %token '-' ':' 'd' 'h' 'm' 's' tok_WS
135 
136 %type <val> num opt_hms opt_ms opt_s wsnum posnum
137 
138 %start start
139 
140 %%
141 
142 start: deltat;
143 posnum: tok_NUM | tok_LONGNUM ;
144 num: posnum | '-' posnum { $$ = - $2; } ;
145 ws: /* nothing */ | tok_WS ;
146 wsnum: ws num { $$ = $2; }
147         | ws tok_OVERFLOW { YYERROR; };
148 deltat:
149 	  wsnum 'd' opt_hms                          { DO ($1,  0,  0, $3); }
150 	| wsnum 'h' opt_ms                           { DO ( 0, $1,  0, $3); }
151 	| wsnum 'm' opt_s                            { DO ( 0,  0, $1, $3); }
152 	| wsnum 's'                                  { DO ( 0,  0,  0, $1); }
153 	| wsnum '-' tok_NUM ':' tok_NUM ':' tok_NUM  { DO ($1, $3, $5, $7); }
154 	| wsnum ':' tok_NUM ':' tok_NUM              { DO ( 0, $1, $3, $5); }
155 	| wsnum ':' tok_NUM                          { DO ( 0, $1, $3,  0); }
156 	| wsnum                                      { DO ( 0,  0,  0, $1); }
157 	                                             /* default to 's' */
158 	;
159 
160 opt_hms:
161 	  opt_ms
162 	  | wsnum 'h' opt_ms		{ if (HOUR_NOT_OK($1)) YYERROR;
163 	                                  DO_SUM($$, $1 * 3600, $3); };
164 opt_ms:
165 	  opt_s
166 	| wsnum 'm' opt_s		{ if (MIN_NOT_OK($1)) YYERROR;
167 	                                  DO_SUM($$, $1 * 60, $3); };
168 opt_s:
169 	  ws				{ $$ = 0; }
170 	| wsnum 's' ;
171 
172 %%
173 
174 #ifdef __GNUC__
175 #pragma GCC diagnostic pop
176 #endif
177 
178 static int
179 mylex(int *intp, struct param *tmv)
180 {
181     int num, c;
182 #define P (tmv->p)
183     char *orig_p = P;
184 
185 #ifdef isascii
186     if (!isascii (*P))
187 	return 0;
188 #endif
189     switch (c = *P++) {
190     case '-':
191     case ':':
192     case 'd':
193     case 'h':
194     case 'm':
195     case 's':
196 	return c;
197     case '0':
198     case '1':
199     case '2':
200     case '3':
201     case '4':
202     case '5':
203     case '6':
204     case '7':
205     case '8':
206     case '9':
207 	/* XXX assumes ASCII */
208 	num = c - '0';
209 	while (isdigit ((int) *P)) {
210 	  if (num > MAX_TIME / 10)
211 	    return tok_OVERFLOW;
212 	    num *= 10;
213 	    if (num > MAX_TIME - (*P - '0'))
214 	      return tok_OVERFLOW;
215 	    num += *P++ - '0';
216 	}
217 	*intp = num;
218 	return (P - orig_p > 2) ? tok_LONGNUM : tok_NUM;
219     case ' ':
220     case '\t':
221     case '\n':
222 	while (isspace ((int) *P))
223 	    P++;
224 	return tok_WS;
225     default:
226 	return YYEOF;
227     }
228 }
229 
230 krb5_error_code KRB5_CALLCONV
231 krb5_string_to_deltat(char *string, krb5_deltat *deltatp)
232 {
233     struct param p;
234     p.delta = 0;
235     p.p = string;
236     if (yyparse (&p))
237 	return KRB5_DELTAT_BADFORMAT;
238     *deltatp = p.delta;
239     return 0;
240 }
241