1 /* ======================================================= *
2  * Copyright 1998-2008 Stephen C. Grubb                    *
3  * http://ploticus.sourceforge.net                         *
4  * Covered by GPL; see the file ./Copyright for details.   *
5  * ======================================================= */
6 
7 #include "pl.h"
8 #include "tdhkit.h"
9 
10 #define NONDRAWINGPROCS "page print originaldata usedata tabulate getdata boxplot catslide processdata settings endproc"
11 
12 #define MAXMALLOCATTRS 30   /* max # of multiline attributes per proc including clones */
13 
14 #define GETTING 0
15 #define SCANNING 1
16 
17 
18 static int execline_init = 0;
19 static int lastbs; 	      /* indicates that previous line ended with a backslash, indicating continuation.. */
20 static char procname[NAMEMAXLEN];
21 static char saveas_name[NAMEMAXLEN];
22 static char last_proctok[20]; /* either #proc or #procdef */
23 static char clone_name[NAMEMAXLEN];
24 static int nlhold;
25 static char clonelist[200];
26 static int holdmemflag = 0;
27 static int prevlineblank = 0;	/* prevent -echo from spitting out lots of adjacent blank lines */
28 static char *mem;
29 static int nfl = 0;
30 static char *malloclist[MAXMALLOCATTRS]; /* list of malloced multiline items */
31 static int procstop; 		/* communicates end of proc w/ getmultiline  */
32 
33 
34 static int proc_call(), initproc();
35 
36 /* ================================================================= */
37 int
PL_execline_initstatic()38 PL_execline_initstatic()
39 {
40 execline_init = 0;
41 holdmemflag = 0;
42 prevlineblank = 0;
43 /* don't reset nfl here.. let new job free the list */
44 return( 0 );
45 }
46 
47 /* ================================================================= */
48 /* EXECLINE - execute one ploticus script line.
49  * Returns 0 if ok, or a non-zero error code
50  */
51 
52 int
PL_execline(line)53 PL_execline( line )
54 char *line;	/* line of script file.. */   /* if const, no trailing newline and no #ifspec! (these modify line) */
55 {
56 int i, ix, stat;
57 char firsttok[50], buf2[50];
58 int buflen, endproc, procstat;
59 
60 
61 if( !execline_init ) {
62 	PLS.npages = 0;
63 	lastbs = 0;
64 	strcpy( saveas_name, "" );
65 	strcpy( last_proctok, "" );
66 	strcpy( procname, "" );
67 	strcpy( clonelist, "" );
68 
69 	/* proc line initializations.. */
70 	PLL.nobj = 0;
71 	PLL.nlines = nlhold = 0;
72 
73 	execline_init = 1;
74 	}
75 
76 if( PLS.skipout ) return( 1 ); /* a major error has occured;  don't process any more lines.. just return */
77 
78 buflen = strlen( line );
79 
80 
81 /* remove newlines cr/lf and trailing whitespace from every line... added scg 11/9/07 */
82 for( i = buflen-1; i >= 0; i-- ) if ( !isspace( (int) line[i] )) break;
83 line[i+1] = '\0';
84 buflen = i+1;
85 /* was... */
86 /* if( line[ buflen-1 ] == '\n' ) { line[ buflen-1 ] = '\0'; buflen--; } */ /* don't keep trailing newline.. */
87 /* if( line[ buflen-1 ] == 13 ) { line[ buflen-1] = '\0'; buflen--; } */ /* DOS LF */
88 
89 ix = 0;
90 
91 /* get first token in line.. changed to handle potentially long lines having no whitespace eg. long CSV data lines .. scg 10/25/07 */
92 strncpy( buf2, line, 40 ); buf2[40] = '\0';
93 strcpy( firsttok, GL_getok( buf2, &ix ) );
94 
95 if( PLS.echolines && strcmp( firsttok, "#ifspec" )!= 0 ) {
96 	if( prevlineblank && firsttok[0] == '\0' ); /* multiple blank lines.. don't output */
97 	else if( PLS.echolines == 1 ) { printf( "%s\n", line ); fflush( stdout ); }
98         else if( PLS.echolines == 2 ) { fprintf( PLS.diagfp, "%s\n", line ); fflush( PLS.diagfp ); }
99 	if( firsttok[0] == '\0' ) prevlineblank = 1;
100 	else prevlineblank = 0;
101 	}
102 
103 
104 
105 /* intercept #endproc.. */
106 endproc = 0;
107 if( strcmp( firsttok, "#endproc" )==0 ) {
108 	strcpy( firsttok, "#proc" );
109 	endproc = 1;
110 
111 	/* and add an additional blank line to terminate any last multline item.. */
112 	/* nlines>0 was added .. this caused seg fault on degenerate api case of 0 script lines 5/29/03 */
113 	if( PLL.nlines > 0 && PLL.nlines < PLL.maxproclines-1 ) {
114 		PLL.procline[ PLL.nlines ] = (char *) malloc( 5 );
115 		strcpy( PLL.procline[ PLL.nlines ], "\n" );
116         	(PLL.nlines)++;
117 		}
118 	}
119 
120 
121 /*** #proc(def): get ready to capture next proc, and execute the proc that has just been read... */
122 if( strncmp( firsttok, "#proc", 5 )==0 && !lastbs ) {  /* #proc or #procdef break */
123 
124 	procstat = 0;
125 
126 	/* if #saveas was used, record the name.. */
127 	if( saveas_name[0] != '\0' )  strcpy( PLL.objname[ PLL.nobj ], saveas_name );
128 
129 	/* get line count.. */
130 	PLL.objlen[ PLL.nobj ] = PLL.nlines - nlhold;
131 
132 	/* if not first time around, and #proc was used (as opposed to #procdef), execute proc.. */
133 	if( strcmp( last_proctok, "#proc" )==0 ) {
134 
135 		/* proc page can do the Einit, or we can do it here.. */
136 		if( strcmp( procname, "page" )==0 ) PLS.eready = 1;
137 		if( !PLS.eready && !GL_slmember( procname, NONDRAWINGPROCS )) {
138 			stat = Einit( PLS.device );
139 			if( stat ) { PLS.skipout = 1; return( stat ); }
140 			Epaper( PLS.landscape );
141 			PLS.eready = 1;
142 
143 			/* we are ready to draw.. safe to say page 1.. scg 11/28/00 */
144 			if( strcmp( procname, "page" )!=0 ) PLS.npages = 1;
145 			if( PLS.bkcolorgiven ) {
146 				/* EPS color=transparent - best to do nothing.. scg 1/10/00*/
147 				if( PLS.device == 'e' && strcmp( Ecurbkcolor, "transparent" )==0 ) ;
148 				else Eclr();
149 				}
150 			}
151 
152 
153 		/* execute the appropriate plotting procedure... */
154 		procstat = proc_call( procname );
155 
156 		if( PLS.eready ) Eflush();  /* get output onto screen.. */
157 		}
158 
159 	if( endproc ) strcpy( procname, "endproc" );
160 	else if( sscanf( line, "%*s %s", procname ) < 1 ) {
161 		Eerr( 24, "#proc must be followed by a procedure name", "" );
162 		procstat = 24;
163 		}
164 
165 
166 
167 	/* if we're not told to hold on to the memory (used by getdata), and if
168 	   there were no #saveas.., then free the proc lines now..*/
169 	if( !holdmemflag && saveas_name[0] == '\0' ) {
170 		for( i = nlhold; i < PLL.nlines; i++ ) free( PLL.procline[i] );
171 	 	PLL.nlines = nlhold;
172 		}
173 	else 	{
174 		if( PLL.nobj >= MAXOBJ-1 ) {
175 			Eerr( 25, "too many active procs - see limitations page MAXOBJ", "" );
176 			procstat = 25;
177 			}
178 		else (PLL.nobj)++;
179 		}
180 	holdmemflag = 0;
181 
182 	strcpy( last_proctok, firsttok );
183 
184 	if( procname[ strlen( procname ) - 1 ] == ':' ) procname[ strlen( procname ) - 1 ] = '\0';
185 
186 
187 	/* initialize to capture next proc */
188 	strcpy( saveas_name, "" );
189 	strcpy( clonelist, "" );
190 	strcpy( PLL.objname[ PLL.nobj ], "" );
191 	PLL.objstart[ PLL.nobj ] = PLL.nlines;
192 	nlhold = PLL.nlines;
193 
194 	return( procstat );
195 	}
196 
197 
198 /****** for all other lines, get them, and add them to proc line list, looking for
199  ****** special cases such as #clone and #saveas..  */
200 
201 else 	{
202 
203 	if( firsttok[0] == '#' && firsttok[1] != '#' ) {
204 		if( strncmp( firsttok, "#clone", 6 ) != 0 &&
205 		    strncmp( firsttok, "#saveas", 7 ) != 0 &&
206 		    strncmp( firsttok, "#ifspec", 7 ) != 0 ) Eerr( 57468, "unrecognized operator", firsttok );
207 		}
208 
209 	if( procname[0] == '\0' ) return( 0 ); /* ? */
210 	else 	{
211 		/* add lines to proc line list.. */
212 		/* also look for exceptions such as "#clone" */
213 
214 		if( !lastbs && strncmp( firsttok, "#clone", 6 )==0 ) {
215 			strcpy( clone_name, "" );
216 			sscanf( line, "%*s %s", clone_name );
217 			if( clone_name[0] == '\0' ) {
218 				Eerr( 27, "#clone object name is missing", procname );
219 				return( 1 );
220 				}
221 			strcat( clonelist, clone_name );
222 			strcat( clonelist, " " );
223 			}
224 
225 		else if( !lastbs && strncmp( firsttok, "#saveas", 7 )==0 ) sscanf( line, "%*s %s", saveas_name );
226 
227 
228 		else 	{
229 
230 			/* #ifspec   scg 10/16/03 */
231 			if( !lastbs && strncmp( firsttok, "#ifspec", 7 )==0 ) {  /* #ifspec varname [attrname] */
232 				int nt;
233 				char varname[50], attrname[50], val[DATAMAXLEN+1];
234 				nt = sscanf( line, "%*s %s %s", varname, attrname );
235 				if( nt == 1 ) strcpy( attrname, varname );
236 				stat = TDH_getvar( varname, val );
237 				if( stat == 0 && val[0] != '\0' ) {
238 					sprintf( line, " %s: %s", attrname, val );
239 					if( PLS.echolines == 1 ) printf( "%s\n", line );
240         				else if( PLS.echolines == 2 ) fprintf( PLS.diagfp, "%s\n", line );
241 					}
242 				else strcpy( line, "" );
243 				buflen = strlen( line );
244 				}
245 
246 			PLL.procline[ PLL.nlines ] = (char *) malloc(  buflen+1 );
247 			strncpy( PLL.procline[ PLL.nlines ], line, buflen );
248 			PLL.procline[ PLL.nlines ][ buflen ] = '\0';
249 
250 			if( PLL.nlines >= PLL.maxproclines-1 ) {
251 				PLS.skipout = 1; /* this is severe enough to abort mission.. */
252 				return( err( 28, "Script file - too many lines in current proc plus saved procs; try raising -maxproclines", "" ));
253 				}
254 			(PLL.nlines)++;
255 			if( line[ buflen - 2 ] == '\\' ) lastbs = 1;
256 			else lastbs = 0;
257 			}
258 		}
259 
260 	return( 0 );
261 	}
262 
263 }
264 
265 /* ========================= */
266 /* HOLDMEM - allow other modules to tell execline() to not free the lines
267    for the current proc.. Used by getdata.
268  */
269 int
PL_holdmem(stat)270 PL_holdmem( stat )
271 int stat;
272 {
273 holdmemflag = stat;
274 return( 0 );
275 }
276 
277 /* ========================= */
278 /* PROC_CALL - call the appropriate proc routine */
proc_call(procname)279 static int proc_call( procname )
280 char *procname;
281 {
282 int stat;
283 int n;
284 
285 stat = 0;
286 
287 initproc(); /* initialize attribute malloc stuff for this proc  (see below) */
288 
289 if( PLS.debug ) {
290 	if( strcmp( procname, "endproc" )==0 ) { fprintf( PLS.diagfp, "(endproc)\n" ); fflush( PLS.diagfp ); }
291 	else fprintf( PLS.diagfp, "Executing %s\n", procname ); fflush( PLS.diagfp );
292 	}
293 
294 if( strcmp( procname, "areadef" )==0 ) {
295 	stat = PLP_areadef();
296 	if( stat != 0 ) {
297 		PLS.skipout = 1;
298 		return( Eerr( 10, "cannot set up plotting area .. likely culprits: bad xrange or yrange, or bad area rectangle", "" ));
299 		}
300 	}
301 else if( strcmp( procname, "page" )==0 ) {
302 	stat = PLP_page();
303 	if( stat ) { PLS.skipout = 1; return( stat ); }
304 	}
305 else if( strcmp( procname, "xaxis" )==0 ) stat = PLP_axis( 'x', 0 );
306 else if( strcmp( procname, "yaxis" )==0 ) stat = PLP_axis( 'y', 0 );
307 else if( strcmp( procname, "getdata" )==0 ) stat = PLP_getdata();
308 else if( strcmp( procname, "categories" )==0 ) stat = PLP_categories( 0 );
309 else if( strcmp( procname, "legend" )==0 ) stat = PLP_legend();
310 else if( strcmp( procname, "bars" )==0 ) stat = PLP_bars();
311 else if( strcmp( procname, "scatterplot" )==0 ) stat = PLP_scatterplot();
312 else if( strcmp( procname, "pie" )==0 ) stat = PLP_pie();
313 else if( strcmp( procname, "lineplot" )==0 ) stat = PLP_lineplot();
314 else if( strcmp( procname, "rangesweep" )==0 ) stat = PLP_rangesweep();
315 else if( strcmp( procname, "boxplot" )==0 ) stat = PLP_boxplot();
316 else if( strcmp( procname, "annotate" )==0 ) stat = PLP_annotate();
317 else if( strcmp( procname, "processdata" )==0 ) stat = PLP_processdata();
318 else if( strcmp( procname, "catlines" )==0 ) stat = PLP_catlines();
319 else if( strcmp( procname, "curvefit" )==0 ) stat = PLP_curvefit();
320 else if( strcmp( procname, "vector" )==0 ) stat = PLP_vector();
321 else if( strcmp( procname, "usedata" )==0 ) stat = PLP_usedata();
322 else if( strcmp( procname, "legendentry" )==0 ) stat = PLP_legendentry();
323 else if( strcmp( procname, "line" )==0 ) stat = PLP_line();
324 else if( strcmp( procname, "rect" )==0 ) stat = PLP_rect();
325 else if( strcmp( procname, "tree" )==0 ) stat = PLP_tree();
326 else if( strcmp( procname, "venndisk" )==0 ) stat = PLP_venndisk();
327 else if( strcmp( procname, "settings" )==0 ) stat = PLP_settings();
328 else if( strcmp( procname, "breakaxis" )==0 ) stat = PLP_breakaxis();
329 else if( strcmp( procname, "image" )==0 ) stat = PLP_image();
330 else if( strcmp( procname, "drawcommands" )==0 ) stat = PLP_drawcommands();
331 else if( strcmp( procname, "tabulate" )==0 ) stat = PLP_tabulate();
332 else if( strcmp( procname, "symbol" )==0 ) stat = PLP_symbol();
333 else if( strcmp( procname, "print" )==0 ) stat = PLP_print();
334 
335 else if( strcmp( procname, "trailer" )==0 ) ; /* do nothing */
336 else if( strcmp( procname, "endproc" )==0 ) ; /* do nothing */
337 
338 else if( strcmp( procname, "catslide" )==0 ) stat = PLP_categories( 0 ); /* maps to: proc categories */
339 else if( strcmp( procname, "transform" )==0 ) stat = PLP_processdata(); /* maps to: proc processdata */
340 else if( strcmp( procname, "originaldata" )==0 ) stat = PLP_usedata();  /* maps to: proc usedata */
341 else if( strcmp( procname, "bevelrect" )==0 ) stat = PLP_rect();        /* maps to: proc rect */
342 else if( strcmp( procname, "import" )==0 ) stat = PLP_image(); 	/* maps to: proc image */
343 else if( strcmp( procname, "datesettings" )==0 ) stat = PLP_settings(); /* maps to: proc settings */
344 
345 else if( strcmp( procname, "rangebar" )==0 ) return( Eerr( 27925, "proc rangebar has been replaced with proc boxplot", "" ) );
346 else if( strcmp( procname, "defineunits" )==0 ) return( Eerr( 27926, "proc defineunits discontinued; use proc areadef", "xnewunits and ynewunits" ));
347 
348 else return( Eerr( 101, "procedure name unrecognized", procname ) );
349 
350 TDH_errprog( "pl" );
351 
352 if( PLS.eready ) Eflush();
353 n = report_convmsgcount();
354 if( PLS.debug && n > 0 ) {
355 	fprintf( PLS.diagfp, "note: ploticus proc %s encountered %d unplottable data values\n", procname, n );
356 	zero_convmsgcount();
357 	}
358 return( stat );
359 }
360 
361 /* ================================================================= */
362 /* GETNEXTATTR - serve up the next proc line, or NULL if no more */
363 /* This function returns a pointer to the proc line, and returns some values in the parameters. */
364 
365 char *
PL_getnextattr(firsttime,attr,valpos)366 PL_getnextattr( firsttime, attr, valpos )
367 int firsttime;	/* 1 = first call for proc */
368 char *attr;	/* returned: attribute name */
369 int *valpos;    /* returned: char position in the string returned by this function, where value content begins */
370 {
371 static int cloneix, state;
372 static char *line;
373 int j, ix, alen;
374 char clone_name[NAMEMAXLEN];
375 
376 /* states:  0 = init   1 = getting clone  2 = getting proc  3 = done */
377 
378 if( firsttime ) { state = 0; cloneix = 0; }
379 
380 if( state == 3 ) {
381 	line = NULL;
382 	return( line );
383 	}
384 
385 if( state == 0 ) {
386 	RETRY:
387 	strcpy( clone_name, GL_getok( clonelist, &cloneix ));
388 	if( clone_name[0] != '\0' ) {
389 		/* look up obj in list, starting with latest entry and working backward.. */
390 		for( j = (PLL.nobj)-1; j >= 0; j-- ) if( strcmp( PLL.objname[j], clone_name )==0 ) break;
391 		if( j < 0 ) {
392 			Eerr( 2506, "#clone object not found", clone_name );
393 			goto RETRY;
394 			}
395 		PLL.curline = PLL.objstart[j];
396 		procstop = PLL.objstart[j] + PLL.objlen[j];
397 		state = 1;
398 		}
399 	else 	{
400 		PLL.curline = PLL.objstart[ PLL.nobj ];
401 		procstop = PLL.nlines;
402 		state = 2;
403 		}
404 	}
405 
406 if( state == 1 || state == 2 ) {
407 	RETRY2:
408 	if( PLL.curline >= PLL.nlines ) return( NULL );
409 	line = PLL.procline[ PLL.curline ];
410 	ix = 0;
411 
412 	strncpy( attr, GL_getok( line, &ix ), 38 );   /* get 1st token (truncate at 38 chars) */
413 	attr[38] = '\0';
414 
415 	if( attr[0] == '\0' ) {   /* blank line.. skip */
416 		(PLL.curline)++;
417 		if( PLL.curline >= procstop && state == 1 ) { state = 0; goto RETRY; }
418 		else if( PLL.curline >= procstop && state == 2 ) { state = 3; return( NULL ); }
419 		else goto RETRY2;
420 		}
421 	alen = strlen( attr );
422 	if( attr[ alen-1 ] == ':' ) attr[ alen-1 ] = '\0';
423 	if( attr[0] != '\0' ) while( isspace( (int) line[ix] )) ix++; /* skip over ws */
424 	*valpos = ix;
425 
426 	PLL.curline++;
427 	if( PLL.curline >= procstop ) {
428 		if( state == 1 ) state = 0;
429 		else state = 3;
430 		}
431 
432 	return( line );
433 	}
434 return( NULL );
435 }
436 
437 /* ================================================================= */
438 /* GETMULTILINE - get a multi-line text item from script file.  Terminates when first empty line is encountered.
439 	If mode == "get", sufficient memory is malloc'ed, the text is copied into it, and function returns pointer to text.
440 	If mode == "skip", we simply advance to the end of the multiline text (see proc_getdata)
441 */
442 
443 char *
PL_getmultiline(firstline,mode)444 PL_getmultiline( firstline, mode )
445 char *firstline; /* first row of data, without attribute name */
446 char *mode;  /* either "get" or "skip" */
447 {
448 char *line;
449 int i, iline;
450 int txtlen, txtstartline, txtstopline, memlen, emptyline;
451 
452 txtstartline = PLL.curline;
453 
454 /* first, scan thru all rows to get count of total # chars... */
455 txtlen = strlen( firstline );
456 
457 /* go until we hit an empty line, or reach end of proc.. */
458 for( iline = txtstartline; iline <= procstop ; iline++ ) {
459 	line = PLL.procline[ iline ];
460 	for( i = 0, emptyline = 1; line[i] != '\0'; i++ ) if( !isspace( (int) line[i] )) { emptyline = 0; break; }
461 	if( emptyline ) break;
462 	if( mode[0] == 'g' ) txtlen += (strlen( &line[i] ) + 2);  /* mode = "get", accumulate length sans leading ws */
463 	}
464 
465 /* remember where we stopped.. */
466 txtstopline = iline;
467 PLL.curline = iline;  /* so scanner can resume at the right place.. */
468 
469 if( mode[0] == 's' ) return( 0 );  /* mode = "skip" */
470 
471 mem = malloc( txtlen+2 * sizeof( char *) );
472 if( mem == (char *)NULL ) { PLS.skipout = 1; Eerr( 27509, "multiline malloc failed", "" ); return( "" ); }
473 malloclist[nfl++] = mem;
474 
475 memlen = 0;
476 
477 /* copy first line content.. */
478 for( i = 0; firstline[i] != '\0'; i++ ) if( !isspace( (int) firstline[i] )) break; /* skip leading ws */
479 if( firstline[i] != '\0' ) {
480 	sprintf( mem, "%s\n", &firstline[i] );
481 	memlen = strlen( &firstline[i] ) + 1;
482 	}
483 
484 
485 /* now fill mem.. */
486 for( iline = txtstartline; iline < txtstopline && iline <= procstop; iline++ ) {
487 	line = PLL.procline[ iline ];
488 
489 	/* skip over leading whitespace as well as any leading backslash.. */
490 	for( i = 0; line[i] != '\0'; i++ ) if( !isspace( (int) line[i] )) break;
491 	if( line[i] == '\\' ) i++;
492 
493 	strcpy( &mem[memlen], &line[i] );
494 	memlen += strlen( &line[i] );
495 	mem[ memlen++ ] = '\n';
496 	mem[ memlen ] = '\0';
497 	}
498 return( mem );
499 }
500 
501 
502 
503 /* ========================================= */
504 /* TOKNCPY - copy 1st token of lineval into val, up to maxlen (maxlen should be same as var declaration size) */
505 int
PL_tokncpy(val,lineval,maxlen)506 PL_tokncpy( val, lineval, maxlen )
507 char *val, *lineval;
508 int maxlen;
509 {
510 int i;
511 for( i = 0; i < maxlen-1; i++ ) {
512 	if( isspace( (int)lineval[i] )) break;
513 	val[i] = lineval[i];
514 	}
515 val[i] = '\0';
516 if( i == (maxlen-1) ) return( 1 );
517 else return( 0 );
518 }
519 
520 /* ======================================== */
521 /* ITOKNCOPY - do tokncpy and convert to integer using atoi() */
522 int
PL_itokncpy(lineval)523 PL_itokncpy( lineval )
524 char *lineval;
525 {
526 char val[80];
527 tokncpy( val, lineval, 80 );
528 return( atoi( val ) );
529 }
530 
531 /* ======================================== */
532 /* FTOKNCOPY - do tokncpy and convert to float using atof() */
533 double
PL_ftokncpy(lineval)534 PL_ftokncpy( lineval )
535 char *lineval;
536 {
537 char val[80];
538 tokncpy( val, lineval, 80 );
539 return( atof( val ) );
540 }
541 
542 
543 
544 #ifdef HOLD
545 /* ========================================= */
546 /* NEWATTR - malloc some memory for an attribute value, and copy the attribute value into it. */
547 char *
PL_newattr(lineval,len)548 PL_newattr( lineval, len )
549 char *lineval;
550 int len;
551 {
552 if( nfl >= MAXMALLOCATTRS-1 ) { PLS.skipout = 1; Eerr( 29, "too many malloced attributes in this proc", "" ); return( "" ); }
553 if( len < 1 ) len = strlen( lineval );
554 mem = malloc( len+2 * sizeof( char *) );
555 if( mem == (char *)NULL ) { PLS.skipout = 1; Eerr( 27508, "newattr malloc failed", "" ); return( "" ); }
556 malloclist[nfl++] = mem;
557 strncpy( mem, lineval, len );
558 mem[len] = '\0';
559 return( mem );
560 }
561 #endif
562 
563 
564 /* =========================================== */
565 /* INITPROC - free all currently malloc'ed attr memory (if any) and initialize for next proc  */
566 static int
initproc()567 initproc()
568 {
569 int i;
570 for( i = 0; i < nfl; i++ ) free( malloclist[i] );
571 nfl = 0;
572 return( 0 );
573 }
574 
575 
576 
577 
578 /* ======================================================= *
579  * Copyright 1998-2008 Stephen C. Grubb                    *
580  * http://ploticus.sourceforge.net                         *
581  * Covered by GPL; see the file ./Copyright for details.   *
582  * ======================================================= */
583