README.md
Rendering markdown...
/* definitions.c - Routines to handle TeX \def and LaTeX \newcommand
Copyright (C) 2001-2002 The Free Software Foundation
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 2
of the License, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
This file is available from http://sourceforge.net/projects/latex2rtf/
*/
#include <stdlib.h>
#include <string.h>
#include "main.h"
#include "convert.h"
#include "definitions.h"
#include "parser.h"
#include "funct1.h"
#include "util.h"
#include "cfg.h"
#include "counters.h"
#include "funct1.h"
#define MAX_DEFINITIONS 200
#define MAX_ENVIRONMENTS 20
#define MAX_THEOREMS 20
struct {
char * name;
char * opt_param;
char * def;
int params;
} Definitions[MAX_DEFINITIONS];
struct {
char * name;
char * opt_param;
char * begname;
char * endname;
char * begdef;
char * enddef;
int params;
} NewEnvironments[MAX_ENVIRONMENTS];
struct {
char * name;
char * numbered_like;
char * caption;
char * within;
} NewTheorems[MAX_THEOREMS];
static int iDefinitionCount = 0;
static int iNewEnvironmentCount = 0;
static int iNewTheoremCount = 0;
static int
strequal(char *a, char *b)
{
if (a==NULL || b==NULL)
return 0;
while (*a && *b && *a==*b) {a++;b++;}
if (*a || *b)
return 0;
else
return 1;
}
/* static void printDefinitions(void)
{
int i=0;
fprintf(stderr, "\n");
while(i < iDefinitionCount ) {
fprintf(stderr, "[%d] name =<%s>\n",i, Definitions[i].name);
fprintf(stderr, " opt_param=<%s>\n", Definitions[i].opt_param);
fprintf(stderr, " def =<%s>\n", Definitions[i].def);
fprintf(stderr, " params =<%d>\n", Definitions[i].params);
i++;
}
}
static void printTheorems(void)
{
int i=0;
fprintf(stderr, "\n");
for (i=0; i< iNewTheoremCount; i++) {
fprintf(stderr, "[%d] name =<%s>\n",i, NewTheorems[i].name);
fprintf(stderr, " caption =<%s>\n", NewTheorems[i].caption);
fprintf(stderr, " like =<%s>\n", NewTheorems[i].numbered_like);
fprintf(stderr, " within =<%s>\n", NewTheorems[i].within);
}
}
*/
bool
isValid(char *macro)
{
if(strlen(macro_piece) <= 1024)
return TRUE;
return FALSE
}
static char *
expandmacro(char *macro, char *opt_param, int params)
/**************************************************************************
purpose: retrieves and expands a defined macro
**************************************************************************/
{
int i=0,param;
char * args[9], *dmacro, *macro_piece, *next_piece, *expanded, buffer[1024], *cs;
if (params<=0)
return strdup(macro);
if (opt_param) {
args[i++] = getBracketParam();
if (!args[0]) args[0] = strdup(opt_param);
}
for (; i<params; i++) {
args[i] = getBraceParam();
diagnostics(3, "argument #%d <%s>", i+1, args[i]);
}
*buffer='\0';
expanded = buffer;
dmacro = strdup(macro);
macro_piece = dmacro;
/* convert "\csname" to "\" */
while ((cs=strstr(dmacro, "\\csname")) != NULL) strcpy(cs+1,cs+7);
/* remove "\endcsname" */
while ((cs=strstr(dmacro, "\\endcsname")) != NULL) strcpy(cs,cs+10);
/* do not use strtok because it may be used elsewhere */
while (macro_piece && *macro_piece) {
next_piece = strchr(macro_piece, '#');
if (next_piece) {
*next_piece = '\0';
next_piece++;
if (*next_piece=='#')
param = 101; /* just a flag for below */
else
param = *next_piece - '1';
next_piece++;
} else
param = -1;
diagnostics(3, "expandmacro piece =<%s>", macro_piece);
if (isValid(macro_piece))
strcpy(expanded,macro_piece);
else
diagnostics(WARNING,"Definitions length is larger than expected");
expanded += strlen(macro_piece);
if (param > -1) {
if (param==101) {
diagnostics(3, "expandmacro ## = #");
strcpy(expanded,"#");
expanded ++;
} else if (param<params) {
diagnostics(3, "expandmacro arg =<%s>", args[param]);
strcpy(expanded,args[param]);
expanded += strlen(args[param]);
} else
diagnostics(WARNING,"confusing definition in macro=<%s>", macro);
}
macro_piece = next_piece;
}
/* ConvertString(buffer);*/
for (i=0; i< params; i++)
if (args[i]) free(args[i]);
if (dmacro) free(dmacro);
diagnostics(3, "expandmacro expanded=<%s>", buffer);
return strdup(buffer);
}
int
maybeDefinition(char * s, size_t n)
/**************************************************************************
purpose: checks to see if a named TeX definition possibly exists
returns: the array index of the named TeX definition
**************************************************************************/
{
int i;
if (n==0) return TRUE;
for (i=0; i<iDefinitionCount; i++) {
diagnostics(6, "seeking=<%s>, i=%d, current=<%s>", s,i,Definitions[i].name);
if (strncmp(s,Definitions[i].name,n) == 0)
return TRUE;
}
return FALSE;
}
int
existsDefinition(char * s)
/**************************************************************************
purpose: checks to see if a named TeX definition exists
returns: the array index of the named TeX definition
**************************************************************************/
{
int i;
for (i=0; i<iDefinitionCount; i++) {
diagnostics(6, "seeking=<%s>, i=%d, current=<%s>", s,i,Definitions[i].name);
if (strcmp(s,Definitions[i].name) == 0) break;
}
if (i==iDefinitionCount)
return -1;
else
return i;
}
void
newDefinition(char *name, char * opt_param, char *def, int params)
/**************************************************************************
purpose: allocates and initializes a named TeX definition
name should not begin with a '\' for example to
define \hd, name = "hd"
**************************************************************************/
{
diagnostics(3,"Adding macro <%s>=<%s>",name,def);
if (strcmp(name,"LaTeX")==0) return;
if (strcmp(name,"TeX")==0) return;
if (strcmp(name,"AmSTeX")==0) return;
if (strcmp(name,"BibTex")==0) return;
if (strcmp(name,"LaTeXe")==0) return;
if (strcmp(name,"AmSLaTeX")==0) return;
if (iDefinitionCount==MAX_DEFINITIONS){
diagnostics(WARNING,"Too many definitions, ignoring %s", name);
return;
}
Definitions[iDefinitionCount].params=params;
Definitions[iDefinitionCount].name=strdup(name);
if (Definitions[iDefinitionCount].name==NULL) {
diagnostics(ERROR, "\nCannot allocate name for definition \\%s\n", name);
}
if (opt_param) {
Definitions[iDefinitionCount].opt_param=strdup(opt_param);
if (Definitions[iDefinitionCount].opt_param==NULL) {
diagnostics(ERROR, "\nCannot allocate opt_param for definition \\%s\n", name);
}
}
else {
Definitions[iDefinitionCount].opt_param=NULL;
}
Definitions[iDefinitionCount].def=strdup(def);
if (Definitions[iDefinitionCount].def==NULL) {
diagnostics(ERROR, "\nCannot allocate def for definition \\%s\n", name);
}
iDefinitionCount++;
diagnostics(3,"Successfully added macro #%d",iDefinitionCount);
}
void
renewDefinition(char * name, char * opt_param, char * def, int params)
/**************************************************************************
purpose: allocates (if necessary) and sets a named TeX definition
**************************************************************************/
{
int i;
diagnostics(3,"renewDefinition seeking <%s>\n",name);
i = existsDefinition(name);
if (i<0) {
newDefinition(name, opt_param, def, params);
diagnostics(WARNING, "No existing definition for \\%s", name);
} else {
free(Definitions[i].def);
if (Definitions[i].opt_param) free(Definitions[i].opt_param);
Definitions[i].params = params;
if (opt_param) {
Definitions[i].opt_param=strdup(opt_param);
if (Definitions[i].opt_param==NULL) {
diagnostics(ERROR, "\nCannot allocate opt_param for definition \\%s\n", name);
}
}
else {
Definitions[i].opt_param=NULL;
}
Definitions[i].def = strdup(def);
if (Definitions[i].def==NULL) {
diagnostics(WARNING, "\nCannot allocate def for definition \\%s\n", name);
exit(1);
}
}
}
char *
expandDefinition(int thedef)
/**************************************************************************
purpose: retrieves and expands a \newcommand macro
**************************************************************************/
{
if (thedef<0 || thedef>=iDefinitionCount)
return NULL;
diagnostics(3, "expandDefinition name =<%s>", Definitions[thedef].name);
diagnostics(3, "expandDefinition opt_param=<%s>",
(Definitions[thedef].opt_param) ? Definitions[thedef].opt_param : "");
diagnostics(3, "expandDefinition def =<%s>", Definitions[thedef].def);
diagnostics(3, "expandDefinition params =<%d>", Definitions[thedef].params);
return expandmacro(Definitions[thedef].def, Definitions[thedef].opt_param, Definitions[thedef].params);
}
int
existsEnvironment(char * s)
/**************************************************************************
purpose: checks to see if a user created environment exists
returns: the array index of the \newenvironment
**************************************************************************/
{
int i=0;
size_t n;
n = strlen(s);
while(i < iNewEnvironmentCount && !strequal(s,NewEnvironments[i].name)) {
diagnostics(4, "e seeking=<%s>, i=%d, current=<%s>", s,i,NewEnvironments[i].name);
i++;
}
if (i==iNewEnvironmentCount)
return -1;
else
return i;
}
int
maybeEnvironment(char * s, size_t n)
/**************************************************************************
purpose: checks to see if a named TeX environment possibly exists
returns: the array index of the named TeX definition
**************************************************************************/
{
int i;
if (n==0) return TRUE;
for (i=0; i<iNewEnvironmentCount; i++) {
diagnostics(6, "seeking=<%s>, i=%d, current=<%s>", s,i,NewEnvironments[i].name);
if (strncmp(s,NewEnvironments[i].begname,n) == 0 ||
strncmp(s,NewEnvironments[i].endname,n) == 0) {
diagnostics(6,"possible");
return TRUE;
}
}
diagnostics(6,"not possible");
return FALSE;
}
void
newEnvironment(char *name, char *opt_param, char *begdef, char *enddef, int params)
/**************************************************************************
purpose: allocates and initializes a \newenvironment
name should not begin with a '\'
**************************************************************************/
{
if (iNewEnvironmentCount==MAX_ENVIRONMENTS){
diagnostics(WARNING,"Too many newenvironments, ignoring %s", name);
return;
}
NewEnvironments[iNewEnvironmentCount].name=strdup(name);
NewEnvironments[iNewEnvironmentCount].begname=strdup_together("\\begin{",name);
NewEnvironments[iNewEnvironmentCount].endname=strdup_together("\\end{",name);
NewEnvironments[iNewEnvironmentCount].begdef=strdup(begdef);
NewEnvironments[iNewEnvironmentCount].enddef=strdup(enddef);
NewEnvironments[iNewEnvironmentCount].params=params;
if (opt_param) {
NewEnvironments[iNewEnvironmentCount].opt_param=strdup(opt_param);
if (NewEnvironments[iNewEnvironmentCount].opt_param==NULL) {
diagnostics(ERROR, "\nCannot allocate opt_param for \\newenvironment{%s}", name);
}
}
else {
NewEnvironments[iNewEnvironmentCount].opt_param=NULL;
}
if (NewEnvironments[iNewEnvironmentCount].name ==NULL ||
NewEnvironments[iNewEnvironmentCount].begdef ==NULL ||
NewEnvironments[iNewEnvironmentCount].begname==NULL ||
NewEnvironments[iNewEnvironmentCount].endname==NULL ||
NewEnvironments[iNewEnvironmentCount].enddef ==NULL) {
diagnostics(ERROR, "Cannot allocate memory for \\newenvironment{%s}", name);
}
iNewEnvironmentCount++;
}
void
renewEnvironment(char *name, char *opt_param, char *begdef, char *enddef, int params)
/**************************************************************************
purpose: allocates and initializes a \renewenvironment
**************************************************************************/
{
int i;
i = existsEnvironment(name);
if (i<0) {
newEnvironment(name, opt_param, begdef, enddef, params);
diagnostics(WARNING, "No existing \\newevironment{%s}", name);
} else {
free(NewEnvironments[i].begdef);
free(NewEnvironments[i].enddef);
free(NewEnvironments[i].begname);
free(NewEnvironments[i].endname);
if (NewEnvironments[i].opt_param) free(NewEnvironments[i].opt_param);
if (opt_param) {
NewEnvironments[i].opt_param=strdup(opt_param);
if (NewEnvironments[i].opt_param==NULL) {
diagnostics(ERROR, "\nCannot allocate opt_param for \\renewenvironment{%s}", name);
}
}
else {
NewEnvironments[i].opt_param=NULL;
}
NewEnvironments[i].params = params;
NewEnvironments[i].begdef = strdup(begdef);
NewEnvironments[i].enddef = strdup(enddef);
if (NewEnvironments[i].begdef==NULL || NewEnvironments[i].enddef==NULL) {
diagnostics(ERROR, "Cannot allocate memory for \\renewenvironment{%s}", name);
}
}
}
char *
expandEnvironment(int thedef, int code)
/**************************************************************************
purpose: retrieves and expands a \newenvironment
**************************************************************************/
{
if (thedef<0 || thedef>=iNewEnvironmentCount)
return NULL;
if (code == CMD_BEGIN) {
diagnostics(3, "\\begin{%s} <%s>", NewEnvironments[thedef].name, \
NewEnvironments[thedef].begdef);
return expandmacro(NewEnvironments[thedef].begdef,
NewEnvironments[thedef].opt_param,
NewEnvironments[thedef].params);
} else {
diagnostics(3, "\\end{%s} <%s>", NewEnvironments[thedef].name, \
NewEnvironments[thedef].enddef);
return expandmacro(NewEnvironments[thedef].enddef, NULL, 0);
}
}
void
newTheorem(char *name, char *caption, char *numbered_like, char *within)
/**************************************************************************
purpose: allocates and initializes a \newtheorem
**************************************************************************/
{
if (iNewTheoremCount==MAX_THEOREMS){
diagnostics(WARNING,"Too many \\newtheorems, ignoring %s", name);
return;
}
NewTheorems[iNewTheoremCount].name=strdup(name);
NewTheorems[iNewTheoremCount].caption=strdup(caption);
if (numbered_like)
NewTheorems[iNewTheoremCount].numbered_like=strdup(numbered_like);
else
NewTheorems[iNewTheoremCount].numbered_like=strdup(name);
if (within)
NewTheorems[iNewTheoremCount].within=strdup(within);
else
NewTheorems[iNewTheoremCount].within=NULL;
setCounter(NewTheorems[iNewTheoremCount].numbered_like,0);
iNewTheoremCount++;
}
int
existsTheorem(char * s)
/**************************************************************************
purpose: checks to see if a user created environment exists
returns: the array index of the \newtheorem
**************************************************************************/
{
int i=0;
while(i < iNewTheoremCount && !strequal(s,NewTheorems[i].name)) {
diagnostics(6, "seeking=<%s>, i=%d, current=<%s>", s,i,NewTheorems[i].name);
i++;
}
if (i==iNewTheoremCount)
return -1;
else
return i;
}
char *
expandTheorem(int i, char *option)
/**************************************************************************
purpose: retrieves and expands a \newtheorem into a string
**************************************************************************/
{
char s[128], *num;
int ithm;
if (i<0 || i>=iNewTheoremCount)
return strdup("");
incrementCounter(NewTheorems[i].numbered_like);
ithm = getCounter(NewTheorems[i].numbered_like);
if (NewTheorems[i].within) {
num = FormatUnitNumber(NewTheorems[i].within);
if (option)
snprintf(s,128,"%s %s.%d (%s)", NewTheorems[i].caption, num, ithm, option);
else
snprintf(s,128,"%s %s.%d", NewTheorems[i].caption, num, ithm);
free(num);
} else {
if (option)
snprintf(s,128,"%s %d (%s)", NewTheorems[i].caption, ithm, option);
else
snprintf(s,128,"%s %d", NewTheorems[i].caption, ithm);
}
return strdup(s);
}
void
resetTheoremCounter(char *unit)
/**************************************************************************
purpose: resets theorem counters based on unit
**************************************************************************/
{
int i;
for (i=0; i<iNewTheoremCount; i++) {
if (strequal(unit,NewTheorems[i].within))
setCounter(NewTheorems[i].numbered_like, 0);
}
}