sbstr.c

Go to the documentation of this file.
00001 /* SB - Copyright 1982 by Ken Harrenstien, SRI International
00002  *      This software is quasi-public; it may be used freely with
00003  *      like software, but may NOT be sold or made part of licensed
00004  *      products without permission of the author.  In all cases
00005  *      the source code and any modifications thereto must remain
00006  *      available to any user.
00007  *
00008  *      This is part of the SB library package.
00009  *      Any software using the SB library must likewise be made
00010  *      quasi-public, with freely available sources.
00011  */
00012 
00013 #if 0
00014 Todo stuff:
00015         New definitions:
00016                 sbbuffer - old sbstr.  Abbrev & struct "sbbuff".  Macro SBBUFF
00017                         (or SBBUF?)
00018                 sbstring - list of sds.  Abbrev sbstr.  Macro SBSTR.
00019                         Should *sbstr == *sdblk?  Yeah.
00020                 sbfile - as before.  Macro SBFILE. (or SBFIL?)
00021 
00022         Try to get zero-length sdblks flushed on the fly,
00023                 rather than waiting for moby GC.  Also, need to set
00024                 up compaction of SD freelist, as well as SM freelist.
00025                 Make SM freelist compact self-invoked by SBM_MGET?
00026         Any need for phys disk ptrs other than for tempfile?
00027                 Can do sbm_forn through SDblks to find active sdfiles
00028                 so list isn''t needed for that.
00029         Can sdback be flushed?  (not needed for keeping list sorted,
00030                 or for searching it -- only used when linking
00031                 blocks in or out of list.)  Perhaps use circular list?
00032                 If list only used for tmpfile, then to link in/out could
00033                 always start from sfptr1 of tmpfile? Sure, but slow?
00034                 Last SD on phys list could belong to no logical list,
00035                 and denote free space on tmpfile?
00036 
00037         --------------------------
00038 
00039         An "open" SBBUFFER will allow one to read, write, insert into,
00040 and delete from a sbstring (a logical character string).  "Dot" refers
00041 to the current logical character position, which is where all
00042 operations must happen; sb_fseek must be used to change this location.
00043 There are several states that the I/O can be in:
00044 !SBCUR          ----CLOSED----
00045                 All other elements, including SBIOP, should also be 0.
00046                 Dot is 0.
00047 SBCUR && !SBIOP ----OPEN/IDLE----
00048                 SBCUR points to a SD block (its SDMEM may or may not exist)
00049                 SBIOP==0 (otherwise it would be open/ready)
00050                 Dot is SBDOT + SBOFF.
00051                 R/Wleft must be 0.
00052 SBCUR && SBIOP  ----OPEN/READY----
00053                 SBCUR points to a SDBLK (SDMEM must exist!)
00054                 SBIOP exists.
00055                 Dot is SBDOT + offset into SMBLK.  SBOFF is ignored!
00056                 SB_WRIT flag is set if "smuse" must be updated.
00057                 The R/Wleft counts are set up:
00058                 1. Rleft 0, Wleft 0 -- Since SBIOP is set, must assume
00059                         counts are too.
00060                         So this means at end of text, no room left.
00061                         Otherwise would imply that setup needs doing.
00062                 2. Rleft N, Wleft 0 -- At beg or middle of text
00063                 3. Rleft 0, Wleft N -- At end of text
00064                 4. Rleft N, Wleft N -- Shouldn''t ever happen
00065 
00066                 Note that Rleft is always correct.  Wleft is sometimes
00067                 set 0 in order to force a call to determine real state.
00068 
00069 Note that SBIOP alone is a sufficient test for being OPEN/READY.
00070 
00071 The important thing about updating the smblk is to ensure that the "smuse"
00072 field is correct.  This can only be changed by writing or deleting.  We assume
00073 that deletions always update immediately, thus to determine if an update
00074 is necessary, see if SB_WRIT is set.  If so, update smuse before doing
00075 anything but more writing!!!!
00076 
00077 The SDBLK must be marked "modified" whenever a write operation is
00078 done.  We try to do this only the first time, by keeping Wleft zero
00079 until after the first write.  This is also when SB_WRIT gets set.
00080 However, if in overwrite mode, Wleft must be kept zero in order to
00081 force the proper actions; SB_WRIT is also not turned on since smuse
00082 will not change.  Note that at EOF, overwrite becomes the same thing
00083 as insert and is treated identically...
00084 
00085         If a SBLK has an in-core copy but no disk copy, it can be
00086 freely modified.  Otherwise, modifications should preferably split
00087 the block so as to retain "pure" blocks as long as possible.  "Pure" blocks
00088 can always have their in-core versions flushed immediately (unless for
00089 compaction purposes they''ll need to be written out in the same GC pass).
00090 Alternatively, mods can simply mark the disk copy "free" and go
00091 ahead as if no such copy existed.
00092         No additions or changes to a pure block are allowed, but
00093 deletions from the end or beginning are always allowed.  All other
00094 changes must split or insert new blocks to accomplish the changes.
00095 
00096 Locking:
00097         SDBLKs are subject to unpredictable relocation, compaction,
00098 and garbage collecting.  There are three ways in which a SDBLK can
00099 remain fixed:
00100 
00101         1. The SDBLK has the SD_LOCK flag set.  This flag is used whenever
00102                 a SBBUF''s SBCUR is pointing to this SDBLK.
00103         2. The SDBLK has the SD_LCK2 flag set.  This flag is used only
00104                 during execution of various internal routines and should
00105                 not be seen anywhere during execution of user code.
00106         3. The SDBLK has no back-pointer (is first block in a sbstring).
00107                 Such SDBLKs cannot be relocated (since it is not known
00108                 what may be pointing to them) but unlike the other 2 cases
00109                 they are still subject to compaction with succeeding SDBLKs.
00110 
00111 The SDBLK must be locked with SD_LOCK for as long as it is being
00112 pointed to by SBCUR.  The sole exception is when a SBBUF in the
00113 OPEN/IDLE state is pointing to the first SDBLK of a sbstring; this
00114 sdblk is guaranteed not to be moved, since sdblks without a
00115 back-pointer are never moved.  SD_LOCK is asserted as soon as the state
00116 changes to OPEN/READY, of course.  The internal routines take pains to
00117 always move SD_LOCK as appropriate.  Note that only one SD in a
00118 sbstring can ever have SD_LOCK turned on.  SD_LCK2 is an auxiliary flag
00119 which may appear in more than one SDBLK, for use by low-level routines
00120 for various temporary reasons; either will prevent the SDBLK from being
00121 modified in any way by the storage compactor.
00122 
00123 SEEKs are a problem because it''s unclear at seek time what will happen
00124 next, so the excision of the smblk can''t be optimized.  If the seek
00125 happens to land in a sdblk with an existing smblk, there''s no problem;
00126 but if it''s a sdblk alone, how to decide which part of it to read in???
00127 If next action is:
00128         write - split up sdblk and create new one.  Read nothing in.
00129         read - read in 512 bytes starting at disk blk boundary if possible
00130                 else read in 128 bytes starting with selected char
00131                 (include beg of sdblk if less than 64 chars away)
00132         overwrite - as for read.
00133         backread - like read but position at end of sdblk.
00134         delete - split up sdblk, read nothing in.
00135 
00136 We solve this through the OPEN/IDLE state, where SBIOP == 0 means SBOFF
00137 points to logical offset from start of current sdblk, so that the seek
00138 need not take any action.  Only when a specific operation is requested
00139 will the transition to OPEN/READY take place, at which time we''ll know
00140 what the optimal excision strategy is.  The routine SBX_READY performs
00141 this function.
00142 
00143 The physical links (SDFORW and SDBACK) are only valid when SDFILE is
00144 set (likewise for SDLEN and SDADDR).  In other words, mungs to a sdblk
00145 must check SDFILE to see whether or not the phys links should be
00146 altered.  Normally they aren''t except during sdblk creation, deletion,
00147 or swapout, no matter how much the sdblk gets shuffled around
00148 logically.  The disk physical list is kept sorted in order of starting
00149 addresses.  The text blocks indicated can overlap.  When a GC is
00150 necessary, the code must figure out how much space is actually free.
00151 
00152 -------------- Old woolgathering, ignore rest of this page ---------------
00153 
00154 Question: should 512-byte buffers be maintained, one for each SBFILE?
00155 Or should the in-core text be hacked up to serve for buffering?
00156 Question is where to point the READ/WRITE system calls.  Currently,
00157 they are pointed directly at the in-core text, and there are no
00158 auxiliary buffers.
00159 
00160 If use auxiliary buffers:
00161         How to handle flushing, when changing location etc?
00162         Could be clever about reading from large disk block, only
00163         get part of it into buffer instead of splitting up in order to
00164         read a "whole" block.
00165         Problem: sbstrings can include pieces of several different files.
00166                 Hard to maintain just one buffer per FD without hacking
00167                 done on one sbstring screwing that on another.
00168 If don''t use buffers:
00169         Need to have a "chars-left" field in mem blocks, so know how
00170         much more can be added.  Will need heuristics for how much
00171         extra space to allocate.
00172 #endif /*COMMENT*/
00173 
00174 /* Includes, initial definitions */
00175 
00176 #include <stdio.h>
00177 #include "sb.h"
00178 
00179 #ifndef V6
00180 #define V6 0
00181 #endif
00182 
00183 #if V6
00184 #include <stat.h>
00185 #else
00186 #include <sys/types.h>
00187 #include <sys/stat.h>
00188 #if MINIX
00189 #include <fcntl.h>      /* For open() flags */
00190 #else
00191 #include <sys/file.h>   /* For open() flags */
00192 #endif /* MINIX */
00193 #endif /*-V6*/
00194 
00195 extern int errno;
00196 extern char *strerror();        /* From ANSI <string.h> */
00197 
00198 /* Allocation decls */
00199 SBFILE sbv_tf;          /* SBFILE for temp swapout file */
00200 int (*sbv_debug)();     /* Error handler address */
00201 
00202 
00203 /* SBX_READY argument flags (internal to SBSTR routines only)
00204  * The following values should all be unique; the exact value
00205  * doesn't matter as long as the right SKM flags are given.
00206  */
00207 #define SK_READF 0                      /* 0-skip fwd,  align BOB */
00208 #define SK_READB (0|SKM_0BACK|SKM_EOB)  /* 0-skip bkwd, align EOB */
00209 #define SK_WRITEF (0|SKM_EOB)           /* 0-skip fwd,  align EOB */
00210 #define SK_DELF (4|SKM_0BACK)           /* 0-skip bkwd, align BOB */
00211 #define SK_DELB (4|SKM_EOB)             /* 0-skip fwd,  align EOB */
00212 #define SKM_0BACK 01    /* Zero-skip direction: 0 = fwd, set = backwd
00213                          * Don't ever change this value! See SBX_NORM. */
00214 #define SKM_EOB   02    /* Alignment: 0 = Beg-Of-Buf, set = End-Of-Buf */
00215 
00216 /* Note on routine names:
00217  *      "SB_"   User callable, deals with sbbufs (usually).
00218  *      "SBS_"  User callable, deals with sbstrings only.
00219  *      "SBX_"  Internal routine, not meant for external use.
00220  *      "SBM_"  Routine handling mem alloc, usually user callable.
00221  */
00222 
00223 /* SBBUF Opening, Closing, Mode setting */
00224 
00225 /* SB_OPEN(sb,sd) - Sets up SBBUF given pointer to first SD of a sbstring.
00226  *      If SD == 0 then creates null sbstring.
00227  *      Any previous contents of SBBUF are totally ignored!!!  If you
00228  *              want to save the stuff, use SB_UNSET.
00229  *      Sets I/O ptr to start of sbstring.
00230  *      Returns 0 if error, else the given SB.
00231  */
00232 SBBUF *
00233 sb_open(sbp,sdp)
00234 SBBUF *sbp;
00235 SBSTR *sdp;
00236 {       register struct sdblk *sd;
00237         register int cnt;
00238         register WORD *clrp;
00239 
00240         if(!sbp) return((SBBUF *)0);
00241         if((sd = sdp) == 0)
00242           {     sd = sbx_ndget();       /* Get a fresh node */
00243                 clrp = (WORD *) sd;     /* Clear it all */
00244                 cnt = rnddiv(sizeof(struct sdblk));
00245                 do { *clrp++ = 0; } while(--cnt);
00246                 sd->sdflags = SD_NID;   /* Except flags of course */
00247           }
00248         else if(sd->slback)             /* Must be first thing in sbstring */
00249                 return((SBBUF *)0);             /* Perhaps could normalize tho */
00250 
00251         clrp = (WORD *) sbp;            /* Clear sbbuffer stuff */
00252         cnt = rnddiv(sizeof(SBBUF));
00253         do { *clrp++ = 0; } while(--cnt);
00254 
00255         sbp->sbcur = sd;
00256         /* Note that SD_LOCK need not be set, because first SDBLK has no
00257          * backptr.  This is desirable to allow storage compactor maximum
00258          * freedom in merging sdblks.
00259          */
00260         /*      sd->sdflags |= SD_LOCK; */      /* Lock this one */
00261         return(sbp);
00262 }
00263 
00264 
00265 /* SB_CLOSE(sb) - Close a SBBUF.
00266  *      Returns pointer to start of sbstring (first SD).
00267  *      Returns 0 if error.
00268  */
00269 SBSTR *
00270 sb_close(sbp)
00271 SBBUF *sbp;
00272 {       register SBBUF *sb;
00273         register struct sdblk *sd;
00274 
00275         if((sb = sbp) == 0)     /* Verify pointer */
00276                 return((SBSTR *)0);
00277         sb_rewind(sb);          /* Do most of the work, including unlock */
00278         sd = sb->sbcur;         /* Save ptr to sbstring */
00279         sb->sbcur = 0;          /* Now reset the sbbuffer structure */
00280         sb->sbflags = 0;
00281         return(sd);
00282 }
00283 
00284 
00285 /* SB_SETOVW(sbp) - Set SBBUF Over-write mode for PUTC's.
00286  * SB_CLROVW(sbp) - Clear ditto.
00287  */
00288 sb_setovw(sbp)
00289 SBBUF *sbp;
00290 {       register SBBUF *sb;
00291         if(sb=sbp)
00292           {     sb->sbflags |= SB_OVW;
00293                 sb->sbwleft = 0;
00294           }
00295 }
00296 
00297 sb_clrovw(sbp)
00298 SBBUF *sbp;
00299 {       register SBBUF *sb;
00300         if(sb=sbp) sb->sbflags &= ~SB_OVW;
00301 }
00302 
00303 /* SBSTRING file system operations (see also sb_fsave) */
00304 
00305 /* SB_FDUSE(fd) - Make a sbstring for given file.
00306  *      FD is an open file descriptor.
00307  *      Returns pointer to a SBSTR containing the given file, or 0 if error.
00308  *      The FD must not be closed until a SB_FDCLS is done to
00309  *      purge memory of any blocks pointing at the file.
00310  * ** Maybe allocate sbfile structs with sbx_ndget, i.e. overlay on
00311  * ** top of sdblk node??  Wd this screw verify, GC, etc? Maybe not if
00312  * ** SD_LCK2 set...
00313  */
00314 
00315 struct sbfile *sbv_ftab[SB_NFILES];
00316 
00317 chroff
00318 sbx_fdlen(fd)
00319 int fd;
00320 {
00321 #if !V6
00322         struct stat statb;
00323 #else
00324         struct statb statb;
00325         chroff len;
00326         struct {int hiwd ; int lowd;} foo;
00327 #endif /*V6*/
00328 
00329         if(fstat(fd,&statb) < 0) return((chroff)-1);
00330 #if V6
00331         len = statb.i_size1;
00332         len.hiwd = statb.i_size0 & 0377;
00333         return(len);
00334 #else
00335         return((chroff)statb.st_size);
00336 #endif /*-V6*/
00337 }
00338 
00339 SBSTR *
00340 sb_fduse(ifd)
00341 int ifd;
00342 {       register struct sdblk *sd;
00343         register struct sbfile *sf;
00344         register int fd;
00345         chroff len;
00346 
00347         if((fd = ifd) < 0 || SB_NFILES <= fd    /* Check for absurd FD */
00348           || sbv_ftab[fd])                      /* and slot already in use */
00349                 return((SBSTR *)0);
00350         if((len = sbx_fdlen(fd)) < 0) return((SBSTR *)0);
00351         sbv_ftab[fd]= sf = (struct sbfile *)sbx_malloc(sizeof(struct sbfile));
00352         sf->sffd = fd;
00353         sf->sfptr1 = sd = sbx_ndget();
00354         sf->sflen = len;
00355         sd->slforw = 0;
00356         sd->slback = 0;
00357         sd->sdforw = 0;
00358         sd->sdback = 0;
00359         sd->sdmem = 0;
00360         sd->sdfile = sf;
00361         sd->sdlen = len;
00362         sd->sdaddr = 0;
00363         return(sd);
00364 }
00365 
00366 /* SB_FDCLS(fd) - Close a file descriptor being used by sbstrings.
00367  *      If arg is -1, closes all FD's that are unused (a "sweep").
00368  *      For specific arg, returns 0 if couldn't close FD because still in use.
00369  *      Perhaps later version of routine could have option to copy
00370  *      still-used SD's to tempfile, and force the FD closed?
00371  */
00372 sb_fdcls(ifd)
00373 int ifd;
00374 {       register int fd;
00375 
00376         if((fd = ifd) >= 0)
00377           {     if(fd >= SB_NFILES) return(0);  /* Error of sorts */
00378                 return(sbx_fcls(sbv_ftab[fd]));
00379           }
00380         fd = SB_NFILES-1;
00381         do {
00382                 sbx_fcls(sbv_ftab[fd]);
00383           } while(--fd);        /* Doesn't try FD 0 ! */
00384         return(1);
00385 }
00386 
00387 sbx_fcls(sfp)
00388 struct sbfile *sfp;
00389 {       register struct sbfile *sf;
00390         register int fd;
00391 
00392         if((sf = sfp)==0                /* Ignore null args */
00393           || sf == &sbv_tf)             /* and never close our tempfile! */
00394                 return(0);
00395         fd = sf->sffd;                  /* Find sys file descriptor */
00396         if(sbv_ftab[fd] != sf)          /* Ensure consistency */
00397                 return(sbx_err(0,"SF table inconsistency"));
00398         if(sf->sfptr1)                  /* Any phys list still exists? */
00399                 return(0);              /* Yes, still in use, can't close */
00400         close(fd);                      /* Maybe do this when list gone? */
00401         sbv_ftab[fd] = 0;               /* Remove from table */
00402         free(sf);                       /* Remove sbfile struct from mem */
00403 }
00404 
00405 /* SB_FDINP(sb,fd) - Returns TRUE if specified fd is still in use
00406  *      by specified sbbuffer.
00407  */
00408 sb_fdinp(sb, fd)
00409 register SBBUF *sb;
00410 int fd;
00411 {       register struct sdblk *sd;
00412         register struct sbfile *sf;
00413 
00414         if((sf = sbv_ftab[fd]) == 0
00415           || (sd = sb->sbcur) == 0)
00416                 return(0);
00417         sd = sbx_beg(sd);               /* Move to beginning of sbstring */
00418         for(; sd; sd = sd->slforw)      /* Scan thru all blocks in string */
00419                 if(sd->sdfile == sf)    /* If any of them match, */
00420                         return(1);      /* Return tally-ho */
00421         return(0); 
00422 }
00423 
00424 /* SB_FSAVE(sb,fd) - Write entire SBBUF out to specified FD.
00425  *      Returns 0 if successful, else system call error number.
00426  */
00427 sb_fsave(sb,fd)         /* Write all of given sbbuf to given fd */
00428 register SBBUF *sb;
00429 int fd;
00430 {
00431         sbx_smdisc(sb);
00432         return(sbx_aout(sbx_beg(sb->sbcur), 2, fd));
00433 }
00434 
00435 /* SBBUF Character Operations */
00436 
00437 /* SB_GETC(sb) - Get next char from sbstring.
00438  *      Returns char at current location and advances I/O ptr.
00439  *      Returns EOF on error or end-of-string.
00440  */
00441 int
00442 sb_sgetc(sb)
00443 register SBBUF *sb;
00444 {
00445         if(--(sb->sbrleft) >= 0)
00446                 return sb_uchartoint(*sb->sbiop++);
00447 
00448         /* Must do hard stuff -- check ptrs, get next blk */
00449         sb->sbrleft = 0;                        /* Reset cnt to zero */
00450         if(sb->sbcur == 0                       /* Make sure sbbuffer there */
00451           || (int)sbx_ready(sb,SK_READF,0,SB_BUFSIZ) <= 0)  /* Normalize & gobble */
00452                 return(EOF);
00453         return(sb_sgetc(sb));                   /* Try again */
00454 }       /* Loop wd be faster, but PDL OV will catch infinite-loop bugs */
00455 
00456 
00457 /* SB_PUTC(sb,ch) - Put char into sbstring.
00458  *      Inserts char at current location.
00459  *      Returns EOF on error, else the char value.
00460  */
00461 int
00462 sb_sputc(sb,ch)
00463 register SBBUF *sb;
00464 int ch;
00465 {
00466         register struct sdblk *sd;
00467 
00468         if(--(sb->sbwleft) >= 0) return(*sb->sbiop++ = ch);
00469 
00470         sb->sbwleft = 0;                /* Reset cnt to avoid overflow */
00471         if((sd = sb->sbcur) == 0)       /* Verify string is there */
00472                 return(EOF);            /* Could perhaps create it?? */
00473         if(sb->sbflags&SB_OVW)          /* If overwriting, handle std case */
00474           {     if(sb->sbiop &&
00475                   --sb->sbrleft >= 0)           /* Use this for real count */
00476                   {     sd->sdflags |= SD_MOD;  /* Win, munging... */
00477                         return(*sb->sbiop++ = ch);
00478                   }
00479                 /* Overwriting and hit end of this block. */
00480                 if((int)sbx_ready(sb,SK_READF,0,SB_BUFSIZ) > 0) /* Re-normalize */
00481                         return(sb_sputc(sb,ch));
00482 
00483                 /*  No blks left, fall through to insert stuff at end */
00484           }
00485 
00486         /* Do canonical setup with heavy artillery */
00487         if((int)sbx_ready(sb,SK_WRITEF,SB_SLOP,SB_BUFSIZ) <= 0) /* Get room */
00488                 return(EOF);            /* Should never happen, but... */
00489         sb->sbflags |= SB_WRIT;
00490         sb->sbcur->sdflags |= SD_MOD;
00491         return(sb_sputc(sb,ch));        /* Try again */
00492 }       /* Loop wd be faster, but PDL OV will catch infinite-loop bugs */
00493 
00494 
00495 /* SB_PEEKC(sb) - Peek at next char from sbstring.
00496  *      Returns char that sb_getc would next return, but without
00497  *      changing I/O ptr.
00498  *      Returns EOF on error or end-of-string.
00499  */
00500 int
00501 sb_speekc(sb)
00502 register SBBUF *sb;
00503 {
00504         if (sb->sbrleft <= 0)                   /* See if OK to read */
00505           {     if (sb_sgetc(sb) == EOF)        /* No, try hard to get next */
00506                         return EOF;             /* Failed, return EOF */
00507                 sb_backc(sb);                   /* Won, back up */
00508           }
00509         return sb_uchartoint(*sb->sbiop);
00510 }
00511 
00512 /* SB_RGETC(sb) - Get previous char from sbstring.
00513  *      Returns char prior to current location and backs up I/O ptr.
00514  *      Returns EOF on error or beginning-of-string.
00515  */
00516 int
00517 sb_rgetc(sb)
00518 register SBBUF *sb;
00519 {
00520         register struct smblk *sm;
00521         register struct sdblk *sd;
00522 
00523         if((sd=sb->sbcur) && (sm = sd->sdmem)
00524           && sb->sbiop > sm->smaddr)
00525           {     if(sb->sbflags&SB_WRIT)
00526                   {     sm->smuse = sb->sbiop - sm->smaddr;
00527                         sb->sbwleft = 0;
00528                         sb->sbflags &= ~SB_WRIT;
00529                   }
00530                 sb->sbrleft++;
00531                 return sb_uchartoint(*--sb->sbiop);     /* Return char */ 
00532           }
00533         if((int)sbx_ready(sb,SK_READB,SB_BUFSIZ,0) <= 0)
00534                 return(EOF);
00535         return(sb_rgetc(sb));
00536 }
00537 
00538 /* SB_RDELC(sb) - Delete backwards one char.
00539  *      Returns nothing.
00540  */
00541 sb_rdelc(sbp)
00542 SBBUF *sbp;
00543 {       register SBBUF *sb;
00544         register struct sdblk *sd;
00545 
00546         if(((sb=sbp)->sbflags&SB_WRIT)  /* Handle simple case fast */
00547           && sb->sbiop > (sd = sb->sbcur)->sdmem->smaddr)
00548                   {     sb->sbwleft++;
00549                         sb->sbiop--;
00550                         sd->sdflags |= SD_MOD;
00551                         return;
00552                   }
00553         else sb_deln(sb,(chroff) -1);   /* Else punt... */
00554 }
00555 
00556 /* SB_DELC(sb) - Delete one char forward? */
00557 /* SB_INSC(sb,ch) - Insert char?  (instead of or in addition to PUTC) */
00558 
00559 
00560 /* SBBUF string or N-char operations */
00561 
00562 /* SB_DELN(sb,chroff) - delete N chars.  Negative N means backwards.
00563  *      Differs from sb_killn in that it flushes the text forever,
00564  *      and doesn't return anything.
00565  */
00566 
00567 sb_deln(sbp, num)
00568 SBBUF *sbp;
00569 chroff num;
00570 {
00571         register struct sdblk *sd;
00572 
00573         if(sd = sb_killn(sbp,num))
00574                 sbs_del(sd);    /* Punt */
00575 }
00576 
00577 /* SB_KILLN(sb,chroff) - delete N chars, saving.  Negative N means backwards.
00578  *      Returns SD pointer to beginning of saved sbstring.
00579  */
00580 struct sdblk *
00581 sb_killn(sbp, num)
00582 SBBUF *sbp;
00583 chroff num;
00584 {       register SBBUF *sb;
00585         register struct sdblk *sd, *sd2;
00586         struct sdblk *sdr, *sdx;
00587         chroff savdot;
00588 
00589         if((sd = sbx_xcis((sb=sbp),num,&sdr,&savdot)) == 0)
00590                 return((struct sdblk *)0);
00591 
00592         sb->sbcur->sdflags &= ~SD_LOCK; /* Now can flush sbcur lock */
00593 
00594         /* SD and SD2 now delimit bounds of stuff to excise.
00595          * First do direction dependent fixups
00596          */
00597         if(num >= 0)                    /* If deleting forward, */
00598                 sb->sbdot = savdot;     /* must reset dot to initial loc */
00599 
00600         /* SD and SD2 now in first/last order.  Complete SBCUR fixup. */
00601         sd2 = sdr;                      /* sdr has ptr to end of stuff */
00602         if(sd2 = sd2->slforw)           /* More stuff after killed list? */
00603           {     sb->sbcur = sd2;        /* Yes, point at it */
00604                 sb->sboff = 0;          /* Dot already set right */
00605           }
00606         else if(sdx = sd->slback)       /* See if any prior to killed list */
00607           {     sb->sbcur = sdx;                /* Yes, point at it */
00608                 sb->sboff = (sdx->sdmem ?       /* Get len of prev blk */
00609                         sdx->sdmem->smuse : sdx->sdlen);
00610                 sb->sbdot -= sb->sboff;
00611           }
00612         else sb_open(sb,(SBSTR *)0);    /* No stuff left!  Create null sbstring */
00613 
00614         /* Fix up logical links.  Note SD2 points to succ of killed stuff */
00615         if(sd->slback)                  /* If previous exists */
00616           {     if(sd->slback->slforw = sd2)    /* Point it to succ, and */
00617                         sd2->slback = sd->slback; /* thence to self */
00618                 sd->slback = 0;                 /* Now init killed list */
00619           }
00620         else if(sd2) sd2->slback = 0;           /* No prev, clean rest */
00621         (sd2 = sdr)->slforw = 0;                /* Finish killed list */
00622 
00623         sb->sbcur->sdflags |= SD_LOCK;  /* Ensure current SD now locked */
00624         sd->sdflags &= ~SD_LCK2;        /* And unlock killed list */
00625         sd2->sdflags &= ~SD_LCK2;
00626         return(sd);
00627 }
00628 
00629 /* SB_CPYN(sbp,num) - Copy num characters, returns SD to sbstring.
00630  *      Like SB_KILLN but doesn't take chars out of original sbstring.
00631  */
00632 SBSTR *
00633 sb_cpyn(sbp,num)
00634 SBBUF *sbp;
00635 chroff num;
00636 {       register SBBUF *sb;
00637         register struct sdblk *sd, *sd2;
00638         struct sdblk *sdr;
00639         chroff savloc;
00640 
00641         sb = sbp;
00642         if((sd = sbx_xcis(sb,num,&sdr,&savloc)) == 0)
00643                 return((SBSTR *)0);
00644         sd2 = sbx_scpy(sd,sdr);
00645         sb_seek(sb,-num,1);             /* Return to original loc */
00646         return(sd2);            /* Return val is ptr to head of copy.
00647                                  * It needn't be locked, because GC will
00648                                  * never move list heads!
00649                                  */
00650 }
00651 
00652 /* SB_SINS(sb,sd) - Insert sbstring at current location
00653  *
00654  */
00655 sb_sins(sbp,sdp)
00656 SBBUF *sbp;
00657 struct sdblk *sdp;
00658 {       register SBBUF *sb;
00659         register struct sdblk *sd, *sdx;
00660         chroff inslen;
00661 
00662         if((sb = sbp)==0
00663           || (sd = sdp) == 0)
00664                 return(0);
00665         if(sd->slback)          /* Perhaps normalize to beg? */
00666                 return(0);
00667         if((sdx = (struct sdblk *)sbx_ready(sb,SK_DELB)) == 0)  /* Get cur pos ready */
00668                 return(0);
00669         inslen = sbs_len(sd);           /* Save length of inserted stuff */
00670 
00671         sd->slback = sdx;               /* Fix up links */
00672         if(sdx->slforw)
00673           {     while(sd->slforw)       /* Hunt for end of inserted sbstring */
00674                         sd = sd->slforw;
00675                 sd->slforw = sdx->slforw;
00676                 sd->slforw->slback = sd;
00677           }
00678         sdx->slforw = sdp;
00679         sb->sboff += inslen;            /* Set IO ptr to end of new stuff */
00680         return(1);
00681 }
00682 
00683 /* SBSTRING routines - operate on "bare" sbstrings. */
00684 
00685 /* SBS_CPY(sd) - Copies given sbstring, returns ptr to new sbstring.
00686  */
00687 SBSTR *
00688 sbs_cpy(sdp)
00689 SBSTR *sdp;
00690 {       return(sbx_scpy(sdp,(struct sdblk *)0));
00691 }
00692 
00693 /* SBS_DEL(sd) - Flush a sbstring.
00694  */
00695 sbs_del(sdp)
00696 SBSTR *sdp;
00697 {       register struct sdblk *sd;
00698 
00699         if(sd = sdp)
00700                 while(sd = sbx_ndel(sd));
00701 }
00702 
00703 
00704 /* SBS_APP(sd1,sd2) - Appends sbstring sd2 at end of sbstring sd1.
00705  *      Returns sd1 (pointer to new sbstring).
00706  */
00707 
00708 SBSTR *
00709 sbs_app(sdp,sdp2)
00710 struct sdblk *sdp,*sdp2;
00711 {       register struct sdblk *sd, *sdx;
00712 
00713         if(sd = sdp)
00714           {     while(sdx = sd->slforw)
00715                         sd = sdx;
00716                 if(sd->slforw = sdx = sdp2)
00717                         sdx->slback = sd;
00718           }
00719         return(sdp);
00720 }
00721 
00722 /* SBS_LEN(sd) - Find length of sbstring.
00723  */
00724 chroff
00725 sbs_len(sdp)
00726 SBSTR *sdp;
00727 {       register struct sdblk *sd;
00728         register struct smblk *sm;
00729         chroff len;
00730 
00731         if((sd = sdp)==0) return((chroff)0);
00732         len = 0;
00733         for(; sd ; sd = sd->slforw)
00734           {     if(sm = sd->sdmem)
00735                         len += (chroff)sm->smuse;
00736                 else len += sd->sdlen;
00737           }
00738         return(len);
00739 }
00740 
00741 /* SBBUF I/O pointer ("dot") routines */
00742 
00743 /* SB_SEEK(sb,chroff,flag) - Like FSEEK.  Changes I/O ptr value as
00744  *      indicated by "flag":
00745  *              0 - offset from beg
00746  *              1 - offset from current pos
00747  *              2 - offset from EOF
00748  *      Returns -1 on errors.
00749  *      Seeking beyond beginning or end of sbbuf will leave pointer
00750  *      at the beginning or end respectively.
00751  *      Returns 0 unless error (then returns -1).
00752  */
00753 sb_seek(sbp, coff, flg)
00754 SBBUF *sbp;
00755 chroff coff;
00756 int flg;
00757 {       register SBBUF *sb;
00758         register struct smblk *sm;
00759         register struct sdblk *sd;
00760         SBMO moff;
00761 
00762         sb = sbp;
00763         if((sd = sb->sbcur) == 0) return(-1);
00764         if(sb->sbiop == 0)
00765           {     switch(flg)
00766                   {     case 0: if(coff == 0)   /* Optimize common case */
00767                                         return(sb_rewind(sb));
00768                                 sb->sboff = coff - sb->sbdot;   /* Abs */
00769                                 break;
00770                         case 1: sb->sboff += coff;              /* Rel */
00771                                 break;
00772                         case 2: sb->sboff += sb_ztell(sb) + coff;
00773                                 break;
00774                         default: return(-1);
00775                   }
00776                 sbx_norm(sb,0);
00777                 return(0);
00778           }
00779         if((sm = sd->sdmem) == 0)
00780                 return(sbx_err(-1,"SDMEM 0"));
00781         moff = sb->sbiop - sm->smaddr;  /* Get cur smblk offset */
00782         if(sb->sbflags&SB_WRIT)         /* Update since moving out */
00783           {     sm->smuse = moff;
00784                 sb->sbflags &= ~SB_WRIT;
00785           }
00786         sb->sbwleft = 0;                /* Always gets zapped */
00787         switch(flg)
00788           {     case 0:         /* Offset from beginning */
00789                         coff -= sb->sbdot + (chroff)moff; /* Make rel */
00790 
00791                 case 1:         /* Offset from current loc */
00792                         break;
00793 
00794                 case 2:         /* Offset from end */
00795                         coff += sb_ztell(sb);
00796                         break;
00797                 default: return(-1);
00798           }
00799 
00800         /* COFF now has relative offset from current location */
00801         if (-(chroff)moff <= coff && coff <= sb->sbrleft)
00802           {                             /* Win! Handle repos-within-smblk */
00803                 sb->sbiop += coff;
00804                 sb->sbrleft -= coff;    /* Set r; wleft already 0 */
00805                 return(0);
00806           }
00807 
00808         /* Come here when moving to a different sdblk. */
00809         sb->sbrleft = 0;
00810         sb->sbiop = 0;
00811         sb->sboff = coff + (chroff)moff;
00812         sbx_norm(sb,0);
00813         return(0);
00814 }
00815 
00816 /* SB_REWIND(sb) - Go to beginning of sbbuffer.
00817  *      Much faster than using sb_seek.  Note that this leaves the sbbuffer
00818  *      in an open/idle state which is maximally easy to compact.
00819  */
00820 sb_rewind(sbp)
00821 SBBUF *sbp;
00822 {       register SBBUF *sb;
00823         register struct sdblk *sd;
00824 
00825         if((sb = sbp)==0) return;
00826         sbx_smdisc(sb);                 /* Ensure I/O disconnected */
00827         (sd = sb->sbcur)->sdflags &= ~SD_LOCK;  /* Unlock current blk */
00828         sd = sbx_beg(sd);               /* Move to beg of sbstring */
00829         /* Need not lock - see sb_open comments, also sb_close */
00830         /*      sd->sdflags |= SD_LOCK; */      /* Lock onto this one */
00831         sb->sbcur = sd;
00832         sb->sbdot = 0;
00833         sb->sboff = 0;
00834 }
00835 
00836 /* SB_TELL(sb) - Get I/O ptr value for SBBUF.
00837  *      Returns -1 on errors.
00838  */
00839 
00840 chroff
00841 sb_tell(sbp)
00842 SBBUF *sbp;
00843 {       register SBBUF *sb;
00844         register struct smblk *sm;
00845         register struct sdblk *sd;
00846 
00847         if((sd = (sb=sbp)->sbcur) == 0)
00848                 return((chroff)-1);
00849         if(sb->sbiop == 0)
00850                 return(sb->sbdot + sb->sboff);
00851         if((sm = sd->sdmem) == 0)
00852                 return(sbx_err(0,"SDMEM 0"));
00853         return(sb->sbdot + (unsigned)(sb->sbiop - sm->smaddr));
00854 }
00855 
00856 /* SB_ZTELL(sb) - Get I/O ptr relative to "Z" (EOF).
00857  *      Returns # chars from current location to EOF; 0 if any errors.
00858  */
00859 chroff
00860 sb_ztell(sbp)
00861 SBBUF *sbp;
00862 {       register SBBUF *sb;
00863         register struct smblk *sm;
00864         register struct sdblk *sd;
00865 
00866         if((sd = (sb=sbp)->sbcur) == 0)
00867                 return((chroff)0);
00868         if(sb->sbiop && (sm = sd->sdmem))
00869           {     if(sb->sbflags&SB_WRIT)         /* If actively writing, */
00870                         return(sbs_len(sd->slforw));    /* ignore this blk. */
00871                         /* Note that previous code makes it unnecessary
00872                          * to invoke sbx_smdisc.  (otherwise wrong
00873                          * smuse would confuse sbs_len).
00874                          */
00875                 return(sbs_len(sd) - (sb->sbiop - sm->smaddr));
00876           }
00877         else
00878                 return(sbs_len(sd) - sb->sboff);
00879 }
00880 
00881 /* Code past this point should insofar as possible be INTERNAL. */
00882 
00883 /* SBX_READY(sb,type,cmin,cmax) - Set up SBBUF for reading or writing.
00884  *
00885  * If no current smblk:
00886  *      reading - set up for reading
00887  *      writing - set up for splitting?
00888  * If current smblk:
00889  *      reading - if can read, OK.  Else position at beg of next sdblk
00890  *      writing - if can write, OK.  Else position at end of prev sdblk,
00891  *              or set up for splitting?
00892  * Types:
00893  *      0 - Read forward (BOB)
00894  *      1 - Read backward (EOB)
00895  *      3 - Write (insert forward) (EOB)
00896  *      4 - Delete forward (return SD, force BOB-aligned)
00897  *      5 - Delete backward (return SD, force EOB-aligned)
00898  * Connected SD is always locked.
00899  * Returns 0 if error, -1 if EOF-type error, 1 for success.
00900  *
00901  * For types 0,1:
00902  *      CMIN,CMAX represent max # chars to read in to left and right of
00903  *              I/O ptr (prev and post).  Actual amount read in may be
00904  *              much less, but will never be zero.
00905  *      Successful return guarantees that SBIOP etc. are ready.
00906  * For type 3:
00907  *      If new block is allocated, CMIN and CMAX represent min, max sizes
00908  *              of the block.
00909  *      Successful return guarantees that SBIOP etc. are ready, but
00910  *      NOTE that SB_WRIT and SD_MOD are not set!  If not going to use
00911  *      for writing, be sure to clear sbwleft on return!
00912  * For types 4,5:
00913  *      CMIN, CMAX are ignored.
00914  *      SBIOP is always cleared.  SBOFF is guaranteed to be 0 for
00915  *      type 4, SMUSE for type 5.
00916  *      Return value is a SD ptr; 0 indicates error.  -1 isn't used.
00917  */
00918 
00919 struct sdblk *
00920 sbx_ready(sbp,type,cmin,cmax)
00921 SBBUF *sbp;
00922 int type;
00923 SBMO cmin,cmax;
00924 {       register SBBUF *sb;
00925         register struct sdblk *sd;
00926         register struct smblk *sm;
00927         int cnt, slop, rem;
00928         SBMO moff;
00929 
00930         if((sd = (sb=sbp)->sbcur) == 0)
00931                 return(0);
00932         if(sb->sbiop)           /* Canonicalize for given operation */
00933           {     if((sm = sd->sdmem)==0)
00934                         return(0);
00935                 moff = sb->sbiop - sm->smaddr;  /* Current block offset */
00936           switch(type)
00937           {
00938         case SK_READF:          /* Read Forward */
00939                 if(sb->sbrleft > 0)     /* Already set up? */
00940                         return(1);      /* Yup, fast return */
00941                 sbx_smdisc(sb);         /* None left, disc to get next */
00942                 if((sd = sbx_next(sb)) == 0)    /* Try to get next blk */
00943                         return(-1);     /* At EOF */
00944                 break;
00945 
00946         case SK_READB:          /* Read Backward */
00947                 if(moff)                /* Stuff there to read? */
00948                   {     if(sb->sbflags&SB_WRIT) /* Yup, turn writes off */
00949                           {     sm->smuse = moff;
00950                                 sb->sbflags &= ~SB_WRIT;
00951                           }
00952                         sb->sbwleft = 0;
00953                         return(1);
00954                   }
00955                 sbx_smdisc(sb);
00956                 break;
00957 
00958         case SK_WRITEF:         /* Writing */
00959                 if(sb->sbrleft <= 0)
00960                         sb->sbwleft = sm->smlen - moff;
00961                 if(sb->sbwleft > 0)
00962                         return(1);      /* OK to write now */
00963                                         /* NOTE: flags not set!!! */
00964                 sbx_smdisc(sb);
00965                 break;
00966 
00967         case SK_DELF:           /* Delete forward - force BOB */
00968                 if(sb->sbrleft <= 0)            /* At end of blk? */
00969                   {     sbx_smdisc(sb);         /* Win, unhook */
00970                         return(sbx_next(sb));   /* Return next or 0 if EOF */
00971                   }
00972                 sbx_smdisc(sb);                 /* Not at end, but see if */
00973                 if(moff == 0)                   /* at beg of blk? */
00974                         return(sd);     /* Fast win! */
00975                 break;
00976 
00977         case SK_DELB:           /* Delete backward - force EOB */
00978                 if(sb->sbrleft <= 0)            /* Win if already EOB */
00979                   {     sbx_smdisc(sb);
00980                         return(sd);
00981                   }
00982                 sbx_smdisc(sb);
00983                 break;
00984 
00985         default:
00986                 return(0);
00987           }
00988           }
00989 
00990         /* Schnarf in the text, or whatever.
00991          * SD points to current sdblk (must be SD_LOCKed)
00992          * SBDOT must have correct value for this SD
00993          * SBOFF has offset from there to put I/O ptr at.
00994          *
00995          * After normalization, SBOFF is guaranteed to point within
00996          * the SD.  Other guarantees apply to boundary cases, depending
00997          * on the mode (type) bits.
00998          */
00999         sd = sbx_norm(sb,type); /* Normalize I/O pos appropriately */
01000         sm = sd->sdmem;
01001         switch(type)
01002           {
01003         case SK_READB:          /* Read Backward */
01004                 if(sb->sboff == 0)      /* Due to normalize, if 0 seen */
01005                         return(-1);     /* then we know it's BOF */
01006                 if(sm) goto sekr2;
01007                 else goto sekr1;
01008 
01009         case SK_READF:          /* Read Forward */
01010                 if(sm) goto sekr2;
01011                 if(sb->sboff == sd->sdlen)      /* Normalize means if EOB */
01012                         return(-1);             /* then at EOF. */
01013         sekr1:  slop = SB_SLOP;
01014         sekr3:  if(sb->sboff > cmin+slop)       /* Too much leading text? */
01015                   {                             /* Split off leading txt */
01016                         sbx_split(sd,(chroff)(sb->sboff - cmin));
01017                         sd = sbx_next(sb);      /* Point to next sdblk */
01018                         sb->sboff = cmin;       /* Set correct offset */
01019                                                 /* (sbx_next assumes 0) */
01020                   }
01021                 if(sd->sdlen > sb->sboff+cmax+slop) /* Too much trailing txt? */
01022                         sbx_split(sd,(chroff)(sb->sboff+cmax));
01023 
01024                 /* ----- Try to get mem blk to read stuff into ----- */
01025                 /* Note alignment hack for extra efficiency.  This ensures
01026                  * that all reads from disk to memory are made with the same
01027                  * source and destination word alignment, so the system kernel
01028                  * only needs byte-moves for the first or last bytes; all
01029                  * others can be word-moves.
01030                  * This works because sbx_mget always returns word-aligned
01031                  * storage, and we use sbx_msplit to trim off the right number
01032                  * of bytes from the start.
01033                  */
01034                 cnt = sd->sdlen;                /* Get # bytes we'd like */
01035                 if(rem = rndrem(sd->sdaddr))    /* If disk not word-aligned */
01036                         cnt += rem;             /* allow extra for aligning.*/
01037                 if(sm == 0)                     /* Always true 1st time */
01038                   {     sm = sbx_mget(SB_SLOP,cnt); /* Get room (may GC!)*/
01039                         if(sm->smlen < cnt)     /* Got what we wanted? */
01040                           {     slop = 0;       /* NO!!  Impose stricter */
01041                                 cmin = 0;       /* limits.  Allow for new */
01042                                 cmax = sm->smlen - (WDSIZE-1); /* rem. */
01043                                 if(type == SK_READB)
01044                                   {     cmin = cmax; cmax = 0; }
01045                                 goto sekr3;     /* Go try again, sigh. */
01046                           }
01047                   }
01048                 else if(sm->smlen < cnt)        /* 2nd time shd always win */
01049                   {     sbx_err(0,"Readin blksiz err"); /* Internal error, */
01050                         if((cmax /= 2) > 0) goto sekr3; /* w/crude recovery */
01051                         return(0);
01052                   }
01053                 if(rem)         /* If disk not word-aligned, hack stuff */
01054                   {     sm = sbx_msplit(sm, (SBMO)rem); /* Trim off from beg*/
01055                         sbm_mfree(sm->smback);          /* Free the excess */
01056                   }
01057                 sd->sdmem = sm;
01058                 sm->smuse = sd->sdlen;
01059 
01060                 if(sd->sdfile == 0)
01061                         return(sbx_err(0,"No file"));   /* Gasp? */
01062                 if(!sbx_rdf(sd->sdfile->sffd, sm->smaddr, sm->smuse,
01063                                 1, sd->sdaddr))
01064                         return(sbx_err(0,"Readin SD: %o", sd));
01065                 /* ------- */
01066 
01067         sekr2:  sbx_sbrdy(sb);          /* Make it current, pt to beg */
01068                 sb->sbwleft = 0;        /* Ensure not set (esp if READB) */
01069                 break;
01070 
01071         case SK_WRITEF:         /* Write-type seek */
01072                 if(sm == 0)
01073                   {     /* Block is on disk, so always split (avoid readin) */
01074                         if(sd->sdlen)                   /* May be empty */
01075                           {     sbx_split(sd, sb->sboff); /* Split at IO ptr */
01076                                 sd = sbx_next(sb);      /* Move to 2nd part */
01077                                 if(sd->sdlen)           /* If stuff there, */
01078                                                         /* split it again. */
01079                                         sbx_split(sd, (chroff) 0);
01080                           }
01081                         goto sekwget;
01082                   }
01083 
01084                 /* Block in memory */
01085                 moff = sm->smuse;
01086                 if(sb->sboff == moff)           /* At end of the block? */
01087                   {     if(sm->smlen > moff)    /* Yes, have room? */
01088                                 goto sekw;      /* Win, go setup and ret */
01089                         if(sm->smforw                   /* If next mem blk */
01090                           && (sm->smforw->smflags       /* Can have bytes */
01091                                 & (SM_USE|SM_NXM))==0   /* stolen from it */
01092                           && (sd->sdflags&SD_MOD)       /* and we ain't pure*/
01093                           && sm->smlen < cmax)          /* and not too big */
01094                           {     /* Then steal some core!!  Note that without
01095                                  * the size test, a stream of putc's could
01096                                  * create a monster block gobbling all mem.
01097                                  */
01098                                 cmin = cmax - sm->smlen;
01099                                 if(cmin&01) cmin++;     /* Ensure wd-align */
01100                                 if(sm->smforw->smlen <= cmin)
01101                                   {     sbm_mmrg(sm);
01102                                         goto sekw;
01103                                   }
01104                                 sm->smforw->smlen -= cmin;
01105                                 sm->smforw->smaddr += cmin;
01106                                 sm->smlen += cmin;
01107                                 goto sekw;
01108                           }
01109                         /* Last try... check next logical blk for room */
01110                         if(sd->slforw && (sm = sd->slforw->sdmem)
01111                           && sm->smuse == 0
01112                           && sm->smlen)
01113                           {     sd = sbx_next(sb);      /* Yup, go there */
01114                                 goto sekw;
01115                           }
01116                   }
01117 
01118                 /* Middle of block, split up to insert */
01119                 sbx_split(sd, sb->sboff);       /* Split at IO ptr */
01120                 if(sd->sdmem)                   /* Unless blk now empty, */
01121                   {     sd = sbx_next(sb);      /* move to next. */
01122                         if(sd->sdmem)           /* If not empty either */
01123                           sbx_split(sd, (chroff) 0);    /* Split again */
01124                   }
01125 
01126                 /* Have empty SD block, get some mem for it */
01127   sekwget:      sd->sdmem = sm = sbx_mget(cmin,cmax);
01128                 sm->smuse = 0;
01129    sekw:        sbx_sbrdy(sb);          /* Sets up sbwleft... */
01130                 return(1);
01131 
01132         case SK_DELF:           /* Delete forward */
01133                 if(sb->sboff == 0)      /* At block beg already? */
01134                         return(sd);     /* Win, return it */
01135                 sbx_split(sd, sb->sboff);       /* No, split up and */
01136                 return(sbx_next(sb));   /* return ptr to 2nd part */
01137 
01138         case SK_DELB:           /* Delete backward (force EOB align) */
01139                 if(sb->sboff !=         /* If not at EOB already, */
01140                   (sm ? (chroff)(sm->smuse) : sd->sdlen))
01141                         sbx_split(sd, sb->sboff);       /* Then split */
01142                 return(sd);     /* And return ptr to 1st part */
01143                 break;
01144 
01145         default:
01146                 return(0);
01147           }     /* End of switch */
01148         return(1);
01149 }
01150 
01151 struct sdblk *
01152 sbx_next (sbp)
01153 SBBUF *sbp;
01154 {       register SBBUF *sb;
01155         register struct sdblk *sd, *sdf;
01156         if((sdf = (sd = (sb=sbp)->sbcur)->slforw) == 0)
01157                 return((struct sdblk *)0);
01158         sb->sbdot += (sd->sdmem ? (chroff)sd->sdmem->smuse : sd->sdlen);
01159         sb->sboff = 0;
01160         sd->sdflags &= ~SD_LOCK;        /* Unlock current */
01161         sdf->sdflags |= SD_LOCK;        /* Lock next */
01162         sb->sbcur = sdf;
01163         return(sdf);
01164 }
01165 
01166 /* SBX_NORM(sb,mode) - Normalizes I/O position as desired.
01167  *      The SBBUF must have I/O disconnected (SBIOP==0).
01168  *      Adjusts SBCUR, SBDOT, and SBOFF so that SBOFF is guaranteed
01169  *      to point to a location in the current SD block.
01170  *      The mode flags determine action when there is more than
01171  *      one possible SD that could be pointed to, as is the case
01172  *      when the I/O pos falls on a block boundary (possibly with
01173  *      adjacent zero-length blocks as well).
01174  *      SKM_0BACK - Zero-skip direction.
01175  *                0 = Skip forward over zero-length blocks.
01176  *              set = Skip backward over zero-length blocks.
01177  *      SKM_EOB   - Block-end selection (applies after skipping done).
01178  *                0 = Point to BOB (Beginning Of Block).
01179  *              set = Point to EOB (End Of Block).
01180  * Returns the new current SD as a convenience.
01181  * Notes:
01182  *      The SKM_0BACK flag value is a special hack to search in
01183  *              the right direction when SBOFF is initially 0.
01184  *      None of the mode flags have any effect if the I/O pos falls
01185  *              within a block.
01186  *      Perhaps this routine should flush the zero-length blks it
01187  *              finds, if they're not locked??
01188  */
01189 struct sdblk *
01190 sbx_norm(sbp,mode)
01191 SBBUF *sbp;
01192 int mode;
01193 {       register struct sdblk *sd;
01194         register struct smblk *sm;
01195         register SBBUF *sb;
01196         chroff len;
01197 
01198         if((sd = (sb=sbp)->sbcur) == 0)
01199           {     sb->sbdot = 0;
01200                 sb->sboff = 0;
01201                 return(sd);
01202           }
01203         sd->sdflags &= ~SD_LOCK;        /* Unlock current blk */
01204 
01205         if(sb->sboff >= (mode&01))      /* Hack hack to get right skip */
01206           for(;;)                       /* Scan forwards */
01207           {     if(sm = sd->sdmem)              /* Get length of this blk */
01208                         len = sm->smuse;
01209                 else len = sd->sdlen;
01210                 if(sb->sboff <= len)
01211                   if(sb->sboff < len    /* If == and fwd 0-skip, continue */
01212                   || (mode&SKM_0BACK))
01213                     {   if((mode&SKM_EOB)       /* Done, adjust to EOB? */
01214                           && sb->sboff == 0     /* Yes, are we at BOB? */
01215                           && sd->slback)        /* and can do it? */
01216                           {     sd = sd->slback;        /* Move to EOB */
01217                                 sb->sboff = (sm = sd->sdmem) 
01218                                         ? (chroff)(sm->smuse) : sd->sdlen;
01219                                 sb->sbdot -= sb->sboff;
01220                           }
01221                         break;
01222                     }
01223                 if(sd->slforw == 0)     /* At EOF? */
01224                   {     sb->sboff = len;
01225                         break;
01226                   }
01227                 sd = sd->slforw;
01228                 sb->sboff -= len;
01229                 sb->sbdot += len;
01230           }
01231         else                            /* Scan backwards */
01232          for(;;)
01233           {     if(sd->slback == 0)     /* At BOF? */
01234                   {     sb->sboff = 0;
01235                         sb->sbdot = 0;  /* Should already be 0, but... */
01236                         break;
01237                   }
01238                 sd = sd->slback;
01239                 if(sm = sd->sdmem)              /* Get length of this blk */
01240                         len = sm->smuse;
01241                 else len = sd->sdlen;
01242                 sb->sbdot -= len;
01243                 if((sb->sboff += len) >= 0)
01244                   if(sb->sboff > 0      /* If == 0 and bkwd 0-skip, continue */
01245                     || !(mode&SKM_0BACK))
01246                     {   if((mode&SKM_EOB) == 0  /* Done, adjust to BOB? */
01247                           && sb->sboff == len   /* Yes, are we at EOB? */
01248                           && sd->slforw)        /* and can do it? */
01249                           {     sd = sd->slforw;        /* Move to BOB */
01250                                 sb->sboff = 0;
01251                                 sb->sbdot += len;
01252                           }
01253                         break;
01254                     }
01255           }
01256         sb->sbcur = sd;
01257         sd->sdflags |= SD_LOCK;
01258         return(sd);
01259 }
01260 
01261 
01262 struct sdblk *
01263 sbx_beg(sdp)
01264 struct sdblk *sdp;
01265 {       register struct sdblk *sd, *sdx;
01266         if(sd = sdp)
01267                 while(sdx = sd->slback)
01268                         sd = sdx;
01269         return(sd);
01270 }
01271 
01272 
01273 sbx_smdisc(sbp)
01274 SBBUF *sbp;
01275 {       register SBBUF *sb;
01276         register struct smblk *sm;
01277         register struct sdblk *sd;
01278 
01279         sb = sbp;
01280         if((sd = sb->sbcur) == 0
01281           || (sm = sd->sdmem) == 0)
01282                 return;
01283         if(sb->sbflags&SB_WRIT)
01284           {     sm->smuse = sb->sbiop - sm->smaddr;
01285                 sb->sbflags &= ~SB_WRIT;
01286           }
01287         sb->sboff = sb->sbiop - sm->smaddr;
01288         sb->sbiop = 0;
01289         sb->sbrleft = sb->sbwleft = 0;
01290 }
01291 
01292 sbx_sbrdy(sbp)          /* Sets up SBIOP, SBRLEFT, SBWLEFT */
01293 SBBUF *sbp;
01294 {       register SBBUF *sb;
01295         register struct sdblk *sd;
01296         register struct smblk *sm;
01297 
01298         if((sd = (sb=sbp)->sbcur) == 0
01299           || (sm = sd->sdmem) == 0)
01300                 return;
01301         sd->sdflags |= SD_LOCK;
01302         sb->sbiop = sm->smaddr + sb->sboff;
01303         if(sb->sbrleft = sm->smuse - sb->sboff)
01304                 sb->sbwleft = 0;
01305         else sb->sbwleft = sm->smlen - sm->smuse;
01306 }
01307 
01308 
01309 /* SBX_SCPY(sd,sdl) - Copies given sbstring, returns ptr to new sbstring.
01310  *      Only goes as far as sdl (last copied blk); 0 for entire sbstring.
01311  */
01312 struct sdblk *
01313 sbx_scpy(sdp,sdlast)
01314 struct sdblk *sdp, *sdlast;
01315 {       register struct sdblk *sd, *sd2, *sdn;
01316         struct sdblk *sdr;
01317 
01318         if((sd = sdp) == 0) return((struct sdblk *)0);
01319         sdn = 0;
01320         do {
01321                 sd->sdflags |= SD_LCK2;
01322                 sd2 = sbx_sdcpy(sd);
01323                 if(sd2->slback = sdn)
01324                   {     sdn->slforw = sd2;
01325                         sdn->sdflags &= ~SD_LOCKS;
01326                   }
01327                 else sdr = sd2;         /* Save 1st */
01328                 sdn = sd2;
01329                 sd->sdflags &= ~SD_LCK2;
01330           } while(sd != sdlast && (sd = sd->slforw));
01331         sd2->slforw = 0;
01332         sd2->sdflags &= ~SD_LOCKS;
01333         return(sdr);
01334 }
01335 
01336 
01337 /* SBX_SDCPY(sd) - Copies given sdblk, returns ptr to new blk.
01338  *      Does not set locks, assumes caller does this (which it MUST,
01339  *      to avoid compaction lossage!)
01340  */
01341 
01342 struct sdblk *
01343 sbx_sdcpy(sdp)
01344 struct sdblk *sdp;
01345 {       register struct sdblk *sd, *sd2;
01346         register struct smblk *sm, *sm2;
01347 
01348         if((sd = sdp) == 0) return((struct sdblk *)0);
01349         sd2 = sbx_ndget();              /* Get a free sdblk */
01350         bcopy((SBMA)sd, (SBMA)sd2, sizeof(struct sdblk));       /* Copy sdblk data */
01351         sd2->slforw = 0;                /* Don't let it think it's on a list */
01352         sd2->slback = 0;
01353         if(sd2->sdfile)                 /* If has disk copy, */
01354           {     sd->sdforw = sd2;       /* Fix phys list ptrs */
01355                 sd2->sdback = sd;
01356                 if(sd2->sdforw)
01357                         sd2->sdforw->sdback = sd2;
01358           }
01359         if(sm = sd2->sdmem)             /* If has in-core copy, try to */
01360           {     if(sm2 = sbm_mget(sm->smuse,sm->smuse)) /* get mem for it */
01361                   {     bcopy(sm->smaddr,sm2->smaddr,sm->smuse);
01362                         sm2->smuse = sm->smuse;
01363                         sd2->sdmem = sm2;       /* Point new sd to copy */
01364                   }
01365                 else                            /* Can't get mem... */
01366                   {     if(sd2->sdflags&SD_MOD)
01367                                 sbx_aout(sd2,1);        /* Swap out the blk */
01368                         sd2->sdmem = 0;         /* Don't have incore copy */
01369                   }
01370           }
01371         return(sd2);
01372 }
01373 
01374 /* SBX_XCIS(sbp,coff,&sdp2,adot) - Internal routine to excise a sbstring,
01375  *      defined as everything between current location and given offset.
01376  *      SD to first sdblk is returned (0 if error)
01377  *      SD2 (address passed as 3rd arg) is set to last sdblk.
01378  *      Both are locked with LCK2 to ensure that pointers are valid.
01379  *      The current location at time of call is also returned via adot.
01380  */
01381 struct sdblk *
01382 sbx_xcis(sbp,num,asd2,adot)
01383 SBBUF *sbp;
01384 chroff num, *adot;
01385 struct sdblk **asd2;
01386 {       register SBBUF *sb;
01387         register struct sdblk *sd, *sd2;
01388         int dirb;
01389 
01390         if((sb = sbp) == 0) return((struct sdblk *)0);
01391         dirb = 0;               /* Delete forward */
01392         if(num == 0) return((struct sdblk *)0); /* Delete nothing */
01393         if(num < 0) dirb++;     /* Delete backward */
01394 
01395         if((sd = (struct sdblk *)
01396                         sbx_ready(sb, (dirb ? SK_DELB : SK_DELF))) == 0)
01397                 return((struct sdblk *)0);              /* Maybe nothing there */
01398         sd->sdflags |= SD_LCK2;         /* Lock up returned SD */
01399         *adot = sb->sbdot;              /* Save current location */
01400         sb->sboff += num;               /* Move to other end of range */
01401 
01402         if((sd2 = (struct sdblk *)
01403                         sbx_ready(sb,(dirb ? SK_DELF : SK_DELB))) == 0)
01404           {     sd->sdflags &= ~SD_LCK2;        /* This shd never happen if */
01405                 return(                         /* we got this far, but...  */
01406                   (struct sdblk *)sbx_err(0,"KILLN SD2 failed"));
01407           }
01408         sd2->sdflags |= SD_LCK2;        /* Lock up other end of stuff */
01409 
01410         /* SD and SD2 now delimit bounds of stuff to excise.
01411          * Now do direction dependent fixups
01412          */
01413         if(dirb)
01414           {     /* Backward, current sbdot is ok but must get SD/SD2
01415                  * into first/last order.  Also, due to nature of block
01416                  * splitups, a backward delete within single block will leave
01417                  * SD actually pointing at predecessor block.
01418                  */
01419                 if(sd->slforw == sd2)   /* If SD became pred, fix things. */
01420                   {     sd->sdflags &= ~SD_LCK2;        /* Oops, unlock! */
01421                         sd = sd2;
01422                   }
01423                 else    /* Just need to swap SD, SD2 ptrs. */
01424                   {     /* Goddamit why doesn't C have an */
01425                         /* exchange operator??? */
01426                         *asd2 = sd;
01427                         return(sd2);
01428                   }
01429           }
01430         *asd2 = sd2;
01431         return(sd);
01432 }
01433 
01434 /* SBX_SPLIT(sd,chroff) - Splits block SD at point CHROFF (offset from
01435  *      start of block).  SD remains valid; it is left locked.
01436  *      The smblk is split too, if one exists, and SMUSE adjusted.
01437  *      If offset 0, or equal to block length, the 1st or 2nd SD respectively
01438  *      will not have a smblk and its sdlen will be 0.
01439  *      (Note that if a smblk exists, a zero sdlen doesn't indicate much)
01440  */
01441 struct sdblk *
01442 sbx_split(sdp, coff)
01443 struct sdblk *sdp;
01444 chroff coff;
01445 {       register struct sdblk *sd, *sdf, *sdx;
01446 
01447         if((sd=sdp) == 0)
01448                 return((struct sdblk *)0);
01449         sd->sdflags |= SD_LOCK;
01450         if(sd->sdflags&SD_MOD)          /* If block has been munged, */
01451                 sbx_npdel(sd);          /* Flush from phys list now. */
01452         sdf = sbx_ndget();              /* Get a sdblk node */
01453         bcopy((SBMA)sd, (SBMA)sdf, (sizeof (struct sdblk)));    /* Copy node */
01454         /* Note that the flags are copied, so both sdblks are locked and
01455          * safe from possible GC compaction during call to sbx_msplit...
01456          */
01457         if(coff == 0)                   /* If offset was 0, */
01458           {                             /* then 1st SD becomes null */
01459                 if(sdf->sdfile)         /* Fix up phys links here */
01460                   {     if(sdx = sdf->sdback)
01461                                 sdx->sdforw = sdf;
01462                         else sdf->sdfile->sfptr1 = sdf;
01463                         if(sdx = sdf->sdforw)
01464                                 sdx->sdback = sdf;
01465                   }
01466                 sdx = sd;
01467                 goto nulsdx;
01468           }
01469         else if(sd->sdmem)
01470                 if(coff >= sd->sdmem->smuse)
01471                         goto nulsdf;
01472                 else sdf->sdmem = sbx_msplit(sd->sdmem, (SBMO)coff);
01473         else if(coff >= sd->sdlen)
01474 nulsdf:   {     sdx = sdf;
01475 nulsdx:         sdx->sdforw = 0;
01476                 sdx->sdback = 0;
01477                 sdx->sdmem = 0;
01478                 sdx->sdfile = 0;
01479                 sdx->sdlen = 0;
01480                 sdx->sdaddr = 0;
01481                 goto nulskp;
01482           }
01483         if(sd->sdfile)
01484           {     sdf->sdlen -= coff;             /* Set size of remainder */
01485                 sdf->sdaddr += coff;            /* and address */
01486                 sd->sdlen = coff;               /* Set size of 1st part */
01487 
01488         /* Link 2nd block into proper place in physical sequence.
01489          * 1st block is already in right place.  Search forward until
01490          * find a block with same or higher disk address, and insert
01491          * in front of it.  If sdlen is zero, just flush the links,
01492          * which is OK since the 1st block is what's pointed to anyway.
01493          */
01494                 if(sdf->sdlen > 0)
01495                   {     while((sdx = sd->sdforw) /* Find place to insert */
01496                           && sdf->sdaddr > sdx->sdaddr)
01497                                 sd = sdx;
01498                         sdf->sdback = sd;       /* Link following sd. */
01499                         if(sdf->sdforw = sd->sdforw)
01500                                 sdf->sdforw->sdback = sdf;
01501                         sd->sdforw = sdf;
01502                         sd = sdp;               /* Restore pointer */
01503                   }
01504                 else
01505                   {     sdf->sdforw = 0;
01506                         sdf->sdback = 0;
01507                         sdf->sdfile = 0;        /* Say no disk */
01508                   }
01509           }
01510 
01511 nulskp: sdf->slback = sd;               /* Link in logical sequence */
01512         if(sd->slforw)
01513                 sd->slforw->slback = sdf;
01514         sd->slforw = sdf;
01515 
01516         sdf->sdflags &= ~SD_LOCKS;      /* Unlock 2nd but not 1st */
01517         return(sd);                     /* Note sd, not sdf */
01518 }
01519 
01520 /* SBX_MSPLIT - Like sbm_split but never fails, and sets
01521  *      SMUSE values appropriately
01522  */
01523 struct smblk *
01524 sbx_msplit(smp, size)
01525 struct smblk *smp;
01526 SBMO size;
01527 {       register struct smblk *sm, *smx;
01528         register int lev;
01529 
01530         lev = 0;
01531         while((smx = sbm_split((sm = smp), size)) == 0)
01532                 sbx_comp(SB_BUFSIZ,lev++); /* Need to get some smblk nodes */
01533         if(sm->smlen >= sm->smuse)      /* Split across used portion? */
01534                 smx->smuse = 0;         /* Nope, new blk is all free */
01535         else
01536           {     smx->smuse = sm->smuse - sm->smlen;
01537                 sm->smuse = sm->smlen;
01538           }
01539         return(smx);
01540 }
01541 
01542 /* SBX_NDEL - flush a SD and associated SM.  Fixes up logical
01543  * and physical links properly.  Returns ptr to next logical SD.
01544  * NOTE: if sd->slback does not exist, the returned SD is your
01545  * only hold on the list, since the SD gets flushed anyway!
01546  */
01547 struct sdblk *
01548 sbx_ndel(sdp)
01549 struct sdblk *sdp;
01550 {       register struct sdblk *sd, *sdx;
01551         register struct smblk *sm;
01552 
01553         sd = sdp;
01554         if(sm = sd->sdmem)              /* If smblk exists, */
01555           {     sbm_mfree(sm);          /* flush it. */
01556                 sd->sdmem = 0;
01557           }
01558         if(sdx = sd->slback)
01559                 sdx->slforw = sd->slforw;
01560         if(sd->slforw)
01561                 sd->slforw->slback = sdx;       /* May be zero */
01562 
01563         /* Logical links done, now hack phys links */
01564         if(sd->sdfile)                  /* Have phys links? */
01565                 sbx_npdel(sd);          /* Yes, flush from phys list */
01566 
01567         sdx = sd->slforw;
01568         sbx_ndfre(sd);
01569         return(sdx);
01570 }
01571 
01572 sbx_npdel(sdp)
01573 struct sdblk *sdp;
01574 {       register struct sdblk *sd, *sdx;
01575         register struct sbfile *sf;
01576 
01577         if((sf = (sd=sdp)->sdfile) == 0)
01578                 return;
01579         if(sdx = sd->sdback)    /* Start of disk file? */
01580                 sdx->sdforw = sd->sdforw;
01581         else
01582                 sf->sfptr1 = sd->sdforw;
01583         if(sdx = sd->sdforw)
01584                 sdx->sdback = sd->sdback;
01585         sd->sdfile = 0;
01586         sd->sdlen = 0;
01587 }
01588 
01589 
01590 struct sdblk *sbx_nfl;  /* Pointer to sdblk node freelist */
01591 
01592 struct sdblk *
01593 sbx_ndget()             /* Like sbm_nget but never fails! */
01594 {       register struct sdblk *sd;
01595         register int lev;
01596 
01597         lev = 0;
01598         while((sd = sbx_nfl) == 0               /* Get a node */
01599                                                 /* If fail, make more */
01600                 && (sd = sbm_nmak((sizeof (struct sdblk)),SM_DNODS)) == 0)
01601                                                 /* If still fail, try GC */
01602                         sbx_comp(sizeof(struct sdblk)*SM_DNODS,lev++);
01603 
01604         sbx_nfl = sd->slforw;           /* Take it off freelist */
01605         sd->sdflags = SD_NID;
01606         return(sd);                     /* Return ptr to it */
01607 }
01608 
01609 sbx_ndfre(sdp)
01610 struct sdblk *sdp;
01611 {       register struct sdblk *sd;
01612         (sd = sdp)->sdflags = 0;
01613         sd->slforw = sbx_nfl;
01614         sbx_nfl = sd;
01615 }
01616 
01617 SBMA
01618 sbx_malloc(size)
01619 unsigned size;
01620 {
01621         register int lev;
01622         register SBMA res;
01623 
01624         lev = 0;
01625         while((res = (SBMA)malloc(size)) == 0)
01626                 sbx_comp(size,lev++);
01627         return(res);
01628 }
01629 
01630 struct smblk *
01631 sbx_mget(cmin,cmax)     /* like sbm_mget but never fails! */
01632 SBMO cmin, cmax;
01633 {       register struct smblk *sm;
01634         register int lev, csiz;
01635 
01636         lev = 0;
01637         csiz = cmax;
01638         for(;;)
01639           {     if(sm = sbm_mget(csiz,cmax))
01640                         return(sm);             /* Won right off... */
01641                 sbx_comp(csiz,lev++);           /* Barf, invoke GC */
01642                 if(sm = sbm_mget(csiz,cmax))    /* Then try again */
01643                         return(sm);
01644                 if((csiz >>= 1) < cmin)         /* If still short, reduce */
01645                         csiz = cmin;            /* request down to min */
01646           }
01647 }
01648 
01649 chroff sbv_taddr;       /* Disk addr of place to write into (set by TSET) */
01650 struct sdblk *sbv_tsd;  /* SD that disk addr comes after (set by TSET) */
01651 
01652 #define sbx_qlk(sd)  (sd->sdflags&SD_LOCKS)
01653 
01654 #if 0
01655         This is the compaction routine, which is the key to the
01656 entire scheme.  Paging text to and from disk is trivial, but the
01657 ability to merge blocks is the important thing since it allows
01658 flushing the pointer information as well as the actual text!  This
01659 eliminates fragmentation as a fatal problem.
01660         There are a variety of ways that storage can be reclaimed:
01661 
01662 - "pure" in-core blocks can be flushed instantly.
01663 - "impure" incore blocks can be written to tempfile storage and flushed.
01664 - The SM node freelist can be compacted so as to flush memory which is
01665         used for nothing but holding free nodes.
01666 - The SD node freelist can be compacted, ditto.
01667 - SBBUFs can be compacted, by:
01668         - Merging logically & physically adjacent on-disk pieces
01669         - merging logically & physically adjacent in-core pieces
01670         - merging logically adjacent in-core pieces
01671         - merging logically adjacent disk pieces, by reading in
01672                 and then writing out to tempfile storage.
01673                 Worst case would reduce whole sbstr to single tempfile block.
01674 
01675 Problems:
01676         What is "optimal" algorithm for typical usage?
01677         Must go over all code to make sure right things get locked
01678                 and unlocked to avoid having rug pulled out from under.
01679         Could have optional "registration table" for sbstruc; if exist
01680                 in table, can check during GC.  If find one, can first
01681                 do sbx_smdisc and then repoint sbcur to 1st block,
01682                 with sbdot of 0 and sboff of sb_tell().  This allows
01683                 reducing whole thing to one block even tho "locked".
01684                 Never touch stuff locked with SD_LCK2, though.
01685                 Also may need way to protect the sbstr SD actually being
01686                 pointed to by current sbx routine processing.
01687         Could have count of # nodes free for SM and SD; don''t GC 
01688                 unless # is some number greater than size of a node block!
01689         Have different levels of compaction; pass level # down thru calls
01690                 so as to invoke progressively sterner compaction measures.
01691                 Can invoke sbx_comp with any particular level!
01692         Must have list somewhere of SBBUFs?  or maybe OK to scan core
01693                 for SM_DNODS, then scan sdblks?
01694         Screw: could happen that stuff gets flushed (cuz pure) or even
01695                 written out to tempfile, and then we have to read it back
01696                 in so as to compact more stuff into tempfile... how to avoid?
01697                 If pure stuff small and next to impure stuff, merge?
01698         Some calls just want to get another free node and don''t need
01699                 new core.  How to indicate this?  How to decide between
01700                 freeing up used nodes, and creating new node freelist?
01701 #endif /*COMMENT*/
01702 /* Compact stuff.
01703  * General algorithm for getting storage is:
01704  *      1) allocate from freelist if enough there
01705  *      2) find unlocked pure smblk to free up
01706  *      3) find unlocked impure smblks, write out.
01707  *      4) Compact stuff by reducing # of sdblks.  This is key to scheme!
01708  *              Otherwise fragmentation will kill program.
01709  * Maybe put age cnt in each sbstr?  Bump global and set cntr each time
01710  * sbstr gets major hacking (not just getc/putc).
01711  */
01712 extern struct smblk *sbm_list;
01713 sbx_comp(cmin,lev)
01714 int cmin, lev;
01715 {       int sbx_sdgc();
01716 
01717         if(lev > 100)           /* If program has no way to handle this, */
01718                 abort();        /* then simply blow up. */
01719         if(lev > 10)            /* Too many iterations? Try to warn. */
01720                 return(sbx_err(0,"GC loop, cannot free block of size %d",
01721                                 cmin));
01722 
01723         /* Step thru core hunting for SD node blocks */
01724         sbm_nfor(SM_DNODS,sizeof(struct sdblk),sbx_sdgc,lev);
01725 }
01726 
01727 /* Do GC stuff on a sdblk.  Guaranteed to exist, but may be locked */
01728 sbx_sdgc(sdp,lev)
01729 struct sdblk *sdp;
01730 int lev;
01731 {       register struct sdblk *sd, *sdf;
01732         register struct smblk *sm;
01733         struct smblk *smf, *sbm_exp ();
01734         SBMO more;
01735 
01736         sd = sdp;
01737         if(sbx_qlk(sd)) return(0);
01738         sm = sd->sdmem;
01739         sdf = sd->slforw;
01740         if (lev < 4) goto lev3;
01741 
01742         /* Level 4 - write out everything possible */
01743         /* Back up to start of sbstr */
01744         while((sdf = sd->slback) && !sbx_qlk(sdf))
01745                 sd = sdf;
01746         if((sdf = sd->slforw) == 0      /* If only 1 blk, ensure on disk */
01747           || sbx_qlk(sdf))
01748           {     if(sm = sd->sdmem)
01749                   {     if(sd->sdflags&SD_MOD)          /* If impure, */
01750                                 sbx_aout(sd, 1);        /* swap out the SD */
01751                         sbm_mfree(sm);
01752                         sd->sdmem = 0;
01753                   }
01754                 return(0);
01755           }
01756         /* At least two blocks in string.  Set up for flushout. */
01757         sbx_aout(sd, 0);        /* Swapout as much of sbstring as possible */
01758         return(0);
01759 
01760 lev3:                   /* Level 3 - write out more */
01761 lev2:                   /* Level 2 - write out all impure & small pure */
01762 lev1:   if(lev >= 1)    /* Level 1 - merge small impure & small pure */
01763           {     if(!sm || !sdf) return(0);
01764                 while(((smf = sdf->sdmem) && !(sdf->sdflags&SD_LOCKS)
01765                   && (more = smf->smuse + sm->smuse) < SB_BUFSIZ) )
01766                   {     if(sm->smforw != smf
01767                           && more > sm->smlen)          /* If need more rm */
01768                           {     sm = sbm_exp(sm,more);  /* Get it */
01769                                 if(!sm) return(0);      /* If none, stop */
01770                                 sd->sdmem = sm;
01771                           }
01772                         bcopy(smf->smaddr,
01773                              sm->smaddr + sm->smuse, smf->smuse);
01774                         sm->smuse = more;
01775                         if(sm->smforw == smf)
01776                           {     sdf->sdmem = 0;
01777                                 sbm_mmrg(sm);   /* Merge */
01778                                 if(sm->smlen > more+SB_SLOP)
01779                                         sbm_mfree(sbm_split(sm, more));
01780                                         /* Guaranteed to win since mmrg
01781                                          * just freed a mem node */
01782                           }
01783                         sd->sdflags |= SD_MOD;
01784                         if(sdf = sbx_ndel(sdf))
01785                                 continue;
01786                         return(0);
01787                   }
01788           }
01789 
01790         if(lev <= 0)    /* Level 0 - free up large pure blocks */
01791                         /* Also merge blocks which are adjacent on disk */
01792           {     if(sm)
01793                   {     if(sm->smuse == 0)
01794                                 sd->sdlen = 0;
01795                         else if((sd->sdflags&SD_MOD) == 0
01796                             && sm->smuse > 64)
01797                           {     sbm_mfree(sm);
01798                                 sd->sdmem = 0;
01799                                 goto lev0adj;
01800                           }
01801                         else goto lev0adj;
01802                   }
01803 
01804                 if(sd->sdlen == 0       /* Free zero blocks */
01805                   && sd->slback)        /* Make sure don't lose list */
01806                   {     sbx_ndel(sd);
01807                         if((sd = sdf) == 0)
01808                                 return(0);
01809                         sdf = sd->slforw;
01810                   }
01811         lev0adj:        /* Merge blocks if adjacent on disk */
01812                         /* This is common after reading thru large chunks
01813                         * of a file but not modifying it much.
01814                         */
01815                 if((sd->sdflags&SD_MOD) == 0    /* Pure */
01816                   && sdf && (sdf->sdflags&(SD_LOCKS|SD_MOD)) == 0
01817                   && sd->sdfile && (sd->sdfile == sdf->sdfile)
01818                   && (sd->sdaddr + sd->sdlen) == sdf->sdaddr )
01819                   {     sd->sdlen += sdf->sdlen;
01820                         sbx_ndel(sdf);          /* Flush 2nd */
01821                         if(sm = sd->sdmem)
01822                           {     sbm_mfree(sm);
01823                                 sd->sdmem = 0;
01824                           }
01825                   }
01826                 return(0);
01827           }
01828         return(0);
01829 }
01830 
01831 /* SBX_AOUT - output ALL of a hackable sbstring starting at given sdblk.
01832  *      Note that code is careful to do things so that an abort at any
01833  *      time (e.g. write error) will still leave sbstring in valid state.
01834  * Flag value:
01835  *      0 - Writes out as many unlocked sdblks as possible, and merges
01836  *              so that resulting sdblk (same one pointed to by arg)
01837  *              incorporates all stuff written out.
01838  *      1 - Writes out single sdblk indicated, whether unlocked or not.
01839  *              Doesn't free mem or merge anything; does update physlist
01840  *              and flags.
01841  *      2 - Writes out all sdblks to specified FD/offset, no mods at all,
01842  *              not even to physlist or flags.  Good for saving files
01843  *              when something seems wrong.  (How to pass fd/off args?)
01844  *              (offset arg not implemented, no need yet; 0 assumed)
01845  * Returns 0 if successful, else UNIX system call error number.
01846  */
01847 
01848 sbx_aout(sdp,flag,fd)
01849 struct sdblk *sdp;
01850 int flag, fd;
01851 {       register struct sdblk *sd;
01852         register struct smblk *sm;
01853         register int cnt;
01854         int ifd, ofd, skflg, rem;
01855         chroff inlen;
01856         extern SBMA sbm_lowaddr;        /* Need this from SBM for rndrem */
01857         char buf[SB_BUFSIZ+16]; /* Get buffer space from stack! */
01858                                 /* Allow extra for word-align reads. */
01859                                 /* This should be +WDSIZE, but some */
01860                                 /* C compilers (eg XENIX) can't handle */
01861                                 /* "sizeof" arith in allocation stmts! */
01862 
01863         /* This flag and the two ptrs below are needed because UNIX
01864          * maintains only one I/O ptr per open file, and we can sometimes
01865          * be reading from/writing to the swapout file at same time.
01866          * Using DUP() to get a new FD (to avoid seeking back and forth)
01867          * won't help since both FD's will use the same I/O ptr!!!
01868          * Lastly, can't depend on returned value of LSEEK to push/pop
01869          * ptr, since V6 systems don't implement tell() or lseek() directly.
01870          * So we have to do it by hand...
01871          */
01872         int botchflg;
01873         chroff outptr, inptr;
01874 
01875         if((sd = sdp)==0) return;
01876         ofd = sbv_tf.sffd;              /* Default output FD */
01877         if(flag==0)
01878           {     sbx_tset(sbx_qlen(sd),0);/* Find place for whole string */
01879                 outptr = sbv_taddr;     /* We'll have to update wrt ptr */
01880           }
01881         else if (flag==1)       /* Single SD block, so it's reasonable to 
01882                                  * try aligning the output with the input. */
01883           {     if(sm = sd->sdmem)
01884                   {     cnt = rndrem(sm->smaddr - sbm_lowaddr);
01885                         sbx_tset((chroff)(sm->smuse),cnt);
01886                   }
01887                 else
01888                   {     cnt = rndrem(sd->sdaddr);
01889                         sbx_tset(sd->sdlen, cnt);
01890                   }
01891                 outptr = sbv_taddr;     /* We'll have to update wrt ptr */
01892           }
01893         else            /* Outputting a whole sbstring to a file */
01894           {     ofd = fd;
01895                 outptr = 0;
01896           }
01897 
01898         for(; sd;)
01899           {     if(flag==0 && sbx_qlk(sd))
01900                         break;          /* Stop when hit locked sdblk */
01901                 if(sm = sd->sdmem)
01902                   {     if(cnt = sm->smuse)
01903                                 if(write(ofd, sm->smaddr, cnt) != cnt)
01904                                         return(sbx_err(errno,"Swapout wrt err"));
01905                         outptr += cnt;
01906                         if(flag==0)
01907                           {     sd->sdmem = 0;  /* Flush the mem used */
01908                                 sbm_mfree(sm);
01909                           }
01910                         inlen = cnt;
01911                   }
01912                 else if(inlen = sd->sdlen)
01913                   {     if(sd->sdfile == 0)
01914                                 return(sbx_err(errno,"Sdfile 0, SD %o",sd));
01915                         /* Foo on UNIX */
01916                         botchflg = ((ifd = sd->sdfile->sffd) == ofd) ? 1 : 0;
01917                         skflg = 1;              /* Always seek first time */
01918                         inptr = sd->sdaddr;
01919                         /* Efficiency hack - set up for first read so that
01920                          * transfer is word-aligned and terminates at end
01921                          * of a disk block.
01922                          */
01923                         rem = rndrem(inptr);            /* Get alignment */
01924                         cnt = SB_BUFSIZ - (int)(inptr%SB_BUFSIZ);
01925                         while(inlen > 0)
01926                           {
01927                                 if(inlen < cnt) cnt = inlen;
01928                                 if(!sbx_rdf(ifd, buf+rem, cnt, skflg, inptr))
01929                                         return(sbx_err(errno,"Swapout err, SD %o",sd));
01930                                 /* Further seeks depend on botch setting */
01931                                 if(skflg = botchflg)
01932                                   {     if(lseek(ofd,outptr,0) < 0)
01933                                                 return(sbx_err(errno,
01934                                                         "Swapout sk err"));
01935                                         inptr += cnt;
01936                                   }
01937                                 if(write(ofd, buf+rem, cnt) != cnt)
01938                                         return(sbx_err(errno,
01939                                                 "Swapout wrt err"));
01940                                 outptr += cnt;
01941                                 inlen -= cnt;
01942                                 cnt = SB_BUFSIZ; /* Now can use full blocks */
01943                                 rem = 0;        /* Aligned nicely, too! */
01944                           }
01945                         inlen = sd->sdlen;
01946                   }
01947 
01948                 /* Text written out, now merge block in */
01949                 if(flag == 2)                   /* No merge if saving file */
01950                         goto donxt;
01951                 if(sd != sdp)                   /* First block? */
01952                   {     sdp->sdlen += inlen;    /* No, simple merge */
01953                         sd = sbx_ndel(sd);      /* Flush, get next */
01954                         continue;
01955                   }
01956 
01957                 /* Handle 1st block specially */
01958                 if(sd->sdfile           /* Unlink from phys list */
01959                   && sd != sbv_tsd)     /* Don't unlink if self */
01960                         sbx_npdel(sd);
01961                 sd->sdlen = inlen;
01962                 sd->sdfile = &sbv_tf;
01963                 sd->sdaddr = sbv_taddr; /* Set from sbx_tset val */
01964                 sd->sdflags &= ~SD_MOD; /* On disk, no longer modified */
01965 
01966                 /* Now insert into phys list at specified place */
01967                 if(sd == sbv_tsd)       /* If already same place */
01968                         goto next;      /* Skip linkin. */
01969                 if(sd->sdback = sbv_tsd)
01970                   {     sd->sdforw = sbv_tsd->sdforw;
01971                         sd->sdback->sdforw = sd;
01972                   }
01973                 else
01974                   {     sd->sdforw = sbv_tf.sfptr1;
01975                         sbv_tf.sfptr1 = sd;
01976                   }
01977                 if(sd->sdforw)
01978                         sd->sdforw->sdback = sd;
01979 
01980         next:   if(flag==1)             /* If only doing 1 sdblk, */
01981                         break;          /* stop here. */
01982         donxt:  sd = sd->slforw;        /* Done with 1st, get next */
01983           }
01984         return(0);                      /* Win return, no errors */
01985 }
01986 
01987 /* Returns hackable length of a sbstring (ends at EOF or locked block) */
01988 chroff
01989 sbx_qlen(sdp)
01990 struct sdblk *sdp;
01991 {       register struct sdblk *sd;
01992         register struct smblk *sm;
01993         chroff len;
01994 
01995         len = 0;
01996         for(sd = sdp; sd && !sbx_qlk(sd); sd = sd->slforw)
01997                 if(sm = sd->sdmem)
01998                         len += (chroff)sm->smuse;
01999                 else len += sd->sdlen;
02000         return(len);
02001 }
02002 
02003 
02004 /* SBX_TSET - finds a place on temp swapout file big enough to hold
02005  *      given # of chars.  Sets SBV_TADDR to that location, as well
02006  *      as seeking to it so the next write call will output there.
02007  *      This location is guaranteed to have the requested
02008  *      byte alignment (0 = word-aligned).
02009  */
02010 sbx_tset(loff, align)
02011 chroff loff;
02012 int align;
02013 {       register int fd;
02014 
02015         if(sbv_tf.sffd <= 0)
02016           {             /* Must open the temp file! */
02017 /* Temporary file mechanism is system-dependent.  Eventually this
02018 ** will probably require inclusion of a true c-env header file; for the
02019 ** time being, we can cheat a little by checking O_T20_WILD, which will
02020 ** be defined by <sys/file.h> on TOPS-20.  Otherwise, we assume we are
02021 ** on a real Unix.
02022 */
02023 #ifdef O_T20_WILD
02024                 extern char *tmpnam();  /* Use ANSI function */
02025                 fd = open(tmpnam((char *)NULL),
02026                                 O_RDWR | O_CREAT | O_TRUNC | O_BINARY);
02027                 if(fd < 0)
02028                         return(sbx_err(0,"Swapout creat err"));         
02029 #else /* Real Unix */
02030                 static char fcp[] = "/tmp/sbd.XXXXXX";
02031                 if((fd = creat(mktemp(fcp),0600)) < 0)
02032                         return(sbx_err(0,"Swapout creat err"));
02033                 /* Must re-open so that we can both read and write to it */
02034                 close(fd);
02035                 if((fd = open(fcp,2)) < 0)
02036                         return(sbx_err(0,"Swapout open err"));
02037                 unlink(fcp);    /* Set so it vanishes when we do */
02038 #endif
02039 
02040                 sbv_tf.sffd = fd;       /* Initialize the sbfile struct */
02041                 sbv_tf.sfptr1 = 0;
02042                 sbv_ftab[fd] = &sbv_tf; /* Record in table of all sbfiles */
02043                 sbv_taddr = 0;          /* "Return" this value */
02044                 return;         /* Ignore alignment for now */
02045           }
02046         sbv_tsd = sbx_ffnd(&sbv_tf, loff+align, &sbv_taddr);
02047         sbv_taddr += align;
02048         if(lseek(sbv_tf.sffd, sbv_taddr, 0) < 0)
02049                 return(sbx_err(0,"Swapout seek err: (%d,%ld,0) %d %s",
02050                         sbv_tf.sffd, sbv_taddr, errno, strerror(errno)));
02051 
02052 }
02053 
02054 /* SBX_FFND - searches disk list of given file for free space of
02055  *      at least size chars.  Note that list must be sorted by ascending
02056  *      disk addrs in order for this to work!  If sdaddrs are only
02057  *      changed in SBX_SPLIT this will be true.
02058  *      Sets "aloc" to disk address for writing (this is guaranteed to
02059  *      be word-aligned, for efficiency), and returns SD ptr to
02060  *      block which this addr should follow in the physical list.  If ptr
02061  *      is 0, it means addr should be 1st thing in list.
02062  */
02063 struct sdblk *
02064 sbx_ffnd(sfp, size, aloc)
02065 SBFILE *sfp;
02066 chroff size, *aloc;
02067 {       register struct sdblk *sd, *sds, *sdl;
02068         chroff cur;
02069 
02070         cur = 0;
02071         sds = 0;
02072         sd = sfp->sfptr1;
02073 redo:   for(; sd ; sd = (sds=sd)->sdforw)
02074           {     if(cur < sd->sdaddr)            /* Gap seen? */
02075                   {     if(size <= (sd->sdaddr - cur))  /* Yes, big enuf? */
02076                                 break;                  /* Yup! */
02077                   }                                     /* No, bump. */
02078                 else if(cur >=(sd->sdaddr + sd->sdlen)) /* No gap but chk */
02079                         continue;                       /* No overlap, ok */
02080                 /* Bump to next possible gap. */
02081                 cur = sd->sdaddr + sd->sdlen;
02082                 cur = (long)rndup(cur); /* Round up to word boundary! */
02083           }
02084         *aloc = cur;            /* Return winning addr */
02085 
02086         /* Perform verification check -- make sure this really is OK
02087          * and complain if not.  If this never blows up, eventually can
02088          * take the check out.
02089          */
02090         sdl = sd;
02091         for(sd = sfp->sfptr1; sd; sd = sd->sdforw)
02092           {     if(cur < sd->sdaddr)
02093                   {     if(size <= (sd->sdaddr - cur))
02094                                 continue;
02095                   }
02096                 else if(cur >= (sd->sdaddr + sd->sdlen))
02097                         continue;
02098 
02099                 sbx_err(0,"FFND blew it, but recovered. SD %o siz %ld",
02100                         sd, size);
02101                 sd = (sds = sdl)->sdforw;
02102                 goto redo;
02103           }
02104 
02105 
02106         return(sds);            /* Return ptr to block this addr follows */
02107 }
02108 
02109 sbx_rdf(fd,addr,cnt,skflg,loc)
02110 register int fd;
02111 char *addr;
02112 int skflg;
02113 chroff loc;
02114 {       register int rres, eres;
02115         long lres;
02116         char *errtyp, *ftyp;
02117         chroff curlen;
02118 
02119         errno = 0;
02120         if(skflg && (lres = lseek(fd, (long)loc, 0)) == -1)
02121           {     errtyp = "Sk err";
02122                 goto errhan;
02123           }
02124         if((rres = read(fd, addr, cnt)) != cnt)
02125           {     lres = rres;
02126                 errtyp = "Rd err";
02127                 goto errhan;
02128           }
02129         return(rres);
02130 errhan:                         /* Handle read or seek error */
02131         eres = errno;
02132         if(fd == sbv_tf.sffd)   /* See if dealing with swapout file */
02133           {     ftyp = "(swap)";
02134                 curlen = 0;
02135           }
02136         else {                  /* No, normal buffer file. */
02137                 ftyp = "";
02138                 curlen = sbx_fdlen(fd);
02139                 if(sbv_ftab[fd] &&
02140                   (curlen != sbv_ftab[fd]->sflen))      /* File changed? */
02141                         if(sbx_rugpull(fd))     /* Yes, handle special case */
02142                                 return(cnt);    /* Allow "win" return */
02143           }
02144         sbx_err(0,"%s %d:%s, %ld:(%d%s,%o,%d)=%ld (fl %ld)",
02145                         errtyp, eres, strerror(eres),
02146                         loc, fd, ftyp, addr, cnt, lres,
02147                         curlen);
02148         return(0);
02149 }
02150 
02151 /* SBX_RUGPULL(fd) - Special routine called when package detects that
02152  *      the file indicated by the fd has changed since its original
02153  *      opening.  This can happen when a file is over-written by some
02154  *      other program (ED, for example).
02155  *      This means that all sdblks which reference this fd
02156  *      are probably bad.  Pass special error back up to the calling
02157  *      program to give it a chance at doing something.
02158  *      Extra credit: scan all sdblks and unpurify all which point to this
02159  *      file, so as to protect everything we still remember about it.
02160  *      Otherwise a GC could flush pure in-core portions.
02161  */
02162 sbx_rugpull(fd)         /* FD already known to have entry in sbv_ftab */
02163 register int fd;
02164 {       int sbx_unpur();
02165 
02166         /* First scan all sdblks to save what we still have. */
02167         /* This does NOT remove the sdfile pointer, so we can still
02168          * find blocks that are affected. */
02169         sbm_nfor(SM_DNODS, sizeof(struct sdblk), sbx_unpur, sbv_ftab[fd]);
02170 
02171         if((int)sbv_debug == 1 || !sbv_debug)
02172                 return(0);                      /* Nothing else we can do */
02173         return((*sbv_debug)(2,(int *)0,"",fd)); /* Let caller handle it */
02174 }
02175 sbx_unpur(sd, sf)               /* Auxiliary routine for SBX_RUGPULL */
02176 register struct sdblk *sd;
02177 register struct sbfile *sf;
02178 {       if(sd->sdfile == sf     /* If sdblk belongs to affected file */
02179           && sd->sdmem)         /* and has in-core version of text, */
02180                 sd->sdflags |= SD_MOD;  /* then ensure core version is used */
02181 }
02182 
02183 sbx_err(val,str,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12)
02184 char *str;
02185 {       int *sptr;
02186 
02187         sptr = (int *) &sptr;   /* Point to self on stack */
02188         sptr += 5;              /* Point to return addr */
02189         if((int)sbv_debug == 1)
02190           {     abort();
02191           }
02192         if(sbv_debug)
02193                 (*sbv_debug)(1,*sptr,str,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12);
02194         return(val);
02195 }

Generated on Fri Apr 14 22:56:52 2006 for minix by  doxygen 1.4.6