Process of ImageMagick
[TeXnicard.git] / texnicard.w
blob8c53942777d79c3115bf569b3a13c5dd3430f75a
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;
1336 @ @<Put brackets around string at top of stack@>= {
1337 char*buf=malloc(strlen(stack_ptr->text)+3);
1338 sprintf(buf,"[%s]",stack_ptr->text);
1339 free(stack_ptr->text);
1340 stack_ptr->text=buf;
1343 @ @<Convert top of stack to string representation of a number@>= {
1344 char buf[32];
1345 sprintf(buf,"%d",stack_ptr->number);
1346 stack_drop();
1347 push_string(buf);
1350 @ Here is how the ``Arithmetic IF'' command works: On the stack you have
1351 any three values at the top, and a number underneath it. Those are all
1352 removed, except one of the three values which is selected based on the
1353 sign of the number (the condition value).
1355 @<Cases for condition/compare commands@>=
1356 @-case 'i': {
1357 // Arithmetic IF
1358 @<Do the ``Arithmetic IF''@>;
1359 break;
1361 @-case '&': {
1362 // Bitwise AND
1363 int n=pop_num();
1364 if(stack_ptr[0].is_string || stack_ptr[1].is_string)
1365 program_error("Number expected");
1366 stack_ptr->number&=n;
1367 break;
1370 @ Do you like this algorithm? Is this a real question?
1372 @^strange codes@>
1374 @<Do the ``Arithmetic IF''@>= {
1375 register_value v=stack_ptr[-3];
1376 int n=v.number;
1377 n=-(n<0?2:!n);
1378 stack_ptr[-3]=stack_ptr[n];
1379 stack_ptr[n]=v;
1380 stack_drop();@+stack_drop();@+stack_drop();
1383 @ @<Cases for local registers commands@>=
1384 @-case '<': {
1385 // Save locals
1386 @<Save local registers to the save stack@>;
1387 break;
1389 @-case '>': {
1390 // Restore locals
1391 @<Load local registers from the save stack@>;
1392 break;
1395 @ When there is a program error (such as stack underflow), the following
1396 subroutine is used to handle it.
1398 @d program_error(_text) program_error_(prog,ptr,_text)
1400 @-p void program_error_(char*prog,char*ptr,char*msg) {
1401 fprintf(stderr,"Error in %s on line %d",current_filename,current_line);
1402 fprintf(stderr,"\n! %s\ns%dS%dp%d near \"",msg,stack_ptr-stack,
1403 save_stack_ptr-save_stack,ptr-prog);
1404 @<Display the codes near the part that caused the error@>;
1405 fprintf(stderr,"\"\n");
1406 exit(1);
1409 @ @<Display the codes near the part that caused the error@>= {
1410 char buf[32];
1411 char*p=ptr-5;
1412 int i;
1413 if(p<prog || p>ptr) p=prog;
1414 for(i=0;p+i<=ptr && p[i];i++) buf[i]=p[i];
1415 buf[i]=0;
1416 fprintf(stderr,"%s",buf);
1419 @*Tables and registers. The tables must be stored here. There are 128
1420 tables with 256 entries each, each of which can store one byte of data.
1421 These tables are used for converting uppercase/lowercase, for deciding
1422 which characters need to be escaped in \TeX, and so on.
1424 The purposes of the built-in registers are also described in this chapter.
1425 The tables and registers named by uppercase letters are for system use.
1426 The tables and registers named by lowercase can be used by the user.
1428 @<Global variables@>=
1429 unsigned char tables[128][256];
1431 @ Here are the uses of the built-in tables and registers:
1432 @^built-in registers@>
1433 @^built-in tables@>
1435 Register \.A: The current position in the current cards area.
1437 Register \.C: The current cards area.
1439 Register \.D: Dots per inch, multiplied by 100.
1441 Register \.E: The escape character for \TeX. If this is a string, the
1442 entire string is the prefix; otherwise, it is a ASCII number of the
1443 character to be used.
1445 Register \.K: Index number for last keyword entry added. Also used when
1446 dealing with keyword operation commands, and when a keyword is matched in
1447 a pattern.
1449 Register \.P: The current pattern area.
1451 Register \.Q: The parameters for the ImageMagick command-line, separated
1452 by spaces.
1454 Register \.T: Alignment tab character for \TeX. Same considerations apply
1455 as the \.E register.
1457 Register \.U: A code to execute for a deck specification enrty with \.x
1458 flag set.
1460 Register \.V: The version number of this program.
1462 Register \.W: A code which pushes the whatsit replacements onto the stack.
1463 It is initialized to a blank string before each line in a card area. It
1464 should push the replacements in the reverse order of the whatsits, so you
1465 could use a code like this, for example: \.{[(Abracadabra)]lW+sW}
1467 Register \.X: Horizontal coordinate across the page (in pixels).
1469 Register \.Y: Vertical coordinate across the page (in pixels).
1471 Register \.Z: Should be set to a code to execute after doing everything
1472 else (but before writing output files).
1474 Table \.E: Indicates which characters need escaped for \TeX.
1476 Table \.G: Table containing information for sorting and grouping.
1478 Table \.L: Conversion to lowercase.
1480 Table \.S: Information for natural sorting.
1482 Table \.U: Conversion to uppercase.
1484 Table \.W: Table for word form rules. Zero means a letter, one means a
1485 word separator, two means use to mark beginning of a word, three means use
1486 to mark the end of a word. In this program, it is advantageous to use the
1487 fact that zero means word characters (such as letters), and nonzero means
1488 nonword characters.
1490 @d init_register(_reg,_val) do@+{
1491 registers[_reg].is_string=0;
1492 registers[_reg].number=(_val);
1493 }@+while(0)@;
1495 @d init_register_str(_reg,_val) do@+{
1496 registers[_reg].is_string=1;
1497 registers[_reg].text=strdup(_val);
1498 }@+while(0)@;
1500 @<Initialize the tables and registers@>= {
1501 int i;
1502 for(i=0;i<256;i++) init_register(i,0);
1503 init_register('E','\\');
1504 init_register('V',version_number);
1505 @<Initialize table of alphabetical case conversion@>;
1508 @ @<Initialize table of alphabetical case conversion@>= {
1509 for(i=0;i<256;i++) tables['L'][i]=tables['U'][i]=i;
1510 for(i='A';i<='Z';i++) {
1511 tables['L'][i]=i+'a'-'A';
1512 tables['U'][i+'a'-'A']=i;
1516 @ @<Display the contents of table |*++ptr|@>= {
1517 int t=*++ptr;
1518 int i;
1519 for(i=0;i<256;i++) {
1520 printf("%c%c",tables[t][i]?'+':'.',@|
1521 (tables[t][i]<0x7F && tables[t][i]>=' ')?tables[t][i]:'.'
1523 if((i&0x0F)==0x0F) printf("\n");
1525 for(i=' ';i<0x7F;i++) if(tables[t][i]) printf("%c",i);
1528 @*Diagnostics. Here is diagnostics commands. These are used to display the
1529 internal information on standard output, so that you can check how these
1530 things are working. (You can also use \.{gdb} for debugging purposes.) A
1531 diagnostics command always starts with a question mark, and is then
1532 followed by one more character indicating the type of diagnostics
1533 requestsed. (Some are followed by an additional character after that.)
1535 @<Do a diagnostics command@>= {
1536 switch(*++ptr) {
1537 case 'c': @<Display the sorted card list@>; @+break;
1538 case 'd': @<Display the deck list@>; @+break;
1539 case 'f': @<Display font information@>; @+break;
1540 case 'k': @<Display the list of keywords@>; @+break;
1541 case 'n': @<Display the list of names@>; @+break;
1542 case 'p': @<Display the list of patterns@>; @+break;
1543 case 's': @<Display the contents of the stack@>; @+break;
1544 case 't': @<Display the contents of table |*++ptr|@>; @+break;
1545 case 'w': @<Display the list of word form rules@>; @+break;
1546 case 'x': @<Display the list of typeset nodes@>; @+break;
1547 case 'y': @<Display typesetting diagnostics@>; @+break;
1548 default: program_error("Unknown type of diagnostics");
1552 @ One subroutine is used here for displaying strings with escaped, so that
1553 it will display on a terminal without messing it up or omitting the
1554 display of some characters.
1556 @-p void display_string(char*s) {
1557 for(;*s;s++) {
1558 if(*s<' ' || *s==0x7F) {
1559 printf("^%c",0x40^*s);
1560 } @+else {
1561 printf("%c",*s);
1566 @ @<Display the contents of the stack@>= {
1567 register_value*p;
1568 for(p=stack;p<=stack_ptr;p++) {
1569 if(p->is_string) {
1570 printf("[");
1571 display_string(p->text);
1572 printf("]\n");
1573 } @+else {
1574 printf("%d\n",p->number);
1579 @ More of the diagnostics functions are included in the chapters for the
1580 data structures which it is displaying.
1582 @*Pattern Matching. Now, finally, after the chapter about patterns, and
1583 going through many other things in between, comes to the chapter in which
1584 patterns are actually being matched.
1586 One structure is used here for the information about how to match it, and
1587 what has been matched from it. The parameter |num_capture| is how many
1588 captured parts there are, and the |start| and |end| arrays store the index
1589 into the |src| string of where the matches are. The entire matched part is
1590 indicated by |start[0]| and |end[0]| (note always |start[0]==0|).
1592 @<Typedefs@>=
1593 typedef struct {
1594 char*src;
1595 char*truesrc; // used for checking true beginning of the line
1596 char*pattern;
1597 unsigned int category;
1598 int start[16];
1599 int end[16];
1600 int num_capture;
1601 } match_info;
1603 @ This first one just matches one pattern against a string to see if it
1604 matches. It returns true if it does match. (It is somewhat inefficient.)
1606 @-p boolean match_pattern(match_info*mat) {
1607 char*src; // current start of source string
1608 char*ptr; // pointer into source string |src|
1609 char*pptr; // pointer into pattern string
1610 src=mat->src; @+ mat->num_capture=0; @+ pptr=mat->pattern; @+ ptr=src;
1611 @<Execute the pattern on the string |src|@>;
1612 mismatch: return 0;
1615 @ This loop executes each command in the pattern in attempt to match each
1616 character. In case of mismatch, it will break out of this loop, and
1617 continue with the next iteration of the loop in the previous section.
1619 @d not_a_marker !(pptr[-1]&0x80)
1621 @<Execute the pattern on the string |src|@>= {
1622 while(*pptr) {
1623 switch(*pptr++) {
1624 case begin_capture:
1625 mat->start[++mat->num_capture]=ptr-mat->src; @+break;
1626 case end_capture: mat->end[mat->num_capture]=ptr-mat->src; @+break;
1627 case match_keyword: @<Do |match_keyword|@>; @+break;
1628 case match_table:
1629 if(!tables[*pptr++][*ptr++]) goto mismatch; @+break;
1630 case optional_table: ptr+=!!tables[*pptr++][*ptr]; @+break;
1631 case failed_match: goto mismatch;
1632 case jump_table:
1633 if(!(pptr=strchr(mat->pattern,0x80|tables[*pptr++][*ptr++])))
1634 goto mismatch;
1635 @+break;
1636 case successful_match: @<Do |successful_match|@>;
1637 case back_one_space: if(ptr--==mat->src) goto mismatch; @+break;
1638 case forward_one_space: if(!*ptr++) goto mismatch; @+break;
1639 case match_left_side: if(ptr!=mat->truesrc) goto mismatch; @+break;
1640 case match_right_side: if(*ptr>=' ') goto mismatch; @+break;
1641 default: if(not_a_marker && pptr[-1]!=*ptr++) goto mismatch;
1646 @ @<Do |successful_match|@>= {
1647 mat->start[0]=0;
1648 mat->end[0]=ptr-mat->src;
1649 return 1;
1652 @ And now, the next part matches from an area and changes the string in
1653 place, possibly by reallocating it. The |src| pointer passed to this
1654 function should be one that can be freed!
1656 @-p char*do_patterns(char*src,int area) {
1657 pattern_data*pat;
1658 match_info mat;
1659 int index=0; // index into |src| string
1660 @<Cancel if there isn't a pattern area@>;
1661 continue_matching:
1662 if(index>=strlen(src)) return src;
1663 pat=pattern_areas.data+name_info(area).pattern_area;
1664 for(;;) {
1665 @<Fill up the |mat| structure for testing the current pattern@>;
1666 if(mat.pattern && match_pattern(&mat)) {
1667 @<Push the captured strings to the stack@>;
1668 @<Call the subroutine associated with this pattern@>;
1669 if(stack_ptr->is_string) {
1670 @<Replace the matched part from the stack and fix the |index|@>;
1671 } @+else {
1672 index+=mat.end[0];
1674 stack_drop();
1675 goto continue_matching;
1677 @<Select the next pattern in this area or |break|@>;
1679 index++; @+ goto continue_matching;
1682 @ @<Cancel if there isn't a pattern area@>= {
1683 if(area<256) return src;
1684 if(!name_info(area).has_pattern_area) return src;
1687 @ @<Fill up the |mat| structure for testing the current pattern@>= {
1688 mat.src=src+index;
1689 mat.truesrc=src;
1690 mat.pattern=pat->text;
1691 mat.category=pat->category;
1694 @ @<Push the captured strings to the stack@>= {
1695 int i;
1696 for(i=mat.num_capture;i;i--) {
1697 push_string(src+index+mat.start[i]);
1698 stack_ptr->text[mat.end[i]-mat.start[i]]=0;
1702 @ @<Call the subroutine associated with this pattern@>= {
1703 execute_program(names.data[pat->subroutine].value.text);
1706 @ The memory allocated is probably more than is needed, but this way is
1707 simpler. It is always sufficient amount, though. Think about it.
1709 @^thought@>
1711 @<Replace the matched part from the stack and fix the |index|@>= {
1712 char*q=malloc(strlen(src)+strlen(stack_ptr->text)+1);
1713 strcpy(q,src);
1714 sprintf(q+index,"%s%s",stack_ptr->text,src+index+mat.end[0]);
1715 free(src);
1716 src=q;
1717 index+=strlen(stack_ptr->text);
1720 @ @<Select the next pattern in this area or |break|@>= {
1721 if(pat->next==none) break;
1722 pat=pattern_areas.data+pat->next;
1725 @ Finally, there is a command |'M'| to do a pattern matching and
1726 replacement with a string, inside of a stack subroutine code.
1728 @<Cases for system commands@>=
1729 @-case 'M': {
1730 // do pattern matching and replacement
1731 int n=pop_num();
1732 if(!stack_ptr->is_string) program_error("Type mismatch");
1733 stack_ptr->text=do_patterns(stack_ptr->text,n);
1734 break;
1737 @*Matching Keywords. Codes for matching keywords have been placed in
1738 another chapter, instead of making the previous chapter longer.
1740 So now we can see how it is matched keywords in a pattern code.
1742 @<Do |match_keyword|@>= {
1743 match_info m;
1744 char mstr[512];
1745 char t=*pptr++; // indicate which table to use
1746 data_index best=none;
1747 int best_length=-1;
1748 @<Try matching each keyword belonging to the category@>;
1749 if(best==none) goto mismatch;
1750 @<Adjust the \.K register for this keyword match@>;
1751 ptr+=m.end[0];
1754 @ @<Adjust the \.K register for this keyword match@>= {
1755 if(registers['K'].is_string) free(registers['K'].text);
1756 registers['K'].is_string=0;
1757 registers['K'].number=best;
1760 @ When matching keywords, all of them will be tried, in case there are
1761 better candidates for the search (bigger is better (so, for example,
1762 |"Power of One"| will override |"Power"|); failing that, later ones are
1763 better than earlier ones (so that user files can override keywords in
1764 template files)).
1766 @^Courtenay, Bryce@>
1767 @^Houghton, Israel@>
1768 @^Luce, Ron@>
1770 @<Try matching each keyword belonging to the category@>= {
1771 data_index i;
1772 foreach(i,keywords) {
1773 if(keywords.data[i].category&mat->category &&
1774 strlen(keywords.data[i].match)>=best_length) {
1775 @<Set up the |match_info| structure called |m|@>;
1776 @<Attempt applying this keyword match@>;
1781 @ @<Set up the |match_info| structure called |m|@>= {
1782 sprintf(mstr,"%s%c%c%c",
1783 keywords.data[i].match,match_table,t,successful_match);
1784 m.src=m.truesrc=ptr;
1785 m.pattern=mstr;
1788 @ @<Attempt applying this keyword match@>= {
1789 if(match_pattern(&m)) {
1790 best=i;
1791 best_length=strlen(keywords.data[i].match);
1795 @*Sorting and Grouping. The card lists can be sorted/grouped using these
1796 commands, which are generally used by macros that create the records for
1797 the cards in the card areas.
1799 @<Cases for system commands@>=
1800 @-case 'n': {
1801 // Add a new list entry
1802 data_index n=new_record(card_list);
1803 card_list.data[n].token_ptr=
1804 card_areas.data[set_card_area(registers['C'].number)].used
1806 break;
1808 @-case 'f': {
1809 // Set a field value of the list entry
1810 data_index n=card_list.used-1;
1811 int x=pop_num();
1812 int y=pop_num();
1813 if(n==none) program_error("No card list is available");
1814 card_list.data[n].field[x&31]=y;
1815 break;
1818 @ Other than the commands to make the list entries above, there must be,
1819 of course, the actual sorting and grouping being done!
1821 Sorting and grouping are controlled by the \.G table. Starting from a
1822 given offset (added), you use thirty-two entries for the thirty-two
1823 fields.
1825 @<Cases for system commands@>=
1826 @-case 'G': {
1827 // Sort the list
1828 sorting_table_offset=pop_num();
1829 qsort(card_list.data,card_list.used,sizeof(list_entry),list_compare);
1830 @<Mark positions in the sorted list@>;
1831 break;
1834 @ @<Global variables@>=
1835 int sorting_table_offset;
1837 @ This is the compare function for the list sorting. It is also worth to
1838 notice here what values belong in the \.G table.
1840 @d no_sort 0
1841 @d primary_ascending 'A'
1842 @d primary_descending 'Z'
1843 @d secondary_ascending 'a'
1844 @d secondary_descending 'z'
1845 @d record_sorted_position 'R'
1847 @d G_table(_field) (tables['G'][((sorting_table_offset+(_field))&0xFF)])
1848 @d p1s ((list_entry*)p1)
1849 @d p2s ((list_entry*)p2)
1851 @-p int list_compare(const void*p1,const void*p2) {
1852 @<Compare using fields indicated by \.G table@>;
1853 @<Compare using the card's name and the \.S table@>;
1854 @<Compare using the order in which the cards are typed in@>;
1855 return 0; // This can't, but will, happen.
1858 @ @<Compare using fields indicated by \.G table@>= {
1859 int i;
1860 for(i=0;i<32;i++) if(p1s->field[i]!=p2s->field[i]) {
1861 if(G_table(i)==primary_ascending) {
1862 return (p1s->field[i]>p2s->field[i])?1:-1;
1863 } @+else if(G_table(i)==primary_descending) {
1864 return (p1s->field[i]<p2s->field[i])?1:-1;
1867 for(i=0;i<32;i++) if(p1s->field[i]!=p2s->field[i]) {
1868 if(G_table(i)==secondary_ascending) {
1869 return (p1s->field[i]>p2s->field[i])?1:-1;
1870 } @+else if(G_table(i)==secondary_descending) {
1871 return (p1s->field[i]<p2s->field[i])?1:-1;
1876 @ When all else fails, \strike{play dead} use the order in which the cards
1877 have been typed in. This is how it is made stable, and that you can get
1878 the same results on any computer.
1880 @^Smith, Steve@>
1882 @<Compare using the order in which the cards...@>= {
1883 if(p1s->token_ptr>p2s->token_ptr) return 1;
1884 if(p1s->token_ptr<p2s->token_ptr) return -1;
1887 @ The last thing to do after sorting, is mark positions in the list if it
1888 is requested to do so.
1890 @<Mark positions in the sorted list@>= {
1891 data_index i;
1892 int j;
1893 for(j=0;j<16;j++) {
1894 if(G_table(j)==record_sorted_position) {
1895 foreach(i,card_list) card_list.data[i].field[j]=i;
1900 @ @<Display the sorted card list@>= {
1901 data_index i;
1902 int j;
1903 foreach(i,card_list) {
1904 printf("%d=[ ",card_list.data[i].token_ptr);
1905 for(j=0;j<16;j++) printf("%d ",card_list.data[i].field[j]);
1906 printf("]\n");
1910 @*Natural Sorting. A natural compare algorithm is used here. It is a
1911 generalization of Martin Pool's algorithm\biblio{Pool, Martin. ``Natural
1912 Order String Comparison''. {\tt
1913 http://sourcefrog.net/projects/natsort/}.}.
1915 The \.S table maps from character tokens to the sorting specifications.
1916 Name tokens are converted to |whatsit| when looking up in this table.
1918 Tokens are grouped into digits, letters, and priority letters. There are
1919 also some extras, such as spaces and radix point. A string of consecutive
1920 digits is treated as numeric, so a number with more digits comes after a
1921 number with less digits.
1923 Priority letters are used mainly for sorting roman numerals. Two or more
1924 consecutive priority letters are considered as a group, otherwise they are
1925 treated in the same way as ordinary letters. A group is ranked with the
1926 letters latest in the alphabet, so for example, if |'I'| and |'X'| are
1927 priority, then |"IX"| is placed between |"W"| and |"X"|. This way, all
1928 roman numerals from I to XXXIX will be sorted correctly.
1930 @^natural compare@>
1931 @^Pool, Martin@>
1933 @d nat_end_low 0
1934 @d nat_end_high 1
1935 @d nat_space 2
1936 @d nat_ignore 3
1937 @d nat_radix_point 4
1939 @d nat_digit_zero 64 // digits go up to 127
1940 @d nat_first_letter 128 // letters go up to 191
1941 @d nat_first_priority_letter 192 // priority letters go up to 255
1942 @d nat_high_value 256
1944 @<Compare using the card's name and the \.S table@>= {
1945 token*pa=card_areas.data[set_card_area(registers['C'].number)].tokens
1946 +p1s->token_ptr;
1947 token*pb=card_areas.data[set_card_area(registers['C'].number)].tokens
1948 +p2s->token_ptr;
1949 boolean fractional=0; // Are we reading digits after a radix point?
1950 int a,b,c;
1951 for(;;pa++,pb++) {
1952 begin_natural_compare_loop: @/
1953 a=tables['S'][*pa>=256?whatsit:*pa];
1954 @+ b=tables['S'][*pb>=256?whatsit:*pb];
1955 @<Skip over leading spaces and/or zeros@>;
1956 @<Process a run of digits@>;
1957 @<Check if the end of either string is reached@>;
1958 @<Check for a radix point@>;
1959 @<Process priority letters@>;
1960 @<Check if the current positions of each string sufficiently differ@>;
1964 @ @<Skip over leading spaces and/or zeros@>= {
1965 while(a==nat_space||a==nat_ignore||(!fractional&&a==nat_digit_zero)) {
1966 int aa=tables['S'][pa[1]>=256?whatsit:pa[1]];
1967 if(a!=nat_ignore) fractional=0;
1968 if(!fractional && a==nat_digit_zero
1969 && aa>=nat_digit_zero && aa<nat_first_letter) break;
1970 pa++; @+ a=tables['S'][*pa>=256?whatsit:*pa];
1972 while(b==nat_space||b==nat_ignore||(!fractional&&b==nat_digit_zero)) {
1973 int bb=tables['S'][pa[1]>=256?whatsit:pa[1]];
1974 if(b!=nat_ignore) fractional=0;
1975 if(!fractional && b==nat_digit_zero
1976 && bb>=nat_digit_zero && bb<nat_first_letter) break;
1977 pb++; @+ b=tables['S'][*pb>=256?whatsit:*pb];
1981 @ @<Process a run of digits@>= {
1982 if(a>=nat_digit_zero&&a<nat_first_letter&&
1983 b>=nat_digit_zero&&b<nat_first_letter) {
1984 if((c=(fractional?compare_left:compare_right)(pa,pb))) return c;
1985 @<Skip the run of digits, since they are the same@>;
1986 fractional=0;
1989 @^strange codes@>
1991 @ Compare two left-aligned numbers: the first to have a different value
1992 wins. This function and |compare_right| are basically equivalent, there
1993 are only a few differences (this one is the simpler one).
1995 @-p int compare_left(token*pa,token*pb) {
1996 int a,b;
1997 for(;;pa++,pb++) {
1998 a=tables['S'][*pa>=256?whatsit:*pa];
1999 @+ b=tables['S'][*pb>=256?whatsit:*pb];
2000 @<Skip over ignored characters@>;
2001 @<If neither |a| nor |b| is digit, |break|@>;
2002 @<If one is a digit and the other isn't, the longest run wins@>;
2003 @<If both are different digits, the greater one wins@>;
2005 return 0;
2008 @ The longest run of digits wins. That aside, the greatest value wins, but
2009 we can't know that it will until we've scanned both numbers to know they
2010 have the same magnitude, so we remember it in |bias|.
2012 @-p int compare_right(token*pa,token*pb) {
2013 int a,b;
2014 int bias=0;
2015 for(;;pa++,pb++) {
2016 a=tables['S'][*pa>=256?whatsit:*pa];
2017 @+ b=tables['S'][*pb>=256?whatsit:*pb];
2018 @<Skip over ignored characters@>;
2019 @<If neither |a| nor |b| is digit, |break|@>;
2020 @<If one is a digit and the other isn't, the longest run wins@>;
2021 @<If both are digits, set the |bias|@>;
2023 return bias;
2026 @ Ignored characters might be commas for grouping digits into thousands.
2028 @<Skip over ignored characters@>= {
2029 while(a==nat_ignore) {
2030 pa++; @+ a=tables['S'][*pa>=256?whatsit:*pa];
2032 while(b==nat_ignore) {
2033 pb++; @+ b=tables['S'][*pb>=256?whatsit:*pb];
2037 @ @<If neither |a| nor |b| is digit, |break|@>= {
2038 if(!(a>=nat_digit_zero&&a<nat_first_letter)&&
2039 !(b>=nat_digit_zero&&b<nat_first_letter)) break;
2042 @ @<If one is a digit and the other isn't, the longest run wins@>= {
2043 if(!(a>=nat_digit_zero&&a<nat_first_letter)) return -1;
2044 if(!(b>=nat_digit_zero&&b<nat_first_letter)) return 1;
2047 @ @<If both are different digits, the greater one wins@>= {
2048 if(a!=b) return a-b;
2051 @ @<If both are digits, set the |bias|@>= {
2052 if(a!=b && !bias) bias=(a<b)?-1:1;
2055 @ @<Skip the run of digits, since they are the same@>= {
2056 while(a>=nat_digit_zero&&a<nat_first_letter) {
2057 pa++; @+ pb++; @+ a=tables['S'][*pa>=256?whatsit:*pa];
2059 b=tables['S'][*pb>=256?whatsit:*pb];
2062 @ @<Check if the end of either string is reached@>= {
2063 if(a==nat_end_low && b>nat_end_high) return -1;
2064 if(b==nat_end_low && a>nat_end_high) return 1;
2065 if(a==nat_end_high && b>nat_end_high) return 1;
2066 if(b==nat_end_high && a>nat_end_high) return -1;
2067 if(a<=nat_end_high && b<=nat_end_high) break; // tied
2070 @ A radix point must be followed by a digit, otherwise it is considered to
2071 be punctuation (and ignored). Radix points come before digits in the
2072 sorting order (|".5"| comes before |"5"|).
2074 @<Check for a radix point@>= {
2075 if(a==nat_radix_point && b==nat_radix_point) {
2076 int aa=tables['S'][pa[1]>=256?whatsit:pa[1]];
2077 int bb=tables['S'][pb[1]>=256?whatsit:pb[1]];
2078 if(aa>=nat_digit_zero&&aa<nat_first_letter
2079 &&bb>=nat_digit_zero&&bb<nat_first_letter) fractional=1;
2080 } @+else if(a==nat_radix_point) {
2081 int aa=tables['S'][pa[1]>=256?whatsit:pa[1]];
2082 if(!(aa>=nat_digit_zero&&aa<nat_first_letter)) {
2083 pa++; goto begin_natural_compare_loop;
2085 } @+else if(b==nat_radix_point) {
2086 int bb=tables['S'][pb[1]>=256?whatsit:pb[1]];
2087 if(!(bb>=nat_digit_zero&&bb<nat_first_letter)) {
2088 pb++; goto begin_natural_compare_loop;
2093 @ This is used so that |"IX"| can be sorted between |"VIII"| and |"X"|. In
2094 normal alphabetical order, |"IX"| sorts before |"V"|. This algorithm makes
2095 it so that doesn't happen. For example: |a| is |'I'| and |aa| (the
2096 character after |a| in the text) is |'X'| (the check |aa>a| ensures that
2097 it too is priority, in addition to checking that |a| represents a negative
2098 part of a roman number), and |b| is |'V'|. Now, since |'V'| comes between
2099 |'I'| and |'X'| in the alphabetical order, the condition is checked to be
2100 valid and it overrides the later check.
2102 @<Process priority letters@>= {
2103 if(a>=nat_first_priority_letter) {
2104 int aa=tables['S'][pa[1]>=256?whatsit:pa[1]];
2105 if(aa>a && b>=nat_first_letter && (b&63)>(a&63) && (b&63)<(aa&63))
2106 return 1;
2108 if(b>=nat_first_priority_letter) {
2109 int bb=tables['S'][pb[1]>=256?whatsit:pb[1]];
2110 if(bb>b && a>=nat_first_letter && (a&63)>(b&63) && (a&63)<(bb&63))
2111 return -1;
2115 @ At this point, |a| and |b| will both be |@[@]>=nat_radix_point|. Numbers
2116 always come after letters (this rule is designed so that when a radix
2117 point is found after a number, it will make a larger number; otherwise it
2118 will be followed by a letter and therefore the one followed by the letter
2119 is lesser since it has no fractional part to make it greater).
2121 @<Check if the current positions of each string suffic...@>= {
2122 if(a>=nat_first_priority_letter) a-=64;
2123 if(b>=nat_first_priority_letter) b-=64;
2124 if(a<nat_first_letter) a+=128;
2125 if(b<nat_first_letter) b+=128;
2126 if(a!=b) return (a<b)?-1:1;
2129 @*Statistics. After the card lists are created and sorted and grouped, it
2130 can make statistics from them. It can be just a plain list, or it can be
2131 in summary of groups, measuring count, minimum, maximum, mean, median, and
2132 so on.
2134 First we do the simple iteration.
2136 @^mean@>
2137 @^median@>
2138 @^groups@>
2139 @^minimum@>
2140 @^maximum@>
2142 @<Cases for system commands@>=
2143 @-case 'V': {
2144 // Iterate the card list
2145 data_index i;
2146 char*q=pop_string();
2147 if(!stack_ptr[1].is_string) program_error("Type mismatch");
2148 foreach(i,card_list) {
2149 push_num(card_list.data[i].token_ptr);
2150 store('A');
2151 execute_program(q);
2153 free(q);
2154 break;
2156 @-case 'v': {
2157 // Read a field from the card list
2158 int x=pop_num()&31;
2159 int y=0;
2160 data_index i;
2161 foreach(i,card_list) {
2162 if(registers['A'].number==card_list.data[i].token_ptr)
2163 y=card_list.data[i].field[x];
2165 push_num(y);
2166 break;
2169 @ That was simple, see? Now to do gathering statistics of summary of
2170 groups, which is a bit more complicated. The list is expected to be sorted
2171 by the group field primary, and the statistics field ascending as
2172 secondary, in order to make the correct calculation of the fields.
2174 @<Cases for system commands@>=
2175 @-case 'g': {
2176 // Gather statistics of groups
2177 data_index i,si=0;
2178 int x=pop_num()&31; // field for grouping
2179 int y=pop_num()&31; // field to measure statistics with
2180 int sum1,sum2; // running totals of $s_1$ and $s_2$
2181 sum1=sum2=0;
2182 char*q=pop_string(); // code to execute for each group
2183 if(!stack_ptr[1].is_string) program_error("Type mismatch");
2184 foreach(i,card_list) {
2185 if(card_list.data[i].field[x]!=card_list.data[si].field[x]) {
2186 @<Send the results of the current group@>;
2187 sum1=sum2=0; @+ si=i;
2189 @<Add to the running totals@>;
2191 @<Send the results of the current group@>;
2192 free(q);
2193 break;
2196 @ Running totals are kept for two quantities called $s_1$ and $s_2$. There
2197 is also $s_0$, but that can be calculated easily using subtraction, so
2198 there is no need to keep a running total. If the sample values are denoted
2199 $x_k$, the following equation represents the running totals:
2200 $$s_j=\sum_{k=1}^N{x_k^j}$$ (note that $s_0=N$.)
2202 @^mathematics@>
2204 @<Add to the running totals@>= {
2205 sum1+=card_list.data[i].field[y];
2206 sum2+=card_list.data[i].field[y]*card_list.data[i].field[y];
2209 @ Now we will send the results and call |q|. The results are sent to the
2210 stack in the following order: $s_0$, $s_1$, $s_2$, $Q_0$, $2Q_2$, $Q_4$
2211 (where $Q_0$ is the minimum, $Q_2$ the median, and $Q_4$ the maximum).
2213 From these results, it is then possible to calculate the standard
2214 deviation: $$\sigma={1\over s_0}\sqrt{s_0s_2-s_1^2}$$ and
2215 $$s=\sqrt{s_0s_2-s_1^2\over s_0(s_0-1)}.$$
2217 @^mathematics@>
2219 @<Send the results of the current group@>= {
2220 push_num(i-si); // $s_0$
2221 push_num(sum1); // $s_1$
2222 push_num(sum2); // $s_2$
2223 push_num(card_list.data[si].field[y]); // $Q_0$
2224 push_num(
2225 card_list.data[(si+i)/2].field[y]+card_list.data[(si+i+1)/2].field[y]
2226 ); // $2Q_2$
2227 push_num(card_list.data[i-1].field[y]); // $Q_4$
2228 @# push_num(card_list.data[si].token_ptr); @+ store('A');
2229 execute_program(q);
2232 @*Random Pack Generation. Now the codes so that it can create random packs
2233 (such as booster packs) by using the card lists and deck lists.
2235 A command |'P'| is used for evaluation of a deck list. It expects the deck
2236 list number and the code to execute for each card on the list.
2238 @^booster pack@>
2240 @<Cases for system commands@>=
2241 @-case 'P': {
2242 // Generate a random pack or deck
2243 data_index s=set_deck_list(pop_num());
2244 data_index n; // current deck list entry
2245 if(stack_ptr[1].is_string) program_error("Number expected");
2246 @<Figure out what cards belong in the pack@>;
2247 @<Execute the code on the stack for each card in the pack@>;
2248 break;
2251 @ @<Figure out what cards belong in the pack@>= {
2252 deck_entry*e;
2253 int tries=1000; // How many times can you retry if it fails?
2254 figure_out_again:
2255 if(!--tries) program_error("No cards matched the deck criteria");
2256 n=s;
2257 @<Reset |amount_in_pack| of each card to zero@>;
2258 while(n!=none && (n=(e=deck_lists.data+n)->next)!=none)
2259 @<Process this deck entry@>;
2262 @ @<Reset |amount_in_pack| of each card to zero@>= {
2263 data_index i;
2264 foreach(i,card_list) card_list.data[i].amount_in_pack=0;
2267 @ The deck entry must be processed according to the flags. Here is a list
2268 of flags:
2270 \.a: Use all cards that meet the criteria, instead of only one. If this is
2271 the case, it is possible to use negative weights to remove cards from the
2272 pack. Also, it cannot fail.
2273 [Combine with \.{x}]
2275 \.k: Select without replacement. It is fail if the total weight is not
2276 enough. There are two ways in which this differs from \.u (below). One is
2277 that the previous lines in the deck list are not used. The other one is
2278 that if the weight is more than one, there will be more than one ball for
2279 that card, therefore the same card can be picked up multiple times.
2280 [Combine with \.{sux}]
2282 \.n: Use the |amount| as a probability. If |amount<=100| then the
2283 probability is |amount/100| otherwise it is |100/amount|. This is a
2284 probability of using the |name| to select another deck list instead of
2285 this one.
2286 [Combine with nothing]
2288 \.s: Skip the next line if this line does not fail. (Normally, if one line
2289 fails, everything does, and you have to try again.)
2290 [Combine with \.{kux}]
2292 \.u: Require unique selection. It is fail if the card is already in this
2293 pack.
2294 [Combine with \.{ksx}]
2296 \.x: Pass the |name| as a string to the code in the \.U register, and then
2297 use the resulting code as the code to determine weights instead of using
2298 the code in the register named by |name| directly. Now you can type things
2299 such as |"12x Forest"| into your deck list.
2300 [Combine with \.{aksu}]
2302 @<Process this deck entry@>= {
2303 if(e->flags&lflag('n')) {
2304 @<Determine whether or not to skip to another deck list@>;
2305 } @+else {
2306 char*c; // code for weights of each card
2307 int total; // total weight of cards
2308 data_index*bag=malloc(sizeof(data_index));
2309 @<Get the code |c| for the weights of each card@>;
2310 @<Calculate the weights of each card@>;
2311 if(!(e->flags&lflag('a')))
2312 @<Select some of the cards at random and add them to the pack@>;
2313 if(e->flags&lflag('x')) free(c);
2314 free(bag);
2318 @ @<Determine whether or not to skip to another deck list@>= {
2319 boolean q;
2320 if(e->amount<=100) {
2321 q=(gen_random(100)<e->amount);
2322 } @+else {
2323 q=(100<gen_random(e->amount));
2325 if(q) n=set_deck_list(find_name(e->name));
2328 @ @<Get the code |c| for the weights of each card@>= {
2329 if(e->flags&lflag('x')) {
2330 execute_program(registers['U'].text);
2331 if(stack_ptr->is_string) {
2332 c=pop_string();
2333 } @+else {
2334 program_error("Type mismatch");
2336 } @+else {
2337 int n=find_name(e->name);
2338 if(name_info(n).value.is_string) {
2339 c=name_info(n).value.text;
2340 } @+else {
2341 program_error("Type mismatch");
2346 @ @<Calculate the weights of each card@>= {
2347 data_index i;
2348 foreach(i,card_list) {
2349 registers['A'].number=card_list.data[i].token_ptr;
2350 execute_program(c);
2351 if(stack_ptr->number) {
2352 if(e->flags&lflag('a')) {
2353 card_list.data[i].amount_in_pack+=e->amount*stack_ptr->number;
2354 } @+else if(stack_ptr->number>0) {
2355 @<Add the cards to the |bag|@>;
2358 stack_drop();
2362 @ The |bag| is like, you put the balls in the bag so that you can mix it
2363 and take one out, whatever number is on the ball is the card you put into
2364 the pack. Except, that there is no balls and no bag.
2366 There is one ball per point of weight.
2368 @^balls@>
2370 @<Add the cards to the |bag|@>= {
2371 int j=stack_ptr->number;
2372 bag=realloc(bag,(total+j)*sizeof(data_index));
2373 while(j--) bag[total+j]=i;
2374 total+=stack_ptr->number;
2377 @ If it is not a line which adds all possibilities at once, then the cards
2378 must be selected from the |bag| at random to bag them. In some cases it
2379 will fail.
2381 @<Select some of the cards at random and add them to the pack@>= {
2382 data_index r;
2383 int amount=e->amount;
2384 bag_next:
2385 if(!total) @<Deal with bag failure@>;
2386 r=gen_random(total);
2387 if((e->flags&lflag('u')) && card_list.data[bag[r]].amount_in_pack) {
2388 bag[r]=bag[--total];
2389 goto bag_next;
2391 card_list.data[bag[r]].amount_in_pack++;
2392 if(e->flags&lflag('k')) bag[r]=bag[--total];
2393 if(amount--) goto bag_next;
2394 @#if(e->flags&lflag('s')) n=deck_lists.data[n].next;
2395 bag_done: ;
2398 @ @<Deal with bag failure@>= {
2399 if(e->flags&lflag('s')) goto bag_done;
2400 else goto figure_out_again;
2403 @ Now it must do stuff using the list which is generated. The quantity for
2404 how many of that card is pushed on the stack, and this is done even for
2405 cards with negative quantity (but not for zero quantity).
2407 @<Execute the code on the stack for each card in the pack@>= {
2408 data_index i;
2409 char*q=pop_string();
2410 if(!stack_ptr[1].is_string) program_error("Type mismatch");
2411 foreach(i,card_list) {
2412 if(card_list.data[i].amount_in_pack) {
2413 push_num(card_list.data[i].amount_in_pack);
2414 execute_program(q);
2417 free(q);
2420 @*Reading Input Files. Now it is time for the part of the program where
2421 input files are read and processed. The areas of the file (and other
2422 special commands) are indicated using \.@@ signs.
2424 At first we have state information. Each state is labeled by uppercase
2425 letters, or by digits 1 to 9. The high bit is set for the heading state,
2426 meaning the first line that contains the name and/or other heading
2427 information.
2429 @d null_state 0
2430 @d card_state 'C'
2431 @d deck_state 'D'
2432 @d execute_state 'E'
2433 @d file_state 'F'
2434 @d include_state 'I'
2435 @d keyword_state 'K'
2436 @d pattern_state 'P'
2437 @d subroutine_state 'S'
2438 @d wordforms_state 'W'
2439 @d heading 0x80
2441 @<Global variables@>=
2442 int cur_state;
2443 data_index cur_name;
2444 data_index cur_data;
2445 boolean omit_line_break;
2447 @ The next thing that must be kept track of for input files is the stack
2448 of open input files.
2450 @d max_pathname_length 128
2451 @d max_filename_length 128
2452 @d max_input_stack 128
2453 @d max_line_length 256
2455 @<Typedefs@>=
2456 typedef struct {
2457 FILE*fp; // zero for terminal input
2458 char name[max_filename_length+1];
2459 int line;
2460 } input_file_data;
2462 @ @<Global variables@>=
2463 input_file_data input_files[max_input_stack];
2464 input_file_data*current_input_file=input_files;
2465 char input_buffer[max_line_length];
2467 @ Some macros are useful to access the current file data.
2469 @d current_line (current_input_file->line)
2470 @d current_filename (current_input_file->name)
2471 @d current_fp (current_input_file->fp)
2473 @d parsing_error(_text) fprintf(stderr,"%s on line %d in %s\n",
2474 _text,current_line,current_filename)@;
2476 @ There is also conditional processing directives, which uses a single
2477 variable to keep track of the level. If it is greater than zero, the
2478 condition is false, and it is increased for nesting conditions (the
2479 nested conditions have no truth to them).
2481 @<Global variables@>=
2482 int condition_level=0;
2484 @ This subroutine inputs the next line. True is returned if there is a
2485 line, or false if it is finished.
2487 It is necessary to check for end of file and if so, close that file and
2488 try the one it was included from; and if it is terminal input, display the
2489 current state when prompting input from the user.
2491 @-p boolean input_line(void) {
2492 input_line_again: if(current_fp) {
2493 @<Get a line of input from the file@>;
2494 } @+else {
2495 @<Get a line of terminal input@>;
2497 @<Remove trailing |'\n'|, |'\r'|, and spaces@>;
2498 ++current_line;
2499 return 1;
2502 @ @<Get a line of input from the file@>= {
2503 if(!fgets(input_buffer,max_line_length,current_fp)) {
2504 memusage_log("Closing input file",current_input_file-input_files)@;
2505 fclose(current_fp);
2506 if(current_input_file>input_files) {
2507 --current_input_file;
2508 goto input_line_again;
2509 } @+else {
2510 return 0;
2515 @ @<Get a line of terminal input@>= {
2516 printf("\n%c> ",cur_state?cur_state:'>');
2517 fflush(stdout);
2518 if(!fgets(input_buffer,max_line_length,stdin)) return 0;
2521 @ This function is used in order to open another input file. One way is
2522 that the file might be included from another file, or it might be the
2523 main file.
2525 @-p void open_input(char*name) {
2526 if(++current_input_file>input_files+max_input_stack) {
2527 fprintf(stderr,"Too many simultaneous input files\n");
2528 exit(1);
2530 memusage_log("Opening input file",current_input_file-input_files)@;
2531 strcpy(current_filename,name);
2532 current_line=0;
2533 current_fp=fopen(name,"r");
2534 if(!current_fp) {
2535 fprintf(stderr,"Cannot open input file: %s\n",name);
2536 exit(1);
2540 @ Trailing newlines and spaces are removed. On some computers, there will
2541 be a carriage return before the line feed, it should be removed, so that
2542 the same file will work on other computers, too.
2544 @d last_character_input input_buffer[strlen(input_buffer)-1]
2546 @<Remove trailing |'\n'|, |'\r'|, and spaces@>= {
2547 if(last_character_input=='\n') last_character_input=0;
2548 if(last_character_input=='\r') last_character_input=0;
2549 while(last_character_input==' ') last_character_input=0;
2552 @ The input states start at these values.
2554 @<Initialize the input states@>= {
2555 cur_state=execute_state;
2556 cur_name=cur_data=0;
2559 @ Now it is the time to do the actual processing according to the contents
2560 of the lines of the file. A line starting with \.@@ sign will indicate a
2561 special command (to operate in all modes) or a mode switch command.
2563 @d delete_chars(_buf,_c) memmove((_buf),(_buf)+(_c),strlen((_buf)+(_c))+1)
2565 @<Process the input files@>= {
2566 char*buf;
2567 while(input_line()) {
2568 buf=input_buffer;
2569 if(condition_level) {
2570 buf+=strspn(buf," ");
2571 condition_level+=!strcmp(buf,"@@<");
2572 condition_level-=!strcmp(buf,"@@>");
2573 } @+else {
2574 omit_line_break=1;
2575 @<Convert \.@@ commands in the |input_buffer|@>;
2576 omit_line_break=0;
2577 process_line(buf);
2582 @ @<Convert \.@@ commands in the |input_buffer|@>= {
2583 char*ptr=input_buffer;
2584 while(*ptr) {
2585 if(*ptr=='@@') {
2586 @<Convert the current \.@@ command@>;
2587 } @+else {
2588 ptr++;
2593 @ @<Convert the current \.@@ command@>= {
2594 switch(*++ptr) {
2595 case '@@': @/
2596 delete_chars(ptr,1);
2597 break;
2598 case '.': @<Process \.{@@.} command@>;@+break;
2599 case '&': @<Process \.{@@\&} command@>;@+break;
2600 case '^': @<Process \.{@@\^} command@>;@+break;
2601 case '(': @<Process \.{@@(} command@>;@+break;
2602 case '<': @<Process \.{@@<} command@>;@+break;
2603 case '>': @<Remove this command from the input@>;@+break;
2604 default: @/
2605 if((*ptr>='A' && *ptr<='Z') || (*ptr>='0' && *ptr<='9')) {
2606 @<Enter a |heading| state@>;
2607 } @+else {
2608 parsing_error("Unknown @@ command");
2613 @ @<Remove this command from the input@>= {
2614 ptr--;
2615 delete_chars(ptr,2);
2618 @ Heading states are used for the first line of a section in the file.
2619 After that line is processed, it becomes the corresponding non-heading
2620 state |(cur_state&~heading)|.
2622 Note: The state |'0'| is deliberately unused; you might use it for
2623 documentation areas, for example.
2625 @^documentation areas@>
2627 @<Enter a |heading| state@>= {
2628 cur_state=heading|*ptr--;
2629 delete_chars(ptr,2);
2630 while(*ptr==' ' || *ptr=='\t') delete_chars(ptr,1);
2633 @ @-p void process_line(char*buf) {
2634 int q=cur_state;
2635 cur_state&=~heading;
2636 switch(q) {
2637 case card_state: @<Process card state@>;@+break;
2638 case deck_state: @<Process deck state@>;@+break;
2639 case execute_state: @<Process execute state@>;@+break;
2640 case file_state: @<Process file state@>;@+break;
2641 case keyword_state: @<Process keyword state@>;@+break;
2642 case pattern_state: @<Process pattern state@>;@+break;
2643 case subroutine_state: @<Process subroutine state@>;@+break;
2644 case wordforms_state: @<Process word forms state@>;@+break;
2645 case card_state|heading: @<Process card heading@>;@+break;
2646 case deck_state|heading: @<Process deck heading@>;@+break;
2647 case file_state|heading: @<Process file heading@>;@+break;
2648 case include_state|heading: @<Process include heading@>;@+break;
2649 case keyword_state|heading: @<Process keyword heading@>;@+break;
2650 case pattern_state|heading: @<Process pattern heading@>;@+break;
2651 case subroutine_state|heading: @<Process subroutine heading@>;@+break;
2652 default: ; // nothing happens
2656 @ Sometimes you might want a macro which can send a line programmatically.
2657 So, here is the way that it is done.
2659 @<Cases for system commands@>=
2660 @-case 'X': {
2661 // Process a line by programming
2662 int n;
2663 if(stack_ptr->is_string) program_error("Type mismatch");
2664 n=pop_num();
2665 if(n) cur_state=n|heading;
2666 if(!stack_ptr->is_string) program_error("Type mismatch");
2667 omit_line_break=1;
2668 process_line(stack_ptr->text);
2669 stack_drop();
2670 break;
2673 @*Inner Commands. These are commands other than the section headings.
2675 @ The first command to deal with is simple--it is a comment. The rest of
2676 the current line is simply discarded.
2678 @<Process \.{@@.} command@>= {
2679 ptr[-1]=0;
2682 @ This command is a pattern split. It means it will process the part of
2683 the line before this command and then process the stuff after it. The
2684 variable |omit_line_break| is 1 if this command is used; because it means
2685 there will not be a line break. (Otherwise, patterns and so on are split
2686 at line breaks.)
2688 @<Process \.{@@\&} command@>= {
2689 ptr[-1]=0;
2690 process_line(buf);
2691 buf=++ptr;
2694 @ This allows control characters to be inserted into the input. This code
2695 takes advantage of the way the ASCII code works, in which stripping all
2696 but the five low bits can convert a letter (uppercase or lowercase) to its
2697 corresponding control character.
2699 @^control character@>
2701 @<Process \.{@@\^} command@>= {
2702 ptr[1]&=0x1F;
2703 --ptr;
2704 delete_chars(ptr,2);
2707 @ The following command is used to execute a code in a different state and
2708 then include the results in this area.
2710 @<Process \.{@@(} command@>= {
2711 char*p;
2712 char*q;
2713 @<Skip over the name and save the rest of the line@>;
2714 @<Execute the code for the named subroutine@>;
2715 @<Insert the returned string and fix the line buffer@>;
2718 @ @<Skip over the name and save the rest of the line@>= {
2719 p=ptr+1;
2720 while(*ptr && *ptr!=')') ptr++;
2721 q=strdup(ptr+!!*ptr);
2722 *ptr=0;
2725 @ @<Execute the code for the named subroutine@>= {
2726 int n=find_name(p);
2727 execute_program(fetch_code(n));
2730 @ @<Insert the returned string and fix the line buffer@>= {
2731 char*s=pop_string();
2732 sprintf(p-2,"%s%s",s,q);
2733 ptr=p+strlen(s)-2;
2734 free(s);
2735 free(q);
2738 @ This command is used for conditional processing. The condition value
2739 comes from the stack. Zero is false, everything else is true.
2741 @<Process \.{@@<} command@>= {
2742 --ptr;
2743 delete_chars(ptr,2);
2744 condition_level=!stack_ptr->number;
2745 stack_drop();
2748 @*Card State. Cards are added to the card areas by using the card state.
2749 The \.C register tells which is the current card area, and \.P register is
2750 used to select the current pattern area. The pattern area is used to match
2751 patterns after reading a line. Please note that it won't work to change
2752 the value of the \.C register during the card state.
2754 @<Process card heading@>= {
2755 int n=find_name(buf);
2756 cur_data=set_card_area(n);
2757 cur_name=n-256;
2758 push_num(n);@+store('C');
2761 @ @<Process card state@>= {
2762 char*b;
2763 if(!omit_line_break) strcpy(buf+strlen(buf),"\n");
2764 @<Initialize the \.W register@>;
2765 b=do_patterns(strdup(buf),registers['P'].number);
2766 if(registers['W'].is_string) execute_program(registers['W'].text);
2767 @<Send the tokens of |b| and replace whatsits@>;
2768 free(b);
2771 @ @<Initialize the \.W register@>= {
2772 if(registers['W'].is_string) free(registers['W'].text);
2773 registers['W'].is_string=1;
2774 registers['W'].text=strdup("");
2777 @ @<Send the tokens of |b| and replace whatsits@>= {
2778 char*p;
2779 for(p=b;*p;p++) {
2780 if(*p==whatsit) {
2781 send_token(cur_data,pop_num());
2782 } @+else {
2783 send_token(cur_data,(*p==1)?0:*p);
2788 @ There is one command you might want to send tokens in any other time.
2790 @<Cases for system commands@>=
2791 @-case 'T': {
2792 // Add tokens to the card area
2793 if(stack_ptr->is_string) {
2794 @<Send tokens from the string on the stack@>;
2795 } @+else {
2796 send_token(set_card_area(registers['C'].number),stack_ptr->number);
2798 stack_drop();
2799 break;
2802 @ @<Send tokens from the string on the stack@>= {
2803 char*p;
2804 data_index q=set_card_area(registers['C'].number);
2805 for(p=stack_ptr->text;*p;p++) send_token(q,*p);
2808 @*Deck State. Deck state is used for creating deck lists and random packs.
2810 @<Process deck heading@>= {
2811 cur_name=find_name(buf)-256;
2812 cur_data=set_deck_list(cur_name+256);
2813 @<Skip to the end of the deck list@>;
2816 @ @<Skip to the end of the deck list@>= {
2817 while(deck_lists.data[cur_data].next!=none)
2818 cur_data=deck_lists.data[cur_data].next;
2821 @ Now to parse each line in turn. Each line consists of a number, the
2822 flags, and a text.
2824 @<Process deck state@>= {
2825 int n=strtol(buf,&buf,10);
2826 unsigned int f=0;
2827 if(n) {
2828 buf+=strspn(buf,"\x20\t");
2829 @<Read the flags for the deck list@>;
2830 buf+=strspn(buf,"\x20\t"); // Now we are at the point of the text
2831 @<Send this line to the deck list@>;
2832 @<Create and advance to the new terminator of the deck list@>;
2836 @ @<Read the flags for the deck list@>= {
2837 while(*buf>='a' && *buf<='z') f |=lflag(*buf++);
2838 buf++; // Skip terminator of flags
2841 @ If the \.x flag is set, it will be determined what to do with the text
2842 by the user-defined code. Otherwise, it is always a name, so we can save
2843 memory by pointing to the name buffer (since name buffers never vary).
2845 @<Send this line to the deck list@>= {
2846 if(f&lflag('x')) {
2847 deck_lists.data[cur_data].name=strdup(buf);
2848 } @+else {
2849 deck_lists.data[cur_data].name=name_info(find_name(buf)).name;
2853 @ @<Create and advance to the new terminator of the deck list@>= {
2854 data_index i=new_record(deck_lists);
2855 deck_lists.data[cur_data].next=i;
2856 deck_lists.data[cur_data=i].next=none;
2859 @*Execute State. This state is simple, just execute stack codes. It is the
2860 initial state; you can use it with terminal input, too.
2862 @<Process execute state@>= {
2863 execute_program(buf);
2866 @*File State. This state is used to make list of output files. Each one is
2867 stored as a string, like subroutine state. The difference is that newlines
2868 will not be discarded. The other difference is that there is a flag in the
2869 |name_data| record for it that tells it that it is a file that should be
2870 sent to output.
2872 @<More elements of |name_data|@>=
2873 boolean is_output_file;
2875 @ @<Process file heading@>= {
2876 cur_name=find_name(buf)-256;
2877 if(!names.data[cur_name].value.is_string) {
2878 names.data[cur_name].value.is_string=1;
2879 names.data[cur_name].value.text=strdup("");
2880 names.data[cur_name].is_output_file=1;
2884 @ @<Process file state@>= {
2885 int z=strlen(names.data[cur_name].value.text);
2886 if(!omit_line_break) strcpy(buf+strlen(buf),"\n");
2887 names.data[cur_name].value.text=realloc(names.data[cur_name].value.text,
2888 z+strlen(buf)+1);
2889 strcpy(names.data[cur_name].value.text+z,buf);
2892 @*Include State. The include state causes inclusion of another source file
2893 from this one.
2895 @<Process include heading@>= {
2896 cur_state=execute_state;
2897 @<Push the include file onto the input stack@>;
2898 @<Attempt to open the include file...@>;
2901 @ @<Push the include file onto the input stack@>= {
2902 ++current_input_file;
2903 memusage_log("Opening input file",current_input_file-input_files)@;
2904 strcpy(current_filename,buf);
2905 current_line=0;
2906 current_fp=0;
2909 @ Include files are searched using the search path specified in the
2910 environment variable called \.{TEXNICARDPATH}, which is a list of paths
2911 delimited by colons on UNIX systems (including Cygwin), but semicolons on
2912 Windows (colons are used in Windows for drive letters). A forward slash is
2913 the path separator. Please note that if you want to use include files in
2914 the current directory, you must include |"."| as the first entry in the
2915 search path!!
2917 @^search path@>
2918 @.TEXNICARDPATH@>
2919 @^Windows@>
2921 @<Set |includepath_separator| depending on operating system@>=
2922 #ifdef WIN32
2923 #define @!includepath_separator ';'
2924 #else
2925 #define includepath_separator ':'
2926 #endif
2928 @ @<Attempt to open the include file by finding it in the search path@>= {
2929 char searchpath[max_pathname_length+max_filename_length+1];
2930 char*cpath;
2931 char*npath=getenv("TEXNICARDPATH");
2932 strcpy(searchpath,npath?npath:".");
2933 npath=cpath=searchpath;
2934 @<Set |includepath_separator| depending on operating system@>;
2935 @<Attempt to open the file from each each directory in the search path@>;
2936 @<It is a fatal error if no such file was found@>;
2939 @ @<Attempt to open the file from each each directory...@>= {
2940 while(!current_fp) {
2941 char f[max_pathname_length+max_filename_length+1];
2942 @<Select the next path name into |cpath| and |npath|@>;
2943 sprintf(f,"%s/%s",cpath,current_filename);
2944 current_fp=fopen(f,"r");
2948 @ @<Select the next path name into |cpath| and |npath|@>= {
2949 if(!(cpath=npath)) break;
2950 if((npath=strchr(npath,includepath_separator))) *npath++=0;
2953 @ @<It is a fatal error if no such file was found@>= {
2954 if(!current_fp) {
2955 fprintf(stderr,"%s not found in search path.\n",current_filename);
2956 exit(1);
2960 @*Keyword State. You can add keywords to the keyword area by using this.
2961 Each keyword heading is one entry in the list.
2963 @<Process keyword heading@>= {
2964 cur_data=new_record(keywords);
2965 keywords.data[cur_data].match=strdup(buf);
2966 keywords.data[cur_data].replacement=strdup("");
2969 @ @<Process keyword state@>= {
2970 keyword_data*k=&keywords.data[cur_data];
2971 if(*buf=='+') {
2972 k->category|=find_category(buf+1);
2973 } @+else {
2974 if(!omit_line_break) strcpy(buf+strlen(buf),"\n");
2975 @<Append buffer to keyword text@>;
2979 @ @<Append buffer to keyword text@>= {
2980 if(*buf) {
2981 int z=strlen(k->replacement);
2982 k->replacement=realloc(k->replacement,z+strlen(buf)+1);
2983 strcpy(k->replacement+z,buf);
2987 @*Pattern State. This state compiles patterns into a pattern area. It
2988 uses its own syntax, and then is converted into the proper control codes
2989 for the |text| of a pattern.
2991 @<Process pattern heading@>= {
2992 cur_name=find_name(buf)-256;
2993 cur_data=set_pattern_area(cur_name+256);
2996 @ The stuff inside the pattern state has its own commands.
2998 @<Process pattern state@>= {
2999 char add_buf[1024]; // buffer of text to add to the current pattern
3000 pattern_data*pat=&pattern_areas.data[cur_data];
3001 *add_buf=0;
3002 switch(*buf) {
3003 case '<': @<Create a new pattern with top priority@>;@+break;
3004 case '>': @<Create a new pattern with bottom priority@>;@+break;
3005 case ':': @<Make a pattern text with a marker@>;@+break;
3006 case '+': @<Add a keyword category to this pattern@>;@+break;
3007 default: ; // do nothing
3009 @<Append contents of |add_buf| to the pattern, if needed@>;
3012 @ @<Create a new pattern with top priority@>= {
3013 cur_data=new_record(pattern_areas);
3014 pattern_areas.data[cur_data].text=strdup("");
3015 pattern_areas.data[cur_data].subroutine=find_name(buf+1)-256;
3016 pattern_areas.data[cur_data].next=names.data[cur_name].pattern_area;
3017 names.data[cur_name].pattern_area=cur_data;
3020 @ @<Create a new pattern with bottom priority@>= {
3021 data_index n;
3022 cur_data=new_record(pattern_areas);
3023 pattern_areas.data[cur_data].text=strdup("");
3024 pattern_areas.data[cur_data].subroutine=find_name(buf+1)-256;
3025 pattern_areas.data[cur_data].next=none;
3026 @<Find the bottom pattern and store its index in |n|@>;
3027 pattern_areas.data[n].next=cur_data;
3030 @ @<Find the bottom pattern and...@>= {
3031 n=names.data[cur_name].pattern_area;
3032 while(pattern_areas.data[n].next!=none && pattern_areas.data[n].text &&
3033 pattern_areas.data[pattern_areas.data[n].next].next!=none)
3034 n=pattern_areas.data[n].next;
3037 @ Actually, the name of this \strike{cake} chunk is a lie, because it does
3038 not always add a marker.
3040 @<Make a pattern text with a marker@>= {
3041 char*p;
3042 char*b=add_buf;
3043 @<Add the pattern marker if applicable@>;
3044 for(p=buf+2;p[-1] && *p;p++) {
3045 switch(*p) {
3046 case '\\': *b++=*++p; @+break;
3047 case '(': *b++=begin_capture; @+break;
3048 case ')': *b++=end_capture; @+break;
3049 case '%': *b++=match_keyword; @+*b++=*++p; @+break;
3050 case '!': *b++=match_table; @+*b++=*++p; @+break;
3051 case '?': *b++=optional_table; @+*b++=*++p; @+break;
3052 case '#': *b++=failed_match; @+break;
3053 case '&': *b++=jump_table; @+*b++=*++p; @+break;
3054 case ';': *b++=successful_match; @+break;
3055 case '<': *b++=back_one_space; @+break;
3056 case '>': *b++=forward_one_space; @+break;
3057 case '[': *b++=match_left_side; @+break;
3058 case ']': *b++=match_right_side; @+break;
3059 default: *b++=*p; @+break;
3062 *b=0;
3065 @ @<Add the pattern marker if applicable@>= {
3066 if(buf[1]>' ') *b++=buf[1]|0x80;
3069 @ @<Add a keyword category to this pattern@>= {
3070 pattern_areas.data[cur_data].category=find_category(buf+1);
3073 @ @<Append contents of |add_buf| to the pattern...@>= {
3074 if(*add_buf) {
3075 int z=strlen(pat->text);
3076 pat->text=realloc(pat->text,z+strlen(add_buf)+1);
3077 strcpy(pat->text+z,add_buf);
3081 @*Subroutine State. This state is used to add a named subroutine.
3083 @<Process subroutine heading@>= {
3084 cur_name=find_name(buf)-256;
3085 if(!names.data[cur_name].value.is_string) {
3086 names.data[cur_name].value.is_string=1;
3087 names.data[cur_name].value.text=strdup("");
3091 @ @<Process subroutine state@>= {
3092 int z=strlen(names.data[cur_name].value.text);
3093 names.data[cur_name].value.text=realloc(names.data[cur_name].value.text,
3094 z+strlen(buf)+1);
3095 strcpy(names.data[cur_name].value.text+z,buf);
3098 @*Word Forms State. You can use the word forms state to enter rules and
3099 exceptions for word forms, such as plurals.
3101 @<Global variables@>=
3102 char wordform_code[256]; // code to execute at \.= line
3103 char wordform_kind; // which kind of word forms is being made now?
3105 @ @<Process word forms state@>= {
3106 switch(*buf) {
3107 case '>': @<Process \.> line in word forms state@>; @+break;
3108 case '=': @<Process \.= line in word forms state@>; @+break;
3109 case '#': wordform_kind=buf[1]; @+break;
3110 default: if(*buf>='0' && *buf<='9')
3111 @<Process numeric line in word forms state@>;
3115 @ The commands are \.>, \.=, and numbers. The command \.> sets a code for
3116 processing \.= commands, and then add to the list.
3118 @<Process \.> line in word forms state@>= {
3119 strcpy(wordform_code,buf+1);
3122 @ @<Process \.= line in word forms state@>= {
3123 int level,kind;
3124 char*orig;
3125 char*dest;
3126 push_string(buf+1);
3127 execute_program(wordform_code);
3128 kind=pop_num(); @+ level=pop_num();
3129 dest=pop_string(); @+ orig=pop_string();
3130 add_word_form(kind,level,orig,dest);
3131 free(orig); @+ free(dest);
3134 @ Now the command for numeric forms. You put ``level\.:orig\.:dest'' in
3135 that order, please.
3137 @<Process numeric line in word forms state@>= {
3138 int level=strtol(buf,&buf,0);
3139 char*p;
3140 if(*buf==':') buf++;
3141 p=strchr(buf,':');
3142 if(p) *p=0;
3143 add_word_form(wordform_kind,level,buf,p+1);
3146 @*Writing Output Files. Finally, it will be time to send any output
3147 generated into the files (if there is any, which there usually is).
3149 @^output@>
3151 @d ctrl(_letter) (0x1F&(_letter))
3153 @d call_final_subroutine ctrl('C')
3154 @d copy_field ctrl('F')
3155 @d newline ctrl('J')
3156 @d loop_point ctrl('L')
3157 @d next_record ctrl('N')
3158 @d skip_one_character ctrl('S')
3160 @<Write the output files@>= {
3161 data_index n;
3162 foreach(n,names) {
3163 if(names.data[n].is_output_file && names.data[n].value.is_string)
3164 @<Write this output file@>;
3168 @ @<Write this output file@>= {
3169 FILE*fout=fopen(names.data[n].name,"w");
3170 char*ptr=names.data[n].value.text;
3171 char*loopptr=ptr; // loop point
3172 if(!fout) @<Error about unable to open output file@>;
3173 while(*ptr) @<Write the character and advance to the next one@>;
3174 fclose(fout);
3177 @ @<Error about unable to open output file@>= {
3178 fprintf(stderr,"Unable to open output file: %s\n",names.data[n].name);
3179 exit(1);
3182 @ @<Write the character and advance to the next one@>= {
3183 switch(*ptr) {
3184 case call_final_subroutine: @<Do |call_final_subroutine|@>; @+break;
3185 case copy_field: @<Do |copy_field|@>; @+break;
3186 case loop_point: loopptr=++ptr; @+break;
3187 case next_record: @<Do |next_record|@>; @+break;
3188 case skip_one_character: ptr+=2; @+break;
3189 default: fputc(*ptr++,fout);
3191 done_writing_one_character: ;
3194 @ @<Do |call_final_subroutine|@>= {
3195 register_value*v;
3196 if(*++ptr=='(') {
3197 char*p=strchr(ptr,')');
3198 *p=0;
3199 v=&name_info(find_name(ptr+1)).value;
3200 *p=')';
3201 ptr=p+1;
3202 } @+else {
3203 v=&registers[*ptr++];
3205 if(v->is_string) {
3206 execute_program(v->text);
3207 @<Write or loop based on result of subroutine call@>;
3208 stack_drop();
3212 @ @<Write or loop based on result of subroutine call@>= {
3213 if(stack_ptr->is_string) {
3214 fprintf(fout,"%s",stack_ptr->text);
3215 } @+else if(stack_ptr->number) {
3216 ptr=loopptr;
3220 @ This command is used to copy the next field.
3222 Look at the definition for the |send_reg_char_or_text| macro. It is
3223 strange, but it should work wherever a statement is expected. Please note
3224 that a ternary condition operator should have both choices of the same
3225 type.
3227 @^strange codes@>
3229 @d tok_idx (registers['A'].number)
3230 @d tok_area
3231 (card_areas.data[name_info(registers['C'].number).value.number].tokens)
3233 @d send_reg_char_or_text(_reg)
3234 if(!registers[_reg].is_string || *registers[_reg].text)
3235 fprintf(fout, "%c%s",
3236 registers[_reg].is_string?
3237 *registers[_reg].text:registers[_reg].number,
3238 registers[_reg].is_string?
3239 registers[_reg].text+1:(unsigned char*)""
3242 @<Do |copy_field|@>= {
3243 ++ptr;
3244 for(;;) {
3245 switch(tok_area[tok_idx++]) {
3246 case null_char: @<Unexpected |null_char|@>;
3247 case end_transmission: tok_idx=0; @+goto done_writing_one_character;
3248 case tabulation: send_reg_char_or_text('T'); @+break;
3249 case raw_data: @<Do |raw_data|@>; @+break;
3250 case escape_code: send_reg_char_or_text('E'); @+break;
3251 case record_separator: tok_idx--; @+goto done_writing_one_character;
3252 case field_separator: @+goto done_writing_one_character;
3253 default: @/
3254 if(tok_area[--tok_idx]&~0xFF)
3255 @<Deal with name code@>@;
3256 else
3257 @<Deal with normal character@>;
3258 tok_idx++;
3263 @ @<Unexpected |null_char|@>= {
3264 fprintf(stderr,"Unexpected null character found in a card area\n");
3265 exit(1);
3268 @ @<Do |raw_data|@>= {
3269 while(tok_area[tok_idx]) fputc(tok_area[tok_idx++],fout);
3270 tok_idx++;
3273 @ A name code found here is a code to tell it to call the subroutine code
3274 when it is time to write it out to the file. It should return a string on
3275 the stack (if it is a number, it will be ignored).
3277 @<Deal with name code@>= {
3278 if(name_info(tok_area[tok_idx]).value.is_string)
3279 execute_program(name_info(tok_area[tok_idx]).value.text);
3280 if(stack_ptr->is_string) fprintf(fout,"%s",stack_ptr->text);
3281 stack_drop();
3284 @ In case of a normal character, normally just write it out. But some
3285 characters need escaped for \TeX.
3287 @<Deal with normal character@>= {
3288 if(tables['E'][tok_area[tok_idx]]) send_reg_char_or_text('E');
3289 fputc(tok_area[tok_idx],fout);
3292 @ This one moves to the next record, looping if a next record is in fact
3293 available. Otherwise, just continue. Note that a |record_separator|
3294 immediately followed by a |end_transmission| is assumed to mean there is
3295 no next record, and that there is allowed to be a optional
3296 |record_separator| just before the |end_transmission|.
3298 @<Do |next_record|@>= {
3299 ++ptr;
3300 while(tok_area[tok_idx]!=record_separator &&
3301 tok_area[tok_idx]!=end_transmission) tok_idx++;
3302 if(tok_area[tok_idx]!=end_transmission &&
3303 tok_area[tok_idx+1]!=end_transmission) ptr=loopptr;
3306 @*Functions Common to DVI and GF. Numbers for \.{GF} and \.{DVI} files use
3307 the |dvi_number| data type. (Change this in the change file if the current
3308 setting is inappropriate for your system.)
3310 There is also the |dvi_measure| type, which is twice as long and is used
3311 to compute numbers that can be fractional (with thirty-two fractional bits
3312 and thirty-two normal bits).
3314 @<Typedefs@>=
3315 @q[Type of DVI numbers::]@>
3316 typedef signed int dvi_number;
3317 typedef signed long long int dvi_measure;
3318 @q[::Type of DVI numbers]@>
3320 @ There is one subroutine here to read a |dvi_number| from a file. They
3321 come in different sizes and some are signed and some are unsigned.
3323 @^endianness@>
3324 @^byte order@>
3326 @-p dvi_number get_dvi_number(FILE*fp,boolean is_signed,int size) {
3327 dvi_number r=0;
3328 if(size) r=fgetc(fp);
3329 if((r&0x80) && is_signed) r|=0xFFFFFF00;
3330 while(--size) r=(r<<8)|fgetc(fp);
3331 return r;
3334 @ Some macros are defined here in order to deal with |dvi_measure| values.
3336 @^fractions@>
3338 @d to_measure(_value) (((dvi_measure)(_value))<<32)
3339 @d floor(_value) ((dvi_number)((_value)>>32))
3340 @d round(_value) ((dvi_number)(((_value)+0x8000)>>32))
3341 @d ceiling(_value) ((dvi_number)(((_value)+0xFFFF)>>32))
3343 @ Here division must be done in a careful way, to ensure that none of the
3344 intermediate results exceed sixty-four bits.
3346 @d fraction_one to_measure(1)
3348 @-p dvi_measure make_fraction(dvi_measure p,dvi_measure q) {
3349 dvi_measure f,n;
3350 boolean negative=(p<0)^(q<0);
3351 if(p<0) p=-p;
3352 if(q<0) q=-q;
3353 n=p/q; @+ p=p%q;
3354 n=(n-1)*fraction_one;
3355 @<Compute $f=\lfloor2^{32}(1+p/q)+{1\over2}\rfloor$@>;
3356 return (f+n)*(negative?-1:1);
3359 @ Notice that the computation specifies $(p-q)+p$ instead of $(p+p)-q$,
3360 because the latter could overflow.
3362 @<Compute $f=...@>= {
3363 register dvi_measure b;
3364 f=1;
3365 while(f<fraction_one) {
3366 b=p-q; @+ p+=b;
3367 if(p>=0) {
3368 f+=f+1;
3369 } @+else {
3370 f<<=1;
3371 p+=q;
3376 @ And a few miscellaneous macros.
3378 @d upto4(_var,_cmd) (_var>=_cmd && _var<_cmd+4)
3380 @*DVI Reading. The device-independent file format is a format invented by
3381 David R.~Fuchs in 1979. The file format need not be explained here, since
3382 there are other books which explain it\biblio{Knuth, Donald. ``\TeX: The
3383 Program''. Computers {\char`\&} Typesetting. ISBN 0-201-13437-3.}\biblio{%
3384 Knuth, Donald. ``\TeX ware''. Stanford Computer Science Report 1097.}.
3386 \edef\TeXwareBiblio{\the\bibliocount}
3387 @^Fuchs, David@>
3388 @.DVI@>
3389 @^device independent@>
3391 At first, names will be given for the commands in a \.{DVI} file.
3393 @d set_char_0 0 // Set a character and move [up to 127]
3394 @d set1 128 // Take one parameter to set character [up to 131]
3395 @d set_rule 132 // Set a rule and move down, two parameters
3396 @d put1 133 // As |set1| but no move [up to 136]
3397 @d put_rule 137 // As |set_rule| but no move
3398 @d nop 138 // No operation
3399 @d bop 139 // Beginning of a page
3400 @d eop 140 // End of a page
3401 @d push 141 // Push $(h,v,w,x,y,z)$ to the stack
3402 @d pop 142 // Pop $(h,v,w,x,y,z)$ from the stack
3403 @d right1 143 // Take one parameter, move right [up to 146]
3404 @d w0 147 // Move right $w$ units
3405 @d w1 148 // Set $w$ and move right [up to 151]
3406 @d x0 152 // Move right $x$ units
3407 @d x1 153 // Set $x$ and move right [up to 156]
3408 @d down1 157 // Take one parameter, move down [up to 160]
3409 @d y0 161 // Move down $y$ units
3410 @d y1 162 // Set $y$ and move down [up to 165]
3411 @d z0 166 // Move down $z$ units
3412 @d z1 167 // Set $z$ and move down [up to 170]
3413 @d fnt_num_0 171 // Select font 0 [up to 234]
3414 @d fnt1 235 // Take parameter to select font [up to 238]
3415 @d xxx1 239 // Specials [up to 242]
3416 @d fnt_def1 243 // Font definitions [up to 246]
3417 @d pre 247 // Preamble
3418 @d post 248 // Postamble
3419 @d post_post 249 // Postpostamble
3421 @ We should now start reading the \.{DVI} file. Filenames of fonts being
3422 used will be sent to standard output.
3424 @-p boolean read_dvi_file(char*filename) {
3425 boolean fonts_okay=1;
3426 FILE*fp=fopen(filename,"rb");
3427 if(!fp) dvi_error(fp,"Unable to open file");
3428 @#@<Skip the preamble of the \.{DVI} file@>;
3429 @<Skip to the next page@>;
3430 @<Read the metapage heading@>;
3431 @<Compute the conversion factor@>;
3432 read_dvi_page(fp);
3433 @<Skip to and read the postamble@>;
3434 @<Read the font definitions and load the fonts@>;
3435 if(fonts_okay) @<Read the pages for each card@>;
3436 @#fclose(fp);
3437 return fonts_okay;
3440 @ @-p void dvi_error(FILE*fp,char*text) {
3441 fprintf(stderr,"DVI error");
3442 if(fp) fprintf(stderr," at %08X",ftell(fp));
3443 fprintf(stderr,": %s\n",text);
3444 exit(1);
3447 @ Please note the version number of the \.{DVI} file must be 2.
3449 @<Skip the preamble of the \.{DVI} file@>= {
3450 if(fgetc(fp)!=pre) dvi_error(fp,"Bad preamble");
3451 if(fgetc(fp)!=2) dvi_error(fp,"Wrong DVI version");
3452 @<Read the measurement parameters@>;
3453 @<Skip the DVI comment@>;
3456 @ @<Read the measurement parameters@>= {
3457 unit_num=get_dvi_number(fp,0,4);
3458 unit_den=get_dvi_number(fp,0,4);
3459 unit_mag=get_dvi_number(fp,0,4);
3462 @ @<Skip the DVI comment@>= {
3463 int n=fgetc(fp);
3464 fseek(fp,n,SEEK_CUR);
3467 @ From the postamble we can read the pointer for the last page.
3469 @<Global variables@>=
3470 dvi_number last_page_ptr;
3472 @ @<Skip to and read the postamble@>= {
3473 fseek(fp,-4,SEEK_END);
3474 while(fgetc(fp)==223) fseek(fp,-2,SEEK_CUR);
3475 fseek(fp,-5,SEEK_CUR);
3476 fseek(fp,get_dvi_number(fp,0,4)+1,SEEK_SET);
3477 last_page_ptr=get_dvi_number(fp,0,4);
3478 fseek(fp,20,SEEK_CUR); // Skipped parameters of |post|
3479 dvi_stack=malloc(get_dvi_number(fp,0,2)*sizeof(dvi_stack_entry));
3480 fseek(fp,2,SEEK_CUR);
3483 @ Between the preamble and the first page can be |nop| commands and font
3484 definitions, so these will be skipped. The same things can occur between
3485 the end of one page and the beginning of the next page.
3487 @<Skip to the next page@>= {
3488 int c;
3489 for(;;) {
3490 c=fgetc(fp);
3491 if(c==bop) break;
3492 if(c>=fnt_def1 && c<fnt_def1+4) {
3493 @<Skip a font definition@>;
3494 } @+else if(c!=nop) {
3495 dvi_error(fp,"Bad command between pages");
3500 @ @<Skip a font definition@>= {
3501 int a,l;
3502 fseek(fp,c+13-fnt_def1,SEEK_CUR);
3503 a=fgetc(fp);
3504 l=fgetc(fp);
3505 fseek(fp,a+l,SEEK_CUR);
3508 @ The metapage includes the resolution and other things which must be set,
3509 such as subroutine codes. The resolution must be read before fonts can be
3510 read. Please note that no characters can be typeset on the metapage, since
3511 fonts have not been loaded yet. You can still place empty boxes. The DPI
3512 setting (resolution) is read from the \.{\\count1} register.
3514 @<Read the metapage heading@>= {
3515 dvi_number n=get_dvi_number(fp,0,4);
3516 if(n) {
3517 fprintf(stderr,"Metapage must be numbered zero (found %d).\n",n);
3518 exit(1);
3520 push_num(get_dvi_number(fp,0,4)); @+ store('D');
3521 fseek(fp,9*4,SEEK_CUR); // Skip other parameters
3522 layer_width=layer_height=0;
3525 @ A stack is kept of the page registers, for use with the |push| and |pop|
3526 commands of a \.{DVI} file. This stack is used by the |read_dvi_page|
3527 subroutine and stores the |quan| registers (described in the next
3528 chapter).
3530 @<Typedefs@>=
3531 typedef struct {
3532 dvi_number h;
3533 dvi_number v;
3534 dvi_number w;
3535 dvi_number x;
3536 dvi_number y;
3537 dvi_number z;
3538 dvi_number hh;
3539 dvi_number vv;
3540 } dvi_stack_entry;
3542 @ @<Global variables@>=
3543 dvi_stack_entry*dvi_stack;
3544 dvi_stack_entry*dvi_stack_ptr;
3546 @ Here is the subroutine to read commands from a DVI page. The file
3547 position should be at the beginning of the page after the |bop| command.
3549 @^pages@>
3551 @-p void read_dvi_page(FILE*fp) {
3552 memusage_log("Beginning of page",fseek(fp));
3553 @<Reset the page registers and stack@>;
3554 typeset_new_page();
3555 @<Read the commands of this page@>;
3556 if(layer_width && layer_height) @<Render this page@>;
3559 @ @<Reset the page registers and stack@>= {
3560 quan('A')=quan('B')=quan('H')=quan('I')=quan('J')=quan('L')=quan('V')=
3561 quan('W')=quan('X')=quan('Y')=quan('Z')=0;
3562 dvi_stack_ptr=dvi_stack;
3565 @ @<Read the commands of this page@>= {
3566 int c,a;
3567 boolean moveaftertyping;
3568 for(;;) {
3569 c=fgetc(fp);
3570 if(c<set1) {
3571 moveaftertyping=1;
3572 @<Typeset character |c| on the current layer@>;
3573 } @+else if(upto4(c,set1)) {
3574 moveaftertyping=1;
3575 c=get_dvi_number(fp,0,c+1-set1);
3576 @<Typeset character |c| on the current layer@>;
3577 } @+else if(c==set_rule || c==put_rule) {
3578 moveaftertyping=(c==set_rule);
3579 c=get_dvi_number(fp,1,4);
3580 a=get_dvi_number(fp,1,4);
3581 @<Typeset |a| by |c| rule on the current layer@>;
3582 } @+else if(upto4(c,put1)) {
3583 moveaftertyping=0;
3584 c=get_dvi_number(fp,0,c+1-put1);
3585 @<Typeset character |c| on the current layer@>;
3586 } @+else if(c==eop) {
3587 break;
3588 } @+else if(c==push) {
3589 if(dvi_stack) @<Push DVI registers to stack@>;
3590 } @+else if(c==pop) {
3591 if(dvi_stack) @<Pop DVI registers from stack@>;
3592 } @+else if(upto4(c,right1)) {
3593 c=get_dvi_number(fp,1,c+1-right1);
3594 horizontal_movement(c);
3595 } @+else if(c==w0) {
3596 horizontal_movement(quan('W'));
3597 } @+else if(upto4(c,w1)) {
3598 c=get_dvi_number(fp,1,c+1-w1);
3599 horizontal_movement(quan('W')=c);
3600 } @+else if(c==x0) {
3601 horizontal_movement(quan('X'));
3602 } @+else if(upto4(c,x1)) {
3603 c=get_dvi_number(fp,1,c+1-x1);
3604 horizontal_movement(quan('X')=c);
3605 } @+else if(upto4(c,down1)) {
3606 c=get_dvi_number(fp,1,c+1-down1);
3607 vertical_movement(c);
3608 } @+else if(c==y0) {
3609 vertical_movement(quan('Y'));
3610 } @+else if(upto4(c,y1)) {
3611 c=get_dvi_number(fp,1,c+1-y1);
3612 vertical_movement(quan('Y')=c);
3613 } @+else if(c==z0) {
3614 vertical_movement(quan('Z'));
3615 } @+else if(upto4(c,z1)) {
3616 c=get_dvi_number(fp,1,c+1-z1);
3617 vertical_movement(quan('Z')=c);
3618 } @+else if(c>=fnt_num_0 && c<fnt1) {
3619 quan('F')=c-fnt_num_0;
3620 } @+else if(upto4(c,fnt1)) {
3621 quan('F')=get_dvi_number(fp,0,c+1-fnt1);
3622 } @+else if(upto4(c,xxx1)) {
3623 c=get_dvi_number(fp,0,c+1-xxx1);
3624 @<Read a special of length |c|@>;
3625 } @+else if(upto4(c,fnt_def1)) {
3626 @<Skip a font definition@>;
3627 } @+else if(c!=nop) {
3628 dvi_error(fp,"Unknown DVI command");
3633 @ @<Push DVI registers to stack@>= {
3634 dvi_stack_ptr->h=quan('H');
3635 dvi_stack_ptr->v=quan('V');
3636 dvi_stack_ptr->w=quan('W');
3637 dvi_stack_ptr->x=quan('X');
3638 dvi_stack_ptr->y=quan('Y');
3639 dvi_stack_ptr->z=quan('Z');
3640 dvi_stack_ptr->hh=quan('I');
3641 dvi_stack_ptr->vv=quan('J');
3642 ++dvi_stack_ptr;
3645 @ @<Pop DVI registers from stack@>= {
3646 --dvi_stack_ptr;
3647 quan('H')=dvi_stack_ptr->h;
3648 quan('V')=dvi_stack_ptr->v;
3649 quan('W')=dvi_stack_ptr->w;
3650 quan('X')=dvi_stack_ptr->x;
3651 quan('Y')=dvi_stack_ptr->y;
3652 quan('Z')=dvi_stack_ptr->z;
3653 quan('I')=dvi_stack_ptr->hh;
3654 quan('J')=dvi_stack_ptr->vv;
3657 @ A special in \TeX nicard is used to execute a special code while reading
3658 the DVI file. Uses might be additional calculations, changes of registers,
3659 special effects, layer selection, etc. All of these possible commands are
3660 dealt with elsewhere in this program. All we do here is to read it and to
3661 send it to the |execute_program| subroutine.
3663 @^specials@>
3665 @<Read a special of length |c|@>= {
3666 char*buf=malloc(c+1);
3667 fread(buf,1,c,fp);
3668 buf[c]=0;
3669 @<Set \.X and \.Y registers to prepare for the special@>;
3670 execute_program(buf);
3671 free(buf);
3674 @ @<Set \.X and \.Y registers to prepare for the special@>= {
3675 registers['X'].is_string=registers['Y'].is_string=0;
3676 registers['X'].number=quan('I');
3677 registers['Y'].number=quan('J');
3680 @ In order to read all the pages for each card, we can skip backwards by
3681 using the back pointers. Either we will print all cards (in reverse
3682 order), or we will print cards listed on the command-line, or we will
3683 print cards listed in a file (this last way might be used to print decks
3684 or booster packs).
3686 Card numbers should be one-based, and should not be negative. Any pages
3687 with negative page numbers will be ignored when it is in the mode for
3688 printing all cards.
3690 @d printing_all_cards 0
3691 @d printing_list 1
3692 @d printing_list_from_file 2
3694 @<Global variables@>=
3695 unsigned char printing_mode;
3696 char*printlisttext;
3697 FILE*printlistfile;
3699 @ @<Read the pages for each card@>= {
3700 dvi_number page_ptr=last_page_ptr;
3701 dvi_number e=0,n; // page numbers
3702 boolean pagenotfound=0;
3703 for(;;) {
3704 @<Read the next entry from the list of pages (if applicable)@>;
3705 try_next_page:
3706 @<Seek the next page to print@>;
3707 @<Read the heading for this page@>;
3708 @<If this page shouldn't be printed now, |goto try_next_page|@>;
3709 pagenotfound=0;
3710 read_dvi_page(fp);
3712 @#done_printing:;
3715 @ @<Read the next entry from the list of pages (if applicable)@>= {
3716 if(printing_mode==printing_list) {
3717 if(!*printlisttext) goto done_printing;
3718 e=strtol(printlisttext,&printlisttext,10);
3719 if(!e) goto done_printing;
3720 if(*printlisttext) printlisttext++;
3721 } @+else if(printing_mode==printing_list_from_file) {
3722 char buf[256];
3723 if(!printlistfile || feof(printlistfile)) goto done_printing;
3724 if(!fgets(buf,255,printlistfile)) goto done_printing;
3725 e=strtol(buf,0,10);
3729 @ @<Seek the next page to print@>= {
3730 if(page_ptr==-1) {
3731 if(pagenotfound) {
3732 fprintf(stderr,"No page found: %d\n",e);
3733 exit(1);
3735 page_ptr=last_page_ptr;
3736 if(printing_mode==printing_all_cards) goto done_printing;
3737 pagenotfound=1;
3739 fseek(fp,page_ptr+1,SEEK_SET);
3742 @ @<Read the heading for this page@>= {
3743 n=quan('P')=get_dvi_number(fp,1,4);
3744 fseek(fp,4,SEEK_CUR);
3745 layer_width=get_dvi_number(fp,1,4);
3746 layer_height=get_dvi_number(fp,1,4);
3747 fseek(fp,4*6,SEEK_CUR);
3748 page_ptr=get_dvi_number(fp,1,4);
3751 @ @<If this page shouldn't be printed now, |goto try_next_page|@>= {
3752 if(n<=0 && printing_mode==printing_all_cards) goto try_next_page;
3753 if(n!=e && printing_mode!=printing_all_cards) goto try_next_page;
3756 @*DVI Font Metrics. Here, the fonts are loaded. It is assumed all fonts
3757 are in the current directory, and the ``area'' of the font name is
3758 ignored. The checksum will also be ignored (it can be checked with
3759 external programs if necessary).
3761 @^area@>
3762 @^font loading@>
3764 @<Read the font definitions and load the fonts@>= {
3765 int c;
3766 for(;;) {
3767 c=fgetc(fp);
3768 if(c==post_post) break;
3769 if(c>=fnt_def1 && c<fnt_def1+4) {
3770 int k=get_dvi_number(fp,0,c+1-fnt_def1);
3771 if(k&~0xFF) dvi_error(fp,"Too many fonts");
3772 memusage_log("Loading font",k);
3773 @<Read the definition for font |k| and load it@>;
3774 } @+else if(c!=nop) {
3775 dvi_error(fp,"Bad command in postamble");
3778 memusage_log("End of postamble",c);
3781 @ When reading fonts, it will be necessary to keep a list of the fonts
3782 and their character indices. Only 256 fonts are permitted in one job.
3784 @<Global variables@>=
3785 data_index fontindex[256];
3787 @ @<Read the definition for font |k| and load it@>= {
3788 dvi_number c=get_dvi_number(fp,0,4); // checksum (unused)
3789 dvi_number s=get_dvi_number(fp,0,4); // scale factor
3790 dvi_number d=get_dvi_number(fp,0,4); // design size
3791 int a=get_dvi_number(fp,0,1); // length of area
3792 int l=get_dvi_number(fp,0,1); // length of name
3793 char n[257];
3794 fseek(fp,a,SEEK_CUR);
3795 fread(n,1,l,fp);
3796 n[l]=0;
3797 if((fontindex[k]=read_gf_file(n,s,d))==none) fonts_okay=0;
3800 @ An important part of reading the font metrics is the width computation,
3801 which involves multiplying the relative widths in the \.{TFM} file (or
3802 \.{GF} file) by the scaling factor in the \.{DVI} file. This
3803 multiplication must be done in precisely the same way by all \.{DVI}
3804 reading programs, in order to validate the assumptions made by \.{DVI}-%
3805 writing programs such as \TeX.
3807 % (The following paragraph is taken directly from "dvitype.web")
3808 Let us therefore summarize what needs to be done. Each width in a \.{TFM}
3809 file appears as a four-byte quantity called a |fix_word|. A |fix_word|
3810 whose respective bytes are $(a,b,c,d)$ represents the number
3811 $$x=\left\{\vcenter{\halign{$#$,\hfil\qquad&if $#$\hfil\cr
3812 b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=0;\cr
3813 -16+b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=255.\cr}}\right.$$
3814 (No other choices of $a$ are allowed, since the magnitude of a \.{TFM}
3815 dimension must be less than 16.) We want to multiply this quantity by the
3816 integer~|z|, which is known to be less than $2^{27}$.
3817 If $|z|<2^{23}$, the individual multiplications $b\cdot z$, $c\cdot z$,
3818 $d\cdot z$ cannot overflow; otherwise we will divide |z| by 2, 4, 8, or
3819 16, to obtain a multiplier less than $2^{23}$, and we can compensate for
3820 this later. If |z| has thereby been replaced by $|z|^\prime=|z|/2^e$, let
3821 $\beta=2^{4-e}$; we shall compute
3822 $$\lfloor(b+c\cdot2^{-8}+d\cdot2^{-16})\,z^\prime/\beta\rfloor$$ if $a=0$,
3823 or the same quantity minus $\alpha=2^{4+e}z^\prime$ if $a=255$.
3824 This calculation must be
3825 done exactly, for the reasons stated above; the following program does the
3826 job in a system-independent way, assuming that arithmetic is exact on
3827 numbers less than $2^{31}$ in magnitude.
3829 \def\zprime{z'}
3831 @f alpha TeX
3832 @f beta TeX
3833 @f zprime TeX
3835 @<Compute |zprime|, |alpha|, and |beta|@>= {
3836 zprime=s; @+ alpha=16;
3837 while(zprime>=040000000) {
3838 zprime>>=1; @+ alpha<<=1;
3840 beta=256/alpha; @+ alpha*=zprime;
3843 @ @<Compute the character width |w|@>= {
3844 w=(((((b3*zprime)>>8)+(b2*zprime))>>8)+(b1*zprime))/beta;
3845 if(b0) w-=alpha;
3848 @*GF Reading. The \.{GF} format is a ``generic font'' format. It has a lot
3849 in common with \.{DVI} format.
3851 At first, names will be given for the commands in a \.{GF} file. Many
3852 commands have the same numbers as they do in a \.{DVI} file (described in
3853 the previous chapter), which makes it very convenient\biblio{This is
3854 probably on purpose for the this very reason, so that a WEB or CWEB
3855 program can use one set of named constants for reading both files.}.
3857 @d paint_0 0 // Paint $d$ pixels black or white [up to 63]
3858 @d paint1 64 // Take parameter, paint pixels [up to 66]
3859 @d boc 67 // Beginning of a character picture
3860 @d boc1 68 // Short form of |boc|
3861 @d eoc 69 // End of a character picture
3862 @d skip0 70 // Skip some rows
3863 @d skip1 71 // Skip some rows [up to 73]
3864 @d new_row_0 74 // Start a new row and move right [up to 238]
3865 @d yyy 243 // Numeric specials
3866 @d no_op 244 // No operation
3867 @d char_loc 245 // Character locator
3868 @d char_loc0 246 // Short form of |char_loc|
3870 @ The |font_struct| structure stores the information for each character in
3871 a font. The |raster| field points to a bitmap with eight pixels per octet,
3872 most significant bit for the leftmost pixel, each row always padded to a
3873 multiple of eight pixels.
3875 While it is reading the postamble, it will fill in this structure with the
3876 |ptr| field set. After the postamble is read, it will fill in the other
3877 fields belonging to its union.
3879 @<Typedefs@>=
3880 typedef struct {
3881 dvi_number dx; // character escapement in pixels
3882 dvi_number w; // width in DVI units
3883 union {
3884 struct {
3885 dvi_number min_n,max_n,min_m,max_m; // bounding box (in pixels)
3886 unsigned short n; // character code number
3887 unsigned char*raster;
3888 unsigned char flag; // bitfield of flags for this character
3889 }@+;
3890 dvi_number ptr;
3891 }@+;
3892 data_index next;
3893 } font_struct;
3895 @ List of flags follows. Some of these flags might be used in order to
3896 allow$\mathord{}>256$ characters per font, since {\TeX} does not have a
3897 command to enter characters with codes more than one byte long. These
3898 flags are specified using numeric specials.
3900 @d ff_select 0x01 // set high octet all characters
3901 @d ff_prefix 0x02 // set high octet for codes 128-255
3902 @d ff_roundafter 0x04 // round $\it hh$ after sending character
3903 @d ff_roundbefore 0x08 // round $\it hh$ before sending character
3904 @d ff_reset 0x10 // reset high octet
3905 @d ff_strip 0x20 // strip highest bit of prefix
3906 @d ff_space 0x40 // do not save the raster (space only)
3907 @d ff_reserved 0x80 // {\bf DO NOT USE}
3909 @ @<Global variables@>=
3910 memory_of(font_struct) font_data;
3912 @ @<Initialize memory@>= init_memory(font_data,4);
3914 @ When loading a \.{GF} font, the filename will contain the resolution
3915 in dots per inch.
3917 @^font loading@>
3919 @-p data_index read_gf_file(char*fontname,dvi_measure s,dvi_measure d) {
3920 unsigned int dpi=(resolution*unit_mag*s+500*d)/(100000*d);
3921 FILE*fp;
3922 data_index index=none;
3923 data_index first_index=none;
3924 data_index last_index=none;
3925 dvi_number zprime,alpha,beta; // used for width computation
3926 @<Compute |zprime|, |alpha|, and |beta|@>;
3927 @<Figure out the filename and open the file, |return none| if it can't@>;
3928 @<Skip to the postamble of the \.{GF} file@>;
3929 @<Read the character locators@>;
3930 @<Read the character rasters and flags@>;
3931 fclose(fp);
3932 return last_index;
3935 @ When figuring out the filename, it will send it to standard output so
3936 that a list can be made of the required fonts.
3938 @<Figure out the filename and open the file, ...@>= {
3939 char n[295];
3940 sprintf(n,"%s.%dgf",fontname,dpi);
3941 printf("%s\n",n);
3942 fp=fopen(n,"rb");
3943 if(!fp) return none;
3946 @ @<Skip to the postamble of the \.{GF} file@>= {
3947 int c;
3948 fseek(fp,-4,SEEK_END);
3949 while(fgetc(fp)==223) fseek(fp,-2,SEEK_CUR);
3950 fseek(fp,-5,SEEK_CUR);
3951 fseek(fp,get_dvi_number(fp,0,4)+37,SEEK_SET); // nothing matters anymore
3954 @ @<Read the character locators@>= {
3955 int c,b0,b1,b2,b3;
3956 dvi_number dx,w,p;
3957 for(;;) {
3958 c=fgetc(fp);
3959 if(c==post_post) break;
3960 p=-1;
3961 if(c==char_loc) {
3962 @<Read a long character locator@>;
3963 } @+else if(c==char_loc0) {
3964 @<Read a short character locator@>;
3965 } @+else if(c!=no_op) {
3966 fprintf(stderr,"Bad command in GF postamble.\n");
3967 fprintf(stderr,"(Command %d, address %08X)\n",c,ftell(fp)-1);
3968 exit(1);
3970 if(p!=-1) @<Defer this character locator into |font_data|@>;
3972 last_index=index;
3975 @ There are some parameters we do not care about. First is $c$, which is
3976 the character code residue (modulo 256). This is not important since it
3977 is duplicated in the |boc| heading for each character. The second
3978 parameter which we do not care about is the $\it dy$ parameter, since it
3979 should be zero for \.{DVI} files.
3981 @<Read a long character locator@>= {
3982 fseek(fp,1,SEEK_CUR);
3983 dx=get_dvi_number(fp,1,4)>>16;
3984 fseek(fp,4,SEEK_CUR);
3985 @<Read four bytes@>;
3986 p=get_dvi_number(fp,1,4);
3989 @ @<Read a short character locator@>= {
3990 fseek(fp,1,SEEK_CUR);
3991 dx=get_dvi_number(fp,0,1);
3992 @<Read four bytes@>;
3993 p=get_dvi_number(fp,1,4);
3996 @ @<Read four bytes@>= {
3997 b0=fgetc(fp);@+b1=fgetc(fp);@+b2=fgetc(fp);@+b3=fgetc(fp);
4000 @ This processing is deferred, and the rest of the parameters will be
4001 filled in later (and the |ptr| field will be overwritten since it will
4002 no longer be needed at that time).
4004 @<Defer this character locator into |font_data|@>= {
4005 data_index n=new_record(font_data);
4006 @<Compute the character width |w|@>;
4007 font_data.data[n].next=index;
4008 font_data.data[n].dx=dx;
4009 font_data.data[n].w=w;
4010 font_data.data[n].ptr=p;
4011 if(index==none) first_index=n;
4012 index=n;
4015 @ Now is time to go through the list we made up and this time actually
4016 fill in the parameters and pictures.
4018 @<Read the character rasters and flags@>= {
4019 while(index!=none) {
4020 fseek(fp,font_data.data[index].ptr,SEEK_SET);
4021 font_data.data[index].flag=0;
4022 font_data.data[index].raster=0;
4023 @<Read commands for this character@>;
4024 @#index=font_data.data[index].next;
4028 @ Painting the picture uses the value of |paint_switch| to determine
4029 to draw or skip. The current position in the array |raster| is also
4030 pointed by the |pic| pointer. Note that |black| and |white| are not
4031 necessary black and white (but they are on normal paper).
4033 Note the value of $n$ is not needed since the |pic| pointer automatically
4034 keeps track of this kinds of stuff. However, |m| is needed because of
4035 commands that can skip rows, to know how many columns must be skipped to
4036 reach the next row. There is also |b|, which keeps track of the bit
4037 position in the current byte.
4039 @d white 0
4040 @d black 1
4042 @d reset_m
4043 m=(font_data.data[index].max_m-font_data.data[index].min_m)/8+1@;
4045 @<Read commands for this character@>= {
4046 unsigned int c,m,b;
4047 unsigned char*pic;
4048 boolean paint_switch;
4049 for(;;) {
4050 c=fgetc(fp);
4051 if(c<paint1) {
4052 @<Paint |c| pixels |black| or |white|@>;
4053 } @+else if(c>=paint1 && c<paint1+3) {
4054 c=get_dvi_number(fp,0,c+1-paint1);
4055 @<Paint |c| pixels |black| or |white|@>;
4056 } @+else if(c==boc) {
4057 @<Initialize parameters and picture (long form)@>;
4058 } @+else if(c==boc1) {
4059 @<Initialize parameters and picture (short form)@>;
4060 } @+else if(c==eoc) {
4061 break; // Well Done!
4062 } @+else if(upto4(c,skip0)) {
4063 if(c==skip0) c=0;
4064 else c=get_dvi_number(fp,0,c+1-skip1);
4065 @<Finish a row and skip |c| rows@>;
4066 } @+else if(c>=new_row_0 && c<=new_row_0+164) {
4067 c-=new_row_0;
4068 @<Finish a row and skip |c| columns@>;
4069 } @+else if(c==yyy) {
4070 font_data.data[index].flag|=get_dvi_number(fp,0,4)>>16;
4071 } @+else if(c!=no_op) {
4072 fprintf(stderr,"Unknown GF command!\n");
4073 fprintf(stderr,"(Command %d, address %08X)\n",c,ftell(fp)-1);
4078 @ Actually |m| is something a bit different than the standard, because |m|
4079 now tells how many bytes are remaining in the current row.
4081 @d pic_rows (1+font_data.data[index].max_n-font_data.data[index].min_n)
4083 @<Initialize parameters and picture (long form)@>= {
4084 font_data.data[index].n=get_dvi_number(fp,0,4);
4085 @<Deal with $p$ (pointer to previous character with same metrics)@>;
4086 font_data.data[index].min_m=get_dvi_number(fp,1,4);
4087 font_data.data[index].max_m=get_dvi_number(fp,1,4);
4088 font_data.data[index].min_n=get_dvi_number(fp,1,4);
4089 font_data.data[index].max_n=get_dvi_number(fp,1,4);
4090 @<Initialize picture@>;
4093 @ @<Initialize picture@>= {
4094 if(font_data.data[index].flag&ff_space) break;
4095 paint_switch=white;
4096 reset_m;
4097 b=0;
4098 pic=font_data.data[index].raster=malloc(m*pic_rows+1);
4099 memset(pic,0,m*pic_rows);
4102 @ @<Initialize parameters and picture (short form)@>= {
4103 int d;
4104 font_data.data[index].n=get_dvi_number(fp,0,1);
4105 d=get_dvi_number(fp,0,1);
4106 font_data.data[index].max_m=get_dvi_number(fp,0,1);
4107 font_data.data[index].min_m=font_data.data[index].max_m-d;
4108 d=get_dvi_number(fp,0,1);
4109 font_data.data[index].max_n=get_dvi_number(fp,0,1);
4110 font_data.data[index].min_n=font_data.data[index].max_n-d;
4111 @<Initialize picture@>;
4114 @ The pointers to other characters will also be deferred in the same way
4115 as the character locators, but this time from the other end. Now, once it
4116 is finished all the characters, it will {\sl automatically} know to read
4117 the next one properly! (Now you can see what the purpose of the
4118 |@!first_index| variable is.)
4120 @<Deal with $p$ (pointer to previous character with same metrics)@>= {
4121 dvi_number p=get_dvi_number(fp,1,4);
4122 if(p!=-1) {
4123 data_index i=new_record(font_data);
4124 font_data.data[i].next=none;
4125 font_data.data[i].dx=font_data.data[index].dx;
4126 font_data.data[i].w=font_data.data[index].w;
4127 font_data.data[i].ptr=p;
4128 font_data.data[first_index].next=i;
4129 first_index=i;
4133 @ Now we get to the actual painting. We can assume the value of |m| is
4134 never negative and that everything else is also okay.
4136 @<Paint |c| pixels |black| or |white|@>= {
4137 if(paint_switch) {
4138 if(b+c<=8) {
4139 @<Paint a small block of pixels in the current byte@>;
4140 } else {
4141 @<Paint the rest of the pixels in the current byte@>;
4142 @<Fill up the bytes in the middle@>;
4143 @<Clear the pixels needed clearing at the end@>;
4146 @<Update |paint_switch|, |pic|, |b|, and |m|@>;
4149 @ @<Update |paint_switch|, |pic|, |b|, and |m|@>= {
4150 paint_switch^=1;
4151 b+=c;
4152 pic+=b>>3;
4153 m-=b>>3;
4154 b&=7;
4157 @ @<Paint a small block of pixels in the current byte@>= {
4158 *pic|=(0xFF>>b)&~(0xFF>>(b+c));
4161 @ @<Paint the rest of the pixels in the current byte@>= {
4162 *pic|=0xFF>>b;
4165 @ @<Fill up the bytes in the middle@>= {
4166 memset(pic+1,0xFF,(c+b)>>3);
4169 @ @<Clear the pixels needed clearing at the end@>= {
4170 pic[(c+b)>>3]&=~(0xFF>>((c+b)&7));
4173 @ @<Finish a row and skip |c| rows@>= {
4174 pic+=m;
4175 b=0;
4176 reset_m;
4177 pic+=m*c;
4178 paint_switch=white;
4181 @ @<Finish a row and skip |c| columns@>= {
4182 pic+=m;
4183 reset_m;
4184 m-=c>>3;
4185 pic+=c>>3;
4186 b=c&7;
4187 paint_switch=black;
4190 @ @<Display font information@>= {
4191 data_index i;
4192 foreach(i,font_data) {
4193 printf("[%d] box=(%d,%d,%d,%d) dx=%d w=%d n=%d flag=%d [%d]\n"
4194 ,i,font_data.data[i].min_n,font_data.data[i].max_n
4195 ,font_data.data[i].min_m,font_data.data[i].max_m
4196 ,font_data.data[i].dx,font_data.data[i].w,font_data.data[i].n
4197 ,font_data.data[i].flag,font_data.data[i].next
4202 @*Layer Computation. Now is the chapter for actually deciding rendering on
4203 the page, where everything should go, etc.$^{[\TeXwareBiblio]}$
4205 @<Global variables@>=
4206 dvi_measure unit_num; // Numerator for units of measurement
4207 dvi_measure unit_den; // Denominator for units of measurement
4208 dvi_measure unit_mag; // Magnification for measurement
4209 dvi_measure unit_conv; // Conversion factor
4211 @ There are also a number of ``internal typesetting quantities''. These
4212 are parameters stored in a separate array, and are used to keep track of
4213 the current state of the typesetting. They are labeled with letters from
4214 \.A to \.Z. They can be modified inside of specials, although some of them
4215 probably shouldn't be modified in this way. Here is the list of them:
4217 \.A, \.B: Horizontal and vertical offset added to \.I and \.J.
4219 \.C: Character code prefix. If bit eight is not set, it only affects
4220 character codes with bit seven set.
4222 \.D: Maximum horizontal drift (in pixels), meaning how far away the \.I
4223 and \.J parameters are allowed to be from the correctly rounded values.
4225 \.E: Maximum vertical drift.
4227 \.F: The current font.
4229 \.H: The horizontal position on the page, in DVI units.
4231 \.I: The horizontal position on the page, in pixels.
4233 \.J: The vertical position on the page, in pixels.
4235 \.L: The current layer number. If this is zero, nothing is placed on the
4236 page, although the positions can still be changed and specials can still
4237 be used.
4239 \.P: Page number. This is used to determine the filename of output.
4241 \.R, \.S: The limits for when horizontal motion should add the number of
4242 pixels or when it should recalculate the pixels entirely.
4244 \.T, \.U: Like \.R and \.S, but for vertical motions.
4246 \.V: The vertical position on the page, in DVI units.
4248 \.W, \.X, \.Y, \.Z: The current spacing amounts, in DVI units.
4250 @d quan(_name) (type_quan[(_name)&0x1F])
4252 @<Global variables@>=
4253 dvi_number type_quan[32];
4255 @ @<Cases for system commands@>=
4256 @-case 'm': {
4257 // Modify an internal typesetting quantity
4258 if(stack_ptr->is_string) program_error("Type mismatch");
4259 quan(*++ptr)=pop_num();
4260 break;
4263 @ The conversion factor |unit_conv| is figured as follows: There are
4264 exactly |unit_num/unit_den| decimicrons per DVI unit, and 254000
4265 decimicrons per inch, and |resolution/100| pixels per inch. Then we have
4266 to adjust this by the magnification |unit_mag|.
4268 Division must be done slightly carefully to avoid overflow.
4270 @d resolution (registers['D'].number)
4272 @<Compute the conversion factor@>= {
4273 unit_conv=make_fraction(unit_num*resolution*unit_mag,unit_den*100000);
4274 unit_conv/=254000;
4277 @ Here are the codes to compute movements. The definition of \.{DVI} files
4278 refers to six registers which hold integer values in DVI units. However,
4279 we also have two more registers, for horizontal and vertical pixel units.
4281 A sequence of characters or rules might cause the pixel values to drift
4282 from their correctly rounded values, since they are not usually an exact
4283 integer number of pixels.
4285 @d to_pixels(_val) round((_val)*unit_conv)
4287 @-p void horizontal_movement(dvi_number x) {
4288 quan('H')+=x;
4289 if(x>quan('S') || x<quan('R')) {
4290 quan('I')=to_pixels(quan('H'));
4291 } @+else {
4292 quan('I')+=to_pixels(x);
4293 if(to_pixels(quan('H'))-quan('I')>quan('D'))
4294 quan('I')=to_pixels(quan('H'))+quan('D');
4295 if(to_pixels(quan('H'))-quan('I')<-quan('D'))
4296 quan('I')=to_pixels(quan('H'))-quan('D');
4300 @ @-p void vertical_movement(dvi_number x) {
4301 quan('V')+=x;
4302 if(x>quan('U') || x<quan('T')) {
4303 quan('J')=to_pixels(quan('V'));
4304 } @+else {
4305 quan('J')+=to_pixels(x);
4306 if(to_pixels(quan('V'))-quan('J')>quan('E'))
4307 quan('J')=to_pixels(quan('V'))+quan('E');
4308 if(to_pixels(quan('V'))-quan('J')<-quan('E'))
4309 quan('J')=to_pixels(quan('V'))-quan('E');
4313 @ This is now the part that does actual sending. When many characters
4314 come next to each other, the rounding will be done such that the number
4315 of pixels between two letters will always be the same whenever those two
4316 letters occur next to each other.
4318 @<Typeset character |c| on the current layer@>= {
4319 data_index n=fontindex[quan('F')&0xFF];
4320 if((quan('C')&0x100) || (c&0x80)) c|=quan('C')<<8;
4321 while(n!=none && c!=font_data.data[n].n)
4322 n=font_data.data[n].next;
4323 if(n==none) dvi_error(fp,"Character not in font");
4324 @<Typeset the character and update the current position@>;
4325 @<Update the character code prefix@>;
4328 @ @<Typeset the character and update the current position@>= {
4329 if(font_data.data[n].flag&ff_roundbefore)
4330 quan('I')=to_pixels(quan('H'));
4331 if(quan('L') && font_data.data[n].raster) typeset_char_here(n);
4332 if(moveaftertyping) {
4333 quan('H')+=font_data.data[n].w;
4334 quan('I')+=font_data.data[n].dx;
4335 if(font_data.data[n].flag&ff_roundafter)
4336 quan('I')=to_pixels(quan('H'));
4337 else horizontal_movement(0);
4341 @ If you have a typesetting program that can ship out characters with
4342 codes more than eight bits long, you won't need this. It is provided for
4343 use with normal {\TeX} system.
4345 @<Update the character code prefix@>= {
4346 if(font_data.data[n].flag&ff_strip) c&=0x7F; else c&=0xFF;
4347 if(font_data.data[n].flag&ff_select) quan('C')=c|0x100;
4348 if(font_data.data[n].flag&ff_prefix) quan('C')=c;
4349 if(font_data.data[n].flag&ff_reset) quan('C')=0;
4352 @ The number of pixels in the height or width of a rule will always be
4353 rounded up. However, unlike DVItype, this program has no floating point
4354 rounding errors.
4356 @d to_rule_pixels(_val) ceiling((_val)*unit_conv)
4358 @<Typeset |a| by |c| rule on the current layer@>= {
4359 dvi_number x=to_rule_pixels(a);
4360 dvi_number y=to_rule_pixels(c);
4361 if(quan('L') && a>0 && c>0) typeset_rule_here(x,y);
4362 if(moveaftertyping) {
4363 quan('I')+=x;
4364 horizontal_movement(0);
4368 @ Sometimes you might want DVI units converted to pixels inside of a user
4369 program contained in a DVI file. Here is how it is done.
4371 @<Cases for system commands@>=
4372 @-case 'C': {
4373 // Convert DVI units to pixels
4374 if(stack_ptr->is_string) program_error("Type mismatch");
4375 stack_ptr->number=to_pixels(stack_ptr->number);
4376 break;
4379 @*Layer Rendering. Please note, these numbers are |short|, which means
4380 that you cannot have more than 65536 pixels in width or in height. This
4381 should not be a problem, because even if you have 3000 dots per inch, and
4382 each card is 10 inches long, that is still only 30000 which is less than
4383 half of the available width. (All units here are in pixels.)
4385 In order to save memory, all typeset nodes are stored in one list at
4386 first, and then rendered to a pixel buffer as each layer is being written
4387 out to the \.{PBM} file, and then the buffer can be freed (or reset to
4388 zero) afterwards to save memory.
4390 @<Typedefs@>=
4391 typedef struct {
4392 unsigned short x; // X position on page
4393 unsigned short y; // Y position on page
4394 union {
4395 struct {
4396 unsigned short w; // Width of rule
4397 unsigned short h; // Height of rule
4398 }@+;
4399 data_index c; // Character index in |font_data|
4400 }@+;
4401 unsigned char l; // Layer (high bit set for rules)
4402 } typeset_node;
4404 @ @<Global variables@>=
4405 memory_of(typeset_node) typeset_nodes;
4407 @ @<Initialize memory@>= init_memory(typeset_nodes,8);
4409 @ We also have variables for the layer size (loaded from \.{\\count2}
4410 and \.{\\count3} registers for the current page). If they are both zero,
4411 then nothing will be rendered.
4413 @<Global variables@>=
4414 unsigned short layer_width;
4415 unsigned short layer_height;
4417 @ Here are the subroutines which typeset characters and rules onto the
4418 page buffer. They are not rendered into a picture yet.
4420 @d typeset_new_page() (typeset_nodes.used=0)
4421 @d typeset_rule_here(_w,_h) typeset_rule(quan('I'),quan('J'),(_w),(_h));
4422 @d typeset_char_here(_ch) typeset_char(quan('I'),quan('J'),(_ch));
4424 @-p void typeset_rule(int x,int y,int w,int h) {
4425 data_index n=new_record(typeset_nodes);
4426 @<Ensure |w| and |h| are not too large to fit on the page@>;
4427 typeset_nodes.data[n].x=x;
4428 typeset_nodes.data[n].y=y;
4429 typeset_nodes.data[n].w=w;
4430 typeset_nodes.data[n].h=h;
4431 typeset_nodes.data[n].l=quan('L')|0x80;
4434 @ @<Ensure |w| and |h| are not too large to fit on the page@>= {
4435 if(x+w>layer_width) w=layer_width-x;
4436 if(y+h>layer_height) h=layer_height-y;
4439 @ @-p void typeset_char(int x,int y,data_index c) {
4440 data_index n=new_record(typeset_nodes);
4441 typeset_nodes.data[n].x=x;
4442 typeset_nodes.data[n].y=y;
4443 typeset_nodes.data[n].c=c;
4444 typeset_nodes.data[n].l=quan('L');
4447 @ Here is a variable |image|. This is a pointer to the buffer for the
4448 picture of the current layer, in \.{PBM} format. The internal quantity
4449 \.L should be set now to the largest layer number in use, at the end of
4450 the page, because it is used to determine how many layers must be sent to
4451 the output.
4453 @d image_max (image+layer_size)
4455 @<Global variables@>=
4456 unsigned char*image;
4458 @ @<Render this page@>= {
4459 unsigned int row_size=((layer_width+7)>>3);
4460 unsigned int layer_size=row_size*layer_height;
4461 image=malloc(layer_size+1);
4462 while(quan('L')) {
4463 memset(image,0,layer_size);
4464 @<Read the |typeset_nodes| list and render any applicable nodes@>;
4465 @<Send the current layer to a file@>;
4466 --quan('L');
4468 free(image);
4471 @ @<Read the |typeset_nodes| list and render any applicable nodes@>= {
4472 data_index i;
4473 foreach(i,typeset_nodes) {
4474 if((typeset_nodes.data[i].l&0x7F)==quan('L')) {
4475 if(typeset_nodes.data[i].l&0x80) {
4476 @<Render a rule node@>;
4477 } @+else {
4478 @<Render a character node@>;
4484 @ In order to render a rule node (which is a filled |black| rectangle), it
4485 is split into rows, and each row is split into three parts: the left end,
4486 the filling, and the right end. However, if the width is sufficiently
4487 small, it will fit in one byte and will not have to be split in this way.
4489 There are also some checks to ensure that the entire rectangle will be
4490 within the bounds of the image.
4492 @<Render a rule node@>= {
4493 int y=1+typeset_nodes.data[i].y-typeset_nodes.data[i].h;
4494 int x=typeset_nodes.data[i].x;
4495 int w=typeset_nodes.data[i].w;
4496 if(y<0) y=0;
4497 if(typeset_nodes.data[i].y>=layer_height)
4498 typeset_nodes.data[i].y=layer_height-1;
4499 if((x&7)+w<=8) {
4500 @<Render a small rule node@>;
4501 } @+else {
4502 @<Render a large rule node@>;
4506 @ @<Render a small rule node@>= {
4507 for(;y<=typeset_nodes.data[i].y;y++) {
4508 image[y*row_size+(x>>3)]|=(0xFF>>(x&7))&~(0xFF>>((x&7)+w));
4512 @ @<Render a large rule node@>= {
4513 for(;y<=typeset_nodes.data[i].y;y++) {
4514 unsigned char*p=image+(y*row_size+(x>>3));
4515 *p++|=0xFF>>(x&7); // left
4516 memset(p,0xFF,((x&7)+w)>>3); // filling
4517 p[((x&7)+w)>>3]|=~(0xFF>>((x+w)&7)); // right
4521 @ Character nodes are a bit different. The pictures are already stored,
4522 now we have to paste them into the layer picture. Since they will not
4523 always be aligned to a multiple to eight columns (one byte), it will have
4524 to shift out and shift in.
4526 Again, it is necessary to ensure it doesn't go out of bounds. It has to be
4527 a bit more careful for characters than it does for rules. Also note that
4528 the \.{GF} format does not require that |min_m| and so on are the tightest
4529 bounds possible.
4531 @<Render a character node@>= {
4532 unsigned int ch=typeset_nodes.data[i].c;
4533 unsigned int x=typeset_nodes.data[i].x+font_data.data[ch].min_m;
4534 unsigned int y=typeset_nodes.data[i].y-font_data.data[ch].max_n;
4535 unsigned int z=typeset_nodes.data[i].y-font_data.data[ch].min_n;
4536 unsigned int w=(font_data.data[ch].max_m-font_data.data[ch].min_m)/8+1;
4537 register unsigned char sh=x&7; // shifting amount for right shift
4538 register unsigned char lsh=8-sh; // shifting amount for left shift
4539 unsigned char*p=image+(y*row_size+(x>>3));
4540 unsigned char*q=font_data.data[ch].raster;
4541 @<Cut off the part of character above the top of the layer image@>;
4542 while(y<=z && p+w<image_max) {
4543 @<Render the current row of the character raster@>;
4544 @<Advance to the next row of the character@>;
4548 @ @<Cut off the part of character above the top of the layer image@>= {
4549 if(y<0) {
4550 p-=row_size*y;
4551 q-=w*y;
4552 y=0;
4554 if(p<image) p=image;
4557 @ @<Render the current row of the character raster@>= {
4558 int j;
4559 for(j=0;j<w;j++) {
4560 p[j]|=q[j]>>sh;
4561 p[j+1]|=q[j]<<lsh;
4565 @ @<Advance to the next row of the character@>= {
4566 y++;
4567 q+=w;
4568 p+=row_size;
4571 @ Layer files are output in \.{PBM} format, which is very similar to the
4572 format which this program uses internally. ImageMagick is capable of
4573 reading this format.
4575 @.PBM@>
4576 @^Portable Bitmap@>
4577 @^ImageMagick@>
4578 @^output@>
4580 @<Send the current layer to a file@>= {
4581 FILE*fp;
4582 char filename[256];
4583 sprintf(filename,"P%dL%d.pbm",quan('P'),quan('L'));
4584 fp=fopen(filename,"wb");
4585 fprintf(fp,"P4%d %d ",layer_width,layer_height);
4586 fwrite(image,1,layer_size,fp);
4587 fclose(fp);
4590 @ @<Display the list of typeset nodes@>= {
4591 data_index i;
4592 foreach(i,typeset_nodes) {
4593 if(typeset_nodes.data[i].l&0x80) {
4594 printf("[%d] %dx%d%+d%+d\n",typeset_nodes.data[i].l&0x7F
4595 ,typeset_nodes.data[i].w,typeset_nodes.data[i].h
4596 ,typeset_nodes.data[i].x,typeset_nodes.data[i].y
4598 } @+else {
4599 printf("[%d] %d(%d) %+d%+d\n",typeset_nodes.data[i].l
4600 ,typeset_nodes.data[i].c,font_data.data[typeset_nodes.data[i].c].n
4601 ,typeset_nodes.data[i].x,typeset_nodes.data[i].y
4607 @ @<Display typesetting diagnostics@>= {
4608 int i;
4609 for(i=0;i<32;i++) {
4610 if(type_quan[i]) printf("%c=%d\n",i+'@@',type_quan[i]);
4612 printf("unit_conv: %lld [%d]\n",unit_conv,round(unit_conv));
4613 printf("nodes: %d/%d\n",typeset_nodes.used,typeset_nodes.allocated);
4614 printf("fonts: %d/%d\n",font_data.used,font_data.allocated);
4615 if(dvi_stack) printf("stack: %d\n",dvi_stack_ptr-dvi_stack);
4618 @*Process of ImageMagick. The filename of ImageMagick \.{convert} is found
4619 by using the \.{IMCONVERT} environment variable. The entire command-line
4620 is stored in the \.Q register, with arguments separated by spaces, and it
4621 might be very long.
4623 @^ImageMagick@>
4624 @.IMCONVERT@>
4626 @d add_magick_arg(_val) magick_args.data[new_record(magick_args)]=_val
4628 @<Typedefs@>=
4629 typedef char*char_ptr;
4631 @ @<Global variables@>=
4632 memory_of(char_ptr) magick_args;
4634 @ @<Switch to ImageMagick@>= {
4635 init_memory(magick_args,4);
4636 add_magick_arg("convert"); // |argv[0]| (program name)
4637 @<Add arguments from \.Q register@>;
4638 add_magick_arg(0); // (terminator)
4639 @<Call the ImageMagick executable file@>;
4642 @ The \.Q register will be clobbered here. But that is OK since it will no
4643 longer be used within \TeX nicard.
4645 @<Add arguments from \.Q register@>= {
4646 char*q=registers['Q'].text;
4647 char*p;
4648 while(q && *q) {
4649 p=q;
4650 if(q=strchr(q,' ')) *q++=0;
4651 if(*p) add_magick_arg(p);
4655 @ @<Call the ImageMagick executable file@>= {
4656 char*e=getenv("IMCONVERT");
4657 if(!e) @<Display the arguments and quit@>;
4658 execv(e,magick_args.data);
4659 fprintf(stderr,"Unable to run ImageMagick\n");
4660 return 1;
4663 @ @<Display the arguments and quit@>= {
4664 data_index i;
4665 char*p;
4666 foreach(i,magick_args) if(p=magick_args.data[i]) printf("%s\n",p);
4667 return 0;
4670 @*Main Program. This is where the program starts and ends. Everything else
4671 in the other chapters is started from here.
4673 @<Include files@>=
4674 #include <stdio.h>
4675 #include <stdlib.h>
4676 #include <string.h>
4677 #include <unistd.h>
4678 #include <time.h>
4680 @ @-p int main(int argc,char**argv) {
4681 boolean dvi_mode=0;
4682 @<Initialize memory@>;
4683 @<Display the banner message@>;
4684 @<Decide whether in DVI reading mode@>;
4685 if(!dvi_mode) @<Open the main input file@>;
4686 @<Initialize the input states@>;
4687 @<Initialize the tables and registers@>;
4688 @<Initialize the random number generator@>;
4689 @<Set registers according to command-line parameters@>;
4690 if(!dvi_mode) @<Process the input files@>;
4691 if(dvi_mode) dvi_mode=read_dvi_file(argv[1]);
4692 @<Call program in \.Z register if necessary@>;
4693 if(!dvi_mode) @<Send |end_transmission| to each card area@>;
4694 @<Write the output files@>;
4695 if(registers['Q'].is_string && dvi_mode &&
4696 (argv[0][0]!='-' || argv[0][1]!='z')) @<Switch to ImageMagick@>;
4697 return 0;
4700 @ @<Display the banner message@>= {
4701 fprintf(stderr,"TeXnicard version %s\n",version_string);
4702 fprintf(stderr,
4703 "This program is free software and comes with NO WARRANTY.\n");
4704 fflush(stderr);
4707 @ @<Set registers according to command-line parameters@>= {
4708 int i;
4709 for(i=2;i<argc;i++) {
4710 registers[i+('0'-2)].is_string=1;
4711 registers[i+('0'-2)].text=strdup(argv[i]);
4715 @ The main input file will be either the terminal, or another file if the
4716 command-line argument is given.
4718 @<Open the main input file@>= {
4719 if(argc>1 && strcmp(argv[1],"-")!=0) {
4720 --current_input_file;
4721 open_input(argv[1]);
4722 } @+else {
4723 current_fp=0;
4724 strcpy(current_filename,"<Teletype>");
4728 @ @<Call program in \.Z register if necessary@>= {
4729 if(registers['Z'].is_string) execute_program(registers['Z'].text);
4732 @ The alternative mode to run this program is DVI mode. DVI mode is
4733 specified by a command-line switch.
4735 @.DVI@>
4737 @<Decide whether in DVI reading mode@>= {
4738 if(argv[1][0]=='-' && argv[1][1]) {
4739 dvi_mode=1;
4740 argv++; @+ argc--;
4741 if(argv[0][1]=='a') {
4742 printing_mode=printing_all_cards;
4743 } @+else if(argv[0][1]=='f') {
4744 printing_mode=printing_list_from_file;
4745 printlistfile=fopen(argv[1],"r");
4746 argv++; @+ argc--;
4747 } @+else if(argv[0][1]=='n') {
4748 printing_mode=printing_list;
4749 printlisttext=argv[1];
4750 argv++; @+ argc--;
4751 } @+else if(argv[0][1]=='z') {
4752 printing_mode=printing_list;
4753 printlisttext="";
4758 @*The Future. Here are some ideas for future versions of this program:
4760 $\bullet$ A customizable Inform7-like parser, that would compile into a C
4761 code, so that you can play the cards on rule-enforcing computer programs.
4762 @^Inform@>
4764 $\bullet$ A database to keep track of how many copies of a card have been
4765 sold, for inventory purposes.
4766 @^commercial viability@>
4768 $\bullet$ Full text search, for things such as the Oracle text search.
4769 @^Oracle@>
4771 $\bullet$ Allow more than 256 fonts in one card set.
4773 $\bullet$ Unicode input (UTF-8).
4775 $\bullet$ Big spider!
4776 @^arachnids@>
4777 @^spider@>
4779 @*Bibliography.
4781 \count255=0 %
4782 \long\def\Par{\csname par\endcsname}%
4783 \loop\ifnum\count255<\bibliocount%
4784 \advance\count255 by 1
4785 \Par$^{[\the\count255]}$\csname biblio \the\count255\endcsname\Par%
4786 \repeat%
4788 @*Index. Here you can find references to the definition and use of all the
4789 variables, subroutines, etc.\ used in this program, as well as a few other
4790 things of interest. Underlined entries indicate where it is defined.
4792 {\bf Important note:} All the numbers in this index are section numbers,
4793 not page numbers.
4795 % End of file "texnicard.w"