home *** CD-ROM | disk | FTP | other *** search
/ pc.louisiana.edu/pub/unix/ / Louisiana_UNIX.tar / Louisiana_UNIX / xspread3.0.zoo / cmds.c < prev    next >
C/C++ Source or Header  |  1994-04-28  |  48KB  |  2,145 lines

  1. /*    SC    A Spreadsheet Calculator
  2.  *        Command routines
  3.  *
  4.  *        original by James Gosling, September 1982
  5.  *        modifications by Mark Weiser and Bruce Israel,
  6.  *            University of Maryland
  7.  *
  8.  *              More mods Robert Bond, 12/86
  9.  *
  10.  *        $Revision: 6.21 A $
  11.  *    
  12.  *        More mods by Dan Coppersmith, 5/94
  13.  */
  14. #include "config.h"
  15.  
  16. #ifndef DOINGX
  17.    Mistake!  You should have DOINGX defined, since this is
  18.    xspread and not SC.  One day xspread and SC will be the same,
  19.    but not yet!!!
  20. #endif
  21.  
  22. #include <sys/types.h>
  23. #ifdef DOINGX
  24. #include <stdio.h>
  25. #include <X11/Xlib.h>
  26. #include <X11/Xutil.h>
  27. #else
  28. #include <curses.h>
  29. #endif
  30.  
  31. #if defined(BSD42) || defined(BSD43)
  32. #include <strings.h>
  33. #else
  34. #ifndef SYSIII
  35. #include <string.h>
  36. #endif
  37. #endif
  38.  
  39. #if defined(BSD42) || defined(BSD43) || defined(VMS)
  40. #include <sys/file.h>
  41. #else
  42. #include <fcntl.h>
  43. #endif
  44.  
  45. #include "sc.h"
  46. #ifdef DOINGX
  47. #include "scXstuff.h"
  48. #endif
  49. #include <signal.h>
  50. #include <errno.h>
  51.  
  52. #ifdef __STDC__
  53. static    void    openrow(int);
  54. static    void    print_options(FILE *);
  55. static    void    syncref(struct enode *);
  56. static    void    unspecial(FILE *, char *, int);
  57. #else
  58. static    void    openrow();
  59. static    void    print_options();
  60. static    void    syncref();
  61. static    void    unspecial();
  62. #endif
  63.  
  64. extern    int    errno;
  65.  
  66. /* To store location of previous cursor postion */
  67. extern  int     prvmx, prvmy, prvcol;     /* line added by Bob Parbs 12-92 */
  68. extern  int     lastmx, lastmy, lastcol;  /* line added by Bob Parbs 12-92 */
  69.  
  70. /* a linked list of free [struct ent]'s, uses .next as the pointer */
  71. extern    struct ent *freeents;
  72.  
  73. /* a linked list of free [struct enodes]'s, uses .e.o.left as the pointer */
  74. extern    struct enode *freeenodes;
  75.  
  76. #define DEFCOLDELIM ':'
  77.  
  78. /* copy the current row (currow) and place the cursor in the new row */
  79. void
  80. duprow()
  81. {
  82.     if (currow >= maxrows - 1 || maxrow >= maxrows - 1) {
  83.     if (!growtbl(GROWROW, 0, 0))
  84.         return;
  85.     }
  86.     modflg++;
  87.     currow++;
  88.     openrow (currow);
  89.     for (curcol = 0; curcol <= maxcol; curcol++) {
  90.     register struct ent *p = *ATBL(tbl, currow - 1, curcol);
  91.     if (p) {
  92.         register struct ent *n;
  93.         n = lookat (currow, curcol);
  94.         (void)copyent ( n, p, 1, 0);
  95.     }
  96.     }
  97.     for (curcol = 0; curcol <= maxcol; curcol++) {
  98.     register struct ent *p = *ATBL(tbl, currow, curcol);
  99.     if (p && (p -> flags & is_valid) && !p -> expr)
  100.         break;
  101.     }
  102.     if (curcol > maxcol)
  103.     curcol = 0;
  104. }
  105.  
  106. /* copy the current column (curcol) and place the cursor in the new column */
  107. void
  108. dupcol() 
  109. {
  110.     if (curcol >= maxcols - 1 || maxcol >= maxcols - 1) {
  111.     if (!growtbl(GROWCOL, 0, 0))
  112.         return;
  113.     }
  114.     modflg++;
  115.     curcol++;
  116.     opencol (curcol, 1);
  117.     for (currow = 0; currow <= maxrow; currow++) {
  118.     register struct ent *p = *ATBL(tbl, currow, curcol - 1);
  119.     if (p) {
  120.         register struct ent *n;
  121.         n = lookat (currow, curcol);
  122.         copyent ( n, p, 0, 1);
  123.     }
  124.     }
  125.     for (currow = 0; currow <= maxrow; currow++) {
  126.     register struct ent *p = *ATBL(tbl, currow, curcol);
  127.     if (p && (p -> flags & is_valid) && !p -> expr)
  128.         break;
  129.     }
  130.     if (currow > maxrow)
  131.     currow = 0;
  132. }
  133.  
  134. /* insert 'arg' rows before currow */
  135. void
  136. insertrow(arg)
  137. register int arg;
  138. {
  139.     while (--arg>=0) openrow (currow);
  140. }
  141.  
  142. /* delete 'arg' rows starting at currow (deletes from currow downward) */
  143. void
  144. deleterow(arg)
  145. register int arg;
  146. {
  147.     if (any_locked_cells(currow, 0, currow + arg - 1, maxcol))
  148.     error("Locked cells encountered. Nothing changed");
  149.     else {
  150.         flush_saved();
  151.         erase_area(currow, 0, currow + arg - 1, maxcol);
  152.         currow += arg;
  153.         while (--arg>=0) closerow (--currow);
  154.         sync_refs();
  155.     }
  156. }
  157.  
  158. void
  159. erase_area(sr, sc, er, ec)
  160. int sr, sc, er, ec;
  161. {
  162.     register int r, c;
  163.     register struct ent **pp;
  164.  
  165.     if (sr > er) {
  166.     r = sr; sr = er; er= r;    
  167.     }
  168.  
  169.     if (sc > ec) {
  170.     c = sc; sc = ec; ec= c;    
  171.     }
  172.  
  173.     if (sr < 0)
  174.     sr = 0; 
  175.     if (sc < 0)
  176.     sc = 0;
  177.     checkbounds(&er, &ec);
  178.  
  179.     for (r = sr; r <= er; r++) {
  180.     for (c = sc; c <= ec; c++) {
  181.         pp = ATBL(tbl, r, c);
  182.         if (*pp && !((*pp)->flags&is_locked)) {
  183.         free_ent(*pp);
  184.         *pp = (struct ent *)0;
  185.         }
  186.     }
  187.     }
  188. }
  189.  
  190. /*
  191.  * deletes the expression associated w/ a cell and turns it into a constant
  192.  * containing whatever was on the screen
  193.  */
  194. void
  195. valueize_area(sr, sc, er, ec)
  196. int sr, sc, er, ec;
  197. {
  198.     register int r, c;
  199.     register struct ent *p;
  200.  
  201.     if (sr > er) {
  202.     r = sr; sr = er; er= r;    
  203.     }
  204.  
  205.     if (sc > ec) {
  206.     c = sc; sc = ec; ec= c;    
  207.     }
  208.  
  209.     if (sr < 0)
  210.     sr = 0; 
  211.     if (sc < 0)
  212.     sc = 0;
  213.     checkbounds(&er, &ec);
  214.  
  215.     for (r = sr; r <= er; r++) {
  216.     for (c = sc; c <= ec; c++) {
  217.         p = *ATBL(tbl, r, c);
  218.         if (p && p->flags&is_locked) {
  219.         sprintf(stringbuf, " Cell %s%d is locked", coltoa(c), r);
  220.         error(stringbuf);
  221.         continue;
  222.         }
  223.         if (p && p->expr) {
  224.         efree(p->expr);
  225.         p->expr = (struct enode *)0;
  226.         p->flags &= ~is_strexpr;
  227.         }
  228.     }
  229.     }
  230.  
  231. }
  232.  
  233. void
  234. pullcells(to_insert)
  235. int to_insert;
  236. {
  237.     register struct ent *p, *n;
  238.     register int deltar, deltac;
  239.     int minrow, mincol;
  240.     int mxrow, mxcol;
  241.     int numrows, numcols;
  242.  
  243.     if (! to_fix)
  244.     {
  245.     error ("No data to pull");
  246.     return;
  247.     }
  248.  
  249.     minrow = maxrows; 
  250.     mincol = maxcols;
  251.     mxrow = 0;
  252.     mxcol = 0;
  253.  
  254.     for (p = to_fix; p; p = p->next) {
  255.     if (p->row < minrow)
  256.         minrow = p->row;
  257.     if (p->row > mxrow)
  258.         mxrow = p->row;
  259.     if (p->col < mincol)
  260.         mincol = p->col;
  261.     if (p->col > mxcol)
  262.         mxcol = p->col;
  263.     }
  264.  
  265.     numrows = mxrow - minrow + 1;
  266.     numcols = mxcol - mincol + 1;
  267.     deltar = currow - minrow;
  268.     deltac = curcol - mincol;
  269.  
  270.     if (to_insert == 'r') {
  271.     insertrow(numrows);
  272.     deltac = 0;
  273.     } else if (to_insert == 'c') {
  274.     opencol(curcol, numcols);
  275.     deltar = 0;
  276.     }
  277.  
  278.     FullUpdate++;
  279.     modflg++;
  280.  
  281.     for (p = to_fix; p; p = p->next) {
  282.     n = lookat (p->row + deltar, p->col + deltac);
  283.     (void) clearent(n);
  284.     copyent( n, p, deltar, deltac);
  285.     n -> flags = p -> flags & ~is_deleted;
  286.     }
  287. }
  288.  
  289. void
  290. colshow_op()
  291. {
  292.     register int i,j;
  293.     for (i=0; i<maxcols; i++)
  294.     if (col_hidden[i]) 
  295.         break;
  296.     for(j=i; j<maxcols; j++)
  297.     if (!col_hidden[j])
  298.         break;
  299.     j--;
  300.     if (i>=maxcols)
  301.     error ("No hidden columns to show");
  302.     else {
  303.     (void) sprintf(line,"show %s:", coltoa(i));
  304.     (void) sprintf(line + strlen(line),"%s",coltoa(j));
  305.     linelim = strlen (line);
  306.     }
  307. }
  308.  
  309. void
  310. rowshow_op()
  311. {
  312.     register int i,j;
  313.     for (i=0; i<maxrows; i++)
  314.     if (row_hidden[i]) 
  315.         break;
  316.     for(j=i; j<maxrows; j++)
  317.     if (!row_hidden[j]) {
  318.         break;
  319.     }
  320.     j--;
  321.  
  322.     if (i>=maxrows)
  323.     error ("No hidden rows to show");
  324.     else {
  325.     (void) sprintf(line,"show %d:%d", i, j);
  326.         linelim = strlen (line);
  327.     }
  328. }
  329.  
  330. #ifdef notdef
  331. /*
  332.  * Given a row/column command letter, emit a small menu, then read a qualifier
  333.  * character for a row/column command and convert it to 'r' (row), 'c'
  334.  * (column), or 0 (unknown).  If ch is 'p', an extra qualifier 'm' is allowed.
  335.  */
  336. int
  337. get_rcqual (ch)
  338.     int ch;
  339. {
  340.     sprintf(stringbuf, "%sow/column:  r: row  c: column%s",
  341.  
  342.         (ch == 'i') ? "Insert r" :
  343.         (ch == 'a') ? "Append r" :
  344.         (ch == 'd') ? "Delete r" :
  345.         (ch == 'p') ? "Pull r" :
  346.         (ch == 'v') ? "Values r" :
  347.         (ch == 'z') ? "Zap r" :
  348.         (ch == 's') ? "Show r" : "R",
  349.  
  350.         (ch == 'p') ? "  m: merge" : "");
  351.     error(stringbuf);
  352.  
  353. #ifndef DOINGX
  354.     (void) refresh();
  355. #endif
  356.  
  357.     switch (nmgetch())
  358.     {
  359.         case 'r':
  360.         case 'l':
  361.         case 'h':
  362.         case ctl('f'):
  363.         case ctl('b'):    return ('r');
  364.  
  365.         case 'c':
  366.         case 'j':
  367.         case 'k':
  368.         case ctl('p'):
  369.         case ctl('n'):    return ('c');
  370.  
  371.         case 'm':    return ((ch == 'p') ? 'm' : 0);
  372.  
  373.         case ESC:
  374.         case ctl('g'):    return (ESC);
  375.  
  376.         default:    return (0);
  377.     }
  378.     /*NOTREACHED*/
  379. }
  380. #endif
  381.  
  382. static    void
  383. openrow (rs)
  384. int    rs;
  385. {
  386.     register    r, c;
  387.     struct ent    **tmprow, **pp;
  388.  
  389.     if (rs > maxrow) maxrow = rs;
  390.     if (maxrow >= maxrows - 1 || rs > maxrows - 1) {
  391.     if (!growtbl(GROWROW, rs, 0))
  392.         return;
  393.     }
  394.     /*
  395.      * save the last active row+1, shift the rows downward, put the last
  396.      * row in place of the first
  397.      */
  398.     tmprow = tbl[++maxrow];
  399.     for (r = maxrow; r > rs; r--) {
  400.     row_hidden[r] = row_hidden[r-1];
  401.     tbl[r] = tbl[r-1];
  402.     pp = ATBL(tbl, r, 0);
  403.     for (c = 0; c < maxcols; c++, pp++)
  404.         if (*pp)
  405.             (*pp)->row = r;
  406.     }
  407.     tbl[r] = tmprow;    /* the last row was never used.... */
  408.     FullUpdate++;
  409.     modflg++;
  410. }
  411.  
  412. /* delete row r */
  413. void
  414. closerow (r)
  415. register int    r;
  416. {
  417.     register struct ent **pp;
  418.     register c;
  419.     struct ent    **tmprow;
  420.  
  421.     if (r > maxrow) return;
  422.  
  423.     /* save the row and empty it out */
  424.     tmprow = tbl[r];
  425.     pp = ATBL(tbl, r, 0);
  426.     for (c=maxcol+1; --c>=0; pp++) {
  427.     if (*pp)
  428.     {    free_ent(*pp);
  429.         *pp = (struct ent *)0;
  430.     }
  431.     }
  432.  
  433.     /* move the rows, put the deleted, but now empty, row at the end */
  434.     for (; r < maxrows - 1; r++) {
  435.     row_hidden[r] = row_hidden[r+1];
  436.     tbl[r] = tbl[r+1];
  437.     pp = ATBL(tbl, r, 0);
  438.     for (c = 0; c < maxcols; c++, pp++)
  439.         if (*pp)
  440.             (*pp)->row = r;
  441.     }
  442.     tbl[r] = tmprow;
  443.  
  444.     maxrow--;
  445.     FullUpdate++;
  446.     modflg++;
  447. }
  448.  
  449. void
  450. opencol (cs, numcol)
  451. int    cs;
  452. int    numcol;
  453. {
  454.     register r;
  455.     register struct ent **pp;
  456.     register c;
  457.     register lim = maxcol-cs+1;
  458.     int i;
  459.  
  460.     if (cs > maxcol)
  461.     maxcol = cs;
  462.     maxcol += numcol;
  463.  
  464.     if ((maxcol >= maxcols - 1) && !growtbl(GROWCOL, 0, maxcol))
  465.         return;
  466.  
  467.     for (i = maxcol; i > cs; i--) {
  468.     fwidth[i] = fwidth[i-numcol];
  469.     precision[i] = precision[i-numcol];
  470.     realfmt[i] = realfmt[i-numcol];
  471.     col_hidden[i] = col_hidden[i-numcol];
  472.     }
  473.     for (c = cs; c - cs < numcol; c++)
  474.     {    fwidth[c] = DEFWIDTH;
  475.     precision[c] =  DEFPREC;
  476.     realfmt[c] = DEFREFMT;
  477.     }
  478.     
  479.     for (r=0; r<=maxrow; r++) {
  480.     pp = ATBL(tbl, r, maxcol);
  481.     for (c=lim; --c>=0; pp--)
  482.         if (pp[0] = pp[-numcol])
  483.         pp[0]->col += numcol;
  484.  
  485.     pp = ATBL(tbl, r, cs);
  486.     for (c = cs; c - cs < numcol; c++, pp++)
  487.         *pp = (struct ent *)0;
  488.     }
  489.     FullUpdate++;
  490.     modflg++;
  491. }
  492.  
  493. /* delete group of columns (1 or more) */
  494. void
  495. closecol (cs, numcol)
  496. int    cs;
  497. int    numcol;
  498. {
  499.     register r;
  500.     register struct ent **pp;
  501.     register struct ent *q;
  502.     register c;
  503.     register lim = maxcol-cs;
  504.     int i;
  505.     char buf[50];
  506.  
  507.     if (lim - numcol < -1)
  508.     {    (void) sprintf(buf, "Can't delete %d column%s %d columns left", numcol,
  509.             (numcol > 1 ? "s," : ","), lim+1);
  510.     error(buf);
  511.     return;
  512.     }
  513.     if (any_locked_cells(0, curcol, maxrow, curcol + numcol - 1)) {
  514.     error("Locked cells encountered. Nothing changed");
  515.     return;
  516.     }
  517.     flush_saved();
  518.     erase_area(0, curcol, maxrow, curcol + numcol - 1);
  519.     sync_refs();
  520.  
  521.     /* clear then copy the block left */
  522.     lim = maxcols - numcol - 1;
  523.     for (r=0; r<=maxrow; r++) {
  524.     for (c = cs; c - cs < numcol; c++)
  525.         if (q = *ATBL(tbl, r, c))
  526.             free_ent(q);
  527.  
  528.     pp = ATBL(tbl, r, cs);
  529.     for (c=cs; c <= lim; c++, pp++)
  530.     {   if (c > lim)
  531.         *pp = (struct ent *)0;
  532.         else
  533.         if (pp[0] = pp[numcol])
  534.         pp[0]->col -= numcol;
  535.     }
  536.  
  537.     c = numcol;
  538.     for (; --c >= 0; pp++)        
  539.         *pp = (struct ent *)0;
  540.     }
  541.  
  542.     for (i = cs; i < maxcols - numcol - 1; i++) {
  543.     fwidth[i] = fwidth[i+numcol];
  544.     precision[i] = precision[i+numcol];
  545.     realfmt[i] = realfmt[i+numcol];
  546.     col_hidden[i] = col_hidden[i+numcol];
  547.     }
  548.     for (; i < maxcols - 1; i++) {
  549.     fwidth[i] = DEFWIDTH;
  550.     precision[i] = DEFPREC;
  551.     realfmt[i] = DEFREFMT;
  552.     col_hidden[i] = FALSE;
  553.     }
  554.  
  555.     maxcol -= numcol;
  556.     FullUpdate++;
  557.     modflg++;
  558. }
  559.  
  560. void
  561. doend(rowinc, colinc)
  562. int rowinc, colinc;
  563. {
  564.     register struct ent *p;
  565.     int r, c;
  566.  
  567.     if (VALID_CELL(p, currow, curcol)) {
  568.     r = currow + rowinc;
  569.     c = curcol + colinc;
  570.     if (r >= 0 && r < maxrows && 
  571.         c >= 0 && c < maxcols &&
  572.         !VALID_CELL(p, r, c)) {
  573.         currow = r;
  574.         curcol = c;
  575.     }
  576.     }
  577.  
  578.     if (!VALID_CELL(p, currow, curcol)) {
  579.         switch (rowinc) {
  580.         case -1:
  581.         while (!VALID_CELL(p, currow, curcol) && currow > 0)
  582.         currow--;
  583.         break;
  584.         case  1:
  585.         while (!VALID_CELL(p, currow, curcol) && currow < maxrows-1)
  586.         currow++;
  587.         break;
  588.         case  0:
  589.             switch (colinc) {
  590.          case -1:
  591.             while (!VALID_CELL(p, currow, curcol) && curcol > 0)
  592.             curcol--;
  593.             break;
  594.          case  1:
  595.             while (!VALID_CELL(p, currow, curcol) && curcol < maxcols-1)
  596.             curcol++;
  597.             break;
  598.         }
  599.             break;
  600.         }
  601.  
  602.     error ("");    /* clear line */
  603.     return;
  604.     }
  605.  
  606.     switch (rowinc) {
  607.     case -1:
  608.     while (VALID_CELL(p, currow, curcol) && currow > 0)
  609.         currow--;
  610.     break;
  611.     case  1:
  612.     while (VALID_CELL(p, currow, curcol) && currow < maxrows-1)
  613.         currow++;
  614.     break;
  615.     case  0:
  616.     switch (colinc) {
  617.     case -1:
  618.         while (VALID_CELL(p, currow, curcol) && curcol > 0)
  619.         curcol--;
  620.         break;
  621.     case  1:
  622.         while (VALID_CELL(p, currow, curcol) && curcol < maxcols-1)
  623.         curcol++;
  624.         break;
  625.     }
  626.     break;
  627.     }
  628.     if (!VALID_CELL(p, currow, curcol)) {
  629.         currow -= rowinc;
  630.         curcol -= colinc;
  631.     }
  632. }
  633.  
  634. /* Modified 9/17/90 THA to handle more formats */
  635. void
  636. doformat(c1,c2,w,p,r)
  637. int c1,c2,w,p,r;
  638. {
  639.     register int i;
  640.     int crows = 0;
  641.     int ccols = c2;
  642.  
  643.     if (c1 >= maxcols && !growtbl(GROWCOL, 0, c1)) c1 = maxcols-1 ;
  644.     if (c2 >= maxcols && !growtbl(GROWCOL, 0, c2)) c2 = maxcols-1 ;
  645.  
  646.     if (w > maintextcols - RESCOL - 2) {
  647.     sprintf(stringbuf,"Format too large - Maximum = %d", maintextcols - RESCOL - 2);
  648.     error(stringbuf);
  649.     w = maintextcols - RESCOL - 2;
  650.     }
  651.  
  652.     if (p > w) {
  653.     error("Precision too large");
  654.     p = w;
  655.     }
  656.  
  657.     checkbounds(&crows, &ccols);
  658.     if (ccols < c2) {
  659.     sprintf(stringbuf,"Format statement failed to create implied column %d", c2);
  660.     error(stringbuf);
  661.     return;
  662.     }
  663.  
  664.     for(i = c1; i<=c2; i++)
  665.         fwidth[i] = w, precision[i] = p, realfmt[i] = r;
  666.  
  667.     FullUpdate++;
  668.     modflg++;
  669. }
  670.  
  671. static    void
  672. print_options(f)
  673. FILE *f;
  674. {
  675.     if(
  676.        autocalc &&
  677.        propagation == 10 &&
  678.        calc_order == BYROWS &&
  679.        !numeric &&
  680.        prescale == 1.0 &&
  681.        !extfunc &&
  682.        showcell &&
  683.        showtop &&
  684.        tbl_style == 0 &&
  685.        craction == 0 &&
  686.        rowlimit == -1 &&
  687.        collimit == -1
  688.       )
  689.         return;        /* No reason to do this */
  690.  
  691.     (void) fprintf(f, "set");
  692.     if(!autocalc) 
  693.     (void) fprintf(f," !autocalc");
  694.     if(propagation != 10)
  695.     (void) fprintf(f, " iterations = %d", propagation);
  696.     if(calc_order != BYROWS )
  697.     (void) fprintf(f, " bycols");
  698.     if (numeric)
  699.     (void) fprintf(f, " numeric");
  700.     if (prescale != 1.0)
  701.     (void) fprintf(f, " prescale");
  702.     if (extfunc)
  703.     (void) fprintf(f, " extfun");
  704.     if (!showcell)
  705.     (void) fprintf(f, " !cellcur");
  706.     if (!showtop)
  707.     (void) fprintf(f, " !toprow");
  708.     if (tbl_style)
  709.     (void) fprintf(f, " tblstyle = %s", tbl_style == TBL ? "tbl" :
  710.                     tbl_style == LATEX ? "latex" :
  711.                     tbl_style == SLATEX ? "slatex" :
  712.                     tbl_style == TEX ? "tex" :
  713.                     tbl_style == FRAME ? "frame" : "0" );
  714.     if (craction)
  715.     (void) fprintf(f, " craction = %d", craction);
  716.     if (rowlimit >= 0)
  717.     (void) fprintf(f, " rowlimit = %d", rowlimit);
  718.     if (collimit >= 0)
  719.     (void) fprintf(f, " collimit = %d", collimit);
  720.     (void) fprintf(f, "\n");
  721. }
  722.  
  723. void
  724. printfile (fname, r0, c0, rn, cn)
  725. char *fname;
  726. int r0, c0, rn, cn;
  727. {
  728.     FILE *f;
  729.     static char *pline = NULL;        /* only malloc once, malloc is slow */
  730.     static unsigned fbufs_allocated = 0;
  731.     int plinelim;
  732.     int pid;
  733.     int fieldlen = 0;
  734.     int nextcol = 0;
  735.     register row, col;
  736.     register struct ent **pp;
  737.     char    *newname;
  738.  
  739.     /* strip off the .sc ending and add a .asc suffix */
  740.     if (*fname == '\0')
  741.     {    newname = fsuffix(curfile, ".asc");
  742.     fname = newname;
  743.     }
  744.     else
  745.     newname = NULL;
  746.  
  747.     if ((strcmp(fname, curfile) == 0) &&
  748.     !yn_ask("Confirm that you want to destroy the data base: (y,n)"))
  749.     {
  750.     if (newname != NULL)
  751.         scxfree(newname);
  752.     return;
  753.     }
  754.  
  755.     if (!pline && (pline = scxmalloc((unsigned)(FBUFLEN *
  756.                     ++fbufs_allocated))) == (char *)NULL)
  757.     {   error("Malloc failed in printfile()");
  758.     if (newname != NULL)
  759.         scxfree(newname);
  760.         return;
  761.     }
  762.  
  763.     if ((f = openout(fname, &pid)) == (FILE *)0)
  764.     {    sprintf(stringbuf, "Can't create file \"%s\"", fname);
  765.     error(stringbuf);
  766.     if (newname != NULL)
  767.         scxfree(newname);
  768.     return;
  769.     }
  770.     for (row=r0;row<=rn; row++) {
  771.     register c = 0;
  772.  
  773.     if (row_hidden[row])
  774.         continue;
  775.  
  776.     pline[plinelim=0] = '\0';
  777.     for (pp = ATBL(tbl, row, col=c0); col<=cn;
  778.             pp += nextcol-col, col = nextcol, c += fieldlen) {
  779.  
  780.         nextcol = col+1;
  781.         if (col_hidden[col]) {
  782.         fieldlen = 0;
  783.         continue;
  784.         }
  785.  
  786.         fieldlen = fwidth[col];
  787.         if (*pp) {
  788.         char *s;
  789.  
  790.         /* 
  791.          * dynamically allocate pline, making sure we are not 
  792.          * attempting to write 'out of bounds'.
  793.          */
  794.         while(c > (fbufs_allocated * FBUFLEN)) {
  795.           if((pline = scxrealloc
  796.                    ((char *)pline, 
  797.                 (unsigned)(FBUFLEN * ++fbufs_allocated)))
  798.              == NULL)
  799.           {
  800.             error ("Realloc failed in printfile()");
  801.             if (newname != NULL)
  802.             scxfree(newname);
  803.             return;
  804.           }
  805.         }          
  806.         while (plinelim<c) pline[plinelim++] = ' ';
  807.         plinelim = c;
  808.         if ((*pp)->flags&is_valid) {
  809.             while(plinelim + fwidth[col] > 
  810.               (fbufs_allocated * FBUFLEN)) {
  811.               if((pline = ((char *)scxrealloc
  812.                    ((char *)pline, 
  813.                     (unsigned)(FBUFLEN * ++fbufs_allocated))))
  814.              == NULL) {
  815.             error ("Realloc failed in printfile()");
  816.             if (newname != NULL)
  817.                 scxfree(newname);
  818.             return;
  819.               }
  820.             }
  821.             if ((*pp)->cellerror)
  822.             (void) sprintf (pline+plinelim, "%*s",
  823.                 fwidth[col],
  824.             ((*pp)->cellerror == CELLERROR ? "ERROR" : "INVALID"));
  825.             else
  826.             {
  827.               if ((*pp)->format) {
  828.                    char field[FBUFLEN];
  829.             format ((*pp)->format, (*pp)->v, field,
  830.                        sizeof(field));
  831.             (void) sprintf (pline+plinelim, "%*s", fwidth[col],
  832.                     field);
  833.               } else {
  834.                    char field[FBUFLEN];
  835.             (void) engformat(realfmt[col], fwidth[col],
  836.                                              precision[col], (*pp) -> v,
  837.                                              field, sizeof(field));
  838.             (void) sprintf (pline+plinelim, "%*s", fwidth[col],
  839.                        field);
  840.               }
  841.             }
  842.             plinelim += strlen (pline+plinelim);
  843.         }
  844.         if (s = (*pp)->label) {
  845.             int slen;
  846.             char *start, *last;
  847.             register char *fp;
  848.             struct ent *nc;
  849.  
  850.             /*
  851.              * Figure out if the label slops over to a blank field. A
  852.              * string started with backslah is defining repition char
  853.              */
  854.             slen = strlen(s);
  855.             if ( *s == '\\' && *(s+1)!= '\0' )
  856.             slen = fwidth[col];
  857.             while (slen > fieldlen && nextcol <= cn &&
  858.                 !((nc = lookat(row,nextcol))->flags & is_valid) &&
  859.                 !(nc->label)) {
  860.             
  861.                     if (!col_hidden[nextcol])
  862.                  fieldlen += fwidth[nextcol];
  863.  
  864.             nextcol++;
  865.             }
  866.             if (slen > fieldlen)
  867.             slen = fieldlen;
  868.             
  869.             while(c + fieldlen + 2 > (fbufs_allocated * FBUFLEN)) {
  870.               if((pline = ((char *)scxrealloc
  871.                    ((char *)pline, 
  872.                     (unsigned)(FBUFLEN * ++fbufs_allocated))))
  873.              == NULL) {
  874.             error ("scxrealloc failed in printfile()");
  875.             if (newname != NULL)
  876.                 scxfree(newname);
  877.             return;
  878.               }
  879.             }          
  880.  
  881.             /* Now justify and print */
  882.             start = (*pp)->flags & is_leftflush ? pline + c
  883.                     : pline + c + fieldlen - slen;
  884.             if( (*pp)->flags & is_label )
  885.             start = pline + (c + ((fwidth[col]>slen)?(fwidth[col]-slen)/2:0));
  886.             last = pline + c + fieldlen;
  887.             fp = plinelim < c ? pline + plinelim : pline + c;
  888.             while (fp < start)
  889.             *fp++ = ' ';
  890.             if( *s == '\\' && *(s+1)!= '\0' ) {
  891.             char *strt;
  892.             strt = ++s;
  893.  
  894.             while(slen--) {
  895.                 *fp++ = *s++; if( *s == '\0' ) s = strt;
  896.             }
  897.             }
  898.             else
  899.             while (slen--)
  900.             *fp++ = *s++;
  901.  
  902.             if (!((*pp)->flags & is_valid) || fieldlen != fwidth[col])
  903.             while(fp < last)
  904.                 *fp++ = ' ';
  905.             if (plinelim < fp - pline)
  906.             plinelim = fp - pline;
  907.         }
  908.         }
  909.     }
  910.     pline[plinelim++] = '\n';
  911.     pline[plinelim] = '\0';
  912.     (void) fputs (pline, f);
  913.     }
  914.     if (newname != NULL)
  915.     scxfree(newname);
  916.  
  917.     closeout(f, pid);
  918. }
  919.  
  920. void
  921. tblprintfile (fname, r0, c0, rn, cn)
  922. char *fname;
  923. int r0, c0, rn, cn;
  924. {
  925.     FILE *f;
  926.     int    pid;
  927.     register int row, col;
  928.     register struct ent **pp;
  929.     char coldelim = DEFCOLDELIM;
  930.     char *newname;
  931.     char *suffix;
  932.  
  933.     /* strip off the .sc ending and add a suffix */
  934.     newname = NULL;
  935.     if (*fname == '\0')
  936.     {    if (tbl_style == 0)
  937.         suffix = ".cln";
  938.     else if (tbl_style == TBL)
  939.         suffix = ".tbl";
  940.     else if (tbl_style == LATEX)
  941.         suffix = ".lat";
  942.     else if (tbl_style == SLATEX)
  943.         suffix = ".stx";
  944.     else if (tbl_style == TEX)
  945.         suffix = ".tex";
  946.     else
  947.         suffix = NULL;
  948.     if (suffix != NULL)
  949.     {    newname = fsuffix(curfile, suffix);
  950.         fname = newname;
  951.     }
  952.     }
  953.  
  954.     if ((strcmp(fname, curfile) == 0) &&
  955.     !yn_ask("Confirm that you want to destroy the data base: (y,n)"))
  956.     {    if (newname != NULL)
  957.         scxfree(newname);
  958.     return;
  959.     }
  960.  
  961.     if ((f = openout(fname, &pid)) == (FILE *)0)
  962.     {    sprintf(stringbuf, "Can't create file \"%s\"", fname);
  963.     error(stringbuf);
  964.     if (newname != NULL)
  965.         scxfree(newname);
  966.     return;
  967.     }
  968.  
  969.     if ( tbl_style == TBL ) {
  970.     fprintf(f,".\\\" ** %s spreadsheet output \n.TS\n",progname);
  971.     fprintf(f,"tab(%c);\n",coldelim);
  972.     for (col=c0;col<=cn; col++) fprintf(f," n");
  973.     fprintf(f, ".\n");
  974.     }
  975.     else if ( tbl_style == LATEX ) {
  976.     fprintf(f,"%% ** %s spreadsheet output\n\\begin{tabular}{",progname);
  977.     for (col=c0;col<=cn; col++) fprintf(f,"c");
  978.     fprintf(f, "}\n");
  979.     coldelim = '&';
  980.     }
  981.     else if ( tbl_style == SLATEX ) {
  982.     fprintf(f,"%% ** %s spreadsheet output\n!begin<tabular><",progname);
  983.     for (col=c0;col<=cn; col++) fprintf(f,"c");
  984.     fprintf(f, ">\n");
  985.     coldelim = '&';
  986.     }
  987.     else if ( tbl_style == TEX ) {
  988.     fprintf(f,"{\t%% ** %s spreadsheet output\n\\settabs %d \\columns\n",
  989.         progname, cn-c0+1);
  990.     coldelim = '&';
  991.     }
  992.     else if ( tbl_style == FRAME ) {
  993.     fprintf(f,"<MIFFile 3.00> # generated by the sc spreadsheet calculator\n");
  994.     fprintf(f,"<Tbls\n");
  995.     fprintf(f," <Tbl \n");
  996.     fprintf(f,"  <TblID 1> # This table's ID is 1\n");
  997.     fprintf(f,"  <TblFormat \n");
  998.     fprintf(f,"   <TblTag `Format A'> # Table Format Catalog\n");
  999.     fprintf(f,"  > # end of TblFormat\n");
  1000.     fprintf(f,"  <TblNumColumns %d> # Has %d columns\n",cn-c0+1,cn-c0+1);
  1001.     fprintf(f,"  <TblTitleContent\n");
  1002.     fprintf(f,"   <Para\n");
  1003.     fprintf(f,"    <PgfTag `TableTitle'> # Forces lookup in Paragraph Format Catalog\n");
  1004.     fprintf(f,"    <ParaLine\n");
  1005.     fprintf(f,"     <String `%s'>\n",fname);
  1006.     fprintf(f,"    > # end of ParaLine\n");
  1007.     fprintf(f,"   > # end of Para\n");
  1008.     fprintf(f,"  > # end of TblTitleContent\n");
  1009.     fprintf(f,"  <TblH # The heading\n");
  1010.     fprintf(f,"   <Row # The heading row\n");
  1011.     for (col=c0; col <= cn; col++) {
  1012.         fprintf(f,"    <Cell <CellContent <Para # Cell in column \n");
  1013.         fprintf(f,"       <PgfTag `CellHeading'> # in Paragraph Format Catalog\n");
  1014.         fprintf(f,"       <ParaLine <String `%c'>>\n",'A'+col);
  1015.         fprintf(f,"    >>> # end of Cell\n");
  1016.     }
  1017.     fprintf(f,"   > # end of Row\n");
  1018.     fprintf(f,"  > # end of TblH\n");
  1019.     fprintf(f,"  <TblBody # The body\n");
  1020.     }
  1021.  
  1022.     for (row=r0; row<=rn; row++) {
  1023.     if ( tbl_style == TEX )
  1024.         (void) fprintf (f, "\\+");
  1025.     else if ( tbl_style == FRAME ) {
  1026.         fprintf(f,"   <Row # The next body row\n");
  1027.     }
  1028.     
  1029.     for (pp = ATBL(tbl, row, col=c0); col<=cn; col++, pp++) {
  1030.         if ( tbl_style == FRAME ) {
  1031.         fprintf(f,"    <Cell <CellContent <Para\n");
  1032.         fprintf(f,"       <PgfTag `CellBody'> # in Paragraph Format Catalog\n");
  1033.         fprintf(f,"       <ParaLine <String `");
  1034.         } 
  1035.         if (*pp) {
  1036.         char *s;
  1037.         if ((*pp)->flags&is_valid) {
  1038.             if ((*pp)->cellerror) {
  1039.             (void) fprintf (f, "%*s",
  1040.                     fwidth[col],
  1041.             ((*pp)->cellerror == CELLERROR ? "ERROR" : "INVALID"));
  1042.             }
  1043.             else
  1044.             if ((*pp)->format) {
  1045.                 char field[FBUFLEN];
  1046.             
  1047.             (void) format ((*pp)->format, (*pp)->v, field,
  1048.                        sizeof(field));
  1049.             unspecial (f, field, coldelim);
  1050.             } else {
  1051.                 char field[FBUFLEN];
  1052.                         (void) engformat(realfmt[col], fwidth[col],
  1053.                                              precision[col], (*pp) -> v,
  1054.                                              field, sizeof(field));
  1055.             unspecial (f, field, coldelim);
  1056.             }
  1057.         }
  1058.         if (s = (*pp)->label) {
  1059.                 unspecial (f, s, coldelim);
  1060.         }
  1061.         }
  1062.         if (tbl_style == FRAME) {
  1063.         fprintf(f, "'>>\n");
  1064.         fprintf(f,"    >>> # end of Cell\n");
  1065.         }
  1066.         if ( col < cn )
  1067.         if (tbl_style != FRAME)
  1068.             (void) fprintf(f,"%c", coldelim);
  1069.     }
  1070.     if ( tbl_style == LATEX ) {
  1071.         if ( row < rn ) (void) fprintf (f, "\\\\");
  1072.     }
  1073.     else if ( tbl_style == SLATEX ) {
  1074.         if ( row < rn ) (void) fprintf (f, "!!");
  1075.     }
  1076.     else if ( tbl_style == TEX ) {
  1077.         (void) fprintf (f, "\\cr");
  1078.     }
  1079.     else if ( tbl_style == FRAME ) {
  1080.         fprintf(f,"   > # end of Row\n");
  1081.     }
  1082.     (void) fprintf (f,"\n");
  1083.     }
  1084.  
  1085.     if ( tbl_style == TBL )
  1086.     (void) fprintf (f,".TE\n.\\\" ** end of %s spreadsheet output\n", progname);
  1087.     else if ( tbl_style == LATEX )
  1088.     (void) fprintf (f,"\\end{tabular}\n%% ** end of %s spreadsheet output\n", progname);
  1089.     else if ( tbl_style == SLATEX )
  1090.     (void) fprintf (f,"!end<tabular>\n%% ** end of %s spreadsheet output\n", progname);
  1091.     else if ( tbl_style == TEX )
  1092.     (void) fprintf (f,"}\n%% ** end of %s spreadsheet output\n", progname);
  1093.     else if ( tbl_style == FRAME ) {
  1094.     fprintf(f,"  > # end of TblBody\n");
  1095.     fprintf(f," ># end of Tbl\n");
  1096.     fprintf(f,"> # end of Tbls\n");
  1097.     fprintf(f,"<TextFlow <Para \n");
  1098.     fprintf(f,"  <PgfTag Body> \n");
  1099.     fprintf(f,"  <ParaLine <ATbl 1>> # Reference to table ID 1\n");
  1100.     fprintf(f,">>\n");
  1101.     }
  1102.  
  1103.     if (newname != NULL)
  1104.     scxfree(newname);
  1105.     closeout(f, pid);
  1106. }
  1107.  
  1108. /* unspecial (backquote) things that are special chars in a table */
  1109. static    void
  1110. unspecial(f, str, delim)
  1111. FILE    *f;
  1112. char    *str;
  1113. int    delim;
  1114. {
  1115.     if( *str == '\\' ) str++; /* delete wheeling string operator, OK? */
  1116.     while (*str)
  1117.     {    if (((tbl_style == LATEX) || (tbl_style == SLATEX) ||
  1118.             (tbl_style == TEX)) &&
  1119.             ((*str == delim) || (*str == '$') || (*str == '#') ||
  1120.             (*str == '%') || (*str == '{') || (*str == '}') ||
  1121.             (*str == '[') || (*str == ']') || (*str == '&')))
  1122.             putc('\\', f);
  1123.         putc(*str, f);
  1124.         str++;
  1125.     }
  1126. }
  1127.  
  1128. struct enode *
  1129. copye (e, Rdelta, Cdelta)
  1130. register struct enode *e;
  1131. int Rdelta, Cdelta;
  1132. {
  1133.     register struct enode *ret;
  1134.  
  1135.     if (e == (struct enode *)0) {
  1136.         ret = (struct enode *)0;
  1137.     } else if (e->op & REDUCE) {
  1138.     int newrow, newcol;
  1139.     if (freeenodes)
  1140.     {    ret = freeenodes;
  1141.         freeenodes = ret->e.o.left;
  1142.     }
  1143.     else
  1144.         ret = (struct enode *) scxmalloc ((unsigned) sizeof (struct enode));
  1145.     ret->op = e->op;
  1146.     newrow=e->e.r.left.vf & FIX_ROW ? e->e.r.left.vp->row :
  1147.                       e->e.r.left.vp->row+Rdelta;
  1148.     newcol=e->e.r.left.vf & FIX_COL ? e->e.r.left.vp->col :
  1149.                       e->e.r.left.vp->col+Cdelta;
  1150.     ret->e.r.left.vp = lookat (newrow, newcol);
  1151.     ret->e.r.left.vf = e->e.r.left.vf;
  1152.     newrow=e->e.r.right.vf & FIX_ROW ? e->e.r.right.vp->row :
  1153.                        e->e.r.right.vp->row+Rdelta;
  1154.     newcol=e->e.r.right.vf & FIX_COL ? e->e.r.right.vp->col :
  1155.                        e->e.r.right.vp->col+Cdelta;
  1156.     ret->e.r.right.vp = lookat (newrow, newcol);
  1157.     ret->e.r.right.vf = e->e.r.right.vf;
  1158.     } else {
  1159.     if (freeenodes)
  1160.     {    ret = freeenodes;
  1161.         freeenodes = ret->e.o.left;
  1162.     }
  1163.     else
  1164.         ret = (struct enode *) scxmalloc ((unsigned) sizeof (struct enode));
  1165.     ret->op = e->op;
  1166.     switch (ret->op) {
  1167.     case 'v':
  1168.         {
  1169.             int newrow, newcol;
  1170.             newrow=e->e.v.vf & FIX_ROW ? e->e.v.vp->row :
  1171.                          e->e.v.vp->row+Rdelta;
  1172.             newcol=e->e.v.vf & FIX_COL ? e->e.v.vp->col :
  1173.                          e->e.v.vp->col+Cdelta;
  1174.             ret->e.v.vp = lookat (newrow, newcol);
  1175.             ret->e.v.vf = e->e.v.vf;
  1176.             break;
  1177.         }
  1178.     case 'k':
  1179.         ret->e.k = e->e.k;
  1180.         break;
  1181.     case 'f':
  1182.         ret->e.o.right = copye (e->e.o.right,0,0);
  1183.         ret->e.o.left = (struct enode *)0;
  1184.          break;
  1185.     case '$':
  1186.         ret->e.s = scxmalloc((unsigned) strlen(e->e.s)+1);
  1187.         (void) strcpy(ret->e.s, e->e.s);
  1188.         break;
  1189.     default:
  1190.         ret->e.o.right = copye (e->e.o.right,Rdelta,Cdelta);
  1191.         ret->e.o.left = copye (e->e.o.left,Rdelta,Cdelta);
  1192.         break;
  1193.     }
  1194.     }
  1195.     return ret;
  1196. }
  1197.  
  1198. /*
  1199.  * sync_refs and syncref are used to remove references to
  1200.  * deleted struct ents.  Note that the deleted structure must still
  1201.  * be hanging around before the call, but not referenced by an entry
  1202.  * in tbl.  Thus the free_ent calls in sc.c
  1203.  */
  1204. void
  1205. sync_refs ()
  1206. {
  1207.     register i,j;
  1208.     register struct ent *p;
  1209.     sync_ranges();
  1210.     for (i=0; i<=maxrow; i++)
  1211.     for (j=0; j<=maxcol; j++)
  1212.         if ((p = *ATBL(tbl, i, j)) && p->expr)
  1213.         syncref(p->expr);
  1214. }
  1215.  
  1216. static    void
  1217. syncref(e)
  1218. register struct enode *e;
  1219. {
  1220.     if (e == (struct enode *)0)
  1221.     return;
  1222.     else if (e->op & REDUCE) {
  1223.      e->e.r.right.vp = lookat(e->e.r.right.vp->row, e->e.r.right.vp->col);
  1224.      e->e.r.left.vp = lookat(e->e.r.left.vp->row, e->e.r.left.vp->col);
  1225.     } else {
  1226.     switch (e->op) {
  1227.     case 'v':
  1228.         e->e.v.vp = lookat(e->e.v.vp->row, e->e.v.vp->col);
  1229.         break;
  1230.     case 'k':
  1231.         break;
  1232.     case '$':
  1233.         break;
  1234.     default:
  1235.         syncref(e->e.o.right);
  1236.         syncref(e->e.o.left);
  1237.         break;
  1238.     }
  1239.     }
  1240. }
  1241.  
  1242. /* mark a row as hidden */
  1243. void
  1244. hiderow(arg)
  1245. int arg;
  1246. {
  1247.     register int r1;
  1248.     register int r2;
  1249.  
  1250.     r1 = currow;
  1251.     r2 = r1 + arg - 1;
  1252.     if (r1 < 0 || r1 > r2) {
  1253.     error ("Invalid range");
  1254.     return;
  1255.     }
  1256.     if (r2 >= maxrows-1)
  1257.     {    if (!growtbl(GROWROW, arg+1, 0))
  1258.     {    error("You can't hide the last row");
  1259.         return;
  1260.     }
  1261.     }
  1262.     FullUpdate++;
  1263.     modflg++;
  1264.     while (r1 <= r2)
  1265.     row_hidden[r1++] = 1;
  1266. }
  1267.  
  1268. /* mark a column as hidden */
  1269. void
  1270. hidecol(arg)
  1271. int arg;
  1272. {
  1273.     register int c1;
  1274.     register int c2;
  1275.  
  1276.     c1 = curcol;
  1277.     c2 = c1 + arg - 1;
  1278.     if (c1 < 0 || c1 > c2) {
  1279.     error ("Invalid range");
  1280.     return;
  1281.     }
  1282.     if (c2 >= maxcols-1)
  1283.     {    if ((arg >= ABSMAXCOLS-1) || !growtbl(GROWCOL, 0, arg+1))
  1284.     {    error("You can't hide the last col");
  1285.         return;
  1286.     }
  1287.     }
  1288.     FullUpdate++;
  1289.     modflg++;
  1290.     while (c1 <= c2)
  1291.     col_hidden[c1++] = TRUE;
  1292. }
  1293.  
  1294. /* mark a row as not-hidden */
  1295. void
  1296. showrow(r1, r2)
  1297. int r1, r2;
  1298. {
  1299.     if (r1 < 0 || r1 > r2) {
  1300.     error ("Invalid range");
  1301.     return;
  1302.     }
  1303.     if (r2 > maxrows-1) {
  1304.     r2 = maxrows-1;
  1305.     }
  1306.     FullUpdate++;
  1307.     modflg++;
  1308.     while (r1 <= r2)
  1309.     row_hidden[r1++] = 0;
  1310. }
  1311.  
  1312. /* mark a column as not-hidden */
  1313. void
  1314. showcol(c1, c2)
  1315. int c1, c2;
  1316. {
  1317.     if (c1 < 0 || c1 > c2) {
  1318.     error ("Invalid range");
  1319.     return;
  1320.     }
  1321.     if (c2 > maxcols-1) {
  1322.     c2 = maxcols-1;
  1323.     }
  1324.     FullUpdate++;
  1325.     modflg++;
  1326.     while (c1 <= c2)
  1327.     col_hidden[c1++] = FALSE;
  1328. }
  1329.  
  1330. /* Open the output file, setting up a pipe if needed */
  1331. FILE *
  1332. openout(fname, rpid)
  1333. char *fname;
  1334. int *rpid;
  1335. {
  1336.     int pipefd[2];
  1337.     int pid;
  1338.     FILE *f = NULL;
  1339.     char *efname;
  1340.  
  1341.     while (*fname && (*fname == ' '))  /* Skip leading blanks */
  1342.     fname++;
  1343.  
  1344.     if (*fname != '|') {        /* Open file if not pipe */
  1345.     *rpid = 0;
  1346.     
  1347.     efname = findhome(fname);
  1348. #ifdef DOBACKUPS
  1349.     if (!backup_file(efname) &&
  1350.         (yn_ask("Could not create backup copy, Save anyhow?: (y,n)") != 1))
  1351.         return(0);
  1352. #endif
  1353.     return(fopen(efname, "w"));
  1354.     }
  1355.  
  1356. #if defined(MSDOS)
  1357.     error("Piping not available under MS-DOS\n");
  1358.     return(0);
  1359. #else
  1360.     fname++;                /* Skip | */
  1361.     if ( pipe (pipefd) < 0) {
  1362.     error("Can't make pipe to child");
  1363.     *rpid = 0;
  1364.     return(0);
  1365.     }
  1366.  
  1367. #ifndef DOINGX
  1368.     deraw();
  1369. #endif
  1370. #ifdef VMS
  1371.     fprintf(stderr, "No son tasks available yet under VMS--sorry\n");
  1372. #else /* VMS */
  1373.  
  1374.     if ((pid=fork()) == 0)              /* if child  */
  1375.     {
  1376.     (void) close (0);              /* close stdin */
  1377.     (void) close (pipefd[1]);
  1378.     (void) dup (pipefd[0]);          /* connect to pipe input */
  1379.     (void) signal (SIGINT, SIG_DFL);      /* reset */
  1380.     (void) execl ("/bin/sh", "sh", "-c", fname, 0);
  1381.     exit (-127);
  1382.     }
  1383.     else                  /* else parent */
  1384.     {
  1385.     *rpid = pid;
  1386.     if ((f = fdopen (pipefd[1], "w")) == (FILE *)0)
  1387.     {
  1388.         (void) kill (pid, -9);
  1389.         error ("Can't fdopen output");
  1390.         (void) close (pipefd[1]);
  1391.         *rpid = 0;
  1392.         return(0);
  1393.     }
  1394.     }
  1395. #endif /* VMS */
  1396.     return(f);
  1397. #endif /* MSDOS */
  1398. }
  1399.  
  1400. /* close a file opened by openout(), if process wait for return */
  1401. void
  1402. closeout(f, pid)
  1403. FILE *f;
  1404. int pid;
  1405. {
  1406.     int temp;
  1407.  
  1408.     (void) fclose (f);
  1409. #if !defined(MSDOS)
  1410.     if (pid) {
  1411.          while (pid != wait(&temp)) /**/;
  1412.      (void) printf("Press RETURN to continue ");
  1413.      (void) fflush(stdout);
  1414.      (void) nmgetch();
  1415. #ifndef DOINGX
  1416.      goraw();
  1417. #endif
  1418.     }
  1419. #endif /* MSDOS */
  1420. }
  1421.  
  1422. void
  1423. copyent(n,p,dr,dc)
  1424.         register struct ent *n, *p;
  1425.         int dr, dc;
  1426. {
  1427.     if(!n||!p)
  1428.     {    error("internal error");
  1429.     return;
  1430.     }
  1431.     n -> v = p -> v;
  1432.     n -> flags = p -> flags;
  1433.     n -> expr = copye (p -> expr, dr, dc);
  1434.     n -> label = (char *)0;
  1435.     if (p -> label) {
  1436.     n -> label = scxmalloc ((unsigned) (strlen (p -> label) + 1));
  1437.     (void) strcpy (n -> label, p -> label);
  1438.     }
  1439.     n -> format = 0;
  1440.     if (p -> format) {
  1441.         n -> format = scxmalloc ((unsigned) (strlen (p -> format) + 1));
  1442.     (void) strcpy (n -> format, p -> format);
  1443.     }
  1444. }
  1445.  
  1446. void
  1447. write_fd (f, r0, c0, rn, cn)
  1448. register FILE *f;
  1449. int r0, c0, rn, cn;
  1450. {
  1451.     register struct ent **pp;
  1452.     register r, c;
  1453.  
  1454.     (void) fprintf (f, "# This data file was generated by the Spreadsheet ");
  1455.     (void) fprintf (f, "Calculator.\n");
  1456.     (void) fprintf (f, "# You almost certainly shouldn't edit it.\n\n");
  1457.     graphic_write_defn(f);    /* write graph definitions, if any exist */
  1458.     print_options(f);
  1459.     for (c=0; c<maxcols; c++)
  1460.     if (fwidth[c] != DEFWIDTH || precision[c] != DEFPREC || realfmt[c] != DEFREFMT )
  1461.         (void) fprintf (f, "format %s %d %d %d\n",coltoa(c),fwidth[c],precision[c],realfmt[c]);
  1462.     for (c=c0; c<cn; c++) {
  1463.         if (col_hidden[c]) {
  1464.             (void) fprintf(f, "hide %s\n", coltoa(c));
  1465.         }
  1466.     }
  1467.     for (r=r0; r<=rn; r++) {
  1468.     if (row_hidden[r]) {
  1469.         (void) fprintf(f, "hide %d\n", r);
  1470.     }
  1471.     }
  1472.  
  1473.     write_range(f);
  1474.  
  1475.     if (mdir) 
  1476.         (void) fprintf(f, "mdir \"%s\"\n", mdir);
  1477.     for (r=r0; r<=rn; r++) {
  1478.     pp = ATBL(tbl, r, c0);
  1479.     for (c=c0; c<=cn; c++, pp++)
  1480.         if (*pp) {
  1481.         if ((*pp)->label || (*pp)->flags&is_strexpr) {
  1482.             edits(r,c);
  1483.             (void) fprintf(f, "%s\n",line);
  1484.         }
  1485.         if ((*pp)->flags&is_valid) {
  1486.             editv (r, c);
  1487.             (void) fprintf (f, "%s\n",line);
  1488.         }
  1489.         if ((*pp)->format) {
  1490.             editfmt (r, c);
  1491.             (void) fprintf (f, "%s\n",line);
  1492.         }
  1493.         if ((*pp)->flags&is_locked)
  1494.             (void) fprintf(f, "lock %s%d\n", coltoa((*pp)->col),
  1495.                              (*pp)->row) ;
  1496.         }
  1497.     }
  1498.     if (rndinfinity)
  1499.     fprintf(f, "set rndinfinity\n");
  1500.     fprintf(f, "goto %s\n", v_name( currow, curcol ) );
  1501. }
  1502.  
  1503. int
  1504. writefile (fname, r0, c0, rn, cn)
  1505. char *fname;
  1506. int r0, c0, rn, cn;
  1507. {
  1508.     register FILE *f;
  1509.     char save[PATHLEN];
  1510.     int pid;
  1511.  
  1512. #if !defined(VMS) && !defined(MSDOS) && defined(CRYPT_PATH)
  1513.     if (Crypt) {
  1514.     return (cwritefile(fname, r0, c0, rn, cn));
  1515.     }
  1516. #endif /* VMS */
  1517.  
  1518.     if (*fname == '\0') fname = curfile;
  1519.  
  1520.     (void) strcpy(save,fname);
  1521.  
  1522.     if ((f= openout(fname, &pid)) == (FILE *)0)
  1523.     {    sprintf(stringbuf, "Can't create file \"%s\"", fname);
  1524.     error(stringbuf);
  1525.     return (-1);
  1526.     }
  1527.  
  1528.     write_fd(f, r0, c0, rn, cn);
  1529.     
  1530.     closeout(f, pid);
  1531.  
  1532.     if (!pid) {
  1533.         (void) strcpy(curfile, save);
  1534.         modflg = 0;
  1535.     sprintf(stringbuf,"File \"%s\" written.",curfile);
  1536.     error(stringbuf);
  1537.     }
  1538.  
  1539.     return (0);
  1540. }
  1541.  
  1542. void
  1543. readfile (fname,eraseflg)
  1544. char *fname;
  1545. int eraseflg;
  1546. {
  1547.     register FILE *f;
  1548.     char save[PATHLEN];
  1549.     int tempautolabel;
  1550.  
  1551.     tempautolabel = autolabel;        /* turn off auto label */
  1552.     autolabel = 0;            /* when reading a file  */
  1553.  
  1554.     if (*fname == '*' && mdir) { 
  1555.        (void) strcpy(save, mdir);
  1556.        *fname = '/';
  1557.        (void) strcat(save, fname);
  1558.     } else {
  1559.         if (*fname == '\0')
  1560.         fname = curfile;
  1561.         (void) strcpy(save,fname);
  1562.     }
  1563.  
  1564. #if !defined(VMS) && !defined(MSDOS) && defined(CRYPT_PATH)
  1565.     if (Crypt)  {
  1566.     creadfile(save, eraseflg);
  1567.     return;
  1568.     }
  1569. #endif /* VMS */
  1570.  
  1571.     if (eraseflg && strcmp(fname,curfile) && modcheck(" first"))
  1572.     return;
  1573.  
  1574.     if ((f = fopen(findhome(save), "r")) == (FILE *)0)
  1575.     {    sprintf(stringbuf, "Can't read file \"%s\"", save);
  1576.     error(stringbuf);
  1577.     return;
  1578.     }
  1579.  
  1580.     if (eraseflg)
  1581.     erasedb();
  1582.  
  1583.     loading++;
  1584.     while (fgets(line, sizeof(line), f)) {
  1585.     linelim = 0;
  1586.     if (line[0] == 'G')     /* indicates graph definitions */
  1587.         graphic_read_defn(f);
  1588.     else if (line[0] != '#') (void) yyparse ();
  1589.     }
  1590.     --loading;
  1591.     (void) fclose (f);
  1592.     linelim = -1;
  1593.     modflg++;
  1594.     if (eraseflg) {
  1595.     (void) strcpy(curfile,save);
  1596.     modflg = 0;
  1597.     }
  1598.     autolabel = tempautolabel;
  1599.     EvalAll();
  1600. }
  1601.  
  1602.  
  1603. /* erase the database (tbl, etc.) */
  1604. void
  1605. erasedb ()
  1606. {
  1607.     register r, c;
  1608.     for (c = 0; c<=maxcol; c++) {
  1609.     fwidth[c] = DEFWIDTH;
  1610.     precision[c] = DEFPREC;
  1611.     realfmt[c] = DEFREFMT;
  1612.     }
  1613.  
  1614.     for (r = 0; r<=maxrow; r++) {
  1615.     register struct ent **pp = ATBL(tbl, r, 0);
  1616.     for (c=0; c++<=maxcol; pp++)
  1617.         if (*pp) {
  1618.         if ((*pp)->expr)  efree ((*pp) -> expr);
  1619.         if ((*pp)->label) scxfree ((char *)((*pp) -> label));
  1620.         (*pp)->next = freeents;    /* save [struct ent] for reuse */
  1621.         freeents = *pp;
  1622.         *pp = (struct ent *)0;
  1623.         }
  1624.     }
  1625.     maxrow = 0;
  1626.     maxcol = 0;
  1627.     clean_range();
  1628.     FullUpdate++;
  1629. }
  1630.  
  1631. /* moves curcol back one displayed column */
  1632. void
  1633. backcol(arg)
  1634.     int arg;
  1635. {    
  1636.     prvmx=lastmx; prvmy=lastmy, prvcol=lastcol; /* line added by Bob Parbs 12-92 */ 
  1637.     while (--arg>=0) {
  1638.     if (curcol)
  1639.         curcol--;
  1640.     else
  1641.     {    error ("At column A");
  1642.         break;
  1643.     }
  1644.     while(col_hidden[curcol] && curcol)
  1645.         curcol--;
  1646.     }
  1647. }
  1648.  
  1649. /* moves curcol forward one displayed column */
  1650. void
  1651. forwcol(arg)
  1652.     int arg;
  1653. {
  1654.     prvmx=lastmx; prvmy=lastmy; prvcol=lastcol; /* line added by Bob Parbs 12-92 */
  1655.     while (--arg>=0) {
  1656.     if (curcol < maxcols - 1)
  1657.         curcol++;
  1658.     else
  1659.     if (!growtbl(GROWCOL, 0, arg))    /* get as much as needed */
  1660.         break;
  1661.     else
  1662.         curcol++;
  1663.     while(col_hidden[curcol]&&(curcol<maxcols-1))
  1664.         curcol++;
  1665.     }
  1666. }
  1667.  
  1668. /* moves currow forward one displayed row */
  1669. void
  1670. forwrow(arg)
  1671.     int arg;
  1672. {
  1673.     prvmx=lastmx; prvmy=lastmy; prvcol=lastcol; /* line added by Bob Parbs 12-92 */
  1674.     while (--arg>=0) {
  1675.     if (currow < maxrows - 1)
  1676.         currow++;
  1677.     else
  1678.     if (!growtbl(GROWROW, arg, 0))    /* get as much as needed */
  1679.         break;
  1680.     else
  1681.         currow++;
  1682.     while (row_hidden[currow]&&(currow<maxrows-1))
  1683.         currow++;
  1684.     }
  1685. }
  1686.  
  1687. /* moves currow backward one displayed row */
  1688. void
  1689. backrow(arg)
  1690.     int arg;
  1691. {
  1692.     prvmx=lastmx; prvmy=lastmy; prvcol=lastcol; /* line added by Bob Parbs 12-92 */
  1693.     while (--arg>=0) {
  1694.     if (currow)
  1695.         currow--;
  1696.     else
  1697.     {    error ("At row zero");
  1698.         break;
  1699.     }
  1700.     while (row_hidden[currow] && currow)
  1701.         currow--;
  1702.     }
  1703. }
  1704.  
  1705.  
  1706. /*
  1707.  * Show a cell's label string or expression value.  May overwrite value if
  1708.  * there is one already displayed in the cell.  Created from old code in
  1709.  * update(), copied with minimal changes.
  1710.  */
  1711.  
  1712. void
  1713. showstring (string, dirflush, hasvalue, row, col, nextcolp, mxcol, fieldlenp, r, c)
  1714.     char *string;    /* to display */
  1715.     int dirflush;    /* or rightflush or centered */
  1716.     int hasvalue;    /* is there a numeric value? */
  1717.     int row, col;    /* spreadsheet location */
  1718.     int *nextcolp;    /* value returned through it */
  1719.     int mxcol;        /* last column displayed? */
  1720.     int *fieldlenp;    /* value returned through it */
  1721.     int r, c;        /* screen row and column */
  1722. {
  1723.     register int nextcol  = *nextcolp;
  1724.     register int fieldlen = *fieldlenp;
  1725.  
  1726.     char field[FBUFLEN];
  1727.     int  slen;
  1728.     char *start, *last;
  1729.     register char *fp;
  1730.     struct ent *nc;
  1731.  
  1732.     /* This figures out if the label is allowed to
  1733.        slop over into the next blank field */
  1734.  
  1735.     slen = strlen (string);
  1736.     if( *string == '\\' && *(string+1)!= '\0' )
  1737.     slen = fwidth[col];
  1738.     while ((slen > fieldlen) && (nextcol <= mxcol) &&
  1739.        !((nc = lookat (row, nextcol)) -> flags & is_valid) &&
  1740.        !(nc->label)) {
  1741.  
  1742.     if (! col_hidden [nextcol])
  1743.         fieldlen += fwidth [nextcol];
  1744.  
  1745.     nextcol++;
  1746.     }
  1747.     if (slen > fieldlen)
  1748.     slen = fieldlen;
  1749.  
  1750.     /* Now justify and print */
  1751.     start = (dirflush&is_leftflush) ? field : field + fieldlen - slen;
  1752.     if( dirflush & is_label )
  1753.     start = field + ((slen<fwidth[col])?(fieldlen-slen)/2:0);
  1754.     last = field+fieldlen;
  1755.     fp = field;
  1756.     while (fp < start)
  1757.     *fp++ = ' ';
  1758.     if( *string == '\\'  && *(string+1)!= '\0') {
  1759.     char *strt;
  1760.     strt = ++string;
  1761.  
  1762.     while(slen--) {
  1763.         *fp++ = *string++;
  1764.         if( *string == '\0' )
  1765.             string = strt;
  1766.     }
  1767.     }
  1768.     else
  1769.     while (slen--)
  1770.     *fp++ = *string++;
  1771.  
  1772.     if ((! hasvalue) || fieldlen != fwidth[col]) 
  1773.     while (fp < last)
  1774.         *fp++ = ' ';
  1775.     *fp = '\0';
  1776. #ifdef DOINGX
  1777.     XDrawImageString(dpy, mainwin, maingc,
  1778.              textcol(c), textrow(r),
  1779.              field, strlen(field));
  1780. #else
  1781. # ifdef VMS
  1782.     mvaddstr(r, c, field);    /* this is a macro */
  1783. # else
  1784.     (void) mvaddstr(r, c, field);
  1785. # endif
  1786. #endif
  1787.  
  1788.     *nextcolp  = nextcol;
  1789.     *fieldlenp = fieldlen;
  1790. }
  1791.  
  1792. int
  1793. etype(e)
  1794. register struct enode *e;
  1795. {
  1796.     if (e == (struct enode *)0)
  1797.     return NUM;
  1798.     switch (e->op) {
  1799.     case UPPER: case LOWER: case CAPITAL:
  1800.     case O_SCONST: case '#': case DATE: case FMT: case STINDEX:
  1801.     case EXT: case SVAL: case SUBSTR:
  1802.         return (STR);
  1803.  
  1804.     case '?':
  1805.     case IF:
  1806.         return(etype(e->e.o.right->e.o.left));
  1807.  
  1808.     case 'f':
  1809.         return(etype(e->e.o.right));
  1810.  
  1811.     case O_VAR: {
  1812.     register struct ent *p;
  1813.     p = e->e.v.vp;
  1814.     if (p->expr) 
  1815.         return(p->flags & is_strexpr ? STR : NUM);
  1816.     else if (p->label)
  1817.         return(STR);
  1818.     else
  1819.         return(NUM);
  1820.     }
  1821.  
  1822.     default:
  1823.     return(NUM);
  1824.     }
  1825. }
  1826.  
  1827. /* return 1 if yes given, 0 otherwise */
  1828. int
  1829. yn_ask(msg)
  1830. char    *msg;
  1831. {    char ch;
  1832.     int done = 0;
  1833.  
  1834. #ifdef DOINGX
  1835.     XEvent event;
  1836.     char buffer[8];
  1837.  
  1838. /* I'd rather use line 1 than line 0, because that is where the user will 
  1839.  * be looking most often.  (B.Backman) 
  1840.  */
  1841.     clearlines(1,1);
  1842.     XDrawImageString(dpy, mainwin, maingc,
  1843.              textcol(0), textrow(1),
  1844.              msg, strlen(msg));
  1845.     while (!done)
  1846.     {
  1847.         XPeekEvent(dpy, &event);
  1848.  
  1849.         while (event.type == MotionNotify)
  1850.            XNextEvent(dpy,&event);
  1851.         if (event.type == KeyPress)
  1852.         {  
  1853.            XNextEvent(dpy,&event);
  1854.            if(XLookupString(&(event.xkey),buffer,8,0,0))
  1855.            {
  1856.             ch = buffer[0];
  1857.             if (ch != 'y' && ch != 'Y' && ch != 'n' && ch != 'N')
  1858.             {    if (ch == ctl('g') || ch == ESC)
  1859.                     return(-1);
  1860.                 error("y or n response required");
  1861.                 return (-1);
  1862.             }
  1863.             if (ch == 'y' || ch == 'Y')
  1864.                 return(1);
  1865.             else
  1866.                 return(0);
  1867.                    }
  1868.         }
  1869.         else 
  1870.         {
  1871.            error("Y or N keypress is required");
  1872.            return(-1);
  1873.         }
  1874.     }
  1875. #else
  1876.     (void) move (0, 0);
  1877.     (void) clrtoeol ();
  1878.     (void) addstr (msg);
  1879.     (void) refresh();
  1880.     ch = nmgetch();
  1881.     if ( ch != 'y' && ch != 'Y' && ch != 'n' && ch != 'N' ) {
  1882.         if (ch == ctl('g') || ch == ESC)
  1883.             return(-1);
  1884.         error("y or n response required");
  1885.         return (-1);
  1886.     }
  1887.     if (ch == 'y' || ch == 'Y')
  1888.         return(1);
  1889.     else
  1890.         return(0);
  1891. #endif
  1892. }
  1893.  
  1894. /* expand a ~ in a path to your home directory */
  1895. #if !defined(MSDOS) && !defined(VMS)
  1896. #include <pwd.h>
  1897. #endif
  1898. char    *
  1899. findhome(path)
  1900. char    *path;
  1901. {
  1902.     static    char    *HomeDir = NULL;
  1903.  
  1904.     if (*path == '~')
  1905.     {    char    *pathptr;
  1906.         char    tmppath[PATHLEN];
  1907.  
  1908.         if (HomeDir == NULL)
  1909.         {    HomeDir = getenv("HOME");
  1910.             if (HomeDir == NULL)
  1911.                 HomeDir = "/";
  1912.         }
  1913.         pathptr = path + 1;
  1914.         if ((*pathptr == '/') || (*pathptr == '\0'))
  1915.         {    strcpy(tmppath, HomeDir);
  1916.         }
  1917. #if !defined(MSDOS) && !defined(VMS)
  1918.         else
  1919.         {    struct    passwd *pwent;
  1920. #ifndef __STDC__
  1921.             extern    struct    passwd *getpwnam();
  1922. #endif
  1923.             char    *namep;
  1924.             char    name[50];
  1925.  
  1926.             namep = name;
  1927.             while ((*pathptr != '\0') && (*pathptr != '/'))
  1928.                 *(namep++) = *(pathptr++);
  1929.             *namep = '\0';
  1930.             if ((pwent = getpwnam(name)) == NULL)
  1931.             {    (void) sprintf(path, "Can't find user %s", name);
  1932.                 return(NULL);
  1933.             }
  1934.             strcpy(tmppath, pwent->pw_dir);
  1935.         }
  1936. #endif
  1937.         strcat(tmppath, pathptr);
  1938.         strcpy(path, tmppath);
  1939.     }
  1940.     return(path);
  1941. }
  1942.  
  1943. #ifdef DOBACKUPS
  1944. #include <sys/stat.h>
  1945.  
  1946. /******************************************
  1947.  * Determine default dir to be printed out, 
  1948.  * based on mdir.  Check for //  **********/ 
  1949. void get_default_dir(char tmp[PATHLEN])
  1950. {
  1951.     int length; 
  1952.  
  1953.     if (mdir)
  1954.         strcpy(tmp, mdir);       
  1955.     else {          /* make home dir the default dir */
  1956.         strcpy(tmp, getenv("HOME"));
  1957.         if (tmp == NULL)
  1958.             *tmp = '/';
  1959.     }
  1960.     /* append exactly one / to end of tmp */
  1961.     length = strlen(tmp);
  1962.     if ((tmp[length-1] == '/')&&(tmp[length-2] == '/'))
  1963.     tmp[length-1] = NULL;
  1964.     else if (tmp[length-1] != '/')
  1965.     {    tmp[length] = '/';
  1966.     tmp[length+1] = NULL;
  1967.     }
  1968. }
  1969.  
  1970.  
  1971. /***************************************************************
  1972.  * give tmp the string to be printed out as the default path, as
  1973.  * determined by mdir and curfile, which may also be adjusted ***/
  1974. void get_default_path(char tmp[PATHLEN])
  1975. {
  1976.     int i, j, len_mdir, len_curfile, len_same, length;
  1977.     char tmp2[PATHLEN];
  1978.  
  1979.     if (!(mdir)&&!(*curfile))       /* give tmp the home dir, with 1 / at end */
  1980.     {    strcpy(tmp, getenv("HOME"));
  1981.         if (tmp == NULL)
  1982.             *tmp = '/';
  1983.         else        /* this is to add a / to end of default dir */
  1984.         {   length = strlen(tmp);
  1985.             tmp[length] = '/';
  1986.             tmp[length +1] = NULL;                
  1987.         }
  1988.     }
  1989.     else if((mdir)&&!(*curfile))     /* copy mdir to tmp, with 1 / at end */ 
  1990.     {    length = strlen(mdir);
  1991.         strcpy(tmp, mdir); 
  1992.         if ((tmp[length -1] == '/')&&(tmp[length -2] == '/'))
  1993.             tmp[length -1] = NULL;
  1994.     if (tmp[length -1] != '/')
  1995.     {   tmp[length] = '/';
  1996.         tmp[length +1] = NULL;    
  1997.      }
  1998.     }                                                    
  1999.     else if(!(mdir)&&(*curfile))      /* print out most recent curfile */
  2000.          strcpy(tmp, curfile);
  2001.  
  2002.     else      /* ((mdir)&&(*curfile)), find best solution */
  2003.     {    len_mdir = strlen(mdir);
  2004.         len_curfile = strlen(curfile);      
  2005.  
  2006.     if (strcmp(mdir, "/") == 0)
  2007.     {   if (*curfile == '/')
  2008.         strcpy(tmp, curfile);
  2009.         else        /* tmp = /curfile */
  2010.         {    tmp[0] = '/';
  2011.         strcpy(tmp2, curfile);
  2012.         for (i = 1; i <= len_curfile; i++) 
  2013.             tmp[i] = tmp2[i -1];
  2014.         }
  2015.         return;
  2016.     }
  2017.     /* find out how many initial chars are the same */
  2018.     strcpy(tmp, curfile);
  2019.     strcpy(tmp2, mdir);
  2020.     for (i = 0; (i < len_mdir)&&(i < len_curfile); i++)
  2021.         if (tmp[i] != tmp2[i])  break;
  2022.     len_same = i;
  2023.     if (len_same < 3)    /* leave it alone */
  2024.         return;
  2025.     else
  2026.     {   /* find length of last word in curfile */
  2027.         for (i = len_curfile -1; i > 0; i--)
  2028.         if (tmp[i] == '/')    /* strange, no / present */
  2029.             return;
  2030.         /* now copy entire path minus filename to mdir */
  2031.         strcpy(tmp2, tmp); /* note: length of mdir can be shortened, */
  2032.         tmp2[i +1] = NULL; /*     but not increased.  This is      */    
  2033.         strcpy(mdir, tmp2);/*    still better than nothing.     */
  2034.     }
  2035.     }
  2036. }   /* get_default_path */
  2037.  
  2038.  
  2039. /*
  2040.  * make a backup copy of a file, use the same mode and name in the format
  2041.  * [path/]#file~
  2042.  * return 1 if we were successful, 0 otherwise
  2043.  */
  2044. int
  2045. backup_file(path)
  2046. char    *path;
  2047. {
  2048.     struct    stat    statbuf;
  2049.     char    fname[PATHLEN];
  2050.     char    tpath[PATHLEN];
  2051. #ifdef sequent
  2052.     static    char    *buf = NULL;
  2053.     static    unsigned buflen = 0;
  2054. #else
  2055.     char    buf[BUFSIZ];
  2056. #endif
  2057.     char    *tpp;
  2058.     int    infd, outfd;
  2059.     int    count;
  2060.  
  2061.     /* tpath will be the [path/]file ---> [path/]#file~ */
  2062.     strcpy(tpath, path);
  2063.     if ((tpp = strrchr(tpath, '/')) == NULL)
  2064.         tpp = tpath;
  2065.     else
  2066.         tpp++;
  2067.     strcpy(fname, tpp);
  2068.     (void) sprintf(tpp, "#%s~", fname);
  2069.  
  2070.     if (stat(path, &statbuf) == 0)
  2071.     {
  2072.         /* if we know the optimum block size, use it */
  2073. #ifdef sequent
  2074.         if ((statbuf.st_blksize > buflen) || (buf == NULL))
  2075.         {    buflen = statbuf.st_blksize;
  2076.             if ((buf = scxrealloc(buf, buflen)) == (char *)0)
  2077.             {    buflen = 0;
  2078.                 return(0);
  2079.             }
  2080.         }
  2081. #endif
  2082.  
  2083.         if ((infd = open(path, O_RDONLY, 0)) < 0)
  2084.             return(0);
  2085.  
  2086.         if ((outfd = open(tpath, O_TRUNC|O_WRONLY|O_CREAT,
  2087.                     statbuf.st_mode)) < 0)
  2088.             return(0);
  2089.  
  2090. #ifdef sequent
  2091.         while((count = read(infd, buf, statbuf.st_blksize)) > 0)
  2092. #else
  2093.         while((count = read(infd, buf, sizeof(buf))) > 0)
  2094. #endif
  2095.         {    if (write(outfd, buf, count) != count)
  2096.             {    count = -1;
  2097.                 break;
  2098.             }
  2099.         }
  2100.         close(infd);
  2101.         close(outfd);
  2102.  
  2103.         return((count < 0) ? 0 : 1);
  2104.     }
  2105.     else
  2106.     if (errno == ENOENT)
  2107.         return(1);
  2108.     return(0);
  2109. }
  2110. #endif
  2111.  
  2112. /* replace a possible '.sc', '.s', or '.' suffix with "ending" */
  2113. char *
  2114. fsuffix(fname, ending)
  2115. char    *fname;
  2116. char    *ending;
  2117. {    char    *newname, *chp, *slp;
  2118.  
  2119.     if ((newname = scxmalloc(strlen(fname)+1+strlen(ending))) != NULL)
  2120.     {
  2121.         strcpy(newname, fname);
  2122.         /*
  2123.          * chp will point to the start of the filename, path seperator
  2124.          */
  2125.         if ((slp = strrchr(newname, '/')) != NULL)
  2126.         {    *slp = '\0';
  2127.             chp = slp+1;
  2128.         }
  2129.         else
  2130.             chp = newname;
  2131.     
  2132.         /* start of .sc? */
  2133.         if ((chp = strrchr(chp, '.')) != NULL)
  2134.         {    if (strncmp(chp, ".sc", strlen(chp)) == 0)
  2135.                 *chp = '\0';
  2136.         }
  2137.  
  2138.         /* put the path '/' back */
  2139.         if (slp != NULL)
  2140.             *slp = '/';
  2141.         strcat(newname, ending);
  2142.     }
  2143.     return(newname);
  2144. }
  2145.