source: functions.c @ a999d9e

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