root/usr/src/cmd/vi/port/ex_cmds2.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/*        All Rights Reserved   */


/* Copyright (c) 1981 Regents of the University of California */

#include "ex.h"
#include "ex_argv.h"
#include "ex_temp.h"
#include "ex_tty.h"
#include "ex_vis.h"
#include <unistd.h>

extern bool     pflag, nflag;           /* extern; also in ex_cmds.c */
extern int      poffset;                /* extern; also in ex_cmds.c */
extern short    slevel;                 /* extern; has level of source() */

/*
 * Subroutines for major command loop.
 */

/*
 * Is there a single letter indicating a named buffer next?
 */
int
cmdreg(void)
{
        int c = 0;
        int wh = skipwh();

#ifdef XPG4
        if (wh && isalpha(c = peekchar()) && isascii(c) && !isdigit(c))
#else /* XPG4 */
        if (wh && isalpha(c = peekchar()) && isascii(c))
#endif /* XPG4 */
                c = getchar();

#ifdef XPG4
        if (isdigit(c)) {
                c = 0;
        }
#endif /* XPG4 */
        return (c);
}

/*
 * Tell whether the character ends a command
 */
int
endcmd(int ch)
{
        switch (ch) {

        case '\n':
        case EOF:
                endline = 1;
                return (1);

        case '|':
        case '"':
                endline = 0;
                return (1);
        }
        return (0);
}

/*
 * Insist on the end of the command.
 */
void
eol(void)
{

        if (!skipend())
                error(value(vi_TERSE) ? gettext("Extra chars") :
                        gettext("Extra characters at end of command"));
        ignnEOF();
}

#ifdef XPG4
/*
 * Print out the message in the error message file at str,
 * with i an integer argument to printf.
 */
/*VARARGS2*/
void
error(str, i)
        unsigned char *str;
        int i;
{
        tagflg = 0;
        errcnt++;
        noerror(str, i);
}

/*
 * noerror(): like error(), but doesn't inc errcnt.
 * the reason why we created this routine, instead of fixing up errcnt
 * after error() is called, is because we will do a longjmp, and
 * not a return. it does other things closing file i/o, reset, etc;
 * so we follow those procedures.
 */
/*VARARGS2*/
void
noerror(str, i)
        unsigned char *str;
        int i;
{

        error0();
        merror(str, i);
        if (writing) {
                serror((unsigned char *)
                    gettext(" [Warning - %s is incomplete]"), file);
                writing = 0;
        }
        error1(str);
}

#else /* !XPG4 */
/*
 * Print out the message in the error message file at str,
 * with i an integer argument to printf.
 */
/*VARARGS2*/
void
error(str, i)
        unsigned char *str;
        int i;
{
        tagflg = 0;
        errcnt++;
        error0();
        merror(str, i);
        if (writing) {
                serror((unsigned char *)
                    gettext(" [Warning - %s is incomplete]"), file);
                writing = 0;
        }
        error1(str);
}
#endif /* XPG4 */

/*
 * Rewind the argument list.
 */
void
erewind(void)
{

        argc = argc0;
        argv = argv0;
        args = args0;
        if (argc > 1 && !hush && cur_term) {
                viprintf(mesg(value(vi_TERSE) ? gettext("%d files") :
                        gettext("%d files to edit")), argc);
                if (inopen)
                        putchar(' ');
                else
                        putNFL();
        }
}

/*
 * Guts of the pre-printing error processing.
 * If in visual and catching errors, then we don't mung up the internals,
 * just fixing up the echo area for the print.
 * Otherwise we reset a number of externals, and discard unused input.
 */
void
error0(void)
{

        if (laste) {
#ifdef VMUNIX
                tlaste();
#endif
                laste = 0;
                sync();
        }
        if (vcatch) {
                if (splitw == 0)
                        fixech();
                if (!enter_standout_mode || !exit_bold)
                        dingdong();
                return;
        }
        if (input) {
                input = strend(input) - 1;
                if (*input == '\n')
                        setlastchar('\n');
                input = 0;
        }
        setoutt();
        flush();
        resetflav();
        if (!enter_standout_mode || !exit_bold)
                dingdong();
        if (inopen) {
                /*
                 * We are coming out of open/visual ungracefully.
                 * Restore columns, undo, and fix tty mode.
                 */
                columns = OCOLUMNS;
                undvis();
                ostop(normf);
                /* ostop should be doing this
                putpad(cursor_normal);
                putpad(key_eol);
                */
                putnl();
        }
        inopen = 0;
        holdcm = 0;
}

/*
 * Post error printing processing.
 * Close the i/o file if left open.
 * If catching in visual then throw to the visual catch,
 * else if a child after a fork, then exit.
 * Otherwise, in the normal command mode error case,
 * finish state reset, and throw to top.
 */
void
error1(unsigned char *str)
{
        bool die;
        extern short ttyindes;

        if ((io > 0) && (io != ttyindes)) {
                close(io);
                io = -1;
        }

        die = (getpid() != ppid);       /* Only children die */
        inappend = inglobal = 0;
        globp = vglobp = vmacp = 0;
        if (vcatch && !die) {
                inopen = 1;
                vcatch = 0;
                if (str)
                        noonl();
                fixol();
                if (slevel > 0)
                        reset();
                longjmp(vreslab,1);
        }
        if (str && !vcatch)
                putNFL();
        if (die)
                exit(++errcnt);
        lseek(0, 0L, 2);
        if (inglobal)
                setlastchar('\n');

        if (inexrc) {
                /*
                 * Set inexrc to 0 so that this error is printed only
                 * once (eg. when stdin is redirected from /dev/null and
                 * vi prints "Input read error" because it is unable to
                 * read() the <CR>).
                 */
                inexrc = 0;
                lprintf(gettext(
                    "Error detected in .exrc.[Hit return to continue] "),
                    0);
                putNFL();
                getkey();
        }

        while ((lastchar() != '\n') && (lastchar() != EOF))
                ignchar();
        ungetchar(0);
        endline = 1;
        reset();
}

void
fixol(void)
{
        if (Outchar != vputchar) {
                flush();
                if (state == ONEOPEN || state == HARDOPEN)
                        outline = destline = 0;
                Outchar = vputchar;
                vcontin(1);
                /*
                 * Outchar could be set to termchar() through vcontin().
                 * So reset it again.
                 */
                Outchar = vputchar;
        } else {
                if (destcol)
                        vclreol();
                vclean();
        }
}

/*
 * Does an ! character follow in the command stream?
 */
int
exclam(void)
{

        if (peekchar() == '!') {
                ignchar();
                return (1);
        }
        return (0);
}

/*
 * Make an argument list for e.g. next.
 */
void
makargs(void)
{

        glob(&frob);
        argc0 = frob.argc0;
        argv0 = frob.argv;
        args0 = argv0[0];
        erewind();
}

/*
 * Advance to next file in argument list.
 */
void
next(void)
{
        extern short isalt;     /* defined in ex_io.c */

        if (argc == 0)
                error(value(vi_TERSE) ?
                        (unsigned char *)gettext("No more files") :
                        (unsigned char *)gettext("No more files to edit"));
        morargc = argc;
        isalt = (strcmp(altfile, args)==0) + 1;
        if (savedfile[0])
                CP(altfile, savedfile);
        (void) strlcpy(savedfile, args, sizeof (savedfile));
        argc--;
        args = argv ? *++argv : strend(args) + 1;
#if i386 || i286
        destcol = 0;
#endif
}

/*
 * Eat trailing flags and offsets after a command,
 * saving for possible later post-command prints.
 */
void
donewline(void)
{
        int c;

        resetflav();
        for (;;) {
                c = getchar();
                switch (c) {

                case '^':
                case '-':
                        poffset--;
                        break;

                case '+':
                        poffset++;
                        break;

                case 'l':
                        listf++;
                        break;

                case '#':
                        nflag++;
                        break;

                case 'p':
                        listf = 0;
                        break;

                case ' ':
                case '\t':
                        continue;

                case '"':
                        comment();
                        setflav();
                        return;

                default:
                        if (!endcmd(c))
                            serror(value(vi_TERSE) ?
                                (unsigned char *)gettext("Extra chars") :
                                (unsigned char *)gettext(
                                "Extra characters at end of \"%s\" command"),
                                    Command);
                        if (c == EOF)
                                ungetchar(c);
                        setflav();
                        return;
                }
                pflag++;
        }
}

/*
 * Before quit or respec of arg list, check that there are
 * no more files in the arg list.
 */
int
nomore(void)
{

        if (argc == 0 || morargc == argc)
                return(0);
        morargc = argc;
        if (argc == 1) {
                merror(value(vi_TERSE) ? gettext("1 more file") :
                       gettext("1 more file to edit"), argc);
        } else {
                merror(value(vi_TERSE) ? gettext("%d more files") :
                        gettext("%d more files to edit"), argc);
        }
        return(1);
}

/*
 * Before edit of new file check that either an ! follows
 * or the file has not been changed.
 */
int
quickly(void)
{

        if (exclam())
                return (1);
        if (chng && dol > zero) {
/*
                chng = 0;
*/
                xchng = 0;
                error(value(vi_TERSE) ? (unsigned char *)gettext("No write") :
                    (unsigned char *)
                    gettext("No write since last change (:%s! overrides)"),
                    Command);
        }
        return (0);
}

/*
 * Reset the flavor of the output to print mode with no numbering.
 */
void
resetflav(void)
{

        if (inopen)
                return;
        listf = 0;
        nflag = 0;
        pflag = 0;
        poffset = 0;
        setflav();
}

/*
 * Print an error message with a %s type argument to printf.
 * Message text comes from error message file.
 */
void
serror(unsigned char *str, unsigned char *cp)
{
        tagflg = 0;
        error0();
        smerror(str, cp);
        error1(str);
}

/*
 * Set the flavor of the output based on the flags given
 * and the number and list options to either number or not number lines
 * and either use normally decoded (ARPAnet standard) characters or list mode,
 * where end of lines are marked and tabs print as ^I.
 */
void
setflav(void)
{

        if (inopen)
                return;
        setnumb(nflag || value(vi_NUMBER));
        setlist(listf || value(vi_LIST));
        if (!inopen)
                setoutt();
}

/*
 * Skip white space and tell whether command ends then.
 */
int
skipend(void)
{

        pastwh();
        return (endcmd(peekchar()) && peekchar() != '"');
}

/*
 * Set the command name for non-word commands.
 */
void
tailspec(int c)
{
        static unsigned char foocmd[2];

        foocmd[0] = c;
        Command = foocmd;
}

/*
 * Try to read off the rest of the command word.
 * If alphabetics follow, then this is not the command we seek.
 */
void
tail(unsigned char *comm)
{

        tailprim(comm, 1, 0);
}

void
tail2of(unsigned char *comm)
{

        tailprim(comm, 2, 0);
}

unsigned char   tcommand[20];

void
tailprim(unsigned char *comm, int i, bool notinvis)
{
        unsigned char *cp;
        int c;

        Command = comm;
        for (cp = tcommand; i > 0; i--)
                *cp++ = *comm++;
        while (*comm && peekchar() == *comm)
                *cp++ = getchar(), comm++;
        c = peekchar();
        if (notinvis || (isalpha(c) && isascii(c))) {
                /*
                 * Of the trailing lp funny business, only dl and dp
                 * survive the move from ed to ex.
                 */
                if (tcommand[0] == 'd' && any(c, "lp"))
                        goto ret;
                if (tcommand[0] == 's' && any(c, "gcr"))
                        goto ret;
                while (cp < &tcommand[19] && isalpha(c = peekchar()) && isascii(c))
                        *cp++ = getchar();
                *cp = 0;
                if (notinvis)
                        serror(value(vi_TERSE) ?
                            (unsigned char *)gettext("What?") :
                            (unsigned char *)gettext(
                            "%s: No such command from open/visual"), tcommand);
                else
                        serror(value(vi_TERSE) ?
                            (unsigned char *)gettext("What?") :
                            (unsigned char *)gettext(
                            "%s: Not an editor command"), tcommand);
        }
ret:
        *cp = 0;
}

/*
 * Continue after a : command from open/visual.
 */
void
vcontin(bool ask)
{

        if (vcnt > 0)
                vcnt = -vcnt;
        if (inopen) {
                if (state != VISUAL) {
                        /*
                         * We don't know what a shell command may have left on
                         * the screen, so we move the cursor to the right place
                         * and then put out a newline.  But this makes an extra
                         * blank line most of the time so we only do it for :sh
                         * since the prompt gets left on the screen.
                         *
                         * BUG: :!echo longer than current line \\c
                         * will mess it up.
                         */
                        if (state == CRTOPEN) {
                                termreset();
                                vgoto(WECHO, 0);
                        }
                        if (!ask) {
                                (void) putch('\r');
                                (void) putch('\n');
                        }
                        return;
                }
                if (ask) {
                        merror(gettext("[Hit return to continue] "));
                        flush();
                }
#ifndef CBREAK
                vraw();
#endif
                if (ask) {
#ifdef notdef
                        /*
                         * Gobble ^Q/^S since the tty driver should be eating
                         * them (as far as the user can see)
                         */
                        while (peekkey() == CTRL('Q') || peekkey() == CTRL('S'))
                                ignore(getkey());
#endif
                        if(getkey() == ':') {
                                /* Extra newlines, but no other way */
                                (void) putch('\n');
                                outline = WECHO;
                                ungetkey(':');
                        }
                }
                vclrech(1);
                if (Peekkey != ':') {
                        fixterm();
                        putpad((unsigned char *)enter_ca_mode);
                        tostart();
                }
        }
}

/*
 * Put out a newline (before a shell escape)
 * if in open/visual.
 */
void
vnfl(void)
{

        if (inopen) {
                if (state != VISUAL && state != CRTOPEN && destline <= WECHO)
                        vclean();
                else
                        vmoveitup(1, 0);
                vgoto(WECHO, 0);
                vclrbyte(vtube[WECHO], WCOLS);
                tostop();
                /* replaced by the ostop above
                putpad(cursor_normal);
                putpad(key_eol);
                */
        }
        flush();
}