1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444 |
- /*
- Copyright (c) 1990-2007 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
- */
- /* 2005-02-14 SMS.
- Added some ODS5 support.
- Use longer name structures in NAML, where available.
- Locate special characters mindful of "^" escapes.
- Replaced compile-time case preservation (VMS_PRESERVE_CASE macro)
- with command-line-specified case preservation (vms_case_x
- variables).
- Prototyped all functions.
- Removed "#ifndef UTIL", as no one should be compiling it that way.
- */
- #include "zip.h"
- #include "vmsmunch.h"
- #include "vms.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 <dvidef.h>
- #include <lib$routines.h>
- #include <ssdef.h>
- #include <stsdef.h>
- #include <starlet.h>
- /* Directory file type with version, and its strlen(). */
- #define DIR_TYPE_VER ".DIR;1"
- #define DIR_TYPE_VER_LEN (sizeof( DIR_TYPE_VER)- 1)
- /* Extra malloc() space in names for cutpath(). (May have to change
- ".FOO]" to "]FOO.DIR;1".)
- */
- #define DIR_PAD (DIR_TYPE_VER_LEN- 1)
- /* Hex digit table. */
- char hex_digit[ 16] = {
- '0', '1', '2', '3', '4', '5', '6', '7',
- '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
- };
- /* Character property table for (re-)escaping ODS5 extended file names.
- Note that this table ignore Unicode, and does not identify invalid
- characters.
- ODS2 valid characters: 0-9 A-Z a-z $ - _
- ODS5 Invalid characters:
- C0 control codes (0x00 to 0x1F inclusive)
- Asterisk (*)
- Question mark (?)
- ODS5 Invalid characters only in VMS V7.2 (which no one runs, right?):
- Double quotation marks (")
- Backslash (\)
- Colon (:)
- Left angle bracket (<)
- Right angle bracket (>)
- Slash (/)
- Vertical bar (|)
- Characters escaped by "^":
- SP ! # % & ' ( ) + , . ; = @ [ ] ^ ` { } ~
- Either "^_" or "^ " is accepted as a space. Period (.) is a special
- case. Note that un-escaped < and > can also confuse a directory
- spec.
- Characters put out as ^xx:
- 7F (DEL)
- 80-9F (C1 control characters)
- A0 (nonbreaking space)
- FF (Latin small letter y diaeresis)
- Other cases:
- Unicode: "^Uxxxx", where "xxxx" is four hex digits.
- Property table values:
- Normal escape: 1
- Space: 2
- Dot: 4
- Hex-hex escape: 8
- -------------------
- Hex digit: 64
- */
- unsigned char char_prop[ 256] = {
- /* NUL SOH STX ETX EOT ENQ ACK BEL BS HT LF VT FF CR SO SI */
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- /* DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US */
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- /* SP ! " # $ % & ' ( ) * + , - . / */
- 2, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 4, 0,
- /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
- 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0, 1, 1, 1, 1, 1,
- /* @ A B C D E F G H I J K L M N O */
- 1, 64, 64, 64, 64, 64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- /* P Q R S T U V W X Y Z [ \ ] ^ _ */
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0,
- /* ` a b c d e f g h i j k l m n o */
- 1, 64, 64, 64, 64, 64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- /* p q r s t u v w x y z { | } ~ DEL */
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8
- };
- /* 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_STRUCT nam;
- char d_qualwildname[ NAM_MAXRSS+ 1];
- char d_name[ NAM_MAXRSS+ 1];
- } zDIR;
- extern char *label;
- local ulg label_time = 0;
- local ulg label_mode = 0;
- local time_t label_utim = 0;
- local int relative_dir_s = 0; /* Relative directory spec. */
- /* 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( 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 does not handle all EFN cases, but it
- could be made more complicated.
- */
- local void eat_carets( char *str)
- /* char *str; Source pointer. */
- {
- char *strd; /* Destination pointer. */
- char hdgt;
- unsigned char uchr;
- unsigned char prop;
- /* 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')
- {
- uchr = *str;
- if (uchr == '^')
- {
- /* Found a caret. Skip it, and check the next character. */
- uchr = *(++str);
- prop = char_prop[ uchr];
- if (prop& 64)
- {
- /* Hex digit. Get char code from this and next hex digit. */
- if (uchr <= '9')
- {
- hdgt = uchr- '0'; /* '0' - '9' -> 0 - 9. */
- }
- else
- {
- hdgt = ((uchr- 'A')& 7)+ 10; /* [Aa] - [Ff] -> 10 - 15. */
- }
- hdgt <<= 4; /* X16. */
- uchr = *(++str); /* Next char must be hex digit. */
- if (uchr <= '9')
- {
- uchr = hdgt+ uchr- '0';
- }
- else
- {
- uchr = hdgt+ ((uchr- 'A')& 15)+ 10;
- }
- }
- else if (uchr == '_')
- {
- /* Convert escaped "_" to " ". */
- uchr = ' ';
- }
- else if (uchr == '/')
- {
- /* Convert escaped "/" (invalid Zip) to "?" (invalid VMS). */
- uchr = '?';
- }
- /* Else, not a hex digit. Must be a simple escaped character
- (or Unicode, which is not yet handled here).
- */
- }
- /* Else, not a caret. Use as-is. */
- *strd = uchr;
- /* Advance destination and source pointers. */
- strd++;
- str++;
- }
- /* Terminate the destination string. */
- *strd = '\0';
- }
- }
- /* 2007-05-22 SMS.
- * explicit_dev().
- *
- * Determine if an explicit device name is present in a (VMS) file
- * specification.
- */
- local int explicit_dev( char *file_spec)
- {
- int sts;
- struct FAB fab; /* FAB. */
- struct NAM_STRUCT nam; /* NAM[L]. */
- /* Initialize the FAB and NAM[L], and link the NAM[L] to the FAB. */
- nam = CC_RMS_NAM;
- fab = cc$rms_fab;
- fab.FAB_NAM = &nam;
- /* Point the FAB/NAM[L] fields to the actual name and default name. */
- #ifdef NAML$C_MAXRSS
- fab.fab$l_dna = (char *) -1; /* Using NAML for default name. */
- fab.fab$l_fna = (char *) -1; /* Using NAML for file name. */
- #endif /* def NAML$C_MAXRSS */
- /* File name. */
- FAB_OR_NAML( fab, nam).FAB_OR_NAML_FNA = file_spec;
- FAB_OR_NAML( fab, nam).FAB_OR_NAML_FNS = strlen( file_spec);
- nam.NAM_NOP = NAM_M_SYNCHK; /* Syntax-only analysis. */
- sts = sys$parse( &fab, 0, 0); /* Parse the file spec. */
- /* Device found = $PARSE success and "device was explicit" flag. */
- return (((sts& STS$M_SEVERITY) == STS$M_SUCCESS) &&
- ((nam.NAM_FNB& NAM_M_EXP_DEV) != 0));
- }
- /* 2005-02-04 SMS.
- find_dir().
- Find directory boundaries in an ODS2 or ODS5 file spec.
- Returns length (zero if no directory, negative if error),
- and sets "start" argument to first character (typically "[") location.
- No one will care about the details, but the return values are:
- 0 No dir.
- -2 [, no end. -3 <, no end.
- -4 [, multiple start. -5 <, multiple start.
- -8 ], no start. -9 >, no start.
- -16 ], wrong end. -17 >, wrong end.
- -32 ], multiple end. -33 >, multiple end.
- Note that the current scheme handles only simple EFN cases, but it
- could be made more complicated.
- */
- int find_dir( char *file_spec, char **start)
- {
- char *cp;
- char chr;
- char *end_tmp = NULL;
- char *start_tmp = NULL;
- int lenth = 0;
- for (cp = file_spec; cp < file_spec+ strlen( file_spec); cp++)
- {
- chr = *cp;
- if (chr == '^')
- {
- /* Skip ODS5 extended name escaped characters. */
- cp++;
- /* If escaped char is a hex digit, skip the second hex digit, too. */
- if (char_prop[ (unsigned char) *cp]& 64)
- cp++;
- }
- else if (chr == '[')
- {
- /* Found start. */
- if (start_tmp == NULL)
- {
- /* First time. Record start location. */
- start_tmp = cp;
- /* Error if no end. */
- lenth = -2;
- }
- else
- {
- /* Multiple start characters. */
- lenth = -4;
- break;
- }
- }
- else if (chr == '<')
- {
- /* Found start. */
- if (start_tmp == NULL)
- {
- /* First time. Record start location. */
- start_tmp = cp;
- /* Error if no end. */
- lenth = -3;
- }
- else
- {
- /* Multiple start characters. */
- lenth = -5;
- break;
- }
- }
- else if (chr == ']')
- {
- /* Found end. */
- if (end_tmp == NULL)
- {
- /* First time. */
- if (lenth == 0)
- {
- /* End without start. */
- lenth = -8;
- break;
- }
- else if (lenth != -2)
- {
- /* Wrong kind of end. */
- lenth = -16;
- break;
- }
- /* End ok. Record end location. */
- end_tmp = cp;
- lenth = end_tmp+ 1- start_tmp;
- /* Could break here, ignoring excessive end characters. */
- }
- else
- {
- /* Multiple end characters. */
- lenth = -32;
- break;
- }
- }
- else if (chr == '>')
- {
- /* Found end. */
- if (end_tmp == NULL)
- {
- /* First time. */
- if (lenth == 0)
- {
- /* End without start. */
- lenth = -9;
- break;
- }
- else if (lenth != -3)
- {
- /* Wrong kind of end. */
- lenth = -17;
- break;
- }
- /* End ok. Record end location. */
- end_tmp = cp;
- lenth = end_tmp+ 1- start_tmp;
- /* Could break here, ignoring excessive end characters. */
- }
- else
- {
- /* Multiple end characters. */
- lenth = -33;
- break;
- }
- }
- }
- /* If both start and end were found,
- then set result pointer where safe.
- */
- if (lenth > 0)
- {
- if (start != NULL)
- {
- *start = start_tmp;
- }
- }
- return lenth;
- }
- /* 2005-02-08 SMS.
- file_sys_type().
- Determine the file system type for the (VMS) path name argument.
- */
- local int file_sys_type( char *path)
- {
- int acp_code;
- #ifdef DVI$C_ACP_F11V5
- /* Should know about ODS5 file system. Do actual check.
- (This should be non-VAX with __CRTL_VER >= 70200000.)
- */
- int sts;
- struct dsc$descriptor_s dev_descr =
- { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0 };
- /* Load path argument into device descriptor. */
- dev_descr.dsc$a_pointer = path;
- dev_descr.dsc$w_length = strlen( dev_descr.dsc$a_pointer);
- /* Get filesystem type code.
- (Text results for this item code have been unreliable.)
- */
- sts = lib$getdvi( &((int) DVI$_ACPTYPE), 0, &dev_descr, &acp_code, 0, 0);
- if ((sts & STS$M_SUCCESS) != STS$K_SUCCESS)
- {
- acp_code = -1;
- }
- #else /* def DVI$C_ACP_F11V5 */
- /* Too old for ODS5 file system. Must be ODS2. */
- acp_code = DVI$C_ACP_F11V2;
- #endif /* def DVI$C_ACP_F11V5 */
- return acp_code;
- }
- /*---------------------------------------------------------------------------
- _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.
- 2005-01-04 SMS.
- Changed to use NAML instead of NAM, where available.
- ---------------------------------------------------------------------------*/
- static char wild_version_part[10]="\0";
- local void vms_wild( char *p, zDIR *d)
- {
- /*
- * Do wildcard setup.
- */
- /* Set up the FAB and NAM[L] blocks. */
- d->fab = cc$rms_fab; /* Initialize FAB. */
- d->nam = CC_RMS_NAM; /* Initialize NAM[L]. */
- d->fab.FAB_NAM = &d->nam; /* FAB -> NAM[L] */
- #ifdef NAML$C_MAXRSS
- d->fab.fab$l_dna =(char *) -1; /* Using NAML for default name. */
- d->fab.fab$l_fna = (char *) -1; /* Using NAML for file name. */
- #endif /* def NAML$C_MAXRSS */
- /* Argument file name and length. */
- d->FAB_OR_NAML( fab, nam).FAB_OR_NAML_FNA = p;
- d->FAB_OR_NAML( fab, nam).FAB_OR_NAML_FNS = strlen(p);
- #define DEF_DEVDIR "SYS$DISK:[]"
- /* Default file spec and length. */
- d->FAB_OR_NAML( fab, nam).FAB_OR_NAML_DNA = DEF_DEVDIR;
- d->FAB_OR_NAML( fab, nam).FAB_OR_NAML_DNS = sizeof( DEF_DEVDIR)- 1;
- d->nam.NAM_ESA = d->d_qualwildname; /* qualified wild name */
- d->nam.NAM_ESS = NAM_MAXRSS; /* max length */
- d->nam.NAM_RSA = d->d_name; /* matching file name */
- d->nam.NAM_RSS = NAM_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_ESL] = '\0';
- d->d_wild = (d->nam.NAM_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( ZCONST char *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.
- 2005-01-31 SMS. Changed to require ";1", as VMS does, which
- simplified the code slightly, too. Note that ODS5 allows ".DIR" in
- any case (upper, lower, mixed).
- */
- if ((m > 0) && (*(c = strcpy(p,n)+m-1) != ']'))
- {
- if ((c- p < DIR_TYPE_VER_LEN) ||
- strcasecmp((c+ 1- DIR_TYPE_VER_LEN), DIR_TYPE_VER))
- {
- free((zvoid *)d); free((zvoid *)p);
- return NULL;
- }
- c -= 4; /* The "D". */
- *c-- = '\0'; /* terminate at "DIR;1" */
- *c = ']'; /* "." --> "]" */
- /* Replace the formerly last "]" with ".".
- For ODS5, ignore "^]".
- */
- while ((c > p) && ((*--c != ']') || (*(c- 1) == '^')))
- ;
- *c = '.'; /* "]" --> "." */
- }
- strcat(p, "*.*");
- strcat(p, wild_version_part);
- vms_wild(p, d); /* set up wildcard */
- free((zvoid *)p);
- return d;
- }
- local char *readd( zDIR *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? */
- /*
- 2005-02-04 SMS.
- From the docs:
- Note that you must close the file before invoking the Search
- service (FAB$W_IFI must be 0).
- The same is true for PARSE. Most likely, it's cleared by setting
- "fab = cc$rms_fab", and left that way, so clearing it here may very
- well be pointless. (I think it is, and I've never seen it explicitly
- cleared elsewhere, but I haven't tested it everywhere either.)
- */
- /* get next match to possible wildcard */
- if ((r = sys$search(&d->fab)) == RMS$_NORMAL)
- {
- d->d_name[d->nam.NAM_RSL] = '\0'; /* null terminate */
- return (char *)d->d_name; /* OK */
- }
- } while (r == RMS$_PRV);
- return NULL;
- }
- int wild( char *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.
- Note that any command-line file argument may need wildcard expansion,
- so all user-specified constituent file names pass through here.
- */
- {
- zDIR *d; /* stream for reading directory */
- char *e; /* name found in directory */
- int f; /* true if there was a match */
- int dir_len; /* Length of the directory part of the name. */
- char *dir_start; /* First character of the directory part. */
- /* special handling of stdin request */
- if (strcmp(p, "-") == 0) /* if compressing stdin */
- return newname(p, 0, 0);
- /* Determine whether this name has an absolute or relative directory
- spec. It's relative if there is no directory, or if the directory
- has a leading dot ("[.").
- */
- dir_len = find_dir( p, &dir_start);
- relative_dir_s = ((dir_len <= 0)? 1 : (dir_start[ 1] == '.'));
- /* 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( char *n, int 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( 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( 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( char *x, int isdir, int *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.
- 2005-02-09 SMS.
- Added some ODS5 support.
- Note that if we were really clever, we'd save the truncated original
- file name for later use as "iname", instead of running the de-escaped
- product back through in2ex() to recover it later.
- 2005-11-13 SMS.
- Changed to translate "[..." into enough "/" characters to cause
- in2ex() to reconstruct it. This should not be needed, however, as
- pattern matching really should avoid ex2in() and in2ex().
- */
- {
- char *n; /* Internal file name (malloc'ed). */
- char *nn; /* Temporary "n"-like pointer. */
- char *ext_dir_and_name; /* External dir]name (less "dev:["). */
- char chr; /* Temporary character storage. */
- int dosflag;
- int down_case; /* Resultant down-case flag. */
- int dir_len; /* Directory spec length. */
- int ods_level; /* File system type. */
- dosflag = dosify; /* default for non-DOS and non-OS/2 */
- /* Locate the directory part of the external name. */
- dir_len = find_dir( x, &ext_dir_and_name);
- if (dir_len <= 0)
- {
- /* Directory not found. Use whole external name. */
- ext_dir_and_name = x;
- }
- else if (pathput)
- {
- /* Include directory. */
- if (ext_dir_and_name[ 1] == '.')
- {
- /* Relative path. If not a directory-depth wildcard, then drop
- first "[." (or "<."). If "[..." (or "<..."), then preserve all
- characters, including the first "[" (or "<") for special
- handling below.
- */
- if ((ext_dir_and_name[ 2] != '.') || (ext_dir_and_name[ 3] != '.'))
- {
- /* Normal relative path. Drop first "[." (or "<."). */
- dir_len -= 2;
- ext_dir_and_name += 2;
- }
- }
- else
- {
- /* Absolute path. Skip first "[" (or "<"). */
- dir_len -= 1;
- ext_dir_and_name += 1;
- /* 2007-04-26 SMS.
- Skip past "000000." or "000000]" (or "000000>"), which should
- not be stored in the archive. This arises, for example, with
- "zip -r archive [000000]foo.dir"
- */
- #define MFD "000000"
- if ((strncmp( ext_dir_and_name, MFD, strlen( MFD)) == 0) &&
- ((ext_dir_and_name[ 6] == '.') ||
- (ext_dir_and_name[ 6] == ']') ||
- (ext_dir_and_name[ 6] == '>')))
- {
- dir_len -= 7;
- ext_dir_and_name += 7;
- }
- }
- }
- else
- {
- /* Junking paths. Skip the whole directory spec. */
- ext_dir_and_name += dir_len;
- dir_len = 0;
- }
- /* Malloc space for internal name and copy it. */
- if ((n = malloc(strlen( ext_dir_and_name)+ 1)) == NULL)
- return NULL;
- strcpy( n, ext_dir_and_name);
- /* Convert VMS directory separators (".") to "/". */
- if (dir_len > 0)
- {
- for (nn = n; nn < n+ dir_len; nn++)
- {
- chr = *nn;
- if (chr == '^')
- {
- /* Skip ODS5 extended name escaped characters. */
- nn++;
- /* If escaped char is a hex digit, skip the second hex digit, too. */
- if (char_prop[ (unsigned char) *nn]& 64)
- nn++;
- }
- else if ((chr == '.') || ((nn == n) && ((chr == '[') || (chr == '<'))))
- {
- /* Convert VMS directory separator (".", or initial "[" or "<"
- of "[..." or "<...") to "/".
- */
- *nn = '/';
- }
- }
- /* Replace directory end character (typically "]") with "/". */
- n[ dir_len- 1] = '/';
- }
- /* If relative path, then strip off the current directory. */
- if (relative_dir_s)
- {
- char cwd[ NAM_MAXRSS+ 1];
- char *cwd_dir_only;
- char *q;
- int cwd_dir_only_len;
- q = getcwd( cwd, (sizeof( cwd)- 1));
- /* 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.
- */
- /* Locate the directory part of the external name. */
- dir_len = find_dir( q, &cwd_dir_only);
- if (dir_len > 0)
- {
- /* Skip first "[" (or "<"). */
- cwd_dir_only++;
- /* Convert VMS directory separators (".") to "/". */
- for (q = cwd_dir_only; q < cwd_dir_only+ dir_len; q++)
- {
- chr = *q;
- if (chr == '^')
- {
- /* Skip ODS5 extended name escaped characters. */
- q++;
- /* If escaped char is a hex digit, skip the second hex digit, too. */
- if (char_prop[ (unsigned char) *q]& 64)
- q++;
- }
- else if (chr == '.')
- {
- /* Convert VMS directory separator (".") to "/". */
- *q = '/';
- }
- }
- /* Replace directory end character (typically "]") with "/". */
- cwd_dir_only[ dir_len- 2] = '/';
- }
- /* If the slash-converted cwd matches the front of the internal
- name, then shuffle the remainder of the internal name to the
- beginning of the internal name storage.
- Because we already know that the path is relative, this test may
- always succeed.
- */
- cwd_dir_only_len = strlen( cwd_dir_only);
- if (strncasecmp( n, cwd_dir_only, cwd_dir_only_len) == 0)
- {
- nn = n+ cwd_dir_only_len;
- q = n;
- while (*q++ = *nn++);
- }
- } /* (relative_dir_s) */
- /* 2007-05-22 SMS.
- * If a device name is present, assume that it's a real (VMS) file
- * specification, and do down-casing according to the ODS2 or ODS5
- * down-casing policy. If no device name is present, assume that it's
- * a pattern ("-i", ...), and do no down-casing here. (Case
- * sensitivity in patterns is handled elsewhere.)
- */
- if (explicit_dev( x))
- {
- /* If ODS5 is possible, do complicated down-case check.
- Note that the test for ODS2/ODS5 is misleading and over-broad.
- Here, "ODS2" includes anything from DVI$C_ACP_F11V1 (=1, ODS1) up
- to (but not including) DVI$C_ACP_F11V5 (= 11, DVI$C_ACP_F11V5),
- while "ODS5" includes anything from DVI$C_ACP_F11V5 on up. See
- DVIDEF.H.
- */
- #if defined( DVI$C_ACP_F11V5) && defined( NAML$C_MAXRSS)
- /* Check options and/or ODS level for down-case or preserve case. */
- down_case = 0; /* Assume preserve case. */
- if ((vms_case_2 <= 0) && (vms_case_5 < 0))
- {
- /* Always down-case. */
- down_case = 1;
- }
- else if ((vms_case_2 <= 0) || (vms_case_5 < 0))
- {
- /* Down-case depending on ODS level. (Use (full) external name.) */
- ods_level = file_sys_type( x);
- if (ods_level > 0)
- {
- /* Valid ODS level. (Name (full) contains device.)
- * Down-case accordingly.
- */
- if (((ods_level < DVI$C_ACP_F11V5) && (vms_case_2 <= 0)) ||
- ((ods_level >= DVI$C_ACP_F11V5) && (vms_case_5 < 0)))
- {
- /* Down-case for this ODS level. */
- down_case = 1;
- }
- }
- }
- #else /* defined( DVI$C_ACP_F11V5) && defined( NAML$C_MAXRSS) */
- /* No case-preserved names are possible (VAX). Do simple down-case check. */
- down_case = (vms_case_2 <= 0);
- #endif /* defined( DVI$C_ACP_F11V5) && defined( NAML$C_MAXRSS) [else] */
- /* If down-casing, convert to lower case. */
- if (down_case != 0)
- {
- strlower( n);
- }
- }
- /* Remove simple ODS5 extended file name escape characters. */
- eat_carets( n);
- if (isdir)
- {
- if (strcasecmp( (nn = n+ strlen( n)- DIR_TYPE_VER_LEN), DIR_TYPE_VER))
- error("directory not version 1");
- else
- if (pathput)
- strcpy( nn, "/");
- else
- *n = '\0'; /* directories are discarded with zip -rj */
- }
- else if (vmsver == 0)
- {
- /* If not keeping version numbers, truncate the name at the ";".
- (No escaped characters are expected in the version.)
- */
- if ((ext_dir_and_name = strrchr( n, ';')) != NULL)
- *ext_dir_and_name = '\0';
- }
- else if (vmsver > 1)
- {
- /* Keeping version numbers, but as ".nnn", not ";nnn". */
- if ((ext_dir_and_name = strrchr( n, ';')) != NULL)
- *ext_dir_and_name = '.';
- }
- /* Remove a type-less dot. */
- /* (Note that currently "name..ver" is not altered.) */
- if ((ext_dir_and_name = strrchr( n, '.')) != NULL)
- {
- if (ext_dir_and_name[ 1] == '\0') /* "name." -> "name" */
- *ext_dir_and_name = '\0';
- else if (ext_dir_and_name[ 1] == ';') /* "name.;ver" -> "name;ver" */
- {
- char *f = ext_dir_and_name+ 1;
- while (*ext_dir_and_name++ = *f++);
- }
- }
- if (dosify)
- msname(n);
- /* Returned malloc'ed name */
- if (pdosflag)
- *pdosflag = dosflag;
- return n;
- }
- char *in2ex( char *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 */
- int i;
- char chr;
- char *endp;
- char *last_slash;
- char *versionp;
- #ifdef NAML$C_MAXRSS
- char buf[ NAML$C_MAXRSS+ 1];
- unsigned char prop;
- unsigned char uchr;
- char *last_dot;
- #endif /* def NAML$C_MAXRSS */
- /* Locate the last slash. */
- last_slash = strrchr( n, '/');
- /* If ODS5 is possible, replace escape carets in name. */
- #ifdef NAML$C_MAXRSS
- endp = n+ strlen( n);
- /* Locate the version delimiter, if one is expected. */
- if (vmsver == 0)
- { /* No version expected. */
- versionp = endp;
- }
- else
- {
- if (vmsver > 1)
- { /* Expect a dot-version, ".nnn". Locate the version ".".
- Temporarily terminate at this dot to allow the last-dot search
- below to find the last non-version dot.
- */
- versionp = strrchr( n, '.');
- if (versionp != NULL) /* Can't miss. */
- {
- *versionp = '\0';
- }
- }
- else
- { /* Expect a semi-colon-version, ";nnn". Locate the ";". */
- versionp = strrchr( n, ';');
- }
- if ((versionp == NULL) || (versionp < last_slash))
- { /* If confused, and the version delimiter was not in the name,
- then ignore it.
- */
- versionp = endp;
- }
- }
- /* No escape needed for the last dot, if it's part of the file name.
- All dots in a directory must be escaped.
- */
- last_dot = strrchr( n, '.');
- if ((last_dot != NULL) && (last_slash != NULL) && (last_dot < last_slash))
- {
- last_dot = last_slash;
- }
- /* Replace the version dot if necessary. */
- if ((vmsver > 1) && (versionp != NULL) && (versionp < endp))
- {
- *versionp = '.';
- }
- /* Add ODS5 escape sequences. Leave "/" and "?" for later.
- The name here looks (roughly) like: dir1/dir2/a.b
- */
- t = n;
- x = buf;
- while (uchr = *t++)
- {
- /* Characters in the version do not need escaping. */
- if (t <= versionp)
- {
- prop = char_prop[ uchr]& 31;
- if (prop)
- {
- if (prop& 4)
- { /* Dot. */
- if (t < last_dot)
- {
- /* Dot which must be escaped. */
- *x++ = '^';
- }
- }
- else if (prop& 8)
- {
- /* Character needing hex-hex escape. */
- *x++ = '^';
- *x++ = hex_digit[ uchr>> 4];
- uchr = hex_digit[ uchr& 15];
- }
- else
- {
- /* Non-dot character which must be escaped (and simple works).
- "?" gains the caret but remains "?" until later.
- ("/" remains (unescaped) "/".)
- */
- *x++ = '^';
- if (prop& 2)
- {
- /* Escaped space (represented as "^_"). */
- uchr = '_';
- }
- }
- }
- }
- *x++ = uchr;
- }
- *x = '\0';
- /* Point "n" to altered name buffer, and re-find the last slash. */
- n = buf;
- last_slash = strrchr( n, '/');
- #endif /* def NAML$C_MAXRSS */
- if ((t = last_slash) == NULL)
- {
- if ((x = malloc(strlen(n) + 1 + DIR_PAD)) == NULL)
- return NULL;
- strcpy(x, n);
- }
- else
- {
- if ((x = malloc(strlen(n) + 3 + DIR_PAD)) == NULL)
- return NULL;
- /* Begin with "[". */
- x[ 0] = '[';
- i = 1;
- if (*n != '/')
- {
- /* Relative path. Add ".". */
- x[ i++] = '.';
- }
- else
- {
- /* Absolute path. Skip leading "/". */
- n++;
- }
- strcpy( (x+ i), n);
- /* Place the final ']'. Remember where the name starts. */
- *(t = x + i + (t - n)) = ']';
- last_slash = t;
- /* Replace "/" with ".", and "?" with (now escaped) "/", in the
- directory part of the name.
- */
- while (--t > x)
- {
- chr = *t;
- if (chr == '/')
- {
- *t = '.';
- }
- else if (chr == '?')
- {
- *t = '/';
- }
- }
- /* Replace "?" with (now escaped) "/", in the non-directory part of
- the name.
- */
- while ((chr = *(++last_slash)) != '\0')
- {
- if (chr == '?')
- {
- *last_slash = '/';
- }
- }
- }
- /* If case preservation is impossible (VAX, say), and down-casing, then
- up-case. If case preservation is possible and wasn't done, then
- there's no way to ensure proper restoration of original case, so
- don't try. This may differ from pre-3.0 behavior.
- */
- #ifndef NAML$C_MAXRSS
- if (vms_case_2 <= 0)
- {
- strupper( x);
- }
- #endif /* ndef NAML$C_MAXRSS */
- return x;
- }
- void stamp( char *f, ulg 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( char *f, ulg *a, zoff_t *n, iztimes *t)
- /* char *f; name of file to get info on */
- /* ulg *a; return value: file attributes */
- /* zoff_t *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() */
- /* convert to a malloc string dump 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( char *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;
- }
|