source: functions.c @ f449096

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