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[*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)
228 /* the char * that str points to is free'd by this function.
229 * if you want to keep it, save it yourself
232 verbatim(string str, int bracketsonly)
237 if (strlen(str) == pure_text_length(str,0)) {
238 /* No environments, so consider the fast-and-easy methods */
240 if (not_contains(str,allbracket_set)) {
241 temp = string_Copy(str);
246 if (not_contains(str,abracket_set)) {
247 temp=(char *) malloc((len=strlen(str))+4);
250 (void) memcpy(temp+2,str,len);
256 if (not_contains(str,sbracket_set)) {
257 temp=(char *) malloc((len=strlen(str))+4);
260 (void) memcpy(temp+2,str,len);
266 if (not_contains(str,cbracket_set)) {
267 temp=(char *) malloc((len=strlen(str))+4);
270 (void) memcpy(temp+2,str,len);
276 if (not_contains(str,paren_set)) {
277 temp=(char *) malloc((len=strlen(str))+4);
280 (void) memcpy(temp+2,str,len);
288 temp=lbreak(&str,bracketsonly?allbracket_set:allmaskable_set);
290 bracketnum=(int) (strchr(brackets,str[0])-brackets);
291 temp=string_Concat2(temp,openbracket[bracketnum]);
292 temp=string_Concat2(temp,temp2=lany(&str," "));
294 temp=string_Concat2(temp,closebracket[bracketnum]);
295 temp=string_Concat2(temp,temp2=lbreak(&str,bracketsonly?
296 allbracket_set:allmaskable_set));
299 free(str); /* str is "" at this point, anyway */
304 /* text points to beginning of text string. return value is
305 length of string, up to but not including the passed terminator
306 or the default terminator \0. The text will not be modified,
307 and @@ will be counted twice */
312 string temp,temp2,temp3;
317 temp = string_Copy("");
319 chs = char_stack_create();
322 tos = (char_stack_empty(chs)?0:char_stack_top(chs));
325 /* if the character is the next terminator */
327 temp = (char *) realloc(temp,++templen);
328 temp[templen-2] = *str++;
330 temp[templen-1] = '\0';
331 } else if (len = pure_text_length(str,tos)) {
333 /* if the block is text in an environment, just copy it */
335 temp2 = string_CreateFromData(str,len);
337 temp = string_Concat2(temp,temp2);
341 /* if the block is top level text, verbatim brackets only
342 (not @'s) and add text to temp */
344 temp2 = string_CreateFromData(str,len);
346 temp3 = verbatim(temp2,1);
347 temp = string_Concat2(temp,temp3);
348 templen += strlen(temp3);
352 /* if the block is an environment, copy it, push delimiter */
354 len = env_length(str+1);
355 char_stack_push(chs,otherside(str[len+1]));
357 temp2 = string_CreateFromData(str,len);
359 temp = string_Concat2(temp,temp2);
364 /* all blocks have been copied. */
366 while (!char_stack_empty(chs)) {
367 temp = (char *) realloc(temp,++templen);
368 temp[templen-2] = char_stack_top(chs);
371 temp[templen-1] = '\0';
376 /* str points to a string. return value is another string
377 which is the original with all styles removed. */
379 stylestrip(string str)
381 int templen = 0, otherchar;
382 char *temp = (char *) malloc(string_Length(str) + 1);
386 chs = char_stack_create();
390 int len = env_length(str + 1);
393 if ((len == 4 && !strncasecmp(str + 1, "font", 4))
394 || (len == 5 && !strncasecmp(str + 1, "color", 5)))
396 otherchar |= otherside(str[len + 1]);
397 char_stack_push(chs, otherchar);
402 if (!char_stack_empty(chs) && *str == (char_stack_top(chs) & 0x7f)) {
407 if (!char_stack_empty(chs) && (char_stack_top(chs) & 0x80))
410 temp[templen++] = *str++;
414 while (!char_stack_empty(chs))
422 free_desc(desctype *desc)
426 while (desc->code != DT_EOF) {
427 next_desc = desc->next;
434 /* text points to beginning of possible env name. return value is
435 length of env name, not including @ or opener, or -1 if not a
436 possible env name. */
438 env_length(char *text)
442 while (*text && (isalnum(*text) || *text=='_')) {
447 if ((*text=='(') || (*text=='{') || (*text=='[') || (*text=='<'))
453 /* text points to beginning of text string. return value is
454 length of string, up to but not including the passed terminator
455 or the default terminators \0 \n @. This can modify text, and 0
456 is a valid return value. */
458 text_length(char *text,
464 while (*text!='@' && *text!='\n' && *text!=terminator && *text) {
473 (void) memmove(text+1,text+2,strlen(text+1));
474 else if (env_length(text+1) != -1)
482 /* parses str into a desc linked list. Returns number of strings and
483 newlines in *pstr and *pnl */
486 disp_get_cmds(char *str,
490 desctype *desc,*here;
492 char_stack terminators = char_stack_create();
497 desc=(desctype *) malloc(sizeof(desctype));
507 } else if (*curstr==terminator) { /* if this is the end of an env */
509 terminator = char_stack_top(terminators);
510 char_stack_pop(terminators);
512 } else if (len=text_length(curstr,terminator)) { /* if there is a text
519 } else if (*curstr=='@') { /* if this is the beginning of an env */
520 len=env_length(curstr+1);
524 char_stack_push(terminators, terminator);
525 terminator=otherside(*(curstr+1+len));
526 curstr+=(len+2); /* jump over @, env name, and opener */
529 here->next=(desctype *) malloc(sizeof(desctype));
533 while (!char_stack_empty(terminators)) {
535 terminator = char_stack_top(terminators);
536 char_stack_pop(terminators);
537 here->next=(desctype *) malloc(sizeof(desctype));
544 #ifdef DEBUG_PRINTOUT
547 while (here->code != DT_EOF) {
548 if (here->code == DT_STR || here->code == DT_ENV) {
549 temp = string_CreateFromData(here->str, here->len);
550 printf("[%d <%s>]\n", here->code, temp);
553 printf("[%d]\n", here->code);