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