1 /*
2  * aegis - project change supervisor
3  * Copyright (C) 2001-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 (at
9  * 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 GNU
14  * 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 <http://www.gnu.org/licenses/>.
18  */
19 
20 %define api.prefix {format_sccs_gram_}
21 %{
22 
23 #include <common/ac/assert.h>
24 #include <common/ac/stdio.h>
25 #include <common/ac/stdlib.h>
26 
27 #include <common/debug.h>
28 #include <common/gettime.h>
29 #include <common/itab.h>
30 #include <common/str_list.h>
31 #include <libaegis/help.h>
32 
33 #include <aeimport/format/sccs/gram.h>
34 #include <aeimport/format/sccs/lex.h>
35 #include <aeimport/format/version.h>
36 #include <aeimport/format/version_list.h>
37 
38 #ifdef DEBUG
39 #undef YYDEBUG
40 #define YYDEBUG 1
41 #endif
42 
43 %}
44 
45 %token COMMENT
46 %token DELTA_BEGIN
47 %token DELTA_END
48 %token D_KEYWORD
49 %token E_KEYWORD
50 %token FLAGS
51 %token HEADER
52 %token I_KEYWORD
53 %token JUNK
54 %token MR
55 %token MR_EXCLUDE
56 %token MR_IGNORE
57 %token MR_INCLUDE
58 %token STRING
59 %token SUMMARY
60 %token TEXTLINE
61 %token TITLE_BEGIN
62 %token TITLE_END
63 %token USERS_BEGIN
64 %token USERS_END
65 
66 %union {
67     string_ty       *lv_string;
68     long            lv_long;
69 }
70 
71 %type <lv_long> date_and_time number
72 %type <lv_string> COMMENT STRING TEXTLINE
73 
74 %{
75 
76 static format_version_ty *current;
77 static itab_ty  *itp;
78 static string_ty *pfn;
79 static string_ty *lfn;
80 
81 
82 static format_version_ty *
find(int n)83 find(int n)
84 {
85     format_version_ty *fvp;
86 
87     assert(itp);
88     fvp = (format_version_ty *)itab_query(itp, n);
89     if (!fvp)
90     {
91         fvp = new format_version_ty();
92         itab_assign(itp, n, fvp);
93         fvp->filename_physical = str_copy(pfn);
94         fvp->filename_logical = str_copy(lfn);
95     }
96     return fvp;
97 }
98 
99 
100 static int
count_the_dots(string_ty * s)101 count_the_dots(string_ty *s)
102 {
103     const char      *cp;
104     int             result;
105 
106     result = 0;
107     for (cp = s->str_text; *cp; ++cp)
108         if (*cp == '.')
109             ++result;
110     return result;
111 }
112 
113 
114 static void
walker(itab_ty *,itab_key_ty,void * data,void *)115 walker(itab_ty *, itab_key_ty, void *data, void *)
116 {
117     format_version_ty *fvp;
118     int             ndots;
119     size_t          j;
120 
121     fvp = (format_version_ty *)data;
122     if (fvp->after_branch)
123     {
124         ndots = count_the_dots(fvp->edit);
125         for (j = 0; j < fvp->after_branch->length; ++j)
126         {
127             format_version_ty *after;
128 
129             after = fvp->after_branch->item[j];
130             if (after->edit && count_the_dots(after->edit) <= ndots)
131             {
132                 fvp->after = after;
133                 if (j + 1 < fvp->after_branch->length)
134                 {
135                     fvp->after_branch->item[j] =
136                         fvp->after_branch->item[fvp->after_branch->length - 1];
137                 }
138                 fvp->after_branch->length--;
139                 if (fvp->after_branch->length == 0)
140                 {
141                     format_version_list_delete(fvp->after_branch, 0);
142                     fvp->after_branch = 0;
143                 }
144                 break;
145             }
146         }
147     }
148 }
149 
150 
151 format_version_ty *
sccs_parse(string_ty * physical,string_ty * logical)152 sccs_parse(string_ty *physical, string_ty *logical)
153 {
154     extern int yyparse(void);
155     format_version_ty *result;
156 
157     itp = itab_alloc();
158     pfn = physical;
159     lfn = logical;
160 
161     sccs_lex_open(physical);
162 #if YYDEBUG
163     { extern int yydebug; yydebug = 1; }
164 #endif
165     yyparse();
166     sccs_lex_close();
167 
168     itab_walk(itp, walker, 0);
169 
170     result = find(1);
171     itab_free(itp);
172     itp = 0;
173     return result;
174 }
175 
176 %}
177 
178 %%
179 
180 sccsfile
181     : header deltas users flags title body
182     ;
183 
184 header
185     : HEADER ignore_arguments
186     ;
187 
188 deltas
189     : delta
190     | deltas delta
191     ;
192 
193 delta
194     : summary delta_begin delta_lines delta_end
195     ;
196 
197 summary
198     : SUMMARY ignore_arguments
199     ;
200 
201 delta_begin
202     : DELTA_BEGIN STRING STRING date_and_time STRING number number
203         {
204             format_version_ty *before;
205 
206             /*
207              * $2 some kind of state info, we ignore it
208              * $3 sid
209              * $4 time_t
210              * $5 who
211              * $6 number of this delta
212              * $7 number of predecessor
213              */
214             before = $7 ? find($7) : 0;
215             str_free($2);
216             current = find($6);
217             current->edit = $3;
218             current->when = $4;
219             current->who = $5 ;
220             current->before = before;
221             if (before)
222             {
223                 if (!before->after_branch)
224                 {
225                     before->after_branch = format_version_list_new();
226                 }
227                 format_version_list_append(before->after_branch, current);
228             }
229         }
230     ;
231 
232 date_and_time
233     : STRING STRING
234         {
235             string_list_ty  sl;
236             string_ty       *s;
237             time_t          t;
238 
239             /*
240              * Convert from the emminently sensable YY/MM/DD
241              * into the hideous american MM/DD/YY
242              */
243             sl.split($1, "/");
244             str_free($1);
245             while (sl.nstrings < 3)
246                 sl.push_back(str_from_c(""));
247             s =
248                 str_format
249                 (
250                     "%s/%s/%s %s GMT",
251                     sl.string[1]->str_text,
252                     sl.string[2]->str_text,
253                     sl.string[0]->str_text,
254                     $2->str_text
255                 );
256             str_free($2);
257             t = date_scan(s->str_text);
258             if (t == (time_t)-1)
259                 fatal_date_unknown(s->str_text);
260             str_free(s);
261             $$ = t;
262         }
263     ;
264 
265 number
266     : STRING
267         {
268             char *cp = 0;
269             $$ = strtol($1->str_text, &cp, 10);
270             if ($1->str_length && *cp != 0)
271                 yyerror("number expected");
272             str_free($1);
273         }
274     ;
275 
276 delta_lines
277     : /* empty */
278     | delta_lines delta_line
279     ;
280 
281 delta_line
282     : comment
283     | mr
284     | included
285     | excluded
286     | ignored
287     | error
288     ;
289 
290 comment
291     : COMMENT
292         {
293             string_ty       *s;
294 
295             assert(current);
296             if (current)
297             {
298                 if (current->description)
299                 {
300                     s =
301                         str_format
302                         (
303                             "%s\n%s",
304                             current->description->str_text,
305                             $1->str_text
306                         );
307                     str_free(current->description);
308                     current->description = s;
309                 }
310                 else
311                     current->description = str_copy($1);
312             }
313             str_free($1);
314         }
315     ;
316 
317 mr
318     : MR ignore_arguments
319     ;
320 
321 included
322     : MR_INCLUDE ignore_arguments
323     ;
324 
325 excluded
326     : MR_EXCLUDE ignore_arguments
327     ;
328 
329 ignored
330     : MR_IGNORE ignore_arguments
331     ;
332 
333 delta_end
334     : DELTA_END ignore_arguments
335         {
336             assert(current);
337             if (!current->description)
338             {
339                 current->description = str_from_c("no description");
340             }
341             current = 0;
342         }
343     ;
344 
345 users
346     : users_start textlines_opt users_end
347     ;
348 
349 users_start
350     : USERS_BEGIN ignore_arguments
351     ;
352 
353 users_end
354     : USERS_END ignore_arguments
355     ;
356 
357 flags
358     : /* empty */
359     | flags flag
360     ;
361 
362 flag
363     : FLAGS ignore_arguments
364     ;
365 
366 ignore_arguments
367     : /* empty */
368     | ignore_arguments STRING
369         { str_free($2); }
370     ;
371 
372 title
373     : title_begin textlines_opt title_end
374     ;
375 
376 title_begin
377     : TITLE_BEGIN ignore_arguments
378     ;
379 
380 title_end
381     : TITLE_END ignore_arguments
382     ;
383 
384 body
385     : /* empty */
386     | body I_stuff
387     | body D_stuff
388     | body E_stuff
389     | body error
390     ;
391 
392 /* ^AI <n> */
393 I_stuff
394     : I_KEYWORD ignore_arguments textlines_opt
395     ;
396 
397 /* ^AD <n> */
398 D_stuff
399     : D_KEYWORD ignore_arguments textlines_opt
400     ;
401 
402 /* ^AE <n> */
403 E_stuff
404     : E_KEYWORD ignore_arguments textlines_opt
405     ;
406 
407 textlines_opt
408     : /* empty */
409     | textlines_opt TEXTLINE
410         { str_free($2); }
411     ;
412 
413 
414 /* vim: set ts=8 sw=4 et : */
415