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