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