Annotation of researchv10no/cmd/bcp/Text.c, revision 1.1

1.1     ! root        1: /* Copyright (c) 1989, 1990 AT&T --- All Rights Reserved.              */
        !             2: /* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T.                */
        !             3: /* The copyright notice does not imply actual or intended publication. */
        !             4: /* AUTHORS:                                            */
        !             5: /*     H. S. Baird - ATT-BL MH - first versions        */
        !             6: 
        !             7: /* Text.c - functions for Document-image file ("dim" file) handling
        !             8: 
        !             9:    General Introduction
        !            10:    --------------------
        !            11: 
        !            12:    Dim files describe a document as a collection of records of these types:
        !            13:        Page    full page
        !            14:        Block   block (often a column) of text
        !            15:        Txtln   line of text
        !            16:        Word    word
        !            17:        Char    character, isolated symbol, `graph'
        !            18:        Interp  interpretation of a character (result of classification)
        !            19:        Sfeats  scalar features of a character
        !            20:        Shapes  local shape features of a character
        !            21:        Bfeats  binary indicator features of a character
        !            22:        Blob    connected component (maximal subset of 8-connected black pixels)
        !            23:        Lag     line-adjacency graph of runs
        !            24:        Run     horizontal run of black pixels
        !            25:         Pixel   bilevel (black/white) picture element (symbols are expected to
        !            26:                be black against a white background); usually square
        !            27: 
        !            28:    They may be organized hierarchically --
        !            29:        a Page may own :  Blocks, Txtlns, Words, Chars, & Blobs
        !            30:          a Block may own :  Blocks, Txtlns, Words, Chars, & Blobs
        !            31:            a Txtln may own :  Txtlns, Words, Chars, & Blobs
        !            32:              a Word may own :  Words, Chars & Blobs
        !            33:                a Char may own :  Blobs, Interps, Sfeats, Shapes, & Bfeats
        !            34:                  a Blob may own :  Runs (variously represented)
        !            35:                    a Run owns :  black Pixels
        !            36:     Note that the hierarchy is strict except that Blocks, Txtlns, & Words may
        !            37:     own records of their own type.  This serves several functions:
        !            38:     --  a Block may own other Blocks, conventionally nested within it, to
        !            39:        represent a physical or logical page layout decomposition;
        !            40:     --  a Txtln may have alternative interpretations & segmentations into Words;
        !            41:     --  a Word may have alternative segmentations into Chars.
        !            42: 
        !            43:     For each record type R, there should exist these data-structures:
        !            44:        R                       struct
        !            45:        Init_R                  #define'd initialization string
        !            46:        empty_R                 an extern ``empty'' (initialized) instance of R
        !            47:     Also there are library functions (in which _R is spelled in lower-case):
        !            48:        R *alloc_R()            allocate memory and initialize to ``empty''
        !            49:        free_R(R *)             free memory (only of record R, not what it owns)
        !            50:        free_R_etc(R *,ids)     free R and what it owns (as specified by `ids')
        !            51:        R *dup_R(R *)           return distinct copy, with duplicated contents
        !            52:        R *dup_R_etc(R *,ids)   return distinct copy, duplicated contents & parts
        !            53:        char *R_toa(R *)        convert to printable ASCII string
        !            54:        frdb_R(FILE *, R *)     fread R (binary) from file thru pointer
        !            55:        frdb_R_etc(",",ids)     fread R (binary) and what it owns (as specified)
        !            56:        fwrb_R(FILE *, R *)     fwrite R (binary) to file thru pointer
        !            57:        fwrb_R_etc(",",ids)     fwrite R (binary) and what it owns (as specified)
        !            58:     (some of these may be unimplemented if they haven't yet been needed)
        !            59: 
        !            60:     I/O conventions:
        !            61:     -- frd?_... return one of:
        !            62:            1  normal & successful
        !            63:            0  EOF
        !            64:           <0  I/O errors
        !            65:        Quite a few functions don't obey this rule yet.
        !            66:        Many intermediate fns that don't directly perform I/O
        !            67:        don't bother passing back status from fns they call.
        !            68:        Those that perform I/O directly often complain to stderr and exit(2).
        !            69:        The situation is fairly chaotic, and has already caused bugs.
        !            70: 
        !            71:     Generally:
        !            72:     -- An `etc' argument is a set of Ident bits specifying a set of record types;
        !            73:        it is passed along unchanged in calls to other _etc functions.
        !            74:     --  `fwrb_R_etc' fns obey the `etc' instructions carefully,  writing only those
        !            75:        record types specified
        !            76:     --  `frdb_R_etc' fns mostly ignore `etc', blindly reading everything they
        !            77:        see (since they aren't clever enough to skip past yet);  however, the
        !            78:        Blob-reading fns look at the Runs_?? bits to select a main memory
        !            79:        format for Runs
        !            80:     -- `frdb_R_etc' uses alloc_Y to allocate all new Y
        !            81:     -- `fwrb_R_etc', however, does NOT free anything: this must be done explicitly
        !            82:        afterwards.
        !            83: 
        !            84:     Ownership of a set of records is implemented by two fields in the owner record:
        !            85:     (1) a count of the number of members, and (2) either a ``set'' or a ``list''
        !            86:     pointer:
        !            87:        set:  to a NULL-terminated array of pointers to records
        !            88:        list: to the first in a singly-linked, NULL-terminated, chain
        !            89:     sets are used for sets of Blocks, Txtlns, Words, Chars, and Blobs
        !            90:     lists are used for Interpl, and for Blobs owned by Chars, since they tend
        !            91:        to be fewer
        !            92: 
        !            93:     Blobs and Runs are treated more elaborately than other records.  Blobs are
        !            94:     collected in lists when owned by Chars, but in sets otherwise --
        !            95:     the motivation was that the Char lists are usually very short, and the malloc
        !            96:     overhead of creating them might be unpleasant -- however, maintaining two
        !            97:     kinds of sets has caused other headaches; it might be good someday to
        !            98:     abolish lists in favor of sets throughout.
        !            99:     Runs can often be compressed to about half the usual size by using char
        !           100:     fields instead of shorts, and this is done automatically when writing to files.    There is both a set and a list form of Run.
        !           101: 
        !           102:     The peripheral file format is:
        !           103:     - deliberately decoupled from the internal (main memory) format:  i.e.
        !           104:        the main memory structs can be (and are) changed frequently for
        !           105:        purposes of experimentation without forcing frequent reformatting
        !           106:        of the (by now large) backlog of archived files (particularly the
        !           107:        character image databases).  To bring such files up-to-date, the program
        !           108:        `renew' should be used: it must be edited to reflect changes since
        !           109:        the last overhaul of the file format.
        !           110:     - designed to be ``scannable'': that is, one can skip rapidly from record
        !           111:        to record (using the Ident headers) without minding the hierarchy.
        !           112:        This is most useful for the graphics editor "met", where it permits
        !           113:        quick response at the outset.  It is also used widely in the
        !           114:        off-line training programs, where main memory is at a premium.
        !           115:     - machine-independent:  floating-point representations are forced to
        !           116:        fixed point and scaled to integer; all integer representations are
        !           117:        written as a sequence of bytes in a fixed order, via putc()/getc().
        !           118:        fwrite()/fread() are never used for binary I/O.  This seems to work
        !           119:        on virtually all UNIX machines.  See fioi.h.
        !           120: 
        !           121:     CCITT Group 4 encodings have been implemented and are available
        !           122:     as a peripheral file format for Blobs.  They offer a large compression
        !           123:     factor (x8) over RunF and RunFS records, and the CPU overhead is not
        !           124:     excessive.  It turns out that the connectivity information represented
        !           125:     explicitly in Run records but lost in ccitt-g4 can be recovered in linear
        !           126:     time and space (see fix_lag()); in practice the extra recovery time is
        !           127:     negligible and the extra space is 0.  This is possible because Blobs are
        !           128:     known to be connected:  if not, then (I suspect that) superlinear time is
        !           129:     required in general to recover the lag.
        !           130: 
        !           131:   */
        !           132: 
        !           133: #include <stdio.h>
        !           134: #include <math.h>
        !           135: #define LIBC_INCL 1
        !           136: #include "CPU.h"
        !           137: #include "stdocr.h"
        !           138: #include "rle.h"
        !           139: #include "Text.h"
        !           140: #include "bitio.h"
        !           141: #include "CCITT.h"
        !           142: 
        !           143:        long fseek(),lseek();
        !           144: 
        !           145: #define dbg_fwrb T     /* failsafe consistency checking */
        !           146: #define dbg_frdb T     /* failsafe consistency checking */
        !           147: #define dbg_fwrb_runs F        /* announce no. bytes used to write each Blob's Runs */
        !           148: #define dbg_frdb_runs F        /* announce no. bytes used to write each Blob's Runs */
        !           149: 
        !           150: /* return ASCII string describing ident bits */
        !           151: char *ident_toa(id)
        !           152:        Ident id;
        !           153: {      static char s[80];
        !           154:        s[0]='\0';
        !           155:     if((id&IsALL)==IsALL) strcat(s,"ALL");
        !           156:     else {
        !           157:        if(id&IsPage) {
        !           158:                strcat(s,"PG");
        !           159:                if(id&(Page_label)) {
        !           160:                        strcat(s,".");
        !           161:                        if(id&Page_label) strcat(s,"l");
        !           162:                        };
        !           163:                }
        !           164:        if(id&IsBlock) {
        !           165:                strcat(s,"BK");
        !           166:                if(id&(Block_wst|Block_label)) {
        !           167:                        strcat(s,".");
        !           168:                        if(id&Block_wst) strcat(s,"w");
        !           169:                        if(id&Block_label) strcat(s,"l");
        !           170:                        };
        !           171:                };
        !           172:        if(id&IsTxtln) {
        !           173:                strcat(s,"TL");
        !           174:                if(id&(Txtln_basl|Txtln_size|Txtln_label)) {
        !           175:                        strcat(s,".");
        !           176:                        if(id&Txtln_basl) strcat(s,"b");
        !           177:                        if(id&Txtln_size) strcat(s,"s");
        !           178:                        if(id&Txtln_label) strcat(s,"l");
        !           179:                        };
        !           180:                };
        !           181:        if(id&IsWord) {
        !           182:                strcat(s,"WD");
        !           183:                if(id&(Word_spelled|Word_label)) {
        !           184:                        strcat(s,".");
        !           185:                        if(id&Word_spelled) strcat(s,"s");
        !           186:                        if(id&Word_label) strcat(s,"l");
        !           187:                        };
        !           188:                };
        !           189:        if(id&IsWordInterp) {
        !           190:                strcat(s,"WI");
        !           191:                if(id&(Word_spelled|Word_numeric|Word_initcap|Word_allcaps|Word_hyphens|Word_slashes|Word_termhyp|Word_endsent)) {
        !           192:                        strcat(s,".");
        !           193:                        if(id&Word_spelled) strcat(s,"s");
        !           194:                        if(id&Word_numeric) strcat(s,"n");
        !           195:                        if(id&Word_initcap) strcat(s,"i");
        !           196:                        if(id&Word_allcaps) strcat(s,"a");
        !           197:                        if(id&Word_hyphens) strcat(s,"-");
        !           198:                        if(id&Word_slashes) strcat(s,"/");
        !           199:                        if(id&Word_termhyp) strcat(s,"h");
        !           200:                        if(id&Word_endsent) strcat(s,".");
        !           201:                        };
        !           202:                };
        !           203:        if(id&IsChar) {
        !           204:                strcat(s,"CH");
        !           205:                if(id&(Char_spelled|Char_confused|Char_termhyp|Char_omit|Char_label|Char_ranparms)) {
        !           206:                        strcat(s,".");
        !           207:                        if(id&Char_spelled) strcat(s,"s");
        !           208:                        if(id&Char_confused) strcat(s,"c");
        !           209:                        if(id&Char_termhyp) strcat(s,"h");
        !           210:                        if(id&Char_omit) strcat(s,"o");
        !           211:                        if(id&Char_label) strcat(s,"l");
        !           212:                        if(id&Char_ranparms) strcat(s,"r");
        !           213:                        if(id&Char_split) strcat(s,"S");
        !           214:                        if(id&Char_merged) strcat(s,"M");
        !           215:                        };
        !           216:                };
        !           217:        if(id&IsInterp) {
        !           218:                strcat(s,"IN");
        !           219:                if(id&Interp_spelled) {
        !           220:                        strcat(s,".s");
        !           221:                        };
        !           222:                };
        !           223:        if(id&IsBlob) {
        !           224:                strcat(s,"BB");
        !           225:                if( id&(Blob_lm|Blob_rm|Blob_tm|Blob_bm
        !           226:                         |Blob_chopt|Blob_chopb|Blob_chopl|Blob_chopr
        !           227:                         |Blob_small|Blob_local)
        !           228:                         ) {
        !           229:                        strcat(s,".");
        !           230:                        if(id&(Blob_tm|Blob_bm|Blob_lm|Blob_rm)) {
        !           231:                                if(id&Blob_lm) strcat(s,"l");
        !           232:                                if(id&Blob_rm) strcat(s,"r");
        !           233:                                if(id&Blob_tm) strcat(s,"t");
        !           234:                                if(id&Blob_bm) strcat(s,"b");
        !           235:                                };
        !           236:                        if(id&(Blob_chopt|Blob_chopb|Blob_chopl|Blob_chopr)) {
        !           237:                                if(id&Blob_chopl) strcat(s,"L");
        !           238:                                if(id&Blob_chopr) strcat(s,"R");
        !           239:                                if(id&Blob_chopt) strcat(s,"T");
        !           240:                                if(id&Blob_chopb) strcat(s,"B");
        !           241:                                };
        !           242:                        if(id&Blob_small) strcat(s,"s");
        !           243:                        if(id&Blob_local) strcat(s,"o");
        !           244:                        };
        !           245:                }
        !           246:            };
        !           247:        return(s);
        !           248:        }
        !           249: 
        !           250: /* convert a conventional character-code to a record type */
        !           251: Ident cto_ident(c)
        !           252:     char c;
        !           253: {   Ident type;
        !           254:        switch(c) {
        !           255:            case 'b':
        !           256:                type=IsBlob;  break;
        !           257:            case 'B': case 'k':
        !           258:                type=IsBlock;  break;
        !           259:            case 'c':
        !           260:                type=IsChar;  break;
        !           261:            case 'i':
        !           262:                type=IsInterp;  break;
        !           263:            case 'p':  case 'P':
        !           264:                type=IsPage;  break;
        !           265:            case 'r':
        !           266:                type=IsRun;  break;
        !           267:            case 't': case 'l':
        !           268:                type=IsTxtln;  break;
        !           269:            case 'w':
        !           270:                type=IsWord;  break;
        !           271:            case 'y':
        !           272:                type=IsBdy;  break;
        !           273:            };
        !           274:        return(type);
        !           275:        }
        !           276: 
        !           277: Ident cto_flag(c,type)
        !           278:     char c;
        !           279:     Ident type;
        !           280: {   Ident flag;
        !           281:        flag = IsNONE;
        !           282:        if(type&IsPage) {
        !           283:                switch(c) {
        !           284:                        case 'l': flag |= Page_label; break;
        !           285:                        };
        !           286:                }
        !           287:        else if(type&IsBlock) {
        !           288:                switch(c) {
        !           289:                        case 'w': flag |= Block_wst; break;
        !           290:                        case 'l': flag |= Block_label; break;
        !           291:                        };
        !           292:                }
        !           293:        else if(type&IsTxtln) {
        !           294:                switch(c) {
        !           295:                        case 'b': flag |= Txtln_basl; break;
        !           296:                        case 's': flag |= Txtln_size; break;
        !           297:                        case 'l': flag |= Txtln_label; break;
        !           298:                        };
        !           299:                }
        !           300:        else if(type&IsWord) {
        !           301:                switch(c) {
        !           302:                        case 's': flag |= Word_spelled; break;
        !           303:                        case 'n': flag |= Word_numeric; break;
        !           304:                        case 'i': flag |= Word_initcap; break;
        !           305:                        case 'a': flag |= Word_allcaps; break;
        !           306:                        case '-': flag |= Word_hyphens; break;
        !           307:                        case '/': flag |= Word_slashes; break;
        !           308:                        case 'h': flag |= Word_termhyp; break;
        !           309:                        case '.': flag |= Word_endsent; break;
        !           310:                        case 'l': flag |= Word_label; break;
        !           311:                        };
        !           312:                }
        !           313:        else if(type&IsChar) {
        !           314:                switch(c) {
        !           315:                        case 's': flag |= Char_spelled; break;
        !           316:                        case 'c': flag |= Char_confused; break;
        !           317:                        case 'h': flag |= Char_termhyp; break;
        !           318:                        case 'o': flag |= Char_omit; break;
        !           319:                        case 'l': flag |= Char_label; break;
        !           320:                        case 'r': flag |= Char_ranparms; break;
        !           321:                        case 'S': flag |= Char_split; break;
        !           322:                        case 'M': flag |= Char_merged; break;
        !           323:                        };
        !           324:                }
        !           325:        else if(type&IsBlob) {
        !           326:                switch(c) {
        !           327:                        case 'B': flag |= Blob_chopb; break;
        !           328:                        case 'L': flag |= Blob_chopl; break;
        !           329:                        case 'R': flag |= Blob_chopr; break;
        !           330:                        case 'T': flag |= Blob_chopt; break;
        !           331:                        case 'b': flag |= Blob_bm; break;
        !           332:                        case 'l': flag |= Blob_lm; break;
        !           333:                        case 'o': flag |= Blob_local; break;
        !           334:                        case 'r': flag |= Blob_rm; break;
        !           335:                        case 's': flag |= Blob_small; break;
        !           336:                        case 't': flag |= Blob_tm; break;
        !           337:                        };
        !           338:                }
        !           339:        else if(type&IsInterp) {
        !           340:                switch(c) {
        !           341:                        case 's': flag |= Interp_spelled; break;
        !           342:                        };
        !           343:                };
        !           344:        return(flag);
        !           345:        }
        !           346: 
        !           347: #if FRDI
        !           348: /* read only the Ident of the next record; if EOF, return 0 */
        !           349: Ident frdb_ident(fp)
        !           350:     FILE *fp;
        !           351: {   Ident ident;
        !           352:        ident = frdi_Ident(fp);
        !           353: #if dbg_frdb_toa
        !           354:        err("frdb_ident: %s",ident_toa(ident));
        !           355: #endif
        !           356:        if(feof(fp)) return(0);
        !           357:        else return(ident);
        !           358:        }
        !           359: #else
        !           360: /* read only the Ident of the next record; return 0 iff EOF */
        !           361: Ident frdb_ident(fp)
        !           362:     FILE *fp;
        !           363: {   Ident ident;
        !           364:     int stat;
        !           365:        if((stat=fread(&ident,sizeof(Ident),1,fp))!=1) {
        !           366:                if(stat==0) return(0);
        !           367:                else abort("frdb_ident: can't fread, status %d",stat);
        !           368:                };
        !           369:        if( Readvax ) ident = swapintin(ident);
        !           370: #if dbg_frdb_toa
        !           371:        err("frdb_ident: %s",ident_toa(ident));
        !           372: #endif
        !           373:        if(feof(fp)) return(0);
        !           374:        else return(ident);
        !           375:        }
        !           376: #endif
        !           377: 
        !           378: #if FRDI
        !           379: /* read label into malloc space */
        !           380: char *frdb_label(f)
        !           381:     FILE *f;
        !           382: {   register char *res;
        !           383:        res=frdi_str(f);
        !           384: #if dbg_frdb_toa
        !           385:        err("frdb_label: \"%s\"",res);
        !           386: #endif
        !           387:        return(res);
        !           388:        }
        !           389: #else
        !           390: /* read label into malloc space */
        !           391: char *frdb_label(fp)
        !           392:     FILE *fp;
        !           393: {   static char s[MAX_LABEL_LEN];
        !           394:     char *c,*ce,*l;
        !           395:     int ch;
        !           396:        ce=(c=s)+MAX_LABEL_LEN-1;
        !           397:        while((c<ce)&&((ch=getc(fp))!=EOF)&&(ch!='\0')) *(c++) = ch;
        !           398:        *c='\0';
        !           399:        if(c==ce) {
        !           400:                /* label is truncated; find end of it */
        !           401:                while(((ch=getc(fp))!=EOF)&&(ch!='\0')) ;
        !           402:                };
        !           403:        if((l=strdup(s))==NULL)
        !           404:                abort("frdb_label: can't dup char *s[%d]",strlen(s));
        !           405: #if dbg_frdb_toa
        !           406:        err("frdb_label: \"%s\"",l);
        !           407: #endif
        !           408:        return(l);
        !           409:        }
        !           410: #endif
        !           411: 
        !           412: /* skip label */
        !           413: fskb_label(fp)
        !           414:     FILE *fp;
        !           415: {   int ch;
        !           416:        while(((ch=getc(fp))!=EOF)&&(ch!='\0')&&(ch!='\n')) ;
        !           417:        if(ch==EOF) return(0); else return(1);
        !           418:        }
        !           419: 
        !           420: /* Skip the REST of this record (the Ident is assumed to have been read),
        !           421:    and return the Ident of the next record in the file;
        !           422:    else return:
        !           423:         0      EOF
        !           424:        -1      I/O error
        !           425:        -2      not one of: Page, Block, Txtln, Char, Interp
        !           426:    */
        !           427: char *pp_toa(ppp)
        !           428:     Pp *ppp;
        !           429: {    static char s[30];
        !           430:        sprintf(s,"(%0.2f,%0.2f)",ppp->x,ppp->y);
        !           431:        return(s);
        !           432:        }
        !           433: 
        !           434: /* return integer string in the range 00-99 */
        !           435: char *merit_toa(m)
        !           436:     Merit m;
        !           437: {   static char s[3];
        !           438:     int im;
        !           439:        im = (int)(m * 100);
        !           440:        if(im>99) im=99; else if(im<0) im=0;
        !           441:        if(im<10) {
        !           442:                s[0] = '0';
        !           443:                sprintf(s+1,"%d",im);
        !           444:                }
        !           445:        else sprintf(s,"%2d",im);
        !           446:        s[3] = '\0';
        !           447:        return(s);
        !           448:        }
        !           449: 
        !           450: char *pts_toa(p)
        !           451:     Pts p;
        !           452: {   static char s[10];
        !           453:        sprintf(s,"%g",((int)((p*10.0)+0.5))/10.0);
        !           454:        return(s);
        !           455:        }
        !           456: 
        !           457: Bdy *alloc_bdy()
        !           458: {    Bdy *p;
        !           459:        if((p=(Bdy *)malloc(sizeof(Bdy)))==NULL)
        !           460:                abort("alloc_bdy: can't malloc");
        !           461:        else {  *p = empty_Bdy;
        !           462:                return(p);
        !           463:                };
        !           464:        }
        !           465: 
        !           466: Bdys *alloc_bdys()
        !           467: {    Bdys *p;
        !           468:        if((p=(Bdys *)malloc(sizeof(Bdys)))==NULL)
        !           469:                abort("alloc_bdys: can't malloc");
        !           470:        else {  *p = empty_Bdys;
        !           471:                return(p);
        !           472:                };
        !           473:        }
        !           474: 
        !           475: char *bdyedge_toa(bep)
        !           476:     BdyEdge *bep;
        !           477: {   static char s[80];
        !           478:     char aps[20];
        !           479:        strcpy(aps,sp_toa(bep->ap));
        !           480:        sprintf(s,"%s,%s p%d c%s a%0.2f",
        !           481:                aps, sp_toa(bep->bp),
        !           482:                bep->per,
        !           483:                pp_toa(&(bep->ctr)),
        !           484:                (bep->ang/PI)*180.0);
        !           485:        return(s);
        !           486:        }
        !           487: 
        !           488: free_bdyedges(besp)
        !           489:     BdyEdges *besp;
        !           490: {   register BdyEdge *bep,**bepp;
        !           491:        if(besp->pa!=NULL) {
        !           492:                if(besp->mny>0) {
        !           493:                        for(bep= *(bepp=besp->pa); bep!=NULL; bep= *(++bepp))
        !           494:                                free(bep);
        !           495:                        };
        !           496:                free(besp->pa);
        !           497:                };
        !           498:        *besp = empty_BdyEdges;
        !           499:        }
        !           500: 
        !           501: /* Skip past the "TYPE=document-image\n\n" (or "TYPE=dim\n\n") ASCII header from
        !           502:    file *fp, read and check the Doc record, and, if all's well, return 1.
        !           503:    If an immediate EOF, return 0.  If not EOF, and no "TYPE=" header,
        !           504:    leave fp as it was, and return -1.  If a malformed "TYPE=" header, leave fp
        !           505:    as it was, and return -2. */
        !           506: int skip_type(fp)
        !           507:     FILE *fp;
        !           508: {   int ich,stat;
        !           509:     long seek;
        !           510: #define MAX_TYPE_HDR_LEN (100)
        !           511:     char type_eq[MAX_TYPE_HDR_LEN+1];
        !           512:     register char *pcp,*ccp,*ecp;
        !           513:     int version;
        !           514: 
        !           515:        seek = ftell(fp);
        !           516:        /* test for EOF */
        !           517:        if(feof(fp) || (ich=getc(fp))==EOF)
        !           518:                /* premature EOF*/ return(0);
        !           519:        if((type_eq[0]=ich)!='T') {
        !           520:                /* no "TYPE=" header */
        !           521:                ungetc(ich,fp);         /* fast, guaranteed push-back */
        !           522:                return(-1);
        !           523:                };
        !           524:        /* try to read the rest of "TYPE=" */
        !           525:        for(ecp=(ccp=type_eq+1)+4; ccp<ecp; ccp++) {
        !           526:                if(feof(fp) || (ich=getc(fp))==EOF) {
        !           527:                        /* no "TYPE=" header */
        !           528:                        fseek(fp,seek,0);
        !           529:                        return(-1);
        !           530:                        }
        !           531:                else *ccp=ich;
        !           532:                }
        !           533:        *ccp='\0';
        !           534:        /* check for "TYPE=" so far */
        !           535:        if(strcmp(type_eq,"TYPE=")==0) {
        !           536:                /* read rest of ASCII header */
        !           537:                ccp=(pcp=type_eq+3)+1;  ecp=type_eq+MAX_TYPE_HDR_LEN;
        !           538:                do {    ccp++; pcp++;
        !           539:                        if((ich=getc(fp))==EOF) {
        !           540:                                /* malformed "TYPE=" header */
        !           541:                                fseek(fp,seek,0);
        !           542:                                return(-2);
        !           543:                                }
        !           544:                        else *ccp=ich;
        !           545:                        }
        !           546:                while(ccp<ecp && (*ccp!='\n'||*pcp!='\n'));
        !           547:                if(ccp==ecp) {
        !           548:                        /* malformed "TYPE=" header */
        !           549:                        fseek(fp,seek,0);
        !           550:                        return(-2);
        !           551:                        };
        !           552:                *pcp='\0';
        !           553:                /* check that it is "TYPE=document-image\n\n" or "TYPE=dim\n\n" */
        !           554:                if(strcmp(type_eq,"TYPE=document-image")!=0
        !           555:                   && strcmp(type_eq,"TYPE=dim")!=0) {
        !           556:                        /* malformed "TYPE=" header */
        !           557:                        fseek(fp,seek,0);
        !           558:                        return(-2);
        !           559:                        };
        !           560: #if dbg_frdb_toa
        !           561:                err("skip_type: \"%s\\n\\n\"",type_eq);
        !           562: #endif
        !           563:                return(1);      /* normal and successful TYPE= header */
        !           564:                }
        !           565:        else {  /* no "TYPE=" header */
        !           566:                fseek(fp,seek,0);
        !           567:                return(-1);
        !           568:                };
        !           569:        }
        !           570: 
        !           571: Doc *alloc_doc()
        !           572: {    Doc *p;
        !           573:        if((p=(Doc *)malloc(sizeof(Doc)))==NULL)
        !           574:                abort("alloc_doc: can't malloc");
        !           575:        else {  *p = empty_Doc;
        !           576:                return(p);
        !           577:                };
        !           578:        }
        !           579: 
        !           580: char *doc_toa(p)
        !           581:     Doc *p;
        !           582: {    static char s[80];
        !           583:        sprintf(s,"Doc v%d p%d l%s",
        !           584:                p->version,
        !           585:                p->ps.mny,
        !           586:                (p->l!=NULL)? p->l: "");
        !           587:        return(s);
        !           588:        }
        !           589: 
        !           590: #if FWRI
        !           591: /* write the given doc (record only) */
        !           592: fwrb_doc(f,p)
        !           593:     FILE *f;
        !           594:     Doc *p;
        !           595: {   int stat;
        !           596:        fwri_uint1(f,p->version);
        !           597:        fwri_uint2(f,p->ps.mny);
        !           598: #if dbg_fwrb_toa
        !           599:        err("fwrb_doc: %s",doc_toa(p));
        !           600: #endif
        !           601:        if(p->l!=NULL) {
        !           602:                fwri_ch(f,'l');
        !           603:                fwrb_label(f,p->l);
        !           604:                }
        !           605:        else fwri_ch(f,'\0');
        !           606:        }
        !           607: #else
        !           608: fwrb_doc(f,p)
        !           609:     FILE *f;
        !           610:     Doc *p;
        !           611: {}
        !           612: #endif
        !           613: 
        !           614: #if FRDI
        !           615: /* read a Doc record (only) - it must be next in the file;
        !           616:    don't immediately read its parts; return 0 iff immediate EOF.
        !           617:    Check for correct version no: if fails, abort.  */
        !           618: int frdb_doc(f,p)
        !           619:     FILE *f;
        !           620:     Doc *p;
        !           621: {   char qlabel;
        !           622:        *p = empty_Doc;
        !           623:        if(feof(f))
        !           624:                return(0);
        !           625:        p->version=frdi_uint1(f);
        !           626:        p->ps.mny=frdi_uint2(f);
        !           627:        if((qlabel=frdi_ch(f))=='l')
        !           628:                p->l=frdb_label(f);
        !           629: #if dbg_frdb_toa
        !           630:        err("frdb_doc: %s",doc_toa(p));
        !           631: #endif
        !           632:        if(ferror(f)) return(-errno); else return(1);
        !           633:        }
        !           634: #else
        !           635: int frdb_doc(f,p)
        !           636:     FILE *f;
        !           637:     Doc *p;
        !           638: {      return(1);
        !           639:        }
        !           640: #endif
        !           641: 
        !           642: #if FWRI
        !           643: /* Write the initial Doc record */
        !           644: put_doc(f)
        !           645:     FILE *f;
        !           646: {   Doc doc;
        !           647:        doc = empty_Doc;
        !           648:        doc.version = DIM_VERSION;
        !           649:        doc.ps.mny = 1;
        !           650:        fwrb_doc(f,&doc);
        !           651:        }
        !           652: #else
        !           653: put_doc(f)
        !           654:     FILE *f;
        !           655: {}
        !           656: #endif
        !           657: 
        !           658: #if FRDI
        !           659: /* read, check, discard, and skip past the Doc record */
        !           660: int skip_doc(f)
        !           661:     FILE *f;
        !           662: {   Doc doc;
        !           663:        frdb_doc(f,&doc);
        !           664:        if(doc.version!=DIM_VERSION)
        !           665:                abort("skip_doc: version %d != DIM_VERSION %d",
        !           666:                        doc.version,DIM_VERSION);
        !           667:        if(doc.l!=NULL) free(doc.l);
        !           668:        return(1);
        !           669:        }
        !           670: #else
        !           671: int skip_doc(f)
        !           672:     FILE *f;
        !           673: {   Doc doc;
        !           674:        return(1);
        !           675:        }
        !           676: #endif
        !           677: 
        !           678: Page *alloc_page()
        !           679: {    Page *p;
        !           680:        if((p=(Page *)malloc(sizeof(Page)))==NULL)
        !           681:                abort("alloc_page: can't malloc");
        !           682:        alloc_census(Page,1);
        !           683:        *p = empty_Page;
        !           684:        return(p);
        !           685:        }
        !           686: 
        !           687: char *page_toa(p)
        !           688:     Page *p;
        !           689: {    static char s[80];
        !           690:        sprintf(s,"%s bx%s res%d,%d sk%0.2f,sh%0.2f bk%d l%d c%d b%d",
        !           691:                ident_toa(p->ident),
        !           692:                bbx_toa(&(p->bx)),
        !           693:                p->res_x,p->res_y,
        !           694:                (p->skew/PI)*180.0,
        !           695:                (p->shear/PI)*180.0,
        !           696:                p->bks.mny,p->ls.mny,p->cs.mny,p->bs.mny,
        !           697:                (p->l!=NULL)? p->l: "");
        !           698:        return(s);
        !           699:        }
        !           700: 
        !           701: #if FWRI
        !           702: /* write the given page (record only) */
        !           703: fwrb_page(f,p)
        !           704:     FILE *f;
        !           705:     Page *p;
        !           706: {   int stat;
        !           707: #if dbg_fwrb
        !           708:        if(f==stdout||f==stderr||ftell(f)<=0L) {
        !           709:                fprintf(f,"TYPE=dim\n\n");
        !           710: #if dbg_fwrb_toa
        !           711:                err("fwrb_page: \"TYPE=dim\\n\\n\"");
        !           712: #endif
        !           713:                put_doc(f);
        !           714:                };
        !           715:        if((!(p->ident&IsPage))||(p->ident&(IsALL&(~IsPage))))
        !           716:                err("fwrb_page: %s",page_toa(p));
        !           717:        if(p->ident&Page_label && p->l==NULL) {
        !           718:                err("fwrb_page: Page_label set but .l==NULL");
        !           719:                p->ident &= ~Page_label;
        !           720:                };
        !           721: #endif
        !           722:        fwri_Ident(f,p->ident);
        !           723:        fwri_Bbx(f,&(p->bx));
        !           724:        fwri_int2(f,p->res_x);
        !           725:        fwri_int2(f,p->res_y);
        !           726:        fwri_Radians(f,p->skew);
        !           727:        fwri_Radians(f,p->shear);
        !           728:        fwri_uint2(f,p->bks.mny);
        !           729:        fwri_uint2(f,p->ls.mny);
        !           730:        fwri_uint2(f,p->ws.mny);
        !           731:        fwri_uint2(f,p->cs.mny);
        !           732:        fwri_uint3(f,p->bs.mny);
        !           733: #if dbg_fwrb_toa
        !           734:        err("fwrb_page: %s",page_toa(p));
        !           735: #endif
        !           736:        if(p->ident&Page_label) fwrb_label(f,p->l);
        !           737:        }
        !           738: #else
        !           739: /* write the given page (record only) */
        !           740: fwrb_page(fp,pp)
        !           741:     FILE *fp;
        !           742:     Page *pp;
        !           743: {   PageF pf;
        !           744:     int stat;
        !           745: #if dbg_fwrb
        !           746:        if((!(pp->ident&IsPage))||(pp->ident&(IsALL&(~IsPage))))
        !           747:                err("fwrb_page: %s",page_toa(pp));
        !           748: #endif
        !           749:        memset(&pf,'\0',sizeof(pf));
        !           750:        pf.ident = pp->ident;
        !           751:        pf.res_x=pp->res_x;
        !           752:        pf.res_y=pp->res_y;
        !           753:        pf.bx=pp->bx;
        !           754:        pf.skew=pp->skew;
        !           755:        pf.shear=pp->shear;
        !           756:        pf.bkmny=pp->bks.mny;
        !           757:        pf.lmny=pp->ls.mny;
        !           758:        pf.wmny=pp->ws.mny;
        !           759:        pf.cmny=pp->cs.mny;
        !           760:        pf.bmny=pp->bs.mny;
        !           761:        if(fp==stdout||fp==stderr||ftell(fp)<=0L) {
        !           762:                fprintf(fp,"TYPE=document-image\n\n");
        !           763: #if dbg_fwrb_toa
        !           764:                err("fwrb_page: \"TYPE=document-image\\n\\n\"");
        !           765: #endif
        !           766:                };
        !           767:        if((stat=fwrite(&pf,sizeof(PageF),1,fp))!=1)
        !           768:                abort("fwrb_page: can't fwrite - status %d",stat);
        !           769: #if dbg_fwrb_toa
        !           770:        err("fwrb_page: %s",page_toa(pp));
        !           771: #endif
        !           772:        if(pf.ident&Page_label) fwrb_label(fp,pp->l);
        !           773:        }
        !           774: #endif
        !           775: 
        !           776: /* write a page and its specified parts */
        !           777: fwrb_page_etc(fp,pp,etc)
        !           778:     FILE *fp;
        !           779:     Page *pp;
        !           780:     Ident etc;
        !           781: {   static Ident parts = (IsBlock|IsTxtln|IsWord|IsChar|IsBlob);
        !           782:     Page pg;
        !           783:        if((etc&parts)!=parts) /* write selected parts */ {
        !           784:                pg = *pp;
        !           785:                if(!(etc&IsBlock)) pg.bks.mny=0;
        !           786:                if(!(etc&IsTxtln)) pg.ls.mny=0;
        !           787:                if(!(etc&IsWord)) pg.ws.mny=0;
        !           788:                if(!(etc&IsChar)) pg.cs.mny=0;
        !           789:                if(!(etc&IsBlob)) pg.bs.mny=0;
        !           790:                pp = &pg;       /* write modified record */
        !           791:                };
        !           792:        fwrb_page(fp,pp);
        !           793:        fwrb_blocks_etc(fp,pp->bks,etc);
        !           794:        fwrb_txtlns_etc(fp,pp->ls,etc);
        !           795:        fwrb_words_etc(fp,pp->ws,etc);
        !           796:        fwrb_chars_etc(fp,pp->cs,etc);
        !           797:        fwrb_blobs_etc(fp,pp->bs,etc);
        !           798:        }
        !           799: 
        !           800: /* write a page and its specified parts, ASCII (INCOMPLETE) */
        !           801: fwra_page_etc(fp,pp,etc)
        !           802:     FILE *fp;
        !           803:     Page *pp;
        !           804:     Ident etc;
        !           805: {   static Ident parts = (IsBlock|IsTxtln|IsWord|IsChar|IsBlob);
        !           806:     Page pg;
        !           807:        if((etc&parts)!=parts) /* write selected parts */ {
        !           808:                pg = *pp;
        !           809:                if(!(etc&IsBlock)) pg.bks.mny=0;
        !           810:                if(!(etc&IsTxtln)) pg.ls.mny=0;
        !           811:                if(!(etc&IsWord)) pg.ws.mny=0;
        !           812:                if(!(etc&IsChar)) pg.cs.mny=0;
        !           813:                if(!(etc&IsBlob)) pg.bs.mny=0;
        !           814:                pp = &pg;       /* write modified record */
        !           815:                };
        !           816:        if(etc&IsPage) fwrb_page(fp,pp);
        !           817:        fwrb_blocks_etc(fp,pp->bks,etc);
        !           818:        fwrb_txtlns_etc(fp,pp->ls,etc);
        !           819:        fwrb_words_etc(fp,pp->ws,etc);
        !           820:        fwrb_chars_etc(fp,pp->cs,etc);
        !           821:        fwrb_blobs_etc(fp,pp->bs,etc);
        !           822:        }
        !           823: 
        !           824: #if FRDI
        !           825: /* read a Page record (only) - it must be next in the file;
        !           826:    don't immediately read its parts; return 0 iff immediate EOF  */
        !           827: int frdb_page(f,p)
        !           828:     FILE *f;
        !           829:     Page *p;
        !           830: {   char *pfc; /* for debugging */
        !           831:     int stat;
        !           832:        switch(skip_type(f)) {
        !           833:            case 1:  /* "TYPE=" header */
        !           834:                skip_doc(f);
        !           835:                break;
        !           836:            case 0:  /* immediate EOF */
        !           837:                return(0);
        !           838:                break;
        !           839:            case -1:  /* no "TYPE=" header */
        !           840:                break;
        !           841:            case -2:  /* garbled "TYPE=" header */
        !           842:                abort("frdb_page: garbled \"TYPE=\" header");
        !           843:                break;
        !           844:            };
        !           845:        *p = empty_Page;
        !           846:        if(feof(f))
        !           847:                return(0);
        !           848:        p->ident=frdi_Ident(f);
        !           849:        frdi_Bbx(f,&(p->bx));
        !           850:        p->res_x=frdi_int2(f);
        !           851:        p->res_y=frdi_int2(f);
        !           852:        p->skew=frdi_Radians(f);
        !           853:        p->shear=frdi_Radians(f);
        !           854:        p->bks.mny=frdi_uint2(f);
        !           855:        p->ls.mny=frdi_uint2(f);
        !           856:        p->ws.mny=frdi_uint2(f);
        !           857:        p->cs.mny=frdi_uint2(f);
        !           858:        p->bs.mny=frdi_uint3(f);
        !           859: #if dbg_frdb
        !           860:        if((!(p->ident&IsPage))||(p->ident&(IsALL&(~IsPage))))
        !           861:                err("frdb_page: %s",page_toa(p));
        !           862: #endif
        !           863: #if dbg_frdb_toa
        !           864:        err("frdb_page: %s",page_toa(p));
        !           865: #endif
        !           866:        if(p->ident&Page_label) p->l = frdb_label(f);
        !           867:        if(ferror(f)) return(-errno); else return(1);
        !           868:        }
        !           869: #else
        !           870: /* read a Page record (only) - it must be next in the file;
        !           871:    don't immediately read its parts; return T iff not early EOF  */
        !           872: int frdb_page(fp,pp)
        !           873:     FILE *fp;
        !           874:     Page *pp;
        !           875: {   PageF pf;
        !           876:     char *pfc; /* for debugging */
        !           877:     int stat;
        !           878:        if((stat=skip_type(fp))!=1 /* "TYPE=" header */
        !           879:            && stat!=-1 /* no "TYPE=" header */ ) {
        !           880:                /* premature EOF or error */ return(stat);
        !           881:                };
        !           882:        if((stat=fread(&pf,sizeof(PageF),1,fp))!=1)
        !           883:                /* premature EOF*/ return(stat);
        !           884:        pfc = (char *)&pf;
        !           885:        *pp = empty_Page;
        !           886:        pp->ident=pf.ident;
        !           887:        pp->res_x=pf.res_x;
        !           888:        pp->res_y=pf.res_y;
        !           889:        pp->bx=pf.bx;
        !           890:        pp->skew=pf.skew;
        !           891:        pp->shear=pf.shear;
        !           892:        pp->bks.mny = pf.bkmny; pp->bks.bpa=NULL;
        !           893:        pp->ls.mny = pf.lmny;   pp->ls.lpa=NULL;
        !           894:        pp->ws.mny = pf.wmny;   pp->ws.wpa=NULL;
        !           895:        pp->cs.mny = pf.cmny;   pp->cs.cpa=NULL;
        !           896:        pp->bs.mny = pf.bmny;   pp->bs.bpa=NULL;
        !           897: #if dbg_frdb_toa
        !           898:        err("frdb_page: %s",page_toa(pp));
        !           899: #endif
        !           900:        if(pp->ident&Page_label) pp->l = frdb_label(fp);
        !           901: #if dbg_frdb
        !           902:        if((!(pp->ident&IsPage))||(pp->ident&(IsALL&(~IsPage))))
        !           903:                err("frdb_page: %s",page_toa(pp));
        !           904: #endif
        !           905:        return(stat);
        !           906:        }
        !           907: #endif
        !           908: 
        !           909: /* Read a page and selected parts */
        !           910: int frdb_page_etc(fp,pp,etc)
        !           911:     FILE *fp;
        !           912:     Page *pp;
        !           913:     Ident etc;
        !           914: {   int stat;
        !           915:        if((etc&IsALL)==IsALL) {
        !           916:            long here;
        !           917:                /* In order to minimize malloc time to read an often huge file,
        !           918:                   malloc and then free storage roughly equal to what's needed */
        !           919:                if( /* DEFEATED */ F && (here=ftell(fp))>=0 && fseek(fp,0,2)>=0 ) {
        !           920:                        /* *fp is connected to a file, not a pipe */
        !           921:                        free(malloc(ftell(fp)));
        !           922:                        fseek(fp,here,0);
        !           923:                        };
        !           924:                };
        !           925:        if((stat=frdb_page(fp,pp))!=1) return(stat);
        !           926:        if(etc&IsBlock && pp->bks.mny>0)
        !           927:                frdb_blocks_etc(fp,&(pp->bks),etc);
        !           928:        else pp->bks = empty_Blocks;
        !           929:        if(etc&IsTxtln && pp->ls.mny>0)
        !           930:                frdb_txtlns_etc(fp,&(pp->ls),etc);
        !           931:        else pp->ls = empty_Txtlns;
        !           932:        if(etc&IsWord && pp->ws.mny>0)
        !           933:                frdb_words_etc(fp,&(pp->ws),etc);
        !           934:        else pp->ws = empty_Words;
        !           935:        if(etc&IsChar && pp->cs.mny>0)
        !           936:                frdb_chars_etc(fp,&(pp->cs),etc);
        !           937:        else pp->cs = empty_Chars;
        !           938:        if(etc&IsBlob && pp->bs.mny>0)
        !           939:                frdb_blobs_etc(fp,&(pp->bs),etc);
        !           940:        else pp->bs = empty_Blobs;
        !           941:        return(1/*normal return*/);
        !           942:        }
        !           943: 
        !           944: free_page_etc(p,etc)
        !           945:     Page *p;
        !           946:     Ident etc;
        !           947: {      free_blocks_etc(&(p->bks),etc);
        !           948:        free_txtlns_etc(&(p->ls),etc);
        !           949:        free_words_etc(&(p->ws),etc);
        !           950:        free_chars_etc(&(p->cs),etc);
        !           951:        free_blobs_etc(&(p->bs),etc);
        !           952:        if(p->ident&Page_label && p->l!=NULL) {
        !           953:                free(p->l);  p->l=NULL;  p->ident &= ~Page_label;
        !           954:                };
        !           955:        if(etc&IsPage) {
        !           956:                free(p);
        !           957:                free_census(Page,1);
        !           958:                };
        !           959:        }
        !           960: 
        !           961: /* return a pointer to a distinct and duplicate copy of record Page *p,
        !           962:    created out of malloc space */
        !           963: Page *dup_page(p)
        !           964:     Page *p;
        !           965: {   Page *dup;
        !           966:        dup=alloc_page();  *dup = *p;  return(dup);
        !           967:        }
        !           968: 
        !           969: /* Return a pointer to a distinct and duplicate copy of Page *p, and its parts
        !           970:    as specified by `etc', created out of malloc space. */
        !           971: Page *dup_page_etc(p,etc)
        !           972:     Page *p;
        !           973:     Ident etc;
        !           974: {   Page *dup;
        !           975:        dup = dup_page(p);
        !           976:        if(etc&IsBlob&&dup->bs.mny>0) dup->bs = *dup_blobs_etc(&(p->bs),etc);
        !           977:        else dup->bs = empty_Blobs;
        !           978:        if(etc&IsChar&&dup->cs.mny>0) dup->cs = *dup_chars_etc(&(p->cs),etc);
        !           979:        else dup->cs = empty_Chars;
        !           980:        if(etc&IsWord&&dup->ws.mny>0) dup->ws = *dup_words_etc(&(p->ws),etc);
        !           981:        else dup->ws = empty_Words;
        !           982:        if(etc&IsTxtln&&dup->ls.mny>0) dup->ls = *dup_txtlns_etc(&(p->ls),etc);
        !           983:        else dup->ls = empty_Txtlns;
        !           984:        if(etc&IsBlock&&dup->bks.mny>0) dup->bks = *dup_blocks_etc(&(p->bks),etc);
        !           985:        else dup->bks = empty_Blocks;
        !           986:        return(dup);
        !           987:        }
        !           988: 
        !           989: Block *alloc_block()
        !           990: {    Block *p;
        !           991:        if((p=(Block *)malloc(sizeof(Block)))==NULL)
        !           992:                abort("alloc_block: can't malloc");
        !           993:        alloc_census(Block,1);
        !           994:        *p = empty_Block;
        !           995:        return(p);
        !           996:        }
        !           997: 
        !           998: char *block_toa(p)
        !           999:     Block *p;
        !          1000: {   static char s[80];
        !          1001:        sprintf(s,"%s bx%s sk%0.2f,sh%0.2f wst%0.2f m%s bk%d l%d w%d c%d b%d",
        !          1002:                ident_toa(p->ident),
        !          1003:                bbx_toa(&(p->bx)),
        !          1004:                p->skew/DtoR,
        !          1005:                p->shear/DtoR,
        !          1006:                p->wst,
        !          1007:                merit_toa(p->m),
        !          1008:                p->bks.mny,p->ls.mny,p->ws.mny,p->cs.mny,p->bs.mny,
        !          1009:                (p->l!=NULL)? p->l: "");
        !          1010:        return(s);
        !          1011:        }
        !          1012: 
        !          1013: #if FWRI
        !          1014: fwrb_block(f,p)
        !          1015:     FILE *f;
        !          1016:     Block *p;
        !          1017: {   int stat;
        !          1018: #if dbg_fwrb
        !          1019:        if((!(p->ident&IsBlock))||(p->ident&(IsALL&(~IsBlock))))
        !          1020:                err("fwrb_block: %s",block_toa(p));
        !          1021:        if(p->ident&Block_label && p->l==NULL) {
        !          1022:                err("fwrb_page: Block_label set but .l==NULL");
        !          1023:                p->ident &= ~Block_label;
        !          1024:                };
        !          1025: #endif
        !          1026:        fwri_Ident(f,p->ident);
        !          1027:        fwri_Bbx(f,&(p->bx));
        !          1028:        fwri_Radians(f,p->skew);
        !          1029:        fwri_Radians(f,p->shear);
        !          1030:        fwri_Ems(f,p->wst);
        !          1031:        fwri_Merit(f,p->m);
        !          1032:        fwri_uint2(f,p->bks.mny);
        !          1033:        fwri_uint2(f,p->ls.mny);
        !          1034:        fwri_uint2(f,p->ws.mny);
        !          1035:        fwri_uint2(f,p->cs.mny);
        !          1036:        fwri_uint3(f,p->bs.mny);
        !          1037: #if dbg_fwrb_toa
        !          1038:        err("fwrb_block: %s",block_toa(p));
        !          1039: #endif
        !          1040:        if(p->ident&Block_label) fwrb_label(f,p->l);
        !          1041:        }
        !          1042: #else
        !          1043: fwrb_block(fp,bkp)
        !          1044:     FILE *fp;
        !          1045:     Block *bkp;
        !          1046: {   BlockF bf;
        !          1047:     int stat;
        !          1048: #if dbg_fwrb
        !          1049:        if((!(bkp->ident&IsBlock))||(bkp->ident&(IsALL&(~IsBlock))))
        !          1050:                err("fwrb_block: %s",block_toa(bkp));
        !          1051: #endif
        !          1052:        memset(&bf,'\0',sizeof(bf));
        !          1053:        bf.ident = bkp->ident;
        !          1054:        bf.bx = bkp->bx;
        !          1055:        bf.wst = bkp->wst;
        !          1056:        bf.skew = bkp->skew;
        !          1057:        bf.shear = bkp->shear;
        !          1058:        bf.lmny = bkp->ls.mny;
        !          1059:        bf.wmny = bkp->ws.mny;
        !          1060:        bf.cmny = bkp->cs.mny;
        !          1061:        bf.bmny = bkp->bs.mny;
        !          1062: #if dbg_fwrb_toa
        !          1063:        err("fwrb_block: %s",block_toa(bkp));
        !          1064: #endif
        !          1065:        if((stat=fwrite(&bf,sizeof(BlockF),1,fp))!=1)
        !          1066:                abort("fwrb_block: can't fwrite - status %d",stat);
        !          1067:        }
        !          1068: #endif
        !          1069: 
        !          1070: fwrb_blocks_etc(fp,bks,etc)
        !          1071:     FILE *fp;
        !          1072:     Blocks bks;
        !          1073:     Ident etc;
        !          1074: {   register Block *bkp,**bkpp;
        !          1075:        if(bks.mny>0) for(bkp= *(bkpp=bks.bpa); bkp!=NULL; bkp= *(++bkpp))
        !          1076:                fwrb_block_etc(fp,bkp,etc);
        !          1077:        }
        !          1078: 
        !          1079: fwrb_block_etc(fp,bkp,etc)
        !          1080:     FILE *fp;
        !          1081:     Block *bkp;
        !          1082:     Ident etc;
        !          1083: {   static Ident parts = (IsTxtln|IsWord|IsChar|IsBlob);
        !          1084:     Block bk;
        !          1085:        if((etc&parts)!=parts) /* write selected parts */ {
        !          1086:                bk = *bkp;
        !          1087:                if(!(etc&IsTxtln)) bk.ls.mny=0;
        !          1088:                if(!(etc&IsWord)) bk.ws.mny=0;
        !          1089:                if(!(etc&IsChar)) bk.cs.mny=0;
        !          1090:                if(!(etc&IsBlob)) bk.bs.mny=0;
        !          1091:                bkp = &bk;      /* write modified record */
        !          1092:                };
        !          1093:        fwrb_block(fp,bkp);
        !          1094:        fwrb_txtlns_etc(fp,bkp->ls,etc);
        !          1095:        fwrb_words_etc(fp,bkp->ws,etc);
        !          1096:        fwrb_chars_etc(fp,bkp->cs,etc);
        !          1097:        fwrb_blobs_etc(fp,bkp->bs,etc);
        !          1098:        }
        !          1099: 
        !          1100: #if FRDI
        !          1101: int frdb_block(f,p)
        !          1102:     FILE *f;
        !          1103:     Block *p;
        !          1104: {      *p = empty_Block;
        !          1105:        if(feof(f))
        !          1106:                return(0);
        !          1107:        p->ident=frdi_Ident(f);
        !          1108:        frdi_Bbx(f,&(p->bx));
        !          1109:        p->skew=frdi_Radians(f);
        !          1110:        p->shear=frdi_Radians(f);
        !          1111:        p->wst=frdi_Ems(f);
        !          1112:        p->m=frdi_Merit(f);
        !          1113:        p->bks.mny=frdi_uint2(f);
        !          1114:        p->ls.mny=frdi_uint2(f);
        !          1115:        p->ws.mny=frdi_uint2(f);
        !          1116:        p->cs.mny=frdi_uint2(f);
        !          1117:        p->bs.mny=frdi_uint3(f);
        !          1118: #if dbg_frdb_toa
        !          1119:        err("frdb_block: %s",block_toa(p));
        !          1120: #endif
        !          1121: #if dbg_frdb
        !          1122:        if((!(p->ident&IsBlock))||(p->ident&(IsALL&(~IsBlock))))
        !          1123:                err("frdb_block: %s",block_toa(p));
        !          1124: #endif
        !          1125:        if(p->ident&Block_label) p->l = frdb_label(f);
        !          1126:        if(ferror(f)) return(-errno); else return(1);
        !          1127:        }
        !          1128: #else
        !          1129: int frdb_block(fp,bkp)
        !          1130:     FILE *fp;
        !          1131:     Block *bkp;
        !          1132: {   BlockF bf;
        !          1133:     int stat;
        !          1134:        if((stat=fread(&bf,sizeof(BlockF),1,fp))!=1)
        !          1135:                abort("frdb_block: can't fread - status %d",stat);
        !          1136:        *bkp = empty_Block;
        !          1137:        bkp->ident = bf.ident;
        !          1138:        bkp->bx = bf.bx;
        !          1139:        bkp->skew = bf.skew;
        !          1140:        bkp->shear = bf.shear;
        !          1141:        bkp->wst = bf.wst;
        !          1142:        bkp->ls.mny = bf.lmny;
        !          1143:        bkp->ws.mny = bf.wmny;
        !          1144:        bkp->cs.mny = bf.cmny;
        !          1145:        bkp->bs.mny = bf.bmny;
        !          1146: #if dbg_frdb
        !          1147:        if((!(bkp->ident&IsBlock))||(bkp->ident&(IsALL&(~IsBlock))))
        !          1148:                err("frdb_block: %s",block_toa(bkp));
        !          1149: #endif
        !          1150: #if dbg_frdb_toa
        !          1151:        err("frdb_block: %s",block_toa(bkp));
        !          1152: #endif
        !          1153:        if(bkp->ident&Block_label) bkp->l = frdb_label(fp);
        !          1154:        if(ferror(fp)) return(-errno); else return(1);
        !          1155:        }
        !          1156: #endif
        !          1157: 
        !          1158: frdb_block_etc(fp,bkp,etc)
        !          1159:     FILE *fp;
        !          1160:     Block *bkp;
        !          1161:     Ident etc;
        !          1162: {   BlockF bf;
        !          1163:     int stat;
        !          1164:        frdb_block(fp,bkp);
        !          1165:        if(etc&IsTxtln && bkp->ls.mny>0)
        !          1166:                frdb_txtlns_etc(fp,&(bkp->ls),etc);
        !          1167:        else bkp->ls = empty_Txtlns;
        !          1168:        if(etc&IsWord && bkp->ws.mny>0)
        !          1169:                frdb_words_etc(fp,&(bkp->ws),etc);
        !          1170:        else bkp->ws = empty_Words;
        !          1171:        if(etc&IsChar && bkp->cs.mny>0)
        !          1172:                frdb_chars_etc(fp,&(bkp->cs),etc);
        !          1173:        else bkp->cs = empty_Chars;
        !          1174:        if(etc&IsBlob && bkp->bs.mny>0)
        !          1175:                frdb_blobs_etc(fp,&(bkp->bs),etc);
        !          1176:        else bkp->bs = empty_Blobs;
        !          1177:        if(ferror(fp)) return(-errno); else return(1);
        !          1178:        }
        !          1179: 
        !          1180: /* read number of blocks, and their parts */
        !          1181: frdb_blocks_etc(fp,bksp,etc)
        !          1182:     FILE *fp;
        !          1183:     Blocks *bksp;
        !          1184:     Ident etc;
        !          1185: {   int bi;
        !          1186:     register Block *bkp,**bkpp;
        !          1187:        if(bksp->mny<=0) {
        !          1188:                *bksp = empty_Blocks;
        !          1189:                return(1);
        !          1190:                };
        !          1191: 
        !          1192:        if((bkpp=bksp->bpa=(Block **)malloc((bksp->mny+1)*sizeof(Block *)))==NULL)
        !          1193:                abort("can't alloc Blocks.bpa array");
        !          1194:        for(bi=0; bi<bksp->mny; bi++) {
        !          1195:                *(bkpp++) = bkp = alloc_block();
        !          1196:                frdb_block_etc(fp,bkp,etc);
        !          1197:                };
        !          1198:        *bkpp = NULL;
        !          1199:        if(ferror(fp)) return(-errno); else return(1);
        !          1200:        }
        !          1201: 
        !          1202: free_block_etc(p,etc)
        !          1203:     Block *p;
        !          1204:     Ident etc;
        !          1205: {      free_txtlns_etc(&(p->ls),etc);
        !          1206:        free_words_etc(&(p->ws),etc);
        !          1207:        free_chars_etc(&(p->cs),etc);
        !          1208:        free_blobs_etc(&(p->bs),etc);
        !          1209:        if(p->ident&Block_label && p->l!=NULL) {
        !          1210:                free(p->l);  p->l=NULL;  p->ident &= ~Block_label;
        !          1211:                };
        !          1212:        if(etc&IsBlock) {
        !          1213:                free(p);
        !          1214:                free_census(Block,1);
        !          1215:                };
        !          1216:        }
        !          1217: 
        !          1218: /* Unconditionally free the malloc-space array of pointers, and empty the set.
        !          1219:    Don't free the records that it owned.
        !          1220:    */
        !          1221: free_blocks(p)
        !          1222:     Blocks *p;
        !          1223: {      if(p->bpa!=NULL) { free(p->bpa);  p->bpa = NULL; }
        !          1224:        p->mny = 0;
        !          1225:        }
        !          1226: 
        !          1227: free_blocks_etc(p,etc)
        !          1228:     Blocks *p;
        !          1229:     Ident etc;
        !          1230: {   register Block *bkp,**bkpp;
        !          1231:        if(p->mny>0&&(etc&IsBlock))
        !          1232:                for(bkp= *(bkpp=p->bpa); bkp!=NULL; bkp= *(++bkpp))
        !          1233:                        free_block_etc(bkp,etc);
        !          1234:        free_blocks(p);
        !          1235:        }
        !          1236: 
        !          1237: /* return a pointer to a distinct and duplicate copy of *bkp,
        !          1238:    created out of malloc space */
        !          1239: Block *dup_block(bkp)
        !          1240:     Block *bkp;
        !          1241: {   Block *dup;
        !          1242:        dup=alloc_block();
        !          1243:        *dup = *bkp;
        !          1244:        return(dup);
        !          1245:        }
        !          1246: 
        !          1247: /* Return a pointer to a distinct and duplicate copy of *bkp, and its parts
        !          1248:    as specified by `etc', created out of malloc space. */
        !          1249: Block *dup_block_etc(bkp,etc)
        !          1250:     Block *bkp;
        !          1251:     Ident etc;
        !          1252: {   Block *dup;
        !          1253:        dup = dup_block(bkp);
        !          1254:        if(etc&IsBlob&&dup->bs.mny>0) dup->bs = *dup_blobs_etc(&(bkp->bs),etc);
        !          1255:        else dup->bs = empty_Blobs;
        !          1256:        if(etc&IsChar&&dup->cs.mny>0) dup->cs = *dup_chars_etc(&(bkp->cs),etc);
        !          1257:        else dup->cs = empty_Chars;
        !          1258:        if(etc&IsWord&&dup->ws.mny>0) dup->ws = *dup_words_etc(&(bkp->ws),etc);
        !          1259:        else dup->ws = empty_Words;
        !          1260:        if(etc&IsTxtln&&dup->ls.mny>0) dup->ls = *dup_txtlns_etc(&(bkp->ls),etc);
        !          1261:        else dup->ls = empty_Txtlns;
        !          1262:        return(dup);
        !          1263:        }
        !          1264: 
        !          1265: /* Return a pointer to a distinct local static duplicate of non-empty *bksp.
        !          1266:    Its bkpa array is created newly out of malloc space.
        !          1267:    If etc&IsBlock, all its Blocks are also duplicated,
        !          1268:    else the contents of bkpa point to the old unduplicated Blocks.
        !          1269:    */
        !          1270: Blocks *dup_blocks_etc(bksp,etc)
        !          1271:     Blocks *bksp;
        !          1272:     Ident etc;         /* parts to duplicate */
        !          1273: {   static Blocks dup;
        !          1274:     register Block **bkpp,**dpp;
        !          1275:        if((dup.mny = bksp->mny)<=0) dup = empty_Blocks;
        !          1276:        else {  if((dup.bpa=(Block **)malloc((dup.mny+1)*sizeof(Block *)))==NULL)
        !          1277:                        abort("can't dup bks.bpa");
        !          1278:                for(bkpp=bksp->bpa,dpp=dup.bpa; *bkpp!=NULL; bkpp++,dpp++) {
        !          1279:                        if(etc&IsBlock) *dpp = dup_block_etc(*bkpp,etc);
        !          1280:                        else *dpp = *bkpp;
        !          1281:                        };
        !          1282:                *dpp = NULL;
        !          1283:                };
        !          1284:        return(&dup);
        !          1285:        }
        !          1286: 
        !          1287: /* Append a block to the end of a blocks set. 
        !          1288:    Return appended Block *. */
        !          1289: Block *append_block(bkp,bksp)
        !          1290:     Block *bkp;
        !          1291:     Blocks *bksp;
        !          1292: {   register Block *rp,**rpp,**npp;
        !          1293:        if(bksp->mny==0) {
        !          1294:                if((bksp->bpa=(Block **)malloc(2*sizeof(Block *)))==NULL)
        !          1295:                        abort("append_block: can't malloc bksp->bpa");
        !          1296:                }
        !          1297:        else {  if((bksp->bpa=(Block **)realloc(
        !          1298:                                bksp->bpa,
        !          1299:                                (bksp->mny+2)*sizeof(Block *))
        !          1300:                                )==NULL)
        !          1301:                        abort("append_block: can't realloc bksp->bpa");
        !          1302:                };
        !          1303:        bksp->bpa[bksp->mny] = bkp;
        !          1304:        bksp->bpa[++bksp->mny] = NULL;
        !          1305:        return(bkp);
        !          1306:        }
        !          1307: 
        !          1308: /* remove a block from a blocks set */
        !          1309: remove_block(bkp,bksp)
        !          1310:     Block *bkp;
        !          1311:     Blocks *bksp;
        !          1312: {   register Block *rp,**rpp,**npp;
        !          1313:        if(bksp->mny==0) err("can't remove Block - Blocks empty");
        !          1314:        else {  for(rp= *(rpp=bksp->bpa); rp!=NULL; rp= *(++rpp)) if(rp==bkp) break;
        !          1315:                if(rp==NULL) err("can't remove Block - not found");
        !          1316:                else {  /* move later entries up */
        !          1317:                        npp=rpp+1;
        !          1318:                        do *(rpp++)= *(npp++); while ((*rpp)!=NULL);
        !          1319:                        if((--(bksp->mny))==0) {free(bksp->bpa); bksp->bpa=NULL;};
        !          1320:                        /* don't bother to realloc downwards */
        !          1321:                        };
        !          1322:                };
        !          1323:        }
        !          1324: 
        !          1325: ConstPitch *alloc_constpitch()
        !          1326: {    ConstPitch *p;
        !          1327:        if((p=(ConstPitch *)malloc(sizeof(ConstPitch)))==NULL)
        !          1328:                abort("can't alloc ConstPitch");
        !          1329:        else {  *p = empty_ConstPitch;
        !          1330:                return(p);
        !          1331:                };
        !          1332:        }
        !          1333: 
        !          1334: char *constpitch_toa(p)
        !          1335:     ConstPitch *p;
        !          1336: {    static char s[80];
        !          1337:        sprintf(s,"{w%.3f,o%d,r%.1f}",
        !          1338:                p->w,p->o,p->r);
        !          1339:        return(s);
        !          1340:        }
        !          1341: 
        !          1342: free_constpitch(p)
        !          1343:     ConstPitch *p;
        !          1344: {      free(p);
        !          1345:        }
        !          1346: 
        !          1347: Txtln *alloc_txtln()
        !          1348: {    Txtln *p;
        !          1349:        if((p=(Txtln *)malloc(sizeof(Txtln)))==NULL)
        !          1350:                abort("can't alloc Txtln");
        !          1351:        alloc_census(Txtln,1);
        !          1352:        *p = empty_Txtln;
        !          1353:        return(p);
        !          1354:        }
        !          1355: 
        !          1356: char *txtln_toa(p)
        !          1357:     Txtln *p;
        !          1358: {    static char s[80];
        !          1359:        sprintf(s,"%s bx%s sz%s ba%d cw%s m%s l%d w%d c%d b%d %s",
        !          1360:                ident_toa(p->ident),
        !          1361:                bbx_toa(&(p->bx)),
        !          1362:                pts_toa(p->size),
        !          1363:                p->basl,
        !          1364:                /** proj omitted **/
        !          1365:                ((p->cp!=NULL)? constpitch_toa(p->cp): "0"),
        !          1366:                merit_toa(p->m),
        !          1367:                p->ls.mny,p->ws.mny,p->cs.mny,p->bs.mny,
        !          1368:                (p->l!=NULL)? p->l: "");
        !          1369:        return(s);
        !          1370:        }
        !          1371: 
        !          1372: #if FWRI
        !          1373: fwrb_txtln(f,p)
        !          1374:     FILE *f;
        !          1375:     Txtln *p;
        !          1376: {
        !          1377: #if dbg_fwrb
        !          1378:        if((!(p->ident&IsTxtln))||(p->ident&(IsALL&(~IsTxtln))))
        !          1379:                err("fwrb_txtln: %s",txtln_toa(p));
        !          1380:        if(p->ident&Txtln_label && p->l==NULL) {
        !          1381:                err("fwrb_page: Txtln_label set but .l==NULL");
        !          1382:                p->ident &= ~Txtln_label;
        !          1383:                };
        !          1384: #endif
        !          1385:        fwri_Ident(f,p->ident);
        !          1386:        fwri_Bbx(f,&(p->bx));
        !          1387:        fwri_Pts(f,p->size);
        !          1388:        fwri_Scoor(f,p->basl);
        !          1389:        /* fwri_?(f,p->proj); */
        !          1390:        /* fwri_ConstPitch(f,p->cp); */
        !          1391:        fwri_Merit(f,p->m);
        !          1392:        fwri_uint2(f,p->ls.mny);
        !          1393:        fwri_uint2(f,p->ws.mny);
        !          1394:        fwri_uint2(f,p->cs.mny);
        !          1395:        fwri_uint3(f,p->bs.mny);
        !          1396: #if dbg_fwrb_toa
        !          1397:        err("fwrb_txtln: %s",txtln_toa(p));
        !          1398: #endif
        !          1399:        if(p->ident&Txtln_label) fwrb_label(f,p->l);
        !          1400:        }
        !          1401: #else
        !          1402: fwrb_txtln(fp,lp)
        !          1403:     FILE *fp;
        !          1404:     Txtln *lp;
        !          1405: {   TxtlnF lf;
        !          1406:     int stat;
        !          1407: #if dbg_fwrb
        !          1408:        if((!(lp->ident&IsTxtln))||(lp->ident&(IsALL&(~IsTxtln))))
        !          1409:                err("fwrb_txtln: %s",txtln_toa(lp));
        !          1410: #endif
        !          1411:        memset(&lf,'\0',sizeof(lf));
        !          1412:        lf.ident = lp->ident;
        !          1413:        lf.bx = lp->bx;
        !          1414:        lf.basl = lp->basl;
        !          1415:        lf.size = lp->size;
        !          1416:        if(lp->proj!=NULL) lf.pmny = lp->bx.b.y - lp->bx.a.y + 1;
        !          1417:        else lf.pmny = 0;
        !          1418:        /* ignore lp->cp for now */
        !          1419:        lf.wmny = lp->ws.mny;
        !          1420:        lf.cmny = lp->cs.mny;
        !          1421:        lf.bmny = lp->bs.mny;
        !          1422:        if((stat=fwrite(&lf,sizeof(TxtlnF),1,fp))!=1)
        !          1423:                abort("can't write TxtlnF, status %d",stat);
        !          1424: #if dbg_fwrb_toa
        !          1425:        err("fwrb_txtln: %s",txtln_toa(lp));
        !          1426: #endif
        !          1427:        if(lf.ident&Txtln_label) fwrb_label(fp,lp->l);
        !          1428:        }
        !          1429: #endif
        !          1430: 
        !          1431: fwrb_txtlns_etc(fp,ls,etc)
        !          1432:     FILE *fp;
        !          1433:     Txtlns ls;
        !          1434:     Ident etc;
        !          1435: {   register Txtln *lp,**lpp;
        !          1436:        if(ls.mny>0) for(lp= *(lpp=ls.lpa); lp!=NULL; lp= *(++lpp))
        !          1437:                fwrb_txtln_etc(fp,lp,etc);
        !          1438:        }
        !          1439: 
        !          1440: fwrb_txtln_etc(fp,lp,etc)
        !          1441:     FILE *fp;
        !          1442:     Txtln *lp;
        !          1443:     Ident etc;
        !          1444: {   static Ident parts = (IsWord|IsChar|IsBlob);
        !          1445:     Txtln tl;
        !          1446:        if((etc&parts)!=parts) /* write selected parts */ {
        !          1447:                tl = *lp;
        !          1448:                if(!(etc&IsWord)) tl.ws.mny=0;
        !          1449:                if(!(etc&IsChar)) tl.cs.mny=0;
        !          1450:                if(!(etc&IsBlob)) tl.bs.mny=0;
        !          1451:                lp = &tl;       /* write modified record */
        !          1452:                };
        !          1453:        fwrb_txtln(fp,lp);
        !          1454:        if(lp->proj!=NULL) {/* write projection array someday */};
        !          1455:        fwrb_words_etc(fp,lp->ws,etc);
        !          1456:        fwrb_chars_etc(fp,lp->cs,etc);
        !          1457:        fwrb_blobs_etc(fp,lp->bs,etc);
        !          1458:        }
        !          1459: 
        !          1460: #if FRDI
        !          1461: int frdb_txtln(f,p)
        !          1462:     FILE *f;
        !          1463:     Txtln *p;
        !          1464: {      *p = empty_Txtln;
        !          1465:        if(feof(f))
        !          1466:                return(0);
        !          1467:        p->ident=frdi_Ident(f);
        !          1468:        frdi_Bbx(f,&(p->bx));
        !          1469:        p->size=frdi_Pts(f);
        !          1470:        p->basl=frdi_Scoor(f);
        !          1471:        /* p->proj=frdi_?(f); */
        !          1472:        /* p->cp=frdi_ConstPitch(f); */
        !          1473:        p->m=frdi_Merit(f);
        !          1474:        p->ls.mny=frdi_uint2(f);
        !          1475:        p->ws.mny=frdi_uint2(f);
        !          1476:        p->cs.mny=frdi_uint2(f);
        !          1477:        p->bs.mny=frdi_uint3(f);
        !          1478: #if dbg_frdb
        !          1479:        if((!(p->ident&IsTxtln))||(p->ident&(IsALL&(~IsTxtln))))
        !          1480:                err("frdb_txtln: %s",txtln_toa(p));
        !          1481: #endif
        !          1482: #if dbg_frdb_toa
        !          1483:        err("frdb_txtln: %s",txtln_toa(p));
        !          1484: #endif
        !          1485:        if(p->ident&Txtln_label) p->l = frdb_label(f);
        !          1486:        if(ferror(f)) return(-errno); else return(1);
        !          1487:        }
        !          1488: #else
        !          1489: int frdb_txtln(fp,lp)
        !          1490:     FILE *fp;
        !          1491:     Txtln *lp;
        !          1492: {   TxtlnF lf;
        !          1493:     int stat;
        !          1494:        if((stat=fread(&lf,sizeof(TxtlnF),1,fp))!=1)
        !          1495:                abort("can't read TxtlnF, status %d",stat);
        !          1496:        *lp = empty_Txtln;
        !          1497:        lp->ident = lf.ident;
        !          1498:        lp->bx = lf.bx;
        !          1499:        lp->basl = lf.basl;
        !          1500:        lp->size = lf.size;
        !          1501:        if(lf.pmny>0) lp->proj=(short *)malloc(lf.pmny);
        !          1502:        else lp->proj = NULL;
        !          1503:        lp->cp = NULL;
        !          1504:        lp->ws.mny = lf.wmny;
        !          1505:        lp->cs.mny = lf.cmny;
        !          1506:        lp->bs.mny = lf.bmny;
        !          1507: #if dbg_frdb
        !          1508:        if((!(lp->ident&IsTxtln))||(lp->ident&(IsALL&(~IsTxtln))))
        !          1509:                err("frdb_txtln: %s",txtln_toa(lp));
        !          1510: #endif
        !          1511: #if dbg_frdb_toa
        !          1512:        err("frdb_txtln: %s",txtln_toa(lp));
        !          1513: #endif
        !          1514:        if(lp->ident&Txtln_label) lp->l = frdb_label(fp);
        !          1515:        if(ferror(fp)) return(-errno); else return(1);
        !          1516:        }
        !          1517: #endif
        !          1518: 
        !          1519: frdb_txtln_etc(fp,lp,etc)
        !          1520:     FILE *fp;
        !          1521:     Txtln *lp;
        !          1522:     Ident etc;
        !          1523: {   TxtlnF lf;
        !          1524:     int stat;
        !          1525:        frdb_txtln(fp,lp);
        !          1526:        if(lp->proj!=NULL) {/* read projection array someday */};
        !          1527:        if(etc&IsWord && lp->ws.mny>0)
        !          1528:                frdb_words_etc(fp,&(lp->ws),etc);
        !          1529:        else lp->ws = empty_Words;
        !          1530:        if(etc&IsChar && lp->cs.mny>0)
        !          1531:                frdb_chars_etc(fp,&(lp->cs),etc);
        !          1532:        else lp->cs = empty_Chars;
        !          1533:        if(etc&IsBlob && lp->bs.mny>0)
        !          1534:                frdb_blobs_etc(fp,&(lp->bs),etc);
        !          1535:        else lp->bs = empty_Blobs;
        !          1536:        if(ferror(fp)) return(-errno); else return(1);
        !          1537:        }
        !          1538: 
        !          1539: /* read number of txtlns, and parts */
        !          1540: frdb_txtlns_etc(fp,lsp,etc)
        !          1541:     FILE *fp;
        !          1542:     Txtlns *lsp;
        !          1543:     Ident etc;
        !          1544: {   int li;
        !          1545:     register Txtln *lp,**lpp;
        !          1546:        if(lsp->mny<=0) {
        !          1547:                *lsp = empty_Txtlns;
        !          1548:                return(1);
        !          1549:                };
        !          1550:        if((lpp=lsp->lpa=(Txtln **)malloc((lsp->mny+1)*sizeof(Txtln *)))==NULL)
        !          1551:                abort("can't alloc Txtlns.lpa array");
        !          1552:        for(li=0; li<lsp->mny; li++) {
        !          1553:                *(lpp++) = lp = alloc_txtln();
        !          1554:                frdb_txtln_etc(fp,lp,etc);
        !          1555:                };
        !          1556:        *lpp = NULL;
        !          1557:        if(ferror(fp)) return(-errno); else return(1);
        !          1558:        }
        !          1559: 
        !          1560: /* Append a txtln to the end of a txtlns set. 
        !          1561:    Do NOT maintain Txtlns in order sorted ascending on Txtln.bx.a.y.
        !          1562:    Return appended Txtln *. */
        !          1563: Txtln *append_txtln(lp,lsp)
        !          1564:     Txtln *lp;
        !          1565:     Txtlns *lsp;
        !          1566: {   register Txtln *rp,**rpp,**npp;
        !          1567:        if(lsp->mny==0) {
        !          1568:                if((lsp->lpa=(Txtln **)malloc(2*sizeof(Txtln *)))==NULL)
        !          1569:                        abort("append_txtln: can't malloc lsp->lpa");
        !          1570:                }
        !          1571:        else {  if((lsp->lpa=(Txtln **)realloc(
        !          1572:                                lsp->lpa,
        !          1573:                                (lsp->mny+2)*sizeof(Txtln *))
        !          1574:                                )==NULL)
        !          1575:                        abort("append_txtln: can't realloc lsp->lpa");
        !          1576:                };
        !          1577:        lsp->lpa[lsp->mny] = lp;
        !          1578:        lsp->lpa[++lsp->mny] = NULL;
        !          1579:        return(lp);
        !          1580:        }
        !          1581: 
        !          1582: /* remove a txtln from a txtlns set */
        !          1583: remove_txtln(lp,lsp)
        !          1584:     Txtln *lp;
        !          1585:     Txtlns *lsp;
        !          1586: {   register Txtln *rp,**rpp,**npp;
        !          1587:        if(lsp->mny==0) err("can't remove Txtln - Txtlns empty");
        !          1588:        else {  for(rp= *(rpp=lsp->lpa); rp!=NULL; rp= *(++rpp)) if(rp==lp) break;
        !          1589:                if(rp==NULL) err("can't remove Txtln - not found");
        !          1590:                else {  /* move later entries up */
        !          1591:                        npp=rpp+1;
        !          1592:                        do *(rpp++)= *(npp++); while ((*rpp)!=NULL);
        !          1593:                        if((--(lsp->mny))==0) {free(lsp->lpa); lsp->lpa=NULL;};
        !          1594:                        /* don't bother to realloc downwards */
        !          1595:                        };
        !          1596:                };
        !          1597:        }
        !          1598: 
        !          1599: free_txtln_etc(p,etc)
        !          1600:     Txtln *p;
        !          1601:     Ident etc;
        !          1602: {      free_words_etc(&(p->ws),etc);
        !          1603:        free_chars_etc(&(p->cs),etc);
        !          1604:        free_blobs_etc(&(p->bs),etc);
        !          1605:        if(p->ident&Txtln_label && p->l!=NULL) {
        !          1606:                free(p->l);  p->l=NULL;  p->ident &= ~Txtln_label;
        !          1607:                };
        !          1608:        if(etc&IsTxtln) {
        !          1609:                if(p->l!=NULL) free(p->l);
        !          1610:                free(p);
        !          1611:                free_census(Txtln,1);
        !          1612:                };
        !          1613:        }
        !          1614: 
        !          1615: /* Unconditionally free the malloc-space array of pointers, and empty the set.
        !          1616:    Don't free the records that it owned.
        !          1617:    */
        !          1618: free_txtlns(lsp)
        !          1619:     Txtlns *lsp;
        !          1620: {      if(lsp->lpa!=NULL) { free(lsp->lpa); lsp->lpa = NULL; }
        !          1621:        lsp->mny = 0;
        !          1622:        }
        !          1623: 
        !          1624: free_txtlns_etc(lsp,etc)
        !          1625:     Txtlns *lsp;
        !          1626:     Ident etc;
        !          1627: {   register Txtln *lp,**lpp;
        !          1628:        if(lsp->mny>0&&(etc&IsTxtln))
        !          1629:                for(lp= *(lpp=lsp->lpa); lp!=NULL; lp= *(++lpp))
        !          1630:                        free_txtln_etc(lp,etc);
        !          1631:        free_txtlns(lsp);
        !          1632:        }
        !          1633: 
        !          1634: /* return a pointer to a distinct and duplicate copy of *lp,
        !          1635:    created out of malloc space */
        !          1636: Txtln *dup_txtln(lp)
        !          1637:     Txtln *lp;
        !          1638: {   Txtln *dup;
        !          1639:        if((dup=(Txtln *)malloc(sizeof(Txtln)))==NULL)
        !          1640:                abort("can't dup Txtln");
        !          1641:        alloc_census(Txtln,1);
        !          1642:        *dup = *lp;
        !          1643:        return(dup);
        !          1644:        }
        !          1645: 
        !          1646: /* Return a pointer to a distinct and duplicate copy of *lp, and its parts
        !          1647:    as specified by `etc', created out of malloc space. */
        !          1648: Txtln *dup_txtln_etc(lp,etc)
        !          1649:     Txtln *lp;
        !          1650:     Ident etc;
        !          1651: {   Txtln *dup;
        !          1652:        dup = dup_txtln(lp);
        !          1653:        if(etc&IsBlob&&dup->bs.mny>0) dup->bs = *dup_blobs_etc(&(lp->bs),etc);
        !          1654:        else dup->bs = empty_Blobs;
        !          1655:        if(etc&IsChar&&dup->cs.mny>0) dup->cs = *dup_chars_etc(&(lp->cs),etc);
        !          1656:        else dup->cs = empty_Chars;
        !          1657:        if(etc&IsWord&&dup->ws.mny>0) dup->ws = *dup_words_etc(&(lp->ws),etc);
        !          1658:        else dup->ws = empty_Words;
        !          1659:        return(dup);
        !          1660:        }
        !          1661: 
        !          1662: /* Return a pointer to a distinct local static duplicate of non-empty *lsp.
        !          1663:    Its lpa array is created newly out of malloc space.
        !          1664:    If etc&IsTxtln, all its Txtlns are also duplicated,
        !          1665:    else the contents of lpa point to the old unduplicated Txtlns.
        !          1666:    */
        !          1667: Txtlns *dup_txtlns_etc(lsp,etc)
        !          1668:     Txtlns *lsp;
        !          1669:     Ident etc;         /* parts to duplicate */
        !          1670: {   static Txtlns dup;
        !          1671:     register Txtln **lpp,**dpp;
        !          1672:        if((dup.mny = lsp->mny)<=0) dup = empty_Txtlns;
        !          1673:        else {  if((dup.lpa=(Txtln **)malloc((dup.mny+1)*sizeof(Txtln *)))==NULL)
        !          1674:                        abort("can't dup ls.lpa");
        !          1675:                for(lpp=lsp->lpa,dpp=dup.lpa; *lpp!=NULL; lpp++,dpp++) {
        !          1676:                        if(etc&IsTxtln) *dpp = dup_txtln_etc(*lpp,etc);
        !          1677:                        else *dpp = *lpp;
        !          1678:                        };
        !          1679:                *dpp = NULL;
        !          1680:                };
        !          1681:        return(&dup);
        !          1682:        }
        !          1683: 
        !          1684: Word *alloc_word()
        !          1685: {    Word *p;
        !          1686:        if((p=(Word *)malloc(sizeof(Word)))==NULL)
        !          1687:                abort("can't alloc Word");
        !          1688:        alloc_census(Word,1);
        !          1689:        *p = empty_Word;
        !          1690:        return(p);
        !          1691:        }
        !          1692: 
        !          1693: char *word_toa(p)
        !          1694:     Word *p;
        !          1695: {    static char s[80];
        !          1696:        sprintf(s,"%s bx%s ws%0.2f c%d b%d m%s w%d",
        !          1697:                ident_toa(p->ident),
        !          1698:                bbx_toa(&(p->bx)),
        !          1699:                p->wsp,
        !          1700:                p->cs.mny,p->bs.mny,merit_toa(p->m),p->ws.mny);
        !          1701:        return(s);
        !          1702:        }
        !          1703: 
        !          1704: /* Are two Words identical?  A not quite exhaustively-detailed test. */
        !          1705: boolean eq_word(w1,w2)
        !          1706:     Word *w1,*w2;
        !          1707: {   register Char **c1,**c2;
        !          1708:        if(w1->cs.mny!=w2->cs.mny) return(F);
        !          1709:        for(c1=w1->cs.cpa,c2=w2->cs.cpa; *c1!=NULL; c1++,c2++) {
        !          1710:                if((*c1)->area!=(*c2)->area) return(F);
        !          1711:                if(!bbx_eq(&((*c1)->bx),&((*c2)->bx))) return(F);
        !          1712:                if((*c1)->bmny!=(*c2)->bmny) return(F);
        !          1713:                };
        !          1714:        /* don't bother to compare Blob lists in detail (they aren't
        !          1715:           in any particular order). */
        !          1716:        return(T);
        !          1717:        }
        !          1718: 
        !          1719: /* Compute a hash key for this Word that is likely to indicate whether or
        !          1720:    not it is geometrically identical to another Word. */
        !          1721: int hash_word(w)
        !          1722:     Word *w;
        !          1723: {   register int k;
        !          1724:     register Char **c;
        !          1725:        if(w->cs.mny==0) return(0);
        !          1726:        k = w->bx.a.x * w->bx.a.y * w->bx.b.x * w->bx.b.y;
        !          1727:        for(c=w->cs.cpa; (*c)!=NULL; c++) {
        !          1728:                k += (*c)->bx.a.x * (*c)->bx.a.y * (*c)->bx.b.x
        !          1729:                        * (*c)->bx.b.y * (*c)->bmny * (*c)->area;
        !          1730:                };
        !          1731:        return(k);
        !          1732:        }
        !          1733: 
        !          1734: #if FWRI
        !          1735: fwrb_word(f,p)
        !          1736:     FILE *f;
        !          1737:     Word *p;
        !          1738: {
        !          1739: #if dbg_fwrb
        !          1740:        if((!(p->ident&IsWord))||(p->ident&(IsALL&(~IsWord))))
        !          1741:                err("fwrb_word: %s",word_toa(p));
        !          1742:        if(p->ident&Word_label && p->l==NULL) {
        !          1743:                err("fwrb_page: Word_label set but .l==NULL");
        !          1744:                p->ident &= ~Word_label;
        !          1745:                };
        !          1746: #endif
        !          1747:        fwri_Ident(f,p->ident);
        !          1748:        fwri_Bbx(f,&(p->bx));
        !          1749:        fwri_int2(f,p->wsp*UCHAR_MAX);
        !          1750:        fwri_Merit(f,p->m);
        !          1751:        fwri_Prob(f,p->p);
        !          1752:        fwri_uint2(f,p->ws.mny);
        !          1753:        fwri_uint2(f,p->cs.mny);
        !          1754:        fwri_uint3(f,p->bs.mny);
        !          1755:        /* fwri_int4(f,p->hash); */
        !          1756: #if dbg_fwrb_toa
        !          1757:        err("fwrb_word: %s",word_toa(p));
        !          1758: #endif
        !          1759:        if(p->ident&Word_label) fwrb_label(f,p->l);
        !          1760:        }
        !          1761: #else
        !          1762: fwrb_word(fp,wp)
        !          1763:     FILE *fp;
        !          1764:     Word *wp;
        !          1765: {   WordF wf;
        !          1766:     int stat;
        !          1767: #if dbg_fwrb
        !          1768:        if((!(wp->ident&IsWord))||(wp->ident&(IsALL&(~IsWord))))
        !          1769:                err("fwrb_word: %s",word_toa(wp));
        !          1770: #endif
        !          1771:        memset(&wf,'\0',sizeof(wf));
        !          1772:        wf.ident = wp->ident;
        !          1773:        wf.bx = wp->bx;
        !          1774:        wf.wsp = wp->wsp;
        !          1775:        wf.m = wp->m;
        !          1776:        wf.wmny = wp->ws.mny;
        !          1777:        wf.cmny = wp->cs.mny;
        !          1778:        wf.bmny = wp->bs.mny;
        !          1779:        if((stat=fwrite(&wf,sizeof(WordF),1,fp))!=1)
        !          1780:                abort("can't write WordF, status %d",stat);
        !          1781: #if dbg_fwrb_toa
        !          1782:        err("fwrb_word: %s",word_toa(wp));
        !          1783: #endif
        !          1784:        if(wf.ident&Word_label) fwrb_label(fp,wp->l);
        !          1785:        }
        !          1786: #endif
        !          1787: 
        !          1788: fwrb_words_etc(fp,ws,etc)
        !          1789:     FILE *fp;
        !          1790:     Words ws;
        !          1791:     Ident etc;
        !          1792: {   register Word *wp,**wpp;
        !          1793:        if(ws.mny>0) for(wp= *(wpp=ws.wpa); wp!=NULL; wp= *(++wpp))
        !          1794:                fwrb_word_etc(fp,wp,etc);
        !          1795:        }
        !          1796: 
        !          1797: fwrb_word_etc(fp,wp,etc)
        !          1798:     FILE *fp;
        !          1799:     Word *wp;
        !          1800:     Ident etc;
        !          1801: {   static Ident parts = (IsWord|IsChar|IsBlob);
        !          1802:     Word wd;
        !          1803:        if((etc&parts)!=parts) /* write selected parts */ {
        !          1804:                wd = *wp;
        !          1805:                if(!(etc&IsWord)) wd.ws.mny=0;
        !          1806:                if(!(etc&IsChar)) wd.cs.mny=0;
        !          1807:                if(!(etc&IsBlob)) wd.bs.mny=0;
        !          1808:                wp = &wd;       /* write modified record */
        !          1809:                };
        !          1810:        fwrb_word(fp,wp);
        !          1811:        if(etc&IsWord) fwrb_words_etc(fp,wp->ws,IsALL);
        !          1812:        fwrb_chars_etc(fp,wp->cs,etc);
        !          1813:        fwrb_blobs_etc(fp,wp->bs,etc);
        !          1814:        }
        !          1815: 
        !          1816: #if FRDI
        !          1817: int frdb_word(f,p)
        !          1818:     FILE *f;
        !          1819:     Word *p;
        !          1820: {      *p = empty_Word;
        !          1821:        if(feof(f))
        !          1822:                return(0);
        !          1823:        p->ident=frdi_Ident(f);
        !          1824:        frdi_Bbx(f,&(p->bx));
        !          1825:        p->wsp=frdi_int2(f)/(float)UCHAR_MAX;
        !          1826:        p->m=frdi_Merit(f);
        !          1827:        p->p=frdi_Prob(f);
        !          1828:        p->ws.mny=frdi_uint2(f);
        !          1829:        p->cs.mny=frdi_uint2(f);
        !          1830:        p->bs.mny=frdi_uint3(f);
        !          1831:        /* p->hash=frdi_int4(f); */
        !          1832: #if dbg_frdb
        !          1833:        if((!(p->ident&IsWord))||(p->ident&(IsALL&(~IsWord))))
        !          1834:                err("frdb_word: %s",word_toa(p));
        !          1835: #endif
        !          1836: #if dbg_frdb_toa
        !          1837:        err("frdb_word: %s",word_toa(p));
        !          1838: #endif
        !          1839:        if(p->ident&Word_label) p->l = frdb_label(f);
        !          1840:        if(ferror(f)) return(-errno); else return(1);
        !          1841:        }
        !          1842: #else
        !          1843: int frdb_word(fp,wp)
        !          1844:     FILE *fp;
        !          1845:     Word *wp;
        !          1846: {   WordF wf;
        !          1847:     int stat;
        !          1848:        if((stat=fread(&wf,sizeof(WordF),1,fp))!=1)
        !          1849:                abort("can't read WordF, status %d",stat);
        !          1850:        *wp = empty_Word;
        !          1851:        wp->ident = wf.ident;
        !          1852:        wp->bx = wf.bx;
        !          1853:        wp->wsp = wf.wsp;
        !          1854:        wp->m = wf.m;
        !          1855:        wp->ws.mny = wf.wmny;
        !          1856:        wp->cs.mny = wf.cmny;
        !          1857:        wp->bs.mny = wf.bmny;
        !          1858: #if dbg_frdb
        !          1859:        if((!(wp->ident&IsWord))||(wp->ident&(IsALL&(~IsWord))))
        !          1860:                err("frdb_word: %s",word_toa(wp));
        !          1861: #endif
        !          1862: #if dbg_frdb_toa
        !          1863:        err("frdb_word: %s",word_toa(wp));
        !          1864: #endif
        !          1865:        if(wp->ident&Word_label) wp->l = frdb_label(fp);
        !          1866:        if(ferror(fp)) return(-errno); else return(1);
        !          1867:        }
        !          1868: #endif
        !          1869: 
        !          1870: frdb_word_etc(fp,wp,etc)
        !          1871:     FILE *fp;
        !          1872:     Word *wp;
        !          1873:     Ident etc;
        !          1874: {   WordF cf;
        !          1875:     int stat;
        !          1876:        frdb_word(fp,wp);
        !          1877:        frdb_words_etc(fp,&(wp->ws),IsALL); /* rd alternatives entirely, if any */
        !          1878:        if(etc&IsChar && wp->cs.mny>0)
        !          1879:                frdb_chars_etc(fp,&(wp->cs),etc);
        !          1880:        else wp->cs = empty_Chars;
        !          1881:        if(etc&IsBlob && wp->bs.mny>0)
        !          1882:                frdb_blobs_etc(fp,&(wp->bs),etc);
        !          1883:        else wp->bs = empty_Blobs;
        !          1884:        if(ferror(fp)) return(-errno); else return(1);
        !          1885:        }
        !          1886: 
        !          1887: /* read words, and their parts */
        !          1888: frdb_words_etc(fp,wsp,etc)
        !          1889:     FILE *fp;
        !          1890:     Words *wsp;
        !          1891:     Ident etc;
        !          1892: {   int wi;
        !          1893:     register Word *wp,**wpp;
        !          1894:        if(wsp->mny<=0) {
        !          1895:                *wsp = empty_Words;
        !          1896:                return(1);
        !          1897:                };
        !          1898:        if((wpp=wsp->wpa=(Word **)malloc((wsp->mny+1)*sizeof(Word *)))==NULL)
        !          1899:                abort("can't alloc Words.wpa array");
        !          1900:        for(wi=0; wi<wsp->mny; wi++) {
        !          1901:                *(wpp++) = wp = alloc_word();
        !          1902:                frdb_word_etc(fp,wp,etc);
        !          1903:                };
        !          1904:        *wpp = NULL;
        !          1905:        if(ferror(fp)) return(-errno); else return(1);
        !          1906:        }
        !          1907: 
        !          1908: /* remove a word from a words set */
        !          1909: remove_word(wp,wsp)
        !          1910:     Word *wp;
        !          1911:     Words *wsp;
        !          1912: {   register Word *rp,**rpp,**npp;
        !          1913:        if(wsp->mny==0) err("can't remove Word - Words empty");
        !          1914:        else {  for(rp= *(rpp=wsp->wpa); rp!=NULL; rp= *(++rpp)) if(rp==wp) break;
        !          1915:                if(rp==NULL) err("can't remove Word - not found");
        !          1916:                else {  /* move later entries up */
        !          1917:                        npp=rpp+1;
        !          1918:                        do *(rpp++)= *(npp++); while ((*rpp)!=NULL);
        !          1919:                        if((--(wsp->mny))==0) {free(wsp->wpa); wsp->wpa=NULL;};
        !          1920:                        /* don't bother to realloc downwards */
        !          1921:                        };
        !          1922:                };
        !          1923:        }
        !          1924: 
        !          1925: /* Remove a Word from a Txtln's Words set.
        !          1926:    Shrink the Txtln's Bbx if the Word abuts it on the left or right.
        !          1927:    Ensure that the first Word in the line has wsp==0.0
        !          1928:    BUG:  doesn't update wsp of trailing Word */
        !          1929: remove_word_txtln(wp,lp)
        !          1930:     Word *wp;
        !          1931:     Txtln *lp;
        !          1932: {   register Word *cwp,**wpp;
        !          1933:        remove_word(wp,&(lp->ws));
        !          1934:        if(lp->ws.mny>0) {
        !          1935:                if(wp->bx.a.x==lp->bx.a.x || wp->bx.b.x==lp->bx.b.x) {
        !          1936:                        /* recompute possibly shrunken bx */
        !          1937:                        lp->bx=empty_Bbx;
        !          1938:                        for(cwp= *(wpp=lp->ws.wpa); cwp!=NULL; cwp= *(++wpp))
        !          1939:                                merge_bbx(&(cwp->bx),&(lp->bx));
        !          1940:                        };
        !          1941:                (lp->ws.wpa[0])->wsp=0.0;
        !          1942:                };
        !          1943:        }
        !          1944: 
        !          1945: /* Append a word to the end of a words set.
        !          1946:    Do NOT maintain set in order ascending on Word.bx.a.x.
        !          1947:    Return the appended Word. */
        !          1948: Word *append_word(wp,wsp)
        !          1949:     Word *wp;
        !          1950:     Words *wsp;
        !          1951: {      if(wsp->mny==0) {
        !          1952:                if((wsp->wpa=(Word **)malloc(2*sizeof(Word *)))==NULL)
        !          1953:                        abort("append_word: can't malloc wsp->wpa");
        !          1954:                }
        !          1955:        else {  if((wsp->wpa=(Word **)realloc(
        !          1956:                                wsp->wpa,
        !          1957:                                (wsp->mny+2)*sizeof(Word *))
        !          1958:                                )==NULL)
        !          1959:                        abort("append_word: can't realloc wsp->wpa");
        !          1960:                };
        !          1961:        wsp->wpa[wsp->mny++] = wp;
        !          1962:        wsp->wpa[wsp->mny] = NULL;
        !          1963:        return(wp);
        !          1964:        }
        !          1965: 
        !          1966: /* Insert a Word into a Words set.  Words is assumed to be sorted ascending
        !          1967:    on Word.bx.a.x, and this order is maintained.
        !          1968:    Words with equal Word.bx.a.x are stored in the order that they were
        !          1969:    inserted:  so that the first one inserted remains the first among equals
        !          1970:    (this feature is important for certain WordSet applications).
        !          1971:    Return inserted (Word *) */
        !          1972: Word *insert_word(wp,wsp)
        !          1973:     Word *wp;
        !          1974:     Words *wsp;
        !          1975: {   register Word **wpp,*nwp,*pwp;
        !          1976:        if(wsp->mny==0) {
        !          1977:                if((wsp->wpa=(Word **)malloc(2*sizeof(Word *)))==NULL)
        !          1978:                        abort("insert_word: can't malloc wsp->wpa");
        !          1979:                wsp->wpa[wsp->mny] = wp;
        !          1980:                }
        !          1981:        else {  if((wpp=wsp->wpa=(Word **)realloc(
        !          1982:                                wsp->wpa,
        !          1983:                                (wsp->mny+2)*sizeof(Word *))
        !          1984:                                )==NULL)
        !          1985:                        abort("insert_word: can't realloc wsp->wpa");
        !          1986:                while(((*wpp)!=NULL)&&(*wpp)->bx.a.x<=wp->bx.a.x) wpp++;
        !          1987:                /* **wpp is now 1st entry > *wp in sorted order; insert
        !          1988:                   new word just before it */
        !          1989:                pwp=wp;
        !          1990:                do {    nwp= *wpp;
        !          1991:                        *(wpp++)=pwp;
        !          1992:                        pwp=nwp;
        !          1993:                        }
        !          1994:                while(pwp!=NULL);
        !          1995:                };
        !          1996:        wsp->wpa[++wsp->mny] = NULL;
        !          1997:        return(wp);
        !          1998:        }
        !          1999: 
        !          2000: /* Insert a Word into the Words owned by a given Txtln,
        !          2001:    maintaining the Txtln's Bbx and sorted order in the Words set.
        !          2002:    Return the inserted Word * */
        !          2003: Word *insert_word_txtln(wp,lp)
        !          2004:     Word *wp;
        !          2005:     Txtln *lp;
        !          2006: {      merge_bbx(&(wp->bx),&(lp->bx));
        !          2007:        return(insert_word(wp,&(lp->ws)));
        !          2008:        }
        !          2009: 
        !          2010: /* Free wordinterp and its parts as specified in etc.
        !          2011:    Always free its strings (if any).
        !          2012:    If etc&IsWordInterp, free the record itself also.
        !          2013: */
        !          2014: free_wordinterp_etc(p,etc)
        !          2015:     WordInterp *p;
        !          2016:     Ident etc;
        !          2017: {      if(p->s!=NULL) { free(p->s);  p->s=NULL; };
        !          2018:        if(p->pp!=NULL) { free(p->pp);  p->pp=NULL; };
        !          2019:        if(p->by!=NULL) { free(p->by);  p->by=NULL; };
        !          2020:        if(p->po!=NULL) { free(p->po);  p->po=NULL; };
        !          2021:        if(p->ps!=NULL) { free(p->ps);  p->ps=NULL; };
        !          2022:        if(etc&IsWordInterp) free(p);
        !          2023:        }
        !          2024: 
        !          2025: /* Duplicate (return ptr to local static copy of) a WordInterp.
        !          2026:    Always free its strings (if any).
        !          2027:    'etc' is ignored.
        !          2028: */
        !          2029: WordInterp *dup_wordinterp_etc(p,etc)
        !          2030:     WordInterp *p;
        !          2031:     Ident etc;
        !          2032: {   static WordInterp d;
        !          2033:        d = *p;
        !          2034:        if(p->s!=NULL) d.s = strdup(p->s);
        !          2035:        if(p->pp!=NULL) d.pp = strdup(p->pp);
        !          2036:        if(p->by!=NULL) d.by = strdup(p->by);
        !          2037:        if(p->po!=NULL) d.po = strdup(p->po);
        !          2038:        if(p->ps!=NULL) d.ps = strdup(p->ps);
        !          2039:        return(&d);
        !          2040:        }
        !          2041: 
        !          2042: /* Unconditionally free the malloc-space array of pointers, and empty the set.
        !          2043:    Don't free the records that it owned.
        !          2044:    */
        !          2045: free_words(wsp)
        !          2046:     Words *wsp;
        !          2047: {      if(wsp->wpa!=NULL) { free(wsp->wpa); wsp->wpa = NULL; }
        !          2048:        wsp->mny = 0;
        !          2049:        }
        !          2050: 
        !          2051: free_words_etc(wsp,etc)
        !          2052:     Words *wsp;
        !          2053:     Ident etc;
        !          2054: {   register Word *wp,**wpp;
        !          2055:     int wi;
        !          2056:        if(wsp->mny>0&&(etc&IsWord))
        !          2057:                for(wp= *(wpp=wsp->wpa); wp!=NULL; wp= *(++wpp))
        !          2058:                        free_word_etc(wp,etc);
        !          2059:        free_words(wsp);
        !          2060:        }
        !          2061: 
        !          2062: /* return a pointer to a distinct and duplicate copy of *wp,
        !          2063:    created out of malloc space */
        !          2064: Word *dup_word(wp)
        !          2065:     Word *wp;
        !          2066: {   Word *dup;
        !          2067:        if((dup=(Word *)malloc(sizeof(Word)))==NULL)
        !          2068:                abort("can't dup Word");
        !          2069:        alloc_census(Word,1);
        !          2070:        *dup = *wp;
        !          2071:        return(dup);
        !          2072:        }
        !          2073: 
        !          2074: /* Return a pointer to a distinct and duplicate copy of *wp, and ALL it owns,
        !          2075:    created out of malloc space. (HOW they are duplicated is controlled by flags
        !          2076:    in etc.) */
        !          2077: Word *dup_word_etc(wp,etc)
        !          2078:     Word *wp;
        !          2079:     Ident etc;
        !          2080: {   Word *dup;
        !          2081:        dup = dup_word(wp);
        !          2082:        if(dup->ws.mny>0) dup->ws = *dup_words_etc(&(wp->ws),etc);
        !          2083:        else dup->ws = empty_Words;
        !          2084:        if(dup->cs.mny>0) dup->cs = *dup_chars_etc(&(wp->cs),etc);
        !          2085:        else dup->cs = empty_Chars;
        !          2086:        if(dup->bs.mny>0) dup->bs = *dup_blobs_etc(&(wp->bs),etc);
        !          2087:        else dup->bs = empty_Blobs;
        !          2088:        return(dup);
        !          2089:        }
        !          2090: 
        !          2091: /* Return a pointer to a distinct local static duplicate of non-empty *wsp.
        !          2092:    Its wpa array is created newly out of malloc space.
        !          2093:    If etc&IsWord, all its Words are also duplicated,
        !          2094:    else the contents of wpa point to the old unduplicated Words.
        !          2095:    */
        !          2096: Words *dup_words_etc(wsp,etc)
        !          2097:     Words *wsp;
        !          2098:     Ident etc;         /* parts to duplicate */
        !          2099: {   static Words dup;
        !          2100:     register Word **wpp,**dpp;
        !          2101:        if((dup.mny=wsp->mny)<=0) dup = empty_Words;
        !          2102:        else {  if((dup.wpa=(Word **)malloc((dup.mny+1)*sizeof(Word *)))==NULL)
        !          2103:                        abort("can't dup ws.wpa");
        !          2104:                for(wpp=wsp->wpa,dpp=dup.wpa; *wpp!=NULL; wpp++,dpp++) {
        !          2105:                        if(etc&IsWord) *dpp = dup_word_etc(*wpp,etc);
        !          2106:                        else *dpp = *wpp;
        !          2107:                        };
        !          2108:                *dpp = NULL;
        !          2109:                };
        !          2110:        return(&dup);
        !          2111:        }
        !          2112: 
        !          2113: WordInterp *alloc_wordinterp()
        !          2114: {    WordInterp *p;
        !          2115:        if((p=(WordInterp *)malloc(sizeof(WordInterp)))==NULL)
        !          2116:                abort("can't alloc WordInterp");
        !          2117:        else {  *p = empty_WordInterp;
        !          2118:                return(p);
        !          2119:                };
        !          2120:        }
        !          2121: 
        !          2122: char *wordinterp_toa(p)
        !          2123:     WordInterp *p;
        !          2124: {    static char s[80];
        !          2125:        sprintf(s,"%s %s %s|%s|%s|%s",
        !          2126:                ident_toa(p->ident),
        !          2127:                ((p->s!=NULL)? p->s: ""),
        !          2128:                ((p->pp!=NULL)? p->pp: ""),
        !          2129:                ((p->by!=NULL)? p->by: ""),
        !          2130:                ((p->po!=NULL)? p->po: ""),
        !          2131:                ((p->ps!=NULL)? p->ps: "")
        !          2132:                );
        !          2133:        return(s);
        !          2134:        }
        !          2135: 
        !          2136: /* Free Word and its parts, as specified by etc.
        !          2137:    Always free the pointer-arrays of ws, cs & bs.
        !          2138:    If etc&IsWord, free all Words in ws also.
        !          2139:    If etc&IsChar, free all Chars in cs also.
        !          2140:    If etc&IsBlob, free all Blobs in bs also.
        !          2141: */
        !          2142: free_word_etc(p,etc)
        !          2143:     Word *p;
        !          2144:     Ident etc;
        !          2145: #define dbg_fwe (F)
        !          2146: {      if(dbg_fwe) {
        !          2147:                err("free_word_etc: %s",word_toa(p));
        !          2148:                err("free_word_etc: %s",ident_toa(etc));
        !          2149:                err_census_all;
        !          2150:                };
        !          2151:        free_words_etc(&(p->ws),IsALL);
        !          2152:        free_chars_etc(&(p->cs),etc);
        !          2153:        free_blobs_etc(&(p->bs),etc);
        !          2154:        if(p->ident&Word_label && p->l!=NULL) {
        !          2155:                free(p->l);  p->l=NULL;  p->ident &= ~Word_label;
        !          2156:                };
        !          2157:        if(etc&IsWord) free_word(p);
        !          2158:        if(dbg_fwe) err_census_all;
        !          2159:        }
        !          2160: 
        !          2161: free_word(wp)
        !          2162:     Word *wp;
        !          2163: {      free(wp);
        !          2164:        free_census(Word,1);
        !          2165:        }
        !          2166: 
        !          2167: fwrb_sfeats(fp,sfv)
        !          2168:     FILE *fp;
        !          2169:     SFv sfv;
        !          2170: {   register Pval *vp,*vq;
        !          2171:        for(vq=(vp=sfv)+SF_MNY; vp<vq; vp++)
        !          2172:                fwri_int3(fp,USHRT_MAX*(*vp)+0.5);
        !          2173: #if dbg_fwrb_toa
        !          2174:        /** err("fwrb_sfeats: %s",sfeats_toa(sfv)); **/
        !          2175: #endif
        !          2176:        }
        !          2177: 
        !          2178: int frdb_sfeats(fp,sfv)
        !          2179:     FILE *fp;
        !          2180:     SFv sfv;
        !          2181: {   register Pval *vp,*vq;
        !          2182:        for(vq=(vp=sfv)+SF_MNY; vp<vq; vp++)
        !          2183:                *vp = frdi_int3(fp)/((Pval)USHRT_MAX);
        !          2184: #if dbg_frdb_toa
        !          2185:        /** err("frdb_sfeats: %s",sfeats_toa(sfv)); **/
        !          2186: #endif
        !          2187:        }
        !          2188: 
        !          2189: /* return ptr to local static copy of Shapes of given size */
        !          2190: Shapes *alloc_shapes(mny)
        !          2191:     int mny;
        !          2192: {   static Shapes shs;
        !          2193:        shs.mny = shs.alloc = mny;
        !          2194:        if((shs.sa=(Nb_s *)malloc(shs.alloc*sizeof(Nb_s)))==NULL)
        !          2195:                abort("can't alloc Shapes.sa[%d]",shs.alloc);
        !          2196:        return(&shs);
        !          2197:        }
        !          2198: 
        !          2199: fwrb_shapes(fp,p)
        !          2200:     FILE *fp;
        !          2201:     Shapes *p; /* on entry, p->mny has already been written */
        !          2202: {   int dim;
        !          2203:     register Nb_s *sp,*sq;
        !          2204:     register Pval *vp;
        !          2205:        if(p->mny>0&&p->sa!=NULL) {
        !          2206:                for(sq=(sp=p->sa)+p->mny; sp<sq; sp++) {
        !          2207:                        dim=Sh_dim[sp->t&(~Sh_tiny)];
        !          2208:                        fwri_uint1(fp,(sp->t&0x7F)|((sp->t&Sh_tiny)?0x80:0x00));
        !          2209:                        fwri_int2(fp,(*(vp=sp->p))*SHRT_MAX);
        !          2210:                        if(dim>1) { fwri_int2(fp,(*(++vp))*SHRT_MAX);
        !          2211:                         if(dim>2) { fwri_int2(fp,(*(++vp))*SHRT_MAX);
        !          2212:                          if(dim>3) { fwri_int2(fp,(*(++vp))*SHRT_MAX);
        !          2213:                                } } };
        !          2214:                        };
        !          2215:                };
        !          2216: #if dbg_fwrb_toa
        !          2217:        /** err("fwrb_shapes: %s",shapes_toa(p)); **/
        !          2218: #endif
        !          2219:        }
        !          2220: 
        !          2221: int frdb_shapes(fp,p)
        !          2222:     FILE *fp;
        !          2223:     Shapes *p; /* on entry, p->mny has already been read */
        !          2224: {   register Nb_s *sp,*sq;
        !          2225:     register Pval *vp;
        !          2226:     uint1 ui1;
        !          2227:     int dim;
        !          2228:     
        !          2229:        if(p->mny>0) {
        !          2230:                *p = *alloc_shapes(p->mny);
        !          2231:                for(sq=(sp=p->sa)+p->mny; sp<sq; sp++) {
        !          2232:                        ui1 = frdi_uint1(fp);
        !          2233:                        sp->t = ui1&0x7F|((ui1&0x80)?Sh_tiny:0);
        !          2234:                        dim=Sh_dim[sp->t&(~Sh_tiny)];
        !          2235:                        *(vp=sp->p) = ((float)frdi_int2(fp))/SHRT_MAX;
        !          2236:                        if(dim>1) { *(++vp) = ((float)frdi_int2(fp))/SHRT_MAX;
        !          2237:                         if(dim>2) { *(++vp) = ((float)frdi_int2(fp))/SHRT_MAX;
        !          2238:                          if(dim>3) { *(++vp) = ((float)frdi_int2(fp))/SHRT_MAX;
        !          2239:                                } } };
        !          2240:                        };
        !          2241:                }
        !          2242:        else {  p->alloc = 0;
        !          2243:                if(p->sa!=NULL) free(p->sa);
        !          2244:                p->sa = NULL;
        !          2245:                };
        !          2246: #if dbg_frdb_toa
        !          2247:        /** err("frdb_shapes: %s",shapes_toa(p)); **/
        !          2248: #endif
        !          2249:        }
        !          2250: 
        !          2251: fwra_shapes_etc(fp,p,t)
        !          2252:     FILE *fp;
        !          2253:     Shapes *p;
        !          2254:     char *t;   /* string of shape types (empty => all) */
        !          2255: {   Nb_s *s;
        !          2256:     int i,j;
        !          2257:     char a[80],f[10];
        !          2258:        for(i=0,s=p->sa; i<p->mny; i++,s++) {
        !          2259:                if(t[0]=='\0'||strchr(t,Sh_nam[s->t&(~Sh_tiny)])!=NULL) {
        !          2260:                        sprintf(a,"%c%c ",
        !          2261:                                Sh_nam[s->t&(~Sh_tiny)],
        !          2262:                                (s->t&Sh_tiny)?'\'':' '
        !          2263:                                );
        !          2264:                        for(j=0;j<Sh_dim[s->t&(~Sh_tiny)];j++) {
        !          2265:                                sprintf(f," %0.3f",s->p[j]);
        !          2266:                                strcat(a,f);
        !          2267:                                };
        !          2268:                        fprintf(fp,"%s\n",a);
        !          2269:                        };
        !          2270:                };
        !          2271:        }
        !          2272: 
        !          2273: err_shapes(shp)
        !          2274:     Shapes *shp;
        !          2275: {   int si,di;
        !          2276:     Nb_s *sp;
        !          2277:        fprintf(stderr,"shapes %d:\n",shp->mny);
        !          2278:        for(si=0,sp=shp->sa; si<shp->mny; si++,sp++) {
        !          2279:                fprintf(stderr,"  %c",Sh_nam[sp->t]);
        !          2280:                for(di=0; di<Sh_dim[sp->t]; di++)
        !          2281:                        fprintf(stderr," %6.3f",sp->p[di]);
        !          2282:                fprintf(stderr,"\n");
        !          2283:                };
        !          2284:        }
        !          2285: 
        !          2286: /* Unconditionally free the malloc-space array of pointers, and empty the set.
        !          2287:    Don't free the records that it owned.
        !          2288:    */
        !          2289: free_shapes(shp)
        !          2290:     Shapes *shp;
        !          2291: {      if(shp->sa!=NULL) {
        !          2292:                if(shp->alloc<=0) abort("can't free unalloc'ed shapes");
        !          2293:                else { free(shp->sa); shp->sa=NULL; };
        !          2294:                };
        !          2295:        shp->mny = shp->alloc = 0;
        !          2296:        }
        !          2297: 
        !          2298: /* functions for RanParms */
        !          2299: 
        !          2300: RanParms *alloc_ranparms()
        !          2301: {    RanParms *rp;
        !          2302:        if((rp=(RanParms *)malloc(sizeof(RanParms)))==NULL)
        !          2303:                abort("alloc_ranparms: can't alloc");
        !          2304:        *rp = empty_RanParms;
        !          2305:        return(rp);
        !          2306:        }
        !          2307: 
        !          2308: free_ranparms(ran)
        !          2309:     RanParms *ran;
        !          2310: {      free(ran);
        !          2311:        }
        !          2312: 
        !          2313: RanParms *dup_ranparms(p)
        !          2314:     RanParms *p;
        !          2315: {   RanParms *dup;
        !          2316:        dup = alloc_ranparms();
        !          2317:        *dup = *p;
        !          2318:        return(dup);
        !          2319:        }
        !          2320: 
        !          2321: char *ranparms_toa(ran,select)
        !          2322:     RanParms *ran;
        !          2323:     char *select;      /* a string, subset of: "*rpabejkstxy" if "*" or "" ALL */
        !          2324: {   static char s[200];
        !          2325:     char p[20];
        !          2326:     char *cp;
        !          2327:     static char select_all[] = "abejkprstxy";
        !          2328:        s[0]='\0';
        !          2329:        if(select[0]=='*'||select[0]=='\0') select=select_all;
        !          2330:        for(cp=select; *cp!='\0'; cp++) {
        !          2331:            switch(*cp) {
        !          2332:                case 'a':  sprintf(p,"a%0.3f ",ran->skew/DtoR);  strcat(s,p);  break;
        !          2333:                case 'b':  sprintf(p,"b%0.3f ",ran->bhgt);  strcat(s,p);  break;
        !          2334:                case 'e':  sprintf(p,"e%0.3f ",ran->blur);  strcat(s,p);  break;
        !          2335:                case 'j':  sprintf(p,"j%0.3f ",ran->jitter);  strcat(s,p);  break;
        !          2336:                case 'k':  sprintf(p,"k%0.3f ",ran->kern);  strcat(s,p);  break;
        !          2337:                case 'p':  sprintf(p,"p%g ",ran->size);  strcat(s,p);  break;
        !          2338:                case 'r':  sprintf(p,"r%d,%d ",ran->res_x,ran->res_y);  strcat(s,p);  break;
        !          2339:                case 's':  sprintf(p,"s%0.4f ",ran->speckle);  strcat(s,p);  break;
        !          2340:                case 't':  sprintf(p,"t%0.3f ",ran->thresh);  strcat(s,p);  break;
        !          2341:                case 'x':  sprintf(p,"x%0.3f ",ran->xscale);  strcat(s,p);  break;
        !          2342:                case 'y':  sprintf(p,"y%0.3f ",ran->yscale);  strcat(s,p);  break;
        !          2343:                };
        !          2344:            };
        !          2345:        return(s);
        !          2346:        }
        !          2347: 
        !          2348: RanParms *ato_ranparms(s)
        !          2349:     char *s;
        !          2350: {   static RanParms ran;
        !          2351:     char *f,n,*v;
        !          2352: #define TOKEN_SEP "=, \n"
        !          2353:        f=strtok(s,TOKEN_SEP);
        !          2354:        while(f!=NULL&&strlen(f)>0) {
        !          2355:                n=f[0];  v=f+1;
        !          2356:                switch(n) {
        !          2357:                    case 'r':   /* res */
        !          2358:                        ran.res_x = atoi(v);
        !          2359:                        v=strtok((char *)0,TOKEN_SEP);
        !          2360:                        ran.res_y = atoi(v);
        !          2361:                        break;
        !          2362:                    case 'p':   ran.size = atof(v);  break;
        !          2363:                    case 'a':   ran.skew = atof(v)*DtoR;  break;
        !          2364:                    case 'b':   ran.bhgt = atof(v);  break;
        !          2365:                    case 'e':   ran.blur = atof(v);  break;
        !          2366:                    case 'j':   ran.jitter = atof(v);  break;
        !          2367:                    case 'k':   ran.kern = atof(v);  break;
        !          2368:                    case 's':   ran.speckle = atof(v);  break;
        !          2369:                    case 't':   ran.thresh = atof(v);  break;
        !          2370:                    case 'x':   ran.xscale = atof(v);  break;
        !          2371:                    case 'y':   ran.yscale = atof(v);  break;
        !          2372:                    };
        !          2373:                f=strtok((char *)0,TOKEN_SEP);
        !          2374:                };
        !          2375:        return(&ran);
        !          2376:        }
        !          2377: 
        !          2378: #if FWRI
        !          2379: fwrb_ranparms(f,p)
        !          2380:     FILE *f;
        !          2381:     RanParms *p;
        !          2382: {      fwri_int2(f,p->res_x);
        !          2383:        fwri_int2(f,p->res_y);
        !          2384:        fwri_Pts(f,p->size);
        !          2385:        fwri_Radians(f,p->skew);
        !          2386:        fwri_Ems(f,p->bhgt);
        !          2387:        fwri_int3(f,p->blur*10000.0);
        !          2388:        fwri_int3(f,p->jitter*10000.0);
        !          2389:        fwri_int3(f,p->kern*10000.0);
        !          2390:        fwri_int3(f,p->speckle*10000.0);
        !          2391:        fwri_int3(f,p->thresh*10000.0);
        !          2392:        fwri_int3(f,p->xscale*10000.0);
        !          2393:        fwri_int3(f,p->yscale*10000.0);
        !          2394: #if dbg_fwrb_toa
        !          2395:        err("fwrb_ranparms: %s",ranparms_toa(p));
        !          2396: #endif
        !          2397:        }
        !          2398: #else
        !          2399: fwrb_ranparms(f,p)
        !          2400:     FILE *f;
        !          2401:     RanParms *p;
        !          2402: {      if((fwrite(p,sizeof(RanParms),1,f))!=1) return(0);
        !          2403: #if dbg_fwrb_toa
        !          2404:        err("fwrb_ranparms: %s",ranparms_toa(p));
        !          2405: #endif
        !          2406:        }
        !          2407: #endif
        !          2408: 
        !          2409: #if FRDI
        !          2410: int frdb_ranparms(f,p)
        !          2411:     FILE *f;
        !          2412:     RanParms *p;
        !          2413: {      *p = empty_RanParms;
        !          2414:        if(feof(f))
        !          2415:                return(0);
        !          2416:        p->res_x=frdi_int2(f);
        !          2417:        p->res_y=frdi_int2(f);
        !          2418:        p->size=frdi_Pts(f);
        !          2419:        p->skew=frdi_Radians(f);
        !          2420:        p->bhgt=frdi_Ems(f);
        !          2421:        p->blur=frdi_int3(f)/10000.0;
        !          2422:        p->jitter=frdi_int3(f)/10000.0;
        !          2423:        p->kern=frdi_int3(f)/10000.0;
        !          2424:        p->speckle=frdi_int3(f)/10000.0;
        !          2425:        p->thresh=frdi_int3(f)/10000.0;
        !          2426:        p->xscale=frdi_int3(f)/10000.0;
        !          2427:        p->yscale=frdi_int3(f)/10000.0;
        !          2428: #if dbg_frdb_toa
        !          2429:        err("frdb_ranparms: %s",ranparms_toa(p));
        !          2430: #endif
        !          2431:        if(ferror(f)) return(-errno); else return(1);
        !          2432:        }
        !          2433: #else
        !          2434: int frdb_ranparms(fp,ran)
        !          2435:     FILE *fp;
        !          2436:     RanParms *ran;
        !          2437: {      if((fread(ran,sizeof(RanParms),1,fp))<1) return(0);
        !          2438: #if dbg_frdb_toa
        !          2439:        err("frdb_ranparms: %s",ranparms_toa(ran));
        !          2440: #endif
        !          2441:        }
        !          2442: #endif
        !          2443: 
        !          2444: Char *alloc_char()
        !          2445: {    Char *p;
        !          2446:        if((p=(Char *)malloc(sizeof(Char)))==NULL)
        !          2447:                abort("can't alloc Char");
        !          2448:        alloc_census(Char,1);
        !          2449:        *p = empty_Char;
        !          2450:        return(p);
        !          2451:        }
        !          2452: 
        !          2453: char *char_toa(cp)
        !          2454:     Char *cp;
        !          2455: {   static char s[100],bfs[24];
        !          2456:        if(cp->bfsp==NULL) strcpy(bfs,"0");
        !          2457:        else if(cp->bfsp->bm.n==0) strcpy(bfs,"0");
        !          2458:        else if(cp->bfsp->bm.mny==0) sprintf(bfs,"%d",cp->bfsp->bm.n);
        !          2459:        else sprintf(bfs,"%d,%d",cp->bfsp->bm.n,cp->bfsp->bm.mny);
        !          2460:        sprintf(s,"%s bx%s w%d,h%d cs%d bl%d ar%d pe%d sf%d sh%d bf%s i%d",
        !          2461:                ident_toa(cp->ident),
        !          2462:                bbx_toa(&(cp->bx)),
        !          2463:                bbx_wid(&cp->bx),bbx_hgt(&cp->bx),
        !          2464:                cp->csp,
        !          2465:                cp->bmny,
        !          2466:                cp->area,cp->per,
        !          2467:                (cp->sfv==NULL)? 0: 1,
        !          2468:                cp->sh.mny,
        !          2469:                bfs,
        !          2470:                cp->il.mny
        !          2471:                );
        !          2472:        return(s);
        !          2473:        }
        !          2474: 
        !          2475: /* compute the centroid of the Char (offset from bx.a) */
        !          2476: Pp *char_centroid(cp)
        !          2477:     Char *cp;
        !          2478: {   static Pp c;
        !          2479:     Pp *bc;    /* blob centroid */
        !          2480:     Blob *bp;
        !          2481:     int bi;
        !          2482:        c.x = c.y = 0.0;
        !          2483:        for(bi=0, bp=cp->fi; bi<cp->bmny; bi++, bp=bp->n) {
        !          2484:                bc = blob_centroid(bp);
        !          2485:                c.x += bp->area * (bc->x + (cp->bx.a.x - cp->bx.a.x));
        !          2486:                c.y += bp->area * (bc->y + (cp->bx.a.y - cp->bx.a.y));
        !          2487:                };
        !          2488:        c.x /= cp->area;
        !          2489:        c.y /= cp->area;
        !          2490:        return(&c);
        !          2491:        }
        !          2492: 
        !          2493: #if FWRI
        !          2494: fwrb_char(f,p)
        !          2495:     FILE *f;
        !          2496:     Char *p;
        !          2497: {
        !          2498: #if dbg_fwrb
        !          2499:        if((!(p->ident&IsChar))||(p->ident&(IsALL&(~IsChar))))
        !          2500:                err("fwrb_char: %s",char_toa(p));
        !          2501:        if(p->ident&Char_label && p->l==NULL) {
        !          2502:                err("fwrb_page: Char_label set but .l==NULL");
        !          2503:                p->ident &= ~Char_label;
        !          2504:                };
        !          2505:        if(p->ident&Char_ranparms && p->rp==NULL) {
        !          2506:                err("fwrb_page: Char_ranparms set but .rp==NULL");
        !          2507:                p->ident &= ~Char_ranparms;
        !          2508:                };
        !          2509: #endif
        !          2510:        fwri_Ident(f,p->ident);
        !          2511:        fwri_Bbx(f,&(p->bx));
        !          2512:        fwri_Scoor(f,p->csp);
        !          2513:        fwri_uint4(f,p->area);
        !          2514:        fwri_uint4(f,p->per);
        !          2515:        fwri_Scoor(f,p->basl);
        !          2516:        fwri_uint3(f,p->bmny);
        !          2517:        fwri_uint2(f,p->il.mny);
        !          2518:        fwri_uint1(f,(p->sfv!=NULL)?SF_MNY:0);
        !          2519:        fwri_uint2(f,p->sh.mny);
        !          2520:        fwri_uint2(f,(p->bfsp!=NULL)?p->bfsp->bm.n:0);
        !          2521: #if dbg_fwrb_toa
        !          2522:        err("fwrb_char: %s",char_toa(p));
        !          2523: #endif
        !          2524:        if(p->ident&Char_label) fwrb_label(f,p->l);
        !          2525:        if(p->ident&Char_ranparms) fwrb_ranparms(f,p->rp);
        !          2526:        }
        !          2527: #else
        !          2528: fwrb_char(fp,cp)
        !          2529:     FILE *fp;
        !          2530:     Char *cp;
        !          2531: {   CharF cf;
        !          2532:     int stat;
        !          2533: #if dbg_fwrb
        !          2534:        if((!(cp->ident&IsChar))||(cp->ident&(IsALL&(~IsChar))))
        !          2535:                err("fwrb_char: %s",char_toa(cp));
        !          2536: #endif
        !          2537:        memset(&cf,'\0',sizeof(cf));
        !          2538:        if(cp->ident&Char_ranparms && cp->rp==NULL)
        !          2539:                cp->ident &= ~Char_ranparms;
        !          2540:        cf.ident = cp->ident | IsChar;
        !          2541:        cf.bx = cp->bx;
        !          2542:        cf.csp = cp->csp;
        !          2543:        cf.area = cp->area;
        !          2544:        cf.per = cp->per;
        !          2545:        /* basl omitted */
        !          2546:        cf.bmny = cp->bmny;
        !          2547:        cf.imny = cp->il.mny;
        !          2548:        cf.sfmny = (cp->sfv!=NULL)?SF_MNY:0;
        !          2549:        cf.shmny = cp->sh.mny;
        !          2550:        if(cp->bfsp!=NULL) cf.bfmny = cp->bfsp->bm.n; else cf.bfmny=0;
        !          2551:        if((stat=fwrite(&cf,sizeof(CharF),1,fp))!=1)
        !          2552:                abort("can't write CharF, status %d",stat);
        !          2553: #if dbg_fwrb_toa
        !          2554:        err("fwrb_char: %s",char_toa(cp));
        !          2555: #endif
        !          2556:        if(cf.ident&Char_ranparms) fwrb_ranparms(fp,cp->rp);
        !          2557:        if(cf.ident&Char_label) fwrb_label(fp,cp->l);
        !          2558:        }
        !          2559: #endif
        !          2560: 
        !          2561: fwrb_chars_etc(fp,cs,etc)
        !          2562:     FILE *fp;
        !          2563:     Chars cs;
        !          2564:     Ident etc;
        !          2565: {   register Char *cp,**cpp;
        !          2566:        if(cs.mny>0) for(cp= *(cpp=cs.cpa); cp!=NULL; cp= *(++cpp))
        !          2567:                fwrb_char_etc(fp,cp,etc);
        !          2568:        }
        !          2569: 
        !          2570: fwrb_char_etc(fp,cp,etc)
        !          2571:     FILE *fp;
        !          2572:     Char *cp;
        !          2573:     Ident etc;
        !          2574: {   static Ident parts = (IsInterp|IsBlob|IsShapes|IsBfeats|IsSfeats);
        !          2575:     Char ch;
        !          2576:        if((etc&parts)!=parts) /* write selected parts */ {
        !          2577:                ch = *cp;
        !          2578:                if(!(etc&IsInterp)) ch.il.mny=0;
        !          2579:                if(!(etc&IsBlob)) ch.bmny=0;
        !          2580:                if(!(etc&IsSfeats)) ch.sfv = NULL;
        !          2581:                if(!(etc&IsShapes)) ch.sh = empty_Shapes;
        !          2582:                if(!(etc&IsBfeats)) ch.bfsp = NULL;
        !          2583:                cp = &ch;       /* write modified record */
        !          2584:                };
        !          2585:        fwrb_char(fp,cp);
        !          2586:        if(cp->sfv!=NULL) fwrb_sfeats(fp,cp->sfv);
        !          2587:        if(cp->sh.mny>0) fwrb_shapes(fp,&(cp->sh));
        !          2588: #if CPU!=CRAY
        !          2589:        if(cp->bfsp!=NULL) fwrb_bfeats(fp,cp->bfsp);
        !          2590: #endif
        !          2591:        fwrb_interpl_etc(fp,cp->il,etc);
        !          2592:        if(cp->bmny>0) fwrb_blobl_etc(fp,cp->bmny,cp->fi,etc);
        !          2593:        }
        !          2594: 
        !          2595: #if FRDI
        !          2596: int frdb_char(f,p)
        !          2597:     FILE *f;
        !          2598:     Char *p;
        !          2599: {   int sf_mny,bf_mny;
        !          2600:        *p = empty_Char;
        !          2601:        if(feof(f))
        !          2602:                return(0);
        !          2603:        p->ident=frdi_Ident(f);
        !          2604:        frdi_Bbx(f,&(p->bx));
        !          2605:        p->csp=frdi_Scoor(f);
        !          2606:        p->area=frdi_uint4(f);
        !          2607:        p->per=frdi_uint4(f);
        !          2608:        p->basl=frdi_Scoor(f);
        !          2609:        p->bmny=frdi_uint3(f);
        !          2610:        p->il.mny=frdi_uint2(f);
        !          2611:        if((sf_mny=frdi_uint1(f))>0) {
        !          2612:                if((p->sfv=(Pval *)malloc(sf_mny*sizeof(Pval)))==NULL)
        !          2613:                        abort("frdb_char: can't alloc p->sfv[%d]",sf_mny);
        !          2614:                memset(p->sfv,'\0',sf_mny*sizeof(Pval));
        !          2615:                };
        !          2616:        p->sh.mny=frdi_uint2(f);
        !          2617:        if((bf_mny=frdi_uint2(f))>0) {
        !          2618:                p->bfsp = alloc_bfeats(bf_mny,0);
        !          2619:                };
        !          2620: #if dbg_frdb
        !          2621:        if((!(p->ident&IsChar))||(p->ident&(IsALL&(~IsChar))))
        !          2622:                err("frdb_char: %s",char_toa(p));
        !          2623: #endif
        !          2624: #if dbg_frdb_toa
        !          2625:        err("frdb_char: %s",char_toa(p));
        !          2626: #endif
        !          2627:        if(p->ident&Char_label) p->l = frdb_label(f);
        !          2628:        if(p->ident&Char_ranparms) {
        !          2629:                p->rp = alloc_ranparms();
        !          2630:                frdb_ranparms(f,p->rp);
        !          2631:                };
        !          2632:        if(ferror(f)) return(-errno); else return(1);
        !          2633:        }
        !          2634: #else
        !          2635: int frdb_char(fp,cp)
        !          2636:     FILE *fp;
        !          2637:     Char *cp;
        !          2638: {   CharF cf;
        !          2639:     int stat;
        !          2640:        if((stat=fread(&cf,sizeof(CharF),1,fp))!=1)
        !          2641:                abort("frdb_char: can't read CharF, status %d",stat);
        !          2642:        *cp = empty_Char;
        !          2643:        cp->ident = cf.ident;
        !          2644:        cp->bx = cf.bx;
        !          2645:        cp->csp = cf.csp;
        !          2646:        cp->area = cf.area;
        !          2647:        cp->per = cf.per;
        !          2648:        cp->basl = 0;
        !          2649:        cp->bmny = cf.bmny;
        !          2650:        cp->il.mny = cf.imny;
        !          2651:        if(cf.sfmny>0) {
        !          2652:                if((cp->sfv=(Pval *)malloc(SF_MNY*sizeof(Pval)))==NULL)
        !          2653:                        abort("frdb_char: can't alloc cp->sfv[%d]",SF_MNY);
        !          2654:                memset(cp->sfv,'\0',SF_MNY*sizeof(Pval));
        !          2655:                }
        !          2656:        else cp->sfv = NULL;
        !          2657:        cp->sh.mny = cf.shmny;
        !          2658: #if CPU!=CRAY
        !          2659: #if dbg_frdb
        !          2660:        if((!(cp->ident&IsChar))||(cp->ident&(IsALL&(~IsChar))))
        !          2661:                err("frdb_char: %s",char_toa(cp));
        !          2662: #endif
        !          2663: #endif
        !          2664: #if dbg_frdb_toa
        !          2665:        err("frdb_char: %s",char_toa(cp));
        !          2666: #endif
        !          2667:        if(cp->ident&Char_ranparms) {
        !          2668:                cp->rp = alloc_ranparms();
        !          2669:                frdb_ranparms(fp,cp->rp);
        !          2670:                };
        !          2671:        if(cp->ident&Char_label) cp->l = frdb_label(fp);
        !          2672: #if CPU!=CRAY
        !          2673:        if(cf.bfmny>0) cp->bfsp = alloc_bfeats(cf.bfmny,0);
        !          2674:        else 
        !          2675: #endif
        !          2676:             cp->bfsp = NULL;
        !          2677:        if(ferror(fp)) return(-errno); else return(1);
        !          2678:        }
        !          2679: #endif
        !          2680: 
        !          2681: frdb_char_etc(fp,cp,etc)
        !          2682:     FILE *fp;
        !          2683:     Char *cp;
        !          2684:     Ident etc;
        !          2685: {      frdb_char(fp,cp);
        !          2686:        if(cp->sfv!=NULL) frdb_sfeats(fp,cp->sfv);
        !          2687:        if(cp->sh.mny>0) frdb_shapes(fp,&(cp->sh));
        !          2688: #if CPU!=CRAY
        !          2689:        if(cp->bfsp!=NULL) {
        !          2690:                frdb_bfeats(fp,cp->bfsp);
        !          2691:                };
        !          2692: #endif
        !          2693:        if(cp->il.mny>0) {
        !          2694:                frdb_interpl_etc(fp,&(cp->il),etc);
        !          2695:                /**if(!(etc&IsInterp)) free_interpl_etc(&(cp->il),IsALL);**/
        !          2696:                };
        !          2697:        if(etc&IsBlob && cp->bmny>0)
        !          2698:                frdb_blobl_etc(fp,cp->bmny,&(cp->fi),etc);
        !          2699:        else { cp->bmny = 0; cp->fi = NULL; };
        !          2700:        if(ferror(fp)) return(-errno); else return(1);
        !          2701:        }
        !          2702: 
        !          2703: /* read a set of chars, and parts */
        !          2704: frdb_chars_etc(fp,csp,etc)
        !          2705:     FILE *fp;
        !          2706:     Chars *csp;
        !          2707:     Ident etc;
        !          2708: {   int ci;
        !          2709:     register Char *cp,**cpp;
        !          2710:        if(csp->mny<=0) {
        !          2711:                *csp = empty_Chars;
        !          2712:                return(1);
        !          2713:                };
        !          2714:        if((cpp=csp->cpa=(Char **)malloc((csp->mny+1)*sizeof(Char *)))==NULL)
        !          2715:                abort("frdb_chars_etc:  can't alloc Chars.cpa array");
        !          2716:        for(ci=0; ci<csp->mny; ci++) {
        !          2717:                *(cpp++) = cp = alloc_char();
        !          2718:                frdb_char_etc(fp,cp,etc);
        !          2719:                };
        !          2720:        *cpp = NULL;
        !          2721:        if(ferror(fp)) return(-errno); else return(1);
        !          2722:        }
        !          2723: 
        !          2724: /* return a pointer to a distinct and duplicate copy of *cp;
        !          2725:    it is created out of malloc space.
        !          2726:    RanParms & label are treated as an integral part of the Char. */
        !          2727: Char *dup_char(cp)
        !          2728:     Char *cp;
        !          2729: {   Char *dup;
        !          2730:        if((dup=(Char *)malloc(sizeof(Char)))==NULL)
        !          2731:                abort("can't dup Char");
        !          2732:        alloc_census(Char,1);
        !          2733:        *dup = *cp;
        !          2734:        if(dup->ident&Char_label && dup->l!=NULL)
        !          2735:                dup->l = strdup(cp->l);
        !          2736:        else {dup->ident &= ~Char_label; dup->l = NULL;};
        !          2737:        if(dup->ident&Char_ranparms && dup->rp!=NULL)
        !          2738:                dup->rp = dup_ranparms(cp->rp);
        !          2739:        else {dup->ident &= ~Char_ranparms; dup->rp = NULL;};
        !          2740:        return(dup);
        !          2741:        }
        !          2742: 
        !          2743: /* return a pointer to a distinct and duplicate copy of *cp and those of
        !          2744:    its lists as specified by etc.  (Does not conform to practice for dup_word_etc.)
        !          2745:    */
        !          2746: Char *dup_char_etc(cp,etc)
        !          2747:     Char *cp;
        !          2748:     Ident etc;
        !          2749: {   Char *dup;
        !          2750:        dup = dup_char(cp);
        !          2751:        if(dup->bmny>0&&etc&IsBlob) dup->fi = dup_blobl_etc(cp->fi,etc);
        !          2752:        else {dup->bmny=0; dup->fi=NULL;};
        !          2753:        if(dup->il.mny>0&&etc&IsInterp) dup->il = *dup_interpl_etc(&(cp->il),etc);
        !          2754:        else dup->il = empty_Interpl;
        !          2755:        /**
        !          2756:        if(dup->sfv!=NULL>0&&etc&IsSfeats) dup->sfv = dup_sfeats(cp->sfv);
        !          2757:        else dup->sfv = NULL;
        !          2758:        if(dup->sh.mny>0&&etc&IsShapes) dup->sh = *dup_shapes(&cp->sh);
        !          2759:        else dup->sh = empty_Shapes;
        !          2760:        **/
        !          2761: #if CPU!=CRAY
        !          2762:        if(dup->bfsp!=NULL>0&&etc&IsBfeats) dup->bfsp = dup_bfeats(cp->bfsp);
        !          2763:        else 
        !          2764: #endif
        !          2765:             dup->bfsp = NULL;
        !          2766:        return(dup);
        !          2767:        }
        !          2768: 
        !          2769: /* Return a pointer to a local static duplicate of non-empty *csp.
        !          2770:    Its cpa array is new, created out of malloc space.
        !          2771:    If etc&IsChar, all its Characters are also duplicated,
        !          2772:    else the contents of cpa point to the old unduplicated Chars.
        !          2773:    */
        !          2774: Chars *dup_chars_etc(csp,etc)
        !          2775:     Chars *csp;
        !          2776:     Ident etc;         /* parts to duplicate */
        !          2777: {   static Chars dup;
        !          2778:     register Char **cpp,**dpp;
        !          2779:        if((dup.mny=csp->mny)<=0) dup = empty_Chars;
        !          2780:        else {  if((dup.cpa=(Char **)malloc((dup.mny+1)*sizeof(Char *)))==NULL)
        !          2781:                        abort("can't dup cs.cpa");
        !          2782:                for(cpp=csp->cpa,dpp=dup.cpa; (*cpp)!=NULL; cpp++,dpp++) {
        !          2783:                        if(etc&IsChar) *dpp = dup_char_etc(*cpp,etc);
        !          2784:                        else *dpp = *cpp;
        !          2785:                        };
        !          2786:                *dpp = NULL;
        !          2787:                };
        !          2788:        return(&dup);
        !          2789:        }
        !          2790: 
        !          2791: err_chars(p)
        !          2792:     Chars *p;
        !          2793: {   register Char **cpp;
        !          2794:        fprintf(stderr,"Chars %d: ",p->mny);
        !          2795:        if(p->mny>0) for(cpp=p->cpa; (*cpp)!=NULL; cpp++)
        !          2796:                fprintf(stderr,"%X ",*cpp);
        !          2797:        fprintf(stderr,"\n");
        !          2798:        }
        !          2799: 
        !          2800: /* Make a Char out of a single Blob.  Return a freshly-allocated Char
        !          2801:    that owns the given Blob as is (not duplicated, copied or freed).
        !          2802:    The Blob's next pointer is set to NULL. */
        !          2803: Char *char_of_blob(b)
        !          2804:     Blob *b;
        !          2805: {   Char *c;
        !          2806:        c=alloc_char();
        !          2807:        c->bx = b->bx;
        !          2808:        c->area = b->area;
        !          2809:        c->per = b->per;
        !          2810:        c->bmny = 1;
        !          2811:        c->fi = b;
        !          2812:        b->n = NULL;
        !          2813:        return(c);
        !          2814:        }
        !          2815: 
        !          2816: Interp *alloc_interp()
        !          2817: {   Interp *new;
        !          2818:        if((new=(Interp *)malloc(sizeof(Interp)))==NULL)
        !          2819:                abort("alloc_interp: can't malloc");
        !          2820:        alloc_census(Interp,1);
        !          2821:        *new = empty_Interp;
        !          2822:        return(new);
        !          2823:        }
        !          2824: 
        !          2825: Interp *dup_interp(ip)
        !          2826:     Interp *ip;
        !          2827: {   Interp *dup;
        !          2828:        if((dup=(Interp *)malloc(sizeof(Interp)))==NULL)
        !          2829:                abort("dup_interp: can't malloc");
        !          2830:        alloc_census(Interp,1);
        !          2831:        *dup = *ip;
        !          2832:        return(dup);
        !          2833:        }
        !          2834: 
        !          2835: /* Print the interpl */
        !          2836: fwra_interpl(fp,ilp)
        !          2837:     FILE *fp;
        !          2838:     Interpl *ilp;
        !          2839: {   register Interp *cp;
        !          2840:        fprintf(fp,"Interpl[%d] ",ilp->mny);
        !          2841:        for(cp=ilp->fi; cp!=NULL; cp=cp->n) {
        !          2842:                fprintf(fp," %s --",interp_toa(cp));
        !          2843:                };
        !          2844:        fprintf(fp,"\n");
        !          2845:        }
        !          2846: 
        !          2847: /* Print the interpl very briefly */
        !          2848: fwra_interpl_brief(fp,ilp)
        !          2849:     FILE *fp;
        !          2850:     Interpl *ilp;
        !          2851: {   register Interp *ip;
        !          2852:        for(ip=ilp->fi; ip!=NULL; ip=ip->n) {
        !          2853:                fprintf(fp,"%s %s  ",ip->ci.c,merit_toa(ip->m));
        !          2854:                };
        !          2855:        fprintf(fp,"\n");
        !          2856:        }
        !          2857: 
        !          2858: err_interpl(ilp)
        !          2859:     Interpl *ilp;
        !          2860: {      fwra_interpl(stderr,ilp);
        !          2861:        }
        !          2862: 
        !          2863: /* return the address of a local static copy of the given Interpl,
        !          2864:    for which each Interp is a duplicate of those in the given list */
        !          2865: Interpl *dup_interpl_etc(ilp,etc)
        !          2866:     Interpl *ilp;
        !          2867:     Ident etc;
        !          2868: {   static Interpl lis;
        !          2869:     register Interp *cp,*pp,*dp;
        !          2870:        lis = *ilp;
        !          2871:        if(ilp->fi==NULL) return(NULL);
        !          2872:        for(pp=NULL,cp=ilp->fi; cp!=NULL; pp=dp,cp=cp->n) {
        !          2873:                dp=dup_interp(cp);
        !          2874:                if(pp==NULL) lis.fi = dp;
        !          2875:                else pp->n = dp;
        !          2876:                };
        !          2877:        pp->n=NULL;
        !          2878:        return(&lis);
        !          2879:        }
        !          2880: 
        !          2881: /* Return a pointer to a local static duplicate of *isp.
        !          2882:    Its pa array is new, created out of malloc space.
        !          2883:    If etc&IsInterp, all its Interps are also duplicated,
        !          2884:    else the contents of pa point to the old unduplicated Interps.
        !          2885:    */
        !          2886: Interps *dup_interps_etc(isp,etc)
        !          2887:     Interps *isp;
        !          2888:     Ident etc;         /* parts to duplicate */
        !          2889: {   static Interps dup;
        !          2890:     register Interp **ipp,**dpp;
        !          2891:        dup = *isp;
        !          2892:        if(dup.mny<=0) dup = empty_Interps;
        !          2893:        else {  if((dup.pa=(Interp **)malloc((dup.mny+1)*sizeof(Interp *)))==NULL)
        !          2894:                        abort("can't dup (Interp *)is.pa[%d]",(dup.mny+1));
        !          2895:                for(ipp=isp->pa,dpp=dup.pa; *ipp!=NULL; ipp++,dpp++) {
        !          2896:                        if(etc&IsInterp) *dpp = dup_interp(*ipp);
        !          2897:                        else *dpp = *ipp;
        !          2898:                        };
        !          2899:                *dpp = NULL;
        !          2900:                };
        !          2901:        return(&dup);
        !          2902:        }
        !          2903: 
        !          2904: free_interps_etc(isp,etc)
        !          2905:     Interps *isp;
        !          2906:     Ident etc;         /* parts to free */
        !          2907: {   register Interp **ipp,**dpp;
        !          2908:        if(etc&IsInterp) {
        !          2909:                /* free contents */
        !          2910:                for(ipp=isp->pa; *ipp!=NULL; ipp++) free_interp(*ipp);
        !          2911:                };
        !          2912:        if(isp->pa!=NULL) { free(isp->pa); isp->pa = NULL; };
        !          2913:        isp->mny = 0;
        !          2914:        }
        !          2915: 
        !          2916: /* remove a char from a chars set */
        !          2917: remove_char(cp,csp)
        !          2918:     Char *cp;
        !          2919:     Chars *csp;
        !          2920: {   register Char *rp,**rpp,**npp;
        !          2921:        if(csp->mny==0) err("remove_char: can't - Chars empty");
        !          2922:        else {  for(rp= *(rpp=csp->cpa); rp!=NULL; rp= *(++rpp)) if(rp==cp) break;
        !          2923:                if(rp==NULL) err("remove_char: can't - not found");
        !          2924:                else {  /* move later entries up */
        !          2925:                        npp=rpp+1;
        !          2926:                        do *(rpp++)= *(npp++); while ((*rpp)!=NULL);
        !          2927:                        if((--(csp->mny))==0) {free(csp->cpa); csp->cpa=NULL;};
        !          2928:                        /* don't bother to realloc downwards */
        !          2929:                        };
        !          2930:                };
        !          2931:        }
        !          2932: 
        !          2933: /* Append a char to the end of a chars set. 
        !          2934:    Do NOT attempt to maintain Chars in order sorted ascending on Char.bx.a.x.
        !          2935:    Return appended Char *. */
        !          2936: Char *append_char(cp,csp)
        !          2937:     Char *cp;
        !          2938:     Chars *csp;
        !          2939: {   register Char *rp,**rpp,**npp;
        !          2940:        if(csp->mny==0) {
        !          2941:                if((csp->cpa=(Char **)malloc(2*sizeof(Char *)))==NULL)
        !          2942:                        abort("append_char: can't malloc csp->cpa[%d]",2);
        !          2943:                }
        !          2944:        else {  if((csp->cpa=(Char **)realloc(
        !          2945:                                csp->cpa,
        !          2946:                                (csp->mny+2)*sizeof(Char *))
        !          2947:                                )==NULL)
        !          2948:                        abort("append_char: can't realloc csp->cpa[%d]",
        !          2949:                                (csp->mny+2));
        !          2950:                };
        !          2951:        csp->cpa[csp->mny] = cp;
        !          2952:        csp->cpa[++csp->mny] = NULL;
        !          2953:        return(cp);
        !          2954:        }
        !          2955: 
        !          2956: /* Insert a Char into a Chars set.  Chars is assumed to be sorted ascending
        !          2957:    on on Char.bx.a.x, and this order is maintained.
        !          2958:    Return inserted Char * */
        !          2959: Char *insert_char(cp,csp)
        !          2960:     Char *cp;
        !          2961:     Chars *csp;
        !          2962: {   register Char **cpp,*ncp,*pcp;
        !          2963:        if(csp->mny==0) {
        !          2964:                if((csp->cpa=(Char **)malloc(2*sizeof(Char *)))==NULL)
        !          2965:                        abort("insert_char: can't malloc csp->cpa[%d]",2);
        !          2966:                csp->cpa[csp->mny] = cp;
        !          2967:                }
        !          2968:        else {  if((cpp=csp->cpa=(Char **)realloc(
        !          2969:                                csp->cpa,
        !          2970:                                (csp->mny+2)*sizeof(Char *))
        !          2971:                                )==NULL)
        !          2972:                        abort("insert_char: can't realloc csp->cpa[%d]",csp->mny+2);
        !          2973:                while(((*cpp)!=NULL)&&(*cpp)->bx.a.x<cp->bx.a.x) cpp++;
        !          2974:                /* **cpp is now 1st entry >= *cp in sorted order */
        !          2975:                pcp=cp;
        !          2976:                do {    ncp= *cpp;
        !          2977:                        *(cpp++)=pcp;
        !          2978:                        pcp=ncp;
        !          2979:                        }
        !          2980:                while(pcp!=NULL);
        !          2981:                };
        !          2982:        csp->cpa[++csp->mny] = NULL;
        !          2983:        return(cp);
        !          2984:        }
        !          2985: 
        !          2986: /* Append the contents of a "source" chars set to a "destination" chars set.
        !          2987:    Merely copy (Char *) pointers:  don't duplicate Chars.
        !          2988:    Do NOT attempt to maintain Chars in order sorted ascending on Char.bx.a.x.
        !          2989:    Return destination (Chars *). */
        !          2990: Chars *append_chars(s,d)
        !          2991:     Chars *s;  /* source */
        !          2992:     Chars *d;  /* destination */
        !          2993: {   register Char *rp,**rpp,**npp;
        !          2994:        if(s->mny==0) return(d);
        !          2995:        if(d->mny==0) {
        !          2996:                if((d->cpa=(Char **)malloc((s->mny+1)*sizeof(Char *)))==NULL)
        !          2997:                        abort("append_chars: can't malloc d->cpa[%d]",2);
        !          2998:                }
        !          2999:        else {  if((d->cpa=(Char **)realloc(
        !          3000:                                d->cpa,
        !          3001:                                (d->mny+s->mny+1)*sizeof(Char *))
        !          3002:                                )==NULL)
        !          3003:                        abort("append_chars: can't realloc d->cpa[%d]",
        !          3004:                                (d->mny+s->mny+1));
        !          3005:                };
        !          3006:        memcpy(d->cpa+d->mny,s->cpa,(s->mny+1)*sizeof(Char *));
        !          3007:        d->mny += s->mny;
        !          3008:        return(d);
        !          3009:        }
        !          3010: 
        !          3011: /* Insert a Char into the Chars owned by a given Word,
        !          3012:    maintaining order in set, and updating the Word's bx */
        !          3013: Char *insert_char_word(cp,wp)
        !          3014:     Char *cp;
        !          3015:     Word *wp;
        !          3016: {      merge_bbx(&(cp->bx),&(wp->bx));
        !          3017:        return(insert_char(cp,&(wp->cs)));
        !          3018:        }
        !          3019: 
        !          3020: free_char_etc(p,etc)
        !          3021:     Char *p;   /* !=NULL */
        !          3022:     Ident etc;
        !          3023: {      if(etc&IsBlob) free_blobl_etc(&(p->bmny),&(p->fi),etc);
        !          3024:        if(etc&IsInterp) free_interpl(&(p->il));
        !          3025:        if(etc&IsSfeats && p->sfv!=NULL) { free(p->sfv); p->sfv=NULL; };
        !          3026:        if(etc&IsShapes) free_shapes(&(p->sh));
        !          3027: #if CPU!=CRAY
        !          3028:        if(etc&IsBfeats && p->bfsp!=NULL) { free_bfeats(p->bfsp); p->bfsp=NULL; };
        !          3029: #endif
        !          3030:        if(etc&IsChar) {
        !          3031:                if(p->ident&Char_label && p->l!=NULL) {
        !          3032:                        free(p->l);  p->l=NULL;
        !          3033:                        p->ident &= ~Char_label;
        !          3034:                        };
        !          3035:                if(p->ident&Char_ranparms && p->rp!=NULL) {
        !          3036:                        free_ranparms(p->rp);  p->rp=NULL;
        !          3037:                        p->ident &= ~Char_ranparms;
        !          3038:                        };
        !          3039:                free(p);
        !          3040:                free_census(Char,1);
        !          3041:                };
        !          3042:        }
        !          3043: 
        !          3044: /* Unconditionally free the malloc-space array of pointers, and empty the set.
        !          3045:    Don't free the records that it owned.
        !          3046:    */
        !          3047: free_chars(csp)
        !          3048:     Chars *csp;
        !          3049: {      if(csp->cpa!=NULL) { free(csp->cpa); csp->cpa = NULL; }
        !          3050:        csp->mny = 0;
        !          3051:        }
        !          3052: 
        !          3053: free_chars_etc(csp,etc)
        !          3054:     Chars *csp;
        !          3055:     Ident etc;
        !          3056: {   register Char *cp,**cpp;
        !          3057:        if(csp->mny>0&&(etc&IsChar))
        !          3058:                for(cp= *(cpp=csp->cpa); cp!=NULL; cp= *(++cpp))
        !          3059:                        free_char_etc(cp,etc);
        !          3060:        free_chars(csp);
        !          3061:        }
        !          3062: 
        !          3063: /** Blob handling:
        !          3064:        alloc_blob_pool(sz,debug) - create pool of free blob records
        !          3065:        free_blob_pool() - free the pool
        !          3066: Blob   *alloc_blob() - allocate a new blob (in pool)
        !          3067:        free_blob(bp) -  free a specified blob
        !          3068:        free_blob_runs(bp) - free a blob & the runs in its blob set
        !          3069:        free_blobl_etc(int *,Blob **,etc) - free blobs etc in char's blob list
        !          3070:        out_blob(bp) - print (ascii) to stdout
        !          3071:        fwrb_blob(fp,bp) - write binary Blob (only) to fp
        !          3072:         fwrb_blob_etc(fp,bp,etc) - write binary Blob & specified parts to fp
        !          3073:         frdb_blob_etc(fp,bp,etc) - read binary Blob & parts in specified form
        !          3074:        frdb_runfs(fp,bp,max) - read *bp's (binary) RunF's from fp
        !          3075:        err_blob(s,bp) - print (ascii) to stderr
        !          3076:        err_blob_runs(s,bp) - print Blob & its Runs (ascii) to stderr
        !          3077:        err_blob_runfs(s,bp) - print Blob & its RunFs (ascii) to stderr
        !          3078:        err_blob_briefly(s,bp) - print (ascii) to stderr
        !          3079:        err_blobf(s,bp) - print file-format blob (ascii) to stderr
        !          3080:        err_blob_stats() - report blob statistics
        !          3081:        blob_small(bp) - test whether its runs' data can be compressed to chars
        !          3082:  BUGS
        !          3083:      most fread/fwrite/fseek should be read/write/lseek, for speed
        !          3084:      -- but how to choose?
        !          3085:  **/
        !          3086: 
        !          3087: /* make (empty) pool of `size' Blobs (return F if can't allocate) */
        !          3088: boolean alloc_blob_pool(size,dbg)
        !          3089:     int size;
        !          3090:     boolean dbg;
        !          3091: {   register Blob *cbp, *cbq, *pbp;
        !          3092:        blob_max=size;
        !          3093:        if((blob_pool=(Blob *)malloc(blob_max*sizeof(Blob)))==NULL) return(F);
        !          3094:        blob_debug = dbg;
        !          3095:        pbp= &blob_fr;
        !          3096:        /* link up all blobs into free list using only first-ptrs */
        !          3097:        for(cbq=(cbp=blob_pool)+blob_max; cbp<cbq; cbp++) {
        !          3098:                pbp->n = cbp;
        !          3099:                pbp = cbp;
        !          3100:                };
        !          3101:        pbp->n = NULL;  /* marks end of free list */
        !          3102:        blob_fr_mny = blob_max;  blob_hi=0;  blob_chopped = 0;
        !          3103:        hi_blob_no = 0;
        !          3104:        return(T);
        !          3105:        }
        !          3106: 
        !          3107: free_blob_pool()
        !          3108: {      free(blob_pool);
        !          3109:        }
        !          3110: 
        !          3111: Blob *alloc_blob()
        !          3112: {    Blob *p;
        !          3113:        if((p=(Blob *)malloc(sizeof(Blob)))==NULL)
        !          3114:                abort("alloc_blob: can't");
        !          3115:        alloc_census(Blob,1);
        !          3116:        *p = empty_Blob;
        !          3117:        return(p);
        !          3118:        }
        !          3119: 
        !          3120: /* Unconditionally free this Blob record. */
        !          3121: free_blob(bp)
        !          3122:     Blob *bp;
        !          3123: {      free(bp);
        !          3124:        free_census(Blob,1);
        !          3125:        }
        !          3126: 
        !          3127: /* Allocate new Blob record and assign to it the next blob no */
        !          3128: Blob *alloc_pool_blob()
        !          3129: {      register Blob *bp;
        !          3130:        if((bp = blob_fr.n)==NULL) {
        !          3131:                err("too many alloc_blob() calls - aborting:");
        !          3132: #ifdef STATS
        !          3133:                err_blob_stats();
        !          3134: #endif
        !          3135:                exit(1);
        !          3136:                };
        !          3137:        blob_fr.n = bp->n;
        !          3138: #ifdef STATS
        !          3139:        blob_fr_mny--;
        !          3140:        if(blob_hi<(blob_max-blob_fr_mny)) blob_hi = blob_max-blob_fr_mny;
        !          3141: #endif
        !          3142:        bp->no = hi_blob_no++;
        !          3143:        return(bp);
        !          3144:        }
        !          3145: 
        !          3146: free_pool_blob(bp)
        !          3147:        Blob *bp;
        !          3148: {
        !          3149:        if(blob_debug&&bp==NULL) abort("free_pool_blob: can't free a NULL Blob");
        !          3150: #ifdef STATS
        !          3151:        blob_fr_mny++;
        !          3152:        if(blob_debug&&(blob_fr_mny>blob_max)) {
        !          3153:                err("free_pool_blob: too many free_blob() calls - aborting:");
        !          3154: #ifdef STATS
        !          3155:                err_blob_stats();
        !          3156: #endif
        !          3157:                exit(1);
        !          3158:                };
        !          3159: #endif
        !          3160:        bp->n = blob_fr.n;
        !          3161:        blob_fr.n = bp;
        !          3162:        }
        !          3163: 
        !          3164: /* free the runs belonging to this blob */
        !          3165: free_runs(bp)
        !          3166:     Blob *bp;
        !          3167: {   register Run *crp,*nrp;
        !          3168:        if(bp->ident&Runs_ff) {
        !          3169:                if(bp->r.ff!=NULL) free(bp->r.ff);
        !          3170:                bp->r.ff=NULL;
        !          3171:                }
        !          3172:        else if(bp->ident&Runs_f) {
        !          3173:                for(crp=bp->r.f; crp!=NULL; crp=nrp) {
        !          3174:                        nrp=crp->n;
        !          3175:                        free_run(crp);
        !          3176:                        };
        !          3177:                }
        !          3178:        else if(bp->ident&Runs_seek) /* none to free */ ;
        !          3179:        }
        !          3180: 
        !          3181: /* free blob *bp and those parts (runs) specified */
        !          3182: free_blob_etc(bp,etc)
        !          3183:     Blob *bp;
        !          3184:     Ident etc;
        !          3185: {      if(etc&IsRun) free_runs(bp);
        !          3186:        if(etc&IsBlob) free_blob(bp);
        !          3187:        }
        !          3188: 
        !          3189: /* return a pointer to a distinct and duplicate copy of *bp;
        !          3190:    it is created out of malloc space */
        !          3191: Blob *dup_blob(bp)
        !          3192:     Blob *bp;
        !          3193: {   Blob *dup;
        !          3194:        if((dup=(Blob *)malloc(sizeof(Blob)))==NULL)
        !          3195:                abort("dup_blob: can't malloc");
        !          3196:        alloc_census(Blob,1);
        !          3197:        *dup = *bp;
        !          3198:        return(dup);
        !          3199:        }
        !          3200: 
        !          3201: /* return a pointer to a distinct and duplicate copy of *bp and what it owns,
        !          3202:    as described by etc; created out of malloc space */
        !          3203: Blob *dup_blob_etc(bp,etc)
        !          3204:     Blob *bp;
        !          3205:     Ident etc;
        !          3206: {   Blob *dup;
        !          3207:        dup = dup_blob(bp);
        !          3208:        if(bp->runs>0) {
        !          3209:                if(bp->ident&Runs_ff) {
        !          3210:                    int ri;
        !          3211:                    RunF *rp,*drp;
        !          3212:                        if((dup->r.ff=
        !          3213:                            (RunF *)malloc(bp->runs*sizeof(RunF)))==NULL)
        !          3214:                                abort("dup_blob_etc: can't malloc RunF[%d]",
        !          3215:                                        bp->runs);
        !          3216:                        for(ri=0,rp=bp->r.ff,drp=dup->r.ff; ri<bp->runs; ri++)
        !          3217:                                *(drp++) = *(rp++);
        !          3218:                        }
        !          3219:                else abort("dup_blob_etc: only Runs_ff implemented");
        !          3220:                };
        !          3221:        return(dup);
        !          3222:        }
        !          3223: 
        !          3224: /* Return a pointer to a local static duplicate of non-empty *bsp.
        !          3225:    Its bpa array is created newly out of malloc space.
        !          3226:    If etc&IsBlob, all its Blobs are also fresh duplicates,
        !          3227:    else the contents of bpa point to the old unduplicated Blobs.
        !          3228:    */
        !          3229: Blobs *dup_blobs_etc(bsp,etc)
        !          3230:     Blobs *bsp;
        !          3231:     Ident etc;         /* parts to duplicate */
        !          3232: {   static Blobs dup;
        !          3233:     register Blob **bpp,**dpp;
        !          3234:        if((dup.mny = bsp->mny)<=0) dup = empty_Blobs;
        !          3235:        else {  if((dup.bpa=(Blob **)malloc((dup.mny+1)*sizeof(Blob *)))==NULL)
        !          3236:                        abort("dup_blobs_etc: can't malloc bs.bpa[%d]",dup.mny+1);
        !          3237:                for(bpp=bsp->bpa,dpp=dup.bpa; *bpp!=NULL; bpp++,dpp++) {
        !          3238:                        if(etc&IsBlob) *dpp = dup_blob_etc(*bpp,etc);
        !          3239:                        else *dpp = *bpp;
        !          3240:                        };
        !          3241:                *dpp = NULL;
        !          3242:                };
        !          3243:        return(&dup);
        !          3244:        }
        !          3245: 
        !          3246: /* Split the given Blobs into two, *b0 & *b1, according to the value 0 or !0
        !          3247:    respectively returned by the function s(b,a) applied to each Blob *b and
        !          3248:    blind argument *a.
        !          3249:    s() will be evaluated exactly once for each Blob, and the Blob's will be
        !          3250:    assigned in the order found in the original Blobs.  Only pointers to Blob's
        !          3251:    are created;  the Blob's themselves are not duplicated.  The original Blobs
        !          3252:    record is unchanged.
        !          3253:    */
        !          3254: split_blobs(b,s,a,b0,b1)
        !          3255:     Blobs *b;
        !          3256:     int (*s)();
        !          3257:     VOID *a;           /* passed transparently to s() */
        !          3258:     Blobs *b0,*b1;
        !          3259: {   char *sel,*sp;
        !          3260:     register Blob *bp,**bpp;
        !          3261:     Blob **b0pp;
        !          3262:     Blob **b1pp;
        !          3263:        *b0 = *b1 = empty_Blobs;
        !          3264:        if(b->mny<=0) return;
        !          3265:        if((sel=(char *)malloc(b->mny))==NULL)
        !          3266:                abort("split_blobs: can't malloc sel[%d]",b->mny);
        !          3267:        for(bp= *(bpp=b->bpa),sp=sel; bp!=NULL; bp= *(++bpp),sp++)
        !          3268:                if(*sp = s(bp,a)) b1->mny++; else b0->mny++;
        !          3269:        if(b0->mny>0)
        !          3270:                if((b0->bpa=(Blob **)malloc((b0->mny+1)*sizeof(Blob *)))==NULL)
        !          3271:                        abort("split_blobs: can't malloc b0->bpa[%d]",b0->mny+1);
        !          3272:        if(b1->mny>0)
        !          3273:                if((b1->bpa=(Blob **)malloc((b1->mny+1)*sizeof(Blob *)))==NULL)
        !          3274:                        abort("split_blobs: can't malloc b1->bpa[%d]",b1->mny+1);
        !          3275:        b0pp=b0->bpa;
        !          3276:        b1pp=b1->bpa;
        !          3277:        for(bp= *(bpp=b->bpa),sp=sel; bp!=NULL; bp= *(++bpp),sp++)
        !          3278:                if(*sp) *(b1pp++)=bp; else *(b0pp++)=bp;
        !          3279:        if(b0->mny>0) *b0pp=NULL;
        !          3280:        if(b1->mny>0) *b1pp=NULL;
        !          3281:        free(sel);
        !          3282:        }
        !          3283: 
        !          3284: /* Return the address of the first in a list of Blobs, duplicated from
        !          3285:    the given list. */
        !          3286: Blob *dup_blobl_etc(bp,etc)
        !          3287:     Blob *bp;
        !          3288:     Ident etc;
        !          3289: {   Blob *fi,*cp,*pp,*dp;
        !          3290:        if(bp==NULL) return(NULL);
        !          3291:        for(pp=NULL,cp=bp; cp!=NULL; pp=dp,cp=cp->n) {
        !          3292:                dp=dup_blob_etc(cp,etc);
        !          3293:                if(pp==NULL) fi = dp;
        !          3294:                else pp->n = dp;
        !          3295:                };
        !          3296:        pp->n=NULL;
        !          3297:        return(fi);
        !          3298:        }
        !          3299: 
        !          3300: /* Prepend this blob to the given Char's bloblist; update Char's bx, area, & per.
        !          3301:    Inverse of `remove_blobl()'. */
        !          3302: Blob *insert_blobl(bp,cp)
        !          3303:     Blob *bp;
        !          3304:     Char *cp;
        !          3305: {   register Blob *pbp,*nbp;
        !          3306:        if(cp->bmny>0) bp->n = cp->fi;
        !          3307:        else bp->n = NULL;
        !          3308:        cp->fi = bp;
        !          3309:        cp->bmny++;
        !          3310:        merge_bbx(&(bp->bx),&(cp->bx));
        !          3311:        cp->area += bp->area;
        !          3312:        cp->per += bp->per;
        !          3313:        return(bp);
        !          3314:        }
        !          3315: 
        !          3316: /* Remove this blob from a char's bloblist; update Char's bx, area, per.
        !          3317:    Inverse of `insert_blobl()'. */
        !          3318: remove_blobl(bp,cp)
        !          3319:     Blob *bp;
        !          3320:     Char *cp;
        !          3321: {   Blob *rp,*pp;
        !          3322:     Bbx bx;            /* Char's new Bbx */
        !          3323:        if(cp->bmny==1) {  /* frequent case */
        !          3324:                cp->fi=NULL; cp->bmny=0;
        !          3325:                }
        !          3326:        else if(cp->bmny>1){
        !          3327:                bx=empty_Bbx;
        !          3328:                pp=NULL;
        !          3329:                for(rp=cp->fi; rp!=NULL; pp=rp,rp=rp->n) {
        !          3330:                        if(rp==bp) break;
        !          3331:                        else {  if(bx.a.x > rp->bx.a.x) bx.a.x = rp->bx.a.x;
        !          3332:                                if(bx.a.y > rp->bx.a.y) bx.a.y = rp->bx.a.y;
        !          3333:                                if(bx.b.x < rp->bx.b.x) bx.b.x = rp->bx.b.x;
        !          3334:                                if(bx.b.y < rp->bx.b.y) bx.b.y = rp->bx.b.y;
        !          3335:                                };
        !          3336:                        };
        !          3337:                if(rp!=NULL) {  /* remove from list */
        !          3338:                        if(pp==NULL) cp->fi=bp->n;
        !          3339:                        else pp->n=bp->n;
        !          3340:                        cp->bmny--;
        !          3341:                        for(rp=bp->n; rp!=NULL; rp=rp->n) {
        !          3342:                                if(bx.a.x > rp->bx.a.x) bx.a.x = rp->bx.a.x;
        !          3343:                                if(bx.a.y > rp->bx.a.y) bx.a.y = rp->bx.a.y;
        !          3344:                                if(bx.b.x < rp->bx.b.x) bx.b.x = rp->bx.b.x;
        !          3345:                                if(bx.b.y < rp->bx.b.y) bx.b.y = rp->bx.b.y;
        !          3346:                                };
        !          3347:                        cp->bx = bx;
        !          3348:                        }
        !          3349:                else err("remove_blobl: can't - not found");
        !          3350:                }
        !          3351:        else err("remove_blobl: can't - Blobl empty");
        !          3352:        cp->area -= bp->area;
        !          3353:        cp->per -= bp->per;
        !          3354:        }
        !          3355: 
        !          3356: /* prepend this blob to the given Blob list */
        !          3357: Blob *prepend_blobl(bp,blp)
        !          3358:     Blob *bp;
        !          3359:     Blobl *blp;
        !          3360: {   register Blob *pbp,*nbp;
        !          3361:        if(blp->mny>0) bp->n = blp->fi;
        !          3362:        else {  bp->n = NULL;
        !          3363:                blp->la = bp;
        !          3364:                };
        !          3365:        blp->fi = bp;
        !          3366:        blp->mny++;
        !          3367:        return(bp);
        !          3368:        }
        !          3369: 
        !          3370: /* append this blob to the given Blob list */
        !          3371: Blob *append_blobl(bp,blp)
        !          3372:     Blob *bp;
        !          3373:     Blobl *blp;
        !          3374: {   register Blob *pbp,*nbp;
        !          3375:        if(blp->mny==0) blp->fi = bp;
        !          3376:        else blp->la->n = bp;
        !          3377:        blp->la = bp;
        !          3378:        bp->n = NULL;
        !          3379:        blp->mny++;
        !          3380:        return(bp);
        !          3381:        }
        !          3382: 
        !          3383: /* remove a blob from a blobs set */
        !          3384: remove_blob(bp,bsp)
        !          3385:     Blob *bp;
        !          3386:     Blobs *bsp;
        !          3387: {   register Blob *rp,**rpp,**npp;
        !          3388:        if(bsp->mny==0) err("remove_blob: can't - Blobs empty");
        !          3389:        else {  for(rp= *(rpp=bsp->bpa); rp!=NULL; rp= *(++rpp)) if(rp==bp) break;
        !          3390:                if(rp==NULL) err("remove_blob: can't - not found");
        !          3391:                else {  /* move later entries up */
        !          3392:                        npp=rpp+1;
        !          3393:                        do *(rpp++)= *(npp++); while ((*rpp)!=NULL);
        !          3394:                        if((--(bsp->mny))==0) {free(bsp->bpa); bsp->bpa=NULL;};
        !          3395:                        /* don't bother to realloc downwards */
        !          3396:                        };
        !          3397:                };
        !          3398:        }
        !          3399: 
        !          3400: /* Append a blob to the end of a blobs set. 
        !          3401:    Return appended Blob *. */
        !          3402: Blob *append_blob(p,sp)
        !          3403:     Blob *p;
        !          3404:     Blobs *sp;
        !          3405: {   register Blob *rp,**rpp,**npp;
        !          3406:        if(sp->mny==0) {
        !          3407:                if((sp->bpa=(Blob **)malloc(2*sizeof(Blob *)))==NULL)
        !          3408:                        abort("append_blob: can't malloc sp->bpa[2]");
        !          3409:                }
        !          3410:        else {  if((sp->bpa=(Blob **)realloc(
        !          3411:                                sp->bpa,
        !          3412:                                (sp->mny+2)*sizeof(Blob *))
        !          3413:                                )==NULL)
        !          3414:                        abort("append_blob: can't realloc sp->bpa[%d]",sp->mny+2);
        !          3415:                };
        !          3416:        sp->bpa[sp->mny] = p;
        !          3417:        sp->bpa[++sp->mny] = NULL;
        !          3418:        return(p);
        !          3419:        }
        !          3420: 
        !          3421: /* Append Blobs *p1 to Blobs *p2.  On return, *p1 is unchanged, *p2 is in general
        !          3422:    longer, and all the Blob's owned by *p1 are now also owned by *p2. */
        !          3423: append_blobs_blobs(p1,p2)
        !          3424:     Blobs *p1,*p2;
        !          3425: {   int mny;
        !          3426:     register Blob **pp1,**pp2;
        !          3427:        if(p1->mny==0) return;
        !          3428:        if(p2->mny==0) { *p2 = *dup_blobs_etc(p1,IsNONE); return; };
        !          3429:        mny = p2->mny + p1->mny;
        !          3430:        if((p2->bpa=(Blob **)realloc(p2->bpa,(mny+1)*sizeof(Blob *)))==NULL)
        !          3431:                abort("append_blobs_blobs: can't realloc p2->bpa[%d]",mny+1);
        !          3432:        pp1=p1->bpa;  pp2=p2->bpa+p2->mny;  while(*pp1!=NULL) *(pp2++) = *(pp1++);
        !          3433:        *pp2 = NULL;
        !          3434:        p2->mny = mny;
        !          3435:        }
        !          3436: 
        !          3437: free_blobl_etc(mnyp,fip,etc)
        !          3438:     int *mnyp;
        !          3439:     Blob **fip;
        !          3440:     Ident etc;
        !          3441: {   register Blob *bp,*nbp;
        !          3442:        if(*mnyp>0) {
        !          3443:                for(bp= *fip; bp != NULL; bp=nbp)
        !          3444:                        { nbp=bp->n; free_blob_etc(bp,etc); };
        !          3445:                };
        !          3446:        *mnyp=0;
        !          3447:        *fip=NULL;
        !          3448:        }
        !          3449: 
        !          3450: /* Unconditionally free the malloc-space array of pointers, and empty the set.
        !          3451:    Don't free the records that it owned.
        !          3452:    */
        !          3453: free_blobs(bsp)
        !          3454:     Blobs *bsp;
        !          3455: {      if(bsp->bpa!=NULL) { free(bsp->bpa); bsp->bpa=NULL; };
        !          3456:        bsp->mny = 0;
        !          3457:        }
        !          3458: 
        !          3459: free_blobs_etc(bsp,etc)
        !          3460:    Blobs *bsp;
        !          3461:    Ident etc;
        !          3462: {  register Blob *bp, **bpp;
        !          3463:        if(bsp->mny>0&&(etc&IsBlob)) for(bp= *(bpp=bsp->bpa); bp!=NULL; bp= *(++bpp))
        !          3464:                free_blob_etc(bp,etc);
        !          3465:        free_blobs(bsp);
        !          3466:        }
        !          3467: 
        !          3468: Blobs *blobl_to_blobs(blp)
        !          3469:     Blobl *blp;
        !          3470: {   static Blobs bs;
        !          3471:     register Blob *bp,**bpp;
        !          3472:        bs = empty_Blobs;
        !          3473:        bs.mny = blp->mny;
        !          3474:        if(bs.mny>0) {
        !          3475:                if((bs.bpa=(Blob **)malloc((bs.mny+1)*sizeof(Blob *)))==NULL)
        !          3476:                        abort("blobl_to_blobs: can't malloc Blobs.bpa[%d]",bs.mny+1);
        !          3477:                for(bp=blp->fi,bpp=bs.bpa; bp!=NULL; bp=bp->n,bpp++)
        !          3478:                        *bpp = bp;
        !          3479:                *bpp = NULL;
        !          3480:                };
        !          3481:        return(&bs);
        !          3482:        }
        !          3483: 
        !          3484: int bp_tod(bp)
        !          3485:     Blob *bp;
        !          3486: {      if(bp==NULL) return(-2);
        !          3487:        else return(bp-blob_pool);
        !          3488:        }
        !          3489: 
        !          3490: /* test whether for this Blob, each runs's data fields could all be compressed
        !          3491:    from a short to a char */
        !          3492: boolean blob_small(bp)
        !          3493:     Blob *bp;
        !          3494: {      if(bp->runs>255) return(F);
        !          3495:        if(bbx_wid(&bp->bx)>255) return(F);
        !          3496:        if(bbx_hgt(&bp->bx)>255) return(F);
        !          3497:        return(T);
        !          3498:        }
        !          3499: 
        !          3500: char *blob_toa(bp)
        !          3501:     Blob *bp;
        !          3502: {   char s1[80];
        !          3503:     static char s[80];
        !          3504:     Scoor hgt,wid;
        !          3505:        if(bp==NULL) strcpy(s,"NULL");
        !          3506:        else {  hgt = bp->bx.b.y - bp->bx.a.y + 1;
        !          3507:                wid = bp->bx.b.x - bp->bx.a.x + 1;
        !          3508:                sprintf(s1,"%s bx%s w%d,h%d ar%d pe%d r.",
        !          3509:                        ident_toa(bp->ident),
        !          3510:                        bbx_toa(&(bp->bx)),
        !          3511:                        wid,
        !          3512:                        hgt,
        !          3513:                        bp->area,
        !          3514:                        bp->per
        !          3515:                        );
        !          3516:                if(bp->ident&Runs_f) strcat(s1,"f");
        !          3517:                else if(bp->ident&Runs_ff) strcat(s1,"ff");
        !          3518:                else if(bp->ident&Runs_seek) strcat(s1,"sk");
        !          3519:                else if(bp->ident&Runs_g4) strcat(s1,"g");
        !          3520:                else strcat(s1,"?");
        !          3521:                sprintf(s,"%s%d",s1,bp->runs);
        !          3522:                };
        !          3523:        return(s);
        !          3524:        }
        !          3525: 
        !          3526: /* compute the centroid of the Blob, w.r.t bx.a */
        !          3527: Pp *blob_centroid(bp)
        !          3528:     Blob *bp;
        !          3529: {   static Pp c;
        !          3530:     RunF *rfp;
        !          3531:     int ri,area;
        !          3532:        c.x = c.y = 0.0;
        !          3533:        if(bp->ident&Runs_ff) {
        !          3534:                for(ri=0, rfp=bp->r.ff; ri<bp->runs; ri++, rfp++) {
        !          3535:                        c.x += (area=(rfp->xe-rfp->xs+1)) * (rfp->xe+rfp->xs);
        !          3536:                        c.y += area * rfp->y;
        !          3537:                        };
        !          3538:                c.x /= 2.0*bp->area;
        !          3539:                c.y /= bp->area;
        !          3540:                }
        !          3541:        else abort("blob_centroid: only Runs_ff supported");
        !          3542:        return(&c);
        !          3543:        }
        !          3544: 
        !          3545: /* write (printably) to stdout */
        !          3546: out_blob(bp)
        !          3547:     Blob *bp;
        !          3548: {   Run *crp;
        !          3549:     static char pad[] = "     ";
        !          3550:     short y;
        !          3551:        if(bp==NULL) fprintf(stderr,"Bb NULL.\n");
        !          3552:        else fprintf(stdout,"%s\n",blob_toa(bp));
        !          3553:        fprintf(stdout,"%sy%d:",pad,bp->bx.a.y);
        !          3554:        for(crp=bp->r.f,y=crp->y; crp!=NULL; crp=crp->n) {
        !          3555:                if(crp->y!=y) {
        !          3556:                        fprintf(stdout,"\n%sy%d:",pad,crp->y);
        !          3557:                        y=crp->y;
        !          3558:                        };
        !          3559:                fprintf(stdout," [%d,%d]",crp->xs,crp->xe);
        !          3560:                };
        !          3561:        fprintf(stdout,"\n");
        !          3562:        }
        !          3563: 
        !          3564: #if FWRI
        !          3565: /* Write blob record (only) to FILE *f.
        !          3566:    Check whether Blob is ``small'' and label *p accordingly. */
        !          3567: fwrb_blob(f,p)
        !          3568:     FILE *f;
        !          3569:     Blob *p;
        !          3570: {
        !          3571: #if dbg_fwrb
        !          3572:        if((!(p->ident&IsBlob))||(p->ident&(IsALL&(~IsBlob))))
        !          3573:                err("fwrb_blob: %s",blob_toa(p));
        !          3574: #endif
        !          3575:        if(blob_small(p)) p->ident |= Blob_small; else p->ident &= ~Blob_small;
        !          3576:        fwri_Ident(f,p->ident);
        !          3577:        /* fwri_Seq(f,p->no); */
        !          3578:        fwri_Bbx(f,&(p->bx));
        !          3579:        fwri_uint4(f,p->area);
        !          3580:        fwri_uint4(f,p->per);
        !          3581:        /* don't write .n */
        !          3582:        fwri_Merit(f,p->m);
        !          3583:        fwri_uint3(f,p->runs);
        !          3584:        fwri_uint2(f,((p->bdsp==NULL)? 0: p->bdsp->mny));
        !          3585: #if dbg_fwrb_toa
        !          3586:        err("fwrb_blob: %s",blob_toa(p));
        !          3587: #endif
        !          3588:        }
        !          3589: #else
        !          3590: /* Write blob record (only) to FILE *fp.
        !          3591:    Check whether Blob is ``small'' and label *bp accordingly. */
        !          3592: fwrb_blob(fp,bp)
        !          3593:     FILE *fp;
        !          3594:     Blob *bp;
        !          3595: {   BlobF bf;
        !          3596: #if dbg_fwrb
        !          3597:        if((!(bp->ident&IsBlob))||(bp->ident&(IsALL&(~IsBlob))))
        !          3598:                err("fwrb_blob: %s",blob_toa(bp));
        !          3599: #endif
        !          3600:        memset(&bf,'\0',sizeof(bf));
        !          3601:        if(blob_small(bp)) bp->ident |= Blob_small;
        !          3602:        else bp->ident &= ~Blob_small;
        !          3603:        bf.ident = bp->ident;
        !          3604:        bf.bx=bp->bx;
        !          3605:        bf.area=bp->area;
        !          3606:        bf.per=bp->per;
        !          3607:        bf.runs=bp->runs;
        !          3608:        if(bp->bdsp==NULL) bf.bdys=0;
        !          3609:        else bf.bdys=bp->bdsp->mny;
        !          3610:        if(blob_debug) err_blobf("",&bf);
        !          3611:        if(fwrite(&bf,sizeof(BlobF),1,fp)!=1)
        !          3612:                abort("fwrb_blob: can't fwrite");
        !          3613: #if dbg_fwrb_toa
        !          3614:        err("fwrb_blob: %s",blob_toa(bp));
        !          3615: #endif
        !          3616:        }
        !          3617: #endif
        !          3618: 
        !          3619: /* Write detailed description of Runs to stderr. */
        !          3620: Blob *err_runs(name,bp)
        !          3621:     char *name;
        !          3622:     Blob *bp;
        !          3623: {   Run *rp;
        !          3624:     RunF *rfp,*rfe;
        !          3625:     int ri;
        !          3626:        if(bp->ident&Runs_f) {
        !          3627:                fprintf(stderr,"%s Runs_f[%d] in %s:\n",
        !          3628:                        name,bp->runs,bbx_toa(&bp->bx));
        !          3629:                for(rp=bp->r.f,ri=0; rp!=NULL; rp=rp->n,ri++) { 
        !          3630:                        fprintf(stderr,"  %2d: %d[%d,%d]\n",
        !          3631:                                ri,rp->y,rp->xs,rp->xe);
        !          3632:                        };
        !          3633:                }
        !          3634:        else if(bp->ident&Runs_ff) {
        !          3635:                fprintf(stderr,"%s Runs_ff[%d] in %s: ",
        !          3636:                        name,bp->runs,bbx_toa(&(bp->bx)));
        !          3637:                for(rfe=(rfp=bp->r.ff)+bp->runs,ri=0; rfp<rfe; rfp++,ri++) {    
        !          3638:                        fprintf(stderr," %d:%d[%d,%d]",
        !          3639:                                ri,rfp->y,rfp->xs,rfp->xe);
        !          3640:                        };
        !          3641:                fprintf(stderr,"\n");
        !          3642:                }
        !          3643:        else if(bp->ident&Runs_seek) {
        !          3644:                goto unsupported;
        !          3645:                }
        !          3646:        else {  goto unsupported;
        !          3647:                };
        !          3648:        return;
        !          3649: unsupported:
        !          3650:        abort("err_runs: %s unsupported",ident_toa(bp->ident));
        !          3651:        }
        !          3652: 
        !          3653: /* Convert the type of Runs in *bp to the type specified by id.
        !          3654:    Supported:  only bp->ident&Runs_f to id&Runs_ff.
        !          3655:    Return pointer to local static duplicate of *bp.
        !          3656:    */
        !          3657: Blob *runs_to_runs(bp,id)
        !          3658:     Blob *bp;
        !          3659:     Ident id;
        !          3660: {   static Blob b;
        !          3661:     register Run *rp;
        !          3662:     RunF *rfp;
        !          3663: 
        !          3664:        b = *bp;
        !          3665:        if(bp->ident&Runs_f) {
        !          3666:                if(id&Runs_ff) {
        !          3667:                        b.ident &= ~(Runs_f|Blob_small);
        !          3668:                        b.ident |= Runs_ff;
        !          3669:                        if(blob_small(bp)) b.ident |= Blob_small;
        !          3670:                        if((b.r.ff=(RunF *)malloc(b.runs*sizeof(RunF)))==NULL)
        !          3671:                                abort("runs_to_runs: can't malloc Blob.r.ff[%d]",
        !          3672:                                                b.runs);                                                        for(rp=bp->r.f,rfp=b.r.ff; rp!=NULL; rp=rp->n,rfp++) {  
        !          3673:                                /* y, xs, xe will be relative to Blob.bx.a */
        !          3674:                                rfp->y = rp->y - bp->bx.a.y;
        !          3675:                                rfp->xs = rp->xs - bp->bx.a.x;
        !          3676:                                rfp->xe = rp->xe - bp->bx.a.x;
        !          3677:                                /* above,below connecting indices are relative to
        !          3678:                                   the sequence no. of this run (ac>0 & bc>0) */
        !          3679:                                if((rfp->ad = rp->ad)!=0)
        !          3680:                                        rfp->ac = rp->u.no - rp->ac->u.no;
        !          3681:                                else rfp->ac = 0;
        !          3682:                                if((rfp->bd = rp->bd)!=0)
        !          3683:                                        rfp->bc = rp->bc->u.no - rp->u.no;
        !          3684:                                else rfp->bc = 0;
        !          3685:                                if(0) err_runf("   ",rfp);
        !          3686:                                };
        !          3687:                        }
        !          3688:                else {  goto unsupported;
        !          3689:                        };
        !          3690:                }
        !          3691:        else if(bp->ident&Runs_ff) {
        !          3692:                if(bp->ident&Blob_small) {
        !          3693:                        goto unsupported;
        !          3694:                        }
        !          3695:                else {  goto unsupported;
        !          3696:                        };
        !          3697:                }
        !          3698:        else if(bp->ident&Runs_seek) {
        !          3699:                goto unsupported;
        !          3700:                }
        !          3701:        else {  goto unsupported;
        !          3702:                };
        !          3703:        return(&b);
        !          3704: 
        !          3705: unsupported:
        !          3706:        abort("runs_to_runs: %s --> %s unsupported",
        !          3707:                ident_toa(bp->ident),ident_toa(id));
        !          3708:        }
        !          3709: 
        !          3710: #if CPU!=CRAY
        !          3711: /* Write these RunFs to FILE *fp in CCITT Group 4 format.
        !          3712:    BUG:  uses bitio to one end of a pipe, just to store the entire
        !          3713:    byte-stream so it can be counted; then, it is copied to *fp.
        !          3714:    This fails on large bitmaps since the space available for pipe
        !          3715:    buffering is limited by system constraints and can run out at any
        !          3716:    time (symptom: the user process hangs in I state).  Also, since
        !          3717:    it relies so heavily on streams, it may not prove to be very portable.
        !          3718:    Should be rewritten with an expandable byte-buffer of some kind,
        !          3719:    which requires that the `putting' function in bitio be user-selectable.
        !          3720:    */
        !          3721: fwrb_runfs_g4(fp,bxp,runs,ff)
        !          3722:     FILE *fp;
        !          3723:     Bbx *bxp;
        !          3724:     int runs;
        !          3725:     RunF *ff;
        !          3726: #define dbg_fwrb_runfs_g4_detail F
        !          3727: {   RLE_Line l0,l1;
        !          3728:     RLE_Line *cl,*pl,*swap;    /* current/prior/swap line */
        !          3729:     int wid,hgt;               /* width,height of rectangular box of pixels */
        !          3730:     BITFILE *bf;
        !          3731:     int iri;
        !          3732:     RunF *irp;
        !          3733:     RLE_Run *crp;
        !          3734:     static int pipe_fd[2] = {-1,-1};
        !          3735:     static FILE *pipe_fp[2] = {NULL,NULL};
        !          3736:     unsigned long bytes,bi;
        !          3737:     int och;
        !          3738:        /* buffer g4 code in a pipe in order to count it */
        !          3739:        if(pipe_fd[0]==-1) {
        !          3740:                pipe(pipe_fd);
        !          3741:                if((pipe_fp[0] = fdopen(pipe_fd[0],"r"))==NULL)
        !          3742:                        abort("fwrb_runfs_g4: can't open pipe \"r\", fd=%d",
        !          3743:                                pipe_fd[0]);
        !          3744:                if((pipe_fp[1] = fdopen(pipe_fd[1],"w"))==NULL)
        !          3745:                        abort("fwrb_runfs_g4: can't open pipe \"w\", fd=%d",
        !          3746:                                pipe_fd[1]);
        !          3747:                /* leave this pipe open for the duration of the process */
        !          3748:                };
        !          3749:        /* treat pipe_fp[1] as a sequence of bits */
        !          3750:        if((bf=bopen(pipe_fp[1],"w"))==NULL)
        !          3751:                abort("fwrb_runfs_g4: can't open bitfile");
        !          3752:        wid = bbx_wid(bxp);  hgt=bbx_hgt(bxp);
        !          3753:        BOF_to_g4(bf);
        !          3754:        cl= &l0;  pl= &l1;  cl->len = pl->len = wid;  cl->y=0;  pl->runs=0;
        !          3755:        iri=0;  irp=ff;
        !          3756:        do {    /* build current RLE_Line */
        !          3757:                crp=cl->r;
        !          3758:                while(iri<runs&&irp->y==cl->y) {
        !          3759:                        crp->xs=irp->xs;
        !          3760:                        crp->xe=irp->xe;
        !          3761:                        if(dbg_fwrb_runfs_g4_detail)
        !          3762:                                err("i%d:  y%d x[%d,%d]",
        !          3763:                                        iri,cl->y,crp->xs,crp->xe);
        !          3764:                        iri++; irp++; crp++;
        !          3765:                        };
        !          3766:                cl->runs = crp-cl->r;
        !          3767:                /* write current RLE_line to file */
        !          3768:                rlel_to_g4(pl,cl,wid,bf);
        !          3769:                /* save current line as prior */
        !          3770:                swap=pl;  pl=cl;  cl=swap;  cl->runs=0;  cl->y=pl->y+1;
        !          3771:                }
        !          3772:        while(cl->y<hgt);
        !          3773:        /** By policy, don't append EOFB (i.e. omit EOF_to_g4(bf)) **/
        !          3774:        bytes = bclose(bf);
        !          3775:        if(dbg_fwrb_runs) err("fwrb_runfs_g4: %d bytes",bytes);
        !          3776: #if FWRI
        !          3777:        fwri_uint4(fp,bytes);
        !          3778: #else
        !          3779:        fwrite(&bytes,sizeof(bytes),1,fp);
        !          3780: #endif
        !          3781:        /* copy contents in pipe to fp */
        !          3782:        fflush(pipe_fp[1]);
        !          3783:        for(bi=0;bi<bytes;bi++) {
        !          3784:                putc(och=getc(pipe_fp[0]),fp);
        !          3785: #if dbg_fwrb_runs
        !          3786:                fprintf(stderr,"%02x",(unsigned char)och);
        !          3787: #endif
        !          3788:                };
        !          3789:        if(dbg_fwrb_runs) fprintf(stderr,"\n");
        !          3790:        }
        !          3791: #else
        !          3792: fwrb_runfs_g4(fp,bxp,runs,ff)
        !          3793:     FILE *fp;
        !          3794:     Bbx *bxp;
        !          3795:     int runs;
        !          3796:     RunF *ff;
        !          3797: {      abort("fwrb_runfs_g4: unimplemented on Cray");
        !          3798:        }
        !          3799: #endif
        !          3800: 
        !          3801: #if CPU != CRAY
        !          3802: /* Read a connected group of Runs in CCITT Group 4 format, into an array of RunFs.
        !          3803:    Return:  1 if normal & successful, 0 if EOF, and -1 if error.
        !          3804:    */
        !          3805: int frdb_g4_runfs(fp,bxp,runs,ff)
        !          3806:     FILE *fp;
        !          3807:     Bbx *bxp;  /* their bounding box, shrink-wrapped to fit exactly */
        !          3808:     int runs;  /* expect exactly this number of runs */
        !          3809:     RunF *ff;  /* space for `runs' RunFs */
        !          3810: #define dbg_frdb_g4_runfs_detail F
        !          3811: {   RLE_Line *cl;      /* current line */
        !          3812:     int wid,hgt;       /* width,height of rectangular box of pixels */
        !          3813:     int y;             /* height of current line */
        !          3814:     boolean bof;
        !          3815:     BITFILE *bf;
        !          3816:     static DST_table *tbl = NULL;
        !          3817:     int iri;
        !          3818:     RLE_Run *irp;
        !          3819:     int ori;
        !          3820:     RunF *orp;
        !          3821:     unsigned long bytes,g4_bytes;
        !          3822: #if FRDI
        !          3823:        bytes=frdi_uint4(fp);
        !          3824: #else
        !          3825:        fread(&bytes,sizeof(bytes),1,fp);
        !          3826: #endif
        !          3827:        if(dbg_frdb_runs) err("frdb_g4_runfs: %d bytes",bytes);
        !          3828:        /* read FILE *fp as a sequence of bits */
        !          3829:        if((bf=bopen(fp,"r"))==NULL)
        !          3830:                abort("frdb_g4_runfs: can't open bitfile");
        !          3831:        if(tbl==NULL) {
        !          3832:                tbl=ccitt_table();
        !          3833:                if(dbg_frdb_g4_runfs_detail) ccitt_err_tbl(tbl);
        !          3834:                };
        !          3835:        wid=bbx_wid(bxp);  hgt=bbx_hgt(bxp);
        !          3836:        y=0;  bof=T;  ori=0;  orp=ff;
        !          3837:        while((y<hgt)&&(cl=g4_to_rlel(tbl,bf,bof,wid))!=NULL) {
        !          3838:                /* copy runs to RunF ff[] */
        !          3839:                for(iri=0,irp=cl->r; iri<cl->runs; iri++,irp++) {
        !          3840:                        if(dbg_frdb_g4_runfs_detail)
        !          3841:                                err("o%d i%d:  y%d x[%d,%d]",
        !          3842:                                        ori,iri,y,irp->xs,irp->xe);
        !          3843:                        if(ori<runs) {
        !          3844:                                orp->y = y;  orp->xs = irp->xs;  orp->xe = irp->xe;
        !          3845:                                orp++;
        !          3846:                                }
        !          3847:                        else err("frdb_g4_runfs: too many runs: ori%d >= runs%d",
        !          3848:                                ori,runs);
        !          3849:                        ori++;
        !          3850:                        };
        !          3851:                y++;  bof=F;
        !          3852:                };
        !          3853:        if(cl==NULL) {
        !          3854:                err("frdb_g4_runfs: unexpected EOF");
        !          3855:                return(0);
        !          3856:                };
        !          3857:        /* recover connectivity among runs, on the assumption that they
        !          3858:           are still in increasing lexicographic order on (y,xs) */
        !          3859:        fix_lag(orp-ff,(char *)ff,Runs_ff);
        !          3860:        if(ori!=runs) {
        !          3861:                err("frdb_g4_runfs: expected %d runs, but read %d",runs,ori);
        !          3862:                return(-1);
        !          3863:                };
        !          3864:        if((g4_bytes=bclose(bf))!=bytes) {
        !          3865:                err("frdb_g4_runfs: expected %d bytes, but read %d",
        !          3866:                        bytes,g4_bytes);
        !          3867:                return(-1);
        !          3868:                };
        !          3869:        if(ferror(fp)) return(-errno); else return(1);
        !          3870:        }
        !          3871: #else
        !          3872: int frdb_g4_runfs(fp,bxp,runs,ff)
        !          3873:     FILE *fp;
        !          3874:     Bbx *bxp;  /* exact bounding box, shrink-wrapped to fit */
        !          3875:     int runs;  /* expect exactly this number of runs */
        !          3876:     RunF *ff;  /* space for `runs' RunFs */
        !          3877: {      abort("frdb_g4_runfs:  unimplemented on Cray");
        !          3878:        }
        !          3879: #endif
        !          3880: 
        !          3881: /* Write blob, etc to FILE *fp in binary format.
        !          3882:    If !(etc&IsBlob), then do nothing.
        !          3883:    else write Blob record and:
        !          3884:      If etc&IsRun, then also try to write Runs:
        !          3885:        If Blob.runs==0, write no runs.
        !          3886:        else
        !          3887:           if etc&Runs_g4, write in CCITT G4 format
        !          3888:                compact but needs large streams; may not port well;
        !          3889:           else write in Runs_ff format
        !          3890:                if blob is small enough, use compressed RunFS form on output.
        !          3891: 
        !          3892:     */
        !          3893: fwrb_blob_etc(fp,bp,etc)
        !          3894:     FILE *fp;
        !          3895:     Blob *bp;
        !          3896:     Ident etc;
        !          3897: {   Ident sv_ident;
        !          3898:     int sv_runs;
        !          3899:     Run *rp;
        !          3900:     RunF rf;
        !          3901:     register RunF *rfp, *rfq;
        !          3902:     RunFS *rsp;
        !          3903: #if FWRI
        !          3904:     RunFS rfs;
        !          3905: #else
        !          3906:     static RunFS rfsa[256];
        !          3907: #endif
        !          3908:     int ri;
        !          3909:        if(!(etc&IsBlob)) return;
        !          3910: 
        !          3911:        sv_ident = bp->ident;
        !          3912:         sv_runs = bp->runs;
        !          3913:        if(etc&IsRun) {
        !          3914:                /* decide which Runs_* bits should be set on output */
        !          3915:                bp->ident &= ~(Runs_f|Runs_ff|Runs_seek|Runs_g4);
        !          3916:                if(etc&Runs_g4) {
        !          3917:                        bp->ident |= Runs_g4;
        !          3918:                        }
        !          3919:                else {  /* assume Runs_ff is wanted */
        !          3920:                        bp->ident |= Runs_ff;
        !          3921:                        };
        !          3922:                }
        !          3923:        else {  bp->ident &= ~(IsRun|Runs_f|Runs_ff|Runs_seek|Runs_g4);
        !          3924:                bp->runs = 0;
        !          3925:                };
        !          3926:        fwrb_blob(fp,bp);
        !          3927:        bp->ident = sv_ident;
        !          3928:        bp->runs = sv_runs;
        !          3929: 
        !          3930:        if(bp->runs==0) return;
        !          3931: 
        !          3932:        if(etc&Runs_g4) {
        !          3933:                /* write CCITT Group 4 encoding for Runs */
        !          3934:                if(bp->ident&Runs_ff) {
        !          3935:                        fwrb_runfs_g4(fp,&(bp->bx),bp->runs,bp->r.ff);
        !          3936:                        }
        !          3937:                else abort("fwrb_blob_etc: etc&Runs_g4 but !(id&Runs_ff)");
        !          3938:                }
        !          3939:        else {  /* Assume Runs_ff format is desired */
        !          3940:                if(bp->ident&Runs_f) {
        !          3941:                        rp=bp->r.f;     
        !          3942:                        if(bp->ident&Blob_small) { /* use compressed Run records */
        !          3943:                                if(blob_debug) err("small");
        !          3944:                                /* can use small local static array */
        !          3945: #if FWRI
        !          3946:                                rsp= &rfs;
        !          3947: #else
        !          3948:                                rsp=rfsa;
        !          3949: #endif
        !          3950:                                while(rp!=NULL) {
        !          3951:                                        /* y, xs, xe will be relative to Blob.bx.a */
        !          3952:                                        rsp->y = rp->y - bp->bx.a.y;
        !          3953:                                        rsp->xs = rp->xs - bp->bx.a.x;
        !          3954:                                        rsp->xe = rp->xe - bp->bx.a.x;
        !          3955:                                        /* above,below connecting run indices are relative to
        !          3956:                                           the sequence no. of this run (ac>0 & bc>0) */
        !          3957:                                        if((rsp->ad = rp->ad)!=0)
        !          3958:                                                rsp->ac = rp->u.no - rp->ac->u.no;
        !          3959:                                        else rsp->ac = 0;
        !          3960:                                        if((rsp->bd = rp->bd)!=0)
        !          3961:                                                rsp->bc = rp->bc->u.no - rp->u.no;
        !          3962:                                        else rsp->bc = 0;
        !          3963:                                        if(blob_debug) err_runfs("  ",rsp);
        !          3964:                                        rp=rp->n;
        !          3965: #if FWRI
        !          3966:                                        fwri_RunFS(fp,rsp);
        !          3967: #else
        !          3968:                                        rsp++;
        !          3969: #endif
        !          3970:                                        };
        !          3971: #if !FWRI
        !          3972:                                if(fwrite(rfsa,sizeof(RunFS),bp->runs,fp)!=bp->runs)
        !          3973:                                        abort("fwrb_blob_etc: can't fwrite RunFS[%d]",bp->runs);
        !          3974:                                if(dbg_fwrb_runs)
        !          3975:                                        err("fwrb_blob_etc: %d bytes",bp->runs*sizeof(RunFS));
        !          3976: #endif
        !          3977:                                }
        !          3978:                        else {  if(blob_debug) err("large");
        !          3979:                                while(rp!=NULL) {       
        !          3980:                                        /* y, xs, xe will be relative to Blob.bx.a */
        !          3981:                                        rf.y = rp->y - bp->bx.a.y;
        !          3982:                                        rf.xs = rp->xs - bp->bx.a.x;
        !          3983:                                        rf.xe = rp->xe - bp->bx.a.x;
        !          3984:                                        /* above,below connecting run indices are relative to
        !          3985:                                           the sequence no. of this run (ac>0 & bc>0) */
        !          3986:                                        if((rf.ad = rp->ad)!=0)
        !          3987:                                                rf.ac = rp->u.no - rp->ac->u.no;
        !          3988:                                        else rf.ac = 0;
        !          3989:                                        if((rf.bd = rp->bd)!=0)
        !          3990:                                                rf.bc = rp->bc->u.no - rp->u.no;
        !          3991:                                        else rf.bc = 0;
        !          3992:                                        if(blob_debug) err_runf("   ",&rf);
        !          3993:                
        !          3994:                                        /* since there may be many, won't use a local array */
        !          3995: #if FWRI
        !          3996:                                        fwri_RunF(fp,&rf);
        !          3997: #else
        !          3998:                                        if(fwrite(&rf,sizeof(RunF),1,fp)!=1)
        !          3999:                                                abort("fwrb_blob_etc: can't fwrite RunF");
        !          4000: #endif
        !          4001:                                        rp=rp->n;
        !          4002:                                        };
        !          4003:                                if(dbg_fwrb_runs)
        !          4004:                                        err("fwrb_blob_etc: %d bytes",bp->runs*sizeof(RunF));
        !          4005:                                };
        !          4006:                        }
        !          4007:                else if(bp->ident&Runs_ff) {
        !          4008:                        if(bp->ident&Blob_small) { /* use compressed Run records */
        !          4009:                                if(blob_debug) err("small");
        !          4010:                                /* can use small local static array */
        !          4011: #if FWRI
        !          4012:                                rsp= &rfs;
        !          4013: #else
        !          4014:                                rsp=rfsa;
        !          4015: #endif
        !          4016:                                for(rfp=bp->r.ff,ri=0; ri<bp->runs; rfp++,ri++) {
        !          4017:                                        rsp->y = rfp->y;
        !          4018:                                        rsp->xs = rfp->xs;
        !          4019:                                        rsp->xe = rfp->xe;
        !          4020:                                        rsp->ad = rfp->ad;
        !          4021:                                        rsp->bd = rfp->bd;
        !          4022:                                        rsp->ac = rfp->ac;
        !          4023:                                        rsp->bc = rfp->bc;
        !          4024: #if FWRI
        !          4025:                                        fwri_RunFS(fp,rsp);
        !          4026: #else
        !          4027:                                        rsp++;
        !          4028: #endif
        !          4029:                                        };
        !          4030: #if !FWRI
        !          4031:                                if(fwrite(rfsa,sizeof(RunFS),bp->runs,fp)!=bp->runs)
        !          4032:                                        abort("fwrb_blob_etc: can't fwrite RunFS[%d]",bp->runs);
        !          4033:                                if(dbg_fwrb_runs)
        !          4034:                                        err("fwrb_blob_etc: %d bytes",bp->runs*sizeof(RunFS));
        !          4035: #endif
        !          4036:                                }
        !          4037:                        else {  if(blob_debug) err("large");
        !          4038: #if FWRI
        !          4039:                                for(rfq=(rfp=bp->r.ff)+bp->runs; rfp<rfq; rfp++) {
        !          4040:                                        fwri_RunF(fp,rfp);
        !          4041:                                        };
        !          4042: 
        !          4043: #else
        !          4044:                                if(fwrite(bp->r.ff,sizeof(RunF),bp->runs,fp)!=bp->runs)
        !          4045:                                        abort("fwrb_blob_etc: can't fwrite RunF[%d]",bp->runs);
        !          4046:                                if(dbg_fwrb_runs)
        !          4047:                                        err("fwrb_blob_etc: %d bytes",bp->runs*sizeof(RunF));
        !          4048: #endif
        !          4049:                                };
        !          4050:                        };
        !          4051:                }; 
        !          4052:        }
        !          4053: 
        !          4054: /* Read a given Blob's RunF's (binary) from FILE *fp; return F on EOF.
        !          4055:    If bp->runs<=max, read the RunF's into bp->r.ff, and set Runs_ff bit.
        !          4056:    otherwise ignore them all and set bp->r.ff==NULL.  */
        !          4057: int frdb_runfs(fp,bp,max)
        !          4058:     FILE *fp;
        !          4059:     Blob *bp;  /* *bp's fields already set up */
        !          4060:     int max;   /* maximum no. RunF records expected */
        !          4061: {   register RunFS *rsp,*rsq;
        !          4062:     static RunFS rfsa[256];
        !          4063:     register RunF *rfp,*rfq;
        !          4064:        if(bp->r.ff!=NULL && bp->runs <= max) {
        !          4065:                bp->ident |= Runs_ff;  bp->ident &= ~(Runs_f|Runs_seek);
        !          4066:                if(bp->ident&Blob_small) {
        !          4067:                        if(blob_debug) err("small");
        !          4068: #if FRDI
        !          4069:                        for(rsq=(rsp=rfsa)+bp->runs; rsp<rsq; rsp++) {
        !          4070:                                frdi_RunFS(fp,rsp);
        !          4071:                                };
        !          4072: #else
        !          4073:                        if(fread(rfsa,sizeof(RunFS),bp->runs,fp) < bp->runs)
        !          4074:                                return(0);
        !          4075: #endif
        !          4076:                        /* copy compressed RunFS's into RunF's */
        !          4077:                        for(rsq=(rsp=rfsa)+bp->runs,rfp=bp->r.ff;
        !          4078:                               rsp<rsq;
        !          4079:                                  rsp++,rfp++) {
        !          4080:                                /* beware sign extension from unsigned chars */
        !          4081:                                if(blob_debug) err_runfs("   ",rsp);
        !          4082:                                rfp->y = 0377&rsp->y;
        !          4083:                                rfp->xs = 0377&rsp->xs;
        !          4084:                                rfp->xe = 0377&rsp->xe;
        !          4085:                                rfp->ad = 0377&rsp->ad;
        !          4086:                                rfp->bd = 0377&rsp->bd;
        !          4087:                                rfp->ac = 0377&rsp->ac;
        !          4088:                                rfp->bc = 0377&rsp->bc;
        !          4089:                                };
        !          4090:                        }
        !          4091:                else {  if(blob_debug) err("large");
        !          4092: #if FRDI
        !          4093:                        for(rfq=(rfp=bp->r.ff)+bp->runs; rfp<rfq; rfp++)
        !          4094:                                frdi_RunF(fp,rfp);
        !          4095: #else
        !          4096:                        if(fread(bp->r.ff,sizeof(RunF),bp->runs,fp) != bp->runs)
        !          4097:                                return(F);
        !          4098: #endif
        !          4099:                        };
        !          4100:                }
        !          4101:        else {  bp->ident &= ~(Runs_ff|Runs_f);
        !          4102:                bp->ident |= Runs_seek;
        !          4103:                bp->r.seek=ftell(fp);
        !          4104:                /* skip past excessive no. of runs */
        !          4105: /** What if FRDI?? **/
        !          4106:                if(bp->ident&Blob_small)
        !          4107:                        fseek(fp,(long)((bp->runs)*sizeof(RunFS)),1);
        !          4108:                else
        !          4109:                        fseek(fp,(long)((bp->runs)*sizeof(RunF)),1);
        !          4110:                };
        !          4111:        if(ferror(fp)) return(-errno); else return(1);
        !          4112:        }
        !          4113: 
        !          4114: #if FRDI
        !          4115: /* read a Blob record (only) into *p */
        !          4116: int frdb_blob(f,p)
        !          4117:     FILE *f;
        !          4118:     Blob *p;
        !          4119: {   int bdy_mny;
        !          4120:        *p = empty_Blob;
        !          4121:        if(feof(f))
        !          4122:                return(0);
        !          4123:        p->ident=frdi_Ident(f);
        !          4124:        /* p->no=frdi_Seq(f); */
        !          4125:        frdi_Bbx(f,&(p->bx));
        !          4126:        p->area=frdi_uint4(f);
        !          4127:        p->per=frdi_uint4(f);
        !          4128:        /* don't read .n */
        !          4129:        p->m=frdi_Merit(f);
        !          4130:        p->runs=frdi_uint3(f);
        !          4131:        if((bdy_mny=frdi_uint2(f))>0) {
        !          4132:                p->bdsp = alloc_bdys();
        !          4133:                p->bdsp->mny = bdy_mny;
        !          4134:                };
        !          4135: #if dbg_frdb
        !          4136:        if((!(p->ident&IsBlob))||(p->ident&(IsALL&(~IsBlob))))
        !          4137:                err("frdb_blob: %s",blob_toa(p));
        !          4138: #endif
        !          4139: #if dbg_frdb_toa
        !          4140:        err("frdb_blob: %s",blob_toa(p));
        !          4141: #endif
        !          4142:        if(ferror(f))
        !          4143:                return(-errno);
        !          4144:        else return(1);
        !          4145:        }
        !          4146: #else
        !          4147: /* read a Blob record (only) into *bp;  return F if EOF */
        !          4148: int frdb_blob(fp,bp)
        !          4149:     FILE *fp;
        !          4150:     Blob *bp;
        !          4151: {   BlobF bf;
        !          4152:        if(fread(&bf,sizeof(BlobF),1,fp)!=1) return(0);
        !          4153:        if(blob_debug) err_blobf("bf",&bf);
        !          4154:        bp->ident=bf.ident;
        !          4155:        bp->no = 0;
        !          4156:        bp->bx=bf.bx;
        !          4157:        bp->area=bf.area;
        !          4158:        /*bp->per=bf.per;*/
        !          4159:        bp->runs=bf.runs;
        !          4160:        /*bp->bdys=bf.bdys;*/
        !          4161:        bp->n = NULL;
        !          4162:        bp->r.f = NULL;
        !          4163:        bp->bdsp = NULL;
        !          4164: #if dbg_frdb
        !          4165:        if((!(bp->ident&IsBlob))||(bp->ident&(IsALL&(~IsBlob))))
        !          4166:                err("frdb_blob: %s",blob_toa(bp));
        !          4167: #endif
        !          4168: #if dbg_frdb_toa
        !          4169:        err("frdb_blob: %s",blob_toa(bp));
        !          4170: #endif
        !          4171:        if(ferror(fp)) return(-errno); else return(1);
        !          4172:        }
        !          4173: #endif
        !          4174: 
        !          4175: err_blob(s,bp)
        !          4176:     char *s;
        !          4177:     Blob *bp;
        !          4178: {      fprintf(stderr,"%s ",s);
        !          4179:        if(bp==NULL) fprintf(stderr,"Blob NULL.\n");
        !          4180:        else fprintf(stderr,
        !          4181:                "Blob %d: x[%d,%d],y[%d,%d] ar%d ft%s r(#%d f%d)\n",
        !          4182:                bp->no,bp->bx.a.x,bp->bx.b.x,bp->bx.a.y,bp->bx.b.y,
        !          4183:                bp->area,ident_toa(bp->ident),bp->runs,rp_tod(bp->r.f));
        !          4184:        }
        !          4185: 
        !          4186: err_blob_runs(s,bp)
        !          4187:     char *s;
        !          4188:     Blob *bp;
        !          4189: {   Run *crp;
        !          4190:     char cs[20];
        !          4191:        fprintf(stderr,"%s ",s);
        !          4192:        err_blob(s,bp);
        !          4193:        if(bp!=NULL&&bp->runs>0&&bp->r.f!=NULL) {
        !          4194:                sprintf(cs,"%*s",strlen(s)+2," ");
        !          4195:                for(crp=bp->r.f; crp!=NULL; crp=crp->n) err_run(cs,crp);
        !          4196:                };
        !          4197:        }
        !          4198: 
        !          4199: err_blob_runfs(s,bp)
        !          4200:     char *s;
        !          4201:     Blob *bp;
        !          4202: {   RunF *crp;
        !          4203:     int ri;
        !          4204:     char cs[20];
        !          4205:        fprintf(stderr,"%s ",s);
        !          4206:        err_blob_briefly("",bp);
        !          4207:        if(bp!=NULL&&bp->runs>0&&bp->r.ff!=NULL) {
        !          4208:                sprintf(cs,"%*s",strlen(s)+2," ");
        !          4209:                for(crp=bp->r.ff,ri=0; ri<bp->runs; crp++,ri++) err_runf(cs,crp);
        !          4210:                };
        !          4211:        }
        !          4212: 
        !          4213: err_blobf(s,bp)
        !          4214:     char *s;
        !          4215:     BlobF *bp;
        !          4216: {   Run *crp;
        !          4217:     char cs[20];
        !          4218:        fprintf(stderr,"%s ",s);
        !          4219:        if(bp==NULL) fprintf(stderr,"BlobF NULL.\n");
        !          4220:        else fprintf(stderr,
        !          4221:                "BlobF: x[%d,%d],y[%d,%d] ar%d ft%s #r%d\n",
        !          4222:                bp->bx.a.x,bp->bx.b.x,bp->bx.a.y,bp->bx.b.y,
        !          4223:                bp->area,ident_toa(bp->ident),bp->runs);
        !          4224:        }
        !          4225: 
        !          4226: err_blob_briefly(s,bp)
        !          4227:     char s[];
        !          4228:     Blob *bp;
        !          4229: {   Run *crp;
        !          4230:        if(bp==NULL) err("%s Blob NULL.",s);
        !          4231:        else { fprintf(stderr,
        !          4232:                        "%s Blob #%d: bx(%d,%d),(%d,%d) ar%d f%s runs%d ",
        !          4233:                        s,
        !          4234:                        bp->no,
        !          4235:                        bp->bx.a.x,bp->bx.a.y,bp->bx.b.x,bp->bx.b.y,
        !          4236:                        bp->area,
        !          4237:                        ident_toa(bp->ident),
        !          4238:                        bp->runs);
        !          4239:                fprintf(stderr,"\n");
        !          4240:                };
        !          4241:        }
        !          4242: 
        !          4243: err_blob_stats()
        !          4244: {      err("Blob stats:  %d now, %d total, %d hi-water\n",
        !          4245:                        blob_max-blob_fr_mny,hi_blob_no+1,blob_hi);
        !          4246:        }
        !          4247: 
        !          4248: /* write blob list starting at fi */
        !          4249: fwrb_blobl_etc(fp,mny,fi,etc)
        !          4250:     FILE *fp;
        !          4251:     int mny;
        !          4252:     Blob *fi;
        !          4253:     Ident etc;
        !          4254: {   int bi;
        !          4255:     Blob *bp;
        !          4256:        if(mny>0) for(bi=0,bp=fi; bi<mny; bi++,bp=bp->n) {
        !          4257:                if(bp!=NULL) fwrb_blob_etc(fp,bp,etc);
        !          4258:                else {  err("fwrb_blobl_etc: bmny==%d but %dth (Blob *) is NULL",
        !          4259:                                mny,bi);
        !          4260:                        break;
        !          4261:                        };
        !          4262:                };
        !          4263:        }
        !          4264: 
        !          4265: fwrb_blobs_etc(fp,bs,etc)
        !          4266:     FILE *fp;
        !          4267:     Blobs bs;
        !          4268:     Ident etc;
        !          4269: {   register Blob *bp,**bpp;
        !          4270:        if(bs.mny==0) return;
        !          4271:        for(bp= *(bpp=bs.bpa); bp!=NULL; bp= *(++bpp))
        !          4272:                fwrb_blob_etc(fp,bp,etc);
        !          4273:        }
        !          4274: 
        !          4275: /* read a set of blobs, and their parts */
        !          4276: frdb_blobs_etc(fp,bsp,etc)
        !          4277:     FILE *fp;
        !          4278:     Blobs *bsp;
        !          4279:     Ident etc;         /* read only the parts indicated */
        !          4280: {   int bi;
        !          4281:     register Blob *bp,**bpp;
        !          4282:        if(bsp->mny<=0) {
        !          4283:                *bsp = empty_Blobs;
        !          4284:                return(1);
        !          4285:                };
        !          4286: 
        !          4287:        if((bpp=bsp->bpa=(Blob **)malloc((bsp->mny+1)*sizeof(Blob *)))==NULL)
        !          4288:                abort("frdb_blobs_etc: can't alloc Blobs.bpa[%d]",bsp->mny+1);
        !          4289:        for(bi=0; bi<bsp->mny; bi++) {
        !          4290:                *(bpp++) = bp = alloc_blob();
        !          4291:                if(!frdb_blob_etc(fp,bp,etc))
        !          4292:                        abort("frdb_blobs_etc: unexpected EOF");
        !          4293:                };
        !          4294:        *bpp = NULL;
        !          4295:        if(ferror(fp)) return(-errno); else return(1);
        !          4296:        }
        !          4297: 
        !          4298: /* Read a blob record into *bp, and then read its parts as specified by etc.
        !          4299:    If there aren't any runs, return normally.
        !          4300:    If etc&IsRun is false, then set Runs_seek and proceed as described below.
        !          4301:    If etc&Runs_ff, then the contents of r.ff is examined:
        !          4302:        if NULL, then space is allocated here for `runs' RunF's;
        !          4303:        if non-NULL, then on the assumption that it points to space for
        !          4304:        at least `runs' many, they are read into it.
        !          4305:    If etc&Runs_seek, then the ftell value is placed in r.seek,
        !          4306:        and the runs are simply skipped over.
        !          4307:    The Runs_f (list) option is unimplemented.
        !          4308:    The Blob is marked with the appropriate Runs_X bit.
        !          4309:    Return:  1 if normal & successful, 0 if EOF, and -1 if error.
        !          4310:     */
        !          4311: int frdb_blob_etc(fp,bp,etc)
        !          4312:     FILE *fp;
        !          4313:     Blob *bp;
        !          4314:     Ident etc;
        !          4315: {   int stat;
        !          4316:        if((stat=frdb_blob(fp,bp))!=1)
        !          4317:                return(stat);
        !          4318:        if(bp->runs<=0) {
        !          4319:                bp->runs = 0;
        !          4320:                bp->r.ff = NULL;
        !          4321:                bp->ident &= ~(Runs_f|Runs_seek|Runs_g4);
        !          4322:                bp->ident |= Runs_ff;
        !          4323:                return(1);
        !          4324:                };
        !          4325: 
        !          4326:        if(!(etc&IsRun)) {
        !          4327:                etc |= IsRun|Runs_seek;
        !          4328:                etc &= ~(Runs_f|Runs_ff|Runs_g4);
        !          4329:                };
        !          4330:        if (!(etc&(Runs_f|Runs_ff|Runs_g4|Runs_seek)) || etc&Runs_ff) {
        !          4331:                /* Want to deliver Runs in Runf ff[] format */
        !          4332:                if(bp->r.ff==NULL) {
        !          4333:                        if( (bp->r.ff=
        !          4334:                            (RunF *)malloc(bp->runs*sizeof(RunF)))==NULL)
        !          4335:                                abort("frdb_blob_etc: can't alloc r.ff[%d]",bp->runs);
        !          4336:                        };
        !          4337:                if(bp->ident&Runs_g4) {
        !          4338:                        /* Runs are in CCITT Group 4 format */
        !          4339:                        stat=frdb_g4_runfs(fp,&(bp->bx),bp->runs,bp->r.ff);
        !          4340:                        }
        !          4341:                else {  /* Runs are in RunF or RunFS format */
        !          4342:                        stat=frdb_runfs(fp,bp,bp->runs);
        !          4343:                        };
        !          4344:                bp->ident |= Runs_ff;  bp->ident &= ~(Runs_f|Runs_seek|Runs_g4);
        !          4345:                return(stat);
        !          4346:                }
        !          4347:        else if(etc&Runs_seek) {
        !          4348:                bp->ident |= Runs_seek;  bp->ident &= ~(Runs_ff|Runs_f);
        !          4349:                bp->r.seek=ftell(fp);   /* save seek addr */
        !          4350:                /* skip past runs */
        !          4351:                if(bp->ident&Blob_small)
        !          4352:                        fseek(fp,(long)((bp->runs)*sizeof(RunFS)),1);
        !          4353:                else    fseek(fp,(long)((bp->runs)*sizeof(RunF)),1);
        !          4354:                }
        !          4355:        else /* (etc&Runs_f) || (etc&Runs_g4) */ {
        !          4356:                abort("frdb_blob_etc:  Runs_f & Runs_g4 etc option unimplemented");
        !          4357:                };
        !          4358:        if(ferror(fp)) return(-errno); else return(1);
        !          4359:        }
        !          4360: 
        !          4361: /* read a number of blobs, and their parts, into linked-list (mny, *fi);
        !          4362:    return T iff not EOF  */
        !          4363: boolean frdb_blobl_etc(fp,bmny,fip,etc)
        !          4364:     FILE *fp;
        !          4365:     int bmny;
        !          4366:     Blob **fip;
        !          4367:     Ident etc;
        !          4368: {   int bi;
        !          4369:     register Blob *bp,**bpp;
        !          4370:     RunF *rp;
        !          4371:     int stat;
        !          4372:        if(bmny<=0) return(1);
        !          4373:        bi=0; bpp=fip;
        !          4374:        do {    bp=alloc_blob();
        !          4375:                if((stat=frdb_blob_etc(fp,bp,etc))!=1) return(stat);
        !          4376:                *bpp=bp;  bpp= &(bp->n);
        !          4377:                }
        !          4378:        while((++bi)<bmny);
        !          4379:        *bpp=NULL;
        !          4380:        if(ferror(fp)) return(-errno); else return(1);
        !          4381:        }
        !          4382: 
        !          4383: char *interp_toa(ip)
        !          4384:     Interp *ip;
        !          4385: {   static char s[80];
        !          4386:     char ms[3],shs[3],szs[3],hts[3],prs[3];
        !          4387:        strcpy(ms,merit_toa(ip->m));
        !          4388:        strcpy(shs,merit_toa(ip->mshap));
        !          4389:        strcpy(szs,merit_toa(ip->msize));
        !          4390:        strcpy(hts,merit_toa(ip->mbhgt));
        !          4391:        strcpy(prs,merit_toa(ip->p));
        !          4392:        sprintf(s,"%s %s sz%s ba%d m%s (sh%s sz%s ht%s) p%s",
        !          4393:                ident_toa(ip->ident),
        !          4394: #if CPU!=CRAY
        !          4395:                classid_toa(&(ip->ci)),
        !          4396: #else
        !          4397:                ip->ci.c,
        !          4398: #endif
        !          4399:                pts_toa(ip->size),
        !          4400:                ip->basl,
        !          4401:                ms,shs,szs,hts,prs);
        !          4402:        return(s);
        !          4403:        }
        !          4404: 
        !          4405: frdb_interpl_etc(fp,ilp,etc)
        !          4406:     FILE *fp;
        !          4407:     Interpl *ilp;
        !          4408:     Ident etc;
        !          4409: {   int ii,stat;
        !          4410:     Interp *ip,*pp;
        !          4411:        if(ilp->mny<=0) {
        !          4412:                *ilp = empty_Interpl;
        !          4413:                return(1);
        !          4414:                };
        !          4415: 
        !          4416:        for(ii=0; ii<ilp->mny; ii++) {
        !          4417:                ip=alloc_interp();
        !          4418:                if(ii==0) ilp->fi = ip;
        !          4419:                else pp->n = ip;
        !          4420:                if((stat=frdb_interp(fp,ip))!=1) return(stat);
        !          4421:                pp=ip;
        !          4422:                };
        !          4423:        ip->n = NULL;
        !          4424:        if(ferror(fp)) return(-errno); else return(1);
        !          4425:        }
        !          4426: 
        !          4427: #if FRDI
        !          4428: int frdb_interp(f,p)
        !          4429:     FILE *f;
        !          4430:     Interp *p;
        !          4431: {   int stat;
        !          4432:        *p = empty_Interp;
        !          4433:        if(feof(f))
        !          4434:                return(0);
        !          4435:        p->ident=frdi_Ident(f);
        !          4436:        frdi_ClassId(f,&(p->ci));
        !          4437:        p->mshap=frdi_Merit(f);
        !          4438:        p->size=frdi_Pts(f);
        !          4439:        p->msize=frdi_Merit(f);
        !          4440:        p->basl=frdi_Scoor(f);
        !          4441:        p->mbhgt=frdi_Merit(f);
        !          4442:        p->m=frdi_Merit(f);
        !          4443:        p->p=frdi_Prob(f);
        !          4444: #if dbg_frdb
        !          4445:        if((!(p->ident&IsInterp))||(p->ident&(IsALL&(~IsInterp))))
        !          4446:                err("frdb_interp: %s",interp_toa(p));
        !          4447: #endif
        !          4448:        if(ferror(f)) return(-errno); else return(1);
        !          4449:        }
        !          4450: #else
        !          4451: int frdb_interp(fp,ip)
        !          4452:     FILE *fp;
        !          4453:     Interp *ip;
        !          4454: {   InterpF inf;
        !          4455:     int stat;
        !          4456:        if((stat=fread(&inf,sizeof(InterpF),1,fp))!=1) {
        !          4457:                err("can't fread InterpF, status %d",stat);
        !          4458:                return(stat);
        !          4459:                };
        !          4460:        *ip = empty_Interp;
        !          4461:        ip->ident = inf.ident;
        !          4462:        ip->ci = inf.ci;
        !          4463:        ip->clp = NULL;
        !          4464:        ip->clsp = NULL;
        !          4465:        ip->mshap = inf.mshap;
        !          4466:        ip->size = inf.size;
        !          4467:        ip->msize = inf.msize;
        !          4468:        ip->basl = inf.basl;
        !          4469:        ip->mbhgt = inf.mbhgt;
        !          4470:        ip->m = inf.m;
        !          4471: #if dbg_frdb
        !          4472:        if((!(ip->ident&IsInterp))||(ip->ident&(IsALL&(~IsInterp))))
        !          4473:                err("frdb_interp: %s",interp_toa(ip));
        !          4474: #endif
        !          4475:        if(ferror(fp)) return(-errno); else return(1);
        !          4476:        }
        !          4477: #endif
        !          4478: 
        !          4479: free_interpl(ilp)
        !          4480:     Interpl *ilp;
        !          4481: {   int ii;
        !          4482:     Interp *ip,*np;
        !          4483:        if(ilp->mny>0) {
        !          4484:                for(ip=ilp->fi; ip!=NULL; ip=np) { np=ip->n; free_interp(ip); }
        !          4485:                ilp->mny=0;
        !          4486:                };
        !          4487:        ilp->fi=NULL;
        !          4488:        };
        !          4489: 
        !          4490: fwrb_interpl_etc(fp,is,etc)
        !          4491:     FILE *fp;
        !          4492:     Interpl is;
        !          4493:     Ident etc;
        !          4494: {   Interp *ip;
        !          4495:     int ii;
        !          4496:        if(is.mny>0) for(ii=0,ip=is.fi; ii<is.mny; ii++,ip=ip->n)
        !          4497:                fwrb_interp_etc(fp,ip,etc);
        !          4498:        }
        !          4499: 
        !          4500: /* remove this Interp from the Interp-list */
        !          4501: remove_interpl(ip,ilp)
        !          4502:     Interp *ip;
        !          4503:     Interpl *ilp;
        !          4504: {   Interp *rp,*pp;
        !          4505:        if(ilp->mny==1) {  /* frequent case */
        !          4506:                ilp->fi=NULL; ilp->mny=0;
        !          4507:                }
        !          4508:        else if(ilp->mny>1){
        !          4509:                pp=NULL;
        !          4510:                for(rp=ilp->fi; rp!=ip&&rp!=NULL; pp=rp,rp=rp->n) ;
        !          4511:                if(rp!=NULL) {  /* remove from list */
        !          4512:                        if(pp==NULL) ilp->fi=ip->n;
        !          4513:                        else pp->n=ip->n;
        !          4514:                        ilp->mny--;
        !          4515:                        }
        !          4516:                else err("remove_interpl: can't - not found");
        !          4517:                }
        !          4518:        else err("remove_interpl: can't - Interpl empty");
        !          4519:        }
        !          4520: 
        !          4521: /* prepend this Interp to the Interp-list */
        !          4522: prepend_interpl(ip,ilp)
        !          4523:     Interp *ip;
        !          4524:     Interpl *ilp;
        !          4525: {   Interp *rp,*pp;
        !          4526:        ip->n = ilp->fi;
        !          4527:        ilp->fi = ip;
        !          4528:        ilp->mny++;
        !          4529:        }
        !          4530: 
        !          4531: fwrb_interp_etc(fp,ip,etc)
        !          4532:     FILE *fp;
        !          4533:     Interp *ip;
        !          4534:     Ident etc;
        !          4535: {      fwrb_interp(fp,ip);
        !          4536:        }
        !          4537: 
        !          4538: #if FWRI
        !          4539: fwrb_interp(f,p)
        !          4540:     FILE *f;
        !          4541:     Interp *p;
        !          4542: {   InterpF inf;
        !          4543:     int stat;
        !          4544: #if dbg_fwrb
        !          4545:        if((!(p->ident&IsInterp))||(p->ident&(IsALL&(~IsInterp))))
        !          4546:                err("fwrb_interp: %s",interp_toa(p));
        !          4547: #endif
        !          4548:        fwri_Ident(f,p->ident);
        !          4549:        fwri_ClassId(f,&(p->ci));
        !          4550:        fwri_Merit(f,p->mshap);
        !          4551:        fwri_Pts(f,p->size);
        !          4552:        fwri_Merit(f,p->msize);
        !          4553:        fwri_Scoor(f,p->basl);
        !          4554:        fwri_Merit(f,p->mbhgt);
        !          4555:        fwri_Merit(f,p->m);
        !          4556:        fwri_Prob(f,p->p);
        !          4557: #if dbg_fwrb_toa
        !          4558:        err("fwrb_interp: %s",interp_toa(p));
        !          4559: #endif
        !          4560:        }
        !          4561: #else
        !          4562: fwrb_interp(fp,ip)
        !          4563:     FILE *fp;
        !          4564:     Interp *ip;
        !          4565: {   InterpF inf;
        !          4566:     int stat;
        !          4567: #if dbg_fwrb
        !          4568:        if((!(ip->ident&IsInterp))||(ip->ident&(IsALL&(~IsInterp))))
        !          4569:                err("fwrb_interp: %s",interp_toa(ip));
        !          4570: #endif
        !          4571:        memset(&inf,'\0',sizeof(inf));
        !          4572:        inf.ident = ip->ident;
        !          4573:        inf.ci = ip->ci;
        !          4574:        inf.mshap = ip->mshap;
        !          4575:        inf.size = ip->size;
        !          4576:        inf.msize = ip->msize;
        !          4577:        inf.basl = ip->basl;
        !          4578:        inf.mbhgt = ip->mbhgt;
        !          4579:        inf.m = ip->m;
        !          4580:        if((stat=fwrite(&inf,sizeof(InterpF),1,fp))!=1)
        !          4581:                abort("fwrb_interp: can't fwrite, status %d",stat);
        !          4582: #if dbg_fwrb_toa
        !          4583:        err("fwrb_interp: %s",interp_toa(ip));
        !          4584: #endif
        !          4585:        }
        !          4586: #endif
        !          4587: 
        !          4588: /** Run handling:
        !          4589:        alloc_run_pool -create pool of free run records
        !          4590:        free_run_pool - free entire pool
        !          4591:        alloc_run -     allocate a new run from pool
        !          4592:        free_run -      free a specified run (into pool)
        !          4593:        err_run -       print Run (ascii) to stderr
        !          4594:        err_runb -      print Run (ascii) to stderr (after added to blob set)
        !          4595:        err_runf -      print RunF (ascii) to stderr (after added to blob set)
        !          4596:        err_runfs -     print RunFS (ascii) to stderr (after added to blob set)
        !          4597:        err_run_stats - report statistics to stderr
        !          4598:    **/
        !          4599: 
        !          4600: /* RunPool functions: variable-size pool, speed-optimized alloc/free */
        !          4601: 
        !          4602: /* Initialize pool of Runs with increment `incr' (return F if can't allocate) */
        !          4603: boolean alloc_run_pool(incr,dbg)
        !          4604:     int incr;
        !          4605:     boolean dbg;       /* trace actions on Runs */
        !          4606: {   register Run *crp, *prp;
        !          4607:     register int i;
        !          4608:        _RunPool.incr = incr;
        !          4609:        _RunPool.pools = 1;
        !          4610:        if((_RunPool.pool=(Run **)malloc(_RunPool.pools*sizeof(Run *)))==NULL)
        !          4611:                return(F);
        !          4612:        if((_RunPool.pool[_RunPool.pools-1] =
        !          4613:            (Run *)malloc(_RunPool.incr*sizeof(Run)))==NULL) {
        !          4614:                free(_RunPool.pool);
        !          4615:                return(F);
        !          4616:                };
        !          4617:        _RunPool.next = 0;
        !          4618:        _RunPool.free = NULL;
        !          4619: #if STATS
        !          4620:        _RunPool.total = 0;
        !          4621: #endif
        !          4622:        _RunPool.dbg = dbg;
        !          4623:        if(_RunPool.dbg) err("alloc_run_pool: incr%d",_RunPool.incr);
        !          4624:        return(T);
        !          4625:        }
        !          4626: 
        !          4627: free_run_pool()
        !          4628: {   register int i;
        !          4629:        for(i=0; i<_RunPool.pools; i++) free(_RunPool.pool[i]);
        !          4630:        free(_RunPool.pool);
        !          4631:        }
        !          4632: 
        !          4633: /* The ``hard'' case of allocating runs from RunPool: can't be inline */
        !          4634: Run *hard_alloc_run()
        !          4635: {      _RunPool.pools++;
        !          4636:        if((_RunPool.pool=
        !          4637:                (Run **)realloc(_RunPool.pool,_RunPool.pools*sizeof(Run *)))==NULL)
        !          4638:                abort("alloc_Run: can't realloc");
        !          4639:        if((_RunPool.cur=_RunPool.pool[_RunPool.pools-1] =
        !          4640:            (Run *)malloc(_RunPool.incr*sizeof(Run)))==NULL)
        !          4641:                abort("alloc_Run: can't malloc");
        !          4642:        _RunPool.next=1;
        !          4643:        *(_RunPool.cur) = empty_Run;
        !          4644:        return(_RunPool.cur);
        !          4645:        }
        !          4646: 
        !          4647: int rp_tod(rp)
        !          4648:     Run *rp;
        !          4649: {      return((int)rp);
        !          4650:        }
        !          4651: 
        !          4652: char *runf_toa(rp)
        !          4653:     RunF *rp;
        !          4654: {  static char s[80];
        !          4655:    char s1[80];
        !          4656:        strcpy(s,"RunF ");
        !          4657:        if(rp==NULL) strcat(s,"NULL");
        !          4658:        else {  sprintf(s1,
        !          4659:                        "y%d,x[%d,%d] lag(ad%d,bd%d ac%d,bc%d)",
        !          4660:                        rp->y,rp->xs,rp->xe, 
        !          4661:                        rp->ad,rp->bd, rp->ac,rp->bc );
        !          4662:                strcat(s,s1);
        !          4663:                };
        !          4664:        return(s);
        !          4665:        }
        !          4666: 
        !          4667: char *runfs_toa(rp)
        !          4668:     RunFS *rp;
        !          4669: {  static char s[80];
        !          4670:    char s1[80];
        !          4671:        strcpy(s,"RunFS ");
        !          4672:        if(rp==NULL) strcat(s,"NULL");
        !          4673:        else {  sprintf(s1,
        !          4674:                        "y%d,x[%d,%d] lag(ad%d,bd%d ac%d,bc%d)",
        !          4675:                        rp->y,rp->xs,rp->xe, 
        !          4676:                        rp->ad,rp->bd, rp->ac,rp->bc );
        !          4677:                strcat(s,s1);
        !          4678:                };
        !          4679:        return(s);
        !          4680:        }
        !          4681: 
        !          4682: err_run(s,rp)
        !          4683:        char *s;
        !          4684:        Run *rp;
        !          4685: {      fprintf(stderr,"%s ",s);
        !          4686:        if(rp==NULL) fprintf(stderr,"Run NULL.\n");
        !          4687:        else fprintf(stderr,
        !          4688:                "Run %d: y%d,x[%d,%d] l(n%d) t(o%x ad%d,bd%d ac%d,bc%d)\n",
        !          4689:                rp_tod(rp), rp->y,rp->xs,rp->xe, 
        !          4690:                rp_tod(rp->n),
        !          4691: #if CPU!=CRAY
        !          4692:                (int)rp->u.o,
        !          4693: #else
        !          4694:                0,
        !          4695: #endif
        !          4696:                rp->ad,rp->bd,
        !          4697:                rp_tod(rp->ac),
        !          4698:                rp_tod(rp->bc) );
        !          4699:        }
        !          4700: 
        !          4701: /* print Run after it has been added to Blob set */
        !          4702: err_runb(s,rp)
        !          4703:        char *s;
        !          4704:        Run *rp;
        !          4705: {      fprintf(stderr,"%s ",s);
        !          4706:        if(rp==NULL) fprintf(stderr,"Run NULL.\n");
        !          4707:        else fprintf(stderr,
        !          4708:                "Run %d: y%d,x[%d,%d] l(n%d) t(no%d ad%d,bd%d ac%d,bc%d)\n",
        !          4709:                rp_tod(rp), rp->y,rp->xs,rp->xe, 
        !          4710:                rp_tod(rp->n),
        !          4711:                rp->u.no, rp->ad,rp->bd, rp_tod(rp->ac),rp_tod(rp->bc) );
        !          4712:        }
        !          4713: 
        !          4714: err_runf(s,rp)
        !          4715:        char *s;
        !          4716:        RunF *rp;
        !          4717: {      fprintf(stderr,"%s ",s);
        !          4718:        if(rp==NULL) fprintf(stderr,"RunF NULL.\n");
        !          4719:        else fprintf(stderr,
        !          4720:                "RunF: y%d,x[%d,%d] t(ad%d,bd%d ac%d,bc%d)\n",
        !          4721:                rp->y,rp->xs,rp->xe, 
        !          4722:                rp->ad,rp->bd, rp->ac,rp->bc );
        !          4723:        }
        !          4724: 
        !          4725: err_runfs(s,rp)
        !          4726:        char *s;
        !          4727:        RunFS *rp;
        !          4728: {   RunF rf;
        !          4729:        rf.y = 0377&rp->y;
        !          4730:        rf.xs = 0377&rp->xs;
        !          4731:        rf.xe = 0377&rp->xe;
        !          4732:        rf.ad = 0377&rp->ad;
        !          4733:        rf.bd = 0377&rp->bd;
        !          4734:        rf.ac = 0377&rp->ac;
        !          4735:        rf.bc = 0377&rp->bc;
        !          4736:        err_runf(s,&rf);
        !          4737:        }
        !          4738: 
        !          4739: int runs_of_blob(p,ra,max)
        !          4740:     Blob *p;
        !          4741:     RLE_Yrun *ra;
        !          4742:     int max;
        !          4743: {   int runs,ri;
        !          4744:     RunF *rfp;
        !          4745:        runs = p->runs;
        !          4746:        if(runs>max) {
        !          4747:                err("runs_of_blob: max exceeded - skip this blob");
        !          4748:                runs = 0;
        !          4749:                }
        !          4750:        else if(runs>0) {
        !          4751:                if(p->ident&Runs_ff) {
        !          4752:                        for(ri=0,rfp=p->r.ff; ri<runs; ri++,rfp++) {
        !          4753:                                ra->y = p->bx.a.y + rfp->y;
        !          4754:                                ra->xs = p->bx.a.x + rfp->xs;
        !          4755:                                ra->xe = p->bx.a.x + rfp->xe;
        !          4756:                                ra++;
        !          4757:                                };
        !          4758:                        }
        !          4759:                else {  err("runs_of_blob: handle only Runs_ff - skip this blob");
        !          4760:                        runs = 0;
        !          4761:                        };
        !          4762:                };
        !          4763:        return(runs);
        !          4764:        }
        !          4765: 
        !          4766: int no_runs_of_blobl(p)
        !          4767:     Blobl *p;
        !          4768: {   int runs;
        !          4769:     Blob *pp;
        !          4770:        runs = 0;
        !          4771:        if(p->mny>0) for(pp=p->fi; pp!=NULL; pp=pp->n) runs += pp->runs;
        !          4772:        return(runs);
        !          4773:        }
        !          4774: 
        !          4775: int runs_of_blobl(p,ra,max)
        !          4776:     Blobl *p;
        !          4777:     RLE_Yrun *ra;
        !          4778:     int max;
        !          4779: {   int runs;
        !          4780:     Blob *pp;
        !          4781:        runs = 0;
        !          4782:        if(p->mny>0) for(pp=p->fi; pp!=NULL; pp=pp->n)
        !          4783:                runs += runs_of_blob(pp,ra+runs,max-runs);
        !          4784:        return(runs);
        !          4785:        }
        !          4786: 
        !          4787: int no_runs_of_char(p)
        !          4788:     Char *p;
        !          4789: {   int runs;
        !          4790:     Blobl bl;
        !          4791:        runs = 0;
        !          4792:        bl.mny = p->bmny;  bl.fi = p->fi;
        !          4793:        runs += no_runs_of_blobl(&bl);
        !          4794:        return(runs);
        !          4795:        }
        !          4796: 
        !          4797: int runs_of_char(p,ra,max)
        !          4798:     Char *p;
        !          4799:     RLE_Yrun *ra;
        !          4800:     int max;
        !          4801: {   int runs;
        !          4802:     Blobl bl;
        !          4803:        runs = 0;
        !          4804:        bl.mny = p->bmny;  bl.fi = p->fi;
        !          4805:        runs += runs_of_blobl(&bl,ra,max);
        !          4806:        return(runs);
        !          4807:        }
        !          4808: 
        !          4809: /* ascending lexicographic order on y,xs */
        !          4810: int rn_asc(r1,r2)
        !          4811:    RLE_Yrun *r1,*r2;
        !          4812: {      if(r1->y < r2->y) return(-1);
        !          4813:        else if(r1->y==r2->y) {
        !          4814:                if(r1->xs < r2->xs) return(-1);
        !          4815:                else if (r1->xs == r2->xs) return(0);
        !          4816:                else return(1);
        !          4817:                }
        !          4818:        else return(1);
        !          4819:        }
        !          4820: 
        !          4821: RLE_Lines *rlines_of_char(chp)
        !          4822:     Char *chp;
        !          4823: {   static RLE_Lines rls;
        !          4824:     int no_runs,cy,ri;
        !          4825:     RLE_Run *lr;
        !          4826:     RLE_Yrun *ra,*cr;
        !          4827:     RLE_Line *cl;
        !          4828:        /* count runs */
        !          4829:        no_runs = no_runs_of_char(chp);
        !          4830:        /* allocate runs array */
        !          4831:        if((ra=(RLE_Yrun *)malloc(no_runs*sizeof(RLE_Yrun)))==NULL)
        !          4832:                abort("rlines_of_char: can't malloc ra[%d]",no_runs);
        !          4833:        runs_of_char(chp,ra,no_runs);
        !          4834:        /* sort runs ascending on (y,xs) */
        !          4835:        qsort(ra,no_runs,sizeof(RLE_Yrun),rn_asc);
        !          4836:        /* count RLE_lines.mny */
        !          4837:        rls.mny = 0; cy=Scoor_MIN;
        !          4838:        for(ri=0,cr=ra; ri<no_runs; ri++,cr++)
        !          4839:                if(cr->y!=cy) {
        !          4840:                        rls.mny++;
        !          4841:                        cy = cr->y;
        !          4842:                        };
        !          4843:        /* allocate RLE_lines */
        !          4844:        if((rls.rla=(RLE_Line *)malloc(rls.mny*sizeof(RLE_Line)))==NULL)
        !          4845:                abort("rlines_of_page: can't malloc rls.rla[%d]",rls.mny);
        !          4846:        /* fill in RLE_lines */
        !          4847:        cy=Scoor_MIN;  cl=rls.rla-1;
        !          4848:        for(ri=0,cr=ra; ri<no_runs; ri++,cr++) {
        !          4849:                if(cr->y!=cy) {
        !          4850:                        cl++;
        !          4851:                        cl->y = cy = cr->y;
        !          4852:                        cl->runs = 0;
        !          4853:                        lr = cl->r;
        !          4854:                        };
        !          4855:                lr->xs = cr->xs;  lr->xe = cr->xe;  lr++;  cl->runs++;
        !          4856:                };
        !          4857:        free(ra);
        !          4858:        return(&rls);
        !          4859:        }
        !          4860: 
        !          4861: int asc_merit(m1,m2)
        !          4862:     Merit *m1,*m2;
        !          4863: {      if(*m1 < *m2) return(-1);
        !          4864:        else if(*m1 > *m2) return(1);
        !          4865:        else return(0);
        !          4866:        }
        !          4867: 
        !          4868: /* Word merit is computed as a function of the merit of its Chars.
        !          4869:    No Chars forces merit to 0.0.
        !          4870:    The result is dominated by the weakest merit in the Word, slightly
        !          4871:    improved by the others. */
        !          4872: Merit wordmerit(w)
        !          4873:     Word *w;
        !          4874: {   register Char *cp,**cpp;
        !          4875:     static int m_alloc = 0;
        !          4876:     static Merit *ma;  /* ma[m_alloc] */
        !          4877:     int mi;
        !          4878:     Merit m;
        !          4879:     double wgt,sumwgt;
        !          4880:     
        !          4881:        if(w->cs.mny<=0) return(0.0);
        !          4882:        if(w->cs.mny>m_alloc) {
        !          4883:                if(m_alloc==0) {
        !          4884:                        m_alloc=w->cs.mny;
        !          4885:                        if((ma=(Merit *)malloc(m_alloc*sizeof(Merit)))==NULL)
        !          4886:                                abort("wordmerit: can't malloc ma[%d]",m_alloc);
        !          4887:                        }
        !          4888:                else {  m_alloc=w->cs.mny;
        !          4889:                        if((ma=(Merit *)realloc(ma,m_alloc*sizeof(Merit)))==NULL)
        !          4890:                                abort("wordmerit: can't realloc ma[%d]",m_alloc);
        !          4891:                        };
        !          4892:                };
        !          4893:        for(cp= *(cpp=w->cs.cpa),mi=0; cp!=NULL; cp= *(++cpp),mi++) {
        !          4894:                if(cp->area==0 || cp->il.mny==0) ma[mi]=0.0;
        !          4895:                else ma[mi] = cp->il.fi->m;
        !          4896:                };
        !          4897:        qsort(ma,w->cs.mny,sizeof(Merit),asc_merit);
        !          4898: #define DOM (5.0)
        !          4899:        /* The worst merit is DOM times more significant than second worst, which
        !          4900:           is DOM times more significant than the third worst, and so on.
        !          4901:           */
        !          4902:        m=ma[0];  sumwgt=wgt=1.0;
        !          4903:        for(mi=1; mi<w->cs.mny; mi++) {
        !          4904:                m += ma[mi]*(wgt /= DOM);  sumwgt += wgt;
        !          4905:                };
        !          4906:        m /= sumwgt;
        !          4907:        return(m);
        !          4908:        }
        !          4909: 
        !          4910: WordSet *alloc_wordset(top,cut,cap)
        !          4911:     double top;                /* initial maximum merit (is never reduced) */
        !          4912:     double cut;                /* cut ratio:  don't keep merit < cut*top */
        !          4913:     int cap;           /* capacity: maximum no. at a time (0 ==> no limit) */
        !          4914: {   WordSet *res;
        !          4915:        if((res=(WordSet *)malloc(sizeof(WordSet)))==NULL)
        !          4916:                abort("alloc_wordset: can't");
        !          4917:        *res = empty_WordSet;
        !          4918:        res->top = top;
        !          4919:        res->cut = cut;
        !          4920:        if((res->cap = cap)==0) res->cap=INT_MAX;
        !          4921:        return(res);
        !          4922:        }
        !          4923: 
        !          4924: int free_wordset_etc(s,etc)
        !          4925:     WordSet *s;
        !          4926:     Ident etc;
        !          4927: {   int high;
        !          4928:        free_words_etc(&(s->ws),etc);
        !          4929:        high = s->high;
        !          4930:        free(s);
        !          4931:        return(high);
        !          4932:        }
        !          4933: 
        !          4934: /* Find the maximum-merit word in the set; among ties, pick the first seen. */
        !          4935: Word *find_max_wordset(s)
        !          4936:     WordSet *s;
        !          4937: {   register Word *max,**wpp;
        !          4938:        max=NULL;
        !          4939:        if(s->ws.mny>0) for(wpp=s->ws.wpa; *wpp!=NULL; wpp++) {
        !          4940:                if(max==NULL || max->m < (*wpp)->m) max=(*wpp);
        !          4941:                };
        !          4942:        return(max);
        !          4943:        }
        !          4944: 
        !          4945: /* Find the maximum-merit word in the set; among ties, pick the last seen. */
        !          4946: Word *find_min_wordset(s)
        !          4947:     WordSet *s;
        !          4948: {   register Word *min,**wpp;
        !          4949:        min=NULL;
        !          4950:        if(s->ws.mny>0) for(wpp=s->ws.wpa; *wpp!=NULL; wpp++) {
        !          4951:                if(min==NULL || min->m >= (*wpp)->m) min=(*wpp);
        !          4952:                };
        !          4953:        return(min);
        !          4954:        }
        !          4955: 
        !          4956: /* Insert a Word (without duplicating it).
        !          4957:    Update top, min, max, & high.
        !          4958:    If the new word's merit is equal to the current maximum, it does not become
        !          4959:    become the new maximum.
        !          4960:    If the new word's merit is equal to the current minimum, it becomes
        !          4961:    the new minimum.
        !          4962:    */
        !          4963: put_wordset(w,s)
        !          4964:     Word *w;   /* must have w->m set up */
        !          4965:     WordSet *s;
        !          4966: {      insert_word(w,&(s->ws));
        !          4967:        if(max_wordset(s)==NULL || max_wordset(s)->m < w->m) {
        !          4968:                max_wordset(s) = w;
        !          4969:                if(s->top < max_wordmerit(s)) s->top = max_wordmerit(s);
        !          4970:                };
        !          4971:        if(min_wordset(s)==NULL || min_wordset(s)->m >= w->m)
        !          4972:                min_wordset(s) = w;
        !          4973:        if(s->high < s->ws.mny) s->high = s->ws.mny;
        !          4974:        }
        !          4975: 
        !          4976: /* Remove a Word (without freeing it). Maintain min/max, but don't prune items.
        !          4977:    Also, don't lower top or high. */
        !          4978: Word *get_wordset(w,s)
        !          4979:     Word *w;
        !          4980:     WordSet *s;
        !          4981: {      if(w==NULL || s->ws.mny==0) return(NULL);
        !          4982:        remove_word(w,&s->ws);
        !          4983:        if(s->ws.mny==0) {
        !          4984:                max_wordset(s) = NULL;
        !          4985:                min_wordset(s) = NULL;
        !          4986:                }
        !          4987:        else {  if(w==max_wordset(s)) max_wordset(s) = find_max_wordset(s);
        !          4988:                if(w==min_wordset(s)) min_wordset(s) = find_min_wordset(s);
        !          4989:                };
        !          4990:        return(w);
        !          4991:        }
        !          4992: 
        !          4993: /* Remove a Word (without freeing it).  Maintain min/max.  Don't lower top. */
        !          4994: Word *remove_wordset(w,s,n)
        !          4995:     Word *w;
        !          4996:     WordSet *s;
        !          4997:     char *n;
        !          4998: {   register Word *gw;
        !          4999: #if dbg_ws
        !          5000:        if(w!=NULL) err("remove_wordset: %X %s",w,word_toa(w));
        !          5001:        else err("remove_wordset: NULL");
        !          5002: #endif
        !          5003:        gw=get_wordset(w,s);
        !          5004: #if dbg_ws
        !          5005:        err_wordset(s,n);
        !          5006: #endif
        !          5007:        return(gw);
        !          5008:        }
        !          5009: 
        !          5010: /* Check whether a given Word is identical to any already in a given set.
        !          5011:    Word *w must have its hash key set up. */
        !          5012: boolean member_wordset(w,s)
        !          5013:     Word *w;
        !          5014:     WordSet *s;
        !          5015: {   register Word **ww;
        !          5016:        if(s->ws.mny>0) {
        !          5017:                for(ww=s->ws.wpa; (*ww)!=NULL; ww++) {
        !          5018:                        if(w->hash==(*ww)->hash && eq_word(w,(*ww)))
        !          5019:                                return(T);
        !          5020:                        };
        !          5021:                };
        !          5022:        return(F);
        !          5023:        }
        !          5024: 
        !          5025: /* Insert a Word into a set, maintaining uniqueness, a ``range'' property, and
        !          5026:    a maximum capacity.
        !          5027:    Uniqueness is maintained by refusing to insert words that are identical
        !          5028:    (eq_word()) to a word already in the set (or in set `u').
        !          5029:    The range property is that every word in the set must have merit no less
        !          5030:    than `s->cut' times the lifetime maximum for the set `s->top'.
        !          5031:    Maximum capacity is maintained as follows:  if insertion would mean more
        !          5032:    members than `cap', then either (a) the insertion is refused (if its
        !          5033:    merit is less than the current `min', or (b) the current `min' is deleted
        !          5034:    (and silently freed in its entirety), and the insertion occurs.
        !          5035:    The inserted Word is not duplicated.  If, as a result of an insertion,
        !          5036:    some of its words no longer satisfy the range property, they are silently
        !          5037:    removed (and freed in their entirety).
        !          5038:    Return T iff the Word is actually inserted.
        !          5039:    */
        !          5040: boolean insert_wordset(w,s,n,u)
        !          5041:     Word *w;
        !          5042:     WordSet *s;
        !          5043:     char *n;   /* name of set `s' */
        !          5044:     WordSet *u;        /* if !=NULL, also check uniqueness here */
        !          5045: {   Word *fr;
        !          5046: #if dbg_ws
        !          5047:        err("insert_wordset: %X %s",w,word_toa(w));
        !          5048: #endif
        !          5049:        if((w->m = wordmerit(w)) >= (s->cut * s->top)) {
        !          5050:                w->hash = hash_word(w);
        !          5051:                if(member_wordset(w,s) || (u!=NULL&&member_wordset(w,u)))
        !          5052:                        /* don't insert */
        !          5053:                        return(F);
        !          5054:                if(s->ws.mny==s->cap) /* at capacity */ {
        !          5055:                        if(w->m <= min_wordmerit(s)) /* too poor */ return(F);
        !          5056:                        else {  fr = remove_wordset(min_wordset(s),s);
        !          5057:                                free_word_etc(fr,IsALL);
        !          5058:                                };
        !          5059:                        };
        !          5060:                put_wordset( w, s );
        !          5061:                if(max_wordset(s)==w) /* this has become the new maximum */ {
        !          5062:                        while(min_wordmerit(s) < (s->cut * s->top)) {
        !          5063:                                fr = remove_wordset(min_wordset(s),s);
        !          5064:                                free_word_etc(fr,IsALL);
        !          5065:                                };
        !          5066:                        };
        !          5067: #if dbg_ws
        !          5068:                err_wordset(s,n);
        !          5069: #endif
        !          5070:                return(T);
        !          5071:                }
        !          5072:        else {
        !          5073: #if dbg_ws
        !          5074:                err_wordset(s,n);
        !          5075: #endif
        !          5076:                return(F);
        !          5077:                };
        !          5078:        }
        !          5079: 
        !          5080: err_wordset(s,n)
        !          5081:     WordSet *s;
        !          5082:     char *n;   /* name of set */
        !          5083: {   char m1[10],m2[10];
        !          5084:     register Word *wp,**wpp;
        !          5085:        fprintf( stderr,"WordSet %s: t%g c%g [%s/%X,%s/%X] m%d h%d: ",
        !          5086:                n,
        !          5087:                s->top,s->cut,
        !          5088:                strcpy(m1,merit_toa(max_wordmerit(s))),max_wordset(s),
        !          5089:                strcpy(m2,merit_toa(min_wordmerit(s))),min_wordset(s),
        !          5090:                s->ws.mny,s->high );
        !          5091:        if(s->ws.mny>0) for(wp= *(wpp=s->ws.wpa); wp!=NULL; wp= *(++wpp))
        !          5092:                fprintf(stderr,"%X/%s/%d ",wp,merit_toa(wp->m),wp->cs.mny);
        !          5093:        fprintf(stderr,"\n");
        !          5094:        }
        !          5095: 
        !          5096: translate_txtln(lp,off)
        !          5097:     Txtln *lp;
        !          5098:     Sp off;
        !          5099: {      lp->bx = *translate_bbx(&lp->bx,off);
        !          5100:        lp->basl += off.y;
        !          5101:        translate_chars(lp->cs,off);
        !          5102:        }
        !          5103: 
        !          5104: translate_chars(csp,off)
        !          5105:     Chars *csp;
        !          5106:     Sp off;
        !          5107: {   register Char *cp,**cpp;
        !          5108:        for(cp= *(cpp=csp->cpa); cp!=NULL; cp= *(++cpp))
        !          5109:                translate_char(cp,off);
        !          5110:        }
        !          5111: 
        !          5112: translate_char(cp,off)
        !          5113:     Char *cp;
        !          5114:     Sp off;
        !          5115: {      cp->bx = *translate_bbx(&cp->bx,off);
        !          5116:        translate_blobl(cp,off);
        !          5117:        }
        !          5118: 
        !          5119: translate_blobs(b,off)
        !          5120:     Blobs *b;
        !          5121:     Sp off;
        !          5122: {   register Blob *bp,**bpp;
        !          5123:        if(b->mny>0) for ( bp= *(bpp=b->bpa); bp!=NULL; bp= *(++bpp) )
        !          5124:                translate_blob(bp,off);
        !          5125:        }
        !          5126: 
        !          5127: translate_blobl(cp,off)
        !          5128:     Char *cp;
        !          5129:     Sp off;
        !          5130: {   int bi;
        !          5131:     register Blob *bp;
        !          5132:        for(bi=0,bp=cp->fi; bi<cp->bmny&&bp!=NULL; bi++,bp=bp->n)
        !          5133:                translate_blob(bp,off);
        !          5134:        }
        !          5135: 
        !          5136: translate_blob(bp,off)
        !          5137:     Blob *bp;
        !          5138:     Sp off;
        !          5139: {      bp->bx = *translate_bbx(&bp->bx,off);
        !          5140:        }
        !          5141: 

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.