1 /* ======================================================= *
2 * Copyright 1998-2005 Stephen C. Grubb *
3 * http://ploticus.sourceforge.net *
4 * Covered by GPL; see the file ./Copyright for details. *
5 * ======================================================= */
6
7 /* routines related to categories.. */
8
9 #include "pl.h"
10 #include <string.h>
11
12 #define CONTAINS 0
13 #define EXACT -1
14
15
16 static char **cats[2] = { NULL, NULL }; /* category list backbone (X, Y) */
17 static int ncats[2] = { 0, 0 }; /* number of categories in list (X, Y) */
18 static int nextcat[2] = { 0, 0 }; /* used by nextcat() for looping across categories (not widely used)*/
19 static int dont_init_ncats[2] = { 0, 0 }; /* for when items have been prepended */
20 static int catcompmethod[2] = { CONTAINS, CONTAINS }; /* category comparison method:
21 0 = contains -1 = exact >0 = compare 1st n characters */
22
23 static int sys_init[2] = { 0, 0 }; /* 1 if category list malloced and ready to go.. */
24 static int req_ncats[2] = { MAXNCATS, MAXNCATS }; /* size of category lists */
25 static int check_uniq[2] = { 1, 1 }; /* 1 = ensure unique cats 0 don't ensure uniqueness (faster, but dups will cause trouble) */
26 static int roundrobin[2] = { 1, 1 }; /* used with roundrobin category lookup */
27 static int curcat[2] = { 0, 0 }; /* used with roundrobin category lookup */
28
29
30 /* ================================================= */
31 /* CATFREE - free all malloced storage and initialize to original state.. */
32 int
PL_catfree()33 PL_catfree()
34 {
35 int i, j;
36 for( i = 0; i < 2; i++ ) {
37 if( sys_init[i] ) {
38 for( j = 0; j < ncats[i]; j++ ) free( cats[i][j] );
39 free( cats[i] );
40 }
41 }
42
43 /* these values must match the initializations at top */
44 cats[0] = NULL; cats[1] = NULL;
45 ncats[0] = 0; ncats[1] = 0;
46 nextcat[0] = 0; nextcat[1] = 0;
47 dont_init_ncats[0] = 0; dont_init_ncats[1] = 0;
48 catcompmethod[0] = CONTAINS; catcompmethod[1] = CONTAINS;
49 sys_init[0] = 0; sys_init[1] = 0;
50 req_ncats[0] = MAXNCATS; req_ncats[1] = MAXNCATS;
51 check_uniq[0] = 1; check_uniq[1] = 1;
52 roundrobin[0] = 1; roundrobin[1] = 1;
53 curcat[0] = 0; curcat[1] = 0;
54
55 return( 0 );
56 }
57
58 /* ================================================= */
59 /* SETCATS - fill categories list */
60 int
PL_setcats(ax,inbuf)61 PL_setcats( ax, inbuf )
62 char ax;
63 const char *inbuf;
64 {
65 int df;
66 int axi, textloc;
67 int i, j;
68 char buf[200];
69 char fname[NAMEMAXLEN];
70 int inbuflen, buflen, ix, ixhold;
71 char fieldspec[80], selex[256];
72 int stat, result;
73 char *s, *t;
74 int tlen;
75
76 if( ax == 'x' ) axi = 0;
77 else axi = 1;
78
79 if( !sys_init[axi] ) {
80 /* malloc the backbone - done only once per script */
81 cats[axi] = (char **) malloc( req_ncats[axi] * sizeof( char *) );
82 if( PLS.debug ) fprintf( PLS.diagfp, "categories in %c: list of size=%d malloced\n", ax, req_ncats[axi] );
83 sys_init[axi] = 1;
84 ncats[axi] = 0;
85 }
86 else if( ncats[axi] > 0 && !dont_init_ncats[axi] ) {
87 /* free malloced category labels */
88 for( i = 0; i < ncats[axi]; i++ ) free( cats[axi][i] );
89 ncats[axi] = 0;
90 }
91
92 strcpy( selex, "" );
93
94 if( strnicmp( inbuf, "datafield", 9 )==0 ) { /* fill cats list from a data field.. */
95
96 if( Nrecords < 1 )
97 return( Eerr( 3895, "cannot get categories from data field, no data has been read yet", "" ) );
98
99 else {
100 ix = 0;
101
102 /* datafield=xxxxx */
103 strcpy( fieldspec, GL_getok( inbuf, &ix ) );
104 if( GL_smember( fieldspec, "datafield datafields" )) /* handle old syntax 'datafield[s] xxx' */
105 strcpy( fname, GL_getok( inbuf, &ix ) );
106 else strcpy( fname, &fieldspec[10] );
107
108 /* optional selectrows=xxx xx xxx */ /* scg 2/28/02 */
109 while( isspace( (int) inbuf[ix] ) && inbuf[ix] != '\0' ) ix++ ; /* advance */
110 ixhold = ix;
111 strcpy( buf, GL_getok( inbuf, &ix ) );
112 if( strnicmp( buf, "selectrows=", 11 )==0 ) strcpy( selex, &inbuf[ixhold+11] );
113
114 df = fref( fname );
115
116 if( !dont_init_ncats[ axi ] ) ncats[ axi ] = 0;
117
118 for( i = 0; i < Nrecords; i++ ) {
119
120 if( selex[0] != '\0' ) { /* process against selection condition if any.. */
121 stat = do_select( selex, i, &result );
122 if( stat != 0 ) { Eerr( stat, "selectrows error", selex ); continue; }
123 if( result == 0 ) continue; /* reject */
124 }
125
126
127 t = da( i, df-1 );
128 tlen = strlen( t );
129
130 if( check_uniq[ axi ] ) {
131 /* make sure we don't have it already.. */
132 for( j = 0; j < ncats[ axi ]; j++ ) {
133 if( stricmp( cats[ axi ][j], t ) ==0 ) break;
134 }
135 }
136 else j = ncats[ axi ];
137
138 if( j == ncats[ axi ] ) { /* only add it if not yet seen.. */
139
140 if( ncats[ axi ] >= req_ncats[ axi ] )
141 return( Eerr( 4824, "category list is full, some entries ignored (use proc categories to raise)", "" ));
142
143 s = (char *) malloc( tlen+1 );
144 cats[ axi ][ ncats[ axi ]] = s;
145 strcpy( s, t );
146 ncats[ axi ]++;
147 }
148 }
149 }
150 }
151
152 else { /* fill cats list from literal text chunk.. */
153 if( !dont_init_ncats[ axi ] ) ncats[ axi ] = 0;
154
155
156 textloc = 0;
157 inbuflen = strlen( inbuf );
158 while( 1 ) {
159 if( textloc >= inbuflen ) break;
160 GL_getseg( buf, inbuf, &textloc, "\n" );
161 buflen = strlen( buf );
162
163
164 if( check_uniq[ axi ] ) {
165 /* make sure we don't have it already.. added scg 6/1/06 */
166 for( j = 0; j < ncats[ axi ]; j++ ) {
167 if( stricmp( cats[ axi ][j], buf ) ==0 ) break;
168 }
169 }
170 else j = ncats[ axi ];
171
172 if( j == ncats[ axi ] ) { /* only add it if not yet seen.. */ /* added scg 6/1/06 */
173 if( ncats[ axi ] >= MAXNCATS )
174 return( Eerr( 4825, "category list is full, some entries ignored (use proc categories to raise)", "" ));
175 s = (char *) malloc( buflen+1 );
176 cats[ axi ][ ncats[ axi ]] = s;
177 strcpy( s, buf );
178 ncats[ axi ]++;
179 }
180 }
181 }
182 dont_init_ncats[ axi ] = 0; /* for future go-rounds */
183 nextcat[ axi ] = 0;
184 curcat[ axi ] = 0;
185
186 if( PLS.debug ) fprintf( PLS.diagfp, "categories in %c: setting up %d categories\n", ax, ncats[axi] );
187
188 /* fprintf( PLS.diagfp, "[cat axis=%d ncats=%d]", axi, ncats[axi] );
189 * for( i = 0; i < ncats[axi]; i++ ) fprintf( PLS.diagfp, "[%s]", cats[axi][i] );
190 * fprintf( PLS.diagfp, "\n" );
191 */
192
193
194 return( 0 );
195 }
196
197 /* ======================================================= */
198 /* ADDCAT - prepend or append a category to the cat list */
199 /* If prepend, this must be called before main cats list is set up */
200 int
PL_addcat(ax,pos,name)201 PL_addcat( ax, pos, name )
202 char ax; /* 'x' or 'y' */
203 char *pos; /* "pre" or "post" */
204 char *name; /* category name */
205 {
206 int axi, buflen;
207 char *s;
208
209 if( ax == 'x' ) axi = 0;
210 else axi = 1;
211
212 buflen = strlen( name );
213
214 if( strcmp( pos, "pre" )==0 ) {
215 if( !sys_init[axi] ) {
216 cats[axi] = (char **) malloc( req_ncats[axi] * sizeof( char *) );
217 sys_init[axi] = 1;
218 ncats[axi] = 0;
219 }
220 if( ! dont_init_ncats[ axi ] ) ncats[ axi ] = 0;
221 dont_init_ncats[ axi ] = 1;
222 }
223 if( strcmp( pos, "post" )==0 ) dont_init_ncats[ axi ] = 0;
224
225 s = (char *) malloc( buflen+1 );
226 cats[ axi ][ ncats[ axi ]] = s;
227 strcpy( s, name );
228 ncats[ axi ]++;
229 return( 0 );
230 }
231
232
233 /* =============================================== */
234 /* NEXTCAT - for getting categories sequentially.. get next category in list.
235 nextcat var maintains current position. */
236 int
PL_nextcat(ax,result,maxlen)237 PL_nextcat( ax, result, maxlen )
238 char ax;
239 char *result;
240 int maxlen;
241 {
242 int axi;
243 if( ax == 'x' ) axi = 0;
244 else axi = 1;
245
246 if( nextcat[ axi ] >= ncats[ axi ] ) { strcpy( result, "" ); return( 0 ); }
247
248 strncpy( result, cats[ axi ][ nextcat[ axi] ], maxlen );
249 result[maxlen] = '\0';
250 nextcat[ axi ]++;
251 return( 0 );
252 }
253
254 /* ================================================ */
255 /* GETCAT - get category name for slot n */
256 int
PL_getcat(ax,n,result,maxlen)257 PL_getcat( ax, n, result, maxlen )
258 char ax;
259 int n;
260 char *result; /* changed to strcpy into a buffer, scg 8/4/04 */
261 int maxlen;
262 {
263 int axi;
264 if( ax == 'x' ) axi = 0;
265 else axi = 1;
266 if( n >= ncats[ axi ] ) return( 1 );
267 else strncpy( result, cats[ axi ][ n ], maxlen );
268 result[ maxlen ] = '\0';
269 return( 0 );
270 }
271
272 /* ================================================ */
273 /* NCATS - return number of categories for an axis */
274 int
PL_ncats(ax)275 PL_ncats( ax )
276 char ax;
277 {
278 int axi;
279 if( ax == 'x' ) axi = 0;
280 else axi = 1;
281 return( ncats[ axi ] );
282 }
283
284
285 /* ================================================ */
286 /* FINDCAT - category look up. Return slot (0 .. max) or -1 if not found */
287 /* roundrobin search option is more efficient when categories will tend to be accessed in order */
288 int
PL_findcat(ax,s)289 PL_findcat( ax, s )
290 char ax, *s;
291 {
292 int axi, j, slen, recurs;
293 if( ax == 'x' ) axi = 0;
294 else axi = 1;
295
296 recurs = 0;
297 slen = strlen( s );
298
299 if( roundrobin[ axi ] ) {
300 j = curcat[ axi ];
301 if( j == -1 ) { recurs = 1; j = curcat[axi] = 0; }
302 }
303 else j = 0;
304
305 /* contains */
306 if( catcompmethod[axi] == CONTAINS ) { for( ; j < ncats[ axi ]; j++ ) { if( strnicmp( s, cats[axi][j], slen )==0 ) break; }}
307
308 /* exact */
309 else if( catcompmethod[axi] == EXACT ) { for( ; j < ncats[ axi ]; j++ ) { if( stricmp( s, cats[axi][j] )==0 ) break; }}
310
311 /* specified length */
312 else if( catcompmethod[axi] > 0 ) { for( ; j < ncats[ axi ]; j++ ) { if( strnicmp( s, cats[axi][j], catcompmethod[axi] )==0 ) break; }}
313
314 if( j >= ncats[ axi ] ) {
315 if( roundrobin[ axi ] && !recurs ) {
316 /* if working in round robin mode and we reach the end of the list,
317 we need to search ONE more time from beginning of list.. */
318 curcat[ axi ] = -1;
319 return( PL_findcat( ax, s ) );
320 }
321 return( -1 );
322 }
323 else {
324 curcat[ axi ] = j;
325 return( j );
326 }
327 }
328
329 /* ================================================ */
330 /* SETCATPARMS - set the category comparison method */
331 int
PL_setcatparms(ax,what,parm)332 PL_setcatparms( ax, what, parm )
333 char ax;
334 char *what;
335 int parm;
336 {
337 int axi;
338 if( ax == 'x' ) axi = 0;
339 else axi = 1;
340
341 if( strcmp( what, "compmethod" )==0 ) catcompmethod[axi] = parm;
342 else if( strcmp( what, "listsize" )==0 ) {
343 if( sys_init[axi] ) return( Eerr( 2750, "categories already defined; listsize ignored", "" ));
344 req_ncats[axi] = parm;
345 }
346 else if( strcmp( what, "checkuniq" )==0 ) check_uniq[axi] = parm;
347 else if( strcmp( what, "roundrobin" )==0 ) roundrobin[axi] = parm;
348 return( 0 );
349 }
350
351 /* ======================================================= *
352 * Copyright 1998-2005 Stephen C. Grubb *
353 * http://ploticus.sourceforge.net *
354 * Covered by GPL; see the file ./Copyright for details. *
355 * ======================================================= */
356