summaryrefslogtreecommitdiff
path: root/zipsplit.c
diff options
context:
space:
mode:
Diffstat (limited to 'zipsplit.c')
-rw-r--r--zipsplit.c804
1 files changed, 804 insertions, 0 deletions
diff --git a/zipsplit.c b/zipsplit.c
new file mode 100644
index 0000000..da38705
--- /dev/null
+++ b/zipsplit.c
@@ -0,0 +1,804 @@
+/*
+ Copyright (c) 1990-2005 Info-ZIP. All rights reserved.
+
+ See the accompanying file LICENSE, version 2005-Feb-10 or later
+ (the contents of which are also included in zip.h) for terms of use.
+ If, for some reason, both of these files are missing, the Info-ZIP license
+ also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html
+*/
+/*
+ * zipsplit.c by Mark Adler.
+ */
+#define __ZIPSPLIT_C
+
+#ifndef UTIL
+#define UTIL
+#endif
+#include "zip.h"
+#define DEFCPYRT /* main module: enable copyright string defines! */
+#include "revision.h"
+#include <signal.h>
+
+#define DEFSIZ 36000L /* Default split size (change in help() too) */
+#ifdef MSDOS
+# define NL 2 /* Number of bytes written for a \n */
+#else /* !MSDOS */
+# define NL 1 /* Number of bytes written for a \n */
+#endif /* ?MSDOS */
+#ifdef RISCOS
+# define INDEX "zipspl/idx" /* Name of index file */
+# define TEMPL_FMT "%%0%dld"
+# define TEMPL_SIZ 13
+# define ZPATH_SEP '.'
+#else
+#ifdef QDOS
+# define ZPATH_SEP '_'
+# define INDEX "zipsplit_idx" /* Name of index file */
+# define TEMPL_FMT "%%0%dld_zip"
+# define TEMPL_SIZ 17
+# define exit(p1) QDOSexit()
+#else
+#ifdef VM_CMS
+# define INDEX "zipsplit.idx" /* Name of index file */
+# define TEMPL_FMT "%%0%dld.zip"
+# define TEMPL_SIZ 21
+# define ZPATH_SEP '.'
+#else
+# define INDEX "zipsplit.idx" /* Name of index file */
+# define TEMPL_FMT "%%0%dld.zip"
+# define TEMPL_SIZ 17
+# define ZPATH_SEP '.'
+#endif /* VM_CMS */
+#endif /* QDOS */
+#endif /* RISCOS */
+
+#ifdef MACOS
+#define ziperr(c, h) zipspliterr(c, h)
+#define zipwarn(a, b) zipsplitwarn(a, b)
+void zipsplitwarn(ZCONST char *a, ZCONST char *b);
+void zipspliterr(int c, ZCONST char *h);
+#endif /* MACOS */
+
+/* Local functions */
+local zvoid *talloc OF((extent));
+local void tfree OF((zvoid *));
+local void tfreeall OF((void));
+local void handler OF((int));
+local void license OF((void));
+local void help OF((void));
+local void version_info OF((void));
+local extent simple OF((ulg *, extent, ulg, ulg));
+local int descmp OF((ZCONST zvoid *, ZCONST zvoid *));
+local extent greedy OF((ulg *, extent, ulg, ulg));
+local int retry OF((void));
+int main OF((int, char **));
+
+
+/* Output zip files */
+local char template[TEMPL_SIZ]; /* name template for output files */
+local int zipsmade = 0; /* number of zip files made */
+local int indexmade = 0; /* true if index file made */
+local char *path = NULL; /* space for full name */
+local char *name; /* where name goes in path[] */
+
+
+/* The talloc() and tree() routines extend malloc() and free() to keep
+ track of all allocated memory. Then the tfreeall() routine uses this
+ information to free all allocated memory before exiting. */
+
+#define TMAX 6 /* set intelligently by examining the code */
+zvoid *talls[TMAX]; /* malloc'ed pointers to track */
+int talln = 0; /* number of entries in talls[] */
+
+
+local zvoid *talloc(s)
+extent s;
+/* does a malloc() and saves the pointer to free later (does not check
+ for an overflow of the talls[] list) */
+{
+ zvoid *p;
+
+ if ((p = (zvoid *)malloc(s)) != NULL)
+ talls[talln++] = p;
+ return p;
+}
+
+
+local void tfree(p)
+zvoid *p;
+/* does a free() and also removes the pointer from the talloc() list */
+{
+ int i;
+
+ free(p);
+ i = talln;
+ while (i--)
+ if (talls[i] == p)
+ break;
+ if (i >= 0)
+ {
+ while (++i < talln)
+ talls[i - 1] = talls[i];
+ talln--;
+ }
+}
+
+
+local void tfreeall()
+/* free everything talloc'ed and not tfree'd */
+{
+ while (talln)
+ free(talls[--talln]);
+}
+
+
+void ziperr(c, h)
+int c; /* error code from the ZE_ class */
+ZCONST char *h; /* message about how it happened */
+/* Issue a message for the error, clean up files and memory, and exit. */
+{
+ if (PERR(c))
+ perror("zipsplit error");
+ fprintf(stderr, "zipsplit error: %s (%s)\n", ziperrors[c-1], h);
+ if (indexmade)
+ {
+ strcpy(name, INDEX);
+ destroy(path);
+ }
+ for (; zipsmade; zipsmade--)
+ {
+ sprintf(name, template, zipsmade);
+ destroy(path);
+ }
+ tfreeall();
+ if (zipfile != NULL)
+ free((zvoid *)zipfile);
+ EXIT(c);
+}
+
+
+local void handler(s)
+int s; /* signal number (ignored) */
+/* Upon getting a user interrupt, abort cleanly using ziperr(). */
+{
+#ifndef MSDOS
+ putc('\n', stderr);
+#endif /* !MSDOS */
+ ziperr(ZE_ABORT, "aborting");
+ s++; /* keep some compilers happy */
+}
+
+
+void zipwarn(a, b)
+ZCONST char *a, *b; /* message strings juxtaposed in output */
+/* Print a warning message to stderr and return. */
+{
+ fprintf(stderr, "zipsplit warning: %s%s\n", a, b);
+}
+
+
+local void license()
+/* Print license information to stdout. */
+{
+ extent i; /* counter for copyright array */
+
+ for (i = 0; i < sizeof(swlicense)/sizeof(char *); i++)
+ puts(swlicense[i]);
+}
+
+
+local void help()
+/* Print help (along with license info) to stdout. */
+{
+ extent i; /* counter for help array */
+
+ /* help array */
+ static ZCONST char *text[] = {
+"",
+"ZipSplit %s (%s)",
+#ifdef VM_CMS
+"Usage: zipsplit [-tips] [-n size] [-r room] [-b fm] zipfile",
+#else
+"Usage: zipsplit [-tips] [-n size] [-r room] [-b path] zipfile",
+#endif
+" -t report how many files it will take, but don't make them",
+#ifdef RISCOS
+" -i make index (" INDEX ") and count its size against first zip file",
+#else
+" -i make index (zipsplit.idx) and count its size against first zip file",
+#endif
+" -n make zip files no larger than \"size\" (default = 36000)",
+" -r leave room for \"room\" bytes on the first disk (default = 0)",
+#ifdef VM_CMS
+" -b use \"fm\" as the filemode for the output zip files",
+#else
+" -b use \"path\" for the output zip files",
+#endif
+" -p pause between output zip files",
+" -s do a sequential split even if it takes more zip files",
+" -h show this help -v show version info -L show software license"
+ };
+
+ for (i = 0; i < sizeof(copyright)/sizeof(char *); i++) {
+ printf(copyright[i], "zipsplit");
+ putchar('\n');
+ }
+ for (i = 0; i < sizeof(text)/sizeof(char *); i++)
+ {
+ printf(text[i], VERSION, REVDATE);
+ putchar('\n');
+ }
+}
+
+
+local void version_info()
+/* Print verbose info about program version and compile time options
+ to stdout. */
+{
+ extent i; /* counter in text arrays */
+
+ /* Options info array */
+ static ZCONST char *comp_opts[] = {
+#ifdef DEBUG
+ "DEBUG",
+#endif
+ NULL
+ };
+
+ for (i = 0; i < sizeof(copyright)/sizeof(char *); i++)
+ {
+ printf(copyright[i], "zipsplit");
+ putchar('\n');
+ }
+
+ for (i = 0; i < sizeof(versinfolines)/sizeof(char *); i++)
+ {
+ printf(versinfolines[i], "ZipSplit", VERSION, REVDATE);
+ putchar('\n');
+ }
+
+ version_local();
+
+ puts("ZipSplit special compilation options:");
+ for (i = 0; (int)i < (int)(sizeof(comp_opts)/sizeof(char *) - 1); i++)
+ {
+ printf("\t%s\n",comp_opts[i]);
+ }
+ if (i == 0)
+ puts("\t[none]");
+}
+
+
+local extent simple(a, n, c, d)
+ulg *a; /* items to put in bins, return value: destination bins */
+extent n; /* number of items */
+ulg c; /* capacity of each bin */
+ulg d; /* amount to deduct from first bin */
+/* Return the number of bins of capacity c that are needed to contain the
+ integers in a[0..n-1] placed sequentially into the bins. The value d
+ is deducted initially from the first bin (space for index). The entries
+ in a[] are replaced by the destination bins. */
+{
+ extent k; /* current bin number */
+ ulg t; /* space used in current bin */
+
+ t = k = 0;
+ while (n--)
+ {
+ if (*a + t > c - (k == 0 ? d : 0))
+ {
+ k++;
+ t = 0;
+ }
+ t += *a;
+ *(ulg huge *)a++ = k;
+ }
+ return k + 1;
+}
+
+
+local int descmp(a, b)
+ZCONST zvoid *a, *b; /* pointers to pointers to ulg's to compare */
+/* Used by qsort() in greedy() to do a descending sort. */
+{
+ return **(ulg **)a < **(ulg **)b ? 1 : (**(ulg **)a > **(ulg **)b ? -1 : 0);
+}
+
+
+local extent greedy(a, n, c, d)
+ulg *a; /* items to put in bins, return value: destination bins */
+extent n; /* number of items */
+ulg c; /* capacity of each bin */
+ulg d; /* amount to deduct from first bin */
+/* Return the number of bins of capacity c that are needed to contain the
+ items with sizes a[0..n-1] placed non-sequentially into the bins. The
+ value d is deducted initially from the first bin (space for index).
+ The entries in a[] are replaced by the destination bins. */
+{
+ ulg *b; /* space left in each bin (malloc'ed for each m) */
+ ulg *e; /* copy of argument a[] (malloc'ed) */
+ extent i; /* steps through items */
+ extent j; /* steps through bins */
+ extent k; /* best bin to put current item in */
+ extent m; /* current number of bins */
+ ulg **s; /* pointers to e[], sorted descending (malloc'ed) */
+ ulg t; /* space left in best bin (index k) */
+
+ /* Algorithm:
+ 1. Copy a[] to e[] and sort pointers to e[0..n-1] (in s[]), in
+ descending order.
+ 2. Compute total of s[] and set m to the smallest number of bins of
+ capacity c that can hold the total.
+ 3. Allocate m bins.
+ 4. For each item in s[], starting with the largest, put it in the
+ bin with the smallest current capacity greater than or equal to the
+ item's size. If no bin has enough room, increment m and go to step 4.
+ 5. Else, all items ended up in a bin--return m.
+ */
+
+ /* Copy a[] to e[], put pointers to e[] in s[], and sort s[]. Also compute
+ the initial number of bins (minus 1). */
+ if ((e = (ulg *)malloc(n * sizeof(ulg))) == NULL ||
+ (s = (ulg **)malloc(n * sizeof(ulg *))) == NULL)
+ {
+ if (e != NULL)
+ free((zvoid *)e);
+ ziperr(ZE_MEM, "was trying a smart split");
+ return 0; /* only to make compiler happy */
+ }
+ memcpy((char *)e, (char *)a, n * sizeof(ulg));
+ for (t = i = 0; i < n; i++)
+ t += *(s[i] = e + i);
+ m = (extent)((t + c - 1) / c) - 1; /* pre-decrement for loop */
+ qsort((char *)s, n, sizeof(ulg *), descmp);
+
+ /* Stuff bins until successful */
+ do {
+ /* Increment the number of bins, allocate and initialize bins */
+ if ((b = (ulg *)malloc(++m * sizeof(ulg))) == NULL)
+ {
+ free((zvoid *)s);
+ free((zvoid *)e);
+ ziperr(ZE_MEM, "was trying a smart split");
+ }
+ b[0] = c - d; /* leave space in first bin */
+ for (j = 1; j < m; j++)
+ b[j] = c;
+
+ /* Fill the bins greedily */
+ for (i = 0; i < n; i++)
+ {
+ /* Find smallest bin that will hold item i (size s[i]) */
+ t = c + 1;
+ for (k = j = 0; j < m; j++)
+ if (*s[i] <= b[j] && b[j] < t)
+ t = b[k = j];
+
+ /* If no bins big enough for *s[i], try next m */
+ if (t == c + 1)
+ break;
+
+ /* Diminish that bin and save where it goes */
+ b[k] -= *s[i];
+ a[(int)((ulg huge *)(s[i]) - (ulg huge *)e)] = k;
+ }
+
+ /* Clean up */
+ free((zvoid *)b);
+
+ /* Do until all items put in a bin */
+ } while (i < n);
+
+ /* Done--clean up and return the number of bins needed */
+ free((zvoid *)s);
+ free((zvoid *)e);
+ return m;
+}
+
+
+local int retry()
+{
+ char m[10];
+ fputs("Error writing to disk--redo entire disk? ", stderr);
+ fgets(m, 10, stdin);
+ return *m == 'y' || *m == 'Y';
+}
+
+
+#ifndef USE_ZIPSPLITMAIN
+int main(argc, argv)
+#else
+int zipsplitmain(argc, argv)
+#endif
+
+int argc; /* number of tokens in command line */
+char **argv; /* command line tokens */
+/* Split a zip file into several zip files less than a specified size. See
+ the command help in help() above. */
+{
+ ulg *a; /* malloc'ed list of sizes, dest bins */
+ extent *b; /* heads of bin linked lists (malloc'ed) */
+ ulg c; /* bin capacity, start of central directory */
+ int d; /* if true, just report the number of disks */
+ FILE *e; /* input zip file */
+ FILE *f; /* output index and zip files */
+ extent g; /* number of bins from greedy(), entry to write */
+ int h; /* how to split--true means simple split, counter */
+ ulg i = 0; /* size of index file plus room to leave */
+ extent j; /* steps through zip entries, bins */
+ int k; /* next argument type */
+ extent *n = NULL; /* next item in bin list (heads in b) */
+ ulg *p; /* malloc'ed list of sizes, dest bins for greedy() */
+ char *q; /* steps through option characters */
+ int r; /* temporary variable, counter */
+ extent s; /* number of bins needed */
+ ulg t; /* total of sizes, end of central directory */
+ int u; /* flag to wait for user on output files */
+ struct zlist far **w; /* malloc'ed table for zfiles linked list */
+ int x; /* if true, make an index file */
+ struct zlist far *z; /* steps through zfiles linked list */
+#ifdef AMIGA
+ char tailchar; /* temporary variable used in name generation below */
+#endif
+
+#ifdef THEOS
+ setlocale(LC_CTYPE, "I");
+#endif
+
+ /* If no args, show help */
+ if (argc == 1)
+ {
+ help();
+ EXIT(0);
+ }
+
+ init_upper(); /* build case map table */
+
+ /* Go through args */
+ signal(SIGINT, handler);
+#ifdef SIGTERM /* Amiga has no SIGTERM */
+ signal(SIGTERM, handler);
+#endif
+ k = h = x = d = u = 0;
+ c = DEFSIZ;
+ for (r = 1; r < argc; r++)
+ if (*argv[r] == '-')
+ {
+ if (argv[r][1])
+ for (q = argv[r]+1; *q; q++)
+ switch (*q)
+ {
+ case 'b': /* Specify path for output files */
+ if (k)
+ ziperr(ZE_PARMS, "options are separate and precede zip file");
+ else
+ k = 1; /* Next non-option is path */
+ break;
+ case 'h': /* Show help */
+ help(); EXIT(0);
+ case 'i': /* Make an index file */
+ x = 1;
+ break;
+ case 'l': case 'L': /* Show copyright and disclaimer */
+ license(); EXIT(0);
+ case 'n': /* Specify maximum size of resulting zip files */
+ if (k)
+ ziperr(ZE_PARMS, "options are separate and precede zip file");
+ else
+ k = 2; /* Next non-option is size */
+ break;
+ case 'p':
+ u = 1;
+ break;
+ case 'r':
+ if (k)
+ ziperr(ZE_PARMS, "options are separate and precede zip file");
+ else
+ k = 3; /* Next non-option is room to leave */
+ break;
+ case 's':
+ h = 1; /* Only try simple */
+ break;
+ case 't': /* Just report number of disks */
+ d = 1;
+ break;
+ case 'v': /* Show version info */
+ version_info(); EXIT(0);
+ default:
+ ziperr(ZE_PARMS, "Use option -h for help.");
+ }
+ else
+ ziperr(ZE_PARMS, "zip file cannot be stdin");
+ }
+ else
+ switch (k)
+ {
+ case 0:
+ if (zipfile == NULL)
+ {
+ if ((zipfile = ziptyp(argv[r])) == NULL)
+ ziperr(ZE_MEM, "was processing arguments");
+ }
+ else
+ ziperr(ZE_PARMS, "can only specify one zip file");
+ break;
+ case 1:
+ tempath = argv[r];
+ k = 0;
+ break;
+ case 2:
+ if ((c = (ulg)atol(argv[r])) < 100) /* 100 is smallest zip file */
+ ziperr(ZE_PARMS, "invalid size given. Use option -h for help.");
+ k = 0;
+ break;
+ default: /* k must be 3 */
+ i = (ulg)atol(argv[r]);
+ k = 0;
+ break;
+ }
+ if (zipfile == NULL)
+ ziperr(ZE_PARMS, "need to specify zip file");
+
+
+ /* Read zip file */
+ if ((r = readzipfile()) != ZE_OK)
+ ziperr(r, zipfile);
+ if (zfiles == NULL)
+ ziperr(ZE_NAME, zipfile);
+
+ /* Make a list of sizes and check against capacity. Also compute the
+ size of the index file. */
+ c -= ENDHEAD + 4; /* subtract overhead/zipfile */
+ if ((a = (ulg *)talloc(zcount * sizeof(ulg))) == NULL ||
+ (w = (struct zlist far **)talloc(zcount * sizeof(struct zlist far *))) ==
+ NULL)
+ {
+ ziperr(ZE_MEM, "was computing split");
+ return 1;
+ }
+ t = 0;
+ for (j = 0, z = zfiles; j < zcount; j++, z = z->nxt)
+ {
+ w[j] = z;
+ if (x)
+ i += z->nam + 6 + NL;
+ t += a[j] = 8 + LOCHEAD + CENHEAD +
+ 2 * (ulg)z->nam + 2 * (ulg)z->ext + z->com + z->siz;
+ if (a[j] > c)
+ ziperr(ZE_BIG, z->zname);
+ }
+
+ /* Decide on split to use, report number of files */
+ if (h)
+ s = simple(a, zcount, c, i);
+ else
+ {
+ if ((p = (ulg *)talloc(zcount * sizeof(ulg))) == NULL)
+ ziperr(ZE_MEM, "was computing split");
+ memcpy((char *)p, (char *)a, zcount * sizeof(ulg));
+ s = simple(a, zcount, c, i);
+ g = greedy(p, zcount, c, i);
+ if (s <= g)
+ tfree((zvoid *)p);
+ else
+ {
+ tfree((zvoid *)a);
+ a = p;
+ s = g;
+ }
+ }
+ printf("%ld zip files w%s be made (%ld%% efficiency)\n",
+ (ulg)s, d ? "ould" : "ill", ((200 * ((t + c - 1)/c)) / s + 1) >> 1);
+ if (d)
+ {
+ tfreeall();
+ free((zvoid *)zipfile);
+ zipfile = NULL;
+ EXIT(0);
+ }
+
+ /* Set up path for output files */
+ /* Point "name" past the path, where the filename should go */
+ if ((path = (char *)talloc(tempath == NULL ? 13 : strlen(tempath) + 14)) ==
+ NULL)
+ ziperr(ZE_MEM, "was making output file names");
+ if (tempath == NULL)
+ name = path;
+ else
+ {
+#ifndef VM_CMS
+ /* Copy the output path to the target */
+ strcpy(path, tempath);
+#endif
+#ifdef AMIGA
+ tailchar = path[strlen(path) - 1]; /* last character */
+ if (path[0] && (tailchar != '/') && (tailchar != ':'))
+ strcat(path, "/");
+#else
+# ifdef RISCOS
+ if (path[0] && path[strlen(path) - 1] != '.')
+ strcat(path, ".");
+# else /* !RISCOS */
+# ifdef QDOS
+ if (path[0] && path[strlen(path) - 1] != '_')
+ strcat(path, "_");
+# else
+ if (path[0] && path[strlen(path) - 1] != '/')
+ strcat(path, "/");
+# endif
+# endif
+#endif /* ?AMIGA */
+ name = path + strlen(path);
+ }
+
+ /* Make linked lists of results */
+ if ((b = (extent *)talloc(s * sizeof(extent))) == NULL ||
+ (n = (extent *)talloc(zcount * sizeof(extent))) == NULL)
+ ziperr(ZE_MEM, "was computing split");
+ for (j = 0; j < s; j++)
+ b[j] = (extent)-1;
+ j = zcount;
+ while (j--)
+ {
+ g = (extent)a[j];
+ n[j] = b[g];
+ b[g] = j;
+ }
+
+ /* Make a name template for the zip files that is eight or less characters
+ before the .zip, and that will not overwrite the original zip file. */
+ for (k = 1, j = s; j >= 10; j /= 10)
+ k++;
+ if (k > 7)
+ ziperr(ZE_PARMS, "way too many zip files must be made");
+/*
+ * XXX, ugly ....
+ */
+/* Find the final "path" separator character */
+#ifdef QDOS
+ q = LastDir(zipfile);
+#else
+#ifdef VMS
+ if ((q = strrchr(zipfile, ']')) != NULL)
+#else
+#ifdef AMIGA
+ if (((q = strrchr(zipfile, '/')) != NULL)
+ || ((q = strrchr(zipfile, ':'))) != NULL)
+#else
+#ifdef RISCOS
+ if ((q = strrchr(zipfile, '.')) != NULL)
+#else
+#ifdef MVS
+ if ((q = strrchr(zipfile, '.')) != NULL)
+#else
+ if ((q = strrchr(zipfile, '/')) != NULL)
+#endif /* MVS */
+#endif /* RISCOS */
+#endif /* AMIGA */
+#endif /* VMS */
+ q++;
+ else
+ q = zipfile;
+#endif /* QDOS */
+
+ r = 0;
+ while ((g = *q++) != '\0' && g != ZPATH_SEP && r < 8 - k)
+ template[r++] = (char)g;
+ if (r == 0)
+ template[r++] = '_';
+ else if (g >= '0' && g <= '9')
+ template[r - 1] = (char)(template[r - 1] == '_' ? '-' : '_');
+ sprintf(template + r, TEMPL_FMT, k);
+#ifdef VM_CMS
+ /* For CMS, add the "path" as the filemode at the end */
+ if (tempath)
+ {
+ strcat(template,".");
+ strcat(template,tempath);
+ }
+#endif
+
+ /* Make the zip files from the linked lists of entry numbers */
+ if ((e = fopen(zipfile, FOPR)) == NULL)
+ ziperr(ZE_NAME, zipfile);
+ free((zvoid *)zipfile);
+ zipfile = NULL;
+ for (j = 0; j < s; j++)
+ {
+ /* jump here on a disk retry */
+ redobin:
+
+ /* prompt if requested */
+ if (u)
+ {
+ char m[10];
+ fprintf(stderr, "Insert disk #%ld of %ld and hit return: ",
+ (ulg)j + 1, (ulg)s);
+ fgets(m, 10, stdin);
+ }
+
+ /* write index file on first disk if requested */
+ if (j == 0 && x)
+ {
+ strcpy(name, INDEX);
+ printf("creating: %s\n", path);
+ indexmade = 1;
+ if ((f = fopen(path, "w")) == NULL)
+ {
+ if (u && retry()) goto redobin;
+ ziperr(ZE_CREAT, path);
+ }
+ for (j = 0; j < zcount; j++)
+ fprintf(f, "%5ld %s\n", a[j] + 1, w[j]->zname);
+ if ((j = ferror(f)) != 0 || fclose(f))
+ {
+ if (j)
+ fclose(f);
+ if (u && retry()) goto redobin;
+ ziperr(ZE_WRITE, path);
+ }
+ }
+
+ /* create output zip file j */
+ sprintf(name, template, j + 1L);
+ printf("creating: %s\n", path);
+ zipsmade = j + 1;
+ if ((f = fopen(path, FOPW)) == NULL)
+ {
+ if (u && retry()) goto redobin;
+ ziperr(ZE_CREAT, path);
+ }
+ tempzn = 0;
+
+ /* write local headers and copy compressed data */
+ for (g = b[j]; g != (extent)-1; g = (extent)n[g])
+ {
+ if (fseek(e, w[g]->off, SEEK_SET))
+ ziperr(ferror(e) ? ZE_READ : ZE_EOF, zipfile);
+ if ((r = zipcopy(w[g], e, f)) != ZE_OK)
+ {
+ if (r == ZE_TEMP)
+ {
+ if (u && retry()) goto redobin;
+ ziperr(ZE_WRITE, path);
+ }
+ else
+ ziperr(r, zipfile);
+ }
+ }
+
+ /* write central headers */
+ if ((c = ftell(f)) == (ulg)(-1L))
+ {
+ if (u && retry()) goto redobin;
+ ziperr(ZE_WRITE, path);
+ }
+ for (g = b[j], k = 0; g != (extent)-1; g = n[g], k++)
+ if ((r = putcentral(w[g], f)) != ZE_OK)
+ {
+ if (u && retry()) goto redobin;
+ ziperr(ZE_WRITE, path);
+ }
+
+ /* write end-of-central header */
+ if ((t = ftell(f)) == (ulg)(-1L) ||
+ (r = putend(k, t - c, c, (extent)0, (char *)NULL, f)) != ZE_OK ||
+ ferror(f) || fclose(f))
+ {
+ if (u && retry()) goto redobin;
+ ziperr(ZE_WRITE, path);
+ }
+#ifdef RISCOS
+ /* Set the filetype to &DDC */
+ setfiletype(path,0xDDC);
+#endif
+ }
+ fclose(e);
+
+ /* Done! */
+ if (u)
+ fputs("Done.\n", stderr);
+ tfreeall();
+
+ RETURN(0);
+}