xref: /netbsd/external/bsd/nvi/dist/common/main.c (revision cc73507a)
1 /*	$NetBSD: main.c,v 1.6 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.
7  *
8  * See the LICENSE file for redistribution information.
9  */
10 
11 #include "config.h"
12 
13 #include <sys/cdefs.h>
14 #if 0
15 #ifndef lint
16 static const char copyright[] =
17 "%Z% Copyright (c) 1992, 1993, 1994\n\
18 	The Regents of the University of California.  All rights reserved.\n\
19 %Z% Copyright (c) 1992, 1993, 1994, 1995, 1996\n\
20 	Keith Bostic.  All rights reserved.\n";
21 #endif /* not lint */
22 #else
23 __RCSID("$NetBSD: main.c,v 1.6 2014/01/26 21:43:45 christos Exp $");
24 #endif
25 
26 #include <sys/cdefs.h>
27 #if 0
28 #ifndef lint
29 static const char sccsid[] = "Id: main.c,v 10.63 2001/11/01 15:24:43 skimo Exp  (Berkeley) Date: 2001/11/01 15:24:43 ";
30 #endif /* not lint */
31 #else
32 __RCSID("$NetBSD: main.c,v 1.6 2014/01/26 21:43:45 christos Exp $");
33 #endif
34 
35 #include <sys/types.h>
36 #include <sys/queue.h>
37 #include <sys/stat.h>
38 #include <sys/time.h>
39 
40 #include <bitstring.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <limits.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48 
49 #include "common.h"
50 #include "../vi/vi.h"
51 #include "pathnames.h"
52 
53 static void	 v_estr __P((const char *, int, const char *));
54 static int	 v_obsolete __P((char *, char *[]));
55 
56 /*
57  * editor --
58  *	Main editor routine.
59  *
60  * PUBLIC: int editor __P((WIN *, int, char *[]));
61  */
62 int
editor(WIN * wp,int argc,char ** argv)63 editor(WIN *wp, int argc, char **argv)
64 {
65 	extern int optind;
66 	extern char *optarg;
67 	const char *p;
68 	EVENT ev;
69 	FREF *frp;
70 	SCR *sp;
71 	GS *gp;
72 	size_t len;
73 	u_int flags;
74 	int ch, flagchk, lflag, secure, startup, readonly, rval, silent;
75 #ifdef GTAGS
76 	int gtags = 0;
77 #endif
78 	char *tag_f, *wsizearg, path[256];
79 	const CHAR_T *w;
80 	size_t wlen;
81 
82 	gp = wp->gp;
83 
84 	/* Initialize the busy routine, if not defined by the screen. */
85 	if (gp->scr_busy == NULL)
86 		gp->scr_busy = vs_busy;
87 	/* Initialize the message routine, if not defined by the screen. */
88 	if (wp->scr_msg == NULL)
89 		wp->scr_msg = vs_msg;
90 
91 	/* Set initial screen type and mode based on the program name. */
92 	readonly = 0;
93 	if (!strcmp(gp->progname, "ex") || !strcmp(gp->progname, "nex"))
94 		LF_INIT(SC_EX);
95 	else {
96 		/* Nview, view are readonly. */
97 		if (!strcmp(gp->progname, "nview") ||
98 		    !strcmp(gp->progname, "view"))
99 			readonly = 1;
100 
101 		/* Vi is the default. */
102 		LF_INIT(SC_VI);
103 	}
104 
105 	/* Convert old-style arguments into new-style ones. */
106 	if (v_obsolete(gp->progname, argv))
107 		return (1);
108 
109 	/* Parse the arguments. */
110 	flagchk = '\0';
111 	tag_f = wsizearg = NULL;
112 	lflag = secure = silent = 0;
113 	startup = 1;
114 
115 	/* Set the file snapshot flag. */
116 	F_SET(gp, G_SNAPSHOT);
117 
118 	while ((ch = getopt(argc, argv, "c:"
119 #ifdef DEBUG
120 	    "D:"
121 #endif
122 	    "eF"
123 #ifdef GTAGS
124 	    "G"
125 #endif
126 	    "lRrSsT:t:vw:")) != EOF)
127 		switch (ch) {
128 		case 'c':		/* Run the command. */
129 			/*
130 			 * XXX
131 			 * We should support multiple -c options.
132 			 */
133 			if (gp->c_option != NULL) {
134 				v_estr(gp->progname, 0,
135 				    "only one -c command may be specified.");
136 				return (1);
137 			}
138 			gp->c_option = optarg;
139 			break;
140 #ifdef DEBUG
141 		case 'D':
142 			switch (optarg[0]) {
143 			case 's':
144 				startup = 0;
145 				break;
146 			case 'w':
147 				attach(gp);
148 				break;
149 			default:
150 				v_estr(gp->progname, 0,
151 				    "usage: -D requires s or w argument.");
152 				return (1);
153 			}
154 			break;
155 #endif
156 		case 'e':		/* Ex mode. */
157 			LF_CLR(SC_VI);
158 			LF_SET(SC_EX);
159 			break;
160 		case 'F':		/* No snapshot. */
161 			v_estr(gp->progname, 0,
162 			    "-F option no longer supported.");
163 			break;
164 		case 'l':		/* Set lisp, showmatch options. */
165 			lflag = 1;
166 			break;
167 #ifdef GTAGS
168 		case 'G':               /* gtags mode. */
169 			gtags = 1;
170 			break;
171 #endif
172 		case 'R':		/* Readonly. */
173 			readonly = 1;
174 			break;
175 		case 'r':		/* Recover. */
176 			if (flagchk == 't') {
177 				v_estr(gp->progname, 0,
178 				    "only one of -r and -t may be specified.");
179 				return (1);
180 			}
181 			flagchk = 'r';
182 			break;
183 		case 'S':
184 			secure = 1;
185 			break;
186 		case 's':
187 			silent = 1;
188 			break;
189 #ifdef TRACE
190 		case 'T':		/* Trace. */
191 			(void)vtrace_init(optarg);
192 			break;
193 #endif
194 		case 't':		/* Tag. */
195 			if (flagchk == 'r') {
196 				v_estr(gp->progname, 0,
197 				    "only one of -r and -t may be specified.");
198 				return (1);
199 			}
200 			if (flagchk == 't') {
201 				v_estr(gp->progname, 0,
202 				    "only one tag file may be specified.");
203 				return (1);
204 			}
205 			flagchk = 't';
206 			tag_f = optarg;
207 			break;
208 		case 'v':		/* Vi mode. */
209 			LF_CLR(SC_EX);
210 			LF_SET(SC_VI);
211 			break;
212 		case 'w':
213 			wsizearg = optarg;
214 			break;
215 		case '?':
216 		default:
217 			(void)gp->scr_usage();
218 			return (1);
219 		}
220 	argc -= optind;
221 	argv += optind;
222 
223 	/*
224 	 * -s option is only meaningful to ex.
225 	 *
226 	 * If not reading from a terminal, it's like -s was specified.
227 	 */
228 	if (silent && !LF_ISSET(SC_EX)) {
229 		v_estr(gp->progname, 0, "-s option is only applicable to ex.");
230 		goto err;
231 	}
232 	if (LF_ISSET(SC_EX) && F_ISSET(gp, G_SCRIPTED))
233 		silent = 1;
234 
235 	/*
236 	 * Build and initialize the first/current screen.  This is a bit
237 	 * tricky.  If an error is returned, we may or may not have a
238 	 * screen structure.  If we have a screen structure, put it on a
239 	 * display queue so that the error messages get displayed.
240 	 *
241 	 * !!!
242 	 * Everything we do until we go interactive is done in ex mode.
243 	 */
244 	if (screen_init(gp, NULL, &sp)) {
245 		if (sp != NULL) {
246 			TAILQ_INSERT_HEAD(&wp->scrq, sp, q);
247 			sp->wp = wp;
248 		}
249 		goto err;
250 	}
251 	F_SET(sp, SC_EX);
252 	TAILQ_INSERT_HEAD(&wp->scrq, sp, q);
253 	sp->wp = wp;
254 
255 	if (v_key_init(sp))		/* Special key initialization. */
256 		goto err;
257 
258 	{ int oargs[5], *oargp = oargs;
259 	if (lflag) {			/* Command-line options. */
260 		*oargp++ = O_LISP;
261 		*oargp++ = O_SHOWMATCH;
262 	}
263 	if (readonly)
264 		*oargp++ = O_READONLY;
265 #ifdef GTAGS
266 	if (gtags)
267 		*oargp++ = O_GTAGSMODE;
268 #endif
269 	if (secure)
270 		*oargp++ = O_SECURE;
271 	*oargp = -1;			/* Options initialization. */
272 	if (opts_init(sp, oargs))
273 		goto err;
274 	}
275 	if (wsizearg != NULL) {
276 		ARGS *av[2], a, b;
277 		(void)snprintf(path, sizeof(path), "window=%s", wsizearg);
278 		a.bp = (CHAR_T *)path;
279 		a.len = strlen(path);
280 		b.bp = NULL;
281 		b.len = 0;
282 		av[0] = &a;
283 		av[1] = &b;
284 		(void)opts_set(sp, av, NULL);
285 	}
286 	if (silent) {			/* Ex batch mode option values. */
287 		O_CLR(sp, O_AUTOPRINT);
288 		O_CLR(sp, O_PROMPT);
289 		O_CLR(sp, O_VERBOSE);
290 		O_CLR(sp, O_WARN);
291 		F_SET(sp, SC_EX_SILENT);
292 	}
293 
294 	sp->rows = O_VAL(sp, O_LINES);	/* Make ex formatting work. */
295 	sp->cols = O_VAL(sp, O_COLUMNS);
296 
297 	if (!silent && startup) {	/* Read EXINIT, exrc files. */
298 		if (ex_exrc(sp))
299 			goto err;
300 		if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) {
301 			if (screen_end(sp))
302 				goto err;
303 			goto done;
304 		}
305 	}
306 
307 	/*
308 	 * List recovery files if -r specified without file arguments.
309 	 * Note, options must be initialized and startup information
310 	 * read before doing this.
311 	 */
312 	if (flagchk == 'r' && argv[0] == NULL) {
313 		if (rcv_list(sp))
314 			goto err;
315 		if (screen_end(sp))
316 			goto err;
317 		goto done;
318 	}
319 
320 	/*
321 	 * !!!
322 	 * Initialize the default ^D, ^U scrolling value here, after the
323 	 * user has had every opportunity to set the window option.
324 	 *
325 	 * It's historic practice that changing the value of the window
326 	 * option did not alter the default scrolling value, only giving
327 	 * a count to ^D/^U did that.
328 	 */
329 	sp->defscroll = (O_VAL(sp, O_WINDOW) + 1) / 2;
330 
331 	/*
332 	 * If we don't have a command-line option, switch into the right
333 	 * editor now, so that we position default files correctly, and
334 	 * so that any tags file file-already-locked messages are in the
335 	 * vi screen, not the ex screen.
336 	 *
337 	 * XXX
338 	 * If we have a command-line option, the error message can end
339 	 * up in the wrong place, but I think that the combination is
340 	 * unlikely.
341 	 */
342 	if (gp->c_option == NULL) {
343 		F_CLR(sp, SC_EX | SC_VI);
344 		F_SET(sp, LF_ISSET(SC_EX | SC_VI));
345 	}
346 
347 	/* Open a tag file if specified. */
348 	if (tag_f != NULL) {
349 		CHAR2INT(sp, tag_f, strlen(tag_f) + 1, w, wlen);
350 		if (ex_tag_first(sp, w))
351 			goto err;
352 	}
353 
354 	/*
355 	 * Append any remaining arguments as file names.  Files are recovery
356 	 * files if -r specified.  If the tag option or ex startup commands
357 	 * loaded a file, then any file arguments are going to come after it.
358 	 */
359 	if (*argv != NULL) {
360 		if (sp->frp != NULL) {
361 			/* Cheat -- we know we have an extra argv slot. */
362 			MALLOC_NOMSG(sp,
363 			    *--argv, char *, strlen(sp->frp->name) + 1);
364 			if (*argv == NULL) {
365 				v_estr(gp->progname, errno, NULL);
366 				goto err;
367 			}
368 			(void)strcpy(*argv, sp->frp->name);
369 		}
370 		sp->argv = sp->cargv = argv;
371 		F_SET(sp, SC_ARGNOFREE);
372 		if (flagchk == 'r')
373 			F_SET(sp, SC_ARGRECOVER);
374 	}
375 
376 	/*
377 	 * If the ex startup commands and or/the tag option haven't already
378 	 * created a file, create one.  If no command-line files were given,
379 	 * use a temporary file.
380 	 */
381 	if (sp->frp == NULL) {
382 		if (sp->argv == NULL) {
383 			if ((frp = file_add(sp, NULL)) == NULL)
384 				goto err;
385 		} else  {
386 			if ((frp = file_add(sp, sp->argv[0])) == NULL)
387 				goto err;
388 			if (F_ISSET(sp, SC_ARGRECOVER))
389 				F_SET(frp, FR_RECOVER);
390 		}
391 
392 		if (file_init(sp, frp, NULL, 0))
393 			goto err;
394 		if (EXCMD_RUNNING(wp)) {
395 			(void)ex_cmd(sp);
396 			if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) {
397 				if (screen_end(sp))
398 					goto err;
399 				goto done;
400 			}
401 		}
402 	}
403 
404 	/*
405 	 * Check to see if we need to wait for ex.  If SC_SCR_EX is set, ex
406 	 * was forced to initialize the screen during startup.  We'd like to
407 	 * wait for a single character from the user, but we can't because
408 	 * we're not in raw mode.  We can't switch to raw mode because the
409 	 * vi initialization will switch to xterm's alternate screen, causing
410 	 * us to lose the messages we're pausing to make sure the user read.
411 	 * So, wait for a complete line.
412 	 */
413 	if (F_ISSET(sp, SC_SCR_EX)) {
414 		p = msg_cmsg(sp, CMSG_CONT_R, &len);
415 		(void)write(STDOUT_FILENO, p, len);
416 		for (;;) {
417 			if (v_event_get(sp, &ev, 0, 0))
418 				goto err;
419 			if (ev.e_event == E_INTERRUPT ||
420 			    (ev.e_event == E_CHARACTER &&
421 			     (ev.e_value == K_CR || ev.e_value == K_NL)))
422 				break;
423 			(void)gp->scr_bell(sp);
424 		}
425 	}
426 
427 	/* Switch into the right editor, regardless. */
428 	F_CLR(sp, SC_EX | SC_VI);
429 	F_SET(sp, LF_ISSET(SC_EX | SC_VI) | SC_STATUS_CNT);
430 
431 	/*
432 	 * Main edit loop.  Vi handles split screens itself, we only return
433 	 * here when switching editor modes or restarting the screen.
434 	 */
435 	while (sp != NULL)
436 		if (F_ISSET(sp, SC_EX) ? ex(&sp) : vi(&sp))
437 			goto err;
438 
439 done:	rval = 0;
440 	if (0)
441 err:		rval = 1;
442 
443 	return (rval);
444 }
445 
446 /*
447  * v_obsolete --
448  *	Convert historic arguments into something getopt(3) will like.
449  */
450 static int
v_obsolete(char * name,char ** argv)451 v_obsolete(char *name, char **argv)
452 {
453 	size_t len;
454 	char *p;
455 
456 	/*
457 	 * Translate old style arguments into something getopt will like.
458 	 * Make sure it's not text space memory, because ex modifies the
459 	 * strings.
460 	 *	Change "+" into "-c$".
461 	 *	Change "+<anything else>" into "-c<anything else>".
462 	 *	Change "-" into "-s"
463 	 *	The c, T, t and w options take arguments so they can't be
464 	 *	    special arguments.
465 	 *
466 	 * Stop if we find "--" as an argument, the user may want to edit
467 	 * a file named "+foo".
468 	 */
469 	while (*++argv && strcmp(argv[0], "--"))
470 		if (argv[0][0] == '+') {
471 			if (argv[0][1] == '\0') {
472 				MALLOC_NOMSG(NULL, argv[0], char *, 4);
473 				if (argv[0] == NULL)
474 					goto nomem;
475 				(void)strcpy(argv[0], "-c$");
476 			} else  {
477 				p = argv[0];
478 				len = strlen(argv[0]);
479 				MALLOC_NOMSG(NULL, argv[0], char *, len + 2);
480 				if (argv[0] == NULL)
481 					goto nomem;
482 				argv[0][0] = '-';
483 				argv[0][1] = 'c';
484 				(void)strcpy(argv[0] + 2, p + 1);
485 			}
486 		} else if (argv[0][0] == '-') {
487 			if (argv[0][1] == '\0') {
488 				MALLOC_NOMSG(NULL, argv[0], char *, 3);
489 				if (argv[0] == NULL) {
490 nomem:					v_estr(name, errno, NULL);
491 					return (1);
492 				}
493 				(void)strcpy(argv[0], "-s");
494 			} else
495 				if ((argv[0][1] == 'c' || argv[0][1] == 'T' ||
496 				    argv[0][1] == 't' || argv[0][1] == 'w') &&
497 				    argv[0][2] == '\0')
498 					++argv;
499 		}
500 	return (0);
501 }
502 
503 #ifdef DEBUG
504 static void
attach(GS * gp)505 attach(GS *gp)
506 {
507 	int fd;
508 	char ch;
509 
510 	if ((fd = open(_PATH_TTY, O_RDONLY, 0)) < 0) {
511 		v_estr(gp->progname, errno, _PATH_TTY);
512 		return;
513 	}
514 
515 	(void)printf("process %lu waiting, enter <CR> to continue: ",
516 	    (u_long)getpid());
517 	(void)fflush(stdout);
518 
519 	do {
520 		if (read(fd, &ch, 1) != 1) {
521 			(void)close(fd);
522 			return;
523 		}
524 	} while (ch != '\n' && ch != '\r');
525 	(void)close(fd);
526 }
527 #endif
528 
529 static void
v_estr(const char * name,int eno,const char * msg)530 v_estr(const char *name, int eno, const char *msg)
531 {
532 	(void)fprintf(stderr, "%s", name);
533 	if (msg != NULL)
534 		(void)fprintf(stderr, ": %s", msg);
535 	if (eno)
536 		(void)fprintf(stderr, ": %s", strerror(errno));
537 	(void)fprintf(stderr, "\n");
538 }
539