source: functions.c @ 7dfe886

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