source: trunk/third/nvi/common/main.c @ 14302

Revision 14302, 14.2 KB checked in by ghudson, 25 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r14301, which included commits to RCS files with non-trunk default branches.
Line 
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.
6 *
7 * See the LICENSE file for redistribution information.
8 */
9
10#include "config.h"
11
12#ifndef lint
13static const char copyright[] =
14"@(#) Copyright (c) 1992, 1993, 1994\n\
15        The Regents of the University of California.  All rights reserved.\n\
16@(#) Copyright (c) 1992, 1993, 1994, 1995, 1996\n\
17        Keith Bostic.  All rights reserved.\n";
18#endif /* not lint */
19
20#ifndef lint
21static const char sccsid[] = "@(#)main.c        10.48 (Berkeley) 10/11/96";
22#endif /* not lint */
23
24#include <sys/types.h>
25#include <sys/queue.h>
26#include <sys/stat.h>
27#include <sys/time.h>
28
29#include <bitstring.h>
30#include <errno.h>
31#include <fcntl.h>
32#include <limits.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <unistd.h>
37
38#include "common.h"
39#include "../vi/vi.h"
40#include "pathnames.h"
41
42static void      attach __P((GS *));
43static void      v_estr __P((char *, int, char *));
44static int       v_obsolete __P((char *, char *[]));
45
46/*
47 * editor --
48 *      Main editor routine.
49 *
50 * PUBLIC: int editor __P((GS *, int, char *[]));
51 */
52int
53editor(gp, argc, argv)
54        GS *gp;
55        int argc;
56        char *argv[];
57{
58        extern int optind;
59        extern char *optarg;
60        const char *p;
61        EVENT ev;
62        FREF *frp;
63        SCR *sp;
64        size_t len;
65        u_int flags;
66        int ch, flagchk, lflag, secure, startup, readonly, rval, silent;
67        char *tag_f, *wsizearg, path[256];
68
69        /* Initialize the busy routine, if not defined by the screen. */
70        if (gp->scr_busy == NULL)
71                gp->scr_busy = vs_busy;
72        /* Initialize the message routine, if not defined by the screen. */
73        if (gp->scr_msg == NULL)
74                gp->scr_msg = vs_msg;
75
76        /* Common global structure initialization. */
77        CIRCLEQ_INIT(&gp->dq);
78        CIRCLEQ_INIT(&gp->hq);
79        LIST_INIT(&gp->ecq);
80        LIST_INSERT_HEAD(&gp->ecq, &gp->excmd, q);
81        gp->noprint = DEFAULT_NOPRINT;
82
83        /* Structures shared by screens so stored in the GS structure. */
84        CIRCLEQ_INIT(&gp->frefq);
85        CIRCLEQ_INIT(&gp->dcb_store.textq);
86        LIST_INIT(&gp->cutq);
87        LIST_INIT(&gp->seqq);
88
89        /* Set initial screen type and mode based on the program name. */
90        readonly = 0;
91        if (!strcmp(gp->progname, "ex") || !strcmp(gp->progname, "nex"))
92                LF_INIT(SC_EX);
93        else {
94                /* Nview, view are readonly. */
95                if (!strcmp(gp->progname, "nview") ||
96                    !strcmp(gp->progname, "view"))
97                        readonly = 1;
98               
99                /* Vi is the default. */
100                LF_INIT(SC_VI);
101        }
102
103        /* Convert old-style arguments into new-style ones. */
104        if (v_obsolete(gp->progname, argv))
105                return (1);
106
107        /* Parse the arguments. */
108        flagchk = '\0';
109        tag_f = wsizearg = NULL;
110        lflag = secure = silent = 0;
111        startup = 1;
112
113        /* Set the file snapshot flag. */
114        F_SET(gp, G_SNAPSHOT);
115
116#ifdef DEBUG
117        while ((ch = getopt(argc, argv, "c:D:eFlRrSsT:t:vw:")) != EOF)
118#else
119        while ((ch = getopt(argc, argv, "c:eFlRrSst:vw:")) != EOF)
120#endif
121                switch (ch) {
122                case 'c':               /* Run the command. */
123                        /*
124                         * XXX
125                         * We should support multiple -c options.
126                         */
127                        if (gp->c_option != NULL) {
128                                v_estr(gp->progname, 0,
129                                    "only one -c command may be specified.");
130                                return (1);
131                        }
132                        gp->c_option = optarg;
133                        break;
134#ifdef DEBUG
135                case 'D':
136                        switch (optarg[0]) {
137                        case 's':
138                                startup = 0;
139                                break;
140                        case 'w':
141                                attach(gp);
142                                break;
143                        default:
144                                v_estr(gp->progname, 0,
145                                    "usage: -D requires s or w argument.");
146                                return (1);
147                        }
148                        break;
149#endif
150                case 'e':               /* Ex mode. */
151                        LF_CLR(SC_VI);
152                        LF_SET(SC_EX);
153                        break;
154                case 'F':               /* No snapshot. */
155                        F_CLR(gp, G_SNAPSHOT);
156                        break;
157                case 'l':               /* Set lisp, showmatch options. */
158                        lflag = 1;
159                        break;
160                case 'R':               /* Readonly. */
161                        readonly = 1;
162                        break;
163                case 'r':               /* Recover. */
164                        if (flagchk == 't') {
165                                v_estr(gp->progname, 0,
166                                    "only one of -r and -t may be specified.");
167                                return (1);
168                        }
169                        flagchk = 'r';
170                        break;
171                case 'S':
172                        secure = 1;
173                        break;
174                case 's':
175                        silent = 1;
176                        break;
177#ifdef DEBUG
178                case 'T':               /* Trace. */
179                        if ((gp->tracefp = fopen(optarg, "w")) == NULL) {
180                                v_estr(gp->progname, errno, optarg);
181                                goto err;
182                        }
183                        (void)fprintf(gp->tracefp,
184                            "\n===\ntrace: open %s\n", optarg);
185                        break;
186#endif
187                case 't':               /* Tag. */
188                        if (flagchk == 'r') {
189                                v_estr(gp->progname, 0,
190                                    "only one of -r and -t may be specified.");
191                                return (1);
192                        }
193                        if (flagchk == 't') {
194                                v_estr(gp->progname, 0,
195                                    "only one tag file may be specified.");
196                                return (1);
197                        }
198                        flagchk = 't';
199                        tag_f = optarg;
200                        break;
201                case 'v':               /* Vi mode. */
202                        LF_CLR(SC_EX);
203                        LF_SET(SC_VI);
204                        break;
205                case 'w':
206                        wsizearg = optarg;
207                        break;
208                case '?':
209                default:
210                        (void)gp->scr_usage();
211                        return (1);
212                }
213        argc -= optind;
214        argv += optind;
215
216        /*
217         * -s option is only meaningful to ex.
218         *
219         * If not reading from a terminal, it's like -s was specified.
220         */
221        if (silent && !LF_ISSET(SC_EX)) {
222                v_estr(gp->progname, 0, "-s option is only applicable to ex.");
223                goto err;
224        }
225        if (LF_ISSET(SC_EX) && F_ISSET(gp, G_SCRIPTED))
226                silent = 1;
227
228        /*
229         * Build and initialize the first/current screen.  This is a bit
230         * tricky.  If an error is returned, we may or may not have a
231         * screen structure.  If we have a screen structure, put it on a
232         * display queue so that the error messages get displayed.
233         *
234         * !!!
235         * Everything we do until we go interactive is done in ex mode.
236         */
237        if (screen_init(gp, NULL, &sp)) {
238                if (sp != NULL)
239                        CIRCLEQ_INSERT_HEAD(&gp->dq, sp, q);
240                goto err;
241        }
242        F_SET(sp, SC_EX);
243        CIRCLEQ_INSERT_HEAD(&gp->dq, sp, q);
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        if (secure)
256                *oargp++ = O_SECURE;
257        *oargp = -1;                    /* Options initialization. */
258        if (opts_init(sp, oargs))
259                goto err;
260        }
261        if (wsizearg != NULL) {
262                ARGS *av[2], a, b;
263                (void)snprintf(path, sizeof(path), "window=%s", wsizearg);
264                a.bp = (CHAR_T *)path;
265                a.len = strlen(path);
266                b.bp = NULL;
267                b.len = 0;
268                av[0] = &a;
269                av[1] = &b;
270                (void)opts_set(sp, av, NULL);
271        }
272        if (silent) {                   /* Ex batch mode option values. */
273                O_CLR(sp, O_AUTOPRINT);
274                O_CLR(sp, O_PROMPT);
275                O_CLR(sp, O_VERBOSE);
276                O_CLR(sp, O_WARN);
277                F_SET(sp, SC_EX_SILENT);
278        }
279
280        sp->rows = O_VAL(sp, O_LINES);  /* Make ex formatting work. */
281        sp->cols = O_VAL(sp, O_COLUMNS);
282
283        if (!silent && startup) {       /* Read EXINIT, exrc files. */
284                if (ex_exrc(sp))
285                        goto err;
286                if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) {
287                        if (screen_end(sp))
288                                goto err;
289                        goto done;
290                }
291        }
292
293        /*
294         * List recovery files if -r specified without file arguments.
295         * Note, options must be initialized and startup information
296         * read before doing this.
297         */
298        if (flagchk == 'r' && argv[0] == NULL) {
299                if (rcv_list(sp))
300                        goto err;
301                if (screen_end(sp))
302                        goto err;
303                goto done;
304        }
305
306        /*
307         * !!!
308         * Initialize the default ^D, ^U scrolling value here, after the
309         * user has had every opportunity to set the window option.
310         *
311         * It's historic practice that changing the value of the window
312         * option did not alter the default scrolling value, only giving
313         * a count to ^D/^U did that.
314         */
315        sp->defscroll = (O_VAL(sp, O_WINDOW) + 1) / 2;
316
317        /*
318         * If we don't have a command-line option, switch into the right
319         * editor now, so that we position default files correctly, and
320         * so that any tags file file-already-locked messages are in the
321         * vi screen, not the ex screen.
322         *
323         * XXX
324         * If we have a command-line option, the error message can end
325         * up in the wrong place, but I think that the combination is
326         * unlikely.
327         */
328        if (gp->c_option == NULL) {
329                F_CLR(sp, SC_EX | SC_VI);
330                F_SET(sp, LF_ISSET(SC_EX | SC_VI));
331        }
332
333        /* Open a tag file if specified. */
334        if (tag_f != NULL && ex_tag_first(sp, tag_f))
335                goto err;
336
337        /*
338         * Append any remaining arguments as file names.  Files are recovery
339         * files if -r specified.  If the tag option or ex startup commands
340         * loaded a file, then any file arguments are going to come after it.
341         */
342        if (*argv != NULL) {
343                if (sp->frp != NULL) {
344                        /* Cheat -- we know we have an extra argv slot. */
345                        MALLOC_NOMSG(sp,
346                            *--argv, char *, strlen(sp->frp->name) + 1);
347                        if (*argv == NULL) {
348                                v_estr(gp->progname, errno, NULL);
349                                goto err;
350                        }
351                        (void)strcpy(*argv, sp->frp->name);
352                }
353                sp->argv = sp->cargv = argv;
354                F_SET(sp, SC_ARGNOFREE);
355                if (flagchk == 'r')
356                        F_SET(sp, SC_ARGRECOVER);
357        }
358
359        /*
360         * If the ex startup commands and or/the tag option haven't already
361         * created a file, create one.  If no command-line files were given,
362         * use a temporary file.
363         */
364        if (sp->frp == NULL) {
365                if (sp->argv == NULL) {
366                        if ((frp = file_add(sp, NULL)) == NULL)
367                                goto err;
368                } else  {
369                        if ((frp = file_add(sp, (CHAR_T *)sp->argv[0])) == NULL)
370                                goto err;
371                        if (F_ISSET(sp, SC_ARGRECOVER))
372                                F_SET(frp, FR_RECOVER);
373                }
374
375                if (file_init(sp, frp, NULL, 0))
376                        goto err;
377                if (EXCMD_RUNNING(gp)) {
378                        (void)ex_cmd(sp);
379                        if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) {
380                                if (screen_end(sp))
381                                        goto err;
382                                goto done;
383                        }
384                }
385        }
386
387        /*
388         * Check to see if we need to wait for ex.  If SC_SCR_EX is set, ex
389         * was forced to initialize the screen during startup.  We'd like to
390         * wait for a single character from the user, but we can't because
391         * we're not in raw mode.  We can't switch to raw mode because the
392         * vi initialization will switch to xterm's alternate screen, causing
393         * us to lose the messages we're pausing to make sure the user read.
394         * So, wait for a complete line. 
395         */
396        if (F_ISSET(sp, SC_SCR_EX)) {
397                p = msg_cmsg(sp, CMSG_CONT_R, &len);
398                (void)write(STDOUT_FILENO, p, len);
399                for (;;) {
400                        if (v_event_get(sp, &ev, 0, 0))
401                                goto err;
402                        if (ev.e_event == E_INTERRUPT ||
403                            ev.e_event == E_CHARACTER &&
404                            (ev.e_value == K_CR || ev.e_value == K_NL))
405                                break;
406                        (void)gp->scr_bell(sp);
407                }
408        }
409
410        /* Switch into the right editor, regardless. */
411        F_CLR(sp, SC_EX | SC_VI);
412        F_SET(sp, LF_ISSET(SC_EX | SC_VI) | SC_STATUS_CNT);
413
414        /*
415         * Main edit loop.  Vi handles split screens itself, we only return
416         * here when switching editor modes or restarting the screen.
417         */
418        while (sp != NULL)
419                if (F_ISSET(sp, SC_EX) ? ex(&sp) : vi(&sp))
420                        goto err;
421
422done:   rval = 0;
423        if (0)
424err:            rval = 1;
425
426        /* Clean out the global structure. */
427        v_end(gp);
428
429        return (rval);
430}
431
432/*
433 * v_end --
434 *      End the program, discarding screens and most of the global area.
435 *
436 * PUBLIC: void v_end __P((GS *));
437 */
438void
439v_end(gp)
440        GS *gp;
441{
442        MSGS *mp;
443        SCR *sp;
444
445        /* If there are any remaining screens, kill them off. */
446        if (gp->ccl_sp != NULL) {
447                (void)file_end(gp->ccl_sp, NULL, 1);
448                (void)screen_end(gp->ccl_sp);
449        }
450        while ((sp = gp->dq.cqh_first) != (void *)&gp->dq)
451                (void)screen_end(sp);
452        while ((sp = gp->hq.cqh_first) != (void *)&gp->hq)
453                (void)screen_end(sp);
454
455#ifdef HAVE_PERL_INTERP
456        perl_end(gp);
457#endif
458
459#if defined(DEBUG) || defined(PURIFY) || defined(LIBRARY)
460        { FREF *frp;
461                /* Free FREF's. */
462                while ((frp = gp->frefq.cqh_first) != (FREF *)&gp->frefq) {
463                        CIRCLEQ_REMOVE(&gp->frefq, frp, q);
464                        if (frp->name != NULL)
465                                free(frp->name);
466                        if (frp->tname != NULL)
467                                free(frp->tname);
468                        free(frp);
469                }
470        }
471
472        /* Free key input queue. */
473        if (gp->i_event != NULL)
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
485        /* Close message catalogs. */
486        msg_close(gp);
487#endif
488
489        /* Ring the bell if scheduled. */
490        if (F_ISSET(gp, G_BELLSCHED))
491                (void)fprintf(stderr, "\07");           /* \a */
492
493        /*
494         * Flush any remaining messages.  If a message is here, it's almost
495         * certainly the message about the event that killed us (although
496         * it's possible that the user is sourcing a file that exits from the
497         * editor).
498         */
499        while ((mp = gp->msgq.lh_first) != NULL) {
500                (void)fprintf(stderr, "%s%.*s",
501                    mp->mtype == M_ERR ? "ex/vi: " : "", (int)mp->len, mp->buf);
502                LIST_REMOVE(mp, q);
503#if defined(DEBUG) || defined(PURIFY) || defined(LIBRARY)
504                free(mp->buf);
505                free(mp);
506#endif
507        }
508
509#if defined(DEBUG) || defined(PURIFY) || defined(LIBRARY)
510        /* Free any temporary space. */
511        if (gp->tmp_bp != NULL)
512                free(gp->tmp_bp);
513
514#if defined(DEBUG)
515        /* Close debugging file descriptor. */
516        if (gp->tracefp != NULL)
517                (void)fclose(gp->tracefp);
518#endif
519#endif
520}
521
522/*
523 * v_obsolete --
524 *      Convert historic arguments into something getopt(3) will like.
525 */
526static int
527v_obsolete(name, argv)
528        char *name, *argv[];
529{
530        size_t len;
531        char *p;
532
533        /*
534         * Translate old style arguments into something getopt will like.
535         * Make sure it's not text space memory, because ex modifies the
536         * strings.
537         *      Change "+" into "-c$".
538         *      Change "+<anything else>" into "-c<anything else>".
539         *      Change "-" into "-s"
540         *      The c, T, t and w options take arguments so they can't be
541         *          special arguments.
542         *
543         * Stop if we find "--" as an argument, the user may want to edit
544         * a file named "+foo".
545         */
546        while (*++argv && strcmp(argv[0], "--"))
547                if (argv[0][0] == '+') {
548                        if (argv[0][1] == '\0') {
549                                MALLOC_NOMSG(NULL, argv[0], char *, 4);
550                                if (argv[0] == NULL)
551                                        goto nomem;
552                                (void)strcpy(argv[0], "-c$");
553                        } else  {
554                                p = argv[0];
555                                len = strlen(argv[0]);
556                                MALLOC_NOMSG(NULL, argv[0], char *, len + 2);
557                                if (argv[0] == NULL)
558                                        goto nomem;
559                                argv[0][0] = '-';
560                                argv[0][1] = 'c';
561                                (void)strcpy(argv[0] + 2, p + 1);
562                        }
563                } else if (argv[0][0] == '-')
564                        if (argv[0][1] == '\0') {
565                                MALLOC_NOMSG(NULL, argv[0], char *, 3);
566                                if (argv[0] == NULL) {
567nomem:                                  v_estr(name, errno, NULL);
568                                        return (1);
569                                }
570                                (void)strcpy(argv[0], "-s");
571                        } else
572                                if ((argv[0][1] == 'c' || argv[0][1] == 'T' ||
573                                    argv[0][1] == 't' || argv[0][1] == 'w') &&
574                                    argv[0][2] == '\0')
575                                        ++argv;
576        return (0);
577}
578
579#ifdef DEBUG
580static void
581attach(gp)
582        GS *gp;
583{
584        int fd;
585        char ch;
586
587        if ((fd = open(_PATH_TTY, O_RDONLY, 0)) < 0) {
588                v_estr(gp->progname, errno, _PATH_TTY);
589                return;
590        }
591
592        (void)printf("process %lu waiting, enter <CR> to continue: ",
593            (u_long)getpid());
594        (void)fflush(stdout);
595
596        do {
597                if (read(fd, &ch, 1) != 1) {
598                        (void)close(fd);
599                        return;
600                }
601        } while (ch != '\n' && ch != '\r');
602        (void)close(fd);
603}
604#endif
605
606static void
607v_estr(name, eno, msg)
608        char *name, *msg;
609        int eno;
610{
611        (void)fprintf(stderr, "%s", name);
612        if (msg != NULL)
613                (void)fprintf(stderr, ": %s", msg);
614        if (eno)
615                (void)fprintf(stderr, ": %s", strerror(errno));
616        (void)fprintf(stderr, "\n");
617}
Note: See TracBrowser for help on using the repository browser.