source: functions.c @ d8671a1

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