]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/zwgc/formatter.c
r4275@bucket (orig r265): kcr | 2008-01-21 02:57:32 -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[(int)*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    return 0;
227 }
228
229 /* the char * that str points to is free'd by this function.
230  * if you want to keep it, save it yourself
231  */
232 string
233 verbatim(string str, int bracketsonly)
234 {
235    char *temp,*temp2;
236    int bracketnum,len;
237
238    if (strlen(str) == pure_text_length(str,0)) {
239       /* No environments, so consider the fast-and-easy methods */
240
241       if (not_contains(str,allbracket_set)) {
242          temp = string_Copy(str);
243          free(str);
244          return(temp);
245       }
246
247       if (not_contains(str,abracket_set)) {
248          temp=(char *) malloc((len=strlen(str))+4);
249          temp[0]='@';
250          temp[1]='<';
251          (void) memcpy(temp+2,str,len);
252          temp[len+2]='>';
253          temp[len+3]='\0';
254          free(str);
255          return(temp);
256       }
257       if (not_contains(str,sbracket_set)) {
258          temp=(char *) malloc((len=strlen(str))+4);
259          temp[0]='@';
260          temp[1]='[';
261          (void) memcpy(temp+2,str,len);
262          temp[len+2]=']';
263          temp[len+3]='\0';
264          free(str);
265          return(temp);
266       }
267       if (not_contains(str,cbracket_set)) {
268          temp=(char *) malloc((len=strlen(str))+4);
269          temp[0]='@';
270          temp[1]='{';
271          (void) memcpy(temp+2,str,len);
272          temp[len+2]='}';
273          temp[len+3]='\0';
274          free(str);
275          return(temp);
276       }
277       if (not_contains(str,paren_set)) {
278          temp=(char *) malloc((len=strlen(str))+4);
279          temp[0]='@';
280          temp[1]='(';
281          (void) memcpy(temp+2,str,len);
282          temp[len+2]=')';
283          temp[len+3]='\0';
284          free(str);
285          return(temp);
286       }
287    }
288
289    temp=lbreak(&str,bracketsonly?allbracket_set:allmaskable_set);
290    while(*str) {
291       bracketnum=(int) (strchr(brackets,str[0])-brackets);
292       temp=string_Concat2(temp,openbracket[bracketnum]);
293       temp=string_Concat2(temp,temp2=lany(&str," "));
294       free(temp2);
295       temp=string_Concat2(temp,closebracket[bracketnum]);
296       temp=string_Concat2(temp,temp2=lbreak(&str,bracketsonly?
297                                           allbracket_set:allmaskable_set));
298       free(temp2);
299    }
300    free(str);  /* str is "" at this point, anyway */
301
302    return(temp);
303 }
304
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 */
309
310 string
311 protect(string str)
312 {
313    string temp,temp2,temp3;
314    int len,templen;
315    char_stack chs;
316    char tos;
317
318    temp = string_Copy("");
319    templen = 1;
320    chs = char_stack_create();
321
322    while(*str) {
323       tos = (char_stack_empty(chs)?0:char_stack_top(chs));
324
325       if (*str == tos) {
326          /* if the character is the next terminator */
327
328          temp = (char *) realloc(temp,++templen);
329          temp[templen-2] = *str++;
330          char_stack_pop(chs);
331          temp[templen-1] = '\0';
332       } else if ((len = pure_text_length(str,tos))) {
333          if (tos) {
334             /* if the block is text in an environment, just copy it */
335
336             temp2 = string_CreateFromData(str,len);
337             str += len;
338             temp = string_Concat2(temp,temp2);
339             templen += len;
340             free(temp2);
341          } else {
342             /* if the block is top level text, verbatim brackets only
343                (not @'s) and add text to temp */
344
345             temp2 = string_CreateFromData(str,len);
346             str += len;
347             temp3 = verbatim(temp2,1);
348             temp = string_Concat2(temp,temp3);
349             templen += strlen(temp3);
350             free(temp3);
351          }
352       } else {
353          /* if the block is an environment, copy it, push delimiter */
354
355          len = env_length(str+1);
356          char_stack_push(chs,otherside(str[len+1]));
357          len += 2;
358          temp2 = string_CreateFromData(str,len);
359          str += len;
360          temp = string_Concat2(temp,temp2);
361          templen += len;
362          free(temp2);
363       }
364    }
365    /* all blocks have been copied. */
366
367    while (!char_stack_empty(chs)) {
368       temp = (char *) realloc(temp,++templen);
369       temp[templen-2] = char_stack_top(chs);
370       char_stack_pop(chs);
371    }
372    temp[templen-1] = '\0';
373
374    return(temp);
375 }
376
377 /* str points to a string.  return value is another string
378    which is the original with all styles removed. */
379 string
380 stylestrip(string str)
381 {
382     int templen = 0, otherchar;
383     char *temp = (char *) malloc(string_Length(str) + 1);
384     char_stack chs;
385     string ostr = str;
386
387     chs = char_stack_create();
388
389     while (*str) {
390         if (*str == '@') {
391             int len = env_length(str + 1);
392             if (len != -1) {
393                 otherchar = 0;
394                 if ((len == 4 && !strncasecmp(str + 1, "font", 4))
395                   || (len == 5 && !strncasecmp(str + 1, "color", 5)))
396                     otherchar = 0x80;
397                 otherchar |= otherside(str[len + 1]);
398                 char_stack_push(chs, otherchar);
399                 str += len + 2;
400                 continue;
401             }
402         }
403         if (!char_stack_empty(chs) && *str == (char_stack_top(chs) & 0x7f)) {
404             char_stack_pop(chs);
405             str++;
406             continue;
407         }
408         if (!char_stack_empty(chs) && (char_stack_top(chs) & 0x80))
409             str++;
410         else
411             temp[templen++] = *str++;
412     }
413     temp[templen] = 0;
414
415     while (!char_stack_empty(chs))
416         char_stack_pop(chs);
417     free(ostr);
418
419     return(temp);
420 }
421
422 void
423 free_desc(desctype *desc)
424 {
425     desctype *next_desc;
426
427     while (desc->code != DT_EOF) {
428         next_desc = desc->next;
429         free(desc);
430         desc = next_desc;
431     }
432     free(desc);
433 }
434
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. */
438 static int
439 env_length(char *text)
440 {
441    int len=0;
442
443    while (*text && (isalnum(*text) || *text=='_')) {
444       text++;
445       len++;
446    }
447
448    if ((*text=='(') || (*text=='{') || (*text=='[') || (*text=='<'))
449      return(len);
450    else
451      return(-1);
452 }
453
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. */
458 static int
459 text_length(char *text,
460             char terminator)
461 {
462    int len=0;
463
464    while (1) {
465       while (*text!='@' && *text!='\n' && *text!=terminator && *text) {
466          text++;
467          len++;
468       }
469
470       if (*text!='@')
471          return(len);
472
473       if (*(text+1)=='@')
474          (void) memmove(text+1,text+2,strlen(text+1));
475       else if (env_length(text+1) != -1)
476         return(len);
477
478       text++;
479       len++;
480    }
481 }
482
483 /* parses str into a desc linked list.  Returns number of strings and
484    newlines in *pstr and *pnl */
485
486 desctype *
487 disp_get_cmds(char *str,
488               int *pstr,
489               int *pnl)
490 {
491    desctype *desc,*here;
492    int len;
493    char_stack terminators = char_stack_create();
494    char terminator;
495    int nstr=0, nnl=0;
496    char *curstr;
497
498    desc=(desctype *) malloc(sizeof(desctype));
499    here=desc;
500    curstr=str;
501    terminator = '\0';
502
503    while (*curstr) {
504       if (*curstr=='\n') {
505          here->code=DT_NL;
506          curstr++;
507          nnl++;
508       } else if (*curstr==terminator) { /* if this is the end of an env */
509          here->code=DT_END;
510          terminator = char_stack_top(terminators);
511          char_stack_pop(terminators);
512          curstr++;
513       } else if ((len=text_length(curstr, terminator))) { /* if there is a text
514                                                              block here */
515          here->code=DT_STR;
516          here->str=curstr;
517          here->len=len;
518          curstr+=len;
519          nstr++;
520       } else if (*curstr=='@') { /* if this is the beginning of an env */
521          len=env_length(curstr+1);
522          here->code=DT_ENV;
523          here->str=curstr+1;
524          here->len=len;
525          char_stack_push(terminators, terminator);
526          terminator=otherside(*(curstr+1+len));
527          curstr+=(len+2); /* jump over @, env name, and opener */
528       }
529
530       here->next=(desctype *) malloc(sizeof(desctype));
531       here=here->next;
532    }
533
534    while (!char_stack_empty(terminators)) {
535       here->code=DT_END;
536       terminator = char_stack_top(terminators);
537       char_stack_pop(terminators);
538       here->next=(desctype *) malloc(sizeof(desctype));
539       here=here->next;
540    }
541    here->code=DT_EOF;
542    *pstr=nstr;
543    *pnl=nnl;
544
545 #ifdef DEBUG_PRINTOUT
546    { string temp;
547        here = desc;
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);
552                free(temp);
553            } else
554              printf("[%d]\n", here->code);
555            here=here->next;
556        }
557  }
558 #endif
559
560    return(desc);
561 }