Yet Another Shell (part 4 of 11)

Dave Clemans dclemans.falcon at mntgfx.mentor.com
Thu Mar 16 08:20:48 AEST 1989


With all the talk about shells that has been going on recently...

Here's an early pre-release of a "Korn"-like shell for anyone
who might want to experiment.  It is definitely NOT complete,
but it starting to be usable.  It does use some GNU code (for
expression evaluation), so it presumably comes under their
"copyleft".

It basically runs on BSD/USG Unix systems, and on the Atari ST.
I'm currently working on a port to the Amiga.

dgc

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 4 (of 11)."
# Contents:  main.c reader/editing.c reader/reader.c
# Wrapped by dclemans at dclemans on Wed Mar 15 14:03:55 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'main.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'main.c'\"
else
echo shar: Extracting \"'main.c'\" \(16634 characters\)
sed "s/^X//" >'main.c' <<'END_OF_FILE'
X/*
X * Command Input Shell
X * Dave Clemans
X * 12/88-1/89
X *
X * "spiritually" based on Bourne, Korn shells
X *
X * Main Routine
X *
X * $Id: main.c,v 1.11 89/03/07 19:06:03 dclemans Exp $
X *
X * $Log:	main.c,v $
X * Revision 1.11  89/03/07  19:06:03  dclemans
X * portability fixes
X * 
X * Revision 1.10  89/02/22  21:32:00  dclemans
X * Implement simple background job monitoring facility
X * 
X * Revision 1.9  89/02/22  15:39:30  dclemans
X * and also the SYSV default window size
X * 
X * Revision 1.8  89/02/22  15:06:52  dclemans
X * pick up BSD default window size.
X * 
X * Revision 1.7  89/02/21  19:39:25  dclemans
X * Implement RANDOM and SECONDS variables
X * 
X * Revision 1.6  89/02/21  08:36:48  dclemans
X * Implement pseudo-signals ERR, EXIT, DEBUG
X * 
X * Revision 1.5  89/02/20  22:30:31  dclemans
X * Add hooks to setup default signals
X * 
X * Revision 1.4  89/02/20  20:12:00  dclemans
X * Add RCS identifiers
X * 
X */
X#include <stdio.h>
X#include "shell.h"
X#include <time.h>
Xextern unsigned long time();
X#ifndef GEMDOS
X#ifdef  unix
X#ifndef USG
X#include <sgtty.h>
X#else
X#include <termio.h>
X#endif  /* USG */
X#include <sys/param.h>
X#include <fcntl.h>
Xextern int errno;
X#endif  /* unix */
X#endif  /* GEMDOS */
X
X#ifdef  GEMDOS
X#ifdef  MWC
Xlong    _stksize = DEFSTACK;
X#endif  /* MWC */
X#endif  /* GEMDOS */
X
Xint flag_echoinput = 0;
Xint flag_echoexec = 0;
Xint flag_keywords = 0;
Xint flag_cmdhash = 0;
Xint flag_varerr = 0;
Xint flag_noglob = 0;
Xint flag_allexport = 0;
Xint flag_noexec = 0;
Xint flag_interactive = 1;
X#ifndef GEMDOS
Xint flag_monitor = 1;
X#else
Xint flag_monitor = 0;
X#endif  /* GEMDOS */
Xstruct  envinfo base_env;
X
X#ifndef GEMDOS
Xstatic int max_sysfd = NOFILE;
Xstatic char open_sysfds[NOFILE/8+1];
X#endif  /* GEMDOS */
X
X#ifdef  MWC
Xextern  char *_iovector;
X#endif  /* MWC */
X
Xmain(argc,argv)
Xint argc;
Xchar **argv;
X{
X    register struct phrase *pp;
X    register struct phrase *sp;
X    register int i;
X    int flag,rc;
X    char *p;
X    char *cmd;
X    char buffer[16];
X#ifndef GEMDOS
X#ifndef USG
X    struct winsize window;
X#endif  /* USG */
X#endif  /* GEMDOS */
X
X    base_env.start_at = time(0L);
X
X    cmd_forceexit = 0;
X    cmd_count = 1;
X    for (i = 0; i <= PROMPT_MAX; i++)
X    {   /* set up prompts */
X        base_env.prompts[i] = (char *)NULL;
X        base_env.prompts_issued[i] = 0;
X    }
X    base_env.columns = 0;
X    base_env.lines = 0;
X    base_env.homedir = (char *)NULL;
X    base_env.shellname = (char *)NULL;
X    base_env.envfile = (char *)NULL;
X    base_env.cd_path = (char *)NULL;
X    base_env.exec_path = (char *)NULL;
X    base_env.separators = (char *)NULL;
X    base_env.var = (struct varstack *)NULL;
X    base_env.alias_table = (struct aliases *)NULL;
X    base_env.func_table = (struct function *)NULL;
X    base_env.temp_count = 0;
X    base_env.break_level = 0;
X    base_env.continue_level = 0;
X#ifdef  GEMDOS
X    base_env.fd_con = base_env.fd_aux = base_env.fd_prn = -1;
X    base_env.tmpdir = (char *)NULL;
X    base_env.exec_suff = (char *)NULL;
X#endif  /* GEMDOS */
X    base_env.dir = (struct dirstack *)NULL;
X    base_env.io = (struct iostack *)NULL;
X    base_env.pending_heredocs = (struct herestack *)NULL;
X    base_env.history_list = (struct hist_phrase *)NULL;
X    base_env.history_size = 0;
X
X    if (main_varpush() < 0)
X        exit(1);
X    if (main_iopush() < 0)
X    {   /* enough io set up to run? */
X        errmsg(0,LOC("main"),"unable to create I/O environment to run in");
X        exit(1);
X    }
X    base_env.fd_input = base_env.io->input;
X    rc = lchdir(".",&p);
X    if (rc != 0)
X    {   /* if we couldn't find it... */
X        errmsg(0,LOC("main"),"couldn't get home directory");
X        exit(2);
X    }
X    if (main_dirpush() < 0)
X    {   /* enough memory? */
X        /* message already printed */
X        exit(3);
X    }
X    var_define0("HOME",p,0);
X    var_define0("PWD",p,0);
X    free(p);
X    var_settype("RANDOM",TYPE_FUNCTION,~0);
X    var_settype("SECONDS",TYPE_FUNCTION,~0);
X
X    for (i = 0; var_init[i] != (char *)NULL; i += 2)
X        var_define0(var_init[i],var_init[i+1],0);
X    sprintf(buffer,"%d",DEFAULT_HISTORY);
X    var_define0("HISTSIZE",buffer,0);
X#ifndef GEMDOS
X#ifndef USG
X    ioctl(base_env.io->input,TIOCGWINSZ,&window);
X#else
X    ioctl(base_env.io->input,TCGETWINSZ,&window);
X#endif  /* USG */
X    sprintf(buffer,"%d",window.ws_row);
X    var_define0("LINES",buffer,0);
X    sprintf(buffer,"%d",window.ws_col);
X    var_define0("COLUMNS",buffer,0);
X#endif  /* GEMDOS */
X    var_readenv();          /* defines environment variables */
X
X    alias_init();           /* defines default aliases */
X
X    signal_init();          /* sets up default signals */
X
X    if (argc > 0 && argv != (char **)NULL && argv != (char **)-1)
X    {   /* if there are any args */
X        var_setarg0(argv[0]);
X        for (i = 1; i < argc; i++)
X        {   /* look for our own args */
X            if (argv[i][0] != '-')
X            {   /* a file to read... */
X#ifdef  GEMDOS
X                if (strlen(argv[i]) == 0)
X                    break;
X#endif  /* GEMDOS */
X#ifdef  GEMDOS
X                base_env.fd_input = Fopen(argv[i],0);
X#else
X                base_env.fd_input = open(argv[i],0);
X#endif  /* GEMDOS */
X                if (base_env.io->input < 0)
X                {   /* is that a file? */
X                    errmsg(0,LOC("main"),"can't open %s",argv[i]);
X                    exit(4);
X                }
X                flag_interactive = 0;
X                i++;
X                break;
X            }
X            p = &argv[i][1];
X            if (*p == '\0' || *p == '-')
X            {   /* end of switches? */
X                if (*p == '\0')
X                    flag_echoinput = flag_echoexec = 0;
X                i++;
X                break;
X            }
X            for (; *p != '\0'; p++)
X            {   /* for each arg character */
X                switch (*p)
X                {   /* which arg? */
X                    case 'a':   /* export all? */
X                        flag_allexport = 1;
X                        break;
X                    case 'f':   /* no file expansion? */
X                        flag_noglob = 1;
X                        break;
X                    case 'm':   /* monitor jobs? */
X                        flag_monitor = 1;
X                        break;
X                    case 'n':   /* no execution? */
X                        flag_noexec = 1;
X                        break;
X                    case 'u':   /* unknown var errors? */
X                        flag_varerr = 1;
X                        break;
X                    case 'v':   /* echo input? */
X                        flag_echoinput = 1;
X                        break;
X                    case 'x':   /* echo execution? */
X                        flag_echoexec = 1;
X                        break;
X                    case 'k':   /* all keywords? */
X                        flag_keywords = 1;
X                        break;
X                    case 'h':   /* command hashing? */
X                        flag_cmdhash = 1;
X                        break;
X                    case 'i':   /* force interactiveness? */
X                        flag_interactive = 1;
X                        break;
X                    case 't':   /* only one command? */
X                        cmd_forceexit = 1;
X                        break;
X                    case 'c':   /* exec one command? */
X                        i++;
X                        cmd = argv[i];
X                        if (cmd == (char *)NULL)
X                        {   /* if nothing there */
X                            errmsg(0,LOC("main"),"missing command arg to -c switch");
X                            exit(5);
X                        }
X                        io_savestring(cmd);
X                        cmd_forceexit = 1;
X                        flag_interactive = 0;
X                        break;
X                    default:
X                        errmsg(0,LOC("main"),"bad argument: '%s'",argv[i]);
X                        exit(6);
X                }
X            }
X        }
X        var_setargs(argc-i+1,&argv[i-1]);
X    }
X
X    if (base_env.envfile != (char *)NULL)
X        io_pushfile(base_env.envfile,0,0,0);
X
X    while ((sp = lex_sentence(0)) != (struct phrase *)NULL)
X    {   /* get sentences until EOF */
X        if (base_env.prompts_issued[0] > 0 && base_env.history_size > 0 && sp->group != (struct phrase *)NULL)
X        {   /* stick onto history list */
X            if (base_env.history_list[base_env.history_size-1].cmd != (struct phrase *)NULL)
X                sentence_free(base_env.history_list[base_env.history_size-1].cmd);
X            for (i = base_env.history_size-1; i > 0; i--)
X            {   /* slide up the commands */
X                base_env.history_list[i].cmd = base_env.history_list[i-1].cmd;
X                base_env.history_list[i].number = base_env.history_list[i-1].number;
X            }
X            base_env.history_list[0].cmd = copy_group(sp->group,0,0,1);
X            base_env.history_list[0].number = base_env.prompts_issued[0];
X        }
X        rc = exec_sentence(sp,0);
X        flag = 1;
X        for (pp = sp->group; pp != (struct phrase *)NULL; pp = pp->next)
X        {   /* check for early termination */
X            if (pp->type == (struct token *)NULL)
X                flag = 0;
X        }
X        sentence_free(sp);
X        do_deletes();
X        if (!flag || rc == 1)
X            break;
X        cmd_count++;
X    }
X    force_signal("EXIT");
X
X    exit(cmd_lastrc);
X}   /* end of main */
X
X#ifndef GEMDOS
Xint main_fdget()
X{
X    register int i;
X
X    i = max_sysfd-1;
X    while (i >= 0)
X    {   /* look for unused fd */
X        if (!(open_sysfds[i/8] & (1<<(i%8))))
X        {   /* an unused fd? */
X            open_sysfds[i/8] |= (1<<(i%8));
X            return i;
X        }
X        i--;
X    }
X}   /* end of main_fdget */
X
Xint main_fdput(i)
Xint i;
X{
X    open_sysfds[i/8] &= ~(1<<(i%8));
X    return 0;
X}   /* end of main_fdput */
X#endif  /* GEMDOS */
X
Xint main_iopush()
X{
X    long fd;
X    int i;
X    register struct iostack *iop;
X#ifndef GEMDOS
X    int topfd,rc;
X#endif  /* GEMDOS */
X
X    iop = (struct iostack *)malloc(sizeof(*iop));
X    if (iop == (struct iostack *)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("main_iopush"));
X        return -1;
X    }
X    iop->next = base_env.io;
X    iop->input = iop->output = iop->errout = -1;
X    base_env.io = iop;
X
X#ifdef  GEMDOS
X#define FD_INP  0
X#define FD_OUT  1
X#define FD_AUX  2
X#define FD_PRN  3
X
X    if (base_env.fd_con < 0)
X    {   /* if console, default stderr not there yet? */
X        fd = Fopen("con:",2);
X        if (fd < 0)
X        {   /* all set up for console? */
X            iop->errout = FD_OUT;
X            errmsg(0,LOC("main_iopush"),"can't set up stderr");
X            return -1;
X        }
X        iop->errout = Fdup(fd);
X        if (iop->errout < 0)
X        {   /* all set up for console? */
X            iop->errout = FD_OUT;
X            errmsg(0,LOC("main_iopush"),"can't set up stderr");
X            return -1;
X        }
X        base_env.fd_con = iop->errout;
X    }
X    else
X    {   /* just dup currently setup stderr */
X        iop->errout = Fdup(2);
X        if (iop->errout < 0)
X        {   /* all set up for stderr? */
X            iop->errout = base_env.fd_con;
X            errmsg(0,LOC("main_iopush"),"can't set up stderr");
X            return -1;
X        }
X    }
X    iop->input = Fdup(FD_INP);
X    if (iop->input < 0)
X    {   /* can we get a new stdin? */
X        errmsg(0,LOC("main_iopush"),"can't setup stdin for use");
X        return -1;
X    }
X    iop->output = Fdup(FD_OUT);
X    if (iop->output < 0)
X    {   /* can we get a new stdout? */
X        errmsg(0,LOC("main_iopush"),"can't setup stdout for use");
X        return -1;
X    }
X    if (base_env.fd_aux < 0)
X    {   /* got ptr to aux: */
X        base_env.fd_aux = Fdup(FD_AUX);
X        if (base_env.fd_aux < 0)
X        {   /* can we save the aux: handle? */
X            errmsg(0,LOC("main_iopush"),"can't save aux:");
X            return -1;
X        }
X    }
X    if (base_env.fd_prn < 0)
X    {   /* get ptr to prn: */
X        base_env.fd_prn = Fdup(FD_PRN);
X        if (base_env.fd_prn < 0)
X        {   /* can we save the prn: handle? */
X            errmsg(0,LOC("main_iopush"),"can't save prn:");
X            return -1;
X        }
X    }
X    for (i = 0; i <= MAXUFD; i++)
X        Fclose(i);
X#else
X    topfd = main_fdget();
X    if ((rc = dup2(2,topfd)) < 0)
X    {   /* set up standard error? */
X        if (iop->next != (struct iostack *)NULL)
X            iop->errout = iop->next->errout;
X        else iop->errout = 2;
X        errmsg(0,LOC("main_iopush"),"can't setup stderr rc=%d,errno=%d",rc,errno);
X        return -1;
X    }
X    iop->errout = topfd;
X    fcntl(iop->errout,F_SETFD,1);
X    topfd = main_fdget();
X    if ((rc = dup2(1,topfd)) < 0)
X    {   /* set up standard output? */
X        errmsg(0,LOC("main_iopush"),"can't setup stdout rc=%d,errno=%d",rc,errno);
X        return -1;
X    }
X    iop->output = topfd;
X    fcntl(iop->output,F_SETFD,1);
X    topfd = main_fdget();
X    if ((rc = dup2(0,topfd)) < 0)
X    {   /* set up standard input? */
X        errmsg(0,LOC("main_iopush"),"can't setup stdin rc=%d,errno=%d",rc,errno);
X        return -1;
X    }
X    iop->input = topfd;
X    fcntl(iop->input,F_SETFD,1);
X
X    for (i = 0; i < MAXUFD; i++)
X        close(i);
X#endif  /* GEMDOS */
X    return 0;
X}   /* end of main_iopush */
X
Xint main_iopop()
X{
X    register struct iostack *iop;
X
X    iop = base_env.io;
X    base_env.io = iop->next;
X
X#ifdef  GEMDOS
X    Fclose(iop->input);
X    Fclose(iop->output);
X    Fclose(iop->errout);
X#else
X    if (iop->input != -1)
X    {   /* close it down */
X        close(iop->input);
X        main_fdput(iop->input);
X    }
X    if (iop->output != -1)
X    {   /* close it down */
X        close(iop->output);
X        main_fdput(iop->output);
X    }
X    if (iop->errout != -1)
X    {   /* close it down */
X        close(iop->errout);
X        main_fdput(iop->errout);
X    }
X#endif  /* GEMDOS */
X
X    free(iop);
X    return 0;
X}   /* end of main_iopop */
X
Xint main_dirpush()
X{
X    register struct dirstack *dp;
X
X    dp = (struct dirstack *)malloc(sizeof(*dp));
X    if (dp == (struct dirstack *)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("main_dirpush"));
X        return -1;
X    }
X    dp->next = base_env.dir;
X    base_env.dir = dp;
X    dp->current = (char *)NULL;
X    return 0;
X}   /* end of main_dirpush */
X
Xint main_dirpop()
X{
X    int rc;
X    char *newdir;
X    register struct dirstack *dp;
X
X    dp = base_env.dir;
X    base_env.dir = dp->next;
X
X    newdir = (char *)NULL;
X    if (base_env.dir != (struct dirstack *)NULL)
X    {   /* if something to go back to */
X        rc = lchdir(base_env.dir->current,&newdir);
X        if (rc == 0)
X        {   /* if we got back to old dir */
X            var_define0("PWD",newdir,0);
X            free(newdir);
X        }
X    }
X    if (dp->current != (char *)NULL)
X        free(dp->current);
X    free(dp);
X    return rc;
X}   /* end of main_dirpop */
X
Xint main_varpush()
X{
X    register struct varstack *vs;
X
X    vs = (struct varstack *)malloc(sizeof(struct varstack));
X    if (vs == (struct varstack *)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("main_varpush"));
X        return -1;
X    }
X    vs->table = (struct variable *)NULL;
X    vs->next = base_env.var;
X    base_env.var = vs;
X    return 0;
X}   /* end of main_varpush */
X
Xint main_varpop()
X{
X    register struct varstack *vs;
X
X    if (base_env.var == (struct varstack *)NULL)
X        return 1;
X    vs = base_env.var;
X    base_env.var = vs->next;
X
X    var_tablefree(vs->table);
X    free(vs);
X    return 0;
X}   /* end of main_varpop */
X
Xstruct argstack
X{
X    int     argc;
X    char    **argv;
X    struct  argstack *next;
X};
Xstatic struct argstack *argtop = (struct argstack *)NULL;
X
Xint main_argspush(pp)
Xstruct phrase *pp;
X{
X    register struct argstack *as;
X
X    as = (struct argstack *)malloc(sizeof(*as));
X    if (as == (struct argstack *)NULL)
X    {   /* enough memory? */
X        errmsg(SHERR_NOMEM,LOC("main_argspush"));
X        return -1;
X    }
X    as->next = argtop;
X    argtop = as;
X    as->argc = var_argc;
X    as->argv = var_argv;
X    var_argc = 0;
X    var_argv = (char **)NULL;
X    if (pp->body != (struct token *)NULL)
X        var_resetargs(pp->body->next);
X    return 0;
X}   /* end of main_argspush */
X
Xint main_argspop()
X{
X    register int i;
X
X    if (argtop == (struct argstack *)NULL)
X        return 1;
X    for (i = 1; i < var_argc; i++)
X        if (var_argv[i] != (char *)NULL)
X            free(var_argv[i]);
X    if (var_argv != (char **)NULL)
X        free(var_argv);
X    var_argc = argtop->argc;
X    var_argv = argtop->argv;
X    argtop = argtop->next;
X    return 0;
X}   /* end of main_argspop */
END_OF_FILE
if test 16634 -ne `wc -c <'main.c'`; then
    echo shar: \"'main.c'\" unpacked with wrong size!
fi
# end of 'main.c'
fi
if test -f 'reader/editing.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'reader/editing.c'\"
else
echo shar: Extracting \"'reader/editing.c'\" \(18659 characters\)
sed "s/^X//" >'reader/editing.c' <<'END_OF_FILE'
X/*
X * Various editing subroutines for internal use by the "reader" package
X *
X * Dave Clemans, 4/84 (initial version); 1/89 (more generality)
X *
X * $Id: editing.c,v 1.2 89/02/20 20:20:06 dclemans Exp $
X *
X * $Log:	editing.c,v $
X * Revision 1.2  89/02/20  20:20:06  dclemans
X * Add RCS identifiers
X * 
X */
X#include <ctype.h>
X#ifndef GEMDOS
X#include <sgtty.h>
X#endif  GEMDOS
X#ifndef LOC
X#define LOC(r)  __FILE__,r,__LINE__
X#endif  LOC
X
X#include "history.h"
X
X/*
X * Interesting function definitions
X */
Xextern char *substr();
X
X/*
X * Is this character printable?  ...  i.e., would we want to print it
X * directly or generate a translation of it.
X */
Xstatic int printable(c)
Xregister char c;
X{
X	if (isprint(c) || (c == '\n') || (c == '\r') || (c == '\t'))
X		return(1);
X	return(0);
X};	/* end of printable */
X
X/*
X * How much space will this character take up on the screen?
X */
Xstatic int charwidth(c,ttycursor,cursor,size)
Xregister char c;
Xregister int ttycursor;
X{
X	register int width;
X
X	if (cursor == size)
X		width = 1;
X	else if (!printable(c))
X		width = 2;
X        else if (c == '\r')
X                width = 0;
X	else if (c != '\t')
X		width = 1;
X	else
X	{	/* expand a tab; tab stop every 8 spaces */
X		width = 1;
X		while ((ttycursor+width) & 07)
X			width++;
X	}
X	return(width);
X};	/* end of charwidth */
X
X/*
X * What is the on-screen width of the line up to given character?
X */
Xstatic int linewidth(index)
Xregister int index;
X{
X	register int counter,width;
X
X	width = 0;
X	for (counter = 0; counter < index; counter++)
X	{	/* find tty width of line before tab */
X		width += charwidth(History.currentLine->contents[counter],width,
X			History.currentLine->cursor,History.currentLine->size);
X	}
X	return(width);
X};	/* end of linewidth */
X
X/*
X * Go back a character on the display
X */
Xstatic backchar()
X{
X	register char c;
X	register int counter,width;
X
X	counter = History.currentLine->cursor-1;
X	if (counter < 0)	/* already all the way back */
X		return;
X	c = History.currentLine->contents[counter];
X	History.currentLine->cursor--;
X
X        if (c != '\r')
X		_writechar('\b');
X	History.currentLine->ttycursor--;
X
X	if (c != '\t')
X	{	/* only tabs handled specially here */
X		return;
X	}
X
X	width = linewidth(counter);
X
X	while (History.currentLine->ttycursor & 07)	/* tab=8 spaces */
X	{	/* go back to tab stop */
X		if (width >= History.currentLine->ttycursor)
X			break;
X		_writechar('\b');
X		History.currentLine->ttycursor--;
X	}
X};	/* end of backchar */
X
X/*
X * Echo a character; if non-printing print as ^c
X */
X_echochar(c)
Xregister char c;
X{
X	History.currentLine->ttycursor++;
X	if (printable(c))
X	{	/* normal stuff for printable characters */
X		if (c == '\t')
X		{	/* special stuff for tab */
X			_writechar(' ');
X			while (History.currentLine->ttycursor & 07) /* tab=8 spaces */
X			{	/* expand a tab */
X				_writechar(' ');
X				History.currentLine->ttycursor++;
X			}
X		}
X		else if (c == '\r')
X		    History.currentLine->ttycursor--;
X       		else	_writechar(c);
X	}
X	else
X	{	/* special handling for non-printables */
X		_writechar('^');
X		if (c == '\177')
X			_writechar('?');
X		else	_writechar((char)((int)c | 0100));
X		History.currentLine->ttycursor++;
X	}
X};	/* end of _echochar */
X
X/*
X * Do a standard character erase
X */
X_doStandardErase(c)
Xchar c;
X{
X	register int i,width;
X
X	if (History.currentLine->cursor > 0)
X	{	/* kill a character */
X#ifndef GEMDOS
X		if (_savedState.localAttributes & LCRTBS)
X		{	/* crt style erase */
X#endif  GEMDS
X			i = 1;
X			if (!printable(History.currentLine->contents[History.currentLine->cursor-1]))
X				i = 2;
X			for (; i > 0; --i)
X			{	/* start erasing */
X				backchar();
X				_writechar(' ');
X				_writechar('\b');
X				if (i > 1)
X					History.currentLine->cursor++;
X			}
X#ifndef GEMDOS
X		}
X		else if (_savedState.localAttributes & LPRTERA)
X		{	/* paper style erase */
X			_writechar('/');
X			_echochar(History.currentLine->contents[History.currentLine->cursor-1]);
X			_writechar('/');
X		}
X		else	backchar();
X#endif  GEMDOS
X		History.currentLine->cursor++;
X		if (History.currentLine->cursor == History.currentLine->size)
X		{	/* cursor at end of line */
X			History.currentLine->size--;
X			History.currentLine->cursor--;
X		}
X		else if (History.currentLine->cursor > History.currentLine->size)
X		{	/* line buffer screwed up */
X			errmsg(0,LOC("doStandardErase"),"line buffer screwed up");
X			if (_savedState.stream >= 0)
X				_terminate(_savedState.stream);
X			_savedState.stream = -1;
X			exit(-1);
X		}
X		else
X		{	/* else take out from middle of line */
X			strncpy(&History.currentLine->contents[History.currentLine->cursor-1],
X				&History.currentLine->contents[History.currentLine->cursor],
X				History.currentLine->size-History.currentLine->cursor);
X			History.currentLine->cursor--;
X			History.currentLine->size--;
X			width = History.currentLine->ttycursor;
X			_doStandardEndLine(c);
X			_writechar(' ');
X			_writechar('\b');
X			while (History.currentLine->ttycursor > width)
X				_doStandardBackspace(c);
X		}
X	}
X};	/* end of _doStandardErase */
X
X/*
X * Do a standard line kill
X */
X_doStandardKill(c)
Xchar c;
X{
X	register int i,j;
X	char cc;
X
X	_doStandardEndLine('\000');
X#ifndef GEMDOS
X	if (_savedState.localAttributes & LCRTERA)
X	{	/* crt style kill */
X#endif  GEMDOS
X		for (i = History.currentLine->size; i > 0; i--)
X		{	/* erase the line */
X			j = 1;
X			cc = History.currentLine->contents[i-1];
X			if (!printable(cc))
X				j = 2;
X			for (; j > 0; --j)
X			{	/* start killing */
X				backchar();
X				_writechar(' ');
X				_writechar('\b');
X				if (j > 1)
X					History.currentLine->cursor++;
X			}
X		}
X#ifndef GEMDOS
X	}
X	else if (_savedState.localAttributes & LPRTERA)
X	{	/* printing style kill */
X		if (c)
X			_echochar(c);
X		_writechar('\n');
X	}
X	else
X	{	/* go to start of line */
X		for (i = History.currentLine->size; i > 0; i--)
X                {
X			backchar();
X                }
X	}
X#endif  GEMDOS
X	History.currentLine->size = 0;
X	History.currentLine->cursor = 0;
X	History.currentLine->ttycursor = 0;
X};	/* end of _doStandardKill */
X
X/*
X * Retype the line
X */
X_doStandardRetype(c)
Xchar c;
X{
X	register int i,cursor;
X
X	cursor = History.currentLine->cursor;
X	if (c)
X	{	/* if a user typed character */
X		_doStandardEndLine(c);
X		_echochar(c);
X		_writechar('\n');
X	}
X	History.currentLine->ttycursor = 0;
X	for (i = 0; i < History.currentLine->size; i++)
X	{	/* reprint the line */
X		_echochar(History.currentLine->contents[i]);
X	}
X	_doStandardStartLine(c);
X	while (History.currentLine->cursor < cursor)
X		_doStandardSpace(c);
X};	/* end of _doStandardRetype */
X
X/*
X * Do unambigous command/filename expansion for last word in current line
X */
X_doStandardExpand(c)
Xchar c;
X{
X	register int rc,i;
X
X	History.currentLine->contents[History.currentLine->size] = '\0';
X	rc = ExpandAName(History.currentLine->contents,
X		sizeof(History.currentLine->contents)-1,
X		History.currentLine->size, 1);
X	i = History.currentLine->size;
X	while (History.currentLine->contents[i])
X	{	/* enter the characters in as input */
X		_savechar(History.currentLine->contents[i++]);
X	}
X	if (rc != 1)
X	{	/* if ambigous or unknown */
X		_writechar('\007');
X	}
X};	/* end of _doStandardExpand */
X
X/*
X * Insert or append a character to the current line
X */
X_doStandardCharacter(c)
Xchar c;
X{
X	register int counter,width;
X	char buffer[HISTORY_BUFFER];
X
X	if (History.currentLine->cursor == History.currentLine->size)
X	{	/* append to line */
X		_echochar(c);
X		History.currentLine->contents[History.currentLine->size] = c;
X		History.currentLine->size++;
X		History.currentLine->cursor++;
X	}
X	else if (History.currentLine->cursor > History.currentLine->size)
X	{	/* line buffer screwed up */
X		errmsg(0,LOC("doStandardCharacter"),"line buffer screwed up");
X		if (_savedState.stream >= 0)
X			_terminate(_savedState.stream);
X		_savedState.stream = -1;
X		exit(-1);
X	}
X	else
X	{	/* insert into line */
X		strncpy(buffer,History.currentLine->contents,History.currentLine->cursor);
X		buffer[History.currentLine->cursor] = c;
X		strncpy(&buffer[History.currentLine->cursor+1],
X			&History.currentLine->contents[History.currentLine->cursor],
X			History.currentLine->size-History.currentLine->cursor);
X		strncpy(History.currentLine->contents,buffer,History.currentLine->size+1);
X		History.currentLine->size++;
X		History.currentLine->cursor++;
X		_echochar(c);
X		width = History.currentLine->ttycursor;
X		for (counter = History.currentLine->cursor; counter < History.currentLine->size; counter++)
X		{	/* repaint rest of line */
X			_echochar(History.currentLine->contents[counter]);
X			History.currentLine->cursor++;
X		}
X		while (History.currentLine->ttycursor > width)
X			_doStandardBackspace('\0');
X	}
X};	/* end of _doStandardCharacter */
X
X/*
X * Delete next character in line
X */
X_doStandardDelete(c)
Xchar c;
X{
X	register int counter,width;
X
X	if ((History.currentLine->cursor >= 0) &&
X	    (History.currentLine->cursor < History.currentLine->size))
X	{	/* something there to delete */
X		width = linewidth(History.currentLine->cursor);
X		strncpy(&History.currentLine->contents[History.currentLine->cursor],
X			&History.currentLine->contents[History.currentLine->cursor+1],
X			History.currentLine->size-History.currentLine->cursor-1);
X		History.currentLine->size--;
X		width = History.currentLine->ttycursor;
X		_doStandardEndLine(c);
X		_writechar(' ');
X		_writechar('\b');
X		while (History.currentLine->ttycursor > width)
X			_doStandardBackspace(c);
X	}
X};	/* end of _doStandardDelete */
X
X/*
X * Move the cursor back
X */
X_doStandardBackspace(c)
Xchar c;
X{
X	if (History.currentLine->cursor > 0)
X	{	/* if something to backspace over */
X		backchar();
X	}
X};	/* end of _doStandardBackspace */
X
X/*
X * Move the cursor forward
X */
X_doStandardSpace(c)
Xchar c;
X{
X	if (History.currentLine->cursor < History.currentLine->size)
X	{	/* if something to go forward over */
X		_echochar(History.currentLine->contents[History.currentLine->cursor]);
X		History.currentLine->cursor++;
X	}
X};	/* end of _doStandardSpace */
X
X/*
X * Get an older line from the history list and clobber the current line
X * in the history list with it.
X */
X_doStandardPrevLine(c)
Xchar c;
X{
X	register struct historyLine *hp;
X
X	hp = History.listPointer->next;
X	if (hp && hp->command)
X	{	/* there's an older line there */
X		_doStandardKill('\0');
X		strncpy(History.currentLine->contents,hp->contents,hp->size);
X		History.currentLine->size = hp->size-1;
X		History.currentLine->cursor = hp->size-1;
X		_doStandardRetype('\0');
X		History.listPointer = hp;
X	}
X};	/* end of _doStandardPrevLine */
X
X/*
X * Get a newer line from the history list and clobber the current line
X * in the history list with it.
X */
X_doStandardNextLine(c)
Xchar c;
X{
X	register struct historyLine *hp;
X
X	for (hp = History.listTop; hp && (hp->next != History.listPointer); hp = hp->next)
X		continue;
X	if (hp && (hp != History.currentLine))
X	{	/* if a newer line exists */
X		_doStandardKill('\0');
X		strncpy(History.currentLine->contents,hp->contents,hp->size);
X		History.currentLine->size = hp->size-1;
X		History.currentLine->cursor = hp->size-1;
X		_doStandardRetype('\0');
X		History.listPointer = hp;
X	}
X};	/* end of _doStandardNextLine */
X
X/*
X * Goto the beginning of the line
X */
X_doStandardStartLine(c)
Xchar c;
X{
X	while (History.currentLine->cursor > 0)
X	{	/* while stuff to space over */
X		backchar();
X	}
X};	/* end of _doStandardStartLine */
X
X
X/*
X * Goto the end of the line
X */
X_doStandardEndLine(c)
Xchar c;
X{
X	while (History.currentLine->cursor < History.currentLine->size)
X	{	/* if something to go forward over */
X		_echochar(History.currentLine->contents[History.currentLine->cursor]);
X		History.currentLine->cursor++;
X	}
X};	/* end of _doStandardEndLine */
X
X/*
X * Capitalize the current character
X */
X_doStandardCapitalize(c)
Xchar c;
X{
X	register int cursor;
X
X	cursor = History.currentLine->cursor;
X	if (cursor == History.currentLine->size)
X		return;		/* no character at end of line */
X	if (islower(History.currentLine->contents[cursor]))
X		History.currentLine->contents[cursor] =
X		    toupper(History.currentLine->contents[cursor]);
X	_echochar(History.currentLine->contents[cursor]);
X	History.currentLine->cursor++;
X	backchar();
X	History.currentLine->cursor--;
X};	/* end of _doStandardCapitalize */
X
X/*
X * Transpose the current and next characters
X */
X_doStandardTranspose(c)
Xchar c;
X{
X	register char cc;
X	register int cursor;
X
X	cursor = History.currentLine->cursor;
X	if (cursor == History.currentLine->size)
X		return;		/* nothing to transpose */
X	cc = History.currentLine->contents[cursor];
X	if (cursor+1 < History.currentLine->size)
X	{	/* if room for two chars */
X		History.currentLine->contents[cursor]=
X			History.currentLine->contents[cursor+1];
X		History.currentLine->contents[cursor+1] = cc;
X		_echochar(History.currentLine->contents[cursor]);
X		History.currentLine->cursor++;
X		_echochar(cc);
X		backchar();
X		History.currentLine->cursor--;
X		backchar();
X	}
X	else
X	{	/* else no; treat end of line like space */
X		History.currentLine->contents[cursor] = ' ';
X		_echochar(' ');
X		History.currentLine->cursor++;
X		_doStandardCharacter(cc);
X		_doStandardBackspace(c);
X		backchar();
X		History.currentLine->cursor--;
X	}
X};	/* end of _doStandardTranspose */
X
X/*
X * Search lines backwards in time for a string.
X * If found, set global History.listPointer to point to it.
X * The search starts at History.listPointer.
X * If string not found, History.listPointer is not changed.
X * Returns index of start of search string if found, -1 if not found.
X */
Xstatic int backsearchfor(string)
Xchar *string;
X{
X	register struct historyLine *hp;
X	register char *cp;
X
X	cp = (char *)NULL;
X	for (hp = History.listPointer->next; hp; hp = hp->next)
X	{	/* start searching */
X		hp->contents[hp->size] = '\0';
X		cp = substr(hp->contents,string);
X		if (cp)
X			break;
X	}
X	if (!cp || !hp)
X		return(-1);
X	History.listPointer = hp;
X	return((int)cp - (int)hp->contents);
X};	/* end of backsearchfor */
X
X/*
X * Search lines forwards in time for a string.
X * If found, set global History.listPointer to point to it.
X * The search starts at History.listPointer.
X * If string not found, History.listPointer is not changed.
X * Returns index of start of search string if found, -1 if not found.
X */
Xstatic int searchfor(string)
Xchar *string;
X{
X	register struct historyLine *hp,*bp;
X	register char *cp;
X
X	cp = (char *)NULL;
X	for (hp = History.listPointer; hp && (hp != History.listTop); )
X	{	/* start searching */
X		for (bp = History.listTop; bp && (bp->next != hp); bp = bp->next)
X			continue;
X		if (!bp || (bp == History.listTop))
X			break;
X		hp = bp;
X		hp->contents[hp->size] = '\0';
X		cp = substr(hp->contents,string);
X		if (cp)
X			break;
X	}
X	if (!cp || !hp)
X		return(-1);
X	History.listPointer = hp;
X	return((int)cp - (int)hp->contents);
X};	/* end of searchfor */
X
X/*
X * Simple read a line routine; just turn on normal tty mode; read a
X * line with normal tty editing; and put the tty modes back.
X */
Xstatic readline(buffer,size)
Xregister char *buffer;
Xregister int size;
X{
X	register int cursor;
X	register int c;
X#ifndef GEMDOS
X	struct sgttyb ttymodes;
X#endif  GEMDOS
X
X	buffer[0] = '\0';
X	if (_savedState.stream < 0)
X		return;
X#ifndef GEMDOS
X	ttymodes = _savedState.basicAttributes;
X	ttymodes.sg_flags &= ~ECHO;
X	ioctl(_savedState.stream, TIOCSETP, &ttymodes);
X#endif  GEMDOS
X	for (cursor = 0; cursor < size; )
X	{	/* start filling the buffer till cr or nl */
X		c = _readchar(_savedState.stream);
X		if ((c == '\n') || (c == '\r') || (c == EOF))
X			break;
X		buffer[cursor++] = c;
X	}
X	buffer[cursor] = '\0';
X#ifndef GEMDOS
X	ioctl(_savedState.stream, TIOCSETP, &_newState.basicAttributes);
X#endif  GEMDOS
X};	/* end of readline */
X
Xstatic char searchString[HISTORY_BUFFER];
X
X/*
X * Search for older lines from the history list and clobber the current line
X * in the history list with it.
X */
X_doStandardSrchPrevLine(c,flag)
Xchar c;
Xint flag;
X{
X	register struct historyLine *hp;
X	register int cursor;
X	char search[HISTORY_BUFFER];
X
X	search[0] = '\0';
X	if (!flag)
X		readline(search,sizeof search-1);
X	if (!search[0])
X		strcpy(search,searchString);
X	if (!search[0])
X	{	/* nothing to search for */
X		_writechar('\007');
X		return;
X	}
X	cursor = backsearchfor(search);
X	if (cursor < 0)
X		return;
X	hp = History.listPointer;
X	if (hp)
X	{	/* there's an older line there */
X		_doStandardKill('\0');
X		strncpy(History.currentLine->contents,hp->contents,hp->size);
X		History.currentLine->size = hp->size-1;
X		History.currentLine->cursor = hp->size-1;
X		_doStandardRetype('\0');
X		History.listPointer = hp;
X	}
X	strcpy(searchString,search);
X};	/* end of _doStandardSrchPrevLine */
X
X/*
X * Search for newer lines from the history list and clobber the current line
X * in the history list with it.
X */
X_doStandardSearchNextLine(c,flag)
Xchar c;
Xint flag;
X{
X	register struct historyLine *hp;
X	register int cursor;
X	char search[HISTORY_BUFFER];
X
X	search[0] = '\0';
X	if (!flag)
X		readline(search,sizeof search-1);
X	if (!search[0])
X		strcpy(search,searchString);
X	if (!search[0])
X	{	/* nothing to search for */
X		_writechar('\007');
X		return;
X	}
X	cursor = searchfor(search);
X	if (cursor < 0)
X		return;
X	hp = History.listPointer;
X	if (hp)
X	{	/* if a newer line exists */
X		_doStandardKill('\0');
X		strncpy(History.currentLine->contents,hp->contents,hp->size);
X		History.currentLine->size = hp->size-1;
X		History.currentLine->cursor = hp->size-1;
X		_doStandardRetype('\0');
X		History.listPointer = hp;
X	}
X	strcpy(searchString,search);
X};	/* end of _doStandardSearchNextLine */
X
X/*
X * move to a given position in the current line
X */
Xstatic moveto(position)
Xregister int position;
X{
X	if (position < 0)
X		position = 0;
X	else if (position > History.currentLine->size)
X		position = History.currentLine->size;
X	if (History.currentLine->cursor > position)
X	{	/* move backwards */
X		while (History.currentLine->cursor > position)
X			_doStandardBackspace('\0');
X	}
X	else if (History.currentLine->cursor < position)
X	{	/* move forwards */
X		while (History.currentLine->cursor < position)
X			_doStandardSpace('\0');
X	}
X};	/* end of moveto */
X
X/*
X * delete a given range of characters
X */
X_deleteTo(start,stop)
Xregister int start,stop;
X{
X	register int count;
X	register int savecursor;
X
X	if (start < 0)
X		start = 0;
X	else if (start > History.currentLine->size)
X		start = History.currentLine->size;
X	if (stop < 0)
X		stop = 0;
X	else if (stop > History.currentLine->size)
X		stop = History.currentLine->size;
X	savecursor = History.currentLine->cursor;
X
X	_pushOnKillRing(start,stop);
X
X	count = stop-start+1;
X	if (count <= 0)
X		return;
X
X	moveto(start);
X	for (; count > 0; --count)
X	{	/* start deleting characters */
X		_doStandardDelete('\0');
X	}
X	moveto(savecursor);
X};	/* end of _deleteTo */
X
X/*
X * Delete from current position to end of line
X */
X_doStandardDelEndLine(c)
Xchar c;
X{
X	_deleteTo(History.currentLine->cursor,History.currentLine->size);
X};	/* end of _doStandardDelEndLine */
END_OF_FILE
if test 18659 -ne `wc -c <'reader/editing.c'`; then
    echo shar: \"'reader/editing.c'\" unpacked with wrong size!
fi
# end of 'reader/editing.c'
fi
if test -f 'reader/reader.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'reader/reader.c'\"
else
echo shar: Extracting \"'reader/reader.c'\" \(17920 characters\)
sed "s/^X//" >'reader/reader.c' <<'END_OF_FILE'
X/*
X * reader	-	read line with editing
X *
X * Dave Clemans, 4/84 (first version); 1/89 more generality
X *
X * This module in a sense is a superset of 'fgets' in that it reads a
X * line at a time from a file stream.  However, it is also a lot more.
X *
X * Depending on the value of the "EDITOR" environment variable, reader
X * provides 'vi' or 'emacs' style input editing.
X *
X * Reader keeps a history of the lines that it has seen.  The history
X * list can be referenced and its size changed through global variables.
X * There is an alternate point to reader (editor) that also asks for
X * a line from the user; except that the buffer starts out with the
X * contents of some line from the history list instead of being empty.
X *
X * External entry points:
X *	Reader		- the 'reader' get a line routine
X *	Editor		- the 'editor' edit a line routine
X *
X * $Id: reader.c,v 1.7 89/03/07 19:37:36 dclemans Exp $
X *
X * $Log:	reader.c,v $
X * Revision 1.7  89/03/07  19:37:36  dclemans
X * make work on bsd systems
X * 
X * Revision 1.6  89/02/20  20:32:33  dclemans
X * More RCS ID work
X * 
X * Revision 1.5  89/02/20  20:20:23  dclemans
X * Add RCS identifiers
X * 
X */
Xchar reader_version[] = "Reader Version 0.2 (dgc); $Date: 89/03/07 19:37:36 $";
X
X#include <ctype.h>
X#include <strings.h>
X#ifdef	GEMDOS
X#include <osbind.h>
X#define	index	strchr
X#define	rindex	strrchr
X#else
X#include <sys/types.h>
X#include <sgtty.h>
X#include <signal.h>
X#endif	GEMDOS
X#include <errno.h>
X#ifndef LOC
X#define LOC(r)  __FILE__,r,__LINE__
X#endif  LOC
X
X#include "shell.h"
X#define DEFAULT_MAXSIZE DEFAULT_HISTORY
X#include "history.h"
Xstruct History History;
Xstruct savedState _savedState;
Xstruct newState _newState;
X
X#ifndef DIR_SEPARATOR
X#ifndef GEMDOS
X#define DIR_SEPARATOR '/'
X#else
X#define DIR_SEPARATOR '\\'
X#endif  GEMDOS
X#endif  DIR_SEPARATOR
X
X/*
X * Interesting function declarations
X */
Xextern char *var_normal();
Xextern int wild_match();
X
X/*
X * For saving pending input during terminal mode changes
X */
Xstatic struct	pendingInput
X{
X	char buffer[BUFSIZ];
X	int size;
X	int next;
X} pendingInput;
X
X#ifndef GEMDOS
X/*
X * For restoring things and exiting after catching a signal
X */
Xstatic catchSignal(sig)
Xint sig;
X{
X	_savechar(EOF);
X	signal(sig,catchSignal);
X};	/* end of catchSignal */
X
X/*
X * Handle a suspend signal
X */
Xstatic catchSuspend()
X{
X	int stream;
X
X	stream = _savedState.stream;
X	if (stream >= 0)
X		_terminate(stream);
X	signal(SIGTSTP,SIG_DFL);
X	killpg(getpgrp(getpid()),SIGTSTP);
X	if (stream >= 0)
X		initialize(stream);
X};	/* end of catchSuspend */
X#endif  GEMDOS
X
X/*
X * Initialize the users environment and terminal attributes to match
X * our needs.
X *
X * Before we change anything, state is saved.
X *
X * We do control character munging so that we can see characters that
X * CBREAK mode would otherwise hide from us.
X */
Xstatic initialize(fp)
Xregister int fp;
X{
X#ifndef GEMDOS
X	register int rc;
X
X	if (!isatty(fp))
X		return;
X#endif  GEMDOS
X	_savedState.stream = fp;
X
X#ifndef GEMDOS
X	/*
X	 * Get the initial state
X	 */
X	ioctl(fp, TIOCGETP, &_savedState.basicAttributes);
X	ioctl(fp, TIOCGETC, &_savedState.basicCharacters);
X	ioctl(fp, TIOCLGET, &_savedState.localAttributes);
X	ioctl(fp, TIOCGLTC, &_savedState.localCharacters);
X	_savedState.sigint = (int)signal(SIGINT,SIG_IGN);
X	_savedState.sigquit = (int)signal(SIGQUIT,SIG_IGN);
X	_savedState.sigtstp = (int)signal(SIGTSTP,SIG_IGN);
X	signal(SIGINT,_savedState.sigint);
X	signal(SIGQUIT,_savedState.sigquit);
X	signal(SIGTSTP,_savedState.sigtstp);
X	_newState.basicAttributes = _savedState.basicAttributes;
X	_newState.basicCharacters = _savedState.basicCharacters;
X	_newState.localAttributes = _savedState.localAttributes;
X	_newState.localCharacters = _savedState.localCharacters;
X
X	/*
X	 * We want to turn off all control characters
X	 */
X	_newState.basicAttributes.sg_erase = '\377';
X	_newState.basicAttributes.sg_kill = '\377';
X	if (_savedState.sigint == (int)SIG_IGN)
X		_newState.basicCharacters.t_intrc = '\377';
X	signal(SIGINT,catchSignal);
X	if (_savedState.sigquit == (int)SIG_IGN)
X		_newState.basicCharacters.t_quitc = '\377';
X	signal(SIGQUIT,catchSignal);
X	_newState.basicCharacters.t_startc = '\377';
X	_newState.basicCharacters.t_stopc = '\377';
X	_newState.basicCharacters.t_eofc = '\377';
X	_newState.basicCharacters.t_brkc = '\377';
X	if (_savedState.sigtstp == (int)SIG_IGN)
X	{	/* then can clobber suspension chars */
X		_newState.localCharacters.t_suspc = '\377';
X		_newState.localCharacters.t_dsuspc = '\377';
X	}
X	signal(SIGTSTP,catchSuspend);
X	_newState.localCharacters.t_rprntc = '\377';
X	_newState.localCharacters.t_flushc = '\377';
X	_newState.localCharacters.t_werasc = '\377';
X	_newState.localCharacters.t_lnextc = '\377';
X
X	/*
X	 * We want to use cbreak mode, no echo, etc.
X	 */
X	_newState.basicAttributes.sg_flags &= ~(RAW|ECHO);
X	_newState.basicAttributes.sg_flags |= CBREAK;
X
X	/*
X	 * Do all the mode changes
X	 */
X	ioctl(fp, TIOCSETN, &_newState.basicAttributes);
X	ioctl(fp, TIOCSETC, &_newState.basicCharacters);
X	ioctl(fp, TIOCLSET, &_newState.localAttributes);
X	ioctl(fp, TIOCSLTC, &_newState.localCharacters);
X#endif  GEMDOS
X};	/* end of initialize */
X
X/*
X * Reset the users environment and terminal attributes to where they
X * were when we were entered.  Typeahead (which would otherwise be
X * lost) is handled by re-inputting it after everything is in the
X * right state.
X */
X#ifndef GEMDOS
X_terminate(fp)
Xregister int fp;
X{
X	register int rc;
X	long available;
X	char c;
X
X	if (!isatty(fp))
X		return;
X
X	signal(SIGINT,_savedState.sigint);
X	signal(SIGQUIT,_savedState.sigquit);
X	signal(SIGTSTP,_savedState.sigtstp);
X
X	ioctl(fp, TIOCSETN, &_savedState.basicAttributes);
X	ioctl(fp, TIOCSETC, &_savedState.basicCharacters);
X	ioctl(fp, TIOCLSET, &_savedState.localAttributes);
X	ioctl(fp, TIOCSLTC, &_savedState.localCharacters);
X};	/* end of _terminate */
X#else
X_terminate()
X{
X};	/* end of _terminate */
X#endif  GEMDOS
X
X/*
X * Get a line to use for an input buffer from the history list
X *
X * Returns it by setting the global History.currentLine
X */
Xstatic getHistoryBuffer()
X{
X	register int counter;
X	register struct historyLine *hp,*bottom;
X
X	if (!History.listTop)
X		History.maxSize = DEFAULT_MAXSIZE;
X	if (History.maxSize <= 0)
X		History.maxSize = 1;
X	if (History.currentSize > History.maxSize)
X	{	/* user has shrunk the stack; so free up the excess entries */
X		bottom = (struct historyLine *)NULL;
X		for (counter = 0,hp = History.listTop;
X		    hp; hp = hp->next,counter++)
X		{	/* go through what we will keep */
X			if (counter == (History.maxSize-1))
X			{	/* set new end of list */
X				bottom = hp;
X                                hp = hp->next;
X                                break;
X			}
X		}
X		if (bottom)
X		{	/* set new end of list */
X			History.listBottom = bottom;
X			bottom->next = (struct historyLine *)NULL;
X		}
X		for (; hp; )
X		{	/* free up excess entries */
X			bottom = hp;
X			hp = hp->next;
X			free(bottom);
X		}
X	}
X
X	if (History.currentLine && (!History.currentLine->size ||
X	    ((History.currentLine->size == 1) &&
X	    ((History.currentLine->contents[0] == '\n') ||
X	    (History.currentLine->contents[0] == '\r')))))
X	{	/* empty entry at top; so use it */
X		/* History.currentLine->command = ++History.currentCommand; leave same */
X		History.listPointer = History.currentLine;
X		History.currentLine->cursor = 0;
X		History.currentLine->size = 0;
X		History.currentLine->ttycursor = 0;
X		History.currentLine->contents[0] = '\0';
X		return;
X	}
X
X	if (History.currentSize < History.maxSize)
X	{	/* more room available; make new entry */
X		while (History.currentSize < History.maxSize)
X		{	/* make all new entries at once */
X			History.currentSize++;
X			hp = (struct historyLine *)malloc(sizeof(*hp));
X			if (!hp)
X			{	/* out of core */
X				errmsg(-1,LOC("getHistoryBuffer"));
X				exit(-1);
X			}
X			hp->contents[0] = '\0';
X			hp->size = 0;
X			hp->cursor = 0;
X			hp->ttycursor = 0;
X			hp->command = 0;
X			hp->next = (struct historyLine *)NULL;
X			if (!History.listTop)
X				History.listTop = History.listBottom = hp;
X			else
X			{	/* push onto top */
X				hp->next = History.listTop;
X				History.listTop = hp;
X			}
X		}
X		History.currentLine = hp;
X		History.listPointer = History.currentLine;
X		hp->command = ++History.currentCommand;
X		return;
X	}
X
X	/* Take entry from bottom and put on top */
X	hp = History.listBottom;
X	for (bottom = History.listTop; bottom && (bottom->next != hp); bottom = bottom->next)
X		continue;
X	if (!bottom)
X	{	/* list screwed up */
X		errmsg(0,LOC("getHistoryBuffer"),"screwed up history list");
X		exit(-1);
X	}
X	History.listBottom = bottom;
X	bottom->next = (struct historyLine *)NULL;
X	hp->next = History.listTop;
X	History.listTop = hp;
X	History.currentLine = hp;
X	History.listPointer = hp;
X	hp->size = 0;
X	hp->cursor = 0;
X	hp->ttycursor = 0;
X	hp->contents[0] = '\0';
X	hp->command = ++History.currentCommand;
X};	/* end of getHistoryBuffer */
X
X/*
X * Get the next user input character; if pending input take from that
X * else get it from the system.
X */
Xint _readchar(fp)
Xregister int fp;
X{
X	extern errno;
X	char c;
X    int cc;
X	register int rc;
X
X	if (pendingInput.size > 0)
X	{	/* take from already read in chars */
X		if (pendingInput.next != pendingInput.size)
X		{	/* if something was already there */
X			c = pendingInput.buffer[pendingInput.next++];
X			c &= 0x7f;
X			if (pendingInput.next == pendingInput.size)
X				pendingInput.next = pendingInput.size = 0;
X			return(c);
X		}
X	}
X	errno = 0;
X	cc = EOF;
X#ifndef GEMDOS
X	rc = read(fp,&c,1);
X	if ((rc <= 0) && (errno == EINTR))
X		return(_readchar(fp));
X    cc = c;
X#else
X        if (fp >= 0)
X        {
X            rc = 1;
X            cc = Bconin(2);
X        }
X        else rc = 0;
X#endif  GEMDOS
X	cc &= 0x7f;
X	if (rc <= 0)
X		cc = EOF;
X	return(cc);
X};	/* end of _readchar */
X
X/*
X * Simple output routine
X */
X_writechar(c)
Xchar c;
X{
X#ifndef GEMDOS
X	write(TTY,&c,1);
X#else
X        Bconout(2,c);
X#endif  GEMDOS
X};	/* end of _writechar */
X
X/*
X * Save a character in the pending input buffer to be read later
X */
X_savechar(c)
Xchar c;
X{
X	if (pendingInput.size >= sizeof(pendingInput.buffer)-1)
X		return;		/* no space to save it */
X	pendingInput.buffer[pendingInput.size++] = c;
X	pendingInput.buffer[pendingInput.size] = '\0';
X};	/* end of _savechar */
X
X/*
X * The basic "get a line" with editing routine.
X * We return the number of characters read, or
X *		0 on end-of-file
X *	       <0 on I/O error
X *
X * If history structure not allocated yet, allocate it.
X */
Xstatic int localReader(hp,fp,buffer,size)
Xstruct historyLine *hp;
Xregister int fp;
Xchar *buffer;
Xint size;
X{
X	char nextCharacter;
X	register char *cp;
X	register int rc;
X	extern errno;
X
X	/*
X	 * Get an input buffer
X	 */
X	getHistoryBuffer();
X	if (hp)
X	{	/* start with a non-empty line */
X		strncpy(History.currentLine->contents,hp->contents,hp->size);
X		History.currentLine->size = hp->size;
X		History.currentLine->cursor = hp->size;
X		_doStandardRetype('\0');
X	}
X
X	/*
X	 * Non-terminal mode dependant initialization
X	 */
X        if (_savedState.localEditor == (char *)NULL)
X	{
X		_savedState.localEditor = var_normal("$VISUAL");
X		if (_savedState.localEditor == (char *)NULL)
X			_savedState.localEditor = var_normal("$EDITOR");
X		if (_savedState.localEditor == (char *)NULL)
X			_savedState.localEditor = var_normal("$FCEDIT");
X	}
X	if (_savedState.localEditor != (char *)NULL)
X	{
X		cp = rindex(_savedState.localEditor,DIR_SEPARATOR);
X		if (cp != (char *)NULL)
X			cp++;
X		else	cp = _savedState.localEditor;
X		if (_savedState.isEmacs >= 0 && _savedState.isVi >= 0)
X		{
X			if (wild_match(cp,"*[eE][mM][aA][cC][sS]*"))
X			{
X				_savedState.isEmacs = 1;
X				_savedState.isVi = 0;
X			}
X			else
X			{	/* else check for other editors */
X				if (wild_match(cp,"*[vV][iI]*"))
X				{
X					_savedState.isEmacs = 0;
X					_savedState.isVi = 1;
X				}
X			}
X		}
X	}
X
X	/*
X	 * Set up terminal modes
X	 */
X	initialize(fp);
X
X	/*
X	 * Start reading the line
X	 */
X	for (;;)
X	{	/* loop til eol */
X		nextCharacter = _readchar(fp);
X		if (nextCharacter == EOF)
X		{	/* I/O error */
X			History.currentLine->size = 0;
X			History.currentLine->cursor = 0;
X			size = -1;
X			break;
X		}
X#ifndef GEMDOS
X		if ((nextCharacter == _savedState.basicCharacters.t_eofc) &&
X		    (History.currentLine->size == 0))
X		{	/* end-of-file */
X			break;
X		}
X		if ((nextCharacter == '\\') ||
X		    (nextCharacter == _savedState.localCharacters.t_lnextc))
X		{	/* quote the next character */
X			if (nextCharacter == '\\')
X			{	/* save this character */
X				_doStandardCharacter(nextCharacter);
X				if (History.currentLine->size >= sizeof(History.currentLine->contents)-1)
X					break;
X			}
X			nextCharacter = _readchar(fp);
X			if (nextCharacter == EOF)
X			{	/* end of file */
X				History.currentLine->cursor = 0;
X				History.currentLine->size = 0;
X				size = -1;
X				break;
X			}
X			_doStandardCharacter(nextCharacter);
X			if (History.currentLine->size >= sizeof(History.currentLine->contents)-1)
X				break;
X			continue;
X		}
X#else
X		if ((nextCharacter == '\032') &&
X		    (History.currentLine->size == 0))
X		{	/* end-of-file */
X			break;
X		}
X#endif  GEMDOS
X
X		/*
X		 * Check for special editors
X		 */
X		if (_savedState.isEmacs)
X		{	/* emacs style editing */
X			rc = _doEmacs(nextCharacter);
X			if (rc < 0)
X			{	/* end of file */
X				break;
X			}
X			if (rc > 0)
X				continue;
X		}
X		if (_savedState.isVi)
X		{	/* vi style editing */
X			rc = _doVi(nextCharacter);
X			if (rc < 0)
X			{	/* end of file */
X				break;
X			}
X			if (rc > 0)
X				continue;
X		}
X
X		/*
X		 * The "basic" editor; it basically matches the UCB 4.2
X		 * "newtty" driver except for the extension that the
X		 * "escape" character does command or filename completion.
X		 */
X#ifndef  GEMDOS
X		if (nextCharacter == _savedState.basicCharacters.t_eofc)
X		{	/* an eof */
X			break;
X		}
X		if (nextCharacter == _savedState.basicAttributes.sg_erase)
X		{	/* a simple backspace */
X			_doStandardErase(nextCharacter);
X			continue;
X		}
X		if (nextCharacter == _savedState.basicAttributes.sg_kill)
X		{	/* kill a line */
X			_doStandardKill(nextCharacter);
X			continue;
X		}
X		if (nextCharacter == _savedState.localCharacters.t_rprntc)
X		{	/* retype a line */
X			_doStandardRetype(nextCharacter);
X			continue;
X		}
X		if (nextCharacter == _savedState.localCharacters.t_werasc)
X		{	/* erase a word */
X			_deletePrevStdWord();
X			continue;
X		}
X		switch(nextCharacter)
X		{	/* decide what to do with it, if not caught above */
X		    case '\032': /* other end-of-file character */
X			size = 0;
X			break;
X
X		    default:	/* a character with no special meaning */
X			_doStandardCharacter(nextCharacter);
X			if (History.currentLine->size >= sizeof(History.currentLine->contents)-1)
X				break;
X			continue;
X
X		    case '\033':/* name expansion requested */
X			_doStandardExpand(nextCharacter);
X			continue;
X
X		    case '\014':/* list expansion possibilities character */
X			ExpandAName(History.currentLine->contents,
X				sizeof(History.currentLine->contents)-1,
X				History.currentLine->size, 0);
X			continue;
X
X		    case '\n':	/* eol */
X		    case '\r':
X			_doStandardEndLine(nextCharacter);
X			_doStandardCharacter(nextCharacter);
X			break;
X		}
X		break;
X#else
X		switch(nextCharacter)
X		{	/* decide what to do with it, if not caught above */
X                    case '\027':
X			_deletePrevStdWord();
X			continue;
X
X                    case '\022':
X			_doStandardRetype(nextCharacter);
X			continue;
X
X                    case '\030':
X			_doStandardKill(nextCharacter);
X			continue;
X
X		    case '\010':
X			_doStandardErase(nextCharacter);
X                        continue;
X
X		    case '\004': /* other end-of-file character */
X                    case '\032':
X			size = 0;
X			break;
X
X		    default:	/* a character with no special meaning */
X			_doStandardCharacter(nextCharacter);
X			if (History.currentLine->size >= sizeof(History.currentLine->contents)-1)
X				break;
X			continue;
X
X		    case '\033':/* name expansion requested */
X			_doStandardExpand(nextCharacter);
X			continue;
X
X		    case '\014':/* list expansion possibilities character */
X			ExpandAName(History.currentLine->contents,
X				sizeof(History.currentLine->contents)-1,
X				History.currentLine->size, 0);
X			continue;
X
X		    case '\r':
X		    	_writechar('\n');
X			_doStandardEndLine(nextCharacter);
X			_doStandardCharacter(nextCharacter);
X			break;
X		    case '\n':	/* eol */
X			_writechar('\r');
X			_doStandardEndLine(nextCharacter);
X			_doStandardCharacter(nextCharacter);
X			break;
X		}
X		break;
X#endif  GEMDOS
X	}
X
X	/*
X	 * Clean up and return
X	 */
X	_terminate(fp);
X	_savedState.stream = -1;
X	buffer[0] = '\0';
X	size = (size < History.currentLine->size) ? size : History.currentLine->size;
X	if (size >= 0)
X	{	/* copy in the line to be returned */
X		if (size > 0)
X			strncpy(buffer,History.currentLine->contents,size);
X		buffer[size] = '\0';
X		History.currentLine->contents[History.currentLine->size] = '\0';
X	}
X	else	History.currentLine->contents[0] = '\0';
X#ifdef  GEMDOS
X        _writechar('\r');
X#endif  GEMDOS
X	if (_savedState.localEditor != (char *)NULL)
X		free(_savedState.localEditor);
X	_savedState.localEditor = (char *)NULL;
X	return(size);
X};	/* end of localReader */
X
X/*
X * The main "Reader" entry point; get a completely new line
X */
Xint Reader(fp,buffer,size)
Xint fp;
Xchar *buffer;
Xint size;
X{
X	return(localReader((struct historyLine *)NULL,fp,buffer,size));
X};	/* end of Reader */
X
X/*
X * The main "Editor" entry point; edit an old line
X */
Xint Editor(hp,fp,buffer,size)
Xstruct historyLine *hp;
Xint fp;
Xchar *buffer;
Xint size;
X{
X	struct historyLine line;
X	register int rc;
X
X	strncpy(line.contents,hp->contents,hp->size);
X	line.size = hp->size;
X	line.cursor = hp->cursor;
X	if (line.contents[line.size-1] == '\n')
X		line.size--;
X	if (line.contents[line.size-1] == '\r')
X		line.size--;
X	rc = localReader(&line,fp,buffer,size);
X	return(rc);
X};	/* end of Editor */
X
X/*
X *	Is s2 a substring of s1?
X */
Xchar *substr(s1,s2)
Xchar *s1;
Xregister char *s2;
X{
X	register char *s;
X	register int sl;
X
X	sl = strlen(s2);
X	for (s = s1; *s; s++)
X	{
X		if (strncmp(s,s2,sl) == 0)
X			return(s);
X	}
X	return((char *)NULL);
X};	/* end of substr */
END_OF_FILE
if test 17920 -ne `wc -c <'reader/reader.c'`; then
    echo shar: \"'reader/reader.c'\" unpacked with wrong size!
fi
# end of 'reader/reader.c'
fi
echo shar: End of archive 4 \(of 11\).
cp /dev/null ark4isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 11 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0



More information about the Alt.sources mailing list