4 % Licensed by GNU GPL v3 or later version.
6 \def\contentspagenumber
{1}\pageno
=3
7 \def\title
{\TeX nicard
}
8 \def\covernote
{{\fiverm Batteries not included. Do not use this book as a
9 flotation device. This is free software
; see source file for details.
}}
11 % Prevent \outer from getting in the way
, stupid
!
15 ``u
{YJ
"@<Predeclaration of procedures@>=
22 \long\def\IndexCharacter#1':{`\.{\char`#1}'}
25 qAqA
/@
!@^\IndexCharacter\
36 \newcount\bibliocount \bibliocount
=0
38 \advance\bibliocount by
1 %
39 $^
{[\the\bibliocount
]}$
%
40 \expandafter\def\csname biblio \the\bibliocount\endcsname
{#
1}%
43 \emergencystretch
=\hsize
47 \rlap
{\vrule height
3.2pt depth
-2.5pt width \wd0
}{\box0
}%
50 \def\sectionnumber\PB#
1{\sectionnumbernext#
1}
51 \def\sectionnumbernext$\
X#
1:#
2\
X$
{#
1}
53 @
*Introduction. This is \TeX nicard
, a program designed for similar
54 purposes of Magic Set Editor
, but in a different
(and better
) way. It
55 should be able to produce higher quality cards than Wizards of the Coast
,
56 and then they ought to use this program
, too
!
59 @^Wizards of the Coast@
>
60 @^commercial viability@
>
63 @
<Memory usage logging@
>@
;
64 @
<Interpreted C codes@
>@
;
69 @
<The include file for memory managed types@
>@
;
70 @
<Global variables@
>@
;
71 @
<Predeclaration of procedures@
>@
;
74 @ This line below should be changed with the current version number
,
75 whenever a new version is released.
(If you fork this program
, you should
76 also include some indication of forking in the \\
{version\_string
}.
)
77 % (it doesn't work if I use vertical bars here
)
81 @d version_string
"0.1"
82 @d version_number
1 // one major is worth ten minors
85 typedef unsigned char boolean
;
87 @ You might be wondering what this section is for
(especially since it
88 appears to be unused
). The reason is that some metamacros use it in order
89 to force the compiler to know the correct line numbers
(in case some lines
90 have been added by metamacros
).
95 @
<Nothing~@
>= /* ...
*/
97 @ There is also memory usage logging. If it is not being compiled for
98 memory usage logging
, it should just ignore these kind of commands.
100 @
<Memory usage logging@
>=
101 #ifndef @
!memusage_log
102 #define @
[memusage_log
(_text
,_arg1
)@
]
105 @
*Memory Management. This program uses a lot of similar memory management
,
106 so they will be defined in this chapter.
108 @^memory management@
>
110 @d none
-1 // indication that a |data_index| means nothing
114 char
*data
; // pointer to array of blocks
(|char
*| for use with |sizeof|
)
115 int used
; // number of blocks used
116 int allocated
; // number of blocks allocated
118 @#typedef int data_index
;
120 @ We will use an interpreted C code here
, which will send output to a
121 header file |
"memory_management.h"|.
123 @
<The include file for memory managed types@
>=
124 #include
"memory_management.h"
126 @ We will need some variables now just to keep track of which kinds of
127 memory managed areas are needed.
129 @
<Interpreted C codes@
>= @
{
130 char
**memory_managed_types
;
131 int num_memory_managed_types
;
132 memory_managed_types
=malloc
(128*sizeof
(char
*));
133 num_memory_managed_types
=0;
136 @ From this code
, the structure will be created in the header file for
137 each type that we need a |memory_of|. This section
, however
, is just a
138 ``wrapper'' code for the template.
140 @f @
!memory_of _decl_head_
// category
9
142 @
<Interpreted C codes@
>= @
{
146 sendc
(0200|'
{'
); // begin interpret mode
147 send
("send_memory_of(\"");
150 sendc
(0200|'
}'
); // end interpret mode
157 @ Here is what it does in order to keep a list of the memory managed
158 types. Note the type name was enclosed in quotation marks
, so now it will
159 be received as a string.
161 @
<Interpreted C codes@
>= @
{
162 void send_memory_of
(char
*s
) {
165 @
<Send the proper name of the memory managed type@
>;
166 for
(i
=0;i
<num_memory_managed_types
;i
++) {
167 if
(!strcmp
(s
,memory_managed_types
[i
])) return
;
169 memory_managed_types
[num_memory_managed_types
++]=s
;
173 @ @
<Send the proper name of the memory managed type@
>= {
179 @ Now the code you get to in order to define the structures in the header
180 file. We are mostly just copying the form of our |managed_memory|
181 structure
, but it will be customized to work with the specific type of the
184 @
<Interpreted C codes@
>= @
{
185 void send_memory_managed_types
() {
187 for
(i
=0;i
<num_memory_managed_types
;i
++) {
188 send
("typedef struct {");
189 send
(memory_managed_types
[i
]);
190 send
("*data; int used; int allocated; } x__");
191 send
(memory_managed_types
[i
]);
197 @ @
(memory_management.h@
>= @
{
198 send_memory_managed_types
();
201 @ These next two subroutines are used to allocate additional memory.
203 @d init_memory
(_a
,_size
) init_memory_
(&(_a),sizeof(*((_a).data)),(_size))
204 @d new_record
(_area
) new_record_
(&(_area),sizeof(*((_area).data)))
206 @
-p void
*init_memory_
(void
*mem
,int record_size
,int num_records
) {
207 managed_memory
*m
=mem
;
208 m-
>data
=malloc
(record_size
*num_records
);
210 m-
>allocated
=num_records
;
211 if
(!m-
>data
) @
<Fatal error due to lack of memory@
>;
215 @ @
-p data_index new_record_
(void
*mem
,int record_size
) {
216 managed_memory
*m
=mem
;
218 if
(m-
>used
>m-
>allocated
) {
220 m-
>data
=realloc
(m-
>data
,m-
>allocated
*record_size
);
222 if
(!m-
>data
) @
<Fatal error due to lack of memory@
>;
223 @
<Zero the new record@
>;
227 @ @
<Fatal error due to lack of memory@
>= {
228 fprintf
(stderr
,"Out of memory\n");
233 @ @
<Zero the new record@
>= {
234 memset
(m-
>data
+(record_size
*(m-
>used-1
)),0,record_size
);
237 @ Now just one more thing. It is useful to have a |foreach| macro to
240 @d foreach
(_var
,_area
) for
(_var
=0;_var
<_area.used
;_var
++)@
;
243 @
*Symbolic Names. There will be some names defined for the use of naming
244 subroutines
, symbolic constants
, patterns
, card areas
, etc. These names
245 are stored in a |managed_memory| called |names|.
247 It also stores references to other things
(defined in later chapters
). The
248 numeric value of a name in |names.data
[x
]| is |x
+256|.
253 @
<More elements of |name_data|@
>@
;
256 @ @
<Global variables@
>=
257 memory_of
(name_data
) names
;
259 @ @
<Initialize memory@
>= init_memory
(names
,16);
261 @ This subroutine finds a name
, adding it if necessary. The number
262 corresponding to it
(as described above
) will be the return value.
264 @
-p int find_name
(char
*name
) {
265 @
<Search for the |name| in |names|@
>;
266 @
<Add the new name
(it was not found
)@
>;
269 @ @
<Search for the |name| in |names|@
>= {
272 if
(!strcmp
(names.data
[i
].name
,name
)) return i
+256;
276 @ @
<Add the new name
(it was not found
)@
>= {
277 int n
=new_record
(names
);
278 names.data
[n
].name
=strdup
(name
);
282 @ A macro will be useful to access the data from a number.
284 @d name_info
(_num
) names.data
[(_num
)-0x0100]
286 @ This code lists the names. It is used for a diagnostic purpose.
288 @
<Display the list of names@
>= {
291 printf
("%d \"%s\
" ",n
+256,names.data
[n
].name
);
292 @
<Display other fields of |names.data
[n
]|@
>;
297 @
*Storage of Tokens. Tokens are stored as
16-bit numbers. Values |
0x0020|
298 to |
0x00FF| represent those ASCII characters
, and |
0x0000| to |
0x001F| are
299 ASCII control codes. Higher numbers represent an index into the |names|
300 array
(where |
0x0101| represents |names.data
[0x0001]|
).
303 @q
[data type of tokens
]@
>
304 typedef unsigned short token
;
306 @ This section lists the ASCII control codes which can be used. Some of
307 them have slightly different meaning from the ASCII standard.
309 @d null_char
0x00 // end of a |raw_data| string or similar things
310 @d pre_null_char
0x01 // becomes |null_char|
311 @d end_transmission
0x04 // marks the end of the last card in this area
312 @d tabulation
0x09 // represents a tab in a
{\TeX
} alignment
313 @d raw_data
0x10 // enter raw
{\TeX
} mode
314 @d whatsit
0x1A // a token for converting into a name token
315 @d escape_code
0x1B // represents a
{\TeX
} control sequence introducer
316 @d record_separator
0x1E // marks the end of a card
317 @d field_separator
0x1F // marks the end of a field of a card
318 @d start_name_code
0x0100
320 @ These tokens are used in card areas
, which are defined
(and described
)
323 @
*Cards. The data of the cards is stored in card areas. Each card area
324 is a list of tokens
, terminated by |record_separator|. The final card in
325 the area is terminated by |end_transmission|.
334 @ @
<More elements of |name_data|@
>=
335 boolean has_card_area
;
336 data_index card_area
;
338 @ @
<Global variables@
>=
339 memory_of
(card_area_data
) card_areas
;
341 @ @
<Initialize memory@
>= init_memory
(card_areas
,1);
343 @ A new card area is created with this.
345 @
-p data_index set_card_area
(int num
) {
346 name_data
*m
=&name_info(num);
347 @
<Use the card area which is already set
, if able@
>;
348 @
<Otherwise
, create a new card area and use the new one@
>;
351 @ @
<Use the card area which is already set
, if able@
>= {
352 if
(m-
>has_card_area
) return m-
>card_area
;
355 @ @
<Otherwise
, create a new card area and use the new one@
>= {
356 data_index n
=new_record
(card_areas
);
358 card_areas.data
[n
].allocated
=0x100;
359 card_areas.data
[n
].tokens
=malloc
(0x100*sizeof
(token
));
360 card_areas.data
[n
].used
=0;
364 @ This subroutine sends a token to a card area.
366 @
-p void send_token
(data_index a
,token x
) {
367 if
(card_areas.data
[a
].allocated
<card_areas.data
[a
].used
+4)
368 @
<Double the allocation of card area tokens@
>;
369 card_areas.data
[a
].tokens
[card_areas.data
[a
].used
++]=x
;
372 @ @
<Double the allocation of card area tokens@
>= {
373 int n
=(card_areas.data
[a
].allocated
*=2)*sizeof
(token
);
374 card_areas.data
[a
].tokens
=realloc
(card_areas.data
[a
].tokens
,n
);
377 @ @
<Display other fields of |names.data
[n
]|@
>= {
378 if
(names.data
[n
].has_card_area
)
379 printf
("C(%d) ",names.data
[n
].card_area
);
382 @ The code in this section is used to ensure that each card area is
383 properly terminated with |end_transmission| marker
, so that when it is
384 time to write the output files
, it will know when to stop.
386 @
<Send |end_transmission| to each card area@
>= {
388 foreach
(a
,card_areas
) send_token
(a
,end_transmission
);
391 @
*Patterns. For pattern matching
, we store the patterns in one memory
392 managed area. The index of the beginning of each pattern area is stored
395 These constants are special codes which can occur in the |text| string
400 @d match_keyword
3 // match a keyword followed by a character in a table
401 @d match_table
4 // match a character using a table
402 @d optional_table
5 // match a character optional using a table
404 @d jump_table
7 // use a table to jump to a marker
405 @d successful_match
8
407 @d forward_one_space
10
408 @d match_left_side
11 // match at beginning of line
409 @d match_right_side
12 // match at end of line
410 @d match_eight_bit
13 // match
8-bit encodings and control characters
415 unsigned int category
; // category for keywords
416 data_index subroutine
;
420 @ @
<More elements of |name_data|@
>=
421 boolean has_pattern_area
;
422 data_index pattern_area
;
424 @ @
<Global variables@
>=
425 memory_of
(pattern_data
) pattern_areas
;
427 @ @
<Initialize memory@
>= init_memory
(pattern_areas
,4);
429 @ @
<Display other fields of |names.data
[n
]|@
>= {
430 if
(names.data
[n
].has_pattern_area
)
431 printf
("P(%d) ",names.data
[n
].pattern_area
);
434 @ A new pattern area is created with this. The patterns in an area are
435 stored like a linked list. The last one with |next| pointing to nothing
,
436 is the terminator entry.
438 @
-p data_index set_pattern_area
(int num
) {
439 name_data
*m
=&name_info(num);
440 @
<Use the pattern area which is already set
, if able@
>;
441 @
<Otherwise
, create a new pattern area and use the new one@
>;
444 @ @
<Use the pattern area which is already set
, if able@
>= {
445 if
(m-
>has_pattern_area
) return m-
>pattern_area
;
448 @ @
<Otherwise
, create a new pattern area and use the new one@
>= {
449 data_index n
=new_record
(pattern_areas
);
450 m-
>has_pattern_area
=1;
451 pattern_areas.data
[n
].subroutine
=none
;
452 pattern_areas.data
[n
].next
=none
;
456 @ @
<Display the list of patterns@
>= {
458 foreach
(i
,pattern_areas
) {
459 if
(pattern_areas.data
[i
].text
) {
460 printf
("%d:%08X:%d:%d\n",i
,pattern_areas.data
[i
].category
461 ,pattern_areas.data
[i
].subroutine
,pattern_areas.data
[i
].next
463 display_string
(pattern_areas.data
[i
].text
);
469 @
*Keywords. Keywords means words which can be placed on the card and which
470 can have special meanings
, and possibly reminder text.
472 Keywords are stored in a large list in only one keyword area. A category
473 can be given a name
, which will automatically be assigned for the next bit
474 of the keyword category when it is entered the first time.
478 char
*match
; // match text
(can contain pattern codes
)
479 unsigned int category
; // bitfield of categories
482 char
*replacement
; // replacement text or reminder text
485 @ @
<Global variables@
>=
486 unsigned int next_keyword_category
=1;
487 memory_of
(keyword_data
) keywords
;
489 @ @
<Initialize memory@
>= init_memory
(keywords
,4);
491 @ A keyword category is found
(and created
, if it is not found
) using the
494 @
-p unsigned int find_category
(char
*name
) {
495 int i
=find_name
(name
);
496 if
(name_info
(i
).value.number
) {
497 return name_info
(i
).value.number
;
498 } @
+else if
(!name_info
(i
).value.is_string
) {
499 name_info
(i
).value.number
=next_keyword_category
;
500 next_keyword_category
<<=1;
501 if
(!next_keyword_category
)
502 fprintf
(stderr
,"Too many keyword categories: %s\n",name
);
503 @.Too many keyword categories@
>
504 return name_info
(i
).value.number
;
508 @ Some stack code commands are used when dealing with reading
/writing
511 In order that you might be able to iterate them
, it will exit out of the
512 current block when trying to read nonexisting keyword info instead of
513 displaying an error message.
515 @
<Cases for system commands@
>=
518 if
(registers
['K'
].number
<0 || registers
['K'
].number
>=keywords.used
)
520 push_num
(keywords.data
[registers
['K'
].number
].extra1
);
521 push_num
(keywords.data
[registers
['K'
].number
].extra2
);
522 push_string
(keywords.data
[registers
['K'
].number
].replacement
);
526 // Write keyword info
527 if
(registers
['K'
].number
<0 || registers
['K'
].number
>=keywords.used
)
528 program_error
("Out of range");
529 free
(keywords.data
[registers
['K'
].number
].replacement
);
530 keywords.data
[registers
['K'
].number
].replacement
=pop_string
();
531 keywords.data
[registers
['K'
].number
].extra2
=pop_num
();
532 keywords.data
[registers
['K'
].number
].extra1
=pop_num
();
536 @ @
<Display the list of keywords@
>= {
538 foreach
(i
,keywords
) {
539 display_string
(keywords.data
[i
].match
);
540 printf
(" [%d:%08X:%d:%d:%d]\n",i
,keywords.data
[i
].category
541 ,keywords.data
[i
].extra1
,keywords.data
[i
].extra2
542 ,strlen
(keywords.data
[i
].replacement
)
547 @
*Card List. A sorted summary list of the cards is kept in one list
,
548 having thirty-two general-purpose numeric fields
, and a pointer to the
549 beginning of the record
(usually the name in which it will be indexed by
).
555 int amount_in_pack
; // used in pack generation
558 @ @
<Global variables@
>=
559 memory_of
(list_entry
) card_list
;
561 @ @
<Initialize memory@
>= init_memory
(card_list
,16);
563 @
*Deck Lists. Deck lists involve lists of cards or rules for cards that
564 belong to a deck or pack.
568 There is one macro |lflag| here just to convert letters to bit flags. For
569 example |lflag
('a'
)| is the least significant bit.
571 @d lflag
(_ch
) (1<<((_ch
)-'a'
))
581 @ @
<Global variables@
>=
582 memory_of
(deck_entry
) deck_lists
;
584 @ @
<More elements of |name_data|@
>=
585 boolean has_deck_list
;
586 data_index deck_list
;
588 @ @
<Initialize memory@
>= init_memory
(deck_lists
,4);
590 @ A new deck list is created with this. The deck entries are stored like a
591 linked list. The terminator has |next| pointing to |none|.
593 @
-p data_index set_deck_list
(int num
) {
594 name_data
*m
=&name_info(num);
595 @
<Use the deck list which is already set
, if able@
>;
596 @
<Otherwise
, create a new deck list and use the new one@
>;
599 @ @
<Use the deck list which is already set
, if able@
>= {
600 if
(m-
>has_deck_list
) return m-
>deck_list
;
603 @ @
<Otherwise
, create a new deck list and use the new one@
>= {
604 data_index n
=new_record
(deck_lists
);
606 deck_lists.data
[n
].next
=none
;
610 @ @
<Display the deck list@
>= {
612 foreach
(i
,deck_lists
) {
614 if
(deck_lists.data
[i
].name
) display_string
(deck_lists.data
[i
].name
);
616 printf
(" [%08X:%d:%d]\n",deck_lists.data
[i
].flags
617 ,deck_lists.data
[i
].amount
,deck_lists.data
[i
].next
);
621 @
*Image Manipulators. Image manipulators are used to render the card
622 images from the typesetting and external PNG files and special effects.
623 Each line of code is one or more
16-bit unsigned numbers
, where the first
624 number indicates what command is being used. These specify effects such as
625 convolution
, blur
, mask
, etc.
629 unsigned char data_len
;
634 @ @
<Global variables@
>=
635 memory_of
(image_manipulator
) image_manips
;
637 @ @
<More elements of |name_data|@
>=
638 boolean has_image_manip
;
639 data_index image_manip
;
641 @ @
<Initialize memory@
>= init_memory
(image_manips
,1);
643 @ Creating a new image manipulation is similar to a new deck list and so
644 on. The program is a linked list of the program lines
, with the terminator
645 |next| pointing to |none|.
647 @
-p data_index set_image_manip
(int num
) {
648 name_data
*m
=&name_info(num);
649 @
<Use the image manipulator which is already set
, if able@
>;
650 @
<Otherwise
, create a new image manipulator and use the new one@
>;
653 @ @
<Use the image manipulator which is already set
, if able@
>= {
654 if
(m-
>has_image_manip
) return m-
>deck_list
;
657 @ @
<Otherwise
, create a new image manipulator and use the new one@
>= {
658 data_index n
=new_record
(image_manips
);
659 m-
>has_image_manip
=1;
660 image_manips.data
[n
].next
=none
;
664 @
*Word Forms. These structures are used to store word form rules
, such as
665 plurals\biblio
{Conway
, Damian. ``An Algorithmic Approach to English
666 Pluralization''. \hskip
0pt plus
1in\hbox
{}
667 \.
{http
://www.csse.monash.edu.au
/\~damian
/papers
/HTML
/Plurals.html
}}. You
668 can store up to four different kinds
, in case of languages other than
678 unsigned char orig
[32];
679 unsigned char dest
[32];
680 boolean left_boundary
;
681 boolean right_boundary
;
684 @ @
<Global variables@
>=
685 memory_of
(word_form_entry
) word_forms
;
687 @ @
<Initialize memory@
>= {
689 init_memory
(word_forms
,16);
692 word_forms.data
[i
].orig
[0]=word_forms.data
[i
].dest
[0]=0;
693 word_forms.data
[i
].next
=i
+1;
694 word_forms.data
[i
].level
=0x7FFFFFFF;
695 word_forms.data
[i
+1].orig
[0]=word_forms.data
[i
+1].dest
[0]=0;
696 word_forms.data
[i
+1].next
=none
;
697 word_forms.data
[i
+1].level
=0;
701 @ Word form rules are added and then inserted in the correct place in the
702 linked list using the |next| field. Entries with a higher numbered level
703 take higher priority
, therefore will be placed before the ones with lower
704 numbered level. Next
, longer |orig| strings come before shorter strings
,
705 since they might be more specific forms of the others and will therefore
708 @
-p data_index add_word_form
(int kind
,int level
,char
*orig
,char
*dest
) {
709 data_index n
=new_record
(word_forms
);
710 @
<Set the fields of the new word form rule@
>;
711 @
<Insert the new word form rule into the linked list@
>;
715 @ The |left_boundary| and |right_boundary| fields specify if they should
716 match only at the boundary. Characters are checked using the \.W table and
717 removed from the string to place in the list.
719 @d last_character
(_str
) ((_str
)[strlen
(_str
)-1])
721 @
<Set the fields of the new word form rule@
>= {
722 word_forms.data
[n
].level
=level
;
723 strcpy
(word_forms.data
[n
].orig
,orig
+(tables
['W'
][*orig
]==2));
724 word_forms.data
[n
].left_boundary
=(tables
['W'
][*orig
]==2);
725 if
((word_forms.data
[n
].right_boundary
=
726 (tables
['W'
][last_character
(word_forms.data
[n
].orig
)]==3)))
727 last_character
(word_forms.data
[n
].orig
)=0;
728 strcpy
(word_forms.data
[n
].dest
,dest
+(tables
['W'
][*dest
]==2));
729 if
(tables
['W'
][last_character
(word_forms.data
[n
].dest
)]==3)
730 last_character
(word_forms.data
[n
].dest
)=0;
733 @ @
<Insert the new word form rule into the linked list@
>= {
734 data_index y
=(kind
&3)<<1; // previous item to |x|
735 data_index x
=word_forms.data
[y
].next
; // current item
737 for
(;x
!=none
;y
=x
,x
=word_forms.data
[y
].next
) {
738 if
(word_forms.data
[x
].next
==none
) break
;
739 @#if
(word_forms.data
[x
].level
<level
) break
;
740 if
(word_forms.data
[x
].level
>level
) continue
;
741 @#if
(strlen
(word_forms.data
[x
].orig
)<s
) break
;
743 word_forms.data
[y
].next
=n
;
744 word_forms.data
[n
].next
=x
;
747 @ Now to do computation of changing a word by word forms. This function
748 expects only one word from input
, or multiple words where the last one
749 should be the word to be converted. Uppercase letters are converted to
750 lowercase for conversion
(but not the other way around
), but if the
751 letters are uppercase in the input
, the output will also have uppercase
752 letters on those positions. The algorithm starts from the right side of
755 The parameter |src| is the input
, and |dest| should point to a buffer
756 which is large enough to store the output string.
760 @
-p data_index reform_word
(int kind
,char
*src
,char
*dest
) {
761 char
*l
=src
+strlen
(src
);
762 data_index n
=word_forms.data
[(kind
&3)<<1].next;
763 strcpy
(dest
,src
); // this is used later
764 @
<Try each word form rule
, following the |next| pointers@
>;
765 return none
; // in case there is nothing to do
768 @ @
<Try each word form rule
, following the |next| pointers@
>= {
771 while
(n
!=none
&& word_forms.data[n].next!=none) {
772 s
=strlen
(word_forms.data
[n
].orig
); @
+ p
=l-s
;
773 @
<Check the characters matching from |p|
, going backwards@
>;
774 n
=word_forms.data
[n
].next
;
778 @ Look ahead for the definition of |wcasecmp|
(true means it matches
).
780 @
<Check the characters matching from |p|
, going backwards@
>= {
782 if
((!word_forms.data
[n
].left_boundary || p
==src
783 || tables
['W'
][p
[-1]])
784 && wcasecmp(word_forms.data[n].orig,p))
785 @
<A match to the word form rules has been found@
>;
786 @
<Go backwards
, stop if we are not allowed to continue backwards@
>;
790 @ @
<A match to the word form rules has been found@
>= {
792 sprintf
(o
,"%s%s",word_forms.data
[n
].dest
,p
+s
);
793 @
<Change the capitalization to match the original@
>;
797 @ Remember
, that for example if ``cow'' becomes ``kine''
, then ``Cow''
798 will become ``Kine''. So
, it will retain capitalization.
802 @
<Change the capitalization to match the original@
>= {
803 char
*q
=word_forms.data
[n
].orig
;
804 for
(;*p
&& *q;p++,o++,q++)
805 if
(*p
==tables
['U'
][*q
] && *p!=tables['L'][*q]) *o=tables['U'][*o];
808 @ @
<Go backwards
, stop if we are not allowed to continue backwards@
>= {
809 if
(word_forms.data
[n
].right_boundary
) break
; // matches only on boundary
810 if
(tables
['W'
][p
[s
]]) break
; // only the last word
(s
) can be matched
811 if
(p--
==src
) break
; // stop at beginning
814 @ This function is defined to compare strings in the way needed for
815 matching word forms
, including case conversion. The lowercase letters in
816 the |shorter| string are permitted to match lowercase and uppercase
817 letters in the |longer| string
, and the |shorter| string is permitted to
818 be shorter and still match.
820 @
-p boolean wcasecmp
(char
*shorter
,char
*longer
) {
821 for
(;;shorter
++,longer
++) {
822 if
(!*shorter
) return
1;
823 if
(!*longer
) return
0;
824 if
(*shorter
!=*longer
&& *shorter!=tables['L'][*longer]) return 0;
828 @ Of course it is now needed a command that can access these features from
829 within a \TeX nicard template. The |level| of the matched rule is also
830 returned
, in case your program might use that information for something.
832 @
<Cases for system commands@
>=
834 // Convert a word form
838 data_index n
=reform_word
(k
,o
,q
);
840 if
(n
==none
) push_num
(0);
841 else push_num
(word_forms.data
[n
].level
);
846 @ @
<Display the list of word form rules@
>= {
848 foreach
(i
,word_forms
) {
849 printf
("%d %c\"",i,word_forms.data[i].left_boundary?'[':' ');
850 display_string(word_forms.data[i].orig);
851 printf("\
"%c -> \"",word_forms.data[i].right_boundary?']':' ');
852 display_string(word_forms.data[i].dest);
853 printf("\
" %d >%d\n",word_forms.data
[i
].level
854 ,word_forms.data
[i
].next
);
858 @
*Random Number Generation. This program uses the Xorshift algorithm
,
859 invented by George Marsaglia\biblio
{Marsaglia
(July
2003). ``Xorshift
860 RNGs''. Journal of Statistical Software Vol.~
8 (Issue
14).
{\tt
861 http
://www.jstatsoft.org
/v08
/i14
/paper
}.
}.
863 @^Marsaglia
, George@
>
866 @
<Global variables@
>=
872 @ @
<Initialize the random number generator@
>= {
873 @q
[initialize the random seed
::]@
>
874 rng_seed
((unsigned int
)time
(0));
875 @q
[::initialize the random seed
]@
>
878 @ The seed parameters for the random number generator will be seeded using
879 the linear congruential generator
, which is a simpler generator which can
880 be used to seed it with.
882 The parameters |lcg_a| and |lcg_c| are parameters to the linear
883 congruential generator algorithm. The values used here are the same as
884 those used in GNU C. In this program they will be specified explicitly so
885 that you can get identical output on different computers.
890 @
-p void rng_seed
(unsigned int x
) {
891 rng_x
=x
=lcg_a
*x
+lcg_c
;
892 rng_y
=x
=lcg_a
*x
+lcg_c
;
893 rng_z
=x
=lcg_a
*x
+lcg_c
;
894 rng_w
=x
=lcg_a
*x
+lcg_c
;
897 @ There is a command to reseed it using a constant
(so that you can
898 generate the same numbers on different computers
).
900 @
<Cases for system commands@
>=
902 // Reseed the random number generator
903 if
(stack_ptr-
>is_string
) program_error
("Type mismatch");
908 @ And now follows the algorithm for generating random numbers. One change
909 has been made so that once it is modulo
, all number will still be of equal
912 Numbers are generated in the range from
0 up to but not including |limit|.
914 @d max_uint
((unsigned int
)(-1))
916 @
-p unsigned int gen_random
(unsigned int limit
) {
917 unsigned int r
=max_uint-
(max_uint
%limit
); // range check
919 @
<Make the next number |rng_w|...@
>;
920 @
<Check the range
, try again if out of range
, else |return|@
>;
924 @ @
<Make the next number |rng_w| by Xorshift algorithm@
>= {
925 unsigned int t
= rng_x ^
(rng_x
<< 11);
926 rng_x
= rng_y
; @
+ rng_y
= rng_z
; @
+ rng_z
= rng_w
;
927 rng_w ^
= (rng_w
>> 19) ^ t ^
(t
>> 8);
930 @ @
<Check the range
, try again if out of range
, else |return|@
>= {
931 if
(rng_w
<=r
) return rng_w
%limit
;
934 @ @
<Cases for system commands@
>=
936 // Generate a random number
937 if
(stack_ptr-
>is_string
) program_error
("Type mismatch");
938 stack_ptr-
>number
=gen_random
(stack_ptr-
>number
);
942 @
*Stack Programming Language. Now we get to the part where the user can
943 enter a program
, in order to control the features of this program. The
944 programming language used is like \.
{dc
}, but different.
948 Subroutines are simply stored as strings in the |names| area
, since they
949 are the same as registers.
951 @ Now we have the storage of registers. Registers
0 to
255 are stored in
952 this separate list
, while other register values are just stored in the
953 |names| list. There is also a stack
, which has storage of the same values
954 as registers can contain.
967 @ @
<More elements of |name_data|@
>=
968 register_value value
;
970 @ @
<Global variables@
>=
971 register_value registers
[256];
972 register_value stack
[max_stack
];
973 register_value
*stack_ptr
=stack-1
; // current top of stack element
975 @ Here are some codes for pushing and popping the stack.
977 @d pop_num
() ((stack_ptr--
)->number
)
979 @
-p inline void push_string
(char
*s
) {
981 stack_ptr-
>is_string
=1;
982 stack_ptr-
>text
=strdup
(s
);
985 @ @
-p inline void push_num
(int n
) {
987 stack_ptr-
>is_string
=0;
991 @ @
-p inline void stack_dup
(void
) {
992 if
((stack_ptr
[1].is_string
=stack_ptr-
>is_string
)) {
993 stack_ptr
[1].text
=strdup
(stack_ptr-
>text
);
995 stack_ptr
[1].number
=stack_ptr-
>number
;
1000 @ @
-p inline void stack_drop
(void
) {
1001 if
(stack_ptr-
>is_string
) free
(stack_ptr-
>text
);
1005 @ @
-p inline char
*pop_string
(void
) {
1006 char
*p
=stack_ptr-
>text
;
1007 stack_ptr-
>is_string
=0; stack_ptr-
>text
=0;
1012 @ Also
, some subroutines are needed here in order to deal with registers.
1014 For |fetch_code|
, the string |
"0[]+"| is returned if it is not a string
,
1015 generating a ``Type mismatch'' error when you try to run it.
1017 @
-p inline char
*fetch_code
(int r
) {
1019 if
(!registers
[r
].is_string
) return
"0[]+";
1020 return registers
[r
].text
;
1022 if
(!name_info
(r
).value.is_string
) return
"0[]+";
1023 return name_info
(r
).value.text
;
1027 @ @
-p inline void fetch
(int r
) {
1029 if
(!(r
&~0xFF)) v=&(registers[r]);
1030 else v
=&(name_info(r).value);
1031 (++stack_ptr
)->is_string
=v-
>is_string
;
1033 stack_ptr-
>text
=strdup
(v-
>text
);
1035 stack_ptr-
>number
=v-
>number
;
1039 @ @
-p inline void store
(int r
) {
1041 if
(!(r
&~0xFF)) v=&(registers[r]);
1042 else v
=&(name_info(r).value);
1043 if
(v-
>is_string
) free
(v-
>text
);
1044 v-
>is_string
=stack_ptr-
>is_string
;
1046 v-
>text
=stack_ptr-
>text
;
1048 v-
>number
=stack_ptr-
>number
;
1053 @ There is also a save stack. This save stack stores the saved values of
1054 the registers |'
0'| to |'
9'|
, so that you can have local variables in a
1057 @
<Global variables@
>=
1058 register_value save_stack
[520];
1059 register_value
*save_stack_ptr
=save_stack
;
1061 @ These codes deal with the save stack. Strings will be copied when
1062 saving. When loading
, strings that were previously in the registers will
1065 @
<Save local registers to the save stack@
>= {
1067 for
(i
='
0'
;i
<='
9'
;i
++) {
1068 *save_stack_ptr
=registers
[i
];
1069 if
(registers
[i
].is_string
)
1070 save_stack_ptr-
>text
=strdup
(save_stack_ptr-
>text
);
1075 @ @
<Load local registers from the save stack@
>= {
1077 for
(i
='
9'
;i
>='
0'
;i--
) {
1078 if
(registers
[i
].is_string
) free
(registers
[i
].text
);
1079 registers
[i
]=*--save_stack_ptr
;
1083 @
*Commands for Stack Programming Language. Finally
, is the code where it
1084 can be executed. The return value of this function indicates how many
1085 levels should be exit when it is called.
1087 @
-p int execute_program
(unsigned char
*prog
) {
1088 unsigned char
*ptr
=prog
;
1089 reset_execute_program
:
1092 @
<Cases for literal data commands@
>@
;
1093 @
<Cases for stack manipulation commands@
>@
;
1094 @
<Cases for arithmetic commands@
>@
;
1095 @
<Cases for flow-control commands@
>@
;
1096 @
<Cases for register
/table operation commands@
>@
;
1097 @
<Cases for string commands@
>@
;
1098 @
<Cases for condition
/compare commands@
>@
;
1099 @
<Cases for local registers commands@
>@
;
1100 @
<Cases for system commands@
>@
;
1101 @
-case '?'
: @
<Do a diagnostics command@
>@
;@
+break
;
1102 @
-case '
='
: @
<Do a typesetting command@
>@
;@
+break
;
1104 if
(*ptr
>='
0'
&& *ptr<='9') {
1105 @
<Read a literal number and push to stack@
>;
1106 } @
+else if
(0x80&*ptr) {
1107 @
<Execute a subroutine code from the current character@
>;
1111 if
(stack_ptr
<stack-1
) program_error
("Stack underflow");
1112 if
(stack_ptr
>stack
+max_stack
) program_error
("Stack overflow");
1117 @ @
<Cases for literal data commands@
>=
1119 // Literal ASCII character
1125 @
<Read a literal string and push to stack@
>;
1130 @
<Read a literal name and push its number to the stack@
>;
1134 @ @
<Read a literal number and push to stack@
>= {
1136 while
(*ptr
>='
0'
&& *ptr<='9') n=10*n+(*ptr++)-'0';
1141 @ @
<Read a literal string and push to stack@
>= {
1149 if
(!*ptr
) program_error
("Unterminated string literal");
1155 @ @
<Read a literal name and push its number to the stack@
>= {
1157 while
(*ptr
&& *ptr!=')') ptr++;
1158 if
(!*ptr
) program_error
("Unterminated string literal");
1160 push_num
(find_name
(p
));
1164 @ @
<Cases for stack manipulation commands@
>=
1166 // Drop top item of stack
1171 // Clears the stack
, rendering it empty
1172 while
(stack_ptr
>=stack
) stack_drop
();
1176 // Duplicates the value on top of the stack.
1181 // Swaps the top two values on the stack
1182 stack_ptr
[1]=stack_ptr
[0];
1183 stack_ptr
[0]=stack_ptr
[-1];
1184 stack_ptr
[-1]=stack_ptr
[1];
1188 @ @
<Cases for arithmetic commands@
>=
1190 // Add two numbers
, or concatenate two strings
1191 if
(stack_ptr-
>is_string
) {
1192 @
<Concatenate strings on the stack@
>;
1195 if
(stack_ptr-
>is_string
)
1196 program_error
("Type mismatch");
1197 stack_ptr-
>number
+=n
;
1202 // Subtract two numbers
, or compare two strings
1203 if
(stack_ptr-
>is_string
) {
1204 @
<Compare strings on the stack@
>;
1207 if
(stack_ptr-
>is_string
)
1208 program_error
("Type mismatch");
1209 stack_ptr-
>number-
=n
;
1214 // Multiply two numbers
1216 if
(stack_ptr
[0].is_string || stack_ptr
[1].is_string
)
1217 program_error
("Number expected");
1218 stack_ptr-
>number
*=n
;
1222 // Divide two numbers
1224 if
(stack_ptr
[0].is_string || stack_ptr
[1].is_string
)
1225 program_error
("Number expected");
1226 if
(n
==0) program_error
("Division by zero");
1227 stack_ptr-
>number
/=n
;
1231 // Modulo of two numbers
1233 if
(stack_ptr
[0].is_string || stack_ptr
[1].is_string
)
1234 program_error
("Number expected");
1235 if
(n
==0) program_error
("Division by zero");
1236 stack_ptr-
>number
%=n
;
1240 @ @
<Concatenate strings on the stack@
>= {
1241 char
*s
=pop_string
();
1243 if
(!stack_ptr-
>is_string
) program_error
("Type mismatch");
1244 q
=malloc
(strlen
(s
)+strlen
(stack_ptr-
>text
)+1);
1245 strcpy
(q
,stack_ptr-
>text
);
1246 strcpy
(q
+strlen
(q
),s
);
1253 @ @
<Compare strings on the stack@
>= {
1254 char
*s
=pop_string
();
1255 char
*q
=pop_string
();
1256 push_num
(strcmp
(q
,s
));
1261 @ @
<Cases for flow-control commands@
>=
1263 // Exit from multiple levels
1269 // Go back to beginning
1274 // Exit from two levels
1279 // Execute code from top of stack
1280 @
<Execute a string or subroutine code from top of stack@
>;
1284 @ Note here
, it is a recursive function call.
1287 @
<Execute a string or subroutine code from top of stack@
>= {
1288 if
(stack_ptr-
>is_string
) {
1289 char
*p
=pop_string
();
1290 int q
=execute_program
(p
);
1294 char
*p
=fetch_code
(pop_num
());
1295 int q
=execute_program
(p
);
1300 @ Since the extended characters
(|
0x80| to |
0xFF|
) do not correspond to
1301 any commands
, here we can use them to execute a subroutine code
, allowing
1302 many things related to self-modifying code
(and other stuff
) to be done
1303 that would be difficult otherwise.
1305 @
<Execute a subroutine code from the current character@
>= {
1306 char
*p
=fetch_code
(*ptr
);
1307 int q
=execute_program
(p
);
1311 @ @
<Cases for register
/table operation commands@
>=
1313 // Store value to table
1315 if
(stack_ptr-
>is_string
) program_error
("Number expected");
1317 tables
[0x7F&*++ptr][n]=pop_num();
1321 // Load value from table
1322 stack_ptr-
>number
=tables
[0x7F&*++ptr][stack_ptr->number];
1326 // Load value from register named by stack
1327 if
(stack_ptr-
>is_string
) program_error
("Number expected");
1332 // Store value in register named by stack
1333 if
(stack_ptr-
>is_string
) program_error
("Number expected");
1338 // Load value from register
1343 // Store value in register
1348 @ @
<Cases for string commands@
>=
1350 // Put brackets around a string
, or convert number to text
1351 if
(stack_ptr-
>is_string
) {
1352 @
<Put brackets around string at top of stack@
>;
1354 @
<Convert top of stack to string representation of a number@
>;
1359 // Calculate number of characters in a string
1360 char
*s
=pop_string
();
1361 push_num
(strlen
(s
));
1366 // ``ASCIIfy'' a number
1367 if
(stack_ptr-
>is_string
) {
1368 if
(stack_ptr-
>text
[0]) stack_ptr-
>text
[1]=0;
1370 int n
=stack_ptr-
>number
;
1371 stack_ptr-
>is_string
=1;
1372 stack_ptr-
>text
=malloc
(2);
1373 stack_ptr-
>text
[0]=n
;
1374 stack_ptr-
>text
[1]=0;
1379 // Take the first character from the string
1380 char
*s
=stack_ptr-
>text
;
1381 if
(!stack_ptr-
>is_string ||
!*s
) return
0;
1383 stack_ptr
[-1].text
=strdup
(s
+1);
1388 // Convert a register number to its name
1389 int n
=stack_ptr-
>number
;
1390 if
(stack_ptr-
>is_string
) program_error
("Type mismatch");
1391 if
(n
<256 || n
>=names.used
+256) program_error
("Out of range");
1393 push_string
(names.data
[n-256
].name
);
1397 @ @
<Put brackets around string at top of stack@
>= {
1398 char
*buf
=malloc
(strlen
(stack_ptr-
>text
)+3);
1399 sprintf
(buf
,"[%s]",stack_ptr-
>text
);
1400 free
(stack_ptr-
>text
);
1401 stack_ptr-
>text
=buf
;
1404 @ @
<Convert top of stack to string representation of a number@
>= {
1406 sprintf
(buf
,"%d",stack_ptr-
>number
);
1411 @ Here is how the ``Arithmetic
IF'' command works
: On the stack you have
1412 any three values at the top
, and a number underneath it. Those are all
1413 removed
, except one of the three values which is selected based on the
1414 sign of the number
(the condition value
).
1416 @
<Cases for condition
/compare commands@
>=
1419 @
<Do the ``Arithmetic
IF''@
>;
1425 if
(stack_ptr
[0].is_string || stack_ptr
[1].is_string
)
1426 program_error
("Number expected");
1427 stack_ptr-
>number
&=n;
1431 @ Do you like this algorithm? Is this a real question?
1435 @
<Do the ``Arithmetic
IF''@
>= {
1436 register_value v
=stack_ptr
[-3];
1439 stack_ptr
[-3]=stack_ptr
[n
];
1441 stack_drop
();@
+stack_drop
();@
+stack_drop
();
1444 @ @
<Cases for local registers commands@
>=
1447 @
<Save local registers to the save stack@
>;
1452 @
<Load local registers from the save stack@
>;
1456 @ When there is a program error
(such as stack underflow
), the following
1457 subroutine is used to handle it.
1459 @d program_error
(_text
) program_error_
(prog
,ptr
,_text
)
1461 @
-p void program_error_
(char
*prog
,char
*ptr
,char
*msg
) {
1462 fprintf
(stderr
,"Error in %s on line %d",current_filename
,current_line
);
1463 fprintf
(stderr
,"\n! %s\ns%dS%dp%d near \"",msg,stack_ptr-stack,
1464 save_stack_ptr-save_stack,ptr-prog);
1465 @<Display the codes near the part that caused the error@>;
1466 fprintf(stderr,"\
"\n");
1470 @ @
<Display the codes near the part that caused the error@
>= {
1474 if
(p
<prog || p
>ptr
) p
=prog
;
1475 for
(i
=0;p
+i
<=ptr
&& p[i];i++) buf[i]=p[i];
1477 fprintf
(stderr
,"%s",buf
);
1480 @
*Tables and Registers. The tables must be stored here. There are
128
1481 tables with
256 entries each
, each of which can store one byte of data.
1482 These tables are used for converting uppercase
/lowercase
, for deciding
1483 which characters need to be escaped in \TeX
, and so on.
1485 The purposes of the built-in registers are also described in this chapter.
1486 The tables and registers named by uppercase letters are for system use.
1487 The tables and registers named by lowercase can be used by the user.
1489 @
<Global variables@
>=
1490 unsigned char tables
[128][256];
1492 @ Here are the uses of the built-in tables and registers
:
1493 @^built-in registers@
>
1496 Register \.A
: The current position in the current cards area.
1498 Register \.C
: The current cards area.
1500 Register \.D
: Dots per inch
, multiplied by
100.
1502 Register \.E
: The escape character for \TeX. If this is a string
, the
1503 entire string is the prefix
; otherwise
, it is a ASCII number of the
1504 character to be used.
1506 Register \.K
: Index number for last keyword entry added. Also used when
1507 dealing with keyword operation commands
, and when a keyword is matched in
1510 Register \.P
: The current pattern area.
1512 Register \.Q
: The parameters for the ImageMagick command-line
, separated
1515 Register \.T
: Alignment tab character for \TeX. Same considerations apply
1516 as the \.E register.
1518 Register \.U
: A code to execute for a deck specification enrty with \.x
1521 Register \.V
: The version number of this program.
1523 Register \.W
: A code which pushes the whatsit replacements onto the stack.
1524 It is initialized to a blank string before each line in a card area. It
1525 should push the replacements in the reverse order of the whatsits
, so you
1526 could use a code like this
, for example
: \.
{[(Abracadabra
)]lW
+sW
}
1528 Register \.
X: Horizontal coordinate across the page
(in pixels
).
1530 Register \.
Y: Vertical coordinate across the page
(in pixels
).
1532 Register \.Z
: Should be set to a code to execute after doing everything
1533 else
(but before writing output files
).
1535 Table \.E
: Indicates which characters need escaped for \TeX. Also used for
1536 category codes in internal typesetting mode
(a discussion of the category
1537 codes will be deferred to a later part of this book
).
1539 Table \.F
: Space factor codes for internal typesetting
, where
40 is normal
1540 (multiplying these values by
25 results in the corresponding \.
{\\sfcode
}
1541 values in \TeX
). Zero means no change.
1543 Table \.G
: Table containing information for sorting and grouping.
1545 Table \.J
: Left margin protrusions for internal typesetting. A value of
1546 128 is normal. Each one unit less or greater than
128 represents a
1547 distance of
0.005 em
, where number less than
128 for negative kerns and
1548 greater than
128 for positive kerns.
(Note that you will use
{\sl negative
1549 negative
} kerns to protrude into the margin
, both for the left protrusions
1550 and for the right protrusions
!)
1552 Table \.K
: Right margin protrusions for internal typesetting.
1554 Table \.L
: Conversion to lowercase.
1556 Table \.S
: Information for natural sorting.
1558 Table \.U
: Conversion to uppercase.
1560 Table \.W
: Table for word form rules. Zero means a letter
, one means a
1561 word separator
, two means use to mark beginning of a word
, three means use
1562 to mark the end of a word. In this program
, it is advantageous to use the
1563 fact that zero means word characters
(such as letters
), and nonzero means
1566 @d init_register
(_reg
,_val
) do@
+{
1567 registers
[_reg
].is_string
=0;
1568 registers
[_reg
].number
=(_val
);
1571 @d init_register_str
(_reg
,_val
) do@
+{
1572 registers
[_reg
].is_string
=1;
1573 registers
[_reg
].text
=strdup
(_val
);
1576 @
<Initialize the tables and registers@
>= {
1578 for
(i
=0;i
<256;i
++) init_register
(i
,0);
1579 init_register
('E'
,'\\'
);
1580 init_register
('V'
,version_number
);
1581 @
<Initialize table of alphabetical case conversion@
>;
1582 @
<Initialize tables for internal typesetting@
>;
1585 @ @
<Initialize table of alphabetical case conversion@
>= {
1586 for
(i
=0;i
<256;i
++) tables
['L'
][i
]=tables
['U'
][i
]=i
;
1587 for
(i
='A'
;i
<='Z'
;i
++) {
1588 tables
['L'
][i
]=i
+'a'
-'A'
;
1589 tables
['U'
][i
+'a'
-'A'
]=i
;
1593 @ @
<Display the contents of table |
*++ptr|@
>= {
1596 for
(i
=0;i
<256;i
++) {
1597 printf
("%c%c",tables
[t
][i
]?'
+'
:'.'
,@|
1598 (tables
[t
][i
]<0x7F && tables[t][i]>=' ')?tables[t][i]:'.'
1600 if
((i
&0x0F)==0x0F) printf("\n");
1602 for
(i
=' '
;i
<0x7F;i
++) if
(tables
[t
][i
]) printf
("%c",i
);
1605 @
*Diagnostics. Here is diagnostics commands. These are used to display the
1606 internal information on standard output
, so that you can check how these
1607 things are working.
(You can also use \.
{gdb
} for debugging purposes.
) A
1608 diagnostics command always starts with a question mark
, and is then
1609 followed by one more character indicating the type of diagnostics
1610 requestsed.
(Some are followed by an additional character after that.
)
1612 @
<Do a diagnostics command@
>= {
1614 case 'c'
: @
<Display the sorted card list@
>; @
+break
;
1615 case 'd'
: @
<Display the deck list@
>; @
+break
;
1616 case 'f'
: @
<Display font information@
>; @
+break
;
1617 case 'k'
: @
<Display the list of keywords@
>; @
+break
;
1618 case 'n'
: @
<Display the list of names@
>; @
+break
;
1619 case 'p'
: @
<Display the list of patterns@
>; @
+break
;
1620 case 's'
: @
<Display the contents of the stack@
>; @
+break
;
1621 case 't'
: @
<Display the contents of table |
*++ptr|@
>; @
+break
;
1622 case 'w'
: @
<Display the list of word form rules@
>; @
+break
;
1623 case 'x'
: @
<Display the list of typeset nodes@
>; @
+break
;
1624 case 'y'
: @
<Display typesetting diagnostics@
>; @
+break
;
1625 default
: program_error
("Unknown type of diagnostics");
1629 @ One subroutine is used here for displaying strings with escaped
, so that
1630 it will display on a terminal without messing it up or omitting the
1631 display of some characters.
1633 @
-p void display_string
(char
*s
) {
1635 if
(*s
<' ' ||
*s
==0x7F) {
1636 printf
("^%c",0x40^
*s
);
1643 @ @
<Display the contents of the stack@
>= {
1645 for
(p
=stack
;p
<=stack_ptr
;p
++) {
1648 display_string
(p-
>text
);
1651 printf
("%d\n",p-
>number
);
1656 @ More of the diagnostics functions are included in the chapters for the
1657 data structures which it is displaying.
1659 @
*Pattern Matching. Now
, finally
, after the chapter about patterns
, and
1660 going through many other things in between
, comes to the chapter in which
1661 patterns are actually being matched.
1663 One structure is used here for the information about how to match it
, and
1664 what has been matched from it. The parameter |num_capture| is how many
1665 captured parts there are
, and the |start| and |end| arrays store the index
1666 into the |src| string of where the matches are. The entire matched part is
1667 indicated by |start
[0]| and |end
[0]|
(note always |start
[0]==0|
).
1672 char
*truesrc
; // used for checking true beginning of the line
1674 unsigned int category
;
1680 @ This first one just matches one pattern against a string to see if it
1681 matches. It returns true if it does match.
(It is somewhat inefficient.
)
1683 @
-p boolean match_pattern
(match_info
*mat
) {
1684 char
*src
; // current start of source string
1685 char
*ptr
; // pointer into source string |src|
1686 char
*pptr
; // pointer into pattern string
1687 src
=mat-
>src
; @
+ mat-
>num_capture
=0; @
+ pptr
=mat-
>pattern
; @
+ ptr
=src
;
1688 @
<Execute the pattern on the string |src|@
>;
1692 @ This loop executes each command in the pattern in attempt to match each
1693 character. In case of mismatch
, it will break out of this loop
, and
1694 continue with the next iteration of the loop in the previous section.
1696 @d not_a_marker
!(pptr
[-1]&0x80)
1698 @
<Execute the pattern on the string |src|@
>= {
1702 mat-
>start
[++mat-
>num_capture
]=ptr-mat-
>src
; @
+break
;
1703 case end_capture
: mat-
>end
[mat-
>num_capture
]=ptr-mat-
>src
; @
+break
;
1704 case match_keyword
: @
<Do |match_keyword|@
>; @
+break
;
1706 if
(!tables
[*pptr
++][*ptr
++]) goto mismatch
; @
+break
;
1707 case optional_table
: ptr
+=!!tables
[*pptr
++][*ptr
]; @
+break
;
1708 case failed_match
: goto mismatch
;
1710 if
(!(pptr
=strchr
(mat-
>pattern
,0x80|tables
[*pptr
++][*ptr
++])))
1713 case successful_match
: @
<Do |successful_match|@
>;
1714 case back_one_space
: if
(ptr--
==mat-
>src
) goto mismatch
; @
+break
;
1715 case forward_one_space
: if
(!*ptr
++) goto mismatch
; @
+break
;
1716 case match_left_side
: if
(ptr
!=mat-
>truesrc
) goto mismatch
; @
+break
;
1717 case match_right_side
: if
(*ptr
>=' '
) goto mismatch
; @
+break
;
1718 default
: if
(not_a_marker
&& pptr[-1]!=*ptr++) goto mismatch;
1723 @ @
<Do |successful_match|@
>= {
1725 mat-
>end
[0]=ptr-mat-
>src
;
1729 @ And now
, the next part matches from an area and changes the string in
1730 place
, possibly by reallocating it. The |src| pointer passed to this
1731 function should be one that can be freed
!
1733 @
-p char
*do_patterns
(char
*src
,int area
) {
1736 int index
=0; // index into |src| string
1737 @
<Cancel if there isn't a pattern area@
>;
1739 if
(index
>=strlen
(src
)) return src
;
1740 pat
=pattern_areas.data
+name_info
(area
).pattern_area
;
1742 @
<Fill up the |mat| structure for testing the current pattern@
>;
1743 if
(mat.pattern
&& match_pattern(&mat)) {
1744 @
<Push the captured strings to the stack@
>;
1745 @
<Call the subroutine associated with this pattern@
>;
1746 if
(stack_ptr-
>is_string
) {
1747 @
<Replace the matched part from the stack and fix the |index|@
>;
1752 goto continue_matching
;
1754 @
<Select the next pattern in this area or |break|@
>;
1756 index
++; @
+ goto continue_matching
;
1759 @ @
<Cancel if there isn't a pattern area@
>= {
1760 if
(area
<256) return src
;
1761 if
(!name_info
(area
).has_pattern_area
) return src
;
1764 @ @
<Fill up the |mat| structure for testing the current pattern@
>= {
1767 mat.pattern
=pat-
>text
;
1768 mat.category
=pat-
>category
;
1771 @ @
<Push the captured strings to the stack@
>= {
1773 for
(i
=mat.num_capture
;i
;i--
) {
1774 push_string
(src
+index
+mat.start
[i
]);
1775 stack_ptr-
>text
[mat.end
[i
]-mat.start
[i
]]=0;
1779 @ @
<Call the subroutine associated with this pattern@
>= {
1780 execute_program
(names.data
[pat-
>subroutine
].value.text
);
1783 @ The memory allocated is probably more than is needed
, but this way is
1784 simpler. It is always sufficient amount
, though. Think about it.
1788 @
<Replace the matched part from the stack and fix the |index|@
>= {
1789 char
*q
=malloc
(strlen
(src
)+strlen
(stack_ptr-
>text
)+1);
1791 sprintf
(q
+index
,"%s%s",stack_ptr-
>text
,src
+index
+mat.end
[0]);
1794 index
+=strlen
(stack_ptr-
>text
);
1797 @ @
<Select the next pattern in this area or |break|@
>= {
1798 if
(pat-
>next
==none
) break
;
1799 pat
=pattern_areas.data
+pat-
>next
;
1802 @ Finally
, there is a command |'M'| to do a pattern matching and
1803 replacement with a string
, inside of a stack subroutine code.
1805 @
<Cases for system commands@
>=
1807 // do pattern matching and replacement
1809 if
(!stack_ptr-
>is_string
) program_error
("Type mismatch");
1810 stack_ptr-
>text
=do_patterns
(stack_ptr-
>text
,n
);
1814 @
*Matching Keywords. Codes for matching keywords have been placed in
1815 another chapter
, instead of making the previous chapter longer.
1817 So now we can see how it is matched keywords in a pattern code.
1819 @
<Do |match_keyword|@
>= {
1822 char t
=*pptr
++; // indicate which table to use
1823 data_index best
=none
;
1825 @
<Try matching each keyword belonging to the category@
>;
1826 if
(best
==none
) goto mismatch
;
1827 @
<Adjust the \.K register for this keyword match@
>;
1831 @ @
<Adjust the \.K register for this keyword match@
>= {
1832 if
(registers
['K'
].is_string
) free
(registers
['K'
].text
);
1833 registers
['K'
].is_string
=0;
1834 registers
['K'
].number
=best
;
1837 @ When matching keywords
, all of them will be tried
, in case there are
1838 better candidates for the search
(bigger is better
(so
, for example
,
1839 |
"Power of One"| will override |
"Power"|
); failing that
, later ones are
1840 better than earlier ones
(so that user files can override keywords in
1843 @^Courtenay
, Bryce@
>
1844 @^Houghton
, Israel@
>
1847 @
<Try matching each keyword belonging to the category@
>= {
1849 foreach
(i
,keywords
) {
1850 if
(keywords.data
[i
].category
&mat->category &&
1851 strlen
(keywords.data
[i
].match
)>=best_length
) {
1852 @
<Set up the |match_info| structure called |m|@
>;
1853 @
<Attempt applying this keyword match@
>;
1858 @ @
<Set up the |match_info| structure called |m|@
>= {
1859 sprintf
(mstr
,"%s%c%c%c",
1860 keywords.data
[i
].match
,match_table
,t
,successful_match
);
1861 m.src
=m.truesrc
=ptr
;
1865 @ @
<Attempt applying this keyword match@
>= {
1866 if
(match_pattern
(&m)) {
1868 best_length
=strlen
(keywords.data
[i
].match
);
1872 @
*Sorting and Grouping. The card lists can be sorted
/grouped using these
1873 commands
, which are generally used by macros that create the records for
1874 the cards in the card areas.
1876 @
<Cases for system commands@
>=
1878 // Add a new list entry
1879 data_index n
=new_record
(card_list
);
1880 card_list.data
[n
].token_ptr
=
1881 card_areas.data
[set_card_area
(registers
['C'
].number
)].used
1886 // Set a field value of the list entry
1887 data_index n
=card_list.used-1
;
1890 if
(n
==none
) program_error
("No card list is available");
1891 card_list.data
[n
].field
[x
&31]=y;
1895 @ Other than the commands to make the list entries above
, there must be
,
1896 of course
, the actual sorting and grouping being done
!
1898 Sorting and grouping are controlled by the \.G table. Starting from a
1899 given offset
(added
), you use thirty-two entries for the thirty-two
1902 @
<Cases for system commands@
>=
1905 sorting_table_offset
=pop_num
();
1906 qsort
(card_list.data
,card_list.used
,sizeof
(list_entry
),list_compare
);
1907 @
<Mark positions in the sorted list@
>;
1911 @ @
<Global variables@
>=
1912 int sorting_table_offset
;
1914 @ This is the compare function for the list sorting. It is also worth to
1915 notice here what values belong in the \.G table.
(There are also some
1916 other values
, which are described a bit later.
)
1919 @d primary_ascending 'A'
1920 @d primary_descending 'Z'
1922 @d secondary_ascending 'a'
1923 @d secondary_descending 'z'
1924 @d secondary_name 'n'
1925 @d record_sorted_position 'R'
1926 @d reset_high_bits 'q'
1928 @d G_table
(_field
) (tables
['G'
][((sorting_table_offset
+(_field
))&0xFF)])
1929 @d p1s
((list_entry
*)p1
)
1930 @d p2s
((list_entry
*)p2
)
1932 @
-p int list_compare
(const void
*p1
,const void
*p2
) {
1933 @
<Compare using fields indicated by \.G table@
>;
1934 @
<Compare using the card's name and the \.S table@
>;
1935 @
<Compare using the order in which the cards are typed in@
>;
1936 return
0; // This can't
, but will
, happen.
1939 @ @
<Compare using fields indicated by \.G table@
>= {
1941 for
(i
=0;i
<32;i
++) if
(p1s-
>field
[i
]!=p2s-
>field
[i
]) {
1942 if
(G_table
(i
)==primary_ascending ||
(G_table
(i
)&0x80)) {
1943 return
(p1s-
>field
[i
]>p2s-
>field
[i
])?
1:-1;
1944 } @
+else if
(G_table
(i
)==primary_descending
) {
1945 return
(p1s-
>field
[i
]<p2s-
>field
[i
])?
1:-1;
1946 } @
+else if
(G_table
(i
)==primary_name
) {
1947 return name_compare
(p1s-
>field
[i
],p2s-
>field
[i
]);
1950 for
(i
=0;i
<32;i
++) if
(p1s-
>field
[i
]!=p2s-
>field
[i
]) {
1951 if
(G_table
(i
)==secondary_ascending
) {
1952 return
(p1s-
>field
[i
]>p2s-
>field
[i
])?
1:-1;
1953 } @
+else if
(G_table
(i
)==secondary_descending
) {
1954 return
(p1s-
>field
[i
]<p2s-
>field
[i
])?
1:-1;
1955 } @
+else if
(G_table
(i
)==secondary_name
) {
1956 return name_compare
(p1s-
>field
[i
],p2s-
>field
[i
]);
1961 @ When all else fails
, \strike
{play dead
} use the order in which the cards
1962 have been typed in. This is how it is made stable
, and that you can get
1963 the same results on any computer.
1967 @
<Compare using the order in which the cards...@
>= {
1968 if
(p1s-
>token_ptr
>p2s-
>token_ptr
) return
1;
1969 if
(p1s-
>token_ptr
<p2s-
>token_ptr
) return
-1;
1972 @ The last thing to do after sorting
, is mark positions in the list if it
1973 is requested to do so.
1975 In addition
, it shall also optionally mark high bits
(30 to
27) of some
1976 fields
, based on when other fields change. This helps with doing multi-
%
1977 dimensional statistics. The fields that it is based on will automatically
1978 be primary sorted since such sorting is required for the marking algorithm
1981 @
<Mark positions in the sorted list@
>= {
1985 if
(G_table
(j
)==record_sorted_position
) {
1986 foreach
(i
,card_list
) card_list.data
[i
].field
[j
]=i
;
1987 } @
+else if
(G_table
(j
)&0x80) {
1988 @
<Mark high bits of fields to prepare for...@
>;
1989 } @
+else if
(G_table
(j
)==reset_high_bits
) {
1990 foreach
(i
,card_list
) card_list.data
[i
].field
[j
]&=0x0FFFFFFF;
1995 @ The rule is that whenever the current field's value changes
, the bit in
1996 the corresponding grouping field will be flipped. Since the statistics
1997 grouping always treats consecutive equal values in the grouping field as
1998 belonging to the same group
, this is a way to insert ``group breaks'' into
2001 @
<Mark high bits of fields to prepare for complex statistics@
>= {
2002 int f
=G_table
(j
)&0x1F; // other field number
2003 int v
=card_list.data
[0].field
[j
]; // previous value
2004 int k
=1<<(27+((G_table
(j
)&0x60)>>5)); // bit flip value
2005 int b
=0; // current bit value
2006 foreach
(i
,card_list
) {
2007 if
(v
!=card_list.data
[i
].field
[j
]) b^
=k
;
2008 card_list.data
[i
].field
[f
]&=~k;
2009 card_list.data
[i
].field
[f
]|
=b
;
2010 v
=card_list.data
[i
].field
[j
];
2014 @ @
<Display the sorted card list@
>= {
2017 foreach
(i
,card_list
) {
2018 printf
("%d=[ ",card_list.data
[i
].token_ptr
);
2019 for
(j
=0;j
<32;j
++) printf
("%d ",card_list.data
[i
].field
[j
]);
2024 @
*Natural Sorting. A natural compare algorithm is used here. It is a
2025 generalization of Martin Pool's algorithm\biblio
{Pool
, Martin. ``Natural
2026 Order String Comparison''.
{\tt
2027 http
://sourcefrog.net
/projects
/natsort
/}.
}.
2029 The \.S table maps from character tokens to the sorting specifications.
2030 Name tokens are converted to |whatsit| when looking up in this table.
2032 Tokens are grouped into digits
, letters
, and priority letters. There are
2033 also some extras
, such as spaces and radix point. A string of consecutive
2034 digits is treated as numeric
, so a number with more digits comes after a
2035 number with less digits.
2037 Priority letters are used mainly for sorting roman numerals. Two or more
2038 consecutive priority letters are considered as a group
, otherwise they are
2039 treated in the same way as ordinary letters. A group is ranked with the
2040 letters latest in the alphabet
, so for example
, if |'I'| and |'
X'| are
2041 priority
, then |
"IX"| is placed between |
"W"| and |
"X"|. This way
, all
2042 roman numerals from I to XXXIX will be sorted correctly.
2051 @d nat_radix_point
4
2053 @d nat_digit_zero
64 // digits go up to
127
2054 @d nat_first_letter
128 // letters go up to
191
2055 @d nat_first_priority_letter
192 // priority letters go up to
255
2056 @d nat_high_value
256
2058 @
<Compare using the card's name and the \.S table@
>= {
2059 token
*pa
=card_areas.data
[set_card_area
(registers
['C'
].number
)].tokens
2061 token
*pb
=card_areas.data
[set_card_area
(registers
['C'
].number
)].tokens
2063 boolean fractional
=0; // Are we reading digits after a radix point?
2066 begin_natural_compare_loop
: @
/
2067 a
=tables
['S'
][*pa
>=256?whatsit
:*pa
];
2068 @
+ b
=tables
['S'
][*pb
>=256?whatsit
:*pb
];
2069 @
<Skip over leading spaces and
/or zeros@
>;
2070 @
<Process a run of digits@
>;
2071 @
<Check if the end of either string is reached@
>;
2072 @
<Check for a radix point@
>;
2073 @
<Process priority letters@
>;
2074 @
<Check if the current positions of each string sufficiently differ@
>;
2078 @ @
<Skip over leading spaces and
/or zeros@
>= {
2079 while
(a
==nat_space||a
==nat_ignore||
(!fractional
&&a==nat_digit_zero)) {
2080 int aa
=tables
['S'
][pa
[1]>=256?whatsit
:pa
[1]];
2081 if
(a
!=nat_ignore
) fractional
=0;
2082 if
(!fractional
&& a==nat_digit_zero
2083 && aa>=nat_digit_zero && aa<nat_first_letter) break;
2084 pa
++; @
+ a
=tables
['S'
][*pa
>=256?whatsit
:*pa
];
2086 while
(b
==nat_space||b
==nat_ignore||
(!fractional
&&b==nat_digit_zero)) {
2087 int bb
=tables
['S'
][pa
[1]>=256?whatsit
:pa
[1]];
2088 if
(b
!=nat_ignore
) fractional
=0;
2089 if
(!fractional
&& b==nat_digit_zero
2090 && bb>=nat_digit_zero && bb<nat_first_letter) break;
2091 pb
++; @
+ b
=tables
['S'
][*pb
>=256?whatsit
:*pb
];
2095 @ @
<Process a run of digits@
>= {
2096 if
(a
>=nat_digit_zero
&&a<nat_first_letter&&
2097 b
>=nat_digit_zero
&&b<nat_first_letter) {
2098 if
((c
=(fractional?compare_left
:compare_right
)(pa
,pb
))) return c
;
2099 @
<Skip the run of digits
, since they are the same@
>;
2105 @ Compare two left-aligned numbers
: the first to have a different value
2106 wins. This function and |compare_right| are basically equivalent
, there
2107 are only a few differences
(this one is the simpler one
).
2109 @
-p int compare_left
(token
*pa
,token
*pb
) {
2112 a
=tables
['S'
][*pa
>=256?whatsit
:*pa
];
2113 @
+ b
=tables
['S'
][*pb
>=256?whatsit
:*pb
];
2114 @
<Skip over ignored characters@
>;
2115 @
<If neither |a| nor |b| is digit
, |break|@
>;
2116 @
<If one is a digit and the other isn't
, the longest run wins@
>;
2117 @
<If both are different digits
, the greater one wins@
>;
2122 @ The longest run of digits wins. That aside
, the greatest value wins
, but
2123 we can't know that it will until we've scanned both numbers to know they
2124 have the same magnitude
, so we remember it in |bias|.
2126 @
-p int compare_right
(token
*pa
,token
*pb
) {
2130 a
=tables
['S'
][*pa
>=256?whatsit
:*pa
];
2131 @
+ b
=tables
['S'
][*pb
>=256?whatsit
:*pb
];
2132 @
<Skip over ignored characters@
>;
2133 @
<If neither |a| nor |b| is digit
, |break|@
>;
2134 @
<If one is a digit and the other isn't
, the longest run wins@
>;
2135 @
<If both are digits
, set the |bias|@
>;
2140 @ Ignored characters might be commas for grouping digits into thousands.
2142 @
<Skip over ignored characters@
>= {
2143 while
(a
==nat_ignore
) {
2144 pa
++; @
+ a
=tables
['S'
][*pa
>=256?whatsit
:*pa
];
2146 while
(b
==nat_ignore
) {
2147 pb
++; @
+ b
=tables
['S'
][*pb
>=256?whatsit
:*pb
];
2151 @ @
<If neither |a| nor |b| is digit
, |break|@
>= {
2152 if
(!(a
>=nat_digit_zero
&&a<nat_first_letter)&&
2153 !(b
>=nat_digit_zero
&&b<nat_first_letter)) break;
2156 @ @
<If one is a digit and the other isn't
, the longest run wins@
>= {
2157 if
(!(a
>=nat_digit_zero
&&a<nat_first_letter)) return -1;
2158 if
(!(b
>=nat_digit_zero
&&b<nat_first_letter)) return 1;
2161 @ @
<If both are different digits
, the greater one wins@
>= {
2162 if
(a
!=b
) return a-b
;
2165 @ @
<If both are digits
, set the |bias|@
>= {
2166 if
(a
!=b
&& !bias) bias=(a<b)?-1:1;
2169 @ @
<Skip the run of digits
, since they are the same@
>= {
2170 while
(a
>=nat_digit_zero
&&a<nat_first_letter) {
2171 pa
++; @
+ pb
++; @
+ a
=tables
['S'
][*pa
>=256?whatsit
:*pa
];
2173 b
=tables
['S'
][*pb
>=256?whatsit
:*pb
];
2176 @ @
<Check if the end of either string is reached@
>= {
2177 if
(a
==nat_end_low
&& b>nat_end_high) return -1;
2178 if
(b
==nat_end_low
&& a>nat_end_high) return 1;
2179 if
(a
==nat_end_high
&& b>nat_end_high) return 1;
2180 if
(b
==nat_end_high
&& a>nat_end_high) return -1;
2181 if
(a
<=nat_end_high
&& b<=nat_end_high) break; // tied
2184 @ A radix point must be followed by a digit
, otherwise it is considered to
2185 be punctuation
(and ignored
). Radix points come before digits in the
2186 sorting order
(|
".5"| comes before |
"5"|
).
2188 @
<Check for a radix point@
>= {
2189 if
(a
==nat_radix_point
&& b==nat_radix_point) {
2190 int aa
=tables
['S'
][pa
[1]>=256?whatsit
:pa
[1]];
2191 int bb
=tables
['S'
][pb
[1]>=256?whatsit
:pb
[1]];
2192 if
(aa
>=nat_digit_zero
&&aa<nat_first_letter
2193 &&bb>=nat_digit_zero&&bb<nat_first_letter) fractional=1;
2194 } @
+else if
(a
==nat_radix_point
) {
2195 int aa
=tables
['S'
][pa
[1]>=256?whatsit
:pa
[1]];
2196 if
(!(aa
>=nat_digit_zero
&&aa<nat_first_letter)) {
2197 pa
++; goto begin_natural_compare_loop
;
2199 } @
+else if
(b
==nat_radix_point
) {
2200 int bb
=tables
['S'
][pb
[1]>=256?whatsit
:pb
[1]];
2201 if
(!(bb
>=nat_digit_zero
&&bb<nat_first_letter)) {
2202 pb
++; goto begin_natural_compare_loop
;
2207 @ This is used so that |
"IX"| can be sorted between |
"VIII"| and |
"X"|. In
2208 normal alphabetical order
, |
"IX"| sorts before |
"V"|. This algorithm makes
2209 it so that doesn't happen. For example
: |a| is |'I'| and |aa|
(the
2210 character after |a| in the text
) is |'
X'|
(the check |aa
>a| ensures that
2211 it too is priority
, in addition to checking that |a| represents a negative
2212 part of a roman number
), and |b| is |'V'|. Now
, since |'V'| comes between
2213 |'I'| and |'
X'| in the alphabetical order
, the condition is checked to be
2214 valid and it overrides the later check.
2216 @
<Process priority letters@
>= {
2217 if
(a
>=nat_first_priority_letter
) {
2218 int aa
=tables
['S'
][pa
[1]>=256?whatsit
:pa
[1]];
2219 if
(aa
>a
&& b>=nat_first_letter && (b&63)>(a&63) && (b&63)<(aa&63))
2222 if
(b
>=nat_first_priority_letter
) {
2223 int bb
=tables
['S'
][pb
[1]>=256?whatsit
:pb
[1]];
2224 if
(bb
>b
&& a>=nat_first_letter && (a&63)>(b&63) && (a&63)<(bb&63))
2229 @ At this point
, |a| and |b| will both be |@
[@
]>=nat_radix_point|. Numbers
2230 always come after letters
(this rule is designed so that when a radix
2231 point is found after a number
, it will make a larger number
; otherwise it
2232 will be followed by a letter and therefore the one followed by the letter
2233 is lesser since it has no fractional part to make it greater
).
2235 @
<Check if the current positions of each string suffic...@
>= {
2236 if
(a
>=nat_first_priority_letter
) a-
=64;
2237 if
(b
>=nat_first_priority_letter
) b-
=64;
2238 if
(a
<nat_first_letter
) a
+=128;
2239 if
(b
<nat_first_letter
) b
+=128;
2240 if
(a
!=b
) return
(a
<b
)?
-1:1;
2243 @
*Name Sorting. This kind of sorting is used when items are grouped
2244 together by some extra field in the statistics
, such as creature types in
2245 Magic
: the Gathering.
2247 It works in a similar way to the natural sorting algorithm
, but this time
2248 it is simpler and not as many things need to be checked. Digits and
2249 priority letters are treated as normal letters
, and the types |nat_space|
,
2250 |nat_ignore|
, and |nat_radix_point| are all ignored. In addition
, a null
2251 terminator is always treated as |nat_end_low|.
2253 If both names compare the same
, their number is used instead
, in order to
2254 force sorting stability.
2256 @
-p int name_compare
(int n1
,int n2
) {
2257 char
*s1
=name_info
(n1
).name
;
2258 char
*s2
=name_info
(n2
).name
;
2260 for
(;*s1 ||
*s2
;s1
++,s2
++) {
2261 a
=(*s1
)?tables
['S'
][*s1
]:nat_end_low
;
2262 b
=(*s2
)?tables
['S'
][*s2
]:nat_end_low
;
2263 @
<Skip over spaces and ignored characters@
>;
2264 @
<Check if the end of either string is reached@
>;
2265 @
<Check if the current positions of...@
>;
2267 return
(n1
<n2
)?
-1:1;
2270 @ @
<Skip over spaces and ignored characters@
>= {
2271 while
(a
<nat_digit_zero
) {
2272 s1
++; @
+ a
=(*s1
)?tables
['S'
][*s1
]:nat_end_low
;
2274 while
(b
<nat_digit_zero
) {
2275 s2
++; @
+ b
=(*s2
)?tables
['S'
][*s2
]:nat_end_low
;
2279 @
*Statistics. After the card lists are created and sorted and grouped
, it
2280 can make statistics from them. It can be just a plain list
, or it can be
2281 in summary of groups
, measuring count
, minimum
, maximum
, mean
, median
, and
2284 First we do the simple iteration.
2292 @
<Cases for system commands@
>=
2294 // Iterate the card list
2296 char
*q
=pop_string
();
2297 if
(!stack_ptr
[1].is_string
) program_error
("Type mismatch");
2298 foreach
(i
,card_list
) {
2299 push_num
(card_list.data
[i
].token_ptr
);
2307 // Read a field from the card list
2311 foreach
(i
,card_list
) {
2312 if
(registers
['A'
].number
==card_list.data
[i
].token_ptr
)
2313 y
=card_list.data
[i
].field
[x
];
2319 @ That was simple
, see? Now to do gathering statistics of summary of
2320 groups
, which is a bit more complicated. The list is expected to be sorted
2321 by the group field primary
, and the statistics field ascending as
2322 secondary
, in order to make the correct calculation of the fields.
2324 However
, it will not do the sorting automatically
, since there are some
2325 reasons why you might want it to work differently. One thing you can do is
2326 to sort the group field
{\sl secondary
} and some other more major group as
2327 primary
, in order to do two-dimensional statistics
, and this will work as
2328 long as you do not require the minimum
, maximum
, or median.
2330 @
<Cases for system commands@
>=
2332 // Gather statistics of groups
2334 int x
=pop_num
()&31; // field for grouping
2335 int y
=pop_num
()&31; // field to measure statistics with
2336 int sum1
,sum2
; // running totals of $s_1$ and $s_2$
2338 char
*q
=pop_string
(); // code to execute for each group
2339 if
(!stack_ptr
[1].is_string
) program_error
("Type mismatch");
2340 foreach
(i
,card_list
) {
2341 if
(card_list.data
[i
].field
[x
]!=card_list.data
[si
].field
[x
]) {
2342 @
<Send the results of the current group@
>;
2343 sum1
=sum2
=0; @
+ si
=i
;
2345 @
<Add to the running totals@
>;
2347 @
<Send the results of the current group@
>;
2352 @ Running totals are kept for two quantities called $s_1$ and $s_2$. There
2353 is also $s_0$
, but that can be calculated easily using subtraction
, so
2354 there is no need to keep a running total. If the sample values are denoted
2355 $x_k$
, the following equation represents the running totals
:
2356 $$s_j
=\sum_
{k
=1}^N
{x_k^j
}$$
(note that $s_0
=N$.
)
2360 @
<Add to the running totals@
>= {
2361 sum1
+=card_list.data
[i
].field
[y
];
2362 sum2
+=card_list.data
[i
].field
[y
]*card_list.data
[i
].field
[y
];
2365 @ Now we will send the results and call |q|. The results are sent to the
2366 stack in the following order
: $s_0$
, $s_1$
, $s_2$
, $Q_0$
, $
2Q_2$
, $Q_4$
2367 (where $Q_0$ is the minimum
, $Q_2$ the median
, and $Q_4$ the maximum
).
2369 From these results
, it is then possible to calculate the standard
2370 deviation
: $$\sigma
={1\over s_0
}\sqrt
{s_0s_2-s_1^
2}$$ and
2371 $$s
=\sqrt
{s_0s_2-s_1^
2\over s_0
(s_0-1
)}.$$
2375 @
<Send the results of the current group@
>= {
2376 push_num
(i-si
); // $s_0$
2377 push_num
(sum1
); // $s_1$
2378 push_num
(sum2
); // $s_2$
2379 push_num
(card_list.data
[si
].field
[y
]); // $Q_0$
2381 card_list.data
[(si
+i
)/2].field
[y
]+card_list.data
[(si
+i
+1)/2].field
[y
]
2383 push_num
(card_list.data
[i-1
].field
[y
]); // $Q_4$
2384 @# push_num
(card_list.data
[si
].token_ptr
); @
+ store
('A'
);
2388 @
*Random Pack Generation. Now the codes so that it can create random packs
2389 (such as booster packs
) by using the card lists and deck lists.
2391 A command |'P'| is used for evaluation of a deck list. It expects the deck
2392 list number and the code to execute for each card on the list.
2396 @
<Cases for system commands@
>=
2398 // Generate a random pack or deck
2399 data_index s
=set_deck_list
(pop_num
());
2400 data_index n
; // current deck list entry
2401 if
(stack_ptr
[1].is_string
) program_error
("Number expected");
2402 @
<Figure out what cards belong in the pack@
>;
2403 @
<Execute the code on the stack for each card in the pack@
>;
2407 @ @
<Figure out what cards belong in the pack@
>= {
2409 int tries
=1000; // How many times can you retry if it fails?
2411 if
(!--tries
) program_error
("No cards matched the deck criteria");
2413 @
<Reset |amount_in_pack| of each card to zero@
>;
2414 while
(n
!=none
&& (n=(e=deck_lists.data+n)->next)!=none)
2415 @
<Process this deck entry@
>;
2418 @ @
<Reset |amount_in_pack| of each card to zero@
>= {
2420 foreach
(i
,card_list
) card_list.data
[i
].amount_in_pack
=0;
2423 @ The deck entry must be processed according to the flags. Here is a list
2426 \.a
: Use all cards that meet the criteria
, instead of only one. If this is
2427 the case
, it is possible to use negative weights to remove cards from the
2428 pack. Also
, it cannot fail.
2429 [Combine with \.
{x
}]
2431 \.k
: Select without replacement. It is fail if the total weight is not
2432 enough. There are two ways in which this differs from \.u
(below
). One is
2433 that the previous lines in the deck list are not used. The other one is
2434 that if the weight is more than one
, there will be more than one ball for
2435 that card
, therefore the same card can be picked up multiple times.
2436 [Combine with \.
{sux
}]
2438 \.n
: Use the |amount| as a probability. If |amount
<=100| then the
2439 probability is |amount
/100| otherwise it is |
100/amount|. This is a
2440 probability of using the |name| to select another deck list instead of
2442 [Combine with nothing
]
2444 \.s
: Skip the next line if this line does not fail.
(Normally
, if one line
2445 fails
, everything does
, and you have to try again.
)
2446 [Combine with \.
{kux
}]
2448 \.u
: Require unique selection. It is fail if the card is already in this
2450 [Combine with \.
{ksx
}]
2452 \.x
: Pass the |name| as a string to the code in the \.U register
, and then
2453 use the resulting code as the code to determine weights instead of using
2454 the code in the register named by |name| directly. Now you can type things
2455 such as |
"12x Forest"| into your deck list.
2456 [Combine with \.
{aksu
}]
2458 @
<Process this deck entry@
>= {
2459 if
(e-
>flags
&lflag('n')) {
2460 @
<Determine whether or not to skip to another deck list@
>;
2462 char
*c
; // code for weights of each card
2463 int total
; // total weight of cards
2464 data_index
*bag
=malloc
(sizeof
(data_index
));
2465 @
<Get the code |c| for the weights of each card@
>;
2466 @
<Calculate the weights of each card@
>;
2467 if
(!(e-
>flags
&lflag('a')))
2468 @
<Select some of the cards at random and add them to the pack@
>;
2469 if
(e-
>flags
&lflag('x')) free(c);
2474 @ @
<Determine whether or not to skip to another deck list@
>= {
2476 if
(e-
>amount
<=100) {
2477 q
=(gen_random
(100)<e-
>amount
);
2479 q
=(100<gen_random
(e-
>amount
));
2481 if
(q
) n
=set_deck_list
(find_name
(e-
>name
));
2484 @ @
<Get the code |c| for the weights of each card@
>= {
2485 if
(e-
>flags
&lflag('x')) {
2486 execute_program
(registers
['U'
].text
);
2487 if
(stack_ptr-
>is_string
) {
2490 program_error
("Type mismatch");
2493 int n
=find_name
(e-
>name
);
2494 if
(name_info
(n
).value.is_string
) {
2495 c
=name_info
(n
).value.text
;
2497 program_error
("Type mismatch");
2502 @ @
<Calculate the weights of each card@
>= {
2504 foreach
(i
,card_list
) {
2505 registers
['A'
].number
=card_list.data
[i
].token_ptr
;
2507 if
(stack_ptr-
>number
) {
2508 if
(e-
>flags
&lflag('a')) {
2509 card_list.data
[i
].amount_in_pack
+=e-
>amount
*stack_ptr-
>number
;
2510 } @
+else if
(stack_ptr-
>number
>0) {
2511 @
<Add the cards to the |bag|@
>;
2518 @ The |bag| is like
, you put the balls in the bag so that you can mix it
2519 and take one out
, whatever number is on the ball is the card you put into
2520 the pack. Except
, that there is no balls and no bag.
2522 There is one ball per point of weight.
2526 @
<Add the cards to the |bag|@
>= {
2527 int j
=stack_ptr-
>number
;
2528 bag
=realloc
(bag
,(total
+j
)*sizeof
(data_index
));
2529 while
(j--
) bag
[total
+j
]=i
;
2530 total
+=stack_ptr-
>number
;
2533 @ If it is not a line which adds all possibilities at once
, then the cards
2534 must be selected from the |bag| at random to bag them. In some cases it
2537 @
<Select some of the cards at random and add them to the pack@
>= {
2539 int amount
=e-
>amount
;
2541 if
(!total
) @
<Deal with bag failure@
>;
2542 r
=gen_random
(total
);
2543 if
((e-
>flags
&lflag('u')) && card_list.data[bag[r]].amount_in_pack) {
2544 bag
[r
]=bag
[--total
];
2547 card_list.data
[bag
[r
]].amount_in_pack
++;
2548 if
(e-
>flags
&lflag('k')) bag[r]=bag[--total];
2549 if
(amount--
) goto bag_next
;
2550 @#if
(e-
>flags
&lflag('s')) n=deck_lists.data[n].next;
2554 @ @
<Deal with bag failure@
>= {
2555 if
(e-
>flags
&lflag('s')) goto bag_done;
2556 else goto figure_out_again
;
2559 @ Now it must do stuff using the list which is generated. The quantity for
2560 how many of that card is pushed on the stack
, and this is done even for
2561 cards with negative quantity
(but not for zero quantity
).
2563 @
<Execute the code on the stack for each card in the pack@
>= {
2565 char
*q
=pop_string
();
2566 if
(!stack_ptr
[1].is_string
) program_error
("Type mismatch");
2567 foreach
(i
,card_list
) {
2568 if
(card_list.data
[i
].amount_in_pack
) {
2569 push_num
(card_list.data
[i
].amount_in_pack
);
2576 @
*Reading Input Files. Now it is time for the part of the program where
2577 input files are read and processed. The areas of the file
(and other
2578 special commands
) are indicated using \.@@ signs.
2580 At first we have state information. Each state is labeled by uppercase
2581 letters
, or by digits
1 to
9. The high bit is set for the heading state
,
2582 meaning the first line that contains the name and
/or other heading
2588 @d execute_state 'E'
2590 @d include_state 'I'
2591 @d keyword_state 'K'
2593 @d pattern_state 'P'
2594 @d subroutine_state 'S'
2596 @d encoding_state 'U'
2597 @d wordforms_state 'W'
2600 @
<Global variables@
>=
2602 data_index cur_name
;
2603 data_index cur_data
;
2604 boolean omit_line_break
;
2606 @ The next thing that must be kept track of for input files is the stack
2607 of open input files.
2609 @d max_pathname_length
128
2610 @d max_filename_length
128
2611 @d max_input_stack
128
2612 @d max_line_length
256
2616 FILE*fp
; // zero for terminal input
2617 char name
[max_filename_length
+1];
2621 @ @
<Global variables@
>=
2622 input_file_data input_files
[max_input_stack
];
2623 input_file_data
*current_input_file
=input_files
;
2624 char input_buffer
[max_line_length
];
2626 @ Some macros are useful to access the current file data.
2628 @d current_line
(current_input_file-
>line
)
2629 @d current_filename
(current_input_file-
>name
)
2630 @d current_fp
(current_input_file-
>fp
)
2632 @d parsing_error
(_text
) fprintf
(stderr
,"%s on line %d in %s\n",
2633 _text
,current_line
,current_filename
)@
;
2635 @ There is also conditional processing directives
, which uses a single
2636 variable to keep track of the level. If it is greater than zero
, the
2637 condition is false
, and it is increased for nesting conditions
(the
2638 nested conditions have no truth to them
).
2640 @
<Global variables@
>=
2641 int condition_level
=0;
2643 @ This subroutine inputs the next line. True is returned if there is a
2644 line
, or false if it is finished.
2646 It is necessary to check for end of file and if so
, close that file and
2647 try the one it was included from
; and if it is terminal input
, display the
2648 current state when prompting input from the user.
2650 @
-p boolean input_line
(void
) {
2651 input_line_again
: if
(current_fp
) {
2652 @
<Get a line of input from the file@
>;
2654 @
<Get a line of terminal input@
>;
2656 @
<Remove trailing |'\n'|
, |'\r'|
, and spaces@
>;
2661 @ @
<Get a line of input from the file@
>= {
2662 if
(!fgets
(input_buffer
,max_line_length
,current_fp
)) {
2663 memusage_log
("Closing input file",current_input_file-input_files
)@
;
2665 if
(current_input_file
>input_files
) {
2666 --current_input_file
;
2667 goto input_line_again
;
2674 @ @
<Get a line of terminal input@
>= {
2675 printf
("\n%c> ",cur_state?cur_state
:'
>'
);
2677 if
(!fgets
(input_buffer
,max_line_length
,stdin
)) return
0;
2680 @ This function is used to open the main input file.
2682 @
-p void open_input
(char
*name
) {
2683 if
(++current_input_file
>input_files
+max_input_stack
) {
2684 fprintf
(stderr
,"Too many simultaneous input files\n");
2685 @.Too many simultaneous...@
>
2688 memusage_log
("Opening input file",current_input_file-input_files
)@
;
2689 strcpy
(current_filename
,name
);
2691 current_fp
=fopen
(name
,"r");
2693 fprintf
(stderr
,"Cannot open input file: %s\n",name
);
2694 @.Cannot open input file@
>
2699 @ Trailing newlines and spaces are removed. On some computers
, there will
2700 be a carriage return before the line feed
, it should be removed
, so that
2701 the same file will work on other computers
, too.
2703 @d last_character_input input_buffer
[strlen
(input_buffer
)-1]
2705 @
<Remove trailing |'\n'|
, |'\r'|
, and spaces@
>= {
2706 if
(last_character_input
=='\n'
) last_character_input
=0;
2707 if
(last_character_input
=='\r'
) last_character_input
=0;
2708 while
(last_character_input
==' '
) last_character_input
=0;
2711 @ The input states start at these values.
2713 @
<Initialize the input states@
>= {
2714 cur_state
=execute_state
;
2715 cur_name
=cur_data
=0;
2718 @ Now it is the time to do the actual processing according to the contents
2719 of the lines of the file. A line starting with \.@@ sign will indicate a
2720 special command
(to operate in all modes
) or a mode switch command.
2722 @d delete_chars
(_buf
,_c
) memmove
((_buf
),(_buf
)+(_c
),strlen
((_buf
)+(_c
))+1)
2724 @
<Process the input files@
>= {
2726 while
(input_line
()) {
2728 if
(condition_level
) {
2729 buf
+=strspn
(buf
," ");
2730 condition_level
+=!strcmp
(buf
,"@@<");
2731 condition_level-
=!strcmp
(buf
,"@@>");
2734 @
<Convert \.@@ commands in the |input_buffer|@
>;
2741 @ @
<Convert \.@@ commands in the |input_buffer|@
>= {
2742 char
*ptr
=input_buffer
;
2745 @
<Convert the current \.@@ command@
>;
2752 @ @
<Convert the current \.@@ command@
>= {
2755 delete_chars
(ptr
,1);
2757 case '.'
: @
<Process \.
{@@.
} command@
>;@
+break
;
2758 case '
&': @<Process \.{@@\&} command@>;@+break;
2759 case '^'
: @
<Process \.
{@@\^
} command@
>;@
+break
;
2760 case '
('
: @
<Process \.
{@@
(} command@
>;@
+break
;
2761 case '
<'
: @
<Process \.
{@@
<} command@
>;@
+break
;
2762 case '
>'
: @
<Remove this command from the input@
>;@
+break
;
2764 if
((*ptr
>='A'
&& *ptr<='Z') || (*ptr>='0' && *ptr<='9')) {
2765 @
<Enter a |heading| state@
>;
2767 parsing_error
("Unknown @@ command");
2772 @ @
<Remove this command from the input@
>= {
2774 delete_chars
(ptr
,2);
2777 @ Heading states are used for the first line of a section in the file.
2778 After that line is processed
, it becomes the corresponding non-heading
2779 state |
(cur_state
&~heading)|.
2781 Note
: The state |'
0'| is deliberately unused
; you might use it for
2782 documentation areas
, for example.
2784 @^documentation areas@
>
2786 @
<Enter a |heading| state@
>= {
2787 cur_state
=heading|
*ptr--
;
2788 delete_chars
(ptr
,2);
2789 while
(*ptr
==' ' ||
*ptr
=='\t'
) delete_chars
(ptr
,1);
2792 @ @
-p void process_line
(char
*buf
) {
2794 cur_state
&=~heading;
2796 case card_state
: @
<Process card state@
>;@
+break
;
2797 case deck_state
: @
<Process deck state@
>;@
+break
;
2798 case execute_state
: @
<Process execute state@
>;@
+break
;
2799 case file_state
: @
<Process file state@
>;@
+break
;
2800 case keyword_state
: @
<Process keyword state@
>;@
+break
;
2801 case keyword_state
: @
<Process image state@
>;@
+break
;
2802 case pattern_state
: @
<Process pattern state@
>;@
+break
;
2803 case subroutine_state
: @
<Process subroutine state@
>;@
+break
;
2804 case wordforms_state
: @
<Process word forms state@
>;@
+break
;
2805 case card_state|heading
: @
<Process card heading@
>;@
+break
;
2806 case deck_state|heading
: @
<Process deck heading@
>;@
+break
;
2807 case file_state|heading
: @
<Process file heading@
>;@
+break
;
2808 case include_state|heading
: @
<Process include heading@
>;@
+break
;
2809 case keyword_state|heading
: @
<Process keyword heading@
>;@
+break
;
2810 case image_state|heading
: @
<Process image heading@
>;@
+break
;
2811 case pattern_state|heading
: @
<Process pattern heading@
>;@
+break
;
2812 case subroutine_state|heading
: @
<Process subroutine heading@
>;@
+break
;
2813 default
: ; // nothing happens
2817 @ Sometimes you might want a macro which can send a line programmatically.
2818 So
, here is the way that it is done.
2820 @
<Cases for system commands@
>=
2822 // Process a line by programming
2824 if
(stack_ptr-
>is_string
) program_error
("Type mismatch");
2826 if
(n
) cur_state
=n|heading
;
2827 if
(!stack_ptr-
>is_string
) program_error
("Type mismatch");
2829 process_line
(stack_ptr-
>text
);
2834 @
*Inner Commands. These are commands other than the section headings.
2836 @ The first command to deal with is simple--it is a comment. The rest of
2837 the current line is simply discarded.
2839 @
<Process \.
{@@.
} command@
>= {
2843 @ This command is a pattern split. It means it will process the part of
2844 the line before this command and then process the stuff after it. The
2845 variable |omit_line_break| is
1 if this command is used
; because it means
2846 there will not be a line break.
(Otherwise
, patterns and so on are split
2849 @
<Process \.
{@@\
&} command@>= {
2855 @ This allows control characters to be inserted into the input. This code
2856 takes advantage of the way the ASCII code works
, in which stripping all
2857 but the five low bits can convert a letter
(uppercase or lowercase
) to its
2858 corresponding control character.
2860 @^control character@
>
2862 @
<Process \.
{@@\^
} command@
>= {
2865 delete_chars
(ptr
,2);
2868 @ The following command is used to execute a code in a different state and
2869 then include the results in this area.
2871 @
<Process \.
{@@
(} command@
>= {
2874 @
<Skip over the name and save the rest of the line@
>;
2875 @
<Execute the code for the named subroutine@
>;
2876 @
<Insert the returned string and fix the line buffer@
>;
2879 @ @
<Skip over the name and save the rest of the line@
>= {
2881 while
(*ptr
&& *ptr!=')') ptr++;
2882 q
=strdup
(ptr
+!!*ptr
);
2886 @ @
<Execute the code for the named subroutine@
>= {
2888 execute_program
(fetch_code
(n
));
2891 @ @
<Insert the returned string and fix the line buffer@
>= {
2892 char
*s
=pop_string
();
2893 sprintf
(p-2
,"%s%s",s
,q
);
2899 @ This command is used for conditional processing. The condition value
2900 comes from the stack. Zero is false
, everything else is true.
2902 @
<Process \.
{@@
<} command@
>= {
2904 delete_chars
(ptr
,2);
2905 condition_level
=!stack_ptr-
>number
;
2909 @
*Card State. Cards are added to the card areas by using the card state.
2910 The \.C register tells which is the current card area
, and \.P register is
2911 used to select the current pattern area. The pattern area is used to match
2912 patterns after reading a line. Please note that it won't work to change
2913 the value of the \.C register during the card state.
2915 @
<Process card heading@
>= {
2916 int n
=find_name
(buf
);
2917 cur_data
=set_card_area
(n
);
2919 push_num
(n
);@
+store
('C'
);
2922 @ @
<Process card state@
>= {
2924 if
(!omit_line_break
) strcpy
(buf
+strlen
(buf
),"\n");
2925 @
<Initialize the \.W register@
>;
2926 b
=do_patterns
(strdup
(buf
),registers
['P'
].number
);
2927 if
(registers
['W'
].is_string
) execute_program
(registers
['W'
].text
);
2928 @
<Send the tokens of |b| and replace whatsits@
>;
2932 @ @
<Initialize the \.W register@
>= {
2933 if
(registers
['W'
].is_string
) free
(registers
['W'
].text
);
2934 registers
['W'
].is_string
=1;
2935 registers
['W'
].text
=strdup
("");
2938 @ @
<Send the tokens of |b| and replace whatsits@
>= {
2942 send_token
(cur_data
,pop_num
());
2944 send_token
(cur_data
,(*p
==1)?
0:*p
);
2949 @ There is one command you might want to send tokens in any other time.
2951 @
<Cases for system commands@
>=
2953 // Add tokens to the card area
2954 if
(stack_ptr-
>is_string
) {
2955 @
<Send tokens from the string on the stack@
>;
2957 send_token
(set_card_area
(registers
['C'
].number
),stack_ptr-
>number
);
2963 @ @
<Send tokens from the string on the stack@
>= {
2965 data_index q
=set_card_area
(registers
['C'
].number
);
2966 for
(p
=stack_ptr-
>text
;*p
;p
++) send_token
(q
,*p
);
2969 @
*Deck State. Deck state is used for creating deck lists and random packs.
2971 @
<Process deck heading@
>= {
2972 cur_name
=find_name
(buf
)-256;
2973 cur_data
=set_deck_list
(cur_name
+256);
2974 @
<Skip to the end of the deck list@
>;
2977 @ @
<Skip to the end of the deck list@
>= {
2978 while
(deck_lists.data
[cur_data
].next
!=none
)
2979 cur_data
=deck_lists.data
[cur_data
].next
;
2982 @ Now to parse each line in turn. Each line consists of a number
, the
2985 @
<Process deck state@
>= {
2986 int n
=strtol
(buf
,&buf,10);
2989 buf
+=strspn
(buf
,"\x20\t");
2990 @
<Read the flags for the deck list@
>;
2991 buf
+=strspn
(buf
,"\x20\t"); // Now we are at the point of the text
2992 @
<Send this line to the deck list@
>;
2993 @
<Create and advance to the new terminator of the deck list@
>;
2997 @ @
<Read the flags for the deck list@
>= {
2998 while
(*buf
>='a'
&& *buf<='z') f |=lflag(*buf++);
2999 buf
++; // Skip terminator of flags
3002 @ If the \.x flag is set
, it will be determined what to do with the text
3003 by the user-defined code. Otherwise
, it is always a name
, so we can save
3004 memory by pointing to the name buffer
(since name buffers never vary
).
3006 @
<Send this line to the deck list@
>= {
3008 deck_lists.data
[cur_data
].name
=strdup
(buf
);
3010 deck_lists.data
[cur_data
].name
=name_info
(find_name
(buf
)).name
;
3014 @ @
<Create and advance to the new terminator of the deck list@
>= {
3015 data_index i
=new_record
(deck_lists
);
3016 deck_lists.data
[cur_data
].next
=i
;
3017 deck_lists.data
[cur_data
=i
].next
=none
;
3020 @
*Execute State. This state is simple
, just execute stack codes. It is the
3021 initial state
; you can use it with terminal input
, too.
3023 @
<Process execute state@
>= {
3024 execute_program
(buf
);
3027 @
*File State. This state is used to make list of output files. Each one is
3028 stored as a string
, like subroutine state. The difference is that newlines
3029 will not be discarded. The other difference is that there is a flag in the
3030 |name_data| record for it that tells it that it is a file that should be
3033 @
<More elements of |name_data|@
>=
3034 boolean is_output_file
;
3036 @ @
<Process file heading@
>= {
3037 cur_name
=find_name
(buf
)-256;
3038 if
(!names.data
[cur_name
].value.is_string
) {
3039 names.data
[cur_name
].value.is_string
=1;
3040 names.data
[cur_name
].value.text
=strdup
("");
3041 names.data
[cur_name
].is_output_file
=1;
3045 @ @
<Process file state@
>= {
3046 int z
=strlen
(names.data
[cur_name
].value.text
);
3047 if
(!omit_line_break
) strcpy
(buf
+strlen
(buf
),"\n");
3048 names.data
[cur_name
].value.text
=realloc
(names.data
[cur_name
].value.text
,
3050 strcpy
(names.data
[cur_name
].value.text
+z
,buf
);
3053 @
*Include State. The include state causes inclusion of another source file
3056 @
<Process include heading@
>= {
3057 cur_state
=execute_state
;
3058 @
<Push the include file onto the input stack@
>;
3059 @
<Attempt to open the include file...@
>;
3062 @ @
<Push the include file onto the input stack@
>= {
3063 ++current_input_file
;
3064 memusage_log
("Opening input file",current_input_file-input_files
)@
;
3065 strcpy
(current_filename
,buf
);
3070 @ Include files are searched using the search path specified in the
3071 environment variable called \.
{TEXNICARDPATH
}, which is a list of paths
3072 delimited by colons on
UNIX systems
(including Cygwin
), but semicolons on
3073 Windows
(colons are used in Windows for drive letters
). A forward slash is
3074 the path separator. Please note that if you want to use include files in
3075 the current directory
, you must include |
"."| as the first entry in the
3082 @
<Set |includepath_separator| depending on operating system@
>=
3084 #define @
!includepath_separator '
;'
3086 #define includepath_separator '
:'
3089 @ @
<Attempt to open the include file by finding it in the search path@
>= {
3090 current_fp
=open_file
(current_filename
,"r");
3091 @
<It is a fatal error if no such file was found@
>;
3094 @ Since this part of the code is activated in many parts of the program
,
3095 we will make it a subroutine that can open files in different modes.
3097 @
-p
FILE*open_file
(char
*filename
,char
*mode
) {
3098 char searchpath
[max_pathname_length
+max_filename_length
+1];
3100 char
*npath
=getenv
("TEXNICARDPATH");
3102 strcpy
(searchpath
,npath?npath
:".");
3103 npath
=cpath
=searchpath
;
3104 @
<Set |includepath_separator| depending on operating system@
>;
3105 @
<Attempt to open the file from each each directory in the search path@
>;
3109 @ @
<Attempt to open the file from each each directory...@
>= {
3111 char f
[max_pathname_length
+max_filename_length
+1];
3112 @
<Select the next path name into |cpath| and |npath|@
>;
3113 sprintf
(f
,"%s/%s",cpath
,filename
);
3118 @ @
<Select the next path name into |cpath| and |npath|@
>= {
3119 if
(!(cpath
=npath
)) break
;
3120 if
((npath
=strchr
(npath
,includepath_separator
))) *npath
++=0;
3123 @ @
<It is a fatal error if no such file was found@
>= {
3125 fprintf
(stderr
,"%s not found in search path.\n",current_filename
);
3126 @.not found in search path@
>
3131 @
*Keyword State. You can add keywords to the keyword area by using this.
3132 Each keyword heading is one entry in the list.
3134 @
<Process keyword heading@
>= {
3135 cur_data
=new_record
(keywords
);
3136 keywords.data
[cur_data
].match
=strdup
(buf
);
3137 keywords.data
[cur_data
].replacement
=strdup
("");
3140 @ @
<Process keyword state@
>= {
3141 keyword_data
*k
=&keywords.data[cur_data];
3143 k-
>category|
=find_category
(buf
+1);
3145 if
(!omit_line_break
) strcpy
(buf
+strlen
(buf
),"\n");
3146 @
<Append buffer to keyword text@
>;
3150 @ @
<Append buffer to keyword text@
>= {
3152 int z
=strlen
(k-
>replacement
);
3153 k-
>replacement
=realloc
(k-
>replacement
,z
+strlen
(buf
)+1);
3154 strcpy
(k-
>replacement
+z
,buf
);
3158 @
*Image State. This state compiles image manipulator commands.
3160 @
<Process image heading@
>= {
3161 int n
=find_name
(buf
);
3162 cur_data
=set_image_manip
(n
);
3166 @ Each line will be executed and then the numbers on the stack are used as
3167 the command codes. This means you can also use names and calculations and
3168 so on
, instead of always entering the commands directly.
3170 @
<Process image state@
>= {
3171 register_value
*p
=stack_ptr
;
3172 execute_program
(buf
);
3174 fprintf
(stderr
,"Stack underflow\n");
3178 @
<Get the numbers from the stack
, store at image manipulation area@
>;
3179 while
(stack_ptr
>p
) stack_drop
();
3183 @ @
<Get the numbers from the stack
, store at image manipulation area@
>= {
3184 register_value
*s
=stack_ptr
;
3186 image_manipulator
*im
=image_manips.data
+cur_data
;
3187 while
(im-
>next
!=none
) im
=image_manips.data
+im-
>next
;
3188 im-
>data
=malloc
((im-
>data_len
=s-p
)*sizeof
(unsigned short
));
3189 for
(;s
<p
;s
++,i
++) im-
>data
[i
]=s-
>number
;
3192 @
*Pattern State. This state compiles patterns into a pattern area. It
3193 uses its own syntax
, and then is converted into the proper control codes
3194 for the |text| of a pattern.
3196 @
<Process pattern heading@
>= {
3197 cur_name
=find_name
(buf
)-256;
3198 cur_data
=set_pattern_area
(cur_name
+256);
3201 @ The stuff inside the pattern state has its own commands.
3203 @
<Process pattern state@
>= {
3204 char add_buf
[1024]; // buffer of text to add to the current pattern
3205 pattern_data
*pat
=&pattern_areas.data[cur_data];
3208 case '
<'
: @
<Create a new pattern with top priority@
>;@
+break
;
3209 case '
>'
: @
<Create a new pattern with bottom priority@
>;@
+break
;
3210 case '
:'
: @
<Make a pattern text with a marker@
>;@
+break
;
3211 case '
+'
: @
<Add a keyword category to this pattern@
>;@
+break
;
3212 default
: ; // do nothing
3214 @
<Append contents of |add_buf| to the pattern
, if needed@
>;
3217 @ @
<Create a new pattern with top priority@
>= {
3218 cur_data
=new_record
(pattern_areas
);
3219 pattern_areas.data
[cur_data
].text
=strdup
("");
3220 pattern_areas.data
[cur_data
].subroutine
=find_name
(buf
+1)-256;
3221 pattern_areas.data
[cur_data
].next
=names.data
[cur_name
].pattern_area
;
3222 names.data
[cur_name
].pattern_area
=cur_data
;
3225 @ @
<Create a new pattern with bottom priority@
>= {
3227 cur_data
=new_record
(pattern_areas
);
3228 pattern_areas.data
[cur_data
].text
=strdup
("");
3229 pattern_areas.data
[cur_data
].subroutine
=find_name
(buf
+1)-256;
3230 pattern_areas.data
[cur_data
].next
=none
;
3231 @
<Find the bottom pattern and store its index in |n|@
>;
3232 pattern_areas.data
[n
].next
=cur_data
;
3235 @ @
<Find the bottom pattern and...@
>= {
3236 n
=names.data
[cur_name
].pattern_area
;
3237 while
(pattern_areas.data
[n
].next
!=none
&& pattern_areas.data[n].text &&
3238 pattern_areas.data
[pattern_areas.data
[n
].next
].next
!=none
)
3239 n
=pattern_areas.data
[n
].next
;
3242 @ Actually
, the name of this \strike
{cake
} chunk is a lie
, because it does
3243 not always add a marker.
3245 @
<Make a pattern text with a marker@
>= {
3248 @
<Add the pattern marker if applicable@
>;
3249 for
(p
=buf
+2;p
[-1] && *p;p++) {
3251 case '\\'
: *b
++=*++p
; @
+break
;
3252 case '
('
: *b
++=begin_capture
; @
+break
;
3253 case '
)'
: *b
++=end_capture
; @
+break
;
3254 case '
%'
: *b
++=match_keyword
; @
+*b
++=*++p
; @
+break
;
3255 case '
!'
: *b
++=match_table
; @
+*b
++=*++p
; @
+break
;
3256 case '?'
: *b
++=optional_table
; @
+*b
++=*++p
; @
+break
;
3257 case '#'
: *b
++=failed_match
; @
+break
;
3258 case '
&': *b++=jump_table; @+*b++=*++p; @+break;
3259 case '
;'
: *b
++=successful_match
; @
+break
;
3260 case '
<'
: *b
++=back_one_space
; @
+break
;
3261 case '
>'
: *b
++=forward_one_space
; @
+break
;
3262 case '
['
: *b
++=match_left_side
; @
+break
;
3263 case '
]'
: *b
++=match_right_side
; @
+break
;
3264 default
: *b
++=*p
; @
+break
;
3270 @ @
<Add the pattern marker if applicable@
>= {
3271 if
(buf
[1]>' '
) *b
++=buf
[1]|
0x80;
3274 @ @
<Add a keyword category to this pattern@
>= {
3275 pattern_areas.data
[cur_data
].category
=find_category
(buf
+1);
3278 @ @
<Append contents of |add_buf| to the pattern...@
>= {
3280 int z
=strlen
(pat-
>text
);
3281 pat-
>text
=realloc
(pat-
>text
,z
+strlen
(add_buf
)+1);
3282 strcpy
(pat-
>text
+z
,add_buf
);
3286 @
*Subroutine State. This state is used to add a named subroutine.
3288 @
<Process subroutine heading@
>= {
3289 cur_name
=find_name
(buf
)-256;
3290 if
(!names.data
[cur_name
].value.is_string
) {
3291 names.data
[cur_name
].value.is_string
=1;
3292 names.data
[cur_name
].value.text
=strdup
("");
3296 @ @
<Process subroutine state@
>= {
3297 int z
=strlen
(names.data
[cur_name
].value.text
);
3298 names.data
[cur_name
].value.text
=realloc
(names.data
[cur_name
].value.text
,
3300 strcpy
(names.data
[cur_name
].value.text
+z
,buf
);
3303 @
*Word Forms State. You can use the word forms state to enter rules and
3304 exceptions for word forms
, such as plurals.
3306 @
<Global variables@
>=
3307 char wordform_code
[256]; // code to execute at \.
= line
3308 char wordform_kind
; // which kind of word forms is being made now?
3310 @ @
<Process word forms state@
>= {
3312 case '
>'
: @
<Process \.
> line in word forms state@
>; @
+break
;
3313 case '
='
: @
<Process \.
= line in word forms state@
>; @
+break
;
3314 case '#'
: wordform_kind
=buf
[1]; @
+break
;
3315 default
: if
(*buf
>='
0'
&& *buf<='9')
3316 @
<Process numeric line in word forms state@
>;
3320 @ The commands are \.
>, \.
=, and numbers. The command \.
> sets a code for
3321 processing \.
= commands
, and then add to the list.
3323 @
<Process \.
> line in word forms state@
>= {
3324 strcpy
(wordform_code
,buf
+1);
3327 @ @
<Process \.
= line in word forms state@
>= {
3332 execute_program
(wordform_code
);
3333 kind
=pop_num
(); @
+ level
=pop_num
();
3334 dest
=pop_string
(); @
+ orig
=pop_string
();
3335 add_word_form
(kind
,level
,orig
,dest
);
3336 free
(orig
); @
+ free
(dest
);
3339 @ Now the command for numeric forms. You put ``level\.
:orig\.
:dest'' in
3342 @
<Process numeric line in word forms state@
>= {
3343 int level
=strtol
(buf
,&buf,0);
3345 if
(*buf
=='
:'
) buf
++;
3348 add_word_form
(wordform_kind
,level
,buf
,p
+1);
3351 @
*Writing Output Files. Finally
, it will be time to send any output
3352 generated into the files
(if there is any
, which there usually is
).
3356 @d ctrl
(_letter
) (0x1F&(_letter))
3358 @d call_final_subroutine ctrl
('C'
)
3359 @d copy_field ctrl
('F'
)
3360 @d newline ctrl
('J'
)
3361 @d loop_point ctrl
('L'
)
3362 @d next_record ctrl
('N'
)
3363 @d skip_one_character ctrl
('S'
)
3365 @
<Write the output files@
>= {
3368 if
(names.data
[n
].is_output_file
&& names.data[n].value.is_string)
3369 @
<Write this output file@
>;
3373 @ @
<Write this output file@
>= {
3374 FILE*fout
=fopen
(names.data
[n
].name
,"w");
3375 char
*ptr
=names.data
[n
].value.text
;
3376 char
*loopptr
=ptr
; // loop point
3377 if
(!fout
) @
<Error about unable to open output file@
>;
3378 while
(*ptr
) @
<Write the character and advance to the next one@
>;
3382 @ @
<Error about unable to open output file@
>= {
3383 fprintf
(stderr
,"Unable to open output file: %s\n",names.data
[n
].name
);
3384 @.Unable to open output file@
>
3388 @ @
<Write the character and advance to the next one@
>= {
3390 case call_final_subroutine
: @
<Do |call_final_subroutine|@
>; @
+break
;
3391 case copy_field
: @
<Do |copy_field|@
>; @
+break
;
3392 case loop_point
: loopptr
=++ptr
; @
+break
;
3393 case next_record
: @
<Do |next_record|@
>; @
+break
;
3394 case skip_one_character
: ptr
+=2; @
+break
;
3395 default
: fputc
(*ptr
++,fout
);
3397 done_writing_one_character
: ;
3400 @ @
<Do |call_final_subroutine|@
>= {
3403 char
*p
=strchr
(ptr
,'
)'
);
3405 v
=&name_info(find_name(ptr+1)).value;
3409 v
=®isters[*ptr++];
3412 execute_program
(v-
>text
);
3413 @
<Write or loop based on result of subroutine call@
>;
3418 @ @
<Write or loop based on result of subroutine call@
>= {
3419 if
(stack_ptr-
>is_string
) {
3420 fprintf
(fout
,"%s",stack_ptr-
>text
);
3421 } @
+else if
(stack_ptr-
>number
) {
3426 @ This command is used to copy the next field.
3428 Look at the definition for the |send_reg_char_or_text| macro. It is
3429 strange
, but it should work wherever a statement is expected. Please note
3430 that a ternary condition operator should have both choices of the same
3435 @d tok_idx
(registers
['A'
].number
)
3437 (card_areas.data
[name_info
(registers
['C'
].number
).value.number
].tokens
)
3439 @d send_reg_char_or_text
(_reg
)
3440 if
(!registers
[_reg
].is_string ||
*registers
[_reg
].text
)
3441 fprintf
(fout
, "%c%s",
3442 registers
[_reg
].is_string?
3443 *registers
[_reg
].text
:registers
[_reg
].number
,
3444 registers
[_reg
].is_string?
3445 registers
[_reg
].text
+1:(unsigned char
*)""
3448 @
<Do |copy_field|@
>= {
3451 switch
(tok_area
[tok_idx
++]) {
3452 case null_char
: @
<Unexpected |null_char|@
>;
3453 case end_transmission
: tok_idx
=0; @
+goto done_writing_one_character
;
3454 case tabulation
: send_reg_char_or_text
('T'
); @
+break
;
3455 case raw_data
: @
<Do |raw_data|@
>; @
+break
;
3456 case escape_code
: send_reg_char_or_text
('E'
); @
+break
;
3457 case record_separator
: tok_idx--
; @
+goto done_writing_one_character
;
3458 case field_separator
: @
+goto done_writing_one_character
;
3460 if
(tok_area
[--tok_idx
]&~0xFF)
3461 @
<Deal with name code@
>@
;
3463 @
<Deal with normal character@
>;
3469 @ @
<Unexpected |null_char|@
>= {
3470 fprintf
(stderr
,"Unexpected null character found in a card area\n");
3471 @.Unexpected null character...@
>
3475 @ @
<Do |raw_data|@
>= {
3476 while
(tok_area
[tok_idx
]) fputc
(tok_area
[tok_idx
++],fout
);
3480 @ A name code found here is a code to tell it to call the subroutine code
3481 when it is time to write it out to the file. It should return a string on
3482 the stack
(if it is a number
, it will be ignored
).
3484 @
<Deal with name code@
>= {
3485 if
(name_info
(tok_area
[tok_idx
]).value.is_string
)
3486 execute_program
(name_info
(tok_area
[tok_idx
]).value.text
);
3487 if
(stack_ptr-
>is_string
) fprintf
(fout
,"%s",stack_ptr-
>text
);
3491 @ In case of a normal character
, normally just write it out. But some
3492 characters need escaped for \TeX.
3494 @
<Deal with normal character@
>= {
3495 if
(tables
['E'
][tok_area
[tok_idx
]]) send_reg_char_or_text
('E'
);
3496 fputc
(tok_area
[tok_idx
],fout
);
3499 @ This one moves to the next record
, looping if a next record is in fact
3500 available. Otherwise
, just continue. Note that a |record_separator|
3501 immediately followed by a |end_transmission| is assumed to mean there is
3502 no next record
, and that there is allowed to be a optional
3503 |record_separator| just before the |end_transmission|.
3505 @
<Do |next_record|@
>= {
3507 while
(tok_area
[tok_idx
]!=record_separator
&&
3508 tok_area
[tok_idx
]!=end_transmission
) tok_idx
++;
3509 if
(tok_area
[tok_idx
]!=end_transmission
&&
3510 tok_area
[tok_idx
+1]!=end_transmission
) ptr
=loopptr
;
3513 @
*Functions Common to DVI and GF. Numbers for \.
{GF
} and \.
{DVI
} files use
3514 the |dvi_number| data type.
(Change this in the change file if the current
3515 setting is inappropriate for your system.
)
3517 There is also the |dvi_measure| type
, which is twice as long and is used
3518 to compute numbers that can be fractional
(with thirty-two fractional bits
3519 and thirty-two normal bits
).
3522 @q
[Type of DVI numbers
::]@
>
3523 typedef signed int dvi_number
;
3524 typedef signed long long int dvi_measure
;
3525 @q
[::Type of DVI numbers
]@
>
3527 @ There is one subroutine here to read a |dvi_number| from a file. They
3528 come in different sizes and some are signed and some are unsigned.
3533 @
-p dvi_number get_dvi_number
(FILE*fp
,boolean is_signed
,int size
) {
3535 if
(size
) r
=fgetc
(fp
);
3536 if
((r
&0x80) && is_signed) r|=0xFFFFFF00;
3537 while
(--size
) r
=(r
<<8)|fgetc
(fp
);
3541 @ Some macros are defined here in order to deal with |dvi_measure| values.
3545 @d to_measure
(_value
) (((dvi_measure
)(_value
))<<32)
3546 @d floor
(_value
) ((dvi_number
)((_value
)>>32))
3547 @d round
(_value
) ((dvi_number
)(((_value
)+0x8000)>>32))
3548 @d ceiling
(_value
) ((dvi_number
)(((_value
)+0xFFFF)>>32))
3550 @ Here division must be done in a careful way
, to ensure that none of the
3551 intermediate results exceed sixty-four bits.
3553 @d fraction_one to_measure
(1)
3555 @
-p dvi_measure make_fraction
(dvi_measure p
,dvi_measure q
) {
3557 boolean negative
=(p
<0)^
(q
<0);
3561 n
=(n-1
)*fraction_one
;
3562 @
<Compute $f
=\lfloor2^
{32}(1+p
/q
)+{1\over2
}\rfloor$@
>;
3563 return
(f
+n
)*(negative?
-1:1);
3566 @ Notice that the computation specifies $
(p-q
)+p$ instead of $
(p
+p
)-q$
,
3567 because the latter could overflow.
3569 @
<Compute $f
=...@
>= {
3570 register dvi_measure b
;
3572 while
(f
<fraction_one
) {
3583 @ And a few miscellaneous macros.
3585 @d upto4
(_var
,_cmd
) (_var
>=_cmd
&& _var<_cmd+4)
3587 @
*DVI Reading. The device-independent file format is a format invented by
3588 David R.~Fuchs in
1979. The file format need not be explained here
, since
3589 there are other books which explain it\biblio
{Knuth
, Donald. ``\TeX
: The
3590 Program''. Computers
{\char`\
&} Typesetting. ISBN 0-201-13437-3.}\biblio{%
3591 Knuth
, Donald. ``\TeX ware''. Stanford Computer Science Report
1097.
}.
3593 \edef\TeXwareBiblio
{\the\bibliocount
}
3596 @^device independent@
>
3598 At first
, names will be given for the commands in a \.
{DVI
} file.
3600 @d set_char_0
0 // Set a character and move
[up to
127]
3601 @d set1
128 // Take one parameter to set character
[up to
131]
3602 @d set_rule
132 // Set a rule and move down
, two parameters
3603 @d put1
133 // As |set1| but no move
[up to
136]
3604 @d put_rule
137 // As |set_rule| but no move
3605 @d nop
138 // No operation
3606 @d bop
139 // Beginning of a page
3607 @d eop
140 // End of a page
3608 @d push
141 // Push $
(h
,v
,w
,x
,y
,z
)$ to the stack
3609 @d pop
142 // Pop $
(h
,v
,w
,x
,y
,z
)$ from the stack
3610 @d right1
143 // Take one parameter
, move right
[up to
146]
3611 @d w0
147 // Move right $w$ units
3612 @d w1
148 // Set $w$ and move right
[up to
151]
3613 @d x0
152 // Move right $x$ units
3614 @d x1
153 // Set $x$ and move right
[up to
156]
3615 @d down1
157 // Take one parameter
, move down
[up to
160]
3616 @d y0
161 // Move down $y$ units
3617 @d y1
162 // Set $y$ and move down
[up to
165]
3618 @d z0
166 // Move down $z$ units
3619 @d z1
167 // Set $z$ and move down
[up to
170]
3620 @d fnt_num_0
171 // Select font
0 [up to
234]
3621 @d fnt1
235 // Take parameter to select font
[up to
238]
3622 @d xxx1
239 // Specials
[up to
242]
3623 @d fnt_def1
243 // Font definitions
[up to
246]
3624 @d pre
247 // Preamble
3625 @d post
248 // Postamble
3626 @d post_post
249 // Postpostamble
3628 @ We should now start reading the \.
{DVI
} file. Filenames of fonts being
3629 used will be sent to standard output.
3631 @
-p boolean read_dvi_file
(char
*filename
) {
3632 boolean fonts_okay
=1;
3633 FILE*fp
=fopen
(filename
,"rb");
3634 if
(!fp
) dvi_error
(fp
,"Unable to open file");
3635 @#@
<Skip the preamble of the \.
{DVI
} file@
>;
3636 @
<Skip to the next page@
>;
3637 @
<Read the metapage heading@
>;
3638 @
<Compute the conversion factor@
>;
3640 @
<Skip to and read the postamble@
>;
3641 @
<Read the font definitions and load the fonts@
>;
3642 if
(fonts_okay
) @
<Read the pages for each card@
>;
3647 @ @
-p void dvi_error
(FILE*fp
,char
*text
) {
3648 fprintf
(stderr
,"DVI error");
3650 if
(fp
) fprintf
(stderr
," at %08X",ftell
(fp
));
3651 fprintf
(stderr
,": %s\n",text
);
3655 @ Please note the version number of the \.
{DVI
} file must be
2.
3657 @
<Skip the preamble of the \.
{DVI
} file@
>= {
3658 if
(fgetc
(fp
)!=pre
) dvi_error
(fp
,"Bad preamble");
3659 if
(fgetc
(fp
)!=2) dvi_error
(fp
,"Wrong DVI version");
3660 @
<Read the measurement parameters@
>;
3661 @
<Skip the DVI comment@
>;
3664 @ @
<Read the measurement parameters@
>= {
3665 unit_num
=get_dvi_number
(fp
,0,4);
3666 unit_den
=get_dvi_number
(fp
,0,4);
3667 unit_mag
=get_dvi_number
(fp
,0,4);
3670 @ @
<Skip the DVI comment@
>= {
3672 fseek
(fp
,n
,SEEK_CUR
);
3675 @ From the postamble we can read the pointer for the last page.
3677 @
<Global variables@
>=
3678 dvi_number last_page_ptr
;
3680 @ @
<Skip to and read the postamble@
>= {
3681 fseek
(fp
,-4,SEEK_END
);
3682 while
(fgetc
(fp
)==223) fseek
(fp
,-2,SEEK_CUR
);
3683 fseek
(fp
,-5,SEEK_CUR
);
3684 fseek
(fp
,get_dvi_number
(fp
,0,4)+1,SEEK_SET
);
3685 last_page_ptr
=get_dvi_number
(fp
,0,4);
3686 fseek
(fp
,20,SEEK_CUR
); // Skipped parameters of |post|
3687 dvi_stack
=malloc
(get_dvi_number
(fp
,0,2)*sizeof
(dvi_stack_entry
));
3688 fseek
(fp
,2,SEEK_CUR
);
3691 @ Between the preamble and the first page can be |nop| commands and font
3692 definitions
, so these will be skipped. The same things can occur between
3693 the end of one page and the beginning of the next page.
3695 @
<Skip to the next page@
>= {
3700 if
(c
>=fnt_def1
&& c<fnt_def1+4) {
3701 @
<Skip a font definition@
>;
3702 } @
+else if
(c
!=nop
) {
3703 dvi_error
(fp
,"Bad command between pages");
3708 @ @
<Skip a font definition@
>= {
3710 fseek
(fp
,c
+13-fnt_def1
,SEEK_CUR
);
3713 fseek
(fp
,a
+l
,SEEK_CUR
);
3716 @ The metapage includes the resolution and other things which must be set
,
3717 such as subroutine codes. The resolution must be read before fonts can be
3718 read. Please note that no characters can be typeset on the metapage
, since
3719 fonts have not been loaded yet. You can still place empty boxes. The DPI
3720 setting
(resolution
) is read from the \.
{\\count1
} register.
3722 @
<Read the metapage heading@
>= {
3723 dvi_number n
=get_dvi_number
(fp
,0,4);
3725 fprintf
(stderr
,"Metapage must be numbered zero (found %d).\n",n
);
3726 @.Metapage must be...@
>
3729 push_num
(get_dvi_number
(fp
,0,4)); @
+ store
('D'
);
3730 fseek
(fp
,9*4,SEEK_CUR
); // Skip other parameters
3731 layer_width
=layer_height
=0;
3734 @ A stack is kept of the page registers
, for use with the |push| and |pop|
3735 commands of a \.
{DVI
} file. This stack is used by the |read_dvi_page|
3736 subroutine and stores the |quan| registers
(described in the next
3751 @ @
<Global variables@
>=
3752 dvi_stack_entry
*dvi_stack
;
3753 dvi_stack_entry
*dvi_stack_ptr
;
3755 @ Here is the subroutine to read commands from a DVI page. The file
3756 position should be at the beginning of the page after the |bop| command.
3760 @
-p void read_dvi_page
(FILE*fp
) {
3761 memusage_log
("Beginning of page",fseek
(fp
));
3762 @
<Reset the page registers and stack@
>;
3764 @
<Read the commands of this page@
>;
3765 if
(layer_width
&& layer_height) @<Render this page@>;
3768 @ @
<Reset the page registers and stack@
>= {
3769 quan
('A'
)=quan
('B'
)=quan
('H'
)=quan
('I'
)=quan
('J'
)=quan
('L'
)=quan
('V'
)=
3770 quan
('W'
)=quan
('
X'
)=quan
('
Y'
)=quan
('Z'
)=0;
3771 dvi_stack_ptr
=dvi_stack
;
3774 @ @
<Read the commands of this page@
>= {
3776 boolean moveaftertyping
;
3781 @
<Typeset character |c| on the current layer@
>;
3782 } @
+else if
(upto4
(c
,set1
)) {
3784 c
=get_dvi_number
(fp
,0,c
+1-set1
);
3785 @
<Typeset character |c| on the current layer@
>;
3786 } @
+else if
(c
==set_rule || c
==put_rule
) {
3787 moveaftertyping
=(c
==set_rule
);
3788 c
=get_dvi_number
(fp
,1,4);
3789 a
=get_dvi_number
(fp
,1,4);
3790 @
<Typeset |a| by |c| rule on the current layer@
>;
3791 } @
+else if
(upto4
(c
,put1
)) {
3793 c
=get_dvi_number
(fp
,0,c
+1-put1
);
3794 @
<Typeset character |c| on the current layer@
>;
3795 } @
+else if
(c
==eop
) {
3797 } @
+else if
(c
==push
) {
3798 if
(dvi_stack
) @
<Push DVI registers to stack@
>;
3799 } @
+else if
(c
==pop
) {
3800 if
(dvi_stack
) @
<Pop DVI registers from stack@
>;
3801 } @
+else if
(upto4
(c
,right1
)) {
3802 c
=get_dvi_number
(fp
,1,c
+1-right1
);
3803 horizontal_movement
(c
);
3804 } @
+else if
(c
==w0
) {
3805 horizontal_movement
(quan
('W'
));
3806 } @
+else if
(upto4
(c
,w1
)) {
3807 c
=get_dvi_number
(fp
,1,c
+1-w1
);
3808 horizontal_movement
(quan
('W'
)=c
);
3809 } @
+else if
(c
==x0
) {
3810 horizontal_movement
(quan
('
X'
));
3811 } @
+else if
(upto4
(c
,x1
)) {
3812 c
=get_dvi_number
(fp
,1,c
+1-x1
);
3813 horizontal_movement
(quan
('
X'
)=c
);
3814 } @
+else if
(upto4
(c
,down1
)) {
3815 c
=get_dvi_number
(fp
,1,c
+1-down1
);
3816 vertical_movement
(c
);
3817 } @
+else if
(c
==y0
) {
3818 vertical_movement
(quan
('
Y'
));
3819 } @
+else if
(upto4
(c
,y1
)) {
3820 c
=get_dvi_number
(fp
,1,c
+1-y1
);
3821 vertical_movement
(quan
('
Y'
)=c
);
3822 } @
+else if
(c
==z0
) {
3823 vertical_movement
(quan
('Z'
));
3824 } @
+else if
(upto4
(c
,z1
)) {
3825 c
=get_dvi_number
(fp
,1,c
+1-z1
);
3826 vertical_movement
(quan
('Z'
)=c
);
3827 } @
+else if
(c
>=fnt_num_0
&& c<fnt1) {
3828 quan
('F'
)=c-fnt_num_0
;
3829 } @
+else if
(upto4
(c
,fnt1
)) {
3830 quan
('F'
)=get_dvi_number
(fp
,0,c
+1-fnt1
);
3831 } @
+else if
(upto4
(c
,xxx1
)) {
3832 c
=get_dvi_number
(fp
,0,c
+1-xxx1
);
3833 @
<Read a special of length |c|@
>;
3834 } @
+else if
(upto4
(c
,fnt_def1
)) {
3835 @
<Skip a font definition@
>;
3836 } @
+else if
(c
!=nop
) {
3837 dvi_error
(fp
,"Unknown DVI command");
3842 @ @
<Push DVI registers to stack@
>= {
3843 dvi_stack_ptr-
>h
=quan
('H'
);
3844 dvi_stack_ptr-
>v
=quan
('V'
);
3845 dvi_stack_ptr-
>w
=quan
('W'
);
3846 dvi_stack_ptr-
>x
=quan
('
X'
);
3847 dvi_stack_ptr-
>y
=quan
('
Y'
);
3848 dvi_stack_ptr-
>z
=quan
('Z'
);
3849 dvi_stack_ptr-
>hh
=quan
('I'
);
3850 dvi_stack_ptr-
>vv
=quan
('J'
);
3854 @ @
<Pop DVI registers from stack@
>= {
3856 quan
('H'
)=dvi_stack_ptr-
>h
;
3857 quan
('V'
)=dvi_stack_ptr-
>v
;
3858 quan
('W'
)=dvi_stack_ptr-
>w
;
3859 quan
('
X'
)=dvi_stack_ptr-
>x
;
3860 quan
('
Y'
)=dvi_stack_ptr-
>y
;
3861 quan
('Z'
)=dvi_stack_ptr-
>z
;
3862 quan
('I'
)=dvi_stack_ptr-
>hh
;
3863 quan
('J'
)=dvi_stack_ptr-
>vv
;
3866 @ A special in \TeX nicard is used to execute a special code while reading
3867 the DVI file. Uses might be additional calculations
, changes of registers
,
3868 special effects
, layer selection
, etc. All of these possible commands are
3869 dealt with elsewhere in this program. All we do here is to read it and to
3870 send it to the |execute_program| subroutine.
3874 @
<Read a special of length |c|@
>= {
3875 char
*buf
=malloc
(c
+1);
3878 @
<Set \.
X and \.
Y registers to prepare for the special@
>;
3879 execute_program
(buf
);
3883 @ @
<Set \.
X and \.
Y registers to prepare for the special@
>= {
3884 registers
['
X'
].is_string
=registers
['
Y'
].is_string
=0;
3885 registers
['
X'
].number
=quan
('I'
);
3886 registers
['
Y'
].number
=quan
('J'
);
3889 @ In order to read all the pages for each card
, we can skip backwards by
3890 using the back pointers. Either we will print all cards
(in reverse
3891 order
), or we will print cards listed on the command-line
, or we will
3892 print cards listed in a file
(this last way might be used to print decks
3895 Card numbers should be one-based
, and should not be negative. Any pages
3896 with negative page numbers will be ignored when it is in the mode for
3899 @d printing_all_cards
0
3901 @d printing_list_from_file
2
3903 @
<Global variables@
>=
3904 unsigned char printing_mode
;
3908 @ @
<Read the pages for each card@
>= {
3909 dvi_number page_ptr
=last_page_ptr
;
3910 dvi_number e
=0,n
; // page numbers
3911 boolean pagenotfound
=0;
3913 @
<Read the next entry from the list of pages
(if applicable
)@
>;
3915 @
<Seek the next page to print@
>;
3916 @
<Read the heading for this page@
>;
3917 @
<If this page shouldn't be printed now
, |goto try_next_page|@
>;
3924 @ @
<Read the next entry from the list of pages
(if applicable
)@
>= {
3925 if
(printing_mode
==printing_list
) {
3926 if
(!*printlisttext
) goto done_printing
;
3927 e
=strtol
(printlisttext
,&printlisttext,10);
3928 if
(!e
) goto done_printing
;
3929 if
(*printlisttext
) printlisttext
++;
3930 } @
+else if
(printing_mode
==printing_list_from_file
) {
3932 if
(!printlistfile || feof
(printlistfile
)) goto done_printing
;
3933 if
(!fgets
(buf
,255,printlistfile
)) goto done_printing
;
3938 @ @
<Seek the next page to print@
>= {
3941 fprintf
(stderr
,"No page found: %d\n",e
);
3942 @.No page found...@
>
3945 page_ptr
=last_page_ptr
;
3946 if
(printing_mode
==printing_all_cards
) goto done_printing
;
3949 fseek
(fp
,page_ptr
+1,SEEK_SET
);
3952 @ @
<Read the heading for this page@
>= {
3953 n
=quan
('P'
)=get_dvi_number
(fp
,1,4);
3954 fseek
(fp
,4,SEEK_CUR
);
3955 layer_width
=get_dvi_number
(fp
,1,4);
3956 layer_height
=get_dvi_number
(fp
,1,4);
3957 fseek
(fp
,4*6,SEEK_CUR
);
3958 page_ptr
=get_dvi_number
(fp
,1,4);
3961 @ @
<If this page shouldn't be printed now
, |goto try_next_page|@
>= {
3962 if
(n
<=0 && printing_mode==printing_all_cards) goto try_next_page;
3963 if
(n
!=e
&& printing_mode!=printing_all_cards) goto try_next_page;
3966 @
*DVI Font Metrics. Here
, the fonts are loaded. It is assumed all fonts
3967 are in the current directory
, and the ``area'' of the font name is
3968 ignored. The checksum will also be ignored
(it can be checked with
3969 external programs if necessary
).
3974 @
<Read the font definitions and load the fonts@
>= {
3978 if
(c
==post_post
) break
;
3979 if
(c
>=fnt_def1
&& c<fnt_def1+4) {
3980 int k
=get_dvi_number
(fp
,0,c
+1-fnt_def1
);
3981 if
(k
&~0xFF) dvi_error(fp,"Too many fonts");
3982 memusage_log
("Loading font",k
);
3983 @
<Read the definition for font |k| and load it@
>;
3984 } @
+else if
(c
!=nop
) {
3985 dvi_error
(fp
,"Bad command in postamble");
3988 memusage_log
("End of postamble",c
);
3991 @ When reading fonts
, it will be necessary to keep a list of the fonts
3992 and their character indices. Only
256 fonts are permitted in one job.
3994 @
<Global variables@
>=
3995 data_index fontindex
[256];
3997 @ @
<Read the definition for font |k| and load it@
>= {
3998 dvi_number c
=get_dvi_number
(fp
,0,4); // checksum
(unused
)
3999 dvi_number s
=get_dvi_number
(fp
,0,4); // scale factor
4000 dvi_number d
=get_dvi_number
(fp
,0,4); // design size
4001 int a
=get_dvi_number
(fp
,0,1); // length of area
4002 int l
=get_dvi_number
(fp
,0,1); // length of name
4004 fseek
(fp
,a
,SEEK_CUR
);
4007 if
((fontindex
[k
]=read_gf_file
(n
,s
,d
))==none
) fonts_okay
=0;
4010 @ An important part of reading the font metrics is the width computation
,
4011 which involves multiplying the relative widths in the \.
{TFM
} file
(or
4012 \.
{GF
} file
) by the scaling factor in the \.
{DVI
} file. This
4013 multiplication must be done in precisely the same way by all \.
{DVI
}
4014 reading programs
, in order to validate the assumptions made by \.
{DVI
}-%
4015 writing programs such as \TeX.
4017 % (The following paragraph is taken directly from
"dvitype.web")
4018 Let us therefore summarize what needs to be done. Each width in a \.
{TFM
}
4019 file appears as a four-byte quantity called a |fix_word|. A |fix_word|
4020 whose respective bytes are $
(a
,b
,c
,d
)$ represents the number
4021 $$x
=\left\
{\vcenter
{\halign
{$#$
,\hfil\qquad
&if $#$\hfil\cr
4022 b\cdot2^
{-4}+c\cdot2^
{-12}+d\cdot2^
{-20}&a=0;\cr
4023 -16+b\cdot2^
{-4}+c\cdot2^
{-12}+d\cdot2^
{-20}&a=255.\cr}}\right.$$
4024 (No other choices of $a$ are allowed
, since the magnitude of a \.
{TFM
}
4025 dimension must be less than
16.
) We want to multiply this quantity by the
4026 integer~|z|
, which is known to be less than $
2^
{27}$.
4027 If $|z|
<2^
{23}$
, the individual multiplications $b\cdot z$
, $c\cdot z$
,
4028 $d\cdot z$ cannot overflow
; otherwise we will divide |z| by
2, 4, 8, or
4029 16, to obtain a multiplier less than $
2^
{23}$
, and we can compensate for
4030 this later. If |z| has thereby been replaced by $|z|^\prime
=|z|
/2^e$
, let
4031 $\beta
=2^
{4-e
}$
; we shall compute
4032 $$\lfloor
(b
+c\cdot2^
{-8}+d\cdot2^
{-16})\
,z^\prime
/\beta\rfloor$$ if $a
=0$
,
4033 or the same quantity minus $\alpha
=2^
{4+e
}z^\prime$ if $a
=255$.
4034 This calculation must be
4035 done exactly
, for the reasons stated above
; the following program does the
4036 job in a system-independent way
, assuming that arithmetic is exact on
4037 numbers less than $
2^
{31}$ in magnitude.
4045 @
<Compute |zprime|
, |alpha|
, and |beta|@
>= {
4046 zprime
=s
; @
+ alpha
=16;
4047 while
(zprime
>=040000000) {
4048 zprime
>>=1; @
+ alpha
<<=1;
4050 beta
=256/alpha
; @
+ alpha
*=zprime
;
4053 @ @
<Compute the character width |w|@
>= {
4054 w
=(((((b3
*zprime
)>>8)+(b2
*zprime
))>>8)+(b1
*zprime
))/beta
;
4058 @
*GF Reading. The \.
{GF
} format is a ``generic font'' format. It has a lot
4059 in common with \.
{DVI
} format.
4061 At first
, names will be given for the commands in a \.
{GF
} file. Many
4062 commands have the same numbers as they do in a \.
{DVI
} file
(described in
4063 the previous chapter
), which makes it very convenient\biblio
{This is
4064 probably on purpose for the this very reason
, so that a WEB or CWEB
4065 program can use one set of named constants for reading both files.
}.
4067 @d paint_0
0 // Paint $d$ pixels black or white
[up to
63]
4068 @d paint1
64 // Take parameter
, paint pixels
[up to
66]
4069 @d boc
67 // Beginning of a character picture
4070 @d boc1
68 // Short form of |boc|
4071 @d eoc
69 // End of a character picture
4072 @d skip0
70 // Skip some rows
4073 @d skip1
71 // Skip some rows
[up to
73]
4074 @d new_row_0
74 // Start a new row and move right
[up to
238]
4075 @d yyy
243 // Numeric specials
4076 @d no_op
244 // No operation
4077 @d char_loc
245 // Character locator
4078 @d char_loc0
246 // Short form of |char_loc|
4080 @ The |font_struct| structure stores the information for each character in
4081 a font. The |raster| field points to a bitmap with eight pixels per octet
,
4082 most significant bit for the leftmost pixel
, each row always padded to a
4083 multiple of eight pixels.
4085 While it is reading the postamble
, it will fill in this structure with the
4086 |ptr| field set. After the postamble is read
, it will fill in the other
4087 fields belonging to its union.
4091 dvi_number dx
; // character escapement in pixels
4092 dvi_number w
; // width in DVI units
4095 dvi_number min_n
,max_n
,min_m
,max_m
; // bounding box
(in pixels
)
4096 unsigned short n
; // character code number
4097 unsigned char
*raster
;
4098 unsigned char flag
; // bitfield of flags for this character
4105 @ List of flags follows. Some of these flags might be used in order to
4106 allow$\mathord
{}>256$ characters per font
, since
{\TeX
} does not have a
4107 command to enter characters with codes more than one byte long. These
4108 flags are specified using numeric specials.
4110 @d ff_select
0x01 // set high octet all characters
4111 @d ff_prefix
0x02 // set high octet for codes
128-255
4112 @d ff_roundafter
0x04 // round $\it hh$ after sending character
4113 @d ff_roundbefore
0x08 // round $\it hh$ before sending character
4114 @d ff_reset
0x10 // reset high octet
4115 @d ff_strip
0x20 // strip highest bit of prefix
4116 @d ff_space
0x40 // do not save the raster
(space only
)
4117 @d ff_reserved
0x80 // {\bf
DO NOT USE}
4119 @ @
<Global variables@
>=
4120 memory_of
(font_struct
) font_data
;
4122 @ @
<Initialize memory@
>= init_memory
(font_data
,4);
4124 @ When loading a \.
{GF
} font
, the filename will contain the resolution
4129 @
-p data_index read_gf_file
(char
*fontname
,dvi_measure s
,dvi_measure d
) {
4130 unsigned int dpi
=(resolution
*unit_mag
*s
+500*d
)/(100000*d
);
4132 data_index index
=none
;
4133 data_index first_index
=none
;
4134 data_index last_index
=none
;
4135 dvi_number zprime
,alpha
,beta
; // used for width computation
4136 @
<Compute |zprime|
, |alpha|
, and |beta|@
>;
4137 @
<Figure out the filename and open the file
, |return none| if it can't@
>;
4138 @
<Skip to the postamble of the \.
{GF
} file@
>;
4139 @
<Read the character locators@
>;
4140 @
<Read the character rasters and flags@
>;
4145 @ When figuring out the filename
, it will send it to standard output so
4146 that a list can be made of the required fonts.
4148 @
<Figure out the filename and open the file
, ...@
>= {
4150 sprintf
(n
,"%s.%dgf",fontname
,dpi
);
4152 fp
=open_file
(n
,"rb");
4153 if
(!fp
) return none
;
4156 @ @
<Skip to the postamble of the \.
{GF
} file@
>= {
4158 fseek
(fp
,-4,SEEK_END
);
4159 while
(fgetc
(fp
)==223) fseek
(fp
,-2,SEEK_CUR
);
4160 fseek
(fp
,-5,SEEK_CUR
);
4161 fseek
(fp
,get_dvi_number
(fp
,0,4)+37,SEEK_SET
); // nothing matters anymore
4164 @ @
<Read the character locators@
>= {
4169 if
(c
==post_post
) break
;
4172 @
<Read a long character locator@
>;
4173 } @
+else if
(c
==char_loc0
) {
4174 @
<Read a short character locator@
>;
4175 } @
+else if
(c
!=no_op
) {
4176 fprintf
(stderr
,"Bad command in GF postamble.\n");
4177 @.Bad command in GF postamble@
>
4178 fprintf
(stderr
,"(Command %d, address %08X)\n",c
,ftell
(fp
)-1);
4181 if
(p
!=-1) @
<Defer this character locator into |font_data|@
>;
4186 @ There are some parameters we do not care about. First is $c$
, which is
4187 the character code residue
(modulo
256). This is not important since it
4188 is duplicated in the |boc| heading for each character. The second
4189 parameter which we do not care about is the $\it dy$ parameter
, since it
4190 should be zero for \.
{DVI
} files.
4192 @
<Read a long character locator@
>= {
4193 fseek
(fp
,1,SEEK_CUR
);
4194 dx
=get_dvi_number
(fp
,1,4)>>16;
4195 fseek
(fp
,4,SEEK_CUR
);
4196 @
<Read four bytes@
>;
4197 p
=get_dvi_number
(fp
,1,4);
4200 @ @
<Read a short character locator@
>= {
4201 fseek
(fp
,1,SEEK_CUR
);
4202 dx
=get_dvi_number
(fp
,0,1);
4203 @
<Read four bytes@
>;
4204 p
=get_dvi_number
(fp
,1,4);
4207 @ @
<Read four bytes@
>= {
4208 b0
=fgetc
(fp
);@
+b1
=fgetc
(fp
);@
+b2
=fgetc
(fp
);@
+b3
=fgetc
(fp
);
4211 @ This processing is deferred
, and the rest of the parameters will be
4212 filled in later
(and the |ptr| field will be overwritten since it will
4213 no longer be needed at that time
).
4215 @
<Defer this character locator into |font_data|@
>= {
4216 data_index n
=new_record
(font_data
);
4217 @
<Compute the character width |w|@
>;
4218 font_data.data
[n
].next
=index
;
4219 font_data.data
[n
].dx
=dx
;
4220 font_data.data
[n
].w
=w
;
4221 font_data.data
[n
].ptr
=p
;
4222 if
(index
==none
) first_index
=n
;
4226 @ Now is time to go through the list we made up and this time actually
4227 fill in the parameters and pictures.
4229 @
<Read the character rasters and flags@
>= {
4230 while
(index
!=none
) {
4231 fseek
(fp
,font_data.data
[index
].ptr
,SEEK_SET
);
4232 font_data.data
[index
].flag
=0;
4233 font_data.data
[index
].raster
=0;
4234 @
<Read commands for this character@
>;
4235 @#index
=font_data.data
[index
].next
;
4239 @ Painting the picture uses the value of |paint_switch| to determine
4240 to draw or skip. The current position in the array |raster| is also
4241 pointed by the |pic| pointer. Note that |black| and |white| are not
4242 necessary black and white
(but they are on normal paper
).
4244 Note the value of $n$ is not needed since the |pic| pointer automatically
4245 keeps track of this kinds of stuff. However
, |m| is needed because of
4246 commands that can skip rows
, to know how many columns must be skipped to
4247 reach the next row. There is also |b|
, which keeps track of the bit
4248 position in the current byte.
4254 m
=(font_data.data
[index
].max_m-font_data.data
[index
].min_m
)/8+1@
;
4256 @
<Read commands for this character@
>= {
4259 boolean paint_switch
;
4263 @
<Paint |c| pixels |black| or |white|@
>;
4264 } @
+else if
(c
>=paint1
&& c<paint1+3) {
4265 c
=get_dvi_number
(fp
,0,c
+1-paint1
);
4266 @
<Paint |c| pixels |black| or |white|@
>;
4267 } @
+else if
(c
==boc
) {
4268 @
<Initialize parameters and picture
(long form
)@
>;
4269 } @
+else if
(c
==boc1
) {
4270 @
<Initialize parameters and picture
(short form
)@
>;
4271 } @
+else if
(c
==eoc
) {
4272 break
; // Well Done
!
4273 } @
+else if
(upto4
(c
,skip0
)) {
4275 else c
=get_dvi_number
(fp
,0,c
+1-skip1
);
4276 @
<Finish a row and skip |c| rows@
>;
4277 } @
+else if
(c
>=new_row_0
&& c<=new_row_0+164) {
4279 @
<Finish a row and skip |c| columns@
>;
4280 } @
+else if
(c
==yyy
) {
4281 font_data.data
[index
].flag|
=get_dvi_number
(fp
,0,4)>>16;
4282 } @
+else if
(c
!=no_op
) {
4283 fprintf
(stderr
,"Unknown GF command!\n");
4284 @.Unknown GF command@
>
4285 fprintf
(stderr
,"(Command %d, address %08X)\n",c
,ftell
(fp
)-1);
4290 @ Actually |m| is something a bit different than the standard
, because |m|
4291 now tells how many bytes are remaining in the current row.
4293 @d pic_rows
(1+font_data.data
[index
].max_n-font_data.data
[index
].min_n
)
4295 @
<Initialize parameters and picture
(long form
)@
>= {
4296 font_data.data
[index
].n
=get_dvi_number
(fp
,0,4);
4297 @
<Deal with $p$
(pointer to previous character with same metrics
)@
>;
4298 font_data.data
[index
].min_m
=get_dvi_number
(fp
,1,4);
4299 font_data.data
[index
].max_m
=get_dvi_number
(fp
,1,4);
4300 font_data.data
[index
].min_n
=get_dvi_number
(fp
,1,4);
4301 font_data.data
[index
].max_n
=get_dvi_number
(fp
,1,4);
4302 @
<Initialize picture@
>;
4305 @ @
<Initialize picture@
>= {
4306 if
(font_data.data
[index
].flag
&ff_space) break;
4310 pic
=font_data.data
[index
].raster
=malloc
(m
*pic_rows
+1);
4311 memset
(pic
,0,m
*pic_rows
);
4314 @ @
<Initialize parameters and picture
(short form
)@
>= {
4316 font_data.data
[index
].n
=get_dvi_number
(fp
,0,1);
4317 d
=get_dvi_number
(fp
,0,1);
4318 font_data.data
[index
].max_m
=get_dvi_number
(fp
,0,1);
4319 font_data.data
[index
].min_m
=font_data.data
[index
].max_m-d
;
4320 d
=get_dvi_number
(fp
,0,1);
4321 font_data.data
[index
].max_n
=get_dvi_number
(fp
,0,1);
4322 font_data.data
[index
].min_n
=font_data.data
[index
].max_n-d
;
4323 @
<Initialize picture@
>;
4326 @ The pointers to other characters will also be deferred in the same way
4327 as the character locators
, but this time from the other end. Now
, once it
4328 is finished all the characters
, it will
{\sl automatically
} know to read
4329 the next one properly
! (Now you can see what the purpose of the
4330 |@
!first_index| variable is.
)
4332 @
<Deal with $p$
(pointer to previous character with same metrics
)@
>= {
4333 dvi_number p
=get_dvi_number
(fp
,1,4);
4335 data_index i
=new_record
(font_data
);
4336 font_data.data
[i
].next
=none
;
4337 font_data.data
[i
].dx
=font_data.data
[index
].dx
;
4338 font_data.data
[i
].w
=font_data.data
[index
].w
;
4339 font_data.data
[i
].ptr
=p
;
4340 font_data.data
[first_index
].next
=i
;
4345 @ Now we get to the actual painting. We can assume the value of |m| is
4346 never negative and that everything else is also okay.
4348 @
<Paint |c| pixels |black| or |white|@
>= {
4351 @
<Paint a small block of pixels in the current byte@
>;
4353 @
<Paint the rest of the pixels in the current byte@
>;
4354 @
<Fill up the bytes in the middle@
>;
4355 @
<Clear the pixels needed clearing at the end@
>;
4358 @
<Update |paint_switch|
, |pic|
, |b|
, and |m|@
>;
4361 @ @
<Update |paint_switch|
, |pic|
, |b|
, and |m|@
>= {
4369 @ @
<Paint a small block of pixels in the current byte@
>= {
4370 *pic|
=(0xFF>>b
)&~(0xFF>>(b+c));
4373 @ @
<Paint the rest of the pixels in the current byte@
>= {
4377 @ @
<Fill up the bytes in the middle@
>= {
4378 memset
(pic
+1,0xFF,(c
+b
)>>3);
4381 @ @
<Clear the pixels needed clearing at the end@
>= {
4382 pic
[(c
+b
)>>3]&=~(0xFF>>((c+b)&7));
4385 @ @
<Finish a row and skip |c| rows@
>= {
4393 @ @
<Finish a row and skip |c| columns@
>= {
4402 @ @
<Display font information@
>= {
4404 foreach
(i
,font_data
) {
4405 printf
("[%d] box=(%d,%d,%d,%d) dx=%d w=%d n=%d flag=%d [%d]\n"
4406 ,i
,font_data.data
[i
].min_n
,font_data.data
[i
].max_n
4407 ,font_data.data
[i
].min_m
,font_data.data
[i
].max_m
4408 ,font_data.data
[i
].dx
,font_data.data
[i
].w
,font_data.data
[i
].n
4409 ,font_data.data
[i
].flag
,font_data.data
[i
].next
4414 @
*Layer Computation. Now is the chapter for actually deciding rendering on
4415 the page
, where everything should go
, etc.$^
{[\TeXwareBiblio
]}$
4417 @
<Global variables@
>=
4418 dvi_measure unit_num
; // Numerator for units of measurement
4419 dvi_measure unit_den
; // Denominator for units of measurement
4420 dvi_measure unit_mag
; // Magnification for measurement
4421 dvi_measure unit_conv
; // Conversion factor
4423 @ There are also a number of ``internal typesetting quantities''. These
4424 are parameters stored in a separate array
, and are used to keep track of
4425 the current state of the typesetting. They are labeled with letters from
4426 \.A to \.Z. They can be modified inside of specials
, although some of them
4427 probably shouldn't be modified in this way. Here is the list of them
:
4429 \.A
, \.B
: Horizontal and vertical offset added to \.I and \.J.
4431 \.C
: Character code prefix. If bit eight is not set
, it only affects
4432 character codes with bit seven set.
4434 \.D
: Maximum horizontal drift
(in pixels
), meaning how far away the \.I
4435 and \.J parameters are allowed to be from the correctly rounded values.
4437 \.E
: Maximum vertical drift.
4439 \.F
: The current font.
4441 \.H
: The horizontal position on the page
, in DVI units.
4443 \.I
: The horizontal position on the page
, in pixels.
4445 \.J
: The vertical position on the page
, in pixels.
4447 \.L
: The current layer number. If this is zero
, nothing is placed on the
4448 page
, although the positions can still be changed and specials can still
4451 \.P
: Page number. This is used to determine the filename of output.
4453 \.R
, \.S
: The limits for when horizontal motion should add the number of
4454 pixels or when it should recalculate the pixels entirely.
4456 \.T
, \.U
: Like \.R and \.S
, but for vertical motions.
4458 \.V
: The vertical position on the page
, in DVI units.
4460 \.W
, \.
X, \.
Y, \.Z
: The current spacing amounts
, in DVI units.
4462 @d quan
(_name
) (type_quan
[(_name
)&0x1F])
4464 @
<Global variables@
>=
4465 dvi_number type_quan
[32];
4467 @ @
<Cases for system commands@
>=
4469 // Modify an internal typesetting quantity
4470 if
(stack_ptr-
>is_string
) program_error
("Type mismatch");
4471 quan
(*++ptr
)=pop_num
();
4475 @ The conversion factor |unit_conv| is figured as follows
: There are
4476 exactly |unit_num
/unit_den| decimicrons per DVI unit
, and
254000
4477 decimicrons per inch
, and |resolution
/100| pixels per inch. Then we have
4478 to adjust this by the magnification |unit_mag|.
4480 Division must be done slightly carefully to avoid overflow.
4482 @d resolution
(registers
['D'
].number
)
4484 @
<Compute the conversion factor@
>= {
4485 unit_conv
=make_fraction
(unit_num
*resolution
*unit_mag
,unit_den
*100000);
4489 @ Here are the codes to compute movements. The definition of \.
{DVI
} files
4490 refers to six registers which hold integer values in DVI units. However
,
4491 we also have two more registers
, for horizontal and vertical pixel units.
4493 A sequence of characters or rules might cause the pixel values to drift
4494 from their correctly rounded values
, since they are not usually an exact
4495 integer number of pixels.
4497 @d to_pixels
(_val
) round
((_val
)*unit_conv
)
4499 @
-p void horizontal_movement
(dvi_number x
) {
4501 if
(x
>quan
('S'
) || x
<quan
('R'
)) {
4502 quan
('I'
)=to_pixels
(quan
('H'
));
4504 quan
('I'
)+=to_pixels
(x
);
4505 if
(to_pixels
(quan
('H'
))-quan
('I'
)>quan
('D'
))
4506 quan
('I'
)=to_pixels
(quan
('H'
))+quan
('D'
);
4507 if
(to_pixels
(quan
('H'
))-quan
('I'
)<-quan
('D'
))
4508 quan
('I'
)=to_pixels
(quan
('H'
))-quan
('D'
);
4512 @ @
-p void vertical_movement
(dvi_number x
) {
4514 if
(x
>quan
('U'
) || x
<quan
('T'
)) {
4515 quan
('J'
)=to_pixels
(quan
('V'
));
4517 quan
('J'
)+=to_pixels
(x
);
4518 if
(to_pixels
(quan
('V'
))-quan
('J'
)>quan
('E'
))
4519 quan
('J'
)=to_pixels
(quan
('V'
))+quan
('E'
);
4520 if
(to_pixels
(quan
('V'
))-quan
('J'
)<-quan
('E'
))
4521 quan
('J'
)=to_pixels
(quan
('V'
))-quan
('E'
);
4525 @ This is now the part that does actual sending. When many characters
4526 come next to each other
, the rounding will be done such that the number
4527 of pixels between two letters will always be the same whenever those two
4528 letters occur next to each other.
4530 @
<Typeset character |c| on the current layer@
>= {
4531 data_index n
=fontindex
[quan
('F'
)&0xFF];
4532 if
((quan
('C'
)&0x100) || (c&0x80)) c|=quan('C')<<8;
4533 while
(n
!=none
&& c!=font_data.data[n].n)
4534 n
=font_data.data
[n
].next
;
4535 if
(n
==none
) dvi_error
(fp
,"Character not in font");
4536 @
<Typeset the character and update the current position@
>;
4537 @
<Update the character code prefix@
>;
4540 @ @
<Typeset the character and update the current position@
>= {
4541 if
(font_data.data
[n
].flag
&ff_roundbefore)
4542 quan
('I'
)=to_pixels
(quan
('H'
));
4543 if
(quan
('L'
) && font_data.data[n].raster) typeset_char_here(n);
4544 if
(moveaftertyping
) {
4545 quan
('H'
)+=font_data.data
[n
].w
;
4546 quan
('I'
)+=font_data.data
[n
].dx
;
4547 if
(font_data.data
[n
].flag
&ff_roundafter)
4548 quan
('I'
)=to_pixels
(quan
('H'
));
4549 else horizontal_movement
(0);
4553 @ If you have a typesetting program that can ship out characters with
4554 codes more than eight bits long
, you won't need this. It is provided for
4555 use with normal
{\TeX
} system.
4557 @
<Update the character code prefix@
>= {
4558 if
(font_data.data
[n
].flag
&ff_strip) c&=0x7F; else c&=0xFF;
4559 if
(font_data.data
[n
].flag
&ff_select) quan('C')=c|0x100;
4560 if
(font_data.data
[n
].flag
&ff_prefix) quan('C')=c;
4561 if
(font_data.data
[n
].flag
&ff_reset) quan('C')=0;
4564 @ The number of pixels in the height or width of a rule will always be
4565 rounded up. However
, unlike DVItype
, this program has no floating point
4568 @d to_rule_pixels
(_val
) ceiling
((_val
)*unit_conv
)
4570 @
<Typeset |a| by |c| rule on the current layer@
>= {
4571 dvi_number x
=to_rule_pixels
(a
);
4572 dvi_number y
=to_rule_pixels
(c
);
4573 if
(quan
('L'
) && a>0 && c>0) typeset_rule_here(x,y);
4574 if
(moveaftertyping
) {
4576 horizontal_movement
(0);
4580 @ Sometimes you might want DVI units converted to pixels inside of a user
4581 program contained in a DVI file. Here is how it is done.
4583 @
<Cases for system commands@
>=
4585 // Convert DVI units to pixels
4586 if
(stack_ptr-
>is_string
) program_error
("Type mismatch");
4587 stack_ptr-
>number
=to_pixels
(stack_ptr-
>number
);
4591 @
*Layer Rendering. Please note
, these numbers are |short|
, which means
4592 that you cannot have more than
65536 pixels in width or in height. This
4593 should not be a problem
, because even if you have
3000 dots per inch
, and
4594 each card is
10 inches long
, that is still only
30000 which is less than
4595 half of the available width.
(All units here are in pixels.
)
4597 In order to save memory
, all typeset nodes are stored in one list at
4598 first
, and then rendered to a pixel buffer as each layer is being written
4599 out to the \.
{PBM
} file
, and then the buffer can be freed
(or reset to
4600 zero
) afterwards to save memory.
4604 unsigned short x
; // X position on page
4605 unsigned short y
; // Y position on page
4608 unsigned short w
; // Width of rule
4609 unsigned short h
; // Height of rule
4611 data_index c
; // Character index in |font_data|
4613 unsigned char l
; // Layer
(high bit set for rules
)
4616 @ @
<Global variables@
>=
4617 memory_of
(typeset_node
) typeset_nodes
;
4619 @ @
<Initialize memory@
>= init_memory
(typeset_nodes
,8);
4621 @ We also have variables for the layer size
(loaded from \.
{\\count2
}
4622 and \.
{\\count3
} registers for the current page
). If they are both zero
,
4623 then nothing will be rendered.
4625 @
<Global variables@
>=
4626 unsigned short layer_width
;
4627 unsigned short layer_height
;
4629 @ Here are the subroutines which typeset characters and rules onto the
4630 page buffer. They are not rendered into a picture yet.
4632 @d typeset_new_page
() (typeset_nodes.used
=0)
4633 @d typeset_rule_here
(_w
,_h
) typeset_rule
(quan
('I'
),quan
('J'
),(_w
),(_h
));
4634 @d typeset_char_here
(_ch
) typeset_char
(quan
('I'
),quan
('J'
),(_ch
));
4636 @
-p void typeset_rule
(int x
,int y
,int w
,int h
) {
4637 data_index n
=new_record
(typeset_nodes
);
4638 @
<Ensure |w| and |h| are not too large to fit on the page@
>;
4639 typeset_nodes.data
[n
].x
=x
;
4640 typeset_nodes.data
[n
].y
=y
;
4641 typeset_nodes.data
[n
].w
=w
;
4642 typeset_nodes.data
[n
].h
=h
;
4643 typeset_nodes.data
[n
].l
=quan
('L'
)|
0x80;
4646 @ @
<Ensure |w| and |h| are not too large to fit on the page@
>= {
4647 if
(x
+w
>layer_width
) w
=layer_width-x
;
4648 if
(y
+h
>layer_height
) h
=layer_height-y
;
4651 @ @
-p void typeset_char
(int x
,int y
,data_index c
) {
4652 data_index n
=new_record
(typeset_nodes
);
4653 typeset_nodes.data
[n
].x
=x
;
4654 typeset_nodes.data
[n
].y
=y
;
4655 typeset_nodes.data
[n
].c
=c
;
4656 typeset_nodes.data
[n
].l
=quan
('L'
);
4659 @ Here is a variable |image|. This is a pointer to the buffer for the
4660 picture of the current layer
, in \.
{PBM
} format. The internal quantity
4661 \.L should be set now to the largest layer number in use
, at the end of
4662 the page
, because it is used to determine how many layers must be sent to
4665 @d image_max
(image
+layer_size
)
4667 @
<Global variables@
>=
4668 unsigned char
*image
;
4670 @ @
<Render this page@
>= {
4671 unsigned int row_size
=((layer_width
+7)>>3);
4672 unsigned int layer_size
=row_size
*layer_height
;
4673 image
=malloc
(layer_size
+1);
4675 memset
(image
,0,layer_size
);
4676 @
<Read the |typeset_nodes| list and render any applicable nodes@
>;
4677 @
<Send the current layer to a file@
>;
4683 @ @
<Read the |typeset_nodes| list and render any applicable nodes@
>= {
4685 foreach
(i
,typeset_nodes
) {
4686 if
((typeset_nodes.data
[i
].l
&0x7F)==quan('L')) {
4687 if
(typeset_nodes.data
[i
].l
&0x80) {
4688 @
<Render a rule node@
>;
4690 @
<Render a character node@
>;
4696 @ In order to render a rule node
(which is a filled |black| rectangle
), it
4697 is split into rows
, and each row is split into three parts
: the left end
,
4698 the filling
, and the right end. However
, if the width is sufficiently
4699 small
, it will fit in one byte and will not have to be split in this way.
4701 There are also some checks to ensure that the entire rectangle will be
4702 within the bounds of the image.
4704 @
<Render a rule node@
>= {
4705 int y
=1+typeset_nodes.data
[i
].y-typeset_nodes.data
[i
].h
;
4706 int x
=typeset_nodes.data
[i
].x
;
4707 int w
=typeset_nodes.data
[i
].w
;
4709 if
(typeset_nodes.data
[i
].y
>=layer_height
)
4710 typeset_nodes.data
[i
].y
=layer_height-1
;
4712 @
<Render a small rule node@
>;
4714 @
<Render a large rule node@
>;
4718 @ @
<Render a small rule node@
>= {
4719 for
(;y
<=typeset_nodes.data
[i
].y
;y
++) {
4720 image
[y
*row_size
+(x
>>3)]|
=(0xFF>>(x
&7))&~(0xFF>>((x&7)+w));
4724 @ @
<Render a large rule node@
>= {
4725 for
(;y
<=typeset_nodes.data
[i
].y
;y
++) {
4726 unsigned char
*p
=image
+(y
*row_size
+(x
>>3));
4727 *p
++|
=0xFF>>(x
&7); // left
4728 memset
(p
,0xFF,((x
&7)+w)>>3); // filling
4729 p
[((x
&7)+w)>>3]|=~(0xFF>>((x+w)&7)); // right
4733 @ Character nodes are a bit different. The pictures are already stored
,
4734 now we have to paste them into the layer picture. Since they will not
4735 always be aligned to a multiple to eight columns
(one byte
), it will have
4736 to shift out and shift in.
4738 Again
, it is necessary to ensure it doesn't go out of bounds. It has to be
4739 a bit more careful for characters than it does for rules. Also note that
4740 the \.
{GF
} format does not require that |min_m| and so on are the tightest
4743 @
<Render a character node@
>= {
4744 unsigned int ch
=typeset_nodes.data
[i
].c
;
4745 unsigned int x
=typeset_nodes.data
[i
].x
+font_data.data
[ch
].min_m
;
4746 unsigned int y
=typeset_nodes.data
[i
].y-font_data.data
[ch
].max_n
;
4747 unsigned int z
=typeset_nodes.data
[i
].y-font_data.data
[ch
].min_n
;
4748 unsigned int w
=(font_data.data
[ch
].max_m-font_data.data
[ch
].min_m
)/8+1;
4749 register unsigned char sh
=x
&7; // shifting amount for right shift
4750 register unsigned char lsh
=8-sh
; // shifting amount for left shift
4751 unsigned char
*p
=image
+(y
*row_size
+(x
>>3));
4752 unsigned char
*q
=font_data.data
[ch
].raster
;
4753 @
<Cut off the part of character above the top of the layer image@
>;
4754 while
(y
<=z
&& p+w<image_max) {
4755 @
<Render the current row of the character raster@
>;
4756 @
<Advance to the next row of the character@
>;
4760 @ @
<Cut off the part of character above the top of the layer image@
>= {
4766 if
(p
<image
) p
=image
;
4769 @ @
<Render the current row of the character raster@
>= {
4777 @ @
<Advance to the next row of the character@
>= {
4783 @ Layer files are output in \.
{PBM
} format
, which is very similar to the
4784 format which this program uses internally. ImageMagick is capable of
4785 reading this format.
4792 @
<Send the current layer to a file@
>= {
4795 sprintf
(filename
,"P%dL%d.pbm",quan
('P'
),quan
('L'
));
4796 fp
=fopen
(filename
,"wb");
4797 fprintf
(fp
,"P4%d %d ",layer_width
,layer_height
);
4798 fwrite
(image
,1,layer_size
,fp
);
4802 @ @
<Display the list of typeset nodes@
>= {
4804 foreach
(i
,typeset_nodes
) {
4805 if
(typeset_nodes.data
[i
].l
&0x80) {
4806 printf
("[%d] %dx%d%+d%+d\n",typeset_nodes.data
[i
].l
&0x7F
4807 ,typeset_nodes.data
[i
].w
,typeset_nodes.data
[i
].h
4808 ,typeset_nodes.data
[i
].x
,typeset_nodes.data
[i
].y
4811 printf
("[%d] %d(%d) %+d%+d\n",typeset_nodes.data
[i
].l
4812 ,typeset_nodes.data
[i
].c
,font_data.data
[typeset_nodes.data
[i
].c
].n
4813 ,typeset_nodes.data
[i
].x
,typeset_nodes.data
[i
].y
4819 @ @
<Display typesetting diagnostics@
>= {
4822 if
(type_quan
[i
]) printf
("%c=%d\n",i
+'@@'
,type_quan
[i
]);
4824 printf
("unit_conv: %lld [%d]\n",unit_conv
,round
(unit_conv
));
4825 printf
("nodes: %d/%d\n",typeset_nodes.used
,typeset_nodes.allocated
);
4826 printf
("fonts: %d/%d\n",font_data.used
,font_data.allocated
);
4827 if
(dvi_stack
) printf
("stack: %d\n",dvi_stack_ptr-dvi_stack
);
4830 @
*Process of ImageMagick. The filename of ImageMagick \.
{convert
} is found
4831 by using the \.
{IMCONVERT
} environment variable. The entire command-line
4832 is stored in the \.Q register
, with arguments separated by spaces
, and it
4838 @d add_magick_arg
(_val
) magick_args.data
[new_record
(magick_args
)]=_val
4841 typedef char
*char_ptr
;
4843 @ @
<Global variables@
>=
4844 memory_of
(char_ptr
) magick_args
;
4846 @ @
<Switch to ImageMagick@
>= {
4847 init_memory
(magick_args
,4);
4848 add_magick_arg
("convert"); // |argv
[0]|
(program name
)
4849 @
<Add arguments from \.Q register@
>;
4850 add_magick_arg
(0); // (terminator
)
4851 @
<Call the ImageMagick executable file@
>;
4854 @ The \.Q register will be clobbered here. But that is
OK since it will no
4855 longer be used within \TeX nicard.
4857 @
<Add arguments from \.Q register@
>= {
4858 char
*q
=registers
['Q'
].text
;
4862 if
(q
=strchr
(q
,' '
)) *q
++=0;
4863 if
(*p
) add_magick_arg
(p
);
4867 @ @
<Call the ImageMagick executable file@
>= {
4868 char
*e
=getenv
("IMCONVERT");
4869 if
(!e
) @
<Display the arguments and quit@
>;
4870 execv
(e
,magick_args.data
);
4871 fprintf
(stderr
,"Unable to run ImageMagick\n");
4872 @.Unable to run ImageMagick@
>
4876 @ @
<Display the arguments and quit@
>= {
4879 foreach
(i
,magick_args
) if
(p
=magick_args.data
[i
]) printf
("%s\n",p
);
4883 @
*Internal Typesetting. Until now
, we only had the codes for doing
4884 external typesetting and image manipulation
(which was the original plan
4885 for this program
). Now
, we are adding internal typesetting and image
4886 manipulation as well
, to avoid external dependencies.
4888 Some of the algorithms of \TeX\ will be used here
, with some changes. For
4889 example
, there are no leaders
, marks
, footnotes
, alignments
, mathematical
4890 formulas
, or hyphenation. Ligature nodes are not needed either
, because
4891 there is no hyphenation
, so we can just use normal character nodes for
4894 There is also no page breaking
, although you can still do vertical
4895 splitting if you want multiple columns of text on a card
, or for the text
4896 to be interrupted in the middle.
4898 @ Here is a list of the category codes used for internal typesetting
, and
4899 the code to initialize that table and the other tables. There are also
4900 category codes from
32 to
255, which mean that it is a register number
4901 containing a code to execute
(we set up |tabulation| and |escape_code| to
4902 call registers \.t and \.e
, although it is unlikely to use these tokens
).
4904 @d cat_ignore
0 // Ignore this token
4905 @d cat_norm
1 // Add a character from the current font
4906 @d cat_space
2 // Add a glue node with the current space factor
4907 @d cat_exit
3 // Exit the current block
4908 @d cat_accent
4 // Add an accent to the next character
4909 @d cat_xaccent
5 // As above
, but XOR
128
4911 @
<Initialize tables for internal typesetting@
>= {
4912 for
(i
=0;i
<256;i
++) {
4915 tables
['J'
][i
]=tables
['K'
][i
]=128;
4917 tables
['E'
][null_char
]=cat_ignore
;
4918 tables
['E'
][end_transmission
]=cat_exit
; // Not actually used
4919 tables
['E'
][tabulation
]='t'
;
4920 tables
['E'
][escape_code
]='e'
;
4921 tables
['E'
][record_separator
]=cat_exit
;
4922 tables
['E'
][field_separator
]=cat_exit
;
4923 tables
['E'
][' '
]=cat_space
;
4926 @ All dimensions are stored in units of scaled points
(where there are
4927 65536 scaled points in one point
, and $
72.27$ points in one inch
).
4929 There will also be a type for glue ratios
, which is used to multiply by
4930 glue stretch and shrink inside of a box
, where a value of |
0x100000000|
4931 means
100\char`\
%\relax\space stretch or shrink
, or
1pt per fil unit.
4934 typedef signed int scaled
;
4935 typedef signed long long int glue_ratio
;
4937 @
*Data Structures for Boxes. Typesetting is done first by storing
4938 horizontal and vertical boxes of nodes. These boxes may then be included
4939 in other boxes
, or shipped out to the next part of the program
, which is
4942 Here we list the possible kind of nodes. These are four-bit numbers
, with
4943 bit
3 set for a breakable\slash discardable node. The four high bits are
4944 used as a small parameter for the node.
4946 There are structures for many kinds of nodes
, but only one pointer type
4947 will be used. Unions are used to allow many kinds of nodes at once.
4949 @d chars_node
00 // One word of text
(including kerns
, ligatures
, accents
)
4950 @d hlist_node
01 // Horizontal box
4951 @d vlist_node
02 // Vertical box
4952 @d rule_node
03 // Filled rectangle
4953 @d adjust_node
04 // Add material before or after current line
4954 @d special_node
05 // Execute commands when this node is found
4955 @d layer_node
06 // Like |special_node| but with only one purpose
4956 @d kern_node
010 // Fixed movement
4957 @d glue_node
011 // Variable movement
4958 @d penalty_node
012 // Tell how bad it is to break a line
/page here
4960 @d type_of
(_node
) ((_node
)->type_and_subtype
&0x0F)
4961 @d subtype_of
(_node
) ((_node
)->type_and_subtype
>>4)
4963 @d calc_size
(_members
) (sizeof
(struct
{
4964 struct box_node
*y
;unsigned char z
;struct
{_members
}@
+;
4968 typedef struct box_node
{
4969 struct box_node
*next
; // next node
, or
0
4970 unsigned char type_and_subtype
;
4972 @
<Structure of a |chars_node|@
>;
4973 @
<Structure of a |hlist_node|
, |vlist_node|
, or |rule_node|@
>;
4974 @
<Structure of a |adjust_node|@
>;
4975 @
<Structure of a |special_node|@
>;
4976 @
<Structure of a |layer_node|@
>;
4977 @
<Structure of a |kern_node|@
>;
4978 @
<Structure of a |glue_node|@
>;
4979 @
<Structure of a |penalty_node|@
>;
4984 @ In a |chars_node|
, there is a font number
(0 to
255), and then sixteen
4985 bits for each character
, accent
, or kern. Data |
0x0000| to |
0x7FFF| adds a
4986 character
(so only
32768 characters are available
, while \TeX\ supports
4987 only
256 characters
, so it is still more than \TeX
), data |
0x8000| to
4988 |
0xBFFF| specifies an accent for the next character
(so only characters
4989 numbered
0 to
16383 can be used as accents
), |
0xC000| to |
0xFFFE| are
4990 implicit kerns
(allowing only
16383 possible kerns
, although most fonts
4991 use only ten or so
, certainly not as many as sixteen thousand
), and data
4992 |
0xFFFF| is a terminator. All characters are from the same font.
4994 If an accent is specified
, it is added to the immediately next character
4997 @d sizeof_chars_node calc_size
(unsigned char a
;unsigned short b
[0];)
4999 @
<Structure of a |chars_node|@
>=
5002 unsigned short chars
[0];
5005 @ An |hlist_node|
, |vlist_node|
, and |rule_node| are all similar to each
5006 other
, except that a |rule_node| does not have a |list| or |glue_set|
, and
5007 a |hlist_node| has an additional |tracking| parameter.
5009 Tracking is
128 for normal width of each letter. They can be adjusted to a
5010 lesser number to make the letters closer together
, or greater to make
5011 farther apart leters
, for example
64 means half of normal width.
5013 The |subtype_of| a |hlist_node| or |vlist_node| is the glue set order
,
5014 setting the high bit for shrinking
(otherwise it is stretching
).
5016 @d sizeof_hlist_node calc_size
(
5017 scaled a
;scaled b
;scaled c
;scaled d
;
5018 struct box_node
*e
;glue_ratio f
;unsigned char g
;
5020 @d sizeof_vlist_node calc_size
(
5021 scaled a
;scaled b
;scaled c
;scaled d
;
5022 struct box_node
*e
;glue_ratio f
;
5024 @d sizeof_rule_node calc_size
(scaled a
;scaled b
;scaled c
;scaled d
;)
5026 @
<Structure of a |hlist_node|...@
>=
5031 scaled shift_amount
; // shift this box by the specified amount
5033 struct box_node
*list
; // pointer to first child node
5034 glue_ratio glue_set
;
5036 unsigned char tracking
; // adjust letter spacing
5039 @ An |adjust_node| has only a pointer to the sublist
, and the |subtype_of|
5040 should be zero to append the vertical material after this line of the
5041 paragraph
, or one to put it before this line of the paragraph.
5043 @d sizeof_adjust_node calc_size
(struct box_node
*a
;)
5045 @
<Structure of a |adjust_node|@
>=
5047 struct box_node
*sublist
; // pointer to first child node
5050 @ A |special_node| contains a null-terminated C string. The |subtype_of|
5051 specifies how it is used
; they are listed below.
5053 @d spec_measure
1 // Measuring the length of a line in a paragraph
5054 @d spec_break
2 // Breaking a paragraph
5055 @d spec_pack
3 // Packaging a box
5056 @d spec_vbreak
4 // Breaking a vertical box
5057 @d spec_render
5 // Shipping out the nodes to the page
5059 @d sizeof_special_node calc_size
(char a
[0];)
5061 @
<Structure of a |special_node|@
>=
5066 @ A |layer_node| acts like a |special_node| with subtype |spec_render| and
5067 the |program| set to |
"3mL"| if the |layer| parameter is
3. It is probably
5068 a more common kind of special.
5070 For example
, it might be used to specify typing in different colors.
5072 @d sizeof_layer_node calc_size
(unsigned char a
;)
5074 @
<Structure of a |layer_node|@
>=
5076 unsigned char layer
;
5079 @ A |kern_node| represents a horizontal or vertical movement
, such as
5080 where some amount of space is skipped.
5082 @d sizeof_kern_node calc_size
(scaled a
;)
5084 @
<Structure of a |kern_node|@
>=
5089 @ A |glue_node| is similar to a |kern_node| although there are some
5090 differences. One difference is that it can stretch and shrink. The
5091 |subtype_of| parameter has the stretch order in the low two bits and the
5092 shrink order in the high two bits.
5099 @d sizeof_glue_node calc_size
(scaled a
;scaled b
;scaled c
;)
5101 @
<Structure of a |glue_node|@
>=
5108 @ A |penalty_node| specifies a valid breakpoint in a paragraph
, and in
5109 addition
, specifies how bad it is to break here. A penalty value
10000001
5110 is bad enough that it will not break here
, and $
-10000001$ is good enough
5111 that it will definitely break here.
5113 @d sizeof_penalty_node calc_size
(signed int a
;)
5115 @
<Structure of a |penalty_node|@
>=
5120 @ Here are functions for manipulation of box nodes
, including creation
,
5121 destruction
, and so on.
5123 First is simple creation of a node. It sets nothing other than type and
5126 @
-p box_node
*create_node
(int type
,int subtype
,int size
) {
5127 box_node
*ptr
=malloc
(size
);
5129 ptr-
>type_and_subtype
=(type
&0x0F)|(subtype<<4);
5133 @ Now is destruction. It is recursive because some nodes are boxes that
5134 point to other lists too.
5136 @
-p void trash_nodes
(box_node
*this
) {
5140 @
<Recurse if there is a sublist to trash@
>;
5146 @ @
<Recurse if there is a sublist to trash@
>= {
5147 switch
(type_of
(this
)) {
5148 case hlist_node
: case vlist_node
: @
/
5149 trash_nodes
(this-
>list
); @
+break
;
5150 case adjust_node
: @
/
5151 trash_nodes
(this-
>sublist
); @
+break
;
5152 default
: ; // Do nothing
5156 @ You might realize there are no reference counts. They aren't needed
,
5157 because each node is used exactly once.
(Later on in the semantic nest
, it
5158 is seen that this is not quite true
; the box nest also includes a
5159 reference
, which is in addition to the |next| pointers of each node
, but
5160 this is
OK since those are nodes are never isolated or destroyed when
5161 picked off of that list.
)
5163 @
*Font Metric Data. In order to do internal typesetting
, it is necessary
5164 to load the font metric data from a \.
{TFM
} file. The data in a \.
{TFM
}
5165 file consists of
32-bit words in big-endian order.
5167 However
, the first
6 words are twelve
16-bit integers instead
, giving
5168 lengths of various parts of the file.
5171 @ The most important data type used here is a |fix_word|
, which is a
5172 32-bit signed number
, with
12 integer bits and
20 fractional bits. Most of
5173 the |fix_word| values in a \.
{TFM
} file range from $
-16$ to $
+16$.
5176 typedef signed int fix_word
;
5178 @ The twelve lengths are according to the following
:
5180 \hbox to\hsize
{\hfil\vbox
{\smallskip\halign
{\hfil$\it#
={}$
&#\hfil\cr
5181 lf
&length of the entire file, in words\cr
5182 lh
&number of words of header data\cr
5183 bc
&smallest character code in this font\cr
5184 ec
&largest character code in this font\cr
5185 nw
&number of words in the width table\cr
5186 nh
&number of words in the height table\cr
5187 nd
&number of words in the depth table\cr
5188 ni
&number of words in the italic correction table\cr
5189 nl
&number of words in the ligature/kern program\cr
5190 nk
&number of words in the kern table\cr
5191 ne
&number of words in the extensible character table\cr
5192 np
&number of font parameter words\cr
5195 \noindent The parts of the file are in the order listed above. Some of the
5196 sections of the file are not used by this program
(the extensible
5197 characters and the header words
), but they still must be skipped over when
5198 reading the \.
{TFM
} file. Also
, the $\it lf$ parameter is only for
5199 verification
, and this program does not attempt to verify it.
5201 @ Here is data structures for storing information about font metrics. It
5202 is a managed memory. Some elements will be shared by multiple fonts that
5203 use the same \.
{TFM
} file
, such as |design_size|
, |fontname|
, and the
5204 ligature
/kerning programs.
5208 scaled parameters
[16]; // Font parameters
(up to sixteen
)
5209 scaled at_size
; // At size
, for figuring out \.
{GF
} filename
5210 scaled design_size
; // Design size
, for figuring out \.
{GF
} filename
5211 char
*fontname
; // Name of font
, without extension or area
5217 unsigned char min_char
; // Smallest valid character code
5218 unsigned char max_char
; // Largest valid character code
5219 int right_boundary
; // If this is |none| then there is no right boundary
5220 unsigned char lig_limit
; // Code |x| ligatures if |x
<256*lig_limit|
5221 @
<More elements of |font_metric_data|@
>@
;
5224 @ @
<Global variables@
>=
5225 memory_of
(font_metric_data
) metrics
;
5227 @ @
<Initialize memory@
>= init_memory
(metrics
,4);
5229 @ Now the ligature
/kerning program. The purpose of these fields is
5237 unsigned char remainder
;
5240 @ Some fonts will have a fake ``left boundary character''
, which is
5241 implied at the beginning of each word. This points to the command which
5242 should become active at the beginning of a word. If it is null
, then no
5243 ligature
/kerning program will be active.
5245 @
<More elements of |font_metric_data|@
>=
5246 lig_kern_command
*left_boundary
; // Program for left boundary character
5248 @ Another thing is the character info. These are the same data for
5249 different sizes of the same font
, since they are index into the other
5250 arrays
, which are different for each font.
5254 unsigned char width
; // Index into |width_base|
5255 unsigned char height
; // Index into |height_base|
5256 unsigned char depth
; // Index into |depth_base|
5257 unsigned char italic
; // Index into |italic_base|
5258 lig_kern_command
*program
; // Program for this character
(null if none
)
5261 @ @
<More elements of |font_metric_data|@
>=
5262 char_info_data
*info
; // |info
[c
]| is info for character code |c|
5264 @ So let's get started
, please.
5266 The parameter |fontnum| shall be the font number of the first size of this
5267 font set up. The |fontname| is the name of the font
, without extension.
5268 The |at_size| parameter points to the beginning of a zero-terminated list
5269 of at-sizes to load the font at
(much of the data is the same for
5270 different at-sizes so that we can save memory in this way
). However
, the
5271 |at_size| values are |scaled|
, while the \.
{TFM
} expects |fix_word|. This
5272 is easy to correct by right-shifting four spaces.
5274 The |fix_word| values are in the same format as numbers in a \.
{DVI
} file
,
5275 so the same code can be used. A macro is set here to make convenience.
5277 @d get_fix_word
(_fp
) ((fix_word
)get_dvi_number
((_fp
),1,4))
5279 @
-p void load_tfm
(unsigned char fontnum
,char
*fontname
,scaled
*at_size
) {
5280 char filename
[max_filename_length
+1];
5281 short lengths
[12]; // The data described above
, now numbered
0 to
11
5282 lig_kern_command
*program
; // Beginning of ligature
/kerning program
5283 font_metric_data common_data
; // Data common to all sizes of a font
5284 data_index metrics_index
=metrics.used
; // Index into |metrics|
5285 int num_sizes
=0; // How many fonts we are loading at once
5286 int w_offset
; // Offset of width table
5288 @
<Set up the filename of the \.
{TFM
} file and try to open the file@
>;
5289 @
<Load the |lengths| data@
>;
5290 @
<Set up |common_data| and |program|@
>;
5291 @
<Skip the header words@
>;
5292 @
<Load the character info@
>;
5293 @
<Set |w_offset|
, and skip to the ligature
/kerning program@
>;
5294 @
<Load the ligature
/kerning program@
>;
5295 @
<Correct the pointers into the ligature
/kerning program@
>;
5296 @
<Calculate |num_sizes| and allocate font metric structures@
>;
5297 @
<Load the dimension values for each size of this font@
>;
5301 @ @
<Set up the filename of the \.
{TFM
} file and try to open the file@
>= {
5302 sprintf
(filename
,"%s.tfm",fontname
);
5303 fp
=open_file
(filename
,"rb");
5305 fprintf
(stderr
,"Cannot open font %s\n",filename
);
5306 @.Cannot open font...@
>
5311 @ @
<Load the |lengths| data@
>= {
5316 lengths
[i
]=(x
<<8)|y
;
5320 @ @
<Set up |common_data| and |program|@
>= {
5321 common_data.fontname
=strdup
(fontname
);
5322 common_data.min_char
=lengths
[2]; // Hopefully should be zero
5323 common_data.max_char
=lengths
[3];
5324 common_data.right_boundary
=none
;
5325 common_data.lig_limit
=255;
5326 common_data.info
=malloc
((lengths
[3]+1)*sizeof
(char_info_data
));
5327 program
=malloc
(lengths
[8]*sizeof
(lig_kern_command
));
5330 @ @
<Skip the header words@
>= {
5331 fseek
(fp
,4,SEEK_CUR
); // Skip checksum
5332 common_data.design_size
=get_fix_word
(fp
)>>4;
5333 fseek
(fp
,4*(lengths
[1]-2),SEEK_CUR
); // Skip everything else
5336 @ The character info is stored in a packed format. This is then unpacked
5337 and loaded into the |common_data.info| array
, which has already been
5340 @
<Load the character info@
>= {
5341 char_info_data
*info
=common_data.info
+common_data.min_char
;
5343 for
(i
=common_data.min_char
;i
<=common_data.max_char
;i
++) {
5344 info-
>width
=fgetc
(fp
);
5351 info-
>program
=program
+fgetc
(fp
);
5354 fgetc
(fp
); // Lists and extensible recipes are not used
5360 @ The ligature
/kerning program will be read before the dimensions specific
5361 to the font size
, so that the |common_data| can be set up first. And then
5362 we can skip back to |w_offset|
, multiple times
, once for each size that is
5365 @
<Set |w_offset|
, and skip to the ligature
/kerning program@
>= {
5367 fseek
(fp
,4*(lengths
[4]+lengths
[5]+lengths
[6]+lengths
[7]),SEEK_CUR
);
5370 @ @
<Load the ligature
/kerning program@
>= {
5372 for
(i
=0;i
<lengths
[8];i
++) {
5373 program
[i
].skip
=fgetc
(fp
);
5374 program
[i
].next
=fgetc
(fp
);
5375 program
[i
].op
=fgetc
(fp
);
5376 program
[i
].remainder
=fgetc
(fp
);
5380 @ Sometimes you might need large ligature
/kerning programs for many
5381 characters
, so you can start at addresses other than
0 to
255. This is the
5382 way that specifies how that is done.
5384 @
<Correct the pointers into the ligature
/kerning program@
>= {
5386 for
(i
=common_data.min_char
;i
<=common_data.max_char
;i
++)
5387 if
(common_data.info
[i
].program
&&
5388 common_data.info
[i
].program-
>skip
>128)
5389 common_data.info
[i
].program
=program
+
5390 (common_data.info
[i
].program-
>op
<<8)+
5391 common_data.info
[i
].program-
>remainder
;
5394 @ @
<Calculate |num_sizes| and allocate font metric structures@
>= {
5398 n
=new_record
(metrics
);
5399 memcpy
(&(metrics.data[n]),&common_data,sizeof(font_metric_data));
5400 metrics.data
[n
].at_size
=*p
;
5406 @ Now to load the widths
, heights
, depths
, italic corrections
, and kerning
5407 distances. This is what |w_offset| is for
, so that we can skip back to it.
5408 One allocated memory object is used for all dimension values of one size
,
5409 and then the points are moved into the fields of the |font_metric_data|.
5412 (lengths
[4]+lengths
[5]+lengths
[6]+lengths
[7]+lengths
[9])
5413 @d cur_metrics
(metrics.data
[metrics_index
])
5415 @
<Load the dimension values for each size of this font@
>= {
5417 scaled s
,z
,zprime
,alpha
,beta
;
5418 for
(p
=at_size
;*p
;p
++,metrics_index
++) {
5419 scaled
*d
=malloc
(sizeof
(scaled
)*total_font_dimen
);
5421 @
<Ensure |d| is valid@
>;
5422 @
<Set the dimension base pointers for this font@
>;
5423 z
=*p
; // The at size is now called |z|
5424 @
<Compute |zprime|
, |alpha|
, and |beta|@
>;
5425 fseek
(fp
,w_offset
,SEEK_SET
);
5426 c
=lengths
[4]+lengths
[5]+lengths
[6]+lengths
[7];
5427 @
<Load |c| scaled dimension values from |fp| into |d|@
>;
5428 fseek
(fp
,4*lengths
[8],SEEK_CUR
);
5430 @
<Load |c| scaled dimension values from |fp| into |d|@
>;
5431 @
<Load the font parameters@
>;
5435 @ @
<Ensure |d| is valid@
>= {
5437 fprintf
(stderr
,"Out of font memory\n");
5442 @ @
<Set the dimension base pointers for this font@
>= {
5443 cur_metrics.width_base
=d
;
5444 cur_metrics.height_base
=cur_metrics.width_base
+lengths
[4];
5445 cur_metrics.depth_base
=cur_metrics.height_base
+lengths
[5];
5446 cur_metrics.italic_base
=cur_metrics.depth_base
+lengths
[6];
5447 cur_metrics.kern_base
=cur_metrics.italic_base
+lengths
[7];
5450 @ @
<Load |c| scaled dimension values from |fp| into |d|@
>= {
5453 b0
=fgetc
(fp
); @
+ b1
=fgetc
(fp
); @
+ b2
=fgetc
(fp
); @
+ b3
=fgetc
(fp
);
5454 *d
++=(((((b3
*zprime
)>>8)+(b2
*zprime
))>>8)+(b1
*zprime
))/beta
5459 @ Now there are font parameters. There are up to sixteen font parameters
,
5460 but numbered starting at
1. This is the code that makes it to do this.
5462 @
<Load the font parameters@
>= {
5466 cur_metrics.parameters
[0]=cur_metrics.parameters
[1]=0;
5467 if
(lengths
[11]) cur_metrics.parameters
[1]=get_fix_word
(fp
)>>4;
5468 d
=cur_metrics.parameters
+2;
5469 @
<Load |c| scaled dimension values from |fp| into |d|@
>;
5472 @
*Semantic Nest. We might be building many boxes at once
, nested inside of
5473 each other. So
, we need to keep the stack of what kind of boxes are
5474 currently in use
, and the associated parameters
, such as space factors
,
5475 and the previous depth of the box.
5477 There is two kinds
, horizontal and vertical. The outer mode is considered
5478 horizontal so that it does not add leading between boxes
, although it is
5479 not for making a box of the outer mode.
5481 The currently active modes are stored both forwards and backwards
, so that
5482 we can use them as a stack of box nodes. There is a null pointer to mark
5483 the end of the list.
5486 typedef box_node
*box_node_ptr
;
5488 @ @
<Global variables@
>=
5489 memory_of
(box_node_ptr
) box_nest
;
5491 @ @
<Initialize memory@
>=
5492 init_memory
(box_nest
,2);
5494 @ We also have the semantic list with local variables to the current
5495 group. The purpose of the |data| fields depends on whether this state is
5496 in horizontal or vertical mode
, and that is why it is a union so that we
5497 can access then by names in that case
, although they can also be accessed
5501 typedef struct nest_state
{
5502 struct nest_state
*link
; // Link to state this one is inside of
5503 boolean is_vertical
; // 0 for horizontal
, 1 for vertical
5504 data_index box_nest_index
; // Index into |box_nest|
5507 @
<Nest state variables for horizontal mode@
>;
5508 @
<Nest state variables for vertical mode@
>;
5512 @ @
<Global variables@
>=
5513 nest_state
*cur_nest
;
5515 @ @
<Initialize memory@
>= {
5516 cur_nest
=malloc
(sizeof
(nest_state
));
5517 cur_nest-
>link
=0; // Means this is the outer level
5518 cur_nest-
>is_vertical
=0; // Horizontal mode
, no leading
5519 cur_nest-
>box_nest_index
=new_record
(box_nest
);
5520 box_nest.data
[cur_nest-
>box_nest_index
]=0;
5521 cur_nest-
>space_factor
=40; // Normal spacing
5524 @ @
<Nest state variables for horizontal mode@
>=
5526 scaled space_factor
; // Really just a number
, but I don't care
5529 @ @
<Nest state variables for vertical mode@
>=
5534 @ Here are codes to enter a nest.
5536 @
-p void enter_nest
(boolean is_vertical
) {
5537 nest_state
*link
=cur_nest
;
5538 cur_nest
=malloc
(sizeof
(nest_state
));
5539 cur_nest-
>link
=link
;
5540 cur_nest-
>is_vertical
=is_vertical
;
5541 cur_nest-
>box_nest_index
=new_record
(box_nest
);
5542 box_nest.data
[cur_nest-
>box_nest_index
]=0;
5543 if
(is_vertical
) cur_nest-
>prev_depth
=0;
5544 else cur_nest-
>space_factor
=40;
5547 @ And we also need codes to leave a nest. This function returns the
5548 pointer to the first node in the box that was being created
, and then the
5549 packaging programs can use that to make a box and iterate over the |next|
5550 pointers to read the entire list.
5552 @
-p box_node
*leave_nest
(void
) {
5553 nest_state
*link
=cur_nest-
>link
;
5555 @
<Ensure it is not nest underflow@
>;
5556 @
<Set |node| to the node at the beginning of the current list@
>;
5557 @
<Rewind |box_nest| to the end of the parent list@
>;
5563 @ The outer nest should never be left or packaged
; it is only used as a
5564 general-purpose stack and a container for other nests.
(Unlike \TeX
, the
5565 outer nest is never split into pages in \TeX nicard.
)
5567 @
<Ensure it is not nest underflow@
>= {
5569 fprintf
(stderr
,"\nNest underflow\n");
5574 @ Note
: Sometimes |node| will be a null pointer if the current list is
5575 making an empty box
(i.e. no nodes have been pushed
).
5577 @
<Set |node| to the node at the beginning of the current list@
>= {
5578 if
(box_nest.used
==cur_nest-
>box_nest_index
+1) {
5581 node
=box_nest.data
[cur_nest-
>box_nest_index
+1];
5585 @ @
<Rewind |box_nest| to the end of the parent list@
>= {
5586 box_nest.used
=cur_nest-
>box_nest_index
;
5589 @ And finally we have codes to push and pop nodes in the current list.
5590 These are simple codes since there isn't much to do.
5592 @d top_of_nodelist
(box_nest.data
[box_nest.used-1
])
5594 @
-p inline void push_node
(box_node
*ptr
) {
5595 top_of_nodelist-
>next
=ptr
;
5596 box_nest.data
[new_record
(box_nest
)]=ptr
;
5599 @ @
-p box_node
*pop_node
(void
) {
5600 box_node
*ptr
=top_of_nodelist
;
5603 top_of_nodelist-
>next
=0;
5608 @
*Box Calculation. Here are codes to calculate various things about the
5609 boxes
, including badness
, width\slash height\slash depth of a string of
5610 characters
, and so on.
5612 This function is used to compute the ``badness'' of a glue setting
, when a
5613 total $t$ is supposed to be made from amounts that sum to $s$. In this
5614 program
, the badness is $
1000(t
/s
)^
3$
(ten times as much as \TeX
). It does
5615 not have to be extremely accurate
, although it is sufficiently accurate to
5616 do line breaking and so on. Ten million occurs when you stretch more than
5617 21 times as much as it should
; this should never happen so it is given the
5618 maximum possible badness that can be computed using this. The badness
5619 squared should never exceed sixty-three bits
(which it won't
).
5623 @d very_bad
10000000
5626 @
-p int calc_badness
(scaled t
,scaled s
) {
5627 long long int r
; // Apprximately $\root3\of
{1000\cdot2^
{32}}(t
/s
)$
5629 if
(s
<=0) return very_bad
;
5631 if
(r
>2097152LL) return very_bad
;
5632 r
=(r
*r
*r
+(1LL<<31))>>32;
5633 if
(r
>very_bad
) r
=very_bad
;
5637 @ Next we calculate the width
, height
, and depth of a string of
5638 characters in one font
, possibly including accents
, kerns
, and tracking.
5639 Ligatures will have already been dealt with before this code is reached
,
5640 and kerns will already have been added in.
5642 @
-p void calc_chars
(box_node
*b
,scaled
*w
,scaled
*h
,scaled
*d
,short t
) {
5643 font_metric_data
*m
=&(metrics.data[b->font]);
5644 unsigned short
*c
; // Pointer to current character code
5645 scaled junk
; // Ensures no segmentation faults are occuring
5650 for
(c
=b-
>chars
;*c
!=0xFFFF;c
++) {
5653 @
<Process an implicit kern in |calc_chars|@
>;
5655 @
<Process an accent in |calc_chars|@
>;
5658 @
<Process a normal character in |calc_chars|@
>;
5663 @ @
<Process a normal character in |calc_chars|@
>= {
5664 scaled width
=m-
>width_base
[m-
>info
[*c
&0xFF].width];
5665 scaled height
=m-
>height_base
[m-
>info
[*c
&0xFF].height];
5666 scaled depth
=m-
>depth_base
[m-
>info
[*c
&0xFF].depth];
5667 if
(*h
<height
) *h
=height
;
5668 if
(*d
<depth
) *d
=depth
;
5672 @ @
<Process an implicit kern in |calc_chars|@
>= {
5673 scaled width
=m-
>kern_base
[*c
&0x3FFF];
5677 @ Now to do accents. This requires looking ahead to see the height for the
5678 next character. If the accent has positive height and zero depth
, then it
5679 should be adjusted higher in case the letter is taller than an `x'
(for
5680 example uppercase letters such as `\'E'
). However
, if the accent has
5681 positive depth and zero height
, then it is an accent that should not be
5682 adjusted for the height of the character
(for example `\c C'
), although it
5683 might be adjusted for the depth.
5685 It should never happen that the next item is not a normal character
(if it
5686 does
, then I am not considered responsible for your bad luck
).
5688 @
<Process an accent in |calc_chars|@
>= {
5689 scaled height
=m-
>height_base
[m-
>info
[*c
&0xFF].height];
5690 scaled depth
=m-
>depth_base
[m-
>info
[*c
&0xFF].depth];
5691 scaled c_height
=m-
>height_base
[m-
>info
[c
[1]&0xFF].height];
5692 scaled c_depth
=m-
>height_base
[m-
>info
[c
[1]&0xFF].depth];
5693 if
(height
<=0 && depth>0) {
5696 height
+=c_height-m-
>parameters
[5];
5698 if
(*h
<height
) *h
=height
;
5699 if
(*d
<depth
) *d
=depth
;
5702 @
*Packaging. This is how the nest lists are packaged into boxes and the
5703 width
, height
, and depth are calculated from them. They are separate for
5704 horizontal and vertical packing
, although there are similarities.
5706 The packing code is also used to compute the glue set of the box
, and its
5707 badness. Here is the global variable to store the badness.
5709 @
<Global variables@
>=
5710 int last_badness
=too_bad
;
5712 @ There are two such subroutines
, |hpackage| and |vpackage|
, depending on
5713 what kind of box is wanted. Each one also takes three parameters
: |first|
,
5714 the first node in the box
; |at_size|
, the intended size
, and |factor|
, the
5715 amount to multiply the natural size by before adding |at_size|.
5717 @d common_package box_node
*first
,scaled at_size
,signed char factor
5719 @ Horizontal packaging must compute height
, width
, and depth of characters
5720 and other boxes it contains
, as well as compute glue settings
, specials
,
5721 adjustments
, and so on.
5723 For horizontal packaging
, there is also a |tracking| parameter for spacing
5724 the letters in the box.
5726 @
-p box_node
*hpackage
(common_package
,unsigned char tracking
) {
5727 box_node
*box
=create_node
(hlist_node
,0,sizeof_hlist_node
);
5728 scaled stretchability
[4]; // Total stretch of all glue
5729 scaled shrinkability
[4]; // Total shrink of all glue
5730 scaled natural
=0; // Total width
5731 box_node
*this
; // Current node
5732 @
<Initialize variables for |hpackage|@
>;
5733 @
<Read all nodes in a horizontal list to package them@
>;
5734 #define @
!actual @
, box-
>width
5735 actual
=(factor
*natural
)/8+at_size
;
5736 @
<Compute glue set and badness@
>;
5741 @ @
<Initialize variables for |hpackage|@
>= {
5744 box-
>tracking
=tracking
;
5745 box-
>height
=box-
>depth
=box-
>shift_amount
=0;
5747 for
(o
=0;o
<4;o
++) stretchability
[o
]=shrinkability
[o
]=0;
5750 @ @
<Read all nodes in a horizontal list to package them@
>= {
5751 for
(this
=first
;this
;this
=this-
>next
) {
5752 switch
(type_of
(this
)) {
5753 case chars_node
: @
<Add word to box size@
>; @
+break
;
5754 case hlist_node
: case vlist_node
: case rule_node
:
5755 @
<Apply the size of a box to a horizontal list@
>; @
+break
;
5756 case kern_node
: natural
+=this-
>distance
; @
+break
;
5757 case glue_node
: @
<Add glue to box size@
>; @
+break
;
5759 if
(subtype_of
(this
)==spec_pack
) @
<Pack a special node@
>;
5761 default
: break
; // All other nodes are ignored
5766 @ @
<Add word to box size@
>= {
5768 calc_chars
(this
,&w,&h,&d,tracking<<1);
5770 if
(h
>box-
>height
) box-
>height
=h
;
5771 if
(d
>box-
>depth
) box-
>depth
=d
;
5774 @ @
<Apply the size of a box to a horizontal list@
>= {
5775 natural
+=this-
>width
;
5776 if
(this-
>height
+this-
>shift_amount
>box-
>height
)
5777 box-
>height
=this-
>height
+this-
>shift_amount
;
5778 if
(this-
>depth-this-
>shift_amount
>box-
>depth
)
5779 box-
>depth
=this-
>depth-this-
>shift_amount
;
5782 @ @
<Add glue to box size@
>= {
5783 natural
+=this-
>natural
;
5784 stretchability
[subtype_of
(this
)&3]+=this->stretch;
5785 shrinkability
[subtype_of
(this
)>>2]+=this-
>shrink
;
5788 @ When packing a special node that has a code to run during packing
, it
5789 can read and affect the current width and the intended width
; it could
5790 also do other things
, such as accumulating boxes for adjustments and so
5793 @
<Pack a special node@
>= {
5796 @# execute_program
(this-
>program
); @#
5801 @ A macro named |actual| is defined above so that this code can be used
5802 for both horizontal and for vertical packaging.
5804 We also have a macro here to decide setting the glue.
5806 @d set_glue
(_order
,_flag
,_diff
,_glue
)
5807 (box-
>type_and_subtype|
=((_order
)<<4)|
((_flag
)<<7)),
5808 (box-
>glue_set
=make_fraction
(_glue
,_diff
))
5810 @
<Compute glue set and badness@
>= {
5811 if
(actual
>natural
) {
5812 @
<Glue is stretching@
>;
5813 } @
+else if
(actual
<natural
) {
5814 @
<Glue is shrinking@
>;
5816 last_badness
=0; // Perfect
!
5820 @ @
<Glue is stretching@
>= {
5821 if
(stretching
[filll
]!=0) {
5822 set_glue
(filll
,0,actual-natural
,stretching
[filll
]);
5824 } @
+else if
(stretching
[fill
]!=0) {
5825 set_glue
(fill
,0,actual-natural
,stretching
[fill
]);
5827 } @
+else if
(stretching
[fil
]!=0) {
5828 set_glue
(fil
,0,actual-natural
,stretching
[fil
]);
5830 } @
+else if
(stretching
[finite
]!=0) {
5831 set_glue
(finite
,0,actual-natural
,stretching
[finite
]);
5832 last_badness
=calc_badness
(actual-natural
,stretching
[finite
]);
5834 last_badness
=too_bad
;
5838 @ @
<Glue is shrinking@
>= {
5839 if
(shrinking
[filll
]!=0) {
5840 set_glue
(filll
,1,natural-actual
,shrinking
[filll
]);
5842 } @
+else if
(shrinking
[fill
]!=0) {
5843 set_glue
(fill
,1,natural-actual
,shrinking
[fill
]);
5845 } @
+else if
(shrinking
[fil
]!=0) {
5846 set_glue
(fil
,1,natural-actual
,shrinking
[fil
]);
5848 } @
+else if
(shrinking
[finite
]>=natural-actual
) {
5849 set_glue
(finite
,1,natural-actual
,shrinking
[finite
]);
5850 last_badness
=calc_badness
(natural-actual
,shrinking
[finite
]);
5852 set_glue
(finite
,1,1,1); // Shrink as much as possible
5853 last_badness
=too_bad
;
5859 For vertical packaging
, the two extra parameters are |max_dp|
, the maximum
5860 depth
; and |align_top|
, which should be set true if it is wanted to align
5861 at the top instead of at the bottom.
5863 @
-p box_node
*vpackage
(common_package
,scaled max_dp
,boolean align_top
) {
5864 box_node
*box
=create_node
(vlist_node
,0,sizeof_vlist_node
);
5865 scaled stretchability
[4]; // Total stretch of all glue
5866 scaled shrinkability
[4]; // Total shrink of all glue
5867 scaled natural
=0; // Total height plus depth
5868 scaled bonnet
=0; // Height of first item
5869 scaled boot
=0; // Depth of last item
5870 box_node
*this
; // Current node
5871 @
<Initialize variables for |vpackage|@
>;
5872 @
<Read all nodes in a vertical list to package them@
>;
5873 box-
>height
=bonnet
; @
+ box-
>depth
=boot
;
5874 #define @
!actual @
, (*(align_top?
&(box->depth):&(box->height)))
5875 natural-
=align_top?bonnet
:boot
;
5876 actual
=(factor
*natural
)/8+at_size
;
5877 @
<Compute glue set and badness@
>;
5879 @
<Move the reference point to match the maximum depth
, if applicable@
>;
5883 @ @
<Initialize variables for |vpackage|@
>= {
5886 box-
>width
=box-
>shift_amount
=0;
5888 for
(o
=0;o
<4;o
++) stretchability
[o
]=shrinkability
[o
]=0;
5891 @ @
<Read all nodes in a vertical list to package them@
>= {
5892 for
(this
=first
;this
;this
=this-
>next
) {
5893 switch
(type_of
(this
)) {
5894 case hlist_node
: case vlist_node
: case rule_node
:
5895 @
<Apply the size of a box to a vertical list@
>; @
+break
;
5896 case kern_node
: natural
+=this-
>distance
; @
+boot
=0; @
+break
;
5897 case glue_node
: @
<Add glue to box size@
>; @
+break
;
5899 if
(subtype_of
(this
)==spec_pack
) @
<Pack a special node@
>;
5901 default
: break
; // All other nodes are ignored
5903 if
(this
==first
) bonnet
=natural-boot
;
5907 @ @
<Apply the size of a box to a vertical list@
>= {
5908 natural
+=this-
>height
+(boot
=this-
>depth
);
5909 if
(this-
>width
+this-
>shift_amount
>box-
>width
)
5910 box-
>width
=this-
>width
+this-
>width
;
5913 @ @
<Move the reference point to match the maximum depth
, if applicable@
>= {
5914 if
(box-
>depth
>max_dp
) {
5915 box-
>height
+=box-
>depth-max_dp
;
5920 @
*Typesetting Commands. There are various commands available in \TeX
5921 nicard for dealing with typesetting. Another thing it does is to allow you
5922 to enter distances using units.
5924 @
<Do a typesetting command@
>= {
5926 if
((c
>='
0'
&& c<='9') || c=='.') @<Read a distance and units@>@;
5928 @
<Typesetting commands@
>@
;
5932 @ The following units are supported
:
5934 \halign
{\quad\tt#\space\hss
&(#)\hss\cr
5936 bp
&Desktop publishing point\cr
5942 em
&Em width -- font specific\cr
5943 ex
&Ex height -- font specific\cr
5946 @d distance_units
(_a
,_b
,_x
,_y
)
5947 if
(c
==_a
&& decim==_b) { num*=_x; den*=_y; }
5949 @
<Read a distance and units@
>= {
5950 int decim
=1; // Multiply denominator per step
; reused for second letter
5951 int num
=0,den
=1; // Numerator and denominator to scale the units
5952 while
((c
>='
0'
&& c<='9') || c=='.') {
5954 if
(c
=='.'
) decim
=10;
5955 else num
=10*num
+c-'
0'
;
5959 distance_units
('p'
,'t'
,1,1);
5960 distance_units
('b'
,'p'
,7227,7200);
5961 distance_units
('i'
,'n'
,7227,100);
5962 distance_units
('c'
,'m'
,7227,254);
5963 distance_units
('m'
,'m'
,7227,2540);
5964 distance_units
('p'
,'c'
,12,1);
5965 distance_units
('q'
,'u'
,7227,10160);
5966 push_num
(make_fraction
(num
,den
)>>16);
5969 @
*Internal Image Rendering. This program uses LodePNG to read
/write PNG
5970 files.
(The LodePNG included with this program is customized to omit the
5971 things which are not used.
)
5974 @^Portable Network Graphics@
>
5977 #include
"lodepng/lodepng.h"
5979 @ Here are keeping track of the bitmaps
(in PBM format
) and graymaps
(in
5980 (in
8-bit PGM format
). Three or four graymaps are loaded from or saved to
5981 a PNG file and the conversion between is done at that time.
5983 @
<Global variables@
>=
5984 unsigned char
*bitmap
[10];
5985 unsigned char
*graymap
[10];
5987 @ @
-p void execute_image_manip
(image_manipulator
*imp
) {
5988 unsigned int b_row_size
=((layer_width
+7)>>3);
5989 unsigned int b_layer_size
=b_row_size
*layer_height
;
5990 unsigned int g_layer_size
=layer_width
*layer_height
;
5991 while
(imp-
>data_len
&& imp->data[0]) {
5992 if
(imp-
>data
[0]<20) {
5993 @
<Perform image manipulator command with no picture registers@
>;
5994 } @
+else if
(imp-
>data
[0]<1000) {
5995 @
<Perform image manipulator command with one picture register@
>;
5996 } @
+else if
(imp-
>data
[0]<20000) {
5997 @
<Perform image manipulator command with two picture registers@
>;
5999 @
<Perform image manipulator command with three picture registers@
>;
6001 if
(imp-
>next
==none
) return
;
6002 imp
=image_manips.data
+imp-
>next
;
6006 @ Here are some subroutines to initialize and discard the picture memory.
6008 @
-p void init_bitmap
(int n
) {
6009 unsigned int row_size
=((layer_width
+7)>>3);
6010 unsigned int layer_size
=row_size
*layer_height
;
6011 if
(bitmap
[n
]) return
;
6012 memset
(bitmap
[n
]=malloc
(layer_size
+1),0,layer_size
);
6015 @ @
-p void init_graymap
(int n
) {
6016 unsigned int layer_size
=layer_width
*layer_height
;
6017 if
(graymap
[n
]) return
;
6018 memset
(graymap
[n
]=malloc
(layer_size
),0,layer_size
);
6021 @ @
-p void trash_bitmap
(int n
) {
6022 if
(bitmap
[n
]) free
(bitmap
[n
]);
6026 @ @
-p void trash_graymap
(int n
) {
6027 if
(graymap
[n
]) free
(graymap
[n
]);
6031 @ Now we have the commands which appear in an image manipulator.
6033 Since gamma corrections are computed here
, \.
{math.h
} is required.
6041 @^gamma correction@
>
6043 @
<Perform image manipulator command with no picture registers@
>= {
6044 switch
(imp-
>data
[0]) {
6045 case
1: { // Fill multiplication table
6046 unsigned char
*t
=tables
[0];
6048 for
(i
=0;i
<256;i
++) {
6049 j
=(i
*imp-
>data
[1])/2560;
6050 if
(j
>255) t
[i
]=255; else t
[i
]=j
;
6054 case
2: { // Fill table by polynomial
6055 unsigned char
*t
=tables
[0];
6057 for
(i
=0;i
<256;i
++) {
6060 for
(n
=1;n
<imp-
>data_len
;n
++) {
6061 j
+=k
*(int
)(imp-
>data
[n
]-5120);
6071 case
3: { // Fill gamma table
6072 unsigned char
*t
=tables
[0];
6075 for
(i
=0;i
<256;i
++) {
6076 d
=pow
(i
,0.001*(double
)(imp-
>data
[1]+1));
6080 case
4: { // Copy table
6081 unsigned char
*t
=tables
[imp-
>data
[1]&0x7F];
6082 unsigned char
*u
=tables
[imp-
>data
[2]&0x7F];
6084 for
(i
=0;i
<256;i
++) t
[i
]=u
[i
];
6086 case
5: { // Compose table
6087 unsigned char
*t
=tables
[imp-
>data
[1]&0x7F];
6088 unsigned char
*u
=tables
[imp-
>data
[2]&0x7F];
6090 for
(i
=0;i
<256;i
++) t
[i
]=u
[t
[i
]];
6095 @
<Perform image manipulator command with one picture register@
>= {
6096 int rx
=imp-
>data
[0]%10;
6097 switch
(imp-
>data
[0]/10) {
6098 case
2: { // Clear a bitmap
6102 case
3: { // Clear a graymap
6106 case
4: { // Flip a bitmap
6111 case
5: { // Flip a graymap
6116 case
6: { // Mirror a bitmap
6121 case
7: { // Mirror a graymap
6126 case
8: { // Fill graymap with value
6128 memset
(graymap
[rx
],imp-
>data
[1],g_layer_size
);
6131 case
9: { // Modify graymap by table
6132 unsigned char
*t
=tables
[imp-
>data
[1]&0x7F];
6135 for
(i
=0;i
<g_layer_size
;i
++) graymap
[rx
][i
]=t
[graymap
[rx
][i
]];
6138 case
10: { // Bitwise
NOT of bitmaps
6141 for
(i
=0;i
<b_layer_size
;i
++) bitmap
[rx
][i
]^
=0xFF;
6144 case
11: { // Shift a bitmap
6149 case
12: { // Shift a graymap
6157 @
<Perform image manipulator command with two picture registers@
>= {
6158 int rx
=imp-
>data
[0]%10;
6159 int ry
=(imp-
>data
[0]/10)%10;
6160 switch
(imp-
>data
[0]/100) {
6161 case
10: { // Copy bitmap
6162 init_bitmap
(rx
); @
+ init_bitmap
(ry
);
6163 memcpy
(bitmap
[rx
],bitmap
[ry
],b_layer_size
);
6166 case
11: { // Copy graymap
6167 init_graymap
(rx
); @
+ init_graymap
(ry
);
6168 memcpy
(graymap
[rx
],graymap
[ry
],g_layer_size
);
6171 case
12: { // Select maximum of graymaps
6173 init_graymap
(rx
); @
+ init_graymap
(ry
);
6174 for
(i
=0;i
<g_layer_size
;i
++) {
6175 if
(graymap
[ry
][i
]>graymap
[rx
][i
]) graymap
[rx
][i
]=graymap
[ry
][i
];
6179 case
13: { // Select minimum of graymaps
6181 init_graymap
(rx
); @
+ init_graymap
(ry
);
6182 for
(i
=0;i
<g_layer_size
;i
++) {
6183 if
(graymap
[ry
][i
]<graymap
[rx
][i
]) graymap
[rx
][i
]=graymap
[ry
][i
];
6187 case
14: { // Bitwise
AND of bitmaps
6191 for
(i
=0;i
<b_layer_size
;i
++) bitmap
[rx
][i
]&=bitmap[ry][i];
6194 case
15: { // Bitwise
OR of bitmaps
6198 for
(i
=0;i
<b_layer_size
;i
++) bitmap
[rx
][i
]|
=bitmap
[ry
][i
];
6201 case
16: { // Bitwise XOR of bitmaps
6205 for
(i
=0;i
<b_layer_size
;i
++) bitmap
[rx
][i
]^
=bitmap
[ry
][i
];
6208 case
17: { // Copy bitmap to graymap
6214 case
18: { // Blur graymap by graymap
6223 @ @
<Perform image manipulator command with three picture registers@
>= {
6224 int rx
=imp-
>data
[0]%10;
6225 int ry
=(imp-
>data
[0]/10)%10;
6226 int rz
=(imp-
>data
[0]/100)%10;
6227 switch
(imp-
>data
[0]/1000) {
6228 case
20: { // Compose graymap to graymap with alpha channel
6229 init_graymap
(rx
); @
+ init_graymap
(ry
); @
+ init_graymap
(rz
);
6233 case
21: { // Apply color transformation matrix
6234 init_graymap
(rx
); @
+ init_graymap
(ry
); @
+ init_graymap
(rz
);
6241 @
*Main Program. This is where the program starts and ends. Everything else
6242 in the other chapters is started from here.
6252 @ @
-p int main
(int argc
,char
**argv
) {
6254 @
<Set up signal handler@
>;
6255 @
<Initialize memory@
>;
6256 @
<Display the banner message@
>;
6257 @
<Decide whether in DVI reading mode@
>;
6258 if
(!dvi_mode
) @
<Open the main input file@
>;
6259 @
<Initialize the input states@
>;
6260 @
<Initialize the tables and registers@
>;
6261 @
<Initialize the random number generator@
>;
6262 @
<Set registers according to command-line parameters@
>;
6263 if
(!dvi_mode
) @
<Process the input files@
>;
6264 if
(dvi_mode
) dvi_mode
=read_dvi_file
(argv
[1]);
6265 @
<Call program in \.Z register if necessary@
>;
6266 if
(!dvi_mode
) @
<Send |end_transmission| to each card area@
>;
6267 @
<Write the output files@
>;
6268 if
(registers
['Q'
].is_string
&& dvi_mode &&
6269 (argv
[0][0]!='
-' || argv
[0][1]!='z'
)) @
<Switch to ImageMagick@
>;
6273 @ @
<Display the banner message@
>= {
6274 fprintf
(stderr
,"TeXnicard version %s\n",version_string
);
6276 "This program is free software and comes with NO WARRANTY.\n");
6280 @ @
<Set registers according to command-line parameters@
>= {
6282 for
(i
=2;i
<argc
;i
++) {
6283 registers
[i
+('
0'
-2)].is_string
=1;
6284 registers
[i
+('
0'
-2)].text
=strdup
(argv
[i
]);
6288 @ The main input file will be either the terminal
, or another file if the
6289 command-line argument is given.
6291 @
<Open the main input file@
>= {
6292 if
(argc
>1 && strcmp(argv[1],"-")!=0) {
6293 --current_input_file
;
6294 open_input
(argv
[1]);
6297 strcpy
(current_filename
,"<Teletype>");
6301 @ @
<Call program in \.Z register if necessary@
>= {
6302 if
(registers
['Z'
].is_string
) execute_program
(registers
['Z'
].text
);
6305 @ The alternative mode to run this program is DVI mode. DVI mode is
6306 specified by a command-line switch.
6310 @
<Decide whether in DVI reading mode@
>= {
6311 if
(argc
>1 && argv[1][0]=='-' && argv[1][1]) {
6314 if
(argv
[0][1]=='a'
) {
6315 printing_mode
=printing_all_cards
;
6316 } @
+else if
(argv
[0][1]=='f'
) {
6317 printing_mode
=printing_list_from_file
;
6318 printlistfile
=fopen
(argv
[1],"r");
6320 } @
+else if
(argv
[0][1]=='n'
) {
6321 printing_mode
=printing_list
;
6322 printlisttext
=argv
[1];
6324 } @
+else if
(argv
[0][1]=='z'
) {
6325 printing_mode
=printing_list
;
6331 @
*Signal Handlers. The |SIGSEGV| signal should be handled in case
6332 something goes wrong in the program and it causes a segmentation fault
, it
6333 should attempt to recover what you have before terminating
, in order to be
6334 better at diagnosing the error.
6336 @
<Set up signal handler@
>= {
6337 signal
(SIGSEGV
,handle_crash
);
6340 @ Some things will be more careful here to ensure not to cause the error
6341 again
(if it does
, it will just quit
, though
).
6343 @
-p void handle_crash
(int sig
) {
6344 signal
(SIGSEGV
,SIG_DFL
);
6345 @#fprintf
(stderr
,"\nFatal signal error (%d)\n",sig
);
6346 @.Fatal signal error...@
>
6347 fprintf
(stderr
,"cur_state=%d\ncur_name=%d\ncur_data=%d\n",
6348 cur_state
,cur_name
,cur_data
);
6349 if
(current_input_file
>=input_files
&& current_input_file<input_files
6350 +max_input_stack
) @
<Display input stack after a crash@
>;
6351 fprintf
(stderr
,"Program stack level: %d\n",stack_ptr-stack
);
6352 fprintf
(stderr
,"Save stack level: %d\n",save_stack_ptr-save_stack
);
6356 @ @
<Display input stack after a crash@
>= {
6358 fprintf
(stderr
,"File %s line %d\n",current_filename
,current_line
);
6359 if
(current_input_file--
==input_files
) break
;
6363 @
*The Future. Here are some ideas for future versions of this program
:
6365 $\bullet$ A customizable Inform7-like parser
, that would compile into a C
6366 code
(or possibly a Haskell code
), so that you can play the cards on
6367 rule-enforcing computer programs.
6370 $\bullet$ A database to keep track of how many copies of a card have been
6371 sold
, for inventory purposes.
6372 @^commercial viability@
>
6375 $\bullet$ Full text search
, for things such as the Oracle text search.
6378 $\bullet$ Allow more than
256 fonts in one card set.
6380 $\bullet$ Unicode input
(UTF-8
).
6385 \long\def\Par
{\csname par\endcsname
}%
6386 \loop\ifnum\count255
<\bibliocount
%
6387 \advance\count255 by
1
6388 \Par$^
{[\the\count255
]}$\csname biblio \the\count255\endcsname\Par
%
6391 @
*Index. Here you can find references to the definition and use of all the
6392 variables
, subroutines
, etc.\ used in this program
, as well as a few other
6393 things of interest. Underlined entries indicate where it is defined.
6395 {\bf Important note
:} All the numbers in this index are section numbers
,
6398 % End of file
"texnicard.w"