|
1 /* glpenv07.c (stream input/output) */ |
|
2 |
|
3 /*********************************************************************** |
|
4 * This code is part of GLPK (GNU Linear Programming Kit). |
|
5 * |
|
6 * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, |
|
7 * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, |
|
8 * Moscow Aviation Institute, Moscow, Russia. All rights reserved. |
|
9 * E-mail: <mao@gnu.org>. |
|
10 * |
|
11 * GLPK is free software: you can redistribute it and/or modify it |
|
12 * under the terms of the GNU General Public License as published by |
|
13 * the Free Software Foundation, either version 3 of the License, or |
|
14 * (at your option) any later version. |
|
15 * |
|
16 * GLPK is distributed in the hope that it will be useful, but WITHOUT |
|
17 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
|
18 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public |
|
19 * License for more details. |
|
20 * |
|
21 * You should have received a copy of the GNU General Public License |
|
22 * along with GLPK. If not, see <http://www.gnu.org/licenses/>. |
|
23 ***********************************************************************/ |
|
24 |
|
25 #ifdef HAVE_CONFIG_H |
|
26 #include <config.h> |
|
27 #endif |
|
28 |
|
29 #include "glpenv.h" |
|
30 |
|
31 /*********************************************************************** |
|
32 * NAME |
|
33 * |
|
34 * lib_err_msg - save error message string |
|
35 * |
|
36 * SYNOPSIS |
|
37 * |
|
38 * #include "glpenv.h" |
|
39 * void lib_err_msg(const char *msg); |
|
40 * |
|
41 * DESCRIPTION |
|
42 * |
|
43 * The routine lib_err_msg saves an error message string specified by |
|
44 * the parameter msg. The message is obtained by some library routines |
|
45 * with a call to strerror(errno). */ |
|
46 |
|
47 void lib_err_msg(const char *msg) |
|
48 { ENV *env = get_env_ptr(); |
|
49 int len = strlen(msg); |
|
50 if (len >= IOERR_MSG_SIZE) |
|
51 len = IOERR_MSG_SIZE - 1; |
|
52 memcpy(env->ioerr_msg, msg, len); |
|
53 if (len > 0 && env->ioerr_msg[len-1] == '\n') len--; |
|
54 env->ioerr_msg[len] = '\0'; |
|
55 return; |
|
56 } |
|
57 |
|
58 /*********************************************************************** |
|
59 * NAME |
|
60 * |
|
61 * xerrmsg - retrieve error message string |
|
62 * |
|
63 * SYNOPSIS |
|
64 * |
|
65 * #include "glpenv.h" |
|
66 * const char *xerrmsg(void); |
|
67 * |
|
68 * RETURNS |
|
69 * |
|
70 * The routine xerrmsg returns a pointer to an error message string |
|
71 * previously set by some library routine to indicate an error. */ |
|
72 |
|
73 const char *xerrmsg(void) |
|
74 { ENV *env = get_env_ptr(); |
|
75 return env->ioerr_msg; |
|
76 } |
|
77 |
|
78 /*********************************************************************** |
|
79 * NAME |
|
80 * |
|
81 * xfopen - open a stream |
|
82 * |
|
83 * SYNOPSIS |
|
84 * |
|
85 * #include "glpenv.h" |
|
86 * XFILE *xfopen(const char *fname, const char *mode); |
|
87 * |
|
88 * DESCRIPTION |
|
89 * |
|
90 * The routine xfopen opens the file whose name is a string pointed to |
|
91 * by fname and associates a stream with it. |
|
92 * |
|
93 * The parameter mode points to a string, which indicates the open mode |
|
94 * and should be one of the following: |
|
95 * |
|
96 * "r" open text file for reading; |
|
97 * "w" truncate to zero length or create text file for writing; |
|
98 * "rb" open binary file for reading; |
|
99 * "wb" truncate to zero length or create binary file for writing. |
|
100 * |
|
101 * RETURNS |
|
102 * |
|
103 * The routine xfopen returns a pointer to the object controlling the |
|
104 * stream. If the open operation fails, xfopen returns NULL. */ |
|
105 |
|
106 static void *c_fopen(const char *fname, const char *mode); |
|
107 static void *z_fopen(const char *fname, const char *mode); |
|
108 |
|
109 static int is_gz_file(const char *fname) |
|
110 { char *ext = strrchr(fname, '.'); |
|
111 return ext != NULL && strcmp(ext, ".gz") == 0; |
|
112 } |
|
113 |
|
114 XFILE *xfopen(const char *fname, const char *mode) |
|
115 { ENV *env = get_env_ptr(); |
|
116 XFILE *fp; |
|
117 int type; |
|
118 void *fh; |
|
119 if (!is_gz_file(fname)) |
|
120 { type = FH_FILE; |
|
121 fh = c_fopen(fname, mode); |
|
122 } |
|
123 else |
|
124 { type = FH_ZLIB; |
|
125 fh = z_fopen(fname, mode); |
|
126 } |
|
127 if (fh == NULL) |
|
128 { fp = NULL; |
|
129 goto done; |
|
130 } |
|
131 fp = xmalloc(sizeof(XFILE)); |
|
132 fp->type = type; |
|
133 fp->fh = fh; |
|
134 fp->prev = NULL; |
|
135 fp->next = env->file_ptr; |
|
136 if (fp->next != NULL) fp->next->prev = fp; |
|
137 env->file_ptr = fp; |
|
138 done: return fp; |
|
139 } |
|
140 |
|
141 /*********************************************************************** |
|
142 * NAME |
|
143 * |
|
144 * xfgetc - read character from the stream |
|
145 * |
|
146 * SYNOPSIS |
|
147 * |
|
148 * #include "glpenv.h" |
|
149 * int xfgetc(XFILE *fp); |
|
150 * |
|
151 * DESCRIPTION |
|
152 * |
|
153 * If the end-of-file indicator for the input stream pointed to by fp |
|
154 * is not set and a next character is present, the routine xfgetc |
|
155 * obtains that character as an unsigned char converted to an int and |
|
156 * advances the associated file position indicator for the stream (if |
|
157 * defined). |
|
158 * |
|
159 * RETURNS |
|
160 * |
|
161 * If the end-of-file indicator for the stream is set, or if the |
|
162 * stream is at end-of-file, the end-of-file indicator for the stream |
|
163 * is set and the routine xfgetc returns XEOF. Otherwise, the routine |
|
164 * xfgetc returns the next character from the input stream pointed to |
|
165 * by fp. If a read error occurs, the error indicator for the stream is |
|
166 * set and the xfgetc routine returns XEOF. |
|
167 * |
|
168 * Note: An end-of-file and a read error can be distinguished by use of |
|
169 * the routines xfeof and xferror. */ |
|
170 |
|
171 static int c_fgetc(void *fh); |
|
172 static int z_fgetc(void *fh); |
|
173 |
|
174 int xfgetc(XFILE *fp) |
|
175 { int c; |
|
176 switch (fp->type) |
|
177 { case FH_FILE: |
|
178 c = c_fgetc(fp->fh); |
|
179 break; |
|
180 case FH_ZLIB: |
|
181 c = z_fgetc(fp->fh); |
|
182 break; |
|
183 default: |
|
184 xassert(fp != fp); |
|
185 } |
|
186 return c; |
|
187 } |
|
188 |
|
189 /*********************************************************************** |
|
190 * NAME |
|
191 * |
|
192 * xfputc - write character to the stream |
|
193 * |
|
194 * SYNOPSIS |
|
195 * |
|
196 * #include "glpenv.h" |
|
197 * int xfputc(int c, XFILE *fp); |
|
198 * |
|
199 * DESCRIPTION |
|
200 * |
|
201 * The routine xfputc writes the character specified by c (converted |
|
202 * to an unsigned char) to the output stream pointed to by fp, at the |
|
203 * position indicated by the associated file position indicator (if |
|
204 * defined), and advances the indicator appropriately. |
|
205 * |
|
206 * RETURNS |
|
207 * |
|
208 * The routine xfputc returns the character written. If a write error |
|
209 * occurs, the error indicator for the stream is set and xfputc returns |
|
210 * XEOF. */ |
|
211 |
|
212 static int c_fputc(int c, void *fh); |
|
213 static int z_fputc(int c, void *fh); |
|
214 |
|
215 int xfputc(int c, XFILE *fp) |
|
216 { switch (fp->type) |
|
217 { case FH_FILE: |
|
218 c = c_fputc(c, fp->fh); |
|
219 break; |
|
220 case FH_ZLIB: |
|
221 c = z_fputc(c, fp->fh); |
|
222 break; |
|
223 default: |
|
224 xassert(fp != fp); |
|
225 } |
|
226 return c; |
|
227 } |
|
228 |
|
229 /*********************************************************************** |
|
230 * NAME |
|
231 * |
|
232 * xferror - test error indicator for the stream |
|
233 * |
|
234 * SYNOPSIS |
|
235 * |
|
236 * #include "glpenv.h" |
|
237 * int xferror(XFILE *fp); |
|
238 * |
|
239 * DESCRIPTION |
|
240 * |
|
241 * The routine xferror tests the error indicator for the stream |
|
242 * pointed to by fp. |
|
243 * |
|
244 * RETURNS |
|
245 * |
|
246 * The routine xferror returns non-zero if and only if the error |
|
247 * indicator is set for the stream. */ |
|
248 |
|
249 static int c_ferror(void *fh); |
|
250 static int z_ferror(void *fh); |
|
251 |
|
252 int xferror(XFILE *fp) |
|
253 { int ret; |
|
254 switch (fp->type) |
|
255 { case FH_FILE: |
|
256 ret = c_ferror(fp->fh); |
|
257 break; |
|
258 case FH_ZLIB: |
|
259 ret = z_ferror(fp->fh); |
|
260 break; |
|
261 default: |
|
262 xassert(fp != fp); |
|
263 } |
|
264 return ret; |
|
265 } |
|
266 |
|
267 /*********************************************************************** |
|
268 * NAME |
|
269 * |
|
270 * xfeof - test end-of-file indicator for the stream |
|
271 * |
|
272 * SYNOPSIS |
|
273 * |
|
274 * #include "glpenv.h" |
|
275 * int xfeof(XFILE *fp); |
|
276 * |
|
277 * DESCRIPTION |
|
278 * |
|
279 * The routine xfeof tests the end-of-file indicator for the stream |
|
280 * pointed to by fp. |
|
281 * |
|
282 * RETURNS |
|
283 * |
|
284 * The routine xfeof returns non-zero if and only if the end-of-file |
|
285 * indicator is set for the stream. */ |
|
286 |
|
287 static int c_feof(void *fh); |
|
288 static int z_feof(void *fh); |
|
289 |
|
290 int xfeof(XFILE *fp) |
|
291 { int ret; |
|
292 switch (fp->type) |
|
293 { case FH_FILE: |
|
294 ret = c_feof(fp->fh); |
|
295 break; |
|
296 case FH_ZLIB: |
|
297 ret = z_feof(fp->fh); |
|
298 break; |
|
299 default: |
|
300 xassert(fp != fp); |
|
301 } |
|
302 return ret; |
|
303 } |
|
304 |
|
305 int xfprintf(XFILE *file, const char *fmt, ...) |
|
306 { ENV *env = get_env_ptr(); |
|
307 int cnt, j; |
|
308 va_list arg; |
|
309 va_start(arg, fmt); |
|
310 cnt = vsprintf(env->term_buf, fmt, arg); |
|
311 va_end(arg); |
|
312 for (j = 0; j < cnt; j++) |
|
313 { if (xfputc(env->term_buf[j], file) < 0) |
|
314 { cnt = -1; |
|
315 break; |
|
316 } |
|
317 } |
|
318 return cnt; |
|
319 } |
|
320 |
|
321 /*********************************************************************** |
|
322 * NAME |
|
323 * |
|
324 * xfflush - flush the stream |
|
325 * |
|
326 * SYNOPSIS |
|
327 * |
|
328 * #include "glpenv.h" |
|
329 * int xfflush(XFILE *fp); |
|
330 * |
|
331 * DESCRIPTION |
|
332 * |
|
333 * The routine xfflush causes any unwritten data for the output stream |
|
334 * pointed to by fp to be written to the associated file. |
|
335 * |
|
336 * RETURNS |
|
337 * |
|
338 * The routine xfflush returns zero if the stream was successfully |
|
339 * flushed. Otherwise, xfflush sets the error indicator for the stream |
|
340 * and returns XEOF. */ |
|
341 |
|
342 static int c_fflush(void *fh); |
|
343 static int z_fflush(void *fh); |
|
344 |
|
345 int xfflush(XFILE *fp) |
|
346 { int ret; |
|
347 switch (fp->type) |
|
348 { case FH_FILE: |
|
349 ret = c_fflush(fp->fh); |
|
350 break; |
|
351 case FH_ZLIB: |
|
352 ret = z_fflush(fp->fh); |
|
353 break; |
|
354 default: |
|
355 xassert(fp != fp); |
|
356 } |
|
357 return ret; |
|
358 } |
|
359 |
|
360 /*********************************************************************** |
|
361 * NAME |
|
362 * |
|
363 * xfclose - close the stream |
|
364 * |
|
365 * SYNOPSIS |
|
366 * |
|
367 * #include "glpenv.h" |
|
368 * int xfclose(XFILE *fp); |
|
369 * |
|
370 * DESCRIPTION |
|
371 * |
|
372 * A successful call to the routine xfclose causes the stream pointed |
|
373 * to by fp to be flushed and the associated file to be closed. Whether |
|
374 * or not the call succeeds, the stream is disassociated from the file. |
|
375 * |
|
376 * RETURNS |
|
377 * |
|
378 * The routine xfclose returns zero if the stream was successfully |
|
379 * closed, or XEOF if any errors were detected. */ |
|
380 |
|
381 static int c_fclose(void *fh); |
|
382 static int z_fclose(void *fh); |
|
383 |
|
384 int xfclose(XFILE *fp) |
|
385 { ENV *env = get_env_ptr(); |
|
386 int ret; |
|
387 switch (fp->type) |
|
388 { case FH_FILE: |
|
389 ret = c_fclose(fp->fh); |
|
390 break; |
|
391 case FH_ZLIB: |
|
392 ret = z_fclose(fp->fh); |
|
393 break; |
|
394 default: |
|
395 xassert(fp != fp); |
|
396 } |
|
397 fp->type = 0xF00BAD; |
|
398 if (fp->prev == NULL) |
|
399 env->file_ptr = fp->next; |
|
400 else |
|
401 fp->prev->next = fp->next; |
|
402 if (fp->next == NULL) |
|
403 ; |
|
404 else |
|
405 fp->next->prev = fp->prev; |
|
406 xfree(fp); |
|
407 return ret; |
|
408 } |
|
409 |
|
410 /*********************************************************************** |
|
411 * The following routines implement stream input/output based on the |
|
412 * standard C streams. */ |
|
413 |
|
414 static void *c_fopen(const char *fname, const char *mode) |
|
415 { FILE *fh; |
|
416 if (strcmp(fname, "/dev/stdin") == 0) |
|
417 fh = stdin; |
|
418 else if (strcmp(fname, "/dev/stdout") == 0) |
|
419 fh = stdout; |
|
420 else if (strcmp(fname, "/dev/stderr") == 0) |
|
421 fh = stderr; |
|
422 else |
|
423 fh = fopen(fname, mode); |
|
424 if (fh == NULL) |
|
425 lib_err_msg(strerror(errno)); |
|
426 return fh; |
|
427 } |
|
428 |
|
429 static int c_fgetc(void *_fh) |
|
430 { FILE *fh = _fh; |
|
431 int c; |
|
432 if (ferror(fh) || feof(fh)) |
|
433 { c = XEOF; |
|
434 goto done; |
|
435 } |
|
436 c = fgetc(fh); |
|
437 if (ferror(fh)) |
|
438 { lib_err_msg(strerror(errno)); |
|
439 c = XEOF; |
|
440 } |
|
441 else if (feof(fh)) |
|
442 c = XEOF; |
|
443 else |
|
444 xassert(0x00 <= c && c <= 0xFF); |
|
445 done: return c; |
|
446 } |
|
447 |
|
448 static int c_fputc(int c, void *_fh) |
|
449 { FILE *fh = _fh; |
|
450 if (ferror(fh)) |
|
451 { c = XEOF; |
|
452 goto done; |
|
453 } |
|
454 c = (unsigned char)c; |
|
455 fputc(c, fh); |
|
456 if (ferror(fh)) |
|
457 { lib_err_msg(strerror(errno)); |
|
458 c = XEOF; |
|
459 } |
|
460 done: return c; |
|
461 } |
|
462 |
|
463 static int c_ferror(void *_fh) |
|
464 { FILE *fh = _fh; |
|
465 return ferror(fh); |
|
466 } |
|
467 |
|
468 static int c_feof(void *_fh) |
|
469 { FILE *fh = _fh; |
|
470 return feof(fh); |
|
471 } |
|
472 |
|
473 static int c_fflush(void *_fh) |
|
474 { FILE *fh = _fh; |
|
475 int ret; |
|
476 ret = fflush(fh); |
|
477 if (ret != 0) |
|
478 { lib_err_msg(strerror(errno)); |
|
479 ret = XEOF; |
|
480 } |
|
481 return ret; |
|
482 } |
|
483 |
|
484 static int c_fclose(void *_fh) |
|
485 { FILE *fh = _fh; |
|
486 int ret; |
|
487 if (fh == stdin) |
|
488 ret = 0; |
|
489 else if (fh == stdout || fh == stderr) |
|
490 fflush(fh), ret = 0; |
|
491 else |
|
492 ret = fclose(fh); |
|
493 if (ret != 0) |
|
494 { lib_err_msg(strerror(errno)); |
|
495 ret = XEOF; |
|
496 } |
|
497 return ret; |
|
498 } |
|
499 |
|
500 /*********************************************************************** |
|
501 * The following routines implement stream input/output based on the |
|
502 * zlib library, which provides processing .gz files "on the fly". */ |
|
503 |
|
504 #ifndef HAVE_ZLIB |
|
505 |
|
506 static void *z_fopen(const char *fname, const char *mode) |
|
507 { xassert(fname == fname); |
|
508 xassert(mode == mode); |
|
509 lib_err_msg("Compressed files not supported"); |
|
510 return NULL; |
|
511 } |
|
512 |
|
513 static int z_fgetc(void *fh) |
|
514 { xassert(fh != fh); |
|
515 return 0; |
|
516 } |
|
517 |
|
518 static int z_fputc(int c, void *fh) |
|
519 { xassert(c != c); |
|
520 xassert(fh != fh); |
|
521 return 0; |
|
522 } |
|
523 |
|
524 static int z_ferror(void *fh) |
|
525 { xassert(fh != fh); |
|
526 return 0; |
|
527 } |
|
528 |
|
529 static int z_feof(void *fh) |
|
530 { xassert(fh != fh); |
|
531 return 0; |
|
532 } |
|
533 |
|
534 static int z_fflush(void *fh) |
|
535 { xassert(fh != fh); |
|
536 return 0; |
|
537 } |
|
538 |
|
539 static int z_fclose(void *fh) |
|
540 { xassert(fh != fh); |
|
541 return 0; |
|
542 } |
|
543 |
|
544 #else |
|
545 |
|
546 #include <zlib.h> |
|
547 |
|
548 struct z_file |
|
549 { /* .gz file handle */ |
|
550 gzFile file; |
|
551 /* pointer to .gz stream */ |
|
552 int err; |
|
553 /* i/o error indicator */ |
|
554 int eof; |
|
555 /* end-of-file indicator */ |
|
556 }; |
|
557 |
|
558 static void *z_fopen(const char *fname, const char *mode) |
|
559 { struct z_file *fh; |
|
560 gzFile file; |
|
561 if (strcmp(mode, "r") == 0 || strcmp(mode, "rb") == 0) |
|
562 mode = "rb"; |
|
563 else if (strcmp(mode, "w") == 0 || strcmp(mode, "wb") == 0) |
|
564 mode = "wb"; |
|
565 else |
|
566 { lib_err_msg("Invalid open mode"); |
|
567 fh = NULL; |
|
568 goto done; |
|
569 } |
|
570 file = gzopen(fname, mode); |
|
571 if (file == NULL) |
|
572 { lib_err_msg(strerror(errno)); |
|
573 fh = NULL; |
|
574 goto done; |
|
575 } |
|
576 fh = xmalloc(sizeof(struct z_file)); |
|
577 fh->file = file; |
|
578 fh->err = fh->eof = 0; |
|
579 done: return fh; |
|
580 } |
|
581 |
|
582 static int z_fgetc(void *_fh) |
|
583 { struct z_file *fh = _fh; |
|
584 int c; |
|
585 if (fh->err || fh->eof) |
|
586 { c = XEOF; |
|
587 goto done; |
|
588 } |
|
589 c = gzgetc(fh->file); |
|
590 if (c < 0) |
|
591 { int errnum; |
|
592 const char *msg; |
|
593 msg = gzerror(fh->file, &errnum); |
|
594 if (errnum == Z_STREAM_END) |
|
595 fh->eof = 1; |
|
596 else if (errnum == Z_ERRNO) |
|
597 { fh->err = 1; |
|
598 lib_err_msg(strerror(errno)); |
|
599 } |
|
600 else |
|
601 { fh->err = 1; |
|
602 lib_err_msg(msg); |
|
603 } |
|
604 c = XEOF; |
|
605 } |
|
606 else |
|
607 xassert(0x00 <= c && c <= 0xFF); |
|
608 done: return c; |
|
609 } |
|
610 |
|
611 static int z_fputc(int c, void *_fh) |
|
612 { struct z_file *fh = _fh; |
|
613 if (fh->err) |
|
614 { c = XEOF; |
|
615 goto done; |
|
616 } |
|
617 c = (unsigned char)c; |
|
618 if (gzputc(fh->file, c) < 0) |
|
619 { int errnum; |
|
620 const char *msg; |
|
621 fh->err = 1; |
|
622 msg = gzerror(fh->file, &errnum); |
|
623 if (errnum == Z_ERRNO) |
|
624 lib_err_msg(strerror(errno)); |
|
625 else |
|
626 lib_err_msg(msg); |
|
627 c = XEOF; |
|
628 } |
|
629 done: return c; |
|
630 } |
|
631 |
|
632 static int z_ferror(void *_fh) |
|
633 { struct z_file *fh = _fh; |
|
634 return fh->err; |
|
635 } |
|
636 |
|
637 static int z_feof(void *_fh) |
|
638 { struct z_file *fh = _fh; |
|
639 return fh->eof; |
|
640 } |
|
641 |
|
642 static int z_fflush(void *_fh) |
|
643 { struct z_file *fh = _fh; |
|
644 int ret; |
|
645 ret = gzflush(fh->file, Z_FINISH); |
|
646 if (ret == Z_OK) |
|
647 ret = 0; |
|
648 else |
|
649 { int errnum; |
|
650 const char *msg; |
|
651 fh->err = 1; |
|
652 msg = gzerror(fh->file, &errnum); |
|
653 if (errnum == Z_ERRNO) |
|
654 lib_err_msg(strerror(errno)); |
|
655 else |
|
656 lib_err_msg(msg); |
|
657 ret = XEOF; |
|
658 } |
|
659 return ret; |
|
660 } |
|
661 |
|
662 static int z_fclose(void *_fh) |
|
663 { struct z_file *fh = _fh; |
|
664 gzclose(fh->file); |
|
665 xfree(fh); |
|
666 return 0; |
|
667 } |
|
668 |
|
669 #endif |
|
670 |
|
671 /* eof */ |