Annotation of researchv10no/cmd/bcp/Text.c, revision 1.1.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.