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