sbin/hammer: Add a TODO comment for hammer recover
[dragonfly.git] / contrib / nvi2 / common / main.c
blobd9ae96f9e7dd97aa5f3d4e617307d34374f0c7bd
1 /*-
2 * Copyright (c) 1992, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 * Copyright (c) 1992, 1993, 1994, 1995, 1996
5 * Keith Bostic. All rights reserved.
7 * See the LICENSE file for redistribution information.
8 */
10 #include "config.h"
12 #ifndef lint
13 static const char sccsid[] = "$Id: main.c,v 11.0 2012/10/17 06:34:37 zy Exp $";
14 #endif /* not lint */
16 #include <sys/types.h>
17 #include <sys/queue.h>
18 #include <sys/stat.h>
20 #include <bitstring.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <limits.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
29 #include "common.h"
30 #include "../vi/vi.h"
31 #include "pathnames.h"
33 static void attach(GS *);
34 static void v_estr(char *, int, char *);
35 static int v_obsolete(char *, char *[]);
38 * editor --
39 * Main editor routine.
41 * PUBLIC: int editor(GS *, int, char *[]);
43 int
44 editor(
45 GS *gp,
46 int argc,
47 char *argv[])
49 extern int optind;
50 extern char *optarg;
51 const char *p;
52 EVENT ev;
53 FREF *frp;
54 SCR *sp;
55 size_t len;
56 u_int flags;
57 int ch, flagchk, lflag, secure, startup, readonly, rval, silent;
58 char *tag_f, *wsizearg, path[256];
59 CHAR_T *w;
60 size_t wlen;
62 /* Initialize the busy routine, if not defined by the screen. */
63 if (gp->scr_busy == NULL)
64 gp->scr_busy = vs_busy;
65 /* Initialize the message routine, if not defined by the screen. */
66 if (gp->scr_msg == NULL)
67 gp->scr_msg = vs_msg;
68 gp->catd = (nl_catd)-1;
70 /* Common global structure initialization. */
71 TAILQ_INIT(gp->dq);
72 TAILQ_INIT(gp->hq);
73 SLIST_INIT(gp->ecq);
74 SLIST_INSERT_HEAD(gp->ecq, &gp->excmd, q);
75 gp->noprint = DEFAULT_NOPRINT;
77 /* Structures shared by screens so stored in the GS structure. */
78 TAILQ_INIT(gp->frefq);
79 TAILQ_INIT(gp->dcb_store.textq);
80 SLIST_INIT(gp->cutq);
81 SLIST_INIT(gp->seqq);
83 /* Set initial screen type and mode based on the program name. */
84 readonly = 0;
85 if (!strcmp(gp->progname, "ex") || !strcmp(gp->progname, "nex"))
86 LF_INIT(SC_EX);
87 else {
88 /* Nview, view are readonly. */
89 if (!strcmp(gp->progname, "nview") ||
90 !strcmp(gp->progname, "view"))
91 readonly = 1;
93 /* Vi is the default. */
94 LF_INIT(SC_VI);
97 /* Convert old-style arguments into new-style ones. */
98 if (v_obsolete(gp->progname, argv))
99 return (1);
101 /* Parse the arguments. */
102 flagchk = '\0';
103 tag_f = wsizearg = NULL;
104 lflag = secure = silent = 0;
105 startup = 1;
107 /* Set the file snapshot flag. */
108 F_SET(gp, G_SNAPSHOT);
110 #ifdef DEBUG
111 while ((ch = getopt(argc, argv, "c:D:eFlRrSsT:t:vw:")) != EOF)
112 #else
113 while ((ch = getopt(argc, argv, "c:eFlRrSst:vw:")) != EOF)
114 #endif
115 switch (ch) {
116 case 'c': /* Run the command. */
118 * XXX
119 * We should support multiple -c options.
121 if (gp->c_option != NULL) {
122 v_estr(gp->progname, 0,
123 "only one -c command may be specified.");
124 return (1);
126 gp->c_option = optarg;
127 break;
128 #ifdef DEBUG
129 case 'D':
130 switch (optarg[0]) {
131 case 's':
132 startup = 0;
133 break;
134 case 'w':
135 attach(gp);
136 break;
137 default:
138 v_estr(gp->progname, 0,
139 "usage: -D requires s or w argument.");
140 return (1);
142 break;
143 #endif
144 case 'e': /* Ex mode. */
145 LF_CLR(SC_VI);
146 LF_SET(SC_EX);
147 break;
148 case 'F': /* No snapshot. */
149 F_CLR(gp, G_SNAPSHOT);
150 break;
151 case 'l': /* Set lisp, showmatch options. */
152 lflag = 1;
153 break;
154 case 'R': /* Readonly. */
155 readonly = 1;
156 break;
157 case 'r': /* Recover. */
158 if (flagchk == 't') {
159 v_estr(gp->progname, 0,
160 "only one of -r and -t may be specified.");
161 return (1);
163 flagchk = 'r';
164 break;
165 case 'S':
166 secure = 1;
167 break;
168 case 's':
169 silent = 1;
170 break;
171 #ifdef DEBUG
172 case 'T': /* Trace. */
173 if ((gp->tracefp = fopen(optarg, "w")) == NULL) {
174 v_estr(gp->progname, errno, optarg);
175 goto err;
177 (void)fprintf(gp->tracefp,
178 "\n===\ntrace: open %s\n", optarg);
179 break;
180 #endif
181 case 't': /* Tag. */
182 if (flagchk == 'r') {
183 v_estr(gp->progname, 0,
184 "only one of -r and -t may be specified.");
185 return (1);
187 if (flagchk == 't') {
188 v_estr(gp->progname, 0,
189 "only one tag file may be specified.");
190 return (1);
192 flagchk = 't';
193 tag_f = optarg;
194 break;
195 case 'v': /* Vi mode. */
196 LF_CLR(SC_EX);
197 LF_SET(SC_VI);
198 break;
199 case 'w':
200 wsizearg = optarg;
201 break;
202 case '?':
203 default:
204 (void)gp->scr_usage();
205 return (1);
207 argc -= optind;
208 argv += optind;
211 * -s option is only meaningful to ex.
213 * If not reading from a terminal, it's like -s was specified.
215 if (silent && !LF_ISSET(SC_EX)) {
216 v_estr(gp->progname, 0, "-s option is only applicable to ex.");
217 goto err;
219 if (LF_ISSET(SC_EX) && F_ISSET(gp, G_SCRIPTED))
220 silent = 1;
223 * Build and initialize the first/current screen. This is a bit
224 * tricky. If an error is returned, we may or may not have a
225 * screen structure. If we have a screen structure, put it on a
226 * display queue so that the error messages get displayed.
228 * !!!
229 * Everything we do until we go interactive is done in ex mode.
231 if (screen_init(gp, NULL, &sp)) {
232 if (sp != NULL)
233 TAILQ_INSERT_HEAD(gp->dq, sp, q);
234 goto err;
236 F_SET(sp, SC_EX);
237 TAILQ_INSERT_HEAD(gp->dq, sp, q);
239 if (v_key_init(sp)) /* Special key initialization. */
240 goto err;
242 { int oargs[5], *oargp = oargs;
243 if (lflag) { /* Command-line options. */
244 *oargp++ = O_LISP;
245 *oargp++ = O_SHOWMATCH;
247 if (readonly)
248 *oargp++ = O_READONLY;
249 if (secure)
250 *oargp++ = O_SECURE;
251 *oargp = -1; /* Options initialization. */
252 if (opts_init(sp, oargs))
253 goto err;
255 if (wsizearg != NULL) {
256 ARGS *av[2], a, b;
257 (void)snprintf(path, sizeof(path), "window=%s", wsizearg);
258 a.bp = (CHAR_T *)path;
259 a.len = strlen(path);
260 b.bp = NULL;
261 b.len = 0;
262 av[0] = &a;
263 av[1] = &b;
264 (void)opts_set(sp, av, NULL);
266 if (silent) { /* Ex batch mode option values. */
267 O_CLR(sp, O_AUTOPRINT);
268 O_CLR(sp, O_PROMPT);
269 O_CLR(sp, O_VERBOSE);
270 O_CLR(sp, O_WARN);
271 F_SET(sp, SC_EX_SILENT);
274 sp->rows = O_VAL(sp, O_LINES); /* Make ex formatting work. */
275 sp->cols = O_VAL(sp, O_COLUMNS);
277 if (!silent && startup) { /* Read EXINIT, exrc files. */
278 if (ex_exrc(sp))
279 goto err;
280 if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) {
281 if (screen_end(sp))
282 goto err;
283 goto done;
288 * List recovery files if -r specified without file arguments.
289 * Note, options must be initialized and startup information
290 * read before doing this.
292 if (flagchk == 'r' && argv[0] == NULL) {
293 if (rcv_list(sp))
294 goto err;
295 if (screen_end(sp))
296 goto err;
297 goto done;
301 * !!!
302 * Initialize the default ^D, ^U scrolling value here, after the
303 * user has had every opportunity to set the window option.
305 * It's historic practice that changing the value of the window
306 * option did not alter the default scrolling value, only giving
307 * a count to ^D/^U did that.
309 sp->defscroll = (O_VAL(sp, O_WINDOW) + 1) / 2;
312 * If we don't have a command-line option, switch into the right
313 * editor now, so that we position default files correctly, and
314 * so that any tags file file-already-locked messages are in the
315 * vi screen, not the ex screen.
317 * XXX
318 * If we have a command-line option, the error message can end
319 * up in the wrong place, but I think that the combination is
320 * unlikely.
322 if (gp->c_option == NULL) {
323 F_CLR(sp, SC_EX | SC_VI);
324 F_SET(sp, LF_ISSET(SC_EX | SC_VI));
327 /* Open a tag file if specified. */
328 if (tag_f != NULL) {
329 CHAR2INT(sp, tag_f, strlen(tag_f) + 1, w, wlen);
330 if (ex_tag_first(sp, w))
331 goto err;
335 * Append any remaining arguments as file names. Files are recovery
336 * files if -r specified. If the tag option or ex startup commands
337 * loaded a file, then any file arguments are going to come after it.
339 if (*argv != NULL) {
340 if (sp->frp != NULL) {
341 /* Cheat -- we know we have an extra argv slot. */
342 *--argv = strdup(sp->frp->name);
343 if (*argv == NULL) {
344 v_estr(gp->progname, errno, NULL);
345 goto err;
348 sp->argv = sp->cargv = argv;
349 F_SET(sp, SC_ARGNOFREE);
350 if (flagchk == 'r')
351 F_SET(sp, SC_ARGRECOVER);
355 * If the ex startup commands and or/the tag option haven't already
356 * created a file, create one. If no command-line files were given,
357 * use a temporary file.
359 if (sp->frp == NULL) {
360 if (sp->argv == NULL) {
361 if ((frp = file_add(sp, NULL)) == NULL)
362 goto err;
363 } else {
364 if ((frp = file_add(sp, sp->argv[0])) == NULL)
365 goto err;
366 if (F_ISSET(sp, SC_ARGRECOVER))
367 F_SET(frp, FR_RECOVER);
370 if (file_init(sp, frp, NULL, 0))
371 goto err;
372 if (EXCMD_RUNNING(gp)) {
373 (void)ex_cmd(sp);
374 if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) {
375 if (screen_end(sp))
376 goto err;
377 goto done;
383 * Check to see if we need to wait for ex. If SC_SCR_EX is set, ex
384 * was forced to initialize the screen during startup. We'd like to
385 * wait for a single character from the user, but we can't because
386 * we're not in raw mode. We can't switch to raw mode because the
387 * vi initialization will switch to xterm's alternate screen, causing
388 * us to lose the messages we're pausing to make sure the user read.
389 * So, wait for a complete line.
391 if (F_ISSET(sp, SC_SCR_EX)) {
392 p = msg_cmsg(sp, CMSG_CONT_R, &len);
393 (void)write(STDOUT_FILENO, p, len);
394 for (;;) {
395 if (v_event_get(sp, &ev, 0, 0))
396 goto err;
397 if (ev.e_event == E_INTERRUPT ||
398 (ev.e_event == E_CHARACTER &&
399 (ev.e_value == K_CR || ev.e_value == K_NL)))
400 break;
401 (void)gp->scr_bell(sp);
405 /* Switch into the right editor, regardless. */
406 F_CLR(sp, SC_EX | SC_VI);
407 F_SET(sp, LF_ISSET(SC_EX | SC_VI) | SC_STATUS_CNT);
410 * Main edit loop. Vi handles split screens itself, we only return
411 * here when switching editor modes or restarting the screen.
413 while (sp != NULL)
414 if (F_ISSET(sp, SC_EX) ? ex(&sp) : vi(&sp))
415 goto err;
417 done: rval = 0;
418 if (0)
419 err: rval = 1;
421 /* Clean out the global structure. */
422 v_end(gp);
424 return (rval);
428 * v_end --
429 * End the program, discarding screens and most of the global area.
431 * PUBLIC: void v_end(GS *);
433 void
434 v_end(gp)
435 GS *gp;
437 MSGS *mp;
438 SCR *sp;
440 /* If there are any remaining screens, kill them off. */
441 if (gp->ccl_sp != NULL) {
442 (void)file_end(gp->ccl_sp, NULL, 1);
443 (void)screen_end(gp->ccl_sp);
445 while ((sp = TAILQ_FIRST(gp->dq)) != NULL)
446 (void)screen_end(sp);
447 while ((sp = TAILQ_FIRST(gp->hq)) != NULL)
448 (void)screen_end(sp);
450 #if defined(DEBUG) || defined(PURIFY)
451 { FREF *frp;
452 /* Free FREF's. */
453 while ((frp = TAILQ_FIRST(gp->frefq)) != NULL) {
454 TAILQ_REMOVE(gp->frefq, frp, q);
455 if (frp->name != NULL)
456 free(frp->name);
457 if (frp->tname != NULL)
458 free(frp->tname);
459 free(frp);
463 /* Free key input queue. */
464 if (gp->i_event != NULL)
465 free(gp->i_event);
467 /* Free cut buffers. */
468 cut_close(gp);
470 /* Free map sequences. */
471 seq_close(gp);
473 /* Free default buffer storage. */
474 (void)text_lfree(gp->dcb_store.textq);
476 /* Close message catalogs. */
477 msg_close(gp);
478 #endif
480 /* Ring the bell if scheduled. */
481 if (F_ISSET(gp, G_BELLSCHED))
482 (void)fprintf(stderr, "\07"); /* \a */
485 * Flush any remaining messages. If a message is here, it's almost
486 * certainly the message about the event that killed us (although
487 * it's possible that the user is sourcing a file that exits from the
488 * editor).
490 while ((mp = SLIST_FIRST(gp->msgq)) != NULL) {
491 (void)fprintf(stderr, "%s%.*s",
492 mp->mtype == M_ERR ? "ex/vi: " : "", (int)mp->len, mp->buf);
493 SLIST_REMOVE_HEAD(gp->msgq, q);
494 #if defined(DEBUG) || defined(PURIFY)
495 free(mp->buf);
496 free(mp);
497 #endif
500 #if defined(DEBUG) || defined(PURIFY)
501 /* Free any temporary space. */
502 if (gp->tmp_bp != NULL)
503 free(gp->tmp_bp);
505 #if defined(DEBUG)
506 /* Close debugging file descriptor. */
507 if (gp->tracefp != NULL)
508 (void)fclose(gp->tracefp);
509 #endif
510 #endif
514 * v_obsolete --
515 * Convert historic arguments into something getopt(3) will like.
517 static int
518 v_obsolete(
519 char *name,
520 char *argv[])
522 size_t len;
523 char *p;
526 * Translate old style arguments into something getopt will like.
527 * Make sure it's not text space memory, because ex modifies the
528 * strings.
529 * Change "+" into "-c$".
530 * Change "+<anything else>" into "-c<anything else>".
531 * Change "-" into "-s"
532 * The c, T, t and w options take arguments so they can't be
533 * special arguments.
535 * Stop if we find "--" as an argument, the user may want to edit
536 * a file named "+foo".
538 while (*++argv && strcmp(argv[0], "--"))
539 if (argv[0][0] == '+') {
540 if (argv[0][1] == '\0') {
541 argv[0] = strdup("-c$");
542 if (argv[0] == NULL)
543 goto nomem;
544 } else {
545 p = argv[0];
546 len = strlen(argv[0]);
547 argv[0] = malloc(len + 2);
548 if (argv[0] == NULL)
549 goto nomem;
550 argv[0][0] = '-';
551 argv[0][1] = 'c';
552 (void)strlcpy(argv[0] + 2, p + 1, len);
554 } else if (argv[0][0] == '-')
555 if (argv[0][1] == '\0') {
556 argv[0] = strdup("-s");
557 if (argv[0] == NULL) {
558 nomem: v_estr(name, errno, NULL);
559 return (1);
561 } else
562 if ((argv[0][1] == 'c' || argv[0][1] == 'T' ||
563 argv[0][1] == 't' || argv[0][1] == 'w') &&
564 argv[0][2] == '\0')
565 ++argv;
566 return (0);
569 #ifdef DEBUG
570 static void
571 attach(GS *gp)
573 int fd;
574 char ch;
576 if ((fd = open(_PATH_TTY, O_RDONLY, 0)) < 0) {
577 v_estr(gp->progname, errno, _PATH_TTY);
578 return;
581 (void)printf("process %lu waiting, enter <CR> to continue: ",
582 (u_long)getpid());
583 (void)fflush(stdout);
585 do {
586 if (read(fd, &ch, 1) != 1) {
587 (void)close(fd);
588 return;
590 } while (ch != '\n' && ch != '\r');
591 (void)close(fd);
593 #endif
595 static void
596 v_estr(
597 char *name,
598 int eno,
599 char *msg)
601 (void)fprintf(stderr, "%s", name);
602 if (msg != NULL)
603 (void)fprintf(stderr, ": %s", msg);
604 if (eno)
605 (void)fprintf(stderr, ": %s", strerror(errno));
606 (void)fprintf(stderr, "\n");