/* $Id: devnag.c,v 1.15 2008-03-09 15:57:59 icebearsoft Exp $ Version 2.17 * Preprocessor for Devanagari for TeX package Copyright (C) 1991-2016 University of Groningen, The Netherlands * Author : Frans J. Velthuis Date : 09 May 1991 * The maintainer of this program is now Zdenek Wagner . * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Modified by Marc Csernel, March 1998, to support LaTeX commands with arguments and environments between the dn delimiters. */ /* Version 2.0 of devnag is the first major revision of the program since its original release. It is based on version 1.6 (there are no releases 1.7, 1.8 or 1.9), and, in addition to Marc Csernel's LaTeX extensions, it incorporates the following modifications made by John Smith in 1998-9: 1. A major change has been made to the way in which the preprocessor handles "@" commands. Previously it was only possible to use these at the start of the .dn file; now they may be placed anywhere in the text, provided that they do not occur within passages of Devanagari (where "@" has its own meaning). New "negative" commands have been added to reverse the effect of most existing commands: thus it is now possible to enable or disable specific features for specific passages of text. @hindi may be disabled with @sanskrit; @dollars with @nodollars or @dolmode0; @hyphen with @nohyphen; @tabs (a new LaTeX feature added by Marc Csernel) with @notabs. Since "@" is a perfectly legal character in TeX, lines beginning with "@" that do not match any valid "@" command are flagged with a warning, but processing of the file continues. (In the somewhat unlikely event that you actually need to have a line of TeX text consisting exactly of, say, "@hindi", the preprocessor may be fooled by typing "{}@hindi", "{@hindi}", or anything similar.) 2. The old line-splitting code and the @obeylines preprocessor command to disable it have been withdrawn, so that the program now outputs one line to the .tex file for every line of the .dn file. In devnag 2.0, attempted use of @obeylines produces a specific warning; this is likely to be withdrawn in a future release, after which attempts to use it will cause "@obeylines" to be typeset as a part of the text of the document (see 1. above). 3. To complete the line-by-line correspondence between the input and output files, comments are no longer stripped out, and preprocessor commands (such as "@hyphen") are passed through into the output file in the form of comments ("%@hyphen"). 4. There is now an option to show the version of the program. If devnag is invoked as "devnag -v" it will print a banner giving the version number and other information, and then exit. 5. If the input file has a name such as "x.y.dn", the output file will now be "x.y.tex", not, as previously, "x.tex". In addition, it is now mandatory for the files to have the suffixes ".dn" and ".tex", though as before these are supplied by the program if omitted by the user. This change has been made for safety reasons: formerly, typing "devnag myfile.tex" would delete the contents of myfile.tex. 6. The sequence ~nj~na now produces the correct form. 7. The sequences r.l and r.L now produce the correct forms. 8. The sequences ttrya, ttrva, khrya and khrva now produce the correct forms. 9. The correct "caret" form of -r is now produced under kh, ch, etc., even when a subscript vowel or viraama follows. This has necessitated a new macro (\qc), which must be defined in dnmacs.tex (for Plain TeX) or dev.sty (for LaTeX). The definition is: \def\qc#1#2{\setbox0=\hbox{#1}#1\subscr{\char126\kern1.5ex\lower1.25ex \hbox{\char#2}\kern-1.5ex}} Suitable versions of the files are distributed with the current release of the devnag package. 10. A fragment of code which permitted hyphenation in the middle of a consonant sequence containing a viraama has been removed. All hyphenation before consonants stopped with viraama has also been eliminated. 11. A change has been introduced in the output that devnag produces from input such as {\dn .sa.t"siraa.h}, i.e. in cases where an i-matra vowel is associated with a consonant sequence containing a viraama. The previous behaviour was to treat the consonant sequence as if it were a normal conjunct by placing the vowel symbol before the sequence as a whole. The majority opinion seems to be that this is undesirable, and that the vowel symbol should follow the consonant to which the viraama is attached. A change has therefore been made to implement this. However, the previous behaviour can be reinstated by means of a new preprocessor command, @vconjuncts. 12. Tab characters in the input file, which previously were treated as fatal errors, are now silently converted to spaces. 13. (Internal coding changes) The order of the C code has been normalised from the original Pascal-like bottom-up sequence to a more conventional top-down version. Numerous comments have been added to assist potential future developers in understanding the way the program works. A number of bug-fixes and other improvements have been applied. The insecure gets() calls used in prompting for user input have been replaced by fgets(). Variables used only within a single function have been declared local to that function. Some variables have been renamed in the interests of clarity and consistency, and some redundant ones have been eliminated. The functions ch_find() and st_find(), which simply duplicated the standard library functions strchr() and strstr(), have also been eliminated. */ /* Version 2.01 is a bugfix release, containing two changes in coding. (1) Previous versions of devnag have output "[" and "]" for Devanagari half-"sa and half-gha. Under some circumstances this can interact badly with LaTeX, and the form of output has been changed to avoid the problem. (2) \cline has been added to the list of LaTeX commands which devnag knows how to handle. -- John Smith */ /* Version 2.02 redefines all ^^* characters to be output as \3XXw because it is more robust. It puts a definition of \DevnagVersion at the beginning of the output file so that the new preprocessed files can be automatically recognized (Z. Wagner, zdenek.wagner@gmail.com, http://icebearsoft.euweb.cz). This version also defines a new preprodessor directive @modernhindi, which functions in the same way as @hindi but uses far fewer Sanskrit-style ligatures, preferring conjuncts built from half-consonant forms wherever possible. (Thanks to Raymond Doctor for helpful advice on which ligatures to permit and which to suppress.) The definition of @sanskrit has been changed so that it now restores all the ligatures disabled by @modernhindi, not just those disabled by @hindi. The sequences kk.sa, .tk.sa, dmya and hmya have been modified to produce k+k.sa, .t+k.sa, d+mya, h+mya (rather than, as previously, kk+.sa, .tk+.sa dm+ya, hm+ya). The code which handles all such substitutions has been radically improved. The special warning message prompted by the long-defunct preprocessor directive @obeylines has been removed. -- John Smith */ /* Version 2.1 makes the following changes: * * Material within {\dn ... } (or $ ... $) may be enclosed within angle brackets < ... >; it is then not processed, but is passed through verbatim to the output file, e.g. {\dn eka do <\hrule width 10 em> tiina caara} * Changes to the fonts have made an "open ya" glyph available. This is now used wherever the preprocessor would previously have produced consonant + viraama + ya. * The fonts now contain defined glyphs for .n.na; the preprocessor uses these. * In previous releases the "half j~na" glyph was not used in @hindi and @modernhindi modes (the program output "half ja + half ~na"). This has been corrected. * The output now avoids using the following control symbols: \", \#, \$, \%, \&, \'. -- John Smith */ /* Version 2.11 is a bugfix release to deal with two problems: * * Newline characters were not being properly handled in material enclosed within angle brackets < ... >; * The input sequence ~a was producing the wrong output for the initial form of the vowel. -- John Smith */ /* Version 2.12 contains two modification by Kevin Carmody, one to add the ligature disabling character "+", and one bug fix to avoid the use of non-standard syntax in invocations of fixconj(). */ /* Bug fix suggested by John Smith: put_syll() just after case '<' in order to emit pending syllable. The verbatim material should appear between words but you can even write {\dn pa<\kern -2pt >de} if you know what you are doing. However, such use is strongly discouraged. */ /* Version 2.13: ~m is now a synonym for /, i.e. candrabindu */ /* Version 2.14 contains the following modifications: Accented characters with codes >= 128 are allowed in \dn texts within {\rm ...} and <...> UTF-8 is not yet supported. The filename extensions are not fixed but have reasoanble defaults defined by constants DEFAULT_SRC_EXT (= ".dn") and DEFAULT_DEST_EXT (= ".tex"). It is explicitly forbidden for the source file to have the extension defined by DEFAULT_DEST_EXT. The file names are examined as follows: 1. If the source filename does not have the default extension, it is appended. If such a file does not exist, the preprocessor tries again with the original file name. 2. If the destination filename was given, its extension is checked. It is explicitly forbidden for the destination file to have the extension defined by DEFAULT_SRC_EXT. If the filename is equal to the name of the source file, DEFAULT_DEST_EXT is appended, otherwise the file name is used as is. 3. If the destination filename was not given, it is based upon the source filename. If the source filename contains the DEFAULT_SRC_EXT extension, it is stripped of. Afterwards the DEFAULT_DEST_EXT extension is appended. The algorithm was designed to prevent typing errors but it is far from foolproof. The filesystem properties are not examined and all filename tests are case insensitive. The file names are not expanded. For instance, if the working directory is "/some/path", then "x.y", "./x.y", "../path/x.y" as well as "/some/path/x.y" refer to the same file but they will be treated as different by the preprocessor. Moreover, the algorithm does not try to follow symlinks neither does it examine inodes in order to discover hard links. Notes for compilation: Some C libraries do not contain strcasecmp but contain either stricmp or strcmpi (or both) with the same syntax. If it is the case of your compiler, use -DHAS_STRCMPI or -DHAS_STRICMP. GCC/EMX for OS/2 requires -DHAS_STRICMP, gcc 3.3.3 in Linux as well as gcc 2.95.2 from mingw (Windows) know strcasecmp. The preprocessor can read files with any line endings on all platforms (i.e. DOS CRLF, UNIX LF as well as Macintosh CR). More precisely: each CR followed by LF is ignored and each CR followed by anything else is treated as end of line. The line endings of the output file always conform to conventions used on the operating system where the preprocessor runs. */ /* Modifications in version 2.15: \def\DevnagVersion is inserted at the beginning of the second line if the first line starts with %& Error and warning messages written to stderr instead of stdout (requested by Karl Berry) */ /* Modifications in version 2.16: Character and string handling commands were improved by TeX Live developers, these commands cuased errors in several platforms. */ /* Modification in version 2.17 Character '\0' replaced with an empty string so that it compiles using a C++ compiler. The patch is taken from here: https://sourceforge.net/p/miktex/patches/11/ */ const char *version = "2.17"; #include #include #include #include /* Marc Csernel */ /* General constants */ #define TRUE 1 #define FALSE 0 #define VIRAAM 94 #define SMALLBUF 30 #define MEDBUF 512 #define MAXBUF 2048 #define N_NOLIGS (sizeof nolig / sizeof nolig[0]) #define N_MNOLIGS (sizeof modnolig / sizeof modnolig[0]) #define ill_char 29 #define dummy 30 #define end_of_file 30 #define end_of_line 31 /* * Constants used in char_def structs */ /* ch_typ constants */ #define illegal 0 #define cmr 1 #define control 2 #define dn 3 #define numeral 4 /* ch_subtyp constants */ #define lo_vowel 0 #define hi_vowel 1 #define consonant 2 #define special 3 /* Constants representing symbols: defined as indexes into out_string[] */ #define LBRACE 273 #define RBRACE 264 #define RE 263 /* \thinspace on switching from cmr to dn */ #define RDT 266 /* raised r-dot (repha + anusvara) */ #define RN 265 /* numeral, using cmr or dn as appropriate */ #define RS 256 /* \thinspace on switching from dn to cmr */ /* Default extensions */ #define DEFAULT_SRC_EXT ".dn" #define DEFAULT_DEST_EXT ".tex" /* Some C libraries do not have strcasecmp but have either strcmpi or stricmp with the same syntax. Select the one you have. */ #ifdef HAS_STRCMPI #define strcasecmp(s1,s2) strcmpi(s1,s2) #elif HAS_STRICMP #define strcasecmp(s1,s2) stricmp(s1,s2) #endif /* Global variable declarations */ FILE *f_in, *f_out; char *p_in; int charbuf, wasCR = FALSE, charpresent = FALSE; int number; char symbol; char infil[MEDBUF], outfil[MEDBUF]; char inbuf[MAXBUF], outbuf[MAXBUF]; char word[MEDBUF]; short syll[SMALLBUF]; int n_halves, buf_idx; short half_codes[SMALLBUF]; short joincode; unsigned char hindi_mode, mhindi_mode, dollar_mode, d_found; unsigned char tabs_mode; /* Marc Csernel */ unsigned char no_dn, wait_syll, do_hyphen, do_vconjuncts; unsigned char cons_seen, vow_seen, front_r, cmr_mode, num_mode; unsigned char lig_block; /* Kevin Carmody */ short chr_idx, cons_code; const char *banner = "Preprocessor for Devanagari for TeX package\n\ Copyright (C) 1991-1998 University of Groningen, The Netherlands\n\ Author : Frans J. Velthuis \n\ Maintainer : Zdenek Wagner "; /* Function declarations */ int main(int argc, char **argv); void dnproc(void); void put_ch(short code); void sendchar(char c); void put_sym(short code); void put_word(void); void put_syll(void); void tst_half(void); void put_macro(short macro); void err_ill(const char *str); char inp_ch(void); void expand(void); char find_dn(void); int sindex(int i, short t); void fixconj(short *wrong, short *right); char *fgetline(char *buf, int n, FILE *f); /* --------- Addition by Marc Csernel 1998 --------- */ int citation; int optchapter; int linenumber; int test_command(void); char test_sub_com(void); char comm_double_args(void); char comm_special(void); char comm_opt(void); char comm_chapter(void); char comm_cite(void); char *getsubarg(void); char comm_begin(void); char ignore(void); /* ------------------ End Addition ----------------- */ /* * *** Struct declarations: major data types *** */ /* A struct of type char_def contains the following information about any given character: ch_typ: dn -- a character of the Devanagari syllabary cmr -- a Roman character available within {\dn } numeral -- self-evident: either Devanagari or ASCII as specified control -- special TeX character ("\", "{", "}", etc.) illegal -- none of the above ch_subtyp: consonant lo_vowel (subscript) hi_vowel ("regular" and superscript) special -- none of the above ch_code: For ch_typ dn only, gives the octal value of the character's position in the dvng fonts. For vowels, refers to the initial form (ch_subcode refers to non-initial form); composite forms such as initial "o" (= initial "a" + non-initial "o") have special values greater than 257 decimal. ch_subcode: Used for ch_typ dn only. For vowels, gives the octal value of the non-initial form ("a" has the special value 257 decimal). For consonants, gives the index into cons_table[] where the "half-form" of the consonant is defined. */ struct char_def { short ch_typ, ch_subtyp, ch_code, ch_subcode; }; /* A struct of type cons_joins contains the following information about any given consonant or consonant-group Ca: n_ligs: The number of "simple" (two-member) ligatures listed in lig_table[] for Ca. lig_code: The index into lig_table[] of the first such ligature. r_type: Has the value 0 if Ca takes the normal form of following "r" (decimal 125) or if "Cra" is a predefined ligature; 1 if Ca requires the "caret" form of following "r" (decimal 126); 2 if "Cra" is handled as "\qb{Ca}". j_code: Gives the octal value of the "half-form" of Ca if one exists, otherwise 0. */ struct cons_joins { short n_ligs, lig_code; short r_type, j_code; }; /* A struct of type ligs contains the following information about any given ligature: sym_code: The index into char_table[] of the final member of the ligature. May be expressed as as a character constant (e.g. 't') or as a number (e.g. 6, referring to ".s"). sym_new: Gives either (a) the octal value of the ligature, or (b) a negative number to be handled by expand(), which will reverse the sign and subtract 1 (-5 becomes 4), and use the result as an index into r_ligs[]. (This is done for "ktra", "gra", ".dra", "dra", "dhra" and ".s.tra".) join_idx: Gives the index into cons_table[] of the ligature, or 0. in_use: Boolean TRUE or FALSE according to whether the ligature is currently enabled (they are all initially enabled in lig_table[], but may be disabled by e.g. @hindi). */ struct ligs { char sym_code; short sym_new, join_idx; unsigned char in_use; }; /* * *** Data structures used by devnag *** */ /* Ligatures to be disabled by @hindi */ short nolig[] = { 2,3,6,7,16,25,27,34,35,39,40,46,47,49,56,58,59,62,65, 78,81,82,84,85,86,87,88,89,90,91,92,93,94,95,98,99, 100,101,105 }; /* Ligatures to be disabled by @modernhindi */ short modnolig[] = { 0,1,2,3,4,6,7,9,10,11,12,14,16,17,18,19,20,21,22,23, 24,25,26,27,28,29,30,31,32,33,34,35,36,39,40,41,42,43, 44,45,46,47,48,49,50,52,53,54,56,58,59,62,64,65,69,70, 71,72,73,74,75,76,77,78,80,81,82,84,85,86,87,88,89,90,91, 92,93,94,95,98,99,100,101,102,103,104,105,118,119 }; /* * ligidxs[] maps the ligature-numbers that the user may specify * with @lig and @nolig to the appropriate entries in lig_table[] */ short ligidxs[] = { 0,1,2,3,4,6,7,9,11,12,13,14,118,16,17,18,19,20,21,22, 23,24,25,26,27,28,29,30,31,32,33,34,35,36,38,39,40,41, 42,43,44,45,46,47,48,49,50,52,53,119,54,55,56,58,59,60, 61,62,63,64,65,66,68,69,70,71,72,73,74,75,76,77,78,80, 81,82,84,85,86,87,88,89,90,91,92,93,94,95,96,98,99,100, 101,102,104,117,105,106,107,108,109,110,111,112,113 }; /* * In the following character sets, final space is a dummy value */ /* Characters that may be preceded by "." */ char chset1[] = { 'k','t','d','o','n','s','r','g', 'h','a','.','K','T','D','l','L','R','m',' ' }; /* Characters with a special meaning if preceded by "a" */ char chset2[] = {'a','i','u',' '}; /* Characters that may be preceded by '"' */ char chset3[] = {'n','s',' '}; /* Characters that may be followed by "h" */ char chset4[] = {'k','g','c','j','t','d','p','b','R',' '}; /* Replacements for chset2 characters when preceded by "a" */ char chset5[] = {'A','E','O'}; /* Characters that may be preceded by "~" */ char chset6[] = {'n','o','a','r','m',' '}; /* * Private character set used internally by devnag -- basically * ASCII, but with special characters allotted slots below 32: * conversion from input representation done by dnproc(). */ struct char_def char_table[] = { {illegal,special,0,0}, /* 1 not used */ {dn,consonant,'\126',34}, /* 2 .t */ {dn,consonant,'\130',36}, /* 3 .d */ {dn,special,'\72',0}, /* 4 .o */ {dn,consonant,'\132',1}, /* 5 .n */ {dn,consonant,'\161',2}, /* 6 .s */ {dn,lo_vowel,'\33',2}, /* 7 .r */ {dn,consonant,'\13',3}, /* 8 .g */ {dn,special,'\54',0}, /* 9 .h */ {dn,special,'\137',0}, /* 10 .a */ {dn,special,'\24',0}, /* 11 .. */ {dn,consonant,'\14',4}, /* 12 .K */ {dn,consonant,'\127',35}, /* 13 .T */ {dn,consonant,'\131',57}, /* 14 .D */ {dn,lo_vowel,'\30','\37'}, /* 15 .l */ {dn,lo_vowel,'\31','\174'}, /* 16 .L */ {dn,lo_vowel,'\21','\16'}, /* 17 .R */ {dn,consonant,'\122',32}, /* 18 "n */ {dn,consonant,'\146',5}, /* 19 "s */ {dn,consonant,'\170',0}, /* 20 Rh */ {dn,consonant,'\32',11}, /* 21 ~n */ {dn,hi_vowel,267,262}, /* 22 ~o */ {dn,hi_vowel,275,4}, /* 23 ~a */ {dn,consonant,'\35',31}, /* 24 ~r */ {illegal,special,0,0}, /* 25 not used */ {illegal,special,0,0}, /* 26 not used */ {illegal,special,0,0}, /* 27 not used */ {illegal,special,0,0}, /* 28 not used */ {illegal,special,0,0}, /* 29 not used */ {control,special,0,0}, /* 30 dummy */ {control,special,0,0}, /* 31 end_of_line */ {control,special,0,0}, /* 32 space */ {cmr,special,0,0}, /* ! */ {illegal,special,0,0}, /* " */ {dn,special,'\25',0}, /* # */ {illegal,special,0,0}, /* $ */ {control,special,0,0}, /* % */ {dn,special,0,0}, /* & */ {cmr,special,0,0}, /* ' to * */ {cmr,special,0,0}, /* ' to * */ {cmr,special,0,0}, /* ' to * */ {cmr,special,0,0}, /* ' to * */ {dn,special,0,0}, /* + */ /* Kevin Carmody: * "+" changed * from cmr * to dn */ {cmr,special,0,0}, /* , to - */ {cmr,special,0,0}, /* , to - */ {illegal,special,0,0}, /* . */ {dn,special,'\40',0}, /* / */ {numeral,special,0,0}, /* 0 to 9 */ {numeral,special,0,0}, /* 0 to 9 */ {numeral,special,0,0}, /* 0 to 9 */ {numeral,special,0,0}, /* 0 to 9 */ {numeral,special,0,0}, /* 0 to 9 */ {numeral,special,0,0}, /* 0 to 9 */ {numeral,special,0,0}, /* 0 to 9 */ {numeral,special,0,0}, /* 0 to 9 */ {numeral,special,0,0}, /* 0 to 9 */ {numeral,special,0,0}, /* 0 to 9 */ {cmr,special,0,0}, /* : and ; */ {cmr,special,0,0}, /* : and ; */ {control,special,0,0}, /* < */ {cmr,special,0,0}, /* = */ {control,special,0,0}, /* > */ {cmr,special,0,0}, /* ? */ {dn,special,'\177',0}, /* @ */ {dn,hi_vowel,258,'\101'}, /* A */ {dn,consonant,'\102',6}, /* B */ {dn,consonant,'\103',33}, /* C */ {dn,consonant,'\104',7}, /* D */ {dn,hi_vowel,259,'\173'}, /* E */ {illegal,special,0,0}, /* F */ {dn,consonant,'\107',8}, /* G */ {dn,special,'\54',0}, /* H */ {dn,hi_vowel,'\111','\106'}, /* I */ {dn,consonant,'\112',9}, /* J */ {dn,consonant,'\113',10}, /* K */ {dn,consonant,'\17',30}, /* L */ {dn,special,'\134',0}, /* M */ {illegal,special,0,0}, /* N */ {dn,hi_vowel,260,'\117'}, /* O */ {dn,consonant,'\120',12}, /* P */ {illegal,special,0,0}, /* Q */ {dn,consonant,'\167',0}, /* R */ {illegal,special,0,0}, /* S */ {dn,consonant,'\124',13}, /* T */ {dn,lo_vowel,'\125',1}, /* U */ {illegal,special,0,0}, /* V to Z */ {illegal,special,0,0}, /* V to Z */ {illegal,special,0,0}, /* V to Z */ {illegal,special,0,0}, /* V to Z */ {illegal,special,0,0}, /* V to Z */ {cmr,special,0,0}, /* [ */ {control,special,0,0}, /* \ */ {cmr,special,0,0}, /* ] */ {illegal,special,0,0}, /* ^ */ {dn,special,0,0}, /* _ */ /* Marc Csernel: * "_" changed * from illegal * to dn */ {cmr,special,0,0}, /* ` */ {dn,hi_vowel,'\141',257}, /* a */ {dn,consonant,'\142',14}, /* b */ {dn,consonant,'\143',15}, /* c */ {dn,consonant,'\144',37}, /* d */ {dn,hi_vowel,'\145',3}, /* e */ {dn,consonant,'\47',16}, /* f */ {dn,consonant,'\147',17}, /* g */ {dn,consonant,'\150',38}, /* h */ {dn,hi_vowel,'\151','\105'}, /* i */ {dn,consonant,'\152',18}, /* j */ {dn,consonant,'\153',19}, /* k */ {dn,consonant,'\154',20}, /* l */ {dn,consonant,'\155',21}, /* m */ {dn,consonant,'\156',22}, /* n */ {dn,hi_vowel,261,'\157'}, /* o */ {dn,consonant,'\160',23}, /* p */ {dn,consonant,'\52',24}, /* q */ {dn,consonant,'\162',0}, /* r */ {dn,consonant,'\163',25}, /* s */ {dn,consonant,'\164',26}, /* t */ {dn,lo_vowel,'\165',0}, /* u */ {dn,consonant,'\166',27}, /* v */ {illegal,special,0,0}, /* not used */ {illegal,special,0,0}, /* not used */ {dn,consonant,'\171',28}, /* y */ {dn,consonant,'\51',29}, /* z */ {control,special,0,0}, /* left brace */ {dn,special,'\56',0}, /* | */ {control,special,0,0}, /* right brace */ {illegal,special,0,0}, /* ~ */ {illegal,special,0,0} /* del */ }; /* * Unordered table of consonants and consonant-groups giving * information on the available ligatures, the form taken by a * following "r", and the location in the dvng fonts of the * "half-form" (if any) */ struct cons_joins cons_table[] = { {0,0,1,0}, /* 0 */ {1,120,0,'\27'}, /* 1 .n */ {2,100,0,'\11'}, /* 2 .s */ {0,0,0,'\34'}, /* 3 .g */ {0,0,1,'\7'}, /* 4 .k */ {6,94,0,'\133'}, /* 5 "s */ {1,88,0,'\74'}, /* 6 B */ {2,78,0,'\100'}, /* 7 D */ {1,16,0,'\135'}, /* 8 G */ {0,0,1,'\44'}, /* 9 J */ {0,0,1,'\110'}, /* 10 K */ {2,39,1,'\26'}, /* 11 ~n */ {1,115,0,'\45'}, /* 12 P */ {0,0,0,'\114'}, /* 13 T */ {3,85,0,'\116'}, /* 14 b */ {2,34,0,'\121'}, /* 15 c */ {1,116,0,'\10'}, /* 16 f */ {1,15,0,'\140'}, /* 17 g */ {2,37,0,'\76'}, /* 18 j */ {9,0,0,'\77'}, /* 19 k */ {1,91,0,'\123'}, /* 20 l */ {2,89,0,'\115'}, /* 21 m */ {1,80,0,'\6'}, /* 22 n */ {4,81,0,'\75'}, /* 23 p */ {0,0,1,'\12'}, /* 24 q */ {2,105,0,'\55'}, /* 25 s */ {3,55,0,'\50'}, /* 26 t */ {2,92,0,'\46'}, /* 27 v */ {0,0,1,'\5'}, /* 28 y */ {1,114,0,'\36'}, /* 29 z */ {0,0,1,'\20'}, /* 30 L */ {0,0,1,'\35'}, /* 31 ~r */ {8,17,1,0}, /* 32 "n */ {1,36,1,0}, /* 33 C */ {4,41,1,0}, /* 34 .t */ {1,45,1,0}, /* 35 .th */ {6,46,1,0}, /* 36 .d */ {11,58,2,0}, /* 37 d */ {7,107,1,0}, /* 38 h */ {3,9,1,0}, /* 39 k t */ {1,14,1,0}, /* 40 k v */ {3,25,1,0}, /* 41 "n k */ {1,28,1,0}, /* 42 "n kh */ {1,29,1,0}, /* 43 "n g */ {2,30,1,0}, /* 44 "n gh */ {1,32,1,0}, /* 45 "n k t */ {1,33,1,0}, /* 46 "n k .s */ {1,52,1,0}, /* 47 .d g */ {1,53,1,0}, /* 48 .d gh */ {2,71,2,0}, /* 49 d d */ {2,73,2,0}, /* 50 d dh */ {1,75,1,0}, /* 51 d bh */ {1,77,2,0}, /* 52 d v */ {3,102,1,0}, /* 53 .s .t */ {0,0,1,'\43'}, /* 54 k .s */ {0,0,1,0202}, /* 55 t t */ {1,117,1,0}, /* 56 .s .t r */ {1,54,1,0}, /* 57 .dh */ {1,12,1,0}, /* 58 k n */ {1,13,1,0}, /* 59 k r */ {0,0,1,0351}, /* 60 g r */ {0,0,1,0350}, /* 61 gh n */ {0,0,1,0352}, /* 62 j ~n */ {0,0,1,0353}, /* 63 ~n c */ {0,0,1,0354}, /* 64 t r */ {1,69,1,0}, /* 65 d g */ {1,70,1,0}, /* 66 d gh */ {0,0,1,0361}, /* 67 dh n */ {0,0,1,0362}, /* 68 dh r */ {0,0,1,0363}, /* 69 p t */ {0,0,1,0364}, /* 70 "s c */ {0,0,1,0365}, /* 71 "s r */ {0,0,1,0366}, /* 72 "s v */ {1,118,1,0}, /* 73 k t r */ {1,119,1,0}, /* 74 .d r */ {1,76,1,0}, /* 75 d r */ {0,0,2,0} /* 76 d b */ }; /* * Ordered table of available ligatures giving each ligature's * position in the dvng fonts (or a negative number for use by * expand()) and index into cons_table[] * * Note that lig_table[96] is an entry for a ligature that was * removed from the fonts as of release 2.1. It is therefore * marked as (permanently) FALSE; it has not been removed from * the table since to do so would change the index values of all * succeeding entries, and these occur frequently in the code. */ struct ligs lig_table[] = { {'k',0303,0,TRUE}, /* 0 k k */ {'t',0304,39,TRUE}, /* 1 k t */ {'n',0307,58,TRUE}, /* 2 k n */ {'m',0311,0,TRUE}, /* 3 k m */ {'y',0310,0,TRUE}, /* 4 k y */ {'r',0207,59,TRUE}, /* 5 k r */ {'l',0312,0,TRUE}, /* 6 k l */ {'v',0313,40,TRUE}, /* 7 k v */ {6,042,54,TRUE}, /* 8 k .s */ {'y',0305,0,TRUE}, /* 9 k t y */ {'r',-5,73,TRUE}, /* 10 k t r */ {'v',0306,0,TRUE}, /* 11 k t v */ {'y',0346,0,TRUE}, /* 12 k n y */ {'y',0347,0,TRUE}, /* 13 k r y */ {'y',0314,0,TRUE}, /* 14 k v y */ {'r',-2,60,TRUE}, /* 15 g r */ {'n',0315,61,TRUE}, /* 16 gh n */ {'k',0254,41,TRUE}, /* 17 "n k */ {'K',0262,42,TRUE}, /* 18 "n kh */ {'g',0275,43,TRUE}, /* 19 "n g */ {'G',0277,44,TRUE}, /* 20 "n gh */ {18,0274,0,TRUE}, /* 21 "n "n */ {'n',0265,0,TRUE}, /* 22 "n n */ {'m',0301,0,TRUE}, /* 23 "n m */ {'y',0302,0,TRUE}, /* 24 "n y */ {'t',0255,45,TRUE}, /* 25 "n k t */ {'y',0257,0,TRUE}, /* 26 "n k y */ {6,0260,46,TRUE}, /* 27 "n k .s */ {'y',0272,0,TRUE}, /* 28 "n kh y */ {'y',0276,0,TRUE}, /* 29 "n g y */ {'y',0271,0,TRUE}, /* 30 "n gh y */ {'r',0300,0,TRUE}, /* 31 "n gh r */ {'y',0256,0,TRUE}, /* 32 "n k t y */ {'v',0261,0,TRUE}, /* 33 "n k .s v */ {'c',0316,0,TRUE}, /* 34 c c */ {21,0317,0,TRUE}, /* 35 c ~n */ {'y',0320,0,TRUE}, /* 36 ch y */ {21,0342,62,TRUE}, /* 37 j ~n */ {'r',0205,0,TRUE}, /* 38 j r */ {'c',0321,63,TRUE}, /* 39 ~n c */ {'j',0322,0,TRUE}, /* 40 ~n j */ {'k',0326,0,TRUE}, /* 41 .t k */ {2,0323,0,TRUE}, /* 42 .t .t */ {13,0341,0,TRUE}, /* 43 .t .th */ {'y',0324,0,TRUE}, /* 44 .t y */ {'y',0325,0,TRUE}, /* 45 .th y */ {'g',0263,47,TRUE}, /* 46 .d g */ {'G',0264,48,TRUE}, /* 47 .d gh */ {3,0345,0,TRUE}, /* 48 .d .d */ {'m',0273,0,TRUE}, /* 49 .d m */ {'y',0267,0,TRUE}, /* 50 .d y */ {'r',-6,74,TRUE}, /* 51 .d r */ {'y',0270,0,TRUE}, /* 52 .d g y */ {'r',0266,0,TRUE}, /* 53 .d gh r */ {'y',0344,0,TRUE}, /* 54 .dh y */ {'t',0201,55,TRUE}, /* 55 t t */ {'n',0327,0,TRUE}, /* 56 t n */ {'r',057,64,TRUE}, /* 57 t r */ {'g',0213,65,TRUE}, /* 58 d g */ {'G',0212,66,TRUE}, /* 59 d gh */ {'d',0214,49,TRUE}, /* 60 d d */ {'D',0210,50,TRUE}, /* 61 d dh */ {'n',0221,0,TRUE}, /* 62 d n */ {'b',0223,76,TRUE}, /* 63 d b */ {'B',0211,51,TRUE}, /* 64 d bh */ {'m',0224,0,TRUE}, /* 65 d m */ {'y',0215,0,TRUE}, /* 66 d y */ {'r',-3,75,TRUE}, /* 67 d r */ {'v',0222,52,TRUE}, /* 68 d v */ {'r',0355,0,TRUE}, /* 69 d g r */ {'r',0356,0,TRUE}, /* 70 d gh r */ {'y',0220,0,TRUE}, /* 71 d d y */ {'v',0370,76,TRUE}, /* 72 d d v */ {'y',0217,0,TRUE}, /* 73 d dh y */ {'v',0371,0,TRUE}, /* 74 d dh v */ {'y',0216,0,TRUE}, /* 75 d bh y */ {'y',0357,0,TRUE}, /* 76 d r y */ {'y',0225,0,TRUE}, /* 77 d v y */ {'n',0360,67,TRUE}, /* 78 dh n */ {'r',-4,68,TRUE}, /* 79 dh r */ {'n',0340,0,TRUE}, /* 80 n n */ {'t',0330,69,TRUE}, /* 81 p t */ {'n',0331,0,TRUE}, /* 82 p n */ {'r',0376,0,TRUE}, /* 83 p r */ {'l',0332,0,TRUE}, /* 84 p l */ {'n',0247,0,TRUE}, /* 85 b n */ {'b',0251,0,TRUE}, /* 86 b b */ {'v',0333,0,TRUE}, /* 87 b v */ {'n',0336,0,TRUE}, /* 88 bh n */ {'n',0337,0,TRUE}, /* 89 m n */ {'l',0335,0,TRUE}, /* 90 m l */ {'l',0245,0,TRUE}, /* 91 l l */ {'n',0246,0,TRUE}, /* 92 v n */ {'v',0250,0,TRUE}, /* 93 v v */ {'c',0226,70,TRUE}, /* 94 "s c */ {'n',0227,0,TRUE}, /* 95 "s n */ {'b',0233,0,FALSE}, /* 96 "s b */ {'r',0231,71,TRUE}, /* 97 "s r */ {'l',0232,0,TRUE}, /* 98 "s l */ {'v',0230,72,TRUE}, /* 99 "s v */ {2,0243,53,TRUE}, /* 100 .s .t */ {13,0244,0,TRUE}, /* 101 .s .th */ {'y',0367,0,TRUE}, /* 102 .s .t y */ {'r',-1,56,TRUE}, /* 103 .s .t r */ {'v',0253,0,TRUE}, /* 104 .s .t v */ {'n',0334,0,TRUE}, /* 105 s n */ {'r',0372,0,TRUE}, /* 106 s r */ {5,0242,0,TRUE}, /* 107 h .n */ {'n',0241,0,TRUE}, /* 108 h n */ {'m',0234,0,TRUE}, /* 109 h m */ {'y',0235,0,TRUE}, /* 110 h y */ {'r',0240,0,TRUE}, /* 111 h r */ {'l',0236,0,TRUE}, /* 112 h l */ {'v',0237,0,TRUE}, /* 113 h v */ {'r',0206,0,TRUE}, /* 114 z r */ {'r',0203,0,TRUE}, /* 115 ph r */ {'r',0204,0,TRUE}, /* 116 f r */ {'y',0252,0,TRUE}, /* 117 .s .t r y */ {'y',0374,0,TRUE}, /* 118 k t r y */ {'y',0373,0,TRUE}, /* 119 .d r y */ {5,0233,0,TRUE} /* 120 .n .n */ }; /* * Table of the actual codes output by devnag */ const char *out_string[] = { "\\7{","\\8{","\\9{","\\?","\\<","\\305w","\\306w", /* 0-6 */ "\\307w","\\308w","\\309w","\\30Aw","\\30Bw","\\30Cw", /* 7-12 */ "\\0","\\qx{","\\30Fw","\\310w","\\311w","\\312w", /* 13-18 */ "\\313w","\\314w","\\315w","\\316w","\\317w","\\318w", /* 19-24 */ "\\319w","\\31Aw","\\31Bw","\\31Cw","\\31Dw", /* 25-29 */ "\\31Ew","\\qy{","\\1","!","\\322w","\\323w","\\324w", /* 30-36 */ "\\325w","\\326w","\\327w","(",")","\\32Aw","+",",", /* 37-44 */ "-",".","/","0","1","2","3","4","5","6","7","8","9", /* 45-57 */ ":",";","<","=",">","?","@", /* 58-64 */ "A","B","C","D","E","F","G","H","I","J","K","L","M", /* 65-77 */ "N","O","P","Q","R","S","T","U","V","W","X","Y","Z", /* 78-90 */ "\\35Bw","\\2","\\35Dw","\\qq{","\\35Fw","`", /* 91-96 */ "a","b","c","d","e","f","g","h","i","j","k","l","m", /* 97-109 */ "n","o","p","q","r","s","t","u","v","w","x","y","z", /* 110-122 */ "\\4","\\qz{","\\5","\\6{","\\37Fw", /* 123-127 */ "\\380w","\\381w","\\382w","\\383w","\\384w","\\385w", /* 128-133 */ "\\386w","\\387w","\\388w","\\389w","\\38Aw","\\38Bw", /* 134-139 */ "\\38Cw","\\38Dw","\\38Ew","\\38Fw","\\390w","\\391w", /* 140-145 */ "\\392w","\\393w","\\394w","\\395w","\\396w","\\397w", /* 146-151 */ "\\398w","\\399w","\\39Aw","\\39Bw","\\39Cw","\\39Dw", /* 152-157 */ "\\39Ew","\\39Fw","\\3A0w","\\3A1w","\\3A2w","\\3A3w", /* 158-163 */ "\\3A4w","\\3A5w","\\3A6w","\\3A7w","\\3A8w","\\3A9w", /* 164-169 */ "\\3AAw","\\3ABw","\\3ACw","\\3ADw","\\3AEw","\\3AFw", /* 170-175 */ "\\3B0w","\\3B1w","\\3B2w","\\3B3w","\\3B4w","\\3B5w", /* 176-181 */ "\\3B6w","\\3B7w","\\3B8w","\\3B9w","\\3BAw","\\3BBw", /* 182-187 */ "\\3BCw","\\3BDw","\\3BEw","\\3BFw","\\3C0w","\\3C1w", /* 188-193 */ "\\3C2w","\\3C3w","\\3C4w","\\3C5w","\\3C6w","\\3C7w", /* 194-199 */ "\\3C8w","\\3C9w","\\3CAw","\\3CBw","\\3CCw","\\3CDw", /* 200-205 */ "\\3CEw","\\3CFw","\\3D0w","\\3D1w","\\3D2w","\\3D3w", /* 206-211 */ "\\3D4w","\\3D5w","\\3D6w","\\3D7w","\\3D8w","\\3D9w", /* 212-217 */ "\\3DAw","\\3DBw","\\3DCw","\\3DDw","\\3DEw","\\3DFw", /* 218-223 */ "\\3E0w","\\3E1w","\\3E2w","\\3E3w","\\3E4w","\\3E5w", /* 224-229 */ "\\3E6w","\\3E7w","\\3E8w","\\3E9w","\\3EAw","\\3EBw", /* 230-235 */ "\\3ECw","\\3EDw","\\3EEw","\\3EFw","\\3F0w","\\3F1w", /* 236-241 */ "\\3F2w","\\3F3w","\\3F4w","\\3F5w","\\3F6w","\\3F7w", /* 242-247 */ "\\3F8w","\\3F9w","\\3FAw","\\3FBw","\\3FCw","\\3FDw", /* 248-253 */ "\\3FEw","\\3FFw", /* 254-255 */ "{\\rs ","","aA","e\\?","aO","ao","A\\<","\\re}","}", /* 256-264 */ "\\rn{","{\\rdt}","aA\\<","{\\qva}","{\\qvb}","{\\qvc}", /* 265-270 */ "\\qa{","\\qb{","{","\\qc{","a\\<" /* 271-275 */ }; /* * Table specifying the form of following "r" for various consonants * and consonant-groups (used by expand()) */ short r_ligs[6][2] = { {0243,1}, /* .s .t */ {0147,0}, /* g */ {0144,2}, /* d */ {0104,0}, /* dh */ {0304,1}, /* k t */ {0130,1} /* .d */ }; /* --------- Addition by Marc Csernel 1998 --------- */ /* The array contains a list of LaTeX commands preceded by a code: 1 a mandatory argument follows. e.g. \end{enumerate} 2 the text inclosed by a preceding brace must not be translated till next closing brace. 3 two mandatory arguments follow. e.g. \multicolumn{nb}{desc}{text} 4 For begin: a mandatory argument followed by zero, or one optional, depending on the first one e.g. \begin{itemize} or \begin{tabular}{....} 5 a mandatory argument and optional ones which must not be translated. \raisebox{raise}[ht][prof]{text to be translated} 6 one or more optional argument(s) which must not be translated and a mandatory one which must be translated e.g. \framebox[6cm][s]{text to be translated} 7 The command cite: an optional argument which must be translated and a mandatory one which mustn't. 8 Commands like chapter, section... with optional argument which must be translated and a mandatory one which must also be translated. */ typedef struct { int Typ_Com; const char *Name_com; } typcom; typcom TabCom[] = { {4,"begin"}, {1,"end"}, {1,"label"}, {1,"pageref"}, {1,"ref"}, {1,"index"}, {1,"hspace"}, {1,"hspace*"}, {1,"vspace"}, {1,"vspace*"}, {1,"addvspace"}, {1,"enlargethispage"}, {1,"enlargethispage*"}, {1,"include"}, {1,"input"}, {1,"parbox"}, {1,"stepcounter"}, {1,"refstepcounter"}, {1,"bibitem"}, {1,"cline"}, {2,"rm"}, {2,"kern"}, {2,"hskip"}, {2,"vskip"}, {2,"vadjust"}, {3,"addtolength"}, {3,"setlength"}, {3,"setcounter"}, {3,"addtocounter"}, {3,"multicolumn"}, {3,"rule"}, {3,"addcontentsline"}, {5,"raisebox"}, {6,"framebox"}, {6,"makebox"}, {7,"cite"}, {8,"chapter"}, {8,"section"}, {8,"subsection"}, {8,"subsubsection"}, {8,"paragraph"}, {8,"subparagraph"}, {8,"part"}, {0,""} }; /* For the command begin, depending on the first argument we will have zero (default), 1, 2.. other arguments following. Within the following array the first element of an item describes the number of arguments, the second one the name of the first argument related with i.e \begin {tabular} will have 1 argument. */ typcom TabSubCom[]= { {1,"tabular"}, {1,"supertabular"}, {1,"longtable"}, {1,"thebibliography"}, {0,""} }; char command[100]; /* the text of a current command to test */ int nbchcomm; /* the number of character of the curent command */ char subcom [1000]; /* for debugging etc. */ /* This fuction tests if the LaTeX command is one of the TabCom[] array. In this case the code (Typ_Com) is returned. */ int test_command(void) { int i = 0; while (TabCom[i].Name_com[0] != 0) { if (!strcmp(command,TabCom[i++].Name_com)) return TabCom[i-1].Typ_Com; } return 0; } /* This routine writes down all characters it finds; it looks for a '{' followed by some text and a '}'; no nesting of braces is considered. */ char test_sub_com(void) { int nbsub = 0; while (symbol != '}') { sendchar(symbol); subcom[nbsub++] = symbol; subcom[nbsub] = 0; symbol = inp_ch(); } sendchar(symbol); subcom[nbsub++] = symbol; subcom[nbsub] = 0; symbol = inp_ch(); return symbol; } /* This routine writes down all characters it finds; it looks twice for a '{' followed by some text and a '}'; for the second time nesting of braces is considered. */ char comm_double_args(void) { int nbsub = 0; while (symbol != '}') { sendchar(symbol); subcom[nbsub++] = symbol; subcom[nbsub] = 0; symbol = inp_ch(); } /* looking for the end of the second argument */ sendchar(symbol); symbol = inp_ch(); while (symbol != '{') { sendchar(symbol); symbol = inp_ch(); } sendchar(symbol); symbol = inp_ch(); symbol = ignore(); sendchar(symbol); symbol = inp_ch(); return symbol; } /* For LaTeX commands such as \raisebox{5pt}[10pt][25pt]{.sa.t&vidha.m} where the second argument must be translated */ char comm_special(void) { int nbsub = 0; while (symbol != '}') { sendchar(symbol); subcom[nbsub++] = symbol; subcom[nbsub] = 0; symbol = inp_ch(); } while (symbol != '{') { sendchar(symbol); subcom[nbsub++] = symbol; subcom[nbsub] = 0; symbol = inp_ch(); } return symbol; } /* For LaTeX commands such as \framebox[10pt][25pt]{.sa.t&vidha.m} where optional arguments are not translated and the mandatory one must be translated */ char comm_opt(void) { int nbsub = 0; while (symbol != '{') { sendchar(symbol); subcom[nbsub++] = symbol; subcom[nbsub] = 0; symbol = inp_ch(); } return symbol; } char comm_chapter(void) { if (symbol == '[') { optchapter = 1; sendchar(symbol); symbol=inp_ch(); } if (symbol == '*') { sendchar(symbol); symbol=inp_ch(); } return symbol; } char comm_cite(void) { if (symbol == '[') { citation = 1; sendchar(symbol); symbol = inp_ch(); } else test_sub_com(); return symbol; } char *getsubarg(void) { char com[20], *result; int i = 0, j = 0; while (subcom[i] != '{') i++; i++; while (subcom[i] == ' ') i++; while (isalpha((unsigned char)subcom[i])) com[j++]= subcom[i++]; com[j] = 0; i = 0; result = (char*) malloc (strlen(com)+1); strcpy (result,com); return result; } /* The LaTeX command BEGIN treatment */ char comm_begin(void) { int i = 0, nbargs = 0; char *com; int nbsub = 0; while (symbol != '}') { sendchar(symbol); subcom[nbsub++] = symbol; subcom[nbsub] = 0; symbol = inp_ch(); } sendchar(symbol); symbol = inp_ch(); com = getsubarg(); while (TabSubCom[i].Name_com[0] != 0) { if (!strcmp(com,TabSubCom[i++].Name_com)) nbargs = TabSubCom[i-1].Typ_Com; } if (!nbargs) { /* argument not found within the table */ free(com); return symbol; } while (nbargs--) { while (symbol != '{') { sendchar(symbol); subcom[nbsub++] = symbol; subcom[nbsub] = 0; symbol = inp_ch(); } sendchar(symbol); symbol = inp_ch(); symbol = ignore (); } sendchar(symbol); symbol = inp_ch(); free(com); return symbol; } /* This routine assumes that a '{' has already been read; it looks for the corresponding closing brace '}'; nesting of braces is considered. */ char ignore(void) { int nbsub = 0; int nbbraces = 1; while (nbbraces ) { subcom[nbsub++] = symbol; subcom[nbsub] = 0; switch (symbol) { case '{': nbbraces++; sendchar(symbol); break; case '}': nbbraces--; if (!nbbraces) return symbol; sendchar(symbol); break; case ' ': case '\n': sendchar(symbol); put_word(); break; default : sendchar(symbol); break; } symbol = inp_ch(); } return symbol ; } /* ------------------ End Addition ----------------- */ /* * *** Devnag function definitions *** */ int main(int argc, char **argv) { char *s_ptr; int i; unsigned char dn_yes, start_file; /* if invoked with "-v", print banner and exit */ if ((argc == 2) && (strcmp(argv[1], "-v") == 0)) { printf("devnag v%s\n", version); puts(banner); exit(0); } /* get file specifications */ if (argc == 3) { strcpy(infil, argv[1]); strcpy(outfil, argv[2]); } else { if (argc == 2) { strcpy(infil, argv[1]); *outfil = '\0'; } else { do { printf("input file: "); fgets(infil, MEDBUF, stdin); infil[strlen(infil)-1] = '\0'; } while (strlen(infil) == 0); printf("output file: "); fgets(outfil, MEDBUF, stdin); outfil[strlen(outfil)-1] = '\0'; } } /* Check the source file extension */ if (strcasecmp(infil+strlen(infil)-strlen(DEFAULT_DEST_EXT), DEFAULT_DEST_EXT) == 0) { fprintf(stderr, "source file extension %s is forbidden\n", DEFAULT_DEST_EXT); exit(1); } s_ptr = infil+strlen(infil)-strlen(DEFAULT_SRC_EXT); if (strcasecmp(s_ptr, DEFAULT_SRC_EXT) != 0) s_ptr = NULL; if (!s_ptr) strcat(infil, DEFAULT_SRC_EXT); /* Try to open the source file */ f_in = fopen(infil, "rb"); if (!f_in && !s_ptr) { /* strip extension which has not been supplied and try again */ infil[strlen(infil)-strlen(DEFAULT_SRC_EXT)] = '\0'; f_in = fopen(infil, "rb"); } if (!f_in) { if (s_ptr) fprintf(stderr, "cannot open file %s\n", infil); else fprintf(stderr, "cannot open either %s or %s%s\n", infil, infil, DEFAULT_SRC_EXT); exit(1); } /* Destination file name */ if (strlen(outfil) == 0) { strcpy(outfil, infil); if (strcasecmp(outfil+strlen(outfil)-strlen(DEFAULT_SRC_EXT), DEFAULT_SRC_EXT) == 0) { outfil[strlen(outfil)-strlen(DEFAULT_SRC_EXT)] = '\0'; } strcat(outfil, DEFAULT_DEST_EXT); } else { if (strcasecmp(outfil+strlen(outfil)-strlen(DEFAULT_SRC_EXT), DEFAULT_SRC_EXT) == 0) { fclose(f_in); fprintf(stderr, "destination file extension %s is forbidden\n", DEFAULT_SRC_EXT); exit(1); } if (strcasecmp(infil, outfil) == 0) strcat(outfil, DEFAULT_DEST_EXT); } #ifdef TEXLIVE /* In TeX Live we want to share files between Unix and Windows systems. */ #define FOPEN_W_MODE "wb" #else #define FOPEN_W_MODE "w" #endif if ((f_out = fopen(outfil, FOPEN_W_MODE)) == NULL) { fclose(f_in); fprintf(stderr, "cannot open %s for output\n", outfil); exit(1); } /* initialization */ cons_seen = FALSE; vow_seen = FALSE; front_r = FALSE; wait_syll = FALSE; cmr_mode = FALSE; num_mode = FALSE; hindi_mode = FALSE; mhindi_mode = FALSE; dollar_mode = 0; do_hyphen = FALSE; do_vconjuncts = FALSE; tabs_mode = FALSE; /* Marc Csernel */ lig_block = FALSE; /* Kevin Carmody */ start_file = TRUE; chr_idx = 0; n_halves = 0; /* Kevin Carmody */ *word = '\0'; *inbuf = '\0'; *outbuf = '\0'; linenumber = 1; /* main loop */ do { p_in = fgetline(inbuf, MAXBUF, f_in); if (start_file) { if (inbuf[0] != '%' || inbuf[1] != '&') { fprintf(f_out, "\\def\\DevnagVersion{%s}", version); start_file = FALSE; } } /* read preprocessor commands */ if (*inbuf == '@') { while (TRUE) { if (strstr(inbuf, "dollars\n") == inbuf+1) { dollar_mode = 1; fprintf(f_out, "%%%s", inbuf); linenumber++; break; } if (strstr(inbuf, "nodollars\n") == inbuf+1) { dollar_mode = 0; fprintf(f_out, "%%%s", inbuf); linenumber++; break; } if (strstr(inbuf, "dolmode0\n") == inbuf+1) { dollar_mode = 0; fprintf(f_out, "%%%s", inbuf); linenumber++; break; } if (strstr(inbuf, "dolmode1\n") == inbuf+1) { dollar_mode = 1; fprintf(f_out, "%%%s", inbuf); linenumber++; break; } if (strstr(inbuf, "dolmode2\n") == inbuf+1) { dollar_mode = 2; fprintf(f_out, "%%%s", inbuf); linenumber++; break; } if (strstr(inbuf, "dolmode3\n") == inbuf+1) { dollar_mode = 3; fprintf(f_out, "%%%s", inbuf); linenumber++; break; } if (strstr(inbuf, "hindi\n") == inbuf+1) { hindi_mode = TRUE; if (mhindi_mode) { for (i = 0; i < N_MNOLIGS; i++) lig_table[modnolig[i]].in_use = TRUE; mhindi_mode = FALSE; } for (i = 0; i < N_NOLIGS; i++) lig_table[nolig[i]].in_use = FALSE; fprintf(f_out, "%%%s", inbuf); linenumber++; break; } if (strstr(inbuf, "modernhindi\n") == inbuf+1) { hindi_mode = TRUE; mhindi_mode = TRUE; for (i = 0; i < N_MNOLIGS; i++) lig_table[modnolig[i]].in_use = FALSE; fprintf(f_out, "%%%s", inbuf); linenumber++; break; } if (strstr(inbuf, "sanskrit\n") == inbuf+1) { hindi_mode = FALSE; mhindi_mode = FALSE; for (i = 0; i < N_MNOLIGS; i++) lig_table[modnolig[i]].in_use = TRUE; fprintf(f_out, "%%%s", inbuf); linenumber++; break; } if (strstr(inbuf, "lig") == inbuf+1) { s_ptr = inbuf; while (TRUE) { if ((s_ptr = strchr(s_ptr, ' ')) == NULL) break; s_ptr++; if (sscanf(s_ptr, "%d", &number) != 0) lig_table[ligidxs[number-1]].in_use = TRUE; } fprintf(f_out, "%%%s", inbuf); linenumber++; break; } if (strstr(inbuf, "nolig") == inbuf+1) { s_ptr = inbuf; while (TRUE) { if ((s_ptr = strchr(s_ptr, ' ')) == NULL) break; s_ptr++; if (sscanf(s_ptr, "%d", &number) != 0) lig_table[ligidxs[number-1]].in_use = FALSE; } fprintf(f_out, "%%%s", inbuf); linenumber++; break; } if (strstr(inbuf, "hyphen\n") == inbuf+1) { do_hyphen = TRUE; fprintf(f_out, "%%%s", inbuf); linenumber++; break; } if (strstr(inbuf, "nohyphen\n") == inbuf+1) { do_hyphen = FALSE; fprintf(f_out, "%%%s", inbuf); linenumber++; break; } if (strstr(inbuf, "vconjuncts\n") == inbuf+1) { do_vconjuncts = TRUE; fprintf(f_out, "%%%s", inbuf); linenumber++; break; } if (strstr(inbuf, "novconjuncts\n") == inbuf+1) { do_vconjuncts = FALSE; fprintf(f_out, "%%%s", inbuf); linenumber++; break; } /* --------- Addition by Marc Csernel 1998 --------- */ if (strstr(inbuf, "tabs\n") == inbuf+1) { tabs_mode = TRUE; fprintf(f_out, "%%%s", inbuf); linenumber++; break; } /* ------------------ End Addition ----------------- */ if (strstr(inbuf, "notabs\n") == inbuf+1) { tabs_mode = FALSE; fprintf(f_out, "%%%s", inbuf); linenumber++; break; } fprintf(stderr, "Warning: possible illegal preprocessor command at line "); fprintf(stderr, "%d:\n%s", linenumber, inbuf); fprintf(f_out, "%s", inbuf); linenumber++; break; } *inbuf = '\0'; } else { if (!find_dn()) fputs(inbuf, f_out); /* process DN text */ else do { buf_idx = 0; symbol = inp_ch(); dnproc(); dn_yes = find_dn(); if (!dn_yes) { strcat(outbuf, inbuf); fputs(outbuf, f_out); } } while (dn_yes); *inbuf = '\0'; linenumber++; *outbuf = '\0';; } } while (p_in != NULL); fclose(f_in); fclose(f_out); exit(0); } /* * Read and parse DN input, and convert it into the character set * defined in char_table[] */ void dnproc(void) { short i; unsigned char saved, dnready; short brace_lev; int test_val; /* Marc Csernel */ char savchr = 0; char wrong[10]; const char *empty = ""; brace_lev = 1; saved = FALSE; dnready = FALSE; do { switch(symbol) { case '.': savchr = inp_ch(); i = 0; do { i++; } while ((i != 19) && (chset1[i-1] != savchr)); if (i == 19) { wrong[0] = '.'; wrong[1] = savchr; wrong[2] = '\0'; err_ill(wrong); } else { if (i < 4) { savchr = inp_ch(); if (savchr == 'h') i += 11; else saved = TRUE; if (i == 1) err_ill(".k"); } } if (i == 18) put_ch('M'); else put_ch(i); break; /* --------- Addition by Marc Csernel 1998 --------- */ /* cite and chapter command */ case ']': if (citation) { citation = 0; if (num_mode) put_ch(dummy); put_syll(); test_sub_com(); } if (optchapter) { optchapter = 0; if (num_mode) put_ch(dummy); put_syll(); sendchar(symbol); break; } /* ------------------ End Addition ----------------- */ case 'i': savchr = inp_ch(); if (savchr == 'i') put_ch('I'); else { put_ch(symbol); saved = TRUE; } break; case 'u': savchr = inp_ch(); if (savchr == 'u') put_ch('U'); else { put_ch(symbol); saved = TRUE; } break; case 'a': savchr = inp_ch(); i = 0; do { i++; } while ((i != 4) && (chset2[i-1] != savchr)); if (i == 4) { put_ch(symbol); saved = TRUE; } else put_ch(chset5[i-1]); break; case '\"': savchr = inp_ch(); i = 0; do { i++; } while ((i != 3) && (chset3[i-1] != savchr)); if (i == 3) { wrong[0] = '\"'; wrong[1] = savchr; wrong[2] = '\0'; err_ill(wrong); } else put_ch((short)(i+17)); break; case '~': savchr = inp_ch(); i = 0; do { i++; } while ((i != 5) && (chset6[i-1] != savchr)); if (i == 6) { wrong[0] = '~'; wrong[1] = savchr; wrong[2] = '\0'; err_ill(wrong); } else if (i == 5) put_ch(47); else put_ch((short)(i+20)); break; case end_of_line: put_ch(end_of_line); break; case '$': if (!dollar_mode) put_ch(symbol); else { if (no_dn) put_ch(dummy); else put_ch('}'); dnready = TRUE; put_word(); memmove(inbuf, inbuf+buf_idx, strlen(inbuf+buf_idx)+1); } break; case '}': brace_lev -= 1; if ((brace_lev == 0) && (!d_found)) { if (no_dn) put_ch(dummy); else put_ch(symbol); dnready = TRUE; put_word(); memmove(inbuf, inbuf+buf_idx, strlen(inbuf+buf_idx)+1); } else put_ch(symbol); break; case '{': put_ch(symbol); brace_lev += 1; break; case '%': put_ch(symbol); do { symbol = inp_ch(); if (symbol != end_of_line) sendchar(symbol); } while (symbol != end_of_line); sendchar('\n'); break; case '<': put_syll(); do { symbol = inp_ch(); if (symbol == end_of_line) symbol = '\n'; if (symbol != '>') sendchar(symbol); } while (symbol != '>'); break; case '\\': put_ch(symbol); symbol = inp_ch(); if (symbol == end_of_line) { put_word(); strcat(outbuf, "\n"); fputs(outbuf, f_out); *outbuf = '\0'; } else { /* --------- Addition by Marc Csernel 1998 --------- */ /* Modified MC May 98 to take \\[5pt] into account */ if (symbol == '\\') { sendchar(symbol); symbol = inp_ch(); if (symbol == '[') { while (symbol != ']') { sendchar(symbol); symbol = inp_ch(); } sendchar(symbol); symbol = inp_ch(); } savchr = symbol; saved = TRUE; break; } /* ------------------ End Addition ----------------- */ if (!isalpha((unsigned char)symbol)) sendchar(symbol); else { nbchcomm = 0; /* Marc Csernel */ do { command[nbchcomm++] = symbol; /* Marc Csernel */ sendchar(symbol); symbol = inp_ch(); } while (isalpha((unsigned char)symbol)); /* --------- Addition by Marc Csernel 1998 --------- */ command[nbchcomm++] = 0; test_val = test_command(); switch (test_val) { case 1: savchr = test_sub_com(); break; case 2: savchr = ignore(); break; case 3: savchr = comm_double_args(); break; case 4: savchr = comm_begin(); break; case 5: savchr = comm_special(); break; case 6: savchr = comm_opt(); break; case 7: savchr = comm_cite(); break; case 8: savchr = comm_chapter(); break; default: savchr = symbol; } /* ------------------ End Addition ----------------- */ saved = TRUE; } } break; case ill_char: err_ill(empty); break; case end_of_file: fputs("Error: missing }", stderr); exit(1); default: if (symbol < 0) err_ill(empty); /* accented character inside dn mode */ i = 0; do { i++; } while ((i != 10) && (chset4[i-1] != symbol)); if (i == 10) put_ch(symbol); else { savchr = inp_ch(); if (savchr == 'h') { if (i == 9) put_ch(20); else put_ch((short)(symbol-32)); } else { put_ch(symbol); saved = TRUE; } } } if (saved) { symbol = savchr; saved = FALSE; } else { if (!dnready) symbol = inp_ch(); } } while (!dnready); } /* * Read and parse input in the character set defined by char_table[], * and convert it into TeX text and macros for the dvng fonts. */ void put_ch(short code) { short cons_idx = 0; char cstr[2]; struct char_def *c_ptr; struct cons_joins *q_ptr; short i, j; /* --------- Addition by Kevin Carmody 2005 --------- */ /* * Conjunct fixes used by fixconj() */ short wrong_1[] = {63, 63, 113}; short right_1[] = {63, 34, 0}; /* kk.sa */ short wrong_2[] = {94, 214, 264, 113}; short right_2[] = {94, 86, 264, 34, 0}; /* .tk.sa */ short wrong_3[] = {22, 62, 26}; short right_3[] = {22, 226, 0}; /* ~nj~na */ short wrong_4[] = {274, 88, 264, 273, 57, 52, 264, 121}; short right_4[] = {126, 88, 264, 43, 0}; /* .drya */ short wrong_5[] = {94, 140, 264, 118}; short right_5[] = {94, 100, 264, 146, 0}; /* ddva */ short wrong_6[] = {94, 136, 264, 118}; short right_6[] = {94, 100, 264, 64, 118, 0}; /* ddhva */ short wrong_7[] = {271, 100, 264, 273, 57, 52, 264, 121}; short right_7[] = {272, 100, 264, 43, 0}; /* drya */ short wrong_8[] = {9, 274, 86, 264, 273, 57, 52, 264, 121}; short right_8[] = {9, 126, 86, 264, 43, 0}; /* .s.trya */ /* ------------------ End Addition ----------------- */ c_ptr = &char_table[code-1]; switch(c_ptr->ch_typ) { case dn: if (cmr_mode) { cmr_mode = FALSE; put_sym(RE); } else { if (num_mode) { num_mode = FALSE; put_sym(RBRACE); } } switch(c_ptr->ch_subtyp) { case hi_vowel: if (wait_syll) { put_syll(); if (do_hyphen) strcat(word, "\\-"); } if (lig_block) err_ill("+"); /* Kevin Carmody */ if (cons_seen) { if (syll[chr_idx-1] < 0) expand(); if (code == 'i') { for (i = chr_idx; i>= (cons_idx+1); i--) syll[i] = syll[i-1]; syll[cons_idx] = c_ptr->ch_subcode; } else { syll[chr_idx] = c_ptr->ch_subcode; if ((code != 'A') && (code != 'a')) vow_seen = TRUE; } } else syll[chr_idx] = c_ptr->ch_code; chr_idx += 1; wait_syll = TRUE; cons_seen = FALSE; break; case lo_vowel: if (wait_syll) { put_syll(); if (do_hyphen) strcat(word, "\\-"); } if (lig_block) err_ill("+"); /* Kevin Carmody */ if (cons_seen) { if (syll[chr_idx-1] < 0) expand(); if ((syll[chr_idx-1] == 'r') && (code == 'u')) syll[--chr_idx] = 'z'; else { if ((syll[chr_idx-1] == 'r') && (code == 'U')) syll[--chr_idx] = '!'; else { if ((syll[chr_idx-1] == 'h') && (code == 7)) syll[--chr_idx] = 0343; else { if ((syll[chr_idx-1] == 'r') && ((code == 7) || (code == 15) || (code == 16) || (code == 17))) { syll[--chr_idx] = c_ptr->ch_code; front_r = TRUE; } else { put_macro(c_ptr->ch_subcode); chr_idx -= 1; } } } } } else syll[chr_idx] = c_ptr->ch_code; chr_idx += 1; wait_syll = TRUE; cons_seen = FALSE; break; case consonant: if (wait_syll) { put_syll(); if (do_hyphen) strcat(word, "\\-"); } if (!cons_seen) { /* Is this the first consonant? */ cons_seen = TRUE; cons_idx = chr_idx; cons_code = c_ptr->ch_subcode; syll[chr_idx] = c_ptr->ch_code; chr_idx += 1; tst_half(); } else { q_ptr = &cons_table[cons_code]; if (syll[cons_idx] == 'r') { /* Was there an initial "r"? */ front_r = TRUE; syll[chr_idx-1] = c_ptr->ch_code; cons_code = c_ptr->ch_subcode; tst_half(); } else { i = q_ptr->lig_code; for (j = i+(q_ptr->n_ligs); i != j; i++) if (lig_table[i].sym_code == code) break; if ((i != j) && (lig_table[i].in_use) && (!lig_block)) { /* lig_block added Kevin Carmody */ /* Is there a ligature? */ syll[chr_idx-1] = lig_table[i].sym_new; cons_code = lig_table[i].join_idx; joincode = cons_table[cons_code].j_code; if (joincode != 0) { half_codes[0] = joincode; n_halves = 1; } else { /* ... or a half-form? */ joincode = cons_table[c_ptr->ch_subcode].j_code; if ((joincode != 0) && (n_halves != 0)) { half_codes[n_halves] = joincode; n_halves += 1; } else n_halves = 0; } } else { if ((code == 'r') && (!lig_block)) { /* lig_block added Kevin Carmody */ if (q_ptr->r_type != 0) { /* "Special" non-initial "r"? */ syll[chr_idx] = syll[chr_idx-1]; syll[chr_idx-1] = (q_ptr->r_type == 1) ? '\176' : 272; syll[chr_idx+1] = RBRACE; chr_idx += 2; } else { /* "Normal" non-initial "r"? */ syll[chr_idx] = '\175'; chr_idx += 1; n_halves = 0; } cons_code = 0; } else { if ((q_ptr->j_code != 0) && (!lig_block)) { /* lig_block added by Kevin Carmody */ /* Can we build a conjunct using a ligature? */ syll[chr_idx-1] = q_ptr->j_code; syll[chr_idx] = c_ptr->ch_code; cons_code = c_ptr->ch_subcode; chr_idx += 1; tst_half(); } else { if (n_halves != 0) { /* ... or a half-form? */ if (syll[chr_idx-1] != RBRACE) { /* * There might already have been a * subscript "-r" in the conjunct! * (E.g. khrya, ttrya) * -- John Smith */ for (i = 0; i < n_halves; i++) syll[chr_idx-1+i] = half_codes[i]; chr_idx += n_halves; syll[chr_idx-1] = c_ptr->ch_code; cons_code = c_ptr->ch_subcode; tst_half(); } else { for (i = 0; i < n_halves; i++) syll[chr_idx-2+i] = half_codes[i]; chr_idx += n_halves; syll[chr_idx-1] = c_ptr->ch_code; syll[chr_idx-2] = RBRACE; cons_code = c_ptr->ch_subcode; tst_half(); } } else { /* Fall back on viraama */ if (syll[chr_idx-1] < 0) expand(); put_macro(VIRAAM); cons_code = c_ptr->ch_subcode; syll[chr_idx] = c_ptr->ch_code; chr_idx += 1; tst_half(); } } } /* * This hack is to secure the correct representation * for various conjuncts that may assume undesirable * forms. */ /* --------- Modification by Kevin Carmody 2005 --------- */ fixconj(wrong_1, right_1); fixconj(wrong_2, right_2); fixconj(wrong_3, right_3); fixconj(wrong_4, right_4); fixconj(wrong_5, right_5); fixconj(wrong_6, right_6); fixconj(wrong_7, right_7); fixconj(wrong_8, right_8); /* ------------------ End Modification ----------------- */ /* * Now replace the sequence consonant + viraama + ya * by consonant + open ya (glyph became available only * Devnag 2.1). */ for (i = 0; i < chr_idx-3; i++) { if ((syll[i] == 94) && (syll[i+2] == 264) && (syll[i+3] == 121)) { syll[i] = syll[i+1]; syll[i+1] = 43; chr_idx = i + 2; } } } } } lig_block = FALSE; /* Kevin Carmody */ break; case special: if (lig_block) err_ill("+"); /* Kevin Carmody */ if (cons_seen) { if (syll[chr_idx-1] < 0) expand(); } if ((code == 'M') && (front_r)) { front_r = FALSE; if (vow_seen) syll[chr_idx] = 270; else syll[chr_idx] = RDT; chr_idx += 1; put_syll(); } else { if ((code == 'M') && (syll[chr_idx-1] == 'I')) { syll[chr_idx-1] = 18; put_syll(); } else { if ((code == 'M') && (vow_seen)) { syll[chr_idx] = 268; chr_idx += 1; put_syll(); } /* --------- Addition by Marc Csernel 1998 --------- */ else { if (code == '_') { if(!cons_seen) err_ill("_"); else { put_macro(VIRAAM); put_syll(); } } else { if (code == '&') { if(!tabs_mode) { if (!cons_seen) { fprintf(stderr, "Error: tabs_mode not selected\n"); err_ill("&"); } else { put_macro(VIRAAM); put_syll(); } } else { if(!cons_seen) sendchar((char)code); else { put_macro(VIRAAM); put_syll(); } } } /* ------------------ End Addition ----------------- */ else { /* --------- Addition by Kevin Carmody 2005 --------- */ if (code == '+') { /* half letter or viraama */ if (!cons_seen) err_ill("+"); lig_block = TRUE; } /* ------------------ End Addition ------------------ */ else { if ((!hindi_mode) && (cons_seen)) put_macro(VIRAAM); put_syll(); put_sym(c_ptr->ch_code); } } } } } } } break; case illegal: cstr[0] = code; cstr[1] = '\0'; err_ill(cstr); break; case control: if (cmr_mode) { cmr_mode = FALSE; put_sym(RE); } else { if (lig_block) err_ill("+"); /* Kevin Carmody */ if (num_mode) { num_mode = FALSE; put_sym(RBRACE); } else { if (cons_seen) { if (syll[chr_idx-1] < 0) expand(); } if ((!hindi_mode) && (cons_seen)) put_macro(VIRAAM); put_syll(); } } if (code == end_of_line) { put_word(); strcat(outbuf, "\n"); fputs(outbuf, f_out); *outbuf = '\0'; } else if (code != dummy) sendchar((char)code); break; case cmr: if (cmr_mode) sendchar((char)code); else { cmr_mode = TRUE; if (num_mode) { num_mode = FALSE; put_sym(RBRACE); } else { if (lig_block) err_ill("+"); /* Kevin Carmody */ if (cons_seen) { if (syll[chr_idx-1] < 0) expand(); } if ((!hindi_mode) && (cons_seen)) put_macro(VIRAAM); put_syll(); } put_sym(RS); sendchar((char)code); } break; case numeral: if (num_mode) sendchar((char)code); else { num_mode = TRUE; if (cmr_mode) { cmr_mode = FALSE; put_sym(RE); } else { if (lig_block) err_ill("+"); /* Kevin Carmody */ if (cons_seen) { if (syll[chr_idx-1] < 0) expand(); } if ((!hindi_mode) && (cons_seen)) put_macro(VIRAAM); put_syll(); } put_sym(RN); sendchar((char)code); } } } /* * Append a character to word[]; if character is whitespace, * call put_word(). */ void sendchar(char c) { int i = strlen(word); word[i] = c == end_of_line ? '\n' : c; word[i+1] = '\0'; if (isspace((unsigned char)c)) put_word(); } /* * Use the current chacter value as an index into out_string[], and * append the code found there to the end of word[]. */ void put_sym(short code) { strcat(word, out_string[code]); } /* * Append word[] to outbuf[]. */ void put_word(void) { /* * The hack is to avoid hyphenation before any consonant stopped * with viraama. -- John Smith */ char *w_ptr; if (do_hyphen) { /* hack */ do { w_ptr = strstr(word, "\\-\\qq{"); if (w_ptr != NULL) memmove(w_ptr, w_ptr+2, strlen(w_ptr+2)+1); } while (w_ptr != NULL); } strcat(outbuf, word); *word = '\0'; } /* * Append syll[] to word[], using the codes defined in out_string[] */ void put_syll(void) { /* * put_syll() now checks to see whether "old-style" treatment of * consonant sequences containing viraama has been requested with * the @vconjuncts preprocessor command. If not, it re-orders the * characters in any such sequence containing "i" so that the * i-matra vowel is placed after the viraama rather than before * the entire sequence. -- John Smith */ short i; int ipos, vpos, bpos; if (do_vconjuncts == FALSE) { ipos = sindex(0, '\105'); /* 'i' */ if (ipos != -1) { vpos = sindex(ipos, '\136'); /* viraama */ if (vpos == -1) { vpos = sindex(ipos, 271); if (vpos == -1) vpos = sindex(ipos, 274); if (vpos != -1) { vpos = sindex(vpos, 273); /* alternative viraama */ if ((syll[vpos+1] != '9') || (syll[vpos+2] != '4')) vpos = -1; } } if (vpos != -1) { bpos = sindex(vpos, 264); /* right brace after viraama */ if (bpos != -1) { for (i = ipos+1; i <= bpos; i++) syll[i-1] = syll[i]; syll[bpos] = '\105'; } } } } for (i = 0; i < chr_idx; i++) strcat(word, out_string[syll[i]]); if (front_r) { if (vow_seen) strcat(word, out_string[269]); else strcat(word, out_string[13]); front_r = FALSE; } chr_idx = 0; cons_seen = FALSE; vow_seen = FALSE; wait_syll = FALSE; lig_block = FALSE; /* Kevin Carmody */ } /* * Check whether a "half-form" exists for current consonant, and * store the result. */ void tst_half(void) { joincode = cons_table[cons_code].j_code; if (joincode != 0) { half_codes[0] = joincode; n_halves = 1; } else n_halves = 0; } /* * Manipulate contents of syll[] to incorporate a macro call, * using values that are indices into out_string[] (so that * put_syll() will produce the correct output). */ void put_macro(short macro) { char tmp[5]; int lt, i; if (syll[chr_idx-1] == '\175') { syll[chr_idx+1] = '\175'; syll[chr_idx] = RBRACE; syll[chr_idx-1] = syll[chr_idx-2]; syll[chr_idx-2] = macro; chr_idx += 2; } else { if (syll[chr_idx-1] == RBRACE) { /* * This is a bit of a hack on Velthuis's part! What it * really means is: if syll[] currently consists of "dr" * OR a consonant(-group) with the subscript "caret" form * of "-r" already attached, AND if a subscript vowel * (or viraama) is to be added to it. In pre-2.0 releases * of devnag this always produced "\qa{consonant}{vowel}", * i.e. it used the simple, non-"caret" form of "-r", * whatever the consonant to which it was to be attached. * I have fixed this bug (feature?), but at the cost of * invoking a new macro, \qc, which will have to be * defined in dnmacs.tex etc. for the new code to work. * -- John Smith */ if (syll[chr_idx-3] == 126) syll[chr_idx-3] = 274; else syll[chr_idx-3] = 271; syll[chr_idx] = LBRACE; sprintf(tmp, "%d", macro); lt = strlen(tmp); chr_idx += 1; for (i = 0; i < lt; i++) syll[chr_idx+i] = tmp[i]; chr_idx += lt; syll[chr_idx] = RBRACE; chr_idx += 1; } else { syll[chr_idx] = syll[chr_idx-1]; syll[chr_idx-1] = macro; syll[chr_idx+1] = RBRACE; chr_idx += 2; } } } /* * Exit with error message in the case of illegal input. */ void err_ill(const char *str) { fprintf(stderr, "Error: illegal character(s) \"%s\" detected at line %d:\n", str, linenumber); fprintf(stderr, inbuf); exit(1); } /* * Read and return the next character from inbuf[], taking appropriate * action in the case of EOL, EOF or an illegal character. */ char inp_ch(void) { char ch, ch_out; ch = inbuf[buf_idx++]; if (ch == '\n') { if (p_in == 0) ch_out = end_of_file; else { p_in = fgetline(inbuf, MAXBUF, f_in); linenumber++; buf_idx = 0; ch_out = end_of_line; } } else { if (ch == '\t') ch_out = ' '; /* Change TABs to spaces */ else { if ((unsigned)ch < 32) ch_out = ill_char; /* Allow accented characters */ else ch_out = ch; } } return(ch_out); } /* * Invoked if syll[chr_idx-1] is negative (instead of containing * an octal character value). Fixes appropriate representation * for "r". */ void expand(void) { short indx; indx = -1-syll[chr_idx-1]; if (r_ligs[indx][1] != 0) { syll[chr_idx] = r_ligs[indx][0]; syll[chr_idx-1] = (r_ligs[indx][1] == 1) ? '\176' : 272; syll[chr_idx+1] = RBRACE; chr_idx += 2; } else { syll[chr_idx-1] = r_ligs[indx][0]; syll[chr_idx] = '\175'; chr_idx += 1; } cons_code = 0; } /* * Search inbuf[] for "{\dn" followed by non-letter or, in dollar * mode only, for "$". Return TRUE or FALSE as appropriate. If * TRUE, send inbuf[] text prior to mode-switch to outbuf[], followed * by mode-switch indicator, and make inbuf[] start at beginning of * Devanagari text. */ char find_dn(void) { char *d_ptr; char *dn_ptr; char *svbuf; int again; d_found = FALSE; svbuf = inbuf; do { again = FALSE; dn_ptr = strstr(svbuf, "{\\dn"); if (dn_ptr != NULL) { again = isalpha((unsigned char)dn_ptr[4]); svbuf = dn_ptr+4; } } while (again); if (dollar_mode) { d_ptr = strchr(inbuf, '$'); if ((d_ptr != NULL) && ((dn_ptr == NULL) || (d_ptr < dn_ptr))) { d_found = TRUE; dn_ptr = d_ptr; } } if (dn_ptr == NULL) return(FALSE); strncat(outbuf, inbuf, dn_ptr-inbuf); no_dn = FALSE; if (!d_found) { if (dn_ptr[4] == '#') { /* equivalent to @dolmode3 */ no_dn = TRUE; dn_ptr += 1; } else strcat(outbuf, "{\\dn"); dn_ptr += 4; } else { switch(dollar_mode) { case 1: strcat(outbuf, "{\\dn "); break; case 2: strcat(outbuf, "\\pdn "); case 3: no_dn = TRUE; } dn_ptr += 1; } memmove(inbuf, dn_ptr, strlen(dn_ptr)+1); return(TRUE); } /* * Return index of character value in syll[] starting at syll[i], * -1 if none */ int sindex(int i, short t) { int j; for (j = i; j < chr_idx; j++) { if (syll[j] == t) return j; } return -1; } /* * Replace infelicitous conjuncts */ void fixconj(short *wrong, short *right) { int i; for (i = 0; i < chr_idx; i++) { if (syll[i] != wrong[i]) return; } for (i = 0; right[i] != 0; i++) { syll[i] = right[i]; } chr_idx = i; } /* * Read a line with either line ending (UNIX = LF, DOS = CR LF, Mac = CR) * return the pointer to the buffer of NULL at end of file or I/O Error * (replacement of fgets, requires global variables) */ char *fgetline(char *buf, int n, FILE *f) { int k; buf[0] = '\0'; for (k = 0; k < n-1 && (k == 0 || buf[k-1] != '\n'); ) { if (charpresent) charpresent = FALSE; else charbuf = fgetc(f); if (wasCR) { wasCR = FALSE; buf[k++] = '\n'; if (charbuf != '\n') charpresent = TRUE; } else { if (charbuf == EOF) { if (k) { charpresent = TRUE; break; } else return NULL; } if (charbuf == '\r') wasCR = TRUE; else buf[k++] = charbuf; } } buf[k] = '\0'; return buf; }