source: owl.c @ 13ee8f2

release-1.10release-1.8release-1.9
Last change on this file since 13ee8f2 was 9efa5bd, checked in by David Benjamin <davidben@mit.edu>, 13 years ago
Clamp the number of color pairs to 256 without ext-color Debian doesn't build their ncurses with ext-color support, so it only supports 256 colorpairs. However, ncurses still reports the full 32768 value, and color pairs end up trampling over each other. When 256 is exceeded, the existing colorpair reset logic will now kick in and fix things. Reword a comment accordingly. While I'm here, get rid of the colorpairs member in owl_global. ncurses' works just fine. Reported-by: Mats Ahlgren <mats_a@mit.edu>
  • Property mode set to 100644
File size: 17.8 KB
Line 
1/*  Copyright (c) 2006-2011 The BarnOwl Developers. All rights reserved.
2 *  Copyright (c) 2004 James Kretchmar. All rights reserved.
3 *
4 *  This program is free software. You can redistribute it and/or
5 *  modify under the terms of the Sleepycat License. See the COPYING
6 *  file included with the distribution for more information.
7 */
8
9#include <stdio.h>
10#include <unistd.h>
11#include <getopt.h>
12#include <stdlib.h>
13#include <string.h>
14#include <signal.h>
15#include <time.h>
16#include <sys/param.h>
17#include <sys/types.h>
18#include <sys/time.h>
19#include <termios.h>
20#include <sys/stat.h>
21#include <locale.h>
22#include "owl.h"
23
24
25#if OWL_STDERR_REDIR
26#ifdef HAVE_SYS_IOCTL_H
27#include <sys/ioctl.h>
28#endif
29#ifdef HAVE_SYS_FILIO_H
30#include <sys/filio.h>
31#endif
32int stderr_replace(void);
33#endif
34
35owl_global g;
36
37typedef struct _owl_options {
38  bool load_initial_subs;
39  char *configfile;
40  char *tty;
41  char *confdir;
42  bool debug;
43} owl_options;
44
45void usage(void)
46{
47  fprintf(stderr, "Barnowl version %s\n", OWL_VERSION_STRING);
48  fprintf(stderr, "Usage: barnowl [-n] [-d] [-D] [-v] [-h] [-c <configfile>] [-s <confdir>] [-t <ttyname>]\n");
49  fprintf(stderr, "  -n,--no-subs        don't load zephyr subscriptions\n");
50  fprintf(stderr, "  -d,--debug          enable debugging\n");
51  fprintf(stderr, "  -v,--version        print the Barnowl version number and exit\n");
52  fprintf(stderr, "  -h,--help           print this help message\n");
53  fprintf(stderr, "  -c,--config-file    specify an alternate config file\n");
54  fprintf(stderr, "  -s,--config-dir     specify an alternate config dir (default ~/.owl)\n");
55  fprintf(stderr, "  -t,--tty            set the tty name\n");
56}
57
58/* TODO: free owl_options after init is done? */
59void owl_parse_options(int argc, char *argv[], owl_options *opts) {
60  static const struct option long_options[] = {
61    { "no-subs",         0, 0, 'n' },
62    { "config-file",     1, 0, 'c' },
63    { "config-dir",      1, 0, 's' },
64    { "tty",             1, 0, 't' },
65    { "debug",           0, 0, 'd' },
66    { "version",         0, 0, 'v' },
67    { "help",            0, 0, 'h' },
68    { NULL, 0, NULL, 0}
69  };
70  char c;
71
72  while((c = getopt_long(argc, argv, "nc:t:s:dDvh",
73                         long_options, NULL)) != -1) {
74    switch(c) {
75    case 'n':
76      opts->load_initial_subs = 0;
77      break;
78    case 'c':
79      opts->configfile = g_strdup(optarg);
80      break;
81    case 's':
82      opts->confdir = g_strdup(optarg);
83      break;
84    case 't':
85      opts->tty = g_strdup(optarg);
86      break;
87    case 'd':
88      opts->debug = 1;
89      break;
90    case 'v':
91      printf("This is barnowl version %s\n", OWL_VERSION_STRING);
92      exit(0);
93    case 'h':
94    default:
95      usage();
96      exit(1);
97    }
98  }
99}
100
101void owl_start_color(void) {
102  start_color();
103#ifdef HAVE_USE_DEFAULT_COLORS
104  use_default_colors();
105#endif
106
107  /* define simple color pairs */
108  if (has_colors() && COLOR_PAIRS>=8) {
109    int bg = COLOR_BLACK;
110#ifdef HAVE_USE_DEFAULT_COLORS
111    bg = -1;
112#endif
113    init_pair(OWL_COLOR_BLACK,   COLOR_BLACK,   bg);
114    init_pair(OWL_COLOR_RED,     COLOR_RED,     bg);
115    init_pair(OWL_COLOR_GREEN,   COLOR_GREEN,   bg);
116    init_pair(OWL_COLOR_YELLOW,  COLOR_YELLOW,  bg);
117    init_pair(OWL_COLOR_BLUE,    COLOR_BLUE,    bg);
118    init_pair(OWL_COLOR_MAGENTA, COLOR_MAGENTA, bg);
119    init_pair(OWL_COLOR_CYAN,    COLOR_CYAN,    bg);
120    init_pair(OWL_COLOR_WHITE,   COLOR_WHITE,   bg);
121  }
122}
123
124void owl_start_curses(void) {
125  struct termios tio;
126  /* save initial terminal settings */
127  tcgetattr(STDIN_FILENO, owl_global_get_startup_tio(&g));
128
129  tcgetattr(STDIN_FILENO, &tio);
130  tio.c_iflag &= ~(ISTRIP|IEXTEN);
131  tio.c_cc[VQUIT] = fpathconf(STDIN_FILENO, _PC_VDISABLE);
132  tio.c_cc[VSUSP] = fpathconf(STDIN_FILENO, _PC_VDISABLE);
133  tio.c_cc[VSTART] = fpathconf(STDIN_FILENO, _PC_VDISABLE);
134  tio.c_cc[VSTOP] = fpathconf(STDIN_FILENO, _PC_VDISABLE);
135  tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio);
136
137  /* screen init */
138  initscr();
139  cbreak();
140  noecho();
141
142  owl_start_color();
143}
144
145void owl_shutdown_curses(void) {
146  endwin();
147  /* restore terminal settings */
148  tcsetattr(STDIN_FILENO, TCSAFLUSH, owl_global_get_startup_tio(&g));
149}
150
151/*
152 * Process a new message passed to us on the message queue from some
153 * protocol. This includes adding it to the message list, updating the
154 * view and scrolling if appropriate, logging it, and so on.
155 *
156 * Either a pointer is kept to the message internally, or it is freed
157 * if unneeded. The caller no longer ``owns'' the message's memory.
158 *
159 * Returns 1 if the message was added to the message list, and 0 if it
160 * was ignored due to user settings or otherwise.
161 */
162int owl_process_message(owl_message *m) {
163  const owl_filter *f;
164  /* if this message it on the puntlist, nuke it and continue */
165  if (owl_global_message_is_puntable(&g, m)) {
166    owl_message_delete(m);
167    return 0;
168  }
169
170  /*  login or logout that should be ignored? */
171  if (owl_global_is_ignorelogins(&g)
172      && owl_message_is_loginout(m)) {
173    owl_message_delete(m);
174    return 0;
175  }
176
177  if (!owl_global_is_displayoutgoing(&g)
178      && owl_message_is_direction_out(m)) {
179    owl_message_delete(m);
180    return 0;
181  }
182
183  /* add it to the global list */
184  owl_messagelist_append_element(owl_global_get_msglist(&g), m);
185  /* add it to any necessary views; right now there's only the current view */
186  owl_view_consider_message(owl_global_get_current_view(&g), m);
187
188  if(owl_message_is_direction_in(m)) {
189    /* let perl know about it*/
190    owl_perlconfig_getmsg(m, NULL);
191
192    /* do we need to autoreply? */
193    if (owl_global_is_zaway(&g) && !owl_message_get_attribute_value(m, "isauto")) {
194      if (owl_message_is_type_zephyr(m)) {
195        owl_zephyr_zaway(m);
196      } else if (owl_message_is_type_aim(m)) {
197        if (owl_message_is_private(m)) {
198          owl_function_send_aimawymsg(owl_message_get_sender(m), owl_global_get_zaway_msg(&g));
199        }
200      }
201    }
202
203    /* ring the bell if it's a personal */
204    if (!strcmp(owl_global_get_personalbell(&g), "on")) {
205      if (!owl_message_is_loginout(m) &&
206          !owl_message_is_mail(m) &&
207          owl_message_is_personal(m)) {
208        owl_function_beep();
209      }
210    } else if (!strcmp(owl_global_get_personalbell(&g), "off")) {
211      /* do nothing */
212    } else {
213      f=owl_global_get_filter(&g, owl_global_get_personalbell(&g));
214      if (f && owl_filter_message_match(f, m)) {
215        owl_function_beep();
216      }
217    }
218
219    /* if it matches the alert filter, do the alert action */
220    f=owl_global_get_filter(&g, owl_global_get_alert_filter(&g));
221    if (f && owl_filter_message_match(f, m)) {
222      owl_function_command_norv(owl_global_get_alert_action(&g));
223    }
224
225    /* if it's a zephyr login or logout, update the zbuddylist */
226    if (owl_message_is_type_zephyr(m) && owl_message_is_loginout(m)) {
227      if (owl_message_is_login(m)) {
228        owl_zbuddylist_adduser(owl_global_get_zephyr_buddylist(&g), owl_message_get_sender(m));
229      } else if (owl_message_is_logout(m)) {
230        owl_zbuddylist_deluser(owl_global_get_zephyr_buddylist(&g), owl_message_get_sender(m));
231      } else {
232        owl_function_error("Internal error: received login notice that is neither login nor logout");
233      }
234    }
235  }
236
237  /* let perl know about it */
238  owl_perlconfig_newmsg(m, NULL);
239  /* log the message if we need to */
240  owl_log_message(m);
241  /* redraw the sepbar; TODO: don't violate layering */
242  owl_global_sepbar_dirty(&g);
243
244  return 1;
245}
246
247/*
248 * Process any new messages we have waiting in the message queue.
249 * Returns 1 if any messages were added to the message list, and 0 otherwise.
250 */
251int owl_process_messages(owl_ps_action *d, void *p)
252{
253  int newmsgs=0;
254  int followlast = owl_global_should_followlast(&g);
255  owl_message *m;
256
257  /* Grab incoming messages. */
258  while (owl_global_messagequeue_pending(&g)) {
259    m = owl_global_messagequeue_popmsg(&g);
260    if (owl_process_message(m))
261      newmsgs = 1;
262  }
263
264  if (newmsgs) {
265    /* follow the last message if we're supposed to */
266    if (followlast)
267      owl_function_lastmsg_noredisplay();
268
269    /* do the newmsgproc thing */
270    owl_function_do_newmsgproc();
271
272    /* redisplay if necessary */
273    /* this should be optimized to not run if the new messages won't be displayed */
274    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
275  }
276  return newmsgs;
277}
278
279void owl_process_input(const owl_io_dispatch *d, void *data)
280{
281  owl_input j;
282
283  while (1) {
284    j.ch = wgetch(g.input_pad);
285    if (j.ch == ERR) return;
286
287    j.uch = '\0';
288    if (j.ch >= KEY_MIN && j.ch <= KEY_MAX) {
289      /* This is a curses control character. */
290    }
291    else if (j.ch > 0x7f && j.ch < 0xfe) {
292      /* Pull in a full utf-8 character. */
293      int bytes, i;
294      char utf8buf[7];
295      memset(utf8buf, '\0', 7);
296     
297      utf8buf[0] = j.ch;
298     
299      if ((j.ch & 0xc0) && (~j.ch & 0x20)) bytes = 2;
300      else if ((j.ch & 0xe0) && (~j.ch & 0x10)) bytes = 3;
301      else if ((j.ch & 0xf0) && (~j.ch & 0x08)) bytes = 4;
302      else if ((j.ch & 0xf8) && (~j.ch & 0x04)) bytes = 5;
303      else if ((j.ch & 0xfc) && (~j.ch & 0x02)) bytes = 6;
304      else bytes = 1;
305     
306      for (i = 1; i < bytes; i++) {
307        int tmp = wgetch(g.input_pad);
308        /* If what we got was not a byte, or not a continuation byte */
309        if (tmp > 0xff || !(tmp & 0x80 && ~tmp & 0x40)) {
310          /* ill-formed UTF-8 code unit subsequence, put back the
311             char we just got. */
312          ungetch(tmp);
313          j.ch = ERR;
314          break;
315        }
316        utf8buf[i] = tmp;
317      }
318     
319      if (j.ch != ERR) {
320        if (g_utf8_validate(utf8buf, -1, NULL)) {
321          j.uch = g_utf8_get_char(utf8buf);
322        }
323        else {
324          j.ch = ERR;
325        }
326      }
327    }
328    else if (j.ch <= 0x7f) {
329      j.uch = j.ch;
330    }
331
332    owl_process_input_char(j);
333  }
334}
335
336void sig_handler(int sig, siginfo_t *si, void *data)
337{
338  if (sig==SIGWINCH) {
339    /* we can't inturrupt a malloc here, so it just sets a flag
340     * schedulding a resize for later
341     */
342    owl_function_resize();
343  } else if (sig==SIGPIPE || sig==SIGCHLD) {
344    /* Set a flag and some info that we got the sigpipe
345     * so we can record that we got it and why... */
346    owl_global_set_errsignal(&g, sig, si);
347  } else if (sig==SIGTERM || sig==SIGHUP) {
348    owl_function_quit();
349  }
350}
351
352void sigint_handler(int sig, siginfo_t *si, void *data)
353{
354  owl_global_set_interrupted(&g);
355}
356
357static int owl_errsignal_pre_select_action(owl_ps_action *a, void *data)
358{
359  siginfo_t si;
360  int signum;
361  if ((signum = owl_global_get_errsignal_and_clear(&g, &si)) > 0) {
362    owl_function_error("Got unexpected signal: %d %s  (code: %d band: %ld  errno: %d)",
363        signum, signum==SIGPIPE?"SIGPIPE":"SIG????",
364        si.si_code, si.si_band, si.si_errno);
365  }
366  return 0;
367}
368
369void owl_register_signal_handlers(void) {
370  struct sigaction sigact;
371
372  /* signal handler */
373  /*sigact.sa_handler=sig_handler;*/
374  sigact.sa_sigaction=sig_handler;
375  sigemptyset(&sigact.sa_mask);
376  sigact.sa_flags=SA_SIGINFO;
377  sigaction(SIGWINCH, &sigact, NULL);
378  sigaction(SIGALRM, &sigact, NULL);
379  sigaction(SIGPIPE, &sigact, NULL);
380  sigaction(SIGTERM, &sigact, NULL);
381  sigaction(SIGHUP, &sigact, NULL);
382
383  sigact.sa_sigaction=sigint_handler;
384  sigaction(SIGINT, &sigact, NULL);
385}
386
387#if OWL_STDERR_REDIR
388
389/* Replaces stderr with a pipe so that we can read from it.
390 * Returns the fd of the pipe from which stderr can be read. */
391int stderr_replace(void)
392{
393  int pipefds[2];
394  if (0 != pipe(pipefds)) {
395    perror("pipe");
396    owl_function_debugmsg("stderr_replace: pipe FAILED\n");
397    return -1;
398  }
399    owl_function_debugmsg("stderr_replace: pipe: %d,%d\n", pipefds[0], pipefds[1]);
400  if (-1 == dup2(pipefds[1], 2 /*stderr*/)) {
401    owl_function_debugmsg("stderr_replace: dup2 FAILED (%s)\n", strerror(errno));
402    perror("dup2");
403    return -1;
404  }
405  return pipefds[0];
406}
407
408/* Sends stderr (read from rfd) messages to the error console */
409void stderr_redirect_handler(const owl_io_dispatch *d, void *data)
410{
411  int navail, bread;
412  char buf[4096];
413  int rfd = d->fd;
414  char *err;
415
416  if (rfd<0) return;
417  if (-1 == ioctl(rfd, FIONREAD, &navail)) {
418    return;
419  }
420  /*owl_function_debugmsg("stderr_redirect: navail = %d\n", navail);*/
421  if (navail <= 0) return;
422  if (navail > sizeof(buf)-1) {
423    navail = sizeof(buf)-1;
424  }
425  bread = read(rfd, buf, navail);
426  if (buf[navail-1] != '\0') {
427    buf[navail] = '\0';
428  }
429
430  err = g_strdup_printf("[stderr]\n%s", buf);
431
432  owl_function_log_err(err);
433  g_free(err);
434}
435
436#endif /* OWL_STDERR_REDIR */
437
438static int owl_refresh_pre_select_action(owl_ps_action *a, void *data)
439{
440  owl_colorpair_mgr *cpmgr;
441
442  /* if a resize has been scheduled, deal with it */
443  owl_global_check_resize(&g);
444  /* update the terminal if we need to */
445  owl_window_redraw_scheduled();
446  /* On colorpair shortage, reset and redraw /everything/. NOTE: if we
447   * still overflow, this be useless work. With 8-colors, we get 64
448   * pairs. With 256-colors, we get 32768 pairs with ext-colors
449   * support and 256 otherwise. */
450  cpmgr = owl_global_get_colorpair_mgr(&g);
451  if (cpmgr->overflow) {
452    owl_function_debugmsg("colorpairs: used all %d pairs; reset pairs and redraw.",
453                          owl_util_get_colorpairs());
454    owl_fmtext_reset_colorpairs(cpmgr);
455    owl_function_full_redisplay();
456    owl_window_redraw_scheduled();
457  }
458  return 0;
459}
460
461
462int main(int argc, char **argv, char **env)
463{
464  int argc_copy;
465  char **argv_copy;
466  char *perlout, *perlerr;
467  const owl_style *s;
468  const char *dir;
469  owl_options opts;
470
471  if (!GLIB_CHECK_VERSION (2, 12, 0))
472    g_error ("GLib version 2.12.0 or above is needed.");
473
474  argc_copy = argc;
475  argv_copy = g_strdupv(argv);
476
477  setlocale(LC_ALL, "");
478
479  memset(&opts, 0, sizeof opts);
480  opts.load_initial_subs = 1;
481  owl_parse_options(argc, argv, &opts);
482  g.load_initial_subs = opts.load_initial_subs;
483
484  owl_register_signal_handlers();
485  owl_start_curses();
486
487  /* owl global init */
488  owl_global_init(&g);
489  if (opts.debug) owl_global_set_debug_on(&g);
490  if (opts.confdir) owl_global_set_confdir(&g, opts.confdir);
491  owl_function_debugmsg("startup: first available debugging message");
492  owl_global_set_startupargs(&g, argc_copy, argv_copy);
493  g_strfreev(argv_copy);
494  owl_global_set_haveaim(&g);
495
496  /* register STDIN dispatch; throw away return, we won't need it */
497  owl_select_add_io_dispatch(STDIN_FILENO, OWL_IO_READ, &owl_process_input, NULL, NULL);
498  owl_zephyr_initialize();
499
500#if OWL_STDERR_REDIR
501  /* Do this only after we've started curses up... */
502  owl_function_debugmsg("startup: doing stderr redirection");
503  owl_select_add_io_dispatch(stderr_replace(), OWL_IO_READ, &stderr_redirect_handler, NULL, NULL);
504#endif
505
506  /* create the owl directory, in case it does not exist */
507  owl_function_debugmsg("startup: creating owl directory, if not present");
508  dir=owl_global_get_confdir(&g);
509  mkdir(dir, S_IRWXU);
510
511  /* set the tty, either from the command line, or by figuring it out */
512  owl_function_debugmsg("startup: setting tty name");
513  if (opts.tty) {
514    owl_global_set_tty(&g, opts.tty);
515  } else {
516    char *tty = owl_util_get_default_tty();
517    owl_global_set_tty(&g, tty);
518    g_free(tty);
519  }
520
521  /* Initialize perl */
522  owl_function_debugmsg("startup: processing config file");
523
524  owl_global_pop_context(&g);
525  owl_global_push_context(&g, OWL_CTX_READCONFIG, NULL, NULL, NULL);
526
527  perlerr=owl_perlconfig_initperl(opts.configfile, &argc, &argv, &env);
528  if (perlerr) {
529    endwin();
530    fprintf(stderr, "Internal perl error: %s\n", perlerr);
531    fflush(stderr);
532    printf("Internal perl error: %s\n", perlerr);
533    fflush(stdout);
534    exit(1);
535  }
536
537  owl_global_complete_setup(&g);
538
539  owl_global_setup_default_filters(&g);
540
541  /* set the current view */
542  owl_function_debugmsg("startup: setting the current view");
543  owl_view_create(owl_global_get_current_view(&g), "main",
544                  owl_global_get_filter(&g, "all"),
545                  owl_global_get_style_by_name(&g, "default"));
546
547  /* AIM init */
548  owl_function_debugmsg("startup: doing AIM initialization");
549  owl_aim_init();
550
551  /* execute the startup function in the configfile */
552  owl_function_debugmsg("startup: executing perl startup, if applicable");
553  perlout = owl_perlconfig_execute("BarnOwl::Hooks::_startup();");
554  if (perlout) g_free(perlout);
555
556  /* welcome message */
557  owl_function_debugmsg("startup: creating splash message");
558  owl_function_adminmsg("",
559    "-----------------------------------------------------------------------\n"
560    "Welcome to barnowl version " OWL_VERSION_STRING ".\n"
561    "To see a quick introduction, type ':show quickstart'.                  \n"
562    "Press 'h' for on-line help.                                            \n"
563    "                                                                       \n"
564    "BarnOwl is free software. Type ':show license' for more                \n"
565    "information.                                                     ^ ^   \n"
566    "                                                                 OvO   \n"
567    "Please report any bugs or suggestions to bug-barnowl@mit.edu    (   )  \n"
568    "-----------------------------------------------------------------m-m---\n"
569  );
570
571  /* process the startup file */
572  owl_function_debugmsg("startup: processing startup file");
573  owl_function_source(NULL);
574
575  owl_function_debugmsg("startup: set style for the view: %s", owl_global_get_default_style(&g));
576  s = owl_global_get_style_by_name(&g, owl_global_get_default_style(&g));
577  if(s)
578      owl_view_set_style(owl_global_get_current_view(&g), s);
579  else
580      owl_function_error("No such style: %s", owl_global_get_default_style(&g));
581
582  owl_function_debugmsg("startup: setting context interactive");
583
584  owl_global_pop_context(&g);
585  owl_global_push_context(&g, OWL_CTX_INTERACTIVE|OWL_CTX_RECV, NULL, "recv", NULL);
586
587  owl_select_add_pre_select_action(owl_refresh_pre_select_action, NULL, NULL);
588  owl_select_add_pre_select_action(owl_process_messages, NULL, NULL);
589  owl_select_add_pre_select_action(owl_errsignal_pre_select_action, NULL, NULL);
590
591  owl_function_debugmsg("startup: entering main loop");
592  owl_select_run_loop();
593
594  /* Shut down everything. */
595  owl_zephyr_shutdown();
596  owl_shutdown_curses();
597  return 0;
598}
Note: See TracBrowser for help on using the repository browser.