1/*
2  Copyright (c) 1990-2000 Info-ZIP.  All rights reserved.
3
4  See the accompanying file LICENSE, version 2000-Apr-09 or later
5  (the contents of which are also included in unzip.h) for terms of use.
6  If, for some reason, all these files are missing, the Info-ZIP license
7  also may be found at:  ftp://ftp.info-zip.org/pub/infozip/license.html
8*/
9/*----------------------------------------------------------------*
10 | envargs - add default options from environment to command line
11 |----------------------------------------------------------------
12 | Author: Bill Davidsen, original 10/13/91, revised 23 Oct 1991.
13 | This program is in the public domain.
14 |----------------------------------------------------------------
15 | Minor program notes:
16 |  1. Yes, the indirection is a tad complex
17 |  2. Parentheses were added where not needed in some cases
18 |     to make the action of the code less obscure.
19 |----------------------------------------------------------------
20 | UnZip notes: 24 May 92 ("v1.4"):
21 |  1. #include "unzip.h" for prototypes (24 May 92)
22 |  2. changed ch to type char (24 May 92)
23 |  3. added an ifdef to avoid Borland warnings (24 May 92)
24 |  4. included Rich Wales' mksargs() routine (for MS-DOS, maybe
25 |     OS/2? NT?) (4 Dec 93)
26 |  5. added alternate-variable string envstr2 (21 Apr 94)
27 |  6. added support for quoted arguments (6 Jul 96)
28 *----------------------------------------------------------------*/
29
30
31#define __ENVARGS_C     /* identifies this source module */
32#define UNZIP_INTERNAL
33#include "unzip.h"
34
35#ifdef __EMX__          /* emx isspace() returns TRUE on extended ASCII !! */
36#  define ISspace(c) ((c) & 0x80 ? 0 : isspace((unsigned)c))
37#else
38#  define ISspace(c) isspace((unsigned)c)
39#endif /* ?__EMX__ */
40
41static int count_args OF((ZCONST char *));
42
43
44/* envargs() returns PK-style error code */
45
46int envargs(Pargc, Pargv, envstr, envstr2)
47    int *Pargc;
48    char ***Pargv;
49    ZCONST char *envstr, *envstr2;
50{
51#ifndef RISCOS
52    char *getenv();
53#endif
54    char *envptr;       /* value returned by getenv */
55    char *bufptr;       /* copy of env info */
56    int argc = 0;       /* internal arg count */
57    register int ch;    /* spare temp value */
58    char **argv;        /* internal arg vector */
59    char **argvect;     /* copy of vector address */
60
61    /* see if anything in the environment */
62    if ((envptr = getenv(envstr)) != (char *)NULL)        /* usual var */
63        while (ISspace(*envptr))        /* must discard leading spaces */
64            envptr++;
65    if (envptr == (char *)NULL || *envptr == '\0')
66        if ((envptr = getenv(envstr2)) != (char *)NULL)   /* alternate var */
67            while (ISspace(*envptr))
68                envptr++;
69    if (envptr == (char *)NULL || *envptr == '\0')
70        return PK_OK;
71
72    bufptr = malloc(1 + strlen(envptr));
73    if (bufptr == (char *)NULL)
74        return PK_MEM;
75#if (defined(WIN32) || defined(WINDLL))
76# ifdef WIN32
77    if (IsWinNT()) {
78        /* SPC: don't know codepage of 'real' WinNT console */
79        strcpy(bufptr, envptr);
80    } else {
81        /* Win95 environment is DOS and uses OEM character coding */
82        OEM_TO_INTERN(envptr, bufptr);
83    }
84# else /* !WIN32 */
85    /* DOS environment uses OEM codepage */
86    OEM_TO_INTERN(envptr, bufptr);
87# endif
88#else /* !(WIN32 || WINDLL) */
89    strcpy(bufptr, envptr);
90#endif /* ?(WIN32 || WINDLL) */
91
92    /* count the args so we can allocate room for them */
93    argc = count_args(bufptr);
94    /* allocate a vector large enough for all args */
95    argv = (char **)malloc((argc + *Pargc + 1) * sizeof(char *));
96    if (argv == (char **)NULL) {
97        free(bufptr);
98        return PK_MEM;
99    }
100    argvect = argv;
101
102    /* copy the program name first, that's always true */
103    *(argv++) = *((*Pargv)++);
104
105    /* copy the environment args next, may be changed */
106    do {
107#if defined(AMIGA) || defined(UNIX)
108        if (*bufptr == '"') {
109            char *argstart = ++bufptr;
110
111            *(argv++) = argstart;
112            for (ch = *bufptr; ch != '\0' && ch != '\"';
113                 ch = *PREINCSTR(bufptr))
114                if (ch == '\\' && bufptr[1] != '\0')
115                    ++bufptr;           /* advance to char after backslash */
116            if (ch != '\0')
117                *(bufptr++) = '\0';     /* overwrite trailing " */
118
119            /* remove escape characters */
120            while ((argstart = MBSCHR(argstart, '\\')) != (char *)NULL) {
121                strcpy(argstart, argstart + 1);
122                if (*argstart)
123                    ++argstart;
124            }
125        } else {
126            *(argv++) = bufptr;
127            while ((ch = *bufptr) != '\0' && !ISspace(ch))
128                INCSTR(bufptr);
129            if (ch != '\0')
130                *(bufptr++) = '\0';
131        }
132#else
133#ifdef DOS_FLX_NLM_OS2_W32
134        /* we do not support backslash-quoting of quotes in quoted
135         * strings under DOS_FLX_NLM_OS2_W32, because backslashes are
136         * directory separators and double quotes are illegal in filenames */
137        if (*bufptr == '"') {
138            *(argv++) = ++bufptr;
139            while ((ch = *bufptr) != '\0' && ch != '\"')
140                INCSTR(bufptr);
141            if (ch != '\0')
142                *(bufptr++) = '\0';
143        } else {
144            *(argv++) = bufptr;
145            while ((ch = *bufptr) != '\0' && !ISspace(ch))
146                INCSTR(bufptr);
147            if (ch != '\0')
148                *(bufptr++) = '\0';
149        }
150#else
151        *(argv++) = bufptr;
152        while ((ch = *bufptr) != '\0' && !ISspace(ch))
153            INCSTR(bufptr);
154        if (ch != '\0')
155            *(bufptr++) = '\0';
156#endif /* ?DOS_FLX_NLM_OS2_W32 */
157#endif /* ?(AMIGA || UNIX) */
158        while ((ch = *bufptr) != '\0' && ISspace(ch))
159            INCSTR(bufptr);
160    } while (ch);
161
162    /* now save old argc and copy in the old args */
163    argc += *Pargc;
164    while (--(*Pargc))
165        *(argv++) = *((*Pargv)++);
166
167    /* finally, add a NULL after the last arg, like Unix */
168    *argv = (char *)NULL;
169
170    /* save the values and return, indicating succes */
171    *Pargv = argvect;
172    *Pargc = argc;
173
174    return PK_OK;
175}
176
177
178
179static int count_args(s)
180    ZCONST char *s;
181{
182    int count = 0;
183    char ch;
184
185    do {
186        /* count and skip args */
187        ++count;
188#if defined(AMIGA) || defined(UNIX)
189        if (*s == '\"') {
190            for (ch = *PREINCSTR(s);  ch != '\0' && ch != '\"';
191                 ch = *PREINCSTR(s))
192                if (ch == '\\' && s[1] != '\0')
193                    ++s;
194            if (*s)
195                ++s;        /* trailing quote */
196        } else
197#else
198#ifdef DOS_FLX_NLM_OS2_W32
199        if (*s == '\"') {
200            ++s;                /* leading quote */
201            while ((ch = *s) != '\0' && ch != '\"')
202                INCSTR(s);
203            if (*s)
204                ++s;        /* trailing quote */
205        } else
206#endif /* DOS_FLX_NLM_OS2_W32 */
207#endif /* ?(AMIGA || UNIX) */
208        while ((ch = *s) != '\0' && !ISspace(ch))  /* note else-clauses above */
209            INCSTR(s);
210        while ((ch = *s) != '\0' && ISspace(ch))
211            INCSTR(s);
212    } while (ch);
213
214    return count;
215}
216
217
218
219#ifdef TEST
220
221int main(argc, argv)
222    int argc;
223    char **argv;
224{
225    int err;
226
227    printf("Orig argv: %p\n", argv);
228    dump_args(argc, argv);
229    if ((err = envargs(&argc, &argv, "ENVTEST")) != PK_OK) {
230        perror("envargs:  cannot get memory for arguments");
231        EXIT(err);
232    }
233    printf(" New argv: %p\n", argv);
234    dump_args(argc, argv);
235}
236
237
238
239void dump_args(argc, argv)
240    int argc;
241    char *argv[];
242{
243    int i;
244
245    printf("\nDump %d args:\n", argc);
246    for (i = 0; i < argc; ++i)
247        printf("%3d %s\n", i, argv[i]);
248}
249
250#endif /* TEST */
251
252
253
254#ifdef MSDOS   /* DOS_OS2?  DOS_OS2_W32? */
255
256/*
257 * void mksargs(int *argcp, char ***argvp)
258 *
259 *    Substitutes the extended command line argument list produced by
260 *    the MKS Korn Shell in place of the command line info from DOS.
261 *
262 *    The MKS shell gets around DOS's 128-byte limit on the length of
263 *    a command line by passing the "real" command line in the envi-
264 *    ronment.  The "real" arguments are flagged by prepending a tilde
265 *    (~) to each one.
266 *
267 *    This "mksargs" routine creates a new argument list by scanning
268 *    the environment from the beginning, looking for strings begin-
269 *    ning with a tilde character.  The new list replaces the original
270 *    "argv" (pointed to by "argvp"), and the number of arguments
271 *    in the new list replaces the original "argc" (pointed to by
272 *    "argcp").
273 *
274 *    Rich Wales
275 */
276void mksargs(argcp, argvp)
277    int *argcp;
278    char ***argvp;
279{
280#ifndef MSC /* declared differently in MSC 7.0 headers, at least */
281#ifndef __WATCOMC__
282    extern char **environ;          /* environment */
283#endif
284#endif
285    char        **envp;             /* pointer into environment */
286    char        **newargv;          /* new argument list */
287    char        **argp;             /* pointer into new arg list */
288    int         newargc;            /* new argument count */
289
290    /* sanity check */
291    if (environ == NULL || argcp == NULL || argvp == NULL || *argvp == NULL)
292        return;
293
294    /* find out how many environment arguments there are */
295    for (envp = environ, newargc = 0;
296         *envp != NULL && (*envp)[0] == '~';
297         envp++, newargc++)
298        ;
299    if (newargc == 0)
300        return;     /* no environment arguments */
301
302    /* set up new argument list */
303    newargv = (char **) malloc(sizeof(char **) * (newargc+1));
304    if (newargv == NULL)
305        return;     /* malloc failed */
306
307    for (argp = newargv, envp = environ; *envp != NULL && (*envp)[0] == '~';
308         *argp++ = &(*envp++)[1])
309        ;
310    *argp = NULL;   /* null-terminate the list */
311
312    /* substitute new argument list in place of old one */
313    *argcp = newargc;
314    *argvp = newargv;
315}
316
317#endif /* MSDOS */
318