2682588efe76042c07575c8200ba50a259e881e2
[pspp-builds.git] / src / sort-prs.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., 51 Franklin Street, Fifth Floor, Boston, MA
18    02110-1301, USA. */
19
20 #include <config.h>
21 #include <sys/types.h>
22 #include <assert.h>
23 #include <stdlib.h>
24
25 #include "alloc.h"
26 #include "error.h"
27 #include "lexer.h"
28 #include "sort-prs.h"
29 #include "sort.h"
30 #include "var.h"
31
32
33
34 static bool  is_terminator(int tok, const int *terminators);
35
36
37 /* Parses a list of sort keys and returns a struct sort_criteria
38    based on it.  Returns a null pointer on error.
39    If SAW_DIRECTION is nonnull, sets *SAW_DIRECTION to true if at
40    least one parenthesized sort direction was specified, false
41    otherwise. 
42    If TERMINATORS is non-null, then it must be a pointer to a 
43    null terminated list of tokens, in addition to the defaults,
44    which are to be considered terminators of the clause being parsed.
45    The default terminators are '/' and '.'
46    
47 */
48 struct sort_criteria *
49 sort_parse_criteria (const struct dictionary *dict,
50                      struct variable ***vars, int *var_cnt,
51                      bool *saw_direction,
52                      const int *terminators
53                      )
54 {
55   struct sort_criteria *criteria;
56   struct variable **local_vars = NULL;
57   size_t local_var_cnt;
58
59   assert ((vars == NULL) == (var_cnt == NULL));
60   if (vars == NULL) 
61     {
62       vars = &local_vars;
63       var_cnt = &local_var_cnt;
64     }
65
66   criteria = xmalloc (sizeof *criteria);
67   criteria->crits = NULL;
68   criteria->crit_cnt = 0;
69
70   *vars = NULL;
71   *var_cnt = 0;
72   if (saw_direction != NULL)
73     *saw_direction = false;
74
75   do
76     {
77       int prev_var_cnt = *var_cnt;
78       enum sort_direction direction;
79
80       /* Variables. */
81       if (!parse_variables (dict, vars, var_cnt,
82                             PV_NO_DUPLICATE | PV_APPEND | PV_NO_SCRATCH))
83         goto error;
84
85       /* Sort direction. */
86       if (lex_match ('('))
87         {
88           if (lex_match_id ("D") || lex_match_id ("DOWN"))
89             direction = SRT_DESCEND;
90           else if (lex_match_id ("A") || lex_match_id ("UP"))
91             direction = SRT_ASCEND;
92           else
93             {
94               msg (SE, _("`A' or `D' expected inside parentheses."));
95               goto error;
96             }
97           if (!lex_match (')'))
98             {
99               msg (SE, _("`)' expected."));
100               goto error;
101             }
102           if (saw_direction != NULL)
103             *saw_direction = true;
104         }
105       else
106         direction = SRT_ASCEND;
107
108       criteria->crits = xrealloc (criteria->crits,
109                                   sizeof *criteria->crits * *var_cnt);
110       criteria->crit_cnt = *var_cnt;
111       for (; prev_var_cnt < criteria->crit_cnt; prev_var_cnt++) 
112         {
113           struct sort_criterion *c = &criteria->crits[prev_var_cnt];
114           c->fv = (*vars)[prev_var_cnt]->fv;
115           c->width = (*vars)[prev_var_cnt]->width;
116           c->dir = direction;
117         }
118     }
119   while (token != '.' && token != '/' && !is_terminator(token, terminators));
120
121   free (local_vars);
122   return criteria;
123
124  error:
125   free (local_vars);
126   sort_destroy_criteria (criteria);
127   return NULL;
128 }
129
130 /* Return TRUE if TOK is a member of the list of TERMINATORS.
131    FALSE otherwise */
132 static bool 
133 is_terminator(int tok, const int *terminators)
134 {
135   if (terminators == NULL ) 
136     return false;
137
138   while ( *terminators) 
139     {
140       if (tok == *terminators++)
141         return true;
142     }
143
144   return false;
145 }
146
147
148
149 /* Destroys a SORT CASES program. */
150 void
151 sort_destroy_criteria (struct sort_criteria *criteria) 
152 {
153   if (criteria != NULL) 
154     {
155       free (criteria->crits);
156       free (criteria);
157     }
158 }
159