source: functions.c @ 8dfb59c

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