alpar@1: /* glpenv07.c (stream input/output) */ alpar@1: alpar@1: /*********************************************************************** alpar@1: * This code is part of GLPK (GNU Linear Programming Kit). alpar@1: * alpar@1: * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, alpar@1: * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, alpar@1: * Moscow Aviation Institute, Moscow, Russia. All rights reserved. alpar@1: * E-mail: . alpar@1: * alpar@1: * GLPK is free software: you can redistribute it and/or modify it alpar@1: * under the terms of the GNU General Public License as published by alpar@1: * the Free Software Foundation, either version 3 of the License, or alpar@1: * (at your option) any later version. alpar@1: * alpar@1: * GLPK is distributed in the hope that it will be useful, but WITHOUT alpar@1: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY alpar@1: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public alpar@1: * License for more details. alpar@1: * alpar@1: * You should have received a copy of the GNU General Public License alpar@1: * along with GLPK. If not, see . alpar@1: ***********************************************************************/ alpar@1: alpar@1: #ifdef HAVE_CONFIG_H alpar@1: #include alpar@1: #endif alpar@1: alpar@1: #include "glpenv.h" alpar@1: alpar@1: /*********************************************************************** alpar@1: * NAME alpar@1: * alpar@1: * lib_err_msg - save error message string alpar@1: * alpar@1: * SYNOPSIS alpar@1: * alpar@1: * #include "glpenv.h" alpar@1: * void lib_err_msg(const char *msg); alpar@1: * alpar@1: * DESCRIPTION alpar@1: * alpar@1: * The routine lib_err_msg saves an error message string specified by alpar@1: * the parameter msg. The message is obtained by some library routines alpar@1: * with a call to strerror(errno). */ alpar@1: alpar@1: void lib_err_msg(const char *msg) alpar@1: { ENV *env = get_env_ptr(); alpar@1: int len = strlen(msg); alpar@1: if (len >= IOERR_MSG_SIZE) alpar@1: len = IOERR_MSG_SIZE - 1; alpar@1: memcpy(env->ioerr_msg, msg, len); alpar@1: if (len > 0 && env->ioerr_msg[len-1] == '\n') len--; alpar@1: env->ioerr_msg[len] = '\0'; alpar@1: return; alpar@1: } alpar@1: alpar@1: /*********************************************************************** alpar@1: * NAME alpar@1: * alpar@1: * xerrmsg - retrieve error message string alpar@1: * alpar@1: * SYNOPSIS alpar@1: * alpar@1: * #include "glpenv.h" alpar@1: * const char *xerrmsg(void); alpar@1: * alpar@1: * RETURNS alpar@1: * alpar@1: * The routine xerrmsg returns a pointer to an error message string alpar@1: * previously set by some library routine to indicate an error. */ alpar@1: alpar@1: const char *xerrmsg(void) alpar@1: { ENV *env = get_env_ptr(); alpar@1: return env->ioerr_msg; alpar@1: } alpar@1: alpar@1: /*********************************************************************** alpar@1: * NAME alpar@1: * alpar@1: * xfopen - open a stream alpar@1: * alpar@1: * SYNOPSIS alpar@1: * alpar@1: * #include "glpenv.h" alpar@1: * XFILE *xfopen(const char *fname, const char *mode); alpar@1: * alpar@1: * DESCRIPTION alpar@1: * alpar@1: * The routine xfopen opens the file whose name is a string pointed to alpar@1: * by fname and associates a stream with it. alpar@1: * alpar@1: * The parameter mode points to a string, which indicates the open mode alpar@1: * and should be one of the following: alpar@1: * alpar@1: * "r" open text file for reading; alpar@1: * "w" truncate to zero length or create text file for writing; alpar@1: * "rb" open binary file for reading; alpar@1: * "wb" truncate to zero length or create binary file for writing. alpar@1: * alpar@1: * RETURNS alpar@1: * alpar@1: * The routine xfopen returns a pointer to the object controlling the alpar@1: * stream. If the open operation fails, xfopen returns NULL. */ alpar@1: alpar@1: static void *c_fopen(const char *fname, const char *mode); alpar@1: static void *z_fopen(const char *fname, const char *mode); alpar@1: alpar@1: static int is_gz_file(const char *fname) alpar@1: { char *ext = strrchr(fname, '.'); alpar@1: return ext != NULL && strcmp(ext, ".gz") == 0; alpar@1: } alpar@1: alpar@1: XFILE *xfopen(const char *fname, const char *mode) alpar@1: { ENV *env = get_env_ptr(); alpar@1: XFILE *fp; alpar@1: int type; alpar@1: void *fh; alpar@1: if (!is_gz_file(fname)) alpar@1: { type = FH_FILE; alpar@1: fh = c_fopen(fname, mode); alpar@1: } alpar@1: else alpar@1: { type = FH_ZLIB; alpar@1: fh = z_fopen(fname, mode); alpar@1: } alpar@1: if (fh == NULL) alpar@1: { fp = NULL; alpar@1: goto done; alpar@1: } alpar@1: fp = xmalloc(sizeof(XFILE)); alpar@1: fp->type = type; alpar@1: fp->fh = fh; alpar@1: fp->prev = NULL; alpar@1: fp->next = env->file_ptr; alpar@1: if (fp->next != NULL) fp->next->prev = fp; alpar@1: env->file_ptr = fp; alpar@1: done: return fp; alpar@1: } alpar@1: alpar@1: /*********************************************************************** alpar@1: * NAME alpar@1: * alpar@1: * xfgetc - read character from the stream alpar@1: * alpar@1: * SYNOPSIS alpar@1: * alpar@1: * #include "glpenv.h" alpar@1: * int xfgetc(XFILE *fp); alpar@1: * alpar@1: * DESCRIPTION alpar@1: * alpar@1: * If the end-of-file indicator for the input stream pointed to by fp alpar@1: * is not set and a next character is present, the routine xfgetc alpar@1: * obtains that character as an unsigned char converted to an int and alpar@1: * advances the associated file position indicator for the stream (if alpar@1: * defined). alpar@1: * alpar@1: * RETURNS alpar@1: * alpar@1: * If the end-of-file indicator for the stream is set, or if the alpar@1: * stream is at end-of-file, the end-of-file indicator for the stream alpar@1: * is set and the routine xfgetc returns XEOF. Otherwise, the routine alpar@1: * xfgetc returns the next character from the input stream pointed to alpar@1: * by fp. If a read error occurs, the error indicator for the stream is alpar@1: * set and the xfgetc routine returns XEOF. alpar@1: * alpar@1: * Note: An end-of-file and a read error can be distinguished by use of alpar@1: * the routines xfeof and xferror. */ alpar@1: alpar@1: static int c_fgetc(void *fh); alpar@1: static int z_fgetc(void *fh); alpar@1: alpar@1: int xfgetc(XFILE *fp) alpar@1: { int c; alpar@1: switch (fp->type) alpar@1: { case FH_FILE: alpar@1: c = c_fgetc(fp->fh); alpar@1: break; alpar@1: case FH_ZLIB: alpar@1: c = z_fgetc(fp->fh); alpar@1: break; alpar@1: default: alpar@1: xassert(fp != fp); alpar@1: } alpar@1: return c; alpar@1: } alpar@1: alpar@1: /*********************************************************************** alpar@1: * NAME alpar@1: * alpar@1: * xfputc - write character to the stream alpar@1: * alpar@1: * SYNOPSIS alpar@1: * alpar@1: * #include "glpenv.h" alpar@1: * int xfputc(int c, XFILE *fp); alpar@1: * alpar@1: * DESCRIPTION alpar@1: * alpar@1: * The routine xfputc writes the character specified by c (converted alpar@1: * to an unsigned char) to the output stream pointed to by fp, at the alpar@1: * position indicated by the associated file position indicator (if alpar@1: * defined), and advances the indicator appropriately. alpar@1: * alpar@1: * RETURNS alpar@1: * alpar@1: * The routine xfputc returns the character written. If a write error alpar@1: * occurs, the error indicator for the stream is set and xfputc returns alpar@1: * XEOF. */ alpar@1: alpar@1: static int c_fputc(int c, void *fh); alpar@1: static int z_fputc(int c, void *fh); alpar@1: alpar@1: int xfputc(int c, XFILE *fp) alpar@1: { switch (fp->type) alpar@1: { case FH_FILE: alpar@1: c = c_fputc(c, fp->fh); alpar@1: break; alpar@1: case FH_ZLIB: alpar@1: c = z_fputc(c, fp->fh); alpar@1: break; alpar@1: default: alpar@1: xassert(fp != fp); alpar@1: } alpar@1: return c; alpar@1: } alpar@1: alpar@1: /*********************************************************************** alpar@1: * NAME alpar@1: * alpar@1: * xferror - test error indicator for the stream alpar@1: * alpar@1: * SYNOPSIS alpar@1: * alpar@1: * #include "glpenv.h" alpar@1: * int xferror(XFILE *fp); alpar@1: * alpar@1: * DESCRIPTION alpar@1: * alpar@1: * The routine xferror tests the error indicator for the stream alpar@1: * pointed to by fp. alpar@1: * alpar@1: * RETURNS alpar@1: * alpar@1: * The routine xferror returns non-zero if and only if the error alpar@1: * indicator is set for the stream. */ alpar@1: alpar@1: static int c_ferror(void *fh); alpar@1: static int z_ferror(void *fh); alpar@1: alpar@1: int xferror(XFILE *fp) alpar@1: { int ret; alpar@1: switch (fp->type) alpar@1: { case FH_FILE: alpar@1: ret = c_ferror(fp->fh); alpar@1: break; alpar@1: case FH_ZLIB: alpar@1: ret = z_ferror(fp->fh); alpar@1: break; alpar@1: default: alpar@1: xassert(fp != fp); alpar@1: } alpar@1: return ret; alpar@1: } alpar@1: alpar@1: /*********************************************************************** alpar@1: * NAME alpar@1: * alpar@1: * xfeof - test end-of-file indicator for the stream alpar@1: * alpar@1: * SYNOPSIS alpar@1: * alpar@1: * #include "glpenv.h" alpar@1: * int xfeof(XFILE *fp); alpar@1: * alpar@1: * DESCRIPTION alpar@1: * alpar@1: * The routine xfeof tests the end-of-file indicator for the stream alpar@1: * pointed to by fp. alpar@1: * alpar@1: * RETURNS alpar@1: * alpar@1: * The routine xfeof returns non-zero if and only if the end-of-file alpar@1: * indicator is set for the stream. */ alpar@1: alpar@1: static int c_feof(void *fh); alpar@1: static int z_feof(void *fh); alpar@1: alpar@1: int xfeof(XFILE *fp) alpar@1: { int ret; alpar@1: switch (fp->type) alpar@1: { case FH_FILE: alpar@1: ret = c_feof(fp->fh); alpar@1: break; alpar@1: case FH_ZLIB: alpar@1: ret = z_feof(fp->fh); alpar@1: break; alpar@1: default: alpar@1: xassert(fp != fp); alpar@1: } alpar@1: return ret; alpar@1: } alpar@1: alpar@1: int xfprintf(XFILE *file, const char *fmt, ...) alpar@1: { ENV *env = get_env_ptr(); alpar@1: int cnt, j; alpar@1: va_list arg; alpar@1: va_start(arg, fmt); alpar@1: cnt = vsprintf(env->term_buf, fmt, arg); alpar@1: va_end(arg); alpar@1: for (j = 0; j < cnt; j++) alpar@1: { if (xfputc(env->term_buf[j], file) < 0) alpar@1: { cnt = -1; alpar@1: break; alpar@1: } alpar@1: } alpar@1: return cnt; alpar@1: } alpar@1: alpar@1: /*********************************************************************** alpar@1: * NAME alpar@1: * alpar@1: * xfflush - flush the stream alpar@1: * alpar@1: * SYNOPSIS alpar@1: * alpar@1: * #include "glpenv.h" alpar@1: * int xfflush(XFILE *fp); alpar@1: * alpar@1: * DESCRIPTION alpar@1: * alpar@1: * The routine xfflush causes any unwritten data for the output stream alpar@1: * pointed to by fp to be written to the associated file. alpar@1: * alpar@1: * RETURNS alpar@1: * alpar@1: * The routine xfflush returns zero if the stream was successfully alpar@1: * flushed. Otherwise, xfflush sets the error indicator for the stream alpar@1: * and returns XEOF. */ alpar@1: alpar@1: static int c_fflush(void *fh); alpar@1: static int z_fflush(void *fh); alpar@1: alpar@1: int xfflush(XFILE *fp) alpar@1: { int ret; alpar@1: switch (fp->type) alpar@1: { case FH_FILE: alpar@1: ret = c_fflush(fp->fh); alpar@1: break; alpar@1: case FH_ZLIB: alpar@1: ret = z_fflush(fp->fh); alpar@1: break; alpar@1: default: alpar@1: xassert(fp != fp); alpar@1: } alpar@1: return ret; alpar@1: } alpar@1: alpar@1: /*********************************************************************** alpar@1: * NAME alpar@1: * alpar@1: * xfclose - close the stream alpar@1: * alpar@1: * SYNOPSIS alpar@1: * alpar@1: * #include "glpenv.h" alpar@1: * int xfclose(XFILE *fp); alpar@1: * alpar@1: * DESCRIPTION alpar@1: * alpar@1: * A successful call to the routine xfclose causes the stream pointed alpar@1: * to by fp to be flushed and the associated file to be closed. Whether alpar@1: * or not the call succeeds, the stream is disassociated from the file. alpar@1: * alpar@1: * RETURNS alpar@1: * alpar@1: * The routine xfclose returns zero if the stream was successfully alpar@1: * closed, or XEOF if any errors were detected. */ alpar@1: alpar@1: static int c_fclose(void *fh); alpar@1: static int z_fclose(void *fh); alpar@1: alpar@1: int xfclose(XFILE *fp) alpar@1: { ENV *env = get_env_ptr(); alpar@1: int ret; alpar@1: switch (fp->type) alpar@1: { case FH_FILE: alpar@1: ret = c_fclose(fp->fh); alpar@1: break; alpar@1: case FH_ZLIB: alpar@1: ret = z_fclose(fp->fh); alpar@1: break; alpar@1: default: alpar@1: xassert(fp != fp); alpar@1: } alpar@1: fp->type = 0xF00BAD; alpar@1: if (fp->prev == NULL) alpar@1: env->file_ptr = fp->next; alpar@1: else alpar@1: fp->prev->next = fp->next; alpar@1: if (fp->next == NULL) alpar@1: ; alpar@1: else alpar@1: fp->next->prev = fp->prev; alpar@1: xfree(fp); alpar@1: return ret; alpar@1: } alpar@1: alpar@1: /*********************************************************************** alpar@1: * The following routines implement stream input/output based on the alpar@1: * standard C streams. */ alpar@1: alpar@1: static void *c_fopen(const char *fname, const char *mode) alpar@1: { FILE *fh; alpar@1: if (strcmp(fname, "/dev/stdin") == 0) alpar@1: fh = stdin; alpar@1: else if (strcmp(fname, "/dev/stdout") == 0) alpar@1: fh = stdout; alpar@1: else if (strcmp(fname, "/dev/stderr") == 0) alpar@1: fh = stderr; alpar@1: else alpar@1: fh = fopen(fname, mode); alpar@1: if (fh == NULL) alpar@1: lib_err_msg(strerror(errno)); alpar@1: return fh; alpar@1: } alpar@1: alpar@1: static int c_fgetc(void *_fh) alpar@1: { FILE *fh = _fh; alpar@1: int c; alpar@1: if (ferror(fh) || feof(fh)) alpar@1: { c = XEOF; alpar@1: goto done; alpar@1: } alpar@1: c = fgetc(fh); alpar@1: if (ferror(fh)) alpar@1: { lib_err_msg(strerror(errno)); alpar@1: c = XEOF; alpar@1: } alpar@1: else if (feof(fh)) alpar@1: c = XEOF; alpar@1: else alpar@1: xassert(0x00 <= c && c <= 0xFF); alpar@1: done: return c; alpar@1: } alpar@1: alpar@1: static int c_fputc(int c, void *_fh) alpar@1: { FILE *fh = _fh; alpar@1: if (ferror(fh)) alpar@1: { c = XEOF; alpar@1: goto done; alpar@1: } alpar@1: c = (unsigned char)c; alpar@1: fputc(c, fh); alpar@1: if (ferror(fh)) alpar@1: { lib_err_msg(strerror(errno)); alpar@1: c = XEOF; alpar@1: } alpar@1: done: return c; alpar@1: } alpar@1: alpar@1: static int c_ferror(void *_fh) alpar@1: { FILE *fh = _fh; alpar@1: return ferror(fh); alpar@1: } alpar@1: alpar@1: static int c_feof(void *_fh) alpar@1: { FILE *fh = _fh; alpar@1: return feof(fh); alpar@1: } alpar@1: alpar@1: static int c_fflush(void *_fh) alpar@1: { FILE *fh = _fh; alpar@1: int ret; alpar@1: ret = fflush(fh); alpar@1: if (ret != 0) alpar@1: { lib_err_msg(strerror(errno)); alpar@1: ret = XEOF; alpar@1: } alpar@1: return ret; alpar@1: } alpar@1: alpar@1: static int c_fclose(void *_fh) alpar@1: { FILE *fh = _fh; alpar@1: int ret; alpar@1: if (fh == stdin) alpar@1: ret = 0; alpar@1: else if (fh == stdout || fh == stderr) alpar@1: fflush(fh), ret = 0; alpar@1: else alpar@1: ret = fclose(fh); alpar@1: if (ret != 0) alpar@1: { lib_err_msg(strerror(errno)); alpar@1: ret = XEOF; alpar@1: } alpar@1: return ret; alpar@1: } alpar@1: alpar@1: /*********************************************************************** alpar@1: * The following routines implement stream input/output based on the alpar@1: * zlib library, which provides processing .gz files "on the fly". */ alpar@1: alpar@1: #ifndef HAVE_ZLIB alpar@1: alpar@1: static void *z_fopen(const char *fname, const char *mode) alpar@1: { xassert(fname == fname); alpar@1: xassert(mode == mode); alpar@1: lib_err_msg("Compressed files not supported"); alpar@1: return NULL; alpar@1: } alpar@1: alpar@1: static int z_fgetc(void *fh) alpar@1: { xassert(fh != fh); alpar@1: return 0; alpar@1: } alpar@1: alpar@1: static int z_fputc(int c, void *fh) alpar@1: { xassert(c != c); alpar@1: xassert(fh != fh); alpar@1: return 0; alpar@1: } alpar@1: alpar@1: static int z_ferror(void *fh) alpar@1: { xassert(fh != fh); alpar@1: return 0; alpar@1: } alpar@1: alpar@1: static int z_feof(void *fh) alpar@1: { xassert(fh != fh); alpar@1: return 0; alpar@1: } alpar@1: alpar@1: static int z_fflush(void *fh) alpar@1: { xassert(fh != fh); alpar@1: return 0; alpar@1: } alpar@1: alpar@1: static int z_fclose(void *fh) alpar@1: { xassert(fh != fh); alpar@1: return 0; alpar@1: } alpar@1: alpar@1: #else alpar@1: alpar@1: #include alpar@1: alpar@1: struct z_file alpar@1: { /* .gz file handle */ alpar@1: gzFile file; alpar@1: /* pointer to .gz stream */ alpar@1: int err; alpar@1: /* i/o error indicator */ alpar@1: int eof; alpar@1: /* end-of-file indicator */ alpar@1: }; alpar@1: alpar@1: static void *z_fopen(const char *fname, const char *mode) alpar@1: { struct z_file *fh; alpar@1: gzFile file; alpar@1: if (strcmp(mode, "r") == 0 || strcmp(mode, "rb") == 0) alpar@1: mode = "rb"; alpar@1: else if (strcmp(mode, "w") == 0 || strcmp(mode, "wb") == 0) alpar@1: mode = "wb"; alpar@1: else alpar@1: { lib_err_msg("Invalid open mode"); alpar@1: fh = NULL; alpar@1: goto done; alpar@1: } alpar@1: file = gzopen(fname, mode); alpar@1: if (file == NULL) alpar@1: { lib_err_msg(strerror(errno)); alpar@1: fh = NULL; alpar@1: goto done; alpar@1: } alpar@1: fh = xmalloc(sizeof(struct z_file)); alpar@1: fh->file = file; alpar@1: fh->err = fh->eof = 0; alpar@1: done: return fh; alpar@1: } alpar@1: alpar@1: static int z_fgetc(void *_fh) alpar@1: { struct z_file *fh = _fh; alpar@1: int c; alpar@1: if (fh->err || fh->eof) alpar@1: { c = XEOF; alpar@1: goto done; alpar@1: } alpar@1: c = gzgetc(fh->file); alpar@1: if (c < 0) alpar@1: { int errnum; alpar@1: const char *msg; alpar@1: msg = gzerror(fh->file, &errnum); alpar@1: if (errnum == Z_STREAM_END) alpar@1: fh->eof = 1; alpar@1: else if (errnum == Z_ERRNO) alpar@1: { fh->err = 1; alpar@1: lib_err_msg(strerror(errno)); alpar@1: } alpar@1: else alpar@1: { fh->err = 1; alpar@1: lib_err_msg(msg); alpar@1: } alpar@1: c = XEOF; alpar@1: } alpar@1: else alpar@1: xassert(0x00 <= c && c <= 0xFF); alpar@1: done: return c; alpar@1: } alpar@1: alpar@1: static int z_fputc(int c, void *_fh) alpar@1: { struct z_file *fh = _fh; alpar@1: if (fh->err) alpar@1: { c = XEOF; alpar@1: goto done; alpar@1: } alpar@1: c = (unsigned char)c; alpar@1: if (gzputc(fh->file, c) < 0) alpar@1: { int errnum; alpar@1: const char *msg; alpar@1: fh->err = 1; alpar@1: msg = gzerror(fh->file, &errnum); alpar@1: if (errnum == Z_ERRNO) alpar@1: lib_err_msg(strerror(errno)); alpar@1: else alpar@1: lib_err_msg(msg); alpar@1: c = XEOF; alpar@1: } alpar@1: done: return c; alpar@1: } alpar@1: alpar@1: static int z_ferror(void *_fh) alpar@1: { struct z_file *fh = _fh; alpar@1: return fh->err; alpar@1: } alpar@1: alpar@1: static int z_feof(void *_fh) alpar@1: { struct z_file *fh = _fh; alpar@1: return fh->eof; alpar@1: } alpar@1: alpar@1: static int z_fflush(void *_fh) alpar@1: { struct z_file *fh = _fh; alpar@1: int ret; alpar@1: ret = gzflush(fh->file, Z_FINISH); alpar@1: if (ret == Z_OK) alpar@1: ret = 0; alpar@1: else alpar@1: { int errnum; alpar@1: const char *msg; alpar@1: fh->err = 1; alpar@1: msg = gzerror(fh->file, &errnum); alpar@1: if (errnum == Z_ERRNO) alpar@1: lib_err_msg(strerror(errno)); alpar@1: else alpar@1: lib_err_msg(msg); alpar@1: ret = XEOF; alpar@1: } alpar@1: return ret; alpar@1: } alpar@1: alpar@1: static int z_fclose(void *_fh) alpar@1: { struct z_file *fh = _fh; alpar@1: gzclose(fh->file); alpar@1: xfree(fh); alpar@1: return 0; alpar@1: } alpar@1: alpar@1: #endif alpar@1: alpar@1: /* eof */