source: functions.c @ 38cc669

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