source: functions.c @ 4fd3c04

Last change on this file since 4fd3c04 was 4fd3c04, checked in by Anders Kaseorg <andersk@mit.edu>, 7 years ago
Remove AIM support This code has received almost no security attention, and anyway, AIM is shutting down on December 15, 2017. https://aimemories.tumblr.com/post/166091776077/aimemories Signed-off-by: Anders Kaseorg <andersk@mit.edu>
  • Property mode set to 100644
File size: 90.4 KB
RevLine 
[f271129]1#include "owl.h"
2#include "filterproc.h"
[7d4fbcd]3#include <stdio.h>
[2adaf1d]4#include <sys/stat.h>
[8f44c6b]5#include <sys/wait.h>
[7d4fbcd]6
[6829afc]7CALLER_OWN char *owl_function_command(const char *cmdbuff)
[d54838d]8{
[7d4fbcd]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
[6829afc]14CALLER_OWN char *owl_function_command_argv(const char *const *argv, int argc)
[c4ba74d]15{
16  return owl_cmddict_execute_argv(owl_global_get_cmddict(&g),
17                                  owl_global_get_context(&g),
18                                  argv, argc);
19}
20
[e19eb97]21void owl_function_command_norv(const char *cmdbuff)
[d54838d]22{
[7d4fbcd]23  char *rv;
[4b464a4]24  rv=owl_function_command(cmdbuff);
[3b8a563]25  g_free(rv);
[7d4fbcd]26}
27
[e19eb97]28void owl_function_command_alias(const char *alias_from, const char *alias_to)
[d54838d]29{
[7d4fbcd]30  owl_cmddict_add_alias(owl_global_get_cmddict(&g), alias_from, alias_to);
31}
32
[0a0fb74]33const owl_cmd *owl_function_get_cmd(const char *name)
[d54838d]34{
[7d4fbcd]35  return owl_cmddict_find(owl_global_get_cmddict(&g), name);
36}
37
[c79a047]38void owl_function_show_commands(void)
[d54838d]39{
[ce68f23]40  GPtrArray *l;
[7d4fbcd]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");
[ce68f23]46  l = owl_cmddict_get_names(owl_global_get_cmddict(&g));
47  owl_fmtext_append_list(&fm, l, "\n", owl_function_cmd_describe);
[7d4fbcd]48  owl_fmtext_append_normal(&fm, "\n");
49  owl_function_popless_fmtext(&fm);
[ce68f23]50  owl_ptr_array_free(l, g_free);
[7ab0020]51  owl_fmtext_cleanup(&fm);
[7d4fbcd]52}
53
[e19eb97]54void owl_function_show_view(const char *viewname)
[ef56a67]55{
[9e5c9f3]56  const owl_view *v;
[ef56a67]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))) {
[ec6ff52]62    owl_function_error("No view named '%s'", viewname);
[ef56a67]63    return;
64  }
65
66  owl_fmtext_init_null(&fm);
67  owl_view_to_fmtext(v, &fm);
68  owl_function_popless_fmtext(&fm);
[7ab0020]69  owl_fmtext_cleanup(&fm);
[ef56a67]70}
71
[c79a047]72void owl_function_show_styles(void) {
[ce68f23]73  GPtrArray *l;
[f1e629d]74  owl_fmtext fm;
75
76  owl_fmtext_init_null(&fm);
77  owl_fmtext_append_bold(&fm, "Styles:\n");
[ce68f23]78  l = owl_global_get_style_names(&g);
79  owl_fmtext_append_list(&fm, l, "\n", owl_function_style_describe);
[f1e629d]80  owl_fmtext_append_normal(&fm, "\n");
81  owl_function_popless_fmtext(&fm);
[ce68f23]82  owl_ptr_array_free(l, g_free);
[7ab0020]83  owl_fmtext_cleanup(&fm);
[f1e629d]84}
85
[6829afc]86CALLER_OWN char *owl_function_style_describe(const char *name)
[d427f08]87{
[e19eb97]88  const char *desc;
[65b2173]89  char *s;
[1fdab04]90  const owl_style *style;
[f1e629d]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  }
[3472845]97  s = g_strdup_printf("%-20s - %s%s", name,
98                      0 == owl_style_validate(style) ? "" : "[INVALID] ",
99                      desc);
[f1e629d]100  return s;
101}
[ef56a67]102
[6829afc]103CALLER_OWN char *owl_function_cmd_describe(const char *name)
[d54838d]104{
[0a0fb74]105  const owl_cmd *cmd = owl_cmddict_find(owl_global_get_cmddict(&g), name);
[7d4fbcd]106  if (cmd) return owl_cmd_describe(cmd);
107  else return(NULL);
108}
109
[e19eb97]110void owl_function_show_command(const char *name)
[d54838d]111{
[7d4fbcd]112  owl_function_help_for_command(name);
113}
114
[c79a047]115void owl_function_show_license(void)
[debb15d]116{
[ca1fb26a]117  char *text = g_strdup_printf(
118    "BarnOwl version %s\n"
[b03c714]119    "Copyright (c) 2006-2011 The BarnOwl Developers. All rights reserved.\n"
[debb15d]120    "Copyright (c) 2004 James Kretchmar. All rights reserved.\n"
121    "\n"
122    "Redistribution and use in source and binary forms, with or without\n"
123    "modification, are permitted provided that the following conditions are\n"
124    "met:\n"
125    "\n"
126    "   * Redistributions of source code must retain the above copyright\n"
127    "     notice, this list of conditions and the following disclaimer.\n"
128    "\n"
129    "   * Redistributions in binary form must reproduce the above copyright\n"
130    "     notice, this list of conditions and the following disclaimer in\n"
131    "     the documentation and/or other materials provided with the\n"
132    "     distribution.\n"
133    "\n"
134    "   * Redistributions in any form must be accompanied by information on\n"
[a16d7e5]135    "     how to obtain complete source code for the BarnOwl software and any\n"
136    "     accompanying software that uses the BarnOwl software. The source code\n"
[debb15d]137    "     must either be included in the distribution or be available for no\n"
138    "     more than the cost of distribution plus a nominal fee, and must be\n"
139    "     freely redistributable under reasonable conditions. For an\n"
140    "     executable file, complete source code means the source code for\n"
141    "     all modules it contains. It does not include source code for\n"
142    "     modules or files that typically accompany the major components of\n"
143    "     the operating system on which the executable file runs.\n"
144    "\n"
145    "THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
146    "IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n"
147    "WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR\n"
148    "NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE\n"
149    "LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n"
150    "CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n"
151    "SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR\n"
152    "BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n"
153    "WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE\n"
154    "OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN\n"
[ca1fb26a]155    "IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n",
156    version);
[debb15d]157  owl_function_popless_text(text);
[ca1fb26a]158  g_free(text);
[debb15d]159}
160
[c79a047]161void owl_function_show_quickstart(void)
[799b60e]162{
[e19eb97]163    const char *message =
[799b60e]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    ;
173
174    if (owl_perlconfig_is_function("BarnOwl::Hooks::_get_quickstart")) {
175        char *perlquickstart = owl_perlconfig_execute("BarnOwl::Hooks::_get_quickstart()");
176        if (perlquickstart) {
[3472845]177            char *result = g_strdup_printf("%s%s", message, perlquickstart);
[799b60e]178            owl_function_adminmsg("BarnOwl Quickstart", result);
[ddbbcffa]179            g_free(result);
180            g_free(perlquickstart);
[799b60e]181            return;
182        }
183    }
184    owl_function_adminmsg("BarnOwl Quickstart", message);
185}
186
187
[15b34fd]188/* Create an admin message, append it to the global list of messages
189 * and redisplay if necessary.
190 */
[e19eb97]191void owl_function_adminmsg(const char *header, const char *body)
[d54838d]192{
[7d4fbcd]193  owl_message *m;
[4b464a4]194
[7dcef03]195  m=g_slice_new(owl_message);
[15b34fd]196  owl_message_create_admin(m, header, body);
197 
[8fec514]198  /* add it to the global list and current view */
[7d4fbcd]199  owl_messagelist_append_element(owl_global_get_msglist(&g), m);
200  owl_view_consider_message(owl_global_get_current_view(&g), m);
201
[15b34fd]202  /* do followlast if necessary */
[3b17b57]203  if (owl_global_should_followlast(&g)) owl_function_lastmsg();
[7d4fbcd]204
[15b34fd]205  /* redisplay etc. */
[7d4fbcd]206  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
207}
208
[e5da3fe]209/* Queues outgoing zephyrs; if z sends to n people, queue n messages
210 * (except in case of cc). If there are no recipients queues 1
211 * message.
[15b34fd]212 */
[e5da3fe]213void owl_function_add_outgoing_zephyrs(const owl_zwrite *z)
[d09e5a1]214{
[bd482c3]215  if (z->cc && owl_zwrite_is_personal(z)) {
[e5da3fe]216    /* create the message */
[7dcef03]217    owl_message *m = g_slice_new(owl_message);
[e5da3fe]218    owl_message_create_from_zwrite(m, z, owl_zwrite_get_message(z), 0);
[d09e5a1]219
[e5da3fe]220    owl_global_messagequeue_addmsg(&g, m);
221  } else {
222    int i;
223    for (i = 0; i < owl_zwrite_get_numrecips(z); i++) {
[bd482c3]224      owl_message *m;
225
226      if (!owl_zwrite_recip_is_personal(owl_zwrite_get_recip_n(z, i)))
227        continue;
228
[e5da3fe]229      /* create the message */
[7dcef03]230      m = g_slice_new(owl_message);
[e5da3fe]231      owl_message_create_from_zwrite(m, z, owl_zwrite_get_message(z), i);
[15b34fd]232
[e5da3fe]233      owl_global_messagequeue_addmsg(&g, m);
234    }
235  }
[15b34fd]236}
237
238/* Create an outgoing loopback message and return a pointer to it.
239 * Does not append it to the global queue, use
[13fe062]240 * owl_global_messagequeue_addmsg() for that.
[15b34fd]241 */
[6829afc]242CALLER_OWN owl_message *owl_function_make_outgoing_loopback(const char *body)
[37eab7f]243{
244  owl_message *m;
245
246  /* create the message */
[7dcef03]247  m=g_slice_new(owl_message);
[37eab7f]248  owl_message_create_loopback(m, body);
249  owl_message_set_direction_out(m);
250
[15b34fd]251  return(m);
[37eab7f]252}
253
[c737503]254owl_editwin *owl_function_start_edit_win(const char *line)
[d54838d]255{
[7d4fbcd]256  owl_editwin *e;
[c394de8]257  owl_context *ctx;
[bdbec0a]258  char *s;
259
260  /* create and setup the editwin */
[58d47ca]261  e = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_MULTILINE,
262                                   owl_global_get_msg_history(&g));
[bdbec0a]263  owl_editwin_set_dotsend(e);
[3472845]264  s = g_strdup_printf("----> %s\n", line);
[bdbec0a]265  owl_editwin_set_locktext(e, s);
[ddbbcffa]266  g_free(s);
[bdbec0a]267
[4a41f16]268  ctx = owl_editcontext_new(OWL_CTX_EDITMULTI, e, "editmulti",
269                            owl_global_deactivate_editcontext, &g);
[c394de8]270  owl_global_push_context_obj(&g, ctx);
[c737503]271  return e;
[bdbec0a]272}
273
[987cf3f]274static void owl_function_write_setup(const char *noun)
[bdbec0a]275{
276
277  if (!owl_global_get_lockout_ctrld(&g))
278    owl_function_makemsg("Type your %s below.  "
279                         "End with ^D or a dot on a line by itself."
280                         "  ^C will quit.", noun);
281  else
282    owl_function_makemsg("Type your %s below.  "
283                         "End with a dot on a line by itself.  ^C will quit.",
284                         noun);
285}
286
[987cf3f]287void owl_function_zwrite_setup(owl_zwrite *z)
[bdbec0a]288{
[c737503]289  owl_editwin *e;
[7d4fbcd]290  /* send a ping if necessary */
291  if (owl_global_is_txping(&g)) {
[987cf3f]292    owl_zwrite_send_ping(z);
[7d4fbcd]293  }
294
[987cf3f]295
296  owl_function_write_setup("zephyr");
[c737503]297  e = owl_function_start_edit_win(z->zwriteline);
298  owl_editwin_set_cbdata(e, z, (void (*)(void *))owl_zwrite_delete);
299  owl_editwin_set_callback(e, &owl_callback_zwrite);
[7d4fbcd]300}
301
[c79a047]302void owl_function_loopwrite_setup(void)
[37eab7f]303{
[c737503]304  owl_editwin *e;
[987cf3f]305  owl_function_write_setup("message");
[c737503]306  e = owl_function_start_edit_win("loopwrite");
307  owl_editwin_set_callback(e, &owl_callback_loopwrite);
[1b6b2f3]308}
309
[7803326]310void owl_callback_zwrite(owl_editwin *e, bool success)
311{
312  if (!success) return;
[987cf3f]313  owl_zwrite *z = owl_editwin_get_cbdata(e);
314  owl_function_zwrite(z, owl_editwin_get_text(e));
[37eab7f]315}
316
[e5da3fe]317/* send, log and display outgoing zephyrs.  If 'msg' is NULL the
318 * message is expected to be set from the zwrite line itself
[9ceee9d]319 */
[c23f678]320#ifdef HAVE_LIBZEPHYR
[987cf3f]321void owl_function_zwrite(owl_zwrite *z, const char *msg)
[d54838d]322{
[0743696]323  int ret;
[d309eb3]324
[987cf3f]325  if(strcmp(z->cmd, "zcrypt") == 0) {
326    owl_function_zcrypt(z, msg);
[a5fc448]327    return;
328  }
329
[9ceee9d]330  /* create the zwrite and send the message */
[987cf3f]331  owl_zwrite_populate_zsig(z);
[9ceee9d]332  if (msg) {
[987cf3f]333    owl_zwrite_set_message(z, msg);
[d309eb3]334  }
[0743696]335  ret = owl_zwrite_send_message(z);
336  if (ret != 0) {
337    owl_function_makemsg("Error sending zephyr: %s", error_message(ret));
338    return;
339  }
[9ceee9d]340  owl_function_makemsg("Waiting for ack...");
[d309eb3]341
[bd482c3]342  /* create the outgoing message */
343  owl_function_add_outgoing_zephyrs(z);
[d309eb3]344}
[c23f678]345#else
346void owl_function_zwrite(owl_zwrite *z, const char *msg) {
347}
348#endif
[d309eb3]349
[e5da3fe]350/* send, log and display outgoing zcrypt zephyrs.  If 'msg' is NULL
[ce7db4d]351 * the message is expected to be set from the zwrite line itself
352 */
[987cf3f]353void owl_function_zcrypt(owl_zwrite *z, const char *msg)
[d54838d]354{
[9ceee9d]355  char *cryptmsg;
[d564c3d]356  const char *argv[7];
[9a7b4f2]357  char *zcrypt;
[d564c3d]358  int rv, status;
[96582d5]359  char *old_msg;
[7d4fbcd]360
361  /* create the zwrite and send the message */
[987cf3f]362  owl_zwrite_populate_zsig(z);
[ce7db4d]363  if (msg) {
[987cf3f]364    owl_zwrite_set_message(z, msg);
[ce7db4d]365  }
[d4927a7]366  old_msg = g_strdup(owl_zwrite_get_message(z));
[d564c3d]367
[dde1b4d]368  zcrypt = g_build_filename(owl_get_bindir(), "zcrypt", NULL);
[97cdbaf5]369  argv[0] = zcrypt;
[d564c3d]370  argv[1] = "-E";
[987cf3f]371  argv[2] = "-c"; argv[3] = owl_zwrite_get_class(z);
372  argv[4] = "-i"; argv[5] = owl_zwrite_get_instance(z);
[d564c3d]373  argv[6] = NULL;
374
[97cdbaf5]375  rv = call_filter(argv, owl_zwrite_get_message(z), &cryptmsg, &status);
[9a7b4f2]376
[ddbbcffa]377  g_free(zcrypt);
[d564c3d]378
379  if (rv || status) {
[3b8a563]380    g_free(cryptmsg);
[ddbbcffa]381    g_free(old_msg);
[ec6ff52]382    owl_function_error("Error in zcrypt, possibly no key found.  Message not sent.");
[9ceee9d]383    owl_function_beep();
384    return;
385  }
386
[7bfc613]387  owl_zwrite_set_message_raw(z, cryptmsg);
[987cf3f]388  owl_zwrite_set_opcode(z, "crypt");
[7bfc613]389
[987cf3f]390  owl_zwrite_send_message(z);
[7d4fbcd]391  owl_function_makemsg("Waiting for ack...");
392
[bd482c3]393  /* Create the outgoing message. Restore the un-crypted message for display. */
394  owl_zwrite_set_message_raw(z, old_msg);
395  owl_function_add_outgoing_zephyrs(z);
[7d4fbcd]396
[ecfbdcc]397  /* Clean up. */
[ddbbcffa]398  g_free(cryptmsg);
[ecfbdcc]399  g_free(old_msg);
[7d4fbcd]400}
401
[7803326]402void owl_callback_loopwrite(owl_editwin *e, bool success)
403{
404  if (!success) return;
[1b6b2f3]405  owl_function_loopwrite(owl_editwin_get_text(e));
406}
407
[e19eb97]408void owl_function_loopwrite(const char *msg)
[37eab7f]409{
[15b34fd]410  owl_message *min, *mout;
[37eab7f]411
412  /* create a message and put it on the message queue.  This simulates
413   * an incoming message */
[7dcef03]414  min=g_slice_new(owl_message);
[4211f50b]415  mout=owl_function_make_outgoing_loopback(msg);
[13a3c1db]416
[37eab7f]417  if (owl_global_is_displayoutgoing(&g)) {
[13a3c1db]418    owl_global_messagequeue_addmsg(&g, mout);
[15b34fd]419  } else {
[91634ec]420    owl_message_delete(mout);
[37eab7f]421  }
422
[13a3c1db]423  owl_message_create_loopback(min, msg);
424  owl_message_set_direction_in(min);
425  owl_global_messagequeue_addmsg(&g, min);
426
[37eab7f]427  /* fake a makemsg */
428  owl_function_makemsg("loopback message sent");
429}
430
[b950088]431/* If filter is non-null, looks for the next message matching
432 * that filter.  If skip_deleted, skips any deleted messages.
433 * If last_if_none, will stop at the last message in the view
434 * if no matching messages are found.  */
[e19eb97]435void owl_function_nextmsg_full(const char *filter, int skip_deleted, int last_if_none)
[d54838d]436{
[b950088]437  int curmsg, i, viewsize, found;
[9e5c9f3]438  const owl_view *v;
[4542047]439  const owl_filter *f = NULL;
[c08c70a]440  const owl_message *m;
[7d4fbcd]441
442  v=owl_global_get_current_view(&g);
[b950088]443
444  if (filter) {
445    f=owl_global_get_filter(&g, filter);
446    if (!f) {
[ec6ff52]447      owl_function_error("No %s filter defined", filter);
[b950088]448      return;
449    }
[7d4fbcd]450  }
451
[b950088]452  curmsg=owl_global_get_curmsg(&g);
453  viewsize=owl_view_get_size(v);
454  found=0;
[7d4fbcd]455
[b950088]456  /* just check to make sure we're in bounds... */
457  if (curmsg>viewsize-1) curmsg=viewsize-1;
458  if (curmsg<0) curmsg=0;
[7d4fbcd]459
[b950088]460  for (i=curmsg+1; i<viewsize; i++) {
461    m=owl_view_get_element(v, i);
462    if (skip_deleted && owl_message_is_delete(m)) continue;
463    if (f && !owl_filter_message_match(f, m)) continue;
464    found = 1;
465    break;
466  }
467
468  if (i>owl_view_get_size(v)-1) i=owl_view_get_size(v)-1;
[5763474]469  if (i<0) i=0;
[b950088]470
471  if (!found) {
[799b60e]472    owl_function_makemsg("already at last%s message%s%s%s",
[b950088]473                         skip_deleted?" non-deleted":"",
[799b60e]474                         filter?" in ":"", filter?filter:"",
475                         owl_mainwin_is_curmsg_truncated(owl_global_get_mainwin(&g)) ?
476                         ", press Enter to scroll" : "");
[5a6e6b9]477    /* if (!skip_deleted) owl_function_beep(); */
[7d4fbcd]478  }
479
[b950088]480  if (last_if_none || found) {
481    owl_global_set_curmsg(&g, i);
482    owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
483    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
484    owl_global_set_direction_downwards(&g);
485  }
486}
[7d4fbcd]487
[e19eb97]488void owl_function_prevmsg_full(const char *filter, int skip_deleted, int first_if_none)
[d54838d]489{
[402eb16f]490  int curmsg, i, found;
[9e5c9f3]491  const owl_view *v;
[4542047]492  const owl_filter *f = NULL;
[c08c70a]493  const owl_message *m;
[7d4fbcd]494
495  v=owl_global_get_current_view(&g);
496
[b950088]497  if (filter) {
498    f=owl_global_get_filter(&g, filter);
499    if (!f) {
[ec6ff52]500      owl_function_error("No %s filter defined", filter);
[b950088]501      return;
[7d4fbcd]502    }
[b950088]503  }
[7d4fbcd]504
[b950088]505  curmsg=owl_global_get_curmsg(&g);
506  found=0;
507
508  /* just check to make sure we're in bounds... */
509  if (curmsg<0) curmsg=0;
510
511  for (i=curmsg-1; i>=0; i--) {
512    m=owl_view_get_element(v, i);
513    if (skip_deleted && owl_message_is_delete(m)) continue;
514    if (f && !owl_filter_message_match(f, m)) continue;
515    found = 1;
516    break;
517  }
518
519  if (i<0) i=0;
520
521  if (!found) {
[f51bc78]522    owl_function_makemsg("already at first%s message%s%s",
[b950088]523                         skip_deleted?" non-deleted":"",
524                         filter?" in ":"", filter?filter:"");
[5a6e6b9]525    /* if (!skip_deleted) owl_function_beep(); */
[b950088]526  }
527
528  if (first_if_none || found) {
529    owl_global_set_curmsg(&g, i);
530    owl_function_calculate_topmsg(OWL_DIRECTION_UPWARDS);
531    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
532    owl_global_set_direction_upwards(&g);
[7d4fbcd]533  }
534}
535
[c79a047]536void owl_function_nextmsg(void)
[d54838d]537{
[b950088]538  owl_function_nextmsg_full(NULL, 0, 1);
539}
[7d4fbcd]540
[c79a047]541void owl_function_prevmsg(void)
[d54838d]542{
[b950088]543  owl_function_prevmsg_full(NULL, 0, 1);
544}
[7d4fbcd]545
[c79a047]546void owl_function_nextmsg_notdeleted(void)
[d54838d]547{
[b950088]548  owl_function_nextmsg_full(NULL, 1, 1);
549}
[7d4fbcd]550
[c79a047]551void owl_function_prevmsg_notdeleted(void)
[d54838d]552{
[b950088]553  owl_function_prevmsg_full(NULL, 1, 1);
[7d4fbcd]554}
555
[efeec7f]556void owl_function_delete_and_expunge_message(int n)
557{
558  owl_messagelist *ml = owl_global_get_msglist(&g);
559  owl_view *v = owl_global_get_current_view(&g);
560  int lastmsgid = owl_function_get_curmsg_id(v);
561
562  /* delete and expunge the message */
563  owl_messagelist_delete_and_expunge_element(ml, n);
564
565  owl_function_redisplay_to_nearest(lastmsgid, v);
566}
567
568void owl_function_delete_and_expunge_cur(bool exclaim_success)
569{
570  int curmsg;
571  const owl_view *v = owl_global_get_current_view(&g);
572
573  /* bail if there's no current message */
574  if (owl_view_get_size(v) < 1) {
575    owl_function_error("No current message to delete");
576    return;
577  }
578
579  /* delete the current message */
580  curmsg = owl_global_get_curmsg(&g);
581  owl_function_delete_and_expunge_message(curmsg);
582  if (exclaim_success)
583    owl_function_makemsg("Message deleted and expunged");
584}
585
[b950088]586/* if move_after is 1, moves after the delete */
[d54838d]587void owl_function_deletecur(int move_after)
588{
[7d4fbcd]589  int curmsg;
590  owl_view *v;
591
592  v=owl_global_get_current_view(&g);
593
594  /* bail if there's no current message */
595  if (owl_view_get_size(v) < 1) {
[ec6ff52]596    owl_function_error("No current message to delete");
[7d4fbcd]597    return;
598  }
599
600  /* mark the message for deletion */
601  curmsg=owl_global_get_curmsg(&g);
602  owl_view_delete_element(v, curmsg);
603
[b950088]604  if (move_after) {
605    /* move the poiner in the appropriate direction
606     * to the next undeleted msg */
607    if (owl_global_get_direction(&g)==OWL_DIRECTION_UPWARDS) {
608      owl_function_prevmsg_notdeleted();
609    } else {
610      owl_function_nextmsg_notdeleted();
611    }
[7d4fbcd]612  }
613}
614
[d54838d]615void owl_function_undeletecur(int move_after)
616{
[7d4fbcd]617  int curmsg;
618  owl_view *v;
619
620  v=owl_global_get_current_view(&g);
621 
622  if (owl_view_get_size(v) < 1) {
[ec6ff52]623    owl_function_error("No current message to undelete");
[7d4fbcd]624    return;
625  }
626  curmsg=owl_global_get_curmsg(&g);
627
628  owl_view_undelete_element(v, curmsg);
629
[b950088]630  if (move_after) {
631    if (owl_global_get_direction(&g)==OWL_DIRECTION_UPWARDS) {
632      if (curmsg>0) {
633        owl_function_prevmsg();
634      } else {
635        owl_function_nextmsg();
636      }
[7d4fbcd]637    } else {
[b950088]638      owl_function_nextmsg();
[7d4fbcd]639    }
640  }
641
642  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
643}
644
[3eeb6ed]645/* returns the current message id, if it exists.  Otherwise returns
646 * -1 if we are past the end of the message list, and 0 otherwise. */
647int owl_function_get_curmsg_id(const owl_view *v)
648{
649  int curmsg = owl_global_get_curmsg(&g);
650  const owl_message *m = owl_view_get_element(v, curmsg);
651  if (m)
652    return owl_message_get_id(m);
653  if (curmsg > 0) /* past the end of the message list (probably) */
654    return -1;
655  return 0;
656}
657
658/* redisplays the view to the nearest message to the id given.
659 * if msgid < 0, redisplay to past the end of the message list */
660void owl_function_redisplay_to_nearest(int msgid, owl_view *v)
[d54838d]661{
[7d4fbcd]662  int curmsg;
[3eeb6ed]663  /* update all views (we only have one right now) */
664  owl_view_recalculate(v);
[7d4fbcd]665
[3eeb6ed]666  /* find where the new position should be */
667  if (msgid < 0) {
668    /* If already at the end, blank the screen and move curmsg
669     * past the end of the messages. */
670    curmsg = owl_view_get_size(v);
671    owl_global_set_topmsg(&g, curmsg);
672    owl_global_set_curmsg(&g, curmsg);
673  } else {
674    curmsg = owl_view_get_nearest_to_msgid(v, msgid);
675    if (curmsg > owl_view_get_size(v) - 1)
676      curmsg = owl_view_get_size(v) - 1;
677    if (curmsg < 0)
678      curmsg = 0;
679    owl_global_set_curmsg(&g, curmsg);
680    owl_function_calculate_topmsg(OWL_DIRECTION_NONE);
681  }
682  /* if there are no messages set the direction to down in case we
683   * delete everything upwards */
684  owl_global_set_direction_downwards(&g);
[7d4fbcd]685
[3eeb6ed]686  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
687}
688
689void owl_function_expunge(void)
690{
691  owl_messagelist *ml = owl_global_get_msglist(&g);
692  owl_view *v = owl_global_get_current_view(&g);
693  int lastmsgid = owl_function_get_curmsg_id(v);
[7d4fbcd]694
695  /* expunge the message list */
696  owl_messagelist_expunge(ml);
697
[3eeb6ed]698  owl_function_redisplay_to_nearest(lastmsgid, v);
[7d4fbcd]699 
700  owl_function_makemsg("Messages expunged");
701}
702
[c79a047]703void owl_function_firstmsg(void)
[d54838d]704{
[7d4fbcd]705  owl_global_set_curmsg(&g, 0);
706  owl_global_set_topmsg(&g, 0);
707  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
708  owl_global_set_direction_downwards(&g);
709}
710
[3b17b57]711void owl_function_lastmsg(void)
[d54838d]712{
[5eeea3b]713  int oldcurmsg, curmsg;
[9e5c9f3]714  const owl_view *v;
[7d4fbcd]715
716  v=owl_global_get_current_view(&g);
[5eeea3b]717  oldcurmsg=owl_global_get_curmsg(&g);
718  curmsg=owl_view_get_size(v)-1; 
[7d4fbcd]719  if (curmsg<0) curmsg=0;
720  owl_global_set_curmsg(&g, curmsg);
[5eeea3b]721  if (oldcurmsg < curmsg) {
722    owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
723  } else if (curmsg<owl_view_get_size(v)) {
724    /* If already at the end, blank the screen and move curmsg
725     * past the end of the messages. */
726    owl_global_set_topmsg(&g, curmsg+1);
727    owl_global_set_curmsg(&g, curmsg+1);
728  } 
[3b17b57]729  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
[7d4fbcd]730  owl_global_set_direction_downwards(&g);
731}
732
[c79a047]733void owl_function_shift_right(void)
[d54838d]734{
[7d4fbcd]735  owl_global_set_rightshift(&g, owl_global_get_rightshift(&g)+10);
736}
737
[c79a047]738void owl_function_shift_left(void)
[d54838d]739{
[7d4fbcd]740  int shift;
741
742  shift=owl_global_get_rightshift(&g);
[8c97fa1]743  if (shift > 0) {
744    owl_global_set_rightshift(&g, MAX(shift - 10, 0));
[7d4fbcd]745  } else {
746    owl_function_beep();
[f51bc78]747    owl_function_makemsg("Already full left");
[7d4fbcd]748  }
749}
750
[c79a047]751void owl_function_unsuball(void)
[d54838d]752{
[60e8617]753  if (unsuball())
754    owl_function_makemsg("Unsubscribed from all messages.");
[7d4fbcd]755}
756
[95474d7]757
758/* Load zephyr subscriptions from the named 'file' and load zephyr's
759 * default subscriptions as well.  An error message is printed if
760 * 'file' can't be opened or if zephyr reports an error in
761 * subscribing.
762 *
763 * If 'file' is NULL, this look for the default filename
764 * $HOME/.zephyr.subs.  If the file can not be opened in this case
765 * only, no error message is printed.
766 */
[e19eb97]767void owl_function_loadsubs(const char *file)
[d54838d]768{
[05adccf]769  int ret, ret2, ret3;
[65b2173]770  char *path;
[ecd5dc5]771
[95474d7]772  if (file==NULL) {
773    ret=owl_zephyr_loadsubs(NULL, 0);
774  } else {
[27d8d83]775    path = owl_util_makepath(file);
776    ret=owl_zephyr_loadsubs(path, 1);
[ddbbcffa]777    g_free(path);
[95474d7]778  }
[ecd5dc5]779
[4357be8]780  /* for backwards compatibility for now */
[05adccf]781  ret2 = owl_zephyr_loaddefaultsubs();
782  ret3 = owl_zephyr_loadbarnowldefaultsubs();
[4357be8]783
[ecd5dc5]784  if (!owl_context_is_interactive(owl_global_get_context(&g))) return;
[95474d7]785
[05adccf]786  if (ret == 0 && ret2 == 0 && ret3 == 0) {
[95474d7]787    if (!file) {
788      owl_function_makemsg("Subscribed to messages.");
789    } else {
790      owl_function_makemsg("Subscribed to messages from %s", file);
791    }
[05adccf]792  } else if (ret == -1) {
793    owl_function_error("Could not read %s", file ? file : "file");
794  } else if (ret2 == -1) {
[95474d7]795    owl_function_error("Error subscribing to messages");
[05adccf]796  } else {
797    owl_function_error("Error subscribing to instanced personals");
[7d4fbcd]798  }
799}
800
[e19eb97]801void owl_function_loadloginsubs(const char *file)
[d54838d]802{
[7933748]803  int ret;
[ecd5dc5]804
[7933748]805  ret=owl_zephyr_loadloginsubs(file);
[ecd5dc5]806
807  if (!owl_context_is_interactive(owl_global_get_context(&g))) return;
[7933748]808  if (ret==0) {
809  } else if (ret==-1) {
[ec6ff52]810    owl_function_error("Could not open file for login subscriptions.");
[7933748]811  } else {
[ec6ff52]812    owl_function_error("Error subscribing to login messages from file.");
[7933748]813  }
814}
815
[c79a047]816void owl_function_suspend(void)
[d54838d]817{
[7d4fbcd]818  endwin();
819  printf("\n");
820  kill(getpid(), SIGSTOP);
821
822  /* resize to reinitialize all the windows when we come back */
823  owl_command_resize();
824}
825
[c79a047]826void owl_function_zaway_toggle(void)
[d54838d]827{
[7d4fbcd]828  if (!owl_global_is_zaway(&g)) {
829    owl_global_set_zaway_msg(&g, owl_global_get_zaway_msg_default(&g));
830    owl_function_zaway_on();
831  } else {
832    owl_function_zaway_off();
833  }
834}
835
[c79a047]836void owl_function_zaway_on(void)
[d54838d]837{
[7d4fbcd]838  owl_global_set_zaway_on(&g);
[4b660cc]839  owl_function_makemsg("zaway set (%s)", owl_global_get_zaway_msg(&g));
[7d4fbcd]840}
841
[c79a047]842void owl_function_zaway_off(void)
[d54838d]843{
[7d4fbcd]844  owl_global_set_zaway_off(&g);
[4b660cc]845  owl_function_makemsg("zaway off");
846}
847
[a38becd]848bool owl_function_is_away(void)
849{
850  return owl_global_is_zaway(&g) ||
851         owl_perlconfig_perl_call_bool("BarnOwl::Hooks::_get_is_away", 0, NULL);
852}
853
[c79a047]854void owl_function_quit(void)
[d54838d]855{
[7d4fbcd]856  char *ret;
857 
858  /* zlog out if we need to */
[7433402]859  if (owl_global_is_havezephyr(&g) &&
860      owl_global_is_shutdownlogout(&g)) {
[31e48a3]861    owl_zephyr_zlog_out();
[7d4fbcd]862  }
863
864  /* execute the commands in shutdown */
[0337203]865  ret = owl_perlconfig_execute("BarnOwl::Hooks::_shutdown();");
[3b8a563]866  g_free(ret);
[7d4fbcd]867
[d09e5a1]868  /* signal our child process, if any */
869  if (owl_global_get_newmsgproc_pid(&g)) {
870    kill(owl_global_get_newmsgproc_pid(&g), SIGHUP);
871  }
[8c46404]872
[a16d7e5]873  owl_function_debugmsg("Quitting BarnOwl");
[3ecd78b]874  owl_select_quit_loop();
[7d4fbcd]875}
876
[d54838d]877void owl_function_calculate_topmsg(int direction)
878{
[aa2f33b3]879  int recwinlines, topmsg, curmsg;
[9e5c9f3]880  const owl_view *v;
[7d4fbcd]881
882  v=owl_global_get_current_view(&g);
[aa2f33b3]883  curmsg=owl_global_get_curmsg(&g);
884  topmsg=owl_global_get_topmsg(&g);
[7d4fbcd]885  recwinlines=owl_global_get_recwin_lines(&g);
886
[f9c43ae]887  /*
[7d4fbcd]888  if (owl_view_get_size(v) < 1) {
889    return;
890  }
[f9c43ae]891  */
[aa2f33b3]892
893  switch (owl_global_get_scrollmode(&g)) {
894  case OWL_SCROLLMODE_TOP:
[f9c43ae]895    topmsg = owl_function_calculate_topmsg_top(direction, v, curmsg, topmsg, recwinlines);
[aa2f33b3]896    break;
897  case OWL_SCROLLMODE_NEARTOP:
[f9c43ae]898    topmsg = owl_function_calculate_topmsg_neartop(direction, v, curmsg, topmsg, recwinlines);
[aa2f33b3]899    break;
900  case OWL_SCROLLMODE_CENTER:
[f9c43ae]901    topmsg = owl_function_calculate_topmsg_center(direction, v, curmsg, topmsg, recwinlines);
[aa2f33b3]902    break;
903  case OWL_SCROLLMODE_PAGED:
[f9c43ae]904    topmsg = owl_function_calculate_topmsg_paged(direction, v, curmsg, topmsg, recwinlines, 0);
[aa2f33b3]905    break;
906  case OWL_SCROLLMODE_PAGEDCENTER:
[f9c43ae]907    topmsg = owl_function_calculate_topmsg_paged(direction, v, curmsg, topmsg, recwinlines, 1);
[aa2f33b3]908    break;
909  case OWL_SCROLLMODE_NORMAL:
910  default:
[f9c43ae]911    topmsg = owl_function_calculate_topmsg_normal(direction, v, curmsg, topmsg, recwinlines);
[aa2f33b3]912  }
[3a2daac]913  owl_function_debugmsg("Calculated a topmsg of %i", topmsg);
[aa2f33b3]914  owl_global_set_topmsg(&g, topmsg);
915}
916
917/* Returns what the new topmsg should be. 
918 * Passed the last direction of movement,
919 * the current view,
920 * the current message number in the view,
921 * the top message currently being displayed,
922 * and the number of lines in the recwin.
923 */
[9e5c9f3]924int owl_function_calculate_topmsg_top(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
[d54838d]925{
[f9c43ae]926  return(curmsg);
[aa2f33b3]927}
928
[9e5c9f3]929int owl_function_calculate_topmsg_neartop(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
[d54838d]930{
[aa2f33b3]931  if (curmsg>0 
932      && (owl_message_get_numlines(owl_view_get_element(v, curmsg-1))
933          <  recwinlines/2)) {
[f9c43ae]934    return(curmsg-1);
[aa2f33b3]935  } else {
[f9c43ae]936    return(curmsg);
[aa2f33b3]937  }
938}
939 
[9e5c9f3]940int owl_function_calculate_topmsg_center(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
[d54838d]941{
[aa2f33b3]942  int i, last, lines;
943
944  last = curmsg;
945  lines = 0;
946  for (i=curmsg-1; i>=0; i--) {
947    lines += owl_message_get_numlines(owl_view_get_element(v, i));
948    if (lines > recwinlines/2) break;
949    last = i;
950  }
[f9c43ae]951  return(last);
[aa2f33b3]952}
953 
[9e5c9f3]954int owl_function_calculate_topmsg_paged(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines, int center_on_page)
[d54838d]955{
[aa2f33b3]956  int i, last, lines, savey;
957 
958  /* If we're off the top of the screen, scroll up such that the
959   * curmsg is near the botton of the screen. */
960  if (curmsg < topmsg) {
961    last = curmsg;
962    lines = 0;
963    for (i=curmsg; i>=0; i--) {
964      lines += owl_message_get_numlines(owl_view_get_element(v, i));
965      if (lines > recwinlines) break;
966    last = i;
967    }
968    if (center_on_page) {
[f9c43ae]969      return(owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines));
[aa2f33b3]970    } else {
[f9c43ae]971      return(last);
[aa2f33b3]972    }
973  }
974
975  /* Find number of lines from top to bottom of curmsg (store in savey) */
976  savey=0;
977  for (i=topmsg; i<=curmsg; i++) {
978    savey+=owl_message_get_numlines(owl_view_get_element(v, i));
979  }
980
981  /* if we're off the bottom of the screen, scroll down */
982  if (savey > recwinlines) {
983    if (center_on_page) {
[f9c43ae]984      return(owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines));
[aa2f33b3]985    } else {
[f9c43ae]986      return(curmsg);
[aa2f33b3]987    }
988  }
989
990  /* else just stay as we are... */
[f9c43ae]991  return(topmsg);
[aa2f33b3]992}
993
[9e5c9f3]994int owl_function_calculate_topmsg_normal(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
[d54838d]995{
[801b7ac]996  int savey, i, foo, y;
[f9c43ae]997
[88736cb]998  if (curmsg<0) return(topmsg);
999   
[f9c43ae]1000  /* If we're off the top of the screen then center */
1001  if (curmsg<topmsg) {
1002    topmsg=owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines);
1003  }
1004
[801b7ac]1005  /* If curmsg is so far past topmsg that there are more messages than
1006     lines, skip the line counting that follows because we're
1007     certainly off screen.  */
1008  savey=curmsg-topmsg;
1009  if (savey <= recwinlines) {
1010    /* Find number of lines from top to bottom of curmsg (store in savey) */
1011    savey = 0;
1012    for (i=topmsg; i<=curmsg; i++) {
1013      savey+=owl_message_get_numlines(owl_view_get_element(v, i));
1014    }
[7d4fbcd]1015  }
1016
[f9c43ae]1017  /* If we're off the bottom of the screen, set the topmsg to curmsg
1018   * and scroll upwards */
1019  if (savey > recwinlines) {
1020    topmsg=curmsg;
[801b7ac]1021    savey=owl_message_get_numlines(owl_view_get_element(v, curmsg));
[f9c43ae]1022    direction=OWL_DIRECTION_UPWARDS;
[7d4fbcd]1023  }
[f9c43ae]1024 
[7d4fbcd]1025  /* If our bottom line is less than 1/4 down the screen then scroll up */
1026  if (direction == OWL_DIRECTION_UPWARDS || direction == OWL_DIRECTION_NONE) {
1027    if (savey < (recwinlines / 4)) {
1028      y=0;
[801b7ac]1029      for (i=curmsg; i>=0; i--) {
1030        foo=owl_message_get_numlines(owl_view_get_element(v, i));
[7d4fbcd]1031        /* will we run the curmsg off the screen? */
1032        if ((foo+y) >= recwinlines) {
[801b7ac]1033          i++;
1034          if (i>curmsg) i=curmsg;
[7d4fbcd]1035          break;
1036        }
1037        /* have saved 1/2 the screen space? */
1038        y+=foo;
1039        if (y > (recwinlines / 2)) break;
1040      }
[801b7ac]1041      if (i<0) i=0;
1042      return(i);
[7d4fbcd]1043    }
1044  }
1045
1046  if (direction == OWL_DIRECTION_DOWNWARDS || direction == OWL_DIRECTION_NONE) {
1047    /* If curmsg bottom line is more than 3/4 down the screen then scroll down */
1048    if (savey > ((recwinlines * 3)/4)) {
1049      y=0;
1050      /* count lines from the top until we can save 1/2 the screen size */
[801b7ac]1051      for (i=topmsg; i<curmsg; i++) {
1052        y+=owl_message_get_numlines(owl_view_get_element(v, i));
[7d4fbcd]1053        if (y > (recwinlines / 2)) break;
1054      }
[801b7ac]1055      if (i==curmsg) {
1056        i--;
[7d4fbcd]1057      }
[801b7ac]1058      return(i+1);
[7d4fbcd]1059    }
1060  }
[aa2f33b3]1061
[f9c43ae]1062  return(topmsg);
[7d4fbcd]1063}
1064
[c79a047]1065void owl_function_resize(void)
[d54838d]1066{
[7d4fbcd]1067  owl_global_set_resize_pending(&g);
1068}
1069
[4479497]1070void G_GNUC_PRINTF(1, 2) owl_function_debugmsg(const char *fmt, ...)
[d54838d]1071{
[4ebbfbc]1072  char *tmpbuff;
[7d4fbcd]1073  FILE *file;
1074  time_t now;
[ee6b30f]1075  struct tm tm;
[7d4fbcd]1076  va_list ap;
1077
[52761cc]1078  if (!owl_global_is_debug_fast(&g))
1079    return;
[7d4fbcd]1080
[d12a8c7]1081  file = owl_global_get_debug_file_handle(&g);
[52761cc]1082  if (!file) /* XXX should report this */
1083    return;
[7d4fbcd]1084
[52761cc]1085  now = time(NULL);
[7d4fbcd]1086
[ee6b30f]1087  tmpbuff = owl_util_format_time(localtime_r(&now, &tm));
[4ebbfbc]1088  fprintf(file, "[%d -  %s - %lds]: ",
1089          (int) getpid(), tmpbuff, now - owl_global_get_starttime(&g));
1090  g_free(tmpbuff);
[b61ad80]1091
1092  va_start(ap, fmt);
[7d4fbcd]1093  vfprintf(file, fmt, ap);
[b61ad80]1094  va_end(ap);
1095
[52761cc]1096  putc('\n', file);
[d12a8c7]1097  fflush(file);
[7d4fbcd]1098}
1099
[c79a047]1100void owl_function_beep(void)
[d54838d]1101{
[7d4fbcd]1102  if (owl_global_is_bell(&g)) {
1103    beep();
1104  }
1105}
1106
[e19eb97]1107int owl_function_subscribe(const char *class, const char *inst, const char *recip)
[d54838d]1108{
[7d4fbcd]1109  int ret;
1110
1111  ret=owl_zephyr_sub(class, inst, recip);
1112  if (ret) {
[ec6ff52]1113    owl_function_error("Error subscribing.");
[7d4fbcd]1114  } else {
1115    owl_function_makemsg("Subscribed.");
1116  }
[3617286]1117  return(ret);
[7d4fbcd]1118}
1119
[e19eb97]1120void owl_function_unsubscribe(const char *class, const char *inst, const char *recip)
[d54838d]1121{
[7d4fbcd]1122  int ret;
1123
1124  ret=owl_zephyr_unsub(class, inst, recip);
1125  if (ret) {
[ec6ff52]1126    owl_function_error("Error subscribing.");
[7d4fbcd]1127  } else {
1128    owl_function_makemsg("Unsubscribed.");
1129  }
1130}
1131
[b343c2c]1132static void _dirty_everything(gpointer data, gpointer user_data) {
1133  owl_window *w = data;
[e8128c5]1134  if (!owl_window_is_realized(w))
1135    return;
1136  owl_window_dirty(w);
[b343c2c]1137  owl_window_children_foreach(w, _dirty_everything, NULL);
[7d4fbcd]1138}
1139
[c79a047]1140void owl_function_full_redisplay(void)
[d54838d]1141{
[1d81c51]1142  /* Ask every widget to redraw itself. */
[b343c2c]1143  _dirty_everything(owl_window_get_screen(), NULL);
[1d81c51]1144  /* Force ncurses to redisplay everything. */
1145  clearok(stdscr, TRUE);
[7d4fbcd]1146}
1147
[e19eb97]1148void owl_function_popless_text(const char *text)
[d54838d]1149{
[7d4fbcd]1150  owl_popwin *pw;
1151  owl_viewwin *v;
1152
[9eb38bb]1153  if (owl_global_get_popwin(&g) || owl_global_get_viewwin(&g)) {
[4cf7b1b]1154    owl_function_error("Popwin already in use.");
1155    return;
1156  }
[03ca005]1157  pw = owl_popwin_new();
1158  owl_global_set_popwin(&g, pw);
1159  owl_popwin_up(pw);
[9eb38bb]1160
1161  v = owl_viewwin_new_text(owl_popwin_get_content(pw), text);
1162  owl_global_set_viewwin(&g, v);
1163
[07b59ea]1164  owl_global_push_context(&g, OWL_CTX_POPLESS, v, "popless", NULL);
[7d4fbcd]1165}
1166
[075ba92]1167void owl_function_popless_fmtext(const owl_fmtext *fm)
[d54838d]1168{
[7d4fbcd]1169  owl_popwin *pw;
1170  owl_viewwin *v;
1171
[9eb38bb]1172  if (owl_global_get_popwin(&g) || owl_global_get_viewwin(&g)) {
[4cf7b1b]1173    owl_function_error("Popwin already in use.");
1174    return;
1175  }
[03ca005]1176  pw = owl_popwin_new();
1177  owl_global_set_popwin(&g, pw);
1178  owl_popwin_up(pw);
[9eb38bb]1179
1180  v = owl_viewwin_new_fmtext(owl_popwin_get_content(pw), fm);
1181  owl_global_set_viewwin(&g, v);
1182
[07b59ea]1183  owl_global_push_context(&g, OWL_CTX_POPLESS, v, "popless", NULL);
[f17bff98]1184}
1185
[e19eb97]1186void owl_function_popless_file(const char *filename)
[f17bff98]1187{
1188  owl_fmtext fm;
1189  FILE *file;
[b7ee89b]1190  char *s = NULL;
[f17bff98]1191
1192  file=fopen(filename, "r");
1193  if (!file) {
1194    owl_function_error("Could not open file: %s", filename);
1195    return;
1196  }
1197
1198  owl_fmtext_init_null(&fm);
[b7ee89b]1199  while (owl_getline(&s, file))
1200    owl_fmtext_append_normal(&fm, s);
[ddbbcffa]1201  g_free(s);
[f17bff98]1202
1203  owl_function_popless_fmtext(&fm);
[7ab0020]1204  owl_fmtext_cleanup(&fm);
[f17bff98]1205  fclose(file);
[7d4fbcd]1206}
1207
[c79a047]1208void owl_function_about(void)
[d54838d]1209{
[ca1fb26a]1210  char *text = g_strdup_printf(
1211    "This is BarnOwl version %s.\n\n"
[b8a3e00]1212    "BarnOwl is a fork of the Owl zephyr client, written and\n"
[2101a50]1213    "maintained by Alejandro Sedeno and Nelson Elhage at the\n"
1214    "Massachusetts Institute of Technology. \n"
1215    "\n"
1216    "Owl was written by James Kretchmar. The first version, 0.5, was\n"
1217    "released in March 2002.\n"
1218    "\n"
1219    "The name 'owl' was chosen in reference to the owls in the\n"
1220    "Harry Potter novels, who are tasked with carrying messages\n"
[b8a3e00]1221    "between Witches and Wizards. The name 'BarnOwl' was chosen\n"
[2101a50]1222    "because we feel our owls should live closer to our ponies.\n"
1223    "\n"
[b03c714]1224    "Copyright (c) 2006-2011 The BarnOwl Developers. All rights reserved.\n"
[2101a50]1225    "Copyright (c) 2004 James Kretchmar. All rights reserved.\n"
1226    "Copyright 2002 Massachusetts Institute of Technology\n"
1227    "\n"
1228    "This program is free software. You can redistribute it and/or\n"
1229    "modify under the terms of the Sleepycat License. Use the \n"
[ca1fb26a]1230      "':show license' command to display the full license\n",
1231      version);
1232  owl_function_popless_text(text);
1233  g_free(text);
[7d4fbcd]1234}
1235
[c79a047]1236void owl_function_info(void)
[d54838d]1237{
[c08c70a]1238  const owl_message *m;
[5789230]1239  owl_fmtext fm, attrfm;
[9e5c9f3]1240  const owl_view *v;
[6646fdb]1241  char *time;
[09489b89]1242#ifdef HAVE_LIBZEPHYR
[1077891a]1243  const ZNotice_t *n;
[09489b89]1244#endif
[7d4fbcd]1245
[d0d65df]1246  owl_fmtext_init_null(&fm);
1247 
[7d4fbcd]1248  v=owl_global_get_current_view(&g);
[5eeea3b]1249  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1250  if (!m || owl_view_get_size(v)==0) {
[ec6ff52]1251    owl_function_error("No message selected\n");
[7d4fbcd]1252    return;
1253  }
1254
[5789230]1255  owl_fmtext_append_bold(&fm, "General Information:\n");
[57609b3]1256  owl_fmtext_appendf_normal(&fm, "  Msg Id    : %i\n", owl_message_get_id(m));
[df0d93a]1257
[5789230]1258  owl_fmtext_append_normal(&fm, "  Type      : ");
[37eab7f]1259  owl_fmtext_append_bold(&fm, owl_message_get_type(m));
[df0d93a]1260  owl_fmtext_append_normal(&fm, "\n");
1261
[4b464a4]1262  if (owl_message_is_direction_in(m)) {
[5789230]1263    owl_fmtext_append_normal(&fm, "  Direction : in\n");
[4b464a4]1264  } else if (owl_message_is_direction_out(m)) {
[5789230]1265    owl_fmtext_append_normal(&fm, "  Direction : out\n");
[4b464a4]1266  } else if (owl_message_is_direction_none(m)) {
[5789230]1267    owl_fmtext_append_normal(&fm, "  Direction : none\n");
[4b464a4]1268  } else {
[5789230]1269    owl_fmtext_append_normal(&fm, "  Direction : unknown\n");
[4b464a4]1270  }
[df0d93a]1271
[6646fdb]1272  time = owl_message_format_time(m);
1273  owl_fmtext_appendf_normal(&fm, "  Time      : %s\n", time);
1274  g_free(time);
[4b464a4]1275
[df0d93a]1276  if (!owl_message_is_type_admin(m)) {
[57609b3]1277    owl_fmtext_appendf_normal(&fm, "  Sender    : %s\n", owl_message_get_sender(m));
1278    owl_fmtext_appendf_normal(&fm, "  Recipient : %s\n", owl_message_get_recipient(m));
[df0d93a]1279  }
[57609b3]1280
[0ff8fb57]1281  if (owl_message_is_type_zephyr(m)) {
[5789230]1282    owl_fmtext_append_bold(&fm, "\nZephyr Specific Information:\n");
[0ff8fb57]1283   
[57609b3]1284    owl_fmtext_appendf_normal(&fm, "  Class     : %s\n", owl_message_get_class(m));
1285    owl_fmtext_appendf_normal(&fm, "  Instance  : %s\n", owl_message_get_instance(m));
1286    owl_fmtext_appendf_normal(&fm, "  Opcode    : %s\n", owl_message_get_opcode(m));
[09489b89]1287#ifdef HAVE_LIBZEPHYR
[b9517cf]1288    n = owl_message_get_notice(m);
1289    if (n != NULL) {
[6500907]1290      char *tmpbuff, *tmpbuff2;
[259e60a8]1291      int i, fields;
[d199207]1292      const char *f;
[09489b89]1293
[5a95b69]1294      if (!owl_message_is_pseudo(m)) {
1295        owl_fmtext_append_normal(&fm, "  Kind      : ");
1296        if (n->z_kind==UNSAFE) {
1297          owl_fmtext_append_normal(&fm, "UNSAFE\n");
1298        } else if (n->z_kind==UNACKED) {
1299          owl_fmtext_append_normal(&fm, "UNACKED\n");
1300        } else if (n->z_kind==ACKED) {
1301          owl_fmtext_append_normal(&fm, "ACKED\n");
1302        } else if (n->z_kind==HMACK) {
1303          owl_fmtext_append_normal(&fm, "HMACK\n");
1304        } else if (n->z_kind==HMCTL) {
1305          owl_fmtext_append_normal(&fm, "HMCTL\n");
1306        } else if (n->z_kind==SERVACK) {
1307          owl_fmtext_append_normal(&fm, "SERVACK\n");
1308        } else if (n->z_kind==SERVNAK) {
1309          owl_fmtext_append_normal(&fm, "SERVNACK\n");
1310        } else if (n->z_kind==CLIENTACK) {
1311          owl_fmtext_append_normal(&fm, "CLIENTACK\n");
1312        } else if (n->z_kind==STAT) {
1313          owl_fmtext_append_normal(&fm, "STAT\n");
1314        } else {
1315          owl_fmtext_append_normal(&fm, "ILLEGAL VALUE\n");
1316        }
[d0d65df]1317      }
[57609b3]1318      owl_fmtext_appendf_normal(&fm, "  Host      : %s\n", owl_message_get_hostname(m));
[5a95b69]1319
1320      if (!owl_message_is_pseudo(m)) {
1321        owl_fmtext_append_normal(&fm, "\n");
[57609b3]1322        owl_fmtext_appendf_normal(&fm, "  Port      : %i\n", ntohs(n->z_port));
[c55930e]1323        owl_fmtext_appendf_normal(&fm, "  Charset   : %s\n", owl_zephyr_get_charsetstr(n));
[f12d199]1324        owl_fmtext_appendf_normal(&fm, "  Auth      : %s\n", owl_zephyr_get_authstr(n));
[57609b3]1325
1326        /* FIXME make these more descriptive */
[f12d199]1327        owl_fmtext_appendf_normal(&fm, "  Checkd Ath: %i\n", n->z_checked_auth);
[57609b3]1328        owl_fmtext_appendf_normal(&fm, "  Multi notc: %s\n", n->z_multinotice);
1329        owl_fmtext_appendf_normal(&fm, "  Num other : %i\n", n->z_num_other_fields);
1330        owl_fmtext_appendf_normal(&fm, "  Msg Len   : %i\n", n->z_message_len);
[5a95b69]1331
1332        fields=owl_zephyr_get_num_fields(n);
[57609b3]1333        owl_fmtext_appendf_normal(&fm, "  Fields    : %i\n", fields);
1334
[d199207]1335        for (i = 0, f = owl_zephyr_first_raw_field(n); f != NULL;
1336             i++, f = owl_zephyr_next_raw_field(n, f)) {
1337          tmpbuff = owl_zephyr_field_as_utf8(n, f);
[6500907]1338          tmpbuff2 = owl_text_indent(tmpbuff, 14, false);
1339          owl_fmtext_appendf_normal(&fm, "  Field %i   : %s\n", i + 1, tmpbuff2);
1340          g_free(tmpbuff2);
1341          g_free(tmpbuff);
[5a95b69]1342        }
[6500907]1343        tmpbuff = owl_text_indent(n->z_default_format, 14, false);
1344        owl_fmtext_appendf_normal(&fm, "  Default Fm: %s\n", tmpbuff);
1345        g_free(tmpbuff);
[d0d65df]1346      }
[57609b3]1347
[7d4fbcd]1348    }
[57609b3]1349#endif
[7d4fbcd]1350  }
[0ff8fb57]1351
[a16d7e5]1352  owl_fmtext_append_bold(&fm, "\nBarnOwl Message Attributes:\n");
[5789230]1353  owl_message_attributes_tofmtext(m, &attrfm);
1354  owl_fmtext_append_fmtext(&fm, &attrfm);
[d0d65df]1355 
1356  owl_function_popless_fmtext(&fm);
[7ab0020]1357  owl_fmtext_cleanup(&fm);
1358  owl_fmtext_cleanup(&attrfm);
[7d4fbcd]1359}
1360
[5639bf2]1361/* print the current message in a popup window.
1362 * Use the 'default' style regardless of whatever
1363 * style the user may be using
1364 */
[c79a047]1365void owl_function_curmsg_to_popwin(void)
[d54838d]1366{
[9e5c9f3]1367  const owl_view *v;
[c08c70a]1368  const owl_message *m;
[1fdab04]1369  const owl_style *s;
[5639bf2]1370  owl_fmtext fm;
[7d4fbcd]1371
[5639bf2]1372  v=owl_global_get_current_view(&g);
1373  s=owl_global_get_style_by_name(&g, "default");
[7d4fbcd]1374
[5eeea3b]1375  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1376
1377  if (!m || owl_view_get_size(v)==0) {
[ec6ff52]1378    owl_function_error("No current message");
[7d4fbcd]1379    return;
1380  }
1381
[5639bf2]1382  owl_fmtext_init_null(&fm);
1383  owl_style_get_formattext(s, &fm, m);
1384
1385  owl_function_popless_fmtext(&fm);
[7ab0020]1386  owl_fmtext_cleanup(&fm);
[7d4fbcd]1387}
1388
[d54838d]1389void owl_function_page_curmsg(int step)
1390{
[7d4fbcd]1391  /* scroll down or up within the current message IF the message is truncated */
1392
1393  int offset, curmsg, lines;
[9e5c9f3]1394  const owl_view *v;
[7d4fbcd]1395  owl_message *m;
1396
1397  offset=owl_global_get_curmsg_vert_offset(&g);
1398  v=owl_global_get_current_view(&g);
1399  curmsg=owl_global_get_curmsg(&g);
1400  m=owl_view_get_element(v, curmsg);
[5eeea3b]1401  if (!m || owl_view_get_size(v)==0) return;
[7d4fbcd]1402  lines=owl_message_get_numlines(m);
1403
1404  if (offset==0) {
1405    /* Bail if the curmsg isn't the last one displayed */
1406    if (curmsg != owl_mainwin_get_last_msg(owl_global_get_mainwin(&g))) {
[f51bc78]1407      owl_function_makemsg("The entire message is already displayed");
[7d4fbcd]1408      return;
1409    }
1410   
1411    /* Bail if we're not truncated */
1412    if (!owl_mainwin_is_curmsg_truncated(owl_global_get_mainwin(&g))) {
[f51bc78]1413      owl_function_makemsg("The entire message is already displayed");
[7d4fbcd]1414      return;
1415    }
1416  }
1417 
1418 
1419  /* don't scroll past the last line */
1420  if (step>0) {
1421    if (offset+step > lines-1) {
1422      owl_global_set_curmsg_vert_offset(&g, lines-1);
1423    } else {
1424      owl_global_set_curmsg_vert_offset(&g, offset+step);
1425    }
1426  }
1427
1428  /* would we be before the beginning of the message? */
1429  if (step<0) {
1430    if (offset+step<0) {
1431      owl_global_set_curmsg_vert_offset(&g, 0);
1432    } else {
1433      owl_global_set_curmsg_vert_offset(&g, offset+step);
1434    }
1435  }
1436 
1437  /* redisplay */
1438  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1439}
1440
[d54838d]1441void owl_function_resize_typwin(int newsize)
1442{
[7d4fbcd]1443  owl_global_set_typwin_lines(&g, newsize);
[f6fae8d]1444  owl_mainpanel_layout_contents(&g.mainpanel);
[7d4fbcd]1445}
1446
[c79a047]1447void owl_function_mainwin_pagedown(void)
[d54838d]1448{
[7d4fbcd]1449  int i;
1450
1451  i=owl_mainwin_get_last_msg(owl_global_get_mainwin(&g));
1452  if (i<0) return;
[f2e36b5]1453  if (owl_mainwin_is_last_msg_truncated(owl_global_get_mainwin(&g))
1454      && (owl_global_get_curmsg(&g) < i)
1455      && (i>0)) {
1456    i--;
1457  }
[7d4fbcd]1458  owl_global_set_curmsg(&g, i);
1459  owl_function_nextmsg();
1460}
1461
[c79a047]1462void owl_function_mainwin_pageup(void)
[d54838d]1463{
[7d4fbcd]1464  owl_global_set_curmsg(&g, owl_global_get_topmsg(&g));
1465  owl_function_prevmsg();
1466}
1467
[c79a047]1468void owl_function_getsubs(void)
[d54838d]1469{
[09489b89]1470  char *buff;
[7d4fbcd]1471
[09489b89]1472  buff=owl_zephyr_getsubs();
[7d4fbcd]1473
[09489b89]1474  if (buff) {
1475    owl_function_popless_text(buff);
1476  } else {
1477    owl_function_popless_text("Error getting subscriptions");
[7d4fbcd]1478  }
[09489b89]1479           
[ddbbcffa]1480  g_free(buff);
[7d4fbcd]1481}
1482
[c79a047]1483void owl_function_printallvars(void)
[d54838d]1484{
[ca54fd6]1485  const owl_variable *v;
[e19eb97]1486  const char *name;
[010a951]1487  char *var;
[ce68f23]1488  GPtrArray *varnames;
1489  int i;
[b4c270c]1490  GString *str   = g_string_new("");
[7d4fbcd]1491
[b4c270c]1492  g_string_append_printf(str, "%-20s = %s\n", "VARIABLE", "VALUE");
1493  g_string_append_printf(str, "%-20s   %s\n",  "--------", "-----");
[ce68f23]1494  varnames = owl_variable_dict_get_names(owl_global_get_vardict(&g));
1495  for (i = 0; i < varnames->len; i++) {
1496    name = varnames->pdata[i];
[7d4fbcd]1497    if (name && name[0]!='_') {
[b4c270c]1498      g_string_append_printf(str, "\n%-20s = ", name);
[ca54fd6]1499      v = owl_variable_get_var(owl_global_get_vardict(&g), name);
1500      var = owl_variable_get_tostring(v);
[010a951]1501      if (var) {
[ca749a9]1502        g_string_append(str, var);
1503        g_free(var);
1504      } else {
1505        g_string_append(str, "<null>");
[010a951]1506      }
[7d4fbcd]1507    }
1508  }
[b4c270c]1509  g_string_append(str, "\n");
[ce68f23]1510  owl_ptr_array_free(varnames, g_free);
[b4c270c]1511
1512  owl_function_popless_text(str->str);
[d222c44]1513  g_string_free(str, true);
[7d4fbcd]1514}
1515
[c79a047]1516void owl_function_show_variables(void)
[d54838d]1517{
[ca54fd6]1518  const owl_variable *v;
[ce68f23]1519  GPtrArray *varnames;
[7d4fbcd]1520  owl_fmtext fm; 
[ce68f23]1521  int i;
[e19eb97]1522  const char *varname;
[7d4fbcd]1523
1524  owl_fmtext_init_null(&fm);
1525  owl_fmtext_append_bold(&fm, 
1526      "Variables: (use 'show variable <name>' for details)\n");
[ce68f23]1527  varnames = owl_variable_dict_get_names(owl_global_get_vardict(&g));
1528  for (i = 0; i < varnames->len; i++) {
1529    varname = varnames->pdata[i];
[7d4fbcd]1530    if (varname && varname[0]!='_') {
[ca54fd6]1531      v = owl_variable_get_var(owl_global_get_vardict(&g), varname);
1532      owl_variable_describe(v, &fm);
[7d4fbcd]1533    }
1534  }
[ce68f23]1535  owl_ptr_array_free(varnames, g_free);
[7d4fbcd]1536  owl_function_popless_fmtext(&fm);
[7ab0020]1537  owl_fmtext_cleanup(&fm);
[7d4fbcd]1538}
1539
[e19eb97]1540void owl_function_show_variable(const char *name)
[d54838d]1541{
[ca54fd6]1542  const owl_variable *v;
[7d4fbcd]1543  owl_fmtext fm; 
1544
1545  owl_fmtext_init_null(&fm);
[ca54fd6]1546  v = owl_variable_get_var(owl_global_get_vardict(&g), name);
1547  if (v)
1548    owl_variable_get_help(v, &fm);
1549  else
1550    owl_fmtext_append_normal(&fm, "No such variable...\n");
[7d4fbcd]1551  owl_function_popless_fmtext(&fm);
[7ab0020]1552  owl_fmtext_cleanup(&fm);
[7d4fbcd]1553}
1554
[efeec7f]1555void owl_function_delete_and_expunge_by_id(int id, bool exclaim_success)
1556{
1557  const owl_messagelist *ml = owl_global_get_msglist(&g);
1558  int msg = owl_messagelist_get_index_by_id(ml, id);
1559  if (msg < 0) {
1560    owl_function_error("No message with id %d: unable to delete", id);
1561  } else {
1562    owl_function_delete_and_expunge_message(msg);
1563    if (exclaim_success)
1564      owl_function_makemsg("Message deleted and expunged");
1565  }
1566}
1567
[7d4fbcd]1568/* note: this applies to global message list, not to view.
1569 * If flag is 1, deletes.  If flag is 0, undeletes. */
[d54838d]1570void owl_function_delete_by_id(int id, int flag)
1571{
[3eb599d]1572  const owl_messagelist *ml;
[7d4fbcd]1573  owl_message *m;
1574  ml = owl_global_get_msglist(&g);
1575  m = owl_messagelist_get_by_id(ml, id);
1576  if (m) {
1577    if (flag == 1) {
1578      owl_message_mark_delete(m);
1579    } else if (flag == 0) {
1580      owl_message_unmark_delete(m);
1581    }
1582    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1583  } else {
[ec6ff52]1584    owl_function_error("No message with id %d: unable to mark for (un)delete",id);
[7d4fbcd]1585  }
1586}
1587
[c79a047]1588void owl_function_delete_automsgs(void)
[d54838d]1589{
[7d4fbcd]1590  /* mark for deletion all messages in the current view that match the
1591   * 'trash' filter */
1592
1593  int i, j, count;
1594  owl_message *m;
[9e5c9f3]1595  const owl_view *v;
[4542047]1596  const owl_filter *f;
[7d4fbcd]1597
1598  /* get the trash filter */
1599  f=owl_global_get_filter(&g, "trash");
1600  if (!f) {
[ec6ff52]1601    owl_function_error("No trash filter defined");
[7d4fbcd]1602    return;
1603  }
1604
1605  v=owl_global_get_current_view(&g);
1606
1607  count=0;
1608  j=owl_view_get_size(v);
1609  for (i=0; i<j; i++) {
1610    m=owl_view_get_element(v, i);
1611    if (owl_filter_message_match(f, m)) {
1612      count++;
1613      owl_message_mark_delete(m);
1614    }
1615  }
1616  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
[1c6c4d3]1617  owl_function_makemsg("%i messages marked for deletion", count);
[7d4fbcd]1618}
1619
[c79a047]1620void owl_function_status(void)
[d54838d]1621{
[4ebbfbc]1622  char *tmpbuff;
[49a8434]1623  char buff[MAXPATHLEN+1];
[7d4fbcd]1624  time_t start;
[ee6b30f]1625  struct tm tm;
[7d4fbcd]1626  int up, days, hours, minutes;
[a352335c]1627  owl_fmtext fm;
1628
1629  owl_fmtext_init_null(&fm);
[7d4fbcd]1630
1631  start=owl_global_get_starttime(&g);
1632
[d9b0b972]1633  owl_fmtext_append_normal(&fm, "General Information:\n");
1634
1635  owl_fmtext_append_normal(&fm, "  Version: ");
[ca1fb26a]1636  owl_fmtext_append_normal(&fm, version);
[a352335c]1637  owl_fmtext_append_normal(&fm, "\n");
1638
[cdd3959]1639  owl_fmtext_append_normal(&fm, "  Startup Arguments: ");
[a352335c]1640  owl_fmtext_append_normal(&fm, owl_global_get_startupargs(&g));
1641  owl_fmtext_append_normal(&fm, "\n");
[b6a7367]1642
1643  owl_fmtext_append_normal(&fm, "  Current Directory: ");
[49a8434]1644  if(getcwd(buff, MAXPATHLEN) == NULL) {
1645    owl_fmtext_append_normal(&fm, "<Error in getcwd>");
1646  } else {
1647    owl_fmtext_append_normal(&fm, buff);
1648  }
[b6a7367]1649  owl_fmtext_append_normal(&fm, "\n");
1650
[ee6b30f]1651  tmpbuff = owl_util_format_time(localtime_r(&start, &tm));
[4ebbfbc]1652  owl_fmtext_appendf_normal(&fm, "  Startup Time: %s\n", tmpbuff);
1653  g_free(tmpbuff);
[7d4fbcd]1654
1655  up=owl_global_get_runtime(&g);
1656  days=up/86400;
1657  up-=days*86400;
1658  hours=up/3600;
1659  up-=hours*3600;
1660  minutes=up/60;
1661  up-=minutes*60;
[c1d166b]1662  owl_fmtext_appendf_normal(&fm, "  Run Time: %i days %2.2i:%2.2i:%2.2i\n", days, hours, minutes, up);
[7d4fbcd]1663
[d9b0b972]1664  owl_fmtext_append_normal(&fm, "\nProtocol Options:\n");
1665  owl_fmtext_append_normal(&fm, "  Zephyr included    : ");
1666  if (owl_global_is_havezephyr(&g)) {
1667    owl_fmtext_append_normal(&fm, "yes\n");
[7d4fbcd]1668  } else {
[d9b0b972]1669    owl_fmtext_append_normal(&fm, "no\n");
[7d4fbcd]1670  }
[d9b0b972]1671  owl_fmtext_append_normal(&fm, "  Loopback included  : yes\n");
1672
[8262340]1673
[d9b0b972]1674  owl_fmtext_append_normal(&fm, "\nBuild Options:\n");
1675  owl_fmtext_append_normal(&fm, "  Stderr redirection : ");
1676#if OWL_STDERR_REDIR
1677  owl_fmtext_append_normal(&fm, "yes\n");
1678#else
1679  owl_fmtext_append_normal(&fm, "no\n");
1680#endif
[a352335c]1681
1682  owl_function_popless_fmtext(&fm);
[7ab0020]1683  owl_fmtext_cleanup(&fm);
[7d4fbcd]1684}
1685
[c79a047]1686void owl_function_show_term(void)
[d54838d]1687{
[7d4fbcd]1688  owl_fmtext fm;
1689
1690  owl_fmtext_init_null(&fm);
[c1d166b]1691  owl_fmtext_appendf_normal(&fm, "Terminal Lines: %i\nTerminal Columns: %i\n",
[7d4fbcd]1692          owl_global_get_lines(&g),
1693          owl_global_get_cols(&g));
1694
[7b4f3be]1695  if (has_colors()) {
[7d4fbcd]1696    owl_fmtext_append_normal(&fm, "Color: Yes\n");
[9efa5bd]1697    owl_fmtext_appendf_normal(&fm, "Number of color pairs: %i\n", owl_util_get_colorpairs());
[c1d166b]1698    owl_fmtext_appendf_normal(&fm, "Can change colors: %s\n", can_change_color() ? "yes" : "no");
[7d4fbcd]1699  } else {
1700    owl_fmtext_append_normal(&fm, "Color: No\n");
1701  }
1702
1703  owl_function_popless_fmtext(&fm);
[7ab0020]1704  owl_fmtext_cleanup(&fm);
[7d4fbcd]1705}
1706
[e7cc1c3]1707/* if type = 0 then normal reply.
1708 * if type = 1 then it's a reply to sender
1709 * if enter = 0 then allow the command to be edited
1710 * if enter = 1 then don't wait for editing
1711 */
[d54838d]1712void owl_function_reply(int type, int enter)
1713{
[740d5f7]1714  char *buff=NULL;
[c08c70a]1715  const owl_message *m;
[4542047]1716  const owl_filter *f;
[7d4fbcd]1717 
1718  if (owl_view_get_size(owl_global_get_current_view(&g))==0) {
[ec6ff52]1719    owl_function_error("No message selected");
[7d4fbcd]1720  } else {
[5ebc202]1721    char *cmd;
[7d4fbcd]1722   
1723    m=owl_view_get_element(owl_global_get_current_view(&g), owl_global_get_curmsg(&g));
[5eeea3b]1724    if (!m) {
[ec6ff52]1725      owl_function_error("No message selected");
[5eeea3b]1726      return;
1727    }
1728
[7d4fbcd]1729    /* first check if we catch the reply-lockout filter */
1730    f=owl_global_get_filter(&g, "reply-lockout");
1731    if (f) {
1732      if (owl_filter_message_match(f, m)) {
[ec6ff52]1733        owl_function_error("Sorry, replies to this message have been disabled by the reply-lockout filter");
[7d4fbcd]1734        return;
1735      }
1736    }
[4b464a4]1737
[2c09826]1738    /* then check if it's a question and just bring up the command prompt */
1739    if (owl_message_is_question(m)) {
1740      owl_function_start_command("");
1741      return;
1742    }
1743
[740d5f7]1744    if((type == 0 &&
1745        (cmd=owl_perlconfig_message_call_method(m, "replycmd", 0, NULL))) ||
1746       (type == 1 &&
1747        (cmd=owl_perlconfig_message_call_method(m, "replysendercmd", 0, NULL)))) {
1748      buff = cmd;
[d09e5a1]1749    }
[1b6b2f3]1750
[e0540e4]1751    if(!buff) {
1752        owl_function_error("I don't know how to reply to that message.");
1753        return;
1754    }
[740d5f7]1755
[d09e5a1]1756    if (enter) {
1757      owl_history *hist = owl_global_get_cmd_history(&g);
[b470451]1758      owl_history_store(hist, buff, false);
[d09e5a1]1759      owl_function_command_norv(buff);
1760    } else {
1761      owl_function_start_command(buff);
[7d4fbcd]1762    }
[ddbbcffa]1763    g_free(buff);
[7d4fbcd]1764  }
1765}
1766
[e19eb97]1767void owl_function_zlocate(int argc, const char *const *argv, int auth)
[d54838d]1768{
[2527615]1769  owl_fmtext fm;
[dca3b27]1770  char *ptr;
1771  char *result;
[2527615]1772  int i;
1773
1774  owl_fmtext_init_null(&fm);
[7d4fbcd]1775
[2527615]1776  for (i=0; i<argc; i++) {
[dca3b27]1777    ptr = long_zuser(argv[i]);
1778    result = owl_zephyr_zlocate(ptr, auth);
1779    owl_fmtext_append_normal(&fm, result);
[ddbbcffa]1780    g_free(result);
1781    g_free(ptr);
[7d4fbcd]1782  }
1783
[2527615]1784  owl_function_popless_fmtext(&fm);
[7ab0020]1785  owl_fmtext_cleanup(&fm);
[7d4fbcd]1786}
1787
[7803326]1788void owl_callback_command(owl_editwin *e, bool success)
[5934b87]1789{
[7803326]1790  if (!success) return;
[5934b87]1791  char *rv;
1792  const char *line = owl_editwin_get_text(e);
1793
1794  rv = owl_function_command(line);
1795   if (rv) {
1796    owl_function_makemsg("%s", rv);
[ddbbcffa]1797    g_free(rv);
[5934b87]1798  }
1799}
1800
[c737503]1801owl_editwin *owl_function_start_command(const char *line)
[d54838d]1802{
[7d4fbcd]1803  owl_editwin *tw;
[c394de8]1804  owl_context *ctx;
[7d4fbcd]1805
[58d47ca]1806  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
[10b866d]1807
[7d4fbcd]1808  owl_editwin_set_locktext(tw, "command: ");
1809
[47519e1b]1810  owl_editwin_insert_string(tw, line);
[cf83b7a]1811
[4a41f16]1812  ctx = owl_editcontext_new(OWL_CTX_EDITLINE, tw, "editline",
1813                            owl_global_deactivate_editcontext, &g);
[c394de8]1814  owl_global_push_context_obj(&g, ctx);
[5934b87]1815  owl_editwin_set_callback(tw, owl_callback_command);
[c737503]1816  return tw;
[cf83b7a]1817}
1818
[c737503]1819owl_editwin *owl_function_start_question(const char *line)
[cf83b7a]1820{
1821  owl_editwin *tw;
[c394de8]1822  owl_context *ctx;
[cf83b7a]1823
[58d47ca]1824  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
[cf83b7a]1825
1826  owl_editwin_set_locktext(tw, line);
1827
[4a41f16]1828  ctx = owl_editcontext_new(OWL_CTX_EDITRESPONSE, tw, "editresponse",
1829                            owl_global_deactivate_editcontext, &g);
[c394de8]1830  owl_global_push_context_obj(&g, ctx);
[9186c75]1831  return tw;
[7d4fbcd]1832}
1833
[c737503]1834owl_editwin *owl_function_start_password(const char *line)
[453bd70]1835{
1836  owl_editwin *tw;
[c394de8]1837  owl_context *ctx;
[453bd70]1838
[58d47ca]1839  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, NULL);
1840
[453bd70]1841  owl_editwin_set_echochar(tw, '*');
1842
1843  owl_editwin_set_locktext(tw, line);
1844
[4a41f16]1845  ctx = owl_editcontext_new(OWL_CTX_EDITRESPONSE, tw, "editresponse",
1846                            owl_global_deactivate_editcontext, &g);
[c394de8]1847  owl_global_push_context_obj(&g, ctx);
[9186c75]1848  return tw;
[453bd70]1849}
1850
[6829afc]1851CALLER_OWN char *owl_function_exec(int argc, const char *const *argv, const char *buff, int type)
[d54838d]1852{
[7d4fbcd]1853  /* if type == 1 display in a popup
1854   * if type == 2 display an admin messages
1855   * if type == 0 return output
1856   * else display in a popup
1857   */
[e19eb97]1858  const char *redirect = " 2>&1 < /dev/null";
[65b2173]1859  char *newbuff;
[b7ee89b]1860  char *out;
[7d4fbcd]1861  FILE *p;
1862
[2a2bb60]1863#if OWL_STDERR_REDIR
1864  redirect = " < /dev/null";
1865#endif
1866
[7d4fbcd]1867  if (argc<2) {
[ec6ff52]1868    owl_function_error("Wrong number of arguments to the exec command");
[7d4fbcd]1869    return NULL;
1870  }
1871
1872  buff = skiptokens(buff, 1);
[1a30f05]1873  newbuff = g_strdup_printf("exec%s; %s", redirect, buff);
[7d4fbcd]1874
[7ba9e0de]1875  if (type == OWL_OUTPUT_POPUP) {
[afbf668]1876    owl_popexec_new(newbuff);
[7d4fbcd]1877  } else {
[b7ee89b]1878    p = popen(newbuff, "r");
1879    out = owl_slurp(p);
[afbf668]1880    pclose(p);
1881   
[2cfc6d7]1882    if (type == OWL_OUTPUT_RETURN) {
[ddbbcffa]1883      g_free(newbuff);
[afbf668]1884      return out;
[7ba9e0de]1885    } else if (type == OWL_OUTPUT_ADMINMSG) {
[afbf668]1886      owl_function_adminmsg(buff, out);
1887    }
[ddbbcffa]1888    g_free(out);
[7d4fbcd]1889  }
[ddbbcffa]1890  g_free(newbuff);
[7d4fbcd]1891  return NULL;
1892}
1893
[6829afc]1894CALLER_OWN char *owl_function_perl(int argc, const char *const *argv, const char *buff, int type)
[d54838d]1895{
[7d4fbcd]1896  /* if type == 1 display in a popup
1897   * if type == 2 display an admin messages
1898   * if type == 0 return output
1899   * else display in a popup
1900   */
1901  char *perlout;
1902
1903  if (argc<2) {
[ec6ff52]1904    owl_function_error("Wrong number of arguments to perl command");
[7d4fbcd]1905    return NULL;
1906  }
1907
1908  /* consume first token (argv[0]) */
1909  buff = skiptokens(buff, 1);
1910
[f1e629d]1911  perlout = owl_perlconfig_execute(buff);
[7d4fbcd]1912  if (perlout) { 
[7ba9e0de]1913    if (type == OWL_OUTPUT_POPUP) {
[7d4fbcd]1914      owl_function_popless_text(perlout);
[7ba9e0de]1915    } else if (type == OWL_OUTPUT_ADMINMSG) {
[7d4fbcd]1916      owl_function_adminmsg(buff, perlout);
[7ba9e0de]1917    } else if (type == OWL_OUTPUT_RETURN) {
[7d4fbcd]1918      return perlout;
1919    }
[ddbbcffa]1920    g_free(perlout);
[7d4fbcd]1921  }
1922  return NULL;
1923}
1924
[5e0b690]1925/* Change the filter associated with the current view.
1926 * This also figures out which message in the new filter
1927 * should have the pointer.
1928 */
[e19eb97]1929void owl_function_change_currentview_filter(const char *filtname)
[c3ab155]1930{
1931  owl_view *v;
1932  owl_filter *f;
1933  int curid=-1, newpos, curmsg;
[c08c70a]1934  const owl_message *curm=NULL;
[c3ab155]1935
1936  v=owl_global_get_current_view(&g);
1937
1938  curmsg=owl_global_get_curmsg(&g);
1939  if (curmsg==-1) {
1940    owl_function_debugmsg("Hit the curmsg==-1 case in change_view");
1941  } else {
1942    curm=owl_view_get_element(v, curmsg);
1943    if (curm) {
1944      curid=owl_message_get_id(curm);
1945      owl_view_save_curmsgid(v, curid);
1946    }
1947  }
1948
1949  f=owl_global_get_filter(&g, filtname);
1950  if (!f) {
[ec6ff52]1951    owl_function_error("Unknown filter %s", filtname);
[c3ab155]1952    return;
1953  }
1954
1955  owl_view_new_filter(v, f);
1956
1957  /* Figure out what to set the current message to.
1958   * - If the view we're leaving has messages in it, go to the closest message
1959   *   to the last message pointed to in that view.
1960   * - If the view we're leaving is empty, try to restore the position
1961   *   from the last time we were in the new view.  */
1962  if (curm) {
1963    newpos = owl_view_get_nearest_to_msgid(v, curid);
1964  } else {
1965    newpos = owl_view_get_nearest_to_saved(v);
1966  }
1967
1968  owl_global_set_curmsg(&g, newpos);
1969  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
1970  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1971  owl_global_set_direction_downwards(&g);
1972}
[7d4fbcd]1973
[5e0b690]1974/* Create a new filter, or replace an existing one
[287c634]1975 * with a new definition. Returns true on success.
[5e0b690]1976 */
[287c634]1977bool owl_function_create_filter(int argc, const char *const *argv)
[d54838d]1978{
[7d4fbcd]1979  owl_filter *f;
[9e5c9f3]1980  const owl_view *v;
[23fddad]1981  int inuse = 0;
[7d4fbcd]1982
1983  if (argc < 2) {
[ec6ff52]1984    owl_function_error("Wrong number of arguments to filter command");
[287c634]1985    return false;
[7d4fbcd]1986  }
1987
[3895e23]1988  owl_function_debugmsg("owl_function_create_filter: starting to create filter named %s", argv[1]);
1989
[7d4fbcd]1990  v=owl_global_get_current_view(&g);
1991
1992  /* don't touch the all filter */
1993  if (!strcmp(argv[1], "all")) {
[ec6ff52]1994    owl_function_error("You may not change the 'all' filter.");
[287c634]1995    return false;
[7d4fbcd]1996  }
1997
1998  /* deal with the case of trying change the filter color */
1999  if (argc==4 && !strcmp(argv[2], "-c")) {
2000    f=owl_global_get_filter(&g, argv[1]);
2001    if (!f) {
[ec6ff52]2002      owl_function_error("The filter '%s' does not exist.", argv[1]);
[287c634]2003      return false;
[7d4fbcd]2004    }
[601733d]2005    if (owl_util_string_to_color(argv[3])==OWL_COLOR_INVALID) {
[12c35df]2006      owl_function_error("The color '%s' is not available.", argv[3]);
[287c634]2007      return false;
[12c35df]2008    }
[8fa9562]2009    owl_filter_set_fgcolor(f, owl_util_string_to_color(argv[3]));
2010    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
[287c634]2011    return false;
[8fa9562]2012  }
2013  if (argc==4 && !strcmp(argv[2], "-b")) {
2014    f=owl_global_get_filter(&g, argv[1]);
2015    if (!f) {
2016      owl_function_error("The filter '%s' does not exist.", argv[1]);
[287c634]2017      return false;
[8fa9562]2018    }
[601733d]2019    if (owl_util_string_to_color(argv[3])==OWL_COLOR_INVALID) {
[8fa9562]2020      owl_function_error("The color '%s' is not available.", argv[3]);
[287c634]2021      return false;
[8fa9562]2022    }
2023    owl_filter_set_bgcolor(f, owl_util_string_to_color(argv[3]));
[7d4fbcd]2024    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
[287c634]2025    return true;
[7d4fbcd]2026  }
2027
2028  /* create the filter and check for errors */
[23fddad]2029  f = owl_filter_new(argv[1], argc-2, argv+2);
2030  if (f == NULL) {
[06470d7]2031    owl_function_error("Invalid filter: %s", argv[1]);
[287c634]2032    return false;
[7d4fbcd]2033  }
2034
2035  /* if the named filter is in use by the current view, remember it */
2036  if (!strcmp(owl_view_get_filtname(v), argv[1])) {
2037    inuse=1;
2038  }
2039
2040  /* if the named filter already exists, nuke it */
2041  if (owl_global_get_filter(&g, argv[1])) {
2042    owl_global_remove_filter(&g, argv[1]);
2043  }
2044
2045  /* add the filter */
2046  owl_global_add_filter(&g, f);
2047
2048  /* if it was in use by the current view then update */
2049  if (inuse) {
[3895e23]2050    owl_function_change_currentview_filter(argv[1]);
[7d4fbcd]2051  }
2052  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
[287c634]2053  return true;
[7d4fbcd]2054}
2055
[3895e23]2056/* If 'filtername' does not start with 'not-' create a filter named
2057 * 'not-<filtername>' defined as "not filter <filtername>".  If the
2058 * filter 'not-<filtername>' already exists, do not overwrite it.  If
2059 * 'filtername' begins with 'not-' and a filter 'filtername' already
2060 * exists, then do nothing.  If the filter 'filtername' does not
2061 * exist, create it and define it as 'not filter <filtername>'
2062 *
2063 * Returns the name of the negated filter, which the caller must free.
2064 */
[6829afc]2065CALLER_OWN char *owl_function_create_negative_filter(const char *filtername)
[3895e23]2066{
2067  char *newname;
[4542047]2068  const owl_filter *tmpfilt;
[e19eb97]2069  const char *argv[5];
[3895e23]2070
2071  owl_function_debugmsg("owl_function_create_negative_filter");
2072 
2073  if (!strncmp(filtername, "not-", 4)) {
[d4927a7]2074    newname=g_strdup(filtername+4);
[3895e23]2075  } else {
[3472845]2076    newname=g_strdup_printf("not-%s", filtername);
[3895e23]2077  }
2078
2079  tmpfilt=owl_global_get_filter(&g, newname);
2080  if (!tmpfilt) {
2081    argv[0]="filter"; /* anything is fine here */
2082    argv[1]=newname;
2083    argv[2]="not";
2084    argv[3]="filter";
2085    argv[4]=filtername;
2086    owl_function_create_filter(5, argv);
2087  }
2088
2089  owl_function_debugmsg("owl_function_create_negative_filter: returning with %s", newname);
2090  return(newname);
2091}
2092
[c79a047]2093void owl_function_show_filters(void)
[d54838d]2094{
[4542047]2095  const owl_filter *f;
[129e609]2096  GList *fl;
[7d4fbcd]2097  owl_fmtext fm;
2098
2099  owl_fmtext_init_null(&fm);
2100
2101  owl_fmtext_append_bold(&fm, "Filters:\n");
2102
[129e609]2103  for (fl = g.filterlist; fl; fl = g_list_next(fl)) {
2104    f = fl->data;
[7d4fbcd]2105    owl_fmtext_append_normal(&fm, "   ");
[7b4f3be]2106    owl_fmtext_append_normal_color(&fm, owl_filter_get_name(f),
2107                                   owl_filter_get_fgcolor(f),
2108                                   owl_filter_get_bgcolor(f));
[7d4fbcd]2109    owl_fmtext_append_normal(&fm, "\n");
2110  }
2111  owl_function_popless_fmtext(&fm);
[7ab0020]2112  owl_fmtext_cleanup(&fm);
[7d4fbcd]2113}
2114
[e19eb97]2115void owl_function_show_filter(const char *name)
[d54838d]2116{
[4542047]2117  const owl_filter *f;
[cdc6ff1]2118  char *buff, *tmp;
[7d4fbcd]2119
2120  f=owl_global_get_filter(&g, name);
2121  if (!f) {
[ec6ff52]2122    owl_function_error("There is no filter named %s", name);
[7d4fbcd]2123    return;
2124  }
[cdc6ff1]2125  tmp = owl_filter_print(f);
[3472845]2126  buff = g_strdup_printf("%s: %s", owl_filter_get_name(f), tmp);
[7d4fbcd]2127  owl_function_popless_text(buff);
[ddbbcffa]2128  g_free(buff);
2129  g_free(tmp);
[7d4fbcd]2130}
2131
[c79a047]2132void owl_function_show_zpunts(void)
[d54838d]2133{
[4542047]2134  const owl_filter *f;
[e6d7e4e]2135  const GPtrArray *fl;
[0504f63]2136  char *tmp;
[7d4fbcd]2137  owl_fmtext fm;
[e6d7e4e]2138  int i;
[7d4fbcd]2139
2140  owl_fmtext_init_null(&fm);
2141
2142  fl=owl_global_get_puntlist(&g);
2143  owl_fmtext_append_bold(&fm, "Active zpunt filters:\n");
2144
[e6d7e4e]2145  for (i = 0; i < fl->len; i++) {
2146    f = fl->pdata[i];
[e3a75ed]2147    owl_fmtext_appendf_normal(&fm, "[% 2d] ", i+1);
[0504f63]2148    tmp = owl_filter_print(f);
2149    owl_fmtext_append_normal(&fm, tmp);
[ddbbcffa]2150    g_free(tmp);
[7d4fbcd]2151  }
2152  owl_function_popless_fmtext(&fm);
[7ab0020]2153  owl_fmtext_cleanup(&fm);
[7d4fbcd]2154}
2155
[3abf28b]2156/* Create a filter for a class, instance if one doesn't exist.  If
2157 * instance is NULL then catch all messgaes in the class.  Returns the
[c7fe23e]2158 * name of the filter or null.  The caller must free this name.
[66e409c]2159 * If 'related' is nonzero, encompass unclasses and .d classes as well.
[3abf28b]2160 */
[6829afc]2161CALLER_OWN char *owl_function_classinstfilt(const char *c, const char *i, int related) 
[d54838d]2162{
[7d4fbcd]2163  owl_filter *f;
[c426bc2]2164  char *filtname;
[d54838d]2165  char *tmpclass, *tmpinstance = NULL;
[7a20e4c]2166  char *class, *instance = NULL;
[c426bc2]2167  GString *buf;
[7d4fbcd]2168
[66e409c]2169  if (related) {
2170    class = owl_util_baseclass(c);
2171    if (i) {
2172      instance = owl_util_baseclass(i);
2173    }
2174  } else {
[d4927a7]2175    class = g_strdup(c);
[66e409c]2176    if (i) {
[d4927a7]2177      instance = g_strdup(i);
[66e409c]2178    }
[7a20e4c]2179  }
2180
[7d4fbcd]2181  /* name for the filter */
2182  if (!instance) {
[3472845]2183    filtname = g_strdup_printf("%sclass-%s", related ? "related-" : "", class);
[7d4fbcd]2184  } else {
[3472845]2185    filtname = g_strdup_printf("%sclass-%s-instance-%s", related ? "related-" : "", class, instance);
[7d4fbcd]2186  }
[ed2412d]2187  /* downcase it */
[28ee32b]2188  {
2189    char *temp = g_utf8_strdown(filtname, -1);
2190    if (temp) {
[ddbbcffa]2191      g_free(filtname);
[28ee32b]2192      filtname = temp;
2193    }
2194  }
[ed2412d]2195 
[7d4fbcd]2196  /* if it already exists then go with it.  This lets users override */
2197  if (owl_global_get_filter(&g, filtname)) {
[ff426f9]2198    goto done;
[7d4fbcd]2199  }
2200
2201  /* create the new filter */
[995eb4b]2202  tmpclass=owl_text_quote(class, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
[ed2412d]2203  if (instance) {
[995eb4b]2204    tmpinstance=owl_text_quote(instance, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
[ed2412d]2205  }
[a0e6082]2206
[c426bc2]2207  buf = g_string_new("");
2208  owl_string_appendf_quoted(buf,
2209                            related ? "class ^(un)*%q(\\.d)*$" : "class ^%q$",
2210                            tmpclass);
2211
[d54838d]2212  if (tmpinstance) {
[c426bc2]2213    owl_string_appendf_quoted(buf,
2214                              related ?
2215                              " and ( instance ^(un)*%q(\\.d)*$ )" :
2216                              " and instance ^%q$",
2217                              tmpinstance);
[7d4fbcd]2218  }
[ddbbcffa]2219  g_free(tmpclass);
2220  g_free(tmpinstance);
[7d4fbcd]2221
[c426bc2]2222  f = owl_filter_new_fromstring(filtname, buf->str);
2223  g_string_free(buf, true);
[c7fe23e]2224  if (f == NULL) {
2225    /* Couldn't make a filter for some reason. Return NULL. */
2226    owl_function_error("Error creating filter '%s'", filtname);
[ddbbcffa]2227    g_free(filtname);
[c7fe23e]2228    filtname = NULL;
2229    goto done;
2230  }
[7d4fbcd]2231
2232  /* add it to the global list */
2233  owl_global_add_filter(&g, f);
2234
[ff426f9]2235done:
[ddbbcffa]2236  g_free(class);
[3b8a563]2237  g_free(instance);
[ed2412d]2238  return(filtname);
[7d4fbcd]2239}
2240
[3abf28b]2241/* Create a filter for personal zephyrs to or from the specified
2242 * zephyr user.  Includes login/logout notifications for the user.
[811644f]2243 * The name of the filter will be 'user-<shortuser>'.  If a filter already
[3abf28b]2244 * exists with this name, no new filter will be created.  This allows
2245 * the configuration to override this function.  Returns the name of
2246 * the filter, which the caller must free.
2247 */
[6829afc]2248CALLER_OWN char *owl_function_zuserfilt(const char *longuser)
[d54838d]2249{
[7d4fbcd]2250  owl_filter *f;
[811644f]2251  char *argbuff, *esclonguser, *shortuser, *filtname;
[7d4fbcd]2252
2253  /* name for the filter */
[811644f]2254  shortuser = short_zuser(longuser);
[3472845]2255  filtname = g_strdup_printf("user-%s", shortuser);
[ddbbcffa]2256  g_free(shortuser);
[7d4fbcd]2257
2258  /* if it already exists then go with it.  This lets users override */
2259  if (owl_global_get_filter(&g, filtname)) {
[6cc3306]2260    return filtname;
[7d4fbcd]2261  }
2262
2263  /* create the new-internal filter */
[1d12db24]2264  esclonguser = owl_text_quote(longuser, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2265
[a5f477c]2266  argbuff=owl_string_build_quoted("( type ^zephyr$ and filter personal and "
2267      "( ( direction ^in$ and sender ^%q$ ) or ( direction ^out$ and "
2268      "recipient ^%q$ ) ) ) or ( ( class ^login$ ) and ( sender ^%q$ ) )",
2269      esclonguser, esclonguser, esclonguser);
[ddbbcffa]2270  g_free(esclonguser);
[7d4fbcd]2271
[23fddad]2272  f = owl_filter_new_fromstring(filtname, argbuff);
[ddbbcffa]2273  g_free(argbuff);
[c7fe23e]2274
2275  if (f == NULL) {
2276    /* Couldn't make a filter for some reason. Return NULL. */
2277    owl_function_error("Error creating filter '%s'", filtname);
[ddbbcffa]2278    g_free(filtname);
[c7fe23e]2279    return NULL;
2280  }
[7d4fbcd]2281
2282  /* add it to the global list */
2283  owl_global_add_filter(&g, f);
2284
[ed2412d]2285  return(filtname);
[7d4fbcd]2286}
2287
[6829afc]2288CALLER_OWN char *owl_function_typefilt(const char *type)
[d54838d]2289{
[f73e519]2290  owl_filter *f;
[1d12db24]2291  char *argbuff, *filtname, *esctype;
[f73e519]2292
2293  /* name for the filter */
[3472845]2294  filtname=g_strdup_printf("type-%s", type);
[f73e519]2295
2296  /* if it already exists then go with it.  This lets users override */
2297  if (owl_global_get_filter(&g, filtname)) {
2298    return filtname;
2299  }
2300
2301  /* create the new-internal filter */
[1d12db24]2302  esctype = owl_text_quote(type, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2303
[42115bf9]2304  argbuff = owl_string_build_quoted("type ^%q$", esctype);
[ddbbcffa]2305  g_free(esctype);
[f73e519]2306
[23fddad]2307  f = owl_filter_new_fromstring(filtname, argbuff);
[ddbbcffa]2308  g_free(argbuff);
[c7fe23e]2309
2310  if (f == NULL) {
2311    owl_function_error("Error creating filter '%s'", filtname);
[ddbbcffa]2312    g_free(filtname);
[c7fe23e]2313    return NULL;
2314  }
[f73e519]2315
2316  /* add it to the global list */
2317  owl_global_add_filter(&g, f);
2318
2319  return filtname;
2320}
2321
[7d4fbcd]2322/* If flag is 1, marks for deletion.  If flag is 0,
2323 * unmarks for deletion. */
[d54838d]2324void owl_function_delete_curview_msgs(int flag)
2325{
[9e5c9f3]2326  const owl_view *v;
[7d4fbcd]2327  int i, j;
2328
2329  v=owl_global_get_current_view(&g);
2330  j=owl_view_get_size(v);
2331  for (i=0; i<j; i++) {
2332    if (flag == 1) {
2333      owl_message_mark_delete(owl_view_get_element(v, i));
2334    } else if (flag == 0) {
2335      owl_message_unmark_delete(owl_view_get_element(v, i));
2336    }
2337  }
2338
2339  owl_function_makemsg("%i messages marked for %sdeletion", j, flag?"":"un");
2340
2341  owl_mainwin_redisplay(owl_global_get_mainwin(&g)); 
2342}
2343
[6829afc]2344static CALLER_OWN char *owl_function_smartfilter_cc(const owl_message *m)
[d427f08]2345{
[ecaec21]2346  const char *ccs;
[d222c44]2347  char *ccs_quoted;
[ecaec21]2348  char *filtname;
2349  owl_filter *f;
[d222c44]2350  GString *buf;
[ecaec21]2351
2352  ccs = owl_message_get_attribute_value(m, "zephyr_ccs");
2353
[3472845]2354  filtname = g_strdup_printf("conversation-%s", ccs);
[7865479]2355  g_strdelimit(filtname, " ", '-');
[ecaec21]2356
2357  if (owl_global_get_filter(&g, filtname)) {
2358    return filtname;
2359  }
2360
[d222c44]2361  buf = g_string_new("type ^zephyr$ and filter personal and "
2362                     "zephyr_ccs ^");
2363  ccs_quoted = owl_text_quote(ccs, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2364  owl_string_append_quoted_arg(buf, ccs_quoted);
2365  g_string_append_c(buf, '$');
[ddbbcffa]2366  g_free(ccs_quoted);
[ecaec21]2367
[d222c44]2368  f = owl_filter_new_fromstring(filtname, buf->str);
2369  g_string_free(buf, true);
[ecaec21]2370
[c7fe23e]2371  if (f == NULL) {
2372    owl_function_error("Error creating filter '%s'", filtname);
[ddbbcffa]2373    g_free(filtname);
[c7fe23e]2374    return NULL;
2375  }
2376
[ecaec21]2377  owl_global_add_filter(&g, f);
2378
2379  return filtname;
2380}
2381
[3abf28b]2382/* Create a filter based on the current message.  Returns the name of
2383 * a filter or null.  The caller must free this name.
2384 *
2385 * if the curmsg is a personal zephyr return a filter name
[e6d989f]2386 *    to the zephyr conversation with that user.
[3abf28b]2387 * If the curmsg is a zephyr class message, instance foo, recip *,
2388 *    return a filter name to the class, inst.
2389 * If the curmsg is a zephyr class message and type==0 then
2390 *    return a filter name for just the class.
2391 * If the curmsg is a zephyr class message and type==1 then
2392 *    return a filter name for the class and instance.
2393 */
[6829afc]2394CALLER_OWN char *owl_function_smartfilter(int type, int invert_related)
[d54838d]2395{
[9e5c9f3]2396  const owl_view *v;
[c08c70a]2397  const owl_message *m;
[811644f]2398  char *filtname = NULL;
2399  const char *argv[2], *zperson;
[8a5b5a1]2400  int related = owl_global_is_narrow_related(&g) ^ invert_related;
2401
[7d4fbcd]2402  v=owl_global_get_current_view(&g);
2403  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
2404
[5eeea3b]2405  if (!m || owl_view_get_size(v)==0) {
[ec6ff52]2406    owl_function_error("No message selected\n");
[4b464a4]2407    return(NULL);
[7d4fbcd]2408  }
2409
[f73e519]2410  /* very simple handling of admin messages for now */
[4b464a4]2411  if (owl_message_is_type_admin(m)) {
[3abf28b]2412    return(owl_function_typefilt("admin"));
2413  }
2414
[995eb4b]2415  /* very simple handling of loopback messages for now */
2416  if (owl_message_is_type_loopback(m)) {
2417    return(owl_function_typefilt("loopback"));
2418  }
2419
[4b464a4]2420  /* narrow personal and login messages to the sender or recip as appropriate */
[25729b2]2421  if (owl_message_is_type_zephyr(m)) {
[0ef0e8f]2422    if (owl_message_is_personal(m) || owl_message_is_loginout(m)) {
[ecaec21]2423      if (owl_message_get_attribute_value(m, "zephyr_ccs") != NULL) {
2424        return owl_function_smartfilter_cc(m);
2425      }
2426
[4b464a4]2427      if (owl_message_is_direction_in(m)) {
[811644f]2428        zperson = owl_message_get_sender(m);
[4b464a4]2429      } else {
[811644f]2430        zperson = owl_message_get_recipient(m);
[4b464a4]2431      }
[811644f]2432      filtname = owl_function_zuserfilt(zperson);
2433      return filtname;
[7d4fbcd]2434    }
2435
[25729b2]2436    /* narrow class MESSAGE, instance foo, recip * messages to class, inst */
[ce74deb]2437    if (!strcasecmp(owl_message_get_class(m), "message")) {
[66e409c]2438      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m), related);
[25729b2]2439      return(filtname);
2440    }
2441
2442    /* otherwise narrow to the class */
2443    if (type==0) {
[66e409c]2444      filtname=owl_function_classinstfilt(owl_message_get_class(m), NULL, related);
[25729b2]2445    } else if (type==1) {
[66e409c]2446      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m), related);
[25729b2]2447    }
[4b464a4]2448    return(filtname);
[7d4fbcd]2449  }
2450
[25729b2]2451  /* pass it off to perl */
[66e409c]2452  argv[0] = type ? "1" : "0";
2453  argv[1] = related ? "1" : "0";
2454  return owl_perlconfig_message_call_method(m, "smartfilter", 2, argv);
[7d4fbcd]2455}
2456
[d54838d]2457void owl_function_smartzpunt(int type)
2458{
[d36f2cb]2459  /* Starts a zpunt command based on the current class,instance pair.
2460   * If type=0, uses just class.  If type=1, uses instance as well. */
[9e5c9f3]2461  const owl_view *v;
[c08c70a]2462  const owl_message *m;
[d222c44]2463  const char *mclass, *minst;
2464  GString *buf;
[d36f2cb]2465 
2466  v=owl_global_get_current_view(&g);
2467  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
2468
[5eeea3b]2469  if (!m || owl_view_get_size(v)==0) {
[ec6ff52]2470    owl_function_error("No message selected\n");
[d36f2cb]2471    return;
2472  }
2473
2474  /* for now we skip admin messages. */
[4b464a4]2475  if (owl_message_is_type_admin(m)
[5789230]2476      || owl_message_is_loginout(m)
[4b464a4]2477      || !owl_message_is_type_zephyr(m)) {
[ec6ff52]2478    owl_function_error("smartzpunt doesn't support this message type.");
[d36f2cb]2479    return;
2480  }
2481
[cee1f25]2482  mclass = owl_message_get_class(m);
2483  minst = owl_message_get_instance(m);
[d36f2cb]2484  if (!mclass || !*mclass || *mclass==' '
2485      || (!strcasecmp(mclass, "message") && !strcasecmp(minst, "personal"))
2486      || (type && (!minst || !*minst|| *minst==' '))) {
[ec6ff52]2487    owl_function_error("smartzpunt can't safely do this for <%s,%s>",
[d36f2cb]2488                         mclass, minst);
2489  } else {
[d222c44]2490    buf = g_string_new("start-command zpunt ");
2491    owl_string_append_quoted_arg(buf, mclass);
[d36f2cb]2492    if (type) {
[d222c44]2493      g_string_append_c(buf, ' ');
2494      owl_string_append_quoted_arg(buf, minst);
[d36f2cb]2495    } else {
[d222c44]2496      g_string_append(buf, " *");
[d36f2cb]2497    }
[c809f5e]2498    owl_function_command_norv(buf->str);
[d222c44]2499    g_string_free(buf, true);
[d36f2cb]2500  }
2501}
2502
[5e0b690]2503/* Set the color of the current view's filter to
2504 * be 'color'
2505 */
[e19eb97]2506void owl_function_color_current_filter(const char *fgcolor, const char *bgcolor)
[d54838d]2507{
[e19eb97]2508  const char *name;
[7d4fbcd]2509
2510  name=owl_view_get_filtname(owl_global_get_current_view(&g));
[8fa9562]2511  owl_function_color_filter(name, fgcolor, bgcolor);
[5e0b690]2512}
2513
2514/* Set the color of the filter 'filter' to be 'color'.  If the color
2515 * name does not exist, return -1, if the filter does not exist or is
2516 * the "all" filter, return -2.  Return 0 on success
2517 */
[e19eb97]2518int owl_function_color_filter(const char *filtname, const char *fgcolor, const char *bgcolor)
[5e0b690]2519{
2520  owl_filter *f;
2521
2522  f=owl_global_get_filter(&g, filtname);
[7d4fbcd]2523  if (!f) {
[ec6ff52]2524    owl_function_error("Unknown filter");
[5e0b690]2525    return(-2);
[7d4fbcd]2526  }
2527
2528  /* don't touch the all filter */
[5e0b690]2529  if (!strcmp(filtname, "all")) {
[ec6ff52]2530    owl_function_error("You may not change the 'all' filter.");
[5e0b690]2531    return(-2);
[7d4fbcd]2532  }
2533
[601733d]2534  if (owl_util_string_to_color(fgcolor)==OWL_COLOR_INVALID) {
[8fa9562]2535    owl_function_error("No color named '%s' avilable.", fgcolor);
[5e0b690]2536    return(-1);
[12c35df]2537  }
[8fa9562]2538
2539
2540  if (bgcolor != NULL) {
[601733d]2541    if (owl_util_string_to_color(bgcolor)==OWL_COLOR_INVALID) {
[8fa9562]2542      owl_function_error("No color named '%s' avilable.", bgcolor);
2543      return(-1);
2544    }
2545    owl_filter_set_bgcolor(f, owl_util_string_to_color(bgcolor));
2546  }
2547  owl_filter_set_fgcolor(f, owl_util_string_to_color(fgcolor));
2548 
[7d4fbcd]2549  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
[5e0b690]2550  return(0);
[7d4fbcd]2551}
2552
[c79a047]2553void owl_function_show_colors(void)
[d54838d]2554{
[7d4fbcd]2555  owl_fmtext fm;
[c2c5c77]2556  int i; 
2557 
[7d4fbcd]2558  owl_fmtext_init_null(&fm);
[82b734a]2559  owl_fmtext_append_normal(&fm,"default:  ");
[8fa9562]2560  owl_fmtext_append_normal_color(&fm, "default\n", OWL_COLOR_DEFAULT, OWL_COLOR_DEFAULT);
[ca9142e]2561
[82b734a]2562  owl_fmtext_append_normal(&fm,"black:    ");
2563  owl_fmtext_append_normal_color(&fm, "black\n", OWL_COLOR_BLACK, OWL_COLOR_DEFAULT);
2564
[ca9142e]2565  owl_fmtext_append_normal(&fm,"red:      ");
[8fa9562]2566  owl_fmtext_append_normal_color(&fm, "red\n", OWL_COLOR_RED, OWL_COLOR_DEFAULT);
[ca9142e]2567
2568  owl_fmtext_append_normal(&fm,"green:    ");
[8fa9562]2569  owl_fmtext_append_normal_color(&fm, "green\n", OWL_COLOR_GREEN, OWL_COLOR_DEFAULT);
[ca9142e]2570
2571  owl_fmtext_append_normal(&fm,"yellow:   ");
[8fa9562]2572  owl_fmtext_append_normal_color(&fm, "yellow\n", OWL_COLOR_YELLOW, OWL_COLOR_DEFAULT);
[ca9142e]2573
2574  owl_fmtext_append_normal(&fm,"blue:     ");
[8fa9562]2575  owl_fmtext_append_normal_color(&fm, "blue\n", OWL_COLOR_BLUE, OWL_COLOR_DEFAULT);
[ca9142e]2576
2577  owl_fmtext_append_normal(&fm,"magenta:  ");
[8fa9562]2578  owl_fmtext_append_normal_color(&fm, "magenta\n", OWL_COLOR_MAGENTA, OWL_COLOR_DEFAULT);
[ca9142e]2579
2580  owl_fmtext_append_normal(&fm,"cyan:     ");
[8fa9562]2581  owl_fmtext_append_normal_color(&fm, "cyan\n", OWL_COLOR_CYAN, OWL_COLOR_DEFAULT);
[ca9142e]2582
2583  owl_fmtext_append_normal(&fm,"white:    ");
[8fa9562]2584  owl_fmtext_append_normal_color(&fm, "white\n", OWL_COLOR_WHITE, OWL_COLOR_DEFAULT);
[7d4fbcd]2585
[c2c5c77]2586  for(i = 8; i < COLORS; ++i) {
[3472845]2587    char* str1 = g_strdup_printf("%4i:     ",i);
2588    char* str2 = g_strdup_printf("%i\n",i);
[c2c5c77]2589    owl_fmtext_append_normal(&fm,str1);
2590    owl_fmtext_append_normal_color(&fm, str2, i, OWL_COLOR_DEFAULT);
[ddbbcffa]2591    g_free(str1);
2592     g_free(str2);
[c2c5c77]2593  }
2594 
[7d4fbcd]2595  owl_function_popless_fmtext(&fm);
[7ab0020]2596  owl_fmtext_cleanup(&fm);
[7d4fbcd]2597}
2598
[5bb6c21]2599/* add the given class, inst, recip to the punt list for filtering.
2600 *   if direction==0 then punt
2601 *   if direction==1 then unpunt
2602 */
[e19eb97]2603void owl_function_zpunt(const char *class, const char *inst, const char *recip, int direction)
[d54838d]2604{
[78f6c35]2605  GPtrArray *argv;
[bc08664]2606  char *quoted;
[7d4fbcd]2607
[78f6c35]2608  argv = g_ptr_array_new();
[5bb6c21]2609  if (!strcmp(class, "*")) {
[78f6c35]2610    g_ptr_array_add(argv, g_strdup("class"));
2611    g_ptr_array_add(argv, g_strdup(".*"));
[5bb6c21]2612  } else {
[bc08664]2613    quoted=owl_text_quote(class, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
[78f6c35]2614    g_ptr_array_add(argv, g_strdup("class"));
2615    g_ptr_array_add(argv, g_strdup_printf("^(un)*%s(\\.d)*$", quoted));
[ddbbcffa]2616    g_free(quoted);
[5bb6c21]2617  }
2618  if (!strcmp(inst, "*")) {
[78f6c35]2619    g_ptr_array_add(argv, g_strdup("and"));
2620    g_ptr_array_add(argv, g_strdup("instance"));
2621    g_ptr_array_add(argv, g_strdup(".*"));
[7d4fbcd]2622  } else {
[bc08664]2623    quoted=owl_text_quote(inst, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
[78f6c35]2624    g_ptr_array_add(argv, g_strdup("and"));
2625    g_ptr_array_add(argv, g_strdup("instance"));
2626    g_ptr_array_add(argv, g_strdup_printf("^(un)*%s(\\.d)*$", quoted));
[ddbbcffa]2627    g_free(quoted);
[7d4fbcd]2628  }
[c894c15]2629  if (!strcmp(recip, "*")) {
[78f6c35]2630    /* nothing */
[c894c15]2631  } else {
[e6c59ba]2632    if(!strcmp(recip, "%me%")) {
2633      recip = owl_zephyr_get_sender();
2634    }
[bc08664]2635    quoted=owl_text_quote(recip, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
[78f6c35]2636    g_ptr_array_add(argv, g_strdup("and"));
2637    g_ptr_array_add(argv, g_strdup("recipient"));
2638    g_ptr_array_add(argv, g_strdup_printf("^%s$", quoted));
[ddbbcffa]2639    g_free(quoted);
[5bb6c21]2640  }
[ce7b824]2641
[78f6c35]2642  owl_function_punt(argv->len, (const char *const*) argv->pdata, direction);
[3cdd6d2]2643  owl_ptr_array_free(argv, g_free);
[ce7b824]2644}
2645
[78f6c35]2646void owl_function_punt(int argc, const char *const *argv, int direction)
[ce7b824]2647{
2648  owl_filter *f;
[e6d7e4e]2649  GPtrArray *fl;
2650  int i;
[ce7b824]2651  fl=owl_global_get_puntlist(&g);
2652
2653  /* first, create the filter */
[78f6c35]2654  f = owl_filter_new("punt-filter", argc, argv);
[23fddad]2655  if (f == NULL) {
[ec6ff52]2656    owl_function_error("Error creating filter for zpunt");
[7d4fbcd]2657    return;
2658  }
2659
2660  /* Check for an identical filter */
[e6d7e4e]2661  for (i = 0; i < fl->len; i++) {
2662    if (owl_filter_equiv(f, fl->pdata[i])) {
[ce7b824]2663      owl_function_debugmsg("found an equivalent punt filter");
[7d4fbcd]2664      /* if we're punting, then just silently bow out on this duplicate */
2665      if (direction==0) {
[23fddad]2666        owl_filter_delete(f);
[7d4fbcd]2667        return;
2668      }
2669
2670      /* if we're unpunting, then remove this filter from the puntlist */
2671      if (direction==1) {
[e6d7e4e]2672        owl_filter_delete(g_ptr_array_remove_index(fl, i));
[23fddad]2673        owl_filter_delete(f);
[7d4fbcd]2674        return;
2675      }
2676    }
2677  }
2678
[697221f]2679  if (direction == 0) {
2680    owl_function_debugmsg("punting");
2681    /* If we're punting, add the filter to the global punt list */
[e6d7e4e]2682    g_ptr_array_add(fl, f);
[697221f]2683  } else if (direction == 1) {
2684    owl_function_makemsg("No matching punt filter");
2685 }
[7d4fbcd]2686}
2687
[c79a047]2688void owl_function_show_keymaps(void)
[d54838d]2689{
[ce68f23]2690  GPtrArray *l;
[7d4fbcd]2691  owl_fmtext fm;
[afa200a]2692  const owl_keymap *km;
[12bc46a]2693  const owl_keyhandler *kh;
[ce68f23]2694  int i;
[e19eb97]2695  const char *kmname;
[7d4fbcd]2696
[1aee7d9]2697  kh = owl_global_get_keyhandler(&g);
[7d4fbcd]2698  owl_fmtext_init_null(&fm);
2699  owl_fmtext_append_bold(&fm, "Keymaps:   ");
2700  owl_fmtext_append_normal(&fm, "(use 'show keymap <name>' for details)\n");
[ce68f23]2701  l = owl_keyhandler_get_keymap_names(kh);
2702  owl_fmtext_append_list(&fm, l, "\n", owl_function_keymap_summary);
[7d4fbcd]2703  owl_fmtext_append_normal(&fm, "\n");
[1aee7d9]2704
[ce68f23]2705  for (i = 0; i < l->len; i++) {
2706    kmname = l->pdata[i];
[1aee7d9]2707    km = owl_keyhandler_get_keymap(kh, kmname);
2708    owl_fmtext_append_bold(&fm, "\n\n----------------------------------------------------------------------------------------------------\n\n");
[13ebf92]2709    owl_keymap_get_details(km, &fm, 0);
[1aee7d9]2710  }
2711  owl_fmtext_append_normal(&fm, "\n");
2712 
[7d4fbcd]2713  owl_function_popless_fmtext(&fm);
[ce68f23]2714  owl_ptr_array_free(l, g_free);
[7ab0020]2715  owl_fmtext_cleanup(&fm);
[7d4fbcd]2716}
2717
[6829afc]2718CALLER_OWN char *owl_function_keymap_summary(const char *name)
[d54838d]2719{
[afa200a]2720  const owl_keymap *km
[7d4fbcd]2721    = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), name);
2722  if (km) return owl_keymap_summary(km);
2723  else return(NULL);
2724}
2725
2726/* TODO: implement for real */
[e19eb97]2727void owl_function_show_keymap(const char *name)
[d54838d]2728{
[1fd0b25]2729  owl_fmtext fm;
[afa200a]2730  const owl_keymap *km;
[7d4fbcd]2731
2732  owl_fmtext_init_null(&fm);
2733  km = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), name);
2734  if (km) {
[13ebf92]2735    owl_keymap_get_details(km, &fm, 1);
[7d4fbcd]2736  } else {
2737    owl_fmtext_append_normal(&fm, "No such keymap...\n");
2738  } 
2739  owl_function_popless_fmtext(&fm);
[7ab0020]2740  owl_fmtext_cleanup(&fm);
[7d4fbcd]2741}
2742
[e19eb97]2743void owl_function_help_for_command(const char *cmdname)
[d54838d]2744{
[1fd0b25]2745  owl_fmtext fm;
[7d4fbcd]2746
2747  owl_fmtext_init_null(&fm);
2748  owl_cmd_get_help(owl_global_get_cmddict(&g), cmdname, &fm);
2749  owl_function_popless_fmtext(&fm); 
[7ab0020]2750  owl_fmtext_cleanup(&fm);
[7d4fbcd]2751}
[1fd0b25]2752
[2ec737f]2753void owl_function_set_search(const char *string)
[d54838d]2754{
[41c9a96]2755  owl_regex re;
2756
2757  if (string && owl_regex_create_quoted(&re, string) == 0) {
2758    owl_global_set_search_re(&g, &re);
[5cbc929]2759    owl_regex_cleanup(&re);
[41c9a96]2760  } else {
2761    owl_global_set_search_re(&g, NULL);
2762  }
[2ec737f]2763}
[ab225e0]2764
[118c919]2765void owl_function_search_helper(int consider_current, int direction)
[d54838d]2766{
[1fd0b25]2767  /* move to a message that contains the string.  If direction is
2768   * OWL_DIRECTION_DOWNWARDS then search fowards, if direction is
2769   * OWL_DIRECTION_UPWARDS then search backwards.
2770   *
[118c919]2771   * If consider_current is true then it will stay on the
[9c1e61d4]2772   * current message if it contains the string.
[1fd0b25]2773   */
2774
[9e5c9f3]2775  const owl_view *v;
[1fd0b25]2776  int viewsize, i, curmsg, start;
2777  owl_message *m;
2778
2779  v=owl_global_get_current_view(&g);
2780  viewsize=owl_view_get_size(v);
2781  curmsg=owl_global_get_curmsg(&g);
2782 
2783  if (viewsize==0) {
[4fd211f]2784    owl_function_makemsg("No messages present");
[1fd0b25]2785    return;
2786  }
2787
[118c919]2788  if (consider_current) {
[1fd0b25]2789    start=curmsg;
2790  } else if (direction==OWL_DIRECTION_DOWNWARDS) {
2791    start=curmsg+1;
2792  } else {
2793    start=curmsg-1;
2794  }
2795
2796  /* bounds check */
2797  if (start>=viewsize || start<0) {
[4fd211f]2798    owl_function_makemsg("No further matches found");
[1fd0b25]2799    return;
2800  }
2801
2802  for (i=start; i<viewsize && i>=0;) {
2803    m=owl_view_get_element(v, i);
[41c9a96]2804    if (owl_message_search(m, owl_global_get_search_re(&g))) {
[1fd0b25]2805      owl_global_set_curmsg(&g, i);
2806      owl_function_calculate_topmsg(direction);
2807      owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2808      if (direction==OWL_DIRECTION_DOWNWARDS) {
2809        owl_global_set_direction_downwards(&g);
2810      } else {
2811        owl_global_set_direction_upwards(&g);
2812      }
2813      return;
2814    }
2815    if (direction==OWL_DIRECTION_DOWNWARDS) {
2816      i++;
2817    } else {
2818      i--;
2819    }
[47128d9]2820    if (owl_global_take_interrupt(&g)) {
[bf66f4e]2821      owl_function_makemsg("Search interrupted!");
2822      owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2823      return;
2824    }
[1fd0b25]2825  }
[37c27cf]2826  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
[4fd211f]2827  owl_function_makemsg("No matches found");
[1fd0b25]2828}
2829
2830/* strips formatting from ztext and returns the unformatted text.
2831 * caller is responsible for freeing. */
[6829afc]2832CALLER_OWN char *owl_function_ztext_stylestrip(const char *zt)
[d54838d]2833{
[1fd0b25]2834  owl_fmtext fm;
2835  char *plaintext;
2836
2837  owl_fmtext_init_null(&fm);
2838  owl_fmtext_append_ztext(&fm, zt);
2839  plaintext = owl_fmtext_print_plain(&fm);
[7ab0020]2840  owl_fmtext_cleanup(&fm);
[1fd0b25]2841  return(plaintext);
2842}
[42abb10]2843
[5a95b69]2844/* Popup a buddylisting.  If filename is NULL use the default .anyone */
[4fd3c04]2845void owl_function_buddylist(int zephyr, const char *filename)
[d54838d]2846{
[4fd3c04]2847  int i;
[8daf504]2848  int interrupted = 0;
[42abb10]2849  owl_fmtext fm;
[09489b89]2850#ifdef HAVE_LIBZEPHYR
[fea7992]2851  int x;
[ecffae6]2852  GPtrArray *anyone;
[e19eb97]2853  const char *user;
[65b2173]2854  char *tmp;
[09489b89]2855  ZLocations_t location[200];
2856  int numlocs, ret;
2857#endif
[42abb10]2858
[aa5f725]2859  owl_fmtext_init_null(&fm);
[42abb10]2860
[09489b89]2861#ifdef HAVE_LIBZEPHYR
[aa5f725]2862  if (zephyr) {
[bfbf590]2863    if(!owl_global_is_havezephyr(&g)) {
2864      owl_function_error("Zephyr currently not available.");
[a0a5179]2865    } else {
[bfbf590]2866      owl_fmtext_append_bold(&fm, "Zephyr users logged in:\n");
[ecffae6]2867