]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/zwgc/tty_filter.c
pull up to latest athena current
[1ts-debian.git] / zephyr / zwgc / tty_filter.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: tty_filter.c,v 1.21 2002/04/18 18:28:17 rbasch Exp $
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_tty_filter_c[] = "$Id: tty_filter.c,v 1.21 2002/04/18 18:28:17 rbasch Exp $";
18 #endif
19
20 #include <zephyr/mit-copyright.h>
21
22 /****************************************************************************/
23 /*                                                                          */
24 /*                         The tty & plain filters:                         */
25 /*                                                                          */
26 /****************************************************************************/
27
28 #include "new_memory.h"
29 #include "new_string.h"
30 #include "string_dictionary_aux.h"
31 #include "formatter.h"
32 #include "zwgc.h"
33 #include "error.h"
34
35 /***************************************************************************/
36
37 extern int tgetent();
38 extern char *tgetstr(),*getenv();
39 #if 0
40 short ospeed;
41 char PC;
42 #endif
43
44 /* Dictionary naming convention:
45
46    B.xxx is the termcap sequence to begin environment xxx.
47    E.xxx is the termcap sequence to end environment xxx.
48
49    */
50
51 static string_dictionary termcap_dict;
52 static char code_buf[10240], *code_buf_pos = code_buf, *code;
53
54 /* Define the following commands:
55
56    (Hopefully) shared with all devices:
57    
58    @center      Guess.
59    
60    @em          Emphasis.  User underline if available, else reverse video.
61    @bold        Bold letters.  If not available, reverse video, else underline.
62    @beep        "bl" termcap entry, else "^G"
63
64    Other:
65
66    @blink       "mb"/"me" termcap entry, else nothing.
67    @rv          "so"/"se" termcap entry.
68    @u           "us"/"ue" termcap entry.
69  */
70
71 #define TD_SET(k,v) (string_dictionary_Define(termcap_dict, (k), &ex)->value \
72                      = (v))
73 #define EXPAND(k) (code = code_buf_pos, tputs(tmp, 1, tty_outc), \
74                    *code_buf_pos++ = 0, TD_SET(k, code))
75
76 static int
77 tty_outc(c)
78     int c;
79 {
80     *code_buf_pos++ = c;
81     return 0;
82 }
83
84 /* ARGSUSED */
85 int tty_filter_init(drivername, notfirst, pargc, argv)
86 char *drivername;
87 char notfirst;
88 int *pargc;
89 char **argv;
90 {
91     static char st_buf[128];
92     char tc_buf[1024], *p = st_buf, *tmp, *term;
93     int ex;
94     string_dictionary_binding *b;
95     int isrealtty = string_Eq(drivername, "tty");
96 #if 0
97 #ifdef HAVE_TERMIOS_H
98     struct termios tbuf;
99
100     ospeed = (tcgetattr(STDIN_FILENO, &tbuf) == 0) ? cfgetospeed(&tbuf) : 2400;
101 #else
102     struct sgttyb sgttyb;
103
104     ospeed = (ioctl(0, TIOCGETP, &sgttyb) == 0) ? sgttyb.sg_ospeed : 2400;
105 #endif
106 #endif
107
108     if (termcap_dict == (string_dictionary) NULL)
109       termcap_dict = string_dictionary_Create(7);
110
111     if (!(term = getenv("TERM"))) {     /* Only use termcap if $TERM.   */
112         if (isrealtty && !notfirst)
113             /* only complain if initializing tty mode, and would be first
114                available port */
115             ERROR("$TERM not set.  tty mode will be plain.\n");
116     }
117 #ifdef _AIX
118     /* 
119      * This is a temporary KLUDGE to get around the problem where some people
120      * might start zwgc in their ~/.startup.X and it hangs on the RISC/6000.
121      * Apparently, the call to tgetent() with the Athena console window causes
122      * the process to get stopped on tty access.  Since the terminal type is
123      * "dumb" (set by tcsh), we can pretty much assume there isn't anything
124      * to setup from the termcap information.
125      */
126     else if (!strcmp(term, "dumb")) { }
127 #endif
128     else {
129         tgetent(tc_buf, term);
130     
131         /* Step 1: get all of {rv,bold,u,bell,blink} that are available. */
132
133         /* We cheat here, and ignore the padding (if any) specified for
134            the mode-change strings (it's a real pain to do "right") */
135
136 #if 0
137         tmp = tgetstr("pc", &p);
138         PC = (tmp) ? *tmp : 0;
139 #endif
140         if (tmp = tgetstr("md",&p)) {   /* bold ? */
141             EXPAND("B.bold");
142             tmp = tgetstr("me",&p);
143             EXPAND("E.bold");
144         }
145         if (tmp = tgetstr("mr",&p)) {   /* reverse video? */
146             EXPAND("B.rw");
147             tmp = tgetstr("me",&p);
148             EXPAND("E.rw");
149         }
150         if (tmp = tgetstr("bl",&p)) {   /* Bell ? */
151             EXPAND("B.bell");
152             TD_SET("E.bell", NULL);
153         }
154         if (tmp = tgetstr("mb",&p)) {   /* Blink ? */
155             EXPAND("B.blink");
156             tmp = tgetstr("me",&p);
157             EXPAND("E.blink");
158         }
159         if (tmp = tgetstr("us",&p))     { /* Underline ? */
160             EXPAND("B.u");
161             tmp = tgetstr("ue",&p);
162             EXPAND("E.u");
163         }
164         if (tmp = tgetstr("so",&p))     { /* Standout ? */
165             EXPAND("B.so");
166             tmp = tgetstr("se",&p);
167             EXPAND("E.so");
168         }
169     }    
170     /* Step 2: alias others to the nearest substitute */
171     
172     /* Bold = so, else rv, else ul */
173     if (NULL == string_dictionary_Lookup(termcap_dict,"B.bold")) {
174         if(b = string_dictionary_Lookup(termcap_dict,"B.so")) {
175             TD_SET("B.bold",b->value);
176             TD_SET("E.bold",
177                    string_dictionary_Lookup(termcap_dict,"E.so")->value);
178         } else if (b = string_dictionary_Lookup(termcap_dict,"B.rv")) {
179             TD_SET("B.bold",b->value);
180             TD_SET("E.bold",
181                    string_dictionary_Lookup(termcap_dict,"E.rv")->value);
182         } else if (b = string_dictionary_Lookup(termcap_dict,"B.u")) {
183             TD_SET("B.bold",b->value);
184             TD_SET("E.bold",
185                    string_dictionary_Lookup(termcap_dict,"E.u")->value);
186         }
187     }
188     
189     /* Bell = ^G */
190     if (NULL == string_dictionary_Lookup(termcap_dict,"B.bell")) {
191         TD_SET("B.bell","\007");
192         TD_SET("E.bell",NULL);
193     }
194     
195     /* Underline -> nothing */
196     /* Blink -> nothing */
197     
198     return(0);
199 }
200
201 /***************************************************************************/
202
203
204
205
206 static int fixed_string_eq(pattern, text, text_length)
207      string pattern;
208      char *text;
209      int text_length;
210 {
211     while (*pattern && text_length>0 && *pattern == *text) {
212         pattern++;
213         text++;
214         text_length--;
215     }
216
217     return(!*pattern && !text_length);
218 }
219
220 typedef struct _tty_str_info {
221     struct _tty_str_info *next;
222
223     char *str;
224     int len;
225
226     char alignment; /* 'l', 'c', 'r', or ' ' to indicate newline */
227     unsigned int bold_p : 1;
228     unsigned int italic_p : 1;
229     unsigned int bell_p : 1;
230     unsigned int ignore: 1;
231 } tty_str_info;
232
233 static void free_info(info)
234      tty_str_info *info;
235 {
236     tty_str_info *next_info;
237
238     while (info) {
239         next_info = info->next;
240         free(info);
241         info = next_info;
242     }
243 }
244
245 static int do_mode_change(current_mode_p, text, text_length)
246      tty_str_info *current_mode_p;
247      char *text;
248      int text_length;
249 {
250     /* alignment commands: */
251     if (fixed_string_eq("left", text, text_length) ||
252         fixed_string_eq("l", text, text_length))
253       current_mode_p->alignment = 'l';
254     else if (fixed_string_eq("center", text, text_length) ||
255         fixed_string_eq("c", text, text_length))
256       current_mode_p->alignment = 'c';
257     else if (fixed_string_eq("right", text, text_length) ||
258         fixed_string_eq("r", text, text_length))
259       current_mode_p->alignment = 'r';
260
261     /* font commands: */
262     else if (fixed_string_eq("bold", text, text_length) ||
263              fixed_string_eq("b", text, text_length))
264       current_mode_p->bold_p = 1;
265     else if (fixed_string_eq("italic", text, text_length) ||
266              fixed_string_eq("i", text, text_length))
267       current_mode_p->italic_p = 1;
268     else if (fixed_string_eq("roman", text, text_length)) {
269         current_mode_p->bold_p = 0;
270         current_mode_p->italic_p = 0;
271     } else if (fixed_string_eq("beep", text, text_length)) {
272         current_mode_p->bell_p = 1;
273         return 1;
274     }
275
276     /* commands ignored in tty mode: */
277     else if (fixed_string_eq("color", text, text_length) ||
278              fixed_string_eq("font", text, text_length)) {
279         current_mode_p->ignore = 1;
280     } 
281     return 0;
282 }
283
284 static tty_str_info *convert_desc_to_tty_str_info(desc)
285      desctype *desc;
286 {
287     tty_str_info *temp;
288     tty_str_info *result = NULL;
289     tty_str_info *last_result_block = NULL;
290     int isbeep, did_beep = 0;
291
292 #if !defined(SABER) && defined(__STDC__)
293     tty_str_info current_mode = { NULL, "", 0, 'l', 0 , 0, 0, 0};
294 #else
295     /* This is needed due to a bug in saber, and lack of pre-ANSI support. */
296     tty_str_info current_mode;
297
298     current_mode.next = NULL;
299     current_mode.str = "";
300     current_mode.len = 0;
301     current_mode.alignment = 'l';
302     current_mode.bold_p = 0;
303     current_mode.italic_p = 0;
304     current_mode.bell_p = 0;
305     current_mode.ignore = 0;
306 #endif
307
308     for (; desc->code!=DT_EOF; desc=desc->next) {
309         isbeep = 0;
310         /* Handle environments: */
311         if (desc->code == DT_ENV) {
312             /* PUSH! */
313             temp = (tty_str_info *)malloc(sizeof(struct _tty_str_info));
314             *temp = current_mode;
315             current_mode.next = temp;
316
317             isbeep = do_mode_change(&current_mode, desc->str, desc->len);
318             if (!isbeep || did_beep)
319                 continue;       /* process one beep, ignore other envs */
320         } else if (desc->code == DT_END) {
321             /* POP! */
322             temp = current_mode.next;
323             current_mode = *temp;
324             free(temp);
325             continue;
326         }
327
328         /* Add new block (call it temp) to result: */
329         temp = (tty_str_info *)malloc(sizeof(struct _tty_str_info));
330         *temp = current_mode;
331         if (last_result_block) {
332             last_result_block->next = temp;
333             last_result_block = temp;
334         } else {
335             result = temp;
336             last_result_block = temp;
337         }
338
339         if (isbeep) {
340             /* special processing: need to insert a bell */
341             string_dictionary_binding *b;
342             b = string_dictionary_Lookup(termcap_dict,"B.bell");
343             if (b) {
344                 temp->str = b->value;
345                 temp->len = string_Length(temp->str);
346             } else
347                 /* shouldn't get here! */
348                 abort();
349             did_beep++;
350             continue;
351         }
352         if (desc->code == DT_STR) {
353             /* just combine string info with current mode: */
354             temp->str = desc->str;
355             temp->len = desc->len;
356         } else if (desc->code == DT_NL) {
357             /* make the new block a ' ' alignment block with an empty string */
358             temp->alignment = ' ';
359             temp->len = 0;
360             temp->ignore = 0;
361         }
362     }
363
364     if (last_result_block)
365       last_result_block->next = NULL;
366
367     return(result);
368 }
369
370 #define  max(a,b)                ((a)>(b)?(a):(b))
371
372 static int line_width(left_width, center_width, right_width)
373      int left_width;
374      int center_width;
375      int right_width;
376 {
377     if (center_width>0) {
378         if (left_width==0 && right_width==0)
379           return(center_width);
380         return(center_width+2+max(left_width,right_width)*2);
381     } else {
382         if (left_width && right_width)
383           return(1+left_width+right_width);
384         else
385           return(left_width+right_width);
386     }
387 }
388
389 static int calc_max_line_width(info)
390      tty_str_info *info;
391 {
392     int max_line_width = 0;
393     int left = 0;
394     int center = 0;
395     int right = 0;
396
397     for (; info; info=info->next) {
398         if (info->ignore)
399             continue;
400         switch (info->alignment) {
401           case 'l':
402             left += info->len;
403             break;
404
405           case 'c':
406             center += info->len;
407             break;
408
409           case 'r':
410             right += info->len;
411             break;
412
413           case ' ':
414 #ifdef DEBUG
415             if (zwgc_debug)
416               printf("width: %d %d %d = %d\n", left, center, right,
417                      line_width(left, center, right));
418 #endif
419             max_line_width = max(max_line_width,
420                                  line_width(left, center, right));
421             left = center = right = 0;
422             break;
423         }
424     }
425
426 #ifdef DEBUG
427     if (zwgc_debug)
428       printf("width: %d %d %d = %d\n", left, center, right,
429              line_width(left, center, right));
430 #endif
431     max_line_width = max(max_line_width,
432                          line_width(left, center, right));
433
434     return(max_line_width);
435 }
436
437 string tty_filter(text, use_fonts)
438      string text;
439      int use_fonts;
440 {
441     string text_copy = string_Copy(text);
442     string result_so_far = string_Copy("");
443     desctype *desc;
444     int number_of_strs;
445     int number_of_lines;
446     tty_str_info *info, *info_head;
447     int max_line_width;
448
449     desc = disp_get_cmds(text_copy, &number_of_strs, &number_of_lines);
450     info_head = info = convert_desc_to_tty_str_info(desc);
451     free_desc(desc);
452
453 #ifdef DEBUG
454     if (zwgc_debug)
455       { tty_str_info *ptr;
456         for (ptr=info; ptr; ptr=ptr->next) {
457             printf("%c: %s %s %s <%s>\n", ptr->alignment,
458                    ptr->bold_p ? "(bold)" : "",
459                    ptr->italic_p ? "(italic)" : "",
460                    ptr->bell_p ? "(bell)" : "",
461                    string_CreateFromData(ptr->str, ptr->len));
462         }
463     }
464 #endif
465
466     max_line_width = calc_max_line_width(info);
467     dprintf1("max width = %d\n", max_line_width);
468
469     while (info) {
470         string left, center, right;
471         int left_width, center_width, right_width;
472         char *temp;
473
474         left_width = center_width = right_width = 0;
475         left = string_Copy("");
476         center = string_Copy("");
477         right = string_Copy("");
478
479         for (; info && info->alignment!=' '; info=info->next) {
480             string item;
481
482             if (info->ignore)
483                 continue;
484
485             item = string_Copy("");
486             
487             if (info->bold_p && use_fonts) {
488                 if (temp = string_dictionary_Fetch(termcap_dict, "B.bold"))
489                   item = string_Concat2(item, temp);
490             } else if (info->italic_p && use_fonts) {
491                 if (temp = string_dictionary_Fetch(termcap_dict, "B.u"))
492                   item = string_Concat2(item, temp);
493             }
494             temp = string_CreateFromData(info->str, info->len);
495             item = string_Concat2(item, temp);
496             free(temp);
497
498             if (info->bold_p && use_fonts) {
499                 if (temp = string_dictionary_Fetch(termcap_dict, "E.bold"))
500                   item = string_Concat2(item, temp);
501             } else if (info->italic_p && use_fonts) {
502                 if (temp = string_dictionary_Fetch(termcap_dict, "E.u"))
503                   item = string_Concat2(item, temp);
504             }
505
506             switch (info->alignment) {
507               default:
508               case 'l':
509                 left = string_Concat2(left, item);
510                 left_width += info->len;
511                 break;
512
513               case 'c':
514                 center = string_Concat2(center, item);
515                 center_width += info->len;
516                 break;
517
518               case 'r':
519                 right = string_Concat2(right, item);
520                 right_width += info->len;
521                 break;
522             }
523             free(item);
524         }
525
526         result_so_far = string_Concat2(result_so_far, left);
527         if (center_width)
528           while (left_width < (max_line_width-center_width)/2 ) {
529               result_so_far = string_Concat2(result_so_far, " ");
530               left_width++;
531           }
532         result_so_far = string_Concat2(result_so_far, center);
533         left_width += center_width;
534
535         if (right_width)
536           while (left_width<max_line_width-right_width) {
537               result_so_far = string_Concat2(result_so_far, " ");
538               left_width++;
539           }
540         result_so_far = string_Concat2(result_so_far, right);
541         free(left);  free(center);  free(right);
542
543         if (info && info->alignment == ' ') {
544             info = info->next;
545             result_so_far = string_Concat2(result_so_far, "\r\n");
546         }
547     }
548
549     free_info(info_head);
550     free(text_copy);
551     if (number_of_lines &&
552         (result_so_far[string_Length(result_so_far)-1] != '\n'))
553         /* CRLF-terminate all results */
554         result_so_far = string_Concat2(result_so_far, "\r\n");
555     return(result_so_far);
556 }