1 /*  $Id: devnag.c,v 1.15 2008-03-09 15:57:59 icebearsoft Exp $
2     Version 2.15
3 
4  *
5  Preprocessor for Devanagari for TeX package
6  Copyright (C) 1991-1998  University of Groningen, The Netherlands
7  *
8  Author   : Frans J. Velthuis <velthuis@rc.rug.nl>
9  Date     : 09 May 1991
10  *
11  The maintainer of this program is now Zdenek Wagner <zdenek.wagner@gmail.com>.
12  *
13  This program is free software; you can redistribute it and/or modify
14  it under the terms of the GNU General Public License as published by
15  the Free Software Foundation; either version 1, or (at your option)
16  any later version.
17  *
18  This program is distributed in the hope that it will be useful,
19  but WITHOUT ANY WARRANTY; without even the implied warranty of
20  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  GNU General Public License for more details.
22  *
23  You should have received a copy of the GNU General Public License
24  along with this program; if not, write to the Free Software
25  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26  */
27 
28 /*
29  Modified by Marc Csernel, March 1998, to support LaTeX commands
30  with arguments and environments between the dn delimiters.
31  */
32 
33 /*
34  Version 2.0 of devnag is the first major revision of the program
35  since its original release. It is based on version 1.6 (there are
36  no releases 1.7, 1.8 or 1.9), and, in addition to Marc Csernel's
37  LaTeX extensions, it incorporates the following modifications made
38  by John Smith in 1998-9:
39 
40  1.  A major change has been made to the way in which the
41      preprocessor handles "@" commands. Previously it was only
42      possible to use these at the start of the .dn file; now they may
43      be placed anywhere in the text, provided that they do not occur
44      within passages of Devanagari (where "@" has its own meaning).
45      New "negative" commands have been added to reverse the effect of
46      most existing commands: thus it is now possible to enable or
47      disable specific features for specific passages of text. @hindi
48      may be disabled with @sanskrit; @dollars with @nodollars or
49      @dolmode0; @hyphen with @nohyphen; @tabs (a new LaTeX feature
50      added by Marc Csernel) with @notabs. Since "@" is a perfectly
51      legal character in TeX, lines beginning with "@" that do not
52      match any valid "@" command are flagged with a warning, but
53      processing of the file continues. (In the somewhat unlikely
54      event that you actually need to have a line of TeX text
55      consisting exactly of, say, "@hindi", the preprocessor may be
56      fooled by typing "{}@hindi", "{@hindi}", or anything similar.)
57 
58  2.  The old line-splitting code and the @obeylines preprocessor
59      command to disable it have been withdrawn, so that the program
60      now outputs one line to the .tex file for every line of the
61      .dn file. In devnag 2.0, attempted use of @obeylines produces
62      a specific warning; this is likely to be withdrawn in a future
63      release, after which attempts to use it will cause "@obeylines"
64      to be typeset as a part of the text of the document (see 1.
65      above).
66 
67  3.  To complete the line-by-line correspondence between the input
68      and output files, comments are no longer stripped out, and
69      preprocessor commands (such as "@hyphen") are passed through
70      into the output file in the form of comments ("%@hyphen").
71 
72  4.  There is now an option to show the version of the program. If
73      devnag is invoked as "devnag -v" it will print a banner giving
74      the version number and other information, and then exit.
75 
76  5.  If the input file has a name such as "x.y.dn", the output file
77      will now be "x.y.tex", not, as previously, "x.tex". In addition,
78      it is now mandatory for the files to have the suffixes ".dn" and
79      ".tex", though as before these are supplied by the program if
80      omitted by the user. This change has been made for safety
81      reasons: formerly, typing "devnag myfile.tex" would delete
82      the contents of myfile.tex.
83 
84  6.  The sequence ~nj~na now produces the correct form.
85 
86  7.  The sequences r.l and r.L now produce the correct forms.
87 
88  8.  The sequences ttrya, ttrva, khrya and khrva now produce the
89      correct forms.
90 
91  9.  The correct "caret" form of -r is now produced under kh, ch,
92      etc., even when a subscript vowel or viraama follows. This has
93      necessitated a new macro (\qc), which must be defined in
94      dnmacs.tex (for Plain TeX) or dev.sty (for LaTeX). The
95      definition is:
96 
97      \def\qc#1#2{\setbox0=\hbox{#1}#1\subscr{\char126\kern1.5ex\lower1.25ex
98      \hbox{\char#2}\kern-1.5ex}}
99 
100      Suitable versions of the files are distributed with the current
101      release of the devnag package.
102 
103  10. A fragment of code which permitted hyphenation in the middle of
104      a consonant sequence containing a viraama has been removed.
105      All hyphenation before consonants stopped with viraama has
106      also been eliminated.
107 
108  11. A change has been introduced in the output that devnag produces
109      from input such as {\dn .sa.t"siraa.h}, i.e. in cases where an
110      i-matra vowel is associated with a consonant sequence containing
111      a viraama. The previous behaviour was to treat the consonant
112      sequence as if it were a normal conjunct by placing the vowel
113      symbol before the sequence as a whole. The majority opinion
114      seems to be that this is undesirable, and that the vowel symbol
115      should follow the consonant to which the viraama is attached.
116      A change has therefore been made to implement this. However, the
117      previous behaviour can be reinstated by means of a new
118      preprocessor command, @vconjuncts.
119 
120  12. Tab characters in the input file, which previously were treated
121      as fatal errors, are now silently converted to spaces.
122 
123  13. (Internal coding changes) The order of the C code has been
124      normalised from the original Pascal-like bottom-up sequence
125      to a more conventional top-down version. Numerous comments have
126      been added to assist potential future developers in understanding
127      the way the program works. A number of bug-fixes and other
128      improvements have been applied. The insecure gets() calls used
129      in prompting for user input have been replaced by fgets().
130      Variables used only within a single function have been declared
131      local to that function.  Some variables have been renamed in
132      the interests of clarity and consistency, and some redundant
133      ones have been eliminated. The functions ch_find() and st_find(),
134      which simply duplicated the standard library functions strchr()
135      and strstr(), have also been eliminated.
136  */
137 
138 /*
139  Version 2.01 is a bugfix release, containing two changes in
140  coding. (1) Previous versions of devnag have output "[" and "]"
141  for Devanagari half-"sa and half-gha. Under some circumstances
142  this can interact badly with LaTeX, and the form of output has
143  been changed to avoid the problem. (2) \cline has been added to
144  the list of LaTeX commands which devnag knows how to handle.
145  -- John Smith
146  */
147 
148 /*
149  Version 2.02 redefines all ^^* characters to be output as
150  \3XXw because it is more robust. It puts a definition
151  of \DevnagVersion at the beginning of the output file so
152  that the new preprocessed files can be automatically recognized
153  (Z. Wagner, zdenek.wagner@gmail.com, http://icebearsoft.euweb.cz).
154  This version also defines a new preprodessor directive
155  @modernhindi, which functions in the same way as @hindi but
156  uses far fewer Sanskrit-style ligatures, preferring conjuncts
157  built from half-consonant forms wherever possible. (Thanks to
158  Raymond Doctor for helpful advice on which ligatures to permit
159  and which to suppress.) The definition of @sanskrit has been
160  changed so that it now restores all the ligatures disabled by
161  @modernhindi, not just those disabled by @hindi. The sequences
162  kk.sa, .tk.sa, dmya and hmya have been modified to produce
163  k+k.sa, .t+k.sa, d+mya, h+mya (rather than, as previously,
164  kk+.sa, .tk+.sa dm+ya, hm+ya). The code which handles all such
165  substitutions has been radically improved. The special warning
166  message  prompted by the long-defunct preprocessor directive
167  @obeylines has been removed.
168  -- John Smith
169  */
170 
171 /*
172  Version 2.1 makes the following changes:
173  *
174  * Material within {\dn ... } (or $ ... $) may be enclosed within
175    angle brackets < ... >; it is then not processed, but is passed
176    through verbatim to the output file, e.g.
177      {\dn eka do <\hrule width 10 em> tiina caara}
178  * Changes to the fonts have made an "open ya" glyph available. This
179    is now used wherever the preprocessor would previously have
180    produced consonant + viraama + ya.
181  * The fonts now contain defined glyphs for .n.na; the preprocessor
182    uses these.
183  * In previous releases the "half j~na" glyph was not used in @hindi
184    and @modernhindi modes (the program output "half ja + half ~na").
185    This has been corrected.
186  * The output now avoids using the following control symbols: \", \#,
187    \$, \%, \&, \'.
188  -- John Smith
189  */
190 
191 /*
192  Version 2.11 is a bugfix release to deal with two problems:
193  *
194  * Newline characters were not being properly handled in material
195    enclosed within angle brackets < ... >;
196  * The input sequence ~a was producing the wrong output for the
197    initial form of the vowel.
198  -- John Smith
199  */
200 
201 /*
202  Version 2.12 contains two modification by Kevin Carmody, one to add
203  the ligature disabling character "+", and one bug fix to avoid the use
204  of non-standard syntax in invocations of fixconj().
205  */
206 
207 /*
208  Bug fix suggested by John Smith: put_syll() just after case '<' in order
209  to emit pending syllable. The verbatim material should appear between
210  words but you can even write {\dn pa<\kern -2pt >de} if you know what
211  you are doing. However, such use is strongly discouraged.
212  */
213 
214 /*
215  Version 2.13: ~m is now a synonym for /, i.e. candrabindu
216 */
217 
218 /*
219  Version 2.14 contains the following modifications:
220 
221  Accented characters with codes >= 128 are allowed in \dn texts
222  within {\rm ...} and <...>
223  UTF-8 is not yet supported.
224 
225  The filename extensions are not fixed but have reasoanble defaults
226  defined by constants DEFAULT_SRC_EXT (= ".dn") and
227  DEFAULT_DEST_EXT (= ".tex"). It is explicitly forbidden for the source
228  file to have the extension defined by DEFAULT_DEST_EXT. The file names
229  are examined as follows:
230 
231  1. If the source filename does not have the default extension, it is
232     appended. If such a file does not exist, the preprocessor tries
233     again with the original file name.
234 
235  2. If the destination filename was given, its extension is checked.
236     It is explicitly forbidden for the destination file to have the
237     extension defined by DEFAULT_SRC_EXT. If the filename is equal
238     to the name of the source file, DEFAULT_DEST_EXT is appended,
239     otherwise the file name is used as is.
240 
241  3. If the destination filename was not given, it is based upon the
242     source filename. If the source filename contains the
243     DEFAULT_SRC_EXT extension, it is stripped of. Afterwards the
244     DEFAULT_DEST_EXT extension is appended.
245 
246  The algorithm was designed to prevent typing errors but it is far
247  from foolproof. The filesystem properties are not examined and all
248  filename tests are case insensitive. The file names are not expanded.
249  For instance, if the working directory is "/some/path", then "x.y",
250  "./x.y", "../path/x.y" as well as "/some/path/x.y" refer to the same
251  file but they will be treated as different by the preprocessor.
252  Moreover, the algorithm does not try to follow symlinks neither does it
253  examine inodes in order to discover hard links.
254 
255  Notes for compilation:
256  Some C libraries do not contain strcasecmp but contain either stricmp
257  or strcmpi (or both) with the same syntax. If it is the case of your
258  compiler, use -DHAS_STRCMPI or -DHAS_STRICMP. GCC/EMX for OS/2
259  requires -DHAS_STRICMP, gcc 3.3.3 in Linux as well as gcc 2.95.2
260  from mingw (Windows) know strcasecmp.
261 
262  The preprocessor can read files with any line endings on all platforms
263  (i.e. DOS CRLF, UNIX LF as well as Macintosh CR). More precisely: each
264  CR followed by LF is ignored and each CR followed by anything else is
265  treated as end of line. The line endings of the output file always
266  conform to conventions used on the operating system where the
267  preprocessor runs.
268 */
269 
270 /*
271  Modifications in version 2.15:
272 
273  \def\DevnagVersion is inserted at the beginning of the second line
274  if the first line starts with %&
275 
276  Error and warning messages written to stderr instead of stdout
277  (requested by Karl Berry)
278 */
279 
280 const char *version = "2.15";
281 
282 #include <stdio.h>
283 #include <ctype.h>
284 #include <string.h>
285 #include <stdlib.h>                       /* Marc Csernel */
286 
287 /* General constants */
288 #define TRUE 1
289 #define FALSE 0
290 #define VIRAAM 94
291 #define SMALLBUF 30
292 #define MEDBUF 512
293 #define MAXBUF 2048
294 #define N_NOLIGS (sizeof nolig / sizeof nolig[0])
295 #define N_MNOLIGS (sizeof modnolig / sizeof modnolig[0])
296 #define ill_char  29
297 #define dummy 30
298 #define end_of_file 30
299 #define end_of_line 31
300 
301 /*
302  * Constants used in char_def structs
303  */
304 /* ch_typ constants */
305 #define illegal 0
306 #define cmr 1
307 #define control 2
308 #define dn 3
309 #define numeral 4
310 /* ch_subtyp constants */
311 #define lo_vowel 0
312 #define hi_vowel 1
313 #define consonant 2
314 #define special 3
315 
316 /* Constants representing symbols: defined as indexes into out_string[] */
317 #define LBRACE 273
318 #define RBRACE 264
319 #define RE 263             /* \thinspace on switching from cmr to dn */
320 #define RDT 266            /* raised r-dot (repha + anusvara) */
321 #define RN 265             /* numeral, using cmr or dn as appropriate */
322 #define RS 256             /* \thinspace on switching from dn to cmr */
323 
324 /* Default extensions */
325 #define DEFAULT_SRC_EXT ".dn"
326 #define DEFAULT_DEST_EXT ".tex"
327 
328 /*
329 Some C libraries do not have strcasecmp but have either strcmpi or stricmp
330 with the same syntax. Select the one you have.
331 */
332 #ifdef HAS_STRCMPI
333 #define strcasecmp(s1,s2) strcmpi(s1,s2)
334 #elif HAS_STRICMP
335 #define strcasecmp(s1,s2) stricmp(s1,s2)
336 #endif
337 
338 /* Global variable declarations */
339 FILE *f_in, *f_out;
340 char *p_in;
341 int charbuf, wasCR = FALSE, charpresent = FALSE;
342 int number;
343 char symbol;
344 char infil[MEDBUF], outfil[MEDBUF];
345 char inbuf[MAXBUF], outbuf[MAXBUF];
346 char word[MEDBUF];
347 short syll[SMALLBUF];
348 int n_halves, buf_idx;
349 short half_codes[SMALLBUF];
350 short joincode;
351 unsigned char hindi_mode, mhindi_mode, dollar_mode, d_found;
352 unsigned char tabs_mode;                   /* Marc Csernel */
353 unsigned char no_dn, wait_syll, do_hyphen, do_vconjuncts;
354 unsigned char cons_seen, vow_seen, front_r, cmr_mode, num_mode;
355 unsigned char lig_block;                   /* Kevin Carmody */
356 short chr_idx, cons_code;
357 const char *banner =
358 "Preprocessor for Devanagari for TeX package\n\
359 Copyright (C) 1991-1998  University of Groningen, The Netherlands\n\
360 Author     : Frans J. Velthuis <velthuis@rc.rug.nl>\n\
361 Maintainer : Zdenek Wagner <zdenek.wagner@gmail.com>";
362 
363 /* Function declarations */
364 int main(int argc, char **argv);
365 void dnproc(void);
366 void put_ch(short code);
367 void sendchar(char c);
368 void put_sym(short code);
369 void put_word(void);
370 void put_syll(void);
371 void tst_half(void);
372 void put_macro(short macro);
373 void err_ill(const char *str);
374 char inp_ch(void);
375 void expand(void);
376 char find_dn(void);
377 int sindex(int i, short t);
378 void fixconj(short *wrong, short *right);
379 char *fgetline(char *buf, int n, FILE *f);
380 
381 /* --------- Addition by Marc Csernel 1998 --------- */
382 
383 int citation;
384 int optchapter;
385 int linenumber;
386 int test_command(void);
387 char test_sub_com(void);
388 char comm_double_args(void);
389 char comm_special(void);
390 char comm_opt(void);
391 char comm_chapter(void);
392 char comm_cite(void);
393 char *getsubarg(void);
394 char comm_begin(void);
395 char ignore(void);
396 
397 /* ------------------ End Addition ----------------- */
398 
399 /*
400  * *** Struct declarations: major data types ***
401  */
402 
403 /*
404  A struct of type char_def contains the following information about
405  any given character:
406 
407  ch_typ:     dn      -- a character of the Devanagari syllabary
408 	     cmr     -- a Roman character available within {\dn }
409 	     numeral -- self-evident: either Devanagari or ASCII as
410 			specified
411 	     control -- special TeX character ("\", "{", "}", etc.)
412 	     illegal -- none of the above
413  ch_subtyp:  consonant
414 	     lo_vowel (subscript)
415 	     hi_vowel ("regular" and superscript)
416 	     special  -- none of the above
417  ch_code:    For ch_typ dn only, gives the octal value of the
418 	     character's position in the dvng fonts. For vowels,
419 	     refers to the initial form (ch_subcode refers to
420 	     non-initial form); composite forms such as initial "o"
421 	     (= initial "a" + non-initial "o") have special values
422 	     greater than 257 decimal.
423  ch_subcode: Used for ch_typ dn only. For vowels, gives the octal
424 	     value of the non-initial form ("a" has the special value
425 	     257 decimal). For consonants, gives the index into
426 	     cons_table[] where the "half-form" of the consonant is
427 	     defined.
428 */
429 struct  char_def {
430    short ch_typ, ch_subtyp, ch_code, ch_subcode;
431 };
432 
433 /*
434  A struct of type cons_joins contains the following information about
435  any given consonant or consonant-group Ca:
436 
437  n_ligs:   The number of "simple" (two-member) ligatures listed in
438 	   lig_table[] for Ca.
439  lig_code: The index into lig_table[] of the first such ligature.
440  r_type:   Has the value 0 if Ca takes the normal form of following "r"
441 	   (decimal 125) or if "Cra" is a predefined ligature; 1 if Ca
442 	   requires the "caret" form of following "r" (decimal 126);
443 	   2 if "Cra" is handled as "\qb{Ca}".
444  j_code:   Gives the octal value of the "half-form" of Ca if one exists,
445 	   otherwise 0.
446  */
447 struct cons_joins {
448    short n_ligs, lig_code;
449    short r_type, j_code;
450 };
451 
452 /*
453  A struct of type ligs contains the following information about any
454  given ligature:
455 
456  sym_code: The index into char_table[] of the final member of the
457            ligature. May be expressed as as a character constant
458            (e.g. 't') or as a number (e.g. 6, referring to ".s").
459  sym_new:  Gives either (a) the octal value of the ligature, or (b) a
460 	   negative number to be handled by expand(), which will reverse
461 	   the sign and subtract 1 (-5 becomes 4), and use the result as
462 	   an index into r_ligs[]. (This is done for "ktra", "gra",
463 	   ".dra", "dra", "dhra" and ".s.tra".)
464  join_idx: Gives the index into cons_table[] of the ligature, or 0.
465  in_use:   Boolean TRUE or FALSE according to whether the ligature is
466 	   currently enabled (they are all initially enabled in
467 	   lig_table[], but may be disabled by e.g. @hindi).
468  */
469 struct ligs {
470    char sym_code;
471    short sym_new, join_idx;
472    unsigned char in_use;
473 };
474 
475 /*
476  * *** Data structures used by devnag ***
477  */
478 
479 /* Ligatures to be disabled by @hindi */
480 short nolig[] = {
481    2,3,6,7,16,25,27,34,35,39,40,46,47,49,56,58,59,62,65,
482    78,81,82,84,85,86,87,88,89,90,91,92,93,94,95,98,99,
483    100,101,105
484 };
485 
486 /* Ligatures to be disabled by @modernhindi */
487 short modnolig[] = {
488    0,1,2,3,4,6,7,9,10,11,12,14,16,17,18,19,20,21,22,23,
489    24,25,26,27,28,29,30,31,32,33,34,35,36,39,40,41,42,43,
490    44,45,46,47,48,49,50,52,53,54,56,58,59,62,64,65,69,70,
491    71,72,73,74,75,76,77,78,80,81,82,84,85,86,87,88,89,90,91,
492    92,93,94,95,98,99,100,101,102,103,104,105,118,119
493 };
494 
495 /*
496  * ligidxs[] maps the ligature-numbers that the user may specify
497  * with @lig and @nolig to the appropriate entries in lig_table[]
498  */
499 short ligidxs[] = {
500    0,1,2,3,4,6,7,9,11,12,13,14,118,16,17,18,19,20,21,22,
501    23,24,25,26,27,28,29,30,31,32,33,34,35,36,38,39,40,41,
502    42,43,44,45,46,47,48,49,50,52,53,119,54,55,56,58,59,60,
503    61,62,63,64,65,66,68,69,70,71,72,73,74,75,76,77,78,80,
504    81,82,84,85,86,87,88,89,90,91,92,93,94,95,96,98,99,100,
505    101,102,104,117,105,106,107,108,109,110,111,112,113
506 };
507 
508 /*
509  * In the following character sets, final space is a dummy value
510  */
511 /* Characters that may be preceded by "." */
512 char chset1[] = {
513    'k','t','d','o','n','s','r','g',
514    'h','a','.','K','T','D','l','L','R','m',' '
515 };
516 /* Characters with a special meaning if preceded by "a" */
517 char chset2[] = {'a','i','u',' '};
518 /* Characters that may be preceded by '"' */
519 char chset3[] = {'n','s',' '};
520 /* Characters that may be followed by "h" */
521 char chset4[] = {'k','g','c','j','t','d','p','b','R',' '};
522 /* Replacements for chset2 characters when preceded by "a" */
523 char chset5[] = {'A','E','O'};
524 /* Characters that may be preceded by "~" */
525 char chset6[] = {'n','o','a','r','m',' '};
526 
527 /*
528  * Private character set used internally by devnag -- basically
529  * ASCII, but with special characters allotted slots below 32:
530  * conversion from input representation done by dnproc().
531  */
532 struct char_def char_table[] = {
533      {illegal,special,0,0},                /* 1 not used     */
534      {dn,consonant,'\126',34},             /* 2 .t           */
535      {dn,consonant,'\130',36},             /* 3 .d           */
536      {dn,special,'\72',0},                 /* 4 .o           */
537      {dn,consonant,'\132',1},              /* 5 .n           */
538      {dn,consonant,'\161',2},              /* 6 .s           */
539      {dn,lo_vowel,'\33',2},                /* 7 .r           */
540      {dn,consonant,'\13',3},               /* 8 .g           */
541      {dn,special,'\54',0},                 /* 9 .h           */
542      {dn,special,'\137',0},                /* 10 .a          */
543      {dn,special,'\24',0},                 /* 11 ..          */
544      {dn,consonant,'\14',4},               /* 12 .K          */
545      {dn,consonant,'\127',35},             /* 13 .T          */
546      {dn,consonant,'\131',57},             /* 14 .D          */
547      {dn,lo_vowel,'\30','\37'},            /* 15 .l          */
548      {dn,lo_vowel,'\31','\174'},           /* 16 .L          */
549      {dn,lo_vowel,'\21','\16'},            /* 17 .R          */
550      {dn,consonant,'\122',32},             /* 18 "n          */
551      {dn,consonant,'\146',5},              /* 19 "s          */
552      {dn,consonant,'\170',0},              /* 20 Rh          */
553      {dn,consonant,'\32',11},              /* 21 ~n          */
554      {dn,hi_vowel,267,262},                /* 22 ~o          */
555      {dn,hi_vowel,275,4},                  /* 23 ~a          */
556      {dn,consonant,'\35',31},              /* 24 ~r          */
557      {illegal,special,0,0},                /* 25 not used    */
558      {illegal,special,0,0},                /* 26 not used    */
559      {illegal,special,0,0},                /* 27 not used    */
560      {illegal,special,0,0},                /* 28 not used    */
561      {illegal,special,0,0},                /* 29 not used    */
562      {control,special,0,0},                /* 30 dummy       */
563      {control,special,0,0},                /* 31 end_of_line */
564      {control,special,0,0},                /* 32 space       */
565      {cmr,special,0,0},                    /* !              */
566      {illegal,special,0,0},                /* "              */
567      {dn,special,'\25',0},                 /* #              */
568      {illegal,special,0,0},                /* $              */
569      {control,special,0,0},                /* %              */
570      {dn,special,0,0},                     /* &              */
571      {cmr,special,0,0},                    /* ' to *         */
572      {cmr,special,0,0},                    /* ' to *         */
573      {cmr,special,0,0},                    /* ' to *         */
574      {cmr,special,0,0},                    /* ' to *         */
575      {dn,special,0,0},                     /* +              */
576                                            /*  Kevin Carmody:
577                                             *  "+" changed
578                                             *  from cmr
579                                             *  to dn         */
580      {cmr,special,0,0},                    /* , to -         */
581      {cmr,special,0,0},                    /* , to -         */
582      {illegal,special,0,0},                /* .              */
583      {dn,special,'\40',0},                 /* /              */
584      {numeral,special,0,0},                /* 0 to 9         */
585      {numeral,special,0,0},                /* 0 to 9         */
586      {numeral,special,0,0},                /* 0 to 9         */
587      {numeral,special,0,0},                /* 0 to 9         */
588      {numeral,special,0,0},                /* 0 to 9         */
589      {numeral,special,0,0},                /* 0 to 9         */
590      {numeral,special,0,0},                /* 0 to 9         */
591      {numeral,special,0,0},                /* 0 to 9         */
592      {numeral,special,0,0},                /* 0 to 9         */
593      {numeral,special,0,0},                /* 0 to 9         */
594      {cmr,special,0,0},                    /* : and ;        */
595      {cmr,special,0,0},                    /* : and ;        */
596      {control,special,0,0},                /* <              */
597      {cmr,special,0,0},                    /* =              */
598      {control,special,0,0},                /* >              */
599      {cmr,special,0,0},                    /* ?              */
600      {dn,special,'\177',0},                /* @              */
601      {dn,hi_vowel,258,'\101'},             /* A              */
602      {dn,consonant,'\102',6},              /* B              */
603      {dn,consonant,'\103',33},             /* C              */
604      {dn,consonant,'\104',7},              /* D              */
605      {dn,hi_vowel,259,'\173'},             /* E              */
606      {illegal,special,0,0},                /* F              */
607      {dn,consonant,'\107',8},              /* G              */
608      {dn,special,'\54',0},                 /* H              */
609      {dn,hi_vowel,'\111','\106'},          /* I              */
610      {dn,consonant,'\112',9},              /* J              */
611      {dn,consonant,'\113',10},             /* K              */
612      {dn,consonant,'\17',30},              /* L              */
613      {dn,special,'\134',0},                /* M              */
614      {illegal,special,0,0},                /* N              */
615      {dn,hi_vowel,260,'\117'},             /* O              */
616      {dn,consonant,'\120',12},             /* P              */
617      {illegal,special,0,0},                /* Q              */
618      {dn,consonant,'\167',0},              /* R              */
619      {illegal,special,0,0},                /* S              */
620      {dn,consonant,'\124',13},             /* T              */
621      {dn,lo_vowel,'\125',1},               /* U              */
622      {illegal,special,0,0},                /* V to Z         */
623      {illegal,special,0,0},                /* V to Z         */
624      {illegal,special,0,0},                /* V to Z         */
625      {illegal,special,0,0},                /* V to Z         */
626      {illegal,special,0,0},                /* V to Z         */
627      {cmr,special,0,0},                    /* [              */
628      {control,special,0,0},                /* \              */
629      {cmr,special,0,0},                    /* ]              */
630      {illegal,special,0,0},                /* ^              */
631      {dn,special,0,0},                     /* _              */
632 					   /*  Marc Csernel:
633 					    *  "_" changed
634 					    *  from illegal
635 					    *  to dn         */
636      {cmr,special,0,0},                    /* `              */
637      {dn,hi_vowel,'\141',257},             /* a              */
638      {dn,consonant,'\142',14},             /* b              */
639      {dn,consonant,'\143',15},             /* c              */
640      {dn,consonant,'\144',37},             /* d              */
641      {dn,hi_vowel,'\145',3},               /* e              */
642      {dn,consonant,'\47',16},              /* f              */
643      {dn,consonant,'\147',17},             /* g              */
644      {dn,consonant,'\150',38},             /* h              */
645      {dn,hi_vowel,'\151','\105'},          /* i              */
646      {dn,consonant,'\152',18},             /* j              */
647      {dn,consonant,'\153',19},             /* k              */
648      {dn,consonant,'\154',20},             /* l              */
649      {dn,consonant,'\155',21},             /* m              */
650      {dn,consonant,'\156',22},             /* n              */
651      {dn,hi_vowel,261,'\157'},             /* o              */
652      {dn,consonant,'\160',23},             /* p              */
653      {dn,consonant,'\52',24},              /* q              */
654      {dn,consonant,'\162',0},              /* r              */
655      {dn,consonant,'\163',25},             /* s              */
656      {dn,consonant,'\164',26},             /* t              */
657      {dn,lo_vowel,'\165',0},               /* u              */
658      {dn,consonant,'\166',27},             /* v              */
659      {illegal,special,0,0},                /* not used       */
660      {illegal,special,0,0},                /* not used       */
661      {dn,consonant,'\171',28},             /* y              */
662      {dn,consonant,'\51',29},              /* z              */
663      {control,special,0,0},                /* left brace     */
664      {dn,special,'\56',0},                 /* |              */
665      {control,special,0,0},                /* right brace    */
666      {illegal,special,0,0},                /* ~              */
667      {illegal,special,0,0}                 /* del            */
668 };
669 
670 /*
671  * Unordered table of consonants and consonant-groups giving
672  * information on the available ligatures, the form taken by a
673  * following "r", and the location in the dvng fonts of the
674  * "half-form" (if any)
675  */
676 struct cons_joins cons_table[] = {
677      {0,0,1,0},                            /* 0          */
678      {1,120,0,'\27'},                      /* 1 .n       */
679      {2,100,0,'\11'},                      /* 2 .s       */
680      {0,0,0,'\34'},                        /* 3 .g       */
681      {0,0,1,'\7'},                         /* 4 .k       */
682      {6,94,0,'\133'},                      /* 5 "s       */
683      {1,88,0,'\74'},                       /* 6 B        */
684      {2,78,0,'\100'},                      /* 7 D        */
685      {1,16,0,'\135'},                      /* 8 G        */
686      {0,0,1,'\44'},                        /* 9 J        */
687      {0,0,1,'\110'},                       /* 10 K       */
688      {2,39,1,'\26'},                       /* 11 ~n      */
689      {1,115,0,'\45'},                      /* 12 P       */
690      {0,0,0,'\114'},                       /* 13 T       */
691      {3,85,0,'\116'},                      /* 14 b       */
692      {2,34,0,'\121'},                      /* 15 c       */
693      {1,116,0,'\10'},                      /* 16 f       */
694      {1,15,0,'\140'},                      /* 17 g       */
695      {2,37,0,'\76'},                       /* 18 j       */
696      {9,0,0,'\77'},                        /* 19 k       */
697      {1,91,0,'\123'},                      /* 20 l       */
698      {2,89,0,'\115'},                      /* 21 m       */
699      {1,80,0,'\6'},                        /* 22 n       */
700      {4,81,0,'\75'},                       /* 23 p       */
701      {0,0,1,'\12'},                        /* 24 q       */
702      {2,105,0,'\55'},                      /* 25 s       */
703      {3,55,0,'\50'},                       /* 26 t       */
704      {2,92,0,'\46'},                       /* 27 v       */
705      {0,0,1,'\5'},                         /* 28 y       */
706      {1,114,0,'\36'},                      /* 29 z       */
707      {0,0,1,'\20'},                        /* 30 L       */
708      {0,0,1,'\35'},                        /* 31 ~r      */
709      {8,17,1,0},                           /* 32 "n      */
710      {1,36,1,0},                           /* 33 C       */
711      {4,41,1,0},                           /* 34 .t      */
712      {1,45,1,0},                           /* 35 .th     */
713      {6,46,1,0},                           /* 36 .d      */
714      {11,58,2,0},                          /* 37 d       */
715      {7,107,1,0},                          /* 38 h       */
716      {3,9,1,0},                            /* 39 k t     */
717      {1,14,1,0},                           /* 40 k v     */
718      {3,25,1,0},                           /* 41 "n k    */
719      {1,28,1,0},                           /* 42 "n kh   */
720      {1,29,1,0},                           /* 43 "n g    */
721      {2,30,1,0},                           /* 44 "n gh   */
722      {1,32,1,0},                           /* 45 "n k t  */
723      {1,33,1,0},                           /* 46 "n k .s */
724      {1,52,1,0},                           /* 47 .d g    */
725      {1,53,1,0},                           /* 48 .d gh   */
726      {2,71,2,0},                           /* 49 d d     */
727      {2,73,2,0},                           /* 50 d dh    */
728      {1,75,1,0},                           /* 51 d bh    */
729      {1,77,2,0},                           /* 52 d v     */
730      {3,102,1,0},                          /* 53 .s .t   */
731      {0,0,1,'\43'},                        /* 54 k .s    */
732      {0,0,1,0202},                         /* 55 t t     */
733      {1,117,1,0},                          /* 56 .s .t r */
734      {1,54,1,0},                           /* 57 .dh     */
735      {1,12,1,0},                           /* 58 k n     */
736      {1,13,1,0},                           /* 59 k r     */
737      {0,0,1,0351},                         /* 60 g r     */
738      {0,0,1,0350},                         /* 61 gh n    */
739      {0,0,1,0352},                         /* 62 j ~n    */
740      {0,0,1,0353},                         /* 63 ~n c    */
741      {0,0,1,0354},                         /* 64 t r     */
742      {1,69,1,0},                           /* 65 d g     */
743      {1,70,1,0},                           /* 66 d gh    */
744      {0,0,1,0361},                         /* 67 dh n    */
745      {0,0,1,0362},                         /* 68 dh r    */
746      {0,0,1,0363},                         /* 69 p t     */
747      {0,0,1,0364},                         /* 70 "s c    */
748      {0,0,1,0365},                         /* 71 "s r    */
749      {0,0,1,0366},                         /* 72 "s v    */
750      {1,118,1,0},                          /* 73 k t r   */
751      {1,119,1,0},                          /* 74 .d r    */
752      {1,76,1,0},                           /* 75 d r     */
753      {0,0,2,0}                             /* 76 d b     */
754 };
755 
756 /*
757  * Ordered table of available ligatures giving each ligature's
758  * position in the dvng fonts (or a negative number for use by
759  * expand()) and index into cons_table[]
760  *
761  * Note that lig_table[96] is an entry for a ligature that was
762  * removed from the fonts as of release 2.1. It is therefore
763  * marked as (permanently) FALSE; it has not been removed from
764  * the table since to do so would change the index values of all
765  * succeeding entries, and these occur frequently in the code.
766  */
767 struct ligs lig_table[] = {
768      {'k',0303,0,TRUE},                    /* 0   k k        */
769      {'t',0304,39,TRUE},                   /* 1   k t        */
770      {'n',0307,58,TRUE},                   /* 2   k n        */
771      {'m',0311,0,TRUE},                    /* 3   k m        */
772      {'y',0310,0,TRUE},                    /* 4   k y        */
773      {'r',0207,59,TRUE},                   /* 5   k r        */
774      {'l',0312,0,TRUE},                    /* 6   k l        */
775      {'v',0313,40,TRUE},                   /* 7   k v        */
776      {6,042,54,TRUE},                      /* 8   k .s       */
777      {'y',0305,0,TRUE},                    /* 9   k t y      */
778      {'r',-5,73,TRUE},                     /* 10  k t r      */
779      {'v',0306,0,TRUE},                    /* 11  k t v      */
780      {'y',0346,0,TRUE},                    /* 12  k n y      */
781      {'y',0347,0,TRUE},                    /* 13  k r y      */
782      {'y',0314,0,TRUE},                    /* 14  k v y      */
783      {'r',-2,60,TRUE},                     /* 15  g r        */
784      {'n',0315,61,TRUE},                   /* 16  gh n       */
785      {'k',0254,41,TRUE},                   /* 17  "n k       */
786      {'K',0262,42,TRUE},                   /* 18  "n kh      */
787      {'g',0275,43,TRUE},                   /* 19  "n g       */
788      {'G',0277,44,TRUE},                   /* 20  "n gh      */
789      {18,0274,0,TRUE},                     /* 21  "n "n      */
790      {'n',0265,0,TRUE},                    /* 22  "n n       */
791      {'m',0301,0,TRUE},                    /* 23  "n m       */
792      {'y',0302,0,TRUE},                    /* 24  "n y       */
793      {'t',0255,45,TRUE},                   /* 25  "n k t     */
794      {'y',0257,0,TRUE},                    /* 26  "n k y     */
795      {6,0260,46,TRUE},                     /* 27  "n k .s    */
796      {'y',0272,0,TRUE},                    /* 28  "n kh y    */
797      {'y',0276,0,TRUE},                    /* 29  "n g y     */
798      {'y',0271,0,TRUE},                    /* 30  "n gh y    */
799      {'r',0300,0,TRUE},                    /* 31  "n gh r    */
800      {'y',0256,0,TRUE},                    /* 32  "n k t y   */
801      {'v',0261,0,TRUE},                    /* 33  "n k .s v  */
802      {'c',0316,0,TRUE},                    /* 34  c c        */
803      {21,0317,0,TRUE},                     /* 35  c ~n       */
804      {'y',0320,0,TRUE},                    /* 36  ch y       */
805      {21,0342,62,TRUE},                    /* 37  j ~n       */
806      {'r',0205,0,TRUE},                    /* 38  j r        */
807      {'c',0321,63,TRUE},                   /* 39  ~n c       */
808      {'j',0322,0,TRUE},                    /* 40  ~n j       */
809      {'k',0326,0,TRUE},                    /* 41  .t k       */
810      {2,0323,0,TRUE},                      /* 42  .t .t      */
811      {13,0341,0,TRUE},                     /* 43  .t .th     */
812      {'y',0324,0,TRUE},                    /* 44  .t y       */
813      {'y',0325,0,TRUE},                    /* 45  .th y      */
814      {'g',0263,47,TRUE},                   /* 46  .d g       */
815      {'G',0264,48,TRUE},                   /* 47  .d gh      */
816      {3,0345,0,TRUE},                      /* 48  .d .d      */
817      {'m',0273,0,TRUE},                    /* 49  .d m       */
818      {'y',0267,0,TRUE},                    /* 50  .d y       */
819      {'r',-6,74,TRUE},                     /* 51  .d r       */
820      {'y',0270,0,TRUE},                    /* 52  .d g y     */
821      {'r',0266,0,TRUE},                    /* 53  .d gh r    */
822      {'y',0344,0,TRUE},                    /* 54  .dh y      */
823      {'t',0201,55,TRUE},                   /* 55  t t        */
824      {'n',0327,0,TRUE},                    /* 56  t n        */
825      {'r',057,64,TRUE},                    /* 57  t r        */
826      {'g',0213,65,TRUE},                   /* 58  d g        */
827      {'G',0212,66,TRUE},                   /* 59  d gh       */
828      {'d',0214,49,TRUE},                   /* 60  d d        */
829      {'D',0210,50,TRUE},                   /* 61  d dh       */
830      {'n',0221,0,TRUE},                    /* 62  d n        */
831      {'b',0223,76,TRUE},                   /* 63  d b        */
832      {'B',0211,51,TRUE},                   /* 64  d bh       */
833      {'m',0224,0,TRUE},                    /* 65  d m        */
834      {'y',0215,0,TRUE},                    /* 66  d y        */
835      {'r',-3,75,TRUE},                     /* 67  d r        */
836      {'v',0222,52,TRUE},                   /* 68  d v        */
837      {'r',0355,0,TRUE},                    /* 69  d g r      */
838      {'r',0356,0,TRUE},                    /* 70  d gh r     */
839      {'y',0220,0,TRUE},                    /* 71  d d y      */
840      {'v',0370,76,TRUE},                   /* 72  d d v      */
841      {'y',0217,0,TRUE},                    /* 73  d dh y     */
842      {'v',0371,0,TRUE},                    /* 74  d dh v     */
843      {'y',0216,0,TRUE},                    /* 75  d bh y     */
844      {'y',0357,0,TRUE},                    /* 76  d r y      */
845      {'y',0225,0,TRUE},                    /* 77  d v y      */
846      {'n',0360,67,TRUE},                   /* 78  dh n       */
847      {'r',-4,68,TRUE},                     /* 79  dh r       */
848      {'n',0340,0,TRUE},                    /* 80  n n        */
849      {'t',0330,69,TRUE},                   /* 81  p t        */
850      {'n',0331,0,TRUE},                    /* 82  p n        */
851      {'r',0376,0,TRUE},                    /* 83  p r        */
852      {'l',0332,0,TRUE},                    /* 84  p l        */
853      {'n',0247,0,TRUE},                    /* 85  b n        */
854      {'b',0251,0,TRUE},                    /* 86  b b        */
855      {'v',0333,0,TRUE},                    /* 87  b v        */
856      {'n',0336,0,TRUE},                    /* 88  bh n       */
857      {'n',0337,0,TRUE},                    /* 89  m n        */
858      {'l',0335,0,TRUE},                    /* 90  m l        */
859      {'l',0245,0,TRUE},                    /* 91  l l        */
860      {'n',0246,0,TRUE},                    /* 92  v n        */
861      {'v',0250,0,TRUE},                    /* 93  v v        */
862      {'c',0226,70,TRUE},                   /* 94  "s c       */
863      {'n',0227,0,TRUE},                    /* 95  "s n       */
864      {'b',0233,0,FALSE},                   /* 96  "s b       */
865      {'r',0231,71,TRUE},                   /* 97  "s r       */
866      {'l',0232,0,TRUE},                    /* 98  "s l       */
867      {'v',0230,72,TRUE},                   /* 99  "s v       */
868      {2,0243,53,TRUE},                     /* 100  .s .t     */
869      {13,0244,0,TRUE},                     /* 101 .s .th     */
870      {'y',0367,0,TRUE},                    /* 102 .s .t y    */
871      {'r',-1,56,TRUE},                     /* 103 .s .t r    */
872      {'v',0253,0,TRUE},                    /* 104 .s .t v    */
873      {'n',0334,0,TRUE},                    /* 105  s n       */
874      {'r',0372,0,TRUE},                    /* 106  s r       */
875      {5,0242,0,TRUE},                      /* 107  h .n      */
876      {'n',0241,0,TRUE},                    /* 108  h n       */
877      {'m',0234,0,TRUE},                    /* 109  h m       */
878      {'y',0235,0,TRUE},                    /* 110  h y       */
879      {'r',0240,0,TRUE},                    /* 111  h r       */
880      {'l',0236,0,TRUE},                    /* 112  h l       */
881      {'v',0237,0,TRUE},                    /* 113  h v       */
882      {'r',0206,0,TRUE},                    /* 114  z r       */
883      {'r',0203,0,TRUE},                    /* 115  ph r      */
884      {'r',0204,0,TRUE},                    /* 116  f r       */
885      {'y',0252,0,TRUE},                    /* 117  .s .t r y */
886      {'y',0374,0,TRUE},                    /* 118  k t r y   */
887      {'y',0373,0,TRUE},                    /* 119  .d r y    */
888      {5,0233,0,TRUE}                       /* 120  .n .n     */
889 };
890 
891 /*
892  * Table of the actual codes output by devnag
893  */
894 const char *out_string[] = {
895    "\\7{","\\8{","\\9{","\\?","\\<","\\305w","\\306w",        /*   0-6   */
896    "\\307w","\\308w","\\309w","\\30Aw","\\30Bw","\\30Cw",     /*   7-12  */
897    "\\0","\\qx{","\\30Fw","\\310w","\\311w","\\312w",         /*  13-18  */
898    "\\313w","\\314w","\\315w","\\316w","\\317w","\\318w",     /*  19-24  */
899    "\\319w","\\31Aw","\\31Bw","\\31Cw","\\31Dw",              /*  25-29  */
900    "\\31Ew","\\qy{","\\1","!","\\322w","\\323w","\\324w",     /*  30-36  */
901    "\\325w","\\326w","\\327w","(",")","\\32Aw","+",",",       /*  37-44  */
902    "-",".","/","0","1","2","3","4","5","6","7","8","9",       /*  45-57  */
903    ":",";","<","=",">","?","@",                               /*  58-64  */
904    "A","B","C","D","E","F","G","H","I","J","K","L","M",       /*  65-77  */
905    "N","O","P","Q","R","S","T","U","V","W","X","Y","Z",       /*  78-90  */
906    "\\35Bw","\\2","\\35Dw","\\qq{","\\35Fw","`",              /*  91-96  */
907    "a","b","c","d","e","f","g","h","i","j","k","l","m",       /*  97-109 */
908    "n","o","p","q","r","s","t","u","v","w","x","y","z",       /* 110-122 */
909    "\\4","\\qz{","\\5","\\6{","\\37Fw",                       /* 123-127 */
910    "\\380w","\\381w","\\382w","\\383w","\\384w","\\385w",     /* 128-133 */
911    "\\386w","\\387w","\\388w","\\389w","\\38Aw","\\38Bw",     /* 134-139 */
912    "\\38Cw","\\38Dw","\\38Ew","\\38Fw","\\390w","\\391w",     /* 140-145 */
913    "\\392w","\\393w","\\394w","\\395w","\\396w","\\397w",     /* 146-151 */
914    "\\398w","\\399w","\\39Aw","\\39Bw","\\39Cw","\\39Dw",     /* 152-157 */
915    "\\39Ew","\\39Fw","\\3A0w","\\3A1w","\\3A2w","\\3A3w",     /* 158-163 */
916    "\\3A4w","\\3A5w","\\3A6w","\\3A7w","\\3A8w","\\3A9w",     /* 164-169 */
917    "\\3AAw","\\3ABw","\\3ACw","\\3ADw","\\3AEw","\\3AFw",     /* 170-175 */
918    "\\3B0w","\\3B1w","\\3B2w","\\3B3w","\\3B4w","\\3B5w",     /* 176-181 */
919    "\\3B6w","\\3B7w","\\3B8w","\\3B9w","\\3BAw","\\3BBw",     /* 182-187 */
920    "\\3BCw","\\3BDw","\\3BEw","\\3BFw","\\3C0w","\\3C1w",     /* 188-193 */
921    "\\3C2w","\\3C3w","\\3C4w","\\3C5w","\\3C6w","\\3C7w",     /* 194-199 */
922    "\\3C8w","\\3C9w","\\3CAw","\\3CBw","\\3CCw","\\3CDw",     /* 200-205 */
923    "\\3CEw","\\3CFw","\\3D0w","\\3D1w","\\3D2w","\\3D3w",     /* 206-211 */
924    "\\3D4w","\\3D5w","\\3D6w","\\3D7w","\\3D8w","\\3D9w",     /* 212-217 */
925    "\\3DAw","\\3DBw","\\3DCw","\\3DDw","\\3DEw","\\3DFw",     /* 218-223 */
926    "\\3E0w","\\3E1w","\\3E2w","\\3E3w","\\3E4w","\\3E5w",     /* 224-229 */
927    "\\3E6w","\\3E7w","\\3E8w","\\3E9w","\\3EAw","\\3EBw",     /* 230-235 */
928    "\\3ECw","\\3EDw","\\3EEw","\\3EFw","\\3F0w","\\3F1w",     /* 236-241 */
929    "\\3F2w","\\3F3w","\\3F4w","\\3F5w","\\3F6w","\\3F7w",     /* 242-247 */
930    "\\3F8w","\\3F9w","\\3FAw","\\3FBw","\\3FCw","\\3FDw",     /* 248-253 */
931    "\\3FEw","\\3FFw",                                         /* 254-255 */
932    "{\\rs ","","aA","e\\?","aO","ao","A\\<","\\re}","}",      /* 256-264 */
933    "\\rn{","{\\rdt}","aA\\<","{\\qva}","{\\qvb}","{\\qvc}",   /* 265-270 */
934    "\\qa{","\\qb{","{","\\qc{","a\\<"                         /* 271-275 */
935 };
936 
937 /*
938  * Table specifying the form of following "r" for various consonants
939  * and consonant-groups (used by expand())
940  */
941 short r_ligs[6][2] = {
942      {0243,1},                             /* .s .t */
943      {0147,0},                             /* g     */
944      {0144,2},                             /* d     */
945      {0104,0},                             /* dh    */
946      {0304,1},                             /* k t   */
947      {0130,1}                              /* .d    */
948 };
949 
950 /* --------- Addition by Marc Csernel 1998 --------- */
951 
952 /* The array contains a list of LaTeX commands preceded by a code:
953 
954      1   a mandatory argument follows. e.g. \end{enumerate}
955      2   the text inclosed by a preceding brace must not be translated
956 	 till next closing brace.
957      3   two mandatory arguments follow. e.g. \multicolumn{nb}{desc}{text}
958      4   For begin: a mandatory argument followed by zero, or one optional,
959 	 depending on the first one e.g. \begin{itemize} or
960 	 \begin{tabular}{....}
961      5   a mandatory argument and optional ones which  must not be translated.
962 	 \raisebox{raise}[ht][prof]{text to be translated}
963      6   one or more optional argument(s) which must not be translated and
964 	 a mandatory one which must be translated e.g.
965 	 \framebox[6cm][s]{text to be translated}
966      7   The command cite: an optional argument which must be translated
967 	 and a mandatory one which mustn't.
968      8   Commands like chapter, section... with optional argument which
969 	 must be translated and a mandatory one which must also be
970 	 translated.
971 
972 */
973 
974 typedef struct {
975    int Typ_Com;
976    const char *Name_com;
977 } typcom;
978 
979 typcom TabCom[] = {
980      {4,"begin"},
981      {1,"end"},
982      {1,"label"},
983      {1,"pageref"},
984      {1,"ref"},
985      {1,"index"},
986      {1,"hspace"},
987      {1,"hspace*"},
988      {1,"vspace"},
989      {1,"vspace*"},
990      {1,"addvspace"},
991      {1,"enlargethispage"},
992      {1,"enlargethispage*"},
993      {1,"include"},
994      {1,"input"},
995      {1,"parbox"},
996      {1,"stepcounter"},
997      {1,"refstepcounter"},
998      {1,"bibitem"},
999      {1,"cline"},
1000      {2,"rm"},
1001      {2,"kern"},
1002      {2,"hskip"},
1003      {2,"vskip"},
1004      {2,"vadjust"},
1005      {3,"addtolength"},
1006      {3,"setlength"},
1007      {3,"setcounter"},
1008      {3,"addtocounter"},
1009      {3,"multicolumn"},
1010      {3,"rule"},
1011      {3,"addcontentsline"},
1012      {5,"raisebox"},
1013      {6,"framebox"},
1014      {6,"makebox"},
1015      {7,"cite"},
1016      {8,"chapter"},
1017      {8,"section"},
1018      {8,"subsection"},
1019      {8,"subsubsection"},
1020      {8,"paragraph"},
1021      {8,"subparagraph"},
1022      {8,"part"},
1023      {0,""}
1024 };
1025 
1026 /*
1027  For the command begin, depending on the first argument we will
1028  have zero (default), 1, 2.. other arguments following. Within
1029  the following array the first element of an item describes the
1030  number of arguments, the second one the name of the first
1031  argument related with i.e
1032 
1033 	\begin {tabular}
1034 
1035  will have 1 argument.
1036  */
1037 typcom TabSubCom[]= {
1038      {1,"tabular"},
1039      {1,"supertabular"},
1040      {1,"longtable"},
1041      {1,"thebibliography"},
1042      {0,""}
1043 };
1044 
1045 char command[100];       /* the text of a current command to test */
1046 int nbchcomm;            /* the number of character of the curent command */
1047 char subcom [1000];      /* for debugging etc. */
1048 
1049 /*
1050  This fuction tests if the LaTeX command is one of the TabCom[]
1051  array. In this case the code (Typ_Com) is returned.
1052  */
test_command(void)1053 int test_command(void) {
1054    int i = 0;
1055    while (TabCom[i].Name_com[0] != 0) {
1056       if (!strcmp(command,TabCom[i++].Name_com)) return TabCom[i-1].Typ_Com;
1057    }
1058    return 0;
1059 }
1060 
1061 /*
1062  This routine writes down all characters it finds; it looks
1063  for a '{' followed by some text and a '}'; no nesting of braces
1064  is considered.
1065  */
test_sub_com(void)1066 char test_sub_com(void) {
1067    int nbsub = 0;
1068    while (symbol != '}') {
1069       sendchar(symbol);
1070       subcom[nbsub++] = symbol;
1071       subcom[nbsub] = 0;
1072       symbol = inp_ch();
1073    }
1074    sendchar(symbol);
1075    subcom[nbsub++] = symbol;
1076    subcom[nbsub] = 0;
1077    symbol = inp_ch();
1078    return symbol;
1079 }
1080 
1081 /*
1082  This routine writes down all characters it finds; it looks
1083  twice for a '{' followed by some text and a '}'; for the
1084  second time nesting of braces is considered.
1085  */
comm_double_args(void)1086 char comm_double_args(void) {
1087    int nbsub = 0;
1088    while (symbol != '}') {
1089       sendchar(symbol);
1090       subcom[nbsub++] = symbol;
1091       subcom[nbsub] = 0;
1092       symbol = inp_ch();
1093    }
1094    /* looking for the end of the second argument */
1095    sendchar(symbol);
1096    symbol = inp_ch();
1097    while (symbol != '{') {
1098       sendchar(symbol);
1099       symbol = inp_ch();
1100    }
1101    sendchar(symbol);
1102    symbol = inp_ch();
1103    symbol = ignore();
1104    sendchar(symbol);
1105    symbol = inp_ch();
1106    return symbol;
1107 }
1108 
1109 /*
1110  For LaTeX commands such as \raisebox{5pt}[10pt][25pt]{.sa.t&vidha.m}
1111  where the second argument must be translated
1112  */
comm_special(void)1113 char comm_special(void) {
1114    int nbsub = 0;
1115    while (symbol != '}') {
1116       sendchar(symbol);
1117       subcom[nbsub++] = symbol;
1118       subcom[nbsub] = 0;
1119       symbol = inp_ch();
1120    }
1121    while (symbol != '{') {
1122       sendchar(symbol);
1123       subcom[nbsub++] = symbol;
1124       subcom[nbsub] = 0;
1125       symbol = inp_ch();
1126    }
1127    return symbol;
1128 }
1129 
1130 /*
1131  For LaTeX commands such as \framebox[10pt][25pt]{.sa.t&vidha.m}
1132  where optional arguments are not translated and the mandatory
1133  one must be translated
1134  */
comm_opt(void)1135 char comm_opt(void) {
1136    int nbsub = 0;
1137    while (symbol != '{') {
1138       sendchar(symbol);
1139       subcom[nbsub++] = symbol;
1140       subcom[nbsub] = 0;
1141       symbol = inp_ch();
1142    }
1143    return symbol;
1144 }
1145 
comm_chapter(void)1146 char comm_chapter(void) {
1147    if (symbol == '[') {
1148       optchapter = 1;
1149       sendchar(symbol);
1150       symbol=inp_ch();
1151    }
1152    if (symbol == '*') {
1153       sendchar(symbol);
1154       symbol=inp_ch();
1155    }
1156    return symbol;
1157 }
1158 
comm_cite(void)1159 char comm_cite(void) {
1160    if (symbol == '[') {
1161       citation = 1;
1162       sendchar(symbol);
1163       symbol = inp_ch();
1164    }
1165    else test_sub_com();
1166    return symbol;
1167 }
1168 
getsubarg(void)1169 char *getsubarg(void) {
1170    char com[20], *result;
1171    int i = 0, j = 0;
1172    while (subcom[i] != '{') i++;
1173    i++;
1174    while (subcom[i] == ' ') i++;
1175    while (isalpha((unsigned char)subcom[i])) com[j++]= subcom[i++];
1176    com[j] = 0;
1177    i = 0;
1178    result = (char*) malloc (strlen(com)+1);
1179    strcpy (result,com);
1180    return result;
1181 }
1182 
1183 /*
1184  The LaTeX command BEGIN treatment
1185  */
comm_begin(void)1186 char comm_begin(void) {
1187    int i = 0, nbargs = 0;
1188    char *com;
1189    int nbsub = 0;
1190    while (symbol != '}') {
1191       sendchar(symbol);
1192       subcom[nbsub++] = symbol;
1193       subcom[nbsub] = 0;
1194       symbol = inp_ch();
1195    }
1196    sendchar(symbol);
1197    symbol = inp_ch();
1198    com = getsubarg();
1199    while (TabSubCom[i].Name_com[0] != 0) {
1200       if (!strcmp(com,TabSubCom[i++].Name_com))
1201 	nbargs = TabSubCom[i-1].Typ_Com;
1202    }
1203    if (!nbargs) {                   /* argument not found within the table */
1204       free(com);
1205       return symbol;
1206    }
1207    while (nbargs--) {
1208       while (symbol != '{') {
1209 	 sendchar(symbol);
1210 	 subcom[nbsub++] = symbol;
1211 	 subcom[nbsub] = 0;
1212 	 symbol = inp_ch();
1213       }
1214       sendchar(symbol);
1215       symbol = inp_ch();
1216       symbol =  ignore ();
1217    }
1218    sendchar(symbol);
1219    symbol = inp_ch();
1220    free(com);
1221    return symbol;
1222 }
1223 
1224 /*
1225  This routine assumes that a '{' has already been read; it
1226  looks for the corresponding closing brace '}'; nesting of
1227  braces is considered.
1228  */
ignore(void)1229 char ignore(void) {
1230    int nbsub = 0;
1231    int nbbraces = 1;
1232    while (nbbraces ) {
1233       subcom[nbsub++] = symbol;
1234       subcom[nbsub] = 0;
1235       switch (symbol) {
1236        case '{':
1237 	 nbbraces++;
1238 	 sendchar(symbol);
1239 	 break;
1240        case '}':
1241 	 nbbraces--;
1242 	 if (!nbbraces) return symbol;
1243 	 sendchar(symbol);
1244 	 break;
1245        case ' ':
1246        case '\n':
1247 	 sendchar(symbol);
1248 	 put_word();
1249 	 break;
1250        default :
1251 	 sendchar(symbol);
1252 	 break;
1253       }
1254       symbol = inp_ch();
1255    }
1256    return symbol ;
1257 }
1258 
1259 /* ------------------ End Addition ----------------- */
1260 
1261 /*
1262  * *** Devnag function definitions ***
1263  */
1264 
main(int argc,char ** argv)1265 int main(int argc, char **argv) {
1266    char *s_ptr;
1267    int i;
1268    unsigned char dn_yes, start_file;
1269 
1270    /* if invoked with "-v", print banner and exit */
1271 
1272    if ((argc == 2) && (strcmp(argv[1], "-v") == 0)) {
1273       printf("devnag v%s\n", version);
1274       puts(banner);
1275       exit(0);
1276    }
1277 
1278    /* get file specifications */
1279 
1280    if (argc == 3) {
1281       strcpy(infil, argv[1]);
1282       strcpy(outfil, argv[2]);
1283    }
1284    else {
1285       if (argc == 2) {
1286 	 strcpy(infil, argv[1]);
1287 	 *outfil = '\0';
1288       }
1289       else {
1290 	 do {
1291 	    printf("input file: ");
1292 	    fgets(infil, MEDBUF, stdin);
1293 	    infil[strlen(infil)-1] = '\0';
1294 	 } while (strlen(infil) == 0);
1295 	 printf("output file: ");
1296 	 fgets(outfil, MEDBUF, stdin);
1297 	 outfil[strlen(outfil)-1] = '\0';
1298       }
1299    }
1300    /* Check the source file extension */
1301    if (strcasecmp(infil+strlen(infil)-strlen(DEFAULT_DEST_EXT), DEFAULT_DEST_EXT) == 0) {
1302       fprintf(stderr, "source file extension %s is forbidden\n", DEFAULT_DEST_EXT);
1303       exit(1);
1304    }
1305    s_ptr = infil+strlen(infil)-strlen(DEFAULT_SRC_EXT);
1306    if (strcasecmp(s_ptr, DEFAULT_SRC_EXT) != 0) s_ptr = NULL;
1307    if (!s_ptr) strcat(infil, DEFAULT_SRC_EXT);
1308 
1309    /* Try to open the source file */
1310    f_in = fopen(infil, "rb");
1311    if (!f_in && !s_ptr) {
1312       /* strip extension which has not been supplied and try again */
1313       infil[strlen(infil)-strlen(DEFAULT_SRC_EXT)] = '\0';
1314       f_in = fopen(infil, "rb");
1315    }
1316    if (!f_in) {
1317       if (s_ptr) fprintf(stderr, "cannot open file %s\n", infil);
1318       else fprintf(stderr, "cannot open either %s or %s%s\n", infil, infil, DEFAULT_SRC_EXT);
1319       exit(1);
1320    }
1321 
1322    /* Destination file name */
1323    if (strlen(outfil) == 0) {
1324       strcpy(outfil, infil);
1325       if (strcasecmp(outfil+strlen(outfil)-strlen(DEFAULT_SRC_EXT), DEFAULT_SRC_EXT) == 0) {
1326          outfil[strlen(outfil)-strlen(DEFAULT_SRC_EXT)] = '\0';
1327       }
1328       strcat(outfil, DEFAULT_DEST_EXT);
1329    } else {
1330       if (strcasecmp(outfil+strlen(outfil)-strlen(DEFAULT_SRC_EXT), DEFAULT_SRC_EXT) == 0) {
1331          fclose(f_in);
1332          fprintf(stderr, "destination file extension %s is forbidden\n", DEFAULT_SRC_EXT);
1333          exit(1);
1334       }
1335       if (strcasecmp(infil, outfil) == 0) strcat(outfil, DEFAULT_DEST_EXT);
1336    }
1337 #ifdef TEXLIVE
1338 /* In TeX Live we want to share files between Unix and Windows systems.  */
1339 #define FOPEN_W_MODE "wb"
1340 #else
1341 #define FOPEN_W_MODE "w"
1342 #endif
1343    if ((f_out = fopen(outfil, FOPEN_W_MODE)) == NULL) {
1344       fclose(f_in);
1345       fprintf(stderr, "cannot open %s for output\n", outfil);
1346       exit(1);
1347    }
1348 
1349    /* initialization */
1350 
1351    cons_seen = FALSE;
1352    vow_seen = FALSE;
1353    front_r = FALSE;
1354    wait_syll = FALSE;
1355    cmr_mode = FALSE;
1356    num_mode = FALSE;
1357    hindi_mode = FALSE;
1358    mhindi_mode = FALSE;
1359    dollar_mode = 0;
1360    do_hyphen = FALSE;
1361    do_vconjuncts = FALSE;
1362    tabs_mode = FALSE;                      /* Marc Csernel */
1363    lig_block = FALSE;                      /* Kevin Carmody */
1364    start_file = TRUE;
1365 
1366    chr_idx = 0;
1367    n_halves = 0;                           /* Kevin Carmody */
1368    *word = '\0';
1369    *inbuf = '\0';
1370    *outbuf = '\0';
1371    linenumber = 1;
1372 
1373 
1374    /* main loop */
1375 
1376    do {
1377       p_in = fgetline(inbuf, MAXBUF, f_in);
1378       if (start_file) {
1379          if (inbuf[0] != '%' || inbuf[1] != '&') {
1380             fprintf(f_out, "\\def\\DevnagVersion{%s}", version);
1381             start_file = FALSE;
1382           }
1383       }
1384 
1385       /* read preprocessor commands */
1386 
1387       if (*inbuf == '@') {
1388 	 while (TRUE) {
1389 	    if (strstr(inbuf, "dollars\n") == inbuf+1) {
1390 	       dollar_mode = 1;
1391 	       fprintf(f_out, "%%%s", inbuf);
1392 	       linenumber++;
1393 	       break;
1394 	    }
1395 	    if (strstr(inbuf, "nodollars\n") == inbuf+1) {
1396 	       dollar_mode = 0;
1397 	       fprintf(f_out, "%%%s", inbuf);
1398 	       linenumber++;
1399 	       break;
1400 	    }
1401 	    if (strstr(inbuf, "dolmode0\n") == inbuf+1) {
1402 	       dollar_mode = 0;
1403 	       fprintf(f_out, "%%%s", inbuf);
1404 	       linenumber++;
1405 	       break;
1406 	    }
1407 	    if (strstr(inbuf, "dolmode1\n") == inbuf+1) {
1408 	       dollar_mode = 1;
1409 	       fprintf(f_out, "%%%s", inbuf);
1410 	       linenumber++;
1411 	       break;
1412 	    }
1413 	    if (strstr(inbuf, "dolmode2\n") == inbuf+1) {
1414 	       dollar_mode = 2;
1415 	       fprintf(f_out, "%%%s", inbuf);
1416 	       linenumber++;
1417 	       break;
1418 	    }
1419 	    if (strstr(inbuf, "dolmode3\n") == inbuf+1) {
1420 	       dollar_mode = 3;
1421 	       fprintf(f_out, "%%%s", inbuf);
1422 	       linenumber++;
1423 	       break;
1424 	    }
1425 	    if (strstr(inbuf, "hindi\n") == inbuf+1) {
1426 	       hindi_mode = TRUE;
1427 	       if (mhindi_mode) {
1428 		  for (i = 0; i < N_MNOLIGS; i++)
1429 		    lig_table[modnolig[i]].in_use = TRUE;
1430 		  mhindi_mode = FALSE;
1431 	       }
1432 	       for (i = 0; i < N_NOLIGS; i++)
1433 		 lig_table[nolig[i]].in_use = FALSE;
1434 	       fprintf(f_out, "%%%s", inbuf);
1435 	       linenumber++;
1436 	       break;
1437 	    }
1438 	    if (strstr(inbuf, "modernhindi\n") == inbuf+1) {
1439 	       hindi_mode = TRUE;
1440 	       mhindi_mode = TRUE;
1441 	       for (i = 0; i < N_MNOLIGS; i++)
1442 		 lig_table[modnolig[i]].in_use = FALSE;
1443 	       fprintf(f_out, "%%%s", inbuf);
1444 	       linenumber++;
1445 	       break;
1446 	    }
1447 	    if (strstr(inbuf, "sanskrit\n") == inbuf+1) {
1448 	       hindi_mode = FALSE;
1449 	       mhindi_mode = FALSE;
1450 	       for (i = 0; i < N_MNOLIGS; i++)
1451 		 lig_table[modnolig[i]].in_use = TRUE;
1452 	       fprintf(f_out, "%%%s", inbuf);
1453 	       linenumber++;
1454 	       break;
1455 	    }
1456 	    if (strstr(inbuf, "lig") == inbuf+1) {
1457 	       s_ptr = inbuf;
1458 	       while (TRUE) {
1459 		  if ((s_ptr = strchr(s_ptr, ' ')) == NULL) break;
1460 		  s_ptr++;
1461 		  if (sscanf(s_ptr, "%d", &number) != 0)
1462 		    lig_table[ligidxs[number-1]].in_use = TRUE;
1463 	       }
1464 	       fprintf(f_out, "%%%s", inbuf);
1465 	       linenumber++;
1466 	       break;
1467 	    }
1468 	    if (strstr(inbuf, "nolig") == inbuf+1) {
1469 	       s_ptr = inbuf;
1470 	       while (TRUE) {
1471 		  if ((s_ptr = strchr(s_ptr, ' ')) == NULL) break;
1472 		  s_ptr++;
1473 		  if (sscanf(s_ptr, "%d", &number) != 0)
1474 		    lig_table[ligidxs[number-1]].in_use = FALSE;
1475 	       }
1476 	       fprintf(f_out, "%%%s", inbuf);
1477 	       linenumber++;
1478 	       break;
1479 	    }
1480 	    if (strstr(inbuf, "hyphen\n") == inbuf+1) {
1481 	       do_hyphen = TRUE;
1482 	       fprintf(f_out, "%%%s", inbuf);
1483 	       linenumber++;
1484 	       break;
1485 	    }
1486 	    if (strstr(inbuf, "nohyphen\n") == inbuf+1) {
1487 	       do_hyphen = FALSE;
1488 	       fprintf(f_out, "%%%s", inbuf);
1489 	       linenumber++;
1490 	       break;
1491 	    }
1492 	    if (strstr(inbuf, "vconjuncts\n") == inbuf+1) {
1493 	       do_vconjuncts = TRUE;
1494 	       fprintf(f_out, "%%%s", inbuf);
1495 	       linenumber++;
1496 	       break;
1497 	    }
1498 	    if (strstr(inbuf, "novconjuncts\n") == inbuf+1) {
1499 	       do_vconjuncts = FALSE;
1500 	       fprintf(f_out, "%%%s", inbuf);
1501 	       linenumber++;
1502 	       break;
1503 	    }
1504 
1505 /* --------- Addition by Marc Csernel 1998 --------- */
1506 
1507 	    if (strstr(inbuf, "tabs\n") == inbuf+1) {
1508 	       tabs_mode = TRUE;
1509 	       fprintf(f_out, "%%%s", inbuf);
1510 	       linenumber++;
1511 	       break;
1512 	    }
1513 
1514 /* ------------------ End Addition ----------------- */
1515 
1516 	    if (strstr(inbuf, "notabs\n") == inbuf+1) {
1517 	       tabs_mode = FALSE;
1518 	       fprintf(f_out, "%%%s", inbuf);
1519 	       linenumber++;
1520 	       break;
1521 	    }
1522 
1523 	    fprintf(stderr, "Warning: possible illegal preprocessor command at line ");
1524 	    fprintf(stderr, "%d:\n%s", linenumber, inbuf);
1525 	    fprintf(f_out, "%s", inbuf);
1526 	    linenumber++;
1527 	    break;
1528 	 }
1529 	 *inbuf = '\0';
1530       }
1531       else {
1532 	 if (!find_dn()) fputs(inbuf, f_out);
1533 
1534 	 /* process DN text */
1535 
1536 	 else do {
1537 	    buf_idx = 0;
1538 	    symbol = inp_ch();
1539 	    dnproc();
1540 	    dn_yes = find_dn();
1541 	    if (!dn_yes) {
1542 	       strcat(outbuf, inbuf);
1543 	       fputs(outbuf, f_out);
1544 	    }
1545 	 }
1546 	 while (dn_yes);
1547 	 *inbuf = '\0';
1548 	 linenumber++;
1549 	 *outbuf = '\0';;
1550       }
1551    }
1552    while (p_in != NULL);
1553    fclose(f_in);
1554    fclose(f_out);
1555    exit(0);
1556 }
1557 
1558 
1559 /*
1560  * Read and parse DN input, and convert it into the character set
1561  * defined in char_table[]
1562  */
dnproc(void)1563 void dnproc(void) {
1564    short i;
1565    unsigned char saved, dnready;
1566    short brace_lev;
1567    int test_val;                           /* Marc Csernel */
1568    char savchr = 0;
1569    char wrong[10];
1570    brace_lev = 1;
1571    saved = FALSE;
1572    dnready = FALSE;
1573    do {
1574       switch(symbol) {
1575        case '.':
1576 	 savchr = inp_ch();
1577 	 i = 0;
1578 	 do { i++; } while ((i != 19) && (chset1[i-1] != savchr));
1579 	 if (i == 19) {
1580 	    wrong[0] = '.';
1581 	    wrong[1] = savchr;
1582 	    wrong[2] = '\0';
1583 	    err_ill(wrong);
1584 	 }
1585 	 else {
1586 	    if (i < 4) {
1587 	       savchr = inp_ch();
1588 	       if (savchr == 'h')  i += 11;
1589 	       else saved = TRUE;
1590 	       if (i == 1)  err_ill(".k");
1591 	    }
1592 	 }
1593 	 if (i == 18) put_ch('M');
1594 	 else put_ch(i);
1595 	 break;
1596 
1597 /* --------- Addition by Marc Csernel 1998 --------- */
1598 
1599 	 /* cite and chapter command */
1600 	 case ']':
1601 	 if (citation) {
1602 	    citation = 0;
1603 	    if (num_mode) put_ch(dummy);
1604 	    put_syll();
1605 	    test_sub_com();
1606 	 }
1607 	 if (optchapter) {
1608 	    optchapter = 0;
1609 	    if (num_mode) put_ch(dummy);
1610 	    put_syll();
1611 	    sendchar(symbol);
1612 	    break;
1613 	 }
1614 
1615 /* ------------------ End Addition ----------------- */
1616 
1617        case 'i':
1618 	 savchr = inp_ch();
1619 	 if (savchr == 'i')  put_ch('I');
1620 	 else {
1621 	    put_ch(symbol);
1622 	    saved = TRUE;
1623 	 }
1624 	 break;
1625        case 'u':
1626 	 savchr = inp_ch();
1627 	 if (savchr == 'u') put_ch('U');
1628 	 else {
1629 	    put_ch(symbol);
1630 	    saved = TRUE;
1631 	 }
1632 	 break;
1633        case 'a':
1634 	 savchr = inp_ch();
1635 	 i = 0;
1636 	 do { i++; } while ((i != 4) && (chset2[i-1] != savchr));
1637 	 if (i == 4) {
1638 	    put_ch(symbol);
1639 	    saved = TRUE;
1640 	 }
1641 	 else put_ch(chset5[i-1]);
1642 	 break;
1643        case '\"':
1644 	 savchr = inp_ch();
1645 	 i = 0;
1646 	 do { i++; } while ((i != 3) && (chset3[i-1] != savchr));
1647 	 if (i == 3) {
1648 	    wrong[0] = '\"';
1649 	    wrong[1] = savchr;
1650 	    wrong[2] = '\0';
1651 	    err_ill(wrong);
1652 	 }
1653 	 else put_ch((short)(i+17));
1654 	 break;
1655        case '~':
1656 	 savchr = inp_ch();
1657 	 i = 0;
1658 	 do { i++; } while ((i != 5) && (chset6[i-1] != savchr));
1659 	 if (i == 6) {
1660 	    wrong[0] = '~';
1661 	    wrong[1] = savchr;
1662 	    wrong[2] = '\0';
1663 	    err_ill(wrong);
1664 	 }
1665 	 else if (i == 5) put_ch(47);
1666 	 else put_ch((short)(i+20));
1667 	 break;
1668        case end_of_line:
1669 	 put_ch(end_of_line);
1670 	 break;
1671        case '$':
1672 	 if (!dollar_mode) put_ch(symbol);
1673 	 else {
1674 	    if (no_dn) put_ch(dummy);
1675 	    else put_ch('}');
1676 	    dnready = TRUE;
1677 	    put_word();
1678 	    memmove(inbuf, inbuf+buf_idx, strlen(inbuf+buf_idx)+1);
1679 	 }
1680 	 break;
1681        case '}':
1682 	 brace_lev -= 1;
1683 	 if ((brace_lev == 0) && (!d_found)) {
1684 	    if (no_dn) put_ch(dummy);
1685 	    else put_ch(symbol);
1686 	    dnready = TRUE;
1687 	    put_word();
1688 	    memmove(inbuf, inbuf+buf_idx, strlen(inbuf+buf_idx)+1);
1689 	 }
1690 	 else put_ch(symbol);
1691 	 break;
1692        case '{':
1693 	 put_ch(symbol);
1694 	 brace_lev += 1;
1695 	 break;
1696        case '%':
1697 	 put_ch(symbol);
1698 	 do {
1699 	    symbol = inp_ch();
1700 	    if (symbol != end_of_line) sendchar(symbol);
1701 	 }
1702 	 while (symbol != end_of_line);
1703 	 sendchar('\n');
1704 	 break;
1705        case '<':
1706 	 put_syll();
1707 	 do {
1708 	    symbol = inp_ch();
1709 	    if (symbol == end_of_line) symbol = '\n';
1710 	    if (symbol != '>') sendchar(symbol);
1711 	 }
1712 	 while (symbol != '>');
1713 	 break;
1714        case '\\':
1715 	 put_ch(symbol);
1716 	 symbol = inp_ch();
1717 	 if (symbol == end_of_line) {
1718 	    put_word();
1719 	    strcat(outbuf, "\n");
1720 	    fputs(outbuf, f_out);
1721 	    *outbuf = '\0';
1722 	 }
1723 	 else {
1724 
1725 /* --------- Addition by Marc Csernel 1998 --------- */
1726 
1727 	    /* Modified MC May 98 to take \\[5pt] into account */
1728 	    if (symbol == '\\') {
1729 	       sendchar(symbol);
1730 	       symbol = inp_ch();
1731 	       if (symbol == '[') {
1732 		  while (symbol != ']') {
1733 		     sendchar(symbol);
1734 		     symbol = inp_ch();
1735 		  }
1736 		  sendchar(symbol);
1737 		  symbol = inp_ch();
1738 	       }
1739 	       savchr = symbol;
1740 	       saved = TRUE;
1741 	       break;
1742 	    }
1743 
1744 /* ------------------ End Addition ----------------- */
1745 
1746 	    if (!isalpha((unsigned char)symbol)) sendchar(symbol);
1747 	    else {
1748 	      nbchcomm = 0;                             /* Marc Csernel */
1749 	       do {
1750 		  command[nbchcomm++] = symbol;         /* Marc Csernel */
1751 		  sendchar(symbol);
1752 		  symbol = inp_ch();
1753 	       }
1754 	       while (isalpha((unsigned char)symbol));
1755 
1756 /* --------- Addition by Marc Csernel 1998 --------- */
1757 
1758 	       command[nbchcomm++] = 0;
1759 	       test_val = test_command();
1760 	       switch (test_val) {
1761 		case 1:
1762 		  savchr = test_sub_com();
1763 		  break;
1764 		case 2:
1765 		  savchr = ignore();
1766 		  break;
1767 		case 3:
1768 		  savchr = comm_double_args();
1769 		  break;
1770 		case 4:
1771 		  savchr = comm_begin();
1772 		  break;
1773 		case 5:
1774 		  savchr = comm_special();
1775 		  break;
1776 		case 6:
1777 		  savchr = comm_opt();
1778 		  break;
1779 		case 7:
1780 		  savchr = comm_cite();
1781 		  break;
1782 		case 8:
1783 		  savchr = comm_chapter();
1784 		  break;
1785 		default:
1786 		  savchr = symbol;
1787 	       }
1788 
1789 /* ------------------ End Addition ----------------- */
1790 
1791 	       saved = TRUE;
1792 	    }
1793 	 }
1794 	 break;
1795        case ill_char:
1796 	 err_ill('\0');
1797 	 break;
1798        case end_of_file:
1799 	 fputs("Error: missing }", stderr);
1800 	 exit(1);
1801        default:
1802 	 if (symbol < 0) err_ill('\0');  /* accented character inside dn mode */
1803 	 i = 0;
1804 	 do { i++; } while ((i != 10) && (chset4[i-1] != symbol));
1805 	 if (i == 10) put_ch(symbol);
1806 	 else {
1807 	    savchr = inp_ch();
1808 	    if (savchr == 'h') {
1809 	       if (i == 9)  put_ch(20);
1810 	       else put_ch((short)(symbol-32));
1811 	    }
1812 	    else {
1813 	       put_ch(symbol);
1814 	       saved = TRUE;
1815 	    }
1816 	 }
1817       }
1818       if (saved) {
1819 	 symbol = savchr;
1820 	 saved = FALSE;
1821       }
1822       else {
1823 	 if (!dnready) symbol = inp_ch(); }
1824    }
1825    while (!dnready);
1826 }
1827 
1828 /*
1829  * Read and parse input in the character set defined by char_table[],
1830  * and convert it into TeX text and macros for the dvng fonts.
1831  */
put_ch(short code)1832 void put_ch(short code) {
1833    short cons_idx = 0;
1834    char cstr[2];
1835    struct char_def *c_ptr;
1836    struct cons_joins *q_ptr;
1837    short i, j;
1838    /* --------- Addition by Kevin Carmody 2005 --------- */
1839    /*
1840     * Conjunct fixes used by fixconj()
1841     */
1842    short wrong_1[] = {63, 63, 113};
1843    short right_1[] = {63, 34, 0};                              /* kk.sa   */
1844    short wrong_2[] = {94, 214, 264, 113};
1845    short right_2[] = {94, 86, 264, 34, 0};                     /* .tk.sa  */
1846    short wrong_3[] = {22, 62, 26};
1847    short right_3[] = {22, 226, 0};                             /* ~nj~na  */
1848    short wrong_4[] = {274, 88, 264, 273, 57, 52, 264, 121};
1849    short right_4[] = {126, 88, 264, 43, 0};                    /* .drya   */
1850    short wrong_5[] = {94, 140, 264, 118};
1851    short right_5[] = {94, 100, 264, 146, 0};                   /* ddva    */
1852    short wrong_6[] = {94, 136, 264, 118};
1853    short right_6[] = {94, 100, 264, 64, 118, 0};               /* ddhva   */
1854    short wrong_7[] = {271, 100, 264, 273, 57, 52, 264, 121};
1855    short right_7[] = {272, 100, 264, 43, 0};                   /* drya    */
1856    short wrong_8[] = {9, 274, 86, 264, 273, 57, 52, 264, 121};
1857    short right_8[] = {9, 126, 86, 264, 43, 0};                 /* .s.trya */
1858    /* ------------------ End Addition ----------------- */
1859 
1860    c_ptr = &char_table[code-1];
1861    switch(c_ptr->ch_typ) {
1862     case dn:
1863       if (cmr_mode) {
1864 	 cmr_mode = FALSE;
1865 	 put_sym(RE);
1866       }
1867       else {
1868 	 if (num_mode) {
1869 	    num_mode = FALSE;
1870 	    put_sym(RBRACE);
1871 	 }
1872       }
1873       switch(c_ptr->ch_subtyp) {
1874        case hi_vowel:
1875 	 if (wait_syll) {
1876 	    put_syll();
1877 	    if (do_hyphen) strcat(word, "\\-");
1878 	 }
1879          if (lig_block) err_ill("+");      /* Kevin Carmody */
1880 	 if (cons_seen) {
1881 	    if (syll[chr_idx-1] < 0) expand();
1882 	    if (code == 'i') {
1883 	       for (i = chr_idx; i>= (cons_idx+1); i--)
1884 		 syll[i] = syll[i-1];
1885 	       syll[cons_idx] = c_ptr->ch_subcode;
1886 	    }
1887 	    else {
1888 	       syll[chr_idx] = c_ptr->ch_subcode;
1889 	       if ((code != 'A') && (code != 'a')) vow_seen = TRUE;
1890 	    }
1891 	 }
1892 	 else syll[chr_idx] = c_ptr->ch_code;
1893 	 chr_idx += 1;
1894 	 wait_syll = TRUE;
1895 	 cons_seen = FALSE;
1896 	 break;
1897        case lo_vowel:
1898 	 if (wait_syll) {
1899 	    put_syll();
1900 	    if (do_hyphen) strcat(word, "\\-");
1901 	 }
1902          if (lig_block) err_ill("+");      /* Kevin Carmody */
1903 	 if (cons_seen) {
1904 	    if (syll[chr_idx-1] < 0) expand();
1905 	    if ((syll[chr_idx-1] == 'r') && (code == 'u'))
1906 	      syll[--chr_idx] = 'z';
1907 	    else {
1908 	       if ((syll[chr_idx-1] == 'r') && (code == 'U'))
1909 		 syll[--chr_idx] = '!';
1910 	       else {
1911 		  if ((syll[chr_idx-1] == 'h') && (code == 7))
1912 		    syll[--chr_idx] = 0343;
1913 		  else {
1914 		     if ((syll[chr_idx-1] == 'r') && ((code == 7)
1915 			   || (code == 15) || (code == 16) || (code == 17))) {
1916 			syll[--chr_idx] = c_ptr->ch_code;
1917 			front_r = TRUE;
1918 		     }
1919 		     else {
1920 			put_macro(c_ptr->ch_subcode);
1921 			chr_idx -= 1;
1922 		     }
1923 		  }
1924 	       }
1925 	    }
1926 	 }
1927 	 else syll[chr_idx] = c_ptr->ch_code;
1928 	 chr_idx += 1;
1929 	 wait_syll = TRUE;
1930 	 cons_seen = FALSE;
1931 	 break;
1932        case consonant:
1933 	 if (wait_syll) {
1934 	    put_syll();
1935 	    if (do_hyphen) strcat(word, "\\-");
1936          }
1937 	 if (!cons_seen) {
1938 
1939 	    /* Is this the first consonant? */
1940 
1941 	    cons_seen = TRUE;
1942 	    cons_idx = chr_idx;
1943 	    cons_code = c_ptr->ch_subcode;
1944 	    syll[chr_idx] = c_ptr->ch_code;
1945 	    chr_idx += 1;
1946 	    tst_half();
1947 	 }
1948 	 else {
1949 	    q_ptr = &cons_table[cons_code];
1950 	    if (syll[cons_idx] == 'r') {
1951 
1952 	       /* Was there an initial "r"? */
1953 
1954 	       front_r = TRUE;
1955 	       syll[chr_idx-1] = c_ptr->ch_code;
1956 	       cons_code = c_ptr->ch_subcode;
1957 	       tst_half();
1958 	    }
1959 	    else {
1960 	       i = q_ptr->lig_code;
1961 	       for (j = i+(q_ptr->n_ligs); i != j; i++)
1962 		 if (lig_table[i].sym_code == code) break;
1963                if ((i != j) && (lig_table[i].in_use)
1964 		   && (!lig_block)) {       /* lig_block added Kevin Carmody */
1965 
1966 		  /* Is there a ligature? */
1967 
1968 		  syll[chr_idx-1] = lig_table[i].sym_new;
1969 		  cons_code = lig_table[i].join_idx;
1970 		  joincode = cons_table[cons_code].j_code;
1971 		  if (joincode != 0) {
1972 		     half_codes[0] = joincode;
1973 		     n_halves = 1;
1974 		  }
1975 		  else {
1976 
1977 		     /* ... or a half-form? */
1978 
1979 		     joincode = cons_table[c_ptr->ch_subcode].j_code;
1980 		     if ((joincode != 0) && (n_halves != 0)) {
1981 			half_codes[n_halves] = joincode;
1982 			n_halves += 1;
1983 		     }
1984 		     else n_halves = 0;
1985 		  }
1986 	       }
1987 	       else {
1988                   if ((code == 'r') && (!lig_block)) {
1989 		                            /* lig_block added Kevin Carmody */
1990                      if (q_ptr->r_type != 0) {
1991 
1992 			/* "Special" non-initial "r"? */
1993 
1994 			syll[chr_idx] = syll[chr_idx-1];
1995 			syll[chr_idx-1] = (q_ptr->r_type == 1) ? '\176' : 272;
1996 			syll[chr_idx+1] = RBRACE;
1997 			chr_idx += 2;
1998 		     }
1999 		     else {
2000 
2001 			/* "Normal" non-initial "r"? */
2002 
2003 			syll[chr_idx] = '\175';
2004 			chr_idx += 1;
2005 			n_halves = 0;
2006 		     }
2007 		     cons_code = 0;
2008 		  }
2009 		  else {
2010                      if ((q_ptr->j_code != 0) && (!lig_block)) {
2011 			                 /* lig_block added by Kevin Carmody */
2012 
2013 			/* Can we build a conjunct using a ligature? */
2014 
2015 			syll[chr_idx-1] = q_ptr->j_code;
2016 			syll[chr_idx] = c_ptr->ch_code;
2017 			cons_code = c_ptr->ch_subcode;
2018 			chr_idx += 1;
2019 			tst_half();
2020 		     }
2021 		     else {
2022 			if (n_halves != 0) {
2023 
2024 			   /* ... or a half-form? */
2025 
2026 			   if (syll[chr_idx-1] != RBRACE) {
2027 			      /*
2028 			       * There might already have been a
2029 			       * subscript "-r" in the conjunct!
2030 			       * (E.g. khrya, ttrya)
2031 			       *                   -- John Smith
2032 			       */
2033 			      for (i = 0; i < n_halves; i++)
2034 				syll[chr_idx-1+i] = half_codes[i];
2035 			      chr_idx += n_halves;
2036 			      syll[chr_idx-1] = c_ptr->ch_code;
2037 			      cons_code = c_ptr->ch_subcode;
2038 			      tst_half();
2039 			   }
2040 			   else {
2041 			      for (i = 0; i < n_halves; i++)
2042 				syll[chr_idx-2+i] = half_codes[i];
2043 			      chr_idx += n_halves;
2044 			      syll[chr_idx-1] = c_ptr->ch_code;
2045 			      syll[chr_idx-2] = RBRACE;
2046 			      cons_code = c_ptr->ch_subcode;
2047 			      tst_half();
2048 			   }
2049 			}
2050 			else {
2051 
2052 			   /* Fall back on viraama */
2053 
2054 			   if (syll[chr_idx-1] < 0) expand();
2055 			   put_macro(VIRAAM);
2056 			   cons_code = c_ptr->ch_subcode;
2057 			   syll[chr_idx] = c_ptr->ch_code;
2058 			   chr_idx += 1;
2059 			   tst_half();
2060 			}
2061 		     }
2062 		  }
2063 		  /*
2064 		   * This hack is to secure the correct representation
2065 		   * for various conjuncts that may assume undesirable
2066 		   * forms.
2067 		   */
2068 /* --------- Modification by Kevin Carmody 2005 --------- */
2069                   fixconj(wrong_1, right_1);
2070                   fixconj(wrong_2, right_2);
2071                   fixconj(wrong_3, right_3);
2072                   fixconj(wrong_4, right_4);
2073                   fixconj(wrong_5, right_5);
2074                   fixconj(wrong_6, right_6);
2075                   fixconj(wrong_7, right_7);
2076                   fixconj(wrong_8, right_8);
2077 /* ------------------ End Modification ----------------- */
2078 		  /*
2079 		   * Now replace the sequence consonant + viraama + ya
2080 		   * by consonant + open ya (glyph became available only
2081 		   * Devnag 2.1).
2082 		   */
2083 		  for (i = 0; i < chr_idx-3; i++) {
2084 		     if ((syll[i] == 94) && (syll[i+2] == 264)
2085 			 && (syll[i+3] == 121)) {
2086 			syll[i] = syll[i+1];
2087 			syll[i+1] = 43;
2088 			chr_idx = i + 2;
2089 		     }
2090 		  }
2091 	       }
2092 	    }
2093 	 }
2094          lig_block = FALSE;                /* Kevin Carmody */
2095 	 break;
2096        case special:
2097          if (lig_block) err_ill("+");      /* Kevin Carmody */
2098 	 if (cons_seen) { if (syll[chr_idx-1] < 0) expand(); }
2099 	 if ((code == 'M') && (front_r)) {
2100 	    front_r = FALSE;
2101 	    if (vow_seen) syll[chr_idx] = 270;
2102 	    else syll[chr_idx] = RDT;
2103 	    chr_idx += 1;
2104 	    put_syll();
2105 	 }
2106 	 else {
2107 	    if ((code == 'M') && (syll[chr_idx-1] == 'I')) {
2108 	       syll[chr_idx-1] = 18;
2109 	       put_syll();
2110 	    }
2111 	    else {
2112 	       if ((code == 'M') && (vow_seen)) {
2113 		  syll[chr_idx] = 268;
2114 		  chr_idx += 1;
2115 		  put_syll();
2116 	       }
2117 
2118 /* --------- Addition by Marc Csernel 1998 --------- */
2119 
2120 	       else {
2121 		  if (code == '_') {
2122 		     if(!cons_seen) err_ill("_");
2123 		     else {
2124 			put_macro(VIRAAM);
2125 			put_syll();
2126 		     }
2127 		  }
2128 		  else {
2129 		     if (code == '&') {
2130 			if(!tabs_mode) {
2131 			   if  (!cons_seen) {
2132 			      fprintf(stderr, "Error: tabs_mode not selected\n");
2133 			      err_ill("&");
2134 			   }
2135 			   else {
2136 			      put_macro(VIRAAM);
2137 			      put_syll();
2138 			   }
2139 			}
2140 			else {
2141 			   if(!cons_seen) sendchar((char)code);
2142 			   else {
2143 			      put_macro(VIRAAM);
2144 			      put_syll();
2145 			   }
2146 			}
2147 		     }
2148 
2149 /* ------------------ End Addition ----------------- */
2150 
2151 		     else {
2152 
2153 /* --------- Addition by Kevin Carmody 2005 --------- */
2154 
2155                         if (code == '+') {  /* half letter or viraama */
2156                            if (!cons_seen) err_ill("+");
2157                            lig_block = TRUE;
2158                         }
2159 /* ------------------ End Addition ------------------ */
2160 
2161                         else {
2162                            if ((!hindi_mode) && (cons_seen)) put_macro(VIRAAM);
2163                            put_syll();
2164                            put_sym(c_ptr->ch_code);
2165                         }
2166 		     }
2167 		  }
2168 	       }
2169 	    }
2170 	 }
2171       }
2172       break;
2173     case illegal:
2174       cstr[0] = code;
2175       cstr[1] = '\0';
2176       err_ill(cstr);
2177       break;
2178     case control:
2179       if (cmr_mode) {
2180 	 cmr_mode = FALSE;
2181 	 put_sym(RE);
2182       }
2183       else {
2184          if (lig_block) err_ill("+");      /* Kevin Carmody */
2185 	 if (num_mode) {
2186 	    num_mode = FALSE;
2187 	    put_sym(RBRACE);
2188 	 }
2189 	 else {
2190 	    if (cons_seen) {
2191 	       if (syll[chr_idx-1] < 0) expand();
2192 	    }
2193 	    if ((!hindi_mode) && (cons_seen)) put_macro(VIRAAM);
2194 	    put_syll();
2195 	 }
2196       }
2197       if (code == end_of_line) {
2198 	 put_word();
2199 	 strcat(outbuf, "\n");
2200 	 fputs(outbuf, f_out);
2201 	 *outbuf = '\0';
2202       }
2203       else if (code != dummy) sendchar((char)code);
2204       break;
2205     case cmr:
2206       if (cmr_mode) sendchar((char)code);
2207       else {
2208        cmr_mode = TRUE;
2209        if (num_mode) {
2210 	  num_mode = FALSE;
2211 	  put_sym(RBRACE);
2212        }
2213        else {
2214           if (lig_block) err_ill("+");      /* Kevin Carmody */
2215 	  if (cons_seen) { if (syll[chr_idx-1] < 0) expand(); }
2216 	  if ((!hindi_mode) && (cons_seen)) put_macro(VIRAAM);
2217 	  put_syll();
2218        }
2219        put_sym(RS);
2220        sendchar((char)code);
2221     }
2222       break;
2223     case numeral:
2224       if (num_mode) sendchar((char)code);
2225       else {
2226 	 num_mode = TRUE;
2227 	 if (cmr_mode) {
2228 	    cmr_mode = FALSE;
2229 	    put_sym(RE);
2230 	 }
2231 	 else {
2232             if (lig_block) err_ill("+");      /* Kevin Carmody */
2233 	    if (cons_seen) { if (syll[chr_idx-1] < 0) expand(); }
2234 	    if ((!hindi_mode) && (cons_seen)) put_macro(VIRAAM);
2235 	    put_syll();
2236 	 }
2237 	 put_sym(RN);
2238 	 sendchar((char)code);
2239       }
2240    }
2241 }
2242 
2243 /*
2244  * Append a character to word[]; if character is whitespace,
2245  * call put_word().
2246  */
sendchar(char c)2247 void sendchar(char c) {
2248    int i = strlen(word);
2249    word[i] = c == end_of_line ? '\n' : c;
2250    word[i+1] = '\0';
2251    if (isspace((unsigned char)c)) put_word();
2252 }
2253 
2254 /*
2255  * Use the current chacter value as an index into out_string[], and
2256  * append the code found there to the end of word[].
2257  */
put_sym(short code)2258 void put_sym(short code) {
2259    strcat(word, out_string[code]);
2260 }
2261 
2262 /*
2263  * Append word[] to outbuf[].
2264  */
put_word(void)2265 void put_word(void) {
2266    /*
2267     * The hack is to avoid hyphenation before any consonant stopped
2268     * with viraama.                                   -- John Smith
2269     */
2270    char *w_ptr;
2271    if (do_hyphen) {                                          /* hack */
2272       do {
2273 	 w_ptr = strstr(word, "\\-\\qq{");
2274 	 if (w_ptr != NULL) memmove(w_ptr, w_ptr+2, strlen(w_ptr+2)+1);
2275       }
2276       while (w_ptr != NULL);
2277    }
2278    strcat(outbuf, word);
2279    *word = '\0';
2280 }
2281 
2282 /*
2283  * Append syll[] to word[], using the codes defined in out_string[]
2284  */
put_syll(void)2285 void put_syll(void) {
2286    /*
2287     * put_syll() now checks to see whether "old-style" treatment of
2288     * consonant sequences containing viraama has been requested with
2289     * the @vconjuncts preprocessor command. If not, it re-orders the
2290     * characters in any such sequence containing "i" so that the
2291     * i-matra vowel is placed after the viraama rather than before
2292     * the entire sequence.                             -- John Smith
2293     */
2294    short i;
2295    int ipos, vpos, bpos;
2296    if (do_vconjuncts == FALSE) {
2297       ipos = sindex(0, '\105');            /* 'i' */
2298       if (ipos != -1) {
2299 	 vpos = sindex(ipos, '\136');      /* viraama */
2300 	 if (vpos == -1) {
2301 	    vpos = sindex(ipos, 271);
2302 	    if (vpos == -1) vpos = sindex(ipos, 274);
2303 	    if (vpos != -1) {
2304 	       vpos = sindex(vpos, 273);   /* alternative viraama */
2305 	       if ((syll[vpos+1] != '9') || (syll[vpos+2] != '4')) vpos = -1;
2306 	    }
2307 	 }
2308 	 if (vpos != -1) {
2309 	    bpos = sindex(vpos, 264);      /* right brace after viraama */
2310 	    if (bpos != -1) {
2311 	       for (i = ipos+1; i <= bpos; i++)
2312 		 syll[i-1] = syll[i];
2313 	       syll[bpos] = '\105';
2314 	    }
2315 	 }
2316       }
2317    }
2318    for (i = 0; i < chr_idx; i++)
2319      strcat(word, out_string[syll[i]]);
2320    if (front_r) {
2321       if (vow_seen) strcat(word, out_string[269]);
2322       else strcat(word, out_string[13]);
2323       front_r = FALSE;
2324    }
2325    chr_idx = 0;
2326    cons_seen = FALSE;
2327    vow_seen = FALSE;
2328    wait_syll = FALSE;
2329    lig_block = FALSE;                   /* Kevin Carmody */
2330 }
2331 
2332 /*
2333  * Check whether a "half-form" exists for current consonant, and
2334  * store the result.
2335  */
tst_half(void)2336 void tst_half(void) {
2337    joincode = cons_table[cons_code].j_code;
2338    if (joincode != 0) {
2339       half_codes[0] = joincode;
2340       n_halves = 1;
2341    }
2342    else n_halves = 0;
2343 }
2344 
2345 /*
2346  * Manipulate contents of syll[] to incorporate a macro call,
2347  * using values that are indices into out_string[] (so that
2348  * put_syll() will produce the correct output).
2349  */
put_macro(short macro)2350 void put_macro(short macro) {
2351    char tmp[5];
2352    int lt, i;
2353    if (syll[chr_idx-1] == '\175') {
2354       syll[chr_idx+1] = '\175';
2355       syll[chr_idx] = RBRACE;
2356       syll[chr_idx-1] = syll[chr_idx-2];
2357       syll[chr_idx-2] = macro;
2358       chr_idx += 2;
2359    }
2360    else {
2361       if (syll[chr_idx-1] == RBRACE) {
2362 	 /*
2363 	  * This is a bit of a hack on Velthuis's part! What it
2364 	  * really means is: if syll[] currently consists of "dr"
2365 	  * OR a consonant(-group) with the subscript "caret" form
2366 	  * of "-r" already attached, AND if a subscript vowel
2367 	  * (or viraama) is to be added to it. In pre-2.0 releases
2368 	  * of devnag this always produced "\qa{consonant}{vowel}",
2369 	  * i.e. it used the simple, non-"caret" form of "-r",
2370 	  * whatever the consonant to which it was to be attached.
2371 	  * I have fixed this bug (feature?), but at the cost of
2372 	  * invoking a new macro, \qc, which will have to be
2373 	  * defined in dnmacs.tex etc. for the new code to work.
2374 	  *                                          -- John Smith
2375 	  */
2376 	 if (syll[chr_idx-3] == 126) syll[chr_idx-3] = 274;
2377 	 else syll[chr_idx-3] = 271;
2378 	 syll[chr_idx] = LBRACE;
2379 	 sprintf(tmp, "%d", macro);
2380 	 lt = strlen(tmp);
2381 	 chr_idx += 1;
2382 	 for (i = 0; i < lt; i++)
2383 	   syll[chr_idx+i] = tmp[i];
2384 	 chr_idx += lt;
2385 	 syll[chr_idx] = RBRACE;
2386 	 chr_idx += 1;
2387       }
2388       else {
2389 	 syll[chr_idx] = syll[chr_idx-1];
2390 	 syll[chr_idx-1] = macro;
2391 	 syll[chr_idx+1] = RBRACE;
2392 	 chr_idx += 2;
2393       }
2394    }
2395 }
2396 
2397 /*
2398  * Exit with error message in the case of illegal input.
2399  */
err_ill(const char * str)2400 void err_ill(const char *str) {
2401    fprintf(stderr, "Error: illegal character(s) \"%s\" detected at line %d:\n",
2402 	  str, linenumber);
2403    fprintf(stderr, inbuf);
2404    exit(1);
2405 }
2406 
2407 /*
2408  * Read and return the next character from inbuf[], taking appropriate
2409  * action in the case of EOL, EOF or an illegal character.
2410  */
inp_ch(void)2411 char inp_ch(void) {
2412    char ch, ch_out;
2413    ch = inbuf[buf_idx++];
2414    if (ch == '\n') {
2415       if (p_in == 0) ch_out = end_of_file;
2416       else {
2417 	 p_in = fgetline(inbuf, MAXBUF, f_in);
2418 	 linenumber++;
2419 	 buf_idx = 0;
2420 	 ch_out = end_of_line;
2421       }
2422    }
2423    else {
2424       if (ch == '\t') ch_out = ' ';        /* Change TABs to spaces */
2425       else {
2426 	 if ((unsigned)ch < 32) ch_out = ill_char;  /* Allow accented characters */
2427 	 else ch_out = ch;
2428       }
2429    }
2430    return(ch_out);
2431 }
2432 
2433 /*
2434  * Invoked if syll[chr_idx-1] is negative (instead of containing
2435  * an octal character value). Fixes appropriate representation
2436  * for "r".
2437  */
expand(void)2438 void expand(void) {
2439    short indx;
2440    indx = -1-syll[chr_idx-1];
2441    if (r_ligs[indx][1] != 0) {
2442       syll[chr_idx] = r_ligs[indx][0];
2443       syll[chr_idx-1] = (r_ligs[indx][1] == 1) ? '\176' : 272;
2444       syll[chr_idx+1] = RBRACE;
2445       chr_idx += 2;
2446    }
2447    else {
2448       syll[chr_idx-1] = r_ligs[indx][0];
2449       syll[chr_idx] = '\175';
2450       chr_idx += 1;
2451    }
2452    cons_code = 0;
2453 }
2454 
2455 /*
2456  * Search inbuf[] for "{\dn" followed by non-letter or, in dollar
2457  * mode only, for "$". Return TRUE or FALSE as appropriate. If
2458  * TRUE, send inbuf[] text prior to mode-switch to outbuf[], followed
2459  * by mode-switch indicator, and make inbuf[] start at beginning of
2460  * Devanagari text.
2461  */
find_dn(void)2462 char find_dn(void) {
2463    char *d_ptr;
2464    char *dn_ptr;
2465    char *svbuf;
2466    int again;
2467    d_found = FALSE;
2468    svbuf = inbuf;
2469    do {
2470       again = FALSE;
2471       dn_ptr = strstr(svbuf, "{\\dn");
2472       if (dn_ptr != NULL) {
2473 	 again = isalpha((unsigned char)dn_ptr[4]);
2474 	 svbuf = dn_ptr+4;
2475       }
2476    }
2477    while (again);
2478    if (dollar_mode) {
2479       d_ptr = strchr(inbuf, '$');
2480       if ((d_ptr != NULL) && ((dn_ptr == NULL) || (d_ptr < dn_ptr))) {
2481 	 d_found = TRUE;
2482 	 dn_ptr = d_ptr;
2483       }
2484    }
2485    if (dn_ptr == NULL) return(FALSE);
2486    strncat(outbuf, inbuf, dn_ptr-inbuf);
2487    no_dn = FALSE;
2488    if (!d_found) {
2489       if (dn_ptr[4] == '#') {              /* equivalent to @dolmode3 */
2490 	 no_dn = TRUE;
2491 	 dn_ptr += 1;
2492       }
2493       else strcat(outbuf, "{\\dn");
2494       dn_ptr += 4;
2495    }
2496    else {
2497       switch(dollar_mode) {
2498        case 1:
2499 	 strcat(outbuf, "{\\dn ");
2500 	 break;
2501        case 2:
2502 	 strcat(outbuf, "\\pdn ");
2503        case 3:
2504 	 no_dn = TRUE;
2505       }
2506       dn_ptr += 1;
2507    }
2508    memmove(inbuf, dn_ptr, strlen(dn_ptr)+1);
2509    return(TRUE);
2510 }
2511 
2512 /*
2513  * Return index of character value in syll[] starting at syll[i],
2514  * -1 if none
2515  */
sindex(int i,short t)2516 int sindex(int i, short t) {
2517    int j;
2518    for (j = i; j < chr_idx; j++) {
2519       if (syll[j] == t) return j;
2520    }
2521    return -1;
2522 }
2523 
2524 /*
2525  * Replace infelicitous conjuncts
2526  */
fixconj(short * wrong,short * right)2527 void fixconj(short *wrong, short *right) {
2528    int i;
2529    for (i = 0; i < chr_idx; i++) {
2530       if (syll[i] != wrong[i]) return;
2531    }
2532    for (i = 0; right[i] != 0; i++) {
2533       syll[i] = right[i];
2534    }
2535    chr_idx = i;
2536 }
2537 
2538 /*
2539  * Read a line with either line ending (UNIX = LF, DOS = CR LF, Mac = CR)
2540  * return the pointer to the buffer of NULL at end of file or I/O Error
2541  * (replacement of fgets, requires global variables)
2542  */
fgetline(char * buf,int n,FILE * f)2543 char *fgetline(char *buf, int n, FILE *f) {
2544    int k;
2545    buf[0] = '\0';
2546    for (k = 0; k < n-1 && (k == 0 || buf[k-1] != '\n'); ) {
2547       if (charpresent) charpresent = FALSE;
2548       else charbuf = fgetc(f);
2549       if (wasCR) {
2550          wasCR = FALSE;
2551          buf[k++] = '\n';
2552          if (charbuf != '\n') charpresent = TRUE;
2553       } else {
2554          if (charbuf == EOF) {
2555             if (k) {
2556                charpresent = TRUE;   break;
2557             }
2558             else return NULL;
2559          }
2560          if (charbuf == '\r') wasCR = TRUE;
2561          else buf[k++] = charbuf;
2562       }
2563    }
2564    buf[k] = '\0';
2565    return buf;
2566 }
2567