PLplot  5.11.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
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 //
6 // This file is part of PLplot.
7 //
8 // PLplot is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU Library General Public License as published
10 // by the Free Software Foundation; either version 2 of the License, or
11 // (at your option) any later version.
12 //
13 // PLplot is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Library General Public License for more details.
17 //
18 // You should have received a copy of the GNU Library General Public License
19 // along with PLplot; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 //
22 //
23 
24 #include "plplotP.h"
25 
26 #if defined ( MSDOS ) || defined ( WIN32 )
27 #include <sys/types.h>
28 #include <fcntl.h>
29 #endif
30 
31 // This is needed for mode flags for mkfifo as well as for MSDOS / WIN32
32 #include <sys/stat.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 //
48 // plio_write()
49 //
50 // Writes the contents of buf to stream. Handles any I/O error conditions
51 // so that the caller can "fire and forget."
52 //
53 
54 void
55 plio_fwrite( void *buf, size_t size, size_t nmemb, FILE *stream )
56 {
57  dbug_enter( "plio_fwrite" );
58 
59  // Exit if there is nothing to write
60  if ( size == 0 || nmemb == 0 )
61  return;
62 
63  // Clear the error flag for this steam
64  clearerr( stream );
65 
66  fwrite( buf, size, nmemb, stream );
67 
68  if ( ferror( stream ) )
69  {
70  // Perhaps we can add a flag (global or per output stream)
71  // in order to decide if we should abort or warn. I think
72  // I/O errors should generate an abort
73  plabort( "Error writing to file" );
74  }
75 }
76 
77 //
78 // plio_fread()
79 //
80 // Read from stream into buf. Like plio_write(), this function will
81 // handle any I/O error conditions.
82 //
83 
84 void
85 plio_fread( void *buf, size_t size, size_t nmemb, FILE *stream )
86 {
87  size_t bytes;
88 
89  dbug_enter( "plio_fread" );
90 
91  // If the buffer has a size of zero, we should complain
92  if ( size == 0 || nmemb == 0 )
93  {
94  plwarn( "Zero length buffer size in plio_fread, returning" );
95  return;
96  }
97 
98  // Clear the error flag for this steam
99  clearerr( stream );
100 
101  bytes = fread( buf, size, nmemb, stream );
102 
103  if ( ( bytes < nmemb ) && ferror( stream ) )
104  {
105  // The read resulted in an error
106  plabort( "Error reading from file" );
107  }
108 }
109 
110 //
111 // plio_fgets()
112 //
113 // Read from stream into buf. This version of fgets is designed for the occasions
114 // where the caller wants to ignore the return value.
115 //
116 // NOTE: If one is reading from a file until an EOF condition, fgets() is better suited
117 // than this function, i.e.
118 //
119 // while(fgets(buf, size, fp) != NULL) { ... do some stuff ... }
120 //
121 // rather than
122 //
123 // while(!feof(fp)) { plio_fgets(buf, size, fp); ... do some stuff ... }
124 //
125 // which would require checking for an empty buffer.
126 //
127 
128 void
129 plio_fgets( char *buf, int size, FILE *stream )
130 {
131  char *s;
132 
133  dbug_enter( "plio_fgets" );
134 
135  // If the buffer has a size of zero, we should complain
136  if ( size == 0 )
137  {
138  plwarn( "Zero length buffer size in plio_fgets, returning" );
139  return;
140  }
141 
142  // Clear the error flag for this steam
143  clearerr( stream );
144 
145  s = fgets( buf, size, stream );
146 
147  if ( s == NULL && ferror( stream ) )
148  {
149  // The read resulted in an error
150  plabort( "Error reading from file" );
151  }
152 }
153 
154 //
155 // pl_create_tempfile()
156 //
157 // Securely create a temporary file and return a file handle to it.
158 // This provides cross-platform compatibility and also adds some
159 // additional functionality over mkstemp in that it honours the TMP /
160 // TMPDIR / TEMP environment variables.
161 //
162 // The function returns the file handle.
163 //
164 // If the fname variable is not NULL, then on return it will contain
165 // a pointer to the full temporary file name. This will be allocated
166 // with malloc. It is the caller's responsibility to ensure this
167 // memory is free'd and to ensure the file is deleted after use.
168 // If fname is NULL then the file will be automatically deleted
169 // when it is closed.
170 //
171 FILE *
172 pl_create_tempfile( char **fname )
173 {
174  FILE *fd;
175  const char *tmpdir;
176  char *template;
177  const char *tmpname = "plplot_XXXXXX";
178 #if !defined PL_HAVE_MKSTEMP
179  int flags;
180 #endif
181 
182 #if defined ( MSDOS ) || defined ( WIN32 )
183  tmpdir = getenv( "TEMP" );
184 #else
185  tmpdir = getenv( "TMPDIR" );
186 #endif
187 
188 // The P_TMPDIR macro is defined in stdio.h on many UNIX systems - try that
189 #ifdef P_TMPDIR
190  if ( tmpdir == NULL )
191  tmpdir = P_TMPDIR;
192 #endif
193 
194  if ( tmpdir == NULL )
195  {
196 #if defined ( MSDOS ) || defined ( WIN32 )
197  tmpdir = "c:\\windows\\Temp";
198 #else
199  tmpdir = "/tmp";
200 #endif
201  }
202 
203  // N.B. Malloc ensures template is long enough so strcpy and strcat
204  // are safe here
205  template = (char *) malloc( sizeof ( char ) * ( strlen( tmpdir ) + strlen( tmpname ) + 2 ) );
206  strcpy( template, tmpdir );
207 #if defined ( MSDOS ) || defined ( WIN32 )
208  strcat( template, "\\" );
209 #else
210  strcat( template, "/" );
211 #endif
212  strcat( template, tmpname );
213 
214 #ifdef PL_HAVE_MKSTEMP
215  fd = fdopen( mkstemp( template ), "wb+" );
216  if ( fd == NULL )
217  {
218  plwarn( "pl_create_tempfile: Unable to open temporary file - returning" );
219  if ( fname != NULL )
220  *fname = NULL;
221  free( template );
222  return NULL;
223  }
224  // If we are not returning the file name then unlink the file so it is
225  // automatically deleted.
226 #ifdef PL_HAVE_UNLINK
227  if ( fname == NULL )
228  unlink( template );
229 #endif
230 #else
231 #if !defined ( _S_IREAD )
232 #define _S_IREAD 256
233 #endif
234 #if !defined ( _S_IWRITE )
235 #define _S_IWRITE 128
236 #endif
237  fd = NULL;
238  flags = O_RDWR | O_BINARY | O_CREAT | O_EXCL | _O_SHORT_LIVED;
239  // If we are not returning the file name then add flag to automatically
240  // delete file once all file handles are closed.
241  if ( fname == NULL )
242  flags = flags | _O_TEMPORARY;
243  mktemp( template );
244  fd = fdopen( open( template, flags, _S_IREAD | _S_IWRITE ), "wb+" );
245 #endif
246 
247  if ( fname != NULL )
248  {
249  *fname = template;
250  }
251  else
252  {
253  free( template );
254  }
255 
256  return fd;
257 }
258 
259 //
260 // pl_create_tempfifo()
261 //
262 // Securely create a temporary fifo and return the file name.
263 // This only works on POSIX compliant platforms at the moment.
264 // It creates a secure directory first using mkdtemp, then
265 // creates the named fifo in this directory. The combination of
266 // a private directory and mkfifo failing if the file name already exists
267 // makes this secure against race conditions / DoS attacks. This function
268 // includes additional functionality over mkdtemp in that it honours the
269 // TMP / TMPDIR / TEMP environment variables.
270 //
271 // The function returns the file name of the fifo.
272 //
273 char *
274 pl_create_tempfifo( const char **p_fifoname, const char **p_dirname )
275 {
276 #if !defined PL_HAVE_MKDTEMP || !defined PL_HAVE_MKFIFO
277  plwarn( "Creating fifos not supported on this platform" );
278  return NULL;
279 #else
280  const char *tmpdir;
281  char *template;
282  char *dirname;
283  const char *tmpname = "plplot_dir_XXXXXX";
284  const char *fifoname = "plplot_fifo";
285 
286 #if defined ( MSDOS ) || defined ( WIN32 )
287  tmpdir = getenv( "TEMP" );
288 #else
289  tmpdir = getenv( "TMPDIR" );
290 #endif
291 
292 // The P_TMPDIR macro is defined in stdio.h on many UNIX systems - try that
293 #ifdef P_TMPDIR
294  if ( tmpdir == NULL )
295  tmpdir = P_TMPDIR;
296 #endif
297 
298  if ( tmpdir == NULL )
299  {
300 #if defined ( MSDOS ) || defined ( WIN32 )
301  tmpdir = "c:\\windows\\Temp";
302 #else
303  tmpdir = "/tmp";
304 #endif
305  }
306 
307  // N.B. Malloc ensures template is long enough so strcpy and strcat
308  // are safe here
309  dirname = (char *) malloc( sizeof ( char ) * ( strlen( tmpdir ) + strlen( tmpname ) + 2 ) );
310  strcpy( dirname, tmpdir );
311 #if defined ( MSDOS ) || defined ( WIN32 )
312  strcat( dirname, "\\" );
313 #else
314  strcat( dirname, "/" );
315 #endif
316  strcat( dirname, tmpname );
317  // Create the temporary directory
318  dirname = mkdtemp( dirname );
319  *p_dirname = dirname;
320 
321  // Now create the fifo in the directory
322  template = (char *) malloc( sizeof ( char ) * ( strlen( tmpdir ) + strlen( tmpname ) + strlen( fifoname ) + 4 ) );
323  strcpy( template, dirname );
324 #if defined ( MSDOS ) || defined ( WIN32 )
325  strcat( template, "\\" );
326 #else
327  strcat( template, "/" );
328 #endif
329  strcat( template, fifoname );
330  *p_fifoname = template;
331 
332  // Check that mkfifo succeeds safely
333  if ( mkfifo( template, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ) < 0 )
334  {
335  plwarn( "mkfifo error" );
336  free( template );
337  *p_fifoname = NULL;
338  free( dirname );
339  *p_dirname = NULL;
340  return NULL;
341  }
342 
343  return template;
344 #endif
345 }
346