PLplot: plstdio.c Source File

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 }
plplotP.h
plexit
void plexit(PLCHAR_VECTOR errormsg)
Definition: plctrl.c:1958
pl_create_tempfile
FILE * pl_create_tempfile(char **fname)
Definition: plstdio.c:240
PLCHAR_VECTOR
const char * PLCHAR_VECTOR
Definition: plplot.h:243
_S_IREAD
#define _S_IREAD
plabort
void plabort(PLCHAR_VECTOR errormsg)
Definition: plctrl.c:1894
PLPLOT_MAX_PATH
#define PLPLOT_MAX_PATH
Definition: plplotP.h:446
_S_IWRITE
#define _S_IWRITE
PLINT
int PLINT
Definition: plplot.h:181
plio_fwrite
void plio_fwrite(void *buf, size_t size, size_t nmemb, FILE *stream)
Definition: plstdio.c:68
plio_fgets
void plio_fgets(char *buf, int size, FILE *stream)
Definition: plstdio.c:142
pl_create_tempfifo
char * pl_create_tempfifo(const char **p_fifoname, const char **p_dirname)
Definition: plstdio.c:344
dbug_enter
#define dbug_enter(a)
Definition: tclMatrix.c:59
plio_fread
void plio_fread(void *buf, size_t size, size_t nmemb, FILE *stream)
Definition: plstdio.c:98
buf
static char buf[200]
Definition: tclAPI.c:873
plwarn
void plwarn(PLCHAR_VECTOR errormsg)
Definition: plctrl.c:1863
PL_MAXNTMPDIR_LIST
#define PL_MAXNTMPDIR_LIST
get_tmpdir_list
static PLINT get_tmpdir_list(PLINT max_ntmpdir_list, char **tmpdir_list, PLINT maxcurrdir, char *currdir)
Definition: plstdio.c:179