source: functions.c @ 5934b87

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