/* Copyright (c) 1990-2005 Info-ZIP. All rights reserved. See the accompanying file LICENSE, version 2000-Apr-09 or later (the contents of which are also included in unzip.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 */ /*----------------------------------------------------------------* | envargs - add default options from environment to command line |---------------------------------------------------------------- | Author: Bill Davidsen, original 10/13/91, revised 23 Oct 1991. | This program is in the public domain. |---------------------------------------------------------------- | Minor program notes: | 1. Yes, the indirection is a tad complex | 2. Parentheses were added where not needed in some cases | to make the action of the code less obscure. |---------------------------------------------------------------- | UnZip notes: 24 May 92 ("v1.4"): | 1. #include "unzip.h" for prototypes (24 May 92) | 2. changed ch to type char (24 May 92) | 3. added an ifdef to avoid Borland warnings (24 May 92) | 4. included Rich Wales' mksargs() routine (for MS-DOS, maybe | OS/2? NT?) (4 Dec 93) | 5. added alternate-variable string envstr2 (21 Apr 94) | 6. added support for quoted arguments (6 Jul 96) *----------------------------------------------------------------*/ #define __ENVARGS_C /* identifies this source module */ #define UNZIP_INTERNAL #include "unzip.h" #ifdef __EMX__ /* emx isspace() returns TRUE on extended ASCII !! */ # define ISspace(c) ((c) & 0x80 ? 0 : isspace((unsigned)c)) #else # define ISspace(c) isspace((unsigned)c) #endif /* ?__EMX__ */ #if (!defined(RISCOS) && (!defined(MODERN) || defined(NO_STDLIB_H))) extern char *getenv(); #endif static int count_args OF((ZCONST char *)); /* envargs() returns PK-style error code */ int envargs(Pargc, Pargv, envstr, envstr2) int *Pargc; char ***Pargv; ZCONST char *envstr, *envstr2; { char *envptr; /* value returned by getenv */ char *bufptr; /* copy of env info */ int argc = 0; /* internal arg count */ register int ch; /* spare temp value */ char **argv; /* internal arg vector */ char **argvect; /* copy of vector address */ /* see if anything in the environment */ if ((envptr = getenv(envstr)) != (char *)NULL) /* usual var */ while (ISspace(*envptr)) /* must discard leading spaces */ envptr++; if (envptr == (char *)NULL || *envptr == '\0') if ((envptr = getenv(envstr2)) != (char *)NULL) /* alternate var */ while (ISspace(*envptr)) envptr++; if (envptr == (char *)NULL || *envptr == '\0') return PK_OK; bufptr = malloc(1 + strlen(envptr)); if (bufptr == (char *)NULL) return PK_MEM; #if ((defined(WIN32) || defined(WINDLL)) && !defined(_WIN32_WCE)) # ifdef WIN32 if (IsWinNT()) { /* SPC: don't know codepage of 'real' WinNT console */ strcpy(bufptr, envptr); } else { /* Win95 environment is DOS and uses OEM character coding */ OEM_TO_INTERN(envptr, bufptr); } # else /* !WIN32 */ /* DOS (Win 3.x) environment uses OEM codepage */ OEM_TO_INTERN(envptr, bufptr); # endif #else /* !((WIN32 || WINDLL) && !_WIN32_WCE) */ strcpy(bufptr, envptr); #endif /* ?((WIN32 || WINDLL) && !_WIN32_WCE) */ /* count the args so we can allocate room for them */ argc = count_args(bufptr); /* allocate a vector large enough for all args */ argv = (char **)malloc((argc + *Pargc + 1) * sizeof(char *)); if (argv == (char **)NULL) { free(bufptr); return PK_MEM; } argvect = argv; /* copy the program name first, that's always true */ *(argv++) = *((*Pargv)++); /* copy the environment args next, may be changed */ do { #if defined(AMIGA) || defined(UNIX) if (*bufptr == '"') { char *argstart = ++bufptr; *(argv++) = argstart; for (ch = *bufptr; ch != '\0' && ch != '\"'; ch = *PREINCSTR(bufptr)) if (ch == '\\' && bufptr[1] != '\0') ++bufptr; /* advance to char after backslash */ if (ch != '\0') *(bufptr++) = '\0'; /* overwrite trailing " */ /* remove escape characters */ while ((argstart = MBSCHR(argstart, '\\')) != (char *)NULL) { strcpy(argstart, argstart + 1); if (*argstart) ++argstart; } } else { *(argv++) = bufptr; while ((ch = *bufptr) != '\0' && !ISspace(ch)) INCSTR(bufptr); if (ch != '\0') *(bufptr++) = '\0'; } #else #ifdef DOS_FLX_NLM_OS2_W32 /* we do not support backslash-quoting of quotes in quoted * strings under DOS_FLX_NLM_OS2_W32, because backslashes are * directory separators and double quotes are illegal in filenames */ if (*bufptr == '"') { *(argv++) = ++bufptr; while ((ch = *bufptr) != '\0' && ch != '\"') INCSTR(bufptr); if (ch != '\0') *(bufptr++) = '\0'; } else { *(argv++) = bufptr; while ((ch = *bufptr) != '\0' && !ISspace(ch)) INCSTR(bufptr); if (ch != '\0') *(bufptr++) = '\0'; } #else *(argv++) = bufptr; while ((ch = *bufptr) != '\0' && !ISspace(ch)) INCSTR(bufptr); if (ch != '\0') *(bufptr++) = '\0'; #endif /* ?DOS_FLX_NLM_OS2_W32 */ #endif /* ?(AMIGA || UNIX) */ while ((ch = *bufptr) != '\0' && ISspace(ch)) INCSTR(bufptr); } while (ch); /* now save old argc and copy in the old args */ argc += *Pargc; while (--(*Pargc)) *(argv++) = *((*Pargv)++); /* finally, add a NULL after the last arg, like Unix */ *argv = (char *)NULL; /* save the values and return, indicating succes */ *Pargv = argvect; *Pargc = argc; return PK_OK; } static int count_args(s) ZCONST char *s; { int count = 0; char ch; do { /* count and skip args */ ++count; #if defined(AMIGA) || defined(UNIX) if (*s == '\"') { for (ch = *PREINCSTR(s); ch != '\0' && ch != '\"'; ch = *PREINCSTR(s)) if (ch == '\\' && s[1] != '\0') ++s; if (*s) ++s; /* trailing quote */ } else #else #ifdef DOS_FLX_NLM_OS2_W32 if (*s == '\"') { ++s; /* leading quote */ while ((ch = *s) != '\0' && ch != '\"') INCSTR(s); if (*s) ++s; /* trailing quote */ } else #endif /* DOS_FLX_NLM_OS2_W32 */ #endif /* ?(AMIGA || UNIX) */ while ((ch = *s) != '\0' && !ISspace(ch)) /* note else-clauses above */ INCSTR(s); while ((ch = *s) != '\0' && ISspace(ch)) INCSTR(s); } while (ch); return count; } #ifdef TEST int main(argc, argv) int argc; char **argv; { int err; printf("Orig argv: %p\n", argv); dump_args(argc, argv); if ((err = envargs(&argc, &argv, "ENVTEST")) != PK_OK) { perror("envargs: cannot get memory for arguments"); EXIT(err); } printf(" New argv: %p\n", argv); dump_args(argc, argv); } void dump_args(argc, argv) int argc; char *argv[]; { int i; printf("\nDump %d args:\n", argc); for (i = 0; i < argc; ++i) printf("%3d %s\n", i, argv[i]); } #endif /* TEST */ #ifdef MSDOS /* DOS_OS2? DOS_OS2_W32? */ /* * void mksargs(int *argcp, char ***argvp) * * Substitutes the extended command line argument list produced by * the MKS Korn Shell in place of the command line info from DOS. * * The MKS shell gets around DOS's 128-byte limit on the length of * a command line by passing the "real" command line in the envi- * ronment. The "real" arguments are flagged by prepending a tilde * (~) to each one. * * This "mksargs" routine creates a new argument list by scanning * the environment from the beginning, looking for strings begin- * ning with a tilde character. The new list replaces the original * "argv" (pointed to by "argvp"), and the number of arguments * in the new list replaces the original "argc" (pointed to by * "argcp"). * * Rich Wales */ void mksargs(argcp, argvp) int *argcp; char ***argvp; { #ifndef MSC /* declared differently in MSC 7.0 headers, at least */ #ifndef __WATCOMC__ extern char **environ; /* environment */ #endif #endif char **envp; /* pointer into environment */ char **newargv; /* new argument list */ char **argp; /* pointer into new arg list */ int newargc; /* new argument count */ /* sanity check */ if (environ == NULL || argcp == NULL || argvp == NULL || *argvp == NULL) return; /* find out how many environment arguments there are */ for (envp = environ, newargc = 0; *envp != NULL && (*envp)[0] == '~'; envp++, newargc++) ; if (newargc == 0) return; /* no environment arguments */ /* set up new argument list */ newargv = (char **) malloc(sizeof(char **) * (newargc+1)); if (newargv == NULL) return; /* malloc failed */ for (argp = newargv, envp = environ; *envp != NULL && (*envp)[0] == '~'; *argp++ = &(*envp++)[1]) ; *argp = NULL; /* null-terminate the list */ /* substitute new argument list in place of old one */ *argcp = newargc; *argvp = newargv; } #endif /* MSDOS */