/* * lib/krb5/krb/deltat.y * * Copyright 1999 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * * * krb5_string_to_deltat() */ /* For a clean, thread-safe interface, we must use the "pure parser" facility of GNU Bison. Unfortunately, standard YACC has no such option. */ /* N.B.: For simplicity in dealing with the distribution, the Makefile.in listing for deltat.c does *not* normally list this file. If you change this file, tweak the Makefile so it'll rebuild deltat.c, or do it manually. */ %{ /* * GCC optimizer will detect a variable used without being set in a YYERROR * path. As this is generated code, suppress the complaint. */ #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wuninitialized" #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #endif #include #include #include "k5-int.h" struct param { krb5_int32 delta; char *p; }; #define MAX_TIME KRB5_INT32_MAX #define MIN_TIME KRB5_INT32_MIN #define DAY (24 * 3600) #define HOUR 3600 #define MAX_DAY (MAX_TIME / DAY) #define MIN_DAY (MIN_TIME / DAY) #define MAX_HOUR (MAX_TIME / HOUR) #define MIN_HOUR (MIN_TIME / HOUR) #define MAX_MIN (MAX_TIME / 60) #define MIN_MIN (MIN_TIME / 60) /* An explanation of the tests being performed. We do not want to overflow a 32 bit integer with out manipulations, even for testing for overflow. Therefore we rely on the following: The lex parser will not return a number > MAX_TIME (which is out 32 bit limit). Therefore, seconds (s) will require MIN_TIME < s < MAX_TIME For subsequent tests, the logic is as follows: If A < MAX_TIME and B < MAX_TIME If we want to test if A+B < MAX_TIME, there are two cases if (A > 0) then A + B < MAX_TIME if B < MAX_TIME - A else A + B < MAX_TIME always. if we want to test if MIN_TIME < A + B if A > 0 - then nothing to test otherwise, we test if MIN_TIME - A < B. We of course are testing for: MIN_TIME < A + B < MAX_TIME */ #define DAY_NOT_OK(d) (d) > MAX_DAY || (d) < MIN_DAY #define HOUR_NOT_OK(h) (h) > MAX_HOUR || (h) < MIN_HOUR #define MIN_NOT_OK(m) (m) > MAX_MIN || (m) < MIN_MIN #define SUM_OK(a, b) (((a) > 0) ? ( (b) <= MAX_TIME - (a)) : (MIN_TIME - (a) <= (b))) #define DO_SUM(res, a, b) if (!SUM_OK((a), (b))) YYERROR; \ res = (a) + (b) #define OUT_D tmv->delta #define DO(D,H,M,S) \ { \ /* Overflow testing - this does not handle negative values well.. */ \ if (DAY_NOT_OK(D) || HOUR_NOT_OK(H) || MIN_NOT_OK(M)) YYERROR; \ OUT_D = D * DAY; \ DO_SUM(OUT_D, OUT_D, H * HOUR); \ DO_SUM(OUT_D, OUT_D, M * 60); \ DO_SUM(OUT_D, OUT_D, S); \ } static int mylex(int *intp, struct param *tmv); #undef yylex #define yylex(U, P) mylex (&(U)->val, (P)) #undef yyerror #define yyerror(tmv, msg) static int yyparse(struct param *); %} %union {int val;} %parse-param {struct param *tmv} %lex-param {struct param *tmv} %define api.pure %token NUM LONGNUM OVERFLOW %token '-' ':' 'd' 'h' 'm' 's' tok_WS %type num opt_hms opt_ms opt_s wsnum posnum %start start %% start: deltat; posnum: NUM | LONGNUM ; num: posnum | '-' posnum { $$ = - $2; } ; ws: /* nothing */ | tok_WS ; wsnum: ws num { $$ = $2; } | ws OVERFLOW { YYERROR; }; deltat: wsnum 'd' opt_hms { DO ($1, 0, 0, $3); } | wsnum 'h' opt_ms { DO ( 0, $1, 0, $3); } | wsnum 'm' opt_s { DO ( 0, 0, $1, $3); } | wsnum 's' { DO ( 0, 0, 0, $1); } | wsnum '-' NUM ':' NUM ':' NUM { DO ($1, $3, $5, $7); } | wsnum ':' NUM ':' NUM { DO ( 0, $1, $3, $5); } | wsnum ':' NUM { DO ( 0, $1, $3, 0); } | wsnum { DO ( 0, 0, 0, $1); } /* default to 's' */ ; opt_hms: opt_ms | wsnum 'h' opt_ms { if (HOUR_NOT_OK($1)) YYERROR; DO_SUM($$, $1 * 3600, $3); }; opt_ms: opt_s | wsnum 'm' opt_s { if (MIN_NOT_OK($1)) YYERROR; DO_SUM($$, $1 * 60, $3); }; opt_s: ws { $$ = 0; } | wsnum 's' ; %% #ifdef __GNUC__ #pragma GCC diagnostic pop #endif static int mylex(int *intp, struct param *tmv) { int num, c; #define P (tmv->p) char *orig_p = P; #ifdef isascii if (!isascii (*P)) return 0; #endif switch (c = *P++) { case '-': case ':': case 'd': case 'h': case 'm': case 's': return c; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': /* XXX assumes ASCII */ num = c - '0'; while (isdigit ((int) *P)) { if (num > MAX_TIME / 10) return OVERFLOW; num *= 10; if (num > MAX_TIME - (*P - '0')) return OVERFLOW; num += *P++ - '0'; } *intp = num; return (P - orig_p > 2) ? LONGNUM : NUM; case ' ': case '\t': case '\n': while (isspace ((int) *P)) P++; return tok_WS; default: return YYEOF; } } krb5_error_code KRB5_CALLCONV krb5_string_to_deltat(char *string, krb5_deltat *deltatp) { struct param p; p.delta = 0; p.p = string; if (yyparse (&p)) return KRB5_DELTAT_BADFORMAT; *deltatp = p.delta; return 0; }