source: functions.c @ b644688

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