Imported from ../bash-2.04.tar.gz.
[platform/upstream/bash.git] / general.c
1 /* general.c -- Stuff that is used by all files. */
2
3 /* Copyright (C) 1987-1999 Free Software Foundation, Inc.
4
5    This file is part of GNU Bash, the Bourne Again SHell.
6
7    Bash is free software; you can redistribute it and/or modify it under
8    the terms of the GNU General Public License as published by the Free
9    Software Foundation; either version 2, or (at your option) any later
10    version.
11
12    Bash is distributed in the hope that it will be useful, but WITHOUT ANY
13    WARRANTY; without even the implied warranty of MERCHANTABILITY or
14    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15    for more details.
16
17    You should have received a copy of the GNU General Public License along
18    with Bash; see the file COPYING.  If not, write to the Free Software
19    Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
20
21 #include "config.h"
22
23 #include "bashtypes.h"
24 #ifndef _MINIX
25 #  include <sys/param.h>
26 #endif
27 #include "posixstat.h"
28
29 #if defined (HAVE_UNISTD_H)
30 #  include <unistd.h>
31 #endif
32
33 #include "filecntl.h"
34 #include "bashansi.h"
35 #include <stdio.h>
36 #include <ctype.h>
37 #include <errno.h>
38
39 #include "shell.h"
40 #include <tilde/tilde.h>
41
42 #include "maxpath.h"
43
44 #if !defined (errno)
45 extern int errno;
46 #endif /* !errno */
47
48 #ifndef to_upper
49 #  define to_upper(c) (islower(c) ? toupper(c) : (c))
50 #  define to_lower(c) (isupper(c) ? tolower(c) : (c))
51 #endif
52
53 extern int interrupt_immediately;
54 extern int interactive_comments;
55
56 /* A standard error message to use when getcwd() returns NULL. */
57 char *bash_getcwd_errstr = "getcwd: cannot access parent directories";
58
59 /* Do whatever is necessary to initialize `Posix mode'. */
60 void
61 posix_initialize (on)
62      int on;
63 {
64   interactive_comments = on != 0;
65 }
66
67 /* **************************************************************** */
68 /*                                                                  */
69 /*  Functions to convert to and from and display non-standard types */
70 /*                                                                  */
71 /* **************************************************************** */
72
73 #if defined (RLIMTYPE)
74 RLIMTYPE
75 string_to_rlimtype (s)
76      char *s;
77 {
78   RLIMTYPE ret;
79   int neg;
80
81   ret = 0;
82   neg = 0;
83   while (s && *s && whitespace (*s))
84     s++;
85   if (*s == '-' || *s == '+')
86     {
87       neg = *s == '-';
88       s++;
89     }
90   for ( ; s && *s && digit (*s); s++)
91     ret = (ret * 10) + digit_value (*s);
92   return (neg ? -ret : ret);
93 }
94
95 void
96 print_rlimtype (n, addnl)
97      RLIMTYPE n;
98      int addnl;
99 {
100   char s[sizeof (RLIMTYPE) * 3 + 1];
101   int len;
102
103   if (n == 0)
104     {
105       printf ("0%s", addnl ? "\n" : "");
106       return;
107     }
108
109   if (n < 0)
110     {
111       putchar ('-');
112       n = -n;
113     }
114
115   len = sizeof (RLIMTYPE) * 3 + 1;
116   s[--len] = '\0';
117   for ( ; n != 0; n /= 10)
118     s[--len] = n % 10 + '0';
119   printf ("%s%s", s + len, addnl ? "\n" : "");
120 }
121 #endif /* RLIMTYPE */
122
123 /* **************************************************************** */
124 /*                                                                  */
125 /*                     Input Validation Functions                   */
126 /*                                                                  */
127 /* **************************************************************** */
128
129 /* Return non-zero if all of the characters in STRING are digits. */
130 int
131 all_digits (string)
132      char *string;
133 {
134   while (*string)
135     {
136       if (!digit (*string))
137         return (0);
138       else
139         string++;
140     }
141   return (1);
142 }
143
144 /* Return non-zero if the characters pointed to by STRING constitute a
145    valid number.  Stuff the converted number into RESULT if RESULT is
146    a non-null pointer to a long. */
147 int
148 legal_number (string, result)
149      char *string;
150      long *result;
151 {
152   long value;
153   char *ep;
154
155   if (result)
156     *result = 0;
157
158   value = strtol (string, &ep, 10);
159
160   /* If *string is not '\0' but *ep is '\0' on return, the entire string
161      is valid. */
162   if (string && *string && *ep == '\0')
163     {
164       if (result)
165         *result = value;
166       /* The SunOS4 implementation of strtol() will happily ignore
167          overflow conditions, so this cannot do overflow correctly
168          on those systems. */
169       return 1;
170     }
171     
172   return (0);
173 }
174
175 /* Return 1 if this token is a legal shell `identifier'; that is, it consists
176    solely of letters, digits, and underscores, and does not begin with a
177    digit. */
178 int
179 legal_identifier (name)
180      char *name;
181 {
182   register char *s;
183
184   if (!name || !*name || (legal_variable_starter (*name) == 0))
185     return (0);
186
187   for (s = name + 1; *s; s++)
188     {
189       if (legal_variable_char (*s) == 0)
190         return (0);
191     }
192   return (1);
193 }
194
195 /* Make sure that WORD is a valid shell identifier, i.e.
196    does not contain a dollar sign, nor is quoted in any way.  Nor
197    does it consist of all digits.  If CHECK_WORD is non-zero,
198    the word is checked to ensure that it consists of only letters,
199    digits, and underscores. */
200 int
201 check_identifier (word, check_word)
202      WORD_DESC *word;
203      int check_word;
204 {
205   if ((word->flags & (W_HASDOLLAR|W_QUOTED)) || all_digits (word->word))
206     {
207       internal_error ("`%s': not a valid identifier", word->word);
208       return (0);
209     }
210   else if (check_word && legal_identifier (word->word) == 0)
211     {
212       internal_error ("`%s': not a valid identifier", word->word);
213       return (0);
214     }
215   else
216     return (1);
217 }
218
219 /* **************************************************************** */
220 /*                                                                  */
221 /*           Functions to manage files and file descriptors         */
222 /*                                                                  */
223 /* **************************************************************** */
224
225 /* A function to unset no-delay mode on a file descriptor.  Used in shell.c
226    to unset it on the fd passed as stdin.  Should be called on stdin if
227    readline gets an EAGAIN or EWOULDBLOCK when trying to read input. */
228
229 #if !defined (O_NDELAY)
230 #  if defined (FNDELAY)
231 #    define O_NDELAY FNDELAY
232 #  endif
233 #endif /* O_NDELAY */
234
235 /* Make sure no-delay mode is not set on file descriptor FD. */
236 int
237 unset_nodelay_mode (fd)
238      int fd;
239 {
240   int flags, bflags;
241
242   if ((flags = fcntl (fd, F_GETFL, 0)) < 0)
243     return -1;
244
245   bflags = 0;
246
247   /* This is defined to O_NDELAY in filecntl.h if O_NONBLOCK is not present
248      and O_NDELAY is defined. */
249 #ifdef O_NONBLOCK
250   bflags |= O_NONBLOCK;
251 #endif
252
253 #ifdef O_NDELAY
254   bflags |= O_NDELAY;
255 #endif
256
257   if (flags & bflags)
258     {
259       flags &= ~bflags;
260       return (fcntl (fd, F_SETFL, flags));
261     }
262
263   return 0;
264 }
265
266 /* There is a bug in the NeXT 2.1 rlogind that causes opens
267    of /dev/tty to fail. */
268
269 #if defined (__BEOS__)
270 /* On BeOS, opening in non-blocking mode exposes a bug in BeOS, so turn it
271    into a no-op.  This should probably go away in the future. */
272 #  undef O_NONBLOCK
273 #  define O_NONBLOCK 0
274 #endif /* __BEOS__ */
275
276 void
277 check_dev_tty ()
278 {
279   int tty_fd;
280   char *tty;
281
282   tty_fd = open ("/dev/tty", O_RDWR|O_NONBLOCK);
283
284   if (tty_fd < 0)
285     {
286       tty = (char *)ttyname (fileno (stdin));
287       if (tty == 0)
288         return;
289       tty_fd = open (tty, O_RDWR|O_NONBLOCK);
290     }
291   close (tty_fd);
292 }
293
294 /* Return 1 if PATH1 and PATH2 are the same file.  This is kind of
295    expensive.  If non-NULL STP1 and STP2 point to stat structures
296    corresponding to PATH1 and PATH2, respectively. */
297 int
298 same_file (path1, path2, stp1, stp2)
299      char *path1, *path2;
300      struct stat *stp1, *stp2;
301 {
302   struct stat st1, st2;
303
304   if (stp1 == NULL)
305     {
306       if (stat (path1, &st1) != 0)
307         return (0);
308       stp1 = &st1;
309     }
310
311   if (stp2 == NULL)
312     {
313       if (stat (path2, &st2) != 0)
314         return (0);
315       stp2 = &st2;
316     }
317
318   return ((stp1->st_dev == stp2->st_dev) && (stp1->st_ino == stp2->st_ino));
319 }
320
321 /* Move FD to a number close to the maximum number of file descriptors
322    allowed in the shell process, to avoid the user stepping on it with
323    redirection and causing us extra work.  If CHECK_NEW is non-zero,
324    we check whether or not the file descriptors are in use before
325    duplicating FD onto them.  MAXFD says where to start checking the
326    file descriptors.  If it's less than 20, we get the maximum value
327    available from getdtablesize(2). */
328 int
329 move_to_high_fd (fd, check_new, maxfd)
330      int fd, check_new, maxfd;
331 {
332   int script_fd, nfds, ignore;
333
334   if (maxfd < 20)
335     {
336       nfds = getdtablesize ();
337       if (nfds <= 0)
338         nfds = 20;
339       if (nfds > 256)
340         nfds = 256;
341     }
342   else
343     nfds = maxfd;
344
345   for (nfds--; check_new && nfds > 3; nfds--)
346     if (fcntl (nfds, F_GETFD, &ignore) == -1)
347       break;
348
349   if (nfds && fd != nfds && (script_fd = dup2 (fd, nfds)) != -1)
350     {
351       if (check_new == 0 || fd != fileno (stderr))      /* don't close stderr */
352         close (fd);
353       return (script_fd);
354     }
355
356   return (fd);
357 }
358  
359 /* Return non-zero if the characters from SAMPLE are not all valid
360    characters to be found in the first line of a shell script.  We
361    check up to the first newline, or SAMPLE_LEN, whichever comes first.
362    All of the characters must be printable or whitespace. */
363
364 #if !defined (isspace)
365 #define isspace(c) ((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\f')
366 #endif
367
368 #if !defined (isprint)
369 #define isprint(c) (isletter(c) || digit(c) || ispunct(c))
370 #endif
371
372 int
373 check_binary_file (sample, sample_len)
374      unsigned char *sample;
375      int sample_len;
376 {
377   register int i;
378
379   for (i = 0; i < sample_len; i++)
380     {
381       if (sample[i] == '\n')
382         return (0);
383
384       if (isspace (sample[i]) == 0 && isprint (sample[i]) == 0)
385         return (1);
386     }
387
388   return (0);
389 }
390
391 /* **************************************************************** */
392 /*                                                                  */
393 /*                  Functions to manipulate pathnames               */
394 /*                                                                  */
395 /* **************************************************************** */
396
397 /* Return 1 if PATH corresponds to a directory. */
398 static int
399 canon_stat (path)
400      char *path;
401 {
402   int l;
403   char *s;
404   struct stat sb;
405
406   l = strlen (path);
407   s = xmalloc (l + 3);
408   strcpy (s, path);
409   s[l] = '/';
410   s[l+1] = '.';
411   s[l+2] = '\0';
412   l = stat (s, &sb) == 0 && S_ISDIR (sb.st_mode);
413   free (s);
414   return l;
415 }
416
417 /* Canonicalize PATH, and return a new path.  The new path differs from PATH
418    in that:
419         Multple `/'s are collapsed to a single `/'.
420         Leading `./'s and trailing `/.'s are removed.
421         Trailing `/'s are removed.
422         Non-leading `../'s and trailing `..'s are handled by removing
423         portions of the path. */
424 char *
425 canonicalize_pathname (path)
426      char *path;
427 {
428   register int i, start;
429   char stub_char;
430   char *result;
431
432   /* The result cannot be larger than the input PATH. */
433   result = savestring (path);
434
435   stub_char = (*path == '/') ? '/' : '.';
436
437   /* Walk along RESULT looking for things to compact. */
438   i = 0;
439   while (1)
440     {
441       if (!result[i])
442         break;
443
444       while (result[i] && result[i] != '/')
445         i++;
446
447       start = i++;
448
449       /* If we didn't find any slashes, then there is nothing left to do. */
450       if (!result[start])
451         break;
452
453       /* Handle multiple `/'s in a row. */
454       while (result[i] == '/')
455         i++;
456
457 #if 0
458       if ((start + 1) != i)
459 #else
460       /* Leave a leading `//' alone, as POSIX requires. */
461       if ((start + 1) != i && (start != 0 || i != 2))
462 #endif
463         {
464           strcpy (result + start + 1, result + i);
465           i = start + 1;
466           /* Make sure that what we have so far corresponds to a directory.
467              If it does not, just punt. */
468           if (*result)
469             {
470               char c;
471               c = result[start];
472               result[start] = '\0';
473               if (canon_stat (result) == 0)
474                 {
475                   free (result);
476                   return ((char *)NULL);
477                 }
478               result[start] = c;
479             }
480         }
481 #if 0
482       /* Handle backslash-quoted `/'. */
483       if (start > 0 && result[start - 1] == '\\')
484         continue;
485 #endif
486
487       /* Check for trailing `/'. */
488       if (start && !result[i])
489         {
490         zero_last:
491           result[--i] = '\0';
492           break;
493         }
494
495       /* Check for `../', `./' or trailing `.' by itself. */
496       if (result[i] == '.')
497         {
498           /* Handle trailing `.' by itself. */
499           if (!result[i + 1])
500             goto zero_last;
501
502           /* Handle `./'. */
503           if (result[i + 1] == '/')
504             {
505               strcpy (result + i, result + i + 1);
506               i = (start < 0) ? 0 : start;
507               continue;
508             }
509
510           /* Handle `../' or trailing `..' by itself. */
511           if (result[i + 1] == '.' &&
512               (result[i + 2] == '/' || !result[i + 2]))
513             {
514               /* Make sure that the last component corresponds to a directory
515                  before blindly chopping it off. */
516               if (i)
517                 {
518                   result[i] = '\0';
519                   if (canon_stat (result) == 0)
520                     {
521                       free (result);
522                       return ((char *)NULL);
523                     }
524                   result[i] = '.';
525                 }
526               while (--start > -1 && result[start] != '/');
527               strcpy (result + start + 1, result + i + 2);
528 #if 0   /* Unnecessary */
529               if (*result && canon_stat (result) == 0)
530                 {
531                   free (result);
532                   return ((char *)NULL);
533                 }
534 #endif
535               i = (start < 0) ? 0 : start;
536               continue;
537             }
538         }
539     }
540
541   if (!*result)
542     {
543       *result = stub_char;
544       result[1] = '\0';
545     }
546
547   /* If the result starts with `//', but the original path does not, we
548      can turn the // into /. */
549   if ((result[0] == '/' && result[1] == '/' && result[2] != '/') &&
550       (path[0] != '/' || path[1] != '/' || path[2] == '/'))
551     {
552       char *r2;
553       if (result[2] == '\0')    /* short-circuit for bare `//' */
554         result[1] = '\0';
555       else
556         {
557           r2 = savestring (result + 1);
558           free (result);
559           result = r2;
560         }
561     }
562
563   return (result);
564 }
565
566 /* Turn STRING (a pathname) into an absolute pathname, assuming that
567    DOT_PATH contains the symbolic location of `.'.  This always
568    returns a new string, even if STRING was an absolute pathname to
569    begin with. */
570 char *
571 make_absolute (string, dot_path)
572      char *string, *dot_path;
573 {
574   char *result;
575
576   if (dot_path == 0 || *string == '/')
577     result = savestring (string);
578   else
579     result = sh_makepath (dot_path, string, 0);
580
581   return (result);
582 }
583
584 /* Return 1 if STRING contains an absolute pathname, else 0. */
585 int
586 absolute_pathname (string)
587      char *string;
588 {
589   if (!string || !*string)
590     return (0);
591
592   if (*string == '/')
593     return (1);
594
595   if (*string++ == '.')
596     {
597       if (!*string || *string == '/' ||
598            (*string == '.' && (string[1] == '\0' || string[1] == '/')))
599         return (1);
600     }
601   return (0);
602 }
603
604 /* Return 1 if STRING is an absolute program name; it is absolute if it
605    contains any slashes.  This is used to decide whether or not to look
606    up through $PATH. */
607 int
608 absolute_program (string)
609      char *string;
610 {
611   return ((char *)strchr (string, '/') != (char *)NULL);
612 }
613
614 /* Return the `basename' of the pathname in STRING (the stuff after the
615    last '/').  If STRING is not a full pathname, simply return it. */
616 char *
617 base_pathname (string)
618      char *string;
619 {
620   char *p;
621
622   if (!absolute_pathname (string))
623     return (string);
624
625   p = (char *)strrchr (string, '/');
626   return (p ? ++p : string);
627 }
628
629 /* Return the full pathname of FILE.  Easy.  Filenames that begin
630    with a '/' are returned as themselves.  Other filenames have
631    the current working directory prepended.  A new string is
632    returned in either case. */
633 char *
634 full_pathname (file)
635      char *file;
636 {
637   char *ret;
638
639   file = (*file == '~') ? bash_tilde_expand (file) : savestring (file);
640
641   if ((*file == '/') && absolute_pathname (file))
642     return (file);
643
644   ret = sh_makepath ((char *)NULL, file, (MP_DOCWD|MP_RMDOT));
645   free (file);
646
647   return (ret);
648 }
649
650 /* A slightly related function.  Get the prettiest name of this
651    directory possible. */
652 static char tdir[PATH_MAX];
653
654 /* Return a pretty pathname.  If the first part of the pathname is
655    the same as $HOME, then replace that with `~'.  */
656 char *
657 polite_directory_format (name)
658      char *name;
659 {
660   char *home;
661   int l;
662
663   home = get_string_value ("HOME");
664   l = home ? strlen (home) : 0;
665   if (l > 1 && strncmp (home, name, l) == 0 && (!name[l] || name[l] == '/'))
666     {
667       strncpy (tdir + 1, name + l, sizeof(tdir) - 2);
668       tdir[0] = '~';
669       tdir[sizeof(tdir) - 1] = '\0';
670       return (tdir);
671     }
672   else
673     return (name);
674 }
675
676 /* Given a string containing units of information separated by colons,
677    return the next one pointed to by (P_INDEX), or NULL if there are no more.
678    Advance (P_INDEX) to the character after the colon. */
679 char *
680 extract_colon_unit (string, p_index)
681      char *string;
682      int *p_index;
683 {
684   int i, start, len;
685   char *value;
686
687   if (string == 0)
688     return (string);
689
690   len = strlen (string);
691   if (*p_index >= len)
692     return ((char *)NULL);
693
694   i = *p_index;
695
696   /* Each call to this routine leaves the index pointing at a colon if
697      there is more to the path.  If I is > 0, then increment past the
698      `:'.  If I is 0, then the path has a leading colon.  Trailing colons
699      are handled OK by the `else' part of the if statement; an empty
700      string is returned in that case. */
701   if (i && string[i] == ':')
702     i++;
703
704   for (start = i; string[i] && string[i] != ':'; i++)
705     ;
706
707   *p_index = i;
708
709   if (i == start)
710     {
711       if (string[i])
712         (*p_index)++;
713       /* Return "" in the case of a trailing `:'. */
714       value = xmalloc (1);
715       value[0] = '\0';
716     }
717   else
718     value = substring (string, start, i);
719
720   return (value);
721 }
722
723 /* **************************************************************** */
724 /*                                                                  */
725 /*                  Tilde Initialization and Expansion              */
726 /*                                                                  */
727 /* **************************************************************** */
728
729 #if defined (PUSHD_AND_POPD)
730 extern char *get_dirstack_from_string __P((char *));
731 #endif
732
733 /* If tilde_expand hasn't been able to expand the text, perhaps it
734    is a special shell expansion.  This function is installed as the
735    tilde_expansion_preexpansion_hook.  It knows how to expand ~- and ~+.
736    If PUSHD_AND_POPD is defined, ~[+-]N expands to directories from the
737    directory stack. */
738 static char *
739 bash_special_tilde_expansions (text)
740      char *text;
741 {
742   char *result;
743
744   result = (char *)NULL;
745
746   if (text[0] == '+' && text[1] == '\0')
747     result = get_string_value ("PWD");
748   else if (text[0] == '-' && text[1] == '\0')
749     result = get_string_value ("OLDPWD");
750 #if defined (PUSHD_AND_POPD)
751   else if (isdigit (*text) || ((*text == '+' || *text == '-') && isdigit (text[1])))
752     result = get_dirstack_from_string (text);
753 #endif
754
755   return (result ? savestring (result) : (char *)NULL);
756 }
757
758 /* Initialize the tilde expander.  In Bash, we handle `~-' and `~+', as
759    well as handling special tilde prefixes; `:~" and `=~' are indications
760    that we should do tilde expansion. */
761 void
762 tilde_initialize ()
763 {
764   static int times_called = 0;
765
766   /* Tell the tilde expander that we want a crack first. */
767   tilde_expansion_preexpansion_hook = (CPFunction *)bash_special_tilde_expansions;
768
769   /* Tell the tilde expander about special strings which start a tilde
770      expansion, and the special strings that end one.  Only do this once.
771      tilde_initialize () is called from within bashline_reinitialize (). */
772   if (times_called++ == 0)
773     {
774       tilde_additional_prefixes = alloc_array (3);
775       tilde_additional_prefixes[0] = "=~";
776       tilde_additional_prefixes[1] = ":~";
777       tilde_additional_prefixes[2] = (char *)NULL;
778
779       tilde_additional_suffixes = alloc_array (3);
780       tilde_additional_suffixes[0] = ":";
781       tilde_additional_suffixes[1] = "=~";
782       tilde_additional_suffixes[2] = (char *)NULL;
783     }
784 }
785
786 char *
787 bash_tilde_expand (s)
788      char *s;
789 {
790   int old_immed;
791   char *ret;
792
793   old_immed = interrupt_immediately;
794   interrupt_immediately = 1;
795   ret = tilde_expand (s);
796   interrupt_immediately = old_immed;
797   return (ret);
798 }
799
800 /* **************************************************************** */
801 /*                                                                  */
802 /*        Functions to manipulate and search the group list         */
803 /*                                                                  */
804 /* **************************************************************** */
805
806 static int ngroups, maxgroups;
807
808 /* The set of groups that this user is a member of. */
809 static GETGROUPS_T *group_array = (GETGROUPS_T *)NULL;
810
811 #if !defined (NOGROUP)
812 #  define NOGROUP (gid_t) -1
813 #endif
814
815 #if defined (HAVE_SYSCONF) && defined (_SC_NGROUPS_MAX)
816 #  define getmaxgroups() sysconf(_SC_NGROUPS_MAX)
817 #else
818 #  if defined (NGROUPS_MAX)
819 #    define getmaxgroups() NGROUPS_MAX
820 #  else /* !NGROUPS_MAX */
821 #    if defined (NGROUPS)
822 #      define getmaxgroups() NGROUPS
823 #    else /* !NGROUPS */
824 #      define getmaxgroups() 64
825 #    endif /* !NGROUPS */
826 #  endif /* !NGROUPS_MAX */
827 #endif /* !HAVE_SYSCONF || !SC_NGROUPS_MAX */
828
829 static void
830 initialize_group_array ()
831 {
832   register int i;
833
834   if (maxgroups == 0)
835     maxgroups = getmaxgroups ();
836
837   ngroups = 0;
838   group_array = (GETGROUPS_T *)xrealloc (group_array, maxgroups * sizeof (GETGROUPS_T));
839
840 #if defined (HAVE_GETGROUPS)
841   ngroups = getgroups (maxgroups, group_array);
842 #endif
843
844   /* If getgroups returns nothing, or the OS does not support getgroups(),
845      make sure the groups array includes at least the current gid. */
846   if (ngroups == 0)
847     {
848       group_array[0] = current_user.gid;
849       ngroups = 1;
850     }
851
852   /* If the primary group is not in the groups array, add it as group_array[0]
853      and shuffle everything else up 1, if there's room. */
854   for (i = 0; i < ngroups; i++)
855     if (current_user.gid == (gid_t)group_array[i])
856       break;
857   if (i == ngroups && ngroups < maxgroups)
858     {
859       for (i = ngroups; i > 0; i--)
860         group_array[i] = group_array[i - 1];
861       group_array[0] = current_user.gid;
862       ngroups++;
863     }
864
865   /* If the primary group is not group_array[0], swap group_array[0] and
866      whatever the current group is.  The vast majority of systems should
867      not need this; a notable exception is Linux. */
868   if (group_array[0] != current_user.gid)
869     {
870       for (i = 0; i < ngroups; i++)
871         if (group_array[i] == current_user.gid)
872           break;
873       if (i < ngroups)
874         {
875           group_array[i] = group_array[0];
876           group_array[0] = current_user.gid;
877         }
878     }
879 }
880
881 /* Return non-zero if GID is one that we have in our groups list. */
882 int
883 #if defined (__STDC__) || defined ( _MINIX)
884 group_member (gid_t gid)
885 #else
886 group_member (gid)
887      gid_t gid;
888 #endif /* !__STDC__ && !_MINIX */
889 {
890 #if defined (HAVE_GETGROUPS)
891   register int i;
892 #endif
893
894   /* Short-circuit if possible, maybe saving a call to getgroups(). */
895   if (gid == current_user.gid || gid == current_user.egid)
896     return (1);
897
898 #if defined (HAVE_GETGROUPS)
899   if (ngroups == 0)
900     initialize_group_array ();
901
902   /* In case of error, the user loses. */
903   if (ngroups <= 0)
904     return (0);
905
906   /* Search through the list looking for GID. */
907   for (i = 0; i < ngroups; i++)
908     if (gid == (gid_t)group_array[i])
909       return (1);
910 #endif
911
912   return (0);
913 }
914
915 char **
916 get_group_list (ngp)
917      int *ngp;
918 {
919   static char **group_vector = (char **)NULL;
920   register int i;
921   char *nbuf;
922
923   if (group_vector)
924     {
925       if (ngp)
926         *ngp = ngroups;
927       return group_vector;
928     }
929
930   if (ngroups == 0)
931     initialize_group_array ();
932
933   if (ngroups <= 0)
934     {
935       if (ngp)
936         *ngp = 0;
937       return (char **)NULL;
938     }
939
940   group_vector = alloc_array (ngroups);
941   for (i = 0; i < ngroups; i++)
942     {
943       nbuf = itos ((int)group_array[i]);
944       group_vector[i] = nbuf;
945     }
946
947   if (ngp)
948     *ngp = ngroups;
949   return group_vector;
950 }
951
952 int *
953 get_group_array (ngp)
954      int *ngp;
955 {
956   int i;
957   static int *group_iarray = (int *)NULL;
958
959   if (group_iarray)
960     {
961       if (ngp)
962         *ngp = ngroups;
963       return (group_iarray);
964     }
965
966   if (ngroups == 0)
967     initialize_group_array ();    
968
969   if (ngroups <= 0)
970     {
971       if (ngp)
972         *ngp = 0;
973       return (int *)NULL;
974     }
975
976   group_iarray = (int *)xmalloc (ngroups * sizeof (int));
977   for (i = 0; i < ngroups; i++)
978     group_iarray[i] = (int)group_array[i];
979
980   if (ngp)
981     *ngp = ngroups;
982   return group_iarray;
983 }