home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Spezial / SPEZIAL2_97.zip / SPEZIAL2_97.iso / ANWEND / EDITOR / NVI179B / NVI179B.ZIP / cl / cl_screen.c < prev    next >
C/C++ Source or Header  |  1997-06-27  |  16KB  |  662 lines

  1. /*-
  2.  * Copyright (c) 1993, 1994
  3.  *    The Regents of the University of California.  All rights reserved.
  4.  * Copyright (c) 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 sccsid[] = "@(#)cl_screen.c    10.49 (Berkeley) 9/24/96";
  14. #endif /* not lint */
  15.  
  16. #include <sys/types.h>
  17. #include <sys/queue.h>
  18.  
  19. #include <bitstring.h>
  20. #include <curses.h>
  21. #include <errno.h>
  22. #include <signal.h>
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include <termios.h>
  27. #include <unistd.h>
  28.  
  29. #include "../common/common.h"
  30. #include "cl.h"
  31.  
  32. #ifdef __EMX__
  33. #define INCL_DOSPROCESS
  34. #define INCL_VIO
  35. #include <os2.h>
  36. #endif
  37.  
  38. static int    cl_ex_end __P((GS *));
  39. static int    cl_ex_init __P((SCR *));
  40. static void    cl_freecap __P((CL_PRIVATE *));
  41. static int    cl_vi_end __P((GS *));
  42. static int    cl_vi_init __P((SCR *));
  43. static int    cl_putenv __P((char *, char *, u_long));
  44.  
  45. /*
  46.  * cl_screen --
  47.  *    Switch screen types.
  48.  *
  49.  * PUBLIC: int cl_screen __P((SCR *, u_int32_t));
  50.  */
  51. int
  52. cl_screen(sp, flags)
  53.     SCR *sp;
  54.     u_int32_t flags;
  55. {
  56.     CL_PRIVATE *clp;
  57.     GS *gp;
  58.  
  59.     gp = sp->gp;
  60.     clp = CLP(sp);
  61.  
  62.     /* See if the current information is incorrect. */
  63.     if (F_ISSET(gp, G_SRESTART)) {
  64.         if (cl_quit(gp))
  65.             return (1);
  66.         F_CLR(gp, G_SRESTART);
  67.     }
  68.     
  69.     /* See if we're already in the right mode. */
  70.     if (LF_ISSET(SC_EX) && F_ISSET(sp, SC_SCR_EX) ||
  71.         LF_ISSET(SC_VI) && F_ISSET(sp, SC_SCR_VI))
  72.         return (0);
  73.  
  74.     /*
  75.      * Fake leaving ex mode.
  76.      *
  77.      * We don't actually exit ex or vi mode unless forced (e.g. by a window
  78.      * size change).  This is because many curses implementations can't be
  79.      * called twice in a single program.  Plus, it's faster.  If the editor
  80.      * "leaves" vi to enter ex, when it exits ex we'll just fall back into
  81.      * vi.
  82.      */
  83.     if (F_ISSET(sp, SC_SCR_EX))
  84.         F_CLR(sp, SC_SCR_EX);
  85.  
  86.     /*
  87.      * Fake leaving vi mode.
  88.      *
  89.      * Clear out the rest of the screen if we're in the middle of a split
  90.      * screen.  Move to the last line in the current screen -- this makes
  91.      * terminal scrolling happen naturally.  Note: *don't* move past the
  92.      * end of the screen, as there are ex commands (e.g., :read ! cat file)
  93.      * that don't want to.  Don't clear the info line, its contents may be
  94.      * valid, e.g. :file|append.
  95.      */
  96.     if (F_ISSET(sp, SC_SCR_VI)) {
  97.         F_CLR(sp, SC_SCR_VI);
  98.  
  99.         if (sp->q.cqe_next != (void *)&gp->dq) {
  100.             (void)move(RLNO(sp, sp->rows), 0);
  101.             clrtobot();
  102.         }
  103.         (void)move(RLNO(sp, sp->rows) - 1, 0);
  104.         refresh();
  105.  
  106. #ifdef __EMX__
  107. #ifdef EMX_CURSOR
  108.         if ((_emx_env & 0x0200) && clp->cs1 != -1)
  109.         {
  110.             VIOCURSORINFO ci;
  111.  
  112.             ci.yStart = clp->cs1;
  113.             ci.cEnd = clp->cs2;
  114.             ci.attr = clp->csa;
  115.             ci.cx = 1;
  116.             VioSetCurType(&ci, (HVIO) 0);
  117.         }
  118. #endif
  119. #endif
  120.     }
  121.  
  122.     /* Enter the requested mode. */
  123.     if (LF_ISSET(SC_EX)) {
  124.         if (cl_ex_init(sp))
  125.             return (1);
  126.         F_SET(clp, CL_IN_EX | CL_SCR_EX_INIT);
  127.  
  128.         /*
  129.          * If doing an ex screen for ex mode, move to the last line
  130.          * on the screen.
  131.          */
  132.         if (F_ISSET(sp, SC_EX) && clp->cup != NULL)
  133.             tputs(tgoto(clp->cup,
  134.                 0, O_VAL(sp, O_LINES) - 1), 1, cl_putchar);
  135.     } else {
  136.         if (cl_vi_init(sp))
  137.             return (1);
  138.         F_CLR(clp, CL_IN_EX);
  139.         F_SET(clp, CL_SCR_VI_INIT);
  140.     }
  141.     return (0);
  142. }
  143.  
  144. /*
  145.  * cl_quit --
  146.  *    Shutdown the screens.
  147.  *
  148.  * PUBLIC: int cl_quit __P((GS *));
  149.  */
  150. int
  151. cl_quit(gp)
  152.     GS *gp;
  153. {
  154.     CL_PRIVATE *clp;
  155.     int rval;
  156.  
  157.     rval = 0;
  158.     clp = GCLP(gp);
  159.  
  160.     /*
  161.      * If we weren't really running, ignore it.  This happens if the
  162.      * screen changes size before we've called curses.
  163.      */
  164.     if (!F_ISSET(clp, CL_SCR_EX_INIT | CL_SCR_VI_INIT))
  165.         return (0);
  166.  
  167.     /* Clean up the terminal mappings. */
  168.     if (cl_term_end(gp))
  169.         rval = 1;
  170.  
  171.     /* Really leave vi mode. */
  172.     if (F_ISSET(clp, CL_STDIN_TTY) &&
  173.         F_ISSET(clp, CL_SCR_VI_INIT) && cl_vi_end(gp))
  174.         rval = 1;
  175.  
  176.     /* Really leave ex mode. */
  177.     if (F_ISSET(clp, CL_STDIN_TTY) &&
  178.         F_ISSET(clp, CL_SCR_EX_INIT) && cl_ex_end(gp))
  179.         rval = 1;
  180.  
  181.     /*
  182.      * If we were running ex when we quit, or we're using an implementation
  183.      * of curses where endwin() doesn't get this right, restore the original
  184.      * terminal modes.
  185.      *
  186.      * XXX
  187.      * We always do this because it's too hard to figure out what curses
  188.      * implementations get it wrong.  It may discard type-ahead characters
  189.      * from the tty queue.
  190.      */
  191.     (void)tcsetattr(STDIN_FILENO, TCSADRAIN | TCSASOFT, &clp->orig);
  192.  
  193. #ifdef __EMX__
  194. #ifdef EMX_MOUSE
  195.     /* If using a mouse thread (OS/2), end it. */
  196.     if (clp->mou_pipe != -1)
  197.     {
  198.         close(clp->mou_pipe);
  199.         DosKillThread((TID) clp->mou_thr);
  200.     }
  201. #endif
  202. #ifdef EMX_CURSOR
  203.     /* Restore the cursor */
  204.     if ((_emx_env & 0x0200) && clp->cs1 != -1)
  205.     {
  206.         VIOCURSORINFO ci;
  207.  
  208.         ci.yStart = clp->cs1;
  209.         ci.cEnd = clp->cs2;
  210.         ci.attr = clp->csa;
  211.         ci.cx = 1;
  212.         VioSetCurType(&ci, (HVIO) 0);
  213.     }
  214. #endif
  215. #endif
  216.  
  217.     F_CLR(clp, CL_SCR_EX_INIT | CL_SCR_VI_INIT);
  218.     return (rval);
  219. }
  220.  
  221. /*
  222.  * cl_vi_init --
  223.  *    Initialize the curses vi screen.
  224.  */
  225. static int
  226. cl_vi_init(sp)
  227.     SCR *sp;
  228. {
  229.     CL_PRIVATE *clp;
  230.     GS *gp;
  231.     char *o_cols, *o_lines, *o_term, *ttype;
  232.  
  233.     gp = sp->gp;
  234.     clp = CLP(sp);
  235.  
  236.     /* If already initialized, just set the terminal modes. */
  237.     if (F_ISSET(clp, CL_SCR_VI_INIT))
  238.         goto fast;
  239.  
  240.     /* Curses vi always reads from (and writes to) a terminal. */
  241.     if (!F_ISSET(clp, CL_STDIN_TTY) || !isatty(STDOUT_FILENO)) {
  242.         msgq(sp, M_ERR,
  243.             "016|Vi's standard input and output must be a terminal");
  244.         return (1);
  245.     }
  246.  
  247.     /* We'll need a terminal type. */
  248.     if (opts_empty(sp, O_TERM, 0))
  249.         return (1);
  250.     ttype = O_STR(sp, O_TERM);
  251.  
  252.     /*
  253.      * XXX
  254.      * Changing the row/column and terminal values is done by putting them
  255.      * into the environment, which is then read by curses.  What this loses
  256.      * in ugliness, it makes up for in stupidity.  We can't simply put the
  257.      * values into the environment ourselves, because in the presence of a
  258.      * kernel mechanism for returning the window size, entering values into
  259.      * the environment will screw up future screen resizing events, e.g. if
  260.      * the user enters a :shell command and then resizes their window.  So,
  261.      * if they weren't already in the environment, we make sure to delete
  262.      * them immediately after setting them.
  263.      *
  264.      * XXX
  265.      * Putting the TERM variable into the environment is necessary, even
  266.      * though we're using newterm() here.  We may be using initscr() as
  267.      * the underlying function.
  268.      */
  269.     o_term = getenv("TERM");
  270.     cl_putenv("TERM", ttype, 0);
  271.     o_lines = getenv("LINES");
  272.     cl_putenv("LINES", NULL, (u_long)O_VAL(sp, O_LINES));
  273.     o_cols = getenv("COLUMNS");
  274.     cl_putenv("COLUMNS", NULL, (u_long)O_VAL(sp, O_COLUMNS));
  275.  
  276.     /*
  277.      * We don't care about the SCREEN reference returned by newterm, we
  278.      * never have more than one SCREEN at a time.
  279.      *
  280.      * XXX
  281.      * The SunOS initscr() can't be called twice.  Don't even think about
  282.      * using it.  It fails in subtle ways (e.g. select(2) on fileno(stdin)
  283.      * stops working).  (The SVID notes that applications should only call
  284.      * initscr() once.)
  285.      *
  286.      * XXX
  287.      * The HP/UX newterm doesn't support the NULL first argument, so we
  288.      * have to specify the terminal type.
  289.      */
  290.     errno = 0;
  291.     if (newterm(ttype, stdout, stdin) == NULL) {
  292.         if (errno)
  293.             msgq(sp, M_SYSERR, "%s", ttype);
  294.         else
  295.             msgq(sp, M_ERR, "%s: unknown terminal type", ttype);
  296.         return (1);
  297.     }
  298.  
  299.     if (o_term == NULL)
  300.         unsetenv("TERM");
  301.     if (o_lines == NULL)
  302.         unsetenv("LINES");
  303.     if (o_cols == NULL)
  304.         unsetenv("COLUMNS");
  305.  
  306.     /*
  307.      * XXX
  308.      * Someone got let out alone without adult supervision -- the SunOS
  309.      * newterm resets the signal handlers.  There's a race, but it's not
  310.      * worth closing.
  311.      */
  312.     (void)sig_init(sp->gp, sp);
  313.  
  314.     /*
  315.      * We use raw mode.  What we want is 8-bit clean, however, signals
  316.      * and flow control should continue to work.  Admittedly, it sounds
  317.      * like cbreak, but it isn't.  Using cbreak() can get you additional
  318.      * things like IEXTEN, which turns on flags like DISCARD and LNEXT.
  319.      *
  320.      * !!!
  321.      * If raw isn't turning off echo and newlines, something's wrong.
  322.      * However, it shouldn't hurt.
  323.      */
  324.     noecho();            /* No character echo. */
  325.     nonl();                /* No CR/NL translation. */
  326.     raw();                /* 8-bit clean. */
  327.     idlok(stdscr, 1);        /* Use hardware insert/delete line. */
  328.  
  329.     /* Put the cursor keys into application mode. */
  330.     (void)keypad(stdscr, TRUE);
  331.  
  332.     /*
  333.      * XXX
  334.      * The screen TI sequence just got sent.  See the comment in
  335.      * cl_funcs.c:cl_attr().
  336.      */
  337.     clp->ti_te = TI_SENT;
  338.  
  339. #if !VI_DOSISH
  340.     /*
  341.      * XXX
  342.      * Historic implementations of curses handled SIGTSTP signals
  343.      * in one of three ways.  They either:
  344.      *
  345.      *    1: Set their own handler, regardless.
  346.      *    2: Did not set a handler if a handler was already installed.
  347.      *    3: Set their own handler, but then called any previously set
  348.      *       handler after completing their own cleanup.
  349.      *
  350.      * We don't try and figure out which behavior is in place, we force
  351.      * it to SIG_DFL after initializing the curses interface, which means
  352.      * that curses isn't going to take the signal.  Since curses isn't
  353.      * reentrant (i.e., the whole curses SIGTSTP interface is a fantasy),
  354.      * we're doing The Right Thing.
  355.      */
  356.     (void)signal(SIGTSTP, SIG_DFL);
  357. #endif
  358.  
  359.     /*
  360.      * If flow control was on, turn it back on.  Turn signals on.  ISIG
  361.      * turns on VINTR, VQUIT, VDSUSP and VSUSP.   The main curses code
  362.      * already installed a handler for VINTR.  We're going to disable the
  363.      * other three.
  364.      *
  365.      * XXX
  366.      * We want to use ^Y as a vi scrolling command.  If the user has the
  367.      * DSUSP character set to ^Y (common practice) clean it up.  As it's
  368.      * equally possible that the user has VDSUSP set to 'a', we disable
  369.      * it regardless.  It doesn't make much sense to suspend vi at read,
  370.      * so I don't think anyone will care.  Alternatively, we could look
  371.      * it up in the table of legal command characters and turn it off if
  372.      * it matches one.  VDSUSP wasn't in POSIX 1003.1-1990, so we test for
  373.      * it.
  374.      *
  375.      * XXX
  376.      * We don't check to see if the user had signals enabled originally.
  377.      * If they didn't, it's unclear what we're supposed to do here, but
  378.      * it's also pretty unlikely.
  379.      */
  380.     if (tcgetattr(STDIN_FILENO, &clp->vi_enter)) {
  381.         msgq(sp, M_SYSERR, "tcgetattr");
  382.         goto err;
  383.     }
  384.     if (clp->orig.c_iflag & IXON)
  385.         clp->vi_enter.c_iflag |= IXON;
  386.     if (clp->orig.c_iflag & IXOFF)
  387.         clp->vi_enter.c_iflag |= IXOFF;
  388.  
  389.     clp->vi_enter.c_lflag |= ISIG;
  390. #ifdef VDSUSP
  391.     clp->vi_enter.c_cc[VDSUSP] = _POSIX_VDISABLE;
  392. #endif
  393.     clp->vi_enter.c_cc[VQUIT] = _POSIX_VDISABLE;
  394.     clp->vi_enter.c_cc[VSUSP] = _POSIX_VDISABLE;
  395.  
  396.     /*
  397.      * XXX
  398.      * OSF/1 doesn't turn off the <discard>, <literal-next> or <status>
  399.      * characters when curses switches into raw mode.  It should be OK
  400.      * to do it explicitly for everyone.
  401.      */
  402. #ifdef VDISCARD
  403.     clp->vi_enter.c_cc[VDISCARD] = _POSIX_VDISABLE;
  404. #endif
  405. #ifdef VLNEXT
  406.     clp->vi_enter.c_cc[VLNEXT] = _POSIX_VDISABLE;
  407. #endif
  408. #ifdef VSTATUS
  409.     clp->vi_enter.c_cc[VSTATUS] = _POSIX_VDISABLE;
  410. #endif
  411.  
  412.     /* Initialize terminal based information. */
  413.     if (cl_term_init(sp))
  414.         goto err;
  415.  
  416. fast:    /* Set the terminal modes. */
  417.     if (tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &clp->vi_enter)) {
  418.         msgq(sp, M_SYSERR, "tcsetattr");
  419. err:        (void)cl_vi_end(sp->gp);
  420.         return (1);
  421.     }
  422.  
  423. #ifdef __EMX__
  424. #ifdef EMX_CURSOR
  425.     /* enable block cursor */
  426.     if (_emx_env & 0x200)
  427.     {
  428.         VIOCURSORINFO ci;
  429.         USHORT rc;
  430.  
  431.         if (clp->cs1 == -1)
  432.         {
  433.         rc = VioGetCurType(&ci, (HVIO) 0);
  434.         if (rc != 0)
  435.             fprintf(stderr, "[VGCT%d]\n", rc);
  436.         clp->cs1 = ci.yStart;
  437.         clp->cs2 = ci.cEnd;
  438.         clp->csa = ci.attr;
  439.         }
  440.         ci.yStart = 0;
  441.         /* I'm assuming it's sane... naughty naughty */
  442.         if (clp->cs2)
  443.         ci.cEnd = clp->cs2;
  444.         else
  445.         ci.cEnd = -100;
  446.         ci.cx = 1;
  447.         ci.attr = 0;
  448.         rc = VioSetCurType(&ci, (HVIO) 0);
  449.         if (rc != 0)
  450.         fprintf(stderr, "[VSCT%d]\n", rc);
  451.     }
  452. #endif
  453. #endif
  454.  
  455.     return (0);
  456. }
  457.  
  458. /*
  459.  * cl_vi_end --
  460.  *    Shutdown the vi screen.
  461.  */
  462. static int
  463. cl_vi_end(gp)
  464.     GS *gp;
  465. {
  466.     CL_PRIVATE *clp;
  467.  
  468.     clp = GCLP(gp);
  469.  
  470.     /* Restore the cursor keys to normal mode. */
  471.     (void)keypad(stdscr, FALSE);
  472.  
  473.     /*
  474.      * If we were running vi when we quit, scroll the screen up a single
  475.      * line so we don't lose any information.
  476.      *
  477.      * Move to the bottom of the window (some endwin implementations don't
  478.      * do this for you).
  479.      */
  480.     if (!F_ISSET(clp, CL_IN_EX)) {
  481.         (void)move(0, 0);
  482.         (void)deleteln();
  483.         (void)move(LINES - 1, 0);
  484.         (void)refresh();
  485.     }
  486.  
  487.     cl_freecap(clp);
  488.  
  489.     /* End curses window. */
  490.     (void)endwin();
  491.  
  492.     /*
  493.      * XXX
  494.      * The screen TE sequence just got sent.  See the comment in
  495.      * cl_funcs.c:cl_attr().
  496.      */
  497.     clp->ti_te = TE_SENT;
  498.  
  499.     return (0);
  500. }
  501.  
  502. /*
  503.  * cl_ex_init --
  504.  *    Initialize the ex screen.
  505.  */
  506. static int
  507. cl_ex_init(sp)
  508.     SCR *sp;
  509. {
  510.     CL_PRIVATE *clp;
  511.  
  512.     clp = CLP(sp);
  513.  
  514.     /* If already initialized, just set the terminal modes. */
  515.     if (F_ISSET(clp, CL_SCR_EX_INIT))
  516.         goto fast;
  517.  
  518.     /* If not reading from a file, we're done. */
  519.     if (!F_ISSET(clp, CL_STDIN_TTY))
  520.         return (0);
  521.  
  522.     /* Get the ex termcap/terminfo strings. */
  523.     (void)cl_getcap(sp, "cup", &clp->cup);
  524.     (void)cl_getcap(sp, "smso", &clp->smso);
  525.     (void)cl_getcap(sp, "rmso", &clp->rmso);
  526.     (void)cl_getcap(sp, "el", &clp->el);
  527.     (void)cl_getcap(sp, "cuu1", &clp->cuu1);
  528.  
  529.     /* Enter_standout_mode and exit_standout_mode are paired. */
  530.     if (clp->smso == NULL || clp->rmso == NULL) {
  531.         if (clp->smso != NULL) {
  532.             free(clp->smso);
  533.             clp->smso = NULL;
  534.         }
  535.         if (clp->rmso != NULL) {
  536.             free(clp->rmso);
  537.             clp->rmso = NULL;
  538.         }
  539.     }
  540.  
  541.     /*
  542.      * Turn on canonical mode, with normal input and output processing.
  543.      * Start with the original terminal settings as the user probably
  544.      * had them (including any local extensions) set correctly for the
  545.      * current terminal.
  546.      *
  547.      * !!!
  548.      * We can't get everything that we need portably; for example, ONLCR,
  549.      * mapping <newline> to <carriage-return> on output isn't required
  550.      * by POSIX 1003.1b-1993.  If this turns out to be a problem, then
  551.      * we'll either have to play some games on the mapping, or we'll have
  552.      * to make all ex printf's output \r\n instead of \n.
  553.      */
  554.     clp->ex_enter = clp->orig;
  555.     clp->ex_enter.c_lflag  |= ECHO | ECHOE | ECHOK | ICANON | IEXTEN | ISIG;
  556. #ifdef ECHOCTL
  557.     clp->ex_enter.c_lflag |= ECHOCTL;
  558. #endif
  559. #ifdef ECHOKE
  560.     clp->ex_enter.c_lflag |= ECHOKE;
  561. #endif
  562.     clp->ex_enter.c_iflag |= ICRNL;
  563.     clp->ex_enter.c_oflag |= OPOST;
  564. #ifdef ONLCR
  565.     clp->ex_enter.c_oflag |= ONLCR;
  566. #endif
  567.  
  568. fast:    if (tcsetattr(STDIN_FILENO, TCSADRAIN | TCSASOFT, &clp->ex_enter)) {
  569.         msgq(sp, M_SYSERR, "tcsetattr");
  570.         return (1);
  571.     }
  572.     return (0);
  573. }
  574.  
  575. /*
  576.  * cl_ex_end --
  577.  *    Shutdown the ex screen.
  578.  */
  579. static int
  580. cl_ex_end(gp)
  581.     GS *gp;
  582. {
  583.     CL_PRIVATE *clp;
  584.  
  585.     clp = GCLP(gp);
  586.  
  587.     cl_freecap(clp);
  588.  
  589.     return (0);
  590. }
  591.  
  592. /*
  593.  * cl_getcap --
  594.  *    Retrieve termcap/terminfo strings.
  595.  *
  596.  * PUBLIC: int cl_getcap __P((SCR *, char *, char **));
  597.  */
  598. int
  599. cl_getcap(sp, name, elementp)
  600.     SCR *sp;
  601.     char *name, **elementp;
  602. {
  603.     size_t len;
  604.     char *t;
  605.  
  606.     if ((t = tigetstr(name)) != NULL &&
  607.         t != (char *)-1 && (len = strlen(t)) != 0) {
  608.         MALLOC_RET(sp, *elementp, char *, len + 1);
  609.         memmove(*elementp, t, len + 1);
  610.     }
  611.     return (0);
  612. }
  613.  
  614. /*
  615.  * cl_freecap --
  616.  *    Free any allocated termcap/terminfo strings.
  617.  */
  618. static void
  619. cl_freecap(clp)
  620.     CL_PRIVATE *clp;
  621. {
  622.     if (clp->el != NULL) {
  623.         free(clp->el);
  624.         clp->el = NULL;
  625.     }
  626.     if (clp->cup != NULL) {
  627.         free(clp->cup);
  628.         clp->cup = NULL;
  629.     }
  630.     if (clp->cuu1 != NULL) {
  631.         free(clp->cuu1);
  632.         clp->cuu1 = NULL;
  633.     }
  634.     if (clp->rmso != NULL) {
  635.         free(clp->rmso);
  636.         clp->rmso = NULL;
  637.     }
  638.     if (clp->smso != NULL) {
  639.         free(clp->smso);
  640.         clp->smso = NULL;
  641.     }
  642. }
  643.  
  644. /*
  645.  * cl_putenv --
  646.  *    Put a value into the environment.
  647.  */
  648. static int
  649. cl_putenv(name, str, value)
  650.     char *name, *str;
  651.     u_long value;
  652.  
  653. {
  654.     char buf[40];
  655.  
  656.     if (str == NULL) {
  657.         (void)snprintf(buf, sizeof(buf), "%lu", value);
  658.         return (setenv(name, buf, 1));
  659.     } else
  660.         return (setenv(name, str, 1));
  661. }
  662.