source: functions.c @ a5f477c

release-1.10release-1.8release-1.9
Last change on this file since a5f477c was a5f477c, checked in by David Benjamin <davidben@mit.edu>, 14 years ago
Reimplement owl_function_zuserfilt with proper quoting [nelhage@nelhage.com: Use owl_string_appendf_quoted]
  • Property mode set to 100644
File size: 96.6 KB
Line 
1#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4#include <signal.h>
5#include <netinet/in.h>
6#include <string.h>
7#include <time.h>
8#include <sys/types.h>
9#include <sys/stat.h>
10#include <sys/wait.h>
11#include <errno.h>
12#include <signal.h>
13#include "owl.h"
14#include "filterproc.h"
15
16char *owl_function_command(const char *cmdbuff)
17{
18  owl_function_debugmsg("executing command: %s", cmdbuff);
19  return owl_cmddict_execute(owl_global_get_cmddict(&g), 
20                             owl_global_get_context(&g), cmdbuff);
21}
22
23char *owl_function_command_argv(const char *const *argv, int argc)
24{
25  return owl_cmddict_execute_argv(owl_global_get_cmddict(&g),
26                                  owl_global_get_context(&g),
27                                  argv, argc);
28}
29
30void owl_function_command_norv(const char *cmdbuff)
31{
32  char *rv;
33  rv=owl_function_command(cmdbuff);
34  if (rv) owl_free(rv);
35}
36
37void owl_function_command_alias(const char *alias_from, const char *alias_to)
38{
39  owl_cmddict_add_alias(owl_global_get_cmddict(&g), alias_from, alias_to);
40}
41
42const owl_cmd *owl_function_get_cmd(const char *name)
43{
44  return owl_cmddict_find(owl_global_get_cmddict(&g), name);
45}
46
47void owl_function_show_commands(void)
48{
49  owl_list l;
50  owl_fmtext fm;
51
52  owl_fmtext_init_null(&fm);
53  owl_fmtext_append_bold(&fm, "Commands:  ");
54  owl_fmtext_append_normal(&fm, "(use 'show command <name>' for details)\n");
55  owl_cmddict_get_names(owl_global_get_cmddict(&g), &l);
56  owl_fmtext_append_list(&fm, &l, "\n", owl_function_cmd_describe);
57  owl_fmtext_append_normal(&fm, "\n");
58  owl_function_popless_fmtext(&fm);
59  owl_cmddict_namelist_cleanup(&l);
60  owl_fmtext_cleanup(&fm);
61}
62
63void owl_function_show_view(const char *viewname)
64{
65  const owl_view *v;
66  owl_fmtext fm;
67
68  /* we only have the one view right now */
69  v=owl_global_get_current_view(&g);
70  if (viewname && strcmp(viewname, owl_view_get_name(v))) {
71    owl_function_error("No view named '%s'", viewname);
72    return;
73  }
74
75  owl_fmtext_init_null(&fm);
76  owl_view_to_fmtext(v, &fm);
77  owl_function_popless_fmtext(&fm);
78  owl_fmtext_cleanup(&fm);
79}
80
81void owl_function_show_styles(void) {
82  owl_list l;
83  owl_fmtext fm;
84
85  owl_fmtext_init_null(&fm);
86  owl_fmtext_append_bold(&fm, "Styles:\n");
87  owl_global_get_style_names(&g, &l);
88  owl_fmtext_append_list(&fm, &l, "\n", owl_function_style_describe);
89  owl_fmtext_append_normal(&fm, "\n");
90  owl_function_popless_fmtext(&fm);
91  owl_list_cleanup(&l, owl_free);
92  owl_fmtext_cleanup(&fm);
93}
94
95static void _owl_function_timer_append_fmtext(gpointer data, gpointer user_data) {
96  owl_fmtext *fm = user_data;
97  owl_timer *timer = data;
98  char *str = owl_sprintf("- %s: in %d seconds",
99                          timer->name ? timer->name : "(unnamed)",
100                          (int)(timer->time - time(NULL)));
101  owl_fmtext_append_normal(fm, str);
102  owl_free(str);
103  if (timer->interval) {
104    str = owl_sprintf(", repeat every %d seconds", timer->interval);
105    owl_fmtext_append_normal(fm, str);
106    owl_free(str);
107  }
108  owl_fmtext_append_normal(fm, "\n");
109}
110
111void owl_function_show_timers(void) {
112  owl_fmtext fm;
113  GList **timers;
114
115  owl_fmtext_init_null(&fm);
116  owl_fmtext_append_bold(&fm, "Active timers:\n");
117
118  timers = owl_global_get_timerlist(&g);
119  g_list_foreach(*timers, _owl_function_timer_append_fmtext, &fm);
120
121  owl_function_popless_fmtext(&fm);
122  owl_fmtext_cleanup(&fm);
123}
124
125char *owl_function_style_describe(const char *name) {
126  const char *desc;
127  char *s;
128  const owl_style *style;
129  style = owl_global_get_style_by_name(&g, name);
130  if (style) {
131    desc = owl_style_get_description(style);
132  } else {
133    desc = "???";
134  }
135  s = owl_sprintf("%-20s - %s%s", name, 
136                  0==owl_style_validate(style)?"":"[INVALID] ",
137                  desc);
138  return s;
139}
140
141char *owl_function_cmd_describe(const char *name)
142{
143  const owl_cmd *cmd = owl_cmddict_find(owl_global_get_cmddict(&g), name);
144  if (cmd) return owl_cmd_describe(cmd);
145  else return(NULL);
146}
147
148void owl_function_show_command(const char *name)
149{
150  owl_function_help_for_command(name);
151}
152
153void owl_function_show_license(void)
154{
155  const char *text;
156
157  text=""
158    "barnowl version " OWL_VERSION_STRING "\n"
159    "Copyright (c) 2006-2010 The BarnOwl Developers. All rights reserved.\n"
160    "Copyright (c) 2004 James Kretchmar. All rights reserved.\n"
161    "\n"
162    "Redistribution and use in source and binary forms, with or without\n"
163    "modification, are permitted provided that the following conditions are\n"
164    "met:\n"
165    "\n"
166    "   * Redistributions of source code must retain the above copyright\n"
167    "     notice, this list of conditions and the following disclaimer.\n"
168    "\n"
169    "   * Redistributions in binary form must reproduce the above copyright\n"
170    "     notice, this list of conditions and the following disclaimer in\n"
171    "     the documentation and/or other materials provided with the\n"
172    "     distribution.\n"
173    "\n"
174    "   * Redistributions in any form must be accompanied by information on\n"
175    "     how to obtain complete source code for the Owl software and any\n"
176    "     accompanying software that uses the Owl software. The source code\n"
177    "     must either be included in the distribution or be available for no\n"
178    "     more than the cost of distribution plus a nominal fee, and must be\n"
179    "     freely redistributable under reasonable conditions. For an\n"
180    "     executable file, complete source code means the source code for\n"
181    "     all modules it contains. It does not include source code for\n"
182    "     modules or files that typically accompany the major components of\n"
183    "     the operating system on which the executable file runs.\n"
184    "\n"
185    "THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
186    "IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n"
187    "WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR\n"
188    "NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE\n"
189    "LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n"
190    "CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n"
191    "SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR\n"
192    "BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n"
193    "WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE\n"
194    "OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN\n"
195    "IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n";
196  owl_function_popless_text(text);
197}
198
199void owl_function_show_quickstart(void)
200{
201    const char *message =
202    "Move between messages with the arrow keys, and press 'r' to reply.\n"
203    "For more info, press 'h' or visit http://barnowl.mit.edu/\n\n"
204#ifdef HAVE_LIBZEPHYR
205    "@b(Zephyr:)\n"
206    "To send a message to a user, type ':zwrite @b(username)'. You can also\n"
207    "press 'z' and then type the username. To subscribe to a class, type\n"
208    "':sub @b(class)', and then type ':zwrite -c @b(class)' to send.\n\n"
209#endif
210    "@b(AIM:)\n"
211    "Log in to AIM with ':aimlogin @b(screenname)'. Use ':aimwrite @b(screenname)',\n"
212    "or 'a' and then the screen name, to send someone a message.\n\n"
213    ;
214
215    if (owl_perlconfig_is_function("BarnOwl::Hooks::_get_quickstart")) {
216        char *perlquickstart = owl_perlconfig_execute("BarnOwl::Hooks::_get_quickstart()");
217        if (perlquickstart) {
218            char *result = owl_sprintf("%s%s", message, perlquickstart);
219            owl_function_adminmsg("BarnOwl Quickstart", result);
220            owl_free(result);
221            owl_free(perlquickstart);
222            return;
223        }
224    }
225    owl_function_adminmsg("BarnOwl Quickstart", message);
226}
227
228
229/* Create an admin message, append it to the global list of messages
230 * and redisplay if necessary.
231 */
232void owl_function_adminmsg(const char *header, const char *body)
233{
234  owl_message *m;
235
236  m=owl_malloc(sizeof(owl_message));
237  owl_message_create_admin(m, header, body);
238 
239  /* add it to the global list and current view */
240  owl_messagelist_append_element(owl_global_get_msglist(&g), m);
241  owl_view_consider_message(owl_global_get_current_view(&g), m);
242
243  /* do followlast if necessary */
244  if (owl_global_should_followlast(&g)) owl_function_lastmsg_noredisplay();
245
246  /* redisplay etc. */
247  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
248}
249
250/* Create an outgoing zephyr message and return a pointer to it.  Does
251 * not put it on the global queue, use owl_global_messagequeue_addmsg() for
252 * that.
253 */
254owl_message *owl_function_make_outgoing_zephyr(const owl_zwrite *z)
255{
256  owl_message *m;
257
258  /* create the message */
259  m=owl_malloc(sizeof(owl_message));
260  owl_message_create_from_zwrite(m, z, owl_zwrite_get_message(z));
261
262  return(m);
263}
264
265/* Create an outgoing AIM message, returns a pointer to the created
266 * message or NULL if we're not logged into AIM (and thus unable to
267 * create the message).  Does not put it on the global queue.  Use
268 * owl_global_messagequeue_addmsg() for that .
269 */
270owl_message *owl_function_make_outgoing_aim(const char *body, const char *to)
271{
272  owl_message *m;
273
274  /* error if we're not logged into aim */
275  if (!owl_global_is_aimloggedin(&g)) return(NULL);
276 
277  m=owl_malloc(sizeof(owl_message));
278  owl_message_create_aim(m,
279                         owl_global_get_aim_screenname(&g),
280                         to,
281                         body,
282                         OWL_MESSAGE_DIRECTION_OUT,
283                         0);
284  return(m);
285}
286
287/* Create an outgoing loopback message and return a pointer to it.
288 * Does not append it to the global queue, use
289 * owl_global_messagequeue_addmsg() for that.
290 */
291owl_message *owl_function_make_outgoing_loopback(const char *body)
292{
293  owl_message *m;
294
295  /* create the message */
296  m=owl_malloc(sizeof(owl_message));
297  owl_message_create_loopback(m, body);
298  owl_message_set_direction_out(m);
299
300  return(m);
301}
302
303void owl_function_start_edit_win(const char *line, void (*callback)(owl_editwin *), void *data, void (*cleanup)(void *))
304{
305  owl_editwin *e;
306  owl_context *ctx;
307  char *s;
308
309  /* create and setup the editwin */
310  e = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_MULTILINE,
311                                   owl_global_get_msg_history(&g));
312  owl_editwin_set_dotsend(e);
313  s = owl_sprintf("----> %s\n", line);
314  owl_editwin_set_locktext(e, s);
315  owl_free(s);
316
317  owl_editwin_set_cbdata(e, data, cleanup);
318  owl_editwin_set_callback(e, callback);
319  ctx = owl_editcontext_new(OWL_CTX_EDITMULTI, e, "editmulti",
320                            owl_global_deactivate_editcontext, &g);
321  owl_global_push_context_obj(&g, ctx);
322
323}
324
325static void owl_function_write_setup(const char *noun)
326{
327
328  if (!owl_global_get_lockout_ctrld(&g))
329    owl_function_makemsg("Type your %s below.  "
330                         "End with ^D or a dot on a line by itself."
331                         "  ^C will quit.", noun);
332  else
333    owl_function_makemsg("Type your %s below.  "
334                         "End with a dot on a line by itself.  ^C will quit.",
335                         noun);
336}
337
338void owl_function_zwrite_setup(owl_zwrite *z)
339{
340  /* send a ping if necessary */
341  if (owl_global_is_txping(&g)) {
342    owl_zwrite_send_ping(z);
343  }
344
345
346  owl_function_write_setup("zephyr");
347  owl_function_start_edit_win(z->zwriteline,
348                              &owl_callback_zwrite,
349                              z, (void(*)(void*))owl_zwrite_delete);
350}
351
352void owl_function_aimwrite_setup(const char *line)
353{
354  owl_function_write_setup("message");
355  owl_function_start_edit_win(line,
356                              &owl_callback_aimwrite,
357                              owl_strdup(line),
358                              owl_free);
359
360}
361
362void owl_function_loopwrite_setup(void)
363{
364  owl_function_write_setup("message");
365  owl_function_start_edit_win("loopwrite",
366                              &owl_callback_loopwrite,
367                              NULL, NULL);
368}
369
370void owl_callback_zwrite(owl_editwin *e) {
371  owl_zwrite *z = owl_editwin_get_cbdata(e);
372  owl_function_zwrite(z, owl_editwin_get_text(e));
373}
374
375/* send, log and display an outgoing zephyr.  If 'msg' is NULL
376 * the message is expected to be set from the zwrite line itself
377 */
378#ifdef HAVE_LIBZEPHYR
379void owl_function_zwrite(owl_zwrite *z, const char *msg)
380{
381  owl_message *m;
382  int ret;
383
384  if(strcmp(z->cmd, "zcrypt") == 0) {
385    owl_function_zcrypt(z, msg);
386    return;
387  }
388
389  /* create the zwrite and send the message */
390  owl_zwrite_populate_zsig(z);
391  if (msg) {
392    owl_zwrite_set_message(z, msg);
393  }
394  ret = owl_zwrite_send_message(z);
395  if (ret != 0) {
396    owl_function_makemsg("Error sending zephyr: %s", error_message(ret));
397    return;
398  }
399  owl_function_makemsg("Waiting for ack...");
400
401  /* If it's personal */
402  if (owl_zwrite_is_personal(z)) {
403    /* create the outgoing message */
404    m=owl_function_make_outgoing_zephyr(z);
405
406    if (m) {
407      owl_global_messagequeue_addmsg(&g, m);
408    } else {
409      owl_function_error("Could not create outgoing zephyr message");
410    }
411  }
412}
413#else
414void owl_function_zwrite(owl_zwrite *z, const char *msg) {
415}
416#endif
417
418/* send, log and display an outgoing zcrypt zephyr.  If 'msg' is NULL
419 * the message is expected to be set from the zwrite line itself
420 */
421void owl_function_zcrypt(owl_zwrite *z, const char *msg)
422{
423  char *cryptmsg;
424  owl_message *m;
425  const char *argv[7];
426  char *zcrypt;
427  int rv, status;
428  char *old_msg;
429
430  /* create the zwrite and send the message */
431  owl_zwrite_populate_zsig(z);
432  if (msg) {
433    owl_zwrite_set_message(z, msg);
434  }
435  old_msg = owl_strdup(owl_zwrite_get_message(z));
436
437  zcrypt = owl_sprintf("%s/zcrypt", owl_get_bindir());
438  argv[0] = "zcrypt";
439  argv[1] = "-E";
440  argv[2] = "-c"; argv[3] = owl_zwrite_get_class(z);
441  argv[4] = "-i"; argv[5] = owl_zwrite_get_instance(z);
442  argv[6] = NULL;
443
444  rv = call_filter(zcrypt, argv, owl_zwrite_get_message(z), &cryptmsg, &status);
445
446  owl_free(zcrypt);
447
448  if (rv || status) {
449    if(cryptmsg) owl_free(cryptmsg);
450    owl_free(old_msg);
451    owl_function_error("Error in zcrypt, possibly no key found.  Message not sent.");
452    owl_function_beep();
453    return;
454  }
455
456  owl_zwrite_set_message_raw(z, cryptmsg);
457  owl_zwrite_set_opcode(z, "crypt");
458
459  owl_zwrite_send_message(z);
460  owl_function_makemsg("Waiting for ack...");
461
462  /* If it's personal */
463  if (owl_zwrite_is_personal(z)) {
464    /* Create the outgoing message. Restore the un-crypted message for display. */
465    owl_zwrite_set_message_raw(z, old_msg);
466    m=owl_function_make_outgoing_zephyr(z);
467    if (m) {
468      owl_global_messagequeue_addmsg(&g, m);
469    } else {
470      owl_function_error("Could not create outgoing zephyr message");
471    }
472  }
473
474  /* free the zwrite */
475  owl_free(cryptmsg);
476}
477
478void owl_callback_aimwrite(owl_editwin *e) {
479  char *command = owl_editwin_get_cbdata(e);
480  owl_function_aimwrite(command,
481                        owl_editwin_get_text(e));
482}
483
484void owl_function_aimwrite(const char *line, const char *msg)
485{
486  int ret;
487  const char *to;
488  char *format_msg;
489  owl_message *m;
490
491  to = line + 9;
492
493  /* make a formatted copy of the message */
494  format_msg=owl_strdup(msg);
495  owl_text_wordunwrap(format_msg);
496 
497  /* send the message */
498  ret=owl_aim_send_im(to, format_msg);
499  if (!ret) {
500    owl_function_makemsg("AIM message sent.");
501  } else {
502    owl_function_error("Could not send AIM message.");
503  }
504
505  /* create the outgoing message */
506  m=owl_function_make_outgoing_aim(msg, to);
507
508  if (m) {
509    owl_global_messagequeue_addmsg(&g, m);
510  } else {
511    owl_function_error("Could not create outgoing AIM message");
512  }
513
514  owl_free(format_msg);
515}
516
517void owl_function_send_aimawymsg(const char *to, const char *msg)
518{
519  int ret;
520  char *format_msg;
521  owl_message *m;
522
523  /* make a formatted copy of the message */
524  format_msg=owl_strdup(msg);
525  owl_text_wordunwrap(format_msg);
526 
527  /* send the message */
528  ret=owl_aim_send_awaymsg(to, format_msg);
529  if (!ret) {
530    /* owl_function_makemsg("AIM message sent."); */
531  } else {
532    owl_function_error("Could not send AIM message.");
533  }
534
535  /* create the message */
536  m=owl_function_make_outgoing_aim(msg, to);
537  if (m) {
538    owl_global_messagequeue_addmsg(&g, m);
539  } else {
540    owl_function_error("Could not create AIM message");
541  }
542  owl_free(format_msg);
543}
544
545void owl_callback_loopwrite(owl_editwin *e) {
546  owl_function_loopwrite(owl_editwin_get_text(e));
547}
548
549void owl_function_loopwrite(const char *msg)
550{
551  owl_message *min, *mout;
552
553  /* create a message and put it on the message queue.  This simulates
554   * an incoming message */
555  min=owl_malloc(sizeof(owl_message));
556  mout=owl_function_make_outgoing_loopback(msg);
557
558  if (owl_global_is_displayoutgoing(&g)) {
559    owl_global_messagequeue_addmsg(&g, mout);
560  } else {
561    owl_message_delete(mout);
562  }
563
564  owl_message_create_loopback(min, msg);
565  owl_message_set_direction_in(min);
566  owl_global_messagequeue_addmsg(&g, min);
567
568  /* fake a makemsg */
569  owl_function_makemsg("loopback message sent");
570}
571
572/* If filter is non-null, looks for the next message matching
573 * that filter.  If skip_deleted, skips any deleted messages.
574 * If last_if_none, will stop at the last message in the view
575 * if no matching messages are found.  */
576void owl_function_nextmsg_full(const char *filter, int skip_deleted, int last_if_none)
577{
578  int curmsg, i, viewsize, found;
579  const owl_view *v;
580  const owl_filter *f = NULL;
581  const owl_message *m;
582
583  v=owl_global_get_current_view(&g);
584
585  if (filter) {
586    f=owl_global_get_filter(&g, filter);
587    if (!f) {
588      owl_function_error("No %s filter defined", filter);
589      return;
590    }
591  }
592
593  curmsg=owl_global_get_curmsg(&g);
594  viewsize=owl_view_get_size(v);
595  found=0;
596
597  /* just check to make sure we're in bounds... */
598  if (curmsg>viewsize-1) curmsg=viewsize-1;
599  if (curmsg<0) curmsg=0;
600
601  for (i=curmsg+1; i<viewsize; i++) {
602    m=owl_view_get_element(v, i);
603    if (skip_deleted && owl_message_is_delete(m)) continue;
604    if (f && !owl_filter_message_match(f, m)) continue;
605    found = 1;
606    break;
607  }
608
609  if (i>owl_view_get_size(v)-1) i=owl_view_get_size(v)-1;
610  if (i<0) i=0;
611
612  if (!found) {
613    owl_function_makemsg("already at last%s message%s%s%s",
614                         skip_deleted?" non-deleted":"",
615                         filter?" in ":"", filter?filter:"",
616                         owl_mainwin_is_curmsg_truncated(owl_global_get_mainwin(&g)) ?
617                         ", press Enter to scroll" : "");
618    /* if (!skip_deleted) owl_function_beep(); */
619  }
620
621  if (last_if_none || found) {
622    owl_global_set_curmsg(&g, i);
623    owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
624    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
625    owl_global_set_direction_downwards(&g);
626  }
627}
628
629void owl_function_prevmsg_full(const char *filter, int skip_deleted, int first_if_none)
630{
631  int curmsg, i, found;
632  const owl_view *v;
633  const owl_filter *f = NULL;
634  const owl_message *m;
635
636  v=owl_global_get_current_view(&g);
637
638  if (filter) {
639    f=owl_global_get_filter(&g, filter);
640    if (!f) {
641      owl_function_error("No %s filter defined", filter);
642      return;
643    }
644  }
645
646  curmsg=owl_global_get_curmsg(&g);
647  found=0;
648
649  /* just check to make sure we're in bounds... */
650  if (curmsg<0) curmsg=0;
651
652  for (i=curmsg-1; i>=0; i--) {
653    m=owl_view_get_element(v, i);
654    if (skip_deleted && owl_message_is_delete(m)) continue;
655    if (f && !owl_filter_message_match(f, m)) continue;
656    found = 1;
657    break;
658  }
659
660  if (i<0) i=0;
661
662  if (!found) {
663    owl_function_makemsg("already at first%s message%s%s",
664                         skip_deleted?" non-deleted":"",
665                         filter?" in ":"", filter?filter:"");
666    /* if (!skip_deleted) owl_function_beep(); */
667  }
668
669  if (first_if_none || found) {
670    owl_global_set_curmsg(&g, i);
671    owl_function_calculate_topmsg(OWL_DIRECTION_UPWARDS);
672    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
673    owl_global_set_direction_upwards(&g);
674  }
675}
676
677void owl_function_nextmsg(void)
678{
679  owl_function_nextmsg_full(NULL, 0, 1);
680}
681
682void owl_function_prevmsg(void)
683{
684  owl_function_prevmsg_full(NULL, 0, 1);
685}
686
687void owl_function_nextmsg_notdeleted(void)
688{
689  owl_function_nextmsg_full(NULL, 1, 1);
690}
691
692void owl_function_prevmsg_notdeleted(void)
693{
694  owl_function_prevmsg_full(NULL, 1, 1);
695}
696
697/* if move_after is 1, moves after the delete */
698void owl_function_deletecur(int move_after)
699{
700  int curmsg;
701  owl_view *v;
702
703  v=owl_global_get_current_view(&g);
704
705  /* bail if there's no current message */
706  if (owl_view_get_size(v) < 1) {
707    owl_function_error("No current message to delete");
708    return;
709  }
710
711  /* mark the message for deletion */
712  curmsg=owl_global_get_curmsg(&g);
713  owl_view_delete_element(v, curmsg);
714
715  if (move_after) {
716    /* move the poiner in the appropriate direction
717     * to the next undeleted msg */
718    if (owl_global_get_direction(&g)==OWL_DIRECTION_UPWARDS) {
719      owl_function_prevmsg_notdeleted();
720    } else {
721      owl_function_nextmsg_notdeleted();
722    }
723  }
724}
725
726void owl_function_undeletecur(int move_after)
727{
728  int curmsg;
729  owl_view *v;
730
731  v=owl_global_get_current_view(&g);
732 
733  if (owl_view_get_size(v) < 1) {
734    owl_function_error("No current message to undelete");
735    return;
736  }
737  curmsg=owl_global_get_curmsg(&g);
738
739  owl_view_undelete_element(v, curmsg);
740
741  if (move_after) {
742    if (owl_global_get_direction(&g)==OWL_DIRECTION_UPWARDS) {
743      if (curmsg>0) {
744        owl_function_prevmsg();
745      } else {
746        owl_function_nextmsg();
747      }
748    } else {
749      owl_function_nextmsg();
750    }
751  }
752
753  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
754}
755
756void owl_function_expunge(void)
757{
758  int curmsg;
759  const owl_message *m;
760  owl_messagelist *ml;
761  owl_view *v;
762  int lastmsgid=0;
763
764  curmsg=owl_global_get_curmsg(&g);
765  v=owl_global_get_current_view(&g);
766  ml=owl_global_get_msglist(&g);
767
768  m=owl_view_get_element(v, curmsg);
769  if (m) lastmsgid = owl_message_get_id(m);
770
771  /* expunge the message list */
772  owl_messagelist_expunge(ml);
773
774  /* update all views (we only have one right now) */
775  owl_view_recalculate(v);
776
777  /* find where the new position should be
778     (as close as possible to where we last where) */
779  curmsg = owl_view_get_nearest_to_msgid(v, lastmsgid);
780  if (curmsg>owl_view_get_size(v)-1) curmsg = owl_view_get_size(v)-1;
781  if (curmsg<0) curmsg = 0;
782  owl_global_set_curmsg(&g, curmsg);
783  owl_function_calculate_topmsg(OWL_DIRECTION_NONE);
784  /* if there are no messages set the direction to down in case we
785     delete everything upwards */
786  owl_global_set_direction_downwards(&g);
787 
788  owl_function_makemsg("Messages expunged");
789  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
790}
791
792void owl_function_firstmsg(void)
793{
794  owl_global_set_curmsg(&g, 0);
795  owl_global_set_topmsg(&g, 0);
796  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
797  owl_global_set_direction_downwards(&g);
798}
799
800void owl_function_lastmsg_noredisplay(void)
801{
802  int oldcurmsg, curmsg;
803  const owl_view *v;
804
805  v=owl_global_get_current_view(&g);
806  oldcurmsg=owl_global_get_curmsg(&g);
807  curmsg=owl_view_get_size(v)-1; 
808  if (curmsg<0) curmsg=0;
809  owl_global_set_curmsg(&g, curmsg);
810  if (oldcurmsg < curmsg) {
811    owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
812  } else if (curmsg<owl_view_get_size(v)) {
813    /* If already at the end, blank the screen and move curmsg
814     * past the end of the messages. */
815    owl_global_set_topmsg(&g, curmsg+1);
816    owl_global_set_curmsg(&g, curmsg+1);
817  } 
818  /* owl_mainwin_redisplay(owl_global_get_mainwin(&g)); */
819  owl_global_set_direction_downwards(&g);
820}
821
822void owl_function_lastmsg(void)
823{
824  owl_function_lastmsg_noredisplay();
825  owl_mainwin_redisplay(owl_global_get_mainwin(&g)); 
826}
827
828void owl_function_shift_right(void)
829{
830  owl_global_set_rightshift(&g, owl_global_get_rightshift(&g)+10);
831}
832
833void owl_function_shift_left(void)
834{
835  int shift;
836
837  shift=owl_global_get_rightshift(&g);
838  if (shift > 0) {
839    owl_global_set_rightshift(&g, MAX(shift - 10, 0));
840  } else {
841    owl_function_beep();
842    owl_function_makemsg("Already full left");
843  }
844}
845
846void owl_function_unsuball(void)
847{
848  unsuball();
849  owl_function_makemsg("Unsubscribed from all messages.");
850}
851
852
853/* Load zephyr subscriptions from the named 'file' and load zephyr's
854 * default subscriptions as well.  An error message is printed if
855 * 'file' can't be opened or if zephyr reports an error in
856 * subscribing.
857 *
858 * If 'file' is NULL, this look for the default filename
859 * $HOME/.zephyr.subs.  If the file can not be opened in this case
860 * only, no error message is printed.
861 */
862void owl_function_loadsubs(const char *file)
863{
864  int ret, ret2;
865  const char *foo;
866  char *path;
867
868  if (file==NULL) {
869    ret=owl_zephyr_loadsubs(NULL, 0);
870  } else {
871    path = owl_util_makepath(file);
872    ret=owl_zephyr_loadsubs(path, 1);
873    owl_free(path);
874  }
875
876  /* for backwards compatibility for now */
877  ret2=owl_zephyr_loaddefaultsubs();
878
879  if (!owl_context_is_interactive(owl_global_get_context(&g))) return;
880
881  foo=file?file:"file";
882  if (ret==0 && ret2==0) {
883    if (!file) {
884      owl_function_makemsg("Subscribed to messages.");
885    } else {
886      owl_function_makemsg("Subscribed to messages from %s", file);
887    }
888  } else if (ret==-1) {
889    owl_function_error("Could not read %s", foo);
890  } else {
891    owl_function_error("Error subscribing to messages");
892  }
893}
894
895void owl_function_loadloginsubs(const char *file)
896{
897  int ret;
898
899  ret=owl_zephyr_loadloginsubs(file);
900
901  if (!owl_context_is_interactive(owl_global_get_context(&g))) return;
902  if (ret==0) {
903  } else if (ret==-1) {
904    owl_function_error("Could not open file for login subscriptions.");
905  } else {
906    owl_function_error("Error subscribing to login messages from file.");
907  }
908}
909
910void owl_callback_aimlogin(owl_editwin *e) {
911  char *user = owl_editwin_get_cbdata(e);
912  owl_function_aimlogin(user,
913                        owl_editwin_get_text(e));
914}
915
916void owl_function_aimlogin(const char *user, const char *passwd) {
917  int ret;
918
919  /* clear the buddylist */
920  owl_buddylist_clear(owl_global_get_buddylist(&g));
921
922  /* try to login */
923  ret=owl_aim_login(user, passwd);
924  if (ret) owl_function_makemsg("Warning: login for %s failed.\n", user);
925}
926
927void owl_function_suspend(void)
928{
929  endwin();
930  printf("\n");
931  kill(getpid(), SIGSTOP);
932
933  /* resize to reinitialize all the windows when we come back */
934  owl_command_resize();
935}
936
937void owl_function_zaway_toggle(void)
938{
939  if (!owl_global_is_zaway(&g)) {
940    owl_global_set_zaway_msg(&g, owl_global_get_zaway_msg_default(&g));
941    owl_function_zaway_on();
942  } else {
943    owl_function_zaway_off();
944  }
945}
946
947void owl_function_zaway_on(void)
948{
949  owl_global_set_zaway_on(&g);
950  owl_function_makemsg("zaway set (%s)", owl_global_get_zaway_msg(&g));
951}
952
953void owl_function_zaway_off(void)
954{
955  owl_global_set_zaway_off(&g);
956  owl_function_makemsg("zaway off");
957}
958
959void owl_function_aaway_toggle(void)
960{
961  if (!owl_global_is_aaway(&g)) {
962    owl_global_set_aaway_msg(&g, owl_global_get_aaway_msg_default(&g));
963    owl_function_aaway_on();
964  } else {
965    owl_function_aaway_off();
966  }
967}
968
969void owl_function_aaway_on(void)
970{
971  owl_global_set_aaway_on(&g);
972  /* owl_aim_set_awaymsg(owl_global_get_zaway_msg(&g)); */
973  owl_function_makemsg("AIM away set (%s)", owl_global_get_aaway_msg(&g));
974}
975
976void owl_function_aaway_off(void)
977{
978  owl_global_set_aaway_off(&g);
979  /* owl_aim_set_awaymsg(""); */
980  owl_function_makemsg("AIM away off");
981}
982
983void owl_function_quit(void)
984{
985  char *ret;
986 
987  /* zlog out if we need to */
988  if (owl_global_is_havezephyr(&g) &&
989      owl_global_is_shutdownlogout(&g)) {
990    owl_zephyr_zlog_out();
991  }
992
993  /* execute the commands in shutdown */
994  ret = owl_perlconfig_execute("BarnOwl::Hooks::_shutdown();");
995  if (ret) owl_free(ret);
996
997  /* signal our child process, if any */
998  if (owl_global_get_newmsgproc_pid(&g)) {
999    kill(owl_global_get_newmsgproc_pid(&g), SIGHUP);
1000  }
1001 
1002  /* Quit AIM */
1003  if (owl_global_is_aimloggedin(&g)) {
1004    owl_aim_logout();
1005  }
1006
1007  owl_function_debugmsg("Quitting Owl");
1008  owl_select_quit_loop();
1009}
1010
1011void owl_function_calculate_topmsg(int direction)
1012{
1013  int recwinlines, topmsg, curmsg;
1014  const owl_view *v;
1015
1016  v=owl_global_get_current_view(&g);
1017  curmsg=owl_global_get_curmsg(&g);
1018  topmsg=owl_global_get_topmsg(&g);
1019  recwinlines=owl_global_get_recwin_lines(&g);
1020
1021  /*
1022  if (owl_view_get_size(v) < 1) {
1023    return;
1024  }
1025  */
1026
1027  switch (owl_global_get_scrollmode(&g)) {
1028  case OWL_SCROLLMODE_TOP:
1029    topmsg = owl_function_calculate_topmsg_top(direction, v, curmsg, topmsg, recwinlines);
1030    break;
1031  case OWL_SCROLLMODE_NEARTOP:
1032    topmsg = owl_function_calculate_topmsg_neartop(direction, v, curmsg, topmsg, recwinlines);
1033    break;
1034  case OWL_SCROLLMODE_CENTER:
1035    topmsg = owl_function_calculate_topmsg_center(direction, v, curmsg, topmsg, recwinlines);
1036    break;
1037  case OWL_SCROLLMODE_PAGED:
1038    topmsg = owl_function_calculate_topmsg_paged(direction, v, curmsg, topmsg, recwinlines, 0);
1039    break;
1040  case OWL_SCROLLMODE_PAGEDCENTER:
1041    topmsg = owl_function_calculate_topmsg_paged(direction, v, curmsg, topmsg, recwinlines, 1);
1042    break;
1043  case OWL_SCROLLMODE_NORMAL:
1044  default:
1045    topmsg = owl_function_calculate_topmsg_normal(direction, v, curmsg, topmsg, recwinlines);
1046  }
1047  owl_function_debugmsg("Calculated a topmsg of %i", topmsg);
1048  owl_global_set_topmsg(&g, topmsg);
1049}
1050
1051/* Returns what the new topmsg should be. 
1052 * Passed the last direction of movement,
1053 * the current view,
1054 * the current message number in the view,
1055 * the top message currently being displayed,
1056 * and the number of lines in the recwin.
1057 */
1058int owl_function_calculate_topmsg_top(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
1059{
1060  return(curmsg);
1061}
1062
1063int owl_function_calculate_topmsg_neartop(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
1064{
1065  if (curmsg>0 
1066      && (owl_message_get_numlines(owl_view_get_element(v, curmsg-1))
1067          <  recwinlines/2)) {
1068    return(curmsg-1);
1069  } else {
1070    return(curmsg);
1071  }
1072}
1073 
1074int owl_function_calculate_topmsg_center(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
1075{
1076  int i, last, lines;
1077
1078  last = curmsg;
1079  lines = 0;
1080  for (i=curmsg-1; i>=0; i--) {
1081    lines += owl_message_get_numlines(owl_view_get_element(v, i));
1082    if (lines > recwinlines/2) break;
1083    last = i;
1084  }
1085  return(last);
1086}
1087 
1088int owl_function_calculate_topmsg_paged(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines, int center_on_page)
1089{
1090  int i, last, lines, savey;
1091 
1092  /* If we're off the top of the screen, scroll up such that the
1093   * curmsg is near the botton of the screen. */
1094  if (curmsg < topmsg) {
1095    last = curmsg;
1096    lines = 0;
1097    for (i=curmsg; i>=0; i--) {
1098      lines += owl_message_get_numlines(owl_view_get_element(v, i));
1099      if (lines > recwinlines) break;
1100    last = i;
1101    }
1102    if (center_on_page) {
1103      return(owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines));
1104    } else {
1105      return(last);
1106    }
1107  }
1108
1109  /* Find number of lines from top to bottom of curmsg (store in savey) */
1110  savey=0;
1111  for (i=topmsg; i<=curmsg; i++) {
1112    savey+=owl_message_get_numlines(owl_view_get_element(v, i));
1113  }
1114
1115  /* if we're off the bottom of the screen, scroll down */
1116  if (savey > recwinlines) {
1117    if (center_on_page) {
1118      return(owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines));
1119    } else {
1120      return(curmsg);
1121    }
1122  }
1123
1124  /* else just stay as we are... */
1125  return(topmsg);
1126}
1127
1128int owl_function_calculate_topmsg_normal(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
1129{
1130  int savey, i, foo, y;
1131
1132  if (curmsg<0) return(topmsg);
1133   
1134  /* If we're off the top of the screen then center */
1135  if (curmsg<topmsg) {
1136    topmsg=owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines);
1137  }
1138
1139  /* If curmsg is so far past topmsg that there are more messages than
1140     lines, skip the line counting that follows because we're
1141     certainly off screen.  */
1142  savey=curmsg-topmsg;
1143  if (savey <= recwinlines) {
1144    /* Find number of lines from top to bottom of curmsg (store in savey) */
1145    savey = 0;
1146    for (i=topmsg; i<=curmsg; i++) {
1147      savey+=owl_message_get_numlines(owl_view_get_element(v, i));
1148    }
1149  }
1150
1151  /* If we're off the bottom of the screen, set the topmsg to curmsg
1152   * and scroll upwards */
1153  if (savey > recwinlines) {
1154    topmsg=curmsg;
1155    savey=owl_message_get_numlines(owl_view_get_element(v, curmsg));
1156    direction=OWL_DIRECTION_UPWARDS;
1157  }
1158 
1159  /* If our bottom line is less than 1/4 down the screen then scroll up */
1160  if (direction == OWL_DIRECTION_UPWARDS || direction == OWL_DIRECTION_NONE) {
1161    if (savey < (recwinlines / 4)) {
1162      y=0;
1163      for (i=curmsg; i>=0; i--) {
1164        foo=owl_message_get_numlines(owl_view_get_element(v, i));
1165        /* will we run the curmsg off the screen? */
1166        if ((foo+y) >= recwinlines) {
1167          i++;
1168          if (i>curmsg) i=curmsg;
1169          break;
1170        }
1171        /* have saved 1/2 the screen space? */
1172        y+=foo;
1173        if (y > (recwinlines / 2)) break;
1174      }
1175      if (i<0) i=0;
1176      return(i);
1177    }
1178  }
1179
1180  if (direction == OWL_DIRECTION_DOWNWARDS || direction == OWL_DIRECTION_NONE) {
1181    /* If curmsg bottom line is more than 3/4 down the screen then scroll down */
1182    if (savey > ((recwinlines * 3)/4)) {
1183      y=0;
1184      /* count lines from the top until we can save 1/2 the screen size */
1185      for (i=topmsg; i<curmsg; i++) {
1186        y+=owl_message_get_numlines(owl_view_get_element(v, i));
1187        if (y > (recwinlines / 2)) break;
1188      }
1189      if (i==curmsg) {
1190        i--;
1191      }
1192      return(i+1);
1193    }
1194  }
1195
1196  return(topmsg);
1197}
1198
1199void owl_function_resize(void)
1200{
1201  owl_global_set_resize_pending(&g);
1202}
1203
1204void owl_function_debugmsg(const char *fmt, ...)
1205{
1206  FILE *file;
1207  time_t now;
1208  va_list ap;
1209  va_start(ap, fmt);
1210
1211  if (!owl_global_is_debug_fast(&g))
1212    return;
1213
1214  file = owl_global_get_debug_file_handle(&g);
1215  if (!file) /* XXX should report this */
1216    return;
1217
1218  now = time(NULL);
1219
1220  fprintf(file, "[%d -  %.24s - %lds]: ",
1221          (int) getpid(), ctime(&now), now - owl_global_get_starttime(&g));
1222  vfprintf(file, fmt, ap);
1223  putc('\n', file);
1224  fflush(file);
1225
1226  va_end(ap);
1227}
1228
1229void owl_function_beep(void)
1230{
1231  if (owl_global_is_bell(&g)) {
1232    beep();
1233  }
1234}
1235
1236int owl_function_subscribe(const char *class, const char *inst, const char *recip)
1237{
1238  int ret;
1239
1240  ret=owl_zephyr_sub(class, inst, recip);
1241  if (ret) {
1242    owl_function_error("Error subscribing.");
1243  } else {
1244    owl_function_makemsg("Subscribed.");
1245  }
1246  return(ret);
1247}
1248
1249void owl_function_unsubscribe(const char *class, const char *inst, const char *recip)
1250{
1251  int ret;
1252
1253  ret=owl_zephyr_unsub(class, inst, recip);
1254  if (ret) {
1255    owl_function_error("Error subscribing.");
1256  } else {
1257    owl_function_makemsg("Unsubscribed.");
1258  }
1259}
1260
1261static void _dirty_everything(owl_window *w) {
1262  if (!owl_window_is_realized(w))
1263    return;
1264  owl_window_dirty(w);
1265  owl_window_children_foreach(w, (GFunc)_dirty_everything, NULL);
1266}
1267
1268void owl_function_full_redisplay(void)
1269{
1270  /* Ask every widget to redraw itself. */
1271  _dirty_everything(owl_window_get_screen());
1272  /* Force ncurses to redisplay everything. */
1273  clearok(stdscr, TRUE);
1274}
1275
1276void owl_function_popless_text(const char *text)
1277{
1278  owl_popwin *pw;
1279  owl_viewwin *v;
1280
1281  if (owl_global_get_popwin(&g) || owl_global_get_viewwin(&g)) {
1282    owl_function_error("Popwin already in use.");
1283    return;
1284  }
1285  pw = owl_popwin_new();
1286  owl_global_set_popwin(&g, pw);
1287  owl_popwin_up(pw);
1288
1289  v = owl_viewwin_new_text(owl_popwin_get_content(pw), text);
1290  owl_global_set_viewwin(&g, v);
1291
1292  owl_global_push_context(&g, OWL_CTX_POPLESS, v, "popless", NULL);
1293}
1294
1295void owl_function_popless_fmtext(const owl_fmtext *fm)
1296{
1297  owl_popwin *pw;
1298  owl_viewwin *v;
1299
1300  if (owl_global_get_popwin(&g) || owl_global_get_viewwin(&g)) {
1301    owl_function_error("Popwin already in use.");
1302    return;
1303  }
1304  pw = owl_popwin_new();
1305  owl_global_set_popwin(&g, pw);
1306  owl_popwin_up(pw);
1307
1308  v = owl_viewwin_new_fmtext(owl_popwin_get_content(pw), fm);
1309  owl_global_set_viewwin(&g, v);
1310
1311  owl_global_push_context(&g, OWL_CTX_POPLESS, v, "popless", NULL);
1312}
1313
1314void owl_function_popless_file(const char *filename)
1315{
1316  owl_fmtext fm;
1317  FILE *file;
1318  char *s = NULL;
1319
1320  file=fopen(filename, "r");
1321  if (!file) {
1322    owl_function_error("Could not open file: %s", filename);
1323    return;
1324  }
1325
1326  owl_fmtext_init_null(&fm);
1327  while (owl_getline(&s, file))
1328    owl_fmtext_append_normal(&fm, s);
1329  owl_free(s);
1330
1331  owl_function_popless_fmtext(&fm);
1332  owl_fmtext_cleanup(&fm);
1333  fclose(file);
1334}
1335
1336void owl_function_about(void)
1337{
1338  owl_function_popless_text(
1339    "This is barnowl version " OWL_VERSION_STRING ".\n\n"
1340    "barnowl is a fork of the Owl zephyr client, written and\n"
1341    "maintained by Alejandro Sedeno and Nelson Elhage at the\n"
1342    "Massachusetts Institute of Technology. \n"
1343    "\n"
1344    "Owl was written by James Kretchmar. The first version, 0.5, was\n"
1345    "released in March 2002.\n"
1346    "\n"
1347    "The name 'owl' was chosen in reference to the owls in the\n"
1348    "Harry Potter novels, who are tasked with carrying messages\n"
1349    "between Witches and Wizards. The name 'barnowl' was chosen\n"
1350    "because we feel our owls should live closer to our ponies.\n"
1351    "\n"
1352    "Copyright (c) 2006-2010 The BarnOwl Developers. All rights reserved.\n"
1353    "Copyright (c) 2004 James Kretchmar. All rights reserved.\n"
1354    "Copyright 2002 Massachusetts Institute of Technology\n"
1355    "\n"
1356    "This program is free software. You can redistribute it and/or\n"
1357    "modify under the terms of the Sleepycat License. Use the \n"
1358    "':show license' command to display the full license\n"
1359  );
1360}
1361
1362void owl_function_info(void)
1363{
1364  const owl_message *m;
1365  owl_fmtext fm, attrfm;
1366  const owl_view *v;
1367#ifdef HAVE_LIBZEPHYR
1368  const ZNotice_t *n;
1369#endif
1370
1371  owl_fmtext_init_null(&fm);
1372 
1373  v=owl_global_get_current_view(&g);
1374  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1375  if (!m || owl_view_get_size(v)==0) {
1376    owl_function_error("No message selected\n");
1377    return;
1378  }
1379
1380  owl_fmtext_append_bold(&fm, "General Information:\n");
1381  owl_fmtext_appendf_normal(&fm, "  Msg Id    : %i\n", owl_message_get_id(m));
1382
1383  owl_fmtext_append_normal(&fm, "  Type      : ");
1384  owl_fmtext_append_bold(&fm, owl_message_get_type(m));
1385  owl_fmtext_append_normal(&fm, "\n");
1386
1387  if (owl_message_is_direction_in(m)) {
1388    owl_fmtext_append_normal(&fm, "  Direction : in\n");
1389  } else if (owl_message_is_direction_out(m)) {
1390    owl_fmtext_append_normal(&fm, "  Direction : out\n");
1391  } else if (owl_message_is_direction_none(m)) {
1392    owl_fmtext_append_normal(&fm, "  Direction : none\n");
1393  } else {
1394    owl_fmtext_append_normal(&fm, "  Direction : unknown\n");
1395  }
1396
1397  owl_fmtext_appendf_normal(&fm, "  Time      : %s\n", owl_message_get_timestr(m));
1398
1399  if (!owl_message_is_type_admin(m)) {
1400    owl_fmtext_appendf_normal(&fm, "  Sender    : %s\n", owl_message_get_sender(m));
1401    owl_fmtext_appendf_normal(&fm, "  Recipient : %s\n", owl_message_get_recipient(m));
1402  }
1403
1404  if (owl_message_is_type_zephyr(m)) {
1405    owl_fmtext_append_bold(&fm, "\nZephyr Specific Information:\n");
1406   
1407    owl_fmtext_appendf_normal(&fm, "  Class     : %s\n", owl_message_get_class(m));
1408    owl_fmtext_appendf_normal(&fm, "  Instance  : %s\n", owl_message_get_instance(m));
1409    owl_fmtext_appendf_normal(&fm, "  Opcode    : %s\n", owl_message_get_opcode(m));
1410#ifdef HAVE_LIBZEPHYR
1411    if (owl_message_is_direction_in(m)) {
1412      char *ptr, tmpbuff[1024];
1413      int i, j, fields, len;
1414
1415      n=owl_message_get_notice(m);
1416
1417      if (!owl_message_is_pseudo(m)) {
1418        owl_fmtext_append_normal(&fm, "  Kind      : ");
1419        if (n->z_kind==UNSAFE) {
1420          owl_fmtext_append_normal(&fm, "UNSAFE\n");
1421        } else if (n->z_kind==UNACKED) {
1422          owl_fmtext_append_normal(&fm, "UNACKED\n");
1423        } else if (n->z_kind==ACKED) {
1424          owl_fmtext_append_normal(&fm, "ACKED\n");
1425        } else if (n->z_kind==HMACK) {
1426          owl_fmtext_append_normal(&fm, "HMACK\n");
1427        } else if (n->z_kind==HMCTL) {
1428          owl_fmtext_append_normal(&fm, "HMCTL\n");
1429        } else if (n->z_kind==SERVACK) {
1430          owl_fmtext_append_normal(&fm, "SERVACK\n");
1431        } else if (n->z_kind==SERVNAK) {
1432          owl_fmtext_append_normal(&fm, "SERVNACK\n");
1433        } else if (n->z_kind==CLIENTACK) {
1434          owl_fmtext_append_normal(&fm, "CLIENTACK\n");
1435        } else if (n->z_kind==STAT) {
1436          owl_fmtext_append_normal(&fm, "STAT\n");
1437        } else {
1438          owl_fmtext_append_normal(&fm, "ILLEGAL VALUE\n");
1439        }
1440      }
1441      owl_fmtext_appendf_normal(&fm, "  Host      : %s\n", owl_message_get_hostname(m));
1442
1443      if (!owl_message_is_pseudo(m)) {
1444        owl_fmtext_append_normal(&fm, "\n");
1445        owl_fmtext_appendf_normal(&fm, "  Port      : %i\n", ntohs(n->z_port));
1446        owl_fmtext_appendf_normal(&fm, "  Auth      : %s\n", owl_zephyr_get_authstr(n));
1447
1448        /* FIXME make these more descriptive */
1449        owl_fmtext_appendf_normal(&fm, "  Checkd Ath: %i\n", n->z_checked_auth);
1450        owl_fmtext_appendf_normal(&fm, "  Multi notc: %s\n", n->z_multinotice);
1451        owl_fmtext_appendf_normal(&fm, "  Num other : %i\n", n->z_num_other_fields);
1452        owl_fmtext_appendf_normal(&fm, "  Msg Len   : %i\n", n->z_message_len);
1453
1454        fields=owl_zephyr_get_num_fields(n);
1455        owl_fmtext_appendf_normal(&fm, "  Fields    : %i\n", fields);
1456
1457        for (i=0; i<fields; i++) {
1458          ptr=owl_zephyr_get_field_as_utf8(n, i+1);
1459          len=strlen(ptr);
1460          if (len<30) {
1461            strncpy(tmpbuff, ptr, len);
1462            tmpbuff[len]='\0';
1463          } else {
1464            strncpy(tmpbuff, ptr, 30);
1465            tmpbuff[30]='\0';
1466            strcat(tmpbuff, "...");
1467          }
1468          owl_free(ptr);
1469
1470          for (j=0; j<strlen(tmpbuff); j++) {
1471            if (tmpbuff[j]=='\n') tmpbuff[j]='~';
1472            if (tmpbuff[j]=='\r') tmpbuff[j]='!';
1473          }
1474
1475          owl_fmtext_appendf_normal(&fm, "  Field %i   : %s\n", i+1, tmpbuff);
1476        }
1477        owl_fmtext_appendf_normal(&fm, "  Default Fm: %s\n", n->z_default_format);
1478      }
1479
1480    }
1481#endif
1482  }
1483
1484  owl_fmtext_append_bold(&fm, "\nOwl Message Attributes:\n");
1485  owl_message_attributes_tofmtext(m, &attrfm);
1486  owl_fmtext_append_fmtext(&fm, &attrfm);
1487 
1488  owl_function_popless_fmtext(&fm);
1489  owl_fmtext_cleanup(&fm);
1490  owl_fmtext_cleanup(&attrfm);
1491}
1492
1493/* print the current message in a popup window.
1494 * Use the 'default' style regardless of whatever
1495 * style the user may be using
1496 */
1497void owl_function_curmsg_to_popwin(void)
1498{
1499  const owl_view *v;
1500  const owl_message *m;
1501  const owl_style *s;
1502  owl_fmtext fm;
1503
1504  v=owl_global_get_current_view(&g);
1505  s=owl_global_get_style_by_name(&g, "default");
1506
1507  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1508
1509  if (!m || owl_view_get_size(v)==0) {
1510    owl_function_error("No current message");
1511    return;
1512  }
1513
1514  owl_fmtext_init_null(&fm);
1515  owl_style_get_formattext(s, &fm, m);
1516
1517  owl_function_popless_fmtext(&fm);
1518  owl_fmtext_cleanup(&fm);
1519}
1520
1521void owl_function_page_curmsg(int step)
1522{
1523  /* scroll down or up within the current message IF the message is truncated */
1524
1525  int offset, curmsg, lines;
1526  const owl_view *v;
1527  owl_message *m;
1528
1529  offset=owl_global_get_curmsg_vert_offset(&g);
1530  v=owl_global_get_current_view(&g);
1531  curmsg=owl_global_get_curmsg(&g);
1532  m=owl_view_get_element(v, curmsg);
1533  if (!m || owl_view_get_size(v)==0) return;
1534  lines=owl_message_get_numlines(m);
1535
1536  if (offset==0) {
1537    /* Bail if the curmsg isn't the last one displayed */
1538    if (curmsg != owl_mainwin_get_last_msg(owl_global_get_mainwin(&g))) {
1539      owl_function_makemsg("The entire message is already displayed");
1540      return;
1541    }
1542   
1543    /* Bail if we're not truncated */
1544    if (!owl_mainwin_is_curmsg_truncated(owl_global_get_mainwin(&g))) {
1545      owl_function_makemsg("The entire message is already displayed");
1546      return;
1547    }
1548  }
1549 
1550 
1551  /* don't scroll past the last line */
1552  if (step>0) {
1553    if (offset+step > lines-1) {
1554      owl_global_set_curmsg_vert_offset(&g, lines-1);
1555    } else {
1556      owl_global_set_curmsg_vert_offset(&g, offset+step);
1557    }
1558  }
1559
1560  /* would we be before the beginning of the message? */
1561  if (step<0) {
1562    if (offset+step<0) {
1563      owl_global_set_curmsg_vert_offset(&g, 0);
1564    } else {
1565      owl_global_set_curmsg_vert_offset(&g, offset+step);
1566    }
1567  }
1568 
1569  /* redisplay */
1570  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1571}
1572
1573void owl_function_resize_typwin(int newsize)
1574{
1575  owl_global_set_typwin_lines(&g, newsize);
1576  owl_mainpanel_layout_contents(&g.mainpanel);
1577}
1578
1579void owl_function_mainwin_pagedown(void)
1580{
1581  int i;
1582
1583  i=owl_mainwin_get_last_msg(owl_global_get_mainwin(&g));
1584  if (i<0) return;
1585  if (owl_mainwin_is_last_msg_truncated(owl_global_get_mainwin(&g))
1586      && (owl_global_get_curmsg(&g) < i)
1587      && (i>0)) {
1588    i--;
1589  }
1590  owl_global_set_curmsg(&g, i);
1591  owl_function_nextmsg();
1592}
1593
1594void owl_function_mainwin_pageup(void)
1595{
1596  owl_global_set_curmsg(&g, owl_global_get_topmsg(&g));
1597  owl_function_prevmsg();
1598}
1599
1600void owl_function_getsubs(void)
1601{
1602  char *buff;
1603
1604  buff=owl_zephyr_getsubs();
1605
1606  if (buff) {
1607    owl_function_popless_text(buff);
1608  } else {
1609    owl_function_popless_text("Error getting subscriptions");
1610  }
1611           
1612  owl_free(buff);
1613}
1614
1615void owl_function_printallvars(void)
1616{
1617  const char *name;
1618  char var[LINE];
1619  owl_list varnames;
1620  int i, numvarnames;
1621  GString *str   = g_string_new("");
1622
1623  g_string_append_printf(str, "%-20s = %s\n", "VARIABLE", "VALUE");
1624  g_string_append_printf(str, "%-20s   %s\n",  "--------", "-----");
1625  owl_variable_dict_get_names(owl_global_get_vardict(&g), &varnames);
1626  numvarnames = owl_list_get_size(&varnames);
1627  for (i=0; i<numvarnames; i++) {
1628    name = owl_list_get_element(&varnames, i);
1629    if (name && name[0]!='_') {
1630      g_string_append_printf(str, "\n%-20s = ", name);
1631      owl_variable_get_tostring(owl_global_get_vardict(&g), name, var, LINE);
1632      g_string_append(str, var);
1633    }
1634  }
1635  g_string_append(str, "\n");
1636  owl_variable_dict_namelist_cleanup(&varnames);
1637
1638  owl_function_popless_text(str->str);
1639  g_string_free(str, true);
1640}
1641
1642void owl_function_show_variables(void)
1643{
1644  owl_list varnames;
1645  owl_fmtext fm; 
1646  int i, numvarnames;
1647  const char *varname;
1648
1649  owl_fmtext_init_null(&fm);
1650  owl_fmtext_append_bold(&fm, 
1651      "Variables: (use 'show variable <name>' for details)\n");
1652  owl_variable_dict_get_names(owl_global_get_vardict(&g), &varnames);
1653  numvarnames = owl_list_get_size(&varnames);
1654  for (i=0; i<numvarnames; i++) {
1655    varname = owl_list_get_element(&varnames, i);
1656    if (varname && varname[0]!='_') {
1657      owl_variable_describe(owl_global_get_vardict(&g), varname, &fm);
1658    }
1659  }
1660  owl_variable_dict_namelist_cleanup(&varnames);
1661  owl_function_popless_fmtext(&fm);
1662  owl_fmtext_cleanup(&fm);
1663}
1664
1665void owl_function_show_variable(const char *name)
1666{
1667  owl_fmtext fm; 
1668
1669  owl_fmtext_init_null(&fm);
1670  owl_variable_get_help(owl_global_get_vardict(&g), name, &fm);
1671  owl_function_popless_fmtext(&fm);
1672  owl_fmtext_cleanup(&fm);
1673}
1674
1675/* note: this applies to global message list, not to view.
1676 * If flag is 1, deletes.  If flag is 0, undeletes. */
1677void owl_function_delete_by_id(int id, int flag)
1678{
1679  const owl_messagelist *ml;
1680  owl_message *m;
1681  ml = owl_global_get_msglist(&g);
1682  m = owl_messagelist_get_by_id(ml, id);
1683  if (m) {
1684    if (flag == 1) {
1685      owl_message_mark_delete(m);
1686    } else if (flag == 0) {
1687      owl_message_unmark_delete(m);
1688    }
1689    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1690  } else {
1691    owl_function_error("No message with id %d: unable to mark for (un)delete",id);
1692  }
1693}
1694
1695void owl_function_delete_automsgs(void)
1696{
1697  /* mark for deletion all messages in the current view that match the
1698   * 'trash' filter */
1699
1700  int i, j, count;
1701  owl_message *m;
1702  const owl_view *v;
1703  const owl_filter *f;
1704
1705  /* get the trash filter */
1706  f=owl_global_get_filter(&g, "trash");
1707  if (!f) {
1708    owl_function_error("No trash filter defined");
1709    return;
1710  }
1711
1712  v=owl_global_get_current_view(&g);
1713
1714  count=0;
1715  j=owl_view_get_size(v);
1716  for (i=0; i<j; i++) {
1717    m=owl_view_get_element(v, i);
1718    if (owl_filter_message_match(f, m)) {
1719      count++;
1720      owl_message_mark_delete(m);
1721    }
1722  }
1723  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1724  owl_function_makemsg("%i messages marked for deletion", count);
1725}
1726
1727void owl_function_status(void)
1728{
1729  char buff[MAXPATHLEN+1];
1730  time_t start;
1731  int up, days, hours, minutes;
1732  owl_fmtext fm;
1733
1734  owl_fmtext_init_null(&fm);
1735
1736  start=owl_global_get_starttime(&g);
1737
1738  owl_fmtext_append_normal(&fm, "General Information:\n");
1739
1740  owl_fmtext_append_normal(&fm, "  Version: ");
1741  owl_fmtext_append_normal(&fm, OWL_VERSION_STRING);
1742  owl_fmtext_append_normal(&fm, "\n");
1743
1744  owl_fmtext_append_normal(&fm, "  Startup Arguments: ");
1745  owl_fmtext_append_normal(&fm, owl_global_get_startupargs(&g));
1746  owl_fmtext_append_normal(&fm, "\n");
1747
1748  owl_fmtext_append_normal(&fm, "  Current Directory: ");
1749  if(getcwd(buff, MAXPATHLEN) == NULL) {
1750    owl_fmtext_append_normal(&fm, "<Error in getcwd>");
1751  } else {
1752    owl_fmtext_append_normal(&fm, buff);
1753  }
1754  owl_fmtext_append_normal(&fm, "\n");
1755
1756  owl_fmtext_appendf_normal(&fm, "  Startup Time: %s", ctime(&start));
1757
1758  up=owl_global_get_runtime(&g);
1759  days=up/86400;
1760  up-=days*86400;
1761  hours=up/3600;
1762  up-=hours*3600;
1763  minutes=up/60;
1764  up-=minutes*60;
1765  owl_fmtext_appendf_normal(&fm, "  Run Time: %i days %2.2i:%2.2i:%2.2i\n", days, hours, minutes, up);
1766
1767  owl_fmtext_append_normal(&fm, "\nProtocol Options:\n");
1768  owl_fmtext_append_normal(&fm, "  Zephyr included    : ");
1769  if (owl_global_is_havezephyr(&g)) {
1770    owl_fmtext_append_normal(&fm, "yes\n");
1771  } else {
1772    owl_fmtext_append_normal(&fm, "no\n");
1773  }
1774  owl_fmtext_append_normal(&fm, "  AIM included       : yes\n");
1775  owl_fmtext_append_normal(&fm, "  Loopback included  : yes\n");
1776
1777
1778  owl_fmtext_append_normal(&fm, "\nBuild Options:\n");
1779  owl_fmtext_append_normal(&fm, "  Stderr redirection : ");
1780#if OWL_STDERR_REDIR
1781  owl_fmtext_append_normal(&fm, "yes\n");
1782#else
1783  owl_fmtext_append_normal(&fm, "no\n");
1784#endif
1785 
1786
1787  owl_fmtext_append_normal(&fm, "\nAIM Status:\n");
1788  owl_fmtext_append_normal(&fm, "  Logged in: ");
1789  if (owl_global_is_aimloggedin(&g)) {
1790    owl_fmtext_append_normal(&fm, owl_global_get_aim_screenname(&g));
1791    owl_fmtext_append_normal(&fm, "\n");
1792  } else {
1793    owl_fmtext_append_normal(&fm, "(not logged in)\n");
1794  }
1795
1796  owl_fmtext_append_normal(&fm, "  Processing events: ");
1797  if (owl_global_is_doaimevents(&g)) {
1798    owl_fmtext_append_normal(&fm, "yes\n");
1799  } else {
1800    owl_fmtext_append_normal(&fm, "no\n");
1801  }
1802
1803  owl_function_popless_fmtext(&fm);
1804  owl_fmtext_cleanup(&fm);
1805}
1806
1807void owl_function_show_term(void)
1808{
1809  owl_fmtext fm;
1810
1811  owl_fmtext_init_null(&fm);
1812  owl_fmtext_appendf_normal(&fm, "Terminal Lines: %i\nTerminal Columns: %i\n",
1813          owl_global_get_lines(&g),
1814          owl_global_get_cols(&g));
1815
1816  if (owl_global_get_hascolors(&g)) {
1817    owl_fmtext_append_normal(&fm, "Color: Yes\n");
1818    owl_fmtext_appendf_normal(&fm, "Number of color pairs: %i\n", owl_global_get_colorpairs(&g));
1819    owl_fmtext_appendf_normal(&fm, "Can change colors: %s\n", can_change_color() ? "yes" : "no");
1820  } else {
1821    owl_fmtext_append_normal(&fm, "Color: No\n");
1822  }
1823
1824  owl_function_popless_fmtext(&fm);
1825  owl_fmtext_cleanup(&fm);
1826}
1827
1828/* if type = 0 then normal reply.
1829 * if type = 1 then it's a reply to sender
1830 * if enter = 0 then allow the command to be edited
1831 * if enter = 1 then don't wait for editing
1832 */
1833void owl_function_reply(int type, int enter)
1834{
1835  char *buff=NULL;
1836  const owl_message *m;
1837  const owl_filter *f;
1838 
1839  if (owl_view_get_size(owl_global_get_current_view(&g))==0) {
1840    owl_function_error("No message selected");
1841  } else {
1842    char *cmd;
1843   
1844    m=owl_view_get_element(owl_global_get_current_view(&g), owl_global_get_curmsg(&g));
1845    if (!m) {
1846      owl_function_error("No message selected");
1847      return;
1848    }
1849
1850    /* first check if we catch the reply-lockout filter */
1851    f=owl_global_get_filter(&g, "reply-lockout");
1852    if (f) {
1853      if (owl_filter_message_match(f, m)) {
1854        owl_function_error("Sorry, replies to this message have been disabled by the reply-lockout filter");
1855        return;
1856      }
1857    }
1858
1859    /* then check if it's a question and just bring up the command prompt */
1860    if (owl_message_is_question(m)) {
1861      owl_function_start_command("");
1862      return;
1863    }
1864
1865    if((type == 0 &&
1866        (cmd=owl_perlconfig_message_call_method(m, "replycmd", 0, NULL))) ||
1867       (type == 1 &&
1868        (cmd=owl_perlconfig_message_call_method(m, "replysendercmd", 0, NULL)))) {
1869      buff = cmd;
1870    }
1871
1872    if(!buff) {
1873        owl_function_error("I don't know how to reply to that message.");
1874        return;
1875    }
1876
1877    if (enter) {
1878      owl_history *hist = owl_global_get_cmd_history(&g);
1879      owl_history_store(hist, buff);
1880      owl_history_reset(hist);
1881      owl_function_command_norv(buff);
1882    } else {
1883      owl_function_start_command(buff);
1884    }
1885    owl_free(buff);
1886  }
1887}
1888
1889void owl_function_zlocate(int argc, const char *const *argv, int auth)
1890{
1891  owl_fmtext fm;
1892  char *ptr;
1893  char *result;
1894  int i;
1895
1896  owl_fmtext_init_null(&fm);
1897
1898  for (i=0; i<argc; i++) {
1899    ptr = long_zuser(argv[i]);
1900    result = owl_zephyr_zlocate(ptr, auth);
1901    owl_fmtext_append_normal(&fm, result);
1902    owl_free(result);
1903    owl_free(ptr);
1904  }
1905
1906  owl_function_popless_fmtext(&fm);
1907  owl_fmtext_cleanup(&fm);
1908}
1909
1910void owl_callback_command(owl_editwin *e)
1911{
1912  char *rv;
1913  const char *line = owl_editwin_get_text(e);
1914
1915  rv = owl_function_command(line);
1916   if (rv) {
1917    owl_function_makemsg("%s", rv);
1918    owl_free(rv);
1919  }
1920}
1921
1922void owl_function_start_command(const char *line)
1923{
1924  owl_editwin *tw;
1925  owl_context *ctx;
1926
1927  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
1928
1929  owl_editwin_set_locktext(tw, "command: ");
1930
1931  owl_editwin_insert_string(tw, line);
1932
1933  ctx = owl_editcontext_new(OWL_CTX_EDITLINE, tw, "editline",
1934                            owl_global_deactivate_editcontext, &g);
1935  owl_global_push_context_obj(&g, ctx);
1936  owl_editwin_set_callback(tw, owl_callback_command);
1937}
1938
1939owl_editwin *owl_function_start_question(const char *line)
1940{
1941  owl_editwin *tw;
1942  owl_context *ctx;
1943
1944  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
1945
1946  owl_editwin_set_locktext(tw, line);
1947
1948  ctx = owl_editcontext_new(OWL_CTX_EDITRESPONSE, tw, "editresponse",
1949                            owl_global_deactivate_editcontext, &g);
1950  owl_global_push_context_obj(&g, ctx);
1951  return tw;
1952}
1953
1954owl_editwin *owl_function_start_password(const char *line)
1955{
1956  owl_editwin *tw;
1957  owl_context *ctx;
1958
1959  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, NULL);
1960
1961  owl_editwin_set_echochar(tw, '*');
1962
1963  owl_editwin_set_locktext(tw, line);
1964
1965  ctx = owl_editcontext_new(OWL_CTX_EDITRESPONSE, tw, "editresponse",
1966                            owl_global_deactivate_editcontext, &g);
1967  owl_global_push_context_obj(&g, ctx);
1968  return tw;
1969}
1970
1971char *owl_function_exec(int argc, const char *const *argv, const char *buff, int type)
1972{
1973  /* if type == 1 display in a popup
1974   * if type == 2 display an admin messages
1975   * if type == 0 return output
1976   * else display in a popup
1977   */
1978  const char *redirect = " 2>&1 < /dev/null";
1979  char *newbuff;
1980  char *out;
1981  FILE *p;
1982
1983#if OWL_STDERR_REDIR
1984  redirect = " < /dev/null";
1985#endif
1986
1987  if (argc<2) {
1988    owl_function_error("Wrong number of arguments to the exec command");
1989    return NULL;
1990  }
1991
1992  buff = skiptokens(buff, 1);
1993  newbuff = owl_sprintf("%s%s", buff, redirect);
1994
1995  if (type == OWL_OUTPUT_POPUP) {
1996    owl_popexec_new(newbuff);
1997  } else {
1998    p = popen(newbuff, "r");
1999    out = owl_slurp(p);
2000    pclose(p);
2001   
2002    if (type == OWL_OUTPUT_RETURN) {
2003      owl_free(newbuff);
2004      return out;
2005    } else if (type == OWL_OUTPUT_ADMINMSG) {
2006      owl_function_adminmsg(buff, out);
2007    }
2008    owl_free(out);
2009  }
2010  owl_free(newbuff);
2011  return NULL;
2012}
2013
2014char *owl_function_perl(int argc, const char *const *argv, const char *buff, int type)
2015{
2016  /* if type == 1 display in a popup
2017   * if type == 2 display an admin messages
2018   * if type == 0 return output
2019   * else display in a popup
2020   */
2021  char *perlout;
2022
2023  if (argc<2) {
2024    owl_function_error("Wrong number of arguments to perl command");
2025    return NULL;
2026  }
2027
2028  /* consume first token (argv[0]) */
2029  buff = skiptokens(buff, 1);
2030
2031  perlout = owl_perlconfig_execute(buff);
2032  if (perlout) { 
2033    if (type == OWL_OUTPUT_POPUP) {
2034      owl_function_popless_text(perlout);
2035    } else if (type == OWL_OUTPUT_ADMINMSG) {
2036      owl_function_adminmsg(buff, perlout);
2037    } else if (type == OWL_OUTPUT_RETURN) {
2038      return perlout;
2039    }
2040    owl_free(perlout);
2041  }
2042  return NULL;
2043}
2044
2045/* Change the filter associated with the current view.
2046 * This also figures out which message in the new filter
2047 * should have the pointer.
2048 */
2049void owl_function_change_currentview_filter(const char *filtname)
2050{
2051  owl_view *v;
2052  owl_filter *f;
2053  int curid=-1, newpos, curmsg;
2054  const owl_message *curm=NULL;
2055
2056  v=owl_global_get_current_view(&g);
2057
2058  curmsg=owl_global_get_curmsg(&g);
2059  if (curmsg==-1) {
2060    owl_function_debugmsg("Hit the curmsg==-1 case in change_view");
2061  } else {
2062    curm=owl_view_get_element(v, curmsg);
2063    if (curm) {
2064      curid=owl_message_get_id(curm);
2065      owl_view_save_curmsgid(v, curid);
2066    }
2067  }
2068
2069  f=owl_global_get_filter(&g, filtname);
2070  if (!f) {
2071    owl_function_error("Unknown filter %s", filtname);
2072    return;
2073  }
2074
2075  owl_view_new_filter(v, f);
2076
2077  /* Figure out what to set the current message to.
2078   * - If the view we're leaving has messages in it, go to the closest message
2079   *   to the last message pointed to in that view.
2080   * - If the view we're leaving is empty, try to restore the position
2081   *   from the last time we were in the new view.  */
2082  if (curm) {
2083    newpos = owl_view_get_nearest_to_msgid(v, curid);
2084  } else {
2085    newpos = owl_view_get_nearest_to_saved(v);
2086  }
2087
2088  owl_global_set_curmsg(&g, newpos);
2089  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
2090  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2091  owl_global_set_direction_downwards(&g);
2092}
2093
2094/* Create a new filter, or replace an existing one
2095 * with a new definition.
2096 */
2097void owl_function_create_filter(int argc, const char *const *argv)
2098{
2099  owl_filter *f;
2100  const owl_view *v;
2101  int inuse = 0;
2102
2103  if (argc < 2) {
2104    owl_function_error("Wrong number of arguments to filter command");
2105    return;
2106  }
2107
2108  owl_function_debugmsg("owl_function_create_filter: starting to create filter named %s", argv[1]);
2109
2110  v=owl_global_get_current_view(&g);
2111
2112  /* don't touch the all filter */
2113  if (!strcmp(argv[1], "all")) {
2114    owl_function_error("You may not change the 'all' filter.");
2115    return;
2116  }
2117
2118  /* deal with the case of trying change the filter color */
2119  if (argc==4 && !strcmp(argv[2], "-c")) {
2120    f=owl_global_get_filter(&g, argv[1]);
2121    if (!f) {
2122      owl_function_error("The filter '%s' does not exist.", argv[1]);
2123      return;
2124    }
2125    if (owl_util_string_to_color(argv[3])==OWL_COLOR_INVALID) {
2126      owl_function_error("The color '%s' is not available.", argv[3]);
2127      return;
2128    }
2129    owl_filter_set_fgcolor(f, owl_util_string_to_color(argv[3]));
2130    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2131    return;
2132  }
2133  if (argc==4 && !strcmp(argv[2], "-b")) {
2134    f=owl_global_get_filter(&g, argv[1]);
2135    if (!f) {
2136      owl_function_error("The filter '%s' does not exist.", argv[1]);
2137      return;
2138    }
2139    if (owl_util_string_to_color(argv[3])==OWL_COLOR_INVALID) {
2140      owl_function_error("The color '%s' is not available.", argv[3]);
2141      return;
2142    }
2143    owl_filter_set_bgcolor(f, owl_util_string_to_color(argv[3]));
2144    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2145    return;
2146  }
2147
2148  /* create the filter and check for errors */
2149  f = owl_filter_new(argv[1], argc-2, argv+2);
2150  if (f == NULL) {
2151    owl_function_error("Invalid filter");
2152    return;
2153  }
2154
2155  /* if the named filter is in use by the current view, remember it */
2156  if (!strcmp(owl_view_get_filtname(v), argv[1])) {
2157    inuse=1;
2158  }
2159
2160  /* if the named filter already exists, nuke it */
2161  if (owl_global_get_filter(&g, argv[1])) {
2162    owl_global_remove_filter(&g, argv[1]);
2163  }
2164
2165  /* add the filter */
2166  owl_global_add_filter(&g, f);
2167
2168  /* if it was in use by the current view then update */
2169  if (inuse) {
2170    owl_function_change_currentview_filter(argv[1]);
2171  }
2172  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2173}
2174
2175/* If 'filtername' does not start with 'not-' create a filter named
2176 * 'not-<filtername>' defined as "not filter <filtername>".  If the
2177 * filter 'not-<filtername>' already exists, do not overwrite it.  If
2178 * 'filtername' begins with 'not-' and a filter 'filtername' already
2179 * exists, then do nothing.  If the filter 'filtername' does not
2180 * exist, create it and define it as 'not filter <filtername>'
2181 *
2182 * Returns the name of the negated filter, which the caller must free.
2183 */
2184char *owl_function_create_negative_filter(const char *filtername)
2185{
2186  char *newname;
2187  const owl_filter *tmpfilt;
2188  const char *argv[5];
2189
2190  owl_function_debugmsg("owl_function_create_negative_filter");
2191 
2192  if (!strncmp(filtername, "not-", 4)) {
2193    newname=owl_strdup(filtername+4);
2194  } else {
2195    newname=owl_sprintf("not-%s", filtername);
2196  }
2197
2198  tmpfilt=owl_global_get_filter(&g, newname);
2199  if (!tmpfilt) {
2200    argv[0]="filter"; /* anything is fine here */
2201    argv[1]=newname;
2202    argv[2]="not";
2203    argv[3]="filter";
2204    argv[4]=filtername;
2205    owl_function_create_filter(5, argv);
2206  }
2207
2208  owl_function_debugmsg("owl_function_create_negative_filter: returning with %s", newname);
2209  return(newname);
2210}
2211
2212void owl_function_show_filters(void)
2213{
2214  const owl_filter *f;
2215  GList *fl;
2216  owl_fmtext fm;
2217
2218  owl_fmtext_init_null(&fm);
2219
2220  owl_fmtext_append_bold(&fm, "Filters:\n");
2221
2222  for (fl = g.filterlist; fl; fl = g_list_next(fl)) {
2223    f = fl->data;
2224    owl_fmtext_append_normal(&fm, "   ");
2225    if (owl_global_get_hascolors(&g)) {
2226      owl_fmtext_append_normal_color(&fm, owl_filter_get_name(f), owl_filter_get_fgcolor(f), owl_filter_get_bgcolor(f));
2227    } else {
2228      owl_fmtext_append_normal(&fm, owl_filter_get_name(f));
2229    }
2230    owl_fmtext_append_normal(&fm, "\n");
2231  }
2232  owl_function_popless_fmtext(&fm);
2233  owl_fmtext_cleanup(&fm);
2234}
2235
2236void owl_function_show_filter(const char *name)
2237{
2238  const owl_filter *f;
2239  char *buff, *tmp;
2240
2241  f=owl_global_get_filter(&g, name);
2242  if (!f) {
2243    owl_function_error("There is no filter named %s", name);
2244    return;
2245  }
2246  tmp = owl_filter_print(f);
2247  buff = owl_sprintf("%s: %s", owl_filter_get_name(f), tmp);
2248  owl_function_popless_text(buff);
2249  owl_free(buff);
2250  owl_free(tmp);
2251}
2252
2253void owl_function_show_zpunts(void)
2254{
2255  const owl_filter *f;
2256  const owl_list *fl;
2257  char buff[5000];
2258  char *tmp;
2259  owl_fmtext fm;
2260  int i, j;
2261
2262  owl_fmtext_init_null(&fm);
2263
2264  fl=owl_global_get_puntlist(&g);
2265  j=owl_list_get_size(fl);
2266  owl_fmtext_append_bold(&fm, "Active zpunt filters:\n");
2267
2268  for (i=0; i<j; i++) {
2269    f=owl_list_get_element(fl, i);
2270    snprintf(buff, sizeof(buff), "[% 2d] ", i+1);
2271    owl_fmtext_append_normal(&fm, buff);
2272    tmp = owl_filter_print(f);
2273    owl_fmtext_append_normal(&fm, tmp);
2274    owl_free(tmp);
2275  }
2276  owl_function_popless_fmtext(&fm);
2277  owl_fmtext_cleanup(&fm);
2278}
2279
2280/* Create a filter for a class, instance if one doesn't exist.  If
2281 * instance is NULL then catch all messgaes in the class.  Returns the
2282 * name of the filter or null.  The caller must free this name.
2283 * If 'related' is nonzero, encompass unclasses and .d classes as well.
2284 */
2285char *owl_function_classinstfilt(const char *c, const char *i, int related) 
2286{
2287  owl_filter *f;
2288  char *filtname;
2289  char *tmpclass, *tmpinstance = NULL;
2290  char *class, *instance = NULL;
2291  GString *buf;
2292
2293  if (related) {
2294    class = owl_util_baseclass(c);
2295    if (i) {
2296      instance = owl_util_baseclass(i);
2297    }
2298  } else {
2299    class = owl_strdup(c);
2300    if (i) {
2301      instance = owl_strdup(i);
2302    }
2303  }
2304
2305  /* name for the filter */
2306  if (!instance) {
2307    filtname = owl_sprintf("%sclass-%s", related ? "related-" : "", class);
2308  } else {
2309    filtname = owl_sprintf("%sclass-%s-instance-%s", related ? "related-" : "", class, instance);
2310  }
2311  /* downcase it */
2312  {
2313    char *temp = g_utf8_strdown(filtname, -1);
2314    if (temp) {
2315      owl_free(filtname);
2316      filtname = temp;
2317    }
2318  }
2319  /* turn spaces, single quotes, and double quotes into dots */
2320  owl_text_tr(filtname, ' ', '.');
2321  owl_text_tr(filtname, '\'', '.');
2322  owl_text_tr(filtname, '"', '.');
2323 
2324  /* if it already exists then go with it.  This lets users override */
2325  if (owl_global_get_filter(&g, filtname)) {
2326    goto done;
2327  }
2328
2329  /* create the new filter */
2330  tmpclass=owl_text_quote(class, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2331  if (instance) {
2332    tmpinstance=owl_text_quote(instance, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2333  }
2334
2335  buf = g_string_new("");
2336  owl_string_appendf_quoted(buf,
2337                            related ? "class ^(un)*%q(\\.d)*$" : "class ^%q$",
2338                            tmpclass);
2339
2340  if (tmpinstance) {
2341    owl_string_appendf_quoted(buf,
2342                              related ?
2343                              " and ( instance ^(un)*%q(\\.d)*$ )" :
2344                              " and instance ^%q$",
2345                              tmpinstance);
2346  }
2347  owl_free(tmpclass);
2348  owl_free(tmpinstance);
2349
2350  f = owl_filter_new_fromstring(filtname, buf->str);
2351  g_string_free(buf, true);
2352  if (f == NULL) {
2353    /* Couldn't make a filter for some reason. Return NULL. */
2354    owl_function_error("Error creating filter '%s'", filtname);
2355    owl_free(filtname);
2356    filtname = NULL;
2357    goto done;
2358  }
2359
2360  /* add it to the global list */
2361  owl_global_add_filter(&g, f);
2362
2363done:
2364  owl_free(class);
2365  if (instance) {
2366    owl_free(instance);
2367  }
2368  return(filtname);
2369}
2370
2371/* Create a filter for personal zephyrs to or from the specified
2372 * zephyr user.  Includes login/logout notifications for the user.
2373 * The name of the filter will be 'user-<shortuser>'.  If a filter already
2374 * exists with this name, no new filter will be created.  This allows
2375 * the configuration to override this function.  Returns the name of
2376 * the filter, which the caller must free.
2377 */
2378char *owl_function_zuserfilt(const char *longuser)
2379{
2380  owl_filter *f;
2381  char *argbuff, *esclonguser, *shortuser, *filtname;
2382
2383  /* name for the filter */
2384  shortuser = short_zuser(longuser);
2385  filtname = owl_sprintf("user-%s", shortuser);
2386  owl_free(shortuser);
2387
2388  /* if it already exists then go with it.  This lets users override */
2389  if (owl_global_get_filter(&g, filtname)) {
2390    return filtname;
2391  }
2392
2393  /* create the new-internal filter */
2394  esclonguser = owl_text_quote(longuser, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2395
2396  argbuff=owl_string_build_quoted("( type ^zephyr$ and filter personal and "
2397      "( ( direction ^in$ and sender ^%q$ ) or ( direction ^out$ and "
2398      "recipient ^%q$ ) ) ) or ( ( class ^login$ ) and ( sender ^%q$ ) )",
2399      esclonguser, esclonguser, esclonguser);
2400  owl_free(esclonguser);
2401
2402  f = owl_filter_new_fromstring(filtname, argbuff);
2403  owl_free(argbuff);
2404
2405  if (f == NULL) {
2406    /* Couldn't make a filter for some reason. Return NULL. */
2407    owl_function_error("Error creating filter '%s'", filtname);
2408    owl_free(filtname);
2409    return NULL;
2410  }
2411
2412  /* add it to the global list */
2413  owl_global_add_filter(&g, f);
2414
2415  return(filtname);
2416}
2417
2418/* Create a filter for AIM IM messages to or from the specified
2419 * screenname.  The name of the filter will be 'aimuser-<user>'.  If a
2420 * filter already exists with this name, no new filter will be
2421 * created.  This allows the configuration to override this function.
2422 * Returns the name of the filter, which the caller must free.
2423 */
2424char *owl_function_aimuserfilt(const char *user)
2425{
2426  owl_filter *f;
2427  char *argbuff, *filtname;
2428  char *escuser;
2429
2430  /* name for the filter */
2431  filtname=owl_sprintf("aimuser-%s", user);
2432
2433  /* if it already exists then go with it.  This lets users override */
2434  if (owl_global_get_filter(&g, filtname)) {
2435    return(owl_strdup(filtname));
2436  }
2437
2438  /* create the new-internal filter */
2439  escuser = owl_text_quote(user, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2440
2441  argbuff = owl_sprintf(
2442      "( type ^aim$ and ( ( sender ^%1$s$ and recipient ^%2$s$ ) or "
2443      "( sender ^%2$s$ and recipient ^%1$s$ ) ) )",
2444      escuser, owl_global_get_aim_screenname_for_filters(&g));
2445  owl_free(escuser);
2446
2447  f = owl_filter_new_fromstring(filtname, argbuff);
2448  owl_free(argbuff);
2449
2450  if (f == NULL) {
2451    owl_function_error("Error creating filter '%s'", filtname);
2452    owl_free(filtname);
2453    return NULL;
2454  }
2455
2456  /* add it to the global list */
2457  owl_global_add_filter(&g, f);
2458
2459  return(filtname);
2460}
2461
2462char *owl_function_typefilt(const char *type)
2463{
2464  owl_filter *f;
2465  char *argbuff, *filtname, *esctype;
2466
2467  /* name for the filter */
2468  filtname=owl_sprintf("type-%s", type);
2469
2470  /* if it already exists then go with it.  This lets users override */
2471  if (owl_global_get_filter(&g, filtname)) {
2472    return filtname;
2473  }
2474
2475  /* create the new-internal filter */
2476  esctype = owl_text_quote(type, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2477
2478  argbuff = owl_sprintf("type ^%s$", esctype);
2479  owl_free(esctype);
2480
2481  f = owl_filter_new_fromstring(filtname, argbuff);
2482  owl_free(argbuff);
2483
2484  if (f == NULL) {
2485    owl_function_error("Error creating filter '%s'", filtname);
2486    owl_free(filtname);
2487    return NULL;
2488  }
2489
2490  /* add it to the global list */
2491  owl_global_add_filter(&g, f);
2492
2493  return filtname;
2494}
2495
2496/* If flag is 1, marks for deletion.  If flag is 0,
2497 * unmarks for deletion. */
2498void owl_function_delete_curview_msgs(int flag)
2499{
2500  const owl_view *v;
2501  int i, j;
2502
2503  v=owl_global_get_current_view(&g);
2504  j=owl_view_get_size(v);
2505  for (i=0; i<j; i++) {
2506    if (flag == 1) {
2507      owl_message_mark_delete(owl_view_get_element(v, i));
2508    } else if (flag == 0) {
2509      owl_message_unmark_delete(owl_view_get_element(v, i));
2510    }
2511  }
2512
2513  owl_function_makemsg("%i messages marked for %sdeletion", j, flag?"":"un");
2514
2515  owl_mainwin_redisplay(owl_global_get_mainwin(&g)); 
2516}
2517
2518static char *owl_function_smartfilter_cc(const owl_message *m) {
2519  const char *ccs;
2520  char *ccs_quoted;
2521  char *filtname;
2522  owl_filter *f;
2523  GString *buf;
2524
2525  ccs = owl_message_get_attribute_value(m, "zephyr_ccs");
2526
2527  filtname = owl_sprintf("conversation-%s", ccs);
2528  owl_text_tr(filtname, ' ', '-');
2529
2530  if (owl_global_get_filter(&g, filtname)) {
2531    return filtname;
2532  }
2533
2534  buf = g_string_new("type ^zephyr$ and filter personal and "
2535                     "zephyr_ccs ^");
2536  ccs_quoted = owl_text_quote(ccs, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2537  owl_string_append_quoted_arg(buf, ccs_quoted);
2538  g_string_append_c(buf, '$');
2539  owl_free(ccs_quoted);
2540
2541  f = owl_filter_new_fromstring(filtname, buf->str);
2542  g_string_free(buf, true);
2543
2544  if (f == NULL) {
2545    owl_function_error("Error creating filter '%s'", filtname);
2546    owl_free(filtname);
2547    return NULL;
2548  }
2549
2550  owl_global_add_filter(&g, f);
2551
2552  return filtname;
2553}
2554
2555/* Create a filter based on the current message.  Returns the name of
2556 * a filter or null.  The caller must free this name.
2557 *
2558 * if the curmsg is a personal zephyr return a filter name
2559 *    to the zephyr conversation with that user.
2560 * If the curmsg is a zephyr class message, instance foo, recip *,
2561 *    return a filter name to the class, inst.
2562 * If the curmsg is a zephyr class message and type==0 then
2563 *    return a filter name for just the class.
2564 * If the curmsg is a zephyr class message and type==1 then
2565 *    return a filter name for the class and instance.
2566 * If the curmsg is a personal AIM message returna  filter
2567 *    name to the AIM conversation with that user
2568 */
2569char *owl_function_smartfilter(int type, int invert_related)
2570{
2571  const owl_view *v;
2572  const owl_message *m;
2573  char *filtname = NULL;
2574  const char *argv[2], *zperson;
2575  int related = owl_global_is_narrow_related(&g) ^ invert_related;
2576
2577  v=owl_global_get_current_view(&g);
2578  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
2579
2580  if (!m || owl_view_get_size(v)==0) {
2581    owl_function_error("No message selected\n");
2582    return(NULL);
2583  }
2584
2585  /* very simple handling of admin messages for now */
2586  if (owl_message_is_type_admin(m)) {
2587    return(owl_function_typefilt("admin"));
2588  }
2589
2590  /* very simple handling of loopback messages for now */
2591  if (owl_message_is_type_loopback(m)) {
2592    return(owl_function_typefilt("loopback"));
2593  }
2594
2595  /* aim messages */
2596  if (owl_message_is_type_aim(m)) {
2597    if (owl_message_is_direction_in(m)) {
2598      filtname=owl_function_aimuserfilt(owl_message_get_sender(m));
2599    } else if (owl_message_is_direction_out(m)) {
2600      filtname=owl_function_aimuserfilt(owl_message_get_recipient(m));
2601    }
2602    return(filtname);
2603  }
2604
2605  /* narrow personal and login messages to the sender or recip as appropriate */
2606  if (owl_message_is_type_zephyr(m)) {
2607    if (owl_message_is_personal(m) || owl_message_is_loginout(m)) {
2608      if (owl_message_get_attribute_value(m, "zephyr_ccs") != NULL) {
2609        return owl_function_smartfilter_cc(m);
2610      }
2611
2612      if (owl_message_is_direction_in(m)) {
2613        zperson = owl_message_get_sender(m);
2614      } else {
2615        zperson = owl_message_get_recipient(m);
2616      }
2617      filtname = owl_function_zuserfilt(zperson);
2618      return filtname;
2619    }
2620
2621    /* narrow class MESSAGE, instance foo, recip * messages to class, inst */
2622    if (!strcasecmp(owl_message_get_class(m), "message")) {
2623      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m), related);
2624      return(filtname);
2625    }
2626
2627    /* otherwise narrow to the class */
2628    if (type==0) {
2629      filtname=owl_function_classinstfilt(owl_message_get_class(m), NULL, related);
2630    } else if (type==1) {
2631      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m), related);
2632    }
2633    return(filtname);
2634  }
2635
2636  /* pass it off to perl */
2637  argv[0] = type ? "1" : "0";
2638  argv[1] = related ? "1" : "0";
2639  return owl_perlconfig_message_call_method(m, "smartfilter", 2, argv);
2640}
2641
2642void owl_function_smartzpunt(int type)
2643{
2644  /* Starts a zpunt command based on the current class,instance pair.
2645   * If type=0, uses just class.  If type=1, uses instance as well. */
2646  const owl_view *v;
2647  const owl_message *m;
2648  const char *mclass, *minst;
2649  GString *buf;
2650 
2651  v=owl_global_get_current_view(&g);
2652  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
2653
2654  if (!m || owl_view_get_size(v)==0) {
2655    owl_function_error("No message selected\n");
2656    return;
2657  }
2658
2659  /* for now we skip admin messages. */
2660  if (owl_message_is_type_admin(m)
2661      || owl_message_is_loginout(m)
2662      || !owl_message_is_type_zephyr(m)) {
2663    owl_function_error("smartzpunt doesn't support this message type.");
2664    return;
2665  }
2666
2667  mclass = owl_message_get_class(m);
2668  minst = owl_message_get_instance(m);
2669  if (!mclass || !*mclass || *mclass==' '
2670      || (!strcasecmp(mclass, "message") && !strcasecmp(minst, "personal"))
2671      || (type && (!minst || !*minst|| *minst==' '))) {
2672    owl_function_error("smartzpunt can't safely do this for <%s,%s>",
2673                         mclass, minst);
2674  } else {
2675    buf = g_string_new("start-command zpunt ");
2676    owl_string_append_quoted_arg(buf, mclass);
2677    if (type) {
2678      g_string_append_c(buf, ' ');
2679      owl_string_append_quoted_arg(buf, minst);
2680    } else {
2681      g_string_append(buf, " *");
2682    }
2683    owl_function_command(buf->str);
2684    g_string_free(buf, true);
2685  }
2686}
2687
2688/* Set the color of the current view's filter to
2689 * be 'color'
2690 */
2691void owl_function_color_current_filter(const char *fgcolor, const char *bgcolor)
2692{
2693  const char *name;
2694
2695  name=owl_view_get_filtname(owl_global_get_current_view(&g));
2696  owl_function_color_filter(name, fgcolor, bgcolor);
2697}
2698
2699/* Set the color of the filter 'filter' to be 'color'.  If the color
2700 * name does not exist, return -1, if the filter does not exist or is
2701 * the "all" filter, return -2.  Return 0 on success
2702 */
2703int owl_function_color_filter(const char *filtname, const char *fgcolor, const char *bgcolor)
2704{
2705  owl_filter *f;
2706
2707  f=owl_global_get_filter(&g, filtname);
2708  if (!f) {
2709    owl_function_error("Unknown filter");
2710    return(-2);
2711  }
2712
2713  /* don't touch the all filter */
2714  if (!strcmp(filtname, "all")) {
2715    owl_function_error("You may not change the 'all' filter.");
2716    return(-2);
2717  }
2718
2719  if (owl_util_string_to_color(fgcolor)==OWL_COLOR_INVALID) {
2720    owl_function_error("No color named '%s' avilable.", fgcolor);
2721    return(-1);
2722  }
2723
2724
2725  if (bgcolor != NULL) {
2726    if (owl_util_string_to_color(bgcolor)==OWL_COLOR_INVALID) {
2727      owl_function_error("No color named '%s' avilable.", bgcolor);
2728      return(-1);
2729    }
2730    owl_filter_set_bgcolor(f, owl_util_string_to_color(bgcolor));
2731  }
2732  owl_filter_set_fgcolor(f, owl_util_string_to_color(fgcolor));
2733 
2734  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2735  return(0);
2736}
2737
2738void owl_function_show_colors(void)
2739{
2740  owl_fmtext fm;
2741  int i; 
2742 
2743  owl_fmtext_init_null(&fm);
2744  owl_fmtext_append_normal(&fm, "default: ");
2745  owl_fmtext_append_normal_color(&fm, "default\n", OWL_COLOR_DEFAULT, OWL_COLOR_DEFAULT);
2746
2747  owl_fmtext_append_normal(&fm,"red:      ");
2748  owl_fmtext_append_normal_color(&fm, "red\n", OWL_COLOR_RED, OWL_COLOR_DEFAULT);
2749
2750  owl_fmtext_append_normal(&fm,"green:    ");
2751  owl_fmtext_append_normal_color(&fm, "green\n", OWL_COLOR_GREEN, OWL_COLOR_DEFAULT);
2752
2753  owl_fmtext_append_normal(&fm,"yellow:   ");
2754  owl_fmtext_append_normal_color(&fm, "yellow\n", OWL_COLOR_YELLOW, OWL_COLOR_DEFAULT);
2755
2756  owl_fmtext_append_normal(&fm,"blue:     ");
2757  owl_fmtext_append_normal_color(&fm, "blue\n", OWL_COLOR_BLUE, OWL_COLOR_DEFAULT);
2758
2759  owl_fmtext_append_normal(&fm,"magenta:  ");
2760  owl_fmtext_append_normal_color(&fm, "magenta\n", OWL_COLOR_MAGENTA, OWL_COLOR_DEFAULT);
2761
2762  owl_fmtext_append_normal(&fm,"cyan:     ");
2763  owl_fmtext_append_normal_color(&fm, "cyan\n", OWL_COLOR_CYAN, OWL_COLOR_DEFAULT);
2764
2765  owl_fmtext_append_normal(&fm,"white:    ");
2766  owl_fmtext_append_normal_color(&fm, "white\n", OWL_COLOR_WHITE, OWL_COLOR_DEFAULT);
2767
2768  for(i = 8; i < COLORS; ++i) {
2769    char* str1 = owl_sprintf("%4i:     ",i);
2770    char* str2 = owl_sprintf("%i\n",i);
2771    owl_fmtext_append_normal(&fm,str1);
2772    owl_fmtext_append_normal_color(&fm, str2, i, OWL_COLOR_DEFAULT);
2773    owl_free(str1);
2774     owl_free(str2);
2775  }
2776 
2777  owl_function_popless_fmtext(&fm);
2778  owl_fmtext_cleanup(&fm);
2779}
2780
2781/* add the given class, inst, recip to the punt list for filtering.
2782 *   if direction==0 then punt
2783 *   if direction==1 then unpunt
2784 */
2785void owl_function_zpunt(const char *class, const char *inst, const char *recip, int direction)
2786{
2787  char *puntexpr, *classexpr, *instexpr, *recipexpr;
2788  char *quoted;
2789
2790  if (!strcmp(class, "*")) {
2791    classexpr = owl_sprintf("class .*");
2792  } else {
2793    quoted=owl_text_quote(class, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2794    owl_text_tr(quoted, ' ', '.');
2795    owl_text_tr(quoted, '\'', '.');
2796    owl_text_tr(quoted, '"', '.');
2797    classexpr = owl_sprintf("class ^(un)*%s(\\.d)*$", quoted);
2798    owl_free(quoted);
2799  }
2800  if (!strcmp(inst, "*")) {
2801    instexpr = owl_sprintf(" and instance .*");
2802  } else {
2803    quoted=owl_text_quote(inst, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2804    owl_text_tr(quoted, ' ', '.');
2805    owl_text_tr(quoted, '\'', '.');
2806    owl_text_tr(quoted, '"', '.');
2807    instexpr = owl_sprintf(" and instance ^(un)*%s(\\.d)*$", quoted);
2808    owl_free(quoted);
2809  }
2810  if (!strcmp(recip, "*")) {
2811    recipexpr = owl_sprintf("");
2812  } else {
2813    if(!strcmp(recip, "%me%")) {
2814      recip = owl_zephyr_get_sender();
2815    }
2816    quoted=owl_text_quote(recip, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2817    owl_text_tr(quoted, ' ', '.');
2818    owl_text_tr(quoted, '\'', '.');
2819    owl_text_tr(quoted, '"', '.');
2820    recipexpr = owl_sprintf(" and recipient ^%s$", quoted);
2821    owl_free(quoted);
2822  }
2823
2824  puntexpr = owl_sprintf("%s %s %s", classexpr, instexpr, recipexpr);
2825  owl_function_punt(puntexpr, direction);
2826  owl_free(puntexpr);
2827  owl_free(classexpr);
2828  owl_free(instexpr);
2829  owl_free(recipexpr);
2830}
2831
2832void owl_function_punt(const char *filter, int direction)
2833{
2834  owl_filter *f;
2835  owl_list *fl;
2836  int i, j;
2837  fl=owl_global_get_puntlist(&g);
2838
2839  /* first, create the filter */
2840  owl_function_debugmsg("About to filter %s", filter);
2841  f = owl_filter_new_fromstring("punt-filter", filter);
2842  if (f == NULL) {
2843    owl_function_error("Error creating filter for zpunt");
2844    return;
2845  }
2846
2847  /* Check for an identical filter */
2848  j=owl_list_get_size(fl);
2849  for (i=0; i<j; i++) {
2850    if (owl_filter_equiv(f, owl_list_get_element(fl, i))) {
2851      owl_function_debugmsg("found an equivalent punt filter");
2852      /* if we're punting, then just silently bow out on this duplicate */
2853      if (direction==0) {
2854        owl_filter_delete(f);
2855        return;
2856      }
2857
2858      /* if we're unpunting, then remove this filter from the puntlist */
2859      if (direction==1) {
2860        owl_filter_delete(owl_list_get_element(fl, i));
2861        owl_list_remove_element(fl, i);
2862        owl_filter_delete(f);
2863        return;
2864      }
2865    }
2866  }
2867
2868  owl_function_debugmsg("punting");
2869  /* If we're punting, add the filter to the global punt list */
2870  if (direction==0) {
2871    owl_list_append_element(fl, f);
2872  }
2873}
2874
2875void owl_function_show_keymaps(void)
2876{
2877  owl_list l;
2878  owl_fmtext fm;
2879  const owl_keymap *km;
2880  const owl_keyhandler *kh;
2881  int i, numkm;
2882  const char *kmname;
2883
2884  kh = owl_global_get_keyhandler(&g);
2885  owl_fmtext_init_null(&fm);
2886  owl_fmtext_append_bold(&fm, "Keymaps:   ");
2887  owl_fmtext_append_normal(&fm, "(use 'show keymap <name>' for details)\n");
2888  owl_keyhandler_get_keymap_names(kh, &l);
2889  owl_fmtext_append_list(&fm, &l, "\n", owl_function_keymap_summary);
2890  owl_fmtext_append_normal(&fm, "\n");
2891
2892  numkm = owl_list_get_size(&l);
2893  for (i=0; i<numkm; i++) {
2894    kmname = owl_list_get_element(&l, i);
2895    km = owl_keyhandler_get_keymap(kh, kmname);
2896    owl_fmtext_append_bold(&fm, "\n\n----------------------------------------------------------------------------------------------------\n\n");
2897    owl_keymap_get_details(km, &fm, 0);
2898  }
2899  owl_fmtext_append_normal(&fm, "\n");
2900 
2901  owl_function_popless_fmtext(&fm);
2902  owl_keyhandler_keymap_namelist_cleanup(&l);
2903  owl_fmtext_cleanup(&fm);
2904}
2905
2906char *owl_function_keymap_summary(const char *name)
2907{
2908  const owl_keymap *km
2909    = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), name);
2910  if (km) return owl_keymap_summary(km);
2911  else return(NULL);
2912}
2913
2914/* TODO: implement for real */
2915void owl_function_show_keymap(const char *name)
2916{
2917  owl_fmtext fm;
2918  const owl_keymap *km;
2919
2920  owl_fmtext_init_null(&fm);
2921  km = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), name);
2922  if (km) {
2923    owl_keymap_get_details(km, &fm, 1);
2924  } else {
2925    owl_fmtext_append_normal(&fm, "No such keymap...\n");
2926  } 
2927  owl_function_popless_fmtext(&fm);
2928  owl_fmtext_cleanup(&fm);
2929}
2930
2931void owl_function_help_for_command(const char *cmdname)
2932{
2933  owl_fmtext fm;
2934
2935  owl_fmtext_init_null(&fm);
2936  owl_cmd_get_help(owl_global_get_cmddict(&g), cmdname, &fm);
2937  owl_function_popless_fmtext(&fm); 
2938  owl_fmtext_cleanup(&fm);
2939}
2940
2941void owl_function_set_search(const char *string)
2942{
2943  owl_regex re;
2944
2945  if (string && owl_regex_create_quoted(&re, string) == 0) {
2946    owl_global_set_search_re(&g, &re);
2947    owl_regex_cleanup(&re);
2948  } else {
2949    owl_global_set_search_re(&g, NULL);
2950  }
2951}
2952
2953void owl_function_search_helper(int consider_current, int direction)
2954{
2955  /* move to a message that contains the string.  If direction is
2956   * OWL_DIRECTION_DOWNWARDS then search fowards, if direction is
2957   * OWL_DIRECTION_UPWARDS then search backwards.
2958   *
2959   * If consider_current is true then it will stay on the
2960   * current message if it contains the string.
2961   */
2962
2963  const owl_view *v;
2964  int viewsize, i, curmsg, start;
2965  owl_message *m;
2966
2967  v=owl_global_get_current_view(&g);
2968  viewsize=owl_view_get_size(v);
2969  curmsg=owl_global_get_curmsg(&g);
2970 
2971  if (viewsize==0) {
2972    owl_function_error("No messages present");
2973    return;
2974  }
2975
2976  if (consider_current) {
2977    start=curmsg;
2978  } else if (direction==OWL_DIRECTION_DOWNWARDS) {
2979    start=curmsg+1;
2980  } else {
2981    start=curmsg-1;
2982  }
2983
2984  /* bounds check */
2985  if (start>=viewsize || start<0) {
2986    owl_function_error("No further matches found");
2987    return;
2988  }
2989
2990  for (i=start; i<viewsize && i>=0;) {
2991    m=owl_view_get_element(v, i);
2992    if (owl_message_search(m, owl_global_get_search_re(&g))) {
2993      owl_global_set_curmsg(&g, i);
2994      owl_function_calculate_topmsg(direction);
2995      owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2996      if (direction==OWL_DIRECTION_DOWNWARDS) {
2997        owl_global_set_direction_downwards(&g);
2998      } else {
2999        owl_global_set_direction_upwards(&g);
3000      }
3001      return;
3002    }
3003    if (direction==OWL_DIRECTION_DOWNWARDS) {
3004      i++;
3005    } else {
3006      i--;
3007    }
3008    owl_function_mask_sigint(NULL);
3009    if(owl_global_is_interrupted(&g)) {
3010      owl_global_unset_interrupted(&g);
3011      owl_function_unmask_sigint(NULL);
3012      owl_function_makemsg("Search interrupted!");
3013      owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3014      return;
3015    }
3016    owl_function_unmask_sigint(NULL);
3017  }
3018  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3019  owl_function_error("No matches found");
3020}
3021
3022/* strips formatting from ztext and returns the unformatted text.
3023 * caller is responsible for freeing. */
3024char *owl_function_ztext_stylestrip(const char *zt)
3025{
3026  owl_fmtext fm;
3027  char *plaintext;
3028
3029  owl_fmtext_init_null(&fm);
3030  owl_fmtext_append_ztext(&fm, zt);
3031  plaintext = owl_fmtext_print_plain(&fm);
3032  owl_fmtext_cleanup(&fm);
3033  return(plaintext);
3034}
3035
3036/* Popup a buddylisting.  If filename is NULL use the default .anyone */
3037void owl_function_buddylist(int aim, int zephyr, const char *filename)
3038{
3039  int i, j, idle;
3040  int interrupted = 0;
3041  owl_fmtext fm;
3042  const owl_buddylist *bl;
3043  const owl_buddy *b;
3044  char *timestr;
3045#ifdef HAVE_LIBZEPHYR
3046  int x;
3047  owl_list anyone;
3048  const char *user;
3049  char *tmp;
3050  ZLocations_t location[200];
3051  int numlocs, ret;
3052#endif
3053
3054  owl_fmtext_init_null(&fm);
3055
3056  /* AIM first */
3057  if (aim && owl_global_is_aimloggedin(&g)) {
3058    bl=owl_global_get_buddylist(&g);
3059
3060    owl_fmtext_append_bold(&fm, "AIM users logged in:\n");
3061    /* we're assuming AIM for now */
3062    j=owl_buddylist_get_size(bl);
3063    for (i=0; i<j; i++) {
3064      b=owl_buddylist_get_buddy_n(bl, i);
3065      idle=owl_buddy_get_idle_time(b);
3066      if (idle!=0) {
3067        timestr=owl_util_minutes_to_timestr(idle);
3068      } else {
3069        timestr=owl_strdup("");
3070      }
3071      owl_fmtext_appendf_normal(&fm, "  %-20.20s %-12.12s\n", owl_buddy_get_name(b), timestr);
3072      owl_free(timestr);
3073    }
3074  }
3075
3076#ifdef HAVE_LIBZEPHYR
3077  if (zephyr) {
3078    if(!owl_global_is_havezephyr(&g)) {
3079      owl_function_error("Zephyr currently not available.");
3080    } else {
3081      owl_fmtext_append_bold(&fm, "Zephyr users logged in:\n");
3082      owl_list_create(&anyone);
3083      ret=owl_zephyr_get_anyone_list(&anyone, filename);
3084      if (ret) {
3085        if (errno == ENOENT) {
3086          owl_fmtext_append_normal(&fm, " You have not added any zephyr buddies.  Use the\n");
3087          owl_fmtext_append_normal(&fm, " command ':addbuddy zephyr ");
3088          owl_fmtext_append_bold(  &fm, "<username>");
3089          owl_fmtext_append_normal(&fm, "'.\n");
3090        } else {
3091          owl_fmtext_append_normal(&fm, " Could not read zephyr buddies from the .anyone file.\n");
3092        }
3093      } else {
3094        j=owl_list_get_size(&anyone);
3095        for (i=0; i<j; i++) {
3096          user=owl_list_get_element(&anyone, i);
3097          ret=ZLocateUser(zstr(user), &numlocs, ZAUTH);
3098
3099          owl_function_mask_sigint(NULL);
3100          if(owl_global_is_interrupted(&g)) {
3101            interrupted = 1;
3102            owl_global_unset_interrupted(&g);
3103            owl_function_unmask_sigint(NULL);
3104            owl_function_makemsg("Interrupted!");
3105            break;
3106          }
3107
3108          owl_function_unmask_sigint(NULL);
3109
3110          if (ret!=ZERR_NONE) {
3111            owl_function_error("Error getting location for %s", user);
3112            continue;
3113          }
3114
3115          numlocs=200;
3116          ret=ZGetLocations(location, &numlocs);
3117          if (ret==0) {
3118            for (x=0; x<numlocs; x++) {
3119              tmp=short_zuser(user);
3120              owl_fmtext_appendf_normal(&fm, "  %-10.10s %-24.24s %-12.12s  %20.20s\n",
3121                                        tmp,
3122                                        location[x].host,
3123                                        location[x].tty,
3124                                        location[x].time);
3125              owl_free(tmp);
3126            }
3127            if (numlocs>=200) {
3128              owl_fmtext_append_normal(&fm, "  Too many locations found for this user, truncating.\n");
3129            }
3130          }
3131        }
3132      }
3133      owl_list_cleanup(&anyone, owl_free);
3134    }
3135  }
3136#endif
3137
3138  if (aim && zephyr) {
3139    if (owl_perlconfig_is_function("BarnOwl::Hooks::_get_blist")) {
3140      char * perlblist = owl_perlconfig_execute("BarnOwl::Hooks::_get_blist()");
3141      if (perlblist) {
3142        owl_fmtext_append_ztext(&fm, perlblist);
3143        owl_free(perlblist);
3144      }
3145    }
3146  }
3147
3148  if(!interrupted) {
3149    owl_function_popless_fmtext(&fm);
3150  }
3151  owl_fmtext_cleanup(&fm);
3152}
3153
3154/* Dump messages in the current view to the file 'filename'. */
3155void owl_function_dump(const char *filename) 
3156{
3157  int i, j;
3158  owl_message *m;
3159  const owl_view *v;
3160  FILE *file;
3161  char *plaintext;
3162
3163  v=owl_global_get_current_view(&g);
3164
3165  /* in the future make it ask yes/no */
3166  /*
3167  ret=stat(filename, &sbuf);
3168  if (!ret) {
3169    ret=owl_function_askyesno("File exists, continue? [Y/n]");
3170    if (!ret) return;
3171  }
3172  */
3173
3174  file=fopen(filename, "w");
3175  if (!file) {
3176    owl_function_error("Error opening file");
3177    return;
3178  }
3179
3180  j=owl_view_get_size(v);
3181  for (i=0; i<j; i++) {
3182    m=owl_view_get_element(v, i);
3183    plaintext = owl_strip_format_chars(owl_message_get_text(m));
3184    if (plaintext) {
3185      fputs(plaintext, file);
3186      owl_free(plaintext);
3187    }
3188  }
3189  fclose(file);
3190  owl_function_makemsg("Messages dumped to %s", filename);
3191}
3192
3193void owl_function_do_newmsgproc(void)
3194{
3195  if (owl_global_get_newmsgproc(&g) && strcmp(owl_global_get_newmsgproc(&g), "")) {
3196    /* if there's a process out there, we need to check on it */
3197    if (owl_global_get_newmsgproc_pid(&g)) {
3198      owl_function_debugmsg("Checking on newmsgproc pid==%i", owl_global_get_newmsgproc_pid(&g));
3199      owl_function_debugmsg("Waitpid return is %i", waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG));
3200      waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG);
3201      if (waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG)==-1) {
3202        /* it exited */
3203        owl_global_set_newmsgproc_pid(&g, 0);
3204        owl_function_debugmsg("newmsgproc exited");
3205      } else {
3206        owl_function_debugmsg("newmsgproc did not exit");
3207      }
3208    }
3209   
3210    /* if it exited, fork & exec a new one */
3211    if (owl_global_get_newmsgproc_pid(&g)==0) {
3212      pid_t i;
3213      int myargc;
3214      i=fork();
3215      if (i) {
3216        /* parent set the child's pid */
3217        owl_global_set_newmsgproc_pid(&g, i);
3218        owl_function_debugmsg("I'm the parent and I started a new newmsgproc with pid %i", i);
3219      } else {
3220        /* child exec's the program */
3221        char **parsed;
3222        parsed=owl_parseline(owl_global_get_newmsgproc(&g), &myargc);
3223        if (myargc < 0) {
3224          owl_function_debugmsg("Could not parse newmsgproc '%s': unbalanced quotes?", owl_global_get_newmsgproc(&g));
3225        }
3226        if (myargc <= 0) {
3227          _exit(127);
3228        }
3229        parsed=owl_realloc(parsed, sizeof(*parsed) * (myargc+1));
3230        parsed[myargc] = NULL;
3231       
3232        owl_function_debugmsg("About to exec \"%s\" with %d arguments", parsed[0], myargc);
3233       
3234        execvp(parsed[0], parsed);
3235       
3236       
3237        /* was there an error exec'ing? */
3238        owl_function_debugmsg("Cannot run newmsgproc '%s': cannot exec '%s': %s", 
3239                              owl_global_get_newmsgproc(&g), parsed[0], strerror(errno));
3240        _exit(127);
3241      }
3242    }
3243  }
3244}
3245
3246/* print the xterm escape sequence to raise the window */
3247void owl_function_xterm_raise(void)
3248{
3249  printf("\033[5t");
3250}
3251
3252/* print the xterm escape sequence to deiconify the window */
3253void owl_function_xterm_deiconify(void)
3254{
3255  printf("\033[1t");
3256}
3257
3258/* Add the specified command to the startup file.  Eventually this
3259 * should be clever, and rewriting settings that will obviosly
3260 * override earlier settings with 'set' 'bindkey' and 'alias'
3261 * commands.  For now though we just remove any line that would
3262 * duplicate this one and then append this line to the end of
3263 * startupfile.
3264 */
3265void owl_function_addstartup(const char *buff)
3266{
3267  FILE *file;
3268  const char *filename;
3269
3270  filename=owl_global_get_startupfile(&g);
3271
3272  /* delete earlier copies */
3273  owl_util_file_deleteline(filename, buff, 1);
3274
3275  file=fopen(filename, "a");
3276  if (!file) {
3277    owl_function_error("Error opening startupfile for new command");
3278    return;
3279  }
3280
3281  /* add this line */
3282  fprintf(file, "%s\n", buff);
3283
3284  fclose(file);
3285}
3286
3287/* Remove the specified command from the startup file. */
3288void owl_function_delstartup(const char *buff)
3289{
3290  const char *filename;
3291  filename=owl_global_get_startupfile(&g);
3292  owl_util_file_deleteline(filename, buff, 1);
3293}
3294
3295/* Execute owl commands from the given filename.  If the filename
3296 * is NULL, use the default owl startup commands file.
3297 */
3298void owl_function_source(const char *filename)
3299{
3300  char *path;
3301  FILE *file;
3302  char *s = NULL;
3303  int fail_silent = 0;
3304
3305  if (!filename) {
3306    fail_silent = 1;
3307    path = owl_strdup(owl_global_get_startupfile(&g));
3308  } else {
3309    path = owl_util_makepath(filename);
3310  }
3311  file = fopen(path, "r");
3312  owl_free(path);
3313  if (!file) {
3314    if (!fail_silent) {
3315      owl_function_error("Error opening file: %s", filename);
3316    }
3317    return;
3318  }
3319  while (owl_getline_chomp(&s, file)) {
3320    if (s[0] == '\0' || s[0] == '#')
3321      continue;
3322    owl_function_command(s);
3323  }
3324
3325  owl_free(s);
3326  fclose(file);
3327}
3328
3329void owl_function_change_style(owl_view *v, const char *stylename)
3330{
3331  const owl_style *s;
3332
3333  s=owl_global_get_style_by_name(&g, stylename);
3334  if (!s) {
3335    owl_function_error("No style named %s", stylename);
3336    return;
3337  }
3338  owl_view_set_style(v, s);
3339  owl_messagelist_invalidate_formats(owl_global_get_msglist(&g));
3340  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
3341  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3342}
3343
3344void owl_function_toggleoneline(void)
3345{
3346  owl_view *v;
3347  const owl_style *s;
3348
3349  v=owl_global_get_current_view(&g);
3350  s=owl_view_get_style(v);
3351
3352  if (!owl_style_matches_name(s, "oneline")) {
3353    owl_function_change_style(v, "oneline");
3354  } else {
3355    owl_function_change_style(v, owl_global_get_default_style(&g));
3356  }
3357
3358  owl_messagelist_invalidate_formats(owl_global_get_msglist(&g));
3359  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
3360  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3361}
3362
3363void owl_function_error(const char *fmt, ...)
3364{
3365  static int in_error = 0;
3366  va_list ap;
3367  char *buff;
3368  const char *nl;
3369
3370  if (++in_error > 2) {
3371    /* More than two nested errors, bail immediately. */
3372    in_error--;
3373    return;
3374  }
3375
3376  va_start(ap, fmt);
3377  buff = g_strdup_vprintf(fmt, ap);
3378  va_end(ap);
3379
3380  owl_function_debugmsg("ERROR: %s", buff);
3381  owl_function_log_err(buff);
3382
3383  nl = strchr(buff, '\n');
3384
3385  /*
3386    Showing admin messages triggers a lot of code. If we have a
3387    recursive error call, that's the most likely candidate, so
3388    suppress the call in that case, to try to avoid infinite looping.
3389  */
3390
3391  if(nl && *(nl + 1) && in_error == 1) {
3392    /* Multiline error */
3393    owl_function_adminmsg("ERROR", buff);
3394  } else {
3395    owl_function_makemsg("[Error] %s", buff);
3396  }
3397
3398  owl_free(buff);
3399
3400  in_error--;
3401}
3402
3403void owl_function_log_err(const char *string)
3404{
3405  char *date;
3406  time_t now;
3407  char *buff;
3408
3409  now=time(NULL);
3410  date=owl_strdup(ctime(&now));
3411  date[strlen(date)-1]='\0';
3412
3413  buff = owl_sprintf("%s %s", date, string);
3414
3415  owl_errqueue_append_err(owl_global_get_errqueue(&g), buff);
3416
3417  owl_free(buff);
3418  owl_free(date);
3419}
3420
3421void owl_function_showerrs(void)
3422{
3423  owl_fmtext fm;
3424
3425  owl_fmtext_init_null(&fm);
3426  owl_fmtext_append_normal(&fm, "Errors:\n\n");
3427  owl_errqueue_to_fmtext(owl_global_get_errqueue(&g), &fm);
3428  owl_function_popless_fmtext(&fm);
3429}
3430
3431void owl_function_makemsg(const char *fmt, ...)
3432{
3433  va_list ap;
3434  char *str;
3435
3436  va_start(ap, fmt);
3437  str = g_strdup_vprintf(fmt, ap);
3438  va_end(ap);
3439
3440  owl_function_debugmsg("makemsg: %s", str);
3441  owl_msgwin_set_text_nocopy(&g.msgwin, str);
3442}
3443
3444/* get locations for everyone in .anyone.  If 'notify' is '1' then
3445 * send a pseudo login or logout message for everyone not in sync with
3446 * the global zephyr buddy list.  The list is updated regardless of
3447 * the status of 'notify'.
3448 */
3449void owl_function_zephyr_buddy_check(int notify)
3450{
3451#ifdef HAVE_LIBZEPHYR
3452  int i, j;
3453  owl_list anyone;
3454  GList **zaldlist;
3455  GList *zaldptr;
3456  ZAsyncLocateData_t *zald;
3457  const char *user;
3458
3459  if (!owl_global_is_havezephyr(&g)) return;
3460  owl_global_set_pseudologin_notify(&g, notify);
3461  zaldlist = owl_global_get_zaldlist(&g);
3462
3463  /* Clear the existing ZALDs first. */
3464  zaldptr = g_list_first(*zaldlist);
3465  while (zaldptr) {
3466    ZFreeALD(zaldptr->data);
3467    owl_free(zaldptr->data);
3468    zaldptr = g_list_next(zaldptr);
3469  }
3470  g_list_free(*zaldlist);
3471  *zaldlist = NULL;
3472
3473  owl_list_create(&anyone);
3474  owl_zephyr_get_anyone_list(&anyone, NULL);
3475  j = owl_list_get_size(&anyone);
3476  for (i = 0; i < j; i++) {
3477    user = owl_list_get_element(&anyone, i);
3478    zald = owl_malloc(sizeof(ZAsyncLocateData_t));
3479    if (ZRequestLocations(zstr(user), zald, UNACKED, ZAUTH) == ZERR_NONE) {
3480      *zaldlist = g_list_append(*zaldlist, zald);
3481    } else {
3482      owl_free(zald);
3483    }
3484  }
3485
3486  owl_list_cleanup(&anyone, owl_free);
3487#endif
3488}
3489
3490void owl_function_aimsearch_results(const char *email, owl_list *namelist)
3491{
3492  owl_fmtext fm;
3493  int i, j;
3494
3495  owl_fmtext_init_null(&fm);
3496  owl_fmtext_append_normal(&fm, "AIM screennames associated with ");
3497  owl_fmtext_append_normal(&fm, email);
3498  owl_fmtext_append_normal(&fm, ":\n");
3499
3500  j=owl_list_get_size(namelist);
3501  for (i=0; i<j; i++) {
3502    owl_fmtext_append_normal(&fm, "  ");
3503    owl_fmtext_append_normal(&fm, owl_list_get_element(namelist, i));
3504    owl_fmtext_append_normal(&fm, "\n");
3505  }
3506
3507  owl_function_popless_fmtext(&fm);
3508  owl_fmtext_cleanup(&fm);
3509}
3510
3511int owl_function_get_color_count(void)
3512{
3513     return COLORS;
3514}
3515
3516void owl_function_mask_sigint(sigset_t *oldmask) {
3517  sigset_t intr;
3518
3519  sigemptyset(&intr);
3520  sigaddset(&intr, SIGINT);
3521  sigprocmask(SIG_BLOCK, &intr, oldmask);
3522}
3523
3524void owl_function_unmask_sigint(sigset_t *oldmask) {
3525  sigset_t intr;
3526
3527  sigemptyset(&intr);
3528  sigaddset(&intr, SIGINT);
3529  sigprocmask(SIG_UNBLOCK, &intr, oldmask);
3530}
3531
3532void _owl_function_mark_message(const owl_message *m)
3533{
3534  if (m) {
3535    owl_global_set_markedmsgid(&g, owl_message_get_id(m));
3536    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3537  }
3538}
3539
3540void owl_function_mark_message(void)
3541{
3542  const owl_message *m;
3543  const owl_view *v;
3544
3545  v=owl_global_get_current_view(&g);
3546
3547  /* bail if there's no current message */
3548  if (owl_view_get_size(v) < 1) {
3549    owl_function_error("No messages to mark");
3550    return;
3551  }
3552
3553  /* mark the message */
3554  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
3555  _owl_function_mark_message(m);
3556  owl_function_makemsg("Mark set");
3557}
3558
3559void owl_function_swap_cur_marked(void)
3560{
3561  int marked_id;
3562  const owl_message *m;
3563  const owl_view *v;
3564
3565  marked_id=owl_global_get_markedmsgid(&g);
3566  if (marked_id == -1) {
3567    owl_function_error("Mark not set.");
3568    return;
3569  }
3570
3571  v=owl_global_get_current_view(&g);
3572  /* bail if there's no current message */
3573  if (owl_view_get_size(v) < 1) {
3574    return;
3575  }
3576
3577  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
3578  _owl_function_mark_message(m);
3579  owl_global_set_curmsg(&g, owl_view_get_nearest_to_msgid(v, marked_id));
3580  owl_function_calculate_topmsg(OWL_DIRECTION_NONE);
3581  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3582  owl_global_set_direction_downwards(&g);
3583}
Note: See TracBrowser for help on using the repository browser.