source: functions.c @ bcde7926

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