1 /* This file is part of the Project Athena Zephyr Notification System.
2 * It is one of the source files comprising zwgc, the Zephyr WindowGram
5 * Created by: Marc Horowitz <marc@athena.mit.edu>
9 * Copyright (c) 1989 by the Massachusetts Institute of Technology.
10 * For copying and distribution information, see the file
16 #if (!defined(lint) && !defined(SABER))
17 static const char rcsid_formatter_c[] = "$Id$";
20 #include <zephyr/mit-copyright.h>
21 #include <zephyr/zephyr.h>
23 #include "new_memory.h"
24 #include "char_stack.h"
25 #include "string_dictionary.h"
26 #include "formatter.h"
27 #include "text_operations.h"
29 #if !defined(__STDC__) && !defined(const)
33 static int pure_text_length(char *, char);
34 static int env_length(char *);
37 static character_class atsign_set = { /* '@' = 0x40 */
38 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
39 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
40 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
41 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
42 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
43 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
44 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
45 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
46 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
47 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
48 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
49 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
50 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
51 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
52 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
53 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
57 static const character_class paren_set = { /* '(' = 0x28, ')' = 0x29 */
58 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
59 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
60 0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,
61 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
62 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
63 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
64 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
65 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
66 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
67 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
68 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
69 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
70 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
71 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
72 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
73 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
76 static const character_class sbracket_set = { /* '[' = 0x5b, ']' = 0x5d */
77 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
78 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
79 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
80 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
81 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
82 0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,
83 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
84 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
85 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
86 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
87 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
88 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
89 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
90 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
91 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
92 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
95 static const character_class abracket_set = { /* '<' = 0x3c, '>' = 0x3e */
96 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
97 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
98 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
99 0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,
100 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
101 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
102 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
103 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
104 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
105 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
106 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
107 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
108 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
109 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
110 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
111 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
114 static const character_class cbracket_set = { /* '{' = 0x7b, '}' = 0x7d */
115 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
116 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
117 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
118 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
119 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
120 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
121 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
122 0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,
123 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
124 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
125 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
126 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
127 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
128 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
129 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
130 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
133 static const character_class allbracket_set = {
134 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
135 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
136 0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,
137 0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,
138 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
139 0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,
140 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
141 0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,
142 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
143 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
144 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
145 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
146 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
147 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
148 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
149 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
152 static const character_class allmaskable_set = {
153 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
154 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
155 0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,
156 0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,
157 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
158 0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,
159 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
160 0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,
161 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
162 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
163 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
164 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
165 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
166 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
167 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
168 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
171 static char brackets[]="()<>[]{}@";
172 static char *openbracket[]={"@<","@<","@[","@[","@{","@{","@(","@(","@("};
173 static char *closebracket[]={">",">","]","]","}","}",")",")",")"};
176 not_contains(string str,
177 const character_class set)
179 while (*str && ! set[(int)*str]) str++;
184 pure_text_length(char *text,
190 while (*text!='@' && *text!=terminator && *text) {
198 if (*(text+1)=='@') {
201 } else if (env_length(text+1) != -1)
210 otherside(char opener)
229 /* the char * that str points to is free'd by this function.
230 * if you want to keep it, save it yourself
233 verbatim(string str, int bracketsonly)
238 if (strlen(str) == pure_text_length(str,0)) {
239 /* No environments, so consider the fast-and-easy methods */
241 if (not_contains(str,allbracket_set)) {
242 temp = string_Copy(str);
247 if (not_contains(str,abracket_set)) {
248 temp=(char *) malloc((len=strlen(str))+4);
251 (void) memcpy(temp+2,str,len);
257 if (not_contains(str,sbracket_set)) {
258 temp=(char *) malloc((len=strlen(str))+4);
261 (void) memcpy(temp+2,str,len);
267 if (not_contains(str,cbracket_set)) {
268 temp=(char *) malloc((len=strlen(str))+4);
271 (void) memcpy(temp+2,str,len);
277 if (not_contains(str,paren_set)) {
278 temp=(char *) malloc((len=strlen(str))+4);
281 (void) memcpy(temp+2,str,len);
289 temp=lbreak(&str,bracketsonly?allbracket_set:allmaskable_set);
291 bracketnum=(int) (strchr(brackets,str[0])-brackets);
292 temp=string_Concat2(temp,openbracket[bracketnum]);
293 temp=string_Concat2(temp,temp2=lany(&str," "));
295 temp=string_Concat2(temp,closebracket[bracketnum]);
296 temp=string_Concat2(temp,temp2=lbreak(&str,bracketsonly?
297 allbracket_set:allmaskable_set));
300 free(str); /* str is "" at this point, anyway */
305 /* text points to beginning of text string. return value is
306 length of string, up to but not including the passed terminator
307 or the default terminator \0. The text will not be modified,
308 and @@ will be counted twice */
313 string temp,temp2,temp3;
318 temp = string_Copy("");
320 chs = char_stack_create();
323 tos = (char_stack_empty(chs)?0:char_stack_top(chs));
326 /* if the character is the next terminator */
328 temp = (char *) realloc(temp,++templen);
329 temp[templen-2] = *str++;
331 temp[templen-1] = '\0';
332 } else if ((len = pure_text_length(str,tos))) {
334 /* if the block is text in an environment, just copy it */
336 temp2 = string_CreateFromData(str,len);
338 temp = string_Concat2(temp,temp2);
342 /* if the block is top level text, verbatim brackets only
343 (not @'s) and add text to temp */
345 temp2 = string_CreateFromData(str,len);
347 temp3 = verbatim(temp2,1);
348 temp = string_Concat2(temp,temp3);
349 templen += strlen(temp3);
353 /* if the block is an environment, copy it, push delimiter */
355 len = env_length(str+1);
356 char_stack_push(chs,otherside(str[len+1]));
358 temp2 = string_CreateFromData(str,len);
360 temp = string_Concat2(temp,temp2);
365 /* all blocks have been copied. */
367 while (!char_stack_empty(chs)) {
368 temp = (char *) realloc(temp,++templen);
369 temp[templen-2] = char_stack_top(chs);
372 temp[templen-1] = '\0';
377 /* str points to a string. return value is another string
378 which is the original with all styles removed. */
380 stylestrip(string str)
382 int templen = 0, otherchar;
383 char *temp = (char *) malloc(string_Length(str) + 1);
387 chs = char_stack_create();
391 int len = env_length(str + 1);
394 if ((len == 4 && !strncasecmp(str + 1, "font", 4))
395 || (len == 5 && !strncasecmp(str + 1, "color", 5)))
397 otherchar |= otherside(str[len + 1]);
398 char_stack_push(chs, otherchar);
403 if (!char_stack_empty(chs) && *str == (char_stack_top(chs) & 0x7f)) {
408 if (!char_stack_empty(chs) && (char_stack_top(chs) & 0x80))
411 temp[templen++] = *str++;
415 while (!char_stack_empty(chs))
423 free_desc(desctype *desc)
427 while (desc->code != DT_EOF) {
428 next_desc = desc->next;
435 /* text points to beginning of possible env name. return value is
436 length of env name, not including @ or opener, or -1 if not a
437 possible env name. */
439 env_length(char *text)
443 while (*text && (isalnum(*text) || *text=='_')) {
448 if ((*text=='(') || (*text=='{') || (*text=='[') || (*text=='<'))
454 /* text points to beginning of text string. return value is
455 length of string, up to but not including the passed terminator
456 or the default terminators \0 \n @. This can modify text, and 0
457 is a valid return value. */
459 text_length(char *text,
465 while (*text!='@' && *text!='\n' && *text!=terminator && *text) {
474 (void) memmove(text+1,text+2,strlen(text+1));
475 else if (env_length(text+1) != -1)
483 /* parses str into a desc linked list. Returns number of strings and
484 newlines in *pstr and *pnl */
487 disp_get_cmds(char *str,
491 desctype *desc,*here;
493 char_stack terminators = char_stack_create();
498 desc=(desctype *) malloc(sizeof(desctype));
508 } else if (*curstr==terminator) { /* if this is the end of an env */
510 terminator = char_stack_top(terminators);
511 char_stack_pop(terminators);
513 } else if ((len=text_length(curstr, terminator))) { /* if there is a text
520 } else if (*curstr=='@') { /* if this is the beginning of an env */
521 len=env_length(curstr+1);
525 char_stack_push(terminators, terminator);
526 terminator=otherside(*(curstr+1+len));
527 curstr+=(len+2); /* jump over @, env name, and opener */
530 here->next=(desctype *) malloc(sizeof(desctype));
534 while (!char_stack_empty(terminators)) {
536 terminator = char_stack_top(terminators);
537 char_stack_pop(terminators);
538 here->next=(desctype *) malloc(sizeof(desctype));
545 #ifdef DEBUG_PRINTOUT
548 while (here->code != DT_EOF) {
549 if (here->code == DT_STR || here->code == DT_ENV) {
550 temp = string_CreateFromData(here->str, here->len);
551 printf("[%d <%s>]\n", here->code, temp);
554 printf("[%d]\n", here->code);