📄 sbstr.c
字号:
/* Level 4 - write out everything possible */ /* Back up to start of sbstr */ while((sdf = sd->slback) && !sbx_qlk(sdf)) sd = sdf; if((sdf = sd->slforw) == 0 /* If only 1 blk, ensure on disk */ || sbx_qlk(sdf)) { if(sm = sd->sdmem) { if(sd->sdflags&SD_MOD) /* If impure, */ sbx_aout(sd, 1); /* swap out the SD */ sbm_mfree(sm); sd->sdmem = 0; } return(0); } /* At least two blocks in string. Set up for flushout. */ sbx_aout(sd, 0); /* Swapout as much of sbstring as possible */ return(0);lev3: /* Level 3 - write out more */lev2: /* Level 2 - write out all impure & small pure */lev1: if(lev >= 1) /* Level 1 - merge small impure & small pure */ { if(!sm || !sdf) return(0); while(((smf = sdf->sdmem) && !(sdf->sdflags&SD_LOCKS) && (more = smf->smuse + sm->smuse) < SB_BUFSIZ) ) { if(sm->smforw != smf && more > sm->smlen) /* If need more rm */ { sm = sbm_exp(sm,more); /* Get it */ if(!sm) return(0); /* If none, stop */ sd->sdmem = sm; } bcopy(smf->smaddr, sm->smaddr + sm->smuse, smf->smuse); sm->smuse = more; if(sm->smforw == smf) { sdf->sdmem = 0; sbm_mmrg(sm); /* Merge */ if(sm->smlen > more+SB_SLOP) sbm_mfree(sbm_split(sm, more)); /* Guaranteed to win since mmrg * just freed a mem node */ } sd->sdflags |= SD_MOD; if(sdf = sbx_ndel(sdf)) continue; return(0); } } if(lev <= 0) /* Level 0 - free up large pure blocks */ /* Also merge blocks which are adjacent on disk */ { if(sm) { if(sm->smuse == 0) sd->sdlen = 0; else if((sd->sdflags&SD_MOD) == 0 && sm->smuse > 64) { sbm_mfree(sm); sd->sdmem = 0; goto lev0adj; } else goto lev0adj; } if(sd->sdlen == 0 /* Free zero blocks */ && sd->slback) /* Make sure don't lose list */ { sbx_ndel(sd); if((sd = sdf) == 0) return(0); sdf = sd->slforw; } lev0adj: /* Merge blocks if adjacent on disk */ /* This is common after reading thru large chunks * of a file but not modifying it much. */ if((sd->sdflags&SD_MOD) == 0 /* Pure */ && sdf && (sdf->sdflags&(SD_LOCKS|SD_MOD)) == 0 && sd->sdfile && (sd->sdfile == sdf->sdfile) && (sd->sdaddr + sd->sdlen) == sdf->sdaddr ) { sd->sdlen += sdf->sdlen; sbx_ndel(sdf); /* Flush 2nd */ if(sm = sd->sdmem) { sbm_mfree(sm); sd->sdmem = 0; } } return(0); } return(0);}/* SBX_AOUT - output ALL of a hackable sbstring starting at given sdblk. * Note that code is careful to do things so that an abort at any * time (e.g. write error) will still leave sbstring in valid state. * Flag value: * 0 - Writes out as many unlocked sdblks as possible, and merges * so that resulting sdblk (same one pointed to by arg) * incorporates all stuff written out. * 1 - Writes out single sdblk indicated, whether unlocked or not. * Doesn't free mem or merge anything; does update physlist * and flags. * 2 - Writes out all sdblks to specified FD/offset, no mods at all, * not even to physlist or flags. Good for saving files * when something seems wrong. (How to pass fd/off args?) * (offset arg not implemented, no need yet; 0 assumed) * Returns 0 if successful, else UNIX system call error number. */sbx_aout(sdp,flag,fd)struct sdblk *sdp;int flag, fd;{ register struct sdblk *sd; register struct smblk *sm; register int cnt; int ifd, ofd, skflg, rem; chroff inlen; extern SBMA sbm_lowaddr; /* Need this from SBM for rndrem */ char buf[SB_BUFSIZ+16]; /* Get buffer space from stack! */ /* Allow extra for word-align reads. */ /* This should be +WDSIZE, but some */ /* C compilers (eg XENIX) can't handle */ /* "sizeof" arith in allocation stmts! */ /* This flag and the two ptrs below are needed because UNIX * maintains only one I/O ptr per open file, and we can sometimes * be reading from/writing to the swapout file at same time. * Using DUP() to get a new FD (to avoid seeking back and forth) * won't help since both FD's will use the same I/O ptr!!! * Lastly, can't depend on returned value of LSEEK to push/pop * ptr, since V6 systems don't implement tell() or lseek() directly. * So we have to do it by hand... */ int botchflg; chroff outptr, inptr; if((sd = sdp)==0) return; ofd = sbv_tf.sffd; /* Default output FD */ if(flag==0) { sbx_tset(sbx_qlen(sd),0);/* Find place for whole string */ outptr = sbv_taddr; /* We'll have to update wrt ptr */ } else if (flag==1) /* Single SD block, so it's reasonable to * try aligning the output with the input. */ { if(sm = sd->sdmem) { cnt = rndrem(sm->smaddr - sbm_lowaddr); sbx_tset((chroff)(sm->smuse),cnt); } else { cnt = rndrem(sd->sdaddr); sbx_tset(sd->sdlen, cnt); } outptr = sbv_taddr; /* We'll have to update wrt ptr */ } else /* Outputting a whole sbstring to a file */ { ofd = fd; outptr = 0; } for(; sd;) { if(flag==0 && sbx_qlk(sd)) break; /* Stop when hit locked sdblk */ if(sm = sd->sdmem) { if(cnt = sm->smuse) if(write(ofd, sm->smaddr, cnt) != cnt) return(sbx_err(errno,"Swapout wrt err")); outptr += cnt; if(flag==0) { sd->sdmem = 0; /* Flush the mem used */ sbm_mfree(sm); } inlen = cnt; } else if(inlen = sd->sdlen) { if(sd->sdfile == 0) return(sbx_err(errno,"Sdfile 0, SD %o",sd)); /* Foo on UNIX */ botchflg = ((ifd = sd->sdfile->sffd) == ofd) ? 1 : 0; skflg = 1; /* Always seek first time */ inptr = sd->sdaddr; /* Efficiency hack - set up for first read so that * transfer is word-aligned and terminates at end * of a disk block. */ rem = rndrem(inptr); /* Get alignment */ cnt = SB_BUFSIZ - (int)(inptr%SB_BUFSIZ); while(inlen > 0) { if(inlen < cnt) cnt = inlen; if(!sbx_rdf(ifd, buf+rem, cnt, skflg, inptr)) return(sbx_err(errno,"Swapout err, SD %o",sd)); /* Further seeks depend on botch setting */ if(skflg = botchflg) { if(lseek(ofd,outptr,0) < 0) return(sbx_err(errno, "Swapout sk err")); inptr += cnt; } if(write(ofd, buf+rem, cnt) != cnt) return(sbx_err(errno, "Swapout wrt err")); outptr += cnt; inlen -= cnt; cnt = SB_BUFSIZ; /* Now can use full blocks */ rem = 0; /* Aligned nicely, too! */ } inlen = sd->sdlen; } /* Text written out, now merge block in */ if(flag == 2) /* No merge if saving file */ goto donxt; if(sd != sdp) /* First block? */ { sdp->sdlen += inlen; /* No, simple merge */ sd = sbx_ndel(sd); /* Flush, get next */ continue; } /* Handle 1st block specially */ if(sd->sdfile /* Unlink from phys list */ && sd != sbv_tsd) /* Don't unlink if self */ sbx_npdel(sd); sd->sdlen = inlen; sd->sdfile = &sbv_tf; sd->sdaddr = sbv_taddr; /* Set from sbx_tset val */ sd->sdflags &= ~SD_MOD; /* On disk, no longer modified */ /* Now insert into phys list at specified place */ if(sd == sbv_tsd) /* If already same place */ goto next; /* Skip linkin. */ if(sd->sdback = sbv_tsd) { sd->sdforw = sbv_tsd->sdforw; sd->sdback->sdforw = sd; } else { sd->sdforw = sbv_tf.sfptr1; sbv_tf.sfptr1 = sd; } if(sd->sdforw) sd->sdforw->sdback = sd; next: if(flag==1) /* If only doing 1 sdblk, */ break; /* stop here. */ donxt: sd = sd->slforw; /* Done with 1st, get next */ } return(0); /* Win return, no errors */}/* Returns hackable length of a sbstring (ends at EOF or locked block) */chroffsbx_qlen(sdp)struct sdblk *sdp;{ register struct sdblk *sd; register struct smblk *sm; chroff len; len = 0; for(sd = sdp; sd && !sbx_qlk(sd); sd = sd->slforw) if(sm = sd->sdmem) len += (chroff)sm->smuse; else len += sd->sdlen; return(len);}/* SBX_TSET - finds a place on temp swapout file big enough to hold * given # of chars. Sets SBV_TADDR to that location, as well * as seeking to it so the next write call will output there. * This location is guaranteed to have the requested * byte alignment (0 = word-aligned). */sbx_tset(loff, align)chroff loff;int align;{ register int fd; if(sbv_tf.sffd <= 0) { /* Must open the temp file! *//* Temporary file mechanism is system-dependent. Eventually this** will probably require inclusion of a true c-env header file; for the** time being, we can cheat a little by checking O_T20_WILD, which will** be defined by <sys/file.h> on TOPS-20. Otherwise, we assume we are** on a real Unix.*/#ifdef O_T20_WILD extern char *tmpnam(); /* Use ANSI function */ fd = open(tmpnam((char *)NULL), O_RDWR | O_CREAT | O_TRUNC | O_BINARY); if(fd < 0) return(sbx_err(0,"Swapout creat err")); #else /* Real Unix */ static char fcp[] = "/tmp/sbd.XXXXXX"; if((fd = creat(mktemp(fcp),0600)) < 0) return(sbx_err(0,"Swapout creat err")); /* Must re-open so that we can both read and write to it */ close(fd); if((fd = open(fcp,2)) < 0) return(sbx_err(0,"Swapout open err")); unlink(fcp); /* Set so it vanishes when we do */#endif sbv_tf.sffd = fd; /* Initialize the sbfile struct */ sbv_tf.sfptr1 = 0; sbv_ftab[fd] = &sbv_tf; /* Record in table of all sbfiles */ sbv_taddr = 0; /* "Return" this value */ return; /* Ignore alignment for now */ } sbv_tsd = sbx_ffnd(&sbv_tf, loff+align, &sbv_taddr); sbv_taddr += align; if(lseek(sbv_tf.sffd, sbv_taddr, 0) < 0) return(sbx_err(0,"Swapout seek err: (%d,%ld,0) %d %s", sbv_tf.sffd, sbv_taddr, errno, strerror(errno)));}/* SBX_FFND - searches disk list of given file for free space of * at least size chars. Note that list must be sorted by ascending * disk addrs in order for this to work! If sdaddrs are only * changed in SBX_SPLIT this will be true. * Sets "aloc" to disk address for writing (this is guaranteed to * be word-aligned, for efficiency), and returns SD ptr to * block which this addr should follow in the physical list. If ptr * is 0, it means addr should be 1st thing in list. */struct sdblk *sbx_ffnd(sfp, size, aloc)SBFILE *sfp;chroff size, *aloc;{ register struct sdblk *sd, *sds, *sdl; chroff cur; cur = 0; sds = 0; sd = sfp->sfptr1;redo: for(; sd ; sd = (sds=sd)->sdforw) { if(cur < sd->sdaddr) /* Gap seen? */ { if(size <= (sd->sdaddr - cur)) /* Yes, big enuf? */ break; /* Yup! */ } /* No, bump. */ else if(cur >=(sd->sdaddr + sd->sdlen)) /* No gap but chk */ continue; /* No overlap, ok */ /* Bump to next possible gap. */ cur = sd->sdaddr + sd->sdlen; cur = (long)rndup(cur); /* Round up to word boundary! */ } *aloc = cur; /* Return winning addr */ /* Perform verification check -- make sure this really is OK * and complain if not. If this never blows up, eventually can * take the check out. */ sdl = sd; for(sd = sfp->sfptr1; sd; sd = sd->sdforw) { if(cur < sd->sdaddr) { if(size <= (sd->sdaddr - cur)) continue; } else if(cur >= (sd->sdaddr + sd->sdlen)) continue; sbx_err(0,"FFND blew it, but recovered. SD %o siz %ld", sd, size); sd = (sds = sdl)->sdforw; goto redo; } return(sds); /* Return ptr to block this addr follows */}sbx_rdf(fd,addr,cnt,skflg,loc)register int fd;char *addr;int skflg;chroff loc;{ register int rres, eres; long lres; char *errtyp, *ftyp; chroff curlen; errno = 0; if(skflg && (lres = lseek(fd, (long)loc, 0)) == -1) { errtyp = "Sk err"; goto errhan; } if((rres = read(fd, addr, cnt)) != cnt) { lres = rres; errtyp = "Rd err"; goto errhan; } return(rres);errhan: /* Handle read or seek error */ eres = errno; if(fd == sbv_tf.sffd) /* See if dealing with swapout file */ { ftyp = "(swap)"; curlen = 0; } else { /* No, normal buffer file. */ ftyp = ""; curlen = sbx_fdlen(fd); if(sbv_ftab[fd] && (curlen != sbv_ftab[fd]->sflen)) /* File changed? */ if(sbx_rugpull(fd)) /* Yes, handle special case */ return(cnt); /* Allow "win" return */ } sbx_err(0,"%s %d:%s, %ld:(%d%s,%o,%d)=%ld (fl %ld)", errtyp, eres, strerror(eres), loc, fd, ftyp, addr, cnt, lres, curlen); return(0);}/* SBX_RUGPULL(fd) - Special routine called when package detects that * the file indicated by the fd has changed since its original * opening. This can happen when a file is over-written by some * other program (ED, for example). * This means that all sdblks which reference this fd * are probably bad. Pass special error back up to the calling * program to give it a chance at doing something. * Extra credit: scan all sdblks and unpurify all which point to this * file, so as to protect everything we still remember about it. * Otherwise a GC could flush pure in-core portions. */sbx_rugpull(fd) /* FD already known to have entry in sbv_ftab */register int fd;{ int sbx_unpur(); /* First scan all sdblks to save what we still have. */ /* This does NOT remove the sdfile pointer, so we can still * find blocks that are affected. */ sbm_nfor(SM_DNODS, sizeof(struct sdblk), sbx_unpur, sbv_ftab[fd]); if((int)sbv_debug == 1 || !sbv_debug) re
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -