/*---------------------------------------------------------------------------  mapname.c  This routine changes DEC-20, VAX/VMS, and DOS-style filenames into normal  Unix names (and vice versa, in some cases); it also creates any necessary   directories, if the -d switch was specified.  ---------------------------------------------------------------------------  Notes:     - This routine REALLY needs to be rewritten (different routines for       each output OS, with different rules for different parts of the path       name).  If each zip program stores local-format names (like the VMS       one did at one time), it would probably be best to convert to an in-       termediate format first (assuming we're not extracting under the same       OS as that under which the zipfile was created), then from that to       the current operating system's format.     - The strcpy and strcat operations on both cdp and filename may over-       write memory, since they don't check lengths.  With a kilobyte in       which to work, this is probably not that big a deal, but it could       cause problems eventually.  ---------------------------------------------------------------------------*/#include "unzip.h"/*******************//* Mapname Defines *//*******************/#ifdef VMS#  define PERMS   0#else#  define PERMS   0777#endif#ifndef NO_MKDIR#  if (defined(DOS_OS2) && !defined(__GO32__))#    if (_MSC_VER >= 600)       /* have special MSC mkdir prototype */#      include <direct.h>#    else                       /* own prototype because dir.h conflicts? */       int mkdir(const char *path);#    endif /* ?(MSC 6.0 or later) */#    define MKDIR(path,mode)   mkdir(path)#  else /* !DOS_OS2 || __GO32__ */#    ifdef MACOS#      define MKDIR(path,mode)   macmkdir(path,gnVRefNum,glDirID)#    else /* !MACOS */#      define MKDIR(path,mode)   mkdir(path,mode)#    endif /* ?MACOS */#  endif /* ?(DOS_OS2 && !__GO32__)  */#endif /* !NO_MKDIR *//************************//*  Function mapname()  *//************************/int mapname(create_dirs)   /* return 0 if no error, 1 if caution (filename */    int create_dirs;       /*  truncated), 2 if warning (skip file because */{                          /*  dir doesn't exist), 3 if error (skip file) */#ifdef NO_MKDIR    char command[FILNAMSIZ+40]; /* buffer for system() call */#endif#ifdef VMS    int stat_val;               /* temp. holder for stat() return value */    char *dp, *xp;              /* pointers to directory name */    char *np;                   /* pointer into filename */#endif /* VMS */#ifdef DOS_VMS    char *last_dot=NULL;        /* last dot not converted to underscore */#endif /* DOS_VMS */#ifdef OS2    char *last;    extern char longfilename[]; /*  AFTER file created and closed */    extern int longname;        /* used also in file_io.c:  set EAs */    int longdir;#endif /* OS2 */    char name[FILNAMSIZ];       /* file name buffer */    char *pp, *cp, *cdp;        /* character pointers */    char delim = '\0';          /* directory delimiter */    int quote = FALSE;          /* flags */    int indir = FALSE;    int done = FALSE;    int created = FALSE;    register unsigned workch;   /* hold the character being tested *//*---------------------------------------------------------------------------    Initialize various pointers and counters and stuff.  ---------------------------------------------------------------------------*/#ifdef MAP_DEBUG    fprintf(stderr, "%s ", filename);   /* echo name of this file */#endif    cdp = (char *)NULL;    pp = name;                  /* point to translation buffer */    *name = '\0';               /* initialize buffer */    if (!jflag) {               /* -j => junk pathnames */        cdp = (char *)malloc(strlen(filename) + 3);   /* place for holding */        if (cdp == (char *)NULL) {                    /*  directory name */            fprintf(stderr, "mapname:  out of memory [%s]\n", filename);            return 3;        }#ifdef VMS        *cdp++ = '[';        xp = cdp;               /* always points to last non-NULL char */        *cdp++ = '.';#endif /* VMS */#ifdef MACOS        *cdp = ':';             /* the Mac uses ':' as a directory separator */        cdp[1] = '\0';#else /* !MACOS */        *cdp = '\0';#endif /* ?MACOS */    }/*---------------------------------------------------------------------------    Begin main loop through characters in filename.  ---------------------------------------------------------------------------*/    for (cp = filename; (workch = (unsigned char) *cp++) != 0  &&  !done;) {        if (quote) {                 /* if char quoted, */            *pp++ = (char) workch;   /*  include it literally */            quote = FALSE;        } else if (indir) {          /* if in directory name, */            if (workch == (unsigned)delim)  /*  look for end delimiter */                indir = FALSE;        } else            switch (workch) {            case '<':                /* discard DEC-20 directory name */                indir = TRUE;                delim = '>';                break;            case '[':                /* discard VMS directory name */                indir = TRUE;                delim = ']';                break;            case '/':                /* discard Unix path name  */            case '\\':               /*  or MS-DOS path name... */                                     /*  iff -j flag was given  */                /*                 * Special processing case:  if -j flag was not specified on                 * command line and create_dirs is TRUE, create any necessary                 * directories included in the pathname.  Creation of dirs is                 * straightforward on BSD and MS-DOS machines but requires use                 * of the system() command on SysV systems (or any others which                 * don't have mkdir()).  The stat() check is necessary with                 * MSC because it doesn't have an EEXIST errno, and it saves                 * the overhead of multiple system() calls on SysV machines.                 */                if (!jflag) {                    *pp = '\0';#ifdef VMS                    dp = name;                    while (*++xp = *dp++);  /* copy name to cdp */                    last_dot = NULL;        /* dir name:  no dots allowed */                    strcpy(xp, ".dir");     /* add extension for stat check */                    stat_val = stat(cdp, &statbuf);                    *xp = '\0';             /* remove extension for all else */                    if (stat_val) {         /* doesn't exist, so create */#else /* !VMS */#ifdef MSDOS                    if (last_dot != NULL) {  /* one dot in dir name is legal */                        *last_dot = '.';                        last_dot = NULL;                    }#endif /* MSDOS */                    strcat(cdp, name);#ifdef OS2                    if ((longdir = !IsFileNameValid(cdp)) != 0) {                        last = strrchr(cdp, '/');                        strcpy(longfilename, last ? last + 1 : cdp);                        fprintf(stderr, "renaming directory \"%s\"", cdp);                        ChangeNameForFAT(cdp);                        fprintf(stderr, " to \"%s\"\n", cdp);                    }#endif /* OS2 */                    if (stat(cdp, &statbuf)) {  /* doesn't exist, so create */#endif /* ?VMS */                        if (!create_dirs) /* told not to create (freshening) */                            return 2;#ifdef NO_MKDIR                        sprintf(command,                          "IFS=\" \t\n\" /bin/mkdir %s 2>/dev/null", cdp);                        if (system(command)) {#else /* !NO_MKDIR */                        if (MKDIR(cdp, PERMS) == -1) {#endif /* ?NO_MKDIR */                            perror(cdp);                            free(cdp);                            fprintf(stderr, "mapame:  unable to process [%s]\n",                              filename);                            return 3;                        }                        created = TRUE;#ifdef OS2                        if (longdir)                            SetLongNameEA(cdp, longfilename);#endif /* OS2 */                    } else if (!(statbuf.st_mode & S_IFDIR)) {                        fprintf(stderr,                          "mapname:  %s exists but is not a directory\n", cdp);                        free(cdp);                        fprintf(stderr, "mapame:  unable to process [%s]\n",                          filename);                        return 3;                    }#ifdef VMS                    *xp = '/';  /* for now... (mkdir()) */#else /* !VMS */#ifdef MACOS                    strcat(cdp, ":");#else /* !MACOS */                    strcat(cdp, "/");#endif /* ?MACOS */#endif /* ?VMS */                }                pp = name;                break;            case ':':#ifdef UNIX                       /* colon is a valid character in Unix */                *pp++ = workch;   /*  filenames, so keep it; anywhere else, */#else /* !UNIX */                 /*  change it to an underscore (should  */                *pp++ = '_';      /*  NOT have stored drive/node names!!) */#endif /* ?UNIX */             /* pp = name;  (OLD) discard DEC dev: or node:: name */                break;            case '.':                   /* DEC-20 generation number or */#ifdef DOS_VMS                          /*  MS-DOS or VMS separator */                last_dot = pp;          /* point at last dot so far... */                *pp++ = '_';            /* convert dot to underscore */#else /* !DOS_VMS */                *pp++ = workch;#endif /* ?DOS_VMS */                break;            case ';':                   /* VMS generation or DEC-20 attrib */#ifdef MACOS                if (V_flag || macflag)#else /* !MACOS */                if (V_flag)                 /* if requested, save VMS ";##" */#endif /* ?MACOS */                         /*  version info or Macintosh */                    *pp++ = (char) workch;  /*  (?) info; otherwise discard */                else                        /*  everything starting with */                    done = TRUE;            /*  semicolon.  (Worry about */                break;                      /*  DEC-20 later.) */            case '\026':                /* control-V quote for special chars */                quote = TRUE;           /* set flag for next character */                break;            case ' ':#if (defined(VMS) || defined(MTS))                *pp++ = '_';            /* change spaces to underscore */#else /* !(VMS || MTS) */               /*  under VMS and MTS, and under DOS */#ifdef DOS_OS2                          /*  and OS/2 if -s not specified. */                if (!sflag)                    *pp++ = '_';                else#endif /* DOS_OS2 */                *pp++ = (char) workch;  /* otherwise, leave as spaces */#endif /* ?(VMS || MTS) */                break;            default:#ifdef MACOS                if ((macflag && ((unsigned)workch > 0x1F)) || isprint(workch))#else /* !MACOS */#if (defined(DOS_OS2) || (defined(UNIX) && !defined(VMS)))  /* allow non-US */                if (isprint(workch) || (128 <= workch && workch <= 254))#else /* !(DOS_OS2 || UNIX) */                if (isprint(workch))    /* other printable, just keep */#endif /* ?(DOS_OS2 || UNIX) */#endif /* ?MACOS */                    *pp++ = (char) workch;            } /* end switch */    } /* end for loop */    *pp = '\0';                         /* done with name:  terminate it */#ifdef DOS_VMS                          /*  and put a dot back in if VMS */    if (last_dot != NULL)               /*  or MS-DOS */        *last_dot = '.';#endif /* DOS_VMS *//*---------------------------------------------------------------------------    We COULD check for existing names right now, create a "unique" name, etc.    At present, we do this in extract_or_test_files() (immediately after we    return from here).  If conversion went bad, the name'll either be nulled    out (in which case we'll return non-0), or following procedures won't be    able to create the extracted file and other error msgs will result.  ---------------------------------------------------------------------------*/    if (filename[strlen(filename) - 1] == '/') {        /* A directory was extracted. It had a trailing /,          * don't report the error below. */        if (created) {            printf("   Creating: %s", filename);#ifdef OS2            SetPathInfo(filename, lrec.last_mod_file_date,                                  lrec.last_mod_file_time, -1);            if (extra_field)                SetEAs(filename, extra_field);#endif            printf("\n");        }        return 2; /* but skip file */    }    if (*name == '\0') {        fprintf(stderr, "mapname:  conversion of [%s] failed\n", filename);        return 3;    }#ifdef OS2    if (!longname && ((longname = !IsFileNameValid(name)) != 0)) {        /* in case of second call after user renamed the file, skip this */        last = strrchr(name, '/');      /* THIS time, save for file_io */        last = last ? last + 1 : name;  /* only last component */        strcpy(longfilename, last);        fprintf(stderr, "renaming \"%s\"", name);        ChangeNameForFAT(last);        fprintf(stderr, " to \"%s\"\n", name);    }#endif /* OS2 */#ifdef VMS    /* convert filename to legal VMS one, substituting underscores for     * all invalid characters */    for (np = name;  *np;  ++np)        if (!(isdigit(*np) || isalpha(*np) || (*np == '$') ||            (*np == '-') || (*np == '_') || (*np == '.') || (*np == ';')))            *np = '_';#endif /* VMS */    if (!jflag) {#ifdef VMS        *xp++ = ']';                 /* proper end-of-dir-name delimiter */        if (xp == cdp) {             /* no path-name stuff, so... */            strcpy(filename, name);  /* copy file name into global */            cdp -= 2;                /*   prepare to free malloc'd space */        } else {                     /* we've added path-name stuff... */            *xp = '\0';              /*   so terminate and convert to */            dp = cdp;                /*   VMS subdir separators (skip */            while (*++dp)            /*   first char:  better not be */                if (*dp == '/')      /*   "/"):  change all slashes */                    *dp = '.';       /*   to dots */            cdp -= 2;                /*   include leading bracket and dot */            strcpy(filename, cdp);   /* copy VMS-style path name into global */            strcat(filename, name);  /* concatenate file name to global */        }#else /* !VMS */        strcpy(filename, cdp);       /* either "" or slash-terminated path */        strcat(filename, name);      /* append file name to path name */#endif /* ?VMS */        free(cdp);    } else        strcpy(filename, name);      /* copy converted name into global */#if PATH_MAX < (FILNAMSIZ - 1)    /* check the length of the file name and truncate if necessary */    if (PATH_MAX < strlen(filename)) {        fprintf(stderr, "caution:  truncating filename\n");        filename[PATH_MAX] = '\0';        fprintf(stderr, "[ %s ]\n", filename);        return 1;             /* 1:  warning error */    }#endif    return 0;}