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
5 * Created by: Marc Horowitz <marc@athena.mit.edu>
7 * $Id: tty_filter.c 2529 2009-08-09 20:43:14Z kcr@ATHENA.MIT.EDU $
9 * Copyright (c) 1989 by the Massachusetts Institute of Technology.
10 * For copying and distribution information, see the file
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 $";
20 #include <zephyr/mit-copyright.h>
22 /****************************************************************************/
24 /* The tty & plain filters: */
26 /****************************************************************************/
32 /* I blame Solaris. Solaris to blame. */
42 #include <zephyr/zephyr.h>
44 #include "new_memory.h"
45 #include "new_string.h"
46 #include "variables.h"
47 #include "string_dictionary_aux.h"
48 #include "formatter.h"
52 /***************************************************************************/
53 #ifndef HAVE_TERMCAP_H
55 extern char *tgetstr(),*getenv();
57 extern speed_t ospeed;
64 /* Dictionary naming convention:
66 B.xxx is the termcap sequence to begin environment xxx.
67 E.xxx is the termcap sequence to end environment xxx.
71 static string_dictionary termcap_dict;
72 static char code_buf[10240], *code_buf_pos = code_buf, *code;
74 /* Define the following commands:
76 (Hopefully) shared with all devices:
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"
86 @blink "mb"/"me" termcap entry, else nothing.
87 @rv "so"/"se" termcap entry.
88 @u "us"/"ue" termcap entry.
91 #define TD_SET(k,v) (string_dictionary_Define(termcap_dict, (k), &ex)->value \
93 #define EXPAND(k) (code = code_buf_pos, tputs(tmp, 1, tty_outc), \
94 *code_buf_pos++ = 0, TD_SET(k, code))
105 tty_filter_init(char *drivername,
110 static char st_buf[128];
111 char tc_buf[1024], *p = st_buf, *tmp, *term;
113 string_dictionary_binding *b;
114 int isrealtty = string_Eq(drivername, "tty");
117 ospeed = (tcgetattr(STDIN_FILENO, &tbuf) == 0) ? cfgetospeed(&tbuf) : 2400;
119 if (termcap_dict == (string_dictionary) NULL)
120 termcap_dict = string_dictionary_Create(7);
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
126 ERROR("$TERM not set. tty mode will be plain.\n");
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.
137 else if (!strcmp(term, "dumb")) { }
140 tgetent(tc_buf, term);
142 /* Step 1: get all of {rv,bold,u,bell,blink} that are available. */
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") */
147 tmp = tgetstr("pc", &p);
148 PC = (tmp) ? *tmp : 0;
149 tmp = tgetstr("md", &p);
150 if (tmp) { /* bold ? */
152 tmp = tgetstr("me",&p);
155 tmp = tgetstr("mr", &p);
156 if (tmp) { /* reverse video? */
158 tmp = tgetstr("me", &p);
161 tmp = tgetstr("bl", &p);
162 if (tmp) { /* Bell ? */
164 TD_SET("E.bell", NULL);
166 tmp = tgetstr("mb", &p);
167 if (tmp) { /* Blink ? */
169 tmp = tgetstr("me", &p);
172 tmp = tgetstr("us", &p);
173 if (tmp) { /* Underline ? */
175 tmp = tgetstr("ue", &p);
178 tmp = tgetstr("so", &p);
179 if (tmp) { /* Standout ? */
181 tmp = tgetstr("se", &p);
185 /* Step 2: alias others to the nearest substitute */
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);
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);
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);
200 string_dictionary_Lookup(termcap_dict, "E.u")->value);
205 if (NULL == string_dictionary_Lookup(termcap_dict, "B.bell")) {
206 TD_SET("B.bell", "\007");
207 TD_SET("E.bell", NULL);
210 /* Underline -> nothing */
211 /* Blink -> nothing */
216 /***************************************************************************/
222 fixed_string_eq(string pattern,
226 while (*pattern && text_length>0 && *pattern == *text) {
232 return(!*pattern && !text_length);
235 typedef struct _tty_str_info {
236 struct _tty_str_info *next;
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;
248 const char *info_default_string = "";
251 free_info(tty_str_info *info)
253 tty_str_info *next_info;
256 next_info = info->next;
257 if (info->str != info_default_string)
265 do_mode_change(tty_str_info *current_mode_p,
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';
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;
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;
304 zwgc_transliterate(char *in, int inlen, char **out, int *outlen){
306 string notice_charset = var_get_variable("notice_charset");
307 string tty_charset = var_get_variable("tty_charset");
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);
318 var_set_variable("error", strerror(retval));
321 static tty_str_info *
322 convert_desc_to_tty_str_info(desctype *desc)
325 tty_str_info *result = NULL;
326 tty_str_info *last_result_block = NULL;
327 int isbeep, did_beep = 0;
329 #if !defined(SABER) && defined(__STDC__)
330 tty_str_info current_mode = { NULL, "", 0, 'l', 0 , 0, 0, 0};
332 /* This is needed due to a bug in saber, and lack of pre-ANSI support. */
333 tty_str_info current_mode;
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;
345 for (; desc->code!=DT_EOF; desc=desc->next) {
347 /* Handle environments: */
348 if (desc->code == DT_ENV) {
350 temp = (tty_str_info *)malloc(sizeof(struct _tty_str_info));
351 *temp = current_mode;
352 current_mode.next = temp;
354 isbeep = do_mode_change(¤t_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) {
359 temp = current_mode.next;
360 current_mode = *temp;
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;
373 last_result_block = temp;
377 /* special processing: need to insert a bell */
378 string_dictionary_binding *b;
379 b = string_dictionary_Lookup(termcap_dict,"B.bell");
381 temp->str = strdup(b->value);
382 temp->len = string_Length(temp->str);
384 /* shouldn't get here! */
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 = ' ';
400 if (last_result_block)
401 last_result_block->next = NULL;
406 #define max(a,b) ((a)>(b)?(a):(b))
409 line_width(int left_width,
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);
418 if (left_width && right_width)
419 return(1+left_width+right_width);
421 return(left_width+right_width);
426 calc_max_line_width(tty_str_info *info)
428 int max_line_width = 0;
433 for (; info; info=info->next) {
436 switch (info->alignment) {
452 printf("width: %d %d %d = %d\n", left, center, right,
453 line_width(left, center, right));
455 max_line_width = max(max_line_width,
456 line_width(left, center, right));
457 left = center = right = 0;
464 printf("width: %d %d %d = %d\n", left, center, right,
465 line_width(left, center, right));
467 max_line_width = max(max_line_width,
468 line_width(left, center, right));
470 return(max_line_width);
474 tty_filter(string text,
477 string text_copy = string_Copy(text);
478 string result_so_far = string_Copy("");
482 tty_str_info *info, *info_head;
485 desc = disp_get_cmds(text_copy, &number_of_strs, &number_of_lines);
486 info_head = info = convert_desc_to_tty_str_info(desc);
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));
502 max_line_width = calc_max_line_width(info);
503 dprintf1("max width = %d\n", max_line_width);
506 string left, center, right;
507 int left_width, center_width, right_width;
510 left_width = center_width = right_width = 0;
511 left = string_Copy("");
512 center = string_Copy("");
513 right = string_Copy("");
515 for (; info && info->alignment!=' '; info=info->next) {
521 item = string_Copy("");
523 if (info->bold_p && use_fonts) {
524 temp = string_dictionary_Fetch(termcap_dict, "B.bold");
526 item = string_Concat2(item, temp);
527 } else if (info->italic_p && use_fonts) {
528 temp = string_dictionary_Fetch(termcap_dict, "B.u");
530 item = string_Concat2(item, temp);
532 temp = string_CreateFromData(info->str, info->len);
533 item = string_Concat2(item, temp);
536 if (info->bold_p && use_fonts) {
537 temp = string_dictionary_Fetch(termcap_dict, "E.bold");
539 item = string_Concat2(item, temp);
540 } else if (info->italic_p && use_fonts) {
541 temp = string_dictionary_Fetch(termcap_dict, "E.u");
543 item = string_Concat2(item, temp);
546 switch (info->alignment) {
549 left = string_Concat2(left, item);
550 left_width += info->len;
554 center = string_Concat2(center, item);
555 center_width += info->len;
559 right = string_Concat2(right, item);
560 right_width += info->len;
566 result_so_far = string_Concat2(result_so_far, left);
568 while (left_width < (max_line_width-center_width)/2 ) {
569 result_so_far = string_Concat2(result_so_far, " ");
572 result_so_far = string_Concat2(result_so_far, center);
573 left_width += center_width;
576 while (left_width<max_line_width-right_width) {
577 result_so_far = string_Concat2(result_so_far, " ");
580 result_so_far = string_Concat2(result_so_far, right);
581 free(left); free(center); free(right);
583 if (info && info->alignment == ' ') {
585 result_so_far = string_Concat2(result_so_far, "\r\n");
589 free_info(info_head);
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);