static char rcsid[] = "$Id: file.c,v 1.6 1993/08/24 14:50:02 mike Exp $"; /* $Log: file.c,v $ * Revision 1.6 1993/08/24 14:50:02 mike * - Display the `Wrote n lines' message after writing a file. * - On write failure remove the temporary output file. * * Revision 1.5 1992/11/10 21:44:14 mike * - Don't overwrite an error message from the `fileio' routines. * * Revision 1.4 1992/11/08 23:02:30 mike * - Reorganized some of the file visit stuff. * - Set read-only buffer mode on read-only files automagically * when reading a file. * * Revision 1.3 1992/10/28 15:08:50 mike * - Replaced `toggle_cursor()' by `Cursor_on()' and `Cursor_off()'. * * Revision 1.2 1992/10/02 22:31:40 mike * - Some changes for the Atari ST/TT version. * * Revision 1.1 1992/09/05 01:13:32 mike * Initial revision * */ /* * FILE.C MODULE * * The routines in this file handle the reading and writing of disk files. * All of details about the reading and writing of the disk itself are in * "fileio.c". */ /* Copyright 1990, 1991, 1992 Craig Durland * Distributed under the terms of the GNU General Public License. * Distributed "as is", without warranties of any kind, but comments, * suggestions and bug reports are welcome. */ #include #include #include "me2.h" #include "config.h" #if ATARI #include #endif char *l_to_a(), *strcat(), *strcpy(); void makename(); static int closeit(); int fr_hook; /* file-read-hook */ /* * Save the contents of the current buffer in its associatd file. Do * nothing if nothing has changed (this may be a bug, not a feature). * Error if there is no remembered file name for the buffer. * Bound to "C-x C-s". */ filesave(f,n) { int s; if (!is_buffer_modified(curbp)) return TRUE; /* Return, no changes */ if (*get_dString(curbp->b_fname) == '\0') /* Must have a name */ { mlwrite("No file name for buffer %s.",get_dString(curbp->b_bname)); return FALSE; } if ((s = writeout(get_dString(curbp->b_fname)))) set_buffer_modified(curbp,FALSE); return s; } /* * Read a file into the current buffer. This is really easy; all you do * is find the name of the file, and call the standard "read a file * into the current buffer" code. * Bound to "C-x C-r". */ fileread(f,n) int f,n; { register int s; char fname[NFILEN], *ptr; if ((s = mlreply("Read file: ",fname,NFILEN,CC_FNAME)) == ABORT) return s; if (s == FALSE) ptr = get_dString(curbp->b_fname); else { if (!arf(fname)) return FALSE; /* bogus file name syntax */ ptr = fname; } return readin(ptr); } /* Insert a file into the current buffer at the current mark. * Bound to "C-x i" */ insert_file(f,n) int f,n; { register int s; char fname[NFILEN]; if ((s = mlreply("Insert file: ",fname,NFILEN,CC_FNAME)) == ABORT) return s; if (s == FALSE) return insfile(get_dString(curbp->b_fname)); if (!arf(fname)) return FALSE; return insfile(fname); } /* * Select a file for editing. * Look around to see if you can find the file in another buffer; If you * can find it just pop it up and make it current. If you cannot find * the file, read it into a buffer, pop it up and make current. * Input: * f: if no arg, use the current window to display the buffer. * Otherwise, split the current window to create a new window for the * buffer. * Bound to "C-x C-v" and "C-x C-f" */ filevisit(f,n) { register Buffer *bp; char bname[NBUFN], zname[NBUFN], fname[NFILEN]; int s; if ((s = mlreply("Visit file: ",fname,NFILEN,CC_FNAME)) != TRUE) return s; if (!arf(fname)) return FALSE; for (bp = first_buffer; bp; bp = bp->nextb) { /* check to see if the file is already in a buffer */ if (strcmp(get_dString(bp->b_fname),fname) == 0) /* found the buffer */ { mlwrite("[Old buffer]"); if (f) return bpopup(bp,TRUE); /* popup a window for it */ use_buffer(bp,TRUE); goto_BoB(FALSE,1); /* use current window */ return TRUE; } } makename(bname,fname); /* New buffer name */ strcpy(zname,bname); n = 1; while (bfind(bname,FALSE,0)) /* make sure buffer name is unique */ { n++; strcat(strcat(strcat( strcpy(bname,zname), "<"), l_to_a((long int)n)), ">"); } if ((bp = bfind(bname,TRUE,BFINTERACTIVE)) == NULL) { mlwrite("Cannot create buffer"); return ABORT; } /* put the buffer in a window and make it current */ if (!f) { use_buffer(bp,TRUE); goto_BoB(FALSE,1); } /* use current window */ else if (!bpopup(bp,TRUE)) return ABORT; /* popup a window for it */ return readin(fname); /* Read file into the current buffer */ } /* * Ask for a file name and write the contents of the current buffer * to that file. Clear the buffer changed flag. * Bound to "C-x C-w". */ filewrite(f,n) int f,n; { char fname[NFILEN]; int s; if ((s = mlreply("Write file: ",fname,NFILEN,CC_FNAME))==ABORT) return ABORT; if (s == FALSE && *strcpy(fname,get_dString(curbp->b_fname)) == '\0') return FALSE; #if FILENAME_COMPLETION { char tmp[NFILEN]; if (fxpand(fname,TRUE,FALSE,TRUE,tmp,(pfi)NULL)) return FALSE; if ((s = writeout(tmp))) set_buffer_modified(curbp,FALSE); } #else if ((s = writeout(fname))) set_buffer_modified(curbp,FALSE); #endif return s; } #ifdef atarist int file_select(path,message,pathname,filename) char *path; char *message; char *pathname; /* Path to use in the file selector box */ char *filename; /* Filename to use in the file selector box */ { extern char *strrchr(); extern unsigned int TOS_version; unsigned int button; if (pathname[0] == '\0') { pathname[0] = 'A' + Dgetdrv(); pathname[1] = ':'; Dgetpath(&pathname[2],0); strcat(pathname,"\\*.*"); } if (TOS_version < 0x104) fsel_input(pathname,filename,&button); else fsel_exinput(pathname,filename,&button,message); if (button == 1 && strlen(filename) > 0) { strcpy(path,pathname); strrchr(path,'\\')[1] = '\0'; strcat(path,filename); return 1; } return 0; } /* * This is the main part of the `GEM_filevisit' function below. * It does the whole work except for getting the filename. */ int filevisit_guts(fname) char *fname; { register Buffer *bp; char bname[NBUFN], zname[NBUFN]; int s,n; Cursor_off(); if (!arf(fname)) { s = FALSE; goto done; } for (bp = first_buffer; bp; bp = bp->nextb) { /* * check to see if the file is already in a buffer */ if (strcmp(get_dString(bp->b_fname),fname) == 0) /* found the buffer */ { mlwrite("[Old buffer]"); t_beep(); use_buffer(bp,TRUE); goto_BoB(FALSE,1); /* use current window */ s = TRUE; goto done; } } makename(bname,fname); /* New buffer name */ strcpy(zname,bname); n = 1; while (bfind(bname,FALSE,0)) /* make sure buffer name is unique */ { n++; strcat(strcat(strcat( strcpy(bname,zname), "<"), l_to_a((long int)n)), ">"); } if ((bp = bfind(bname,TRUE,BFINTERACTIVE)) == NULL) { mlwrite("Cannot create buffer"); s = ABORT; goto done; } /* * put the buffer in a window and make it current */ use_buffer(bp,TRUE); goto_BoB(FALSE,1); s = readin(fname); /* Read file into the current buffer */ done : update(); Cursor_on(); return s; } /* * Select a file for editing. * This almost like `filevisit', but it uses the GEM fileselector box * to get the filename and does not allow any parameters. * Look around to see if you can find the file in another buffer; If you * can find it just pop it up and make it current. If you cannot find * the file, read it into a buffer, pop it up and make current. * Bound to "C-x C-v" and "C-x C-f" */ int GEM_filevisit() { static char pathname[NFILEN]; static char filename[13]; char fname[NFILEN]; int s; if ((s = file_select(fname,"Visit file",pathname,filename)) != TRUE) return s; return filevisit_guts(fname); } #endif /* * This command allows the user to modify the file name associated with * the current buffer. It is like the "f" command in UNIX "ed". The * operation is simple; just zap the name in the Buffer structure, and * mark the windows as needing an update. You can type a blank line at * the prompt if you wish. */ newfilename(bp,fname) Buffer *bp; char *fname; { char name[NFILEN]; strcpy(name,fname); if (*name && !arf(name)) return FALSE; set_dString(&bp->b_fname,name); fixWB(bp,WFMODE); /* Update mode lines */ return TRUE; } /* ******************************************************************** */ /* ******************************************************************** */ /* ******************************************************************** */ /* * Read a file into the current buffer, blowing away any text found there. * Called by both the read and visit commands. Also called by the * mainline, to read in a file specified on the command line as an * argument. * Input: * fname: name of the file to read. Expected to be canonized. * bp : buffer to read the file into. * Returns: * TRUE: Everything went as expected. * FALSE: Couldn't open file, user won't let me clear buffer, file close * error. * ABORT: simular to FALSE. * Notes: * Undo is a mess. bclear() might blow it away and I don't save any. * yech. * Dot is left at the start of the buffer. * (file-read-hook) is called unless there is a problem with the read. */ readin(fname) char *fname; { char line[FLINE]; int nline, s; if ((s = bclear(curbp)) != TRUE) return s; /* Might be old */ set_dString(&curbp->b_fname,fname); if ((s = ffropen(fname)) != FIOSUC) { /*fixwin(curbp,FALSE);*/ if (s == FIOFNF) /* File not found */ { mlwrite("[New file]"); return TRUE; } return FALSE; } if (access(fname,W_OK)) curbp->b_flags |= BFVIEW; mlwrite("[Reading file]"); mline_dirty = FALSE; /* Dirty trick! */ nline = 0; while ((s = ffgetline(line,FLINE)) == FIOSUC) { if (!buffer_append(curbp,line)) { s = FIOERR; break; } /* Keep error message displayed */ ++nline; } fixwin(curbp,FALSE); /* curbp has changed a lot, adjust windows */ s = closeit("Read",nline,s); goto_BoB(FALSE,1); if (s) /* read the file in OK so call read-file-hook */ bhook(curbp,fr_hook); set_buffer_modified(curbp,FALSE); /* save an undo if current buffer */ return s; } /* * Read file "fname" and insert it into the current buffer. * openline() sets the buffer to changed. * Return the final status of the read. * Very simular to readin(). * Expects fname to be real - ie no wildcards. Does not have to canonized. * Returns: TRUE if all OK, else FALSE. */ insfile(fname) char fname[]; { char line[FLINE]; int nbytes, nline, s; register Line *lp1, *lp2, *anchorline; if (ffropen(fname) != FIOSUC) { mlwrite("%s not found.",fname); return FALSE; } mlwrite("[Inserting file]"); /* split the current line at the cursor */ if (!openline(FALSE,1)) return FALSE; /* leave dot on first line */ lp2 = the_dot->line; /* line containing dot. Insert new line after this */ anchorline = lp2->l_next; /* Second half of split line. This never moves */ nline = 0; mline_dirty = FALSE; /* Dirty trick! */ while ((s = ffgetline(line,FLINE)) == FIOSUC) { nbytes = strlen(line); if ((lp1 = lalloc(nbytes,FALSE)) == NULL) { s = FIOERR; break; } /* Keep message on the display */ lp1->l_prev = lp2; lp2->l_next = lp1; /* link in new line */ blkmov((lp2=lp1)->l_text,line,nbytes); ++nline; } lp2->l_next = anchorline; anchorline->l_prev = lp2; /* make the last link */ forwdel(FALSE,1); /* rejoin split line to inserted line */ return closeit("Inserted",nline,s); } /* Write the current buffer to a file. * This function performs the details of file writing. Uses the file * management routines in the "fileio.c" package. The number of lines * written is displayed. Sadly, it looks inside a Line; provide * a macro for this. * Most of the grief is error checking of some sort. * Input: * fname: Expected to be real - ie no wildcards. Must be something the * OS can open. Does not have to canonized. * Returns: * TRUE: Everything went as expected. * FALSE: Problems - disk full, invalid file name, write protected, can't * close file. */ writeout(fname) char *fname; { int nline, s; register Line *lp; if (ffwopen(fname) != FIOSUC) return FALSE; /* Open writes err message */ mlwrite("[Writing file]"); nline = 0; s = FIOSUC; for (lp = BUFFER_FIRST_LINE(curbp); lp != BUFFER_LAST_LINE(curbp); lp = lforw(lp)) { if ((s = ffputline(lp->l_text,llength(lp))) != FIOSUC) break; ++nline; } mline_dirty = FALSE; return closeit("Wrote",nline,s); } /* Try to close the open file. If we can, say so. * Returns: TRUE if all OK, else FALSE. */ static int closeit(word,n,s) char *word; { if (s != FIOERR && ffclose() == FIOSUC) /* All OK */ { if (!mline_dirty) mlwrite("[%s %d line%s]",word,n, n==1 ? "" : "s"); return TRUE; } else ffcleanup(); return FALSE; /* Some sort of error */ } /* ******************************************************************** */ /* ******************************************************************** */ /* ******************************************************************** */ /* Massage a filename: expand wildcards and canonize. * Massaged filename is put on top of fname. */ arf(fname) char *fname; { extern char current_directory[], /* in os.c */ *canonize(); char tmp[NFILEN]; #if FILENAME_COMPLETION if (fxpand(fname,TRUE,FALSE,TRUE,tmp,(pfi)NULL)) return FALSE; #else strcpy(tmp,fname); #endif return (canonize(tmp,fname, current_directory) != NULL); } /* * Fabricate a buffer name from a file name. * On most systems, this is just . */ void makename(bname,fname) register char *bname; char *fname; { extern char *nanex(); register char *ptr; register int n = NBUFN; /* remember to leave room for the '\0' */ ptr = nanex(fname); while (--n && (*bname++ = *ptr++)) ; *bname = '\0'; } /* * Check to see if a file exists. Useful for Mutt programming. */ file_exists(name) char *name; { char fname[NFILEN]; /* if not a valid name or can't open it, it don't exist */ if (!arf(strcpy(fname,name)) || ffropen(fname) != FIOSUC) return FALSE; ffclose(); return TRUE; }