home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Spezial / SPEZIAL2_97.zip / SPEZIAL2_97.iso / ANWEND / EDITOR / NVI179B / NVI179B.ZIP / common / main.c < prev    next >
C/C++ Source or Header  |  1996-10-11  |  15KB  |  618 lines

  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
  13. static 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
  21. static 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.  
  42. static void     attach __P((GS *));
  43. static void     v_estr __P((char *, int, char *));
  44. static int     v_obsolete __P((char *, char *[]));
  45.  
  46. /*
  47.  * editor --
  48.  *    Main editor routine.
  49.  *
  50.  * PUBLIC: int editor __P((GS *, int, char *[]));
  51.  */
  52. int
  53. editor(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.  
  422. done:    rval = 0;
  423.     if (0)
  424. err:        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.  */
  438. void
  439. v_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.  */
  526. static int
  527. v_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) {
  567. nomem:                    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
  580. static void
  581. attach(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.  
  606. static void
  607. v_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. }
  618.