source: functions.c @ 96ee74c

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