summaryrefslogtreecommitdiff
path: root/Build/source/texk/web2c/cwebdir/twinx.w
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/web2c/cwebdir/twinx.w')
-rw-r--r--Build/source/texk/web2c/cwebdir/twinx.w460
1 files changed, 0 insertions, 460 deletions
diff --git a/Build/source/texk/web2c/cwebdir/twinx.w b/Build/source/texk/web2c/cwebdir/twinx.w
deleted file mode 100644
index ecfce0c8da8..00000000000
--- a/Build/source/texk/web2c/cwebdir/twinx.w
+++ /dev/null
@@ -1,460 +0,0 @@
-\datethis
-
-@*Introduction. This short program compiles a master index for a
-set of programs that have been processed by \.{CTWILL}.
-To use it, you say, e.g., \.{twinx} \.{*.tex} \.{>index.tex}.
-The individual programs should define their names with a line of
-the form `\.{\\def\\title\{NAME\}}'.
-
-@c
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-@<Type definitions@>@;
-@<Global variables@>@;
-@<Procedures@>@;
-@#
-int main(
- int argc,
- char *argv[])
-{ @<Local variables@>;
- @<Initialize the data structures@>;
- while (--argc) {
- f=fopen(*++argv,"r");
- if (!f)
- fprintf(stderr,"twinx: Couldn't open file %s for reading!\n",*argv);
- else {
- @<Scan file |f| until coming to the title@>;
- fclose(f);
- strncpy(*argv+strlen(*argv)-3,"idx",3);
- f=fopen(*argv,"r");
- if (!f)
- fprintf(stderr,"twinx: Couldn't open file %s for reading!\n",*argv);
- else {
- @<Copy the index file |f| into the data structures@>;
- fclose(f);
- }
- }
- }
- @<Output the data structures to make a master index@>;
- return 0;
-}
-
-@ @d buf_size 100 /* input lines won't be this long */
-
-@<Glob...@>=
-FILE *f;
-char buf[buf_size];
-char title[buf_size];
-char cur_name[buf_size];
-
-@ @<Scan file |f|...@>=
-while (1) {
- if (fgets(buf,buf_size,f)==NULL) {
- fprintf(stderr,"twinx: (no title found in file %s)\n",*argv);
- title[0]='\0';
- break;
- }
- if (strncmp(buf,"\\def\\title\{",11)==0) {@+register char *p,*q;
- for (p=buf+11,q=title;*p && *p!='}';p++) *q++=*p;
- *q='\0';
- break;
- }
-}
-
-@* Data structures.
-Our main task is to collate a bunch of texts associated with keys that
-have already been sorted. It seems easiest to do this by repeatedly merging
-the new data into the old, even though this means we'll be passing over
-some of the same keys 30 times or more; the computer is fast, and this
-program won't be run often.
-
-Further examination shows that a merging strategy isn't so easy after all,
-because the sorting done by \.{CTWILL} (and by \.{CWEAVE}) is weird
-in certain cases. When two index entries agree except for their
-``ilk,'' the order in which they appear in the index depends on the order in
-which they appear in the program. Thus, they might well appear in
-different order in two of the indexes we are merging.
-(There's also another glitch, although not quite as devasting:
-When two index entries have the
-same letters and the same ilk, but differ with respect to uppercase
-versus lowercase, the order in which they appear depends on the
-hash code used in \.{CWEB}'s \.{common.w} code!)
-
-So we'll use Plan B: All index entries will first be copied into a long
-list. The list will almost always consist of many sorted sublists,
-but we will not assume anything about its order. After all the copying
-has been done, we will use a list-merge sort to finish the job.
-
-The data structure is built from nodes that each contain three pointers.
-The first pointer is to an |id| string; the third pointer is to the
-|next| node; and the second pointer is either |data.s|, a pointer to
-a string of text, or |data.n|, a pointer to a node. In the main list,
-the |id| fields are the keys of the index, and the |data.n| fields
-point to lists of associated texts. In the latter lists, the |id| fields
-are the individual program titles, while the |data.s| fields are the
-texts.
-
-@<Type...@>=
-typedef union {
- char *s;
- struct node_struct *n;
-} mixed;
-typedef struct node_struct {
- const char *id;
- mixed data;
- struct node_struct *next;
-} node;
-
-@ We copy strings into blocks of storage that are allocated as needed.
-Here's a routine that stashes away a given string. It makes no attempt to
-handle extremely long strings, because such strings will arise only
-if the input is all screwed up.
-
-@d string_block_size 8192 /* number of bytes per string block */
-
-@<Proc...@>=
-char *save_string(
- char *s)
-{
- register char *p,*q; register int l;
- for (p=s;*p;p++) ;
- l=p-s+1;
- if (l>string_block_size) {
- fprintf(stderr,"twinx: Huge string `%.20s...' will be truncated!\n",s);
- l=string_block_size;
- s[l-1]='\0';
- }
- if (next_string+l>=bad_string) {
- next_string=(char*)malloc(string_block_size);
- if (next_string==NULL) {
- fprintf(stderr,"twinx: Not enough room for strings!\n");
- exit(-1);
- }
- bad_string=next_string+string_block_size;
- }
- for (p=s,q=next_string;*p;p++) *q++=*p;
- *q='\0';
- next_string=q+1;
- return next_string-l;
-}
-
-@ Nodes are allocated with a similar but simpler mechanism.
-
-@d nodes_per_block 340
-
-@<Proc...@>=
-node *new_node(void)
-{
- if (next_node==bad_node) {
- next_node=(node*)calloc(nodes_per_block,sizeof(node));
- if (next_node==NULL) {
- fprintf(stderr,"twinx: Not enough room for nodes!\n");
- exit(-2);
- }
- bad_node=next_node+nodes_per_block;
- }
- next_node++;
- return next_node-1;
-}
-
-@ @<Glob...@>=
-char *next_string,*bad_string;
-node *next_node,*bad_node;
-node header; /* the main list begins at |header.next| */
-node sentinel; /* intermediate lists will end at this node */
-
-@ We don't really have to initialize the string and node storage
-pointers, because global variables are zero already. But we might as
-well be tidy and state the initial conditions explicitly.
-
-It will be convenient to have extremely small and large keys in
-the dummy nodes.
-
-@<Initialize the data structures@>=
-next_string=bad_string=NULL;
-next_node=bad_node=NULL;
-header.next=NULL;
-header.id=" {"; /* smaller than any valid |id| */
-sentinel.id=" {\200}"; /* larger than any valid |id| */
-main_node=&header;
-
-@ @<Local v...@>=
-register node* main_node; /* current end of main list */
-
-@* Copying.
-Lines in the index file |f| that we're reading either begin a new
-entry or continue a long entry. In the first case, the line begins with
-\.{\\I} and then either \.{\\\\\{key\}} or \.{\\\char'174\{key\}} or
-\.{\\.\{key\}} or \.{\\\&\{key\}} or \.{\\\$\{key\}} or \.{\\9\{key\}}
-or just \.{\ \ \{key\}}. (These correspond to multi-character italic,
-single-digit italic, typewriter, bold, custom, variable, and roman styles.)
-In the second case, the line begins with a page number or \.{\\[};
-however, we recognize the second case by the fact that the previous line
-did not end with a period.
-
-@<Copy the index...@>=
-while (1) {@+register node *cur_node;
- if (fgets(buf,buf_size,f)==NULL) break; /* end of file */
- if (strncmp(buf,"\\I",2)==0) {
- @<Copy a new index entry into |cur_name| and |cur_node|@>;
- main_node->next=new_node();@+main_node=main_node->next;
- main_node->id=save_string(cur_name);
- main_node->data.n=cur_node;
- }
- else if (buf[0]!='\n')
- fprintf(stderr,"twinx: couldn't deal with `%.10s...' in file %s!\n",
- buf,*argv);
-}
-
-@ @<Copy a new index entry...@>=
-if (buf[4]!='{') {
- fprintf(stderr,"twinx: missing brace in file %s: `%.20s...'\n",*argv,buf);
- break;
-}
-{@+register char *p,*q;@+register int bal=1;
- cur_name[0]=buf[2];@+cur_name[1]=buf[3];@+cur_name[2]='{';
- for (p=buf+5,q=cur_name+3;*p&&(bal||*p=='{');p++) {
- if (*p=='{') bal++;
- else if (*p=='}') bal--;
- *q++=*p;
- }
- if (bal) {
- fprintf(stderr,"twinx: unbalanced entry in file %s: `%.20s...'\n",*argv,buf);
- break;
- }
- if (*p++!=',') {
- fprintf(stderr,"twinx: missing comma in file %s: `%.20s...'\n",*argv,buf);
- break;
- }
- if (*p++!=' ') {
- fprintf(stderr,"twinx: missing space in file %s: `%.20s...'\n",*argv,buf);
- break;
- }
- *q='\0';
- @<Copy the text part of the index entry into |cur_node|@>;
-}
-
-@ When we get here, |p| points to the beginning of the text following a key
-in the index. The index entry ends with the next period, possibly several
-lines hence. In the multiple-line case, |cur_node| will point to the
-final line, which points to the penultimate line, etc.
-
-@<Copy the text part of the index entry...@>=
-{@+int period_sensed=0;
- node *continuation;
- cur_node=new_node();
- cur_node->id=save_string(title);
- do@+{
- for (q=p;*q&&*q!='\n'&&*q!='.';q++) ;
- if (*q=='.') period_sensed=1;
- *q='\0';
- cur_node->data.s=save_string(p);
- if (period_sensed) break;
- continuation=new_node(); /* the |id| field is |NULL| */
- continuation->next=cur_node;
- cur_node=continuation;
- p=buf;
- }@+while(fgets(buf,buf_size,f));
- if (!period_sensed) {
- fprintf(stderr,"twinx: File %s ended in middle of entry for %s!\n",
- *argv,cur_name);
- break;
- }
-}
-
-@* Sorting.
-Let us opt for simplicity instead of tuning up for speed. The idea in
-this step is to take a list that contains $k$ ascending runs and reduce it
-to a list that contains $\lceil k/2\rceil$ runs, repeating until $k=1$.
-We could make the program about twice as fast if we took the trouble to
-remember the boundaries of runs on the previous pass; here, every
-pass will be the same.
-
-@<Output the data structures to make a master index@>=
-@<Sort the main list, collapsing entries with the same |id|@>;
-@<Output the main list in suitable \TeX\ format@>;
-
-@ The |compare| subroutine, which specifies the relative order of
-|id| fields in two nodes, appears below. Let's get the sorting logic
-right first.
-
-The algorithm is, in fact, rather pretty---I hate to say cute, but
-that's the word that comes to mind. Some day I must write out the
-nice invariant relations in these loops. Too bad it's not more efficient.
-
-Remember that |header.id| is $-\infty$ and |sentinel.id| is $+\infty$.
-Also remember that the main list begins and ends at the header node.
-
-@<Sort the main list, collapsing entries with the same |id|@>=
-main_node->next=&header;
-while (1) {@+register node *p,*q,*r,*s,*t;
- t=&header;
- r=t->next;
- while (1) {
- if (r==&header) break;
- p=s=r;
- @<Advance |s| until it exceeds |r=s->next|@>;
- if (r==&header) break;
- s->next=&sentinel;
- q=s=r;
- @<Advance |s| until it exceeds |r=s->next|@>;
- s->next=&sentinel;
- @<Merge |p| and |q|, appending to |t|@>;
- t->next=r;
- }
- if (t==&header) break;
-}
-
-@ @<Advance...@>=
-do@+{@+register int d;
- r=s->next;
- d=compare(s,r);
- if (d>0) break; /* |s->id>r->id| */
- if (d==0) { /* |s->id=r->id| */
- collapse(s,r); /* put |r|'s data into |s|'s list */
- s->next=r->next; /* node |r| will be unclaimed garbage */
- } else s=r; /* this is the normal case, |s->id<r->id| */
-}@+while(1);
-
-@ Merging takes place in such a way that sorting is stable.
-Thus, index entries for a key that appears in different programs
-will remain in the order of the \.{.tex} files on the command line.
-
-@<Merge...@>=
-do@+{@+register int d;
- d=compare(p,q);
- if (d>0) { /* |p->id>q->id| */
- t->next=q;
- t=q;
- q=q->next;
- } else if (d<0) { /* |p->id<q->id| */
- t->next=p; /* |p->id<q->id| */
- t=p;
- p=p->next;
- } else if (p==&sentinel) break;
- else {
- collapse(p,q); /* put |q|'s data into |p|'s list */
- q=q->next;
- }
-}@+while(1);
-
-@ Comparison is a three-stage process in general. First we compare the
-keys without regarding case or format type. If they are equal with
-respect to that criterion, we try again, with case significant.
-If they are still equal, we look at the format characters (the first
-two characters of the |id| field).
-
-@<Proc...@>=
-int compare(
- node *p, node *q)
-{@+register unsigned char *pp,*qq;
- for (pp=(unsigned char*)p->id+3,qq=(unsigned char*)q->id+3;
- *pp&&ord[*pp]==ord[*qq];pp++,qq++) ;
- if (*pp || *qq) return ord[*pp]-ord[*qq];
- for (pp=(unsigned char*)p->id+3,qq=(unsigned char*)q->id+3;
- *pp&&*pp==*qq;pp++,qq++) ;
- if (*pp || *qq) return (int)*pp-(int)*qq;
- if (p->id[0]!=q->id[0]) return p->id[0]-q->id[0];
- return p->id[1]-q->id[1];
-}
-
-@ The collation order follows a string copied from \.{CWEAVE}.
-
-@<Glob...@>=
-char collate[102]; /* collation order */
-char ord[256]; /* rank in collation order */
-
-@ The right brace is placed lowest in collating order, because each
-key is actually followed by a right brace when we are sorting.
-
-Apology: I haven't had time to update this part of the program to
-allow 8-bit characters. At present the data is assumed to be
-7-bit ASCII, as it was in the early versions of \.{CWEAVE}.
-
-@<Init...@>=
-collate[0]=0; strcpy(collate+1,"} \1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\
-\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\
-!\42#$%&'()*+,-./:;<=>?@@[\\]^`{|~_\
-abcdefghijklmnopqrstuvwxyz0123456789");
-{@+register int j;
- for (j=1;collate[j];j++) ord[(int)collate[j]]=j;
- ord[128]=j; /* this affects the ordering of |sentinel.id| */
- for (j='A';j<='Z';j++) ord[j]=ord[tolower(j)];
-}
-
-@ When two lists are combined, we put the data from the second node
-before the data from the first node, because we are going to reverse
-the order when printing. After this procedure has acted, the field
-|q->data.n| should not be considered an active pointer.
-
-@<Proc...@>=
-void collapse(
- node *p, node *q)
-{@+register node *x;
- for (x=q->data.n;x->next;x=x->next) ;
- x->next=p->data.n;
- p->data.n=q->data.n;
-}
-
-@ The only remaining trick is to format the underline characters
-properly, especially in the ``custom'' format when they must become
-\.x's.
-
-@<Output the main list in suitable \TeX\ format@>=
-{@+register node *x;
- printf("\\input cttwinxmac\n");
- for (x=header.next;x!=&header;x=x->next) {
- printf("\\I");
- @<Output |x->id| in suitable \TeX\ format@>;
- @<Output the lines of |x->data.n| in reverse order@>;
- }
- printf("\\fin\n");
-}
-
-@ @<Output |x->id|...@>=
-{@+register const char *p=x->id;
- if (*p==' ') {
- if (*(p+1)!=' ') goto unknown;
- goto known;
- }
- if (*p!='\\') goto unknown;
- switch (*(p+1)) {
- case '\\': case '|': case '.': case '&': case '9':
- printf("\\%c",*(p+1));
- goto known;
- case '$': printf("$\\");
- for (p+=3;*p!='}';p++)
- if (*p=='_') putchar('x');
- else putchar(*p);
- putchar('$');
- goto done;
- default: goto unknown;
- }
- unknown: fprintf(stderr,"twinx: `%s' has unknown format!\n",p);
- known: for (p+=2;*p;p++) {
- if (*p=='_') putchar('\\');
- putchar(*p);
- }
-done:;
-}
-
-@ @<Output the lines of |x->data.n| in reverse order@>=
-{@+register node *y=x->data.n,*z=NULL;
- while (y) {@+register node *w;
- w=y->next;
- y->next=z;
- z=y;
- y=w;
- }
- while (z) {
- if (z->id) printf("\\unskip, {\\sc %s}~",z->id);
- fputs(z->data.s,stdout);
- z=z->next;
- if (z) putchar('\n');
- else puts(".");
- }
-}
-
-@* Index.