source: functions.c @ 6df57d4

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