source: functions.c @ f7456bc

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