source: functions.c @ 36486be

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