source: functions.c @ 303a9e1

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