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