source: functions.c @ c0ddaea

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