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

Line Branch Exec Source
1
/*	$OpenBSD: main.c,v 1.41 2017/11/10 18:31:36 martijn 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 flock rpath cpath wpath",
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
			fflush(gp->tracefp);
192
			break;
193
#endif
194
		case 't':		/* Tag. */
195
			if (flagchk == 'r') {
196
				warnx(
197
				    "only one of -r and -t may be specified.");
198
				return (1);
199
			}
200
			if (flagchk == 't') {
201
				warnx("only one tag file may be specified.");
202
				return (1);
203
			}
204
			flagchk = 't';
205
			tag_f = optarg;
206
			break;
207
		case 'v':		/* Vi mode. */
208
			LF_CLR(SC_EX);
209
			LF_SET(SC_VI);
210
			break;
211
		case 'w':
212
			wsizearg = optarg;
213
			break;
214
		case '?':
215
		default:
216
			(void)gp->scr_usage();
217
			return (1);
218
		}
219
	argc -= optind;
220
	argv += optind;
221
222
	if (secure)
223
		if (pledge("stdio rpath wpath cpath fattr flock getpw tty", NULL) == -1) {
224
			perror("pledge");
225
			goto err;
226
		}
227
228
	/*
229
	 * -s option is only meaningful to ex.
230
	 *
231
	 * If not reading from a terminal, it's like -s was specified.
232
	 */
233
	if (silent && !LF_ISSET(SC_EX)) {
234
		warnx("-s option is only applicable to ex.");
235
		goto err;
236
	}
237
	if (LF_ISSET(SC_EX) && F_ISSET(gp, G_SCRIPTED))
238
		silent = 1;
239
240
	/*
241
	 * Build and initialize the first/current screen.  This is a bit
242
	 * tricky.  If an error is returned, we may or may not have a
243
	 * screen structure.  If we have a screen structure, put it on a
244
	 * display queue so that the error messages get displayed.
245
	 *
246
	 * !!!
247
	 * Everything we do until we go interactive is done in ex mode.
248
	 */
249
	if (screen_init(gp, NULL, &sp)) {
250
		if (sp != NULL)
251
			TAILQ_INSERT_HEAD(&gp->dq, sp, q);
252
		goto err;
253
	}
254
	F_SET(sp, SC_EX);
255
	TAILQ_INSERT_HEAD(&gp->dq, sp, q);
256
257
	if (v_key_init(sp))		/* Special key initialization. */
258
		goto err;
259
260
	{ int oargs[5], *oargp = oargs;
261
	if (readonly)			/* Command-line options. */
262
		*oargp++ = O_READONLY;
263
	if (secure)
264
		*oargp++ = O_SECURE;
265
	*oargp = -1;			/* Options initialization. */
266
	if (opts_init(sp, oargs))
267
		goto err;
268
	}
269
	if (wsizearg != NULL) {
270
		ARGS *av[2], a, b;
271
		(void)snprintf(path, sizeof(path), "window=%s", wsizearg);
272
		a.bp = (CHAR_T *)path;
273
		a.len = strlen(path);
274
		b.bp = NULL;
275
		b.len = 0;
276
		av[0] = &a;
277
		av[1] = &b;
278
		(void)opts_set(sp, av, NULL);
279
	}
280
	if (silent) {			/* Ex batch mode option values. */
281
		O_CLR(sp, O_AUTOPRINT);
282
		O_CLR(sp, O_PROMPT);
283
		O_CLR(sp, O_VERBOSE);
284
		O_CLR(sp, O_WARN);
285
		F_SET(sp, SC_EX_SILENT);
286
	}
287
288
	sp->rows = O_VAL(sp, O_LINES);	/* Make ex formatting work. */
289
	sp->cols = O_VAL(sp, O_COLUMNS);
290
291
	if (!silent && startup) {	/* Read EXINIT, exrc files. */
292
		if (ex_exrc(sp))
293
			goto err;
294
		if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) {
295
			if (screen_end(sp))
296
				goto err;
297
			goto done;
298
		}
299
	}
300
301
	/*
302
	 * List recovery files if -r specified without file arguments.
303
	 * Note, options must be initialized and startup information
304
	 * read before doing this.
305
	 */
306
	if (flagchk == 'r' && argv[0] == NULL) {
307
		if (rcv_list(sp))
308
			goto err;
309
		if (screen_end(sp))
310
			goto err;
311
		goto done;
312
	}
313
314
	/*
315
	 * !!!
316
	 * Initialize the default ^D, ^U scrolling value here, after the
317
	 * user has had every opportunity to set the window option.
318
	 *
319
	 * It's historic practice that changing the value of the window
320
	 * option did not alter the default scrolling value, only giving
321
	 * a count to ^D/^U did that.
322
	 */
323
	sp->defscroll = (O_VAL(sp, O_WINDOW) + 1) / 2;
324
325
	/*
326
	 * If we don't have a command-line option, switch into the right
327
	 * editor now, so that we position default files correctly, and
328
	 * so that any tags file file-already-locked messages are in the
329
	 * vi screen, not the ex screen.
330
	 *
331
	 * XXX
332
	 * If we have a command-line option, the error message can end
333
	 * up in the wrong place, but I think that the combination is
334
	 * unlikely.
335
	 */
336
	if (gp->c_option == NULL) {
337
		F_CLR(sp, SC_EX | SC_VI);
338
		F_SET(sp, LF_ISSET(SC_EX | SC_VI));
339
	}
340
341
	/* Open a tag file if specified. */
342
	if (tag_f != NULL && ex_tag_first(sp, tag_f))
343
		goto err;
344
345
	/*
346
	 * Append any remaining arguments as file names.  Files are recovery
347
	 * files if -r specified.  If the tag option or ex startup commands
348
	 * loaded a file, then any file arguments are going to come after it.
349
	 */
350
	if (*argv != NULL) {
351
		if (sp->frp != NULL) {
352
			size_t l;
353
			/* Cheat -- we know we have an extra argv slot. */
354
			l = strlen(sp->frp->name) + 1;
355
			if ((*--argv = malloc(l)) == NULL) {
356
				warn(NULL);
357
				goto err;
358
			}
359
			(void)strlcpy(*argv, sp->frp->name, l);
360
		}
361
		sp->argv = sp->cargv = argv;
362
		F_SET(sp, SC_ARGNOFREE);
363
		if (flagchk == 'r')
364
			F_SET(sp, SC_ARGRECOVER);
365
	}
366
367
	/*
368
	 * If the ex startup commands and or/the tag option haven't already
369
	 * created a file, create one.  If no command-line files were given,
370
	 * use a temporary file.
371
	 */
372
	if (sp->frp == NULL) {
373
		if (sp->argv == NULL) {
374
			if ((frp = file_add(sp, NULL)) == NULL)
375
				goto err;
376
		} else  {
377
			if ((frp = file_add(sp, (CHAR_T *)sp->argv[0])) == NULL)
378
				goto err;
379
			if (F_ISSET(sp, SC_ARGRECOVER))
380
				F_SET(frp, FR_RECOVER);
381
		}
382
383
		if (file_init(sp, frp, NULL, 0))
384
			goto err;
385
		if (EXCMD_RUNNING(gp)) {
386
			(void)ex_cmd(sp);
387
			if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) {
388
				if (screen_end(sp))
389
					goto err;
390
				goto done;
391
			}
392
		}
393
	}
394
395
	/*
396
	 * Check to see if we need to wait for ex.  If SC_SCR_EX is set, ex
397
	 * was forced to initialize the screen during startup.  We'd like to
398
	 * wait for a single character from the user, but we can't because
399
	 * we're not in raw mode.  We can't switch to raw mode because the
400
	 * vi initialization will switch to xterm's alternate screen, causing
401
	 * us to lose the messages we're pausing to make sure the user read.
402
	 * So, wait for a complete line.
403
	 */
404
	if (F_ISSET(sp, SC_SCR_EX)) {
405
		p = msg_cmsg(sp, CMSG_CONT_R, &len);
406
		(void)write(STDOUT_FILENO, p, len);
407
		for (;;) {
408
			if (v_event_get(sp, &ev, 0, 0))
409
				goto err;
410
			if (ev.e_event == E_INTERRUPT ||
411
			    (ev.e_event == E_CHARACTER &&
412
			    (ev.e_value == K_CR || ev.e_value == K_NL)))
413
				break;
414
			(void)gp->scr_bell(sp);
415
		}
416
	}
417
418
	/* Switch into the right editor, regardless. */
419
	F_CLR(sp, SC_EX | SC_VI);
420
	F_SET(sp, LF_ISSET(SC_EX | SC_VI) | SC_STATUS_CNT);
421
422
	/*
423
	 * Main edit loop.  Vi handles split screens itself, we only return
424
	 * here when switching editor modes or restarting the screen.
425
	 */
426
	while (sp != NULL)
427
		if (F_ISSET(sp, SC_EX) ? ex(&sp) : vi(&sp))
428
			goto err;
429
430
done:	rval = 0;
431
	if (0)
432
err:		rval = 1;
433
434
	/* Clean out the global structure. */
435
	v_end(gp);
436
437
	return (rval);
438
}
439
440
/*
441
 * v_end --
442
 *	End the program, discarding screens and most of the global area.
443
 *
444
 * PUBLIC: void v_end(GS *);
445
 */
446
void
447
v_end(GS *gp)
448
{
449
	MSGS *mp;
450
	SCR *sp;
451
452
	/* If there are any remaining screens, kill them off. */
453
	if (gp->ccl_sp != NULL) {
454
		(void)file_end(gp->ccl_sp, NULL, 1);
455
		(void)screen_end(gp->ccl_sp);
456
	}
457
	while ((sp = TAILQ_FIRST(&gp->dq)))
458
		(void)screen_end(sp);	/* Removes sp from the queue. */
459
	while ((sp = TAILQ_FIRST(&gp->hq)))
460
		(void)screen_end(sp);	/* Removes sp from the queue. */
461
462
#if defined(DEBUG) || defined(PURIFY)
463
	{ FREF *frp;
464
		/* Free FREF's. */
465
		while ((frp = TAILQ_FIRST(&gp->frefq))) {
466
			TAILQ_REMOVE(&gp->frefq, frp, q);
467
			free(frp->name);
468
			free(frp->tname);
469
			free(frp);
470
		}
471
	}
472
473
	/* Free key input queue. */
474
	free(gp->i_event);
475
476
	/* Free cut buffers. */
477
	cut_close(gp);
478
479
	/* Free map sequences. */
480
	seq_close(gp);
481
482
	/* Free default buffer storage. */
483
	(void)text_lfree(&gp->dcb_store.textq);
484
#endif
485
486
	/* Ring the bell if scheduled. */
487
	if (F_ISSET(gp, G_BELLSCHED))
488
		(void)fprintf(stderr, "\07");		/* \a */
489
490
	/*
491
	 * Flush any remaining messages.  If a message is here, it's almost
492
	 * certainly the message about the event that killed us (although
493
	 * it's possible that the user is sourcing a file that exits from the
494
	 * editor).
495
	 */
496
	while ((mp = LIST_FIRST(&gp->msgq)) != NULL) {
497
		(void)fprintf(stderr, "%s%.*s",
498
		    mp->mtype == M_ERR ? "ex/vi: " : "", (int)mp->len, mp->buf);
499
		LIST_REMOVE(mp, q);
500
#if defined(DEBUG) || defined(PURIFY)
501
		free(mp->buf);
502
		free(mp);
503
#endif
504
	}
505
506
#if defined(DEBUG) || defined(PURIFY)
507
	/* Free any temporary space. */
508
	free(gp->tmp_bp);
509
510
#if defined(DEBUG)
511
	/* Close debugging file descriptor. */
512
	if (gp->tracefp != NULL)
513
		(void)fclose(gp->tracefp);
514
#endif
515
#endif
516
}
517
518
/*
519
 * v_obsolete --
520
 *	Convert historic arguments into something getopt(3) will like.
521
 */
522
static int
523
v_obsolete(char *argv[])
524
{
525
	size_t len;
526
	char *p;
527
528
	/*
529
	 * Translate old style arguments into something getopt will like.
530
	 * Make sure it's not text space memory, because ex modifies the
531
	 * strings.
532
	 *	Change "+" into "-c$".
533
	 *	Change "+<anything else>" into "-c<anything else>".
534
	 *	Change "-" into "-s"
535
	 *	The c, T, t and w options take arguments so they can't be
536
	 *	    special arguments.
537
	 *
538
	 * Stop if we find "--" as an argument, the user may want to edit
539
	 * a file named "+foo".
540
	 */
541
	while (*++argv && strcmp(argv[0], "--"))
542
		if (argv[0][0] == '+') {
543
			if (argv[0][1] == '\0') {
544
				argv[0] = strdup("-c$");
545
				if (argv[0] == NULL)
546
					goto nomem;
547
			} else  {
548
				p = argv[0];
549
				len = strlen(argv[0]);
550
				if ((argv[0] = malloc(len + 2)) == NULL)
551
					goto nomem;
552
				argv[0][0] = '-';
553
				argv[0][1] = 'c';
554
				(void)strlcpy(argv[0] + 2, p + 1, len);
555
			}
556
		} else if (argv[0][0] == '-') {
557
			if (argv[0][1] == '\0') {
558
				argv[0] = strdup("-s");
559
				if (argv[0] == NULL) {
560
nomem:					warn(NULL);
561
					return (1);
562
				}
563
			} else
564
				if ((argv[0][1] == 'c' || argv[0][1] == 'T' ||
565
				    argv[0][1] == 't' || argv[0][1] == 'w') &&
566
				    argv[0][2] == '\0')
567
					++argv;
568
		}
569
	return (0);
570
}
571
572
#ifdef DEBUG
573
static void
574
attach(GS *gp)
575
{
576
	int fd;
577
	char ch;
578
579
	if ((fd = open(_PATH_TTY, O_RDONLY, 0)) < 0) {
580
		warn("%s", _PATH_TTY);
581
		return;
582
	}
583
584
	(void)printf("process %ld waiting, enter <CR> to continue: ",
585
	    (long)getpid());
586
	(void)fflush(stdout);
587
588
	do {
589
		if (read(fd, &ch, 1) != 1) {
590
			(void)close(fd);
591
			return;
592
		}
593
	} while (ch != '\n' && ch != '\r');
594
	(void)close(fd);
595
}
596
#endif