summaryrefslogtreecommitdiff
path: root/vms/vmszip.c
diff options
context:
space:
mode:
Diffstat (limited to 'vms/vmszip.c')
-rw-r--r--vms/vmszip.c703
1 files changed, 703 insertions, 0 deletions
diff --git a/vms/vmszip.c b/vms/vmszip.c
new file mode 100644
index 0000000..2221644
--- /dev/null
+++ b/vms/vmszip.c
@@ -0,0 +1,703 @@
+/*
+ Copyright (c) 1990-2005 Info-ZIP. All rights reserved.
+
+ See the accompanying file LICENSE, version 2004-May-22 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
+*/
+
+/* 2004-09-25 SMS.
+ Added case-insensitive file name comparisons, with the option of
+ preserving case in file names. Defining VMS_PRESERVE_CASE will cause
+ incompatibility with Zip 2.3 and earlier.
+*/
+
+/* #define VMS_PRESERVE_CASE */ /* Not for general use. */
+
+#include "zip.h"
+
+#include <ctype.h>
+#include <time.h>
+#include <unixlib.h>
+
+/* Judge availability of str[n]casecmp() in C RTL.
+ (Note: This must follow a "#include <decc$types.h>" in something to
+ ensure that __CRTL_VER is as defined as it will ever be. DEC C on
+ VAX may not define it itself.)
+*/
+#ifdef __CRTL_VER
+#if __CRTL_VER >= 70000000
+#define HAVE_STRCASECMP
+#endif /* __CRTL_VER >= 70000000 */
+#endif /* def __CRTL_VER */
+
+#ifdef HAVE_STRCASECMP
+#include <strings.h> /* str[n]casecmp() */
+#endif /* def HAVE_STRCASECMP */
+
+#include <descrip.h>
+#include <rms.h>
+#include <ssdef.h>
+#include <starlet.h>
+
+#define PATH_START '['
+#define PATH_END ']'
+#define PATH_START2 '<'
+#define PATH_END2 '>'
+#include "vms/vmsmunch.h"
+
+/* Extra malloc() space in names for cutpath() */
+#define PAD 5 /* may have to change .FOO] to ]FOO.DIR;1 */
+
+
+#ifndef UTIL /* the companion #endif is a bit of ways down ... */
+
+/* The C RTL from OpenVMS 7.0 and newer supplies POSIX compatible versions of
+ * opendir() et al. Thus, we have to use other names in our private code for
+ * directory scanning to prevent symbol name conflicts at link time.
+ * For now, we do not use the library supplied "dirent.h" functions, since
+ * our private implementation provides some functionality which may not be
+ * present in the library versions. For example:
+ * ==> zopendir("DISK:[DIR.SUB1]SUB2.DIR") scans "DISK:[DIR.SUB1.SUB2]".
+ */
+
+typedef struct zdirent {
+ int d_wild; /* flag for wildcard vs. non-wild */
+ struct FAB fab;
+ struct NAM nam;
+ char d_qualwildname[NAM$C_MAXRSS + 1];
+ char d_name[NAM$C_MAXRSS + 1];
+} zDIR;
+
+extern char *label;
+local ulg label_time = 0;
+local ulg label_mode = 0;
+local time_t label_utim = 0;
+
+/* Local functions */
+local void vms_wild OF((char *, zDIR *));
+local zDIR *zopendir OF((ZCONST char *));
+local char *readd OF((zDIR *));
+local char *strlower OF((char *));
+local char *strupper OF((char *));
+
+/* 2004-09-25 SMS.
+ str[n]casecmp() replacement for old C RTL.
+ Assumes a prehistorically incompetent toupper().
+*/
+#ifndef HAVE_STRCASECMP
+
+int strncasecmp( s1, s2, n)
+char *s1;
+char *s2;
+size_t n;
+{
+ /* Initialization prepares for n == 0. */
+ char c1 = '\0';
+ char c2 = '\0';
+
+ while (n-- > 0)
+ {
+ /* Set c1 and c2. Convert lower-case characters to upper-case. */
+ if (islower( c1 = *s1))
+ c1 = toupper( c1);
+
+ if (islower( c2 = *s2))
+ c2 = toupper( c2);
+
+ /* Quit at inequality or NUL. */
+ if ((c1 != c2) || (c1 == '\0'))
+ break;
+
+ s1++;
+ s2++;
+ }
+return ((unsigned int)c1 - (unsigned int)c2);
+}
+
+#ifndef UINT_MAX
+#define UINT_MAX 4294967295U
+#endif
+
+#define strcasecmp( s1, s2) strncasecmp( s1, s2, UINT_MAX)
+
+#endif /* ndef HAVE_STRCASECMP */
+
+
+/* 2004-09-27 SMS.
+ eat_carets().
+ Delete ODS5 extended file name escape characters ("^") in the
+ original buffer.
+ Note that the current scheme handles only simple EFN cases, but it
+ could be made more complicated.
+*/
+
+local void eat_carets( str)
+char *str; /* Source pointer. */
+{
+ char *strd; /* Destination pointer. */
+
+ /* Skip ahead to the first "^", if any. */
+ while ((*str != '\0') && (*str != '^'))
+ str++;
+
+ /* If no caret was found, quit early. */
+ if (*str != '\0')
+ {
+ /* Shift characters leftward as carets are found. */
+ strd = str;
+ while (*str != '\0')
+ {
+ if (*str == '^')
+ {
+ /* Found a caret. Skip it, and take the next character. */
+ *strd = *(++str);
+ }
+ else
+ {
+ /* Found a non-caret. Take it. */
+ *strd = *str;
+ }
+
+ /* Advance destination and source pointers. */
+ strd++;
+ str++;
+ }
+ /* Terminate the destination string. */
+ *strd = '\0';
+ }
+}
+
+/*---------------------------------------------------------------------------
+
+ _vms_findfirst() and _vms_findnext(), based on public-domain DECUS C
+ fwild() and fnext() routines (originally written by Martin Minow, poss-
+ ibly modified by Jerry Leichter for bintnxvms.c), were written by Greg
+ Roelofs and are still in the public domain. Routines approximate the
+ behavior of MS-DOS (MSC and Turbo C) findfirst and findnext functions.
+
+ ---------------------------------------------------------------------------*/
+
+static char wild_version_part[10]="\0";
+
+local void vms_wild(p, d)
+char *p;
+zDIR *d;
+{
+ /*
+ * Do wildcard setup
+ */
+ /* set up the FAB and NAM blocks. */
+ d->fab = cc$rms_fab; /* initialize fab */
+ d->nam = cc$rms_nam; /* initialize nam */
+
+ d->fab.fab$l_nam = &d->nam; /* fab -> nam */
+ d->fab.fab$l_fna = p; /* argument wild name */
+ d->fab.fab$b_fns = strlen(p); /* length */
+
+ d->fab.fab$l_dna = "sys$disk:[]"; /* Default fspec */
+ d->fab.fab$b_dns = sizeof("sys$disk:[]")-1;
+
+ d->nam.nam$l_esa = d->d_qualwildname; /* qualified wild name */
+ d->nam.nam$b_ess = NAM$C_MAXRSS; /* max length */
+ d->nam.nam$l_rsa = d->d_name; /* matching file name */
+ d->nam.nam$b_rss = NAM$C_MAXRSS; /* max length */
+
+ /* parse the file name */
+ if (sys$parse(&d->fab) != RMS$_NORMAL)
+ return;
+ /* Does this replace d->fab.fab$l_fna with a new string in its own space?
+ I sure hope so, since p is free'ed before this routine returns. */
+
+ /* have qualified wild name (i.e., disk:[dir.subdir]*.*); null-terminate
+ * and set wild-flag */
+ d->d_qualwildname[d->nam.nam$b_esl] = '\0';
+ d->d_wild = (d->nam.nam$l_fnb & NAM$M_WILDCARD)? 1 : 0; /* not used... */
+#ifdef DEBUG
+ fprintf(mesg, " incoming wildname: %s\n", p);
+ fprintf(mesg, " qualified wildname: %s\n", d->d_qualwildname);
+#endif /* DEBUG */
+}
+
+local zDIR *zopendir(n)
+ZCONST char *n; /* directory to open */
+/* Start searching for files in the VMS directory n */
+{
+ char *c; /* scans VMS path */
+ zDIR *d; /* malloc'd return value */
+ int m; /* length of name */
+ char *p; /* malloc'd temporary string */
+
+ if ((d = (zDIR *)malloc(sizeof(zDIR))) == NULL ||
+ (p = malloc((m = strlen(n)) + 4)) == NULL) {
+ if (d != NULL) free((zvoid *)d);
+ return NULL;
+ }
+ /* Directory may be in form "[DIR.SUB1.SUB2]" or "[DIR.SUB1]SUB2.DIR;1".
+ If latter, convert to former. */
+ if (m > 0 && *(c = strcpy(p,n)+m-1) != ']')
+ {
+ while (--c > p && *c != ';')
+ ;
+ if ((c- p < 5) || strncasecmp( (c- 4), ".DIR", 4))
+ {
+ free((zvoid *)d); free((zvoid *)p);
+ return NULL;
+ }
+ c -= 3;
+ *c-- = '\0'; /* terminate at "DIR;#" */
+ *c = ']'; /* "." --> "]" */
+ while (c > p && *--c != ']')
+ ;
+ *c = '.'; /* "]" --> "." */
+ }
+ strcat(p, "*.*");
+ strcat(p, wild_version_part);
+ vms_wild(p, d); /* set up wildcard */
+ free((zvoid *)p);
+ return d;
+}
+
+local char *readd(d)
+zDIR *d; /* directory stream to read from */
+/* Return a pointer to the next name in the directory stream d, or NULL if
+ no more entries or an error occurs. */
+{
+ int r; /* return code */
+
+ do {
+ d->fab.fab$w_ifi = 0; /* internal file index: what does this do? */
+
+ /* get next match to possible wildcard */
+ if ((r = sys$search(&d->fab)) == RMS$_NORMAL)
+ {
+ d->d_name[d->nam.nam$b_rsl] = '\0'; /* null terminate */
+ return (char *)d->d_name; /* OK */
+ }
+ } while (r == RMS$_PRV);
+ return NULL;
+}
+
+int wild(p)
+char *p; /* path/pattern to match */
+/* Expand the pattern based on the contents of the file system. Return an
+ error code in the ZE_ class. */
+{
+ zDIR *d; /* stream for reading directory */
+ char *e; /* name found in directory */
+ int f; /* true if there was a match */
+
+ /* special handling of stdin request */
+ if (strcmp(p, "-") == 0) /* if compressing stdin */
+ return newname(p, 0, 0);
+
+ /* Search given pattern for matching names */
+ if ((d = (zDIR *)malloc(sizeof(zDIR))) == NULL)
+ return ZE_MEM;
+ vms_wild(p, d); /* pattern may be more than just directory name */
+
+ /*
+ * Save version specified by user to use in recursive drops into
+ * subdirectories.
+ */
+ strncpy(wild_version_part,d->nam.nam$l_ver,d->nam.nam$b_ver);
+ wild_version_part[d->nam.nam$b_ver] = '\0';
+
+ f = 0;
+ while ((e = readd(d)) != NULL) /* "dosmatch" is already built in */
+ if (procname(e, 0) == ZE_OK)
+ f = 1;
+ free(d);
+
+ /* Done */
+ return f ? ZE_OK : ZE_MISS;
+}
+
+int procname(n, caseflag)
+char *n; /* name to process */
+int caseflag; /* true to force case-sensitive match */
+/* Process a name or sh expression to operate on (or exclude). Return
+ an error code in the ZE_ class. */
+{
+ zDIR *d; /* directory stream from zopendir() */
+ char *e; /* pointer to name from readd() */
+ int m; /* matched flag */
+ char *p; /* path for recursion */
+ struct stat s; /* result of stat() */
+ struct zlist far *z; /* steps through zfiles list */
+
+ if (strcmp(n, "-") == 0) /* if compressing stdin */
+ return newname(n, 0, caseflag);
+ else if (LSSTAT(n, &s)
+#if defined(__TURBOC__) || defined(VMS) || defined(__WATCOMC__)
+ /* For these 3 compilers, stat() succeeds on wild card names! */
+ || isshexp(n)
+#endif
+ )
+ {
+ /* Not a file or directory--search for shell expression in zip file */
+ if (caseflag) {
+ p = malloc(strlen(n) + 1);
+ if (p != NULL)
+ strcpy(p, n);
+ } else
+ 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->name);
+ m = 0;
+ }
+ }
+ free((zvoid *)p);
+ return m ? ZE_MISS : ZE_OK;
+ }
+
+ /* Live name--use if file, recurse if directory */
+ if ((s.st_mode & S_IFDIR) == 0)
+ {
+ /* add or remove name of file */
+ if ((m = newname(n, 0, caseflag)) != ZE_OK)
+ return m;
+ } else {
+ if (dirnames && (m = newname(n, 1, caseflag)) != ZE_OK) {
+ return m;
+ }
+ /* recurse into directory */
+ if (recurse && (d = zopendir(n)) != NULL)
+ {
+ while ((e = readd(d)) != NULL) {
+ if ((m = procname(e, caseflag)) != ZE_OK) /* recurse on name */
+ {
+ free(d);
+ return m;
+ }
+ }
+ free(d);
+ }
+ } /* (s.st_mode & S_IFDIR) == 0) */
+ return ZE_OK;
+}
+
+/* 2004-09-24 SMS.
+ Cuter strlower() and strupper() functions.
+*/
+
+local char *strlower( s)
+char *s;
+/* Convert all uppercase letters to lowercase in string s */
+{
+ for ( ; *s != '\0'; s++)
+ if (isupper( *s))
+ *s = tolower( *s);
+
+ return s;
+}
+
+local char *strupper( s)
+char *s;
+/* Convert all lowercase letters to uppercase in string s */
+{
+ for ( ; *s != '\0'; s++)
+ if (islower( *s))
+ *s = toupper( *s);
+
+ return s;
+}
+
+char *ex2in(x, isdir, pdosflag)
+char *x; /* external file name */
+int isdir; /* input: x is a directory */
+int *pdosflag; /* output: force MSDOS file attributes? */
+/* Convert the external file name to a zip file name, returning the malloc'ed
+ string or NULL if not enough memory. */
+{
+ char *n; /* internal file name (malloc'ed) */
+ char *t; /* shortened name */
+ int dosflag;
+
+ dosflag = dosify; /* default for non-DOS and non-OS/2 */
+
+ /* Find starting point in name before doing malloc */
+ t = x;
+ if ((n = strrchr(t, ':')) != NULL)
+ t = n + 1;
+ if ( (*t == PATH_START && (n = strrchr(t, PATH_END)) != NULL)
+ || (*t == PATH_START2 && (n = strrchr(t, PATH_END2)) != NULL) )
+ /* external name contains valid VMS path specification */
+ if (*(++t) == '.')
+ /* path is relative to current directory, skip leading '.' */
+ t++;
+
+ if (!pathput)
+ t = last(last(t, PATH_END), PATH_END2);
+
+ /* Malloc space for internal name and copy it */
+ if ((n = malloc(strlen(t) + 1)) == NULL)
+ return NULL;
+ strcpy(n, t);
+
+ if (((t = strrchr(n, PATH_END)) != NULL) ||
+ (t = strrchr(n, PATH_END2)) != NULL)
+ {
+ *t = '/';
+ while (--t > n)
+ if (*t == '.')
+ *t = '/';
+ }
+
+ /* Fix from Greg Roelofs: */
+ /* Get current working directory and strip from n (t now = n) */
+ {
+ char cwd[256], *p, *q;
+ int c;
+
+ q = getcwd( cwd, 256);
+
+ /* 2004-09-24 SMS.
+ With SET PROCESSS /PARSE = EXTENDED, getcwd() can return a
+ mixed-case result, confounding the comparisons below with an
+ all-uppercase name in "n". Always use a case-insensitive
+ comparison around here.
+ */
+
+#if 0 /* fix by Igor */
+ if ((q != NULL) && ((p = strchr(cwd, '.')) != NULL))
+#else
+ if ((q != NULL) && ((p = strchr(cwd, PATH_START)) != NULL ||
+ (p = strchr(cwd, PATH_START2)) != NULL))
+#endif
+ {
+ if (*(++p) == '.')
+ p++;
+ if ((q = strrchr(p, PATH_END)) != NULL ||
+ (q = strrchr(p, PATH_END2)) != NULL)
+ {
+ *q = '/';
+ while (--q > p)
+ if (*q == '.')
+ *q = '/';
+
+ /* strip bogus path parts from n */
+ if (strncasecmp( n, p, (c = strlen( p))) == 0)
+ {
+ q = n + c;
+ while (*t++ = *q++)
+ ;
+ }
+ }
+ }
+ }
+
+#ifndef VMS_PRESERVE_CASE
+ strlower( n);
+#endif /* ndef VMS_PRESERVE_CASE */
+
+ /* Remove simple ODS5 extended file name escape characters. */
+ eat_carets( n);
+
+ if (isdir)
+ {
+ if (strcasecmp( (t = n + strlen( n) - 6), ".DIR;1"))
+ error("directory not version 1");
+ else
+ if (pathput)
+ strcpy(t, "/");
+ else
+ *n = '\0'; /* directories are discarded with zip -rj */
+ }
+ else if (!vmsver)
+ if ((t = strrchr(n, ';')) != NULL)
+ *t = '\0';
+
+ if ((t = strrchr(n, '.')) != NULL)
+ {
+ if ( t[1] == '\0') /* "filename." -> "filename" */
+ *t = '\0';
+ else if (t[1] == ';') /* "filename.;vvv" -> "filename;vvv" */
+ {
+ char *f = t+1;
+ while (*t++ = *f++) ;
+ }
+ }
+
+ if (dosify)
+ msname(n);
+
+ /* Returned malloc'ed name */
+ if (pdosflag)
+ *pdosflag = dosflag;
+ return n;
+}
+
+
+char *in2ex(n)
+char *n; /* internal file name */
+/* Convert the zip file name to an external file name, returning the malloc'ed
+ string or NULL if not enough memory. */
+{
+ char *x; /* external file name */
+ char *t; /* scans name */
+
+ if ((t = strrchr(n, '/')) == NULL)
+ {
+ if ((x = malloc(strlen(n) + 1 + PAD)) == NULL)
+ return NULL;
+ strcpy(x, n);
+ }
+ else
+ {
+ if ((x = malloc(strlen(n) + 3 + PAD)) == NULL)
+ return NULL;
+ x[0] = PATH_START;
+ x[1] = '.';
+ strcpy(x + 2, n);
+ *(t = x + 2 + (t - n)) = PATH_END;
+ while (--t > x)
+ if (*t == '/')
+ *t = '.';
+ }
+
+#ifndef VMS_PRESERVE_CASE
+ strupper( x);
+#endif /* ndef VMS_PRESERVE_CASE */
+
+ return x;
+}
+
+void stamp(f, d)
+char *f; /* name of file to change */
+ulg d; /* dos-style time to change it to */
+/* Set last updated and accessed time of file f to the DOS time d. */
+{
+ int tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year;
+ char timbuf[24];
+ static ZCONST char *month[] = {"JAN", "FEB", "MAR", "APR", "MAY", "JUN",
+ "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
+ struct VMStimbuf {
+ char *actime; /* VMS revision date, ASCII format */
+ char *modtime; /* VMS creation date, ASCII format */
+ } ascii_times;
+
+ ascii_times.actime = ascii_times.modtime = timbuf;
+
+ /* Convert DOS time to ASCII format for VMSmunch */
+ tm_sec = (int)(d << 1) & 0x3e;
+ tm_min = (int)(d >> 5) & 0x3f;
+ tm_hour = (int)(d >> 11) & 0x1f;
+ tm_mday = (int)(d >> 16) & 0x1f;
+ tm_mon = ((int)(d >> 21) & 0xf) - 1;
+ tm_year = ((int)(d >> 25) & 0x7f) + 1980;
+ sprintf(timbuf, "%02d-%3s-%04d %02d:%02d:%02d.00", tm_mday, month[tm_mon],
+ tm_year, tm_hour, tm_min, tm_sec);
+
+ /* Set updated and accessed times of f */
+ if (VMSmunch(f, SET_TIMES, (char *)&ascii_times) != RMS$_NMF)
+ zipwarn("can't set zipfile time: ", f);
+}
+
+ulg filetime(f, a, n, t)
+char *f; /* name of file to get info on */
+ulg *a; /* return value: file attributes */
+long *n; /* return value: file size */
+iztimes *t; /* return value: access, modific. and creation times */
+/* If file *f does not exist, return 0. Else, return the file's last
+ modified date and time as an MSDOS date and time. The date and
+ time is returned in a long with the date most significant to allow
+ unsigned integer comparison of absolute times. Also, if a is not
+ a NULL pointer, store the file attributes there, with the high two
+ bytes being the Unix attributes, and the low byte being a mapping
+ of that to DOS attributes. If n is not NULL, store the file size
+ there. If t is not NULL, the file's access, modification and creation
+ times are stored there as UNIX time_t values.
+ If f is "-", use standard input as the file. If f is a device, return
+ a file size of -1 */
+{
+ struct stat s; /* results of stat() */
+ /* malloc name so not dependent on FNMAX - 11/8/04 EG */
+ char *name;
+ int len = strlen(f);
+
+ if (f == label) {
+ if (a != NULL)
+ *a = label_mode;
+ if (n != NULL)
+ *n = -2; /* convention for a label name */
+ if (t != NULL)
+ t->atime = t->mtime = t->ctime = label_utim;
+ return label_time;
+ }
+ if ((name = malloc(len + 1)) == NULL) {
+ ZIPERR(ZE_MEM, "filetime");
+ }
+ strcpy(name, f);
+ if (name[len - 1] == '/')
+ name[len - 1] = '\0';
+ /* not all systems allow stat'ing a file with / appended */
+
+ if (strcmp(f, "-") == 0) {
+ if (fstat(fileno(stdin), &s) != 0) {
+ free(name);
+ error("fstat(stdin)");
+ }
+ } else if (LSSTAT(name, &s) != 0) {
+ /* Accept about any file kind including directories
+ * (stored with trailing / with -r option)
+ */
+ free(name);
+ return 0;
+ }
+ free(name);
+
+ if (a != NULL) {
+ *a = ((ulg)s.st_mode << 16) | !(s.st_mode & S_IWRITE);
+ if ((s.st_mode & S_IFDIR) != 0) {
+ *a |= MSDOS_DIR_ATTR;
+ }
+ }
+ if (n != NULL)
+ *n = (s.st_mode & S_IFMT) == S_IFREG ? s.st_size : -1;
+ if (t != NULL) {
+ t->atime = s.st_mtime;
+#ifdef USE_MTIME
+ t->mtime = s.st_mtime; /* Use modification time in VMS */
+#else
+ t->mtime = s.st_ctime; /* Use creation time in VMS */
+#endif
+ t->ctime = s.st_ctime;
+ }
+
+#ifdef USE_MTIME
+ return unix2dostime((time_t *)&s.st_mtime); /* Use modification time in VMS */
+#else
+ return unix2dostime((time_t *)&s.st_ctime); /* Use creation time in VMS */
+#endif
+}
+
+int deletedir(d)
+char *d; /* directory to delete */
+/* Delete the directory *d if it is empty, do nothing otherwise.
+ Return the result of rmdir(), delete(), or system().
+ For VMS, d must be in format [x.y]z.dir;1 (not [x.y.z]).
+ */
+{
+ /* code from Greg Roelofs, who horked it from Mark Edwards (unzip) */
+ int r, len;
+ char *s; /* malloc'd string for system command */
+
+ len = strlen(d);
+ if ((s = malloc(len + 34)) == NULL)
+ return 127;
+
+ system(strcat(strcpy(s, "set prot=(o:rwed) "), d));
+ r = delete(d);
+ free(s);
+ return r;
+}
+
+#endif /* !UTIL */