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(), env_length();
36 static character_class atsign_set = { /* '@' = 0x40 */
37 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
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 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
42 0,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
56 static const character_class paren_set = { /* '(' = 0x28, ')' = 0x29 */
57 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
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,1,1,0,0,0,0,0,0,
60 0,0,0,0,0,0,0,0,0,0,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
75 static const character_class sbracket_set = { /* '[' = 0x5b, ']' = 0x5d */
76 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
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,1,0,1,0,0,
82 0,0,0,0,0,0,0,0,0,0,0,0,0,0,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
94 static const character_class abracket_set = { /* '<' = 0x3c, '>' = 0x3e */
95 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
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,1,0,1,0,
99 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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
113 static const character_class cbracket_set = { /* '{' = 0x7b, '}' = 0x7d */
114 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
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,1,0,1,0,0,
122 0,0,0,0,0,0,0,0,0,0,0,0,0,0,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
132 static const character_class allbracket_set = {
133 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
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,1,1,0,0,0,0,0,0,
136 0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,
137 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
138 0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,
139 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
140 0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,
141 0,0,0,0,0,0,0,0,0,0,0,0,0,0,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
151 static const character_class allmaskable_set = {
152 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
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,1,1,0,0,0,0,0,0,
155 0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,
156 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
157 0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,
158 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
159 0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,
160 0,0,0,0,0,0,0,0,0,0,0,0,0,0,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
170 static char brackets[]="()<>[]{}@";
171 static char *openbracket[]={"@<","@<","@[","@[","@{","@{","@(","@(","@("};
172 static char *closebracket[]={">",">","]","]","}","}",")",")",")"};
174 static int not_contains(str, set)
176 const character_class set;
178 while (*str && ! set[*str]) str++;
182 static int pure_text_length(text,terminator)
189 while (*text!='@' && *text!=terminator && *text) {
197 if (*(text+1)=='@') {
200 } else if (env_length(text+1) != -1)
208 static char otherside(opener)
227 /* the char * that str points to is free'd by this function.
228 * if you want to keep it, save it yourself
230 string verbatim(str, 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. */
378 string stylestrip(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))
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. */
437 static int env_length(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. */
457 static int text_length(text,terminator)
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 */
485 desctype *disp_get_cmds(str,pstr,pnl)
489 desctype *desc,*here;
491 char_stack terminators = char_stack_create();
496 desc=(desctype *) malloc(sizeof(desctype));
506 } else if (*curstr==terminator) { /* if this is the end of an env */
508 terminator = char_stack_top(terminators);
509 char_stack_pop(terminators);
511 } else if (len=text_length(curstr,terminator)) { /* if there is a text
518 } else if (*curstr=='@') { /* if this is the beginning of an env */
519 len=env_length(curstr+1);
523 char_stack_push(terminators, terminator);
524 terminator=otherside(*(curstr+1+len));
525 curstr+=(len+2); /* jump over @, env name, and opener */
528 here->next=(desctype *) malloc(sizeof(desctype));
532 while (!char_stack_empty(terminators)) {
534 terminator = char_stack_top(terminators);
535 char_stack_pop(terminators);
536 here->next=(desctype *) malloc(sizeof(desctype));
543 #ifdef DEBUG_PRINTOUT
546 while (here->code != DT_EOF) {
547 if (here->code == DT_STR || here->code == DT_ENV) {
548 temp = string_CreateFromData(here->str, here->len);
549 printf("[%d <%s>]\n", here->code, temp);
552 printf("[%d]\n", here->code);