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.
16 sbbuffer
- old sbstr
. Abbrev
& struct "sbbuff". Macro SBBUFF
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
:
45 All other elements
, including SBIOP
, should also be
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
)
52 SBCUR
&& SBIOP
----OPEN
/READY
----
53 SBCUR points to a
SDBLK (SDMEM must exist
!)
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
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
.
97 SDBLKs are subject to unpredictable relocation
, compaction
,
98 and garbage collecting
. There are three ways in which a SDBLK can
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
???
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
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
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
.
174 /* Includes, initial definitions */
186 #include <sys/types.h>
187 #include <sys/stat.h>
189 #include <fcntl.h> /* For open() flags */
191 #include <sys/file.h> /* For open() flags */
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.
236 { register struct sdblk
*sd
;
240 if(!sbp
) return((SBBUF
*)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
);
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 */
265 /* SB_CLOSE(sb) - Close a SBBUF.
266 * Returns pointer to start of sbstring (first SD).
267 * Returns 0 if error.
272 { register SBBUF
*sb
;
273 register struct sdblk
*sd
;
275 if((sb
= sbp
) == 0) /* Verify pointer */
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 */
285 /* SB_SETOVW(sbp) - Set SBBUF Over-write mode for PUTC's.
286 * SB_CLROVW(sbp) - Clear ditto.
290 { register SBBUF
*sb
;
292 { sb
->sbflags
|= SB_OVW
;
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
315 struct sbfile
*sbv_ftab
[SB_NFILES
];
326 struct {int hiwd
; int lowd
;} foo
;
329 if(fstat(fd
,&statb
) < 0) return((chroff
)-1);
332 len
.hiwd
= statb
.i_size0
& 0377;
335 return((chroff
)statb
.st_size
);
342 { register struct sdblk
*sd
;
343 register struct sbfile
*sf
;
347 if((fd
= ifd
) < 0 || SB_NFILES
<= fd
/* Check for absurd FD */
348 || sbv_ftab
[fd
]) /* and slot already in use */
350 if((len
= sbx_fdlen(fd
)) < 0) return((SBSTR
*)0);
351 sbv_ftab
[fd
]= sf
= (struct sbfile
*)sbx_malloc(sizeof(struct sbfile
));
353 sf
->sfptr1
= sd
= sbx_ndget();
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?
377 { if(fd
>= SB_NFILES
) return(0); /* Error of sorts */
378 return(sbx_fcls(sbv_ftab
[fd
]));
382 sbx_fcls(sbv_ftab
[fd
]);
383 } while(--fd
); /* Doesn't try FD 0 ! */
389 { register struct sbfile
*sf
;
392 if((sf
= sfp
)==0 /* Ignore null args */
393 || sf
== &sbv_tf
) /* and never close our tempfile! */
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.
411 { register struct sdblk
*sd
;
412 register struct sbfile
*sf
;
414 if((sf
= sbv_ftab
[fd
]) == 0
415 || (sd
= sb
->sbcur
) == 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 */
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 */
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.
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 */
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.
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 */
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
498 * Returns EOF on error or end-of-string.
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.
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
;
528 sb
->sbflags
&= ~SB_WRIT
;
531 return sb_uchartoint(*--sb
->sbiop
); /* Return char */
533 if((int)sbx_ready(sb
,SK_READB
,SB_BUFSIZ
,0) <= 0)
535 return(sb_rgetc(sb
));
538 /* SB_RDELC(sb) - Delete backwards one char.
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
)
550 sd
->sdflags
|= SD_MOD
;
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.
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.
584 { register SBBUF
*sb
;
585 register struct sdblk
*sd
, *sd2
;
586 struct sdblk
*sdr
, *sdx
;
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
;
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.
636 { register SBBUF
*sb
;
637 register struct sdblk
*sd
, *sd2
;
642 if((sd
= sbx_xcis(sb
,num
,&sdr
,&savloc
)) == 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
658 { register SBBUF
*sb
;
659 register struct sdblk
*sd
, *sdx
;
665 if(sd
->slback
) /* Perhaps normalize to beg? */
667 if((sdx
= (struct sdblk
*)sbx_ready(sb
,SK_DELB
)) == 0) /* Get cur pos ready */
669 inslen
= sbs_len(sd
); /* Save length of inserted stuff */
671 sd
->slback
= sdx
; /* Fix up links */
673 { while(sd
->slforw
) /* Hunt for end of inserted sbstring */
675 sd
->slforw
= sdx
->slforw
;
676 sd
->slforw
->slback
= sd
;
679 sb
->sboff
+= inslen
; /* Set IO ptr to end of new stuff */
683 /* SBSTRING routines - operate on "bare" sbstrings. */
685 /* SBS_CPY(sd) - Copies given sbstring, returns ptr to new sbstring.
690 { return(sbx_scpy(sdp
,(struct sdblk
*)0));
693 /* SBS_DEL(sd) - Flush a sbstring.
697 { register struct sdblk
*sd
;
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).
710 struct sdblk
*sdp
,*sdp2
;
711 { register struct sdblk
*sd
, *sdx
;
714 { while(sdx
= sd
->slforw
)
716 if(sd
->slforw
= sdx
= sdp2
)
722 /* SBS_LEN(sd) - Find length of sbstring.
727 { register struct sdblk
*sd
;
728 register struct smblk
*sm
;
731 if((sd
= sdp
)==0) return((chroff
)0);
733 for(; sd
; sd
= sd
->slforw
)
735 len
+= (chroff
)sm
->smuse
;
736 else len
+= sd
->sdlen
;
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
)
757 { register SBBUF
*sb
;
758 register struct smblk
*sm
;
759 register struct sdblk
*sd
;
763 if((sd
= sb
->sbcur
) == 0) return(-1);
766 { case 0: if(coff
== 0) /* Optimize common case */
767 return(sb_rewind(sb
));
768 sb
->sboff
= coff
- sb
->sbdot
; /* Abs */
770 case 1: sb
->sboff
+= coff
; /* Rel */
772 case 2: sb
->sboff
+= sb_ztell(sb
) + coff
;
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 */
784 sb
->sbflags
&= ~SB_WRIT
;
786 sb
->sbwleft
= 0; /* Always gets zapped */
788 { case 0: /* Offset from beginning */
789 coff
-= sb
->sbdot
+ (chroff
)moff
; /* Make rel */
791 case 1: /* Offset from current loc */
794 case 2: /* Offset from end */
795 coff
+= sb_ztell(sb
);
800 /* COFF now has relative offset from current location */
801 if (-(chroff
)moff
<= coff
&& coff
<= sb
->sbrleft
)
802 { /* Win! Handle repos-within-smblk */
804 sb
->sbrleft
-= coff
; /* Set r; wleft already 0 */
808 /* Come here when moving to a different sdblk. */
811 sb
->sboff
= coff
+ (chroff
)moff
;
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.
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 */
836 /* SB_TELL(sb) - Get I/O ptr value for SBBUF.
837 * Returns -1 on errors.
843 { register SBBUF
*sb
;
844 register struct smblk
*sm
;
845 register struct sdblk
*sd
;
847 if((sd
= (sb
=sbp
)->sbcur
) == 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.
862 { register SBBUF
*sb
;
863 register struct smblk
*sm
;
864 register struct sdblk
*sd
;
866 if((sd
= (sb
=sbp
)->sbcur
) == 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
));
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?
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?
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.
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.
907 * If new block is allocated, CMIN and CMAX represent min, max sizes
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!
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.
920 sbx_ready(sbp
,type
,cmin
,cmax
)
924 { register SBBUF
*sb
;
925 register struct sdblk
*sd
;
926 register struct smblk
*sm
;
930 if((sd
= (sb
=sbp
)->sbcur
) == 0)
932 if(sb
->sbiop
) /* Canonicalize for given operation */
933 { if((sm
= sd
->sdmem
)==0)
935 moff
= sb
->sbiop
- sm
->smaddr
; /* Current block offset */
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 */
946 case SK_READB
: /* Read Backward */
947 if(moff
) /* Stuff there to read? */
948 { if(sb
->sbflags
&SB_WRIT
) /* Yup, turn writes off */
950 sb
->sbflags
&= ~SB_WRIT
;
958 case SK_WRITEF
: /* Writing */
960 sb
->sbwleft
= sm
->smlen
- moff
;
962 return(1); /* OK to write now */
963 /* NOTE: flags not set!!! */
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! */
977 case SK_DELB
: /* Delete backward - force EOB */
978 if(sb
->sbrleft
<= 0) /* Win if already EOB */
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 */
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 */
1009 case SK_READF
: /* Read Forward */
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 */
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 */
1058 sm
->smuse
= sd
->sdlen
;
1061 return(sbx_err(0,"No file")); /* Gasp? */
1062 if(!sbx_rdf(sd
->sdfile
->sffd
, sm
->smaddr
, sm
->smuse
,
1064 return(sbx_err(0,"Readin SD: %o", sd
));
1067 sekr2
: sbx_sbrdy(sb
); /* Make it current, pt to beg */
1068 sb
->sbwleft
= 0; /* Ensure not set (esp if READB) */
1071 case SK_WRITEF
: /* Write-type seek */
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);
1084 /* Block in memory */
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
)
1104 sm
->smforw
->smlen
-= cmin
;
1105 sm
->smforw
->smaddr
+= cmin
;
1109 /* Last try... check next logical blk for room */
1110 if(sd
->slforw
&& (sm
= sd
->slforw
->sdmem
)
1113 { sd
= sbx_next(sb
); /* Yup, go there */
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
);
1129 sekw
: sbx_sbrdy(sb
); /* Sets up sbwleft... */
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 */
1147 } /* End of switch */
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
);
1160 sd
->sdflags
&= ~SD_LOCK
; /* Unlock current */
1161 sdf
->sdflags
|= SD_LOCK
; /* Lock next */
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.
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
1186 * Perhaps this routine should flush the zero-length blks it
1187 * finds, if they're not locked??
1193 { register struct sdblk
*sd
;
1194 register struct smblk
*sm
;
1198 if((sd
= (sb
=sbp
)->sbcur
) == 0)
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 */
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
;
1223 if(sd
->slforw
== 0) /* At EOF? */
1231 else /* Scan backwards */
1233 { if(sd
->slback
== 0) /* At BOF? */
1235 sb
->sbdot
= 0; /* Should already be 0, but... */
1239 if(sm
= sd
->sdmem
) /* Get length of this blk */
1241 else len
= sd
->sdlen
;
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 */
1257 sd
->sdflags
|= SD_LOCK
;
1265 { register struct sdblk
*sd
, *sdx
;
1267 while(sdx
= sd
->slback
)
1275 { register SBBUF
*sb
;
1276 register struct smblk
*sm
;
1277 register struct sdblk
*sd
;
1280 if((sd
= sb
->sbcur
) == 0
1281 || (sm
= sd
->sdmem
) == 0)
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
;
1289 sb
->sbrleft
= sb
->sbwleft
= 0;
1292 sbx_sbrdy(sbp
) /* Sets up SBIOP, SBRLEFT, SBWLEFT */
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)
1301 sd
->sdflags
|= SD_LOCK
;
1302 sb
->sbiop
= sm
->smaddr
+ sb
->sboff
;
1303 if(sb
->sbrleft
= sm
->smuse
- sb
->sboff
)
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.
1313 sbx_scpy(sdp
,sdlast
)
1314 struct sdblk
*sdp
, *sdlast
;
1315 { register struct sdblk
*sd
, *sd2
, *sdn
;
1318 if((sd
= sdp
) == 0) return((struct sdblk
*)0);
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 */
1329 sd
->sdflags
&= ~SD_LCK2
;
1330 } while(sd
!= sdlast
&& (sd
= sd
->slforw
));
1332 sd2
->sdflags
&= ~SD_LOCKS
;
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!)
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 */
1353 if(sd2
->sdfile
) /* If has disk copy, */
1354 { sd
->sdforw
= sd2
; /* Fix phys list ptrs */
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 */
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.
1382 sbx_xcis(sbp
,num
,asd2
,adot
)
1385 struct sdblk
**asd2
;
1386 { register SBBUF
*sb
;
1387 register struct sdblk
*sd
, *sd2
;
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
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! */
1423 else /* Just need to swap SD, SD2 ptrs. */
1424 { /* Goddamit why doesn't C have an */
1425 /* exchange operator??? */
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)
1442 sbx_split(sdp
, coff
)
1445 { register struct sdblk
*sd
, *sdf
, *sdx
;
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
)
1462 else sdf
->sdfile
->sfptr1
= sdf
;
1463 if(sdx
= sdf
->sdforw
)
1470 if(coff
>= sd
->sdmem
->smuse
)
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;
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.
1495 { while((sdx
= sd
->sdforw
) /* Find place to insert */
1496 && sdf
->sdaddr
> sdx
->sdaddr
)
1498 sdf
->sdback
= sd
; /* Link following sd. */
1499 if(sdf
->sdforw
= sd
->sdforw
)
1500 sdf
->sdforw
->sdback
= sdf
;
1502 sd
= sdp
; /* Restore pointer */
1507 sdf
->sdfile
= 0; /* Say no disk */
1511 nulskp
: sdf
->slback
= sd
; /* Link in logical sequence */
1513 sd
->slforw
->slback
= 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
1524 sbx_msplit(smp
, size
)
1527 { register struct smblk
*sm
, *smx
;
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 */
1536 { smx
->smuse
= sm
->smuse
- sm
->smlen
;
1537 sm
->smuse
= sm
->smlen
;
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!
1550 { register struct sdblk
*sd
, *sdx
;
1551 register struct smblk
*sm
;
1554 if(sm
= sd
->sdmem
) /* If smblk exists, */
1555 { sbm_mfree(sm
); /* flush it. */
1558 if(sdx
= sd
->slback
)
1559 sdx
->slforw
= 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 */
1574 { register struct sdblk
*sd
, *sdx
;
1575 register struct sbfile
*sf
;
1577 if((sf
= (sd
=sdp
)->sdfile
) == 0)
1579 if(sdx
= sd
->sdback
) /* Start of disk file? */
1580 sdx
->sdforw
= sd
->sdforw
;
1582 sf
->sfptr1
= sd
->sdforw
;
1583 if(sdx
= sd
->sdforw
)
1584 sdx
->sdback
= sd
->sdback
;
1590 struct sdblk
*sbx_nfl
; /* Pointer to sdblk node freelist */
1593 sbx_ndget() /* Like sbm_nget but never fails! */
1594 { register struct sdblk
*sd
;
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 */
1611 { register struct sdblk
*sd
;
1612 (sd
= sdp
)->sdflags
= 0;
1613 sd
->slforw
= sbx_nfl
;
1625 while((res
= (SBMA
)malloc(size
)) == 0)
1626 sbx_comp(size
,lev
++);
1631 sbx_mget(cmin
,cmax
) /* like sbm_mget but never fails! */
1633 { register struct smblk
*sm
;
1634 register int lev
, csiz
;
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 */
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)
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
.
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
?
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
;
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",
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 */
1731 { register struct sdblk
*sd
, *sdf
;
1732 register struct smblk
*sm
;
1733 struct smblk
*smf
, *sbm_exp ();
1737 if(sbx_qlk(sd
)) return(0);
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
))
1746 if((sdf
= sd
->slforw
) == 0 /* If only 1 blk, ensure on disk */
1748 { if(sm
= sd
->sdmem
)
1749 { if(sd
->sdflags
&SD_MOD
) /* If impure, */
1750 sbx_aout(sd
, 1); /* swap out the SD */
1756 /* At least two blocks in string. Set up for flushout. */
1757 sbx_aout(sd
, 0); /* Swapout as much of sbstring as possible */
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 */
1773 sm
->smaddr
+ sm
->smuse
, smf
->smuse
);
1775 if(sm
->smforw
== smf
)
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
))
1790 if(lev
<= 0) /* Level 0 - free up large pure blocks */
1791 /* Also merge blocks which are adjacent on disk */
1793 { if(sm
->smuse
== 0)
1795 else if((sd
->sdflags
&SD_MOD
) == 0
1804 if(sd
->sdlen
== 0 /* Free zero blocks */
1805 && sd
->slback
) /* Make sure don't lose list */
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 */
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.
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
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
)
1851 { register struct sdblk
*sd
;
1852 register struct smblk
*sm
;
1854 int ifd
, ofd
, skflg
, rem
;
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...
1873 chroff outptr
, inptr
;
1875 if((sd
= sdp
)==0) return;
1876 ofd
= sbv_tf
.sffd
; /* Default output FD */
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
);
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 */
1899 { if(flag
==0 && sbx_qlk(sd
))
1900 break; /* Stop when hit locked sdblk */
1902 { if(cnt
= sm
->smuse
)
1903 if(write(ofd
, sm
->smaddr
, cnt
) != cnt
)
1904 return(sbx_err(errno
,"Swapout wrt err"));
1907 { sd
->sdmem
= 0; /* Flush the mem used */
1912 else if(inlen
= sd
->sdlen
)
1913 { if(sd
->sdfile
== 0)
1914 return(sbx_err(errno
,"Sdfile 0, SD %o",sd
));
1916 botchflg
= ((ifd
= sd
->sdfile
->sffd
) == ofd
) ? 1 : 0;
1917 skflg
= 1; /* Always seek first time */
1919 /* Efficiency hack - set up for first read so that
1920 * transfer is word-aligned and terminates at end
1923 rem
= rndrem(inptr
); /* Get alignment */
1924 cnt
= SB_BUFSIZ
- (int)(inptr
%SB_BUFSIZ
);
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
,
1937 if(write(ofd
, buf
+rem
, cnt
) != cnt
)
1938 return(sbx_err(errno
,
1939 "Swapout wrt err"));
1942 cnt
= SB_BUFSIZ
; /* Now can use full blocks */
1943 rem
= 0; /* Aligned nicely, too! */
1948 /* Text written out, now merge block in */
1949 if(flag
== 2) /* No merge if saving file */
1951 if(sd
!= sdp
) /* First block? */
1952 { sdp
->sdlen
+= inlen
; /* No, simple merge */
1953 sd
= sbx_ndel(sd
); /* Flush, get next */
1957 /* Handle 1st block specially */
1958 if(sd
->sdfile
/* Unlink from phys list */
1959 && sd
!= sbv_tsd
) /* Don't unlink if self */
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
;
1974 { sd
->sdforw
= sbv_tf
.sfptr1
;
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) */
1991 { register struct sdblk
*sd
;
1992 register struct smblk
*sm
;
1996 for(sd
= sdp
; sd
&& !sbx_qlk(sd
); sd
= sd
->slforw
)
1998 len
+= (chroff
)sm
->smuse
;
1999 else len
+= sd
->sdlen
;
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
)
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
2024 extern char *tmpnam(); /* Use ANSI function */
2025 fd
= open(tmpnam((char *)NULL
),
2026 O_RDWR
| O_CREAT
| O_TRUNC
| O_BINARY
);
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 */
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 */
2040 sbv_tf
.sffd
= fd
; /* Initialize the sbfile struct */
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
);
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.
2064 sbx_ffnd(sfp
, size
, aloc
)
2067 { register struct sdblk
*sd
, *sds
, *sdl
;
2073 redo
: for(; sd
; sd
= (sds
=sd
)->sdforw
)
2074 { if(cur
< sd
->sdaddr
) /* Gap seen? */
2075 { if(size
<= (sd
->sdaddr
- cur
)) /* Yes, big enuf? */
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.
2091 for(sd
= sfp
->sfptr1
; sd
; sd
= sd
->sdforw
)
2092 { if(cur
< sd
->sdaddr
)
2093 { if(size
<= (sd
->sdaddr
- cur
))
2096 else if(cur
>= (sd
->sdaddr
+ sd
->sdlen
))
2099 sbx_err(0,"FFND blew it, but recovered. SD %o siz %ld",
2101 sd
= (sds
= sdl
)->sdforw
;
2106 return(sds
); /* Return ptr to block this addr follows */
2109 sbx_rdf(fd
,addr
,cnt
,skflg
,loc
)
2114 { register int rres
, eres
;
2116 char *errtyp
, *ftyp
;
2120 if(skflg
&& (lres
= lseek(fd
, (long)loc
, 0)) == -1)
2121 { errtyp
= "Sk err";
2124 if((rres
= read(fd
, addr
, cnt
)) != cnt
)
2130 errhan
: /* Handle read or seek error */
2132 if(fd
== sbv_tf
.sffd
) /* See if dealing with swapout file */
2136 else { /* No, normal buffer file. */
2138 curlen
= sbx_fdlen(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
,
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 */
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
)
2187 sptr
= (int *) &sptr
; /* Point to self on stack */
2188 sptr
+= 5; /* Point to return addr */
2189 if((int)sbv_debug
== 1)
2193 (*sbv_debug
)(1,*sptr
,str
,a0
,a1
,a2
,a3
,a4
,a5
,a6
,a7
,a8
,a9
,a10
,a11
,a12
);