1% tftopl.ch for C compilation with web2c. 2% 3% 04/04/83 (PC) Original version, made to work with version 1.0 of TFtoPL, 4% released with version 0.96 of TeX in February, 1983. 5% 04/16/83 (PC) Brought up to version 1.0 released with version 0.97 of TeX 6% in April, 1983. 7% 06/30/83 (HWT) Revised changefile format, for use with version 1.7 Tangle. 8% 07/28/83 (HWT) Brought up to version 2 9% 11/21/83 (HWT) Brought up to version 2.1 10% 03/24/84 (HWT) Brought up to version 2.2 11% 07/12/84 (HWT) Brought up to version 2.3 12% 07/05/87 (ETM) Brought up to version 2.5 13% 03/22/88 (ETM) Converted for use with WEB to C. 14% 11/30/89 (KB) Version 3. 15% 01/16/90 (SR) Version 3.1. 16% (more recent changes in the ChangeLog) 17 18@x [0] WEAVE: print changes only. 19\pageno=\contentspagenumber \advance\pageno by 1 20@y 21\pageno=\contentspagenumber \advance\pageno by 1 22\let\maybe=\iffalse 23\def\title{TF\lowercase{to}PL changes for C} 24@z 25 26@x [1] Define my_name 27@d banner=='This is TFtoPL, Version 3.3' {printed when the program starts} 28@y 29@d my_name=='tftopl' 30@d banner=='This is TFtoPL, Version 3.3' {printed when the program starts} 31@z 32 33@x [2] Print all terminal output on stderr, so the pl can be sent to stdout. 34@d print(#)==write(#) 35@d print_ln(#)==write_ln(#) 36@y 37@d print(#)==write(stderr,#) 38@d print_ln(#)==write_ln(stderr,#) 39@z 40 41% [2] Fix files in program statement. We need to tell web2c about one 42% special variable. Perhaps it would be better to allow @define's 43% anywhere in a source file, but that seemed just as painful as this. 44@x 45@p program TFtoPL(@!tfm_file,@!pl_file,@!output); 46@y 47@p 48{Tangle doesn't recognize @@ when it's right after the \.=.} 49@\@= @@define var tfm;@>@\ 50program TFtoPL(@!tfm_file,@!pl_file,@!output); 51@z 52 53@x [2] No global label. 54label @<Labels in the outer block@>@/ 55@y 56@z 57 58@x [still 2] Don't print banner until later (and unless verbose). 59procedure initialize; {this procedure gets things started properly} 60 begin print_ln(banner);@/ 61@y 62@<Define |parse_arguments|@> 63procedure initialize; {this procedure gets things started properly} 64 begin 65 kpse_set_program_name (argv[0], my_name); 66 kpse_init_prog ('TFTOPL', 0, nil, nil); 67 {We |xrealloc| when we know how big the file is. The 1000 comes 68 from the negative lower bound.} 69 tfm_file_array := xmalloc_array (byte, 1002); 70 parse_arguments; 71@z 72 73@x [3] No global label. 74@<Labels...@>=final_end; 75@y 76@z 77 78@x [5] Increase sizes to match vptovf. 79@!tfm_size=30000; {maximum length of |tfm| data, in bytes} 80@!lig_size=5000; {maximum length of |lig_kern| program, in words} 81@!hash_size=5003; {preferably a prime number, a bit larger than the number 82 of character pairs in lig/kern steps} 83@y 84@!lig_size=32510; {maximum length of |lig_kern| program, in words ($<2^{15}$)} 85@!hash_size=32579; {preferably a prime number, a bit larger than the number 86 of character pairs in lig/kern steps} 87@z 88 89@x [7] Open the TFM file. 90@ On some systems you may have to do something special to read a 91packed file of bytes. For example, the following code didn't work 92when it was first tried at Stanford, because packed files have to be 93opened with a special switch setting on the \PASCAL\ that was used. 94@^system dependencies@> 95 96@<Set init...@>= 97reset(tfm_file); 98@y 99@ On some systems you may have to do something special to read a 100packed file of bytes. With C under Unix, we just open the file by name 101and read characters from it. 102 103@<Set init...@>= 104tfm_file := kpse_open_file (tfm_name, kpse_tfm_format); 105if verbose then begin 106 print (banner); 107 print_ln (version_string); 108end; 109@z 110 111@x [17] Open the PL file. 112@ @<Set init...@>= 113rewrite(pl_file); 114@y 115@ If an explicit filename isn't given, we write to |stdout|. 116 117@<Set init...@>= 118if optind + 1 = argc then begin 119 pl_file := stdout; 120end else begin 121 pl_name := extend_filename (cmdline (optind + 1), 'pl'); 122 rewrite (pl_file, pl_name); 123end; 124@z 125 126@x [18,19] Make |tfm| be dynamically allocated, and rename `index'. 127@<Types...@>= 128@!byte=0..255; {unsigned eight-bit quantity} 129@!index=0..tfm_size; {address of a byte in |tfm|} 130 131@ @<Glob...@>= 132@!tfm:array [-1000..tfm_size] of byte; {the input data all goes here} 133@y 134@d index == index_type 135 136@<Types...@>= 137@!byte=0..255; {unsigned eight-bit quantity} 138@!index=integer; {address of a byte in |tfm|} 139 140@ @<Glob...@>= 141{Kludge here to define |tfm| as a macro which takes care of the negative 142 lower bound. We've defined |tfm| for the benefit of web2c above.} 143@=#define tfm (tfmfilearray + 1001);@>@\ 144@!tfm_file_array: ^byte; {the input data all goes here} 145@z 146 147@x [20] Allow arbitrarily large input files. 148if 4*lf-1>tfm_size then abort('The file is bigger than I can handle!'); 149@.The file is bigger...@> 150@y 151tfm_file_array := xrealloc_array (tfm_file_array, byte, 4 * lf + 1000); 152@z 153 154% [27, 28] Change strings to C char pointers. The Pascal strings are 155% indexed starting at 1, so we pad with a blank. 156@x 157@!ASCII_04,@!ASCII_10,@!ASCII_14: packed array [1..32] of char; 158 {strings for output in the user's external character set} 159@!MBL_string,@!RI_string,@!RCE_string:packed array [1..3] of char; 160 {handy string constants for |face| codes} 161@y 162@!ASCII_04,@!ASCII_10,@!ASCII_14: const_c_string; 163 {strings for output in the user's external character set} 164@!ASCII_all: packed array[0..256] of char; 165@!MBL_string,@!RI_string,@!RCE_string: const_c_string; 166 {handy string constants for |face| codes} 167@z 168 169@x 170ASCII_04:=' !"#$%&''()*+,-./0123456789:;<=>?';@/ 171ASCII_10:='@@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_';@/ 172ASCII_14:='`abcdefghijklmnopqrstuvwxyz{|}~ ';@/ 173MBL_string:='MBL'; RI_string:='RI '; RCE_string:='RCE'; 174@y 175ASCII_04:=' !"#$%&''()*+,-./0123456789:;<=>?';@/ 176ASCII_10:=' @@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_';@/ 177ASCII_14:=' `abcdefghijklmnopqrstuvwxyz{|}~ ';@/ 178strcpy (ASCII_all, ASCII_04); 179strcat (ASCII_all, '@@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_'); 180strcat (ASCII_all, '`abcdefghijklmnopqrstuvwxyz{|}~');@/ 181MBL_string:=' MBL'; RI_string:=' RI '; RCE_string:=' RCE'; 182@z 183 184% [38] How we output the character code depends on |charcode_format|. 185%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 186@x 187begin if font_type>vanilla then 188 begin tfm[0]:=c; out_octal(0,1) 189 end 190else if (c>="0")and(c<="9") then 191 out(' C ',c-"0":1) 192else if (c>="A")and(c<="Z") then 193 out(' C ',ASCII_10[c-"A"+2]) 194else if (c>="a")and(c<="z") then 195 out(' C ',ASCII_14[c-"a"+2]) 196else begin tfm[0]:=c; out_octal(0,1); 197@y 198begin if (font_type > vanilla) or (charcode_format = charcode_octal) then 199 begin tfm[0]:=c; out_octal(0,1) 200 end 201else if (charcode_format = charcode_ascii) and (c > " ") and (c <= "~") 202 and (c <> "(") and (c <> ")") then 203 out(' C ', ASCII_all[c - " " + 1]) 204{default case, use \.C only for letters and digits} 205else if (c>="0")and(c<="9") then 206 out(' C ',c-"0":1) 207else if (c>="A")and(c<="Z") then 208 out(' C ',ASCII_10[c-"A"+2]) 209else if (c>="a")and(c<="z") then 210 out(' C ',ASCII_14[c-"a"+2]) 211else begin tfm[0]:=c; out_octal(0,1); 212@z 213 214% [39] Don't output the face code as an integer. 215%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 216@x 217 out(MBL_string[1+(b mod 3)]); 218 out(RI_string[1+s]); 219 out(RCE_string[1+(b div 3)]); 220@y 221 put_byte(MBL_string[1+(b mod 3)], pl_file); 222 put_byte(RI_string[1+s], pl_file); 223 put_byte(RCE_string[1+(b div 3)], pl_file); 224@z 225 226% [78] No progress reports unless verbose. 227%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 228@x 229 incr(chars_on_line); 230 end; 231 print_octal(c); {progress report} 232@y 233 if verbose then incr(chars_on_line); 234 end; 235 if verbose then print_octal(c); {progress report} 236@z 237 238% [89] Change the name of the variable `class', since AIX 3.1's <math.h> 239% defines a function by that name. 240%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 241@x 242@d pending=4 {$f(x,y)$ is being evaluated} 243@y 244@d pending=4 {$f(x,y)$ is being evaluated} 245 246@d class == class_var 247@z 248 249@x [90] 250 goto final_end; 251@y 252 uexit(1);; 253@z 254 255% [90] Change name of the function `f'. 256%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 257@x 258 r:=f(r,(hash[r]-1)div 256,(hash[r]-1)mod 256); 259@y 260 r:=f_fn(r,(hash[r]-1)div 256,(hash[r]-1)mod 256); 261@z 262 263@x [90] 264 out('(INFINITE LIGATURE LOOP MUST BE BROKEN!)'); goto final_end; 265@y 266 out('(INFINITE LIGATURE LOOP MUST BE BROKEN!)'); uexit(1); 267@z 268 269% [94] web2c can't handle these mutually recursive procedures. 270% But let's do a fake definition of f here, so that it gets into web2c's 271% symbol table. We also have to change the name, because there is also a 272% variable named `f', and some C compilers can't deal with that. 273@x 274@p function f(@!h,@!x,@!y:index):index; forward;@t\2@> 275 {compute $f$ for arguments known to be in |hash[h]|} 276@y 277@p 278ifdef('notdef') 279function f_fn(@!h,@!x,@!y:index):index; begin end;@t\2@> 280 {compute $f$ for arguments known to be in |hash[h]|} 281endif('notdef') 282@z 283@x 284else eval:=f(h,x,y); 285@y 286else eval:=f_fn(h,x,y); 287@z 288 289% [95] The real definition of f. 290%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 291@x 292@p function f; 293@y 294@p function f_fn(@!h,@!x,@!y:index):index; 295@z 296@x 297f:=lig_z[h]; 298@y 299f_fn:=lig_z[h]; 300@z 301 302@x [99] 303if not organize then goto final_end; 304@y 305if not organize then uexit(1); 306@z 307 308% [99] No final newline unless verbose. 309%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 310@x 311do_characters; print_ln('.');@/ 312@y 313do_characters; if verbose then print_ln('.');@/ 314@z 315 316@x [99] 317final_end:end. 318@y 319end. 320@z 321 322@x [100] System-dependent changes. 323This section should be replaced, if necessary, by changes to the program 324that are necessary to make \.{TFtoPL} work at a particular installation. 325It is usually best to design your change file so that all changes to 326previous sections preserve the section numbering; then everybody's version 327will be consistent with the printed program. More extensive changes, 328which introduce new sections, can be inserted here; then only the index 329itself will get a new section number. 330@^system dependencies@> 331@y 332Parse a Unix-style command line. 333 334@d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0) 335 336@<Define |parse_arguments|@> = 337procedure parse_arguments; 338const n_options = 4; {Pascal won't count array lengths for us.} 339var @!long_options: array[0..n_options] of getopt_struct; 340 @!getopt_return_val: integer; 341 @!option_index: c_int_type; 342 @!current_option: 0..n_options; 343begin 344 @<Initialize the option variables@>; 345 @<Define the option table@>; 346 repeat 347 getopt_return_val := getopt_long_only (argc, argv, '', long_options, 348 address_of (option_index)); 349 if getopt_return_val = -1 then begin 350 {End of arguments; we exit the loop below.} ; 351 352 end else if getopt_return_val = "?" then begin 353 usage (my_name); 354 355 end else if argument_is ('help') then begin 356 usage_help (TFTOPL_HELP, nil); 357 358 end else if argument_is ('version') then begin 359 print_version_and_exit (banner, nil, 'D.E. Knuth', nil); 360 361 end else if argument_is ('charcode-format') then begin 362 if strcmp (optarg, 'ascii') = 0 then 363 charcode_format := charcode_ascii 364 else if strcmp (optarg, 'octal') = 0 then 365 charcode_format := charcode_octal 366 else 367 print_ln ('Bad character code format ', stringcast(optarg), '.'); 368 369 end; {Else it was a flag; |getopt| has already done the assignment.} 370 until getopt_return_val = -1; 371 372 {Now |optind| is the index of first non-option on the command line.} 373 if (optind + 1 <> argc) and (optind + 2 <> argc) then begin 374 print_ln (my_name, ': Need one or two file arguments.'); 375 usage (my_name); 376 end; 377 378 tfm_name := cmdline (optind); 379end; 380 381@ Here are the options we allow. The first is one of the standard GNU options. 382@.-help@> 383 384@<Define the option...@> = 385current_option := 0; 386long_options[current_option].name := 'help'; 387long_options[current_option].has_arg := 0; 388long_options[current_option].flag := 0; 389long_options[current_option].val := 0; 390incr (current_option); 391 392@ Another of the standard options. 393@.-version@> 394 395@<Define the option...@> = 396long_options[current_option].name := 'version'; 397long_options[current_option].has_arg := 0; 398long_options[current_option].flag := 0; 399long_options[current_option].val := 0; 400incr (current_option); 401 402@ Print progress information? 403@.-verbose@> 404 405@<Define the option...@> = 406long_options[current_option].name := 'verbose'; 407long_options[current_option].has_arg := 0; 408long_options[current_option].flag := address_of (verbose); 409long_options[current_option].val := 1; 410incr (current_option); 411 412@ 413@<Glob...@> = 414@!verbose: c_int_type; 415 416@ 417@<Initialize the option...@> = 418verbose := false; 419 420@ This option changes how we output character codes. 421@.-charcode-format@> 422 423@<Define the option...@> = 424long_options[current_option].name := 'charcode-format'; 425long_options[current_option].has_arg := 1; 426long_options[current_option].flag := 0; 427long_options[current_option].val := 0; 428incr (current_option); 429 430@ We use an ``enumerated'' type to store the information. 431 432@<Type...@> = 433@!charcode_format_type = charcode_ascii..charcode_default; 434 435@ 436@<Const...@> = 437@!charcode_ascii = 0; 438@!charcode_octal = 1; 439@!charcode_default = 2; 440 441@ 442@<Global...@> = 443@!charcode_format: charcode_format_type; 444 445@ It starts off as the default, that is, we output letters and digits as 446ASCII characters, everything else in octal. 447 448@<Initialize the option...@> = 449charcode_format := charcode_default; 450 451@ An element with all zeros always ends the list. 452 453@<Define the option...@> = 454long_options[current_option].name := 0; 455long_options[current_option].has_arg := 0; 456long_options[current_option].flag := 0; 457long_options[current_option].val := 0; 458 459@ Global filenames. 460 461@<Global...@> = 462@!tfm_name, @!pl_name:const_c_string; 463@z 464