source: functions.c @ b3b1b05

release-1.7release-1.8release-1.9
Last change on this file since b3b1b05 was 4cf7b1b, checked in by David Benjamin <davidben@mit.edu>, 11 years ago
If owl_popwin_up fails, abort the command
  • Property mode set to 100644
File size: 94.9 KB
Line 
1#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4#include <signal.h>
5#include <netinet/in.h>
6#include <string.h>
7#include <time.h>
8#include <sys/types.h>
9#include <sys/stat.h>
10#include <sys/wait.h>
11#include <errno.h>
12#include <signal.h>
13#include "owl.h"
14#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  if (owl_popwin_up(pw) != 0) {
1243    owl_function_error("Popwin already in use.");
1244    return;
1245  }
1246  owl_global_push_context(&g, OWL_CTX_POPLESS, v, "popless", NULL);
1247  owl_viewwin_init_text(v, owl_popwin_get_content(pw), text);
1248}
1249
1250void owl_function_popless_fmtext(const owl_fmtext *fm)
1251{
1252  owl_popwin *pw;
1253  owl_viewwin *v;
1254
1255  pw=owl_global_get_popwin(&g);
1256  v=owl_global_get_viewwin(&g);
1257
1258  if (owl_popwin_up(pw) != 0) {
1259    owl_function_error("Popwin already in use.");
1260    return;
1261  }
1262  owl_global_push_context(&g, OWL_CTX_POPLESS, v, "popless", NULL);
1263  owl_viewwin_init_fmtext(v, owl_popwin_get_content(pw), fm);
1264}
1265
1266void owl_function_popless_file(const char *filename)
1267{
1268  owl_fmtext fm;
1269  FILE *file;
1270  char *s = NULL;
1271
1272  file=fopen(filename, "r");
1273  if (!file) {
1274    owl_function_error("Could not open file: %s", filename);
1275    return;
1276  }
1277
1278  owl_fmtext_init_null(&fm);
1279  while (owl_getline(&s, file))
1280    owl_fmtext_append_normal(&fm, s);
1281  owl_free(s);
1282
1283  owl_function_popless_fmtext(&fm);
1284  owl_fmtext_cleanup(&fm);
1285  fclose(file);
1286}
1287
1288void owl_function_about(void)
1289{
1290  owl_function_popless_text(
1291    "This is barnowl version " OWL_VERSION_STRING ".\n\n"
1292    "barnowl is a fork of the Owl zephyr client, written and\n"
1293    "maintained by Alejandro Sedeno and Nelson Elhage at the\n"
1294    "Massachusetts Institute of Technology. \n"
1295    "\n"
1296    "Owl was written by James Kretchmar. The first version, 0.5, was\n"
1297    "released in March 2002.\n"
1298    "\n"
1299    "The name 'owl' was chosen in reference to the owls in the\n"
1300    "Harry Potter novels, who are tasked with carrying messages\n"
1301    "between Witches and Wizards. The name 'barnowl' was chosen\n"
1302    "because we feel our owls should live closer to our ponies.\n"
1303    "\n"
1304    "Copyright (c) 2006-2010 The BarnOwl Developers. All rights reserved.\n"
1305    "Copyright (c) 2004 James Kretchmar. All rights reserved.\n"
1306    "Copyright 2002 Massachusetts Institute of Technology\n"
1307    "\n"
1308    "This program is free software. You can redistribute it and/or\n"
1309    "modify under the terms of the Sleepycat License. Use the \n"
1310    "':show license' command to display the full license\n"
1311  );
1312}
1313
1314void owl_function_info(void)
1315{
1316  const owl_message *m;
1317  owl_fmtext fm, attrfm;
1318  const owl_view *v;
1319#ifdef HAVE_LIBZEPHYR
1320  const ZNotice_t *n;
1321#endif
1322
1323  owl_fmtext_init_null(&fm);
1324 
1325  v=owl_global_get_current_view(&g);
1326  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1327  if (!m || owl_view_get_size(v)==0) {
1328    owl_function_error("No message selected\n");
1329    return;
1330  }
1331
1332  owl_fmtext_append_bold(&fm, "General Information:\n");
1333  owl_fmtext_appendf_normal(&fm, "  Msg Id    : %i\n", owl_message_get_id(m));
1334
1335  owl_fmtext_append_normal(&fm, "  Type      : ");
1336  owl_fmtext_append_bold(&fm, owl_message_get_type(m));
1337  owl_fmtext_append_normal(&fm, "\n");
1338
1339  if (owl_message_is_direction_in(m)) {
1340    owl_fmtext_append_normal(&fm, "  Direction : in\n");
1341  } else if (owl_message_is_direction_out(m)) {
1342    owl_fmtext_append_normal(&fm, "  Direction : out\n");
1343  } else if (owl_message_is_direction_none(m)) {
1344    owl_fmtext_append_normal(&fm, "  Direction : none\n");
1345  } else {
1346    owl_fmtext_append_normal(&fm, "  Direction : unknown\n");
1347  }
1348
1349  owl_fmtext_appendf_normal(&fm, "  Time      : %s\n", owl_message_get_timestr(m));
1350
1351  if (!owl_message_is_type_admin(m)) {
1352    owl_fmtext_appendf_normal(&fm, "  Sender    : %s\n", owl_message_get_sender(m));
1353    owl_fmtext_appendf_normal(&fm, "  Recipient : %s\n", owl_message_get_recipient(m));
1354  }
1355
1356  if (owl_message_is_type_zephyr(m)) {
1357    owl_fmtext_append_bold(&fm, "\nZephyr Specific Information:\n");
1358   
1359    owl_fmtext_appendf_normal(&fm, "  Class     : %s\n", owl_message_get_class(m));
1360    owl_fmtext_appendf_normal(&fm, "  Instance  : %s\n", owl_message_get_instance(m));
1361    owl_fmtext_appendf_normal(&fm, "  Opcode    : %s\n", owl_message_get_opcode(m));
1362#ifdef HAVE_LIBZEPHYR
1363    if (owl_message_is_direction_in(m)) {
1364      char *ptr, tmpbuff[1024];
1365      int i, j, fields, len;
1366
1367      n=owl_message_get_notice(m);
1368
1369      if (!owl_message_is_pseudo(m)) {
1370        owl_fmtext_append_normal(&fm, "  Kind      : ");
1371        if (n->z_kind==UNSAFE) {
1372          owl_fmtext_append_normal(&fm, "UNSAFE\n");
1373        } else if (n->z_kind==UNACKED) {
1374          owl_fmtext_append_normal(&fm, "UNACKED\n");
1375        } else if (n->z_kind==ACKED) {
1376          owl_fmtext_append_normal(&fm, "ACKED\n");
1377        } else if (n->z_kind==HMACK) {
1378          owl_fmtext_append_normal(&fm, "HMACK\n");
1379        } else if (n->z_kind==HMCTL) {
1380          owl_fmtext_append_normal(&fm, "HMCTL\n");
1381        } else if (n->z_kind==SERVACK) {
1382          owl_fmtext_append_normal(&fm, "SERVACK\n");
1383        } else if (n->z_kind==SERVNAK) {
1384          owl_fmtext_append_normal(&fm, "SERVNACK\n");
1385        } else if (n->z_kind==CLIENTACK) {
1386          owl_fmtext_append_normal(&fm, "CLIENTACK\n");
1387        } else if (n->z_kind==STAT) {
1388          owl_fmtext_append_normal(&fm, "STAT\n");
1389        } else {
1390          owl_fmtext_append_normal(&fm, "ILLEGAL VALUE\n");
1391        }
1392      }
1393      owl_fmtext_appendf_normal(&fm, "  Host      : %s\n", owl_message_get_hostname(m));
1394
1395      if (!owl_message_is_pseudo(m)) {
1396        owl_fmtext_append_normal(&fm, "\n");
1397        owl_fmtext_appendf_normal(&fm, "  Port      : %i\n", ntohs(n->z_port));
1398        owl_fmtext_appendf_normal(&fm, "  Auth      : %s\n", owl_zephyr_get_authstr(n));
1399
1400        /* FIXME make these more descriptive */
1401        owl_fmtext_appendf_normal(&fm, "  Checkd Ath: %i\n", n->z_checked_auth);
1402        owl_fmtext_appendf_normal(&fm, "  Multi notc: %s\n", n->z_multinotice);
1403        owl_fmtext_appendf_normal(&fm, "  Num other : %i\n", n->z_num_other_fields);
1404        owl_fmtext_appendf_normal(&fm, "  Msg Len   : %i\n", n->z_message_len);
1405
1406        fields=owl_zephyr_get_num_fields(n);
1407        owl_fmtext_appendf_normal(&fm, "  Fields    : %i\n", fields);
1408
1409        for (i=0; i<fields; i++) {
1410          ptr=owl_zephyr_get_field_as_utf8(n, i+1);
1411          len=strlen(ptr);
1412          if (len<30) {
1413            strncpy(tmpbuff, ptr, len);
1414            tmpbuff[len]='\0';
1415          } else {
1416            strncpy(tmpbuff, ptr, 30);
1417            tmpbuff[30]='\0';
1418            strcat(tmpbuff, "...");
1419          }
1420          owl_free(ptr);
1421
1422          for (j=0; j<strlen(tmpbuff); j++) {
1423            if (tmpbuff[j]=='\n') tmpbuff[j]='~';
1424            if (tmpbuff[j]=='\r') tmpbuff[j]='!';
1425          }
1426
1427          owl_fmtext_appendf_normal(&fm, "  Field %i   : %s\n", i+1, tmpbuff);
1428        }
1429        owl_fmtext_appendf_normal(&fm, "  Default Fm: %s\n", n->z_default_format);
1430      }
1431
1432    }
1433#endif
1434  }
1435
1436  owl_fmtext_append_bold(&fm, "\nOwl Message Attributes:\n");
1437  owl_message_attributes_tofmtext(m, &attrfm);
1438  owl_fmtext_append_fmtext(&fm, &attrfm);
1439 
1440  owl_function_popless_fmtext(&fm);
1441  owl_fmtext_cleanup(&fm);
1442  owl_fmtext_cleanup(&attrfm);
1443}
1444
1445/* print the current message in a popup window.
1446 * Use the 'default' style regardless of whatever
1447 * style the user may be using
1448 */
1449void owl_function_curmsg_to_popwin(void)
1450{
1451  const owl_view *v;
1452  const owl_message *m;
1453  const owl_style *s;
1454  owl_fmtext fm;
1455
1456  v=owl_global_get_current_view(&g);
1457  s=owl_global_get_style_by_name(&g, "default");
1458
1459  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1460
1461  if (!m || owl_view_get_size(v)==0) {
1462    owl_function_error("No current message");
1463    return;
1464  }
1465
1466  owl_fmtext_init_null(&fm);
1467  owl_style_get_formattext(s, &fm, m);
1468
1469  owl_function_popless_fmtext(&fm);
1470  owl_fmtext_cleanup(&fm);
1471}
1472
1473void owl_function_page_curmsg(int step)
1474{
1475  /* scroll down or up within the current message IF the message is truncated */
1476
1477  int offset, curmsg, lines;
1478  const owl_view *v;
1479  owl_message *m;
1480
1481  offset=owl_global_get_curmsg_vert_offset(&g);
1482  v=owl_global_get_current_view(&g);
1483  curmsg=owl_global_get_curmsg(&g);
1484  m=owl_view_get_element(v, curmsg);
1485  if (!m || owl_view_get_size(v)==0) return;
1486  lines=owl_message_get_numlines(m);
1487
1488  if (offset==0) {
1489    /* Bail if the curmsg isn't the last one displayed */
1490    if (curmsg != owl_mainwin_get_last_msg(owl_global_get_mainwin(&g))) {
1491      owl_function_makemsg("The entire message is already displayed");
1492      return;
1493    }
1494   
1495    /* Bail if we're not truncated */
1496    if (!owl_mainwin_is_curmsg_truncated(owl_global_get_mainwin(&g))) {
1497      owl_function_makemsg("The entire message is already displayed");
1498      return;
1499    }
1500  }
1501 
1502 
1503  /* don't scroll past the last line */
1504  if (step>0) {
1505    if (offset+step > lines-1) {
1506      owl_global_set_curmsg_vert_offset(&g, lines-1);
1507    } else {
1508      owl_global_set_curmsg_vert_offset(&g, offset+step);
1509    }
1510  }
1511
1512  /* would we be before the beginning of the message? */
1513  if (step<0) {
1514    if (offset+step<0) {
1515      owl_global_set_curmsg_vert_offset(&g, 0);
1516    } else {
1517      owl_global_set_curmsg_vert_offset(&g, offset+step);
1518    }
1519  }
1520 
1521  /* redisplay */
1522  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1523}
1524
1525void owl_function_resize_typwin(int newsize)
1526{
1527  owl_global_set_typwin_lines(&g, newsize);
1528  owl_mainpanel_layout_contents(&g.mainpanel);
1529}
1530
1531void owl_function_mainwin_pagedown(void)
1532{
1533  int i;
1534
1535  i=owl_mainwin_get_last_msg(owl_global_get_mainwin(&g));
1536  if (i<0) return;
1537  if (owl_mainwin_is_last_msg_truncated(owl_global_get_mainwin(&g))
1538      && (owl_global_get_curmsg(&g) < i)
1539      && (i>0)) {
1540    i--;
1541  }
1542  owl_global_set_curmsg(&g, i);
1543  owl_function_nextmsg();
1544}
1545
1546void owl_function_mainwin_pageup(void)
1547{
1548  owl_global_set_curmsg(&g, owl_global_get_topmsg(&g));
1549  owl_function_prevmsg();
1550}
1551
1552void owl_function_getsubs(void)
1553{
1554  char *buff;
1555
1556  buff=owl_zephyr_getsubs();
1557
1558  if (buff) {
1559    owl_function_popless_text(buff);
1560  } else {
1561    owl_function_popless_text("Error getting subscriptions");
1562  }
1563           
1564  owl_free(buff);
1565}
1566
1567void owl_function_printallvars(void)
1568{
1569  const char *name;
1570  char var[LINE];
1571  owl_list varnames;
1572  int i, numvarnames;
1573  GString *str   = g_string_new("");
1574
1575  g_string_append_printf(str, "%-20s = %s\n", "VARIABLE", "VALUE");
1576  g_string_append_printf(str, "%-20s   %s\n",  "--------", "-----");
1577  owl_variable_dict_get_names(owl_global_get_vardict(&g), &varnames);
1578  numvarnames = owl_list_get_size(&varnames);
1579  for (i=0; i<numvarnames; i++) {
1580    name = owl_list_get_element(&varnames, i);
1581    if (name && name[0]!='_') {
1582      g_string_append_printf(str, "\n%-20s = ", name);
1583      owl_variable_get_tostring(owl_global_get_vardict(&g), name, var, LINE);
1584      g_string_append(str, var);
1585    }
1586  }
1587  g_string_append(str, "\n");
1588  owl_variable_dict_namelist_cleanup(&varnames);
1589
1590  owl_function_popless_text(str->str);
1591  g_string_free(str, TRUE);
1592}
1593
1594void owl_function_show_variables(void)
1595{
1596  owl_list varnames;
1597  owl_fmtext fm; 
1598  int i, numvarnames;
1599  const char *varname;
1600
1601  owl_fmtext_init_null(&fm);
1602  owl_fmtext_append_bold(&fm, 
1603      "Variables: (use 'show variable <name>' for details)\n");
1604  owl_variable_dict_get_names(owl_global_get_vardict(&g), &varnames);
1605  numvarnames = owl_list_get_size(&varnames);
1606  for (i=0; i<numvarnames; i++) {
1607    varname = owl_list_get_element(&varnames, i);
1608    if (varname && varname[0]!='_') {
1609      owl_variable_describe(owl_global_get_vardict(&g), varname, &fm);
1610    }
1611  }
1612  owl_variable_dict_namelist_cleanup(&varnames);
1613  owl_function_popless_fmtext(&fm);
1614  owl_fmtext_cleanup(&fm);
1615}
1616
1617void owl_function_show_variable(const char *name)
1618{
1619  owl_fmtext fm; 
1620
1621  owl_fmtext_init_null(&fm);
1622  owl_variable_get_help(owl_global_get_vardict(&g), name, &fm);
1623  owl_function_popless_fmtext(&fm);
1624  owl_fmtext_cleanup(&fm);
1625}
1626
1627/* note: this applies to global message list, not to view.
1628 * If flag is 1, deletes.  If flag is 0, undeletes. */
1629void owl_function_delete_by_id(int id, int flag)
1630{
1631  const owl_messagelist *ml;
1632  owl_message *m;
1633  ml = owl_global_get_msglist(&g);
1634  m = owl_messagelist_get_by_id(ml, id);
1635  if (m) {
1636    if (flag == 1) {
1637      owl_message_mark_delete(m);
1638    } else if (flag == 0) {
1639      owl_message_unmark_delete(m);
1640    }
1641    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1642  } else {
1643    owl_function_error("No message with id %d: unable to mark for (un)delete",id);
1644  }
1645}
1646
1647void owl_function_delete_automsgs(void)
1648{
1649  /* mark for deletion all messages in the current view that match the
1650   * 'trash' filter */
1651
1652  int i, j, count;
1653  owl_message *m;
1654  const owl_view *v;
1655  const owl_filter *f;
1656
1657  /* get the trash filter */
1658  f=owl_global_get_filter(&g, "trash");
1659  if (!f) {
1660    owl_function_error("No trash filter defined");
1661    return;
1662  }
1663
1664  v=owl_global_get_current_view(&g);
1665
1666  count=0;
1667  j=owl_view_get_size(v);
1668  for (i=0; i<j; i++) {
1669    m=owl_view_get_element(v, i);
1670    if (owl_filter_message_match(f, m)) {
1671      count++;
1672      owl_message_mark_delete(m);
1673    }
1674  }
1675  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1676  owl_function_makemsg("%i messages marked for deletion", count);
1677}
1678
1679void owl_function_status(void)
1680{
1681  char buff[MAXPATHLEN+1];
1682  time_t start;
1683  int up, days, hours, minutes;
1684  owl_fmtext fm;
1685
1686  owl_fmtext_init_null(&fm);
1687
1688  start=owl_global_get_starttime(&g);
1689
1690  owl_fmtext_append_normal(&fm, "General Information:\n");
1691
1692  owl_fmtext_append_normal(&fm, "  Version: ");
1693  owl_fmtext_append_normal(&fm, OWL_VERSION_STRING);
1694  owl_fmtext_append_normal(&fm, "\n");
1695
1696  owl_fmtext_append_normal(&fm, "  Startup Arguments: ");
1697  owl_fmtext_append_normal(&fm, owl_global_get_startupargs(&g));
1698  owl_fmtext_append_normal(&fm, "\n");
1699
1700  owl_fmtext_append_normal(&fm, "  Current Directory: ");
1701  if(getcwd(buff, MAXPATHLEN) == NULL) {
1702    owl_fmtext_append_normal(&fm, "<Error in getcwd>");
1703  } else {
1704    owl_fmtext_append_normal(&fm, buff);
1705  }
1706  owl_fmtext_append_normal(&fm, "\n");
1707
1708  owl_fmtext_appendf_normal(&fm, "  Startup Time: %s", ctime(&start));
1709
1710  up=owl_global_get_runtime(&g);
1711  days=up/86400;
1712  up-=days*86400;
1713  hours=up/3600;
1714  up-=hours*3600;
1715  minutes=up/60;
1716  up-=minutes*60;
1717  owl_fmtext_appendf_normal(&fm, "  Run Time: %i days %2.2i:%2.2i:%2.2i\n", days, hours, minutes, up);
1718
1719  owl_fmtext_append_normal(&fm, "\nProtocol Options:\n");
1720  owl_fmtext_append_normal(&fm, "  Zephyr included    : ");
1721  if (owl_global_is_havezephyr(&g)) {
1722    owl_fmtext_append_normal(&fm, "yes\n");
1723  } else {
1724    owl_fmtext_append_normal(&fm, "no\n");
1725  }
1726  owl_fmtext_append_normal(&fm, "  AIM included       : yes\n");
1727  owl_fmtext_append_normal(&fm, "  Loopback included  : yes\n");
1728
1729
1730  owl_fmtext_append_normal(&fm, "\nBuild Options:\n");
1731  owl_fmtext_append_normal(&fm, "  Stderr redirection : ");
1732#if OWL_STDERR_REDIR
1733  owl_fmtext_append_normal(&fm, "yes\n");
1734#else
1735  owl_fmtext_append_normal(&fm, "no\n");
1736#endif
1737 
1738
1739  owl_fmtext_append_normal(&fm, "\nAIM Status:\n");
1740  owl_fmtext_append_normal(&fm, "  Logged in: ");
1741  if (owl_global_is_aimloggedin(&g)) {
1742    owl_fmtext_append_normal(&fm, owl_global_get_aim_screenname(&g));
1743    owl_fmtext_append_normal(&fm, "\n");
1744  } else {
1745    owl_fmtext_append_normal(&fm, "(not logged in)\n");
1746  }
1747
1748  owl_fmtext_append_normal(&fm, "  Processing events: ");
1749  if (owl_global_is_doaimevents(&g)) {
1750    owl_fmtext_append_normal(&fm, "yes\n");
1751  } else {
1752    owl_fmtext_append_normal(&fm, "no\n");
1753  }
1754
1755  owl_function_popless_fmtext(&fm);
1756  owl_fmtext_cleanup(&fm);
1757}
1758
1759void owl_function_show_term(void)
1760{
1761  owl_fmtext fm;
1762
1763  owl_fmtext_init_null(&fm);
1764  owl_fmtext_appendf_normal(&fm, "Terminal Lines: %i\nTerminal Columns: %i\n",
1765          owl_global_get_lines(&g),
1766          owl_global_get_cols(&g));
1767
1768  if (owl_global_get_hascolors(&g)) {
1769    owl_fmtext_append_normal(&fm, "Color: Yes\n");
1770    owl_fmtext_appendf_normal(&fm, "Number of color pairs: %i\n", owl_global_get_colorpairs(&g));
1771    owl_fmtext_appendf_normal(&fm, "Can change colors: %s\n", can_change_color() ? "yes" : "no");
1772  } else {
1773    owl_fmtext_append_normal(&fm, "Color: No\n");
1774  }
1775
1776  owl_function_popless_fmtext(&fm);
1777  owl_fmtext_cleanup(&fm);
1778}
1779
1780/* if type = 0 then normal reply.
1781 * if type = 1 then it's a reply to sender
1782 * if enter = 0 then allow the command to be edited
1783 * if enter = 1 then don't wait for editing
1784 */
1785void owl_function_reply(int type, int enter)
1786{
1787  char *buff=NULL;
1788  const owl_message *m;
1789  const owl_filter *f;
1790 
1791  if (owl_view_get_size(owl_global_get_current_view(&g))==0) {
1792    owl_function_error("No message selected");
1793  } else {
1794    char *cmd;
1795   
1796    m=owl_view_get_element(owl_global_get_current_view(&g), owl_global_get_curmsg(&g));
1797    if (!m) {
1798      owl_function_error("No message selected");
1799      return;
1800    }
1801
1802    /* first check if we catch the reply-lockout filter */
1803    f=owl_global_get_filter(&g, "reply-lockout");
1804    if (f) {
1805      if (owl_filter_message_match(f, m)) {
1806        owl_function_error("Sorry, replies to this message have been disabled by the reply-lockout filter");
1807        return;
1808      }
1809    }
1810
1811    /* then check if it's a question and just bring up the command prompt */
1812    if (owl_message_is_question(m)) {
1813      owl_function_start_command("");
1814      return;
1815    }
1816
1817    if((type == 0 &&
1818        (cmd=owl_perlconfig_message_call_method(m, "replycmd", 0, NULL))) ||
1819       (type == 1 &&
1820        (cmd=owl_perlconfig_message_call_method(m, "replysendercmd", 0, NULL)))) {
1821      buff = cmd;
1822    }
1823
1824    if(!buff) {
1825        owl_function_error("I don't know how to reply to that message.");
1826        return;
1827    }
1828
1829    if (enter) {
1830      owl_history *hist = owl_global_get_cmd_history(&g);
1831      owl_history_store(hist, buff);
1832      owl_history_reset(hist);
1833      owl_function_command_norv(buff);
1834    } else {
1835      owl_function_start_command(buff);
1836    }
1837    owl_free(buff);
1838  }
1839}
1840
1841void owl_function_zlocate(int argc, const char *const *argv, int auth)
1842{
1843  owl_fmtext fm;
1844  char *ptr;
1845  char *result;
1846  int i;
1847
1848  owl_fmtext_init_null(&fm);
1849
1850  for (i=0; i<argc; i++) {
1851    ptr = long_zuser(argv[i]);
1852    result = owl_zephyr_zlocate(ptr, auth);
1853    owl_fmtext_append_normal(&fm, result);
1854    owl_free(result);
1855    owl_free(ptr);
1856  }
1857
1858  owl_function_popless_fmtext(&fm);
1859  owl_fmtext_cleanup(&fm);
1860}
1861
1862void owl_callback_command(owl_editwin *e)
1863{
1864  char *rv;
1865  const char *line = owl_editwin_get_text(e);
1866
1867  rv = owl_function_command(line);
1868   if (rv) {
1869    owl_function_makemsg("%s", rv);
1870    owl_free(rv);
1871  }
1872}
1873
1874void owl_function_start_command(const char *line)
1875{
1876  owl_editwin *tw;
1877
1878  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
1879
1880  owl_editwin_set_locktext(tw, "command: ");
1881
1882  owl_editwin_insert_string(tw, line);
1883
1884  owl_global_push_context(&g, OWL_CTX_EDITLINE, tw, "editline", owl_global_get_typwin_window(&g));
1885  owl_editwin_set_callback(tw, owl_callback_command);
1886}
1887
1888owl_editwin *owl_function_start_question(const char *line)
1889{
1890  owl_editwin *tw;
1891
1892  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
1893
1894  owl_editwin_set_locktext(tw, line);
1895
1896  owl_global_push_context(&g, OWL_CTX_EDITRESPONSE, tw, "editresponse", owl_global_get_typwin_window(&g));
1897  return tw;
1898}
1899
1900owl_editwin *owl_function_start_password(const char *line)
1901{
1902  owl_editwin *tw;
1903
1904  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, NULL);
1905
1906  owl_editwin_set_echochar(tw, '*');
1907
1908  owl_editwin_set_locktext(tw, line);
1909
1910  owl_global_push_context(&g, OWL_CTX_EDITRESPONSE, tw, "editresponse", owl_global_get_typwin_window(&g));
1911  return tw;
1912}
1913
1914char *owl_function_exec(int argc, const char *const *argv, const char *buff, int type)
1915{
1916  /* if type == 1 display in a popup
1917   * if type == 2 display an admin messages
1918   * if type == 0 return output
1919   * else display in a popup
1920   */
1921  const char *redirect = " 2>&1 < /dev/null";
1922  char *newbuff;
1923  char *out;
1924  FILE *p;
1925
1926#if OWL_STDERR_REDIR
1927  redirect = " < /dev/null";
1928#endif
1929
1930  if (argc<2) {
1931    owl_function_error("Wrong number of arguments to the exec command");
1932    return NULL;
1933  }
1934
1935  buff = skiptokens(buff, 1);
1936  newbuff = owl_sprintf("%s%s", buff, redirect);
1937
1938  if (type == OWL_OUTPUT_POPUP) {
1939    owl_popexec_new(newbuff);
1940  } else {
1941    p = popen(newbuff, "r");
1942    out = owl_slurp(p);
1943    pclose(p);
1944   
1945    if (type == OWL_OUTPUT_RETURN) {
1946      owl_free(newbuff);
1947      return out;
1948    } else if (type == OWL_OUTPUT_ADMINMSG) {
1949      owl_function_adminmsg(buff, out);
1950    }
1951    owl_free(out);
1952  }
1953  owl_free(newbuff);
1954  return NULL;
1955}
1956
1957char *owl_function_perl(int argc, const char *const *argv, const char *buff, int type)
1958{
1959  /* if type == 1 display in a popup
1960   * if type == 2 display an admin messages
1961   * if type == 0 return output
1962   * else display in a popup
1963   */
1964  char *perlout;
1965
1966  if (argc<2) {
1967    owl_function_error("Wrong number of arguments to perl command");
1968    return NULL;
1969  }
1970
1971  /* consume first token (argv[0]) */
1972  buff = skiptokens(buff, 1);
1973
1974  perlout = owl_perlconfig_execute(buff);
1975  if (perlout) { 
1976    if (type == OWL_OUTPUT_POPUP) {
1977      owl_function_popless_text(perlout);
1978    } else if (type == OWL_OUTPUT_ADMINMSG) {
1979      owl_function_adminmsg(buff, perlout);
1980    } else if (type == OWL_OUTPUT_RETURN) {
1981      return perlout;
1982    }
1983    owl_free(perlout);
1984  }
1985  return NULL;
1986}
1987
1988/* Change the filter associated with the current view.
1989 * This also figures out which message in the new filter
1990 * should have the pointer.
1991 */
1992void owl_function_change_currentview_filter(const char *filtname)
1993{
1994  owl_view *v;
1995  owl_filter *f;
1996  int curid=-1, newpos, curmsg;
1997  const owl_message *curm=NULL;
1998
1999  v=owl_global_get_current_view(&g);
2000
2001  curmsg=owl_global_get_curmsg(&g);
2002  if (curmsg==-1) {
2003    owl_function_debugmsg("Hit the curmsg==-1 case in change_view");
2004  } else {
2005    curm=owl_view_get_element(v, curmsg);
2006    if (curm) {
2007      curid=owl_message_get_id(curm);
2008      owl_view_save_curmsgid(v, curid);
2009    }
2010  }
2011
2012  f=owl_global_get_filter(&g, filtname);
2013  if (!f) {
2014    owl_function_error("Unknown filter %s", filtname);
2015    return;
2016  }
2017
2018  owl_view_new_filter(v, f);
2019
2020  /* Figure out what to set the current message to.
2021   * - If the view we're leaving has messages in it, go to the closest message
2022   *   to the last message pointed to in that view.
2023   * - If the view we're leaving is empty, try to restore the position
2024   *   from the last time we were in the new view.  */
2025  if (curm) {
2026    newpos = owl_view_get_nearest_to_msgid(v, curid);
2027  } else {
2028    newpos = owl_view_get_nearest_to_saved(v);
2029  }
2030
2031  owl_global_set_curmsg(&g, newpos);
2032  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
2033  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2034  owl_global_set_direction_downwards(&g);
2035}
2036
2037/* Create a new filter, or replace an existing one
2038 * with a new definition.
2039 */
2040void owl_function_create_filter(int argc, const char *const *argv)
2041{
2042  owl_filter *f;
2043  const owl_view *v;
2044  int inuse = 0;
2045
2046  if (argc < 2) {
2047    owl_function_error("Wrong number of arguments to filter command");
2048    return;
2049  }
2050
2051  owl_function_debugmsg("owl_function_create_filter: starting to create filter named %s", argv[1]);
2052
2053  v=owl_global_get_current_view(&g);
2054
2055  /* don't touch the all filter */
2056  if (!strcmp(argv[1], "all")) {
2057    owl_function_error("You may not change the 'all' filter.");
2058    return;
2059  }
2060
2061  /* deal with the case of trying change the filter color */
2062  if (argc==4 && !strcmp(argv[2], "-c")) {
2063    f=owl_global_get_filter(&g, argv[1]);
2064    if (!f) {
2065      owl_function_error("The filter '%s' does not exist.", argv[1]);
2066      return;
2067    }
2068    if (owl_util_string_to_color(argv[3])==OWL_COLOR_INVALID) {
2069      owl_function_error("The color '%s' is not available.", argv[3]);
2070      return;
2071    }
2072    owl_filter_set_fgcolor(f, owl_util_string_to_color(argv[3]));
2073    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2074    return;
2075  }
2076  if (argc==4 && !strcmp(argv[2], "-b")) {
2077    f=owl_global_get_filter(&g, argv[1]);
2078    if (!f) {
2079      owl_function_error("The filter '%s' does not exist.", argv[1]);
2080      return;
2081    }
2082    if (owl_util_string_to_color(argv[3])==OWL_COLOR_INVALID) {
2083      owl_function_error("The color '%s' is not available.", argv[3]);
2084      return;
2085    }
2086    owl_filter_set_bgcolor(f, owl_util_string_to_color(argv[3]));
2087    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2088    return;
2089  }
2090
2091  /* create the filter and check for errors */
2092  f = owl_filter_new(argv[1], argc-2, argv+2);
2093  if (f == NULL) {
2094    owl_function_error("Invalid filter");
2095    return;
2096  }
2097
2098  /* if the named filter is in use by the current view, remember it */
2099  if (!strcmp(owl_view_get_filtname(v), argv[1])) {
2100    inuse=1;
2101  }
2102
2103  /* if the named filter already exists, nuke it */
2104  if (owl_global_get_filter(&g, argv[1])) {
2105    owl_global_remove_filter(&g, argv[1]);
2106  }
2107
2108  /* add the filter */
2109  owl_global_add_filter(&g, f);
2110
2111  /* if it was in use by the current view then update */
2112  if (inuse) {
2113    owl_function_change_currentview_filter(argv[1]);
2114  }
2115  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2116}
2117
2118/* If 'filtername' does not start with 'not-' create a filter named
2119 * 'not-<filtername>' defined as "not filter <filtername>".  If the
2120 * filter 'not-<filtername>' already exists, do not overwrite it.  If
2121 * 'filtername' begins with 'not-' and a filter 'filtername' already
2122 * exists, then do nothing.  If the filter 'filtername' does not
2123 * exist, create it and define it as 'not filter <filtername>'
2124 *
2125 * Returns the name of the negated filter, which the caller must free.
2126 */
2127char *owl_function_create_negative_filter(const char *filtername)
2128{
2129  char *newname;
2130  const owl_filter *tmpfilt;
2131  const char *argv[5];
2132
2133  owl_function_debugmsg("owl_function_create_negative_filter");
2134 
2135  if (!strncmp(filtername, "not-", 4)) {
2136    newname=owl_strdup(filtername+4);
2137  } else {
2138    newname=owl_sprintf("not-%s", filtername);
2139  }
2140
2141  tmpfilt=owl_global_get_filter(&g, newname);
2142  if (!tmpfilt) {
2143    argv[0]="filter"; /* anything is fine here */
2144    argv[1]=newname;
2145    argv[2]="not";
2146    argv[3]="filter";
2147    argv[4]=filtername;
2148    owl_function_create_filter(5, argv);
2149  }
2150
2151  owl_function_debugmsg("owl_function_create_negative_filter: returning with %s", newname);
2152  return(newname);
2153}
2154
2155void owl_function_show_filters(void)
2156{
2157  const owl_filter *f;
2158  GList *fl;
2159  owl_fmtext fm;
2160
2161  owl_fmtext_init_null(&fm);
2162
2163  owl_fmtext_append_bold(&fm, "Filters:\n");
2164
2165  for (fl = g.filterlist; fl; fl = g_list_next(fl)) {
2166    f = fl->data;
2167    owl_fmtext_append_normal(&fm, "   ");
2168    if (owl_global_get_hascolors(&g)) {
2169      owl_fmtext_append_normal_color(&fm, owl_filter_get_name(f), owl_filter_get_fgcolor(f), owl_filter_get_bgcolor(f));
2170    } else {
2171      owl_fmtext_append_normal(&fm, owl_filter_get_name(f));
2172    }
2173    owl_fmtext_append_normal(&fm, "\n");
2174  }
2175  owl_function_popless_fmtext(&fm);
2176  owl_fmtext_cleanup(&fm);
2177}
2178
2179void owl_function_show_filter(const char *name)
2180{
2181  const owl_filter *f;
2182  char *buff, *tmp;
2183
2184  f=owl_global_get_filter(&g, name);
2185  if (!f) {
2186    owl_function_error("There is no filter named %s", name);
2187    return;
2188  }
2189  tmp = owl_filter_print(f);
2190  buff = owl_sprintf("%s: %s", owl_filter_get_name(f), tmp);
2191  owl_function_popless_text(buff);
2192  owl_free(buff);
2193  owl_free(tmp);
2194}
2195
2196void owl_function_show_zpunts(void)
2197{
2198  const owl_filter *f;
2199  const owl_list *fl;
2200  char buff[5000];
2201  char *tmp;
2202  owl_fmtext fm;
2203  int i, j;
2204
2205  owl_fmtext_init_null(&fm);
2206
2207  fl=owl_global_get_puntlist(&g);
2208  j=owl_list_get_size(fl);
2209  owl_fmtext_append_bold(&fm, "Active zpunt filters:\n");
2210
2211  for (i=0; i<j; i++) {
2212    f=owl_list_get_element(fl, i);
2213    snprintf(buff, sizeof(buff), "[% 2d] ", i+1);
2214    owl_fmtext_append_normal(&fm, buff);
2215    tmp = owl_filter_print(f);
2216    owl_fmtext_append_normal(&fm, tmp);
2217    owl_free(tmp);
2218  }
2219  owl_function_popless_fmtext(&fm);
2220  owl_fmtext_cleanup(&fm);
2221}
2222
2223/* Create a filter for a class, instance if one doesn't exist.  If
2224 * instance is NULL then catch all messgaes in the class.  Returns the
2225 * name of the filter, which the caller must free.
2226 * If 'related' is nonzero, encompass unclasses and .d classes as well.
2227 */
2228char *owl_function_classinstfilt(const char *c, const char *i, int related) 
2229{
2230  owl_filter *f;
2231  char *argbuff, *filtname;
2232  char *tmpclass, *tmpinstance = NULL;
2233  char *class, *instance = NULL;
2234
2235  if (related) {
2236    class = owl_util_baseclass(c);
2237    if (i) {
2238      instance = owl_util_baseclass(i);
2239    }
2240  } else {
2241    class = owl_strdup(c);
2242    if (i) {
2243      instance = owl_strdup(i);
2244    }
2245  }
2246
2247  /* name for the filter */
2248  if (!instance) {
2249    filtname = owl_sprintf("%sclass-%s", related ? "related-" : "", class);
2250  } else {
2251    filtname = owl_sprintf("%sclass-%s-instance-%s", related ? "related-" : "", class, instance);
2252  }
2253  /* downcase it */
2254  {
2255    char *temp = g_utf8_strdown(filtname, -1);
2256    if (temp) {
2257      owl_free(filtname);
2258      filtname = temp;
2259    }
2260  }
2261  /* turn spaces, single quotes, and double quotes into dots */
2262  owl_text_tr(filtname, ' ', '.');
2263  owl_text_tr(filtname, '\'', '.');
2264  owl_text_tr(filtname, '"', '.');
2265 
2266  /* if it already exists then go with it.  This lets users override */
2267  if (owl_global_get_filter(&g, filtname)) {
2268    goto done;
2269  }
2270
2271  /* create the new filter */
2272  tmpclass=owl_text_quote(class, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2273  owl_text_tr(tmpclass, ' ', '.');
2274  owl_text_tr(tmpclass, '\'', '.');
2275  owl_text_tr(tmpclass, '"', '.');
2276  if (instance) {
2277    tmpinstance=owl_text_quote(instance, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2278    owl_text_tr(tmpinstance, ' ', '.');
2279    owl_text_tr(tmpinstance, '\'', '.');
2280    owl_text_tr(tmpinstance, '"', '.');
2281  }
2282
2283  argbuff = owl_sprintf(related ? "class ^(un)*%s(\\.d)*$" : "class ^%s$", tmpclass);
2284  if (tmpinstance) {
2285    char *tmp = argbuff;
2286    argbuff = owl_sprintf(related ? "%s and ( instance ^(un)*%s(\\.d)*$ )" : "%s and instance ^%s$", tmp, tmpinstance);
2287    owl_free(tmp);
2288  }
2289  owl_free(tmpclass);
2290  if (tmpinstance) owl_free(tmpinstance);
2291
2292  f = owl_filter_new_fromstring(filtname, argbuff);
2293
2294  /* add it to the global list */
2295  owl_global_add_filter(&g, f);
2296
2297  owl_free(argbuff);
2298done:
2299  owl_free(class);
2300  if (instance) {
2301    owl_free(instance);
2302  }
2303  return(filtname);
2304}
2305
2306/* Create a filter for personal zephyrs to or from the specified
2307 * zephyr user.  Includes login/logout notifications for the user.
2308 * The name of the filter will be 'user-<user>'.  If a filter already
2309 * exists with this name, no new filter will be created.  This allows
2310 * the configuration to override this function.  Returns the name of
2311 * the filter, which the caller must free.
2312 */
2313char *owl_function_zuserfilt(const char *user)
2314{
2315  owl_filter *f;
2316  char *argbuff, *longuser, *esclonguser, *shortuser, *filtname;
2317
2318  /* stick the local realm on if it's not there */
2319  longuser=long_zuser(user);
2320  shortuser=short_zuser(user);
2321
2322  /* name for the filter */
2323  filtname=owl_sprintf("user-%s", shortuser);
2324
2325  /* if it already exists then go with it.  This lets users override */
2326  if (owl_global_get_filter(&g, filtname)) {
2327    return filtname;
2328  }
2329
2330  /* create the new-internal filter */
2331  esclonguser = owl_text_quote(longuser, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2332
2333  argbuff=owl_sprintf("( type ^zephyr$ and filter personal and "
2334      "( ( direction ^in$ and sender ^%1$s$ ) or ( direction ^out$ and "
2335      "recipient ^%1$s$ ) ) ) or ( ( class ^login$ ) and ( sender ^%1$s$ ) )",
2336      esclonguser);
2337
2338  f = owl_filter_new_fromstring(filtname, argbuff);
2339
2340  /* add it to the global list */
2341  owl_global_add_filter(&g, f);
2342
2343  /* free stuff */
2344  owl_free(argbuff);
2345  owl_free(longuser);
2346  owl_free(esclonguser);
2347  owl_free(shortuser);
2348
2349  return(filtname);
2350}
2351
2352/* Create a filter for AIM IM messages to or from the specified
2353 * screenname.  The name of the filter will be 'aimuser-<user>'.  If a
2354 * filter already exists with this name, no new filter will be
2355 * created.  This allows the configuration to override this function.
2356 * Returns the name of the filter, which the caller must free.
2357 */
2358char *owl_function_aimuserfilt(const char *user)
2359{
2360  owl_filter *f;
2361  char *argbuff, *filtname;
2362  char *escuser;
2363
2364  /* name for the filter */
2365  filtname=owl_sprintf("aimuser-%s", user);
2366
2367  /* if it already exists then go with it.  This lets users override */
2368  if (owl_global_get_filter(&g, filtname)) {
2369    return(owl_strdup(filtname));
2370  }
2371
2372  /* create the new-internal filter */
2373  escuser = owl_text_quote(user, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2374
2375  argbuff = owl_sprintf(
2376      "( type ^aim$ and ( ( sender ^%1$s$ and recipient ^%2$s$ ) or "
2377      "( sender ^%2$s$ and recipient ^%1$s$ ) ) )",
2378      escuser, owl_global_get_aim_screenname_for_filters(&g));
2379
2380  f = owl_filter_new_fromstring(filtname, argbuff);
2381
2382  /* add it to the global list */
2383  owl_global_add_filter(&g, f);
2384
2385  /* free stuff */
2386  owl_free(argbuff);
2387  owl_free(escuser);
2388
2389  return(filtname);
2390}
2391
2392char *owl_function_typefilt(const char *type)
2393{
2394  owl_filter *f;
2395  char *argbuff, *filtname, *esctype;
2396
2397  /* name for the filter */
2398  filtname=owl_sprintf("type-%s", type);
2399
2400  /* if it already exists then go with it.  This lets users override */
2401  if (owl_global_get_filter(&g, filtname)) {
2402    return filtname;
2403  }
2404
2405  /* create the new-internal filter */
2406  esctype = owl_text_quote(type, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2407
2408  argbuff = owl_sprintf("type ^%s$", esctype);
2409
2410  f = owl_filter_new_fromstring(filtname, argbuff);
2411
2412  /* add it to the global list */
2413  owl_global_add_filter(&g, f);
2414
2415  /* free stuff */
2416  owl_free(argbuff);
2417  owl_free(esctype);
2418
2419  return filtname;
2420}
2421
2422/* If flag is 1, marks for deletion.  If flag is 0,
2423 * unmarks for deletion. */
2424void owl_function_delete_curview_msgs(int flag)
2425{
2426  const owl_view *v;
2427  int i, j;
2428
2429  v=owl_global_get_current_view(&g);
2430  j=owl_view_get_size(v);
2431  for (i=0; i<j; i++) {
2432    if (flag == 1) {
2433      owl_message_mark_delete(owl_view_get_element(v, i));
2434    } else if (flag == 0) {
2435      owl_message_unmark_delete(owl_view_get_element(v, i));
2436    }
2437  }
2438
2439  owl_function_makemsg("%i messages marked for %sdeletion", j, flag?"":"un");
2440
2441  owl_mainwin_redisplay(owl_global_get_mainwin(&g)); 
2442}
2443
2444static char *owl_function_smartfilter_cc(const owl_message *m) {
2445  const char *ccs;
2446  char *filtname;
2447  char *text;
2448  owl_filter *f;
2449
2450  ccs = owl_message_get_attribute_value(m, "zephyr_ccs");
2451
2452  filtname = owl_sprintf("conversation-%s", ccs);
2453  owl_text_tr(filtname, ' ', '-');
2454
2455  if (owl_global_get_filter(&g, filtname)) {
2456    return filtname;
2457  }
2458
2459  text = owl_sprintf("type ^zephyr$ and filter personal and "
2460                     "zephyr_ccs ^%s%s%s$",
2461                     owl_getquoting(ccs), ccs, owl_getquoting(ccs));
2462
2463  f = owl_filter_new_fromstring(filtname, text);
2464
2465  owl_global_add_filter(&g, f);
2466
2467  owl_free(text);
2468
2469  return filtname;
2470}
2471
2472/* Create a filter based on the current message.  Returns the name of
2473 * a filter or null.  The caller must free this name.
2474 *
2475 * if the curmsg is a personal zephyr return a filter name
2476 *    to the zephyr conversation with that user.
2477 * If the curmsg is a zephyr class message, instance foo, recip *,
2478 *    return a filter name to the class, inst.
2479 * If the curmsg is a zephyr class message and type==0 then
2480 *    return a filter name for just the class.
2481 * If the curmsg is a zephyr class message and type==1 then
2482 *    return a filter name for the class and instance.
2483 * If the curmsg is a personal AIM message returna  filter
2484 *    name to the AIM conversation with that user
2485 */
2486char *owl_function_smartfilter(int type, int invert_related)
2487{
2488  const owl_view *v;
2489  const owl_message *m;
2490  char *zperson, *filtname=NULL;
2491  const char *argv[2];
2492  int related = owl_global_is_narrow_related(&g) ^ invert_related;
2493
2494  v=owl_global_get_current_view(&g);
2495  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
2496
2497  if (!m || owl_view_get_size(v)==0) {
2498    owl_function_error("No message selected\n");
2499    return(NULL);
2500  }
2501
2502  /* very simple handling of admin messages for now */
2503  if (owl_message_is_type_admin(m)) {
2504    return(owl_function_typefilt("admin"));
2505  }
2506
2507  /* very simple handling of loopback messages for now */
2508  if (owl_message_is_type_loopback(m)) {
2509    return(owl_function_typefilt("loopback"));
2510  }
2511
2512  /* aim messages */
2513  if (owl_message_is_type_aim(m)) {
2514    if (owl_message_is_direction_in(m)) {
2515      filtname=owl_function_aimuserfilt(owl_message_get_sender(m));
2516    } else if (owl_message_is_direction_out(m)) {
2517      filtname=owl_function_aimuserfilt(owl_message_get_recipient(m));
2518    }
2519    return(filtname);
2520  }
2521
2522  /* narrow personal and login messages to the sender or recip as appropriate */
2523  if (owl_message_is_type_zephyr(m)) {
2524    if (owl_message_is_personal(m) || owl_message_is_loginout(m)) {
2525      if (owl_message_get_attribute_value(m, "zephyr_ccs") != NULL) {
2526        return owl_function_smartfilter_cc(m);
2527      }
2528
2529      if (owl_message_is_direction_in(m)) {
2530        zperson=short_zuser(owl_message_get_sender(m));
2531      } else {
2532        zperson=short_zuser(owl_message_get_recipient(m));
2533      }
2534      filtname=owl_function_zuserfilt(zperson);
2535      owl_free(zperson);
2536      return(filtname);
2537    }
2538
2539    /* narrow class MESSAGE, instance foo, recip * messages to class, inst */
2540    if (!strcasecmp(owl_message_get_class(m), "message")) {
2541      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m), related);
2542      return(filtname);
2543    }
2544
2545    /* otherwise narrow to the class */
2546    if (type==0) {
2547      filtname=owl_function_classinstfilt(owl_message_get_class(m), NULL, related);
2548    } else if (type==1) {
2549      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m), related);
2550    }
2551    return(filtname);
2552  }
2553
2554  /* pass it off to perl */
2555  argv[0] = type ? "1" : "0";
2556  argv[1] = related ? "1" : "0";
2557  return owl_perlconfig_message_call_method(m, "smartfilter", 2, argv);
2558}
2559
2560void owl_function_smartzpunt(int type)
2561{
2562  /* Starts a zpunt command based on the current class,instance pair.
2563   * If type=0, uses just class.  If type=1, uses instance as well. */
2564  const owl_view *v;
2565  const owl_message *m;
2566  const char *cmdprefix, *mclass, *minst;
2567  char *cmd;
2568 
2569  v=owl_global_get_current_view(&g);
2570  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
2571
2572  if (!m || owl_view_get_size(v)==0) {
2573    owl_function_error("No message selected\n");
2574    return;
2575  }
2576
2577  /* for now we skip admin messages. */
2578  if (owl_message_is_type_admin(m)
2579      || owl_message_is_loginout(m)
2580      || !owl_message_is_type_zephyr(m)) {
2581    owl_function_error("smartzpunt doesn't support this message type.");
2582    return;
2583  }
2584
2585  mclass = owl_message_get_class(m);
2586  minst = owl_message_get_instance(m);
2587  if (!mclass || !*mclass || *mclass==' '
2588      || (!strcasecmp(mclass, "message") && !strcasecmp(minst, "personal"))
2589      || (type && (!minst || !*minst|| *minst==' '))) {
2590    owl_function_error("smartzpunt can't safely do this for <%s,%s>",
2591                         mclass, minst);
2592  } else {
2593    cmdprefix = "start-command zpunt ";
2594    cmd = owl_malloc(strlen(cmdprefix)+strlen(mclass)+strlen(minst)+10);
2595    strcpy(cmd, cmdprefix);
2596    strcat(cmd, owl_getquoting(mclass));
2597    strcat(cmd, mclass);
2598    strcat(cmd, owl_getquoting(mclass));
2599    if (type) {
2600      strcat(cmd, " ");
2601      strcat(cmd, owl_getquoting(minst));
2602      strcat(cmd, minst);
2603      strcat(cmd, owl_getquoting(minst));
2604    } else {
2605      strcat(cmd, " *");
2606    }
2607    owl_function_command(cmd);
2608    owl_free(cmd);
2609  }
2610}
2611
2612/* Set the color of the current view's filter to
2613 * be 'color'
2614 */
2615void owl_function_color_current_filter(const char *fgcolor, const char *bgcolor)
2616{
2617  const char *name;
2618
2619  name=owl_view_get_filtname(owl_global_get_current_view(&g));
2620  owl_function_color_filter(name, fgcolor, bgcolor);
2621}
2622
2623/* Set the color of the filter 'filter' to be 'color'.  If the color
2624 * name does not exist, return -1, if the filter does not exist or is
2625 * the "all" filter, return -2.  Return 0 on success
2626 */
2627int owl_function_color_filter(const char *filtname, const char *fgcolor, const char *bgcolor)
2628{
2629  owl_filter *f;
2630
2631  f=owl_global_get_filter(&g, filtname);
2632  if (!f) {
2633    owl_function_error("Unknown filter");
2634    return(-2);
2635  }
2636
2637  /* don't touch the all filter */
2638  if (!strcmp(filtname, "all")) {
2639    owl_function_error("You may not change the 'all' filter.");
2640    return(-2);
2641  }
2642
2643  if (owl_util_string_to_color(fgcolor)==OWL_COLOR_INVALID) {
2644    owl_function_error("No color named '%s' avilable.", fgcolor);
2645    return(-1);
2646  }
2647
2648
2649  if (bgcolor != NULL) {
2650    if (owl_util_string_to_color(bgcolor)==OWL_COLOR_INVALID) {
2651      owl_function_error("No color named '%s' avilable.", bgcolor);
2652      return(-1);
2653    }
2654    owl_filter_set_bgcolor(f, owl_util_string_to_color(bgcolor));
2655  }
2656  owl_filter_set_fgcolor(f, owl_util_string_to_color(fgcolor));
2657 
2658  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2659  return(0);
2660}
2661
2662void owl_function_show_colors(void)
2663{
2664  owl_fmtext fm;
2665  int i; 
2666 
2667  owl_fmtext_init_null(&fm);
2668  owl_fmtext_append_normal(&fm, "default: ");
2669  owl_fmtext_append_normal_color(&fm, "default\n", OWL_COLOR_DEFAULT, OWL_COLOR_DEFAULT);
2670
2671  owl_fmtext_append_normal(&fm,"red:      ");
2672  owl_fmtext_append_normal_color(&fm, "red\n", OWL_COLOR_RED, OWL_COLOR_DEFAULT);
2673
2674  owl_fmtext_append_normal(&fm,"green:    ");
2675  owl_fmtext_append_normal_color(&fm, "green\n", OWL_COLOR_GREEN, OWL_COLOR_DEFAULT);
2676
2677  owl_fmtext_append_normal(&fm,"yellow:   ");
2678  owl_fmtext_append_normal_color(&fm, "yellow\n", OWL_COLOR_YELLOW, OWL_COLOR_DEFAULT);
2679
2680  owl_fmtext_append_normal(&fm,"blue:     ");
2681  owl_fmtext_append_normal_color(&fm, "blue\n", OWL_COLOR_BLUE, OWL_COLOR_DEFAULT);
2682
2683  owl_fmtext_append_normal(&fm,"magenta:  ");
2684  owl_fmtext_append_normal_color(&fm, "magenta\n", OWL_COLOR_MAGENTA, OWL_COLOR_DEFAULT);
2685
2686  owl_fmtext_append_normal(&fm,"cyan:     ");
2687  owl_fmtext_append_normal_color(&fm, "cyan\n", OWL_COLOR_CYAN, OWL_COLOR_DEFAULT);
2688
2689  owl_fmtext_append_normal(&fm,"white:    ");
2690  owl_fmtext_append_normal_color(&fm, "white\n", OWL_COLOR_WHITE, OWL_COLOR_DEFAULT);
2691
2692  for(i = 8; i < COLORS; ++i) {
2693    char* str1 = owl_sprintf("%4i:     ",i);
2694    char* str2 = owl_sprintf("%i\n",i);
2695    owl_fmtext_append_normal(&fm,str1);
2696    owl_fmtext_append_normal_color(&fm, str2, i, OWL_COLOR_DEFAULT);
2697    owl_free(str1);
2698     owl_free(str2);
2699  }
2700 
2701  owl_function_popless_fmtext(&fm);
2702  owl_fmtext_cleanup(&fm);
2703}
2704
2705/* add the given class, inst, recip to the punt list for filtering.
2706 *   if direction==0 then punt
2707 *   if direction==1 then unpunt
2708 */
2709void owl_function_zpunt(const char *class, const char *inst, const char *recip, int direction)
2710{
2711  char *puntexpr, *classexpr, *instexpr, *recipexpr;
2712  char *quoted;
2713
2714  if (!strcmp(class, "*")) {
2715    classexpr = owl_sprintf("class .*");
2716  } else {
2717    quoted=owl_text_quote(class, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2718    owl_text_tr(quoted, ' ', '.');
2719    owl_text_tr(quoted, '\'', '.');
2720    owl_text_tr(quoted, '"', '.');
2721    classexpr = owl_sprintf("class ^(un)*%s(\\.d)*$", quoted);
2722    owl_free(quoted);
2723  }
2724  if (!strcmp(inst, "*")) {
2725    instexpr = owl_sprintf(" and instance .*");
2726  } else {
2727    quoted=owl_text_quote(inst, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2728    owl_text_tr(quoted, ' ', '.');
2729    owl_text_tr(quoted, '\'', '.');
2730    owl_text_tr(quoted, '"', '.');
2731    instexpr = owl_sprintf(" and instance ^(un)*%s(\\.d)*$", quoted);
2732    owl_free(quoted);
2733  }
2734  if (!strcmp(recip, "*")) {
2735    recipexpr = owl_sprintf("");
2736  } else {
2737    if(!strcmp(recip, "%me%")) {
2738      recip = owl_zephyr_get_sender();
2739    }
2740    quoted=owl_text_quote(recip, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2741    owl_text_tr(quoted, ' ', '.');
2742    owl_text_tr(quoted, '\'', '.');
2743    owl_text_tr(quoted, '"', '.');
2744    recipexpr = owl_sprintf(" and recipient ^%s$", quoted);
2745    owl_free(quoted);
2746  }
2747
2748  puntexpr = owl_sprintf("%s %s %s", classexpr, instexpr, recipexpr);
2749  owl_function_punt(puntexpr, direction);
2750  owl_free(puntexpr);
2751  owl_free(classexpr);
2752  owl_free(instexpr);
2753  owl_free(recipexpr);
2754}
2755
2756void owl_function_punt(const char *filter, int direction)
2757{
2758  owl_filter *f;
2759  owl_list *fl;
2760  int i, j;
2761  fl=owl_global_get_puntlist(&g);
2762
2763  /* first, create the filter */
2764  owl_function_debugmsg("About to filter %s", filter);
2765  f = owl_filter_new_fromstring("punt-filter", filter);
2766  if (f == NULL) {
2767    owl_function_error("Error creating filter for zpunt");
2768    return;
2769  }
2770
2771  /* Check for an identical filter */
2772  j=owl_list_get_size(fl);
2773  for (i=0; i<j; i++) {
2774    if (owl_filter_equiv(f, owl_list_get_element(fl, i))) {
2775      owl_function_debugmsg("found an equivalent punt filter");
2776      /* if we're punting, then just silently bow out on this duplicate */
2777      if (direction==0) {
2778        owl_filter_delete(f);
2779        return;
2780      }
2781
2782      /* if we're unpunting, then remove this filter from the puntlist */
2783      if (direction==1) {
2784        owl_filter_delete(owl_list_get_element(fl, i));
2785        owl_list_remove_element(fl, i);
2786        owl_filter_delete(f);
2787        return;
2788      }
2789    }
2790  }
2791
2792  owl_function_debugmsg("punting");
2793  /* If we're punting, add the filter to the global punt list */
2794  if (direction==0) {
2795    owl_list_append_element(fl, f);
2796  }
2797}
2798
2799void owl_function_show_keymaps(void)
2800{
2801  owl_list l;
2802  owl_fmtext fm;
2803  const owl_keymap *km;
2804  const owl_keyhandler *kh;
2805  int i, numkm;
2806  const char *kmname;
2807
2808  kh = owl_global_get_keyhandler(&g);
2809  owl_fmtext_init_null(&fm);
2810  owl_fmtext_append_bold(&fm, "Keymaps:   ");
2811  owl_fmtext_append_normal(&fm, "(use 'show keymap <name>' for details)\n");
2812  owl_keyhandler_get_keymap_names(kh, &l);
2813  owl_fmtext_append_list(&fm, &l, "\n", owl_function_keymap_summary);
2814  owl_fmtext_append_normal(&fm, "\n");
2815
2816  numkm = owl_list_get_size(&l);
2817  for (i=0; i<numkm; i++) {
2818    kmname = owl_list_get_element(&l, i);
2819    km = owl_keyhandler_get_keymap(kh, kmname);
2820    owl_fmtext_append_bold(&fm, "\n\n----------------------------------------------------------------------------------------------------\n\n");
2821    owl_keymap_get_details(km, &fm, 0);
2822  }
2823  owl_fmtext_append_normal(&fm, "\n");
2824 
2825  owl_function_popless_fmtext(&fm);
2826  owl_keyhandler_keymap_namelist_cleanup(&l);
2827  owl_fmtext_cleanup(&fm);
2828}
2829
2830char *owl_function_keymap_summary(const char *name)
2831{
2832  const owl_keymap *km
2833    = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), name);
2834  if (km) return owl_keymap_summary(km);
2835  else return(NULL);
2836}
2837
2838/* TODO: implement for real */
2839void owl_function_show_keymap(const char *name)
2840{
2841  owl_fmtext fm;
2842  const owl_keymap *km;
2843
2844  owl_fmtext_init_null(&fm);
2845  km = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), name);
2846  if (km) {
2847    owl_keymap_get_details(km, &fm, 1);
2848  } else {
2849    owl_fmtext_append_normal(&fm, "No such keymap...\n");
2850  } 
2851  owl_function_popless_fmtext(&fm);
2852  owl_fmtext_cleanup(&fm);
2853}
2854
2855void owl_function_help_for_command(const char *cmdname)
2856{
2857  owl_fmtext fm;
2858
2859  owl_fmtext_init_null(&fm);
2860  owl_cmd_get_help(owl_global_get_cmddict(&g), cmdname, &fm);
2861  owl_function_popless_fmtext(&fm); 
2862  owl_fmtext_cleanup(&fm);
2863}
2864
2865void owl_function_search_start(const char *string, int direction)
2866{
2867  /* direction is OWL_DIRECTION_DOWNWARDS or OWL_DIRECTION_UPWARDS or
2868   * OWL_DIRECTION_NONE */
2869  owl_regex re;
2870
2871  if (string && owl_regex_create_quoted(&re, string) == 0) {
2872    owl_global_set_search_re(&g, &re);
2873    owl_regex_cleanup(&re);
2874  } else {
2875    owl_global_set_search_re(&g, NULL);
2876  }
2877
2878  if (direction == OWL_DIRECTION_NONE)
2879    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2880  else
2881    owl_function_search_helper(0, direction);
2882}
2883
2884void owl_function_search_continue(int direction)
2885{
2886  /* direction is OWL_DIRECTION_DOWNWARDS or OWL_DIRECTION_UPWARDS */
2887  owl_function_search_helper(1, direction);
2888}
2889
2890void owl_function_search_helper(int mode, int direction)
2891{
2892  /* move to a message that contains the string.  If direction is
2893   * OWL_DIRECTION_DOWNWARDS then search fowards, if direction is
2894   * OWL_DIRECTION_UPWARDS then search backwards.
2895   *
2896   * If mode==0 then it will stay on the current message if it
2897   * contains the string.
2898   */
2899
2900  const owl_view *v;
2901  int viewsize, i, curmsg, start;
2902  owl_message *m;
2903
2904  v=owl_global_get_current_view(&g);
2905  viewsize=owl_view_get_size(v);
2906  curmsg=owl_global_get_curmsg(&g);
2907 
2908  if (viewsize==0) {
2909    owl_function_error("No messages present");
2910    return;
2911  }
2912
2913  if (mode==0) {
2914    start=curmsg;
2915  } else if (direction==OWL_DIRECTION_DOWNWARDS) {
2916    start=curmsg+1;
2917  } else {
2918    start=curmsg-1;
2919  }
2920
2921  /* bounds check */
2922  if (start>=viewsize || start<0) {
2923    owl_function_error("No further matches found");
2924    return;
2925  }
2926
2927  for (i=start; i<viewsize && i>=0;) {
2928    m=owl_view_get_element(v, i);
2929    if (owl_message_search(m, owl_global_get_search_re(&g))) {
2930      owl_global_set_curmsg(&g, i);
2931      owl_function_calculate_topmsg(direction);
2932      owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2933      if (direction==OWL_DIRECTION_DOWNWARDS) {
2934        owl_global_set_direction_downwards(&g);
2935      } else {
2936        owl_global_set_direction_upwards(&g);
2937      }
2938      return;
2939    }
2940    if (direction==OWL_DIRECTION_DOWNWARDS) {
2941      i++;
2942    } else {
2943      i--;
2944    }
2945    owl_function_mask_sigint(NULL);
2946    if(owl_global_is_interrupted(&g)) {
2947      owl_global_unset_interrupted(&g);
2948      owl_function_unmask_sigint(NULL);
2949      owl_function_makemsg("Search interrupted!");
2950      owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2951      return;
2952    }
2953    owl_function_unmask_sigint(NULL);
2954  }
2955  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2956  owl_function_error("No matches found");
2957}
2958
2959/* strips formatting from ztext and returns the unformatted text.
2960 * caller is responsible for freeing. */
2961char *owl_function_ztext_stylestrip(const char *zt)
2962{
2963  owl_fmtext fm;
2964  char *plaintext;
2965
2966  owl_fmtext_init_null(&fm);
2967  owl_fmtext_append_ztext(&fm, zt);
2968  plaintext = owl_fmtext_print_plain(&fm);
2969  owl_fmtext_cleanup(&fm);
2970  return(plaintext);
2971}
2972
2973/* Popup a buddylisting.  If filename is NULL use the default .anyone */
2974void owl_function_buddylist(int aim, int zephyr, const char *filename)
2975{
2976  int i, j, idle;
2977  int interrupted = 0;
2978  owl_fmtext fm;
2979  const owl_buddylist *bl;
2980  const owl_buddy *b;
2981  char *timestr;
2982#ifdef HAVE_LIBZEPHYR
2983  int x;
2984  owl_list anyone;
2985  const char *user;
2986  char *tmp;
2987  ZLocations_t location[200];
2988  int numlocs, ret;
2989#endif
2990
2991  owl_fmtext_init_null(&fm);
2992
2993  /* AIM first */
2994  if (aim && owl_global_is_aimloggedin(&g)) {
2995    bl=owl_global_get_buddylist(&g);
2996
2997    owl_fmtext_append_bold(&fm, "AIM users logged in:\n");
2998    /* we're assuming AIM for now */
2999    j=owl_buddylist_get_size(bl);
3000    for (i=0; i<j; i++) {
3001      b=owl_buddylist_get_buddy_n(bl, i);
3002      idle=owl_buddy_get_idle_time(b);
3003      if (idle!=0) {
3004        timestr=owl_util_minutes_to_timestr(idle);
3005      } else {
3006        timestr=owl_strdup("");
3007      }
3008      owl_fmtext_appendf_normal(&fm, "  %-20.20s %-12.12s\n", owl_buddy_get_name(b), timestr);
3009      owl_free(timestr);
3010    }
3011  }
3012
3013#ifdef HAVE_LIBZEPHYR
3014  if (zephyr) {
3015    if(!owl_global_is_havezephyr(&g)) {
3016      owl_function_error("Zephyr currently not available.");
3017    } else {
3018      owl_fmtext_append_bold(&fm, "Zephyr users logged in:\n");
3019      owl_list_create(&anyone);
3020      ret=owl_zephyr_get_anyone_list(&anyone, filename);
3021      if (ret) {
3022        if (errno == ENOENT) {
3023          owl_fmtext_append_normal(&fm, " You have not added any zephyr buddies.  Use the\n");
3024          owl_fmtext_append_normal(&fm, " command ':addbuddy zephyr ");
3025          owl_fmtext_append_bold(  &fm, "<username>");
3026          owl_fmtext_append_normal(&fm, "'.\n");
3027        } else {
3028          owl_fmtext_append_normal(&fm, " Could not read zephyr buddies from the .anyone file.\n");
3029        }
3030      } else {
3031        j=owl_list_get_size(&anyone);
3032        for (i=0; i<j; i++) {
3033          user=owl_list_get_element(&anyone, i);
3034          ret=ZLocateUser(zstr(user), &numlocs, ZAUTH);
3035
3036          owl_function_mask_sigint(NULL);
3037          if(owl_global_is_interrupted(&g)) {
3038            interrupted = 1;
3039            owl_global_unset_interrupted(&g);
3040            owl_function_unmask_sigint(NULL);
3041            owl_function_makemsg("Interrupted!");
3042            break;
3043          }
3044
3045          owl_function_unmask_sigint(NULL);
3046
3047          if (ret!=ZERR_NONE) {
3048            owl_function_error("Error getting location for %s", user);
3049            continue;
3050          }
3051
3052          numlocs=200;
3053          ret=ZGetLocations(location, &numlocs);
3054          if (ret==0) {
3055            for (x=0; x<numlocs; x++) {
3056              tmp=short_zuser(user);
3057              owl_fmtext_appendf_normal(&fm, "  %-10.10s %-24.24s %-12.12s  %20.20s\n",
3058                                        tmp,
3059                                        location[x].host,
3060                                        location[x].tty,
3061                                        location[x].time);
3062              owl_free(tmp);
3063            }
3064            if (numlocs>=200) {
3065              owl_fmtext_append_normal(&fm, "  Too many locations found for this user, truncating.\n");
3066            }
3067          }
3068        }
3069      }
3070      owl_list_cleanup(&anyone, owl_free);
3071    }
3072  }
3073#endif
3074
3075  if (aim && zephyr) {
3076    if (owl_perlconfig_is_function("BarnOwl::Hooks::_get_blist")) {
3077      char * perlblist = owl_perlconfig_execute("BarnOwl::Hooks::_get_blist()");
3078      if (perlblist) {
3079        owl_fmtext_append_ztext(&fm, perlblist);
3080        owl_free(perlblist);
3081      }
3082    }
3083  }
3084
3085  if(!interrupted) {
3086    owl_function_popless_fmtext(&fm);
3087  }
3088  owl_fmtext_cleanup(&fm);
3089}
3090
3091/* Dump messages in the current view to the file 'filename'. */
3092void owl_function_dump(const char *filename) 
3093{
3094  int i, j;
3095  owl_message *m;
3096  const owl_view *v;
3097  FILE *file;
3098  char *plaintext;
3099
3100  v=owl_global_get_current_view(&g);
3101
3102  /* in the future make it ask yes/no */
3103  /*
3104  ret=stat(filename, &sbuf);
3105  if (!ret) {
3106    ret=owl_function_askyesno("File exists, continue? [Y/n]");
3107    if (!ret) return;
3108  }
3109  */
3110
3111  file=fopen(filename, "w");
3112  if (!file) {
3113    owl_function_error("Error opening file");
3114    return;
3115  }
3116
3117  j=owl_view_get_size(v);
3118  for (i=0; i<j; i++) {
3119    m=owl_view_get_element(v, i);
3120    plaintext = owl_strip_format_chars(owl_message_get_text(m));
3121    if (plaintext) {
3122      fputs(plaintext, file);
3123      owl_free(plaintext);
3124    }
3125  }
3126  fclose(file);
3127  owl_function_makemsg("Messages dumped to %s", filename);
3128}
3129
3130void owl_function_do_newmsgproc(void)
3131{
3132  if (owl_global_get_newmsgproc(&g) && strcmp(owl_global_get_newmsgproc(&g), "")) {
3133    /* if there's a process out there, we need to check on it */
3134    if (owl_global_get_newmsgproc_pid(&g)) {
3135      owl_function_debugmsg("Checking on newmsgproc pid==%i", owl_global_get_newmsgproc_pid(&g));
3136      owl_function_debugmsg("Waitpid return is %i", waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG));
3137      waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG);
3138      if (waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG)==-1) {
3139        /* it exited */
3140        owl_global_set_newmsgproc_pid(&g, 0);
3141        owl_function_debugmsg("newmsgproc exited");
3142      } else {
3143        owl_function_debugmsg("newmsgproc did not exit");
3144      }
3145    }
3146   
3147    /* if it exited, fork & exec a new one */
3148    if (owl_global_get_newmsgproc_pid(&g)==0) {
3149      pid_t i;
3150      int myargc;
3151      i=fork();
3152      if (i) {
3153        /* parent set the child's pid */
3154        owl_global_set_newmsgproc_pid(&g, i);
3155        owl_function_debugmsg("I'm the parent and I started a new newmsgproc with pid %i", i);
3156      } else {
3157        /* child exec's the program */
3158        char **parsed;
3159        parsed=owl_parseline(owl_global_get_newmsgproc(&g), &myargc);
3160        if (myargc < 0) {
3161          owl_function_debugmsg("Could not parse newmsgproc '%s': unbalanced quotes?", owl_global_get_newmsgproc(&g));
3162        }
3163        if (myargc <= 0) {
3164          _exit(127);
3165        }
3166        parsed=owl_realloc(parsed, sizeof(*parsed) * (myargc+1));
3167        parsed[myargc] = NULL;
3168       
3169        owl_function_debugmsg("About to exec \"%s\" with %d arguments", parsed[0], myargc);
3170       
3171        execvp(parsed[0], parsed);
3172       
3173       
3174        /* was there an error exec'ing? */
3175        owl_function_debugmsg("Cannot run newmsgproc '%s': cannot exec '%s': %s", 
3176                              owl_global_get_newmsgproc(&g), parsed[0], strerror(errno));
3177        _exit(127);
3178      }
3179    }
3180  }
3181}
3182
3183/* print the xterm escape sequence to raise the window */
3184void owl_function_xterm_raise(void)
3185{
3186  printf("\033[5t");
3187}
3188
3189/* print the xterm escape sequence to deiconify the window */
3190void owl_function_xterm_deiconify(void)
3191{
3192  printf("\033[1t");
3193}
3194
3195/* Add the specified command to the startup file.  Eventually this
3196 * should be clever, and rewriting settings that will obviosly
3197 * override earlier settings with 'set' 'bindkey' and 'alias'
3198 * commands.  For now though we just remove any line that would
3199 * duplicate this one and then append this line to the end of
3200 * startupfile.
3201 */
3202void owl_function_addstartup(const char *buff)
3203{
3204  FILE *file;
3205  const char *filename;
3206
3207  filename=owl_global_get_startupfile(&g);
3208
3209  /* delete earlier copies */
3210  owl_util_file_deleteline(filename, buff, 1);
3211
3212  file=fopen(filename, "a");
3213  if (!file) {
3214    owl_function_error("Error opening startupfile for new command");
3215    return;
3216  }
3217
3218  /* add this line */
3219  fprintf(file, "%s\n", buff);
3220
3221  fclose(file);
3222}
3223
3224/* Remove the specified command from the startup file. */
3225void owl_function_delstartup(const char *buff)
3226{
3227  const char *filename;
3228  filename=owl_global_get_startupfile(&g);
3229  owl_util_file_deleteline(filename, buff, 1);
3230}
3231
3232/* Execute owl commands from the given filename.  If the filename
3233 * is NULL, use the default owl startup commands file.
3234 */
3235void owl_function_source(const char *filename)
3236{
3237  char *path;
3238  FILE *file;
3239  char *s = NULL;
3240  int fail_silent = 0;
3241
3242  if (!filename) {
3243    fail_silent = 1;
3244    path = owl_strdup(owl_global_get_startupfile(&g));
3245  } else {
3246    path = owl_util_makepath(filename);
3247  }
3248  file = fopen(path, "r");
3249  owl_free(path);
3250  if (!file) {
3251    if (!fail_silent) {
3252      owl_function_error("Error opening file: %s", filename);
3253    }
3254    return;
3255  }
3256  while (owl_getline_chomp(&s, file)) {
3257    if (s[0] == '\0' || s[0] == '#')
3258      continue;
3259    owl_function_command(s);
3260  }
3261
3262  owl_free(s);
3263  fclose(file);
3264}
3265
3266void owl_function_change_style(owl_view *v, const char *stylename)
3267{
3268  const owl_style *s;
3269
3270  s=owl_global_get_style_by_name(&g, stylename);
3271  if (!s) {
3272    owl_function_error("No style named %s", stylename);
3273    return;
3274  }
3275  owl_view_set_style(v, s);
3276  owl_messagelist_invalidate_formats(owl_global_get_msglist(&g));
3277  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
3278  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3279}
3280
3281void owl_function_toggleoneline(void)
3282{
3283  owl_view *v;
3284  const owl_style *s;
3285
3286  v=owl_global_get_current_view(&g);
3287  s=owl_view_get_style(v);
3288
3289  if (!owl_style_matches_name(s, "oneline")) {
3290    owl_function_change_style(v, "oneline");
3291  } else {
3292    owl_function_change_style(v, owl_global_get_default_style(&g));
3293  }
3294
3295  owl_messagelist_invalidate_formats(owl_global_get_msglist(&g));
3296  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
3297  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3298}
3299
3300void owl_function_error(const char *fmt, ...)
3301{
3302  static int in_error = 0;
3303  va_list ap;
3304  char *buff;
3305  const char *nl;
3306
3307  if (++in_error > 2) {
3308    /* More than two nested errors, bail immediately. */
3309    in_error--;
3310    return;
3311  }
3312
3313  va_start(ap, fmt);
3314  buff = g_strdup_vprintf(fmt, ap);
3315  va_end(ap);
3316
3317  owl_function_debugmsg("ERROR: %s", buff);
3318  owl_function_log_err(buff);
3319
3320  nl = strchr(buff, '\n');
3321
3322  /*
3323    Showing admin messages triggers a lot of code. If we have a
3324    recursive error call, that's the most likely candidate, so
3325    suppress the call in that case, to try to avoid infinite looping.
3326  */
3327
3328  if(nl && *(nl + 1) && in_error == 1) {
3329    /* Multiline error */
3330    owl_function_adminmsg("ERROR", buff);
3331  } else {
3332    owl_function_makemsg("[Error] %s", buff);
3333  }
3334
3335  owl_free(buff);
3336
3337  in_error--;
3338}
3339
3340void owl_function_log_err(const char *string)
3341{
3342  char *date;
3343  time_t now;
3344  char *buff;
3345
3346  now=time(NULL);
3347  date=owl_strdup(ctime(&now));
3348  date[strlen(date)-1]='\0';
3349
3350  buff = owl_sprintf("%s %s", date, string);
3351
3352  owl_errqueue_append_err(owl_global_get_errqueue(&g), buff);
3353
3354  owl_free(buff);
3355  owl_free(date);
3356}
3357
3358void owl_function_showerrs(void)
3359{
3360  owl_fmtext fm;
3361
3362  owl_fmtext_init_null(&fm);
3363  owl_fmtext_append_normal(&fm, "Errors:\n\n");
3364  owl_errqueue_to_fmtext(owl_global_get_errqueue(&g), &fm);
3365  owl_function_popless_fmtext(&fm);
3366}
3367
3368void owl_function_makemsg(const char *fmt, ...)
3369{
3370  va_list ap;
3371  char *str;
3372
3373  va_start(ap, fmt);
3374  str = g_strdup_vprintf(fmt, ap);
3375  va_end(ap);
3376
3377  owl_function_debugmsg("makemsg: %s", str);
3378  owl_msgwin_set_text(&g.msgwin, str);
3379}
3380
3381/* get locations for everyone in .anyone.  If 'notify' is '1' then
3382 * send a pseudo login or logout message for everyone not in sync with
3383 * the global zephyr buddy list.  The list is updated regardless of
3384 * the status of 'notify'.
3385 */
3386void owl_function_zephyr_buddy_check(int notify)
3387{
3388#ifdef HAVE_LIBZEPHYR
3389  int i, j;
3390  owl_list anyone;
3391  owl_zbuddylist *zbl;
3392  GList **zaldlist;
3393  GList *zaldptr;
3394  ZAsyncLocateData_t *zald;
3395  const char *user;
3396
3397  if (!owl_global_is_havezephyr(&g)) return;
3398  owl_global_set_pseudologin_notify(&g, notify);
3399  zbl = owl_global_get_zephyr_buddylist(&g);
3400  zaldlist = owl_global_get_zaldlist(&g);
3401
3402  /* Clear the existing ZALDs first. */
3403  zaldptr = g_list_first(*zaldlist);
3404  while (zaldptr) {
3405    ZFreeALD(zaldptr->data);
3406    owl_free(zaldptr->data);
3407    zaldptr = g_list_next(zaldptr);
3408  }
3409  g_list_free(*zaldlist);
3410  *zaldlist = NULL;
3411
3412  owl_list_create(&anyone);
3413  owl_zephyr_get_anyone_list(&anyone, NULL);
3414  j = owl_list_get_size(&anyone);
3415  for (i = 0; i < j; i++) {
3416    user = owl_list_get_element(&anyone, i);
3417    zald = owl_malloc(sizeof(ZAsyncLocateData_t));
3418    if (ZRequestLocations(zstr(user), zald, UNACKED, ZAUTH) == ZERR_NONE) {
3419      *zaldlist = g_list_append(*zaldlist, zald);
3420    } else {
3421      owl_free(zald);
3422    }
3423  }
3424
3425  owl_list_cleanup(&anyone, owl_free);
3426#endif
3427}
3428
3429void owl_function_aimsearch_results(const char *email, owl_list *namelist)
3430{
3431  owl_fmtext fm;
3432  int i, j;
3433
3434  owl_fmtext_init_null(&fm);
3435  owl_fmtext_append_normal(&fm, "AIM screennames associated with ");
3436  owl_fmtext_append_normal(&fm, email);
3437  owl_fmtext_append_normal(&fm, ":\n");
3438
3439  j=owl_list_get_size(namelist);
3440  for (i=0; i<j; i++) {
3441    owl_fmtext_append_normal(&fm, "  ");
3442    owl_fmtext_append_normal(&fm, owl_list_get_element(namelist, i));
3443    owl_fmtext_append_normal(&fm, "\n");
3444  }
3445
3446  owl_function_popless_fmtext(&fm);
3447  owl_fmtext_cleanup(&fm);
3448}
3449
3450int owl_function_get_color_count(void)
3451{
3452     return COLORS;
3453}
3454
3455void owl_function_mask_sigint(sigset_t *oldmask) {
3456  sigset_t intr;
3457
3458  sigemptyset(&intr);
3459  sigaddset(&intr, SIGINT);
3460  sigprocmask(SIG_BLOCK, &intr, oldmask);
3461}
3462
3463void owl_function_unmask_sigint(sigset_t *oldmask) {
3464  sigset_t intr;
3465
3466  sigemptyset(&intr);
3467  sigaddset(&intr, SIGINT);
3468  sigprocmask(SIG_UNBLOCK, &intr, oldmask);
3469}
3470
3471void _owl_function_mark_message(const owl_message *m)
3472{
3473  if (m) {
3474    owl_global_set_markedmsgid(&g, owl_message_get_id(m));
3475    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3476  }
3477}
3478
3479void owl_function_mark_message(void)
3480{
3481  const owl_message *m;
3482  const owl_view *v;
3483
3484  v=owl_global_get_current_view(&g);
3485
3486  /* bail if there's no current message */
3487  if (owl_view_get_size(v) < 1) {
3488    owl_function_error("No messages to mark");
3489    return;
3490  }
3491
3492  /* mark the message */
3493  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
3494  _owl_function_mark_message(m);
3495  owl_function_makemsg("Mark set");
3496}
3497
3498void owl_function_swap_cur_marked(void)
3499{
3500  int marked_id;
3501  const owl_message *m;
3502  const owl_view *v;
3503
3504  marked_id=owl_global_get_markedmsgid(&g);
3505  if (marked_id == -1) {
3506    owl_function_error("Mark not set.");
3507    return;
3508  }
3509
3510  v=owl_global_get_current_view(&g);
3511  /* bail if there's no current message */
3512  if (owl_view_get_size(v) < 1) {
3513    return;
3514  }
3515
3516  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
3517  _owl_function_mark_message(m);
3518  owl_global_set_curmsg(&g, owl_view_get_nearest_to_msgid(v, marked_id));
3519  owl_function_calculate_topmsg(OWL_DIRECTION_NONE);
3520  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3521  owl_global_set_direction_downwards(&g);
3522}
Note: See TracBrowser for help on using the repository browser.