summaryrefslogtreecommitdiffstats
path: root/contrib/nvi/ex/ex_read.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/nvi/ex/ex_read.c')
-rw-r--r--contrib/nvi/ex/ex_read.c360
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 0000000..78296ff
--- /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);
+}
OpenPOWER on IntegriCloud