📄 io.c
字号:
fnamebuf[FILESIZE];#ifdef MAC if (Macmode) { if (!(fname = pfile(fnamebuf))) return; } else#endif /* MAC */ fname = ask_file((char *)NULL, curbuf->b_fname, fnamebuf); /* Don't allow bad characters when creating new files. */ if (!OkayBadChars && (curbuf->b_fname==NULL || strcmp(curbuf->b_fname, fnamebuf) != 0)) {#ifdef UNIX static const char badchars[] = "!$^&*()~`{}\"'\\|<>? ";#endif /* UNIX */#ifdef MSDOS static const char badchars[] = "*|<>? ";#endif /* MSDOS */#ifdef MAC static const char badchars[] = ":";#endif /* MAC */ register char *cp = fnamebuf; register int c; while ((c = *cp++ & CHARMASK) != '\0') /* avoid sign extension... */ if (c < ' ' || c == '\177' || strchr(badchars, c)) complain("'%p': bad character in filename.", c); } chk_mtime(curbuf, fname, "write"); filemunge(fname); curbuf->b_type = B_FILE; /* in case it wasn't before */ setfname(curbuf, fname); file_write(fname, NO);}/* Open file FNAME supplying the buffer IO routine with buffer BUF. HOW is F_READ, F_WRITE or F_APPEND. IFBAD == COMPLAIN means that if we fail at opening the file, call complain. LOUDNESS says whether or not to print the "reading ..." message on the message line. NOTE: This opens the pr_name(fname, NO) of fname. That is, FNAME is usually an entire pathname, which can be slow when the pathname is long and there are lots of symbolic links along the way (which has become very common in my experience). So, this speeds up opens file names in the local directory. It will not speed up things like "../scm/foo.scm" simple because by the time we get here that's already been expanded to an absolute pathname. But this is a start. */File *open_file(fname, buf, how, complainifbad, quiet)register char *fname;char *buf;register int how;int complainifbad, quiet;{ register File *fp; io_chars = 0; io_lines = 0; fp = f_open(pr_name(fname, NO), how, buf, LBSIZE); if (fp == NULL) { message(IOerr((how == F_READ) ? "open" : "create", fname)); if (complainifbad) complain((char *)NULL); } else { int rd_only = FALSE;#ifndef MAC if (access(pr_name(fname, NO), W_OK) == -1 && errno != ENOENT) { rd_only = TRUE; fp->f_flags |= F_READONLY; }#endif if (!quiet) { fp->f_flags |= F_TELLALL; f_mess("\"%s\"%s", pr_name(fname, YES), rd_only ? " [Read only]" : NullStr); } } return fp;}#ifndef MSDOS/* Check to see if the file has been modified since it was last written. If so, make sure they know what they're doing. I hate to use another stat(), but to use confirm we gotta do this before we open the file. NOTE: This stats FNAME after converting it to a path-relative name. I can't see why this would cause a problem ... */voidchk_mtime(thisbuf, fname, how)Buffer *thisbuf;char *fname, *how;{ struct stat stbuf; Buffer *b; static const char mesg[] = "Shall I go ahead and %s anyway? "; if ((thisbuf->b_mtime != 0) && /* if we care ... */ ((b = file_exists(fname)) != NULL) && /* we already have this file */ (b == thisbuf) && /* and it's the current buffer */ (stat(pr_name(fname, NO), &stbuf) != -1) && /* and we can stat it */ (stbuf.st_mtime != b->b_mtime)) { /* and there's trouble. */ rbell(); redisplay(); /* Ring that bell! */ TOstart("Warning", TRUE); Typeout("\"%s\" now saved on disk is not what you last", pr_name(fname, YES)); Typeout("visited or saved. Probably someone else is editing"); Typeout("your file at the same time."); if (how) { Typeout(""); Typeout("Type \"y\" if I should %s, anyway.", how); f_mess(mesg, how); } TOstop(); if (how) confirm(mesg, how); }}#endif /* !MSDOS */voidfile_write(fname, app)char *fname;bool app;{ File *fp;#ifdef BACKUPFILES if (!app && BkupOnWrite) file_backup(fname);#endif fp = open_file(fname, iobuff, app ? F_APPEND : F_WRITE, YES, NO); if (EndWNewline) { /* Make sure file ends with a newLine */ Bufpos save; DOTsave(&save); ToLast(); if (length(curline)) /* Not a blank Line */ LineInsert(1); SetDot(&save); } putreg(fp, curbuf->b_first, 0, curbuf->b_last, length(curbuf->b_last), NO); close_file(fp); set_ino(curbuf); unmodify();}voidReadFile(){ Buffer *bp; char *fname, fnamebuf[FILESIZE]; int lineno;#ifdef MAC if (Macmode) { if (!(fname = gfile(fnamebuf))) return; } else#endif /* MAC */ fname = ask_file((char *)NULL, curbuf->b_fname, fnamebuf); chk_mtime(curbuf, fname, "read"); if (IsModified(curbuf)) { char *y_or_n; int c; for (;;) { rbell(); y_or_n = ask(NullStr, "Shall I make your changes to \"%s\" permanent? ", curbuf->b_name); c = CharUpcase(*y_or_n); if (c == 'Y' || c == 'N') break; } if (c == 'Y') SaveFile(); } if ((bp = file_exists(fnamebuf)) != NULL && (bp == curbuf)) lineno = pnt_line() - 1; else lineno = 0; unmodify(); initlist(curbuf); setfname(curbuf, fname); read_file(fname, NO); SetLine(next_line(curbuf->b_first, lineno));}voidInsFile(){ char *fname, fnamebuf[FILESIZE];#ifdef MAC if (Macmode) { if (!(fname = gfile(fnamebuf))) return; } else#endif /* MAC */ fname = ask_file((char *)NULL, curbuf->b_fname, fnamebuf); read_file(fname, YES);}#include "temp.h"bool DOLsave = NO; /* Do Lsave flag. If lines aren't being saved when you think they should have been, this flag is probably not being set, or is being cleared before lsave() was called. */private int nleft, /* number of good characters left in current block */ tmpfd = -1;daddr DFree = 1; /* pointer to end of tmp file */private char *tfname;private voidtmpinit(){ char buf[FILESIZE];#ifdef MAC swritef(buf, sizeof(buf), "%s/%s", HomeDir, d_tempfile);#else swritef(buf, sizeof(buf), "%s/%s", TmpFilePath, d_tempfile);#endif tfname = copystr(buf); tfname = mktemp(tfname); (void) close(creat(tfname, 0600));#ifndef MSDOS tmpfd = open(tfname, 2);#else /* MSDOS */ tmpfd = open(tfname, 0x8002); /* MSDOS fix */#endif /* MSDOS */ if (tmpfd == -1) complain("Warning: cannot create tmp file! %s", strerror(errno));}/* Close tempfile before execing a child process. * Since we might be vforking, we must not change any variables * (in particular tmpfd). */voidtmpclose(){ if (tmpfd != -1) (void) close(tmpfd);}/* Close and remove tempfile before exiting. */voidtmpremove(){ if (tmpfd != -1) { tmpclose(); (void) unlink(tfname); }}/* get a line at `tl' in the tmp file into `buf' which should be LBSIZE long */int Jr_Len; /* length of Just Read Line */voidgetline(addr, buf)daddr addr;register char *buf;{ register char *bp, *lp; lp = buf; bp = getblock(addr >> 1, FALSE); do ; while ((*lp++ = *bp++) != '\0'); Jr_Len = (lp - buf) - 1;}/* Put `buf' and return the disk address */daddrputline(buf)char *buf;{ register char *bp, *lp; register int nl; daddr free_ptr; lp = buf; free_ptr = DFree; bp = getblock(free_ptr, TRUE); nl = nleft; free_ptr = blk_round(free_ptr); while ((*bp = *lp++) != '\0') { if (*bp++ == '\n') { *--bp = '\0'; break; } if (--nl == 0) { free_ptr = forward_block(free_ptr); DFree = free_ptr; bp = getblock(free_ptr, TRUE); lp = buf; /* start over ... */ nl = nleft; } } free_ptr = DFree; DFree += (((lp - buf) + CH_SIZE - 1) / CH_SIZE); /* (lp - buf) includes the null */ return (free_ptr << 1);}/* The theory is that critical section of code inside this procedure will never cause a problem to occur. Basically, we need to ensure that two blocks are in memory at the same time, but I think that this can never screw up. */#define lockblock(addr)#define unlockblock(addr)private boolf_getputl(line, fp)Line *line;register File *fp;{ register char *bp; register int c, nl, room = LBSIZE-1; daddr free_ptr; char *base; free_ptr = DFree; base = bp = getblock(free_ptr, TRUE); nl = nleft; free_ptr = blk_round(free_ptr); do { /* We can't store NUL in our buffer, so ignore it. * Of course, with a little ingenuity we could: * NUL could be represented by \n! */ c = jgetc(fp); if (c == '\0') continue;#ifdef MSDOS if (c == '\r') continue;#endif /* MSDOS */ if (c == EOF || c == '\n') break; if (--nl == 0) { char *newbp; size_t nbytes; lockblock(free_ptr); DFree = free_ptr = forward_block(free_ptr); nbytes = bp - base; newbp = getblock(free_ptr, TRUE); nl = nleft; byte_copy(base, newbp, nbytes); bp = newbp + nbytes; base = newbp; unlockblock(free_ptr); } *bp++ = c; } while (--room > 0); *bp++ = '\0'; free_ptr = DFree; DFree += (((bp - base) + CH_SIZE - 1) / CH_SIZE); line->l_dline = (free_ptr << 1); if (room == 0) { add_mess(" [Line too long]"); rbell(); return YES; } if (c == EOF) { if (--bp != base) add_mess(" [Incomplete last line]"); return YES; } io_lines += 1; return NO;}typedef struct block { char b_dirty; /* (bool) */ short b_bno; char b_buf[JBUFSIZ]; struct block *b_LRUnext, *b_LRUprev, *b_HASHnext;} Block;#define HASHSIZE 7 /* Primes work best (so I'm told) */#define B_HASH(bno) ((bno) % HASHSIZE)#ifdef MACprivate Block *b_cache,#elseprivate Block b_cache[NBUF],#endif *bht[HASHSIZE], /* Block hash table. Must be zero initially */ *f_block = NULL, *l_block = NULL;private int max_bno = -1, NBlocks;private void (*blkio) ptrproto((Block *, int (*) ptrproto((int, UnivPtr, size_t))));#ifdef MACpublic boolmake_cache() /* Only 32K of static space on Mac, so... */{ return (b_cache = (Block *) calloc(NBUF,sizeof(Block))) != NULL;}#endif /* MAC */private voidreal_blkio(b, iofcn)register Block *b;register int (*iofcn) ptrproto((int, UnivPtr, size_t));{ (void) lseek(tmpfd, (long) ((unsigned) b->b_bno) * JBUFSIZ, 0); if ((*iofcn)(tmpfd, (UnivPtr) b->b_buf, (size_t)JBUFSIZ) != JBUFSIZ) error("[Tmp file %s error: to continue editing would be dangerous]", (iofcn == read) ? "READ" : "WRITE");}private voidfake_blkio(b, iofcn)register Block *b;register int (*iofcn) ptrproto((int, UnivPtr, size_t));{ tmpinit(); blkio = real_blkio; real_blkio(b, iofcn);}voidd_cache_init(){ register Block *bp, /* Block pointer */ **hp; /* Hash pointer */ register short bno; for (bp = b_cache, bno = NBUF; --bno >= 0; bp++) { NBlocks += 1; bp->b_dirty = NO; bp->b_bno = bno; if (l_block == NULL) l_block = bp; bp->b_LRUprev = NULL; bp->b_LRUnext = f_block; if (f_block != NULL) f_block->b_LRUprev = bp; f_block = bp; bp->b_HASHnext = *(hp = &bht[B_HASH(bno)]); *hp = bp; } blkio = fake_blkio;}voidSyncTmp(){ register Block *b;#ifdef IBMPC register int bno = 0; /* sync the blocks in order, for file systems that don't allow holes (MSDOS). Perhaps this benefits floppy-based file systems. */ for (bno = 0; bno <= max_bno; ) { if ((b = lookup(bno++)) && b->b_dirty) { (*blkio)(b, write); b->b_dirty = NO; } }#else for (b = f_block; b != NULL; b = b->b_LRUnext) if (b->b_dirty) { (*blkio)(b, (int (*) ptrproto((int, UnivPtr, size_t)))write); b->b_dirty = NO; }#endif}private Block *lookup(bno)register short bno;{ register Block *bp; for (bp = bht[B_HASH(bno)]; bp != NULL; bp = bp->b_HASHnext) if (bp->b_bno == bno) break; return bp;}private voidLRUunlink(b)register Block *b;{ if (b->b_LRUprev == NULL) f_block = b->b_LRUnext; else b->b_LRUprev->b_LRUnext = b->b_LRUnext; if (b->b_LRUnext == NULL) l_block = b->b_LRUprev; else b->b_LRUnext->b_LRUprev = b->b_LRUprev;}private Block *b_unlink(bp)register Block *bp;{ register Block *hp, *prev = NULL; LRUunlink(bp); /* Now that we have the block, we remove it from its position in the hash table, so we can THEN put it somewhere else with it's new block assignment. */ for (hp = bht[B_HASH(bp->b_bno)]; hp != NULL; prev = hp, hp = hp->b_HASHnext) if (hp == bp) break; if (hp == NULL) { writef("\rBlock %d missing!", bp->b_bno); finish(0); } if (prev) prev->b_HASHnext = hp->b_HASHnext; else bht[B_HASH(bp->b_bno)] = hp->b_HASHnext; if (bp->b_dirty) { /* do, now, the delayed write */ (*blkio)(bp, (int (*) ptrproto((int, UnivPtr, size_t)))write); bp->b_dirty = NO; } return bp;}/* Get a block which contains at least part of the line with the address atl. Returns a pointer to the block and sets the global variable nleft (number of good characters left in the buffer). */private char *getblock(atl, IsWrite)daddr atl;bool IsWrite;{ register int bno, off; register Block *bp; static Block *lastb = NULL; bno = da_to_bno(atl); off = da_to_off(atl); if (da_too_huge(atl)) error("Tmp file too large. Get help!"); nleft = JBUFSIZ - off; if (lastb != NULL && lastb->b_bno == bno) { lastb->b_dirty |= IsWrite; return lastb->b_buf + off; } /* The requested block already lives in memory, so we move it to the end of the LRU list (making it Most Recently Used) and then return a pointer to it. */ if ((bp = lookup(bno)) != NULL) { if (bp != l_block) { LRUunlink(bp); if (l_block == NULL) f_block = l_block = bp; else l_block->b_LRUnext = bp; bp->b_LRUprev = l_block; l_block = bp; bp->b_LRUnext = NULL; } if (bp->b_bno > max_bno) max_bno = bp->b_bno; bp->b_dirty |= IsWrite; lastb = bp; return bp->b_buf + off; } /* The block we want doesn't reside in memory so we take the least recently used clean block (if there is one) and use it. */ bp = f_block; if (bp->b_dirty) /* The best block is dirty ... */ SyncTmp(); bp = b_unlink(bp); if (l_block == NULL) l_block = f_block = bp; else l_block->b_LRUnext = bp; /* Place it at the end ... */ bp->b_LRUprev = l_block; l_block = bp; bp->b_LRUnext = NULL; /* so it's Most Recently Used */ bp->b_dirty = IsWrite; bp->b_bno = bno; bp->b_HASHnext = bht[B_HASH(bno)]; bht[B_HASH(bno)] = bp; /* Get the current contents of the block UNLESS this is a new block that's never been looked at before, i.e., it's past the end of the tmp file. */ if (bp->b_bno <= max_bno) (*blkio)(bp, read); else max_bno = bno; lastb = bp; return bp->b_buf + off;}char *lbptr(line)Line *line;{ return getblock(line->l_dline >> 1, FALSE);}/* save the current contents of linebuf, if it has changed */voidlsave(){ if (curbuf == NULL || !DOLsave) /* Nothing modified recently */ return; if (strcmp(lbptr(curline), linebuf) != 0) SavLine(curline, linebuf); /* Put linebuf on the disk. */ DOLsave = NO;}#ifdef BACKUPFILESprivate voidfile_backup(fname)char *fname;{#ifndef MSDOS char *s; register int i; int fd1, fd2; char tmp1[JBUFSIZ], tmp2[JBUFSIZ]; struct stat buf; int mode; strcpy(tmp1, fname); if ((s = strrchr(tmp1, '/')) == NULL) swritef(tmp2, sizeof(tmp2), "#%s~", fname); else { *s++ = '\0'; swritef(tmp2, sizeof(tmp2), "%s/#%s~", tmp1, s); } if ((fd1 = open(fname, 0)) < 0) return; /* create backup file with same mode as input file */#ifndef MAC if (fstat(fd1, &buf) != 0) mode = CreatMode; else#endif mode = buf.st_mode; if ((fd2 = creat(tmp2, mode)) < 0) { (void) close(fd1); return; } while ((i = read(fd1, (UnivPtr) tmp1, sizeof(tmp1))) > 0) write(fd2, (UnivPtr) tmp1, (size_t) i);#ifdef BSD4_2 (void) fsync(fd2);#endif (void) close(fd2); (void) close(fd1);#else /* MSDOS */ char *dot, *slash, tmp[FILESIZE]; strcpy(tmp, fname); slash = basename(tmp); if (dot = strrchr(slash, '.')) { if (!stricmp(dot,".bak")) return; *dot = '\0'; } strcat(tmp, ".bak"); unlink(tmp); rename(fname, tmp);#endif /* MSDOS */}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -