glpenv07.c 16 KB

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