home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / vile-src.zip / vile-8.1 / random.c < prev    next >
C/C++ Source or Header  |  1998-04-28  |  21KB  |  1,112 lines

  1. /*
  2.  * This file contains the command processing functions for a number of random
  3.  * commands. There is no functional grouping here, for sure.
  4.  *
  5.  * $Header: /usr/build/vile/vile/RCS/random.c,v 1.191 1998/04/28 10:18:29 tom Exp $
  6.  *
  7.  */
  8.  
  9. #ifdef _WIN32
  10. # include <windows.h>
  11. #endif
  12.  
  13. #include    "estruct.h"
  14. #include    "edef.h"
  15. #include    "nefunc.h"
  16.  
  17. #if SYS_UNIX
  18. #if HAVE_POLL && HAVE_POLL_H
  19. # include <poll.h>
  20. #endif
  21. #endif
  22.  
  23. #include    "dirstuff.h"
  24.  
  25. #if CC_TURBO
  26. #include <dir.h>    /* for 'chdir()' */
  27. #endif
  28.  
  29. #if SYS_VMS
  30. #include <starlet.h>
  31. #include <lib$routines.h>
  32. #endif
  33.  
  34. /*--------------------------------------------------------------------------*/
  35.  
  36. /*
  37.  * Set default parameters for an automatically-generated, view-only buffer.
  38.  * This is invoked after buffer creation, but usually before the buffer is
  39.  * loaded with text.
  40.  * the "mode" argument should be MDVIEW or MDREADONLY
  41.  */
  42. void
  43. set_rdonly(BUFFER *bp, const char *name, int mode)
  44. {
  45.     ch_fname(bp, name);
  46.  
  47.     b_clr_changed(bp);        /* assumes text is loaded... */
  48.     bp->b_active = TRUE;
  49.  
  50.     make_local_b_val(bp, mode);
  51.     set_b_val(bp,mode,TRUE);
  52.  
  53.     make_local_b_val(bp,VAL_TAB);
  54.     set_b_val(bp,VAL_TAB,8);
  55.  
  56.     make_local_b_val(bp,MDDOS);
  57.     set_b_val(bp, MDDOS, CRLF_LINES);
  58.  
  59.     fix_cmode(bp, FALSE);
  60. }
  61.  
  62. /* generic "lister", which takes care of popping a window/buffer pair under
  63.     the given name, and calling "func" with a couple of args to fill in
  64.     the buffer */
  65. int
  66. liststuff(
  67. const char *name,
  68. int appendit,
  69. void (*func) (LIST_ARGS),    /* ptr to function to execute */
  70. int iarg,
  71. void *vargp)
  72. {
  73.     register BUFFER *bp;
  74.     register int    s;
  75.     WINDOW  *wp;
  76.     int alreadypopped;
  77.     BUFFER *ocurbp = curbp;
  78.  
  79.     /* create the buffer list buffer   */
  80.     bp = bfind(name, BFSCRTCH);
  81.     if (bp == NULL)
  82.         return FALSE;
  83.  
  84.     if (!appendit && (s=bclear(bp)) != TRUE) /* clear old text (?) */
  85.         return (s);
  86.     b_set_scratch(bp);
  87.     alreadypopped = (bp->b_nwnd != 0);
  88.     if (popupbuff(bp) == FALSE) {
  89.         (void)zotbuf(bp);
  90.         return (FALSE);
  91.     }
  92.  
  93.     if ((wp = bp2any_wp(bp)) != NULL) {
  94.         make_local_w_val(wp,WMDNUMBER);
  95.         set_w_val(wp,WMDNUMBER,FALSE);
  96.     }
  97.     if (appendit) {
  98.         (void)gotoeob(FALSE,1);
  99.         MK = DOT;
  100.     }
  101.     /* call the passed in function, giving it both the integer and
  102.         character pointer arguments */
  103.     (*func)(iarg,vargp);
  104.     if (alreadypopped && appendit) {
  105.         (void)gomark(FALSE,1);
  106.         (void)forwbline(FALSE,1);
  107.         (void)reposition(FALSE,1);
  108.     } else { /* if we're not appending, go to the top */
  109.         (void)gotobob(FALSE,1);
  110.     }
  111.     set_rdonly(bp, non_filename(), MDVIEW);
  112.  
  113.     if (alreadypopped) /* don't switch to the popup if it wasn't there */
  114.         swbuffer(ocurbp);
  115.     else
  116.         shrinkwrap(); /* only resize if it's fresh */
  117.  
  118.     return TRUE;
  119. }
  120.  
  121. /*
  122.  * Display the current position of the cursor, lines and columns, in the file,
  123.  * the character that is under the cursor (in hex), and the fraction of the
  124.  * text that is before the cursor. The displayed column is not the current
  125.  * column, but the column that would be used on an infinite width display.
  126.  */
  127. /* ARGSUSED */
  128. int
  129. showcpos(int f GCC_UNUSED, int n GCC_UNUSED)
  130. {
  131.     register LINE    *lp;        /* current line */
  132.     register B_COUNT numchars = 0;    /* # of chars in file */
  133.     register L_NUM     numlines = 0;    /* # of lines in file */
  134.     register B_COUNT predchars = 0;    /* # chars preceding point */
  135.     register L_NUM     predlines = 0;    /* # lines preceding point */
  136.     register int    curchar = '\n';    /* character under cursor */
  137.     long ratio;
  138.     C_NUM col;
  139.     C_NUM savepos;            /* temp save for current offset */
  140.     C_NUM ecol;            /* column pos/end of current line */
  141.  
  142. #if CC_WATCOM || CC_DJGPP /* for testing interrupts */
  143.     if (f && n == 11) {
  144.         mlwrite("DOS interrupt test.  hit control-C or control-BREAK");
  145.         while (!interrupted())
  146.             ;
  147.         mlwrite("whew.  got interrupted");
  148.         return ABORT;
  149.     }
  150. #endif
  151. #if debug_undo_log
  152.     extern int do_undolog;
  153.     if (f && n == 11)
  154.         do_undolog = !do_undolog;
  155. #endif
  156.     /* count chars and lines */
  157.     for_each_line(lp, curbp) {
  158.         /* if we are on the current line, record it */
  159.         if (lp == DOT.l) {
  160.             predlines = numlines;
  161.             predchars = numchars + DOT.o;
  162.             if (DOT.o == llength(lp))
  163.                 curchar = '\n';
  164.             else
  165.                 curchar = char_at(DOT);
  166.         }
  167.         /* on to the next line */
  168.         ++numlines;
  169.         numchars += llength(lp) + 1;
  170.     }
  171.  
  172.     if (!b_val(curbp,MDNEWLINE))
  173.         numchars--;
  174.  
  175.     /* if at end of file, record it */
  176.     if (is_header_line(DOT,curbp)) {
  177.         predlines = numlines;
  178.         predchars = numchars;
  179.     }
  180.  
  181.     /* Get real column and end-of-line column. */
  182.     col = mk_to_vcol(DOT, FALSE, 0);
  183.     savepos = DOT.o;
  184.     DOT.o = llength(DOT.l);
  185.     ecol = mk_to_vcol(DOT, FALSE, 0);
  186.     DOT.o = savepos;
  187.  
  188.     ratio = 0;        /* Ratio before dot. */
  189.     if (numchars != 0)
  190.         ratio = (100L*predchars) / numchars;
  191.  
  192.     /* summarize and report the info */
  193.     mlforce(
  194. "Line %d of %d, Col %d of %d, Char %D of %D (%D%%) char is 0x%x or 0%o",
  195.         predlines+1, numlines, col+1, ecol,
  196.         predchars+1, numchars, ratio, curchar, curchar);
  197.     return TRUE;
  198. }
  199.  
  200. /* ARGSUSED */
  201. int
  202. showlength(int f GCC_UNUSED, int n GCC_UNUSED)
  203. {
  204.     /* actually, can be used to show any address-value */
  205.     mlforce("%d", line_no(curbp, MK.l));
  206.     return TRUE;
  207. }
  208.  
  209. int
  210. line_report(L_NUM before)
  211. {
  212.     L_NUM    after = line_count(curbp);
  213.  
  214.     if (do_report(before-after)) {
  215.         if (before > after)
  216.             mlwrite("[%d fewer lines]", before - after);
  217.         else
  218.             mlwrite("[%d more lines]", after - before);
  219.         return TRUE;
  220.     }
  221.     return FALSE;
  222. }
  223.  
  224. L_NUM
  225. line_count(BUFFER *the_buffer)
  226. {
  227.     if (the_buffer == (BUFFER *) 0)
  228.     return 0;
  229.     else {
  230. #if !SMALLER
  231.     (void)bsizes(the_buffer);
  232.     return the_buffer->b_linecount;
  233. #else
  234.     register LINE    *lp;        /* current line */
  235.     register L_NUM    numlines = 0;    /* # of lines in file */
  236.  
  237.     for_each_line(lp, the_buffer)
  238.         ++numlines;
  239.  
  240.     return numlines;
  241. #endif
  242.     }
  243. }
  244.  
  245. L_NUM
  246. line_no(        /* return the number of the given line */
  247. BUFFER *the_buffer,
  248. LINEPTR the_line)
  249. {
  250.     if (the_line != null_ptr) {
  251. #if !SMALLER
  252.         L_NUM    it;
  253.         (void)bsizes(the_buffer);
  254.         if ((it = the_line->l_number) == 0)
  255.             it = the_buffer->b_linecount + 1;
  256.         return it;
  257. #else
  258.         register LINE    *lp;        /* current line */
  259.         register L_NUM    numlines = 0;    /* # of lines before point */
  260.  
  261.         for_each_line(lp, the_buffer) {
  262.             /* if we are on the specified line, record it */
  263.             if (lp == the_line)
  264.                 break;
  265.             ++numlines;
  266.         }
  267.  
  268.         /* and return the resulting count */
  269.         return(numlines + 1);
  270. #endif
  271.     }
  272.     return 0;
  273. }
  274.  
  275. #if OPT_EVAL
  276. L_NUM
  277. getcline(void)    /* get the current line number */
  278. {
  279.     return line_no(curbp, DOT.l);
  280. }
  281. #endif
  282.  
  283. /*
  284.  * Return the screen column in any line given an offset.
  285.  * Assume the line is in curwp/curbp
  286.  */
  287. int
  288. getcol(
  289. MARK mark,
  290. int actual)    /* false: effective column (expand tabs) */
  291.         /* true: compute the actual column (depends on 'list' mode)*/
  292. {
  293.     register C_NUM c, i;
  294.     register C_NUM col = 0;
  295.  
  296.     if (llength(mark.l) > 0) {
  297.         if (actual) {
  298.             col = offs2col(curwp, mark.l, mark.o) - nu_width(curwp);
  299.         } else {
  300.             C_NUM len = mark.o;
  301.             if (len > llength(mark.l))
  302.                 len = llength(mark.l);
  303.             for (i = w_left_margin(curwp); i < len; ++i) {
  304.                 c = lgetc(mark.l, i);
  305.                 col = next_column(c,col); /* assumes curbp */
  306.             }
  307.         }
  308.     }
  309.     return col;
  310. }
  311.  
  312. /*
  313.  * Return current screen column.  Stop at first non-blank given TRUE argument.
  314.  */
  315. int
  316. getccol(int bflg)
  317. {
  318.     return getcol(DOT, bflg);
  319. }
  320.  
  321.  
  322. /*
  323.  * Set current column, based on counting from 1
  324.  */
  325. int
  326. gotocol(int f, int n)
  327. {
  328.     if (!f || n <= 0)
  329.         n = 1;
  330.     return gocol(n - 1);
  331. }
  332.  
  333. /* given a column, return the offset */
  334. /*  if there aren't that many columns, return how too few we were */
  335. /*    also, if non-null, return the column we _did_ reach in *rcolp */
  336. int
  337. getoff(
  338. C_NUM goal,
  339. C_NUM *rcolp)
  340. {
  341.     register C_NUM c;        /* character being scanned */
  342.     register C_NUM i;        /* index into current line */
  343.     register C_NUM col;    /* current cursor column   */
  344.     register C_NUM llen;    /* length of line in bytes */
  345.  
  346.     col = 0;
  347.     llen = llength(DOT.l);
  348.  
  349.     /* scan the line until we are at or past the target column */
  350.     for (i = w_left_margin(curwp); i < llen; ++i) {
  351.         /* upon reaching the target, drop out */
  352.         if (col >= goal)
  353.             break;
  354.  
  355.         /* advance one character */
  356.         c = lgetc(DOT.l, i);
  357.         col = next_column(c,col);
  358.     }
  359.  
  360.     if (rcolp)
  361.         *rcolp = col;
  362.  
  363.     /* and tell whether we made it */
  364.     if (col >= goal)
  365.         return i;    /* we made it */
  366.     else
  367.         return col - goal; /* else how far short (in spaces) we were */
  368. }
  369.  
  370. /* really set column, based on counting from 0, for internal use */
  371. int
  372. gocol(int n)
  373. {
  374.     register int offs;    /* current cursor column   */
  375.  
  376.     offs = getoff(n, (C_NUM *)0);
  377.  
  378.     if (offs >= 0) {
  379.         DOT.o = offs;
  380.         return TRUE;
  381.     }
  382.  
  383.     DOT.o = llength(DOT.l) - 1;
  384.     return FALSE;
  385.  
  386. }
  387.  
  388.  
  389. #if ! SMALLER
  390. /*
  391.  * Twiddle the two characters on under and to the left of dot.  If dot is
  392.  * at the end of the line twiddle the two characters before it.  This fixes
  393.  * up a very common typo with a single stroke.  This always works within a
  394.  * line, so "WFEDIT" is good enough.
  395.  */
  396. /* ARGSUSED */
  397. int
  398. twiddle(int f GCC_UNUSED, int n GCC_UNUSED)
  399. {
  400.     MARK        dot;
  401.     register char    cl;
  402.     register char    cr;
  403.  
  404.     dot = DOT;
  405.     if (llength(dot.l) <= 1)
  406.         return FALSE;
  407.     if (is_at_end_of_line(dot))
  408.         --dot.o;
  409.     if (dot.o == 0)
  410.         dot.o = 1;
  411.     cr = char_at(dot);
  412.     --dot.o;
  413.     cl = char_at(dot);
  414.     copy_for_undo(dot.l);
  415.     put_char_at(dot, cr);
  416.     ++dot.o;
  417.     put_char_at(dot, cl);
  418.     chg_buff(curbp, WFEDIT);
  419.     return (TRUE);
  420. }
  421. #endif
  422.  
  423.  
  424.  
  425. #if OPT_AEDIT
  426. /*
  427.  * Force zero or more blank lines at dot.  no argument leaves no blanks.
  428.  * Lines will be deleted or added as needed to match the argument if it
  429.  * is given.
  430.  */
  431. int
  432. forceblank(int f, int n)
  433. {
  434.     register LINE    *lp1;
  435.     register LINE    *lp2 = 0;
  436.     B_COUNT nld;
  437.     B_COUNT n_arg;
  438.     C_NUM    nchar;
  439.     int s = TRUE;
  440.  
  441.     if (!f || n < 0)
  442.         n = 0;
  443.     n_arg = n;
  444.  
  445.     lp1 = DOT.l;
  446.     /* scan backward */
  447.     while (firstchar(lp1) < 0 &&
  448.             (lp2 = lback(lp1)) != buf_head(curbp))
  449.         lp1 = lp2;
  450.     lp2 = lp1;
  451.  
  452.     nld = 0;
  453.     nchar = 0;
  454.  
  455.     /* scan forward */
  456.     while ((lp2 = lforw(lp2)) != buf_head(curbp) &&
  457.             firstchar(lp2) < 0) {
  458.         ++nld;
  459.         if (nld > n_arg)
  460.             nchar += llength(lp2) + 1;
  461.     }
  462.  
  463.     DOT.l = lforw(lp1);
  464.     DOT.o = 0;
  465.  
  466.     if (n_arg == nld) {        /* things are just right */
  467.         /*EMPTY*/;
  468.     } else if (n_arg < nld) {    /* delete (nld - n_arg) lines */
  469.         DOT.l = lp2;
  470.         DOT.o = 0;
  471.         backchar(TRUE,nchar);
  472.         s = ldelete((B_COUNT)nchar, FALSE);
  473.     } else {             /* insert (n_arg - nld) lines */
  474.         n_arg = n_arg - nld;
  475.         while (s && n_arg--)
  476.             s = lnewline();
  477.     }
  478.  
  479.     /* scan forward */
  480.     while ((firstchar(DOT.l) < 0) &&
  481.             (DOT.l != buf_head(curbp)))
  482.         DOT.l = lforw(DOT.l);
  483.  
  484.     return s;
  485. }
  486.  
  487. #endif
  488.  
  489. /* '~' is the traditional vi flip: flip a char and advance one */
  490. int
  491. flipchar(int f, int n)
  492. {
  493.     int s;
  494.  
  495.     havemotion = &f_forwchar_to_eol;
  496.     s = operflip(f,n);
  497.     if (s != TRUE)
  498.         return s;
  499.     return forwchar_to_eol(f,n);
  500. }
  501.  
  502. /* 'x' is synonymous with 'd<space>' */
  503. int
  504. forwdelchar(int f, int n)
  505. {
  506.     havemotion = &f_forwchar_to_eol;
  507.     return operdel(f,n);
  508. }
  509.  
  510. /* 'X' is synonymous with 'd<backspace>' */
  511. int
  512. backdelchar(int f, int n)
  513. {
  514.     havemotion = &f_backchar_to_bol;
  515.     return operdel(f,n);
  516. }
  517.  
  518. /* 'D' is synonymous with 'd$' */
  519. /* ARGSUSED */
  520. int
  521. deltoeol(int f GCC_UNUSED, int n GCC_UNUSED)
  522. {
  523.     if (is_empty_line(DOT))
  524.         return TRUE;
  525.  
  526.     havemotion = &f_gotoeol;
  527.     return operdel(FALSE,1);
  528. }
  529.  
  530. /* 'C' is synonymous with 'c$' */
  531. int
  532. chgtoeol(int f, int n)
  533. {
  534.     if (is_empty_line(DOT)) {
  535.         return ins();
  536.     } else {
  537.         havemotion = &f_gotoeol;
  538.         return operchg(f,n);
  539.     }
  540. }
  541.  
  542. /* 'Y' is synonymous with 'yy' */
  543. int
  544. yankline(int f, int n)
  545. {
  546.     havemotion = &f_godotplus;
  547.     return(operyank(f,n));
  548. }
  549.  
  550. /* 'S' is synonymous with 'cc' */
  551. int
  552. chgline(int f, int n)
  553. {
  554.     havemotion = &f_godotplus;
  555.     return(operchg(f,n));
  556. }
  557.  
  558. /* 's' is synonymous with 'c<space>' */
  559. int
  560. chgchar(int f, int n)
  561. {
  562.     havemotion = &f_forwchar_to_eol;
  563.     return(operchg(f,n));
  564. }
  565.  
  566.  
  567.  
  568. /*    This function simply clears the message line,
  569.         mainly for macro usage            */
  570.  
  571. /* ARGSUSED */
  572. int
  573. clrmes(int f GCC_UNUSED, int n GCC_UNUSED)
  574. {
  575.     mlerase();
  576.     return(TRUE);
  577. }
  578.  
  579. #if ! SMALLER
  580.  
  581. /*    This function writes a string on the message line
  582.         mainly for macro usage            */
  583.  
  584. /* ARGSUSED */
  585. int
  586. writemsg(int f GCC_UNUSED, int n GCC_UNUSED)
  587. {
  588.     register int status;
  589.     char buf[NPAT];        /* buffer to receive message into */
  590.  
  591.     buf[0] = EOS;
  592.     if ((status = mlreply("Message to write: ", buf, sizeof(buf))) != TRUE)
  593.         return(status);
  594.  
  595.     /* write the message out */
  596.     mlforce("%s",buf);
  597.     return(TRUE);
  598. }
  599.  
  600. /* ARGSUSED */
  601. int
  602. userbeep(int f GCC_UNUSED, int n GCC_UNUSED)
  603. {
  604.     TTbeep();
  605.     return TRUE;
  606. }
  607. #endif /* !SMALLER */
  608.  
  609.  
  610. /* delay for the given number of milliseconds.  if "watchinput" is true,
  611.     then user input will abort the delay */
  612. void
  613. catnap(int milli, int watchinput)
  614. {
  615.     if (milli == 0)
  616.         return;
  617. #if DISP_X11
  618.     if (watchinput)
  619.     x_typahead(milli);
  620.     else
  621. #endif
  622.     {
  623. #if SYS_UNIX
  624. # if HAVE_SELECT
  625.  
  626.     struct timeval tval;
  627.     fd_set read_bits;
  628. #  if HAVE_SIGPROCMASK
  629.     sigset_t newset, oldset;
  630.     sigemptyset(&newset);
  631.     sigaddset(&newset, SIGALRM);
  632.     sigprocmask(SIG_BLOCK, &newset, &oldset);
  633. #  endif
  634.  
  635.     FD_ZERO(&read_bits);
  636.     if (watchinput) {
  637.         FD_SET(0, &read_bits);
  638.     }
  639.     tval.tv_sec = milli / 1000;
  640.     tval.tv_usec = (milli % 1000) * 1000;    /* microseconds */
  641.     (void)select (1, &read_bits, (fd_set*)0, (fd_set*)0, &tval);
  642.  
  643. #  if HAVE_SIGPROCMASK
  644.     sigprocmask(SIG_SETMASK, &oldset, (sigset_t*)0);
  645. #  endif
  646.  
  647. # else
  648. #  if HAVE_POLL && HAVE_POLL_H
  649.  
  650.     struct pollfd pfd;
  651.     int fdcnt;
  652.     if (watchinput) {
  653.         pfd.fd = 0;
  654.         pfd.events = POLLIN;
  655.         fdcnt = 1;
  656.     } else {
  657.         fdcnt = 0;
  658.     }
  659.     (void)poll(&pfd, fdcnt, milli); /* milliseconds */
  660.  
  661. #  else
  662.  
  663.     int seconds = (milli + 999) / 1000;
  664.     if (watchinput)  {
  665.         while(seconds > 0) {
  666.             sleep(1);
  667.             if (TTtypahead())
  668.                 return;
  669.             seconds -= 1;
  670.         }
  671.     } else {
  672.         sleep(seconds);
  673.     }
  674.  
  675. #  endif
  676. # endif
  677. #define have_slept 1
  678. #endif
  679.  
  680. #if SYS_VMS
  681.     float    seconds = milli/1000.;
  682.     if (watchinput)  {
  683.         float tenth = .1;
  684.         while(seconds > 0.1) {
  685.             lib$wait(&tenth);
  686.             if (TTtypahead())
  687.                 return;
  688.             seconds -= tenth;
  689.         }
  690.     }
  691.     lib$wait(&seconds);
  692. #define have_slept 1
  693. #endif
  694.  
  695. #if SYS_WINNT || (CC_NEWDOSCC && SYS_MSDOS) || (CC_WATCOM && (SYS_OS2 || SYS_MSDOS))
  696. #if SYS_WINNT
  697. #define delay(a) Sleep(a)
  698. #endif
  699.     if (watchinput)  {
  700.         while(milli > 100) {
  701.             delay(100);
  702.             if (TTtypahead())
  703.                 return;
  704.             milli -= 100;
  705.         }
  706.     }
  707.     delay(milli);
  708. #define have_slept 1
  709. #endif
  710.  
  711. #ifndef have_slept
  712.     long i;
  713.     for (i = 0; i < term.t_pause; i++)
  714.         ;
  715. #endif
  716.     }
  717. }
  718.  
  719. static char    current_dirname[NFILEN];
  720.  
  721. /* Return a string naming the current directory.  If we're unable to read the
  722.  * directory name, return "."; otherwise we'll expect a fully-resolved path.
  723.  */
  724. char *
  725. current_directory(int force)
  726. {
  727.     char *s;
  728.     static char *cwd;
  729.  
  730.     if (!force && cwd)
  731.         return cwd;
  732. #if HAVE_GETCWD
  733.     cwd = getcwd(current_dirname, NFILEN);
  734. #else
  735. # if HAVE_GETWD
  736.     cwd = getwd(current_dirname);
  737. # else
  738.     {
  739.         FILE *f;
  740.         int n;
  741.         f = npopen("pwd", "r");
  742.         if (f != NULL) {
  743.             n = fread(current_dirname, 1, NFILEN, f);
  744.             current_dirname[n] = EOS;
  745.             npclose(f);
  746.             cwd = current_dirname;
  747.         } else
  748.             cwd = 0;
  749.     }
  750. # endif
  751. #endif
  752.     if (cwd == NULL) {
  753.         cwd = current_dirname;
  754.         current_dirname[0] = '.';
  755.         current_dirname[1] = EOS;
  756.     } else {
  757.         s = strchr(cwd, '\n');
  758.         if (s)
  759.             *s = EOS;
  760.     }
  761. #if OPT_MSDOS_PATH
  762.     lengthen_path(cwd);
  763. #if !OPT_CASELESS
  764.     mklower(cwd);
  765. #endif
  766. #endif
  767.  
  768. #if SYS_MSDOS || SYS_OS2
  769.     update_dos_drv_dir(cwd);
  770. #endif
  771.  
  772.     TRACE(("current_directory(%s)\n", cwd))
  773.     return cwd;
  774. }
  775.  
  776. #if SYS_MSDOS || SYS_OS2
  777.  
  778. /* convert drive index to _letter_ */
  779. static int
  780. drive2char(unsigned d)
  781. {
  782.     if (d >= 26) {
  783.         mlforce("[Illegal drive index %d]", d);
  784.         d = 0;
  785.     }
  786.     return (d + 'A');
  787. }
  788.  
  789. /* convert drive _letter_ to index */
  790. static unsigned
  791. char2drive(int d)
  792. {
  793.     if (isAlpha(d)) {
  794.         if (isLower(d))
  795.             d = toUpper(d);
  796.     } else {
  797.         mlforce("[Not a drive '%c']", d);
  798.         d = curdrive();
  799.     }
  800.     return (d - 'A');
  801. }
  802.  
  803. /* returns drive _letter_ */
  804. int
  805. curdrive(void)
  806. {
  807. #if SYS_OS2
  808.     unsigned d;
  809. # if CC_CSETPP
  810.     d = _getdrive();
  811. # else
  812.     _dos_getdrive(&d);      /* A=1 B=2 ... */
  813. # endif
  814.     return drive2char((d-1) & 0xff);
  815. #else
  816.     return drive2char(bdos(0x19, 0, 0) & 0xff);
  817. #endif
  818. }
  819.  
  820. /* take drive _letter_ as arg. */
  821. int
  822. setdrive(int d)
  823. {
  824.     if (isAlpha(d)) {
  825. #if SYS_OS2
  826. # if CC_CSETPP
  827.         _chdrive(char2drive(d) + 1);
  828. # else
  829.         unsigned maxdrives;
  830.         _dos_setdrive(char2drive(d)+1, &maxdrives); /* 1 based */
  831. # endif
  832. #else
  833.         bdos(0x0e, char2drive(d), 0); /* 0 based */
  834. #endif
  835.         return TRUE;
  836.     }
  837.     mlforce("[Bad drive specifier]");
  838.     return FALSE;
  839. }
  840.  
  841.  
  842. static int curd;        /* current drive-letter */
  843. static char *cwds[26];        /* list of current dirs on each drive */
  844.  
  845. const char *
  846. curr_dir_on_drive(int drive)
  847. {
  848.     int    n = char2drive(drive);
  849.  
  850.     if (n != 0) {
  851.         if (curd == 0)
  852.             curd = curdrive();
  853.  
  854.         if (cwds[n])
  855.             return cwds[n];
  856.         else {
  857.             cwds[n] = castalloc(char,NFILEN);
  858.  
  859.             if (cwds[n]) {
  860.                 if (setdrive(drive) == TRUE) {
  861.                     (void)strcpy(cwds[n], current_directory(TRUE));
  862.                     (void)setdrive(curd);
  863.                     (void)current_directory(TRUE);
  864.                     return cwds[n];
  865.                 }
  866.             }
  867.         }
  868.     }
  869.     return current_directory(FALSE);
  870. }
  871.  
  872. void
  873. update_dos_drv_dir(char *cwd)
  874. {
  875.     char    *s;
  876.  
  877.     if ((s = is_msdos_drive(cwd)) != 0) {
  878.         int n = char2drive(*cwd);
  879.  
  880.         if (!cwds[n])
  881.             cwds[n] = castalloc(char,NFILEN);
  882.  
  883.         if (cwds[n])
  884.             (void)strcpy(cwds[n], s);
  885.     }
  886. }
  887. #endif
  888.  
  889. #if OPT_SHELL
  890. /* ARGSUSED */
  891. int
  892. cd(int f GCC_UNUSED, int n GCC_UNUSED)
  893. {
  894.     register int status;
  895.     static    TBUFF    *last;
  896.     char cdirname[NFILEN];
  897.  
  898.     status = mlreply_dir("Change to directory: ", &last, cdirname);
  899. #if SYS_UNIX || SYS_VMS
  900.     if (status == FALSE) {        /* empty reply, go HOME */
  901.         (void)strcpy(cdirname, "~");
  902.         status = TRUE;
  903.     }
  904. #endif
  905.     if (status == TRUE)
  906.         status = set_directory(cdirname);
  907.     return status;
  908. }
  909.  
  910. /* ARGSUSED */
  911. int
  912. pwd(int f GCC_UNUSED, int n GCC_UNUSED)
  913. {
  914.     mlforce("%s",current_directory(f));
  915.     return TRUE;
  916. }
  917.  
  918. static char prevdir[NFILEN];
  919.  
  920. static int
  921. cd_and_pwd(char *path)
  922. {
  923. #if OPT_PROCEDURES
  924.     static int cdhooking;
  925. #endif
  926. #if CC_CSETPP
  927.     if (_chdir(SL_TO_BSL(path)) == 0)
  928. #else
  929.     if (chdir(SL_TO_BSL(path)) == 0)
  930. #endif
  931.     {
  932. #if SYS_UNIX
  933.         const char *p = current_directory(TRUE);
  934.         if (!is_slashc(*p)) {
  935.             if (is_slashc(*prevdir))
  936.                 p = lengthen_path(pathcat(current_dirname, prevdir, path));
  937.             sgarbf = TRUE;    /* some shells print to stderr */
  938.             update(TRUE);
  939.         }
  940.  
  941.         mlforce("%s", p);
  942. #else
  943.         (void)pwd(TRUE,1);
  944. #endif
  945. #if OPT_PROCEDURES
  946.         if (!cdhooking && *cdhook) {
  947.             cdhooking = TRUE;
  948.             run_procedure(cdhook);
  949.             cdhooking = FALSE;
  950.         }
  951. #endif
  952.         updatelistbuffers();
  953.         return TRUE;
  954.     }
  955.     return FALSE;
  956. }
  957.  
  958. #if OPT_EVAL
  959. char *
  960. previous_directory(void)
  961. {
  962.     if (*prevdir)
  963.         return prevdir;
  964.     else
  965.         return current_directory(FALSE);
  966. }
  967. #endif
  968.  
  969. /* move to the named directory.  (Dave Lemke) */
  970. int
  971. set_directory(const char *dir)
  972. {
  973.     char       exdir[NFILEN];
  974. #if SYS_UNIX
  975.     const char *cdpath = 0;
  976.     char       cdpathdir[NFILEN];
  977. #endif
  978.     char *exdp;
  979. #if SYS_MSDOS || SYS_OS2
  980.     int curd = curdrive();
  981. #endif
  982.  
  983.     upmode();
  984.  
  985.     TRACE(("set_directory(%s)\n", dir))
  986.     exdp = strcpy(exdir, dir);
  987.  
  988.     if (doglob(exdp)) {
  989. #if SYS_MSDOS || SYS_OS2
  990.     char    *s;
  991.     if ((s = is_msdos_drive(exdp)) != 0) {
  992.         if (setdrive(*exdp) == TRUE) {
  993.             exdp = s;    /* skip device-part */
  994.             if (!*exdp) {
  995.                 return pwd(TRUE,1);
  996.             }
  997.         } else {
  998.             return FALSE;
  999.         }
  1000.     }
  1001. #endif
  1002.     /*
  1003.     ** "cd -" switches to the previous directory.
  1004.     */
  1005.     if (!strcmp(exdp, "-"))
  1006.     {
  1007.         if (*prevdir)
  1008.             (void) strcpy(exdp, prevdir);
  1009.         else
  1010.         {
  1011.             mlforce("[No previous directory");
  1012.             return FALSE;
  1013.         }
  1014.     }
  1015.  
  1016.     /* Save current directory for subsequent "cd -". */
  1017.     (void) strcpy(prevdir, current_directory(FALSE));
  1018.  
  1019. #if OPT_VMS_PATH
  1020.     if (!*exdp)
  1021.         strcpy(exdp, "~");
  1022.     if (!strcmp(exdp, "/"))        /* cannot really "cd /" on vms */
  1023.         lengthen_path(exdp);
  1024.     if (!is_vms_pathname(exdp,TRUE)) {
  1025.         int end = strlen(exdp);
  1026.         if (exdp[end-1] != SLASHC) {
  1027.             exdp[end++]  = SLASHC;
  1028.             exdp[end]    = EOS;
  1029.         }
  1030.         unix2vms_path(exdp,exdp);
  1031.     }
  1032. #endif
  1033.  
  1034.     if (cd_and_pwd(exdp))
  1035.         return TRUE;
  1036.  
  1037. #if SYS_UNIX && OPT_PATHLOOKUP
  1038.     /*
  1039.     ** chdir failed.  If the directory name doesn't begin with any of
  1040.     ** "/", "./", or "../", get the CDPATH environment variable and check
  1041.     ** if the specified directory name is a subdirectory of a
  1042.     ** directory in CDPATH.
  1043.     */
  1044.     if (!is_pathname(exdp)
  1045.      && (cdpath = getenv("CDPATH")) != 0) {
  1046.         /* For each colon-separated component in CDPATH */
  1047.         while ((cdpath = parse_pathlist(cdpath, cdpathdir)) != 0) {
  1048.             if (cd_and_pwd(pathcat(cdpathdir, cdpathdir, exdp))) {
  1049.                 return TRUE;
  1050.             }
  1051.         }
  1052.     }
  1053. #endif
  1054.     }
  1055. #if SYS_MSDOS || SYS_OS2
  1056.     setdrive(curd);
  1057.     current_directory(TRUE);
  1058. #endif
  1059.     mlforce("[Couldn't change to \"%s\"]", exdir);
  1060.     return FALSE;
  1061. }
  1062. #endif /* OPT_SHELL */
  1063.  
  1064. void
  1065. ch_fname(BUFFER *bp, const char *fname)
  1066. {
  1067.     int len;
  1068.     char nfilen[NFILEN];
  1069.     char *np;
  1070.     char *holdp = NULL;
  1071.  
  1072.     np = strcpy(nfilen, fname);
  1073.  
  1074.     /* produce a full pathname, unless already absolute or "internal" */
  1075.     if (!isInternalName(np))
  1076.         (void) lengthen_path(nfilen);
  1077.  
  1078.     len = strlen(np)+1;
  1079.  
  1080.     if (bp->b_fname == 0 || strcmp(bp->b_fname, np)) {
  1081.  
  1082.         if (bp->b_fname && bp->b_fnlen < len ) {
  1083.             /* don't free it yet -- it _may_ have been passed in as
  1084.              * the current file-name
  1085.              */
  1086.             holdp = bp->b_fname;
  1087.             bp->b_fname = NULL;
  1088.         }
  1089.  
  1090.         if (!bp->b_fname) {
  1091.             bp->b_fname = strmalloc(np);
  1092.             if (!bp->b_fname) {
  1093.                 bp->b_fname = out_of_mem;
  1094.                 bp->b_fnlen = strlen(bp->b_fname);
  1095.                 return;
  1096.             }
  1097.             bp->b_fnlen = len;
  1098.         }
  1099.  
  1100.         /* it'll fit, leave len untouched */
  1101.         (void)strcpy(bp->b_fname, np);
  1102.  
  1103.         if (holdp != out_of_mem)
  1104.             FreeIfNeeded(holdp);
  1105.         updatelistbuffers();
  1106.     }
  1107. #ifdef    MDCHK_MODTIME
  1108.     (void)get_modtime(bp, &(bp->b_modtime));
  1109.     bp->b_modtime_at_warn = 0;
  1110. #endif
  1111. }
  1112.