1 
2 /* definitions.c - Routines to handle TeX \def and LaTeX \newcommand
3 
4 Copyright (C) 2001-2002 The Free Software Foundation
5 
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 
20 This file is available from http://sourceforge.net/projects/latex2rtf/
21 */
22 
23 #include <stdlib.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include "main.h"
27 #include "convert.h"
28 #include "definitions.h"
29 #include "parser.h"
30 #include "funct1.h"
31 #include "utils.h"
32 #include "cfg.h"
33 #include "counters.h"
34 #include "funct1.h"
35 
36 #define MAX_DEFINITIONS 2000
37 #define MAX_ENVIRONMENTS 200
38 #define MAX_THEOREMS 200
39 
40 typedef struct {
41     char *name;
42     char *opt_param;
43     char *def;
44     int params;
45 } definition_type;
46 
47 definition_type Definitions[MAX_DEFINITIONS];
48 
49 typedef struct {
50     char *name;
51     char *opt_param;
52     char *begname;
53     char *endname;
54     char *begdef;
55     char *enddef;
56     int params;
57 } environment_type;
58 
59 environment_type NewEnvironments[MAX_ENVIRONMENTS];
60 
61 typedef struct {
62     char *name;
63     char *numbered_like;
64     char *caption;
65     char *within;
66 } theorem_type;
67 
68 theorem_type NewTheorems[MAX_THEOREMS];
69 
70 static int iDefinitionCount = 0;
71 static int iNewEnvironmentCount = 0;
72 static int iNewTheoremCount = 0;
73 
strequal(char * a,char * b)74 static int strequal(char *a, char *b)
75 {
76     if (a == NULL || b == NULL)
77         return 0;
78 
79     while (*a && *b && *a == *b) {
80         a++;
81         b++;
82     }
83 
84     if (*a || *b)
85         return 0;
86     else
87         return 1;
88 }
89 
90 /* static void printDefinitions(void)
91 {
92 int i=0;
93     diagnostics(WARNING, "\n");
94     while(i < iDefinitionCount ) {
95         diagnostics(WARNING, "[%d] name     =<%s>\n",i, Definitions[i].name);
96         diagnostics(WARNING, "    opt_param=<%s>\n", Definitions[i].opt_param);
97         diagnostics(WARNING, "    def      =<%s>\n", Definitions[i].def);
98         diagnostics(WARNING, "    params   =<%d>\n", Definitions[i].params);
99         i++;
100     }
101 }
102 
103 static void printTheorems(void)
104 {
105 int i=0;
106     diagnostics(WARNING, "\n");
107     for (i=0; i< iNewTheoremCount; i++) {
108         diagnostics(WARNING, "[%d] name   =<%s>\n",i, NewTheorems[i].name);
109         diagnostics(WARNING, "    caption    =<%s>\n", NewTheorems[i].caption);
110         diagnostics(WARNING, "    like =<%s>\n", NewTheorems[i].numbered_like);
111         diagnostics(WARNING, "    within    =<%s>\n", NewTheorems[i].within);
112     }
113 }
114 */
115 
116 #define POUND_POUND 9997
117 
expandmacro(char * macro,char * opt_param,int params)118 static char *expandmacro(char *macro, char *opt_param, int params)
119 
120 /**************************************************************************
121      purpose: retrieves and expands a defined macro
122               care is taken to avoid buffer overruns
123 **************************************************************************/
124 {
125     int i = 0, param;
126     char *args[9], *dmacro, *macro_piece, *next_piece, *expanded, *buffer=NULL, *cs;
127     int buff_size = 512;   /* extra slop for macro expansion */
128 
129     diagnostics(5, "expandmacro...");
130     diagnostics(5, "expandmacro: contents '%s'", macro);
131     diagnostics(5, "expandmacro: optional '%s'", opt_param);
132     diagnostics(5, "expandmacro: num args  %d", params);
133 
134     if (params <= 0)
135         return strdup(macro);
136 
137     if (opt_param) {
138         args[0] = getBracketParam();
139         if (!args[0])
140             args[0] = strdup(opt_param);
141         buff_size += (int) strlen(args[0]);
142         i = 1;
143     }
144 
145     for (; i < params; i++) {
146         args[i] = getBraceRawParam();
147         buff_size += strlen(args[i]);
148         diagnostics(5, "Macro #%d --> '%s'", i + 1, args[i]);
149     }
150 
151     dmacro = strdup(macro);
152     macro_piece = dmacro;
153     buff_size += (int) strlen(macro_piece);
154 
155     diagnostics(5, "expandmacro: buff_size = %d\n", buff_size);
156     if(buff_size > 0)
157         buffer = (char*) calloc(sizeof(char) * (buff_size+1), sizeof(char));
158 
159     expanded = buffer;
160 
161     /* convert "\csname " but leave inital backslash */
162     while ((cs = strstr(dmacro, "\\csname ")) != NULL)
163         my_strcpy(cs+1, cs + strlen("\\csname "));
164     while ((cs = strstr(dmacro, "\\csname")) != NULL)
165         my_strcpy(cs+1, cs + strlen("\\csname"));
166 
167     /* remove "\endcsname" */
168     while ((cs = strstr(dmacro, "\\endcsname ")) != NULL)
169         my_strcpy(cs, cs + strlen("\\endcsname "));
170     while ((cs = strstr(dmacro, "\\endcsname")) != NULL)
171         my_strcpy(cs, cs + strlen("\\endcsname"));
172 
173     diagnostics(5, "expandmacro: after removing cs crap '%s'", macro_piece);
174 
175     /* do not use strtok because it may be used elsewhere */
176     while (macro_piece && *macro_piece) {
177 
178         param = -1;
179 
180         next_piece = strchr(macro_piece, '#');
181         if (next_piece) {
182             *next_piece = '\0';        /* later we'll copy macro up to this point */
183             next_piece++;
184 
185             /* the only characters that should follow # are '1'-'9' and '#' */
186 
187             if (*next_piece == '#')
188                 param = POUND_POUND;    /* just a flag for below */
189             else
190                 param = *next_piece - '1';
191 
192             next_piece++;
193         }
194 
195         diagnostics(5, "expandmacro: next section of macro before '#' is '%s'", macro_piece);
196 
197         /* copy unchanged part of macro into expanded */
198         strcpy(expanded, macro_piece);
199         expanded += strlen(macro_piece);
200 
201         diagnostics(5, "expandmacro: before appending next piece '%s'", buffer);
202 
203         if (param > -1) {
204             if (param == POUND_POUND) {
205                 diagnostics(5, "expandmacro: found ##, appending # to expanded macro");
206                 if (expanded+1 < buffer+buff_size) {
207                     *expanded='#';
208                     expanded++;
209                 } else
210                     diagnostics(WARNING, "insufficient buffer to expand macro <%s>", macro);
211 
212             } else if (param < params) {
213                 diagnostics(5, "expandmacro: found #%d appending '%s'", param+1, args[param]);
214                 if (expanded+strlen(args[param])+1 <buffer+buff_size) {
215 
216                     /* begin with a space if the last character was a character */
217                     char c = *(expanded-1);
218                     if (isalpha(c)) {
219                         *expanded=' ';
220                         expanded++;
221                     }
222 
223                     strcpy(expanded, args[param]);
224                     expanded += strlen(args[param]);
225                 } else
226                     diagnostics(WARNING, "expandmacro: insufficient buffer to expand macro <%s>", macro);
227 
228             } else
229                 diagnostics(WARNING, "expandmacro: confusing definition in macro=<%s>", macro);
230         }
231 
232         macro_piece = next_piece;
233     }
234 
235     expanded = strdup(buffer);
236 
237     for (i = 0; i < params; i++) {
238         if (args[i])
239             free(args[i]);
240     }
241 
242     if (dmacro)
243         free(dmacro);
244 
245     if (buffer)
246         free(buffer);
247 
248     diagnostics(4, "expandmacro: result is '%s'", expanded);
249 
250     return expanded;
251 }
252 
maybeDefinition(char * s,size_t n)253 int maybeDefinition(char *s, size_t n)
254 
255 /**************************************************************************
256      purpose: checks to see if a named TeX definition possibly exists
257      returns: the array index of the named TeX definition
258 **************************************************************************/
259 {
260     int i;
261 
262     if (n == 0)
263         return TRUE;
264 
265     for (i = 0; i < iDefinitionCount; i++) {
266         /*
267         char *ss = my_strndup(s,strlen(Definitions[i].name));
268         diagnostics(WARNING, "def seeking=<%s>, i=%d, current=<%s>", ss, i, Definitions[i].name);
269         free(ss);
270         */
271         if (strncmp(s, Definitions[i].name, n) == 0)
272             return TRUE;
273     }
274 
275     return FALSE;
276 }
277 
existsDefinition(char * s)278 int existsDefinition(char *s)
279 
280 /**************************************************************************
281      purpose: checks to see if a named TeX definition exists
282      returns: the array index of the named TeX definition
283 **************************************************************************/
284 {
285     int i;
286 
287     for (i = 0; i < iDefinitionCount; i++) {
288     /*
289         char *tt = my_strndup(s,strlen(Definitions[i].name));
290         diagnostics(WARNING, "def text has=<%s>, i=%d, trying=<%s>", tt, i, Definitions[i].name);
291         free(tt);
292     */
293         if (strcmp(s, Definitions[i].name) == 0) {
294     /*
295             char *ss = my_strndup(s,strlen(Definitions[i].name));
296             diagnostics(WARNING, "def text has=<%s>, i=%d, matched=<%s>", ss, i, Definitions[i].name);
297             free(ss);
298     */
299             break;
300         }
301     }
302 
303     if (i == iDefinitionCount)
304         return -1;
305     else
306         return i;
307 }
308 
newDefinition(char * name,char * opt_param,char * def,int params)309 void newDefinition(char *name, char *opt_param, char *def, int params)
310 
311 /**************************************************************************
312      purpose: allocates and initializes a named TeX definition
313               name should not begin with a '\'  for example to
314               define \hd, name = "hd"
315 **************************************************************************/
316 {
317     diagnostics(2, "Adding macro '%s' = [%s]", name, def);
318 
319     if (strcmp(name, "LaTeX") == 0)
320         return;
321     if (strcmp(name, "TeX") == 0)
322         return;
323     if (strcmp(name, "AmSTeX") == 0)
324         return;
325     if (strcmp(name, "BibTex") == 0)
326         return;
327     if (strcmp(name, "LaTeXe") == 0)
328         return;
329     if (strcmp(name, "AmSLaTeX") == 0)
330         return;
331 
332     if (iDefinitionCount == MAX_DEFINITIONS) {
333         diagnostics(WARNING, "Too many definitions, ignoring %s", name);
334         return;
335     }
336 
337     Definitions[iDefinitionCount].params = params;
338 
339     Definitions[iDefinitionCount].name = strdup(name);
340 
341     if (Definitions[iDefinitionCount].name == NULL) {
342         diagnostics(ERROR, "\nCannot allocate name for definition \\%s\n", name);
343     }
344 
345     if (opt_param) {
346         Definitions[iDefinitionCount].opt_param = strdup(opt_param);
347 
348         if (Definitions[iDefinitionCount].opt_param == NULL) {
349             diagnostics(ERROR, "\nCannot allocate opt_param for definition \\%s\n", name);
350         }
351     } else {
352         Definitions[iDefinitionCount].opt_param = NULL;
353     }
354 
355     Definitions[iDefinitionCount].def = strdup(def);
356 
357     if (Definitions[iDefinitionCount].def == NULL) {
358         diagnostics(ERROR, "\nCannot allocate def for definition \\%s\n", name);
359     }
360 
361     iDefinitionCount++;
362     diagnostics(3, "Successfully added macro #%d", iDefinitionCount);
363 }
364 
renewDefinition(char * name,char * opt_param,char * def,int params)365 void renewDefinition(char *name, char *opt_param, char *def, int params)
366 
367 /**************************************************************************
368      purpose: allocates (if necessary) and sets a named TeX definition
369 **************************************************************************/
370 {
371     int i;
372 
373     diagnostics(3, "renewDefinition seeking '%s'\n", name);
374     i = existsDefinition(name);
375 
376     if (i < 0) {
377         newDefinition(name, opt_param, def, params);
378         diagnostics(2, "No existing definition for \\%s", name);
379 
380     } else {
381         free(Definitions[i].def);
382         if (Definitions[i].opt_param)
383             free(Definitions[i].opt_param);
384         Definitions[i].params = params;
385         if (opt_param) {
386             Definitions[i].opt_param = strdup(opt_param);
387             if (Definitions[i].opt_param == NULL) {
388                 diagnostics(ERROR, "\nCannot allocate opt_param for definition \\%s\n", name);
389             }
390         } else {
391             Definitions[i].opt_param = NULL;
392         }
393 
394         Definitions[i].def = strdup(def);
395         if (Definitions[i].def == NULL) {
396             diagnostics(WARNING, "\nCannot allocate def for definition \\%s\n", name);
397             exit(1);
398         }
399     }
400 }
401 
expandDefinition(int thedef)402 char *expandDefinition(int thedef)
403 
404 /**************************************************************************
405      purpose: retrieves and expands a \newcommand macro
406 **************************************************************************/
407 {
408 
409     if (thedef < 0 || thedef >= iDefinitionCount)
410         return NULL;
411 
412     diagnostics(4, "expandDefinition name     =<%s>", Definitions[thedef].name);
413     diagnostics(5, "expandDefinition opt_param=<%s>",
414       (Definitions[thedef].opt_param) ? Definitions[thedef].opt_param : "");
415     diagnostics(5, "expandDefinition def      =<%s>", Definitions[thedef].def);
416     diagnostics(5, "expandDefinition params   =<%d>", Definitions[thedef].params);
417 
418     return expandmacro(Definitions[thedef].def, Definitions[thedef].opt_param, Definitions[thedef].params);
419 }
420 
existsEnvironment(char * s)421 int existsEnvironment(char *s)
422 
423 /**************************************************************************
424      purpose: checks to see if a user created environment exists
425      returns: the array index of the \newenvironment
426 **************************************************************************/
427 {
428     int i = 0;
429 
430     while (i < iNewEnvironmentCount && !strequal(s, NewEnvironments[i].name)) {
431         diagnostics(6, "existsEnv seeking=<%s>, i=%d, current=<%s>", s, i, NewEnvironments[i].name);
432         i++;
433     }
434 
435     if (i == iNewEnvironmentCount)
436         return -1;
437     else {
438         diagnostics(4, "user env found=<%s>, i=%d, current=<%s>", s, i, NewEnvironments[i].name);
439         return i;
440     }
441 }
442 
maybeEnvironment(char * s,size_t n)443 int maybeEnvironment(char *s, size_t n)
444 
445 /**************************************************************************
446      purpose: checks to see if a named TeX environment possibly exists
447      returns: the array index of the named TeX definition
448 **************************************************************************/
449 {
450     int i;
451 
452     if (n == 0)
453         return TRUE;
454 
455     for (i = 0; i < iNewEnvironmentCount; i++) {
456 
457         if (0) {
458         char *ss = my_strndup(s,n);
459         diagnostics(2, "trying env seeking=<%s>, i=%d, current=<%s>", ss, i, NewEnvironments[i].endname);
460         free(ss);
461         }
462 
463         if (strncmp(s, NewEnvironments[i].begname, n) == 0) {
464             if (0) {
465             char *ss = my_strndup(s,n);
466             diagnostics(2, "possible env seeking=<%s>, i=%d, current=<%s>", ss, i, NewEnvironments[i].begname);
467             free(ss);
468             }
469             return TRUE;
470         }
471 
472 
473         if (strncmp(s, NewEnvironments[i].endname, n) == 0) {
474             if (0) {
475             char *ss = my_strndup(s,n);
476             diagnostics(2, "possible env seeking=<%s>, i=%d, current=<%s>", ss, i, NewEnvironments[i].endname);
477             free(ss);
478             }
479             return TRUE;
480         }
481     }
482 
483     if (0) {
484     char *ss = my_strndup(s,n);
485     diagnostics(2, "failed all env seeking=<%s>", ss);
486     free(ss);
487     }
488     return FALSE;
489 }
490 
newEnvironment(char * name,char * opt_param,char * begdef,char * enddef,int params)491 void newEnvironment(char *name, char *opt_param, char *begdef, char *enddef, int params)
492 
493 /**************************************************************************
494      purpose: allocates and initializes a \newenvironment
495               name should not begin with a '\'
496 **************************************************************************/
497 {
498     if (iNewEnvironmentCount == MAX_ENVIRONMENTS) {
499         diagnostics(WARNING, "Too many newenvironments, ignoring %s", name);
500         return;
501     }
502 
503     NewEnvironments[iNewEnvironmentCount].name = strdup(name);
504     NewEnvironments[iNewEnvironmentCount].begname = strdup_together("\\begin{", name);
505     NewEnvironments[iNewEnvironmentCount].endname = strdup_together("\\end{", name);
506     NewEnvironments[iNewEnvironmentCount].begdef = strdup(begdef);
507     NewEnvironments[iNewEnvironmentCount].enddef = strdup(enddef);
508     NewEnvironments[iNewEnvironmentCount].params = params;
509 
510     if (opt_param) {
511         NewEnvironments[iNewEnvironmentCount].opt_param = strdup(opt_param);
512 
513         if (NewEnvironments[iNewEnvironmentCount].opt_param == NULL) {
514             diagnostics(ERROR, "\nCannot allocate opt_param for \\newenvironment{%s}", name);
515         }
516     } else {
517         NewEnvironments[iNewEnvironmentCount].opt_param = NULL;
518     }
519 
520 
521     if (NewEnvironments[iNewEnvironmentCount].name == NULL ||
522       NewEnvironments[iNewEnvironmentCount].begdef == NULL ||
523       NewEnvironments[iNewEnvironmentCount].begname == NULL ||
524       NewEnvironments[iNewEnvironmentCount].endname == NULL || NewEnvironments[iNewEnvironmentCount].enddef == NULL) {
525         diagnostics(ERROR, "Cannot allocate memory for \\newenvironment{%s}", name);
526     }
527 
528     iNewEnvironmentCount++;
529 }
530 
renewEnvironment(char * name,char * opt_param,char * begdef,char * enddef,int params)531 void renewEnvironment(char *name, char *opt_param, char *begdef, char *enddef, int params)
532 
533 /**************************************************************************
534      purpose: allocates and initializes a \renewenvironment
535 **************************************************************************/
536 {
537     int i;
538 
539     i = existsEnvironment(name);
540 
541     if (i < 0) {
542         newEnvironment(name, opt_param, begdef, enddef, params);
543         diagnostics(2, "No existing \\newevironment{%s}", name);
544 
545     } else {
546         free(NewEnvironments[i].begdef);
547         free(NewEnvironments[i].enddef);
548         free(NewEnvironments[i].begname);
549         free(NewEnvironments[i].endname);
550         if (NewEnvironments[i].opt_param)
551             free(NewEnvironments[i].opt_param);
552         if (opt_param) {
553             NewEnvironments[i].opt_param = strdup(opt_param);
554             if (NewEnvironments[i].opt_param == NULL) {
555                 diagnostics(ERROR, "\nCannot allocate opt_param for \\renewenvironment{%s}", name);
556             }
557         } else {
558             NewEnvironments[i].opt_param = NULL;
559         }
560         NewEnvironments[i].params = params;
561         NewEnvironments[i].begdef = strdup(begdef);
562         NewEnvironments[i].enddef = strdup(enddef);
563         if (NewEnvironments[i].begdef == NULL || NewEnvironments[i].enddef == NULL) {
564             diagnostics(ERROR, "Cannot allocate memory for \\renewenvironment{%s}", name);
565         }
566     }
567 }
568 
expandEnvironment(int thedef,int code)569 char *expandEnvironment(int thedef, int code)
570 
571 /**************************************************************************
572      purpose: retrieves and expands a \newenvironment
573 **************************************************************************/
574 {
575     char *s, *t;
576 
577     if (thedef < 0 || thedef >= iNewEnvironmentCount)
578         return NULL;
579 
580     if (code == CMD_BEGIN) {
581 
582         diagnostics(3, "\\begin{%s} <%s>", NewEnvironments[thedef].name, NewEnvironments[thedef].begdef);
583         s= expandmacro(NewEnvironments[thedef].begdef,
584           NewEnvironments[thedef].opt_param, NewEnvironments[thedef].params);
585         t = strdup_together("{",s);
586 
587     } else {
588 
589         diagnostics(3, "\\end{%s} <%s>", NewEnvironments[thedef].name, NewEnvironments[thedef].enddef);
590         s = expandmacro(NewEnvironments[thedef].enddef, NULL, 0);
591         t = strdup_together(s,"}");
592     }
593 
594     free(s);
595     return t;
596 }
597 
newTheorem(char * name,char * caption,char * numbered_like,char * within)598 void newTheorem(char *name, char *caption, char *numbered_like, char *within)
599 
600 /**************************************************************************
601      purpose: allocates and initializes a \newtheorem
602 **************************************************************************/
603 {
604     if (iNewTheoremCount == MAX_THEOREMS) {
605         diagnostics(WARNING, "Too many \\newtheorems, ignoring %s", name);
606         return;
607     }
608 
609     NewTheorems[iNewTheoremCount].name = strdup(name);
610 
611     NewTheorems[iNewTheoremCount].caption = strdup(caption);
612 
613     if (numbered_like)
614         NewTheorems[iNewTheoremCount].numbered_like = strdup(numbered_like);
615     else
616         NewTheorems[iNewTheoremCount].numbered_like = strdup(name);
617 
618     if (within)
619         NewTheorems[iNewTheoremCount].within = strdup(within);
620     else
621         NewTheorems[iNewTheoremCount].within = NULL;
622 
623     setCounter(NewTheorems[iNewTheoremCount].numbered_like, 0);
624 
625     iNewTheoremCount++;
626 }
627 
existsTheorem(char * s)628 int existsTheorem(char *s)
629 
630 /**************************************************************************
631      purpose: checks to see if a user created environment exists
632      returns: the array index of the \newtheorem
633 **************************************************************************/
634 {
635     int i = 0;
636 
637     while (i < iNewTheoremCount && !strequal(s, NewTheorems[i].name)) {
638         diagnostics(6, "seeking=<%s>, i=%d, current=<%s>", s, i, NewTheorems[i].name);
639         i++;
640     }
641 
642     if (i == iNewTheoremCount)
643         return -1;
644     else
645         return i;
646 }
647 
expandTheorem(int i,char * option)648 char *expandTheorem(int i, char *option)
649 
650 /**************************************************************************
651      purpose: retrieves and expands a \newtheorem into a string
652 **************************************************************************/
653 {
654     char s[128], *num;
655     int ithm;
656 
657     if (i < 0 || i >= iNewTheoremCount)
658         return strdup("");
659 
660     incrementCounter(NewTheorems[i].numbered_like);
661     ithm = getCounter(NewTheorems[i].numbered_like);
662 
663     if (NewTheorems[i].within) {
664         num = FormatUnitNumber(NewTheorems[i].within);
665         if (option)
666             snprintf(s, 128, "%s %s.%d (%s)", NewTheorems[i].caption, num, ithm, option);
667         else
668             snprintf(s, 128, "%s %s.%d", NewTheorems[i].caption, num, ithm);
669         free(num);
670     } else {
671         if (option)
672             snprintf(s, 128, "%s %d (%s)", NewTheorems[i].caption, ithm, option);
673         else
674             snprintf(s, 128, "%s %d", NewTheorems[i].caption, ithm);
675     }
676 
677     return strdup(s);
678 }
679 
resetTheoremCounter(char * unit)680 void resetTheoremCounter(char *unit)
681 
682 /**************************************************************************
683      purpose: resets theorem counters based on unit
684 **************************************************************************/
685 {
686     int i;
687 
688     for (i = 0; i < iNewTheoremCount; i++) {
689         if (strequal(unit, NewTheorems[i].within))
690             setCounter(NewTheorems[i].numbered_like, 0);
691     }
692 }
693