home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / jove414s.zip / os2io.c < prev    next >
C/C++ Source or Header  |  1991-07-07  |  31KB  |  1,510 lines

  1. /***************************************************************************
  2.  * This program is Copyright (C) 1986, 1987, 1988 by Jonathan Payne.  JOVE *
  3.  * is provided to you without charge, and with no warranty.  You may give  *
  4.  * away copies of JOVE, including sources, provided that this notice is    *
  5.  * included in all the files.                                              *
  6.  ***************************************************************************/
  7. #ifdef OS2
  8. #  define INCL_BASE
  9. #  include <os2.h>
  10. #endif
  11.  
  12. #include "jove.h"
  13. #include "list.h"
  14. #include "fp.h"
  15. #include "termcap.h"
  16. #include "ctype.h"
  17. #include "scandir.h"
  18. #include "disp.h"
  19.  
  20. #if defined (IPROCS) || defined (OS2IPROCS)
  21. # include <signal.h>
  22. #endif
  23.  
  24. #ifdef MAC
  25. # include "mac.h"
  26. #else
  27. # include <sys/stat.h>
  28. #endif
  29.  
  30. #ifdef UNIX
  31. # include <sys/file.h>
  32. #endif
  33.  
  34. #ifdef MSDOS
  35. # include <fcntl.h>
  36. # include <d:\c600\include\io.h>
  37. # include <direct.h>
  38. # include <dos.h>
  39. #endif /* MSDOS */
  40.  
  41. #include <errno.h>
  42.  
  43. private struct block
  44.     *b_unlink proto((struct block *)),
  45.     *lookup proto((/*int*/short));
  46.  
  47. private char
  48.     *dbackup proto((char *, char *, int)),
  49. #if defined(MSDOS)
  50.     *fixpath proto((char *)),
  51. #endif
  52.     *getblock proto((daddr, int));
  53.  
  54. private void
  55. #if defined(MSDOS)
  56.     abspath proto((char *, char *)),
  57. #endif
  58.     fake_blkio proto((struct block *, int (*)())),
  59.     DoWriteReg proto((int app)),
  60.     LRUunlink proto((struct block *)),
  61.     file_backup proto((char *fname)),
  62.     real_blkio proto((struct block *, int (*)())),
  63.     dfollow proto((char *, char *));
  64.  
  65. #if defined(MSDOS)
  66. private int
  67.     Dchdir proto((char *));
  68. #endif
  69.  
  70. #ifndef W_OK
  71. # define W_OK    2
  72. # define F_OK    0
  73. #endif
  74.  
  75. #define    READ    0
  76. #define    WRITE    1    /* block operation read or write */
  77.  
  78. long    io_chars;        /* number of chars in this open_file */
  79. int    io_lines;        /* number of lines in this open_file */
  80.  
  81. #if defined(VMUNIX) || defined(MSDOS)
  82. char    iobuff[LBSIZE],
  83.     genbuf[LBSIZE],
  84.     linebuf[LBSIZE];
  85. #else
  86. char    *iobuff,
  87.     *genbuf,
  88.     *linebuf;
  89. #endif
  90.  
  91. #ifdef BACKUPFILES
  92. int    BkupOnWrite = 0;
  93. #endif
  94.  
  95. void
  96. close_file(fp)
  97. File    *fp;
  98. {
  99.     if (fp) {
  100.         if (fp->f_flags & F_TELLALL)
  101.             add_mess(" %d lines, %D characters.",
  102.                  io_lines,
  103.                  io_chars);
  104.         f_close(fp);
  105.     }
  106. }
  107.  
  108. /* Write the region from line1/char1 to line2/char2 to FP.  This
  109.    never CLOSES the file since we don't know if we want to. */
  110.  
  111. int    EndWNewline = 1;
  112.  
  113. void
  114. putreg(fp, line1, char1, line2, char2, makesure)
  115. register  File    *fp;
  116. Line    *line1,
  117.     *line2;
  118. int    char1,
  119.     char2,
  120.     makesure;
  121. {
  122.     register  int    c;
  123.     register  char    *lp;
  124.  
  125.     if (makesure)
  126.         (void) fixorder(&line1, &char1, &line2, &char2);
  127.     while (line1 != line2->l_next) {
  128.         lp = lcontents(line1) + char1;
  129.         if (line1 == line2) {
  130.             fputnchar(lp, (char2 - char1), fp);
  131.             io_chars += (char2 - char1);
  132.         } else {
  133.             while ((c = *lp++) != '\0') {
  134.                 jputc(c, fp);
  135.                 io_chars += 1;
  136.             }
  137.         }
  138.         if (line1 != line2) {
  139.             io_lines += 1;
  140.             io_chars += 1;
  141. #ifdef MSDOS
  142.             jputc('\r', fp);
  143. #endif /* MSDOS */
  144.             jputc('\n', fp);
  145.         }
  146.         line1 = line1->l_next;
  147.         char1 = 0;
  148.     }
  149.     flush(fp);
  150. }
  151.  
  152. private void
  153. dofread(fp)
  154. register  File    *fp;
  155. {
  156.     char    end[LBSIZE];
  157.     int    xeof = 0;
  158.     Line    *savel = curline;
  159.     int    savec = curchar;
  160.  
  161.     strcpy(end, linebuf + curchar);
  162.     xeof = f_gets(fp, linebuf + curchar, (size_t) (LBSIZE - curchar));
  163.     SavLine(curline, linebuf);
  164.     if (!xeof) do {
  165.         curline = listput(curbuf, curline);
  166.         xeof = f_getputl(curline, fp);
  167.     } while (!xeof);
  168.     getDOT();
  169.     linecopy(linebuf, (curchar = strlen(linebuf)), end);
  170.     SavLine(curline, linebuf);
  171.     IFixMarks(savel, savec, curline, curchar);
  172. }
  173.  
  174. void
  175. read_file(file, is_insert)
  176. char    *file;
  177. int    is_insert;
  178. {
  179.     Bufpos    save;
  180.     File    *fp;
  181.  
  182.     if (is_insert == NO)
  183.         curbuf->b_ntbf = NO;
  184.     fp = open_file(file, iobuff, F_READ, NO, NO);
  185.     if (fp == NIL) {
  186.         if (!is_insert && errno == ENOENT)
  187.             s_mess("(new file)");
  188.         else
  189.             s_mess(IOerr("open", file));
  190.         return;
  191.     }
  192.     if (is_insert == NO) {
  193.         set_ino(curbuf);
  194.         if (fp->f_flags & F_READONLY) {
  195.             set_arg_value(1);
  196.         } else {
  197.             set_arg_value(0);
  198.         }
  199.         TogMinor(ReadOnly);
  200.     }
  201.  
  202.     DOTsave(&save);
  203.     dofread(fp);
  204.     if (is_insert && io_chars > 0) {
  205.         modify();
  206.         set_mark();
  207.     }
  208.     SetDot(&save);
  209.     getDOT();
  210.     close_file(fp);
  211. }
  212.  
  213. void
  214. SaveFile()
  215. {
  216.     if (IsModified(curbuf)) {
  217.         if (curbuf->b_fname == 0)
  218.             WriteFile();
  219.         else {
  220.             filemunge(curbuf->b_fname);
  221. #if !defined(MAC) && !defined(MSDOS)
  222.             chk_mtime(curbuf, curbuf->b_fname, "save");
  223. #endif
  224.             file_write(curbuf->b_fname, 0);
  225.         }
  226.     } else
  227.         message("No changes need to be written.");
  228. }
  229.  
  230. char    *HomeDir;    /* home directory */
  231. size_t    HomeLen;    /* length of home directory string */
  232.  
  233. private List        *DirStack = 0;
  234. #define dir_name(dp)    ((char *) list_data((dp)))
  235. #define PWD_PTR        (list_data(DirStack))
  236. #define PWD        ((char *) PWD_PTR)
  237.  
  238. char *
  239. pwd()
  240. {
  241.     return (char *) PWD_PTR;
  242. }
  243.  
  244. char *
  245. pr_name(fname, okay_home)
  246. char    *fname;
  247. int    okay_home;
  248. {
  249.     int    n;
  250.  
  251.     if (fname == 0)
  252.         return 0;
  253.     n = numcomp(fname, PWD);
  254.  
  255.     if ((PWD[n] == 0) &&    /* Matched to end of PWD */
  256.         (fname[n] == '/'))
  257.         return fname + n + 1;
  258.  
  259.     if (okay_home && strcmp(HomeDir, "/") != 0 &&
  260.         strncmp(fname, HomeDir, HomeLen) == 0 &&
  261.         fname[HomeLen] == '/') {
  262.         static char    name_buf[100];
  263.  
  264.         swritef(name_buf, "~%s", fname + HomeLen);
  265.         return name_buf;
  266.     }
  267.  
  268.     return fname;    /* return entire path name */
  269. }
  270.  
  271. #ifdef    MSDOS
  272. extern unsigned int fmask;
  273. #endif    /* MSDOS */
  274.  
  275. void
  276. Chdir()
  277. {
  278.     char    dirbuf[FILESIZE];
  279.  
  280. #ifdef MSDOS
  281.     fmask = 0x10;
  282. #endif
  283.     (void) ask_file((char *) 0, PWD, dirbuf);
  284. #ifdef MSDOS
  285.     fmask = 0x13;
  286.     if (Dchdir(dirbuf) == -1)
  287. #else
  288.     if (chdir(dirbuf) == -1)
  289. #endif
  290.     {
  291.         s_mess("cd: cannot change into %s.", dirbuf);
  292.         return;
  293.     }
  294.     UpdModLine = YES;
  295.     setCWD(dirbuf);
  296.     prCWD();
  297. #ifdef MAC
  298.     Bufchange++;
  299. #endif
  300. }
  301.  
  302. #if defined(UNIX)
  303.  
  304. #  if !defined(BSD4_2)
  305. char *
  306. getwd(buffer)
  307. char    *buffer;
  308. {
  309.     Buffer    *old = curbuf;
  310.     char    *ret_val;
  311.  
  312.     SetBuf(do_select((Window *) 0, "pwd-output"));
  313.     curbuf->b_type = B_PROCESS;
  314.     (void) UnixToBuf("pwd-output", NO, 0, YES, "/bin/pwd", (char *) 0);
  315.     ToFirst();
  316.     strcpy(buffer, linebuf);
  317.     SetBuf(old);
  318.     return buffer;
  319. }
  320. #  endif    /* not BSD4_2 */
  321.  
  322. /* Check if dn is the name of the current working directory
  323.    and that it is in cannonical form */
  324.  
  325. int
  326. chkCWD(dn)
  327. char    *dn;
  328. {
  329.     char    filebuf[FILESIZE];
  330.     struct stat    dnstat,
  331.             dotstat;
  332.  
  333.     if (dn[0] != '/')
  334.         return FALSE;        /* need absolute pathname */
  335.     PathParse(dn, filebuf);
  336.     return stat(filebuf, &dnstat) == 0 &&
  337.            stat(".", &dotstat) == 0 &&
  338.            dnstat.st_dev == dotstat.st_dev &&
  339.            dnstat.st_ino == dotstat.st_ino;
  340. }
  341.  
  342. #endif /* UNIX */
  343.  
  344. void
  345. setCWD(d)
  346. char    *d;
  347. {
  348.     if (DirStack == NIL)
  349.         list_push(&DirStack, (Element *) 0);
  350.     if (PWD == 0)
  351.         PWD_PTR = (Element *) emalloc((size_t) (strlen(d) + 1));
  352.     else
  353.         PWD_PTR = (Element *) ralloc(PWD, strlen(d) + 1);
  354.     strcpy(PWD, d);
  355. }
  356.  
  357. void
  358. getCWD()
  359. {
  360.     char    *cwd;
  361.     char    pathname[FILESIZE];
  362. #if defined(UNIX) && defined(JOB_CONTROL)
  363.     extern char    *getwd();
  364. #endif
  365. #if defined(MSDOS)
  366.     extern char    *getcwd();
  367. #endif
  368.  
  369. #ifndef MSDOS
  370.     cwd = getenv("CWD");
  371.     if (cwd == 0 || !chkCWD(cwd)) {
  372.         cwd = getenv("PWD");
  373.         if (cwd == 0 || !chkCWD(cwd))
  374.             cwd = getwd(pathname);
  375.     }
  376. #else /* MSDOS */
  377.         cwd = fixpath(getcwd(pathname, FILESIZE));
  378. #endif /* MSDOS */
  379.     setCWD(cwd);
  380. }
  381.  
  382. void
  383. prDIRS()
  384. {
  385.     register  List    *lp;
  386.  
  387.     s_mess(": %f ");
  388.     for (lp = DirStack; lp != NIL; lp = list_next(lp))
  389.         add_mess("%s ", pr_name(dir_name(lp), YES));
  390. }
  391.  
  392. void
  393. prCWD()
  394. {
  395.     s_mess(": %f => \"%s\"", PWD);
  396. }
  397.  
  398. void
  399. Pushd()
  400. {
  401.     char    *newdir,
  402.         dirbuf[FILESIZE];
  403.  
  404. #ifdef MSDOS
  405.     fmask = 0x10;
  406. #endif
  407.     newdir = ask_file((char *) 0, NullStr, dirbuf);
  408. #ifdef MSDOS
  409.     fmask = 0x13;
  410. #endif
  411.     UpdModLine = YES;
  412.     if (*newdir == 0) {    /* Wants to swap top two entries */
  413.         char    *old_top;
  414.  
  415.         if (list_next(DirStack) == NIL)
  416.             complain("pushd: no other directory.");
  417.         old_top = PWD;
  418.         list_data(DirStack) = (Element *) dir_name(list_next(DirStack));
  419.         list_data(list_next(DirStack)) = (Element *) old_top;
  420. #ifdef MSDOS
  421.         (void) Dchdir(PWD);
  422. #else
  423.         (void) chdir(PWD);
  424. #endif
  425.     } else {
  426. #ifdef MSDOS
  427.         if (Dchdir(dirbuf) == -1)
  428. #else
  429.         if (chdir(dirbuf) == -1)
  430. #endif
  431.         {
  432.             s_mess("pushd: cannot change into %s.", dirbuf);
  433.             return;
  434.         }
  435.         (void) list_push(&DirStack, (Element *) 0);
  436.         setCWD(dirbuf);
  437.     }
  438.     prDIRS();
  439. }
  440.  
  441. void
  442. Popd()
  443. {
  444.     if (list_next(DirStack) == NIL)
  445.         complain("popd: directory stack is empty.");
  446.     UpdModLine = YES;
  447.     free((char *) list_pop(&DirStack));
  448. #ifdef MSDOS
  449.     (void) Dchdir(PWD);    /* If this doesn't work, we's in deep shit. */
  450. #else
  451.     (void) chdir(PWD);    /* If this doesn't work, we's in deep shit. */
  452. #endif
  453.     prDIRS();
  454. }
  455.  
  456. private char *
  457. dbackup(base, offset, c)
  458. register  char    *base,
  459.             *offset;
  460. register  int    c;
  461. {
  462.     while (offset > base && *--offset != c)
  463.         ;
  464.     return offset;
  465. }
  466.  
  467. private void
  468. dfollow(file, into)
  469. char    *file,
  470.     *into;
  471. {
  472.     char    *dp,
  473. #ifdef MSDOS
  474.         filefix[FILESIZE],
  475. #endif
  476.         *sp;
  477.  
  478. #ifndef MSDOS
  479.     if (*file == '/') {        /* Absolute pathname */
  480.         strcpy(into, "/");
  481.         file += 1;
  482.     } else
  483.         strcpy(into, PWD);
  484. #else
  485.     abspath(file, filefix);        /* convert to absolute pathname */
  486.     strcpy(into, filefix);        /* and forget about drives    */
  487.     into[3] = 0;
  488.     into = &(into[2]);
  489.     file = &(filefix[3]);
  490. #endif
  491.     dp = into + strlen(into);
  492.  
  493.     sp = file;
  494.     do {
  495.         if (*file == 0)
  496.             break;
  497.         if ((sp = strchr(file, '/')) != '\0')
  498.             *sp = 0;
  499.         if (strcmp(file, ".") == 0)
  500.             ;    /* So it will get to the end of the loop */
  501.         else if (strcmp(file, "..") == 0) {
  502.             *(dp = dbackup(into, dp, '/')) = 0;
  503.             if (dp == into)
  504.                 strcpy(into, "/"), dp = into + 1;
  505.         } else {
  506.             if (into[strlen(into) - 1] != '/')
  507.                 (void) strcat(into, "/"), dp += 1;
  508.             (void) strcat(into, file);
  509.             dp += strlen(file);    /* stay at the end */
  510.         }
  511.         file = sp + 1;
  512.     } while (sp != 0);
  513. }
  514.  
  515. #if defined(UNIX)
  516.  
  517. # if defined(YP_PASSWD)
  518.  
  519. #include <pwd.h>
  520.  
  521. private void
  522. get_hdir(user, buf)
  523. register  char    *user,
  524.         *buf;
  525. {
  526.     struct passwd    *p;
  527.  
  528.     p = getpwnam(user);
  529.     endpwent();
  530.     if (p == NULL) {
  531.         add_mess(" [unknown user: %s]", user);
  532.         SitFor(7);
  533.         complain((char *) 0);
  534.         /* NOTREACHED */
  535.     }
  536.     strcpy(buf, p->pw_dir);
  537. }
  538.  
  539. #else
  540.  
  541. private
  542. get_hdir(user, buf)
  543. register  char    *user,
  544.         *buf;
  545. {
  546.     char    fbuf[LBSIZE],
  547.         pattern[100];
  548.     register  int    u_len;
  549.     File    *fp;
  550.  
  551.     u_len = strlen(user);
  552.     fp = open_file("/etc/passwd", fbuf, F_READ, YES, YES);
  553.     swritef(pattern, "%s:[^:]*:[^:]*:[^:]*:[^:]*:\\([^:]*\\):", user);
  554.     while (f_gets(fp, genbuf, LBSIZE) != EOF)
  555.         if ((strncmp(genbuf, user, u_len) == 0) &&
  556.             (LookingAt(pattern, genbuf, 0))) {
  557.             putmatch(1, buf, FILESIZE);
  558.             close_file(fp);
  559.             return;
  560.         }
  561.     close_file(fp);
  562.     add_mess(" [unknown user: %s]", user);
  563.     SitFor(7);
  564.     complain((char *) 0);
  565. }
  566.  
  567. #endif /* YP_PASSWD */
  568. #endif /* UNIX */
  569.  
  570. void
  571. PathParse(name, intobuf)
  572. char    *name,
  573.     *intobuf;
  574. {
  575.     char    localbuf[FILESIZE];
  576.  
  577.     intobuf[0] = localbuf[0] = '\0';
  578.     if (*name == '\0')
  579.         return;
  580.     if (*name == '~') {
  581.         if (name[1] == '/' || name[1] == '\0') {
  582.             strcpy(localbuf, HomeDir);
  583.             name += 1;
  584.         }
  585. #if !(defined(MSDOS) || defined(MAC))    /* may add for mac in future */
  586.         else {
  587.             char    *uendp = strchr(name, '/'),
  588.                 unamebuf[30];
  589.  
  590.             if (uendp == 0)
  591.                 uendp = name + strlen(name);
  592.             name += 1;
  593.             null_ncpy(unamebuf, name, (size_t) (uendp - name));
  594.             get_hdir(unamebuf, localbuf);
  595.             name = uendp;
  596.         }
  597. #endif
  598.     }
  599. #ifndef MSDOS
  600.     else if (*name == '\\')
  601.         name += 1;
  602. #endif /* MSDOS */
  603.     (void) strcat(localbuf, name);
  604.     dfollow(localbuf, intobuf);
  605. }
  606.  
  607. void
  608. filemunge(newname)
  609. char    *newname;
  610. {
  611.     struct stat    stbuf;
  612.  
  613.     if (newname == 0)
  614.         return;
  615.     if (stat(newname, &stbuf))
  616.         return;
  617. #ifndef MSDOS
  618.     if (((stbuf.st_dev != curbuf->b_dev) ||
  619.          (stbuf.st_ino != curbuf->b_ino)) &&
  620. #else /* MSDOS */
  621.     if ( /* (stbuf.st_ino != curbuf->b_ino) && */
  622. #endif /* MSDOS */
  623. #ifndef MAC
  624.         ((stbuf.st_mode & S_IFMT) != S_IFCHR) &&
  625. #endif
  626.         (curbuf->b_fname==NIL || strcmp(newname, curbuf->b_fname) != 0)) {
  627.         rbell();
  628.         confirm("\"%s\" already exists; overwrite it? ", newname);
  629.     }
  630. }
  631.  
  632. void
  633. WrtReg()
  634. {
  635.     DoWriteReg(NO);
  636. }
  637.  
  638. void
  639. AppReg()
  640. {
  641.     DoWriteReg(YES);
  642. }
  643.  
  644. int    CreatMode = DFLT_MODE;
  645.  
  646. private void
  647. DoWriteReg(app)
  648. int    app;
  649. {
  650.     char    fnamebuf[FILESIZE],
  651.         *fname;
  652.     Mark    *mp = CurMark();
  653.     File    *fp;
  654.  
  655.     /* Won't get here if there isn't a Mark */
  656.     fname = ask_file((char *) 0, (char *) 0, fnamebuf);
  657.  
  658. #ifdef BACKUPFILES
  659.     if (app == NO) {
  660.         filemunge(fname);
  661.  
  662.         if (BkupOnWrite)
  663.             file_backup(fname);
  664.     }
  665. #else
  666.     if (!app)
  667.         filemunge(fname);
  668. #endif
  669.  
  670.     fp = open_file(fname, iobuff, app ? F_APPEND : F_WRITE, YES, NO);
  671.     putreg(fp, mp->m_line, mp->m_char, curline, curchar, YES);
  672.     close_file(fp);
  673. }
  674.  
  675. int    OkayBadChars = 0;
  676.  
  677. void
  678. WriteFile()
  679. {
  680.     char    *fname,
  681.         fnamebuf[FILESIZE];
  682. #ifdef MAC
  683.     if (Macmode) {
  684.         if(!(fname = pfile(fnamebuf))) return;
  685.     }
  686.     else
  687. #endif /* MAC */
  688.  
  689.     fname = ask_file((char *) 0, curbuf->b_fname, fnamebuf);
  690.     /* Don't allow bad characters when creating new files. */
  691.     if (!OkayBadChars
  692.     && (curbuf->b_fname==NIL || strcmp(curbuf->b_fname, fnamebuf) != 0))
  693.     {
  694. #ifdef UNIX
  695.         static char    *badchars = "!$^&*()~`{}\"'\\|<>? ";
  696. #endif /* UNIX */
  697. #ifdef MSDOS
  698.         static char    *badchars = "*|<>? ";
  699. #endif /* MSDOS */
  700. #ifdef MAC
  701.         static char *badchars = ":";
  702. #endif /* MAC */
  703.         register  char    *cp = fnamebuf;
  704.         register  int    c;
  705.  
  706.         while ((c = *cp++ & CHARMASK) != '\0')    /* avoid sign extension... */
  707.             if (c < ' ' || c == '\177' || strchr(badchars, c))
  708.                 complain("'%p': bad character in filename.", c);
  709.     }
  710.  
  711. #if !defined(MAC) && !defined(MSDOS)
  712.     chk_mtime(curbuf, fname, "write");
  713. #endif
  714.     filemunge(fname);
  715.     curbuf->b_type = B_FILE;      /* in case it wasn't before */
  716.     setfname(curbuf, fname);
  717.     file_write(fname, 0);
  718. }
  719.  
  720. /* Open file FNAME supplying the buffer IO routine with buffer BUF.
  721.    HOW is F_READ, F_WRITE or F_APPEND.  IFBAD == COMPLAIN means that
  722.    if we fail at opening the file, call complain.  LOUDNESS says
  723.    whether or not to print the "reading ..." message on the message
  724.    line.
  725.  
  726.    NOTE:  This opens the pr_name(fname, NO) of fname.  That is, FNAME
  727.       is usually an entire pathname, which can be slow when the
  728.       pathname is long and there are lots of symbolic links along
  729.       the way (which has become very common in my experience).  So,
  730.       this speeds up opens file names in the local directory.  It
  731.       will not speed up things like "../scm/foo.scm" simple because
  732.       by the time we get here that's already been expanded to an
  733.       absolute pathname.  But this is a start.
  734.    */
  735.  
  736. File *
  737. open_file(fname, buf, how, complainifbad, quiet)
  738. register  char    *fname;
  739. char    *buf;
  740. register  int    how;
  741. int    complainifbad,
  742.     quiet;
  743. {
  744.     register  File    *fp;
  745.  
  746.     io_chars = 0;
  747.     io_lines = 0;
  748.  
  749.     fp = f_open(pr_name(fname, NO), how, buf, LBSIZE);
  750.     if (fp == NIL) {
  751.         message(IOerr((how == F_READ) ? "open" : "create", fname));
  752.         if (complainifbad)
  753.             complain((char *) 0);
  754.     } else {
  755.         int    rd_only = FALSE;
  756. #ifndef MAC
  757.         if (access(pr_name(fname, NO), W_OK) == -1 && errno != ENOENT) {
  758.             rd_only = TRUE;
  759.             fp->f_flags |= F_READONLY;
  760.         }
  761. #endif
  762.         if (!quiet) {
  763.             fp->f_flags |= F_TELLALL;
  764.             f_mess("\"%s\"%s", pr_name(fname, YES),
  765.                    rd_only ? " [Read only]" : NullStr);
  766.         }
  767.     }
  768.     return fp;
  769. }
  770.  
  771. #ifndef MSDOS
  772. /* Check to see if the file has been modified since it was
  773.    last written.  If so, make sure they know what they're
  774.    doing.
  775.  
  776.    I hate to use another stat(), but to use confirm we gotta
  777.    do this before we open the file.
  778.  
  779.    NOTE: This stats FNAME after converting it to a path-relative
  780.      name.  I can't see why this would cause a problem ...
  781.    */
  782.  
  783. void
  784. chk_mtime(thisbuf, fname, how)
  785. Buffer    *thisbuf;
  786. char    *fname,
  787.     *how;
  788. {
  789.     struct stat    stbuf;
  790.     Buffer    *b;
  791.     char    *mesg = "Shall I go ahead and %s anyway? ";
  792.  
  793.     if ((thisbuf->b_mtime != 0) &&        /* if we care ... */
  794.         ((b = file_exists(fname)) != NIL) &&        /* we already have this file */
  795.         (b == thisbuf) &&            /* and it's the current buffer */
  796.         (stat(pr_name(fname, NO), &stbuf) != -1) &&    /* and we can stat it */
  797.         (stbuf.st_mtime != b->b_mtime)) {    /* and there's trouble. */
  798.         rbell();
  799.         redisplay();    /* Ring that bell! */
  800.         TOstart("Warning", TRUE);
  801.         Typeout("\"%s\" now saved on disk is not what you last", pr_name(fname, YES));
  802.         Typeout("visited or saved.  Probably someone else is editing");
  803.         Typeout("your file at the same time.");
  804.         if (how) {
  805.             Typeout("");
  806.             Typeout("Type \"y\" if I should %s, anyway.", how);
  807.             f_mess(mesg, how);
  808.         }
  809.         TOstop();
  810.         if (how)
  811.             confirm(mesg, how);
  812.     }
  813. }
  814.  
  815. #endif /* MSDOS */
  816.  
  817. void
  818. file_write(fname, app)
  819. char    *fname;
  820. int    app;
  821. {
  822.     File    *fp;
  823.  
  824. #ifdef BACKUPFILES
  825.     if (!app && BkupOnWrite)
  826.         file_backup(fname);
  827. #endif
  828.  
  829.     fp = open_file(fname, iobuff, app ? F_APPEND : F_WRITE, YES, NO);
  830.  
  831.     if (EndWNewline) {    /* Make sure file ends with a newLine */
  832.         Bufpos    save;
  833.  
  834.         DOTsave(&save);
  835.         ToLast();
  836.         if (length(curline))    /* Not a blank Line */
  837.             LineInsert(1);
  838.         SetDot(&save);
  839.     }
  840.     putreg(fp, curbuf->b_first, 0, curbuf->b_last, length(curbuf->b_last), NO);
  841.     close_file(fp);
  842.     set_ino(curbuf);
  843.     unmodify();
  844. }
  845.  
  846. void
  847. ReadFile()
  848. {
  849.     Buffer    *bp;
  850.     char    *fname,
  851.         fnamebuf[FILESIZE];
  852.     int    lineno;
  853.  
  854. #ifdef MAC
  855.     if(Macmode) {
  856.         if(!(fname = gfile(fnamebuf))) return;
  857.     }
  858.     else
  859. #endif /* MAC */
  860.     fname = ask_file((char *) 0, curbuf->b_fname, fnamebuf);
  861. #if !(defined(MSDOS) || defined(MAC))
  862.     chk_mtime(curbuf, fname, "read");
  863. #endif /* MSDOS || MAC */
  864.  
  865.     if (IsModified(curbuf)) {
  866.         char    *y_or_n;
  867.         int    c;
  868.  
  869.         for (;;) {
  870.             rbell();
  871.             y_or_n = ask(NullStr, "Shall I make your changes to \"%s\" permanent? ", curbuf->b_name);
  872.             c = CharUpcase(*y_or_n);
  873.             if (c == 'Y' || c == 'N')
  874.                 break;
  875.         }
  876.         if (c == 'Y')
  877.             SaveFile();
  878.     }
  879.  
  880.     if ((bp = file_exists(fnamebuf)) != NIL &&
  881.         (bp == curbuf))
  882.         lineno = pnt_line() - 1;
  883.     else
  884.         lineno = 0;
  885.  
  886.     unmodify();
  887.     initlist(curbuf);
  888.     setfname(curbuf, fname);
  889.     read_file(fname, 0);
  890.     SetLine(next_line(curbuf->b_first, lineno));
  891. }
  892.  
  893. void
  894. InsFile()
  895. {
  896.     char    *fname,
  897.         fnamebuf[FILESIZE];
  898. #ifdef MAC
  899.     if(Macmode) {
  900.         if(!(fname = gfile(fnamebuf))) return;
  901.     }
  902.     else
  903. #endif /* MAC */
  904.     fname = ask_file((char *) 0, curbuf->b_fname, fnamebuf);
  905.     read_file(fname, 1);
  906. }
  907.  
  908. #include "temp.h"
  909.  
  910. int    DOLsave = 0;    /* Do Lsave flag.  If lines aren't being saved
  911.                when you think they should have been, this
  912.                flag is probably not being set, or is being
  913.                cleared before lsave() was called. */
  914.  
  915. private int    nleft,    /* number of good characters left in current block */
  916.         tmpfd = -1, tmp_create_fd;
  917. daddr    DFree = 1;  /* pointer to end of tmp file */
  918. private char    *tfname;
  919.  
  920. void
  921. tmpinit()
  922. {
  923.     char    buf[FILESIZE];
  924.  
  925. #ifdef MAC
  926.     swritef(buf, "%s/%s", HomeDir, d_tempfile);
  927. #else
  928.     swritef(buf, "%s/%s", TmpFilePath, d_tempfile);
  929. #endif
  930.     tfname = copystr(buf);
  931.     tfname = mktemp(tfname);
  932.     tmp_create_fd = creat(tfname, S_IREAD | S_IWRITE);
  933.     (void) close (tmp_create_fd);
  934.     //(void) close(creat(tfname, S_IREAD | S_IWRITE));
  935. #ifndef MSDOS
  936.     tmpfd = open(tfname, 2);
  937. #else /* MSDOS */
  938.     //tmpfd = open(tfname, 0x8002);    /* MSDOS fix    */
  939.     tmpfd = open(tfname, O_RDWR);
  940. #endif /* MSDOS */
  941.     if (tmpfd == -1)
  942.         complain("Warning: cannot create tmp file!");
  943. }
  944.  
  945. void
  946. tmpclose()
  947. {
  948.     if (tmpfd == -1)
  949.         return;
  950.     (void) close(tmpfd);
  951.     tmpfd = -1;
  952.     (void) unlink(tfname);
  953. }
  954.  
  955. /* get a line at `tl' in the tmp file into `buf' which should be LBSIZE
  956.    long */
  957.  
  958. int    Jr_Len;        /* length of Just Read Line */
  959.  
  960. #ifdef MAC    /* The Lighspeed compiler can't copy with static here */
  961.     char    *getblock();
  962. #else
  963. private char    *getblock();
  964. #endif
  965. void
  966. getline(addr, buf)
  967. daddr    addr;
  968. register char    *buf;
  969. {
  970.     register  char    *bp,
  971.             *lp;
  972.  
  973.     lp = buf;
  974.     bp = getblock(addr >> 1, READ);
  975.     do ; while ((*lp++ = *bp++) != '\0');
  976.     Jr_Len = (lp - buf) - 1;
  977. }
  978.  
  979. /* Put `buf' and return the disk address */
  980.  
  981. daddr
  982. putline(buf)
  983. char    *buf;
  984. {
  985.     register  char    *bp,
  986.             *lp;
  987.     register  int    nl;
  988.     daddr    free_ptr;
  989.  
  990.     lp = buf;
  991.     free_ptr = DFree;
  992.     bp = getblock(free_ptr, WRITE);
  993.     nl = nleft;
  994.     free_ptr = blk_round(free_ptr);
  995.     while ((*bp = *lp++) != '\0') {
  996.         if (*bp++ == '\n') {
  997.             *--bp = 0;
  998.             break;
  999.         }
  1000.         if (--nl == 0) {
  1001.             free_ptr = forward_block(free_ptr);
  1002.             DFree = free_ptr;
  1003.             bp = getblock(free_ptr, WRITE);
  1004.             lp = buf;    /* start over ... */
  1005.             nl = nleft;
  1006.         }
  1007.     }
  1008.     free_ptr = DFree;
  1009.     DFree += (((lp - buf) + CH_SIZE - 1) / CH_SIZE);
  1010.          /* (lp - buf) includes the null */
  1011.     return (free_ptr << 1);
  1012. }
  1013.  
  1014. /* The theory is that critical section of code inside this procedure
  1015.    will never cause a problem to occur.  Basically, we need to ensure
  1016.    that two blocks are in memory at the same time, but I think that
  1017.    this can never screw up. */
  1018.  
  1019. #define lockblock(addr)
  1020. #define unlockblock(addr)
  1021.  
  1022. daddr
  1023. f_getputl(line, fp)
  1024. Line    *line;
  1025. register  File    *fp;
  1026. {
  1027.     register  char    *bp;
  1028.     register  int    c,
  1029.             nl,
  1030.             room = LBSIZE;
  1031.     daddr    free_ptr;
  1032.     char        *base;
  1033. #ifdef MSDOS
  1034.     char crleft = 0;
  1035. #endif /* MSDOS */
  1036.  
  1037.     free_ptr = DFree;
  1038.     base = bp = getblock(free_ptr, WRITE);
  1039.     nl = nleft;
  1040.     free_ptr = blk_round(free_ptr);
  1041.     while (--room > 0) {
  1042. #ifdef MSDOS
  1043.         if (crleft) {
  1044.            c = crleft;
  1045.            crleft = 0;
  1046.         } else
  1047. #endif /* MSDOS */
  1048.         c = jgetc(fp);
  1049.         if (c == EOF || c == '\n')
  1050.             break;
  1051. #ifdef MSDOS
  1052.         if (c == '\r')
  1053.             if ((crleft = jgetc(fp)) == '\n') {
  1054.                 crleft = 0;
  1055.                 break;
  1056.             }
  1057. #endif /* MSDOS */
  1058.         if (--nl == 0) {
  1059.             char    *newbp;
  1060.             size_t    nbytes;
  1061.  
  1062.             lockblock(free_ptr);
  1063.             DFree = free_ptr = forward_block(free_ptr);
  1064.             nbytes = bp - base;
  1065.             newbp = getblock(free_ptr, WRITE);
  1066.             nl = nleft;
  1067.             byte_copy(base, newbp, nbytes);
  1068.             bp = newbp + nbytes;
  1069.             base = newbp;
  1070.             unlockblock(free_ptr);
  1071.         }
  1072.         *bp++ = c;
  1073.     }
  1074.     *bp++ = '\0';
  1075.     free_ptr = DFree;
  1076.     DFree += (((bp - base) + CH_SIZE - 1) / CH_SIZE);
  1077.     line->l_dline = (free_ptr << 1);
  1078.     if (room == 0) {
  1079.         add_mess(" [Line too long]");
  1080.         rbell();
  1081.         return EOF;
  1082.     }
  1083.     if (c == EOF) {
  1084.         if (--bp != base)
  1085.             add_mess(" [Incomplete last line]");
  1086.         return EOF;
  1087.     }
  1088.     io_lines += 1;
  1089.     return 0;
  1090. }
  1091.  
  1092. typedef struct block {
  1093.     short    b_dirty,
  1094.         b_bno;
  1095.     char    b_buf[JBUFSIZ];
  1096.     struct block
  1097.         *b_LRUnext,
  1098.         *b_LRUprev,
  1099.         *b_HASHnext;
  1100. } Block;
  1101.  
  1102. #define HASHSIZE    7    /* Primes work best (so I'm told) */
  1103. #define B_HASH(bno)    ((bno) % HASHSIZE)
  1104.  
  1105. #ifdef MAC
  1106. private Block    *b_cache,
  1107. #else
  1108. private Block    b_cache[NBUF],
  1109. #endif
  1110.         *bht[HASHSIZE],        /* Block hash table. Must be zero initially */
  1111.         *f_block = 0,
  1112.         *l_block = 0;
  1113. private int    max_bno = -1,
  1114.         NBlocks;
  1115.  
  1116. #ifdef MAC
  1117. void (*blkio)();
  1118. #else
  1119. private void    (*blkio) proto((Block *, int (*)()));
  1120. #endif /* MAC */
  1121.  
  1122. #ifdef MAC
  1123. make_cache()    /* Only 32K of static space on Mac, so... */
  1124. {
  1125.     return((b_cache = (Block *) calloc(NBUF,sizeof(Block))) == 0 ? 0 : 1);
  1126. }
  1127. #endif /* MAC */
  1128.  
  1129. extern int read(), write();
  1130.  
  1131. private void
  1132. real_blkio(b, iofcn)
  1133. register  Block    *b;
  1134. #if defined(MAC) || defined(IBMPC)
  1135. register  int    (*iofcn)();
  1136. #else
  1137. register  int    (*iofcn) proto((int, UnivPtr, size_t));
  1138. #endif /* MAC */
  1139. {
  1140.     (void) lseek(tmpfd, (long) ((unsigned) b->b_bno) * JBUFSIZ, 0);
  1141.     if ((*iofcn)(tmpfd, b->b_buf, (size_t)JBUFSIZ) != JBUFSIZ)
  1142.         error("[Tmp file %s error; to continue editing would be dangerous]",
  1143.             (iofcn == read) ? "READ" : "WRITE");
  1144. }
  1145.  
  1146. private void
  1147. fake_blkio(b, iofcn)
  1148. register  Block    *b;
  1149. register  int    (*iofcn)();
  1150. {
  1151.     tmpinit();
  1152.     blkio = real_blkio;
  1153.     real_blkio(b, iofcn);
  1154. }
  1155.  
  1156. void
  1157. d_cache_init()
  1158. {
  1159.     register  Block    *bp,    /* Block pointer */
  1160.             **hp;    /* Hash pointer */
  1161.     register  short    bno;
  1162.  
  1163.     for (bp = b_cache, bno = NBUF; --bno >= 0; bp++) {
  1164.         NBlocks += 1;
  1165.         bp->b_dirty = 0;
  1166.         bp->b_bno = bno;
  1167.         if (l_block == 0)
  1168.             l_block = bp;
  1169.         bp->b_LRUprev = 0;
  1170.         bp->b_LRUnext = f_block;
  1171.         if (f_block != 0)
  1172.             f_block->b_LRUprev = bp;
  1173.         f_block = bp;
  1174.  
  1175.         bp->b_HASHnext = *(hp = &bht[B_HASH(bno)]);
  1176.         *hp = bp;
  1177.     }
  1178.     blkio = fake_blkio;
  1179. }
  1180.  
  1181. void
  1182. SyncTmp()
  1183. {
  1184.     register  Block    *b;
  1185. #ifdef IBMPC
  1186.     register  int    bno = 0;
  1187.  
  1188.     /* sync the blocks in order, for file systems that don't allow
  1189.        holes (MSDOS).  Perhaps this benefits floppy-based file systems. */
  1190.  
  1191.     for (bno = 0; bno <= max_bno; ) {
  1192.         if ((b = lookup(bno++)) && b->b_dirty) {
  1193.             (*blkio)(b, write);
  1194.             b->b_dirty = 0;
  1195.         }
  1196.     }
  1197. #else
  1198.     for (b = f_block; b != 0; b = b->b_LRUnext)
  1199.         if (b->b_dirty) {
  1200.             (*blkio)(b, write);
  1201.             b->b_dirty = 0;
  1202.         }
  1203. #endif
  1204. }
  1205.  
  1206. private Block *
  1207. lookup(bno)
  1208. register  short    bno;
  1209. {
  1210.     register  Block    *bp;
  1211.  
  1212.     for (bp = bht[B_HASH(bno)]; bp != 0; bp = bp->b_HASHnext)
  1213.         if (bp->b_bno == bno)
  1214.             break;
  1215.     return bp;
  1216. }
  1217.  
  1218. private void
  1219. LRUunlink(b)
  1220. register  Block    *b;
  1221. {
  1222.     if (b->b_LRUprev == 0)
  1223.         f_block = b->b_LRUnext;
  1224.     else
  1225.         b->b_LRUprev->b_LRUnext = b->b_LRUnext;
  1226.     if (b->b_LRUnext == 0)
  1227.         l_block = b->b_LRUprev;
  1228.     else
  1229.         b->b_LRUnext->b_LRUprev = b->b_LRUprev;
  1230. }
  1231.  
  1232. private Block *
  1233. b_unlink(bp)
  1234. register  Block    *bp;
  1235. {
  1236.     register  Block    *hp,
  1237.             *prev = 0;
  1238.  
  1239.     LRUunlink(bp);
  1240.     /* Now that we have the block, we remove it from its position
  1241.        in the hash table, so we can THEN put it somewhere else with
  1242.        it's new block assignment. */
  1243.  
  1244.     for (hp = bht[B_HASH(bp->b_bno)]; hp != 0; prev = hp, hp = hp->b_HASHnext)
  1245.         if (hp == bp)
  1246.             break;
  1247.     if (hp == 0) {
  1248.         writef("\rBlock %d missing!", bp->b_bno);
  1249.         finish(0);
  1250.     }
  1251.     if (prev)
  1252.         prev->b_HASHnext = hp->b_HASHnext;
  1253.     else
  1254.         bht[B_HASH(bp->b_bno)] = hp->b_HASHnext;
  1255.  
  1256.     if (bp->b_dirty) {    /* do, now, the delayed write */
  1257.         (*blkio)(bp, write);
  1258.         bp->b_dirty = 0;
  1259.     }
  1260.  
  1261.     return bp;
  1262. }
  1263.  
  1264. /* Get a block which contains at least part of the line with the address
  1265.    atl.  Returns a pointer to the block and sets the global variable
  1266.    nleft (number of good characters left in the buffer). */
  1267.  
  1268. private char *
  1269. getblock(atl, iof)
  1270. daddr    atl;
  1271. int    iof;
  1272. {
  1273.     register  int    bno,
  1274.             off;
  1275.     register  Block    *bp;
  1276.     static Block    *lastb = 0;
  1277.  
  1278.     bno = da_to_bno(atl);
  1279.     off = da_to_off(atl);
  1280.     if (da_too_huge(atl))
  1281.         error("Tmp file too large.  Get help!");
  1282.     nleft = JBUFSIZ - off;
  1283.     if (lastb != 0 && lastb->b_bno == bno) {
  1284.         lastb->b_dirty |= iof;
  1285.         return lastb->b_buf + off;
  1286.     }
  1287.  
  1288.     /* The requested block already lives in memory, so we move
  1289.        it to the end of the LRU list (making it Most Recently Used)
  1290.        and then return a pointer to it. */
  1291.     if ((bp = lookup(bno)) != NIL) {
  1292.         if (bp != l_block) {
  1293.             LRUunlink(bp);
  1294.             if (l_block == 0)
  1295.                 f_block = l_block = bp;
  1296.             else
  1297.                 l_block->b_LRUnext = bp;
  1298.             bp->b_LRUprev = l_block;
  1299.             l_block = bp;
  1300.             bp->b_LRUnext = 0;
  1301.         }
  1302.         if (bp->b_bno > max_bno)
  1303.             max_bno = bp->b_bno;
  1304.         bp->b_dirty |= iof;
  1305.         lastb = bp;
  1306.         return bp->b_buf + off;
  1307.     }
  1308.  
  1309.     /* The block we want doesn't reside in memory so we take the
  1310.        least recently used clean block (if there is one) and use
  1311.        it.  */
  1312.     bp = f_block;
  1313.     if (bp->b_dirty)    /* The best block is dirty ... */
  1314.         SyncTmp();
  1315.  
  1316.     bp = b_unlink(bp);
  1317.     if (l_block == 0)
  1318.         l_block = f_block = bp;
  1319.     else
  1320.         l_block->b_LRUnext = bp;    /* Place it at the end ... */
  1321.     bp->b_LRUprev = l_block;
  1322.     l_block = bp;
  1323.     bp->b_LRUnext = 0;        /* so it's Most Recently Used */
  1324.  
  1325.     bp->b_dirty = iof;
  1326.     bp->b_bno = bno;
  1327.     bp->b_HASHnext = bht[B_HASH(bno)];
  1328.     bht[B_HASH(bno)] = bp;
  1329.  
  1330.     /* Get the current contents of the block UNLESS this is a new
  1331.        block that's never been looked at before, i.e., it's past
  1332.        the end of the tmp file. */
  1333.  
  1334.     if (bp->b_bno <= max_bno)
  1335.         (*blkio)(bp, read);
  1336.     else
  1337.         max_bno = bno;
  1338.  
  1339.     lastb = bp;
  1340.     return bp->b_buf + off;
  1341. }
  1342.  
  1343. char *
  1344. lbptr(line)
  1345. Line    *line;
  1346. {
  1347.     return getblock(line->l_dline >> 1, READ);
  1348. }
  1349.  
  1350. /* save the current contents of linebuf, if it has changed */
  1351.  
  1352. void
  1353. lsave()
  1354. {
  1355.     if (curbuf == 0 || !DOLsave)    /* Nothing modified recently */
  1356.         return;
  1357.  
  1358.     if (strcmp(lbptr(curline), linebuf) != 0)
  1359.         SavLine(curline, linebuf);    /* Put linebuf on the disk. */
  1360.     DOLsave = 0;
  1361. }
  1362.  
  1363. #ifdef BACKUPFILES
  1364. private void
  1365. file_backup(fname)
  1366. char *fname;
  1367. {
  1368. #ifndef MSDOS
  1369.     char    *s;
  1370.     register  int    i;
  1371.     int    fd1,
  1372.         fd2;
  1373.     char    tmp1[JBUFSIZ],
  1374.         tmp2[JBUFSIZ];
  1375.     struct stat buf;
  1376.     int    mode;
  1377.  
  1378.     strcpy(tmp1, fname);
  1379.     if ((s = strrchr(tmp1, '/')) == NULL)
  1380.         swritef(tmp2, "#%s~", fname);
  1381.     else {
  1382.         *s++ = '\0';
  1383.         swritef(tmp2, "%s/#%s~", tmp1, s);
  1384.     }
  1385.  
  1386.     if ((fd1 = open(fname, 0)) < 0)
  1387.         return;
  1388.  
  1389.     /* create backup file with same mode as input file */
  1390. #ifndef MAC
  1391.     if (fstat(fd1, &buf) != 0)
  1392.         mode = CreatMode;
  1393.     else
  1394. #endif
  1395.         mode = buf.st_mode;
  1396.  
  1397.     if ((fd2 = creat(tmp2, mode)) < 0) {
  1398.         (void) close(fd1);
  1399.         return;
  1400.     }
  1401.     while ((i = read(fd1, tmp1, sizeof(tmp1))) > 0)
  1402.         write(fd2, tmp1, (size_t) i);
  1403. #ifdef BSD4_2
  1404.     (void) fsync(fd2);
  1405. #endif
  1406.     (void) close(fd2);
  1407.     (void) close(fd1);
  1408. #else /* MSDOS */
  1409.     char    *dot,
  1410.             *slash,
  1411.             tmp[FILESIZE];
  1412.  
  1413.     strcpy(tmp, fname);
  1414.     slash = basename(tmp);
  1415.     if (dot = strrchr(slash, '.')) {
  1416.        if (!stricmp(dot,".bak"))
  1417.         return;
  1418.        else *dot = 0;
  1419.     }
  1420.     strcat(tmp, ".bak");
  1421.     unlink(tmp);
  1422.     rename(fname, tmp);
  1423. #endif /* MSDOS */
  1424. }
  1425. #endif
  1426.  
  1427. #if defined(MSDOS)
  1428.  
  1429. private int            /* chdir + drive */
  1430. Dchdir(to)
  1431. char *to;
  1432. {
  1433.     unsigned d, dd, n;
  1434.     long d_map;
  1435.  
  1436.     if (to[1] == ':') {
  1437.         d = to[0];
  1438.         if (d >= 'a') d = d - 'a' + 1;
  1439.         if (d >= 'A') d = d - 'A' + 1;
  1440. #ifndef OS2
  1441.         _dos_getdrive(&dd);
  1442. # else
  1443.         DosQCurDisk(&dd, &d_map);
  1444. #endif /* not OS2 */
  1445.         if (dd != d)
  1446. #ifndef OS2
  1447.             _dos_setdrive(d, &n);
  1448. #else
  1449.         DosSelectDisk(d);
  1450. #endif
  1451.         if (to[2] == 0)
  1452.             return 0;
  1453.     }
  1454.     return chdir(to);
  1455. }
  1456.  
  1457. private char *
  1458. fixpath(p)
  1459. char *p;
  1460. {
  1461.     char *pp = p;
  1462.  
  1463.     while (*p) {
  1464.         if (*p == '\\')
  1465.             *p = '/';
  1466.         p++;
  1467.     }
  1468.     return(strlwr(pp));
  1469. }
  1470. #define _MAX_PATH    260    /* max. length of full pathname */
  1471. #define _MAX_DRIVE    3    /* max. length of drive component */
  1472. #define _MAX_DIR    256    /* max. length of path component */
  1473. #define _MAX_FNAME    256    /* max. length of file name component */
  1474. #define _MAX_EXT    256    /* max. length of extension component */
  1475.  
  1476.  
  1477. private void
  1478. abspath(so, dest)
  1479. char *so, *dest;
  1480. {
  1481.     char cwd[FILESIZE], cwdD[3], cwdDIR[FILESIZE], cwdF[9], cwdEXT[5],
  1482.          soD[_MAX_DRIVE], soDIR[_MAX_DIR], soF[_MAX_FNAME], soEXT[_MAX_EXT];
  1483.     char *drive, *path;
  1484.  
  1485.     _splitpath(fixpath(so), soD, soDIR, soF, soEXT);
  1486.     getcwd(cwd, FILESIZE);
  1487.     if (*soD != 0) {
  1488.         Dchdir(soD);                /* this is kinda messy    */
  1489.         getcwd(cwdDIR, FILESIZE);    /* should probably just    */
  1490.         Dchdir(cwd);                /* call DOS to do it    */
  1491.         strcpy(cwd, cwdDIR);
  1492.     }
  1493.     (void) fixpath(cwd);
  1494.     if (cwd[strlen(cwd)-1] != '/')
  1495.         strcat(cwd, "/x.x");    /* need dummy filename */
  1496.  
  1497.     _splitpath(fixpath(cwd), cwdD, cwdDIR, cwdF, cwdEXT);
  1498.  
  1499.     drive = (*soD == 0) ? cwdD : soD;
  1500.  
  1501.     if (*soDIR != '/')
  1502.         path = strcat(cwdDIR, soDIR);
  1503.     else
  1504.         path = soDIR;
  1505.     _makepath(dest, drive, path, soF, soEXT);
  1506.     fixpath(dest);    /* can't do it often enough */
  1507. }
  1508.  
  1509. #endif
  1510.