diff options
Diffstat (limited to 'ex.c')
-rw-r--r-- | ex.c | 678 |
1 files changed, 678 insertions, 0 deletions
@@ -0,0 +1,678 @@ +/* + * This code contains changes by + * Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved. + * + * Conditions 1, 2, and 4 and the no-warranty notice below apply + * to these changes. + * + * + * Copyright (c) 1980, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * Redistributions of source code and documentation must retain the + * above copyright notice, this list of conditions and the following + * disclaimer. + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed or owned by Caldera + * International, Inc. + * Neither the name of Caldera International, Inc. nor the names of + * other contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA + * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE + * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lint +#ifdef DOSCCS +char *copyright = +"@(#) Copyright (c) 1980 Regents of the University of California.\n\ + All rights reserved.\n"; + +static char sccsid[] = "@(#)ex.c 1.36 (gritter) 2/13/05"; +#endif /* DOSCCS */ +#endif /* !lint */ + +/* from ex.c 7.5.1.1 (Berkeley) 8/12/86 */ + +#include "ex.h" +#include "ex_argv.h" +#include "ex_temp.h" +#include "ex_tty.h" + +#ifdef TRACE +char tttrace[] = { '/','d','e','v','/','t','t','y','x','x',0 }; +#endif + +/* + * The code for ex is divided as follows: + * + * ex.c Entry point and routines handling interrupt, hangup + * signals; initialization code. + * + * ex_addr.c Address parsing routines for command mode decoding. + * Routines to set and check address ranges on commands. + * + * ex_cmds.c Command mode command decoding. + * + * ex_cmds2.c Subroutines for command decoding and processing of + * file names in the argument list. Routines to print + * messages and reset state when errors occur. + * + * ex_cmdsub.c Subroutines which implement command mode functions + * such as append, delete, join. + * + * ex_data.c Initialization of options. + * + * ex_get.c Command mode input routines. + * + * ex_io.c General input/output processing: file i/o, unix + * escapes, filtering, source commands, preserving + * and recovering. + * + * ex_put.c Terminal driving and optimizing routines for low-level + * output (cursor-positioning); output line formatting + * routines. + * + * ex_re.c Global commands, substitute, regular expression + * compilation and execution. + * + * ex_set.c The set command. + * + * ex_subr.c Loads of miscellaneous subroutines. + * + * ex_temp.c Editor buffer routines for main buffer and also + * for named buffers (Q registers if you will.) + * + * ex_tty.c Terminal dependent initializations from termcap + * data base, grabbing of tty modes (at beginning + * and after escapes). + * + * ex_unix.c Routines for the ! command and its variations. + * + * ex_v*.c Visual/open mode routines... see ex_v.c for a + * guide to the overall organization. + */ + +static char *progname; + +void +erropen(void) +{ + close(1); + dup(2); +} + +void +usage(void) +{ + printf(catgets(catd, 1, 1, "\ +Usage: %s [- | -s] [-l] [-L] [-R] [-r [file]] [-t tag]\n\ + [-v] [-V] [-w size] [+cmd | -c cmd] file...\n"), + progname); + flush(); + exitex(1); +} + +void +needarg(int c) +{ + erropen(); + printf(catgets(catd, 1, 2, + "%s: option requires an argument -- %c\n"), progname, c); + usage(); +} + +void +invopt(int c) +{ + erropen(); + printf(catgets(catd, 1, 3, "%s: illegal option -- %c\n"), progname, c); + usage(); +} + +/* + * Return last component of unix path name p. + */ +char * +tailpath(register char *p) +{ + register char *r; + + for (r=p; *p; p++) + if (*p == '/') + r = p+1; + return(r); +} + +/* + * Check ownership of file. Return nonzero if it exists and is owned by the + * user or the option sourceany is used + */ +int +iownit(char *file) +{ + struct stat sb; + + if (*file == '.' && value(EXRC) == 0) + return 0; + if (stat(file, &sb)) + return 0; + if (value(SOURCEANY)) + return 1; + if (sb.st_uid != getuid()) + return 0; + if (sb.st_mode & (S_IWOTH | S_IWGRP)) + return 0; + return 1; +} + +shand +setsig(int signum, shand handler) +{ + struct sigaction nact, oact; + + nact.sa_handler = handler; + sigemptyset(&nact.sa_mask); + nact.sa_flags = 0; + if (signum == SIGALRM) { +#ifdef SA_INTERRUPT + nact.sa_flags |= SA_INTERRUPT; +#endif + /*EMPTY*/ ; + } else { +#ifdef SA_RESTART + nact.sa_flags |= SA_RESTART; +#endif + /*EMPTY*/ ; + } + if (sigaction(signum, &nact, &oact) != 0) + return SIG_ERR; + return oact.sa_handler; +} + +/* + * Initialization, before editing a new file. + * Main thing here is to get a new buffer (in fileinit), + * rest is peripheral state resetting. + */ +void +init(void) +{ + register int i; + + fileinit(); + dot = zero = truedol = unddol = dol = fendcore; + one = zero+1; + undkind = UNDNONE; + chng = 0; + edited = 0; + for (i = 0; i <= 'z'-'a'+1; i++) + names[i] = 1; + anymarks = 0; +} + +/* + * Main procedure. Process arguments and then + * transfer control to the main command processing loop + * in the routine commands. We are entered as either "ex", "edit", "vi" + * or "view" and the distinction is made here. Actually, we are "vi" if + * there is a 'v' in our name, "view" is there is a 'w', and "edit" if + * there is a 'd' in our name. For edit we just diddle options; + * for vi we actually force an early visual command. + */ +int +main(register int ac, register char *av[]) +{ +#ifndef VMUNIX + char *erpath = EXSTRINGS; +#endif + char *cp = NULL; + register int c; + bool ivis; + bool fast = 0; +#ifdef TRACE + register char *tracef; +#endif + + CLOBBGRD(ivis); + CLOBBGRD(fast); + CLOBBGRD(cp); + + /* + * Initialize the built-in memory allocator. + */ +#ifdef VMUNIX + poolsbrk(0); +#endif + + /* + * Immediately grab the tty modes so that we wont + * get messed up if an interrupt comes in quickly. + */ + gTTY(1); + normf = tty; + ppid = getpid(); + /* + * Defend against d's, v's, w's, and a's in directories of + * path leading to our true name. + */ + av[0] = tailpath(av[0]); + + /* + * Figure out how we were invoked: ex, edit, vi, view. + */ + ivis = any('v', av[0]); /* "vi" */ + if (any('w', av[0])) /* "view" */ + value(READONLY) = 1; + if (any('d', av[0])) { /* "edit" */ + value(SHOWMODE) = 1; + /* + * I do not understand why novices should not + * switch to visual mode. So they can now. gritter + */ + /*value(OPEN) = 0;*/ + value(REPORT) = 1; + value(MAGIC) = 0; + } + +#ifndef VMUNIX + /* + * For debugging take files out of . if name is a.out. + */ + if (av[0][0] == 'a') + erpath = tailpath(erpath); +#endif /* !VMUNIX */ + + progname = av[0]; + /* + * Open the error message file. + */ + draino(); +#ifndef VMUNIX + erfile = open(erpath+4, O_RDONLY); + if (erfile < 0) { + erfile = open(erpath, O_RDONLY); + } +#endif /* !VMUNIX */ + pstop(); + + /* + * Initialize interrupt handling. + */ + oldhup = signal(SIGHUP, SIG_IGN); + if (oldhup == SIG_DFL) + signal(SIGHUP, onhup); + oldquit = signal(SIGQUIT, SIG_IGN); +#ifdef SIGXFSZ + oldxfsz = signal(SIGXFSZ, SIG_IGN); +#endif + ruptible = signal(SIGINT, SIG_IGN) == SIG_DFL; + if (signal(SIGTERM, SIG_IGN) == SIG_DFL) + signal(SIGTERM, onhup); +#ifdef SIGEMT + if (signal(SIGEMT, SIG_IGN) == SIG_DFL) + signal(SIGEMT, onemt); +#endif + +#ifdef BIT8 +#ifndef ISO8859_1 + setlocale(LC_CTYPE, ""); +#endif +#endif + +#ifdef MB_CUR_MAX + mb_cur_max = MB_CUR_MAX; +#else + mb_cur_max = 1; +#endif + +#ifdef MB + TRIM = mb_cur_max > 1 ? 0x6fffffff : 0xff; + QUOTE = mb_cur_max > 1 ? 0x10000000 : 0x100; +#endif + +#ifdef LANGMSG + setlocale(LC_MESSAGES, ""); + catd = catopen(CATNAME, NL_CAT_LOCALE); +#endif + + /* + * Process flag arguments. + */ + ac--, av++; + while (ac) { + if (av[0][0] == '+') { + firstpat = &av[0][1]; + if (*firstpat == '\0') + needarg('+'); + } else if (av[0][0] == '-') { +arggroup: + c = av[0][1]; + if (c == 0 + || c == 's' + ) { + hush = 1; + value(AUTOPRINT) = 0; + fast++; + } else switch (c) { + + case '-': + if (av[0][2]) + invopt('-'); + ac--, av++; + goto argend; + + case 'R': + value(READONLY) = 1; + break; + +#ifdef TRACE + case 'T': + if (av[0][2] == 0) + tracef = "trace"; + else { + tracef = tttrace; + tracef[8] = av[0][2]; + if (tracef[8]) + tracef[9] = av[0][3]; + else + tracef[9] = 0; + } + trace = fopen(tracef, "w"); +#define tracbuf NULL + if (trace == NULL) + printf(catgets(catd, 1, 4, + "Trace create error\n")); + else + setbuf(trace, tracbuf); + break; + +#endif /* TRACE */ + + case 'c': + if (av[0][2] == '\0' && (av[1] == NULL + || *av[1] == '-' || *av[1] == '+')) + needarg('c'); + if (av[0][2]) { + firstpat = &av[0][2]; + } else { + firstpat = av[1]; + ac--, av++; + } + break; + + case 'e': + ivis = 0; + break; + +#ifdef LISPCODE + case 'l': + value(LISP) = 1; + value(SHOWMATCH) = 1; + break; +#endif + + case 'L': + case 'r': + recov++; + break; + + case 't': + if (av[0][2]) { + tflag = 1; + safecp(lasttag, av[0], sizeof lasttag, + "argument to -t too long"); + } else if (ac > 1 && av[1][0] != '-' && + av[1][0] != '+') { + ac--, av++; + tflag = 1; + safecp(lasttag, av[0], sizeof lasttag, + "argument to -t too long"); + } else + needarg('t'); + break; + + case 'v': + ivis = 1; + break; + + case 'V': + verbose = 1; + break; + + case 'w': + if (av[0][2]) + cp = &av[0][2]; + else if (ac > 1 && av[1][0] != '-' && av[1][0] != '+') { + cp = av[1]; + ac--, av++; + } else + needarg('w'); + defwind = atoi(cp); + break; + + default: + invopt(c); + } + if (c && c != 'c' && c != 't' && c != 'w' && av[0][2]) { + av[0]++; + goto arggroup; + } + } else + break; + ac--, av++; + } +argend: + + cntrlhm = catgets(catd, 1, 70, "^H discarded\n"); + /* + * Initialize end of core pointers. + * Normally we avoid breaking back to fendcore after each + * file since this can be expensive (much core-core copying). + * If your system can scatter load processes you could do + * this as ed does, saving a little core, but it will probably + * not often make much difference. + */ + fendcore = (line *) sbrk(0); + endcore = fendcore - 2; + +#ifdef SIGTSTP + if (!hush && signal(SIGTSTP, SIG_IGN) == SIG_DFL) + signal(SIGTSTP, onsusp), dosusp++; +#endif /* SIGTSTP */ + + /* + * If we are doing a recover and no filename + * was given, then execute an exrecover command with + * the -r option to type out the list of saved file names. + * Otherwise set the remembered file name to the first argument + * file name so the "recover" initial command will find it. + */ + if (recov) { + if (ac == 0) { + ppid = 0; + setrupt(); + execl(EXRECOVER, "exrecover", "-r", (char *)0); + filioerr(EXRECOVER); + exitex(1); + } + safecp(savedfile, *av++, sizeof savedfile, "Filename too long"); + ac--; + } + + /* + * Initialize the argument list. + */ + argv0 = av; + argc0 = ac; + args0 = av[0]; + erewind(); + + /* + * Initialize a temporary file (buffer) and + * set up terminal environment. Read user startup commands. + */ + if (setexit() == 0) { + setrupt(); + intty = isatty(0); + value(PROMPT) = intty; + if ((cp = getenv("SHELL")) != NULL && *cp != '\0') + safecp(shell, cp, sizeof shell, "$SHELL too long"); + if (fast || !intty) + setterm("dumb"); + else { + gettmode(); + if ((cp = getenv("TERM")) != 0 && *cp) { + setterm(cp); + } + } + } + if (setexit() == 0) { + /* + * This is necessary because 'if (setexit() == 0 && !fast)' + * is rejected on the Cray. + */ + if (fast) + goto skip; + if ((globp = getenv("EXINIT")) && *globp) + commands(1,1); + else { + globp = 0; + if ((cp = getenv("HOME")) != 0 && *cp) { + safecat(safecp(genbuf, cp, sizeof genbuf, + "$HOME too long"), + "/.exrc", sizeof genbuf, + "$HOME too long"); + if (iownit(genbuf)) + source(genbuf, 1); + } + } + /* + * Allow local .exrc too. This loses if . is $HOME, + * but nobody should notice unless they do stupid things + * like putting a version command in .exrc. Besides, + * they should be using EXINIT, not .exrc, right? + * + * This may not be done anymore. GR + */ + /* + * The getcwd() function is not present on very + * old Unix systems. So if this fails, comment out + * the following three lines or supply code e.g. from + * the `pwd' utility. + */ + if (cp == NULL || *cp == '\0' + || getcwd(genbuf, MAXBSIZE) == NULL + || strcmp(cp, genbuf) != 0) + + if (iownit(".exrc")) + source(".exrc", 1); + } +skip: init(); /* moved after prev 2 chunks to fix directory option */ + + /* + * Initial processing. Handle tag, recover, and file argument + * implied next commands. If going in as 'vi', then don't do + * anything, just set initev so we will do it later (from within + * visual). + */ + if (setexit() == 0) { + if (recov) + globp = "recover"; + else if (tflag) + globp = ivis ? "tag" : "tag|p"; + else if (argc) + globp = "next"; + if (ivis) + initev = globp; + else if (globp) { + inglobal = 1; + commands(1, 1); + inglobal = 0; + } + } + + /* + * Vi command... go into visual. + * Strange... everything in vi usually happens + * before we ever "start". + */ + if (ivis) { + /* + * Don't have to be upward compatible with stupidity + * of starting editing at line $. + */ + if (dol > zero) + dot = one; + globp = "visual"; + if (setexit() == 0) + commands(1, 1); + } + + /* + * Clear out trash in state accumulated by startup, + * and then do the main command loop for a normal edit. + * If you quit out of a 'vi' command by doing Q or ^\, + * you also fall through to here. + */ + seenprompt = 1; + ungetchar(0); + globp = 0; + initev = 0; + setlastchar('\n'); + setexit(); + commands(0, 0); + cleanup(1); + exitex(0); + /*NOTREACHED*/ + return 0; +} |