source: functions.c @ 1b1cd2c

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