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