1 /*
2  *      cook - file construction tool
3  *      Copyright (C) 1994, 1997, 1998, 2006, 2007 Peter Miller;
4  *      All rights reserved.
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 #include <common/ac/ctype.h>
22 #include <common/ac/string.h>
23 
24 #include <make2cook/emit.h>
25 #include <make2cook/stmt/if.h>
26 #include <common/trace.h>
27 #include <make2cook/variable.h>
28 
29 typedef struct stmt_if_ty stmt_if_ty;
30 struct stmt_if_ty
31 {
32     STMT
33     blob_list_ty    *condition;
34     stmt_ty         *then_clause;
35     stmt_ty         *else_clause;
36 };
37 
38 
39 static void
destructor(stmt_ty * that)40 destructor(stmt_ty *that)
41 {
42     stmt_if_ty      *this;
43 
44     trace(("if::destructor()\n{\n"));
45     this = (stmt_if_ty *) that;
46     blob_list_free(this->condition);
47     stmt_free(this->then_clause);
48     if (this->else_clause)
49         stmt_free(this->else_clause);
50     trace(("}\n"));
51 }
52 
53 
54 static void
emit(stmt_ty * that)55 emit(stmt_ty *that)
56 {
57     stmt_if_ty      *this;
58     size_t          j;
59 
60     trace(("if::emit()\n{\n"));
61     this = (stmt_if_ty *)that;
62     emit_line_number
63     (
64         this->condition->list[0]->line_number,
65         this->condition->list[0]->file_name
66     );
67     emit_str("#if");
68     for (j = 0; j < this->condition->length; ++j)
69     {
70         emit_char(' ');
71         emit_string(this->condition->list[j]->text);
72     }
73     emit_char('\n');
74 
75     stmt_emit(this->then_clause);
76     emit_bol();
77 
78     if (this->else_clause)
79     {
80         emit_str("#else\n");
81         stmt_emit(this->else_clause);
82         emit_bol();
83     }
84     emit_str("#endif\n");
85     trace(("}\n"));
86 }
87 
88 
89 static void
regroup(stmt_ty * that)90 regroup(stmt_ty *that)
91 {
92     stmt_if_ty      *this;
93 
94     trace(("if::regroup()\n{\n"));
95     this = (stmt_if_ty *) that;
96     stmt_regroup(this->then_clause);
97     if (this->else_clause)
98         stmt_regroup(this->else_clause);
99     trace(("}\n"));
100 }
101 
102 
103 static void
sort(stmt_ty * that)104 sort(stmt_ty *that)
105 {
106     stmt_if_ty      *this;
107 
108     trace(("if::sort()\n{\n"));
109     this = (stmt_if_ty *)that;
110     stmt_sort(this->then_clause);
111     if (this->else_clause)
112         stmt_sort(this->else_clause);
113     trace(("}\n"));
114 }
115 
116 
117 static stmt_method_ty method =
118 {
119     sizeof(stmt_if_ty),
120     "if",
121     0,                          /* constructor */
122     destructor,
123     emit,
124     regroup,
125     sort,
126 };
127 
128 
129 static blob_list_ty *
ifeq(blob_list_ty * blp,string_list_ty * ref)130 ifeq(blob_list_ty *blp, string_list_ty *ref)
131 {
132     blob_ty         *arg;
133     blob_ty         *bp;
134     blob_list_ty    *result;
135     string_ty       *s;
136     string_ty       *s2;
137     char            *cp;
138     size_t          j;
139 
140     /*
141      * allocate the result list
142      */
143     trace(("ifeq()\n{\n"));
144     result = blob_list_alloc();
145 
146     /*
147      * make sure we were given enough arguments
148      */
149     if (blp->length < 2)
150     {
151         arg = blp->list[0];
152         blob_list_append
153         (
154             result,
155             blob_alloc(str_from_c("0"), arg->file_name, arg->line_number)
156         );
157         trace(("}\n"));
158         return result;
159     }
160 
161     /*
162      * turn the list of arguments into a single string
163      */
164     arg = blp->list[1];
165     s = str_copy(blp->list[1]->text);
166     for (j = 2; j < blp->length; ++j)
167     {
168         s2 = str_format("%s %s", s->str_text, blp->list[j]->text->str_text);
169         str_free(s);
170         s = s2;
171     }
172     bp = blob_alloc(s, arg->file_name, arg->line_number);
173 
174     /*
175      * rename the variables
176      * and reform to be a single string, again.
177      */
178     variable_rename(bp, result, ref, VAREN_NO_QUOQUO);
179     blob_free(bp);
180     s = result->length ? str_copy(result->list[0]->text) : str_from_c("0");
181     for (j = 1; j < result->length; ++j)
182     {
183         s2 = str_format("%s %s", s->str_text, result->list[j]->text->str_text);
184         str_free(s);
185         s = s2;
186     }
187     blob_list_free(result);
188 
189     /*
190      * construct the result
191      */
192     result = blob_list_alloc();
193     switch (s->str_text[0])
194     {
195     case '(':
196         /*
197          * ifeq (xxx,yyy)
198          */
199         if (s->str_length < 3)
200             goto useless;
201         cp = strchr(s->str_text, ',');
202         if (cp == 0 || s->str_text[s->str_length - 1] != ')')
203             goto useless;
204 
205         blob_list_append
206         (
207             result,
208             blob_alloc(str_from_c("[in"), arg->file_name, arg->line_number)
209         );
210 
211         s2 = str_n_from_c(s->str_text + 1, cp - s->str_text - 1);
212         if (s2->str_length == 0)
213             s2 = str_from_c("\"\"");
214         bp = blob_alloc(s2, arg->file_name, arg->line_number);
215         blob_list_append(result, bp);
216 
217         s2 = str_n_from_c(cp + 1, s->str_text + s->str_length - cp - 2);
218         if (s2->str_length == 0)
219             s2 = str_from_c("\"\"");
220         bp = blob_alloc(s2, arg->file_name, arg->line_number);
221         blob_list_append(result, bp);
222 
223         blob_list_append
224         (
225             result,
226             blob_alloc(str_from_c("]"), arg->file_name, arg->line_number)
227         );
228         break;
229 
230     case '\'':
231     case '"':
232         /*
233          * ifeq "xxx" "yyy"
234          */
235         if (s->str_length < 5)
236             goto useless;
237         cp = strchr(s->str_text + 1, s->str_text[0]);
238         if
239         (
240             !cp
241         ||
242             cp[1] != ' '
243         ||
244             cp[2] != s->str_text[0]
245         ||
246             s->str_text[s->str_length - 1] != s->str_text[0]
247         )
248             goto useless;
249 
250         blob_list_append
251         (
252             result,
253             blob_alloc(str_from_c("[in"), arg->file_name, arg->line_number)
254         );
255 
256         s2 = str_n_from_c(s->str_text + 1, cp - s->str_text - 1);
257         if (s2->str_length == 0)
258             s2 = str_from_c("\"\"");
259         bp = blob_alloc(s2, arg->file_name, arg->line_number);
260         blob_list_append(result, bp);
261 
262         s2 = str_n_from_c(cp + 3, s->str_text + s->str_length - cp - 4);
263         if (s2->str_length == 0)
264             s2 = str_from_c("\"\"");
265         bp = blob_alloc(s2, arg->file_name, arg->line_number);
266         blob_list_append(result, bp);
267 
268         blob_list_append
269         (
270             result,
271             blob_alloc(str_from_c("]"), arg->file_name, arg->line_number)
272         );
273         break;
274 
275     default:
276         /*
277          * We were given some useless thing, just rename the
278          * variables and copy it through.
279          */
280         useless:
281         bp = blob_alloc(str_copy(s), arg->file_name, arg->line_number);
282         blob_list_append(result, bp);
283         break;
284     }
285     str_free(s);
286     trace(("}\n"));
287     return result;
288 }
289 
290 
291 static blob_list_ty *
ifneq(blob_list_ty * blp,string_list_ty * ref)292 ifneq(blob_list_ty *blp, string_list_ty *ref)
293 {
294     blob_ty         *arg;
295     blob_list_ty    *result;
296     blob_ty         *bp;
297 
298     arg = blp->list[0];
299     result = ifeq(blp, ref);
300     bp = blob_alloc(str_from_c("[not"), arg->file_name, arg->line_number);
301     blob_list_prepend(result, bp);
302     bp = blob_alloc(str_from_c("]"), arg->file_name, arg->line_number);
303     blob_list_append(result, bp);
304     return result;
305 }
306 
307 
308 static blob_list_ty *
ifdef(blob_list_ty * blp,string_list_ty * ref)309 ifdef(blob_list_ty *blp, string_list_ty *ref)
310 {
311     blob_ty         *bp;
312     blob_list_ty    *result;
313     size_t          j;
314 
315     bp = blp->list[0];
316     result = blob_list_alloc();
317     blob_list_append
318     (
319         result,
320         blob_alloc(str_from_c("[defined"), bp->file_name, bp->line_number)
321     );
322     for (j = 1; j < blp->length; ++j)
323         variable_rename(blp->list[j], result, ref, VAREN_QUOTE_SPACES);
324     blob_list_append
325     (
326         result,
327         blob_alloc(str_from_c("]"), bp->file_name, bp->line_number)
328     );
329     return result;
330 }
331 
332 
333 static blob_list_ty *
ifndef(blob_list_ty * blp,string_list_ty * ref)334 ifndef(blob_list_ty *blp, string_list_ty *ref)
335 {
336     blob_ty         *arg;
337     blob_list_ty    *result;
338     blob_ty         *bp;
339 
340     arg = blp->list[0];
341     result = ifdef(blp, ref);
342     bp = blob_alloc(str_from_c("[not"), arg->file_name, arg->line_number);
343     blob_list_prepend(result, bp);
344     bp = blob_alloc(str_from_c("]"), arg->file_name, arg->line_number);
345     blob_list_append(result, bp);
346     return result;
347 }
348 
349 
350 typedef struct table_ty table_ty;
351 struct table_ty
352 {
353     char            *name;
354     blob_list_ty    *(*rewrite)(blob_list_ty *, string_list_ty *);
355     string_ty       *fast;
356 };
357 
358 static table_ty table[] =
359 {
360     { "ifeq",   ifeq,   0 },
361     { "ifneq",  ifneq,  0 },
362     { "ifdef",  ifdef,  0 },
363     { "ifndef", ifndef, 0 },
364 };
365 
366 
367 stmt_ty *
stmt_if_alloc(blob_list_ty * condition,stmt_ty * then_clause,stmt_ty * else_clause)368 stmt_if_alloc(blob_list_ty *condition, stmt_ty *then_clause,
369     stmt_ty *else_clause)
370 {
371     stmt_if_ty      *result;
372     blob_list_ty    *c2;
373     table_ty        *tp;
374 
375     trace(("stmt_if_alloc()\n{\n"));
376     result = (stmt_if_ty *)stmt_alloc(&method);
377 
378     assert(condition->length >= 1);
379     for (tp = table; tp < ENDOF(table); ++tp)
380     {
381         if (!tp->fast)
382             tp->fast = str_from_c(tp->name);
383         if (str_equal(condition->list[0]->text, tp->fast))
384             break;
385     }
386     assert(tp < ENDOF(table));
387     if (tp >= ENDOF(table))
388         tp = &table[0];
389     c2 = tp->rewrite(condition, &result->ref);
390     blob_list_free(condition);
391 
392     result->condition = c2;
393     result->then_clause = then_clause;
394     result->else_clause = else_clause;
395 
396     stmt_variable_merge((stmt_ty *)result, then_clause);
397     if (else_clause)
398         stmt_variable_merge((stmt_ty *)result, else_clause);
399     trace(("}\n"));
400     return (stmt_ty *)result;
401 }
402