1/*
2  Copyright (c) 1990-2006 Info-ZIP.  All rights reserved.
3
4  See the accompanying file LICENSE, version 2005-Feb-10 or later
5  (the contents of which are also included in zip.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 *  util.c by Mark Adler.
11 */
12#define __UTIL_C
13
14#include "zip.h"
15#include "ebcdic.h"
16#include <ctype.h>
17
18#ifdef MSDOS16
19#  include <dos.h>
20#endif
21
22uch upper[256], lower[256];
23/* Country-dependent case map table */
24
25
26#ifndef UTIL /* UTIL picks out namecmp code (all utils) */
27
28/* Local functions */
29local int recmatch OF((ZCONST char *, ZCONST char *, int));
30local int count_args OF((char *s));
31
32#ifdef MSDOS16
33  local unsigned ident OF((unsigned chr));
34#endif
35
36#ifdef NO_MKTIME
37#  ifndef IZ_MKTIME_ONLY
38#    define IZ_MKTIME_ONLY      /* only mktime() related code is pulled in */
39#  endif
40#  include "timezone.c"
41#endif
42
43#ifndef HAVE_FSEEKABLE
44int fseekable(fp)
45FILE *fp;
46{
47    long x;
48
49    return (fp == NULL || (fseek(fp, -1L, SEEK_CUR) == 0 &&
50            (x = ftell(fp)) >= 0 &&
51            fseek(fp,  1L, SEEK_CUR) == 0 &&
52            ftell(fp) == x + 1));
53}
54#endif /* HAVE_FSEEKABLE */
55
56char *isshexp(p)
57char *p;                /* candidate sh expression */
58/* If p is a sh expression, a pointer to the first special character is
59   returned.  Otherwise, NULL is returned. */
60{
61  for (; *p; INCSTR(p))
62    if (*p == '\\' && *(p+1))
63      p++;
64#ifdef VMS
65    else if (*p == '%' || *p == '*')
66#else /* !VMS */
67# ifdef RISCOS
68    /* RISC OS uses # as its single-character wildcard */
69    else if (*p == '#' || *p == '*' || *p == '[')
70# else /* !RISC OS */
71    else if (*p == '?' || *p == '*' || *p == '[')
72# endif
73#endif /* ?VMS */
74      return p;
75  return NULL;
76}
77
78
79local int recmatch(p, s, cs)
80ZCONST char *p;  /* sh pattern to match */
81ZCONST char *s;  /* string to match it to */
82int cs;          /* flag: force case-sensitive matching */
83/* Recursively compare the sh pattern p with the string s and return 1 if
84   they match, and 0 or 2 if they don't or if there is a syntax error in the
85   pattern.  This routine recurses on itself no deeper than the number of
86   characters in the pattern. */
87{
88  int c;        /* pattern char or start of range in [-] loop */
89  /* Get first character, the pattern for new recmatch calls follows */
90  c = *POSTINCSTR(p);
91
92  /* If that was the end of the pattern, match if string empty too */
93  if (c == 0)
94    return *s == 0;
95
96  /* '?' (or '%' or '#') matches any character (but not an empty string) */
97#ifdef VMS
98  if (c == '%')
99#else /* !VMS */
100# ifdef RISCOS
101  if (c == '#')
102# else /* !RISC OS */
103  if (c == '?')
104# endif
105#endif /* ?VMS */
106#ifdef WILD_STOP_AT_DIR
107    return (*s && *s != '/') ? recmatch(p, s + CLEN(s), cs) : 0;
108#else
109    return *s ? recmatch(p, s + CLEN(s), cs) : 0;
110#endif
111
112  /* '*' matches any number of characters, including zero */
113#ifdef AMIGA
114  if (c == '#' && *p == '?')            /* "#?" is Amiga-ese for "*" */
115    c = '*', p++;
116#endif /* AMIGA */
117  if (c == '*')
118  {
119    if (*p == 0)
120      return 1;
121#ifdef WILD_STOP_AT_DIR
122    for (; *s && *s != '/'; INCSTR(s))
123      if ((c = recmatch(p, s, cs)) != 0)
124        return c;
125    return (*p == '/' || (*p == '\\' && p[1] == '/'))
126      ? recmatch(p, s, cs) : 2;
127#else /* !WILD_STOP_AT_DIR */
128    if (!isshexp((char *)p))
129    {
130      /* optimization for rest of pattern being a literal string */
131
132      /* optimization to handle patterns like *.txt */
133      /* if the first char in the pattern is '*' and there */
134      /* are no other shell expression chars, i.e. a literal string */
135      /* then just compare the literal string at the end */
136
137      ZCONST char *srest;
138
139      srest = s + (strlen(s) - strlen(p));
140      if (srest - s < 0)
141        /* remaining literal string from pattern is longer than rest of
142           test string, there can't be a match
143         */
144        return 0;
145      else
146        /* compare the remaining literal pattern string with the last bytes
147           of the test string to check for a match */
148#ifdef _MBCS
149      {
150        ZCONST char *q = s;
151
152        /* MBCS-aware code must not scan backwards into a string from
153         * the end.
154         * So, we have to move forward by character from our well-known
155         * character position s in the test string until we have advanced
156         * to the srest position.
157         */
158        while (q < srest)
159          INCSTR(q);
160        /* In case the byte *srest is a trailing byte of a multibyte
161         * character, we have actually advanced past the position (srest).
162         * For this case, the match has failed!
163         */
164        if (q != srest)
165          return 0;
166        return ((cs ? strcmp(p, q) : namecmp(p, q)) == 0);
167      }
168#else /* !_MBCS */
169        return ((cs ? strcmp(p, srest) : namecmp(p, srest)) == 0);
170#endif /* ?_MBCS */
171    }
172    else
173    {
174      /* pattern contains more wildcards, continue with recursion... */
175      for (; *s; INCSTR(s))
176        if ((c = recmatch(p, s, cs)) != 0)
177          return (int)c;
178      return 2;           /* 2 means give up--shmatch will return false */
179    }
180#endif /* ?WILD_STOP_AT_DIR */
181  }
182
183#ifndef VMS             /* No bracket matching in VMS */
184  /* Parse and process the list of characters and ranges in brackets */
185  if (c == '[')
186  {
187    int e;              /* flag true if next char to be taken literally */
188    ZCONST char *q;     /* pointer to end of [-] group */
189    int r;              /* flag true to match anything but the range */
190
191    if (*s == 0)                        /* need a character to match */
192      return 0;
193    p += (r = (*p == '!' || *p == '^')); /* see if reverse */
194    for (q = p, e = 0; *q; q++)         /* find closing bracket */
195      if (e)
196        e = 0;
197      else
198        if (*q == '\\')
199          e = 1;
200        else if (*q == ']')
201          break;
202    if (*q != ']')                      /* nothing matches if bad syntax */
203      return 0;
204    for (c = 0, e = *p == '-'; p < q; p++)      /* go through the list */
205    {
206      if (e == 0 && *p == '\\')         /* set escape flag if \ */
207        e = 1;
208      else if (e == 0 && *p == '-')     /* set start of range if - */
209        c = *(p-1);
210      else
211      {
212        uch cc = (cs ? (uch)*s : case_map((uch)*s));
213        uch uc = (uch) c;
214        if (*(p+1) != '-')
215          for (uc = uc ? uc : (uch)*p; uc <= (uch)*p; uc++)
216            /* compare range */
217            if ((cs ? uc : case_map(uc)) == cc)
218              return r ? 0 : recmatch(q + CLEN(q), s + CLEN(s), cs);
219        c = e = 0;                      /* clear range, escape flags */
220      }
221    }
222    return r ? recmatch(q + CLEN(q), s + CLEN(s), cs) : 0;
223                                        /* bracket match failed */
224  }
225#endif /* !VMS */
226
227  /* If escape ('\'), just compare next character */
228  if (c == '\\')
229    if ((c = *p++) == '\0')             /* if \ at end, then syntax error */
230      return 0;
231
232#ifdef VMS
233  /* 2005-11-06 SMS.
234     Handle "..." wildcard in p with "." or "]" in s.
235  */
236  if ((c == '.') && (*p == '.') && (*(p+ CLEN( p)) == '.') &&
237   ((*s == '.') || (*s == ']')))
238  {
239    /* Match "...]" with "]".  Continue after "]" in both. */
240    if ((*(p+ 2* CLEN( p)) == ']') && (*s == ']'))
241      return recmatch( (p+ 3* CLEN( p)), (s+ CLEN( s)), cs);
242
243    /* Else, look for a reduced match in s, until "]" in or end of s. */
244    for (; *s && (*s != ']'); INCSTR(s))
245      if (*s == '.')
246        /* If reduced match, then continue after "..." in p, "." in s. */
247        if ((c = recmatch( (p+ CLEN( p)), s, cs)) != 0)
248          return (int)c;
249
250    /* Match "...]" with "]".  Continue after "]" in both. */
251    if ((*(p+ 2* CLEN( p)) == ']') && (*s == ']'))
252      return recmatch( (p+ 3* CLEN( p)), (s+ CLEN( s)), cs);
253
254    /* No reduced match.  Quit. */
255    return 2;
256  }
257
258#endif /* def VMS */
259
260  /* Just a character--compare it */
261  return (cs ? c == *s : case_map((uch)c) == case_map((uch)*s)) ?
262          recmatch(p, s + CLEN(s), cs) : 0;
263}
264
265
266int shmatch(p, s, cs)
267ZCONST char *p;         /* sh pattern to match */
268ZCONST char *s;         /* string to match it to */
269int cs;                 /* force case-sensitive match if TRUE */
270/* Compare the sh pattern p with the string s and return true if they match,
271   false if they don't or if there is a syntax error in the pattern. */
272{
273  return recmatch(p, s, cs) == 1;
274}
275
276
277#if defined(DOS) || defined(WIN32)
278/* XXX  also suitable for OS2?  Atari?  Human68K?  TOPS-20?? */
279
280int dosmatch(p, s, cs)
281ZCONST char *p;         /* dos pattern to match    */
282ZCONST char *s;         /* string to match it to   */
283int cs;                 /* force case-sensitive match if TRUE */
284/* Treat filenames without periods as having an implicit trailing period */
285{
286  char *s1;             /* revised string to match */
287  int r;                /* result */
288
289  if (strchr(p, '.') && !strchr(s, '.') &&
290      ((s1 = malloc(strlen(s) + 2)) != NULL))
291  {
292    strcpy(s1, s);
293    strcat(s1, ".");
294  }
295  else
296  {
297    /* will usually be OK */
298    s1 = (char *)s;
299  }
300
301  r = recmatch(p, s1, cs) == 1;
302  if (s != s1)
303    free((zvoid *)s1);
304  return r == 1;
305}
306
307#endif /* DOS || WIN32 */
308
309zvoid far **search(b, a, n, cmp)
310ZCONST zvoid *b;        /* pointer to value to search for */
311ZCONST zvoid far **a;   /* table of pointers to values, sorted */
312extent n;               /* number of pointers in a[] */
313int (*cmp) OF((ZCONST zvoid *, ZCONST zvoid far *)); /* comparison function */
314
315/* Search for b in the pointer list a[0..n-1] using the compare function
316   cmp(b, c) where c is an element of a[i] and cmp() returns negative if
317   *b < *c, zero if *b == *c, or positive if *b > *c.  If *b is found,
318   search returns a pointer to the entry in a[], else search() returns
319   NULL.  The nature and size of *b and *c (they can be different) are
320   left up to the cmp() function.  A binary search is used, and it is
321   assumed that the list is sorted in ascending order. */
322{
323  ZCONST zvoid far **i; /* pointer to midpoint of current range */
324  ZCONST zvoid far **l; /* pointer to lower end of current range */
325  int r;                /* result of (*cmp)() call */
326  ZCONST zvoid far **u; /* pointer to upper end of current range */
327
328  l = (ZCONST zvoid far **)a;  u = l + (n-1);
329  while (u >= l) {
330    i = l + ((unsigned)(u - l) >> 1);
331    if ((r = (*cmp)(b, (ZCONST char far *)*(struct zlist far **)i)) < 0)
332      u = i - 1;
333    else if (r > 0)
334      l = i + 1;
335    else
336      return (zvoid far **)i;
337  }
338  return NULL;          /* If b were in list, it would belong at l */
339}
340
341#endif /* !UTIL */
342
343#ifdef MSDOS16
344
345local unsigned ident(unsigned chr)
346{
347   return chr; /* in al */
348}
349
350void init_upper()
351{
352  static struct country {
353    uch ignore[18];
354    int (far *casemap)(int);
355    uch filler[16];
356  } country_info;
357
358  struct country far *info = &country_info;
359  union REGS regs;
360  struct SREGS sregs;
361  unsigned int c;
362
363  regs.x.ax = 0x3800; /* get country info */
364  regs.x.dx = FP_OFF(info);
365  sregs.ds  = FP_SEG(info);
366  intdosx(&regs, &regs, &sregs);
367  for (c = 0; c < 128; c++) {
368    upper[c] = (uch) toupper(c);
369    lower[c] = (uch) c;
370  }
371  for (; c < sizeof(upper); c++) {
372    upper[c] = (uch) (*country_info.casemap)(ident(c));
373    /* ident() required because casemap takes its parameter in al */
374    lower[c] = (uch) c;
375  }
376  for (c = 0; c < sizeof(upper); c++ ) {
377    int u = upper[c];
378    if (u != c && lower[u] == (uch) u) {
379      lower[u] = (uch)c;
380    }
381  }
382  for (c = 'A'; c <= 'Z'; c++) {
383    lower[c] = (uch) (c - 'A' + 'a');
384  }
385}
386#else /* !MSDOS16 */
387#  ifndef OS2
388
389void init_upper()
390{
391  unsigned int c;
392#if defined(ATARI) || defined(CMS_MVS)
393#include <ctype.h>
394/* this should be valid for all other platforms too.   (HD 11/11/95) */
395  for (c = 0; c< sizeof(upper); c++) {
396    upper[c] = islower(c) ? toupper(c) : c;
397    lower[c] = isupper(c) ? tolower(c) : c;
398  }
399#else
400  for (c = 0; c < sizeof(upper); c++) upper[c] = lower[c] = (uch)c;
401  for (c = 'a'; c <= 'z';        c++) upper[c] = (uch)(c - 'a' + 'A');
402  for (c = 'A'; c <= 'Z';        c++) lower[c] = (uch)(c - 'A' + 'a');
403#endif
404}
405#  endif /* !OS2 */
406
407#endif /* ?MSDOS16 */
408
409int namecmp(string1, string2)
410  ZCONST char *string1, *string2;
411/* Compare the two strings ignoring case, and correctly taking into
412 * account national language characters. For operating systems with
413 * case sensitive file names, this function is equivalent to strcmp.
414 */
415{
416  int d;
417
418  for (;;)
419  {
420    d = (int) (uch) case_map(*string1)
421      - (int) (uch) case_map(*string2);
422
423    if (d || *string1 == 0 || *string2 == 0)
424      return d;
425
426    string1++;
427    string2++;
428  }
429}
430
431#ifdef EBCDIC
432char *strtoasc(char *str1, ZCONST char *str2)
433{
434  char *old;
435  old = str1;
436  while (*str1++ = (char)ascii[(uch)(*str2++)]);
437  return old;
438}
439
440char *strtoebc(char *str1, ZCONST char *str2)
441{
442  char *old;
443  old = str1;
444  while (*str1++ = (char)ebcdic[(uch)(*str2++)]);
445  return old;
446}
447
448char *memtoasc(char *mem1, ZCONST char *mem2, unsigned len)
449{
450  char *old;
451  old = mem1;
452  while (len--)
453     *mem1++ = (char)ascii[(uch)(*mem2++)];
454  return old;
455}
456
457char *memtoebc(char *mem1, ZCONST char *mem2, unsigned len)
458{
459  char *old;
460  old = mem1;
461  while (len--)
462     *mem1++ = (char)ebcdic[(uch)(*mem2++)];
463  return old;
464}
465#endif /* EBCDIC */
466
467#ifdef IZ_ISO2OEM_ARRAY
468char *str_iso_to_oem(dst, src)
469  ZCONST char *src;
470  char *dst;
471{
472  char *dest_start = dst;
473  while (*dst++ = (char)iso2oem[(uch)(*src++)]);
474  return dest_start;
475}
476#endif
477
478#ifdef IZ_OEM2ISO_ARRAY
479char *str_oem_to_iso(dst, src)
480  ZCONST char *src;
481  char *dst;
482{
483  char *dest_start = dst;
484  while (*dst++ = (char)oem2iso[(uch)(*src++)]);
485  return dest_start;
486}
487#endif
488
489
490
491/* DBCS support for Info-ZIP's zip  (mainly for japanese (-: )
492 * by Yoshioka Tsuneo (QWF00133@nifty.ne.jp,tsuneo-y@is.aist-nara.ac.jp)
493 * This code is public domain!   Date: 1998/12/20
494 */
495#ifdef _MBCS
496
497char *___tmp_ptr;
498
499int lastchar(ptr)
500    ZCONST char *ptr;
501{
502    ZCONST char *oldptr = ptr;
503    while(*ptr != '\0'){
504        oldptr = ptr;
505        INCSTR(ptr);
506    }
507    return (int)(unsigned)*oldptr;
508}
509
510unsigned char *zmbschr(str, c)
511    ZCONST unsigned char *str;
512    unsigned int c;
513{
514    while(*str != '\0'){
515        if (*str == c) {return (char*)str;}
516        INCSTR(str);
517    }
518    return NULL;
519}
520
521unsigned char *zmbsrchr(str, c)
522    ZCONST unsigned char *str;
523    unsigned int c;
524{
525    unsigned char *match = NULL;
526    while(*str != '\0'){
527        if (*str == c) {match = (char*)str;}
528        INCSTR(str);
529    }
530    return match;
531}
532#endif /* _MBCS */
533
534
535
536#ifndef UTIL
537
538/*****************************************************************
539 | envargs - add default options from environment to command line
540 |----------------------------------------------------------------
541 | Author: Bill Davidsen, original 10/13/91, revised 23 Oct 1991.
542 | This program is in the public domain.
543 |----------------------------------------------------------------
544 | Minor program notes:
545 |  1. Yes, the indirection is a tad complex
546 |  2. Parenthesis were added where not needed in some cases
547 |     to make the action of the code less obscure.
548 ****************************************************************/
549
550void envargs(Pargc, Pargv, envstr, envstr2)
551    int *Pargc;
552    char ***Pargv;
553    char *envstr;
554    char *envstr2;
555{
556    char *envptr;                     /* value returned by getenv */
557    char *bufptr;                     /* copy of env info */
558    int argc;                         /* internal arg count */
559    register int ch;                  /* spare temp value */
560    char **argv;                      /* internal arg vector */
561    char **argvect;                   /* copy of vector address */
562
563    /* see if anything in the environment */
564    envptr = getenv(envstr);
565    if (envptr != NULL)                                /* usual var */
566        while (isspace((uch)*envptr))      /* we must discard leading spaces */
567            envptr++;
568    if (envptr == NULL || *envptr == '\0')
569        if ((envptr = getenv(envstr2)) != NULL)                 /* alternate */
570            while (isspace((uch)*envptr))
571                envptr++;
572    if (envptr == NULL || *envptr == '\0')
573        return;
574
575    /* count the args so we can allocate room for them */
576    argc = count_args(envptr);
577    bufptr = malloc(1 + strlen(envptr));
578    if (bufptr == NULL)
579        ziperr(ZE_MEM, "Can't get memory for arguments");
580    strcpy(bufptr, envptr);
581
582    /* allocate a vector large enough for all args */
583    argv = (char **)malloc((argc + *Pargc + 1) * sizeof(char *));
584    if (argv == NULL) {
585        free(bufptr);
586        ziperr(ZE_MEM, "Can't get memory for arguments");
587    }
588    argvect = argv;
589
590    /* copy the program name first, that's always true */
591    *(argv++) = *((*Pargv)++);
592
593    /* copy the environment args first, may be changed */
594    do {
595#if defined(AMIGA) || defined(UNIX)
596        if (*bufptr == '"') {
597            char *argstart = ++bufptr;
598            *(argv++) = argstart;
599            for (ch = *bufptr; ch != '\0' && ch != '\"';
600                 ch = *PREINCSTR(bufptr))
601                if (ch == '\\' && bufptr[1] != '\0')
602                    ++bufptr;               /* skip to char after backslash */
603            if (ch != '\0')                       /* overwrite trailing '"' */
604                *(bufptr++) = '\0';
605
606            /* remove escape characters */
607            while ((argstart = MBSCHR(argstart, '\\')) != NULL) {
608                strcpy(argstart, argstart + 1);
609                if (*argstart)
610                    ++argstart;
611            }
612        } else {
613            *(argv++) = bufptr;
614            while ((ch = *bufptr) != '\0' && !isspace((uch)ch)) INCSTR(bufptr);
615            if (ch != '\0') *(bufptr++) = '\0';
616        }
617#else
618#  ifdef WIN32
619        /* We do not support backslash-quoting of quotes in quoted */
620        /* strings under Win32, because backslashes are directory  */
621        /* separators and double quotes are illegal in filenames.  */
622        if (*bufptr == '"') {
623            *(argv++) = ++bufptr;
624            while ((ch = *bufptr) != '\0' && ch != '\"') INCSTR(bufptr);
625            if (ch != '\0') *(bufptr++) = '\0';
626        } else {
627            *(argv++) = bufptr;
628            while ((ch = *bufptr) != '\0' && !isspace((uch)ch)) INCSTR(bufptr);
629            if (ch != '\0') *(bufptr++) = '\0';
630        }
631#  else
632        *(argv++) = bufptr;
633        while ((ch = *bufptr) != '\0' && !isspace((uch)ch)) INCSTR(bufptr);
634        if (ch != '\0') *(bufptr++) = '\0';
635#  endif
636#endif /* ?(AMIGA || UNIX) */
637        while ((ch = *bufptr) != '\0' && isspace((uch)ch)) INCSTR(bufptr);
638    } while (ch);
639
640    /* now save old argc and copy in the old args */
641    argc += *Pargc;
642    while (--(*Pargc)) *(argv++) = *((*Pargv)++);
643
644    /* finally, add a NULL after the last arg, like UNIX */
645    *argv = NULL;
646
647    /* save the values and return */
648    *Pargv = argvect;
649    *Pargc = argc;
650}
651
652static int count_args(s)
653char *s;
654{
655    int count = 0;
656    char ch;
657
658    do {
659        /* count and skip args */
660        ++count;
661#if defined(AMIGA) || defined(UNIX)
662        if (*s == '\"') {
663            for (ch = *PREINCSTR(s); ch != '\0' && ch != '\"';
664                 ch = *PREINCSTR(s))
665                if (ch == '\\' && s[1] != '\0')
666                    INCSTR(s);
667            if (*s) INCSTR(s);  /* trailing quote */
668        } else
669            while ((ch = *s) != '\0' && !isspace((uch)ch)) INCSTR(s);
670#else
671#  ifdef WIN32
672        if (*s == '\"') {
673            ++s;                /* leading quote */
674            while ((ch = *s) != '\0' && ch != '\"') INCSTR(s);
675            if (*s) INCSTR(s);  /* trailing quote */
676        } else
677            while ((ch = *s) != '\0' && !isspace((uch)ch)) INCSTR(s);
678#  else
679        while ((ch = *s) != '\0' && !isspace((uch)ch)) INCSTR(s);
680#  endif
681#endif /* ?(AMIGA || UNIX) */
682        while ((ch = *s) != '\0' && isspace((uch)ch)) INCSTR(s);
683    } while (ch);
684
685    return(count);
686}
687
688
689
690/* Extended argument processing -- by Rich Wales
691 * This function currently deals only with the MKS shell, but could be
692 * extended later to understand other conventions.
693 *
694 * void expand_args(int *argcp, char ***argvp)
695 *
696 *    Substitutes the extended command line argument list produced by
697 *    the MKS Korn Shell in place of the command line info from DOS.
698 *
699 *    The MKS shell gets around DOS's 128-byte limit on the length of
700 *    a command line by passing the "real" command line in the envi-
701 *    ronment.  The "real" arguments are flagged by prepending a tilde
702 *    (~) to each one.
703 *
704 *    This "expand_args" routine creates a new argument list by scanning
705 *    the environment from the beginning, looking for strings begin-
706 *    ning with a tilde character.  The new list replaces the original
707 *    "argv" (pointed to by "argvp"), and the number of arguments
708 *    in the new list replaces the original "argc" (pointed to by
709 *    "argcp").
710 */
711void expand_args(argcp, argvp)
712      int *argcp;
713      char ***argvp;
714{
715#ifdef DOS
716
717/* Do NEVER include (re)definiton of `environ' variable with any version
718   of MSC or BORLAND/Turbo C. These compilers supply an incompatible
719   definition in <stdlib.h>.  */
720#if defined(__GO32__) || defined(__EMX__)
721      extern char **environ;          /* environment */
722#endif /* __GO32__ || __EMX__ */
723      char        **envp;             /* pointer into environment */
724      char        **newargv;          /* new argument list */
725      char        **argp;             /* pointer into new arg list */
726      int           newargc;          /* new argument count */
727
728      /* sanity check */
729      if (environ == NULL
730          || argcp == NULL
731          || argvp == NULL || *argvp == NULL)
732              return;
733      /* find out how many environment arguments there are */
734      for (envp = environ, newargc = 0;
735           *envp != NULL && (*envp)[0] == '~';
736           envp++, newargc++) ;
737      if (newargc == 0)
738              return;                 /* no environment arguments */
739      /* set up new argument list */
740      newargv = (char **) malloc(sizeof(char **) * (newargc+1));
741      if (newargv == NULL)
742              return;                 /* malloc failed */
743      for (argp = newargv, envp = environ;
744           *envp != NULL && (*envp)[0] == '~';
745           *argp++ = &(*envp++)[1]) ;
746      *argp = NULL;                   /* null-terminate the list */
747      /* substitute new argument list in place of old one */
748      *argcp = newargc;
749      *argvp = newargv;
750#else /* !DOS */
751      if (argcp || argvp) return;
752#endif /* ?DOS */
753}
754
755#endif /* UTIL */
756
757#ifdef DEBUGNAMES
758#undef free
759int Free(x)
760void *x;
761{
762    if (x == (void *) 0xdeadbeef)
763        exit(-1);
764    free(x);
765    return 0;
766}
767
768int printnames()
769{
770     struct zlist far *z;
771
772     for (z = zfiles; z != NULL; z = z->nxt)
773           fprintf(stderr, "%s %s %s %p %p %p %08x %08x %08x\n",
774                            z->name, z->zname, z->iname,
775                            z->name, z->zname, z->iname,
776                            *((int *) z->name), *((int *) z->zname),
777                            *((int *) z->iname));
778     return 0;
779}
780
781#endif /* DEBUGNAMES */
782