1 /*
2   Copyright (c) 1990-2005 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 
41 #if (!defined(RISCOS) && (!defined(MODERN) || defined(NO_STDLIB_H)))
42 extern char *getenv();
43 #endif
44 static int count_args OF((ZCONST char *));
45 
46 
47 /* envargs() returns PK-style error code */
48 
envargs(Pargc,Pargv,envstr,envstr2)49 int envargs(Pargc, Pargv, envstr, envstr2)
50     int *Pargc;
51     char ***Pargv;
52     ZCONST char *envstr, *envstr2;
53 {
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)) && !defined(_WIN32_WCE))
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 (Win 3.x) environment uses OEM codepage */
86     OEM_TO_INTERN(envptr, bufptr);
87 # endif
88 #else /* !((WIN32 || WINDLL) && !_WIN32_WCE) */
89     strcpy(bufptr, envptr);
90 #endif /* ?((WIN32 || WINDLL) && !_WIN32_WCE) */
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 
count_args(s)179 static 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 
main(argc,argv)221 int 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 
dump_args(argc,argv)239 void 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  */
mksargs(argcp,argvp)276 void 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