1 /* $Id: plcore.c,v 1.14 2007/10/24 13:14:43 ajb Exp $
2 
3 	Central dispatch facility for PLplot.
4 	Also contains the PLplot main data structures, external access
5 	routines, and initialization calls.
6 
7 	This stuff used to be in "dispatch.h", "dispatch.c", and "base.c".
8 
9 
10   Copyright (C) 2004  Joao Cardoso
11   Copyright (C) 2004, 2005  Rafael Laboissiere
12   Copyright (C) 2004, 2006  Andrew Ross
13   Copyright (C) 2004  Andrew Roach
14   Copyright (C) 2005  Alan W. Irwin
15   Copyright (C) 2005  Thomas J. Duck
16 
17   This file is part of PLplot.
18 
19   PLplot is free software; you can redistribute it and/or modify
20   it under the terms of the GNU General Library Public License as published
21   by the Free Software Foundation; either version 2 of the License, or
22   (at your option) any later version.
23 
24   PLplot is distributed in the hope that it will be useful,
25   but WITHOUT ANY WARRANTY; without even the implied warranty of
26   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27   GNU Library General Public License for more details.
28 
29   You should have received a copy of the GNU Library General Public License
30   along with PLplot; if not, write to the Free Software
31   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
32 
33 */
34 
35 #define DEBUG
36 
37 #define NEED_PLDEBUG
38 #include "plcore.h"
39 
40 #ifdef ENABLE_DYNDRIVERS
41 #include <ltdl.h>
42 #endif
43 
44 /*--------------------------------------------------------------------------*\
45  * Driver Interface
46  *
47  * These routines are the low-level interface to the driver -- all calls to
48  * driver functions must pass through here.  For implementing driver-
49  * specific functions, the escape function is provided.  The command stream
50  * gets duplicated to the plot buffer here.
51  *
52  * All functions that result in graphics actually being plotted (rather than
53  * just a change of state) are filtered as necessary before being passed on.
54  * The default settings do not require any filtering, i.e.  PLplot physical
55  * coordinates are the same as the device physical coordinates (currently
56  * this can't be changed anyway), and a global view equal to the entire page
57  * is used.
58  *
59  * The reason one wants to put view-specific filtering here is that if
60  * enabled, the plot buffer should receive the unfiltered data stream.  This
61  * allows a specific view to be used from an interactive device (e.g. TCL/TK
62  * driver) but be restored to the full view at any time merely by
63  * reprocessing the contents of the plot buffer.
64  *
65  * The metafile, on the other hand, *should* be affected by changes in the
66  * view, since this is a crucial editing capability.  It is recommended that
67  * the initial metafile be created without a restricted global view, and
68  * modification of the view done on a per-plot basis as desired during
69  * subsequent processing.
70  *
71 \*--------------------------------------------------------------------------*/
72 
73 enum {AT_BOP, DRAWING, AT_EOP};
74 
75 /* Initialize device. */
76 /* The plot buffer must be called last. */
77 
78 /* The following array of chars is used both here and in plsym.c for
79  * translating the Greek characters from the #g escape sequences into
80  * the Hershey and Unicode codings
81  */
82 const char plP_greek_mnemonic[] = "ABGDEZYHIKLMNCOPRSTUFXQWabgdezyhiklmncoprstufxqw";
83 
84 void
plP_init(void)85 plP_init(void)
86 {
87     plsc->page_status = AT_EOP;
88 
89     (*plsc->dispatch_table->pl_init) ((struct PLStream_struct *) plsc);
90 
91     if (plsc->plbuf_write)
92 	plbuf_init(plsc);
93 }
94 
95 /* End of page */
96 /* The plot buffer must be called first. */
97 /* Ignore instruction if already at eop. */
98 
99 void
plP_eop(void)100 plP_eop(void)
101 {
102     int skip_driver_eop = 0;
103 
104     if (plsc->page_status == AT_EOP)
105 	return;
106 
107     plsc->page_status = AT_EOP;
108 
109     if (plsc->plbuf_write)
110 	plbuf_eop(plsc);
111 
112 /* Call user eop handler if present. */
113 
114     if (plsc->eop_handler != NULL)
115 	(*plsc->eop_handler) (plsc->eop_data, &skip_driver_eop);
116 
117     if (!skip_driver_eop)
118 	(*plsc->dispatch_table->pl_eop) ((struct PLStream_struct *) plsc);
119 }
120 
121 /* Set up new page. */
122 /* The plot buffer must be called last. */
123 /* Ignore if already at bop. */
124 /* It's not actually necessary to be AT_EOP here, so don't check for it. */
125 
126 void
plP_bop(void)127 plP_bop(void)
128 {
129     int skip_driver_bop = 0;
130 
131     plP_subpInit();
132     if (plsc->page_status == AT_BOP)
133 	return;
134 
135     plsc->page_status = AT_BOP;
136     plsc->nplwin = 0;
137 
138 /* Call user bop handler if present. */
139 
140     if (plsc->bop_handler != NULL)
141 	(*plsc->bop_handler) (plsc->bop_data, &skip_driver_bop);
142 
143     if (!skip_driver_bop)
144 	(*plsc->dispatch_table->pl_bop) ((struct PLStream_struct *) plsc);
145 
146     if (plsc->plbuf_write)
147 	plbuf_bop(plsc);
148 }
149 
150 /* Tidy up device (flush buffers, close file, etc). */
151 
152 void
plP_tidy(void)153 plP_tidy(void)
154 {
155     if (plsc->tidy) {
156 	(*plsc->tidy) (plsc->tidy_data);
157 	plsc->tidy = NULL;
158 	plsc->tidy_data = NULL;
159     }
160 
161     (*plsc->dispatch_table->pl_tidy) ((struct PLStream_struct *) plsc);
162 
163     if (plsc->plbuf_write) {
164 	plbuf_tidy(plsc);
165     }
166 
167     plsc->OutFile = NULL;
168 
169 }
170 
171 /* Change state. */
172 
173 void
plP_state(PLINT op)174 plP_state(PLINT op)
175 {
176     if (plsc->plbuf_write) plbuf_state(plsc, op);
177 
178     (*plsc->dispatch_table->pl_state) ((struct PLStream_struct *) plsc, op);
179 }
180 
181 /* Escape function, for driver-specific commands. */
182 
183 void
plP_esc(PLINT op,void * ptr)184 plP_esc(PLINT op, void *ptr)
185 {
186   PLINT clpxmi, clpxma, clpymi, clpyma;
187   EscText* args;
188 
189   /* The plot buffer must be called first */
190   if(plsc->plbuf_write) plbuf_esc(plsc, op, ptr);
191 
192   /* Text coordinates must pass through the driver interface filter */
193   if(op==PLESC_HAS_TEXT && plsc->dev_unicode) {
194 
195     /* Apply the driver interface filter */
196     if (plsc->difilt) {
197       args = (EscText*)ptr;
198       difilt(&(args->x),&(args->y),1,&clpxmi,&clpxma,&clpymi,&clpyma);
199     }
200   }
201 
202   (*plsc->dispatch_table->pl_esc) ((struct PLStream_struct *) plsc, op, ptr);
203 }
204 
205 /* Set up plot window parameters. */
206 /* The plot buffer must be called first */
207 /* Some drivers (metafile, Tk) need access to this data */
208 
209 void
plP_swin(PLWindow * plwin)210 plP_swin(PLWindow *plwin)
211 {
212     PLWindow *w;
213     PLINT clpxmi, clpxma, clpymi, clpyma;
214 
215 /* Provide plot buffer with unfiltered window data */
216 
217     if (plsc->plbuf_write)
218 	plbuf_esc(plsc, PLESC_SWIN, (void *) plwin);
219 
220     w = &plsc->plwin[plsc->nplwin++ % PL_MAXWINDOWS];
221 
222     w->dxmi = plwin->dxmi;
223     w->dxma = plwin->dxma;
224     w->dymi = plwin->dymi;
225     w->dyma = plwin->dyma;
226 
227     if (plsc->difilt) {
228 	xscl[0] = plP_dcpcx(w->dxmi);
229 	xscl[1] = plP_dcpcx(w->dxma);
230 	yscl[0] = plP_dcpcy(w->dymi);
231 	yscl[1] = plP_dcpcy(w->dyma);
232 
233 	difilt(xscl, yscl, 2, &clpxmi, &clpxma, &clpymi, &clpyma);
234 
235 	w->dxmi = plP_pcdcx(xscl[0]);
236 	w->dxma = plP_pcdcx(xscl[1]);
237 	w->dymi = plP_pcdcy(yscl[0]);
238 	w->dyma = plP_pcdcy(yscl[1]);
239     }
240 
241     w->wxmi = plwin->wxmi;
242     w->wxma = plwin->wxma;
243     w->wymi = plwin->wymi;
244     w->wyma = plwin->wyma;
245 
246 /* If the driver wants to process swin commands, call it now */
247 /* It must use the filtered data, which it can get from *plsc */
248 
249     if (plsc->dev_swin) {
250 	(*plsc->dispatch_table->pl_esc) ( (struct PLStream_struct *) plsc,
251                                           PLESC_SWIN, NULL );
252     }
253 }
254 
255 /*--------------------------------------------------------------------------*\
256  *  Drawing commands.
257 \*--------------------------------------------------------------------------*/
258 
259 /* Draw line between two points */
260 /* The plot buffer must be called first so it gets the unfiltered data */
261 
262 void
plP_line(short * x,short * y)263 plP_line(short *x, short *y)
264 {
265     PLINT i, npts = 2, clpxmi, clpxma, clpymi, clpyma;
266 
267     plsc->page_status = DRAWING;
268 
269     if (plsc->plbuf_write)
270 	plbuf_line(plsc, x[0], y[0], x[1], y[1]);
271 
272     if (plsc->difilt) {
273 	for (i = 0; i < npts; i++) {
274 	    xscl[i] = x[i];
275 	    yscl[i] = y[i];
276 	}
277 	difilt(xscl, yscl, npts, &clpxmi, &clpxma, &clpymi, &clpyma);
278 	plP_pllclp(xscl, yscl, npts, clpxmi, clpxma, clpymi, clpyma, grline);
279     }
280     else {
281 	grline(x, y, npts);
282     }
283 }
284 
285 /* Draw polyline */
286 /* The plot buffer must be called first */
287 
288 void
plP_polyline(short * x,short * y,PLINT npts)289 plP_polyline(short *x, short *y, PLINT npts)
290 {
291     PLINT i, clpxmi, clpxma, clpymi, clpyma;
292 
293     plsc->page_status = DRAWING;
294 
295     if (plsc->plbuf_write)
296 	plbuf_polyline(plsc, x, y, npts);
297 
298     if (plsc->difilt) {
299 	for (i = 0; i < npts; i++) {
300 	    xscl[i] = x[i];
301 	    yscl[i] = y[i];
302 	}
303 	difilt(xscl, yscl, npts, &clpxmi, &clpxma, &clpymi, &clpyma);
304 	plP_pllclp(xscl, yscl, npts, clpxmi, clpxma, clpymi, clpyma,
305 		   grpolyline);
306     }
307     else {
308 	grpolyline(x, y, npts);
309     }
310 }
311 
312 /* Fill polygon */
313 /* The plot buffer must be called first */
314 /* Here if the desired area fill capability isn't present, we mock up */
315 /* something in software */
316 
317 static int foo;
318 
319 void
plP_fill(short * x,short * y,PLINT npts)320 plP_fill(short *x, short *y, PLINT npts)
321 {
322     PLINT i, clpxmi, clpxma, clpymi, clpyma;
323 
324     plsc->page_status = DRAWING;
325 
326     if (plsc->plbuf_write) {
327 	plsc->dev_npts = npts;
328 	plsc->dev_x = x;
329 	plsc->dev_y = y;
330 	plbuf_esc(plsc, PLESC_FILL, NULL);
331     }
332 
333 /* Account for driver ability to do fills */
334 
335     if (plsc->patt == 0 && ! plsc->dev_fill0) {
336 	if ( ! foo) {
337 	    plwarn("Driver does not support hardware solid fills, switching to software fill.\n");
338 	    foo = 1;
339 	}
340 	plsc->patt = 8;
341 	plpsty(plsc->patt);
342     }
343     if (plsc->dev_fill1) {
344 	plsc->patt = -ABS(plsc->patt);
345     }
346 
347 /* Perform fill.  Here we MUST NOT allow the software fill to pass through the
348    driver interface filtering twice, else we get the infamous 2*rotation for
349    software fills on orientation swaps.
350 */
351 
352     if (plsc->patt > 0)
353 	plfill_soft(x, y, npts);
354 
355     else {
356 	if (plsc->difilt) {
357 	    for (i = 0; i < npts; i++) {
358 		xscl[i] = x[i];
359 		yscl[i] = y[i];
360 	    }
361 	    difilt(xscl, yscl, npts, &clpxmi, &clpxma, &clpymi, &clpyma);
362 	    plP_plfclp(xscl, yscl, npts, clpxmi, clpxma, clpymi, clpyma,
363 		       grfill);
364 	}
365 	else {
366 	    grfill(x, y, npts);
367 	}
368     }
369 }
370 
371 /* Account for driver ability to draw text itself */
372 /*
373 #define DEBUG_TEXT
374 */
375 
376 #define hex2dec( a ) isdigit(a) ? a - 48 : (toupper(a) - 65) + 10
377 
378 int text2num( const char *text, char end, PLUNICODE *num);
379 int text2fci( const char *text, unsigned char *hexdigit,
380 	     unsigned char *hexpower);
381 
382 
383 /*--------------------------------------------------------------------------*\
384  *  int text2num( char *text, char end, PLUNICODE *num)
385  *       char *text - pointer to the text to be parsed
386  *       char end   - end character (i.e. ')' or ']' to stop parsing
387  *       PLUNICODE *num - pointer to an PLUNICODE to store the value
388  *
389  *    Function takes a string, which can be either hex or decimal,
390  *    and converts it into an PLUNICODE, stopping at either a null,
391  *    or the character pointed to by 'end'. It is a bit brain-dead,
392  *    and probably should make more checks, but it works.
393 \*--------------------------------------------------------------------------*/
394 
text2num(const char * text,char end,PLUNICODE * num)395 int text2num( const char *text, char end, PLUNICODE *num)
396 {
397   int base=10;
398   unsigned short i=0;
399   *num=0;
400 
401   if (text[1]=='x')
402     {
403       base=16;
404       i=2;
405     }
406 
407   while ((text[i]!=end)&&(text[i]!=0))
408     {
409       *num*=base;
410       *num+=hex2dec(text[i]);
411       i++;
412     }
413   return(i);
414 }
415 
416 /*--------------------------------------------------------------------------*\
417  *  int text2fci( char *text, unsigned char *hexdigit, unsigned char *hexpower)
418  *       char *text - pointer to the text to be parsed
419  *       unsigned char *hexdigit - pointer to hex value that is stored.
420  *       unsigned char *hexpower - pointer to hex power (left shift) that is stored.
421  *
422  *    Function takes a pointer to a string, which is looked up in a table
423  *    to determine the corresponding FCI (font characterization integer)
424  *    hex digit value and hex power (left shift).  All matched strings
425  *    start with "<" and end with the two characters "/>".
426  *    If the lookup succeeds, hexdigit and hexpower are set to the appropriate
427  *    values in the table, and the function returns the number of characters
428  *    in text that are consumed by the matching string in the table lookup.
429  *
430  *    If the lookup fails, hexdigit is set to 0, hexpower is set to and
431  *    impossible value, and the function returns 0.
432 \*--------------------------------------------------------------------------*/
433 
text2fci(const char * text,unsigned char * hexdigit,unsigned char * hexpower)434 int text2fci( const char *text, unsigned char *hexdigit, unsigned char *hexpower)
435 {
436    typedef struct
437      {
438 	const char *ptext;		/* pmr: const */
439 	unsigned char hexdigit;
440 	unsigned char hexpower;
441 	unsigned char padding[6];	/* pmr: padding to align */
442      }
443    TextLookupTable;
444    /* This defines the various font control commands and the corresponding
445     * hexdigit and hexpower in the FCI.
446     */
447 #define N_TextLookupTable 10
448    const TextLookupTable lookup[N_TextLookupTable] = {
449 	{"<sans-serif/>", PL_FCI_SANS, PL_FCI_FAMILY, "     "},
450 	{"<serif/>", PL_FCI_SERIF, PL_FCI_FAMILY, "     "},
451 	{"<monospace/>", PL_FCI_MONO, PL_FCI_FAMILY, "     "},
452 	{"<script/>", PL_FCI_SCRIPT, PL_FCI_FAMILY, "     "},
453 	{"<symbol/>", PL_FCI_SYMBOL, PL_FCI_FAMILY, "     "},
454 	{"<upright/>", PL_FCI_UPRIGHT, PL_FCI_STYLE, "     "},
455 	{"<italic/>", PL_FCI_ITALIC, PL_FCI_STYLE, "     "},
456 	{"<oblique/>", PL_FCI_OBLIQUE, PL_FCI_STYLE, "     "},
457 	{"<medium/>", PL_FCI_MEDIUM, PL_FCI_WEIGHT, "     "},
458 	{"<bold/>", PL_FCI_BOLD, PL_FCI_WEIGHT, "     "}
459    };
460    int i, length;
461    for (i=0; i<N_TextLookupTable; i++) {
462       length = strlen(lookup[i].ptext);
463       if (! strncmp(text, lookup[i].ptext, length)) {
464 	 *hexdigit = lookup[i].hexdigit;
465 	 *hexpower = lookup[i].hexpower;
466 	 return(length);
467       }
468    }
469    *hexdigit = 0;
470    *hexpower = PL_FCI_HEXPOWER_IMPOSSIBLE;
471    return(0);
472 }
473 
474 PLUNICODE unicode_buffer[1024];
475 
476 void
plP_text(PLINT base,PLFLT just,PLFLT * xform,PLINT x,PLINT y,PLINT refx,PLINT refy,const char * string)477 plP_text(PLINT base, PLFLT just, PLFLT *xform, PLINT x, PLINT y,
478 	 PLINT refx, PLINT refy, const char *string)
479 {
480 
481    if (plsc->dev_text) { /* Does the device render it's own text ? */
482       EscText args;
483       short len=0;
484       char skip;
485       unsigned short i,j;
486       PLUNICODE code;
487       unsigned char esc;
488       int idx;
489 
490       args.base = base;
491       args.just = just;
492       args.xform = xform;
493       args.x = x;
494       args.y = y;
495       args.refx = refx;
496       args.refy = refy;
497       args.string = string;
498 
499       if (plsc->dev_unicode) { /* Does the device also understand unicode? */
500 	 PLINT ig;
501 	 PLUNICODE fci, fcisave;
502 	 unsigned char hexdigit, hexpower;
503 
504 	 /* Now process the text string */
505 
506 	 if (string!=NULL) {        /* If the string isn't blank, then we will
507                                      * continue
508 				     */
509 
510 	    len=strlen(string);     /* this length is only used in the loop
511 				     * counter, we will work out the length of
512 				     * the unicode string as we go */
513 	    plgesc(&esc);
514 
515 	    /* At this stage we will do some translations into unicode, like
516 	     * conversion to Greek , and will save other translations such as
517 	     * superscript for the driver to do later on. As we move through
518 	     * the string and do the translations, we will get
519 	     * rid of the esc character sequence, just replacing it with
520 	     * unicode.
521 	     */
522 
523 	    /* Obtain FCI (font characterization integer) for start of
524 	     * string. */
525 	    plgfci(&fci);
526 	    for (j=i=0;i<len;i++) {    /* Walk through the string, and convert
527 					* some stuff to unicode on the fly */
528 	       skip=0;
529 
530 	       if (string[i]==esc) {
531 		  switch(string[i+1]) {
532 		   case '(':  /* hershey code */
533 		     i+=2+text2num(&string[i+2],')',&code);
534 		     idx=plhershey2unicode(code);
535 		     /* momentarily switch to symbol font. */
536 		     fcisave = fci;
537 		     plP_hex2fci(PL_FCI_SYMBOL, PL_FCI_FAMILY, &fci);
538 		     unicode_buffer[j++]= fci;
539 		     unicode_buffer[j++] = \
540 		       (PLUNICODE)hershey_to_unicode_lookup_table[idx].Unicode;
541 
542 		     /* if unicode_buffer[j-1] corresponds to the escape
543 		      * character must unescape it by appending one more.
544 		      * This will probably always be necessary since it is
545 		      * likely unicode_buffer will always have to contain
546 		      * escape characters that are interpreted by the device
547 		      * driver.
548 		      */
549 		     if (unicode_buffer[j-1]==esc) unicode_buffer[j++]=esc;
550 		     fci = fcisave;
551 		     unicode_buffer[j]= fci;
552 		     skip=1;
553 		     break;
554 
555 		   case '[':  /* unicode */
556 		     i+=2+text2num(&string[i+2],']',&code);
557 		     /* momentarily switch to symbol font. */
558 		     fcisave = fci;
559 		     plP_hex2fci(PL_FCI_SYMBOL, PL_FCI_FAMILY, &fci);
560 		     unicode_buffer[j++]= fci;
561 		     unicode_buffer[j++]=code;
562 		     /* if unicode_buffer[j-1] corresponds to the escape
563 		      * character must unescape it by appending one more.
564 		      * This will probably always be necessary since it is
565 		      * likely unicode_buffer will always have to contain
566 		      * escape characters that are interpreted by the device
567 		      * driver.
568 		      */
569 		     if (unicode_buffer[j-1]==esc) unicode_buffer[j++]=esc;
570 		     fci = fcisave;
571 		     unicode_buffer[j] = fci;
572 		     skip=1;
573 		     break;
574 
575 		   case '<':  /* change font*/
576 		     if ('0' <= string[i+2] && string[i+2] <= '9' ) {
577 			i+=2+text2num(&string[i+2],'>', &code);
578 			if (code & PL_FCI_MARK) {
579 			   /* code is a complete FCI (font characterization
580 			    * integer): change FCI to this value.
581 			    */
582 			   fci = code;
583 			   unicode_buffer[j]=fci;
584 			   skip=1;
585 			}
586 			else {
587 			   /* code is not complete FCI. Change
588 			    * FCI with hex power in rightmost hex
589 			    * digit and hex digit value in second rightmost
590 			    * hex digit.
591 			    */
592 			   hexdigit = (code >> 4) & PL_FCI_HEXDIGIT_MASK;
593 			   hexpower = code & PL_FCI_HEXPOWER_MASK;
594 			   plP_hex2fci(hexdigit, hexpower, &fci);
595 			   unicode_buffer[j]=fci;
596 			   skip=1;
597 			}
598 		     }
599 
600 		     else {
601 			i+=text2fci(&string[i+1], &hexdigit, &hexpower);
602 			if (hexpower < 7) {
603 			   plP_hex2fci(hexdigit, hexpower, &fci);
604 			   unicode_buffer[j]=fci;
605 			   skip=1;
606 			}
607 		     }
608 		     break;
609 
610 		   case 'f':  /* Deprecated Hershey-style font change*/
611 		   case 'F':  /* Deprecated Hershey-style font change*/
612 		     /* We implement an approximate response here so that
613 		      * reasonable results are obtained for unicode fonts,
614 		      * but this method is deprecated and the #<nnn> or
615 		      * #<command string> methods should be used instead
616 		      * to change unicode fonts in mid-string.
617 		      */
618 		     fci = PL_FCI_MARK;
619 		     if (string[i+2] == 'n') {
620 			/* medium, upright, sans-serif */
621 			plP_hex2fci(PL_FCI_SANS, PL_FCI_FAMILY, &fci);
622 		     } else if (string[i+2] == 'r') {
623 			/* medium, upright, serif */
624 			plP_hex2fci(PL_FCI_SERIF, PL_FCI_FAMILY, &fci);
625 		     } else if (string[i+2] == 'i') {
626 			/* medium, italic, serif */
627 			plP_hex2fci(PL_FCI_ITALIC, PL_FCI_STYLE, &fci);
628 			plP_hex2fci(PL_FCI_SERIF, PL_FCI_FAMILY, &fci);
629 		     } else if (string[i+2] == 's') {
630 			/* medium, upright, script */
631 			plP_hex2fci(PL_FCI_SCRIPT, PL_FCI_FAMILY, &fci);
632 		     } else
633 		       fci = PL_FCI_IMPOSSIBLE;
634 
635 		     if (fci != PL_FCI_IMPOSSIBLE){
636 			i+=2;
637 			unicode_buffer[j] = fci;
638 			skip = 1;
639 		     }
640 		     break;
641 
642 		   case 'g':  /* Greek font */
643 		   case 'G':  /* Greek font */
644 		     /* Get the index in the lookup table
645 		      * 527 = upper case alpha displacement in Hershey Table
646 		      * 627 = lower case alpha displacement in Hershey Table
647 		      */
648 		     /* momentarily switch to symbol font. */
649 		     fcisave = fci;
650 		     plP_hex2fci(PL_FCI_SYMBOL, PL_FCI_FAMILY, &fci);
651 		     unicode_buffer[j++]= fci;
652 		     ig = plP_strpos(plP_greek_mnemonic, string[i+2]);
653 		     if (ig >= 0) {
654 			if (ig >= 24)
655 			  ig = ig + 100 - 24;
656 			idx=plhershey2unicode(ig+527);
657 			unicode_buffer[j++] = \
658 			  (PLUNICODE)hershey_to_unicode_lookup_table[idx].Unicode;
659 			i+=2;
660 			skip=1;  /* skip is set if we have copied something
661 				  * into the unicode table */
662 		     }
663 		     else {
664 			/* Use "unknown" unicode character if string[i+2]
665 			 * is not in the Greek array.*/
666 			unicode_buffer[j++]=(PLUNICODE)0x00;
667 			i+=2;
668 			skip=1;  /* skip is set if we have copied something
669 				  * into  the unicode table */
670 		     }
671 		     fci = fcisave;
672 		     unicode_buffer[j]= fci;
673 		     break;
674 
675 		  }
676 	       }
677 
678 	       if (skip==0) {
679                   PLUNICODE unichar = 0;
680 #ifdef HAVE_LIBUNICODE
681 		  char* ptr = unicode_get_utf8 (string + i, &unichar);
682 #else
683 		  const char* ptr = utf8_to_ucs4 (string + i, &unichar);
684 #endif
685                   if (ptr == NULL) {
686                     char buf[80];
687                     strncpy (buf, string, 30);
688                     sprintf (buf, "UTF-8 string is malformed: %s%s",
689                              buf, strlen (string) > 30 ? "[...]" : "");
690                     plabort (buf);
691                   }
692                   unicode_buffer [j] = unichar;
693                   i += ptr - (string + i) - 1;
694 
695 		  /* Search for escesc (an unescaped escape) in the input
696 		   * string and adjust unicode_buffer accordingly).
697 		   */
698 		  if (unicode_buffer[j] == esc && string[i+1] == esc) {
699 		    i++;
700 		    unicode_buffer[++j] = esc;
701 		  }
702 	       }
703 	       j++;
704 	    }
705 	    if (j > 0) {
706 	       args.unicode_array_len=j; /* Much easier to set the length than
707 					  * work it out later :-) */
708 	       args.unicode_array=&unicode_buffer[0]; /* Get address of the
709 						       * unicode buffer (even
710 						       * though it is
711 						       * currently  static) */
712 	    } else
713 	      /* Don't print anything, if there is no unicode to print! */
714 	      return;
715 	 }
716       }
717 
718       if (plsc->dev_unicode) {
719 	args.string=NULL; /* We are using unicode */
720       }
721       else  {
722 	args.string = string;
723       }
724 
725       plP_esc(PLESC_HAS_TEXT, &args);
726 #ifndef DEBUG_TEXT
727    } else {
728 #endif
729       plstr(base, xform, refx, refy, string);
730    }
731 }
732 
733 /* convert utf8 string to ucs4 unichar */
734 static const char *
utf8_to_ucs4(const char * ptr,PLUNICODE * unichar)735 utf8_to_ucs4(const char *ptr, PLUNICODE *unichar)
736 {
737    char tmp;
738    int isFirst = 1;
739    int cnt = 0;
740 
741    do {
742       /* Get next character in string */
743       tmp = *ptr++;
744       if (isFirst) { /* First char in UTF8 sequence */
745 	 isFirst = 0;
746 	 /* Determine length of sequence */
747 	 if ((unsigned char)(tmp & 0x80) == 0x00) { /* single char */
748 	    *unichar = (unsigned int)tmp & 0x7F;
749 	    cnt = 0;
750 	 } else if ((unsigned char)(tmp & 0xE0) == 0xC0) { /* 2 chars */
751 	    *unichar = (unsigned int)tmp & 0x1F;
752 	    cnt = 1;
753 	 } else if ((unsigned char)(tmp & 0xF0) == 0xE0) { /* 3 chars */
754 	    *unichar = (unsigned char)tmp & 0x0F;
755 	    cnt = 2;
756 	 } else if ((unsigned char)(tmp & 0xF8) == 0xF0) { /* 4 chars */
757 	    *unichar = (unsigned char)tmp & 0x07;
758 	    cnt = 3;
759 	 } else if ((unsigned char)(tmp & 0xFC) == 0xF8) { /* 5 chars */
760 	    *unichar = (unsigned char)tmp & 0x03;
761 	    cnt = 4;
762 	 } else if ((unsigned char)(tmp & 0xFE) == 0xFC) { /* 6 chars */
763 	    *unichar = (unsigned char)tmp & 0x01;
764 	    cnt = 5;
765 	 } else { /* Malformed */
766 	    ptr = NULL;
767 	    cnt = 0;
768 	 }
769       } else { /* Subsequent char in UTF8 sequence */
770 	 if ((unsigned char)(tmp & 0xC0) == 0x80) {
771 	    *unichar = (*unichar << 6) | ((unsigned int)tmp & 0x3F);
772 	    cnt--;
773 	 } else { /* Malformed */
774 	    ptr = NULL;
775 	    cnt = 0;
776 	 }
777       }
778    } while (cnt > 0);
779    return (const char *) ptr;
780 }
781 
782 /* convert ucs4 unichar to utf8 string */
783 int
ucs4_to_utf8(PLUNICODE unichar,char * ptr)784 ucs4_to_utf8(PLUNICODE unichar, char *ptr)
785 {
786   unsigned char *tmp;
787   int len;
788 
789   tmp = (unsigned char *)ptr;
790 
791   if ( (unichar & 0xffff80) == 0 ) {  /* single byte */
792     *tmp = (unsigned char) unichar;
793     tmp++;
794     len = 1;
795   }
796   else if ( (unichar & 0xfff800) == 0) { /* two bytes */
797     *tmp = (unsigned char) 0xc0 | (unichar >> 6);
798     tmp++;
799     *tmp = (unsigned char) 0x80 | (unichar & 0x3f);
800     tmp++;
801     len = 2;
802   }
803   else if ( (unichar & 0xff0000) == 0) { /* three bytes */
804     *tmp = (unsigned char) 0xe0 | (unichar >> 12);
805     tmp++;
806     *tmp = (unsigned char) 0x80 | ((unichar >> 6) & 0x3f);
807     tmp++;
808     *tmp = (unsigned char) 0x80 | (unichar & 0x3f);
809     tmp++;
810     len = 3;
811   }
812   else if ( (unichar & 0xe0000) == 0){ /* four bytes */
813     *tmp = (unsigned char) 0xf0 | (unichar >> 18);
814     tmp++;
815     *tmp = (unsigned char) 0x80 | ((unichar >> 12) & 0x3f);
816     tmp++;
817     *tmp = (unsigned char) 0x80 | ((unichar >> 6) & 0x3f);
818     tmp++;
819     *tmp = (unsigned char) 0x80 | (unichar & 0x3f);
820     tmp++;
821     len = 4;
822   }
823   else {  /* Illegal coding */
824     len = 0;
825   }
826   *tmp = '\0';
827 
828   return len;
829 }
830 
831 static void
grline(short * x,short * y,PLINT npts)832 grline(short *x, short *y, PLINT npts)
833 {
834     (void) npts; 			/* pmr: make it used */
835    (*plsc->dispatch_table->pl_line) ( (struct PLStream_struct *) plsc,
836 				      x[0], y[0], x[1], y[1] );
837 }
838 
839 static void
grpolyline(short * x,short * y,PLINT npts)840 grpolyline(short *x, short *y, PLINT npts)
841 {
842    (*plsc->dispatch_table->pl_polyline) ( (struct PLStream_struct *) plsc,
843 					  x, y, npts );
844 }
845 
846 static void
grfill(short * x,short * y,PLINT npts)847 grfill(short *x, short *y, PLINT npts)
848 {
849    plsc->dev_npts = npts;
850    plsc->dev_x = x;
851    plsc->dev_y = y;
852 
853    (*plsc->dispatch_table->pl_esc) ( (struct PLStream_struct *) plsc,
854 				     PLESC_FILL, NULL );
855 }
856 
857 /*--------------------------------------------------------------------------*\
858  * void difilt
859  *
860  * Driver interface filter -- passes all coordinates through a variety
861  * of filters.  These include filters to change :
862  *
863  *	- mapping of meta to physical coordinates
864  *	- plot orientation
865  *	- window into plot (zooms)
866  *	- window into device (i.e set margins)
867  *
868  * The filters are applied in the order specified above.  Because the
869  * orientation change comes first, subsequent window specifications affect
870  * the new coordinates (i.e. after a 90 degree flip, what was x is now y).
871  * This is the only way that makes sense from a graphical interface
872  * (e.g. TCL/TK driver).
873  *
874  * Where appropriate, the page clip limits are modified.
875 \*--------------------------------------------------------------------------*/
876 
877 void
difilt(PLINT * myxscl,PLINT * myyscl,PLINT npts,PLINT * clpxmi,PLINT * clpxma,PLINT * clpymi,PLINT * clpyma)878 difilt(PLINT *myxscl, PLINT *myyscl, PLINT npts,
879        PLINT *clpxmi, PLINT *clpxma, PLINT *clpymi, PLINT *clpyma)
880 {
881     PLINT i, x, y;
882 
883 /* Map meta coordinates to physical coordinates */
884 
885     if (plsc->difilt & PLDI_MAP) {
886 	for (i = 0; i < npts; i++) {
887 	    myxscl[i] = plsc->dimxax * myxscl[i] + plsc->dimxb;
888 	    myyscl[i] = plsc->dimyay * myyscl[i] + plsc->dimyb;
889 	}
890     }
891 
892 /* Change orientation */
893 
894     if (plsc->difilt & PLDI_ORI) {
895 	for (i = 0; i < npts; i++) {
896 	    x = plsc->dioxax * myxscl[i] + plsc->dioxay * myyscl[i] +
897 		plsc->dioxb;
898 	    y = plsc->dioyax * myxscl[i] + plsc->dioyay * myyscl[i] +
899 		plsc->dioyb;
900 	    myxscl[i] = x;
901 	    myyscl[i] = y;
902 	}
903     }
904 
905 /* Change window into plot space */
906 
907     if (plsc->difilt & PLDI_PLT) {
908 	for (i = 0; i < npts; i++) {
909 	    myxscl[i] = plsc->dipxax * myxscl[i] + plsc->dipxb;
910 	    myyscl[i] = plsc->dipyay * myyscl[i] + plsc->dipyb;
911 	}
912     }
913 
914 /* Change window into device space and set clip limits */
915 /* (this is the only filter that modifies them) */
916 
917     if (plsc->difilt & PLDI_DEV) {
918 	for (i = 0; i < npts; i++) {
919 	    myxscl[i] = plsc->didxax * myxscl[i] + plsc->didxb;
920 	    myyscl[i] = plsc->didyay * myyscl[i] + plsc->didyb;
921 	}
922 	*clpxmi = plsc->diclpxmi;
923 	*clpxma = plsc->diclpxma;
924 	*clpymi = plsc->diclpymi;
925 	*clpyma = plsc->diclpyma;
926     }
927     else {
928       *clpxmi = plsc->phyxmi;
929       *clpxma = plsc->phyxma;
930       *clpymi = plsc->phyymi;
931       *clpyma = plsc->phyyma;
932     }
933 }
934 
935 static void
sdifilt(short * myxscl,short * myyscl,PLINT npts,PLINT * clpxmi,PLINT * clpxma,PLINT * clpymi,PLINT * clpyma)936 sdifilt(short *myxscl, short *myyscl, PLINT npts,
937        PLINT *clpxmi, PLINT *clpxma, PLINT *clpymi, PLINT *clpyma)
938 {
939   int i;
940   short x, y;
941 
942 /* Map meta coordinates to physical coordinates */
943 
944     if (plsc->difilt & PLDI_MAP) {
945     for (i = 0; i < npts; i++) {
946         myxscl[i] = plsc->dimxax * myxscl[i] + plsc->dimxb;
947         myyscl[i] = plsc->dimyay * myyscl[i] + plsc->dimyb;
948     }
949     }
950 
951 /* Change orientation */
952 
953     if (plsc->difilt & PLDI_ORI) {
954     for (i = 0; i < npts; i++) {
955         x = plsc->dioxax * myxscl[i] + plsc->dioxay * myyscl[i] + plsc->dioxb;
956         y = plsc->dioyax * myxscl[i] + plsc->dioyay * myyscl[i] + plsc->dioyb;
957         myxscl[i] = x;
958         myyscl[i] = y;
959     }
960     }
961 
962 /* Change window into plot space */
963 
964     if (plsc->difilt & PLDI_PLT) {
965     for (i = 0; i < npts; i++) {
966         myxscl[i] = plsc->dipxax * myxscl[i] + plsc->dipxb;
967         myyscl[i] = plsc->dipyay * myyscl[i] + plsc->dipyb;
968     }
969     }
970 
971 /* Change window into device space and set clip limits */
972 /* (this is the only filter that modifies them) */
973 
974     if (plsc->difilt & PLDI_DEV) {
975     for (i = 0; i < npts; i++) {
976         myxscl[i] = plsc->didxax * myxscl[i] + plsc->didxb;
977         myyscl[i] = plsc->didyay * myyscl[i] + plsc->didyb;
978     }
979     *clpxmi = plsc->diclpxmi;
980     *clpxma = plsc->diclpxma;
981     *clpymi = plsc->diclpymi;
982     *clpyma = plsc->diclpyma;
983     }
984     else {
985     *clpxmi = plsc->phyxmi;
986     *clpxma = plsc->phyxma;
987     *clpymi = plsc->phyymi;
988     *clpyma = plsc->phyyma;
989     }
990 }
991 
992 /*--------------------------------------------------------------------------*\
993  * void pldi_ini
994  *
995  * Updates driver interface, making sure everything is in order.
996  * Even if filter is not being used, the defaults need to be set up.
997 \*--------------------------------------------------------------------------*/
998 
999 static void
setdef_diplt(void)1000 setdef_diplt(void)			/* pmr: fix prototype */
1001 {
1002     plsc->dipxmin = 0.0;
1003     plsc->dipxmax = 1.0;
1004     plsc->dipymin = 0.0;
1005     plsc->dipymax = 1.0;
1006 }
1007 
1008 static void			/* pmr: fix prototype */
setdef_didev(void)1009 setdef_didev(void)
1010 {
1011     plsc->mar = 0.0;
1012     plsc->aspect = 0.0;
1013     plsc->jx = 0.0;
1014     plsc->jy = 0.0;
1015 }
1016 
1017 static void
setdef_diori(void)1018 setdef_diori(void)			/* pmr: fix prototype */
1019 {
1020     plsc->diorot = 0.;
1021 }
1022 
1023 static void
pldi_ini(void)1024 pldi_ini(void)
1025 {
1026     if (plsc->level >= 1) {
1027 	if (plsc->difilt & PLDI_MAP)	/* Coordinate mapping */
1028 	    calc_dimap();
1029 
1030 	if (plsc->difilt & PLDI_ORI)	/* Orientation */
1031 	    calc_diori();
1032 	else
1033 	    setdef_diori();
1034 
1035 	if (plsc->difilt & PLDI_PLT) 	/* Plot window */
1036 	    calc_diplt();
1037 	else
1038 	    setdef_diplt();
1039 
1040 	if (plsc->difilt & PLDI_DEV)	/* Device window */
1041 	    calc_didev();
1042 	else
1043 	    setdef_didev();
1044     }
1045 }
1046 
1047 /*--------------------------------------------------------------------------*\
1048  * void pldid2pc
1049  *
1050  * Converts input values from relative device coordinates to relative plot
1051  * coordinates.  This function must be called when selecting a plot window
1052  * from a display driver, since the coordinates chosen by the user are
1053  * necessarily device-specific.
1054 \*--------------------------------------------------------------------------*/
1055 
1056 void
pldid2pc(PLFLT * xmin,PLFLT * ymin,PLFLT * xmax,PLFLT * ymax)1057 pldid2pc(PLFLT *xmin, PLFLT *ymin, PLFLT *xmax, PLFLT *ymax)
1058 {
1059     PLFLT pxmin, pymin, pxmax, pymax;
1060     PLFLT sxmin, symin, sxmax, symax;
1061     PLFLT rxmin, rymin, rxmax, rymax;
1062 
1063     if (plsc->difilt & PLDI_DEV) {
1064 
1065 	pldebug("pldid2pc",
1066 		"Relative device coordinates (in): %f, %f, %f, %f\n",
1067 		*xmin, *ymin, *xmax, *ymax);
1068 
1069 	pxmin = plP_dcpcx(*xmin);
1070 	pymin = plP_dcpcy(*ymin);
1071 	pxmax = plP_dcpcx(*xmax);
1072 	pymax = plP_dcpcy(*ymax);
1073 
1074 	sxmin = (pxmin - plsc->didxb) / plsc->didxax;
1075 	symin = (pymin - plsc->didyb) / plsc->didyay;
1076 	sxmax = (pxmax - plsc->didxb) / plsc->didxax;
1077 	symax = (pymax - plsc->didyb) / plsc->didyay;
1078 
1079 	rxmin = plP_pcdcx(sxmin);
1080 	rymin = plP_pcdcy(symin);
1081 	rxmax = plP_pcdcx(sxmax);
1082 	rymax = plP_pcdcy(symax);
1083 
1084 	*xmin = (rxmin < 0) ? 0 : rxmin;
1085 	*xmax = (rxmax > 1) ? 1 : rxmax;
1086 	*ymin = (rymin < 0) ? 0 : rymin;
1087 	*ymax = (rymax > 1) ? 1 : rymax;
1088 
1089 	pldebug("pldid2pc",
1090 		"Relative plot coordinates (out): %f, %f, %f, %f\n",
1091 		rxmin, rymin, rxmax, rymax);
1092     }
1093 }
1094 
1095 /*--------------------------------------------------------------------------*\
1096  * void pldip2dc
1097  *
1098  * Converts input values from relative plot coordinates to relative
1099  * device coordinates.
1100 \*--------------------------------------------------------------------------*/
1101 
1102 void
pldip2dc(PLFLT * xmin,PLFLT * ymin,PLFLT * xmax,PLFLT * ymax)1103 pldip2dc(PLFLT *xmin, PLFLT *ymin, PLFLT *xmax, PLFLT *ymax)
1104 {
1105     PLFLT pxmin, pymin, pxmax, pymax;
1106     PLFLT sxmin, symin, sxmax, symax;
1107     PLFLT rxmin, rymin, rxmax, rymax;
1108 
1109     if (plsc->difilt & PLDI_DEV) {
1110 
1111 	pldebug("pldip2pc",
1112 		"Relative plot coordinates (in): %f, %f, %f, %f\n",
1113 		*xmin, *ymin, *xmax, *ymax);
1114 
1115 	pxmin = plP_dcpcx(*xmin);
1116 	pymin = plP_dcpcy(*ymin);
1117 	pxmax = plP_dcpcx(*xmax);
1118 	pymax = plP_dcpcy(*ymax);
1119 
1120 	sxmin = pxmin * plsc->didxax + plsc->didxb;
1121 	symin = pymin * plsc->didyay + plsc->didyb;
1122 	sxmax = pxmax * plsc->didxax + plsc->didxb;
1123 	symax = pymax * plsc->didyay + plsc->didyb;
1124 
1125 	rxmin = plP_pcdcx(sxmin);
1126 	rymin = plP_pcdcy(symin);
1127 	rxmax = plP_pcdcx(sxmax);
1128 	rymax = plP_pcdcy(symax);
1129 
1130 	*xmin = (rxmin < 0) ? 0 : rxmin;
1131 	*xmax = (rxmax > 1) ? 1 : rxmax;
1132 	*ymin = (rymin < 0) ? 0 : rymin;
1133 	*ymax = (rymax > 1) ? 1 : rymax;
1134 
1135 	pldebug("pldip2pc",
1136 		"Relative device coordinates (out): %f, %f, %f, %f\n",
1137 		rxmin, rymin, rxmax, rymax);
1138     }
1139 }
1140 
1141 /*--------------------------------------------------------------------------*\
1142  * void plsdiplt
1143  *
1144  * Set window into plot space
1145 \*--------------------------------------------------------------------------*/
1146 
1147 void
c_plsdiplt(PLFLT xmin,PLFLT ymin,PLFLT xmax,PLFLT ymax)1148 c_plsdiplt(PLFLT xmin, PLFLT ymin, PLFLT xmax, PLFLT ymax)
1149 {
1150     plsc->dipxmin = (xmin < xmax) ? xmin : xmax;
1151     plsc->dipxmax = (xmin < xmax) ? xmax : xmin;
1152     plsc->dipymin = (ymin < ymax) ? ymin : ymax;
1153     plsc->dipymax = (ymin < ymax) ? ymax : ymin;
1154 
1155     if (xmin == 0. && xmax == 1. && ymin == 0. && ymax == 1.)  {
1156 	plsc->difilt &= ~PLDI_PLT;
1157 	return;
1158     }
1159 
1160     plsc->difilt |= PLDI_PLT;
1161     pldi_ini();
1162 }
1163 
1164 /*--------------------------------------------------------------------------*\
1165  * void plsdiplz
1166  *
1167  * Set window into plot space incrementally (zoom)
1168 \*--------------------------------------------------------------------------*/
1169 
1170 void
c_plsdiplz(PLFLT xmin,PLFLT ymin,PLFLT xmax,PLFLT ymax)1171 c_plsdiplz(PLFLT xmin, PLFLT ymin, PLFLT xmax, PLFLT ymax)
1172 {
1173     if (plsc->difilt & PLDI_PLT) {
1174 	xmin = plsc->dipxmin + (plsc->dipxmax - plsc->dipxmin) * xmin;
1175 	ymin = plsc->dipymin + (plsc->dipymax - plsc->dipymin) * ymin;
1176 	xmax = plsc->dipxmin + (plsc->dipxmax - plsc->dipxmin) * xmax;
1177 	ymax = plsc->dipymin + (plsc->dipymax - plsc->dipymin) * ymax;
1178     }
1179 
1180     plsdiplt(xmin, ymin, xmax, ymax);
1181 }
1182 
1183 /*--------------------------------------------------------------------------*\
1184  * void calc_diplt
1185  *
1186  * Calculate transformation coefficients to set window into plot space.
1187  *
1188  * Note: if driver has requested to handle these commands itself, we must
1189  * send the appropriate escape command.  If the driver succeeds it will
1190  * cancel the filter operation.  The command is deferred until this point
1191  * to ensure that the driver has been initialized.
1192 \*--------------------------------------------------------------------------*/
1193 
1194 static void
calc_diplt(void)1195 calc_diplt(void)
1196 {
1197     PLINT pxmin, pxmax, pymin, pymax, pxlen, pylen;
1198 
1199     if (plsc->dev_di) {
1200 	(*plsc->dispatch_table->pl_esc) ( (struct PLStream_struct *) plsc,
1201                                           PLESC_DI, NULL );
1202     }
1203 
1204     if ( ! (plsc->difilt & PLDI_PLT))
1205 	return;
1206 
1207     pxmin = plP_dcpcx(plsc->dipxmin);
1208     pxmax = plP_dcpcx(plsc->dipxmax);
1209     pymin = plP_dcpcy(plsc->dipymin);
1210     pymax = plP_dcpcy(plsc->dipymax);
1211 
1212     pxlen = pxmax - pxmin;
1213     pylen = pymax - pymin;
1214     pxlen = MAX(1, pxlen);
1215     pylen = MAX(1, pylen);
1216 
1217     plsc->dipxax = plsc->phyxlen / (double) pxlen;
1218     plsc->dipyay = plsc->phyylen / (double) pylen;
1219     plsc->dipxb = plsc->phyxmi - plsc->dipxax * pxmin;
1220     plsc->dipyb = plsc->phyymi - plsc->dipyay * pymin;
1221 }
1222 
1223 /*--------------------------------------------------------------------------*\
1224  * void plgdiplt
1225  *
1226  * Retrieve current window into plot space
1227 \*--------------------------------------------------------------------------*/
1228 
1229 void
c_plgdiplt(PLFLT * p_xmin,PLFLT * p_ymin,PLFLT * p_xmax,PLFLT * p_ymax)1230 c_plgdiplt(PLFLT *p_xmin, PLFLT *p_ymin, PLFLT *p_xmax, PLFLT *p_ymax)
1231 {
1232     *p_xmin = plsc->dipxmin;
1233     *p_xmax = plsc->dipxmax;
1234     *p_ymin = plsc->dipymin;
1235     *p_ymax = plsc->dipymax;
1236 }
1237 
1238 /*--------------------------------------------------------------------------*\
1239  * void plsdidev
1240  *
1241  * Set window into device space using margin, aspect ratio, and
1242  * justification.  If you want to just use the previous value for any of
1243  * these, just pass in the magic value PL_NOTSET.
1244  *
1245  * It is unlikely that one should ever need to change the aspect ratio
1246  * but it's in there for completeness.
1247 \*--------------------------------------------------------------------------*/
1248 
1249 void
c_plsdidev(PLFLT mar,PLFLT aspect,PLFLT jx,PLFLT jy)1250 c_plsdidev(PLFLT mar, PLFLT aspect, PLFLT jx, PLFLT jy)
1251 {
1252     plsetvar(plsc->mar, mar);
1253     plsetvar(plsc->aspect, aspect);
1254     plsetvar(plsc->jx, jx);
1255     plsetvar(plsc->jy, jy);
1256 
1257     if (mar == 0. && aspect == 0. && jx == 0. && jy == 0. &&
1258 	! (plsc->difilt & PLDI_ORI)) {
1259 	plsc->difilt &= ~PLDI_DEV;
1260 	return;
1261     }
1262 
1263     plsc->difilt |= PLDI_DEV;
1264     pldi_ini();
1265 }
1266 
1267 /*--------------------------------------------------------------------------*\
1268  * void calc_didev
1269  *
1270  * Calculate transformation coefficients to set window into device space.
1271  * Calculates relative window bounds and calls plsdidxy to finish the job.
1272 \*--------------------------------------------------------------------------*/
1273 
1274 static void
calc_didev(void)1275 calc_didev(void)
1276 {
1277     PLFLT lx, ly, aspect, aspdev;
1278     PLFLT xmin, xmax, xlen, ymin, ymax, ylen;
1279     PLINT pxmin, pxmax, pymin, pymax, pxlen, pylen;
1280 
1281     if (plsc->dev_di) {
1282 	(*plsc->dispatch_table->pl_esc) ( (struct PLStream_struct *) plsc,
1283                                           PLESC_DI, NULL );
1284     }
1285 
1286     if ( ! (plsc->difilt & PLDI_DEV))
1287 	return;
1288 
1289 /* Calculate aspect ratio of physical device */
1290 
1291     lx = plsc->phyxlen / plsc->xpmm;
1292     ly = plsc->phyylen / plsc->ypmm;
1293     aspdev = lx / ly;
1294 
1295     if (plsc->difilt & PLDI_ORI)
1296 	aspect = plsc->aspori;
1297     else
1298 	aspect = plsc->aspect;
1299 
1300     if (aspect <= 0.)
1301 	aspect = plsc->aspdev;
1302 
1303 /* Failsafe */
1304 
1305     plsc->mar = (plsc->mar > 0.5) ? 0.5 : plsc->mar;
1306     plsc->mar = (plsc->mar < 0.0) ? 0.0 : plsc->mar;
1307     plsc->jx = (plsc->jx >  0.5) ?  0.5 : plsc->jx;
1308     plsc->jx = (plsc->jx < -0.5) ? -0.5 : plsc->jx;
1309     plsc->jy = (plsc->jy >  0.5) ?  0.5 : plsc->jy;
1310     plsc->jy = (plsc->jy < -0.5) ? -0.5 : plsc->jy;
1311 
1312 /* Relative device coordinates that neutralize aspect ratio difference */
1313 
1314     xlen = (aspect < aspdev) ? (aspect / aspdev) : 1.0;
1315     ylen = (aspect < aspdev) ? 1.0 : (aspdev / aspect);
1316 
1317     xlen *= (1.0 - 2.*plsc->mar);
1318     ylen *= (1.0 - 2.*plsc->mar);
1319 
1320     xmin = (1. - xlen) * (0.5 + plsc->jx);
1321     xmax = xmin + xlen;
1322 
1323     ymin = (1. - ylen) * (0.5 + plsc->jy);
1324     ymax = ymin + ylen;
1325 
1326 /* Calculate transformation coefficients */
1327 
1328     pxmin = plP_dcpcx(xmin);
1329     pxmax = plP_dcpcx(xmax);
1330     pymin = plP_dcpcy(ymin);
1331     pymax = plP_dcpcy(ymax);
1332 
1333     pxlen = pxmax - pxmin;
1334     pylen = pymax - pymin;
1335     pxlen = MAX(1, pxlen);
1336     pylen = MAX(1, pylen);
1337 
1338     plsc->didxax = pxlen / (double) plsc->phyxlen;
1339     plsc->didyay = pylen / (double) plsc->phyylen;
1340     plsc->didxb = pxmin - plsc->didxax * plsc->phyxmi;
1341     plsc->didyb = pymin - plsc->didyay * plsc->phyymi;
1342 
1343 /* Set clip limits to conform to new page size */
1344 
1345     plsc->diclpxmi = plsc->didxax * plsc->phyxmi + plsc->didxb;
1346     plsc->diclpxma = plsc->didxax * plsc->phyxma + plsc->didxb;
1347     plsc->diclpymi = plsc->didyay * plsc->phyymi + plsc->didyb;
1348     plsc->diclpyma = plsc->didyay * plsc->phyyma + plsc->didyb;
1349 }
1350 
1351 /*--------------------------------------------------------------------------*\
1352  * void plgdidev
1353  *
1354  * Retrieve current window into device space
1355 \*--------------------------------------------------------------------------*/
1356 
1357 void
c_plgdidev(PLFLT * p_mar,PLFLT * p_aspect,PLFLT * p_jx,PLFLT * p_jy)1358 c_plgdidev(PLFLT *p_mar, PLFLT *p_aspect, PLFLT *p_jx, PLFLT *p_jy)
1359 {
1360     *p_mar = plsc->mar;
1361     *p_aspect = plsc->aspect;
1362     *p_jx = plsc->jx;
1363     *p_jy = plsc->jy;
1364 }
1365 
1366 /*--------------------------------------------------------------------------*\
1367  * void plsdiori
1368  *
1369  * Set plot orientation, specifying rotation in units of pi/2.
1370 \*--------------------------------------------------------------------------*/
1371 
1372 void
c_plsdiori(PLFLT rot)1373 c_plsdiori(PLFLT rot)
1374 {
1375     plsc->diorot = rot;
1376     if (rot == 0.) {
1377 	plsc->difilt &= ~PLDI_ORI;
1378 	pldi_ini();
1379 	return;
1380     }
1381 
1382     plsc->difilt |= PLDI_ORI;
1383     pldi_ini();
1384 }
1385 
1386 /*--------------------------------------------------------------------------*\
1387  * void calc_diori
1388  *
1389  * Calculate transformation coefficients to arbitrarily orient plot.
1390  * Preserve aspect ratios so the output doesn't suck.
1391 \*--------------------------------------------------------------------------*/
1392 
1393 static void
calc_diori(void)1394 calc_diori(void)
1395 {
1396     PLFLT r11, r21, r12, r22, cost, sint;
1397     PLFLT xx0, yy0, lx, ly, aspect;
1398 
1399     if (plsc->dev_di) {
1400 	(*plsc->dispatch_table->pl_esc) ( (struct PLStream_struct *) plsc,
1401                                           PLESC_DI, NULL );
1402     }
1403 
1404     if ( ! (plsc->difilt & PLDI_ORI))
1405 	return;
1406 
1407 /* Center point of rotation */
1408 
1409     xx0 = (plsc->phyxma + plsc->phyxmi) / 2.;
1410     yy0 = (plsc->phyyma + plsc->phyymi) / 2.;
1411 
1412 /* Rotation matrix */
1413 
1414     r11 = cos(plsc->diorot * PI / 2.);
1415     r21 = sin(plsc->diorot * PI / 2.);
1416     r12 = -r21;
1417     r22 = r11;
1418 
1419     cost = ABS(r11);
1420     sint = ABS(r21);
1421 
1422 /* Flip aspect ratio as necessary.  Grungy but I don't see a better way */
1423 
1424     aspect = plsc->aspect;
1425     if (aspect == 0.)
1426 	aspect = plsc->aspdev;
1427 
1428     if (plsc->freeaspect)
1429 	plsc->aspori = aspect;
1430     else
1431 	plsc->aspori = (aspect * cost + sint) / (aspect * sint + cost);
1432 
1433     if ( ! (plsc->difilt & PLDI_DEV)) {
1434 	plsc->difilt |= PLDI_DEV;
1435 	setdef_didev();
1436     }
1437     calc_didev();
1438 
1439 /* Compute scale factors */
1440 
1441     lx = plsc->phyxlen;
1442     ly = plsc->phyylen;
1443 
1444 /* Transformation coefficients */
1445 
1446     plsc->dioxax = r11;
1447     plsc->dioxay = r21 * (lx / ly);
1448     plsc->dioxb = (1. - r11) * xx0 - r21 * yy0 * (lx / ly);
1449 
1450     plsc->dioyax = r12 * (ly / lx);
1451     plsc->dioyay = r22;
1452     plsc->dioyb = (1. - r22) * yy0 - r12 * xx0 * (ly / lx);
1453 }
1454 
1455 /*--------------------------------------------------------------------------*\
1456  * void plgdiori
1457  *
1458  * Get plot orientation
1459 \*--------------------------------------------------------------------------*/
1460 
1461 void
c_plgdiori(PLFLT * p_rot)1462 c_plgdiori(PLFLT *p_rot)
1463 {
1464     *p_rot = plsc->diorot;
1465 }
1466 
1467 /*--------------------------------------------------------------------------*\
1468  * void plsdimap
1469  *
1470  * Set up transformation from metafile coordinates.  The size of the plot is
1471  * scaled so as to preserve aspect ratio.  This isn't intended to be a
1472  * general-purpose facility just yet (not sure why the user would need it,
1473  * for one).
1474 \*--------------------------------------------------------------------------*/
1475 
1476 void
c_plsdimap(PLINT dimxmin,PLINT dimxmax,PLINT dimymin,PLINT dimymax,PLFLT dimxpmm,PLFLT dimypmm)1477 c_plsdimap(PLINT dimxmin, PLINT dimxmax, PLINT dimymin, PLINT dimymax,
1478 	   PLFLT dimxpmm, PLFLT dimypmm)
1479 {
1480     plsetvar(plsc->dimxmin, dimxmin);
1481     plsetvar(plsc->dimxmax, dimxmax);
1482     plsetvar(plsc->dimymin, dimymin);
1483     plsetvar(plsc->dimymax, dimymax);
1484     plsetvar(plsc->dimxpmm, dimxpmm);
1485     plsetvar(plsc->dimypmm, dimypmm);
1486 
1487     plsc->difilt |= PLDI_MAP;
1488     pldi_ini();
1489 }
1490 
1491 /*--------------------------------------------------------------------------*\
1492  * void calc_dimap
1493  *
1494  * Set up transformation from metafile coordinates.  The size of the plot is
1495  * scaled so as to preserve aspect ratio.  This isn't intended to be a
1496  * general-purpose facility just yet (not sure why the user would need it,
1497  * for one).
1498 \*--------------------------------------------------------------------------*/
1499 
1500 static void
calc_dimap(void)1501 calc_dimap(void)			/* pmr: prototype */
1502 {
1503     PLFLT lx, ly;
1504     PLINT pxmin, pxmax, pymin, pymax;
1505     PLFLT dimxlen, dimylen, pxlen, pylen;
1506 
1507     if ((plsc->dimxmin == plsc->phyxmi) && (plsc->dimxmax == plsc->phyxma) &&
1508 	(plsc->dimymin == plsc->phyymi) && (plsc->dimymax == plsc->phyyma) &&
1509 	(plsc->dimxpmm == plsc->xpmm) && (plsc->dimypmm == plsc->ypmm)) {
1510 	plsc->difilt &= ~PLDI_MAP;
1511 	return;
1512     }
1513 
1514 /* Set default aspect ratio */
1515 
1516     lx = (plsc->dimxmax - plsc->dimxmin + 1) / plsc->dimxpmm;
1517     ly = (plsc->dimymax - plsc->dimymin + 1) / plsc->dimypmm;
1518 
1519     plsc->aspdev = lx / ly;
1520 
1521 /* Build transformation to correct physical coordinates */
1522 
1523     dimxlen = plsc->dimxmax - plsc->dimxmin;
1524     dimylen = plsc->dimymax - plsc->dimymin;
1525 
1526     pxmin = plsc->phyxmi;
1527     pxmax = plsc->phyxma;
1528     pymin = plsc->phyymi;
1529     pymax = plsc->phyyma;
1530     pxlen = pxmax - pxmin;
1531     pylen = pymax - pymin;
1532 
1533     plsc->dimxax = pxlen / dimxlen;
1534     plsc->dimyay = pylen / dimylen;
1535     plsc->dimxb = pxmin - pxlen * plsc->dimxmin / dimxlen;
1536     plsc->dimyb = pymin - pylen * plsc->dimymin / dimylen;
1537 }
1538 
1539 /*--------------------------------------------------------------------------*\
1540  * void plflush()
1541  *
1542  * Flushes the output stream.  Use sparingly, if at all.
1543 \*--------------------------------------------------------------------------*/
1544 
1545 void
c_plflush(void)1546 c_plflush(void)
1547 {
1548     if (plsc->dev_flush) {
1549 	(*plsc->dispatch_table->pl_esc) ( (struct PLStream_struct *) plsc,
1550                                           PLESC_FLUSH, NULL );
1551     }
1552     else {
1553 	if (plsc->OutFile != NULL)
1554 	    fflush(plsc->OutFile);
1555     }
1556 }
1557 
1558 /*--------------------------------------------------------------------------*\
1559  * Startup routines.
1560 \*--------------------------------------------------------------------------*/
1561 
1562 /*--------------------------------------------------------------------------*\
1563  * void pllib_init()
1564  *
1565  * Initialize library.  Called internally by every startup routine.
1566  * Everything you want to always be initialized before plinit() is called
1567  * you should put here.  E.g. dispatch table setup, rcfile read, etc.
1568 \*--------------------------------------------------------------------------*/
1569 
1570 void
pllib_init(void)1571 pllib_init(void)			/* pmr: prototype */
1572 {
1573     if (lib_initialized) return;
1574     lib_initialized = 1;
1575 
1576 #ifdef ENABLE_DYNDRIVERS
1577 /* Create libltdl resources */
1578         lt_dlinit();
1579 #endif
1580 
1581 /* Initialize the dispatch table with the info from the static drivers table
1582    and the available dynamic drivers. */
1583 
1584     plInitDispatchTable();
1585 }
1586 
1587 /*--------------------------------------------------------------------------*\
1588  * void plstar(nx, ny)
1589  *
1590  * Initialize PLplot, passing in the windows/page settings.
1591 \*--------------------------------------------------------------------------*/
1592 
1593 void
c_plstar(PLINT nx,PLINT ny)1594 c_plstar(PLINT nx, PLINT ny)
1595 {
1596     pllib_init();
1597 
1598     if (plsc->level != 0)
1599 	plend1();
1600 
1601     plssub(nx, ny);
1602 
1603     c_plinit();
1604 }
1605 
1606 /*--------------------------------------------------------------------------*\
1607  * void plstart(devname, nx, ny)
1608  *
1609  * Initialize PLplot, passing the device name and windows/page settings.
1610 \*--------------------------------------------------------------------------*/
1611 
1612 void
c_plstart(const char * devname,PLINT nx,PLINT ny)1613 c_plstart(const char *devname, PLINT nx, PLINT ny)
1614 {
1615     pllib_init();
1616 
1617     if (plsc->level != 0)
1618 	plend1();
1619 
1620     plssub(nx, ny);
1621     plsdev(devname);
1622 
1623     c_plinit();
1624 }
1625 
1626 /*--------------------------------------------------------------------------*\
1627  * void plinit()
1628  *
1629  * Initializes PLplot, using preset or default options.
1630 \*--------------------------------------------------------------------------*/
1631 
1632 void
c_plinit(void)1633 c_plinit(void)
1634 {
1635     PLFLT def_arrow_x[6] = {-0.5, 0.5, 0.3, 0.5, 0.3, 0.5};
1636     PLFLT def_arrow_y[6] = {0.0, 0.0,   0.2, 0.0, -0.2, 0.0};
1637     PLFLT lx, ly, xpmm_loc, ypmm_loc, aspect_old, aspect_new;
1638     PLINT mk = 0, sp = 0, inc = 0, del = 2000;
1639 
1640     pllib_init();
1641 
1642     if (plsc->level != 0)
1643 	plend1();
1644 
1645 /* Set stream number */
1646 
1647     plsc->ipls = ipls;
1648 
1649 /* Set up devices */
1650 
1651     pllib_devinit();
1652 
1653 /* Auxiliary stream setup */
1654 
1655     plstrm_init();
1656 
1657 /* Initialize device & first page */
1658 
1659     plP_init();
1660     plP_bop();
1661     plsc->level = 1;
1662 
1663 /* Calculate factor such that the character aspect ratio is preserved
1664  * when the overall aspect ratio is changed, i.e., if portrait mode is
1665  * requested (only honored for subset of drivers) or if the aspect ratio
1666  * is specified in any way, or if a 90 deg rotation occurs with
1667  * -freeaspect. */
1668 
1669 /* Case where plsc->aspect has a value.... (e.g., -a aspect on the
1670  * command line or 2nd parameter of plsdidev specified) */
1671     if (plsc->aspect > 0.) {
1672        lx = plsc->phyxlen / plsc->xpmm;
1673        ly = plsc->phyylen / plsc->ypmm;
1674        aspect_old = lx / ly;
1675        aspect_new = plsc->aspect;
1676        plsc->caspfactor = sqrt(aspect_old/aspect_new);
1677     }
1678 /* Case of 90 deg rotations with -freeaspect (this is also how portraite
1679  * mode is implemented for the drivers that honor -portrait). */
1680     else if (plsc->freeaspect && ABS(cos(plsc->diorot * PI / 2.)) <= 1.e-5) {
1681        lx = plsc->phyxlen / plsc->xpmm;
1682        ly = plsc->phyylen / plsc->ypmm;
1683        aspect_old = lx / ly;
1684        aspect_new = ly / lx;
1685        plsc->caspfactor = sqrt(aspect_old/aspect_new);
1686     }
1687 
1688     else
1689        plsc->caspfactor = 1.;
1690 
1691 /* Load fonts */
1692 
1693     plsc->cfont = 1;
1694     plfntld(initfont);
1695 
1696 /* Set up subpages */
1697 
1698     plP_subpInit();
1699 
1700 /* Set up number of allowed digits before switching to scientific notation */
1701 /* The user can always change this */
1702 
1703     if (plsc->xdigmax == 0)
1704 	plsc->xdigmax = 4;
1705 
1706     if (plsc->ydigmax == 0)
1707 	plsc->ydigmax = 4;
1708 
1709     if (plsc->zdigmax == 0)
1710 	plsc->zdigmax = 3;
1711 
1712 /* Switch to graphics mode and set color and arrow style*/
1713 
1714     plgra();
1715     plcol(1);
1716 
1717     plstyl(0, &mk, &sp);
1718     plpat(1, &inc, &del);
1719 
1720     plsvect(def_arrow_x, def_arrow_y, 6, 0);
1721 
1722 /* Set clip limits. */
1723 
1724     plsc->clpxmi = plsc->phyxmi;
1725     plsc->clpxma = plsc->phyxma;
1726     plsc->clpymi = plsc->phyymi;
1727     plsc->clpyma = plsc->phyyma;
1728 
1729 /* Page aspect ratio. */
1730 
1731     lx = plsc->phyxlen / plsc->xpmm;
1732     ly = plsc->phyylen / plsc->ypmm;
1733     plsc->aspdev = lx / ly;
1734 
1735 /* Initialize driver interface */
1736 
1737     pldi_ini();
1738 
1739 /* Apply compensating factor to original xpmm and ypmm so that
1740  * character aspect ratio is preserved when overall aspect ratio
1741  * is changed.  This must appear here in the code because previous
1742  * code in this routine and in routines that it calls must use the original
1743  * values of xpmm and ypmm before the compensating factor is applied.  */
1744 
1745     plP_gpixmm(&xpmm_loc, &ypmm_loc);
1746     plP_setpxl(xpmm_loc*plsc->caspfactor, ypmm_loc/plsc->caspfactor);
1747 }
1748 
1749 /*--------------------------------------------------------------------------*\
1750  * void plend()
1751  *
1752  * End a plotting session for all open streams.
1753 \*--------------------------------------------------------------------------*/
1754 
1755 void
c_plend(void)1756 c_plend(void)
1757 {
1758     PLINT i;
1759 
1760     if (lib_initialized == 0) return;
1761 
1762     for (i = PL_NSTREAMS-1; i >= 0; i--) {
1763 	if (pls[i] != NULL) {
1764 	    plsstrm(i);
1765 	    c_plend1();
1766 	}
1767     }
1768     plfontrel();
1769 #ifdef ENABLE_DYNDRIVERS
1770 /* Release the libltdl resources */
1771     lt_dlexit();
1772 /* Free up memory allocated to the dispatch tables */
1773     for (i = 0; i < npldynamicdevices; i++) {
1774       free_mem(loadable_device_list[i].devnam);
1775       free_mem(loadable_device_list[i].description);
1776       free_mem(loadable_device_list[i].drvnam);
1777       free_mem(loadable_device_list[i].tag);
1778     }
1779     free_mem(loadable_device_list);
1780     for (i = 0; i < nloadabledrivers; i++) {
1781       free_mem(loadable_driver_list[i].drvnam);
1782     }
1783     free_mem(loadable_driver_list);
1784     for (i = nplstaticdevices; i < npldrivers; i++) {
1785       free_mem(dispatch_table[i]->pl_MenuStr);
1786       free_mem(dispatch_table[i]->pl_DevName);
1787       free_mem(dispatch_table[i]);
1788     }
1789 #endif
1790     for (i = 0; i < nplstaticdevices; i++) {
1791       free_mem(dispatch_table[i]);
1792     }
1793     free_mem(dispatch_table);
1794 
1795     plP_FreeDrvOpts();
1796 
1797     lib_initialized = 0;
1798 }
1799 
1800 /*--------------------------------------------------------------------------*\
1801  * void plend1()
1802  *
1803  * End a plotting session for the current stream only.  After the stream is
1804  * ended the memory associated with the stream's PLStream data structure is
1805  * freed (for stream > 0), and the stream counter is set to 0 (the default).
1806 \*--------------------------------------------------------------------------*/
1807 
1808 void
c_plend1(void)1809 c_plend1(void)
1810 {
1811     if (plsc->level > 0) {
1812 	plP_eop();
1813 	plP_tidy();
1814 	plsc->level = 0;
1815     }
1816     /* Move from plP_tidy because FileName may be set even if level == 0 */
1817     if (plsc->FileName)
1818 	free_mem(plsc->FileName);
1819 
1820 /* Free all malloc'ed stream memory */
1821 
1822     free_mem(plsc->cmap0);
1823     free_mem(plsc->cmap1);
1824     free_mem(plsc->plwindow);
1825     free_mem(plsc->geometry);
1826     free_mem(plsc->dev);
1827     free_mem(plsc->BaseName);
1828     free_mem(plsc->Ext);		/* pmr: added for EMBOSS */
1829 #ifndef BUFFERED_FILE
1830     free_mem(plsc->plbuf_buffer);
1831 #endif
1832 /*    if (plsc->program) free_mem(plsc->program);*/ /* pmr: now const */
1833     if (plsc->server_name) free_mem(plsc->server_name);
1834     if (plsc->server_host) free_mem(plsc->server_host);
1835     if (plsc->server_port) free_mem(plsc->server_port);
1836     if (plsc->user) free_mem(plsc->user);
1837     if (plsc->plserver) free_mem(plsc->plserver);
1838     if (plsc->auto_path) free_mem(plsc->auto_path);
1839 
1840     if (plsc->arrow_x) free_mem(plsc->arrow_x);
1841     if (plsc->arrow_y) free_mem(plsc->arrow_y);
1842 
1843 /* Free malloc'ed stream if not in initial stream, else clear it out */
1844 
1845     if (ipls > 0) {
1846 	free_mem(plsc);
1847 	pls[ipls] = NULL;
1848 	plsstrm(0);
1849     }
1850     else {
1851 	memset((char *) pls[ipls], 0, sizeof(PLStream));
1852     }
1853 }
1854 
1855 /*--------------------------------------------------------------------------*\
1856  * void plsstrm
1857  *
1858  * Set stream number.  If the data structure for a new stream is
1859  * unallocated, we allocate it here.
1860 \*--------------------------------------------------------------------------*/
1861 
1862 void
c_plsstrm(PLINT strm)1863 c_plsstrm(PLINT strm)
1864 {
1865     if (strm < 0 || strm >= PL_NSTREAMS) {
1866 	fprintf(stderr,
1867 		"plsstrm: Illegal stream number %d, must be in [0, %d]\n",
1868 		(int) strm, PL_NSTREAMS);
1869     }
1870     else {
1871 	ipls = strm;
1872 	if (pls[ipls] == NULL) {
1873 	    pls[ipls] = (PLStream *) malloc((size_t) sizeof(PLStream));
1874 	    if (pls[ipls] == NULL)
1875 		plexit("plsstrm: Out of memory.");
1876 
1877 	    memset((char *) pls[ipls], 0, sizeof(PLStream));
1878 	}
1879 	plsc = pls[ipls];
1880 	plsc->ipls = ipls;
1881     }
1882 }
1883 
1884 /*--------------------------------------------------------------------------*\
1885  * void plgstrm
1886  *
1887  * Get current stream number.
1888 \*--------------------------------------------------------------------------*/
1889 
1890 void
c_plgstrm(PLINT * p_strm)1891 c_plgstrm(PLINT *p_strm)
1892 {
1893     *p_strm = ipls;
1894 }
1895 
1896 /*--------------------------------------------------------------------------*\
1897  * void plmkstrm
1898  *
1899  * Creates a new stream and makes it the default.  Differs from using
1900  * plsstrm(), in that a free stream number is found, and returned.
1901  *
1902  * Unfortunately, I /have/ to start at stream 1 and work upward, since
1903  * stream 0 is preallocated.  One of the BIG flaws in the PLplot API is
1904  * that no initial, library-opening call is required.  So stream 0 must be
1905  * preallocated, and there is no simple way of determining whether it is
1906  * already in use or not.
1907 \*--------------------------------------------------------------------------*/
1908 
1909 void
c_plmkstrm(PLINT * p_strm)1910 c_plmkstrm(PLINT *p_strm)
1911 {
1912     int i;
1913 
1914     for (i = 1; i < PL_NSTREAMS; i++) {
1915 	if (pls[i] == NULL)
1916 	    break;
1917     }
1918 
1919     if (i == PL_NSTREAMS) {
1920 	fprintf(stderr, "plmkstrm: Cannot create new stream\n");
1921 	*p_strm = -1;
1922     }
1923     else {
1924 	*p_strm = i;
1925 	plsstrm(i);
1926     }
1927     plstrm_init();
1928 }
1929 
1930 /*--------------------------------------------------------------------------*\
1931  * void plstrm_init
1932  *
1933  * Does required startup initialization of a stream.  Should be called right
1934  * after creating one (for allocating extra memory, etc).  Users shouldn't
1935  * need to call this directly.
1936  *
1937  * This function can be called multiple times for a given stream, in which
1938  * case only the first call produces any effect.  For streams >= 1, which
1939  * are created dynamically, this is called by the routine that allocates
1940  * the stream.  Stream 0, which is preallocated, is much harder to deal with
1941  * because any of a number of different calls may be the first call to the
1942  * library.  This is handled by just calling plstrm_init() from every
1943  * function that might be called first.  Sucks, but it should work.
1944 \*--------------------------------------------------------------------------*/
1945 
1946 void
plstrm_init(void)1947 plstrm_init(void)
1948 {
1949     if ( ! plsc->initialized) {
1950 	plsc->initialized = 1;
1951 
1952 	if (plsc->cmap0 == NULL)
1953 	    plscmap0n(0);
1954 
1955 	if (plsc->cmap1 == NULL)
1956 	    plscmap1n(0);
1957     }
1958 
1959     plsc->psdoc = NULL;
1960 }
1961 
1962 /*--------------------------------------------------------------------------*\
1963  * pl_cpcolor
1964  *
1965  * Utility to copy one PLColor to another.
1966 \*--------------------------------------------------------------------------*/
1967 
1968 void
pl_cpcolor(PLColor * to,PLColor * from)1969 pl_cpcolor(PLColor *to, PLColor *from)
1970 {
1971     to->r = from->r;
1972     to->g = from->g;
1973     to->b = from->b;
1974 }
1975 
1976 /*--------------------------------------------------------------------------*\
1977  * void plcpstrm
1978  *
1979  * Copies state parameters from the reference stream to the current stream.
1980  * Tell driver interface to map device coordinates unless flags == 1.
1981  *
1982  * This function is used for making save files of selected plots (e.g.
1983  * from the TK driver).  After initializing, you can get a copy of the
1984  * current plot to the specified device by switching to this stream and
1985  * issuing a plcpstrm() and a plreplot(), with calls to plbop() and
1986  * pleop() as appropriate.  The plot buffer must have previously been
1987  * enabled (done automatically by some display drivers, such as X).
1988 \*--------------------------------------------------------------------------*/
1989 
1990 void
c_plcpstrm(PLINT iplsr,PLINT flags)1991 c_plcpstrm(PLINT iplsr, PLINT flags)
1992 {
1993     int i;
1994     PLStream *plsr;
1995 
1996     plsr = pls[iplsr];
1997     if (plsr == NULL) {
1998 	fprintf(stderr, "plcpstrm: stream %d not in use\n", (int) iplsr);
1999 	return;
2000     }
2001 
2002 /* May be debugging */
2003 
2004     plsc->debug = plsr->debug;
2005 
2006 /* Plot buffer -- need to copy file pointer so that plreplot() works */
2007 /* This also prevents inadvertent writes into the plot buffer */
2008 
2009 #ifdef BUFFERED_FILE
2010     plsc->plbufFile = plsr->plbufFile;
2011 #else
2012     plsc->plbuf_buffer = plsr->plbuf_buffer;
2013     plsc->plbuf_buffer_grow = plsr->plbuf_buffer_grow;
2014     plsc->plbuf_buffer_size = plsr->plbuf_buffer_size;
2015     plsc->plbuf_top = plsr->plbuf_top;
2016     plsc->plbuf_readpos = plsr->plbuf_readpos;
2017 #endif
2018 
2019 /* Driver interface */
2020 /* Transformation must be recalculated in current driver coordinates */
2021 
2022     if (plsr->difilt & PLDI_PLT)
2023 	plsdiplt(plsr->dipxmin, plsr->dipymin, plsr->dipxmax, plsr->dipymax);
2024 
2025     if (plsr->difilt & PLDI_DEV)
2026 	plsdidev(plsr->mar, plsr->aspect, plsr->jx, plsr->jy);
2027 
2028     if (plsr->difilt & PLDI_ORI)
2029 	plsdiori(plsr->diorot);
2030 
2031 /* Map device coordinates */
2032 
2033     if ( ! (flags & 0x01)) {
2034 	pldebug("plcpstrm", "mapping parameters: %d %d %d %d %f %f\n",
2035 		plsr->phyxmi, plsr->phyxma, plsr->phyymi, plsr->phyyma,
2036 		plsr->xpmm, plsr->ypmm);
2037 	plsdimap(plsr->phyxmi, plsr->phyxma, plsr->phyymi, plsr->phyyma,
2038 		 plsr->xpmm, plsr->ypmm);
2039     }
2040 
2041 /* current color */
2042 
2043     pl_cpcolor(&plsc->curcolor, &plsr->curcolor);
2044 
2045 /* cmap 0 */
2046 
2047     plsc->icol0 = plsr->icol0;
2048     plsc->ncol0 = plsr->ncol0;
2049     if (plsc->cmap0 != NULL)
2050 	free((void *) plsc->cmap0);
2051 
2052     plsc->cmap0 = (PLColor *) calloc(1, plsc->ncol0 * sizeof(PLColor));
2053     for (i = 0; i < plsc->ncol0; i++)
2054 	pl_cpcolor(&plsc->cmap0[i], &plsr->cmap0[i]);
2055 
2056 /* cmap 1 */
2057 
2058     plsc->icol1 = plsr->icol1;
2059     plsc->ncol1 = plsr->ncol1;
2060     if (plsc->cmap1 != NULL)
2061 	free((void *) plsc->cmap1);
2062 
2063     plsc->cmap1 = (PLColor *) calloc(1, plsc->ncol1 * sizeof(PLColor));
2064     for (i = 0; i < plsc->ncol1; i++)
2065 	pl_cpcolor(&plsc->cmap1[i], &plsr->cmap1[i]);
2066 
2067 /* Initialize if it hasn't been done yet. */
2068 
2069     if (plsc->level == 0)
2070 	plinit();
2071 }
2072 
2073 /*--------------------------------------------------------------------------*\
2074  * pllib_devinit()
2075  *
2076  * Does preliminary setup of device driver.
2077  *
2078  * This function (previously plGetDev) used to be what is now shown as
2079  * plSelectDev below.  However, the situation is a bit more complicated now in
2080  * the dynloadable drivers era.  We now have to:
2081  *
2082  * 1) Make sure the dispatch table is initialized to the union of static
2083  *    drivers and available dynamic drivers (done from pllib_init now).
2084  * 2) Allow the user to select the desired device.
2085  * 3) Initialize the dispatch table entries for the selected device, in the
2086  *    case that it is a dynloadable driver that has not yet been loaded.
2087  *
2088  * Also made non-static, in order to allow some device calls to be made prior
2089  * to calling plinit().  E.g. plframe needs to tell the X driver to create its
2090  * internal data structure during widget construction time (using the escape
2091  * function), but doesn't call plinit() until the plframe is actually mapped.
2092 \*--------------------------------------------------------------------------*/
2093 
2094 void
pllib_devinit(void)2095 pllib_devinit(void)			/* pmr: prototype */
2096 {
2097     if (plsc->dev_initialized) return;
2098     plsc->dev_initialized = 1;
2099 
2100     plSelectDev();
2101 
2102     plLoadDriver();
2103 
2104 /* offset by one since table is zero-based, but input list is not */
2105     plsc->dispatch_table = dispatch_table[plsc->device - 1];
2106 }
2107 
plInBuildTree(void)2108 int PLDLLIMPEXP plInBuildTree(void)			/* pmr: prototype */
2109 {
2110   static int inited = 0;
2111   static int inBuildTree = 0;
2112 
2113   if (inited == 0) {
2114     char currdir[256];
2115 
2116 /* AM: getcwd has a somewhat strange status on Windows, its proper
2117    name is _getcwd, this is a problem in the case of DLLs, like with
2118    the Java bindings.
2119 */
2120 #if defined(WIN32) && !defined(__BORLANDC__)
2121 #define getcwd _getcwd
2122 #endif
2123 
2124     if (getcwd(currdir, 256) == NULL) {
2125       pldebug("plInBuildTree():", "Not enough buffer space");
2126     } else if (strncmp(BUILD_DIR, currdir, strlen(BUILD_DIR)) == 0)
2127       inBuildTree = 1;
2128     inited = 1;
2129   }
2130   return inBuildTree;
2131 }
2132 
2133 #ifdef ENABLE_DYNDRIVERS
2134 
2135 static char*
plGetDrvDir()2136 plGetDrvDir ()
2137 {
2138     char* drvdir;
2139 
2140 /* Get drivers directory in PLPLOT_DRV_DIR or DRV_DIR,
2141  *  on this order
2142  */
2143 
2144     if (plInBuildTree() == 1) {
2145       drvdir = BUILD_DIR "/drivers";
2146       pldebug("plGetDrvDir", "Using %s as the driver directory.\n", drvdir);
2147     } else {
2148       pldebug("plGetDrvDir", "Trying to read env var PLPLOT_DRV_DIR\n");
2149       drvdir = getenv ("PLPLOT_DRV_DIR");
2150 
2151       if (drvdir == NULL) {
2152         pldebug("plGetDrvDir",
2153 	        "Will use drivers dir: " DRV_DIR "\n");
2154 	drvdir = DRV_DIR;
2155       }
2156     }
2157 
2158     return drvdir;
2159 }
2160 
2161 #endif
2162 
2163 
2164 /*--------------------------------------------------------------------------*\
2165  * void plInitDispatchTable()
2166  *
2167  * ...
2168 \*--------------------------------------------------------------------------*/
2169 
plDispatchSequencer(const void * p1,const void * p2)2170 static int plDispatchSequencer( const void *p1, const void *p2 )
2171 {
2172     const PLDispatchTable* t1 = *(const PLDispatchTable * const *) p1;
2173     const PLDispatchTable* t2 = *(const PLDispatchTable * const *) p2;
2174 
2175 /*     printf( "sorting: t1.name=%s t1.seq=%d t2.name=%s t2.seq=%d\n", */
2176 /*             t1->pl_DevName, t1->pl_seq, t2->pl_DevName, t2->pl_seq ); */
2177 
2178     return t1->pl_seq - t2->pl_seq;
2179 }
2180 
2181 static void
plInitDispatchTable(void)2182 plInitDispatchTable(void)		/* pmr: prototype */
2183 {
2184     int n;
2185 
2186 #ifdef ENABLE_DYNDRIVERS
2187     char buf[300];
2188     char* drvdir;
2189     char *devnam, *devdesc, *devtype, *driver, *tag, *seqstr;
2190     int seq;
2191     int i, j, driver_found, done=0;
2192     FILE *fp_drvdb = NULL;
2193     DIR* dp_drvdir = NULL;
2194     struct dirent* entry;
2195     /* lt_dlhandle dlhand; */
2196 
2197     /* Make sure driver counts are zeroed */
2198     npldynamicdevices = 0;
2199     nloadabledrivers = 0;
2200 
2201 /* Open a temporary file in which all the plD_DEVICE_INFO_<driver> strings
2202    will be stored */
2203     fp_drvdb = tmpfile ();
2204 
2205 /* Open the drivers directory */
2206     drvdir = plGetDrvDir ();
2207     dp_drvdir = opendir (drvdir);
2208     if (dp_drvdir == NULL) {
2209       plabort ("plInitDispatchTable: Could not open drivers directory");
2210       return;
2211     }
2212 
2213 /* Loop over each entry in the drivers directory */
2214 
2215     pldebug ("plInitDispatchTable", "Scanning dyndrivers dir\n");
2216     while ((entry = readdir (dp_drvdir)) != NULL)
2217     {
2218         char* name = entry->d_name;
2219         int len = strlen (name) - 3;
2220 
2221             pldebug ("plInitDispatchTable",
2222                      "Consider file %s\n", name);
2223 
2224 /* Only consider entries that have the ".rc" suffix */
2225 	if ((len > 0) && (strcmp (name + len, ".rc") == 0)) {
2226 	    char path[300];
2227 	    char buf[300];
2228             FILE* fd;
2229 
2230 /* Open the driver's info file */
2231             sprintf (path, "%s/%s", drvdir, name);
2232             fd = fopen (path, "r");
2233             if (fd == NULL) {
2234 	        sprintf (buf,
2235                   "plInitDispatchTable: Could not open driver info file %s\n",
2236                   name);
2237 	        plabort (buf);
2238 	        return;
2239 	    }
2240 
2241 /* Each line in the <driver>.rc file corresponds to a specific device.
2242  * Write it to the drivers db file and take care of leading newline
2243  * character */
2244 
2245             pldebug ("plInitDispatchTable",
2246                      "Opened driver info file %s\n", name);
2247             while (fgets (buf, 300, fd) != NULL)
2248 	    {
2249                 fprintf (fp_drvdb, "%s", buf);
2250 		if ( buf [strlen (buf) - 1] != '\n' )
2251 		    fprintf (fp_drvdb, "\n");
2252                 npldynamicdevices++;
2253 	    }
2254 	    fclose (fd);
2255 	}
2256     }
2257 
2258 /* Make sure that the temporary file containing the drivers database
2259  * is ready to read and close the directory handle */
2260     fflush (fp_drvdb);
2261     closedir (dp_drvdir);
2262 
2263 #endif
2264 
2265 /* Allocate space for the dispatch table. */
2266     dispatch_table = (PLDispatchTable **)
2267 	malloc( (nplstaticdevices + npldynamicdevices) * sizeof(PLDispatchTable *) );
2268 
2269 /* Initialize the dispatch table entries for the static devices by calling
2270    the dispatch table initialization function for each static device.  This
2271    is the same function that would be called at load time for dynamic
2272    drivers. */
2273 
2274     for( n=0; n < nplstaticdevices; n++ )
2275     {
2276         dispatch_table[n] = (PLDispatchTable *)malloc( sizeof(PLDispatchTable) );
2277 
2278         (*static_device_initializers[n])( dispatch_table[n] );
2279     }
2280     npldrivers = nplstaticdevices;
2281 
2282 #ifdef ENABLE_DYNDRIVERS
2283 
2284 /* Allocate space for the device and driver specs.  We may not use all of
2285  * these driver descriptors, but we obviously won't need more drivers than
2286  * devices... */
2287     loadable_device_list = malloc( npldynamicdevices * sizeof(PLLoadableDevice) );
2288     loadable_driver_list = malloc( npldynamicdevices * sizeof(PLLoadableDriver) );
2289 
2290     rewind( fp_drvdb );
2291 
2292     i = 0;
2293     done = !(i < npldynamicdevices);
2294     while( !done ) {
2295         char *p = fgets( buf, 300, fp_drvdb );
2296 
2297         if (p == 0) {
2298             done = 1;
2299             continue;
2300         }
2301 
2302         devnam  = strtok( buf, ":" );
2303         devdesc = strtok( 0, ":" );
2304         devtype = strtok( 0, ":" );
2305         driver  = strtok( 0, ":" );
2306         seqstr  = strtok( 0, ":" );
2307         tag     = strtok( 0, "\n" );
2308 
2309         seq     = atoi(seqstr);
2310 
2311         n = npldrivers++;
2312 
2313         dispatch_table[n] = malloc( sizeof(PLDispatchTable) );
2314 
2315     /* Fill in the dispatch table entries. */
2316         dispatch_table[n]->pl_MenuStr = plstrdup(devdesc);
2317         dispatch_table[n]->pl_DevName = plstrdup(devnam);
2318         dispatch_table[n]->pl_type = atoi(devtype);
2319         dispatch_table[n]->pl_seq = seq;
2320         dispatch_table[n]->pl_init = 0;
2321         dispatch_table[n]->pl_line = 0;
2322         dispatch_table[n]->pl_polyline = 0;
2323         dispatch_table[n]->pl_eop = 0;
2324         dispatch_table[n]->pl_bop = 0;
2325         dispatch_table[n]->pl_tidy = 0;
2326         dispatch_table[n]->pl_state = 0;
2327         dispatch_table[n]->pl_esc = 0;
2328 
2329     /* Add a record to the loadable device list */
2330         loadable_device_list[i].devnam = plstrdup(devnam);
2331         loadable_device_list[i].description = plstrdup(devdesc);
2332         loadable_device_list[i].drvnam = plstrdup(driver);
2333         loadable_device_list[i].tag = plstrdup(tag);
2334 
2335     /* Now see if this driver has been seen before.  If not, add a driver
2336      * entry for it. */
2337         driver_found = 0;
2338         for( j=0; j < nloadabledrivers; j++ )
2339             if (strcmp( driver, loadable_driver_list[j].drvnam) == 0)
2340             {
2341                 driver_found = 1;
2342                 break;
2343             }
2344 
2345         if (!driver_found)
2346         {
2347             loadable_driver_list[nloadabledrivers].drvnam = plstrdup(driver);
2348             loadable_driver_list[nloadabledrivers].dlhand = 0;
2349             nloadabledrivers++;
2350         }
2351 
2352         loadable_device_list[i].drvidx = j;
2353 
2354     /* Get ready for next loadable device spec */
2355         i++;
2356     }
2357 
2358 /* RML: close fp_drvdb */
2359     fclose (fp_drvdb);
2360 
2361 #endif
2362 
2363 /* Finally, we need to sort the list into presentation order, based on the
2364    sequence number in the dispatch ttable entries. */
2365 
2366     qsort( dispatch_table, npldrivers, sizeof(PLDispatchTable*),
2367            plDispatchSequencer );
2368 }
2369 
2370 /*--------------------------------------------------------------------------*\
2371  * void plSelectDev()
2372  *
2373  * If the user has not already specified the output device, or the
2374  * one specified is either: (a) not available, (b) "?", or (c) NULL, the
2375  * user is prompted for it.
2376  *
2377  * Prompting quits after 10 unsuccessful tries in case the user has
2378  * run the program in the background with insufficient input.
2379 \*--------------------------------------------------------------------------*/
2380 
2381 static void
plSelectDev(void)2382 plSelectDev(void)			/* pmr: prototype */
2383 {
2384     int dev, i, count, length;
2385     char response[80];
2386     int tlen;
2387 
2388 /* Device name already specified.  See if it is valid. */
2389 
2390     if (*(plsc->DevName) != '\0' && *(plsc->DevName) != '?') {
2391 	length = strlen(plsc->DevName);
2392 	for (i = 0; i < npldrivers; i++) {
2393 	    if ((*plsc->DevName == *dispatch_table[i]->pl_DevName) &&
2394 		(strncmp(plsc->DevName,
2395 			 dispatch_table[i]->pl_DevName, length) == 0))
2396 		break;
2397 	}
2398 	if (i < npldrivers) {
2399 	    plsc->device = i + 1;
2400 	    return;
2401 	}
2402 	else {
2403 	    fprintf(stderr, "Requested device %s not available\n",
2404 		    plsc->DevName);
2405 	}
2406     }
2407 
2408     dev = 0;
2409     count = 0;
2410 
2411     if (npldrivers == 1)
2412 	dev = 1;
2413 
2414 /* User hasn't specified it correctly yet, so we prompt */
2415 
2416     while (dev < 1 || dev > npldrivers) {
2417 	fprintf(stdout, "\nPlotting Options:\n");
2418 	for (i = 0; i < npldrivers; i++) {
2419 	    fprintf(stdout, " <%2d> %-10s %s\n", i + 1,
2420 		    dispatch_table[i]->pl_DevName,
2421 		    dispatch_table[i]->pl_MenuStr);
2422 	}
2423 	if (ipls == 0)
2424 	    fprintf(stdout, "\nEnter device number or keyword: ");
2425 	else
2426 	    fprintf(stdout, "\nEnter device number or keyword (stream %d): ",
2427 		   (int) ipls);
2428 
2429 	plio_fgets(response, sizeof(response), stdin);
2430 
2431     /* First check to see if device keyword was entered. */
2432     /* Final "\n" in response messes things up, so ignore it.  */
2433 
2434 	length = strlen(response);
2435 	if(!length)
2436 	{
2437 	    fprintf(stderr,"Error: empty response\n");
2438 	    exit(-1);
2439 	}
2440 
2441 	tlen = length - 1;
2442 	if (*(response + tlen) == '\n')
2443 	    length--;
2444 
2445 	for (i = 0; i < npldrivers; i++) {
2446 	    if ( ! strncmp(response, dispatch_table[i]->pl_DevName,
2447 			   (unsigned int) length))
2448 		break;
2449 	}
2450 	if (i < npldrivers) {
2451 	    dev = i + 1;
2452 	}
2453 	else {
2454 	    if ((dev = atoi(response)) < 1) {
2455 		fprintf(stdout, "\nInvalid device: %s", response);
2456 		dev = 0;
2457 	    }
2458 	}
2459 	if (count++ > 10)
2460 	    plexit("plSelectDev: Too many tries.");
2461     }
2462     plsc->device = dev;
2463     strcpy(plsc->DevName, dispatch_table[dev - 1]->pl_DevName);
2464 }
2465 
2466 /*--------------------------------------------------------------------------*\
2467  * void plLoadDriver()
2468  *
2469  * Make sure the selected driver is loaded.  Static drivers are already
2470  * loaded, but if the user selected a dynamically loadable driver, we may
2471  * have to take care of that now.
2472 \*--------------------------------------------------------------------------*/
2473 
2474 static void
plLoadDriver(void)2475 plLoadDriver(void)
2476 {
2477 #ifdef ENABLE_DYNDRIVERS
2478     int i, drvidx;
2479     char sym[60];
2480     char *tag;
2481 
2482     int n=plsc->device - 1;
2483     PLDispatchTable *dev = dispatch_table[n];
2484     PLLoadableDriver *driver = 0;
2485 
2486 /* If the dispatch table is already filled in, then either the device was
2487  * linked in statically, or else perhaps it was already loaded.  In either
2488  * case, we have nothing left to do. */
2489     if (dev->pl_init)
2490         return;
2491 
2492     pldebug("plLoadDriver", "Device not loaded!\n");
2493 
2494 /* Now search through the list of loadable devices, looking for the record
2495  * that corresponds to the requested device. */
2496     for( i=0; i < npldynamicdevices; i++ )
2497         if (strcmp( dev->pl_DevName, loadable_device_list[i].devnam ) == 0)
2498             break;
2499 
2500 /* If we couldn't find such a record, then there is some sort of internal
2501  * logic flaw since plSelectDev is supposed to only select a valid device.
2502  */
2503     if (i == npldynamicdevices) {
2504         fprintf( stderr, "No such device: %s.\n", dev->pl_DevName );
2505         plexit("plLoadDriver detected device logic screwup");
2506     }
2507 
2508 /* Note the device tag, and the driver index. Note that a given driver could
2509  * supply multiple devices, each with a unique tag to distinguish the driver
2510  * entry points for the differnet supported devices. */
2511     tag = loadable_device_list[i].tag;
2512     drvidx = loadable_device_list[i].drvidx;
2513 
2514     pldebug("plLoadDriver", "tag=%s, drvidx=%d\n", tag, drvidx );
2515 
2516     driver = &loadable_driver_list[drvidx];
2517 
2518 /* Load the driver if it hasn't been loaded yet. */
2519     if (!driver->dlhand)
2520     {
2521         char drvspec[ 400 ];
2522         sprintf( drvspec, "%s/%s", plGetDrvDir (), driver->drvnam );
2523 
2524 	pldebug("plLoadDriver", "Trying to load %s on %s\n",
2525 		driver->drvnam, drvspec );
2526 
2527         driver->dlhand = lt_dlopenext( drvspec);
2528     }
2529 
2530 /* If it still isn't loaded, then we're doomed. */
2531     if (!driver->dlhand)
2532     {
2533         pldebug("plLoadDriver", "lt_dlopenext failed because of "
2534 		 "the following reason:\n%s\n", lt_dlerror ());
2535         fprintf( stderr, "Unable to load driver: %s.\n", driver->drvnam );
2536         plexit("Unable to load driver");
2537     }
2538 
2539 /* Now we are ready to ask the driver's device dispatch init function to
2540    initialize the entries in the dispatch table. */
2541 
2542     sprintf( sym, "plD_dispatch_init_%s", tag );
2543     {
2544         PLDispatchInit dispatch_init = (PLDispatchInit) lt_dlsym( driver->dlhand, sym );
2545         if (!dispatch_init)
2546         {
2547             fprintf( stderr,
2548                      "Unable to locate dispatch table initialization function for driver: %s.\n",
2549 		     driver->drvnam );
2550             return;
2551         }
2552 
2553         (*dispatch_init)( dev );
2554     }
2555 #endif
2556 }
2557 
2558 /*--------------------------------------------------------------------------*\
2559  * void plfontld()
2560  *
2561  * Load specified font set.
2562 \*--------------------------------------------------------------------------*/
2563 
2564 void
c_plfontld(PLINT ifont)2565 c_plfontld(PLINT ifont)
2566 {
2567     if (ifont != 0)
2568 	ifont = 1;
2569 
2570     if (plsc->level > 0)
2571 	plfntld(ifont);
2572     else
2573 	initfont = ifont;
2574 }
2575 
2576 /*--------------------------------------------------------------------------*\
2577  * void plreplot()
2578  *
2579  * Replays contents of plot buffer to current device/file.
2580 \*--------------------------------------------------------------------------*/
2581 
2582 void
c_plreplot(void)2583 c_plreplot(void)
2584 {
2585 #ifdef BUFFERED_FILE
2586     if (plsc->plbufFile != NULL) {
2587 #else
2588     if (plsc->plbuf_buffer != NULL) {
2589 #endif
2590 	plRemakePlot(plsc);
2591     }
2592     else {
2593 	plwarn("plreplot: plot buffer not available");
2594     }
2595 }
2596 
2597 /*--------------------------------------------------------------------------*\
2598  * void plgFileDevs()
2599  *
2600  * Returns a list of file-oriented device names and their menu strings,
2601  * for use in a graphical interface.  The caller must allocate enough
2602  * space for (*p_menustr) and (*p_devname) to hold a pointer for each
2603  * device -- 20 or so is plenty.  E.g. char *menustr[20].  The size of
2604  * these arrays should be passed in *p_ndev, which, on exit, holds the
2605  * number of devices actually present.
2606 \*--------------------------------------------------------------------------*/
2607 
2608 void					/* pmr: const  */
2609 plgFileDevs(const char ***p_menustr, const char ***p_devname, int *p_ndev)
2610 {
2611     plgdevlst(*p_menustr, *p_devname, p_ndev, 0);
2612 }
2613 
2614 /*--------------------------------------------------------------------------*\
2615  * void plgDevs()
2616  *
2617  * Like plgFileDevs(), but returns names and menu strings for all devices.
2618 \*--------------------------------------------------------------------------*/
2619 
2620 void					/* pmr: const  */
2621 plgDevs(const char ***p_menustr, const char ***p_devname, int *p_ndev)
2622 {
2623     plgdevlst(*p_menustr, *p_devname, p_ndev, -1);
2624 }
2625 
2626 static void				/* pmr: const */
2627 plgdevlst(const char **p_menustr, const char **p_devname, int *p_ndev, int type)
2628 {
2629     int i, j;
2630 
2631     pllib_init();
2632 
2633     for (i = j = 0; i < npldrivers; i++) {
2634 	if (type < 0 || dispatch_table[i]->pl_type == type) {
2635 	    p_menustr[j] = dispatch_table[i]->pl_MenuStr;
2636 	    p_devname[j] = dispatch_table[i]->pl_DevName;
2637 	    if (++j + 1 >= *p_ndev) {
2638 	        plwarn("plgdevlst:  too many devices");
2639 		break;
2640 	      }
2641 	}
2642     }
2643     p_menustr[j] = NULL;
2644     p_devname[j] = NULL;
2645     *p_ndev = j;
2646 }
2647 
2648 /*--------------------------------------------------------------------------*\
2649  *  Various external access routines.
2650 \*--------------------------------------------------------------------------*/
2651 
2652 /* Get output device parameters. */
2653 
2654 void
2655 c_plgpage(PLFLT *p_xp, PLFLT *p_yp,
2656 	  PLINT *p_xleng, PLINT *p_yleng, PLINT *p_xoff, PLINT *p_yoff)
2657 {
2658     *p_xp = plsc->xdpi;
2659     *p_yp = plsc->ydpi;
2660     *p_xleng = plsc->xlength;
2661     *p_yleng = plsc->ylength;
2662     *p_xoff = plsc->xoffset;
2663     *p_yoff = plsc->yoffset;
2664 }
2665 
2666 /* Set output device parameters.  Usually ignored by the driver. */
2667 
2668 void
2669 c_plspage(PLFLT xp, PLFLT yp, PLINT xleng, PLINT yleng, PLINT xoff, PLINT yoff)
2670 {
2671     if (plsc->level > 0)
2672         plwarn("calling plspage() after plinit() may give unpredictable results");
2673 
2674     if (xp)
2675 	plsc->xdpi = xp;
2676     if (yp)
2677 	plsc->ydpi = yp;
2678 
2679     if (xleng)
2680 	plsc->xlength = xleng;
2681     if (yleng)
2682 	plsc->ylength = yleng;
2683 
2684     if (xoff)
2685 	plsc->xoffset = xoff;
2686     if (yoff)
2687 	plsc->yoffset = yoff;
2688 
2689     plsc->pageset = 1;
2690 }
2691 
2692 /* Set the number of subwindows in x and y */
2693 
2694 void
2695 c_plssub(PLINT nx, PLINT ny)
2696 {
2697     if (nx > 0)
2698 	plsc->nsubx = nx;
2699     if (ny > 0)
2700 	plsc->nsuby = ny;
2701 
2702 /* Force a page advance */
2703 
2704     if (plsc->level > 0) {
2705         plP_subpInit();
2706 /*AWI	plP_eop();
2707 	plP_bop();*/
2708     }
2709 }
2710 
2711 /* Set the device (keyword) name */
2712 
2713 void
2714 c_plsdev(const char *devname)
2715 {
2716     if (plsc->level > 0) {
2717 	plwarn("plsdev: Must be called before plinit.");
2718 	return;
2719     }
2720     if (devname != NULL) {
2721 	strncpy(plsc->DevName, devname, sizeof(plsc->DevName) - 1);
2722 	plsc->DevName[sizeof(plsc->DevName) - 1] = '\0';
2723     }
2724 }
2725 
2726 /* Get the current device (keyword) name */
2727 /* Note: you MUST have allocated space for this (80 characters is safe) */
2728 
2729 void
2730 c_plgdev(char *p_dev)
2731 {
2732     strcpy(p_dev, plsc->DevName);
2733 }
2734 
2735 /* Set the memory area to be plotted (with the 'mem' driver) as the 'dev'
2736    member of the stream structure.  Also set the number
2737    of pixels in the memory passed in in 'plotmem'.
2738    Plotmem is a block of memory maxy by maxx by 3 bytes long, say:
2739    480 x 640 x 3 (Y, X, RGB)
2740 
2741    This memory will be freed by the user!
2742 */
2743 
2744 void
2745 c_plsmem(PLINT maxx, PLINT maxy, void *plotmem)
2746 {
2747     plsc->dev = plotmem;
2748     plP_setphy (0, maxx, 0, maxy);
2749 }
2750 
2751 /* Get the current stream pointer */
2752 
2753 void
2754 plgpls(PLStream **p_pls)
2755 {
2756     *p_pls = plsc;
2757 }
2758 
2759 /* Get the (current) run level.
2760  * Valid settings are:
2761  *   0	uninitialized
2762  *   1	initialized
2763  *   2	viewport defined
2764  *   3	world coords defined
2765  */
2766 
2767 void
2768 c_plglevel(PLINT *p_level)
2769 {
2770     *p_level = plsc->level;
2771 }
2772 
2773 /* Set the function pointer for the keyboard event handler */
2774 
2775 void
2776 plsKeyEH(void (*KeyEH) (PLGraphicsIn *, void *, int *),
2777 	 void *KeyEH_data)
2778 {
2779     plsc->KeyEH = KeyEH;
2780     plsc->KeyEH_data = KeyEH_data;
2781 }
2782 
2783 /* Set the function pointer for the (mouse) button event handler */
2784 
2785 void
2786 plsButtonEH(void (*ButtonEH) (PLGraphicsIn *, void *, int *),
2787 	    void *ButtonEH_data)
2788 {
2789     plsc->ButtonEH = ButtonEH;
2790     plsc->ButtonEH_data = ButtonEH_data;
2791 }
2792 
2793 /* Sets an optional user bop handler. */
2794 
2795 void
2796 plsbopH(void (*handler) (void *, int *), void *handler_data)
2797 {
2798     plsc->bop_handler = handler;
2799     plsc->bop_data = handler_data;
2800 }
2801 
2802 /* Sets an optional user eop handler. */
2803 
2804 void
2805 plseopH(void (*handler) (void *, int *), void *handler_data)
2806 {
2807     plsc->eop_handler = handler;
2808     plsc->eop_data = handler_data;
2809 }
2810 
2811 /* Set the variables to be used for storing error info */
2812 
2813 void
2814 plsError(PLINT *errcode, char *errmsg)
2815 {
2816     if (errcode != NULL)
2817 	plsc->errcode = errcode;
2818 
2819     if (errmsg != NULL)
2820 	plsc->errmsg = errmsg;
2821 }
2822 
2823 /* Set orientation.  Must be done before calling plinit. */
2824 
2825 void
2826 c_plsori(PLINT ori)
2827 {
2828     plsdiori((PLFLT) ori);
2829 }
2830 
2831 /*
2832  * Set pen width.  Can be done any time, but before calling plinit is best
2833  * since otherwise it may be volatile (i.e. reset on next page advance).
2834  * If width < 0 or is unchanged by the call, nothing is done.
2835  */
2836 
2837 void
2838 c_plwid(PLINT width)
2839 {
2840     if (width != plsc->width && width >= 0) {
2841 	plsc->width = width;
2842 
2843 	if (plsc->level > 0) {
2844 	    if ( ! plsc->widthlock)
2845 		plP_state(PLSTATE_WIDTH);
2846 	}
2847     }
2848 }
2849 
2850 /* Set the output file pointer */
2851 
2852 void
2853 plgfile(FILE **p_file)
2854 {
2855     *p_file = plsc->OutFile;
2856 }
2857 
2858 /* Get the output file pointer */
2859 
2860 void
2861 plsfile(FILE *file)
2862 {
2863     plsc->OutFile = file;
2864 }
2865 
2866 /* Get the (current) output file name.  Must be preallocated to >=80 bytes */
2867 /* Beyond that, I truncate it.  You have been warned. */
2868 
2869 void
2870 c_plgfnam(char *fnam)
2871 {
2872     if (fnam == NULL) {
2873 	plabort("filename string must be preallocated to >=80 bytes");
2874 	return;
2875     }
2876 
2877     *fnam = '\0';
2878     if (plsc->FileName != NULL) {
2879 	strncpy(fnam, plsc->FileName, 79);
2880 	fnam[79] = '\0';
2881     }
2882 }
2883 
2884 /* Set the output file name. */
2885 
2886 void
2887 c_plsfnam(const char *fnam)
2888 {
2889     plP_sfnam(plsc, fnam);
2890 }
2891 
2892 void
2893 c_plxsfnam(const char *fnam, const char* ext)
2894 {
2895     plPX_sfnam(plsc, fnam, ext);
2896 }
2897 
2898 /* Set the pause (on end-of-page) status */
2899 
2900 void
2901 c_plspause(PLINT mypause)		/* pmr: pause in unistd.h */
2902 {
2903     plsc->nopause = ! mypause;
2904 }
2905 
2906 /* Set the floating point precision (in number of places) in numeric labels. */
2907 
2908 void
2909 c_plprec(PLINT setp, PLINT prec)
2910 {
2911     plsc->setpre = setp;
2912     plsc->precis = prec;
2913 }
2914 
2915 /* Get the floating point precision (in number of places) in numeric labels. */
2916 
2917 void
2918 plP_gprec(PLINT *p_setp, PLINT *p_prec)
2919 {
2920     *p_setp = plsc->setpre;
2921     *p_prec = plsc->precis;
2922 }
2923 
2924 /*
2925  * Set the escape character for text strings.
2926  * From C you can pass as a character, from Fortran it needs to be the decimal
2927  * ASCII value.  Only selected characters are allowed to prevent the user from
2928  * shooting himself in the foot (a '\' isn't allowed since it conflicts with
2929  * C's use of backslash as a character escape).
2930  */
2931 
2932 void
2933 c_plsesc(char esc)
2934 {
2935     switch (esc) {
2936 	case '!':		/* ASCII 33 */
2937 	case '#':		/* ASCII 35 */
2938 	case '$':		/* ASCII 36 */
2939 	case '%':		/* ASCII 37 */
2940 	case '&':		/* ASCII 38 */
2941 	case '*':		/* ASCII 42 */
2942 	case '@':		/* ASCII 64 */
2943 	case '^':		/* ASCII 94 */
2944 	case '~':		/* ASCII 126 */
2945 	plsc->esc = esc;
2946 	break;
2947 
2948       default:
2949 	plwarn("plsesc: Invalid escape character, ignoring.");
2950     }
2951 }
2952 
2953 /* Get the escape character for text strings. */
2954 
2955 void
2956 plgesc(unsigned char *p_esc)
2957 {
2958     if (plsc->esc == '\0')
2959 	plsc->esc = '#';
2960 
2961     *p_esc = plsc->esc;
2962 }
2963 
2964 /* Set the FCI (font characterization integer) for unicode-enabled device
2965  * drivers.
2966  */
2967 void
2968 c_plsfci(PLUNICODE fci)
2969 {
2970    /* Always mark FCI as such. */
2971    plsc->fci = fci | PL_FCI_MARK;
2972 }
2973 
2974 /* Get the FCI (font characterization integer) for unicode-enabled device
2975  * drivers.
2976  */
2977 void
2978 c_plgfci(PLUNICODE *pfci)
2979 {
2980    /* Always mark FCI as such. */
2981    *pfci = plsc->fci | PL_FCI_MARK;
2982 }
2983 /* Store hex digit value shifted to the left by hexdigit hexadecimal digits
2984  * into pre-existing FCI.
2985  */
2986 void
2987 plP_hex2fci(unsigned char hexdigit, unsigned char hexpower, PLUNICODE *pfci)
2988 {
2989    PLUNICODE mask;
2990    hexpower = hexpower & PL_FCI_HEXPOWER_MASK;
2991    mask = ~ (((PLUNICODE) PL_FCI_HEXDIGIT_MASK) << ((PLUNICODE) 4*hexpower));
2992    *pfci = *pfci & mask;
2993    mask = (((PLUNICODE) (hexdigit & PL_FCI_HEXDIGIT_MASK)) << (4*hexpower));
2994    *pfci = *pfci | mask;
2995 }
2996 
2997 /* Retrieve hex digit value from FCI that is masked out and shifted to the
2998  * right by hexpower hexadecimal digits. */
2999 void
3000 plP_fci2hex(PLUNICODE fci, unsigned char *phexdigit, unsigned char hexpower)
3001 {
3002    PLUNICODE mask;
3003    hexpower = hexpower & PL_FCI_HEXPOWER_MASK;
3004    mask = (((PLUNICODE) PL_FCI_HEXPOWER_MASK) << ((PLUNICODE) (4*hexpower)));
3005    *phexdigit = (unsigned char) ((fci & mask) >>
3006 				 ((PLUNICODE) (4*hexpower)));
3007 }
3008 
3009 /* Get the current library version number */
3010 /* Note: you MUST have allocated space for this (80 characters is safe) */
3011 void
3012 c_plgver(char *p_ver)
3013 {
3014     strcpy(p_ver, PLPLT_VERSION);
3015 }
3016 
3017 /* Set inferior X window */
3018 
3019 void
3020 plsxwin(PLINT window_id)
3021 {
3022     plsc->window_id = window_id;
3023 }
3024 
3025 /*--------------------------------------------------------------------------*\
3026  *  These set/get information for family files, and may be called prior
3027  *  to plinit to set up the necessary parameters.  Arguments:
3028  *
3029  *	fam	familying flag (boolean)
3030  *	num	member number
3031  *	bmax	maximum member size
3032 \*--------------------------------------------------------------------------*/
3033 
3034 /* Get family file parameters */
3035 
3036 void
3037 c_plgfam(PLINT *p_fam, PLINT *p_num, PLINT *p_bmax)
3038 {
3039     *p_fam = plsc->family;
3040     *p_num = plsc->member;
3041     *p_bmax = plsc->bytemax;
3042 }
3043 
3044 /* Set family file parameters */
3045 
3046 void
3047 c_plsfam(PLINT fam, PLINT num, PLINT bmax)
3048 {
3049     if (plsc->level > 0)
3050 	plwarn("plsfam: Must be called before plinit.");
3051 
3052     if (fam >= 0)
3053 	plsc->family = fam;
3054     if (num >= 0)
3055 	plsc->member = num;
3056     if (bmax >= 0)
3057 	plsc->bytemax = bmax;
3058 }
3059 
3060 /* Advance to the next family file on the next new page */
3061 
3062 void
3063 c_plfamadv(void)
3064 {
3065     plsc->famadv = 1;
3066 }
3067 
3068 /*--------------------------------------------------------------------------*\
3069  *  Interface routines for axis labling parameters.
3070  *  See pldtik.c for more info.
3071 \*--------------------------------------------------------------------------*/
3072 
3073 /* Get x axis labeling parameters */
3074 
3075 void
3076 c_plgxax(PLINT *p_digmax, PLINT *p_digits)
3077 {
3078     *p_digmax = plsc->xdigmax;
3079     *p_digits = plsc->xdigits;
3080 }
3081 
3082 /* Set x axis labeling parameters */
3083 
3084 void
3085 c_plsxax(PLINT digmax, PLINT digits)
3086 {
3087     plsc->xdigmax = digmax;
3088     plsc->xdigits = digits;
3089 }
3090 
3091 /* Get y axis labeling parameters */
3092 
3093 void
3094 c_plgyax(PLINT *p_digmax, PLINT *p_digits)
3095 {
3096     *p_digmax = plsc->ydigmax;
3097     *p_digits = plsc->ydigits;
3098 }
3099 
3100 /* Set y axis labeling parameters */
3101 
3102 void
3103 c_plsyax(PLINT digmax, PLINT digits)
3104 {
3105     plsc->ydigmax = digmax;
3106     plsc->ydigits = digits;
3107 }
3108 
3109 /* Get z axis labeling parameters */
3110 
3111 void
3112 c_plgzax(PLINT *p_digmax, PLINT *p_digits)
3113 {
3114     *p_digmax = plsc->zdigmax;
3115     *p_digits = plsc->zdigits;
3116 }
3117 
3118 /* Set z axis labeling parameters */
3119 
3120 void
3121 c_plszax(PLINT digmax, PLINT digits)
3122 {
3123     plsc->zdigmax = digmax;
3124     plsc->zdigits = digits;
3125 }
3126 
3127 /* Get character default height and current (scaled) height */
3128 
3129 void
3130 c_plgchr(PLFLT *p_def, PLFLT *p_ht)
3131 {
3132     *p_def = plsc->chrdef;
3133     *p_ht = plsc->chrht;
3134 }
3135 
3136 /* Get viewport boundaries in normalized device coordinates */
3137 
3138 void
3139 c_plgvpd(PLFLT *p_xmin, PLFLT *p_xmax, PLFLT *p_ymin, PLFLT *p_ymax)
3140 {
3141     *p_xmin = plsc->vpdxmi;
3142     *p_xmax = plsc->vpdxma;
3143     *p_ymin = plsc->vpdymi;
3144     *p_ymax = plsc->vpdyma;
3145 }
3146 
3147 /* Get viewport boundaries in world coordinates */
3148 
3149 void
3150 c_plgvpw(PLFLT *p_xmin, PLFLT *p_xmax, PLFLT *p_ymin, PLFLT *p_ymax)
3151 {
3152     *p_xmin = plsc->vpwxmi;
3153     *p_xmax = plsc->vpwxma;
3154     *p_ymin = plsc->vpwymi;
3155     *p_ymax = plsc->vpwyma;
3156 }
3157 
3158 /*--------------------------------------------------------------------------*\
3159  *  These should not be called by the user.
3160 \*--------------------------------------------------------------------------*/
3161 
3162 /* Get x-y domain in world coordinates for 3d plots */
3163 
3164 void
3165 plP_gdom(PLFLT *p_xmin, PLFLT *p_xmax, PLFLT *p_ymin, PLFLT *p_ymax)
3166 {
3167     *p_xmin = plsc->domxmi;
3168     *p_xmax = plsc->domxma;
3169     *p_ymin = plsc->domymi;
3170     *p_ymax = plsc->domyma;
3171 }
3172 
3173 /* Get vertical (z) scale parameters for 3-d plot */
3174 
3175 void
3176 plP_grange(PLFLT *p_zscl, PLFLT *p_zmin, PLFLT *p_zmax)
3177 {
3178     *p_zscl = plsc->zzscl;
3179     *p_zmin = plsc->ranmi;
3180     *p_zmax = plsc->ranma;
3181 }
3182 
3183 /* Get parameters used in 3d plots */
3184 
3185 void
3186 plP_gw3wc(PLFLT *p_dxx, PLFLT *p_dxy, PLFLT *p_dyx, PLFLT *p_dyy, PLFLT *p_dyz)
3187 {
3188     *p_dxx = plsc->cxx;
3189     *p_dxy = plsc->cxy;
3190     *p_dyx = plsc->cyx;
3191     *p_dyy = plsc->cyy;
3192     *p_dyz = plsc->cyz;
3193 }
3194 
3195 /* Get clip boundaries in physical coordinates */
3196 
3197 void
3198 plP_gclp(PLINT *p_ixmin, PLINT *p_ixmax, PLINT *p_iymin, PLINT *p_iymax)
3199 {
3200     *p_ixmin = plsc->clpxmi;
3201     *p_ixmax = plsc->clpxma;
3202     *p_iymin = plsc->clpymi;
3203     *p_iymax = plsc->clpyma;
3204 }
3205 
3206 /* Set clip boundaries in physical coordinates */
3207 
3208 void
3209 plP_sclp(PLINT ixmin, PLINT ixmax, PLINT iymin, PLINT iymax)
3210 {
3211     plsc->clpxmi = ixmin;
3212     plsc->clpxma = ixmax;
3213     plsc->clpymi = iymin;
3214     plsc->clpyma = iymax;
3215 }
3216 
3217 /* Get physical device limits in physical coordinates */
3218 
3219 void
3220 plP_gphy(PLINT *p_ixmin, PLINT *p_ixmax, PLINT *p_iymin, PLINT *p_iymax)
3221 {
3222     *p_ixmin = plsc->phyxmi;
3223     *p_ixmax = plsc->phyxma;
3224     *p_iymin = plsc->phyymi;
3225     *p_iymax = plsc->phyyma;
3226 }
3227 
3228 /* Get number of subpages on physical device and current subpage */
3229 
3230 void
3231 plP_gsub(PLINT *p_nx, PLINT *p_ny, PLINT *p_cs)
3232 {
3233     *p_nx = plsc->nsubx;
3234     *p_ny = plsc->nsuby;
3235     *p_cs = plsc->cursub;
3236 }
3237 
3238 /* Set number of subpages on physical device and current subpage */
3239 
3240 void
3241 plP_ssub(PLINT nx, PLINT ny, PLINT cs)
3242 {
3243     plsc->nsubx = nx;
3244     plsc->nsuby = ny;
3245     plsc->cursub = cs;
3246 }
3247 
3248 /* Get number of pixels to a millimeter */
3249 
3250 void
3251 plP_gpixmm(PLFLT *p_x, PLFLT *p_y)
3252 {
3253     *p_x = plsc->xpmm;
3254     *p_y = plsc->ypmm;
3255 }
3256 
3257 /* All the drivers call this to set physical pixels/mm. */
3258 
3259 void
3260 plP_setpxl(PLFLT xpmm, PLFLT ypmm)
3261 {
3262     plsc->xpmm = xpmm;
3263     plsc->ypmm = ypmm;
3264     plsc->umx = 1000.0 / plsc->xpmm;
3265     plsc->umy = 1000.0 / plsc->ypmm;
3266 }
3267 
3268 /* Sets up physical limits of plotting device. */
3269 
3270 void
3271 plP_setphy(PLINT xmin, PLINT xmax, PLINT ymin, PLINT ymax)
3272 {
3273     if (xmin > xmax || ymin > ymax)
3274 	plexit("plP_setphy: device minima must not exceed maxima");
3275 
3276     plsc->phyxmi = xmin;
3277     plsc->phyxma = xmax;
3278     plsc->phyymi = ymin;
3279     plsc->phyyma = ymax;
3280     plsc->phyxlen = xmax - xmin;
3281     plsc->phyylen = ymax - ymin;
3282 }
3283 
3284 /*--------------------------------------------------------------------------*\
3285  * void c_plscompression()
3286  *
3287  * Set compression.
3288  * Has to be done before plinit.
3289 \*--------------------------------------------------------------------------*/
3290 
3291 void
3292 c_plscompression(PLINT compression)
3293 {
3294   if (plsc->level <= 0)
3295      {
3296       plsc->dev_compression=compression;
3297      }
3298 }
3299 
3300 /*--------------------------------------------------------------------------*\
3301  * void c_plgcompression()
3302  *
3303  * Get compression
3304 \*--------------------------------------------------------------------------*/
3305 
3306 void
3307 c_plgcompression(PLINT *compression)
3308 {
3309     *compression = plsc->dev_compression;
3310 }
3311 
3312 
3313 /*--------------------------------------------------------------------------*\
3314  * void plP_getinitdriverlist()
3315  *
3316  * Check to see if a driver/stream has been initialised
3317  * Returns a space separated list of matches streams/drivers
3318  * If more than one stream uses the same device, then the device name
3319  * will be returned for each stream.
3320  * Caller must allocate enough memory for "names" to hold the answer.
3321 \*--------------------------------------------------------------------------*/
3322 
3323 void
3324 plP_getinitdriverlist(char *names)
3325 {
3326 int i;
3327 
3328 for (i=0;i<PL_NSTREAMS;++i)
3329    {
3330     if (pls[i]!=NULL)
3331        {
3332        if (i==0)
3333           strcpy(names,pls[i]->DevName);
3334        else
3335           {
3336           strcat(names," ");
3337           strcat(names,pls[i]->DevName);
3338           }
3339        }
3340     else
3341        break;
3342    }
3343 }
3344 
3345 
3346 /*--------------------------------------------------------------------------*\
3347  * PLINT plP_checkdriverinit()
3348  *
3349  * Checks from a list of given drivers which ones have been initialised
3350  * and returns the number of devices matching the list, or -1 if in error.
3351  * Effectively returns the number of streams matching the given stream.
3352 \*--------------------------------------------------------------------------*/
3353 
3354 PLINT plP_checkdriverinit( char *names)
3355 {
3356 char *buff;
3357 char *tok=NULL;
3358 PLINT ret=0;   /* set up return code to 0, the value if no devices match*/
3359 
3360 buff=(char *)malloc((size_t) PL_NSTREAMS*8); /* Allocate enough memory for 8
3361                                                 characters for each possible stream */
3362 
3363 if (buff!=NULL)
3364    {
3365     memset(buff,0,PL_NSTREAMS*8);    /* Make sure we clear it               */
3366     plP_getinitdriverlist(buff);     /* Get the list of initialised devices */
3367 
3368     for (tok = strtok(buff, " ,");   /* Check each device against the "name" */
3369          tok; tok=strtok(0, " ,"))   /* supplied to the subroutine   */
3370         {
3371         if (strstr(names,tok)!=NULL)  /* Check to see if the device has been initialised */
3372            {
3373             ret++;                   /* Bump the return code if it has      */
3374            }
3375         }
3376     free(buff);                      /* Clear up that memory we allocated   */
3377     }
3378 else
3379    ret=-1;                           /* Error flag */
3380 
3381 return(ret);
3382 }
3383 
3384 
3385 /*--------------------------------------------------------------------------*\
3386  * plP_image
3387  *
3388  * Author: Alessandro Mirone, Nov 2001
3389  *
3390  *
3391  *
3392 \*--------------------------------------------------------------------------*/
3393 
3394 void
3395 plP_image(short *x, short *y, unsigned short *z , PLINT nx, PLINT ny, PLFLT xmin, PLFLT ymin, PLFLT dx, PLFLT dy, unsigned short zmin, unsigned short zmax)
3396 {
3397   PLINT i, npts;
3398   short *myxscl, *myyscl;
3399   int   plbuf_write;
3400 
3401   plsc->page_status = DRAWING;
3402 
3403   if (plsc->dev_fastimg == 0) {
3404     plimageslow(x, y, z, nx-1, ny-1,
3405          xmin, ymin, dx, dy, zmin, zmax);
3406     return ;
3407   }
3408 
3409   if (plsc->plbuf_write) {
3410     IMG_DT img_dt;
3411 
3412     img_dt.xmin=xmin;
3413     img_dt.ymin=ymin;
3414     img_dt.dx=dx;
3415     img_dt.dy=dy;
3416 
3417     plsc->dev_ix = x;
3418     plsc->dev_iy = y;
3419     plsc->dev_z = z;
3420     plsc->dev_nptsX = nx;
3421     plsc->dev_nptsY = ny;
3422     plsc->dev_zmin = zmin;
3423     plsc->dev_zmax = zmax;
3424 
3425     plbuf_esc(plsc, PLESC_IMAGE, &img_dt);
3426   }
3427 
3428   /* avoid re-saving plot buffer while in plP_esc() */
3429   plbuf_write = plsc->plbuf_write;
3430   plsc->plbuf_write = 0;
3431 
3432   npts = nx*ny;
3433   if (plsc->difilt) { /* isn't this odd? when replaying the plot buffer, e.g., when resizing the window, difilt() is caled again! the plot buffer should already contain the transformed data--it would save a lot of time! (and allow for differently oriented plots when in multiplot mode) */
3434     PLINT clpxmi, clpxma, clpymi, clpyma;
3435 
3436     myxscl = (short *) malloc(nx*ny*sizeof(short));
3437     myyscl = (short *) malloc(nx*ny*sizeof(short));
3438     for (i = 0; i < npts; i++) {
3439       myxscl[i] = x[i];
3440       myyscl[i] = y[i];
3441     }
3442     sdifilt(myxscl, myyscl, npts, &clpxmi, &clpxma, &clpymi, &clpyma);
3443     plsc->imclxmin = clpxmi;
3444     plsc->imclymin = clpymi;
3445     plsc->imclxmax = clpxma;
3446     plsc->imclymax = clpyma;
3447     grimage(myxscl, myyscl, z, nx, ny);
3448     free(myxscl);
3449     free(myyscl);
3450   } else {
3451     plsc->imclxmin = plsc->phyxmi;
3452     plsc->imclymin = plsc->phyymi;
3453     plsc->imclxmax = plsc->phyxma;
3454     plsc->imclymax = plsc->phyyma;
3455     grimage(x, y, z, nx, ny );
3456   }
3457   plsc->plbuf_write = plbuf_write;
3458 }
3459