home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / x / volume17 / tcl-editor / part08 / fileio.c next >
Encoding:
C/C++ Source or Header  |  1992-03-18  |  18.5 KB  |  831 lines

  1. /* $Header: /nfs/unmvax/faculty/crowley/x/pt/RCS/fileio.c,v 1.9 1992/03/04 17:07:18 crowley Exp crowley $ */
  2.  
  3. #include <ctype.h>
  4. #include <string.h>
  5. #include <stdio.h>
  6. #include <sys/types.h>
  7. #include <sys/stat.h>
  8. #include <sys/file.h>
  9. #ifdef uts
  10. #include <fcntl.h>
  11. #endif /* uts */
  12. #include "pt.h"
  13.  
  14. /* the open files */
  15. struct openFile *files;
  16.  
  17. extern struct diskBuffer *buffers;
  18. extern struct diskBuffer *bufHash[];
  19. extern nextBuffer;
  20.  
  21. void
  22. initFileio()
  23. {
  24.     extern char msgBuffer[];
  25.     extern Piece freePList;
  26.     extern struct piece pieceTable[];
  27.     extern char *bufferSpace;
  28.     extern int nBuffers;
  29.     extern unsigned int piecesLeft;
  30.     extern unsigned int bytesLeft;
  31.     extern int maxFiles;
  32.     extern int debug;
  33.     
  34.     register int i;
  35.     unsigned int size;
  36.     unsigned char *bs;
  37.  
  38.     for(i = 0; i < maxFiles; i++) {
  39.         files[i].origHandle = -1;
  40.     }
  41.  
  42.     /* set up the buffer hash scheme */
  43.     for(i = 0; i < NBUFHASH; i++)
  44.         bufHash[i] = NULL;
  45.     nextBuffer = -1;
  46.  
  47.     /* allocate the space (out of this address space) for the buffers */
  48. retryAllocation:
  49.     size = BUFFERSIZE * nBuffers;
  50.     bs = (unsigned char *)PtMalloc(size, "IO buffer");
  51.     if( bs == NULL ) {
  52.         /* if there is not enough space, try fewer buffers */
  53.         if( nBuffers >= 25 ) {
  54.             nBuffers -= 20;
  55.             goto retryAllocation;
  56.         }
  57.         size -= 16*i;
  58.         printf(
  59.             "NOT ENOUGH MEMORY! (%u bytes short) Use fewer buffers\n",
  60.             size);
  61.         exit(1);
  62.     }
  63.  
  64.     /* set up each buffer header to the address of the */
  65.     /* allocated buffer. Buffer i is at buffers[i].bufferAddress  */
  66.     for(i = 0; i < nBuffers; i++) {
  67.         buffers[i].handle = -1;
  68.         buffers[i].bufferAddress = bs;
  69.         bs += BUFFERSIZE;
  70.     }
  71.     
  72.     /* set up the free piece list */
  73.     freePList = NULL;
  74.     piecesLeft = 0;
  75. }
  76.  
  77. static void
  78. InitCommandHistory( ff )
  79.     struct openFile *ff;
  80. {
  81.     /* initialize the command history */
  82.     ff->cmdHistory = (struct changeItem *)
  83.             PtMalloc( sizeof(struct changeItem), "change item" );
  84.     ff->cmdHistory->type = CNULL;
  85.  
  86.     /* be SURE that this change is not used */
  87.     ff->cmdHistory->flags = CHANGE_WAS_UNDONE
  88.                 | FILE_WAS_CLOSED
  89.                 | BLOCK_UNDO_BEGIN;
  90.  
  91.     /* make this the only item in the command history list */
  92.     ff->cmdHistory->prev = NULL;
  93.     ff->cmdHistory->next = NULL;
  94. }
  95.  
  96. int
  97. getFileId(origName)
  98.     char *origName;
  99. {
  100.     extern char msgBuffer[];
  101.     extern char scratchFileName[];
  102.     extern int addHandle;
  103.     extern int readOnly;
  104.     extern int maxFiles;
  105.     extern int trace_file;
  106.  
  107.     int fileNumber, origHandle;
  108.     int freeFileId;
  109.     Offset size;
  110.     Piece pp;
  111.     struct openFile *ff;
  112.     char *fullFilename;
  113.  
  114.     if( origName == NULL ) {
  115.         scratchFileName[0] = '\0';
  116.         fullFilename = &scratchFileName[0];
  117.     } else
  118.         fullFilename = makeFullPathname(origName);
  119.  
  120.     /* find a free file structure */
  121.     freeFileId = -1;
  122.     for(fileNumber = 0; fileNumber < maxFiles; fileNumber++) {
  123.         if( files[fileNumber].origHandle == -1 ) {
  124.             if( freeFileId == -1 )
  125.                 freeFileId = fileNumber;
  126.         } else if(
  127.             strcmp(files[fileNumber].origName, fullFilename)
  128.                                 == 0 ) {
  129.             ++(files[fileNumber].useCount);
  130.             return fileNumber;
  131.         }
  132.     }
  133.     if( freeFileId == -1 ) {
  134.         msg("openFile: out of file structures", 3);
  135.         return -1;
  136.     } else
  137.         fileNumber = freeFileId;
  138.  
  139.     /* open the original file */
  140.     ff = &files[fileNumber];
  141.     strncpy(ff->origName, fullFilename, FILENAMESIZE);
  142.     
  143.     if( fullFilename[0] == '\0' ) {
  144.         origHandle = addHandle;
  145.     } else {
  146.         origHandle = open(fullFilename, O_RDONLY, 0);
  147.  
  148.         if( origHandle < 0 ) {
  149.             sprintf(msgBuffer, "Cannot open %s",
  150.                 fullFilename);
  151.             msg(msgBuffer, 3);
  152.             return -1;
  153.         }
  154.     }
  155.     ff->origHandle = origHandle;
  156.     if( trace_file > 0 ) {
  157.         sprintf( msgBuffer, "# file id of %2d is file `%s'\n",
  158.             fileNumber, fullFilename );
  159.         write( trace_file, msgBuffer, strlen(msgBuffer) );
  160.     }
  161.  
  162.     /* initialize the other fields */
  163.     if( origHandle != addHandle ) {
  164.         size = lseek(origHandle, 0L, 2);
  165.         lseek(origHandle, 0L, 0);
  166.     } else
  167.         size = 0;
  168.     ff->fileSize = size;
  169.     ff->origFileSize = size;
  170.     ff->isView = 0;
  171.     ff->useCount = 1;
  172.     ff->flags = 0;
  173.     if( readOnly )
  174.         ff->flags |= READ_ONLY;
  175.     else if( origHandle == addHandle ) {
  176.         ff->flags &= ~READ_ONLY;
  177.     } else {
  178.         /* check for read and write permissions (6 => RW) */
  179.         /* edit in readOnly mode if the UNIX file permissions */
  180.         /* do not allow writing the file */
  181.         if( access(ff->origName, R_OK | W_OK) == 0)
  182.             ff->flags &= ~READ_ONLY;
  183.         else
  184.             ff->flags |= READ_ONLY;
  185.     }
  186.  
  187.     /* initialize the piece table */
  188.     pp = getFreePiece();
  189.     pp->file = origHandle;
  190.     pp->position = 0;
  191.     pp->length = size;
  192.     ff->pieceList = pp;
  193.  
  194.     InitCommandHistory( ff );
  195.  
  196.     /* initialize the optimization fields */
  197.     ff->loLogPiece = 0;
  198.     ff->hiLogPiece = size - 1;
  199.     ff->logPiece = pp;
  200.     ff->hiLogBuffer = -1;
  201.     ff->loLogBuffer = -1;
  202.     ff->logBuf = NULL;
  203.  
  204.     /*return the fileId */
  205.     return fileNumber;
  206. }
  207.  
  208. static void
  209. MakeBackups(ff, tempFile )
  210.     struct openFile *ff;
  211.     char *tempFile;
  212. {
  213.     extern int backupDepth;
  214.     extern char *backupNameFormat;
  215.     extern int backupByCopy;
  216.     extern char msgBuffer[];
  217.     extern char textBuffer[];
  218.  
  219.     int i, version;
  220.     char *end, *f, *p, *q;
  221.     char ch;
  222.  
  223. if( !(ff->flags&BAK_MADE) && backupDepth > 0 ) {
  224.     p = textBuffer;        /* create backup file name here */
  225.     f = backupNameFormat;    /* printf-like name format */
  226.     while( 1 ) {
  227.         ch = *f++;
  228.         if( ch == '\0' )
  229.             break;
  230.         if( ch != '%' ) {
  231.             *p++ = ch;
  232.             continue;
  233.         }
  234.         ch = *f++;
  235.         switch( ch ) {
  236.  
  237.         case 'n':    /* original file name */
  238.             q = ff->origName;
  239.             while( 1 ) {
  240.                 ch = *q++;
  241.                 if( ch == '\0' )
  242.                     break;
  243.                 *p++ = ch;
  244.             }
  245.             break;
  246.  
  247.         case 'b':    /* base name of original file */
  248.             q = ff->origName;
  249.             end = q + strlen(q) - 1;
  250.             while( end > q && *end != '.' )
  251.                 --end;
  252.             if( end == q )
  253.                 end = q + strlen(q);
  254.             while( q < end )
  255.                 *p++ = *q++;
  256.             break;
  257.  
  258.         case 'v':    /* version number -- must be 1 to 9 */
  259.             version = p - textBuffer;
  260.             *p++ = '0';    /* placeholder */
  261.             break;
  262.         default:
  263.             *p++ = ch;
  264.             break;
  265.         }
  266.     }
  267.     *p = '\0';
  268.     /* if file p does not exist, this will return an error code but */
  269.     /* is is cheaper just to do it than to ask if it exists first */
  270.     /* delete the last backup */
  271.     (void)unlink( textBuffer );
  272.     strcpy( msgBuffer, textBuffer );
  273.     /* move all the other existing backups down one slot */
  274.     for(i = backupDepth - 1; i > 0; --i ) {
  275.         textBuffer[version] = i + '0';
  276.         msgBuffer[version] = i + 1 + '0';
  277.         (void)rename( textBuffer, msgBuffer );
  278.     }
  279. #ifdef LATERLATER
  280.     if( backupByCopy ) {
  281.         sprintf( msgBuffer, "cp %s %s", ff->origName, textBuffer );
  282.         (void)system(msgBuffer);
  283.         /* OR
  284.         Tcl_VarExec( mainBrowser->interp, "copy ", ff->origName,
  285.                             textBuffer, NULL );
  286.         */
  287.     } else
  288. #endif
  289.         (void)rename(ff->origName, textBuffer);
  290.     ff->flags |= BAK_MADE;
  291. } else {
  292.     if( unlink(ff->origName) ) {
  293.         strcpy( textBuffer, ff->origName );
  294.         p = tildefyFilename( textBuffer );
  295.         sprintf(msgBuffer,
  296.             "Delete of old version of %s failed; new version in %s",
  297.             p, tempFile );
  298.         msg(msgBuffer, 3);
  299.     }
  300. }
  301. if( rename(tempFile, ff->origName) ) {
  302.     strcpy( textBuffer, ff->origName );
  303.     p = tildefyFilename( textBuffer );
  304.     sprintf(msgBuffer,
  305.         "Rename of %s [new version] to %s [old version] failed",
  306.         tempFile,p, textBuffer, tempFile );
  307.     msg(msgBuffer, 3);
  308. }
  309. }
  310.  
  311. static void
  312. FreeCommandHistory( ff )
  313.     struct openFile *ff;
  314. {
  315.     struct changeItem * change, * change_to_free;
  316.  
  317.     /* first find the end of the list */
  318.     change = ff->cmdHistory;
  319.     if( change == NULL )
  320.         /* this should not happen */
  321.         return;
  322.     while( change->next != NULL )
  323.         change = change->next;
  324.  
  325.     /* now walk down the chain and free each one */
  326.     while( change != NULL ) {
  327.         change_to_free = change;
  328.         change = change->prev;
  329.         PtFree( (char *)change_to_free );
  330.     }
  331.     /* just to be neat */
  332.     ff->cmdHistory = NULL;
  333. }
  334.  
  335. void
  336. saveFile(w)
  337.     struct window *w;
  338. {
  339.     extern char msgBuffer[];
  340.     extern char textBuffer[];
  341.     extern struct openFile *files;
  342.     extern int addHandle;
  343.     extern int hypertextOn;
  344.  
  345.     struct openFile *ff;
  346.     char *p, *tempFile;
  347.     int i;
  348.     int fid;
  349.     Offset size;
  350.     Piece pp;
  351.     struct stat statbuf;
  352.  
  353. #ifdef HYPERTEXT
  354.     if( hypertextOn && w->document != NULL )
  355.         fid = w->realFileId;
  356.     else
  357. #endif
  358.         fid = w->fileId;
  359.     ff = &files[fid];
  360.     
  361.     /* Do not save if the file is read only */
  362.     if( ff->flags & READ_ONLY ) {
  363.         strcpy( textBuffer, ff->origName );
  364.         p = tildefyFilename( textBuffer );
  365.         sprintf( msgBuffer, "File %s is read only", p );
  366.         msg(msgBuffer, 3);
  367.         return;
  368.     }
  369.  
  370.     /* get a temporary name and write the new version of the file to it */
  371.     tempFile = makeTempFor(ff->origName);
  372.     strcpy( textBuffer, ff->origName );
  373.     p = tildefyFilename( textBuffer );
  374.     sprintf(msgBuffer, "Writing file %s [%ld bytes]...", p, ff->fileSize);
  375.     msg(msgBuffer, 1);
  376.  
  377.     if( !doWrite( fid, tempFile ) )
  378.         return;
  379.  
  380.     /* change the permissions on the new file to match the old one */
  381.     i = stat( ff->origName, &statbuf );
  382.     if( i != 0 ) {
  383.         printf("Stat of %s failed\n", ff->origName);
  384.     } else {
  385.         chown( tempFile, statbuf.st_uid, statbuf.st_gid );
  386.         chmod( tempFile, statbuf.st_mode );
  387.     }
  388.  
  389.     /* write out the message telling the user the file was written */
  390.     strcpy( textBuffer, ff->origName );
  391.     p = tildefyFilename( textBuffer );
  392.     sprintf(msgBuffer, "%s written...%ld bytes", p, ff->fileSize);
  393.     msg(msgBuffer, 1);
  394.  
  395.     /* invalidate any buffers allocated to this open file */
  396.     fidInvalid(ff->origHandle, fid);
  397.     ff->hiLogBuffer = -1;
  398.     ff->loLogBuffer = -1;
  399.  
  400.     /* reset the change flag */
  401.     ff->flags &= ~IS_CHANGED;
  402.     NewOpenList();
  403.  
  404.     /* close the original file for this open file */
  405.     if( ff->origHandle != addHandle ) {
  406.         i = close(ff->origHandle);
  407.         if( i != 0 ) {
  408.             strcpy( textBuffer, ff->origName );
  409.             tildefyFilename( textBuffer );
  410.             p = (char *)sprintf(msgBuffer,
  411.                     "saveFile: close of %s failed, ret=%d", p, i);
  412.             msg(msgBuffer, 1);
  413.         }
  414.     }
  415.  
  416.     /* for now free the command history */
  417.     /* LATERLATER: fix things up so we can keep the command history */
  418.     /* even over a save.  It is just a matter of changing the */
  419.     /* file you consider the original file */
  420.     FreeCommandHistory( ff );
  421.     InitCommandHistory( ff );
  422.  
  423.     MakeBackups( ff, tempFile );
  424.  
  425.     /* open it up again */
  426.     if( ff->origHandle != addHandle )
  427.         ff->origHandle = open(ff->origName, O_RDONLY, 0644);
  428.  
  429.     /* re-initialize the other fields */
  430.     size = lseek(ff->origHandle, 0L, 2);
  431.     ff->fileSize = size;
  432.     ff->origFileSize = size;
  433.     lseek(ff->origHandle, 0L, 0);
  434.     
  435.     /* free all the pieces */
  436.     freePieces(ff->pieceList);
  437.  
  438.     /* re-initialize the piece table */
  439.     pp = getFreePiece();
  440.     ff->pieceList = pp;
  441.     pp->file = ff->origHandle;
  442.     pp->position = 0;
  443.     pp->length = size;
  444.  
  445.     /* re-initialize the optimization fields */
  446.     ff->loLogPiece = 0;
  447.     ff->hiLogPiece = size - 1;
  448.     ff->logPiece = pp;
  449.     ff->hiLogBuffer = -1L;
  450.     ff->loLogBuffer = -1L;
  451.     ff->logBuf = NULL;
  452.  
  453.     /* redraw the banner to remove the file modified `*' */
  454.     banner( w, 0 );    /* 0 ==> don't redo the slider */
  455. }
  456.  
  457. void
  458. writeFile(w)
  459.     struct window *w;
  460. {
  461.     extern struct openFile *files;
  462.     extern char msgBuffer[];
  463.     extern char textBuffer[];
  464.     extern char * returnString;
  465.  
  466.     sprintf( msgBuffer,
  467. "MakeModalEntry {Save As} {Filename to write to} {Okay} {Cancel write}");
  468.     (void)ExecTclCommand( msgBuffer );
  469.     command( FWAITFORRETURNSTRING, "", "", "", "", "", "" );
  470.     if( strcmp(returnString,"XXXcancelXXX")==0 ) {
  471.     cancel:
  472.         msg("Write file cancelled", 1);
  473.         return;
  474.     }
  475.  
  476.     /* check if the file already exists */
  477.     if( access(returnString, F_OK) == 0 ) {
  478.         sprintf( msgBuffer,
  479.             "MakeModalYesNo {%s} {%s %s %s} {%s} {%s}",
  480.             "Write over file?",
  481.             "File", returnString, "already exists.",
  482.             "Write over it",
  483.             "Cancel write" );
  484.         (void)ExecTclCommand( textBuffer );
  485.         command( FWAITFORRETURNSTRING, "", "", "", "", "", "" );
  486.         if( returnString[0] != 'y' )
  487.             goto cancel;
  488.     }
  489.  
  490.     sprintf(textBuffer, "Writing file %s [%ld bytes]...",
  491.         msgBuffer, files[w->fileId].fileSize);
  492.     msg(textBuffer, 1);
  493.  
  494.     if( !doWrite(w->fileId, returnString) )
  495.         return;
  496.  
  497.     sprintf(textBuffer, "%s written...%ld bytes",
  498.         returnString, files[w->fileId].fileSize);
  499.     msg(textBuffer, 1);
  500. }
  501.  
  502. int
  503. doWrite(fileId, tempFile)
  504.     int fileId;
  505.     char *tempFile;
  506. {
  507.     extern char msgBuffer[];
  508.     extern int getSpanSize;
  509.  
  510.     Offset cp, fSize;
  511.     int fid, iBuffer, lineSize;
  512.     unsigned char *firstByte;
  513.     unsigned char *lastByte;
  514.     unsigned char *sBuffer;
  515.     unsigned int sizeOfBuffer;
  516.  
  517. fSize = fileSize(fileId);
  518.  
  519. /* open the output file */
  520. fid = open(tempFile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
  521. if( fid < 0 ) {
  522.     perror("Create temp file");
  523.     sprintf( msgBuffer, "Could not create %s, ret=%d", tempFile, fid );
  524.     msg(msgBuffer, 1);
  525.     return 0;
  526. }
  527.  
  528. /* allocate the IO buffer */
  529. sizeOfBuffer = 8192;
  530. while( 1 ) {
  531.     sBuffer = (unsigned char *)PtMalloc(sizeOfBuffer, "output buffer");
  532.     if( sBuffer == NULL ) {
  533.         extern char textBuffer[];
  534.         /* try again with a buffer half as large */
  535.         sizeOfBuffer >>= 1;
  536.         if( sizeOfBuffer <= MSGBUFFERSIZE ) {
  537.             sBuffer = (unsigned char *)textBuffer;
  538.             break;
  539.         }
  540.         continue;
  541.     } else
  542.         break;
  543. }
  544.  
  545. cp = 0;
  546. iBuffer = 0;
  547. while( 1 ) {
  548.     /* special case for files of length 0 */
  549.     if( fSize == 0 )
  550.         fSize = 1;
  551.     sprintf(msgBuffer, "File is %d%% written out",
  552.         (int)( (100*cp) / fSize) );
  553.     msg(msgBuffer, 0);
  554.     if( getSpan(fileId, cp, &firstByte, &lastByte, 0) )
  555.         break;
  556.     lineSize = (int)(lastByte - firstByte + 1);
  557. getSpanSize += lineSize;
  558.     cp = cp + lineSize;
  559.  
  560.     if( (iBuffer+lineSize) > sizeOfBuffer ) {/* will it fit? */
  561.         /* no, so write out the buffer to empty it */
  562.         int writeRet = write(fid, sBuffer, iBuffer);
  563.         if( writeRet < iBuffer ) {
  564. error:
  565.             perror("write to temp file");
  566.             PtFree((char *)sBuffer);
  567.             /* avoid recursive calls if msg tries to write disk */
  568. printf("doWrite: trying to write %d bytes but only wrote %d bytes\n",
  569.                 iBuffer, writeRet);
  570.             close(fid);
  571.             unlink(tempFile);
  572.             return 0;
  573.         } else
  574.             iBuffer = 0;
  575.     }
  576.     /* move lineSize bytes from firstByte to sBuffer+iBuffer */
  577.     bcopy( firstByte, sBuffer + iBuffer, lineSize );
  578.     iBuffer += lineSize;
  579. }
  580. if( write(fid, sBuffer, iBuffer) < iBuffer )
  581.     goto error;
  582. close(fid);
  583.  
  584. /* all is ok */
  585. PtFree((char *)sBuffer);
  586. return 1;
  587. }
  588.  
  589. char *
  590. makeTempFor(name)
  591.     char *name;
  592. {
  593.     static char tname[64];
  594.     register int len;
  595.     int pathEnd;
  596.     char ch;
  597.  
  598.     len = 0;
  599.     pathEnd = -1;
  600.     while( 1 ) {
  601.         if( (ch = name[len]) == '\0' )
  602.             break;
  603.         tname[len] = ch;
  604.         switch( ch ) {
  605.         case '/':
  606.             pathEnd = len;
  607.         }
  608.         ++len;
  609.     }
  610.     tname[pathEnd+1] = '\0';
  611.     strcat(tname, "tempfile.pt");
  612.     return &tname[0];
  613. }
  614.  
  615. char *
  616. noWhiteSpace(fileName)
  617.     register char *fileName;
  618. {
  619.     register int n;
  620.  
  621.     if( fileName != NULL || fileName[0] == '\0' ) {
  622.         /* eliminate white space around fileName */
  623.  
  624.         /* first white space in the beginning */
  625.         while( isspace(*fileName) )
  626.             ++fileName;
  627.  
  628.         /* now white space at the end */
  629.         n = strlen(fileName) - 1;
  630.         while( isspace(fileName[n]) )
  631.             --n;
  632.         fileName[n+1] = '\0';
  633.     }
  634.     return fileName;
  635. }
  636.  
  637. int
  638. makeName(s)
  639.     register char *s;
  640. {
  641.     register int l;
  642.     int nTries;
  643.     char ch;
  644.  
  645.     /* try to create a new name by changing the last letter */
  646.     l = strlen(s) - 1;
  647.     nTries = 0;
  648.     ch = s[l];    /* save first character */
  649.     if( isupper(ch) )
  650.         ch = tolower(ch);
  651.     while( access(s, 0) == 0 ) {
  652.         /* the file name exists so try another */
  653.         ++nTries;
  654.         if( s[l] == 'z' )
  655.             s[l] = 'a';
  656.         else
  657.             ++s[l];
  658.         if( s[l] == ch )
  659.             return -1;    /* could not find an unused name */
  660.     }
  661.     return nTries;
  662. }
  663.  
  664. int
  665. getBaseName(s)
  666.     char *s;
  667. {
  668.     int n;
  669.  
  670.     n = strlen(s) - 1;
  671.     while( 1 ) {
  672.         if( s[n] == '/' )
  673.             break;
  674.         if( --n < 0 )
  675.             break;
  676.     }
  677.     return n+1;
  678. }
  679.  
  680. Offset
  681. fileSize(fileNumber)
  682.     int fileNumber;
  683. {
  684.     return files[fileNumber].fileSize;
  685. }
  686.  
  687. int
  688. closeFile(fileId, ask)
  689.     int fileId, ask;
  690. {
  691.     extern char msgBuffer[];
  692.     extern char textBuffer[];
  693.     extern struct changeItem scrapBuffer;
  694.     extern int addHandle;
  695.     extern int trace_file;
  696.     extern char * returnString;
  697.  
  698.     int i, writing;
  699.     char *p, *tempFile;
  700.     register struct openFile *ff;
  701.  
  702.     if( fileId == -1 )
  703.         return 0;
  704.  
  705.     if( trace_file > 0 ) {
  706.         sprintf( msgBuffer, "# close file id %2d\n", fileId );
  707.         write( trace_file, msgBuffer, strlen(msgBuffer) );
  708.     }
  709.  
  710.     ff = &files[fileId];
  711.  
  712.     /* check for 'files' that are really views */
  713.     if( ff->isView )
  714.         goto ViewOnly;
  715.  
  716.     /* is this the last close? */
  717.     if( --(ff->useCount) > 0 )
  718.         return 0;
  719.  
  720.     /* see if we need to write the file */
  721.     writing = 1;
  722.     /* has the file changed? */
  723.     if( ff->pieceList->nextPiece == NULL
  724.      && ff->pieceList->file == ff->origHandle ) {
  725.         if( ff->fileSize == ff->origFileSize ) 
  726.             writing = 0;
  727.     }
  728.     if( ask == 2 )    /* quit and discard mode */
  729.         writing = 0;
  730.     /* do not write the messages file into itself */
  731.     if( ff->origHandle == addHandle )
  732.         writing = 0;
  733.     /* see if we already saved these changes */
  734.     if( !(ff->flags & IS_CHANGED) )
  735.         writing = 0;
  736.     if( writing && (ff->flags & READ_ONLY) ) {
  737.         strcpy( textBuffer, ff->origName );
  738.         p = tildefyFilename( textBuffer );
  739.         sprintf( msgBuffer,
  740.             "MakeModalYesNo {%s} {%s %s %s} {%s} {%s}",
  741.             "Read only file",
  742.             "File", p, "is read only but changed.",
  743.             "Close file without writing",
  744.             "Cancel close" );
  745.         (void)ExecTclCommand( textBuffer );
  746.         command( FWAITFORRETURNSTRING, "", "", "", "", "", "" );
  747.         if( returnString[0] != 'y' )
  748.             return -1;
  749.         writing = 0;
  750.     }
  751.     if( writing ) {
  752.         /* verify the write if ask is true */
  753.         if( ask ) {
  754.             strcpy( textBuffer, ff->origName );
  755.             p = tildefyFilename( textBuffer );
  756.         sprintf( msgBuffer,
  757.             "MakeModalYesNo {%s} {%s %s %s} {%s} {%s}",
  758.             "Save file?",
  759.             "Save file", p, "?",
  760.             "Yes, save the changes",
  761.             "No, discard changes" );
  762.         (void)ExecTclCommand( textBuffer );
  763.         command( FWAITFORRETURNSTRING, "", "", "", "", "", "" );
  764.             writing = (returnString[0] -= 'y');
  765.         }
  766.         if( writing ) {
  767.             tempFile = makeTempFor(ff->origName);
  768.             strcpy( textBuffer, ff->origName );
  769.             p = tildefyFilename( textBuffer );
  770.             sprintf(msgBuffer, "Writing file %s [%ld bytes]...",
  771.                 p, ff->fileSize);
  772.             msg(msgBuffer, 1);
  773.             if( doWrite(fileId, tempFile) == 0 )
  774.                 return -1;
  775.             strcpy( textBuffer, ff->origName );
  776.             p = tildefyFilename( textBuffer );
  777.             sprintf(msgBuffer, "%s written...%ld bytes",
  778.                 p, ff->fileSize);
  779.             msg(msgBuffer, 1);
  780.         }
  781.     }
  782.  
  783.     /* do the close */
  784.     if( ff->origHandle == -1 ) {
  785.         strcpy( textBuffer, ff->origName );
  786.         p = tildefyFilename( textBuffer );
  787.         sprintf(msgBuffer, "closeFile: file %s is not open", p);
  788.         msg(msgBuffer, 1);
  789.         return 0;
  790.     }
  791.     
  792.     /* see if the scrap points into this file */
  793.     if( ff->origHandle == scrapBuffer.fileId && scrapBuffer.type == 0 )
  794.         /* this call only copies the scrap text to the add file */
  795.         /* it does not really do any insert (just using the code) */
  796.         insScrap( 0, 0 );
  797.  
  798.     /* invalidate any buffers allocated to this open file */
  799.     if( ff->origHandle != addHandle )
  800.         fidInvalid(ff->origHandle, fileId);
  801.         
  802.     /* free the command history items */
  803.     FreeCommandHistory( ff );
  804.     
  805.     /* close the original file for this open file */
  806.     if( ff->origHandle != addHandle ) {
  807.         i = close(ff->origHandle);
  808.         if( i != 0 ) {
  809.             strcpy( textBuffer, ff->origName );
  810.             p = tildefyFilename( textBuffer );
  811.             sprintf(msgBuffer,
  812.                 "closeFile: close of %s failed, ret=%d", p, i);
  813.             msg(msgBuffer, 1);
  814.         }
  815.     }
  816.  
  817.     /* rename the saved file */
  818.     if( writing == 1) {
  819.         MakeBackups( ff, tempFile );
  820.     }
  821.  
  822. ViewOnly:
  823.     /* free all the pieces */
  824.     freePieces(ff->pieceList);
  825.  
  826.     /* indicate the file structure is free */
  827.     ff->origHandle = -1;
  828.  
  829.     return 0;    /* all ok */
  830. }
  831.