factor out time_t-to-string conversion idiom
[platform/upstream/coreutils.git] / src / expr.c
1 /* expr -- evaluate expressions.
2    Copyright (C) 86, 1991-1997, 1999-2008 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
16
17 /* Author: Mike Parker.
18
19    This program evaluates expressions.  Each token (operator, operand,
20    parenthesis) of the expression must be a seperate argument.  The
21    parser used is a reasonably general one, though any incarnation of
22    it is language-specific.  It is especially nice for expressions.
23
24    No parse tree is needed; a new node is evaluated immediately.
25    One function can handle multiple operators all of equal precedence,
26    provided they all associate ((x op x) op x).
27
28    Define EVAL_TRACE to print an evaluation trace.  */
29
30 #include <config.h>
31 #include <stdio.h>
32 #include <sys/types.h>
33 #include "system.h"
34
35 #include <regex.h>
36 #include "long-options.h"
37 #include "error.h"
38 #include "quotearg.h"
39 #include "strnumcmp.h"
40 #include "xstrtol.h"
41
42 /* The official name of this program (e.g., no `g' prefix).  */
43 #define PROGRAM_NAME "expr"
44
45 #define AUTHORS proper_name ("Mike Parker")
46
47 /* Exit statuses.  */
48 enum
49   {
50     /* Invalid expression: e.g., its form does not conform to the
51        grammar for expressions.  Our grammar is an extension of the
52        POSIX grammar.  */
53     EXPR_INVALID = 2,
54
55     /* An internal error occurred, e.g., arithmetic overflow, storage
56        exhaustion.  */
57     EXPR_FAILURE
58   };
59
60 /* The kinds of value we can have.  */
61 enum valtype
62 {
63   integer,
64   string
65 };
66 typedef enum valtype TYPE;
67
68 /* A value is.... */
69 struct valinfo
70 {
71   TYPE type;                    /* Which kind. */
72   union
73   {                             /* The value itself. */
74     intmax_t i;
75     char *s;
76   } u;
77 };
78 typedef struct valinfo VALUE;
79
80 /* The arguments given to the program, minus the program name.  */
81 static char **args;
82
83 static VALUE *eval (bool);
84 static bool nomoreargs (void);
85 static bool null (VALUE *v);
86 static void printv (VALUE *v);
87
88 void
89 usage (int status)
90 {
91   if (status != EXIT_SUCCESS)
92     fprintf (stderr, _("Try `%s --help' for more information.\n"),
93              program_name);
94   else
95     {
96       printf (_("\
97 Usage: %s EXPRESSION\n\
98   or:  %s OPTION\n\
99 "),
100               program_name, program_name);
101       putchar ('\n');
102       fputs (HELP_OPTION_DESCRIPTION, stdout);
103       fputs (VERSION_OPTION_DESCRIPTION, stdout);
104       fputs (_("\
105 \n\
106 Print the value of EXPRESSION to standard output.  A blank line below\n\
107 separates increasing precedence groups.  EXPRESSION may be:\n\
108 \n\
109   ARG1 | ARG2       ARG1 if it is neither null nor 0, otherwise ARG2\n\
110 \n\
111   ARG1 & ARG2       ARG1 if neither argument is null or 0, otherwise 0\n\
112 "), stdout);
113       fputs (_("\
114 \n\
115   ARG1 < ARG2       ARG1 is less than ARG2\n\
116   ARG1 <= ARG2      ARG1 is less than or equal to ARG2\n\
117   ARG1 = ARG2       ARG1 is equal to ARG2\n\
118   ARG1 != ARG2      ARG1 is unequal to ARG2\n\
119   ARG1 >= ARG2      ARG1 is greater than or equal to ARG2\n\
120   ARG1 > ARG2       ARG1 is greater than ARG2\n\
121 "), stdout);
122       fputs (_("\
123 \n\
124   ARG1 + ARG2       arithmetic sum of ARG1 and ARG2\n\
125   ARG1 - ARG2       arithmetic difference of ARG1 and ARG2\n\
126 "), stdout);
127       /* Tell xgettext that the "% A" below is not a printf-style
128          format string:  xgettext:no-c-format */
129       fputs (_("\
130 \n\
131   ARG1 * ARG2       arithmetic product of ARG1 and ARG2\n\
132   ARG1 / ARG2       arithmetic quotient of ARG1 divided by ARG2\n\
133   ARG1 % ARG2       arithmetic remainder of ARG1 divided by ARG2\n\
134 "), stdout);
135       fputs (_("\
136 \n\
137   STRING : REGEXP   anchored pattern match of REGEXP in STRING\n\
138 \n\
139   match STRING REGEXP        same as STRING : REGEXP\n\
140   substr STRING POS LENGTH   substring of STRING, POS counted from 1\n\
141   index STRING CHARS         index in STRING where any CHARS is found, or 0\n\
142   length STRING              length of STRING\n\
143 "), stdout);
144       fputs (_("\
145   + TOKEN                    interpret TOKEN as a string, even if it is a\n\
146                                keyword like `match' or an operator like `/'\n\
147 \n\
148   ( EXPRESSION )             value of EXPRESSION\n\
149 "), stdout);
150       fputs (_("\
151 \n\
152 Beware that many operators need to be escaped or quoted for shells.\n\
153 Comparisons are arithmetic if both ARGs are numbers, else lexicographical.\n\
154 Pattern matches return the string matched between \\( and \\) or null; if\n\
155 \\( and \\) are not used, they return the number of characters matched or 0.\n\
156 "), stdout);
157       fputs (_("\
158 \n\
159 Exit status is 0 if EXPRESSION is neither null nor 0, 1 if EXPRESSION is null\n\
160 or 0, 2 if EXPRESSION is syntactically invalid, and 3 if an error occurred.\n\
161 "), stdout);
162       emit_bug_reporting_address ();
163     }
164   exit (status);
165 }
166
167 /* Report a syntax error and exit.  */
168 static void
169 syntax_error (void)
170 {
171   error (EXPR_INVALID, 0, _("syntax error"));
172 }
173
174 /* Report an integer overflow for operation OP and exit.  */
175 static void
176 integer_overflow (char op)
177 {
178   error (EXPR_FAILURE, ERANGE, "%c", op);
179 }
180
181 int
182 main (int argc, char **argv)
183 {
184   VALUE *v;
185
186   initialize_main (&argc, &argv);
187   set_program_name (argv[0]);
188   setlocale (LC_ALL, "");
189   bindtextdomain (PACKAGE, LOCALEDIR);
190   textdomain (PACKAGE);
191
192   initialize_exit_failure (EXPR_FAILURE);
193   atexit (close_stdout);
194
195   parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, VERSION,
196                       usage, AUTHORS, (char const *) NULL);
197   /* The above handles --help and --version.
198      Since there is no other invocation of getopt, handle `--' here.  */
199   if (argc > 1 && STREQ (argv[1], "--"))
200     {
201       --argc;
202       ++argv;
203     }
204
205   if (argc <= 1)
206     {
207       error (0, 0, _("missing operand"));
208       usage (EXPR_INVALID);
209     }
210
211   args = argv + 1;
212
213   v = eval (true);
214   if (!nomoreargs ())
215     syntax_error ();
216   printv (v);
217
218   exit (null (v));
219 }
220
221 /* Return a VALUE for I.  */
222
223 static VALUE *
224 int_value (intmax_t i)
225 {
226   VALUE *v = xmalloc (sizeof *v);
227   v->type = integer;
228   v->u.i = i;
229   return v;
230 }
231
232 /* Return a VALUE for S.  */
233
234 static VALUE *
235 str_value (char const *s)
236 {
237   VALUE *v = xmalloc (sizeof *v);
238   v->type = string;
239   v->u.s = xstrdup (s);
240   return v;
241 }
242
243 /* Free VALUE V, including structure components.  */
244
245 static void
246 freev (VALUE *v)
247 {
248   if (v->type == string)
249     free (v->u.s);
250   free (v);
251 }
252
253 /* Print VALUE V.  */
254
255 static void
256 printv (VALUE *v)
257 {
258   char *p;
259   char buf[INT_BUFSIZE_BOUND (intmax_t)];
260
261   switch (v->type)
262     {
263     case integer:
264       p = imaxtostr (v->u.i, buf);
265       break;
266     case string:
267       p = v->u.s;
268       break;
269     default:
270       abort ();
271     }
272
273   puts (p);
274 }
275
276 /* Return true if V is a null-string or zero-number.  */
277
278 static bool
279 null (VALUE *v)
280 {
281   switch (v->type)
282     {
283     case integer:
284       return v->u.i == 0;
285     case string:
286       {
287         char const *cp = v->u.s;
288         if (*cp == '\0')
289           return true;
290
291         cp += (*cp == '-');
292
293         do
294           {
295             if (*cp != '0')
296               return false;
297           }
298         while (*++cp);
299
300         return true;
301       }
302     default:
303       abort ();
304     }
305 }
306
307 /* Return true if CP takes the form of an integer.  */
308
309 static bool
310 looks_like_integer (char const *cp)
311 {
312   cp += (*cp == '-');
313
314   do
315     if (! ISDIGIT (*cp))
316       return false;
317   while (*++cp);
318
319   return true;
320 }
321
322 /* Coerce V to a string value (can't fail).  */
323
324 static void
325 tostring (VALUE *v)
326 {
327   char buf[INT_BUFSIZE_BOUND (intmax_t)];
328
329   switch (v->type)
330     {
331     case integer:
332       v->u.s = xstrdup (imaxtostr (v->u.i, buf));
333       v->type = string;
334       break;
335     case string:
336       break;
337     default:
338       abort ();
339     }
340 }
341
342 /* Coerce V to an integer value.  Return true on success, false on failure.  */
343
344 static bool
345 toarith (VALUE *v)
346 {
347   switch (v->type)
348     {
349     case integer:
350       return true;
351     case string:
352       {
353         intmax_t value;
354
355         if (! looks_like_integer (v->u.s))
356           return false;
357         if (xstrtoimax (v->u.s, NULL, 10, &value, NULL) != LONGINT_OK)
358           error (EXPR_FAILURE, ERANGE, "%s", v->u.s);
359         free (v->u.s);
360         v->u.i = value;
361         v->type = integer;
362         return true;
363       }
364     default:
365       abort ();
366     }
367 }
368
369 /* Return true and advance if the next token matches STR exactly.
370    STR must not be NULL.  */
371
372 static bool
373 nextarg (char const *str)
374 {
375   if (*args == NULL)
376     return false;
377   else
378     {
379       bool r = STREQ (*args, str);
380       args += r;
381       return r;
382     }
383 }
384
385 /* Return true if there no more tokens.  */
386
387 static bool
388 nomoreargs (void)
389 {
390   return *args == 0;
391 }
392
393 #ifdef EVAL_TRACE
394 /* Print evaluation trace and args remaining.  */
395
396 static void
397 trace (fxn)
398      char *fxn;
399 {
400   char **a;
401
402   printf ("%s:", fxn);
403   for (a = args; *a; a++)
404     printf (" %s", *a);
405   putchar ('\n');
406 }
407 #endif
408
409 /* Do the : operator.
410    SV is the VALUE for the lhs (the string),
411    PV is the VALUE for the rhs (the pattern).  */
412
413 static VALUE *
414 docolon (VALUE *sv, VALUE *pv)
415 {
416   VALUE *v IF_LINT (= NULL);
417   const char *errmsg;
418   struct re_pattern_buffer re_buffer;
419   char fastmap[UCHAR_MAX + 1];
420   struct re_registers re_regs;
421   regoff_t matchlen;
422
423   tostring (sv);
424   tostring (pv);
425
426   re_regs.num_regs = 0;
427   re_regs.start = NULL;
428   re_regs.end = NULL;
429
430   re_buffer.buffer = NULL;
431   re_buffer.allocated = 0;
432   re_buffer.fastmap = fastmap;
433   re_buffer.translate = NULL;
434   re_syntax_options =
435     RE_SYNTAX_POSIX_BASIC & ~RE_CONTEXT_INVALID_DUP & ~RE_NO_EMPTY_RANGES;
436   errmsg = re_compile_pattern (pv->u.s, strlen (pv->u.s), &re_buffer);
437   if (errmsg)
438     error (EXPR_INVALID, 0, "%s", errmsg);
439   re_buffer.newline_anchor = 0;
440
441   matchlen = re_match (&re_buffer, sv->u.s, strlen (sv->u.s), 0, &re_regs);
442   if (0 <= matchlen)
443     {
444       /* Were \(...\) used? */
445       if (re_buffer.re_nsub > 0)
446         {
447           sv->u.s[re_regs.end[1]] = '\0';
448           v = str_value (sv->u.s + re_regs.start[1]);
449         }
450       else
451         v = int_value (matchlen);
452     }
453   else if (matchlen == -1)
454     {
455       /* Match failed -- return the right kind of null.  */
456       if (re_buffer.re_nsub > 0)
457         v = str_value ("");
458       else
459         v = int_value (0);
460     }
461   else
462     error (EXPR_FAILURE,
463            (matchlen == -2 ? errno : EOVERFLOW),
464            _("error in regular expression matcher"));
465
466   if (0 < re_regs.num_regs)
467     {
468       free (re_regs.start);
469       free (re_regs.end);
470     }
471   re_buffer.fastmap = NULL;
472   regfree (&re_buffer);
473   return v;
474 }
475
476 /* Handle bare operands and ( expr ) syntax.  */
477
478 static VALUE *
479 eval7 (bool evaluate)
480 {
481   VALUE *v;
482
483 #ifdef EVAL_TRACE
484   trace ("eval7");
485 #endif
486   if (nomoreargs ())
487     syntax_error ();
488
489   if (nextarg ("("))
490     {
491       v = eval (evaluate);
492       if (!nextarg (")"))
493         syntax_error ();
494       return v;
495     }
496
497   if (nextarg (")"))
498     syntax_error ();
499
500   return str_value (*args++);
501 }
502
503 /* Handle match, substr, index, and length keywords, and quoting "+".  */
504
505 static VALUE *
506 eval6 (bool evaluate)
507 {
508   VALUE *l;
509   VALUE *r;
510   VALUE *v;
511   VALUE *i1;
512   VALUE *i2;
513
514 #ifdef EVAL_TRACE
515   trace ("eval6");
516 #endif
517   if (nextarg ("+"))
518     {
519       if (nomoreargs ())
520         syntax_error ();
521       return str_value (*args++);
522     }
523   else if (nextarg ("length"))
524     {
525       r = eval6 (evaluate);
526       tostring (r);
527       v = int_value (strlen (r->u.s));
528       freev (r);
529       return v;
530     }
531   else if (nextarg ("match"))
532     {
533       l = eval6 (evaluate);
534       r = eval6 (evaluate);
535       if (evaluate)
536         {
537           v = docolon (l, r);
538           freev (l);
539         }
540       else
541         v = l;
542       freev (r);
543       return v;
544     }
545   else if (nextarg ("index"))
546     {
547       l = eval6 (evaluate);
548       r = eval6 (evaluate);
549       tostring (l);
550       tostring (r);
551       v = int_value (strcspn (l->u.s, r->u.s) + 1);
552       if (v->u.i == strlen (l->u.s) + 1)
553         v->u.i = 0;
554       freev (l);
555       freev (r);
556       return v;
557     }
558   else if (nextarg ("substr"))
559     {
560       size_t llen;
561       l = eval6 (evaluate);
562       i1 = eval6 (evaluate);
563       i2 = eval6 (evaluate);
564       tostring (l);
565       llen = strlen (l->u.s);
566       if (!toarith (i1) || !toarith (i2)
567           || llen < i1->u.i
568           || i1->u.i <= 0 || i2->u.i <= 0)
569         v = str_value ("");
570       else
571         {
572           size_t vlen = MIN (i2->u.i, llen - i1->u.i + 1);
573           char *vlim;
574           v = xmalloc (sizeof *v);
575           v->type = string;
576           v->u.s = xmalloc (vlen + 1);
577           vlim = mempcpy (v->u.s, l->u.s + i1->u.i - 1, vlen);
578           *vlim = '\0';
579         }
580       freev (l);
581       freev (i1);
582       freev (i2);
583       return v;
584     }
585   else
586     return eval7 (evaluate);
587 }
588
589 /* Handle : operator (pattern matching).
590    Calls docolon to do the real work.  */
591
592 static VALUE *
593 eval5 (bool evaluate)
594 {
595   VALUE *l;
596   VALUE *r;
597   VALUE *v;
598
599 #ifdef EVAL_TRACE
600   trace ("eval5");
601 #endif
602   l = eval6 (evaluate);
603   while (1)
604     {
605       if (nextarg (":"))
606         {
607           r = eval6 (evaluate);
608           if (evaluate)
609             {
610               v = docolon (l, r);
611               freev (l);
612               l = v;
613             }
614           freev (r);
615         }
616       else
617         return l;
618     }
619 }
620
621 /* Handle *, /, % operators.  */
622
623 static VALUE *
624 eval4 (bool evaluate)
625 {
626   VALUE *l;
627   VALUE *r;
628   enum { multiply, divide, mod } fxn;
629   intmax_t val = 0;
630
631 #ifdef EVAL_TRACE
632   trace ("eval4");
633 #endif
634   l = eval5 (evaluate);
635   while (1)
636     {
637       if (nextarg ("*"))
638         fxn = multiply;
639       else if (nextarg ("/"))
640         fxn = divide;
641       else if (nextarg ("%"))
642         fxn = mod;
643       else
644         return l;
645       r = eval5 (evaluate);
646       if (evaluate)
647         {
648           if (!toarith (l) || !toarith (r))
649             error (EXPR_INVALID, 0, _("non-numeric argument"));
650           if (fxn == multiply)
651             {
652               val = l->u.i * r->u.i;
653               if (! (l->u.i == 0 || r->u.i == 0
654                      || ((val < 0) == ((l->u.i < 0) ^ (r->u.i < 0))
655                          && val / l->u.i == r->u.i)))
656                 integer_overflow ('*');
657             }
658           else
659             {
660               if (r->u.i == 0)
661                 error (EXPR_INVALID, 0, _("division by zero"));
662               if (l->u.i < - INTMAX_MAX && r->u.i == -1)
663                 {
664                   /* Some x86-style hosts raise an exception for
665                      INT_MIN / -1 and INT_MIN % -1, so handle these
666                      problematic cases specially.  */
667                   if (fxn == divide)
668                     integer_overflow ('/');
669                   val = 0;
670                 }
671               else
672                 val = fxn == divide ? l->u.i / r->u.i : l->u.i % r->u.i;
673             }
674         }
675       freev (l);
676       freev (r);
677       l = int_value (val);
678     }
679 }
680
681 /* Handle +, - operators.  */
682
683 static VALUE *
684 eval3 (bool evaluate)
685 {
686   VALUE *l;
687   VALUE *r;
688   enum { plus, minus } fxn;
689   intmax_t val = 0;
690
691 #ifdef EVAL_TRACE
692   trace ("eval3");
693 #endif
694   l = eval4 (evaluate);
695   while (1)
696     {
697       if (nextarg ("+"))
698         fxn = plus;
699       else if (nextarg ("-"))
700         fxn = minus;
701       else
702         return l;
703       r = eval4 (evaluate);
704       if (evaluate)
705         {
706           if (!toarith (l) || !toarith (r))
707             error (EXPR_INVALID, 0, _("non-numeric argument"));
708           if (fxn == plus)
709             {
710               val = l->u.i + r->u.i;
711               if ((val < l->u.i) != (r->u.i < 0))
712                 integer_overflow ('+');
713             }
714           else
715             {
716               val = l->u.i - r->u.i;
717               if ((l->u.i < val) != (r->u.i < 0))
718                 integer_overflow ('-');
719             }
720         }
721       freev (l);
722       freev (r);
723       l = int_value (val);
724     }
725 }
726
727 /* Handle comparisons.  */
728
729 static VALUE *
730 eval2 (bool evaluate)
731 {
732   VALUE *l;
733
734 #ifdef EVAL_TRACE
735   trace ("eval2");
736 #endif
737   l = eval3 (evaluate);
738   while (1)
739     {
740       VALUE *r;
741       enum
742         {
743           less_than, less_equal, equal, not_equal, greater_equal, greater_than
744         } fxn;
745       bool val = false;
746
747       if (nextarg ("<"))
748         fxn = less_than;
749       else if (nextarg ("<="))
750         fxn = less_equal;
751       else if (nextarg ("=") || nextarg ("=="))
752         fxn = equal;
753       else if (nextarg ("!="))
754         fxn = not_equal;
755       else if (nextarg (">="))
756         fxn = greater_equal;
757       else if (nextarg (">"))
758         fxn = greater_than;
759       else
760         return l;
761       r = eval3 (evaluate);
762
763       if (evaluate)
764         {
765           int cmp;
766           tostring (l);
767           tostring (r);
768
769           if (looks_like_integer (l->u.s) && looks_like_integer (r->u.s))
770             cmp = strintcmp (l->u.s, r->u.s);
771           else
772             {
773               errno = 0;
774               cmp = strcoll (l->u.s, r->u.s);
775
776               if (errno)
777                 {
778                   error (0, errno, _("string comparison failed"));
779                   error (0, 0, _("set LC_ALL='C' to work around the problem"));
780                   error (EXPR_INVALID, 0,
781                          _("the strings compared were %s and %s"),
782                          quotearg_n_style (0, locale_quoting_style, l->u.s),
783                          quotearg_n_style (1, locale_quoting_style, r->u.s));
784                 }
785             }
786
787           switch (fxn)
788             {
789             case less_than:     val = (cmp <  0); break;
790             case less_equal:    val = (cmp <= 0); break;
791             case equal:         val = (cmp == 0); break;
792             case not_equal:     val = (cmp != 0); break;
793             case greater_equal: val = (cmp >= 0); break;
794             case greater_than:  val = (cmp >  0); break;
795             default: abort ();
796             }
797         }
798
799       freev (l);
800       freev (r);
801       l = int_value (val);
802     }
803 }
804
805 /* Handle &.  */
806
807 static VALUE *
808 eval1 (bool evaluate)
809 {
810   VALUE *l;
811   VALUE *r;
812
813 #ifdef EVAL_TRACE
814   trace ("eval1");
815 #endif
816   l = eval2 (evaluate);
817   while (1)
818     {
819       if (nextarg ("&"))
820         {
821           r = eval2 (evaluate & ~ null (l));
822           if (null (l) || null (r))
823             {
824               freev (l);
825               freev (r);
826               l = int_value (0);
827             }
828           else
829             freev (r);
830         }
831       else
832         return l;
833     }
834 }
835
836 /* Handle |.  */
837
838 static VALUE *
839 eval (bool evaluate)
840 {
841   VALUE *l;
842   VALUE *r;
843
844 #ifdef EVAL_TRACE
845   trace ("eval");
846 #endif
847   l = eval1 (evaluate);
848   while (1)
849     {
850       if (nextarg ("|"))
851         {
852           r = eval1 (evaluate & null (l));
853           if (null (l))
854             {
855               freev (l);
856               l = r;
857               if (null (l))
858                 {
859                   freev (l);
860                   l = int_value (0);
861                 }
862             }
863           else
864             freev (r);
865         }
866       else
867         return l;
868     }
869 }