GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/vi/build/../common/main.c Lines: 0 198 0.0 %
Date: 2017-11-07 Branches: 0 197 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: main.c,v 1.40 2017/07/03 07:01:14 bentley Exp $	*/
2
3
/*-
4
 * Copyright (c) 1992, 1993, 1994
5
 *	The Regents of the University of California.  All rights reserved.
6
 * Copyright (c) 1992, 1993, 1994, 1995, 1996
7
 *	Keith Bostic.  All rights reserved.
8
 *
9
 * See the LICENSE file for redistribution information.
10
 */
11
12
#include "config.h"
13
14
#include <sys/types.h>
15
#include <sys/queue.h>
16
#include <sys/stat.h>
17
#include <sys/time.h>
18
19
#include <bitstring.h>
20
#include <err.h>
21
#include <errno.h>
22
#include <fcntl.h>
23
#include <limits.h>
24
#include <paths.h>
25
#include <stdio.h>
26
#include <stdlib.h>
27
#include <string.h>
28
#include <unistd.h>
29
30
#include "common.h"
31
#include "../vi/vi.h"
32
33
#ifdef DEBUG
34
static void	 attach(GS *);
35
#endif
36
static int	 v_obsolete(char *[]);
37
38
/*
39
 * editor --
40
 *	Main editor routine.
41
 *
42
 * PUBLIC: int editor(GS *, int, char *[]);
43
 */
44
int
45
editor(GS *gp, int argc, char *argv[])
46
{
47
	extern int optind;
48
	extern char *optarg;
49
	const char *p;
50
	EVENT ev;
51
	FREF *frp;
52
	SCR *sp;
53
	size_t len;
54
	u_int flags;
55
	int ch, flagchk, secure, startup, readonly, rval, silent;
56
	char *tag_f, *wsizearg, path[256];
57
58
	static const char *optstr[3] = {
59
#ifdef DEBUG
60
		"c:D:FlRrSsT:t:vw:",
61
		"c:D:eFlRrST:t:w:",
62
		"c:D:eFlrST:t:w:"
63
#else
64
		"c:FlRrSst:vw:",
65
		"c:eFlRrSt:w:",
66
		"c:eFlrSt:w:"
67
#endif
68
	};
69
70
	if (pledge("stdio rpath wpath cpath fattr flock getpw tty proc exec",
71
	    NULL) == -1) {
72
		perror("pledge");
73
		goto err;
74
	}
75
76
	/* Initialize the busy routine, if not defined by the screen. */
77
	if (gp->scr_busy == NULL)
78
		gp->scr_busy = vs_busy;
79
	/* Initialize the message routine, if not defined by the screen. */
80
	if (gp->scr_msg == NULL)
81
		gp->scr_msg = vs_msg;
82
83
	/* Common global structure initialization. */
84
	TAILQ_INIT(&gp->dq);
85
	TAILQ_INIT(&gp->hq);
86
	LIST_INIT(&gp->ecq);
87
	LIST_INSERT_HEAD(&gp->ecq, &gp->excmd, q);
88
	gp->noprint = DEFAULT_NOPRINT;
89
90
	/* Structures shared by screens so stored in the GS structure. */
91
	TAILQ_INIT(&gp->frefq);
92
	TAILQ_INIT(&gp->dcb_store.textq);
93
	LIST_INIT(&gp->cutq);
94
	LIST_INIT(&gp->seqq);
95
96
	/* Set initial screen type and mode based on the program name. */
97
	readonly = 0;
98
	if (!strcmp(getprogname(), "ex") || !strcmp(getprogname(), "nex"))
99
		LF_INIT(SC_EX);
100
	else {
101
		/* Nview, view are readonly. */
102
		if (!strcmp(getprogname(), "nview") ||
103
		    !strcmp(getprogname(), "view"))
104
			readonly = 1;
105
106
		/* Vi is the default. */
107
		LF_INIT(SC_VI);
108
	}
109
110
	/* Convert old-style arguments into new-style ones. */
111
	if (v_obsolete(argv))
112
		return (1);
113
114
	/* Parse the arguments. */
115
	flagchk = '\0';
116
	tag_f = wsizearg = NULL;
117
	secure = silent = 0;
118
	startup = 1;
119
120
	/* Set the file snapshot flag. */
121
	F_SET(gp, G_SNAPSHOT);
122
123
	pmode = MODE_EX;
124
	if (!strcmp(getprogname(), "ex"))
125
		pmode = MODE_EX;
126
	else if (!strcmp(getprogname(), "vi"))
127
		pmode = MODE_VI;
128
	else if (!strcmp(getprogname(), "view"))
129
		pmode = MODE_VIEW;
130
131
	while ((ch = getopt(argc, argv, optstr[pmode])) != -1)
132
		switch (ch) {
133
		case 'c':		/* Run the command. */
134
			/*
135
			 * XXX
136
			 * We should support multiple -c options.
137
			 */
138
			if (gp->c_option != NULL) {
139
				warnx("only one -c command may be specified.");
140
				return (1);
141
			}
142
			gp->c_option = optarg;
143
			break;
144
#ifdef DEBUG
145
		case 'D':
146
			switch (optarg[0]) {
147
			case 's':
148
				startup = 0;
149
				break;
150
			case 'w':
151
				attach(gp);
152
				break;
153
			default:
154
				warnx("-D requires s or w argument.");
155
				return (1);
156
			}
157
			break;
158
#endif
159
		case 'e':		/* Ex mode. */
160
			LF_CLR(SC_VI);
161
			LF_SET(SC_EX);
162
			break;
163
		case 'F':		/* No snapshot. */
164
			F_CLR(gp, G_SNAPSHOT);
165
			break;
166
		case 'R':		/* Readonly. */
167
			readonly = 1;
168
			break;
169
		case 'r':		/* Recover. */
170
			if (flagchk == 't') {
171
				warnx(
172
				    "only one of -r and -t may be specified.");
173
				return (1);
174
			}
175
			flagchk = 'r';
176
			break;
177
		case 'S':
178
			secure = 1;
179
			break;
180
		case 's':
181
			silent = 1;
182
			break;
183
#ifdef DEBUG
184
		case 'T':		/* Trace. */
185
			if ((gp->tracefp = fopen(optarg, "w")) == NULL) {
186
				warn("%s", optarg);
187
				goto err;
188
			}
189
			(void)fprintf(gp->tracefp,
190
			    "\n===\ntrace: open %s\n", optarg);
191
			break;
192
#endif
193
		case 't':		/* Tag. */
194
			if (flagchk == 'r') {
195
				warnx(
196
				    "only one of -r and -t may be specified.");
197
				return (1);
198
			}
199
			if (flagchk == 't') {
200
				warnx("only one tag file may be specified.");
201
				return (1);
202
			}
203
			flagchk = 't';
204
			tag_f = optarg;
205
			break;
206
		case 'v':		/* Vi mode. */
207
			LF_CLR(SC_EX);
208
			LF_SET(SC_VI);
209
			break;
210
		case 'w':
211
			wsizearg = optarg;
212
			break;
213
		case '?':
214
		default:
215
			(void)gp->scr_usage();
216
			return (1);
217
		}
218
	argc -= optind;
219
	argv += optind;
220
221
	if (secure)
222
		if (pledge("stdio rpath wpath cpath fattr flock getpw tty", NULL) == -1) {
223
			perror("pledge");
224
			goto err;
225
		}
226
227
	/*
228
	 * -s option is only meaningful to ex.
229
	 *
230
	 * If not reading from a terminal, it's like -s was specified.
231
	 */
232
	if (silent && !LF_ISSET(SC_EX)) {
233
		warnx("-s option is only applicable to ex.");
234
		goto err;
235
	}
236
	if (LF_ISSET(SC_EX) && F_ISSET(gp, G_SCRIPTED))
237
		silent = 1;
238
239
	/*
240
	 * Build and initialize the first/current screen.  This is a bit
241
	 * tricky.  If an error is returned, we may or may not have a
242
	 * screen structure.  If we have a screen structure, put it on a
243
	 * display queue so that the error messages get displayed.
244
	 *
245
	 * !!!
246
	 * Everything we do until we go interactive is done in ex mode.
247
	 */
248
	if (screen_init(gp, NULL, &sp)) {
249
		if (sp != NULL)
250
			TAILQ_INSERT_HEAD(&gp->dq, sp, q);
251
		goto err;
252
	}
253
	F_SET(sp, SC_EX);
254
	TAILQ_INSERT_HEAD(&gp->dq, sp, q);
255
256
	if (v_key_init(sp))		/* Special key initialization. */
257
		goto err;
258
259
	{ int oargs[5], *oargp = oargs;
260
	if (readonly)			/* Command-line options. */
261
		*oargp++ = O_READONLY;
262
	if (secure)
263
		*oargp++ = O_SECURE;
264
	*oargp = -1;			/* Options initialization. */
265
	if (opts_init(sp, oargs))
266
		goto err;
267
	}
268
	if (wsizearg != NULL) {
269
		ARGS *av[2], a, b;
270
		(void)snprintf(path, sizeof(path), "window=%s", wsizearg);
271
		a.bp = (CHAR_T *)path;
272
		a.len = strlen(path);
273
		b.bp = NULL;
274
		b.len = 0;
275
		av[0] = &a;
276
		av[1] = &b;
277
		(void)opts_set(sp, av, NULL);
278
	}
279
	if (silent) {			/* Ex batch mode option values. */
280
		O_CLR(sp, O_AUTOPRINT);
281
		O_CLR(sp, O_PROMPT);
282
		O_CLR(sp, O_VERBOSE);
283
		O_CLR(sp, O_WARN);
284
		F_SET(sp, SC_EX_SILENT);
285
	}
286
287
	sp->rows = O_VAL(sp, O_LINES);	/* Make ex formatting work. */
288
	sp->cols = O_VAL(sp, O_COLUMNS);
289
290
	if (!silent && startup) {	/* Read EXINIT, exrc files. */
291
		if (ex_exrc(sp))
292
			goto err;
293
		if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) {
294
			if (screen_end(sp))
295
				goto err;
296
			goto done;
297
		}
298
	}
299
300
	/*
301
	 * List recovery files if -r specified without file arguments.
302
	 * Note, options must be initialized and startup information
303
	 * read before doing this.
304
	 */
305
	if (flagchk == 'r' && argv[0] == NULL) {
306
		if (rcv_list(sp))
307
			goto err;
308
		if (screen_end(sp))
309
			goto err;
310
		goto done;
311
	}
312
313
	/*
314
	 * !!!
315
	 * Initialize the default ^D, ^U scrolling value here, after the
316
	 * user has had every opportunity to set the window option.
317
	 *
318
	 * It's historic practice that changing the value of the window
319
	 * option did not alter the default scrolling value, only giving
320
	 * a count to ^D/^U did that.
321
	 */
322
	sp->defscroll = (O_VAL(sp, O_WINDOW) + 1) / 2;
323
324
	/*
325
	 * If we don't have a command-line option, switch into the right
326
	 * editor now, so that we position default files correctly, and
327
	 * so that any tags file file-already-locked messages are in the
328
	 * vi screen, not the ex screen.
329
	 *
330
	 * XXX
331
	 * If we have a command-line option, the error message can end
332
	 * up in the wrong place, but I think that the combination is
333
	 * unlikely.
334
	 */
335
	if (gp->c_option == NULL) {
336
		F_CLR(sp, SC_EX | SC_VI);
337
		F_SET(sp, LF_ISSET(SC_EX | SC_VI));
338
	}
339
340
	/* Open a tag file if specified. */
341
	if (tag_f != NULL && ex_tag_first(sp, tag_f))
342
		goto err;
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
			size_t l;
352
			/* Cheat -- we know we have an extra argv slot. */
353
			l = strlen(sp->frp->name) + 1;
354
			if ((*--argv = malloc(l)) == NULL) {
355
				warn(NULL);
356
				goto err;
357
			}
358
			(void)strlcpy(*argv, sp->frp->name, l);
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, (CHAR_T *)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(gp)) {
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
	/* Clean out the global structure. */
434
	v_end(gp);
435
436
	return (rval);
437
}
438
439
/*
440
 * v_end --
441
 *	End the program, discarding screens and most of the global area.
442
 *
443
 * PUBLIC: void v_end(GS *);
444
 */
445
void
446
v_end(GS *gp)
447
{
448
	MSGS *mp;
449
	SCR *sp;
450
451
	/* If there are any remaining screens, kill them off. */
452
	if (gp->ccl_sp != NULL) {
453
		(void)file_end(gp->ccl_sp, NULL, 1);
454
		(void)screen_end(gp->ccl_sp);
455
	}
456
	while ((sp = TAILQ_FIRST(&gp->dq)))
457
		(void)screen_end(sp);	/* Removes sp from the queue. */
458
	while ((sp = TAILQ_FIRST(&gp->hq)))
459
		(void)screen_end(sp);	/* Removes sp from the queue. */
460
461
#if defined(DEBUG) || defined(PURIFY)
462
	{ FREF *frp;
463
		/* Free FREF's. */
464
		while ((frp = TAILQ_FIRST(&gp->frefq))) {
465
			TAILQ_REMOVE(&gp->frefq, frp, q);
466
			free(frp->name);
467
			free(frp->tname);
468
			free(frp);
469
		}
470
	}
471
472
	/* Free key input queue. */
473
	free(gp->i_event);
474
475
	/* Free cut buffers. */
476
	cut_close(gp);
477
478
	/* Free map sequences. */
479
	seq_close(gp);
480
481
	/* Free default buffer storage. */
482
	(void)text_lfree(&gp->dcb_store.textq);
483
#endif
484
485
	/* Ring the bell if scheduled. */
486
	if (F_ISSET(gp, G_BELLSCHED))
487
		(void)fprintf(stderr, "\07");		/* \a */
488
489
	/*
490
	 * Flush any remaining messages.  If a message is here, it's almost
491
	 * certainly the message about the event that killed us (although
492
	 * it's possible that the user is sourcing a file that exits from the
493
	 * editor).
494
	 */
495
	while ((mp = LIST_FIRST(&gp->msgq)) != NULL) {
496
		(void)fprintf(stderr, "%s%.*s",
497
		    mp->mtype == M_ERR ? "ex/vi: " : "", (int)mp->len, mp->buf);
498
		LIST_REMOVE(mp, q);
499
#if defined(DEBUG) || defined(PURIFY)
500
		free(mp->buf);
501
		free(mp);
502
#endif
503
	}
504
505
#if defined(DEBUG) || defined(PURIFY)
506
	/* Free any temporary space. */
507
	free(gp->tmp_bp);
508
509
#if defined(DEBUG)
510
	/* Close debugging file descriptor. */
511
	if (gp->tracefp != NULL)
512
		(void)fclose(gp->tracefp);
513
#endif
514
#endif
515
}
516
517
/*
518
 * v_obsolete --
519
 *	Convert historic arguments into something getopt(3) will like.
520
 */
521
static int
522
v_obsolete(char *argv[])
523
{
524
	size_t len;
525
	char *p;
526
527
	/*
528
	 * Translate old style arguments into something getopt will like.
529
	 * Make sure it's not text space memory, because ex modifies the
530
	 * strings.
531
	 *	Change "+" into "-c$".
532
	 *	Change "+<anything else>" into "-c<anything else>".
533
	 *	Change "-" into "-s"
534
	 *	The c, T, t and w options take arguments so they can't be
535
	 *	    special arguments.
536
	 *
537
	 * Stop if we find "--" as an argument, the user may want to edit
538
	 * a file named "+foo".
539
	 */
540
	while (*++argv && strcmp(argv[0], "--"))
541
		if (argv[0][0] == '+') {
542
			if (argv[0][1] == '\0') {
543
				argv[0] = strdup("-c$");
544
				if (argv[0] == NULL)
545
					goto nomem;
546
			} else  {
547
				p = argv[0];
548
				len = strlen(argv[0]);
549
				if ((argv[0] = malloc(len + 2)) == NULL)
550
					goto nomem;
551
				argv[0][0] = '-';
552
				argv[0][1] = 'c';
553
				(void)strlcpy(argv[0] + 2, p + 1, len);
554
			}
555
		} else if (argv[0][0] == '-') {
556
			if (argv[0][1] == '\0') {
557
				argv[0] = strdup("-s");
558
				if (argv[0] == NULL) {
559
nomem:					warn(NULL);
560
					return (1);
561
				}
562
			} else
563
				if ((argv[0][1] == 'c' || argv[0][1] == 'T' ||
564
				    argv[0][1] == 't' || argv[0][1] == 'w') &&
565
				    argv[0][2] == '\0')
566
					++argv;
567
		}
568
	return (0);
569
}
570
571
#ifdef DEBUG
572
static void
573
attach(GS *gp)
574
{
575
	int fd;
576
	char ch;
577
578
	if ((fd = open(_PATH_TTY, O_RDONLY, 0)) < 0) {
579
		warn("%s", _PATH_TTY);
580
		return;
581
	}
582
583
	(void)printf("process %ld waiting, enter <CR> to continue: ",
584
	    (long)getpid());
585
	(void)fflush(stdout);
586
587
	do {
588
		if (read(fd, &ch, 1) != 1) {
589
			(void)close(fd);
590
			return;
591
		}
592
	} while (ch != '\n' && ch != '\r');
593
	(void)close(fd);
594
}
595
#endif