This is zfile.c in view mode; [Download] [Up]
/* * zfile.c - Transparent handling of compressed files. * * Written by * Ettore Perazzoli (ettore@comm2000.it) * * BZIP v2 support added by * Andreas Boose (boose@rzgw.rz.fh-hannover.de) * * ARCHIVE, ZIPCODE and LYNX supports added by * Teemu Rantanen (tvr@cs.hut.fi) * * This file is part of VICE, the Versatile Commodore Emulator. * See README for copyright notice. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA. * */ /* This code might be improved a lot... */ #include "vice.h" #include <stdlib.h> #include <unistd.h> #include <limits.h> #include <string.h> #include <stdarg.h> #include <ctype.h> #include <errno.h> #include "utils.h" #include "zfile.h" #include "zipcode.h" /* ------------------------------------------------------------------------- */ /* #define DEBUG_ZFILE */ #ifdef DEBUG_ZFILE #define ZDEBUG(a) printf a #else #define ZDEBUG(a) #endif /* We could add more here... */ enum compression_type { COMPR_NONE, COMPR_GZIP, COMPR_BZIP, COMPR_ARCHIVE, COMPR_ZIPCODE, COMPR_LYNX }; /* This defines a linked list of all the compressed files that have been opened. */ struct zfile { char *tmp_name; /* Name of the temporary file. */ char *orig_name; /* Name of the original file. */ int write_mode; /* Non-zero if the file is open for writing. */ FILE *stream; /* Associated stdio-style stream. */ int fd; /* Associated file descriptor. */ enum compression_type type; /* Compression algorithm. */ struct zfile *prev, *next; /* Link to the previous and next nodes. */ }; struct zfile *zfile_list = NULL; /* ------------------------------------------------------------------------- */ static int zinit_done = 0; static int zinit(void) { struct zfile *p; /* Free the `zfile_list' if not empty. */ for (p = zfile_list; p != NULL; ) { struct zfile *next; free(p->orig_name); free(p->tmp_name); next = p->next; free(p); p = next; } zfile_list = NULL; zinit_done = 1; return 0; } /* Add one zfile to the list. `orig_name' is automatically expanded to the complete path. */ static void zfile_list_add(const char *tmp_name, const char *orig_name, enum compression_type type, int write_mode, FILE *stream, int fd) { struct zfile *new_zfile = (struct zfile *)xmalloc(sizeof(struct zfile)); /* Make sure we have the complete path of the file. */ #ifdef __MSDOS__ /* MS-DOS version. */ new_zfile->orig_name = _truename(orig_name, NULL); if (new_zfile->orig_name == NULL) { fprintf(stderr, "zfile_list_add: warning, illegal file name `%s'.\n", orig_name); new_zfile->orig_name = stralloc(orig_name); } #else /* Unix version. */ if (*orig_name == '/') { new_zfile->orig_name = stralloc(orig_name); } else { static char *cwd; cwd = get_current_dir(); new_zfile->orig_name = concat(cwd, "/", orig_name, NULL); free(cwd); } #endif /* The new zfile becomes first on the list. */ new_zfile->tmp_name = stralloc(tmp_name); new_zfile->write_mode = write_mode; new_zfile->stream = stream; new_zfile->fd = fd; new_zfile->type = type; new_zfile->next = zfile_list; new_zfile->prev = NULL; if (zfile_list != NULL) zfile_list->prev = new_zfile; zfile_list = new_zfile; } /* ------------------------------------------------------------------------ */ /* Uncompression. */ /* If `name' has a gzip-like extension, try to uncompress it into a temporary file using gzip. If this succeeds, return the name of the temporary file; return NULL otherwise. */ static char *try_uncompress_with_gzip(const char *name) { static char tmp_name[L_tmpnam]; int l = strlen(name); int exit_status; char *argv[4]; /* Check whether the name sounds like a gzipped file by checking the extension. The last case (3-character extensions whose last character is a `z' (or 'Z'), is the standard convention for the MS-DOS version of gzip. */ if ((l < 4 || strcasecmp(name + l - 3, ".gz") != 0) && (l < 3 || strcasecmp(name + l - 2, ".z") != 0) && (l < 4 || toupper(name[l - 1]) != 'Z' || name[l - 4] != '.')) return NULL; /* `exec*()' does not want these to be constant... */ argv[0] = stralloc("gzip"); argv[1] = stralloc("-cd"); argv[2] = stralloc(name); argv[3] = NULL; ZDEBUG(("try_uncompress_with_gzip: spawning gzip -cd %s\n", name)); tmpnam(tmp_name); exit_status = spawn("gzip", argv, tmp_name, NULL); free(argv[0]); free(argv[1]); free(argv[2]); if (exit_status == 0) { ZDEBUG(("try_uncompress_with_gzip: OK\n")); return tmp_name; } else { ZDEBUG(("try_uncompress_with_gzip: failed\n")); unlink(tmp_name); return NULL; } } /* If `name' has a bzip-like extension, try to uncompress it into a temporary file using bzip. If this succeeds, return the name of the temporary file; return NULL otherwise. */ static char *try_uncompress_with_bzip(const char *name) { static char tmp_name[L_tmpnam]; int l = strlen(name); int exit_status; char *argv[4]; /* Check whether the name sounds like a bzipped file by checking the extension. MSDOS and UNIX variants of bzip v2 use the extension '.bz2'. bzip v1 is obsolete. */ if (l < 5 || strcasecmp(name + l - 4, ".bz2") != 0) return NULL; /* `exec*()' does not want these to be constant... */ argv[0] = stralloc("bzip2"); argv[1] = stralloc("-cd"); argv[2] = stralloc(name); argv[3] = NULL; ZDEBUG(("try_uncompress_with_bzip: spawning bzip -cd %s\n", name)); tmpnam(tmp_name); exit_status = spawn("bzip2", argv, tmp_name, NULL); free(argv[0]); free(argv[1]); free(argv[2]); if (exit_status == 0) { ZDEBUG(("try_uncompress_with_bzip: OK\n")); return tmp_name; } else { ZDEBUG(("try_uncompress_with_bzip: failed\n")); unlink(tmp_name); return NULL; } } /* is the name zipcode -name? */ static int is_zipcode_name(char *name) { if (name[0] >= '1' && name[0] <= '4' && name[1] == '!') return 1; return 0; } /* Extensions we know about */ static char *extensions[] = { ".d64", ".x64", ".dsk", ".t64", ".p00", ".prg", ".lnx", NULL }; static int is_valid_extension(char *end, int l, int nameoffset) { int i, len; /* zipcode testing is a special case */ if (l > nameoffset + 2 && is_zipcode_name(end + nameoffset)) return 1; /* others */ for (i = 0; extensions[i]; i++) { len = strlen(extensions[i]); if (l < nameoffset + len) continue; if (!strcasecmp(extensions[i], end + l - len)) return 1; } return 0; } /* If `name' has a correct extension, try to list its contents and search for the first file with a proper extension; if found, extract it. If this succeeds, return the name of the temporary file; if the archive file is valid but `write_mode' is non-zero, return a zero-length string; in all the other cases, return NULL. */ static char *try_uncompress_archive(const char *name, int write_mode, char *program, char *listopts, char *extractopts, char *extension, char *search) { static char tmp_name[L_tmpnam]; int l = strlen(name), nameoffset, found = 0, len; int exit_status; char *argv[8]; FILE *fd; char tmp[1024]; /* Do we have correct extension? */ len = strlen(extension); if (l <= len || strcasecmp(name + l - len, extension) != 0) return NULL; /* First run listing and search for first recognizeable extension. */ argv[0] = stralloc(program); argv[1] = stralloc(listopts); argv[2] = stralloc(name); argv[3] = NULL; ZDEBUG(("try_uncompress_archive: spawning `%d %s %s'\n", program, listopts, name)); tmpnam(tmp_name); exit_status = spawn(program, argv, tmp_name, NULL); free(argv[0]); free(argv[1]); free(argv[2]); /* No luck? */ if (exit_status != 0) { ZDEBUG(("try_uncompress_archive: `%s %s' failed.\n", program, listopts)); unlink(tmp_name); return NULL; } ZDEBUG(("try_uncompress_archive: `%s %s' successful.\n", program, listopts)); fd = fopen(tmp_name, "r"); if (!fd) { ZDEBUG(("try_uncompress_archive: cannot read `%s %s' output.\n", program, archive)); unlink(tmp_name); return NULL; } ZDEBUG(("try_uncompress_archive: searching for the first valid file.\n")); /* Search for `search' first (if any) to see the offset where filename begins, then search for first recognizeable file. */ nameoffset = search ? -1 : 0; len = search ? strlen(search) : 0; while (!feof(fd)) { fgets(tmp, 1024, fd); l = strlen(tmp); while (l > 0 && (tmp[l-1] == '\n' || tmp[l-1] == '\r')) tmp[--l] = 0; if (nameoffset < 0 && l >= len && !strcasecmp(tmp + l - len, search) != 0) { nameoffset = l - 4; } if (nameoffset >= 0 && is_valid_extension(tmp, l, nameoffset)) { ZDEBUG(("try_uncompress_archive: found `%s'.\n", tmp + nameoffset)); found = 1; break; } } fclose(fd); unlink(tmp_name); if (!found) { ZDEBUG(("try_uncompress_archive: no valid file found.\n")); return NULL; } /* This would be a valid ZIP file, but we cannot handle ZIP files in write mode. Return a null temporary file name to report this. */ if (write_mode) { ZDEBUG(("try_uncompress_archive: cannot open file in write mode.\n")); return ""; } /* And then file inside zip. If we have a zipcode extract all of them to the same file. */ argv[0] = stralloc(program); argv[1] = stralloc(extractopts); argv[2] = stralloc(name); if (is_zipcode_name(tmp + nameoffset)) { argv[3] = stralloc(tmp + nameoffset); argv[4] = stralloc(tmp + nameoffset); argv[5] = stralloc(tmp + nameoffset); argv[6] = stralloc(tmp + nameoffset); argv[7] = NULL; argv[3][0] = '1'; argv[4][0] = '2'; argv[5][0] = '3'; argv[6][0] = '4'; } else { argv[3] = stralloc(tmp + nameoffset); argv[4] = NULL; } ZDEBUG(("try_uncompress_archive: spawning `%s %s %s %s'.\n", program, extractopts, name, tmp + nameoffset)); exit_status = spawn(program, argv, tmp_name, NULL); free(argv[0]); free(argv[1]); free(argv[2]); free(argv[3]); free(argv[4]); if (is_zipcode_name(tmp + nameoffset)) { free(argv[5]); free(argv[6]); free(argv[7]); } if (exit_status != 0) { ZDEBUG(("try_uncompress_archive: `%s %s' failed.", program, extractopts)); unlink(tmp_name); return NULL; } ZDEBUG(("try_uncompress_archive: `%s %s' successful.", program, archive)); return tmp_name; } /* If this file looks like a zipcode, try to extract is using c1541. We have to figure this out by reading the contents of the file */ static char *try_uncompress_zipcode(const char *name, int write_mode) { static char tmp_name[L_tmpnam]; int fd, i, count, sector, sectors = 0; unsigned char tmp[256]; char *argv[5]; int exit_status; /* can we read this file? */ fd = open(name, O_RDONLY); if (fd < 0) return NULL; /* Read first track to see if this is zipcode */ lseek(fd, 4, SEEK_SET); for (count = 1; count < 21; count++) { i = zipcode_read_sector(fd, 1, §or, tmp); if (i || sector < 0 || sector > 20 || (sectors & (1 << sector))) { close(fd); return NULL; } sectors |= 1 << sector; } close(fd); /* it is a zipcode. We cannot support write_mode */ if (write_mode) return ""; /* format image first */ tmpnam(tmp_name); argv[0] = stralloc("c1541"); argv[1] = stralloc("-format"); argv[2] = stralloc(tmp_name); argv[3] = stralloc("a,bc"); argv[4] = NULL; exit_status = spawn("c1541", argv, NULL, NULL); free(argv[0]); free(argv[1]); free(argv[2]); free(argv[3]); if (exit_status) { unlink(tmp_name); return NULL; } /* ok, now extract the zipcode */ argv[0] = stralloc("c1541"); argv[1] = stralloc("-zcreate"); argv[2] = stralloc(tmp_name); argv[3] = stralloc(name); argv[4] = NULL; exit_status = spawn("c1541", argv, NULL, NULL); free(argv[0]); free(argv[1]); free(argv[2]); free(argv[3]); if (exit_status) { unlink(tmp_name); return NULL; } /* everything ok */ return tmp_name; } /* If the file looks like a lynx image, try to extract it using c1541. We have to figure this out by reading the contsnts of the file */ static char *try_uncompress_lynx(const char *name, int write_mode) { static char tmp_name[L_tmpnam]; int fd, i, count; unsigned char tmp[256]; char *argv[5]; int exit_status; /* can we read this file? */ fd = open(name, O_RDONLY); if (fd < 0) return NULL; /* is this lynx -image? */ i = read(fd, tmp, 2); if (i != 2 || tmp[0] != 1 || tmp[1] != 8) { close(fd); return NULL; } count = 0; while (1) { i = read(fd, tmp, 1); if (i != 1) { close(fd); return NULL; } if (tmp[0]) count = 0; else count++; if (count == 3) break; } i = read(fd, tmp, 1); if (i != 1 || tmp[0] != 13) { close(fd); return NULL; } count = 0; while (1) { i = read(fd, &tmp[count], 1); if (i != 1 || count == 254) { close(fd); return NULL; } if (tmp[count++] == 13) break; } tmp[count] = 0; if (!atoi(tmp)) { close(fd); return NULL; } /* XXX: this is not a full check, but perhaps enough? */ close(fd); /* it is a lynx image. We cannot support write_mode */ if (write_mode) return ""; /* format image first */ tmpnam(tmp_name); argv[0] = stralloc("c1541"); argv[1] = stralloc("-format"); argv[2] = stralloc(tmp_name); argv[3] = stralloc("a,bc"); argv[4] = NULL; exit_status = spawn("c1541", argv, NULL, NULL); free(argv[0]); free(argv[1]); free(argv[2]); free(argv[3]); if (exit_status) { unlink(tmp_name); return NULL; } /* ok, now create the image */ argv[0] = stralloc("c1541"); argv[1] = stralloc("-unlynx"); argv[2] = stralloc(tmp_name); argv[3] = stralloc(name); argv[4] = NULL; exit_status = spawn("c1541", argv, NULL, NULL); free(argv[0]); free(argv[1]); free(argv[2]); free(argv[3]); if (exit_status) { unlink(tmp_name); return NULL; } /* everything ok */ return tmp_name; } /* List of archives we understand. */ static struct { char *program; char *listopts; char *extractopts; char *extension; char *search; } valid_archives[] = { #ifndef __MSDOS__ { "unzip", "-l", "-p", ".zip", "Name" }, { "lha", "lv", "pq", ".lzh", NULL }, { "lha", "lv", "pq", ".lha", NULL }, /* Hmmm. Did non-gnu tar have a -O -option? */ { "gtar", "-tf", "-xOf", ".tar", NULL }, { "tar", "-tf", "-xOf", ".tar", NULL }, { "gtar", "-ztf", "-zxOf", ".tar.gz", NULL }, { "tar", "-ztf", "-zxOf", ".tar.gz", NULL }, { "gtar", "-ztf", "-zxOf", ".tgz", NULL }, { "tar", "-ztf", "-zxOf", ".tgz", NULL }, /* this might be overkill, but adding this was sooo easy... */ { "zoo", "lf1q", "xpq", ".zoo", NULL }, #endif { NULL } }; /* Try to uncompress file `name' using the algorithms we now of. If this is not possible, return `COMPR_NONE'. Otherwise, uncompress the file into a temporary file, return the type of algorithm used and the name of the temporary file in `tmp_name'. If `write_mode' is non-zero and the returned `tmp_name' has zero length, then the file cannot be accessed in write mode. */ static enum compression_type try_uncompress(const char *name, char **tmp_name, int write_mode) { int i; for (i = 0; valid_archives[i].program; i++) { if ((*tmp_name = try_uncompress_archive(name, write_mode, valid_archives[i].program, valid_archives[i].listopts, valid_archives[i].extractopts, valid_archives[i].extension, valid_archives[i].search)) != NULL) { return COMPR_ARCHIVE; } } /* need this order or .tar.gz is misunderstood */ if ((*tmp_name = try_uncompress_with_gzip(name)) != NULL) return COMPR_GZIP; if ((*tmp_name = try_uncompress_with_bzip(name)) != NULL) return COMPR_BZIP; if ((*tmp_name = try_uncompress_zipcode(name, write_mode)) != NULL) return COMPR_ZIPCODE; if ((*tmp_name = try_uncompress_lynx(name, write_mode)) != NULL) return COMPR_LYNX; return COMPR_NONE; } /* ------------------------------------------------------------------------- */ /* Compression. */ /* Compress `src' into `dest' using gzip. */ static int compress_with_gzip(const char *src, const char *dest) { static char *argv[4]; int exit_status; /* `exec*()' does not want these to be constant... */ argv[0] = stralloc("gzip"); argv[1] = stralloc("-c"); argv[2] = stralloc(src); argv[3] = NULL; ZDEBUG(("compress_with_gzip: spawning gzip -c %s\n", src)); exit_status = spawn("gzip", argv, dest, NULL); free(argv[0]); free(argv[1]); free(argv[2]); if (exit_status == 0) { ZDEBUG(("compress_with_gzip: OK.\n")); return 0; } else { ZDEBUG(("compress_with_gzip: failed.\n")); return -1; } } /* Compress `src' into `dest' using bzip. */ static int compress_with_bzip(const char *src, const char *dest) { static char *argv[4]; int exit_status; /* `exec*()' does not want these to be constant... */ argv[0] = stralloc("bzip2"); argv[1] = stralloc("-c"); argv[2] = stralloc(src); argv[3] = NULL; ZDEBUG(("compress_with_bzip: spawning bzip -c %s\n", src)); exit_status = spawn("bzip2", argv, dest, NULL); free(argv[0]); free(argv[1]); free(argv[2]); if (exit_status == 0) { ZDEBUG(("compress_with_bzip: OK.\n")); return 0; } else { ZDEBUG(("compress_with_bzip: failed.\n")); return -1; } } /* Compress `src' into `dest' using algorithm `type'. */ static int compress(const char *src, const char *dest, enum compression_type type) { char *dest_backup_name; int retval; /* This shouldn't happen */ if (type == COMPR_ARCHIVE) { fprintf(stderr, "compress: trying to compress archive -file\n"); return -1; } /* This shouldn't happen */ if (type == COMPR_ZIPCODE) { fprintf(stderr, "compress: trying to compress zipcode -file\n"); return -1; } /* This shouldn't happen */ if (type == COMPR_LYNX) { fprintf(stderr, "compress: trying to compress lynx -file\n"); return -1; } /* Check whether `compression_type' is a known one. */ if (type != COMPR_GZIP && type != COMPR_BZIP) { ZDEBUG(("compress: unknown compression type\n")); return -1; } /* If we have no write permissions for `dest', give up. */ if (access(dest, W_OK) < 0) { ZDEBUG(("compress: no write permissions for `%s'\n", dest)); return -1; } if (access(dest, R_OK) < 0) { ZDEBUG(("compress: no read permissions for `%s'\n", dest)); perror(dest); dest_backup_name = NULL; } else { /* If `dest' exists, make a backup first. */ dest_backup_name = make_backup_filename(dest); if (dest_backup_name != NULL) ZDEBUG(("compress: making backup %s... ", dest_backup_name)); if (dest_backup_name != NULL && rename(dest, dest_backup_name) < 0) { ZDEBUG(("failed.\n")); perror("rename"); fprintf(stderr, "Could not make pre-compression backup.\n"); return -1; } else { ZDEBUG(("OK\n")); } } switch (type) { case COMPR_GZIP: retval = compress_with_gzip(src, dest); break; case COMPR_BZIP: retval = compress_with_bzip(src, dest); break; default: retval = -1; } if (retval == -1) { /* Compression failed: restore original file. */ if (dest_backup_name != NULL && rename(dest_backup_name, dest) < 0) { perror("rename"); fprintf(stderr, "Could not restore backup file after failed compression.\n"); } } else { /* Compression succeeded: remove backup file. */ if (dest_backup_name != NULL && unlink(dest_backup_name) < 0) { perror("unlink"); fprintf(stderr, "Warning: could not remove backup file.\n"); /* Do not return an error anyway (no data is lost). */ } } if (dest_backup_name) free(dest_backup_name); return retval; } /* ------------------------------------------------------------------------ */ /* Here we have the actual open/fopen and close/fclose wrappers. These functions work exactly like the standard library versions, but handle compression and decompression automatically. When a file is opened, we check whether it looks like a compressed file of some kind. If so, we uncompress it and then actually open the uncompressed version. When a file that was opened for writing is closed, we re-compress the uncompressed version and update the original file. */ /* `open()' wrapper. */ int zopen(const char *name, mode_t opt, int flags) { char *tmp_name; int fd; enum compression_type type; int write_mode; if (!zinit_done) zinit(); /* Do we want to write to this file? */ write_mode = opt & (O_RDWR | O_WRONLY); /* Check for write permissions. */ if (write_mode && access(name, W_OK) < 0) return -1; type = try_uncompress(name, &tmp_name, write_mode); if (type == COMPR_NONE) return open(name, opt, flags); else if (*tmp_name == '\0') { errno = EACCES; return -1; } /* (Arghl... The following code is very nice, except that it cannot work backwards, and it also clobbers `type' causing even "plain" compression not to work correctly anymore.) */ #if 0 /* OK, we managed to decompress that. Let's see if we can do that again. If we can, we can delete the previous tmpfile */ while (1) { type = try_uncompress(tmp_name, &tmp_name2, write_mode); if (type == COMPR_NONE) break; if (*tmp_name == '\0') { unlink(tmp_name); errno = EACCES; return -1; } unlink(tmp_name); tmp_name = tmp_name2; } #endif /* Open the uncompressed version of the file. */ fd = open(tmp_name, opt, flags); if (fd < 0) return fd; zfile_list_add(tmp_name, name, type, write_mode, NULL, fd); return fd; } /* `fopen()' wrapper. */ FILE *zfopen(const char *name, const char *mode) { char *tmp_name; FILE *stream; enum compression_type type; int write_mode; if (!zinit_done) zinit(); /* Do we want to write to this file? */ write_mode = (strchr(mode, 'w') != NULL); /* Check for write permissions. */ if (write_mode && access(name, W_OK) < 0) return NULL; type = try_uncompress(name, &tmp_name, write_mode); if (type == COMPR_NONE) return fopen(name, mode); else if (*tmp_name == '\0') { errno = EACCES; return NULL; } /* Open the uncompressed version of the file. */ stream = fopen(tmp_name, mode); if (stream == NULL) return NULL; zfile_list_add(tmp_name, name, type, write_mode, stream, -1); return stream; } /* Handle close of a compressed file. `ptr' points to the zfile to close. */ static int handle_close(struct zfile *ptr) { ZDEBUG(("handle_close: closing `%s' (`%s'), write_mode = %d\n", ptr->tmp_name, ptr->orig_name, ptr->write_mode)); /* Recompress into the original file. */ if (ptr->write_mode && compress(ptr->tmp_name, ptr->orig_name, ptr->type)) return -1; /* Remove temporary file. */ if (unlink(ptr->tmp_name) < 0) perror(ptr->tmp_name); /* Remove item from list. */ if (ptr->prev != NULL) ptr->prev->next = ptr->next; else zfile_list = ptr->next; free(ptr->orig_name); free(ptr->tmp_name); free(ptr); return 0; } /* `close()' wrapper. */ int zclose(int fd) { struct zfile *ptr; if (!zinit_done) { ZDEBUG(("zclose: closing without init!?\n")); errno = EBADF; return -1; } ZDEBUG(("zclose: searching for the matching file...\n")); /* Search for the matching file in the list. */ for (ptr = zfile_list; ptr != NULL; ptr = ptr->next) { if (ptr->fd == fd) { ZDEBUG(("zclose: file found, closing.\n")); /* Close temporary file. */ if (close(fd) == -1) { ZDEBUG(("zclose: cannot close temporary file: %s\n", strerror(errno))); return -1; } if (handle_close(ptr) < 0) { errno = EBADF; return -1; } return 0; } } ZDEBUG(("zclose: file descriptor not in the list, closing normally.\n")); return close(fd); } /* `fclose()' wrapper. */ int zfclose(FILE *stream) { struct zfile *ptr; if (!zinit_done) { errno = EBADF; return -1; } /* Search for the matching file in the list. */ for (ptr = zfile_list; ptr != NULL; ptr = ptr->next) { if (ptr->stream == stream) { /* Close temporary file. */ if (fclose(stream) == -1) return -1; if (handle_close(ptr) < 0) { errno = EBADF; return -1; } return 0; } } return fclose(stream); } /* Close all files. */ int zclose_all(void) { struct zfile *p = zfile_list, *pnext; int ret = 0; if (!zinit_done) return 0; while (p != NULL) { if (p->stream != NULL) { if (fclose(p->stream) == -1) ret = -1; } else if (p->fd != -1) { if (close(p->fd) == -1) ret = -1; } else { fprintf(stderr, "Inconsistent zfile list!\n"); continue; } /* Recompress into the original file. */ if (p->write_mode && compress(p->tmp_name, p->orig_name, p->type)) return -1; if (unlink(p->tmp_name) < 0) perror(p->tmp_name); free(p->orig_name); free(p->tmp_name); pnext = p->next; free(p); p = pnext; } return ret; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.