fixes for glibc time/gettimeofday issue; fix issue with history file containing one line too few if saving timestamps; fix for signal arriving while displaying readline completions

This commit is contained in:
Chet Ramey
2023-03-27 09:28:12 -04:00
parent f539a75606
commit 64b2b7c08d
25 changed files with 344 additions and 125 deletions
+71
View File
@@ -5748,3 +5748,74 @@ pcomplete.c
- gen_globpat_matches: set noglob_dot_filenames here since we're not
calling shell_glob_filename to do it
From a report by Grisha Levit <grishalevit@gmail.com>
3/22
----
lib/readline/misc.c
- rl_maybe_replace_line: if we're replacing a history entry with the
current line buffer and undo list, then zero out any undo list in
_rl_saved_line_for_history, since we're not going to use it any more
Fixes asan error reported by Grisha Levit <grishalevit@gmail.com>
lib/readline/undo.c
- rl_free_undo_list: if we're freeing rl_undo_list, it can't be a
valid value for data in _rl_saved_line_for_history
Fixes asan error reported by Grisha Levit <grishalevit@gmail.com>
lib/readline/complete.c
- display_matches: don't call rl_display_match_list if we received a
signal during get_y_or_n and didn't jump out for some reason
From a report by Grisha Levit <grishalevit@gmail.com>
- rl_display_match_list: return if we received a signal during
_rl_internal_pager (via get_y_or_n) and didn't jump out
From a report by Grisha Levit <grishalevit@gmail.com>
3/23
----
lib/readline/history.c
- history_truncate_file: don't bother trying to read the existing
history file if we're just truncating it to 0 lines
3/24
----
lib/readline/history.c
- history_truncate_file: fix off-by-one error that resulted in the
timestamp associated with the first history entry not being written
to the truncated history file
From a report by Grisha Levit <grishalevit@gmail.com>
parse.y
- decode_prompt_string: do something rational if localtime() returns
NULL (extraordinarily rare)
From a report by Paul Eggert <eggert@cs.ucla.edu>
examples/loadables/stat.c
- stattime: ditto
examples/loadables/strftime.c
- strftime_builtin: ditto
lib/readline/examples/histexamp.c
- main: ditto
include/posixtime.h
- getnow(): new inline function, calls gettimeofday and returns tv_sec;
replacement for time(0). From Paul Eggert <eggert@cs.ucla.edu>
general.h
- NOW: use a call to getnow() to standardize on gettimeofday for time
information
lib/readline/history.c
- include #posixtime.h for getnow()
- hist_inittime: use getnow() instead of calling time(0)
parse.y
- posixtime.h: include instead of <time.h>
- decode_prompt_string: use getnow() instead of calling time(0)
3/27
----
parse.y
- count_all_jobs: add extern definition if JOB_CONTROL is not defined.
From Emanuele Torre <torreemanuele6@gmail.com>
+1
View File
@@ -1208,6 +1208,7 @@ tests/history3.sub f
tests/history4.sub f
tests/history5.sub f
tests/history6.sub f
tests/history7.sub f
tests/ifs.tests f
tests/ifs.right f
tests/ifs1.sub f
Vendored
+3
View File
@@ -238,6 +238,9 @@ AC_CACHE_VAL(bash_cv_dup2_broken,
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
int
main()
{
Vendored
+3
View File
@@ -18135,6 +18135,9 @@ else $as_nop
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
int
main()
{
+22 -74
View File
@@ -6,7 +6,7 @@
*/
/*
Copyright (C) 1999-2009,2022 Free Software Foundation, Inc.
Copyright (C) 1999-2009,2022,2023 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
@@ -96,8 +96,7 @@ static int pmask;
#define OPTIONS "acdgiflmnopsuACGMP:U"
static int
octal(s)
char *s;
octal(char *s)
{
int r;
@@ -108,9 +107,7 @@ char *s;
}
static int
finfo_main(argc, argv)
int argc;
char **argv;
finfo_main(int argc, char **argv)
{
register int i;
int mode, flags, opt;
@@ -162,8 +159,7 @@ char **argv;
}
static struct stat *
getstat(f)
char *f;
getstat(char *f)
{
static struct stat st;
int fd, r;
@@ -190,8 +186,7 @@ char *f;
}
static int
printfinfo(f)
char *f;
printfinfo(char *f)
{
struct stat *st;
@@ -200,15 +195,13 @@ char *f;
}
static int
getperm(m)
int m;
getperm(int m)
{
return (m & (S_IRWXU|S_IRWXG|S_IRWXO|S_ISUID|S_ISGID));
}
static void
perms(m)
int m;
perms(int m)
{
char ubits[4], gbits[4], obits[4]; /* u=rwx,g=rwx,o=rwx */
int i;
@@ -251,8 +244,7 @@ int m;
}
static void
printmode(mode)
int mode;
printmode(int mode)
{
if (S_ISBLK(mode))
printf("S_IFBLK ");
@@ -277,8 +269,7 @@ int mode;
}
static int
printst(st)
struct stat *st;
printst(struct stat *st)
{
struct passwd *pw;
struct group *gr;
@@ -314,9 +305,7 @@ struct stat *st;
}
static int
printsome(f, flags)
char *f;
int flags;
printsome(char *f, int flags)
{
struct stat *st;
struct passwd *pw;
@@ -400,8 +389,7 @@ int flags;
#ifndef NOBUILTIN
int
finfo_builtin(list)
WORD_LIST *list;
finfo_builtin(WORD_LIST *list)
{
int c, r;
char **v;
@@ -456,34 +444,26 @@ struct builtin finfo_struct = {
#endif
#ifdef NOBUILTIN
#if defined (PREFER_STDARG)
# include <stdarg.h>
#else
# if defined (PREFER_VARARGS)
# include <varargs.h>
# endif
#endif
#include <stdarg.h>
char *this_command_name;
main(argc, argv)
int argc;
char **argv;
int
main(int argc, char **argv)
{
this_command_name = argv[0];
exit(finfo_main(argc, argv));
}
void
builtin_usage()
builtin_usage(void)
{
fprintf(stderr, "%s: usage: %s [-%s] [file ...]\n", prog, prog, OPTIONS);
}
#ifndef HAVE_STRERROR
char *
strerror(e)
int e;
strerror(int e)
{
static char ebuf[40];
extern int sys_nerr;
@@ -497,23 +477,20 @@ int e;
}
#endif
char *
xmalloc(s)
size_t s;
PTR_T
xmalloc(size_t s)
{
char *ret;
extern char *malloc();
ret = malloc(s);
if (ret)
return (ret);
fprintf(stderr, "%s: cannot malloc %d bytes\n", prog, s);
fprintf(stderr, "%s: cannot malloc %zu bytes\n", prog, s);
exit(1);
}
char *
base_pathname(p)
char *p;
base_pathname(char *p)
{
char *t;
@@ -523,9 +500,7 @@ char *p;
}
int
legal_number (string, result)
char *string;
long *result;
legal_number (char *string, long result)
{
int sign;
long value;
@@ -584,9 +559,7 @@ extern int optind;
extern char *optarg;
int
sh_getopt(c, v, o)
int c;
char **v, *o;
sh_getopt(int c, char **v, char *o)
{
int r;
@@ -596,43 +569,18 @@ char **v, *o;
return r;
}
#if defined (USE_VARARGS)
void
#if defined (PREFER_STDARG)
builtin_error (const char *format, ...)
#else
builtin_error (format, va_alist)
const char *format;
va_dcl
#endif
{
va_list args;
if (this_command_name && *this_command_name)
fprintf (stderr, "%s: ", this_command_name);
#if defined (PREFER_STDARG)
va_start (args, format);
#else
va_start (args);
#endif
vfprintf (stderr, format, args);
va_end (args);
fprintf (stderr, "\n");
}
#else
void
builtin_error (format, arg1, arg2, arg3, arg4, arg5)
char *format, *arg1, *arg2, *arg3, *arg4, *arg5;
{
if (this_command_name && *this_command_name)
fprintf (stderr, "%s: ", this_command_name);
fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5);
fprintf (stderr, "\n");
fflush (stderr);
}
#endif /* !USE_VARARGS */
#endif
+2 -2
View File
@@ -41,9 +41,9 @@
#endif
#if !defined (HAVE_GETPW_DECLS)
extern struct passwd *getpwuid ();
extern struct passwd *getpwuid (uid_t);
#endif
extern struct group *getgrgid ();
extern struct group *getgrgid (gid_t);
#include "shell.h"
#include "builtins.h"
+3 -1
View File
@@ -3,7 +3,7 @@
/* See Makefile for compilation details. */
/*
Copyright (C) 2016,2022 Free Software Foundation, Inc.
Copyright (C) 2016,2022-2023 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
@@ -261,6 +261,8 @@ stattime (time_t t, const char *timefmt)
fmt = timefmt ? timefmt : DEFTIMEFMT;
tm = localtime (&t);
if (tm == 0)
return (itos (t));
ret = xmalloc (TIMELEN_MAX);
+6
View File
@@ -80,6 +80,12 @@ strftime_builtin (WORD_LIST *list)
secs = NOW;
t = localtime (&secs);
if (t == 0)
{
builtin_error ("%s: timestamp out of range", list && list->word->word ? list->word->word : "now");
return (EXECUTION_FAILURE);
}
tbsize = strlen (format) * 4;
tbuf = 0;
+1 -1
View File
@@ -58,7 +58,7 @@ static FLIST *tee_flist;
extern int interrupt_immediately;
extern char *strerror ();
extern char *strerror (int);
int
tee_builtin (WORD_LIST *list)
+1 -1
View File
@@ -28,7 +28,7 @@
#include "bashgetopt.h"
#include "common.h"
extern char *ttyname ();
extern char *ttyname (int);
int
tty_builtin (WORD_LIST *list)
+1 -1
View File
@@ -242,7 +242,7 @@ typedef int sh_builtin_func_t (WORD_LIST *); /* sh_wlist_func_t */
#endif /* SH_FUNCTION_TYPEDEF */
#define NOW ((time_t) time ((time_t *) 0))
#define NOW getnow()
#define GETTIME(tv) gettimeofday(&(tv), NULL)
/* Some defines for calling file status functions. */
+9
View File
@@ -52,6 +52,15 @@ struct timeval
extern int gettimeofday (struct timeval * restrict, void * restrict);
#endif
/* consistently use gettimeofday for time information */
static inline time_t
getnow(void)
{
struct timeval now;
gettimeofday (&now, 0);
return now.tv_sec;
}
/* These exist on BSD systems, at least. */
#if !defined (timerclear)
# define timerclear(tvp) do { (tvp)->tv_sec = 0; (tvp)->tv_usec = 0; } while (0)
+5 -3
View File
@@ -1630,7 +1630,7 @@ rl_display_match_list (char **matches, int len, int max)
if (_rl_page_completions && lines >= (_rl_screenheight - 1) && i < count)
{
lines = _rl_internal_pager (lines);
if (lines < 0)
if (lines < 0 || _rl_complete_display_matches_interrupt)
return;
}
}
@@ -1658,7 +1658,7 @@ rl_display_match_list (char **matches, int len, int max)
if (_rl_page_completions && lines >= _rl_screenheight - 1)
{
lines = _rl_internal_pager (lines);
if (lines < 0)
if (lines < 0 || _rl_complete_display_matches_interrupt)
return;
}
}
@@ -1745,7 +1745,9 @@ display_matches (char **matches)
}
}
rl_display_match_list (matches, len, max);
/* We rely on the caller to set MATCHES to 0 when this returns. */
if (_rl_complete_display_matches_interrupt == 0)
rl_display_match_list (matches, len, max);
rl_forced_update_display ();
rl_display_fixed = 1;
+5 -5
View File
@@ -32,9 +32,7 @@
#include <string.h>
int
main (argc, argv)
int argc;
char **argv;
main (int argc, char **argv)
{
char line[1024], *t;
int len, done;
@@ -91,6 +89,7 @@ main (argc, argv)
register HIST_ENTRY **the_list;
register int i;
time_t tt;
struct tm *tm;
char timestr[128];
the_list = history_list ();
@@ -98,8 +97,9 @@ main (argc, argv)
for (i = 0; the_list[i]; i++)
{
tt = history_get_time (the_list[i]);
if (tt)
strftime (timestr, sizeof (timestr), "%a %R", localtime(&tt));
tm = tt ? localtime (&tt) : 0;
if (tm)
strftime (timestr, sizeof (timestr), "%a %R", tm);
else
strcpy (timestr, "??");
printf ("%d: %s: %s\n", i + history_base, timestr, the_list[i]->line);
+36 -12
View File
@@ -1,6 +1,6 @@
/* histfile.c - functions to manipulate the history file. */
/* Copyright (C) 1989-2019 Free Software Foundation, Inc.
/* Copyright (C) 1989-2019,2023 Free Software Foundation, Inc.
This file contains the GNU History Library (History), a set of
routines for managing the text of previously typed lines.
@@ -578,6 +578,15 @@ history_truncate_file (const char *fname, int lines)
goto truncate_exit;
}
/* Don't bother with any of this if we're truncating to zero length. */
if (lines == 0)
{
close (file);
buffer[chars_read = 0] = '\0';
bp = buffer;
goto truncate_write;
}
chars_read = read (file, buffer, file_size);
close (file);
@@ -586,30 +595,38 @@ history_truncate_file (const char *fname, int lines)
rv = (chars_read < 0) ? errno : 0;
goto truncate_exit;
}
buffer[chars_read] = '\0'; /* for the initial check of bp1[1] */
/* Count backwards from the end of buffer until we have passed
LINES lines. bp1 is set funny initially. But since bp[1] can't
be a comment character (since it's off the end) and *bp can't be
both a newline and the history comment character, it should be OK. */
for (bp1 = bp = buffer + chars_read - 1; lines && bp > buffer; bp--)
both a newline and the history comment character, it should be OK.
If we are writing history timestamps, we need to add one to LINES
because we decrement it one extra time the first time through the loop
and we need the final timestamp line. */
lines += history_write_timestamps;
for (bp1 = bp = buffer + chars_read - 1; lines > 0 && bp > buffer; bp--)
{
if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0)
lines--;
bp1 = bp;
}
/* If this is the first line, then the file contains exactly the
/* This is the right line, so move to its start. If we're writing history
timestamps, we want to go back until *bp == '\n' and bp1 starts a
history timestamp. If we're not, just move back to *bp == '\n'.
If this is the first line, then the file contains exactly the
number of lines we want to truncate to, so we don't need to do
anything. It's the first line if we don't find a newline between
the current value of i and 0. Otherwise, write from the start of
this line until the end of the buffer. */
anything, and we'll end up with bp == buffer.
Otherwise, write from the start of this line until the end of the
buffer. */
for ( ; bp > buffer; bp--)
{
if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0)
{
if (*bp == '\n' && (history_write_timestamps == 0 || HIST_TIMESTAMP_START(bp1)))
{
bp++;
break;
}
}
bp1 = bp;
}
@@ -623,15 +640,22 @@ history_truncate_file (const char *fname, int lines)
goto truncate_exit;
}
truncate_write:
tempname = history_tempfile (filename);
if ((file = open (tempname, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0600)) != -1)
{
if (write (file, bp, chars_read - (bp - buffer)) < 0)
rv = errno;
{
rv = errno;
close (file);
}
if (rv == 0 && fstat (file, &nfinfo) < 0)
rv = errno;
{
rv = errno;
close (file);
}
if (rv == 0 && close (file) < 0)
rv = errno;
+2 -1
View File
@@ -42,6 +42,7 @@
# endif
# include <unistd.h>
#endif
#include "posixtime.h"
#include <errno.h>
@@ -261,7 +262,7 @@ hist_inittime (void)
time_t t;
char ts[64], *ret;
t = (time_t) time ((time_t *)0);
t = getnow ();
#if defined (HAVE_VSNPRINTF) /* assume snprintf if vsnprintf exists */
snprintf (ts, sizeof (ts) - 1, "X%lu", (unsigned long) t);
#else
+5 -4
View File
@@ -340,13 +340,14 @@ rl_maybe_replace_line (void)
xfree (temp->line);
FREE (temp->timestamp);
xfree (temp);
/* What about _rl_saved_line_for_history? if the saved undo list is
rl_undo_list, and we just put that into a history entry, should
we set the saved undo list to NULL? */
if (_rl_saved_line_for_history && (UNDO_LIST *)_rl_saved_line_for_history->data == rl_undo_list)
_rl_saved_line_for_history->data = 0;
/* Do we want to set rl_undo_list = 0 here since we just saved it into
a history entry? */
rl_undo_list = 0;
/* XXX - what about _rl_saved_line_for_history? if the saved undo list
is rl_undo_list, and we just put that into a history entry, should
we set the saved undo list to NULL? */
}
return 0;
}
+2
View File
@@ -122,6 +122,8 @@ rl_free_undo_list (void)
_rl_free_undo_list (rl_undo_list);
rl_undo_list = (UNDO_LIST *)NULL;
_hs_replace_history_data (-1, (histdata_t *)orig_list, (histdata_t *)NULL);
if (_rl_saved_line_for_history && (UNDO_LIST *)_rl_saved_line_for_history->data == orig_list)
_rl_saved_line_for_history->data = 0;
}
UNDO_LIST *
+2 -1
View File
@@ -279,10 +279,11 @@ char *
sh_mktmpdir (const char *nameroot, int flags)
{
char *filename;
int fd;
#ifdef USE_MKDTEMP
char *tdir, *dirname;
const char *lroot;
int fd, tdlen;
int tdlen;
filename = (char *)xmalloc (PATH_MAX + 1);
tdir = get_tmpdir (flags);
+34 -15
View File
@@ -73,6 +73,7 @@
# include "jobs.h"
#else
extern int cleanup_dead_jobs (void);
extern int count_all_jobs (void);
#endif /* JOB_CONTROL */
#if defined (ALIAS)
@@ -85,7 +86,7 @@ typedef void *alias_t;
# ifndef _MINIX
# include <sys/param.h>
# endif
# include <time.h>
# include "posixtime.h"
# if defined (TM_IN_SYS_TIME)
# include <sys/types.h>
# include <sys/time.h>
@@ -5976,13 +5977,17 @@ decode_prompt_string (char *string)
case '@':
case 'A':
/* Make the current time/date into a string. */
(void) time (&the_time);
the_time = getnow ();
#if defined (HAVE_TZSET)
sv_tz ("TZ"); /* XXX -- just make sure */
#endif
tm = localtime (&the_time);
if (c == 'd')
if (tm == 0)
{
strcpy (timebuf, "??");
tslen = 2;
}
else if (c == 'd')
tslen = strftime (timebuf, sizeof (timebuf), "%a %b %d", tm);
else if (c == 't')
tslen = strftime (timebuf, sizeof (timebuf), "%H:%M:%S", tm);
@@ -6005,22 +6010,36 @@ decode_prompt_string (char *string)
if (string[1] != '{') /* } */
goto not_escape;
(void) time (&the_time);
the_time = getnow ();
tm = localtime (&the_time);
string += 2; /* skip { */
timefmt = xmalloc (strlen (string) + 3);
for (t = timefmt; *string && *string != '}'; )
*t++ = *string++;
*t = '\0';
t = string;
while (*string && *string != '}')
string++;
c = *string; /* tested at add_string */
if (timefmt[0] == '\0')
if (tm)
{
timefmt[0] = '%';
timefmt[1] = 'X'; /* locale-specific current time */
timefmt[2] = '\0';
size_t tflen;
tflen = string - t;
timefmt = xmalloc (tflen + 3);
memcpy (timefmt, t, tflen);
timefmt[tflen] = '\0';
if (timefmt[0] == '\0')
{
timefmt[0] = '%';
timefmt[1] = 'X'; /* locale-specific current time */
timefmt[2] = '\0';
}
tslen = strftime (timebuf, sizeof (timebuf), timefmt, tm);
free (timefmt);
}
else
{
strcpy (timebuf, "??");
tslen = 2;
}
tslen = strftime (timebuf, sizeof (timebuf), timefmt, tm);
free (timefmt);
if (tslen == 0)
timebuf[0] = '\0';
+11 -2
View File
@@ -452,10 +452,19 @@ print_sig(void)
struct tm *timetm;
time_t clock;
datbuf[0] = '\0';
#ifdef HAVE_GETTIMEOFDAY
struct timeval tv;
gettimeofday (&tv, 0);
clock = tv.tv_sec;
#else
clock = time(NULL);
#endif
timetm = localtime(&clock);
strftime(datbuf, MED_STR_MAX, TIMEFORMAT, timetm);
datbuf[0] = '\0';
if (timetm)
strftime(datbuf, MED_STR_MAX, TIMEFORMAT, timetm);
else
strcpy (datbuf, "??");
printf(signature, manpage, datbuf);
}
+44
View File
@@ -297,3 +297,47 @@ out of range 4
7 echo 9
8 echo 10
5 echo 10
$ 1
$ 2
$ 3
$ exit
6
$ 1
$ 2
$ 3
$ 4
$ 5
$ exit
6
$ 1
$ 2
$ exit
4
$ e 1
$ e 2
$ e 3
$ exit
3
e 1
e 2
e 3
$ x 1
$ x 2
$ x 3
$ x 4
$ x 5
$ exit
3
x 3
x 4
x 5
$ y 1
$ y 2
$ exit
2
y 1
y 2
$ 1
$ 2
$ exit
0
+1
View File
@@ -131,3 +131,4 @@ ${THIS_SH} ./history3.sub
${THIS_SH} ./history4.sub
${THIS_SH} ./history5.sub
${THIS_SH} ./history6.sub
${THIS_SH} ./history7.sub
+73
View File
@@ -0,0 +1,73 @@
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# test history file truncation for various values of $HISTFILESIZE
: ${THIS_SH:=./bash}
: ${TMPDIR:=/var/tmp}
export HISTTIMEFORMAT=
export HISTFILESIZE=3
export HISTFILE=$TMPDIR/hist-$$
export PS1='$ '
rm -f $HISTFILE
# exactly the number of lines
${THIS_SH} --norc -in <<<$'1\n2\n3'
wc -l < $HISTFILE
rm -f $HISTFILE
# truncating to fewer lines
${THIS_SH} --norc -in <<<$'1\n2\n3\n4\n5'
wc -l < $HISTFILE
rm -f $HISTFILE
# the history file contains fewer lines than $HISTFILESIZE
${THIS_SH} --norc -in <<<$'1\n2'
wc -l < $HISTFILE
rm -f $HISTFILE
# now we try it without timestamps
unset HISTTIMEFORMAT
# exactly the number of lines
${THIS_SH} --norc -in <<<$'e 1\ne 2\ne 3'
wc -l < $HISTFILE
cat $HISTFILE
rm -f $HISTFILE
# truncating to fewer lines
${THIS_SH} --norc -in <<<$'x 1\nx 2\nx 3\nx 4\nx 5'
wc -l < $HISTFILE
cat $HISTFILE
rm -f $HISTFILE
# the history file contains fewer lines than $HISTFILESIZE
${THIS_SH} --norc -in <<<$'y 1\ny 2'
wc -l < $HISTFILE
cat $HISTFILE
rm -f $HISTFILE
# we want to truncate the history file to zero length
HISTFILESIZE=0
${THIS_SH} --norc -in <<<$'1\n2'
wc -l < $HISTFILE
rm -f $HISTFILE
+1 -2
View File
@@ -2445,8 +2445,7 @@ find_function_def (const char *name)
/* Return the value of VAR. VAR is assumed to have been the result of a
lookup without any subscript, if arrays are compiled into the shell. */
char *
get_variable_value (var)
SHELL_VAR *var;
get_variable_value (SHELL_VAR *var)
{
if (var == 0)
return ((char *)NULL);