re-enable munmap().
[minix.git] / commands / elle / sbstr.c
bloba0bc95dec10b9bd8272b15c659c0831ea79bd29d
1 /* SB - Copyright 1982 by Ken Harrenstien, SRI International
2 * This software is quasi-public; it may be used freely with
3 * like software, but may NOT be sold or made part of licensed
4 * products without permission of the author. In all cases
5 * the source code and any modifications thereto must remain
6 * available to any user.
8 * This is part of the SB library package.
9 * Any software using the SB library must likewise be made
10 * quasi-public, with freely available sources.
13 #if 0
14 Todo stuff:
15 New definitions:
16 sbbuffer - old sbstr. Abbrev & struct "sbbuff". Macro SBBUFF
17 (or SBBUF?)
18 sbstring - list of sds. Abbrev sbstr. Macro SBSTR.
19 Should *sbstr == *sdblk? Yeah.
20 sbfile - as before. Macro SBFILE. (or SBFIL?)
22 Try to get zero-length sdblks flushed on the fly,
23 rather than waiting for moby GC. Also, need to set
24 up compaction of SD freelist, as well as SM freelist.
25 Make SM freelist compact self-invoked by SBM_MGET?
26 Any need for phys disk ptrs other than for tempfile?
27 Can do sbm_forn through SDblks to find active sdfiles
28 so list isn''t needed for that.
29 Can sdback be flushed? (not needed for keeping list sorted,
30 or for searching it -- only used when linking
31 blocks in or out of list.) Perhaps use circular list?
32 If list only used for tmpfile, then to link in/out could
33 always start from sfptr1 of tmpfile? Sure, but slow?
34 Last SD on phys list could belong to no logical list,
35 and denote free space on tmpfile?
37 --------------------------
39 An "open" SBBUFFER will allow one to read, write, insert into,
40 and delete from a sbstring (a logical character string). "Dot" refers
41 to the current logical character position, which is where all
42 operations must happen; sb_fseek must be used to change this location.
43 There are several states that the I/O can be in:
44 !SBCUR ----CLOSED----
45 All other elements, including SBIOP, should also be 0.
46 Dot is 0.
47 SBCUR && !SBIOP ----OPEN/IDLE----
48 SBCUR points to a SD block (its SDMEM may or may not exist)
49 SBIOP==0 (otherwise it would be open/ready)
50 Dot is SBDOT + SBOFF.
51 R/Wleft must be 0.
52 SBCUR && SBIOP ----OPEN/READY----
53 SBCUR points to a SDBLK (SDMEM must exist!)
54 SBIOP exists.
55 Dot is SBDOT + offset into SMBLK. SBOFF is ignored!
56 SB_WRIT flag is set if "smuse" must be updated.
57 The R/Wleft counts are set up:
58 1. Rleft 0, Wleft 0 -- Since SBIOP is set, must assume
59 counts are too.
60 So this means at end of text, no room left.
61 Otherwise would imply that setup needs doing.
62 2. Rleft N, Wleft 0 -- At beg or middle of text
63 3. Rleft 0, Wleft N -- At end of text
64 4. Rleft N, Wleft N -- Shouldn''t ever happen
66 Note that Rleft is always correct. Wleft is sometimes
67 set 0 in order to force a call to determine real state.
69 Note that SBIOP alone is a sufficient test for being OPEN/READY.
71 The important thing about updating the smblk is to ensure that the "smuse"
72 field is correct. This can only be changed by writing or deleting. We assume
73 that deletions always update immediately, thus to determine if an update
74 is necessary, see if SB_WRIT is set. If so, update smuse before doing
75 anything but more writing!!!!
77 The SDBLK must be marked "modified" whenever a write operation is
78 done. We try to do this only the first time, by keeping Wleft zero
79 until after the first write. This is also when SB_WRIT gets set.
80 However, if in overwrite mode, Wleft must be kept zero in order to
81 force the proper actions; SB_WRIT is also not turned on since smuse
82 will not change. Note that at EOF, overwrite becomes the same thing
83 as insert and is treated identically...
85 If a SBLK has an in-core copy but no disk copy, it can be
86 freely modified. Otherwise, modifications should preferably split
87 the block so as to retain "pure" blocks as long as possible. "Pure" blocks
88 can always have their in-core versions flushed immediately (unless for
89 compaction purposes they''ll need to be written out in the same GC pass).
90 Alternatively, mods can simply mark the disk copy "free" and go
91 ahead as if no such copy existed.
92 No additions or changes to a pure block are allowed, but
93 deletions from the end or beginning are always allowed. All other
94 changes must split or insert new blocks to accomplish the changes.
96 Locking:
97 SDBLKs are subject to unpredictable relocation, compaction,
98 and garbage collecting. There are three ways in which a SDBLK can
99 remain fixed:
101 1. The SDBLK has the SD_LOCK flag set. This flag is used whenever
102 a SBBUF''s SBCUR is pointing to this SDBLK.
103 2. The SDBLK has the SD_LCK2 flag set. This flag is used only
104 during execution of various internal routines and should
105 not be seen anywhere during execution of user code.
106 3. The SDBLK has no back-pointer (is first block in a sbstring).
107 Such SDBLKs cannot be relocated (since it is not known
108 what may be pointing to them) but unlike the other 2 cases
109 they are still subject to compaction with succeeding SDBLKs.
111 The SDBLK must be locked with SD_LOCK for as long as it is being
112 pointed to by SBCUR. The sole exception is when a SBBUF in the
113 OPEN/IDLE state is pointing to the first SDBLK of a sbstring; this
114 sdblk is guaranteed not to be moved, since sdblks without a
115 back-pointer are never moved. SD_LOCK is asserted as soon as the state
116 changes to OPEN/READY, of course. The internal routines take pains to
117 always move SD_LOCK as appropriate. Note that only one SD in a
118 sbstring can ever have SD_LOCK turned on. SD_LCK2 is an auxiliary flag
119 which may appear in more than one SDBLK, for use by low-level routines
120 for various temporary reasons; either will prevent the SDBLK from being
121 modified in any way by the storage compactor.
123 SEEKs are a problem because it''s unclear at seek time what will happen
124 next, so the excision of the smblk can''t be optimized. If the seek
125 happens to land in a sdblk with an existing smblk, there''s no problem;
126 but if it''s a sdblk alone, how to decide which part of it to read in???
127 If next action is:
128 write - split up sdblk and create new one. Read nothing in.
129 read - read in 512 bytes starting at disk blk boundary if possible
130 else read in 128 bytes starting with selected char
131 (include beg of sdblk if less than 64 chars away)
132 overwrite - as for read.
133 backread - like read but position at end of sdblk.
134 delete - split up sdblk, read nothing in.
136 We solve this through the OPEN/IDLE state, where SBIOP == 0 means SBOFF
137 points to logical offset from start of current sdblk, so that the seek
138 need not take any action. Only when a specific operation is requested
139 will the transition to OPEN/READY take place, at which time we''ll know
140 what the optimal excision strategy is. The routine SBX_READY performs
141 this function.
143 The physical links (SDFORW and SDBACK) are only valid when SDFILE is
144 set (likewise for SDLEN and SDADDR). In other words, mungs to a sdblk
145 must check SDFILE to see whether or not the phys links should be
146 altered. Normally they aren''t except during sdblk creation, deletion,
147 or swapout, no matter how much the sdblk gets shuffled around
148 logically. The disk physical list is kept sorted in order of starting
149 addresses. The text blocks indicated can overlap. When a GC is
150 necessary, the code must figure out how much space is actually free.
152 -------------- Old woolgathering, ignore rest of this page ---------------
154 Question: should 512-byte buffers be maintained, one for each SBFILE?
155 Or should the in-core text be hacked up to serve for buffering?
156 Question is where to point the READ/WRITE system calls. Currently,
157 they are pointed directly at the in-core text, and there are no
158 auxiliary buffers.
160 If use auxiliary buffers:
161 How to handle flushing, when changing location etc?
162 Could be clever about reading from large disk block, only
163 get part of it into buffer instead of splitting up in order to
164 read a "whole" block.
165 Problem: sbstrings can include pieces of several different files.
166 Hard to maintain just one buffer per FD without hacking
167 done on one sbstring screwing that on another.
168 If don''t use buffers:
169 Need to have a "chars-left" field in mem blocks, so know how
170 much more can be added. Will need heuristics for how much
171 extra space to allocate.
172 #endif /*COMMENT*/
174 /* Includes, initial definitions */
176 #include <stdio.h>
177 #include "sb.h"
179 #ifndef V6
180 #define V6 0
181 #endif
183 #if V6
184 #include <stat.h>
185 #else
186 #include <sys/types.h>
187 #include <sys/stat.h>
188 #if MINIX
189 #include <fcntl.h> /* For open() flags */
190 #else
191 #include <sys/file.h> /* For open() flags */
192 #endif /* MINIX */
193 #endif /*-V6*/
195 extern int errno;
196 extern char *strerror(); /* From ANSI <string.h> */
198 /* Allocation decls */
199 SBFILE sbv_tf; /* SBFILE for temp swapout file */
200 int (*sbv_debug)(); /* Error handler address */
203 /* SBX_READY argument flags (internal to SBSTR routines only)
204 * The following values should all be unique; the exact value
205 * doesn't matter as long as the right SKM flags are given.
207 #define SK_READF 0 /* 0-skip fwd, align BOB */
208 #define SK_READB (0|SKM_0BACK|SKM_EOB) /* 0-skip bkwd, align EOB */
209 #define SK_WRITEF (0|SKM_EOB) /* 0-skip fwd, align EOB */
210 #define SK_DELF (4|SKM_0BACK) /* 0-skip bkwd, align BOB */
211 #define SK_DELB (4|SKM_EOB) /* 0-skip fwd, align EOB */
212 #define SKM_0BACK 01 /* Zero-skip direction: 0 = fwd, set = backwd
213 * Don't ever change this value! See SBX_NORM. */
214 #define SKM_EOB 02 /* Alignment: 0 = Beg-Of-Buf, set = End-Of-Buf */
216 /* Note on routine names:
217 * "SB_" User callable, deals with sbbufs (usually).
218 * "SBS_" User callable, deals with sbstrings only.
219 * "SBX_" Internal routine, not meant for external use.
220 * "SBM_" Routine handling mem alloc, usually user callable.
223 /* SBBUF Opening, Closing, Mode setting */
225 /* SB_OPEN(sb,sd) - Sets up SBBUF given pointer to first SD of a sbstring.
226 * If SD == 0 then creates null sbstring.
227 * Any previous contents of SBBUF are totally ignored!!! If you
228 * want to save the stuff, use SB_UNSET.
229 * Sets I/O ptr to start of sbstring.
230 * Returns 0 if error, else the given SB.
232 SBBUF *
233 sb_open(sbp,sdp)
234 SBBUF *sbp;
235 SBSTR *sdp;
236 { register struct sdblk *sd;
237 register int cnt;
238 register WORD *clrp;
240 if(!sbp) return((SBBUF *)0);
241 if((sd = sdp) == 0)
242 { sd = sbx_ndget(); /* Get a fresh node */
243 clrp = (WORD *) sd; /* Clear it all */
244 cnt = rnddiv(sizeof(struct sdblk));
245 do { *clrp++ = 0; } while(--cnt);
246 sd->sdflags = SD_NID; /* Except flags of course */
248 else if(sd->slback) /* Must be first thing in sbstring */
249 return((SBBUF *)0); /* Perhaps could normalize tho */
251 clrp = (WORD *) sbp; /* Clear sbbuffer stuff */
252 cnt = rnddiv(sizeof(SBBUF));
253 do { *clrp++ = 0; } while(--cnt);
255 sbp->sbcur = sd;
256 /* Note that SD_LOCK need not be set, because first SDBLK has no
257 * backptr. This is desirable to allow storage compactor maximum
258 * freedom in merging sdblks.
260 /* sd->sdflags |= SD_LOCK; */ /* Lock this one */
261 return(sbp);
265 /* SB_CLOSE(sb) - Close a SBBUF.
266 * Returns pointer to start of sbstring (first SD).
267 * Returns 0 if error.
269 SBSTR *
270 sb_close(sbp)
271 SBBUF *sbp;
272 { register SBBUF *sb;
273 register struct sdblk *sd;
275 if((sb = sbp) == 0) /* Verify pointer */
276 return((SBSTR *)0);
277 sb_rewind(sb); /* Do most of the work, including unlock */
278 sd = sb->sbcur; /* Save ptr to sbstring */
279 sb->sbcur = 0; /* Now reset the sbbuffer structure */
280 sb->sbflags = 0;
281 return(sd);
285 /* SB_SETOVW(sbp) - Set SBBUF Over-write mode for PUTC's.
286 * SB_CLROVW(sbp) - Clear ditto.
288 sb_setovw(sbp)
289 SBBUF *sbp;
290 { register SBBUF *sb;
291 if(sb=sbp)
292 { sb->sbflags |= SB_OVW;
293 sb->sbwleft = 0;
297 sb_clrovw(sbp)
298 SBBUF *sbp;
299 { register SBBUF *sb;
300 if(sb=sbp) sb->sbflags &= ~SB_OVW;
303 /* SBSTRING file system operations (see also sb_fsave) */
305 /* SB_FDUSE(fd) - Make a sbstring for given file.
306 * FD is an open file descriptor.
307 * Returns pointer to a SBSTR containing the given file, or 0 if error.
308 * The FD must not be closed until a SB_FDCLS is done to
309 * purge memory of any blocks pointing at the file.
310 * ** Maybe allocate sbfile structs with sbx_ndget, i.e. overlay on
311 * ** top of sdblk node?? Wd this screw verify, GC, etc? Maybe not if
312 * ** SD_LCK2 set...
315 struct sbfile *sbv_ftab[SB_NFILES];
317 chroff
318 sbx_fdlen(fd)
319 int fd;
321 #if !V6
322 struct stat statb;
323 #else
324 struct statb statb;
325 chroff len;
326 struct {int hiwd ; int lowd;} foo;
327 #endif /*V6*/
329 if(fstat(fd,&statb) < 0) return((chroff)-1);
330 #if V6
331 len = statb.i_size1;
332 len.hiwd = statb.i_size0 & 0377;
333 return(len);
334 #else
335 return((chroff)statb.st_size);
336 #endif /*-V6*/
339 SBSTR *
340 sb_fduse(ifd)
341 int ifd;
342 { register struct sdblk *sd;
343 register struct sbfile *sf;
344 register int fd;
345 chroff len;
347 if((fd = ifd) < 0 || SB_NFILES <= fd /* Check for absurd FD */
348 || sbv_ftab[fd]) /* and slot already in use */
349 return((SBSTR *)0);
350 if((len = sbx_fdlen(fd)) < 0) return((SBSTR *)0);
351 sbv_ftab[fd]= sf = (struct sbfile *)sbx_malloc(sizeof(struct sbfile));
352 sf->sffd = fd;
353 sf->sfptr1 = sd = sbx_ndget();
354 sf->sflen = len;
355 sd->slforw = 0;
356 sd->slback = 0;
357 sd->sdforw = 0;
358 sd->sdback = 0;
359 sd->sdmem = 0;
360 sd->sdfile = sf;
361 sd->sdlen = len;
362 sd->sdaddr = 0;
363 return(sd);
366 /* SB_FDCLS(fd) - Close a file descriptor being used by sbstrings.
367 * If arg is -1, closes all FD's that are unused (a "sweep").
368 * For specific arg, returns 0 if couldn't close FD because still in use.
369 * Perhaps later version of routine could have option to copy
370 * still-used SD's to tempfile, and force the FD closed?
372 sb_fdcls(ifd)
373 int ifd;
374 { register int fd;
376 if((fd = ifd) >= 0)
377 { if(fd >= SB_NFILES) return(0); /* Error of sorts */
378 return(sbx_fcls(sbv_ftab[fd]));
380 fd = SB_NFILES-1;
381 do {
382 sbx_fcls(sbv_ftab[fd]);
383 } while(--fd); /* Doesn't try FD 0 ! */
384 return(1);
387 sbx_fcls(sfp)
388 struct sbfile *sfp;
389 { register struct sbfile *sf;
390 register int fd;
392 if((sf = sfp)==0 /* Ignore null args */
393 || sf == &sbv_tf) /* and never close our tempfile! */
394 return(0);
395 fd = sf->sffd; /* Find sys file descriptor */
396 if(sbv_ftab[fd] != sf) /* Ensure consistency */
397 return(sbx_err(0,"SF table inconsistency"));
398 if(sf->sfptr1) /* Any phys list still exists? */
399 return(0); /* Yes, still in use, can't close */
400 close(fd); /* Maybe do this when list gone? */
401 sbv_ftab[fd] = 0; /* Remove from table */
402 free(sf); /* Remove sbfile struct from mem */
405 /* SB_FDINP(sb,fd) - Returns TRUE if specified fd is still in use
406 * by specified sbbuffer.
408 sb_fdinp(sb, fd)
409 register SBBUF *sb;
410 int fd;
411 { register struct sdblk *sd;
412 register struct sbfile *sf;
414 if((sf = sbv_ftab[fd]) == 0
415 || (sd = sb->sbcur) == 0)
416 return(0);
417 sd = sbx_beg(sd); /* Move to beginning of sbstring */
418 for(; sd; sd = sd->slforw) /* Scan thru all blocks in string */
419 if(sd->sdfile == sf) /* If any of them match, */
420 return(1); /* Return tally-ho */
421 return(0);
424 /* SB_FSAVE(sb,fd) - Write entire SBBUF out to specified FD.
425 * Returns 0 if successful, else system call error number.
427 sb_fsave(sb,fd) /* Write all of given sbbuf to given fd */
428 register SBBUF *sb;
429 int fd;
431 sbx_smdisc(sb);
432 return(sbx_aout(sbx_beg(sb->sbcur), 2, fd));
435 /* SBBUF Character Operations */
437 /* SB_GETC(sb) - Get next char from sbstring.
438 * Returns char at current location and advances I/O ptr.
439 * Returns EOF on error or end-of-string.
442 sb_sgetc(sb)
443 register SBBUF *sb;
445 if(--(sb->sbrleft) >= 0)
446 return sb_uchartoint(*sb->sbiop++);
448 /* Must do hard stuff -- check ptrs, get next blk */
449 sb->sbrleft = 0; /* Reset cnt to zero */
450 if(sb->sbcur == 0 /* Make sure sbbuffer there */
451 || (int)sbx_ready(sb,SK_READF,0,SB_BUFSIZ) <= 0) /* Normalize & gobble */
452 return(EOF);
453 return(sb_sgetc(sb)); /* Try again */
454 } /* Loop wd be faster, but PDL OV will catch infinite-loop bugs */
457 /* SB_PUTC(sb,ch) - Put char into sbstring.
458 * Inserts char at current location.
459 * Returns EOF on error, else the char value.
462 sb_sputc(sb,ch)
463 register SBBUF *sb;
464 int ch;
466 register struct sdblk *sd;
468 if(--(sb->sbwleft) >= 0) return(*sb->sbiop++ = ch);
470 sb->sbwleft = 0; /* Reset cnt to avoid overflow */
471 if((sd = sb->sbcur) == 0) /* Verify string is there */
472 return(EOF); /* Could perhaps create it?? */
473 if(sb->sbflags&SB_OVW) /* If overwriting, handle std case */
474 { if(sb->sbiop &&
475 --sb->sbrleft >= 0) /* Use this for real count */
476 { sd->sdflags |= SD_MOD; /* Win, munging... */
477 return(*sb->sbiop++ = ch);
479 /* Overwriting and hit end of this block. */
480 if((int)sbx_ready(sb,SK_READF,0,SB_BUFSIZ) > 0) /* Re-normalize */
481 return(sb_sputc(sb,ch));
483 /* No blks left, fall through to insert stuff at end */
486 /* Do canonical setup with heavy artillery */
487 if((int)sbx_ready(sb,SK_WRITEF,SB_SLOP,SB_BUFSIZ) <= 0) /* Get room */
488 return(EOF); /* Should never happen, but... */
489 sb->sbflags |= SB_WRIT;
490 sb->sbcur->sdflags |= SD_MOD;
491 return(sb_sputc(sb,ch)); /* Try again */
492 } /* Loop wd be faster, but PDL OV will catch infinite-loop bugs */
495 /* SB_PEEKC(sb) - Peek at next char from sbstring.
496 * Returns char that sb_getc would next return, but without
497 * changing I/O ptr.
498 * Returns EOF on error or end-of-string.
501 sb_speekc(sb)
502 register SBBUF *sb;
504 if (sb->sbrleft <= 0) /* See if OK to read */
505 { if (sb_sgetc(sb) == EOF) /* No, try hard to get next */
506 return EOF; /* Failed, return EOF */
507 sb_backc(sb); /* Won, back up */
509 return sb_uchartoint(*sb->sbiop);
512 /* SB_RGETC(sb) - Get previous char from sbstring.
513 * Returns char prior to current location and backs up I/O ptr.
514 * Returns EOF on error or beginning-of-string.
517 sb_rgetc(sb)
518 register SBBUF *sb;
520 register struct smblk *sm;
521 register struct sdblk *sd;
523 if((sd=sb->sbcur) && (sm = sd->sdmem)
524 && sb->sbiop > sm->smaddr)
525 { if(sb->sbflags&SB_WRIT)
526 { sm->smuse = sb->sbiop - sm->smaddr;
527 sb->sbwleft = 0;
528 sb->sbflags &= ~SB_WRIT;
530 sb->sbrleft++;
531 return sb_uchartoint(*--sb->sbiop); /* Return char */
533 if((int)sbx_ready(sb,SK_READB,SB_BUFSIZ,0) <= 0)
534 return(EOF);
535 return(sb_rgetc(sb));
538 /* SB_RDELC(sb) - Delete backwards one char.
539 * Returns nothing.
541 sb_rdelc(sbp)
542 SBBUF *sbp;
543 { register SBBUF *sb;
544 register struct sdblk *sd;
546 if(((sb=sbp)->sbflags&SB_WRIT) /* Handle simple case fast */
547 && sb->sbiop > (sd = sb->sbcur)->sdmem->smaddr)
548 { sb->sbwleft++;
549 sb->sbiop--;
550 sd->sdflags |= SD_MOD;
551 return;
553 else sb_deln(sb,(chroff) -1); /* Else punt... */
556 /* SB_DELC(sb) - Delete one char forward? */
557 /* SB_INSC(sb,ch) - Insert char? (instead of or in addition to PUTC) */
560 /* SBBUF string or N-char operations */
562 /* SB_DELN(sb,chroff) - delete N chars. Negative N means backwards.
563 * Differs from sb_killn in that it flushes the text forever,
564 * and doesn't return anything.
567 sb_deln(sbp, num)
568 SBBUF *sbp;
569 chroff num;
571 register struct sdblk *sd;
573 if(sd = sb_killn(sbp,num))
574 sbs_del(sd); /* Punt */
577 /* SB_KILLN(sb,chroff) - delete N chars, saving. Negative N means backwards.
578 * Returns SD pointer to beginning of saved sbstring.
580 struct sdblk *
581 sb_killn(sbp, num)
582 SBBUF *sbp;
583 chroff num;
584 { register SBBUF *sb;
585 register struct sdblk *sd, *sd2;
586 struct sdblk *sdr, *sdx;
587 chroff savdot;
589 if((sd = sbx_xcis((sb=sbp),num,&sdr,&savdot)) == 0)
590 return((struct sdblk *)0);
592 sb->sbcur->sdflags &= ~SD_LOCK; /* Now can flush sbcur lock */
594 /* SD and SD2 now delimit bounds of stuff to excise.
595 * First do direction dependent fixups
597 if(num >= 0) /* If deleting forward, */
598 sb->sbdot = savdot; /* must reset dot to initial loc */
600 /* SD and SD2 now in first/last order. Complete SBCUR fixup. */
601 sd2 = sdr; /* sdr has ptr to end of stuff */
602 if(sd2 = sd2->slforw) /* More stuff after killed list? */
603 { sb->sbcur = sd2; /* Yes, point at it */
604 sb->sboff = 0; /* Dot already set right */
606 else if(sdx = sd->slback) /* See if any prior to killed list */
607 { sb->sbcur = sdx; /* Yes, point at it */
608 sb->sboff = (sdx->sdmem ? /* Get len of prev blk */
609 sdx->sdmem->smuse : sdx->sdlen);
610 sb->sbdot -= sb->sboff;
612 else sb_open(sb,(SBSTR *)0); /* No stuff left! Create null sbstring */
614 /* Fix up logical links. Note SD2 points to succ of killed stuff */
615 if(sd->slback) /* If previous exists */
616 { if(sd->slback->slforw = sd2) /* Point it to succ, and */
617 sd2->slback = sd->slback; /* thence to self */
618 sd->slback = 0; /* Now init killed list */
620 else if(sd2) sd2->slback = 0; /* No prev, clean rest */
621 (sd2 = sdr)->slforw = 0; /* Finish killed list */
623 sb->sbcur->sdflags |= SD_LOCK; /* Ensure current SD now locked */
624 sd->sdflags &= ~SD_LCK2; /* And unlock killed list */
625 sd2->sdflags &= ~SD_LCK2;
626 return(sd);
629 /* SB_CPYN(sbp,num) - Copy num characters, returns SD to sbstring.
630 * Like SB_KILLN but doesn't take chars out of original sbstring.
632 SBSTR *
633 sb_cpyn(sbp,num)
634 SBBUF *sbp;
635 chroff num;
636 { register SBBUF *sb;
637 register struct sdblk *sd, *sd2;
638 struct sdblk *sdr;
639 chroff savloc;
641 sb = sbp;
642 if((sd = sbx_xcis(sb,num,&sdr,&savloc)) == 0)
643 return((SBSTR *)0);
644 sd2 = sbx_scpy(sd,sdr);
645 sb_seek(sb,-num,1); /* Return to original loc */
646 return(sd2); /* Return val is ptr to head of copy.
647 * It needn't be locked, because GC will
648 * never move list heads!
652 /* SB_SINS(sb,sd) - Insert sbstring at current location
655 sb_sins(sbp,sdp)
656 SBBUF *sbp;
657 struct sdblk *sdp;
658 { register SBBUF *sb;
659 register struct sdblk *sd, *sdx;
660 chroff inslen;
662 if((sb = sbp)==0
663 || (sd = sdp) == 0)
664 return(0);
665 if(sd->slback) /* Perhaps normalize to beg? */
666 return(0);
667 if((sdx = (struct sdblk *)sbx_ready(sb,SK_DELB)) == 0) /* Get cur pos ready */
668 return(0);
669 inslen = sbs_len(sd); /* Save length of inserted stuff */
671 sd->slback = sdx; /* Fix up links */
672 if(sdx->slforw)
673 { while(sd->slforw) /* Hunt for end of inserted sbstring */
674 sd = sd->slforw;
675 sd->slforw = sdx->slforw;
676 sd->slforw->slback = sd;
678 sdx->slforw = sdp;
679 sb->sboff += inslen; /* Set IO ptr to end of new stuff */
680 return(1);
683 /* SBSTRING routines - operate on "bare" sbstrings. */
685 /* SBS_CPY(sd) - Copies given sbstring, returns ptr to new sbstring.
687 SBSTR *
688 sbs_cpy(sdp)
689 SBSTR *sdp;
690 { return(sbx_scpy(sdp,(struct sdblk *)0));
693 /* SBS_DEL(sd) - Flush a sbstring.
695 sbs_del(sdp)
696 SBSTR *sdp;
697 { register struct sdblk *sd;
699 if(sd = sdp)
700 while(sd = sbx_ndel(sd));
704 /* SBS_APP(sd1,sd2) - Appends sbstring sd2 at end of sbstring sd1.
705 * Returns sd1 (pointer to new sbstring).
708 SBSTR *
709 sbs_app(sdp,sdp2)
710 struct sdblk *sdp,*sdp2;
711 { register struct sdblk *sd, *sdx;
713 if(sd = sdp)
714 { while(sdx = sd->slforw)
715 sd = sdx;
716 if(sd->slforw = sdx = sdp2)
717 sdx->slback = sd;
719 return(sdp);
722 /* SBS_LEN(sd) - Find length of sbstring.
724 chroff
725 sbs_len(sdp)
726 SBSTR *sdp;
727 { register struct sdblk *sd;
728 register struct smblk *sm;
729 chroff len;
731 if((sd = sdp)==0) return((chroff)0);
732 len = 0;
733 for(; sd ; sd = sd->slforw)
734 { if(sm = sd->sdmem)
735 len += (chroff)sm->smuse;
736 else len += sd->sdlen;
738 return(len);
741 /* SBBUF I/O pointer ("dot") routines */
743 /* SB_SEEK(sb,chroff,flag) - Like FSEEK. Changes I/O ptr value as
744 * indicated by "flag":
745 * 0 - offset from beg
746 * 1 - offset from current pos
747 * 2 - offset from EOF
748 * Returns -1 on errors.
749 * Seeking beyond beginning or end of sbbuf will leave pointer
750 * at the beginning or end respectively.
751 * Returns 0 unless error (then returns -1).
753 sb_seek(sbp, coff, flg)
754 SBBUF *sbp;
755 chroff coff;
756 int flg;
757 { register SBBUF *sb;
758 register struct smblk *sm;
759 register struct sdblk *sd;
760 SBMO moff;
762 sb = sbp;
763 if((sd = sb->sbcur) == 0) return(-1);
764 if(sb->sbiop == 0)
765 { switch(flg)
766 { case 0: if(coff == 0) /* Optimize common case */
767 return(sb_rewind(sb));
768 sb->sboff = coff - sb->sbdot; /* Abs */
769 break;
770 case 1: sb->sboff += coff; /* Rel */
771 break;
772 case 2: sb->sboff += sb_ztell(sb) + coff;
773 break;
774 default: return(-1);
776 sbx_norm(sb,0);
777 return(0);
779 if((sm = sd->sdmem) == 0)
780 return(sbx_err(-1,"SDMEM 0"));
781 moff = sb->sbiop - sm->smaddr; /* Get cur smblk offset */
782 if(sb->sbflags&SB_WRIT) /* Update since moving out */
783 { sm->smuse = moff;
784 sb->sbflags &= ~SB_WRIT;
786 sb->sbwleft = 0; /* Always gets zapped */
787 switch(flg)
788 { case 0: /* Offset from beginning */
789 coff -= sb->sbdot + (chroff)moff; /* Make rel */
791 case 1: /* Offset from current loc */
792 break;
794 case 2: /* Offset from end */
795 coff += sb_ztell(sb);
796 break;
797 default: return(-1);
800 /* COFF now has relative offset from current location */
801 if (-(chroff)moff <= coff && coff <= sb->sbrleft)
802 { /* Win! Handle repos-within-smblk */
803 sb->sbiop += coff;
804 sb->sbrleft -= coff; /* Set r; wleft already 0 */
805 return(0);
808 /* Come here when moving to a different sdblk. */
809 sb->sbrleft = 0;
810 sb->sbiop = 0;
811 sb->sboff = coff + (chroff)moff;
812 sbx_norm(sb,0);
813 return(0);
816 /* SB_REWIND(sb) - Go to beginning of sbbuffer.
817 * Much faster than using sb_seek. Note that this leaves the sbbuffer
818 * in an open/idle state which is maximally easy to compact.
820 sb_rewind(sbp)
821 SBBUF *sbp;
822 { register SBBUF *sb;
823 register struct sdblk *sd;
825 if((sb = sbp)==0) return;
826 sbx_smdisc(sb); /* Ensure I/O disconnected */
827 (sd = sb->sbcur)->sdflags &= ~SD_LOCK; /* Unlock current blk */
828 sd = sbx_beg(sd); /* Move to beg of sbstring */
829 /* Need not lock - see sb_open comments, also sb_close */
830 /* sd->sdflags |= SD_LOCK; */ /* Lock onto this one */
831 sb->sbcur = sd;
832 sb->sbdot = 0;
833 sb->sboff = 0;
836 /* SB_TELL(sb) - Get I/O ptr value for SBBUF.
837 * Returns -1 on errors.
840 chroff
841 sb_tell(sbp)
842 SBBUF *sbp;
843 { register SBBUF *sb;
844 register struct smblk *sm;
845 register struct sdblk *sd;
847 if((sd = (sb=sbp)->sbcur) == 0)
848 return((chroff)-1);
849 if(sb->sbiop == 0)
850 return(sb->sbdot + sb->sboff);
851 if((sm = sd->sdmem) == 0)
852 return(sbx_err(0,"SDMEM 0"));
853 return(sb->sbdot + (unsigned)(sb->sbiop - sm->smaddr));
856 /* SB_ZTELL(sb) - Get I/O ptr relative to "Z" (EOF).
857 * Returns # chars from current location to EOF; 0 if any errors.
859 chroff
860 sb_ztell(sbp)
861 SBBUF *sbp;
862 { register SBBUF *sb;
863 register struct smblk *sm;
864 register struct sdblk *sd;
866 if((sd = (sb=sbp)->sbcur) == 0)
867 return((chroff)0);
868 if(sb->sbiop && (sm = sd->sdmem))
869 { if(sb->sbflags&SB_WRIT) /* If actively writing, */
870 return(sbs_len(sd->slforw)); /* ignore this blk. */
871 /* Note that previous code makes it unnecessary
872 * to invoke sbx_smdisc. (otherwise wrong
873 * smuse would confuse sbs_len).
875 return(sbs_len(sd) - (sb->sbiop - sm->smaddr));
877 else
878 return(sbs_len(sd) - sb->sboff);
881 /* Code past this point should insofar as possible be INTERNAL. */
883 /* SBX_READY(sb,type,cmin,cmax) - Set up SBBUF for reading or writing.
885 * If no current smblk:
886 * reading - set up for reading
887 * writing - set up for splitting?
888 * If current smblk:
889 * reading - if can read, OK. Else position at beg of next sdblk
890 * writing - if can write, OK. Else position at end of prev sdblk,
891 * or set up for splitting?
892 * Types:
893 * 0 - Read forward (BOB)
894 * 1 - Read backward (EOB)
895 * 3 - Write (insert forward) (EOB)
896 * 4 - Delete forward (return SD, force BOB-aligned)
897 * 5 - Delete backward (return SD, force EOB-aligned)
898 * Connected SD is always locked.
899 * Returns 0 if error, -1 if EOF-type error, 1 for success.
901 * For types 0,1:
902 * CMIN,CMAX represent max # chars to read in to left and right of
903 * I/O ptr (prev and post). Actual amount read in may be
904 * much less, but will never be zero.
905 * Successful return guarantees that SBIOP etc. are ready.
906 * For type 3:
907 * If new block is allocated, CMIN and CMAX represent min, max sizes
908 * of the block.
909 * Successful return guarantees that SBIOP etc. are ready, but
910 * NOTE that SB_WRIT and SD_MOD are not set! If not going to use
911 * for writing, be sure to clear sbwleft on return!
912 * For types 4,5:
913 * CMIN, CMAX are ignored.
914 * SBIOP is always cleared. SBOFF is guaranteed to be 0 for
915 * type 4, SMUSE for type 5.
916 * Return value is a SD ptr; 0 indicates error. -1 isn't used.
919 struct sdblk *
920 sbx_ready(sbp,type,cmin,cmax)
921 SBBUF *sbp;
922 int type;
923 SBMO cmin,cmax;
924 { register SBBUF *sb;
925 register struct sdblk *sd;
926 register struct smblk *sm;
927 int cnt, slop, rem;
928 SBMO moff;
930 if((sd = (sb=sbp)->sbcur) == 0)
931 return(0);
932 if(sb->sbiop) /* Canonicalize for given operation */
933 { if((sm = sd->sdmem)==0)
934 return(0);
935 moff = sb->sbiop - sm->smaddr; /* Current block offset */
936 switch(type)
938 case SK_READF: /* Read Forward */
939 if(sb->sbrleft > 0) /* Already set up? */
940 return(1); /* Yup, fast return */
941 sbx_smdisc(sb); /* None left, disc to get next */
942 if((sd = sbx_next(sb)) == 0) /* Try to get next blk */
943 return(-1); /* At EOF */
944 break;
946 case SK_READB: /* Read Backward */
947 if(moff) /* Stuff there to read? */
948 { if(sb->sbflags&SB_WRIT) /* Yup, turn writes off */
949 { sm->smuse = moff;
950 sb->sbflags &= ~SB_WRIT;
952 sb->sbwleft = 0;
953 return(1);
955 sbx_smdisc(sb);
956 break;
958 case SK_WRITEF: /* Writing */
959 if(sb->sbrleft <= 0)
960 sb->sbwleft = sm->smlen - moff;
961 if(sb->sbwleft > 0)
962 return(1); /* OK to write now */
963 /* NOTE: flags not set!!! */
964 sbx_smdisc(sb);
965 break;
967 case SK_DELF: /* Delete forward - force BOB */
968 if(sb->sbrleft <= 0) /* At end of blk? */
969 { sbx_smdisc(sb); /* Win, unhook */
970 return(sbx_next(sb)); /* Return next or 0 if EOF */
972 sbx_smdisc(sb); /* Not at end, but see if */
973 if(moff == 0) /* at beg of blk? */
974 return(sd); /* Fast win! */
975 break;
977 case SK_DELB: /* Delete backward - force EOB */
978 if(sb->sbrleft <= 0) /* Win if already EOB */
979 { sbx_smdisc(sb);
980 return(sd);
982 sbx_smdisc(sb);
983 break;
985 default:
986 return(0);
990 /* Schnarf in the text, or whatever.
991 * SD points to current sdblk (must be SD_LOCKed)
992 * SBDOT must have correct value for this SD
993 * SBOFF has offset from there to put I/O ptr at.
995 * After normalization, SBOFF is guaranteed to point within
996 * the SD. Other guarantees apply to boundary cases, depending
997 * on the mode (type) bits.
999 sd = sbx_norm(sb,type); /* Normalize I/O pos appropriately */
1000 sm = sd->sdmem;
1001 switch(type)
1003 case SK_READB: /* Read Backward */
1004 if(sb->sboff == 0) /* Due to normalize, if 0 seen */
1005 return(-1); /* then we know it's BOF */
1006 if(sm) goto sekr2;
1007 else goto sekr1;
1009 case SK_READF: /* Read Forward */
1010 if(sm) goto sekr2;
1011 if(sb->sboff == sd->sdlen) /* Normalize means if EOB */
1012 return(-1); /* then at EOF. */
1013 sekr1: slop = SB_SLOP;
1014 sekr3: if(sb->sboff > cmin+slop) /* Too much leading text? */
1015 { /* Split off leading txt */
1016 sbx_split(sd,(chroff)(sb->sboff - cmin));
1017 sd = sbx_next(sb); /* Point to next sdblk */
1018 sb->sboff = cmin; /* Set correct offset */
1019 /* (sbx_next assumes 0) */
1021 if(sd->sdlen > sb->sboff+cmax+slop) /* Too much trailing txt? */
1022 sbx_split(sd,(chroff)(sb->sboff+cmax));
1024 /* ----- Try to get mem blk to read stuff into ----- */
1025 /* Note alignment hack for extra efficiency. This ensures
1026 * that all reads from disk to memory are made with the same
1027 * source and destination word alignment, so the system kernel
1028 * only needs byte-moves for the first or last bytes; all
1029 * others can be word-moves.
1030 * This works because sbx_mget always returns word-aligned
1031 * storage, and we use sbx_msplit to trim off the right number
1032 * of bytes from the start.
1034 cnt = sd->sdlen; /* Get # bytes we'd like */
1035 if(rem = rndrem(sd->sdaddr)) /* If disk not word-aligned */
1036 cnt += rem; /* allow extra for aligning.*/
1037 if(sm == 0) /* Always true 1st time */
1038 { sm = sbx_mget(SB_SLOP,cnt); /* Get room (may GC!)*/
1039 if(sm->smlen < cnt) /* Got what we wanted? */
1040 { slop = 0; /* NO!! Impose stricter */
1041 cmin = 0; /* limits. Allow for new */
1042 cmax = sm->smlen - (WDSIZE-1); /* rem. */
1043 if(type == SK_READB)
1044 { cmin = cmax; cmax = 0; }
1045 goto sekr3; /* Go try again, sigh. */
1048 else if(sm->smlen < cnt) /* 2nd time shd always win */
1049 { sbx_err(0,"Readin blksiz err"); /* Internal error, */
1050 if((cmax /= 2) > 0) goto sekr3; /* w/crude recovery */
1051 return(0);
1053 if(rem) /* If disk not word-aligned, hack stuff */
1054 { sm = sbx_msplit(sm, (SBMO)rem); /* Trim off from beg*/
1055 sbm_mfree(sm->smback); /* Free the excess */
1057 sd->sdmem = sm;
1058 sm->smuse = sd->sdlen;
1060 if(sd->sdfile == 0)
1061 return(sbx_err(0,"No file")); /* Gasp? */
1062 if(!sbx_rdf(sd->sdfile->sffd, sm->smaddr, sm->smuse,
1063 1, sd->sdaddr))
1064 return(sbx_err(0,"Readin SD: %o", sd));
1065 /* ------- */
1067 sekr2: sbx_sbrdy(sb); /* Make it current, pt to beg */
1068 sb->sbwleft = 0; /* Ensure not set (esp if READB) */
1069 break;
1071 case SK_WRITEF: /* Write-type seek */
1072 if(sm == 0)
1073 { /* Block is on disk, so always split (avoid readin) */
1074 if(sd->sdlen) /* May be empty */
1075 { sbx_split(sd, sb->sboff); /* Split at IO ptr */
1076 sd = sbx_next(sb); /* Move to 2nd part */
1077 if(sd->sdlen) /* If stuff there, */
1078 /* split it again. */
1079 sbx_split(sd, (chroff) 0);
1081 goto sekwget;
1084 /* Block in memory */
1085 moff = sm->smuse;
1086 if(sb->sboff == moff) /* At end of the block? */
1087 { if(sm->smlen > moff) /* Yes, have room? */
1088 goto sekw; /* Win, go setup and ret */
1089 if(sm->smforw /* If next mem blk */
1090 && (sm->smforw->smflags /* Can have bytes */
1091 & (SM_USE|SM_NXM))==0 /* stolen from it */
1092 && (sd->sdflags&SD_MOD) /* and we ain't pure*/
1093 && sm->smlen < cmax) /* and not too big */
1094 { /* Then steal some core!! Note that without
1095 * the size test, a stream of putc's could
1096 * create a monster block gobbling all mem.
1098 cmin = cmax - sm->smlen;
1099 if(cmin&01) cmin++; /* Ensure wd-align */
1100 if(sm->smforw->smlen <= cmin)
1101 { sbm_mmrg(sm);
1102 goto sekw;
1104 sm->smforw->smlen -= cmin;
1105 sm->smforw->smaddr += cmin;
1106 sm->smlen += cmin;
1107 goto sekw;
1109 /* Last try... check next logical blk for room */
1110 if(sd->slforw && (sm = sd->slforw->sdmem)
1111 && sm->smuse == 0
1112 && sm->smlen)
1113 { sd = sbx_next(sb); /* Yup, go there */
1114 goto sekw;
1118 /* Middle of block, split up to insert */
1119 sbx_split(sd, sb->sboff); /* Split at IO ptr */
1120 if(sd->sdmem) /* Unless blk now empty, */
1121 { sd = sbx_next(sb); /* move to next. */
1122 if(sd->sdmem) /* If not empty either */
1123 sbx_split(sd, (chroff) 0); /* Split again */
1126 /* Have empty SD block, get some mem for it */
1127 sekwget: sd->sdmem = sm = sbx_mget(cmin,cmax);
1128 sm->smuse = 0;
1129 sekw: sbx_sbrdy(sb); /* Sets up sbwleft... */
1130 return(1);
1132 case SK_DELF: /* Delete forward */
1133 if(sb->sboff == 0) /* At block beg already? */
1134 return(sd); /* Win, return it */
1135 sbx_split(sd, sb->sboff); /* No, split up and */
1136 return(sbx_next(sb)); /* return ptr to 2nd part */
1138 case SK_DELB: /* Delete backward (force EOB align) */
1139 if(sb->sboff != /* If not at EOB already, */
1140 (sm ? (chroff)(sm->smuse) : sd->sdlen))
1141 sbx_split(sd, sb->sboff); /* Then split */
1142 return(sd); /* And return ptr to 1st part */
1143 break;
1145 default:
1146 return(0);
1147 } /* End of switch */
1148 return(1);
1151 struct sdblk *
1152 sbx_next (sbp)
1153 SBBUF *sbp;
1154 { register SBBUF *sb;
1155 register struct sdblk *sd, *sdf;
1156 if((sdf = (sd = (sb=sbp)->sbcur)->slforw) == 0)
1157 return((struct sdblk *)0);
1158 sb->sbdot += (sd->sdmem ? (chroff)sd->sdmem->smuse : sd->sdlen);
1159 sb->sboff = 0;
1160 sd->sdflags &= ~SD_LOCK; /* Unlock current */
1161 sdf->sdflags |= SD_LOCK; /* Lock next */
1162 sb->sbcur = sdf;
1163 return(sdf);
1166 /* SBX_NORM(sb,mode) - Normalizes I/O position as desired.
1167 * The SBBUF must have I/O disconnected (SBIOP==0).
1168 * Adjusts SBCUR, SBDOT, and SBOFF so that SBOFF is guaranteed
1169 * to point to a location in the current SD block.
1170 * The mode flags determine action when there is more than
1171 * one possible SD that could be pointed to, as is the case
1172 * when the I/O pos falls on a block boundary (possibly with
1173 * adjacent zero-length blocks as well).
1174 * SKM_0BACK - Zero-skip direction.
1175 * 0 = Skip forward over zero-length blocks.
1176 * set = Skip backward over zero-length blocks.
1177 * SKM_EOB - Block-end selection (applies after skipping done).
1178 * 0 = Point to BOB (Beginning Of Block).
1179 * set = Point to EOB (End Of Block).
1180 * Returns the new current SD as a convenience.
1181 * Notes:
1182 * The SKM_0BACK flag value is a special hack to search in
1183 * the right direction when SBOFF is initially 0.
1184 * None of the mode flags have any effect if the I/O pos falls
1185 * within a block.
1186 * Perhaps this routine should flush the zero-length blks it
1187 * finds, if they're not locked??
1189 struct sdblk *
1190 sbx_norm(sbp,mode)
1191 SBBUF *sbp;
1192 int mode;
1193 { register struct sdblk *sd;
1194 register struct smblk *sm;
1195 register SBBUF *sb;
1196 chroff len;
1198 if((sd = (sb=sbp)->sbcur) == 0)
1199 { sb->sbdot = 0;
1200 sb->sboff = 0;
1201 return(sd);
1203 sd->sdflags &= ~SD_LOCK; /* Unlock current blk */
1205 if(sb->sboff >= (mode&01)) /* Hack hack to get right skip */
1206 for(;;) /* Scan forwards */
1207 { if(sm = sd->sdmem) /* Get length of this blk */
1208 len = sm->smuse;
1209 else len = sd->sdlen;
1210 if(sb->sboff <= len)
1211 if(sb->sboff < len /* If == and fwd 0-skip, continue */
1212 || (mode&SKM_0BACK))
1213 { if((mode&SKM_EOB) /* Done, adjust to EOB? */
1214 && sb->sboff == 0 /* Yes, are we at BOB? */
1215 && sd->slback) /* and can do it? */
1216 { sd = sd->slback; /* Move to EOB */
1217 sb->sboff = (sm = sd->sdmem)
1218 ? (chroff)(sm->smuse) : sd->sdlen;
1219 sb->sbdot -= sb->sboff;
1221 break;
1223 if(sd->slforw == 0) /* At EOF? */
1224 { sb->sboff = len;
1225 break;
1227 sd = sd->slforw;
1228 sb->sboff -= len;
1229 sb->sbdot += len;
1231 else /* Scan backwards */
1232 for(;;)
1233 { if(sd->slback == 0) /* At BOF? */
1234 { sb->sboff = 0;
1235 sb->sbdot = 0; /* Should already be 0, but... */
1236 break;
1238 sd = sd->slback;
1239 if(sm = sd->sdmem) /* Get length of this blk */
1240 len = sm->smuse;
1241 else len = sd->sdlen;
1242 sb->sbdot -= len;
1243 if((sb->sboff += len) >= 0)
1244 if(sb->sboff > 0 /* If == 0 and bkwd 0-skip, continue */
1245 || !(mode&SKM_0BACK))
1246 { if((mode&SKM_EOB) == 0 /* Done, adjust to BOB? */
1247 && sb->sboff == len /* Yes, are we at EOB? */
1248 && sd->slforw) /* and can do it? */
1249 { sd = sd->slforw; /* Move to BOB */
1250 sb->sboff = 0;
1251 sb->sbdot += len;
1253 break;
1256 sb->sbcur = sd;
1257 sd->sdflags |= SD_LOCK;
1258 return(sd);
1262 struct sdblk *
1263 sbx_beg(sdp)
1264 struct sdblk *sdp;
1265 { register struct sdblk *sd, *sdx;
1266 if(sd = sdp)
1267 while(sdx = sd->slback)
1268 sd = sdx;
1269 return(sd);
1273 sbx_smdisc(sbp)
1274 SBBUF *sbp;
1275 { register SBBUF *sb;
1276 register struct smblk *sm;
1277 register struct sdblk *sd;
1279 sb = sbp;
1280 if((sd = sb->sbcur) == 0
1281 || (sm = sd->sdmem) == 0)
1282 return;
1283 if(sb->sbflags&SB_WRIT)
1284 { sm->smuse = sb->sbiop - sm->smaddr;
1285 sb->sbflags &= ~SB_WRIT;
1287 sb->sboff = sb->sbiop - sm->smaddr;
1288 sb->sbiop = 0;
1289 sb->sbrleft = sb->sbwleft = 0;
1292 sbx_sbrdy(sbp) /* Sets up SBIOP, SBRLEFT, SBWLEFT */
1293 SBBUF *sbp;
1294 { register SBBUF *sb;
1295 register struct sdblk *sd;
1296 register struct smblk *sm;
1298 if((sd = (sb=sbp)->sbcur) == 0
1299 || (sm = sd->sdmem) == 0)
1300 return;
1301 sd->sdflags |= SD_LOCK;
1302 sb->sbiop = sm->smaddr + sb->sboff;
1303 if(sb->sbrleft = sm->smuse - sb->sboff)
1304 sb->sbwleft = 0;
1305 else sb->sbwleft = sm->smlen - sm->smuse;
1309 /* SBX_SCPY(sd,sdl) - Copies given sbstring, returns ptr to new sbstring.
1310 * Only goes as far as sdl (last copied blk); 0 for entire sbstring.
1312 struct sdblk *
1313 sbx_scpy(sdp,sdlast)
1314 struct sdblk *sdp, *sdlast;
1315 { register struct sdblk *sd, *sd2, *sdn;
1316 struct sdblk *sdr;
1318 if((sd = sdp) == 0) return((struct sdblk *)0);
1319 sdn = 0;
1320 do {
1321 sd->sdflags |= SD_LCK2;
1322 sd2 = sbx_sdcpy(sd);
1323 if(sd2->slback = sdn)
1324 { sdn->slforw = sd2;
1325 sdn->sdflags &= ~SD_LOCKS;
1327 else sdr = sd2; /* Save 1st */
1328 sdn = sd2;
1329 sd->sdflags &= ~SD_LCK2;
1330 } while(sd != sdlast && (sd = sd->slforw));
1331 sd2->slforw = 0;
1332 sd2->sdflags &= ~SD_LOCKS;
1333 return(sdr);
1337 /* SBX_SDCPY(sd) - Copies given sdblk, returns ptr to new blk.
1338 * Does not set locks, assumes caller does this (which it MUST,
1339 * to avoid compaction lossage!)
1342 struct sdblk *
1343 sbx_sdcpy(sdp)
1344 struct sdblk *sdp;
1345 { register struct sdblk *sd, *sd2;
1346 register struct smblk *sm, *sm2;
1348 if((sd = sdp) == 0) return((struct sdblk *)0);
1349 sd2 = sbx_ndget(); /* Get a free sdblk */
1350 bcopy((SBMA)sd, (SBMA)sd2, sizeof(struct sdblk)); /* Copy sdblk data */
1351 sd2->slforw = 0; /* Don't let it think it's on a list */
1352 sd2->slback = 0;
1353 if(sd2->sdfile) /* If has disk copy, */
1354 { sd->sdforw = sd2; /* Fix phys list ptrs */
1355 sd2->sdback = sd;
1356 if(sd2->sdforw)
1357 sd2->sdforw->sdback = sd2;
1359 if(sm = sd2->sdmem) /* If has in-core copy, try to */
1360 { if(sm2 = sbm_mget(sm->smuse,sm->smuse)) /* get mem for it */
1361 { bcopy(sm->smaddr,sm2->smaddr,sm->smuse);
1362 sm2->smuse = sm->smuse;
1363 sd2->sdmem = sm2; /* Point new sd to copy */
1365 else /* Can't get mem... */
1366 { if(sd2->sdflags&SD_MOD)
1367 sbx_aout(sd2,1); /* Swap out the blk */
1368 sd2->sdmem = 0; /* Don't have incore copy */
1371 return(sd2);
1374 /* SBX_XCIS(sbp,coff,&sdp2,adot) - Internal routine to excise a sbstring,
1375 * defined as everything between current location and given offset.
1376 * SD to first sdblk is returned (0 if error)
1377 * SD2 (address passed as 3rd arg) is set to last sdblk.
1378 * Both are locked with LCK2 to ensure that pointers are valid.
1379 * The current location at time of call is also returned via adot.
1381 struct sdblk *
1382 sbx_xcis(sbp,num,asd2,adot)
1383 SBBUF *sbp;
1384 chroff num, *adot;
1385 struct sdblk **asd2;
1386 { register SBBUF *sb;
1387 register struct sdblk *sd, *sd2;
1388 int dirb;
1390 if((sb = sbp) == 0) return((struct sdblk *)0);
1391 dirb = 0; /* Delete forward */
1392 if(num == 0) return((struct sdblk *)0); /* Delete nothing */
1393 if(num < 0) dirb++; /* Delete backward */
1395 if((sd = (struct sdblk *)
1396 sbx_ready(sb, (dirb ? SK_DELB : SK_DELF))) == 0)
1397 return((struct sdblk *)0); /* Maybe nothing there */
1398 sd->sdflags |= SD_LCK2; /* Lock up returned SD */
1399 *adot = sb->sbdot; /* Save current location */
1400 sb->sboff += num; /* Move to other end of range */
1402 if((sd2 = (struct sdblk *)
1403 sbx_ready(sb,(dirb ? SK_DELF : SK_DELB))) == 0)
1404 { sd->sdflags &= ~SD_LCK2; /* This shd never happen if */
1405 return( /* we got this far, but... */
1406 (struct sdblk *)sbx_err(0,"KILLN SD2 failed"));
1408 sd2->sdflags |= SD_LCK2; /* Lock up other end of stuff */
1410 /* SD and SD2 now delimit bounds of stuff to excise.
1411 * Now do direction dependent fixups
1413 if(dirb)
1414 { /* Backward, current sbdot is ok but must get SD/SD2
1415 * into first/last order. Also, due to nature of block
1416 * splitups, a backward delete within single block will leave
1417 * SD actually pointing at predecessor block.
1419 if(sd->slforw == sd2) /* If SD became pred, fix things. */
1420 { sd->sdflags &= ~SD_LCK2; /* Oops, unlock! */
1421 sd = sd2;
1423 else /* Just need to swap SD, SD2 ptrs. */
1424 { /* Goddamit why doesn't C have an */
1425 /* exchange operator??? */
1426 *asd2 = sd;
1427 return(sd2);
1430 *asd2 = sd2;
1431 return(sd);
1434 /* SBX_SPLIT(sd,chroff) - Splits block SD at point CHROFF (offset from
1435 * start of block). SD remains valid; it is left locked.
1436 * The smblk is split too, if one exists, and SMUSE adjusted.
1437 * If offset 0, or equal to block length, the 1st or 2nd SD respectively
1438 * will not have a smblk and its sdlen will be 0.
1439 * (Note that if a smblk exists, a zero sdlen doesn't indicate much)
1441 struct sdblk *
1442 sbx_split(sdp, coff)
1443 struct sdblk *sdp;
1444 chroff coff;
1445 { register struct sdblk *sd, *sdf, *sdx;
1447 if((sd=sdp) == 0)
1448 return((struct sdblk *)0);
1449 sd->sdflags |= SD_LOCK;
1450 if(sd->sdflags&SD_MOD) /* If block has been munged, */
1451 sbx_npdel(sd); /* Flush from phys list now. */
1452 sdf = sbx_ndget(); /* Get a sdblk node */
1453 bcopy((SBMA)sd, (SBMA)sdf, (sizeof (struct sdblk))); /* Copy node */
1454 /* Note that the flags are copied, so both sdblks are locked and
1455 * safe from possible GC compaction during call to sbx_msplit...
1457 if(coff == 0) /* If offset was 0, */
1458 { /* then 1st SD becomes null */
1459 if(sdf->sdfile) /* Fix up phys links here */
1460 { if(sdx = sdf->sdback)
1461 sdx->sdforw = sdf;
1462 else sdf->sdfile->sfptr1 = sdf;
1463 if(sdx = sdf->sdforw)
1464 sdx->sdback = sdf;
1466 sdx = sd;
1467 goto nulsdx;
1469 else if(sd->sdmem)
1470 if(coff >= sd->sdmem->smuse)
1471 goto nulsdf;
1472 else sdf->sdmem = sbx_msplit(sd->sdmem, (SBMO)coff);
1473 else if(coff >= sd->sdlen)
1474 nulsdf: { sdx = sdf;
1475 nulsdx: sdx->sdforw = 0;
1476 sdx->sdback = 0;
1477 sdx->sdmem = 0;
1478 sdx->sdfile = 0;
1479 sdx->sdlen = 0;
1480 sdx->sdaddr = 0;
1481 goto nulskp;
1483 if(sd->sdfile)
1484 { sdf->sdlen -= coff; /* Set size of remainder */
1485 sdf->sdaddr += coff; /* and address */
1486 sd->sdlen = coff; /* Set size of 1st part */
1488 /* Link 2nd block into proper place in physical sequence.
1489 * 1st block is already in right place. Search forward until
1490 * find a block with same or higher disk address, and insert
1491 * in front of it. If sdlen is zero, just flush the links,
1492 * which is OK since the 1st block is what's pointed to anyway.
1494 if(sdf->sdlen > 0)
1495 { while((sdx = sd->sdforw) /* Find place to insert */
1496 && sdf->sdaddr > sdx->sdaddr)
1497 sd = sdx;
1498 sdf->sdback = sd; /* Link following sd. */
1499 if(sdf->sdforw = sd->sdforw)
1500 sdf->sdforw->sdback = sdf;
1501 sd->sdforw = sdf;
1502 sd = sdp; /* Restore pointer */
1504 else
1505 { sdf->sdforw = 0;
1506 sdf->sdback = 0;
1507 sdf->sdfile = 0; /* Say no disk */
1511 nulskp: sdf->slback = sd; /* Link in logical sequence */
1512 if(sd->slforw)
1513 sd->slforw->slback = sdf;
1514 sd->slforw = sdf;
1516 sdf->sdflags &= ~SD_LOCKS; /* Unlock 2nd but not 1st */
1517 return(sd); /* Note sd, not sdf */
1520 /* SBX_MSPLIT - Like sbm_split but never fails, and sets
1521 * SMUSE values appropriately
1523 struct smblk *
1524 sbx_msplit(smp, size)
1525 struct smblk *smp;
1526 SBMO size;
1527 { register struct smblk *sm, *smx;
1528 register int lev;
1530 lev = 0;
1531 while((smx = sbm_split((sm = smp), size)) == 0)
1532 sbx_comp(SB_BUFSIZ,lev++); /* Need to get some smblk nodes */
1533 if(sm->smlen >= sm->smuse) /* Split across used portion? */
1534 smx->smuse = 0; /* Nope, new blk is all free */
1535 else
1536 { smx->smuse = sm->smuse - sm->smlen;
1537 sm->smuse = sm->smlen;
1539 return(smx);
1542 /* SBX_NDEL - flush a SD and associated SM. Fixes up logical
1543 * and physical links properly. Returns ptr to next logical SD.
1544 * NOTE: if sd->slback does not exist, the returned SD is your
1545 * only hold on the list, since the SD gets flushed anyway!
1547 struct sdblk *
1548 sbx_ndel(sdp)
1549 struct sdblk *sdp;
1550 { register struct sdblk *sd, *sdx;
1551 register struct smblk *sm;
1553 sd = sdp;
1554 if(sm = sd->sdmem) /* If smblk exists, */
1555 { sbm_mfree(sm); /* flush it. */
1556 sd->sdmem = 0;
1558 if(sdx = sd->slback)
1559 sdx->slforw = sd->slforw;
1560 if(sd->slforw)
1561 sd->slforw->slback = sdx; /* May be zero */
1563 /* Logical links done, now hack phys links */
1564 if(sd->sdfile) /* Have phys links? */
1565 sbx_npdel(sd); /* Yes, flush from phys list */
1567 sdx = sd->slforw;
1568 sbx_ndfre(sd);
1569 return(sdx);
1572 sbx_npdel(sdp)
1573 struct sdblk *sdp;
1574 { register struct sdblk *sd, *sdx;
1575 register struct sbfile *sf;
1577 if((sf = (sd=sdp)->sdfile) == 0)
1578 return;
1579 if(sdx = sd->sdback) /* Start of disk file? */
1580 sdx->sdforw = sd->sdforw;
1581 else
1582 sf->sfptr1 = sd->sdforw;
1583 if(sdx = sd->sdforw)
1584 sdx->sdback = sd->sdback;
1585 sd->sdfile = 0;
1586 sd->sdlen = 0;
1590 struct sdblk *sbx_nfl; /* Pointer to sdblk node freelist */
1592 struct sdblk *
1593 sbx_ndget() /* Like sbm_nget but never fails! */
1594 { register struct sdblk *sd;
1595 register int lev;
1597 lev = 0;
1598 while((sd = sbx_nfl) == 0 /* Get a node */
1599 /* If fail, make more */
1600 && (sd = sbm_nmak((sizeof (struct sdblk)),SM_DNODS)) == 0)
1601 /* If still fail, try GC */
1602 sbx_comp(sizeof(struct sdblk)*SM_DNODS,lev++);
1604 sbx_nfl = sd->slforw; /* Take it off freelist */
1605 sd->sdflags = SD_NID;
1606 return(sd); /* Return ptr to it */
1609 sbx_ndfre(sdp)
1610 struct sdblk *sdp;
1611 { register struct sdblk *sd;
1612 (sd = sdp)->sdflags = 0;
1613 sd->slforw = sbx_nfl;
1614 sbx_nfl = sd;
1617 SBMA
1618 sbx_malloc(size)
1619 unsigned size;
1621 register int lev;
1622 register SBMA res;
1624 lev = 0;
1625 while((res = (SBMA)malloc(size)) == 0)
1626 sbx_comp(size,lev++);
1627 return(res);
1630 struct smblk *
1631 sbx_mget(cmin,cmax) /* like sbm_mget but never fails! */
1632 SBMO cmin, cmax;
1633 { register struct smblk *sm;
1634 register int lev, csiz;
1636 lev = 0;
1637 csiz = cmax;
1638 for(;;)
1639 { if(sm = sbm_mget(csiz,cmax))
1640 return(sm); /* Won right off... */
1641 sbx_comp(csiz,lev++); /* Barf, invoke GC */
1642 if(sm = sbm_mget(csiz,cmax)) /* Then try again */
1643 return(sm);
1644 if((csiz >>= 1) < cmin) /* If still short, reduce */
1645 csiz = cmin; /* request down to min */
1649 chroff sbv_taddr; /* Disk addr of place to write into (set by TSET) */
1650 struct sdblk *sbv_tsd; /* SD that disk addr comes after (set by TSET) */
1652 #define sbx_qlk(sd) (sd->sdflags&SD_LOCKS)
1654 #if 0
1655 This is the compaction routine, which is the key to the
1656 entire scheme. Paging text to and from disk is trivial, but the
1657 ability to merge blocks is the important thing since it allows
1658 flushing the pointer information as well as the actual text! This
1659 eliminates fragmentation as a fatal problem.
1660 There are a variety of ways that storage can be reclaimed:
1662 - "pure" in-core blocks can be flushed instantly.
1663 - "impure" incore blocks can be written to tempfile storage and flushed.
1664 - The SM node freelist can be compacted so as to flush memory which is
1665 used for nothing but holding free nodes.
1666 - The SD node freelist can be compacted, ditto.
1667 - SBBUFs can be compacted, by:
1668 - Merging logically & physically adjacent on-disk pieces
1669 - merging logically & physically adjacent in-core pieces
1670 - merging logically adjacent in-core pieces
1671 - merging logically adjacent disk pieces, by reading in
1672 and then writing out to tempfile storage.
1673 Worst case would reduce whole sbstr to single tempfile block.
1675 Problems:
1676 What is "optimal" algorithm for typical usage?
1677 Must go over all code to make sure right things get locked
1678 and unlocked to avoid having rug pulled out from under.
1679 Could have optional "registration table" for sbstruc; if exist
1680 in table, can check during GC. If find one, can first
1681 do sbx_smdisc and then repoint sbcur to 1st block,
1682 with sbdot of 0 and sboff of sb_tell(). This allows
1683 reducing whole thing to one block even tho "locked".
1684 Never touch stuff locked with SD_LCK2, though.
1685 Also may need way to protect the sbstr SD actually being
1686 pointed to by current sbx routine processing.
1687 Could have count of # nodes free for SM and SD; don''t GC
1688 unless # is some number greater than size of a node block!
1689 Have different levels of compaction; pass level # down thru calls
1690 so as to invoke progressively sterner compaction measures.
1691 Can invoke sbx_comp with any particular level!
1692 Must have list somewhere of SBBUFs? or maybe OK to scan core
1693 for SM_DNODS, then scan sdblks?
1694 Screw: could happen that stuff gets flushed (cuz pure) or even
1695 written out to tempfile, and then we have to read it back
1696 in so as to compact more stuff into tempfile... how to avoid?
1697 If pure stuff small and next to impure stuff, merge?
1698 Some calls just want to get another free node and don''t need
1699 new core. How to indicate this? How to decide between
1700 freeing up used nodes, and creating new node freelist?
1701 #endif /*COMMENT*/
1702 /* Compact stuff.
1703 * General algorithm for getting storage is:
1704 * 1) allocate from freelist if enough there
1705 * 2) find unlocked pure smblk to free up
1706 * 3) find unlocked impure smblks, write out.
1707 * 4) Compact stuff by reducing # of sdblks. This is key to scheme!
1708 * Otherwise fragmentation will kill program.
1709 * Maybe put age cnt in each sbstr? Bump global and set cntr each time
1710 * sbstr gets major hacking (not just getc/putc).
1712 extern struct smblk *sbm_list;
1713 sbx_comp(cmin,lev)
1714 int cmin, lev;
1715 { int sbx_sdgc();
1717 if(lev > 100) /* If program has no way to handle this, */
1718 abort(); /* then simply blow up. */
1719 if(lev > 10) /* Too many iterations? Try to warn. */
1720 return(sbx_err(0,"GC loop, cannot free block of size %d",
1721 cmin));
1723 /* Step thru core hunting for SD node blocks */
1724 sbm_nfor(SM_DNODS,sizeof(struct sdblk),sbx_sdgc,lev);
1727 /* Do GC stuff on a sdblk. Guaranteed to exist, but may be locked */
1728 sbx_sdgc(sdp,lev)
1729 struct sdblk *sdp;
1730 int lev;
1731 { register struct sdblk *sd, *sdf;
1732 register struct smblk *sm;
1733 struct smblk *smf, *sbm_exp ();
1734 SBMO more;
1736 sd = sdp;
1737 if(sbx_qlk(sd)) return(0);
1738 sm = sd->sdmem;
1739 sdf = sd->slforw;
1740 if (lev < 4) goto lev3;
1742 /* Level 4 - write out everything possible */
1743 /* Back up to start of sbstr */
1744 while((sdf = sd->slback) && !sbx_qlk(sdf))
1745 sd = sdf;
1746 if((sdf = sd->slforw) == 0 /* If only 1 blk, ensure on disk */
1747 || sbx_qlk(sdf))
1748 { if(sm = sd->sdmem)
1749 { if(sd->sdflags&SD_MOD) /* If impure, */
1750 sbx_aout(sd, 1); /* swap out the SD */
1751 sbm_mfree(sm);
1752 sd->sdmem = 0;
1754 return(0);
1756 /* At least two blocks in string. Set up for flushout. */
1757 sbx_aout(sd, 0); /* Swapout as much of sbstring as possible */
1758 return(0);
1760 lev3: /* Level 3 - write out more */
1761 lev2: /* Level 2 - write out all impure & small pure */
1762 lev1: if(lev >= 1) /* Level 1 - merge small impure & small pure */
1763 { if(!sm || !sdf) return(0);
1764 while(((smf = sdf->sdmem) && !(sdf->sdflags&SD_LOCKS)
1765 && (more = smf->smuse + sm->smuse) < SB_BUFSIZ) )
1766 { if(sm->smforw != smf
1767 && more > sm->smlen) /* If need more rm */
1768 { sm = sbm_exp(sm,more); /* Get it */
1769 if(!sm) return(0); /* If none, stop */
1770 sd->sdmem = sm;
1772 bcopy(smf->smaddr,
1773 sm->smaddr + sm->smuse, smf->smuse);
1774 sm->smuse = more;
1775 if(sm->smforw == smf)
1776 { sdf->sdmem = 0;
1777 sbm_mmrg(sm); /* Merge */
1778 if(sm->smlen > more+SB_SLOP)
1779 sbm_mfree(sbm_split(sm, more));
1780 /* Guaranteed to win since mmrg
1781 * just freed a mem node */
1783 sd->sdflags |= SD_MOD;
1784 if(sdf = sbx_ndel(sdf))
1785 continue;
1786 return(0);
1790 if(lev <= 0) /* Level 0 - free up large pure blocks */
1791 /* Also merge blocks which are adjacent on disk */
1792 { if(sm)
1793 { if(sm->smuse == 0)
1794 sd->sdlen = 0;
1795 else if((sd->sdflags&SD_MOD) == 0
1796 && sm->smuse > 64)
1797 { sbm_mfree(sm);
1798 sd->sdmem = 0;
1799 goto lev0adj;
1801 else goto lev0adj;
1804 if(sd->sdlen == 0 /* Free zero blocks */
1805 && sd->slback) /* Make sure don't lose list */
1806 { sbx_ndel(sd);
1807 if((sd = sdf) == 0)
1808 return(0);
1809 sdf = sd->slforw;
1811 lev0adj: /* Merge blocks if adjacent on disk */
1812 /* This is common after reading thru large chunks
1813 * of a file but not modifying it much.
1815 if((sd->sdflags&SD_MOD) == 0 /* Pure */
1816 && sdf && (sdf->sdflags&(SD_LOCKS|SD_MOD)) == 0
1817 && sd->sdfile && (sd->sdfile == sdf->sdfile)
1818 && (sd->sdaddr + sd->sdlen) == sdf->sdaddr )
1819 { sd->sdlen += sdf->sdlen;
1820 sbx_ndel(sdf); /* Flush 2nd */
1821 if(sm = sd->sdmem)
1822 { sbm_mfree(sm);
1823 sd->sdmem = 0;
1826 return(0);
1828 return(0);
1831 /* SBX_AOUT - output ALL of a hackable sbstring starting at given sdblk.
1832 * Note that code is careful to do things so that an abort at any
1833 * time (e.g. write error) will still leave sbstring in valid state.
1834 * Flag value:
1835 * 0 - Writes out as many unlocked sdblks as possible, and merges
1836 * so that resulting sdblk (same one pointed to by arg)
1837 * incorporates all stuff written out.
1838 * 1 - Writes out single sdblk indicated, whether unlocked or not.
1839 * Doesn't free mem or merge anything; does update physlist
1840 * and flags.
1841 * 2 - Writes out all sdblks to specified FD/offset, no mods at all,
1842 * not even to physlist or flags. Good for saving files
1843 * when something seems wrong. (How to pass fd/off args?)
1844 * (offset arg not implemented, no need yet; 0 assumed)
1845 * Returns 0 if successful, else UNIX system call error number.
1848 sbx_aout(sdp,flag,fd)
1849 struct sdblk *sdp;
1850 int flag, fd;
1851 { register struct sdblk *sd;
1852 register struct smblk *sm;
1853 register int cnt;
1854 int ifd, ofd, skflg, rem;
1855 chroff inlen;
1856 extern SBMA sbm_lowaddr; /* Need this from SBM for rndrem */
1857 char buf[SB_BUFSIZ+16]; /* Get buffer space from stack! */
1858 /* Allow extra for word-align reads. */
1859 /* This should be +WDSIZE, but some */
1860 /* C compilers (eg XENIX) can't handle */
1861 /* "sizeof" arith in allocation stmts! */
1863 /* This flag and the two ptrs below are needed because UNIX
1864 * maintains only one I/O ptr per open file, and we can sometimes
1865 * be reading from/writing to the swapout file at same time.
1866 * Using DUP() to get a new FD (to avoid seeking back and forth)
1867 * won't help since both FD's will use the same I/O ptr!!!
1868 * Lastly, can't depend on returned value of LSEEK to push/pop
1869 * ptr, since V6 systems don't implement tell() or lseek() directly.
1870 * So we have to do it by hand...
1872 int botchflg;
1873 chroff outptr, inptr;
1875 if((sd = sdp)==0) return;
1876 ofd = sbv_tf.sffd; /* Default output FD */
1877 if(flag==0)
1878 { sbx_tset(sbx_qlen(sd),0);/* Find place for whole string */
1879 outptr = sbv_taddr; /* We'll have to update wrt ptr */
1881 else if (flag==1) /* Single SD block, so it's reasonable to
1882 * try aligning the output with the input. */
1883 { if(sm = sd->sdmem)
1884 { cnt = rndrem(sm->smaddr - sbm_lowaddr);
1885 sbx_tset((chroff)(sm->smuse),cnt);
1887 else
1888 { cnt = rndrem(sd->sdaddr);
1889 sbx_tset(sd->sdlen, cnt);
1891 outptr = sbv_taddr; /* We'll have to update wrt ptr */
1893 else /* Outputting a whole sbstring to a file */
1894 { ofd = fd;
1895 outptr = 0;
1898 for(; sd;)
1899 { if(flag==0 && sbx_qlk(sd))
1900 break; /* Stop when hit locked sdblk */
1901 if(sm = sd->sdmem)
1902 { if(cnt = sm->smuse)
1903 if(write(ofd, sm->smaddr, cnt) != cnt)
1904 return(sbx_err(errno,"Swapout wrt err"));
1905 outptr += cnt;
1906 if(flag==0)
1907 { sd->sdmem = 0; /* Flush the mem used */
1908 sbm_mfree(sm);
1910 inlen = cnt;
1912 else if(inlen = sd->sdlen)
1913 { if(sd->sdfile == 0)
1914 return(sbx_err(errno,"Sdfile 0, SD %o",sd));
1915 /* Foo on UNIX */
1916 botchflg = ((ifd = sd->sdfile->sffd) == ofd) ? 1 : 0;
1917 skflg = 1; /* Always seek first time */
1918 inptr = sd->sdaddr;
1919 /* Efficiency hack - set up for first read so that
1920 * transfer is word-aligned and terminates at end
1921 * of a disk block.
1923 rem = rndrem(inptr); /* Get alignment */
1924 cnt = SB_BUFSIZ - (int)(inptr%SB_BUFSIZ);
1925 while(inlen > 0)
1927 if(inlen < cnt) cnt = inlen;
1928 if(!sbx_rdf(ifd, buf+rem, cnt, skflg, inptr))
1929 return(sbx_err(errno,"Swapout err, SD %o",sd));
1930 /* Further seeks depend on botch setting */
1931 if(skflg = botchflg)
1932 { if(lseek(ofd,outptr,0) < 0)
1933 return(sbx_err(errno,
1934 "Swapout sk err"));
1935 inptr += cnt;
1937 if(write(ofd, buf+rem, cnt) != cnt)
1938 return(sbx_err(errno,
1939 "Swapout wrt err"));
1940 outptr += cnt;
1941 inlen -= cnt;
1942 cnt = SB_BUFSIZ; /* Now can use full blocks */
1943 rem = 0; /* Aligned nicely, too! */
1945 inlen = sd->sdlen;
1948 /* Text written out, now merge block in */
1949 if(flag == 2) /* No merge if saving file */
1950 goto donxt;
1951 if(sd != sdp) /* First block? */
1952 { sdp->sdlen += inlen; /* No, simple merge */
1953 sd = sbx_ndel(sd); /* Flush, get next */
1954 continue;
1957 /* Handle 1st block specially */
1958 if(sd->sdfile /* Unlink from phys list */
1959 && sd != sbv_tsd) /* Don't unlink if self */
1960 sbx_npdel(sd);
1961 sd->sdlen = inlen;
1962 sd->sdfile = &sbv_tf;
1963 sd->sdaddr = sbv_taddr; /* Set from sbx_tset val */
1964 sd->sdflags &= ~SD_MOD; /* On disk, no longer modified */
1966 /* Now insert into phys list at specified place */
1967 if(sd == sbv_tsd) /* If already same place */
1968 goto next; /* Skip linkin. */
1969 if(sd->sdback = sbv_tsd)
1970 { sd->sdforw = sbv_tsd->sdforw;
1971 sd->sdback->sdforw = sd;
1973 else
1974 { sd->sdforw = sbv_tf.sfptr1;
1975 sbv_tf.sfptr1 = sd;
1977 if(sd->sdforw)
1978 sd->sdforw->sdback = sd;
1980 next: if(flag==1) /* If only doing 1 sdblk, */
1981 break; /* stop here. */
1982 donxt: sd = sd->slforw; /* Done with 1st, get next */
1984 return(0); /* Win return, no errors */
1987 /* Returns hackable length of a sbstring (ends at EOF or locked block) */
1988 chroff
1989 sbx_qlen(sdp)
1990 struct sdblk *sdp;
1991 { register struct sdblk *sd;
1992 register struct smblk *sm;
1993 chroff len;
1995 len = 0;
1996 for(sd = sdp; sd && !sbx_qlk(sd); sd = sd->slforw)
1997 if(sm = sd->sdmem)
1998 len += (chroff)sm->smuse;
1999 else len += sd->sdlen;
2000 return(len);
2004 /* SBX_TSET - finds a place on temp swapout file big enough to hold
2005 * given # of chars. Sets SBV_TADDR to that location, as well
2006 * as seeking to it so the next write call will output there.
2007 * This location is guaranteed to have the requested
2008 * byte alignment (0 = word-aligned).
2010 sbx_tset(loff, align)
2011 chroff loff;
2012 int align;
2013 { register int fd;
2015 if(sbv_tf.sffd <= 0)
2016 { /* Must open the temp file! */
2017 /* Temporary file mechanism is system-dependent. Eventually this
2018 ** will probably require inclusion of a true c-env header file; for the
2019 ** time being, we can cheat a little by checking O_T20_WILD, which will
2020 ** be defined by <sys/file.h> on TOPS-20. Otherwise, we assume we are
2021 ** on a real Unix.
2023 #ifdef O_T20_WILD
2024 extern char *tmpnam(); /* Use ANSI function */
2025 fd = open(tmpnam((char *)NULL),
2026 O_RDWR | O_CREAT | O_TRUNC | O_BINARY);
2027 if(fd < 0)
2028 return(sbx_err(0,"Swapout creat err"));
2029 #else /* Real Unix */
2030 static char fcp[] = "/tmp/sbd.XXXXXX";
2031 if((fd = creat(mktemp(fcp),0600)) < 0)
2032 return(sbx_err(0,"Swapout creat err"));
2033 /* Must re-open so that we can both read and write to it */
2034 close(fd);
2035 if((fd = open(fcp,2)) < 0)
2036 return(sbx_err(0,"Swapout open err"));
2037 unlink(fcp); /* Set so it vanishes when we do */
2038 #endif
2040 sbv_tf.sffd = fd; /* Initialize the sbfile struct */
2041 sbv_tf.sfptr1 = 0;
2042 sbv_ftab[fd] = &sbv_tf; /* Record in table of all sbfiles */
2043 sbv_taddr = 0; /* "Return" this value */
2044 return; /* Ignore alignment for now */
2046 sbv_tsd = sbx_ffnd(&sbv_tf, loff+align, &sbv_taddr);
2047 sbv_taddr += align;
2048 if(lseek(sbv_tf.sffd, sbv_taddr, 0) < 0)
2049 return(sbx_err(0,"Swapout seek err: (%d,%ld,0) %d %s",
2050 sbv_tf.sffd, sbv_taddr, errno, strerror(errno)));
2054 /* SBX_FFND - searches disk list of given file for free space of
2055 * at least size chars. Note that list must be sorted by ascending
2056 * disk addrs in order for this to work! If sdaddrs are only
2057 * changed in SBX_SPLIT this will be true.
2058 * Sets "aloc" to disk address for writing (this is guaranteed to
2059 * be word-aligned, for efficiency), and returns SD ptr to
2060 * block which this addr should follow in the physical list. If ptr
2061 * is 0, it means addr should be 1st thing in list.
2063 struct sdblk *
2064 sbx_ffnd(sfp, size, aloc)
2065 SBFILE *sfp;
2066 chroff size, *aloc;
2067 { register struct sdblk *sd, *sds, *sdl;
2068 chroff cur;
2070 cur = 0;
2071 sds = 0;
2072 sd = sfp->sfptr1;
2073 redo: for(; sd ; sd = (sds=sd)->sdforw)
2074 { if(cur < sd->sdaddr) /* Gap seen? */
2075 { if(size <= (sd->sdaddr - cur)) /* Yes, big enuf? */
2076 break; /* Yup! */
2077 } /* No, bump. */
2078 else if(cur >=(sd->sdaddr + sd->sdlen)) /* No gap but chk */
2079 continue; /* No overlap, ok */
2080 /* Bump to next possible gap. */
2081 cur = sd->sdaddr + sd->sdlen;
2082 cur = (long)rndup(cur); /* Round up to word boundary! */
2084 *aloc = cur; /* Return winning addr */
2086 /* Perform verification check -- make sure this really is OK
2087 * and complain if not. If this never blows up, eventually can
2088 * take the check out.
2090 sdl = sd;
2091 for(sd = sfp->sfptr1; sd; sd = sd->sdforw)
2092 { if(cur < sd->sdaddr)
2093 { if(size <= (sd->sdaddr - cur))
2094 continue;
2096 else if(cur >= (sd->sdaddr + sd->sdlen))
2097 continue;
2099 sbx_err(0,"FFND blew it, but recovered. SD %o siz %ld",
2100 sd, size);
2101 sd = (sds = sdl)->sdforw;
2102 goto redo;
2106 return(sds); /* Return ptr to block this addr follows */
2109 sbx_rdf(fd,addr,cnt,skflg,loc)
2110 register int fd;
2111 char *addr;
2112 int skflg;
2113 chroff loc;
2114 { register int rres, eres;
2115 long lres;
2116 char *errtyp, *ftyp;
2117 chroff curlen;
2119 errno = 0;
2120 if(skflg && (lres = lseek(fd, (long)loc, 0)) == -1)
2121 { errtyp = "Sk err";
2122 goto errhan;
2124 if((rres = read(fd, addr, cnt)) != cnt)
2125 { lres = rres;
2126 errtyp = "Rd err";
2127 goto errhan;
2129 return(rres);
2130 errhan: /* Handle read or seek error */
2131 eres = errno;
2132 if(fd == sbv_tf.sffd) /* See if dealing with swapout file */
2133 { ftyp = "(swap)";
2134 curlen = 0;
2136 else { /* No, normal buffer file. */
2137 ftyp = "";
2138 curlen = sbx_fdlen(fd);
2139 if(sbv_ftab[fd] &&
2140 (curlen != sbv_ftab[fd]->sflen)) /* File changed? */
2141 if(sbx_rugpull(fd)) /* Yes, handle special case */
2142 return(cnt); /* Allow "win" return */
2144 sbx_err(0,"%s %d:%s, %ld:(%d%s,%o,%d)=%ld (fl %ld)",
2145 errtyp, eres, strerror(eres),
2146 loc, fd, ftyp, addr, cnt, lres,
2147 curlen);
2148 return(0);
2151 /* SBX_RUGPULL(fd) - Special routine called when package detects that
2152 * the file indicated by the fd has changed since its original
2153 * opening. This can happen when a file is over-written by some
2154 * other program (ED, for example).
2155 * This means that all sdblks which reference this fd
2156 * are probably bad. Pass special error back up to the calling
2157 * program to give it a chance at doing something.
2158 * Extra credit: scan all sdblks and unpurify all which point to this
2159 * file, so as to protect everything we still remember about it.
2160 * Otherwise a GC could flush pure in-core portions.
2162 sbx_rugpull(fd) /* FD already known to have entry in sbv_ftab */
2163 register int fd;
2164 { int sbx_unpur();
2166 /* First scan all sdblks to save what we still have. */
2167 /* This does NOT remove the sdfile pointer, so we can still
2168 * find blocks that are affected. */
2169 sbm_nfor(SM_DNODS, sizeof(struct sdblk), sbx_unpur, sbv_ftab[fd]);
2171 if((int)sbv_debug == 1 || !sbv_debug)
2172 return(0); /* Nothing else we can do */
2173 return((*sbv_debug)(2,(int *)0,"",fd)); /* Let caller handle it */
2175 sbx_unpur(sd, sf) /* Auxiliary routine for SBX_RUGPULL */
2176 register struct sdblk *sd;
2177 register struct sbfile *sf;
2178 { if(sd->sdfile == sf /* If sdblk belongs to affected file */
2179 && sd->sdmem) /* and has in-core version of text, */
2180 sd->sdflags |= SD_MOD; /* then ensure core version is used */
2183 sbx_err(val,str,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12)
2184 char *str;
2185 { int *sptr;
2187 sptr = (int *) &sptr; /* Point to self on stack */
2188 sptr += 5; /* Point to return addr */
2189 if((int)sbv_debug == 1)
2190 { abort();
2192 if(sbv_debug)
2193 (*sbv_debug)(1,*sptr,str,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12);
2194 return(val);