home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / VILE327.ZIP / VILE327.TAR / vile3.27 / file.c < prev    next >
C/C++ Source or Header  |  1992-12-14  |  44KB  |  1,851 lines

  1. /*    FILE.C:   for MicroEMACS
  2.  *
  3.  *    The routines in this file handle the reading, writing
  4.  *    and lookup of disk files.  All of details about the
  5.  *    reading and writing of the disk are in "fileio.c".
  6.  *
  7.  *
  8.  * $Log: file.c,v $
  9.  * Revision 1.63  1992/12/05  13:55:06  foxharp
  10.  * rvstrcpy is now here, and ifdef'ed
  11.  *
  12.  * Revision 1.62  1992/12/04  09:26:43  foxharp
  13.  * added apollo leading '//' fix to canonpath (probably needs work elsewhere
  14.  * as well), and deleted unused assigns
  15.  *
  16.  * Revision 1.61  1992/11/19  09:06:35  foxharp
  17.  * added kdone() call to complete the ksetup() operation, and
  18.  * fixed possible someday bug with crypt leaving open files around
  19.  *
  20.  * Revision 1.60  1992/08/20  23:40:48  foxharp
  21.  * typo fixes -- thanks, eric
  22.  *
  23.  * Revision 1.59  1992/08/19  22:56:50  foxharp
  24.  * no longer need to multiply or add to NFILEN
  25.  *
  26.  * Revision 1.58  1992/08/06  23:55:07  foxharp
  27.  * changes to canonical pathnames and directory changing to support DOS and
  28.  * its drive designators
  29.  *
  30.  * Revision 1.57  1992/08/05  22:07:50  foxharp
  31.  * glob() on DOS now does something -- it makes sure the drive designator
  32.  * is uppercase
  33.  *
  34.  * Revision 1.56  1992/08/05  21:51:55  foxharp
  35.  * trim trailing slashes in DOS as well as UNIX
  36.  *
  37.  * Revision 1.55  1992/08/05  21:02:51  foxharp
  38.  * check return of mlyesno explicitly against TRUE, since it might return FALSE _or_ ABORT
  39.  *
  40.  * Revision 1.54  1992/07/30  07:29:55  foxharp
  41.  * if a requested name has no slashes, try for it as a buffer name, then
  42.  * canonicalize, and try for buffer and then file, like we used to
  43.  *
  44.  * Revision 1.53  1992/07/24  07:49:38  foxharp
  45.  * shorten_name changes
  46.  *
  47.  * Revision 1.52  1992/07/22  09:16:49  foxharp
  48.  * shorten_path now prints shell commands as-is
  49.  *
  50.  * Revision 1.51  1992/07/20  22:49:19  foxharp
  51.  * prototyped code sure is picky...
  52.  *
  53.  * Revision 1.50  1992/07/18  13:13:56  foxharp
  54.  * put all path-shortening in one place (shorten_path()), and took out some old code now
  55.  * unnecessary
  56.  *
  57.  * Revision 1.49  1992/07/17  19:14:30  foxharp
  58.  * deleted unused locals
  59.  *
  60.  * Revision 1.48  1992/07/15  08:54:40  foxharp
  61.  * give up pretense that we can canonicalize relative pathnames -- all
  62.  * pathnames are stored absolute now anyway.  also, make canonpath
  63.  * deal with DOS \ path separators
  64.  *
  65.  * Revision 1.47  1992/07/13  20:18:51  foxharp
  66.  * took out old ifdef BEFORE code
  67.  *
  68.  * Revision 1.46  1992/07/13  20:03:54  foxharp
  69.  * the "terse" variable is now a boolean mode
  70.  *
  71.  * Revision 1.45  1992/07/13  19:38:03  foxharp
  72.  * finished canonicalizing pathnames
  73.  *
  74.  * Revision 1.44  1992/07/13  09:28:37  foxharp
  75.  * preliminary changes for canonical path names
  76.  *
  77.  * Revision 1.43  1992/07/10  22:00:32  foxharp
  78.  * in signal handler, don't exit after calling abort, to work around
  79.  * bug in sunos 4.1.1 on sparcs
  80.  *
  81.  * Revision 1.42  1992/06/25  23:00:50  foxharp
  82.  * changes for dos/ibmpc
  83.  *
  84.  * Revision 1.41  1992/05/27  08:32:57  foxharp
  85.  * force screen updates while reading from pipes, otherwise bumping the
  86.  * keyboard freezes the screen until the spawned process is done
  87.  *
  88.  * Revision 1.40  1992/05/19  08:55:44  foxharp
  89.  * more prototype and shadowed decl fixups
  90.  *
  91.  * Revision 1.39  1992/05/16  12:00:31  pgf
  92.  * prototypes/ansi/void-int stuff/microsoftC
  93.  *
  94.  * Revision 1.38  1992/04/14  08:51:44  pgf
  95.  * ifdef fixups for pjr and DOS
  96.  *
  97.  * Revision 1.37  1992/04/02  08:28:59  pgf
  98.  * fixed the realloc case of quickreadf()
  99.  *
  100.  * Revision 1.36  1992/03/20  09:00:40  pgf
  101.  * fixed typo
  102.  *
  103.  * Revision 1.35  1992/03/19  23:34:53  pgf
  104.  * set b_linecount on reads and writes of a file
  105.  *
  106.  * Revision 1.34  1992/03/19  23:20:22  pgf
  107.  * SIGT for signals, linux portability
  108.  *
  109.  * Revision 1.33  1992/03/05  09:19:55  pgf
  110.  * changed some mlwrite() to mlforce(), due to new terse support
  111.  *
  112.  * Revision 1.32  1992/02/17  09:04:03  pgf
  113.  * fix null filename dereference, and
  114.  * kill registers now hold unsigned chars
  115.  *
  116.  * Revision 1.31  1992/01/05  00:06:13  pgf
  117.  * split mlwrite into mlwrite/mlprompt/mlforce to make errors visible more
  118.  * often.  also normalized message appearance somewhat.
  119.  *
  120.  * Revision 1.30  1992/01/03  23:31:49  pgf
  121.  * use new ch_fname() to manipulate filenames, since b_fname is now
  122.  * a malloc'ed sting, to avoid length limits
  123.  *
  124.  * Revision 1.29  1991/11/10  21:21:20  pgf
  125.  * don't look for cr/nl pairs on empty lines
  126.  *
  127.  * Revision 1.28  1991/11/08  13:20:18  pgf
  128.  * lint cleanup, and took out lazy filename matching, and
  129.  * changed the DOSFILES code so it's all in this routine, and made it
  130.  * work with quickreadf, where we don't read every line, and fixed
  131.  * core dumps on long-lined files, and allowed for aborting file read
  132.  * operations
  133.  *
  134.  * Revision 1.27  1991/11/07  03:58:31  pgf
  135.  * lint cleanup
  136.  *
  137.  * Revision 1.26  1991/11/03  17:46:30  pgf
  138.  * removed f,n args from all region functions -- they don't use them,
  139.  * since they're no longer directly called by the user
  140.  *
  141.  * Revision 1.25  1991/11/01  14:38:00  pgf
  142.  * saber cleanup
  143.  *
  144.  * Revision 1.24  1991/10/31  02:35:04  pgf
  145.  * avoid malloc(0) in quickreadf
  146.  *
  147.  * Revision 1.23  1991/10/27  01:44:06  pgf
  148.  * used has_C_suffix routine to determine c-mode or not
  149.  *
  150.  * Revision 1.22  1991/10/20  23:07:13  pgf
  151.  * shorten the big file buffer if we don't use it all due to long lines
  152.  *
  153.  * Revision 1.21  1991/10/18  01:17:34  pgf
  154.  * don't seek too far when long lines found in quickread
  155.  *
  156.  * Revision 1.20  1991/10/10  12:33:33  pgf
  157.  * changes to support "block malloc" of line text -- now for most files
  158.  * there is are two mallocs and a single read, no copies.  previously there
  159.  * were two mallocs per line, and two copies (stdio's and ours).  This change
  160.  * implies that lines and line text should not move between buffers, without
  161.  * checking that the text and line struct do not "belong" to the buffer.
  162.  *
  163.  * Revision 1.19  1991/09/24  01:19:14  pgf
  164.  * don't let buffer name change needlessly in fileread() (:e!)
  165.  *
  166.  * Revision 1.18  1991/09/13  01:47:09  pgf
  167.  * lone ":f" now gives same info as ^G
  168.  *
  169.  * Revision 1.17  1991/09/10  01:52:21  pgf
  170.  * buffer name now changes to match filename after ":e!" command (fileread)
  171.  *
  172.  * Revision 1.16  1991/08/16  10:59:35  pgf
  173.  * added WFKILLS to pipe-reading updates, so scrolling gets invoked
  174.  *
  175.  * Revision 1.15  1991/08/12  09:25:10  pgf
  176.  * now store w_line in w_traits while buffer is offscreen, so reframe
  177.  * isn't always necessary.  don't force reframe on redisplay.
  178.  *
  179.  * Revision 1.14  1991/08/08  13:19:08  pgf
  180.  * fixed MDDOS processing, and don't allow writes of view-only buffers
  181.  *
  182.  * Revision 1.13  1991/08/07  12:35:07  pgf
  183.  * added RCS log messages
  184.  *
  185.  * revision 1.12
  186.  * date: 1991/08/06 15:21:21;
  187.  * global/local values
  188.  * 
  189.  * revision 1.11
  190.  * date: 1991/06/25 19:52:35;
  191.  * massive data structure restructure
  192.  * 
  193.  * revision 1.10
  194.  * date: 1991/06/13 15:18:28;
  195.  * fixed comment on glob()
  196.  * 
  197.  * revision 1.9
  198.  * date: 1991/06/03 12:18:17;
  199.  * ifdef'ed TAGS for unresolved ref
  200.  * 
  201.  * revision 1.8
  202.  * date: 1991/05/31 10:58:11;
  203.  * fixed bug in writereg, and
  204.  * made writeregion more like writefile
  205.  * 
  206.  * revision 1.7
  207.  * date: 1991/04/25 12:08:28;
  208.  * use npopen instead of popen for globbing
  209.  * 
  210.  * revision 1.6
  211.  * date: 1991/04/22 08:59:37;
  212.  * fixed globbing to always use /bin/sh
  213.  * 
  214.  * revision 1.5
  215.  * date: 1991/04/08 15:48:59;
  216.  * only update() in readin() if no input pending
  217.  * 
  218.  * revision 1.4
  219.  * date: 1991/04/04 09:36:10;
  220.  * allow for internal callers of ifile()
  221.  * fixed bug with non-unique buffer names at startup
  222.  * 
  223.  * revision 1.3
  224.  * date: 1990/10/01 12:16:11;
  225.  * make mkdir() stuff conditional on ifdef HAVE_MKDIR
  226.  * 
  227.  * revision 1.2
  228.  * date: 1990/09/25 11:38:17;
  229.  * took out old ifdef BEFORE code
  230.  * 
  231.  * revision 1.1
  232.  * date: 1990/09/21 10:25:16;
  233.  * initial vile RCS revision
  234. */
  235.  
  236. #include        <stdio.h>
  237. #include        <string.h>
  238. #include    "estruct.h"
  239. #include        "edef.h"
  240.  
  241.  
  242. extern int fileispipe;
  243. int doslines, unixlines;
  244.  
  245. #if MSDOS
  246. # define slashc(c) (c == '\\' || c == '/')
  247. #else
  248. # define slashc(c) (c == '/')
  249. #endif
  250.  
  251. /*
  252.  * Read a file into the current
  253.  * buffer. This is really easy; all you do it
  254.  * find the name of the file, and call the standard
  255.  * "read a file into the current buffer" code.
  256.  */
  257. /* ARGSUSED */
  258. int
  259. fileread(f, n)
  260. int f,n;
  261. {
  262.         register int    s;
  263.         static char fname[NFILEN];
  264.         char bname[NBUFN];
  265.  
  266.         if ((s=mlreply("Replace with file: ", fname, NFILEN)) != TRUE)
  267.                 return s;
  268.     if ((s = glob(fname)) != TRUE)
  269.         return FALSE;
  270.     /* we want no errors or complaints, so mark it unchanged */
  271.     curbp->b_flag &= ~BFCHG;
  272.         s = readin(fname, TRUE, curbp, TRUE);
  273.     curbp->b_bname[0] = 0;
  274.     makename(bname, fname);
  275.     unqname(bname, TRUE);
  276.     strcpy(curbp->b_bname, bname);
  277.     return s;
  278. }
  279.  
  280. /*
  281.  * Select a file for editing.
  282.  * Look around to see if you can find the
  283.  * file in another buffer; if you can find it
  284.  * just switch to the buffer. If you cannot find
  285.  * the file, create a new buffer, read in the
  286.  * text, and switch to the new buffer.
  287.  * This is ": e"
  288.  */
  289. /* ARGSUSED */
  290. int
  291. filefind(f, n)
  292. int f,n;
  293. {
  294.         static char fname[NFILEN];    /* file user wishes to find */
  295.     char nfname[NFILEN];
  296.         register int s;        /* status return */
  297.  
  298.     if (clexec || isnamedcmd) {
  299.             if ((s=mlreply("Find file: ", fname, NFILEN)) != TRUE)
  300.                     return s;
  301.         } else {
  302.         screen_string(fname,NFILEN,_pathn);
  303.         }
  304.     if ((s = glob(fname)) != TRUE)
  305.         return FALSE;
  306.     strcpy (nfname, fname);
  307. #ifdef LAZINESS
  308.     if (othmode & OTH_LAZY) {
  309.         char rnfname[NFILEN];
  310.         LINE *lp;
  311.         extern BUFFER *filesbp;
  312.         lp = NULL;
  313.         while (flook(nfname, FL_HERE) == NULL) {
  314.             rvstrcpy(rnfname, fname);
  315.             if (makeflist() == FALSE || !sortsearch(rnfname, 
  316.                     strlen(rnfname), filesbp, FALSE, &lp)) {
  317.                 /* give up, and try what they asked for */
  318.                 strcpy (nfname, fname);
  319.                 break;
  320.             }
  321.             rvstrncpy(nfname, lp->l_text, llength(lp));
  322.         }
  323.     }
  324. #endif
  325.     return getfile(nfname, TRUE);
  326. }
  327.  
  328. #ifdef LAZINESS
  329. /*
  330.  * rvstrcpy -- Reverse string copy.
  331.  */
  332. void
  333. rvstrcpy(rvstr, str)
  334. register char    *rvstr, *str;
  335. {
  336.     register int i;
  337.  
  338.     str += (i = strlen(str));
  339.  
  340.     while (i-- > 0)
  341.         *rvstr++ = *--str;
  342.  
  343.     *rvstr = '\0';
  344. }
  345.  
  346. void
  347. rvstrncpy(rvstr, str, n)
  348. register char    *rvstr, *str;
  349. int n;
  350. {
  351.     register int i;
  352.  
  353.     i = strlen(str);
  354.     if (n < i) i = n;
  355.  
  356.     str += i;
  357.  
  358.     while (i-- > 0)
  359.         *rvstr++ = *--str;
  360.  
  361.     *rvstr = '\0';
  362. }
  363. #endif
  364.  
  365. /* ARGSUSED */
  366. int
  367. viewfile(f, n)    /* visit a file in VIEW mode */
  368. int f,n;
  369. {
  370.         char fname[NFILEN];    /* file user wishes to find */
  371.         register int s;        /* status return */
  372.  
  373.     fname[0] = 0;
  374.         if ((s=mlreply("View file: ", fname, NFILEN)) != TRUE)
  375.                 return s;
  376.     if ((s = glob(fname)) != TRUE)
  377.         return FALSE;
  378.     s = getfile(fname, FALSE);
  379.     if (s == TRUE) {    /* if we succeed, put it in view mode */
  380.         make_local_b_val(curwp->w_bufp,MDVIEW);
  381.         set_b_val(curwp->w_bufp,MDVIEW,TRUE);
  382.         markWFMODE(curwp->w_bufp);
  383.     }
  384.     return s;
  385. }
  386.  
  387. /*
  388.  * Insert a file into the current
  389.  * buffer. This is really easy; all you do it
  390.  * find the name of the file, and call the standard
  391.  * "insert a file into the current buffer" code.
  392.  */
  393. static char insfname[NFILEN];
  394.  
  395. /* ARGSUSED */
  396. int
  397. insfile(f, n)
  398. int f,n;
  399. {
  400.         register int    s;
  401.  
  402.     if (!calledbefore) {
  403.             if ((s=mlreply("Insert file: ", insfname, NFILEN)) != TRUE)
  404.                     return s;
  405.         if ((s = glob(insfname)) != TRUE)
  406.             return FALSE;
  407.     }
  408.     if (ukb == 0)
  409.             return ifile(insfname,TRUE,NULL);
  410.     else
  411.             return kifile(insfname);
  412. }
  413.  
  414. int
  415. getfile(fname, lockfl)
  416. char *fname;        /* file name to find */
  417. int lockfl;        /* check the file for locks? */
  418. {
  419.         register BUFFER *bp;
  420.     static BUFFER *tbp;
  421.         register int    s;
  422.         char bname[NBUFN];    /* buffer name to put file */
  423.  
  424. #if    MSDOS
  425.     /* msdos isn't case sensitive */
  426.     if (isupper(fname[0]) && fname [1] == ':')
  427.         mklower(fname+2);
  428.     else
  429.         mklower(fname);
  430. #endif
  431.  
  432.     /* if there are no path delimiters in the name, then the user
  433.         is likely asking for an existing buffer -- try for that
  434.         first */
  435.         if (strchr(fname,slash) == NULL &&
  436.             (bp=bfind(fname, NO_CREAT, 0)) != NULL) {
  437.         return swbuffer(bp);
  438.     }
  439.     /* oh well.  canonicalize the name, and try again */
  440.  
  441.     if (!tbp) {
  442.         if ((tbp=(BUFFER *)malloc(sizeof(BUFFER))) == NULL)
  443.             return FALSE;
  444.     }
  445.     tbp->b_fname = NULL;
  446.     ch_fname(tbp,fname);        /* fill it out */
  447.     fname = tbp->b_fname;
  448.  
  449.         if ((bp=bfind(fname, NO_CREAT, 0)) == NULL) {
  450.         /* it's not already here by that buffer name */
  451.             for (bp=bheadp; bp!=NULL; bp=bp->b_bufp) {
  452.             /* is it here by that filename? */
  453.                     if (strcmp(bp->b_fname, fname)==0) {
  454.                 swbuffer(bp);
  455.                             curwp->w_flag |= WFMODE|WFHARD;
  456.                 if (fname[0] != '!') {
  457.                                 mlwrite("[Old buffer]");
  458.                 } else {
  459.                                 if (mlyesno(
  460.                  "Old command output -- rerun") == TRUE) {
  461.                             return readin(fname, lockfl, 
  462.                                 curbp, TRUE);
  463.                     }
  464.                 }
  465.                     
  466.                             return TRUE;
  467.                     }
  468.             }
  469.         /* it's not here */
  470.             makename(bname, fname);            /* New buffer name.     */
  471.         /* make sure the buffer name doesn't exist */
  472.             while ((bp=bfind(bname, NO_CREAT, 0)) != NULL) {
  473.             if ( !(bp->b_flag & BFCHG) && is_empty_buf(bp)) {
  474.                 /* empty and unmodified -- then it's okay 
  475.                     to re-use this buffer */
  476.                 bp->b_active = 0;
  477.                 return readin(fname, lockfl, bp, TRUE) &&
  478.                         swbuffer(bp);
  479.             }
  480.             /* old buffer name conflict code */
  481.             unqname(bname,TRUE);
  482.                     s = mlreply("Will use buffer name: ", bname, NBUFN);
  483.                     if (s == ABORT)
  484.                             return s;
  485.             if (s == FALSE || bname[0] == 0)
  486.                         makename(bname, fname);
  487.             }
  488.         /* okay, we've got a unique name -- create it */
  489.             if (bp==NULL && (bp=bfind(bname, OK_CREAT, 0))==NULL) {
  490.                     mlforce("[Cannot create buffer]");
  491.                     return FALSE;
  492.             }
  493.         /* switch and read it in. */
  494.         ch_fname(bp,fname);
  495.     }
  496.     return swbuffer(bp);
  497. }
  498.  
  499. /*
  500.     Read file "fname" into a buffer, blowing away any text
  501.     found there.  Returns the final status of the read.
  502. */
  503.  
  504. /* ARGSUSED */
  505. int
  506. readin(fname, lockfl, bp, mflg)
  507. char    *fname;        /* name of file to read */
  508. int    lockfl;        /* check for file locks? */
  509. register BUFFER *bp;    /* read into this buffer */
  510. int    mflg;        /* print messages? */
  511. {
  512.         register WINDOW *wp;
  513.         register int    s;
  514.         int    nline;
  515. #if VMALLOC
  516.     extern int doverifys;
  517.     int odv;
  518. #endif
  519.  
  520. #if    FILOCK
  521.     if (lockfl && lockchk(fname) == ABORT)
  522.         return ABORT;
  523. #endif
  524. #if    CRYPT
  525.     s = resetkey(bp);
  526.     if (s != TRUE)
  527.         return s;
  528. #endif
  529.         if ((s=bclear(bp)) != TRUE)             /* Might be old.        */
  530.                 return s;
  531.         bp->b_flag &= ~(BFINVS|BFCHG);
  532.     ch_fname(bp,fname);
  533. #if DOSFILES
  534.     make_local_b_val(bp,MDDOS);
  535.     set_b_val(bp, MDDOS, global_b_val(MDDOS) );
  536. #endif
  537.  
  538.     /* turn off ALL keyboard translation in case we get a dos error */
  539.     TTkclose();
  540.  
  541.         if ((s = ffropen(fname)) == FIOERR)       /* Hard file open.      */
  542.                 goto out;
  543.  
  544.         if (s == FIOFNF) {                      /* File not found.      */
  545.                 if (mflg) mlwrite("[New file]");
  546.                 goto out;
  547.         }
  548.  
  549.         if (mflg) {
  550.          mlforce("[Reading %s ]", fname);
  551.     }
  552.  
  553. #if UNIX & before
  554.     if (fileispipe)
  555.         ttclean(TRUE);
  556. #endif
  557.     /* read the file in */
  558.         nline = 0;
  559. #if VMALLOC
  560.     /* we really think this stuff is clean... */
  561.     odv = doverifys;
  562.     doverifys = 0;
  563. #endif
  564. #if ! MSDOS    /* DOS has too many 64K limits for this to work */
  565.     if (fileispipe || (s = quickreadf(bp, &nline)) == FIOMEM)
  566. #endif
  567.         s = slowreadf(bp, &nline);
  568.  
  569.     if (s == FIOERR)
  570.         goto out;
  571. #if VMALLOC
  572.     doverifys = odv;
  573. #endif
  574.     bp->b_flag &= ~BFCHG;
  575. #if FINDERR
  576.     if (fileispipe == TRUE) {
  577.         strncpy(febuff,bp->b_bname,NBUFN);
  578.         newfebuff = TRUE;
  579.     }
  580. #endif
  581.         ffclose();                              /* Ignore errors.       */
  582.     if (mflg)
  583.         readlinesmsg(nline,s,fname,ffronly(fname));
  584.  
  585.     bp->b_linecount = nline;
  586.  
  587.     /* set read-only mode for read-only files */
  588.     if (fname[0] == '!' 
  589. #if RONLYVIEW
  590.         || ffronly(fname) 
  591. #endif
  592.     ) {
  593.         make_local_b_val(bp,MDVIEW);
  594.         set_b_val(bp,MDVIEW,TRUE);
  595.     }
  596.     
  597.     bp->b_active = TRUE;
  598.  
  599. out:
  600. #if DOSFILES
  601.     if (b_val(bp, MDDOS)) { /* should we check for dos files? */
  602.         LINE *lp = lforw(bp->b_line.l);
  603.         while (lp != bp->b_line.l) {
  604.             if (llength(lp) && lgetc(lp,llength(lp)-1) == '\r') {
  605.                 llength(lp)--;
  606.                 doslines++;
  607.             } else {
  608.                 unixlines++;
  609.             }
  610.             lp = lforw(lp);
  611.         }
  612.         set_b_val(bp, MDDOS, doslines > unixlines);
  613.     }
  614. #endif
  615.     /* set C mode for C files */
  616.     make_local_b_val(bp,MDCMOD); /* make it local for all, so that
  617.                     subsequent changes to global value
  618.                     will _not_ affect this buffer */
  619.     set_b_val(bp,MDCMOD, (global_b_val(MDCMOD) && has_C_suffix(bp)));
  620.  
  621.     TTkopen();    /* open the keyboard again */
  622.         for (wp=wheadp; wp!=NULL; wp=wp->w_wndp) {
  623.                 if (wp->w_bufp == bp) {
  624.                         wp->w_line.l = lforw(bp->b_line.l);
  625.                         wp->w_dot.l  = lforw(bp->b_line.l);
  626.                         wp->w_dot.o  = 0;
  627. #ifdef WINMARK
  628.                         wp->w_mark = nullmark;
  629. #endif
  630.                         wp->w_lastdot = nullmark;
  631.                         wp->w_flag |= WFMODE|WFHARD;
  632.                 }
  633.         }
  634.         if (s == FIOERR || s == FIOFNF) {    /* False if error.      */
  635. #if UNIX
  636.         extern int sys_nerr, errno;
  637.         extern char *sys_errlist[];
  638.         if (errno > 0 && errno < sys_nerr)
  639.             mlforce("[%s: %s]",fname,sys_errlist[errno]);
  640. #endif
  641.                 return FALSE;
  642.     }
  643. #if     NeWS
  644.         newsreportmodes() ;
  645. #endif
  646.         return TRUE;
  647. }
  648.  
  649. #if ! MSDOS
  650. int
  651. quickreadf(bp, nlinep)
  652. register BUFFER *bp;
  653. int *nlinep;
  654. {
  655.         register unsigned char *textp;
  656.         unsigned char *countp;
  657.         int nlines;
  658.         int incomplete = FALSE;
  659.     long len;
  660.     long ffsize();
  661.  
  662.     if ((len = ffsize()) < 0)
  663.         return FIOERR;
  664.  
  665.     /* avoid malloc(0) problems down below; let slowreadf() do the work */
  666.     if (len == 0)
  667.         return FIOMEM;
  668. #if     MSDOS
  669.     /* cannot allocate more than 64K in dos */
  670.     if (len >= 65535)
  671.         return FIOMEM;
  672. #endif
  673.  
  674.  
  675.  
  676.     /* leave an extra byte at the front, for the length of the first
  677.         line.  after that, lengths go in place of the newline at
  678.         the end of the previous line */
  679.     bp->b_ltext = (unsigned char *)malloc(len + 1);
  680.     if (bp->b_ltext == NULL)
  681.         return FIOMEM;
  682.  
  683.     if ((len = ffread((char *)&bp->b_ltext[1], len)) < 0) {
  684.         free((char *)bp->b_ltext);
  685.         bp->b_ltext = NULL;
  686.         return FIOERR;
  687.     }
  688.  
  689.  
  690.     /* loop through the buffer, replacing all newlines with the
  691.         length of the _following_ line */
  692.     bp->b_ltext_end = bp->b_ltext + len + 1;
  693.     countp = bp->b_ltext;
  694.     textp = countp + 1;
  695.         nlines = 0;
  696.     while (len--) {
  697.         if (*textp == '\n') {
  698.             if (textp - countp >= 255) {
  699.                 unsigned char *np;
  700.                 len = (long)(countp - bp->b_ltext);
  701.                 incomplete = TRUE;
  702.                 /* we'll re-read the rest later */
  703.                 ffseek(len);
  704.                 np = (unsigned char *)realloc(bp->b_ltext, len);
  705.                 if (np == NULL) { /* ugh.  can this happen? */
  706.                       /* (we're _reducing_ the size...) */
  707.                     ffrewind();
  708.                     free((char *)bp->b_ltext);
  709.                     bp->b_ltext = NULL;
  710.                     return FIOMEM;
  711.                 }
  712.                 bp->b_ltext = np;
  713.                 bp->b_ltext_end = np + len + 1;
  714.                 break;
  715.             }
  716.             *countp = textp - countp - 1;
  717.             countp = textp;
  718.             nlines++;
  719.         }
  720.         ++textp;
  721.     }
  722.  
  723.     /* dbgwrite("lines %d, chars %d", nlines, textp - bp->b_ltext); */
  724.     if (nlines == 0) {
  725.         ffrewind();
  726.         if (bp->b_ltext)
  727.             free((char *)bp->b_ltext);
  728.         bp->b_ltext = NULL;
  729.         incomplete = TRUE;
  730.     } else {
  731.         /* allocate all of the line structs we'll need */
  732.         bp->b_LINEs = (LINE *)malloc(nlines * sizeof(LINE));
  733.         if (bp->b_LINEs == NULL) {
  734.             free((char *)bp->b_ltext);
  735.             bp->b_ltext = NULL;
  736.             ffrewind();
  737.             return FIOMEM;
  738.         }
  739.         bp->b_LINEs_end = bp->b_LINEs + nlines;
  740.  
  741.         /* loop through the buffer again, creating
  742.             line data structure for each line */
  743.         {
  744.             register LINE *lp;
  745.             lp = bp->b_LINEs;
  746.             textp = bp->b_ltext;
  747.             while (lp != bp->b_LINEs_end) {
  748.                 lp->l_used = *textp;
  749.                 lp->l_size = *textp + 1;
  750.                 lp->l_text = (char *)textp + 1;
  751.                 lp->l_fp = lp + 1;
  752.                 lp->l_bp = lp - 1;
  753.                 lsetclear(lp);
  754.                 lp->l_nxtundo = NULL;
  755.                 lp++;
  756.                 textp += *textp + 1;
  757.             }
  758.             /*
  759.             if (textp != bp->b_ltext_end - 1)
  760.                 mlwrite("BUG: textp not equal to end %d %d",
  761.                     textp,bp->b_ltext_end);
  762.             */
  763.             lp--;  /* point at last line again */
  764.  
  765.             /* connect the end of the list */
  766.             lp->l_fp = bp->b_line.l;
  767.             bp->b_line.l->l_bp = lp;
  768.  
  769.             /* connect the front of the list */
  770.             bp->b_LINEs->l_bp = bp->b_line.l;
  771.             bp->b_line.l->l_fp = bp->b_LINEs;
  772.         }
  773.     }
  774.  
  775.     *nlinep = nlines;
  776.  
  777.     if (incomplete)
  778.         return FIOMEM;
  779.     return FIOSUC;
  780. }
  781.  
  782. #endif /* ! MSDOS */
  783.  
  784. int
  785. slowreadf(bp, nlinep)
  786. register BUFFER *bp;
  787. int *nlinep;
  788. {
  789.         register WINDOW *wp;
  790.     int s;
  791.     int flag = 0;
  792.         int len;
  793. #if UNIX
  794.         int    done_update = FALSE;
  795. #endif
  796.         while ((s = ffgetline(&len)) == FIOSUC) {
  797. #if DOSFILES
  798.         if (fline[len-1] == '\r') {
  799.             len--;
  800.             doslines++;
  801.         } else {
  802.             unixlines++;
  803.         }
  804. #endif
  805.         if (addline(bp,fline,len) != TRUE) {
  806.                         s = FIOMEM;             /* Keep message on the  */
  807.                         break;                  /* display.             */
  808.                 } 
  809. #if UNIX
  810.         else {
  811.                     /* reading from a pipe, and internal? */
  812.             if (fileispipe && !ffhasdata()) {
  813.                 flag |= WFEDIT;
  814.                 if (!done_update || bp->b_nwnd > 1)
  815.                     flag |= WFHARD;
  816.                     for (wp=wheadp; wp!=NULL; wp=wp->w_wndp) {
  817.                             if (wp->w_bufp == bp) {
  818.                                     wp->w_line.l=
  819.                             lforw(bp->b_line.l);
  820.                                     wp->w_dot.l =
  821.                             lback(bp->b_line.l);
  822.                                     wp->w_dot.o = 0;
  823.                         wp->w_flag |= flag;
  824.                             }
  825.                     }
  826.                 /* track changes in dosfile as lines arrive */
  827.                 set_b_val(bp, MDDOS, 
  828.                  doslines > unixlines && global_b_val(MDDOS) );
  829.                 curwp->w_flag |= WFMODE|WFKILLS;
  830.                 update(TRUE);
  831.                 done_update = TRUE;
  832.                 flag = 0;
  833.             } else {
  834.                 flag |= WFHARD;
  835.             }
  836.             
  837.         }
  838. #endif
  839.                 ++(*nlinep);
  840.         }
  841.     return s;
  842. }
  843.  
  844. /* utility routine for no. of lines read */
  845. void
  846. readlinesmsg(n,s,f,rdo)
  847. int n;
  848. int s;
  849. char *f;
  850. int rdo;
  851. {
  852.     char *m;
  853.     switch(s) {
  854.         case FIOERR:    m = "I/O ERROR, ";    break;
  855.         case FIOMEM:    m = "OUT OF MEMORY, ";    break;
  856.         case FIOABRT:    m = "ABORTED, ";    break;
  857.         default:    m = "";            break;
  858.     }
  859.     if (!global_b_val(MDTERSE))
  860.         mlwrite("[%sRead %d line%s from \"%s\"%s]", m,
  861.             n, n != 1 ? "s":"", f, rdo ? "  (read-only)":"" );
  862.     else
  863.         mlforce("[%s%d lines]",m,n);
  864. }
  865.  
  866. /*
  867.  * Take a file name, and from it
  868.  * fabricate a buffer name. This routine knows
  869.  * about the syntax of file names on the target system.
  870.  * I suppose that this information could be put in
  871.  * a better place than a line of code.
  872.  */
  873.  
  874. void
  875. makename(bname, fname)
  876. char    bname[];
  877. char    fname[];
  878. {
  879.         register char *lastsl;
  880.  
  881.         register char *fcp;
  882.         register char *bcp;
  883.  
  884.     fcp = &fname[strlen(fname)];
  885.     /* trim trailing whitespace */
  886.     while (fcp != fname && (fcp[-1] == ' ' || fcp[-1] == '\t'
  887. #if UNIX || MSDOS /* trim trailing slashes as well */
  888.                      || slashc(fcp[-1])
  889. #endif
  890.                             ) )
  891.                 *(--fcp) = '\0';
  892.     fcp = fname;
  893.     /* trim leading whitespace */
  894.     while (*fcp == ' ' || *fcp == '\t')
  895.         fcp++;
  896.  
  897. #if    ! UNIX
  898.     bcp = fcp;
  899. #endif
  900. #if     AMIGA
  901.         while (bcp!=fcp && bcp[-1]!=':' && bcp[-1]!='/')
  902.                 --bcp;
  903. #endif
  904. #if     VMS
  905.         while (bcp!=fcp && bcp[-1]!=':' && bcp[-1]!=']')
  906.                 --bcp;
  907. #endif
  908. #if     CPM
  909.         while (bcp!=fcp && bcp[-1]!=':')
  910.                 --bcp;
  911. #endif
  912. #if     MSDOS
  913.         while (bcp!=fcp && bcp[-1]!=':' && bcp[-1]!='\\'&&bcp[-1]!='/')
  914.                 --bcp;
  915. #endif
  916. #if     ST520
  917.         while (bcp!=fcp && bcp[-1]!=':' && bcp[-1]!='\\')
  918.                 --bcp;
  919. #endif
  920. #if     UNIX
  921.     bcp = bname;
  922.     if (*fcp == '!') { /* then it's a shell command.  bname is first word */
  923.         *bcp++ = '!';
  924.         do {
  925.             ++fcp;
  926.         } while (isspace(*fcp));
  927.         while (!isspace(*fcp) && bcp < &bname[NBUFN-1])
  928.             *bcp++ = *fcp++;
  929.         *bcp = '\0';
  930.         return;
  931.     }
  932.     lastsl = strrchr(fcp,'/');
  933.     if (lastsl) {
  934.         strncpy(bcp,lastsl+1,NBUFN);
  935.         bcp[NBUFN-1] = '\0';
  936.     } else {  /* no slashes, use the filename as is */
  937.         strncpy(bcp,fcp,NBUFN);
  938.         bcp[NBUFN-1] = '\0';
  939.     }
  940.     return;
  941.  
  942. #else
  943.     {
  944.         register char *cp2;
  945.         cp2 = &bname[0];
  946.         while (cp2!=&bname[NBUFN-1] && *bcp!=0 && *bcp!=';')
  947.                 *cp2++ = *bcp++;
  948.         *cp2 = 0;
  949.     }
  950. #endif
  951. }
  952.  
  953. void
  954. unqname(name,ok_to_ask)    /* make sure a buffer name is unique */
  955. char *name;    /* name to check on */
  956. int ok_to_ask;  /* prompts allowed? */
  957. {
  958.     register char *sp;
  959.  
  960.     /* check to see if it is in the buffer list */
  961.     while (bfind(name, 0, NO_CREAT) != NULL) {
  962.  
  963.         sp = &name[strlen(name)-1];  /* last char */
  964.         if (sp - name >= 2 && sp[-1] == '-') {
  965.             if (sp[0] == '9')
  966.                 sp[0] = 'A';
  967.             else if (sp[0] == 'Z')
  968.                 goto choosename;
  969.             else if (isdigit(sp[0]) || isupper(sp[0]))
  970.                 sp[0] += 1;
  971.         } else if (sp + 2 < &name[NBUFN-1])  {
  972.             strcat(sp, "-1");
  973.         } else {
  974.         choosename:
  975.             if (ok_to_ask) {
  976.                 do {
  977.                     mlreply("Choose a unique buffer name: ",
  978.                          name, NBUFN);
  979.                 } while (name[0] == '\0');
  980.             } else { /* can't ask, just overwrite end of name */
  981.                 sp[-1] = '-';
  982.                 sp[0] = '1';
  983.             }
  984.         }
  985.     }
  986. }
  987.  
  988. /*
  989.  * Ask for a file name, and write the
  990.  * contents of the current buffer to that file.
  991.  */
  992. /* ARGSUSED */
  993. int
  994. filewrite(f, n)
  995. int f,n;
  996. {
  997.         register int    s;
  998.         static char            fname[NFILEN];
  999.  
  1000.     strncpy(fname, curbp->b_fname, NFILEN);
  1001.     
  1002.     /* HACK -- this implies knowledge of how kbd_engl works! */
  1003.     if (isnamedcmd && lastkey != '\r') {
  1004.             if ((s=mlreply("Write to file: ", fname, NFILEN)) != TRUE)
  1005.                     return s;
  1006.         if ((s = glob(fname)) != TRUE)
  1007.             return FALSE;
  1008.         if (strcmp(fname,curbp->b_fname) &&
  1009.             fname[0] != '!' && flook(fname,FL_HERE)) {
  1010.             if (mlyesno("File exists, okay to overwrite") != TRUE) {
  1011.                 mlwrite("File not written");
  1012.                 return FALSE;
  1013.             }
  1014.         }
  1015.         }
  1016.     if (!strcmp(fname,curbp->b_fname) && b_val(curbp,MDVIEW)) {
  1017.         mlforce("[Can't write-back from view mode]");
  1018.         return FALSE;
  1019.     }
  1020.         if ((s=writeout(fname,curbp,TRUE)) == TRUE) {
  1021.                 curbp->b_flag &= ~BFCHG;
  1022.         markWFMODE(curbp);
  1023.         }
  1024.         return s;
  1025. }
  1026.  
  1027. /*
  1028.  * Save the contents of the current
  1029.  * buffer in its associatd file.
  1030.  * Error if there is no remembered file
  1031.  * name for the buffer.
  1032.  */
  1033. /* ARGSUSED */
  1034. int
  1035. filesave(f, n)
  1036. int f,n;
  1037. {
  1038.         register int    s;
  1039.  
  1040.         if (curbp->b_fname[0] == 0) {           /* Must have a name.    */
  1041.                 mlforce("[No file name]");
  1042.                 return FALSE;
  1043.         }
  1044.         if ((s=writeout(curbp->b_fname,curbp,TRUE)) == TRUE) {
  1045.                 curbp->b_flag &= ~BFCHG;
  1046.         markWFMODE(curbp);
  1047.         }
  1048.         return s;
  1049. }
  1050.  
  1051. /*
  1052.  * This function performs the details of file
  1053.  * writing. Uses the file management routines in the
  1054.  * "fileio.c" package. The number of lines written is
  1055.  * displayed. Sadly, it looks inside a LINE; provide
  1056.  * a macro for this. Most of the grief is error
  1057.  * checking of some sort.
  1058.  */
  1059. int
  1060. writeout(fn,bp,msgf)
  1061. char    *fn;
  1062. BUFFER *bp;
  1063. int msgf;
  1064. {
  1065.         register LINE   *lp;        /* current line */
  1066.         register long   numchars;    /* # of chars in file */
  1067.         REGION region;
  1068.  
  1069.     /* starting at the beginning of the buffer */
  1070.         lp = lforw(bp->b_line.l);
  1071.         region.r_orig.l = lp;
  1072.         region.r_orig.o = 0;
  1073.  
  1074.     /* start counting chars */
  1075.         numchars = 0;
  1076.         while (lp != bp->b_line.l) {
  1077.         numchars += llength(lp) + 1;
  1078.         lp = lforw(lp);
  1079.         }
  1080.         region.r_size = numchars;
  1081.         region.r_end = bp->b_line;
  1082.         
  1083.     return writereg(®ion,fn,msgf,b_val(bp, MDDOS),&bp);
  1084. }
  1085.  
  1086. int
  1087. writeregion()
  1088. {
  1089.         REGION region;
  1090.     int s;
  1091.         static char fname[NFILEN];
  1092.  
  1093.     if (isnamedcmd && lastkey == '\r') {
  1094.         strncpy(fname, curbp->b_fname, NFILEN);
  1095.  
  1096.         if (mlyesno("Okay to write [possible] partial range") != TRUE) {
  1097.             mlwrite("Range not written");
  1098.             return FALSE;
  1099.         }
  1100.     } else {
  1101.         /* HACK -- this implies knowledge of 
  1102.                     how kbd_engl works! */
  1103.             if ((s=mlreply("Write region to file: ", fname, NFILEN))
  1104.                              != TRUE)
  1105.                     return s;
  1106.         if ((s = glob(fname)) != TRUE)
  1107.             return FALSE;
  1108.         if (strcmp(fname,curbp->b_fname) &&
  1109.             fname[0] != '!' && flook(fname,FL_HERE)) {
  1110.             if (mlyesno("File exists, okay to overwrite")
  1111.                             != TRUE) {
  1112.                 mlwrite("File not written");
  1113.                 return FALSE;
  1114.             }
  1115.         }
  1116.         }
  1117.         if ((s=getregion(®ion)) != TRUE)
  1118.                 return s;
  1119.     s = writereg(®ion,fname,TRUE,b_val(curbp, MDDOS), NULL);
  1120.         return s;
  1121. }
  1122.  
  1123.  
  1124. int
  1125. writereg(rp,fn,msgf, do_cr, bpp)
  1126. REGION    *rp;
  1127. char    *fn;
  1128. int     msgf;
  1129. int    do_cr;
  1130. BUFFER    **bpp;
  1131. {
  1132.         register int    s;
  1133.         register LINE   *lp;
  1134.         register int    nline;
  1135.     register int i;
  1136.     long lim;
  1137.     long nchar;
  1138.  
  1139. #if    CRYPT
  1140.     s = resetkey(curbp);
  1141.     if (s != TRUE)
  1142.         return s;
  1143. #endif
  1144.         if (*fn == '[' || *fn == ' ') {
  1145.             mlforce("[No filename]");
  1146.             return FALSE;
  1147.         }
  1148.         
  1149.         if ((s=ffwopen(fn)) != FIOSUC)       /* Open writes message. */
  1150.                 return FALSE;
  1151.  
  1152.     /* tell us we're writing */
  1153.     if (msgf == TRUE)
  1154.         mlwrite("[Writing...]");
  1155.  
  1156. #if UNIX & ! NeWS
  1157.     if (fileispipe)
  1158.         ttclean(TRUE);
  1159. #else
  1160.     TTkclose();
  1161. #endif
  1162.  
  1163.         lp = rp->r_orig.l;
  1164.         nline = 0;                              /* Number of lines     */
  1165.         nchar = 0;                              /* Number of chars     */
  1166.  
  1167.     /* First and maybe only line. */
  1168.     if (rp->r_orig.o <= llength(lp)) {
  1169.         if ((lim = rp->r_orig.o+rp->r_size) > llength(lp))
  1170.             lim = (long)llength(lp);
  1171.         for (i = rp->r_orig.o; i < lim; i++) {
  1172.                 if ((s=ffputc(lgetc(lp,i))) != FIOSUC)
  1173.                         goto out;
  1174.             nchar++;
  1175.         }
  1176.         rp->r_size -= nchar;
  1177.  
  1178.         if (rp->r_size <= 0)
  1179.             goto out;
  1180.  
  1181.         if (do_cr && (s=ffputc('\r')) != FIOSUC)
  1182.                     goto out;
  1183.             if ((s=ffputc('\n')) != FIOSUC)
  1184.                     goto out;
  1185.  
  1186.         nchar++;
  1187.         nline++;
  1188.         rp->r_size--;
  1189.                 lp = lforw(lp);
  1190.  
  1191.     }
  1192.  
  1193.     /* whole lines */
  1194.         while (rp->r_size >= llength(lp)+1) {
  1195.                 if ((s=ffputline(&lp->l_text[0], llength(lp), do_cr))
  1196.                         != FIOSUC)
  1197.                         goto out;
  1198.                 ++nline;
  1199.         nchar += llength(lp) + 1;
  1200.         rp->r_size -= llength(lp) + 1;
  1201.                 lp = lforw(lp);
  1202.         }
  1203.  
  1204.     /* last line */
  1205.     if (rp->r_size > 0) {
  1206.         lim = rp->r_size;
  1207.         for (i = 0; i < lim; i++) {
  1208.                 if ((s=ffputc(lgetc(lp,i))) != FIOSUC)
  1209.                         goto out;
  1210.             nchar++;
  1211.             rp->r_size--;
  1212.         }
  1213.     }
  1214.     if (rp->r_size != 0)
  1215.         mlforce("BUG: writereg, rsize == %d",rp->r_size);
  1216.  
  1217.  out:
  1218.         if (s == FIOSUC) {                      /* No write error.      */
  1219.                 s = ffclose();
  1220.                 if (s == FIOSUC && msgf) {      /* No close error.      */
  1221.             if (!global_b_val(MDTERSE))
  1222.                 mlwrite("[Wrote %d line%s %ld char%s to %s]", 
  1223.                     nline, (nline>1)?"s":"",
  1224.                     nchar, (nchar>1)?"s":"", fn);
  1225.             else
  1226.                 mlforce("[%d lines]", nline);
  1227.                 }
  1228.         } else {                                /* Ignore close error   */
  1229.                 ffclose();                      /* if a write error.    */
  1230.     }
  1231.     if (bpp)
  1232.         (*bpp)->b_linecount = nline;
  1233. #if UNIX & ! NeWS
  1234.     if (fileispipe == TRUE) {
  1235.         ttunclean();
  1236.             TTflush();
  1237.         pressreturn();
  1238.         sgarbf = TRUE;
  1239.     }
  1240. #else
  1241.     TTkopen();
  1242. #endif
  1243.         if (s != FIOSUC)                        /* Some sort of error.  */
  1244.                 return FALSE;
  1245.         return TRUE;
  1246. }
  1247.  
  1248. /*
  1249.  * This function writes the kill register to a file
  1250.  * Uses the file management routines in the
  1251.  * "fileio.c" package. The number of lines written is
  1252.  * displayed.
  1253.  */
  1254. int
  1255. kwrite(fn,msgf)
  1256. char    *fn;
  1257. int    msgf;
  1258. {
  1259.     register KILL *kp;        /* pointer into kill register */
  1260.     register int    nline;
  1261.     register int    s;
  1262.     register int    c;
  1263.     register int    i;
  1264.     register char    *sp;    /* pointer into string to insert */
  1265.  
  1266.     /* make sure there is something to put */
  1267.     if (kbs[ukb].kbufh == NULL) {
  1268.         if (msgf) mlforce("Nothing to write");
  1269.         return FALSE;        /* not an error, just nothing */
  1270.     }
  1271.  
  1272. #if    CRYPT
  1273.     s = resetkey(curbp);
  1274.     if (s != TRUE)
  1275.         return s;
  1276. #endif
  1277.     /* turn off ALL keyboard translation in case we get a dos error */
  1278.     TTkclose();
  1279.  
  1280.     if ((s=ffwopen(fn)) != FIOSUC) {    /* Open writes message. */
  1281.         TTkopen();
  1282.         return FALSE;
  1283.     }
  1284.     /* tell us we're writing */
  1285.     if (msgf == TRUE)
  1286.         mlwrite("[Writing...]");
  1287.     nline = 0;                /* Number of lines.    */
  1288.  
  1289.     kp = kbs[ukb].kbufh;
  1290.     while (kp != NULL) {
  1291.         if (kp->d_next == NULL)
  1292.             i = kbs[ukb].kused;
  1293.         else
  1294.             i = KBLOCK;
  1295.         sp = (char *)kp->d_chunk;
  1296.         while (i--) {
  1297.             if ((c = *sp++) == '\n')
  1298.                 nline++;
  1299.             if ((s = ffputc(c)) != FIOSUC)
  1300.                 break;
  1301.         }
  1302.         kp = kp->d_next;
  1303.     }
  1304.     if (s == FIOSUC) {            /* No write error.    */
  1305.         s = ffclose();
  1306.         if (s == FIOSUC && msgf) {    /* No close error.    */
  1307.             if (!global_b_val(MDTERSE))
  1308.                 mlwrite("[Wrote %d line%s to %s ]",
  1309.                     nline,nline!=1?"s":"", fn);
  1310.             else
  1311.                 mlforce("[%d lines]", nline);
  1312.         }
  1313.     } else    {                /* Ignore close error    */
  1314.         ffclose();            /* if a write error.    */
  1315.     }
  1316.     TTkopen();
  1317.     if (s != FIOSUC)            /* Some sort of error.    */
  1318.         return FALSE;
  1319.     return TRUE;
  1320. }
  1321.  
  1322.  
  1323. /*
  1324.  * The command allows the user
  1325.  * to modify the file name associated with
  1326.  * the current buffer. It is like the "f" command
  1327.  * in UNIX "ed". The operation is simple; just zap
  1328.  * the name in the BUFFER structure, and mark the windows
  1329.  * as needing an update. You can type a blank line at the
  1330.  * prompt if you wish.
  1331.  */
  1332. /* ARGSUSED */
  1333. int
  1334. filename(f, n)
  1335. int f,n;
  1336. {
  1337.         register int    s;
  1338.         static char            fname[NFILEN];
  1339.  
  1340.     if (isnamedcmd && lastkey == '\r') {
  1341.         return showcpos(FALSE,1);
  1342.     }
  1343.         if ((s=mlreply("Name: ", fname, NFILEN)) == ABORT)
  1344.                 return s;
  1345.     if ((s = glob(fname)) != TRUE)
  1346.         return FALSE;
  1347.         if (s == FALSE)
  1348.                 ch_fname(curbp, "");
  1349.         else
  1350.                 ch_fname(curbp, fname);
  1351.     make_global_b_val(curbp,MDVIEW); /* no longer read only mode */
  1352.     markWFMODE(curbp);
  1353.         return TRUE;
  1354. }
  1355.  
  1356. /*
  1357.  * Insert file "fname" into the current
  1358.  * buffer, Called by insert file command. Return the final
  1359.  * status of the read.
  1360.  */
  1361. int
  1362. ifile(fname,belowthisline,haveffp)
  1363. char    *fname;
  1364. int    belowthisline;
  1365. FILE    *haveffp;
  1366. {
  1367.         register LINE   *lp0;
  1368.         register LINE   *lp1;
  1369.         register LINE   *lp2;
  1370.         register BUFFER *bp;
  1371.         register int    s;
  1372.         int    nbytes;
  1373.         register int    nline;
  1374.     extern FILE    *ffp;
  1375.  
  1376.         bp = curbp;                             /* Cheap.               */
  1377.         bp->b_flag |= BFCHG;            /* we have changed    */
  1378.     bp->b_flag &= ~BFINVS;            /* and are not temporary*/
  1379.     if (!haveffp) {
  1380.             if ((s=ffropen(fname)) == FIOERR)       /* Hard file open.      */
  1381.                     goto out;
  1382.             if (s == FIOFNF) {                      /* File not found.      */
  1383.                     mlforce("[No such file \"%s\" ]", fname);
  1384.             return FALSE;
  1385.             }
  1386.             mlwrite("[Inserting...]");
  1387. #if UNIX
  1388.         if (fileispipe)
  1389.             ttclean(TRUE);
  1390. #endif
  1391.  
  1392. #if    CRYPT
  1393.         s = resetkey(curbp);
  1394.         if (s != TRUE)
  1395.             return s;
  1396. #endif
  1397.     } else { /* we already have the file pointer */
  1398.         ffp = haveffp;
  1399.     }
  1400.     lp0 = curwp->w_dot.l;
  1401.     curwp->w_dot.o = 0;
  1402.     MK = DOT;
  1403.  
  1404.     nline = 0;
  1405.     while ((s=ffgetline(&nbytes)) == FIOSUC) {
  1406.         if ((lp1=lalloc(nbytes,curbp)) == NULL) {
  1407.             s = FIOMEM;        /* Keep message on the    */
  1408.             break;            /* display.        */
  1409.         }
  1410.         if (belowthisline) {
  1411.             lp2 = lp0->l_fp;    /* line after insert */
  1412.         } else {
  1413.             lp2 = lp0;
  1414.             lp0 = lp0->l_bp;
  1415.         }
  1416.  
  1417.         /* re-link new line between lp0 and lp2 */
  1418.         lp2->l_bp = lp1;
  1419.         lp0->l_fp = lp1;
  1420.         lp1->l_bp = lp0;
  1421.         lp1->l_fp = lp2;
  1422.  
  1423.         if (nbytes)  /* l_text may be NULL in this case */
  1424.             memcpy(lp1->l_text, fline, nbytes);
  1425.         tag_for_undo(lp1);
  1426.         if (belowthisline)
  1427.             lp0 = lp1;
  1428.         else
  1429.             lp0 = lp2;
  1430.         ++nline;
  1431.     }
  1432.     if (!haveffp) {
  1433. #if UNIX
  1434.         if (fileispipe == TRUE) {
  1435.             ttunclean();
  1436.             TTflush();
  1437.             sgarbf = TRUE;
  1438.         }
  1439. #endif
  1440.         ffclose();                /* Ignore errors.    */
  1441.         readlinesmsg(nline,s,fname,FALSE);
  1442.     }
  1443. out:
  1444.     /* advance to the next line and mark the window for changes */
  1445.     curwp->w_dot.l = lforw(curwp->w_dot.l);
  1446.     curwp->w_flag |= WFHARD | WFMODE;
  1447.  
  1448.     /* copy window parameters back to the buffer structure */
  1449.     curbp->b_wtraits = curwp->w_traits;
  1450.  
  1451.         if (s == FIOERR)                        /* False if error.      */
  1452.                 return FALSE;
  1453.         return TRUE;
  1454. }
  1455.  
  1456. /*
  1457.  * Insert file "fname" into the kill register
  1458.  * Called by insert file command. Return the final
  1459.  * status of the read.
  1460.  */
  1461. int
  1462. kifile(fname)
  1463. char    *fname;
  1464. {
  1465.         register int    i;
  1466.         register int    s;
  1467.         register int    nline;
  1468.         int    nbytes;
  1469.  
  1470.     ksetup();
  1471.         if ((s=ffropen(fname)) == FIOERR)       /* Hard file open.      */
  1472.                 goto out;
  1473.         if (s == FIOFNF) {                      /* File not found.      */
  1474.                 mlforce("[No such file \"%s\"]", fname);
  1475.         return FALSE;
  1476.         }
  1477.         mlwrite("[Reading...]");
  1478.  
  1479. #if UNIX
  1480.     if (fileispipe)
  1481.         ttclean(TRUE);
  1482. #endif
  1483.  
  1484.         nline = 0;
  1485. #if    CRYPT
  1486.     s = resetkey(curbp);
  1487.     if (s == TRUE)
  1488. #endif
  1489.         while ((s=ffgetline(&nbytes)) == FIOSUC) {
  1490.             for (i=0; i<nbytes; ++i)
  1491.                 kinsert(fline[i]);
  1492.             kinsert('\n');
  1493.             ++nline;
  1494.         }
  1495. #if UNIX
  1496.     if (fileispipe == TRUE) {
  1497.         ttunclean();
  1498.             TTflush();
  1499.             sgarbf = TRUE;
  1500.     }
  1501. #endif
  1502.     kdone();
  1503.         ffclose();                              /* Ignore errors.       */
  1504.     readlinesmsg(nline,s,fname,FALSE);
  1505.  
  1506. out:
  1507.         if (s == FIOERR)                        /* False if error.      */
  1508.                 return FALSE;
  1509.         return TRUE;
  1510. }
  1511.  
  1512. #if UNIX
  1513.  
  1514. /* called on hangups, interrupts, and quits */
  1515. /* This code is definitely not production quality, or probably very
  1516.     robust, or probably very secure.  I whipped it up to save
  1517.     myself while debugging...        pgf */
  1518. /* on the other hand, it has worked for well over two years now :-) */
  1519. SIGT
  1520. imdying(signo)
  1521. int signo;
  1522. {
  1523. #if HAVE_MKDIR
  1524.     static char dirnam[NSTRING] = "/tmp/vileDXXXXXX";
  1525. #else
  1526.     static char dirnam[NSTRING] = "/tmp";
  1527. #endif
  1528.     char filnam[50];
  1529.     char cmd[80];
  1530.     BUFFER *bp;
  1531.     char *np;
  1532.     int wrote = 0;
  1533.     int created = 0;
  1534.     char *getenv();
  1535.     char *mktemp();
  1536.  
  1537.  
  1538.     bp = bheadp;
  1539.     while (bp != NULL) {
  1540.         if (((bp->b_flag & BFINVS) == 0) && 
  1541.              bp->b_active == TRUE && 
  1542.                      (bp->b_flag&BFCHG) != 0) {
  1543. #if HAVE_MKDIR
  1544.             if (!created) {
  1545.                 (void)mktemp(dirnam);
  1546.                 if(mkdir(dirnam,0700) != 0) {
  1547.                     vttidy(FALSE);
  1548.                     exit(1);
  1549.                 }
  1550.                 created = 1;
  1551.             }
  1552. #endif
  1553.             strcpy(filnam,dirnam);
  1554.             strcat(filnam,"/");
  1555. #if ! HAVE_MKDIR
  1556.             strcat(filnam,"V");
  1557. #endif
  1558.             strcat(filnam,bp->b_bname);
  1559.             if (writeout(filnam,bp,FALSE) != TRUE) {
  1560.                 vttidy(FALSE);
  1561.                 exit(1);
  1562.             }
  1563.             wrote++;
  1564.         }
  1565.         bp = bp->b_bufp;
  1566.     }
  1567.     if (wrote) {
  1568.         if ((np = getenv("LOGNAME")) || (np = getenv("USER"))) {
  1569.             lsprintf(cmd,
  1570. #if HAVE_MKDIR
  1571.     "(echo Subject: vile died; echo Files saved: ; ls %s/* ) | /bin/mail %s",
  1572. #else
  1573.     "(echo Subject: vile died; echo Files saved: ; ls %s/V* ) | /bin/mail %s",
  1574. #endif
  1575.                 dirnam, np);
  1576.             system(cmd);
  1577.         }
  1578.     }
  1579.     vttidy(FALSE);
  1580.     if (signo > 2)
  1581.         abort();
  1582.     else
  1583.         exit(wrote);
  1584.  
  1585.     /* NOTREACHED */
  1586.     SIGRET;
  1587. }
  1588. #endif
  1589.  
  1590. void
  1591. markWFMODE(bp)
  1592. BUFFER *bp;
  1593. {
  1594.     register WINDOW *wp;    /* scan for windows that need updating */
  1595.         wp = wheadp;                    /* Update mode lines.   */
  1596.         while (wp != NULL) {
  1597.                 if (wp->w_bufp == bp)
  1598.                         wp->w_flag |= WFMODE;
  1599.                 wp = wp->w_wndp;
  1600.         }
  1601. }
  1602.  
  1603. /* use the shell to expand wildcards */
  1604. int
  1605. glob(buf)
  1606. char *buf;
  1607. {
  1608. #if UNIX
  1609.     char *cp;
  1610.     char cmd[NFILEN];
  1611.     FILE *cf;
  1612.     FILE *npopen();
  1613.  
  1614.     /* trim trailing whitespace */
  1615.     cp = &buf[strlen(buf)-1];
  1616.     while (cp != buf) {
  1617.         if (isspace(*cp))
  1618.             *cp = '\0';
  1619.         else
  1620.             break;
  1621.         cp--;
  1622.     }
  1623.  
  1624.     cp = buf;
  1625.     if (*cp == '!' || *cp == '[')    /* it's a shell command, or an */
  1626.         return TRUE;        /* internal name, don't bother */
  1627.  
  1628.     while (*cp) {
  1629.         if (iswild(*cp)) {
  1630.             lsprintf(cmd, "echo %s", buf);
  1631.             cf = npopen(cmd,"r");
  1632.             if (cf == NULL) {
  1633.                 return TRUE;
  1634.             }
  1635.             if (fread(buf,1,NFILEN,cf) <= 0) {
  1636.                 npclose(cf);
  1637.                 return FALSE;
  1638.             }
  1639.             npclose(cf);
  1640.             cp = buf;
  1641.             while (*cp) {
  1642.                 if (*cp == ' ' ) {
  1643.                     if (mlyesno(
  1644.                     "Too many filenames.  Use first"
  1645.                             ) == TRUE) {
  1646.                         *cp = '\0';
  1647.                         break;
  1648.                     } else {
  1649.                         buf[0] = 0;
  1650.                         return FALSE;
  1651.                     }
  1652.                 } else if (*cp == '\n') {
  1653.                     *cp = '\0';
  1654.                     break;
  1655.                 }
  1656.                 cp++;
  1657.             }
  1658.             return TRUE;
  1659.  
  1660.         }
  1661.         cp++;
  1662.     }
  1663. #endif
  1664. #if MSDOS
  1665.     /* this is a just a convenient place to put this little hack */
  1666.     if (islower(buf[0]) && buf[1] == ':')
  1667.         buf[0] = toupper(buf[0]);
  1668. #endif
  1669.     return TRUE;
  1670. }
  1671.  
  1672. #if    CRYPT
  1673. resetkey(bp)    /* reset the encryption key if needed */
  1674. BUFFER *bp;
  1675. {
  1676.     register int s;    /* return status */
  1677.  
  1678.     /* turn off the encryption flag */
  1679.     cryptflag = FALSE;
  1680.  
  1681.     /* if we are in crypt mode */
  1682.     if (b_val(bp, MDCRYPT)) {
  1683.         if (bp->b_key[0] == 0) {
  1684.             s = setkey(FALSE, 0);
  1685.             if (s != TRUE)
  1686.                 return s;
  1687.         }
  1688.  
  1689.         /* let others know... */
  1690.         cryptflag = TRUE;
  1691.  
  1692.         /* and set up the key to be used! */
  1693.         /* de-encrypt it */
  1694.         crypt((char *)NULL, 0);
  1695.         crypt(bp->b_key, strlen(bp->b_key));
  1696.  
  1697.         /* re-encrypt it...seeding it to start */
  1698.         crypt((char *)NULL, 0);
  1699.         crypt(bp->b_key, strlen(bp->b_key));
  1700.     }
  1701.  
  1702.     return TRUE;
  1703. }
  1704. #endif
  1705.  
  1706.  
  1707. /* canonicalize a pathname, to eliminate extraneous /./, /../, and ////
  1708.     sequences.  only guaranteed to work for absolute pathnames */
  1709. char *
  1710. canonpath(ss)
  1711. char *ss;
  1712. {
  1713.     char *p, *pp;
  1714.     char *s;
  1715.     
  1716.     s = ss;
  1717.  
  1718.     if (!*s)
  1719.         return s;
  1720.  
  1721. #if MSDOS
  1722.     /* pretend the drive designator isn't there */
  1723.     if (isalpha(*s) && *(s+1) == ':')
  1724.         s += 2;
  1725. #endif
  1726.  
  1727.     p = pp = s;
  1728.     if (!slashc(*s)) {
  1729.         mlforce("BUG: canonpath called with relative path");
  1730.         return ss;
  1731.     }
  1732. #if APOLLO
  1733.     if (slashc(p[0]) && slashc(p[1])) /* ...true for all absolute paths */
  1734.         p++;
  1735. #endif
  1736.  
  1737.     p++; pp++;    /* leave the leading slash */
  1738.     while (*pp) {
  1739.         switch (*pp) {
  1740.         case '/':
  1741. #if MSDOS
  1742.         case '\\':
  1743. #endif
  1744.             pp++;
  1745.             continue;
  1746.         case '.':
  1747.             if (slashc(*(pp+1))) {
  1748.                 pp += 2;
  1749.                 continue;
  1750.             }
  1751.         default:
  1752.             break;
  1753.         }
  1754.         break;
  1755.     }
  1756.     while (*pp) {
  1757. #if DEBUG
  1758.         if (pp != p)
  1759.             *p = '\0';
  1760.         printf(" s is %s\n",s);
  1761.         printf("pp is %*s%s\n",pp-s,"",pp);
  1762. #endif
  1763.         if (slashc(*pp)) {
  1764.             while (slashc(*(pp+1)))
  1765.                 pp++;
  1766.             if (p > s && !slashc(*(p-1)))
  1767.                 *p++ = slash;
  1768.             if (*(pp+1) == '.') {
  1769.                 if (*(pp+2) == '\0') {
  1770.                     /* change "/." at end to "" */
  1771.                     *(p-1) = '\0';    /* and we're done */
  1772.                     break;
  1773.                 }
  1774.                 if (slashc(*(pp+2))) {
  1775.                     pp += 2;
  1776.                     continue;
  1777.                 } else if (*(pp+2) == '.' && (slashc(*(pp+3))
  1778.                             || *(pp+3) == '\0')) {
  1779.                     while (p-1 > s && slashc(*(p-1)))
  1780.                         p--;
  1781.                     while (p > s && !slashc(*(p-1)))
  1782.                         p--;
  1783.                     if (p == s)
  1784.                         *p++ = slash;
  1785.                     pp += 3;
  1786.                     continue;
  1787.                 }
  1788.             }
  1789.             pp++;
  1790.             continue;
  1791.         } else {
  1792.             *p++ = *pp++;
  1793.         }
  1794.     }
  1795.     if (p > s && slashc(*(p-1)))
  1796.         p--;
  1797.     if (p == s)
  1798.         *p++ = slash;
  1799.     *p = 0;
  1800.     return ss;
  1801. }
  1802.  
  1803. char *
  1804. shorten_path(f)
  1805. char *f;
  1806. {
  1807.     char *cwd;
  1808.     char *ff;
  1809.     char *slp;
  1810.  
  1811.     if (!f || *f == '\0')
  1812.         return NULL;
  1813.  
  1814.     if (*f == '!' || *f == '[')
  1815.         return f;
  1816.  
  1817.     cwd = current_directory(FALSE);
  1818.     slp = ff = f;
  1819.     while (*cwd && *ff && *cwd == *ff) {
  1820.         if (*ff == slash)
  1821.             slp = ff;
  1822.         cwd++;
  1823.         ff++;
  1824.     }
  1825.  
  1826.     /* if we reached the end of cwd, and we're at a path boundary,
  1827.         then the file must be under '.' */
  1828.     if (*cwd == '\0') {
  1829.         if (*ff == slash)
  1830.             return ff+1;
  1831.         if (slp == ff - 1)
  1832.             return ff;
  1833.     }
  1834.     
  1835.     /* if we mismatched during the first path component, we're done */
  1836.     if (slp == f)
  1837.         return f;
  1838.  
  1839.     /* if we mismatched in the last component of cwd, then the file
  1840.         is under '..' */
  1841.     if (strchr(cwd,slash) == NULL) {
  1842.         static char path[NFILEN];
  1843.         strcpy(path,"..");
  1844.         strcat(path,slp);
  1845.         return path;
  1846.     }
  1847.  
  1848.     /* we're off by more than just '..', so use absolute path */
  1849.     return f;
  1850. }
  1851.