summaryrefslogtreecommitdiff
path: root/fileio.c
diff options
context:
space:
mode:
Diffstat (limited to 'fileio.c')
-rw-r--r--fileio.c4903
1 files changed, 4903 insertions, 0 deletions
diff --git a/fileio.c b/fileio.c
new file mode 100644
index 0000000..1847e62
--- /dev/null
+++ b/fileio.c
@@ -0,0 +1,4903 @@
+/*
+ fileio.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
+*/
+/*
+ * fileio.c by Mark Adler
+ */
+#define __FILEIO_C
+
+#include "zip.h"
+#include "crc32.h"
+
+#ifdef MACOS
+# include "helpers.h"
+#endif
+
+#ifdef VMS
+# include "vms/vms.h"
+#endif /* def VMS */
+
+#include <time.h>
+
+#ifdef NO_MKTIME
+time_t mktime OF((struct tm *));
+#endif
+
+#ifdef OSF
+#define EXDEV 18 /* avoid a bug in the DEC OSF/1 header files. */
+#else
+#include <errno.h>
+#endif
+
+#ifdef NO_ERRNO
+extern int errno;
+#endif
+
+/* -----------------------
+ For long option support
+ ----------------------- */
+#include <ctype.h>
+
+
+#if defined(VMS) || defined(TOPS20)
+# define PAD 5
+#else
+# define PAD 0
+#endif
+
+#ifdef NO_RENAME
+int rename OF((ZCONST char *, ZCONST char *));
+#endif
+
+
+/* Local functions */
+local int optionerr OF((char *, ZCONST char *, int, int));
+local unsigned long get_shortopt OF((char **, int, int *, int *, char **, int *, int));
+local unsigned long get_longopt OF((char **, int, int *, int *, char **, int *, int));
+
+#ifdef UNICODE_SUPPORT
+local int utf8_char_bytes OF((ZCONST char *utf8));
+local long ucs4_char_from_utf8 OF((ZCONST char **utf8 ));
+local int utf8_from_ucs4_char OF((char *utf8buf, ulg ch));
+local int utf8_to_ucs4_string OF((ZCONST char *utf8, ulg *usc4buf,
+ int buflen));
+local int ucs4_string_to_utf8 OF((ZCONST ulg *ucs4, char *utf8buf,
+ int buflen));
+#if 0
+ local int utf8_chars OF((ZCONST char *utf8));
+#endif
+#endif /* UNICODE_SUPPORT */
+
+#ifndef UTIL /* the companion #endif is a bit of ways down ... */
+
+local int fqcmp OF((ZCONST zvoid *, ZCONST zvoid *));
+local int fqcmpz OF((ZCONST zvoid *, ZCONST zvoid *));
+
+
+/* Local module level variables. */
+char *label = NULL; /* global, but only used in `system'.c */
+local z_stat zipstatb; /* now use z_stat globally - 7/24/04 EG */
+#if defined(UNICODE_SUPPORT) && defined(WIN32)
+ local zw_stat zipstatbw;
+#endif
+#if (!defined(MACOS) && !defined(WINDLL))
+local int zipstate = -1;
+#else
+int zipstate;
+#endif
+/* -1 unknown, 0 old zip file exists, 1 new zip file */
+
+#if 0
+char *getnam(n, fp)
+char *n; /* where to put name (must have >=FNMAX+1 bytes) */
+#endif
+
+/* converted to return string pointer from malloc to avoid
+ size limitation - 11/8/04 EG */
+#define GETNAM_MAX 9000 /* hopefully big enough for now */
+char *getnam(fp)
+ FILE *fp;
+ /* Read a \n or \r delimited name from stdin into n, and return
+ n. If EOF, then return NULL. Also, if problem return NULL. */
+{
+ char name[GETNAM_MAX + 1];
+ int c; /* last character read */
+ char *p; /* pointer into name area */
+
+
+ p = name;
+ while ((c = getc(fp)) == '\n' || c == '\r')
+ ;
+ if (c == EOF)
+ return NULL;
+ do {
+ if (p - name >= GETNAM_MAX)
+ return NULL;
+ *p++ = (char) c;
+ c = getc(fp);
+ } while (c != EOF && (c != '\n' && c != '\r'));
+#ifdef WIN32
+/*
+ * WIN32 strips off trailing spaces and periods in filenames
+ * XXX what about a filename that only consists of spaces ?
+ * Answer: on WIN32, a filename must contain at least one non-space char
+ */
+ while (p > name) {
+ if ((c = p[-1]) != ' ' && c != '.')
+ break;
+ --p;
+ }
+#endif
+ *p = 0;
+ /* malloc a copy */
+ if ((p = malloc(strlen(name) + 1)) == NULL) {
+ return NULL;
+ }
+ strcpy(p, name);
+ return p;
+}
+
+struct flist far *fexpel(f)
+struct flist far *f; /* entry to delete */
+/* Delete the entry *f in the doubly-linked found list. Return pointer to
+ next entry to allow stepping through list. */
+{
+ struct flist far *t; /* temporary variable */
+
+ t = f->nxt;
+ *(f->lst) = t; /* point last to next, */
+ if (t != NULL)
+ t->lst = f->lst; /* and next to last */
+ if (f->name != NULL) /* free memory used */
+ free((zvoid *)(f->name));
+ if (f->zname != NULL)
+ free((zvoid *)(f->zname));
+ if (f->iname != NULL)
+ free((zvoid *)(f->iname));
+#ifdef UNICODE_SUPPORT
+ if (f->uname)
+ free((zvoid *)f->uname);
+# ifdef WIN32
+ if (f->namew)
+ free((zvoid *)f->namew);
+ if (f->inamew)
+ free((zvoid *)f->inamew);
+ if (f->znamew)
+ free((zvoid *)f->znamew);
+# endif
+#endif
+ farfree((zvoid far *)f);
+ fcount--; /* decrement count */
+ return t; /* return pointer to next */
+}
+
+local int fqcmp(a, b)
+ ZCONST zvoid *a, *b; /* pointers to pointers to found entries */
+/* Used by qsort() to compare entries in the found list by name. */
+{
+ return strcmp((*(struct flist far **)a)->name,
+ (*(struct flist far **)b)->name);
+}
+
+local int fqcmpz(a, b)
+ ZCONST zvoid *a, *b; /* pointers to pointers to found entries */
+/* Used by qsort() to compare entries in the found list by iname. */
+{
+ return strcmp((*(struct flist far **)a)->iname,
+ (*(struct flist far **)b)->iname);
+}
+
+char *last(p, c)
+ char *p; /* sequence of path components */
+ int c; /* path components separator character */
+/* Return a pointer to the start of the last path component. For a directory
+ * name terminated by the character in c, the return value is an empty string.
+ */
+{
+ char *t; /* temporary variable */
+
+ if ((t = strrchr(p, c)) != NULL)
+ return t + 1;
+ else
+#ifndef AOS_VS
+ return p;
+#else
+/* We want to allow finding of end of path in either AOS/VS-style pathnames
+ * or Unix-style pathnames. This presents a few little problems ...
+ */
+ {
+ if (*p == '=' || *p == '^') /* like ./ and ../ respectively */
+ return p + 1;
+ else
+ return p;
+ }
+#endif
+}
+
+#if defined(UNICODE_SUPPORT) && defined(WIN32)
+wchar_t *lastw(pw, c)
+ wchar_t *pw; /* sequence of path components */
+ wchar_t c; /* path components separator character */
+/* Return a pointer to the start of the last path component. For a directory
+ * name terminated by the character in c, the return value is an empty string.
+ */
+{
+ wchar_t *tw; /* temporary variable */
+
+ if ((tw = wcsrchr(pw, c)) != NULL)
+ return tw + 1;
+ else
+# ifndef AOS_VS
+ return pw;
+# else
+/* We want to allow finding of end of path in either AOS/VS-style pathnames
+ * or Unix-style pathnames. This presents a few little problems ...
+ */
+ {
+ if (*pw == (wchar_t)'=' || *pw == (wchar_t)'^') /* like ./ and ../ respectively */
+ return pw + 1;
+ else
+ return pw;
+ }
+# endif
+}
+#endif
+
+
+char *msname(n)
+ char *n;
+/* Reduce all path components to MSDOS upper case 8.3 style names. */
+{
+ int c; /* current character */
+ int f; /* characters in current component */
+ char *p; /* source pointer */
+ char *q; /* destination pointer */
+
+ p = q = n;
+ f = 0;
+ while ((c = (unsigned char)*POSTINCSTR(p)) != 0)
+ if (c == ' ' || c == ':' || c == '"' || c == '*' || c == '+' ||
+ c == ',' || c == ';' || c == '<' || c == '=' || c == '>' ||
+ c == '?' || c == '[' || c == ']' || c == '|')
+ continue; /* char is discarded */
+ else if (c == '/')
+ {
+ *POSTINCSTR(q) = (char)c;
+ f = 0; /* new component */
+ }
+#ifdef __human68k__
+ else if (ismbblead(c) && *p)
+ {
+ if (f == 7 || f == 11)
+ f++;
+ else if (*p && f < 12 && f != 8)
+ {
+ *q++ = c;
+ *q++ = *p++;
+ f += 2;
+ }
+ }
+#endif /* __human68k__ */
+ else if (c == '.')
+ {
+ if (f == 0)
+ continue; /* leading dots are discarded */
+ else if (f < 9)
+ {
+ *POSTINCSTR(q) = (char)c;
+ f = 9; /* now in file type */
+ }
+ else
+ f = 12; /* now just excess characters */
+ }
+ else
+ if (f < 12 && f != 8)
+ {
+ f += CLEN(p); /* do until end of name or type */
+ *POSTINCSTR(q) = (char)(to_up(c));
+ }
+ *q = 0;
+ return n;
+}
+
+#ifdef UNICODE_SUPPORT
+wchar_t *msnamew(nw)
+ wchar_t *nw;
+/* Reduce all path components to MSDOS upper case 8.3 style names. */
+{
+ wchar_t c; /* current character */
+ int f; /* characters in current component */
+ wchar_t *pw; /* source pointer */
+ wchar_t *qw; /* destination pointer */
+
+ pw = qw = nw;
+ f = 0;
+ while ((c = (unsigned char)*pw++) != 0)
+ if (c == ' ' || c == ':' || c == '"' || c == '*' || c == '+' ||
+ c == ',' || c == ';' || c == '<' || c == '=' || c == '>' ||
+ c == '?' || c == '[' || c == ']' || c == '|')
+ continue; /* char is discarded */
+ else if (c == '/')
+ {
+ *qw++ = c;
+ f = 0; /* new component */
+ }
+#ifdef __human68k__
+ else if (ismbblead(c) && *pw)
+ {
+ if (f == 7 || f == 11)
+ f++;
+ else if (*pw && f < 12 && f != 8)
+ {
+ *qw++ = c;
+ *qw++ = *pw++;
+ f += 2;
+ }
+ }
+#endif /* __human68k__ */
+ else if (c == '.')
+ {
+ if (f == 0)
+ continue; /* leading dots are discarded */
+ else if (f < 9)
+ {
+ *qw++ = c;
+ f = 9; /* now in file type */
+ }
+ else
+ f = 12; /* now just excess characters */
+ }
+ else
+ if (f < 12 && f != 8)
+ {
+ f++; /* do until end of name or type */
+ *qw++ = towupper(c);
+ }
+ *qw = 0;
+ return nw;
+}
+#endif
+
+
+int proc_archive_name(n, caseflag)
+ char *n; /* name to process */
+ int caseflag; /* true to force case-sensitive match */
+/* Process a name or sh expression in existing archive to operate
+ on (or exclude). Return an error code in the ZE_ class. */
+{
+ int m; /* matched flag */
+ char *p; /* path for recursion */
+ struct zlist far *z; /* steps through zfiles list */
+
+ if (strcmp(n, "-") == 0) { /* if compressing stdin */
+ zipwarn("Cannot select stdin when selecting archive entries", "");
+ return ZE_MISS;
+ }
+ else
+ {
+ /* Search for shell expression in zip file */
+ p = ex2in(n, 0, (int *)NULL); /* shouldn't affect matching chars */
+ m = 1;
+ for (z = zfiles; z != NULL; z = z->nxt) {
+ if (MATCH(p, z->iname, caseflag))
+ {
+ z->mark = pcount ? filter(z->zname, caseflag) : 1;
+ if (verbose)
+ fprintf(mesg, "zip diagnostic: %scluding %s\n",
+ z->mark ? "in" : "ex", z->oname);
+ m = 0;
+ }
+ }
+#ifdef UNICODE_SUPPORT
+ /* also check escaped Unicode names */
+ for (z = zfiles; z != NULL; z = z->nxt) {
+ if (z->zuname) {
+#ifdef WIN32
+ /* It seems something is lost in going from a listed
+ name from zip -su in a console window to using that
+ name in a command line. This kluge may fix it
+ and just takes zuname, converts to oem (i.e. ouname),
+ then converts it back which ends up not the same as
+ started with.
+ */
+ char *zuname = z->wuname;
+#else
+ char *zuname = z->zuname;
+#endif
+ if (MATCH(p, zuname, caseflag))
+ {
+ z->mark = pcount ? filter(zuname, caseflag) : 1;
+ if (verbose) {
+ fprintf(mesg, "zip diagnostic: %scluding %s\n",
+ z->mark ? "in" : "ex", z->oname);
+ fprintf(mesg, " Escaped Unicode: %s\n",
+ z->ouname);
+ }
+ m = 0;
+ }
+ }
+ }
+#endif
+ free((zvoid *)p);
+ return m ? ZE_MISS : ZE_OK;
+ }
+}
+
+
+int check_dup()
+/* Sort the found list and remove duplicates.
+ Return an error code in the ZE_ class. */
+{
+ struct flist far *f; /* steps through found linked list */
+ extent j, k; /* indices for s */
+ struct flist far **s; /* sorted table */
+ struct flist far **nodup; /* sorted table without duplicates */
+
+ /* sort found list, remove duplicates */
+ if (fcount)
+ {
+ extent fl_size = fcount * sizeof(struct flist far *);
+ if ((fl_size / sizeof(struct flist far *)) != fcount ||
+ (s = (struct flist far **)malloc(fl_size)) == NULL)
+ return ZE_MEM;
+ for (j = 0, f = found; f != NULL; f = f->nxt)
+ s[j++] = f;
+ /* Check names as given (f->name) */
+ qsort((char *)s, fcount, sizeof(struct flist far *), fqcmp);
+ for (k = j = fcount - 1; j > 0; j--)
+ if (strcmp(s[j - 1]->name, s[j]->name) == 0)
+ /* remove duplicate entry from list */
+ fexpel(s[j]); /* fexpel() changes fcount */
+ else
+ /* copy valid entry into destination position */
+ s[k--] = s[j];
+ s[k] = s[0]; /* First entry is always valid */
+ nodup = &s[k]; /* Valid entries are at end of array s */
+
+ /* sort only valid items and check for unique internal names (f->iname) */
+ qsort((char *)nodup, fcount, sizeof(struct flist far *), fqcmpz);
+ for (j = 1; j < fcount; j++)
+ if (strcmp(nodup[j - 1]->iname, nodup[j]->iname) == 0)
+ {
+ char tempbuf[FNMAX+4081];
+
+ sprintf(errbuf, " first full name: %s\n", nodup[j - 1]->name);
+ sprintf(tempbuf, " second full name: %s\n", nodup[j]->name);
+ strcat(errbuf, " ");
+ strcat(errbuf, tempbuf);
+#ifdef EBCDIC
+ strtoebc(nodup[j]->iname, nodup[j]->iname);
+#endif
+ sprintf(tempbuf, "name in zip file repeated: %s", nodup[j]->iname);
+ strcat(errbuf, " ");
+ strcat(errbuf, tempbuf);
+ if (pathput == 0) {
+ strcat(errbuf, "\n this may be a result of using -j");
+ }
+#ifdef EBCDIC
+ strtoasc(nodup[j]->iname, nodup[j]->iname);
+#endif
+ zipwarn(errbuf, "");
+ return ZE_PARMS;
+ }
+ free((zvoid *)s);
+ }
+ return ZE_OK;
+}
+
+int filter(name, casesensitive)
+ char *name;
+ int casesensitive;
+ /* Scan the -R, -i and -x lists for matches to the given name.
+ Return TRUE if the name must be included, FALSE otherwise.
+ Give precedence to -x over -i and -R.
+ Note that if both R and i patterns are given then must
+ have a match for both.
+ This routine relies on the following global variables:
+ patterns array of match pattern structures
+ pcount total number of patterns
+ icount number of -i patterns
+ Rcount number of -R patterns
+ These data are set up by the command line parsing code.
+ */
+{
+ unsigned int n;
+ int slashes;
+ char *p, *q;
+ /* without -i patterns, every name matches the "-i select rules" */
+ int imatch = (icount == 0);
+ /* without -R patterns, every name matches the "-R select rules" */
+ int Rmatch = (Rcount == 0);
+
+ if (pcount == 0) return TRUE;
+
+ for (n = 0; n < pcount; n++) {
+ if (!patterns[n].zname[0]) /* it can happen... */
+ continue;
+ p = name;
+ switch (patterns[n].select) {
+ case 'R':
+ if (Rmatch)
+ /* one -R match is sufficient, skip this pattern */
+ continue;
+ /* With -R patterns, if the pattern has N path components (that is,
+ N-1 slashes), then we test only the last N components of name.
+ */
+ slashes = 0;
+ for (q = patterns[n].zname; (q = MBSCHR(q, '/')) != NULL; MB_NEXTCHAR(q))
+ slashes++;
+ /* The name may have M path components (M-1 slashes) */
+ for (q = p; (q = MBSCHR(q, '/')) != NULL; MB_NEXTCHAR(q))
+ slashes--;
+ /* Now, "slashes" contains the difference "N-M" between the number
+ of path components in the pattern (N) and in the name (M).
+ */
+ if (slashes < 0)
+ /* We found "M > N"
+ --> skip the first (M-N) path components of the name.
+ */
+ for (q = p; (q = MBSCHR(q, '/')) != NULL; MB_NEXTCHAR(q))
+ if (++slashes == 0) {
+ p = q + 1; /* q points at '/', mblen("/") is 1 */
+ break;
+ }
+ break;
+ case 'i':
+ if (imatch)
+ /* one -i match is sufficient, skip this pattern */
+ continue;
+ break;
+ }
+ if (MATCH(patterns[n].zname, p, casesensitive)) {
+ switch (patterns[n].select) {
+ case 'x':
+ /* The -x match takes precedence over everything else */
+ return FALSE;
+ case 'R':
+ Rmatch = TRUE;
+ break;
+ default:
+ /* this must be a type -i match */
+ imatch = TRUE;
+ break;
+ }
+ }
+ }
+ return imatch && Rmatch;
+}
+
+
+#ifdef UNICODE_SUPPORT
+# ifdef WIN32
+
+int newnamew(namew, isdir, casesensitive)
+ wchar_t *namew; /* name to add (or exclude) */
+ int isdir; /* true for a directory */
+ int casesensitive; /* true for case-sensitive matching */
+/* Add (or exclude) the name of an existing disk file. Return an error
+ code in the ZE_ class. */
+{
+ wchar_t *inamew = NULL; /* internal name */
+ wchar_t *znamew = NULL; /* external version of iname */
+ wchar_t *undosmw = NULL; /* zname version with "-j" and "-k" options disabled */
+ char *oname = NULL; /* iname converted for display */
+ char *name = NULL;
+ char *iname = NULL;
+ char *zname = NULL;
+ char *zuname = NULL;
+ char *undosm = NULL;
+ struct flist far *f; /* where in found, or new found entry */
+ struct zlist far *z; /* where in zfiles (if found) */
+ int dosflag;
+
+ /* Scanning files ...
+ *
+ * After 5 seconds output Scanning files...
+ * then a dot every 2 seconds
+ */
+ if (noisy) {
+ /* If find files then output message after delay */
+ if (scan_count == 0) {
+ time_t current = time(NULL);
+ scan_start = current;
+ }
+ scan_count++;
+ if (scan_count % 100 == 0) {
+ time_t current = time(NULL);
+
+ if (current - scan_start > scan_delay) {
+ if (scan_last == 0) {
+ zipmessage_nl("Scanning files ", 0);
+ scan_last = current;
+ }
+ if (current - scan_last > scan_dot_time) {
+ scan_last = current;
+ fprintf(mesg, ".");
+ fflush(mesg);
+ }
+ }
+ }
+ }
+
+ /* Search for name in zip file. If there, mark it, else add to
+ list of new names to do (or remove from that list). */
+ if ((inamew = ex2inw(namew, isdir, &dosflag)) == NULL)
+ return ZE_MEM;
+
+ /* Discard directory names with zip -rj */
+ if (*inamew == (wchar_t)'\0') {
+
+ /* If extensions needs to be swapped, we will have empty directory names
+ instead of the original directory. For example, zipping 'c.', 'c.main'
+ should zip only 'main.c' while 'c.' will be converted to '\0' by ex2in. */
+
+ if (pathput && !recurse) error("empty name without -j or -r");
+ free((zvoid *)inamew);
+ return ZE_OK;
+ }
+
+ if (dosflag || !pathput) {
+ int save_dosify = dosify, save_pathput = pathput;
+ dosify = 0;
+ pathput = 1;
+ /* zname is temporarly mis-used as "undosmode" iname pointer */
+ if ((znamew = ex2inw(namew, isdir, NULL)) != NULL) {
+ undosmw = in2exw(znamew);
+ free(znamew);
+ }
+ dosify = save_dosify;
+ pathput = save_pathput;
+ }
+ if ((znamew = in2exw(inamew)) == NULL)
+ return ZE_MEM;
+
+ /* Convert names from wchar_t to char */
+
+ name = wchar_to_local_string(namew);
+ iname = wchar_to_local_string(inamew);
+ zname = wchar_to_local_string(znamew);
+
+ oname = local_to_display_string(zname);
+
+ zuname = wchar_to_local_string(znamew);
+
+ if (undosmw == NULL)
+ undosmw = znamew;
+ undosm = wchar_to_local_string(undosmw);
+
+ if ((z = zsearch(zuname)) != NULL) {
+ if (pcount && !filter(undosm, casesensitive)) {
+ /* Do not clear z->mark if "exclude", because, when "dosify || !pathput"
+ * is in effect, two files with different filter options may hit the
+ * same z entry.
+ */
+ if (verbose)
+ fprintf(mesg, "excluding %s\n", oname);
+ } else {
+ z->mark = 1;
+ if ((z->name = malloc(strlen(name) + 1 + PAD)) == NULL) {
+ if (undosmw != znamew)
+ free(undosmw);
+ if (undosm) free(undosm);
+ if (inamew) free(inamew);
+ if (znamew) free(znamew);
+ if (name) free(name);
+ if (iname) free(iname);
+ if (zname) free(zname);
+ if (oname) free(oname);
+ if (zuname) free(zuname);
+ return ZE_MEM;
+ }
+ strcpy(z->name, name);
+ z->oname = oname;
+ oname = NULL;
+ z->dosflag = dosflag;
+
+#ifdef FORCE_NEWNAME
+ free((zvoid *)(z->iname));
+ z->iname = iname;
+ iname = NULL;
+#else
+ /* Better keep the old name. Useful when updating on MSDOS a zip file
+ * made on Unix.
+ */
+#endif /* ? FORCE_NEWNAME */
+ }
+
+ if ((z->namew = (wchar_t *)malloc((wcslen(namew) + 1) * sizeof(wchar_t))) == NULL) {
+ if (undosmw != znamew)
+ free(undosmw);
+ if (undosm) free(undosm);
+ if (inamew) free(inamew);
+ if (znamew) free(znamew);
+ if (name) free(name);
+ if (iname) free(iname);
+ if (zname) free(zname);
+ if (oname) free(oname);
+ if (zuname) free(zuname);
+ return ZE_MEM;
+ }
+ wcscpy(z->namew, namew);
+ z->inamew = inamew;
+ inamew = NULL;
+ z->znamew = znamew;
+ znamew = NULL;
+ z->uname = wchar_to_utf8_string(z->inamew);
+ if (name == label) {
+ label = z->name;
+ }
+ } else if (pcount == 0 || filter(undosm, casesensitive)) {
+
+ /* Check that we are not adding the zip file to itself. This
+ * catches cases like "zip -m foo ../dir/foo.zip".
+ */
+/* Version of stat() for CMS/MVS isn't complete enough to see if */
+/* files match. Just let ZIP.C compare the filenames. That's good */
+/* enough for CMS anyway since there aren't paths to worry about. */
+ zw_stat statbw; /* need for wide stat */
+ wchar_t *zipfilew = local_to_wchar_string(zipfile);
+
+ if (zipstate == -1)
+ zipstate = strcmp(zipfile, "-") != 0 &&
+ zwstat(zipfilew, &zipstatbw) == 0;
+ free(zipfilew);
+
+ if (zipstate == 1 && (statbw = zipstatbw, zwstat(namew, &statbw) == 0
+ && zipstatbw.st_mode == statbw.st_mode
+ && zipstatbw.st_ino == statbw.st_ino
+ && zipstatbw.st_dev == statbw.st_dev
+ && zipstatbw.st_uid == statbw.st_uid
+ && zipstatbw.st_gid == statbw.st_gid
+ && zipstatbw.st_size == statbw.st_size
+ && zipstatbw.st_mtime == statbw.st_mtime
+ && zipstatbw.st_ctime == statbw.st_ctime)) {
+ /* Don't compare a_time since we are reading the file */
+ if (verbose)
+ fprintf(mesg, "file matches zip file -- skipping\n");
+ if (undosmw != znamew)
+ free(undosmw);
+ if (undosm) free(undosm);
+ if (inamew) free(inamew);
+ if (znamew) free(znamew);
+ if (name) free(name);
+ if (iname) free(iname);
+ if (zname) free(zname);
+ if (oname) free(oname);
+ if (zuname) free(zuname);
+ return ZE_OK;
+ }
+
+ /* allocate space and add to list */
+ if ((f = (struct flist far *)farmalloc(sizeof(struct flist))) == NULL ||
+ fcount + 1 < fcount ||
+ (f->name = malloc(strlen(name) + 1 + PAD)) == NULL)
+ {
+ if (f != NULL)
+ farfree((zvoid far *)f);
+ if (undosmw != znamew)
+ free(undosmw);
+ if (undosm) free(undosm);
+ if (inamew) free(inamew);
+ if (znamew) free(znamew);
+ if (name) free(name);
+ if (iname) free(iname);
+ if (zname) free(zname);
+ if (oname) free(oname);
+ if (zuname) free(zuname);
+ return ZE_MEM;
+ }
+ if (undosmw != znamew)
+ free((zvoid *)undosmw);
+ strcpy(f->name, name);
+ f->iname = iname;
+ iname = NULL;
+ f->zname = zname;
+ zname = NULL;
+ /* Unicode */
+ if ((f->namew = (wchar_t *)malloc((wcslen(namew) + 1) * sizeof(wchar_t))) == NULL) {
+ if (f != NULL)
+ farfree((zvoid far *)f);
+ if (undosmw != znamew)
+ free(undosmw);
+ if (undosm) free(undosm);
+ if (inamew) free(inamew);
+ if (znamew) free(znamew);
+ if (name) free(name);
+ if (iname) free(iname);
+ if (zname) free(zname);
+ if (oname) free(oname);
+ if (zuname) free(zuname);
+ return ZE_MEM;
+ }
+ wcscpy(f->namew, namew);
+ f->znamew = znamew;
+ znamew = NULL;
+ f->uname = wchar_to_utf8_string(inamew);
+ f->inamew = inamew;
+ inamew = NULL;
+ f->oname = oname;
+ oname = NULL;
+ f->dosflag = dosflag;
+ *fnxt = f;
+ f->lst = fnxt;
+ f->nxt = NULL;
+ fnxt = &f->nxt;
+ fcount++;
+ if (name == label) {
+ label = f->name;
+ }
+ }
+ if (undosm) free(undosm);
+ if (inamew) free(inamew);
+ if (znamew) free(znamew);
+ if (name) free(name);
+ if (iname) free(iname);
+ if (zname) free(zname);
+ if (oname) free(oname);
+ if (zuname) free(zuname);
+ return ZE_OK;
+}
+
+# endif
+#endif
+
+int newname(name, isdir, casesensitive)
+ char *name; /* name to add (or exclude) */
+ int isdir; /* true for a directory */
+ int casesensitive; /* true for case-sensitive matching */
+/* Add (or exclude) the name of an existing disk file. Return an error
+ code in the ZE_ class. */
+{
+ char *iname, *zname; /* internal name, external version of iname */
+ char *undosm; /* zname version with "-j" and "-k" options disabled */
+ char *oname; /* iname converted for display */
+ struct flist far *f; /* where in found, or new found entry */
+ struct zlist far *z; /* where in zfiles (if found) */
+ int dosflag;
+
+ /* Scanning files ...
+ *
+ * After 5 seconds output Scanning files...
+ * then a dot every 2 seconds
+ */
+ if (noisy) {
+ /* If find files then output message after delay */
+ if (scan_count == 0) {
+ time_t current = time(NULL);
+ scan_start = current;
+ }
+ scan_count++;
+ if (scan_count % 100 == 0) {
+ time_t current = time(NULL);
+
+ if (current - scan_start > scan_delay) {
+ if (scan_last == 0) {
+ zipmessage_nl("Scanning files ", 0);
+ scan_last = current;
+ }
+ if (current - scan_last > scan_dot_time) {
+ scan_last = current;
+ fprintf(mesg, ".");
+ fflush(mesg);
+ }
+ }
+ }
+ }
+
+ /* Search for name in zip file. If there, mark it, else add to
+ list of new names to do (or remove from that list). */
+ if ((iname = ex2in(name, isdir, &dosflag)) == NULL)
+ return ZE_MEM;
+
+ /* Discard directory names with zip -rj */
+ if (*iname == '\0') {
+#ifndef AMIGA
+/* A null string is a legitimate external directory name in AmigaDOS; also,
+ * a command like "zip -r zipfile FOO:" produces an empty internal name.
+ */
+# ifndef RISCOS
+ /* If extensions needs to be swapped, we will have empty directory names
+ instead of the original directory. For example, zipping 'c.', 'c.main'
+ should zip only 'main.c' while 'c.' will be converted to '\0' by ex2in. */
+
+ if (pathput && !recurse) error("empty name without -j or -r");
+
+# endif /* !RISCOS */
+#endif /* !AMIGA */
+ free((zvoid *)iname);
+ return ZE_OK;
+ }
+ undosm = NULL;
+ if (dosflag || !pathput) {
+ int save_dosify = dosify, save_pathput = pathput;
+ dosify = 0;
+ pathput = 1;
+ /* zname is temporarly mis-used as "undosmode" iname pointer */
+ if ((zname = ex2in(name, isdir, NULL)) != NULL) {
+ undosm = in2ex(zname);
+ free(zname);
+ }
+ dosify = save_dosify;
+ pathput = save_pathput;
+ }
+ if ((zname = in2ex(iname)) == NULL)
+ return ZE_MEM;
+#ifdef UNICODE_SUPPORT
+ /* Convert name to display or OEM name */
+ oname = local_to_display_string(iname);
+#else
+ if ((oname = malloc(strlen(zname) + 1)) == NULL)
+ return ZE_MEM;
+ strcpy(oname, zname);
+#endif
+ if (undosm == NULL)
+ undosm = zname;
+ if ((z = zsearch(zname)) != NULL) {
+ if (pcount && !filter(undosm, casesensitive)) {
+ /* Do not clear z->mark if "exclude", because, when "dosify || !pathput"
+ * is in effect, two files with different filter options may hit the
+ * same z entry.
+ */
+ if (verbose)
+ fprintf(mesg, "excluding %s\n", oname);
+ free((zvoid *)iname);
+ free((zvoid *)zname);
+ } else {
+ z->mark = 1;
+ if ((z->name = malloc(strlen(name) + 1 + PAD)) == NULL) {
+ if (undosm != zname)
+ free((zvoid *)undosm);
+ free((zvoid *)iname);
+ free((zvoid *)zname);
+ return ZE_MEM;
+ }
+ strcpy(z->name, name);
+ z->oname = oname;
+ z->dosflag = dosflag;
+
+#ifdef FORCE_NEWNAME
+ free((zvoid *)(z->iname));
+ z->iname = iname;
+#else
+ /* Better keep the old name. Useful when updating on MSDOS a zip file
+ * made on Unix.
+ */
+ free((zvoid *)iname);
+ free((zvoid *)zname);
+#endif /* ? FORCE_NEWNAME */
+ }
+#if defined(UNICODE_SUPPORT) && defined(WIN32)
+ z->namew = NULL;
+ z->inamew = NULL;
+ z->znamew = NULL;
+#endif
+ if (name == label) {
+ label = z->name;
+ }
+ } else if (pcount == 0 || filter(undosm, casesensitive)) {
+
+ /* Check that we are not adding the zip file to itself. This
+ * catches cases like "zip -m foo ../dir/foo.zip".
+ */
+#ifndef CMS_MVS
+/* Version of stat() for CMS/MVS isn't complete enough to see if */
+/* files match. Just let ZIP.C compare the filenames. That's good */
+/* enough for CMS anyway since there aren't paths to worry about. */
+ z_stat statb; /* now use structure z_stat and function zstat globally 7/24/04 EG */
+
+ if (zipstate == -1)
+ zipstate = strcmp(zipfile, "-") != 0 &&
+ zstat(zipfile, &zipstatb) == 0;
+
+ if (zipstate == 1 && (statb = zipstatb, zstat(name, &statb) == 0
+ && zipstatb.st_mode == statb.st_mode
+#ifdef VMS
+ && memcmp(zipstatb.st_ino, statb.st_ino, sizeof(statb.st_ino)) == 0
+ && strcmp(zipstatb.st_dev, statb.st_dev) == 0
+ && zipstatb.st_uid == statb.st_uid
+#else /* !VMS */
+ && zipstatb.st_ino == statb.st_ino
+ && zipstatb.st_dev == statb.st_dev
+ && zipstatb.st_uid == statb.st_uid
+ && zipstatb.st_gid == statb.st_gid
+#endif /* ?VMS */
+ && zipstatb.st_size == statb.st_size
+ && zipstatb.st_mtime == statb.st_mtime
+ && zipstatb.st_ctime == statb.st_ctime)) {
+ /* Don't compare a_time since we are reading the file */
+ if (verbose)
+ fprintf(mesg, "file matches zip file -- skipping\n");
+ if (undosm != zname)
+ free((zvoid *)zname);
+ if (undosm != iname)
+ free((zvoid *)undosm);
+ free((zvoid *)iname);
+ free(oname);
+ return ZE_OK;
+ }
+#endif /* CMS_MVS */
+
+ /* allocate space and add to list */
+ if ((f = (struct flist far *)farmalloc(sizeof(struct flist))) == NULL ||
+ fcount + 1 < fcount ||
+ (f->name = malloc(strlen(name) + 1 + PAD)) == NULL)
+ {
+ if (f != NULL)
+ farfree((zvoid far *)f);
+ if (undosm != zname)
+ free((zvoid *)undosm);
+ free((zvoid *)iname);
+ free((zvoid *)zname);
+ free(oname);
+ return ZE_MEM;
+ }
+ strcpy(f->name, name);
+ f->iname = iname;
+ f->zname = zname;
+#ifdef UNICODE_SUPPORT
+ /* Unicode */
+ f->uname = local_to_utf8_string(iname);
+#ifdef WIN32
+ f->namew = NULL;
+ f->inamew = NULL;
+ f->znamew = NULL;
+ if (strcmp(f->name, "-") == 0) {
+ f->namew = local_to_wchar_string(f->name);
+ }
+#endif
+
+#endif
+ f->oname = oname;
+ f->dosflag = dosflag;
+
+ *fnxt = f;
+ f->lst = fnxt;
+ f->nxt = NULL;
+ fnxt = &f->nxt;
+ fcount++;
+ if (name == label) {
+ label = f->name;
+ }
+ }
+ if (undosm != zname)
+ free((zvoid *)undosm);
+ return ZE_OK;
+}
+
+ulg dostime(y, n, d, h, m, s)
+int y; /* year */
+int n; /* month */
+int d; /* day */
+int h; /* hour */
+int m; /* minute */
+int s; /* second */
+/* Convert the date y/n/d and time h:m:s to a four byte DOS date and
+ time (date in high two bytes, time in low two bytes allowing magnitude
+ comparison). */
+{
+ return y < 1980 ? DOSTIME_MINIMUM /* dostime(1980, 1, 1, 0, 0, 0) */ :
+ (((ulg)y - 1980) << 25) | ((ulg)n << 21) | ((ulg)d << 16) |
+ ((ulg)h << 11) | ((ulg)m << 5) | ((ulg)s >> 1);
+}
+
+
+ulg unix2dostime(t)
+time_t *t; /* unix time to convert */
+/* Return the Unix time t in DOS format, rounded up to the next two
+ second boundary. */
+{
+ time_t t_even;
+ struct tm *s; /* result of localtime() */
+
+ t_even = (time_t)(((unsigned long)(*t) + 1) & (~1));
+ /* Round up to even seconds. */
+ s = localtime(&t_even); /* Use local time since MSDOS does. */
+ if (s == (struct tm *)NULL) {
+ /* time conversion error; use current time as emergency value
+ (assuming that localtime() does at least accept this value!) */
+ t_even = (time_t)(((unsigned long)time(NULL) + 1) & (~1));
+ s = localtime(&t_even);
+ }
+ return dostime(s->tm_year + 1900, s->tm_mon + 1, s->tm_mday,
+ s->tm_hour, s->tm_min, s->tm_sec);
+}
+
+int issymlnk(a)
+ulg a; /* Attributes returned by filetime() */
+/* Return true if the attributes are those of a symbolic link */
+{
+#ifndef QDOS
+#ifdef S_IFLNK
+#ifdef __human68k__
+ int *_dos_importlnenv(void);
+
+ if (_dos_importlnenv() == NULL)
+ return 0;
+#endif
+ return ((a >> 16) & S_IFMT) == S_IFLNK;
+#else /* !S_IFLNK */
+ return (int)a & 0; /* avoid warning on unused parameter */
+#endif /* ?S_IFLNK */
+#else
+ return 0;
+#endif
+}
+
+#endif /* !UTIL */
+
+
+#if (!defined(UTIL) && !defined(ZP_NEED_GEN_D2U_TIME))
+ /* There is no need for dos2unixtime() in the ZipUtils' code. */
+# define ZP_NEED_GEN_D2U_TIME
+#endif
+#if ((defined(OS2) || defined(VMS)) && defined(ZP_NEED_GEN_D2U_TIME))
+ /* OS/2 and VMS use a special solution to handle time-stams of files. */
+# undef ZP_NEED_GEN_D2U_TIME
+#endif
+#if (defined(W32_STATROOT_FIX) && !defined(ZP_NEED_GEN_D2U_TIME))
+ /* The Win32 stat()-bandaid to fix stat'ing root directories needs
+ * dos2unixtime() to calculate the time-stamps. */
+# define ZP_NEED_GEN_D2U_TIME
+#endif
+
+#ifdef ZP_NEED_GEN_D2U_TIME
+
+time_t dos2unixtime(dostime)
+ulg dostime; /* DOS time to convert */
+/* Return the Unix time_t value (GMT/UTC time) for the DOS format (local)
+ * time dostime, where dostime is a four byte value (date in most significant
+ * word, time in least significant word), see dostime() function.
+ */
+{
+ struct tm *t; /* argument for mktime() */
+ ZCONST time_t clock = time(NULL);
+
+ t = localtime(&clock);
+ t->tm_isdst = -1; /* let mktime() determine if DST is in effect */
+ /* Convert DOS time to UNIX time_t format */
+ t->tm_sec = (((int)dostime) << 1) & 0x3e;
+ t->tm_min = (((int)dostime) >> 5) & 0x3f;
+ t->tm_hour = (((int)dostime) >> 11) & 0x1f;
+ t->tm_mday = (int)(dostime >> 16) & 0x1f;
+ t->tm_mon = ((int)(dostime >> 21) & 0x0f) - 1;
+ t->tm_year = ((int)(dostime >> 25) & 0x7f) + 80;
+
+ return mktime(t);
+}
+
+#undef ZP_NEED_GEN_D2U_TIME
+#endif /* ZP_NEED_GEN_D2U_TIME */
+
+
+#ifndef MACOS
+int destroy(f)
+ char *f; /* file to delete */
+/* Delete the file *f, returning non-zero on failure. */
+{
+ return unlink(f);
+}
+
+
+int replace(d, s)
+char *d, *s; /* destination and source file names */
+/* Replace file *d by file *s, removing the old *s. Return an error code
+ in the ZE_ class. This function need not preserve the file attributes,
+ this will be done by setfileattr() later.
+ */
+{
+ z_stat t; /* results of stat() */
+#if defined(CMS_MVS)
+ /* cmsmvs.h defines FOPW_TEMP as memory(hiperspace). Since memory is
+ * lost at end of run, always do copy instead of rename.
+ */
+ int copy = 1;
+#else
+ int copy = 0;
+#endif
+ int d_exists;
+
+#if defined(VMS) || defined(CMS_MVS)
+ /* stat() is broken on VMS remote files (accessed through Decnet).
+ * This patch allows creation of remote zip files, but is not sufficient
+ * to update them or compress remote files */
+ unlink(d);
+#else /* !(VMS || CMS_MVS) */
+ d_exists = (LSTAT(d, &t) == 0);
+ if (d_exists)
+ {
+ /*
+ * respect existing soft and hard links!
+ */
+ if (t.st_nlink > 1
+# ifdef S_IFLNK
+ || (t.st_mode & S_IFMT) == S_IFLNK
+# endif
+ )
+ copy = 1;
+ else if (unlink(d))
+ return ZE_CREAT; /* Can't erase zip file--give up */
+ }
+#endif /* ?(VMS || CMS_MVS) */
+#ifndef CMS_MVS
+ if (!copy) {
+ if (rename(s, d)) { /* Just move s on top of d */
+ copy = 1; /* failed ? */
+#if !defined(VMS) && !defined(ATARI) && !defined(AZTEC_C)
+#if !defined(CMS_MVS) && !defined(RISCOS) && !defined(QDOS)
+ /* For VMS, ATARI, AMIGA Aztec, VM_CMS, MVS, RISCOS,
+ always assume that failure is EXDEV */
+ if (errno != EXDEV
+# ifdef THEOS
+ && errno != EEXIST
+# else
+# ifdef ENOTSAM
+ && errno != ENOTSAM /* Used at least on Turbo C */
+# endif
+# endif
+ ) return ZE_CREAT;
+#endif /* !CMS_MVS && !RISCOS */
+#endif /* !VMS && !ATARI && !AZTEC_C */
+ }
+ }
+#endif /* !CMS_MVS */
+
+ if (copy) {
+ FILE *f, *g; /* source and destination files */
+ int r; /* temporary variable */
+
+#ifdef RISCOS
+ if (SWI_OS_FSControl_26(s,d,0xA1)!=NULL) {
+#endif
+
+ /* Use zfopen for almost all opens where fopen is used. For
+ most OS that support large files we use the 64-bit file
+ environment and zfopen maps to fopen, but this allows
+ tweeking ports that don't do that. 7/24/04 */
+ if ((f = zfopen(s, FOPR)) == NULL) {
+ fprintf(mesg," replace: can't open %s\n", s);
+ return ZE_TEMP;
+ }
+ if ((g = zfopen(d, FOPW)) == NULL)
+ {
+ fclose(f);
+ return ZE_CREAT;
+ }
+
+ r = fcopy(f, g, (ulg)-1L);
+ fclose(f);
+ if (fclose(g) || r != ZE_OK)
+ {
+ unlink(d);
+ return r ? (r == ZE_TEMP ? ZE_WRITE : r) : ZE_WRITE;
+ }
+ unlink(s);
+#ifdef RISCOS
+ }
+#endif
+ }
+ return ZE_OK;
+}
+#endif /* !MACOS */
+
+
+int getfileattr(f)
+char *f; /* file path */
+/* Return the file attributes for file f or 0 if failure */
+{
+#ifdef __human68k__
+ struct _filbuf buf;
+
+ return _dos_files(&buf, f, 0xff) < 0 ? 0x20 : buf.atr;
+#else
+ z_stat s;
+
+ return SSTAT(f, &s) == 0 ? (int) s.st_mode : 0;
+#endif
+}
+
+
+int setfileattr(f, a)
+char *f; /* file path */
+int a; /* attributes returned by getfileattr() */
+/* Give the file f the attributes a, return non-zero on failure */
+{
+#if defined(TOPS20) || defined (CMS_MVS)
+ return 0;
+#else
+#ifdef __human68k__
+ return _dos_chmod(f, a) < 0 ? -1 : 0;
+#else
+ return chmod(f, a);
+#endif
+#endif
+}
+
+
+/* tempname */
+
+#ifndef VMS /* VMS-specific function is in VMS.C. */
+
+char *tempname(zip)
+ char *zip; /* path name of zip file to generate temp name for */
+
+/* Return a temporary file name in its own malloc'ed space, using tempath. */
+{
+ char *t = zip; /* malloc'ed space for name (use zip to avoid warning) */
+
+# ifdef CMS_MVS
+ if ((t = malloc(strlen(tempath) + L_tmpnam + 2)) == NULL)
+ return NULL;
+
+# ifdef VM_CMS
+ tmpnam(t);
+ /* Remove filemode and replace with tempath, if any. */
+ /* Otherwise A-disk is used by default */
+ *(strrchr(t, ' ')+1) = '\0';
+ if (tempath!=NULL)
+ strcat(t, tempath);
+ return t;
+# else /* !VM_CMS */
+ /* For MVS */
+ tmpnam(t);
+ if (tempath != NULL)
+ {
+ int l1 = strlen(t);
+ char *dot;
+ if (*t == '\'' && *(t+l1-1) == '\'' && (dot = strchr(t, '.')))
+ {
+ /* MVS and not OE. tmpnam() returns quoted string of 5 qualifiers.
+ * First is HLQ, rest are timestamps. User can only replace HLQ.
+ */
+ int l2 = strlen(tempath);
+ if (strchr(tempath, '.') || l2 < 1 || l2 > 8)
+ ziperr(ZE_PARMS, "On MVS and not OE, tempath (-b) can only be HLQ");
+ memmove(t+1+l2, dot, l1+1-(dot-t)); /* shift dot ready for new hlq */
+ memcpy(t+1, tempath, l2); /* insert new hlq */
+ }
+ else
+ {
+ /* MVS and probably OE. tmpnam() returns filename based on TMPDIR,
+ * no point in even attempting to change it. User should modify TMPDIR
+ * instead.
+ */
+ zipwarn("MVS, assumed to be OE, change TMPDIR instead of option -b: ",
+ tempath);
+ }
+ }
+ return t;
+# endif /* !VM_CMS */
+
+# else /* !CMS_MVS */
+
+# ifdef TANDEM
+ char cur_subvol [FILENAME_MAX];
+ char temp_subvol [FILENAME_MAX];
+ char *zptr;
+ char *ptr;
+ char *cptr = &cur_subvol[0];
+ char *tptr = &temp_subvol[0];
+ short err;
+ FILE *tempf;
+ int attempts;
+
+ t = (char *)malloc(NAMELEN); /* malloc here as you cannot free */
+ /* tmpnam allocated storage later */
+
+ zptr = strrchr(zip, TANDEM_DELIMITER);
+
+ if (zptr != NULL) {
+ /* ZIP file specifies a Subvol so make temp file there so it can just
+ be renamed at end */
+
+ *tptr = *cptr = '\0';
+ strcat(cptr, getenv("DEFAULTS"));
+
+ strncat(tptr, zip, _min(FILENAME_MAX, (zptr - zip)) ); /* temp subvol */
+ strncat(t, zip, _min(NAMELEN, ((zptr - zip) + 1)) ); /* temp stem */
+
+ err = chvol(tptr);
+ ptr = t + strlen(t); /* point to end of stem */
+ }
+ else
+ ptr = t;
+
+ /* If two zips are running in same subvol then we can get contention problems
+ with the temporary filename. As a work around we attempt to create
+ the file here, and if it already exists we get a new temporary name */
+
+ attempts = 0;
+ do {
+ attempts++;
+ tmpnam(ptr); /* Add filename */
+ tempf = zfopen(ptr, FOPW_TMP); /* Attempt to create file */
+ } while (tempf == NULL && attempts < 100);
+
+ if (attempts >= 100) {
+ ziperr(ZE_TEMP, "Could not get unique temp file name");
+ }
+
+ fclose(tempf);
+
+ if (zptr != NULL) {
+ err = chvol(cptr); /* Put ourself back to where we came in */
+ }
+
+ return t;
+
+# else /* !CMS_MVS && !TANDEM */
+/*
+ * Do something with TMPDIR, TMP, TEMP ????
+ */
+ if (tempath != NULL)
+ {
+ if ((t = malloc(strlen(tempath) + 12)) == NULL)
+ return NULL;
+ strcpy(t, tempath);
+
+# if (!defined(VMS) && !defined(TOPS20))
+# ifdef MSDOS
+ {
+ char c = (char)lastchar(t);
+ if (c != '/' && c != ':' && c != '\\')
+ strcat(t, "/");
+ }
+# else
+
+# ifdef AMIGA
+ {
+ char c = (char)lastchar(t);
+ if (c != '/' && c != ':')
+ strcat(t, "/");
+ }
+# else /* !AMIGA */
+# ifdef RISCOS
+ if (lastchar(t) != '.')
+ strcat(t, ".");
+# else /* !RISCOS */
+
+# ifdef QDOS
+ if (lastchar(t) != '_')
+ strcat(t, "_");
+# else
+ if (lastchar(t) != '/')
+ strcat(t, "/");
+# endif /* ?QDOS */
+# endif /* ?RISCOS */
+# endif /* ?AMIGA */
+# endif /* ?MSDOS */
+# endif /* !VMS && !TOPS20 */
+ }
+ else
+ {
+ if ((t = malloc(12)) == NULL)
+ return NULL;
+ *t = 0;
+ }
+# ifdef NO_MKTEMP
+ {
+ char *p = t + strlen(t);
+ sprintf(p, "%08lx", (ulg)time(NULL));
+ return t;
+ }
+# else
+ strcat(t, "ziXXXXXX"); /* must use lowercase for Linux dos file system */
+# if defined(UNIX) && !defined(NO_MKSTEMP)
+ /* tempname should not be called */
+ return t;
+# else
+ return mktemp(t);
+# endif
+# endif /* NO_MKTEMP */
+# endif /* TANDEM */
+# endif /* CMS_MVS */
+}
+#endif /* !VMS */
+
+int fcopy(f, g, n)
+ FILE *f, *g; /* source and destination files */
+ /* now use uzoff_t for all file sizes 5/14/05 CS */
+ uzoff_t n; /* number of bytes to copy or -1 for all */
+/* Copy n bytes from file *f to file *g, or until EOF if (zoff_t)n == -1.
+ Return an error code in the ZE_ class. */
+{
+ char *b; /* malloc'ed buffer for copying */
+ extent k; /* result of fread() */
+ uzoff_t m; /* bytes copied so far */
+
+ if ((b = malloc(CBSZ)) == NULL)
+ return ZE_MEM;
+ m = 0;
+ while (n == (uzoff_t)(-1L) || m < n)
+ {
+ if ((k = fread(b, 1, n == (uzoff_t)(-1) ?
+ CBSZ : (n - m < CBSZ ? (extent)(n - m) : CBSZ), f)) == 0)
+ {
+ if (ferror(f))
+ {
+ free((zvoid *)b);
+ return ZE_READ;
+ }
+ else
+ break;
+ }
+ if (fwrite(b, 1, k, g) != k)
+ {
+ free((zvoid *)b);
+ fprintf(mesg," fcopy: write error\n");
+ return ZE_TEMP;
+ }
+ m += k;
+ }
+ free((zvoid *)b);
+ return ZE_OK;
+}
+
+
+/* from zipfile.c */
+
+#ifdef THEOS
+ /* Macros cause stack overflow in compiler */
+ ush SH(uch* p) { return ((ush)(uch)((p)[0]) | ((ush)(uch)((p)[1]) << 8)); }
+ ulg LG(uch* p) { return ((ulg)(SH(p)) | ((ulg)(SH((p)+2)) << 16)); }
+#else /* !THEOS */
+ /* Macros for converting integers in little-endian to machine format */
+# define SH(a) ((ush)(((ush)(uch)(a)[0]) | (((ush)(uch)(a)[1]) << 8)))
+# define LG(a) ((ulg)SH(a) | ((ulg)SH((a)+2) << 16))
+# ifdef ZIP64_SUPPORT /* zip64 support 08/31/2003 R.Nausedat */
+# define LLG(a) ((zoff_t)LG(a) | ((zoff_t)LG((a)+4) << 32))
+# endif
+#endif /* ?THEOS */
+
+
+/* always copies from global in_file to global output file y */
+int bfcopy(n)
+ /* now use uzoff_t for all file sizes 5/14/05 CS */
+ uzoff_t n; /* number of bytes to copy or -1 for all */
+/* Copy n bytes from in_file to out_file, or until EOF if (zoff_t)n == -1.
+
+ Normally we have the compressed size from either the central directory
+ entry or the local header.
+
+ If n != -1 and EOF, close current split and open next and continue
+ copying.
+
+ If n == -2, copy until find the extended header (data descriptor). Only
+ used for -FF when no size available.
+
+ If fix == 1 calculate CRC of input entry and verify matches.
+
+ If fix == 2 and this entry using data descriptor keep a sliding
+ window in the buffer for looking for signature.
+
+ Return an error code in the ZE_ class. */
+{
+ char *b; /* malloc'ed buffer for copying */
+ extent k; /* result of fread() */
+ uzoff_t m; /* bytes copied so far */
+ extent brd; /* bytes to read */
+ zoff_t data_start = 0;
+ zoff_t des_start = 0;
+ char *split_path;
+ extent kk;
+ int i;
+ char sbuf[4]; /* buffer for sliding signature window for fix = 2 */
+ int des = 0; /* this entry has data descriptor to find */
+
+ if ((b = malloc(CBSZ)) == NULL)
+ return ZE_MEM;
+
+ if (copy_only && !display_globaldots) {
+ /* initialize dot count */
+ dot_count = -1;
+ }
+
+ if (fix == 2 && n == (uzoff_t) -2) {
+ data_start = zftello(in_file);
+ for (kk = 0; kk < 4; kk++)
+ sbuf[kk] = 0;
+ des = 1;
+ }
+
+ des_good = 0;
+
+ m = 0;
+ while (des || n == (uzoff_t)(-1L) || m < n)
+ {
+ if (des || n == (uzoff_t)(-1))
+ brd = CBSZ;
+ else
+ brd = (n - m < CBSZ ? (extent)(n - m) : CBSZ);
+
+ des_start = zftello(in_file);
+
+ if ((k = fread(b, 1, brd, in_file)) == 0)
+ {
+ if (fix == 2 && k < brd) {
+ free((zvoid *)b);
+ return ZE_READ;
+ }
+ else if (ferror(in_file))
+ {
+ free((zvoid *)b);
+ return ZE_READ;
+ }
+ else {
+ break;
+ }
+ }
+
+
+ /* end at extended local header (data descriptor) signature */
+ if (des) {
+ des_crc = 0;
+ des_csize = 0;
+ des_usize = 0;
+
+ /* If first 4 bytes in buffer are data descriptor signature then
+ try to read the data descriptor.
+ If not, scan for signature and break if found, let bfwrite flush
+ the data and then next read should put the data descriptor at
+ the beginning of the buffer.
+ */
+
+ if (
+ (b[0] != 0x50 /*'P' except EBCDIC*/ ||
+ b[1] != 0x4b /*'K' except EBCDIC*/ ||
+ b[2] != '\07' ||
+ b[3] != '\010')) {
+ /* buffer is not start of data descriptor */
+
+ for (kk = 0; kk < k; kk++) {
+ /* add byte to end of sbuf */
+ for (i = 0; i < 3; i++)
+ sbuf[i] = sbuf[i + 1];
+ sbuf[3] = b[kk];
+
+ /* see if this is signature */
+ if (
+ (sbuf[0] == 0x50 /*'P' except EBCDIC*/ &&
+ sbuf[1] == 0x4b /*'K' except EBCDIC*/ &&
+ sbuf[2] == '\07' &&
+ sbuf[3] == '\010')) {
+ kk -= 3;
+ if (zfseeko(in_file, bytes_this_split + kk, SEEK_SET) != 0) {
+ /* seek error */
+ ZIPERR(ZE_READ, "seek failed reading descriptor");
+ }
+ des_start = zftello(in_file);
+ k = kk;
+ break;
+ }
+ }
+ }
+ else
+
+ /* signature at start of buffer */
+ {
+ des_good = 0;
+
+#ifdef ZIP64_SUPPORT
+ if (zip64_entry) {
+
+ /* read Zip64 data descriptor */
+ if (k < 24) {
+ /* not enough bytes, so can't be data descriptor
+ as data descriptors can't be split across splits
+ */
+ }
+ else
+ {
+ /* read the Zip64 descriptor */
+
+ des_crc = LG(b + 4);
+ des_csize = LLG(b + 8);
+ des_usize = LLG(b + 16);
+
+ /* if this is the right data descriptor then the sizes should match */
+ if ((uzoff_t)des_start - (uzoff_t)data_start != des_csize) {
+ /* apparently this signature does not go with this data so skip */
+
+ /* write out signature as data */
+ k = 4;
+ if (zfseeko(in_file, des_start + k, SEEK_SET) != 0) {
+ /* seek error */
+ ZIPERR(ZE_READ, "seek failed reading descriptor");
+ }
+ if (bfwrite(b, 1, k, BFWRITE_DATA) != k)
+ {
+ free((zvoid *)b);
+ fprintf(mesg," fcopy: write error\n");
+ return ZE_TEMP;
+ }
+ m += k;
+ continue;
+ }
+ else
+ {
+ /* apparently this is the correct data descriptor */
+
+ /* we should check the CRC but would need to inflate
+ the data */
+
+ /* skip descriptor as will write out later */
+ des_good = 1;
+ k = 24;
+ data_start = zftello(in_file);
+ if (zfseeko(in_file, des_start + k, SEEK_SET) != 0) {
+ /* seek error */
+ ZIPERR(ZE_READ, "seek failed reading descriptor");
+ }
+ data_start = zftello(in_file);
+ }
+ }
+
+ }
+ else
+#endif
+ {
+ /* read standard data descriptor */
+
+ if (k < 16) {
+ /* not enough bytes, so can't be data descriptor
+ as data descriptors can't be split across splits
+ */
+ }
+ else
+ {
+ /* read the descriptor */
+
+ des_crc = LG(b + 4);
+ des_csize = LG(b + 8);
+ des_usize = LG(b + 12);
+
+ /* if this is the right data descriptor then the sizes should match */
+ if ((uzoff_t)des_start - (uzoff_t)data_start != des_csize) {
+ /* apparently this signature does not go with this data so skip */
+
+ /* write out signature as data */
+ k = 4;
+ if (zfseeko(in_file, des_start + k, SEEK_SET) != 0) {
+ /* seek error */
+ ZIPERR(ZE_READ, "seek failed reading descriptor");
+ }
+ if (bfwrite(b, 1, k, BFWRITE_DATA) != k)
+ {
+ free((zvoid *)b);
+ fprintf(mesg," fcopy: write error\n");
+ return ZE_TEMP;
+ }
+ m += k;
+ continue;
+ }
+ else
+ {
+ /* apparently this is the correct data descriptor */
+
+ /* we should check the CRC but this does not work for
+ encrypted data */
+
+ /* skip descriptor as will write out later */
+ des_good = 1;
+ data_start = zftello(in_file);
+ k = 16;
+ if (zfseeko(in_file, des_start + k, SEEK_SET) != 0) {
+ /* seek error */
+ ZIPERR(ZE_READ, "seek failed reading descriptor");
+ }
+ data_start = zftello(in_file);
+ }
+ }
+
+
+ }
+ }
+ }
+
+
+ if (des_good) {
+ /* skip descriptor as will write out later */
+ } else {
+ /* write out apparently wrong descriptor as data */
+ if (bfwrite(b, 1, k, BFWRITE_DATA) != k)
+ {
+ free((zvoid *)b);
+ fprintf(mesg," fcopy: write error\n");
+ return ZE_TEMP;
+ }
+ m += k;
+ }
+
+ if (copy_only && !display_globaldots) {
+ if (dot_size > 0) {
+ /* initial space */
+ if (noisy && dot_count == -1) {
+#ifndef WINDLL
+ putc(' ', mesg);
+ fflush(mesg);
+#else
+ fprintf(stdout,"%c",' ');
+#endif
+ dot_count++;
+ }
+ dot_count += k;
+ if (dot_size <= dot_count) dot_count = 0;
+ }
+ if ((verbose || noisy) && dot_size && !dot_count) {
+#ifndef WINDLL
+ putc('.', mesg);
+ fflush(mesg);
+#else
+ fprintf(stdout,"%c",'.');
+#endif
+ mesg_line_started = 1;
+ }
+ }
+
+ if (des_good)
+ break;
+
+ if (des)
+ continue;
+
+ if ((des || n != (uzoff_t)(-1L)) && m < n && feof(in_file)) {
+ /* open next split */
+ current_in_disk++;
+
+ if (current_in_disk >= total_disks) {
+ /* done */
+ break;
+
+ } else if (current_in_disk == total_disks - 1) {
+ /* last disk is archive.zip */
+ if ((split_path = malloc(strlen(in_path) + 1)) == NULL) {
+ zipwarn("reading archive: ", in_path);
+ return ZE_MEM;
+ }
+ strcpy(split_path, in_path);
+ } else {
+ /* other disks are archive.z01, archive.z02, ... */
+ split_path = get_in_split_path(in_path, current_in_disk);
+ }
+
+ fclose(in_file);
+
+ /* open the split */
+ while ((in_file = zfopen(split_path, FOPR)) == NULL) {
+ int r = 0;
+
+ /* could not open split */
+
+ if (fix == 1 && skip_this_disk) {
+ free(split_path);
+ free((zvoid *)b);
+ return ZE_FORM;
+ }
+
+ /* Ask for directory with split. Updates in_path */
+ r = ask_for_split_read_path(current_in_disk);
+ if (r == ZE_ABORT) {
+ zipwarn("could not find split: ", split_path);
+ free(split_path);
+ free((zvoid *)b);
+ return ZE_ABORT;
+ }
+ if (r == ZE_EOF) {
+ zipmessage_nl("", 1);
+ zipwarn("user ended reading - closing archive", "");
+ free(split_path);
+ free((zvoid *)b);
+ return ZE_EOF;
+ }
+ if (fix == 2 && skip_this_disk) {
+ /* user asked to skip this disk */
+ zipwarn("skipping split file: ", split_path);
+ current_in_disk++;
+ }
+
+ if (current_in_disk == total_disks - 1) {
+ /* last disk is archive.zip */
+ if ((split_path = malloc(strlen(in_path) + 1)) == NULL) {
+ zipwarn("reading archive: ", in_path);
+ return ZE_MEM;
+ }
+ strcpy(split_path, in_path);
+ } else {
+ /* other disks are archive.z01, archive.z02, ... */
+ split_path = get_in_split_path(zipfile, current_in_disk);
+ }
+ }
+ if (fix == 2 && skip_this_disk) {
+ /* user asked to skip this disk */
+ free(split_path);
+ free((zvoid *)b);
+ return ZE_FORM;
+ }
+ free(split_path);
+ }
+ }
+ free((zvoid *)b);
+ return ZE_OK;
+}
+
+
+
+#ifdef NO_RENAME
+int rename(from, to)
+ZCONST char *from;
+ZCONST char *to;
+{
+ unlink(to);
+ if (link(from, to) == -1)
+ return -1;
+ if (unlink(from) == -1)
+ return -1;
+ return 0;
+}
+
+#endif /* NO_RENAME */
+
+
+#ifdef ZMEM
+
+/************************/
+/* Function memset() */
+/************************/
+
+/*
+ * memset - for systems without it
+ * bill davidsen - March 1990
+ */
+
+char *
+memset(buf, init, len)
+register char *buf; /* buffer loc */
+register int init; /* initializer */
+register unsigned int len; /* length of the buffer */
+{
+ char *start;
+
+ start = buf;
+ while (len--) *(buf++) = init;
+ return(start);
+}
+
+
+/************************/
+/* Function memcpy() */
+/************************/
+
+char *
+memcpy(dst,src,len) /* v2.0f */
+register char *dst, *src;
+register unsigned int len;
+{
+ char *start;
+
+ start = dst;
+ while (len--)
+ *dst++ = *src++;
+ return(start);
+}
+
+
+/************************/
+/* Function memcmp() */
+/************************/
+
+int
+memcmp(b1,b2,len) /* jpd@usl.edu -- 11/16/90 */
+register char *b1, *b2;
+register unsigned int len;
+{
+
+ if (len) do { /* examine each byte (if any) */
+ if (*b1++ != *b2++)
+ return (*((uch *)b1-1) - *((uch *)b2-1)); /* exit when miscompare */
+ } while (--len);
+
+ return(0); /* no miscompares, yield 0 result */
+}
+
+#endif /* ZMEM */
+
+
+/*------------------------------------------------------------------
+ * Split archives
+ */
+
+
+/* ask_for_split_read_path
+ *
+ * If the next split file is not in the current directory, ask
+ * the user where it is.
+ *
+ * in_path is the base path for reading splits and is usually
+ * the same as zipfile. The path in in_path must be the archive
+ * file ending in .zip as this is assumed by get_in_split_path().
+ *
+ * Updates in_path if changed. Returns ZE_OK if OK or ZE_ABORT if
+ * user cancels reading archive.
+ *
+ * If fix = 1 then allow skipping disk (user may not have it).
+ */
+
+#define SPLIT_MAXPATH (FNMAX + 4010)
+
+int ask_for_split_read_path(current_disk)
+ ulg current_disk;
+{
+ FILE *f;
+ int is_readable = 0;
+ int i;
+ char *split_dir = NULL;
+ char *archive_name = NULL;
+ char *split_name = NULL;
+ char *split_path = NULL;
+ char buf[SPLIT_MAXPATH + 100];
+
+ /* get split path */
+ split_path = get_in_split_path(in_path, current_disk);
+
+ /* get the directory */
+ if ((split_dir = malloc(strlen(in_path) + 40)) == NULL) {
+ ZIPERR(ZE_MEM, "split path");
+ }
+ strcpy(split_dir, in_path);
+
+ /* remove any name at end */
+ for (i = strlen(split_dir) - 1; i >= 0; i--) {
+ if (split_dir[i] == '/' || split_dir[i] == '\\'
+ || split_dir[i] == ':') {
+ split_dir[i + 1] = '\0';
+ break;
+ }
+ }
+ if (i < 0)
+ split_dir[0] = '\0';
+
+ /* get the name of the archive */
+ if ((archive_name = malloc(strlen(in_path) + 1)) == NULL) {
+ ZIPERR(ZE_MEM, "split path");
+ }
+ if (strlen(in_path) == strlen(split_dir)) {
+ archive_name[0] = '\0';
+ } else {
+ strcpy(archive_name, in_path + strlen(split_dir));
+ }
+
+ /* get the name of the split */
+ if ((split_name = malloc(strlen(split_path) + 1)) == NULL) {
+ ZIPERR(ZE_MEM, "split path");
+ }
+ if (strlen(in_path) == strlen(split_dir)) {
+ split_name[0] = '\0';
+ } else {
+ strcpy(split_name, split_path + strlen(split_dir));
+ }
+ if (i < 0) {
+ strcpy(split_dir, "(current directory)");
+ }
+
+ fprintf(mesg, "\n\nCould not find:\n");
+ fprintf(mesg, " %s\n", split_path);
+ /*
+ fprintf(mesg, "Please enter the path directory (. for cur dir) where\n");
+ fprintf(mesg, " %s\n", split_name);
+ fprintf(mesg, "is located\n");
+ */
+ for (;;) {
+ if (is_readable) {
+ fprintf(mesg, "\nHit c (change path to where this split file is)");
+ fprintf(mesg, "\n q (abort archive - quit)");
+ fprintf(mesg, "\n or ENTER (continue with this split): ");
+ } else {
+ if (fix == 1) {
+ fprintf(mesg, "\nHit c (change path to where this split file is)");
+ fprintf(mesg, "\n s (skip this split)");
+ fprintf(mesg, "\n q (abort archive - quit)");
+ fprintf(mesg, "\n or ENTER (try reading this split again): ");
+ } else if (fix == 2) {
+ fprintf(mesg, "\nHit c (change path to where this split file is)");
+ fprintf(mesg, "\n s (skip this split)");
+ fprintf(mesg, "\n q (abort archive - quit)");
+ fprintf(mesg, "\n e (end this archive - no more splits)");
+ fprintf(mesg, "\n z (look for .zip split - the last split)");
+ fprintf(mesg, "\n or ENTER (try reading this split again): ");
+ } else {
+ fprintf(mesg, "\nHit c (change path to where this split file is)");
+ fprintf(mesg, "\n q (abort archive - quit)");
+ fprintf(mesg, "\n or ENTER (try reading this split again): ");
+ }
+ }
+ fflush(mesg);
+ fgets(buf, SPLIT_MAXPATH, stdin);
+ /* remove any newline */
+ for (i = 0; buf[i]; i++) {
+ if (buf[i] == '\n') {
+ buf[i] = '\0';
+ break;
+ }
+ }
+ if (toupper(buf[0]) == 'Q') {
+ return ZE_ABORT;
+ } else if ((fix == 1 || fix == 2) && toupper(buf[0]) == 'S') {
+ /*
+ fprintf(mesg, "\nSkip this split/disk? (files in this split will not be recovered) [n/y] ");
+ fflush(mesg);
+ fgets(buf, SPLIT_MAXPATH, stdin);
+ if (buf[0] == 'y' || buf[0] == 'Y') {
+ */
+ skip_this_disk = current_in_disk + 1;
+ return ZE_FORM;
+ } else if (toupper(buf[0]) == 'C') {
+ fprintf(mesg, "\nEnter path where this split is (ENTER = same dir, . = current dir)");
+ fprintf(mesg, "\n: ");
+ fflush(mesg);
+ fgets(buf, SPLIT_MAXPATH, stdin);
+ is_readable = 0;
+ /* remove any newline */
+ for (i = 0; buf[i]; i++) {
+ if (buf[i] == '\n') {
+ buf[i] = '\0';
+ break;
+ }
+ }
+ if (buf[0] == '\0') {
+ /* Hit ENTER so try old path again - could be removable media was changed */
+ strcpy(buf, split_path);
+ }
+ } else if (fix == 2 && toupper(buf[0]) == 'E') {
+ /* no more splits to read */
+ return ZE_EOF;
+ } else if (fix == 2 && toupper(buf[0]) == 'Z') {
+ total_disks = current_disk + 1;
+ free(split_path);
+ split_path = get_in_split_path(in_path, current_disk);
+ buf[0] = '\0';
+ strncat(buf, split_path, SPLIT_MAXPATH);
+ }
+ if (strlen(buf) > 0) {
+ /* changing path */
+
+ /* check if user wants current directory */
+ if (buf[0] == '.' && buf[1] == '\0') {
+ buf[0] = '\0';
+ }
+ /* remove any name at end */
+ for (i = strlen(buf); i >= 0; i--) {
+ if (buf[i] == '/' || buf[i] == '\\'
+ || buf[i] == ':') {
+ buf[i + 1] = '\0';
+ break;
+ }
+ }
+ /* update base_path to newdir/split_name - in_path is the .zip file path */
+ free(in_path);
+ if (i < 0) {
+ /* just name so current directory */
+ strcpy(buf, "(current directory)");
+ if (archive_name == NULL) {
+ i = 0;
+ } else {
+ i = strlen(archive_name);
+ }
+ if ((in_path = malloc(strlen(archive_name) + 40)) == NULL) {
+ ZIPERR(ZE_MEM, "split path");
+ }
+ strcpy(in_path, archive_name);
+ } else {
+ /* not the current directory */
+ /* remove any name at end */
+ for (i = strlen(buf); i >= 0; i--) {
+ if (buf[i] == '/') {
+ buf[i + 1] = '\0';
+ break;
+ }
+ }
+ if (i < 0) {
+ buf[0] = '\0';
+ }
+ if ((in_path = malloc(strlen(buf) + strlen(archive_name) + 40)) == NULL) {
+ ZIPERR(ZE_MEM, "split path");
+ }
+ strcpy(in_path, buf);
+ strcat(in_path, archive_name);
+ }
+
+ free(split_path);
+
+ /* get split path */
+ split_path = get_in_split_path(in_path, current_disk);
+
+ free(split_dir);
+ if ((split_dir = malloc(strlen(in_path) + 40)) == NULL) {
+ ZIPERR(ZE_MEM, "split path");
+ }
+ strcpy(split_dir, in_path);
+ /* remove any name at end */
+ for (i = strlen(split_dir); i >= 0; i--) {
+ if (split_dir[i] == '/') {
+ split_dir[i + 1] = '\0';
+ break;
+ }
+ }
+
+ /* try to open it */
+ if ((f = fopen(split_path, "r")) == NULL) {
+ fprintf(mesg, "\nCould not find or open\n");
+ fprintf(mesg, " %s\n", split_path);
+ /*
+ fprintf(mesg, "Please enter the path (. for cur dir) where\n");
+ fprintf(mesg, " %s\n", split_name);
+ fprintf(mesg, "is located\n");
+ */
+ continue;
+ }
+ fclose(f);
+ is_readable = 1;
+ fprintf(mesg, "Found: %s\n", split_path);
+ } else {
+ /* try to open it */
+ if ((f = fopen(split_path, "r")) == NULL) {
+ fprintf(mesg, "\nCould not find or open\n");
+ fprintf(mesg, " %s\n", split_path);
+ /*
+ fprintf(mesg, "Please enter the path (. for cur dir) where\n");
+ fprintf(mesg, " %s\n", split_name);
+ fprintf(mesg, "is located\n");
+ */
+ continue;
+ }
+ fclose(f);
+ is_readable = 1;
+ fprintf(mesg, "\nFound: %s\n", split_path);
+ break;
+ }
+ }
+ free(archive_name);
+ free(split_dir);
+ free(split_name);
+
+ return ZE_OK;
+}
+
+
+/* ask_for_split_write_path
+ *
+ * Verify the directory for the next split. Called
+ * when -sp is used to pause between writing splits.
+ *
+ * Updates out_path and return 1 if OK or 0 if cancel
+ */
+int ask_for_split_write_path(current_disk)
+ ulg current_disk;
+{
+ unsigned int num = (unsigned int)current_disk + 1;
+ int i;
+ char *split_dir = NULL;
+ char *split_name = NULL;
+ char buf[FNMAX + 40];
+
+ /* get the directory */
+ if ((split_dir = malloc(strlen(out_path) + 40)) == NULL) {
+ ZIPERR(ZE_MEM, "split path");
+ }
+ strcpy(split_dir, out_path);
+
+ /* remove any name at end */
+ for (i = strlen(split_dir); i >= 0; i--) {
+ if (split_dir[i] == '/' || split_dir[i] == '\\'
+ || split_dir[i] == ':') {
+ split_dir[i + 1] = '\0';
+ break;
+ }
+ }
+
+ /* get the name of the split */
+ if ((split_name = malloc(strlen(out_path) + 1)) == NULL) {
+ ZIPERR(ZE_MEM, "split path");
+ }
+ if (strlen(out_path) == strlen(split_dir)) {
+ split_name[0] = '\0';
+ } else {
+ strcpy(split_name, out_path + strlen(split_dir));
+ }
+ if (i < 0) {
+ strcpy(split_dir, "(current directory)");
+ }
+ if (mesg_line_started)
+ fprintf(mesg, "\n");
+ fprintf(mesg, "\nOpening disk %d\n", num);
+ fprintf(mesg, "Hit ENTER to write to default path of\n");
+ fprintf(mesg, " %s\n", split_dir);
+ fprintf(mesg, "or enter a new directory path (. for cur dir) and hit ENTER\n");
+ for (;;) {
+ fprintf(mesg, "\nPath (or hit ENTER to continue): ");
+ fflush(mesg);
+ fgets(buf, FNMAX, stdin);
+ /* remove any newline */
+ for (i = 0; buf[i]; i++) {
+ if (buf[i] == '\n') {
+ buf[i] = '\0';
+ break;
+ }
+ }
+ if (strlen(buf) > 0) {
+ /* changing path */
+
+ /* current directory */
+ if (buf[0] == '.' && buf[1] == '\0') {
+ buf[0] = '\0';
+ }
+ /* remove any name at end */
+ for (i = strlen(buf); i >= 0; i--) {
+ if (buf[i] == '/' || buf[i] == '\\'
+ || buf[i] == ':') {
+ buf[i + 1] = '\0';
+ break;
+ }
+ }
+ /* update out_path to newdir/split_name */
+ free(out_path);
+ if (i < 0) {
+ /* just name so current directory */
+ strcpy(buf, "(current directory)");
+ if (split_name == NULL) {
+ i = 0;
+ } else {
+ i = strlen(split_name);
+ }
+ if ((out_path = malloc(strlen(split_name) + 40)) == NULL) {
+ ZIPERR(ZE_MEM, "split path");
+ }
+ strcpy(out_path, split_name);
+ } else {
+ /* not the current directory */
+ /* remove any name at end */
+ for (i = strlen(buf); i >= 0; i--) {
+ if (buf[i] == '/') {
+ buf[i + 1] = '\0';
+ break;
+ }
+ }
+ if (i < 0) {
+ buf[0] = '\0';
+ }
+ if ((out_path = malloc(strlen(buf) + strlen(split_name) + 40)) == NULL) {
+ ZIPERR(ZE_MEM, "split path");
+ }
+ strcpy(out_path, buf);
+ strcat(out_path, split_name);
+ }
+ fprintf(mesg, "Writing to:\n %s\n", buf);
+ free(split_name);
+ free(split_dir);
+ if ((split_dir = malloc(strlen(out_path) + 40)) == NULL) {
+ ZIPERR(ZE_MEM, "split path");
+ }
+ strcpy(split_dir, out_path);
+ /* remove any name at end */
+ for (i = strlen(split_dir); i >= 0; i--) {
+ if (split_dir[i] == '/') {
+ split_dir[i + 1] = '\0';
+ break;
+ }
+ }
+ if ((split_name = malloc(strlen(out_path) + 1)) == NULL) {
+ ZIPERR(ZE_MEM, "split path");
+ }
+ strcpy(split_name, out_path + strlen(split_dir));
+ } else {
+ break;
+ }
+ }
+ free(split_dir);
+ free(split_name);
+
+ /* for now no way out except Ctrl C */
+ return 1;
+}
+
+
+/* split_name
+ *
+ * get name of split being read
+ */
+char *get_in_split_path(base_path, disk_number)
+ char *base_path;
+ ulg disk_number;
+{
+ char *split_path = NULL;
+ int base_len = 0;
+ int path_len = 0;
+ ulg num = disk_number + 1;
+ char ext[6];
+#ifdef VMS
+ int vers_len; /* File version length. */
+ char *vers_ptr; /* File version string. */
+#endif /* def VMS */
+
+ /*
+ * A split has extension z01, z02, ..., z99, z100, z101, ... z999
+ * We currently support up to .z99999
+ * WinZip will also read .100, .101, ... but AppNote 6.2.2 uses above
+ * so use that. Means on DOS can only have 100 splits.
+ */
+
+ if (num == total_disks) {
+ /* last disk is base path */
+ if ((split_path = malloc(strlen(base_path) + 1)) == NULL) {
+ ZIPERR(ZE_MEM, "base path");
+ }
+ strcpy(split_path, base_path);
+
+ return split_path;
+ } else {
+ if (num > 99999) {
+ ZIPERR(ZE_BIG, "More than 99999 splits needed");
+ }
+ sprintf(ext, "z%02lu", num);
+ }
+
+ /* create path for this split - zip.c checked for .zip extension */
+ base_len = strlen(base_path) - 3;
+ path_len = base_len + strlen(ext);
+
+#ifdef VMS
+ /* On VMS, locate the file version, and adjust base_len accordingly.
+ Note that path_len is correct, as-is.
+ */
+ vers_ptr = vms_file_version( base_path);
+ vers_len = strlen( vers_ptr);
+ base_len -= vers_len;
+#endif /* def VMS */
+
+ if ((split_path = malloc(path_len + 1)) == NULL) {
+ ZIPERR(ZE_MEM, "split path");
+ }
+ /* copy base_path except for end zip */
+ strcpy(split_path, base_path);
+ split_path[base_len] = '\0';
+ /* add extension */
+ strcat(split_path, ext);
+
+#ifdef VMS
+ /* On VMS, append (preserve) the file version. */
+ strcat(split_path, vers_ptr);
+#endif /* def VMS */
+
+ return split_path;
+}
+
+
+/* split_name
+ *
+ * get name of split being written
+ */
+char *get_out_split_path(base_path, disk_number)
+ char *base_path;
+ ulg disk_number;
+{
+ char *split_path = NULL;
+ int base_len = 0;
+ int path_len = 0;
+ ulg num = disk_number + 1;
+ char ext[6];
+#ifdef VMS
+ int vers_len; /* File version length. */
+ char *vers_ptr; /* File version string. */
+#endif /* def VMS */
+
+ /*
+ * A split has extension z01, z02, ..., z99, z100, z101, ... z999
+ * We currently support up to .z99999
+ * WinZip will also read .100, .101, ... but AppNote 6.2.2 uses above
+ * so use that. Means on DOS can only have 100 splits.
+ */
+
+ if (num > 99999) {
+ ZIPERR(ZE_BIG, "More than 99999 splits needed");
+ }
+ sprintf(ext, "z%02lu", num);
+
+ /* create path for this split - zip.c checked for .zip extension */
+ base_len = strlen(base_path) - 3;
+ path_len = base_len + strlen(ext);
+
+#ifdef VMS
+ /* On VMS, locate the file version, and adjust base_len accordingly.
+ Note that path_len is correct, as-is.
+ */
+ vers_ptr = vms_file_version( base_path);
+ vers_len = strlen( vers_ptr);
+ base_len -= vers_len;
+#endif /* def VMS */
+
+ if ((split_path = malloc(path_len + 1)) == NULL) {
+ ZIPERR(ZE_MEM, "split path");
+ }
+ /* copy base_path except for end zip */
+ strcpy(split_path, base_path);
+ split_path[base_len] = '\0';
+ /* add extension */
+ strcat(split_path, ext);
+
+#ifdef VMS
+ /* On VMS, append (preserve) the file version. */
+ strcat(split_path, vers_ptr);
+#endif /* def VMS */
+
+ return split_path;
+}
+
+/* close_split
+ *
+ * close a split - assume that the paths needed for the splits are
+ * available.
+ */
+int close_split(disk_number, tempfile, temp_name)
+ ulg disk_number;
+ FILE *tempfile;
+ char *temp_name;
+{
+ char *split_path = NULL;
+
+ split_path = get_out_split_path(out_path, disk_number);
+
+ if (noisy_splits) {
+ zipmessage("\tClosing split ", split_path);
+ }
+
+ fclose(tempfile);
+
+ rename_split(temp_name, split_path);
+ set_filetype(split_path);
+
+ return ZE_OK;
+}
+
+/* bfwrite
+ Does the fwrite but also counts bytes and does splits */
+size_t bfwrite(buffer, size, count, mode)
+ ZCONST void *buffer;
+ size_t size;
+ size_t count;
+ int mode;
+{
+ size_t bytes_written = 0;
+ size_t r;
+ size_t b = size * count;
+ uzoff_t bytes_left_in_split = 0;
+ size_t bytes_to_write = b;
+
+
+ /* -------------------------------- */
+ /* local header */
+ if (mode == BFWRITE_LOCALHEADER) {
+ /* writing local header - reset entry data count */
+ bytes_this_entry = 0;
+ /* save start of local header so we can rewrite later */
+ current_local_file = y;
+ current_local_disk = current_disk;
+ current_local_offset = bytes_this_split;
+ }
+
+ if (split_size == 0)
+ bytes_left_in_split = bytes_to_write;
+ else
+ bytes_left_in_split = split_size - bytes_this_split;
+
+ if (bytes_to_write > bytes_left_in_split) {
+ if (mode == BFWRITE_HEADER ||
+ mode == BFWRITE_LOCALHEADER ||
+ mode == BFWRITE_CENTRALHEADER) {
+ /* if can't write entire header save for next split */
+ bytes_to_write = 0;
+ } else {
+ /* normal data so fill the split */
+ bytes_to_write = (size_t)bytes_left_in_split;
+ }
+ }
+
+ /* -------------------------------- */
+ /* central header */
+ if (mode == BFWRITE_CENTRALHEADER) {
+ /* set start disk for CD */
+ if (cd_start_disk == (ulg)-1) {
+ cd_start_disk = current_disk;
+ cd_start_offset = bytes_this_split;
+ }
+ cd_entries_this_disk++;
+ total_cd_entries++;
+ }
+
+ /* -------------------------------- */
+ if (bytes_to_write > 0) {
+ /* write out the bytes for this split */
+ r = fwrite(buffer, size, bytes_to_write, y);
+ bytes_written += r;
+ bytes_to_write = b - r;
+ bytes_this_split += r;
+ if (mode == BFWRITE_DATA)
+ /* if data descriptor do not include in count */
+ bytes_this_entry += r;
+ } else {
+ bytes_to_write = b;
+ }
+
+ if (bytes_to_write > 0) {
+ if (split_method) {
+ /* still bytes to write so close split and open next split */
+ bytes_prev_splits += bytes_this_split;
+
+ if (split_method == 1 && ferror(y)) {
+ /* if writing all splits to same place and have problem then bad */
+ ZIPERR(ZE_WRITE, "Could not write split");
+ }
+
+ if (split_method == 2 && ferror(y)) {
+ /* A split must be at least 64K except last .zip split */
+ if (bytes_this_split < 64 * (uzoff_t)0x400) {
+ ZIPERR(ZE_WRITE, "Not enough space to write split");
+ }
+ }
+
+ /* close this split */
+ if (split_method == 1 && current_local_disk == current_disk) {
+ /* keep split open so can update it */
+ current_local_tempname = tempzip;
+ } else {
+ /* close split */
+ close_split(current_disk, y, tempzip);
+ y = NULL;
+ free(tempzip);
+ tempzip = NULL;
+ }
+ cd_entries_this_disk = 0;
+ bytes_this_split = 0;
+
+ /* increment disk - disks are numbered 0, 1, 2, ... and
+ splits are 01, 02, ... */
+ current_disk++;
+
+ if (split_method == 2 && split_bell) {
+ /* bell when pause to ask for next split */
+ putc('\007', mesg);
+ fflush(mesg);
+ }
+
+ for (;;) {
+ /* if method 2 pause and allow changing path */
+ if (split_method == 2) {
+ if (ask_for_split_write_path(current_disk) == 0) {
+ ZIPERR(ZE_ABORT, "could not write split");
+ }
+ }
+
+ /* open next split */
+#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 ((y = fdopen(yd, FOPW_TMP)) == NULL) {
+ ZIPERR(ZE_TEMP, tempzip);
+ }
+ }
+#else
+ if ((tempzip = tempname(zipfile)) == NULL) {
+ ZIPERR(ZE_MEM, "allocating temp filename");
+ }
+ if ((y = zfopen(tempzip, FOPW_TMP)) == NULL) {
+ ZIPERR(ZE_TEMP, tempzip);
+ }
+#endif
+
+ r = fwrite((char *)buffer + bytes_written, 1, bytes_to_write, y);
+ bytes_written += r;
+ bytes_this_split += r;
+ if (!(mode == BFWRITE_HEADER ||
+ mode == BFWRITE_LOCALHEADER ||
+ mode == BFWRITE_CENTRALHEADER)) {
+ bytes_this_entry += r;
+ }
+ if (bytes_to_write > r) {
+ /* buffer bigger than split */
+ if (split_method == 2) {
+ /* let user choose another disk */
+ zipwarn("Not enough room on disk", "");
+ continue;
+ } else {
+ ZIPERR(ZE_WRITE, "Not enough room on disk");
+ }
+ }
+ if (mode == BFWRITE_LOCALHEADER ||
+ mode == BFWRITE_HEADER ||
+ mode == BFWRITE_CENTRALHEADER) {
+ if (split_method == 1 && current_local_file &&
+ current_local_disk != current_disk) {
+ /* We're opening a new split because the next header
+ did not fit on the last split. We need to now close
+ the last split and update the pointers for
+ the current split. */
+ close_split(current_local_disk, current_local_file,
+ current_local_tempname);
+ free(current_local_tempname);
+ }
+ current_local_tempname = tempzip;
+ current_local_file = y;
+ current_local_offset = 0;
+ current_local_disk = current_disk;
+ }
+ break;
+ }
+ }
+ else
+ {
+ /* likely have more than fits but no splits */
+
+ /* probably already have error "no space left on device" */
+ /* could let flush_outbuf() handle error but bfwrite() is called for
+ headers also */
+ if (ferror(y))
+ ziperr(ZE_WRITE, "write error on zip file");
+ }
+ }
+
+
+ /* display dots for archive instead of for each file */
+ if (display_globaldots) {
+ if (dot_size > 0) {
+ /* initial space */
+ if (dot_count == -1) {
+#ifndef WINDLL
+ putc(' ', mesg);
+ fflush(mesg);
+#else
+ fprintf(stdout,"%c",' ');
+#endif
+ /* assume a header will be written first, so avoid 0 */
+ dot_count = 1;
+ }
+ /* skip incrementing dot count for small buffers like for headers */
+ if (size * count > 1000) {
+ dot_count++;
+ if (dot_size <= dot_count * (zoff_t)size * (zoff_t)count) dot_count = 0;
+ }
+ }
+ if (dot_size && !dot_count) {
+ dot_count++;
+#ifndef WINDLL
+ putc('.', mesg);
+ fflush(mesg);
+#else
+ fprintf(stdout,"%c",'.');
+#endif
+ mesg_line_started = 1;
+ }
+ }
+
+
+ return bytes_written;
+}
+
+
+#ifdef UNICODE_SUPPORT
+
+/*---------------------------------------------
+ * Unicode conversion functions
+ *
+ * Provided by Paul Kienitz
+ *
+ * Some modifications to work with Zip
+ *
+ *---------------------------------------------
+ */
+
+/*
+ NOTES APPLICABLE TO ALL STRING FUNCTIONS:
+
+ All of the x_to_y functions take parameters for an output buffer and
+ its available length, and return an int. The value returned is the
+ length of the string that the input produces, which may be larger than
+ the provided buffer length. If the returned value is less than the
+ buffer length, then the contents of the buffer will be null-terminated;
+ otherwise, it will not be terminated and may be invalid, possibly
+ stopping in the middle of a multibyte sequence.
+
+ In all cases you may pass NULL as the buffer and/or 0 as the length, if
+ you just want to learn how much space the string is going to require.
+
+ The functions will return -1 if the input is invalid UTF-8 or cannot be
+ encoded as UTF-8.
+*/
+
+/* utility functions for managing UTF-8 and UCS-4 strings */
+
+
+/* utf8_char_bytes
+ *
+ * Returns the number of bytes used by the first character in a UTF-8
+ * string, or -1 if the UTF-8 is invalid or null.
+ */
+local int utf8_char_bytes(utf8)
+ ZCONST char *utf8;
+{
+ int t, r;
+ unsigned lead;
+
+ if (!utf8)
+ return -1; /* no input */
+ lead = (unsigned char) *utf8;
+ if (lead < 0x80)
+ r = 1; /* an ascii-7 character */
+ else if (lead < 0xC0)
+ return -1; /* error: trailing byte without lead byte */
+ else if (lead < 0xE0)
+ r = 2; /* an 11 bit character */
+ else if (lead < 0xF0)
+ r = 3; /* a 16 bit character */
+ else if (lead < 0xF8)
+ r = 4; /* a 21 bit character (the most currently used) */
+ else if (lead < 0xFC)
+ r = 5; /* a 26 bit character (shouldn't happen) */
+ else if (lead < 0xFE)
+ r = 6; /* a 31 bit character (shouldn't happen) */
+ else
+ return -1; /* error: invalid lead byte */
+ for (t = 1; t < r; t++)
+ if ((unsigned char) utf8[t] < 0x80 || (unsigned char) utf8[t] >= 0xC0)
+ return -1; /* error: not enough valid trailing bytes */
+ return r;
+}
+
+
+/* ucs4_char_from_utf8
+ *
+ * Given a reference to a pointer into a UTF-8 string, returns the next
+ * UCS-4 character and advances the pointer to the next character sequence.
+ * Returns ~0 and does not advance the pointer when input is ill-formed.
+ *
+ * Since the Unicode standard says 32-bit values won't be used (just
+ * up to the current 21-bit mappings) changed this to signed to allow -1 to
+ * be returned.
+ */
+long ucs4_char_from_utf8(utf8)
+ ZCONST char **utf8;
+{
+ ulg ret;
+ int t, bytes;
+
+ if (!utf8)
+ return -1; /* no input */
+ bytes = utf8_char_bytes(*utf8);
+ if (bytes <= 0)
+ return -1; /* invalid input */
+ if (bytes == 1)
+ ret = **utf8; /* ascii-7 */
+ else
+ ret = **utf8 & (0x7F >> bytes); /* lead byte of a multibyte sequence */
+ (*utf8)++;
+ for (t = 1; t < bytes; t++) /* consume trailing bytes */
+ ret = (ret << 6) | (*((*utf8)++) & 0x3F);
+ return (long) ret;
+}
+
+
+/* utf8_from_ucs4_char - Convert UCS char to UTF-8
+ *
+ * Returns the number of bytes put into utf8buf to represent ch, from 1 to 6,
+ * or -1 if ch is too large to represent. utf8buf must have room for 6 bytes.
+ */
+local int utf8_from_ucs4_char(utf8buf, ch)
+ char *utf8buf;
+ ulg ch;
+{
+ int trailing = 0;
+ int leadmask = 0x80;
+ int leadbits = 0x3F;
+ ulg tch = ch;
+ int ret;
+
+ if (ch > 0x7FFFFFFF)
+ return -1; /* UTF-8 can represent 31 bits */
+ if (ch < 0x7F)
+ {
+ *utf8buf++ = (char) ch; /* ascii-7 */
+ return 1;
+ }
+ do {
+ trailing++;
+ leadmask = (leadmask >> 1) | 0x80;
+ leadbits >>= 1;
+ tch >>= 6;
+ } while (tch & ~leadbits);
+ ret = trailing + 1;
+ /* produce lead byte */
+ *utf8buf++ = (char) (leadmask | (ch >> (6 * trailing)));
+ /* produce trailing bytes */
+ while (--trailing >= 0)
+ *utf8buf++ = (char) (0x80 | ((ch >> (6 * trailing)) & 0x3F));
+ return ret;
+}
+
+
+/*===================================================================*/
+
+/* utf8_to_ucs4_string - convert UTF-8 string to UCS string
+ *
+ * Return UCS count. Now returns int so can return -1.
+ */
+local int utf8_to_ucs4_string(utf8, ucs4buf, buflen)
+ ZCONST char *utf8;
+ ulg *ucs4buf;
+ int buflen;
+{
+ int count = 0;
+
+ for (;;)
+ {
+ long ch = ucs4_char_from_utf8(&utf8);
+ if (ch == -1)
+ return -1;
+ else
+ {
+ if (ucs4buf && count < buflen)
+ ucs4buf[count] = ch;
+ if (ch == 0)
+ return count;
+ count++;
+ }
+ }
+}
+
+
+/* ucs4_string_to_utf8
+ *
+ *
+ */
+local int ucs4_string_to_utf8(ucs4, utf8buf, buflen)
+ ZCONST ulg *ucs4;
+ char *utf8buf;
+ int buflen;
+{
+ char mb[6];
+ int count = 0;
+
+ if (!ucs4)
+ return -1;
+ for (;;)
+ {
+ int mbl = utf8_from_ucs4_char(mb, *ucs4++);
+ int c;
+ if (mbl <= 0)
+ return -1;
+ /* We could optimize this a bit by passing utf8buf + count */
+ /* directly to utf8_from_ucs4_char when buflen >= count + 6... */
+ c = buflen - count;
+ if (mbl < c)
+ c = mbl;
+ if (utf8buf && count < buflen)
+ strncpy(utf8buf + count, mb, c);
+ if (mbl == 1 && !mb[0])
+ return count; /* terminating nul */
+ count += mbl;
+ }
+}
+
+
+#if 0 /* currently unused */
+/* utf8_chars
+ *
+ * Wrapper: counts the actual unicode characters in a UTF-8 string.
+ */
+local int utf8_chars(utf8)
+ ZCONST char *utf8;
+{
+ return utf8_to_ucs4_string(utf8, NULL, 0);
+}
+#endif
+
+
+/* --------------------------------------------------- */
+/* Unicode Support
+ *
+ * These functions common for all Unicode ports.
+ *
+ * These functions should allocate and return strings that can be
+ * freed with free().
+ *
+ * 8/27/05 EG
+ *
+ * Use zwchar for wide char which is unsigned long
+ * in zip.h and 32 bits. This avoids problems with
+ * different sizes of wchar_t.
+ */
+
+#ifdef WIN32
+
+zwchar *wchar_to_wide_string(wchar_string)
+ wchar_t *wchar_string;
+{
+ int i;
+ int wchar_len;
+ zwchar *wide_string;
+
+ wchar_len = wcslen(wchar_string);
+
+ if ((wide_string = malloc((wchar_len + 1) * sizeof(zwchar))) == NULL) {
+ ZIPERR(ZE_MEM, "wchar to wide conversion");
+ }
+ for (i = 0; i <= wchar_len; i++) {
+ wide_string[i] = wchar_string[i];
+ }
+
+ return wide_string;
+}
+
+/* is_ascii_stringw
+ * Checks if a wide string is all ascii
+ */
+int is_ascii_stringw(wstring)
+ wchar_t *wstring;
+{
+ wchar_t *pw;
+ wchar_t cw;
+
+ if (wstring == NULL)
+ return 0;
+
+ for (pw = wstring; (cw = *pw) != '\0'; pw++) {
+ if (cw > 0x7F) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+#endif
+
+/* is_ascii_string
+ * Checks if a string is all ascii
+ */
+int is_ascii_string(mbstring)
+ char *mbstring;
+{
+ char *p;
+ uch c;
+
+ if (mbstring == NULL)
+ return 0;
+
+ for (p = mbstring; (c = (uch)*p) != '\0'; p++) {
+ if (c > 0x7F) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/* local to UTF-8 */
+char *local_to_utf8_string(local_string)
+ char *local_string;
+{
+ zwchar *wide_string = local_to_wide_string(local_string);
+ char *utf8_string = wide_to_utf8_string(wide_string);
+
+ free(wide_string);
+ return utf8_string;
+}
+
+/* wide_char_to_escape_string
+ provides a string that represents a wide char not in local char set
+
+ An initial try at an algorithm. Suggestions welcome.
+
+ If not an ASCII char, probably need 2 bytes at least. So if
+ a 2-byte wide encode it as 4 hex digits with a leading #U.
+ Since the Unicode standard has been frozen, it looks like 3 bytes
+ should be enough for any large Unicode character. In these cases
+ prefix the string with #L.
+ So
+ #U1234
+ is a 2-byte wide character with bytes 0x12 and 0x34 while
+ #L123456
+ is a 3-byte wide with bytes 0x12, 0x34, and 0x56.
+ On Windows, wide that need two wide characters as a surrogate pair
+ to represent them need to be converted to a single number.
+ */
+
+ /* set this to the max bytes an escape can be */
+#define MAX_ESCAPE_BYTES 8
+
+char *wide_char_to_escape_string(wide_char)
+ zwchar wide_char;
+{
+ int i;
+ zwchar w = wide_char;
+ uch b[9];
+ char e[7];
+ int len;
+ char *r;
+
+ /* fill byte array with zeros */
+ for (len = 0; len < sizeof(zwchar); len++) {
+ b[len] = 0;
+ }
+ /* get bytes in right to left order */
+ for (len = 0; w; len++) {
+ b[len] = (char)(w % 0x100);
+ w /= 0x100;
+ }
+
+ if ((r = malloc(MAX_ESCAPE_BYTES + 8)) == NULL) {
+ ZIPERR(ZE_MEM, "wide_char_to_escape_string");
+ }
+ strcpy(r, "#");
+ /* either 2 bytes or 4 bytes */
+ if (len < 3) {
+ len = 2;
+ strcat(r, "U");
+ } else {
+ len = 3;
+ strcat(r, "L");
+ }
+ for (i = len - 1; i >= 0; i--) {
+ sprintf(e, "%02x", b[i]);
+ strcat(r, e);
+ }
+ return r;
+}
+
+#if 0
+/* returns the wide character represented by the escape string */
+zwchar escape_string_to_wide(escape_string)
+ char *escape_string;
+{
+ int i;
+ zwchar w;
+ char c;
+ char u;
+ int len;
+ char *e = escape_string;
+
+ if (e == NULL) {
+ return 0;
+ }
+ if (e[0] != '#') {
+ /* no leading # */
+ return 0;
+ }
+ len = strlen(e);
+ /* either #U1234 or #L123456 format */
+ if (len != 6 && len != 8) {
+ return 0;
+ }
+ w = 0;
+ if (e[1] == 'L') {
+ if (len != 8) {
+ return 0;
+ }
+ /* 3 bytes */
+ for (i = 2; i < 8; i++) {
+ c = e[i];
+ u = toupper(c);
+ if (u >= 'A' && u <= 'F') {
+ w = w * 0x10 + (zwchar)(u + 10 - 'A');
+ } else if (c >= '0' && c <= '9') {
+ w = w * 0x10 + (zwchar)(c - '0');
+ } else {
+ return 0;
+ }
+ }
+ } else if (e[1] == 'U') {
+ /* 2 bytes */
+ for (i = 2; i < 6; i++) {
+ c = e[i];
+ u = toupper(c);
+ if (u >= 'A' && u <= 'F') {
+ w = w * 0x10 + (zwchar)(u + 10 - 'A');
+ } else if (c >= '0' && c <= '9') {
+ w = w * 0x10 + (zwchar)(c - '0');
+ } else {
+ return 0;
+ }
+ }
+ }
+ return w;
+}
+#endif
+
+
+char *local_to_escape_string(local_string)
+ char *local_string;
+{
+ zwchar *wide_string = local_to_wide_string(local_string);
+ char *escape_string = wide_to_escape_string(wide_string);
+
+ free(wide_string);
+ return escape_string;
+}
+
+#ifdef WIN32
+char *wchar_to_local_string(wstring)
+ wchar_t *wstring;
+{
+ zwchar *wide_string = wchar_to_wide_string(wstring);
+ char *local_string = wide_to_local_string(wide_string);
+
+ free(wide_string);
+
+ return local_string;
+}
+#endif
+
+
+#ifndef WIN32 /* The Win32 port uses a system-specific variant. */
+/* convert wide character string to multi-byte character string */
+char *wide_to_local_string(wide_string)
+ zwchar *wide_string;
+{
+ int i;
+ wchar_t wc;
+ int b;
+ int state_dependent;
+ int wsize = 0;
+ int max_bytes = MB_CUR_MAX;
+ char buf[9];
+ char *buffer = NULL;
+ char *local_string = NULL;
+
+ for (wsize = 0; wide_string[wsize]; wsize++) ;
+
+ if (MAX_ESCAPE_BYTES > max_bytes)
+ max_bytes = MAX_ESCAPE_BYTES;
+
+ if ((buffer = (char *)malloc(wsize * max_bytes + 1)) == NULL) {
+ ZIPERR(ZE_MEM, "wide_to_local_string");
+ }
+
+ /* convert it */
+ buffer[0] = '\0';
+ /* set initial state if state-dependent encoding */
+ wc = (wchar_t)'a';
+ b = wctomb(NULL, wc);
+ if (b == 0)
+ state_dependent = 0;
+ else
+ state_dependent = 1;
+ for (i = 0; i < wsize; i++) {
+ if (sizeof(wchar_t) < 4 && wide_string[i] > 0xFFFF) {
+ /* wchar_t probably 2 bytes */
+ /* could do surrogates if state_dependent and wctomb can do */
+ wc = zwchar_to_wchar_t_default_char;
+ } else {
+ wc = (wchar_t)wide_string[i];
+ }
+ b = wctomb(buf, wc);
+ if (unicode_escape_all) {
+ if (b == 1 && (uch)buf[0] <= 0x7f) {
+ /* ASCII */
+ strncat(buffer, buf, b);
+ } else {
+ /* use escape for wide character */
+ char *e = wide_char_to_escape_string(wide_string[i]);
+ strcat(buffer, e);
+ free(e);
+ }
+ } else if (b > 0) {
+ /* multi-byte char */
+ strncat(buffer, buf, b);
+ } else {
+ /* no MB for this wide */
+ if (use_wide_to_mb_default) {
+ /* default character */
+ strcat(buffer, wide_to_mb_default_string);
+ } else {
+ /* use escape for wide character */
+ char *e = wide_char_to_escape_string(wide_string[i]);
+ strcat(buffer, e);
+ free(e);
+ }
+ }
+ }
+ if ((local_string = (char *)malloc(strlen(buffer) + 1)) == NULL) {
+ free(buffer);
+ ZIPERR(ZE_MEM, "wide_to_local_string");
+ }
+ strcpy(local_string, buffer);
+ free(buffer);
+
+ return local_string;
+}
+#endif /* !WIN32 */
+
+
+/* convert wide character string to escaped string */
+char *wide_to_escape_string(wide_string)
+ zwchar *wide_string;
+{
+ int i;
+ int wsize = 0;
+ char buf[9];
+ char *buffer = NULL;
+ char *escape_string = NULL;
+
+ for (wsize = 0; wide_string[wsize]; wsize++) ;
+
+ if ((buffer = (char *)malloc(wsize * MAX_ESCAPE_BYTES + 1)) == NULL) {
+ ZIPERR(ZE_MEM, "wide_to_escape_string");
+ }
+
+ /* convert it */
+ buffer[0] = '\0';
+ for (i = 0; i < wsize; i++) {
+ if (wide_string[i] <= 0x7f && isprint((char)wide_string[i])) {
+ /* ASCII */
+ buf[0] = (char)wide_string[i];
+ buf[1] = '\0';
+ strcat(buffer, buf);
+ } else {
+ /* use escape for wide character */
+ char *e = wide_char_to_escape_string(wide_string[i]);
+ strcat(buffer, e);
+ free(e);
+ }
+ }
+ if ((escape_string = (char *)malloc(strlen(buffer) + 1)) == NULL) {
+ ZIPERR(ZE_MEM, "wide_to_escape_string");
+ }
+ strcpy(escape_string, buffer);
+ free(buffer);
+
+ return escape_string;
+}
+
+
+/* convert local string to display character set string */
+char *local_to_display_string(local_string)
+ char *local_string;
+{
+ char *temp_string;
+ char *display_string;
+
+ /* For Windows, OEM string should never be bigger than ANSI string, says
+ CharToOem description.
+ On UNIX, non-printable characters (0x00 - 0xFF) will be replaced by
+ "^x", so more space may be needed. Note that "^" itself is a valid
+ name character, so this leaves an ambiguity, but UnZip displays
+ names this way, too. (0x00 is not possible, I hope.)
+ For all other ports, just make a copy of local_string.
+ */
+
+#ifdef UNIX
+ char *cp_dst; /* Character pointers used in the */
+ char *cp_src; /* copying/changing procedure. */
+#endif
+
+ if ((temp_string = (char *)malloc(2 * strlen(local_string) + 1)) == NULL) {
+ ZIPERR(ZE_MEM, "local_to_display_string");
+ }
+
+#ifdef WIN32
+ /* convert to OEM display character set */
+ local_to_oem_string(temp_string, local_string);
+#else
+# ifdef UNIX
+ /* Copy source string, expanding non-printable characters to "^x". */
+ cp_dst = temp_string;
+ cp_src = local_string;
+ while (*cp_src != '\0') {
+ if ((unsigned char)*cp_src < ' ') {
+ *cp_dst++ = '^';
+ *cp_dst++ = '@'+ *cp_src++;
+ }
+ else {
+ *cp_dst++ = *cp_src++;
+ }
+ }
+ *cp_dst = '\0';
+# else /* not UNIX */
+ strcpy(temp_string, local_string);
+# endif /* UNIX */
+#endif
+
+#ifdef EBCDIC
+ {
+ char *ebc;
+
+ if ((ebc = malloc(strlen(display_string) + 1)) == NULL) {
+ ZIPERR(ZE_MEM, "local_to_display_string");
+ }
+ strtoebc(ebc, display_string);
+ free(display_string);
+ display_string = ebc;
+ }
+#endif
+
+ if ((display_string = (char *)malloc(strlen(temp_string) + 1)) == NULL) {
+ ZIPERR(ZE_MEM, "local_to_display_string");
+ }
+ strcpy(display_string, temp_string);
+ free(temp_string);
+
+ return display_string;
+}
+
+/* UTF-8 to local */
+char *utf8_to_local_string(utf8_string)
+ char *utf8_string;
+{
+ zwchar *wide_string = utf8_to_wide_string(utf8_string);
+ char *loc = wide_to_local_string(wide_string);
+ if (wide_string)
+ free(wide_string);
+ return loc;
+}
+
+/* UTF-8 to local */
+char *utf8_to_escape_string(utf8_string)
+ char *utf8_string;
+{
+ zwchar *wide_string = utf8_to_wide_string(utf8_string);
+ char *escape_string = wide_to_escape_string(wide_string);
+ free(wide_string);
+ return escape_string;
+}
+
+#ifndef WIN32 /* The Win32 port uses a system-specific variant. */
+/* convert multi-byte character string to wide character string */
+zwchar *local_to_wide_string(local_string)
+ char *local_string;
+{
+ int wsize;
+ wchar_t *wc_string;
+ zwchar *wide_string;
+
+ /* for now try to convert as string - fails if a bad char in string */
+ wsize = mbstowcs(NULL, local_string, MB_CUR_MAX );
+ if (wsize == (size_t)-1) {
+ /* could not convert */
+ return NULL;
+ }
+
+ /* convert it */
+ if ((wc_string = (wchar_t *)malloc((wsize + 1) * sizeof(wchar_t))) == NULL) {
+ ZIPERR(ZE_MEM, "local_to_wide_string");
+ }
+ wsize = mbstowcs(wc_string, local_string, strlen(local_string) + 1);
+ wc_string[wsize] = (wchar_t) 0;
+
+ /* in case wchar_t is not zwchar */
+ if ((wide_string = (zwchar *)malloc((wsize + 1) * sizeof(zwchar))) == NULL) {
+ ZIPERR(ZE_MEM, "local_to_wide_string");
+ }
+ for (wsize = 0; (wide_string[wsize] = (zwchar)wc_string[wsize]); wsize++) ;
+ wide_string[wsize] = (zwchar)0;
+ free(wc_string);
+
+ return wide_string;
+}
+#endif /* !WIN32 */
+
+
+#if 0
+/* All wchar functions are only used by Windows and are
+ now in win32zip.c so that the Windows functions can
+ be used and multiple character wide characters can
+ be handled easily. */
+# ifndef WIN32
+char *wchar_to_utf8_string(wstring)
+ wchar_t *wstring;
+{
+ zwchar *wide_string = wchar_to_wide_string(wstring);
+ char *local_string = wide_to_utf8_string(wide_string);
+
+ free(wide_string);
+
+ return local_string;
+}
+# endif
+#endif
+
+
+/* convert wide string to UTF-8 */
+char *wide_to_utf8_string(wide_string)
+ zwchar *wide_string;
+{
+ int mbcount;
+ char *utf8_string;
+
+ /* get size of utf8 string */
+ mbcount = ucs4_string_to_utf8(wide_string, NULL, 0);
+ if (mbcount == -1)
+ return NULL;
+ if ((utf8_string = (char *) malloc(mbcount + 1)) == NULL) {
+ ZIPERR(ZE_MEM, "wide_to_utf8_string");
+ }
+ mbcount = ucs4_string_to_utf8(wide_string, utf8_string, mbcount + 1);
+ if (mbcount == -1)
+ return NULL;
+
+ return utf8_string;
+}
+
+/* convert UTF-8 string to wide string */
+zwchar *utf8_to_wide_string(utf8_string)
+ char *utf8_string;
+{
+ int wcount;
+ zwchar *wide_string;
+
+ wcount = utf8_to_ucs4_string(utf8_string, NULL, 0);
+ if (wcount == -1)
+ return NULL;
+ if ((wide_string = (zwchar *) malloc((wcount + 2) * sizeof(zwchar))) == NULL) {
+ ZIPERR(ZE_MEM, "utf8_to_wide_string");
+ }
+ wcount = utf8_to_ucs4_string(utf8_string, wide_string, wcount + 1);
+
+ return wide_string;
+}
+
+
+#endif /* UNICODE_SUPPORT */
+
+
+/*---------------------------------------------------------------
+ * Long option support
+ * 8/23/2003
+ *
+ * Defines function get_option() to get and process the command
+ * line options and arguments from argv[]. The caller calls
+ * get_option() in a loop to get either one option and possible
+ * value or a non-option argument each loop.
+ *
+ * This version does not include argument file support and can
+ * work directly on argv. The argument file code complicates things and
+ * it seemed best to leave it out for now. If argument file support (reading
+ * in command line arguments stored in a file and inserting into
+ * command line where @filename is found) is added later the arguments
+ * can change and a freeable copy of argv will be needed and can be
+ * created using copy_args in the left out code.
+ *
+ * Supports short and long options as defined in the array options[]
+ * in zip.c, multiple short options in an argument (like -jlv), long
+ * option abbreviation (like --te for --temp-file if --te unique),
+ * short and long option values (like -b filename or --temp-file filename
+ * or --temp-file=filename), optional and required values, option negation
+ * by trailing - (like -S- to not include hidden and system files in MSDOS),
+ * value lists (like -x a b c), argument permuting (returning all options
+ * and values before any non-option arguments), and argument files (where any
+ * non-option non-value argument in form @path gets substituted with the
+ * white space separated arguments in the text file at path). In this
+ * version argument file support has been removed to simplify development but
+ * may be added later.
+ *
+ * E. Gordon
+ */
+
+
+/* message output - char casts are needed to handle constants */
+#define oWARN(message) zipwarn((char *) message, "")
+#define oERR(err,message) ZIPERR(err, (char *) message)
+
+
+/* Although the below provides some support for multibyte characters
+ the proper thing to do may be to use wide characters and support
+ Unicode. May get to it soon. EG
+ */
+
+/* For now stay with muti-byte characters. May support wide characters
+ in Zip 3.1.
+ */
+
+/* multibyte character set support
+ Multibyte characters use typically two or more sequential bytes
+ to represent additional characters than can fit in a single byte
+ character set. The code used here is based on the ANSI mblen function. */
+#ifdef MULTIBYTE_GETOPTNS
+ int mb_clen(ptr)
+ ZCONST char *ptr;
+ {
+ /* return the number of bytes that the char pointed to is. Return 1 if
+ null character or error like not start of valid multibyte character. */
+ int cl;
+
+ cl = mblen(ptr, MB_CUR_MAX);
+ return (cl > 0) ? cl : 1;
+ }
+#endif
+
+
+ /* moved to zip.h */
+#if 0
+#ifdef UNICODE_SUPPORT
+# define MB_CLEN(ptr) (1)
+# define MB_NEXTCHAR(ptr) ((ptr)++)
+# ifdef MULTIBYTE_GETOPTNS
+# undef MULTIBYTE_GETOPTNS
+# endif
+#else
+# ifdef _MBCS
+# ifndef MULTIBYTE_GETOPTNS
+# define MULTIBYTE_GETOPTNS
+# endif
+# endif
+/* multibyte character set support
+ Multibyte characters use typically two or more sequential bytes
+ to represent additional characters than can fit in a single byte
+ character set. The code used here is based on the ANSI mblen function. */
+# ifdef MULTIBYTE_GETOPTNS
+ local int mb_clen OF((ZCONST char *)); /* declare proto first */
+ local int mb_clen(ptr)
+ ZCONST char *ptr;
+ {
+ /* return the number of bytes that the char pointed to is. Return 1 if
+ null character or error like not start of valid multibyte character. */
+ int cl;
+
+ cl = mblen(ptr, MB_CUR_MAX);
+ return (cl > 0) ? cl : 1;
+ }
+# define MB_CLEN(ptr) mb_clen(ptr)
+# define MB_NEXTCHAR(ptr) ((ptr) += MB_CLEN(ptr))
+# else
+# define MB_CLEN(ptr) (1)
+# define MB_NEXTCHAR(ptr) ((ptr)++)
+# endif
+#endif
+#endif
+
+
+/* constants */
+
+/* function get_args_from_arg_file() can return this in depth parameter */
+#define ARG_FILE_ERR -1
+
+/* internal settings for optchar */
+#define SKIP_VALUE_ARG -1
+#define THIS_ARG_DONE -2
+#define START_VALUE_LIST -3
+#define IN_VALUE_LIST -4
+#define NON_OPTION_ARG -5
+#define STOP_VALUE_LIST -6
+/* 7/25/04 EG */
+#define READ_REST_ARGS_VERBATIM -7
+
+
+/* global veriables */
+
+int enable_permute = 1; /* yes - return options first */
+/* 7/25/04 EG */
+int doubledash_ends_options = 1; /* when -- what follows are not options */
+
+/* buffer for error messages (this sizing is a guess but must hold 2 paths) */
+#define OPTIONERR_BUF_SIZE (FNMAX * 2 + 4000)
+local char Far optionerrbuf[OPTIONERR_BUF_SIZE + 1];
+
+/* error messages */
+static ZCONST char Far op_not_neg_err[] = "option %s not negatable";
+static ZCONST char Far op_req_val_err[] = "option %s requires a value";
+static ZCONST char Far op_no_allow_val_err[] = "option %s does not allow a value";
+static ZCONST char Far sh_op_not_sup_err[] = "short option '%c' not supported";
+static ZCONST char Far oco_req_val_err[] = "option %s requires one character value";
+static ZCONST char Far oco_no_mbc_err[] = "option %s does not support multibyte values";
+static ZCONST char Far num_req_val_err[] = "option %s requires number value";
+static ZCONST char Far long_op_ambig_err[] = "long option '%s' ambiguous";
+static ZCONST char Far long_op_not_sup_err[] = "long option '%s' not supported";
+
+static ZCONST char Far no_arg_files_err[] = "argument files not enabled\n";
+
+
+/* below removed as only used for processing argument files */
+
+/* get_nextarg */
+/* get_args_from_string */
+/* insert_args */
+/* get_args_from_arg_file */
+
+
+/* copy error, option name, and option description if any to buf */
+local int optionerr(buf, err, optind, islong)
+ char *buf;
+ ZCONST char *err;
+ int optind;
+ int islong;
+{
+ char optname[50];
+
+ if (options[optind].name && options[optind].name[0] != '\0') {
+ if (islong)
+ sprintf(optname, "'%s' (%s)", options[optind].longopt, options[optind].name);
+ else
+ sprintf(optname, "'%s' (%s)", options[optind].shortopt, options[optind].name);
+ } else {
+ if (islong)
+ sprintf(optname, "'%s'", options[optind].longopt);
+ else
+ sprintf(optname, "'%s'", options[optind].shortopt);
+ }
+ sprintf(buf, err, optname);
+ return 0;
+}
+
+
+/* copy_args
+ *
+ * Copy arguments in args, allocating storage with malloc.
+ * Copies until a NULL argument is found or until max_args args
+ * including args[0] are copied. Set max_args to 0 to copy
+ * until NULL. Always terminates returned args[] with NULL arg.
+ *
+ * Any argument in the returned args can be freed with free(). Any
+ * freed argument should be replaced with either another string
+ * allocated with malloc or by NULL if last argument so that free_args
+ * will properly work.
+ */
+char **copy_args(args, max_args)
+ char **args;
+ int max_args;
+{
+ int j;
+ char **new_args;
+
+ if (args == NULL) {
+ return NULL;
+ }
+
+ /* count args */
+ for (j = 0; args[j] && (max_args == 0 || j < max_args); j++) ;
+
+ if ((new_args = (char **) malloc((j + 1) * sizeof(char *))) == NULL) {
+ oERR(ZE_MEM, "ca");
+ }
+
+ for (j = 0; args[j] && (max_args == 0 || j < max_args); j++) {
+ if (args[j] == NULL) {
+ /* null argument is end of args */
+ new_args[j] = NULL;
+ break;
+ }
+ if ((new_args[j] = malloc(strlen(args[j]) + 1)) == NULL) {
+ free_args(new_args);
+ oERR(ZE_MEM, "ca");
+ }
+ strcpy(new_args[j], args[j]);
+ }
+ new_args[j] = NULL;
+
+ return new_args;
+}
+
+
+/* free args - free args created with one of these functions */
+int free_args(args)
+ char **args;
+{
+ int i;
+
+ if (args == NULL) {
+ return 0;
+ }
+
+ for (i = 0; args[i]; i++) {
+ free(args[i]);
+ }
+ free(args);
+ return i;
+}
+
+
+/* insert_arg
+ *
+ * Insert the argument arg into the array args before argument at_arg.
+ * Return the new count of arguments (argc).
+ *
+ * If free_args is true, this function frees the old args array
+ * (but not the component strings). DO NOT set free_args on original
+ * argv but only on args allocated with malloc.
+ */
+
+int insert_arg(pargs, arg, at_arg, free_args)
+ char ***pargs;
+ ZCONST char *arg;
+ int at_arg;
+ int free_args;
+{
+ char *newarg = NULL;
+ char **args;
+ char **newargs = NULL;
+ int argnum;
+ int newargnum;
+ int argcnt;
+ int newargcnt;
+
+ if (pargs == NULL) {
+ return 0;
+ }
+ args = *pargs;
+
+ /* count args */
+ if (args == NULL) {
+ argcnt = 0;
+ } else {
+ for (argcnt = 0; args[argcnt]; argcnt++) ;
+ }
+ if (arg == NULL) {
+ /* done */
+ return argcnt;
+ }
+ newargcnt = argcnt + 1;
+
+ /* get storage for new args */
+ if ((newargs = (char **) malloc((newargcnt + 1) * sizeof(char *))) == NULL) {
+ oERR(ZE_MEM, "ia");
+ }
+
+ /* copy argument pointers from args to position at_arg, copy arg, then rest args */
+ argnum = 0;
+ newargnum = 0;
+ if (args) {
+ for (; args[argnum] && argnum < at_arg; argnum++) {
+ newargs[newargnum++] = args[argnum];
+ }
+ }
+ /* copy new arg */
+ if ((newarg = (char *) malloc(strlen(arg) + 1)) == NULL) {
+ oERR(ZE_MEM, "ia");
+ }
+ strcpy(newarg, arg);
+
+ newargs[newargnum++] = newarg;
+ if (args) {
+ for ( ; args[argnum]; argnum++) {
+ newargs[newargnum++] = args[argnum];
+ }
+ }
+ newargs[newargnum] = NULL;
+
+ /* free old args array but not component strings - this assumes that
+ args was allocated with malloc as copy_args does. DO NOT DO THIS
+ on the original argv.
+ */
+ if (free_args)
+ free(args);
+
+ *pargs = newargs;
+
+ return newargnum;
+}
+
+/* ------------------------------------- */
+
+
+
+
+/* get_shortopt
+ *
+ * Get next short option from arg. The state is stored in argnum, optchar, and
+ * option_num so no static storage is used. Returns the option_ID.
+ *
+ * parameters:
+ * args - argv array of arguments
+ * argnum - index of current arg in args
+ * optchar - pointer to index of next char to process. Can be 0 or
+ * const defined at top of this file like THIS_ARG_DONE
+ * negated - on return pointer to int set to 1 if option negated or 0 otherwise
+ * value - on return pointer to string set to value of option if any or NULL
+ * if none. If value is returned then the caller should free()
+ * it when not needed anymore.
+ * option_num - pointer to index in options[] of returned option or
+ * o_NO_OPTION_MATCH if none. Do not change as used by
+ * value lists.
+ * depth - recursion depth (0 at top level, 1 or more in arg files)
+ */
+local unsigned long get_shortopt(args, argnum, optchar, negated, value,
+ option_num, depth)
+ char **args;
+ int argnum;
+ int *optchar;
+ int *negated;
+ char **value;
+ int *option_num;
+ int depth;
+{
+ char *shortopt;
+ int clen;
+ char *nextchar;
+ char *s;
+ char *start;
+ int op;
+ char *arg;
+ int match = -1;
+
+
+ /* get arg */
+ arg = args[argnum];
+ /* current char in arg */
+ nextchar = arg + (*optchar);
+ clen = MB_CLEN(nextchar);
+ /* next char in arg */
+ (*optchar) += clen;
+ /* get first char of short option */
+ shortopt = arg + (*optchar);
+ /* no value */
+ *value = NULL;
+
+ if (*shortopt == '\0') {
+ /* no more options in arg */
+ *optchar = 0;
+ *option_num = o_NO_OPTION_MATCH;
+ return 0;
+ }
+
+ /* look for match in options */
+ clen = MB_CLEN(shortopt);
+ for (op = 0; options[op].option_ID; op++) {
+ s = options[op].shortopt;
+ if (s && s[0] == shortopt[0]) {
+ if (s[1] == '\0' && clen == 1) {
+ /* single char match */
+ match = op;
+ } else {
+ /* 2 wide short opt. Could support more chars but should use long opts instead */
+ if (s[1] == shortopt[1]) {
+ /* match 2 char short opt or 2 byte char */
+ match = op;
+ if (clen == 1) (*optchar)++;
+ break;
+ }
+ }
+ }
+ }
+
+ if (match > -1) {
+ /* match */
+ clen = MB_CLEN(shortopt);
+ nextchar = arg + (*optchar) + clen;
+ /* check for trailing dash negating option */
+ if (*nextchar == '-') {
+ /* negated */
+ if (options[match].negatable == o_NOT_NEGATABLE) {
+ if (options[match].value_type == o_NO_VALUE) {
+ optionerr(optionerrbuf, op_not_neg_err, match, 0);
+ if (depth > 0) {
+ /* unwind */
+ oWARN(optionerrbuf);
+ return o_ARG_FILE_ERR;
+ } else {
+ oERR(ZE_PARMS, optionerrbuf);
+ }
+ }
+ } else {
+ *negated = 1;
+ /* set up to skip negating dash */
+ (*optchar) += clen;
+ clen = 1;
+ }
+ }
+
+ /* value */
+ clen = MB_CLEN(arg + (*optchar));
+ /* optional value, one char value, and number value must follow option */
+ if (options[match].value_type == o_ONE_CHAR_VALUE) {
+ /* one char value */
+ if (arg[(*optchar) + clen]) {
+ /* has value */
+ if (MB_CLEN(arg + (*optchar) + clen) > 1) {
+ /* multibyte value not allowed for now */
+ optionerr(optionerrbuf, oco_no_mbc_err, match, 0);
+ if (depth > 0) {
+ /* unwind */
+ oWARN(optionerrbuf);
+ return o_ARG_FILE_ERR;
+ } else {
+ oERR(ZE_PARMS, optionerrbuf);
+ }
+ }
+ if ((*value = (char *) malloc(2)) == NULL) {
+ oERR(ZE_MEM, "gso");
+ }
+ (*value)[0] = *(arg + (*optchar) + clen);
+ (*value)[1] = '\0';
+ *optchar += clen;
+ clen = 1;
+ } else {
+ /* one char values require a value */
+ optionerr(optionerrbuf, oco_req_val_err, match, 0);
+ if (depth > 0) {
+ oWARN(optionerrbuf);
+ return o_ARG_FILE_ERR;
+ } else {
+ oERR(ZE_PARMS, optionerrbuf);
+ }
+ }
+ } else if (options[match].value_type == o_NUMBER_VALUE) {
+ /* read chars until end of number */
+ start = arg + (*optchar) + clen;
+ if (*start == '+' || *start == '-') {
+ start++;
+ }
+ s = start;
+ for (; isdigit(*s); MB_NEXTCHAR(s)) ;
+ if (s == start) {
+ /* no digits */
+ optionerr(optionerrbuf, num_req_val_err, match, 0);
+ if (depth > 0) {
+ oWARN(optionerrbuf);
+ return o_ARG_FILE_ERR;
+ } else {
+ oERR(ZE_PARMS, optionerrbuf);
+ }
+ }
+ start = arg + (*optchar) + clen;
+ if ((*value = (char *) malloc((int)(s - start) + 1)) == NULL) {
+ oERR(ZE_MEM, "gso");
+ }
+ *optchar += (int)(s - start);
+ strncpy(*value, start, (int)(s - start));
+ (*value)[(int)(s - start)] = '\0';
+ clen = MB_CLEN(s);
+ } else if (options[match].value_type == o_OPTIONAL_VALUE) {
+ /* optional value */
+ /* This seemed inconsistent so now if no value attached to argument look
+ to the next argument if that argument is not an option for option
+ value - 11/12/04 EG */
+ if (arg[(*optchar) + clen]) {
+ /* has value */
+ /* add support for optional = - 2/6/05 EG */
+ if (arg[(*optchar) + clen] == '=') {
+ /* skip = */
+ clen++;
+ }
+ if (arg[(*optchar) + clen]) {
+ if ((*value = (char *)malloc(strlen(arg + (*optchar) + clen) + 1))
+ == NULL) {
+ oERR(ZE_MEM, "gso");
+ }
+ strcpy(*value, arg + (*optchar) + clen);
+ }
+ *optchar = THIS_ARG_DONE;
+ } else if (args[argnum + 1] && args[argnum + 1][0] != '-') {
+ /* use next arg for value */
+ if ((*value = (char *)malloc(strlen(args[argnum + 1]) + 1)) == NULL) {
+ oERR(ZE_MEM, "gso");
+ }
+ /* using next arg as value */
+ strcpy(*value, args[argnum + 1]);
+ *optchar = SKIP_VALUE_ARG;
+ }
+ } else if (options[match].value_type == o_REQUIRED_VALUE ||
+ options[match].value_type == o_VALUE_LIST) {
+ /* see if follows option */
+ if (arg[(*optchar) + clen]) {
+ /* has value following option as -ovalue */
+ /* add support for optional = - 6/5/05 EG */
+ if (arg[(*optchar) + clen] == '=') {
+ /* skip = */
+ clen++;
+ }
+ if ((*value = (char *)malloc(strlen(arg + (*optchar) + clen) + 1))
+ == NULL) {
+ oERR(ZE_MEM, "gso");
+ }
+ strcpy(*value, arg + (*optchar) + clen);
+ *optchar = THIS_ARG_DONE;
+ } else {
+ /* use next arg for value */
+ if (args[argnum + 1]) {
+ if ((*value = (char *)malloc(strlen(args[argnum + 1]) + 1)) == NULL) {
+ oERR(ZE_MEM, "gso");
+ }
+ strcpy(*value, args[argnum + 1]);
+ if (options[match].value_type == o_VALUE_LIST) {
+ *optchar = START_VALUE_LIST;
+ } else {
+ *optchar = SKIP_VALUE_ARG;
+ }
+ } else {
+ /* no value found */
+ optionerr(optionerrbuf, op_req_val_err, match, 0);
+ if (depth > 0) {
+ oWARN(optionerrbuf);
+ return o_ARG_FILE_ERR;
+ } else {
+ oERR(ZE_PARMS, optionerrbuf);
+ }
+ }
+ }
+ }
+
+ *option_num = match;
+ return options[match].option_ID;
+ }
+ sprintf(optionerrbuf, sh_op_not_sup_err, *shortopt);
+ if (depth > 0) {
+ /* unwind */
+ oWARN(optionerrbuf);
+ return o_ARG_FILE_ERR;
+ } else {
+ oERR(ZE_PARMS, optionerrbuf);
+ }
+ return 0;
+}
+
+
+/* get_longopt
+ *
+ * Get the long option in args array at argnum.
+ * Parameters same as for get_shortopt.
+ */
+
+local unsigned long get_longopt(args, argnum, optchar, negated, value,
+ option_num, depth)
+ char **args;
+ int argnum;
+ int *optchar;
+ int *negated;
+ char **value;
+ int *option_num;
+ int depth;
+{
+ char *longopt;
+ char *lastchr;
+ char *valuestart;
+ int op;
+ char *arg;
+ int match = -1;
+ *value = NULL;
+
+ if (args == NULL) {
+ *option_num = o_NO_OPTION_MATCH;
+ return 0;
+ }
+ if (args[argnum] == NULL) {
+ *option_num = o_NO_OPTION_MATCH;
+ return 0;
+ }
+ /* copy arg so can chop end if value */
+ if ((arg = (char *)malloc(strlen(args[argnum]) + 1)) == NULL) {
+ oERR(ZE_MEM, "glo");
+ }
+ strcpy(arg, args[argnum]);
+
+ /* get option */
+ longopt = arg + 2;
+ /* no value */
+ *value = NULL;
+
+ /* find = */
+ for (lastchr = longopt, valuestart = longopt;
+ *valuestart && *valuestart != '=';
+ lastchr = valuestart, MB_NEXTCHAR(valuestart)) ;
+ if (*valuestart) {
+ /* found =value */
+ *valuestart = '\0';
+ valuestart++;
+ } else {
+ valuestart = NULL;
+ }
+
+ if (*lastchr == '-') {
+ /* option negated */
+ *negated = 1;
+ *lastchr = '\0';
+ } else {
+ *negated = 0;
+ }
+
+ /* look for long option match */
+ for (op = 0; options[op].option_ID; op++) {
+ if (options[op].longopt && strcmp(options[op].longopt, longopt) == 0) {
+ /* exact match */
+ match = op;
+ break;
+ }
+ if (options[op].longopt && strncmp(options[op].longopt, longopt, strlen(longopt)) == 0) {
+ if (match > -1) {
+ sprintf(optionerrbuf, long_op_ambig_err, longopt);
+ free(arg);
+ if (depth > 0) {
+ /* unwind */
+ oWARN(optionerrbuf);
+ return o_ARG_FILE_ERR;
+ } else {
+ oERR(ZE_PARMS, optionerrbuf);
+ }
+ }
+ match = op;
+ }
+ }
+
+ if (match == -1) {
+ sprintf(optionerrbuf, long_op_not_sup_err, longopt);
+ free(arg);
+ if (depth > 0) {
+ oWARN(optionerrbuf);
+ return o_ARG_FILE_ERR;
+ } else {
+ oERR(ZE_PARMS, optionerrbuf);
+ }
+ }
+
+ /* one long option an arg */
+ *optchar = THIS_ARG_DONE;
+
+ /* if negated then see if allowed */
+ if (*negated && options[match].negatable == o_NOT_NEGATABLE) {
+ optionerr(optionerrbuf, op_not_neg_err, match, 1);
+ free(arg);
+ if (depth > 0) {
+ /* unwind */
+ oWARN(optionerrbuf);
+ return o_ARG_FILE_ERR;
+ } else {
+ oERR(ZE_PARMS, optionerrbuf);
+ }
+ }
+ /* get value */
+ if (options[match].value_type == o_OPTIONAL_VALUE) {
+ /* optional value in form option=value */
+ if (valuestart) {
+ /* option=value */
+ if ((*value = (char *)malloc(strlen(valuestart) + 1)) == NULL) {
+ free(arg);
+ oERR(ZE_MEM, "glo");
+ }
+ strcpy(*value, valuestart);
+ }
+ } else if (options[match].value_type == o_REQUIRED_VALUE ||
+ options[match].value_type == o_NUMBER_VALUE ||
+ options[match].value_type == o_ONE_CHAR_VALUE ||
+ options[match].value_type == o_VALUE_LIST) {
+ /* handle long option one char and number value as required value */
+ if (valuestart) {
+ /* option=value */
+ if ((*value = (char *)malloc(strlen(valuestart) + 1)) == NULL) {
+ free(arg);
+ oERR(ZE_MEM, "glo");
+ }
+ strcpy(*value, valuestart);
+ } else {
+ /* use next arg */
+ if (args[argnum + 1]) {
+ if ((*value = (char *)malloc(strlen(args[argnum + 1]) + 1)) == NULL) {
+ free(arg);
+ oERR(ZE_MEM, "glo");
+ }
+ /* using next arg as value */
+ strcpy(*value, args[argnum + 1]);
+ if (options[match].value_type == o_VALUE_LIST) {
+ *optchar = START_VALUE_LIST;
+ } else {
+ *optchar = SKIP_VALUE_ARG;
+ }
+ } else {
+ /* no value found */
+ optionerr(optionerrbuf, op_req_val_err, match, 1);
+ free(arg);
+ if (depth > 0) {
+ /* unwind */
+ oWARN(optionerrbuf);
+ return o_ARG_FILE_ERR;
+ } else {
+ oERR(ZE_PARMS, optionerrbuf);
+ }
+ }
+ }
+ } else if (options[match].value_type == o_NO_VALUE) {
+ /* this option does not accept a value */
+ if (valuestart) {
+ /* --option=value */
+ optionerr(optionerrbuf, op_no_allow_val_err, match, 1);
+ free(arg);
+ if (depth > 0) {
+ oWARN(optionerrbuf);
+ return o_ARG_FILE_ERR;
+ } else {
+ oERR(ZE_PARMS, optionerrbuf);
+ }
+ }
+ }
+ free(arg);
+
+ *option_num = match;
+ return options[match].option_ID;
+}
+
+
+
+/* get_option
+ *
+ * Main interface for user. Use this function to get options, values and
+ * non-option arguments from a command line provided in argv form.
+ *
+ * To use get_option() first define valid options by setting
+ * the global variable options[] to an array of option_struct. Also
+ * either change defaults below or make variables global and set elsewhere.
+ * Zip uses below defaults.
+ *
+ * Call get_option() to get an option (like -b or --temp-file) and any
+ * value for that option (like filename for -b) or a non-option argument
+ * (like archive name) each call. If *value* is not NULL after calling
+ * get_option() it is a returned value and the caller should either store
+ * the char pointer or free() it before calling get_option() again to avoid
+ * leaking memory. If a non-option non-value argument is returned get_option()
+ * returns o_NON_OPTION_ARG and value is set to the entire argument.
+ * When there are no more arguments get_option() returns 0.
+ *
+ * The parameters argnum (after set to 0 on initial call),
+ * optchar, first_nonopt_arg, option_num, and depth (after initial
+ * call) are set and maintained by get_option() and should not be
+ * changed. The parameters argc, negated, and value are outputs and
+ * can be used by the calling program. get_option() returns either the
+ * option_ID for the current option, a special value defined in
+ * zip.h, or 0 when no more arguments.
+ *
+ * The value returned by get_option() is the ID value in the options
+ * table. This value can be duplicated in the table if different
+ * options are really the same option. The index into the options[]
+ * table is given by option_num, though the ID should be used as
+ * option numbers change when the table is changed. The ID must
+ * not be 0 for any option as this ends the table. If get_option()
+ * finds an option not in the table it calls oERR to post an
+ * error and exit. Errors also result if the option requires a
+ * value that is missing, a value is present but the option does
+ * not take one, and an option is negated but is not
+ * negatable. Non-option arguments return o_NON_OPTION_ARG
+ * with the entire argument in value.
+ *
+ * For Zip, permuting is on and all options and their values are
+ * returned before any non-option arguments like archive name.
+ *
+ * The arguments "-" alone and "--" alone return as non-option arguments.
+ * Note that "-" should not be used as part of a short option
+ * entry in the table but can be used in the middle of long
+ * options such as in the long option "a-long-option". Now "--" alone
+ * stops option processing, returning any arguments following "--" as
+ * non-option arguments instead of options.
+ *
+ * Argument file support is removed from this version. It may be added later.
+ *
+ * After each call:
+ * argc is set to the current size of args[] but should not change
+ * with argument file support removed,
+ * argnum is the index of the current arg,
+ * value is either the value of the returned option or non-option
+ * argument or NULL if option with no value,
+ * negated is set if the option was negated by a trailing dash (-)
+ * option_num is set to either the index in options[] for the option or
+ * o_NO_OPTION_MATCH if no match.
+ * Negation is checked before the value is read if the option is negatable so
+ * that the - is not included in the value. If the option is not negatable
+ * but takes a value then the - will start the value. If permuting then
+ * argnum and first_nonopt_arg are unreliable and should not be used.
+ *
+ * Command line is read from left to right. As get_option() finds non-option
+ * arguments (arguments not starting with - and that are not values to options)
+ * it moves later options and values in front of the non-option arguments.
+ * This permuting is turned off by setting enable_permute to 0. Then
+ * get_option() will return options and non-option arguments in the order
+ * found. Currently permuting is only done after an argument is completely
+ * processed so that any value can be moved with options they go with. All
+ * state information is stored in the parameters argnum, optchar,
+ * first_nonopt_arg and option_num. You should not change these after the
+ * first call to get_option(). If you need to back up to a previous arg then
+ * set argnum to that arg (remembering that args may have been permuted) and
+ * set optchar = 0 and first_nonopt_arg to the first non-option argument if
+ * permuting. After all arguments are returned the next call to get_option()
+ * returns 0. The caller can then call free_args(args) if appropriate.
+ *
+ * get_option() accepts arguments in the following forms:
+ * short options
+ * of 1 and 2 characters, e.g. a, b, cc, d, and ba, after a single
+ * leading -, as in -abccdba. In this example if 'b' is followed by 'a'
+ * it matches short option 'ba' else it is interpreted as short option
+ * b followed by another option. The character - is not legal as a
+ * short option or as part of a 2 character short option.
+ *
+ * If a short option has a value it immediately follows the option or
+ * if that option is the end of the arg then the next arg is used as
+ * the value. So if short option e has a value, it can be given as
+ * -evalue
+ * or
+ * -e value
+ * and now
+ * -e=value
+ * but now that = is optional a leading = is stripped for the first.
+ * This change allows optional short option values to be defaulted as
+ * -e=
+ * Either optional or required values can be specified. Optional values
+ * now use both forms as ignoring the later got confusing. Any
+ * non-value short options can preceed a valued short option as in
+ * -abevalue
+ * Some value types (one_char and number) allow options after the value
+ * so if oc is an option that takes a character and n takes a number
+ * then
+ * -abocVccn42evalue
+ * returns value V for oc and value 42 for n. All values are strings
+ * so programs may have to convert the "42" to a number. See long
+ * options below for how value lists are handled.
+ *
+ * Any short option can be negated by following it with -. Any - is
+ * handled and skipped over before any value is read unless the option
+ * is not negatable but takes a value and then - starts the value.
+ *
+ * If the value for an optional value is just =, then treated as no
+ * value.
+ *
+ * long options
+ * of arbitrary length are assumed if an arg starts with -- but is not
+ * exactly --. Long options are given one per arg and can be abbreviated
+ * if the abbreviation uniquely matches one of the long options.
+ * Exact matches always match before partial matches. If ambiguous an
+ * error is generated.
+ *
+ * Values are specified either in the form
+ * --longoption=value
+ * or can be the following arg if the value is required as in
+ * --longoption value
+ * Optional values to long options must be in the first form.
+ *
+ * Value lists are specified by o_VALUE_LIST and consist of an option
+ * that takes a value followed by one or more value arguments.
+ * The two forms are
+ * --option=value
+ * or
+ * -ovalue
+ * for a single value or
+ * --option value1 value2 value3 ... --option2
+ * or
+ * -o value1 value2 value3 ...
+ * for a list of values. The list ends at the next option, the
+ * end of the command line, or at a single "@" argument.
+ * Each value is treated as if it was preceeded by the option, so
+ * --option1 val1 val2
+ * with option1 value_type set to o_VALUE_LIST is the same as
+ * --option1=val1 --option1=val2
+ *
+ * Long options can be negated by following the option with - as in
+ * --longoption-
+ * Long options with values can also be negated if this makes sense for
+ * the caller as:
+ * --longoption-=value
+ * If = is not followed by anything it is treated as no value.
+ *
+ * @path
+ * When an argument in the form @path is encountered, the file at path
+ * is opened and white space separated arguments read from the file
+ * and inserted into the command line at that point as if the contents
+ * of the file were directly typed at that location. The file can
+ * have options, files to zip, or anything appropriate at that location
+ * in the command line. Since Zip has permuting enabled, options and
+ * files will propagate to the appropriate locations in the command
+ * line.
+ *
+ * Argument files support has been removed from this version. It may
+ * be added back later.
+ *
+ * non-option argument
+ * is any argument not given above. If enable_permute is 1 then
+ * these are returned after all options, otherwise all options and
+ * args are returned in order. Returns option ID o_NON_OPTION_ARG
+ * and sets value to the argument.
+ *
+ *
+ * Arguments to get_option:
+ * char ***pargs - pointer to arg array in the argv form
+ * int *argc - returns the current argc for args incl. args[0]
+ * int *argnum - the index of the current argument (caller
+ * should set = 0 on first call and not change
+ * after that)
+ * int *optchar - index of next short opt in arg or special
+ * int *first_nonopt_arg - used by get_option to permute args
+ * int *negated - option was negated (had trailing -)
+ * char *value - value of option if any (free when done with it) or NULL
+ * int *option_num - the index in options of the last option returned
+ * (can be o_NO_OPTION_MATCH)
+ * int recursion_depth - current depth of recursion
+ * (always set to 0 by caller)
+ * (always 0 with argument files support removed)
+ *
+ * Caller should only read the returned option ID and the value, negated,
+ * and option_num (if required) parameters after each call.
+ *
+ * Ed Gordon
+ * 24 August 2003 (last updated 2 April 2008 EG)
+ *
+ */
+
+unsigned long get_option(pargs, argc, argnum, optchar, value,
+ negated, first_nonopt_arg, option_num, recursion_depth)
+ char ***pargs;
+ int *argc;
+ int *argnum;
+ int *optchar;
+ char **value;
+ int *negated;
+ int *first_nonopt_arg;
+ int *option_num;
+ int recursion_depth;
+{
+ char **args;
+ unsigned long option_ID;
+
+ int argcnt;
+ int first_nonoption_arg;
+ char *arg = NULL;
+ int h;
+ int optc;
+ int argn;
+ int j;
+ int v;
+ int read_rest_args_verbatim = 0; /* 7/25/04 - ignore options and arg files for rest args */
+
+ /* value is outdated. The caller should free value before
+ calling get_option again. */
+ *value = NULL;
+
+ /* if args is NULL then done */
+ if (pargs == NULL) {
+ *argc = 0;
+ return 0;
+ }
+ args = *pargs;
+ if (args == NULL) {
+ *argc = 0;
+ return 0;
+ }
+
+ /* count args */
+ for (argcnt = 0; args[argcnt]; argcnt++) ;
+
+ /* if no provided args then nothing to do */
+ if (argcnt < 1 || (recursion_depth == 0 && argcnt < 2)) {
+ *argc = argcnt;
+ /* return 0 to note that no args are left */
+ return 0;
+ }
+
+ *negated = 0;
+ first_nonoption_arg = *first_nonopt_arg;
+ argn = *argnum;
+ optc = *optchar;
+
+ if (optc == READ_REST_ARGS_VERBATIM) {
+ read_rest_args_verbatim = 1;
+ }
+
+ if (argn == -1 || (recursion_depth == 0 && argn == 0)) {
+ /* first call */
+ /* if depth = 0 then args[0] is argv[0] so skip */
+ *option_num = o_NO_OPTION_MATCH;
+ optc = THIS_ARG_DONE;
+ first_nonoption_arg = -1;
+ }
+
+ /* if option_num is set then restore last option_ID in case continuing value list */
+ option_ID = 0;
+ if (*option_num != o_NO_OPTION_MATCH) {
+ option_ID = options[*option_num].option_ID;
+ }
+
+ /* get next option if any */
+ for (;;) {
+ if (read_rest_args_verbatim) {
+ /* rest of args after "--" are non-option args if doubledash_ends_options set */
+ argn++;
+ if (argn > argcnt || args[argn] == NULL) {
+ /* done */
+ option_ID = 0;
+ break;
+ }
+ arg = args[argn];
+ if ((*value = (char *)malloc(strlen(arg) + 1)) == NULL) {
+ oERR(ZE_MEM, "go");
+ }
+ strcpy(*value, arg);
+ *option_num = o_NO_OPTION_MATCH;
+ option_ID = o_NON_OPTION_ARG;
+ break;
+
+ /* permute non-option args after option args so options are returned first */
+ } else if (enable_permute) {
+ if (optc == SKIP_VALUE_ARG || optc == THIS_ARG_DONE ||
+ optc == START_VALUE_LIST || optc == IN_VALUE_LIST ||
+ optc == STOP_VALUE_LIST) {
+ /* moved to new arg */
+ if (first_nonoption_arg > -1 && args[first_nonoption_arg]) {
+ /* do the permuting - move non-options after this option */
+ /* if option and value separate args or starting list skip option */
+ if (optc == SKIP_VALUE_ARG || optc == START_VALUE_LIST) {
+ v = 1;
+ } else {
+ v = 0;
+ }
+ for (h = first_nonoption_arg; h < argn; h++) {
+ arg = args[first_nonoption_arg];
+ for (j = first_nonoption_arg; j < argn + v; j++) {
+ args[j] = args[j + 1];
+ }
+ args[j] = arg;
+ }
+ first_nonoption_arg += 1 + v;
+ }
+ }
+ } else if (optc == NON_OPTION_ARG) {
+ /* if not permuting then already returned arg */
+ optc = THIS_ARG_DONE;
+ }
+
+ /* value lists */
+ if (optc == STOP_VALUE_LIST) {
+ optc = THIS_ARG_DONE;
+ }
+
+ if (optc == START_VALUE_LIST || optc == IN_VALUE_LIST) {
+ if (optc == START_VALUE_LIST) {
+ /* already returned first value */
+ argn++;
+ optc = IN_VALUE_LIST;
+ }
+ argn++;
+ arg = args[argn];
+ /* if end of args and still in list and there are non-option args then
+ terminate list */
+ if (arg == NULL && (optc == START_VALUE_LIST || optc == IN_VALUE_LIST)
+ && first_nonoption_arg > -1) {
+ /* terminate value list with @ */
+ /* this is only needed for argument files */
+ /* but is also good for show command line so command lines with lists
+ can always be read back in */
+ argcnt = insert_arg(&args, "@", first_nonoption_arg, 1);
+ argn++;
+ if (first_nonoption_arg > -1) {
+ first_nonoption_arg++;
+ }
+ }
+
+ arg = args[argn];
+ if (arg && arg[0] == '@' && arg[1] == '\0') {
+ /* inserted arguments terminator */
+ optc = STOP_VALUE_LIST;
+ continue;
+ } else if (arg && arg[0] != '-') { /* not option */
+ /* - and -- are not allowed in value lists unless escaped */
+ /* another value in value list */
+ if ((*value = (char *)malloc(strlen(args[argn]) + 1)) == NULL) {
+ oERR(ZE_MEM, "go");
+ }
+ strcpy(*value, args[argn]);
+ break;
+
+ } else {
+ argn--;
+ optc = THIS_ARG_DONE;
+ }
+ }
+
+ /* move to next arg */
+ if (optc == SKIP_VALUE_ARG) {
+ argn += 2;
+ optc = 0;
+ } else if (optc == THIS_ARG_DONE) {
+ argn++;
+ optc = 0;
+ }
+ if (argn > argcnt) {
+ break;
+ }
+ if (args[argn] == NULL) {
+ /* done unless permuting and non-option args */
+ if (first_nonoption_arg > -1 && args[first_nonoption_arg]) {
+ /* return non-option arguments at end */
+ if (optc == NON_OPTION_ARG) {
+ first_nonoption_arg++;
+ }
+ /* after first pass args are permuted but skipped over non-option args */
+ /* swap so argn points to first non-option arg */
+ j = argn;
+ argn = first_nonoption_arg;
+ first_nonoption_arg = j;
+ }
+ if (argn > argcnt || args[argn] == NULL) {
+ /* done */
+ option_ID = 0;
+ break;
+ }
+ }
+
+ /* after swap first_nonoption_arg points to end which is NULL */
+ if (first_nonoption_arg > -1 && (args[first_nonoption_arg] == NULL)) {
+ /* only non-option args left */
+ if (optc == NON_OPTION_ARG) {
+ argn++;
+ }
+ if (argn > argcnt || args[argn] == NULL) {
+ /* done */
+ option_ID = 0;
+ break;
+ }
+ if ((*value = (char *)malloc(strlen(args[argn]) + 1)) == NULL) {
+ oERR(ZE_MEM, "go");
+ }
+ strcpy(*value, args[argn]);
+ optc = NON_OPTION_ARG;
+ option_ID = o_NON_OPTION_ARG;
+ break;
+ }
+
+ arg = args[argn];
+
+ /* is it an option */
+ if (arg[0] == '-') {
+ /* option */
+ if (arg[1] == '\0') {
+ /* arg = - */
+ /* treat like non-option arg */
+ *option_num = o_NO_OPTION_MATCH;
+ if (enable_permute) {
+ /* permute args to move all non-option args to end */
+ if (first_nonoption_arg < 0) {
+ first_nonoption_arg = argn;
+ }
+ argn++;
+ } else {
+ /* not permute args so return non-option args when found */
+ if ((*value = (char *)malloc(strlen(arg) + 1)) == NULL) {
+ oERR(ZE_MEM, "go");
+ }
+ strcpy(*value, arg);
+ optc = NON_OPTION_ARG;
+ option_ID = o_NON_OPTION_ARG;
+ break;
+ }
+
+ } else if (arg[1] == '-') {
+ /* long option */
+ if (arg[2] == '\0') {
+ /* arg = -- */
+ if (doubledash_ends_options) {
+ /* Now -- stops permuting and forces the rest of
+ the command line to be read verbatim - 7/25/04 EG */
+
+ /* never permute args after -- and return as non-option args */
+ if (first_nonoption_arg < 1) {
+ /* -- is first non-option argument - 8/7/04 EG */
+ argn--;
+ } else {
+ /* go back to start of non-option args - 8/7/04 EG */
+ argn = first_nonoption_arg - 1;
+ }
+
+ /* disable permuting and treat remaining arguments as not
+ options */
+ read_rest_args_verbatim = 1;
+ optc = READ_REST_ARGS_VERBATIM;
+
+ } else {
+ /* treat like non-option arg */
+ *option_num = o_NO_OPTION_MATCH;
+ if (enable_permute) {
+ /* permute args to move all non-option args to end */
+ if (first_nonoption_arg < 0) {
+ first_nonoption_arg = argn;
+ }
+ argn++;
+ } else {
+ /* not permute args so return non-option args when found */
+ if ((*value = (char *)malloc(strlen(arg) + 1)) == NULL) {
+ oERR(ZE_MEM, "go");
+ }
+ strcpy(*value, arg);
+ optc = NON_OPTION_ARG;
+ option_ID = o_NON_OPTION_ARG;
+ break;
+ }
+ }
+
+ } else {
+ option_ID = get_longopt(args, argn, &optc, negated, value, option_num, recursion_depth);
+ if (option_ID == o_ARG_FILE_ERR) {
+ /* unwind as only get this if recursion_depth > 0 */
+ return option_ID;
+ }
+ break;
+ }
+
+ } else {
+ /* short option */
+ option_ID = get_shortopt(args, argn, &optc, negated, value, option_num, recursion_depth);
+
+ if (option_ID == o_ARG_FILE_ERR) {
+ /* unwind as only get this if recursion_depth > 0 */
+ return option_ID;
+ }
+
+ if (optc == 0) {
+ /* if optc = 0 then ran out of short opts this arg */
+ optc = THIS_ARG_DONE;
+ } else {
+ break;
+ }
+ }
+
+#if 0
+ /* argument file code left out
+ so for now let filenames start with @
+ */
+
+ } else if (allow_arg_files && arg[0] == '@') {
+ /* arg file */
+ oERR(ZE_PARMS, no_arg_files_err);
+#endif
+
+ } else {
+ /* non-option */
+ if (enable_permute) {
+ /* permute args to move all non-option args to end */
+ if (first_nonoption_arg < 0) {
+ first_nonoption_arg = argn;
+ }
+ argn++;
+ } else {
+ /* no permute args so return non-option args when found */
+ if ((*value = (char *)malloc(strlen(arg) + 1)) == NULL) {
+ oERR(ZE_MEM, "go");
+ }
+ strcpy(*value, arg);
+ *option_num = o_NO_OPTION_MATCH;
+ optc = NON_OPTION_ARG;
+ option_ID = o_NON_OPTION_ARG;
+ break;
+ }
+
+ }
+ }
+
+ *pargs = args;
+ *argc = argcnt;
+ *first_nonopt_arg = first_nonoption_arg;
+ *argnum = argn;
+ *optchar = optc;
+
+ return option_ID;
+}