source: functions.c @ 13ebf92

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