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