source: functions.c @ d83621c4

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