source: functions.c @ 2ec737f

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