PLplot  5.15.0
plstdio.c
Go to the documentation of this file.
1 // Standardized I/O handler for PLplot.
2 //
3 // Copyright (C) 2006 Jim Dishaw
4 // Copyright (C) 2006 Hazen Babcock
5 // Copyright (C) 2016 Alan W. Irwin
6 //
7 // This file is part of PLplot.
8 //
9 // PLplot is free software; you can redistribute it and/or modify
10 // it under the terms of the GNU Library General Public License as published
11 // by the Free Software Foundation; either version 2 of the License, or
12 // (at your option) any later version.
13 //
14 // PLplot is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU Library General Public License for more details.
18 //
19 // You should have received a copy of the GNU Library General Public License
20 // along with PLplot; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 //
23 //
24 
25 #define DEBUG
26 #define NEED_PLDEBUG
27 #include "plplotP.h"
28 
29 // These three are needed for open.
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33 
34 // For Visual C++ 2005 and later mktemp() and open() are deprecated (see
35 // http://msdn.microsoft.com/en-us/library/ms235413.aspx and
36 // http://msdn.microsoft.com/en-us/library/ms235491.aspx). mktemp()
37 // is redefined to _mktemp() as well as open() to _open(). In addition
38 // we need to include io.h.
39 //
40 #if defined ( _MSC_VER ) && _MSC_VER >= 1400
41 #include <io.h>
42 #define mktemp _mktemp
43 #define open _open
44 #define fdopen _fdopen
45 #endif
46 
47 // AM: getcwd has a somewhat strange status on Windows, its proper
48 // name is _getcwd, this is a problem in the case of DLLs, like with
49 // the Java bindings. The function _getcwd() is
50 // declared in direct.h for Visual C++.
51 #if defined ( _MSC_VER )
52 # include <direct.h>
53 # define getcwd _getcwd
54 #endif
55 
56 // Static function declaration
57 static PLINT
58 get_tmpdir_list( PLINT max_ntmpdir_list, char ** tmpdir_list, PLINT maxcurrdir, char * currdir );
59 
60 //
61 // plio_write()
62 //
63 // Writes the contents of buf to stream. Handles any I/O error conditions
64 // so that the caller can "fire and forget."
65 //
66 
67 void
68 plio_fwrite( void *buf, size_t size, size_t nmemb, FILE *stream )
69 {
70  dbug_enter( "plio_fwrite" );
71 
72  // Exit if there is nothing to write
73  if ( size == 0 || nmemb == 0 )
74  return;
75 
76  // Clear the error flag for this steam
77  clearerr( stream );
78 
79  fwrite( buf, size, nmemb, stream );
80 
81  if ( ferror( stream ) )
82  {
83  // Perhaps we can add a flag (global or per output stream)
84  // in order to decide if we should abort or warn. I think
85  // I/O errors should generate an abort
86  plabort( "Error writing to file" );
87  }
88 }
89 
90 //
91 // plio_fread()
92 //
93 // Read from stream into buf. Like plio_write(), this function will
94 // handle any I/O error conditions.
95 //
96 
97 void
98 plio_fread( void *buf, size_t size, size_t nmemb, FILE *stream )
99 {
100  size_t bytes;
101 
102  dbug_enter( "plio_fread" );
103 
104  // If the buffer has a size of zero, we should complain
105  if ( size == 0 || nmemb == 0 )
106  {
107  plwarn( "Zero length buffer size in plio_fread, returning" );
108  return;
109  }
110 
111  // Clear the error flag for this steam
112  clearerr( stream );
113 
114  bytes = fread( buf, size, nmemb, stream );
115 
116  if ( ( bytes < nmemb ) && ferror( stream ) )
117  {
118  // The read resulted in an error
119  plabort( "Error reading from file" );
120  }
121 }
122 
123 //
124 // plio_fgets()
125 //
126 // Read from stream into buf. This version of fgets is designed for the occasions
127 // where the caller wants to ignore the return value.
128 //
129 // NOTE: If one is reading from a file until an EOF condition, fgets() is better suited
130 // than this function, i.e.
131 //
132 // while(fgets(buf, size, fp) != NULL) { ... do some stuff ... }
133 //
134 // rather than
135 //
136 // while(!feof(fp)) { plio_fgets(buf, size, fp); ... do some stuff ... }
137 //
138 // which would require checking for an empty buffer.
139 //
140 
141 void
142 plio_fgets( char *buf, int size, FILE *stream )
143 {
144  char *s;
145 
146  dbug_enter( "plio_fgets" );
147 
148  // If the buffer has a size of zero, we should complain
149  if ( size == 0 )
150  {
151  plwarn( "Zero length buffer size in plio_fgets, returning" );
152  return;
153  }
154 
155  // Clear the error flag for this steam
156  clearerr( stream );
157 
158  s = fgets( buf, size, stream );
159 
160  if ( s == NULL && ferror( stream ) )
161  {
162  // The read resulted in an error
163  plabort( "Error reading from file" );
164  }
165 }
166 
167 // Get list of possible directories for creating temporary files.
168 // This list of directories is as follows:
169 // The directory names in the environment variables:
170 // "TMP" [Windows only]
171 // "TEMP" [Windows only]
172 // "TMPDIR" [Unix only]
173 // The value of the P_tmpdir macro as defined in <stdio.h> [Unix only]
174 // The current working directory
175 // Some specific hard-coded locations that are guessed for the
176 // Unix and Windows cases.
177 
178 static PLINT
179 get_tmpdir_list( PLINT max_ntmpdir_list, char ** tmpdir_list, PLINT maxcurrdir, char * currdir )
180 {
181  PLINT ntmpdir_list = 0;
182 #if defined ( MSDOS ) || defined ( _WIN32 )
183  tmpdir_list[ntmpdir_list] = getenv( "TMP" );
184  if ( tmpdir_list[ntmpdir_list] != NULL && ntmpdir_list < max_ntmpdir_list )
185  ntmpdir_list++;
186 
187  tmpdir_list[ntmpdir_list] = getenv( "TEMP" );
188  if ( tmpdir_list[ntmpdir_list] != NULL && ntmpdir_list < max_ntmpdir_list )
189  ntmpdir_list++;
190 #else
191  tmpdir_list[ntmpdir_list] = getenv( "TMPDIR" );
192  if ( tmpdir_list[ntmpdir_list] != NULL && ntmpdir_list < max_ntmpdir_list )
193  ntmpdir_list++;
194 
195 // The P_tmpdir macro is defined in stdio.h on many UNIX systems - try that
196 #ifdef P_tmpdir
197  tmpdir_list[ntmpdir_list] = P_tmpdir;
198  if ( tmpdir_list[ntmpdir_list] != NULL && ntmpdir_list < max_ntmpdir_list )
199  ntmpdir_list++;
200 #endif
201 #endif //#if defined ( MSDOS ) || defined ( _WIN32 )
202 
203 // Add current working directory to list where the currdir char array
204 // space should be provided by the calling routine with size of maxcurrdir.
205  if ( getcwd( currdir, maxcurrdir ) == NULL )
206  plexit( "get_tmpdir_list: getcwd error" );
207  tmpdir_list[ntmpdir_list] = currdir;
208  if ( ntmpdir_list < max_ntmpdir_list )
209  ntmpdir_list++;
210 
211 #if defined ( MSDOS ) || defined ( _WIN32 )
212  tmpdir_list[ntmpdir_list] = "c:\\windows\\Temp";
213 #else
214  tmpdir_list[ntmpdir_list] = "/tmp";
215 #endif
216  if ( ntmpdir_list < max_ntmpdir_list )
217  ntmpdir_list++;
218  return ntmpdir_list;
219 }
220 
221 //
222 // pl_create_tempfile()
223 //
224 // Securely create a temporary file and return a file handle to it.
225 // This provides cross-platform compatibility and also adds some
226 // additional functionality over mkstemp in that it uses the
227 // potential temporary directory locations specified in the
228 // get_tmpdir_list function.
229 //
230 // The function returns the file handle.
231 //
232 // If the fname variable is not NULL, then on return it will contain
233 // a pointer to the full temporary file name. This will be allocated
234 // with malloc. It is the caller's responsibility to ensure this
235 // memory is free'd and to ensure the file is deleted after use.
236 // If fname is NULL then the file will be automatically deleted
237 // when it is closed.
238 //
239 FILE *
240 pl_create_tempfile( char **fname )
241 {
242  FILE *fd;
243  char *tmpdir;
244  char *template = NULL;
245  PLCHAR_VECTOR tmpname = "plplot_XXXXXX";
246 #ifndef PL_HAVE_MKSTEMP
247  int flags;
248 #endif
249  PLINT ntmpdir_list;
250 #define PL_MAXNTMPDIR_LIST 5
251  char * tmpdir_list[PL_MAXNTMPDIR_LIST];
252  char currdir[PLPLOT_MAX_PATH];
253  int itmpdir;
254 
255  ntmpdir_list = get_tmpdir_list( PL_MAXNTMPDIR_LIST, tmpdir_list, PLPLOT_MAX_PATH, currdir );
256  for ( itmpdir = 0; itmpdir < ntmpdir_list; itmpdir++ )
257  {
258  // Only guarantee is that tmpdir is not NULL and points to a NULL-terminated string.
259  tmpdir = tmpdir_list[itmpdir];
260  pldebug( "pl_create_tempfile", "Attempting to create temporary file in %s directory\n", tmpdir );
261 
262  // N.B. realloc ensures template is long enough so strcpy and strcat
263  // are safe here.
264  template = (char *) realloc( template, sizeof ( char ) * ( strlen( tmpdir ) + strlen( tmpname ) + 2 ) );
265  strcpy( template, tmpdir );
266 #if defined ( MSDOS ) || defined ( _WIN32 )
267  strcat( template, "\\" );
268 #else
269  strcat( template, "/" );
270 #endif
271  strcat( template, tmpname );
272 
273 #ifdef PL_HAVE_MKSTEMP
274  fd = fdopen( mkstemp( template ), "wb+" );
275 #else //#ifdef PL_HAVE_MKSTEMP
276  // This conditionally compiled code branch is only an insecure last resort
277  // if mkstemp is not available.
278 #if !defined ( _S_IREAD )
279 #define _S_IREAD 256
280 #endif
281 #if !defined ( _S_IWRITE )
282 #define _S_IWRITE 128
283 #endif
284  fd = NULL;
285  flags = O_RDWR | O_CREAT | O_EXCL;
286 #if defined ( MSDOS ) || defined ( _WIN32 )
287  // These are flags that are only relevant to Windows open.
288  flags = flags | O_BINARY | _O_SHORT_LIVED;
289  // If we are not returning the file name then add (Windows) flag to automatically
290  // delete file once all file handles are closed.
291  if ( fname == NULL )
292  flags = flags | _O_TEMPORARY;
293 #endif
294  mktemp( template );
295  fd = fdopen( open( template, flags, _S_IREAD | _S_IWRITE ), "wb+" );
296 #endif // #ifdef PL_HAVE_MKSTEMP
297  if ( fd == NULL )
298  continue;
299  else
300  break;
301  }
302  if ( fd == NULL )
303  {
304  plwarn( "pl_create_tempfile: Unable to open temporary file - returning" );
305  if ( fname != NULL )
306  *fname = NULL;
307  free( template );
308  return NULL;
309  }
310 #if defined ( PL_HAVE_MKSTEMP ) && defined ( PL_HAVE_UNLINK )
311  // If we are not returning the file name then unlink the file so it is
312  // automatically deleted.
313  if ( fname == NULL )
314  unlink( template );
315 #endif
316  if ( fname != NULL )
317  {
318  *fname = template;
319  }
320  else
321  {
322  free( template );
323  }
324 
325  return fd;
326 }
327 
328 //
329 // pl_create_tempfifo()
330 //
331 // Securely create a temporary fifo and return the file name.
332 // This only works on POSIX compliant platforms at the moment.
333 // It creates a secure directory first using mkdtemp, then
334 // creates the named fifo in this directory. The combination of
335 // a private directory and mkfifo failing if the file name already exists
336 // makes this secure against race conditions / DoS attacks. This function
337 // includes additional functionality over mkdtemp in that it uses the
338 // potential temporary directory locations specified in the
339 // get_tmpdir_list function.
340 //
341 // The function returns the file name of the fifo.
342 //
343 char *
344 pl_create_tempfifo( const char **p_fifoname, const char **p_dirname )
345 {
346 #if !defined PL_HAVE_MKDTEMP || !defined PL_HAVE_MKFIFO
347  plwarn( "Creating fifos not supported on this platform" );
348  return NULL;
349 #else
350  char *tmpdir;
351  char *template = NULL;
352  char *dirname = NULL;
353  PLCHAR_VECTOR tmpname = "plplot_dir_XXXXXX";
354  PLCHAR_VECTOR fifoname = "plplot_fifo";
355 
356  PLINT ntmpdir_list;
357 #define PL_MAXNTMPDIR_LIST 5
358  char * tmpdir_list[PL_MAXNTMPDIR_LIST];
359  char currdir[PLPLOT_MAX_PATH];
360  int itmpdir;
361  int mkfifo_rc;
362 
363  ntmpdir_list = get_tmpdir_list( PL_MAXNTMPDIR_LIST, tmpdir_list, PLPLOT_MAX_PATH, currdir );
364  for ( itmpdir = 0; itmpdir < ntmpdir_list; itmpdir++ )
365  {
366  // Only guarantee is that tmpdir is not NULL and points to a NULL-terminated string.
367  tmpdir = tmpdir_list[itmpdir];
368  pldebug( "pl_create_tempfifo", "Attempting to create temporary fifo in %s directory\n", tmpdir );
369 
370  // N.B. realloc ensures template is long enough so strcpy and strcat
371  // are safe here.
372  dirname = (char *) realloc( dirname, sizeof ( char ) * ( strlen( tmpdir ) + strlen( tmpname ) + 2 ) );
373  strcpy( dirname, tmpdir );
374 #if defined ( MSDOS ) || defined ( _WIN32 )
375  strcat( dirname, "\\" );
376 #else
377  strcat( dirname, "/" );
378 #endif
379  strcat( dirname, tmpname );
380  // Create the temporary directory
381  dirname = mkdtemp( dirname );
382  if ( dirname == NULL )
383  {
384  // Mark this attempt as a failure.
385  mkfifo_rc = -1;
386  // This is a failed attempt so try other possibilities.
387  continue;
388  }
389  *p_dirname = dirname;
390 
391  // Now create the fifo in the directory
392  template = (char *) realloc( template, sizeof ( char ) * ( strlen( tmpdir ) + strlen( tmpname ) + strlen( fifoname ) + 4 ) );
393  strcpy( template, dirname );
394 #if defined ( MSDOS ) || defined ( _WIN32 )
395  strcat( template, "\\" );
396 #else
397  strcat( template, "/" );
398 #endif
399  strcat( template, fifoname );
400  *p_fifoname = template;
401 
402  // Check that mkfifo succeeds safely
403  mkfifo_rc = mkfifo( template, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH );
404  if ( mkfifo_rc < 0 )
405  // This is a failed attempt so try other possibilities.
406  continue;
407  else
408  break;
409  }
410  // Check for failure of all attempts in above loop.
411  if ( mkfifo_rc < 0 )
412  {
413  plwarn( "mkfifo error" );
414  free( template );
415  *p_fifoname = NULL;
416  free( dirname );
417  *p_dirname = NULL;
418  return NULL;
419  }
420 
421  return template;
422 #endif
423 }
void plexit(PLCHAR_VECTOR errormsg)
Definition: plctrl.c:1958
FILE * pl_create_tempfile(char **fname)
Definition: plstdio.c:240
const char * PLCHAR_VECTOR
Definition: plplot.h:243
#define _S_IREAD
void plabort(PLCHAR_VECTOR errormsg)
Definition: plctrl.c:1894
#define PLPLOT_MAX_PATH
Definition: plplotP.h:446
#define _S_IWRITE
int PLINT
Definition: plplot.h:181
void plio_fwrite(void *buf, size_t size, size_t nmemb, FILE *stream)
Definition: plstdio.c:68
void plio_fgets(char *buf, int size, FILE *stream)
Definition: plstdio.c:142
char * pl_create_tempfifo(const char **p_fifoname, const char **p_dirname)
Definition: plstdio.c:344
#define dbug_enter(a)
Definition: tclMatrix.c:59
void plio_fread(void *buf, size_t size, size_t nmemb, FILE *stream)
Definition: plstdio.c:98
static char buf[200]
Definition: tclAPI.c:873
void plwarn(PLCHAR_VECTOR errormsg)
Definition: plctrl.c:1863
#define PL_MAXNTMPDIR_LIST
static PLINT get_tmpdir_list(PLINT max_ntmpdir_list, char **tmpdir_list, PLINT maxcurrdir, char *currdir)
Definition: plstdio.c:179