summaryrefslogtreecommitdiff
path: root/zipnote.c
diff options
context:
space:
mode:
Diffstat (limited to 'zipnote.c')
-rw-r--r--zipnote.c699
1 files changed, 699 insertions, 0 deletions
diff --git a/zipnote.c b/zipnote.c
new file mode 100644
index 0000000..5e02cb6
--- /dev/null
+++ b/zipnote.c
@@ -0,0 +1,699 @@
+/*
+ zipnote.c - Zip 3
+
+ Copyright (c) 1990-2008 Info-ZIP. All rights reserved.
+
+ See the accompanying file LICENSE, version 2007-Mar-4 or later
+ (the contents of which are also included in zip.h) for terms of use.
+ If, for some reason, all these files are missing, the Info-ZIP license
+ also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html
+*/
+/*
+ * zipnote.c by Mark Adler.
+ */
+#define __ZIPNOTE_C
+
+#ifndef UTIL
+#define UTIL
+#endif
+#include "zip.h"
+#define DEFCPYRT /* main module: enable copyright string defines! */
+#include "revision.h"
+#include <signal.h>
+
+/* Calculate size of static line buffer used in write (-w) mode. */
+#define WRBUFSIZ 2047
+/* The line buffer size should be at least as large as FNMAX. */
+#if FNMAX > WRBUFSIZ
+# undef WRBUFSIZ
+# define WRBUFSIZ FNMAX
+#endif
+
+/* Character to mark zip entry names in the comment file */
+#define MARK '@'
+#define MARKE " (comment above this line)"
+#define MARKZ " (zip file comment below this line)"
+
+/* Temporary zip file pointer */
+local FILE *tempzf;
+
+
+/* Local functions */
+local void handler OF((int));
+local void license OF((void));
+local void help OF((void));
+local void version_info OF((void));
+local void putclean OF((char *, extent));
+/* getline name conflicts with GNU getline() function */
+local char *zgetline OF((char *, extent));
+local int catalloc OF((char * far *, char *));
+int main OF((int, char **));
+
+/* keep compiler happy until implement long options - 11/4/2003 EG */
+struct option_struct far options[] = {
+ /* short longopt value_type negatable ID name */
+ {"h", "help", o_NO_VALUE, o_NOT_NEGATABLE, 'h', "help"},
+ /* the end of the list */
+ {NULL, NULL, o_NO_VALUE, o_NOT_NEGATABLE, 0, NULL} /* end has option_ID = 0 */
+ };
+
+#ifdef MACOS
+#define ziperr(c, h) zipnoteerr(c, h)
+#define zipwarn(a, b) zipnotewarn(a, b)
+
+void zipnoteerr(int c, ZCONST char *h);
+void zipnotewarn(ZCONST char *a, ZCONST char *b);
+#endif
+
+#ifdef QDOS
+#define exit(p1) QDOSexit()
+#endif
+
+int set_filetype(out_path)
+ char *out_path;
+{
+#ifdef __BEOS__
+ /* Set the filetype of the zipfile to "application/zip" */
+ setfiletype( out_path, "application/zip" );
+#endif
+
+#ifdef __ATHEOS__
+ /* Set the filetype of the zipfile to "application/x-zip" */
+ setfiletype(out_path, "application/x-zip");
+#endif
+
+#ifdef MACOS
+ /* Set the Creator/Type of the zipfile to 'IZip' and 'ZIP ' */
+ setfiletype(out_path, 'IZip', 'ZIP ');
+#endif
+
+#ifdef RISCOS
+ /* Set the filetype of the zipfile to &DDC */
+ setfiletype(out_path, 0xDDC);
+#endif
+ return ZE_OK;
+}
+
+/* rename a split
+ * A split has a tempfile name until it is closed, then
+ * here rename it as out_path the final name for the split.
+ */
+int rename_split(temp_name, out_path)
+ char *temp_name;
+ char *out_path;
+{
+ int r;
+ /* Replace old zip file with new zip file, leaving only the new one */
+ if ((r = replace(out_path, temp_name)) != ZE_OK)
+ {
+ zipwarn("new zip file left as: ", temp_name);
+ free((zvoid *)tempzip);
+ tempzip = NULL;
+ ZIPERR(r, "was replacing split file");
+ }
+ if (zip_attributes) {
+ setfileattr(out_path, zip_attributes);
+ }
+ return ZE_OK;
+}
+
+void zipmessage_nl(a, nl)
+ZCONST char *a; /* message string to output */
+int nl; /* 1 = add nl to end */
+/* If nl false, print a message to mesg without new line.
+ If nl true, print and add new line. If logfile is
+ open then also write message to log file. */
+{
+ if (noisy) {
+ fprintf(mesg, "%s", a);
+ if (nl) {
+ fprintf(mesg, "\n");
+ mesg_line_started = 0;
+ } else {
+ mesg_line_started = 1;
+ }
+ fflush(mesg);
+ }
+}
+
+void zipmessage(a, b)
+ZCONST char *a, *b; /* message strings juxtaposed in output */
+/* Print a message to mesg and flush. Also write to log file if
+ open. Write new line first if current line has output already. */
+{
+ if (noisy) {
+ if (mesg_line_started)
+ fprintf(mesg, "\n");
+ fprintf(mesg, "%s%s\n", a, b);
+ mesg_line_started = 0;
+ fflush(mesg);
+ }
+}
+
+void ziperr(c, h)
+int c; /* error code from the ZE_ class */
+ZCONST char *h; /* message about how it happened */
+/* Issue a message for the error, clean up files and memory, and exit. */
+{
+ if (PERR(c))
+ perror("zipnote error");
+ fprintf(mesg, "zipnote error: %s (%s)\n", ZIPERRORS(c), h);
+ if (tempzf != NULL)
+ fclose(tempzf);
+ if (tempzip != NULL)
+ {
+ destroy(tempzip);
+ free((zvoid *)tempzip);
+ }
+ if (zipfile != NULL)
+ free((zvoid *)zipfile);
+ EXIT(c);
+}
+
+
+local void handler(s)
+int s; /* signal number (ignored) */
+/* Upon getting a user interrupt, abort cleanly using ziperr(). */
+{
+#ifndef MSDOS
+ putc('\n', mesg);
+#endif /* !MSDOS */
+ ziperr(ZE_ABORT, "aborting");
+ s++; /* keep some compilers happy */
+}
+
+
+void zipwarn(a, b)
+ZCONST char *a, *b; /* message strings juxtaposed in output */
+/* Print a warning message to mesg (usually stderr) and return. */
+{
+ fprintf(mesg, "zipnote warning: %s%s\n", a, b);
+}
+
+
+local void license()
+/* Print license information to stdout. */
+{
+ extent i; /* counter for copyright array */
+
+ for (i = 0; i < sizeof(swlicense)/sizeof(char *); i++)
+ puts(swlicense[i]);
+}
+
+
+local void help()
+/* Print help (along with license info) to stdout. */
+{
+ extent i; /* counter for help array */
+
+ /* help array */
+ static ZCONST char *text[] = {
+"",
+"ZipNote %s (%s)",
+#ifdef VM_CMS
+"Usage: zipnote [-w] [-q] [-b fm] zipfile",
+#else
+"Usage: zipnote [-w] [-q] [-b path] zipfile",
+#endif
+" the default action is to write the comments in zipfile to stdout",
+" -w write the zipfile comments from stdin",
+#ifdef VM_CMS
+" -b use \"fm\" as the filemode for the temporary zip file",
+#else
+" -b use \"path\" for the temporary zip file",
+#endif
+" -q quieter operation, suppress some informational messages",
+" -h show this help -v show version info -L show software license",
+"",
+"Example:",
+#ifdef VMS
+" define/user sys$output foo.tmp",
+" zipnote foo.zip",
+" edit foo.tmp",
+" ... then you edit the comments, save, and exit ...",
+" define/user sys$input foo.tmp",
+" zipnote -w foo.zip",
+#else
+#ifdef RISCOS
+" zipnote foo/zip > foo/tmp",
+" <!Edit> foo/tmp",
+" ... then you edit the comments, save, and exit ...",
+" zipnote -w foo/zip < foo/tmp",
+#else
+#ifdef VM_CMS
+" zipnote foo.zip > foo.tmp",
+" xedit foo tmp",
+" ... then you edit the comments, save, and exit ...",
+" zipnote -w foo.zip < foo.tmp",
+#else
+" zipnote foo.zip > foo.tmp",
+" ed foo.tmp",
+" ... then you edit the comments, save, and exit ...",
+" zipnote -w foo.zip < foo.tmp",
+#endif /* VM_CMS */
+#endif /* RISCOS */
+#endif /* VMS */
+"",
+" \"@ name\" can be followed by an \"@=newname\" line to change the name"
+ };
+
+ for (i = 0; i < sizeof(copyright)/sizeof(char *); i++) {
+ printf(copyright[i], "zipnote");
+ putchar('\n');
+ }
+ for (i = 0; i < sizeof(text)/sizeof(char *); i++)
+ {
+ printf(text[i], VERSION, REVDATE);
+ putchar('\n');
+ }
+}
+
+/*
+ * XXX put this in version.c
+ */
+
+local void version_info()
+/* Print verbose info about program version and compile time options
+ to stdout. */
+{
+ extent i; /* counter in text arrays */
+
+ /* Options info array */
+ static ZCONST char *comp_opts[] = {
+#ifdef DEBUG
+ "DEBUG",
+#endif
+ NULL
+ };
+
+ for (i = 0; i < sizeof(copyright)/sizeof(char *); i++)
+ {
+ printf(copyright[i], "zipnote");
+ putchar('\n');
+ }
+
+ for (i = 0; i < sizeof(versinfolines)/sizeof(char *); i++)
+ {
+ printf(versinfolines[i], "ZipNote", VERSION, REVDATE);
+ putchar('\n');
+ }
+
+ version_local();
+
+ puts("ZipNote special compilation options:");
+ for (i = 0; (int)i < (int)(sizeof(comp_opts)/sizeof(char *) - 1); i++)
+ {
+ printf("\t%s\n",comp_opts[i]);
+ }
+ if (i == 0)
+ puts("\t[none]");
+}
+
+
+local void putclean(s, n)
+char *s; /* string to write to stdout */
+extent n; /* length of string */
+/* Write the string s to stdout, filtering out control characters that are
+ not tab or newline (mainly to remove carriage returns), and prefix MARK's
+ and backslashes with a backslash. Also, terminate with a newline if
+ needed. */
+{
+ int c; /* next character in string */
+ int e; /* last character written */
+
+ e = '\n'; /* if empty, write nothing */
+ while (n--)
+ {
+ c = *(uch *)s++;
+ if (c == MARK || c == '\\')
+ putchar('\\');
+ if (c >= ' ' || c == '\t' || c == '\n')
+ { e=c; putchar(e); }
+ }
+ if (e != '\n')
+ putchar('\n');
+}
+
+
+local char *zgetline(buf, size)
+char *buf;
+extent size;
+/* Read a line of text from stdin into string buffer 'buf' of size 'size'.
+ In case of buffer overflow or EOF, a NULL pointer is returned. */
+{
+ char *line;
+ unsigned len;
+
+ line = fgets(buf, size, stdin);
+ if (line != NULL && (len = strlen(line)) > 0) {
+ if (len == size-1 && line[len-1] != '\n') {
+ /* buffer is full and record delimiter not seen -> overflow */
+ line = NULL;
+ } else {
+ /* delete trailing record delimiter */
+ if (line[len-1] == '\n') line[len-1] = '\0';
+ }
+ }
+ return line;
+}
+
+
+local int catalloc(a, s)
+char * far *a; /* pointer to a pointer to a malloc'ed string */
+char *s; /* string to concatenate on a */
+/* Concatentate the string s to the malloc'ed string pointed to by a.
+ Preprocess s by removing backslash escape characters. */
+{
+ char *p; /* temporary pointer */
+ char *q; /* temporary pointer */
+
+ for (p = q = s; *q; *p++ = *q++)
+ if (*q == '\\' && *(q+1))
+ q++;
+ *p = 0;
+ if ((p = malloc(strlen(*a) + strlen(s) + 3)) == NULL)
+ return ZE_MEM;
+ strcat(strcat(strcpy(p, *a), **a ? "\r\n" : ""), s);
+ free((zvoid *)*a);
+ *a = p;
+ return ZE_OK;
+}
+
+
+#ifndef USE_ZIPNOTEMAIN
+int main(argc, argv)
+#else
+int zipnotemain(argc, argv)
+#endif
+int argc; /* number of tokens in command line */
+char **argv; /* command line tokens */
+/* Write the comments in the zipfile to stdout, or read them from stdin. */
+{
+ char abf[WRBUFSIZ+1]; /* input line buffer */
+ char *a; /* pointer to line buffer or NULL */
+ zoff_t c; /* start of central directory */
+ int k; /* next argument type */
+ char *q; /* steps through option arguments */
+ int r; /* arg counter, temporary variable */
+ zoff_t s; /* length of central directory */
+ int t; /* attributes of zip file */
+ int w; /* true if updating zip file from stdin */
+ FILE *x; /* input file for testing if can write it */
+ struct zlist far *z; /* steps through zfiles linked list */
+
+#ifdef THEOS
+ setlocale(LC_CTYPE, "I");
+#endif
+
+#ifdef UNICODE_SUPPORT
+# ifdef UNIX
+ /* For Unix, set the locale to UTF-8. Any UTF-8 locale is
+ OK and they should all be the same. This allows seeing,
+ writing, and displaying (if the fonts are loaded) all
+ characters in UTF-8. */
+ {
+ char *loc;
+
+ /*
+ loc = setlocale(LC_CTYPE, NULL);
+ printf(" Initial language locale = '%s'\n", loc);
+ */
+
+ loc = setlocale(LC_CTYPE, "en_US.UTF-8");
+
+ /*
+ printf("langinfo %s\n", nl_langinfo(CODESET));
+ */
+
+ if (loc != NULL) {
+ /* using UTF-8 character set so can set UTF-8 GPBF bit 11 */
+ using_utf8 = 1;
+ /*
+ printf(" Locale set to %s\n", loc);
+ */
+ } else {
+ /*
+ printf(" Could not set Unicode UTF-8 locale\n");
+ */
+ }
+ }
+# endif
+#endif
+
+ /* If no args, show help */
+ if (argc == 1)
+ {
+ help();
+ EXIT(ZE_OK);
+ }
+
+ /* Direct info messages to stderr; stdout is used for data output. */
+ mesg = stderr;
+
+ init_upper(); /* build case map table */
+
+ /* Go through args */
+ zipfile = tempzip = NULL;
+ tempzf = NULL;
+ signal(SIGINT, handler);
+#ifdef SIGTERM /* AMIGA has no SIGTERM */
+ signal(SIGTERM, handler);
+#endif
+#ifdef SIGABRT
+ signal(SIGABRT, handler);
+#endif
+#ifdef SIGBREAK
+ signal(SIGBREAK, handler);
+#endif
+#ifdef SIGBUS
+ signal(SIGBUS, handler);
+#endif
+#ifdef SIGILL
+ signal(SIGILL, handler);
+#endif
+#ifdef SIGSEGV
+ signal(SIGSEGV, handler);
+#endif
+ k = w = 0;
+ for (r = 1; r < argc; r++)
+ if (*argv[r] == '-') {
+ if (argv[r][1])
+ for (q = argv[r]+1; *q; q++)
+ switch (*q)
+ {
+ case 'b': /* Specify path for temporary file */
+ if (k)
+ ziperr(ZE_PARMS, "use -b before zip file name");
+ else
+ k = 1; /* Next non-option is path */
+ break;
+ case 'h': /* Show help */
+ help(); EXIT(ZE_OK);
+ case 'l': case 'L': /* Show copyright and disclaimer */
+ license(); EXIT(ZE_OK);
+ case 'q': /* Quiet operation, suppress info messages */
+ noisy = 0; break;
+ case 'v': /* Show version info */
+ version_info(); EXIT(ZE_OK);
+ case 'w':
+ w = 1; break;
+ default:
+ ziperr(ZE_PARMS, "unknown option");
+ }
+ else
+ ziperr(ZE_PARMS, "zip file cannot be stdin");
+ } else
+ if (k == 0)
+ {
+ if (zipfile == NULL)
+ {
+ if ((zipfile = ziptyp(argv[r])) == NULL)
+ ziperr(ZE_MEM, "was processing arguments");
+ }
+ else
+ ziperr(ZE_PARMS, "can only specify one zip file");
+ }
+ else
+ {
+ tempath = argv[r];
+ k = 0;
+ }
+ if (zipfile == NULL)
+ ziperr(ZE_PARMS, "need to specify zip file");
+
+ if ((in_path = malloc(strlen(zipfile) + 1)) == NULL) {
+ ziperr(ZE_MEM, "input");
+ }
+ strcpy(in_path, zipfile);
+
+ /* Read zip file */
+ if ((r = readzipfile()) != ZE_OK)
+ ziperr(r, zipfile);
+ if (zfiles == NULL)
+ ziperr(ZE_NAME, zipfile);
+
+ /* Put comments to stdout, if not -w */
+ if (!w)
+ {
+ for (z = zfiles; z != NULL; z = z->nxt)
+ {
+ printf("%c %s\n", MARK, z->zname);
+ putclean(z->comment, z->com);
+ printf("%c%s\n", MARK, MARKE);
+ }
+ printf("%c%s\n", MARK, MARKZ);
+ putclean(zcomment, zcomlen);
+ EXIT(ZE_OK);
+ }
+
+ /* If updating comments, make sure zip file is writeable */
+ if ((x = fopen(zipfile, "a")) == NULL)
+ ziperr(ZE_CREAT, zipfile);
+ fclose(x);
+ t = getfileattr(zipfile);
+
+ /* Process stdin, replacing comments */
+ z = zfiles;
+ while ((a = zgetline(abf, WRBUFSIZ+1)) != NULL &&
+ (a[0] != MARK || strcmp(a + 1, MARKZ)))
+ { /* while input and not file comment */
+ if (a[0] != MARK || a[1] != ' ') /* better be "@ name" */
+ ziperr(ZE_NOTE, "unexpected input");
+ while (z != NULL && strcmp(a + 2, z->zname))
+ z = z->nxt; /* allow missing entries in order */
+ if (z == NULL)
+ ziperr(ZE_NOTE, "unknown entry name");
+ if ((a = zgetline(abf, WRBUFSIZ+1)) != NULL && a[0] == MARK && a[1] == '=')
+ {
+ if (z->name != z->iname)
+ free((zvoid *)z->iname);
+ if ((z->iname = malloc(strlen(a+1))) == NULL)
+ ziperr(ZE_MEM, "was changing name");
+#ifdef EBCDIC
+ strtoasc(z->iname, a+2);
+#else
+ strcpy(z->iname, a+2);
+#endif
+
+/*
+ * Don't update z->nam here, we need the old value a little later.....
+ * The update is handled in zipcopy().
+ */
+ a = zgetline(abf, WRBUFSIZ+1);
+ }
+ if (z->com) /* change zip entry comment */
+ free((zvoid *)z->comment);
+ z->comment = malloc(1); *(z->comment) = 0;
+ while (a != NULL && *a != MARK)
+ {
+ if ((r = catalloc(&(z->comment), a)) != ZE_OK)
+ ziperr(r, "was building new zipentry comments");
+ a = zgetline(abf, WRBUFSIZ+1);
+ }
+ z->com = strlen(z->comment);
+ z = z->nxt; /* point to next entry */
+ }
+ if (a != NULL) /* change zip file comment */
+ {
+ zcomment = malloc(1); *zcomment = 0;
+ while ((a = zgetline(abf, WRBUFSIZ+1)) != NULL)
+ if ((r = catalloc(&zcomment, a)) != ZE_OK)
+ ziperr(r, "was building new zipfile comment");
+ zcomlen = strlen(zcomment);
+ }
+
+ /* Open output zip file for writing */
+#if defined(UNIX) && !defined(NO_MKSTEMP)
+ {
+ int yd;
+ int i;
+
+ /* use mkstemp to avoid race condition and compiler warning */
+
+ if (tempath != NULL)
+ {
+ /* if -b used to set temp file dir use that for split temp */
+ if ((tempzip = malloc(strlen(tempath) + 12)) == NULL) {
+ ZIPERR(ZE_MEM, "allocating temp filename");
+ }
+ strcpy(tempzip, tempath);
+ if (lastchar(tempzip) != '/')
+ strcat(tempzip, "/");
+ }
+ else
+ {
+ /* create path by stripping name and appending template */
+ if ((tempzip = malloc(strlen(zipfile) + 12)) == NULL) {
+ ZIPERR(ZE_MEM, "allocating temp filename");
+ }
+ strcpy(tempzip, zipfile);
+ for(i = strlen(tempzip); i > 0; i--) {
+ if (tempzip[i - 1] == '/')
+ break;
+ }
+ tempzip[i] = '\0';
+ }
+ strcat(tempzip, "ziXXXXXX");
+
+ if ((yd = mkstemp(tempzip)) == EOF) {
+ ZIPERR(ZE_TEMP, tempzip);
+ }
+ if ((tempzf = y = fdopen(yd, FOPW)) == NULL) {
+ ZIPERR(ZE_TEMP, tempzip);
+ }
+ }
+#else
+ if ((tempzf = y = fopen(tempzip = tempname(zipfile), FOPW)) == NULL)
+ ziperr(ZE_TEMP, tempzip);
+#endif
+
+ /* Open input zip file again, copy preamble if any */
+ if ((in_file = fopen(zipfile, FOPR)) == NULL)
+ ziperr(ZE_NAME, zipfile);
+
+ if (zipbeg && (r = bfcopy(zipbeg)) != ZE_OK)
+ ziperr(r, r == ZE_TEMP ? tempzip : zipfile);
+ tempzn = zipbeg;
+
+ /* Go through local entries, copying them over as is */
+ fix = 3; /* needed for zipcopy if name changed */
+ for (z = zfiles; z != NULL; z = z->nxt) {
+ if ((r = zipcopy(z)) != ZE_OK)
+ ziperr(r, "was copying an entry");
+ }
+ fclose(x);
+
+ /* Write central directory and end of central directory with new comments */
+ if ((c = zftello(y)) == (zoff_t)-1) /* get start of central */
+ ziperr(ZE_TEMP, tempzip);
+ for (z = zfiles; z != NULL; z = z->nxt)
+ if ((r = putcentral(z)) != ZE_OK)
+ ziperr(r, tempzip);
+ if ((s = zftello(y)) == (zoff_t)-1) /* get end of central */
+ ziperr(ZE_TEMP, tempzip);
+ s -= c; /* compute length of central */
+ if ((r = putend((zoff_t)zcount, s, c, zcomlen, zcomment)) != ZE_OK)
+ ziperr(r, tempzip);
+ tempzf = NULL;
+ if (fclose(y))
+ ziperr(ZE_TEMP, tempzip);
+ if ((r = replace(zipfile, tempzip)) != ZE_OK)
+ {
+ zipwarn("new zip file left as: ", tempzip);
+ free((zvoid *)tempzip);
+ tempzip = NULL;
+ ziperr(r, "was replacing the original zip file");
+ }
+ free((zvoid *)tempzip);
+ tempzip = NULL;
+ setfileattr(zipfile, t);
+#ifdef RISCOS
+ /* Set the filetype of the zipfile to &DDC */
+ setfiletype(zipfile,0xDDC);
+#endif
+ free((zvoid *)zipfile);
+ zipfile = NULL;
+
+ /* Done! */
+ RETURN(0);
+}