]> git.saurik.com Git - wxWidgets.git/blobdiff - utils/tex2rtf/src/tex2any.cpp
Changed FindTExt intercase to more easily handle additional flags.
[wxWidgets.git] / utils / tex2rtf / src / tex2any.cpp
index 71ab0cf7b970a95f97c72e45e501330e2feba7b0..bfeecc2ebdab13249b68f20373b80115eca084d3 100644 (file)
@@ -72,6 +72,26 @@ int             hugeFont1 = 20;
 int             HugeFont2 = 24;
 int             HUGEFont3 = 28;
 
+// All of these tokens MUST be found on a line by themselves (no other
+// text) and must start at the first character of the line, or tex2rtf
+// will fail to process them correctly (a limitation of tex2rtf, not TeX)
+static const wxString syntaxTokens[] =
+{ "\\begin{verbatim}",
+  "\\begin{toocomplex}",
+  "\\end{verbatim}",
+  "\\end{toocomplex}",
+  "\\verb",
+  "\\begin{comment}",
+  "\\end{comment}",
+  "\\verbatiminput",
+//  "\\par",
+  "\\input",
+  "\\helpinput",
+  "\\include",
+  wxEmptyString
+};
+
+
 /*
  * USER-ADJUSTABLE SETTINGS
  *
@@ -88,6 +108,8 @@ bool            winHelp = FALSE;  // Output in Windows Help format if TRUE, line
 bool            isInteractive = FALSE;
 bool            runTwice = FALSE;
 int             convertMode = TEX_RTF;
+bool            checkCurleyBraces = FALSE;
+bool            checkSyntax = FALSE;
 bool            headerRule = FALSE;
 bool            footerRule = FALSE;
 bool            compatibilityMode = FALSE; // If TRUE, maximum Latex compatibility
@@ -107,6 +129,7 @@ int             winHelpVersion = 3; // WinHelp Version (3 for Windows 3.1, 4 for
 bool            winHelpContents = FALSE; // Generate .cnt file for WinHelp 4
 bool            htmlIndex = FALSE; // Generate .htx file for HTML
 bool            htmlFrameContents = FALSE; // Use frames for HTML contents page
+char           *htmlStylesheet = NULL; // Use this CSS stylesheet for HTML pages
 bool            useHeadingStyles = TRUE; // Insert \s1, s2 etc.
 bool            useWord = TRUE; // Insert proper Word table of contents, etc etc
 int             contentsDepth = 4; // Depth of Word table of contents
@@ -122,6 +145,8 @@ char            *linkColourString = NULL;
 char            *followedLinkColourString = NULL;
 bool            combineSubSections = FALSE;
 bool            htmlWorkshopFiles = FALSE;
+bool            ignoreBadRefs = FALSE;
+char                   *htmlFaceName = copystring("Times New Roman");
 
 extern int passNumber;
 
@@ -196,7 +221,8 @@ TexMacroDef *VerbatimMacroDef = NULL;
 #define IncrementLineNumber() LineNumbers[CurrentInputIndex] ++
 
 
-TexRef::TexRef(char *label, char *file, char *section, char *sectionN)
+TexRef::TexRef(const char *label, const char *file,
+              const char *section, const char *sectionN)
 {
     refLabel = copystring(label);
     refFile = file ? copystring(file) : (char*) NULL;
@@ -221,7 +247,7 @@ CustomMacro::~CustomMacro()
         delete [] macroBody;
 }
 
-void TexOutput(char *s, bool ordinaryText)
+void TexOutput(const char *s, bool ordinaryText)
 {
   int len = strlen(s);
 
@@ -252,19 +278,19 @@ void TexOutput(char *s, bool ordinaryText)
 
 void ForbidWarning(TexMacroDef *def)
 {
-  char buf[100];
+  wxString informBuf;
   switch (def->forbidden)
   {
     case FORBID_WARN:
     {
-      sprintf(buf, "Warning: it is recommended that command %s is not used.", def->name);
-      OnInform(buf);
+      informBuf.Printf("Warning: it is recommended that command %s is not used.", def->name);
+      OnInform((char *)informBuf.c_str());
       break;
     }
     case FORBID_ABSOLUTELY:
     {
-      sprintf(buf, "Error: command %s cannot be used and will lead to errors.", def->name);
-      OnInform(buf);
+      informBuf.Printf("Error: command %s cannot be used and will lead to errors.", def->name);
+      OnInform((char *)informBuf.c_str());
       break;
     }
     default:
@@ -395,10 +421,10 @@ bool readInVerbatim = FALSE;  // Within a verbatim, but not nec. verbatiminput
 
 // Switched this off because e.g. \verb${$ causes it to fail. There is no
 // detection of \verb yet.
-#define CHECK_BRACES 1
+// #define CHECK_BRACES 1
 
-unsigned long leftCurly = 0;
-unsigned long rightCurly = 0;
+unsigned long leftCurley = 0;
+unsigned long rightCurley = 0;
 static wxString currentFileName = "";
 
 bool read_a_line(char *buf)
@@ -408,7 +434,7 @@ bool read_a_line(char *buf)
     buf[0] = 0;
     return FALSE;
   }
-  
+
   int ch = -2;
   int bufIndex = 0;
   buf[0] = 0;
@@ -430,24 +456,25 @@ bool read_a_line(char *buf)
 
     ch = getc(Inputs[CurrentInputIndex]);
 
-#if CHECK_BRACES
-    if (ch == '{' && !readInVerbatim)
-       leftCurly++;
-    if (ch == '}' && !readInVerbatim)
+    if (checkCurleyBraces)
     {
-       rightCurly++;
-       if (rightCurly > leftCurly)
-       {
-           wxString errBuf;
-           errBuf.Printf("An extra right curly brace ('}') was detected at line %lu inside file %s",LineNumbers[CurrentInputIndex], (const char*) currentFileName.c_str());
-           OnError((char *)errBuf.c_str());
-
-           // Reduce the count of right curly braces, so the mismatched count
-           // isn't reported on every line that has a '}' after the first mismatch
-           rightCurly--;
-       }
+        if (ch == '{' && !readInVerbatim)
+           leftCurley++;
+        if (ch == '}' && !readInVerbatim)
+        {
+           rightCurley++;
+           if (rightCurley > leftCurley)
+           {
+               wxString errBuf;
+               errBuf.Printf("An extra right Curley brace ('}') was detected at line %lu inside file %s",LineNumbers[CurrentInputIndex], (const char*) currentFileName.c_str());
+               OnError((char *)errBuf.c_str());
+
+               // Reduce the count of right Curley braces, so the mismatched count
+               // isn't reported on every line that has a '}' after the first mismatch
+               rightCurley--;
+           }
+        }
     }
-#endif
 
     if (ch != EOF)
     {
@@ -496,14 +523,14 @@ bool read_a_line(char *buf)
       {
 
         // Convert embedded characters to RTF equivalents
-               switch(ch)
-               {
-               case 0xf6: // ö
-               case 0xe4: // ü
-               case 0xfc: // ü
-               case 0xd6: // Ö
-               case 0xc4: // Ä
-               case 0xdc: // Ü         
+        switch(ch)
+        {
+        case 0xf6: // ö
+        case 0xe4: // ü
+        case 0xfc: // ü
+        case 0xd6: // Ö
+        case 0xc4: // Ä
+        case 0xdc: // Ü     
                 if (bufIndex+5 >= MAX_LINE_BUFFER_SIZE)
                 {
                    wxString errBuf;
@@ -512,21 +539,21 @@ bool read_a_line(char *buf)
                    OnError((char *)errBuf.c_str());
                    return FALSE;
                 }
-                               buf[bufIndex++]='\\';
-                               buf[bufIndex++]='"';
-                               buf[bufIndex++]='{';
-                               switch(ch)
-                               {
-                                       case 0xf6:buf[bufIndex++]='o';break; // ö
-                                       case 0xe4:buf[bufIndex++]='a';break; // ä
-                                       case 0xfc:buf[bufIndex++]='u';break; // ü
-                                       case 0xd6:buf[bufIndex++]='O';break; // Ö
-                                       case 0xc4:buf[bufIndex++]='A';break; // Ä
-                                       case 0xdc:buf[bufIndex++]='U';break; // Ü                                       
-                               }                               
-                               buf[bufIndex++]='}';
-                               break;
-               case 0xdf: // ß 
+                buf[bufIndex++]='\\';
+                buf[bufIndex++]='"';
+                buf[bufIndex++]='{';
+                switch(ch)
+                {
+                    case 0xf6:buf[bufIndex++]='o';break; // ö
+                    case 0xe4:buf[bufIndex++]='a';break; // ä
+                    case 0xfc:buf[bufIndex++]='u';break; // ü
+                    case 0xd6:buf[bufIndex++]='O';break; // Ö
+                    case 0xc4:buf[bufIndex++]='A';break; // Ä
+                    case 0xdc:buf[bufIndex++]='U';break; // Ü                   
+                }               
+                buf[bufIndex++]='}';
+                break;
+        case 0xdf: // ß 
             if (bufIndex+5 >= MAX_LINE_BUFFER_SIZE)
             {
               wxString errBuf;
@@ -535,13 +562,13 @@ bool read_a_line(char *buf)
               OnError((char *)errBuf.c_str());
               return FALSE;
             }
-                       buf[bufIndex++]='\\';
-                       buf[bufIndex++]='s';
-                       buf[bufIndex++]='s';
+            buf[bufIndex++]='\\';
+            buf[bufIndex++]='s';
+            buf[bufIndex++]='s';
             buf[bufIndex++]='\\';
             buf[bufIndex++]='/';
-                       break;  
-               default:
+            break;  
+        default:
             if (bufIndex >= MAX_LINE_BUFFER_SIZE)
             {
               wxString errBuf;
@@ -550,8 +577,46 @@ bool read_a_line(char *buf)
               OnError((char *)errBuf.c_str());
               return FALSE;
             }
-                       buf[bufIndex++] = ch;
-                       break;
+            // If the current character read in is a '_', we need to check 
+            // whether there should be a '\' before it or not
+            if (ch != '_')
+            {
+                buf[bufIndex++] = ch;
+                break;
+            }
+
+            if (checkSyntax)
+            {
+                if (readInVerbatim)
+                {
+                    // There should NOT be a '\' before the '_'
+                    if ((bufIndex > 0 && (buf[bufIndex-1] == '\\')) && (buf[0] != '%'))
+                    {
+                        wxString errBuf;
+                        errBuf.Printf("An underscore ('_') was detected at line %lu inside file %s that should NOT have a '\\' before it.",LineNumbers[CurrentInputIndex], (const char*) currentFileName.c_str());
+                        OnError((char *)errBuf.c_str());
+                    }
+                }
+                else
+                {
+                    // There should be a '\' before the '_'
+                    if (bufIndex == 0)
+                    {
+                        wxString errBuf;
+                        errBuf.Printf("An underscore ('_') was detected at line %lu inside file %s that may need a '\\' before it.",LineNumbers[CurrentInputIndex], (const char*) currentFileName.c_str());
+                        OnError((char *)errBuf.c_str());
+                    }
+                    else if ((buf[bufIndex-1] != '\\') && (buf[0] != '%') &&  // If it is a comment line, then no warnings
+                        (strncmp(buf, "\\input", 6))) // do not report filenames that have underscores in them
+                    {
+                        wxString errBuf;
+                        errBuf.Printf("An underscore ('_') was detected at line %lu inside file %s that may need a '\\' before it.",LineNumbers[CurrentInputIndex], (const char*) currentFileName.c_str());
+                        OnError((char *)errBuf.c_str());
+                    }
+                }
+            }
+            buf[bufIndex++] = ch;
+            break;
         }  // switch
       }  // else
     }
@@ -563,16 +628,19 @@ bool read_a_line(char *buf)
       if (CurrentInputIndex > 0) 
          ch = ' '; // No real end of file
       CurrentInputIndex --;
-#if CHECK_BRACES
-      if (leftCurly != rightCurly)
+
+      if (checkCurleyBraces)
       {
-        wxString errBuf;
-        errBuf.Printf("Curly braces do not match inside file %s\n%lu opens, %lu closes", (const char*) currentFileName.c_str(),leftCurly,rightCurly);
-        OnError((char *)errBuf.c_str());
+          if (leftCurley != rightCurley)
+          {
+            wxString errBuf;
+            errBuf.Printf("Curley braces do not match inside file %s\n%lu opens, %lu closes", (const char*) currentFileName.c_str(),leftCurley,rightCurley);
+            OnError((char *)errBuf.c_str());
+          }
+          leftCurley = 0;
+          rightCurley = 0;
       }
-      leftCurly = 0;
-      rightCurly = 0;
-#endif
+
       if (readingVerbatim)
       {
         readingVerbatim = FALSE;
@@ -730,6 +798,41 @@ bool read_a_line(char *buf)
     bool succ = read_a_line(buf);
     return succ;
   }
+
+  if (checkSyntax)
+  {
+      wxString bufStr = buf;
+      int index = 0;
+      size_t pos = 0;
+      for (index=0; syntaxTokens[index] != wxEmptyString; index++)
+      {
+          pos = bufStr.find(syntaxTokens[index]);
+          if (pos != wxString::npos && pos != 0)
+          {
+              size_t commentStart = bufStr.find("%");
+              if (commentStart == wxString::npos || commentStart > pos)
+              {
+                  wxString errBuf;
+                  if (syntaxTokens[index] == "\\verb")
+                  {
+                      errBuf.Printf("'%s$....$' was detected at line %lu inside file %s.  Please replace this form with \\tt{....}",
+                                    syntaxTokens[index].c_str(),
+                                    LineNumbers[CurrentInputIndex],
+                                    currentFileName.c_str());
+                  }
+                  else
+                  {
+                      errBuf.Printf("'%s' was detected at line %lu inside file %s that is not the only text on the line, starting at column one.",
+                                    syntaxTokens[index].c_str(),
+                                    LineNumbers[CurrentInputIndex],
+                                    currentFileName.c_str());
+                  }
+                  OnError((char *)errBuf.c_str());
+              }
+          }
+      }
+  }  // checkSyntax
+
   if (strncmp(buf, "\\begin{verbatim}", 16) == 0 ||
       strncmp(buf, "\\begin{toocomplex}", 18) == 0)
     readInVerbatim = TRUE;
@@ -737,14 +840,15 @@ bool read_a_line(char *buf)
            strncmp(buf, "\\end{toocomplex}", 16) == 0)
     readInVerbatim = FALSE;
 
-#if CHECK_BRACES
-  if (ch == EOF && leftCurly != rightCurly)
+  if (checkCurleyBraces)
   {
-    wxString errBuf;
-    errBuf.Printf("Curly braces do not match inside file %s\n%lu opens, %lu closes", (const char*) currentFileName.c_str(),leftCurly,rightCurly);
-    OnError((char *)errBuf.c_str());
+      if (ch == EOF && leftCurley != rightCurley)
+      {
+        wxString errBuf;
+        errBuf.Printf("Curley braces do not match inside file %s\n%lu opens, %lu closes", (const char*) currentFileName.c_str(),leftCurley,rightCurley);
+        OnError((char *)errBuf.c_str());
+      }
   }
-#endif
 
   return (ch == EOF);
 }  // read_a_line
@@ -841,6 +945,14 @@ void MacroError(char *buffer)
   errBuf.Printf("Could not find macro: %s at line %d, file %s",
              macroBuf, (int)(LineNumbers[CurrentInputIndex]-1), FileNames[CurrentInputIndex]);
   OnError((char *)errBuf.c_str());
+
+  if (wxStrcmp(macroBuf,"\\end{document}") == 0)
+  {
+      wxString buf;
+      buf = "Halted build due to unrecoverable error.";
+      OnInform((char *)buf.c_str());
+      stopRunning = TRUE;
+  }
 }
 
 /*
@@ -921,6 +1033,9 @@ int ParseArg(TexChunk *thisArg, wxList& children, char *buffer, int pos, char *e
       pos = 0;
       len = strlen(buffer);
       // Check for verbatim (or toocomplex, which comes to the same thing)
+      wxString bufStr = buffer;
+//      if (bufStr.find("\\begin{verbatim}") != wxString::npos ||
+//          bufStr.find("\\begin{toocomplex}") != wxString::npos)
       if (strncmp(buffer, "\\begin{verbatim}", 16) == 0 ||
           strncmp(buffer, "\\begin{toocomplex}", 18) == 0)
       {
@@ -1007,7 +1122,6 @@ int ParseArg(TexChunk *thisArg, wxList& children, char *buffer, int pos, char *e
         }
         pos ++;
         
-
         // Try matching \end{environment}
         if (environment && FindEndEnvironment(buffer, &pos, environment))
         {
@@ -1032,7 +1146,7 @@ int ParseArg(TexChunk *thisArg, wxList& children, char *buffer, int pos, char *e
           pos ++;
           int noBraces = 1;
 
-          wxBuffer[0] = 0;
+          wxTex2RTFBuffer[0] = 0;
           int i = 0;
           bool end = FALSE;
           while (!end)
@@ -1043,37 +1157,37 @@ int ParseArg(TexChunk *thisArg, wxList& children, char *buffer, int pos, char *e
               noBraces --;
               if (noBraces == 0)
               {
-                wxBuffer[i] = 0;
+                wxTex2RTFBuffer[i] = 0;
                 end = TRUE;
               }
               else
               {
-                wxBuffer[i] = '}';
+                wxTex2RTFBuffer[i] = '}';
                 i ++;
               }
               pos ++;
             }
             else if (ch == '{')
             {
-              wxBuffer[i] = '{';
+              wxTex2RTFBuffer[i] = '{';
               i ++;
               pos ++;
             }
             else if (ch == '\\' && buffer[pos+1] == '}')
             {
-              wxBuffer[i] = '}';
+              wxTex2RTFBuffer[i] = '}';
               pos += 2;
               i++;
             }
             else if (ch == '\\' && buffer[pos+1] == '{')
             {
-              wxBuffer[i] = '{';
+              wxTex2RTFBuffer[i] = '{';
               pos += 2;
               i++;
             }
             else
             {
-              wxBuffer[i] = ch;
+              wxTex2RTFBuffer[i] = ch;
               pos ++;
               i ++;
               if (ch == 0)
@@ -1094,7 +1208,7 @@ int ParseArg(TexChunk *thisArg, wxList& children, char *buffer, int pos, char *e
           TexChunk *argValue = new TexChunk(CHUNK_TYPE_STRING);
           arg->children.Append((wxObject *)argValue);
           argValue->argn = 1;
-          argValue->value = copystring(wxBuffer);
+          argValue->value = copystring(wxTex2RTFBuffer);
 
           children.Append((wxObject *)chunk);
         }
@@ -1139,8 +1253,8 @@ int ParseArg(TexChunk *thisArg, wxList& children, char *buffer, int pos, char *e
 
           children.Append((wxObject *)chunk);
         }
-       else
-       {
+    else
+    {
           char *env = NULL;
           bool tmpParseToBrace = TRUE;
           TexMacroDef *def = MatchMacro(buffer, &pos, &env, &tmpParseToBrace);
@@ -1149,6 +1263,7 @@ int ParseArg(TexChunk *thisArg, wxList& children, char *buffer, int pos, char *e
           CustomMacro *customMacro = FindCustomMacro(def->name);
 
           TexChunk *chunk = new TexChunk(CHUNK_TYPE_MACRO, def);
+
           chunk->no_args = def->no_args;
 //          chunk->name = copystring(def->name);
           chunk->macroId = def->macroId;
@@ -1237,7 +1352,7 @@ int ParseArg(TexChunk *thisArg, wxList& children, char *buffer, int pos, char *e
               }
             
 //            delete chunk; // Might delete children
-           }
+        }
           }
           else
           {
@@ -1245,7 +1360,7 @@ int ParseArg(TexChunk *thisArg, wxList& children, char *buffer, int pos, char *e
           }
         }
         else
-       {
+    {
          /*
           * If all else fails, we assume that we have
           * a pair of braces on their own, so return a `dummy' macro
@@ -1278,7 +1393,7 @@ int ParseArg(TexChunk *thisArg, wxList& children, char *buffer, int pos, char *e
           arg->macroId = chunk->macroId;
 
           pos = ParseArg(arg, arg->children, buffer, pos, NULL, TRUE, customMacroArgs);
-       }
+    }
         break;
       }
       case '$':
@@ -1440,7 +1555,7 @@ int ParseArg(TexChunk *thisArg, wxList& children, char *buffer, int pos, char *e
  *
  */
  
-int ParseMacroBody(char *macro_name, TexChunk *parent,
+int ParseMacroBody(const char *macro_name, TexChunk *parent,
                    int no_args, char *buffer, int pos,
                    char *environment, bool parseToBrace,
                    TexChunk *customMacroArgs)
@@ -1505,6 +1620,19 @@ int ParseMacroBody(char *macro_name, TexChunk *parent,
         isOptional = TRUE;
         pos += 2;
       }
+      else if (i > 0)
+      {
+        wxString errBuf;
+        wxString tmpBuffer(buffer);
+        if (tmpBuffer.length() > 4)
+        {
+            if (tmpBuffer.Right(4) == "\\par")
+                tmpBuffer = tmpBuffer.Mid(0,tmpBuffer.length()-4);
+        }
+        errBuf.Printf("Missing macro argument in the line:\n\t%s\n",tmpBuffer.c_str());
+        OnError((char *)errBuf.c_str());
+      }
+
     }
     arg->optional = isOptional;
 
@@ -1562,7 +1690,7 @@ bool TexLoadFile(char *filename)
   return FALSE;
 }
 
-TexMacroDef::TexMacroDef(int the_id, char *the_name, int n, bool ig, bool forbidLevel)
+TexMacroDef::TexMacroDef(int the_id, const char *the_name, int n, bool ig, bool forbidLevel)
 {
   name = copystring(the_name);
   no_args = n;
@@ -1754,14 +1882,16 @@ void TraverseFromChunk(TexChunk *chunk, wxNode *thisNode, bool childrenOnly)
         OnMacro(chunk->macroId, chunk->no_args, TRUE);
 
       wxNode *node = chunk->children.First();
+      TexChunk *child_chunk = NULL;
       while (node)
       {
-        TexChunk *child_chunk = (TexChunk *)node->Data();
+        child_chunk = (TexChunk *)node->Data();
         TraverseFromChunk(child_chunk, node);
         node = node->Next();
       }
 
-      if (thisNode && thisNode->Next()) nextChunk = (TexChunk *)thisNode->Next()->Data();
+      if (thisNode && thisNode->Next())
+          nextChunk = (TexChunk *)thisNode->Next()->Data();
 
       if (!childrenOnly)
         OnMacro(chunk->macroId, chunk->no_args, FALSE);
@@ -1789,7 +1919,8 @@ void TraverseFromChunk(TexChunk *chunk, wxNode *thisNode, bool childrenOnly)
 
       currentArgument = chunk;
 
-      if (thisNode && thisNode->Next()) nextChunk = (TexChunk *)thisNode->Next()->Data();
+      if (thisNode && thisNode->Next())
+          nextChunk = (TexChunk *)thisNode->Next()->Data();
 
       isArgOptional = chunk->optional;
       noArgs = chunk->no_args;
@@ -1807,7 +1938,9 @@ void TraverseFromChunk(TexChunk *chunk, wxNode *thisNode, bool childrenOnly)
         // If non-whitespace text, we no longer have a new paragraph.
         if (issuedNewParagraph && !((chunk->value[0] == 10 || chunk->value[0] == 13 || chunk->value[0] == 32)
                                     && chunk->value[1] == 0))
+        {
           issuedNewParagraph = FALSE;
+        }
         TexOutput(chunk->value, TRUE);
       }
       break;
@@ -1832,7 +1965,7 @@ void SetCurrentOutputs(FILE *fd1, FILE *fd2)
   CurrentOutput2 = fd2;
 }
 
-void AddMacroDef(int the_id, char *name, int n, bool ignore, bool forbid)
+void AddMacroDef(int the_id, const char *name, int n, bool ignore, bool forbid)
 {
   MacroDefs.Put(name, new TexMacroDef(the_id, name, n, ignore, forbid));
 }
@@ -3037,10 +3170,9 @@ bool DefaultOnArgument(int macroId, int arg_no, bool start)
         }
         else
         {
-          char buf[300];
-          TexOutput("??", TRUE);
-          sprintf(buf, "Warning: unresolved reference %s.", refName); 
-          OnInform(buf);
+           wxString informBuf;
+           informBuf.Printf("Warning: unresolved reference '%s'", refName); 
+           OnInform((char *)informBuf.c_str());
         }
       }
       else TexOutput("??", TRUE);
@@ -3199,9 +3331,9 @@ bool DefaultOnArgument(int macroId, int arg_no, bool start)
           TexOutput(ref->sectionNumber, TRUE);
           if (strcmp(ref->sectionNumber, "??") == 0)
           {
-            char buf[300];
-            sprintf(buf, "Warning: unresolved citation %s.", citeKey);
-            OnInform(buf);
+            wxString informBuf;
+            informBuf.Printf("Warning: unresolved citation %s.", citeKey);
+            OnInform((char *)informBuf.c_str());
           }
         }
         citeKey = ParseMultifieldString(citeKeys, &pos);