]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blobdiff - zephyr/zwgc/formatter.c
upstream tag
[1ts-debian.git] / zephyr / zwgc / formatter.c
diff --git a/zephyr/zwgc/formatter.c b/zephyr/zwgc/formatter.c
new file mode 100644 (file)
index 0000000..c63f87f
--- /dev/null
@@ -0,0 +1,559 @@
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ *      Created by:     Marc Horowitz <marc@athena.mit.edu>
+ *
+ *      $Id: formatter.c,v 1.15 1999/01/22 23:20:20 ghudson Exp $
+ *
+ *      Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ *      For copying and distribution information, see the file
+ *      "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_formatter_c[] = "$Id: formatter.c,v 1.15 1999/01/22 23:20:20 ghudson Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+#include <zephyr/zephyr.h>
+
+#include "new_memory.h"
+#include "char_stack.h"
+#include "string_dictionary.h"
+#include "formatter.h"
+#include "text_operations.h"
+
+#if !defined(__STDC__) && !defined(const)
+#define const
+#endif
+
+static int pure_text_length(), env_length();
+
+#ifdef notdef
+static character_class atsign_set = { /* '@' = 0x40 */
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+  };
+#endif
+
+static const character_class paren_set = { /* '(' = 0x28, ')' = 0x29 */
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+  };
+
+static const character_class sbracket_set = { /* '[' = 0x5b, ']' = 0x5d */
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+  };
+
+static const character_class abracket_set = { /* '<' = 0x3c, '>' = 0x3e */
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+  };
+
+static const character_class cbracket_set = { /* '{' = 0x7b, '}' = 0x7d */
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+  };
+
+static const character_class allbracket_set = {
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+  };
+
+static const character_class allmaskable_set = {
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,
+   1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+  };
+
+static char brackets[]="()<>[]{}@";
+static char *openbracket[]={"@<","@<","@[","@[","@{","@{","@(","@(","@("};
+static char *closebracket[]={">",">","]","]","}","}",")",")",")"};
+
+static int not_contains(str, set)
+     string str;
+     const character_class set;
+{
+   while (*str && ! set[*str]) str++;
+   return (! *str);
+}
+
+static int pure_text_length(text,terminator)
+     char *text;
+     char terminator;
+{
+   int len=0;
+
+   while (1) {
+      while (*text!='@' && *text!=terminator && *text) {
+         text++;
+         len++;
+      }
+
+      if (*text!='@')
+         return(len);
+
+      if (*(text+1)=='@') {
+        text++;
+        len++;
+      } else if (env_length(text+1) != -1)
+        return(len);
+
+      text++;
+      len++;
+   }
+}
+
+static char otherside(opener)
+char opener;
+{
+   switch (opener) {
+    case '(':
+      return(')');
+    case '{':
+      return('}');
+    case '[':
+      return(']');
+    case '<':
+      return('>');
+   }
+
+#ifdef DEBUG
+   abort();
+#endif
+}
+
+/* the char * that str points to is free'd by this function.
+ * if you want to keep it, save it yourself
+ */
+string verbatim(str, bracketsonly)
+     string str;
+     int bracketsonly;
+{
+   char *temp,*temp2;
+   int bracketnum,len;
+
+   if (strlen(str) == pure_text_length(str,0)) {
+      /* No environments, so consider the fast-and-easy methods */
+
+      if (not_contains(str,allbracket_set)) {
+        temp = string_Copy(str);
+        free(str);
+        return(temp);
+      }
+
+      if (not_contains(str,abracket_set)) {
+        temp=(char *) malloc((len=strlen(str))+4);
+        temp[0]='@';
+        temp[1]='<';
+        (void) memcpy(temp+2,str,len);
+        temp[len+2]='>';
+        temp[len+3]='\0';
+        free(str);
+        return(temp);
+      }
+      if (not_contains(str,sbracket_set)) {
+        temp=(char *) malloc((len=strlen(str))+4);
+        temp[0]='@';
+        temp[1]='[';
+        (void) memcpy(temp+2,str,len);
+        temp[len+2]=']';
+        temp[len+3]='\0';
+        free(str);
+        return(temp);
+      }
+      if (not_contains(str,cbracket_set)) {
+        temp=(char *) malloc((len=strlen(str))+4);
+        temp[0]='@';
+        temp[1]='{';
+        (void) memcpy(temp+2,str,len);
+        temp[len+2]='}';
+        temp[len+3]='\0';
+        free(str);
+        return(temp);
+      }
+      if (not_contains(str,paren_set)) {
+        temp=(char *) malloc((len=strlen(str))+4);
+        temp[0]='@';
+        temp[1]='(';
+        (void) memcpy(temp+2,str,len);
+        temp[len+2]=')';
+        temp[len+3]='\0';
+        free(str);
+        return(temp);
+      }
+   }
+
+   temp=lbreak(&str,bracketsonly?allbracket_set:allmaskable_set);
+   while(*str) {
+      bracketnum=(int) (strchr(brackets,str[0])-brackets);
+      temp=string_Concat2(temp,openbracket[bracketnum]);
+      temp=string_Concat2(temp,temp2=lany(&str," "));
+      free(temp2);
+      temp=string_Concat2(temp,closebracket[bracketnum]);
+      temp=string_Concat2(temp,temp2=lbreak(&str,bracketsonly?
+                                         allbracket_set:allmaskable_set));
+      free(temp2);
+   }
+   free(str);  /* str is "" at this point, anyway */
+
+   return(temp);
+}
+
+/* text points to beginning of text string.  return value is
+   length of string, up to but not including the passed terminator
+   or the default terminator \0.  The text will not be modified,
+   and @@ will be counted twice */
+
+string protect(str)
+     string str;
+{
+   string temp,temp2,temp3;
+   int len,templen;
+   char_stack chs;
+   char tos;
+
+   temp = string_Copy("");
+   templen = 1;
+   chs = char_stack_create();
+
+   while(*str) {
+      tos = (char_stack_empty(chs)?0:char_stack_top(chs));
+
+      if (*str == tos) {
+        /* if the character is the next terminator */
+
+        temp = (char *) realloc(temp,++templen);
+        temp[templen-2] = *str++;
+        char_stack_pop(chs);
+        temp[templen-1] = '\0';
+      } else if (len = pure_text_length(str,tos)) {
+        if (tos) {
+           /* if the block is text in an environment, just copy it */
+
+           temp2 = string_CreateFromData(str,len);
+           str += len;
+           temp = string_Concat2(temp,temp2);
+           templen += len;
+           free(temp2);
+        } else {
+           /* if the block is top level text, verbatim brackets only
+              (not @'s) and add text to temp */
+
+           temp2 = string_CreateFromData(str,len);
+           str += len;
+           temp3 = verbatim(temp2,1);
+           temp = string_Concat2(temp,temp3);
+           templen += strlen(temp3);
+           free(temp3);
+        }
+      } else {
+        /* if the block is an environment, copy it, push delimiter */
+
+        len = env_length(str+1);
+        char_stack_push(chs,otherside(str[len+1]));
+        len += 2;
+        temp2 = string_CreateFromData(str,len);
+        str += len;
+        temp = string_Concat2(temp,temp2);
+        templen += len;
+        free(temp2);
+      }
+   }
+   /* all blocks have been copied. */
+
+   while (!char_stack_empty(chs)) {
+      temp = (char *) realloc(temp,++templen);
+      temp[templen-2] = char_stack_top(chs);
+      char_stack_pop(chs);
+   }
+   temp[templen-1] = '\0';
+
+   return(temp);
+}
+
+/* str points to a string.  return value is another string
+   which is the original with all styles removed. */
+string stylestrip(str)
+     string str;
+{
+    int templen = 0, otherchar;
+    char *temp = (char *) malloc(string_Length(str) + 1);
+    char_stack chs;
+    string ostr = str;
+
+    chs = char_stack_create();
+
+    while (*str) {
+       if (*str == '@') {
+           int len = env_length(str + 1);
+           if (len != -1) {
+               otherchar = 0;
+               if ((len == 4 && !strncasecmp(str + 1, "font", 4))
+                 || (len == 5 && !strncasecmp(str + 1, "color", 5)))
+                   otherchar = 0x80;
+               otherchar |= otherside(str[len + 1]);
+               char_stack_push(chs, otherchar);
+               str += len + 2;
+               continue;
+           }
+       }
+       if (!char_stack_empty(chs) && *str == (char_stack_top(chs) & 0x7f)) {
+           char_stack_pop(chs);
+           str++;
+           continue;
+       }
+       if (!char_stack_empty(chs) && (char_stack_top(chs) & 0x80))
+           str++;
+       else
+           temp[templen++] = *str++;
+    }
+    temp[templen] = 0;
+
+    while (!char_stack_empty(chs))
+       char_stack_pop(chs);
+    free(ostr);
+
+    return(temp);
+}
+
+void free_desc(desc)
+     desctype *desc;
+{
+    desctype *next_desc;
+
+    while (desc->code != DT_EOF) {
+       next_desc = desc->next;
+       free(desc);
+       desc = next_desc;
+    }
+    free(desc);
+}
+
+/* text points to beginning of possible env name.  return value is
+   length of env name, not including @ or opener, or -1 if not a
+   possible env name. */
+static int env_length(text)
+     char *text;
+{
+   int len=0;
+
+   while (*text && (isalnum(*text) || *text=='_')) {
+      text++;
+      len++;
+   }
+
+   if ((*text=='(') || (*text=='{') || (*text=='[') || (*text=='<'))
+     return(len);
+   else
+     return(-1);
+}
+
+/* text points to beginning of text string.  return value is
+   length of string, up to but not including the passed terminator
+   or the default terminators \0 \n @.  This can modify text, and 0
+   is a valid return value. */
+static int text_length(text,terminator)
+     char *text;
+     char terminator;
+{
+   int len=0;
+
+   while (1) {
+      while (*text!='@' && *text!='\n' && *text!=terminator && *text) {
+        text++;
+        len++;
+      }
+
+      if (*text!='@')
+        return(len);
+
+      if (*(text+1)=='@')
+        (void) memmove(text+1,text+2,strlen(text+1));
+      else if (env_length(text+1) != -1)
+       return(len);
+
+      text++;
+      len++;
+   }
+}
+
+/* parses str into a desc linked list.  Returns number of strings and
+   newlines in *pstr and *pnl */
+
+desctype *disp_get_cmds(str,pstr,pnl)
+char *str;
+int *pstr,*pnl;
+{
+   desctype *desc,*here;
+   int len;
+   char_stack terminators = char_stack_create();
+   char terminator;
+   int nstr=0, nnl=0;
+   char *curstr;
+
+   desc=(desctype *) malloc(sizeof(desctype));
+   here=desc;
+   curstr=str;
+   terminator = '\0';
+
+   while (*curstr) {
+      if (*curstr=='\n') {
+        here->code=DT_NL;
+        curstr++;
+        nnl++;
+      } else if (*curstr==terminator) { /* if this is the end of an env */
+        here->code=DT_END;
+        terminator = char_stack_top(terminators);
+        char_stack_pop(terminators);
+        curstr++;
+      } else if (len=text_length(curstr,terminator)) { /* if there is a text
+                                                         block here */
+        here->code=DT_STR;
+        here->str=curstr;
+        here->len=len;
+        curstr+=len;
+        nstr++;
+      } else if (*curstr=='@') { /* if this is the beginning of an env */
+        len=env_length(curstr+1);
+        here->code=DT_ENV;
+        here->str=curstr+1;
+        here->len=len;
+        char_stack_push(terminators, terminator);
+        terminator=otherside(*(curstr+1+len));
+        curstr+=(len+2); /* jump over @, env name, and opener */
+      }
+
+      here->next=(desctype *) malloc(sizeof(desctype));
+      here=here->next;
+   }
+
+   while (!char_stack_empty(terminators)) {
+      here->code=DT_END;
+      terminator = char_stack_top(terminators);
+      char_stack_pop(terminators);
+      here->next=(desctype *) malloc(sizeof(desctype));
+      here=here->next;
+   }
+   here->code=DT_EOF;
+   *pstr=nstr;
+   *pnl=nnl;
+
+#ifdef DEBUG_PRINTOUT
+   { string temp;
+       here = desc;
+       while (here->code != DT_EOF) {
+          if (here->code == DT_STR || here->code == DT_ENV) {
+              temp = string_CreateFromData(here->str, here->len);
+              printf("[%d <%s>]\n", here->code, temp);
+              free(temp);
+          } else
+            printf("[%d]\n", here->code);
+          here=here->next;
+       }
+ }
+#endif
+
+   return(desc);
+}