source: functions.c @ 2fec14b

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