Name sorting
[TeXnicard.git] / texnicard.w
blob92d5f110fdc12ad3cde0e698cbb50d6a30298c69
1 % TeXnicard
2 % version 0.1
4 % Licensed by GNU GPL v3 or later version.
6 \def\title{\TeX nicard}
7 \def\covernote{{\fiverm Batteries not included. Do not use this book as a
8 flotation device. This is free software; see source file for details.}}
10 % Prevent \outer from getting in the way, stupid!
11 \def\+{\tabalign}
13 @mp@-
14 ``u{YJ"@<Predeclaration of procedures@>=
15 qJA";
16 J"@
17 "@<Procedure codes@>=
18 B" {
21 \long\def\IndexCharacter#1':{`\.{\char`#1}'}
22 @mcase@-
23 ``u "case
24 qAqA/@!@^\IndexCharacter\
25 Bqu'B"@>
26 YJ"@<Nothing~@>
29 \iffalse
30 @s _decl_head_ =09
31 @s FILE int
32 \fi
34 \newcount\bibliocount \bibliocount=0
35 \def\biblio#1{%
36 \advance\bibliocount by 1 %
37 $^{[\the\bibliocount]}$%
38 \expandafter\def\csname biblio \the\bibliocount\endcsname{#1}%
41 \emergencystretch=\hsize
43 \def\strike#1{%
44 \setbox0=\hbox{#1}%
45 \rlap{\vrule height 3.2pt depth -2.5pt width \wd0}{\box0}%
48 @*Introduction. This is \TeX nicard, a program designed for similar
49 purposes of Magic Set Editor, but in a different (and better) way. It
50 should be able to produce higher quality cards than Wizards of the Coast,
51 and then they ought to use this program, too!
53 @^Magic Set Editor@>
54 @^Wizards of the Coast@>
55 @^commercial viability@>
58 @<Memory usage logging@>@;
59 @<Interpreted C codes@>@;
60 @<Include files@>@;
62 @<Typedefs@>@;
63 @<Late Typedefs@>@;
64 @<The include file for memory managed types@>@;
65 @<Global variables@>@;
66 @<Predeclaration of procedures@>@;
67 @<Procedure codes@>@;
69 @ This line below should be changed with the current version number,
70 whenever a new version is released. (If you fork this program, you should
71 also include some indication of forking in the \\{version\_string}.)
72 % (it doesn't work if I use vertical bars here)
74 @^forking@>
76 @d version_string "0.1"
77 @d version_number 1 // one major is worth ten minors
79 @ @<Typedefs@>=
80 typedef unsigned char boolean;
82 @ You might be wondering what this section is for (especially since it
83 appears to be unused). The reason is that some metamacros use it in order
84 to force the compiler to know the correct line numbers (in case some lines
85 have been added by metamacros).
87 @^nothing@>
88 @^metamacro@>
90 @<Nothing~@>= /* ... */
92 @ There is also memory usage logging. If it is not being compiled for
93 memory usage logging, it should just ignore these kind of commands.
95 @<Memory usage logging@>=
96 #ifndef @!memusage_log
97 #define @[memusage_log(_text,_arg1)@]
98 #endif
100 @*Memory Management. This program uses a lot of similar memory management,
101 so they will be defined in this chapter.
103 @^memory management@>
105 @d none -1 // indication that a |data_index| means nothing
107 @<Typedefs@>=
108 typedef struct {
109 char*data; // pointer to array of blocks (|char*| for use with |sizeof|)
110 int used; // number of blocks used
111 int allocated; // number of blocks allocated
112 } managed_memory;
113 @#typedef int data_index;
115 @ We will use an interpreted C code here, which will send output to a
116 header file |"memory_management.h"|.
118 @<The include file for memory managed types@>=
119 #include "memory_management.h"
121 @ We will need some variables now just to keep track of which kinds of
122 memory managed areas are needed.
124 @<Interpreted C codes@>= @{
125 char**memory_managed_types;
126 int num_memory_managed_types;
127 memory_managed_types=malloc(128*sizeof(char*));
128 num_memory_managed_types=0;
131 @ From this code, the structure will be created in the header file for
132 each type that we need a |memory_of|. This section, however, is just a
133 ``wrapper'' code for the template.
135 @f @!memory_of _decl_head_ // category 9
137 @<Interpreted C codes@>= @{
138 void memory_of$() {
139 should_output=0;
140 set_goal("bp","",@+{
141 sendc(0200|'{'); // begin interpret mode
142 send("send_memory_of(\"");
143 set_goal("e","",@+{
144 send("\");");
145 sendc(0200|'}'); // end interpret mode
146 should_output=0;
147 }@+);
148 }@+);
152 @ Here is what it does in order to keep a list of the memory managed
153 types. Note the type name was enclosed in quotation marks, so now it will
154 be received as a string.
156 @<Interpreted C codes@>= @{
157 void send_memory_of(char*s) {
158 int i;
159 s++;
160 @<Send the proper name of the memory managed type@>;
161 for(i=0;i<num_memory_managed_types;i++) {
162 if(!strcmp(s,memory_managed_types[i])) return;
164 memory_managed_types[num_memory_managed_types++]=s;
168 @ @<Send the proper name of the memory managed type@>= {
169 send(" x__");
170 send(s);
171 send(" ");
174 @ Now the code you get to in order to define the structures in the header
175 file. We are mostly just copying the form of our |managed_memory|
176 structure, but it will be customized to work with the specific type of the
177 |data| components.
179 @<Interpreted C codes@>= @{
180 void send_memory_managed_types() {
181 int i;
182 for(i=0;i<num_memory_managed_types;i++) {
183 send("typedef struct {");
184 send(memory_managed_types[i]);
185 send("*data; int used; int allocated; } x__");
186 send(memory_managed_types[i]);
187 send(";");
192 @ @(memory_management.h@>= @{
193 send_memory_managed_types();
196 @ These next two subroutines are used to allocate additional memory.
198 @d init_memory(_a,_size) init_memory_(&(_a),sizeof(*((_a).data)),(_size))
199 @d new_record(_area) new_record_(&(_area),sizeof(*((_area).data)))
201 @-p void*init_memory_(void*mem,int record_size,int num_records) {
202 managed_memory*m=mem;
203 m->data=malloc(record_size*num_records);
204 m->used=0;
205 m->allocated=num_records;
206 if(!m->data) @<Fatal error due to lack of memory@>;
207 return m->data;
210 @ @-p data_index new_record_(void*mem,int record_size) {
211 managed_memory*m=mem;
212 m->used++;
213 if(m->used>m->allocated) {
214 m->allocated*=2;
215 m->data=realloc(m->data,m->allocated*record_size);
217 if(!m->data) @<Fatal error due to lack of memory@>;
218 @<Zero the new record@>;
219 return m->used-1;
222 @ @<Fatal error due to lack of memory@>= {
223 fprintf(stderr,"Out of memory\n");
224 exit(1);
227 @ @<Zero the new record@>= {
228 memset(m->data+(record_size*(m->used-1)),0,record_size);
231 @ Now just one more thing. It is useful to have a |foreach| macro to
232 iterate the areas.
234 @d foreach(_var,_area) for(_var=0;_var<_area.used;_var++)@;
235 @f foreach while
237 @*Symbolic Names. There will be some names defined for the use of naming
238 subroutines, symbolic constants, patterns, card areas, etc. These names
239 are stored in a |managed_memory| called |names|.
241 It also stores references to other things (defined in later chapters). The
242 numeric value of a name in |names.data[x]| is |x+256|.
244 @<Late Typedefs@>=
245 typedef struct {
246 char*name;
247 @<More elements of |name_data|@>@;
248 } name_data;
250 @ @<Global variables@>=
251 memory_of(name_data) names;
253 @ @<Initialize memory@>= init_memory(names,16);
255 @ This subroutine finds a name, adding it if necessary. The number
256 corresponding to it (as described above) will be the return value.
258 @-p int find_name(char*name) {
259 @<Search for the |name| in |names|@>;
260 @<Add the new name (it was not found)@>;
263 @ @<Search for the |name| in |names|@>= {
264 int i;
265 foreach(i,names) {
266 if(!strcmp(names.data[i].name,name)) return i+256;
270 @ @<Add the new name (it was not found)@>= {
271 int n=new_record(names);
272 names.data[n].name=strdup(name);
273 return n+256;
276 @ A macro will be useful to access the data from a number.
278 @d name_info(_num) names.data[(_num)-0x0100]
280 @ This code lists the names. It is used for a diagnostic purpose.
282 @<Display the list of names@>= {
283 int n;
284 foreach(n,names) {
285 printf("%d \"%s\" ",n+256,names.data[n].name);
286 @<Display other fields of |names.data[n]|@>;
287 printf("\n");
291 @*Storage of Tokens. Tokens are stored as 16-bit numbers. Values |0x0020|
292 to |0x00FF| represent those ASCII characters, and |0x0000| to |0x001F| are
293 ASCII control codes. Higher numbers represent an index into the |names|
294 array (where |0x0101| represents |names.data[0x0001]|).
296 @<Typedefs@>=
297 @q[data type of tokens]@>
298 typedef unsigned short token;
300 @ This section lists the ASCII control codes which can be used. Some of
301 them have slightly different meaning from the ASCII standard.
303 @d null_char 0x00 // end of a |raw_data| string or similar things
304 @d pre_null_char 0x01 // becomes |null_char|
305 @d end_transmission 0x04 // marks the end of the last card in this area
306 @d tabulation 0x09 // represents a tab in a {\TeX} alignment
307 @d raw_data 0x10 // enter raw {\TeX} mode
308 @d whatsit 0x1A // a token for converting into a name token
309 @d escape_code 0x1B // represents a {\TeX} control sequence introducer
310 @d record_separator 0x1E // marks the end of a card
311 @d field_separator 0x1F // marks the end of a field of a card
312 @d start_name_code 0x0100
314 @ These tokens are used in card areas, which are defined (and described)
315 in the next chapter.
317 @*Cards. The data of the cards is stored in card areas. Each card area
318 is a list of tokens, terminated by |record_separator|. The final card in
319 the area is terminated by |end_transmission|.
321 @<Typedefs@>=
322 typedef struct {
323 token*tokens;
324 int allocated;
325 int used;
326 } card_area_data;
328 @ @<More elements of |name_data|@>=
329 boolean has_card_area;
330 data_index card_area;
332 @ @<Global variables@>=
333 memory_of(card_area_data) card_areas;
335 @ @<Initialize memory@>= init_memory(card_areas,1);
337 @ A new card area is created with this.
339 @-p data_index set_card_area(int num) {
340 name_data*m=&name_info(num);
341 @<Use the card area which is already set, if able@>;
342 @<Otherwise, create a new card area and use the new one@>;
345 @ @<Use the card area which is already set, if able@>= {
346 if(m->has_card_area) return m->card_area;
349 @ @<Otherwise, create a new card area and use the new one@>= {
350 data_index n=new_record(card_areas);
351 m->has_card_area=1;
352 card_areas.data[n].allocated=0x100;
353 card_areas.data[n].tokens=malloc(0x100*sizeof(token));
354 card_areas.data[n].used=0;
355 return n;
358 @ This subroutine sends a token to a card area.
360 @-p void send_token(data_index a,token x) {
361 if(card_areas.data[a].allocated<card_areas.data[a].used+4)
362 @<Double the allocation of card area tokens@>;
363 card_areas.data[a].tokens[card_areas.data[a].used++]=x;
366 @ @<Double the allocation of card area tokens@>= {
367 int n=(card_areas.data[a].allocated*=2)*sizeof(token);
368 card_areas.data[a].tokens=realloc(card_areas.data[a].tokens,n);
371 @ @<Display other fields of |names.data[n]|@>= {
372 if(names.data[n].has_card_area)
373 printf("C(%d) ",names.data[n].card_area);
376 @ The code in this section is used to ensure that each card area is
377 properly terminated with |end_transmission| marker, so that when it is
378 time to write the output files, it will know when to stop.
380 @<Send |end_transmission| to each card area@>= {
381 data_index a;
382 foreach(a,card_areas) send_token(a,end_transmission);
385 @*Patterns. For pattern matching, we store the patterns in one memory
386 managed area. The index of the beginning of each pattern area is stored
387 in the |names| list.
389 These constants are special codes which can occur in the |text| string
390 of a pattern.
392 @d begin_capture 1
393 @d end_capture 2
394 @d match_keyword 3 // match a keyword followed by a character in a table
395 @d match_table 4 // match a character using a table
396 @d optional_table 5 // match a character optional using a table
397 @d failed_match 6
398 @d jump_table 7 // use a table to jump to a marker
399 @d successful_match 8
400 @d back_one_space 9
401 @d forward_one_space 10
402 @d match_left_side 11 // match at beginning of line
403 @d match_right_side 12 // match at end of line
405 @<Typedefs@>=
406 typedef struct {
407 char*text;
408 unsigned int category; // category for keywords
409 data_index subroutine;
410 data_index next;
411 } pattern_data;
413 @ @<More elements of |name_data|@>=
414 boolean has_pattern_area;
415 data_index pattern_area;
417 @ @<Global variables@>=
418 memory_of(pattern_data) pattern_areas;
420 @ @<Initialize memory@>= init_memory(pattern_areas,4);
422 @ @<Display other fields of |names.data[n]|@>= {
423 if(names.data[n].has_pattern_area)
424 printf("P(%d) ",names.data[n].pattern_area);
427 @ A new pattern area is created with this. The patterns in an area are
428 stored like a linked list. The last one with |next| pointing to nothing,
429 is the terminator entry.
431 @-p data_index set_pattern_area(int num) {
432 name_data*m=&name_info(num);
433 @<Use the pattern area which is already set, if able@>;
434 @<Otherwise, create a new pattern area and use the new one@>;
437 @ @<Use the pattern area which is already set, if able@>= {
438 if(m->has_pattern_area) return m->pattern_area;
441 @ @<Otherwise, create a new pattern area and use the new one@>= {
442 data_index n=new_record(pattern_areas);
443 m->has_pattern_area=1;
444 pattern_areas.data[n].subroutine=none;
445 pattern_areas.data[n].next=none;
446 return n;
449 @ @<Display the list of patterns@>= {
450 int i;
451 foreach(i,pattern_areas) {
452 if(pattern_areas.data[i].text) {
453 printf("%d:%08X:%d:%d\n",i,pattern_areas.data[i].category
454 ,pattern_areas.data[i].subroutine,pattern_areas.data[i].next
456 display_string(pattern_areas.data[i].text);
457 printf("\n");
462 @*Keywords. Keywords means words which can be placed on the card and which
463 can have special meanings, and possibly reminder text.
465 Keywords are stored in a large list in only one keyword area. A category
466 can be given a name, which will automatically be assigned for the next bit
467 of the keyword category when it is entered the first time.
469 @<Typedefs@>=
470 typedef struct {
471 char*match; // match text (can contain pattern codes)
472 unsigned int category; // bitfield of categories
473 int extra1;
474 int extra2;
475 char*replacement; // replacement text or reminder text
476 } keyword_data;
478 @ @<Global variables@>=
479 unsigned int next_keyword_category=1;
480 memory_of(keyword_data) keywords;
482 @ @<Initialize memory@>= init_memory(keywords,4);
484 @ A keyword category is found (and created, if it is not found) using the
485 following code.
487 @-p unsigned int find_category(char*name) {
488 int i=find_name(name);
489 if(name_info(i).value.number) {
490 return name_info(i).value.number;
491 } @+else if(!name_info(i).value.is_string) {
492 name_info(i).value.number=next_keyword_category;
493 next_keyword_category<<=1;
494 if(!next_keyword_category)
495 fprintf(stderr,"Too many keyword categories: %s\n",name);
496 return name_info(i).value.number;
500 @ Some stack code commands are used when dealing with reading/writing
501 keyword info.
503 In order that you might be able to iterate them, it will exit out of the
504 current block when trying to read nonexisting keyword info instead of
505 displaying an error message.
507 @<Cases for system commands@>=
508 @-case 'k': {
509 // Read keyword info
510 if(registers['K'].number<0 || registers['K'].number>=keywords.used)
511 return 0;
512 push_num(keywords.data[registers['K'].number].extra1);
513 push_num(keywords.data[registers['K'].number].extra2);
514 push_string(keywords.data[registers['K'].number].replacement);
515 break;
517 @-case 'K': {
518 // Write keyword info
519 if(registers['K'].number<0 || registers['K'].number>=keywords.used)
520 program_error("Out of range");
521 free(keywords.data[registers['K'].number].replacement);
522 keywords.data[registers['K'].number].replacement=pop_string();
523 keywords.data[registers['K'].number].extra2=pop_num();
524 keywords.data[registers['K'].number].extra1=pop_num();
525 break;
528 @ @<Display the list of keywords@>= {
529 int i;
530 foreach(i,keywords) {
531 display_string(keywords.data[i].match);
532 printf(" [%d:%08X:%d:%d:%d]\n",i,keywords.data[i].category
533 ,keywords.data[i].extra1,keywords.data[i].extra2
534 ,strlen(keywords.data[i].replacement)
539 @*Card List. A sorted summary list of the cards is kept in one list,
540 having thirty-two general-purpose numeric fields, and a pointer to the
541 beginning of the record (usually the name in which it will be indexed by).
543 @<Typedefs@>=
544 typedef struct {
545 int token_ptr;
546 int field[32];
547 int amount_in_pack; // used in pack generation
548 } list_entry;
550 @ @<Global variables@>=
551 memory_of(list_entry) card_list;
553 @ @<Initialize memory@>= init_memory(card_list,16);
555 @*Deck Lists. Deck lists involve lists of cards or rules for cards that
556 belong to a deck or pack.
558 @^booster pack@>
560 There is one macro |lflag| here just to convert letters to bit flags. For
561 example |lflag('a')| is the least significant bit.
563 @d lflag(_ch) (1<<((_ch)-'a'))
565 @<Typedefs@>=
566 typedef struct {
567 int amount;
568 unsigned int flags;
569 char*name;
570 data_index next;
571 } deck_entry;
573 @ @<Global variables@>=
574 memory_of(deck_entry) deck_lists;
576 @ @<More elements of |name_data|@>=
577 boolean has_deck_list;
578 data_index deck_list;
580 @ @<Initialize memory@>= init_memory(deck_lists,4);
582 @ A new deck list is created with this. The deck entries are stored like a
583 linked list. The terminator has |next| pointing to |none|.
585 @-p data_index set_deck_list(int num) {
586 name_data*m=&name_info(num);
587 @<Use the deck list which is already set, if able@>;
588 @<Otherwise, create a new deck list and use the new one@>;
591 @ @<Use the deck list which is already set, if able@>= {
592 if(m->has_deck_list) return m->deck_list;
595 @ @<Otherwise, create a new deck list and use the new one@>= {
596 data_index n=new_record(deck_lists);
597 m->has_deck_list=1;
598 deck_lists.data[n].next=none;
599 return n;
602 @ @<Display the deck list@>= {
603 data_index i;
604 foreach(i,deck_lists) {
605 printf("%d ",i);
606 if(deck_lists.data[i].name) display_string(deck_lists.data[i].name);
607 else printf("-");
608 printf(" [%08X:%d:%d]\n",deck_lists.data[i].flags
609 ,deck_lists.data[i].amount,deck_lists.data[i].next);
613 @*Word Forms. These structures are used to store word form rules, such as
614 plurals\biblio{Conway, Damian. ``An Algorithmic Approach to English
615 Pluralization''. \hskip 0pt plus 1in\hbox{}
616 \.{http://www.csse.monash.edu.au/\~damian/papers/HTML/Plurals.html}}. You
617 can store up to four different kinds, in case of languages other than
618 English.
620 @^Conway, Damian@>
621 @^plurals@>
623 @<Typedefs@>=
624 typedef struct {
625 int level;
626 data_index next;
627 unsigned char orig[32];
628 unsigned char dest[32];
629 boolean left_boundary;
630 boolean right_boundary;
631 } word_form_entry;
633 @ @<Global variables@>=
634 memory_of(word_form_entry) word_forms;
636 @ @<Initialize memory@>= {
637 int i;
638 init_memory(word_forms,16);
639 word_forms.used=8;
640 for(i=0;i<8;i+=2) {
641 word_forms.data[i].orig[0]=word_forms.data[i].dest[0]=0;
642 word_forms.data[i].next=i+1;
643 word_forms.data[i].level=0x7FFFFFFF;
644 word_forms.data[i+1].orig[0]=word_forms.data[i+1].dest[0]=0;
645 word_forms.data[i+1].next=none;
646 word_forms.data[i+1].level=0;
650 @ Word form rules are added and then inserted in the correct place in the
651 linked list using the |next| field. Entries with a higher numbered level
652 take higher priority, therefore will be placed before the ones with lower
653 numbered level. Next, longer |orig| strings come before shorter strings,
654 since they might be more specific forms of the others and will therefore
655 override them.
657 @-p data_index add_word_form(int kind,int level,char*orig,char*dest) {
658 data_index n=new_record(word_forms);
659 @<Set the fields of the new word form rule@>;
660 @<Insert the new word form rule into the linked list@>;
661 return n;
664 @ The |left_boundary| and |right_boundary| fields specify if they should
665 match only at the boundary. Characters are checked using the \.W table and
666 removed from the string to place in the list.
668 @d last_character(_str) ((_str)[strlen(_str)-1])
670 @<Set the fields of the new word form rule@>= {
671 word_forms.data[n].level=level;
672 strcpy(word_forms.data[n].orig,orig+(tables['W'][*orig]==2));
673 word_forms.data[n].left_boundary=(tables['W'][*orig]==2);
674 if((word_forms.data[n].right_boundary=
675 (tables['W'][last_character(word_forms.data[n].orig)]==3)))
676 last_character(word_forms.data[n].orig)=0;
677 strcpy(word_forms.data[n].dest,dest+(tables['W'][*dest]==2));
678 if(tables['W'][last_character(word_forms.data[n].dest)]==3)
679 last_character(word_forms.data[n].dest)=0;
682 @ @<Insert the new word form rule into the linked list@>= {
683 data_index y=(kind&3)<<1; // previous item to |x|
684 data_index x=word_forms.data[y].next; // current item
685 int s=strlen(orig);
686 for(;x!=none;y=x,x=word_forms.data[y].next) {
687 if(word_forms.data[x].next==none) break;
688 @#if(word_forms.data[x].level<level) break;
689 if(word_forms.data[x].level>level) continue;
690 @#if(strlen(word_forms.data[x].orig)<s) break;
692 word_forms.data[y].next=n;
693 word_forms.data[n].next=x;
696 @ Now to do computation of changing a word by word forms. This function
697 expects only one word from input, or multiple words where the last one
698 should be the word to be converted. Uppercase letters are converted to
699 lowercase for conversion (but not the other way around), but if the
700 letters are uppercase in the input, the output will also have uppercase
701 letters on those positions. The algorithm starts from the right side of
702 the input string.
704 The parameter |src| is the input, and |dest| should point to a buffer
705 which is large enough to store the output string.
707 @^plurals@>
709 @-p data_index reform_word(int kind,char*src,char*dest) {
710 char*l=src+strlen(src);
711 data_index n=word_forms.data[(kind&3)<<1].next;
712 strcpy(dest,src); // this is used later
713 @<Try each word form rule, following the |next| pointers@>;
714 return none; // in case there is nothing to do
717 @ @<Try each word form rule, following the |next| pointers@>= {
718 char*p;
719 int s;
720 while(n!=none && word_forms.data[n].next!=none) {
721 s=strlen(word_forms.data[n].orig); @+ p=l-s;
722 @<Check the characters matching from |p|, going backwards@>;
723 n=word_forms.data[n].next;
727 @ Look ahead for the definition of |wcasecmp| (true means it matches).
729 @<Check the characters matching from |p|, going backwards@>= {
730 for(;;) {
731 if((!word_forms.data[n].left_boundary || p==src
732 || tables['W'][p[-1]])
733 && wcasecmp(word_forms.data[n].orig,p))
734 @<A match to the word form rules has been found@>;
735 @<Go backwards, stop if we are not allowed to continue backwards@>;
739 @ @<A match to the word form rules has been found@>= {
740 char*o=dest+(p-src);
741 sprintf(o,"%s%s",word_forms.data[n].dest,p+s);
742 @<Change the capitalization to match the original@>;
743 return n;
746 @ Remember, that for example if ``cow'' becomes ``kine'', then ``Cow''
747 will become ``Kine''. So, it will retain capitalization.
749 @^cows@>
751 @<Change the capitalization to match the original@>= {
752 char*q=word_forms.data[n].orig;
753 for(;*p && *q;p++,o++,q++)
754 if(*p==tables['U'][*q] && *p!=tables['L'][*q]) *o=tables['U'][*o];
757 @ @<Go backwards, stop if we are not allowed to continue backwards@>= {
758 if(word_forms.data[n].right_boundary) break; // matches only on boundary
759 if(tables['W'][p[s]]) break; // only the last word(s) can be matched
760 if(p--==src) break; // stop at beginning
763 @ This function is defined to compare strings in the way needed for
764 matching word forms, including case conversion. The lowercase letters in
765 the |shorter| string are permitted to match lowercase and uppercase
766 letters in the |longer| string, and the |shorter| string is permitted to
767 be shorter and still match.
769 @-p boolean wcasecmp(char*shorter,char*longer) {
770 for(;;shorter++,longer++) {
771 if(!*shorter) return 1;
772 if(!*longer) return 0;
773 if(*shorter!=*longer && *shorter!=tables['L'][*longer]) return 0;
777 @ Of course it is now needed a command that can access these features from
778 within a \TeX nicard template. The |level| of the matched rule is also
779 returned, in case your program might use that information for something.
781 @<Cases for system commands@>=
782 @-case 'W': {
783 // Convert a word form
784 int k=pop_num();
785 char*o=pop_string();
786 char q[1500];
787 data_index n=reform_word(k,o,q);
788 push_string(q);
789 if(n==none) push_num(0);
790 else push_num(word_forms.data[n].level);
791 free(o);
792 break;
795 @ @<Display the list of word form rules@>= {
796 data_index i;
797 foreach(i,word_forms) {
798 printf("%d %c\"",i,word_forms.data[i].left_boundary?'[':' ');
799 display_string(word_forms.data[i].orig);
800 printf("\"%c -> \"",word_forms.data[i].right_boundary?']':' ');
801 display_string(word_forms.data[i].dest);
802 printf("\" %d >%d\n",word_forms.data[i].level
803 ,word_forms.data[i].next);
807 @*Random Number Generation. This program uses the Xorshift algorithm,
808 invented by George Marsaglia\biblio{Marsaglia (July 2003). ``Xorshift
809 RNGs''. Journal of Statistical Software Vol.~8 (Issue 14). {\tt
810 http://www.jstatsoft.org/v08/i14/paper}.}.
812 @^Marsaglia, George@>
813 @^random numbers@>
815 @<Global variables@>=
816 unsigned int rng_x;
817 unsigned int rng_y;
818 unsigned int rng_z;
819 unsigned int rng_w;
821 @ @<Initialize the random number generator@>= {
822 @q[initialize the random seed::]@>
823 rng_seed((unsigned int)time(0));
824 @q[::initialize the random seed]@>
827 @ The seed parameters for the random number generator will be seeded using
828 the linear congruential generator, which is a simpler generator which can
829 be used to seed it with.
831 The parameters |lcg_a| and |lcg_c| are parameters to the linear
832 congruential generator algorithm. The values used here are the same as
833 those used in GNU C. In this program they will be specified explicitly so
834 that you can get identical output on different computers.
836 @d lcg_a 1103515245
837 @d lcg_c 12345
839 @-p void rng_seed(unsigned int x) {
840 rng_x=x=lcg_a*x+lcg_c;
841 rng_y=x=lcg_a*x+lcg_c;
842 rng_z=x=lcg_a*x+lcg_c;
843 rng_w=x=lcg_a*x+lcg_c;
846 @ There is a command to reseed it using a constant (so that you can
847 generate the same numbers on different computers).
849 @<Cases for system commands@>=
850 @-case 'U': {
851 // Reseed the random number generator
852 if(stack_ptr->is_string) program_error("Type mismatch");
853 rng_seed(pop_num());
854 break;
857 @ And now follows the algorithm for generating random numbers. One change
858 has been made so that once it is modulo, all number will still be of equal
859 probability.
861 Numbers are generated in the range from 0 up to but not including |limit|.
863 @d max_uint ((unsigned int)(-1))
865 @-p unsigned int gen_random(unsigned int limit) {
866 unsigned int r=max_uint-(max_uint%limit); // range check
867 for(;;) {
868 @<Make the next number |rng_w|...@>;
869 @<Check the range, try again if out of range, else |return|@>;
873 @ @<Make the next number |rng_w| by Xorshift algorithm@>= {
874 unsigned int t = rng_x ^ (rng_x << 11);
875 rng_x = rng_y; @+ rng_y = rng_z; @+ rng_z = rng_w;
876 rng_w ^= (rng_w >> 19) ^ t ^ (t >> 8);
879 @ @<Check the range, try again if out of range, else |return|@>= {
880 if(rng_w<=r) return rng_w%limit;
883 @ @<Cases for system commands@>=
884 @-case 'u': {
885 // Generate a random number
886 if(stack_ptr->is_string) program_error("Type mismatch");
887 stack_ptr->number=gen_random(stack_ptr->number);
888 break;
891 @*Stack Programming Language. Now we get to the part where the user can
892 enter a program, in order to control the features of this program. The
893 programming language used is like \.{dc}, but different.
895 @.dc@>
897 Subroutines are simply stored as strings in the |names| area, since they
898 are the same as registers.
900 @ Now we have the storage of registers. Registers 0 to 255 are stored in
901 this separate list, while other register values are just stored in the
902 |names| list. There is also a stack, which has storage of the same values
903 as registers can contain.
905 @d max_stack 0x1000
907 @<Typedefs@>=
908 typedef struct {
909 boolean is_string;
910 union @+{
911 int number;
912 unsigned char*text;
913 }@+;
914 } register_value;
916 @ @<More elements of |name_data|@>=
917 register_value value;
919 @ @<Global variables@>=
920 register_value registers[256];
921 register_value stack[max_stack];
922 register_value*stack_ptr=stack-1; // current top of stack element
924 @ Here are some codes for pushing and popping the stack.
926 @d pop_num() ((stack_ptr--)->number)
928 @-p inline void push_string(char*s) {
929 ++stack_ptr;
930 stack_ptr->is_string=1;
931 stack_ptr->text=strdup(s);
934 @ @-p inline void push_num(int n) {
935 ++stack_ptr;
936 stack_ptr->is_string=0;
937 stack_ptr->number=n;
940 @ @-p inline void stack_dup(void) {
941 if((stack_ptr[1].is_string=stack_ptr->is_string)) {
942 stack_ptr[1].text=strdup(stack_ptr->text);
943 } @+else {
944 stack_ptr[1].number=stack_ptr->number;
946 stack_ptr++;
949 @ @-p inline void stack_drop(void) {
950 if(stack_ptr->is_string) free(stack_ptr->text);
951 --stack_ptr;
954 @ @-p inline char*pop_string(void) {
955 char*p=stack_ptr->text;
956 stack_ptr->is_string=0; stack_ptr->text=0;
957 --stack_ptr;
958 return p;
961 @ Also, some subroutines are needed here in order to deal with registers.
963 For |fetch_code|, the string |"0[]+"| is returned if it is not a string,
964 generating a ``Type mismatch'' error when you try to run it.
966 @-p inline char*fetch_code(int r) {
967 if(!(r&~0xFF)) {
968 if(!registers[r].is_string) return "0[]+";
969 return registers[r].text;
970 } @+else {
971 if(!name_info(r).value.is_string) return "0[]+";
972 return name_info(r).value.text;
976 @ @-p inline void fetch(int r) {
977 register_value*v;
978 if(!(r&~0xFF)) v=&(registers[r]);
979 else v=&(name_info(r).value);
980 (++stack_ptr)->is_string=v->is_string;
981 if(v->is_string) {
982 stack_ptr->text=strdup(v->text);
983 } @+else {
984 stack_ptr->number=v->number;
988 @ @-p inline void store(int r) {
989 register_value*v;
990 if(!(r&~0xFF)) v=&(registers[r]);
991 else v=&(name_info(r).value);
992 if(v->is_string) free(v->text);
993 v->is_string=stack_ptr->is_string;
994 if(v->is_string) {
995 v->text=stack_ptr->text;
996 } @+else {
997 v->number=stack_ptr->number;
999 --stack_ptr;
1002 @ There is also a save stack. This save stack stores the saved values of
1003 the registers |'0'| to |'9'|, so that you can have local variables in a
1004 subroutine.
1006 @<Global variables@>=
1007 register_value save_stack[520];
1008 register_value*save_stack_ptr=save_stack;
1010 @ These codes deal with the save stack. Strings will be copied when
1011 saving. When loading, strings that were previously in the registers will
1012 be freed.
1014 @<Save local registers to the save stack@>= {
1015 int i;
1016 for(i='0';i<='9';i++) {
1017 *save_stack_ptr=registers[i];
1018 if(registers[i].is_string)
1019 save_stack_ptr->text=strdup(save_stack_ptr->text);
1020 save_stack_ptr++;
1024 @ @<Load local registers from the save stack@>= {
1025 int i;
1026 for(i='9';i>='0';i--) {
1027 if(registers[i].is_string) free(registers[i].text);
1028 registers[i]=*--save_stack_ptr;
1032 @*Commands for Stack Programming Language. Finally, is the code where it
1033 can be executed. The return value of this function indicates how many
1034 levels should be exit when it is called.
1036 @-p int execute_program(unsigned char*prog) {
1037 unsigned char*ptr=prog;
1038 reset_execute_program:
1039 for(;*ptr;ptr++) {
1040 switch(*ptr) {
1041 @<Cases for literal data commands@>@;
1042 @<Cases for stack manipulation commands@>@;
1043 @<Cases for arithmetic commands@>@;
1044 @<Cases for flow-control commands@>@;
1045 @<Cases for register/table operation commands@>@;
1046 @<Cases for string commands@>@;
1047 @<Cases for condition/compare commands@>@;
1048 @<Cases for local registers commands@>@;
1049 @<Cases for system commands@>@;
1050 @-case '?': @<Do a diagnostics command@>@;@+break;
1051 default:
1052 if(*ptr>='0' && *ptr<='9') {
1053 @<Read a literal number and push to stack@>;
1054 } @+else if(0x80&*ptr) {
1055 @<Execute a subroutine code from the current character@>;
1057 break;
1059 if(stack_ptr<stack-1) program_error("Stack underflow");
1060 if(stack_ptr>stack+max_stack) program_error("Stack overflow");
1062 return 0;
1065 @ @<Cases for literal data commands@>=
1066 @-case '`': {
1067 // Literal ASCII character
1068 push_num(*++ptr);
1069 break;
1071 @-case '[': {
1072 // Literal string
1073 @<Read a literal string and push to stack@>;
1074 break;
1076 @-case '(': {
1077 // Literal name
1078 @<Read a literal name and push its number to the stack@>;
1079 break;
1082 @ @<Read a literal number and push to stack@>= {
1083 int n=0;
1084 while(*ptr>='0' && *ptr<='9') n=10*n+(*ptr++)-'0';
1085 --ptr;
1086 push_num(n);
1089 @ @<Read a literal string and push to stack@>= {
1090 char*p=++ptr;
1091 int n=1;
1092 while(n && *ptr) {
1093 if(*ptr=='[') ++n;
1094 if(*ptr==']') --n;
1095 if(n) ptr++;
1097 if(!*ptr) program_error("Unterminated string literal");
1098 *ptr=0;
1099 push_string(p);
1100 *ptr=']';
1103 @ @<Read a literal name and push its number to the stack@>= {
1104 char*p=++ptr;
1105 while(*ptr && *ptr!=')') ptr++;
1106 if(!*ptr) program_error("Unterminated string literal");
1107 *ptr=0;
1108 push_num(find_name(p));
1109 *ptr=')';
1112 @ @<Cases for stack manipulation commands@>=
1113 @-case 'D': {
1114 // Drop top item of stack
1115 stack_drop();
1116 break;
1118 @-case 'c': {
1119 // Clears the stack, rendering it empty
1120 while(stack_ptr>=stack) stack_drop();
1121 break;
1123 @-case 'd': {
1124 // Duplicates the value on top of the stack.
1125 stack_dup();
1126 break;
1128 @-case 'r': {
1129 // Swaps the top two values on the stack
1130 stack_ptr[1]=stack_ptr[0];
1131 stack_ptr[0]=stack_ptr[-1];
1132 stack_ptr[-1]=stack_ptr[1];
1133 break;
1136 @ @<Cases for arithmetic commands@>=
1137 @-case '+': {
1138 // Add two numbers, or concatenate two strings
1139 if(stack_ptr->is_string) {
1140 @<Concatenate strings on the stack@>;
1141 }@+ else {
1142 int n=pop_num();
1143 if(stack_ptr->is_string)
1144 program_error("Type mismatch");
1145 stack_ptr->number+=n;
1147 break;
1149 @-case '-': {
1150 // Subtract two numbers, or compare two strings
1151 if(stack_ptr->is_string) {
1152 @<Compare strings on the stack@>;
1153 }@+ else {
1154 int n=pop_num();
1155 if(stack_ptr->is_string)
1156 program_error("Type mismatch");
1157 stack_ptr->number-=n;
1159 break;
1161 @-case '*': {
1162 // Multiply two numbers
1163 int n=pop_num();
1164 if(stack_ptr[0].is_string || stack_ptr[1].is_string)
1165 program_error("Number expected");
1166 stack_ptr->number*=n;
1167 break;
1169 @-case '/': {
1170 // Divide two numbers
1171 int n=pop_num();
1172 if(stack_ptr[0].is_string || stack_ptr[1].is_string)
1173 program_error("Number expected");
1174 if(n==0) program_error("Division by zero");
1175 stack_ptr->number/=n;
1176 break;
1178 @-case '%': {
1179 // Modulo of two numbers
1180 int n=pop_num();
1181 if(stack_ptr[0].is_string || stack_ptr[1].is_string)
1182 program_error("Number expected");
1183 if(n==0) program_error("Division by zero");
1184 stack_ptr->number%=n;
1185 break;
1188 @ @<Concatenate strings on the stack@>= {
1189 char*s=pop_string();
1190 char*q;
1191 if(!stack_ptr->is_string) program_error("Type mismatch");
1192 q=malloc(strlen(s)+strlen(stack_ptr->text)+1);
1193 strcpy(q,stack_ptr->text);
1194 strcpy(q+strlen(q),s);
1195 stack_drop();
1196 push_string(q);
1197 free(q);
1198 free(s);
1201 @ @<Compare strings on the stack@>= {
1202 char*s=pop_string();
1203 char*q=pop_string();
1204 push_num(strcmp(q,s));
1205 free(q);
1206 free(s);
1209 @ @<Cases for flow-control commands@>=
1210 @-case 'Q': {
1211 // Exit from multiple levels
1212 int q=pop_num();
1213 if(q>0) return q-1;
1214 break;
1216 @-case 'Y': {
1217 // Go back to beginning
1218 ptr=prog-1;
1219 break;
1221 @-case 'q': {
1222 // Exit from two levels
1223 return 1;
1224 break;
1226 @-case 'x': {
1227 // Execute code from top of stack
1228 @<Execute a string or subroutine code from top of stack@>;
1229 break;
1232 @ Note here, it is a recursive function call.
1233 @^recursive@>
1235 @<Execute a string or subroutine code from top of stack@>= {
1236 if(stack_ptr->is_string) {
1237 char*p=pop_string();
1238 int q=execute_program(p);
1239 free(p);
1240 if(q) return q-1;
1241 } @+else {
1242 char*p=fetch_code(pop_num());
1243 int q=execute_program(p);
1244 if(q) return q-1;
1248 @ Since the extended characters (|0x80| to |0xFF|) do not correspond to
1249 any commands, here we can use them to execute a subroutine code, allowing
1250 many things related to self-modifying code (and other stuff) to be done
1251 that would be difficult otherwise.
1253 @<Execute a subroutine code from the current character@>= {
1254 char*p=fetch_code(*ptr);
1255 int q=execute_program(p);
1256 if(q) return q-1;
1259 @ @<Cases for register/table operation commands@>=
1260 @-case ':': {
1261 // Store value to table
1262 int n;
1263 if(stack_ptr->is_string) program_error("Number expected");
1264 n=pop_num();
1265 tables[0x7F&*++ptr][n]=pop_num();
1266 break;
1268 @-case ';': {
1269 // Load value from table
1270 stack_ptr->number=tables[0x7F&*++ptr][stack_ptr->number];
1271 break;
1273 @-case 'L': {
1274 // Load value from register named by stack
1275 if(stack_ptr->is_string) program_error("Number expected");
1276 fetch(pop_num());
1277 break;
1279 @-case 'S': {
1280 // Store value in register named by stack
1281 if(stack_ptr->is_string) program_error("Number expected");
1282 store(pop_num());
1283 break;
1285 @-case 'l': {
1286 // Load value from register
1287 fetch(*++ptr);
1288 break;
1290 @-case 's': {
1291 // Store value in register
1292 store(*++ptr);
1293 break;
1296 @ @<Cases for string commands@>=
1297 @-case 'B': {
1298 // Put brackets around a string, or convert number to text
1299 if(stack_ptr->is_string) {
1300 @<Put brackets around string at top of stack@>;
1301 } @+else {
1302 @<Convert top of stack to string representation of a number@>;
1304 break;
1306 @-case 'Z': {
1307 // Calculate number of characters in a string
1308 char*s=pop_string();
1309 push_num(strlen(s));
1310 free(s);
1311 break;
1313 @-case 'a': {
1314 // ``ASCIIfy'' a number
1315 if(stack_ptr->is_string) {
1316 if(stack_ptr->text[0]) stack_ptr->text[1]=0;
1317 } @+else {
1318 int n=stack_ptr->number;
1319 stack_ptr->is_string=1;
1320 stack_ptr->text=malloc(2);
1321 stack_ptr->text[0]=n;
1322 stack_ptr->text[1]=0;
1324 break;
1326 @-case 'A': {
1327 // Take the first character from the string
1328 char*s=stack_ptr->text;
1329 if(!stack_ptr->is_string || !*s) return 0;
1330 push_num(*s);
1331 stack_ptr[-1].text=strdup(s+1);
1332 free(s);
1333 break;
1335 @-case 'N': {
1336 // Convert a register number to its name
1337 int n=stack_ptr->number;
1338 if(stack_ptr->is_string) program_error("Type mismatch");
1339 if(n<256 || n>=names.used+256) program_error("Out of range");
1340 stack_drop();
1341 push_string(names.data[n-256].name);
1342 break;
1345 @ @<Put brackets around string at top of stack@>= {
1346 char*buf=malloc(strlen(stack_ptr->text)+3);
1347 sprintf(buf,"[%s]",stack_ptr->text);
1348 free(stack_ptr->text);
1349 stack_ptr->text=buf;
1352 @ @<Convert top of stack to string representation of a number@>= {
1353 char buf[32];
1354 sprintf(buf,"%d",stack_ptr->number);
1355 stack_drop();
1356 push_string(buf);
1359 @ Here is how the ``Arithmetic IF'' command works: On the stack you have
1360 any three values at the top, and a number underneath it. Those are all
1361 removed, except one of the three values which is selected based on the
1362 sign of the number (the condition value).
1364 @<Cases for condition/compare commands@>=
1365 @-case 'i': {
1366 // Arithmetic IF
1367 @<Do the ``Arithmetic IF''@>;
1368 break;
1370 @-case '&': {
1371 // Bitwise AND
1372 int n=pop_num();
1373 if(stack_ptr[0].is_string || stack_ptr[1].is_string)
1374 program_error("Number expected");
1375 stack_ptr->number&=n;
1376 break;
1379 @ Do you like this algorithm? Is this a real question?
1381 @^strange codes@>
1383 @<Do the ``Arithmetic IF''@>= {
1384 register_value v=stack_ptr[-3];
1385 int n=v.number;
1386 n=-(n<0?2:!n);
1387 stack_ptr[-3]=stack_ptr[n];
1388 stack_ptr[n]=v;
1389 stack_drop();@+stack_drop();@+stack_drop();
1392 @ @<Cases for local registers commands@>=
1393 @-case '<': {
1394 // Save locals
1395 @<Save local registers to the save stack@>;
1396 break;
1398 @-case '>': {
1399 // Restore locals
1400 @<Load local registers from the save stack@>;
1401 break;
1404 @ When there is a program error (such as stack underflow), the following
1405 subroutine is used to handle it.
1407 @d program_error(_text) program_error_(prog,ptr,_text)
1409 @-p void program_error_(char*prog,char*ptr,char*msg) {
1410 fprintf(stderr,"Error in %s on line %d",current_filename,current_line);
1411 fprintf(stderr,"\n! %s\ns%dS%dp%d near \"",msg,stack_ptr-stack,
1412 save_stack_ptr-save_stack,ptr-prog);
1413 @<Display the codes near the part that caused the error@>;
1414 fprintf(stderr,"\"\n");
1415 exit(1);
1418 @ @<Display the codes near the part that caused the error@>= {
1419 char buf[32];
1420 char*p=ptr-5;
1421 int i;
1422 if(p<prog || p>ptr) p=prog;
1423 for(i=0;p+i<=ptr && p[i];i++) buf[i]=p[i];
1424 buf[i]=0;
1425 fprintf(stderr,"%s",buf);
1428 @*Tables and registers. The tables must be stored here. There are 128
1429 tables with 256 entries each, each of which can store one byte of data.
1430 These tables are used for converting uppercase/lowercase, for deciding
1431 which characters need to be escaped in \TeX, and so on.
1433 The purposes of the built-in registers are also described in this chapter.
1434 The tables and registers named by uppercase letters are for system use.
1435 The tables and registers named by lowercase can be used by the user.
1437 @<Global variables@>=
1438 unsigned char tables[128][256];
1440 @ Here are the uses of the built-in tables and registers:
1441 @^built-in registers@>
1442 @^built-in tables@>
1444 Register \.A: The current position in the current cards area.
1446 Register \.C: The current cards area.
1448 Register \.D: Dots per inch, multiplied by 100.
1450 Register \.E: The escape character for \TeX. If this is a string, the
1451 entire string is the prefix; otherwise, it is a ASCII number of the
1452 character to be used.
1454 Register \.K: Index number for last keyword entry added. Also used when
1455 dealing with keyword operation commands, and when a keyword is matched in
1456 a pattern.
1458 Register \.P: The current pattern area.
1460 Register \.Q: The parameters for the ImageMagick command-line, separated
1461 by spaces.
1463 Register \.T: Alignment tab character for \TeX. Same considerations apply
1464 as the \.E register.
1466 Register \.U: A code to execute for a deck specification enrty with \.x
1467 flag set.
1469 Register \.V: The version number of this program.
1471 Register \.W: A code which pushes the whatsit replacements onto the stack.
1472 It is initialized to a blank string before each line in a card area. It
1473 should push the replacements in the reverse order of the whatsits, so you
1474 could use a code like this, for example: \.{[(Abracadabra)]lW+sW}
1476 Register \.X: Horizontal coordinate across the page (in pixels).
1478 Register \.Y: Vertical coordinate across the page (in pixels).
1480 Register \.Z: Should be set to a code to execute after doing everything
1481 else (but before writing output files).
1483 Table \.E: Indicates which characters need escaped for \TeX.
1485 Table \.G: Table containing information for sorting and grouping.
1487 Table \.L: Conversion to lowercase.
1489 Table \.S: Information for natural sorting.
1491 Table \.U: Conversion to uppercase.
1493 Table \.W: Table for word form rules. Zero means a letter, one means a
1494 word separator, two means use to mark beginning of a word, three means use
1495 to mark the end of a word. In this program, it is advantageous to use the
1496 fact that zero means word characters (such as letters), and nonzero means
1497 nonword characters.
1499 @d init_register(_reg,_val) do@+{
1500 registers[_reg].is_string=0;
1501 registers[_reg].number=(_val);
1502 }@+while(0)@;
1504 @d init_register_str(_reg,_val) do@+{
1505 registers[_reg].is_string=1;
1506 registers[_reg].text=strdup(_val);
1507 }@+while(0)@;
1509 @<Initialize the tables and registers@>= {
1510 int i;
1511 for(i=0;i<256;i++) init_register(i,0);
1512 init_register('E','\\');
1513 init_register('V',version_number);
1514 @<Initialize table of alphabetical case conversion@>;
1517 @ @<Initialize table of alphabetical case conversion@>= {
1518 for(i=0;i<256;i++) tables['L'][i]=tables['U'][i]=i;
1519 for(i='A';i<='Z';i++) {
1520 tables['L'][i]=i+'a'-'A';
1521 tables['U'][i+'a'-'A']=i;
1525 @ @<Display the contents of table |*++ptr|@>= {
1526 int t=*++ptr;
1527 int i;
1528 for(i=0;i<256;i++) {
1529 printf("%c%c",tables[t][i]?'+':'.',@|
1530 (tables[t][i]<0x7F && tables[t][i]>=' ')?tables[t][i]:'.'
1532 if((i&0x0F)==0x0F) printf("\n");
1534 for(i=' ';i<0x7F;i++) if(tables[t][i]) printf("%c",i);
1537 @*Diagnostics. Here is diagnostics commands. These are used to display the
1538 internal information on standard output, so that you can check how these
1539 things are working. (You can also use \.{gdb} for debugging purposes.) A
1540 diagnostics command always starts with a question mark, and is then
1541 followed by one more character indicating the type of diagnostics
1542 requestsed. (Some are followed by an additional character after that.)
1544 @<Do a diagnostics command@>= {
1545 switch(*++ptr) {
1546 case 'c': @<Display the sorted card list@>; @+break;
1547 case 'd': @<Display the deck list@>; @+break;
1548 case 'f': @<Display font information@>; @+break;
1549 case 'k': @<Display the list of keywords@>; @+break;
1550 case 'n': @<Display the list of names@>; @+break;
1551 case 'p': @<Display the list of patterns@>; @+break;
1552 case 's': @<Display the contents of the stack@>; @+break;
1553 case 't': @<Display the contents of table |*++ptr|@>; @+break;
1554 case 'w': @<Display the list of word form rules@>; @+break;
1555 case 'x': @<Display the list of typeset nodes@>; @+break;
1556 case 'y': @<Display typesetting diagnostics@>; @+break;
1557 default: program_error("Unknown type of diagnostics");
1561 @ One subroutine is used here for displaying strings with escaped, so that
1562 it will display on a terminal without messing it up or omitting the
1563 display of some characters.
1565 @-p void display_string(char*s) {
1566 for(;*s;s++) {
1567 if(*s<' ' || *s==0x7F) {
1568 printf("^%c",0x40^*s);
1569 } @+else {
1570 printf("%c",*s);
1575 @ @<Display the contents of the stack@>= {
1576 register_value*p;
1577 for(p=stack;p<=stack_ptr;p++) {
1578 if(p->is_string) {
1579 printf("[");
1580 display_string(p->text);
1581 printf("]\n");
1582 } @+else {
1583 printf("%d\n",p->number);
1588 @ More of the diagnostics functions are included in the chapters for the
1589 data structures which it is displaying.
1591 @*Pattern Matching. Now, finally, after the chapter about patterns, and
1592 going through many other things in between, comes to the chapter in which
1593 patterns are actually being matched.
1595 One structure is used here for the information about how to match it, and
1596 what has been matched from it. The parameter |num_capture| is how many
1597 captured parts there are, and the |start| and |end| arrays store the index
1598 into the |src| string of where the matches are. The entire matched part is
1599 indicated by |start[0]| and |end[0]| (note always |start[0]==0|).
1601 @<Typedefs@>=
1602 typedef struct {
1603 char*src;
1604 char*truesrc; // used for checking true beginning of the line
1605 char*pattern;
1606 unsigned int category;
1607 int start[16];
1608 int end[16];
1609 int num_capture;
1610 } match_info;
1612 @ This first one just matches one pattern against a string to see if it
1613 matches. It returns true if it does match. (It is somewhat inefficient.)
1615 @-p boolean match_pattern(match_info*mat) {
1616 char*src; // current start of source string
1617 char*ptr; // pointer into source string |src|
1618 char*pptr; // pointer into pattern string
1619 src=mat->src; @+ mat->num_capture=0; @+ pptr=mat->pattern; @+ ptr=src;
1620 @<Execute the pattern on the string |src|@>;
1621 mismatch: return 0;
1624 @ This loop executes each command in the pattern in attempt to match each
1625 character. In case of mismatch, it will break out of this loop, and
1626 continue with the next iteration of the loop in the previous section.
1628 @d not_a_marker !(pptr[-1]&0x80)
1630 @<Execute the pattern on the string |src|@>= {
1631 while(*pptr) {
1632 switch(*pptr++) {
1633 case begin_capture:
1634 mat->start[++mat->num_capture]=ptr-mat->src; @+break;
1635 case end_capture: mat->end[mat->num_capture]=ptr-mat->src; @+break;
1636 case match_keyword: @<Do |match_keyword|@>; @+break;
1637 case match_table:
1638 if(!tables[*pptr++][*ptr++]) goto mismatch; @+break;
1639 case optional_table: ptr+=!!tables[*pptr++][*ptr]; @+break;
1640 case failed_match: goto mismatch;
1641 case jump_table:
1642 if(!(pptr=strchr(mat->pattern,0x80|tables[*pptr++][*ptr++])))
1643 goto mismatch;
1644 @+break;
1645 case successful_match: @<Do |successful_match|@>;
1646 case back_one_space: if(ptr--==mat->src) goto mismatch; @+break;
1647 case forward_one_space: if(!*ptr++) goto mismatch; @+break;
1648 case match_left_side: if(ptr!=mat->truesrc) goto mismatch; @+break;
1649 case match_right_side: if(*ptr>=' ') goto mismatch; @+break;
1650 default: if(not_a_marker && pptr[-1]!=*ptr++) goto mismatch;
1655 @ @<Do |successful_match|@>= {
1656 mat->start[0]=0;
1657 mat->end[0]=ptr-mat->src;
1658 return 1;
1661 @ And now, the next part matches from an area and changes the string in
1662 place, possibly by reallocating it. The |src| pointer passed to this
1663 function should be one that can be freed!
1665 @-p char*do_patterns(char*src,int area) {
1666 pattern_data*pat;
1667 match_info mat;
1668 int index=0; // index into |src| string
1669 @<Cancel if there isn't a pattern area@>;
1670 continue_matching:
1671 if(index>=strlen(src)) return src;
1672 pat=pattern_areas.data+name_info(area).pattern_area;
1673 for(;;) {
1674 @<Fill up the |mat| structure for testing the current pattern@>;
1675 if(mat.pattern && match_pattern(&mat)) {
1676 @<Push the captured strings to the stack@>;
1677 @<Call the subroutine associated with this pattern@>;
1678 if(stack_ptr->is_string) {
1679 @<Replace the matched part from the stack and fix the |index|@>;
1680 } @+else {
1681 index+=mat.end[0];
1683 stack_drop();
1684 goto continue_matching;
1686 @<Select the next pattern in this area or |break|@>;
1688 index++; @+ goto continue_matching;
1691 @ @<Cancel if there isn't a pattern area@>= {
1692 if(area<256) return src;
1693 if(!name_info(area).has_pattern_area) return src;
1696 @ @<Fill up the |mat| structure for testing the current pattern@>= {
1697 mat.src=src+index;
1698 mat.truesrc=src;
1699 mat.pattern=pat->text;
1700 mat.category=pat->category;
1703 @ @<Push the captured strings to the stack@>= {
1704 int i;
1705 for(i=mat.num_capture;i;i--) {
1706 push_string(src+index+mat.start[i]);
1707 stack_ptr->text[mat.end[i]-mat.start[i]]=0;
1711 @ @<Call the subroutine associated with this pattern@>= {
1712 execute_program(names.data[pat->subroutine].value.text);
1715 @ The memory allocated is probably more than is needed, but this way is
1716 simpler. It is always sufficient amount, though. Think about it.
1718 @^thought@>
1720 @<Replace the matched part from the stack and fix the |index|@>= {
1721 char*q=malloc(strlen(src)+strlen(stack_ptr->text)+1);
1722 strcpy(q,src);
1723 sprintf(q+index,"%s%s",stack_ptr->text,src+index+mat.end[0]);
1724 free(src);
1725 src=q;
1726 index+=strlen(stack_ptr->text);
1729 @ @<Select the next pattern in this area or |break|@>= {
1730 if(pat->next==none) break;
1731 pat=pattern_areas.data+pat->next;
1734 @ Finally, there is a command |'M'| to do a pattern matching and
1735 replacement with a string, inside of a stack subroutine code.
1737 @<Cases for system commands@>=
1738 @-case 'M': {
1739 // do pattern matching and replacement
1740 int n=pop_num();
1741 if(!stack_ptr->is_string) program_error("Type mismatch");
1742 stack_ptr->text=do_patterns(stack_ptr->text,n);
1743 break;
1746 @*Matching Keywords. Codes for matching keywords have been placed in
1747 another chapter, instead of making the previous chapter longer.
1749 So now we can see how it is matched keywords in a pattern code.
1751 @<Do |match_keyword|@>= {
1752 match_info m;
1753 char mstr[512];
1754 char t=*pptr++; // indicate which table to use
1755 data_index best=none;
1756 int best_length=-1;
1757 @<Try matching each keyword belonging to the category@>;
1758 if(best==none) goto mismatch;
1759 @<Adjust the \.K register for this keyword match@>;
1760 ptr+=m.end[0];
1763 @ @<Adjust the \.K register for this keyword match@>= {
1764 if(registers['K'].is_string) free(registers['K'].text);
1765 registers['K'].is_string=0;
1766 registers['K'].number=best;
1769 @ When matching keywords, all of them will be tried, in case there are
1770 better candidates for the search (bigger is better (so, for example,
1771 |"Power of One"| will override |"Power"|); failing that, later ones are
1772 better than earlier ones (so that user files can override keywords in
1773 template files)).
1775 @^Courtenay, Bryce@>
1776 @^Houghton, Israel@>
1777 @^Luce, Ron@>
1779 @<Try matching each keyword belonging to the category@>= {
1780 data_index i;
1781 foreach(i,keywords) {
1782 if(keywords.data[i].category&mat->category &&
1783 strlen(keywords.data[i].match)>=best_length) {
1784 @<Set up the |match_info| structure called |m|@>;
1785 @<Attempt applying this keyword match@>;
1790 @ @<Set up the |match_info| structure called |m|@>= {
1791 sprintf(mstr,"%s%c%c%c",
1792 keywords.data[i].match,match_table,t,successful_match);
1793 m.src=m.truesrc=ptr;
1794 m.pattern=mstr;
1797 @ @<Attempt applying this keyword match@>= {
1798 if(match_pattern(&m)) {
1799 best=i;
1800 best_length=strlen(keywords.data[i].match);
1804 @*Sorting and Grouping. The card lists can be sorted/grouped using these
1805 commands, which are generally used by macros that create the records for
1806 the cards in the card areas.
1808 @<Cases for system commands@>=
1809 @-case 'n': {
1810 // Add a new list entry
1811 data_index n=new_record(card_list);
1812 card_list.data[n].token_ptr=
1813 card_areas.data[set_card_area(registers['C'].number)].used
1815 break;
1817 @-case 'f': {
1818 // Set a field value of the list entry
1819 data_index n=card_list.used-1;
1820 int x=pop_num();
1821 int y=pop_num();
1822 if(n==none) program_error("No card list is available");
1823 card_list.data[n].field[x&31]=y;
1824 break;
1827 @ Other than the commands to make the list entries above, there must be,
1828 of course, the actual sorting and grouping being done!
1830 Sorting and grouping are controlled by the \.G table. Starting from a
1831 given offset (added), you use thirty-two entries for the thirty-two
1832 fields.
1834 @<Cases for system commands@>=
1835 @-case 'G': {
1836 // Sort the list
1837 sorting_table_offset=pop_num();
1838 qsort(card_list.data,card_list.used,sizeof(list_entry),list_compare);
1839 @<Mark positions in the sorted list@>;
1840 break;
1843 @ @<Global variables@>=
1844 int sorting_table_offset;
1846 @ This is the compare function for the list sorting. It is also worth to
1847 notice here what values belong in the \.G table. (There are also some
1848 other values, which are described a bit later.)
1850 @d no_sort 0
1851 @d primary_ascending 'A'
1852 @d primary_descending 'Z'
1853 @d primary_name 'N'
1854 @d secondary_ascending 'a'
1855 @d secondary_descending 'z'
1856 @d secondary_name 'n'
1857 @d record_sorted_position 'R'
1858 @d reset_high_bits 'q'
1860 @d G_table(_field) (tables['G'][((sorting_table_offset+(_field))&0xFF)])
1861 @d p1s ((list_entry*)p1)
1862 @d p2s ((list_entry*)p2)
1864 @-p int list_compare(const void*p1,const void*p2) {
1865 @<Compare using fields indicated by \.G table@>;
1866 @<Compare using the card's name and the \.S table@>;
1867 @<Compare using the order in which the cards are typed in@>;
1868 return 0; // This can't, but will, happen.
1871 @ @<Compare using fields indicated by \.G table@>= {
1872 int i;
1873 for(i=0;i<32;i++) if(p1s->field[i]!=p2s->field[i]) {
1874 if(G_table(i)==primary_ascending || (G_table(i)&0x80)) {
1875 return (p1s->field[i]>p2s->field[i])?1:-1;
1876 } @+else if(G_table(i)==primary_descending) {
1877 return (p1s->field[i]<p2s->field[i])?1:-1;
1878 } @+else if(G_table(i)==primary_name) {
1879 return name_compare(p1s->field[i],p2s->field[i]);
1882 for(i=0;i<32;i++) if(p1s->field[i]!=p2s->field[i]) {
1883 if(G_table(i)==secondary_ascending) {
1884 return (p1s->field[i]>p2s->field[i])?1:-1;
1885 } @+else if(G_table(i)==secondary_descending) {
1886 return (p1s->field[i]<p2s->field[i])?1:-1;
1887 } @+else if(G_table(i)==secondary_name) {
1888 return name_compare(p1s->field[i],p2s->field[i]);
1893 @ When all else fails, \strike{play dead} use the order in which the cards
1894 have been typed in. This is how it is made stable, and that you can get
1895 the same results on any computer.
1897 @^Smith, Steve@>
1899 @<Compare using the order in which the cards...@>= {
1900 if(p1s->token_ptr>p2s->token_ptr) return 1;
1901 if(p1s->token_ptr<p2s->token_ptr) return -1;
1904 @ The last thing to do after sorting, is mark positions in the list if it
1905 is requested to do so.
1907 In addition, it shall also optionally mark high bits (30 to 27) of some
1908 fields, based on when other fields change. This helps with doing multi-%
1909 dimensional statistics. The fields that it is based on will automatically
1910 be primary sorted since such sorting is required for the marking algorithm
1911 to work properly.
1913 @<Mark positions in the sorted list@>= {
1914 data_index i;
1915 int j;
1916 for(j=0;j<32;j++) {
1917 if(G_table(j)==record_sorted_position) {
1918 foreach(i,card_list) card_list.data[i].field[j]=i;
1919 } @+else if(G_table(j)&0x80) {
1920 @<Mark high bits of fields to prepare for...@>;
1921 } @+else if(G_table(j)==reset_high_bits) {
1922 foreach(i,card_list) card_list.data[i].field[j]&=0x0FFFFFFF;
1927 @ The rule is that whenever the current field's value changes, the bit in
1928 the corresponding grouping field will be flipped. Since the statistics
1929 grouping always treats consecutive equal values in the grouping field as
1930 belonging to the same group, this is a way to insert ``group breaks'' into
1931 the list.
1933 @<Mark high bits of fields to prepare for complex statistics@>= {
1934 int f=G_table(j)&0x1F; // other field number
1935 int v=card_list.data[0].field[j]; // previous value
1936 int k=1<<(27+((G_table(j)&0x60)>>5)); // bit flip value
1937 int b=0; // current bit value
1938 foreach(i,card_list) {
1939 if(v!=card_list.data[i].field[j]) b^=k;
1940 card_list.data[i].field[f]&=~k;
1941 card_list.data[i].field[f]|=b;
1942 v=card_list.data[i].field[j];
1946 @ @<Display the sorted card list@>= {
1947 data_index i;
1948 int j;
1949 foreach(i,card_list) {
1950 printf("%d=[ ",card_list.data[i].token_ptr);
1951 for(j=0;j<32;j++) printf("%d ",card_list.data[i].field[j]);
1952 printf("]\n");
1956 @*Natural Sorting. A natural compare algorithm is used here. It is a
1957 generalization of Martin Pool's algorithm\biblio{Pool, Martin. ``Natural
1958 Order String Comparison''. {\tt
1959 http://sourcefrog.net/projects/natsort/}.}.
1961 The \.S table maps from character tokens to the sorting specifications.
1962 Name tokens are converted to |whatsit| when looking up in this table.
1964 Tokens are grouped into digits, letters, and priority letters. There are
1965 also some extras, such as spaces and radix point. A string of consecutive
1966 digits is treated as numeric, so a number with more digits comes after a
1967 number with less digits.
1969 Priority letters are used mainly for sorting roman numerals. Two or more
1970 consecutive priority letters are considered as a group, otherwise they are
1971 treated in the same way as ordinary letters. A group is ranked with the
1972 letters latest in the alphabet, so for example, if |'I'| and |'X'| are
1973 priority, then |"IX"| is placed between |"W"| and |"X"|. This way, all
1974 roman numerals from I to XXXIX will be sorted correctly.
1976 @^natural compare@>
1977 @^Pool, Martin@>
1979 @d nat_end_low 0
1980 @d nat_end_high 1
1981 @d nat_space 2
1982 @d nat_ignore 3
1983 @d nat_radix_point 4
1985 @d nat_digit_zero 64 // digits go up to 127
1986 @d nat_first_letter 128 // letters go up to 191
1987 @d nat_first_priority_letter 192 // priority letters go up to 255
1988 @d nat_high_value 256
1990 @<Compare using the card's name and the \.S table@>= {
1991 token*pa=card_areas.data[set_card_area(registers['C'].number)].tokens
1992 +p1s->token_ptr;
1993 token*pb=card_areas.data[set_card_area(registers['C'].number)].tokens
1994 +p2s->token_ptr;
1995 boolean fractional=0; // Are we reading digits after a radix point?
1996 int a,b,c;
1997 for(;;pa++,pb++) {
1998 begin_natural_compare_loop: @/
1999 a=tables['S'][*pa>=256?whatsit:*pa];
2000 @+ b=tables['S'][*pb>=256?whatsit:*pb];
2001 @<Skip over leading spaces and/or zeros@>;
2002 @<Process a run of digits@>;
2003 @<Check if the end of either string is reached@>;
2004 @<Check for a radix point@>;
2005 @<Process priority letters@>;
2006 @<Check if the current positions of each string sufficiently differ@>;
2010 @ @<Skip over leading spaces and/or zeros@>= {
2011 while(a==nat_space||a==nat_ignore||(!fractional&&a==nat_digit_zero)) {
2012 int aa=tables['S'][pa[1]>=256?whatsit:pa[1]];
2013 if(a!=nat_ignore) fractional=0;
2014 if(!fractional && a==nat_digit_zero
2015 && aa>=nat_digit_zero && aa<nat_first_letter) break;
2016 pa++; @+ a=tables['S'][*pa>=256?whatsit:*pa];
2018 while(b==nat_space||b==nat_ignore||(!fractional&&b==nat_digit_zero)) {
2019 int bb=tables['S'][pa[1]>=256?whatsit:pa[1]];
2020 if(b!=nat_ignore) fractional=0;
2021 if(!fractional && b==nat_digit_zero
2022 && bb>=nat_digit_zero && bb<nat_first_letter) break;
2023 pb++; @+ b=tables['S'][*pb>=256?whatsit:*pb];
2027 @ @<Process a run of digits@>= {
2028 if(a>=nat_digit_zero&&a<nat_first_letter&&
2029 b>=nat_digit_zero&&b<nat_first_letter) {
2030 if((c=(fractional?compare_left:compare_right)(pa,pb))) return c;
2031 @<Skip the run of digits, since they are the same@>;
2032 fractional=0;
2035 @^strange codes@>
2037 @ Compare two left-aligned numbers: the first to have a different value
2038 wins. This function and |compare_right| are basically equivalent, there
2039 are only a few differences (this one is the simpler one).
2041 @-p int compare_left(token*pa,token*pb) {
2042 int a,b;
2043 for(;;pa++,pb++) {
2044 a=tables['S'][*pa>=256?whatsit:*pa];
2045 @+ b=tables['S'][*pb>=256?whatsit:*pb];
2046 @<Skip over ignored characters@>;
2047 @<If neither |a| nor |b| is digit, |break|@>;
2048 @<If one is a digit and the other isn't, the longest run wins@>;
2049 @<If both are different digits, the greater one wins@>;
2051 return 0;
2054 @ The longest run of digits wins. That aside, the greatest value wins, but
2055 we can't know that it will until we've scanned both numbers to know they
2056 have the same magnitude, so we remember it in |bias|.
2058 @-p int compare_right(token*pa,token*pb) {
2059 int a,b;
2060 int bias=0;
2061 for(;;pa++,pb++) {
2062 a=tables['S'][*pa>=256?whatsit:*pa];
2063 @+ b=tables['S'][*pb>=256?whatsit:*pb];
2064 @<Skip over ignored characters@>;
2065 @<If neither |a| nor |b| is digit, |break|@>;
2066 @<If one is a digit and the other isn't, the longest run wins@>;
2067 @<If both are digits, set the |bias|@>;
2069 return bias;
2072 @ Ignored characters might be commas for grouping digits into thousands.
2074 @<Skip over ignored characters@>= {
2075 while(a==nat_ignore) {
2076 pa++; @+ a=tables['S'][*pa>=256?whatsit:*pa];
2078 while(b==nat_ignore) {
2079 pb++; @+ b=tables['S'][*pb>=256?whatsit:*pb];
2083 @ @<If neither |a| nor |b| is digit, |break|@>= {
2084 if(!(a>=nat_digit_zero&&a<nat_first_letter)&&
2085 !(b>=nat_digit_zero&&b<nat_first_letter)) break;
2088 @ @<If one is a digit and the other isn't, the longest run wins@>= {
2089 if(!(a>=nat_digit_zero&&a<nat_first_letter)) return -1;
2090 if(!(b>=nat_digit_zero&&b<nat_first_letter)) return 1;
2093 @ @<If both are different digits, the greater one wins@>= {
2094 if(a!=b) return a-b;
2097 @ @<If both are digits, set the |bias|@>= {
2098 if(a!=b && !bias) bias=(a<b)?-1:1;
2101 @ @<Skip the run of digits, since they are the same@>= {
2102 while(a>=nat_digit_zero&&a<nat_first_letter) {
2103 pa++; @+ pb++; @+ a=tables['S'][*pa>=256?whatsit:*pa];
2105 b=tables['S'][*pb>=256?whatsit:*pb];
2108 @ @<Check if the end of either string is reached@>= {
2109 if(a==nat_end_low && b>nat_end_high) return -1;
2110 if(b==nat_end_low && a>nat_end_high) return 1;
2111 if(a==nat_end_high && b>nat_end_high) return 1;
2112 if(b==nat_end_high && a>nat_end_high) return -1;
2113 if(a<=nat_end_high && b<=nat_end_high) break; // tied
2116 @ A radix point must be followed by a digit, otherwise it is considered to
2117 be punctuation (and ignored). Radix points come before digits in the
2118 sorting order (|".5"| comes before |"5"|).
2120 @<Check for a radix point@>= {
2121 if(a==nat_radix_point && b==nat_radix_point) {
2122 int aa=tables['S'][pa[1]>=256?whatsit:pa[1]];
2123 int bb=tables['S'][pb[1]>=256?whatsit:pb[1]];
2124 if(aa>=nat_digit_zero&&aa<nat_first_letter
2125 &&bb>=nat_digit_zero&&bb<nat_first_letter) fractional=1;
2126 } @+else if(a==nat_radix_point) {
2127 int aa=tables['S'][pa[1]>=256?whatsit:pa[1]];
2128 if(!(aa>=nat_digit_zero&&aa<nat_first_letter)) {
2129 pa++; goto begin_natural_compare_loop;
2131 } @+else if(b==nat_radix_point) {
2132 int bb=tables['S'][pb[1]>=256?whatsit:pb[1]];
2133 if(!(bb>=nat_digit_zero&&bb<nat_first_letter)) {
2134 pb++; goto begin_natural_compare_loop;
2139 @ This is used so that |"IX"| can be sorted between |"VIII"| and |"X"|. In
2140 normal alphabetical order, |"IX"| sorts before |"V"|. This algorithm makes
2141 it so that doesn't happen. For example: |a| is |'I'| and |aa| (the
2142 character after |a| in the text) is |'X'| (the check |aa>a| ensures that
2143 it too is priority, in addition to checking that |a| represents a negative
2144 part of a roman number), and |b| is |'V'|. Now, since |'V'| comes between
2145 |'I'| and |'X'| in the alphabetical order, the condition is checked to be
2146 valid and it overrides the later check.
2148 @<Process priority letters@>= {
2149 if(a>=nat_first_priority_letter) {
2150 int aa=tables['S'][pa[1]>=256?whatsit:pa[1]];
2151 if(aa>a && b>=nat_first_letter && (b&63)>(a&63) && (b&63)<(aa&63))
2152 return 1;
2154 if(b>=nat_first_priority_letter) {
2155 int bb=tables['S'][pb[1]>=256?whatsit:pb[1]];
2156 if(bb>b && a>=nat_first_letter && (a&63)>(b&63) && (a&63)<(bb&63))
2157 return -1;
2161 @ At this point, |a| and |b| will both be |@[@]>=nat_radix_point|. Numbers
2162 always come after letters (this rule is designed so that when a radix
2163 point is found after a number, it will make a larger number; otherwise it
2164 will be followed by a letter and therefore the one followed by the letter
2165 is lesser since it has no fractional part to make it greater).
2167 @<Check if the current positions of each string suffic...@>= {
2168 if(a>=nat_first_priority_letter) a-=64;
2169 if(b>=nat_first_priority_letter) b-=64;
2170 if(a<nat_first_letter) a+=128;
2171 if(b<nat_first_letter) b+=128;
2172 if(a!=b) return (a<b)?-1:1;
2175 @*Name Sorting. This kind of sorting is used when items are grouped
2176 together by some extra field in the statistics, such as creature types in
2177 Magic: the Gathering.
2179 It works in a similar way to the natural sorting algorithm, but this time
2180 it is simpler and not as many things need to be checked. Digits and
2181 priority letters are treated as normal letters, and the types |nat_space|,
2182 |nat_ignore|, and |nat_radix_point| are all ignored. In addition, a null
2183 terminator is always treated as |nat_end_low|.
2185 If both names compare the same, their number is used instead, in order to
2186 force sorting stability.
2188 @-p int name_compare(int n1,int n2) {
2189 char*s1=name_info(n1).name;
2190 char*s2=name_info(n2).name;
2191 int a,b;
2192 for(;*s1 || *s2;s1++,s2++) {
2193 a=(*s1)?tables['S'][*s1]:nat_end_low;
2194 b=(*s2)?tables['S'][*s2]:nat_end_low;
2195 @<Skip over spaces and ignored characters@>;
2196 @<Check if the end of either string is reached@>;
2197 @<Check if the current positions of...@>;
2199 return (n1<n2)?-1:1;
2202 @ @<Skip over spaces and ignored characters@>= {
2203 while(a<nat_digit_zero) {
2204 s1++; @+ a=(*s1)?tables['S'][*s1]:nat_end_low;
2206 while(b<nat_digit_zero) {
2207 s2++; @+ b=(*s2)?tables['S'][*s2]:nat_end_low;
2211 @*Statistics. After the card lists are created and sorted and grouped, it
2212 can make statistics from them. It can be just a plain list, or it can be
2213 in summary of groups, measuring count, minimum, maximum, mean, median, and
2214 so on.
2216 First we do the simple iteration.
2218 @^mean@>
2219 @^median@>
2220 @^groups@>
2221 @^minimum@>
2222 @^maximum@>
2224 @<Cases for system commands@>=
2225 @-case 'V': {
2226 // Iterate the card list
2227 data_index i;
2228 char*q=pop_string();
2229 if(!stack_ptr[1].is_string) program_error("Type mismatch");
2230 foreach(i,card_list) {
2231 push_num(card_list.data[i].token_ptr);
2232 store('A');
2233 execute_program(q);
2235 free(q);
2236 break;
2238 @-case 'v': {
2239 // Read a field from the card list
2240 int x=pop_num()&31;
2241 int y=0;
2242 data_index i;
2243 foreach(i,card_list) {
2244 if(registers['A'].number==card_list.data[i].token_ptr)
2245 y=card_list.data[i].field[x];
2247 push_num(y);
2248 break;
2251 @ That was simple, see? Now to do gathering statistics of summary of
2252 groups, which is a bit more complicated. The list is expected to be sorted
2253 by the group field primary, and the statistics field ascending as
2254 secondary, in order to make the correct calculation of the fields.
2256 However, it will not do the sorting automatically, since there are some
2257 reasons why you might want it to work differently. One thing you can do is
2258 to sort the group field {\sl secondary} and some other more major group as
2259 primary, in order to do two-dimensional statistics, and this will work as
2260 long as you do not require the minimum, maximum, or median.
2262 @<Cases for system commands@>=
2263 @-case 'g': {
2264 // Gather statistics of groups
2265 data_index i,si=0;
2266 int x=pop_num()&31; // field for grouping
2267 int y=pop_num()&31; // field to measure statistics with
2268 int sum1,sum2; // running totals of $s_1$ and $s_2$
2269 sum1=sum2=0;
2270 char*q=pop_string(); // code to execute for each group
2271 if(!stack_ptr[1].is_string) program_error("Type mismatch");
2272 foreach(i,card_list) {
2273 if(card_list.data[i].field[x]!=card_list.data[si].field[x]) {
2274 @<Send the results of the current group@>;
2275 sum1=sum2=0; @+ si=i;
2277 @<Add to the running totals@>;
2279 @<Send the results of the current group@>;
2280 free(q);
2281 break;
2284 @ Running totals are kept for two quantities called $s_1$ and $s_2$. There
2285 is also $s_0$, but that can be calculated easily using subtraction, so
2286 there is no need to keep a running total. If the sample values are denoted
2287 $x_k$, the following equation represents the running totals:
2288 $$s_j=\sum_{k=1}^N{x_k^j}$$ (note that $s_0=N$.)
2290 @^mathematics@>
2292 @<Add to the running totals@>= {
2293 sum1+=card_list.data[i].field[y];
2294 sum2+=card_list.data[i].field[y]*card_list.data[i].field[y];
2297 @ Now we will send the results and call |q|. The results are sent to the
2298 stack in the following order: $s_0$, $s_1$, $s_2$, $Q_0$, $2Q_2$, $Q_4$
2299 (where $Q_0$ is the minimum, $Q_2$ the median, and $Q_4$ the maximum).
2301 From these results, it is then possible to calculate the standard
2302 deviation: $$\sigma={1\over s_0}\sqrt{s_0s_2-s_1^2}$$ and
2303 $$s=\sqrt{s_0s_2-s_1^2\over s_0(s_0-1)}.$$
2305 @^mathematics@>
2307 @<Send the results of the current group@>= {
2308 push_num(i-si); // $s_0$
2309 push_num(sum1); // $s_1$
2310 push_num(sum2); // $s_2$
2311 push_num(card_list.data[si].field[y]); // $Q_0$
2312 push_num(
2313 card_list.data[(si+i)/2].field[y]+card_list.data[(si+i+1)/2].field[y]
2314 ); // $2Q_2$
2315 push_num(card_list.data[i-1].field[y]); // $Q_4$
2316 @# push_num(card_list.data[si].token_ptr); @+ store('A');
2317 execute_program(q);
2320 @*Random Pack Generation. Now the codes so that it can create random packs
2321 (such as booster packs) by using the card lists and deck lists.
2323 A command |'P'| is used for evaluation of a deck list. It expects the deck
2324 list number and the code to execute for each card on the list.
2326 @^booster pack@>
2328 @<Cases for system commands@>=
2329 @-case 'P': {
2330 // Generate a random pack or deck
2331 data_index s=set_deck_list(pop_num());
2332 data_index n; // current deck list entry
2333 if(stack_ptr[1].is_string) program_error("Number expected");
2334 @<Figure out what cards belong in the pack@>;
2335 @<Execute the code on the stack for each card in the pack@>;
2336 break;
2339 @ @<Figure out what cards belong in the pack@>= {
2340 deck_entry*e;
2341 int tries=1000; // How many times can you retry if it fails?
2342 figure_out_again:
2343 if(!--tries) program_error("No cards matched the deck criteria");
2344 n=s;
2345 @<Reset |amount_in_pack| of each card to zero@>;
2346 while(n!=none && (n=(e=deck_lists.data+n)->next)!=none)
2347 @<Process this deck entry@>;
2350 @ @<Reset |amount_in_pack| of each card to zero@>= {
2351 data_index i;
2352 foreach(i,card_list) card_list.data[i].amount_in_pack=0;
2355 @ The deck entry must be processed according to the flags. Here is a list
2356 of flags:
2358 \.a: Use all cards that meet the criteria, instead of only one. If this is
2359 the case, it is possible to use negative weights to remove cards from the
2360 pack. Also, it cannot fail.
2361 [Combine with \.{x}]
2363 \.k: Select without replacement. It is fail if the total weight is not
2364 enough. There are two ways in which this differs from \.u (below). One is
2365 that the previous lines in the deck list are not used. The other one is
2366 that if the weight is more than one, there will be more than one ball for
2367 that card, therefore the same card can be picked up multiple times.
2368 [Combine with \.{sux}]
2370 \.n: Use the |amount| as a probability. If |amount<=100| then the
2371 probability is |amount/100| otherwise it is |100/amount|. This is a
2372 probability of using the |name| to select another deck list instead of
2373 this one.
2374 [Combine with nothing]
2376 \.s: Skip the next line if this line does not fail. (Normally, if one line
2377 fails, everything does, and you have to try again.)
2378 [Combine with \.{kux}]
2380 \.u: Require unique selection. It is fail if the card is already in this
2381 pack.
2382 [Combine with \.{ksx}]
2384 \.x: Pass the |name| as a string to the code in the \.U register, and then
2385 use the resulting code as the code to determine weights instead of using
2386 the code in the register named by |name| directly. Now you can type things
2387 such as |"12x Forest"| into your deck list.
2388 [Combine with \.{aksu}]
2390 @<Process this deck entry@>= {
2391 if(e->flags&lflag('n')) {
2392 @<Determine whether or not to skip to another deck list@>;
2393 } @+else {
2394 char*c; // code for weights of each card
2395 int total; // total weight of cards
2396 data_index*bag=malloc(sizeof(data_index));
2397 @<Get the code |c| for the weights of each card@>;
2398 @<Calculate the weights of each card@>;
2399 if(!(e->flags&lflag('a')))
2400 @<Select some of the cards at random and add them to the pack@>;
2401 if(e->flags&lflag('x')) free(c);
2402 free(bag);
2406 @ @<Determine whether or not to skip to another deck list@>= {
2407 boolean q;
2408 if(e->amount<=100) {
2409 q=(gen_random(100)<e->amount);
2410 } @+else {
2411 q=(100<gen_random(e->amount));
2413 if(q) n=set_deck_list(find_name(e->name));
2416 @ @<Get the code |c| for the weights of each card@>= {
2417 if(e->flags&lflag('x')) {
2418 execute_program(registers['U'].text);
2419 if(stack_ptr->is_string) {
2420 c=pop_string();
2421 } @+else {
2422 program_error("Type mismatch");
2424 } @+else {
2425 int n=find_name(e->name);
2426 if(name_info(n).value.is_string) {
2427 c=name_info(n).value.text;
2428 } @+else {
2429 program_error("Type mismatch");
2434 @ @<Calculate the weights of each card@>= {
2435 data_index i;
2436 foreach(i,card_list) {
2437 registers['A'].number=card_list.data[i].token_ptr;
2438 execute_program(c);
2439 if(stack_ptr->number) {
2440 if(e->flags&lflag('a')) {
2441 card_list.data[i].amount_in_pack+=e->amount*stack_ptr->number;
2442 } @+else if(stack_ptr->number>0) {
2443 @<Add the cards to the |bag|@>;
2446 stack_drop();
2450 @ The |bag| is like, you put the balls in the bag so that you can mix it
2451 and take one out, whatever number is on the ball is the card you put into
2452 the pack. Except, that there is no balls and no bag.
2454 There is one ball per point of weight.
2456 @^balls@>
2458 @<Add the cards to the |bag|@>= {
2459 int j=stack_ptr->number;
2460 bag=realloc(bag,(total+j)*sizeof(data_index));
2461 while(j--) bag[total+j]=i;
2462 total+=stack_ptr->number;
2465 @ If it is not a line which adds all possibilities at once, then the cards
2466 must be selected from the |bag| at random to bag them. In some cases it
2467 will fail.
2469 @<Select some of the cards at random and add them to the pack@>= {
2470 data_index r;
2471 int amount=e->amount;
2472 bag_next:
2473 if(!total) @<Deal with bag failure@>;
2474 r=gen_random(total);
2475 if((e->flags&lflag('u')) && card_list.data[bag[r]].amount_in_pack) {
2476 bag[r]=bag[--total];
2477 goto bag_next;
2479 card_list.data[bag[r]].amount_in_pack++;
2480 if(e->flags&lflag('k')) bag[r]=bag[--total];
2481 if(amount--) goto bag_next;
2482 @#if(e->flags&lflag('s')) n=deck_lists.data[n].next;
2483 bag_done: ;
2486 @ @<Deal with bag failure@>= {
2487 if(e->flags&lflag('s')) goto bag_done;
2488 else goto figure_out_again;
2491 @ Now it must do stuff using the list which is generated. The quantity for
2492 how many of that card is pushed on the stack, and this is done even for
2493 cards with negative quantity (but not for zero quantity).
2495 @<Execute the code on the stack for each card in the pack@>= {
2496 data_index i;
2497 char*q=pop_string();
2498 if(!stack_ptr[1].is_string) program_error("Type mismatch");
2499 foreach(i,card_list) {
2500 if(card_list.data[i].amount_in_pack) {
2501 push_num(card_list.data[i].amount_in_pack);
2502 execute_program(q);
2505 free(q);
2508 @*Reading Input Files. Now it is time for the part of the program where
2509 input files are read and processed. The areas of the file (and other
2510 special commands) are indicated using \.@@ signs.
2512 At first we have state information. Each state is labeled by uppercase
2513 letters, or by digits 1 to 9. The high bit is set for the heading state,
2514 meaning the first line that contains the name and/or other heading
2515 information.
2517 @d null_state 0
2518 @d card_state 'C'
2519 @d deck_state 'D'
2520 @d execute_state 'E'
2521 @d file_state 'F'
2522 @d include_state 'I'
2523 @d keyword_state 'K'
2524 @d pattern_state 'P'
2525 @d subroutine_state 'S'
2526 @d wordforms_state 'W'
2527 @d heading 0x80
2529 @<Global variables@>=
2530 int cur_state;
2531 data_index cur_name;
2532 data_index cur_data;
2533 boolean omit_line_break;
2535 @ The next thing that must be kept track of for input files is the stack
2536 of open input files.
2538 @d max_pathname_length 128
2539 @d max_filename_length 128
2540 @d max_input_stack 128
2541 @d max_line_length 256
2543 @<Typedefs@>=
2544 typedef struct {
2545 FILE*fp; // zero for terminal input
2546 char name[max_filename_length+1];
2547 int line;
2548 } input_file_data;
2550 @ @<Global variables@>=
2551 input_file_data input_files[max_input_stack];
2552 input_file_data*current_input_file=input_files;
2553 char input_buffer[max_line_length];
2555 @ Some macros are useful to access the current file data.
2557 @d current_line (current_input_file->line)
2558 @d current_filename (current_input_file->name)
2559 @d current_fp (current_input_file->fp)
2561 @d parsing_error(_text) fprintf(stderr,"%s on line %d in %s\n",
2562 _text,current_line,current_filename)@;
2564 @ There is also conditional processing directives, which uses a single
2565 variable to keep track of the level. If it is greater than zero, the
2566 condition is false, and it is increased for nesting conditions (the
2567 nested conditions have no truth to them).
2569 @<Global variables@>=
2570 int condition_level=0;
2572 @ This subroutine inputs the next line. True is returned if there is a
2573 line, or false if it is finished.
2575 It is necessary to check for end of file and if so, close that file and
2576 try the one it was included from; and if it is terminal input, display the
2577 current state when prompting input from the user.
2579 @-p boolean input_line(void) {
2580 input_line_again: if(current_fp) {
2581 @<Get a line of input from the file@>;
2582 } @+else {
2583 @<Get a line of terminal input@>;
2585 @<Remove trailing |'\n'|, |'\r'|, and spaces@>;
2586 ++current_line;
2587 return 1;
2590 @ @<Get a line of input from the file@>= {
2591 if(!fgets(input_buffer,max_line_length,current_fp)) {
2592 memusage_log("Closing input file",current_input_file-input_files)@;
2593 fclose(current_fp);
2594 if(current_input_file>input_files) {
2595 --current_input_file;
2596 goto input_line_again;
2597 } @+else {
2598 return 0;
2603 @ @<Get a line of terminal input@>= {
2604 printf("\n%c> ",cur_state?cur_state:'>');
2605 fflush(stdout);
2606 if(!fgets(input_buffer,max_line_length,stdin)) return 0;
2609 @ This function is used in order to open another input file. One way is
2610 that the file might be included from another file, or it might be the
2611 main file.
2613 @-p void open_input(char*name) {
2614 if(++current_input_file>input_files+max_input_stack) {
2615 fprintf(stderr,"Too many simultaneous input files\n");
2616 exit(1);
2618 memusage_log("Opening input file",current_input_file-input_files)@;
2619 strcpy(current_filename,name);
2620 current_line=0;
2621 current_fp=fopen(name,"r");
2622 if(!current_fp) {
2623 fprintf(stderr,"Cannot open input file: %s\n",name);
2624 exit(1);
2628 @ Trailing newlines and spaces are removed. On some computers, there will
2629 be a carriage return before the line feed, it should be removed, so that
2630 the same file will work on other computers, too.
2632 @d last_character_input input_buffer[strlen(input_buffer)-1]
2634 @<Remove trailing |'\n'|, |'\r'|, and spaces@>= {
2635 if(last_character_input=='\n') last_character_input=0;
2636 if(last_character_input=='\r') last_character_input=0;
2637 while(last_character_input==' ') last_character_input=0;
2640 @ The input states start at these values.
2642 @<Initialize the input states@>= {
2643 cur_state=execute_state;
2644 cur_name=cur_data=0;
2647 @ Now it is the time to do the actual processing according to the contents
2648 of the lines of the file. A line starting with \.@@ sign will indicate a
2649 special command (to operate in all modes) or a mode switch command.
2651 @d delete_chars(_buf,_c) memmove((_buf),(_buf)+(_c),strlen((_buf)+(_c))+1)
2653 @<Process the input files@>= {
2654 char*buf;
2655 while(input_line()) {
2656 buf=input_buffer;
2657 if(condition_level) {
2658 buf+=strspn(buf," ");
2659 condition_level+=!strcmp(buf,"@@<");
2660 condition_level-=!strcmp(buf,"@@>");
2661 } @+else {
2662 omit_line_break=1;
2663 @<Convert \.@@ commands in the |input_buffer|@>;
2664 omit_line_break=0;
2665 process_line(buf);
2670 @ @<Convert \.@@ commands in the |input_buffer|@>= {
2671 char*ptr=input_buffer;
2672 while(*ptr) {
2673 if(*ptr=='@@') {
2674 @<Convert the current \.@@ command@>;
2675 } @+else {
2676 ptr++;
2681 @ @<Convert the current \.@@ command@>= {
2682 switch(*++ptr) {
2683 case '@@': @/
2684 delete_chars(ptr,1);
2685 break;
2686 case '.': @<Process \.{@@.} command@>;@+break;
2687 case '&': @<Process \.{@@\&} command@>;@+break;
2688 case '^': @<Process \.{@@\^} command@>;@+break;
2689 case '(': @<Process \.{@@(} command@>;@+break;
2690 case '<': @<Process \.{@@<} command@>;@+break;
2691 case '>': @<Remove this command from the input@>;@+break;
2692 default: @/
2693 if((*ptr>='A' && *ptr<='Z') || (*ptr>='0' && *ptr<='9')) {
2694 @<Enter a |heading| state@>;
2695 } @+else {
2696 parsing_error("Unknown @@ command");
2701 @ @<Remove this command from the input@>= {
2702 ptr--;
2703 delete_chars(ptr,2);
2706 @ Heading states are used for the first line of a section in the file.
2707 After that line is processed, it becomes the corresponding non-heading
2708 state |(cur_state&~heading)|.
2710 Note: The state |'0'| is deliberately unused; you might use it for
2711 documentation areas, for example.
2713 @^documentation areas@>
2715 @<Enter a |heading| state@>= {
2716 cur_state=heading|*ptr--;
2717 delete_chars(ptr,2);
2718 while(*ptr==' ' || *ptr=='\t') delete_chars(ptr,1);
2721 @ @-p void process_line(char*buf) {
2722 int q=cur_state;
2723 cur_state&=~heading;
2724 switch(q) {
2725 case card_state: @<Process card state@>;@+break;
2726 case deck_state: @<Process deck state@>;@+break;
2727 case execute_state: @<Process execute state@>;@+break;
2728 case file_state: @<Process file state@>;@+break;
2729 case keyword_state: @<Process keyword state@>;@+break;
2730 case pattern_state: @<Process pattern state@>;@+break;
2731 case subroutine_state: @<Process subroutine state@>;@+break;
2732 case wordforms_state: @<Process word forms state@>;@+break;
2733 case card_state|heading: @<Process card heading@>;@+break;
2734 case deck_state|heading: @<Process deck heading@>;@+break;
2735 case file_state|heading: @<Process file heading@>;@+break;
2736 case include_state|heading: @<Process include heading@>;@+break;
2737 case keyword_state|heading: @<Process keyword heading@>;@+break;
2738 case pattern_state|heading: @<Process pattern heading@>;@+break;
2739 case subroutine_state|heading: @<Process subroutine heading@>;@+break;
2740 default: ; // nothing happens
2744 @ Sometimes you might want a macro which can send a line programmatically.
2745 So, here is the way that it is done.
2747 @<Cases for system commands@>=
2748 @-case 'X': {
2749 // Process a line by programming
2750 int n;
2751 if(stack_ptr->is_string) program_error("Type mismatch");
2752 n=pop_num();
2753 if(n) cur_state=n|heading;
2754 if(!stack_ptr->is_string) program_error("Type mismatch");
2755 omit_line_break=1;
2756 process_line(stack_ptr->text);
2757 stack_drop();
2758 break;
2761 @*Inner Commands. These are commands other than the section headings.
2763 @ The first command to deal with is simple--it is a comment. The rest of
2764 the current line is simply discarded.
2766 @<Process \.{@@.} command@>= {
2767 ptr[-1]=0;
2770 @ This command is a pattern split. It means it will process the part of
2771 the line before this command and then process the stuff after it. The
2772 variable |omit_line_break| is 1 if this command is used; because it means
2773 there will not be a line break. (Otherwise, patterns and so on are split
2774 at line breaks.)
2776 @<Process \.{@@\&} command@>= {
2777 ptr[-1]=0;
2778 process_line(buf);
2779 buf=++ptr;
2782 @ This allows control characters to be inserted into the input. This code
2783 takes advantage of the way the ASCII code works, in which stripping all
2784 but the five low bits can convert a letter (uppercase or lowercase) to its
2785 corresponding control character.
2787 @^control character@>
2789 @<Process \.{@@\^} command@>= {
2790 ptr[1]&=0x1F;
2791 --ptr;
2792 delete_chars(ptr,2);
2795 @ The following command is used to execute a code in a different state and
2796 then include the results in this area.
2798 @<Process \.{@@(} command@>= {
2799 char*p;
2800 char*q;
2801 @<Skip over the name and save the rest of the line@>;
2802 @<Execute the code for the named subroutine@>;
2803 @<Insert the returned string and fix the line buffer@>;
2806 @ @<Skip over the name and save the rest of the line@>= {
2807 p=ptr+1;
2808 while(*ptr && *ptr!=')') ptr++;
2809 q=strdup(ptr+!!*ptr);
2810 *ptr=0;
2813 @ @<Execute the code for the named subroutine@>= {
2814 int n=find_name(p);
2815 execute_program(fetch_code(n));
2818 @ @<Insert the returned string and fix the line buffer@>= {
2819 char*s=pop_string();
2820 sprintf(p-2,"%s%s",s,q);
2821 ptr=p+strlen(s)-2;
2822 free(s);
2823 free(q);
2826 @ This command is used for conditional processing. The condition value
2827 comes from the stack. Zero is false, everything else is true.
2829 @<Process \.{@@<} command@>= {
2830 --ptr;
2831 delete_chars(ptr,2);
2832 condition_level=!stack_ptr->number;
2833 stack_drop();
2836 @*Card State. Cards are added to the card areas by using the card state.
2837 The \.C register tells which is the current card area, and \.P register is
2838 used to select the current pattern area. The pattern area is used to match
2839 patterns after reading a line. Please note that it won't work to change
2840 the value of the \.C register during the card state.
2842 @<Process card heading@>= {
2843 int n=find_name(buf);
2844 cur_data=set_card_area(n);
2845 cur_name=n-256;
2846 push_num(n);@+store('C');
2849 @ @<Process card state@>= {
2850 char*b;
2851 if(!omit_line_break) strcpy(buf+strlen(buf),"\n");
2852 @<Initialize the \.W register@>;
2853 b=do_patterns(strdup(buf),registers['P'].number);
2854 if(registers['W'].is_string) execute_program(registers['W'].text);
2855 @<Send the tokens of |b| and replace whatsits@>;
2856 free(b);
2859 @ @<Initialize the \.W register@>= {
2860 if(registers['W'].is_string) free(registers['W'].text);
2861 registers['W'].is_string=1;
2862 registers['W'].text=strdup("");
2865 @ @<Send the tokens of |b| and replace whatsits@>= {
2866 char*p;
2867 for(p=b;*p;p++) {
2868 if(*p==whatsit) {
2869 send_token(cur_data,pop_num());
2870 } @+else {
2871 send_token(cur_data,(*p==1)?0:*p);
2876 @ There is one command you might want to send tokens in any other time.
2878 @<Cases for system commands@>=
2879 @-case 'T': {
2880 // Add tokens to the card area
2881 if(stack_ptr->is_string) {
2882 @<Send tokens from the string on the stack@>;
2883 } @+else {
2884 send_token(set_card_area(registers['C'].number),stack_ptr->number);
2886 stack_drop();
2887 break;
2890 @ @<Send tokens from the string on the stack@>= {
2891 char*p;
2892 data_index q=set_card_area(registers['C'].number);
2893 for(p=stack_ptr->text;*p;p++) send_token(q,*p);
2896 @*Deck State. Deck state is used for creating deck lists and random packs.
2898 @<Process deck heading@>= {
2899 cur_name=find_name(buf)-256;
2900 cur_data=set_deck_list(cur_name+256);
2901 @<Skip to the end of the deck list@>;
2904 @ @<Skip to the end of the deck list@>= {
2905 while(deck_lists.data[cur_data].next!=none)
2906 cur_data=deck_lists.data[cur_data].next;
2909 @ Now to parse each line in turn. Each line consists of a number, the
2910 flags, and a text.
2912 @<Process deck state@>= {
2913 int n=strtol(buf,&buf,10);
2914 unsigned int f=0;
2915 if(n) {
2916 buf+=strspn(buf,"\x20\t");
2917 @<Read the flags for the deck list@>;
2918 buf+=strspn(buf,"\x20\t"); // Now we are at the point of the text
2919 @<Send this line to the deck list@>;
2920 @<Create and advance to the new terminator of the deck list@>;
2924 @ @<Read the flags for the deck list@>= {
2925 while(*buf>='a' && *buf<='z') f |=lflag(*buf++);
2926 buf++; // Skip terminator of flags
2929 @ If the \.x flag is set, it will be determined what to do with the text
2930 by the user-defined code. Otherwise, it is always a name, so we can save
2931 memory by pointing to the name buffer (since name buffers never vary).
2933 @<Send this line to the deck list@>= {
2934 if(f&lflag('x')) {
2935 deck_lists.data[cur_data].name=strdup(buf);
2936 } @+else {
2937 deck_lists.data[cur_data].name=name_info(find_name(buf)).name;
2941 @ @<Create and advance to the new terminator of the deck list@>= {
2942 data_index i=new_record(deck_lists);
2943 deck_lists.data[cur_data].next=i;
2944 deck_lists.data[cur_data=i].next=none;
2947 @*Execute State. This state is simple, just execute stack codes. It is the
2948 initial state; you can use it with terminal input, too.
2950 @<Process execute state@>= {
2951 execute_program(buf);
2954 @*File State. This state is used to make list of output files. Each one is
2955 stored as a string, like subroutine state. The difference is that newlines
2956 will not be discarded. The other difference is that there is a flag in the
2957 |name_data| record for it that tells it that it is a file that should be
2958 sent to output.
2960 @<More elements of |name_data|@>=
2961 boolean is_output_file;
2963 @ @<Process file heading@>= {
2964 cur_name=find_name(buf)-256;
2965 if(!names.data[cur_name].value.is_string) {
2966 names.data[cur_name].value.is_string=1;
2967 names.data[cur_name].value.text=strdup("");
2968 names.data[cur_name].is_output_file=1;
2972 @ @<Process file state@>= {
2973 int z=strlen(names.data[cur_name].value.text);
2974 if(!omit_line_break) strcpy(buf+strlen(buf),"\n");
2975 names.data[cur_name].value.text=realloc(names.data[cur_name].value.text,
2976 z+strlen(buf)+1);
2977 strcpy(names.data[cur_name].value.text+z,buf);
2980 @*Include State. The include state causes inclusion of another source file
2981 from this one.
2983 @<Process include heading@>= {
2984 cur_state=execute_state;
2985 @<Push the include file onto the input stack@>;
2986 @<Attempt to open the include file...@>;
2989 @ @<Push the include file onto the input stack@>= {
2990 ++current_input_file;
2991 memusage_log("Opening input file",current_input_file-input_files)@;
2992 strcpy(current_filename,buf);
2993 current_line=0;
2994 current_fp=0;
2997 @ Include files are searched using the search path specified in the
2998 environment variable called \.{TEXNICARDPATH}, which is a list of paths
2999 delimited by colons on UNIX systems (including Cygwin), but semicolons on
3000 Windows (colons are used in Windows for drive letters). A forward slash is
3001 the path separator. Please note that if you want to use include files in
3002 the current directory, you must include |"."| as the first entry in the
3003 search path!!
3005 @^search path@>
3006 @.TEXNICARDPATH@>
3007 @^Windows@>
3009 @<Set |includepath_separator| depending on operating system@>=
3010 #ifdef WIN32
3011 #define @!includepath_separator ';'
3012 #else
3013 #define includepath_separator ':'
3014 #endif
3016 @ @<Attempt to open the include file by finding it in the search path@>= {
3017 char searchpath[max_pathname_length+max_filename_length+1];
3018 char*cpath;
3019 char*npath=getenv("TEXNICARDPATH");
3020 strcpy(searchpath,npath?npath:".");
3021 npath=cpath=searchpath;
3022 @<Set |includepath_separator| depending on operating system@>;
3023 @<Attempt to open the file from each each directory in the search path@>;
3024 @<It is a fatal error if no such file was found@>;
3027 @ @<Attempt to open the file from each each directory...@>= {
3028 while(!current_fp) {
3029 char f[max_pathname_length+max_filename_length+1];
3030 @<Select the next path name into |cpath| and |npath|@>;
3031 sprintf(f,"%s/%s",cpath,current_filename);
3032 current_fp=fopen(f,"r");
3036 @ @<Select the next path name into |cpath| and |npath|@>= {
3037 if(!(cpath=npath)) break;
3038 if((npath=strchr(npath,includepath_separator))) *npath++=0;
3041 @ @<It is a fatal error if no such file was found@>= {
3042 if(!current_fp) {
3043 fprintf(stderr,"%s not found in search path.\n",current_filename);
3044 exit(1);
3048 @*Keyword State. You can add keywords to the keyword area by using this.
3049 Each keyword heading is one entry in the list.
3051 @<Process keyword heading@>= {
3052 cur_data=new_record(keywords);
3053 keywords.data[cur_data].match=strdup(buf);
3054 keywords.data[cur_data].replacement=strdup("");
3057 @ @<Process keyword state@>= {
3058 keyword_data*k=&keywords.data[cur_data];
3059 if(*buf=='+') {
3060 k->category|=find_category(buf+1);
3061 } @+else {
3062 if(!omit_line_break) strcpy(buf+strlen(buf),"\n");
3063 @<Append buffer to keyword text@>;
3067 @ @<Append buffer to keyword text@>= {
3068 if(*buf) {
3069 int z=strlen(k->replacement);
3070 k->replacement=realloc(k->replacement,z+strlen(buf)+1);
3071 strcpy(k->replacement+z,buf);
3075 @*Pattern State. This state compiles patterns into a pattern area. It
3076 uses its own syntax, and then is converted into the proper control codes
3077 for the |text| of a pattern.
3079 @<Process pattern heading@>= {
3080 cur_name=find_name(buf)-256;
3081 cur_data=set_pattern_area(cur_name+256);
3084 @ The stuff inside the pattern state has its own commands.
3086 @<Process pattern state@>= {
3087 char add_buf[1024]; // buffer of text to add to the current pattern
3088 pattern_data*pat=&pattern_areas.data[cur_data];
3089 *add_buf=0;
3090 switch(*buf) {
3091 case '<': @<Create a new pattern with top priority@>;@+break;
3092 case '>': @<Create a new pattern with bottom priority@>;@+break;
3093 case ':': @<Make a pattern text with a marker@>;@+break;
3094 case '+': @<Add a keyword category to this pattern@>;@+break;
3095 default: ; // do nothing
3097 @<Append contents of |add_buf| to the pattern, if needed@>;
3100 @ @<Create a new pattern with top priority@>= {
3101 cur_data=new_record(pattern_areas);
3102 pattern_areas.data[cur_data].text=strdup("");
3103 pattern_areas.data[cur_data].subroutine=find_name(buf+1)-256;
3104 pattern_areas.data[cur_data].next=names.data[cur_name].pattern_area;
3105 names.data[cur_name].pattern_area=cur_data;
3108 @ @<Create a new pattern with bottom priority@>= {
3109 data_index n;
3110 cur_data=new_record(pattern_areas);
3111 pattern_areas.data[cur_data].text=strdup("");
3112 pattern_areas.data[cur_data].subroutine=find_name(buf+1)-256;
3113 pattern_areas.data[cur_data].next=none;
3114 @<Find the bottom pattern and store its index in |n|@>;
3115 pattern_areas.data[n].next=cur_data;
3118 @ @<Find the bottom pattern and...@>= {
3119 n=names.data[cur_name].pattern_area;
3120 while(pattern_areas.data[n].next!=none && pattern_areas.data[n].text &&
3121 pattern_areas.data[pattern_areas.data[n].next].next!=none)
3122 n=pattern_areas.data[n].next;
3125 @ Actually, the name of this \strike{cake} chunk is a lie, because it does
3126 not always add a marker.
3128 @<Make a pattern text with a marker@>= {
3129 char*p;
3130 char*b=add_buf;
3131 @<Add the pattern marker if applicable@>;
3132 for(p=buf+2;p[-1] && *p;p++) {
3133 switch(*p) {
3134 case '\\': *b++=*++p; @+break;
3135 case '(': *b++=begin_capture; @+break;
3136 case ')': *b++=end_capture; @+break;
3137 case '%': *b++=match_keyword; @+*b++=*++p; @+break;
3138 case '!': *b++=match_table; @+*b++=*++p; @+break;
3139 case '?': *b++=optional_table; @+*b++=*++p; @+break;
3140 case '#': *b++=failed_match; @+break;
3141 case '&': *b++=jump_table; @+*b++=*++p; @+break;
3142 case ';': *b++=successful_match; @+break;
3143 case '<': *b++=back_one_space; @+break;
3144 case '>': *b++=forward_one_space; @+break;
3145 case '[': *b++=match_left_side; @+break;
3146 case ']': *b++=match_right_side; @+break;
3147 default: *b++=*p; @+break;
3150 *b=0;
3153 @ @<Add the pattern marker if applicable@>= {
3154 if(buf[1]>' ') *b++=buf[1]|0x80;
3157 @ @<Add a keyword category to this pattern@>= {
3158 pattern_areas.data[cur_data].category=find_category(buf+1);
3161 @ @<Append contents of |add_buf| to the pattern...@>= {
3162 if(*add_buf) {
3163 int z=strlen(pat->text);
3164 pat->text=realloc(pat->text,z+strlen(add_buf)+1);
3165 strcpy(pat->text+z,add_buf);
3169 @*Subroutine State. This state is used to add a named subroutine.
3171 @<Process subroutine heading@>= {
3172 cur_name=find_name(buf)-256;
3173 if(!names.data[cur_name].value.is_string) {
3174 names.data[cur_name].value.is_string=1;
3175 names.data[cur_name].value.text=strdup("");
3179 @ @<Process subroutine state@>= {
3180 int z=strlen(names.data[cur_name].value.text);
3181 names.data[cur_name].value.text=realloc(names.data[cur_name].value.text,
3182 z+strlen(buf)+1);
3183 strcpy(names.data[cur_name].value.text+z,buf);
3186 @*Word Forms State. You can use the word forms state to enter rules and
3187 exceptions for word forms, such as plurals.
3189 @<Global variables@>=
3190 char wordform_code[256]; // code to execute at \.= line
3191 char wordform_kind; // which kind of word forms is being made now?
3193 @ @<Process word forms state@>= {
3194 switch(*buf) {
3195 case '>': @<Process \.> line in word forms state@>; @+break;
3196 case '=': @<Process \.= line in word forms state@>; @+break;
3197 case '#': wordform_kind=buf[1]; @+break;
3198 default: if(*buf>='0' && *buf<='9')
3199 @<Process numeric line in word forms state@>;
3203 @ The commands are \.>, \.=, and numbers. The command \.> sets a code for
3204 processing \.= commands, and then add to the list.
3206 @<Process \.> line in word forms state@>= {
3207 strcpy(wordform_code,buf+1);
3210 @ @<Process \.= line in word forms state@>= {
3211 int level,kind;
3212 char*orig;
3213 char*dest;
3214 push_string(buf+1);
3215 execute_program(wordform_code);
3216 kind=pop_num(); @+ level=pop_num();
3217 dest=pop_string(); @+ orig=pop_string();
3218 add_word_form(kind,level,orig,dest);
3219 free(orig); @+ free(dest);
3222 @ Now the command for numeric forms. You put ``level\.:orig\.:dest'' in
3223 that order, please.
3225 @<Process numeric line in word forms state@>= {
3226 int level=strtol(buf,&buf,0);
3227 char*p;
3228 if(*buf==':') buf++;
3229 p=strchr(buf,':');
3230 if(p) *p=0;
3231 add_word_form(wordform_kind,level,buf,p+1);
3234 @*Writing Output Files. Finally, it will be time to send any output
3235 generated into the files (if there is any, which there usually is).
3237 @^output@>
3239 @d ctrl(_letter) (0x1F&(_letter))
3241 @d call_final_subroutine ctrl('C')
3242 @d copy_field ctrl('F')
3243 @d newline ctrl('J')
3244 @d loop_point ctrl('L')
3245 @d next_record ctrl('N')
3246 @d skip_one_character ctrl('S')
3248 @<Write the output files@>= {
3249 data_index n;
3250 foreach(n,names) {
3251 if(names.data[n].is_output_file && names.data[n].value.is_string)
3252 @<Write this output file@>;
3256 @ @<Write this output file@>= {
3257 FILE*fout=fopen(names.data[n].name,"w");
3258 char*ptr=names.data[n].value.text;
3259 char*loopptr=ptr; // loop point
3260 if(!fout) @<Error about unable to open output file@>;
3261 while(*ptr) @<Write the character and advance to the next one@>;
3262 fclose(fout);
3265 @ @<Error about unable to open output file@>= {
3266 fprintf(stderr,"Unable to open output file: %s\n",names.data[n].name);
3267 exit(1);
3270 @ @<Write the character and advance to the next one@>= {
3271 switch(*ptr) {
3272 case call_final_subroutine: @<Do |call_final_subroutine|@>; @+break;
3273 case copy_field: @<Do |copy_field|@>; @+break;
3274 case loop_point: loopptr=++ptr; @+break;
3275 case next_record: @<Do |next_record|@>; @+break;
3276 case skip_one_character: ptr+=2; @+break;
3277 default: fputc(*ptr++,fout);
3279 done_writing_one_character: ;
3282 @ @<Do |call_final_subroutine|@>= {
3283 register_value*v;
3284 if(*++ptr=='(') {
3285 char*p=strchr(ptr,')');
3286 *p=0;
3287 v=&name_info(find_name(ptr+1)).value;
3288 *p=')';
3289 ptr=p+1;
3290 } @+else {
3291 v=&registers[*ptr++];
3293 if(v->is_string) {
3294 execute_program(v->text);
3295 @<Write or loop based on result of subroutine call@>;
3296 stack_drop();
3300 @ @<Write or loop based on result of subroutine call@>= {
3301 if(stack_ptr->is_string) {
3302 fprintf(fout,"%s",stack_ptr->text);
3303 } @+else if(stack_ptr->number) {
3304 ptr=loopptr;
3308 @ This command is used to copy the next field.
3310 Look at the definition for the |send_reg_char_or_text| macro. It is
3311 strange, but it should work wherever a statement is expected. Please note
3312 that a ternary condition operator should have both choices of the same
3313 type.
3315 @^strange codes@>
3317 @d tok_idx (registers['A'].number)
3318 @d tok_area
3319 (card_areas.data[name_info(registers['C'].number).value.number].tokens)
3321 @d send_reg_char_or_text(_reg)
3322 if(!registers[_reg].is_string || *registers[_reg].text)
3323 fprintf(fout, "%c%s",
3324 registers[_reg].is_string?
3325 *registers[_reg].text:registers[_reg].number,
3326 registers[_reg].is_string?
3327 registers[_reg].text+1:(unsigned char*)""
3330 @<Do |copy_field|@>= {
3331 ++ptr;
3332 for(;;) {
3333 switch(tok_area[tok_idx++]) {
3334 case null_char: @<Unexpected |null_char|@>;
3335 case end_transmission: tok_idx=0; @+goto done_writing_one_character;
3336 case tabulation: send_reg_char_or_text('T'); @+break;
3337 case raw_data: @<Do |raw_data|@>; @+break;
3338 case escape_code: send_reg_char_or_text('E'); @+break;
3339 case record_separator: tok_idx--; @+goto done_writing_one_character;
3340 case field_separator: @+goto done_writing_one_character;
3341 default: @/
3342 if(tok_area[--tok_idx]&~0xFF)
3343 @<Deal with name code@>@;
3344 else
3345 @<Deal with normal character@>;
3346 tok_idx++;
3351 @ @<Unexpected |null_char|@>= {
3352 fprintf(stderr,"Unexpected null character found in a card area\n");
3353 exit(1);
3356 @ @<Do |raw_data|@>= {
3357 while(tok_area[tok_idx]) fputc(tok_area[tok_idx++],fout);
3358 tok_idx++;
3361 @ A name code found here is a code to tell it to call the subroutine code
3362 when it is time to write it out to the file. It should return a string on
3363 the stack (if it is a number, it will be ignored).
3365 @<Deal with name code@>= {
3366 if(name_info(tok_area[tok_idx]).value.is_string)
3367 execute_program(name_info(tok_area[tok_idx]).value.text);
3368 if(stack_ptr->is_string) fprintf(fout,"%s",stack_ptr->text);
3369 stack_drop();
3372 @ In case of a normal character, normally just write it out. But some
3373 characters need escaped for \TeX.
3375 @<Deal with normal character@>= {
3376 if(tables['E'][tok_area[tok_idx]]) send_reg_char_or_text('E');
3377 fputc(tok_area[tok_idx],fout);
3380 @ This one moves to the next record, looping if a next record is in fact
3381 available. Otherwise, just continue. Note that a |record_separator|
3382 immediately followed by a |end_transmission| is assumed to mean there is
3383 no next record, and that there is allowed to be a optional
3384 |record_separator| just before the |end_transmission|.
3386 @<Do |next_record|@>= {
3387 ++ptr;
3388 while(tok_area[tok_idx]!=record_separator &&
3389 tok_area[tok_idx]!=end_transmission) tok_idx++;
3390 if(tok_area[tok_idx]!=end_transmission &&
3391 tok_area[tok_idx+1]!=end_transmission) ptr=loopptr;
3394 @*Functions Common to DVI and GF. Numbers for \.{GF} and \.{DVI} files use
3395 the |dvi_number| data type. (Change this in the change file if the current
3396 setting is inappropriate for your system.)
3398 There is also the |dvi_measure| type, which is twice as long and is used
3399 to compute numbers that can be fractional (with thirty-two fractional bits
3400 and thirty-two normal bits).
3402 @<Typedefs@>=
3403 @q[Type of DVI numbers::]@>
3404 typedef signed int dvi_number;
3405 typedef signed long long int dvi_measure;
3406 @q[::Type of DVI numbers]@>
3408 @ There is one subroutine here to read a |dvi_number| from a file. They
3409 come in different sizes and some are signed and some are unsigned.
3411 @^endianness@>
3412 @^byte order@>
3414 @-p dvi_number get_dvi_number(FILE*fp,boolean is_signed,int size) {
3415 dvi_number r=0;
3416 if(size) r=fgetc(fp);
3417 if((r&0x80) && is_signed) r|=0xFFFFFF00;
3418 while(--size) r=(r<<8)|fgetc(fp);
3419 return r;
3422 @ Some macros are defined here in order to deal with |dvi_measure| values.
3424 @^fractions@>
3426 @d to_measure(_value) (((dvi_measure)(_value))<<32)
3427 @d floor(_value) ((dvi_number)((_value)>>32))
3428 @d round(_value) ((dvi_number)(((_value)+0x8000)>>32))
3429 @d ceiling(_value) ((dvi_number)(((_value)+0xFFFF)>>32))
3431 @ Here division must be done in a careful way, to ensure that none of the
3432 intermediate results exceed sixty-four bits.
3434 @d fraction_one to_measure(1)
3436 @-p dvi_measure make_fraction(dvi_measure p,dvi_measure q) {
3437 dvi_measure f,n;
3438 boolean negative=(p<0)^(q<0);
3439 if(p<0) p=-p;
3440 if(q<0) q=-q;
3441 n=p/q; @+ p=p%q;
3442 n=(n-1)*fraction_one;
3443 @<Compute $f=\lfloor2^{32}(1+p/q)+{1\over2}\rfloor$@>;
3444 return (f+n)*(negative?-1:1);
3447 @ Notice that the computation specifies $(p-q)+p$ instead of $(p+p)-q$,
3448 because the latter could overflow.
3450 @<Compute $f=...@>= {
3451 register dvi_measure b;
3452 f=1;
3453 while(f<fraction_one) {
3454 b=p-q; @+ p+=b;
3455 if(p>=0) {
3456 f+=f+1;
3457 } @+else {
3458 f<<=1;
3459 p+=q;
3464 @ And a few miscellaneous macros.
3466 @d upto4(_var,_cmd) (_var>=_cmd && _var<_cmd+4)
3468 @*DVI Reading. The device-independent file format is a format invented by
3469 David R.~Fuchs in 1979. The file format need not be explained here, since
3470 there are other books which explain it\biblio{Knuth, Donald. ``\TeX: The
3471 Program''. Computers {\char`\&} Typesetting. ISBN 0-201-13437-3.}\biblio{%
3472 Knuth, Donald. ``\TeX ware''. Stanford Computer Science Report 1097.}.
3474 \edef\TeXwareBiblio{\the\bibliocount}
3475 @^Fuchs, David@>
3476 @.DVI@>
3477 @^device independent@>
3479 At first, names will be given for the commands in a \.{DVI} file.
3481 @d set_char_0 0 // Set a character and move [up to 127]
3482 @d set1 128 // Take one parameter to set character [up to 131]
3483 @d set_rule 132 // Set a rule and move down, two parameters
3484 @d put1 133 // As |set1| but no move [up to 136]
3485 @d put_rule 137 // As |set_rule| but no move
3486 @d nop 138 // No operation
3487 @d bop 139 // Beginning of a page
3488 @d eop 140 // End of a page
3489 @d push 141 // Push $(h,v,w,x,y,z)$ to the stack
3490 @d pop 142 // Pop $(h,v,w,x,y,z)$ from the stack
3491 @d right1 143 // Take one parameter, move right [up to 146]
3492 @d w0 147 // Move right $w$ units
3493 @d w1 148 // Set $w$ and move right [up to 151]
3494 @d x0 152 // Move right $x$ units
3495 @d x1 153 // Set $x$ and move right [up to 156]
3496 @d down1 157 // Take one parameter, move down [up to 160]
3497 @d y0 161 // Move down $y$ units
3498 @d y1 162 // Set $y$ and move down [up to 165]
3499 @d z0 166 // Move down $z$ units
3500 @d z1 167 // Set $z$ and move down [up to 170]
3501 @d fnt_num_0 171 // Select font 0 [up to 234]
3502 @d fnt1 235 // Take parameter to select font [up to 238]
3503 @d xxx1 239 // Specials [up to 242]
3504 @d fnt_def1 243 // Font definitions [up to 246]
3505 @d pre 247 // Preamble
3506 @d post 248 // Postamble
3507 @d post_post 249 // Postpostamble
3509 @ We should now start reading the \.{DVI} file. Filenames of fonts being
3510 used will be sent to standard output.
3512 @-p boolean read_dvi_file(char*filename) {
3513 boolean fonts_okay=1;
3514 FILE*fp=fopen(filename,"rb");
3515 if(!fp) dvi_error(fp,"Unable to open file");
3516 @#@<Skip the preamble of the \.{DVI} file@>;
3517 @<Skip to the next page@>;
3518 @<Read the metapage heading@>;
3519 @<Compute the conversion factor@>;
3520 read_dvi_page(fp);
3521 @<Skip to and read the postamble@>;
3522 @<Read the font definitions and load the fonts@>;
3523 if(fonts_okay) @<Read the pages for each card@>;
3524 @#fclose(fp);
3525 return fonts_okay;
3528 @ @-p void dvi_error(FILE*fp,char*text) {
3529 fprintf(stderr,"DVI error");
3530 if(fp) fprintf(stderr," at %08X",ftell(fp));
3531 fprintf(stderr,": %s\n",text);
3532 exit(1);
3535 @ Please note the version number of the \.{DVI} file must be 2.
3537 @<Skip the preamble of the \.{DVI} file@>= {
3538 if(fgetc(fp)!=pre) dvi_error(fp,"Bad preamble");
3539 if(fgetc(fp)!=2) dvi_error(fp,"Wrong DVI version");
3540 @<Read the measurement parameters@>;
3541 @<Skip the DVI comment@>;
3544 @ @<Read the measurement parameters@>= {
3545 unit_num=get_dvi_number(fp,0,4);
3546 unit_den=get_dvi_number(fp,0,4);
3547 unit_mag=get_dvi_number(fp,0,4);
3550 @ @<Skip the DVI comment@>= {
3551 int n=fgetc(fp);
3552 fseek(fp,n,SEEK_CUR);
3555 @ From the postamble we can read the pointer for the last page.
3557 @<Global variables@>=
3558 dvi_number last_page_ptr;
3560 @ @<Skip to and read the postamble@>= {
3561 fseek(fp,-4,SEEK_END);
3562 while(fgetc(fp)==223) fseek(fp,-2,SEEK_CUR);
3563 fseek(fp,-5,SEEK_CUR);
3564 fseek(fp,get_dvi_number(fp,0,4)+1,SEEK_SET);
3565 last_page_ptr=get_dvi_number(fp,0,4);
3566 fseek(fp,20,SEEK_CUR); // Skipped parameters of |post|
3567 dvi_stack=malloc(get_dvi_number(fp,0,2)*sizeof(dvi_stack_entry));
3568 fseek(fp,2,SEEK_CUR);
3571 @ Between the preamble and the first page can be |nop| commands and font
3572 definitions, so these will be skipped. The same things can occur between
3573 the end of one page and the beginning of the next page.
3575 @<Skip to the next page@>= {
3576 int c;
3577 for(;;) {
3578 c=fgetc(fp);
3579 if(c==bop) break;
3580 if(c>=fnt_def1 && c<fnt_def1+4) {
3581 @<Skip a font definition@>;
3582 } @+else if(c!=nop) {
3583 dvi_error(fp,"Bad command between pages");
3588 @ @<Skip a font definition@>= {
3589 int a,l;
3590 fseek(fp,c+13-fnt_def1,SEEK_CUR);
3591 a=fgetc(fp);
3592 l=fgetc(fp);
3593 fseek(fp,a+l,SEEK_CUR);
3596 @ The metapage includes the resolution and other things which must be set,
3597 such as subroutine codes. The resolution must be read before fonts can be
3598 read. Please note that no characters can be typeset on the metapage, since
3599 fonts have not been loaded yet. You can still place empty boxes. The DPI
3600 setting (resolution) is read from the \.{\\count1} register.
3602 @<Read the metapage heading@>= {
3603 dvi_number n=get_dvi_number(fp,0,4);
3604 if(n) {
3605 fprintf(stderr,"Metapage must be numbered zero (found %d).\n",n);
3606 exit(1);
3608 push_num(get_dvi_number(fp,0,4)); @+ store('D');
3609 fseek(fp,9*4,SEEK_CUR); // Skip other parameters
3610 layer_width=layer_height=0;
3613 @ A stack is kept of the page registers, for use with the |push| and |pop|
3614 commands of a \.{DVI} file. This stack is used by the |read_dvi_page|
3615 subroutine and stores the |quan| registers (described in the next
3616 chapter).
3618 @<Typedefs@>=
3619 typedef struct {
3620 dvi_number h;
3621 dvi_number v;
3622 dvi_number w;
3623 dvi_number x;
3624 dvi_number y;
3625 dvi_number z;
3626 dvi_number hh;
3627 dvi_number vv;
3628 } dvi_stack_entry;
3630 @ @<Global variables@>=
3631 dvi_stack_entry*dvi_stack;
3632 dvi_stack_entry*dvi_stack_ptr;
3634 @ Here is the subroutine to read commands from a DVI page. The file
3635 position should be at the beginning of the page after the |bop| command.
3637 @^pages@>
3639 @-p void read_dvi_page(FILE*fp) {
3640 memusage_log("Beginning of page",fseek(fp));
3641 @<Reset the page registers and stack@>;
3642 typeset_new_page();
3643 @<Read the commands of this page@>;
3644 if(layer_width && layer_height) @<Render this page@>;
3647 @ @<Reset the page registers and stack@>= {
3648 quan('A')=quan('B')=quan('H')=quan('I')=quan('J')=quan('L')=quan('V')=
3649 quan('W')=quan('X')=quan('Y')=quan('Z')=0;
3650 dvi_stack_ptr=dvi_stack;
3653 @ @<Read the commands of this page@>= {
3654 int c,a;
3655 boolean moveaftertyping;
3656 for(;;) {
3657 c=fgetc(fp);
3658 if(c<set1) {
3659 moveaftertyping=1;
3660 @<Typeset character |c| on the current layer@>;
3661 } @+else if(upto4(c,set1)) {
3662 moveaftertyping=1;
3663 c=get_dvi_number(fp,0,c+1-set1);
3664 @<Typeset character |c| on the current layer@>;
3665 } @+else if(c==set_rule || c==put_rule) {
3666 moveaftertyping=(c==set_rule);
3667 c=get_dvi_number(fp,1,4);
3668 a=get_dvi_number(fp,1,4);
3669 @<Typeset |a| by |c| rule on the current layer@>;
3670 } @+else if(upto4(c,put1)) {
3671 moveaftertyping=0;
3672 c=get_dvi_number(fp,0,c+1-put1);
3673 @<Typeset character |c| on the current layer@>;
3674 } @+else if(c==eop) {
3675 break;
3676 } @+else if(c==push) {
3677 if(dvi_stack) @<Push DVI registers to stack@>;
3678 } @+else if(c==pop) {
3679 if(dvi_stack) @<Pop DVI registers from stack@>;
3680 } @+else if(upto4(c,right1)) {
3681 c=get_dvi_number(fp,1,c+1-right1);
3682 horizontal_movement(c);
3683 } @+else if(c==w0) {
3684 horizontal_movement(quan('W'));
3685 } @+else if(upto4(c,w1)) {
3686 c=get_dvi_number(fp,1,c+1-w1);
3687 horizontal_movement(quan('W')=c);
3688 } @+else if(c==x0) {
3689 horizontal_movement(quan('X'));
3690 } @+else if(upto4(c,x1)) {
3691 c=get_dvi_number(fp,1,c+1-x1);
3692 horizontal_movement(quan('X')=c);
3693 } @+else if(upto4(c,down1)) {
3694 c=get_dvi_number(fp,1,c+1-down1);
3695 vertical_movement(c);
3696 } @+else if(c==y0) {
3697 vertical_movement(quan('Y'));
3698 } @+else if(upto4(c,y1)) {
3699 c=get_dvi_number(fp,1,c+1-y1);
3700 vertical_movement(quan('Y')=c);
3701 } @+else if(c==z0) {
3702 vertical_movement(quan('Z'));
3703 } @+else if(upto4(c,z1)) {
3704 c=get_dvi_number(fp,1,c+1-z1);
3705 vertical_movement(quan('Z')=c);
3706 } @+else if(c>=fnt_num_0 && c<fnt1) {
3707 quan('F')=c-fnt_num_0;
3708 } @+else if(upto4(c,fnt1)) {
3709 quan('F')=get_dvi_number(fp,0,c+1-fnt1);
3710 } @+else if(upto4(c,xxx1)) {
3711 c=get_dvi_number(fp,0,c+1-xxx1);
3712 @<Read a special of length |c|@>;
3713 } @+else if(upto4(c,fnt_def1)) {
3714 @<Skip a font definition@>;
3715 } @+else if(c!=nop) {
3716 dvi_error(fp,"Unknown DVI command");
3721 @ @<Push DVI registers to stack@>= {
3722 dvi_stack_ptr->h=quan('H');
3723 dvi_stack_ptr->v=quan('V');
3724 dvi_stack_ptr->w=quan('W');
3725 dvi_stack_ptr->x=quan('X');
3726 dvi_stack_ptr->y=quan('Y');
3727 dvi_stack_ptr->z=quan('Z');
3728 dvi_stack_ptr->hh=quan('I');
3729 dvi_stack_ptr->vv=quan('J');
3730 ++dvi_stack_ptr;
3733 @ @<Pop DVI registers from stack@>= {
3734 --dvi_stack_ptr;
3735 quan('H')=dvi_stack_ptr->h;
3736 quan('V')=dvi_stack_ptr->v;
3737 quan('W')=dvi_stack_ptr->w;
3738 quan('X')=dvi_stack_ptr->x;
3739 quan('Y')=dvi_stack_ptr->y;
3740 quan('Z')=dvi_stack_ptr->z;
3741 quan('I')=dvi_stack_ptr->hh;
3742 quan('J')=dvi_stack_ptr->vv;
3745 @ A special in \TeX nicard is used to execute a special code while reading
3746 the DVI file. Uses might be additional calculations, changes of registers,
3747 special effects, layer selection, etc. All of these possible commands are
3748 dealt with elsewhere in this program. All we do here is to read it and to
3749 send it to the |execute_program| subroutine.
3751 @^specials@>
3753 @<Read a special of length |c|@>= {
3754 char*buf=malloc(c+1);
3755 fread(buf,1,c,fp);
3756 buf[c]=0;
3757 @<Set \.X and \.Y registers to prepare for the special@>;
3758 execute_program(buf);
3759 free(buf);
3762 @ @<Set \.X and \.Y registers to prepare for the special@>= {
3763 registers['X'].is_string=registers['Y'].is_string=0;
3764 registers['X'].number=quan('I');
3765 registers['Y'].number=quan('J');
3768 @ In order to read all the pages for each card, we can skip backwards by
3769 using the back pointers. Either we will print all cards (in reverse
3770 order), or we will print cards listed on the command-line, or we will
3771 print cards listed in a file (this last way might be used to print decks
3772 or booster packs).
3774 Card numbers should be one-based, and should not be negative. Any pages
3775 with negative page numbers will be ignored when it is in the mode for
3776 printing all cards.
3778 @d printing_all_cards 0
3779 @d printing_list 1
3780 @d printing_list_from_file 2
3782 @<Global variables@>=
3783 unsigned char printing_mode;
3784 char*printlisttext;
3785 FILE*printlistfile;
3787 @ @<Read the pages for each card@>= {
3788 dvi_number page_ptr=last_page_ptr;
3789 dvi_number e=0,n; // page numbers
3790 boolean pagenotfound=0;
3791 for(;;) {
3792 @<Read the next entry from the list of pages (if applicable)@>;
3793 try_next_page:
3794 @<Seek the next page to print@>;
3795 @<Read the heading for this page@>;
3796 @<If this page shouldn't be printed now, |goto try_next_page|@>;
3797 pagenotfound=0;
3798 read_dvi_page(fp);
3800 @#done_printing:;
3803 @ @<Read the next entry from the list of pages (if applicable)@>= {
3804 if(printing_mode==printing_list) {
3805 if(!*printlisttext) goto done_printing;
3806 e=strtol(printlisttext,&printlisttext,10);
3807 if(!e) goto done_printing;
3808 if(*printlisttext) printlisttext++;
3809 } @+else if(printing_mode==printing_list_from_file) {
3810 char buf[256];
3811 if(!printlistfile || feof(printlistfile)) goto done_printing;
3812 if(!fgets(buf,255,printlistfile)) goto done_printing;
3813 e=strtol(buf,0,10);
3817 @ @<Seek the next page to print@>= {
3818 if(page_ptr==-1) {
3819 if(pagenotfound) {
3820 fprintf(stderr,"No page found: %d\n",e);
3821 exit(1);
3823 page_ptr=last_page_ptr;
3824 if(printing_mode==printing_all_cards) goto done_printing;
3825 pagenotfound=1;
3827 fseek(fp,page_ptr+1,SEEK_SET);
3830 @ @<Read the heading for this page@>= {
3831 n=quan('P')=get_dvi_number(fp,1,4);
3832 fseek(fp,4,SEEK_CUR);
3833 layer_width=get_dvi_number(fp,1,4);
3834 layer_height=get_dvi_number(fp,1,4);
3835 fseek(fp,4*6,SEEK_CUR);
3836 page_ptr=get_dvi_number(fp,1,4);
3839 @ @<If this page shouldn't be printed now, |goto try_next_page|@>= {
3840 if(n<=0 && printing_mode==printing_all_cards) goto try_next_page;
3841 if(n!=e && printing_mode!=printing_all_cards) goto try_next_page;
3844 @*DVI Font Metrics. Here, the fonts are loaded. It is assumed all fonts
3845 are in the current directory, and the ``area'' of the font name is
3846 ignored. The checksum will also be ignored (it can be checked with
3847 external programs if necessary).
3849 @^area@>
3850 @^font loading@>
3852 @<Read the font definitions and load the fonts@>= {
3853 int c;
3854 for(;;) {
3855 c=fgetc(fp);
3856 if(c==post_post) break;
3857 if(c>=fnt_def1 && c<fnt_def1+4) {
3858 int k=get_dvi_number(fp,0,c+1-fnt_def1);
3859 if(k&~0xFF) dvi_error(fp,"Too many fonts");
3860 memusage_log("Loading font",k);
3861 @<Read the definition for font |k| and load it@>;
3862 } @+else if(c!=nop) {
3863 dvi_error(fp,"Bad command in postamble");
3866 memusage_log("End of postamble",c);
3869 @ When reading fonts, it will be necessary to keep a list of the fonts
3870 and their character indices. Only 256 fonts are permitted in one job.
3872 @<Global variables@>=
3873 data_index fontindex[256];
3875 @ @<Read the definition for font |k| and load it@>= {
3876 dvi_number c=get_dvi_number(fp,0,4); // checksum (unused)
3877 dvi_number s=get_dvi_number(fp,0,4); // scale factor
3878 dvi_number d=get_dvi_number(fp,0,4); // design size
3879 int a=get_dvi_number(fp,0,1); // length of area
3880 int l=get_dvi_number(fp,0,1); // length of name
3881 char n[257];
3882 fseek(fp,a,SEEK_CUR);
3883 fread(n,1,l,fp);
3884 n[l]=0;
3885 if((fontindex[k]=read_gf_file(n,s,d))==none) fonts_okay=0;
3888 @ An important part of reading the font metrics is the width computation,
3889 which involves multiplying the relative widths in the \.{TFM} file (or
3890 \.{GF} file) by the scaling factor in the \.{DVI} file. This
3891 multiplication must be done in precisely the same way by all \.{DVI}
3892 reading programs, in order to validate the assumptions made by \.{DVI}-%
3893 writing programs such as \TeX.
3895 % (The following paragraph is taken directly from "dvitype.web")
3896 Let us therefore summarize what needs to be done. Each width in a \.{TFM}
3897 file appears as a four-byte quantity called a |fix_word|. A |fix_word|
3898 whose respective bytes are $(a,b,c,d)$ represents the number
3899 $$x=\left\{\vcenter{\halign{$#$,\hfil\qquad&if $#$\hfil\cr
3900 b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=0;\cr
3901 -16+b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=255.\cr}}\right.$$
3902 (No other choices of $a$ are allowed, since the magnitude of a \.{TFM}
3903 dimension must be less than 16.) We want to multiply this quantity by the
3904 integer~|z|, which is known to be less than $2^{27}$.
3905 If $|z|<2^{23}$, the individual multiplications $b\cdot z$, $c\cdot z$,
3906 $d\cdot z$ cannot overflow; otherwise we will divide |z| by 2, 4, 8, or
3907 16, to obtain a multiplier less than $2^{23}$, and we can compensate for
3908 this later. If |z| has thereby been replaced by $|z|^\prime=|z|/2^e$, let
3909 $\beta=2^{4-e}$; we shall compute
3910 $$\lfloor(b+c\cdot2^{-8}+d\cdot2^{-16})\,z^\prime/\beta\rfloor$$ if $a=0$,
3911 or the same quantity minus $\alpha=2^{4+e}z^\prime$ if $a=255$.
3912 This calculation must be
3913 done exactly, for the reasons stated above; the following program does the
3914 job in a system-independent way, assuming that arithmetic is exact on
3915 numbers less than $2^{31}$ in magnitude.
3917 \def\zprime{z'}
3919 @f alpha TeX
3920 @f beta TeX
3921 @f zprime TeX
3923 @<Compute |zprime|, |alpha|, and |beta|@>= {
3924 zprime=s; @+ alpha=16;
3925 while(zprime>=040000000) {
3926 zprime>>=1; @+ alpha<<=1;
3928 beta=256/alpha; @+ alpha*=zprime;
3931 @ @<Compute the character width |w|@>= {
3932 w=(((((b3*zprime)>>8)+(b2*zprime))>>8)+(b1*zprime))/beta;
3933 if(b0) w-=alpha;
3936 @*GF Reading. The \.{GF} format is a ``generic font'' format. It has a lot
3937 in common with \.{DVI} format.
3939 At first, names will be given for the commands in a \.{GF} file. Many
3940 commands have the same numbers as they do in a \.{DVI} file (described in
3941 the previous chapter), which makes it very convenient\biblio{This is
3942 probably on purpose for the this very reason, so that a WEB or CWEB
3943 program can use one set of named constants for reading both files.}.
3945 @d paint_0 0 // Paint $d$ pixels black or white [up to 63]
3946 @d paint1 64 // Take parameter, paint pixels [up to 66]
3947 @d boc 67 // Beginning of a character picture
3948 @d boc1 68 // Short form of |boc|
3949 @d eoc 69 // End of a character picture
3950 @d skip0 70 // Skip some rows
3951 @d skip1 71 // Skip some rows [up to 73]
3952 @d new_row_0 74 // Start a new row and move right [up to 238]
3953 @d yyy 243 // Numeric specials
3954 @d no_op 244 // No operation
3955 @d char_loc 245 // Character locator
3956 @d char_loc0 246 // Short form of |char_loc|
3958 @ The |font_struct| structure stores the information for each character in
3959 a font. The |raster| field points to a bitmap with eight pixels per octet,
3960 most significant bit for the leftmost pixel, each row always padded to a
3961 multiple of eight pixels.
3963 While it is reading the postamble, it will fill in this structure with the
3964 |ptr| field set. After the postamble is read, it will fill in the other
3965 fields belonging to its union.
3967 @<Typedefs@>=
3968 typedef struct {
3969 dvi_number dx; // character escapement in pixels
3970 dvi_number w; // width in DVI units
3971 union {
3972 struct {
3973 dvi_number min_n,max_n,min_m,max_m; // bounding box (in pixels)
3974 unsigned short n; // character code number
3975 unsigned char*raster;
3976 unsigned char flag; // bitfield of flags for this character
3977 }@+;
3978 dvi_number ptr;
3979 }@+;
3980 data_index next;
3981 } font_struct;
3983 @ List of flags follows. Some of these flags might be used in order to
3984 allow$\mathord{}>256$ characters per font, since {\TeX} does not have a
3985 command to enter characters with codes more than one byte long. These
3986 flags are specified using numeric specials.
3988 @d ff_select 0x01 // set high octet all characters
3989 @d ff_prefix 0x02 // set high octet for codes 128-255
3990 @d ff_roundafter 0x04 // round $\it hh$ after sending character
3991 @d ff_roundbefore 0x08 // round $\it hh$ before sending character
3992 @d ff_reset 0x10 // reset high octet
3993 @d ff_strip 0x20 // strip highest bit of prefix
3994 @d ff_space 0x40 // do not save the raster (space only)
3995 @d ff_reserved 0x80 // {\bf DO NOT USE}
3997 @ @<Global variables@>=
3998 memory_of(font_struct) font_data;
4000 @ @<Initialize memory@>= init_memory(font_data,4);
4002 @ When loading a \.{GF} font, the filename will contain the resolution
4003 in dots per inch.
4005 @^font loading@>
4007 @-p data_index read_gf_file(char*fontname,dvi_measure s,dvi_measure d) {
4008 unsigned int dpi=(resolution*unit_mag*s+500*d)/(100000*d);
4009 FILE*fp;
4010 data_index index=none;
4011 data_index first_index=none;
4012 data_index last_index=none;
4013 dvi_number zprime,alpha,beta; // used for width computation
4014 @<Compute |zprime|, |alpha|, and |beta|@>;
4015 @<Figure out the filename and open the file, |return none| if it can't@>;
4016 @<Skip to the postamble of the \.{GF} file@>;
4017 @<Read the character locators@>;
4018 @<Read the character rasters and flags@>;
4019 fclose(fp);
4020 return last_index;
4023 @ When figuring out the filename, it will send it to standard output so
4024 that a list can be made of the required fonts.
4026 @<Figure out the filename and open the file, ...@>= {
4027 char n[295];
4028 sprintf(n,"%s.%dgf",fontname,dpi);
4029 printf("%s\n",n);
4030 fp=fopen(n,"rb");
4031 if(!fp) return none;
4034 @ @<Skip to the postamble of the \.{GF} file@>= {
4035 int c;
4036 fseek(fp,-4,SEEK_END);
4037 while(fgetc(fp)==223) fseek(fp,-2,SEEK_CUR);
4038 fseek(fp,-5,SEEK_CUR);
4039 fseek(fp,get_dvi_number(fp,0,4)+37,SEEK_SET); // nothing matters anymore
4042 @ @<Read the character locators@>= {
4043 int c,b0,b1,b2,b3;
4044 dvi_number dx,w,p;
4045 for(;;) {
4046 c=fgetc(fp);
4047 if(c==post_post) break;
4048 p=-1;
4049 if(c==char_loc) {
4050 @<Read a long character locator@>;
4051 } @+else if(c==char_loc0) {
4052 @<Read a short character locator@>;
4053 } @+else if(c!=no_op) {
4054 fprintf(stderr,"Bad command in GF postamble.\n");
4055 fprintf(stderr,"(Command %d, address %08X)\n",c,ftell(fp)-1);
4056 exit(1);
4058 if(p!=-1) @<Defer this character locator into |font_data|@>;
4060 last_index=index;
4063 @ There are some parameters we do not care about. First is $c$, which is
4064 the character code residue (modulo 256). This is not important since it
4065 is duplicated in the |boc| heading for each character. The second
4066 parameter which we do not care about is the $\it dy$ parameter, since it
4067 should be zero for \.{DVI} files.
4069 @<Read a long character locator@>= {
4070 fseek(fp,1,SEEK_CUR);
4071 dx=get_dvi_number(fp,1,4)>>16;
4072 fseek(fp,4,SEEK_CUR);
4073 @<Read four bytes@>;
4074 p=get_dvi_number(fp,1,4);
4077 @ @<Read a short character locator@>= {
4078 fseek(fp,1,SEEK_CUR);
4079 dx=get_dvi_number(fp,0,1);
4080 @<Read four bytes@>;
4081 p=get_dvi_number(fp,1,4);
4084 @ @<Read four bytes@>= {
4085 b0=fgetc(fp);@+b1=fgetc(fp);@+b2=fgetc(fp);@+b3=fgetc(fp);
4088 @ This processing is deferred, and the rest of the parameters will be
4089 filled in later (and the |ptr| field will be overwritten since it will
4090 no longer be needed at that time).
4092 @<Defer this character locator into |font_data|@>= {
4093 data_index n=new_record(font_data);
4094 @<Compute the character width |w|@>;
4095 font_data.data[n].next=index;
4096 font_data.data[n].dx=dx;
4097 font_data.data[n].w=w;
4098 font_data.data[n].ptr=p;
4099 if(index==none) first_index=n;
4100 index=n;
4103 @ Now is time to go through the list we made up and this time actually
4104 fill in the parameters and pictures.
4106 @<Read the character rasters and flags@>= {
4107 while(index!=none) {
4108 fseek(fp,font_data.data[index].ptr,SEEK_SET);
4109 font_data.data[index].flag=0;
4110 font_data.data[index].raster=0;
4111 @<Read commands for this character@>;
4112 @#index=font_data.data[index].next;
4116 @ Painting the picture uses the value of |paint_switch| to determine
4117 to draw or skip. The current position in the array |raster| is also
4118 pointed by the |pic| pointer. Note that |black| and |white| are not
4119 necessary black and white (but they are on normal paper).
4121 Note the value of $n$ is not needed since the |pic| pointer automatically
4122 keeps track of this kinds of stuff. However, |m| is needed because of
4123 commands that can skip rows, to know how many columns must be skipped to
4124 reach the next row. There is also |b|, which keeps track of the bit
4125 position in the current byte.
4127 @d white 0
4128 @d black 1
4130 @d reset_m
4131 m=(font_data.data[index].max_m-font_data.data[index].min_m)/8+1@;
4133 @<Read commands for this character@>= {
4134 unsigned int c,m,b;
4135 unsigned char*pic;
4136 boolean paint_switch;
4137 for(;;) {
4138 c=fgetc(fp);
4139 if(c<paint1) {
4140 @<Paint |c| pixels |black| or |white|@>;
4141 } @+else if(c>=paint1 && c<paint1+3) {
4142 c=get_dvi_number(fp,0,c+1-paint1);
4143 @<Paint |c| pixels |black| or |white|@>;
4144 } @+else if(c==boc) {
4145 @<Initialize parameters and picture (long form)@>;
4146 } @+else if(c==boc1) {
4147 @<Initialize parameters and picture (short form)@>;
4148 } @+else if(c==eoc) {
4149 break; // Well Done!
4150 } @+else if(upto4(c,skip0)) {
4151 if(c==skip0) c=0;
4152 else c=get_dvi_number(fp,0,c+1-skip1);
4153 @<Finish a row and skip |c| rows@>;
4154 } @+else if(c>=new_row_0 && c<=new_row_0+164) {
4155 c-=new_row_0;
4156 @<Finish a row and skip |c| columns@>;
4157 } @+else if(c==yyy) {
4158 font_data.data[index].flag|=get_dvi_number(fp,0,4)>>16;
4159 } @+else if(c!=no_op) {
4160 fprintf(stderr,"Unknown GF command!\n");
4161 fprintf(stderr,"(Command %d, address %08X)\n",c,ftell(fp)-1);
4166 @ Actually |m| is something a bit different than the standard, because |m|
4167 now tells how many bytes are remaining in the current row.
4169 @d pic_rows (1+font_data.data[index].max_n-font_data.data[index].min_n)
4171 @<Initialize parameters and picture (long form)@>= {
4172 font_data.data[index].n=get_dvi_number(fp,0,4);
4173 @<Deal with $p$ (pointer to previous character with same metrics)@>;
4174 font_data.data[index].min_m=get_dvi_number(fp,1,4);
4175 font_data.data[index].max_m=get_dvi_number(fp,1,4);
4176 font_data.data[index].min_n=get_dvi_number(fp,1,4);
4177 font_data.data[index].max_n=get_dvi_number(fp,1,4);
4178 @<Initialize picture@>;
4181 @ @<Initialize picture@>= {
4182 if(font_data.data[index].flag&ff_space) break;
4183 paint_switch=white;
4184 reset_m;
4185 b=0;
4186 pic=font_data.data[index].raster=malloc(m*pic_rows+1);
4187 memset(pic,0,m*pic_rows);
4190 @ @<Initialize parameters and picture (short form)@>= {
4191 int d;
4192 font_data.data[index].n=get_dvi_number(fp,0,1);
4193 d=get_dvi_number(fp,0,1);
4194 font_data.data[index].max_m=get_dvi_number(fp,0,1);
4195 font_data.data[index].min_m=font_data.data[index].max_m-d;
4196 d=get_dvi_number(fp,0,1);
4197 font_data.data[index].max_n=get_dvi_number(fp,0,1);
4198 font_data.data[index].min_n=font_data.data[index].max_n-d;
4199 @<Initialize picture@>;
4202 @ The pointers to other characters will also be deferred in the same way
4203 as the character locators, but this time from the other end. Now, once it
4204 is finished all the characters, it will {\sl automatically} know to read
4205 the next one properly! (Now you can see what the purpose of the
4206 |@!first_index| variable is.)
4208 @<Deal with $p$ (pointer to previous character with same metrics)@>= {
4209 dvi_number p=get_dvi_number(fp,1,4);
4210 if(p!=-1) {
4211 data_index i=new_record(font_data);
4212 font_data.data[i].next=none;
4213 font_data.data[i].dx=font_data.data[index].dx;
4214 font_data.data[i].w=font_data.data[index].w;
4215 font_data.data[i].ptr=p;
4216 font_data.data[first_index].next=i;
4217 first_index=i;
4221 @ Now we get to the actual painting. We can assume the value of |m| is
4222 never negative and that everything else is also okay.
4224 @<Paint |c| pixels |black| or |white|@>= {
4225 if(paint_switch) {
4226 if(b+c<=8) {
4227 @<Paint a small block of pixels in the current byte@>;
4228 } @+else {
4229 @<Paint the rest of the pixels in the current byte@>;
4230 @<Fill up the bytes in the middle@>;
4231 @<Clear the pixels needed clearing at the end@>;
4234 @<Update |paint_switch|, |pic|, |b|, and |m|@>;
4237 @ @<Update |paint_switch|, |pic|, |b|, and |m|@>= {
4238 paint_switch^=1;
4239 b+=c;
4240 pic+=b>>3;
4241 m-=b>>3;
4242 b&=7;
4245 @ @<Paint a small block of pixels in the current byte@>= {
4246 *pic|=(0xFF>>b)&~(0xFF>>(b+c));
4249 @ @<Paint the rest of the pixels in the current byte@>= {
4250 *pic|=0xFF>>b;
4253 @ @<Fill up the bytes in the middle@>= {
4254 memset(pic+1,0xFF,(c+b)>>3);
4257 @ @<Clear the pixels needed clearing at the end@>= {
4258 pic[(c+b)>>3]&=~(0xFF>>((c+b)&7));
4261 @ @<Finish a row and skip |c| rows@>= {
4262 pic+=m;
4263 b=0;
4264 reset_m;
4265 pic+=m*c;
4266 paint_switch=white;
4269 @ @<Finish a row and skip |c| columns@>= {
4270 pic+=m;
4271 reset_m;
4272 m-=c>>3;
4273 pic+=c>>3;
4274 b=c&7;
4275 paint_switch=black;
4278 @ @<Display font information@>= {
4279 data_index i;
4280 foreach(i,font_data) {
4281 printf("[%d] box=(%d,%d,%d,%d) dx=%d w=%d n=%d flag=%d [%d]\n"
4282 ,i,font_data.data[i].min_n,font_data.data[i].max_n
4283 ,font_data.data[i].min_m,font_data.data[i].max_m
4284 ,font_data.data[i].dx,font_data.data[i].w,font_data.data[i].n
4285 ,font_data.data[i].flag,font_data.data[i].next
4290 @*Layer Computation. Now is the chapter for actually deciding rendering on
4291 the page, where everything should go, etc.$^{[\TeXwareBiblio]}$
4293 @<Global variables@>=
4294 dvi_measure unit_num; // Numerator for units of measurement
4295 dvi_measure unit_den; // Denominator for units of measurement
4296 dvi_measure unit_mag; // Magnification for measurement
4297 dvi_measure unit_conv; // Conversion factor
4299 @ There are also a number of ``internal typesetting quantities''. These
4300 are parameters stored in a separate array, and are used to keep track of
4301 the current state of the typesetting. They are labeled with letters from
4302 \.A to \.Z. They can be modified inside of specials, although some of them
4303 probably shouldn't be modified in this way. Here is the list of them:
4305 \.A, \.B: Horizontal and vertical offset added to \.I and \.J.
4307 \.C: Character code prefix. If bit eight is not set, it only affects
4308 character codes with bit seven set.
4310 \.D: Maximum horizontal drift (in pixels), meaning how far away the \.I
4311 and \.J parameters are allowed to be from the correctly rounded values.
4313 \.E: Maximum vertical drift.
4315 \.F: The current font.
4317 \.H: The horizontal position on the page, in DVI units.
4319 \.I: The horizontal position on the page, in pixels.
4321 \.J: The vertical position on the page, in pixels.
4323 \.L: The current layer number. If this is zero, nothing is placed on the
4324 page, although the positions can still be changed and specials can still
4325 be used.
4327 \.P: Page number. This is used to determine the filename of output.
4329 \.R, \.S: The limits for when horizontal motion should add the number of
4330 pixels or when it should recalculate the pixels entirely.
4332 \.T, \.U: Like \.R and \.S, but for vertical motions.
4334 \.V: The vertical position on the page, in DVI units.
4336 \.W, \.X, \.Y, \.Z: The current spacing amounts, in DVI units.
4338 @d quan(_name) (type_quan[(_name)&0x1F])
4340 @<Global variables@>=
4341 dvi_number type_quan[32];
4343 @ @<Cases for system commands@>=
4344 @-case 'm': {
4345 // Modify an internal typesetting quantity
4346 if(stack_ptr->is_string) program_error("Type mismatch");
4347 quan(*++ptr)=pop_num();
4348 break;
4351 @ The conversion factor |unit_conv| is figured as follows: There are
4352 exactly |unit_num/unit_den| decimicrons per DVI unit, and 254000
4353 decimicrons per inch, and |resolution/100| pixels per inch. Then we have
4354 to adjust this by the magnification |unit_mag|.
4356 Division must be done slightly carefully to avoid overflow.
4358 @d resolution (registers['D'].number)
4360 @<Compute the conversion factor@>= {
4361 unit_conv=make_fraction(unit_num*resolution*unit_mag,unit_den*100000);
4362 unit_conv/=254000;
4365 @ Here are the codes to compute movements. The definition of \.{DVI} files
4366 refers to six registers which hold integer values in DVI units. However,
4367 we also have two more registers, for horizontal and vertical pixel units.
4369 A sequence of characters or rules might cause the pixel values to drift
4370 from their correctly rounded values, since they are not usually an exact
4371 integer number of pixels.
4373 @d to_pixels(_val) round((_val)*unit_conv)
4375 @-p void horizontal_movement(dvi_number x) {
4376 quan('H')+=x;
4377 if(x>quan('S') || x<quan('R')) {
4378 quan('I')=to_pixels(quan('H'));
4379 } @+else {
4380 quan('I')+=to_pixels(x);
4381 if(to_pixels(quan('H'))-quan('I')>quan('D'))
4382 quan('I')=to_pixels(quan('H'))+quan('D');
4383 if(to_pixels(quan('H'))-quan('I')<-quan('D'))
4384 quan('I')=to_pixels(quan('H'))-quan('D');
4388 @ @-p void vertical_movement(dvi_number x) {
4389 quan('V')+=x;
4390 if(x>quan('U') || x<quan('T')) {
4391 quan('J')=to_pixels(quan('V'));
4392 } @+else {
4393 quan('J')+=to_pixels(x);
4394 if(to_pixels(quan('V'))-quan('J')>quan('E'))
4395 quan('J')=to_pixels(quan('V'))+quan('E');
4396 if(to_pixels(quan('V'))-quan('J')<-quan('E'))
4397 quan('J')=to_pixels(quan('V'))-quan('E');
4401 @ This is now the part that does actual sending. When many characters
4402 come next to each other, the rounding will be done such that the number
4403 of pixels between two letters will always be the same whenever those two
4404 letters occur next to each other.
4406 @<Typeset character |c| on the current layer@>= {
4407 data_index n=fontindex[quan('F')&0xFF];
4408 if((quan('C')&0x100) || (c&0x80)) c|=quan('C')<<8;
4409 while(n!=none && c!=font_data.data[n].n)
4410 n=font_data.data[n].next;
4411 if(n==none) dvi_error(fp,"Character not in font");
4412 @<Typeset the character and update the current position@>;
4413 @<Update the character code prefix@>;
4416 @ @<Typeset the character and update the current position@>= {
4417 if(font_data.data[n].flag&ff_roundbefore)
4418 quan('I')=to_pixels(quan('H'));
4419 if(quan('L') && font_data.data[n].raster) typeset_char_here(n);
4420 if(moveaftertyping) {
4421 quan('H')+=font_data.data[n].w;
4422 quan('I')+=font_data.data[n].dx;
4423 if(font_data.data[n].flag&ff_roundafter)
4424 quan('I')=to_pixels(quan('H'));
4425 else horizontal_movement(0);
4429 @ If you have a typesetting program that can ship out characters with
4430 codes more than eight bits long, you won't need this. It is provided for
4431 use with normal {\TeX} system.
4433 @<Update the character code prefix@>= {
4434 if(font_data.data[n].flag&ff_strip) c&=0x7F; else c&=0xFF;
4435 if(font_data.data[n].flag&ff_select) quan('C')=c|0x100;
4436 if(font_data.data[n].flag&ff_prefix) quan('C')=c;
4437 if(font_data.data[n].flag&ff_reset) quan('C')=0;
4440 @ The number of pixels in the height or width of a rule will always be
4441 rounded up. However, unlike DVItype, this program has no floating point
4442 rounding errors.
4444 @d to_rule_pixels(_val) ceiling((_val)*unit_conv)
4446 @<Typeset |a| by |c| rule on the current layer@>= {
4447 dvi_number x=to_rule_pixels(a);
4448 dvi_number y=to_rule_pixels(c);
4449 if(quan('L') && a>0 && c>0) typeset_rule_here(x,y);
4450 if(moveaftertyping) {
4451 quan('I')+=x;
4452 horizontal_movement(0);
4456 @ Sometimes you might want DVI units converted to pixels inside of a user
4457 program contained in a DVI file. Here is how it is done.
4459 @<Cases for system commands@>=
4460 @-case 'C': {
4461 // Convert DVI units to pixels
4462 if(stack_ptr->is_string) program_error("Type mismatch");
4463 stack_ptr->number=to_pixels(stack_ptr->number);
4464 break;
4467 @*Layer Rendering. Please note, these numbers are |short|, which means
4468 that you cannot have more than 65536 pixels in width or in height. This
4469 should not be a problem, because even if you have 3000 dots per inch, and
4470 each card is 10 inches long, that is still only 30000 which is less than
4471 half of the available width. (All units here are in pixels.)
4473 In order to save memory, all typeset nodes are stored in one list at
4474 first, and then rendered to a pixel buffer as each layer is being written
4475 out to the \.{PBM} file, and then the buffer can be freed (or reset to
4476 zero) afterwards to save memory.
4478 @<Typedefs@>=
4479 typedef struct {
4480 unsigned short x; // X position on page
4481 unsigned short y; // Y position on page
4482 union {
4483 struct {
4484 unsigned short w; // Width of rule
4485 unsigned short h; // Height of rule
4486 }@+;
4487 data_index c; // Character index in |font_data|
4488 }@+;
4489 unsigned char l; // Layer (high bit set for rules)
4490 } typeset_node;
4492 @ @<Global variables@>=
4493 memory_of(typeset_node) typeset_nodes;
4495 @ @<Initialize memory@>= init_memory(typeset_nodes,8);
4497 @ We also have variables for the layer size (loaded from \.{\\count2}
4498 and \.{\\count3} registers for the current page). If they are both zero,
4499 then nothing will be rendered.
4501 @<Global variables@>=
4502 unsigned short layer_width;
4503 unsigned short layer_height;
4505 @ Here are the subroutines which typeset characters and rules onto the
4506 page buffer. They are not rendered into a picture yet.
4508 @d typeset_new_page() (typeset_nodes.used=0)
4509 @d typeset_rule_here(_w,_h) typeset_rule(quan('I'),quan('J'),(_w),(_h));
4510 @d typeset_char_here(_ch) typeset_char(quan('I'),quan('J'),(_ch));
4512 @-p void typeset_rule(int x,int y,int w,int h) {
4513 data_index n=new_record(typeset_nodes);
4514 @<Ensure |w| and |h| are not too large to fit on the page@>;
4515 typeset_nodes.data[n].x=x;
4516 typeset_nodes.data[n].y=y;
4517 typeset_nodes.data[n].w=w;
4518 typeset_nodes.data[n].h=h;
4519 typeset_nodes.data[n].l=quan('L')|0x80;
4522 @ @<Ensure |w| and |h| are not too large to fit on the page@>= {
4523 if(x+w>layer_width) w=layer_width-x;
4524 if(y+h>layer_height) h=layer_height-y;
4527 @ @-p void typeset_char(int x,int y,data_index c) {
4528 data_index n=new_record(typeset_nodes);
4529 typeset_nodes.data[n].x=x;
4530 typeset_nodes.data[n].y=y;
4531 typeset_nodes.data[n].c=c;
4532 typeset_nodes.data[n].l=quan('L');
4535 @ Here is a variable |image|. This is a pointer to the buffer for the
4536 picture of the current layer, in \.{PBM} format. The internal quantity
4537 \.L should be set now to the largest layer number in use, at the end of
4538 the page, because it is used to determine how many layers must be sent to
4539 the output.
4541 @d image_max (image+layer_size)
4543 @<Global variables@>=
4544 unsigned char*image;
4546 @ @<Render this page@>= {
4547 unsigned int row_size=((layer_width+7)>>3);
4548 unsigned int layer_size=row_size*layer_height;
4549 image=malloc(layer_size+1);
4550 while(quan('L')) {
4551 memset(image,0,layer_size);
4552 @<Read the |typeset_nodes| list and render any applicable nodes@>;
4553 @<Send the current layer to a file@>;
4554 --quan('L');
4556 free(image);
4559 @ @<Read the |typeset_nodes| list and render any applicable nodes@>= {
4560 data_index i;
4561 foreach(i,typeset_nodes) {
4562 if((typeset_nodes.data[i].l&0x7F)==quan('L')) {
4563 if(typeset_nodes.data[i].l&0x80) {
4564 @<Render a rule node@>;
4565 } @+else {
4566 @<Render a character node@>;
4572 @ In order to render a rule node (which is a filled |black| rectangle), it
4573 is split into rows, and each row is split into three parts: the left end,
4574 the filling, and the right end. However, if the width is sufficiently
4575 small, it will fit in one byte and will not have to be split in this way.
4577 There are also some checks to ensure that the entire rectangle will be
4578 within the bounds of the image.
4580 @<Render a rule node@>= {
4581 int y=1+typeset_nodes.data[i].y-typeset_nodes.data[i].h;
4582 int x=typeset_nodes.data[i].x;
4583 int w=typeset_nodes.data[i].w;
4584 if(y<0) y=0;
4585 if(typeset_nodes.data[i].y>=layer_height)
4586 typeset_nodes.data[i].y=layer_height-1;
4587 if((x&7)+w<=8) {
4588 @<Render a small rule node@>;
4589 } @+else {
4590 @<Render a large rule node@>;
4594 @ @<Render a small rule node@>= {
4595 for(;y<=typeset_nodes.data[i].y;y++) {
4596 image[y*row_size+(x>>3)]|=(0xFF>>(x&7))&~(0xFF>>((x&7)+w));
4600 @ @<Render a large rule node@>= {
4601 for(;y<=typeset_nodes.data[i].y;y++) {
4602 unsigned char*p=image+(y*row_size+(x>>3));
4603 *p++|=0xFF>>(x&7); // left
4604 memset(p,0xFF,((x&7)+w)>>3); // filling
4605 p[((x&7)+w)>>3]|=~(0xFF>>((x+w)&7)); // right
4609 @ Character nodes are a bit different. The pictures are already stored,
4610 now we have to paste them into the layer picture. Since they will not
4611 always be aligned to a multiple to eight columns (one byte), it will have
4612 to shift out and shift in.
4614 Again, it is necessary to ensure it doesn't go out of bounds. It has to be
4615 a bit more careful for characters than it does for rules. Also note that
4616 the \.{GF} format does not require that |min_m| and so on are the tightest
4617 bounds possible.
4619 @<Render a character node@>= {
4620 unsigned int ch=typeset_nodes.data[i].c;
4621 unsigned int x=typeset_nodes.data[i].x+font_data.data[ch].min_m;
4622 unsigned int y=typeset_nodes.data[i].y-font_data.data[ch].max_n;
4623 unsigned int z=typeset_nodes.data[i].y-font_data.data[ch].min_n;
4624 unsigned int w=(font_data.data[ch].max_m-font_data.data[ch].min_m)/8+1;
4625 register unsigned char sh=x&7; // shifting amount for right shift
4626 register unsigned char lsh=8-sh; // shifting amount for left shift
4627 unsigned char*p=image+(y*row_size+(x>>3));
4628 unsigned char*q=font_data.data[ch].raster;
4629 @<Cut off the part of character above the top of the layer image@>;
4630 while(y<=z && p+w<image_max) {
4631 @<Render the current row of the character raster@>;
4632 @<Advance to the next row of the character@>;
4636 @ @<Cut off the part of character above the top of the layer image@>= {
4637 if(y<0) {
4638 p-=row_size*y;
4639 q-=w*y;
4640 y=0;
4642 if(p<image) p=image;
4645 @ @<Render the current row of the character raster@>= {
4646 int j;
4647 for(j=0;j<w;j++) {
4648 p[j]|=q[j]>>sh;
4649 p[j+1]|=q[j]<<lsh;
4653 @ @<Advance to the next row of the character@>= {
4654 y++;
4655 q+=w;
4656 p+=row_size;
4659 @ Layer files are output in \.{PBM} format, which is very similar to the
4660 format which this program uses internally. ImageMagick is capable of
4661 reading this format.
4663 @.PBM@>
4664 @^Portable Bitmap@>
4665 @^ImageMagick@>
4666 @^output@>
4668 @<Send the current layer to a file@>= {
4669 FILE*fp;
4670 char filename[256];
4671 sprintf(filename,"P%dL%d.pbm",quan('P'),quan('L'));
4672 fp=fopen(filename,"wb");
4673 fprintf(fp,"P4%d %d ",layer_width,layer_height);
4674 fwrite(image,1,layer_size,fp);
4675 fclose(fp);
4678 @ @<Display the list of typeset nodes@>= {
4679 data_index i;
4680 foreach(i,typeset_nodes) {
4681 if(typeset_nodes.data[i].l&0x80) {
4682 printf("[%d] %dx%d%+d%+d\n",typeset_nodes.data[i].l&0x7F
4683 ,typeset_nodes.data[i].w,typeset_nodes.data[i].h
4684 ,typeset_nodes.data[i].x,typeset_nodes.data[i].y
4686 } @+else {
4687 printf("[%d] %d(%d) %+d%+d\n",typeset_nodes.data[i].l
4688 ,typeset_nodes.data[i].c,font_data.data[typeset_nodes.data[i].c].n
4689 ,typeset_nodes.data[i].x,typeset_nodes.data[i].y
4695 @ @<Display typesetting diagnostics@>= {
4696 int i;
4697 for(i=0;i<32;i++) {
4698 if(type_quan[i]) printf("%c=%d\n",i+'@@',type_quan[i]);
4700 printf("unit_conv: %lld [%d]\n",unit_conv,round(unit_conv));
4701 printf("nodes: %d/%d\n",typeset_nodes.used,typeset_nodes.allocated);
4702 printf("fonts: %d/%d\n",font_data.used,font_data.allocated);
4703 if(dvi_stack) printf("stack: %d\n",dvi_stack_ptr-dvi_stack);
4706 @*Process of ImageMagick. The filename of ImageMagick \.{convert} is found
4707 by using the \.{IMCONVERT} environment variable. The entire command-line
4708 is stored in the \.Q register, with arguments separated by spaces, and it
4709 might be very long.
4711 @^ImageMagick@>
4712 @.IMCONVERT@>
4714 @d add_magick_arg(_val) magick_args.data[new_record(magick_args)]=_val
4716 @<Typedefs@>=
4717 typedef char*char_ptr;
4719 @ @<Global variables@>=
4720 memory_of(char_ptr) magick_args;
4722 @ @<Switch to ImageMagick@>= {
4723 init_memory(magick_args,4);
4724 add_magick_arg("convert"); // |argv[0]| (program name)
4725 @<Add arguments from \.Q register@>;
4726 add_magick_arg(0); // (terminator)
4727 @<Call the ImageMagick executable file@>;
4730 @ The \.Q register will be clobbered here. But that is OK since it will no
4731 longer be used within \TeX nicard.
4733 @<Add arguments from \.Q register@>= {
4734 char*q=registers['Q'].text;
4735 char*p;
4736 while(q && *q) {
4737 p=q;
4738 if(q=strchr(q,' ')) *q++=0;
4739 if(*p) add_magick_arg(p);
4743 @ @<Call the ImageMagick executable file@>= {
4744 char*e=getenv("IMCONVERT");
4745 if(!e) @<Display the arguments and quit@>;
4746 execv(e,magick_args.data);
4747 fprintf(stderr,"Unable to run ImageMagick\n");
4748 return 1;
4751 @ @<Display the arguments and quit@>= {
4752 data_index i;
4753 char*p;
4754 foreach(i,magick_args) if(p=magick_args.data[i]) printf("%s\n",p);
4755 return 0;
4758 @*Main Program. This is where the program starts and ends. Everything else
4759 in the other chapters is started from here.
4761 @<Include files@>=
4762 #include <signal.h>
4763 #include <stdio.h>
4764 #include <stdlib.h>
4765 #include <string.h>
4766 #include <time.h>
4767 #include <unistd.h>
4769 @ @-p int main(int argc,char**argv) {
4770 boolean dvi_mode=0;
4771 @<Set up signal handler@>;
4772 @<Initialize memory@>;
4773 @<Display the banner message@>;
4774 @<Decide whether in DVI reading mode@>;
4775 if(!dvi_mode) @<Open the main input file@>;
4776 @<Initialize the input states@>;
4777 @<Initialize the tables and registers@>;
4778 @<Initialize the random number generator@>;
4779 @<Set registers according to command-line parameters@>;
4780 if(!dvi_mode) @<Process the input files@>;
4781 if(dvi_mode) dvi_mode=read_dvi_file(argv[1]);
4782 @<Call program in \.Z register if necessary@>;
4783 if(!dvi_mode) @<Send |end_transmission| to each card area@>;
4784 @<Write the output files@>;
4785 if(registers['Q'].is_string && dvi_mode &&
4786 (argv[0][0]!='-' || argv[0][1]!='z')) @<Switch to ImageMagick@>;
4787 return 0;
4790 @ @<Display the banner message@>= {
4791 fprintf(stderr,"TeXnicard version %s\n",version_string);
4792 fprintf(stderr,
4793 "This program is free software and comes with NO WARRANTY.\n");
4794 fflush(stderr);
4797 @ @<Set registers according to command-line parameters@>= {
4798 int i;
4799 for(i=2;i<argc;i++) {
4800 registers[i+('0'-2)].is_string=1;
4801 registers[i+('0'-2)].text=strdup(argv[i]);
4805 @ The main input file will be either the terminal, or another file if the
4806 command-line argument is given.
4808 @<Open the main input file@>= {
4809 if(argc>1 && strcmp(argv[1],"-")!=0) {
4810 --current_input_file;
4811 open_input(argv[1]);
4812 } @+else {
4813 current_fp=0;
4814 strcpy(current_filename,"<Teletype>");
4818 @ @<Call program in \.Z register if necessary@>= {
4819 if(registers['Z'].is_string) execute_program(registers['Z'].text);
4822 @ The alternative mode to run this program is DVI mode. DVI mode is
4823 specified by a command-line switch.
4825 @.DVI@>
4827 @<Decide whether in DVI reading mode@>= {
4828 if(argc>1 && argv[1][0]=='-' && argv[1][1]) {
4829 dvi_mode=1;
4830 argv++; @+ argc--;
4831 if(argv[0][1]=='a') {
4832 printing_mode=printing_all_cards;
4833 } @+else if(argv[0][1]=='f') {
4834 printing_mode=printing_list_from_file;
4835 printlistfile=fopen(argv[1],"r");
4836 argv++; @+ argc--;
4837 } @+else if(argv[0][1]=='n') {
4838 printing_mode=printing_list;
4839 printlisttext=argv[1];
4840 argv++; @+ argc--;
4841 } @+else if(argv[0][1]=='z') {
4842 printing_mode=printing_list;
4843 printlisttext="";
4848 @*Signal Handlers. The |SIGSEGV| signal should be handled in case
4849 something goes wrong in the program and it causes a segmentation fault, it
4850 should attempt to recover what you have before terminating, in order to be
4851 better at diagnosing the error.
4853 @<Set up signal handler@>= {
4854 signal(SIGSEGV,handle_crash);
4857 @ Some things will be more careful here to ensure not to cause the error
4858 again (if it does, it will just quit, though).
4860 @-p void handle_crash(int sig) {
4861 signal(SIGSEGV,SIG_DFL);
4862 @#fprintf(stderr,"\nFatal signal error (%d)\n",sig);
4863 fprintf(stderr,"cur_state=%d\ncur_name=%d\ncur_data=%d\n",
4864 cur_state,cur_name,cur_data);
4865 if(current_input_file>=input_files && current_input_file<input_files
4866 +max_input_stack) @<Display input stack after a crash@>;
4867 fprintf(stderr,"Program stack level: %d\n",stack_ptr-stack);
4868 fprintf(stderr,"Save stack level: %d\n",save_stack_ptr-save_stack);
4869 @#exit(3);
4872 @ @<Display input stack after a crash@>= {
4873 for(;;) {
4874 fprintf(stderr,"File %s line %d\n",current_filename,current_line);
4875 if(current_input_file--==input_files) break;
4879 @*The Future. Here are some ideas for future versions of this program:
4881 $\bullet$ A customizable Inform7-like parser, that would compile into a C
4882 code, so that you can play the cards on rule-enforcing computer programs.
4883 @^Inform@>
4885 $\bullet$ A database to keep track of how many copies of a card have been
4886 sold, for inventory purposes.
4887 @^commercial viability@>
4889 $\bullet$ Full text search, for things such as the Oracle text search.
4890 @^Oracle@>
4892 $\bullet$ Allow more than 256 fonts in one card set.
4894 $\bullet$ Unicode input (UTF-8).
4896 $\bullet$ Built-in typesetting (using some of the algorithms of \TeX) and
4897 image manipulation, so that there is no dependence on external programs,
4898 and everything can be done in one pass.
4900 $\bullet$ Big spider!
4901 @^arachnids@>
4902 @^spider@>
4904 @*Bibliography.
4906 \count255=0 %
4907 \long\def\Par{\csname par\endcsname}%
4908 \loop\ifnum\count255<\bibliocount%
4909 \advance\count255 by 1
4910 \Par$^{[\the\count255]}$\csname biblio \the\count255\endcsname\Par%
4911 \repeat%
4913 @*Index. Here you can find references to the definition and use of all the
4914 variables, subroutines, etc.\ used in this program, as well as a few other
4915 things of interest. Underlined entries indicate where it is defined.
4917 {\bf Important note:} All the numbers in this index are section numbers,
4918 not page numbers.
4920 % End of file "texnicard.w"