Fix memory leaks.
[pspp-builds.git] / src / repeat.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18    02111-1307, USA. */
19
20 #include <config.h>
21 #include "repeat.h"
22 #include "error.h"
23 #include <ctype.h>
24 #include <math.h>
25 #include <stdlib.h>
26 #include "alloc.h"
27 #include "command.h"
28 #include "error.h"
29 #include "getline.h"
30 #include "lexer.h"
31 #include "misc.h"
32 #include "settings.h"
33 #include "str.h"
34 #include "var.h"
35
36 #include "debug-print.h"
37
38 /* Describes one DO REPEAT macro. */
39 struct repeat_entry
40   {
41     int type;                   /* 1=variable names, 0=any other. */
42     char id[9];                 /* Macro identifier. */
43     char **replacement;         /* Macro replacement. */
44     struct repeat_entry *next;
45   };
46
47 /* List of macro identifiers. */
48 static struct repeat_entry *repeat_tab;
49
50 /* Number of substitutions for each macro. */
51 static int count;
52
53 /* List of lines before it's actually assigned to a file. */
54 static struct getl_line_list *line_buf_head;
55 static struct getl_line_list *line_buf_tail;
56
57 static int parse_ids (struct repeat_entry *);
58 static int parse_numbers (struct repeat_entry *);
59 static int parse_strings (struct repeat_entry *);
60 static void clean_up (void);
61 static int internal_cmd_do_repeat (void);
62
63 int
64 cmd_do_repeat (void)
65 {
66   if (internal_cmd_do_repeat ())
67     return CMD_SUCCESS;
68
69   clean_up ();
70   return CMD_FAILURE;
71 }
72
73 /* Garbage collects all the allocated memory that's no longer
74    needed. */
75 static void
76 clean_up (void)
77 {
78   struct repeat_entry *iter, *next;
79   int i;
80
81   iter = repeat_tab;
82   repeat_tab = NULL;
83
84   while (iter)
85     {
86       if (iter->replacement)
87         {
88           for (i = 0; i < count; i++)
89             free (iter->replacement[i]);
90           free (iter->replacement);
91         }
92       next = iter->next;
93       free (iter);
94       iter = next;
95     }
96 }
97
98 /* Allocates & appends another record at the end of the line_buf_tail
99    chain. */
100 static inline void
101 append_record (void)
102 {
103   struct getl_line_list *new = xmalloc (sizeof *new);
104   
105   if (line_buf_head == NULL)
106     line_buf_head = line_buf_tail = new;
107   else
108     line_buf_tail = line_buf_tail->next = new;
109 }
110
111 /* Returns nonzero if KEYWORD appears beginning at CONTEXT. */
112 static int
113 recognize_keyword (const char *context, const char *keyword)
114 {
115   const char *end = context;
116   while (isalpha ((unsigned char) *end))
117     end++;
118   return lex_id_match_len (keyword, strlen (keyword), context, end - context);
119 }
120
121 /* Does the real work of parsing the DO REPEAT command and its nested
122    commands. */
123 static int
124 internal_cmd_do_repeat (void)
125 {
126   /* Name of first DO REPEAT macro. */
127   char first_name[9];
128
129   /* Current filename. */
130   const char *current_filename = NULL;
131
132   /* 1=Print lines after preprocessing. */
133   int print;
134
135   /* The first step is parsing the DO REPEAT command itself. */
136   count = 0;
137   line_buf_head = NULL;
138   do
139     {
140       struct repeat_entry *e;
141       struct repeat_entry *iter;
142       int result;
143
144       /* Get a stand-in variable name and make sure it's unique. */
145       if (!lex_force_id ())
146         return 0;
147       for (iter = repeat_tab; iter; iter = iter->next)
148         if (!strcmp (iter->id, tokid))
149           {
150             msg (SE, _("Identifier %s is given twice."), tokid);
151             return 0;
152           }
153
154       /* Make a new stand-in variable entry and link it into the
155          list. */
156       e = xmalloc (sizeof *e);
157       e->type = 0;
158       e->next = repeat_tab;
159       strcpy (e->id, tokid);
160       repeat_tab = e;
161
162       /* Skip equals sign. */
163       lex_get ();
164       if (!lex_force_match ('='))
165         return 0;
166
167       /* Get the details of the variable's possible values. */
168       
169       if (token == T_ID)
170         result = parse_ids (e);
171       else if (token == T_NUM)
172         result = parse_numbers (e);
173       else if (token == T_STRING)
174         result = parse_strings (e);
175       else
176         {
177           lex_error (NULL);
178           return 0;
179         }
180       if (!result)
181         return 0;
182
183       /* If this is the first variable then it defines how many
184          replacements there must be; otherwise enforce this number of
185          replacements. */
186       if (!count)
187         {
188           count = result;
189           strcpy (first_name, e->id);
190         }
191       else if (count != result)
192         {
193           msg (SE, _("There must be the same number of substitutions "
194                      "for each dummy variable specified.  Since there "
195                      "were %d substitutions for %s, there must be %d "
196                      "for %s as well, but %d were specified."),
197                count, first_name, count, e->id, result);
198           return 0;
199         }
200
201       /* Next! */
202       lex_match ('/');
203     }
204   while (token != '.');
205
206   /* Read all the lines inside the DO REPEAT ... END REPEAT. */
207   {
208     int nest = 1;
209
210     for (;;)
211       {
212         if (!getl_read_line ())
213           msg (FE, _("Unexpected end of file."));
214
215         /* If the current file has changed then record the fact. */
216         {
217           const char *curfn;
218           int curln;
219
220           getl_location (&curfn, &curln);
221           if (current_filename != curfn)
222             {
223               assert (curln > 0 && curfn != NULL);
224             
225               append_record ();
226               line_buf_tail->len = -curln;
227               line_buf_tail->line = xstrdup (curfn);
228               current_filename = curfn;
229             }
230         }
231         
232         /* FIXME?  This code is not strictly correct, however if you
233            have begun a line with DO REPEAT or END REPEAT and it's
234            *not* a command name, then you are obviously *trying* to
235            break this mechanism.  And you will.  Also, the entire
236            command names must appear on a single line--they can't be
237            spread out. */
238         {
239           char *cp = ds_c_str (&getl_buf);
240
241           /* Skip leading indentors and any whitespace. */
242           if (*cp == '+' || *cp == '-' || *cp == '.')
243             cp++;
244           while (isspace ((unsigned char) *cp))
245             cp++;
246
247           /* Find END REPEAT. */
248           if (recognize_keyword (cp, "end"))
249             {
250               while (isalpha ((unsigned char) *cp))
251                 cp++;
252               while (isspace ((unsigned char) *cp))
253                 cp++;
254               if (recognize_keyword (cp, "repeat"))
255                 {
256                   nest--;
257
258                   if (!nest)
259                   {
260                     while (isalpha ((unsigned char) *cp))
261                       cp++;
262                     while (isspace ((unsigned char) *cp))
263                       cp++;
264
265                     print = recognize_keyword (cp, "print");
266                     break;
267                   }
268                 }
269             }
270           else /* Find DO REPEAT. */
271             if (!strncasecmp (cp, "do", 2))
272               {
273                 cp += 2;
274                 while (isspace ((unsigned char) *cp))
275                   cp++;
276                 if (!strncasecmp (cp, "rep", 3))
277                   nest++;
278               }
279         }
280
281         append_record ();
282         line_buf_tail->len = ds_length (&getl_buf);
283         line_buf_tail->line = xmalloc (ds_length (&getl_buf) + 1);
284         memcpy (line_buf_tail->line,
285                 ds_c_str (&getl_buf), ds_length (&getl_buf) + 1);
286       }
287   }
288
289   /* FIXME: For the moment we simply discard the contents of the END
290      REPEAT line.  We should actually check for the PRINT specifier.
291      This can be done easier when we buffer entire commands instead of
292      doing it token by token; see TODO. */
293   lex_discard_line ();  
294   
295   /* Tie up the loose end of the chain. */
296   if (line_buf_head == NULL)
297     {
298       msg (SW, _("No commands in scope."));
299       return 1;
300     }
301   line_buf_tail->next = NULL;
302
303   /* Make new variables. */
304   {
305     struct repeat_entry *iter;
306     for (iter = repeat_tab; iter; iter = iter->next)
307       if (iter->type == 1)
308         {
309           int i;
310           for (i = 0; i < count; i++)
311             {
312               /* Note that if the variable already exists there is no
313                  harm done. */
314               dict_create_var (default_dict, iter->replacement[i], 0);
315             }
316         }
317   }
318
319   /* Create the DO REPEAT virtual input file. */
320   {
321     struct getl_script *script = xmalloc (sizeof *script);
322
323     script->first_line = line_buf_head;
324     script->cur_line = NULL;
325     script->remaining_loops = count;
326     script->loop_index = -1;
327     script->macros = repeat_tab;
328     script->print = print;
329
330     getl_add_DO_REPEAT_file (script);
331   }
332
333   return 1;
334 }
335
336 /* Parses a set of ids for DO REPEAT. */
337 static int
338 parse_ids (struct repeat_entry * e)
339 {
340   int i;
341   int n = 0;
342
343   e->type = 1;
344   e->replacement = NULL;
345
346   do
347     {
348       char **names;
349       int nnames;
350
351       if (!parse_mixed_vars (&names, &nnames, PV_NONE))
352         return 0;
353
354       e->replacement = xrealloc (e->replacement,
355                                  (nnames + n) * sizeof *e->replacement);
356       for (i = 0; i < nnames; i++)
357         {
358           e->replacement[n + i] = xstrdup (names[i]);
359           free (names[i]);
360         }
361       free (names);
362       n += nnames;
363     }
364   while (token != '/' && token != '.');
365
366   return n;
367 }
368
369 /* Stores VALUE into *REPL. */
370 static inline void
371 store_numeric (char **repl, long value)
372 {
373   *repl = xmalloc (INT_DIGITS + 1);
374   sprintf (*repl, "%ld", value);
375 }
376
377 /* Parses a list of numbers for DO REPEAT. */
378 static int
379 parse_numbers (struct repeat_entry *e)
380 {
381   /* First and last numbers for TO, plus the step factor. */
382   long a, b;
383
384   /* Alias to e->replacement. */
385   char **array;
386
387   /* Number of entries in array; maximum number for this allocation
388      size. */
389   int n, m;
390
391   n = m = 0;
392   e->type = 0;
393   e->replacement = array = NULL;
394
395   do
396     {
397       /* Parse A TO B into a, b. */
398       if (!lex_force_int ())
399         return 0;
400       a = lex_integer ();
401
402       lex_get ();
403       if (token == T_TO)
404         {
405           lex_get ();
406           if (!lex_force_int ())
407             return 0;
408           b = lex_integer ();
409
410           lex_get ();
411         }
412       else b = a;
413
414       if (n + (abs (b - a) + 1) > m)
415         {
416           m = n + (abs (b - a) + 1) + 16;
417           e->replacement = array = xrealloc (array,
418                                              m * sizeof *e->replacement);
419         }
420
421       if (a == b)
422         store_numeric (&array[n++], a);
423       else
424         {
425           long iter;
426
427           if (a < b)
428             for (iter = a; iter <= b; iter++)
429               store_numeric (&array[n++], iter);
430           else
431             for (iter = a; iter >= b; iter--)
432               store_numeric (&array[n++], iter);
433         }
434
435       lex_match (',');
436     }
437   while (token != '/' && token != '.');
438   e->replacement = xrealloc (array, n * sizeof *e->replacement);
439
440   return n;
441 }
442
443 /* Parses a list of strings for DO REPEAT. */
444 int
445 parse_strings (struct repeat_entry * e)
446 {
447   char **string;
448   int n, m;
449
450   e->type = 0;
451   string = e->replacement = NULL;
452   n = m = 0;
453
454   do
455     {
456       if (token != T_STRING)
457         {
458           int i;
459           msg (SE, _("String expected."));
460           for (i = 0; i < n; i++)
461             free (string[i]);
462           free (string);
463           return 0;
464         }
465
466       if (n + 1 > m)
467         {
468           m += 16;
469           e->replacement = string = xrealloc (string,
470                                               m * sizeof *e->replacement);
471         }
472       string[n++] = lex_token_representation ();
473       lex_get ();
474
475       lex_match (',');
476     }
477   while (token != '/' && token != '.');
478   e->replacement = xrealloc (string, n * sizeof *e->replacement);
479
480   return n;
481 }
482 \f
483 int
484 cmd_end_repeat (void)
485 {
486   msg (SE, _("No matching DO REPEAT."));
487   return CMD_FAILURE;
488 }
489 \f
490 /* Finds a DO REPEAT macro with name MACRO_NAME and returns the
491    appropriate subsitution if found, or NULL if not. */
492 static char *
493 find_DO_REPEAT_substitution (char *macro_name)
494 {
495   struct getl_script *s;
496             
497   for (s = getl_head; s; s = s->included_from)
498     {
499       struct repeat_entry *e;
500       
501       if (s->first_line == NULL)
502         continue;
503
504       for (e = s->macros; e; e = e->next)
505         if (!strcasecmp (e->id, macro_name))
506           return e->replacement[s->loop_index];
507     }
508   
509   return NULL;
510 }
511
512 /* Makes appropriate DO REPEAT macro substitutions within getl_buf. */
513 void
514 perform_DO_REPEAT_substitutions (void)
515 {
516   /* Are we in an apostrophized string or a quoted string? */
517   int in_apos = 0, in_quote = 0;
518
519   /* Source pointer. */
520   char *cp;
521
522   /* Output buffer, size, pointer. */
523   struct string output;
524
525   /* Terminal dot. */
526   int dot = 0;
527
528   ds_init (&output, ds_capacity (&getl_buf));
529
530   /* Strip trailing whitespace, check for & remove terminal dot. */
531   while (ds_length (&getl_buf) > 0
532          && isspace ((unsigned char) ds_end (&getl_buf)[-1]))
533     ds_truncate (&getl_buf, ds_length (&getl_buf) - 1);
534   if (ds_length (&getl_buf) > 0 && ds_end (&getl_buf)[-1] == get_endcmd() )
535     {
536       dot = 1;
537       ds_truncate (&getl_buf, ds_length (&getl_buf) - 1);
538     }
539   
540   for (cp = ds_c_str (&getl_buf); cp < ds_end (&getl_buf); )
541     {
542       if (*cp == '\'' && !in_quote)
543         in_apos ^= 1;
544       else if (*cp == '"' && !in_apos)
545         in_quote ^= 1;
546       
547       if (in_quote || in_apos || !CHAR_IS_ID1 (*cp))
548         {
549           ds_putc (&output, *cp++);
550           continue;
551         }
552
553       /* Collect an identifier. */
554       {
555         char name[9];
556         char *start = cp;
557         char *np = name;
558         char *substitution;
559
560         while (CHAR_IS_IDN (*cp) && np < &name[8])
561           *np++ = *cp++;
562         while (CHAR_IS_IDN (*cp))
563           cp++;
564         *np = 0;
565
566         substitution = find_DO_REPEAT_substitution (name);
567         if (!substitution)
568           {
569             ds_concat (&output, start, cp - start);
570             continue;
571           }
572
573         /* Force output buffer size, copy substitution. */
574         ds_puts (&output, substitution);
575       }
576     }
577   if (dot)
578     ds_putc (&output, get_endcmd() );
579
580   ds_destroy (&getl_buf);
581   getl_buf = output;
582 }