563 lines
14 KiB
C
563 lines
14 KiB
C
/* 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$
|
|
*
|
|
* 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$";
|
|
#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(char *, char);
|
|
static int env_length(char *);
|
|
|
|
#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(string str,
|
|
const character_class set)
|
|
{
|
|
while (*str && ! set[(int)*str]) str++;
|
|
return (! *str);
|
|
}
|
|
|
|
static int
|
|
pure_text_length(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(char opener)
|
|
{
|
|
switch (opener) {
|
|
case '(':
|
|
return(')');
|
|
case '{':
|
|
return('}');
|
|
case '[':
|
|
return(']');
|
|
case '<':
|
|
return('>');
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
abort();
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/* the char * that str points to is free'd by this function.
|
|
* if you want to keep it, save it yourself
|
|
*/
|
|
string
|
|
verbatim(string str, int bracketsonly)
|
|
{
|
|
char *temp,*temp2;
|
|
int bracketnum,len;
|
|
|
|
len = strlen(str);
|
|
if (len == 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(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(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(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(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(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(char *str,
|
|
int *pstr,
|
|
int *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);
|
|
}
|