Esempio n. 1
0
File: vi.c Progetto: Alkzndr/freebsd
/*
 * v_init --
 *	Initialize the vi screen.
 */
static int
v_init(SCR *sp)
{
	GS *gp;
	VI_PRIVATE *vip;

	gp = sp->gp;
	vip = VIP(sp);

	/* Switch into vi. */
	if (gp->scr_screen(sp, SC_VI))
		return (1);
	(void)gp->scr_attr(sp, SA_ALTERNATE, 1);

	F_CLR(sp, SC_EX | SC_SCR_EX);
	F_SET(sp, SC_VI);

	/*
	 * Initialize screen values.
	 *
	 * Small windows: see vs_refresh(), section 6a.
	 *
	 * Setup:
	 *	t_minrows is the minimum rows to display
	 *	t_maxrows is the maximum rows to display (rows - 1)
	 *	t_rows is the rows currently being displayed
	 */
	sp->rows = vip->srows = O_VAL(sp, O_LINES);
	sp->cols = O_VAL(sp, O_COLUMNS);
	sp->t_rows = sp->t_minrows = O_VAL(sp, O_WINDOW);
	if (sp->rows != 1) {
		if (sp->t_rows > sp->rows - 1) {
			sp->t_minrows = sp->t_rows = sp->rows - 1;
			msgq(sp, M_INFO,
			    "214|Windows option value is too large, max is %u",
			    (u_int)sp->t_rows);
		}
		sp->t_maxrows = sp->rows - 1;
	} else
		sp->t_maxrows = 1;
	sp->roff = sp->coff = 0;

	/* Create a screen map. */
	CALLOC_RET(sp, HMAP, SMAP *, SIZE_HMAP(sp), sizeof(SMAP));
	TMAP = HMAP + (sp->t_rows - 1);
	HMAP->lno = sp->lno;
	HMAP->coff = 0;
	HMAP->soff = 1;

	/*
	 * Fill the screen map from scratch -- try and center the line.  That
	 * way if we're starting with a file we've seen before, we'll put the
	 * line in the middle, otherwise, it won't work and we'll end up with
	 * the line at the top.
	 */
	F_SET(sp, SC_SCR_REFORMAT | SC_SCR_CENTER);

	/* Invalidate the cursor. */
	F_SET(vip, VIP_CUR_INVALID);

	/* Paint the screen image from scratch. */
	F_SET(vip, VIP_N_EX_PAINT);

	return (0);
}
Esempio n. 2
0
bool
ACE_Message_Queue_Vx::is_full_i (void)
{
  ACE_TRACE ("ACE_Message_Queue_Vx::is_full_i");
  return ::msgQNumMsgs (msgq ()) >= max_messages_;
}
Esempio n. 3
0
/*
 * cl_event --
 *	Return a single event.
 *
 * PUBLIC: int cl_event __P((SCR *, EVENT *, u_int32_t, int));
 */
int
cl_event(SCR *sp, EVENT *evp, u_int32_t flags, int ms)
{
    struct timeval t, *tp;
    CL_PRIVATE *clp;
    size_t lines, columns;
    int changed, nr = 0;
    CHAR_T *wp;
    size_t wlen;
    int rc;

    /*
     * Queue signal based events.  We never clear SIGHUP or SIGTERM events,
     * so that we just keep returning them until the editor dies.
     */
    clp = CLP(sp);
retest:
    if (LF_ISSET(EC_INTERRUPT) || F_ISSET(clp, CL_SIGINT)) {
        if (F_ISSET(clp, CL_SIGINT)) {
            F_CLR(clp, CL_SIGINT);
            evp->e_event = E_INTERRUPT;
        } else
            evp->e_event = E_TIMEOUT;
        return (0);
    }
    if (F_ISSET(clp, CL_SIGHUP | CL_SIGTERM | CL_SIGWINCH)) {
        if (F_ISSET(clp, CL_SIGHUP)) {
            evp->e_event = E_SIGHUP;
            return (0);
        }
        if (F_ISSET(clp, CL_SIGTERM)) {
            evp->e_event = E_SIGTERM;
            return (0);
        }
        if (F_ISSET(clp, CL_SIGWINCH)) {
            F_CLR(clp, CL_SIGWINCH);
            if (cl_ssize(sp, 1, &lines, &columns, &changed))
                return (1);
            if (changed) {
                (void)cl_resize(sp, lines, columns);
                evp->e_event = E_WRESIZE;
                return (0);
            }
            /* No real change, ignore the signal. */
        }
    }

    /* Set timer. */
    if (ms == 0)
        tp = NULL;
    else {
        t.tv_sec = ms / 1000;
        t.tv_usec = (ms % 1000) * 1000;
        tp = &t;
    }

    /* Read input characters. */
read:
    switch (cl_read(sp, LF_ISSET(EC_QUOTED | EC_RAW),
                    clp->ibuf + clp->skip, SIZE(clp->ibuf) - clp->skip, &nr, tp)) {
    case INP_OK:
        rc = INPUT2INT5(sp, clp->cw, clp->ibuf, nr + clp->skip,
                        wp, wlen);
        evp->e_csp = wp;
        evp->e_len = wlen;
        evp->e_event = E_STRING;
        if (rc < 0) {
            int n = -rc;
            memmove(clp->ibuf, clp->ibuf + nr + clp->skip - n, n);
            clp->skip = n;
            if (wlen == 0)
                goto read;
        } else if (rc == 0)
            clp->skip = 0;
        else
            msgq(sp, M_ERR, "323|Invalid input. Truncated.");
        break;
    case INP_EOF:
        evp->e_event = E_EOF;
        break;
    case INP_ERR:
        evp->e_event = E_ERR;
        break;
    case INP_INTR:
        goto retest;
    case INP_TIMEOUT:
        evp->e_event = E_TIMEOUT;
        break;
    default:
        abort();
    }
    return (0);
}
Esempio n. 4
0
/*
 * cscope_add --
 *	The cscope add command.
 */
static int
cscope_add(SCR *sp, EXCMD *cmdp, const CHAR_T *dname)
{
	struct stat sb;
	EX_PRIVATE *exp;
	CSC *csc;
	size_t len;
	int cur_argc;
	const char *dbname;
	char path[MAXPATHLEN];
	const char *np;
	char *npp;
	size_t nlen;

	exp = EXP(sp);

	/*
	 *  0 additional args: usage.
	 *  1 additional args: matched a file.
	 * >1 additional args: object, too many args.
	 */
	cur_argc = cmdp->argc;
	if (argv_exp2(sp, cmdp, dname, STRLEN(dname))) {
		return (1);
	}
	if (cmdp->argc == cur_argc) {
		(void)csc_help(sp, "add");
		return (1);
	}
	if (cmdp->argc == cur_argc + 1)
		dname = cmdp->argv[cur_argc]->bp;
	else {
		INT2CHAR(sp, dname, STRLEN(dname)+1, np, nlen);
		ex_emsg(sp, np, EXM_FILECOUNT);
		return (1);
	}

	INT2CHAR(sp, dname, STRLEN(dname)+1, np, nlen);

	/*
	 * The user can specify a specific file (so they can have multiple
	 * Cscope databases in a single directory) or a directory.  If the
	 * file doesn't exist, we're done.  If it's a directory, append the
	 * standard database file name and try again.  Store the directory
	 * name regardless so that we can use it as a base for searches.
	 */
	if (stat(np, &sb)) {
		msgq(sp, M_SYSERR, "%s", np);
		return (1);
	}
	if (S_ISDIR(sb.st_mode)) {
		(void)snprintf(path, sizeof(path),
		    "%s/%s", np, CSCOPE_DBFILE);
		if (stat(path, &sb)) {
			msgq(sp, M_SYSERR, "%s", path);
			return (1);
		}
		dbname = CSCOPE_DBFILE;
	} else if ((npp = strrchr(np, '/')) != NULL) {
		*npp = '\0';
		dbname = npp + 1;
	} else {
		dbname = np;
		np = ".";
	}

	/* Allocate a cscope connection structure and initialize its fields. */
	len = strlen(np);
	CALLOC_RET(sp, csc, CSC *, 1, sizeof(CSC) + len);
	csc->dname = csc->buf;
	csc->dlen = len;
	memcpy(csc->dname, np, len);
	csc->mtime = sb.st_mtime;

	/* Get the search paths for the cscope. */
	if (get_paths(sp, csc))
		goto err;

	/* Start the cscope process. */
	if (run_cscope(sp, csc, dbname))
		goto err;

	/*
	 * Add the cscope connection to the screen's list.  From now on, 
	 * on error, we have to call terminate, which expects the csc to
	 * be on the chain.
	 */
	LIST_INSERT_HEAD(&exp->cscq, csc, q);

	/* Read the initial prompt from the cscope to make sure it's okay. */
	return read_prompt(sp, csc);

err:	free(csc);
	return (1);
}
Esempio n. 5
0
/*
 * screen_init --
 *	Do the default initialization of an SCR structure.
 *
 * PUBLIC: int screen_init __P((GS *, SCR *, SCR **));
 */
int
screen_init(GS *gp, SCR *orig, SCR **spp)
{
	SCR *sp;
	size_t len;

	*spp = NULL;
	CALLOC_RET(orig, sp, SCR *, 1, sizeof(SCR));
	*spp = sp;

/* INITIALIZED AT SCREEN CREATE. */
	sp->id = ++gp->id;
	sp->refcnt = 1;

	sp->gp = gp;			/* All ref the GS structure. */

	sp->ccnt = 2;			/* Anything > 1 */

	/*
	 * XXX
	 * sp->defscroll is initialized by the opts_init() code because
	 * we don't have the option information yet.
	 */

	CIRCLEQ_INIT(&sp->tiq);

/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */
	if (orig == NULL) {
		sp->searchdir = NOTSET;
	} else {
		sp->wp = orig->wp;

		/* Alternate file name. */
		if (orig->alt_name != NULL &&
		    (sp->alt_name = strdup(orig->alt_name)) == NULL)
			goto mem;

		/* Last executed at buffer. */
		if (F_ISSET(orig, SC_AT_SET)) {
			F_SET(sp, SC_AT_SET);
			sp->at_lbuf = orig->at_lbuf;
		}

		/* Retain searching/substitution information. */
		sp->searchdir = orig->searchdir == NOTSET ? NOTSET : FORWARD;
		if (orig->re != NULL && (sp->re =
		    v_wstrdup(sp, orig->re, orig->re_len)) == NULL)
			goto mem;
		sp->re_len = orig->re_len;
		if (orig->subre != NULL && (sp->subre =
		    v_wstrdup(sp, orig->subre, orig->subre_len)) == NULL)
			goto mem;
		sp->subre_len = orig->subre_len;
		if (orig->repl != NULL && (sp->repl =
		    v_wstrdup(sp, orig->repl, orig->repl_len)) == NULL)
			goto mem;
		sp->repl_len = orig->repl_len;
		if (orig->newl_len) {
			len = orig->newl_len * sizeof(size_t);
			MALLOC(sp, sp->newl, size_t *, len);
			if (sp->newl == NULL) {
mem:				msgq(orig, M_SYSERR, NULL);
				goto err;
			}
			sp->newl_len = orig->newl_len;
			sp->newl_cnt = orig->newl_cnt;
			memcpy(sp->newl, orig->newl, len);
		}

		if (opts_copy(orig, sp))
			goto err;

		F_SET(sp, F_ISSET(orig, SC_EX | SC_VI));
	}
Esempio n. 6
0
static int
s(SCR *sp, EXCMD *cmdp, char *s, regex_t *re, u_int flags)
{
	EVENT ev;
	MARK from, to;
	TEXTH tiq;
	recno_t elno, lno, slno;
	regmatch_t match[10];
	size_t blen, cnt, last, lbclen, lblen, len, llen;
	size_t offset, saved_offset, scno;
	int lflag, nflag, pflag, rflag;
	int didsub, do_eol_match, eflags, empty_ok, eval;
	int linechanged, matched, quit, rval;
	unsigned long ul;
	char *bp, *lb;

	NEEDFILE(sp, cmdp);

	slno = sp->lno;
	scno = sp->cno;

	/*
	 * !!!
	 * Historically, the 'g' and 'c' suffices were always toggled as flags,
	 * so ":s/A/B/" was the same as ":s/A/B/ccgg".  If O_EDCOMPATIBLE was
	 * not set, they were initialized to 0 for all substitute commands.  If
	 * O_EDCOMPATIBLE was set, they were initialized to 0 only if the user
	 * specified substitute/replacement patterns (see ex_s()).
	 */
	if (!O_ISSET(sp, O_EDCOMPATIBLE))
		sp->c_suffix = sp->g_suffix = 0;

	/*
	 * Historic vi permitted the '#', 'l' and 'p' options in vi mode, but
	 * it only displayed the last change.  I'd disallow them, but they are
	 * useful in combination with the [v]global commands.  In the current
	 * model the problem is combining them with the 'c' flag -- the screen
	 * would have to flip back and forth between the confirm screen and the
	 * ex print screen, which would be pretty awful.  We do display all
	 * changes, though, for what that's worth.
	 *
	 * !!!
	 * Historic vi was fairly strict about the order of "options", the
	 * count, and "flags".  I'm somewhat fuzzy on the difference between
	 * options and flags, anyway, so this is a simpler approach, and we
	 * just take it them in whatever order the user gives them.  (The ex
	 * usage statement doesn't reflect this.)
	 */
	lflag = nflag = pflag = rflag = 0;
	if (s == NULL)
		goto noargs;
	for (lno = OOBLNO; *s != '\0'; ++s)
		switch (*s) {
		case ' ':
		case '\t':
			continue;
		case '+':
			++cmdp->flagoff;
			break;
		case '-':
			--cmdp->flagoff;
			break;
		case '0': case '1': case '2': case '3': case '4':
		case '5': case '6': case '7': case '8': case '9':
			if (lno != OOBLNO)
				goto usage;
			errno = 0;
			if ((ul = strtoul(s, &s, 10)) >= UINT_MAX)
				errno = ERANGE;
			if (*s == '\0')		/* Loop increment correction. */
				--s;
			if (errno == ERANGE) {
				if (ul >= UINT_MAX)
					msgq(sp, M_ERR, "Count overflow");
				else
					msgq(sp, M_SYSERR, NULL);
				return (1);
			}
			lno = (recno_t)ul;
			/*
			 * In historic vi, the count was inclusive from the
			 * second address.
			 */
			cmdp->addr1.lno = cmdp->addr2.lno;
			cmdp->addr2.lno += lno - 1;
			if (!db_exist(sp, cmdp->addr2.lno) &&
			    db_last(sp, &cmdp->addr2.lno))
				return (1);
			break;
		case '#':
			nflag = 1;
			break;
		case 'c':
			sp->c_suffix = !sp->c_suffix;

			/* Ex text structure initialization. */
			if (F_ISSET(sp, SC_EX)) {
				memset(&tiq, 0, sizeof(TEXTH));
				TAILQ_INIT(&tiq);
			}
			break;
		case 'g':
			sp->g_suffix = !sp->g_suffix;
			break;
		case 'l':
			lflag = 1;
			break;
		case 'p':
			pflag = 1;
			break;
		case 'r':
			if (LF_ISSET(SUB_FIRST)) {
				msgq(sp, M_ERR,
		    "Regular expression specified; r flag meaningless");
				return (1);
			}
			if (!F_ISSET(sp, SC_RE_SEARCH)) {
				ex_emsg(sp, NULL, EXM_NOPREVRE);
				return (1);
			}
			rflag = 1;
			re = &sp->re_c;
			break;
		default:
			goto usage;
		}

	if (*s != '\0' || (!rflag && LF_ISSET(SUB_MUSTSETR))) {
usage:		ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
		return (1);
	}

noargs:	if (F_ISSET(sp, SC_VI) && sp->c_suffix && (lflag || nflag || pflag)) {
		msgq(sp, M_ERR,
"The #, l and p flags may not be combined with the c flag in vi mode");
		return (1);
	}

	/*
	 * bp:		if interactive, line cache
	 * blen:	if interactive, line cache length
	 * lb:		build buffer pointer.
	 * lbclen:	current length of built buffer.
	 * lblen;	length of build buffer.
	 */
	bp = lb = NULL;
	blen = lbclen = lblen = 0;

	/* For each line... */
	for (matched = quit = 0, lno = cmdp->addr1.lno,
	    elno = cmdp->addr2.lno; !quit && lno <= elno; ++lno) {

		/* Someone's unhappy, time to stop. */
		if (INTERRUPTED(sp))
			break;

		/* Get the line. */
		if (db_get(sp, lno, DBG_FATAL, &s, &llen))
			goto err;

		/*
		 * Make a local copy if doing confirmation -- when calling
		 * the confirm routine we're likely to lose the cached copy.
		 */
		if (sp->c_suffix) {
			if (bp == NULL) {
				GET_SPACE_RET(sp, bp, blen, llen);
			} else
				ADD_SPACE_RET(sp, bp, blen, llen);
			memcpy(bp, s, llen);
			s = bp;
		}

		/* Start searching from the beginning. */
		offset = 0;
		len = llen;

		/* Reset the build buffer offset. */
		lbclen = 0;

		/* Reset empty match flag. */
		empty_ok = 1;

		/*
		 * We don't want to have to do a setline if the line didn't
		 * change -- keep track of whether or not this line changed.
		 * If doing confirmations, don't want to keep setting the
		 * line if change is refused -- keep track of substitutions.
		 */
		didsub = linechanged = 0;

		/* New line, do an EOL match. */
		do_eol_match = 1;

		/* It's not nul terminated, but we pretend it is. */
		eflags = REG_STARTEND;

		/*
		 * The search area is from s + offset to the EOL.
		 *
		 * Generally, match[0].rm_so is the offset of the start
		 * of the match from the start of the search, and offset
		 * is the offset of the start of the last search.
		 */
nextmatch:	match[0].rm_so = 0;
		match[0].rm_eo = len;

		/* Get the next match. */
		eval = regexec(re, (char *)s + offset, 10, match, eflags);

		/*
		 * There wasn't a match or if there was an error, deal with
		 * it.  If there was a previous match in this line, resolve
		 * the changes into the database.  Otherwise, just move on.
		 */
		if (eval == REG_NOMATCH)
			goto endmatch;
		if (eval != 0) {
			re_error(sp, eval, re);
			goto err;
		}
		matched = 1;

		/* Only the first search can match an anchored expression. */
		eflags |= REG_NOTBOL;

		/*
		 * !!!
		 * It's possible to match 0-length strings -- for example, the
		 * command s;a*;X;, when matched against the string "aabb" will
		 * result in "XbXbX", i.e. the matches are "aa", the space
		 * between the b's and the space between the b's and the end of
		 * the string.  There is a similar space between the beginning
		 * of the string and the a's.  The rule that we use (because vi
		 * historically used it) is that any 0-length match, occurring
		 * immediately after a match, is ignored.  Otherwise, the above
		 * example would have resulted in "XXbXbX".  Another example is
		 * incorrectly using " *" to replace groups of spaces with one
		 * space.
		 *
		 * The way we do this is that if we just had a successful match,
		 * the starting offset does not skip characters, and the match
		 * is empty, ignore the match and move forward.  If there's no
		 * more characters in the string, we were attempting to match
		 * after the last character, so quit.
		 */
		if (!empty_ok && match[0].rm_so == 0 && match[0].rm_eo == 0) {
			empty_ok = 1;
			if (len == 0)
				goto endmatch;
			BUILD(sp, s + offset, 1)
			++offset;
			--len;
			goto nextmatch;
		}

		/* Confirm change. */
		if (sp->c_suffix) {
			/*
			 * Set the cursor position for confirmation.  Note,
			 * if we matched on a '$', the cursor may be past
			 * the end of line.
			 */
			from.lno = to.lno = lno;
			from.cno = match[0].rm_so + offset;
			to.cno = match[0].rm_eo + offset;
			/*
			 * Both ex and vi have to correct for a change before
			 * the first character in the line.
			 */
			if (llen == 0)
				from.cno = to.cno = 0;
			if (F_ISSET(sp, SC_VI)) {
				/*
				 * Only vi has to correct for a change after
				 * the last character in the line.
				 *
				 * XXX
				 * It would be nice to change the vi code so
				 * that we could display a cursor past EOL.
				 */
				if (to.cno >= llen)
					to.cno = llen - 1;
				if (from.cno >= llen)
					from.cno = llen - 1;

				sp->lno = from.lno;
				sp->cno = from.cno;
				if (vs_refresh(sp, 1))
					goto err;

				vs_update(sp, "Confirm change? [n]", NULL);

				if (v_event_get(sp, &ev, 0, 0))
					goto err;
				switch (ev.e_event) {
				case E_CHARACTER:
					break;
				case E_EOF:
				case E_ERR:
				case E_INTERRUPT:
					goto lquit;
				default:
					v_event_err(sp, &ev);
					goto lquit;
				}
			} else {
				if (ex_print(sp, cmdp, &from, &to, 0) ||
				    ex_scprint(sp, &from, &to))
					goto lquit;
				if (ex_txt(sp, &tiq, 0, TXT_CR))
					goto err;
				ev.e_c = TAILQ_FIRST(&tiq)->lb[0];
			}

			switch (ev.e_c) {
			case CH_YES:
				break;
			default:
			case CH_NO:
				didsub = 0;
				BUILD(sp, s +offset, match[0].rm_eo);
				goto skip;
			case CH_QUIT:
				/* Set the quit/interrupted flags. */
lquit:				quit = 1;
				F_SET(sp->gp, G_INTERRUPTED);

				/*
				 * Resolve any changes, then return to (and
				 * exit from) the main loop.
				 */
				goto endmatch;
			}
		}

		/*
		 * Set the cursor to the last position changed, converting
		 * from 1-based to 0-based.
		 */
		sp->lno = lno;
		sp->cno = match[0].rm_so;

		/* Copy the bytes before the match into the build buffer. */
		BUILD(sp, s + offset, match[0].rm_so);

		/* Substitute the matching bytes. */
		didsub = 1;
		if (re_sub(sp, s + offset, &lb, &lbclen, &lblen, match))
			goto err;

		/* Set the change flag so we know this line was modified. */
		linechanged = 1;

		/* Move past the matched bytes. */
skip:		offset += match[0].rm_eo;
		len -= match[0].rm_eo;

		/* A match cannot be followed by an empty pattern. */
		empty_ok = 0;

		/*
		 * If doing a global change with confirmation, we have to
		 * update the screen.  The basic idea is to store the line
		 * so the screen update routines can find it, and restart.
		 */
		if (didsub && sp->c_suffix && sp->g_suffix) {
			/*
			 * The new search offset will be the end of the
			 * modified line.
			 */
			saved_offset = lbclen;

			/* Copy the rest of the line. */
			if (len)
				BUILD(sp, s + offset, len)

			/* Set the new offset. */
			offset = saved_offset;

			/* Store inserted lines, adjusting the build buffer. */
			last = 0;
			if (sp->newl_cnt) {
				for (cnt = 0;
				    cnt < sp->newl_cnt; ++cnt, ++lno, ++elno) {
					if (db_insert(sp, lno,
					    lb + last, sp->newl[cnt] - last))
						goto err;
					last = sp->newl[cnt] + 1;
					++sp->rptlines[L_ADDED];
				}
				lbclen -= last;
				offset -= last;
				sp->newl_cnt = 0;
			}

			/* Store and retrieve the line. */
			if (db_set(sp, lno, lb + last, lbclen))
				goto err;
			if (db_get(sp, lno, DBG_FATAL, &s, &llen))
				goto err;
			ADD_SPACE_RET(sp, bp, blen, llen)
			memcpy(bp, s, llen);
			s = bp;
			len = llen - offset;

			/* Restart the build. */
			lbclen = 0;
			BUILD(sp, s, offset);

			/*
			 * If we haven't already done the after-the-string
			 * match, do one.  Set REG_NOTEOL so the '$' pattern
			 * only matches once.
			 */
			if (!do_eol_match)
				goto endmatch;
			if (offset == len) {
				do_eol_match = 0;
				eflags |= REG_NOTEOL;
			}
			goto nextmatch;
		}

		/*
		 * If it's a global:
		 *
		 * If at the end of the string, do a test for the after
		 * the string match.  Set REG_NOTEOL so the '$' pattern
		 * only matches once.
		 */
		if (sp->g_suffix && do_eol_match) {
			if (len == 0) {
				do_eol_match = 0;
				eflags |= REG_NOTEOL;
			}
			goto nextmatch;
		}

endmatch:	if (!linechanged)
			continue;

		/* Copy any remaining bytes into the build buffer. */
		if (len)
			BUILD(sp, s + offset, len)

		/* Store inserted lines, adjusting the build buffer. */
		last = 0;
		if (sp->newl_cnt) {
			for (cnt = 0;
			    cnt < sp->newl_cnt; ++cnt, ++lno, ++elno) {
				if (db_insert(sp,
				    lno, lb + last, sp->newl[cnt] - last))
					goto err;
				last = sp->newl[cnt] + 1;
				++sp->rptlines[L_ADDED];
			}
			lbclen -= last;
			sp->newl_cnt = 0;
		}

		/* Store the changed line. */
		if (db_set(sp, lno, lb + last, lbclen))
			goto err;

		/* Update changed line counter. */
		if (sp->rptlchange != lno) {
			sp->rptlchange = lno;
			++sp->rptlines[L_CHANGED];
		}

		/*
		 * !!!
		 * Display as necessary.  Historic practice is to only
		 * display the last line of a line split into multiple
		 * lines.
		 */
		if (lflag || nflag || pflag) {
			from.lno = to.lno = lno;
			from.cno = to.cno = 0;
			if (lflag)
				(void)ex_print(sp, cmdp, &from, &to, E_C_LIST);
			if (nflag)
				(void)ex_print(sp, cmdp, &from, &to, E_C_HASH);
			if (pflag)
				(void)ex_print(sp, cmdp, &from, &to, E_C_PRINT);
		}
	}

	/*
	 * !!!
	 * Historically, vi attempted to leave the cursor at the same place if
	 * the substitution was done at the current cursor position.  Otherwise
	 * it moved it to the first non-blank of the last line changed.  There
	 * were some problems: for example, :s/$/foo/ with the cursor on the
	 * last character of the line left the cursor on the last character, or
	 * the & command with multiple occurrences of the matching string in the
	 * line usually left the cursor in a fairly random position.
	 *
	 * We try to do the same thing, with the exception that if the user is
	 * doing substitution with confirmation, we move to the last line about
	 * which the user was consulted, as opposed to the last line that they
	 * actually changed.  This prevents a screen flash if the user doesn't
	 * change many of the possible lines.
	 */
	if (!sp->c_suffix && (sp->lno != slno || sp->cno != scno)) {
		sp->cno = 0;
		(void)nonblank(sp, sp->lno, &sp->cno);
	}

	/*
	 * If not in a global command, and nothing matched, say so.
	 * Else, if none of the lines displayed, put something up.
	 */
	rval = 0;
	if (!matched) {
		if (!F_ISSET(sp, SC_EX_GLOBAL)) {
			msgq(sp, M_ERR, "No match found");
			goto err;
		}
	} else if (!lflag && !nflag && !pflag)
		F_SET(cmdp, E_AUTOPRINT);

	if (0) {
err:		rval = 1;
	}

	if (bp != NULL)
		FREE_SPACE(sp, bp, blen);
	if (lb != NULL)
		free(lb);
	return (rval);
}
Esempio n. 7
0
/*
 * v_match -- %
 *	Search to matching character.
 *
 * PUBLIC: int v_match(SCR *, VICMD *);
 */
int
v_match(SCR *sp, VICMD *vp)
{
	VCS cs;
	MARK *mp;
	size_t cno, len, off;
	int cnt, isempty, matchc, startc, (*gc)(SCR *, VCS *);
	CHAR_T *p;
	CHAR_T *cp;
	const CHAR_T *match_chars;

	/*
	 * Historically vi would match (), {} and [] however
	 * an update included <>.  This is ok for editing HTML
	 * but a pain in the butt for C source.
	 * Making it an option lets the user decide what is 'right'.
	 */
	match_chars = VIP(sp)->mcs;

	/*
	 * !!!
	 * Historic practice; ignore the count.
	 *
	 * !!!
	 * Historical practice was to search for the initial character in the
	 * forward direction only.
	 */
	if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
		if (isempty)
			goto nomatch;
		return (1);
	}
	for (off = vp->m_start.cno;; ++off) {
		if (off >= len) {
nomatch:		msgq(sp, M_BERR, "184|No match character on this line");
			return (1);
		}
		startc = p[off];
		cp = STRCHR(match_chars, startc);
		if (cp != NULL) {
			cnt = cp - match_chars;
			matchc = match_chars[cnt ^ 1];
			gc = cnt & 1 ? cs_prev : cs_next;
			break;
		}
	}

	cs.cs_lno = vp->m_start.lno;
	cs.cs_cno = off;
	if (cs_init(sp, &cs))
		return (1);
	for (cnt = 1;;) {
		if (gc(sp, &cs))
			return (1);
		if (cs.cs_flags != 0) {
			if (cs.cs_flags == CS_EOF || cs.cs_flags == CS_SOF)
				break;
			continue;
		}
		if (cs.cs_ch == startc)
			++cnt;
		else if (cs.cs_ch == matchc && --cnt == 0)
			break;
	}
	if (cnt) {
		msgq(sp, M_BERR, "185|Matching character not found");
		return (1);
	}

	vp->m_stop.lno = cs.cs_lno;
	vp->m_stop.cno = cs.cs_cno;

	/*
	 * If moving right, non-motion commands move to the end of the range.
	 * Delete and yank stay at the start.
	 *
	 * If moving left, all commands move to the end of the range.
	 *
	 * !!!
	 * Don't correct for leftward movement -- historic vi deleted the
	 * starting cursor position when deleting to a match.
	 */
	if (vp->m_start.lno < vp->m_stop.lno ||
	    (vp->m_start.lno == vp->m_stop.lno &&
	    vp->m_start.cno < vp->m_stop.cno))
		vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
	else
		vp->m_final = vp->m_stop;

	/*
	 * !!!
	 * If the motion is across lines, and the earliest cursor position
	 * is at or before any non-blank characters in the line, i.e. the
	 * movement is cutting all of the line's text, and the later cursor
	 * position has nothing other than whitespace characters between it
	 * and the end of its line, the buffer is in line mode.
	 */
	if (!ISMOTION(vp) || vp->m_start.lno == vp->m_stop.lno)
		return (0);
	mp = vp->m_start.lno < vp->m_stop.lno ? &vp->m_start : &vp->m_stop;
	if (mp->cno != 0) {
		cno = 0;
		if (nonblank(sp, mp->lno, &cno))
			return (1);
		if (cno < mp->cno)
			return (0);
	}
	mp = vp->m_start.lno < vp->m_stop.lno ? &vp->m_stop : &vp->m_start;
	if (db_get(sp, mp->lno, DBG_FATAL, &p, &len))
		return (1);
	for (p += mp->cno + 1, len -= mp->cno; --len; ++p)
		if (!isblank(*p))
			return (0);
	F_SET(vp, VM_LMODE);
	return (0);
}
Esempio n. 8
0
/*
 * sscr_insert --
 *	Take a line from the shell and insert it into the file.
 */
static int
sscr_insert(SCR *sp)
{
	struct timeval tv;
	char *endp, *p, *t;
	SCRIPT *sc;
	fd_set rdfd;
	db_recno_t lno;
	size_t len;
	ssize_t nr;
	char bp[1024];
	const CHAR_T *ip;
	size_t ilen = 0;

	/* Find out where the end of the file is. */
	if (db_last(sp, &lno))
		return (1);

	endp = bp;

	/* Read the characters. */
	sc = sp->script;
more:	switch (nr = read(sc->sh_master, endp, bp + sizeof(bp) - endp)) {
	case  0:			/* EOF; shell just exited. */
		sscr_end(sp);
		return (0);
	case -1:			/* Error or interrupt. */
		msgq(sp, M_SYSERR, "shell");
		return (1);
	default:
		endp += nr;
		break;
	}

	/* Append the lines into the file. */
	for (p = t = bp; p < endp; ++p) {
		if (*p == '\r' || *p == '\n') {
			len = p - t;
			if (CHAR2INT(sp, t, len, ip, ilen) ||
			    db_append(sp, 1, lno++, ip, ilen))
				return (1);
			t = p + 1;
		}
	}
	/*
	 * If the last thing from the shell isn't another prompt, wait up to
	 * 1/10 of a second for more stuff to show up, so that we don't break
	 * the output into two separate lines.  Don't want to hang indefinitely
	 * because some program is hanging, confused the shell, or whatever.
	 * Note that sc->sh_prompt can be NULL here.
	 */
	len = p - t;
	if (sc->sh_prompt == NULL || len != sc->sh_prompt_len ||
	    memcmp(t, sc->sh_prompt, len) != 0) {
		tv.tv_sec = 0;
		tv.tv_usec = 100000;
		FD_ZERO(&rdfd);
		FD_SET(sc->sh_master, &rdfd);
		if (select(sc->sh_master + 1, &rdfd, NULL, NULL, &tv) == 1) {
			if (len == sizeof(bp)) {
				if (CHAR2INT(sp, t, len, ip, ilen) ||
				    db_append(sp, 1, lno++, ip, ilen))
					return (1);
				endp = bp;
			} else {
				memmove(bp, t, len);
				endp = bp + len;
			}
			goto more;
		}
		if (sscr_setprompt(sp, t, len))
			return (1);
	}

	/* Append the remains into the file, and the cursor moves to EOF. */
	if (len > 0) {
		if (CHAR2INT(sp, t, len, ip, ilen) ||
		    db_append(sp, 1, lno++, ip, ilen))
			return (1);
		sp->cno = ilen - 1;
	} else
		sp->cno = 0;
	sp->lno = lno;
	return (vs_refresh(sp, 1));
}
Esempio n. 9
0
File: exf.c Progetto: fishman/nvi
/*
 * file_init --
 *	Start editing a file, based on the FREF structure.  If successsful,
 *	let go of any previous file.  Don't release the previous file until
 *	absolutely sure we have the new one.
 *
 * PUBLIC: int file_init __P((SCR *, FREF *, char *, int));
 */
int
file_init(SCR *sp, FREF *frp, char *rcv_name, int flags)
{
    EXF *ep;
    struct stat sb;
    size_t psize;
    int fd, exists, open_err, readonly, stolen;
    char *oname, tname[MAXPATHLEN];

    stolen = open_err = readonly = 0;

    /*
     * If the file is a recovery file, let the recovery code handle it.
     * Clear the FR_RECOVER flag first -- the recovery code does set up,
     * and then calls us!  If the recovery call fails, it's probably
     * because the named file doesn't exist.  So, move boldly forward,
     * presuming that there's an error message the user will get to see.
     */
    if (F_ISSET(frp, FR_RECOVER)) {
        F_CLR(frp, FR_RECOVER);
        return (rcv_read(sp, frp));
    }

    /*
     * Required FRP initialization; the only flag we keep is the
     * cursor information.
     */
    F_CLR(frp, ~FR_CURSORSET);

    /*
     * Scan the user's path to find the file that we're going to
     * try and open.
     */
    if (file_spath(sp, frp, &sb, &exists))
        return (1);

    /*
     * Check whether we already have this file opened in some
     * other screen.
     */
    if (exists) {
        EXF *exfp;
        for (exfp = sp->gp->exfq.cqh_first;
                exfp != (EXF *)&sp->gp->exfq; exfp = exfp->q.cqe_next) {
            if (exfp->mdev == sb.st_dev &&
                    exfp->minode == sb.st_ino &&
                    (exfp != sp->ep || exfp->refcnt > 1)) {
                ep = exfp;
                goto postinit;
            }
        }
    }

    /*
     * Required EXF initialization:
     *	Flush the line caches.
     *	Default recover mail file fd to -1.
     *	Set initial EXF flag bits.
     */
    CALLOC_RET(sp, ep, EXF *, 1, sizeof(EXF));
    CIRCLEQ_INIT(&ep->scrq);
    sp->c_lno = ep->c_nlines = OOBLNO;
    ep->rcv_fd = ep->fcntl_fd = -1;
    F_SET(ep, F_FIRSTMODIFY);

    /*
     * If no name or backing file, for whatever reason, create a backing
     * temporary file, saving the temp file name so we can later unlink
     * it.  If the user never named this file, copy the temporary file name
     * to the real name (we display that until the user renames it).
     */
    oname = frp->name;
    if (LF_ISSET(FS_OPENERR) || oname == NULL || !exists) {
        if (opts_empty(sp, O_TMP_DIRECTORY, 0))
            goto err;
        (void)snprintf(tname, sizeof(tname),
                       "%s/vi.XXXXXX", O_STR(sp, O_TMP_DIRECTORY));
        if ((fd = mkstemp(tname)) == -1) {
            msgq(sp, M_SYSERR,
                 "237|Unable to create temporary file");
            goto err;
        }
        (void)close(fd);

        if (frp->name == NULL)
            F_SET(frp, FR_TMPFILE);
        if ((frp->tname = strdup(tname)) == NULL ||
                (frp->name == NULL &&
                 (frp->name = strdup(tname)) == NULL)) {
            if (frp->tname != NULL) {
                free(frp->tname);
            }
            msgq(sp, M_SYSERR, NULL);
            (void)unlink(tname);
            goto err;
        }
        oname = frp->tname;
        psize = 1024;
        if (!LF_ISSET(FS_OPENERR))
            F_SET(frp, FR_NEWFILE);

        time(&ep->mtime);
    } else {
        /*
         * XXX
         * A seat of the pants calculation: try to keep the file in
         * 15 pages or less.  Don't use a page size larger than 10K
         * (vi should have good locality) or smaller than 1K.
         */
        psize = ((sb.st_size / 15) + 1023) / 1024;
        if (psize > 10)
            psize = 10;
        if (psize == 0)
            psize = 1;
        psize *= 1024;

        F_SET(ep, F_DEVSET);
        ep->mdev = sb.st_dev;
        ep->minode = sb.st_ino;

        ep->mtime = sb.st_mtime;

        if (!S_ISREG(sb.st_mode))
            msgq_str(sp, M_ERR, oname,
                     "238|Warning: %s is not a regular file");
    }

    /* Set up recovery. */
    if (rcv_name == NULL) {
        /* ep->rcv_path NULL if rcv_tmp fails */
        rcv_tmp(sp, ep, frp->name);
    } else {
        if ((ep->rcv_path = strdup(rcv_name)) == NULL) {
            msgq(sp, M_SYSERR, NULL);
            goto err;
        }
        F_SET(ep, F_MODIFIED);
    }

    if (db_setup(sp, ep))
        goto err;

    /* Open a db structure. */
    if ((sp->db_error = db_create(&ep->db, 0, 0)) != 0) {
        msgq(sp, M_DBERR, "db_create");
        goto err;
    }

    ep->db->set_re_delim(ep->db, '\n');		/* Always set. */
    ep->db->set_pagesize(ep->db, psize);
    ep->db->set_flags(ep->db, DB_RENUMBER | DB_SNAPSHOT);
    if (rcv_name == NULL)
        ep->db->set_re_source(ep->db, oname);

    /*
     * Don't let db use mmap when using fcntl for locking
     */
#ifdef HAVE_LOCK_FCNTL
#define NOMMAPIFFCNTL DB_NOMMAP
#else
#define NOMMAPIFFCNTL 0
#endif

#define _DB_OPEN_MODE	S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH

    if ((sp->db_error = db_open(ep->db, ep->rcv_path, DB_RECNO,
                                ((rcv_name == 0) ? DB_TRUNCATE : 0) | VI_DB_THREAD | NOMMAPIFFCNTL,
                                _DB_OPEN_MODE)) != 0) {
        msgq_str(sp,
                 M_DBERR, rcv_name == NULL ? oname : rcv_name, "%s");
        /*
         * !!!
         * Historically, vi permitted users to edit files that couldn't
         * be read.  This isn't useful for single files from a command
         * line, but it's quite useful for "vi *.c", since you can skip
         * past files that you can't read.
         */
        ep->db = NULL; /* Don't close it; it wasn't opened */

        if (LF_ISSET(FS_OPENERR))
            goto err;

        open_err = 1;
        goto oerr;
    }

    /* re_source is loaded into the database.
     * Close it and reopen it in the environment.
     */
    if ((sp->db_error = ep->db->close(ep->db, 0))) {
        msgq(sp, M_DBERR, "close");
        goto err;
    }
    if ((sp->db_error = db_create(&ep->db, ep->env, 0)) != 0) {
        msgq(sp, M_DBERR, "db_create 2");
        goto err;
    }
    if ((sp->db_error = db_open(ep->db, ep->rcv_path, DB_RECNO,
                                VI_DB_THREAD | NOMMAPIFFCNTL, _DB_OPEN_MODE)) != 0) {
        msgq_str(sp,
                 M_DBERR, ep->rcv_path, "%s");
        goto err;
    }

    /*
     * Do the remaining things that can cause failure of the new file,
     * mark and logging initialization.
     */
    if (mark_init(sp, ep) || log_init(sp, ep))
        goto err;

postinit:
    /*
     * Set the alternate file name to be the file we're discarding.
     *
     * !!!
     * Temporary files can't become alternate files, so there's no file
     * name.  This matches historical practice, although it could only
     * happen in historical vi as the result of the initial command, i.e.
     * if vi was executed without a file name.
     */
    if (LF_ISSET(FS_SETALT))
        set_alt_name(sp, sp->frp == NULL ||
                     F_ISSET(sp->frp, FR_TMPFILE) ? NULL : sp->frp->name);

    /*
     * Close the previous file; if that fails, close the new one and run
     * for the border.
     *
     * !!!
     * There's a nasty special case.  If the user edits a temporary file,
     * and then does an ":e! %", we need to re-initialize the backing
     * file, but we can't change the name.  (It's worse -- we're dealing
     * with *names* here, we can't even detect that it happened.)  Set a
     * flag so that the file_end routine ignores the backing information
     * of the old file if it happens to be the same as the new one.
     *
     * !!!
     * Side-effect: after the call to file_end(), sp->frp may be NULL.
     */
    if (sp->ep != NULL) {
        F_SET(frp, FR_DONTDELETE);
        if (file_end(sp, NULL, LF_ISSET(FS_FORCE))) {
            (void)file_end(sp, ep, 1);
            goto err;
        }
        sp->ep = NULL;
        F_CLR(frp, FR_DONTDELETE);
    }

    /*
     * Lock the file; if it's a recovery file, it should already be
     * locked.  Note, we acquire the lock after the previous file
     * has been ended, so that we don't get an "already locked" error
     * for ":edit!".
     *
     * XXX
     * While the user can't interrupt us between the open and here,
     * there's a race between the dbopen() and the lock.  Not much
     * we can do about it.
     *
     * XXX
     * We don't make a big deal of not being able to lock the file.  As
     * locking rarely works over NFS, and often fails if the file was
     * mmap(2)'d, it's far too common to do anything like print an error
     * message, let alone make the file readonly.  At some future time,
     * when locking is a little more reliable, this should change to be
     * an error.
     */
    if (rcv_name == NULL && ep->refcnt == 0) {
        if ((ep->fd = open(oname, O_RDWR)) == -1)
            goto no_lock;

        switch (file_lock(sp, oname, &ep->fcntl_fd, ep->fd, 1)) {
        case LOCK_FAILED:
no_lock:
            F_SET(frp, FR_UNLOCKED);
            break;
        case LOCK_UNAVAIL:
            readonly = 1;
            msgq_str(sp, M_INFO, oname,
                     "239|%s already locked, session is read-only");
            break;
        case LOCK_SUCCESS:
            break;
        }
    }

    /*
         * Historically, the readonly edit option was set per edit buffer in
         * vi, unless the -R command-line option was specified or the program
         * was executed as "view".  (Well, to be truthful, if the letter 'w'
         * occurred anywhere in the program name, but let's not get into that.)
     * So, the persistant readonly state has to be stored in the screen
     * structure, and the edit option value toggles with the contents of
     * the edit buffer.  If the persistant readonly flag is set, set the
     * readonly edit option.
     *
     * Otherwise, try and figure out if a file is readonly.  This is a
     * dangerous thing to do.  The kernel is the only arbiter of whether
     * or not a file is writeable, and the best that a user program can
     * do is guess.  Obvious loopholes are files that are on a file system
     * mounted readonly (access catches this one on a few systems), or
     * alternate protection mechanisms, ACL's for example, that we can't
     * portably check.  Lots of fun, and only here because users whined.
     *
     * !!!
     * Historic vi displayed the readonly message if none of the file
     * write bits were set, or if an an access(2) call on the path
     * failed.  This seems reasonable.  If the file is mode 444, root
     * users may want to know that the owner of the file did not expect
     * it to be written.
     *
     * Historic vi set the readonly bit if no write bits were set for
     * a file, even if the access call would have succeeded.  This makes
     * the superuser force the write even when vi expects that it will
     * succeed.  I'm less supportive of this semantic, but it's historic
     * practice and the conservative approach to vi'ing files as root.
     *
     * It would be nice if there was some way to update this when the user
     * does a "^Z; chmod ...".  The problem is that we'd first have to
     * distinguish between readonly bits set because of file permissions
     * and those set for other reasons.  That's not too hard, but deciding
     * when to reevaluate the permissions is trickier.  An alternative
     * might be to turn off the readonly bit if the user forces a write
     * and it succeeds.
     *
     * XXX
     * Access(2) doesn't consider the effective uid/gid values.  This
     * probably isn't a problem for vi when it's running standalone.
     */
    if (readonly || F_ISSET(sp, SC_READONLY) ||
            (!F_ISSET(frp, FR_NEWFILE) &&
             (!(sb.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) ||
              access(frp->name, W_OK))))
        O_SET(sp, O_READONLY);
    else
        O_CLR(sp, O_READONLY);

    /* Switch... */
    ++ep->refcnt;
    CIRCLEQ_INSERT_HEAD(&ep->scrq, sp, eq);
    sp->ep = ep;
    sp->frp = frp;

    /* Set the initial cursor position, queue initial command. */
    file_cinit(sp);

    /* Report conversion errors again. */
    F_CLR(sp, SC_CONV_ERROR);

    /* Redraw the screen from scratch, schedule a welcome message. */
    F_SET(sp, SC_SCR_REFORMAT | SC_STATUS);

    if (frp->lno == OOBLNO)
        F_SET(sp, SC_SCR_TOP);

    /* Append into the chain of file structures. */
    if (ep->refcnt == 1)
        CIRCLEQ_INSERT_TAIL(&sp->gp->exfq, ep, q);

    return (0);

err:
    if (frp->name != NULL) {
        free(frp->name);
        frp->name = NULL;
    }
    if (frp->tname != NULL) {
        (void)unlink(frp->tname);
        free(frp->tname);
        frp->tname = NULL;
    }

oerr:
    if (F_ISSET(ep, F_RCV_ON))
        (void)unlink(ep->rcv_path);
    if (ep->rcv_path != NULL) {
        free(ep->rcv_path);
        ep->rcv_path = NULL;
    }
    if (ep->db != NULL) {
        (void)ep->db->close(ep->db, DB_NOSYNC);
        ep->db = NULL;
    }
    free(ep);

    return (open_err && !LF_ISSET(FS_OPENERR) ?
            file_init(sp, frp, rcv_name, flags | FS_OPENERR) : 1);
}
Esempio n. 10
0
/*
 * 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(SCR *sp, EXCMD *cmdp)
{
	enum { R_ARG, R_EXPANDARG, R_FILTER } which;
	struct stat sb;
	CHAR_T *arg = NULL;
	const char *name;
	size_t nlen;
	EX_PRIVATE *exp;
	FILE *fp;
	FREF *frp;
	GS *gp;
	MARK rm;
	db_recno_t nlines;
	size_t arglen = 0;
	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_wemsg(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 =
		    v_wstrdup(sp, cmdp->argv[argc]->bp,
				cmdp->argv[argc]->len)) == 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)) {
				INT2CHAR(sp, cmdp->argv[argc]->bp,
				    cmdp->argv[argc]->len + 1, name, nlen);
				(void)ex_printf(sp,
				    "!%s\n", name);
			} 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_wemsg(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:
			INT2CHAR(sp, cmdp->argv[1]->bp, cmdp->argv[1]->len + 1, 
				 name, nlen);
			/*
			 * !!!
			 * 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 = strdup(name)) != 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);
				name = sp->frp->name;
			} else {
				set_alt_name(sp, name);
				name = sp->alt_name;
			}
			break;
		default:
			ex_wemsg(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);
}
Esempio n. 11
0
/*
 * sscr_exec --
 *	Take a line and hand it off to the shell.
 *
 * PUBLIC: int sscr_exec __P((SCR *, db_recno_t));
 */
int
sscr_exec(SCR *sp, db_recno_t lno)
{
	SCRIPT *sc;
	db_recno_t last_lno;
	size_t blen, len, last_len;
	int isempty, matchprompt, rval;
	ssize_t nw;
	char *bp = NULL;
	const char *p;
	const CHAR_T *ip;
	size_t ilen;

	sc = sp->script;

	/* If there's a prompt on the last line, append the command. */
	if (db_last(sp, &last_lno))
		return (1);
	if (db_get(sp, last_lno, DBG_FATAL, __UNCONST(&ip), &ilen))
		return (1);
	INT2CHAR(sp, ip, ilen, p, last_len);
	if (last_len == sc->sh_prompt_len &&
	    memcmp(p, sc->sh_prompt, last_len) == 0) {
		matchprompt = 1;
		GET_SPACE_RETC(sp, bp, blen, last_len + 128);
		memmove(bp, p, last_len);
	} else
		matchprompt = 0;

	/* Get something to execute. */
	if (db_eget(sp, lno, __UNCONST(&ip), &ilen, &isempty)) {
		if (isempty)
			goto empty;
		goto err1;
	}

	/* Empty lines aren't interesting. */
	if (ilen == 0)
		goto empty;
	INT2CHAR(sp, ip, ilen, p, len);

	/* Delete any prompt. */
	if (len >= sc->sh_prompt_len &&
	    memcmp(p, sc->sh_prompt, sc->sh_prompt_len) == 0) {
		len -= sc->sh_prompt_len;
		if (len == 0) {
empty:			msgq(sp, M_BERR, "151|No command to execute");
			goto err1;
		}
		p += sc->sh_prompt_len;
	}

	/* Push the line to the shell. */
	if ((size_t)(nw = write(sc->sh_master, p, len)) != len)
		goto err2;
	rval = 0;
	if (write(sc->sh_master, "\n", 1) != 1) {
err2:		if (nw == 0)
			errno = EIO;
		msgq(sp, M_SYSERR, "shell");
		goto err1;
	}

	if (matchprompt) {
		ADD_SPACE_GOTO(sp, char, bp, blen, last_len + len);
		memmove(bp + last_len, p, len);
		CHAR2INT(sp, bp, last_len + len, ip, ilen);
		if (db_set(sp, last_lno, ip, ilen))
err1:			rval = 1;
	}
	if (matchprompt)
alloc_err:	FREE_SPACE(sp, bp, blen);
	return (rval);
}
Esempio n. 12
0
/*
 * ex_readfp --
 *	Read lines into the file.
 *
 * PUBLIC: int ex_readfp __P((SCR *, const char *, FILE *, MARK *, db_recno_t *, int));
 */
int
ex_readfp(SCR *sp, const char *name, FILE *fp, MARK *fm, db_recno_t *nlinesp, int silent)
{
	EX_PRIVATE *exp;
	GS *gp;
	db_recno_t lcnt, lno;
	size_t len;
	u_long ccnt;			/* XXX: can't print off_t portably. */
	int nf, rval;
	const char *p;
	size_t wlen;
	const CHAR_T *wp;

	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;
			}
		}
		FILE2INT5(sp, exp->ibcw, exp->ibp, len, wp, wlen);
		if (db_append(sp, 1, lno, wp, wlen))
			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) {
		char *q = msg_print(sp, name, &nf);
		msgq(sp, M_INFO,
		    "148|%s: %lu lines, %lu characters", q, (unsigned long)lcnt,
		    (unsigned long)ccnt);
		if (nf)
			FREE_SPACE(sp, q, 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);
}
Esempio n. 13
0
/*
 * put --
 *	Put text buffer contents into the file.
 *
 * PUBLIC: int put __P((SCR *, CB *, CHAR_T *, MARK *, MARK *, int));
 */
int
put(SCR *sp, CB *cbp, ARG_CHAR_T *namep, MARK *cp, MARK *rp, int append)
{
	ARG_CHAR_T name;
	TEXT *ltp, *tp;
	db_recno_t lno;
	size_t blen, clen, len;
	int rval;
	CHAR_T *bp, *t;
	CHAR_T *p;

	if (cbp == NULL) {
		if (namep == NULL) {
			cbp = sp->wp->dcbp;
			if (cbp == NULL) {
				msgq(sp, M_ERR,
				    "053|The default buffer is empty");
				return (1);
			}
		} else {
			name = *namep;
			CBNAME(sp, cbp, name);
			if (cbp == NULL) {
				msgq(sp, M_ERR, "054|Buffer %s is empty",
				    KEY_NAME(sp, name));
				return (1);
			}
		}
	}
	tp = TAILQ_FIRST(&cbp->textq);

	/*
	 * It's possible to do a put into an empty file, meaning that the cut
	 * buffer simply becomes the file.  It's a special case so that we can
	 * ignore it in general.
	 *
	 * !!!
	 * Historically, pasting into a file with no lines in vi would preserve
	 * the single blank line.  This is surely a result of the fact that the
	 * historic vi couldn't deal with a file that had no lines in it.  This
	 * implementation treats that as a bug, and does not retain the blank
	 * line.
	 *
	 * Historical practice is that the cursor ends at the first character
	 * in the file.
	 */
	if (cp->lno == 1) {
		if (db_last(sp, &lno))
			return (1);
		if (lno == 0) {
			for (; tp != NULL;
			    ++lno, ++sp->rptlines[L_ADDED], tp = TAILQ_NEXT(tp, q))
				if (db_append(sp, 1, lno, tp->lb, tp->len))
					return (1);
			rp->lno = 1;
			rp->cno = 0;
			return (0);
		}
	}

	/* If a line mode buffer, append each new line into the file. */
	if (F_ISSET(cbp, CB_LMODE)) {
		lno = append ? cp->lno : cp->lno - 1;
		rp->lno = lno + 1;
		for (; tp != NULL;
		    ++lno, ++sp->rptlines[L_ADDED], tp = TAILQ_NEXT(tp, q))
			if (db_append(sp, 1, lno, tp->lb, tp->len))
				return (1);
		rp->cno = 0;
		(void)nonblank(sp, rp->lno, &rp->cno);
		return (0);
	}

	/*
	 * If buffer was cut in character mode, replace the current line with
	 * one built from the portion of the first line to the left of the
	 * split plus the first line in the CB.  Append each intermediate line
	 * in the CB.  Append a line built from the portion of the first line
	 * to the right of the split plus the last line in the CB.
	 *
	 * Get the first line.
	 */
	lno = cp->lno;
	if (db_get(sp, lno, DBG_FATAL, &p, &len))
		return (1);

	GET_SPACE_RETW(sp, bp, blen, tp->len + len + 1);
	t = bp;

	/* Original line, left of the split. */
	if (len > 0 && (clen = cp->cno + (append ? 1 : 0)) > 0) {
		MEMCPYW(bp, p, clen);
		p += clen;
		t += clen;
	}

	/* First line from the CB. */
	if (tp->len != 0) {
		MEMCPYW(t, tp->lb, tp->len);
		t += tp->len;
	}

	/* Calculate length left in the original line. */
	clen = len == 0 ? 0 : len - (cp->cno + (append ? 1 : 0));

	/*
	 * !!!
	 * In the historical 4BSD version of vi, character mode puts within
	 * a single line have two cursor behaviors: if the put is from the
	 * unnamed buffer, the cursor moves to the character inserted which
	 * appears last in the file.  If the put is from a named buffer,
	 * the cursor moves to the character inserted which appears first
	 * in the file.  In System III/V, it was changed at some point and
	 * the cursor always moves to the first character.  In both versions
	 * of vi, character mode puts that cross line boundaries leave the
	 * cursor on the first character.  Nvi implements the System III/V
	 * behavior, and expect POSIX.2 to do so as well.
	 */
	rp->lno = lno;
	rp->cno = len == 0 ? 0 : sp->cno + (append && tp->len ? 1 : 0);

	/*
	 * If no more lines in the CB, append the rest of the original
	 * line and quit.  Otherwise, build the last line before doing
	 * the intermediate lines, because the line changes will lose
	 * the cached line.
	 */
	if (TAILQ_NEXT(tp, q) == NULL) {
		if (clen > 0) {
			MEMCPYW(t, p, clen);
			t += clen;
		}
		if (db_set(sp, lno, bp, t - bp))
			goto err;
		if (sp->rptlchange != lno) {
			sp->rptlchange = lno;
			++sp->rptlines[L_CHANGED];
		}
	} else {
		/*
		 * Have to build both the first and last lines of the
		 * put before doing any sets or we'll lose the cached
		 * line.  Build both the first and last lines in the
		 * same buffer, so we don't have to have another buffer
		 * floating around.
		 *
		 * Last part of original line; check for space, reset
		 * the pointer into the buffer.
		 */
		ltp = TAILQ_LAST(&cbp->textq, _texth);
		len = t - bp;
		ADD_SPACE_RETW(sp, bp, blen, ltp->len + clen);
		t = bp + len;

		/* Add in last part of the CB. */
		MEMCPYW(t, ltp->lb, ltp->len);
		if (clen)
			MEMCPYW(t + ltp->len, p, clen);
		clen += ltp->len;

		/*
		 * Now: bp points to the first character of the first
		 * line, t points to the last character of the last
		 * line, t - bp is the length of the first line, and
		 * clen is the length of the last.  Just figured you'd
		 * want to know.
		 *
		 * Output the line replacing the original line.
		 */
		if (db_set(sp, lno, bp, t - bp))
			goto err;
		if (sp->rptlchange != lno) {
			sp->rptlchange = lno;
			++sp->rptlines[L_CHANGED];
		}

		/* Output any intermediate lines in the CB. */
		for (tp = TAILQ_NEXT(tp, q);
		    TAILQ_NEXT(tp, q) != NULL;
		    ++lno, ++sp->rptlines[L_ADDED], tp = TAILQ_NEXT(tp, q))
			if (db_append(sp, 1, lno, tp->lb, tp->len))
				goto err;

		if (db_append(sp, 1, lno, t, clen))
			goto err;
		++sp->rptlines[L_ADDED];
	}
	rval = 0;

	if (0)
err:		rval = 1;

	FREE_SPACEW(sp, bp, blen);
	return (rval);
}
Esempio n. 14
0
/*
 * log_line --
 *	Log a line change.
 *
 * PUBLIC: int log_line __P((SCR *, db_recno_t, u_int));
 */
int
log_line(SCR *sp, db_recno_t lno, u_int action)
{
	DBT data, key;
	EXF *ep;
	size_t len;
	CHAR_T *lp;
	db_recno_t lcur;

	ep = sp->ep;
	if (F_ISSET(ep, F_NOLOG))
		return (0);

	/*
	 * XXX
	 *
	 * Kluge for vi.  Clear the EXF undo flag so that the
	 * next 'u' command does a roll-back, regardless.
	 */
	F_CLR(ep, F_UNDO);

	/* Put out one initial cursor record per set of changes. */
	if (ep->l_cursor.lno != OOBLNO) {
		if (log_cursor1(sp, LOG_CURSOR_INIT))
			return (1);
		ep->l_cursor.lno = OOBLNO;
		ep->l_win = sp->wp;
	} /*else if (ep->l_win != sp->wp) {
		printf("log_line own: %p, this: %p\n", ep->l_win, sp->wp);
		return 1;
	}*/

	if ((sp->db_error = 
		__vi_change_log(ep->env, NULL, &ep->lsn_cur, 0, action, 
			    lno)) != 0) {
		msgq(sp, M_DBERR, "change_log");
		return 1;
	}

#if defined(DEBUG) && 0
	switch (action) {
	case LOG_LINE_APPEND_F:
		vtrace(sp, "%u: log_line: append_f: %lu {%u}\n",
		    ep->l_cur, lno, len);
		break;
	case LOG_LINE_APPEND_B:
		vtrace(sp, "%u: log_line: append_b: %lu {%u}\n",
		    ep->l_cur, lno, len);
		break;
	case LOG_LINE_DELETE_F:
		vtrace(sp, "%lu: log_line: delete_f: %lu {%u}\n",
		    ep->l_cur, lno, len);
		break;
	case LOG_LINE_DELETE_B:
		vtrace(sp, "%lu: log_line: delete_b: %lu {%u}\n",
		    ep->l_cur, lno, len);
		break;
	case LOG_LINE_RESET_F:
		vtrace(sp, "%lu: log_line: reset_f: %lu {%u}\n",
		    ep->l_cur, lno, len);
		break;
	case LOG_LINE_RESET_B:
		vtrace(sp, "%lu: log_line: reset_b: %lu {%u}\n",
		    ep->l_cur, lno, len);
		break;
	}
#endif
	/* Reset high water mark. */
	ep->l_high = ++ep->l_cur;

	return (0);
}
Esempio n. 15
0
/*
 * cl_ex_init --
 *	Initialize the ex screen.
 */
static int
cl_ex_init(SCR *sp)
{
	CL_PRIVATE *clp;
	int error;
	const char *ttype;

	clp = CLP(sp);

	/* If already initialized, just set the terminal modes. */
	if (F_ISSET(clp, CL_SCR_EX_INIT))
		goto fast;

	/* If not reading from a file, we're done. */
	if (!F_ISSET(clp, CL_STDIN_TTY))
		return (0);

	if (F_ISSET(clp, CL_CHANGE_TERM)) {
		if (F_ISSET(clp, CL_SETUPTERM) && del_curterm(cur_term))
			return (1);
		F_CLR(clp, CL_SETUPTERM | CL_CHANGE_TERM);
	}

	if (!F_ISSET(clp, CL_SETUPTERM)) {
		/* We'll need a terminal type. */
		if (opts_empty(sp, O_TERM, 0))
			return (1);
		ttype = O_STR(sp, O_TERM);
		(void)setupterm(ttype, STDOUT_FILENO, &error);
		if (error == 0 || error == -1)
			return (1);
	}

	/* Get the ex termcap/terminfo strings. */
	(void)cl_getcap(sp, "cup", &clp->cup);
	(void)cl_getcap(sp, "smso", &clp->smso);
	(void)cl_getcap(sp, "rmso", &clp->rmso);
	(void)cl_getcap(sp, "el", &clp->el);
	(void)cl_getcap(sp, "cuu1", &clp->cuu1);

	/* Enter_standout_mode and exit_standout_mode are paired. */
	if (clp->smso == NULL || clp->rmso == NULL) {
		if (clp->smso != NULL) {
			free(clp->smso);
			clp->smso = NULL;
		}
		if (clp->rmso != NULL) {
			free(clp->rmso);
			clp->rmso = NULL;
		}
	}

	/*
	 * Turn on canonical mode, with normal input and output processing.
	 * Start with the original terminal settings as the user probably
	 * had them (including any local extensions) set correctly for the
	 * current terminal.
	 *
	 * !!!
	 * We can't get everything that we need portably; for example, ONLCR,
	 * mapping <newline> to <carriage-return> on output isn't required
	 * by POSIX 1003.1b-1993.  If this turns out to be a problem, then
	 * we'll either have to play some games on the mapping, or we'll have
	 * to make all ex printf's output \r\n instead of \n.
	 */
	clp->ex_enter = clp->orig;
	clp->ex_enter.c_lflag  |= ECHO | ECHOE | ECHOK | ICANON | IEXTEN | ISIG;
#ifdef ECHOCTL
	clp->ex_enter.c_lflag |= ECHOCTL;
#endif
#ifdef ECHOKE
	clp->ex_enter.c_lflag |= ECHOKE;
#endif
	clp->ex_enter.c_iflag |= ICRNL;
	clp->ex_enter.c_oflag |= OPOST;
#ifdef ONLCR
	clp->ex_enter.c_oflag |= ONLCR;
#endif

fast:	if (tcsetattr(STDIN_FILENO, TCSADRAIN | TCSASOFT, &clp->ex_enter)) {
		if (errno == EINTR)
			goto fast;
		msgq(sp, M_SYSERR, "tcsetattr");
		return (1);
	}
	return (0);
}
Esempio n. 16
0
File: exf.c Progetto: fishman/nvi
/*
 * file_end --
 *	Stop editing a file.
 *
 * PUBLIC: int file_end __P((SCR *, EXF *, int));
 */
int
file_end(SCR *sp, EXF *ep, int force)
{
    FREF *frp;

    /*
     * !!!
     * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
     * (If argument ep is NULL, use sp->ep.)
     *
     * If multiply referenced, just decrement the count and return.
     */
    if (ep == NULL)
        ep = sp->ep;
    CIRCLEQ_REMOVE(&ep->scrq, sp, eq);
    if (--ep->refcnt != 0)
        return (0);

    /*
     *
     * Clean up the FREF structure.
     *
     * Save the cursor location.
     *
     * XXX
     * It would be cleaner to do this somewhere else, but by the time
     * ex or vi knows that we're changing files it's already happened.
     */
    frp = sp->frp;
    frp->lno = sp->lno;
    frp->cno = sp->cno;
    F_SET(frp, FR_CURSORSET);

    /*
     * We may no longer need the temporary backing file, so clean it
     * up.  We don't need the FREF structure either, if the file was
     * never named, so lose it.
     *
     * !!!
     * Re: FR_DONTDELETE, see the comment above in file_init().
     */
    if (!F_ISSET(frp, FR_DONTDELETE) && frp->tname != NULL) {
        if (unlink(frp->tname))
            msgq_str(sp, M_SYSERR, frp->tname, "240|%s: remove");
        free(frp->tname);
        frp->tname = NULL;
        if (F_ISSET(frp, FR_TMPFILE)) {
            CIRCLEQ_REMOVE(&sp->gp->frefq, frp, q);
            if (frp->name != NULL)
                free(frp->name);
            free(frp);
        }
        sp->frp = NULL;
    }

    /*
     * Clean up the EXF structure.
     *
     * Close the db structure.
     */
    if (ep->db->close != NULL) {
        if ((sp->db_error = ep->db->close(ep->db, DB_NOSYNC)) != 0 &&
                !force) {
            msgq_str(sp, M_DBERR, frp->name, "241|%s: close");
            CIRCLEQ_INSERT_HEAD(&ep->scrq, sp, eq);
            ++ep->refcnt;
            return (1);
        }
        ep->db = NULL;
    }

    /* COMMITTED TO THE CLOSE.  THERE'S NO GOING BACK... */

    /* Stop logging. */
    (void)log_end(sp, ep);

    /* Free up any marks. */
    (void)mark_end(sp, ep);

    if (ep->env) {
        DB_ENV *env;

        ep->env->close(ep->env, 0);
        ep->env = 0;
        if ((sp->db_error = db_env_create(&env, 0)))
            msgq(sp, M_DBERR, "env_create");
        if ((sp->db_error = db_env_remove(env, ep->env_path, 0)))
            msgq(sp, M_DBERR, "env->remove");
        if (ep->env_path != NULL && rmdir(ep->env_path))
            msgq_str(sp, M_SYSERR, ep->env_path, "242|%s: remove");
    }

    /*
     * Delete recovery files, close the open descriptor, free recovery
     * memory.  See recover.c for a description of the protocol.
     *
     * XXX
     * Unlink backup file first, we can detect that the recovery file
     * doesn't reference anything when the user tries to recover it.
     * There's a race, here, obviously, but it's fairly small.
     */
    if (!F_ISSET(ep, F_RCV_NORM)) {
        if (ep->rcv_path != NULL && unlink(ep->rcv_path))
            msgq_str(sp, M_SYSERR, ep->rcv_path, "242|%s: remove");
        if (ep->rcv_mpath != NULL && unlink(ep->rcv_mpath))
            msgq_str(sp, M_SYSERR, ep->rcv_mpath, "243|%s: remove");
    }
    CIRCLEQ_REMOVE(&sp->gp->exfq, ep, q);
    if (ep->fd != -1)
        (void)close(ep->fd);
    if (ep->fcntl_fd != -1)
        (void)close(ep->fcntl_fd);
    if (ep->rcv_fd != -1)
        (void)close(ep->rcv_fd);
    if (ep->env_path != NULL)
        free(ep->env_path);
    if (ep->rcv_path != NULL)
        free(ep->rcv_path);
    if (ep->rcv_mpath != NULL)
        free(ep->rcv_mpath);

    free(ep);
    return (0);
}
Esempio n. 17
0
/*
 * ex_move -- :[line [,line]] mo[ve] line
 *	Move selected lines.
 *
 * PUBLIC: int ex_move(SCR *, EXCMD *);
 */
int
ex_move(SCR *sp, EXCMD *cmdp)
{
	LMARK *lmp;
	MARK fm1, fm2;
	recno_t cnt, diff, fl, tl, mfl, mtl;
	size_t blen, len;
	int mark_reset;
	char *bp, *p;

	NEEDFILE(sp, cmdp);

	/*
	 * It's not possible to move things into the area that's being
	 * moved.
	 */
	fm1 = cmdp->addr1;
	fm2 = cmdp->addr2;
	if (cmdp->lineno >= fm1.lno && cmdp->lineno <= fm2.lno) {
		msgq(sp, M_ERR, "Destination line is inside move range");
		return (1);
	}

	/*
	 * Log the positions of any marks in the to-be-deleted lines.  This
	 * has to work with the logging code.  What happens is that we log
	 * the old mark positions, make the changes, then log the new mark
	 * positions.  Then the marks end up in the right positions no matter
	 * which way the log is traversed.
	 *
	 * XXX
	 * Reset the MARK_USERSET flag so that the log can undo the mark.
	 * This isn't very clean, and should probably be fixed.
	 */
	fl = fm1.lno;
	tl = cmdp->lineno;

	/* Log the old positions of the marks. */
	mark_reset = 0;
	LIST_FOREACH(lmp, &sp->ep->marks, q)
		if (lmp->name != ABSMARK1 &&
		    lmp->lno >= fl && lmp->lno <= tl) {
			mark_reset = 1;
			F_CLR(lmp, MARK_USERSET);
			(void)log_mark(sp, lmp);
		}

	/* Get memory for the copy. */
	GET_SPACE_RET(sp, bp, blen, 256);

	/* Move the lines. */
	diff = (fm2.lno - fm1.lno) + 1;
	if (tl > fl) {				/* Destination > source. */
		mfl = tl - diff;
		mtl = tl;
		for (cnt = diff; cnt--;) {
			if (db_get(sp, fl, DBG_FATAL, &p, &len))
				return (1);
			BINC_RET(sp, bp, blen, len);
			memcpy(bp, p, len);
			if (db_append(sp, 1, tl, bp, len))
				return (1);
			if (mark_reset)
				LIST_FOREACH(lmp, &sp->ep->marks, q)
					if (lmp->name != ABSMARK1 &&
					    lmp->lno == fl)
						lmp->lno = tl + 1;
			if (db_delete(sp, fl))
				return (1);
		}
	} else {				/* Destination < source. */
Esempio n. 18
0
File: exf.c Progetto: fishman/nvi
/*
 * file_write --
 *	Write the file to disk.  Historic vi had fairly convoluted
 *	semantics for whether or not writes would happen.  That's
 *	why all the flags.
 *
 * PUBLIC: int file_write __P((SCR *, MARK *, MARK *, char *, int));
 */
int
file_write(SCR *sp, MARK *fm, MARK *tm, char *name, int flags)
{
    enum { NEWFILE, OLDFILE } mtype;
    struct stat sb;
    EXF *ep;
    FILE *fp;
    FREF *frp;
    MARK from, to;
    size_t len;
    u_long nlno, nch;
    int fd, nf, noname, oflags, rval;
    char *p, *s, *t, buf[MAXPATHLEN + 64];
    const char *msgstr;

    ep = sp->ep;
    frp = sp->frp;

    /*
     * Writing '%', or naming the current file explicitly, has the
     * same semantics as writing without a name.
     */
    if (name == NULL || !strcmp(name, frp->name)) {
        noname = 1;
        name = frp->name;
    } else
        noname = 0;

    /* Can't write files marked read-only, unless forced. */
    if (!LF_ISSET(FS_FORCE) && noname && O_ISSET(sp, O_READONLY)) {
        msgq(sp, M_ERR, LF_ISSET(FS_POSSIBLE) ?
             "244|Read-only file, not written; use ! to override" :
             "245|Read-only file, not written");
        return (1);
    }

    /* If not forced, not appending, and "writeany" not set ... */
    if (!LF_ISSET(FS_FORCE | FS_APPEND) && !O_ISSET(sp, O_WRITEANY)) {
        /* Don't overwrite anything but the original file. */
        if ((!noname || F_ISSET(frp, FR_NAMECHANGE)) &&
                !stat(name, &sb)) {
            msgq_str(sp, M_ERR, name,
                     LF_ISSET(FS_POSSIBLE) ?
                     "246|%s exists, not written; use ! to override" :
                     "247|%s exists, not written");
            return (1);
        }

        /*
         * Don't write part of any existing file.  Only test for the
         * original file, the previous test catches anything else.
         */
        if (!LF_ISSET(FS_ALL) && noname && !stat(name, &sb)) {
            msgq(sp, M_ERR, LF_ISSET(FS_POSSIBLE) ?
                 "248|Partial file, not written; use ! to override" :
                 "249|Partial file, not written");
            return (1);
        }
    }

    /*
     * Figure out if the file already exists -- if it doesn't, we display
     * the "new file" message.  The stat might not be necessary, but we
     * just repeat it because it's easier than hacking the previous tests.
     * The information is only used for the user message and modification
     * time test, so we can ignore the obvious race condition.
     *
     * One final test.  If we're not forcing or appending the current file,
     * and we have a saved modification time, object if the file changed
     * since we last edited or wrote it, and make them force it.
     */
    if (stat(name, &sb))
        mtype = NEWFILE;
    else {
        if (noname && !LF_ISSET(FS_FORCE | FS_APPEND) &&
                ((F_ISSET(ep, F_DEVSET) &&
                  (sb.st_dev != ep->mdev || sb.st_ino != ep->minode)) ||
                 sb.st_mtime != ep->mtime)) {
            msgq_str(sp, M_ERR, name, LF_ISSET(FS_POSSIBLE) ?
                     "250|%s: file modified more recently than this copy; use ! to override" :
                     "251|%s: file modified more recently than this copy");
            return (1);
        }

        mtype = OLDFILE;
    }

    /* Set flags to create, write, and either append or truncate. */
    oflags = O_CREAT | O_WRONLY |
             (LF_ISSET(FS_APPEND) ? O_APPEND : O_TRUNC);

    /* Backup the file if requested. */
    if (!opts_empty(sp, O_BACKUP, 1) &&
            file_backup(sp, name, O_STR(sp, O_BACKUP)) && !LF_ISSET(FS_FORCE))
        return (1);

    /* Open the file. */
    SIGBLOCK;
    if ((fd = open(name, oflags,
                   S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) < 0) {
        msgq_str(sp, M_SYSERR, name, "%s");
        SIGUNBLOCK;
        return (1);
    }
    SIGUNBLOCK;

    /* Try and get a lock. */
    if (!noname && file_lock(sp, NULL, NULL, fd, 0) == LOCK_UNAVAIL)
        msgq_str(sp, M_ERR, name,
                 "252|%s: write lock was unavailable");

#if __linux__
    /*
     * XXX
     * In libc 4.5.x, fdopen(fd, "w") clears the O_APPEND flag (if set).
     * This bug is fixed in libc 4.6.x.
     *
     * This code works around this problem for libc 4.5.x users.
     * Note that this code is harmless if you're using libc 4.6.x.
     */
    if (LF_ISSET(FS_APPEND) && lseek(fd, (off_t)0, SEEK_END) < 0) {
        msgq(sp, M_SYSERR, name);
        return (1);
    }
#endif

    /*
     * Use stdio for buffering.
     *
     * XXX
     * SVR4.2 requires the fdopen mode exactly match the original open
     * mode, i.e. you have to open with "a" if appending.
     */
    if ((fp = fdopen(fd, LF_ISSET(FS_APPEND) ? "a" : "w")) == NULL) {
        msgq_str(sp, M_SYSERR, name, "%s");
        (void)close(fd);
        return (1);
    }

    /* Build fake addresses, if necessary. */
    if (fm == NULL) {
        from.lno = 1;
        from.cno = 0;
        fm = &from;
        if (db_last(sp, &to.lno))
            return (1);
        to.cno = 0;
        tm = &to;
    }

    rval = ex_writefp(sp, name, fp, fm, tm, &nlno, &nch, 0);

    /*
     * Save the new last modification time -- even if the write fails
     * we re-init the time.  That way the user can clean up the disk
     * and rewrite without having to force it.
     */
    if (noname) {
        if (stat(name, &sb))
            time(&ep->mtime);
        else {
            F_SET(ep, F_DEVSET);
            ep->mdev = sb.st_dev;
            ep->minode = sb.st_ino;

            ep->mtime = sb.st_mtime;
        }
    }

    /*
     * If the write failed, complain loudly.  ex_writefp() has already
     * complained about the actual error, reinforce it if data was lost.
     */
    if (rval) {
        if (!LF_ISSET(FS_APPEND))
            msgq_str(sp, M_ERR, name,
                     "254|%s: WARNING: FILE TRUNCATED");
        return (1);
    }

    /*
     * Once we've actually written the file, it doesn't matter that the
     * file name was changed -- if it was, we've already whacked it.
     */
    F_CLR(frp, FR_NAMECHANGE);

    /*
     * If wrote the entire file, and it wasn't by appending it to a file,
     * clear the modified bit.  If the file was written to the original
     * file name and the file is a temporary, set the "no exit" bit.  This
     * permits the user to write the file and use it in the context of the
     * filesystem, but still keeps them from discarding their changes by
     * exiting.
     */
    if (LF_ISSET(FS_ALL) && !LF_ISSET(FS_APPEND)) {
        F_CLR(ep, F_MODIFIED);
        if (F_ISSET(frp, FR_TMPFILE)) {
            if (noname)
                F_SET(frp, FR_TMPEXIT);
            else
                F_CLR(frp, FR_TMPEXIT);
        }
    }

    p = msg_print(sp, name, &nf);
    switch (mtype) {
    case NEWFILE:
        msgstr = msg_cat(sp,
                         "256|%s: new file: %lu lines, %lu characters", NULL);
        len = snprintf(buf, sizeof(buf), msgstr, p, nlno, nch);
        break;
    case OLDFILE:
        msgstr = msg_cat(sp, LF_ISSET(FS_APPEND) ?
                         "315|%s: appended: %lu lines, %lu characters" :
                         "257|%s: %lu lines, %lu characters", NULL);
        len = snprintf(buf, sizeof(buf), msgstr, p, nlno, nch);
        break;
    default:
        abort();
    }

    /*
     * There's a nasty problem with long path names.  Cscope and tags files
     * can result in long paths and vi will request a continuation key from
     * the user.  Unfortunately, the user has typed ahead, and chaos will
     * result.  If we assume that the characters in the filenames only take
     * a single screen column each, we can trim the filename.
     */
    s = buf;
    if (len >= sp->cols) {
        for (s = buf, t = buf + strlen(p); s < t &&
                (*s != '/' || len >= sp->cols - 3); ++s, --len);
        if (s == t)
            s = buf;
        else {
            *--s = '.';		/* Leading ellipses. */
            *--s = '.';
            *--s = '.';
        }
    }
    msgq(sp, M_INFO, s);
    if (nf)
        FREE_SPACE(sp, p, 0);
    return (0);
}
Esempio n. 19
0
/*
 * ex_s --
 *	[line [,line]] s[ubstitute] [[/;]pat[/;]/repl[/;] [cgr] [count] [#lp]]
 *
 *	Substitute on lines matching a pattern.
 *
 * PUBLIC: int ex_s(SCR *, EXCMD *);
 */
int
ex_s(SCR *sp, EXCMD *cmdp)
{
	regex_t *re;
	size_t blen, len;
	u_int flags;
	int delim;
	char *bp, *ptrn, *rep, *p, *t;

	/*
	 * Skip leading white space.
	 *
	 * !!!
	 * Historic vi allowed any non-alphanumeric to serve as the
	 * substitution command delimiter.
	 *
	 * !!!
	 * If the arguments are empty, it's the same as &, i.e. we
	 * repeat the last substitution.
	 */
	if (cmdp->argc == 0)
		goto subagain;
	for (p = cmdp->argv[0]->bp,
	    len = cmdp->argv[0]->len; len > 0; --len, ++p) {
		if (!isblank(*p))
			break;
	}
	if (len == 0)
subagain:	return (ex_subagain(sp, cmdp));

	delim = *p++;
	if (isalnum(delim) || delim == '\\')
		return (s(sp, cmdp, p, &sp->subre_c, SUB_MUSTSETR));

	/*
	 * !!!
	 * The full-blown substitute command reset the remembered
	 * state of the 'c' and 'g' suffices.
	 */
	sp->c_suffix = sp->g_suffix = 0;

	/*
	 * Get the pattern string, toss escaping characters.
	 *
	 * !!!
	 * Historic vi accepted any of the following forms:
	 *
	 *	:s/abc/def/		change "abc" to "def"
	 *	:s/abc/def		change "abc" to "def"
	 *	:s/abc/			delete "abc"
	 *	:s/abc			delete "abc"
	 *
	 * QUOTING NOTE:
	 *
	 * Only toss an escaping character if it escapes a delimiter.
	 * This means that "s/A/\\\\f" replaces "A" with "\\f".  It
	 * would be nice to be more regular, i.e. for each layer of
	 * escaping a single escaping character is removed, but that's
	 * not how the historic vi worked.
	 */
	for (ptrn = t = p;;) {
		if (p[0] == '\0' || p[0] == delim) {
			if (p[0] == delim)
				++p;
			/*
			 * !!!
			 * Nul terminate the pattern string -- it's passed
			 * to regcomp which doesn't understand anything else.
			 */
			*t = '\0';
			break;
		}
		if (p[0] == '\\') {
			if (p[1] == delim)
				++p;
			else if (p[1] == '\\')
				*t++ = *p++;
		}
		*t++ = *p++;
	}

	/*
	 * If the pattern string is empty, use the last RE (not just the
	 * last substitution RE).
	 */
	if (*ptrn == '\0') {
		if (sp->re == NULL) {
			ex_emsg(sp, NULL, EXM_NOPREVRE);
			return (1);
		}

		/* Re-compile the RE if necessary. */
		if (!F_ISSET(sp, SC_RE_SEARCH) && re_compile(sp,
		    sp->re, sp->re_len, NULL, NULL, &sp->re_c, RE_C_SEARCH))
			return (1);
		flags = 0;
	} else {
		/*
		 * !!!
		 * Compile the RE.  Historic practice is that substitutes set
		 * the search direction as well as both substitute and search
		 * RE's.  We compile the RE twice, as we don't want to bother
		 * ref counting the pattern string and (opaque) structure.
		 */
		if (re_compile(sp, ptrn, t - ptrn,
		    &sp->re, &sp->re_len, &sp->re_c, RE_C_SEARCH))
			return (1);
		if (re_compile(sp, ptrn, t - ptrn,
		    &sp->subre, &sp->subre_len, &sp->subre_c, RE_C_SUBST))
			return (1);
		
		flags = SUB_FIRST;
		sp->searchdir = FORWARD;
	}
	re = &sp->re_c;

	/*
	 * Get the replacement string.
	 *
	 * The special character & (\& if O_MAGIC not set) matches the
	 * entire RE.  No handling of & is required here, it's done by
	 * re_sub().
	 *
	 * The special character ~ (\~ if O_MAGIC not set) inserts the
	 * previous replacement string into this replacement string.
	 * Count ~'s to figure out how much space we need.  We could
	 * special case nonexistent last patterns or whether or not
	 * O_MAGIC is set, but it's probably not worth the effort.
	 *
	 * QUOTING NOTE:
	 *
	 * Only toss an escaping character if it escapes a delimiter or
	 * if O_MAGIC is set and it escapes a tilde.
	 *
	 * !!!
	 * If the entire replacement pattern is "%", then use the last
	 * replacement pattern.  This semantic was added to vi in System
	 * V and then percolated elsewhere, presumably around the time
	 * that it was added to their version of ed(1).
	 */
	if (p[0] == '\0' || p[0] == delim) {
		if (p[0] == delim)
			++p;
		if (sp->repl != NULL)
			free(sp->repl);
		sp->repl = NULL;
		sp->repl_len = 0;
	} else if (p[0] == '%' && (p[1] == '\0' || p[1] == delim))
		p += p[1] == delim ? 2 : 1;
	else {
		for (rep = p, len = 0;
		    p[0] != '\0' && p[0] != delim; ++p, ++len)
			if (p[0] == '~')
				len += sp->repl_len;
		GET_SPACE_RET(sp, bp, blen, len);
		for (t = bp, len = 0, p = rep;;) {
			if (p[0] == '\0' || p[0] == delim) {
				if (p[0] == delim)
					++p;
				break;
			}
			if (p[0] == '\\') {
				if (p[1] == delim)
					++p;
				else if (p[1] == '\\') {
					*t++ = *p++;
					++len;
				} else if (p[1] == '~') {
					++p;
					if (!O_ISSET(sp, O_MAGIC))
						goto tilde;
				}
			} else if (p[0] == '~' && O_ISSET(sp, O_MAGIC)) {
tilde:				++p;
				memcpy(t, sp->repl, sp->repl_len);
				t += sp->repl_len;
				len += sp->repl_len;
				continue;
			}
			*t++ = *p++;
			++len;
		}
		if ((sp->repl_len = len) != 0) {
			if (sp->repl != NULL)
				free(sp->repl);
			if ((sp->repl = malloc(len)) == NULL) {
				msgq(sp, M_SYSERR, NULL);
				FREE_SPACE(sp, bp, blen);
				return (1);
			}
			memcpy(sp->repl, bp, len);
		}
		FREE_SPACE(sp, bp, blen);
	}
	return (s(sp, cmdp, p, re, flags));
}
Esempio n. 20
0
/*
 * sscr_getprompt --
 *	Eat lines printed by the shell until a line with no trailing
 *	carriage return comes; set the prompt from that line.
 */
static int
sscr_getprompt(SCR *sp)
{
	CHAR_T *endp, *p, *t, buf[1024];
	SCRIPT *sc;
	struct pollfd pfd[1];
	recno_t lline;
	size_t llen, len;
	u_int value;
	int nr;

	endp = buf;
	len = sizeof(buf);

	/* Wait up to a second for characters to read. */
	sc = sp->script;
	pfd[0].fd = sc->sh_master;
	pfd[0].events = POLLIN;
	switch (poll(pfd, 1, 5 * 1000)) {
	case -1:		/* Error or interrupt. */
		msgq(sp, M_SYSERR, "poll");
		goto prompterr;
	case  0:		/* Timeout */
		msgq(sp, M_ERR, "Error: timed out");
		goto prompterr;
	default:		/* Characters to read. */
		break;
	}

	/* Read the characters. */
more:	len = sizeof(buf) - (endp - buf);
	switch (nr = read(sc->sh_master, endp, len)) {
	case  0:			/* EOF. */
		msgq(sp, M_ERR, "Error: shell: EOF");
		goto prompterr;
	case -1:			/* Error or interrupt. */
		msgq(sp, M_SYSERR, "shell");
		goto prompterr;
	default:
		endp += nr;
		break;
	}

	/* If any complete lines, push them into the file. */
	for (p = t = buf; p < endp; ++p) {
		value = KEY_VAL(sp, *p);
		if (value == K_CR || value == K_NL) {
			if (db_last(sp, &lline) ||
			    db_append(sp, 0, lline, t, p - t))
				goto prompterr;
			t = p + 1;
		}
	}
	if (p > buf) {
		memmove(buf, t, endp - t);
		endp = buf + (endp - t);
	}
	if (endp == buf)
		goto more;

	/* Wait up 1/10 of a second to make sure that we got it all. */
	switch (poll(pfd, 1, 100)) {
	case -1:		/* Error or interrupt. */
		msgq(sp, M_SYSERR, "poll");
		goto prompterr;
	case  0:		/* Timeout */
		break;
	default:		/* Characters to read. */
		goto more;
	}

	/* Timed out, so theoretically we have a prompt. */
	llen = endp - buf;
	endp = buf;

	/* Append the line into the file. */
	if (db_last(sp, &lline) || db_append(sp, 0, lline, buf, llen)) {
prompterr:	sscr_end(sp);
		return (1);
	}

	return (sscr_setprompt(sp, buf, llen));
}
Esempio n. 21
0
int
conv_enc (SCR *sp, int option, const char *enc)
{
#if defined(USE_WIDECHAR) && defined(USE_ICONV)
    iconv_t id;
    char2wchar_t    *c2w;
    wchar2char_t    *w2c;

    switch (option) {
    case O_FILEENCODING:
	c2w = &sp->conv.file2int;
	w2c = &sp->conv.int2file;
	break;
    case O_INPUTENCODING:
	c2w = &sp->conv.input2int;
	w2c = NULL;
	break;
    default:
	c2w = NULL;
	w2c = NULL;
	break;
    }

    if (!*enc) {
	if (c2w) *c2w = raw2int;
	if (w2c) *w2c = int2raw;
	return 0;
    }

    if (!strcmp(enc, "WCHAR_T")) {
	if (c2w) *c2w = CHAR_T_char2int;
	if (w2c) *w2c = CHAR_T_int2char;
	return 0;
    }

    id = iconv_open(enc, nl_langinfo(CODESET));
    if (id == (iconv_t)-1)
	goto err;
    iconv_close(id);
    id = iconv_open(nl_langinfo(CODESET), enc);
    if (id == (iconv_t)-1)
	goto err;
    iconv_close(id);

    switch (option) {
    case O_FILEENCODING:
	*c2w = fe_char2int;
	*w2c = fe_int2char;
	break;
    case O_INPUTENCODING:
	*c2w = ie_char2int;
	break;
    }

    F_CLR(sp, SC_CONV_ERROR);
    F_SET(sp, SC_SCR_REFORMAT);

    return 0;
err:
    switch (option) {
    case O_FILEENCODING:
	msgq(sp, M_ERR,
	    "321|File encoding conversion not supported");
	break;
    case O_INPUTENCODING:
	msgq(sp, M_ERR,
	    "322|Input encoding conversion not supported");
	break;
    }
#endif
    return 1;
}
Esempio n. 22
0
/*
 * sscr_exec --
 *	Take a line and hand it off to the shell.
 *
 * PUBLIC: int sscr_exec(SCR *, recno_t);
 */
int
sscr_exec(SCR *sp, recno_t lno)
{
	SCRIPT *sc;
	recno_t last_lno;
	size_t blen, len, last_len, tlen;
	int isempty, matchprompt, nw, rval;
	char *bp, *p;

	/* If there's a prompt on the last line, append the command. */
	if (db_last(sp, &last_lno))
		return (1);
	if (db_get(sp, last_lno, DBG_FATAL, &p, &last_len))
		return (1);
	if (sscr_matchprompt(sp, p, last_len, &tlen) && tlen == 0) {
		matchprompt = 1;
		GET_SPACE_RET(sp, bp, blen, last_len + 128);
		memmove(bp, p, last_len);
	} else
		matchprompt = 0;

	/* Get something to execute. */
	if (db_eget(sp, lno, &p, &len, &isempty)) {
		if (isempty)
			goto empty;
		goto err1;
	}

	/* Empty lines aren't interesting. */
	if (len == 0)
		goto empty;

	/* Delete any prompt. */
	if (sscr_matchprompt(sp, p, len, &tlen)) {
		if (tlen == len) {
empty:			msgq(sp, M_BERR, "No command to execute");
			goto err1;
		}
		p += (len - tlen);
		len = tlen;
	}

	/* Push the line to the shell. */
	sc = sp->script;
	if ((nw = write(sc->sh_master, p, len)) != len)
		goto err2;
	rval = 0;
	if (write(sc->sh_master, "\n", 1) != 1) {
err2:		if (nw == 0)
			errno = EIO;
		msgq(sp, M_SYSERR, "shell");
		goto err1;
	}

	if (matchprompt) {
		ADD_SPACE_RET(sp, bp, blen, last_len + len);
		memmove(bp + last_len, p, len);
		if (db_set(sp, last_lno, bp, last_len + len))
err1:			rval = 1;
	}
	if (matchprompt)
		FREE_SPACE(sp, bp, blen);
	return (rval);
}
/*
 * shift --
 *	Ex shift support.
 */
static int
shift(SCR *sp, EXCMD *cmdp, enum which rl)
{
	db_recno_t from, to;
	size_t blen, len, newcol, newidx, oldcol, oldidx, sw;
	int curset;
	CHAR_T *p;
	CHAR_T *bp, *tbp;

	NEEDFILE(sp, cmdp);

	if (O_VAL(sp, O_SHIFTWIDTH) == 0) {
		msgq(sp, M_INFO, "152|shiftwidth option set to 0");
		return (0);
	}

	/* Copy the lines being shifted into the unnamed buffer. */
	if (cut(sp, NULL, &cmdp->addr1, &cmdp->addr2, CUT_LINEMODE))
		return (1);

	/*
	 * The historic version of vi permitted the user to string any number
	 * of '>' or '<' characters together, resulting in an indent of the
	 * appropriate levels.  There's a special hack in ex_cmd() so that
	 * cmdp->argv[0] points to the string of '>' or '<' characters.
	 *
	 * Q: What's the difference between the people adding features
	 *    to vi and the Girl Scouts?
	 * A: The Girl Scouts have mint cookies and adult supervision.
	 */
	for (p = cmdp->argv[0]->bp, sw = 0; *p == '>' || *p == '<'; ++p)
		sw += O_VAL(sp, O_SHIFTWIDTH);

	GET_SPACE_RETW(sp, bp, blen, 256);

	curset = 0;
	for (from = cmdp->addr1.lno, to = cmdp->addr2.lno; from <= to; ++from) {
		if (db_get(sp, from, DBG_FATAL, &p, &len))
			goto err;
		if (!len) {
			if (sp->lno == from)
				curset = 1;
			continue;
		}

		/*
		 * Calculate the old indent amount and the number of
		 * characters it used.
		 */
		for (oldidx = 0, oldcol = 0; oldidx < len; ++oldidx)
			if (p[oldidx] == ' ')
				++oldcol;
			else if (p[oldidx] == '\t')
				oldcol += O_VAL(sp, O_TABSTOP) -
				    oldcol % O_VAL(sp, O_TABSTOP);
			else
				break;

		/* Calculate the new indent amount. */
		if (rl == RIGHT)
			newcol = oldcol + sw;
		else {
			newcol = oldcol < sw ? 0 : oldcol - sw;
			if (newcol == oldcol) {
				if (sp->lno == from)
					curset = 1;
				continue;
			}
		}

		/* Get a buffer that will hold the new line. */
		ADD_SPACE_RETW(sp, bp, blen, newcol + len);

		/*
		 * Build a new indent string and count the number of
		 * characters it uses.
		 */
		tbp = bp;
		newidx = 0;
		if (!O_ISSET(sp, O_EXPANDTAB)) {
			for (; newcol >= O_VAL(sp, O_TABSTOP); ++newidx) {
				*tbp++ = '\t';
				newcol -= O_VAL(sp, O_TABSTOP);
			}
		}
		for (; newcol > 0; --newcol, ++newidx)
			*tbp++ = ' ';

		/* Add the original line. */
		MEMCPYW(tbp, p + oldidx, len - oldidx);

		/* Set the replacement line. */
		if (db_set(sp, from, bp, (tbp + (len - oldidx)) - bp)) {
err:			FREE_SPACEW(sp, bp, blen);
			return (1);
		}

		/*
		 * !!!
		 * The shift command in historic vi had the usual bizarre
		 * collection of cursor semantics.  If called from vi, the
		 * cursor was repositioned to the first non-blank character
		 * of the lowest numbered line shifted.  If called from ex,
		 * the cursor was repositioned to the first non-blank of the
		 * highest numbered line shifted.  Here, if the cursor isn't
		 * part of the set of lines that are moved, move it to the
		 * first non-blank of the last line shifted.  (This makes
		 * ":3>>" in vi work reasonably.)  If the cursor is part of
		 * the shifted lines, it doesn't get moved at all.  This
		 * permits shifting of marked areas, i.e. ">'a." shifts the
		 * marked area twice, something that couldn't be done with
		 * historic vi.
		 */
		if (sp->lno == from) {
			curset = 1;
			if (newidx > oldidx)
				sp->cno += newidx - oldidx;
			else if (sp->cno >= oldidx - newidx)
				sp->cno -= oldidx - newidx;
		}
	}
	if (!curset) {
		sp->lno = to;
		sp->cno = 0;
		(void)nonblank(sp, to, &sp->cno);
	}

	FREE_SPACEW(sp, bp, blen);

	sp->rptlines[L_SHIFT] += cmdp->addr2.lno - cmdp->addr1.lno + 1;
	return (0);
}
Esempio n. 24
0
/*
 * sscr_insert --
 *	Take a line from the shell and insert it into the file.
 */
static int
sscr_insert(SCR *sp)
{
	CHAR_T *endp, *p, *t;
	SCRIPT *sc;
	struct pollfd pfd[1];
	recno_t lno;
	size_t blen, len, tlen;
	u_int value;
	int nr, rval;
	char *bp;

	/* Find out where the end of the file is. */
	if (db_last(sp, &lno))
		return (1);

#define	MINREAD	1024
	GET_SPACE_RET(sp, bp, blen, MINREAD);
	endp = bp;

	/* Read the characters. */
	rval = 1;
	sc = sp->script;
more:	switch (nr = read(sc->sh_master, endp, MINREAD)) {
	case  0:			/* EOF; shell just exited. */
		sscr_end(sp);
		rval = 0;
		goto ret;
	case -1:			/* Error or interrupt. */
		msgq(sp, M_SYSERR, "shell");
		goto ret;
	default:
		endp += nr;
		break;
	}

	/* Append the lines into the file. */
	for (p = t = bp; p < endp; ++p) {
		value = KEY_VAL(sp, *p);
		if (value == K_CR || value == K_NL) {
			len = p - t;
			if (db_append(sp, 1, lno++, t, len))
				goto ret;
			t = p + 1;
		}
	}
	if (p > t) {
		len = p - t;
		/*
		 * If the last thing from the shell isn't another prompt, wait
		 * up to 1/10 of a second for more stuff to show up, so that
		 * we don't break the output into two separate lines.  Don't
		 * want to hang indefinitely because some program is hanging,
		 * confused the shell, or whatever.
		 */
		if (!sscr_matchprompt(sp, t, len, &tlen) || tlen != 0) {
			pfd[0].fd = sc->sh_master;
			pfd[0].events = POLLIN;
			if (poll(pfd, 1, 100) > 0) {
				memmove(bp, t, len);
				endp = bp + len;
				goto more;
			}
		}
		if (sscr_setprompt(sp, t, len))
			return (1);
		if (db_append(sp, 1, lno++, t, len))
			goto ret;
	}

	/* The cursor moves to EOF. */
	sp->lno = lno;
	sp->cno = len ? len - 1 : 0;
	rval = vs_refresh(sp, 1);

ret:	FREE_SPACE(sp, bp, blen);
	return (rval);
}
Esempio n. 25
0
bool
ACE_Message_Queue_Vx::is_empty_i (void)
{
  ACE_TRACE ("ACE_Message_Queue_Vx::is_empty_i");
  return ::msgQNumMsgs (msgq ()) == 0;
}
Esempio n. 26
0
/*
 * sscr_init --
 *	Create a pty setup for a shell.
 */
static int
sscr_init(SCR *sp)
{
	SCRIPT *sc;
	char *sh, *sh_path;

	/* We're going to need a shell. */
	if (opts_empty(sp, O_SHELL, 0))
		return (1);

	MALLOC_RET(sp, sc, sizeof(SCRIPT));
	sp->script = sc;
	sc->sh_prompt = NULL;
	sc->sh_prompt_len = 0;

	/*
	 * There are two different processes running through this code.
	 * They are the shell and the parent.
	 */
	sc->sh_master = sc->sh_slave = -1;

	if (tcgetattr(STDIN_FILENO, &sc->sh_term) == -1) {
		msgq(sp, M_SYSERR, "tcgetattr");
		goto err;
	}

	/*
	 * Turn off output postprocessing and echo.
	 */
	sc->sh_term.c_oflag &= ~OPOST;
	sc->sh_term.c_cflag &= ~(ECHO|ECHOE|ECHONL|ECHOK);

	if (ioctl(STDIN_FILENO, TIOCGWINSZ, &sc->sh_win) == -1) {
		msgq(sp, M_SYSERR, "tcgetattr");
		goto err;
	}

	if (openpty(&sc->sh_master,
	    &sc->sh_slave, sc->sh_name, &sc->sh_term, &sc->sh_win) == -1) {
		msgq(sp, M_SYSERR, "pty");
		goto err;
	}

	/*
	 * __TK__ huh?
	 * Don't use vfork() here, because the signal semantics differ from
	 * implementation to implementation.
	 */
	switch (sc->sh_pid = fork()) {
	case -1:			/* Error. */
		msgq(sp, M_SYSERR, "fork");
err:		if (sc->sh_master != -1)
			(void)close(sc->sh_master);
		if (sc->sh_slave != -1)
			(void)close(sc->sh_slave);
		return (1);
	case 0:				/* Utility. */
		/*
		 * XXX
		 * So that shells that do command line editing turn it off.
		 */
		if (setenv("TERM", "emacs", 1) == -1 ||
		    setenv("TERMCAP", "emacs:", 1) == -1 ||
		    setenv("EMACS", "t", 1) == -1)
			_exit(126);

		(void)setsid();
		/*
		 * 4.4BSD allocates a controlling terminal using the TIOCSCTTY
		 * ioctl, not by opening a terminal device file.  POSIX 1003.1
		 * doesn't define a portable way to do this.
		 */
		(void)ioctl(sc->sh_slave, TIOCSCTTY, 0);
		(void)close(sc->sh_master);
		(void)dup2(sc->sh_slave, STDIN_FILENO);
		(void)dup2(sc->sh_slave, STDOUT_FILENO);
		(void)dup2(sc->sh_slave, STDERR_FILENO);
		(void)close(sc->sh_slave);

		/* Assumes that all shells have -i. */
		sh_path = O_STR(sp, O_SHELL);
		if ((sh = strrchr(sh_path, '/')) == NULL)
			sh = sh_path;
		else
			++sh;
		execl(sh_path, sh, "-i", (char *)NULL);
		msgq_str(sp, M_SYSERR, sh_path, "execl: %s");
		_exit(127);
	default:			/* Parent. */
		break;
	}

	if (sscr_getprompt(sp))
		return (1);

	F_SET(sp, SC_SCRIPT);
	F_SET(sp->gp, G_SCRWIN);
	return (0);
}
Esempio n. 27
0
/*
 * cl_read --
 *	Read characters from the input.
 */
static input_t
cl_read(SCR *sp, u_int32_t flags, char *bp, size_t blen, int *nrp, struct timeval *tp)
{
    struct termios term1, term2;
    CL_PRIVATE *clp;
    GS *gp;
    fd_set rdfd;
    input_t rval;
    int maxfd, nr, term_reset;

    gp = sp->gp;
    clp = CLP(sp);
    term_reset = 0;

    /*
     * 1: A read from a file or a pipe.  In this case, the reads
     *    never timeout regardless.  This means that we can hang
     *    when trying to complete a map, but we're going to hang
     *    on the next read anyway.
     */
    if (!F_ISSET(clp, CL_STDIN_TTY)) {
        switch (nr = read(STDIN_FILENO, bp, blen)) {
        case 0:
            return (INP_EOF);
        case -1:
            goto err;
        default:
            *nrp = nr;
            return (INP_OK);
        }
        /* NOTREACHED */
    }

    /*
     * 2: A read with an associated timeout, e.g., trying to complete
     *    a map sequence.  If input exists, we fall into #3.
     */
    if (tp != NULL) {
        FD_ZERO(&rdfd);
        FD_SET(STDIN_FILENO, &rdfd);
        switch (select(STDIN_FILENO + 1, &rdfd, NULL, NULL, tp)) {
        case 0:
            return (INP_TIMEOUT);
        case -1:
            goto err;
        default:
            break;
        }
    }

    /*
     * The user can enter a key in the editor to quote a character.  If we
     * get here and the next key is supposed to be quoted, do what we can.
     * Reset the tty so that the user can enter a ^C, ^Q, ^S.  There's an
     * obvious race here, when the key has already been entered, but there's
     * nothing that we can do to fix that problem.
     *
     * The editor can ask for the next literal character even thought it's
     * generally running in line-at-a-time mode.  Do what we can.
     */
    if (LF_ISSET(EC_QUOTED | EC_RAW) && !tcgetattr(STDIN_FILENO, &term1)) {
        term_reset = 1;
        if (LF_ISSET(EC_QUOTED)) {
            term2 = term1;
            term2.c_lflag &= ~ISIG;
            term2.c_iflag &= ~(IXON | IXOFF);
            (void)tcsetattr(STDIN_FILENO,
                            TCSASOFT | TCSADRAIN, &term2);
        } else
            (void)tcsetattr(STDIN_FILENO,
                            TCSASOFT | TCSADRAIN, &clp->vi_enter);
    }

    /*
     * 3: Wait for input.
     *
     * Select on the command input and scripting window file descriptors.
     * It's ugly that we wait on scripting file descriptors here, but it's
     * the only way to keep from locking out scripting windows.
     */
    if (F_ISSET(gp, G_SCRWIN)) {
loop:
        FD_ZERO(&rdfd);
        FD_SET(STDIN_FILENO, &rdfd);
        maxfd = STDIN_FILENO;
        if (F_ISSET(sp, SC_SCRIPT)) {
            FD_SET(sp->script->sh_master, &rdfd);
            if (sp->script->sh_master > maxfd)
                maxfd = sp->script->sh_master;
        }
        switch (select(maxfd + 1, &rdfd, NULL, NULL, NULL)) {
        case 0:
            abort();
        case -1:
            goto err;
        default:
            break;
        }
        if (!FD_ISSET(STDIN_FILENO, &rdfd)) {
            if (sscr_input(sp))
                return (INP_ERR);
            goto loop;
        }
    }

    /*
     * 4: Read the input.
     *
     * !!!
     * What's going on here is some scary stuff.  Ex runs the terminal in
     * canonical mode.  So, the <newline> character terminating a line of
     * input is returned in the buffer, but a trailing <EOF> character is
     * not similarly included.  As ex uses 0<EOF> and ^<EOF> as autoindent
     * commands, it has to see the trailing <EOF> characters to determine
     * the difference between the user entering "0ab" and "0<EOF>ab".  We
     * leave an extra slot in the buffer, so that we can add a trailing
     * <EOF> character if the buffer isn't terminated by a <newline>.  We
     * lose if the buffer is too small for the line and exactly N characters
     * are entered followed by an <EOF> character.
     */
#define	ONE_FOR_EOF	1
    switch (nr = read(STDIN_FILENO, bp, blen - ONE_FOR_EOF)) {
    case  0:				/* EOF. */
        /*
         * ^D in canonical mode returns a read of 0, i.e. EOF.  EOF is
         * a valid command, but we don't want to loop forever because
         * the terminal driver is returning EOF because the user has
         * disconnected. The editor will almost certainly try to write
         * something before this fires, which should kill us, but You
         * Never Know.
         */
        if (++clp->eof_count < 50) {
            bp[0] = clp->orig.c_cc[VEOF];
            *nrp = 1;
            rval = INP_OK;

        } else
            rval = INP_EOF;
        break;
    case -1:				/* Error or interrupt. */
err:
        if (errno == EINTR)
            rval = INP_INTR;
        else {
            rval = INP_ERR;
            msgq(sp, M_SYSERR, "input");
        }
        break;
    default:				/* Input characters. */
        if (F_ISSET(sp, SC_EX) && bp[nr - 1] != '\n')
            bp[nr++] = clp->orig.c_cc[VEOF];
        *nrp = nr;
        clp->eof_count = 0;
        rval = INP_OK;
        break;
    }

    /* Restore the terminal state if it was modified. */
    if (term_reset)
        (void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &term1);
    return (rval);
}
Esempio n. 28
0
/*
 * cl_vi_init --
 *	Initialize the curses vi screen.
 */
static int
cl_vi_init(SCR *sp)
{
	CL_PRIVATE *clp;
	char *o_cols, *o_lines, *o_term;
	const char *ttype;

	clp = CLP(sp);

	/* If already initialized, just set the terminal modes. */
	if (F_ISSET(clp, CL_SCR_VI_INIT))
		goto fast;

	/* Curses vi always reads from (and writes to) a terminal. */
	if (!F_ISSET(clp, CL_STDIN_TTY) || !isatty(STDOUT_FILENO)) {
		msgq(sp, M_ERR,
		    "016|Vi's standard input and output must be a terminal");
		return (1);
	}

	/* We'll need a terminal type. */
	if (opts_empty(sp, O_TERM, 0))
		return (1);
	ttype = O_STR(sp, O_TERM);

	/*
	 * XXX
	 * Changing the row/column and terminal values is done by putting them
	 * into the environment, which is then read by curses.  What this loses
	 * in ugliness, it makes up for in stupidity.  We can't simply put the
	 * values into the environment ourselves, because in the presence of a
	 * kernel mechanism for returning the window size, entering values into
	 * the environment will screw up future screen resizing events, e.g. if
	 * the user enters a :shell command and then resizes their window.  So,
	 * if they weren't already in the environment, we make sure to delete
	 * them immediately after setting them.
	 *
	 * XXX
	 * Putting the TERM variable into the environment is necessary, even
	 * though we're using newterm() here.  We may be using initscr() as
	 * the underlying function.
	 */
	o_term = getenv("TERM");
	cl_putenv(sp, "TERM", ttype, 0);
	o_lines = getenv("LINES");
	cl_putenv(sp, "LINES", NULL, (u_long)O_VAL(sp, O_LINES));
	o_cols = getenv("COLUMNS");
	cl_putenv(sp, "COLUMNS", NULL, (u_long)O_VAL(sp, O_COLUMNS));

	/* Delete cur_term if exists. */
	if (F_ISSET(clp, CL_SETUPTERM)) {
		if (del_curterm(cur_term))
			return (1);
		F_CLR(clp, CL_SETUPTERM);
	}

	/*
	 * XXX
	 * The SunOS initscr() can't be called twice.  Don't even think about
	 * using it.  It fails in subtle ways (e.g. select(2) on fileno(stdin)
	 * stops working).  (The SVID notes that applications should only call
	 * initscr() once.)
	 *
	 * XXX
	 * The HP/UX newterm doesn't support the NULL first argument, so we
	 * have to specify the terminal type.
	 */
	errno = 0;
	if ((clp->screen = newterm(__UNCONST(ttype), stdout, stdin)) == NULL) {
		if (errno)
			msgq(sp, M_SYSERR, "%s", ttype);
		else
			msgq(sp, M_ERR, "%s: unknown terminal type", ttype);
		return (1);
	}

	if (o_term == NULL)
		cl_unsetenv(sp, "TERM");
	if (o_lines == NULL)
		cl_unsetenv(sp, "LINES");
	if (o_cols == NULL)
		cl_unsetenv(sp, "COLUMNS");

	/*
	 * XXX
	 * Someone got let out alone without adult supervision -- the SunOS
	 * newterm resets the signal handlers.  There's a race, but it's not
	 * worth closing.
	 */
	(void)sig_init(sp->gp, sp);

	/*
	 * We use raw mode.  What we want is 8-bit clean, however, signals
	 * and flow control should continue to work.  Admittedly, it sounds
	 * like cbreak, but it isn't.  Using cbreak() can get you additional
	 * things like IEXTEN, which turns on flags like DISCARD and LNEXT.
	 *
	 * !!!
	 * If raw isn't turning off echo and newlines, something's wrong.
	 * However, it shouldn't hurt.
	 */
	noecho();			/* No character echo. */
	nonl();				/* No CR/NL translation. */
	raw();				/* 8-bit clean. */
	idlok(stdscr, 1);		/* Use hardware insert/delete line. */

	/* Put the cursor keys into application mode. */
	(void)keypad(stdscr, TRUE);

	/*
	 * XXX
	 * The screen TI sequence just got sent.  See the comment in
	 * cl_funcs.c:cl_attr().
	 */
	clp->ti_te = TI_SENT;

	/*
	 * XXX
	 * Historic implementations of curses handled SIGTSTP signals
	 * in one of three ways.  They either:
	 *
	 *	1: Set their own handler, regardless.
	 *	2: Did not set a handler if a handler was already installed.
	 *	3: Set their own handler, but then called any previously set
	 *	   handler after completing their own cleanup.
	 *
	 * We don't try and figure out which behavior is in place, we force
	 * it to SIG_DFL after initializing the curses interface, which means
	 * that curses isn't going to take the signal.  Since curses isn't
	 * reentrant (i.e., the whole curses SIGTSTP interface is a fantasy),
	 * we're doing The Right Thing.
	 */
	(void)signal(SIGTSTP, SIG_DFL);

	/*
	 * If flow control was on, turn it back on.  Turn signals on.  ISIG
	 * turns on VINTR, VQUIT, VDSUSP and VSUSP.   The main curses code
	 * already installed a handler for VINTR.  We're going to disable the
	 * other three.
	 *
	 * XXX
	 * We want to use ^Y as a vi scrolling command.  If the user has the
	 * DSUSP character set to ^Y (common practice) clean it up.  As it's
	 * equally possible that the user has VDSUSP set to 'a', we disable
	 * it regardless.  It doesn't make much sense to suspend vi at read,
	 * so I don't think anyone will care.  Alternatively, we could look
	 * it up in the table of legal command characters and turn it off if
	 * it matches one.  VDSUSP wasn't in POSIX 1003.1-1990, so we test for
	 * it.
	 *
	 * XXX
	 * We don't check to see if the user had signals enabled originally.
	 * If they didn't, it's unclear what we're supposed to do here, but
	 * it's also pretty unlikely.
	 */
	if (tcgetattr(STDIN_FILENO, &clp->vi_enter)) {
		msgq(sp, M_SYSERR, "tcgetattr");
		goto err;
	}
	if (clp->orig.c_iflag & IXON)
		clp->vi_enter.c_iflag |= IXON;
	if (clp->orig.c_iflag & IXOFF)
		clp->vi_enter.c_iflag |= IXOFF;

	clp->vi_enter.c_lflag |= ISIG;
#ifdef VDSUSP
	clp->vi_enter.c_cc[VDSUSP] = _POSIX_VDISABLE;
#endif
	clp->vi_enter.c_cc[VQUIT] = _POSIX_VDISABLE;
	clp->vi_enter.c_cc[VSUSP] = _POSIX_VDISABLE;

	/*
	 * XXX
	 * OSF/1 doesn't turn off the <discard>, <literal-next> or <status>
	 * characters when curses switches into raw mode.  It should be OK
	 * to do it explicitly for everyone.
	 */
#ifdef VDISCARD
	clp->vi_enter.c_cc[VDISCARD] = _POSIX_VDISABLE;
#endif
#ifdef VLNEXT
	clp->vi_enter.c_cc[VLNEXT] = _POSIX_VDISABLE;
#endif
#ifdef VSTATUS
	clp->vi_enter.c_cc[VSTATUS] = _POSIX_VDISABLE;
#endif

	/* Initialize terminal based information. */
	if (cl_term_init(sp))
		goto err;

fast:	/* Set the terminal modes. */
	if (tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &clp->vi_enter)) {
		msgq(sp, M_SYSERR, "tcsetattr");
err:		(void)cl_vi_end(sp->gp);
		return (1);
	}
	return (0);
}
Esempio n. 29
0
/*
 * ex_next -- :next [+cmd] [files]
 *	Edit the next file, optionally setting the list of files.
 *
 * !!!
 * The :next command behaved differently from the :rewind command in
 * historic vi.  See nvi/docs/autowrite for details, but the basic
 * idea was that it ignored the force flag if the autowrite flag was
 * set.  This implementation handles them all identically.
 *
 * PUBLIC: int ex_next(SCR *, EXCMD *);
 */
int
ex_next(SCR *sp, EXCMD *cmdp)
{
    ARGS **argv;
    FREF *frp;
    int noargs;
    char **ap;
    CHAR_T *wp;
    size_t wlen;
    char *np;
    size_t nlen;

    /* Check for file to move to. */
    if (cmdp->argc == 0 && (sp->cargv == NULL || sp->cargv[1] == NULL)) {
        msgq(sp, M_ERR, "111|No more files to edit");
        return (1);
    }

    if (F_ISSET(cmdp, E_NEWSCREEN)) {
        /* By default, edit the next file in the old argument list. */
        if (cmdp->argc == 0) {
            CHAR2INT(sp, sp->cargv[1], strlen(sp->cargv[1]) + 1,
                     wp, wlen);
            if (argv_exp0(sp, cmdp, wp, wlen - 1))
                return (1);
            return (ex_edit(sp, cmdp));
        }
        return (ex_N_next(sp, cmdp));
    }

    /* Check modification. */
    if (file_m1(sp,
                FL_ISSET(cmdp->iflags, E_C_FORCE), FS_ALL | FS_POSSIBLE))
        return (1);

    /* Any arguments are a replacement file list. */
    if (cmdp->argc) {
        /* Free the current list. */
        if (!F_ISSET(sp, SC_ARGNOFREE) && sp->argv != NULL) {
            for (ap = sp->argv; *ap != NULL; ++ap)
                free(*ap);
            free(sp->argv);
        }
        F_CLR(sp, SC_ARGNOFREE | SC_ARGRECOVER);
        sp->cargv = NULL;

        /* Create a new list. */
        CALLOC_RET(sp,
                   sp->argv, char **, cmdp->argc + 1, sizeof(char *));
        for (ap = sp->argv,
                argv = cmdp->argv; argv[0]->len != 0; ++ap, ++argv) {
            INT2CHAR(sp, argv[0]->bp, argv[0]->len, np, nlen);
            if ((*ap = v_strdup(sp, np, nlen)) == NULL)
                return (1);
        }
        *ap = NULL;

        /* Switch to the first file. */
        sp->cargv = sp->argv;
        if ((frp = file_add(sp, *sp->cargv)) == NULL)
            return (1);
        noargs = 0;

        /* Display a file count with the welcome message. */
        F_SET(sp, SC_STATUS_CNT);
    } else {
        if ((frp = file_add(sp, sp->cargv[1])) == NULL)
Esempio n. 30
0
File: vi.c Progetto: Alkzndr/freebsd
/*
 * vi --
 * 	Main vi command loop.
 *
 * PUBLIC: int vi __P((SCR **));
 */
int
vi(SCR **spp)
{
	GS *gp;
	MARK abs;
	SCR *next, *sp;
	VICMD cmd = { 0 }, *vp;
	VI_PRIVATE *vip;
	int comcount, mapped, rval;

	/* Get the first screen. */
	sp = *spp;
	gp = sp->gp;

	/* Point to the command structure. */
	vp = &cmd;

	/* Reset strange attraction. */
	F_SET(vp, VM_RCM_SET);

	/* Initialize the vi screen. */
	if (v_init(sp))
		return (1);

	/* Set the focus. */
	(void)sp->gp->scr_rename(sp, sp->frp->name, 1);

	for (vip = VIP(sp), rval = 0;;) {
		/* Resolve messages. */
		if (!MAPPED_KEYS_WAITING(sp) && vs_resolve(sp, NULL, 0))
			goto ret;

		/*
		 * If not skipping a refresh, return to command mode and
		 * refresh the screen.
		 */
		if (F_ISSET(vip, VIP_S_REFRESH))
			F_CLR(vip, VIP_S_REFRESH);
		else {
			sp->showmode = SM_COMMAND;
			if (vs_refresh(sp, 0))
				goto ret;
		}

		/* Set the new favorite position. */
		if (F_ISSET(vp, VM_RCM_SET | VM_RCM_SETFNB | VM_RCM_SETNNB)) {
			F_CLR(vip, VIP_RCM_LAST);
			(void)vs_column(sp, &sp->rcm);
		}

		/*
		 * If not currently in a map, log the cursor position,
		 * and set a flag so that this command can become the
		 * DOT command.
		 */
		if (MAPPED_KEYS_WAITING(sp))
			mapped = 1;
		else {
			if (log_cursor(sp))
				goto err;
			mapped = 0;
		}

		/*
		 * There may be an ex command waiting, and we returned here
		 * only because we exited a screen or file.  In this case,
		 * we simply go back into the ex parser.
		 */
		if (EXCMD_RUNNING(gp)) {
			vp->kp = &vikeys[':'];
			goto ex_continue;
		}

		/* Refresh the command structure. */
		memset(vp, 0, sizeof(VICMD));

		/*
		 * We get a command, which may or may not have an associated
		 * motion.  If it does, we get it too, calling its underlying
		 * function to get the resulting mark.  We then call the
		 * command setting the cursor to the resulting mark.
		 *
		 * !!!
		 * Vi historically flushed mapped characters on error, but
		 * entering extra <escape> characters at the beginning of
		 * a map wasn't considered an error -- in fact, users would
		 * put leading <escape> characters in maps to clean up vi
		 * state before the map was interpreted.  Beauty!
		 */
		switch (v_cmd(sp, DOT, vp, NULL, &comcount, &mapped)) {
		case GC_ERR:
			goto err;
		case GC_ERR_NOFLUSH:
			goto gc_err_noflush;
		case GC_EVENT:
			goto gc_event;
		case GC_FATAL:
			goto ret;
		case GC_INTERRUPT:
			goto intr;
		case GC_OK:
			break;
		}

		/* Check for security setting. */
		if (F_ISSET(vp->kp, V_SECURE) && O_ISSET(sp, O_SECURE)) {
			ex_emsg(sp, KEY_NAME(sp, vp->key), EXM_SECURE);
			goto err;
		}

		/*
		 * Historical practice: if a dot command gets a new count,
		 * any motion component goes away, i.e. "d3w2." deletes a
		 * total of 5 words.
		 */
		if (F_ISSET(vp, VC_ISDOT) && comcount)
			DOTMOTION->count = 1;

		/* Copy the key flags into the local structure. */
		F_SET(vp, vp->kp->flags);

		/* Prepare to set the previous context. */
		if (F_ISSET(vp, V_ABS | V_ABS_C | V_ABS_L)) {
			abs.lno = sp->lno;
			abs.cno = sp->cno;
		}

		/*
		 * Set the three cursor locations to the current cursor.  The
		 * underlying routines don't bother if the cursor doesn't move.
		 * This also handles line commands (e.g. Y) defaulting to the
		 * current line.
		 */
		vp->m_start.lno = vp->m_stop.lno = vp->m_final.lno = sp->lno;
		vp->m_start.cno = vp->m_stop.cno = vp->m_final.cno = sp->cno;

		/*
		 * Do any required motion; v_motion sets the from MARK and the
		 * line mode flag, as well as the VM_RCM flags.
		 */
		if (F_ISSET(vp, V_MOTION) &&
		    v_motion(sp, DOTMOTION, vp, &mapped)) {
			if (INTERRUPTED(sp))
				goto intr;
			goto err;
		}

		/*
		 * If a count is set and the command is line oriented, set the
		 * to MARK here relative to the cursor/from MARK.  This is for
		 * commands that take both counts and motions, i.e. "4yy" and
		 * "y%".  As there's no way the command can know which the user
		 * did, we have to do it here.  (There are commands that are
		 * line oriented and that take counts ("#G", "#H"), for which
		 * this calculation is either completely meaningless or wrong.
		 * Each command must validate the value for itself.
		 */
		if (F_ISSET(vp, VC_C1SET) && F_ISSET(vp, VM_LMODE))
			vp->m_stop.lno += vp->count - 1;

		/* Increment the command count. */
		++sp->ccnt;

#if defined(DEBUG) && defined(COMLOG)
		v_comlog(sp, vp);
#endif
		/* Call the function. */
ex_continue:	if (vp->kp->func(sp, vp))
			goto err;
gc_event:
#ifdef DEBUG
		/* Make sure no function left the temporary space locked. */
		if (F_ISSET(gp, G_TMP_INUSE)) {
			F_CLR(gp, G_TMP_INUSE);
			msgq(sp, M_ERR,
			    "232|vi: temporary buffer not released");
		}
#endif
		/*
		 * If we're exiting this screen, move to the next one, or, if
		 * there aren't any more, return to the main editor loop.  The
		 * ordering is careful, don't discard the contents of sp until
		 * the end.
		 */
		if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) {
			if (file_end(sp, NULL, F_ISSET(sp, SC_EXIT_FORCE)))
				goto ret;
			if (vs_discard(sp, &next))
				goto ret;
			if (next == NULL && vs_swap(sp, &next, NULL))
				goto ret;
			*spp = next;
			if (screen_end(sp))
				goto ret;
			if (next == NULL)
				break;

			/* Switch screens, change focus. */
			sp = next;
			vip = VIP(sp);
			(void)sp->gp->scr_rename(sp, sp->frp->name, 1);

			/* Don't trust the cursor. */
			F_SET(vip, VIP_CUR_INVALID);

			continue;
		}

		/*
		 * Set the dot command structure.
		 *
		 * !!!
		 * Historically, commands which used mapped keys did not
		 * set the dot command, with the exception of the text
		 * input commands.
		 */
		if (F_ISSET(vp, V_DOT) && !mapped) {
			*DOT = cmd;
			F_SET(DOT, VC_ISDOT);

			/*
			 * If a count was supplied for both the command and
			 * its motion, the count was used only for the motion.
			 * Turn the count back on for the dot structure.
			 */
			if (F_ISSET(vp, VC_C1RESET))
				F_SET(DOT, VC_C1SET);

			/* VM flags aren't retained. */
			F_CLR(DOT, VM_COMMASK | VM_RCM_MASK);
		}

		/*
		 * Some vi row movements are "attracted" to the last position
		 * set, i.e. the VM_RCM commands are moths to the VM_RCM_SET
		 * commands' candle.  If the movement is to the EOL the vi
		 * command handles it.  If it's to the beginning, we handle it
		 * here.
		 *
		 * Note, some commands (e.g. _, ^) don't set the VM_RCM_SETFNB
		 * flag, but do the work themselves.  The reason is that they
		 * have to modify the column in case they're being used as a
		 * motion component.  Other similar commands (e.g. +, -) don't
		 * have to modify the column because they are always line mode
		 * operations when used as motions, so the column number isn't
		 * of any interest.
		 *
		 * Does this totally violate the screen and editor layering?
		 * You betcha.  As they say, if you think you understand it,
		 * you don't.
		 */
		switch (F_ISSET(vp, VM_RCM_MASK)) {
		case 0:
		case VM_RCM_SET:
			break;
		case VM_RCM:
			vp->m_final.cno = vs_rcm(sp,
			    vp->m_final.lno, F_ISSET(vip, VIP_RCM_LAST));
			break;
		case VM_RCM_SETLAST:
			F_SET(vip, VIP_RCM_LAST);
			break;
		case VM_RCM_SETFNB:
			vp->m_final.cno = 0;
			/* FALLTHROUGH */
		case VM_RCM_SETNNB:
			if (nonblank(sp, vp->m_final.lno, &vp->m_final.cno))
				goto err;
			break;
		default:
			abort();
		}

		/* Update the cursor. */
		sp->lno = vp->m_final.lno;
		sp->cno = vp->m_final.cno;

		/*
		 * Set the absolute mark -- set even if a tags or similar
		 * command, since the tag may be moving to the same file.
		 */
		if ((F_ISSET(vp, V_ABS) ||
		    (F_ISSET(vp, V_ABS_L) && sp->lno != abs.lno) ||
		    (F_ISSET(vp, V_ABS_C) &&
		    (sp->lno != abs.lno || sp->cno != abs.cno))) &&
		    mark_set(sp, ABSMARK1, &abs, 1))
			goto err;

		if (0) {
err:			if (v_event_flush(sp, CH_MAPPED))
				msgq(sp, M_BERR,
			    "110|Vi command failed: mapped keys discarded");
		}

		/*
		 * Check and clear interrupts.  There's an obvious race, but
		 * it's not worth fixing.
		 */
gc_err_noflush:	if (INTERRUPTED(sp)) {
intr:			CLR_INTERRUPT(sp);
			if (v_event_flush(sp, CH_MAPPED))
				msgq(sp, M_ERR,
				    "231|Interrupted: mapped keys discarded");
			else
				msgq(sp, M_ERR, "236|Interrupted");
		}

		/* If the last command switched screens, update. */
		if (F_ISSET(sp, SC_SSWITCH)) {
			F_CLR(sp, SC_SSWITCH);

			/*
			 * If the current screen is still displayed, it will
			 * need a new status line.
			 */
			F_SET(sp, SC_STATUS);

			/* Switch screens, change focus. */
			sp = sp->nextdisp;
			vip = VIP(sp);
			(void)sp->gp->scr_rename(sp, sp->frp->name, 1);

			/* Don't trust the cursor. */
			F_SET(vip, VIP_CUR_INVALID);

			/* Refresh so we can display messages. */
			if (vs_refresh(sp, 1))
				return (1);
		}

		/* If the last command switched files, change focus. */
		if (F_ISSET(sp, SC_FSWITCH)) {
			F_CLR(sp, SC_FSWITCH);
			(void)sp->gp->scr_rename(sp, sp->frp->name, 1);
		}

		/* If leaving vi, return to the main editor loop. */
		if (F_ISSET(gp, G_SRESTART) || F_ISSET(sp, SC_EX)) {
			*spp = sp;
			v_dtoh(sp);
			gp->scr_discard(sp, NULL);
			break;
		}
	}
	if (0)
ret:		rval = 1;
	return (rval);
}