source: functions.c @ f25df21

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