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