1 //      Misc. control routines, like begin, end, exit, change graphics/text
2 //      mode, change color.  Includes some spillage from plcore.c.  If you
3 //      don't know where it should go, put it here.
4 //
5 // Copyright (C) 1993-2005 Maurice LeBrun
6 // Copyright (C) 1995-2002 Geoffrey Furnish
7 // Copyright (C) 1996 Rady Shouman
8 // Copyright (C) 2000-2019 Alan W. Irwin
9 // Copyright (C) 2001-2003 Joao Cardoso
10 // Copyright (C) 2001-2005 Rafael Laboissiere
11 // Copyright (C) 2002 Vince Darley
12 // Copyright (C) 2002-2007 Andrew Roach
13 // Copyright (C) 2004-2013 Andrew Ross
14 // Copyright (C) 2005 Thomas Duck
15 // Copyright (C) 2006-2011 Arjen Markus
16 // Copyright (C) 2006-2011 Hazen Babcock
17 // Copyright (C) 2008-2009 Werner Smekal
18 // Copyright (C) 2009-2011 Hezekiah M. Carty
19 // Copyright (C) 2015-2018 Phil Rosenberg
20 //
21 // This file is part of PLplot.
22 //
23 // PLplot is free software; you can redistribute it and/or modify
24 // it under the terms of the GNU Library General Public License as published
25 // by the Free Software Foundation; either version 2 of the License, or
26 // (at your option) any later version.
27 //
28 // PLplot is distributed in the hope that it will be useful,
29 // but WITHOUT ANY WARRANTY; without even the implied warranty of
30 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
31 // GNU Library General Public License for more details.
32 //
33 // You should have received a copy of the GNU Library General Public License
34 // along with PLplot; if not, write to the Free Software
35 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
36 //
37 //
38 
39 //! @file
40 //!
41 //! Part 1: Color map routines.
42 //! Part 2: "A grab-bag of various control routines".
43 //!
44 
45 #define DEBUG
46 
47 #define NEED_PLDEBUG
48 #include "plplotP.h"
49 #ifdef macintosh
50 #include "mac.h"
51 // for plMacLibOpen prototype; used in plLibOpen
52 #endif
53 
54 #ifdef DJGPP                    // dos386/djgpp
55 #ifdef __unix
56 #undef __unix
57 #endif
58 #endif
59 
60 #ifdef __unix
61 #include <sys/types.h>
62 #include <sys/stat.h>
63 #ifdef PL_HAVE_UNISTD_H
64 #include <unistd.h>
65 #endif
66 #include <errno.h>
67 #endif
68 
69 // Random number generator (Mersenne Twister)
70 #include "mt19937ar.h"
71 
72 #define BUFFER_SIZE    256
73 #define COLLEN         30
74 #define PALLEN         160
75 #define MSGLEN         1024
76 
77 // small epsilon for fuzzy range checks that is still large enough to
78 // work even in the single precision floating point case.
79 #define FUZZ_EPSILON    1.e-4
80 
81 // Used by any external init code to suggest a path
82 char PLDLLIMPEXP * plplotLibDir = 0;
83 
84 // Static functions
85 
86 static void
87 limit_rgba_range( PLCHAR_VECTOR message, PLINT_NC_SCALAR r, PLINT_NC_SCALAR g, PLINT_NC_SCALAR b, PLFLT_NC_SCALAR alpha );
88 
89 static void
90 color_set( PLINT i, U_CHAR r, U_CHAR g, U_CHAR b, PLFLT a, PLCHAR_VECTOR name );
91 
92 static void
93 strcat_delim( char *dirspec );
94 
95 static int
96 ( *exit_handler )( PLCHAR_VECTOR errormsg );
97 
98 static void
99 ( *abort_handler )( PLCHAR_VECTOR errormsg );
100 
101 static void
102 plcmap0_def( int imin, int imax );
103 
104 static void
105 plcmap1_def( void );
106 
107 static PLFLT
108 value( double n1, double n2, double hue );
109 
110 static char *
111 read_line( char *buffer, int length, FILE *fp );
112 
113 static void
114 cmap0_palette_read( PLCHAR_VECTOR filename,
115                     int *number_colors, unsigned int **r, unsigned int **g,
116                     unsigned int **b, double **a );
117 
118 // An additional hardwired location for lib files.
119 // I have no plans to change these again, ever.
120 
121 #if defined ( DJGPP )
122 #ifndef PLLIBDEV
123 #define PLLIBDEV    "c:/plplot/lib"
124 #endif
125 
126 #elif defined ( MSDOS )
127 #ifndef PLLIBDEV
128 #define PLLIBDEV    "c:\\plplot\\lib"
129 #endif
130 
131 #else
132 
133 // Anything else is assumed to be Unix
134 
135 #ifndef PLLIBDEV
136 #define PLLIBDEV    "/usr/local/plplot/lib"
137 #endif
138 
139 #endif
140 
141 //--------------------------------------------------------------------------
142 //  Routines that deal with colors & color maps.
143 //--------------------------------------------------------------------------
144 
145 //--------------------------------------------------------------------------
146 // plcol0()
147 //
148 //! Set color, map 0.  Argument is a integer between 0 and plsc->ncol0.
149 //!
150 //! @param icol0 The index of the color map 0 color to use as the current
151 //! color. (0 - plsc->ncol0).
152 
153 void
c_plcol0(PLINT icol0)154 c_plcol0( PLINT icol0 )
155 {
156     if ( plsc->level < 1 )
157     {
158         plabort( "plcol0: Please call plinit first" );
159         return;
160     }
161     if ( icol0 < 0 || icol0 >= plsc->ncol0 )
162     {
163         char buffer[BUFFER_SIZE];
164         snprintf( buffer, BUFFER_SIZE, "plcol0: Invalid color map entry: %d", (int) icol0 );
165         plabort( buffer );
166         return;
167     }
168 
169     plsc->icol0      = icol0;
170     plsc->curcolor.r = plsc->cmap0[icol0].r;
171     plsc->curcolor.g = plsc->cmap0[icol0].g;
172     plsc->curcolor.b = plsc->cmap0[icol0].b;
173     plsc->curcolor.a = plsc->cmap0[icol0].a;
174 
175     plsc->curcmap = 0;
176     plP_state( PLSTATE_COLOR0 );
177 }
178 
179 //--------------------------------------------------------------------------
180 // plcol1()
181 //
182 //! Set color for cmap1.  Argument is a float between MIN_PLFLT_CMAP1 and MAX_PLFLT_CMAP1
183 //!
184 //! @param col1 The index of the color map 1 color to use as the current
185 //! color. (MIN_PLFLT_CMAP1 - MAX_PLFLT_CMAP1)
186 
187 void
c_plcol1(PLFLT col1)188 c_plcol1( PLFLT col1 )
189 {
190     PLINT icol1;
191 
192     if ( plsc->level < 1 )
193     {
194         plabort( "plcol1: Please call plinit first" );
195         return;
196     }
197     if ( col1 < MIN_PLFLT_CMAP1 || col1 > MAX_PLFLT_CMAP1 || isnan( col1 ) )
198     {
199         plwarn( "plcol1: Invalid cmap1 index" );
200         fprintf( stderr, "%s\n", "Further information relevant to this warning:" );
201         fprintf( stderr, "%s%e\n", "Invalid index = ", col1 );
202         col1 = MIN_PLFLT_CMAP1;
203         fprintf( stderr, "%s%e\n", "Corrected index = ", col1 );
204     }
205 
206     icol1 = (PLINT) ( col1 * plsc->ncol1 );
207     icol1 = MIN( icol1, plsc->ncol1 - 1 );
208 
209     plsc->icol1      = icol1;
210     plsc->curcolor.r = plsc->cmap1[plsc->icol1].r;
211     plsc->curcolor.g = plsc->cmap1[plsc->icol1].g;
212     plsc->curcolor.b = plsc->cmap1[plsc->icol1].b;
213     plsc->curcolor.a = plsc->cmap1[plsc->icol1].a;
214 
215     plsc->curcmap = 1;
216     plP_state( PLSTATE_COLOR1 );
217 }
218 
219 //--------------------------------------------------------------------------
220 // plscolbg()
221 //
222 //! Set the background color (cmap0[0]) by 8 bit RGB value
223 //!
224 //! @param r Red value of the background color (MIN_PLINT_RGB - MAX_PLINT_RGB).
225 //! @param g Green value of the background color (MIN_PLINT_RGB - MAX_PLINT_RGB).
226 //! @param b Blue value of the background color (MIN_PLINT_RGB - MAX_PLINT_RGB).
227 
228 void
c_plscolbg(PLINT r,PLINT g,PLINT b)229 c_plscolbg( PLINT r, PLINT g, PLINT b )
230 {
231     plscol0( 0, r, g, b );
232 }
233 
234 //--------------------------------------------------------------------------
235 // plscolbga()
236 //
237 //! Set the background color (cmap0[0]) by 8 bit RGB value and alpha value
238 //!
239 //! @param r Red value of the background color (MIN_PLINT_RGB - MAX_PLINT_RGB).
240 //! @param g Green value of the background color (MIN_PLINT_RGB - MAX_PLINT_RGB).
241 //! @param b Blue value of the background color (MIN_PLINT_RGB - MAX_PLINT_RGB).
242 //! @param alpha Alpha (transparency) value of the background color
243 //! (MIN_PLFLT_ALPHA - MAX_PLFLT_ALPHA).
244 
245 //--------------------------------------------------------------------------
246 
247 void
c_plscolbga(PLINT r,PLINT g,PLINT b,PLFLT alpha)248 c_plscolbga( PLINT r, PLINT g, PLINT b, PLFLT alpha )
249 {
250     plscol0a( 0, r, g, b, alpha );
251 }
252 
253 //--------------------------------------------------------------------------
254 // plgcolbg()
255 //
256 //! Returns the background color (cmap0[0]) by 8 bit RGB value
257 //!
258 //! @param r Current red value of the background color.
259 //! @param g Current green value of the background color.
260 //! @param b Current blue value of the background color.
261 
262 void
c_plgcolbg(PLINT * r,PLINT * g,PLINT * b)263 c_plgcolbg( PLINT *r, PLINT *g, PLINT *b )
264 {
265     plgcol0( 0, r, g, b );
266 }
267 
268 //--------------------------------------------------------------------------
269 // plgcolbga()
270 //
271 //! Returns the background color (cmap0[0]) by 8 bit RGB value and alpha value
272 //!
273 //! @param r Current red value of the background color.
274 //! @param g Current green value of the background color.
275 //! @param b Current blue value of the background color.
276 //! @param alpha Current alpha value of the background color.
277 
278 void
c_plgcolbga(PLINT * r,PLINT * g,PLINT * b,PLFLT * alpha)279 c_plgcolbga( PLINT *r, PLINT *g, PLINT *b, PLFLT *alpha )
280 {
281     plgcol0a( 0, r, g, b, alpha );
282 }
283 
284 //--------------------------------------------------------------------------
285 // plscol0()
286 //
287 //! Set a given color from color map 0 by 8 bit RGB value
288 //! Does not result in any additional cells to be allocated.
289 //!
290 //! @param icol0 index of the color to set (0 - plsc->ncol0)
291 //! @param r Red value of the color (MIN_PLINT_RGB - MAX_PLINT_RGB).
292 //! @param g Green value of the color (MIN_PLINT_RGB - MAX_PLINT_RGB).
293 //! @param b Blue value of the color (MIN_PLINT_RGB - MAX_PLINT_RGB).
294 
295 void
c_plscol0(PLINT icol0,PLINT r,PLINT g,PLINT b)296 c_plscol0( PLINT icol0, PLINT r, PLINT g, PLINT b )
297 {
298     if ( plsc->cmap0 == NULL )
299         plscmap0n( 0 );
300     if ( icol0 < 0 || icol0 >= plsc->ncol0 )
301     {
302         char buffer[BUFFER_SIZE];
303         snprintf( buffer, BUFFER_SIZE, "plscol0: Illegal color table value: %d", (int) icol0 );
304         plabort( buffer );
305         return;
306     }
307 
308     limit_rgba_range( "plscol0: invalid cmap0 RGB color has been corrected", &r, &g, &b, NULL );
309 
310     plscol0a( icol0, r, g, b, MAX_PLFLT_ALPHA );
311 }
312 
313 //--------------------------------------------------------------------------
314 // plscol0a()
315 //
316 //! Set a given color from color map 0 by 8 bit RGB value and alpha value.
317 //! Does not result in any additional cells to be allocated.
318 //!
319 //! @param icol0 index of the color to set (0 - plsc->ncol0)
320 //! @param r Red value of the color (MIN_PLINT_RGB - MAX_PLINT_RGB).
321 //! @param g Green value of the color (MIN_PLINT_RGB - MAX_PLINT_RGB).
322 //! @param b Blue value of the color (MIN_PLINT_RGB - MAX_PLINT_RGB).
323 //! @param alpha Alpha value of the color (MIN_PLFLT_ALPHA - MAX_PLFLT_ALPHA).
324 
325 void
c_plscol0a(PLINT icol0,PLINT r,PLINT g,PLINT b,PLFLT alpha)326 c_plscol0a( PLINT icol0, PLINT r, PLINT g, PLINT b, PLFLT alpha )
327 {
328     if ( plsc->cmap0 == NULL )
329         plscmap0n( 0 );
330     if ( icol0 < 0 || icol0 >= plsc->ncol0 )
331     {
332         char buffer[BUFFER_SIZE];
333         snprintf( buffer, BUFFER_SIZE, "plscol0a: Illegal color table value: %d", (int) icol0 );
334         plabort( buffer );
335         return;
336     }
337     limit_rgba_range( "plscol0a: invalid cmap0 RGBA color has been corrected", &r, &g, &b, &alpha );
338     plsc->cmap0[icol0].r = (unsigned char) r;
339     plsc->cmap0[icol0].g = (unsigned char) g;
340     plsc->cmap0[icol0].b = (unsigned char) b;
341     plsc->cmap0[icol0].a = alpha;
342 
343     if ( plsc->level > 0 )
344         plP_state( PLSTATE_CMAP0 );
345 }
346 
347 //--------------------------------------------------------------------------
348 // plgcol0()
349 //
350 //! Returns 8 bit RGB values for given color from color map 0
351 //! Values are negative if an invalid color id is given
352 //!
353 //! @param icol0 Index of the color to be return (0 - plsc->ncol0).
354 //! @param r Current red value of the color.
355 //! @param g Current green value of the color.
356 //! @param b Current blue value of the color.
357 
358 void
c_plgcol0(PLINT icol0,PLINT * r,PLINT * g,PLINT * b)359 c_plgcol0( PLINT icol0, PLINT *r, PLINT *g, PLINT *b )
360 {
361     if ( plsc->cmap0 == NULL )
362         plscmap0n( 0 );
363 
364     *r = -1;
365     *g = -1;
366     *b = -1;
367 
368     if ( icol0 < 0 || icol0 > plsc->ncol0 )
369     {
370         char buffer[BUFFER_SIZE];
371         snprintf( buffer, BUFFER_SIZE, "plgcol0: Invalid color index: %d", (int) icol0 );
372         plabort( buffer );
373         return;
374     }
375 
376     *r = plsc->cmap0[icol0].r;
377     *g = plsc->cmap0[icol0].g;
378     *b = plsc->cmap0[icol0].b;
379 
380     return;
381 }
382 
383 //--------------------------------------------------------------------------
384 // plgcol0a()
385 //
386 //! Returns 8 bit RGB values for given color from color map 0 and alpha value
387 //! Values are negative if an invalid color id is given
388 //!
389 //! @param icol0 Index of the color to be return (0 - plsc->ncol0).
390 //! @param r Current red value of the color.
391 //! @param g Current green value of the color.
392 //! @param b Current blue value of the color.
393 //! @param alpha Current alpha value of the color.
394 
395 void
c_plgcol0a(PLINT icol0,PLINT * r,PLINT * g,PLINT * b,PLFLT * alpha)396 c_plgcol0a( PLINT icol0, PLINT *r, PLINT *g, PLINT *b, PLFLT *alpha )
397 {
398     if ( plsc->cmap0 == NULL )
399         plscmap0n( 0 );
400 
401     if ( icol0 < 0 || icol0 > plsc->ncol0 )
402     {
403         char buffer[BUFFER_SIZE];
404         snprintf( buffer, BUFFER_SIZE, "plgcol0: Invalid color index: %d.  Return opaque red as a warning of this condition.", (int) icol0 );
405         *r     = MAX_PLINT_RGB;
406         *g     = MIN_PLINT_RGB;
407         *b     = MIN_PLINT_RGB;
408         *alpha = MAX_PLFLT_ALPHA;
409 
410         plabort( buffer );
411         return;
412     }
413 
414     *r     = plsc->cmap0[icol0].r;
415     *g     = plsc->cmap0[icol0].g;
416     *b     = plsc->cmap0[icol0].b;
417     *alpha = plsc->cmap0[icol0].a;
418 
419     return;
420 }
421 
422 //--------------------------------------------------------------------------
423 // plscmap0()
424 //
425 //! Set color map 0 colors by 8 bit RGB values.  This sets the entire color
426 //! map -- only as many colors as specified will be allocated.
427 //!
428 //! @param r Array of red values.
429 //! @param g Array of green values.
430 //! @param b Array of blue values.
431 //! @param ncol0 Total number of RGB values.
432 
433 void
c_plscmap0(PLINT_VECTOR r,PLINT_VECTOR g,PLINT_VECTOR b,PLINT ncol0)434 c_plscmap0( PLINT_VECTOR r, PLINT_VECTOR g, PLINT_VECTOR b, PLINT ncol0 )
435 {
436     int   i;
437     PLINT nc_r, nc_g, nc_b;
438 
439     plscmap0n( ncol0 );
440 
441     for ( i = 0; i < plsc->ncol0; i++ )
442     {
443         // Need these assignments so that r[i], g[i], and b[i] remain
444         // unchanged as per their PLINT_VECTOR types.
445         nc_r = r[i];
446         nc_g = g[i];
447         nc_b = b[i];
448         limit_rgba_range( "plscmap0: invalid cmap0 RGB color has been corrected", &nc_r, &nc_g, &nc_b, NULL );
449         plsc->cmap0[i].r = (unsigned char) nc_r;
450         plsc->cmap0[i].g = (unsigned char) nc_g;
451         plsc->cmap0[i].b = (unsigned char) nc_b;
452         plsc->cmap0[i].a = MAX_PLFLT_ALPHA;
453     }
454 
455     if ( plsc->level > 0 )
456         plP_state( PLSTATE_CMAP0 );
457 }
458 
459 //--------------------------------------------------------------------------
460 // plscmap0a()
461 //
462 //! Set color map 0 colors by 8 bit RGB and alpha value.  This sets the
463 //! entire color map -- only as many colors as specified will be allocated.
464 //!
465 //! @param r Array of red values.
466 //! @param g Array of green values.
467 //! @param b Array of blue values.
468 //! @param alpha Array of alpha values.
469 //! @param ncol0 Total number of RGBA values.
470 
471 void
c_plscmap0a(PLINT_VECTOR r,PLINT_VECTOR g,PLINT_VECTOR b,PLFLT_VECTOR alpha,PLINT ncol0)472 c_plscmap0a( PLINT_VECTOR r, PLINT_VECTOR g, PLINT_VECTOR b, PLFLT_VECTOR alpha, PLINT ncol0 )
473 {
474     int   i;
475     PLINT nc_r, nc_g, nc_b;
476     PLFLT nc_alpha;
477 
478 
479     plscmap0n( ncol0 );
480 
481     for ( i = 0; i < plsc->ncol0; i++ )
482     {
483         // Need these assignments so that r[i], g[i], b[i], and
484         // alpha[i] remain unchanged as per their PLINT_VECTOR and
485         // PLFLT_VECTOR types.
486         nc_r     = r[i];
487         nc_g     = g[i];
488         nc_b     = b[i];
489         nc_alpha = alpha[i];
490         limit_rgba_range( "plscmap0a: invalid cmap0 RGBA color has been corrected", &nc_r, &nc_g, &nc_b, &nc_alpha );
491         plsc->cmap0[i].r = (unsigned char) nc_r;
492         plsc->cmap0[i].g = (unsigned char) nc_g;
493         plsc->cmap0[i].b = (unsigned char) nc_b;
494         plsc->cmap0[i].a = nc_alpha;
495     }
496 
497     if ( plsc->level > 0 )
498         plP_state( PLSTATE_CMAP0 );
499 }
500 
501 //--------------------------------------------------------------------------
502 // plscmap1()
503 //
504 //! Set color map 1 colors by 8 bit RGB values
505 //! This also sets the number of colors.
506 //!
507 //! @param r Array of red values.
508 //! @param g Array of green values.
509 //! @param b Array of blue values.
510 //! @param ncol1 Total number of RGB values.
511 
512 void
c_plscmap1(PLINT_VECTOR r,PLINT_VECTOR g,PLINT_VECTOR b,PLINT ncol1)513 c_plscmap1( PLINT_VECTOR r, PLINT_VECTOR g, PLINT_VECTOR b, PLINT ncol1 )
514 {
515     int   i;
516     PLINT nc_r, nc_g, nc_b;
517 
518     plscmap1n( ncol1 );
519 
520     for ( i = 0; i < plsc->ncol1; i++ )
521     {
522         // Need these assignments so that r[i], g[i], and b[i] remain
523         // unchanged as per their PLINT_VECTOR PLINT_VECTOR types.
524         nc_r = r[i];
525         nc_g = g[i];
526         nc_b = b[i];
527         limit_rgba_range( "plscmap1: invalid cmap1 RGB color has been corrected", &nc_r, &nc_g, &nc_b, NULL );
528         plsc->cmap1[i].r = (unsigned char) nc_r;
529         plsc->cmap1[i].g = (unsigned char) nc_g;
530         plsc->cmap1[i].b = (unsigned char) nc_b;
531         plsc->cmap1[i].a = MAX_PLFLT_ALPHA;
532     }
533 
534     if ( plsc->level > 0 )
535         plP_state( PLSTATE_CMAP1 );
536 }
537 
538 //--------------------------------------------------------------------------
539 // plscmap1a()
540 //
541 //! Set color map 1 colors by 8 bit RGB and alpha values
542 //! This also sets the number of colors.
543 //!
544 //! @param r Array of red values.
545 //! @param g Array of green values.
546 //! @param b Array of blue values.
547 //! @param alpha Array of alpha values.
548 //! @param ncol1 Total number of RGBA values.
549 
550 void
c_plscmap1a(PLINT_VECTOR r,PLINT_VECTOR g,PLINT_VECTOR b,PLFLT_VECTOR alpha,PLINT ncol1)551 c_plscmap1a( PLINT_VECTOR r, PLINT_VECTOR g, PLINT_VECTOR b, PLFLT_VECTOR alpha, PLINT ncol1 )
552 {
553     int   i;
554     PLINT nc_r, nc_g, nc_b;
555     PLFLT nc_alpha;
556 
557     plscmap1n( ncol1 );
558 
559     for ( i = 0; i < plsc->ncol1; i++ )
560     {
561         // Need these assignments so that r[i], g[i], b[i], and
562         // alpha[i] remain unchanged as per their PLINT_VECTOR and
563         // PLFLT_VECTOR types.
564         nc_r     = r[i];
565         nc_g     = g[i];
566         nc_b     = b[i];
567         nc_alpha = alpha[i];
568         limit_rgba_range( "plscmap1a: invalid cmap1 RGBA color has been corrected", &nc_r, &nc_g, &nc_b, &nc_alpha );
569         plsc->cmap1[i].r = (unsigned char) nc_r;
570         plsc->cmap1[i].g = (unsigned char) nc_g;
571         plsc->cmap1[i].b = (unsigned char) nc_b;
572         plsc->cmap1[i].a = nc_alpha;
573     }
574 
575     if ( plsc->level > 0 )
576         plP_state( PLSTATE_CMAP1 );
577 }
578 
579 //--------------------------------------------------------------------------
580 // plscmap1l()
581 //
582 //! Set color map 1 colors using a piece-wise linear relationship between
583 //! position in the color map (from 0 to 1) and position in HLS or RGB color
584 //! space.  May be called at any time.
585 //!
586 //! The idea here is to specify a number of control points that specify the
587 //! mapping between HLS (or RGB or CMY) and palette 1 value.  Between these
588 //! points, linear interpolation is used.  By mapping position in the color
589 //! map to function value, this gives a smooth variation of color with
590 //! intensity.  Any number of control points may be specified, located at
591 //! arbitrary positions (intensities), although typically 2 - 4 are enough.
592 //! Another way of stating this is that we are traversing a given number of
593 //! lines through HLS (or RGB) space as we move through cmap 1 entries.  The
594 //! control points at the minimum and maximum intensity (0 and 1) must
595 //! always be specified.  By adding more control points you can get more
596 //! variation.  One good technique for plotting functions that vary about
597 //! some expected average is to use an additional 2 control points in the
598 //! center (intensity ~= 0.5) that are the same color as the background
599 //! (typically white for paper output, black for crt), and same hue as the
600 //! boundary control points.  This allows the highs and lows to be very
601 //! easily distinguished.
602 //!
603 //! Each control point must specify the position in cmap 1 as well as three
604 //! coordinates in HLS or RGB space.  The first and last points MUST correspond to
605 //! cmap1 coordinates MIN_PLFLT_CMAP1 and MAX_PLFLT_CMAP1.
606 //!
607 //! Every change in hue from one control point to the next can be linearly
608 //! interpolated in two ways.  The usual (alt_hue_path[i] false) method for the ith interval
609 //! uses the dh = h[i+1] - h[i] interval for interpolation.  The alternate (alt_hue_path true) method for the ith interval uses the dh = (h[i+1] - h[i]) - 360 if (h[i+1] - h[i]) is positive or dh = 360 - (h[i+1] - h[i]) if (h[i+1] - h[i]) is negative interval for the interpolation.  Thus, alt_hue_path true interpolation intervals always include hue = 0.
610 //! Specifying
611 //! alt_hue_path=NULL is equivalent to setting alt_hue_path[]=false for every control point.
612 //!
613 //! Bounds on RGB coordinates:
614 //!	R,G,B		[0, 1]		magnitude
615 //!
616 //! Bounds on HLS coordinates:
617 //!	hue		[0, 360]	degrees
618 //!	lightness	[0, 1]		magnitude
619 //!	saturation	[0, 1]		magnitude
620 //!
621 //! The inputs are:
622 //! @param itype 0: HLS, 1: RGB
623 //! @param npts	number of control points
624 //! @param intensity[] intensity index for each control point
625 //! @param coord1[] first coordinate for each control point
626 //! @param coord2[] second coordinate for each control point
627 //! @param coord3[] third coordinate for each control point
628 //! @param alt_hue_path[] if true, use alternative hue interpolation path
629 //! for the associated interval.
630 
631 void
c_plscmap1l(PLINT itype,PLINT npts,PLFLT_VECTOR intensity,PLFLT_VECTOR coord1,PLFLT_VECTOR coord2,PLFLT_VECTOR coord3,PLINT_VECTOR alt_hue_path)632 c_plscmap1l( PLINT itype, PLINT npts, PLFLT_VECTOR intensity,
633              PLFLT_VECTOR coord1, PLFLT_VECTOR coord2, PLFLT_VECTOR coord3, PLINT_VECTOR alt_hue_path )
634 {
635     int n;
636 
637     if ( npts < 2 )
638     {
639         plabort( "plscmap1l: Must specify at least two control points" );
640         return;
641     }
642 
643     if ( ( intensity[0] != MIN_PLFLT_CMAP1 ) || ( intensity[npts - 1] != MAX_PLFLT_CMAP1 ) )
644     {
645         plabort( "plscmap1l: First and last control points must correspond to minimum and maximum cmap1 color index" );
646         return;
647     }
648 
649     if ( npts > PL_MAX_CMAP1CP )
650     {
651         plabort( "plscmap1l: exceeded maximum number of control points" );
652         return;
653     }
654 
655     // Allocate if not done yet
656 
657     if ( plsc->cmap1 == NULL )
658         plscmap1n( 0 );
659 
660     // Save control points
661 
662     plsc->cmap1cp_is_rgb = itype == 0 ? 0 : 1;
663     plsc->ncp1           = npts;
664 
665     for ( n = 0; n < npts; n++ )
666     {
667         plsc->cmap1cp[n].c1 = coord1[n];
668         plsc->cmap1cp[n].c2 = coord2[n];
669         plsc->cmap1cp[n].c3 = coord3[n];
670         plsc->cmap1cp[n].p  = intensity[n];
671         plsc->cmap1cp[n].a  = MAX_PLFLT_ALPHA;
672 
673         if ( alt_hue_path == NULL )
674             plsc->cmap1cp[n].alt_hue_path = 0;
675         else if ( n != npts - 1 )
676             plsc->cmap1cp[n].alt_hue_path = alt_hue_path[n];
677         else
678             // Note final element is unused, so we set to zero for completeness.
679             plsc->cmap1cp[n].alt_hue_path = 0;
680     }
681 
682     // Calculate and set color map
683 
684     plcmap1_calc();
685 }
686 
687 //--------------------------------------------------------------------------
688 // plscmap1la()
689 //
690 //! This is the same as plscmap1l, but also allows alpha value interpolation.
691 //!
692 //! @param itype 0: HLS, 1: RGB
693 //! @param npts	number of control points
694 //! @param intensity[] intensity index for each control point
695 //! @param coord1[] first coordinate for each control point
696 //! @param coord2[] second coordinate for each control point
697 //! @param coord3[] third coordinate for each control point
698 //! @param alpha[] alpha value for each control point
699 //! @param alt_hue_path[] if true, use alternative hue interpolation path
700 //! for the associated interval.
701 
702 void
c_plscmap1la(PLINT itype,PLINT npts,PLFLT_VECTOR intensity,PLFLT_VECTOR coord1,PLFLT_VECTOR coord2,PLFLT_VECTOR coord3,PLFLT_VECTOR alpha,PLINT_VECTOR alt_hue_path)703 c_plscmap1la( PLINT itype, PLINT npts, PLFLT_VECTOR intensity,
704               PLFLT_VECTOR coord1, PLFLT_VECTOR coord2, PLFLT_VECTOR coord3, PLFLT_VECTOR alpha, PLINT_VECTOR alt_hue_path )
705 {
706     int n;
707 
708     if ( npts < 2 )
709     {
710         plabort( "plscmap1la: Must specify at least two control points" );
711         return;
712     }
713 
714     if ( ( intensity[0] != MIN_PLFLT_CMAP1 ) || ( intensity[npts - 1] != MAX_PLFLT_CMAP1 ) )
715     {
716         plabort( "plscmap1la: First, last control points must lie on boundary" );
717         return;
718     }
719 
720     if ( npts > PL_MAX_CMAP1CP )
721     {
722         plabort( "plscmap1la: exceeded maximum number of control points" );
723         return;
724     }
725 
726 // Allocate if not done yet
727 
728     if ( plsc->cmap1 == NULL )
729         plscmap1n( 0 );
730 
731 // Save control points
732 
733     plsc->cmap1cp_is_rgb = itype == 0 ? 0 : 1;
734     plsc->ncp1           = npts;
735 
736     for ( n = 0; n < npts; n++ )
737     {
738         plsc->cmap1cp[n].c1 = coord1[n];
739         plsc->cmap1cp[n].c2 = coord2[n];
740         plsc->cmap1cp[n].c3 = coord3[n];
741         plsc->cmap1cp[n].p  = intensity[n];
742         plsc->cmap1cp[n].a  = alpha[n];
743 
744         if ( alt_hue_path == NULL )
745             plsc->cmap1cp[n].alt_hue_path = 0;
746         else if ( n != npts - 1 )
747             plsc->cmap1cp[n].alt_hue_path = alt_hue_path[n];
748         else
749             // Note final element is unused, so we set to zero for completeness.
750             plsc->cmap1cp[n].alt_hue_path = 0;
751     }
752 
753 // Calculate and set color map
754 
755     plcmap1_calc();
756 }
757 
758 //--------------------------------------------------------------------------
759 // plcmap1_calc()
760 //
761 //! Bin up cmap 1 space and assign colors to make inverse mapping easy.
762 
763 void
plcmap1_calc(void)764 plcmap1_calc( void )
765 {
766     int   i, n;
767     PLFLT delta, dp, dh, dl, ds, da, dr, dg, db;
768     PLFLT h, l, s, p, r, g, b, a;
769 
770 // Loop over all control point pairs
771     if ( !plsc->cmap1cp_is_rgb )
772     {
773         for ( n = 0; n < plsc->ncp1 - 1; n++ )
774         {
775             if ( plsc->cmap1cp[n].p == plsc->cmap1cp[n + 1].p )
776                 continue;
777 
778             // Differences in p, h, l, s between ctrl pts
779 
780             dp = plsc->cmap1cp[n + 1].p - plsc->cmap1cp[n].p;
781             dh = plsc->cmap1cp[n + 1].c1 - plsc->cmap1cp[n].c1;
782             dl = plsc->cmap1cp[n + 1].c2 - plsc->cmap1cp[n].c2;
783             ds = plsc->cmap1cp[n + 1].c3 - plsc->cmap1cp[n].c3;
784             da = plsc->cmap1cp[n + 1].a - plsc->cmap1cp[n].a;
785 
786             // Adjust dh if we are to go around "the back side"
787 
788             if ( plsc->cmap1cp[n].alt_hue_path )
789                 dh = ( dh > 0 ) ? dh - 360 : dh + 360;
790 
791             // Loop over all color cells.  Only interested in cells located (in
792             // cmap1 space)  between n_th and n+1_th control points
793 
794             for ( i = 0; i < plsc->ncol1; i++ )
795             {
796                 p = (double) i / ( plsc->ncol1 - 1.0 );
797                 if ( ( p < plsc->cmap1cp[n].p ) ||
798                      ( p > plsc->cmap1cp[n + 1].p ) )
799                     continue;
800 
801                 // Interpolate based on position of color cell in cmap1 space
802 
803                 delta = ( p - plsc->cmap1cp[n].p ) / dp;
804 
805                 // Linearly interpolate to get color cell h, l, s values
806 
807                 h = plsc->cmap1cp[n].c1 + dh * delta;
808                 l = plsc->cmap1cp[n].c2 + dl * delta;
809                 s = plsc->cmap1cp[n].c3 + ds * delta;
810                 a = plsc->cmap1cp[n].a + da * delta;
811 
812                 while ( h >= 360. )
813                     h -= 360.;
814 
815                 while ( h < 0. )
816                     h += 360.;
817 
818                 c_plhlsrgb( h, l, s, &r, &g, &b );
819 
820                 plsc->cmap1[i].r = (unsigned char) MAX( MIN_PLINT_RGB, MIN( MAX_PLINT_RGB, (int) ( ( MAX_PLINT_RGB + 1 ) * r ) ) );
821                 plsc->cmap1[i].g = (unsigned char) MAX( MIN_PLINT_RGB, MIN( MAX_PLINT_RGB, (int) ( ( MAX_PLINT_RGB + 1 ) * g ) ) );
822                 plsc->cmap1[i].b = (unsigned char) MAX( MIN_PLINT_RGB, MIN( MAX_PLINT_RGB, (int) ( ( MAX_PLINT_RGB + 1 ) * b ) ) );
823                 plsc->cmap1[i].a = MAX( MIN_PLFLT_ALPHA, MIN( MAX_PLFLT_ALPHA, a ) );
824             }
825         }
826     }
827     else
828     {
829         for ( n = 0; n < plsc->ncp1 - 1; n++ )
830         {
831             if ( plsc->cmap1cp[n].p == plsc->cmap1cp[n + 1].p )
832                 continue;
833 
834             // Differences in p, h, l, s between ctrl pts
835 
836             dp = plsc->cmap1cp[n + 1].p - plsc->cmap1cp[n].p;
837             dr = plsc->cmap1cp[n + 1].c1 - plsc->cmap1cp[n].c1;
838             dg = plsc->cmap1cp[n + 1].c2 - plsc->cmap1cp[n].c2;
839             db = plsc->cmap1cp[n + 1].c3 - plsc->cmap1cp[n].c3;
840             da = plsc->cmap1cp[n + 1].a - plsc->cmap1cp[n].a;
841 
842             // Loop over all color cells.  Only interested in cells located (in
843             // cmap1 space)  between n_th and n+1_th control points
844 
845             for ( i = 0; i < plsc->ncol1; i++ )
846             {
847                 p = (double) i / ( plsc->ncol1 - 1.0 );
848                 if ( ( p < plsc->cmap1cp[n].p ) ||
849                      ( p > plsc->cmap1cp[n + 1].p ) )
850                     continue;
851 
852                 // Interpolate based on position of color cell in cmap1 space
853 
854                 delta = ( p - plsc->cmap1cp[n].p ) / dp;
855 
856                 // Linearly interpolate to get color cell h, l, s values
857 
858                 r = plsc->cmap1cp[n].c1 + dr * delta;
859                 g = plsc->cmap1cp[n].c2 + dg * delta;
860                 b = plsc->cmap1cp[n].c3 + db * delta;
861                 a = plsc->cmap1cp[n].a + da * delta;
862 
863                 plsc->cmap1[i].r = (unsigned char) MAX( MIN_PLINT_RGB, MIN( MAX_PLINT_RGB, (int) ( ( MAX_PLINT_RGB + 1 ) * r ) ) );
864                 plsc->cmap1[i].g = (unsigned char) MAX( MIN_PLINT_RGB, MIN( MAX_PLINT_RGB, (int) ( ( MAX_PLINT_RGB + 1 ) * g ) ) );
865                 plsc->cmap1[i].b = (unsigned char) MAX( MIN_PLINT_RGB, MIN( MAX_PLINT_RGB, (int) ( ( MAX_PLINT_RGB + 1 ) * b ) ) );
866                 plsc->cmap1[i].a = MAX( MIN_PLFLT_ALPHA, MIN( MAX_PLFLT_ALPHA, a ) );
867             }
868         }
869     }
870 
871     if ( plsc->level > 0 )
872         plP_state( PLSTATE_CMAP1 );
873 }
874 
875 //--------------------------------------------------------------------------
876 //! Set the color map 1 value range to use in continuous color plots.
877 //!
878 //! @param min_color Specifies the minimum cmap1 index to use.  A
879 //! value of MIN_PLFLT_CMAP1 or less indicates that the range should
880 //! start at MIN_PLFLT_CMAP1, the lowest valid cmap1 index available.
881 //! @param max_color Specifies the maximum cmap1 index to use.  A
882 //! value of MAX_PLFLT_CMAP1 or greater indicates that the range
883 //! should finish at MAX_PLFLT_CMAP1, the highest valid cmap1 index
884 //! available.
885 //!
886 //! If the specified range is completely invalid (i.e., min_color >= max_color or max_color
887 //! < MIN_PLFLT_CMAP1, or min_color > MAX_PLFLT_CMAP1), then min_color = MIN_PLFLT_CMAP1
888 //! and max_color = MAX_PLFLT_CMAP1 is used.
889 //--------------------------------------------------------------------------
890 
891 void
c_plscmap1_range(PLFLT min_color,PLFLT max_color)892 c_plscmap1_range( PLFLT min_color, PLFLT max_color )
893 {
894     if ( min_color >= max_color || max_color <= MIN_PLFLT_CMAP1 || min_color >= MAX_PLFLT_CMAP1 )
895     {
896         plwarn( "plscmap1_range called with completely invalid color range so min_color = MIN_PLFLT_CMAP1 and max_color = MAX_PLFLT_CMAP1 used instead." );
897         min_color = MIN_PLFLT_CMAP1;
898         max_color = MAX_PLFLT_CMAP1;
899     }
900     // At this stage, the following conditions have been met:
901     // min_color < max_color, max_color > MIN_PLFLT_CMAP1, and min_color < MAX_PLFLT_CMAP1.
902     if ( min_color < MIN_PLFLT_CMAP1 )
903     {
904         plwarn( "plscmap1_range called with min_color < MIN_PLFLT_CMAP1.  min_color = MIN_PLFLT_CMAP1 < max_color is used instead." );
905         min_color = MIN_PLFLT_CMAP1;
906     }
907     if ( max_color > MAX_PLFLT_CMAP1 )
908     {
909         plwarn( "plscmap1_range called with max_color > MAX_PLFLT_CMAP1. max_color = MAX_PLFLT_CMAP1 > min_color is used instead" );
910         max_color = MAX_PLFLT_CMAP1;
911     }
912     plsc->cmap1_min = min_color;
913     plsc->cmap1_max = max_color;
914 }
915 
916 //--------------------------------------------------------------------------
917 //! Get the color map 1 value range used in continuous color plots.
918 //!
919 //! @param min_color Specifies the minimum color used.
920 //! @param max_color Specifies the maximum color used.
921 //--------------------------------------------------------------------------
922 
923 void
c_plgcmap1_range(PLFLT * min_color,PLFLT * max_color)924 c_plgcmap1_range( PLFLT *min_color, PLFLT *max_color )
925 {
926     *min_color = plsc->cmap1_min;
927     *max_color = plsc->cmap1_max;
928 }
929 
930 //--------------------------------------------------------------------------
931 // plscmap0n()
932 //
933 //! Set number of colors in cmap 0, (re-)allocate cmap 0, and fill with
934 //! default values for those colors not previously allocated (and less
935 //! than index 15, after that you just get grey).
936 //!
937 //! The driver is not guaranteed to support all of these.
938 //!
939 //! @param ncol0 Total number of colors.
940 
941 void
c_plscmap0n(PLINT ncol0)942 c_plscmap0n( PLINT ncol0 )
943 {
944     int ncol, size, imin, imax;
945 
946 // No change
947 
948     if ( ncol0 > 0 && plsc->ncol0 == ncol0 )
949         return;
950 
951 // Handle all possible startup conditions
952 
953     if ( plsc->ncol0 <= 0 && ncol0 <= 0 )
954         ncol = PL_DEFAULT_NCOL0;
955     else if ( ncol0 <= 0 )
956         ncol = plsc->ncol0;
957     else
958         ncol = ncol0;
959 
960     imax = ncol - 1;
961     size = ncol * (int) sizeof ( PLColor );
962 
963 // Allocate the space
964 
965     if ( plsc->cmap0 == NULL )
966     {
967         if ( ( plsc->cmap0 = (PLColor *) calloc( 1, (size_t) size ) ) == NULL )
968         {
969             plexit( "c_plscmap0n: Insufficient memory" );
970         }
971         imin = 0;
972     }
973     else
974     {
975         if ( ( plsc->cmap0 = (PLColor *) realloc( plsc->cmap0, (size_t) size ) ) == NULL )
976         {
977             plexit( "c_plscmap0n: Insufficient memory" );
978         }
979         imin = plsc->ncol0;
980     }
981 
982 // Fill in default entries
983 
984     plsc->ncol0 = ncol;
985     plcmap0_def( imin, imax );
986 
987     if ( plsc->level > 0 )
988         plP_state( PLSTATE_CMAP0 );
989 }
990 
991 //--------------------------------------------------------------------------
992 // color_set()
993 //
994 //! Initializes color table 0 entry by RGB values.
995 //!
996 //! @param i Index of the color.
997 //! @param r Red value of the color.
998 //! @param g Green value of the color.
999 //! @param b Blue value of the color.
1000 //! @param a Alpha value of the color.
1001 //! @param name The name of the color.
1002 
1003 static void
color_set(PLINT i,U_CHAR r,U_CHAR g,U_CHAR b,PLFLT a,PLCHAR_VECTOR name)1004 color_set( PLINT i, U_CHAR r, U_CHAR g, U_CHAR b, PLFLT a, PLCHAR_VECTOR name )
1005 {
1006     plsc->cmap0[i].r    = r;
1007     plsc->cmap0[i].g    = g;
1008     plsc->cmap0[i].b    = b;
1009     plsc->cmap0[i].a    = a;
1010     plsc->cmap0[i].name = name;
1011 }
1012 
1013 #define color_def( i, r, g, b, a, n ) \
1014     if ( i >= imin && i <= imax ) color_set( i, r, g, b, a, n );
1015 
1016 //--------------------------------------------------------------------------
1017 // plcmap0_def()
1018 //
1019 //! Initializes specified color map 0 color entry to its default for
1020 //! index range from imin to imax.
1021 //!
1022 //! @param imin Index of the first color to set to its default.
1023 //! @param imax Index of the last color to set to its default.
1024 
1025 static void
plcmap0_def(int imin,int imax)1026 plcmap0_def( int imin, int imax )
1027 {
1028     int          i;
1029     unsigned int *r, *g, *b;
1030     double       *a;
1031     int          number_colors;
1032     if ( imin <= imax )
1033     {
1034         cmap0_palette_read( "", &number_colors, &r, &g, &b, &a );
1035         for ( i = imin; i <= MIN( ( number_colors - 1 ), imax ); i++ )
1036             color_def( i, (U_CHAR) r[i], (U_CHAR) g[i], (U_CHAR) b[i], a[i],
1037                 "colors defined by default cmap0 palette file" );
1038         free( r );
1039         free( g );
1040         free( b );
1041         free( a );
1042     }
1043     else
1044     {
1045         number_colors = 0;
1046     }
1047 
1048     // Initialize all colours undefined by the default colour palette file
1049     // to opaque red as a warning.
1050     for ( i = MAX( number_colors, imin ); i <= imax; i++ )
1051         color_def( i, MAX_PLINT_RGB, MIN_PLINT_RGB, MIN_PLINT_RGB, MAX_PLFLT_ALPHA,
1052             "opaque red colour to mark not defined by palette file" );
1053 }
1054 
1055 //--------------------------------------------------------------------------
1056 // plscmap1n()
1057 //
1058 //! Set number of colors in cmap 1, (re-)allocate cmap 1, and set default
1059 //! values if this is the first allocation.
1060 //!
1061 //! Note that the driver is allowed to disregard this number.
1062 //! In particular, most use fewer than we use internally.
1063 //!
1064 //! @param ncol1 The number of colors in cmap1.
1065 
1066 void
c_plscmap1n(PLINT ncol1)1067 c_plscmap1n( PLINT ncol1 )
1068 {
1069     PLINT  ncol;
1070     size_t size;
1071 
1072 // No change
1073 
1074     if ( ncol1 > 0 && plsc->ncol1 == ncol1 )
1075         return;
1076 
1077 // Handle all possible startup conditions
1078 
1079     if ( plsc->ncol1 <= 0 && ncol1 <= 0 )
1080         ncol = PL_DEFAULT_NCOL1;
1081     else if ( ncol1 <= 0 )
1082         ncol = plsc->ncol1;
1083     else
1084         ncol = ncol1;
1085 
1086     size = (size_t) ncol * sizeof ( PLColor );
1087 
1088 // Allocate the space
1089 
1090     if ( plsc->ncol1 > 0 )
1091     {
1092         if ( ( plsc->cmap1 = (PLColor *) realloc( plsc->cmap1, size ) ) == NULL )
1093         {
1094             plexit( "c_plscmap1n: Insufficient memory" );
1095         }
1096     }
1097     else
1098     {
1099         if ( ( plsc->cmap1 = (PLColor *) calloc( (size_t) ncol, sizeof ( PLColor ) ) ) == NULL )
1100         {
1101             plexit( "c_plscmap1n: Insufficient memory" );
1102         }
1103     }
1104 
1105 // Fill in default entries
1106 
1107     plsc->ncol1 = ncol;
1108     if ( plsc->ncp1 == 0 )
1109         plcmap1_def();
1110     else
1111         plcmap1_calc();
1112 }
1113 
1114 //--------------------------------------------------------------------------
1115 // plcmap1_def()
1116 //
1117 //! Initializes color map 1.
1118 //!
1119 //! The default initialization uses 6 control points in HLS space, the inner
1120 //! ones being very close to one of the vertices of the HLS double cone.  The
1121 //! vertex used (black or white) is chosen to be the closer to the background
1122 //! color.  The 6 points were chosen over the older 4 points in order to make
1123 //! weaker structures more easily visible, and give more control through the
1124 //! palette editor.  If you don't like these settings.. change them!
1125 //!
1126 
1127 static void
plcmap1_def(void)1128 plcmap1_def( void )
1129 {
1130     PLFLT i[6], h[6], l[6], s[6], midpt = 0., vertex = 0.;
1131 
1132 // Positions of control points
1133 
1134     i[0] = 0;           // left boundary
1135     i[1] = 0.44;        // a little left of center
1136     i[2] = 0.50;        // at center
1137     i[3] = 0.50;        // at center
1138     i[4] = 0.56;        // a little right of center
1139     i[5] = 1;           // right boundary
1140 
1141 // For center control points, pick black or white, whichever is closer to bg
1142 // Be careful to pick just short of top or bottom else hue info is lost
1143 
1144     if ( plsc->cmap0 != NULL )
1145         vertex = ( (PLFLT) plsc->cmap0[0].r +
1146                    (PLFLT) plsc->cmap0[0].g +
1147                    (PLFLT) plsc->cmap0[0].b ) / 3. / (PLFLT) MAX_PLINT_RGB;
1148 
1149     if ( vertex < 0.5 )
1150     {
1151         vertex = 0.01;
1152         midpt  = 0.10;
1153     }
1154     else
1155     {
1156         vertex = 0.99;
1157         midpt  = 0.90;
1158     }
1159 
1160 // Set hue
1161 
1162     h[0] = 260;         // low: blue-violet
1163     h[1] = 260;         // only change as we go over vertex
1164     h[2] = 260;         // only change as we go over vertex
1165     h[3] = 0;           // high: red
1166     h[4] = 0;           // high: red
1167     h[5] = 0;           // keep fixed
1168 
1169 // Set lightness
1170 
1171     l[0] = 0.5;         // low
1172     l[1] = midpt;       // midpoint value
1173     l[2] = vertex;      // bg
1174     l[3] = vertex;      // bg
1175     l[4] = midpt;       // midpoint value
1176     l[5] = 0.5;         // high
1177 
1178 // Set saturation -- keep at maximum
1179 
1180     s[0] = 1;
1181     s[1] = 1;
1182     s[2] = 1;
1183     s[3] = 1;
1184     s[4] = 1;
1185     s[5] = 1;
1186 
1187     c_plscmap1l( 0, 6, i, h, l, s, NULL );
1188 
1189     if ( plsc->level > 0 )
1190         plP_state( PLSTATE_CMAP1 );
1191 }
1192 
1193 //--------------------------------------------------------------------------
1194 // plscolor()
1195 //
1196 //! Used to globally turn color output on/off
1197 //!
1198 //! @param color 0 = no color, Not zero = color.
1199 //--------------------------------------------------------------------------
1200 
1201 void
c_plscolor(PLINT color)1202 c_plscolor( PLINT color )
1203 {
1204     plsc->colorset = 1;
1205     plsc->color    = color;
1206 }
1207 
1208 //--------------------------------------------------------------------------
1209 // void value()
1210 //
1211 //! Auxiliary function used by c_plhlsrgb().
1212 //!
1213 //! @param n1 Lightness/saturation value 1.
1214 //! @param n2 Lightness/saturation value 2.
1215 //! @param hue hue (0.0 - 360.0).
1216 //--------------------------------------------------------------------------
1217 
1218 static PLFLT
value(double n1,double n2,double hue)1219 value( double n1, double n2, double hue )
1220 {
1221     PLFLT val;
1222 
1223     while ( hue >= 360. )
1224         hue -= 360.;
1225     while ( hue < 0. )
1226         hue += 360.;
1227 
1228     if ( hue < 60. )
1229         val = n1 + ( n2 - n1 ) * hue / 60.;
1230     else if ( hue < 180. )
1231         val = n2;
1232     else if ( hue < 240. )
1233         val = n1 + ( n2 - n1 ) * ( 240. - hue ) / 60.;
1234     else
1235         val = n1;
1236 
1237     return ( val );
1238 }
1239 
1240 //--------------------------------------------------------------------------
1241 // void c_plhlsrgb()
1242 //
1243 //! Convert HLS color to RGB color.
1244 //! Bounds on HLS (input):
1245 //!	hue		[0., 360.]	degrees
1246 //!	lightness	[0., 1.]	magnitude
1247 //!	saturation	[0., 1.]	magnitude
1248 //!
1249 //! Hue is always mapped onto the interval [0., 360.] regardless of input.
1250 //! Bounds on RGB (output) is always [0., 1.].  Convert to RGB color values
1251 //! by multiplying by 2**nbits (nbits typically 8).
1252 //!
1253 //! @param h hue in HLS color scheme (0.0 - 360.0)
1254 //! @param l lightness in HLS color scheme (0.0 - 1.0)
1255 //! @param s saturation in HLS color scheme (0.0 - 1.0)
1256 //! @param p_r red value of the HLS color
1257 //! @param p_g green value of the HLS color
1258 //! @param p_b blue value of the HLS color
1259 
1260 void
c_plhlsrgb(PLFLT h,PLFLT l,PLFLT s,PLFLT * p_r,PLFLT * p_g,PLFLT * p_b)1261 c_plhlsrgb( PLFLT h, PLFLT l, PLFLT s, PLFLT *p_r, PLFLT *p_g, PLFLT *p_b )
1262 {
1263     PLFLT m1, m2;
1264 
1265     if ( l <= .5 )
1266         m2 = l * ( s + 1. );
1267     else
1268         m2 = l + s - l * s;
1269 
1270     m1 = 2 * l - m2;
1271 
1272     *p_r = value( m1, m2, h + 120. );
1273     *p_g = value( m1, m2, h );
1274     *p_b = value( m1, m2, h - 120. );
1275 }
1276 
1277 //--------------------------------------------------------------------------
1278 // void c_plrgbhls()
1279 //
1280 //! Convert RGB color to HLS color.
1281 //! Bounds on RGB (input) is always [0., 1.].
1282 //! Bounds on HLS (output):
1283 //!	hue		[0., 360.]	degrees
1284 //!	lightness	[0., 1.]	magnitude
1285 //!	saturation	[0., 1.]	magnitude
1286 //! @param r red in RGB color scheme (0.0 - 1.0)
1287 //! @param g green in RGB color scheme (0.0 - 1.0)
1288 //! @param b blue in RGB color scheme (0.0 - 1.0)
1289 //! @param p_h hue value of the RGB color.
1290 //! @param p_l lightness value of the RGB color.
1291 //! @param p_s saturation value of the RGB color.
1292 
1293 void
c_plrgbhls(PLFLT r,PLFLT g,PLFLT b,PLFLT * p_h,PLFLT * p_l,PLFLT * p_s)1294 c_plrgbhls( PLFLT r, PLFLT g, PLFLT b, PLFLT *p_h, PLFLT *p_l, PLFLT *p_s )
1295 {
1296     PLFLT h, l, s, d, rc, gc, bc, rgb_min, rgb_max;
1297 
1298     rgb_min = MIN( r, MIN( g, b ) );
1299     rgb_max = MAX( r, MAX( g, b ) );
1300 
1301     l = ( rgb_min + rgb_max ) / 2.0;
1302 
1303     if ( rgb_min == rgb_max )
1304     {
1305         s = 0;
1306         h = 0;
1307     }
1308     else
1309     {
1310         d = rgb_max - rgb_min;
1311         if ( l < 0.5 )
1312             s = 0.5 * d / l;
1313         else
1314             s = 0.5 * d / ( 1. - l );
1315 
1316         rc = ( rgb_max - r ) / d;
1317         gc = ( rgb_max - g ) / d;
1318         bc = ( rgb_max - b ) / d;
1319 
1320         if ( r == rgb_max )
1321             h = bc - gc;
1322         else if ( g == rgb_max )
1323             h = rc - bc + 2;
1324         else
1325             h = gc - rc - 2;
1326 
1327         h = h * 60;
1328         if ( h < 0 )
1329             h = h + 360;
1330         else if ( h >= 360 )
1331             h = h - 360;
1332     }
1333     *p_h = h;
1334     *p_l = l;
1335     *p_s = s;
1336 }
1337 
1338 //--------------------------------------------------------------------------
1339 // read_line()
1340 //
1341 //! Read a complete line and fill the buffer with its contents up to
1342 //! capacity. Then sanitize the string - no control characters, no
1343 //! trailing blanks
1344 //!
1345 //! @param buffer storage for the line of text.
1346 //! @param length size of the buffer.
1347 //! @param fp open file pointer from which the line of text should be read.
1348 //!
1349 //! @returns The sanitized line from the file.
1350 
1351 static char *
read_line(char * buffer,int length,FILE * fp)1352 read_line( char *buffer, int length, FILE *fp )
1353 {
1354     char *pchr;
1355 
1356     // Read the string
1357     if ( fgets( buffer, length, fp ) == NULL )
1358     {
1359         return NULL;
1360     }
1361 
1362     // Sanitize the string we read - it may contain EOL characters
1363     // Make sure file reading starts at the next line
1364     pchr = strchr( buffer, '\n' );
1365     if ( pchr != NULL )
1366     {
1367         *pchr = '\0';
1368     }
1369     else
1370     {
1371         if ( fscanf( fp, "%*[^\n]\n" ) == EOF && ferror( fp ) )
1372         {
1373             return NULL;
1374         }
1375     }
1376 
1377 
1378     pchr = strchr( buffer, '\r' );
1379     if ( pchr != NULL )
1380     {
1381         *pchr = '\0';
1382     }
1383 
1384     // Remove trailing blanks
1385     pchr = buffer + strlen( buffer ) - 1;
1386     while ( pchr != buffer && *pchr == ' ' )
1387     {
1388         *pchr = '\0';
1389         pchr--;
1390     }
1391 
1392     return buffer;
1393 }
1394 
1395 //--------------------------------------------------------------------------
1396 // cmap0_palette_read()
1397 //
1398 //! Read and check r, g, b, a data from a cmap0*.pal format file.
1399 //! The caller must free the returned malloc'ed space for r, g, b, and a.
1400 //!
1401 //! @param filename name of the cmap0 palette file.
1402 //! @param number_colors number of color found in the palette file.
1403 //! @param r red value of each color in the palette file.
1404 //! @param g green value of each color in the palette file.
1405 //! @param b blue value of each color in the palette file.
1406 //! @param a alpha value of each color in the palette file.
1407 
1408 static void
cmap0_palette_read(PLCHAR_VECTOR filename,int * number_colors,unsigned int ** r,unsigned int ** g,unsigned int ** b,double ** a)1409 cmap0_palette_read( PLCHAR_VECTOR filename,
1410                     int *number_colors, unsigned int **r, unsigned int **g, unsigned int **b, double **a )
1411 {
1412     int  i, err = 0;
1413     char color_info[COLLEN];
1414     char msgbuf[MSGLEN];
1415     FILE *fp;
1416     char * save_locale = plsave_set_locale();
1417 
1418     if ( strlen( filename ) == 0 )
1419     {
1420         fp = plLibOpen( PL_DEFAULT_CMAP0_FILE );
1421         if ( fp == NULL )
1422         {
1423             snprintf( msgbuf, MSGLEN, "Unable to open cmap0 file %s\n", PL_DEFAULT_CMAP0_FILE );
1424             plwarn( msgbuf );
1425             err = 1;
1426         }
1427     }
1428     else
1429     {
1430         fp = plLibOpen( filename );
1431         if ( fp == NULL )
1432         {
1433             snprintf( msgbuf, MSGLEN, "Unable to open cmap0 file %s\n", filename );
1434             plwarn( msgbuf );
1435             err = 1;
1436         }
1437     }
1438     if ( !err && ( fscanf( fp, "%d\n", number_colors ) != 1 || *number_colors < 1 ) )
1439     {
1440         fclose( fp );
1441         snprintf( msgbuf, MSGLEN, "Unrecognized cmap0 header\n" );
1442         plwarn( msgbuf );
1443         err = 1;
1444     }
1445 
1446     if ( !err )
1447     {
1448         // Allocate arrays to hold r, g, b, and a data for calling routine.
1449         // The caller must free these after it is finished with them.
1450         if ( ( ( *r = (unsigned int *) malloc( (size_t) ( *number_colors ) * sizeof ( unsigned int ) ) ) == NULL ) ||
1451              ( ( *g = (unsigned int *) malloc( (size_t) ( *number_colors ) * sizeof ( unsigned int ) ) ) == NULL ) ||
1452              ( ( *b = (unsigned int *) malloc( (size_t) ( *number_colors ) * sizeof ( unsigned int ) ) ) == NULL ) ||
1453              ( ( *a = (double *) malloc( (size_t) ( *number_colors ) * sizeof ( double ) ) ) == NULL ) )
1454         {
1455             fclose( fp );
1456             plexit( "cmap0_palette_read: insufficient memory" );
1457         }
1458 
1459         for ( i = 0; i < *number_colors; i++ )
1460         {
1461             if ( read_line( color_info, COLLEN, fp ) == NULL )
1462             {
1463                 err = 1;
1464                 break;
1465             }
1466 
1467             // Get the color data
1468             if ( strlen( color_info ) == 7 )
1469             {
1470                 if ( sscanf( color_info, "#%2x%2x%2x",
1471                          (unsigned int *) ( *r + i ), (unsigned int *) ( *g + i ),
1472                          (unsigned int *) ( *b + i ) ) != 3 )
1473                 {
1474                     err = 1;
1475                     break;
1476                 }
1477                 *( *a + i ) = 1.0;
1478             }
1479             else if ( strlen( color_info ) > 9 )
1480             {
1481                 if ( sscanf( color_info, "#%2x%2x%2x %lf",
1482                          (unsigned int *) ( *r + i ), (unsigned int *) ( *g + i ),
1483                          (unsigned int *) ( *b + i ), (double *) ( *a + i ) ) != 4 )
1484                 {
1485                     err = 1;
1486                     break;
1487                 }
1488                 // fuzzy range check.
1489                 if ( *( *a + i ) < -FUZZ_EPSILON || *( *a + i ) > ( 1. + FUZZ_EPSILON ) )
1490                 {
1491                     err = 1;
1492                     break;
1493                 }
1494                 else if ( *( *a + i ) < 0. )
1495                 {
1496                     *( *a + i ) = 0.;
1497                 }
1498                 else if ( *( *a + i ) > 1. )
1499                 {
1500                     *( *a + i ) = 1.;
1501                 }
1502             }
1503             else
1504             {
1505                 err = 1;
1506                 break;
1507             }
1508         }
1509         fclose( fp );
1510         if ( err )
1511         {
1512             snprintf( msgbuf, MSGLEN, "Unrecognized cmap0 format data line.  Line is %s\n",
1513                 color_info );
1514             plwarn( msgbuf );
1515             free( *r );
1516             free( *g );
1517             free( *b );
1518             free( *a );
1519         }
1520     }
1521     // Fall back to opaque red on opaque white as visual warning of any
1522     // error above.
1523     if ( err )
1524     {
1525         *number_colors = 16;
1526         if ( ( ( *r = (unsigned int *) malloc( (size_t) ( *number_colors ) * sizeof ( int ) ) ) == NULL ) ||
1527              ( ( *g = (unsigned int *) malloc( (size_t) ( *number_colors ) * sizeof ( unsigned int ) ) ) == NULL ) ||
1528              ( ( *b = (unsigned int *) malloc( (size_t) ( *number_colors ) * sizeof ( unsigned int ) ) ) == NULL ) ||
1529              ( ( *a = (double *) malloc( (size_t) ( *number_colors ) * sizeof ( double ) ) ) == NULL ) )
1530         {
1531             plexit( "cmap0_palette_read: insufficient memory" );
1532         }
1533         **r = MAX_PLINT_RGB;
1534         **g = MAX_PLINT_RGB;
1535         **b = MAX_PLINT_RGB;
1536         **a = MAX_PLFLT_ALPHA;
1537         for ( i = 1; i < *number_colors; i++ )
1538         {
1539             *( *r + i ) = MAX_PLINT_RGB;
1540             *( *g + i ) = MIN_PLINT_RGB;
1541             *( *b + i ) = MIN_PLINT_RGB;
1542             *( *a + i ) = MAX_PLFLT_ALPHA;
1543         }
1544     }
1545 
1546     plrestore_locale( save_locale );
1547 }
1548 
1549 //--------------------------------------------------------------------------
1550 // void c_plspal0(filename)
1551 //
1552 //! Set the palette for color map 0 using a cmap0*.pal format file.
1553 //! filename: the name of the cmap0*.pal file to use.
1554 //!
1555 //! @param filename name of the cmap0 palette file.
1556 
1557 void
c_plspal0(PLCHAR_VECTOR filename)1558 c_plspal0( PLCHAR_VECTOR filename )
1559 {
1560     int          i;
1561     unsigned int *r, *g, *b;
1562     double       *a;
1563     int          number_colors;
1564     cmap0_palette_read( filename, &number_colors, &r, &g, &b, &a );
1565     // Allocate default number of cmap0 colours if cmap0 allocation not
1566     // done already.
1567     plscmap0n( 0 );
1568     // Allocate sufficient cmap0 colours to contain present data.
1569     if ( number_colors > plsc->ncol0 )
1570     {
1571         plscmap0n( number_colors );
1572     }
1573     for ( i = 0; i < number_colors; i++ )
1574     {
1575         c_plscol0a( i, (PLINT) r[i], (PLINT) g[i], (PLINT) b[i], a[i] );
1576     }
1577     free( r );
1578     free( g );
1579     free( b );
1580     free( a );
1581 }
1582 
1583 //! This code fragment used a lot in plspal1 to deal with
1584 //! floating-point range checking of a value and the adjustment of that
1585 //! value when close to the range when there is floating-point errors.
1586 //!
1587 //! @param value The value to range check.
1588 //! @param min The minimum allowable value.
1589 //! @param max The maximum allowable value.
1590 //! @param fuzz Amount of slop to allow beyond the range defined by min & max.
1591 //! @param err_number The error number.
1592 #define fuzzy_range_check( value, min, max, fuzz, err_number )                                                                        \
1593     if ( value < ( min - fuzz ) || value > ( max + fuzz ) ) {                                                                         \
1594         snprintf( msgbuf, MSGLEN, "Unrecognized cmap1 format data line.  Error number is %d. Line is %s\n", err_number, color_info ); \
1595         plwarn( msgbuf );                                                                                                             \
1596         err = 1;                                                                                                                      \
1597         break;                                                                                                                        \
1598     } else if ( value < min ) {                                                                                                       \
1599         value = min;                                                                                                                  \
1600     } else if ( value > max ) {                                                                                                       \
1601         value = max;                                                                                                                  \
1602     }
1603 
1604 //--------------------------------------------------------------------------
1605 // void c_plspal1(filename)
1606 //
1607 //! Set the palette for color map 1 using a cmap1*.pal format file.
1608 //! filename: the name of the cmap1*.pal file to use.
1609 //!
1610 //! @param filename name of the cmap1 palette file.
1611 //! @param interpolate interpolate between control points.
1612 
1613 void
c_plspal1(PLCHAR_VECTOR filename,PLBOOL interpolate)1614 c_plspal1( PLCHAR_VECTOR filename, PLBOOL interpolate )
1615 {
1616     int          i;
1617     int          number_colors;
1618     int          format_version, err;
1619     PLBOOL       rgb;
1620     char         color_info[PALLEN];
1621     unsigned int r_i, g_i, b_i;
1622     int          pos_i, alt_hue_path_i;
1623     double       r_d, g_d, b_d, a_d, pos_d;
1624     PLFLT        *r, *g, *b, *a, *pos;
1625     PLINT        *ri, *gi, *bi;
1626     PLBOOL       *alt_hue_path;
1627     FILE         *fp;
1628     char         msgbuf[MSGLEN];
1629     char         * save_locale = plsave_set_locale();
1630 
1631     rgb            = TRUE;
1632     err            = 0;
1633     format_version = 0;
1634     if ( strlen( filename ) == 0 )
1635     {
1636         fp = plLibOpen( PL_DEFAULT_CMAP1_FILE );
1637         if ( fp == NULL )
1638         {
1639             snprintf( msgbuf, MSGLEN, "Unable to open cmap1 .pal file %s\n", PL_DEFAULT_CMAP1_FILE );
1640             plwarn( msgbuf );
1641             goto finish;
1642         }
1643     }
1644     else
1645     {
1646         fp = plLibOpen( filename );
1647         if ( fp == NULL )
1648         {
1649             snprintf( msgbuf, MSGLEN, "Unable to open cmap1 .pal file %s\n", filename );
1650             plwarn( msgbuf );
1651             goto finish;
1652         }
1653     }
1654     // Check for new file format
1655     if ( read_line( color_info, PALLEN, fp ) == NULL )
1656     {
1657         snprintf( msgbuf, MSGLEN, "Error reading cmap1 .pal file %s\n", filename );
1658         plwarn( msgbuf );
1659         fclose( fp );
1660         goto finish;
1661     }
1662     if ( strncmp( color_info, "v2 ", 2 ) == 0 )
1663     {
1664         format_version = 1;
1665         if ( strncmp( &color_info[3], "hls", 3 ) == 0 )
1666             rgb = FALSE;
1667         else if ( strncmp( &color_info[3], "rgb", 3 ) == 0 )
1668             rgb = TRUE;
1669         else
1670         {
1671             snprintf( msgbuf, MSGLEN, "Invalid color space %s - assuming RGB\n", &color_info[3] );
1672             plwarn( msgbuf );
1673             rgb = TRUE;
1674         }
1675         if ( read_line( color_info, PALLEN, fp ) == NULL )
1676         {
1677             snprintf( msgbuf, MSGLEN, "Error reading cmap1 .pal file %s\n", filename );
1678             plwarn( msgbuf );
1679             fclose( fp );
1680             goto finish;
1681         }
1682     }
1683 
1684     if ( sscanf( color_info, "%d\n", &number_colors ) != 1 || number_colors < 2 )
1685     {
1686         snprintf( msgbuf, MSGLEN, "Unrecognized cmap1 format (wrong number of colors) %s\n", color_info );
1687         plwarn( msgbuf );
1688         fclose( fp );
1689         goto finish;
1690     }
1691 
1692     r            = (PLFLT *) malloc( (size_t) number_colors * sizeof ( PLFLT ) );
1693     g            = (PLFLT *) malloc( (size_t) number_colors * sizeof ( PLFLT ) );
1694     b            = (PLFLT *) malloc( (size_t) number_colors * sizeof ( PLFLT ) );
1695     ri           = (PLINT *) malloc( (size_t) number_colors * sizeof ( PLINT ) );
1696     gi           = (PLINT *) malloc( (size_t) number_colors * sizeof ( PLINT ) );
1697     bi           = (PLINT *) malloc( (size_t) number_colors * sizeof ( PLINT ) );
1698     a            = (PLFLT *) malloc( (size_t) number_colors * sizeof ( PLFLT ) );
1699     pos          = (PLFLT *) malloc( (size_t) number_colors * sizeof ( PLFLT ) );
1700     alt_hue_path = (PLBOOL *) malloc( (size_t) ( number_colors - 1 ) * sizeof ( PLBOOL ) );
1701 
1702     if ( format_version == 0 )
1703     {
1704         int return_sscanf = -1, return_sscanf_old = 0;
1705         // Old tk file format
1706         for ( i = 0; i < number_colors; i++ )
1707         {
1708             if ( read_line( color_info, PALLEN, fp ) == NULL )
1709             {
1710                 snprintf( msgbuf, MSGLEN, "Error reading cmap1 .pal file %s\n", filename );
1711                 plwarn( msgbuf );
1712                 fclose( fp );
1713                 goto finish;
1714             }
1715             // Ensure string is null terminated if > 160 characters
1716             color_info[PALLEN - 1] = '\0';
1717             return_sscanf          = sscanf( color_info, "#%2x%2x%2x %d %d", &r_i, &g_i, &b_i, &pos_i, &alt_hue_path_i );
1718             if ( return_sscanf < 4 || ( return_sscanf_old != 0 && return_sscanf != return_sscanf_old ) )
1719             {
1720                 snprintf( msgbuf, MSGLEN, "Unrecognized cmap1 format (wrong number of items for version 1 of format) %s\n", color_info );
1721                 plwarn( msgbuf );
1722                 err = 1;
1723                 break;
1724             }
1725             return_sscanf_old = return_sscanf;
1726             // For old format, input colours range from 0 to 255 and
1727             // need to be renormalized to the range from 0. to 1..
1728             r[i]   = (PLFLT) r_i / 255.;
1729             g[i]   = (PLFLT) g_i / 255.;
1730             b[i]   = (PLFLT) b_i / 255.;
1731             a[i]   = 1.0;
1732             pos[i] = 0.01 * (PLFLT) pos_i;
1733             fuzzy_range_check( r[i], 0., 1., FUZZ_EPSILON, 1 );
1734             fuzzy_range_check( g[i], 0., 1., FUZZ_EPSILON, 2 );
1735             fuzzy_range_check( b[i], 0., 1., FUZZ_EPSILON, 3 );
1736             fuzzy_range_check( pos[i], 0., 1., FUZZ_EPSILON, 4 );
1737             if ( ( return_sscanf == 5 ) && ( i != number_colors - 1 ) )
1738             {
1739                 // Next to oldest tk format with alt_hue_path specified.
1740                 alt_hue_path[i] = (PLBOOL) alt_hue_path_i;
1741             }
1742         }
1743         if ( return_sscanf == 4 )
1744         {
1745             // Oldest tk format.  No alt_hue_path specified.
1746             free( alt_hue_path );
1747             alt_hue_path = NULL;
1748         }
1749     }
1750     else
1751     {
1752         // New floating point file version with support for alpha and alt_hue_path values
1753         for ( i = 0; i < number_colors; i++ )
1754         {
1755             if ( read_line( color_info, PALLEN, fp ) == NULL )
1756             {
1757                 snprintf( msgbuf, MSGLEN, "Error reading cmap1 .pal file %s\n", filename );
1758                 plwarn( msgbuf );
1759                 fclose( fp );
1760                 goto finish;
1761             }
1762             if ( sscanf( color_info, "%lf %lf %lf %lf %lf %d", &pos_d, &r_d, &g_d, &b_d, &a_d, &alt_hue_path_i ) != 6 )
1763             {
1764                 snprintf( msgbuf, MSGLEN, "Unrecognized cmap1 format (wrong number of items for version 2 of format) %s\n", color_info );
1765                 plwarn( msgbuf );
1766                 err = 1;
1767                 break;
1768             }
1769 
1770             r[i]   = (PLFLT) r_d;
1771             g[i]   = (PLFLT) g_d;
1772             b[i]   = (PLFLT) b_d;
1773             a[i]   = (PLFLT) a_d;
1774             pos[i] = (PLFLT) pos_d;
1775             // Check that all rgba and pos data within range from 0. to
1776             // 1. except for the hls colour space case where the first
1777             // coordinate is checked within range from 0. to 360.
1778             if ( rgb )
1779             {
1780                 fuzzy_range_check( r[i], 0., 1., FUZZ_EPSILON, 5 );
1781             }
1782             else
1783             {
1784                 fuzzy_range_check( r[i], 0., 360., ( 360. * FUZZ_EPSILON ), 6 );
1785             }
1786             fuzzy_range_check( g[i], 0., 1., FUZZ_EPSILON, 7 );
1787             fuzzy_range_check( b[i], 0., 1., FUZZ_EPSILON, 8 );
1788             fuzzy_range_check( a[i], 0., 1., FUZZ_EPSILON, 9 );
1789             fuzzy_range_check( pos[i], 0., 1., FUZZ_EPSILON, 10 );
1790 
1791             if ( i != number_colors - 1 )
1792                 alt_hue_path[i] = (PLBOOL) alt_hue_path_i;
1793         }
1794     }
1795     fclose( fp );
1796 
1797     if ( !err )
1798     {
1799         if ( interpolate )
1800         {
1801             c_plscmap1la( rgb, number_colors, pos, r, g, b, a, alt_hue_path );
1802         }
1803         else
1804         {
1805             for ( i = 0; i < number_colors; i++ )
1806             {
1807                 ri[i] = (PLINT) ( r[i] * MAX_PLINT_RGB );
1808                 gi[i] = (PLINT) ( g[i] * MAX_PLINT_RGB );
1809                 bi[i] = (PLINT) ( b[i] * MAX_PLINT_RGB );
1810             }
1811             c_plscmap1a( ri, gi, bi, a, number_colors );
1812         }
1813     }
1814     else
1815     {
1816         // Fall back to red scale as visual warning if some problem occurred
1817         // above.
1818         free( r );
1819         free( g );
1820         free( b );
1821         free( pos );
1822         number_colors = 2;
1823         r             = (PLFLT *) malloc( (size_t) number_colors * sizeof ( PLFLT ) );
1824         g             = (PLFLT *) malloc( (size_t) number_colors * sizeof ( PLFLT ) );
1825         b             = (PLFLT *) malloc( (size_t) number_colors * sizeof ( PLFLT ) );
1826         pos           = (PLFLT *) malloc( (size_t) number_colors * sizeof ( PLFLT ) );
1827         r[0]          = 0.;
1828         r[1]          = 1.;
1829         g[0]          = 0.;
1830         g[1]          = 0.;
1831         b[0]          = 0.;
1832         b[1]          = 0.;
1833         pos[0]        = 0.;
1834         pos[1]        = 1.;
1835         c_plscmap1l( TRUE, number_colors, pos, r, g, b, NULL );
1836     }
1837 
1838     free( r );
1839     free( g );
1840     free( b );
1841     free( ri );
1842     free( gi );
1843     free( bi );
1844     free( a );
1845     free( pos );
1846     free( alt_hue_path );
1847 
1848 finish: plrestore_locale( save_locale );
1849 }
1850 
1851 //--------------------------------------------------------------------------
1852 // A grab-bag of various control routines.
1853 //--------------------------------------------------------------------------
1854 
1855 //--------------------------------------------------------------------------
1856 // void plwarn()
1857 //
1858 //! A handy way to issue warnings, if need be.
1859 //!
1860 //! @param errormsg The error message.
1861 
1862 void
plwarn(PLCHAR_VECTOR errormsg)1863 plwarn( PLCHAR_VECTOR errormsg )
1864 {
1865     int was_gfx = 0;
1866 
1867     if ( plsc->graphx == 1 )
1868     {
1869         was_gfx = 1;
1870         pltext();
1871     }
1872 
1873     fprintf( stderr, "\n*** PLPLOT WARNING ***\n" );
1874     if ( *errormsg != '\0' )
1875         fprintf( stderr, "%s\n", errormsg );
1876 
1877     if ( was_gfx == 1 )
1878         plgra();
1879 }
1880 
1881 //--------------------------------------------------------------------------
1882 // void plabort()
1883 //
1884 //! Much the same as plwarn(), but appends ", aborting operation" to the
1885 //! error message.  Helps to keep source code uncluttered and provides a
1886 //! convention for error aborts.
1887 //!
1888 //! If cleanup needs to be done in the main program, the user should write
1889 //! his/her own exit handler and pass it in via plsabort().
1890 //!
1891 //! @param errormsg The error message.
1892 
1893 void
plabort(PLCHAR_VECTOR errormsg)1894 plabort( PLCHAR_VECTOR errormsg )
1895 {
1896     if ( abort_handler != NULL )
1897         ( *abort_handler )( errormsg );
1898 
1899     if ( plsc->errcode != NULL )
1900         *( plsc->errcode ) = 1;
1901 
1902     if ( plsc->errmsg != NULL )
1903     {
1904         sprintf( plsc->errmsg, "\n*** PLPLOT ERROR, ABORTING OPERATION ***\n" );
1905         if ( *errormsg != '\0' )
1906             sprintf( plsc->errmsg, "%s, aborting operation\n", errormsg );
1907     }
1908     else
1909     {
1910         int was_gfx = 0;
1911 
1912         if ( plsc->graphx == 1 )
1913         {
1914             was_gfx = 1;
1915             pltext();
1916         }
1917 
1918         fprintf( stderr, "\n*** PLPLOT ERROR, ABORTING OPERATION ***\n" );
1919         if ( *errormsg != '\0' )
1920             fprintf( stderr, "%s, aborting operation\n", errormsg );
1921 
1922         if ( was_gfx == 1 )
1923             plgra();
1924     }
1925 }
1926 
1927 
1928 //--------------------------------------------------------------------------
1929 // void plsabort()
1930 //
1931 //! Sets an optional user abort handler.
1932 //!
1933 //! @param handler A function that takes a PLCHAR_VECTOR argument that will
1934 //! be called in the event of a abort.
1935 //--------------------------------------------------------------------------
1936 
1937 void
plsabort(void (* handler)(PLCHAR_VECTOR))1938 plsabort( void ( *handler )( PLCHAR_VECTOR ) )
1939 {
1940     abort_handler = handler;
1941 }
1942 
1943 //--------------------------------------------------------------------------
1944 // void plexit()
1945 //
1946 //! In case of an abort this routine is called.  It just prints out an error
1947 //! message and tries to clean up as much as possible.  It's best to turn
1948 //! off pause and then restore previous setting before returning.
1949 //!
1950 //! If cleanup needs to be done in the main program, the user should write
1951 //! his/her own exit handler and pass it in via plsexit().  This function
1952 //! should should either call plend() before exiting, or simply return.
1953 //!
1954 //! @param errormsg The error message.
1955 //--------------------------------------------------------------------------
1956 
1957 void
plexit(PLCHAR_VECTOR errormsg)1958 plexit( PLCHAR_VECTOR errormsg )
1959 {
1960     int status = 1;
1961 
1962     if ( exit_handler != NULL )
1963         status = ( *exit_handler )( errormsg );
1964 
1965     plsc->nopause = 1;
1966     if ( *errormsg != '\0' )
1967     {
1968         fprintf( stderr, "\n*** PLPLOT ERROR, IMMEDIATE EXIT ***\n" );
1969         fprintf( stderr, "%s\n", errormsg );
1970     }
1971     plend();
1972 
1973     fprintf( stderr, "Program aborted\n" );
1974     exit( status );
1975 }
1976 
1977 //--------------------------------------------------------------------------
1978 // void plsexit()
1979 //
1980 //! Sets an optional user exit handler.
1981 //!
1982 //! @param handler A function that takes a PLCHAR_VECTOR argument that will
1983 //! will be called in the event of a exit.
1984 //--------------------------------------------------------------------------
1985 
1986 void
plsexit(int (* handler)(PLCHAR_VECTOR))1987 plsexit( int ( *handler )( PLCHAR_VECTOR ) )
1988 {
1989     exit_handler = handler;
1990 }
1991 
1992 //--------------------------------------------------------------------------
1993 // void plgra()
1994 //
1995 //! Switches to graphics screen.
1996 //!
1997 //! Here and in pltext() it's a good idea to return silently if plinit()
1998 //! hasn't yet been called, since plwarn() calls pltext() and plgra(), and
1999 //! plwarn() may be called at any time.
2000 //--------------------------------------------------------------------------
2001 
2002 void
c_plgra(void)2003 c_plgra( void )
2004 {
2005     if ( plsc->level > 0 )
2006         plP_esc( PLESC_GRAPH, NULL );
2007 }
2008 
2009 //--------------------------------------------------------------------------
2010 // void plxormod()
2011 //
2012 //! Set xor mode? FIXME: Not really sure what this function does.
2013 //!
2014 //! @param mode Boolean.
2015 //! @param status 1 if successful, 0 otherwise.
2016 
2017 void
c_plxormod(PLINT mode,PLINT * status)2018 c_plxormod( PLINT mode, PLINT *status )   // xor mode
2019 {
2020     static int ostate = 0;
2021 
2022     if ( !plsc->dev_xor )
2023     {
2024         *status = 0;
2025         return;
2026     }
2027 
2028     if ( plsc->level > 0 )
2029     {
2030         plP_esc( PLESC_XORMOD, &mode );
2031         if ( mode )
2032         {
2033             ostate            = plsc->plbuf_write;
2034             plsc->plbuf_write = 0;
2035         }
2036         else
2037             plsc->plbuf_write = ostate;
2038     }
2039     *status = 1;
2040 }
2041 
2042 //--------------------------------------------------------------------------
2043 //! Set drawing mode (depends on device support!)
2044 //!
2045 //! @param mode This determines which drawing mode to use.
2046 //!
2047 void
c_plsdrawmode(PLINT mode)2048 c_plsdrawmode( PLINT mode )
2049 {
2050     if ( !plsc->dev_modeset )
2051     {
2052         plwarn( "plsdrawmode: Mode setting is not supported by this device" );
2053     }
2054     else if ( plsc->level > 0 )
2055     {
2056         plP_esc( PLESC_MODESET, &mode );
2057     }
2058     else
2059     {
2060         plwarn( "plsdrawmode: Initialize PLplot first" );
2061     }
2062     return;
2063 }
2064 
2065 //--------------------------------------------------------------------------
2066 //! Get drawing mode (depends on device support!)
2067 //!
2068 //! @returns Current drawing mode
2069 //!
2070 PLINT
c_plgdrawmode(void)2071 c_plgdrawmode( void )
2072 {
2073     PLINT mode;
2074 
2075     if ( !plsc->dev_modeset )
2076     {
2077         plwarn( "plgdrawmode: Mode getting is not supported by this device" );
2078         mode = PL_DRAWMODE_UNKNOWN;
2079     }
2080     else if ( plsc->level > 0 )
2081     {
2082         plP_esc( PLESC_MODEGET, &mode );
2083     }
2084     else
2085     {
2086         plwarn( "plsdrawmode: Initialize PLplot first" );
2087         mode = PL_DRAWMODE_UNKNOWN;
2088     }
2089 
2090     return ( mode );
2091 }
2092 
2093 //--------------------------------------------------------------------------
2094 // void pltext()
2095 //
2096 //! Switches to text screen.
2097 //--------------------------------------------------------------------------
2098 
2099 void
c_pltext(void)2100 c_pltext( void )
2101 {
2102     if ( plsc->level > 0 )
2103         plP_esc( PLESC_TEXT, NULL );
2104 }
2105 
2106 //--------------------------------------------------------------------------
2107 // void pl_cmd()
2108 //
2109 //! Front-end to driver escape function.
2110 //! In principle this can be used to pass just about anything directly
2111 //! to the driver.
2112 //!
2113 //! @param op A PLESC command to pass to the driver.
2114 //! @param ptr Data associated with the op command.
2115 //--------------------------------------------------------------------------
2116 
2117 void
pl_cmd(PLINT op,void * ptr)2118 pl_cmd( PLINT op, void *ptr )
2119 {
2120     if ( plsc->level > 0 )
2121         plP_esc( op, ptr );
2122 }
2123 
2124 //--------------------------------------------------------------------------
2125 // char *plFindCommand
2126 //
2127 //! Looks for the specified executable file.  Search path:
2128 //!      if command invoked in the build tree:
2129 //!         build_tree/tk (plserver lies there - needed for the tk driver)
2130 //!         source_tree/scripts (plpr lies there - needed for the tk driver)
2131 //!      else
2132 //!	PLPLOT_BIN_ENV = $(PLPLOT_BIN)
2133 //!	current directory
2134 //!	PLPLOT_HOME_ENV/bin = $(PLPLOT_HOME)/bin
2135 //!	BIN_DIR
2136 //!
2137 //! The caller must free the returned pointer (points to malloc'ed memory)
2138 //! when finished with it.
2139 //!
2140 //! @param fn Name of the executable(?).
2141 //!
2142 //! @returns The location of the executable file.
2143 //--------------------------------------------------------------------------
2144 
2145 char *
plFindCommand(PLCHAR_VECTOR fn)2146 plFindCommand( PLCHAR_VECTOR fn )
2147 {
2148     char *fs = NULL, *dn;
2149 
2150     //*** see if in build tree **
2151     if ( plInBuildTree() == 1 )
2152     {
2153         plGetName( BUILD_DIR, "bindings/tk", fn, &fs );
2154         if ( !plFindName( fs ) )
2155             return fs;
2156         else
2157         {
2158             plGetName( SOURCE_DIR, "scripts", fn, &fs );
2159             if ( !plFindName( fs ) )
2160                 return fs;
2161         }
2162     }
2163 
2164 // PLPLOT_BIN_ENV = $(PLPLOT_BIN)
2165 
2166 #if defined ( PLPLOT_BIN_ENV )
2167     if ( ( dn = getenv( PLPLOT_BIN_ENV ) ) != NULL )
2168     {
2169         plGetName( dn, "", fn, &fs );
2170         if ( !plFindName( fs ) )
2171             return fs;
2172         fprintf( stderr, PLPLOT_BIN_ENV "=\"%s\"\n", dn ); // what IS set?
2173     }
2174 #endif  // PLPLOT_BIN_ENV
2175 
2176 // Current directory
2177 
2178     plGetName( ".", "", fn, &fs );
2179     if ( !plFindName( fs ) )
2180         return fs;
2181 
2182 // PLPLOT_HOME_ENV/bin = $(PLPLOT_HOME)/bin
2183 
2184 #if defined ( PLPLOT_HOME_ENV )
2185     if ( ( dn = getenv( PLPLOT_HOME_ENV ) ) != NULL )
2186     {
2187         plGetName( dn, "bin", fn, &fs );
2188         if ( !plFindName( fs ) )
2189             return fs;
2190         fprintf( stderr, PLPLOT_HOME_ENV "=\"%s\"\n", dn ); // what IS set?
2191     }
2192 #endif  // PLPLOT_HOME_ENV
2193 
2194 // BIN_DIR
2195 
2196 #if defined ( BIN_DIR )
2197     plGetName( BIN_DIR, "", fn, &fs );
2198     if ( !plFindName( fs ) )
2199         return fs;
2200 #endif
2201 
2202 // Crapped out
2203 
2204     free_mem( fs );
2205     fprintf( stderr, "plFindCommand: cannot locate command: %s\n", fn );
2206 #if defined ( BIN_DIR )
2207     fprintf( stderr, "bin dir=\"" BIN_DIR "\"\n" );      // what WAS set?
2208 #endif  // BIN_DIR
2209     return NULL;
2210 }
2211 
2212 //--------------------------------------------------------------------------
2213 // FILE *plLibOpen(fn)
2214 //
2215 //! Return file pointer to library file (such as a colormap palette).
2216 //! Locations checked:
2217 //!	PLPLOT_LIB_ENV = $(PLPLOT_LIB)
2218 //!	current directory
2219 //!	PLPLOT_HOME_ENV/lib = $(PLPLOT_HOME)/lib
2220 //!	DATA_DIR
2221 //!	PLLIBDEV
2222 //!
2223 //! @param fn Name of the file.
2224 //!
2225 //! @returns A open file pointer (if successful).
2226 //--------------------------------------------------------------------------
2227 
2228 FILE *
plLibOpen(PLCHAR_VECTOR fn)2229 plLibOpen( PLCHAR_VECTOR fn )
2230 {
2231     FILE    *ret = NULL;
2232 
2233     PDFstrm *pdfs = plLibOpenPdfstrm( fn );
2234     if ( pdfs == NULL )
2235     {
2236         return NULL;
2237     }
2238     if ( pdfs->file != NULL )
2239     {
2240         ret        = pdfs->file;
2241         pdfs->file = NULL;
2242     }
2243     pdf_close( pdfs );
2244     return ret;
2245 }
2246 
2247 //--------------------------------------------------------------------------
2248 // FILE *plLibOpenPdfstrm(fn)
2249 //
2250 //! Return file PDFstrm * to a file (originally used for loading fonts?).
2251 //! Locations checked:
2252 //!	PLPLOT_LIB_ENV = $(PLPLOT_LIB)
2253 //!	current directory
2254 //!	PLPLOT_HOME_ENV/lib = $(PLPLOT_HOME)/lib
2255 //!	DATA_DIR
2256 //!	PLLIBDEV
2257 //!
2258 //! @param fn Name of the file.
2259 //!
2260 //! @returns A open PDFstrm file pointer (if successful)
2261 //--------------------------------------------------------------------------
2262 PDFstrm *
plLibOpenPdfstrm(PLCHAR_VECTOR fn)2263 plLibOpenPdfstrm( PLCHAR_VECTOR fn )
2264 {
2265     PDFstrm *file;
2266     char    *fs = NULL, *dn = NULL;
2267 
2268 //***   search build tree               ***
2269 
2270     if ( plInBuildTree() == 1 )
2271     {
2272         plGetName( SOURCE_DIR, "data", fn, &fs );
2273 
2274         if ( ( file = pdf_fopen( fs, "rb" ) ) != NULL )
2275             goto done;
2276     }
2277 
2278 //***	search PLPLOT_LIB_ENV = $(PLPLOT_LIB)	***
2279 
2280 #if defined ( PLPLOT_LIB_ENV )
2281     if ( ( dn = getenv( PLPLOT_LIB_ENV ) ) != NULL )
2282     {
2283         plGetName( dn, "", fn, &fs );
2284 
2285         if ( ( file = pdf_fopen( fs, "rb" ) ) != NULL )
2286             goto done;
2287         fprintf( stderr, PLPLOT_LIB_ENV "=\"%s\"\n", dn ); // what IS set?
2288     }
2289 #endif  // PLPLOT_LIB_ENV
2290 
2291 //***	search current directory	***
2292 
2293     if ( ( file = pdf_fopen( fn, "rb" ) ) != NULL )
2294     {
2295         pldebug( "plLibOpenPdfstr", "Found file %s in current directory.\n", fn );
2296         free_mem( fs );
2297         return ( file );
2298     }
2299 
2300 //***	search PLPLOT_HOME_ENV/lib = $(PLPLOT_HOME)/lib	***
2301 
2302 #if defined ( PLPLOT_HOME_ENV )
2303     if ( ( dn = getenv( PLPLOT_HOME_ENV ) ) != NULL )
2304     {
2305         plGetName( dn, "lib", fn, &fs );
2306 
2307         if ( ( file = pdf_fopen( fs, "rb" ) ) != NULL )
2308             goto done;
2309         fprintf( stderr, PLPLOT_HOME_ENV "=\"%s\"\n", dn ); // what IS set?
2310     }
2311 #endif  // PLPLOT_HOME_ENV/lib
2312 
2313 //***   search installed location	***
2314 
2315 #if defined ( DATA_DIR )
2316     plGetName( DATA_DIR, "", fn, &fs );
2317 
2318     if ( ( file = pdf_fopen( fs, "rb" ) ) != NULL )
2319         goto done;
2320 #endif  // DATA_DIR
2321 
2322 //***   search hardwired location	***
2323 
2324 #ifdef PLLIBDEV
2325     plGetName( PLLIBDEV, "", fn, &fs );
2326 
2327     if ( ( file = pdf_fopen( fs, "rb" ) ) != NULL )
2328         goto done;
2329 #endif  // PLLIBDEV
2330 
2331 #ifdef macintosh
2332     file = plMacLibOpen( fn );
2333     if ( file != NULL )
2334         goto done;
2335 #endif // macintosh
2336 
2337     if ( plplotLibDir != NULL )
2338     {
2339         plGetName( plplotLibDir, "", fn, &fs );
2340         if ( ( file = pdf_fopen( fs, "rb" ) ) != NULL )
2341             goto done;
2342     }
2343 
2344 //***   not found, give up      ***
2345     pldebug( "plLibOpenPdfstr", "File %s not found.\n", fn );
2346     free_mem( fs );
2347     return NULL;
2348 
2349 done:
2350     pldebug( "plLibOpenPdfstr", "Found file %s\n", fs );
2351     free_mem( fs );
2352     return ( file );
2353 }
2354 
2355 //--------------------------------------------------------------------------
2356 // PLINR plFindName
2357 //
2358 //! Authors: Paul Dubois (LLNL), others?
2359 //! This function is in the public domain.
2360 //!
2361 //! Given a pathname, determine if it is a symbolic link.  If so, continue
2362 //! searching to the ultimate terminus - there may be more than one link.
2363 //! Use the error value to determine when the terminus is reached, and to
2364 //! determine if the pathname really exists.  Then stat it to determine
2365 //! whether it's executable.  Return 0 for an executable, errno otherwise.
2366 //! Note that 'p' _must_ have at least one '/' character - it does by
2367 //! construction in this program.  The contents of the array pointed to by
2368 //! 'p' are changed to the actual pathname if findname is successful.
2369 //!
2370 //! This function is only defined under Unix for now.
2371 //!
2372 //! @param p Name of the executable to find.
2373 //!
2374 //! @returns 0 if p is found & is an executable.
2375 //--------------------------------------------------------------------------
2376 
2377 #ifdef __unix
2378 PLINT
plFindName(char * p)2379 plFindName( char *p )
2380 {
2381     ssize_t     n;
2382     char        buf[PLPLOT_MAX_PATH], *cp;
2383     struct stat sbuf;
2384 
2385     pldebug( "plFindName", "Trying to find %s\n", p );
2386     while ( ( n = readlink( p, buf, PLPLOT_MAX_PATH ) ) > 0 )
2387     {
2388         pldebug( "plFindName", "Readlink read %d chars at: %s\n", n, p );
2389         if ( buf[0] == '/' )
2390         {
2391             // Link is an absolute path
2392 
2393             strncpy( p, buf, (size_t) n );
2394             p[n] = '\0';
2395             pldebug( "plFindName", "Link is absolute: %s\n", p );
2396         }
2397         else
2398         {
2399             // Link is relative to its directory; make it absolute
2400 
2401             cp = 1 + strrchr( p, '/' );
2402             strncpy( cp, buf, (size_t) n );
2403             cp[n] = '\0';
2404             pldebug( "plFindName",
2405                 "Link is relative: %s\n\tTotal path:%s\n", cp, p );
2406         }
2407     }
2408 
2409 // This macro not defined on the NEC SX-3
2410 
2411 #ifdef SX
2412 #define S_ISREG( mode )    ( mode & S_IFREG )
2413 #endif
2414 
2415 // SGI machines return ENXIO instead of EINVAL Dubois 11/92
2416 
2417     if ( errno == EINVAL || errno == ENXIO )
2418     {
2419         pldebug( "plFindName", "%s may be the one...\n", p );
2420         if ( ( stat( p, &sbuf ) == 0 ) && S_ISREG( sbuf.st_mode ) )
2421         {
2422             pldebug( "plFindName", "%s is a regular file\n", p );
2423             return ( access( p, X_OK ) );
2424         }
2425     }
2426     pldebug( "plFindName", "%s found but is not executable\n", p );
2427     return ( errno ? errno : -1 );
2428 }
2429 
2430 #else
2431 PLINT
plFindName(char * p)2432 plFindName( char *p )
2433 {
2434     return 1;
2435 }
2436 #endif
2437 
2438 //--------------------------------------------------------------------------
2439 // void plGetName()
2440 //
2441 //! Gets search name for file by concatenating the dir, subdir, and file
2442 //! name, allocating memory as needed.  The appropriate delimiter is added
2443 //! after the dir specification as necessary.  The caller is responsible
2444 //! for freeing the malloc'ed memory.
2445 //!
2446 //! @param dir The directory name.
2447 //! @param subdir The sub-directory name.
2448 //! @param filename The file name.
2449 //! @param filespec The result of concatenating dir, subdir and filename.
2450 //--------------------------------------------------------------------------
2451 
2452 void
plGetName(PLCHAR_VECTOR dir,PLCHAR_VECTOR subdir,PLCHAR_VECTOR filename,char ** filespec)2453 plGetName( PLCHAR_VECTOR dir, PLCHAR_VECTOR subdir, PLCHAR_VECTOR filename, char **filespec )
2454 {
2455     size_t lfilespec;
2456 
2457 // Malloc space for filespec
2458 
2459     free_mem( *filespec );
2460     // Be slightly generous since 3 (two delimiters + NULL byte) should be
2461     // enough.
2462     lfilespec = strlen( dir ) + strlen( subdir ) + strlen( filename ) + 10;
2463     if ( ( *filespec = (char *) malloc( lfilespec ) ) == NULL )
2464     {
2465         plexit( "plGetName: Insufficient memory" );
2466     }
2467 
2468     strcpy( *filespec, dir );
2469 
2470     if ( *subdir != '\0' )
2471     {
2472         strcat_delim( *filespec );
2473         strcat( *filespec, subdir );
2474     }
2475     if ( *filename != '\0' )
2476     {
2477         strcat_delim( *filespec );
2478         strcat( *filespec, filename );
2479     }
2480 #ifdef _WIN32
2481     // According to http://msdn.microsoft.com/en-us/library/vstudio/tcxf1dw6.aspx
2482     // and also Wine tests, Microsoft does not support the c99 standard %zu
2483     // format.  Instead, %lu is recommended for size_t.
2484     pldebug( "plGetName", "Maximum length of full pathname of file to be found is %lu\n", lfilespec - 1 );
2485 #else
2486     pldebug( "plGetName", "Maximum length of full pathname of file to be found is %zu\n", lfilespec - 1 );
2487 #endif
2488     pldebug( "plGetName", "Full pathname of file to be found is %s\n", *filespec );
2489 }
2490 
2491 //--------------------------------------------------------------------------
2492 // void strcat_delim()
2493 //
2494 //! Append path name deliminator if necessary (does not add one if one's
2495 //! there already, or if dealing with a colon-terminated device name).
2496 //!
2497 //! @param dirspec String to add the appropriate delimiter too.
2498 //--------------------------------------------------------------------------
2499 
2500 static void
strcat_delim(char * dirspec)2501 strcat_delim( char *dirspec )
2502 {
2503     size_t ldirspec = strlen( dirspec );
2504 #if defined ( MSDOS ) || defined ( _WIN32 )
2505     if ( dirspec[ldirspec - 1] != '\\' )
2506         strcat( dirspec, "\\" );
2507 #elif defined ( macintosh )
2508     if ( dirspec[ldirspec - 1] != ':' )
2509         strcat( dirspec, ":" );
2510 #else           // unix is the default
2511     if ( dirspec[ldirspec - 1] != '/' )
2512         strcat( dirspec, "/" );
2513 #endif
2514 }
2515 
2516 //--------------------------------------------------------------------------
2517 // plcol_interp()
2518 //
2519 //! Initializes device cmap 1 entry by interpolation from pls->cmap1
2520 //! entries.  Returned PLColor is supposed to represent the i_th color
2521 //! out of a total of ncol colors in the current color scheme.
2522 //!
2523 //! @param pls A plot stream structure.
2524 //! @param newcolor A color structure to store the color in.
2525 //! @param i Index of the desired color.
2526 //! @param ncol Total number of colors (supported by the device?).
2527 //--------------------------------------------------------------------------
2528 
2529 void
plcol_interp(PLStream * pls,PLColor * newcolor,int i,int ncol)2530 plcol_interp( PLStream *pls, PLColor *newcolor, int i, int ncol )
2531 {
2532     PLFLT x, delta;
2533     int   il, ir;
2534 
2535     x     = (double) ( i * ( pls->ncol1 - 1 ) ) / (double) ( ncol - 1 );
2536     il    = (int) x;
2537     ir    = il + 1;
2538     delta = x - il;
2539 
2540     if ( ir > pls->ncol1 || il < 0 )
2541         fprintf( stderr, "Invalid color\n" );
2542 
2543     else if ( ir == pls->ncol1 || ( delta == 0. ) )
2544     {
2545         newcolor->r = pls->cmap1[il].r;
2546         newcolor->g = pls->cmap1[il].g;
2547         newcolor->b = pls->cmap1[il].b;
2548         newcolor->a = pls->cmap1[il].a;
2549     }
2550     else
2551     {
2552         newcolor->r = (unsigned char) ( ( 1. - delta ) * pls->cmap1[il].r + delta * pls->cmap1[ir].r );
2553         newcolor->g = (unsigned char) ( ( 1. - delta ) * pls->cmap1[il].g + delta * pls->cmap1[ir].g );
2554         newcolor->b = (unsigned char) ( ( 1. - delta ) * pls->cmap1[il].b + delta * pls->cmap1[ir].b );
2555         newcolor->a = ( 1. - delta ) * pls->cmap1[il].a + delta * pls->cmap1[ir].a;
2556     }
2557 }
2558 
2559 //--------------------------------------------------------------------------
2560 // plOpenFile()
2561 //
2562 //! Opens file for output, prompting if not set.
2563 //! Prints extra newline at end to make output look better in batch runs.
2564 //! A file name of "-" indicates output to stdout.
2565 //!
2566 //! @param pls A plot stream structure.
2567 //--------------------------------------------------------------------------
2568 
2569 #define MAX_NUM_TRIES    10
2570 void
plOpenFile(PLStream * pls)2571 plOpenFile( PLStream *pls )
2572 {
2573     int    i = 0, count = 0;
2574     size_t len;
2575     char   line[BUFFER_SIZE];
2576 
2577     while ( pls->OutFile == NULL )
2578     {
2579 // Setting pls->FileName = NULL forces creation of a new family member
2580 // You should also free the memory associated with it if you do this
2581 
2582         if ( pls->family && pls->BaseName != NULL )
2583             plP_getmember( pls );
2584 
2585 // Prompt if filename still not known
2586 
2587         if ( pls->FileName == NULL )
2588         {
2589             do
2590             {
2591                 fprintf( stdout, "Enter graphics output file name: " );
2592                 plio_fgets( line, sizeof ( line ), stdin );
2593                 len = strlen( line );
2594                 if ( len )
2595                     len--;
2596                 line[len] = '\0';       // strip new-line
2597                 count++;                // count zero entries
2598             } while ( !len && count < MAX_NUM_TRIES );
2599             plP_sfnam( pls, line );
2600         }
2601 
2602 // If name is "-", send to stdout
2603 
2604         if ( !strcmp( pls->FileName, "-" ) )
2605         {
2606             pls->OutFile     = stdout;
2607             pls->output_type = 1;
2608             break;
2609         }
2610 
2611 // Need this here again, for prompted family initialization
2612 
2613         if ( pls->family && pls->BaseName != NULL )
2614             plP_getmember( pls );
2615 
2616         if ( i++ > 10 )
2617             plexit( "Too many tries." );
2618 
2619         if ( ( pls->OutFile = fopen( pls->FileName, "wb+" ) ) == NULL )
2620             fprintf( stderr, "Can't open %s.\n", pls->FileName );
2621         else
2622             pldebug( "plOpenFile", "Opened %s\n", pls->FileName );
2623     }
2624 }
2625 
2626 //--------------------------------------------------------------------------
2627 // plCloseFile()
2628 //
2629 //! Closes output file unless it is associated with stdout.
2630 //!
2631 //! @param pls A plot stream structure.
2632 //--------------------------------------------------------------------------
2633 
2634 void
plCloseFile(PLStream * pls)2635 plCloseFile( PLStream *pls )
2636 {
2637     if ( pls->OutFile != NULL )
2638     {
2639         // Don't close if the output file was stdout
2640         if ( pls->FileName && strcmp( pls->FileName, "-" ) == 0 )
2641             return;
2642 
2643         fclose( pls->OutFile );
2644         pls->OutFile = NULL;
2645     }
2646 }
2647 
2648 //--------------------------------------------------------------------------
2649 // plP_getmember()
2650 //
2651 //! Sets up next file member name (in pls->FileName), but does not open it.
2652 //!
2653 //! @param pls A plot stream structure.
2654 //--------------------------------------------------------------------------
2655 
2656 void
plP_getmember(PLStream * pls)2657 plP_getmember( PLStream *pls )
2658 {
2659     char   tmp[BUFFER_SIZE];
2660     char   prefix[BUFFER_SIZE];
2661     char   * suffix;
2662     char   num[BUFFER_SIZE];
2663     size_t maxlen;
2664 
2665     maxlen = strlen( pls->BaseName ) + 10;
2666     if ( pls->FileName == NULL )
2667     {
2668         if ( ( pls->FileName = (char *) malloc( maxlen ) ) == NULL )
2669         {
2670             plexit( "plP_getmember: Insufficient memory" );
2671         }
2672     }
2673 
2674     suffix = strstr( pls->BaseName, "%n" );
2675 
2676     snprintf( tmp, BUFFER_SIZE, "%%0%1ii", (int) pls->fflen );
2677     snprintf( num, BUFFER_SIZE, tmp, pls->member );
2678 
2679     if ( suffix == NULL )
2680         snprintf( pls->FileName, maxlen, "%s.%s", pls->BaseName, num );
2681     else
2682     {
2683         strncpy( prefix, pls->BaseName, BUFFER_SIZE - 1 );
2684         prefix [( suffix - pls->BaseName < BUFFER_SIZE ) ? ( suffix - pls->BaseName ) : BUFFER_SIZE - 1] = '\0';
2685         snprintf( pls->FileName, maxlen, "%s%s%s", prefix, num, suffix + 2 );
2686     }
2687 }
2688 
2689 //--------------------------------------------------------------------------
2690 // plP_sfnam()
2691 //
2692 //! Sets up file name (with "%n" removed if present) & family stem name.
2693 //! Reserve some extra space (10 chars) to hold an optional member number.
2694 //!
2695 //! @param pls A plot stream.
2696 //! @param fnam The base file name of the plot files.
2697 //--------------------------------------------------------------------------
2698 
2699 void
plP_sfnam(PLStream * pls,PLCHAR_VECTOR fnam)2700 plP_sfnam( PLStream *pls, PLCHAR_VECTOR fnam )
2701 {
2702     char   prefix[BUFFER_SIZE];
2703     char   * suffix;
2704     size_t maxlen;
2705     pls->OutFile = NULL;
2706 
2707     if ( pls->FileName != NULL )
2708         free( (void *) pls->FileName );
2709 
2710     maxlen = 10 + strlen( fnam );
2711     if ( ( pls->FileName = (char *) malloc( maxlen ) ) == NULL )
2712     {
2713         plexit( "plP_sfnam: Insufficient memory" );
2714     }
2715 
2716     suffix = strstr( fnam, "%n" );
2717 
2718     if ( suffix == NULL )
2719     {
2720         strncpy( pls->FileName, fnam, maxlen - 1 );
2721         pls->FileName[maxlen - 1] = '\0';
2722     }
2723     else
2724     {
2725         strncpy( prefix, fnam, BUFFER_SIZE - 1 );
2726         prefix [( suffix - fnam ) < BUFFER_SIZE ? ( suffix - fnam ) : BUFFER_SIZE - 1] = '\0';
2727         snprintf( pls->FileName, maxlen, "%s%s", prefix, suffix + 2 );
2728     }
2729 
2730     if ( pls->BaseName != NULL )
2731         free( (void *) pls->BaseName );
2732 
2733     if ( ( pls->BaseName = (char *) malloc( maxlen ) ) == NULL )
2734     {
2735         plexit( "plP_sfnam: Insufficient memory" );
2736     }
2737 
2738     strncpy( pls->BaseName, fnam, maxlen - 1 );
2739     pls->BaseName[maxlen - 1] = '\0';
2740 }
2741 
2742 //--------------------------------------------------------------------------
2743 // plFamInit()
2744 //
2745 //! Initializes family file parameters.
2746 //!
2747 //! @param pls A plot stream structure.
2748 //--------------------------------------------------------------------------
2749 
2750 void
plFamInit(PLStream * pls)2751 plFamInit( PLStream *pls )
2752 {
2753     if ( pls->family )
2754     {
2755         pls->bytecnt = 0;
2756         if ( !pls->member )
2757             pls->member = 1;
2758         if ( !pls->finc )
2759             pls->finc = 1;
2760         if ( !pls->fflen )
2761             pls->fflen = 1;
2762         if ( !pls->bytemax )
2763             pls->bytemax = PL_FILESIZE_KB * 1000;
2764     }
2765 }
2766 
2767 //--------------------------------------------------------------------------
2768 // plGetFam()
2769 //
2770 //! Starts new member file of family file set if necessary.
2771 //!
2772 //! Note each member file is a complete graphics file (can be printed
2773 //! individually), although 'plrender' will treat a family as a single
2774 //! logical file if given the family name instead of the member name.
2775 //!
2776 //! @param pls A plot stream structure.
2777 //--------------------------------------------------------------------------
2778 
2779 void
plGetFam(PLStream * pls)2780 plGetFam( PLStream *pls )
2781 {
2782     PLFLT xpmm_loc, ypmm_loc;
2783     if ( pls->family )
2784     {
2785         if ( pls->bytecnt > pls->bytemax || pls->famadv )
2786         {
2787             PLINT local_page_status = pls->page_status;
2788             plP_tidy();
2789             pls->member += pls->finc;
2790             pls->famadv  = 0;
2791             plP_init();
2792             // Restore page status (normally AT_BOP) that was changed
2793             // to AT_EOP by plP_init.
2794             pls->page_status = local_page_status;
2795 
2796             // Apply compensating factor to original xpmm and ypmm so that
2797             // character aspect ratio is preserved when overall aspect ratio
2798             // is changed.
2799             plP_gpixmm( &xpmm_loc, &ypmm_loc );
2800             plP_setpxl( xpmm_loc * plsc->caspfactor, ypmm_loc / plsc->caspfactor );
2801             return;
2802         }
2803     }
2804 }
2805 
2806 //--------------------------------------------------------------------------
2807 // plRotPhy()
2808 //
2809 //! Rotates physical coordinates if necessary for given orientation.
2810 //! Each time orient is incremented, the plot is rotated 90 deg clockwise.
2811 //! Note: this is now used only to rotate by 90 degrees for devices that
2812 //! expect portrait mode.
2813 //!
2814 //! @param orient New plot orientation (0-3)
2815 //! @param xmin Current plot x minimum?
2816 //! @param ymin Current plot y minimum?
2817 //! @param xmax Current plot x maximum?
2818 //! @param ymax Current plot y maximum?
2819 //! @param px Old x coordinate mapped to new x coordinate.
2820 //! @param py Old y coordinate mapped to new y coordinate.
2821 //--------------------------------------------------------------------------
2822 
2823 void
plRotPhy(PLINT orient,PLINT xmin,PLINT ymin,PLINT xmax,PLINT ymax,PLINT * px,PLINT * py)2824 plRotPhy( PLINT orient, PLINT xmin, PLINT ymin, PLINT xmax, PLINT ymax,
2825           PLINT *px, PLINT *py )
2826 {
2827     int x, y;
2828 
2829     x = *px;
2830     y = *py;
2831 
2832     switch ( orient % 4 )
2833     {
2834     case 1:
2835         *px = xmin + ( y - ymin );
2836         *py = ymin + ( xmax - x );
2837         break;
2838 
2839     case 2:
2840         *px = xmin + ( xmax - x );
2841         *py = ymin + ( ymax - y );
2842         break;
2843 
2844     case 3:
2845         *px = xmin + ( ymax - y );
2846         *py = ymin + ( x - xmin );
2847         break;
2848 
2849     default:
2850         break;                  // do nothing
2851     }
2852 }
2853 
2854 //--------------------------------------------------------------------------
2855 // plAllocDev()
2856 //
2857 //! Allocates a standard PLDev structure for device-specific data, stores
2858 //! the address in pls->dev, and returns the address as well.
2859 //!
2860 //! @param pls A plot stream structure.
2861 //!
2862 //! @returns A PLDev *
2863 //--------------------------------------------------------------------------
2864 
2865 PLDev *
plAllocDev(PLStream * pls)2866 plAllocDev( PLStream *pls )
2867 {
2868     if ( pls->dev != NULL )
2869         free( (void *) pls->dev );
2870 
2871     pls->dev = calloc( 1, (size_t) sizeof ( PLDev ) );
2872     if ( pls->dev == NULL )
2873         plexit( "plAllocDev: cannot allocate memory\n" );
2874 
2875     return (PLDev *) pls->dev;
2876 }
2877 
2878 //--------------------------------------------------------------------------
2879 // plGinInit()
2880 //
2881 //! Just fills in the PLGraphicsIn with appropriate initial values.
2882 //!
2883 //! @param gin A plot graphics input (i.e. keypress or mouseclick) structure.
2884 //--------------------------------------------------------------------------
2885 
2886 void
plGinInit(PLGraphicsIn * gin)2887 plGinInit( PLGraphicsIn *gin )
2888 {
2889     gin->type      = 0;
2890     gin->state     = 0;
2891     gin->keysym    = 0;
2892     gin->button    = 0;
2893     gin->string[0] = '\0';
2894     gin->pX        = gin->pY = -1;
2895     gin->dX        = gin->dY = 0.;
2896     gin->wX        = gin->wY = 0.;
2897 }
2898 
2899 //--------------------------------------------------------------------------
2900 // plGetInt()
2901 //
2902 //! Prompts human to input an integer in response to given message.
2903 //!
2904 //! @param s The prompt message.
2905 //!
2906 //! @returns The PLINT the human entered.
2907 //--------------------------------------------------------------------------
2908 
2909 PLINT
plGetInt(PLCHAR_VECTOR s)2910 plGetInt( PLCHAR_VECTOR s )
2911 {
2912     int  m;
2913     int  i = 0;
2914     char line[BUFFER_SIZE];
2915 
2916     while ( i++ < 10 )
2917     {
2918         fputs( s, stdout );
2919         plio_fgets( line, sizeof ( line ), stdin );
2920 
2921 #ifdef MSDOS
2922         m = atoi( line );
2923         return ( m );
2924 #else
2925         if ( sscanf( line, "%d", &m ) == 1 )
2926             return ( m );
2927         fprintf( stdout, "No value or value out of range; please try again\n" );
2928 #endif
2929     }
2930     plexit( "Too many tries." );
2931     return ( 0 );
2932 }
2933 
2934 //--------------------------------------------------------------------------
2935 // plGetFlt()
2936 //
2937 //! Prompts human to input a float in response to given message.
2938 //!
2939 //! @param s The prompt message.
2940 //!
2941 //! @returns The PLFLT the human entered.
2942 //--------------------------------------------------------------------------
2943 
2944 PLFLT
plGetFlt(PLCHAR_VECTOR s)2945 plGetFlt( PLCHAR_VECTOR s )
2946 {
2947     PLFLT  m;
2948     double m1;
2949     int    i = 0;
2950     char   line[BUFFER_SIZE];
2951 
2952     while ( i++ < 10 )
2953     {
2954         fputs( s, stdout );
2955         plio_fgets( line, sizeof ( line ), stdin );
2956 
2957 #ifdef MSDOS
2958         m = atof( line );
2959         return ( m );
2960 #else
2961         if ( sscanf( line, "%lf", &m1 ) == 1 )
2962         {
2963             m = (PLFLT) m1;
2964             return ( m );
2965         }
2966         fprintf( stdout, "No value or value out of range; please try again\n" );
2967 #endif
2968     }
2969     plexit( "Too many tries." );
2970     return ( 0. );
2971 }
2972 
2973 //--------------------------------------------------------------------------
2974 // plstrdup()
2975 //
2976 //! A replacement for strdup(), which isn't portable.
2977 //! Caller responsible for freeing the allocated memory.
2978 //!
2979 //! @param src The string to duplicate.
2980 //!
2981 //! @returns A copy of the string src.
2982 //--------------------------------------------------------------------------
2983 
2984 char PLDLLIMPEXP *
plstrdup(PLCHAR_VECTOR src)2985 plstrdup( PLCHAR_VECTOR src )
2986 {
2987     char *dest = (char *) malloc( ( strlen( src ) + 1 ) * sizeof ( char ) );
2988     if ( dest != NULL )
2989         strcpy( dest, src );
2990     else
2991         plabort( "Out of memory" );
2992 
2993     return dest;
2994 }
2995 
2996 #ifndef PL_HAVE_SNPRINTF
2997 //--------------------------------------------------------------------------
2998 // plsnprintf()
2999 //
3000 //! Dummy function for snprintf(). This function just calls
3001 //! the unsafe function ignoring the string size. This function will
3002 //! rarely be needed if ever.
3003 //!
3004 //! @param buffer String output buffer.
3005 //! @param n Size of buffer.
3006 //! @param format The format string.
3007 //! @param ... The values that go in the format string (...)
3008 //!
3009 //! @returns The length of buffer that is actually used.
3010 //--------------------------------------------------------------------------
3011 
3012 int
plsnprintf(char * buffer,int n,PLCHAR_VECTOR format,...)3013 plsnprintf( char *buffer, int n, PLCHAR_VECTOR format, ... )
3014 {
3015     int     ret;
3016 
3017     va_list args;
3018     va_start( args, format );
3019     ret = vsprintf( buffer, format, args );
3020     va_end( args );
3021 
3022     // Check if overrun occured
3023     if ( ret > n - 1 )
3024         plabort( "plsnprintf: buffer overrun" );
3025 
3026     return ret;
3027 }
3028 
3029 //--------------------------------------------------------------------------
3030 // plsnscanf()
3031 //
3032 //! Dummy function for snscanf(). This function just calls
3033 //! the unsafe function ignoring the string size. This function will
3034 //! rarely be needed if ever.
3035 //!
3036 //! @param buffer String output buffer.
3037 //! @param n Size of buffer.
3038 //! @param format The format string.
3039 //! @param ... The values that go in the format string (...)
3040 //!
3041 //! @returns The length of buffer that is actually used.
3042 //--------------------------------------------------------------------------
3043 
3044 int
plsnscanf(PLCHAR_VECTOR buffer,int n,PLCHAR_VECTOR format,...)3045 plsnscanf( PLCHAR_VECTOR buffer, int n, PLCHAR_VECTOR format, ... )
3046 {
3047     int     ret;
3048 
3049     va_list args;
3050     va_start( args, format );
3051     ret = vsscanf( buffer, format, args );
3052     va_end( args );
3053 
3054     return ret;
3055 }
3056 
3057 #endif // PL_HAVE_SNPRINTF
3058 
3059 //--------------------------------------------------------------------------
3060 // plseed()
3061 //
3062 //! Set the seed for the random number generator included.
3063 //!
3064 //! @param seed The random number generator seed value.
3065 //--------------------------------------------------------------------------
3066 
3067 void
c_plseed(unsigned int seed)3068 c_plseed( unsigned int seed )
3069 {
3070     init_genrand( seed );
3071 }
3072 
3073 //--------------------------------------------------------------------------
3074 // plrandd()
3075 //
3076 //! @returns A random number on [0,1]-interval.
3077 //!
3078 //--------------------------------------------------------------------------
3079 
3080 PLFLT
c_plrandd(void)3081 c_plrandd( void )
3082 {
3083     return (PLFLT) ( genrand_real1() );
3084 }
3085 
3086 //--------------------------------------------------------------------------
3087 // plsave_set_locale()
3088 //
3089 //! Save LC_NUMERIC locale in a string.  The pointer to that string is
3090 //! returned. Then set LC_NUMERIC to "C" locale.
3091 //! n.b. plsave_set_locale and plrestore_locale should always be used as
3092 //! a pair to surround PLplot code that absolutely requires the
3093 //! LC_NUMERIC "C" locale to be in effect.  It is one of plrestore_locale's
3094 //! responsibilities to free the memory allocated here for the locale
3095 //! string.
3096 //!
3097 //! @returns The LC_NUMERIC locale.
3098 //--------------------------------------------------------------------------
3099 
3100 char *
plsave_set_locale(void)3101 plsave_set_locale( void )
3102 {
3103     char * setlocale_ptr;
3104     char * saved_lc_numeric_locale;
3105 
3106     if ( !( saved_lc_numeric_locale = (char *) malloc( 100 * sizeof ( char ) ) ) )
3107     {
3108         plexit( "plsave_set_locale: out of memory" );
3109     }
3110 
3111     //save original LC_NUMERIC locale for restore below.
3112     if ( !( setlocale_ptr = setlocale( LC_NUMERIC, NULL ) ) )
3113     {
3114         plexit( "plsave_set_locale: LC_NUMERIC locale could not be determined for NULL locale.\n" );
3115     }
3116     strncpy( saved_lc_numeric_locale, setlocale_ptr, 100 );
3117     saved_lc_numeric_locale[99] = '\0';
3118 
3119     // Do not use pldebug since get overflowed stack (infinite recursion)
3120     // if device is interactive (i.e., pls->termin is set).
3121     // comment out fprintf (unless there is some emergency debugging to do)
3122     // because output is too voluminous.
3123     //
3124     // fprintf(stderr, "plsave_set_locale: saved LC_NUMERIC locale is \"%s\"\n", saved_lc_numeric_locale);
3125     //
3126 
3127     if ( !( setlocale( LC_NUMERIC, "C" ) ) )
3128     {
3129         plexit( "plsave_set_locale: LC_NUMERIC locale could not be set to \"C\"" );
3130     }
3131     return saved_lc_numeric_locale;
3132 }
3133 
3134 //--------------------------------------------------------------------------
3135 // plrestore_locale()
3136 //
3137 //! Restore LC_NUMERIC locale string that was determined by
3138 //! plsave_set_locale with the pointer to that string as the argument.
3139 //! Also, free the memory for that string.
3140 //!
3141 //! @param saved_lc_numeric_locale The saved numeric locale..
3142 //--------------------------------------------------------------------------
3143 
3144 void
plrestore_locale(char * saved_lc_numeric_locale)3145 plrestore_locale( char *saved_lc_numeric_locale )
3146 {
3147     // Do not use pldebug since get overflowed stack (infinite recursion)
3148     // if device is interactive (i.e., pls->termin is set).
3149     // comment out fprintf (unless there is some emergency debugging to do)
3150     // because output is too voluminous.
3151     //
3152     // fprintf(stderr, "plrestore_locale: restored LC_NUMERIC locale is \"%s\"\n", saved_lc_numeric_locale);
3153     //
3154 
3155     if ( !( setlocale( LC_NUMERIC, saved_lc_numeric_locale ) ) )
3156     {
3157         char msgbuf[1024];
3158         snprintf( msgbuf, 1024, "plrestore_locale: LC_NUMERIC could not be restored to the default \"%s\" locale.\n", saved_lc_numeric_locale );
3159         plexit( msgbuf );
3160     }
3161     free( saved_lc_numeric_locale );
3162 }
3163 
3164 static void
limit_rgba_range(PLCHAR_VECTOR message,PLINT_NC_SCALAR r,PLINT_NC_SCALAR g,PLINT_NC_SCALAR b,PLFLT_NC_SCALAR alpha)3165 limit_rgba_range( PLCHAR_VECTOR message, PLINT_NC_SCALAR r, PLINT_NC_SCALAR g, PLINT_NC_SCALAR b, PLFLT_NC_SCALAR alpha )
3166 {
3167     if ( ( *r < MIN_PLINT_RGB || *r > MAX_PLINT_RGB ) ||
3168          ( *g < MIN_PLINT_RGB || *g > MAX_PLINT_RGB ) ||
3169          ( *b < MIN_PLINT_RGB || *b > MAX_PLINT_RGB ) ||
3170          ( alpha != NULL && ( isnan( *alpha ) || ( *alpha < MIN_PLFLT_ALPHA || *alpha > MAX_PLFLT_ALPHA ) ) ) )
3171     {
3172         plwarn( message );
3173         fprintf( stderr, "%s\n", "Further information relevant to this warning:" );
3174         if ( alpha != NULL )
3175         {
3176             fprintf( stderr, "Invalid RGBA color: %d, %d, %d, %e\n", (int) *r, (int) *g, (int) *b, (double) *alpha );
3177             *r = MAX( MIN_PLINT_RGB, MIN( MAX_PLINT_RGB, *r ) );
3178             *g = MAX( MIN_PLINT_RGB, MIN( MAX_PLINT_RGB, *g ) );
3179             *b = MAX( MIN_PLINT_RGB, MIN( MAX_PLINT_RGB, *b ) );
3180             if ( isnan( *alpha ) )
3181                 *alpha = MAX_PLFLT_ALPHA;
3182             *alpha = MAX( MIN_PLFLT_ALPHA, MIN( MAX_PLFLT_ALPHA, *alpha ) );
3183             fprintf( stderr, "Corrected RGBA color: %d, %d, %d, %e\n", (int) *r, (int) *g, (int) *b, (double) *alpha );
3184         }
3185         else
3186 
3187         {
3188             fprintf( stderr, "Invalid RGB color: %d, %d, %d\n", (int) *r, (int) *g, (int) *b );
3189             *r = MAX( MIN_PLINT_RGB, MIN( MAX_PLINT_RGB, *r ) );
3190             *g = MAX( MIN_PLINT_RGB, MIN( MAX_PLINT_RGB, *g ) );
3191             *b = MAX( MIN_PLINT_RGB, MIN( MAX_PLINT_RGB, *b ) );
3192             fprintf( stderr, "Corrected RGB color: %d, %d, %d\n", (int) *r, (int) *g, (int) *b );
3193         }
3194     }
3195 }
3196