1 
2 /**
3  * @file tpParse.c
4  *
5  *  This module will load a template and return a template structure.
6  *
7  * @addtogroup autogen
8  * @{
9  */
10 /*
11  * This file is part of AutoGen.
12  * Copyright (C) 1992-2018 Bruce Korb - all rights reserved
13  *
14  * AutoGen is free software: you can redistribute it and/or modify it
15  * under the terms of the GNU General Public License as published by the
16  * Free Software Foundation, either version 3 of the License, or
17  * (at your option) any later version.
18  *
19  * AutoGen is distributed in the hope that it will be useful, but
20  * WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
22  * See the GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License along
25  * with this program.  If not, see <http://www.gnu.org/licenses/>.
26  */
27 
28 #if defined(DEBUG_ENABLED)
29  static char const zTUndef[] = "%-10s (%d) line %d - MARKER\n";
30 
31  static int tpl_nest_lvl = 0;
32 
33  static char const tpl_def_fmt[] = "%-10s (%d) line %d end=%d, strlen=%d\n";
34 #endif
35 
36 /**
37  *  Return the enumerated function type corresponding
38  *  to a name pointed to by the input argument.
39  */
40 static mac_func_t
func_code(char const ** pscan)41 func_code(char const ** pscan)
42 {
43     fn_name_type_t const * pNT;
44     char const *      pzFuncName = *pscan;
45     int               hi, lo, av;
46     int               cmp;
47 
48     /*
49      *  IF the name starts with a punctuation, then it is some sort of
50      *  alias.  Find the function in the alias portion of the table.
51      */
52     if (IS_PUNCTUATION_CHAR(*pzFuncName)) {
53         hi = FUNC_ALIAS_HIGH_INDEX;
54         lo = FUNC_ALIAS_LOW_INDEX;
55         do  {
56             av  = (hi + lo)/2;
57             pNT = fn_name_types + av;
58             cmp = (int)(*(pNT->pName)) - (int)(*pzFuncName);
59 
60             /*
61              *  For strings that start with a punctuation, we
62              *  do not need to test for the end of token
63              *  We will not strip off the marker and the load function
64              *  will figure out what to do with the code.
65              */
66             if (cmp == 0)
67                 return pNT->fType;
68             if (cmp > 0)
69                  hi = av - 1;
70             else lo = av + 1;
71         } while (hi >= lo);
72         return FTYP_BOGUS;
73     }
74 
75     if (! IS_VAR_FIRST_CHAR(*pzFuncName))
76         return FTYP_BOGUS;
77 
78     hi = FUNC_NAMES_HIGH_INDEX;
79     lo = FUNC_NAMES_LOW_INDEX;
80 
81     do  {
82         av  = (hi + lo)/2;
83         pNT = fn_name_types + av;
84         cmp = strneqvcmp(pNT->pName, pzFuncName, (int)pNT->cmpLen);
85         if (cmp == 0) {
86             /*
87              *  Make sure we matched to the end of the token.
88              */
89             if (IS_VARIABLE_NAME_CHAR(pzFuncName[pNT->cmpLen]))
90                 break;
91 
92             /*
93              *  Advance the scanner past the macro name.
94              *  The name is encoded in the "fType".
95              */
96             *pscan = pzFuncName + pNT->cmpLen;
97             return pNT->fType;
98         }
99         if (cmp > 0)
100              hi = av - 1;
101         else lo = av + 1;
102     } while (hi >= lo);
103 
104     /*
105      *  Save the name for later lookup
106      */
107     cur_macro->md_name_off =
108         (size_t)(current_tpl->td_scan - current_tpl->td_text);
109     {
110         char * pzCopy = current_tpl->td_scan;
111         char * pe = SPN_VALUE_NAME_CHARS(pzFuncName);
112         size_t l  = (size_t)(pe - pzFuncName);
113         memcpy(pzCopy, pzFuncName, l);
114         pzCopy     += l;
115         pzFuncName += l;
116 
117         /*
118          *  Names are allowed to contain colons, but not end with them.
119          */
120         if (pzCopy[-1] == ':')
121             pzCopy--, pzFuncName--;
122 
123         *(pzCopy++) = NUL;
124         *pscan = pzFuncName;
125         current_tpl->td_scan = pzCopy;
126     }
127 
128     /*
129      *  "Unknown" means we have to check again before we
130      *  know whether to assign it to "FTYP_INVOKE" or "FTYP_COND".
131      *  That depends on whether or not we find a named template
132      *  at template instantiation time.
133      */
134     return FTYP_UNKNOWN;
135 }
136 
137 static char const *
find_mac_end(char const ** ppzMark)138 find_mac_end(char const ** ppzMark)
139 {
140     char const * pzMark = *ppzMark + st_mac_len;
141     char const * pzFunc;
142     char const * pzNextMark;
143     char const * pzEndMark;
144 
145     /*
146      *  Set our pointers to the start of the macro text
147      */
148     for (;;) {
149         pzMark = SPN_NON_NL_WHITE_CHARS(pzMark);
150         if (*pzMark != NL)
151             break;
152         tpl_line++;
153         pzMark++;
154     }
155 
156     pzFunc             = pzMark;
157     cur_macro->md_code = func_code(&pzMark);
158     cur_macro->md_line = tpl_line;
159     *ppzMark           = pzMark;
160 
161     /*
162      *  Find the end.  (We must.)  If the thing is empty, treat as a comment,
163      *  but warn about it.
164      */
165     pzEndMark = strstr(pzMark, end_mac_mark);
166     if (pzEndMark == NULL)
167         AG_ABEND(FIND_MAC_END_NOPE);
168 
169     if (pzEndMark == pzFunc) {
170         cur_macro->md_code = FTYP_COMMENT;
171         fprintf(trace_fp, FIND_MAC_END_EMPTY,
172                 current_tpl->td_file, tpl_line);
173         return pzEndMark;
174     }
175 
176     /*
177      *  Back up over a preceding backslash.  It is a flag to indicate the
178      *  removal of the end of line white space.
179      */
180     if (pzEndMark[-1] == '\\')
181         pzEndMark--;
182 
183     pzNextMark = strstr(pzMark, st_mac_mark);
184     if (pzNextMark == NULL)
185         return pzEndMark;
186 
187     if (pzEndMark > pzNextMark)
188         AG_ABEND(FIND_MAC_END_NESTED);
189 
190     return pzEndMark;
191 }
192 
193 static char const *
find_mac_start(char const * pz,macro_t ** ppm,templ_t * tpl)194 find_mac_start(char const * pz, macro_t ** ppm, templ_t * tpl)
195 {
196     char *       pzCopy;
197     char const * pzEnd;
198     char const * res = strstr(pz, st_mac_mark);
199     macro_t *    mac = *ppm;
200 
201     if (res == pz)
202         return res;
203 
204     /*
205      *  There is some text here.  Make a text macro entry.
206      */
207     pzCopy      = tpl->td_scan;
208     pzEnd       = (res != NULL) ? res : pz + strlen(pz);
209     mac->md_txt_off = (uintptr_t)(pzCopy - tpl->td_text);
210     mac->md_code = FTYP_TEXT;
211     mac->md_line = tpl_line;
212 
213 #if defined(DEBUG_ENABLED)
214     if (HAVE_OPT(SHOW_DEFS)) {
215         int ct = tpl_nest_lvl;
216         fprintf(trace_fp, "%3u ", (unsigned int)(mac - tpl->td_macros));
217         do { fputs("  ", trace_fp); } while (--ct > 0);
218 
219         fprintf(trace_fp, tpl_def_fmt, ag_fun_names[ FTYP_TEXT ], FTYP_TEXT,
220                 mac->md_line, mac->md_end_idx, (unsigned int)(pzEnd - pz));
221     }
222 #endif
223 
224     do  {
225         if ((*(pzCopy++) = *(pz++)) == NL)
226             tpl_line++;
227     } while (pz < pzEnd);
228 
229     *(pzCopy++)   = NUL;
230     *ppm          = mac + 1;
231     tpl->td_scan = pzCopy;
232 
233     return res;  /* may be NULL, if there are no more macros */
234 }
235 
236 static char const *
find_macro(templ_t * tpl,macro_t ** ppm,char const ** pscan)237 find_macro(templ_t * tpl, macro_t ** ppm, char const ** pscan)
238 {
239     char const * scan = *pscan;
240     char const * pzMark;
241 
242     pzMark = find_mac_start(scan, ppm, tpl);
243 
244     /*
245      *  IF no more macro marks are found, THEN we are done...
246      */
247     if (pzMark == NULL)
248         return pzMark;
249 
250     /*
251      *  Find the macro code and the end of the macro invocation
252      */
253     cur_macro = *ppm;
254     scan    = find_mac_end(&pzMark);
255 
256     /*
257      *  Count the lines in the macro text and advance the
258      *  text pointer to after the marker.
259      */
260     {
261         char const *  pzMacEnd = scan;
262         char const *  pz       = pzMark;
263 
264         for (;;pz++) {
265             pz = strchr(pz, NL);
266             if ((pz == NULL) || (pz > pzMacEnd))
267                 break;
268             tpl_line++;
269         }
270 
271         /*
272          *  Strip white space from the macro
273          */
274         pzMark = SPN_WHITESPACE_CHARS(pzMark);
275 
276         if (pzMark != pzMacEnd) {
277             pzMacEnd = SPN_WHITESPACE_BACK( pzMark, pzMacEnd);
278             (*ppm)->md_txt_off = (uintptr_t)pzMark;
279             (*ppm)->md_res     = (uintptr_t)(pzMacEnd - pzMark);
280         }
281     }
282 
283     /*
284      *  IF the end macro mark was preceded by a backslash, then we remove
285      *  trailing white space from there to the end of the line.
286      */
287     if ((*scan != '\\') || (strncmp(end_mac_mark, scan, end_mac_len) == 0))
288         scan += end_mac_len;
289 
290     else {
291         char const * pz;
292         scan += end_mac_len + 1;
293         pz = SPN_NON_NL_WHITE_CHARS(scan);
294         if (*pz == NL) {
295             scan = pz + 1;
296             tpl_line++;
297         }
298     }
299 
300     *pscan = scan;
301     return pzMark;
302 }
303 
304 #if defined(DEBUG_ENABLED)
305  static void
print_indentation(templ_t * tpl,macro_t * mac,int idx)306 print_indentation(templ_t * tpl, macro_t * mac, int idx)
307 {
308     static char const fmt_fmt[] = "%%%us";
309     char fmt[16];
310 
311     if (idx < 0)
312         fputs("    ", trace_fp);
313     else fprintf(trace_fp, "%3u ", (unsigned int)idx);
314     snprintf(fmt, sizeof(fmt), fmt_fmt, tpl_nest_lvl);
315     fprintf(trace_fp, fmt, "");
316     (void)tpl;
317     (void)mac;
318 }
319 
320  static void
print_ag_defs(templ_t * tpl,macro_t * mac)321 print_ag_defs(templ_t * tpl, macro_t * mac)
322 {
323     mac_func_t ft  = mac->md_code;
324     int        ln  = mac->md_line;
325     int idx = (mac->md_code == FTYP_BOGUS) ? -1 : (int)(mac - tpl->td_macros);
326 
327     print_indentation(tpl, mac, idx);
328 
329     if (mac->md_code == FTYP_BOGUS)
330         fprintf(trace_fp, zTUndef, ag_fun_names[ ft ], ft, ln);
331     else {
332         char const * pz;
333         if (ft >= FUNC_CT)
334             ft = FTYP_SELECT;
335         pz = (mac->md_txt_off == 0)
336             ? zNil
337             : (tpl->td_text + mac->md_txt_off);
338         fprintf(trace_fp, tpl_def_fmt, ag_fun_names[ft], mac->md_code,
339                 ln, mac->md_end_idx, (unsigned int)strlen(pz));
340     }
341 }
342 #endif
343 
344 /**
345  * Parse the template.
346  * @param[out]    mac     array of macro descriptors to fill in
347  * @param[in,out] p_scan  pointer to string scanning address
348  */
349 static macro_t *
parse_tpl(macro_t * mac,char const ** p_scan)350 parse_tpl(macro_t * mac, char const ** p_scan)
351 {
352     char const * scan = *p_scan;
353     templ_t *    tpl  = current_tpl;
354 
355 #if defined(DEBUG_ENABLED)
356 
357     #define DEBUG_DEC(l)  l--
358 
359     if (  ((tpl_nest_lvl++) > 0)
360        && HAVE_OPT(SHOW_DEFS)) {
361         int     idx = (int)(mac - tpl->td_macros);
362         macro_t * m = mac - 1;
363 
364         print_indentation(tpl, m, idx);
365 
366         fprintf(trace_fp, zTUndef, ag_fun_names[m->md_code],
367                 m->md_code, m->md_line);
368     }
369 #else
370     #define DEBUG_DEC(l)
371 #endif
372 
373     while (find_macro(tpl, &mac, &scan) != NULL) {
374         /*
375          *  IF the called function returns a NULL next macro pointer,
376          *  THEN some block has completed.  The returned scanning pointer
377          *       will be non-NULL.
378          */
379         load_proc_p_t const fn = load_proc_table[mac->md_code];
380         macro_t *   nxt_mac = fn(tpl, mac, &scan);
381 
382 #if defined(DEBUG_ENABLED)
383         if (HAVE_OPT(SHOW_DEFS))
384             print_ag_defs(tpl, mac);
385 #endif
386 
387         if (nxt_mac == NULL) {
388             *p_scan = scan;
389             DEBUG_DEC(tpl_nest_lvl);
390             return mac;
391         }
392         mac = nxt_mac;
393     }
394 
395     DEBUG_DEC(tpl_nest_lvl);
396 
397     /*
398      *  We reached the end of the input string.
399      *  Return a NULL scanning pointer and a pointer to the end.
400      */
401     *p_scan = NULL;
402     return mac;
403 }
404 /**
405  * @}
406  *
407  * Local Variables:
408  * mode: C
409  * c-file-style: "stroustrup"
410  * indent-tabs-mode: nil
411  * End:
412  * end of agen5/tpParse.c */
413