]> git.ipfire.org Git - thirdparty/bash.git/blob - general.c
Imported from ../bash-2.04.tar.gz.
[thirdparty/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 }