home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume8 / jove / part04 / io.c next >
Encoding:
C/C++ Source or Header  |  1987-02-02  |  20.3 KB  |  1,080 lines

  1. /************************************************************************
  2.  * This program is Copyright (C) 1986 by Jonathan Payne.  JOVE is       *
  3.  * 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 "io.h"
  10. #include "termcap.h"
  11.  
  12. #ifdef IPROCS
  13. #   include <signal.h>
  14. #endif
  15.  
  16. #include <sys/stat.h>
  17. #include <sys/file.h>
  18. #include <errno.h>
  19.  
  20. #ifndef W_OK
  21. #   define W_OK    2
  22. #   define F_OK    0
  23. #endif
  24.  
  25. long    io_chars;        /* number of chars in this open_file */
  26. int    io_lines;        /* number of lines in this open_file */
  27. private int    tellall;    /* display file io info? */
  28.  
  29. #ifdef VMUNIX
  30. char    iobuff[LBSIZE],
  31.     genbuf[LBSIZE],
  32.     linebuf[LBSIZE];
  33. #else
  34. char    *iobuff,
  35.     *genbuf,
  36.     *linebuf;
  37. #endif
  38.  
  39. #ifdef BACKUPFILES
  40. int    BkupOnWrite = 0;
  41. #endif
  42.  
  43. close_file(fp)
  44. File    *fp;
  45. {
  46.     if (fp) {
  47.         f_close(fp);
  48.         if (tellall != QUIET)
  49.             add_mess(" %d lines, %D characters.",
  50.                  io_lines,
  51.                  io_chars);
  52.     }
  53. }
  54.  
  55. /* Write the region from line1/char1 to line2/char2 to FP.  This
  56.    never CLOSES the file since we don't know if we want to. */
  57.  
  58. int    EndWNewline = 1;
  59.  
  60. putreg(fp, line1, char1, line2, char2, makesure)
  61. register File    *fp;
  62. Line    *line1,
  63.     *line2;
  64. {
  65.     register int    c;
  66.     register char    *lp;
  67.  
  68.     if (makesure)
  69.         (void) fixorder(&line1, &char1, &line2, &char2);
  70.     while (line1 != line2->l_next) {
  71.         lp = lcontents(line1) + char1;
  72.         if (line1 == line2) {
  73.             fputnchar(lp, (char2 - char1), fp);
  74.             io_chars += (char2 - char1);
  75.         } else while (c = *lp++) {
  76.             putc(c, fp);
  77.             io_chars++;
  78.         }
  79.         if (line1 != line2) {
  80.             io_lines++;
  81.             io_chars++;
  82.             putc('\n', fp);
  83.         }
  84.         line1 = line1->l_next;
  85.         char1 = 0;
  86.     }
  87.     flush(fp);
  88. }
  89.  
  90. read_file(file, is_insert)
  91. char    *file;
  92. {
  93.     Bufpos    save;
  94.     File    *fp;
  95.  
  96.     if (!is_insert) {
  97.         curbuf->b_ntbf = 0;
  98.         set_ino(curbuf);
  99.     }
  100.     fp = open_file(file, iobuff, F_READ, !COMPLAIN, !QUIET);
  101.     if (fp == NIL) {
  102.         if (!is_insert && errno == ENOENT)
  103.             s_mess("(new file)");
  104.         else
  105.             s_mess(IOerr("open", file));
  106.         return;
  107.     }
  108.     DOTsave(&save);
  109.     dofread(fp);
  110.     SetDot(&save);
  111.     if (is_insert && io_chars > 0)
  112.         modify();
  113.     getDOT();
  114.     close_file(fp);
  115. }
  116.  
  117. dofread(fp)
  118. register File    *fp;
  119. {
  120.     char    end[LBSIZE];
  121.     int    xeof = 0;
  122.     Line    *savel = curline;
  123.     int    savec = curchar;
  124.     disk_line    f_getputl() ;
  125.  
  126.     strcpy(end, linebuf + curchar);
  127.     xeof = f_gets(fp, linebuf + curchar, LBSIZE - curchar);
  128.     SavLine(curline, linebuf);
  129.     if (!xeof) do {
  130.         curline = listput(curbuf, curline);
  131.         xeof = f_getputl(curline, fp);
  132.     } while (!xeof);
  133.     getDOT();
  134.     linecopy(linebuf, (curchar = strlen(linebuf)), end);
  135.     SavLine(curline, linebuf);
  136.     IFixMarks(savel, savec, curline, curchar);
  137. }
  138.  
  139. SaveFile()
  140. {
  141.     if (IsModified(curbuf)) {
  142.         if (curbuf->b_fname == 0)
  143.             WriteFile();
  144.         else {
  145.             filemunge(curbuf->b_fname);
  146.             chk_mtime(curbuf, curbuf->b_fname, "save");
  147.             file_write(curbuf->b_fname, 0);
  148.             unmodify();
  149.         }
  150.     } else
  151.         message("No changes need to be written.");
  152. }
  153.  
  154. char    *HomeDir;    /* home directory */
  155. int    HomeLen = -1;    /* length of home directory string */
  156.  
  157. #ifndef CHDIR
  158.  
  159. char *
  160. pr_name(fname)
  161. char    *fname;
  162. {
  163.     if (fname == 0)
  164.         return 0;
  165.  
  166.     if (strncmp(fname, HomeDir, HomeLen) == 0) {
  167.         static char    name_buf[100];
  168.  
  169.         sprintf(name_buf, "~%s", fname + HomeLen);
  170.         return name_buf;
  171.     }
  172.  
  173.     return fname;
  174. }
  175.  
  176. #else
  177.  
  178. #define NDIRS    5
  179.  
  180. private char    *DirStack[NDIRS] = {0};
  181. private int    DirSP = 0;    /* Directory stack pointer */
  182. #define PWD    (DirStack[DirSP])
  183.  
  184. char *
  185. pwd()
  186. {
  187.     return PWD;
  188. }
  189.  
  190. char *
  191. pr_name(fname)
  192. char    *fname;
  193. {
  194.     int    n;
  195.  
  196.     if (fname == 0)
  197.         return 0;
  198.     n = numcomp(fname, PWD);
  199.  
  200.     if ((PWD[n] == 0) &&    /* Matched to end of PWD */
  201.         (fname[n] == '/'))
  202.         return fname + n + 1;
  203.  
  204.     if (strcmp(HomeDir, "/") != 0 && strncmp(fname, HomeDir, HomeLen) == 0) {
  205.         static char    name_buf[100];
  206.  
  207.         sprintf(name_buf, "~%s", fname + HomeLen);
  208.         return name_buf;
  209.     }
  210.  
  211.     return fname;    /* return entire path name */
  212. }
  213.  
  214. Chdir()
  215. {
  216.     char    dirbuf[FILESIZE];
  217.  
  218.     (void) ask_file((char *) 0, PWD, dirbuf);
  219.     if (chdir(dirbuf) == -1) {
  220.         s_mess("cd: cannot change into %s.", dirbuf);
  221.         return;
  222.     }
  223.     UpdModLine++;
  224.     setCWD(dirbuf);
  225. }
  226.  
  227. #ifndef JOB_CONTROL
  228. char *
  229. getwd()
  230. {
  231.     Buffer    *old = curbuf;
  232.     char    *ret_val;
  233.  
  234.     SetBuf(do_select((Window *) 0, "pwd-output"));
  235.     curbuf->b_type = B_PROCESS;
  236.     (void) UnixToBuf("pwd-output", NO, 0, YES, "/bin/pwd", (char *) 0);
  237.     ToFirst();
  238.     ret_val = sprint(linebuf);
  239.     SetBuf(old);
  240.     return ret_val;
  241. }
  242. #endif
  243.  
  244. setCWD(d)
  245. char    *d;
  246. {
  247.     if (PWD == 0)
  248.         PWD = malloc((unsigned) strlen(d) + 1);
  249.     else {
  250.         extern char    *ralloc();
  251.  
  252.         PWD = ralloc(PWD, strlen(d) + 1);
  253.     }
  254.     strcpy(PWD, d);
  255. }
  256.  
  257. getCWD()
  258. {
  259.     char    *cwd = getenv("CWD");
  260. #ifdef JOB_CONTROL
  261.     extern char    *getwd();
  262.     char    pathname[FILESIZE];
  263. #endif
  264.  
  265.     if (cwd == 0)
  266. #ifdef JOB_CONTROL
  267.         cwd = getwd(pathname);
  268. #else
  269.         cwd = getwd();
  270. #endif
  271.  
  272.     setCWD(cwd);
  273. }    
  274.  
  275. prDIRS()
  276. {
  277.     register int    i;
  278.  
  279.     s_mess(": %f ");
  280.     for (i = DirSP; i >= 0; i--)
  281.         add_mess("%s ", pr_name(DirStack[i]));
  282. }
  283.  
  284. prCWD()
  285. {
  286.     s_mess(": %f => \"%s\"", PWD);
  287. }
  288.  
  289. Pushd()
  290. {
  291.     char    *newdir,
  292.         dirbuf[FILESIZE];
  293.  
  294.     newdir = ask_file((char *) 0, NullStr, dirbuf);
  295.     UpdModLine++;
  296.     if (*newdir == 0) {    /* Wants to swap top two entries */
  297.         char    *old_top;
  298.  
  299.         if (DirSP == 0)
  300.             complain("pushd: no other directory.");
  301.         old_top = PWD;
  302.         DirStack[DirSP] = DirStack[DirSP - 1];
  303.         DirStack[DirSP - 1] = old_top;
  304.         (void) chdir(PWD);
  305.     } else {
  306.         if (chdir(dirbuf) == -1) {
  307.             s_mess("pushd: cannot change into %s.", dirbuf);
  308.             return;
  309.         }
  310.  
  311.         if (DirSP + 1 >= NDIRS)
  312.             complain("pushd: full stack; max of %d pushes.", NDIRS);
  313.         DirSP++;
  314.         setCWD(dirbuf);
  315.     }
  316.     prDIRS();
  317. }
  318.  
  319. Popd()
  320. {
  321.     if (DirSP == 0)
  322.         complain("popd: directory stack is empty.");
  323.     UpdModLine++;
  324.     free(PWD);
  325.     PWD = 0;
  326.     DirSP--;
  327.     (void) chdir(PWD);    /* If this doesn't work, we's in deep shit. */
  328.     prDIRS();
  329. }
  330.  
  331. private char *
  332. dbackup(base, offset, c)
  333. register char    *base,
  334.         *offset,
  335.         c;
  336. {
  337.     while (offset > base && *--offset != c)
  338.         ;
  339.     return offset;
  340. }
  341.  
  342. dfollow(file, into)
  343. char    *file,
  344.     *into;
  345. {
  346.     char    *dp,
  347.         *sp;
  348.  
  349.     if (*file == '/') {        /* Absolute pathname */
  350.         strcpy(into, "/");
  351.         file++;
  352.     } else
  353.         strcpy(into, PWD);
  354.     dp = into + strlen(into);
  355.  
  356.     sp = file;
  357.     do {
  358.         if (*file == 0)
  359.             break;
  360.         if (sp = index(file, '/'))
  361.             *sp = 0;
  362.         if (strcmp(file, ".") == 0)
  363.             ;    /* So it will get to the end of the loop */
  364.         else if (strcmp(file, "..") == 0) {
  365.             *(dp = dbackup(into, dp, '/')) = 0;
  366.             if (dp == into)
  367.                 strcpy(into, "/"), dp = into + 1;
  368.         } else {
  369.             if (into[strlen(into) - 1] != '/')
  370.                 (void) strcat(into, "/");
  371.             (void) strcat(into, file);
  372.             dp += strlen(file);    /* stay at the end */
  373.         }
  374.         file = sp + 1;
  375.     } while (sp != 0);
  376. }
  377.  
  378. #endif CHDIR
  379.  
  380. get_hdir(user, buf)
  381. register char    *user,
  382.         *buf;
  383. {
  384.     char    fbuf[LBSIZE],
  385.         pattern[100];
  386.     register int    u_len;
  387.     File    *fp;
  388.  
  389.     u_len = strlen(user);
  390.     fp = open_file("/etc/passwd", fbuf, F_READ, COMPLAIN, QUIET);
  391.     sprintf(pattern, "%s:[^:]*:[^:]*:[^:]*:[^:]*:\\([^:]*\\):", user);
  392.     while (f_gets(fp, genbuf, LBSIZE) != EOF)
  393.         if ((strncmp(genbuf, user, u_len) == 0) &&
  394.             (LookingAt(pattern, genbuf, 0))) {
  395.             putmatch(1, buf, FILESIZE);
  396.             close_file(fp);
  397.             return;
  398.         }
  399.     f_close(fp);
  400.     complain("[unknown user: %s]", user);
  401. }
  402.  
  403. PathParse(name, intobuf)
  404. char    *name,
  405.     *intobuf;
  406. {
  407.     char    localbuf[FILESIZE];
  408.  
  409.     intobuf[0] = localbuf[0] = '\0';
  410.     if (*name == '\0')
  411.         return;
  412.     if (*name == '~') {
  413.         if (name[1] == '/' || name[1] == '\0') {
  414.             strcpy(localbuf, HomeDir);
  415.             name++;
  416.         } else {
  417.             char    *uendp = index(name, '/'),
  418.                 unamebuf[30];
  419.  
  420.             if (uendp == 0)
  421.                 uendp = name + strlen(name);
  422.             name = name + 1;
  423.             null_ncpy(unamebuf, name, uendp - name);
  424.             get_hdir(unamebuf, localbuf);
  425.             name = uendp;
  426.         }
  427.     } else if (*name == '\\')
  428.         name++;
  429.     (void) strcat(localbuf, name);
  430. #ifdef CHDIR
  431.     dfollow(localbuf, intobuf);
  432. #else
  433.     strcpy(intobuf, localbuf);
  434. #endif
  435. }
  436.  
  437. filemunge(newname)
  438. char    *newname;
  439. {
  440.     struct stat    stbuf;
  441.  
  442.     if (newname == 0)
  443.         return;
  444.     if (stat(newname, &stbuf))
  445.         return;
  446.     if ((stbuf.st_ino != curbuf->b_ino) &&
  447.         ((stbuf.st_mode & S_IFMT) != S_IFCHR) &&
  448.         (strcmp(newname, curbuf->b_fname) != 0)) {
  449.         rbell();
  450.         confirm("\"%s\" already exists; overwrite it? ", newname);
  451.     }
  452. }
  453.  
  454. WrtReg()
  455. {
  456.     DoWriteReg(0);
  457. }
  458.  
  459. AppReg()
  460. {
  461.     DoWriteReg(1);
  462. }
  463.  
  464. int    CreatMode = DFLT_MODE;
  465.  
  466. DoWriteReg(app)
  467. {
  468.     char    fnamebuf[FILESIZE],
  469.         *fname;
  470.     Mark    *mp = CurMark();
  471.     File    *fp;
  472.  
  473.     /* Won't get here if there isn't a Mark */
  474.     fname = ask_file((char *) 0, (char *) 0, fnamebuf);
  475.  
  476. #ifdef BACKUPFILES
  477.     if (!app) {
  478.         filemunge(fname);
  479.  
  480.         if (BkupOnWrite)
  481.             file_backup(fname);
  482.     }
  483. #else
  484.     if (!app)
  485.         filemunge(fname);
  486. #endif
  487.  
  488.     fp = open_file(fname, iobuff, app ? F_APPEND : F_WRITE, COMPLAIN, !QUIET);
  489.     putreg(fp, mp->m_line, mp->m_char, curline, curchar, YES);
  490.     close_file(fp);
  491. }
  492.  
  493. int    OkayBadChars = 0;
  494.  
  495. WriteFile()
  496. {
  497.     char    *fname,
  498.         fnamebuf[FILESIZE];
  499.  
  500.     fname = ask_file((char *) 0, curbuf->b_fname, fnamebuf);
  501.     /* Don't allow bad characters when creating new files. */
  502.     if (!OkayBadChars && strcmp(curbuf->b_fname, fnamebuf) != 0) {
  503.         static char    *badchars = "!$^&*()~`{}\"'\\|<>? ";
  504.         register char    *cp = fnamebuf;
  505.         register int    c;
  506.  
  507.         while (c = *cp++)
  508.             if (c < ' ' || c == '\177' || index(badchars, c))
  509.                 complain("'%p': bad character in filename.", c);
  510.     }
  511.  
  512.     chk_mtime(curbuf, fname, "write");
  513.     filemunge(fname);
  514.     curbuf->b_type = B_FILE;      /* In case it wasn't before. */
  515.     setfname(curbuf, fname);
  516.     file_write(fname, 0);
  517.     unmodify();
  518. }
  519.  
  520. File *
  521. open_file(fname, buf, how, ifbad, loudness)
  522. register char    *fname;
  523. char    *buf;
  524. register int    how;
  525. {
  526.     register File    *fp;
  527.  
  528.     io_chars = 0;
  529.     io_lines = 0;
  530.     tellall = loudness;
  531.  
  532.     fp = f_open(fname, how, buf, LBSIZE);
  533.     if (fp == NIL) {
  534.                 message(IOerr((how == F_READ) ? "open" : "create", fname));
  535.         if (ifbad == COMPLAIN)
  536.             complain((char *) 0);
  537.     } else {
  538.         int    readonly = FALSE;
  539.  
  540.         if (access(fname, W_OK) == -1 && errno != ENOENT)
  541.             readonly = TRUE;
  542.                              
  543.         if (loudness != QUIET)
  544.             f_mess("\"%s\"%s", pr_name(fname),
  545.                    readonly ? " [Read only]" : NullStr);
  546.     }
  547.     return fp;
  548. }
  549.  
  550. /* Check to see if the file has been modified since it was
  551.    last written.  If so, make sure they know what they're
  552.    doing.
  553.  
  554.    I hate to use another stat(), but to use confirm we gotta
  555.    do this before we open the file. */
  556.  
  557. chk_mtime(thisbuf, fname, how)
  558. Buffer    *thisbuf;
  559. char    *fname,
  560.     *how;
  561. {
  562.     struct stat    stbuf;
  563.     Buffer    *b;
  564.         char    *mesg = "Shall I go ahead and %s anyway? ";
  565.  
  566.     if ((thisbuf->b_mtime != 0) &&        /* if we care ... */
  567.         (b = file_exists(fname)) &&        /* we already have this file */
  568.         (b == thisbuf) &&            /* and it's the current buffer */
  569.         (stat(fname, &stbuf) != -1) &&    /* and we can stat it */
  570.         (stbuf.st_mtime != b->b_mtime)) {    /* and there's trouble. */
  571.             rbell();
  572.         redisplay();    /* Ring that bell! */
  573.             TOstart("Warning", TRUE);
  574.             Typeout("\"%s\" now saved on disk is not what you last", pr_name(fname));
  575.         Typeout("visited or saved.  Probably someone else is editing");
  576.         Typeout("your file at the same time.");
  577.             if (how) {
  578.             Typeout("");
  579.             Typeout("Type \"y\" if I should %s, anyway.", how);
  580.                 f_mess(mesg, how);
  581.         }
  582.             TOstop();
  583.             if (how)
  584.                 confirm(mesg, how);
  585.     }
  586. }
  587.  
  588. file_write(fname, app)
  589. char    *fname;
  590. {
  591.     File    *fp;
  592.  
  593. #ifdef BACKUPFILES
  594.     if (!app && BkupOnWrite)
  595.         file_backup(fname);
  596. #endif
  597.  
  598.     fp = open_file(fname, iobuff, app ? F_APPEND : F_WRITE, COMPLAIN, !QUIET);
  599.  
  600.     if (EndWNewline) {    /* Make sure file ends with a newLine */
  601.         Bufpos    save;
  602.  
  603.         DOTsave(&save);
  604.         ToLast();
  605.         if (length(curline))    /* Not a blank Line */
  606.             LineInsert(1);
  607.         SetDot(&save);
  608.     }
  609.     putreg(fp, curbuf->b_first, 0, curbuf->b_last, length(curbuf->b_last), NO);
  610.     set_ino(curbuf);
  611.     close_file(fp);
  612. }
  613.  
  614. ReadFile()
  615. {
  616.     char    *fname,
  617.         fnamebuf[FILESIZE];
  618.  
  619.     fname = ask_file((char *) 0, curbuf->b_fname, fnamebuf);
  620.     chk_mtime(curbuf, fname, "read");
  621.  
  622.     if (IsModified(curbuf)) {
  623.         char    *y_or_n;
  624.         int    c;
  625.  
  626.         for (;;) {
  627.             rbell();
  628.             y_or_n = ask(NullStr, "Shall I make your changes to \"%s\" permanent? ", curbuf->b_name);
  629.             c = Upper(*y_or_n);
  630.             if (c == 'Y' || c == 'N')
  631.                 break;
  632.         }            
  633.         if (c == 'Y')
  634.             SaveFile();
  635.     }
  636.  
  637.     unmodify();
  638.     initlist(curbuf);
  639.     setfname(curbuf, fname);
  640.     read_file(fname, 0);
  641. }
  642.  
  643. InsFile()
  644. {
  645.     char    *fname,
  646.         fnamebuf[FILESIZE];
  647.  
  648.     fname = ask_file((char *) 0, curbuf->b_fname, fnamebuf);
  649.     read_file(fname, 1);
  650. }
  651.  
  652. #include "temp.h"
  653.  
  654. int    DOLsave = 0;    /* Do Lsave flag.  If lines aren't being save
  655.                when you think they should have been, this
  656.                flag is probably not being set, or is being
  657.                cleared before lsave() was called. */
  658.  
  659. private int    nleft,    /* number of good characters left in current block */
  660.         tmpfd = -1;
  661. private disk_line    DFree = 1;
  662.             /* pointer to end of tmp file */
  663. private char    *tfname;
  664.  
  665. tmpinit()
  666. {
  667.     char    buf[FILESIZE];
  668.  
  669.     sprintf(buf, "%s/%s", TmpFilePath, d_tempfile);
  670.     tfname = copystr(buf);
  671.     tfname = mktemp(tfname);
  672.     (void) close(creat(tfname, 0600));
  673.     tmpfd = open(tfname, 2);
  674.     if (tmpfd == -1)
  675.         complain("Warning: cannot create tmp file!");
  676. }
  677.  
  678. tmpclose()
  679. {
  680.     (void) close(tmpfd);
  681.     tmpfd = -1;
  682.     (void) unlink(tfname);
  683. }
  684.  
  685. /* Get a line at `tl' in the tmp file into `buf' which should be LBSIZE
  686.    long. */
  687.  
  688. int    Jr_Len;        /* Length of Just Read Line. */
  689. private char    *getblock();
  690.  
  691. getline(addr, buf)
  692. disk_line    addr;
  693. register char    *buf;
  694. {
  695.     register char    *bp,
  696.             *lp;
  697.  
  698.     lp = buf;
  699.     bp = getblock(addr >> 1, READ);
  700.     while (*lp++ = *bp++)
  701.         ;
  702.     Jr_Len = (lp - buf) - 1;
  703. }
  704.  
  705. /* Put `buf' and return the disk address */
  706.  
  707. disk_line
  708. putline(buf)
  709. char    *buf;
  710. {
  711.     register char    *bp,
  712.             *lp;
  713.     register int    nl;
  714.     disk_line    free_ptr;
  715.  
  716.     lp = buf;
  717.     free_ptr = DFree;
  718.     bp = getblock(free_ptr, WRITE);
  719.     nl = nleft;
  720.     free_ptr = blk_round(free_ptr);
  721.     while (*bp = *lp++) {
  722.         if (*bp++ == '\n') {
  723.             *--bp = 0;
  724.             break;
  725.         }
  726.         if (--nl == 0) {
  727.             free_ptr = forward_block(free_ptr);
  728.             DFree = free_ptr;
  729.             bp = getblock(free_ptr, WRITE);
  730.             lp = buf;    /* start over ... */
  731.             nl = nleft;
  732.         }
  733.     }
  734.     free_ptr = DFree;
  735.     DFree += (((lp - buf) + CH_SIZE - 1) / CH_SIZE);
  736.              /* (lp - buf) includes the null */
  737.     return (free_ptr << 1);
  738. }
  739.  
  740. /* The theory is that critical section of code inside this procedure
  741.    will never cause a problem to occur.  Basically, we need to ensure
  742.    that two blocks are in memory at the same time, but I think that
  743.    this can never screw up. */
  744.  
  745. #define lockblock(addr)
  746. #define unlockblock(addr)
  747.  
  748. disk_line
  749. f_getputl(line, fp)
  750. Line    *line;
  751. register File    *fp;
  752. {
  753.     register char    *bp;
  754.     register int    c,
  755.             nl,
  756.             max = LBSIZE;
  757.     disk_line    free_ptr;
  758.     char        *base;
  759.  
  760.     free_ptr = DFree;
  761.     base = bp = getblock(free_ptr, WRITE);
  762.     nl = nleft;
  763.     free_ptr = blk_round(free_ptr);
  764.     while (--max > 0) {
  765.         c = getc(fp);
  766.         if (c == EOF || c == '\n')
  767.             break;
  768.         if (--nl == 0) {
  769.             char    *newbp;
  770.             int    nbytes;
  771.  
  772.             lockblock(free_ptr);
  773.             DFree = free_ptr = forward_block(free_ptr);
  774.             nbytes = bp - base;
  775.             newbp = getblock(free_ptr, WRITE);
  776.             nl = nleft;
  777.             byte_copy(base, newbp, nbytes);
  778.             bp = newbp + nbytes;
  779.             base = newbp;
  780.             unlockblock(free_ptr);
  781.         }
  782.         *bp++ = c;
  783.     }
  784.     *bp++ = '\0';
  785.     free_ptr = DFree;
  786.     DFree += (((bp - base) + CH_SIZE - 1) / CH_SIZE);
  787.     line->l_dline = (free_ptr << 1);
  788.     if (max == 0) {
  789.         add_mess(" [Line too long]");
  790.         rbell();
  791.         return EOF;
  792.     }
  793.     if (c == EOF) {
  794.         if (--bp != base)
  795.             add_mess(" [Incomplete last line]");
  796.         return EOF;
  797.     }
  798.     io_lines++;
  799.     return NIL;
  800. }
  801.  
  802. typedef struct block {
  803.     short    b_dirty,
  804.         b_bno;
  805.     char    b_buf[BUFSIZ];
  806.     struct block
  807.         *b_LRUnext,
  808.         *b_LRUprev,
  809.         *b_HASHnext;
  810. } Block;
  811.  
  812. #define HASHSIZE    7    /* Primes work best (so I'm told) */
  813. #define B_HASH(bno)    (bno % HASHSIZE)
  814.  
  815. private Block    b_cache[NBUF],
  816.         *bht[HASHSIZE] = {0},        /* Block hash table */
  817.         *f_block = 0,
  818.         *l_block = 0;
  819. private int    max_bno = -1,
  820.         NBlocks;
  821.  
  822. private int    (*blkio)();
  823.  
  824. private
  825. real_blkio(b, iofcn)
  826. register Block    *b;
  827. register int    (*iofcn)();
  828. {
  829.     (void) lseek(tmpfd, (long) ((unsigned) b->b_bno) * BUFSIZ, 0);
  830.     if ((*iofcn)(tmpfd, b->b_buf, BUFSIZ) != BUFSIZ)
  831.         error("Tmp file %s error.", (iofcn == read) ? "read" : "write");
  832. }
  833.  
  834. private
  835. fake_blkio(b, iofcn)
  836. register Block    *b;
  837. register int    (*iofcn)();
  838. {
  839.     tmpinit();
  840.     blkio = real_blkio;
  841.     real_blkio(b, iofcn);
  842. }
  843.  
  844. d_cache_init()
  845. {
  846.     register Block    *bp,    /* Block pointer */
  847.             **hp;    /* Hash pointer */
  848.     register short    bno;
  849.  
  850.     for (bp = b_cache, bno = NBUF; --bno >= 0; bp++) {
  851.         NBlocks++;
  852.         bp->b_dirty = 0;
  853.         bp->b_bno = bno;
  854.         if (l_block == 0)
  855.             l_block = bp;
  856.         bp->b_LRUprev = 0;
  857.         bp->b_LRUnext = f_block;
  858.         if (f_block != 0)
  859.             f_block->b_LRUprev = bp;
  860.         f_block = bp;
  861.  
  862.         bp->b_HASHnext = *(hp = &bht[B_HASH(bno)]);
  863.         *hp = bp;
  864.     }
  865.     blkio = fake_blkio;
  866. }
  867.  
  868. SyncTmp()
  869. {
  870. #ifdef MSDOS
  871.     register int    bno = 0;
  872.     BLock *lookup();
  873.  
  874.     for (bno = 0; bno <= max_bno; )
  875.         (*blkio)(lookup(bno++), write);
  876. #else
  877.     register Block    *b;
  878.  
  879.     for (b = f_block; b != 0; b = b->b_LRUnext)
  880.         if (b->b_dirty) {
  881.             (*blkio)(b, write);
  882.             b->b_dirty = 0;
  883.         }
  884. #endif
  885. }
  886.  
  887. private Block *
  888. lookup(bno)
  889. register short    bno;
  890. {
  891.     register Block    *bp;
  892.  
  893.     for (bp = bht[B_HASH(bno)]; bp != 0; bp = bp->b_HASHnext)
  894.         if (bp->b_bno == bno)
  895.             break;
  896.     return bp;
  897. }
  898.  
  899. private
  900. LRUunlink(b)
  901. register Block    *b;
  902. {
  903.     if (b->b_LRUprev == 0)
  904.         f_block = b->b_LRUnext;
  905.     else
  906.         b->b_LRUprev->b_LRUnext = b->b_LRUnext;
  907.     if (b->b_LRUnext == 0)
  908.         l_block = b->b_LRUprev;
  909.     else
  910.         b->b_LRUnext->b_LRUprev = b->b_LRUprev;
  911. }
  912.  
  913. private Block *
  914. b_unlink(bp)
  915. register Block    *bp;
  916. {
  917.     register Block    *hp,
  918.             *prev = 0;
  919.  
  920.     LRUunlink(bp);
  921.     /* Now that we have the block, we remove it from its position
  922.        in the hash table, so we can THEN put it somewhere else with
  923.        it's new block assignment. */
  924.  
  925.     for (hp = bht[B_HASH(bp->b_bno)]; hp != 0; prev = hp, hp = hp->b_HASHnext)
  926.         if (hp == bp)
  927.             break;
  928.     if (hp == 0) {
  929.         printf("\rBlock %d missing!", bp->b_bno);
  930.         finish(0);
  931.     }
  932.     if (prev)
  933.         prev->b_HASHnext = hp->b_HASHnext;
  934.     else
  935.         bht[B_HASH(bp->b_bno)] = hp->b_HASHnext;
  936.  
  937.     if (bp->b_dirty) {    /* Do, now, the delayed write */
  938.         (*blkio)(bp, write);
  939.         bp->b_dirty = 0;
  940.     }
  941.  
  942.     return bp;
  943. }
  944.  
  945. /* Get a block which contains at least part of the line with the address
  946.    atl.  Returns a pointer to the block and sets the global variable
  947.    nleft (number of good characters left in the buffer). */
  948.  
  949. private char *
  950. getblock(atl, iof)
  951. disk_line    atl;
  952. {
  953.     register int    bno,
  954.             off;
  955.     register Block    *bp;
  956.     static Block    *lastb = 0;
  957.  
  958.     bno = daddr_to_bno(atl);
  959.     off = daddr_to_off(atl);
  960.     if (bno >= MAX_BLOCKS)
  961.         error("Tmp file too large.  Get help!");
  962.     nleft = BUFSIZ - off;
  963.     if (lastb != 0 && lastb->b_bno == bno) {
  964.         lastb->b_dirty |= iof;
  965.         return lastb->b_buf + off;
  966.     }
  967.  
  968.     /* The requested block already lives in memory, so we move
  969.        it to the end of the LRU list (making it Most Recently Used)
  970.        and then return a pointer to it. */
  971.     if (bp = lookup(bno)) {
  972.         if (bp != l_block) {
  973.             LRUunlink(bp);
  974.             if (l_block == 0)
  975.                 f_block = l_block = bp;
  976.             else
  977.                 l_block->b_LRUnext = bp;
  978.             bp->b_LRUprev = l_block;
  979.             l_block = bp;
  980.             bp->b_LRUnext = 0;
  981.         }
  982.         if (bp->b_bno > max_bno)
  983.             max_bno = bp->b_bno;
  984.         bp->b_dirty |= iof;
  985.         lastb = bp;
  986.         return bp->b_buf + off;
  987.     }
  988.  
  989.     /* The block we want doesn't reside in memory so we take the
  990.        least recently used clean block (if there is one) and use
  991.        it.  */
  992.     bp = f_block;
  993.     if (bp->b_dirty)    /* The best block is dirty ... */
  994.         SyncTmp();
  995.  
  996.     bp = b_unlink(bp);
  997.     if (l_block == 0)
  998.         l_block = f_block = bp;
  999.     else
  1000.         l_block->b_LRUnext = bp;    /* Place it at the end ... */
  1001.     bp->b_LRUprev = l_block;
  1002.     l_block = bp;
  1003.     bp->b_LRUnext = 0;        /* so it's Most Recently Used */
  1004.  
  1005.     bp->b_dirty = iof;
  1006.     bp->b_bno = bno;
  1007.     bp->b_HASHnext = bht[B_HASH(bno)];
  1008.     bht[B_HASH(bno)] = bp;
  1009.  
  1010.     /* Get the current contents of the block UNLESS this is a new
  1011.        block that's never been looked at before, i.e., it's past
  1012.        the end of the tmp file. */
  1013.  
  1014.     if (bp->b_bno <= max_bno)
  1015.         (*blkio)(bp, read);
  1016.     else
  1017.         max_bno = bno;
  1018.  
  1019.     lastb = bp;
  1020.     return bp->b_buf + off;
  1021. }
  1022.  
  1023. char *
  1024. lbptr(line)
  1025. Line    *line;
  1026. {
  1027.     return getblock(line->l_dline >> 1, READ);
  1028. }
  1029.  
  1030. /* save the current contents of linebuf, if it has changed */
  1031.  
  1032. lsave()
  1033. {
  1034.     if (curbuf == 0 || !DOLsave)    /* Nothing modified recently */
  1035.         return;
  1036.  
  1037.     if (strcmp(lbptr(curline), linebuf) != 0)
  1038.         SavLine(curline, linebuf);    /* Put linebuf on the disk. */
  1039.     DOLsave = 0;
  1040. }
  1041.  
  1042. #ifdef BACKUPFILES
  1043. file_backup(fname)
  1044. char *fname;
  1045. {
  1046.     char    *s;
  1047.     register int    i;
  1048.     int    fd1,
  1049.         fd2;
  1050.     char    tmp1[BUFSIZ],
  1051.         tmp2[BUFSIZ];
  1052.     
  1053.     strcpy(tmp1, fname);
  1054.     
  1055.     if ((s = rindex(tmp1, '/')) == NULL)
  1056.         sprintf(tmp2, "#%s", fname);
  1057.     else {
  1058.         *s++ = '\0';
  1059.         sprintf(tmp2, "%s/#%s", tmp1, s);
  1060.     }
  1061.  
  1062.     if ((fd1 = open(fname, 0)) < 0)
  1063.         return;
  1064.  
  1065.     if ((fd2 = creat(tmp2, CreatMode)) < 0) {
  1066.         (void) close(fd1);
  1067.         return;
  1068.     }
  1069.  
  1070.     while ((i = read(fd1, tmp1, sizeof(tmp1))) > 0)
  1071.         write(fd2, tmp1, i);
  1072.  
  1073. #ifdef BSD4_2
  1074.     (void) fsync(fd2);
  1075. #endif
  1076.     (void) close(fd2);
  1077.     (void) close(fd1);
  1078. }
  1079. #endif
  1080.