1 /*
2  * aegis - project change supervisor
3  * Copyright (C) 1997-1999, 2001-2008, 2011, 2012 Peter Miller
4  * Copyright (C) 2007 Walter Franzini
5  * Copyright (C) 2020 Aryeh M. Friedman
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or (at
10  * your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 %define api.prefix {cmdline_}
22 %{
23 
24 #include <common/ac/assert.h>
25 #include <common/ac/stdio.h>
26 #include <common/ac/stdlib.h>
27 
28 #include <common/progname.h>
29 #include <common/quit.h>
30 #include <common/str_list.h>
31 #include <common/trace.h>
32 #include <libaegis/aer/value/boolean.h>
33 #include <libaegis/aer/value/error.h>
34 #include <libaegis/aer/value/integer.h>
35 #include <libaegis/aer/value/real.h>
36 #include <libaegis/aer/value/string.h>
37 #include <libaegis/arglex2.h>
38 #include <libaegis/change.h>
39 #include <libaegis/change/file.h>
40 #include <libaegis/gonzo.h>
41 #include <libaegis/help.h>
42 #include <libaegis/os.h>
43 #include <libaegis/project.h>
44 #include <libaegis/project/file.h>
45 #include <libaegis/sub.h>
46 #include <libaegis/user.h>
47 #include <libaegis/version.h>
48 
49 #include <aefind/cmdline.h>
50 #include <aefind/descend.h>
51 #include <aefind/function.h>
52 #include <aefind/function/execute.h>
53 #include <aefind/lex.h>
54 #include <aefind/shorthand/delete.h>
55 #include <aefind/shorthand/name.h>
56 #include <aefind/shorthand/path.h>
57 #include <aefind/shorthand/print.h>
58 #include <aefind/shorthand/stat.h>
59 #include <aefind/tree.h>
60 #include <aefind/tree/arithmetic.h>
61 #include <aefind/tree/bitwise.h>
62 #include <aefind/tree/constant.h>
63 #include <aefind/tree/list.h>
64 #include <aefind/tree/logical.h>
65 #include <aefind/tree/match.h>
66 #include <aefind/tree/now.h>
67 #include <aefind/tree/relative.h>
68 #include <aefind/tree/this.h>
69 
70 #ifdef DEBUG
71 #define YYDEBUG 1
72 #define YYERROR_VERBOSE 1
73 #endif
74 
75 %}
76 
77 %token AMIN
78 %token ANDAND
79 %token ATIME
80 %token BASELINE
81 %token BASE_REL
82 %token BIT_AND
83 %token BIT_OR
84 %token BIT_XOR
85 %token BRANCH
86 %token CHANGE
87 %token CMIN
88 %token COLON
89 %token COMMA
90 %token CTIME
91 %token CUR_REL
92 %token DEBUG_keyword
93 %token DELETE
94 %token DEVDIR
95 %token DIV
96 %token EQ
97 %token EXECUTE
98 %token FALSE_keyword
99 %token GE
100 %token GRANDPARENT
101 %token GT
102 %token HELP
103 %token JOIN
104 %token JUNK
105 %token LE
106 %token LIBRARY
107 %token LPAREN
108 %token LT
109 %token MINUS
110 %token MMIN
111 %token MOD
112 %token MTIME
113 %token MUL
114 %token NAME
115 %token NE
116 %token NEWER
117 %token NOT
118 %token NOW
119 %token NUMBER
120 %token OROR
121 %token PATH
122 %token PERM
123 %token PLUS
124 %token PRINT
125 %token PROJECT
126 %token QUESTION
127 %token REAL
128 %token RESOLVE
129 %token RESOLVE_NOT
130 %token RPAREN
131 %token SEMICOLON
132 %token SHIFT_LEFT
133 %token SHIFT_RIGHT
134 %token SSIZE
135 %token STRING
136 %token STRINGIZE
137 %token THIS
138 %token TILDE
139 %token TRACE
140 %token TRUE_keyword
141 %token TRUNK
142 %token TYPE
143 %token VERSION
144 
145 %union
146 {
147     struct string_ty *lv_string;
148     struct string_list_ty *lv_string_list;
149     tree::pointer *lv_tree;
150     class tree_list *lv_tree_list;
151     long lv_number;
152     double lv_real;
153     diadic_t comparator;
154 }
155 
156 %type <comparator>  comparator
157 %type <lv_number>   NUMBER THIS
158 %type <lv_real>     REAL
159 %type <lv_string>   STRING
160 %type <lv_string>   number_or_string
161 %type <lv_string_list> strings strings_or_dot
162 %type <lv_tree>     tree1 tree2 tree3 tree4 tree5 tree6 tree7
163 %type <lv_tree>     tree8 tree9 tree10 tree11 tree12 tree13 tree14
164 %type <lv_tree_list> list
165 %type <lv_tree_list> list_opt
166 %type <lv_tree>     exec_list
167 
168 %left COMMA
169 %right QUESTION COLON
170 %left OROR
171 %left ANDAND
172 %left BIT_OR
173 %left BIT_XOR
174 %left BIT_AND
175 %left LT LE GT GE
176 %left SHIFT_LEFT SHIFT_RIGHT
177 %left PLUS MINUS JOIN
178 %left MUL DIV MOD TILDE
179 %right NOT unary
180 %right LPAREN RPAREN
181 
182 %{
183 
184 static int      number_of_ops;
185 static string_list_ty *path;
186 static tree::pointer filter;
187 static string_ty *project_name;
188 static long     change_number;
189 static int      grandparent;
190 static string_ty *branch;
191 static int      trunk;
192 static int      baseline;
193 static int      resolve;
194 static int      debug;
195 
196 
197 static void
report_error(const rpt_value::pointer & vp)198 report_error(const rpt_value::pointer &vp)
199 {
200     trace(("%s\n", __PRETTY_FUNCTION__));
201     const rpt_value_error *rve =
202         dynamic_cast<const rpt_value_error *>(vp.get());
203     if (!rve)
204         return;
205     sub_context_ty sc;
206     sc.var_set_string("MeSsaGe", rve->query());
207     sc.fatal_intl(i18n("$message"));
208     /* NOTREACHED */
209 }
210 
211 
212 static void
walker(void *,descend_message_ty msg,string_ty * path_unres,string_ty * path_maybe,string_ty * path_res,struct stat * st)213 walker(void *, descend_message_ty msg, string_ty *path_unres,
214     string_ty *path_maybe, string_ty *path_res, struct stat *st)
215 {
216     trace(("%s\n", __PRETTY_FUNCTION__));
217     switch (msg)
218     {
219     case descend_message_file:
220     case descend_message_dir_before:
221         {
222             rpt_value::pointer vp =
223                 filter->evaluate(path_unres, path_maybe, path_res, st);
224             if (vp->is_an_error())
225                 report_error(vp);
226         }
227         break;
228 
229     case descend_message_dir_after:
230         break;
231     }
232 }
233 
234 
235 static string_list_ty *stack;
236 static project *pp;
237 static change::pointer cp;
238 
239 
240 string_ty *
stack_relative(string_ty * fn)241 stack_relative(string_ty *fn)
242 {
243     trace(("%s\n", __PRETTY_FUNCTION__));
244     assert(stack);
245     os_become_orig();
246     string_ty *s1 = os_pathname(fn, 1);
247     os_become_undo();
248 
249     string_ty *s2 = 0;
250     for (size_t k = 0; k < stack->nstrings; ++k)
251     {
252         s2 = os_below_dir(stack->string[k], s1);
253         if (s2)
254             break;
255     }
256     str_free(s1);
257 
258     if (!s2)
259     {
260         sub_context_ty sc;
261         sc.var_set_string("File_Name", s1);
262         if (cp)
263             change_fatal(cp, &sc, i18n("$filename unrelated"));
264         project_fatal(pp, &sc, i18n("$filename unrelated"));
265         /* NOTREACHED */
266     }
267 
268     if (s2->str_length == 0)
269     {
270         str_free(s2);
271         s2 = str_from_c(".");
272     }
273 
274     return s2;
275 }
276 
277 
278 string_ty *
stack_nth(int n)279 stack_nth(int n)
280 {
281     trace(("%s\n", __PRETTY_FUNCTION__));
282     assert(n >= 0);
283     assert(stack);
284     assert(stack->nstrings);
285     if (!stack)
286         return 0;
287     if (n < 0 || n >= (int)stack->nstrings)
288         return 0;
289     return stack->string[n];
290 }
291 
292 
293 int
stack_eliminate(string_ty * filename)294 stack_eliminate(string_ty *filename)
295 {
296     trace(("%s\n", __PRETTY_FUNCTION__));
297     fstate_src_ty *src = pp->file_find(filename, view_path_simple);
298     if (!src)
299         return 0;
300     switch (src->action)
301     {
302     case file_action_create:
303     case file_action_modify:
304         break;
305 
306     case file_action_remove:
307         return 1;
308 
309     case file_action_insulate:
310     case file_action_transparent:
311         break;
312     }
313     return 0;
314 }
315 
316 
317 void
cmdline_grammar(int argc,char ** argv)318 cmdline_grammar(int argc, char **argv)
319 {
320     trace(("%s\n", __PRETTY_FUNCTION__));
321     extern int yyparse(void);
322     size_t          j;
323     cstate_ty       *cstate_data;
324     int             based;
325 
326     /*
327      * parse the command line
328      */
329     cmdline_lex_open(argc, argv);
330     number_of_ops = 0;
331     resolve = -1;
332     yyparse();
333     cmdline_lex_close();
334 
335     /*
336      * reject illegal combinations of options
337      */
338     if (grandparent)
339     {
340         if (branch)
341         {
342             mutually_exclusive_options
343             (
344                 arglex_token_branch,
345                 arglex_token_grandparent,
346                 usage
347             );
348         }
349         if (trunk)
350         {
351             mutually_exclusive_options
352             (
353                 arglex_token_trunk,
354                 arglex_token_grandparent,
355                 usage
356             );
357         }
358         branch = str_from_c("..");
359     }
360     if (trunk)
361     {
362         if (branch)
363         {
364             mutually_exclusive_options
365             (
366                 arglex_token_branch,
367                 arglex_token_trunk,
368                 usage
369             );
370         }
371         branch = str_from_c("");
372     }
373 
374     /*
375      * locate project data
376      */
377     if (!project_name)
378     {
379         nstring n = user_ty::create()->default_project();
380         project_name = str_copy(n.get_ref());
381     }
382     pp = project_alloc(project_name);
383     str_free(project_name);
384     pp->bind_existing();
385 
386     stack = new string_list_ty();
387     user_ty::pointer up = user_ty::create();
388     if (baseline)
389     {
390         if (change_number)
391         {
392             mutually_exclusive_options
393             (
394                 arglex_token_branch,
395                 arglex_token_change,
396                 usage
397             );
398         }
399 
400         /*
401          * Get the search path from the project.
402          */
403         pp->search_path_get(stack, true);
404 
405         cp = 0;
406         cstate_data = 0;
407     }
408     else
409     {
410         /*
411          * locate change data
412          */
413         if (!change_number)
414             change_number = up->default_change(pp);
415         cp = change_alloc(pp, change_number);
416         change_bind_existing(cp);
417         cstate_data = cp->cstate_get();
418 
419         if (cstate_data->state == cstate_state_completed)
420         {
421             /*
422              * Get the search path from the project.
423              */
424             pp->search_path_get(stack, true);
425 
426             cp = 0;
427             cstate_data = 0;
428         }
429         else
430         {
431             /*
432              * It is an error if the change is not in the
433              * being_developed state (if it does not have a
434              * directory).
435              */
436             if (cstate_data->state < cstate_state_being_developed)
437                 change_fatal(cp, 0, i18n("bad aefind state"));
438 
439             /*
440              * Get the search path from the change.
441              */
442             cp->search_path_get(stack, true);
443         }
444     }
445 
446     /*
447      * resolve the path of each path
448      * 1. the absolute path of the file name is obtained
449      * 2. if the file is inside the development directory, ok
450      * 3. if the file is inside the baseline, ok
451      * 4. if neither, error
452      */
453     assert(up);
454     based =
455         (
456             stack->nstrings >= 1
457         &&
458             (
459                 up->relative_filename_preference
460                 (
461                     uconf_relative_filename_preference_current
462                 )
463             ==
464                 uconf_relative_filename_preference_base
465             )
466         );
467     for (j = 0; j < path->nstrings; ++j)
468     {
469         string_ty       *s0;
470         string_ty       *s1;
471         string_ty       *s2;
472 
473         s0 = path->string[j];
474         if (s0->str_text[0] == '/' || !based)
475             s1 = str_copy(s0);
476         else
477             s1 = os_path_join(stack->string[0], s0);
478         str_free(s0);
479         s2 = stack_relative(s1);
480         assert(s2);
481         str_free(s1);
482         path->string[j] = s2;
483     }
484 
485     /*
486      * Optimize the tree.
487      *
488      * We can't do this as we go, because we don't know the search
489      * path until we finish parsing the command line.
490      */
491     if (debug > 1)
492     {
493         filter->print();
494         printf("\n");
495         fflush(stdout);
496     }
497     filter = filter->optimize();
498 
499     /*
500      * walk each of the directories in turn
501      */
502     if (debug)
503     {
504         filter->print();
505         printf("\n");
506         fflush(stdout);
507     }
508     for (j = 0; j < path->nstrings; ++j)
509         descend(path->string[j], resolve, walker, 0);
510 
511     /*
512      * None of the stuff is deleted.
513      * Assume program exits shortly.
514      */
515 }
516 
517 
518 static tree::pointer
make_sure_has_side_effects(const tree::pointer & x)519 make_sure_has_side_effects(const tree::pointer &x)
520 {
521     trace(("%s\n", __PRETTY_FUNCTION__));
522     if (x->useful())
523         return x;
524 
525 #if 0
526     /*
527      * Most of the time, this is exactly what users want, a file list.
528      * So we don't whine at them.
529      */
530     error_intl
531     (
532         0,
533       i18n("warning: expression has no side effects, assuming you meant -PRint")
534     );
535 #endif
536 
537     return tree_and::create(x, shorthand_print());
538 }
539 
540 %}
541 
542 %%
543 
544 find
545     : HELP
546         {
547             help(0, usage);
548             quit(0);
549         }
550     | generic_options op generic_options
551     ;
552 
553 op
554     : strings_or_dot tree14
555         {
556             path = $1;
557             filter = make_sure_has_side_effects(*$2);
558             delete $2;
559         }
560     ;
561 
562 strings_or_dot
563     : strings
564     | /* empty */
565         {
566             string_ty       *dot;
567 
568             /*
569              * Default the path list to "." (the current directory).
570              */
571             $$ = new string_list_ty();
572             dot = str_from_c(".");
573             $$->push_back(dot);
574             str_free(dot);
575         }
576     ;
577 
578 strings
579     : STRING
580         {
581             $$ = new string_list_ty();
582             $$->push_back($1);
583             str_free($1);
584         }
585     | strings STRING
586         {
587             $$ = $1;
588             $$->push_back($2);
589             str_free($2);
590         }
591     ;
592 
593 /*
594  * The fundamental building blocks of expressions.
595  */
596 tree1
597     : THIS
598         {
599             tree::pointer tp = tree_this::create($1);
600             $$ = new tree::pointer(tp);
601         }
602     | NOW
603         {
604             tree::pointer tp = tree_now_new();
605             $$ = new tree::pointer(tp);
606         }
607     | STRING
608         %prec LPAREN
609         {
610             rpt_value::pointer vp = rpt_value_string::create(nstring($1));
611             str_free($1);
612             tree::pointer tp = tree_constant::create(vp);
613             $$ = new tree::pointer(tp);
614         }
615     | TRUE_keyword
616         {
617             rpt_value::pointer vp = rpt_value_boolean::create(true);
618             tree::pointer tp = tree_constant::create(vp);
619             $$ = new tree::pointer(tp);
620         }
621     | FALSE_keyword
622         {
623             rpt_value::pointer vp = rpt_value_boolean::create(false);
624             tree::pointer tp = tree_constant::create(vp);
625             $$ = new tree::pointer(tp);
626         }
627     | NUMBER
628         {
629             rpt_value::pointer vp = rpt_value_integer::create($1);
630             tree::pointer tp = tree_constant::create(vp);
631             $$ = new tree::pointer(tp);
632         }
633     | REAL
634         {
635             rpt_value::pointer vp = rpt_value_real::create($1);
636             tree::pointer tp = tree_constant::create(vp);
637             $$ = new tree::pointer(tp);
638         }
639     | LPAREN tree14 RPAREN
640         {
641             $$ = $2;
642         }
643     ;
644 
645 /*
646  * The simple tests and actions.
647  */
648 tree1
649     : PRINT
650         {
651             tree::pointer tp = shorthand_print();
652             $$ = new tree::pointer(tp);
653         }
654     | DELETE
655         {
656             tree::pointer tp = shorthand_delete();
657             $$ = new tree::pointer(tp);
658         }
659     | EXECUTE exec_list SEMICOLON
660         {
661             tree::pointer tp = tree_execute::create(*$2);
662             delete $2;
663             $$ = new tree::pointer(tp);
664         }
665     | NAME STRING
666         {
667             tree::pointer tp = shorthand_name(nstring($2));
668             str_free($2);
669             $$ = new tree::pointer(tp);
670         }
671     | PATH STRING
672         {
673             tree::pointer tp = shorthand_path(nstring($2));
674             str_free($2);
675             $$ = new tree::pointer(tp);
676         }
677     | TYPE STRING
678         {
679             tree::pointer tp = shorthand_type(nstring($2));
680             str_free($2);
681             $$ = new tree::pointer(tp);
682         }
683     ;
684 
685 /*
686  * These next few deal with comparing various inode aspects.  They are
687  * all shorthand for various function invocations and a comparison.
688  */
689 tree1
690     : NEWER STRING
691         {
692             tree::pointer tp = shorthand_newer(nstring($2));
693             str_free($2);
694             $$ = new tree::pointer(tp);
695         }
696     | AMIN comparator NUMBER
697         {
698             tree::pointer tp = shorthand_atime($2, $3, 60);
699             $$ = new tree::pointer(tp);
700         }
701     | ATIME comparator NUMBER
702         {
703             tree::pointer tp = shorthand_atime($2, $3, 24*60*60);
704             $$ = new tree::pointer(tp);
705         }
706     | CMIN comparator NUMBER
707         {
708             tree::pointer tp = shorthand_ctime($2, $3, 60);
709             $$ = new tree::pointer(tp);
710         }
711     | CTIME comparator NUMBER
712         {
713             tree::pointer tp = shorthand_ctime($2, $3, 24*60*60);
714             $$ = new tree::pointer(tp);
715         }
716     | MMIN comparator NUMBER
717         {
718             tree::pointer tp = shorthand_mtime($2, $3, 60);
719             $$ = new tree::pointer(tp);
720         }
721     | MTIME comparator NUMBER
722         {
723             tree::pointer tp = shorthand_mtime($2, $3, 24*60*60);
724             $$ = new tree::pointer(tp);
725         }
726     | SSIZE comparator NUMBER
727         {
728             tree::pointer tp = shorthand_size($2, $3);
729             $$ = new tree::pointer(tp);
730         }
731     ;
732 
733 comparator
734     : /* empty */
735         { $$ = &tree_eq::create; }
736     | EQ
737         { $$ = &tree_eq::create; }
738     | NE
739         { $$ = &tree_ne::create; }
740     | LT
741         { $$ = &tree_lt::create; }
742     | LE
743         { $$ = &tree_le::create; }
744     | GT
745         { $$ = &tree_gt::create; }
746     | GE
747         { $$ = &tree_ge::create; }
748     ;
749 
750 tree1
751     : STRING LPAREN list_opt RPAREN
752         {
753             tree::pointer tp = function_indirection($1, *$3);
754             str_free($1);
755             delete $3;
756             $$ = new tree::pointer(tp);
757         }
758     ;
759 
760 list_opt
761     : /* empty */
762         {
763             $$ = new tree_list();
764         }
765     | list
766         {
767             $$ = $1;
768         }
769     ;
770 
771 list
772     : tree13
773         {
774             $$ = new tree_list();
775             $$->append(*$1);
776             delete $1;
777         }
778     | list COMMA tree13
779         {
780             $$ = $1;
781             $$->append(*$3);
782             delete $3;
783         }
784     ;
785 
786 exec_list
787     : tree10
788         {
789             $$ = $1;
790         }
791     | exec_list tree10
792         %prec ANDAND
793         {
794             static tree::pointer t1;
795             if (!t1)
796             {
797                 rpt_value::pointer vp = rpt_value_string::create(" ");
798                 t1 = tree_constant::create(vp);
799             }
800 
801             tree::pointer t2 = tree_join::create(*$1, t1);
802             delete $1;
803             tree::pointer tp = tree_join::create(t2, *$2);
804             delete $2;
805             $$ = new tree::pointer(tp);
806         }
807     ;
808 
809 tree2
810     : tree1
811         {
812             $$ = $1;
813         }
814     | NOT tree2
815         {
816             tree::pointer tp = tree_not::create(*$2);
817             delete $2;
818             $$ = new tree::pointer(tp);
819         }
820     | PLUS tree2
821         %prec unary
822         {
823             tree::pointer tp = tree_pos::create(*$2);
824             delete $2;
825             $$ = new tree::pointer(tp);
826         }
827     | MINUS tree2
828         %prec unary
829         {
830             tree::pointer tp = tree_neg::create(*$2);
831             delete $2;
832             $$ = new tree::pointer(tp);
833         }
834     | TILDE tree2
835         %prec unary
836         {
837             tree::pointer tp = tree_bitwise_not::create(*$2);
838             delete $2;
839             $$ = new tree::pointer(tp);
840         }
841     ;
842 
843 tree3
844     : tree2
845         {
846             $$ = $1;
847         }
848     | tree3 MUL tree2
849         {
850             tree::pointer tp = tree_mul::create(*$1, *$3);
851             delete $1;
852             delete $3;
853             $$ = new tree::pointer(tp);
854         }
855     | tree3 DIV tree2
856         {
857             tree::pointer tp = tree_divide::create(*$1, *$3);
858             delete $1;
859             delete $3;
860             $$ = new tree::pointer(tp);
861         }
862     | tree3 MOD tree2
863         {
864             tree::pointer tp = tree_mod::create(*$1, *$3);
865             delete $1;
866             delete $3;
867             $$ = new tree::pointer(tp);
868         }
869     | tree3 TILDE tree2
870         {
871             tree::pointer tp = tree_match::create(*$1, *$3);
872             delete $1;
873             delete $3;
874             $$ = new tree::pointer(tp);
875         }
876     ;
877 
878 tree4
879     : tree3
880         {
881             $$ = $1;
882         }
883     | tree4 PLUS tree3
884         {
885             tree::pointer tp = tree_plus::create(*$1, *$3);
886             delete $1;
887             delete $3;
888             $$ = new tree::pointer(tp);
889         }
890     | tree4 MINUS tree3
891         {
892             tree::pointer tp = tree_subtract::create(*$1, *$3);
893             delete $1;
894             delete $3;
895             $$ = new tree::pointer(tp);
896         }
897     | tree4 JOIN tree3
898         {
899             tree::pointer tp = tree_join::create(*$1, *$3);
900             delete $1;
901             delete $3;
902             $$ = new tree::pointer(tp);
903         }
904     ;
905 
906 tree5
907     : tree4
908         {
909             $$ = $1;
910         }
911     | tree5 SHIFT_LEFT tree4
912         {
913             tree::pointer tp = tree_shift_left::create(*$1, *$3);
914             delete $1;
915             delete $3;
916             $$ = new tree::pointer(tp);
917         }
918     | tree5 SHIFT_RIGHT tree4
919         {
920             tree::pointer tp = tree_shift_right::create(*$1, *$3);
921             delete $1;
922             delete $3;
923             $$ = new tree::pointer(tp);
924         }
925     ;
926 
927 tree6
928     : tree5
929         {
930             $$ = $1;
931         }
932     | tree6 LT tree5
933         {
934             tree::pointer tp = tree_lt::create(*$1, *$3);
935             delete $1;
936             delete $3;
937             $$ = new tree::pointer(tp);
938         }
939     | tree6 LE tree5
940         {
941             tree::pointer tp = tree_le::create(*$1, *$3);
942             delete $1;
943             delete $3;
944             $$ = new tree::pointer(tp);
945         }
946     | tree6 GT tree5
947         {
948             tree::pointer tp = tree_gt::create(*$1, *$3);
949             delete $1;
950             delete $3;
951             $$ = new tree::pointer(tp);
952         }
953     | tree6 GE tree5
954         {
955             tree::pointer tp = tree_ge::create(*$1, *$3);
956             delete $1;
957             delete $3;
958             $$ = new tree::pointer(tp);
959         }
960     ;
961 
962 tree7
963     : tree6
964         {
965             $$ = $1;
966         }
967     | tree7 EQ tree6
968         {
969             tree::pointer tp = tree_eq::create(*$1, *$3);
970             delete $1;
971             delete $3;
972             $$ = new tree::pointer(tp);
973         }
974     | tree7 NE tree6
975         {
976             tree::pointer tp = tree_ne::create(*$1, *$3);
977             delete $1;
978             delete $3;
979             $$ = new tree::pointer(tp);
980         }
981     ;
982 
983 tree8
984     : tree7
985         {
986             $$ = $1;
987         }
988     | tree8 BIT_AND tree7
989         {
990             tree::pointer tp = tree_bitwise_and::create(*$1, *$3);
991             delete $1;
992             delete $3;
993             $$ = new tree::pointer(tp);
994         }
995     ;
996 
997 tree9
998     : tree8
999         {
1000             $$ = $1;
1001         }
1002     | tree9 BIT_XOR tree8
1003         {
1004             tree::pointer tp = tree_bitwise_xor::create(*$1, *$3);
1005             delete $1;
1006             delete $3;
1007             $$ = new tree::pointer(tp);
1008         }
1009     ;
1010 
1011 tree10
1012     : tree9
1013         {
1014             $$ = $1;
1015         }
1016     | tree10 BIT_OR tree9
1017         {
1018             tree::pointer tp = tree_bitwise_or::create(*$1, *$3);
1019             delete $1;
1020             delete $3;
1021             $$ = new tree::pointer(tp);
1022         }
1023     ;
1024 
1025 tree11
1026     : tree10
1027         {
1028             $$ = $1;
1029         }
1030     | tree11 ANDAND tree10
1031         {
1032             tree::pointer tp = tree_and::create(*$1, *$3);
1033             delete $1;
1034             delete $3;
1035             $$ = new tree::pointer(tp);
1036         }
1037     | tree11 tree10
1038         %prec ANDAND
1039         {
1040             tree::pointer tp = tree_and::create(*$1, *$2);
1041             delete $1;
1042             delete $2;
1043             $$ = new tree::pointer(tp);
1044         }
1045     ;
1046 
1047 tree12
1048     : tree11
1049         {
1050             $$ = $1;
1051         }
1052     | tree12 OROR tree11
1053         {
1054             tree::pointer tp = tree_or::create(*$1, *$3);
1055             delete $1;
1056             delete $3;
1057             $$ = new tree::pointer(tp);
1058         }
1059     ;
1060 
1061 tree13
1062     : tree12
1063         {
1064             $$ = $1;
1065         }
1066     | tree13 QUESTION tree13 COLON tree12
1067         %prec QUESTION
1068         {
1069             tree::pointer tp = tree_triadic::create(*$1, *$3, *$5);
1070             delete $1;
1071             delete $3;
1072             delete $5;
1073             $$ = new tree::pointer(tp);
1074         }
1075     ;
1076 
1077 tree14
1078     : tree13
1079         {
1080             $$ = $1;
1081         }
1082     | tree14 COMMA tree13
1083         {
1084             tree::pointer tp2 = make_sure_has_side_effects(*$1);
1085             delete $1;
1086             tree::pointer tp = tree_comma::create(tp2, *$3);
1087             delete $3;
1088             $$ = new tree::pointer(tp);
1089         }
1090     ;
1091 
1092 generic_options
1093     : /* empty */
1094     | generic_options generic_option
1095     ;
1096 
1097 generic_option
1098     : LIBRARY STRING
1099         {
1100             gonzo_library_append($2->str_text);
1101             str_free($2);
1102         }
1103     | RESOLVE
1104         {
1105             if (resolve > 0)
1106             {
1107                 duplicate_option_by_name(arglex_token_resolve, usage);
1108             }
1109             if (resolve >= 0)
1110             {
1111                 mutually_exclusive_options
1112                 (
1113                     arglex_token_resolve,
1114                     arglex_token_resolve_not,
1115                     usage
1116                 );
1117             }
1118             resolve = 1;
1119         }
1120     | RESOLVE_NOT
1121         {
1122             if (resolve == 0)
1123             {
1124                 duplicate_option_by_name(arglex_token_resolve_not, usage);
1125             }
1126             if (resolve >= 0)
1127             {
1128                 mutually_exclusive_options
1129                 (
1130                     arglex_token_resolve,
1131                     arglex_token_resolve_not,
1132                     usage
1133                 );
1134             }
1135             resolve = 0;
1136         }
1137     | TRACE trace_strings
1138         {
1139 #ifndef DEBUG
1140             error_intl(0, i18n("-TRace needs DEBUG"));
1141 #endif
1142         }
1143     | VERSION
1144         {
1145             version();
1146             quit(0);
1147         }
1148     ;
1149 
1150 trace_strings
1151     : trace_string
1152     | trace_strings trace_string
1153     ;
1154 
1155 trace_string
1156     : STRING
1157         {
1158 #ifdef DEBUG
1159             trace_enable(arglex_value.alv_string);
1160             yydebug = trace_pretest_;
1161 #endif
1162         }
1163     ;
1164 
1165 generic_option
1166     : BASELINE
1167         {
1168             if (baseline)
1169             {
1170                 duplicate_option_by_name(arglex_token_baseline, usage);
1171             }
1172             baseline = 1;
1173         }
1174     | CHANGE NUMBER
1175         {
1176             if (change_number)
1177             {
1178                 duplicate_option_by_name(arglex_token_change, usage);
1179             }
1180             change_number = $2;
1181             if (change_number == 0)
1182                 change_number = MAGIC_ZERO;
1183             else if (change_number < 1)
1184             {
1185                 sub_context_ty  *scp;
1186 
1187                 scp = sub_context_new();
1188                 sub_var_set_long(scp, "Number", change_number);
1189                 fatal_intl(scp, i18n("change $number out of range"));
1190                 /* NOTREACHED */
1191                 sub_context_delete(scp);
1192             }
1193         }
1194     | PROJECT STRING
1195         {
1196             if (project_name)
1197                 duplicate_option_by_name(arglex_token_project, usage);
1198             project_name = str_from_c(arglex_value.alv_string);
1199         }
1200     | BRANCH number_or_string
1201         {
1202             if (branch)
1203                 duplicate_option(usage);
1204             branch = $2;
1205         }
1206     | TRUNK
1207         {
1208             if (trunk)
1209                 duplicate_option(usage);
1210             ++trunk;
1211         }
1212     | GRANDPARENT
1213         {
1214             if (grandparent)
1215                 duplicate_option(usage);
1216             ++grandparent;
1217         }
1218     | DEBUG_keyword
1219         {
1220             ++debug;
1221         }
1222     | BASE_REL
1223         {
1224             user_ty::relative_filename_preference_argument(usage);
1225         }
1226     | CUR_REL
1227         {
1228             user_ty::relative_filename_preference_argument(usage);
1229         }
1230     ;
1231 
1232 number_or_string
1233     : NUMBER
1234         {
1235             $$ = str_format("%ld", $1);
1236         }
1237     | STRING
1238         {
1239             $$ = $1;
1240         }
1241     ;
1242 
1243 
1244 // vim: set ts=8 sw=4 et :
1245