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