diff options
Diffstat (limited to 'contrib/nvi/ex/ex_read.c')
-rw-r--r-- | contrib/nvi/ex/ex_read.c | 360 |
1 files changed, 360 insertions, 0 deletions
diff --git a/contrib/nvi/ex/ex_read.c b/contrib/nvi/ex/ex_read.c new file mode 100644 index 000000000000..78296ff28ca6 --- /dev/null +++ b/contrib/nvi/ex/ex_read.c @@ -0,0 +1,360 @@ +/*- + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1992, 1993, 1994, 1995, 1996 + * Keith Bostic. All rights reserved. + * + * See the LICENSE file for redistribution information. + */ + +#include "config.h" + +#ifndef lint +static const char sccsid[] = "@(#)ex_read.c 10.38 (Berkeley) 8/12/96"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/stat.h> +#include <sys/time.h> + +#include <bitstring.h> +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "../common/common.h" +#include "../vi/vi.h" + +/* + * ex_read -- :read [file] + * :read [!cmd] + * Read from a file or utility. + * + * !!! + * Historical vi wouldn't undo a filter read, for no apparent reason. + * + * PUBLIC: int ex_read __P((SCR *, EXCMD *)); + */ +int +ex_read(sp, cmdp) + SCR *sp; + EXCMD *cmdp; +{ + enum { R_ARG, R_EXPANDARG, R_FILTER } which; + struct stat sb; + CHAR_T *arg, *name; + EX_PRIVATE *exp; + FILE *fp; + FREF *frp; + GS *gp; + MARK rm; + recno_t nlines; + size_t arglen; + int argc, rval; + char *p; + + gp = sp->gp; + + /* + * 0 args: read the current pathname. + * 1 args: check for "read !arg". + */ + switch (cmdp->argc) { + case 0: + which = R_ARG; + break; + case 1: + arg = cmdp->argv[0]->bp; + arglen = cmdp->argv[0]->len; + if (*arg == '!') { + ++arg; + --arglen; + which = R_FILTER; + + /* Secure means no shell access. */ + if (O_ISSET(sp, O_SECURE)) { + ex_emsg(sp, cmdp->cmd->name, EXM_SECURE_F); + return (1); + } + } else + which = R_EXPANDARG; + break; + default: + abort(); + /* NOTREACHED */ + } + + /* Load a temporary file if no file being edited. */ + if (sp->ep == NULL) { + if ((frp = file_add(sp, NULL)) == NULL) + return (1); + if (file_init(sp, frp, NULL, 0)) + return (1); + } + + switch (which) { + case R_FILTER: + /* + * File name and bang expand the user's argument. If + * we don't get an additional argument, it's illegal. + */ + argc = cmdp->argc; + if (argv_exp1(sp, cmdp, arg, arglen, 1)) + return (1); + if (argc == cmdp->argc) { + ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE); + return (1); + } + argc = cmdp->argc - 1; + + /* Set the last bang command. */ + exp = EXP(sp); + if (exp->lastbcomm != NULL) + free(exp->lastbcomm); + if ((exp->lastbcomm = + strdup(cmdp->argv[argc]->bp)) == NULL) { + msgq(sp, M_SYSERR, NULL); + return (1); + } + + /* + * Vi redisplayed the user's argument if it changed, ex + * always displayed a !, plus the user's argument if it + * changed. + */ + if (F_ISSET(sp, SC_VI)) { + if (F_ISSET(cmdp, E_MODIFY)) + (void)vs_update(sp, "!", cmdp->argv[argc]->bp); + } else { + if (F_ISSET(cmdp, E_MODIFY)) + (void)ex_printf(sp, + "!%s\n", cmdp->argv[argc]->bp); + else + (void)ex_puts(sp, "!\n"); + (void)ex_fflush(sp); + } + + /* + * Historically, filter reads as the first ex command didn't + * wait for the user. If SC_SCR_EXWROTE not already set, set + * the don't-wait flag. + */ + if (!F_ISSET(sp, SC_SCR_EXWROTE)) + F_SET(sp, SC_EX_WAIT_NO); + + /* + * Switch into ex canonical mode. The reason to restore the + * original terminal modes for read filters is so that users + * can do things like ":r! cat /dev/tty". + * + * !!! + * We do not output an extra <newline>, so that we don't touch + * the screen on a normal read. + */ + if (F_ISSET(sp, SC_VI)) { + if (gp->scr_screen(sp, SC_EX)) { + ex_emsg(sp, cmdp->cmd->name, EXM_NOCANON_F); + return (1); + } + /* + * !!! + * Historically, the read command doesn't switch to + * the alternate X11 xterm screen, if doing a filter + * read -- don't set SA_ALTERNATE. + */ + F_SET(sp, SC_SCR_EX | SC_SCR_EXWROTE); + } + + if (ex_filter(sp, cmdp, &cmdp->addr1, + NULL, &rm, cmdp->argv[argc]->bp, FILTER_READ)) + return (1); + + /* The filter version of read set the autoprint flag. */ + F_SET(cmdp, E_AUTOPRINT); + + /* + * If in vi mode, move to the first nonblank. Might have + * switched into ex mode, so saved the original SC_VI value. + */ + sp->lno = rm.lno; + if (F_ISSET(sp, SC_VI)) { + sp->cno = 0; + (void)nonblank(sp, sp->lno, &sp->cno); + } + return (0); + case R_ARG: + name = sp->frp->name; + break; + case R_EXPANDARG: + if (argv_exp2(sp, cmdp, arg, arglen)) + return (1); + /* + * 0 args: impossible. + * 1 args: impossible (I hope). + * 2 args: read it. + * >2 args: object, too many args. + * + * The 1 args case depends on the argv_sexp() function refusing + * to return success without at least one non-blank character. + */ + switch (cmdp->argc) { + case 0: + case 1: + abort(); + /* NOTREACHED */ + case 2: + name = cmdp->argv[1]->bp; + /* + * !!! + * Historically, the read and write commands renamed + * "unnamed" files, or, if the file had a name, set + * the alternate file name. + */ + if (F_ISSET(sp->frp, FR_TMPFILE) && + !F_ISSET(sp->frp, FR_EXNAMED)) { + if ((p = v_strdup(sp, cmdp->argv[1]->bp, + cmdp->argv[1]->len)) != NULL) { + free(sp->frp->name); + sp->frp->name = p; + } + /* + * The file has a real name, it's no longer a + * temporary, clear the temporary file flags. + */ + F_CLR(sp->frp, FR_TMPEXIT | FR_TMPFILE); + F_SET(sp->frp, FR_NAMECHANGE | FR_EXNAMED); + + /* Notify the screen. */ + (void)sp->gp->scr_rename(sp, sp->frp->name, 1); + } else + set_alt_name(sp, name); + break; + default: + ex_emsg(sp, cmdp->argv[0]->bp, EXM_FILECOUNT); + return (1); + + } + break; + } + + /* + * !!! + * Historically, vi did not permit reads from non-regular files, nor + * did it distinguish between "read !" and "read!", so there was no + * way to "force" it. We permit reading from named pipes too, since + * they didn't exist when the original implementation of vi was done + * and they seem a reasonable addition. + */ + if ((fp = fopen(name, "r")) == NULL || fstat(fileno(fp), &sb)) { + msgq_str(sp, M_SYSERR, name, "%s"); + return (1); + } + if (!S_ISFIFO(sb.st_mode) && !S_ISREG(sb.st_mode)) { + (void)fclose(fp); + msgq(sp, M_ERR, + "145|Only regular files and named pipes may be read"); + return (1); + } + + /* Try and get a lock. */ + if (file_lock(sp, NULL, NULL, fileno(fp), 0) == LOCK_UNAVAIL) + msgq(sp, M_ERR, "146|%s: read lock was unavailable", name); + + rval = ex_readfp(sp, name, fp, &cmdp->addr1, &nlines, 0); + + /* + * In vi, set the cursor to the first line read in, if anything read + * in, otherwise, the address. (Historic vi set it to the line after + * the address regardless, but since that line may not exist we don't + * bother.) + * + * In ex, set the cursor to the last line read in, if anything read in, + * otherwise, the address. + */ + if (F_ISSET(sp, SC_VI)) { + sp->lno = cmdp->addr1.lno; + if (nlines) + ++sp->lno; + } else + sp->lno = cmdp->addr1.lno + nlines; + return (rval); +} + +/* + * ex_readfp -- + * Read lines into the file. + * + * PUBLIC: int ex_readfp __P((SCR *, char *, FILE *, MARK *, recno_t *, int)); + */ +int +ex_readfp(sp, name, fp, fm, nlinesp, silent) + SCR *sp; + char *name; + FILE *fp; + MARK *fm; + recno_t *nlinesp; + int silent; +{ + EX_PRIVATE *exp; + GS *gp; + recno_t lcnt, lno; + size_t len; + u_long ccnt; /* XXX: can't print off_t portably. */ + int nf, rval; + char *p; + + gp = sp->gp; + exp = EXP(sp); + + /* + * Add in the lines from the output. Insertion starts at the line + * following the address. + */ + ccnt = 0; + lcnt = 0; + p = "147|Reading..."; + for (lno = fm->lno; !ex_getline(sp, fp, &len); ++lno, ++lcnt) { + if ((lcnt + 1) % INTERRUPT_CHECK == 0) { + if (INTERRUPTED(sp)) + break; + if (!silent) { + gp->scr_busy(sp, p, + p == NULL ? BUSY_UPDATE : BUSY_ON); + p = NULL; + } + } + if (db_append(sp, 1, lno, exp->ibp, len)) + goto err; + ccnt += len; + } + + if (ferror(fp) || fclose(fp)) + goto err; + + /* Return the number of lines read in. */ + if (nlinesp != NULL) + *nlinesp = lcnt; + + if (!silent) { + p = msg_print(sp, name, &nf); + msgq(sp, M_INFO, + "148|%s: %lu lines, %lu characters", p, lcnt, ccnt); + if (nf) + FREE_SPACE(sp, p, 0); + } + + rval = 0; + if (0) { +err: msgq_str(sp, M_SYSERR, name, "%s"); + (void)fclose(fp); + rval = 1; + } + + if (!silent) + gp->scr_busy(sp, NULL, BUSY_OFF); + return (rval); +} |