source: functions.c @ 8de7c84

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