source: owl.c @ 8d553bf

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