Remove building with NOCRYPTO option
[minix.git] / external / bsd / nvi / dist / ex / ex_txt.c
blob3d03f7b6a7d2513d29360bbfaeee49517d4da81a
1 /* $NetBSD: ex_txt.c,v 1.4 2014/01/26 21:43:45 christos Exp $ */
2 /*-
3 * Copyright (c) 1992, 1993, 1994
4 * The Regents of the University of California. All rights reserved.
5 * Copyright (c) 1992, 1993, 1994, 1995, 1996
6 * Keith Bostic. All rights reserved.
8 * See the LICENSE file for redistribution information.
9 */
11 #include "config.h"
13 #include <sys/cdefs.h>
14 #if 0
15 #ifndef lint
16 static const char sccsid[] = "Id: ex_txt.c,v 10.23 2001/06/25 15:19:21 skimo Exp (Berkeley) Date: 2001/06/25 15:19:21 ";
17 #endif /* not lint */
18 #else
19 __RCSID("$NetBSD: ex_txt.c,v 1.4 2014/01/26 21:43:45 christos Exp $");
20 #endif
22 #include <sys/types.h>
23 #include <sys/queue.h>
24 #include <sys/time.h>
26 #include <bitstring.h>
27 #include <ctype.h>
28 #include <limits.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
33 #include "../common/common.h"
34 #include "../vi/vi.h"
37 * !!!
38 * The backslash characters was special when it preceded a newline as part of
39 * a substitution replacement pattern. For example, the input ":a\<cr>" would
40 * failed immediately with an error, as the <cr> wasn't part of a substitution
41 * replacement pattern. This implies a frightening integration of the editor
42 * and the parser and/or the RE engine. There's no way I'm going to reproduce
43 * those semantics.
45 * So, if backslashes are special, this code inserts the backslash and the next
46 * character into the string, without regard for the character or the command
47 * being entered. Since "\<cr>" was illegal historically (except for the one
48 * special case), and the command will fail eventually, no historical scripts
49 * should break (presuming they didn't depend on the failure mode itself or the
50 * characters remaining when failure occurred.
53 static int txt_dent __P((SCR *, TEXT *));
54 static void txt_prompt __P((SCR *, TEXT *, ARG_CHAR_T, u_int32_t));
57 * ex_txt --
58 * Get lines from the terminal for ex.
60 * PUBLIC: int ex_txt __P((SCR *, TEXTH *, ARG_CHAR_T, u_int32_t));
62 int
63 ex_txt(SCR *sp, TEXTH *tiqh, ARG_CHAR_T prompt, u_int32_t flags)
65 EVENT ev;
66 GS *gp;
67 TEXT ait, *ntp, *tp;
68 carat_t carat_st;
69 size_t cnt;
70 int rval;
71 int nochange;
73 rval = 0;
76 * Get a TEXT structure with some initial buffer space, reusing the
77 * last one if it's big enough. (All TEXT bookkeeping fields default
78 * to 0 -- text_init() handles this.)
80 if ((tp = TAILQ_FIRST(tiqh)) != NULL) {
81 if (TAILQ_NEXT(tp, q) != NULL || tp->lb_len < 32) {
82 text_lfree(tiqh);
83 goto newtp;
85 tp->len = 0;
86 } else {
87 newtp: if ((tp = text_init(sp, NULL, 0, 32)) == NULL)
88 goto err;
89 TAILQ_INSERT_HEAD(tiqh, tp, q);
92 /* Set the starting line number. */
93 tp->lno = sp->lno + 1;
96 * If it's a terminal, set up autoindent, put out the prompt, and
97 * set it up so we know we were suspended. Otherwise, turn off
98 * the autoindent flag, as that requires less special casing below.
100 * XXX
101 * Historic practice is that ^Z suspended command mode (but, because
102 * it ran in cooked mode, it was unaffected by the autowrite option.)
103 * On restart, any "current" input was discarded, whether in insert
104 * mode or not, and ex was in command mode. This code matches historic
105 * practice, but not 'cause it's easier.
107 gp = sp->gp;
108 if (F_ISSET(gp, G_SCRIPTED))
109 LF_CLR(TXT_AUTOINDENT);
110 else {
111 if (LF_ISSET(TXT_AUTOINDENT)) {
112 LF_SET(TXT_EOFCHAR);
113 if (v_txt_auto(sp, sp->lno, NULL, 0, tp))
114 goto err;
116 txt_prompt(sp, tp, prompt, flags);
119 for (carat_st = C_NOTSET, nochange = 0;;) {
120 if (v_event_get(sp, &ev, 0, 0))
121 goto err;
123 /* Deal with all non-character events. */
124 switch (ev.e_event) {
125 case E_CHARACTER:
126 break;
127 case E_ERR:
128 goto err;
129 case E_REPAINT:
130 case E_WRESIZE:
131 continue;
132 case E_EOF:
133 rval = 1;
134 /* FALLTHROUGH */
135 case E_INTERRUPT:
137 * Handle EOF/SIGINT events by discarding partially
138 * entered text and returning. EOF returns failure,
139 * E_INTERRUPT returns success.
141 goto notlast;
142 default:
143 v_event_err(sp, &ev);
144 goto notlast;
148 * Deal with character events.
150 * Check to see if the character fits into the input buffer.
151 * (Use tp->len, ignore overwrite and non-printable chars.)
153 BINC_GOTOW(sp, tp->lb, tp->lb_len, tp->len + 1);
155 switch (ev.e_value) {
156 case K_CR:
158 * !!!
159 * Historically, <carriage-return>'s in the command
160 * weren't special, so the ex parser would return an
161 * unknown command error message. However, if they
162 * terminated the command if they were in a map. I'm
163 * pretty sure this still isn't right, but it handles
164 * what I've seen so far.
166 if (!FL_ISSET(ev.e_flags, CH_MAPPED))
167 goto ins_ch;
168 /* FALLTHROUGH */
169 case K_NL:
171 * '\' can escape <carriage-return>/<newline>. We
172 * don't discard the backslash because we need it
173 * to get the <newline> through the ex parser.
175 if (LF_ISSET(TXT_BACKSLASH) &&
176 tp->len != 0 && tp->lb[tp->len - 1] == '\\')
177 goto ins_ch;
180 * CR returns from the ex command line.
182 * XXX
183 * Terminate with a nul, needed by filter.
185 if (LF_ISSET(TXT_CR)) {
186 tp->lb[tp->len] = '\0';
187 goto done;
191 * '.' may terminate text input mode; free the current
192 * TEXT.
194 if (LF_ISSET(TXT_DOTTERM) && tp->len == tp->ai + 1 &&
195 tp->lb[tp->len - 1] == '.') {
196 notlast: TAILQ_REMOVE(tiqh, tp, q);
197 text_free(tp);
198 goto done;
201 /* Set up bookkeeping for the new line. */
202 if ((ntp = text_init(sp, NULL, 0, 32)) == NULL)
203 goto err;
204 ntp->lno = tp->lno + 1;
207 * Reset the autoindent line value. 0^D keeps the ai
208 * line from changing, ^D changes the level, even if
209 * there were no characters in the old line. Note, if
210 * using the current tp structure, use the cursor as
211 * the length, the autoindent characters may have been
212 * erased.
214 if (LF_ISSET(TXT_AUTOINDENT)) {
215 if (nochange) {
216 nochange = 0;
217 if (v_txt_auto(sp,
218 OOBLNO, &ait, ait.ai, ntp))
219 goto err;
220 free(ait.lb);
221 } else
222 if (v_txt_auto(sp,
223 OOBLNO, tp, tp->len, ntp))
224 goto err;
225 carat_st = C_NOTSET;
227 txt_prompt(sp, ntp, prompt, flags);
230 * Swap old and new TEXT's, and insert the new TEXT
231 * into the queue.
233 tp = ntp;
234 TAILQ_INSERT_TAIL(tiqh, tp, q);
235 break;
236 case K_CARAT: /* Delete autoindent chars. */
237 if (tp->len <= tp->ai && LF_ISSET(TXT_AUTOINDENT))
238 carat_st = C_CARATSET;
239 goto ins_ch;
240 case K_ZERO: /* Delete autoindent chars. */
241 if (tp->len <= tp->ai && LF_ISSET(TXT_AUTOINDENT))
242 carat_st = C_ZEROSET;
243 goto ins_ch;
244 case K_CNTRLD: /* Delete autoindent char. */
246 * !!!
247 * Historically, the ^D command took (but then ignored)
248 * a count. For simplicity, we don't return it unless
249 * it's the first character entered. The check for len
250 * equal to 0 is okay, TXT_AUTOINDENT won't be set.
252 if (LF_ISSET(TXT_CNTRLD)) {
253 for (cnt = 0; cnt < tp->len; ++cnt)
254 if (!ISBLANK((UCHAR_T)tp->lb[cnt]))
255 break;
256 if (cnt == tp->len) {
257 tp->len = 1;
258 tp->lb[0] = ev.e_c;
259 tp->lb[1] = '\0';
262 * Put out a line separator, in case
263 * the command fails.
265 (void)putchar('\n');
266 goto done;
271 * POSIX 1003.1b-1993, paragraph 7.1.1.9, states that
272 * the EOF characters are discarded if there are other
273 * characters to process in the line, i.e. if the EOF
274 * is not the first character in the line. For this
275 * reason, historic ex discarded the EOF characters,
276 * even if occurring in the middle of the input line.
277 * We match that historic practice.
279 * !!!
280 * The test for discarding in the middle of the line is
281 * done in the switch, because the CARAT forms are N+1,
282 * not N.
284 * !!!
285 * There's considerable magic to make the terminal code
286 * return the EOF character at all. See that code for
287 * details.
289 if (!LF_ISSET(TXT_AUTOINDENT) || tp->len == 0)
290 continue;
291 switch (carat_st) {
292 case C_CARATSET: /* ^^D */
293 if (tp->len > tp->ai + 1)
294 continue;
296 /* Save the ai string for later. */
297 ait.lb = NULL;
298 ait.lb_len = 0;
299 BINC_GOTOW(sp, ait.lb, ait.lb_len, tp->ai);
300 MEMCPYW(ait.lb, tp->lb, tp->ai);
301 ait.ai = ait.len = tp->ai;
303 carat_st = C_NOTSET;
304 nochange = 1;
305 goto leftmargin;
306 case C_ZEROSET: /* 0^D */
307 if (tp->len > tp->ai + 1)
308 continue;
310 carat_st = C_NOTSET;
311 leftmargin: (void)gp->scr_ex_adjust(sp, EX_TERM_CE);
312 tp->ai = tp->len = 0;
313 break;
314 case C_NOTSET: /* ^D */
315 if (tp->len > tp->ai)
316 continue;
318 if (txt_dent(sp, tp))
319 goto err;
320 break;
321 default:
322 abort();
325 /* Clear and redisplay the line. */
326 (void)gp->scr_ex_adjust(sp, EX_TERM_CE);
327 txt_prompt(sp, tp, prompt, flags);
328 break;
329 default:
331 * See the TXT_BEAUTIFY comment in vi/v_txt_ev.c.
333 * Silently eliminate any iscntrl() character that was
334 * not already handled specially, except for <tab> and
335 * <ff>.
337 ins_ch: if (LF_ISSET(TXT_BEAUTIFY) && ISCNTRL(ev.e_c) &&
338 ev.e_value != K_FORMFEED && ev.e_value != K_TAB)
339 break;
341 tp->lb[tp->len++] = ev.e_c;
342 break;
345 /* NOTREACHED */
347 done: return (rval);
349 err:
350 alloc_err:
351 return (1);
355 * txt_prompt --
356 * Display the ex prompt, line number, ai characters. Characters had
357 * better be printable by the terminal driver, but that's its problem,
358 * not ours.
360 static void
361 txt_prompt(SCR *sp, TEXT *tp, ARG_CHAR_T prompt, u_int32_t flags)
363 /* Display the prompt. */
364 if (LF_ISSET(TXT_PROMPT))
365 (void)ex_printf(sp, "%c", prompt);
367 /* Display the line number. */
368 if (LF_ISSET(TXT_NUMBER) && O_ISSET(sp, O_NUMBER))
369 (void)ex_printf(sp, "%6lu ", (u_long)tp->lno);
371 /* Print out autoindent string. */
372 if (LF_ISSET(TXT_AUTOINDENT)) {
373 const char *nstr;
374 size_t nlen;
375 INT2CHAR(sp, tp->lb, tp->ai + 1, nstr, nlen);
376 (void)ex_printf(sp, "%.*s", (int)tp->ai, nstr);
378 (void)ex_fflush(sp);
382 * txt_dent --
383 * Handle ^D outdents.
385 * Ex version of vi/v_ntext.c:txt_dent(). See that code for the (usual)
386 * ranting and raving. This is a fair bit simpler as ^T isn't special.
388 static int
389 txt_dent(SCR *sp, TEXT *tp)
391 u_long sw, ts;
392 size_t cno, off, scno, spaces, tabs;
394 ts = O_VAL(sp, O_TABSTOP);
395 sw = O_VAL(sp, O_SHIFTWIDTH);
397 /* Get the current screen column. */
398 for (off = scno = 0; off < tp->len; ++off)
399 if (tp->lb[off] == '\t')
400 scno += COL_OFF(scno, ts);
401 else
402 ++scno;
404 /* Get the previous shiftwidth column. */
405 cno = scno--;
406 scno -= scno % sw;
409 * Since we don't know what comes before the character(s) being
410 * deleted, we have to resolve the autoindent characters . The
411 * example is a <tab>, which doesn't take up a full shiftwidth
412 * number of columns because it's preceded by <space>s. This is
413 * easy to get if the user sets shiftwidth to a value less than
414 * tabstop, and then uses ^T to indent, and ^D to outdent.
416 * Count up spaces/tabs needed to get to the target.
418 cno = 0;
419 tabs = 0;
420 if (!O_ISSET(sp, O_EXPANDTAB)) {
421 for (; cno + COL_OFF(cno, ts) <= scno; ++tabs)
422 cno += COL_OFF(cno, ts);
424 spaces = scno - cno;
426 /* Make sure there's enough room. */
427 BINC_RETW(sp, tp->lb, tp->lb_len, tabs + spaces + 1);
429 /* Adjust the final ai character count. */
430 tp->ai = tabs + spaces;
432 /* Enter the replacement characters. */
433 for (tp->len = 0; tabs > 0; --tabs)
434 tp->lb[tp->len++] = '\t';
435 for (; spaces > 0; --spaces)
436 tp->lb[tp->len++] = ' ';
437 return (0);