source: functions.c @ fe569d7

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