]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/zwgc/formatter.c
r4264@bucket (orig r254): kcr | 2008-01-20 22:11:44 -0500
[1ts-debian.git] / zephyr / zwgc / formatter.c
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
3  * client.
4  *
5  *      Created by:     Marc Horowitz <marc@athena.mit.edu>
6  *
7  *      $Id$
8  *
9  *      Copyright (c) 1989 by the Massachusetts Institute of Technology.
10  *      For copying and distribution information, see the file
11  *      "mit-copyright.h".
12  */
13
14 #include <sysdep.h>
15
16 #if (!defined(lint) && !defined(SABER))
17 static const char rcsid_formatter_c[] = "$Id$";
18 #endif
19
20 #include <zephyr/mit-copyright.h>
21 #include <zephyr/zephyr.h>
22
23 #include "new_memory.h"
24 #include "char_stack.h"
25 #include "string_dictionary.h"
26 #include "formatter.h"
27 #include "text_operations.h"
28
29 #if !defined(__STDC__) && !defined(const)
30 #define const
31 #endif
32
33 static int pure_text_length(char *, char);
34 static int env_length(char *);
35
36 #ifdef notdef
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
54   };
55 #endif
56
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
74   };
75
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
93   };
94
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
112   };
113
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
131   };
132
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
150   };
151
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
169   };
170
171 static char brackets[]="()<>[]{}@";
172 static char *openbracket[]={"@<","@<","@[","@[","@{","@{","@(","@(","@("};
173 static char *closebracket[]={">",">","]","]","}","}",")",")",")"};
174
175 static int
176 not_contains(string str,
177              const character_class set)
178 {
179    while (*str && ! set[*str]) str++;
180    return (! *str);
181 }
182
183 static int
184 pure_text_length(char *text,
185                  char terminator)
186 {
187    int len=0;
188
189    while (1) {
190       while (*text!='@' && *text!=terminator && *text) {
191          text++;
192          len++;
193       }
194
195       if (*text!='@')
196          return(len);
197
198       if (*(text+1)=='@') {
199          text++;
200          len++;
201       } else if (env_length(text+1) != -1)
202         return(len);
203
204       text++;
205       len++;
206    }
207 }
208
209 static char
210 otherside(char opener)
211 {
212    switch (opener) {
213     case '(':
214       return(')');
215     case '{':
216       return('}');
217     case '[':
218       return(']');
219     case '<':
220       return('>');
221    }
222
223 #ifdef DEBUG
224    abort();
225 #endif
226 }
227
228 /* the char * that str points to is free'd by this function.
229  * if you want to keep it, save it yourself
230  */
231 string
232 verbatim(string str, int bracketsonly)
233 {
234    char *temp,*temp2;
235    int bracketnum,len;
236
237    if (strlen(str) == pure_text_length(str,0)) {
238       /* No environments, so consider the fast-and-easy methods */
239
240       if (not_contains(str,allbracket_set)) {
241          temp = string_Copy(str);
242          free(str);
243          return(temp);
244       }
245
246       if (not_contains(str,abracket_set)) {
247          temp=(char *) malloc((len=strlen(str))+4);
248          temp[0]='@';
249          temp[1]='<';
250          (void) memcpy(temp+2,str,len);
251          temp[len+2]='>';
252          temp[len+3]='\0';
253          free(str);
254          return(temp);
255       }
256       if (not_contains(str,sbracket_set)) {
257          temp=(char *) malloc((len=strlen(str))+4);
258          temp[0]='@';
259          temp[1]='[';
260          (void) memcpy(temp+2,str,len);
261          temp[len+2]=']';
262          temp[len+3]='\0';
263          free(str);
264          return(temp);
265       }
266       if (not_contains(str,cbracket_set)) {
267          temp=(char *) malloc((len=strlen(str))+4);
268          temp[0]='@';
269          temp[1]='{';
270          (void) memcpy(temp+2,str,len);
271          temp[len+2]='}';
272          temp[len+3]='\0';
273          free(str);
274          return(temp);
275       }
276       if (not_contains(str,paren_set)) {
277          temp=(char *) malloc((len=strlen(str))+4);
278          temp[0]='@';
279          temp[1]='(';
280          (void) memcpy(temp+2,str,len);
281          temp[len+2]=')';
282          temp[len+3]='\0';
283          free(str);
284          return(temp);
285       }
286    }
287
288    temp=lbreak(&str,bracketsonly?allbracket_set:allmaskable_set);
289    while(*str) {
290       bracketnum=(int) (strchr(brackets,str[0])-brackets);
291       temp=string_Concat2(temp,openbracket[bracketnum]);
292       temp=string_Concat2(temp,temp2=lany(&str," "));
293       free(temp2);
294       temp=string_Concat2(temp,closebracket[bracketnum]);
295       temp=string_Concat2(temp,temp2=lbreak(&str,bracketsonly?
296                                           allbracket_set:allmaskable_set));
297       free(temp2);
298    }
299    free(str);  /* str is "" at this point, anyway */
300
301    return(temp);
302 }
303
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 */
308
309 string
310 protect(string str)
311 {
312    string temp,temp2,temp3;
313    int len,templen;
314    char_stack chs;
315    char tos;
316
317    temp = string_Copy("");
318    templen = 1;
319    chs = char_stack_create();
320
321    while(*str) {
322       tos = (char_stack_empty(chs)?0:char_stack_top(chs));
323
324       if (*str == tos) {
325          /* if the character is the next terminator */
326
327          temp = (char *) realloc(temp,++templen);
328          temp[templen-2] = *str++;
329          char_stack_pop(chs);
330          temp[templen-1] = '\0';
331       } else if (len = pure_text_length(str,tos)) {
332          if (tos) {
333             /* if the block is text in an environment, just copy it */
334
335             temp2 = string_CreateFromData(str,len);
336             str += len;
337             temp = string_Concat2(temp,temp2);
338             templen += len;
339             free(temp2);
340          } else {
341             /* if the block is top level text, verbatim brackets only
342                (not @'s) and add text to temp */
343
344             temp2 = string_CreateFromData(str,len);
345             str += len;
346             temp3 = verbatim(temp2,1);
347             temp = string_Concat2(temp,temp3);
348             templen += strlen(temp3);
349             free(temp3);
350          }
351       } else {
352          /* if the block is an environment, copy it, push delimiter */
353
354          len = env_length(str+1);
355          char_stack_push(chs,otherside(str[len+1]));
356          len += 2;
357          temp2 = string_CreateFromData(str,len);
358          str += len;
359          temp = string_Concat2(temp,temp2);
360          templen += len;
361          free(temp2);
362       }
363    }
364    /* all blocks have been copied. */
365
366    while (!char_stack_empty(chs)) {
367       temp = (char *) realloc(temp,++templen);
368       temp[templen-2] = char_stack_top(chs);
369       char_stack_pop(chs);
370    }
371    temp[templen-1] = '\0';
372
373    return(temp);
374 }
375
376 /* str points to a string.  return value is another string
377    which is the original with all styles removed. */
378 string
379 stylestrip(string str)
380 {
381     int templen = 0, otherchar;
382     char *temp = (char *) malloc(string_Length(str) + 1);
383     char_stack chs;
384     string ostr = str;
385
386     chs = char_stack_create();
387
388     while (*str) {
389         if (*str == '@') {
390             int len = env_length(str + 1);
391             if (len != -1) {
392                 otherchar = 0;
393                 if ((len == 4 && !strncasecmp(str + 1, "font", 4))
394                   || (len == 5 && !strncasecmp(str + 1, "color", 5)))
395                     otherchar = 0x80;
396                 otherchar |= otherside(str[len + 1]);
397                 char_stack_push(chs, otherchar);
398                 str += len + 2;
399                 continue;
400             }
401         }
402         if (!char_stack_empty(chs) && *str == (char_stack_top(chs) & 0x7f)) {
403             char_stack_pop(chs);
404             str++;
405             continue;
406         }
407         if (!char_stack_empty(chs) && (char_stack_top(chs) & 0x80))
408             str++;
409         else
410             temp[templen++] = *str++;
411     }
412     temp[templen] = 0;
413
414     while (!char_stack_empty(chs))
415         char_stack_pop(chs);
416     free(ostr);
417
418     return(temp);
419 }
420
421 void
422 free_desc(desctype *desc)
423 {
424     desctype *next_desc;
425
426     while (desc->code != DT_EOF) {
427         next_desc = desc->next;
428         free(desc);
429         desc = next_desc;
430     }
431     free(desc);
432 }
433
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
438 env_length(char *text)
439 {
440    int len=0;
441
442    while (*text && (isalnum(*text) || *text=='_')) {
443       text++;
444       len++;
445    }
446
447    if ((*text=='(') || (*text=='{') || (*text=='[') || (*text=='<'))
448      return(len);
449    else
450      return(-1);
451 }
452
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
458 text_length(char *text,
459             char terminator)
460 {
461    int len=0;
462
463    while (1) {
464       while (*text!='@' && *text!='\n' && *text!=terminator && *text) {
465          text++;
466          len++;
467       }
468
469       if (*text!='@')
470          return(len);
471
472       if (*(text+1)=='@')
473          (void) memmove(text+1,text+2,strlen(text+1));
474       else if (env_length(text+1) != -1)
475         return(len);
476
477       text++;
478       len++;
479    }
480 }
481
482 /* parses str into a desc linked list.  Returns number of strings and
483    newlines in *pstr and *pnl */
484
485 desctype *
486 disp_get_cmds(char *str,
487               int *pstr,
488               int *pnl)
489 {
490    desctype *desc,*here;
491    int len;
492    char_stack terminators = char_stack_create();
493    char terminator;
494    int nstr=0, nnl=0;
495    char *curstr;
496
497    desc=(desctype *) malloc(sizeof(desctype));
498    here=desc;
499    curstr=str;
500    terminator = '\0';
501
502    while (*curstr) {
503       if (*curstr=='\n') {
504          here->code=DT_NL;
505          curstr++;
506          nnl++;
507       } else if (*curstr==terminator) { /* if this is the end of an env */
508          here->code=DT_END;
509          terminator = char_stack_top(terminators);
510          char_stack_pop(terminators);
511          curstr++;
512       } else if (len=text_length(curstr,terminator)) { /* if there is a text
513                                                           block here */
514          here->code=DT_STR;
515          here->str=curstr;
516          here->len=len;
517          curstr+=len;
518          nstr++;
519       } else if (*curstr=='@') { /* if this is the beginning of an env */
520          len=env_length(curstr+1);
521          here->code=DT_ENV;
522          here->str=curstr+1;
523          here->len=len;
524          char_stack_push(terminators, terminator);
525          terminator=otherside(*(curstr+1+len));
526          curstr+=(len+2); /* jump over @, env name, and opener */
527       }
528
529       here->next=(desctype *) malloc(sizeof(desctype));
530       here=here->next;
531    }
532
533    while (!char_stack_empty(terminators)) {
534       here->code=DT_END;
535       terminator = char_stack_top(terminators);
536       char_stack_pop(terminators);
537       here->next=(desctype *) malloc(sizeof(desctype));
538       here=here->next;
539    }
540    here->code=DT_EOF;
541    *pstr=nstr;
542    *pnl=nnl;
543
544 #ifdef DEBUG_PRINTOUT
545    { string temp;
546        here = desc;
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);
551                free(temp);
552            } else
553              printf("[%d]\n", here->code);
554            here=here->next;
555        }
556  }
557 #endif
558
559    return(desc);
560 }