source: functions.c @ afa200a

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