source: functions.c @ 8135737

release-1.10release-1.9
Last change on this file since 8135737 was ecfbdcc, checked in by David Benjamin <davidben@mit.edu>, 13 years ago
Don't leak old_msg in owl_function_zcrypt
  • Property mode set to 100644
File size: 97.1 KB
Line 
1#include "owl.h"
2#include "filterproc.h"
3#include <stdio.h>
4#include <sys/stat.h>
5#include <sys/wait.h>
6
7CALLER_OWN char *owl_function_command(const char *cmdbuff)
8{
9  owl_function_debugmsg("executing command: %s", cmdbuff);
10  return owl_cmddict_execute(owl_global_get_cmddict(&g), 
11                             owl_global_get_context(&g), cmdbuff);
12}
13
14CALLER_OWN char *owl_function_command_argv(const char *const *argv, int argc)
15{
16  return owl_cmddict_execute_argv(owl_global_get_cmddict(&g),
17                                  owl_global_get_context(&g),
18                                  argv, argc);
19}
20
21void owl_function_command_norv(const char *cmdbuff)
22{
23  char *rv;
24  rv=owl_function_command(cmdbuff);
25  g_free(rv);
26}
27
28void owl_function_command_alias(const char *alias_from, const char *alias_to)
29{
30  owl_cmddict_add_alias(owl_global_get_cmddict(&g), alias_from, alias_to);
31}
32
33const owl_cmd *owl_function_get_cmd(const char *name)
34{
35  return owl_cmddict_find(owl_global_get_cmddict(&g), name);
36}
37
38void owl_function_show_commands(void)
39{
40  GPtrArray *l;
41  owl_fmtext fm;
42
43  owl_fmtext_init_null(&fm);
44  owl_fmtext_append_bold(&fm, "Commands:  ");
45  owl_fmtext_append_normal(&fm, "(use 'show command <name>' for details)\n");
46  l = owl_cmddict_get_names(owl_global_get_cmddict(&g));
47  owl_fmtext_append_list(&fm, l, "\n", owl_function_cmd_describe);
48  owl_fmtext_append_normal(&fm, "\n");
49  owl_function_popless_fmtext(&fm);
50  owl_ptr_array_free(l, g_free);
51  owl_fmtext_cleanup(&fm);
52}
53
54void owl_function_show_view(const char *viewname)
55{
56  const owl_view *v;
57  owl_fmtext fm;
58
59  /* we only have the one view right now */
60  v=owl_global_get_current_view(&g);
61  if (viewname && strcmp(viewname, owl_view_get_name(v))) {
62    owl_function_error("No view named '%s'", viewname);
63    return;
64  }
65
66  owl_fmtext_init_null(&fm);
67  owl_view_to_fmtext(v, &fm);
68  owl_function_popless_fmtext(&fm);
69  owl_fmtext_cleanup(&fm);
70}
71
72void owl_function_show_styles(void) {
73  GPtrArray *l;
74  owl_fmtext fm;
75
76  owl_fmtext_init_null(&fm);
77  owl_fmtext_append_bold(&fm, "Styles:\n");
78  l = owl_global_get_style_names(&g);
79  owl_fmtext_append_list(&fm, l, "\n", owl_function_style_describe);
80  owl_fmtext_append_normal(&fm, "\n");
81  owl_function_popless_fmtext(&fm);
82  owl_ptr_array_free(l, g_free);
83  owl_fmtext_cleanup(&fm);
84}
85
86CALLER_OWN char *owl_function_style_describe(const char *name)
87{
88  const char *desc;
89  char *s;
90  const owl_style *style;
91  style = owl_global_get_style_by_name(&g, name);
92  if (style) {
93    desc = owl_style_get_description(style);
94  } else {
95    desc = "???";
96  }
97  s = g_strdup_printf("%-20s - %s%s", name,
98                      0 == owl_style_validate(style) ? "" : "[INVALID] ",
99                      desc);
100  return s;
101}
102
103CALLER_OWN char *owl_function_cmd_describe(const char *name)
104{
105  const owl_cmd *cmd = owl_cmddict_find(owl_global_get_cmddict(&g), name);
106  if (cmd) return owl_cmd_describe(cmd);
107  else return(NULL);
108}
109
110void owl_function_show_command(const char *name)
111{
112  owl_function_help_for_command(name);
113}
114
115void owl_function_show_license(void)
116{
117  const char *text;
118
119  text=""
120    "BarnOwl version " OWL_VERSION_STRING "\n"
121    "Copyright (c) 2006-2011 The BarnOwl Developers. All rights reserved.\n"
122    "Copyright (c) 2004 James Kretchmar. All rights reserved.\n"
123    "\n"
124    "Redistribution and use in source and binary forms, with or without\n"
125    "modification, are permitted provided that the following conditions are\n"
126    "met:\n"
127    "\n"
128    "   * Redistributions of source code must retain the above copyright\n"
129    "     notice, this list of conditions and the following disclaimer.\n"
130    "\n"
131    "   * Redistributions in binary form must reproduce the above copyright\n"
132    "     notice, this list of conditions and the following disclaimer in\n"
133    "     the documentation and/or other materials provided with the\n"
134    "     distribution.\n"
135    "\n"
136    "   * Redistributions in any form must be accompanied by information on\n"
137    "     how to obtain complete source code for the BarnOwl software and any\n"
138    "     accompanying software that uses the BarnOwl software. The source code\n"
139    "     must either be included in the distribution or be available for no\n"
140    "     more than the cost of distribution plus a nominal fee, and must be\n"
141    "     freely redistributable under reasonable conditions. For an\n"
142    "     executable file, complete source code means the source code for\n"
143    "     all modules it contains. It does not include source code for\n"
144    "     modules or files that typically accompany the major components of\n"
145    "     the operating system on which the executable file runs.\n"
146    "\n"
147    "THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
148    "IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n"
149    "WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR\n"
150    "NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE\n"
151    "LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n"
152    "CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n"
153    "SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR\n"
154    "BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n"
155    "WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE\n"
156    "OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN\n"
157    "IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n";
158  owl_function_popless_text(text);
159}
160
161void owl_function_show_quickstart(void)
162{
163    const char *message =
164    "Move between messages with the arrow keys, and press 'r' to reply.\n"
165    "For more info, press 'h' or visit http://barnowl.mit.edu/\n\n"
166#ifdef HAVE_LIBZEPHYR
167    "@b(Zephyr:)\n"
168    "To send a message to a user, type ':zwrite @b(username)'. You can also\n"
169    "press 'z' and then type the username. To subscribe to a class, type\n"
170    "':sub @b(class)', and then type ':zwrite -c @b(class)' to send.\n\n"
171#endif
172    "@b(AIM:)\n"
173    "Log in to AIM with ':aimlogin @b(screenname)'. Use ':aimwrite @b(screenname)',\n"
174    "or 'a' and then the screen name, to send someone a message.\n\n"
175    ;
176
177    if (owl_perlconfig_is_function("BarnOwl::Hooks::_get_quickstart")) {
178        char *perlquickstart = owl_perlconfig_execute("BarnOwl::Hooks::_get_quickstart()");
179        if (perlquickstart) {
180            char *result = g_strdup_printf("%s%s", message, perlquickstart);
181            owl_function_adminmsg("BarnOwl Quickstart", result);
182            g_free(result);
183            g_free(perlquickstart);
184            return;
185        }
186    }
187    owl_function_adminmsg("BarnOwl Quickstart", message);
188}
189
190
191/* Create an admin message, append it to the global list of messages
192 * and redisplay if necessary.
193 */
194void owl_function_adminmsg(const char *header, const char *body)
195{
196  owl_message *m;
197
198  m=g_new(owl_message, 1);
199  owl_message_create_admin(m, header, body);
200 
201  /* add it to the global list and current view */
202  owl_messagelist_append_element(owl_global_get_msglist(&g), m);
203  owl_view_consider_message(owl_global_get_current_view(&g), m);
204
205  /* do followlast if necessary */
206  if (owl_global_should_followlast(&g)) owl_function_lastmsg();
207
208  /* redisplay etc. */
209  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
210}
211
212/* Queues outgoing zephyrs; if z sends to n people, queue n messages
213 * (except in case of cc). If there are no recipients queues 1
214 * message.
215 */
216void owl_function_add_outgoing_zephyrs(const owl_zwrite *z)
217{
218  if (z->cc || owl_zwrite_get_numrecips(z) == 0) {
219    /* create the message */
220    owl_message *m = g_new(owl_message, 1);
221    owl_message_create_from_zwrite(m, z, owl_zwrite_get_message(z), 0);
222
223    owl_global_messagequeue_addmsg(&g, m);
224  } else {
225    int i;
226    for (i = 0; i < owl_zwrite_get_numrecips(z); i++) {
227      /* create the message */
228      owl_message *m = g_new(owl_message, 1);
229      owl_message_create_from_zwrite(m, z, owl_zwrite_get_message(z), i);
230
231      owl_global_messagequeue_addmsg(&g, m);
232    }
233  }
234}
235
236/* Create an outgoing AIM message, returns a pointer to the created
237 * message or NULL if we're not logged into AIM (and thus unable to
238 * create the message).  Does not put it on the global queue.  Use
239 * owl_global_messagequeue_addmsg() for that.
240 */
241CALLER_OWN owl_message *owl_function_make_outgoing_aim(const char *body, const char *to)
242{
243  owl_message *m;
244
245  /* error if we're not logged into aim */
246  if (!owl_global_is_aimloggedin(&g)) return(NULL);
247 
248  m=g_new(owl_message, 1);
249  owl_message_create_aim(m,
250                         owl_global_get_aim_screenname(&g),
251                         to,
252                         body,
253                         OWL_MESSAGE_DIRECTION_OUT,
254                         0);
255  return(m);
256}
257
258/* Create an outgoing loopback message and return a pointer to it.
259 * Does not append it to the global queue, use
260 * owl_global_messagequeue_addmsg() for that.
261 */
262CALLER_OWN owl_message *owl_function_make_outgoing_loopback(const char *body)
263{
264  owl_message *m;
265
266  /* create the message */
267  m=g_new(owl_message, 1);
268  owl_message_create_loopback(m, body);
269  owl_message_set_direction_out(m);
270
271  return(m);
272}
273
274owl_editwin *owl_function_start_edit_win(const char *line)
275{
276  owl_editwin *e;
277  owl_context *ctx;
278  char *s;
279
280  /* create and setup the editwin */
281  e = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_MULTILINE,
282                                   owl_global_get_msg_history(&g));
283  owl_editwin_set_dotsend(e);
284  s = g_strdup_printf("----> %s\n", line);
285  owl_editwin_set_locktext(e, s);
286  g_free(s);
287
288  ctx = owl_editcontext_new(OWL_CTX_EDITMULTI, e, "editmulti",
289                            owl_global_deactivate_editcontext, &g);
290  owl_global_push_context_obj(&g, ctx);
291  return e;
292}
293
294static void owl_function_write_setup(const char *noun)
295{
296
297  if (!owl_global_get_lockout_ctrld(&g))
298    owl_function_makemsg("Type your %s below.  "
299                         "End with ^D or a dot on a line by itself."
300                         "  ^C will quit.", noun);
301  else
302    owl_function_makemsg("Type your %s below.  "
303                         "End with a dot on a line by itself.  ^C will quit.",
304                         noun);
305}
306
307void owl_function_zwrite_setup(owl_zwrite *z)
308{
309  owl_editwin *e;
310  /* send a ping if necessary */
311  if (owl_global_is_txping(&g)) {
312    owl_zwrite_send_ping(z);
313  }
314
315
316  owl_function_write_setup("zephyr");
317  e = owl_function_start_edit_win(z->zwriteline);
318  owl_editwin_set_cbdata(e, z, (void (*)(void *))owl_zwrite_delete);
319  owl_editwin_set_callback(e, &owl_callback_zwrite);
320}
321
322void owl_function_aimwrite_setup(const char *to)
323{
324  owl_editwin *e;
325  /* TODO: We probably actually want an owl_aimwrite object like
326   * owl_zwrite. */
327  char *line = g_strdup_printf("aimwrite %s", to);
328  owl_function_write_setup("message");
329  e = owl_function_start_edit_win(line);
330  owl_editwin_set_cbdata(e, g_strdup(to), g_free);
331  owl_editwin_set_callback(e, &owl_callback_aimwrite);
332  g_free(line);
333}
334
335void owl_function_loopwrite_setup(void)
336{
337  owl_editwin *e;
338  owl_function_write_setup("message");
339  e = owl_function_start_edit_win("loopwrite");
340  owl_editwin_set_callback(e, &owl_callback_loopwrite);
341}
342
343void owl_callback_zwrite(owl_editwin *e, bool success)
344{
345  if (!success) return;
346  owl_zwrite *z = owl_editwin_get_cbdata(e);
347  owl_function_zwrite(z, owl_editwin_get_text(e));
348}
349
350/* send, log and display outgoing zephyrs.  If 'msg' is NULL the
351 * message is expected to be set from the zwrite line itself
352 */
353#ifdef HAVE_LIBZEPHYR
354void owl_function_zwrite(owl_zwrite *z, const char *msg)
355{
356  int ret;
357
358  if(strcmp(z->cmd, "zcrypt") == 0) {
359    owl_function_zcrypt(z, msg);
360    return;
361  }
362
363  /* create the zwrite and send the message */
364  owl_zwrite_populate_zsig(z);
365  if (msg) {
366    owl_zwrite_set_message(z, msg);
367  }
368  ret = owl_zwrite_send_message(z);
369  if (ret != 0) {
370    owl_function_makemsg("Error sending zephyr: %s", error_message(ret));
371    return;
372  }
373  owl_function_makemsg("Waiting for ack...");
374
375  /* If it's personal */
376  if (owl_zwrite_is_personal(z)) {
377    /* create the outgoing message */
378    owl_function_add_outgoing_zephyrs(z);
379  }
380}
381#else
382void owl_function_zwrite(owl_zwrite *z, const char *msg) {
383}
384#endif
385
386/* send, log and display outgoing zcrypt zephyrs.  If 'msg' is NULL
387 * the message is expected to be set from the zwrite line itself
388 */
389void owl_function_zcrypt(owl_zwrite *z, const char *msg)
390{
391  char *cryptmsg;
392  const char *argv[7];
393  char *zcrypt;
394  int rv, status;
395  char *old_msg;
396
397  /* create the zwrite and send the message */
398  owl_zwrite_populate_zsig(z);
399  if (msg) {
400    owl_zwrite_set_message(z, msg);
401  }
402  old_msg = g_strdup(owl_zwrite_get_message(z));
403
404  zcrypt = g_build_filename(owl_get_bindir(), "zcrypt", NULL);
405  argv[0] = "zcrypt";
406  argv[1] = "-E";
407  argv[2] = "-c"; argv[3] = owl_zwrite_get_class(z);
408  argv[4] = "-i"; argv[5] = owl_zwrite_get_instance(z);
409  argv[6] = NULL;
410
411  rv = call_filter(zcrypt, argv, owl_zwrite_get_message(z), &cryptmsg, &status);
412
413  g_free(zcrypt);
414
415  if (rv || status) {
416    g_free(cryptmsg);
417    g_free(old_msg);
418    owl_function_error("Error in zcrypt, possibly no key found.  Message not sent.");
419    owl_function_beep();
420    return;
421  }
422
423  owl_zwrite_set_message_raw(z, cryptmsg);
424  owl_zwrite_set_opcode(z, "crypt");
425
426  owl_zwrite_send_message(z);
427  owl_function_makemsg("Waiting for ack...");
428
429  /* If it's personal */
430  if (owl_zwrite_is_personal(z)) {
431    /* Create the outgoing message. Restore the un-crypted message for display. */
432    owl_zwrite_set_message_raw(z, old_msg);
433    owl_function_add_outgoing_zephyrs(z);
434  }
435
436  /* Clean up. */
437  g_free(cryptmsg);
438  g_free(old_msg);
439}
440
441void owl_callback_aimwrite(owl_editwin *e, bool success)
442{
443  if (!success) return;
444  char *to = owl_editwin_get_cbdata(e);
445  owl_function_aimwrite(to, owl_editwin_get_text(e), true);
446}
447
448void owl_function_aimwrite(const char *to, const char *msg, bool unwrap)
449{
450  int ret;
451  char *format_msg;
452  owl_message *m;
453
454  /* make a formatted copy of the message */
455  format_msg = g_strdup(msg);
456  if (unwrap)
457    owl_text_wordunwrap(format_msg);
458 
459  /* send the message */
460  ret=owl_aim_send_im(to, format_msg);
461  if (!ret) {
462    owl_function_makemsg("AIM message sent.");
463  } else {
464    owl_function_error("Could not send AIM message.");
465  }
466
467  /* create the outgoing message */
468  m=owl_function_make_outgoing_aim(msg, to);
469
470  if (m) {
471    owl_global_messagequeue_addmsg(&g, m);
472  } else {
473    owl_function_error("Could not create outgoing AIM message");
474  }
475
476  g_free(format_msg);
477}
478
479void owl_function_send_aimawymsg(const char *to, const char *msg)
480{
481  int ret;
482  char *format_msg;
483  owl_message *m;
484
485  /* make a formatted copy of the message */
486  format_msg=g_strdup(msg);
487  owl_text_wordunwrap(format_msg);
488 
489  /* send the message */
490  ret=owl_aim_send_awaymsg(to, format_msg);
491  if (!ret) {
492    /* owl_function_makemsg("AIM message sent."); */
493  } else {
494    owl_function_error("Could not send AIM message.");
495  }
496
497  /* create the message */
498  m=owl_function_make_outgoing_aim(msg, to);
499  if (m) {
500    owl_global_messagequeue_addmsg(&g, m);
501  } else {
502    owl_function_error("Could not create AIM message");
503  }
504  g_free(format_msg);
505}
506
507void owl_callback_loopwrite(owl_editwin *e, bool success)
508{
509  if (!success) return;
510  owl_function_loopwrite(owl_editwin_get_text(e));
511}
512
513void owl_function_loopwrite(const char *msg)
514{
515  owl_message *min, *mout;
516
517  /* create a message and put it on the message queue.  This simulates
518   * an incoming message */
519  min=g_new(owl_message, 1);
520  mout=owl_function_make_outgoing_loopback(msg);
521
522  if (owl_global_is_displayoutgoing(&g)) {
523    owl_global_messagequeue_addmsg(&g, mout);
524  } else {
525    owl_message_delete(mout);
526  }
527
528  owl_message_create_loopback(min, msg);
529  owl_message_set_direction_in(min);
530  owl_global_messagequeue_addmsg(&g, min);
531
532  /* fake a makemsg */
533  owl_function_makemsg("loopback message sent");
534}
535
536/* If filter is non-null, looks for the next message matching
537 * that filter.  If skip_deleted, skips any deleted messages.
538 * If last_if_none, will stop at the last message in the view
539 * if no matching messages are found.  */
540void owl_function_nextmsg_full(const char *filter, int skip_deleted, int last_if_none)
541{
542  int curmsg, i, viewsize, found;
543  const owl_view *v;
544  const owl_filter *f = NULL;
545  const owl_message *m;
546
547  v=owl_global_get_current_view(&g);
548
549  if (filter) {
550    f=owl_global_get_filter(&g, filter);
551    if (!f) {
552      owl_function_error("No %s filter defined", filter);
553      return;
554    }
555  }
556
557  curmsg=owl_global_get_curmsg(&g);
558  viewsize=owl_view_get_size(v);
559  found=0;
560
561  /* just check to make sure we're in bounds... */
562  if (curmsg>viewsize-1) curmsg=viewsize-1;
563  if (curmsg<0) curmsg=0;
564
565  for (i=curmsg+1; i<viewsize; i++) {
566    m=owl_view_get_element(v, i);
567    if (skip_deleted && owl_message_is_delete(m)) continue;
568    if (f && !owl_filter_message_match(f, m)) continue;
569    found = 1;
570    break;
571  }
572
573  if (i>owl_view_get_size(v)-1) i=owl_view_get_size(v)-1;
574  if (i<0) i=0;
575
576  if (!found) {
577    owl_function_makemsg("already at last%s message%s%s%s",
578                         skip_deleted?" non-deleted":"",
579                         filter?" in ":"", filter?filter:"",
580                         owl_mainwin_is_curmsg_truncated(owl_global_get_mainwin(&g)) ?
581                         ", press Enter to scroll" : "");
582    /* if (!skip_deleted) owl_function_beep(); */
583  }
584
585  if (last_if_none || found) {
586    owl_global_set_curmsg(&g, i);
587    owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
588    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
589    owl_global_set_direction_downwards(&g);
590  }
591}
592
593void owl_function_prevmsg_full(const char *filter, int skip_deleted, int first_if_none)
594{
595  int curmsg, i, found;
596  const owl_view *v;
597  const owl_filter *f = NULL;
598  const owl_message *m;
599
600  v=owl_global_get_current_view(&g);
601
602  if (filter) {
603    f=owl_global_get_filter(&g, filter);
604    if (!f) {
605      owl_function_error("No %s filter defined", filter);
606      return;
607    }
608  }
609
610  curmsg=owl_global_get_curmsg(&g);
611  found=0;
612
613  /* just check to make sure we're in bounds... */
614  if (curmsg<0) curmsg=0;
615
616  for (i=curmsg-1; i>=0; i--) {
617    m=owl_view_get_element(v, i);
618    if (skip_deleted && owl_message_is_delete(m)) continue;
619    if (f && !owl_filter_message_match(f, m)) continue;
620    found = 1;
621    break;
622  }
623
624  if (i<0) i=0;
625
626  if (!found) {
627    owl_function_makemsg("already at first%s message%s%s",
628                         skip_deleted?" non-deleted":"",
629                         filter?" in ":"", filter?filter:"");
630    /* if (!skip_deleted) owl_function_beep(); */
631  }
632
633  if (first_if_none || found) {
634    owl_global_set_curmsg(&g, i);
635    owl_function_calculate_topmsg(OWL_DIRECTION_UPWARDS);
636    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
637    owl_global_set_direction_upwards(&g);
638  }
639}
640
641void owl_function_nextmsg(void)
642{
643  owl_function_nextmsg_full(NULL, 0, 1);
644}
645
646void owl_function_prevmsg(void)
647{
648  owl_function_prevmsg_full(NULL, 0, 1);
649}
650
651void owl_function_nextmsg_notdeleted(void)
652{
653  owl_function_nextmsg_full(NULL, 1, 1);
654}
655
656void owl_function_prevmsg_notdeleted(void)
657{
658  owl_function_prevmsg_full(NULL, 1, 1);
659}
660
661void owl_function_delete_and_expunge_message(int n)
662{
663  owl_messagelist *ml = owl_global_get_msglist(&g);
664  owl_view *v = owl_global_get_current_view(&g);
665  int lastmsgid = owl_function_get_curmsg_id(v);
666
667  /* delete and expunge the message */
668  owl_messagelist_delete_and_expunge_element(ml, n);
669
670  owl_function_redisplay_to_nearest(lastmsgid, v);
671}
672
673void owl_function_delete_and_expunge_cur(bool exclaim_success)
674{
675  int curmsg;
676  const owl_view *v = owl_global_get_current_view(&g);
677
678  /* bail if there's no current message */
679  if (owl_view_get_size(v) < 1) {
680    owl_function_error("No current message to delete");
681    return;
682  }
683
684  /* delete the current message */
685  curmsg = owl_global_get_curmsg(&g);
686  owl_function_delete_and_expunge_message(curmsg);
687  if (exclaim_success)
688    owl_function_makemsg("Message deleted and expunged");
689}
690
691/* if move_after is 1, moves after the delete */
692void owl_function_deletecur(int move_after)
693{
694  int curmsg;
695  owl_view *v;
696
697  v=owl_global_get_current_view(&g);
698
699  /* bail if there's no current message */
700  if (owl_view_get_size(v) < 1) {
701    owl_function_error("No current message to delete");
702    return;
703  }
704
705  /* mark the message for deletion */
706  curmsg=owl_global_get_curmsg(&g);
707  owl_view_delete_element(v, curmsg);
708
709  if (move_after) {
710    /* move the poiner in the appropriate direction
711     * to the next undeleted msg */
712    if (owl_global_get_direction(&g)==OWL_DIRECTION_UPWARDS) {
713      owl_function_prevmsg_notdeleted();
714    } else {
715      owl_function_nextmsg_notdeleted();
716    }
717  }
718}
719
720void owl_function_undeletecur(int move_after)
721{
722  int curmsg;
723  owl_view *v;
724
725  v=owl_global_get_current_view(&g);
726 
727  if (owl_view_get_size(v) < 1) {
728    owl_function_error("No current message to undelete");
729    return;
730  }
731  curmsg=owl_global_get_curmsg(&g);
732
733  owl_view_undelete_element(v, curmsg);
734
735  if (move_after) {
736    if (owl_global_get_direction(&g)==OWL_DIRECTION_UPWARDS) {
737      if (curmsg>0) {
738        owl_function_prevmsg();
739      } else {
740        owl_function_nextmsg();
741      }
742    } else {
743      owl_function_nextmsg();
744    }
745  }
746
747  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
748}
749
750/* returns the current message id, if it exists.  Otherwise returns
751 * -1 if we are past the end of the message list, and 0 otherwise. */
752int owl_function_get_curmsg_id(const owl_view *v)
753{
754  int curmsg = owl_global_get_curmsg(&g);
755  const owl_message *m = owl_view_get_element(v, curmsg);
756  if (m)
757    return owl_message_get_id(m);
758  if (curmsg > 0) /* past the end of the message list (probably) */
759    return -1;
760  return 0;
761}
762
763/* redisplays the view to the nearest message to the id given.
764 * if msgid < 0, redisplay to past the end of the message list */
765void owl_function_redisplay_to_nearest(int msgid, owl_view *v)
766{
767  int curmsg;
768  /* update all views (we only have one right now) */
769  owl_view_recalculate(v);
770
771  /* find where the new position should be */
772  if (msgid < 0) {
773    /* If already at the end, blank the screen and move curmsg
774     * past the end of the messages. */
775    curmsg = owl_view_get_size(v);
776    owl_global_set_topmsg(&g, curmsg);
777    owl_global_set_curmsg(&g, curmsg);
778  } else {
779    curmsg = owl_view_get_nearest_to_msgid(v, msgid);
780    if (curmsg > owl_view_get_size(v) - 1)
781      curmsg = owl_view_get_size(v) - 1;
782    if (curmsg < 0)
783      curmsg = 0;
784    owl_global_set_curmsg(&g, curmsg);
785    owl_function_calculate_topmsg(OWL_DIRECTION_NONE);
786  }
787  /* if there are no messages set the direction to down in case we
788   * delete everything upwards */
789  owl_global_set_direction_downwards(&g);
790
791  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
792}
793
794void owl_function_expunge(void)
795{
796  owl_messagelist *ml = owl_global_get_msglist(&g);
797  owl_view *v = owl_global_get_current_view(&g);
798  int lastmsgid = owl_function_get_curmsg_id(v);
799
800  /* expunge the message list */
801  owl_messagelist_expunge(ml);
802
803  owl_function_redisplay_to_nearest(lastmsgid, v);
804 
805  owl_function_makemsg("Messages expunged");
806}
807
808void owl_function_firstmsg(void)
809{
810  owl_global_set_curmsg(&g, 0);
811  owl_global_set_topmsg(&g, 0);
812  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
813  owl_global_set_direction_downwards(&g);
814}
815
816void owl_function_lastmsg(void)
817{
818  int oldcurmsg, curmsg;
819  const owl_view *v;
820
821  v=owl_global_get_current_view(&g);
822  oldcurmsg=owl_global_get_curmsg(&g);
823  curmsg=owl_view_get_size(v)-1; 
824  if (curmsg<0) curmsg=0;
825  owl_global_set_curmsg(&g, curmsg);
826  if (oldcurmsg < curmsg) {
827    owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
828  } else if (curmsg<owl_view_get_size(v)) {
829    /* If already at the end, blank the screen and move curmsg
830     * past the end of the messages. */
831    owl_global_set_topmsg(&g, curmsg+1);
832    owl_global_set_curmsg(&g, curmsg+1);
833  } 
834  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
835  owl_global_set_direction_downwards(&g);
836}
837
838void owl_function_shift_right(void)
839{
840  owl_global_set_rightshift(&g, owl_global_get_rightshift(&g)+10);
841}
842
843void owl_function_shift_left(void)
844{
845  int shift;
846
847  shift=owl_global_get_rightshift(&g);
848  if (shift > 0) {
849    owl_global_set_rightshift(&g, MAX(shift - 10, 0));
850  } else {
851    owl_function_beep();
852    owl_function_makemsg("Already full left");
853  }
854}
855
856void owl_function_unsuball(void)
857{
858  unsuball();
859  owl_function_makemsg("Unsubscribed from all messages.");
860}
861
862
863/* Load zephyr subscriptions from the named 'file' and load zephyr's
864 * default subscriptions as well.  An error message is printed if
865 * 'file' can't be opened or if zephyr reports an error in
866 * subscribing.
867 *
868 * If 'file' is NULL, this look for the default filename
869 * $HOME/.zephyr.subs.  If the file can not be opened in this case
870 * only, no error message is printed.
871 */
872void owl_function_loadsubs(const char *file)
873{
874  int ret, ret2;
875  const char *foo;
876  char *path;
877
878  if (file==NULL) {
879    ret=owl_zephyr_loadsubs(NULL, 0);
880  } else {
881    path = owl_util_makepath(file);
882    ret=owl_zephyr_loadsubs(path, 1);
883    g_free(path);
884  }
885
886  /* for backwards compatibility for now */
887  ret2=owl_zephyr_loaddefaultsubs();
888
889  if (!owl_context_is_interactive(owl_global_get_context(&g))) return;
890
891  foo=file?file:"file";
892  if (ret==0 && ret2==0) {
893    if (!file) {
894      owl_function_makemsg("Subscribed to messages.");
895    } else {
896      owl_function_makemsg("Subscribed to messages from %s", file);
897    }
898  } else if (ret==-1) {
899    owl_function_error("Could not read %s", foo);
900  } else {
901    owl_function_error("Error subscribing to messages");
902  }
903}
904
905void owl_function_loadloginsubs(const char *file)
906{
907  int ret;
908
909  ret=owl_zephyr_loadloginsubs(file);
910
911  if (!owl_context_is_interactive(owl_global_get_context(&g))) return;
912  if (ret==0) {
913  } else if (ret==-1) {
914    owl_function_error("Could not open file for login subscriptions.");
915  } else {
916    owl_function_error("Error subscribing to login messages from file.");
917  }
918}
919
920void owl_callback_aimlogin(owl_editwin *e, bool success)
921{
922  if (!success) return;
923  char *user = owl_editwin_get_cbdata(e);
924  owl_function_aimlogin(user,
925                        owl_editwin_get_text(e));
926}
927
928void owl_function_aimlogin(const char *user, const char *passwd) {
929  int ret;
930
931  /* clear the buddylist */
932  owl_buddylist_clear(owl_global_get_buddylist(&g));
933
934  /* try to login */
935  ret=owl_aim_login(user, passwd);
936  if (ret) owl_function_makemsg("Warning: login for %s failed.\n", user);
937}
938
939void owl_function_suspend(void)
940{
941  endwin();
942  printf("\n");
943  kill(getpid(), SIGSTOP);
944
945  /* resize to reinitialize all the windows when we come back */
946  owl_command_resize();
947}
948
949void owl_function_zaway_toggle(void)
950{
951  if (!owl_global_is_zaway(&g)) {
952    owl_global_set_zaway_msg(&g, owl_global_get_zaway_msg_default(&g));
953    owl_function_zaway_on();
954  } else {
955    owl_function_zaway_off();
956  }
957}
958
959void owl_function_zaway_on(void)
960{
961  owl_global_set_zaway_on(&g);
962  owl_function_makemsg("zaway set (%s)", owl_global_get_zaway_msg(&g));
963}
964
965void owl_function_zaway_off(void)
966{
967  owl_global_set_zaway_off(&g);
968  owl_function_makemsg("zaway off");
969}
970
971void owl_function_aaway_toggle(void)
972{
973  if (!owl_global_is_aaway(&g)) {
974    owl_global_set_aaway_msg(&g, owl_global_get_aaway_msg_default(&g));
975    owl_function_aaway_on();
976  } else {
977    owl_function_aaway_off();
978  }
979}
980
981void owl_function_aaway_on(void)
982{
983  owl_global_set_aaway_on(&g);
984  /* owl_aim_set_awaymsg(owl_global_get_zaway_msg(&g)); */
985  owl_function_makemsg("AIM away set (%s)", owl_global_get_aaway_msg(&g));
986}
987
988void owl_function_aaway_off(void)
989{
990  owl_global_set_aaway_off(&g);
991  /* owl_aim_set_awaymsg(""); */
992  owl_function_makemsg("AIM away off");
993}
994
995void owl_function_quit(void)
996{
997  char *ret;
998 
999  /* zlog out if we need to */
1000  if (owl_global_is_havezephyr(&g) &&
1001      owl_global_is_shutdownlogout(&g)) {
1002    owl_zephyr_zlog_out();
1003  }
1004
1005  /* execute the commands in shutdown */
1006  ret = owl_perlconfig_execute("BarnOwl::Hooks::_shutdown();");
1007  g_free(ret);
1008
1009  /* signal our child process, if any */
1010  if (owl_global_get_newmsgproc_pid(&g)) {
1011    kill(owl_global_get_newmsgproc_pid(&g), SIGHUP);
1012  }
1013 
1014  /* Quit AIM */
1015  if (owl_global_is_aimloggedin(&g)) {
1016    owl_aim_logout();
1017  }
1018
1019  owl_function_debugmsg("Quitting BarnOwl");
1020  owl_select_quit_loop();
1021}
1022
1023void owl_function_calculate_topmsg(int direction)
1024{
1025  int recwinlines, topmsg, curmsg;
1026  const owl_view *v;
1027
1028  v=owl_global_get_current_view(&g);
1029  curmsg=owl_global_get_curmsg(&g);
1030  topmsg=owl_global_get_topmsg(&g);
1031  recwinlines=owl_global_get_recwin_lines(&g);
1032
1033  /*
1034  if (owl_view_get_size(v) < 1) {
1035    return;
1036  }
1037  */
1038
1039  switch (owl_global_get_scrollmode(&g)) {
1040  case OWL_SCROLLMODE_TOP:
1041    topmsg = owl_function_calculate_topmsg_top(direction, v, curmsg, topmsg, recwinlines);
1042    break;
1043  case OWL_SCROLLMODE_NEARTOP:
1044    topmsg = owl_function_calculate_topmsg_neartop(direction, v, curmsg, topmsg, recwinlines);
1045    break;
1046  case OWL_SCROLLMODE_CENTER:
1047    topmsg = owl_function_calculate_topmsg_center(direction, v, curmsg, topmsg, recwinlines);
1048    break;
1049  case OWL_SCROLLMODE_PAGED:
1050    topmsg = owl_function_calculate_topmsg_paged(direction, v, curmsg, topmsg, recwinlines, 0);
1051    break;
1052  case OWL_SCROLLMODE_PAGEDCENTER:
1053    topmsg = owl_function_calculate_topmsg_paged(direction, v, curmsg, topmsg, recwinlines, 1);
1054    break;
1055  case OWL_SCROLLMODE_NORMAL:
1056  default:
1057    topmsg = owl_function_calculate_topmsg_normal(direction, v, curmsg, topmsg, recwinlines);
1058  }
1059  owl_function_debugmsg("Calculated a topmsg of %i", topmsg);
1060  owl_global_set_topmsg(&g, topmsg);
1061}
1062
1063/* Returns what the new topmsg should be. 
1064 * Passed the last direction of movement,
1065 * the current view,
1066 * the current message number in the view,
1067 * the top message currently being displayed,
1068 * and the number of lines in the recwin.
1069 */
1070int owl_function_calculate_topmsg_top(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
1071{
1072  return(curmsg);
1073}
1074
1075int owl_function_calculate_topmsg_neartop(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
1076{
1077  if (curmsg>0 
1078      && (owl_message_get_numlines(owl_view_get_element(v, curmsg-1))
1079          <  recwinlines/2)) {
1080    return(curmsg-1);
1081  } else {
1082    return(curmsg);
1083  }
1084}
1085 
1086int owl_function_calculate_topmsg_center(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
1087{
1088  int i, last, lines;
1089
1090  last = curmsg;
1091  lines = 0;
1092  for (i=curmsg-1; i>=0; i--) {
1093    lines += owl_message_get_numlines(owl_view_get_element(v, i));
1094    if (lines > recwinlines/2) break;
1095    last = i;
1096  }
1097  return(last);
1098}
1099 
1100int owl_function_calculate_topmsg_paged(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines, int center_on_page)
1101{
1102  int i, last, lines, savey;
1103 
1104  /* If we're off the top of the screen, scroll up such that the
1105   * curmsg is near the botton of the screen. */
1106  if (curmsg < topmsg) {
1107    last = curmsg;
1108    lines = 0;
1109    for (i=curmsg; i>=0; i--) {
1110      lines += owl_message_get_numlines(owl_view_get_element(v, i));
1111      if (lines > recwinlines) break;
1112    last = i;
1113    }
1114    if (center_on_page) {
1115      return(owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines));
1116    } else {
1117      return(last);
1118    }
1119  }
1120
1121  /* Find number of lines from top to bottom of curmsg (store in savey) */
1122  savey=0;
1123  for (i=topmsg; i<=curmsg; i++) {
1124    savey+=owl_message_get_numlines(owl_view_get_element(v, i));
1125  }
1126
1127  /* if we're off the bottom of the screen, scroll down */
1128  if (savey > recwinlines) {
1129    if (center_on_page) {
1130      return(owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines));
1131    } else {
1132      return(curmsg);
1133    }
1134  }
1135
1136  /* else just stay as we are... */
1137  return(topmsg);
1138}
1139
1140int owl_function_calculate_topmsg_normal(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
1141{
1142  int savey, i, foo, y;
1143
1144  if (curmsg<0) return(topmsg);
1145   
1146  /* If we're off the top of the screen then center */
1147  if (curmsg<topmsg) {
1148    topmsg=owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines);
1149  }
1150
1151  /* If curmsg is so far past topmsg that there are more messages than
1152     lines, skip the line counting that follows because we're
1153     certainly off screen.  */
1154  savey=curmsg-topmsg;
1155  if (savey <= recwinlines) {
1156    /* Find number of lines from top to bottom of curmsg (store in savey) */
1157    savey = 0;
1158    for (i=topmsg; i<=curmsg; i++) {
1159      savey+=owl_message_get_numlines(owl_view_get_element(v, i));
1160    }
1161  }
1162
1163  /* If we're off the bottom of the screen, set the topmsg to curmsg
1164   * and scroll upwards */
1165  if (savey > recwinlines) {
1166    topmsg=curmsg;
1167    savey=owl_message_get_numlines(owl_view_get_element(v, curmsg));
1168    direction=OWL_DIRECTION_UPWARDS;
1169  }
1170 
1171  /* If our bottom line is less than 1/4 down the screen then scroll up */
1172  if (direction == OWL_DIRECTION_UPWARDS || direction == OWL_DIRECTION_NONE) {
1173    if (savey < (recwinlines / 4)) {
1174      y=0;
1175      for (i=curmsg; i>=0; i--) {
1176        foo=owl_message_get_numlines(owl_view_get_element(v, i));
1177        /* will we run the curmsg off the screen? */
1178        if ((foo+y) >= recwinlines) {
1179          i++;
1180          if (i>curmsg) i=curmsg;
1181          break;
1182        }
1183        /* have saved 1/2 the screen space? */
1184        y+=foo;
1185        if (y > (recwinlines / 2)) break;
1186      }
1187      if (i<0) i=0;
1188      return(i);
1189    }
1190  }
1191
1192  if (direction == OWL_DIRECTION_DOWNWARDS || direction == OWL_DIRECTION_NONE) {
1193    /* If curmsg bottom line is more than 3/4 down the screen then scroll down */
1194    if (savey > ((recwinlines * 3)/4)) {
1195      y=0;
1196      /* count lines from the top until we can save 1/2 the screen size */
1197      for (i=topmsg; i<curmsg; i++) {
1198        y+=owl_message_get_numlines(owl_view_get_element(v, i));
1199        if (y > (recwinlines / 2)) break;
1200      }
1201      if (i==curmsg) {
1202        i--;
1203      }
1204      return(i+1);
1205    }
1206  }
1207
1208  return(topmsg);
1209}
1210
1211void owl_function_resize(void)
1212{
1213  owl_global_set_resize_pending(&g);
1214}
1215
1216void G_GNUC_PRINTF(1, 2) owl_function_debugmsg(const char *fmt, ...)
1217{
1218  char *tmpbuff;
1219  FILE *file;
1220  time_t now;
1221  va_list ap;
1222  va_start(ap, fmt);
1223
1224  if (!owl_global_is_debug_fast(&g))
1225    return;
1226
1227  file = owl_global_get_debug_file_handle(&g);
1228  if (!file) /* XXX should report this */
1229    return;
1230
1231  now = time(NULL);
1232
1233  tmpbuff = owl_util_format_time(localtime(&now));
1234  fprintf(file, "[%d -  %s - %lds]: ",
1235          (int) getpid(), tmpbuff, now - owl_global_get_starttime(&g));
1236  g_free(tmpbuff);
1237  vfprintf(file, fmt, ap);
1238  putc('\n', file);
1239  fflush(file);
1240
1241  va_end(ap);
1242}
1243
1244void owl_function_beep(void)
1245{
1246  if (owl_global_is_bell(&g)) {
1247    beep();
1248  }
1249}
1250
1251int owl_function_subscribe(const char *class, const char *inst, const char *recip)
1252{
1253  int ret;
1254
1255  ret=owl_zephyr_sub(class, inst, recip);
1256  if (ret) {
1257    owl_function_error("Error subscribing.");
1258  } else {
1259    owl_function_makemsg("Subscribed.");
1260  }
1261  return(ret);
1262}
1263
1264void owl_function_unsubscribe(const char *class, const char *inst, const char *recip)
1265{
1266  int ret;
1267
1268  ret=owl_zephyr_unsub(class, inst, recip);
1269  if (ret) {
1270    owl_function_error("Error subscribing.");
1271  } else {
1272    owl_function_makemsg("Unsubscribed.");
1273  }
1274}
1275
1276static void _dirty_everything(gpointer data, gpointer user_data) {
1277  owl_window *w = data;
1278  if (!owl_window_is_realized(w))
1279    return;
1280  owl_window_dirty(w);
1281  owl_window_children_foreach(w, _dirty_everything, NULL);
1282}
1283
1284void owl_function_full_redisplay(void)
1285{
1286  /* Ask every widget to redraw itself. */
1287  _dirty_everything(owl_window_get_screen(), NULL);
1288  /* Force ncurses to redisplay everything. */
1289  clearok(stdscr, TRUE);
1290}
1291
1292void owl_function_popless_text(const char *text)
1293{
1294  owl_popwin *pw;
1295  owl_viewwin *v;
1296
1297  if (owl_global_get_popwin(&g) || owl_global_get_viewwin(&g)) {
1298    owl_function_error("Popwin already in use.");
1299    return;
1300  }
1301  pw = owl_popwin_new();
1302  owl_global_set_popwin(&g, pw);
1303  owl_popwin_up(pw);
1304
1305  v = owl_viewwin_new_text(owl_popwin_get_content(pw), text);
1306  owl_global_set_viewwin(&g, v);
1307
1308  owl_global_push_context(&g, OWL_CTX_POPLESS, v, "popless", NULL);
1309}
1310
1311void owl_function_popless_fmtext(const owl_fmtext *fm)
1312{
1313  owl_popwin *pw;
1314  owl_viewwin *v;
1315
1316  if (owl_global_get_popwin(&g) || owl_global_get_viewwin(&g)) {
1317    owl_function_error("Popwin already in use.");
1318    return;
1319  }
1320  pw = owl_popwin_new();
1321  owl_global_set_popwin(&g, pw);
1322  owl_popwin_up(pw);
1323
1324  v = owl_viewwin_new_fmtext(owl_popwin_get_content(pw), fm);
1325  owl_global_set_viewwin(&g, v);
1326
1327  owl_global_push_context(&g, OWL_CTX_POPLESS, v, "popless", NULL);
1328}
1329
1330void owl_function_popless_file(const char *filename)
1331{
1332  owl_fmtext fm;
1333  FILE *file;
1334  char *s = NULL;
1335
1336  file=fopen(filename, "r");
1337  if (!file) {
1338    owl_function_error("Could not open file: %s", filename);
1339    return;
1340  }
1341
1342  owl_fmtext_init_null(&fm);
1343  while (owl_getline(&s, file))
1344    owl_fmtext_append_normal(&fm, s);
1345  g_free(s);
1346
1347  owl_function_popless_fmtext(&fm);
1348  owl_fmtext_cleanup(&fm);
1349  fclose(file);
1350}
1351
1352void owl_function_about(void)
1353{
1354  owl_function_popless_text(
1355    "This is BarnOwl version " OWL_VERSION_STRING ".\n\n"
1356    "BarnOwl is a fork of the Owl zephyr client, written and\n"
1357    "maintained by Alejandro Sedeno and Nelson Elhage at the\n"
1358    "Massachusetts Institute of Technology. \n"
1359    "\n"
1360    "Owl was written by James Kretchmar. The first version, 0.5, was\n"
1361    "released in March 2002.\n"
1362    "\n"
1363    "The name 'owl' was chosen in reference to the owls in the\n"
1364    "Harry Potter novels, who are tasked with carrying messages\n"
1365    "between Witches and Wizards. The name 'BarnOwl' was chosen\n"
1366    "because we feel our owls should live closer to our ponies.\n"
1367    "\n"
1368    "Copyright (c) 2006-2011 The BarnOwl Developers. All rights reserved.\n"
1369    "Copyright (c) 2004 James Kretchmar. All rights reserved.\n"
1370    "Copyright 2002 Massachusetts Institute of Technology\n"
1371    "\n"
1372    "This program is free software. You can redistribute it and/or\n"
1373    "modify under the terms of the Sleepycat License. Use the \n"
1374    "':show license' command to display the full license\n"
1375  );
1376}
1377
1378void owl_function_info(void)
1379{
1380  const owl_message *m;
1381  owl_fmtext fm, attrfm;
1382  const owl_view *v;
1383  char *time;
1384#ifdef HAVE_LIBZEPHYR
1385  const ZNotice_t *n;
1386#endif
1387
1388  owl_fmtext_init_null(&fm);
1389 
1390  v=owl_global_get_current_view(&g);
1391  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1392  if (!m || owl_view_get_size(v)==0) {
1393    owl_function_error("No message selected\n");
1394    return;
1395  }
1396
1397  owl_fmtext_append_bold(&fm, "General Information:\n");
1398  owl_fmtext_appendf_normal(&fm, "  Msg Id    : %i\n", owl_message_get_id(m));
1399
1400  owl_fmtext_append_normal(&fm, "  Type      : ");
1401  owl_fmtext_append_bold(&fm, owl_message_get_type(m));
1402  owl_fmtext_append_normal(&fm, "\n");
1403
1404  if (owl_message_is_direction_in(m)) {
1405    owl_fmtext_append_normal(&fm, "  Direction : in\n");
1406  } else if (owl_message_is_direction_out(m)) {
1407    owl_fmtext_append_normal(&fm, "  Direction : out\n");
1408  } else if (owl_message_is_direction_none(m)) {
1409    owl_fmtext_append_normal(&fm, "  Direction : none\n");
1410  } else {
1411    owl_fmtext_append_normal(&fm, "  Direction : unknown\n");
1412  }
1413
1414  time = owl_message_format_time(m);
1415  owl_fmtext_appendf_normal(&fm, "  Time      : %s\n", time);
1416  g_free(time);
1417
1418  if (!owl_message_is_type_admin(m)) {
1419    owl_fmtext_appendf_normal(&fm, "  Sender    : %s\n", owl_message_get_sender(m));
1420    owl_fmtext_appendf_normal(&fm, "  Recipient : %s\n", owl_message_get_recipient(m));
1421  }
1422
1423  if (owl_message_is_type_zephyr(m)) {
1424    owl_fmtext_append_bold(&fm, "\nZephyr Specific Information:\n");
1425   
1426    owl_fmtext_appendf_normal(&fm, "  Class     : %s\n", owl_message_get_class(m));
1427    owl_fmtext_appendf_normal(&fm, "  Instance  : %s\n", owl_message_get_instance(m));
1428    owl_fmtext_appendf_normal(&fm, "  Opcode    : %s\n", owl_message_get_opcode(m));
1429#ifdef HAVE_LIBZEPHYR
1430    n = owl_message_get_notice(m);
1431    if (n != NULL) {
1432      char *tmpbuff, *tmpbuff2;
1433      int i, fields;
1434
1435      if (!owl_message_is_pseudo(m)) {
1436        owl_fmtext_append_normal(&fm, "  Kind      : ");
1437        if (n->z_kind==UNSAFE) {
1438          owl_fmtext_append_normal(&fm, "UNSAFE\n");
1439        } else if (n->z_kind==UNACKED) {
1440          owl_fmtext_append_normal(&fm, "UNACKED\n");
1441        } else if (n->z_kind==ACKED) {
1442          owl_fmtext_append_normal(&fm, "ACKED\n");
1443        } else if (n->z_kind==HMACK) {
1444          owl_fmtext_append_normal(&fm, "HMACK\n");
1445        } else if (n->z_kind==HMCTL) {
1446          owl_fmtext_append_normal(&fm, "HMCTL\n");
1447        } else if (n->z_kind==SERVACK) {
1448          owl_fmtext_append_normal(&fm, "SERVACK\n");
1449        } else if (n->z_kind==SERVNAK) {
1450          owl_fmtext_append_normal(&fm, "SERVNACK\n");
1451        } else if (n->z_kind==CLIENTACK) {
1452          owl_fmtext_append_normal(&fm, "CLIENTACK\n");
1453        } else if (n->z_kind==STAT) {
1454          owl_fmtext_append_normal(&fm, "STAT\n");
1455        } else {
1456          owl_fmtext_append_normal(&fm, "ILLEGAL VALUE\n");
1457        }
1458      }
1459      owl_fmtext_appendf_normal(&fm, "  Host      : %s\n", owl_message_get_hostname(m));
1460
1461      if (!owl_message_is_pseudo(m)) {
1462        owl_fmtext_append_normal(&fm, "\n");
1463        owl_fmtext_appendf_normal(&fm, "  Port      : %i\n", ntohs(n->z_port));
1464        owl_fmtext_appendf_normal(&fm, "  Auth      : %s\n", owl_zephyr_get_authstr(n));
1465
1466        /* FIXME make these more descriptive */
1467        owl_fmtext_appendf_normal(&fm, "  Checkd Ath: %i\n", n->z_checked_auth);
1468        owl_fmtext_appendf_normal(&fm, "  Multi notc: %s\n", n->z_multinotice);
1469        owl_fmtext_appendf_normal(&fm, "  Num other : %i\n", n->z_num_other_fields);
1470        owl_fmtext_appendf_normal(&fm, "  Msg Len   : %i\n", n->z_message_len);
1471
1472        fields=owl_zephyr_get_num_fields(n);
1473        owl_fmtext_appendf_normal(&fm, "  Fields    : %i\n", fields);
1474
1475        for (i = 0; i < fields; i++) {
1476          tmpbuff = owl_zephyr_get_field_as_utf8(n, i + 1);
1477          tmpbuff2 = owl_text_indent(tmpbuff, 14, false);
1478          owl_fmtext_appendf_normal(&fm, "  Field %i   : %s\n", i + 1, tmpbuff2);
1479          g_free(tmpbuff2);
1480          g_free(tmpbuff);
1481        }
1482        tmpbuff = owl_text_indent(n->z_default_format, 14, false);
1483        owl_fmtext_appendf_normal(&fm, "  Default Fm: %s\n", tmpbuff);
1484        g_free(tmpbuff);
1485      }
1486
1487    }
1488#endif
1489  }
1490
1491  owl_fmtext_append_bold(&fm, "\nBarnOwl Message Attributes:\n");
1492  owl_message_attributes_tofmtext(m, &attrfm);
1493  owl_fmtext_append_fmtext(&fm, &attrfm);
1494 
1495  owl_function_popless_fmtext(&fm);
1496  owl_fmtext_cleanup(&fm);
1497  owl_fmtext_cleanup(&attrfm);
1498}
1499
1500/* print the current message in a popup window.
1501 * Use the 'default' style regardless of whatever
1502 * style the user may be using
1503 */
1504void owl_function_curmsg_to_popwin(void)
1505{
1506  const owl_view *v;
1507  const owl_message *m;
1508  const owl_style *s;
1509  owl_fmtext fm;
1510
1511  v=owl_global_get_current_view(&g);
1512  s=owl_global_get_style_by_name(&g, "default");
1513
1514  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1515
1516  if (!m || owl_view_get_size(v)==0) {
1517    owl_function_error("No current message");
1518    return;
1519  }
1520
1521  owl_fmtext_init_null(&fm);
1522  owl_style_get_formattext(s, &fm, m);
1523
1524  owl_function_popless_fmtext(&fm);
1525  owl_fmtext_cleanup(&fm);
1526}
1527
1528void owl_function_page_curmsg(int step)
1529{
1530  /* scroll down or up within the current message IF the message is truncated */
1531
1532  int offset, curmsg, lines;
1533  const owl_view *v;
1534  owl_message *m;
1535
1536  offset=owl_global_get_curmsg_vert_offset(&g);
1537  v=owl_global_get_current_view(&g);
1538  curmsg=owl_global_get_curmsg(&g);
1539  m=owl_view_get_element(v, curmsg);
1540  if (!m || owl_view_get_size(v)==0) return;
1541  lines=owl_message_get_numlines(m);
1542
1543  if (offset==0) {
1544    /* Bail if the curmsg isn't the last one displayed */
1545    if (curmsg != owl_mainwin_get_last_msg(owl_global_get_mainwin(&g))) {
1546      owl_function_makemsg("The entire message is already displayed");
1547      return;
1548    }
1549   
1550    /* Bail if we're not truncated */
1551    if (!owl_mainwin_is_curmsg_truncated(owl_global_get_mainwin(&g))) {
1552      owl_function_makemsg("The entire message is already displayed");
1553      return;
1554    }
1555  }
1556 
1557 
1558  /* don't scroll past the last line */
1559  if (step>0) {
1560    if (offset+step > lines-1) {
1561      owl_global_set_curmsg_vert_offset(&g, lines-1);
1562    } else {
1563      owl_global_set_curmsg_vert_offset(&g, offset+step);
1564    }
1565  }
1566
1567  /* would we be before the beginning of the message? */
1568  if (step<0) {
1569    if (offset+step<0) {
1570      owl_global_set_curmsg_vert_offset(&g, 0);
1571    } else {
1572      owl_global_set_curmsg_vert_offset(&g, offset+step);
1573    }
1574  }
1575 
1576  /* redisplay */
1577  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1578}
1579
1580void owl_function_resize_typwin(int newsize)
1581{
1582  owl_global_set_typwin_lines(&g, newsize);
1583  owl_mainpanel_layout_contents(&g.mainpanel);
1584}
1585
1586void owl_function_mainwin_pagedown(void)
1587{
1588  int i;
1589
1590  i=owl_mainwin_get_last_msg(owl_global_get_mainwin(&g));
1591  if (i<0) return;
1592  if (owl_mainwin_is_last_msg_truncated(owl_global_get_mainwin(&g))
1593      && (owl_global_get_curmsg(&g) < i)
1594      && (i>0)) {
1595    i--;
1596  }
1597  owl_global_set_curmsg(&g, i);
1598  owl_function_nextmsg();
1599}
1600
1601void owl_function_mainwin_pageup(void)
1602{
1603  owl_global_set_curmsg(&g, owl_global_get_topmsg(&g));
1604  owl_function_prevmsg();
1605}
1606
1607void owl_function_getsubs(void)
1608{
1609  char *buff;
1610
1611  buff=owl_zephyr_getsubs();
1612
1613  if (buff) {
1614    owl_function_popless_text(buff);
1615  } else {
1616    owl_function_popless_text("Error getting subscriptions");
1617  }
1618           
1619  g_free(buff);
1620}
1621
1622void owl_function_printallvars(void)
1623{
1624  const owl_variable *v;
1625  const char *name;
1626  char *var;
1627  GPtrArray *varnames;
1628  int i;
1629  GString *str   = g_string_new("");
1630
1631  g_string_append_printf(str, "%-20s = %s\n", "VARIABLE", "VALUE");
1632  g_string_append_printf(str, "%-20s   %s\n",  "--------", "-----");
1633  varnames = owl_variable_dict_get_names(owl_global_get_vardict(&g));
1634  for (i = 0; i < varnames->len; i++) {
1635    name = varnames->pdata[i];
1636    if (name && name[0]!='_') {
1637      g_string_append_printf(str, "\n%-20s = ", name);
1638      v = owl_variable_get_var(owl_global_get_vardict(&g), name);
1639      var = owl_variable_get_tostring(v);
1640      if (var) {
1641        g_string_append(str, var);
1642        g_free(var);
1643      } else {
1644        g_string_append(str, "<null>");
1645      }
1646    }
1647  }
1648  g_string_append(str, "\n");
1649  owl_ptr_array_free(varnames, g_free);
1650
1651  owl_function_popless_text(str->str);
1652  g_string_free(str, true);
1653}
1654
1655void owl_function_show_variables(void)
1656{
1657  const owl_variable *v;
1658  GPtrArray *varnames;
1659  owl_fmtext fm; 
1660  int i;
1661  const char *varname;
1662
1663  owl_fmtext_init_null(&fm);
1664  owl_fmtext_append_bold(&fm, 
1665      "Variables: (use 'show variable <name>' for details)\n");
1666  varnames = owl_variable_dict_get_names(owl_global_get_vardict(&g));
1667  for (i = 0; i < varnames->len; i++) {
1668    varname = varnames->pdata[i];
1669    if (varname && varname[0]!='_') {
1670      v = owl_variable_get_var(owl_global_get_vardict(&g), varname);
1671      owl_variable_describe(v, &fm);
1672    }
1673  }
1674  owl_ptr_array_free(varnames, g_free);
1675  owl_function_popless_fmtext(&fm);
1676  owl_fmtext_cleanup(&fm);
1677}
1678
1679void owl_function_show_variable(const char *name)
1680{
1681  const owl_variable *v;
1682  owl_fmtext fm; 
1683
1684  owl_fmtext_init_null(&fm);
1685  v = owl_variable_get_var(owl_global_get_vardict(&g), name);
1686  if (v)
1687    owl_variable_get_help(v, &fm);
1688  else
1689    owl_fmtext_append_normal(&fm, "No such variable...\n");
1690  owl_function_popless_fmtext(&fm);
1691  owl_fmtext_cleanup(&fm);
1692}
1693
1694void owl_function_delete_and_expunge_by_id(int id, bool exclaim_success)
1695{
1696  const owl_messagelist *ml = owl_global_get_msglist(&g);
1697  int msg = owl_messagelist_get_index_by_id(ml, id);
1698  if (msg < 0) {
1699    owl_function_error("No message with id %d: unable to delete", id);
1700  } else {
1701    owl_function_delete_and_expunge_message(msg);
1702    if (exclaim_success)
1703      owl_function_makemsg("Message deleted and expunged");
1704  }
1705}
1706
1707/* note: this applies to global message list, not to view.
1708 * If flag is 1, deletes.  If flag is 0, undeletes. */
1709void owl_function_delete_by_id(int id, int flag)
1710{
1711  const owl_messagelist *ml;
1712  owl_message *m;
1713  ml = owl_global_get_msglist(&g);
1714  m = owl_messagelist_get_by_id(ml, id);
1715  if (m) {
1716    if (flag == 1) {
1717      owl_message_mark_delete(m);
1718    } else if (flag == 0) {
1719      owl_message_unmark_delete(m);
1720    }
1721    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1722  } else {
1723    owl_function_error("No message with id %d: unable to mark for (un)delete",id);
1724  }
1725}
1726
1727void owl_function_delete_automsgs(void)
1728{
1729  /* mark for deletion all messages in the current view that match the
1730   * 'trash' filter */
1731
1732  int i, j, count;
1733  owl_message *m;
1734  const owl_view *v;
1735  const owl_filter *f;
1736
1737  /* get the trash filter */
1738  f=owl_global_get_filter(&g, "trash");
1739  if (!f) {
1740    owl_function_error("No trash filter defined");
1741    return;
1742  }
1743
1744  v=owl_global_get_current_view(&g);
1745
1746  count=0;
1747  j=owl_view_get_size(v);
1748  for (i=0; i<j; i++) {
1749    m=owl_view_get_element(v, i);
1750    if (owl_filter_message_match(f, m)) {
1751      count++;
1752      owl_message_mark_delete(m);
1753    }
1754  }
1755  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1756  owl_function_makemsg("%i messages marked for deletion", count);
1757}
1758
1759void owl_function_status(void)
1760{
1761  char *tmpbuff;
1762  char buff[MAXPATHLEN+1];
1763  time_t start;
1764  int up, days, hours, minutes;
1765  owl_fmtext fm;
1766
1767  owl_fmtext_init_null(&fm);
1768
1769  start=owl_global_get_starttime(&g);
1770
1771  owl_fmtext_append_normal(&fm, "General Information:\n");
1772
1773  owl_fmtext_append_normal(&fm, "  Version: ");
1774  owl_fmtext_append_normal(&fm, OWL_VERSION_STRING);
1775  owl_fmtext_append_normal(&fm, "\n");
1776
1777  owl_fmtext_append_normal(&fm, "  Startup Arguments: ");
1778  owl_fmtext_append_normal(&fm, owl_global_get_startupargs(&g));
1779  owl_fmtext_append_normal(&fm, "\n");
1780
1781  owl_fmtext_append_normal(&fm, "  Current Directory: ");
1782  if(getcwd(buff, MAXPATHLEN) == NULL) {
1783    owl_fmtext_append_normal(&fm, "<Error in getcwd>");
1784  } else {
1785    owl_fmtext_append_normal(&fm, buff);
1786  }
1787  owl_fmtext_append_normal(&fm, "\n");
1788
1789  tmpbuff = owl_util_format_time(localtime(&start));
1790  owl_fmtext_appendf_normal(&fm, "  Startup Time: %s\n", tmpbuff);
1791  g_free(tmpbuff);
1792
1793  up=owl_global_get_runtime(&g);
1794  days=up/86400;
1795  up-=days*86400;
1796  hours=up/3600;
1797  up-=hours*3600;
1798  minutes=up/60;
1799  up-=minutes*60;
1800  owl_fmtext_appendf_normal(&fm, "  Run Time: %i days %2.2i:%2.2i:%2.2i\n", days, hours, minutes, up);
1801
1802  owl_fmtext_append_normal(&fm, "\nProtocol Options:\n");
1803  owl_fmtext_append_normal(&fm, "  Zephyr included    : ");
1804  if (owl_global_is_havezephyr(&g)) {
1805    owl_fmtext_append_normal(&fm, "yes\n");
1806  } else {
1807    owl_fmtext_append_normal(&fm, "no\n");
1808  }
1809  owl_fmtext_append_normal(&fm, "  AIM included       : yes\n");
1810  owl_fmtext_append_normal(&fm, "  Loopback included  : yes\n");
1811
1812
1813  owl_fmtext_append_normal(&fm, "\nBuild Options:\n");
1814  owl_fmtext_append_normal(&fm, "  Stderr redirection : ");
1815#if OWL_STDERR_REDIR
1816  owl_fmtext_append_normal(&fm, "yes\n");
1817#else
1818  owl_fmtext_append_normal(&fm, "no\n");
1819#endif
1820 
1821
1822  owl_fmtext_append_normal(&fm, "\nAIM Status:\n");
1823  owl_fmtext_append_normal(&fm, "  Logged in: ");
1824  if (owl_global_is_aimloggedin(&g)) {
1825    owl_fmtext_append_normal(&fm, owl_global_get_aim_screenname(&g));
1826    owl_fmtext_append_normal(&fm, "\n");
1827  } else {
1828    owl_fmtext_append_normal(&fm, "(not logged in)\n");
1829  }
1830
1831  owl_fmtext_append_normal(&fm, "  Processing events: ");
1832  if (owl_global_is_doaimevents(&g)) {
1833    owl_fmtext_append_normal(&fm, "yes\n");
1834  } else {
1835    owl_fmtext_append_normal(&fm, "no\n");
1836  }
1837
1838  owl_function_popless_fmtext(&fm);
1839  owl_fmtext_cleanup(&fm);
1840}
1841
1842void owl_function_show_term(void)
1843{
1844  owl_fmtext fm;
1845
1846  owl_fmtext_init_null(&fm);
1847  owl_fmtext_appendf_normal(&fm, "Terminal Lines: %i\nTerminal Columns: %i\n",
1848          owl_global_get_lines(&g),
1849          owl_global_get_cols(&g));
1850
1851  if (has_colors()) {
1852    owl_fmtext_append_normal(&fm, "Color: Yes\n");
1853    owl_fmtext_appendf_normal(&fm, "Number of color pairs: %i\n", owl_util_get_colorpairs());
1854    owl_fmtext_appendf_normal(&fm, "Can change colors: %s\n", can_change_color() ? "yes" : "no");
1855  } else {
1856    owl_fmtext_append_normal(&fm, "Color: No\n");
1857  }
1858
1859  owl_function_popless_fmtext(&fm);
1860  owl_fmtext_cleanup(&fm);
1861}
1862
1863/* if type = 0 then normal reply.
1864 * if type = 1 then it's a reply to sender
1865 * if enter = 0 then allow the command to be edited
1866 * if enter = 1 then don't wait for editing
1867 */
1868void owl_function_reply(int type, int enter)
1869{
1870  char *buff=NULL;
1871  const owl_message *m;
1872  const owl_filter *f;
1873 
1874  if (owl_view_get_size(owl_global_get_current_view(&g))==0) {
1875    owl_function_error("No message selected");
1876  } else {
1877    char *cmd;
1878   
1879    m=owl_view_get_element(owl_global_get_current_view(&g), owl_global_get_curmsg(&g));
1880    if (!m) {
1881      owl_function_error("No message selected");
1882      return;
1883    }
1884
1885    /* first check if we catch the reply-lockout filter */
1886    f=owl_global_get_filter(&g, "reply-lockout");
1887    if (f) {
1888      if (owl_filter_message_match(f, m)) {
1889        owl_function_error("Sorry, replies to this message have been disabled by the reply-lockout filter");
1890        return;
1891      }
1892    }
1893
1894    /* then check if it's a question and just bring up the command prompt */
1895    if (owl_message_is_question(m)) {
1896      owl_function_start_command("");
1897      return;
1898    }
1899
1900    if((type == 0 &&
1901        (cmd=owl_perlconfig_message_call_method(m, "replycmd", 0, NULL))) ||
1902       (type == 1 &&
1903        (cmd=owl_perlconfig_message_call_method(m, "replysendercmd", 0, NULL)))) {
1904      buff = cmd;
1905    }
1906
1907    if(!buff) {
1908        owl_function_error("I don't know how to reply to that message.");
1909        return;
1910    }
1911
1912    if (enter) {
1913      owl_history *hist = owl_global_get_cmd_history(&g);
1914      owl_history_store(hist, buff, false);
1915      owl_function_command_norv(buff);
1916    } else {
1917      owl_function_start_command(buff);
1918    }
1919    g_free(buff);
1920  }
1921}
1922
1923void owl_function_zlocate(int argc, const char *const *argv, int auth)
1924{
1925  owl_fmtext fm;
1926  char *ptr;
1927  char *result;
1928  int i;
1929
1930  owl_fmtext_init_null(&fm);
1931
1932  for (i=0; i<argc; i++) {
1933    ptr = long_zuser(argv[i]);
1934    result = owl_zephyr_zlocate(ptr, auth);
1935    owl_fmtext_append_normal(&fm, result);
1936    g_free(result);
1937    g_free(ptr);
1938  }
1939
1940  owl_function_popless_fmtext(&fm);
1941  owl_fmtext_cleanup(&fm);
1942}
1943
1944void owl_callback_command(owl_editwin *e, bool success)
1945{
1946  if (!success) return;
1947  char *rv;
1948  const char *line = owl_editwin_get_text(e);
1949
1950  rv = owl_function_command(line);
1951   if (rv) {
1952    owl_function_makemsg("%s", rv);
1953    g_free(rv);
1954  }
1955}
1956
1957owl_editwin *owl_function_start_command(const char *line)
1958{
1959  owl_editwin *tw;
1960  owl_context *ctx;
1961
1962  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
1963
1964  owl_editwin_set_locktext(tw, "command: ");
1965
1966  owl_editwin_insert_string(tw, line);
1967
1968  ctx = owl_editcontext_new(OWL_CTX_EDITLINE, tw, "editline",
1969                            owl_global_deactivate_editcontext, &g);
1970  owl_global_push_context_obj(&g, ctx);
1971  owl_editwin_set_callback(tw, owl_callback_command);
1972  return tw;
1973}
1974
1975owl_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
1990owl_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_format_minutes(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 = owl_util_format_time(localtime(&now));
3417
3418  buff = g_strdup_printf("%s %s", date, string);
3419
3420  owl_errqueue_append_err(owl_global_get_errqueue(&g), buff);
3421
3422  g_free(buff);
3423  g_free(date);
3424}
3425
3426void owl_function_showerrs(void)
3427{
3428  owl_fmtext fm;
3429
3430  owl_fmtext_init_null(&fm);
3431  owl_fmtext_append_normal(&fm, "Errors:\n\n");
3432  owl_errqueue_to_fmtext(owl_global_get_errqueue(&g), &fm);
3433  owl_function_popless_fmtext(&fm);
3434}
3435
3436void G_GNUC_PRINTF(1, 2) owl_function_makemsg(const char *fmt, ...)
3437{
3438  va_list ap;
3439  char *str;
3440
3441  va_start(ap, fmt);
3442  str = g_strdup_vprintf(fmt, ap);
3443  va_end(ap);
3444
3445  owl_function_debugmsg("makemsg: %s", str);
3446  owl_msgwin_set_text_nocopy(&g.msgwin, str);
3447}
3448
3449/* get locations for everyone in .anyone.  If 'notify' is '1' then
3450 * send a pseudo login or logout message for everyone not in sync with
3451 * the global zephyr buddy list.  The list is updated regardless of
3452 * the status of 'notify'.
3453 */
3454void owl_function_zephyr_buddy_check(int notify)
3455{
3456#ifdef HAVE_LIBZEPHYR
3457  int i;
3458  GPtrArray *anyone;
3459  GList **zaldlist;
3460  GList *zaldptr;
3461  ZAsyncLocateData_t *zald;
3462  const char *user;
3463
3464  if (!owl_global_is_havezephyr(&g)) return;
3465  owl_global_set_pseudologin_notify(&g, notify);
3466  zaldlist = owl_global_get_zaldlist(&g);
3467
3468  /* Clear the existing ZALDs first. */
3469  zaldptr = g_list_first(*zaldlist);
3470  while (zaldptr) {
3471    ZFreeALD(zaldptr->data);
3472    g_free(zaldptr->data);
3473    zaldptr = g_list_next(zaldptr);
3474  }
3475  g_list_free(*zaldlist);
3476  *zaldlist = NULL;
3477
3478  anyone = owl_zephyr_get_anyone_list(NULL);
3479  if (anyone != NULL) {
3480    for (i = 0; i < anyone->len; i++) {
3481      user = anyone->pdata[i];
3482      zald = g_new(ZAsyncLocateData_t, 1);
3483      if (ZRequestLocations(zstr(user), zald, UNACKED, ZAUTH) == ZERR_NONE) {
3484        *zaldlist = g_list_append(*zaldlist, zald);
3485      } else {
3486        g_free(zald);
3487      }
3488    }
3489    owl_ptr_array_free(anyone, g_free);
3490  }
3491#endif
3492}
3493
3494void owl_function_aimsearch_results(const char *email, GPtrArray *namelist)
3495{
3496  owl_fmtext fm;
3497  int i;
3498
3499  owl_fmtext_init_null(&fm);
3500  owl_fmtext_append_normal(&fm, "AIM screennames associated with ");
3501  owl_fmtext_append_normal(&fm, email);
3502  owl_fmtext_append_normal(&fm, ":\n");
3503
3504  for (i = 0; i < namelist->len; i++) {
3505    owl_fmtext_append_normal(&fm, "  ");
3506    owl_fmtext_append_normal(&fm, namelist->pdata[i]);
3507    owl_fmtext_append_normal(&fm, "\n");
3508  }
3509
3510  owl_function_popless_fmtext(&fm);
3511  owl_fmtext_cleanup(&fm);
3512}
3513
3514int owl_function_get_color_count(void)
3515{
3516     return COLORS;
3517}
3518
3519void _owl_function_mark_message(const owl_message *m)
3520{
3521  if (m) {
3522    owl_global_set_markedmsgid(&g, owl_message_get_id(m));
3523    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3524  }
3525}
3526
3527void owl_function_mark_message(void)
3528{
3529  const owl_message *m;
3530  const owl_view *v;
3531
3532  v=owl_global_get_current_view(&g);
3533
3534  /* bail if there's no current message */
3535  if (owl_view_get_size(v) < 1) {
3536    owl_function_error("No messages to mark");
3537    return;
3538  }
3539
3540  /* mark the message */
3541  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
3542  _owl_function_mark_message(m);
3543  owl_function_makemsg("Mark set");
3544}
3545
3546void owl_function_swap_cur_marked(void)
3547{
3548  int marked_id;
3549  const owl_message *m;
3550  const owl_view *v;
3551
3552  marked_id=owl_global_get_markedmsgid(&g);
3553  if (marked_id == -1) {
3554    owl_function_error("Mark not set.");
3555    return;
3556  }
3557
3558  v=owl_global_get_current_view(&g);
3559  /* bail if there's no current message */
3560  if (owl_view_get_size(v) < 1) {
3561    return;
3562  }
3563
3564  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
3565  _owl_function_mark_message(m);
3566  owl_global_set_curmsg(&g, owl_view_get_nearest_to_msgid(v, marked_id));
3567  owl_function_calculate_topmsg(OWL_DIRECTION_NONE);
3568  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3569  owl_global_set_direction_downwards(&g);
3570}
Note: See TracBrowser for help on using the repository browser.