/* * Copyright (c) 2003 Guido Draheim * Use freely under the restrictions of the ZLIB license. * * This file is used as an example to clarify zzip api usage. */ #include #include #include #include #include #include "__mkdir.h" #include "__string.h" #include "__fnmatch.h" #include "__debug.h" #include "unzzipcat-zip.h" #include "unzzip-states.h" #ifdef ZZIP_HAVE_UNISTD_H #include #endif #ifdef ZZIP_HAVE_IO_H #include #endif static int exitcode(int e) { switch (e) { case ZZIP_NO_ERROR: return EXIT_OK; case ZZIP_OUTOFMEM: /* out of memory */ return EXIT_ENOMEM; case ZZIP_DIR_OPEN: /* failed to open zipfile, see errno for details */ return EXIT_ZIP_NOT_FOUND; case ZZIP_DIR_STAT: /* failed to fstat zipfile, see errno for details */ case ZZIP_DIR_SEEK: /* failed to lseek zipfile, see errno for details */ case ZZIP_DIR_READ: /* failed to read zipfile, see errno for details */ case ZZIP_DIR_TOO_SHORT: case ZZIP_DIR_EDH_MISSING: return EXIT_FILEFORMAT; case ZZIP_DIRSIZE: return EXIT_EARLY_END_OF_FILE; case ZZIP_ENOENT: return EXIT_FILE_NOT_FOUND; case ZZIP_UNSUPP_COMPR: return EXIT_UNSUPPORTED_COMPRESSION; case ZZIP_CORRUPTED: case ZZIP_UNDEF: case ZZIP_DIR_LARGEFILE: return EXIT_FILEFORMAT; } return EXIT_ERRORS; } static void unzzip_cat_file(ZZIP_DIR* disk, char* name, FILE* out) { ZZIP_FILE* file = zzip_file_open (disk, name, 0); if (file) { char buffer[1024]; int len; while ((len = zzip_file_read (file, buffer, 1024))) { fwrite (buffer, 1, len, out); } zzip_file_close (file); } } /* * NAME: remove_dotdotslash * PURPOSE: To remove any "../" components from the given pathname * ARGUMENTS: path: path name with maybe "../" components * RETURNS: Nothing, "path" is modified in-place * NOTE: removing "../" from the path ALWAYS shortens the path, never adds to it! * Also, "path" is not used after creating it. * So modifying "path" in-place is safe to do. */ static inline void remove_dotdotslash(char *path) { /* Note: removing "../" from the path ALWAYS shortens the path, never adds to it! */ char *dotdotslash; int warned = 0; dotdotslash = path; while ((dotdotslash = strstr(dotdotslash, "../")) != NULL) { /* * Remove only if at the beginning of the pathname ("../path/name") * or when preceded by a slash ("path/../name"), * otherwise not ("path../name..")! */ if (dotdotslash == path || dotdotslash[-1] == '/') { char *src, *dst; if (!warned) { /* Note: the first time through the pathname is still intact */ fprintf(stderr, "Removing \"../\" path component(s) in %s\n", path); warned = 1; } /* We cannot use strcpy(), as there "The strings may not overlap" */ for (src = dotdotslash+3, dst=dotdotslash; (*dst = *src) != '\0'; src++, dst++) ; } else dotdotslash +=3; /* skip this instance to prevent infinite loop */ } } static void makedirs(const char* name) { char* p = strrchr(name, '/'); if (p) { char* dir_name = _zzip_strndup(name, p-name); makedirs(dir_name); free (dir_name); } if (_zzip_mkdir(name, 0775) == -1 && errno != EEXIST) { DBG3("while mkdir %s : %s", name, strerror(errno)); } errno = 0; } static FILE* create_fopen(char* name, char* mode, int subdirs) { char *name_stripped; FILE *fp; int mustfree = 0; if ((name_stripped = strdup(name)) != NULL) { remove_dotdotslash(name_stripped); name = name_stripped; mustfree = 1; } if (subdirs) { char* p = strrchr(name, '/'); if (p) { char* dir_name = _zzip_strndup(name, p-name); makedirs(dir_name); free (dir_name); } } fp = fopen(name, mode); if (mustfree) free(name_stripped); return fp; } static int unzzip_cat (int argc, char ** argv, int extract) { int done = 0; int argn; ZZIP_DIR* disk; zzip_error_t error; if (argc == 1) { printf (__FILE__" version "ZZIP_PACKAGE" "ZZIP_VERSION"\n"); return EXIT_OK; /* better provide an archive argument */ } disk = zzip_dir_open (argv[1], &error); if (! disk) { fprintf(stderr, "%s: %s\n", argv[1], zzip_strerror(error)); return exitcode(error); } if (argc == 2) { /* list all */ ZZIP_DIRENT entry; while(zzip_dir_read(disk, &entry)) { char* name = entry.d_name; FILE* out = stdout; if (extract) out = create_fopen(name, "w", 1); if (! out) { DBG3("fopen' %s : %s", name, strerror(errno)); if (errno != EISDIR) done = EXIT_ERRORS; continue; } unzzip_cat_file (disk, name, out); if (extract) fclose(out); } } else { /* list only the matching entries - in order of zip directory */ ZZIP_DIRENT entry; while(zzip_dir_read(disk, &entry)) { char* name = entry.d_name; for (argn=1; argn < argc; argn++) { if (! _zzip_fnmatch (argv[argn], name, FNM_NOESCAPE|FNM_PATHNAME|FNM_PERIOD)) { FILE* out = stdout; if (extract) out = create_fopen(name, "w", 1); if (! out) { DBG3("fopen. %s : %s", name, strerror(errno)); if (errno != EISDIR) done = EXIT_ERRORS; continue; } unzzip_cat_file (disk, name, out); if (extract) fclose(out); break; /* match loop */ } } } } zzip_dir_close(disk); return done; } int unzzip_print (int argc, char ** argv) { return unzzip_cat(argc, argv, 0); } int unzzip_extract (int argc, char ** argv) { return unzzip_cat(argc, argv, 1); } /* * Local variables: * c-file-style: "stroustrup" * End: */