source: functions.c @ f271129

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