#line 23 "pushd.def"
#line 48 "pushd.def"
#line 70 "pushd.def"
#line 93 "pushd.def"
#include <config.h>
#if defined (PUSHD_AND_POPD)
#include <stdio.h>
#ifndef _MINIX
# include <sys/param.h>
#endif
#if defined (HAVE_UNISTD_H)
# ifdef _MINIX
# include <sys/types.h>
# endif
# include <unistd.h>
#endif
#include "../bashansi.h"
#include "../bashintl.h"
#include <errno.h>
#include <tilde/tilde.h>
#include "../shell.h"
#include "maxpath.h"
#include "common.h"
#include "builtext.h"
#ifdef LOADABLE_BUILTIN
# include "builtins.h"
#endif
#if !defined (errno)
extern int errno;
#endif
static char **pushd_directory_list = (char **)NULL;
static int directory_list_size;
static int directory_list_offset;
static void pushd_error __P((int, char *));
static void clear_directory_stack __P((void));
static int cd_to_string __P((char *));
static int change_to_temp __P((char *));
static void add_dirstack_element __P((char *));
static int get_dirstack_index __P((intmax_t, int, int *));
#define NOCD 0x01
#define ROTATE 0x02
#define LONGFORM 0x04
#define CLEARSTAK 0x08
int
pushd_builtin (list)
WORD_LIST *list;
{
WORD_LIST *orig_list;
char *temp, *current_directory, *top;
int j, flags, skipopt;
intmax_t num;
char direction;
orig_list = list;
if (list && list->word && ISOPTION (list->word->word, '-'))
{
list = list->next;
skipopt = 1;
}
else
skipopt = 0;
if (list == 0)
{
if (directory_list_offset == 0)
{
builtin_error (_("no other directory"));
return (EXECUTION_FAILURE);
}
current_directory = get_working_directory ("pushd");
if (current_directory == 0)
return (EXECUTION_FAILURE);
j = directory_list_offset - 1;
temp = pushd_directory_list[j];
pushd_directory_list[j] = current_directory;
j = change_to_temp (temp);
free (temp);
return j;
}
for (flags = 0; skipopt == 0 && list; list = list->next)
{
if (ISOPTION (list->word->word, 'n'))
{
flags |= NOCD;
}
else if (ISOPTION (list->word->word, '-'))
{
list = list->next;
break;
}
else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
break;
else if (((direction = list->word->word[0]) == '+') || direction == '-')
{
if (legal_number (list->word->word + 1, &num) == 0)
{
sh_invalidnum (list->word->word);
builtin_usage ();
return (EXECUTION_FAILURE);
}
if (direction == '-')
num = directory_list_offset - num;
if (num > directory_list_offset || num < 0)
{
pushd_error (directory_list_offset, list->word->word);
return (EXECUTION_FAILURE);
}
flags |= ROTATE;
}
else if (*list->word->word == '-')
{
sh_invalidopt (list->word->word);
builtin_usage ();
return (EXECUTION_FAILURE);
}
else
break;
}
if (flags & ROTATE)
{
temp = get_working_directory ("pushd");
if (num == 0)
{
j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
free (temp);
return j;
}
do
{
top = pushd_directory_list[directory_list_offset - 1];
for (j = directory_list_offset - 2; j > -1; j--)
pushd_directory_list[j + 1] = pushd_directory_list[j];
pushd_directory_list[j + 1] = temp;
temp = top;
num--;
}
while (num);
j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
free (temp);
return j;
}
if (list == 0)
return (EXECUTION_SUCCESS);
current_directory = get_working_directory ("pushd");
if (current_directory == 0)
return (EXECUTION_FAILURE);
j = ((flags & NOCD) == 0) ? cd_builtin (skipopt ? orig_list : list) : EXECUTION_SUCCESS;
if (j == EXECUTION_SUCCESS)
{
add_dirstack_element ((flags & NOCD) ? savestring (list->word->word) : current_directory);
dirs_builtin ((WORD_LIST *)NULL);
if (flags & NOCD)
free (current_directory);
return (EXECUTION_SUCCESS);
}
else
{
free (current_directory);
return (EXECUTION_FAILURE);
}
}
int
popd_builtin (list)
WORD_LIST *list;
{
register int i;
intmax_t which;
int flags;
char direction;
char *which_word;
which_word = (char *)NULL;
for (flags = 0, which = 0, direction = '+'; list; list = list->next)
{
if (ISOPTION (list->word->word, 'n'))
{
flags |= NOCD;
}
else if (ISOPTION (list->word->word, '-'))
{
list = list->next;
break;
}
else if (((direction = list->word->word[0]) == '+') || direction == '-')
{
if (legal_number (list->word->word + 1, &which) == 0)
{
sh_invalidnum (list->word->word);
builtin_usage ();
return (EXECUTION_FAILURE);
}
which_word = list->word->word;
}
else if (*list->word->word == '-')
{
sh_invalidopt (list->word->word);
builtin_usage ();
return (EXECUTION_FAILURE);
}
else
break;
}
if (which > directory_list_offset || (directory_list_offset == 0 && which == 0))
{
pushd_error (directory_list_offset, which_word ? which_word : "");
return (EXECUTION_FAILURE);
}
if ((direction == '+' && which == 0) ||
(direction == '-' && which == directory_list_offset))
{
i = ((flags & NOCD) == 0) ? cd_to_string (pushd_directory_list[directory_list_offset - 1])
: EXECUTION_SUCCESS;
if (i != EXECUTION_SUCCESS)
return (i);
free (pushd_directory_list[--directory_list_offset]);
}
else
{
i = (direction == '+') ? directory_list_offset - which : which;
free (pushd_directory_list[i]);
directory_list_offset--;
for (; i < directory_list_offset; i++)
pushd_directory_list[i] = pushd_directory_list[i + 1];
}
dirs_builtin ((WORD_LIST *)NULL);
return (EXECUTION_SUCCESS);
}
int
dirs_builtin (list)
WORD_LIST *list;
{
int flags, desired_index, index_flag, vflag;
intmax_t i;
char *temp, *w;
for (flags = vflag = index_flag = 0, desired_index = -1, w = ""; list; list = list->next)
{
if (ISOPTION (list->word->word, 'l'))
{
flags |= LONGFORM;
}
else if (ISOPTION (list->word->word, 'c'))
{
flags |= CLEARSTAK;
}
else if (ISOPTION (list->word->word, 'v'))
{
vflag |= 2;
}
else if (ISOPTION (list->word->word, 'p'))
{
vflag |= 1;
}
else if (ISOPTION (list->word->word, '-'))
{
list = list->next;
break;
}
else if (*list->word->word == '+' || *list->word->word == '-')
{
int sign;
if (legal_number (w = list->word->word + 1, &i) == 0)
{
sh_invalidnum (list->word->word);
builtin_usage ();
return (EXECUTION_FAILURE);
}
sign = (*list->word->word == '+') ? 1 : -1;
desired_index = get_dirstack_index (i, sign, &index_flag);
}
else
{
sh_invalidopt (list->word->word);
builtin_usage ();
return (EXECUTION_FAILURE);
}
}
if (flags & CLEARSTAK)
{
clear_directory_stack ();
return (EXECUTION_SUCCESS);
}
if (index_flag && (desired_index < 0 || desired_index > directory_list_offset))
{
pushd_error (directory_list_offset, w);
return (EXECUTION_FAILURE);
}
#define DIRSTACK_FORMAT(temp) \
(flags & LONGFORM) ? temp : polite_directory_format (temp)
if (index_flag == 0 || (index_flag == 1 && desired_index == 0))
{
temp = get_working_directory ("dirs");
if (temp == 0)
temp = savestring (_("<no current directory>"));
if (vflag & 2)
printf ("%2d %s", 0, DIRSTACK_FORMAT (temp));
else
printf ("%s", DIRSTACK_FORMAT (temp));
free (temp);
if (index_flag)
{
putchar ('\n');
return EXECUTION_SUCCESS;
}
}
#define DIRSTACK_ENTRY(i) \
(flags & LONGFORM) ? pushd_directory_list[i] \
: polite_directory_format (pushd_directory_list[i])
if (index_flag)
{
if (vflag & 2)
printf ("%2d %s", directory_list_offset - desired_index,
DIRSTACK_ENTRY (desired_index));
else
printf ("%s", DIRSTACK_ENTRY (desired_index));
}
else
for (i = directory_list_offset - 1; i >= 0; i--)
if (vflag >= 2)
printf ("\n%2d %s", directory_list_offset - (int)i, DIRSTACK_ENTRY (i));
else
printf ("%s%s", (vflag & 1) ? "\n" : " ", DIRSTACK_ENTRY (i));
putchar ('\n');
fflush (stdout);
return (EXECUTION_SUCCESS);
}
static void
pushd_error (offset, arg)
int offset;
char *arg;
{
if (offset == 0)
builtin_error ("directory stack empty");
else
sh_erange (arg, "directory stack index");
}
static void
clear_directory_stack ()
{
register int i;
for (i = 0; i < directory_list_offset; i++)
free (pushd_directory_list[i]);
directory_list_offset = 0;
}
static int
cd_to_string (name)
char *name;
{
WORD_LIST *tlist;
WORD_LIST *dir;
int result;
dir = make_word_list (make_word (name), NULL);
tlist = make_word_list (make_word ("--"), dir);
result = cd_builtin (tlist);
dispose_words (tlist);
return (result);
}
static int
change_to_temp (temp)
char *temp;
{
int tt;
tt = temp ? cd_to_string (temp) : EXECUTION_FAILURE;
if (tt == EXECUTION_SUCCESS)
dirs_builtin ((WORD_LIST *)NULL);
return (tt);
}
static void
add_dirstack_element (dir)
char *dir;
{
if (directory_list_offset == directory_list_size)
pushd_directory_list = strvec_resize (pushd_directory_list, directory_list_size += 10);
pushd_directory_list[directory_list_offset++] = dir;
}
static int
get_dirstack_index (ind, sign, indexp)
intmax_t ind;
int sign, *indexp;
{
if (indexp)
*indexp = sign > 0 ? 1 : 2;
if (ind == 0 && sign > 0)
return 0;
else if (ind == directory_list_offset)
{
if (indexp)
*indexp = sign > 0 ? 2 : 1;
return 0;
}
else if (ind >= 0 && ind <= directory_list_offset)
return (sign > 0 ? directory_list_offset - ind : ind);
else
return -1;
}
char *
get_dirstack_from_string (string)
char *string;
{
int ind, sign, index_flag;
intmax_t i;
sign = 1;
if (*string == '-' || *string == '+')
{
sign = (*string == '-') ? -1 : 1;
string++;
}
if (legal_number (string, &i) == 0)
return ((char *)NULL);
index_flag = 0;
ind = get_dirstack_index (i, sign, &index_flag);
if (index_flag && (ind < 0 || ind > directory_list_offset))
return ((char *)NULL);
if (index_flag == 0 || (index_flag == 1 && ind == 0))
return (get_string_value ("PWD"));
else
return (pushd_directory_list[ind]);
}
#ifdef INCLUDE_UNUSED
char *
get_dirstack_element (ind, sign)
intmax_t ind;
int sign;
{
int i;
i = get_dirstack_index (ind, sign, (int *)NULL);
return (i < 0 || i > directory_list_offset) ? (char *)NULL
: pushd_directory_list[i];
}
#endif
void
set_dirstack_element (ind, sign, value)
intmax_t ind;
int sign;
char *value;
{
int i;
i = get_dirstack_index (ind, sign, (int *)NULL);
if (ind == 0 || i < 0 || i > directory_list_offset)
return;
free (pushd_directory_list[i]);
pushd_directory_list[i] = savestring (value);
}
WORD_LIST *
get_directory_stack (flags)
int flags;
{
register int i;
WORD_LIST *ret;
char *d, *t;
for (ret = (WORD_LIST *)NULL, i = 0; i < directory_list_offset; i++)
{
d = (flags&1) ? polite_directory_format (pushd_directory_list[i])
: pushd_directory_list[i];
ret = make_word_list (make_word (d), ret);
}
d = get_working_directory ("dirstack");
i = 0;
if (d == 0)
d = ".";
else
{
t = polite_directory_format (d);
if (t != d)
{
free (d);
d = t;
}
else
i = 1;
}
ret = make_word_list (make_word (d), ret);
if (i)
free (d);
return ret;
}
#ifdef LOADABLE_BUILTIN
char * const dirs_doc[] = {
N_("Display the list of currently remembered directories. Directories"),
N_("find their way onto the list with the `pushd' command; you can get"),
N_("back up through the list with the `popd' command."),
N_(" "),
N_("The -l flag specifies that `dirs' should not print shorthand versions"),
N_("of directories which are relative to your home directory. This means"),
N_("that `~/bin' might be displayed as `/homes/bfox/bin'. The -v flag"),
N_("causes `dirs' to print the directory stack with one entry per line,"),
N_("prepending the directory name with its position in the stack. The -p"),
N_("flag does the same thing, but the stack position is not prepended."),
N_("The -c flag clears the directory stack by deleting all of the elements."),
N_(" "),
N_("+N displays the Nth entry counting from the left of the list shown by"),
N_(" dirs when invoked without options, starting with zero."),
N_(" "),
N_("-N displays the Nth entry counting from the right of the list shown by"),
N_(" dirs when invoked without options, starting with zero."),
(char *)NULL
};
char * const pushd_doc[] = {
N_("Adds a directory to the top of the directory stack, or rotates"),
N_("the stack, making the new top of the stack the current working"),
N_("directory. With no arguments, exchanges the top two directories."),
N_(" "),
N_("+N Rotates the stack so that the Nth directory (counting"),
N_(" from the left of the list shown by `dirs', starting with"),
N_(" zero) is at the top."),
N_(" "),
N_("-N Rotates the stack so that the Nth directory (counting"),
N_(" from the right of the list shown by `dirs', starting with"),
N_(" zero) is at the top."),
N_(" "),
N_("-n suppress the normal change of directory when adding directories"),
N_(" to the stack, so only the stack is manipulated."),
N_(" "),
N_("dir adds DIR to the directory stack at the top, making it the"),
N_(" new current working directory."),
N_(" "),
N_("You can see the directory stack with the `dirs' command."),
(char *)NULL
};
char * const popd_doc[] = {
N_("Removes entries from the directory stack. With no arguments,"),
N_("removes the top directory from the stack, and cd's to the new"),
N_("top directory."),
N_(" "),
N_("+N removes the Nth entry counting from the left of the list"),
N_(" shown by `dirs', starting with zero. For example: `popd +0'"),
N_(" removes the first directory, `popd +1' the second."),
N_(" "),
N_("-N removes the Nth entry counting from the right of the list"),
N_(" shown by `dirs', starting with zero. For example: `popd -0'"),
N_(" removes the last directory, `popd -1' the next to last."),
N_(" "),
N_("-n suppress the normal change of directory when removing directories"),
N_(" from the stack, so only the stack is manipulated."),
N_(" "),
N_("You can see the directory stack with the `dirs' command."),
(char *)NULL
};
struct builtin pushd_struct = {
"pushd",
pushd_builtin,
BUILTIN_ENABLED,
pushd_doc,
"pushd [+N | -N] [-n] [dir]",
0
};
struct builtin popd_struct = {
"popd",
popd_builtin,
BUILTIN_ENABLED,
popd_doc,
"popd [+N | -N] [-n]",
0
};
struct builtin dirs_struct = {
"dirs",
dirs_builtin,
BUILTIN_ENABLED,
dirs_doc,
"dirs [-clpv] [+N] [-N]",
0
};
#endif
#endif