1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
28
29
30 /* Copyright (c) 1981 Regents of the University of California */
31
32 #include "ex.h"
33 #include "ex_argv.h"
34 #include "ex_temp.h"
35 #include "ex_tty.h"
36 #include "ex_vis.h"
37 #include <unistd.h>
38
39 extern bool pflag, nflag; /* extern; also in ex_cmds.c */
40 extern int poffset; /* extern; also in ex_cmds.c */
41 extern short slevel; /* extern; has level of source() */
42
43 /*
44 * Subroutines for major command loop.
45 */
46
47 /*
48 * Is there a single letter indicating a named buffer next?
49 */
50 int
cmdreg(void)51 cmdreg(void)
52 {
53 int c = 0;
54 int wh = skipwh();
55
56 #ifdef XPG4
57 if (wh && isalpha(c = peekchar()) && isascii(c) && !isdigit(c))
58 #else /* XPG4 */
59 if (wh && isalpha(c = peekchar()) && isascii(c))
60 #endif /* XPG4 */
61 c = getchar();
62
63 #ifdef XPG4
64 if (isdigit(c)) {
65 c = 0;
66 }
67 #endif /* XPG4 */
68 return (c);
69 }
70
71 /*
72 * Tell whether the character ends a command
73 */
74 int
endcmd(int ch)75 endcmd(int ch)
76 {
77 switch (ch) {
78
79 case '\n':
80 case EOF:
81 endline = 1;
82 return (1);
83
84 case '|':
85 case '"':
86 endline = 0;
87 return (1);
88 }
89 return (0);
90 }
91
92 /*
93 * Insist on the end of the command.
94 */
95 void
eol(void)96 eol(void)
97 {
98
99 if (!skipend())
100 error(value(vi_TERSE) ? gettext("Extra chars") :
101 gettext("Extra characters at end of command"));
102 ignnEOF();
103 }
104
105 #ifdef XPG4
106 /*
107 * Print out the message in the error message file at str,
108 * with i an integer argument to printf.
109 */
110 /*VARARGS2*/
111 void
error(str,i)112 error(str, i)
113 unsigned char *str;
114 int i;
115 {
116 tagflg = 0;
117 errcnt++;
118 noerror(str, i);
119 }
120
121 /*
122 * noerror(): like error(), but doesn't inc errcnt.
123 * the reason why we created this routine, instead of fixing up errcnt
124 * after error() is called, is because we will do a longjmp, and
125 * not a return. it does other things closing file i/o, reset, etc;
126 * so we follow those procedures.
127 */
128 /*VARARGS2*/
129 void
noerror(str,i)130 noerror(str, i)
131 unsigned char *str;
132 int i;
133 {
134
135 error0();
136 merror(str, i);
137 if (writing) {
138 serror((unsigned char *)
139 gettext(" [Warning - %s is incomplete]"), file);
140 writing = 0;
141 }
142 error1(str);
143 }
144
145 #else /* !XPG4 */
146 /*
147 * Print out the message in the error message file at str,
148 * with i an integer argument to printf.
149 */
150 /*VARARGS2*/
151 void
error(str,i)152 error(str, i)
153 unsigned char *str;
154 int i;
155 {
156 tagflg = 0;
157 errcnt++;
158 error0();
159 merror(str, i);
160 if (writing) {
161 serror((unsigned char *)
162 gettext(" [Warning - %s is incomplete]"), file);
163 writing = 0;
164 }
165 error1(str);
166 }
167 #endif /* XPG4 */
168
169 /*
170 * Rewind the argument list.
171 */
172 void
erewind(void)173 erewind(void)
174 {
175
176 argc = argc0;
177 argv = argv0;
178 args = args0;
179 if (argc > 1 && !hush && cur_term) {
180 viprintf(mesg(value(vi_TERSE) ? gettext("%d files") :
181 gettext("%d files to edit")), argc);
182 if (inopen)
183 putchar(' ');
184 else
185 putNFL();
186 }
187 }
188
189 /*
190 * Guts of the pre-printing error processing.
191 * If in visual and catching errors, then we don't mung up the internals,
192 * just fixing up the echo area for the print.
193 * Otherwise we reset a number of externals, and discard unused input.
194 */
195 void
error0(void)196 error0(void)
197 {
198
199 if (laste) {
200 #ifdef VMUNIX
201 tlaste();
202 #endif
203 laste = 0;
204 sync();
205 }
206 if (vcatch) {
207 if (splitw == 0)
208 fixech();
209 if (!enter_standout_mode || !exit_bold)
210 dingdong();
211 return;
212 }
213 if (input) {
214 input = strend(input) - 1;
215 if (*input == '\n')
216 setlastchar('\n');
217 input = 0;
218 }
219 setoutt();
220 flush();
221 resetflav();
222 if (!enter_standout_mode || !exit_bold)
223 dingdong();
224 if (inopen) {
225 /*
226 * We are coming out of open/visual ungracefully.
227 * Restore columns, undo, and fix tty mode.
228 */
229 columns = OCOLUMNS;
230 undvis();
231 ostop(normf);
232 /* ostop should be doing this
233 putpad(cursor_normal);
234 putpad(key_eol);
235 */
236 putnl();
237 }
238 inopen = 0;
239 holdcm = 0;
240 }
241
242 /*
243 * Post error printing processing.
244 * Close the i/o file if left open.
245 * If catching in visual then throw to the visual catch,
246 * else if a child after a fork, then exit.
247 * Otherwise, in the normal command mode error case,
248 * finish state reset, and throw to top.
249 */
250 void
error1(unsigned char * str)251 error1(unsigned char *str)
252 {
253 bool die;
254 extern short ttyindes;
255
256 if ((io > 0) && (io != ttyindes)) {
257 close(io);
258 io = -1;
259 }
260
261 die = (getpid() != ppid); /* Only children die */
262 inappend = inglobal = 0;
263 globp = vglobp = vmacp = 0;
264 if (vcatch && !die) {
265 inopen = 1;
266 vcatch = 0;
267 if (str)
268 noonl();
269 fixol();
270 if (slevel > 0)
271 reset();
272 longjmp(vreslab,1);
273 }
274 if (str && !vcatch)
275 putNFL();
276 if (die)
277 exit(++errcnt);
278 lseek(0, 0L, 2);
279 if (inglobal)
280 setlastchar('\n');
281
282 if (inexrc) {
283 /*
284 * Set inexrc to 0 so that this error is printed only
285 * once (eg. when stdin is redirected from /dev/null and
286 * vi prints "Input read error" because it is unable to
287 * read() the <CR>).
288 */
289 inexrc = 0;
290 lprintf(gettext(
291 "Error detected in .exrc.[Hit return to continue] "),
292 0);
293 putNFL();
294 getkey();
295 }
296
297 while ((lastchar() != '\n') && (lastchar() != EOF))
298 ignchar();
299 ungetchar(0);
300 endline = 1;
301 reset();
302 }
303
304 void
fixol(void)305 fixol(void)
306 {
307 if (Outchar != vputchar) {
308 flush();
309 if (state == ONEOPEN || state == HARDOPEN)
310 outline = destline = 0;
311 Outchar = vputchar;
312 vcontin(1);
313 /*
314 * Outchar could be set to termchar() through vcontin().
315 * So reset it again.
316 */
317 Outchar = vputchar;
318 } else {
319 if (destcol)
320 vclreol();
321 vclean();
322 }
323 }
324
325 /*
326 * Does an ! character follow in the command stream?
327 */
328 int
exclam(void)329 exclam(void)
330 {
331
332 if (peekchar() == '!') {
333 ignchar();
334 return (1);
335 }
336 return (0);
337 }
338
339 /*
340 * Make an argument list for e.g. next.
341 */
342 void
makargs(void)343 makargs(void)
344 {
345
346 glob(&frob);
347 argc0 = frob.argc0;
348 argv0 = frob.argv;
349 args0 = argv0[0];
350 erewind();
351 }
352
353 /*
354 * Advance to next file in argument list.
355 */
356 void
next(void)357 next(void)
358 {
359 extern short isalt; /* defined in ex_io.c */
360
361 if (argc == 0)
362 error(value(vi_TERSE) ?
363 (unsigned char *)gettext("No more files") :
364 (unsigned char *)gettext("No more files to edit"));
365 morargc = argc;
366 isalt = (strcmp(altfile, args)==0) + 1;
367 if (savedfile[0])
368 CP(altfile, savedfile);
369 (void) strlcpy(savedfile, args, sizeof (savedfile));
370 argc--;
371 args = argv ? *++argv : strend(args) + 1;
372 #if i386 || i286
373 destcol = 0;
374 #endif
375 }
376
377 /*
378 * Eat trailing flags and offsets after a command,
379 * saving for possible later post-command prints.
380 */
381 void
donewline(void)382 donewline(void)
383 {
384 int c;
385
386 resetflav();
387 for (;;) {
388 c = getchar();
389 switch (c) {
390
391 case '^':
392 case '-':
393 poffset--;
394 break;
395
396 case '+':
397 poffset++;
398 break;
399
400 case 'l':
401 listf++;
402 break;
403
404 case '#':
405 nflag++;
406 break;
407
408 case 'p':
409 listf = 0;
410 break;
411
412 case ' ':
413 case '\t':
414 continue;
415
416 case '"':
417 comment();
418 setflav();
419 return;
420
421 default:
422 if (!endcmd(c))
423 serror(value(vi_TERSE) ?
424 (unsigned char *)gettext("Extra chars") :
425 (unsigned char *)gettext(
426 "Extra characters at end of \"%s\" command"),
427 Command);
428 if (c == EOF)
429 ungetchar(c);
430 setflav();
431 return;
432 }
433 pflag++;
434 }
435 }
436
437 /*
438 * Before quit or respec of arg list, check that there are
439 * no more files in the arg list.
440 */
441 int
nomore(void)442 nomore(void)
443 {
444
445 if (argc == 0 || morargc == argc)
446 return(0);
447 morargc = argc;
448 if (argc == 1) {
449 merror(value(vi_TERSE) ? gettext("1 more file") :
450 gettext("1 more file to edit"), argc);
451 } else {
452 merror(value(vi_TERSE) ? gettext("%d more files") :
453 gettext("%d more files to edit"), argc);
454 }
455 return(1);
456 }
457
458 /*
459 * Before edit of new file check that either an ! follows
460 * or the file has not been changed.
461 */
462 int
quickly(void)463 quickly(void)
464 {
465
466 if (exclam())
467 return (1);
468 if (chng && dol > zero) {
469 /*
470 chng = 0;
471 */
472 xchng = 0;
473 error(value(vi_TERSE) ? (unsigned char *)gettext("No write") :
474 (unsigned char *)
475 gettext("No write since last change (:%s! overrides)"),
476 Command);
477 }
478 return (0);
479 }
480
481 /*
482 * Reset the flavor of the output to print mode with no numbering.
483 */
484 void
resetflav(void)485 resetflav(void)
486 {
487
488 if (inopen)
489 return;
490 listf = 0;
491 nflag = 0;
492 pflag = 0;
493 poffset = 0;
494 setflav();
495 }
496
497 /*
498 * Print an error message with a %s type argument to printf.
499 * Message text comes from error message file.
500 */
501 void
serror(unsigned char * str,unsigned char * cp)502 serror(unsigned char *str, unsigned char *cp)
503 {
504 tagflg = 0;
505 error0();
506 smerror(str, cp);
507 error1(str);
508 }
509
510 /*
511 * Set the flavor of the output based on the flags given
512 * and the number and list options to either number or not number lines
513 * and either use normally decoded (ARPAnet standard) characters or list mode,
514 * where end of lines are marked and tabs print as ^I.
515 */
516 void
setflav(void)517 setflav(void)
518 {
519
520 if (inopen)
521 return;
522 setnumb(nflag || value(vi_NUMBER));
523 setlist(listf || value(vi_LIST));
524 if (!inopen)
525 setoutt();
526 }
527
528 /*
529 * Skip white space and tell whether command ends then.
530 */
531 int
skipend(void)532 skipend(void)
533 {
534
535 pastwh();
536 return (endcmd(peekchar()) && peekchar() != '"');
537 }
538
539 /*
540 * Set the command name for non-word commands.
541 */
542 void
tailspec(int c)543 tailspec(int c)
544 {
545 static unsigned char foocmd[2];
546
547 foocmd[0] = c;
548 Command = foocmd;
549 }
550
551 /*
552 * Try to read off the rest of the command word.
553 * If alphabetics follow, then this is not the command we seek.
554 */
555 void
tail(unsigned char * comm)556 tail(unsigned char *comm)
557 {
558
559 tailprim(comm, 1, 0);
560 }
561
562 void
tail2of(unsigned char * comm)563 tail2of(unsigned char *comm)
564 {
565
566 tailprim(comm, 2, 0);
567 }
568
569 unsigned char tcommand[20];
570
571 void
tailprim(unsigned char * comm,int i,bool notinvis)572 tailprim(unsigned char *comm, int i, bool notinvis)
573 {
574 unsigned char *cp;
575 int c;
576
577 Command = comm;
578 for (cp = tcommand; i > 0; i--)
579 *cp++ = *comm++;
580 while (*comm && peekchar() == *comm)
581 *cp++ = getchar(), comm++;
582 c = peekchar();
583 if (notinvis || (isalpha(c) && isascii(c))) {
584 /*
585 * Of the trailing lp funny business, only dl and dp
586 * survive the move from ed to ex.
587 */
588 if (tcommand[0] == 'd' && any(c, "lp"))
589 goto ret;
590 if (tcommand[0] == 's' && any(c, "gcr"))
591 goto ret;
592 while (cp < &tcommand[19] && isalpha(c = peekchar()) && isascii(c))
593 *cp++ = getchar();
594 *cp = 0;
595 if (notinvis)
596 serror(value(vi_TERSE) ?
597 (unsigned char *)gettext("What?") :
598 (unsigned char *)gettext(
599 "%s: No such command from open/visual"), tcommand);
600 else
601 serror(value(vi_TERSE) ?
602 (unsigned char *)gettext("What?") :
603 (unsigned char *)gettext(
604 "%s: Not an editor command"), tcommand);
605 }
606 ret:
607 *cp = 0;
608 }
609
610 /*
611 * Continue after a : command from open/visual.
612 */
613 void
vcontin(bool ask)614 vcontin(bool ask)
615 {
616
617 if (vcnt > 0)
618 vcnt = -vcnt;
619 if (inopen) {
620 if (state != VISUAL) {
621 /*
622 * We don't know what a shell command may have left on
623 * the screen, so we move the cursor to the right place
624 * and then put out a newline. But this makes an extra
625 * blank line most of the time so we only do it for :sh
626 * since the prompt gets left on the screen.
627 *
628 * BUG: :!echo longer than current line \\c
629 * will mess it up.
630 */
631 if (state == CRTOPEN) {
632 termreset();
633 vgoto(WECHO, 0);
634 }
635 if (!ask) {
636 (void) putch('\r');
637 (void) putch('\n');
638 }
639 return;
640 }
641 if (ask) {
642 merror(gettext("[Hit return to continue] "));
643 flush();
644 }
645 #ifndef CBREAK
646 vraw();
647 #endif
648 if (ask) {
649 #ifdef notdef
650 /*
651 * Gobble ^Q/^S since the tty driver should be eating
652 * them (as far as the user can see)
653 */
654 while (peekkey() == CTRL('Q') || peekkey() == CTRL('S'))
655 ignore(getkey());
656 #endif
657 if(getkey() == ':') {
658 /* Extra newlines, but no other way */
659 (void) putch('\n');
660 outline = WECHO;
661 ungetkey(':');
662 }
663 }
664 vclrech(1);
665 if (Peekkey != ':') {
666 fixterm();
667 putpad((unsigned char *)enter_ca_mode);
668 tostart();
669 }
670 }
671 }
672
673 /*
674 * Put out a newline (before a shell escape)
675 * if in open/visual.
676 */
677 void
vnfl(void)678 vnfl(void)
679 {
680
681 if (inopen) {
682 if (state != VISUAL && state != CRTOPEN && destline <= WECHO)
683 vclean();
684 else
685 vmoveitup(1, 0);
686 vgoto(WECHO, 0);
687 vclrbyte(vtube[WECHO], WCOLS);
688 tostop();
689 /* replaced by the ostop above
690 putpad(cursor_normal);
691 putpad(key_eol);
692 */
693 }
694 flush();
695 }
696