home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 5 / FreshFish_July-August1994.bin / bbs / util / vim-2.0.lha / Vim-2.0 / src / misccmds.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-15  |  16.1 KB  |  864 lines

  1. /* vi:ts=4:sw=4
  2.  *
  3.  * VIM - Vi IMproved
  4.  *
  5.  * Code Contributions By:    Bram Moolenaar            mool@oce.nl
  6.  *                            Tim Thompson            twitch!tjt
  7.  *                            Tony Andrews            onecom!wldrdg!tony 
  8.  *                            G. R. (Fred) Walter        watmath!watcgl!grwalter 
  9.  */
  10.  
  11. /*
  12.  * misccmds.c: functions that didn't seem to fit elsewhere
  13.  */
  14.  
  15. #include "vim.h"
  16. #include "globals.h"
  17. #include "proto.h"
  18. #include "param.h"
  19.  
  20. static char *(si_tab[]) = {"if", "else", "while", "for", "do"};
  21.  
  22. /*
  23.  * count the size of the indent in the current line
  24.  */
  25.     int
  26. get_indent()
  27. {
  28.     register char *ptr;
  29.     register int count = 0;
  30.  
  31.     for (ptr = nr2ptr(Curpos.lnum); *ptr; ++ptr)
  32.     {
  33.         if (*ptr == TAB)    /* count a tab for what it is worth */
  34.             count += (int)p_ts - (count % (int)p_ts);
  35.         else if (*ptr == ' ')
  36.             ++count;            /* count a space for one */
  37.         else
  38.             break;
  39.     }
  40.     return (count);
  41. }
  42.  
  43. /*
  44.  * set the indent of the current line
  45.  * leaves the cursor on the first non-blank in the line
  46.  */
  47.     void
  48. set_indent(size, delete)
  49.     register int size;
  50.     int delete;
  51. {
  52.     int        oldstate = State;
  53.  
  54.     State = INSERT;        /* don't want REPLACE for State */
  55.     Curpos.col = 0;
  56.     if (delete)
  57.     {
  58.         while (isspace(gcharCurpos()))    /* delete old indent */
  59.             delchar(FALSE);
  60.     }
  61.     if (!p_et)            /* if 'expandtab' is set, don't use TABs */
  62.         while (size >= (int)p_ts)
  63.         {
  64.             inschar(TAB);
  65.             size -= (int)p_ts;
  66.         }
  67.     while (size)
  68.     {
  69.         inschar(' ');
  70.         --size;
  71.     }
  72.     State = oldstate;
  73.     script_winsize_pp();
  74. }
  75.  
  76. /*
  77.  * Opencmd
  78.  *
  79.  * Add a blank line below or above the current line.
  80.  */
  81.  
  82.     int
  83. Opencmd(dir, redraw, delspaces)
  84.     int         dir;
  85.     int            redraw;
  86.     int            delspaces;
  87. {
  88.     char   *l;
  89.     char   *ptr, *pp;
  90.     FPOS    oldCurpos;             /* old cursor position */
  91.     int        newcol = 0;            /* new cursor column */
  92.     int     newindent = 0;        /* auto-indent of the new line */
  93.     int     extra = 0;            /* number of bytes to be copied from current line */
  94.     int        n;
  95.     int        truncate = FALSE;    /* truncate current line afterwards */
  96.     int        no_si = FALSE;        /* reset did_si afterwards */
  97.  
  98.     ptr = nr2ptr(Curpos.lnum);
  99.     u_clearline();        /* cannot do "U" command when adding lines */
  100.     did_si = FALSE;
  101.     if (p_ai || p_si)
  102.     {
  103.         /*
  104.          * count white space on current line
  105.          */
  106.         newindent = get_indent();
  107.         if (newindent == 0)
  108.             newindent = old_indent;        /* for ^^D command in insert mode */
  109.         old_indent = 0;
  110.  
  111.             /*
  112.              * If we just did an auto-indent, then we didn't type anything on the
  113.              * prior line, and it should be truncated.
  114.              */
  115.         if (dir == FORWARD && did_ai)
  116.             truncate = TRUE;
  117.         else if (p_si && *ptr != NUL)
  118.         {
  119.             char    *p;
  120.             char    *pp;
  121.             int        i, save;
  122.  
  123.             if (dir == FORWARD)
  124.             {
  125.                 p = ptr + strlen(ptr) - 1;
  126.                 while (p > ptr && isspace(*p))    /* find last non-blank in line */
  127.                     --p;
  128.                 if (*p == '{')                    /* line ends in '{': do indent */
  129.                 {
  130.                     did_si = TRUE;
  131.                     no_si = TRUE;
  132.                 }
  133.                 else                            /* look for "if" and the like */
  134.                 {
  135.                     p = ptr;
  136.                     skipspace(&p);
  137.                     for (pp = p; islower(*pp); ++pp)
  138.                         ;
  139.                     if (!isidchar(*pp))            /* careful for vars starting with "if" */
  140.                     {
  141.                         save = *pp;
  142.                         *pp = NUL;
  143.                         for (i = sizeof(si_tab)/sizeof(char *); --i >= 0; )
  144.                             if (strcmp(p, si_tab[i]) == 0)
  145.                             {
  146.                                 did_si = TRUE;
  147.                                 break;
  148.                             }
  149.                         *pp = save;
  150.                     }
  151.                 }
  152.             }
  153.             else
  154.             {
  155.                 p = ptr;
  156.                 skipspace(&p);
  157.                 if (*p == '}')            /* if line starts with '}': do indent */
  158.                     did_si = TRUE;
  159.             }
  160.         }
  161.         did_ai = TRUE;
  162.         if (p_si)
  163.             can_si = TRUE;
  164.     }
  165.     if (State == INSERT || State == REPLACE)    /* only when dir == FORWARD */
  166.     {
  167.         pp = ptr + Curpos.col;
  168.         if (p_ai && delspaces)
  169.             skipspace(&pp);
  170.         extra = strlen(pp);
  171.     }
  172.     if ((l = alloc_line(extra)) == NULL)
  173.         return (FALSE);
  174.     if (extra)
  175.     {
  176.         strcpy(l, pp);
  177.         did_ai = FALSE;         /* don't trucate now */
  178.     }
  179.  
  180.     oldCurpos = Curpos;
  181.     if (dir == BACKWARD)
  182.         --Curpos.lnum;
  183.     if (appendline(Curpos.lnum, l) == FALSE)
  184.         return FALSE;
  185.     if (newindent || did_si)
  186.     {
  187.         ++Curpos.lnum;
  188.         if (did_si)
  189.         {
  190.             if (p_sr)
  191.                 newindent -= newindent % (int)p_sw;
  192.             newindent += (int)p_sw;
  193.         }
  194.         set_indent(newindent, FALSE);
  195.         newcol = Curpos.col;
  196.         if (no_si)
  197.             did_si = FALSE;
  198.     }
  199.     Curpos = oldCurpos;
  200.  
  201.     if (dir == FORWARD)
  202.     {
  203.         if (truncate || State == INSERT || State == REPLACE)
  204.         {
  205.             if (truncate)
  206.                 *ptr = NUL;
  207.             else
  208.                 *(ptr + Curpos.col) = NUL;    /* truncate current line at cursor */
  209.             canincrease(0);
  210.         }
  211.  
  212.         /*
  213.          * Get the cursor to the start of the line, so that 'Cursrow' gets
  214.          * set to the right physical line number for the stuff that
  215.          * follows...
  216.          */
  217.         Curpos.col = 0;
  218.  
  219.         if (redraw)
  220.         {
  221.             cursupdate();
  222.  
  223.             /*
  224.              * If we're doing an open on the last logical line, then go ahead and
  225.              * scroll the screen up. Otherwise, just insert a blank line at the
  226.              * right place. We use calls to plines() in case the cursor is
  227.              * resting on a long line.
  228.              */
  229.             n = Cursrow + plines(Curpos.lnum);
  230.             if (n == (Rows - 1))
  231.                 scrollup(1L);
  232.             else
  233.                 s_ins(n, 1, TRUE);
  234.         }
  235.         ++Curpos.lnum;    /* cursor moves down */
  236.     }
  237.     else if (redraw)
  238.         s_ins(Cursrow, 1, TRUE); /* insert physical line */
  239.  
  240.     Curpos.col = newcol;
  241.     if (redraw)
  242.     {
  243.         updateScreen(VALID_TO_CURSCHAR);
  244.         cursupdate();            /* update Cursrow */
  245.     }
  246.     CHANGED;
  247.  
  248.     return (TRUE);
  249. }
  250.  
  251. /*
  252.  * plines(p) - return the number of physical screen lines taken by line 'p'
  253.  */
  254.     int
  255. plines(p)
  256.     linenr_t p;
  257. {
  258.     register int        col = 0;
  259.     register u_char        *s;
  260.  
  261.     if (!p_wrap)
  262.         return 1;
  263.  
  264.     s = (u_char *)nr2ptr(p);
  265.     if (*s == NUL)                /* empty line */
  266.         return 1;
  267.  
  268.     while (*s != NUL)
  269.         col += chartabsize(*s++, col);
  270.  
  271.     /*
  272.      * If list mode is on, then the '$' at the end of the line takes up one
  273.      * extra column.
  274.      */
  275.     if (p_list)
  276.         col += 1;
  277.  
  278.     /*
  279.      * If 'number' mode is on, add another 8.
  280.      */
  281.     if (p_nu)
  282.         col += 8;
  283.  
  284.     col = (col + ((int)Columns - 1)) / (int)Columns;
  285.     if (col < Rows)
  286.         return col;
  287.     return (int)(Rows - 1);        /* maximum length */
  288. }
  289.  
  290. /*
  291.  * Count the physical lines (rows) for the lines "first" to "last" inclusive.
  292.  */
  293.     int
  294. plines_m(first, last)
  295.     linenr_t        first, last;
  296. {
  297.         int count = 0;
  298.  
  299.         while (first <= last)
  300.                 count += plines(first++);
  301.         return (count);
  302. }
  303.  
  304.     void
  305. fileinfo(fullname)
  306.     int fullname;
  307. {
  308.     if (bufempty())
  309.     {
  310.         msg("Buffer Empty");
  311.         return;
  312.     }
  313.     sprintf(IObuff, "\"%s\"%s%s%s line %ld of %ld -- %d %% --",
  314.             (!fullname && sFilename != NULL) ? sFilename :
  315.                 ((Filename != NULL) ? Filename : "No File"),
  316.             Changed ? " [Modified]" : "",
  317.             NotEdited ? " [Not edited]" : "",
  318.             p_ro ? " [readonly]" : "",
  319.             (long)Curpos.lnum,
  320.             (long)line_count,
  321.             (int)(((long)Curpos.lnum * 100L) / (long)line_count));
  322.  
  323.     if (numfiles > 1)
  324.         sprintf(IObuff + strlen(IObuff), " (file %d of %d)", curfile + 1, numfiles);
  325.     msg(IObuff);
  326. }
  327.  
  328. /*
  329.  * Set the current file name to 's'.
  330.  * The file name with the full path is also remembered, for when :cd is used.
  331.  */
  332.     void
  333. setfname(s, ss)
  334.     char *s, *ss;
  335. {
  336.     free(Filename);
  337.     free(sFilename);
  338.     if (s == NULL || *s == NUL)
  339.     {
  340.         Filename = NULL;
  341.         sFilename = NULL;
  342.     }
  343.     else
  344.     {
  345.         if (ss == NULL)
  346.             ss = s;
  347.         sFilename = (char *)strsave(ss);
  348.         FullName(s, IObuff, IOSIZE);
  349.         Filename = (char *)strsave(IObuff);
  350.     }
  351.     if (did_cd)
  352.         xFilename = Filename;
  353.     else
  354.         xFilename = sFilename;
  355.  
  356. #ifndef MSDOS
  357.     thisfile_sn = FALSE;
  358. #endif
  359. }
  360.  
  361. /*
  362.  * return nonzero if "s" is not the same file as current file
  363.  */
  364.     int
  365. otherfile(s)
  366.     char *s;
  367. {
  368.     if (s == NULL || *s == NUL || Filename == NULL)        /* no name is different */
  369.         return TRUE;
  370.     FullName(s, IObuff, IOSIZE);
  371.     return fnamecmp(IObuff, Filename);
  372. }
  373.     
  374. /*
  375.  * put filename in title bar of window
  376.  */
  377.     void
  378. maketitle()
  379. {
  380. #ifdef AMIGA
  381.     if (Filename == NULL)
  382.         settitle("");
  383.     else
  384.     {
  385.         if (numfiles <= 1)
  386.             settitle(Filename);
  387.         else
  388.         {
  389.             sprintf(IObuff, "%s (%d of %d)", Filename, curfile + 1, numfiles);
  390.             settitle(IObuff);
  391.         }
  392.     }
  393. #endif
  394. }
  395.  
  396.     void
  397. inschar(c)
  398.     int            c;
  399. {
  400.     register char  *p;
  401.     int                rir0;        /* reverse replace in column 0 */
  402.  
  403.     p = Curpos2ptr();
  404.     rir0 = (State == REPLACE && p_ri && Curpos.col == 0);
  405.     if (rir0 || State != REPLACE || *p == NUL)
  406.     {
  407.             /* make room for the new char. */
  408.         if (!canincrease(1))    /* make room for the new char */
  409.             return;
  410.  
  411.         p = Curpos2ptr();        /* get p again, canincrease() may have changed it */
  412.         memmove(p + 1, p, strlen(p) + 1);    /* move following text and NUL */
  413.     }
  414.     if (rir0)                    /* reverse replace in column 0 */
  415.     {
  416.         *(p + 1) = c;            /* replace the char that was in column 0 */
  417.         c = ' ';                /* insert a space */
  418.         extraspace = TRUE;
  419.     }
  420.     *p = c;
  421.  
  422.     /*
  423.      * If we're in insert mode and showmatch mode is set, then check for
  424.      * right parens and braces. If there isn't a match, then beep. If there
  425.      * is a match AND it's on the screen, then flash to it briefly. If it
  426.      * isn't on the screen, don't do anything.
  427.      */
  428.     if (p_sm && State == INSERT && (c == ')' || c == '}' || c == ']'))
  429.     {
  430.         FPOS           *lpos, csave;
  431.  
  432.         if ((lpos = showmatch()) == NULL)        /* no match, so beep */
  433.             beep();
  434.         else if (lpos->lnum >= Topline)
  435.         {
  436.             updateScreen(VALID_TO_CURSCHAR); /* show the new char first */
  437.             csave = Curpos;
  438.             Curpos = *lpos;     /* move to matching char */
  439.             cursupdate();
  440.             showruler(0);
  441.             setcursor();
  442.             flushbuf();
  443.             vim_delay();        /* brief pause */
  444.             Curpos = csave;     /* restore cursor position */
  445.             cursupdate();
  446.         }
  447.     }
  448.     if (!p_ri)                            /* normal insert: cursor right */
  449.         ++Curpos.col;
  450.     else if (State == REPLACE && !rir0)    /* reverse replace mode: cursor left */
  451.         --Curpos.col;
  452.     CHANGED;
  453. }
  454.  
  455.     void
  456. insstr(s)
  457.     register char  *s;
  458. {
  459.     register char  *p;
  460.     register int    n = strlen(s);
  461.  
  462.     if (!canincrease(n))    /* make room for the new string */
  463.         return;
  464.  
  465.     p = Curpos2ptr();
  466.     memmove(p + n, p, strlen(p) + 1);
  467.     memmove(p, s, (size_t)n);
  468.     Curpos.col += n;
  469.     CHANGED;
  470. }
  471.  
  472.     int
  473. delchar(fixpos)
  474.     int            fixpos;     /* if TRUE fix the cursor position when done */
  475. {
  476.     char        *ptr;
  477.     int            lastchar;
  478.  
  479.     ptr = Curpos2ptr();
  480.  
  481.     if (*ptr == NUL)    /* can't do anything (happens with replace mode) */
  482.         return FALSE;
  483.  
  484.     lastchar = (*++ptr == NUL);
  485.     /* Delete the char. at Curpos by shifting everything in the line down. */
  486.     do
  487.         *(ptr - 1) = *ptr;
  488.     while (*ptr++);
  489.  
  490.     /*
  491.      * If we just took off the last character of a non-blank line, we don't
  492.      * want to end up positioned at the newline.
  493.      */
  494.     if (fixpos && Curpos.col > 0 && lastchar)
  495.         --Curpos.col;
  496.  
  497.     (void)canincrease(0);
  498.     CHANGED;
  499.     return TRUE;
  500. }
  501.  
  502.     void
  503. dellines(nlines, doscreen, undo)
  504.     long             nlines;            /* number of lines to delete */
  505.     int             doscreen;        /* if true, update the screen */
  506.     int                undo;            /* if true, prepare for undo */
  507. {
  508.     int             num_plines = 0;
  509.     char            *ptr;
  510.  
  511.     if (nlines <= 0)
  512.         return;
  513.     /*
  514.      * There's no point in keeping the screen updated if we're deleting more
  515.      * than a screen's worth of lines.
  516.      */
  517.     if (nlines > (Rows - 1 - Cursrow) && doscreen)
  518.     {
  519.         doscreen = FALSE;
  520.         /* flaky way to clear rest of screen */
  521.         s_del(Cursrow, (int)Rows - 1, TRUE);
  522.     }
  523.     if (undo && !u_savedel(Curpos.lnum, nlines))
  524.         return;
  525.     while (nlines-- > 0)
  526.     {
  527.         if (bufempty())         /* nothing to delete */
  528.             break;
  529.  
  530.         /*
  531.          * Set up to delete the correct number of physical lines on the
  532.          * screen
  533.          */
  534.         if (doscreen)
  535.             num_plines += plines(Curpos.lnum);
  536.  
  537.         ptr = delsline(Curpos.lnum, TRUE);
  538.         if (!undo)
  539.             free_line(ptr);
  540.  
  541.         CHANGED;
  542.  
  543.         /* If we delete the last line in the file, stop */
  544.         if (Curpos.lnum > line_count)
  545.         {
  546.             Curpos.lnum = line_count;
  547.             break;
  548.         }
  549.     }
  550.     Curpos.col = 0;
  551.     /*
  552.      * Delete the correct number of physical lines on the screen
  553.      */
  554.     if (doscreen && num_plines > 0)
  555.         s_del(Cursrow, num_plines, TRUE);
  556. }
  557.  
  558.     int
  559. gchar(pos)
  560.     FPOS *pos;
  561. {
  562.     return (int)(*(pos2ptr(pos)));
  563. }
  564.  
  565.     int
  566. gcharCurpos()
  567. {
  568.     return (int)(*(Curpos2ptr()));
  569. }
  570.  
  571.     void
  572. pcharCurpos(c)
  573.     int c;
  574. {
  575.     *(Curpos2ptr()) = c;
  576. }
  577.  
  578. /*
  579.  * return TRUE if the cursor is before or on the first non-blank in the line
  580.  */
  581.     int
  582. inindent()
  583. {
  584.     register char *ptr;
  585.     register int col;
  586.  
  587.     for (col = 0, ptr = nr2ptr(Curpos.lnum); isspace(*ptr++); ++col)
  588.         ;
  589.     if (col >= Curpos.col)
  590.         return TRUE;
  591.     else
  592.         return FALSE;
  593. }
  594.  
  595. /*
  596.  * skipspace: skip over ' ' and '\t'.
  597.  *
  598.  * note: you must give a pointer to a char pointer!
  599.  */
  600.     void
  601. skipspace(pp)
  602.     char **pp;
  603. {
  604.     register char *p;
  605.     
  606.     for (p = *pp; *p == ' ' || *p == '\t'; ++p)    /* skip to next non-white */
  607.         ;
  608.     *pp = p;
  609. }
  610.  
  611. /*
  612.  * skiptospace: skip over text until ' ' or '\t'.
  613.  *
  614.  * note: you must give a pointer to a char pointer!
  615.  */
  616.     void
  617. skiptospace(pp)
  618.     char **pp;
  619. {
  620.     register char *p;
  621.  
  622.     for (p = *pp; *p != ' ' && *p != '\t' && *p != NUL; ++p)
  623.         ;
  624.     *pp = p;
  625. }
  626.  
  627. /*
  628.  * skiptodigit: skip over text until digit found
  629.  *
  630.  * note: you must give a pointer to a char pointer!
  631.  */
  632.     void
  633. skiptodigit(pp)
  634.     char **pp;
  635. {
  636.     register char *p;
  637.  
  638.     for (p = *pp; !isdigit(*p) && *p != NUL; ++p)
  639.         ;
  640.     *pp = p;
  641. }
  642.  
  643. /*
  644.  * getdigits: get a number from a string and skip over it
  645.  *
  646.  * note: you must give a pointer to a char pointer!
  647.  */
  648.  
  649.     long
  650. getdigits(pp)
  651.     char **pp;
  652. {
  653.     register char *p;
  654.     long retval;
  655.     
  656.     p = *pp;
  657.     retval = atol(p);
  658.     while (isdigit(*p))    /* skip to next non-digit */
  659.         ++p;
  660.     *pp = p;
  661.     return retval;
  662. }
  663.  
  664.     char *
  665. plural(n)
  666.     long n;
  667. {
  668.     static char buf[2] = "s";
  669.  
  670.     if (n == 1)
  671.         return &(buf[1]);
  672.     return &(buf[0]);
  673. }
  674.  
  675. /*
  676.  * set_Changed is called whenever something in the file is changed
  677.  * If the file is readonly, give a warning message with the first change.
  678.  * Don't use emsg(), because it flushes the macro buffer.
  679.  */
  680.     void
  681. set_Changed()
  682. {
  683.     change_warning();
  684.     Changed = 1;
  685.     Updated = 1;
  686. }
  687.  
  688.     void
  689. change_warning()
  690. {
  691.     if (Changed == 0 && p_ro)
  692.     {
  693.         msg("Warning: Changing a readonly file");
  694.         sleep(1);            /* give him some time to think about it */
  695.     }
  696. }
  697.  
  698.     int
  699. ask_yesno(str)
  700.     char *str;
  701. {
  702.     int r = ' ';
  703.  
  704.     while (r != 'y' && r != 'n')
  705.     {
  706.         smsg("%s (y/n)? ", str);
  707.         r = vgetc();
  708.         if (r == Ctrl('C'))
  709.             r = 'n';
  710.         outchar(r);        /* show what you typed */
  711.         flushbuf();
  712.     }
  713.     return r;
  714. }
  715.  
  716.     void
  717. msgmore(n)
  718.     long n;
  719. {
  720.     long pn;
  721.  
  722.     if (n > 0)
  723.         pn = n;
  724.     else
  725.         pn = -n;
  726.  
  727.     if (pn > p_report)
  728.         smsg("%ld %s line%s %s", pn, n > 0 ? "more" : "fewer", plural(pn),
  729.                                             got_int ? "(Interrupted)" : "");
  730. }
  731.  
  732. /*
  733.  * give a warning for an error
  734.  */
  735.     void
  736. beep()
  737. {
  738.     flush_buffers(FALSE);        /* flush internal buffers */
  739.     if (p_vb)
  740.     {
  741.         if (T_VB && *T_VB)
  742.             outstr(T_VB);
  743.         else
  744.         {                        /* very primitive visual bell */
  745.             msg("    ^G");
  746.             msg("     ^G");
  747.             msg("    ^G ");
  748.             msg("     ^G");
  749.             msg("       ");
  750.             showmode();            /* may have deleted the mode message */
  751.         }
  752.     }
  753.     else
  754.         outchar('\007');
  755. }
  756.  
  757. /* 
  758.  * Expand environment variable with path name.
  759.  * If anything fails no expansion is done and dst equals src.
  760.  */
  761.     void
  762. expand_env(src, dst, dstlen)
  763.     char    *src;            /* input string e.g. "$HOME/vim.hlp" */
  764.     char    *dst;            /* where to put the result */
  765.     int        dstlen;            /* maximum length of the result */
  766. {
  767.     char    *tail;
  768.     int        c;
  769.     char    *var;
  770.  
  771.     if (*src == '$')
  772.     {
  773. /*
  774.  * The variable name is copied into dst temporarily, because it may be
  775.  * a string in read-only memory.
  776.  */
  777.         tail = src + 1;
  778.         var = dst;
  779.         c = dstlen - 1;
  780.         while (c-- > 0 && *tail && isidchar(*tail))
  781.             *var++ = *tail++;
  782.         *var = NUL;
  783. /*
  784.  * It is possible that vimgetenv() uses IObuff for the expansion, and that the
  785.  * 'dst' is also IObuff. This works, as long as 'var' is the first to be copied
  786.  * to 'dst'!
  787.  */
  788.         var = (char *)vimgetenv(dst);
  789.         if (var && (strlen(var) + strlen(tail) + 1 < (unsigned)dstlen))
  790.         {
  791.             strcpy(dst, var);
  792.             strcat(dst, tail);
  793.             return;
  794.         }
  795.     }
  796.     strncpy(dst, src, (size_t)dstlen);
  797. }
  798.  
  799. /*
  800.  * Compare two file names and return TRUE if they are different files.
  801.  * For the first name environment variables are expanded
  802.  */
  803.     int
  804. fullpathcmp(s1, s2)
  805.     char *s1, *s2;
  806. {
  807. #ifdef UNIX
  808.     struct stat st1, st2;
  809.     char buf1[MAXPATHL];
  810.  
  811.     expand_env(s1, buf1, MAXPATHL);
  812.     if (stat(buf1, &st1) == 0 && stat(s2, &st2) == 0 &&
  813.                 st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino)
  814.         return FALSE;
  815.     return TRUE;
  816. #else
  817.     char buf1[MAXPATHL];
  818.     char buf2[MAXPATHL];
  819.  
  820.     expand_env(s1, buf2, MAXPATHL);
  821.     if (FullName(buf2, buf1, MAXPATHL) && FullName(s2, buf2, MAXPATHL))
  822.         return strcmp(buf1, buf2);
  823.     /*
  824.      * one of the FullNames() failed, file probably doesn't exist.
  825.      */
  826.     return TRUE;
  827. #endif
  828. }
  829.  
  830. /*
  831.  * get the tail of a path: the file name.
  832.  */
  833.     char *
  834. gettail(fname)
  835.     char *fname;
  836. {
  837.     register char *p1, *p2;
  838.  
  839.     for (p1 = p2 = fname; *p2; ++p2)    /* find last part of path */
  840.     {
  841.         if (ispathsep(*p2))
  842.             p1 = p2 + 1;
  843.     }
  844.     return p1;
  845. }
  846.  
  847. /*
  848.  * return TRUE if 'c' is a path separator.
  849.  */
  850.     int
  851. ispathsep(c)
  852.     int c;
  853. {
  854. #ifdef UNIX
  855.     return (c == PATHSEP);        /* UNIX has ':' inside file names */
  856. #else
  857. # ifdef MSDOS
  858.     return (c == ':' || c == PATHSEP || c == '/');
  859. # else
  860.     return (c == ':' || c == PATHSEP);
  861. # endif
  862. #endif
  863. }
  864.