source: functions.c @ 9e596f5

Last change on this file since 9e596f5 was 9e596f5, checked in by Anders Kaseorg <andersk@mit.edu>, 6 years ago
Allow changing the fg + bg colors of an existing filter simutaneously 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;
[9e596f5]1982  int i = 2;
1983  int fgcolor = OWL_COLOR_DEFAULT;
1984  bool set_fgcolor = false;
1985  int bgcolor = OWL_COLOR_DEFAULT;
1986  bool set_bgcolor = false;
[7d4fbcd]1987
1988  if (argc < 2) {
[ec6ff52]1989    owl_function_error("Wrong number of arguments to filter command");
[287c634]1990    return false;
[7d4fbcd]1991  }
1992
[3895e23]1993  owl_function_debugmsg("owl_function_create_filter: starting to create filter named %s", argv[1]);
1994
[7d4fbcd]1995  v=owl_global_get_current_view(&g);
1996
1997  /* don't touch the all filter */
1998  if (!strcmp(argv[1], "all")) {
[ec6ff52]1999    owl_function_error("You may not change the 'all' filter.");
[287c634]2000    return false;
[7d4fbcd]2001  }
2002
[9e596f5]2003  /* set the color */
2004  while (i + 2 <= argc && (!strcmp(argv[i], "-c") ||
2005                           !strcmp(argv[i], "-b"))) {
2006    int color = owl_util_string_to_color(argv[i + 1]);
2007    if (color == OWL_COLOR_INVALID) {
2008      owl_function_error("The color '%s' is not available.", argv[i + 1]);
2009    } else if (argv[i][1] == 'c') {
2010      fgcolor = color;
2011      set_fgcolor = true;
2012    } else {
2013      bgcolor = color;
2014      set_bgcolor = true;
[12c35df]2015    }
[9e596f5]2016    i += 2;
[8fa9562]2017  }
[9e596f5]2018
2019  if (i > 2 && i == argc) {
[8fa9562]2020    f=owl_global_get_filter(&g, argv[1]);
2021    if (!f) {
2022      owl_function_error("The filter '%s' does not exist.", argv[1]);
[287c634]2023      return false;
[8fa9562]2024    }
[9e596f5]2025    if (!set_fgcolor && !set_bgcolor)
[287c634]2026      return false;
[9e596f5]2027    if (set_fgcolor)
2028      owl_filter_set_fgcolor(f, fgcolor);
2029    if (set_bgcolor)
2030      owl_filter_set_bgcolor(f, bgcolor);
[7d4fbcd]2031    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
[287c634]2032    return true;
[7d4fbcd]2033  }
2034
2035  /* create the filter and check for errors */
[9e596f5]2036  f = owl_filter_new_colored(argv[1], argc - i, argv + i, fgcolor, bgcolor);
[23fddad]2037  if (f == NULL) {
[06470d7]2038    owl_function_error("Invalid filter: %s", argv[1]);
[287c634]2039    return false;
[7d4fbcd]2040  }
2041
2042  /* if the named filter is in use by the current view, remember it */
2043  if (!strcmp(owl_view_get_filtname(v), argv[1])) {
2044    inuse=1;
2045  }
2046
2047  /* if the named filter already exists, nuke it */
2048  if (owl_global_get_filter(&g, argv[1])) {
2049    owl_global_remove_filter(&g, argv[1]);
2050  }
2051
2052  /* add the filter */
2053  owl_global_add_filter(&g, f);
2054
2055  /* if it was in use by the current view then update */
2056  if (inuse) {
[3895e23]2057    owl_function_change_currentview_filter(argv[1]);
[7d4fbcd]2058  }
2059  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
[287c634]2060  return true;
[7d4fbcd]2061}
2062
[3895e23]2063/* If 'filtername' does not start with 'not-' create a filter named
2064 * 'not-<filtername>' defined as "not filter <filtername>".  If the
2065 * filter 'not-<filtername>' already exists, do not overwrite it.  If
2066 * 'filtername' begins with 'not-' and a filter 'filtername' already
2067 * exists, then do nothing.  If the filter 'filtername' does not
2068 * exist, create it and define it as 'not filter <filtername>'
2069 *
2070 * Returns the name of the negated filter, which the caller must free.
2071 */
[6829afc]2072CALLER_OWN char *owl_function_create_negative_filter(const char *filtername)
[3895e23]2073{
2074  char *newname;
[4542047]2075  const owl_filter *tmpfilt;
[e19eb97]2076  const char *argv[5];
[3895e23]2077
2078  owl_function_debugmsg("owl_function_create_negative_filter");
2079 
2080  if (!strncmp(filtername, "not-", 4)) {
[d4927a7]2081    newname=g_strdup(filtername+4);
[3895e23]2082  } else {
[3472845]2083    newname=g_strdup_printf("not-%s", filtername);
[3895e23]2084  }
2085
2086  tmpfilt=owl_global_get_filter(&g, newname);
2087  if (!tmpfilt) {
2088    argv[0]="filter"; /* anything is fine here */
2089    argv[1]=newname;
2090    argv[2]="not";
2091    argv[3]="filter";
2092    argv[4]=filtername;
2093    owl_function_create_filter(5, argv);
2094  }
2095
2096  owl_function_debugmsg("owl_function_create_negative_filter: returning with %s", newname);
2097  return(newname);
2098}
2099
[c79a047]2100void owl_function_show_filters(void)
[d54838d]2101{
[4542047]2102  const owl_filter *f;
[129e609]2103  GList *fl;
[7d4fbcd]2104  owl_fmtext fm;
2105
2106  owl_fmtext_init_null(&fm);
2107
2108  owl_fmtext_append_bold(&fm, "Filters:\n");
2109
[129e609]2110  for (fl = g.filterlist; fl; fl = g_list_next(fl)) {
2111    f = fl->data;
[7d4fbcd]2112    owl_fmtext_append_normal(&fm, "   ");
[7b4f3be]2113    owl_fmtext_append_normal_color(&fm, owl_filter_get_name(f),
2114                                   owl_filter_get_fgcolor(f),
2115                                   owl_filter_get_bgcolor(f));
[7d4fbcd]2116    owl_fmtext_append_normal(&fm, "\n");
2117  }
2118  owl_function_popless_fmtext(&fm);
[7ab0020]2119  owl_fmtext_cleanup(&fm);
[7d4fbcd]2120}
2121
[e19eb97]2122void owl_function_show_filter(const char *name)
[d54838d]2123{
[4542047]2124  const owl_filter *f;
[cdc6ff1]2125  char *buff, *tmp;
[7d4fbcd]2126
2127  f=owl_global_get_filter(&g, name);
2128  if (!f) {
[ec6ff52]2129    owl_function_error("There is no filter named %s", name);
[7d4fbcd]2130    return;
2131  }
[cdc6ff1]2132  tmp = owl_filter_print(f);
[3472845]2133  buff = g_strdup_printf("%s: %s", owl_filter_get_name(f), tmp);
[7d4fbcd]2134  owl_function_popless_text(buff);
[ddbbcffa]2135  g_free(buff);
2136  g_free(tmp);
[7d4fbcd]2137}
2138
[c79a047]2139void owl_function_show_zpunts(void)
[d54838d]2140{
[4542047]2141  const owl_filter *f;
[e6d7e4e]2142  const GPtrArray *fl;
[0504f63]2143  char *tmp;
[7d4fbcd]2144  owl_fmtext fm;
[e6d7e4e]2145  int i;
[7d4fbcd]2146
2147  owl_fmtext_init_null(&fm);
2148
2149  fl=owl_global_get_puntlist(&g);
2150  owl_fmtext_append_bold(&fm, "Active zpunt filters:\n");
2151
[e6d7e4e]2152  for (i = 0; i < fl->len; i++) {
2153    f = fl->pdata[i];
[e3a75ed]2154    owl_fmtext_appendf_normal(&fm, "[% 2d] ", i+1);
[0504f63]2155    tmp = owl_filter_print(f);
2156    owl_fmtext_append_normal(&fm, tmp);
[ddbbcffa]2157    g_free(tmp);
[7d4fbcd]2158  }
2159  owl_function_popless_fmtext(&fm);
[7ab0020]2160  owl_fmtext_cleanup(&fm);
[7d4fbcd]2161}
2162
[3abf28b]2163/* Create a filter for a class, instance if one doesn't exist.  If
2164 * instance is NULL then catch all messgaes in the class.  Returns the
[c7fe23e]2165 * name of the filter or null.  The caller must free this name.
[66e409c]2166 * If 'related' is nonzero, encompass unclasses and .d classes as well.
[3abf28b]2167 */
[6829afc]2168CALLER_OWN char *owl_function_classinstfilt(const char *c, const char *i, int related) 
[d54838d]2169{
[7d4fbcd]2170  owl_filter *f;
[c426bc2]2171  char *filtname;
[d54838d]2172  char *tmpclass, *tmpinstance = NULL;
[7a20e4c]2173  char *class, *instance = NULL;
[c426bc2]2174  GString *buf;
[7d4fbcd]2175
[66e409c]2176  if (related) {
2177    class = owl_util_baseclass(c);
2178    if (i) {
2179      instance = owl_util_baseclass(i);
2180    }
2181  } else {
[d4927a7]2182    class = g_strdup(c);
[66e409c]2183    if (i) {
[d4927a7]2184      instance = g_strdup(i);
[66e409c]2185    }
[7a20e4c]2186  }
2187
[7d4fbcd]2188  /* name for the filter */
2189  if (!instance) {
[3472845]2190    filtname = g_strdup_printf("%sclass-%s", related ? "related-" : "", class);
[7d4fbcd]2191  } else {
[3472845]2192    filtname = g_strdup_printf("%sclass-%s-instance-%s", related ? "related-" : "", class, instance);
[7d4fbcd]2193  }
[ed2412d]2194  /* downcase it */
[28ee32b]2195  {
2196    char *temp = g_utf8_strdown(filtname, -1);
2197    if (temp) {
[ddbbcffa]2198      g_free(filtname);
[28ee32b]2199      filtname = temp;
2200    }
2201  }
[ed2412d]2202 
[7d4fbcd]2203  /* if it already exists then go with it.  This lets users override */
2204  if (owl_global_get_filter(&g, filtname)) {
[ff426f9]2205    goto done;
[7d4fbcd]2206  }
2207
2208  /* create the new filter */
[995eb4b]2209  tmpclass=owl_text_quote(class, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
[ed2412d]2210  if (instance) {
[995eb4b]2211    tmpinstance=owl_text_quote(instance, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
[ed2412d]2212  }
[a0e6082]2213
[c426bc2]2214  buf = g_string_new("");
2215  owl_string_appendf_quoted(buf,
2216                            related ? "class ^(un)*%q(\\.d)*$" : "class ^%q$",
2217                            tmpclass);
2218
[d54838d]2219  if (tmpinstance) {
[c426bc2]2220    owl_string_appendf_quoted(buf,
2221                              related ?
2222                              " and ( instance ^(un)*%q(\\.d)*$ )" :
2223                              " and instance ^%q$",
2224                              tmpinstance);
[7d4fbcd]2225  }
[ddbbcffa]2226  g_free(tmpclass);
2227  g_free(tmpinstance);
[7d4fbcd]2228
[c426bc2]2229  f = owl_filter_new_fromstring(filtname, buf->str);
2230  g_string_free(buf, true);
[c7fe23e]2231  if (f == NULL) {
2232    /* Couldn't make a filter for some reason. Return NULL. */
2233    owl_function_error("Error creating filter '%s'", filtname);
[ddbbcffa]2234    g_free(filtname);
[c7fe23e]2235    filtname = NULL;
2236    goto done;
2237  }
[7d4fbcd]2238
2239  /* add it to the global list */
2240  owl_global_add_filter(&g, f);
2241
[ff426f9]2242done:
[ddbbcffa]2243  g_free(class);
[3b8a563]2244  g_free(instance);
[ed2412d]2245  return(filtname);
[7d4fbcd]2246}
2247
[3abf28b]2248/* Create a filter for personal zephyrs to or from the specified
2249 * zephyr user.  Includes login/logout notifications for the user.
[811644f]2250 * The name of the filter will be 'user-<shortuser>'.  If a filter already
[3abf28b]2251 * exists with this name, no new filter will be created.  This allows
2252 * the configuration to override this function.  Returns the name of
2253 * the filter, which the caller must free.
2254 */
[6829afc]2255CALLER_OWN char *owl_function_zuserfilt(const char *longuser)
[d54838d]2256{
[7d4fbcd]2257  owl_filter *f;
[811644f]2258  char *argbuff, *esclonguser, *shortuser, *filtname;
[7d4fbcd]2259
2260  /* name for the filter */
[811644f]2261  shortuser = short_zuser(longuser);
[3472845]2262  filtname = g_strdup_printf("user-%s", shortuser);
[ddbbcffa]2263  g_free(shortuser);
[7d4fbcd]2264
2265  /* if it already exists then go with it.  This lets users override */
2266  if (owl_global_get_filter(&g, filtname)) {
[6cc3306]2267    return filtname;
[7d4fbcd]2268  }
2269
2270  /* create the new-internal filter */
[1d12db24]2271  esclonguser = owl_text_quote(longuser, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2272
[a5f477c]2273  argbuff=owl_string_build_quoted("( type ^zephyr$ and filter personal and "
2274      "( ( direction ^in$ and sender ^%q$ ) or ( direction ^out$ and "
2275      "recipient ^%q$ ) ) ) or ( ( class ^login$ ) and ( sender ^%q$ ) )",
2276      esclonguser, esclonguser, esclonguser);
[ddbbcffa]2277  g_free(esclonguser);
[7d4fbcd]2278
[23fddad]2279  f = owl_filter_new_fromstring(filtname, argbuff);
[ddbbcffa]2280  g_free(argbuff);
[c7fe23e]2281
2282  if (f == NULL) {
2283    /* Couldn't make a filter for some reason. Return NULL. */
2284    owl_function_error("Error creating filter '%s'", filtname);
[ddbbcffa]2285    g_free(filtname);
[c7fe23e]2286    return NULL;
2287  }
[7d4fbcd]2288
2289  /* add it to the global list */
2290  owl_global_add_filter(&g, f);
2291
[ed2412d]2292  return(filtname);
[7d4fbcd]2293}
2294
[6829afc]2295CALLER_OWN char *owl_function_typefilt(const char *type)
[d54838d]2296{
[f73e519]2297  owl_filter *f;
[1d12db24]2298  char *argbuff, *filtname, *esctype;
[f73e519]2299
2300  /* name for the filter */
[3472845]2301  filtname=g_strdup_printf("type-%s", type);
[f73e519]2302
2303  /* if it already exists then go with it.  This lets users override */
2304  if (owl_global_get_filter(&g, filtname)) {
2305    return filtname;
2306  }
2307
2308  /* create the new-internal filter */
[1d12db24]2309  esctype = owl_text_quote(type, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2310
[42115bf9]2311  argbuff = owl_string_build_quoted("type ^%q$", esctype);
[ddbbcffa]2312  g_free(esctype);
[f73e519]2313
[23fddad]2314  f = owl_filter_new_fromstring(filtname, argbuff);
[ddbbcffa]2315  g_free(argbuff);
[c7fe23e]2316
2317  if (f == NULL) {
2318    owl_function_error("Error creating filter '%s'", filtname);
[ddbbcffa]2319    g_free(filtname);
[c7fe23e]2320    return NULL;
2321  }
[f73e519]2322
2323  /* add it to the global list */
2324  owl_global_add_filter(&g, f);
2325
2326  return filtname;
2327}
2328
[7d4fbcd]2329/* If flag is 1, marks for deletion.  If flag is 0,
2330 * unmarks for deletion. */
[d54838d]2331void owl_function_delete_curview_msgs(int flag)
2332{
[9e5c9f3]2333  const owl_view *v;
[7d4fbcd]2334  int i, j;
2335
2336  v=owl_global_get_current_view(&g);
2337  j=owl_view_get_size(v);
2338  for (i=0; i<j; i++) {
2339    if (flag == 1) {
2340      owl_message_mark_delete(owl_view_get_element(v, i));
2341    } else if (flag == 0) {
2342      owl_message_unmark_delete(owl_view_get_element(v, i));
2343    }
2344  }
2345
2346  owl_function_makemsg("%i messages marked for %sdeletion", j, flag?"":"un");
2347
2348  owl_mainwin_redisplay(owl_global_get_mainwin(&g)); 
2349}
2350
[6829afc]2351static CALLER_OWN char *owl_function_smartfilter_cc(const owl_message *m)
[d427f08]2352{
[ecaec21]2353  const char *ccs;
[d222c44]2354  char *ccs_quoted;
[ecaec21]2355  char *filtname;
2356  owl_filter *f;
[d222c44]2357  GString *buf;
[ecaec21]2358
2359  ccs = owl_message_get_attribute_value(m, "zephyr_ccs");
2360
[3472845]2361  filtname = g_strdup_printf("conversation-%s", ccs);
[7865479]2362  g_strdelimit(filtname, " ", '-');
[ecaec21]2363
2364  if (owl_global_get_filter(&g, filtname)) {
2365    return filtname;
2366  }
2367
[d222c44]2368  buf = g_string_new("type ^zephyr$ and filter personal and "
2369                     "zephyr_ccs ^");
2370  ccs_quoted = owl_text_quote(ccs, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2371  owl_string_append_quoted_arg(buf, ccs_quoted);
2372  g_string_append_c(buf, '$');
[ddbbcffa]2373  g_free(ccs_quoted);
[ecaec21]2374
[d222c44]2375  f = owl_filter_new_fromstring(filtname, buf->str);
2376  g_string_free(buf, true);
[ecaec21]2377
[c7fe23e]2378  if (f == NULL) {
2379    owl_function_error("Error creating filter '%s'", filtname);
[ddbbcffa]2380    g_free(filtname);
[c7fe23e]2381    return NULL;
2382  }
2383
[ecaec21]2384  owl_global_add_filter(&g, f);
2385
2386  return filtname;
2387}
2388
[3abf28b]2389/* Create a filter based on the current message.  Returns the name of
2390 * a filter or null.  The caller must free this name.
2391 *
2392 * if the curmsg is a personal zephyr return a filter name
[e6d989f]2393 *    to the zephyr conversation with that user.
[3abf28b]2394 * If the curmsg is a zephyr class message, instance foo, recip *,
2395 *    return a filter name to the class, inst.
2396 * If the curmsg is a zephyr class message and type==0 then
2397 *    return a filter name for just the class.
2398 * If the curmsg is a zephyr class message and type==1 then
2399 *    return a filter name for the class and instance.
2400 */
[6829afc]2401CALLER_OWN char *owl_function_smartfilter(int type, int invert_related)
[d54838d]2402{
[9e5c9f3]2403  const owl_view *v;
[c08c70a]2404  const owl_message *m;
[811644f]2405  char *filtname = NULL;
2406  const char *argv[2], *zperson;
[8a5b5a1]2407  int related = owl_global_is_narrow_related(&g) ^ invert_related;
2408
[7d4fbcd]2409  v=owl_global_get_current_view(&g);
2410  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
2411
[5eeea3b]2412  if (!m || owl_view_get_size(v)==0) {
[ec6ff52]2413    owl_function_error("No message selected\n");
[4b464a4]2414    return(NULL);
[7d4fbcd]2415  }
2416
[f73e519]2417  /* very simple handling of admin messages for now */
[4b464a4]2418  if (owl_message_is_type_admin(m)) {
[3abf28b]2419    return(owl_function_typefilt("admin"));
2420  }
2421
[995eb4b]2422  /* very simple handling of loopback messages for now */
2423  if (owl_message_is_type_loopback(m)) {
2424    return(owl_function_typefilt("loopback"));
2425  }
2426
[4b464a4]2427  /* narrow personal and login messages to the sender or recip as appropriate */
[25729b2]2428  if (owl_message_is_type_zephyr(m)) {
[0ef0e8f]2429    if (owl_message_is_personal(m) || owl_message_is_loginout(m)) {
[ecaec21]2430      if (owl_message_get_attribute_value(m, "zephyr_ccs") != NULL) {
2431        return owl_function_smartfilter_cc(m);
2432      }
2433
[4b464a4]2434      if (owl_message_is_direction_in(m)) {
[811644f]2435        zperson = owl_message_get_sender(m);
[4b464a4]2436      } else {
[811644f]2437        zperson = owl_message_get_recipient(m);
[4b464a4]2438      }
[811644f]2439      filtname = owl_function_zuserfilt(zperson);
2440      return filtname;
[7d4fbcd]2441    }
2442
[25729b2]2443    /* narrow class MESSAGE, instance foo, recip * messages to class, inst */
[ce74deb]2444    if (!strcasecmp(owl_message_get_class(m), "message")) {
[66e409c]2445      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m), related);
[25729b2]2446      return(filtname);
2447    }
2448
2449    /* otherwise narrow to the class */
2450    if (type==0) {
[66e409c]2451      filtname=owl_function_classinstfilt(owl_message_get_class(m), NULL, related);
[25729b2]2452    } else if (type==1) {
[66e409c]2453      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m), related);
[25729b2]2454    }
[4b464a4]2455    return(filtname);
[7d4fbcd]2456  }
2457
[25729b2]2458  /* pass it off to perl */
[66e409c]2459  argv[0] = type ? "1" : "0";
2460  argv[1] = related ? "1" : "0";
2461  return owl_perlconfig_message_call_method(m, "smartfilter", 2, argv);
[7d4fbcd]2462}
2463
[d54838d]2464void owl_function_smartzpunt(int type)
2465{
[d36f2cb]2466  /* Starts a zpunt command based on the current class,instance pair.
2467   * If type=0, uses just class.  If type=1, uses instance as well. */
[9e5c9f3]2468  const owl_view *v;
[c08c70a]2469  const owl_message *m;
[d222c44]2470  const char *mclass, *minst;
2471  GString *buf;
[d36f2cb]2472 
2473  v=owl_global_get_current_view(&g);
2474  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
2475
[5eeea3b]2476  if (!m || owl_view_get_size(v)==0) {
[ec6ff52]2477    owl_function_error("No message selected\n");
[d36f2cb]2478    return;
2479  }
2480
2481  /* for now we skip admin messages. */
[4b464a4]2482  if (owl_message_is_type_admin(m)
[5789230]2483      || owl_message_is_loginout(m)
[4b464a4]2484      || !owl_message_is_type_zephyr(m)) {
[ec6ff52]2485    owl_function_error("smartzpunt doesn't support this message type.");
[d36f2cb]2486    return;
2487  }
2488
[cee1f25]2489  mclass = owl_message_get_class(m);
2490  minst = owl_message_get_instance(m);
[d36f2cb]2491  if (!mclass || !*mclass || *mclass==' '
2492      || (!strcasecmp(mclass, "message") && !strcasecmp(minst, "personal"))
2493      || (type && (!minst || !*minst|| *minst==' '))) {
[ec6ff52]2494    owl_function_error("smartzpunt can't safely do this for <%s,%s>",
[d36f2cb]2495                         mclass, minst);
2496  } else {
[d222c44]2497    buf = g_string_new("start-command zpunt ");
2498    owl_string_append_quoted_arg(buf, mclass);
[d36f2cb]2499    if (type) {
[d222c44]2500      g_string_append_c(buf, ' ');
2501      owl_string_append_quoted_arg(buf, minst);
[d36f2cb]2502    } else {
[d222c44]2503      g_string_append(buf, " *");
[d36f2cb]2504    }
[c809f5e]2505    owl_function_command_norv(buf->str);
[d222c44]2506    g_string_free(buf, true);
[d36f2cb]2507  }
2508}
2509
[5e0b690]2510/* Set the color of the current view's filter to
2511 * be 'color'
2512 */
[e19eb97]2513void owl_function_color_current_filter(const char *fgcolor, const char *bgcolor)
[d54838d]2514{
[e19eb97]2515  const char *name;
[7d4fbcd]2516
2517  name=owl_view_get_filtname(owl_global_get_current_view(&g));
[8fa9562]2518  owl_function_color_filter(name, fgcolor, bgcolor);
[5e0b690]2519}
2520
2521/* Set the color of the filter 'filter' to be 'color'.  If the color
2522 * name does not exist, return -1, if the filter does not exist or is
2523 * the "all" filter, return -2.  Return 0 on success
2524 */
[e19eb97]2525int owl_function_color_filter(const char *filtname, const char *fgcolor, const char *bgcolor)
[5e0b690]2526{
2527  owl_filter *f;
2528
2529  f=owl_global_get_filter(&g, filtname);
[7d4fbcd]2530  if (!f) {
[ec6ff52]2531    owl_function_error("Unknown filter");
[5e0b690]2532    return(-2);
[7d4fbcd]2533  }
2534
2535  /* don't touch the all filter */
[5e0b690]2536  if (!strcmp(filtname, "all")) {
[ec6ff52]2537    owl_function_error("You may not change the 'all' filter.");
[5e0b690]2538    return(-2);
[7d4fbcd]2539  }
2540
[601733d]2541  if (owl_util_string_to_color(fgcolor)==OWL_COLOR_INVALID) {
[8fa9562]2542    owl_function_error("No color named '%s' avilable.", fgcolor);
[5e0b690]2543    return(-1);
[12c35df]2544  }
[8fa9562]2545
2546
2547  if (bgcolor != NULL) {
[601733d]2548    if (owl_util_string_to_color(bgcolor)==OWL_COLOR_INVALID) {
[8fa9562]2549      owl_function_error("No color named '%s' avilable.", bgcolor);
2550      return(-1);
2551    }
2552    owl_filter_set_bgcolor(f, owl_util_string_to_color(bgcolor));
2553  }
2554  owl_filter_set_fgcolor(f, owl_util_string_to_color(fgcolor));
2555 
[7d4fbcd]2556  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
[5e0b690]2557  return(0);
[7d4fbcd]2558}
2559
[c79a047]2560void owl_function_show_colors(void)
[d54838d]2561{
[7d4fbcd]2562  owl_fmtext fm;
[c2c5c77]2563  int i; 
2564 
[7d4fbcd]2565  owl_fmtext_init_null(&fm);
[82b734a]2566  owl_fmtext_append_normal(&fm,"default:  ");
[8fa9562]2567  owl_fmtext_append_normal_color(&fm, "default\n", OWL_COLOR_DEFAULT, OWL_COLOR_DEFAULT);
[ca9142e]2568
[82b734a]2569  owl_fmtext_append_normal(&fm,"black:    ");
2570  owl_fmtext_append_normal_color(&fm, "black\n", OWL_COLOR_BLACK, OWL_COLOR_DEFAULT);
2571
[ca9142e]2572  owl_fmtext_append_normal(&fm,"red:      ");
[8fa9562]2573  owl_fmtext_append_normal_color(&fm, "red\n", OWL_COLOR_RED, OWL_COLOR_DEFAULT);
[ca9142e]2574
2575  owl_fmtext_append_normal(&fm,"green:    ");
[8fa9562]2576  owl_fmtext_append_normal_color(&fm, "green\n", OWL_COLOR_GREEN, OWL_COLOR_DEFAULT);
[ca9142e]2577
2578  owl_fmtext_append_normal(&fm,"yellow:   ");
[8fa9562]2579  owl_fmtext_append_normal_color(&fm, "yellow\n", OWL_COLOR_YELLOW, OWL_COLOR_DEFAULT);
[ca9142e]2580
2581  owl_fmtext_append_normal(&fm,"blue:     ");
[8fa9562]2582  owl_fmtext_append_normal_color(&fm, "blue\n", OWL_COLOR_BLUE, OWL_COLOR_DEFAULT);
[ca9142e]2583
2584  owl_fmtext_append_normal(&fm,"magenta:  ");
[8fa9562]2585  owl_fmtext_append_normal_color(&fm, "magenta\n", OWL_COLOR_MAGENTA, OWL_COLOR_DEFAULT);
[ca9142e]2586
2587  owl_fmtext_append_normal(&fm,"cyan:     ");
[8fa9562]2588  owl_fmtext_append_normal_color(&fm, "cyan\n", OWL_COLOR_CYAN, OWL_COLOR_DEFAULT);
[ca9142e]2589
2590  owl_fmtext_append_normal(&fm,"white:    ");
[8fa9562]2591  owl_fmtext_append_normal_color(&fm, "white\n", OWL_COLOR_WHITE, OWL_COLOR_DEFAULT);
[7d4fbcd]2592
[c2c5c77]2593  for(i = 8; i < COLORS; ++i) {
[3472845]2594    char* str1 = g_strdup_printf("%4i:     ",i);
2595    char* str2 = g_strdup_printf("%i\n",i);
[c2c5c77]2596    owl_fmtext_append_normal(&fm,str1);
2597    owl_fmtext_append_normal_color(&fm, str2, i, OWL_COLOR_DEFAULT);
[ddbbcffa]2598    g_free(str1);
2599     g_free(str2);
[c2c5c77]2600  }
2601 
[7d4fbcd]2602  owl_function_popless_fmtext(&fm);
[7ab0020]2603  owl_fmtext_cleanup(&fm);
[7d4fbcd]2604}
2605
[5bb6c21]2606/* add the given class, inst, recip to the punt list for filtering.
2607 *   if direction==0 then punt
2608 *   if direction==1 then unpunt
2609 */
[e19eb97]2610void owl_function_zpunt(const char *class, const char *inst, const char *recip, int direction)
[d54838d]2611{
[78f6c35]2612  GPtrArray *argv;
[bc08664]2613  char *quoted;
[7d4fbcd]2614
[78f6c35]2615  argv = g_ptr_array_new();
[5bb6c21]2616  if (!strcmp(class, "*")) {
[78f6c35]2617    g_ptr_array_add(argv, g_strdup("class"));
2618    g_ptr_array_add(argv, g_strdup(".*"));
[5bb6c21]2619  } else {
[bc08664]2620    quoted=owl_text_quote(class, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
[78f6c35]2621    g_ptr_array_add(argv, g_strdup("class"));
2622    g_ptr_array_add(argv, g_strdup_printf("^(un)*%s(\\.d)*$", quoted));
[ddbbcffa]2623    g_free(quoted);
[5bb6c21]2624  }
2625  if (!strcmp(inst, "*")) {
[78f6c35]2626    g_ptr_array_add(argv, g_strdup("and"));
2627    g_ptr_array_add(argv, g_strdup("instance"));
2628    g_ptr_array_add(argv, g_strdup(".*"));
[7d4fbcd]2629  } else {
[bc08664]2630    quoted=owl_text_quote(inst, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
[78f6c35]2631    g_ptr_array_add(argv, g_strdup("and"));
2632    g_ptr_array_add(argv, g_strdup("instance"));
2633    g_ptr_array_add(argv, g_strdup_printf("^(un)*%s(\\.d)*$", quoted));
[ddbbcffa]2634    g_free(quoted);
[7d4fbcd]2635  }
[c894c15]2636  if (!strcmp(recip, "*")) {
[78f6c35]2637    /* nothing */
[c894c15]2638  } else {
[e6c59ba]2639    if(!strcmp(recip, "%me%")) {
2640      recip = owl_zephyr_get_sender();
2641    }
[bc08664]2642    quoted=owl_text_quote(recip, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
[78f6c35]2643    g_ptr_array_add(argv, g_strdup("and"));
2644    g_ptr_array_add(argv, g_strdup("recipient"));
2645    g_ptr_array_add(argv, g_strdup_printf("^%s$", quoted));
[ddbbcffa]2646    g_free(quoted);
[5bb6c21]2647  }
[ce7b824]2648
[78f6c35]2649  owl_function_punt(argv->len, (const char *const*) argv->pdata, direction);
[3cdd6d2]2650  owl_ptr_array_free(argv, g_free);
[ce7b824]2651}
2652
[78f6c35]2653void owl_function_punt(int argc, const char *const *argv, int direction)
[ce7b824]2654{
2655  owl_filter *f;
[e6d7e4e]2656  GPtrArray *fl;
2657  int i;
[ce7b824]2658  fl=owl_global_get_puntlist(&g);
2659
2660  /* first, create the filter */
[78f6c35]2661  f = owl_filter_new("punt-filter", argc, argv);
[23fddad]2662  if (f == NULL) {
[ec6ff52]2663    owl_function_error("Error creating filter for zpunt");
[7d4fbcd]2664    return;
2665  }
2666
2667  /* Check for an identical filter */
[e6d7e4e]2668  for (i = 0; i < fl->len; i++) {
2669    if (owl_filter_equiv(f, fl->pdata[i])) {
[ce7b824]2670      owl_function_debugmsg("found an equivalent punt filter");
[7d4fbcd]2671      /* if we're punting, then just silently bow out on this duplicate */
2672      if (direction==0) {
[23fddad]2673        owl_filter_delete(f);
[7d4fbcd]2674        return;
2675      }
2676
2677      /* if we're unpunting, then remove this filter from the puntlist */
2678      if (direction==1) {
[e6d7e4e]2679        owl_filter_delete(g_ptr_array_remove_index(fl, i));
[23fddad]2680        owl_filter_delete(f);
[7d4fbcd]2681        return;
2682      }
2683    }
2684  }
2685
[697221f]2686  if (direction == 0) {
2687    owl_function_debugmsg("punting");
2688    /* If we're punting, add the filter to the global punt list */
[e6d7e4e]2689    g_ptr_array_add(fl, f);
[697221f]2690  } else if (direction == 1) {
2691    owl_function_makemsg("No matching punt filter");
2692 }
[7d4fbcd]2693}
2694
[c79a047]2695void owl_function_show_keymaps(void)
[d54838d]2696{
[ce68f23]2697  GPtrArray *l;
[7d4fbcd]2698  owl_fmtext fm;
[afa200a]2699  const owl_keymap *km;
[12bc46a]2700  const owl_keyhandler *kh;
[ce68f23]2701  int i;
[e19eb97]2702  const char *kmname;
[7d4fbcd]2703
[1aee7d9]2704  kh = owl_global_get_keyhandler(&g);
[7d4fbcd]2705  owl_fmtext_init_null(&fm);
2706  owl_fmtext_append_bold(&fm, "Keymaps:   ");
2707  owl_fmtext_append_normal(&fm, "(use 'show keymap <name>' for details)\n");
[ce68f23]2708  l = owl_keyhandler_get_keymap_names(kh);
2709  owl_fmtext_append_list(&fm, l, "\n", owl_function_keymap_summary);
[7d4fbcd]2710  owl_fmtext_append_normal(&fm, "\n");
[1aee7d9]2711
[ce68f23]2712  for (i = 0; i < l->len; i++) {
2713    kmname = l->pdata[i];
[1aee7d9]2714    km = owl_keyhandler_get_keymap(kh, kmname);
2715    owl_fmtext_append_bold(&fm, "\n\n----------------------------------------------------------------------------------------------------\n\n");
[13ebf92]2716    owl_keymap_get_details(km, &fm, 0);
[1aee7d9]2717  }
2718  owl_fmtext_append_normal(&fm, "\n");
2719 
[7d4fbcd]2720  owl_function_popless_fmtext(&fm);
[ce68f23]2721  owl_ptr_array_free(l, g_free);
[7ab0020]2722  owl_fmtext_cleanup(&fm);
[7d4fbcd]2723}
2724
[6829afc]2725CALLER_OWN char *owl_function_keymap_summary(const char *name)
[d54838d]2726{
[afa200a]2727  const owl_keymap *km
[7d4fbcd]2728    = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), name);
2729  if (km) return owl_keymap_summary(km);
2730  else return(NULL);
2731}
2732
2733/* TODO: implement for real */
[e19eb97]2734void owl_function_show_keymap(const char *name)
[d54838d]2735{
[1fd0b25]2736  owl_fmtext fm;
[afa200a]2737  const owl_keymap *km;
[7d4fbcd]2738
2739  owl_fmtext_init_null(&fm);
2740  km = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), name);
2741  if (km) {
[13ebf92]2742    owl_keymap_get_details(km, &fm, 1);
[7d4fbcd]2743  } else {
2744    owl_fmtext_append_normal(&fm, "No such keymap...\n");
2745  } 
2746  owl_function_popless_fmtext(&fm);
[7ab0020]2747  owl_fmtext_cleanup(&fm);
[7d4fbcd]2748}
2749
[e19eb97]2750void owl_function_help_for_command(const char *cmdname)
[d54838d]2751{
[1fd0b25]2752  owl_fmtext fm;
[7d4fbcd]2753
2754  owl_fmtext_init_null(&fm);
2755  owl_cmd_get_help(owl_global_get_cmddict(&g), cmdname, &fm);
2756  owl_function_popless_fmtext(&fm); 
[7ab0020]2757  owl_fmtext_cleanup(&fm);
[7d4fbcd]2758}
[1fd0b25]2759
[2ec737f]2760void owl_function_set_search(const char *string)
[d54838d]2761{
[41c9a96]2762  owl_regex re;
2763
2764  if (string && owl_regex_create_quoted(&re, string) == 0) {
2765    owl_global_set_search_re(&g, &re);
[5cbc929]2766    owl_regex_cleanup(&re);
[41c9a96]2767  } else {
2768    owl_global_set_search_re(&g, NULL);
2769  }
[2ec737f]2770}
[ab225e0]2771
[118c919]2772void owl_function_search_helper(int consider_current, int direction)
[d54838d]2773{
[1fd0b25]2774  /* move to a message that contains the string.  If direction is
2775   * OWL_DIRECTION_DOWNWARDS then search fowards, if direction is
2776   * OWL_DIRECTION_UPWARDS then search backwards.
2777   *
[118c919]2778   * If consider_current is true then it will stay on the
[9c1e61d4]2779   * current message if it contains the string.
[1fd0b25]2780   */
2781
[9e5c9f3]2782  const owl_view *v;
[1fd0b25]2783  int viewsize, i, curmsg, start;
2784  owl_message *m;
2785
2786  v=owl_global_get_current_view(&g);
2787  viewsize=owl_view_get_size(v);
2788  curmsg=owl_global_get_curmsg(&g);
2789 
2790  if (viewsize==0) {
[4fd211f]2791    owl_function_makemsg("No messages present");
[1fd0b25]2792    return;
2793  }
2794
[118c919]2795  if (consider_current) {
[1fd0b25]2796    start=curmsg;
2797  } else if (direction==OWL_DIRECTION_DOWNWARDS) {
2798    start=curmsg+1;
2799  } else {
2800    start=curmsg-1;
2801  }
2802
2803  /* bounds check */
2804  if (start>=viewsize || start<0) {
[4fd211f]2805    owl_function_makemsg("No further matches found");
[1fd0b25]2806    return;
2807  }
2808
2809  for (i=start; i<viewsize && i>=0;) {
2810    m=owl_view_get_element(v, i);
[41c9a96]2811    if (owl_message_search(m, owl_global_get_search_re(&g))) {
[1fd0b25]2812      owl_global_set_curmsg(&g, i);
2813      owl_function_calculate_topmsg(direction);
2814      owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2815      if (direction==OWL_DIRECTION_DOWNWARDS) {
2816        owl_global_set_direction_downwards(&g);
2817      } else {
2818        owl_global_set_direction_upwards(&g);
2819      }
2820      return;
2821    }
2822    if (direction==OWL_DIRECTION_DOWNWARDS) {
2823      i++;
2824    } else {
2825      i--;
2826    }
[47128d9]2827    if (owl_global_take_interrupt(&g)) {
[bf66f4e]2828      owl_function_makemsg("Search interrupted!");
2829      owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2830      return;
2831    }
[1fd0b25]2832  }
[37c27cf]2833  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
[4fd211f]2834  owl_function_makemsg("No matches found");
[1fd0b25]2835}
2836
2837/* strips formatting from ztext and returns the unformatted text.
2838 * caller is responsible for freeing. */
[6829afc]2839CALLER_OWN char *owl_function_ztext_stylestrip(const char *zt)
[d54838d]2840{
[1fd0b25]2841  owl_fmtext fm;
2842  char *plaintext;
2843
2844  owl_fmtext_init_null(&fm);
2845  owl_fmtext_append_ztext(&fm, zt);
2846  plaintext = owl_fmtext_print_plain(&fm);
[7ab0020]2847  owl_fmtext_cleanup(&fm);
[1fd0b25]2848  return(plaintext);
2849}
[42abb10]2850
[5a95b69]2851/* Popup a buddylisting.  If filename is NULL use the default .anyone */
[4fd3c04]2852void owl_function_buddylist(int zephyr, const char *filename)
[d54838d]2853{
[4fd3c04]2854  int i;
[8daf504]2855  int interrupted = 0;
[42abb10]2856  owl_fmtext fm;
[09489b89]2857#ifdef HAVE_LIBZEPHYR
[fea7992]2858  int x;
[ecffae6]2859  GPtrArray *anyone;
[e19eb97]2860  const char *user;
[65b2173]2861  char *tmp;
[09489b89]2862  ZLocations_t location[200];
2863  int numlocs, ret;
2864#endif
[42abb10]2865
[aa5f725]2866  owl_fmtext_init_null(&fm);
[42abb10]2867
[09489b89]2868#ifdef HAVE_LIBZEPHYR
[aa5f725]2869  if (zephyr) {
[bfbf590]2870    if(!owl_global_is_havezephyr(&g)) {
2871      owl_function_error("Zephyr currently not available.");