source: functions.c @ 38cc669

release-1.6release-1.7release-1.8release-1.9
Last change on this file since 38cc669 was 38cc669, checked in by Nelson Elhage <nelhage@ksplice.com>, 12 years ago
Create/destroy editwins rather than reusing a single one.
  • Property mode set to 100644
File size: 96.0 KB
RevLine 
[7d4fbcd]1#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4#include <signal.h>
[789462a]5#include <netinet/in.h>
[7d4fbcd]6#include <string.h>
7#include <time.h>
[2adaf1d]8#include <sys/types.h>
9#include <sys/stat.h>
[8f44c6b]10#include <sys/wait.h>
11#include <errno.h>
[d09e5a1]12#include <signal.h>
[7d4fbcd]13#include "owl.h"
14
[e19eb97]15char *owl_function_command(const char *cmdbuff)
[d54838d]16{
[7d4fbcd]17  owl_function_debugmsg("executing command: %s", cmdbuff);
18  return owl_cmddict_execute(owl_global_get_cmddict(&g), 
19                             owl_global_get_context(&g), cmdbuff);
20}
21
[e19eb97]22char *owl_function_command_argv(const char *const *argv, int argc)
[c4ba74d]23{
24  return owl_cmddict_execute_argv(owl_global_get_cmddict(&g),
25                                  owl_global_get_context(&g),
26                                  argv, argc);
27}
28
[e19eb97]29void owl_function_command_norv(const char *cmdbuff)
[d54838d]30{
[7d4fbcd]31  char *rv;
[4b464a4]32  rv=owl_function_command(cmdbuff);
[7d4fbcd]33  if (rv) owl_free(rv);
34}
35
[e19eb97]36void owl_function_command_alias(const char *alias_from, const char *alias_to)
[d54838d]37{
[7d4fbcd]38  owl_cmddict_add_alias(owl_global_get_cmddict(&g), alias_from, alias_to);
39}
40
[0a0fb74]41const owl_cmd *owl_function_get_cmd(const char *name)
[d54838d]42{
[7d4fbcd]43  return owl_cmddict_find(owl_global_get_cmddict(&g), name);
44}
45
[c79a047]46void owl_function_show_commands(void)
[d54838d]47{
[7d4fbcd]48  owl_list l;
49  owl_fmtext fm;
50
51  owl_fmtext_init_null(&fm);
52  owl_fmtext_append_bold(&fm, "Commands:  ");
53  owl_fmtext_append_normal(&fm, "(use 'show command <name>' for details)\n");
54  owl_cmddict_get_names(owl_global_get_cmddict(&g), &l);
55  owl_fmtext_append_list(&fm, &l, "\n", owl_function_cmd_describe);
56  owl_fmtext_append_normal(&fm, "\n");
57  owl_function_popless_fmtext(&fm);
[8d4b521]58  owl_cmddict_namelist_cleanup(&l);
[7ab0020]59  owl_fmtext_cleanup(&fm);
[7d4fbcd]60}
61
[e19eb97]62void owl_function_show_view(const char *viewname)
[ef56a67]63{
[9e5c9f3]64  const owl_view *v;
[ef56a67]65  owl_fmtext fm;
66
67  /* we only have the one view right now */
68  v=owl_global_get_current_view(&g);
69  if (viewname && strcmp(viewname, owl_view_get_name(v))) {
[ec6ff52]70    owl_function_error("No view named '%s'", viewname);
[ef56a67]71    return;
72  }
73
74  owl_fmtext_init_null(&fm);
75  owl_view_to_fmtext(v, &fm);
76  owl_function_popless_fmtext(&fm);
[7ab0020]77  owl_fmtext_cleanup(&fm);
[ef56a67]78}
79
[c79a047]80void owl_function_show_styles(void) {
[f1e629d]81  owl_list l;
82  owl_fmtext fm;
83
84  owl_fmtext_init_null(&fm);
85  owl_fmtext_append_bold(&fm, "Styles:\n");
86  owl_global_get_style_names(&g, &l);
87  owl_fmtext_append_list(&fm, &l, "\n", owl_function_style_describe);
88  owl_fmtext_append_normal(&fm, "\n");
89  owl_function_popless_fmtext(&fm);
[8c59178]90  owl_list_cleanup(&l, owl_free);
[7ab0020]91  owl_fmtext_cleanup(&fm);
[f1e629d]92}
93
[e19eb97]94char *owl_function_style_describe(const char *name) {
95  const char *desc;
[65b2173]96  char *s;
[1fdab04]97  const owl_style *style;
[f1e629d]98  style = owl_global_get_style_by_name(&g, name);
99  if (style) {
100    desc = owl_style_get_description(style);
101  } else {
102    desc = "???";
103  }
104  s = owl_sprintf("%-20s - %s%s", name, 
105                  0==owl_style_validate(style)?"":"[INVALID] ",
106                  desc);
107  return s;
108}
[ef56a67]109
[e19eb97]110char *owl_function_cmd_describe(const char *name)
[d54838d]111{
[0a0fb74]112  const owl_cmd *cmd = owl_cmddict_find(owl_global_get_cmddict(&g), name);
[7d4fbcd]113  if (cmd) return owl_cmd_describe(cmd);
114  else return(NULL);
115}
116
[e19eb97]117void owl_function_show_command(const char *name)
[d54838d]118{
[7d4fbcd]119  owl_function_help_for_command(name);
120}
121
[c79a047]122void owl_function_show_license(void)
[debb15d]123{
[e19eb97]124  const char *text;
[debb15d]125
126  text=""
[4228f8b]127    "barnowl version " OWL_VERSION_STRING "\n"
[23e1110]128    "Copyright (c) 2006-2009 The BarnOwl Developers. All rights reserved.\n"
[debb15d]129    "Copyright (c) 2004 James Kretchmar. All rights reserved.\n"
130    "\n"
131    "Redistribution and use in source and binary forms, with or without\n"
132    "modification, are permitted provided that the following conditions are\n"
133    "met:\n"
134    "\n"
135    "   * Redistributions of source code must retain the above copyright\n"
136    "     notice, this list of conditions and the following disclaimer.\n"
137    "\n"
138    "   * Redistributions in binary form must reproduce the above copyright\n"
139    "     notice, this list of conditions and the following disclaimer in\n"
140    "     the documentation and/or other materials provided with the\n"
141    "     distribution.\n"
142    "\n"
143    "   * Redistributions in any form must be accompanied by information on\n"
144    "     how to obtain complete source code for the Owl software and any\n"
145    "     accompanying software that uses the Owl software. The source code\n"
146    "     must either be included in the distribution or be available for no\n"
147    "     more than the cost of distribution plus a nominal fee, and must be\n"
148    "     freely redistributable under reasonable conditions. For an\n"
149    "     executable file, complete source code means the source code for\n"
150    "     all modules it contains. It does not include source code for\n"
151    "     modules or files that typically accompany the major components of\n"
152    "     the operating system on which the executable file runs.\n"
153    "\n"
154    "THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
155    "IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n"
156    "WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR\n"
157    "NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE\n"
158    "LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n"
159    "CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n"
160    "SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR\n"
161    "BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n"
162    "WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE\n"
163    "OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN\n"
164    "IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n";
165  owl_function_popless_text(text);
166}
167
[c79a047]168void owl_function_show_quickstart(void)
[799b60e]169{
[e19eb97]170    const char *message =
[799b60e]171    "Move between messages with the arrow keys, and press 'r' to reply.\n"
172    "For more info, press 'h' or visit http://barnowl.mit.edu/\n\n"
173#ifdef HAVE_LIBZEPHYR
174    "@b(Zephyr:)\n"
175    "To send a message to a user, type ':zwrite @b(username)'. You can also\n"
176    "press 'z' and then type the username. To subscribe to a class, type\n"
177    "':sub @b(class)', and then type ':zwrite -c @b(class)' to send.\n\n"
178#endif
179    "@b(AIM:)\n"
180    "Log in to AIM with ':aimlogin @b(screenname)'. Use ':aimwrite @b(screenname)',\n"
181    "or 'a' and then the screen name, to send someone a message.\n\n"
182    ;
183
184    if (owl_perlconfig_is_function("BarnOwl::Hooks::_get_quickstart")) {
185        char *perlquickstart = owl_perlconfig_execute("BarnOwl::Hooks::_get_quickstart()");
186        if (perlquickstart) {
187            char *result = owl_sprintf("%s%s", message, perlquickstart);
188            owl_function_adminmsg("BarnOwl Quickstart", result);
189            owl_free(result);
190            owl_free(perlquickstart);
191            return;
192        }
193    }
194    owl_function_adminmsg("BarnOwl Quickstart", message);
195}
196
197
[15b34fd]198/* Create an admin message, append it to the global list of messages
199 * and redisplay if necessary.
200 */
[e19eb97]201void owl_function_adminmsg(const char *header, const char *body)
[d54838d]202{
[7d4fbcd]203  owl_message *m;
[4b464a4]204
[7d4fbcd]205  m=owl_malloc(sizeof(owl_message));
[15b34fd]206  owl_message_create_admin(m, header, body);
207 
[8fec514]208  /* add it to the global list and current view */
[7d4fbcd]209  owl_messagelist_append_element(owl_global_get_msglist(&g), m);
210  owl_view_consider_message(owl_global_get_current_view(&g), m);
211
[15b34fd]212  /* do followlast if necessary */
213  if (owl_global_should_followlast(&g)) owl_function_lastmsg_noredisplay();
[7d4fbcd]214
[15b34fd]215  /* redisplay etc. */
[7d4fbcd]216  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
217  owl_global_set_needrefresh(&g);
218}
219
[15b34fd]220/* Create an outgoing zephyr message and return a pointer to it.  Does
[13fe062]221 * not put it on the global queue, use owl_global_messagequeue_addmsg() for
[15b34fd]222 * that.
223 */
[e19eb97]224owl_message *owl_function_make_outgoing_zephyr(const char *body, const char *zwriteline, const char *zsig)
[d09e5a1]225{
226  owl_message *m;
[24ccc01]227  owl_zwrite zw;
228
229  owl_zwrite_create_from_line(&zw, zwriteline);
230  owl_zwrite_set_zsig(&zw, zsig);
[d09e5a1]231
232  /* create the message */
233  m=owl_malloc(sizeof(owl_message));
[24ccc01]234 
235  owl_message_create_from_zwrite(m, &zw, body);
[c230bc1]236  owl_zwrite_cleanup(&zw);
[15b34fd]237
238  return(m);
239}
240
241/* Create an outgoing AIM message, returns a pointer to the created
242 * message or NULL if we're not logged into AIM (and thus unable to
243 * create the message).  Does not put it on the global queue.  Use
[13fe062]244 * owl_global_messagequeue_addmsg() for that .
[15b34fd]245 */
[e19eb97]246owl_message *owl_function_make_outgoing_aim(const char *body, const char *to)
[15b34fd]247{
248  owl_message *m;
249
250  /* error if we're not logged into aim */
251  if (!owl_global_is_aimloggedin(&g)) return(NULL);
252 
253  m=owl_malloc(sizeof(owl_message));
[d559df9]254  owl_message_create_aim(m,
255                         owl_global_get_aim_screenname(&g),
256                         to,
257                         body,
258                         OWL_MESSAGE_DIRECTION_OUT,
259                         0);
[15b34fd]260  return(m);
[d09e5a1]261}
262
[15b34fd]263/* Create an outgoing loopback message and return a pointer to it.
264 * Does not append it to the global queue, use
[13fe062]265 * owl_global_messagequeue_addmsg() for that.
[15b34fd]266 */
[e19eb97]267owl_message *owl_function_make_outgoing_loopback(const char *body)
[37eab7f]268{
269  owl_message *m;
270
271  /* create the message */
272  m=owl_malloc(sizeof(owl_message));
273  owl_message_create_loopback(m, body);
274  owl_message_set_direction_out(m);
275
[15b34fd]276  return(m);
[37eab7f]277}
278
[1b1cd2c]279void owl_function_start_edit_win(const char *line, void (*callback)(owl_editwin *), void *data, void (*cleanup)(void *))
[d54838d]280{
[7d4fbcd]281  owl_editwin *e;
[bdbec0a]282  char *s;
283
284  /* create and setup the editwin */
[38cc669]285  owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_MULTILINE,
286                               owl_global_get_msg_history(&g));
[bdbec0a]287  e = owl_global_get_typwin(&g);
288  owl_editwin_set_dotsend(e);
289  s = owl_sprintf("----> %s\n", line);
290  owl_editwin_set_locktext(e, s);
291  owl_free(s);
292
[38cc669]293  owl_editwin_set_cbdata(e, data, cleanup);
294  owl_editwin_set_callback(e, callback);
[2a17b63]295  owl_global_push_context(&g, OWL_CTX_EDITMULTI, e, "editmulti");
[bdbec0a]296}
297
298static void owl_function_write_setup(const char *line, const char *noun, void (*callback)(owl_editwin *))
299{
300
301  if (!owl_global_get_lockout_ctrld(&g))
302    owl_function_makemsg("Type your %s below.  "
303                         "End with ^D or a dot on a line by itself."
304                         "  ^C will quit.", noun);
305  else
306    owl_function_makemsg("Type your %s below.  "
307                         "End with a dot on a line by itself.  ^C will quit.",
308                         noun);
309
[8dfb59c]310  owl_function_start_edit_win(line, callback,
311                              owl_strdup(line),
312                              owl_free);
[bdbec0a]313}
314
315void owl_function_zwrite_setup(const char *line)
316{
[7d4fbcd]317  owl_zwrite z;
318  int ret;
319
320  /* check the arguments */
321  ret=owl_zwrite_create_from_line(&z, line);
322  if (ret) {
[d768834]323    owl_function_error("Error in zwrite arguments");
[c230bc1]324    owl_zwrite_cleanup(&z);
[7d4fbcd]325    return;
326  }
327
328  /* send a ping if necessary */
329  if (owl_global_is_txping(&g)) {
330    owl_zwrite_send_ping(&z);
331  }
[c230bc1]332  owl_zwrite_cleanup(&z);
[7d4fbcd]333
[bdbec0a]334  owl_function_write_setup(line, "zephyr", &owl_callback_zwrite);
[7d4fbcd]335}
336
[e19eb97]337void owl_function_aimwrite_setup(const char *line)
[d09e5a1]338{
[bdbec0a]339  owl_function_write_setup(line, "message", &owl_callback_aimwrite);
[d09e5a1]340}
341
[c79a047]342void owl_function_loopwrite_setup(void)
[37eab7f]343{
[8a2815b]344  owl_function_write_setup("loopwrite", "message", owl_callback_loopwrite);
[1b6b2f3]345}
346
347void owl_callback_zwrite(owl_editwin *e) {
[8dfb59c]348  char *command = owl_editwin_get_cbdata(e);
349  owl_function_zwrite(command,
[1b6b2f3]350                      owl_editwin_get_text(e));
[37eab7f]351}
352
[9ceee9d]353/* send, log and display an outgoing zephyr.  If 'msg' is NULL
354 * the message is expected to be set from the zwrite line itself
355 */
[e19eb97]356void owl_function_zwrite(const char *line, const char *msg)
[d54838d]357{
[d309eb3]358  owl_zwrite z;
[e19eb97]359  const char *mymsg;
[15b34fd]360  owl_message *m;
[d309eb3]361
[a5fc448]362  if(!strncmp(line, "zcrypt", strlen("zcrypt"))) {
363    owl_function_zcrypt(line, msg);
364    return;
365  }
366
[9ceee9d]367  /* create the zwrite and send the message */
368  owl_zwrite_create_from_line(&z, line);
[3f3ee61]369  owl_zwrite_populate_zsig(&z);
[9ceee9d]370  if (msg) {
371    owl_zwrite_set_message(&z, msg);
[d309eb3]372  }
[9ceee9d]373  owl_zwrite_send_message(&z);
374  owl_function_makemsg("Waiting for ack...");
[d309eb3]375
[15b34fd]376  /* If it's personal */
377  if (owl_zwrite_is_personal(&z)) {
378    /* create the outgoing message */
379    mymsg=owl_zwrite_get_message(&z);
380    m=owl_function_make_outgoing_zephyr(mymsg, line, owl_zwrite_get_zsig(&z));
[d309eb3]381
[3c7d086a]382    if (m) {
[13a3c1db]383      owl_global_messagequeue_addmsg(&g, m);
[15b34fd]384    } else {
[3c7d086a]385      owl_function_error("Could not create outgoing zephyr message");
[9ceee9d]386    }
387  }
[453bd70]388
[9ceee9d]389  /* free the zwrite */
[c230bc1]390  owl_zwrite_cleanup(&z);
[d309eb3]391}
392
[9ceee9d]393/* send, log and display an outgoing zcrypt zephyr.  If 'msg' is NULL
[ce7db4d]394 * the message is expected to be set from the zwrite line itself
395 */
[e19eb97]396void owl_function_zcrypt(const char *line, const char *msg)
[d54838d]397{
[7d4fbcd]398  owl_zwrite z;
[e19eb97]399  const char *mymsg;
[9ceee9d]400  char *cryptmsg;
[15b34fd]401  owl_message *m;
[7d4fbcd]402
403  /* create the zwrite and send the message */
404  owl_zwrite_create_from_line(&z, line);
[3f3ee61]405  owl_zwrite_populate_zsig(&z);
[ce7db4d]406  if (msg) {
407    owl_zwrite_set_message(&z, msg);
408  }
409
[9ceee9d]410  mymsg=owl_zwrite_get_message(&z);
411#ifdef OWL_ENABLE_ZCRYPT
[dacb555]412  cryptmsg = owl_zcrypt_encrypt(mymsg, owl_zwrite_get_class(&z), owl_zwrite_get_instance(&z));
413  if (!cryptmsg) {
[ec6ff52]414    owl_function_error("Error in zcrypt, possibly no key found.  Message not sent.");
[9ceee9d]415    owl_function_beep();
[c230bc1]416    owl_zwrite_cleanup(&z);
[9ceee9d]417    return;
418  }
419#else
420  cryptmsg=owl_strdup(mymsg);
421#endif
422
423  owl_zwrite_set_message(&z, cryptmsg);
424  owl_zwrite_set_opcode(&z, "crypt");
425   
[ce7db4d]426  owl_zwrite_send_message(&z);
[7d4fbcd]427  owl_function_makemsg("Waiting for ack...");
428
[15b34fd]429  /* If it's personal */
430  if (owl_zwrite_is_personal(&z)) {
431    /* create the outgoing message */
432    mymsg=owl_zwrite_get_message(&z);
433    m=owl_function_make_outgoing_zephyr(mymsg, line, owl_zwrite_get_zsig(&z));
[3c7d086a]434    if (m) {
[13a3c1db]435      owl_global_messagequeue_addmsg(&g, m);
[15b34fd]436    } else {
[3c7d086a]437      owl_function_error("Could not create outgoing zephyr message");
[7d4fbcd]438    }
439  }
440
441  /* free the zwrite */
[9ceee9d]442  owl_free(cryptmsg);
[c230bc1]443  owl_zwrite_cleanup(&z);
[7d4fbcd]444}
445
[1b6b2f3]446void owl_callback_aimwrite(owl_editwin *e) {
[8dfb59c]447  char *command = owl_editwin_get_cbdata(e);
448  owl_function_aimwrite(command,
[1b6b2f3]449                        owl_editwin_get_text(e));
450}
451
[e19eb97]452void owl_function_aimwrite(const char *line, const char *msg)
[d09e5a1]453{
[ec6ff52]454  int ret;
[e19eb97]455  const char *to;
[65b2173]456  char *format_msg;
[15b34fd]457  owl_message *m;
[f82e233]458
[4211f50b]459  to = line + 9;
460
[f82e233]461  /* make a formatted copy of the message */
462  format_msg=owl_strdup(msg);
463  owl_text_wordunwrap(format_msg);
[ec6ff52]464 
[f82e233]465  /* send the message */
466  ret=owl_aim_send_im(to, format_msg);
[ec6ff52]467  if (!ret) {
468    owl_function_makemsg("AIM message sent.");
469  } else {
470    owl_function_error("Could not send AIM message.");
471  }
[d09e5a1]472
[15b34fd]473  /* create the outgoing message */
474  m=owl_function_make_outgoing_aim(msg, to);
475
[3c7d086a]476  if (m) {
[13a3c1db]477    owl_global_messagequeue_addmsg(&g, m);
[15b34fd]478  } else {
[3c7d086a]479    owl_function_error("Could not create outgoing AIM message");
[d09e5a1]480  }
[f82e233]481
482  owl_free(format_msg);
[d09e5a1]483}
484
[e19eb97]485void owl_function_send_aimawymsg(const char *to, const char *msg)
[9854278]486{
487  int ret;
488  char *format_msg;
[15b34fd]489  owl_message *m;
[9854278]490
491  /* make a formatted copy of the message */
492  format_msg=owl_strdup(msg);
493  owl_text_wordunwrap(format_msg);
494 
495  /* send the message */
496  ret=owl_aim_send_awaymsg(to, format_msg);
497  if (!ret) {
498    /* owl_function_makemsg("AIM message sent."); */
499  } else {
500    owl_function_error("Could not send AIM message.");
501  }
502
[15b34fd]503  /* create the message */
504  m=owl_function_make_outgoing_aim(msg, to);
505  if (m) {
[13a3c1db]506    owl_global_messagequeue_addmsg(&g, m);
[15b34fd]507  } else {
508    owl_function_error("Could not create AIM message");
[9854278]509  }
510  owl_free(format_msg);
511}
512
[1b6b2f3]513void owl_callback_loopwrite(owl_editwin *e) {
514  owl_function_loopwrite(owl_editwin_get_text(e));
515}
516
[e19eb97]517void owl_function_loopwrite(const char *msg)
[37eab7f]518{
[15b34fd]519  owl_message *min, *mout;
[37eab7f]520
521  /* create a message and put it on the message queue.  This simulates
522   * an incoming message */
[15b34fd]523  min=owl_malloc(sizeof(owl_message));
[4211f50b]524  mout=owl_function_make_outgoing_loopback(msg);
[13a3c1db]525
[37eab7f]526  if (owl_global_is_displayoutgoing(&g)) {
[13a3c1db]527    owl_global_messagequeue_addmsg(&g, mout);
[15b34fd]528  } else {
[91634ec]529    owl_message_delete(mout);
[37eab7f]530  }
531
[13a3c1db]532  owl_message_create_loopback(min, msg);
533  owl_message_set_direction_in(min);
534  owl_global_messagequeue_addmsg(&g, min);
535
[37eab7f]536  /* fake a makemsg */
537  owl_function_makemsg("loopback message sent");
538}
539
[b950088]540/* If filter is non-null, looks for the next message matching
541 * that filter.  If skip_deleted, skips any deleted messages.
542 * If last_if_none, will stop at the last message in the view
543 * if no matching messages are found.  */
[e19eb97]544void owl_function_nextmsg_full(const char *filter, int skip_deleted, int last_if_none)
[d54838d]545{
[b950088]546  int curmsg, i, viewsize, found;
[9e5c9f3]547  const owl_view *v;
[4542047]548  const owl_filter *f = NULL;
[c08c70a]549  const owl_message *m;
[7d4fbcd]550
551  v=owl_global_get_current_view(&g);
[b950088]552
553  if (filter) {
554    f=owl_global_get_filter(&g, filter);
555    if (!f) {
[ec6ff52]556      owl_function_error("No %s filter defined", filter);
[b950088]557      return;
558    }
[7d4fbcd]559  }
560
[b950088]561  curmsg=owl_global_get_curmsg(&g);
562  viewsize=owl_view_get_size(v);
563  found=0;
[7d4fbcd]564
[b950088]565  /* just check to make sure we're in bounds... */
566  if (curmsg>viewsize-1) curmsg=viewsize-1;
567  if (curmsg<0) curmsg=0;
[7d4fbcd]568
[b950088]569  for (i=curmsg+1; i<viewsize; i++) {
570    m=owl_view_get_element(v, i);
571    if (skip_deleted && owl_message_is_delete(m)) continue;
572    if (f && !owl_filter_message_match(f, m)) continue;
573    found = 1;
574    break;
575  }
576
577  if (i>owl_view_get_size(v)-1) i=owl_view_get_size(v)-1;
[5763474]578  if (i<0) i=0;
[b950088]579
580  if (!found) {
[799b60e]581    owl_function_makemsg("already at last%s message%s%s%s",
[b950088]582                         skip_deleted?" non-deleted":"",
[799b60e]583                         filter?" in ":"", filter?filter:"",
584                         owl_mainwin_is_curmsg_truncated(owl_global_get_mainwin(&g)) ?
585                         ", press Enter to scroll" : "");
[5a6e6b9]586    /* if (!skip_deleted) owl_function_beep(); */
[7d4fbcd]587  }
588
[b950088]589  if (last_if_none || found) {
590    owl_global_set_curmsg(&g, i);
591    owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
592    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
593    owl_global_set_direction_downwards(&g);
594  }
595}
[7d4fbcd]596
[e19eb97]597void owl_function_prevmsg_full(const char *filter, int skip_deleted, int first_if_none)
[d54838d]598{
[402eb16f]599  int curmsg, i, found;
[9e5c9f3]600  const owl_view *v;
[4542047]601  const owl_filter *f = NULL;
[c08c70a]602  const owl_message *m;
[7d4fbcd]603
604  v=owl_global_get_current_view(&g);
605
[b950088]606  if (filter) {
607    f=owl_global_get_filter(&g, filter);
608    if (!f) {
[ec6ff52]609      owl_function_error("No %s filter defined", filter);
[b950088]610      return;
[7d4fbcd]611    }
[b950088]612  }
[7d4fbcd]613
[b950088]614  curmsg=owl_global_get_curmsg(&g);
615  found=0;
616
617  /* just check to make sure we're in bounds... */
618  if (curmsg<0) curmsg=0;
619
620  for (i=curmsg-1; i>=0; i--) {
621    m=owl_view_get_element(v, i);
622    if (skip_deleted && owl_message_is_delete(m)) continue;
623    if (f && !owl_filter_message_match(f, m)) continue;
624    found = 1;
625    break;
626  }
627
628  if (i<0) i=0;
629
630  if (!found) {
[f51bc78]631    owl_function_makemsg("already at first%s message%s%s",
[b950088]632                         skip_deleted?" non-deleted":"",
633                         filter?" in ":"", filter?filter:"");
[5a6e6b9]634    /* if (!skip_deleted) owl_function_beep(); */
[b950088]635  }
636
637  if (first_if_none || found) {
638    owl_global_set_curmsg(&g, i);
639    owl_function_calculate_topmsg(OWL_DIRECTION_UPWARDS);
640    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
641    owl_global_set_direction_upwards(&g);
[7d4fbcd]642  }
643}
644
[c79a047]645void owl_function_nextmsg(void)
[d54838d]646{
[b950088]647  owl_function_nextmsg_full(NULL, 0, 1);
648}
[7d4fbcd]649
[c79a047]650void owl_function_prevmsg(void)
[d54838d]651{
[b950088]652  owl_function_prevmsg_full(NULL, 0, 1);
653}
[7d4fbcd]654
[c79a047]655void owl_function_nextmsg_notdeleted(void)
[d54838d]656{
[b950088]657  owl_function_nextmsg_full(NULL, 1, 1);
658}
[7d4fbcd]659
[c79a047]660void owl_function_prevmsg_notdeleted(void)
[d54838d]661{
[b950088]662  owl_function_prevmsg_full(NULL, 1, 1);
[7d4fbcd]663}
664
[b950088]665/* if move_after is 1, moves after the delete */
[d54838d]666void owl_function_deletecur(int move_after)
667{
[7d4fbcd]668  int curmsg;
669  owl_view *v;
670
671  v=owl_global_get_current_view(&g);
672
673  /* bail if there's no current message */
674  if (owl_view_get_size(v) < 1) {
[ec6ff52]675    owl_function_error("No current message to delete");
[7d4fbcd]676    return;
677  }
678
679  /* mark the message for deletion */
680  curmsg=owl_global_get_curmsg(&g);
681  owl_view_delete_element(v, curmsg);
682
[b950088]683  if (move_after) {
684    /* move the poiner in the appropriate direction
685     * to the next undeleted msg */
686    if (owl_global_get_direction(&g)==OWL_DIRECTION_UPWARDS) {
687      owl_function_prevmsg_notdeleted();
688    } else {
689      owl_function_nextmsg_notdeleted();
690    }
[7d4fbcd]691  }
692}
693
[d54838d]694void owl_function_undeletecur(int move_after)
695{
[7d4fbcd]696  int curmsg;
697  owl_view *v;
698
699  v=owl_global_get_current_view(&g);
700 
701  if (owl_view_get_size(v) < 1) {
[ec6ff52]702    owl_function_error("No current message to undelete");
[7d4fbcd]703    return;
704  }
705  curmsg=owl_global_get_curmsg(&g);
706
707  owl_view_undelete_element(v, curmsg);
708
[b950088]709  if (move_after) {
710    if (owl_global_get_direction(&g)==OWL_DIRECTION_UPWARDS) {
711      if (curmsg>0) {
712        owl_function_prevmsg();
713      } else {
714        owl_function_nextmsg();
715      }
[7d4fbcd]716    } else {
[b950088]717      owl_function_nextmsg();
[7d4fbcd]718    }
719  }
720
721  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
722}
723
[c79a047]724void owl_function_expunge(void)
[d54838d]725{
[7d4fbcd]726  int curmsg;
[c08c70a]727  const owl_message *m;
[7d4fbcd]728  owl_messagelist *ml;
729  owl_view *v;
[486688f]730  int lastmsgid=0;
[7d4fbcd]731
732  curmsg=owl_global_get_curmsg(&g);
733  v=owl_global_get_current_view(&g);
734  ml=owl_global_get_msglist(&g);
735
736  m=owl_view_get_element(v, curmsg);
[486688f]737  if (m) lastmsgid = owl_message_get_id(m);
[7d4fbcd]738
739  /* expunge the message list */
740  owl_messagelist_expunge(ml);
741
742  /* update all views (we only have one right now) */
743  owl_view_recalculate(v);
744
[486688f]745  /* find where the new position should be
746     (as close as possible to where we last where) */
747  curmsg = owl_view_get_nearest_to_msgid(v, lastmsgid);
748  if (curmsg>owl_view_get_size(v)-1) curmsg = owl_view_get_size(v)-1;
749  if (curmsg<0) curmsg = 0;
750  owl_global_set_curmsg(&g, curmsg);
751  owl_function_calculate_topmsg(OWL_DIRECTION_NONE);
[7d4fbcd]752  /* if there are no messages set the direction to down in case we
753     delete everything upwards */
754  owl_global_set_direction_downwards(&g);
755 
756  owl_function_makemsg("Messages expunged");
757  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
758}
759
[c79a047]760void owl_function_firstmsg(void)
[d54838d]761{
[7d4fbcd]762  owl_global_set_curmsg(&g, 0);
763  owl_global_set_topmsg(&g, 0);
764  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
765  owl_global_set_direction_downwards(&g);
766}
767
[c79a047]768void owl_function_lastmsg_noredisplay(void)
[d54838d]769{
[5eeea3b]770  int oldcurmsg, curmsg;
[9e5c9f3]771  const owl_view *v;
[7d4fbcd]772
773  v=owl_global_get_current_view(&g);
[5eeea3b]774  oldcurmsg=owl_global_get_curmsg(&g);
775  curmsg=owl_view_get_size(v)-1; 
[7d4fbcd]776  if (curmsg<0) curmsg=0;
777  owl_global_set_curmsg(&g, curmsg);
[5eeea3b]778  if (oldcurmsg < curmsg) {
779    owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
780  } else if (curmsg<owl_view_get_size(v)) {
781    /* If already at the end, blank the screen and move curmsg
782     * past the end of the messages. */
783    owl_global_set_topmsg(&g, curmsg+1);
784    owl_global_set_curmsg(&g, curmsg+1);
785  } 
[6b580b0]786  /* owl_mainwin_redisplay(owl_global_get_mainwin(&g)); */
[7d4fbcd]787  owl_global_set_direction_downwards(&g);
788}
789
[c79a047]790void owl_function_lastmsg(void)
[d54838d]791{
[7d4fbcd]792  owl_function_lastmsg_noredisplay();
793  owl_mainwin_redisplay(owl_global_get_mainwin(&g)); 
794}
795
[c79a047]796void owl_function_shift_right(void)
[d54838d]797{
[7d4fbcd]798  owl_global_set_rightshift(&g, owl_global_get_rightshift(&g)+10);
799  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
800  owl_global_set_needrefresh(&g);
801}
802
[c79a047]803void owl_function_shift_left(void)
[d54838d]804{
[7d4fbcd]805  int shift;
806
807  shift=owl_global_get_rightshift(&g);
[8c97fa1]808  if (shift > 0) {
809    owl_global_set_rightshift(&g, MAX(shift - 10, 0));
[7d4fbcd]810    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
811    owl_global_set_needrefresh(&g);
812  } else {
813    owl_function_beep();
[f51bc78]814    owl_function_makemsg("Already full left");
[7d4fbcd]815  }
816}
817
[c79a047]818void owl_function_unsuball(void)
[d54838d]819{
[7d4fbcd]820  unsuball();
821  owl_function_makemsg("Unsubscribed from all messages.");
822}
823
[95474d7]824
825/* Load zephyr subscriptions from the named 'file' and load zephyr's
826 * default subscriptions as well.  An error message is printed if
827 * 'file' can't be opened or if zephyr reports an error in
828 * subscribing.
829 *
830 * If 'file' is NULL, this look for the default filename
831 * $HOME/.zephyr.subs.  If the file can not be opened in this case
832 * only, no error message is printed.
833 */
[e19eb97]834void owl_function_loadsubs(const char *file)
[d54838d]835{
[4357be8]836  int ret, ret2;
[e19eb97]837  const char *foo;
[65b2173]838  char *path;
[ecd5dc5]839
[95474d7]840  if (file==NULL) {
841    ret=owl_zephyr_loadsubs(NULL, 0);
842  } else {
[27d8d83]843    path = owl_util_makepath(file);
844    ret=owl_zephyr_loadsubs(path, 1);
[27f6487]845    owl_free(path);
[95474d7]846  }
[ecd5dc5]847
[4357be8]848  /* for backwards compatibility for now */
849  ret2=owl_zephyr_loaddefaultsubs();
850
[ecd5dc5]851  if (!owl_context_is_interactive(owl_global_get_context(&g))) return;
[95474d7]852
853  foo=file?file:"file";
854  if (ret==0 && ret2==0) {
855    if (!file) {
856      owl_function_makemsg("Subscribed to messages.");
857    } else {
858      owl_function_makemsg("Subscribed to messages from %s", file);
859    }
[7d4fbcd]860  } else if (ret==-1) {
[95474d7]861    owl_function_error("Could not read %s", foo);
[7d4fbcd]862  } else {
[95474d7]863    owl_function_error("Error subscribing to messages");
[7d4fbcd]864  }
865}
866
[e19eb97]867void owl_function_loadloginsubs(const char *file)
[d54838d]868{
[7933748]869  int ret;
[ecd5dc5]870
[7933748]871  ret=owl_zephyr_loadloginsubs(file);
[ecd5dc5]872
873  if (!owl_context_is_interactive(owl_global_get_context(&g))) return;
[7933748]874  if (ret==0) {
875  } else if (ret==-1) {
[ec6ff52]876    owl_function_error("Could not open file for login subscriptions.");
[7933748]877  } else {
[ec6ff52]878    owl_function_error("Error subscribing to login messages from file.");
[7933748]879  }
880}
881
[1b6b2f3]882void owl_callback_aimlogin(owl_editwin *e) {
[8dfb59c]883  char *user = owl_editwin_get_cbdata(e);
884  owl_function_aimlogin(user,
[db8b00b]885                        owl_editwin_get_text(e));
[1b6b2f3]886}
887
[e19eb97]888void owl_function_aimlogin(const char *user, const char *passwd) {
[4211f50b]889  int ret;
890
891  /* clear the buddylist */
892  owl_buddylist_clear(owl_global_get_buddylist(&g));
893
894  /* try to login */
895  ret=owl_aim_login(user, passwd);
896  if (ret) owl_function_makemsg("Warning: login for %s failed.\n", user);
897}
898
[c79a047]899void owl_function_suspend(void)
[d54838d]900{
[7d4fbcd]901  endwin();
902  printf("\n");
903  kill(getpid(), SIGSTOP);
904
905  /* resize to reinitialize all the windows when we come back */
906  owl_command_resize();
907}
908
[c79a047]909void owl_function_zaway_toggle(void)
[d54838d]910{
[7d4fbcd]911  if (!owl_global_is_zaway(&g)) {
912    owl_global_set_zaway_msg(&g, owl_global_get_zaway_msg_default(&g));
913    owl_function_zaway_on();
914  } else {
915    owl_function_zaway_off();
916  }
917}
918
[c79a047]919void owl_function_zaway_on(void)
[d54838d]920{
[7d4fbcd]921  owl_global_set_zaway_on(&g);
[4b660cc]922  owl_function_makemsg("zaway set (%s)", owl_global_get_zaway_msg(&g));
[7d4fbcd]923}
924
[c79a047]925void owl_function_zaway_off(void)
[d54838d]926{
[7d4fbcd]927  owl_global_set_zaway_off(&g);
[4b660cc]928  owl_function_makemsg("zaway off");
929}
930
[c79a047]931void owl_function_aaway_toggle(void)
[4b660cc]932{
933  if (!owl_global_is_aaway(&g)) {
934    owl_global_set_aaway_msg(&g, owl_global_get_aaway_msg_default(&g));
935    owl_function_aaway_on();
936  } else {
937    owl_function_aaway_off();
938  }
939}
940
[c79a047]941void owl_function_aaway_on(void)
[4b660cc]942{
943  owl_global_set_aaway_on(&g);
944  /* owl_aim_set_awaymsg(owl_global_get_zaway_msg(&g)); */
945  owl_function_makemsg("AIM away set (%s)", owl_global_get_aaway_msg(&g));
946}
947
[c79a047]948void owl_function_aaway_off(void)
[4b660cc]949{
950  owl_global_set_aaway_off(&g);
951  /* owl_aim_set_awaymsg(""); */
952  owl_function_makemsg("AIM away off");
[7d4fbcd]953}
954
[c79a047]955void owl_function_quit(void)
[d54838d]956{
[7d4fbcd]957  char *ret;
958 
959  /* zlog out if we need to */
[7433402]960  if (owl_global_is_havezephyr(&g) &&
961      owl_global_is_shutdownlogout(&g)) {
[31e48a3]962    owl_zephyr_zlog_out();
[7d4fbcd]963  }
964
965  /* execute the commands in shutdown */
[0337203]966  ret = owl_perlconfig_execute("BarnOwl::Hooks::_shutdown();");
[7d4fbcd]967  if (ret) owl_free(ret);
968
[d09e5a1]969  /* signal our child process, if any */
970  if (owl_global_get_newmsgproc_pid(&g)) {
971    kill(owl_global_get_newmsgproc_pid(&g), SIGHUP);
972  }
973
[8c46404]974  /* Quit zephyr */
[09489b89]975  owl_zephyr_shutdown();
[8c46404]976 
977  /* Quit AIM */
978  if (owl_global_is_aimloggedin(&g)) {
979    owl_aim_logout();
980  }
981
[8232149]982  /* done with curses */
[7d4fbcd]983  endwin();
[8232149]984
985  /* restore terminal settings */
986  tcsetattr(0, TCSAFLUSH, owl_global_get_startup_tio(&g));
987
[7d4fbcd]988  owl_function_debugmsg("Quitting Owl");
989  exit(0);
990}
991
[d54838d]992void owl_function_calculate_topmsg(int direction)
993{
[aa2f33b3]994  int recwinlines, topmsg, curmsg;
[9e5c9f3]995  const owl_view *v;
[7d4fbcd]996
997  v=owl_global_get_current_view(&g);
[aa2f33b3]998  curmsg=owl_global_get_curmsg(&g);
999  topmsg=owl_global_get_topmsg(&g);
[7d4fbcd]1000  recwinlines=owl_global_get_recwin_lines(&g);
1001
[f9c43ae]1002  /*
[7d4fbcd]1003  if (owl_view_get_size(v) < 1) {
1004    return;
1005  }
[f9c43ae]1006  */
[aa2f33b3]1007
1008  switch (owl_global_get_scrollmode(&g)) {
1009  case OWL_SCROLLMODE_TOP:
[f9c43ae]1010    topmsg = owl_function_calculate_topmsg_top(direction, v, curmsg, topmsg, recwinlines);
[aa2f33b3]1011    break;
1012  case OWL_SCROLLMODE_NEARTOP:
[f9c43ae]1013    topmsg = owl_function_calculate_topmsg_neartop(direction, v, curmsg, topmsg, recwinlines);
[aa2f33b3]1014    break;
1015  case OWL_SCROLLMODE_CENTER:
[f9c43ae]1016    topmsg = owl_function_calculate_topmsg_center(direction, v, curmsg, topmsg, recwinlines);
[aa2f33b3]1017    break;
1018  case OWL_SCROLLMODE_PAGED:
[f9c43ae]1019    topmsg = owl_function_calculate_topmsg_paged(direction, v, curmsg, topmsg, recwinlines, 0);
[aa2f33b3]1020    break;
1021  case OWL_SCROLLMODE_PAGEDCENTER:
[f9c43ae]1022    topmsg = owl_function_calculate_topmsg_paged(direction, v, curmsg, topmsg, recwinlines, 1);
[aa2f33b3]1023    break;
1024  case OWL_SCROLLMODE_NORMAL:
1025  default:
[f9c43ae]1026    topmsg = owl_function_calculate_topmsg_normal(direction, v, curmsg, topmsg, recwinlines);
[aa2f33b3]1027  }
[3a2daac]1028  owl_function_debugmsg("Calculated a topmsg of %i", topmsg);
[aa2f33b3]1029  owl_global_set_topmsg(&g, topmsg);
1030}
1031
1032/* Returns what the new topmsg should be. 
1033 * Passed the last direction of movement,
1034 * the current view,
1035 * the current message number in the view,
1036 * the top message currently being displayed,
1037 * and the number of lines in the recwin.
1038 */
[9e5c9f3]1039int owl_function_calculate_topmsg_top(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
[d54838d]1040{
[f9c43ae]1041  return(curmsg);
[aa2f33b3]1042}
1043
[9e5c9f3]1044int owl_function_calculate_topmsg_neartop(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
[d54838d]1045{
[aa2f33b3]1046  if (curmsg>0 
1047      && (owl_message_get_numlines(owl_view_get_element(v, curmsg-1))
1048          <  recwinlines/2)) {
[f9c43ae]1049    return(curmsg-1);
[aa2f33b3]1050  } else {
[f9c43ae]1051    return(curmsg);
[aa2f33b3]1052  }
1053}
1054 
[9e5c9f3]1055int owl_function_calculate_topmsg_center(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
[d54838d]1056{
[aa2f33b3]1057  int i, last, lines;
1058
1059  last = curmsg;
1060  lines = 0;
1061  for (i=curmsg-1; i>=0; i--) {
1062    lines += owl_message_get_numlines(owl_view_get_element(v, i));
1063    if (lines > recwinlines/2) break;
1064    last = i;
1065  }
[f9c43ae]1066  return(last);
[aa2f33b3]1067}
1068 
[9e5c9f3]1069int owl_function_calculate_topmsg_paged(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines, int center_on_page)
[d54838d]1070{
[aa2f33b3]1071  int i, last, lines, savey;
1072 
1073  /* If we're off the top of the screen, scroll up such that the
1074   * curmsg is near the botton of the screen. */
1075  if (curmsg < topmsg) {
1076    last = curmsg;
1077    lines = 0;
1078    for (i=curmsg; i>=0; i--) {
1079      lines += owl_message_get_numlines(owl_view_get_element(v, i));
1080      if (lines > recwinlines) break;
1081    last = i;
1082    }
1083    if (center_on_page) {
[f9c43ae]1084      return(owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines));
[aa2f33b3]1085    } else {
[f9c43ae]1086      return(last);
[aa2f33b3]1087    }
1088  }
1089
1090  /* Find number of lines from top to bottom of curmsg (store in savey) */
1091  savey=0;
1092  for (i=topmsg; i<=curmsg; i++) {
1093    savey+=owl_message_get_numlines(owl_view_get_element(v, i));
1094  }
1095
1096  /* if we're off the bottom of the screen, scroll down */
1097  if (savey > recwinlines) {
1098    if (center_on_page) {
[f9c43ae]1099      return(owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines));
[aa2f33b3]1100    } else {
[f9c43ae]1101      return(curmsg);
[aa2f33b3]1102    }
1103  }
1104
1105  /* else just stay as we are... */
[f9c43ae]1106  return(topmsg);
[aa2f33b3]1107}
1108
[9e5c9f3]1109int owl_function_calculate_topmsg_normal(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
[d54838d]1110{
[801b7ac]1111  int savey, i, foo, y;
[f9c43ae]1112
[88736cb]1113  if (curmsg<0) return(topmsg);
1114   
[f9c43ae]1115  /* If we're off the top of the screen then center */
1116  if (curmsg<topmsg) {
1117    topmsg=owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines);
1118  }
1119
[801b7ac]1120  /* If curmsg is so far past topmsg that there are more messages than
1121     lines, skip the line counting that follows because we're
1122     certainly off screen.  */
1123  savey=curmsg-topmsg;
1124  if (savey <= recwinlines) {
1125    /* Find number of lines from top to bottom of curmsg (store in savey) */
1126    savey = 0;
1127    for (i=topmsg; i<=curmsg; i++) {
1128      savey+=owl_message_get_numlines(owl_view_get_element(v, i));
1129    }
[7d4fbcd]1130  }
1131
[f9c43ae]1132  /* If we're off the bottom of the screen, set the topmsg to curmsg
1133   * and scroll upwards */
1134  if (savey > recwinlines) {
1135    topmsg=curmsg;
[801b7ac]1136    savey=owl_message_get_numlines(owl_view_get_element(v, curmsg));
[f9c43ae]1137    direction=OWL_DIRECTION_UPWARDS;
[7d4fbcd]1138  }
[f9c43ae]1139 
[7d4fbcd]1140  /* If our bottom line is less than 1/4 down the screen then scroll up */
1141  if (direction == OWL_DIRECTION_UPWARDS || direction == OWL_DIRECTION_NONE) {
1142    if (savey < (recwinlines / 4)) {
1143      y=0;
[801b7ac]1144      for (i=curmsg; i>=0; i--) {
1145        foo=owl_message_get_numlines(owl_view_get_element(v, i));
[7d4fbcd]1146        /* will we run the curmsg off the screen? */
1147        if ((foo+y) >= recwinlines) {
[801b7ac]1148          i++;
1149          if (i>curmsg) i=curmsg;
[7d4fbcd]1150          break;
1151        }
1152        /* have saved 1/2 the screen space? */
1153        y+=foo;
1154        if (y > (recwinlines / 2)) break;
1155      }
[801b7ac]1156      if (i<0) i=0;
1157      return(i);
[7d4fbcd]1158    }
1159  }
1160
1161  if (direction == OWL_DIRECTION_DOWNWARDS || direction == OWL_DIRECTION_NONE) {
1162    /* If curmsg bottom line is more than 3/4 down the screen then scroll down */
1163    if (savey > ((recwinlines * 3)/4)) {
1164      y=0;
1165      /* count lines from the top until we can save 1/2 the screen size */
[801b7ac]1166      for (i=topmsg; i<curmsg; i++) {
1167        y+=owl_message_get_numlines(owl_view_get_element(v, i));
[7d4fbcd]1168        if (y > (recwinlines / 2)) break;
1169      }
[801b7ac]1170      if (i==curmsg) {
1171        i--;
[7d4fbcd]1172      }
[801b7ac]1173      return(i+1);
[7d4fbcd]1174    }
1175  }
[aa2f33b3]1176
[f9c43ae]1177  return(topmsg);
[7d4fbcd]1178}
1179
[c79a047]1180void owl_function_resize(void)
[d54838d]1181{
[7d4fbcd]1182  owl_global_set_resize_pending(&g);
1183}
1184
[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 */
[dfe94f9]1251  if (is_wintouched(win)) {
1252    owl_function_debugmsg("Warning: owl_function_set_cursor called on dirty window");
1253    update_panels();
1254  }
[7d4fbcd]1255  wnoutrefresh(win);
1256}
1257
[c79a047]1258void owl_function_full_redisplay(void)
[d54838d]1259{
[7d4fbcd]1260  redrawwin(owl_global_get_curs_recwin(&g));
1261  redrawwin(owl_global_get_curs_sepwin(&g));
[af1920fd]1262  /* Work around curses segfualts with windows off the screen */
[451becb]1263  if (g.lines >= owl_global_get_typwin_lines(&g)+2)
1264      redrawwin(owl_global_get_curs_typwin(&g));
1265  if (g.lines >= 2)
1266      redrawwin(owl_global_get_curs_msgwin(&g));
[7d4fbcd]1267
1268  sepbar("");
1269  owl_function_makemsg("");
1270
1271  owl_global_set_needrefresh(&g);
1272}
1273
[e19eb97]1274void owl_function_popless_text(const char *text)
[d54838d]1275{
[7d4fbcd]1276  owl_popwin *pw;
1277  owl_viewwin *v;
1278
1279  pw=owl_global_get_popwin(&g);
1280  v=owl_global_get_viewwin(&g);
1281
1282  owl_popwin_up(pw);
[2a17b63]1283  owl_global_push_context(&g, OWL_CTX_POPLESS, v, "popless");
[7d4fbcd]1284  owl_viewwin_init_text(v, owl_popwin_get_curswin(pw),
1285                        owl_popwin_get_lines(pw), owl_popwin_get_cols(pw),
1286                        text);
[4cca591]1287  owl_viewwin_redisplay(v);
[7d4fbcd]1288  owl_global_set_needrefresh(&g);
1289}
1290
[075ba92]1291void owl_function_popless_fmtext(const owl_fmtext *fm)
[d54838d]1292{
[7d4fbcd]1293  owl_popwin *pw;
1294  owl_viewwin *v;
1295
1296  pw=owl_global_get_popwin(&g);
1297  v=owl_global_get_viewwin(&g);
1298
1299  owl_popwin_up(pw);
[2a17b63]1300  owl_global_push_context(&g, OWL_CTX_POPLESS, v, "popless");
[7d4fbcd]1301  owl_viewwin_init_fmtext(v, owl_popwin_get_curswin(pw),
1302                   owl_popwin_get_lines(pw), owl_popwin_get_cols(pw),
1303                   fm);
[4cca591]1304  owl_viewwin_redisplay(v);
[7d4fbcd]1305  owl_global_set_needrefresh(&g);
[f17bff98]1306}
1307
[e19eb97]1308void owl_function_popless_file(const char *filename)
[f17bff98]1309{
1310  owl_fmtext fm;
1311  FILE *file;
[b7ee89b]1312  char *s = NULL;
[f17bff98]1313
1314  file=fopen(filename, "r");
1315  if (!file) {
1316    owl_function_error("Could not open file: %s", filename);
1317    return;
1318  }
1319
1320  owl_fmtext_init_null(&fm);
[b7ee89b]1321  while (owl_getline(&s, file))
1322    owl_fmtext_append_normal(&fm, s);
1323  owl_free(s);
[f17bff98]1324
1325  owl_function_popless_fmtext(&fm);
[7ab0020]1326  owl_fmtext_cleanup(&fm);
[f17bff98]1327  fclose(file);
[7d4fbcd]1328}
1329
[c79a047]1330void owl_function_about(void)
[d54838d]1331{
[2101a50]1332  owl_function_popless_text(
1333    "This is barnowl version " OWL_VERSION_STRING ".\n\n"
1334    "barnowl is a fork of the Owl zephyr client, written and\n"
1335    "maintained by Alejandro Sedeno and Nelson Elhage at the\n"
1336    "Massachusetts Institute of Technology. \n"
1337    "\n"
1338    "Owl was written by James Kretchmar. The first version, 0.5, was\n"
1339    "released in March 2002.\n"
1340    "\n"
1341    "The name 'owl' was chosen in reference to the owls in the\n"
1342    "Harry Potter novels, who are tasked with carrying messages\n"
1343    "between Witches and Wizards. The name 'barnowl' was chosen\n"
1344    "because we feel our owls should live closer to our ponies.\n"
1345    "\n"
[23e1110]1346    "Copyright (c) 2006-2009 The BarnOwl Developers. All rights reserved.\n"
[2101a50]1347    "Copyright (c) 2004 James Kretchmar. All rights reserved.\n"
1348    "Copyright 2002 Massachusetts Institute of Technology\n"
1349    "\n"
1350    "This program is free software. You can redistribute it and/or\n"
1351    "modify under the terms of the Sleepycat License. Use the \n"
1352    "':show license' command to display the full license\n"
1353  );
[7d4fbcd]1354}
1355
[c79a047]1356void owl_function_info(void)
[d54838d]1357{
[c08c70a]1358  const owl_message *m;
[5789230]1359  owl_fmtext fm, attrfm;
[9e5c9f3]1360  const owl_view *v;
[09489b89]1361#ifdef HAVE_LIBZEPHYR
[1077891a]1362  const ZNotice_t *n;
[09489b89]1363#endif
[7d4fbcd]1364
[d0d65df]1365  owl_fmtext_init_null(&fm);
1366 
[7d4fbcd]1367  v=owl_global_get_current_view(&g);
[5eeea3b]1368  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1369  if (!m || owl_view_get_size(v)==0) {
[ec6ff52]1370    owl_function_error("No message selected\n");
[7d4fbcd]1371    return;
1372  }
1373
[5789230]1374  owl_fmtext_append_bold(&fm, "General Information:\n");
[57609b3]1375  owl_fmtext_appendf_normal(&fm, "  Msg Id    : %i\n", owl_message_get_id(m));
[df0d93a]1376
[5789230]1377  owl_fmtext_append_normal(&fm, "  Type      : ");
[37eab7f]1378  owl_fmtext_append_bold(&fm, owl_message_get_type(m));
[df0d93a]1379  owl_fmtext_append_normal(&fm, "\n");
1380
[4b464a4]1381  if (owl_message_is_direction_in(m)) {
[5789230]1382    owl_fmtext_append_normal(&fm, "  Direction : in\n");
[4b464a4]1383  } else if (owl_message_is_direction_out(m)) {
[5789230]1384    owl_fmtext_append_normal(&fm, "  Direction : out\n");
[4b464a4]1385  } else if (owl_message_is_direction_none(m)) {
[5789230]1386    owl_fmtext_append_normal(&fm, "  Direction : none\n");
[4b464a4]1387  } else {
[5789230]1388    owl_fmtext_append_normal(&fm, "  Direction : unknown\n");
[4b464a4]1389  }
[df0d93a]1390
[57609b3]1391  owl_fmtext_appendf_normal(&fm, "  Time      : %s\n", owl_message_get_timestr(m));
[4b464a4]1392
[df0d93a]1393  if (!owl_message_is_type_admin(m)) {
[57609b3]1394    owl_fmtext_appendf_normal(&fm, "  Sender    : %s\n", owl_message_get_sender(m));
1395    owl_fmtext_appendf_normal(&fm, "  Recipient : %s\n", owl_message_get_recipient(m));
[df0d93a]1396  }
[57609b3]1397
[0ff8fb57]1398  if (owl_message_is_type_zephyr(m)) {
[5789230]1399    owl_fmtext_append_bold(&fm, "\nZephyr Specific Information:\n");
[0ff8fb57]1400   
[57609b3]1401    owl_fmtext_appendf_normal(&fm, "  Class     : %s\n", owl_message_get_class(m));
1402    owl_fmtext_appendf_normal(&fm, "  Instance  : %s\n", owl_message_get_instance(m));
1403    owl_fmtext_appendf_normal(&fm, "  Opcode    : %s\n", owl_message_get_opcode(m));
[09489b89]1404#ifdef HAVE_LIBZEPHYR
[d0d65df]1405    if (owl_message_is_direction_in(m)) {
[09489b89]1406      char *ptr, tmpbuff[1024];
1407      int i, j, fields, len;
1408
[d0d65df]1409      n=owl_message_get_notice(m);
[df0d93a]1410
[5a95b69]1411      if (!owl_message_is_pseudo(m)) {
1412        owl_fmtext_append_normal(&fm, "  Kind      : ");
1413        if (n->z_kind==UNSAFE) {
1414          owl_fmtext_append_normal(&fm, "UNSAFE\n");
1415        } else if (n->z_kind==UNACKED) {
1416          owl_fmtext_append_normal(&fm, "UNACKED\n");
1417        } else if (n->z_kind==ACKED) {
1418          owl_fmtext_append_normal(&fm, "ACKED\n");
1419        } else if (n->z_kind==HMACK) {
1420          owl_fmtext_append_normal(&fm, "HMACK\n");
1421        } else if (n->z_kind==HMCTL) {
1422          owl_fmtext_append_normal(&fm, "HMCTL\n");
1423        } else if (n->z_kind==SERVACK) {
1424          owl_fmtext_append_normal(&fm, "SERVACK\n");
1425        } else if (n->z_kind==SERVNAK) {
1426          owl_fmtext_append_normal(&fm, "SERVNACK\n");
1427        } else if (n->z_kind==CLIENTACK) {
1428          owl_fmtext_append_normal(&fm, "CLIENTACK\n");
1429        } else if (n->z_kind==STAT) {
1430          owl_fmtext_append_normal(&fm, "STAT\n");
1431        } else {
1432          owl_fmtext_append_normal(&fm, "ILLEGAL VALUE\n");
1433        }
[d0d65df]1434      }
[57609b3]1435      owl_fmtext_appendf_normal(&fm, "  Host      : %s\n", owl_message_get_hostname(m));
[5a95b69]1436
1437      if (!owl_message_is_pseudo(m)) {
1438        owl_fmtext_append_normal(&fm, "\n");
[57609b3]1439        owl_fmtext_appendf_normal(&fm, "  Port      : %i\n", ntohs(n->z_port));
[f12d199]1440        owl_fmtext_appendf_normal(&fm, "  Auth      : %s\n", owl_zephyr_get_authstr(n));
[57609b3]1441
1442        /* FIXME make these more descriptive */
[f12d199]1443        owl_fmtext_appendf_normal(&fm, "  Checkd Ath: %i\n", n->z_checked_auth);
[57609b3]1444        owl_fmtext_appendf_normal(&fm, "  Multi notc: %s\n", n->z_multinotice);
1445        owl_fmtext_appendf_normal(&fm, "  Num other : %i\n", n->z_num_other_fields);
1446        owl_fmtext_appendf_normal(&fm, "  Msg Len   : %i\n", n->z_message_len);
[5a95b69]1447
1448        fields=owl_zephyr_get_num_fields(n);
[57609b3]1449        owl_fmtext_appendf_normal(&fm, "  Fields    : %i\n", fields);
1450
[5a95b69]1451        for (i=0; i<fields; i++) {
[5376a95]1452          ptr=owl_zephyr_get_field_as_utf8(n, i+1);
[b0430a6]1453          len=strlen(ptr);
[5a95b69]1454          if (len<30) {
1455            strncpy(tmpbuff, ptr, len);
1456            tmpbuff[len]='\0';
1457          } else {
1458            strncpy(tmpbuff, ptr, 30);
1459            tmpbuff[30]='\0';
1460            strcat(tmpbuff, "...");
1461          }
[b0430a6]1462          owl_free(ptr);
[57609b3]1463
[5a95b69]1464          for (j=0; j<strlen(tmpbuff); j++) {
1465            if (tmpbuff[j]=='\n') tmpbuff[j]='~';
1466            if (tmpbuff[j]=='\r') tmpbuff[j]='!';
1467          }
[57609b3]1468
1469          owl_fmtext_appendf_normal(&fm, "  Field %i   : %s\n", i+1, tmpbuff);
[5a95b69]1470        }
[57609b3]1471        owl_fmtext_appendf_normal(&fm, "  Default Fm: %s\n", n->z_default_format);
[d0d65df]1472      }
[57609b3]1473
[7d4fbcd]1474    }
[57609b3]1475#endif
[7d4fbcd]1476  }
[0ff8fb57]1477
[5789230]1478  owl_fmtext_append_bold(&fm, "\nOwl Message Attributes:\n");
1479  owl_message_attributes_tofmtext(m, &attrfm);
1480  owl_fmtext_append_fmtext(&fm, &attrfm);
[d0d65df]1481 
1482  owl_function_popless_fmtext(&fm);
[7ab0020]1483  owl_fmtext_cleanup(&fm);
1484  owl_fmtext_cleanup(&attrfm);
[7d4fbcd]1485}
1486
[5639bf2]1487/* print the current message in a popup window.
1488 * Use the 'default' style regardless of whatever
1489 * style the user may be using
1490 */
[c79a047]1491void owl_function_curmsg_to_popwin(void)
[d54838d]1492{
[9e5c9f3]1493  const owl_view *v;
[c08c70a]1494  const owl_message *m;
[1fdab04]1495  const owl_style *s;
[5639bf2]1496  owl_fmtext fm;
[7d4fbcd]1497
[5639bf2]1498  v=owl_global_get_current_view(&g);
1499  s=owl_global_get_style_by_name(&g, "default");
[7d4fbcd]1500
[5eeea3b]1501  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1502
1503  if (!m || owl_view_get_size(v)==0) {
[ec6ff52]1504    owl_function_error("No current message");
[7d4fbcd]1505    return;
1506  }
1507
[5639bf2]1508  owl_fmtext_init_null(&fm);
1509  owl_style_get_formattext(s, &fm, m);
1510
1511  owl_function_popless_fmtext(&fm);
[7ab0020]1512  owl_fmtext_cleanup(&fm);
[7d4fbcd]1513}
1514
[d54838d]1515void owl_function_page_curmsg(int step)
1516{
[7d4fbcd]1517  /* scroll down or up within the current message IF the message is truncated */
1518
1519  int offset, curmsg, lines;
[9e5c9f3]1520  const owl_view *v;
[7d4fbcd]1521  owl_message *m;
1522
1523  offset=owl_global_get_curmsg_vert_offset(&g);
1524  v=owl_global_get_current_view(&g);
1525  curmsg=owl_global_get_curmsg(&g);
1526  m=owl_view_get_element(v, curmsg);
[5eeea3b]1527  if (!m || owl_view_get_size(v)==0) return;
[7d4fbcd]1528  lines=owl_message_get_numlines(m);
1529
1530  if (offset==0) {
1531    /* Bail if the curmsg isn't the last one displayed */
1532    if (curmsg != owl_mainwin_get_last_msg(owl_global_get_mainwin(&g))) {
[f51bc78]1533      owl_function_makemsg("The entire message is already displayed");
[7d4fbcd]1534      return;
1535    }
1536   
1537    /* Bail if we're not truncated */
1538    if (!owl_mainwin_is_curmsg_truncated(owl_global_get_mainwin(&g))) {
[f51bc78]1539      owl_function_makemsg("The entire message is already displayed");
[7d4fbcd]1540      return;
1541    }
1542  }
1543 
1544 
1545  /* don't scroll past the last line */
1546  if (step>0) {
1547    if (offset+step > lines-1) {
1548      owl_global_set_curmsg_vert_offset(&g, lines-1);
1549    } else {
1550      owl_global_set_curmsg_vert_offset(&g, offset+step);
1551    }
1552  }
1553
1554  /* would we be before the beginning of the message? */
1555  if (step<0) {
1556    if (offset+step<0) {
1557      owl_global_set_curmsg_vert_offset(&g, 0);
1558    } else {
1559      owl_global_set_curmsg_vert_offset(&g, offset+step);
1560    }
1561  }
1562 
1563  /* redisplay */
1564  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1565  owl_global_set_needrefresh(&g);
1566}
1567
[d54838d]1568void owl_function_resize_typwin(int newsize)
1569{
[7d4fbcd]1570  owl_global_set_typwin_lines(&g, newsize);
1571  owl_function_resize();
1572}
1573
[c79a047]1574void owl_function_mainwin_pagedown(void)
[d54838d]1575{
[7d4fbcd]1576  int i;
1577
1578  i=owl_mainwin_get_last_msg(owl_global_get_mainwin(&g));
1579  if (i<0) return;
[f2e36b5]1580  if (owl_mainwin_is_last_msg_truncated(owl_global_get_mainwin(&g))
1581      && (owl_global_get_curmsg(&g) < i)
1582      && (i>0)) {
1583    i--;
1584  }
[7d4fbcd]1585  owl_global_set_curmsg(&g, i);
1586  owl_function_nextmsg();
1587}
1588
[c79a047]1589void owl_function_mainwin_pageup(void)
[d54838d]1590{
[7d4fbcd]1591  owl_global_set_curmsg(&g, owl_global_get_topmsg(&g));
1592  owl_function_prevmsg();
1593}
1594
[c79a047]1595void owl_function_getsubs(void)
[d54838d]1596{
[09489b89]1597  char *buff;
[7d4fbcd]1598
[09489b89]1599  buff=owl_zephyr_getsubs();
[7d4fbcd]1600
[09489b89]1601  if (buff) {
1602    owl_function_popless_text(buff);
1603  } else {
1604    owl_function_popless_text("Error getting subscriptions");
[7d4fbcd]1605  }
[09489b89]1606           
[1c6c4d3]1607  owl_free(buff);
[7d4fbcd]1608}
1609
[c79a047]1610void owl_function_printallvars(void)
[d54838d]1611{
[e19eb97]1612  const char *name;
[b4c270c]1613  char var[LINE];
[7d4fbcd]1614  owl_list varnames;
[b4c270c]1615  int i, numvarnames;
1616  GString *str   = g_string_new("");
[7d4fbcd]1617
[b4c270c]1618  g_string_append_printf(str, "%-20s = %s\n", "VARIABLE", "VALUE");
1619  g_string_append_printf(str, "%-20s   %s\n",  "--------", "-----");
[7d4fbcd]1620  owl_variable_dict_get_names(owl_global_get_vardict(&g), &varnames);
1621  numvarnames = owl_list_get_size(&varnames);
1622  for (i=0; i<numvarnames; i++) {
1623    name = owl_list_get_element(&varnames, i);
1624    if (name && name[0]!='_') {
[b4c270c]1625      g_string_append_printf(str, "\n%-20s = ", name);
1626      owl_variable_get_tostring(owl_global_get_vardict(&g), name, var, LINE);
1627      g_string_append(str, var);
[7d4fbcd]1628    }
1629  }
[b4c270c]1630  g_string_append(str, "\n");
[0e57335]1631  owl_variable_dict_namelist_cleanup(&varnames);
[b4c270c]1632
1633  owl_function_popless_text(str->str);
1634  g_string_free(str, TRUE);
[7d4fbcd]1635}
1636
[c79a047]1637void owl_function_show_variables(void)
[d54838d]1638{
[7d4fbcd]1639  owl_list varnames;
1640  owl_fmtext fm; 
1641  int i, numvarnames;
[e19eb97]1642  const char *varname;
[7d4fbcd]1643
1644  owl_fmtext_init_null(&fm);
1645  owl_fmtext_append_bold(&fm, 
1646      "Variables: (use 'show variable <name>' for details)\n");
1647  owl_variable_dict_get_names(owl_global_get_vardict(&g), &varnames);
1648  numvarnames = owl_list_get_size(&varnames);
1649  for (i=0; i<numvarnames; i++) {
1650    varname = owl_list_get_element(&varnames, i);
1651    if (varname && varname[0]!='_') {
[aa2f33b3]1652      owl_variable_describe(owl_global_get_vardict(&g), varname, &fm);
[7d4fbcd]1653    }
1654  }
[0e57335]1655  owl_variable_dict_namelist_cleanup(&varnames);
[7d4fbcd]1656  owl_function_popless_fmtext(&fm);
[7ab0020]1657  owl_fmtext_cleanup(&fm);
[7d4fbcd]1658}
1659
[e19eb97]1660void owl_function_show_variable(const char *name)
[d54838d]1661{
[7d4fbcd]1662  owl_fmtext fm; 
1663
1664  owl_fmtext_init_null(&fm);
1665  owl_variable_get_help(owl_global_get_vardict(&g), name, &fm);
1666  owl_function_popless_fmtext(&fm);
[7ab0020]1667  owl_fmtext_cleanup(&fm);
[7d4fbcd]1668}
1669
1670/* note: this applies to global message list, not to view.
1671 * If flag is 1, deletes.  If flag is 0, undeletes. */
[d54838d]1672void owl_function_delete_by_id(int id, int flag)
1673{
[3eb599d]1674  const owl_messagelist *ml;
[7d4fbcd]1675  owl_message *m;
1676  ml = owl_global_get_msglist(&g);
1677  m = owl_messagelist_get_by_id(ml, id);
1678  if (m) {
1679    if (flag == 1) {
1680      owl_message_mark_delete(m);
1681    } else if (flag == 0) {
1682      owl_message_unmark_delete(m);
1683    }
1684    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1685    owl_global_set_needrefresh(&g);
1686  } else {
[ec6ff52]1687    owl_function_error("No message with id %d: unable to mark for (un)delete",id);
[7d4fbcd]1688  }
1689}
1690
[c79a047]1691void owl_function_delete_automsgs(void)
[d54838d]1692{
[7d4fbcd]1693  /* mark for deletion all messages in the current view that match the
1694   * 'trash' filter */
1695
1696  int i, j, count;
1697  owl_message *m;
[9e5c9f3]1698  const owl_view *v;
[4542047]1699  const owl_filter *f;
[7d4fbcd]1700
1701  /* get the trash filter */
1702  f=owl_global_get_filter(&g, "trash");
1703  if (!f) {
[ec6ff52]1704    owl_function_error("No trash filter defined");
[7d4fbcd]1705    return;
1706  }
1707
1708  v=owl_global_get_current_view(&g);
1709
1710  count=0;
1711  j=owl_view_get_size(v);
1712  for (i=0; i<j; i++) {
1713    m=owl_view_get_element(v, i);
1714    if (owl_filter_message_match(f, m)) {
1715      count++;
1716      owl_message_mark_delete(m);
1717    }
1718  }
1719  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
[1c6c4d3]1720  owl_function_makemsg("%i messages marked for deletion", count);
[7d4fbcd]1721  owl_global_set_needrefresh(&g);
1722}
1723
[c79a047]1724void owl_function_status(void)
[d54838d]1725{
[49a8434]1726  char buff[MAXPATHLEN+1];
[7d4fbcd]1727  time_t start;
1728  int up, days, hours, minutes;
[a352335c]1729  owl_fmtext fm;
1730
1731  owl_fmtext_init_null(&fm);
[7d4fbcd]1732
1733  start=owl_global_get_starttime(&g);
1734
[d9b0b972]1735  owl_fmtext_append_normal(&fm, "General Information:\n");
1736
1737  owl_fmtext_append_normal(&fm, "  Version: ");
[a352335c]1738  owl_fmtext_append_normal(&fm, OWL_VERSION_STRING);
1739  owl_fmtext_append_normal(&fm, "\n");
1740
[cdd3959]1741  owl_fmtext_append_normal(&fm, "  Startup Arguments: ");
[a352335c]1742  owl_fmtext_append_normal(&fm, owl_global_get_startupargs(&g));
1743  owl_fmtext_append_normal(&fm, "\n");
[b6a7367]1744
1745  owl_fmtext_append_normal(&fm, "  Current Directory: ");
[49a8434]1746  if(getcwd(buff, MAXPATHLEN) == NULL) {
1747    owl_fmtext_append_normal(&fm, "<Error in getcwd>");
1748  } else {
1749    owl_fmtext_append_normal(&fm, buff);
1750  }
[b6a7367]1751  owl_fmtext_append_normal(&fm, "\n");
1752
[c1d166b]1753  owl_fmtext_appendf_normal(&fm, "  Startup Time: %s", ctime(&start));
[7d4fbcd]1754
1755  up=owl_global_get_runtime(&g);
1756  days=up/86400;
1757  up-=days*86400;
1758  hours=up/3600;
1759  up-=hours*3600;
1760  minutes=up/60;
1761  up-=minutes*60;
[c1d166b]1762  owl_fmtext_appendf_normal(&fm, "  Run Time: %i days %2.2i:%2.2i:%2.2i\n", days, hours, minutes, up);
[7d4fbcd]1763
[d9b0b972]1764  owl_fmtext_append_normal(&fm, "\nProtocol Options:\n");
1765  owl_fmtext_append_normal(&fm, "  Zephyr included    : ");
1766  if (owl_global_is_havezephyr(&g)) {
1767    owl_fmtext_append_normal(&fm, "yes\n");
[7d4fbcd]1768  } else {
[d9b0b972]1769    owl_fmtext_append_normal(&fm, "no\n");
[7d4fbcd]1770  }
[d9b0b972]1771  owl_fmtext_append_normal(&fm, "  AIM included       : yes\n");
1772  owl_fmtext_append_normal(&fm, "  Loopback included  : yes\n");
1773
[8262340]1774
[d9b0b972]1775  owl_fmtext_append_normal(&fm, "\nBuild Options:\n");
1776  owl_fmtext_append_normal(&fm, "  Stderr redirection : ");
1777#if OWL_STDERR_REDIR
1778  owl_fmtext_append_normal(&fm, "yes\n");
1779#else
1780  owl_fmtext_append_normal(&fm, "no\n");
1781#endif
1782 
1783
1784  owl_fmtext_append_normal(&fm, "\nAIM Status:\n");
1785  owl_fmtext_append_normal(&fm, "  Logged in: ");
[a352335c]1786  if (owl_global_is_aimloggedin(&g)) {
1787    owl_fmtext_append_normal(&fm, owl_global_get_aim_screenname(&g));
1788    owl_fmtext_append_normal(&fm, "\n");
1789  } else {
[d9b0b972]1790    owl_fmtext_append_normal(&fm, "(not logged in)\n");
[a352335c]1791  }
[d9b0b972]1792
1793  owl_fmtext_append_normal(&fm, "  Processing events: ");
[a352335c]1794  if (owl_global_is_doaimevents(&g)) {
[d9b0b972]1795    owl_fmtext_append_normal(&fm, "yes\n");
[a352335c]1796  } else {
[d9b0b972]1797    owl_fmtext_append_normal(&fm, "no\n");
[a352335c]1798  }
1799
1800  owl_function_popless_fmtext(&fm);
[7ab0020]1801  owl_fmtext_cleanup(&fm);
[7d4fbcd]1802}
1803
[c79a047]1804void owl_function_show_term(void)
[d54838d]1805{
[7d4fbcd]1806  owl_fmtext fm;
1807
1808  owl_fmtext_init_null(&fm);
[c1d166b]1809  owl_fmtext_appendf_normal(&fm, "Terminal Lines: %i\nTerminal Columns: %i\n",
[7d4fbcd]1810          owl_global_get_lines(&g),
1811          owl_global_get_cols(&g));
1812
1813  if (owl_global_get_hascolors(&g)) {
1814    owl_fmtext_append_normal(&fm, "Color: Yes\n");
[c1d166b]1815    owl_fmtext_appendf_normal(&fm, "Number of color pairs: %i\n", owl_global_get_colorpairs(&g));
1816    owl_fmtext_appendf_normal(&fm, "Can change colors: %s\n", can_change_color() ? "yes" : "no");
[7d4fbcd]1817  } else {
1818    owl_fmtext_append_normal(&fm, "Color: No\n");
1819  }
1820
1821  owl_function_popless_fmtext(&fm);
[7ab0020]1822  owl_fmtext_cleanup(&fm);
[7d4fbcd]1823}
1824
[e7cc1c3]1825/* if type = 0 then normal reply.
1826 * if type = 1 then it's a reply to sender
1827 * if enter = 0 then allow the command to be edited
1828 * if enter = 1 then don't wait for editing
1829 */
[d54838d]1830void owl_function_reply(int type, int enter)
1831{
[740d5f7]1832  char *buff=NULL;
[c08c70a]1833  const owl_message *m;
[4542047]1834  const owl_filter *f;
[7d4fbcd]1835 
1836  if (owl_view_get_size(owl_global_get_current_view(&g))==0) {
[ec6ff52]1837    owl_function_error("No message selected");
[7d4fbcd]1838  } else {
[5ebc202]1839    char *cmd;
[7d4fbcd]1840   
1841    m=owl_view_get_element(owl_global_get_current_view(&g), owl_global_get_curmsg(&g));
[5eeea3b]1842    if (!m) {
[ec6ff52]1843      owl_function_error("No message selected");
[5eeea3b]1844      return;
1845    }
1846
[7d4fbcd]1847    /* first check if we catch the reply-lockout filter */
1848    f=owl_global_get_filter(&g, "reply-lockout");
1849    if (f) {
1850      if (owl_filter_message_match(f, m)) {
[ec6ff52]1851        owl_function_error("Sorry, replies to this message have been disabled by the reply-lockout filter");
[7d4fbcd]1852        return;
1853      }
1854    }
[4b464a4]1855
[2c09826]1856    /* then check if it's a question and just bring up the command prompt */
1857    if (owl_message_is_question(m)) {
1858      owl_function_start_command("");
1859      return;
1860    }
1861
[740d5f7]1862    if((type == 0 &&
1863        (cmd=owl_perlconfig_message_call_method(m, "replycmd", 0, NULL))) ||
1864       (type == 1 &&
1865        (cmd=owl_perlconfig_message_call_method(m, "replysendercmd", 0, NULL)))) {
1866      buff = cmd;
[d09e5a1]1867    }
[1b6b2f3]1868
[e0540e4]1869    if(!buff) {
1870        owl_function_error("I don't know how to reply to that message.");
1871        return;
1872    }
[740d5f7]1873
[d09e5a1]1874    if (enter) {
1875      owl_history *hist = owl_global_get_cmd_history(&g);
1876      owl_history_store(hist, buff);
1877      owl_history_reset(hist);
1878      owl_function_command_norv(buff);
1879    } else {
1880      owl_function_start_command(buff);
[7d4fbcd]1881    }
[d09e5a1]1882    owl_free(buff);
[7d4fbcd]1883  }
1884}
1885
[e19eb97]1886void owl_function_zlocate(int argc, const char *const *argv, int auth)
[d54838d]1887{
[2527615]1888  owl_fmtext fm;
[dca3b27]1889  char *ptr;
1890  char *result;
[2527615]1891  int i;
1892
1893  owl_fmtext_init_null(&fm);
[7d4fbcd]1894
[2527615]1895  for (i=0; i<argc; i++) {
[dca3b27]1896    ptr = long_zuser(argv[i]);
1897    result = owl_zephyr_zlocate(ptr, auth);
1898    owl_fmtext_append_normal(&fm, result);
1899    owl_free(result);
[2527615]1900    owl_free(ptr);
[7d4fbcd]1901  }
1902
[2527615]1903  owl_function_popless_fmtext(&fm);
[7ab0020]1904  owl_fmtext_cleanup(&fm);
[7d4fbcd]1905}
1906
[e19eb97]1907void owl_function_start_command(const char *line)
[d54838d]1908{
[7d4fbcd]1909  owl_editwin *tw;
1910
[38cc669]1911  owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
[7d4fbcd]1912  tw=owl_global_get_typwin(&g);
[10b866d]1913
[7d4fbcd]1914  owl_editwin_set_locktext(tw, "command: ");
1915  owl_global_set_needrefresh(&g);
1916
[47519e1b]1917  owl_editwin_insert_string(tw, line);
[2ee9e8d]1918  owl_editwin_redisplay(tw);
[cf83b7a]1919
[2a17b63]1920  owl_global_push_context(&g, OWL_CTX_EDITLINE, tw, "editline");
[cf83b7a]1921}
1922
[e19eb97]1923void owl_function_start_question(const char *line)
[cf83b7a]1924{
1925  owl_editwin *tw;
1926
[38cc669]1927  owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
[cf83b7a]1928  tw=owl_global_get_typwin(&g);
1929
1930  owl_editwin_set_locktext(tw, line);
1931  owl_global_set_needrefresh(&g);
1932
[2ee9e8d]1933  owl_editwin_redisplay(tw);
[cf83b7a]1934
[6b78afb]1935  owl_global_push_context(&g, OWL_CTX_EDITRESPONSE, tw, "editresponse");
[7d4fbcd]1936}
1937
[e19eb97]1938void owl_function_start_password(const char *line)
[453bd70]1939{
1940  owl_editwin *tw;
1941
[38cc669]1942  owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
1943  tw = owl_global_get_typwin(&g);
[453bd70]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