source: functions.c @ 1077891a

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