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