source: trunk/minix/commands/elle/sbstr.c@ 9

Last change on this file since 9 was 9, checked in by Mattia Monga, 13 years ago

Minix 3.1.2a

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