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