source: functions.c @ f449096

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