source: functions.c @ 03ca005

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