source: functions.c @ 2a17b63

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