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