source: functions.c @ f25812b

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