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