source: functions.c @ 402eb16f

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