PLplot 5.15.0
Loading...
Searching...
No Matches
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
57static PLINT
58get_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
67void
68plio_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
97void
98plio_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
141void
142plio_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
178static PLINT
179get_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//
239FILE *
240pl_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//
343char *
344pl_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 plwarn(PLCHAR_VECTOR errormsg)
Definition plctrl.c:1863
void plexit(PLCHAR_VECTOR errormsg)
Definition plctrl.c:1958
void plabort(PLCHAR_VECTOR errormsg)
Definition plctrl.c:1894
#define PLPLOT_MAX_PATH
Definition plplotP.h:446
const char * PLCHAR_VECTOR
Definition plplot.h:243
int PLINT
Definition plplot.h:181
#define PL_MAXNTMPDIR_LIST
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
static PLINT get_tmpdir_list(PLINT max_ntmpdir_list, char **tmpdir_list, PLINT maxcurrdir, char *currdir)
Definition plstdio.c:179
void plio_fread(void *buf, size_t size, size_t nmemb, FILE *stream)
Definition plstdio.c:98
FILE * pl_create_tempfile(char **fname)
Definition plstdio.c:240
#define _S_IWRITE
#define _S_IREAD
char * pl_create_tempfifo(const char **p_fifoname, const char **p_dirname)
Definition plstdio.c:344
static char buf[200]
Definition tclAPI.c:873
#define dbug_enter(a)
Definition tclMatrix.c:59