Fix memory leaks.
[pspp-builds.git] / src / count.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 "error.h"
22 #include <stdlib.h>
23 #include "alloc.h"
24 #include "case.h"
25 #include "command.h"
26 #include "error.h"
27 #include "lexer.h"
28 #include "str.h"
29 #include "var.h"
30
31 /* Implementation details:
32
33    The S?SS manuals do not specify the order that COUNT subcommands are
34    performed in.  Experiments, however, have shown that they are performed
35    in the order that they are specified in, rather than simultaneously.
36    So, with the two variables A and B, and the two cases,
37
38    A B
39    1 2
40    2 1
41
42    the command COUNT A=A B (1) / B=A B (2) will produce the following
43    results,
44
45    A B
46    1 1
47    1 0
48
49    rather than the results that would be produced if subcommands were
50    simultaneous:
51
52    A B
53    1 1
54    1 1
55
56    Perhaps simultaneity could be implemented as an option.  On the
57    other hand, what good are the above commands?  */
58 \f
59 /* Definitions. */
60
61 enum
62   {
63     CNT_ERROR,                  /* Invalid value. */
64     CNT_SINGLE,                 /* Single value. */
65     CNT_HIGH,                   /* x >= a. */
66     CNT_LOW,                    /* x <= a. */
67     CNT_RANGE,                  /* a <= x <= b. */
68     CNT_ANY,                    /* Count any. */
69     CNT_SENTINEL                /* List terminator. */
70   };
71
72 struct cnt_num
73   {
74     int type;
75     double a, b;
76   };
77
78 struct cnt_str
79   {
80     int type;
81     char *s;
82   };
83
84 struct counting
85   {
86     struct counting *next;
87
88     /* variables to count */
89     struct variable **v;
90     int n;
91
92     /* values to count */
93     int missing;                /* (numeric only)
94                                    0=don't count missing,
95                                    1=count SYSMIS,
96                                    2=count system- and user-missing */
97     union                       /* Criterion values. */
98       {
99         struct cnt_num *n;
100         struct cnt_str *s;
101       }
102     crit;
103   };
104
105 struct cnt_var_info
106   {
107     struct cnt_var_info *next;
108
109     struct variable *d;         /* Destination variable. */
110     char n[9];                  /* Name of dest var. */
111
112     struct counting *c;         /* The counting specifications. */
113   };
114
115 struct count_trns
116   {
117     struct trns_header h;
118     struct cnt_var_info *specs;
119   };
120 \f
121 /* Parser. */
122
123 static trns_proc_func count_trns_proc;
124 static trns_free_func count_trns_free;
125
126 static int parse_numeric_criteria (struct counting *);
127 static int parse_string_criteria (struct counting *);
128
129 int
130 cmd_count (void)
131 {
132   struct cnt_var_info *cnt;     /* Specification currently being parsed. */
133   struct counting *c;           /* Counting currently being parsed. */
134   int ret;                      /* Return value from parsing function. */
135   struct count_trns *trns;      /* Transformation. */
136   struct cnt_var_info *head;    /* First counting in chain. */
137
138   /* Parses each slash-delimited specification. */
139   head = cnt = xmalloc (sizeof *cnt);
140   for (;;)
141     {
142       /* Initialize this struct cnt_var_info to ensure proper cleanup. */
143       cnt->next = NULL;
144       cnt->d = NULL;
145       cnt->c = NULL;
146
147       /* Get destination struct variable, or at least its name. */
148       if (!lex_force_id ())
149         goto fail;
150       cnt->d = dict_lookup_var (default_dict, tokid);
151       if (cnt->d)
152         {
153           if (cnt->d->type == ALPHA)
154             {
155               msg (SE, _("Destination cannot be a string variable."));
156               goto fail;
157             }
158         }
159       else
160         strcpy (cnt->n, tokid);
161
162       lex_get ();
163       if (!lex_force_match ('='))
164         goto fail;
165
166       c = cnt->c = xmalloc (sizeof *c);
167       for (;;)
168         {
169           c->next = NULL;
170           c->v = NULL;
171           if (!parse_variables (default_dict, &c->v, &c->n,
172                                 PV_DUPLICATE | PV_SAME_TYPE))
173             goto fail;
174
175           if (!lex_force_match ('('))
176             goto fail;
177
178           ret = (c->v[0]->type == NUMERIC
179                  ? parse_numeric_criteria
180                  : parse_string_criteria) (c);
181           if (!ret)
182             goto fail;
183
184           if (token == '/' || token == '.')
185             break;
186
187           c = c->next = xmalloc (sizeof *c);
188         }
189
190       if (token == '.')
191         break;
192
193       if (!lex_force_match ('/'))
194         goto fail;
195       cnt = cnt->next = xmalloc (sizeof *cnt);
196     }
197
198   /* Create all the nonexistent destination variables. */
199   for (cnt = head; cnt; cnt = cnt->next)
200     if (!cnt->d)
201       {
202         /* It's valid, though motivationally questionable, to count to
203            the same dest var more than once. */
204         cnt->d = dict_lookup_var (default_dict, cnt->n);
205
206         if (cnt->d == NULL) 
207           cnt->d = dict_create_var_assert (default_dict, cnt->n, 0);
208       }
209
210   trns = xmalloc (sizeof *trns);
211   trns->h.proc = count_trns_proc;
212   trns->h.free = count_trns_free;
213   trns->specs = head;
214   add_transformation ((struct trns_header *) trns);
215
216   return CMD_SUCCESS;
217
218 fail:
219   {
220     struct count_trns t;
221     t.specs = head;
222     count_trns_free ((struct trns_header *) & t);
223     return CMD_FAILURE;
224   }
225 }
226
227 /* Parses a set of numeric criterion values. */
228 static int
229 parse_numeric_criteria (struct counting * c)
230 {
231   int n = 0;
232   int m = 0;
233
234   c->crit.n = 0;
235   c->missing = 0;
236   for (;;)
237     {
238       struct cnt_num *cur;
239       if (n >= m - 1)
240         {
241           m += 16;
242           c->crit.n = xrealloc (c->crit.n, m * sizeof (struct cnt_num));
243         }
244
245       cur = &c->crit.n[n++];
246       if (token == T_NUM)
247         {
248           cur->a = tokval;
249           lex_get ();
250           if (lex_match_id ("THRU"))
251             {
252               if (token == T_NUM)
253                 {
254                   if (!lex_force_num ())
255                     return 0;
256                   cur->b = tokval;
257                   cur->type = CNT_RANGE;
258                   lex_get ();
259
260                   if (cur->a > cur->b)
261                     {
262                       msg (SE, _("%g THRU %g is not a valid range.  The "
263                                  "number following THRU must be at least "
264                                  "as big as the number preceding THRU."),
265                            cur->a, cur->b);
266                       return 0;
267                     }
268                 }
269               else if (lex_match_id ("HI") || lex_match_id ("HIGHEST"))
270                 cur->type = CNT_HIGH;
271               else
272                 {
273                   lex_error (NULL);
274                   return 0;
275                 }
276             }
277           else
278             cur->type = CNT_SINGLE;
279         }
280       else if (lex_match_id ("LO") || lex_match_id ("LOWEST"))
281         {
282           if (!lex_force_match_id ("THRU"))
283             return 0;
284           if (token == T_NUM)
285             {
286               cur->type = CNT_LOW;
287               cur->a = tokval;
288               lex_get ();
289             }
290           else if (lex_match_id ("HI") || lex_match_id ("HIGHEST"))
291             cur->type = CNT_ANY;
292           else
293             {
294               lex_error (NULL);
295               return 0;
296             }
297         }
298       else if (lex_match_id ("SYSMIS"))
299         {
300           if (c->missing < 1)
301             c->missing = 1;
302         }
303       else if (lex_match_id ("MISSING"))
304         c->missing = 2;
305       else
306         {
307           lex_error (NULL);
308           return 0;
309         }
310
311       lex_match (',');
312       if (lex_match (')'))
313         break;
314     }
315
316   c->crit.n[n].type = CNT_SENTINEL;
317   return 1;
318 }
319
320 /* Parses a set of string criteria values.  The skeleton is the same
321    as parse_numeric_criteria(). */
322 static int
323 parse_string_criteria (struct counting * c)
324 {
325   int len = 0;
326
327   int n = 0;
328   int m = 0;
329
330   int i;
331
332   for (i = 0; i < c->n; i++)
333     if (c->v[i]->width > len)
334       len = c->v[i]->width;
335
336   c->crit.n = 0;
337   for (;;)
338     {
339       struct cnt_str *cur;
340       if (n >= m - 1)
341         {
342           m += 16;
343           c->crit.n = xrealloc (c->crit.n, m * sizeof (struct cnt_str));
344         }
345
346       if (!lex_force_string ())
347         return 0;
348       cur = &c->crit.s[n++];
349       cur->type = CNT_SINGLE;
350       cur->s = malloc (len + 1);
351       st_pad_copy (cur->s, ds_c_str (&tokstr), len + 1);
352       lex_get ();
353
354       lex_match (',');
355       if (lex_match (')'))
356         break;
357     }
358
359   c->crit.s[n].type = CNT_SENTINEL;
360   return 1;
361 }
362 \f
363 /* Transformation. */
364
365 /* Counts the number of values in case C matching counting CNT. */
366 static inline int
367 count_numeric (struct counting * cnt, struct ccase * c)
368 {
369   int counter = 0;
370   int i;
371
372   for (i = 0; i < cnt->n; i++)
373     {
374       struct cnt_num *num;
375
376       /* Extract the variable value and eliminate missing values. */
377       double cmp = case_num (c, cnt->v[i]->fv);
378       if (cmp == SYSMIS)
379         {
380           if (cnt->missing >= 1)
381             counter++;
382           continue;
383         }
384       if (cnt->missing >= 2 && is_num_user_missing (cmp, cnt->v[i]))
385         {
386           counter++;
387           continue;
388         }
389
390       /* Try to find the value in the list. */
391       for (num = cnt->crit.n;; num++)
392         switch (num->type)
393           {
394           case CNT_ERROR:
395             assert (0);
396             break;
397           case CNT_SINGLE:
398             if (cmp != num->a)
399               break;
400             counter++;
401             goto done;
402           case CNT_HIGH:
403             if (cmp < num->a)
404               break;
405             counter++;
406             goto done;
407           case CNT_LOW:
408             if (cmp > num->a)
409               break;
410             counter++;
411             goto done;
412           case CNT_RANGE:
413             if (cmp < num->a || cmp > num->b)
414               break;
415             counter++;
416             goto done;
417           case CNT_ANY:
418             counter++;
419             goto done;
420           case CNT_SENTINEL:
421             goto done;
422           default:
423             assert (0);
424           }
425     done: ;
426     }
427   return counter;
428 }
429
430 /* Counts the number of values in case C matching counting CNT. */
431 static inline int
432 count_string (struct counting * cnt, struct ccase * c)
433 {
434   int counter = 0;
435   int i;
436
437   for (i = 0; i < cnt->n; i++)
438     {
439       struct cnt_str *str;
440
441       /* Extract the variable value, variable width. */
442       for (str = cnt->crit.s;; str++)
443         switch (str->type)
444           {
445           case CNT_ERROR:
446             assert (0);
447           case CNT_SINGLE:
448             if (memcmp (case_str (c, cnt->v[i]->fv), str->s,
449                         cnt->v[i]->width))
450               break;
451             counter++;
452             goto done;
453           case CNT_SENTINEL:
454             goto done;
455           default:
456             assert (0);
457           }
458     done: ;
459     }
460   return counter;
461 }
462
463 /* Performs the COUNT transformation T on case C. */
464 static int
465 count_trns_proc (struct trns_header * trns, struct ccase * c,
466                  int case_num UNUSED)
467 {
468   struct cnt_var_info *info;
469   struct counting *cnt;
470   int counter;
471
472   for (info = ((struct count_trns *) trns)->specs; info; info = info->next)
473     {
474       counter = 0;
475       for (cnt = info->c; cnt; cnt = cnt->next)
476         if (cnt->v[0]->type == NUMERIC)
477           counter += count_numeric (cnt, c);
478         else
479           counter += count_string (cnt, c);
480       case_data_rw (c, info->d->fv)->f = counter;
481     }
482   return -1;
483 }
484
485 /* Destroys all dynamic data structures associated with T. */
486 static void
487 count_trns_free (struct trns_header * t)
488 {
489   struct cnt_var_info *iter, *next;
490
491   for (iter = ((struct count_trns *) t)->specs; iter; iter = next)
492     {
493       struct counting *i, *n;
494
495       for (i = iter->c; i; i = n)
496         {
497           if (i->n && i->v)
498             {
499               if (i->v[0]->type == NUMERIC)
500                 free (i->crit.n);
501               else
502                 {
503                   struct cnt_str *s;
504
505                   for (s = i->crit.s; s->type != CNT_SENTINEL; s++)
506                     free (s->s);
507                   free (i->crit.s);
508                 }
509             }
510           free (i->v);
511
512           n = i->next;
513           free (i);
514         }
515
516       next = iter->next;
517       free (iter);
518     }
519 }