home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_300 / 318_01 / redbuf2.c < prev    next >
C/C++ Source or Header  |  1990-06-18  |  22KB  |  1,036 lines

  1. /*
  2.     RED buffer routines -- Full C version
  3.     Part 2 -- line routines.
  4.  
  5.     Source:  redbuf2.c
  6.     Version: February 20, 1984; January 18, 1990.
  7.  
  8.     Written by
  9.     
  10.         Edward K. Ream
  11.         166 N. Prospect
  12.         Madison WI 53705
  13.         (608) 257-0802
  14.  
  15.  
  16.     PUBLIC DOMAIN SOFTWARE
  17.  
  18.     This software is in the public domain.
  19.  
  20.     See red.h for a disclaimer of warranties and other information.
  21. */
  22.  
  23. #include "red.h"
  24.  
  25. /*
  26.     Declare routines local to this file.
  27. */
  28. static void        del_bl        (int nlines);
  29. static void        del_mid        (int dlines);
  30. static void        del_tail    (int slines, int nlines);
  31. static void         combine1    (int diskp1, int diskp2);
  32. static void        free_block    (struct BLOCK * bp);
  33. static struct BLOCK *    new_block    (void);
  34. static struct BLOCK *    split_block    (void);
  35.  
  36. /*
  37.     b_getnum(char_ptr)
  38.     Get an integer given a pointer to char.
  39.  
  40.     b_putnum(char_ptr)
  41.     Put an integer given a pointer to char.
  42. */
  43.  
  44. #if 0 /* The following are all defined as macros in redbuf.h */
  45.  
  46. int
  47. b_getnum(char *p)
  48. {
  49.     TRACEP("b_getnum",
  50.         sl_lpout(); sl_pout(p);
  51.         sl_sout(") returns ");
  52.         sl_iout( * (int *) p);
  53.         sl_cout('\n'));
  54.  
  55.     return  (* (int *) p);
  56. }
  57.  
  58. void
  59. b_putnum(char *p, int num)
  60. {
  61.      TRACEP("b_putnum",  sl_lpout();
  62.         sl_sout("* (int *) (");
  63.         sl_pout(p);
  64.         sl_sout(") = ");
  65.         sl_iout(num); 
  66.         sl_cout('\n'));
  67.  
  68.      * (int *) p = num;
  69. }
  70.  
  71. /*
  72.     The following routines manage the index table,
  73.     which grows down from the end of each block.
  74.  
  75.     b_tab(bp, index)
  76.     b1_tab(index)
  77.     Get an entry of the index table.
  78.  
  79.     b_settab(bp, index, value)
  80.     b1_settab(index, value)
  81.     Set an entry of the index table.
  82. */
  83. int
  84. b1_tab(int index)
  85. {
  86.     TRACEP("b1_tab", sl_sout(":: "));
  87.  
  88.     b_tab(b_bp, index);
  89. }
  90.  
  91. int
  92. b_tab(struct BLOCK *bp, int index)
  93. {
  94.     TRACEP("b_tab",  sl_lpout();
  95.         sl_pout(bp); sl_csout();
  96.         sl_iout(index); 
  97.         sl_sout(") :: "));
  98.  
  99.     b_getnum(bp->d_data + BUFF_SIZE - (sizeof(int)*(index+1)));
  100. }
  101.  
  102. void
  103. b1_settab(int index, int value)
  104. {
  105.     TRACEP("b1_settab", sl_sout(":: "));
  106.  
  107.     b_settab(b_bp, index, value);
  108. }
  109.  
  110. void
  111. b_settab(struct BLOCK  *bp, int index, int value)
  112. {
  113.     TRACEP("b_settab", sl_lpout();
  114.         sl_pout(bp); sl_csout();
  115.         sl_iout(index); sl_csout(); 
  116.         sl_iout(value);
  117.         sl_sout(") :: "));
  118.  
  119.     b_putnum(bp->d_data + BUFF_SIZE - (sizeof(int)*(index+1)),
  120.          value);
  121. }
  122.  
  123. /*
  124.     b_len(bp, line)
  125.     b1_len(line)
  126.     Return the length of a line.
  127.  
  128.     b_ptr(bp, line)
  129.     b1_ptr(line)
  130.     Return a pointer to a line.
  131.  
  132.     b_prefix(bp, line)
  133.     b1_prefix(line)
  134.     Return the number of chars befor the start of a line.
  135. */
  136. int
  137. b1_length(int line)
  138. {
  139.     TRACEP("b1_length", sl_sout(":: "));
  140.  
  141.     b_length(b_bp, line);
  142. }
  143.  
  144. int
  145. b_length(struct BLOCK *bp, int line)
  146. {
  147.     TRACEP("b_length",  sl_lpout();
  148.         sl_pout(bp); sl_csout(); sl_iout(line);
  149.         sl_sout(") :: "));
  150.  
  151.     if (line < 0 || line >= bp -> d_lines) {
  152.         cant_happen("b_len");
  153.     }
  154.  
  155.     if (bufatbot()) {
  156.         /* The last line is always null. */
  157.         TRACE("b_length", sl_iout(0); sl_cout('\n'));
  158.         return 0;
  159.     }
  160.     else if (line == 0) {
  161.         TRACE("b_length", sl_iout(b_tab(bp, line)); sl_cout('\n'));
  162.         return b_tab(bp, line);
  163.     }
  164.     else {
  165.         TRACE("b_length",
  166.             sl_iout(b_tab(bp, line) - b_tab(bp, line - 1));
  167.             sl_cout('\n'));
  168.  
  169.         return (b_tab(bp, line) - b_tab(bp, line - 1));
  170.     }
  171. }
  172.         
  173. char *
  174. b1_ptr(int line)
  175. {
  176.     TRACEP("b1_ptr", sl_sout(":: "));
  177.  
  178.     b_ptr(b_bp, line);
  179. }
  180.  
  181. char *
  182. b_ptr(struct BLOCK  *bp, int line)
  183. {
  184.     TRACEP("b_ptr",  sl_lpout();
  185.         sl_pout(bp); sl_csout(); sl_iout(line);
  186.         sl_sout(") :: "));
  187.  
  188.     if (line < 0 || line >= bp -> d_lines) {
  189.         cant_happen("b_ptr");
  190.     }
  191.     if (line == 0) {
  192.         TRACE("b_ptr", sl_pout(bp -> d_data); sl_cout('\n'));
  193.         return bp -> d_data;
  194.     }
  195.     else {
  196.         TRACE("b_ptr",
  197.             sl_pout(bp -> d_data + b_tab(bp, line - 1));
  198.             sl_cout('\n'));
  199.         return (bp -> d_data + b_tab(bp, line - 1));
  200.     }
  201. }
  202.  
  203. int
  204. b1_prefix(int line)
  205. {
  206.     TRACEP("b1_prefix", sl_sout(":: "));
  207.  
  208.     return b_prefix(b_bp, line);
  209. }
  210.  
  211. int
  212. b_prefix(struct BLOCK *bp, int line)
  213. {
  214.     TRACEP("b_prefix",  sl_lpout();
  215.         sl_pout(bp); sl_csout();
  216.         sl_iout(line);
  217.         sl_sout(") :: "));
  218.  
  219.     if (line < 0 || line >= bp -> d_lines) {
  220.         cant_happen("b_prefix");
  221.     }
  222.     if (line == 0) {
  223.         TRACE("b_prefix", sl_iout(0); sl_cout('\n'));
  224.         return 0;
  225.     }
  226.     else {
  227.         TRACE("b_prefix",
  228.             sl_iout(b_tab (bp, line - 1));
  229.             sl_cout('\n'));
  230.         return (b_tab (bp, line - 1));
  231.     }
  232. }
  233.  
  234. /*
  235.     The following routines make accessing fields of blocks
  236.     a bit easier.  They also save a few bytes of memory.
  237.  
  238.     b1_nlines()
  239.     b_nlines(bp)
  240.     Return the number of lines in a block.
  241.  
  242.     b1_avail()
  243.     b1_avail(bp)
  244.     Return the number of free characters in a block.
  245. */
  246.  
  247. int
  248. b1_nlines(void)
  249. {
  250.     TRACEP("b1_nlines",
  251.         sl_sout("returns ");
  252.         sl_iout(b_bp -> d_lines);
  253.         sl_cout('\n'));
  254.  
  255.     return b_bp -> d_lines;
  256. }
  257.  
  258. int
  259. b_nlines(struct BLOCK *bp)
  260. {
  261.     TRACEP("b_nlines",
  262.         sl_lpout();
  263.         sl_pout(bp);
  264.         sl_sout(") returns ");
  265.         sl_iout(bp -> d_lines);
  266.         sl_cout('\n'));
  267.  
  268.     return bp -> d_lines;
  269. }
  270.  
  271. int
  272. b1_avail(void)
  273. {
  274.     TRACEP("b1_avail", sl_sout(" :: "));
  275.  
  276.     return b_avail(b_bp);
  277. }
  278.  
  279. int
  280. b_avail(struct BLOCK *bp)
  281. {
  282.     TRACEP("b_avail",
  283.         sl_lpout();
  284.         sl_pout(bp);
  285.         sl_sout(") returns "));
  286.  
  287.     if (bp -> d_lines == 0) {
  288.         TRACE("b_avail", sl_iout(BUFF_SIZE); sl_cout('\n'));
  289.         return BUFF_SIZE;
  290.     }
  291.     else {
  292.         TRACE("b_avail",
  293.             sl_iout(BUFF_SIZE - b_tab(bp, bp->d_lines-1) -
  294.                    (sizeof(int) * bp->d_lines));
  295.             sl_cout('\n'));
  296.  
  297.         return (BUFF_SIZE - b_tab(bp, bp->d_lines-1) -
  298.                (sizeof(int) * bp->d_lines));
  299.     }
  300. }
  301.  
  302. #endif /* 0 */
  303.  
  304. /*
  305.     Delete the current line.
  306. */
  307. void
  308. bufdel(void)
  309. {
  310.     TICKB("bufdel");
  311.  
  312.     buf_d(1);
  313.     combine();
  314.  
  315.     TICKX("bufdel");
  316. }
  317.  
  318.  
  319. /*
  320.     Delete multiple lines starting with the current line.
  321.     nlines is the number of lines to delete.
  322. */
  323. void
  324. bufdeln(int nlines)
  325. {
  326.     TRACEPB("bufdeln",  sl_lpout(); sl_iout(nlines); sl_rpout());
  327.  
  328.     buf_d(nlines);
  329.     combine();
  330.  
  331.     TICKX("bufdeln");
  332. }
  333.     
  334. /*
  335.     Internal delete routine.  
  336.  
  337.     This routine does not combine blocks so that bufrepl()
  338.     can wait to the last possible moment to call combine().
  339. */
  340. void
  341. buf_d(int nlines)
  342. {
  343.     int lines, slines;
  344.  
  345.     TRACEPB("buf_d", sl_lpout(); sl_iout(nlines); sl_rpout());
  346.  
  347.     while (nlines > 0 && !bufatbot()) {
  348.  
  349.         /* The current block will become dirty. */
  350.         is_dirty(b_bp);
  351.         b_cflag = TRUE;
  352.  
  353.         /*
  354.             Precompute the number of lines in the
  355.             block and the number of lines befor
  356.             the current line.
  357.         */
  358.  
  359.         lines  = b_bp -> d_lines;
  360.         slines = b_line - b_start;
  361.  
  362.         /* Delete lines block by block. */
  363.         if (slines == 0 && nlines >= lines) {
  364.  
  365.             /* Delete the whole block. */
  366.             del_bl(lines);
  367.             nlines -= lines;
  368.             check_block("delete 1");
  369.         }
  370.         else if (nlines >= lines - slines) {
  371.  
  372.             /* Delete tail of the block. */
  373.             del_tail(slines, lines - slines);
  374.             nlines -= (lines - slines);
  375.             check_block("delete 2");
  376.         }
  377.         else {
  378.             /* Delete from middle of the block. */
  379.             del_mid(nlines);
  380.             check_block("delete 3");
  381.             break;
  382.         }
  383.     }
  384.  
  385.     TICKX("buf_d");
  386. }
  387.  
  388. /*
  389.     Delete the current block containing 'nlines' lines.
  390.  
  391.     There is much more to this code than meets the eye.
  392.     First,  notice that using combine() here would slow
  393.     down the a  multiple-line  delete  much  too  much.
  394.     Second,  we never want to call free_block() for the
  395.     first block since we want any recovery  program  to
  396.     find  the  first block of the work file at block 0.
  397.     Third,  the  call  to  combine()  at the end of the
  398.     bufdeln() routine will eventually fill up  block  0
  399.     unless the entire file is deleted.
  400. */
  401. static void
  402. del_bl(int nlines)
  403. {
  404.     struct BLOCK *bp1, *oldblock;
  405.     int back, next, current;
  406.  
  407.     TRACEPB("del_bl",  sl_lpout(); sl_iout(nlines); sl_rpout());
  408.  
  409.     /* Point to the previous and next blocks. */
  410.     back    = b_bp -> d_back;
  411.     next    = b_bp -> d_next;
  412.     current = b_bp -> d_diskp;
  413.  
  414.     /* Remember the current slot. */
  415.     oldblock = b_bp;
  416.  
  417.     /* Special case block 0. */
  418.     if (current == 0) {
  419.  
  420.         TRACEP("del_bl",
  421.             sl_sout("***** zero FIRST block 0\n"));
  422.  
  423.         /* Zero the block but do not actually delete. */
  424.         b_bp -> d_lines = 0;
  425.         is_dirty(oldblock);
  426.  
  427.         /* Move to next block. */
  428.         if (next != ERROR) {
  429.             b_bp = swap_in(next);
  430.         }
  431.         b_start = b_line = 1;
  432.     }
  433.     else if (next != ERROR) {
  434.  
  435.         TRACEP("del_bl",
  436.             sl_sout("***** free next block ");
  437.             sl_iout(next); sl_cout('\n'));
  438.  
  439.         /* Move to the START of the next block. */
  440.         b_bp    = swap_in(next);
  441.         b_start = b_line;
  442.  
  443.         /* Adjust back pointer of new block. */
  444.         b_bp -> d_back = back;
  445.         is_dirty(b_bp);
  446.  
  447.         /* Adjust next pointer. */
  448.         bp1 = swap_in(back);
  449.         bp1 -> d_next = next;
  450.         is_dirty(bp1);
  451.  
  452.         /* Actually delete the old block. */
  453.         free_block(oldblock);
  454.     }
  455.     else if (back != ERROR) {
  456.  
  457.         TRACEP("del_bl",
  458.             sl_sout("***** free previous block ");
  459.             sl_iout(next); sl_cout('\n'));
  460.  
  461.         /*
  462.             Move the the END of the previous block.
  463.             Set bufatbot() true.
  464.         */
  465.         b_bp     = swap_in(back);
  466.         b_start -= b_bp -> d_lines;
  467.         b_line   = b_start + b_bp -> d_lines;
  468.  
  469.         /* Adjust forward pointer of new block. */
  470.         b_bp -> d_next = next;
  471.         is_dirty(b_bp);
  472.  
  473.         /* Adjust pointer to the last block. */
  474.         b_tail = back;
  475.  
  476.         /* Actually delete the old slot. */
  477.         free_block(oldblock);
  478.     }
  479.     else {
  480.         /* Only block 0 has both links == ERROR. */
  481.         cant_happen("del_bl");
  482.     }
  483.  
  484.     /* Adjust total number of lines. */
  485.     b_max_line -= nlines;
  486.  
  487.     TICKX("del_bl");
  488. }
  489.  
  490. /*
  491.     Delete dlines from the current block.
  492.     There is at least one line after the deleted lines.
  493. */
  494. static void
  495. del_mid(int dlines)
  496. {
  497.     char * source, * dest;
  498.     int  i, length, limit, line, nlines, offset;
  499.  
  500.     TRACEPB("del_mid", sl_lpout(); sl_iout(dlines); sl_rpout());
  501.  
  502.     /* Compute some constants. */
  503.     line   = b_line - b_start;
  504.     nlines = b_bp -> d_lines;
  505.  
  506.     /* Compress the block. */
  507.     source = b1_ptr(line + dlines);
  508.     dest   = b1_ptr(line);
  509.     length = b1_tab(nlines - 1) - b1_tab(line + dlines - 1);
  510.  
  511.     TRACEP("del_mid", sl_sout("***** compressing block with sysmove\n"));
  512.  
  513.     sysmove(source, dest, length);
  514.  
  515.     /* Compress the index table. */
  516.     offset = b1_prefix(line + dlines) - b1_prefix(line);
  517.     limit  = nlines - dlines;
  518.     for (i = line; i < limit; i++) {
  519.         b1_settab(i, b1_tab(i + dlines) - offset);
  520.     }
  521.  
  522.     /* Adjust the counts. */
  523.     b_bp -> d_lines -= dlines;
  524.     b_max_line      -= dlines;
  525.  
  526.     TRACE("v", buftrace());
  527.  
  528.     TICKX("del_mid");
  529. }
  530.  
  531. /*
  532.     Delete the current line and all followings lines in
  533.     the current block.
  534.     slines is the number of preceeding lines in the block.
  535. */
  536. static void
  537. del_tail(int slines, int nlines)
  538. {
  539.     int next;
  540.  
  541.     TRACEPB("del_tail", sl_lpout();
  542.         sl_iout(slines); sl_csout();
  543.         sl_iout(nlines); sl_rpout());
  544.  
  545.     /* Adjust the line count. */
  546.     b_bp -> d_lines = slines;
  547.     is_dirty(b_bp);
  548.  
  549.     next = b_bp -> d_next;
  550.     if (next != ERROR) {
  551.  
  552.         TRACEP("del_tail",
  553.             sl_sout("***** current block becomes ");
  554.             sl_iout(next); sl_cout('\n'));
  555.  
  556.         /* Move the current block forward. */
  557.         b_bp    = swap_in(next);
  558.         b_start = b_line;
  559.     }
  560.  
  561.     /* Adjust the total number of lines. */
  562.     b_max_line -= nlines;
  563.  
  564.     TICKX("del_tail");
  565. }
  566.  
  567. /*
  568.     Copy the current line from the buffer to line [].
  569.     The size of line [] is linelen.
  570.     Return k = the length of the line.
  571.     If k > linelen then truncate k - linelen characters.
  572. */
  573. int
  574. bufgetln(char *line, int linelen)
  575. {
  576.     int    count, limit;
  577.     char    *src;
  578.  
  579.     TRACEPB("bufgetln", sl_lpout();
  580.         sl_pout(line);    sl_csout();
  581.         sl_iout(linelen); sl_rpout());
  582.  
  583.     /* Return null line at the bottom of the buffer. */
  584.     if (bufatbot()) {
  585.         line [0] = '\n';
  586.         RETURN_INT("bufgetln", 0);
  587.     }
  588.  
  589.     /* Copy line to buffer. */
  590.     src   = b1_ptr    (b_line - b_start);
  591.     count = b1_length (b_line - b_start);
  592.     limit = min(count, linelen - 1);
  593.     sysmove(src, line, limit);
  594.     
  595.     /* End with '\n'. */
  596.     line [limit] = '\n';
  597.  
  598.     /* Return the number of characters in the line. */
  599.  
  600.     RETURN_INT("bufgetln", count);
  601. }
  602.  
  603. /*
  604.     Insert line before the current line.  Thus, the line
  605.     number of the current line does not change.  The line
  606.     ends with '\n'.
  607.  
  608.     This is fairly crude code, as it can end up splitting
  609.     the current block into up to three blocks.  However,
  610.     the combine() routine does an effective job of keeping
  611.     the size of the average block big enough.
  612. */
  613. void
  614. bufins(char insline [], int inslen)
  615. {
  616.     struct BLOCK *bp2;
  617.     char *dest, *source;
  618.     int  i, length, line, nlines, prefix;
  619.  
  620.     TRACEPB("bufins",  sl_lpout();
  621.         sl_pout(insline); sl_csout(); sl_iout(inslen);
  622.         sl_sout(") block: "); sl_iout(b_bp -> d_diskp);
  623.         sl_sout(", line: ");  sl_iout(b_line);
  624.         sl_cout('\n'));
  625.  
  626.     if (inslen > BUFF_SIZE) {
  627.         cant_happen("bufins 1");
  628.     }
  629.  
  630.     /* See if the new line will fit. */
  631.     if (inslen + sizeof(int) > b1_avail()) {
  632.  
  633.         TRACEP("bufins", sl_sout("***** splitting block\n"));
  634.         TRACE("v", trace_vars());
  635.  
  636.         /* Split off the trailing lines. */
  637.         bp2 = split_block();
  638.  
  639.         /* See if there is enough room in the old block. */
  640.         if (inslen + sizeof(int) > b1_avail()) {
  641.  
  642.             /* Move on to the next block. */
  643.             b_start += b_bp -> d_lines;
  644.             b_bp     = bp2;
  645.  
  646.             /*
  647.             At this point,  the new line is the
  648.             the first line of the block.  Alas,
  649.             we may still have to split off the
  650.             trailing lines so the new line will fit.
  651.             */
  652.  
  653.             if (inslen + sizeof(int) > b1_avail()) {
  654.  
  655.         TRACEP("bufins", sl_sout("***** splitting block again\n"));
  656.         TRACE("v", trace_vars());
  657.  
  658.                 /* Split the block a second time. */
  659.                 split_block();
  660.             }
  661.         }
  662.     }
  663.  
  664.     if (inslen + sizeof(int) > b1_avail()) {
  665.         cant_happen("bufins 2");
  666.     }
  667.  
  668.  
  669.     /*
  670.         At this point,  we know that the new line can
  671.         be inserted before the current line with room
  672.         for any following lines.
  673.     */
  674.  
  675.     is_dirty(b_bp);
  676.     line   = b_line - b_start;
  677.     nlines = b_bp -> d_lines;
  678.  
  679.     if (nlines == 0) {
  680.  
  681.         TRACEP("bufins",
  682.             sl_sout("***** copy line to empty block\n"));
  683.  
  684.         /* Copy line to empty block. */
  685.         sysmove(insline, b_bp -> d_data, inslen);
  686.  
  687.         /* Create an index table. */
  688.         b1_settab(0, inslen);
  689.     }
  690.     else if (line >= nlines) {
  691.  
  692.         TRACEP("bufins", sl_sout("***** copy line to end of block\n"));
  693.  
  694.         /* Copy line to end of block. */
  695.         sysmove(insline,
  696.             b_bp -> d_data + b1_tab(nlines - 1),
  697.             inslen);
  698.  
  699.         /* Append index to index table. */
  700.         b1_settab(nlines, b1_tab(nlines - 1) + inslen);
  701.     }
  702.     else {
  703.  
  704.         TRACEP("bufins",
  705.           sl_sout("***** make a hole at current line with sysmove\n"));
  706.  
  707.         /* Make a hole at the current line. */
  708.         source = b1_ptr(line);
  709.         dest   = b1_ptr(line) + inslen;
  710.         prefix = b1_prefix(line);
  711.         length = b1_tab(nlines - 1) - prefix;
  712.         sysmove(source, dest, length);
  713.  
  714.         /* Copy the new line into the hole. */
  715.         sysmove(insline, source, inslen);
  716.  
  717.         /* Make a hole in the index table. */
  718.         for (i = nlines; i > line; i--) {
  719.             b1_settab(i, b1_tab(i - 1) + inslen);
  720.         }
  721.  
  722.         /* Copy the new index into the hole. */
  723.         b1_settab(line, prefix + inslen);
  724.     }
  725.         
  726.     /*
  727.         Special case: inserting a null line at the
  728.          end of the file is not a significant change.
  729.     */
  730.     if ( ! (inslen == 0 && bufnrbot()) ) {
  731.         b_cflag = TRUE;
  732.     }
  733.  
  734.     /* Bump the counts. */
  735.     b_bp -> d_lines++;
  736.     b_max_line++;
  737.  
  738.     /*
  739.         It DOES make sense to call combine here.
  740.         In the most common case,  after a split the
  741.         current block might be small enough to be
  742.         combined with the preceding block.
  743.     */
  744.     TRACEP("bufins", sl_sout("***** combine at end of insert\n"));
  745.     TRACE("v", trace_vars());
  746.  
  747.     combine();
  748.  
  749.     /* Check the format of the block. */
  750.     check_block("bufins");
  751.  
  752.     TRACE("v", buftrace());
  753.  
  754.     TICKX("bufins");
  755. }
  756.  
  757.  
  758. /*
  759.     Combine the current block with the preceeding and
  760.     following blocks if possible.
  761.     Make the new block the current bloc.
  762. */
  763. void
  764. combine(void)
  765. {
  766.     TICKB("combine");
  767.  
  768.     combine1(b_bp -> d_back,  b_bp -> d_diskp);
  769.     combine1(b_bp -> d_diskp, b_bp -> d_next);
  770.  
  771.     TICKX("combine");
  772. }
  773.  
  774.  
  775. /*
  776.     Combine two blocks into one if possible.
  777.     Make the new block the current block.
  778.  
  779.     Note that nline1 can be 0 because of the special case
  780.     code in the del_bl() routine.  nline2 is never 0.
  781. */
  782. static void
  783. combine1(int diskp1, int diskp2)
  784. {
  785.     struct BLOCK *bp1, *bp2, *bp3;
  786.     char    *source, *dest;
  787.     int    i, length, limit, nlines1, nlines2, offset;
  788.  
  789.     SL_DISABLE();
  790.  
  791.     /* Make sure the call makes sense. */
  792.     if (diskp1 == ERROR || diskp2 == ERROR) {
  793.         return;
  794.     }
  795.  
  796.     TRACEPB("combine1",  sl_lpout();
  797.         sl_iout(diskp1); sl_csout();
  798.         sl_iout(diskp2); sl_rpout());
  799.  
  800.     /* Get the two blocks. */
  801.     TRACEP("combine1", sl_sout("***** swap in two blocks...\n"));
  802.  
  803.     bp1 = swap_in(diskp1);
  804.     bp2 = swap_in(diskp2);
  805.  
  806.     if ( bp1 -> d_next != diskp2 ||
  807.          bp2 -> d_back != diskp1
  808.        ) {
  809.         cant_happen("combine 1");
  810.     }
  811.     
  812.     /* Do nothing if the blocks are too large. */
  813.     if (b_avail(bp1) + b_avail(bp2) < BUFF_SIZE) {
  814.         RETURN_VOID("combine1");
  815.     }
  816.  
  817.     /* Compute the number of lines in each block. */
  818.     nlines1 = bp1 -> d_lines;
  819.     nlines2 = bp2 -> d_lines;
  820.     if (nlines2 <= 0) {
  821.         cant_happen("combine");
  822.     }
  823.  
  824.     /* Copy buffer 2 to end of buffer 1. */
  825.  
  826.     TRACEP("combine1",
  827.         sl_sout("***** copy buffer "); sl_iout(diskp1);
  828.         sl_sout(" to end of buffer "); sl_iout(diskp2);
  829.         sl_sout("with sysmove\n"));
  830.  
  831.     source = bp2 -> d_data;
  832.     if (nlines1 == 0) {
  833.         dest   = bp1 -> d_data;
  834.         offset = 0;
  835.     }
  836.     else {
  837.         dest   = bp1 -> d_data + b_tab(bp1, nlines1-1);
  838.         offset = b_tab(bp1, nlines1 - 1);
  839.     }
  840.     length = b_tab(bp2, nlines2 - 1);
  841.     sysmove(source, dest, length);
  842.  
  843.     /* Copy table 2 to table 1. */
  844.     for (i = 0; i < nlines2; i++) {
  845.         b_settab(bp1, i + nlines1,
  846.              b_tab(bp2, i) + offset);
  847.     }
  848.  
  849.     /* Both blocks are now dirty. */
  850.     is_dirty(bp1);
  851.     is_dirty(bp2);
  852.  
  853.     /* Adjust the back pointer of the next block. */
  854.     if (bp2 -> d_next != ERROR) {
  855.  
  856.         TRACEP("combine1",
  857.             sl_sout("***** swap in next block.  Adjust back ptr\n"));
  858.  
  859.         bp3 = swap_in(bp2 -> d_next);
  860.         bp3 -> d_back = bp1 -> d_diskp;
  861.         is_dirty(bp3);
  862.     }
  863.  
  864.     /*
  865.         Adjust the current block if needed.
  866.          The value of b_start must be decremented
  867.          by the OLD value of bp1 -> d_lines.
  868.     */
  869.  
  870.     if (b_bp == bp2) {
  871.         b_bp     = bp1;
  872.         b_start -= bp1 -> d_lines;
  873.     }
  874.  
  875.     /* Adjust the header for block 1. */
  876.     bp1 -> d_lines += bp2 -> d_lines;
  877.     bp1 -> d_next   = bp2 -> d_next;
  878.  
  879.     /* Adjust the pointers to the last block. */
  880.     if (diskp2 == b_tail) {
  881.         b_tail = diskp1;
  882.     }
  883.  
  884.     /* Slot 2 must remain in core until this point. */
  885.     free_block(bp2);
  886.  
  887.     /* Check the format of the block. */
  888.     check_block("combine");
  889.  
  890.     TICKX("combine1");
  891. }
  892.  
  893. /*
  894.     Put the block in the slot on the free list.
  895. */
  896. static void
  897. free_block(struct BLOCK * bp)
  898. {
  899.     TRACEPB("free_block",  sl_lpout(); sl_pout(bp); sl_rpout());
  900.  
  901.     /*
  902.         Each block in the free list contains the
  903.         pointer to the next block in the d_next field.
  904.     */
  905.     bp -> d_next = b_free;
  906.     b_free = bp -> d_diskp;
  907.  
  908.     /* Erase the block. */
  909.     bp -> d_lines = 0;
  910.  
  911.     /* Make sure the block is rewritten. */
  912.     is_dirty(bp);
  913.  
  914.     TICKX("free_block");
  915. }
  916.  
  917. /*
  918.     Create a new block linked after the current block.
  919.     Return a pointer to the new block.
  920. */
  921. static struct BLOCK *
  922. new_block (void)
  923. {
  924.     struct BLOCK *bp1, *bp2;
  925.     int diskp;
  926.  
  927.     TICKB("new_block");
  928.  
  929.     /* Get a free disk sector. */
  930.     if (b_free != ERROR) {
  931.  
  932.         TRACEP("new_block",
  933.             sl_sout("***** get new block from free list\n"));
  934.  
  935.         /* Take the first block on the free list. */
  936.         diskp = b_free;
  937.  
  938.         /* Put the block in a free slot. */
  939.         bp1 = swap_in(diskp);
  940.  
  941.         /* Adjust the head of the free list. */
  942.         b_free = bp1 -> d_next;
  943.     }    
  944.     else {
  945.         TRACEP("new_block",
  946.             sl_sout("***** allocate brand new block \n"));
  947.  
  948.         /* Get a free slot. */
  949.         diskp = ++b_max_diskp;
  950.         bp1   = swap_new(diskp);
  951.     }
  952.  
  953.     /* Link the new block after the current block. */
  954.     bp1  -> d_next = b_bp -> d_next;
  955.     bp1  -> d_back = b_bp -> d_diskp;
  956.     b_bp -> d_next = diskp;
  957.     if (bp1 -> d_next != ERROR) {
  958.  
  959.         TRACEP("new_block",
  960.             sl_sout("***** swap in to adjust back ptr in next block\n"));
  961.  
  962.         bp2 = swap_in(bp1 -> d_next);
  963.         bp2 -> d_back = diskp;
  964.         is_dirty(bp2);
  965.     }
  966.     
  967.     /* The block is empty. */
  968.     bp1 -> d_lines = 0;
  969.     is_dirty(bp1);
  970.  
  971.     /* Return a pointer to the new block. */
  972.     RETURN_PTR("new_block", bp1);
  973. }
  974.  
  975. /*
  976.     Split the current block before the current line.
  977.     The current line, and following lines are put in
  978.     a new block.
  979. */
  980. static struct BLOCK *
  981. split_block(void)
  982. {
  983.     struct    BLOCK *bp2;
  984.     char    *dest, *source;
  985.     int    nlines, nlines1, nlines2;
  986.     int    i, length, line, offset;
  987.  
  988.     TRACEPB("split_block", sl_sout("***** create new block\n"));
  989.  
  990.     /* Create a new block. */
  991.     bp2 = new_block();
  992.  
  993.     /* Mark both blocks as dirty. */
  994.     is_dirty(b_bp);
  995.     is_dirty(bp2);
  996.  
  997.     /*
  998.         Count the lines in each block.
  999.         Adjust nline1 and nlines1 if bufatbot().    
  1000.     */
  1001.     line    = b_line - b_start;
  1002.     nlines  = b_bp -> d_lines;
  1003.     nlines1 = min(nlines, b_line - b_start);
  1004.     nlines2 = max(0, nlines - nlines1);
  1005.  
  1006.     /* Copy data area to new block. */
  1007.     if (!bufatbot()) {
  1008.  
  1009.         TRACEP("split_block",
  1010.             sl_sout("***** copy data to new block with sysmove\n"));
  1011.  
  1012.         offset = b1_prefix(line);
  1013.         source = b_bp -> d_data + offset;
  1014.         dest   = bp2  -> d_data;
  1015.         length = b1_tab(nlines - 1) - offset;
  1016.         sysmove(source, dest, length);
  1017.     }
  1018.  
  1019.     /* Copy index to new block. */
  1020.     for(i = 0; i < nlines2; i++) {
  1021.         b_settab(bp2, i, b1_tab(i + nlines1) - offset);
  1022.     }
  1023.  
  1024.     /* Adjust the headers. */
  1025.     bp2  -> d_lines = nlines2;
  1026.     b_bp -> d_lines = nlines1;
  1027.  
  1028.     /* Adjust the pointer to the last block. */
  1029.     if (b_bp -> d_diskp == b_tail) {
  1030.         b_tail = bp2 -> d_diskp;
  1031.     }
  1032.  
  1033.     /* Return a pointer to the new block. */
  1034.     RETURN_PTR("split_block", bp2);
  1035. }
  1036.