1 /*
2  *      aegis - project change supervisor
3  *      Copyright (C) 2001-2006, 2008, 2012 Peter Miller
4  *      Copyright (C) 2020 Aryeh M. Friedman
5  *
6  *      This program is free software; you can redistribute it and/or modify
7  *      it under the terms of the GNU General Public License as published by
8  *      the Free Software Foundation; either version 3 of the License, or
9  *      (at your option) any later version.
10  *
11  *      This program is distributed in the hope that it will be useful,
12  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *      GNU General Public License for more details.
15  *
16  *      You should have received a copy of the GNU General Public License
17  *      along with this program. If not, see
18  *      <http://www.gnu.org/licenses/>.
19  */
20 
21 %define api.prefix {format_rcs_gram_}
22 %{
23 
24 #include <common/ac/assert.h>
25 #include <common/ac/stdlib.h>
26 
27 #include <common/gettime.h>
28 #include <common/str_list.h>
29 #include <common/symtab.h>
30 #include <libaegis/help.h>
31 
32 #include <aeimport/format/rcs/gram.h>
33 #include <aeimport/format/rcs/lex.h>
34 #include <aeimport/format/version_list.h>
35 
36 %}
37 
38 %token ACCESS
39 %token AUTHOR
40 %token BRANCH
41 %token COLON
42 %token COMMENT
43 %token DATE
44 %token DESC
45 %token EXPAND
46 %token HEAD
47 %token IDENTIFIER
48 %token JUNK
49 %token LOCKS
50 %token LOG
51 %token NEXT
52 %token NUMBER
53 %token SEMI
54 %token STATE
55 %token STRICT
56 %token STRING
57 %token SUFFIX
58 %token SYMBOLS
59 %token TEXT
60 
61 %union
62 {
63     string_ty       *lv_string;
64     string_list_ty  *lv_string_list;
65 }
66 
67 %type <lv_string> NUMBER STRING IDENTIFIER date author next log state
68 %type <lv_string_list> strings_opt branch branch_opt identifiers_opt numbers_opt
69 
70 %{
71 
72 static symtab_ty *stp;
73 static format_version_ty *head;
74 static string_ty *pfn;
75 static string_ty *lfn;
76 
77 
78 static format_version_ty *
ancestor(format_version_ty * fvp)79 ancestor(format_version_ty *fvp)
80 {
81     while (fvp->before)
82         fvp = fvp->before;
83     return fvp;
84 }
85 
86 
87 format_version_ty *
rcs_parse(string_ty * physical,string_ty * logical)88 rcs_parse(string_ty *physical, string_ty *logical)
89 {
90     extern int yyparse(void);
91 
92     head = 0;
93 
94     stp = new symtab_ty(5);
95     pfn = physical;
96     lfn = logical;
97 
98     rcs_lex_open(physical);
99     yyparse();
100     rcs_lex_close();
101 
102     delete stp;
103     stp = 0;
104     return ancestor(head);
105 }
106 
107 
108 static time_t
str2date(string_ty * s)109 str2date(string_ty *s)
110 {
111     string_list_ty sl;
112     sl.split(s, ".");
113     if (sl.nstrings != 6)
114     {
115         fatal_date_unknown(s->str_text);
116     }
117 
118     /*
119      * Turn it into a format that scan_date understands.
120      * (Probably not the fastest way to get is converted into a date.)
121      */
122     string_ty *s2 =
123         str_format
124         (
125             "%s/%s/%s %s:%s:%s GMT",
126             sl.string[1]->str_text,
127             sl.string[2]->str_text,
128             sl.string[0]->str_text,
129             sl.string[3]->str_text,
130             sl.string[4]->str_text,
131             sl.string[5]->str_text
132         );
133     time_t t = date_scan(s2->str_text);
134     str_free(s2);
135     if (t == (time_t)-1)
136     {
137         fatal_date_unknown(s->str_text);
138     }
139     return t;
140 }
141 
142 
143 static format_version_ty *
find(string_ty * edit)144 find(string_ty *edit)
145 {
146     format_version_ty *rp;
147 
148     assert(stp);
149     rp = (format_version_ty *)stp->query(edit);
150     if (!rp)
151     {
152         rp = new format_version_ty();
153         stp->assign(edit, rp);
154         rp->edit = str_copy(edit);
155         rp->filename_physical = str_copy(pfn);
156         rp->filename_logical = str_copy(lfn);
157     }
158     return rp;
159 }
160 
161 
162 static int
is_a_branch_version_number(string_ty * s)163 is_a_branch_version_number(string_ty *s)
164 {
165     const char *cp;
166     int count_the_dots;
167 
168     count_the_dots = 0;
169     for (cp = s->str_text; *cp; ++cp)
170     {
171         if (*cp == '.')
172         {
173             ++count_the_dots;
174             if (count_the_dots >= 2)
175                 return 1;
176         }
177     }
178     return 0;
179 }
180 
181 
182 %}
183 
184 %%
185 
186 file
187     : admin tree desc edits
188     ;
189 
190 admin
191     : head branch_opt suffix_opt access symbols locks strict_opt
192       comment_opt expand_opt
193         {
194             delete $2;
195         }
196     ;
197 
198 head
199     : HEAD NUMBER SEMI
200         {
201             head = find($2);
202             str_free($2);
203         }
204     ;
205 
206 branch
207     : BRANCH numbers_opt SEMI
208         { $$ = $2; }
209     ;
210 
211 branch_opt
212     : /* empty */
213         { $$ = new string_list_ty(); }
214     | branch
215         { $$ = $1; }
216     ;
217 
218 suffix
219     : SUFFIX IDENTIFIER SEMI
220         { str_free($2); }
221     | SUFFIX STRING SEMI
222         { str_free($2); }
223     ;
224 
225 suffix_opt
226     : /* empty */
227     | suffix
228     ;
229 
230 access
231     : ACCESS identifiers_opt SEMI
232         { delete $2; }
233     ;
234 
235 symbols
236     : SYMBOLS symbol_list SEMI
237     ;
238 
239 symbol_list
240     : /* empty */
241     | symbol_list symbol
242     ;
243 
244 symbol
245     : IDENTIFIER COLON NUMBER
246         {
247             format_version_ty *rp;
248 
249             rp = find($3);
250             rp->tag.push_back($1);
251 
252             str_free($1);
253             str_free($3);
254         }
255     ;
256 
257 locks
258     : LOCKS lock_list SEMI
259     ;
260 
261 lock_list
262     : /* empty */
263     | lock_list lock
264     ;
265 
266 lock
267     : IDENTIFIER COLON NUMBER
268         {
269             str_free($1);
270             str_free($3);
271         }
272     ;
273 
274 strict
275     : STRICT SEMI
276     ;
277 
278 strict_opt
279     : /* empty */
280     | strict
281     ;
282 
283 comment
284     : COMMENT STRING SEMI
285         { str_free($2); }
286     ;
287 
288 comment_opt
289     : /* empty */
290     | comment
291     ;
292 
293 expand
294     : EXPAND strings_opt SEMI
295         { delete $2; }
296     ;
297 
298 expand_opt
299     : /* empty */
300     | expand
301     ;
302 
303 identifiers_opt
304     : /* empty */
305         {
306             $$ = new string_list_ty();
307         }
308     | identifiers_opt IDENTIFIER
309         {
310             $$ = $1;
311             $$->push_back($2);
312             str_free($2);
313         }
314     ;
315 
316 strings_opt
317     : /* empty */
318         {
319             $$ = new string_list_ty();
320         }
321     | strings_opt STRING
322         {
323             $$ = $1;
324             $$->push_back($2);
325             str_free($2);
326         }
327     ;
328 
329 tree
330     : /* empty */
331     | tree delta
332     ;
333 
334 delta
335     : NUMBER date author state branch next
336         {
337             size_t      j;
338             format_version_ty *rp;
339             static string_ty *dead;
340 
341             rp = find($1);
342 
343             rp->when = str2date($2);
344             str_free($2);
345             rp->who = $3;
346 
347             /*
348              * The RCS manual says that
349              * "Exp" means experimental
350              * "Stab" means stable
351              * "Rel" means released
352              *
353              * CVS uses "dead" to indicate a file which has
354              * been deleted, and should not be checked out.
355              */
356             if (!dead)
357                 dead = str_from_c("dead");
358             rp->dead = str_equal($4, dead);
359             str_free($4);
360 
361             if ($5->nstrings && !rp->after_branch)
362                 rp->after_branch = format_version_list_new();
363             for (j = 0; j < $5->nstrings; ++j)
364             {
365                 format_version_ty *other;
366 
367                 other = find($5->string[j]);
368                 other->before = rp;
369                 format_version_list_append
370                 (
371                     rp->after_branch,
372                     other
373                 );
374             }
375             delete $5;
376 
377             if ($6)
378             {
379                 /*
380                  * The trunk is a roll-back lists, but
381                  * the branches are roll-forward lists.
382                  * (Sheesh!)
383                  */
384                 if (is_a_branch_version_number($1))
385                 {
386                     rp->after = find($6);
387                     rp->after->before = rp;
388                 }
389                 else
390                 {
391                     rp->before = find($6);
392                     rp->before->after = rp;
393                 }
394                 str_free($6);
395             }
396         }
397     ;
398 
399 date
400     : DATE NUMBER SEMI
401         { $$ = $2; }
402     ;
403 
404 author
405     : AUTHOR IDENTIFIER SEMI
406         { $$ = $2; }
407     ;
408 
409 state
410     : STATE IDENTIFIER SEMI
411         { $$ = $2; }
412     ;
413 
414 numbers_opt
415     : /* empty */
416         {
417             $$ = new string_list_ty();
418         }
419     | numbers_opt NUMBER
420         {
421             $$ = $1;
422             $$->push_back($2);
423             str_free($2);
424         }
425     ;
426 
427 next
428     : NEXT NUMBER SEMI
429         { $$ = $2; }
430     | NEXT SEMI
431         { $$ = 0; }
432     ;
433 
434 desc
435     : DESC STRING nosemi
436         { str_free($2); }
437     ;
438 
439 edits
440     : edit
441     | edits edit
442     ;
443 
444 edit
445     : NUMBER log text
446         {
447             format_version_ty *rp;
448 
449             rp = find($1);
450             rp->description = $2;
451         }
452     ;
453 
454 log
455     : LOG STRING nosemi
456         { $$ = $2; }
457     ;
458 
459 text
460     : TEXT STRING nosemi
461         { str_free($2); }
462     ;
463 
464 nosemi
465     : /* empty */
466         { rcs_lex_keyword_expected(); }
467     ;
468 
469 
470 /* vim: set ts=8 sw=4 et : */
471