source: functions.c @ 57a66fe

Last change on this file since 57a66fe was 57a66fe, checked in by Anders Kaseorg <andersk@mit.edu>, 10 years ago
history: Make owl_history_store also reset It doesn’t make sense to run owl_history_store without owl_history_reset (except in one case where we know the current position is already the beginning and we’re about to owl_history_set_partial), so let’s merge these functions. Signed-off-by: Anders Kaseorg <andersk@mit.edu>
  • 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_function_command_norv(buff);
1843    } else {
1844      owl_function_start_command(buff);
1845    }
1846    g_free(buff);
1847  }
1848}
1849
1850void owl_function_zlocate(int argc, const char *const *argv, int auth)
1851{
1852  owl_fmtext fm;
1853  char *ptr;
1854  char *result;
1855  int i;
1856
1857  owl_fmtext_init_null(&fm);
1858
1859  for (i=0; i<argc; i++) {
1860    ptr = long_zuser(argv[i]);
1861    result = owl_zephyr_zlocate(ptr, auth);
1862    owl_fmtext_append_normal(&fm, result);
1863    g_free(result);
1864    g_free(ptr);
1865  }
1866
1867  owl_function_popless_fmtext(&fm);
1868  owl_fmtext_cleanup(&fm);
1869}
1870
1871void owl_callback_command(owl_editwin *e)
1872{
1873  char *rv;
1874  const char *line = owl_editwin_get_text(e);
1875
1876  rv = owl_function_command(line);
1877   if (rv) {
1878    owl_function_makemsg("%s", rv);
1879    g_free(rv);
1880  }
1881}
1882
1883void owl_function_start_command(const char *line)
1884{
1885  owl_editwin *tw;
1886  owl_context *ctx;
1887
1888  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
1889
1890  owl_editwin_set_locktext(tw, "command: ");
1891
1892  owl_editwin_insert_string(tw, line);
1893
1894  ctx = owl_editcontext_new(OWL_CTX_EDITLINE, tw, "editline",
1895                            owl_global_deactivate_editcontext, &g);
1896  owl_global_push_context_obj(&g, ctx);
1897  owl_editwin_set_callback(tw, owl_callback_command);
1898}
1899
1900owl_editwin *owl_function_start_question(const char *line)
1901{
1902  owl_editwin *tw;
1903  owl_context *ctx;
1904
1905  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
1906
1907  owl_editwin_set_locktext(tw, line);
1908
1909  ctx = owl_editcontext_new(OWL_CTX_EDITRESPONSE, tw, "editresponse",
1910                            owl_global_deactivate_editcontext, &g);
1911  owl_global_push_context_obj(&g, ctx);
1912  return tw;
1913}
1914
1915owl_editwin *owl_function_start_password(const char *line)
1916{
1917  owl_editwin *tw;
1918  owl_context *ctx;
1919
1920  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, NULL);
1921
1922  owl_editwin_set_echochar(tw, '*');
1923
1924  owl_editwin_set_locktext(tw, line);
1925
1926  ctx = owl_editcontext_new(OWL_CTX_EDITRESPONSE, tw, "editresponse",
1927                            owl_global_deactivate_editcontext, &g);
1928  owl_global_push_context_obj(&g, ctx);
1929  return tw;
1930}
1931
1932char *owl_function_exec(int argc, const char *const *argv, const char *buff, int type)
1933{
1934  /* if type == 1 display in a popup
1935   * if type == 2 display an admin messages
1936   * if type == 0 return output
1937   * else display in a popup
1938   */
1939  const char *redirect = " 2>&1 < /dev/null";
1940  char *newbuff;
1941  char *out;
1942  FILE *p;
1943
1944#if OWL_STDERR_REDIR
1945  redirect = " < /dev/null";
1946#endif
1947
1948  if (argc<2) {
1949    owl_function_error("Wrong number of arguments to the exec command");
1950    return NULL;
1951  }
1952
1953  buff = skiptokens(buff, 1);
1954  newbuff = g_strdup_printf("exec%s; %s", redirect, buff);
1955
1956  if (type == OWL_OUTPUT_POPUP) {
1957    owl_popexec_new(newbuff);
1958  } else {
1959    p = popen(newbuff, "r");
1960    out = owl_slurp(p);
1961    pclose(p);
1962   
1963    if (type == OWL_OUTPUT_RETURN) {
1964      g_free(newbuff);
1965      return out;
1966    } else if (type == OWL_OUTPUT_ADMINMSG) {
1967      owl_function_adminmsg(buff, out);
1968    }
1969    g_free(out);
1970  }
1971  g_free(newbuff);
1972  return NULL;
1973}
1974
1975char *owl_function_perl(int argc, const char *const *argv, const char *buff, int type)
1976{
1977  /* if type == 1 display in a popup
1978   * if type == 2 display an admin messages
1979   * if type == 0 return output
1980   * else display in a popup
1981   */
1982  char *perlout;
1983
1984  if (argc<2) {
1985    owl_function_error("Wrong number of arguments to perl command");
1986    return NULL;
1987  }
1988
1989  /* consume first token (argv[0]) */
1990  buff = skiptokens(buff, 1);
1991
1992  perlout = owl_perlconfig_execute(buff);
1993  if (perlout) { 
1994    if (type == OWL_OUTPUT_POPUP) {
1995      owl_function_popless_text(perlout);
1996    } else if (type == OWL_OUTPUT_ADMINMSG) {
1997      owl_function_adminmsg(buff, perlout);
1998    } else if (type == OWL_OUTPUT_RETURN) {
1999      return perlout;
2000    }
2001    g_free(perlout);
2002  }
2003  return NULL;
2004}
2005
2006/* Change the filter associated with the current view.
2007 * This also figures out which message in the new filter
2008 * should have the pointer.
2009 */
2010void owl_function_change_currentview_filter(const char *filtname)
2011{
2012  owl_view *v;
2013  owl_filter *f;
2014  int curid=-1, newpos, curmsg;
2015  const owl_message *curm=NULL;
2016
2017  v=owl_global_get_current_view(&g);
2018
2019  curmsg=owl_global_get_curmsg(&g);
2020  if (curmsg==-1) {
2021    owl_function_debugmsg("Hit the curmsg==-1 case in change_view");
2022  } else {
2023    curm=owl_view_get_element(v, curmsg);
2024    if (curm) {
2025      curid=owl_message_get_id(curm);
2026      owl_view_save_curmsgid(v, curid);
2027    }
2028  }
2029
2030  f=owl_global_get_filter(&g, filtname);
2031  if (!f) {
2032    owl_function_error("Unknown filter %s", filtname);
2033    return;
2034  }
2035
2036  owl_view_new_filter(v, f);
2037
2038  /* Figure out what to set the current message to.
2039   * - If the view we're leaving has messages in it, go to the closest message
2040   *   to the last message pointed to in that view.
2041   * - If the view we're leaving is empty, try to restore the position
2042   *   from the last time we were in the new view.  */
2043  if (curm) {
2044    newpos = owl_view_get_nearest_to_msgid(v, curid);
2045  } else {
2046    newpos = owl_view_get_nearest_to_saved(v);
2047  }
2048
2049  owl_global_set_curmsg(&g, newpos);
2050  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
2051  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2052  owl_global_set_direction_downwards(&g);
2053}
2054
2055/* Create a new filter, or replace an existing one
2056 * with a new definition.
2057 */
2058void owl_function_create_filter(int argc, const char *const *argv)
2059{
2060  owl_filter *f;
2061  const owl_view *v;
2062  int inuse = 0;
2063
2064  if (argc < 2) {
2065    owl_function_error("Wrong number of arguments to filter command");
2066    return;
2067  }
2068
2069  owl_function_debugmsg("owl_function_create_filter: starting to create filter named %s", argv[1]);
2070
2071  v=owl_global_get_current_view(&g);
2072
2073  /* don't touch the all filter */
2074  if (!strcmp(argv[1], "all")) {
2075    owl_function_error("You may not change the 'all' filter.");
2076    return;
2077  }
2078
2079  /* deal with the case of trying change the filter color */
2080  if (argc==4 && !strcmp(argv[2], "-c")) {
2081    f=owl_global_get_filter(&g, argv[1]);
2082    if (!f) {
2083      owl_function_error("The filter '%s' does not exist.", argv[1]);
2084      return;
2085    }
2086    if (owl_util_string_to_color(argv[3])==OWL_COLOR_INVALID) {
2087      owl_function_error("The color '%s' is not available.", argv[3]);
2088      return;
2089    }
2090    owl_filter_set_fgcolor(f, owl_util_string_to_color(argv[3]));
2091    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2092    return;
2093  }
2094  if (argc==4 && !strcmp(argv[2], "-b")) {
2095    f=owl_global_get_filter(&g, argv[1]);
2096    if (!f) {
2097      owl_function_error("The filter '%s' does not exist.", argv[1]);
2098      return;
2099    }
2100    if (owl_util_string_to_color(argv[3])==OWL_COLOR_INVALID) {
2101      owl_function_error("The color '%s' is not available.", argv[3]);
2102      return;
2103    }
2104    owl_filter_set_bgcolor(f, owl_util_string_to_color(argv[3]));
2105    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2106    return;
2107  }
2108
2109  /* create the filter and check for errors */
2110  f = owl_filter_new(argv[1], argc-2, argv+2);
2111  if (f == NULL) {
2112    owl_function_error("Invalid filter");
2113    return;
2114  }
2115
2116  /* if the named filter is in use by the current view, remember it */
2117  if (!strcmp(owl_view_get_filtname(v), argv[1])) {
2118    inuse=1;
2119  }
2120
2121  /* if the named filter already exists, nuke it */
2122  if (owl_global_get_filter(&g, argv[1])) {
2123    owl_global_remove_filter(&g, argv[1]);
2124  }
2125
2126  /* add the filter */
2127  owl_global_add_filter(&g, f);
2128
2129  /* if it was in use by the current view then update */
2130  if (inuse) {
2131    owl_function_change_currentview_filter(argv[1]);
2132  }
2133  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2134}
2135
2136/* If 'filtername' does not start with 'not-' create a filter named
2137 * 'not-<filtername>' defined as "not filter <filtername>".  If the
2138 * filter 'not-<filtername>' already exists, do not overwrite it.  If
2139 * 'filtername' begins with 'not-' and a filter 'filtername' already
2140 * exists, then do nothing.  If the filter 'filtername' does not
2141 * exist, create it and define it as 'not filter <filtername>'
2142 *
2143 * Returns the name of the negated filter, which the caller must free.
2144 */
2145char *owl_function_create_negative_filter(const char *filtername)
2146{
2147  char *newname;
2148  const owl_filter *tmpfilt;
2149  const char *argv[5];
2150
2151  owl_function_debugmsg("owl_function_create_negative_filter");
2152 
2153  if (!strncmp(filtername, "not-", 4)) {
2154    newname=g_strdup(filtername+4);
2155  } else {
2156    newname=g_strdup_printf("not-%s", filtername);
2157  }
2158
2159  tmpfilt=owl_global_get_filter(&g, newname);
2160  if (!tmpfilt) {
2161    argv[0]="filter"; /* anything is fine here */
2162    argv[1]=newname;
2163    argv[2]="not";
2164    argv[3]="filter";
2165    argv[4]=filtername;
2166    owl_function_create_filter(5, argv);
2167  }
2168
2169  owl_function_debugmsg("owl_function_create_negative_filter: returning with %s", newname);
2170  return(newname);
2171}
2172
2173void owl_function_show_filters(void)
2174{
2175  const owl_filter *f;
2176  GList *fl;
2177  owl_fmtext fm;
2178
2179  owl_fmtext_init_null(&fm);
2180
2181  owl_fmtext_append_bold(&fm, "Filters:\n");
2182
2183  for (fl = g.filterlist; fl; fl = g_list_next(fl)) {
2184    f = fl->data;
2185    owl_fmtext_append_normal(&fm, "   ");
2186    owl_fmtext_append_normal_color(&fm, owl_filter_get_name(f),
2187                                   owl_filter_get_fgcolor(f),
2188                                   owl_filter_get_bgcolor(f));
2189    owl_fmtext_append_normal(&fm, "\n");
2190  }
2191  owl_function_popless_fmtext(&fm);
2192  owl_fmtext_cleanup(&fm);
2193}
2194
2195void owl_function_show_filter(const char *name)
2196{
2197  const owl_filter *f;
2198  char *buff, *tmp;
2199
2200  f=owl_global_get_filter(&g, name);
2201  if (!f) {
2202    owl_function_error("There is no filter named %s", name);
2203    return;
2204  }
2205  tmp = owl_filter_print(f);
2206  buff = g_strdup_printf("%s: %s", owl_filter_get_name(f), tmp);
2207  owl_function_popless_text(buff);
2208  g_free(buff);
2209  g_free(tmp);
2210}
2211
2212void owl_function_show_zpunts(void)
2213{
2214  const owl_filter *f;
2215  const owl_list *fl;
2216  char *tmp;
2217  owl_fmtext fm;
2218  int i, j;
2219
2220  owl_fmtext_init_null(&fm);
2221
2222  fl=owl_global_get_puntlist(&g);
2223  j=owl_list_get_size(fl);
2224  owl_fmtext_append_bold(&fm, "Active zpunt filters:\n");
2225
2226  for (i=0; i<j; i++) {
2227    f=owl_list_get_element(fl, i);
2228    owl_fmtext_appendf_normal(&fm, "[% 2d] ", i+1);
2229    tmp = owl_filter_print(f);
2230    owl_fmtext_append_normal(&fm, tmp);
2231    g_free(tmp);
2232  }
2233  owl_function_popless_fmtext(&fm);
2234  owl_fmtext_cleanup(&fm);
2235}
2236
2237/* Create a filter for a class, instance if one doesn't exist.  If
2238 * instance is NULL then catch all messgaes in the class.  Returns the
2239 * name of the filter or null.  The caller must free this name.
2240 * If 'related' is nonzero, encompass unclasses and .d classes as well.
2241 */
2242char *owl_function_classinstfilt(const char *c, const char *i, int related) 
2243{
2244  owl_filter *f;
2245  char *filtname;
2246  char *tmpclass, *tmpinstance = NULL;
2247  char *class, *instance = NULL;
2248  GString *buf;
2249
2250  if (related) {
2251    class = owl_util_baseclass(c);
2252    if (i) {
2253      instance = owl_util_baseclass(i);
2254    }
2255  } else {
2256    class = g_strdup(c);
2257    if (i) {
2258      instance = g_strdup(i);
2259    }
2260  }
2261
2262  /* name for the filter */
2263  if (!instance) {
2264    filtname = g_strdup_printf("%sclass-%s", related ? "related-" : "", class);
2265  } else {
2266    filtname = g_strdup_printf("%sclass-%s-instance-%s", related ? "related-" : "", class, instance);
2267  }
2268  /* downcase it */
2269  {
2270    char *temp = g_utf8_strdown(filtname, -1);
2271    if (temp) {
2272      g_free(filtname);
2273      filtname = temp;
2274    }
2275  }
2276 
2277  /* if it already exists then go with it.  This lets users override */
2278  if (owl_global_get_filter(&g, filtname)) {
2279    goto done;
2280  }
2281
2282  /* create the new filter */
2283  tmpclass=owl_text_quote(class, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2284  if (instance) {
2285    tmpinstance=owl_text_quote(instance, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2286  }
2287
2288  buf = g_string_new("");
2289  owl_string_appendf_quoted(buf,
2290                            related ? "class ^(un)*%q(\\.d)*$" : "class ^%q$",
2291                            tmpclass);
2292
2293  if (tmpinstance) {
2294    owl_string_appendf_quoted(buf,
2295                              related ?
2296                              " and ( instance ^(un)*%q(\\.d)*$ )" :
2297                              " and instance ^%q$",
2298                              tmpinstance);
2299  }
2300  g_free(tmpclass);
2301  g_free(tmpinstance);
2302
2303  f = owl_filter_new_fromstring(filtname, buf->str);
2304  g_string_free(buf, true);
2305  if (f == NULL) {
2306    /* Couldn't make a filter for some reason. Return NULL. */
2307    owl_function_error("Error creating filter '%s'", filtname);
2308    g_free(filtname);
2309    filtname = NULL;
2310    goto done;
2311  }
2312
2313  /* add it to the global list */
2314  owl_global_add_filter(&g, f);
2315
2316done:
2317  g_free(class);
2318  g_free(instance);
2319  return(filtname);
2320}
2321
2322/* Create a filter for personal zephyrs to or from the specified
2323 * zephyr user.  Includes login/logout notifications for the user.
2324 * The name of the filter will be 'user-<shortuser>'.  If a filter already
2325 * exists with this name, no new filter will be created.  This allows
2326 * the configuration to override this function.  Returns the name of
2327 * the filter, which the caller must free.
2328 */
2329char *owl_function_zuserfilt(const char *longuser)
2330{
2331  owl_filter *f;
2332  char *argbuff, *esclonguser, *shortuser, *filtname;
2333
2334  /* name for the filter */
2335  shortuser = short_zuser(longuser);
2336  filtname = g_strdup_printf("user-%s", shortuser);
2337  g_free(shortuser);
2338
2339  /* if it already exists then go with it.  This lets users override */
2340  if (owl_global_get_filter(&g, filtname)) {
2341    return filtname;
2342  }
2343
2344  /* create the new-internal filter */
2345  esclonguser = owl_text_quote(longuser, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2346
2347  argbuff=owl_string_build_quoted("( type ^zephyr$ and filter personal and "
2348      "( ( direction ^in$ and sender ^%q$ ) or ( direction ^out$ and "
2349      "recipient ^%q$ ) ) ) or ( ( class ^login$ ) and ( sender ^%q$ ) )",
2350      esclonguser, esclonguser, esclonguser);
2351  g_free(esclonguser);
2352
2353  f = owl_filter_new_fromstring(filtname, argbuff);
2354  g_free(argbuff);
2355
2356  if (f == NULL) {
2357    /* Couldn't make a filter for some reason. Return NULL. */
2358    owl_function_error("Error creating filter '%s'", filtname);
2359    g_free(filtname);
2360    return NULL;
2361  }
2362
2363  /* add it to the global list */
2364  owl_global_add_filter(&g, f);
2365
2366  return(filtname);
2367}
2368
2369/* Create a filter for AIM IM messages to or from the specified
2370 * screenname.  The name of the filter will be 'aimuser-<user>'.  If a
2371 * filter already exists with this name, no new filter will be
2372 * created.  This allows the configuration to override this function.
2373 * Returns the name of the filter, which the caller must free.
2374 */
2375char *owl_function_aimuserfilt(const char *user)
2376{
2377  owl_filter *f;
2378  char *argbuff, *filtname;
2379  char *escuser;
2380
2381  /* name for the filter */
2382  filtname=g_strdup_printf("aimuser-%s", user);
2383
2384  /* if it already exists then go with it.  This lets users override */
2385  if (owl_global_get_filter(&g, filtname)) {
2386    return(g_strdup(filtname));
2387  }
2388
2389  /* create the new-internal filter */
2390  escuser = owl_text_quote(user, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2391
2392  argbuff = g_strdup_printf(
2393      "( type ^aim$ and ( ( sender ^%1$s$ and recipient ^%2$s$ ) or "
2394      "( sender ^%2$s$ and recipient ^%1$s$ ) ) )",
2395      escuser, owl_global_get_aim_screenname_for_filters(&g));
2396  g_free(escuser);
2397
2398  f = owl_filter_new_fromstring(filtname, argbuff);
2399  g_free(argbuff);
2400
2401  if (f == NULL) {
2402    owl_function_error("Error creating filter '%s'", filtname);
2403    g_free(filtname);
2404    return NULL;
2405  }
2406
2407  /* add it to the global list */
2408  owl_global_add_filter(&g, f);
2409
2410  return(filtname);
2411}
2412
2413char *owl_function_typefilt(const char *type)
2414{
2415  owl_filter *f;
2416  char *argbuff, *filtname, *esctype;
2417
2418  /* name for the filter */
2419  filtname=g_strdup_printf("type-%s", type);
2420
2421  /* if it already exists then go with it.  This lets users override */
2422  if (owl_global_get_filter(&g, filtname)) {
2423    return filtname;
2424  }
2425
2426  /* create the new-internal filter */
2427  esctype = owl_text_quote(type, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2428
2429  argbuff = owl_string_build_quoted("type ^%q$", esctype);
2430  g_free(esctype);
2431
2432  f = owl_filter_new_fromstring(filtname, argbuff);
2433  g_free(argbuff);
2434
2435  if (f == NULL) {
2436    owl_function_error("Error creating filter '%s'", filtname);
2437    g_free(filtname);
2438    return NULL;
2439  }
2440
2441  /* add it to the global list */
2442  owl_global_add_filter(&g, f);
2443
2444  return filtname;
2445}
2446
2447/* If flag is 1, marks for deletion.  If flag is 0,
2448 * unmarks for deletion. */
2449void owl_function_delete_curview_msgs(int flag)
2450{
2451  const owl_view *v;
2452  int i, j;
2453
2454  v=owl_global_get_current_view(&g);
2455  j=owl_view_get_size(v);
2456  for (i=0; i<j; i++) {
2457    if (flag == 1) {
2458      owl_message_mark_delete(owl_view_get_element(v, i));
2459    } else if (flag == 0) {
2460      owl_message_unmark_delete(owl_view_get_element(v, i));
2461    }
2462  }
2463
2464  owl_function_makemsg("%i messages marked for %sdeletion", j, flag?"":"un");
2465
2466  owl_mainwin_redisplay(owl_global_get_mainwin(&g)); 
2467}
2468
2469static char *owl_function_smartfilter_cc(const owl_message *m) {
2470  const char *ccs;
2471  char *ccs_quoted;
2472  char *filtname;
2473  owl_filter *f;
2474  GString *buf;
2475
2476  ccs = owl_message_get_attribute_value(m, "zephyr_ccs");
2477
2478  filtname = g_strdup_printf("conversation-%s", ccs);
2479  g_strdelimit(filtname, " ", '-');
2480
2481  if (owl_global_get_filter(&g, filtname)) {
2482    return filtname;
2483  }
2484
2485  buf = g_string_new("type ^zephyr$ and filter personal and "
2486                     "zephyr_ccs ^");
2487  ccs_quoted = owl_text_quote(ccs, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2488  owl_string_append_quoted_arg(buf, ccs_quoted);
2489  g_string_append_c(buf, '$');
2490  g_free(ccs_quoted);
2491
2492  f = owl_filter_new_fromstring(filtname, buf->str);
2493  g_string_free(buf, true);
2494
2495  if (f == NULL) {
2496    owl_function_error("Error creating filter '%s'", filtname);
2497    g_free(filtname);
2498    return NULL;
2499  }
2500
2501  owl_global_add_filter(&g, f);
2502
2503  return filtname;
2504}
2505
2506/* Create a filter based on the current message.  Returns the name of
2507 * a filter or null.  The caller must free this name.
2508 *
2509 * if the curmsg is a personal zephyr return a filter name
2510 *    to the zephyr conversation with that user.
2511 * If the curmsg is a zephyr class message, instance foo, recip *,
2512 *    return a filter name to the class, inst.
2513 * If the curmsg is a zephyr class message and type==0 then
2514 *    return a filter name for just the class.
2515 * If the curmsg is a zephyr class message and type==1 then
2516 *    return a filter name for the class and instance.
2517 * If the curmsg is a personal AIM message returna  filter
2518 *    name to the AIM conversation with that user
2519 */
2520char *owl_function_smartfilter(int type, int invert_related)
2521{
2522  const owl_view *v;
2523  const owl_message *m;
2524  char *filtname = NULL;
2525  const char *argv[2], *zperson;
2526  int related = owl_global_is_narrow_related(&g) ^ invert_related;
2527
2528  v=owl_global_get_current_view(&g);
2529  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
2530
2531  if (!m || owl_view_get_size(v)==0) {
2532    owl_function_error("No message selected\n");
2533    return(NULL);
2534  }
2535
2536  /* very simple handling of admin messages for now */
2537  if (owl_message_is_type_admin(m)) {
2538    return(owl_function_typefilt("admin"));
2539  }
2540
2541  /* very simple handling of loopback messages for now */
2542  if (owl_message_is_type_loopback(m)) {
2543    return(owl_function_typefilt("loopback"));
2544  }
2545
2546  /* aim messages */
2547  if (owl_message_is_type_aim(m)) {
2548    if (owl_message_is_direction_in(m)) {
2549      filtname=owl_function_aimuserfilt(owl_message_get_sender(m));
2550    } else if (owl_message_is_direction_out(m)) {
2551      filtname=owl_function_aimuserfilt(owl_message_get_recipient(m));
2552    }
2553    return(filtname);
2554  }
2555
2556  /* narrow personal and login messages to the sender or recip as appropriate */
2557  if (owl_message_is_type_zephyr(m)) {
2558    if (owl_message_is_personal(m) || owl_message_is_loginout(m)) {
2559      if (owl_message_get_attribute_value(m, "zephyr_ccs") != NULL) {
2560        return owl_function_smartfilter_cc(m);
2561      }
2562
2563      if (owl_message_is_direction_in(m)) {
2564        zperson = owl_message_get_sender(m);
2565      } else {
2566        zperson = owl_message_get_recipient(m);
2567      }
2568      filtname = owl_function_zuserfilt(zperson);
2569      return filtname;
2570    }
2571
2572    /* narrow class MESSAGE, instance foo, recip * messages to class, inst */
2573    if (!strcasecmp(owl_message_get_class(m), "message")) {
2574      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m), related);
2575      return(filtname);
2576    }
2577
2578    /* otherwise narrow to the class */
2579    if (type==0) {
2580      filtname=owl_function_classinstfilt(owl_message_get_class(m), NULL, related);
2581    } else if (type==1) {
2582      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m), related);
2583    }
2584    return(filtname);
2585  }
2586
2587  /* pass it off to perl */
2588  argv[0] = type ? "1" : "0";
2589  argv[1] = related ? "1" : "0";
2590  return owl_perlconfig_message_call_method(m, "smartfilter", 2, argv);
2591}
2592
2593void owl_function_smartzpunt(int type)
2594{
2595  /* Starts a zpunt command based on the current class,instance pair.
2596   * If type=0, uses just class.  If type=1, uses instance as well. */
2597  const owl_view *v;
2598  const owl_message *m;
2599  const char *mclass, *minst;
2600  GString *buf;
2601 
2602  v=owl_global_get_current_view(&g);
2603  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
2604
2605  if (!m || owl_view_get_size(v)==0) {
2606    owl_function_error("No message selected\n");
2607    return;
2608  }
2609
2610  /* for now we skip admin messages. */
2611  if (owl_message_is_type_admin(m)
2612      || owl_message_is_loginout(m)
2613      || !owl_message_is_type_zephyr(m)) {
2614    owl_function_error("smartzpunt doesn't support this message type.");
2615    return;
2616  }
2617
2618  mclass = owl_message_get_class(m);
2619  minst = owl_message_get_instance(m);
2620  if (!mclass || !*mclass || *mclass==' '
2621      || (!strcasecmp(mclass, "message") && !strcasecmp(minst, "personal"))
2622      || (type && (!minst || !*minst|| *minst==' '))) {
2623    owl_function_error("smartzpunt can't safely do this for <%s,%s>",
2624                         mclass, minst);
2625  } else {
2626    buf = g_string_new("start-command zpunt ");
2627    owl_string_append_quoted_arg(buf, mclass);
2628    if (type) {
2629      g_string_append_c(buf, ' ');
2630      owl_string_append_quoted_arg(buf, minst);
2631    } else {
2632      g_string_append(buf, " *");
2633    }
2634    owl_function_command_norv(buf->str);
2635    g_string_free(buf, true);
2636  }
2637}
2638
2639/* Set the color of the current view's filter to
2640 * be 'color'
2641 */
2642void owl_function_color_current_filter(const char *fgcolor, const char *bgcolor)
2643{
2644  const char *name;
2645
2646  name=owl_view_get_filtname(owl_global_get_current_view(&g));
2647  owl_function_color_filter(name, fgcolor, bgcolor);
2648}
2649
2650/* Set the color of the filter 'filter' to be 'color'.  If the color
2651 * name does not exist, return -1, if the filter does not exist or is
2652 * the "all" filter, return -2.  Return 0 on success
2653 */
2654int owl_function_color_filter(const char *filtname, const char *fgcolor, const char *bgcolor)
2655{
2656  owl_filter *f;
2657
2658  f=owl_global_get_filter(&g, filtname);
2659  if (!f) {
2660    owl_function_error("Unknown filter");
2661    return(-2);
2662  }
2663
2664  /* don't touch the all filter */
2665  if (!strcmp(filtname, "all")) {
2666    owl_function_error("You may not change the 'all' filter.");
2667    return(-2);
2668  }
2669
2670  if (owl_util_string_to_color(fgcolor)==OWL_COLOR_INVALID) {
2671    owl_function_error("No color named '%s' avilable.", fgcolor);
2672    return(-1);
2673  }
2674
2675
2676  if (bgcolor != NULL) {
2677    if (owl_util_string_to_color(bgcolor)==OWL_COLOR_INVALID) {
2678      owl_function_error("No color named '%s' avilable.", bgcolor);
2679      return(-1);
2680    }
2681    owl_filter_set_bgcolor(f, owl_util_string_to_color(bgcolor));
2682  }
2683  owl_filter_set_fgcolor(f, owl_util_string_to_color(fgcolor));
2684 
2685  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2686  return(0);
2687}
2688
2689void owl_function_show_colors(void)
2690{
2691  owl_fmtext fm;
2692  int i; 
2693 
2694  owl_fmtext_init_null(&fm);
2695  owl_fmtext_append_normal(&fm,"default:  ");
2696  owl_fmtext_append_normal_color(&fm, "default\n", OWL_COLOR_DEFAULT, OWL_COLOR_DEFAULT);
2697
2698  owl_fmtext_append_normal(&fm,"black:    ");
2699  owl_fmtext_append_normal_color(&fm, "black\n", OWL_COLOR_BLACK, OWL_COLOR_DEFAULT);
2700
2701  owl_fmtext_append_normal(&fm,"red:      ");
2702  owl_fmtext_append_normal_color(&fm, "red\n", OWL_COLOR_RED, OWL_COLOR_DEFAULT);
2703
2704  owl_fmtext_append_normal(&fm,"green:    ");
2705  owl_fmtext_append_normal_color(&fm, "green\n", OWL_COLOR_GREEN, OWL_COLOR_DEFAULT);
2706
2707  owl_fmtext_append_normal(&fm,"yellow:   ");
2708  owl_fmtext_append_normal_color(&fm, "yellow\n", OWL_COLOR_YELLOW, OWL_COLOR_DEFAULT);
2709
2710  owl_fmtext_append_normal(&fm,"blue:     ");
2711  owl_fmtext_append_normal_color(&fm, "blue\n", OWL_COLOR_BLUE, OWL_COLOR_DEFAULT);
2712
2713  owl_fmtext_append_normal(&fm,"magenta:  ");
2714  owl_fmtext_append_normal_color(&fm, "magenta\n", OWL_COLOR_MAGENTA, OWL_COLOR_DEFAULT);
2715
2716  owl_fmtext_append_normal(&fm,"cyan:     ");
2717  owl_fmtext_append_normal_color(&fm, "cyan\n", OWL_COLOR_CYAN, OWL_COLOR_DEFAULT);
2718
2719  owl_fmtext_append_normal(&fm,"white:    ");
2720  owl_fmtext_append_normal_color(&fm, "white\n", OWL_COLOR_WHITE, OWL_COLOR_DEFAULT);
2721
2722  for(i = 8; i < COLORS; ++i) {
2723    char* str1 = g_strdup_printf("%4i:     ",i);
2724    char* str2 = g_strdup_printf("%i\n",i);
2725    owl_fmtext_append_normal(&fm,str1);
2726    owl_fmtext_append_normal_color(&fm, str2, i, OWL_COLOR_DEFAULT);
2727    g_free(str1);
2728     g_free(str2);
2729  }
2730 
2731  owl_function_popless_fmtext(&fm);
2732  owl_fmtext_cleanup(&fm);
2733}
2734
2735/* add the given class, inst, recip to the punt list for filtering.
2736 *   if direction==0 then punt
2737 *   if direction==1 then unpunt
2738 */
2739void owl_function_zpunt(const char *class, const char *inst, const char *recip, int direction)
2740{
2741  GPtrArray *argv;
2742  char *quoted;
2743
2744  argv = g_ptr_array_new();
2745  if (!strcmp(class, "*")) {
2746    g_ptr_array_add(argv, g_strdup("class"));
2747    g_ptr_array_add(argv, g_strdup(".*"));
2748  } else {
2749    quoted=owl_text_quote(class, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2750    g_ptr_array_add(argv, g_strdup("class"));
2751    g_ptr_array_add(argv, g_strdup_printf("^(un)*%s(\\.d)*$", quoted));
2752    g_free(quoted);
2753  }
2754  if (!strcmp(inst, "*")) {
2755    g_ptr_array_add(argv, g_strdup("and"));
2756    g_ptr_array_add(argv, g_strdup("instance"));
2757    g_ptr_array_add(argv, g_strdup(".*"));
2758  } else {
2759    quoted=owl_text_quote(inst, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2760    g_ptr_array_add(argv, g_strdup("and"));
2761    g_ptr_array_add(argv, g_strdup("instance"));
2762    g_ptr_array_add(argv, g_strdup_printf("^(un)*%s(\\.d)*$", quoted));
2763    g_free(quoted);
2764  }
2765  if (!strcmp(recip, "*")) {
2766    /* nothing */
2767  } else {
2768    if(!strcmp(recip, "%me%")) {
2769      recip = owl_zephyr_get_sender();
2770    }
2771    quoted=owl_text_quote(recip, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2772    g_ptr_array_add(argv, g_strdup("and"));
2773    g_ptr_array_add(argv, g_strdup("recipient"));
2774    g_ptr_array_add(argv, g_strdup_printf("^%s$", quoted));
2775    g_free(quoted);
2776  }
2777
2778  owl_function_punt(argv->len, (const char *const*) argv->pdata, direction);
2779  g_ptr_array_foreach(argv, (GFunc)g_free, NULL);
2780  g_ptr_array_free(argv, true);
2781}
2782
2783void owl_function_punt(int argc, const char *const *argv, int direction)
2784{
2785  owl_filter *f;
2786  owl_list *fl;
2787  int i, j;
2788  fl=owl_global_get_puntlist(&g);
2789
2790  /* first, create the filter */
2791  f = owl_filter_new("punt-filter", argc, argv);
2792  if (f == NULL) {
2793    owl_function_error("Error creating filter for zpunt");
2794    return;
2795  }
2796
2797  /* Check for an identical filter */
2798  j=owl_list_get_size(fl);
2799  for (i=0; i<j; i++) {
2800    if (owl_filter_equiv(f, owl_list_get_element(fl, i))) {
2801      owl_function_debugmsg("found an equivalent punt filter");
2802      /* if we're punting, then just silently bow out on this duplicate */
2803      if (direction==0) {
2804        owl_filter_delete(f);
2805        return;
2806      }
2807
2808      /* if we're unpunting, then remove this filter from the puntlist */
2809      if (direction==1) {
2810        owl_filter_delete(owl_list_get_element(fl, i));
2811        owl_list_remove_element(fl, i);
2812        owl_filter_delete(f);
2813        return;
2814      }
2815    }
2816  }
2817
2818  if (direction == 0) {
2819    owl_function_debugmsg("punting");
2820    /* If we're punting, add the filter to the global punt list */
2821    owl_list_append_element(fl, f);
2822  } else if (direction == 1) {
2823    owl_function_makemsg("No matching punt filter");
2824 }
2825}
2826
2827void owl_function_show_keymaps(void)
2828{
2829  owl_list l;
2830  owl_fmtext fm;
2831  const owl_keymap *km;
2832  const owl_keyhandler *kh;
2833  int i, numkm;
2834  const char *kmname;
2835
2836  kh = owl_global_get_keyhandler(&g);
2837  owl_fmtext_init_null(&fm);
2838  owl_fmtext_append_bold(&fm, "Keymaps:   ");
2839  owl_fmtext_append_normal(&fm, "(use 'show keymap <name>' for details)\n");
2840  owl_list_create(&l);
2841  owl_keyhandler_get_keymap_names(kh, &l);
2842  owl_fmtext_append_list(&fm, &l, "\n", owl_function_keymap_summary);
2843  owl_fmtext_append_normal(&fm, "\n");
2844
2845  numkm = owl_list_get_size(&l);
2846  for (i=0; i<numkm; i++) {
2847    kmname = owl_list_get_element(&l, i);
2848    km = owl_keyhandler_get_keymap(kh, kmname);
2849    owl_fmtext_append_bold(&fm, "\n\n----------------------------------------------------------------------------------------------------\n\n");
2850    owl_keymap_get_details(km, &fm, 0);
2851  }
2852  owl_fmtext_append_normal(&fm, "\n");
2853 
2854  owl_function_popless_fmtext(&fm);
2855  owl_list_cleanup(&l, g_free);
2856  owl_fmtext_cleanup(&fm);
2857}
2858
2859char *owl_function_keymap_summary(const char *name)
2860{
2861  const owl_keymap *km
2862    = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), name);
2863  if (km) return owl_keymap_summary(km);
2864  else return(NULL);
2865}
2866
2867/* TODO: implement for real */
2868void owl_function_show_keymap(const char *name)
2869{
2870  owl_fmtext fm;
2871  const owl_keymap *km;
2872
2873  owl_fmtext_init_null(&fm);
2874  km = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), name);
2875  if (km) {
2876    owl_keymap_get_details(km, &fm, 1);
2877  } else {
2878    owl_fmtext_append_normal(&fm, "No such keymap...\n");
2879  } 
2880  owl_function_popless_fmtext(&fm);
2881  owl_fmtext_cleanup(&fm);
2882}
2883
2884void owl_function_help_for_command(const char *cmdname)
2885{
2886  owl_fmtext fm;
2887
2888  owl_fmtext_init_null(&fm);
2889  owl_cmd_get_help(owl_global_get_cmddict(&g), cmdname, &fm);
2890  owl_function_popless_fmtext(&fm); 
2891  owl_fmtext_cleanup(&fm);
2892}
2893
2894void owl_function_set_search(const char *string)
2895{
2896  owl_regex re;
2897
2898  if (string && owl_regex_create_quoted(&re, string) == 0) {
2899    owl_global_set_search_re(&g, &re);
2900    owl_regex_cleanup(&re);
2901  } else {
2902    owl_global_set_search_re(&g, NULL);
2903  }
2904}
2905
2906void owl_function_search_helper(int consider_current, int direction)
2907{
2908  /* move to a message that contains the string.  If direction is
2909   * OWL_DIRECTION_DOWNWARDS then search fowards, if direction is
2910   * OWL_DIRECTION_UPWARDS then search backwards.
2911   *
2912   * If consider_current is true then it will stay on the
2913   * current message if it contains the string.
2914   */
2915
2916  const owl_view *v;
2917  int viewsize, i, curmsg, start;
2918  owl_message *m;
2919
2920  v=owl_global_get_current_view(&g);
2921  viewsize=owl_view_get_size(v);
2922  curmsg=owl_global_get_curmsg(&g);
2923 
2924  if (viewsize==0) {
2925    owl_function_makemsg("No messages present");
2926    return;
2927  }
2928
2929  if (consider_current) {
2930    start=curmsg;
2931  } else if (direction==OWL_DIRECTION_DOWNWARDS) {
2932    start=curmsg+1;
2933  } else {
2934    start=curmsg-1;
2935  }
2936
2937  /* bounds check */
2938  if (start>=viewsize || start<0) {
2939    owl_function_makemsg("No further matches found");
2940    return;
2941  }
2942
2943  for (i=start; i<viewsize && i>=0;) {
2944    m=owl_view_get_element(v, i);
2945    if (owl_message_search(m, owl_global_get_search_re(&g))) {
2946      owl_global_set_curmsg(&g, i);
2947      owl_function_calculate_topmsg(direction);
2948      owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2949      if (direction==OWL_DIRECTION_DOWNWARDS) {
2950        owl_global_set_direction_downwards(&g);
2951      } else {
2952        owl_global_set_direction_upwards(&g);
2953      }
2954      return;
2955    }
2956    if (direction==OWL_DIRECTION_DOWNWARDS) {
2957      i++;
2958    } else {
2959      i--;
2960    }
2961    if (owl_global_take_interrupt(&g)) {
2962      owl_function_makemsg("Search interrupted!");
2963      owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2964      return;
2965    }
2966  }
2967  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2968  owl_function_makemsg("No matches found");
2969}
2970
2971/* strips formatting from ztext and returns the unformatted text.
2972 * caller is responsible for freeing. */
2973char *owl_function_ztext_stylestrip(const char *zt)
2974{
2975  owl_fmtext fm;
2976  char *plaintext;
2977
2978  owl_fmtext_init_null(&fm);
2979  owl_fmtext_append_ztext(&fm, zt);
2980  plaintext = owl_fmtext_print_plain(&fm);
2981  owl_fmtext_cleanup(&fm);
2982  return(plaintext);
2983}
2984
2985/* Popup a buddylisting.  If filename is NULL use the default .anyone */
2986void owl_function_buddylist(int aim, int zephyr, const char *filename)
2987{
2988  int i, j, idle;
2989  int interrupted = 0;
2990  owl_fmtext fm;
2991  const owl_buddylist *bl;
2992  const owl_buddy *b;
2993  char *timestr;
2994#ifdef HAVE_LIBZEPHYR
2995  int x;
2996  owl_list anyone;
2997  const char *user;
2998  char *tmp;
2999  ZLocations_t location[200];
3000  int numlocs, ret;
3001#endif
3002
3003  owl_fmtext_init_null(&fm);
3004
3005  /* AIM first */
3006  if (aim && owl_global_is_aimloggedin(&g)) {
3007    bl=owl_global_get_buddylist(&g);
3008
3009    owl_fmtext_append_bold(&fm, "AIM users logged in:\n");
3010    /* we're assuming AIM for now */
3011    j=owl_buddylist_get_size(bl);
3012    for (i=0; i<j; i++) {
3013      b=owl_buddylist_get_buddy_n(bl, i);
3014      idle=owl_buddy_get_idle_time(b);
3015      if (idle!=0) {
3016        timestr=owl_util_minutes_to_timestr(idle);
3017      } else {
3018        timestr=g_strdup("");
3019      }
3020      owl_fmtext_appendf_normal(&fm, "  %-20.20s %-12.12s\n", owl_buddy_get_name(b), timestr);
3021      g_free(timestr);
3022    }
3023  }
3024
3025#ifdef HAVE_LIBZEPHYR
3026  if (zephyr) {
3027    if(!owl_global_is_havezephyr(&g)) {
3028      owl_function_error("Zephyr currently not available.");
3029    } else {
3030      owl_fmtext_append_bold(&fm, "Zephyr users logged in:\n");
3031      owl_list_create(&anyone);
3032      ret=owl_zephyr_get_anyone_list(&anyone, filename);
3033      if (ret) {
3034        if (errno == ENOENT) {
3035          owl_fmtext_append_normal(&fm, " You have not added any zephyr buddies.  Use the\n");
3036          owl_fmtext_append_normal(&fm, " command ':addbuddy zephyr ");
3037          owl_fmtext_append_bold(  &fm, "<username>");
3038          owl_fmtext_append_normal(&fm, "'.\n");
3039        } else {
3040          owl_fmtext_append_normal(&fm, " Could not read zephyr buddies from the .anyone file.\n");
3041        }
3042      } else {
3043        j=owl_list_get_size(&anyone);
3044        for (i=0; i<j; i++) {
3045          user=owl_list_get_element(&anyone, i);
3046          ret=ZLocateUser(zstr(user), &numlocs, ZAUTH);
3047
3048          if (owl_global_take_interrupt(&g)) {
3049            interrupted = 1;
3050            owl_function_makemsg("Interrupted!");
3051            break;
3052          }
3053
3054          if (ret!=ZERR_NONE) {
3055            owl_function_error("Error getting location for %s", user);
3056            continue;
3057          }
3058
3059          numlocs=200;
3060          ret=ZGetLocations(location, &numlocs);
3061          if (ret==0) {
3062            for (x=0; x<numlocs; x++) {
3063              tmp=short_zuser(user);
3064              owl_fmtext_appendf_normal(&fm, "  %-10.10s %-24.24s %-12.12s  %20.20s\n",
3065                                        tmp,
3066                                        location[x].host,
3067                                        location[x].tty,
3068                                        location[x].time);
3069              g_free(tmp);
3070            }
3071            if (numlocs>=200) {
3072              owl_fmtext_append_normal(&fm, "  Too many locations found for this user, truncating.\n");
3073            }
3074          }
3075        }
3076      }
3077      owl_list_cleanup(&anyone, g_free);
3078    }
3079  }
3080#endif
3081
3082  if (aim && zephyr) {
3083    if (owl_perlconfig_is_function("BarnOwl::Hooks::_get_blist")) {
3084      char * perlblist = owl_perlconfig_execute("BarnOwl::Hooks::_get_blist()");
3085      if (perlblist) {
3086        owl_fmtext_append_ztext(&fm, perlblist);
3087        g_free(perlblist);
3088      }
3089    }
3090  }
3091
3092  if(!interrupted) {
3093    owl_function_popless_fmtext(&fm);
3094  }
3095  owl_fmtext_cleanup(&fm);
3096}
3097
3098/* Dump messages in the current view to the file 'filename'. */
3099void owl_function_dump(const char *filename) 
3100{
3101  int i, j;
3102  owl_message *m;
3103  const owl_view *v;
3104  FILE *file;
3105  char *plaintext;
3106
3107  v=owl_global_get_current_view(&g);
3108
3109  /* in the future make it ask yes/no */
3110  /*
3111  ret=stat(filename, &sbuf);
3112  if (!ret) {
3113    ret=owl_function_askyesno("File exists, continue? [Y/n]");
3114    if (!ret) return;
3115  }
3116  */
3117
3118  file=fopen(filename, "w");
3119  if (!file) {
3120    owl_function_error("Error opening file");
3121    return;
3122  }
3123
3124  j=owl_view_get_size(v);
3125  for (i=0; i<j; i++) {
3126    m=owl_view_get_element(v, i);
3127    plaintext = owl_strip_format_chars(owl_message_get_text(m));
3128    if (plaintext) {
3129      fputs(plaintext, file);
3130      g_free(plaintext);
3131    }
3132  }
3133  fclose(file);
3134  owl_function_makemsg("Messages dumped to %s", filename);
3135}
3136
3137void owl_function_do_newmsgproc(void)
3138{
3139  if (owl_global_get_newmsgproc(&g) && strcmp(owl_global_get_newmsgproc(&g), "")) {
3140    /* if there's a process out there, we need to check on it */
3141    if (owl_global_get_newmsgproc_pid(&g)) {
3142      owl_function_debugmsg("Checking on newmsgproc pid==%i", owl_global_get_newmsgproc_pid(&g));
3143      owl_function_debugmsg("Waitpid return is %i", waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG));
3144      waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG);
3145      if (waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG)==-1) {
3146        /* it exited */
3147        owl_global_set_newmsgproc_pid(&g, 0);
3148        owl_function_debugmsg("newmsgproc exited");
3149      } else {
3150        owl_function_debugmsg("newmsgproc did not exit");
3151      }
3152    }
3153   
3154    /* if it exited, fork & exec a new one */
3155    if (owl_global_get_newmsgproc_pid(&g)==0) {
3156      pid_t i;
3157      int myargc;
3158      i=fork();
3159      if (i) {
3160        /* parent set the child's pid */
3161        owl_global_set_newmsgproc_pid(&g, i);
3162        owl_function_debugmsg("I'm the parent and I started a new newmsgproc with pid %i", i);
3163      } else {
3164        /* child exec's the program */
3165        char **parsed;
3166        parsed=owl_parseline(owl_global_get_newmsgproc(&g), &myargc);
3167        if (myargc < 0) {
3168          owl_function_debugmsg("Could not parse newmsgproc '%s': unbalanced quotes?", owl_global_get_newmsgproc(&g));
3169        }
3170        if (myargc <= 0) {
3171          _exit(127);
3172        }
3173        owl_function_debugmsg("About to exec \"%s\" with %d arguments", parsed[0], myargc);
3174       
3175        execvp(parsed[0], parsed);
3176       
3177       
3178        /* was there an error exec'ing? */
3179        owl_function_debugmsg("Cannot run newmsgproc '%s': cannot exec '%s': %s", 
3180                              owl_global_get_newmsgproc(&g), parsed[0], strerror(errno));
3181        _exit(127);
3182      }
3183    }
3184  }
3185}
3186
3187/* print the xterm escape sequence to raise the window */
3188void owl_function_xterm_raise(void)
3189{
3190  printf("\033[5t");
3191}
3192
3193/* print the xterm escape sequence to deiconify the window */
3194void owl_function_xterm_deiconify(void)
3195{
3196  printf("\033[1t");
3197}
3198
3199/* Add the specified command to the startup file.  Eventually this
3200 * should be clever, and rewriting settings that will obviosly
3201 * override earlier settings with 'set' 'bindkey' and 'alias'
3202 * commands.  For now though we just remove any line that would
3203 * duplicate this one and then append this line to the end of
3204 * startupfile.
3205 */
3206void owl_function_addstartup(const char *buff)
3207{
3208  FILE *file;
3209  const char *filename;
3210
3211  filename=owl_global_get_startupfile(&g);
3212
3213  /* delete earlier copies, if the file exists */
3214  if (g_file_test(filename, G_FILE_TEST_EXISTS))
3215    owl_util_file_deleteline(filename, buff, 1);
3216
3217  file=fopen(filename, "a");
3218  if (!file) {
3219    owl_function_error("Error opening startupfile for new command");
3220    return;
3221  }
3222
3223  /* add this line */
3224  fprintf(file, "%s\n", buff);
3225
3226  fclose(file);
3227}
3228
3229/* Remove the specified command from the startup file. */
3230void owl_function_delstartup(const char *buff)
3231{
3232  const char *filename;
3233  filename=owl_global_get_startupfile(&g);
3234  owl_util_file_deleteline(filename, buff, 1);
3235}
3236
3237/* Execute owl commands from the given filename.  If the filename
3238 * is NULL, use the default owl startup commands file.
3239 */
3240void owl_function_source(const char *filename)
3241{
3242  char *path;
3243  FILE *file;
3244  char *s = NULL;
3245  int fail_silent = 0;
3246
3247  if (!filename) {
3248    fail_silent = 1;
3249    path = g_strdup(owl_global_get_startupfile(&g));
3250  } else {
3251    path = owl_util_makepath(filename);
3252  }
3253  file = fopen(path, "r");
3254  g_free(path);
3255  if (!file) {
3256    if (!fail_silent) {
3257      owl_function_error("Error opening file: %s", filename);
3258    }
3259    return;
3260  }
3261  while (owl_getline_chomp(&s, file)) {
3262    if (s[0] == '\0' || s[0] == '#')
3263      continue;
3264    owl_function_command_norv(s);
3265  }
3266
3267  g_free(s);
3268  fclose(file);
3269}
3270
3271void owl_function_change_style(owl_view *v, const char *stylename)
3272{
3273  const owl_style *s;
3274
3275  s=owl_global_get_style_by_name(&g, stylename);
3276  if (!s) {
3277    owl_function_error("No style named %s", stylename);
3278    return;
3279  }
3280  owl_view_set_style(v, s);
3281  owl_messagelist_invalidate_formats(owl_global_get_msglist(&g));
3282  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
3283  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3284}
3285
3286void owl_function_toggleoneline(void)
3287{
3288  owl_view *v;
3289  const owl_style *s;
3290
3291  v=owl_global_get_current_view(&g);
3292  s=owl_view_get_style(v);
3293
3294  if (!owl_style_matches_name(s, "oneline")) {
3295    owl_function_change_style(v, "oneline");
3296  } else {
3297    owl_function_change_style(v, owl_global_get_default_style(&g));
3298  }
3299
3300  owl_messagelist_invalidate_formats(owl_global_get_msglist(&g));
3301  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
3302  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3303}
3304
3305void G_GNUC_PRINTF(1, 2) owl_function_error(const char *fmt, ...)
3306{
3307  static int in_error = 0;
3308  va_list ap;
3309  char *buff;
3310  const char *nl;
3311
3312  if (++in_error > 2) {
3313    /* More than two nested errors, bail immediately. */
3314    in_error--;
3315    return;
3316  }
3317
3318  va_start(ap, fmt);
3319  buff = g_strdup_vprintf(fmt, ap);
3320  va_end(ap);
3321
3322  owl_function_debugmsg("ERROR: %s", buff);
3323  owl_function_log_err(buff);
3324
3325  nl = strchr(buff, '\n');
3326
3327  /*
3328    Showing admin messages triggers a lot of code. If we have a
3329    recursive error call, that's the most likely candidate, so
3330    suppress the call in that case, to try to avoid infinite looping.
3331  */
3332
3333  if(nl && *(nl + 1) && in_error == 1) {
3334    /* Multiline error */
3335    owl_function_adminmsg("ERROR", buff);
3336  } else {
3337    owl_function_makemsg("[Error] %s", buff);
3338  }
3339
3340  g_free(buff);
3341
3342  in_error--;
3343}
3344
3345void owl_function_log_err(const char *string)
3346{
3347  char *date;
3348  time_t now;
3349  char *buff;
3350
3351  now=time(NULL);
3352  date=g_strdup(ctime(&now));
3353  date[strlen(date)-1]='\0';
3354
3355  buff = g_strdup_printf("%s %s", date, string);
3356
3357  owl_errqueue_append_err(owl_global_get_errqueue(&g), buff);
3358
3359  g_free(buff);
3360  g_free(date);
3361}
3362
3363void owl_function_showerrs(void)
3364{
3365  owl_fmtext fm;
3366
3367  owl_fmtext_init_null(&fm);
3368  owl_fmtext_append_normal(&fm, "Errors:\n\n");
3369  owl_errqueue_to_fmtext(owl_global_get_errqueue(&g), &fm);
3370  owl_function_popless_fmtext(&fm);
3371}
3372
3373void G_GNUC_PRINTF(1, 2) owl_function_makemsg(const char *fmt, ...)
3374{
3375  va_list ap;
3376  char *str;
3377
3378  va_start(ap, fmt);
3379  str = g_strdup_vprintf(fmt, ap);
3380  va_end(ap);
3381
3382  owl_function_debugmsg("makemsg: %s", str);
3383  owl_msgwin_set_text_nocopy(&g.msgwin, str);
3384}
3385
3386/* get locations for everyone in .anyone.  If 'notify' is '1' then
3387 * send a pseudo login or logout message for everyone not in sync with
3388 * the global zephyr buddy list.  The list is updated regardless of
3389 * the status of 'notify'.
3390 */
3391void owl_function_zephyr_buddy_check(int notify)
3392{
3393#ifdef HAVE_LIBZEPHYR
3394  int i, j;
3395  owl_list anyone;
3396  GList **zaldlist;
3397  GList *zaldptr;
3398  ZAsyncLocateData_t *zald;
3399  const char *user;
3400
3401  if (!owl_global_is_havezephyr(&g)) return;
3402  owl_global_set_pseudologin_notify(&g, notify);
3403  zaldlist = owl_global_get_zaldlist(&g);
3404
3405  /* Clear the existing ZALDs first. */
3406  zaldptr = g_list_first(*zaldlist);
3407  while (zaldptr) {
3408    ZFreeALD(zaldptr->data);
3409    g_free(zaldptr->data);
3410    zaldptr = g_list_next(zaldptr);
3411  }
3412  g_list_free(*zaldlist);
3413  *zaldlist = NULL;
3414
3415  owl_list_create(&anyone);
3416  owl_zephyr_get_anyone_list(&anyone, NULL);
3417  j = owl_list_get_size(&anyone);
3418  for (i = 0; i < j; i++) {
3419    user = owl_list_get_element(&anyone, i);
3420    zald = g_new(ZAsyncLocateData_t, 1);
3421    if (ZRequestLocations(zstr(user), zald, UNACKED, ZAUTH) == ZERR_NONE) {
3422      *zaldlist = g_list_append(*zaldlist, zald);
3423    } else {
3424      g_free(zald);
3425    }
3426  }
3427
3428  owl_list_cleanup(&anyone, g_free);
3429#endif
3430}
3431
3432void owl_function_aimsearch_results(const char *email, owl_list *namelist)
3433{
3434  owl_fmtext fm;
3435  int i, j;
3436
3437  owl_fmtext_init_null(&fm);
3438  owl_fmtext_append_normal(&fm, "AIM screennames associated with ");
3439  owl_fmtext_append_normal(&fm, email);
3440  owl_fmtext_append_normal(&fm, ":\n");
3441
3442  j=owl_list_get_size(namelist);
3443  for (i=0; i<j; i++) {
3444    owl_fmtext_append_normal(&fm, "  ");
3445    owl_fmtext_append_normal(&fm, owl_list_get_element(namelist, i));
3446    owl_fmtext_append_normal(&fm, "\n");
3447  }
3448
3449  owl_function_popless_fmtext(&fm);
3450  owl_fmtext_cleanup(&fm);
3451}
3452
3453int owl_function_get_color_count(void)
3454{
3455     return COLORS;
3456}
3457
3458void _owl_function_mark_message(const owl_message *m)
3459{
3460  if (m) {
3461    owl_global_set_markedmsgid(&g, owl_message_get_id(m));
3462    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3463  }
3464}
3465
3466void owl_function_mark_message(void)
3467{
3468  const owl_message *m;
3469  const owl_view *v;
3470
3471  v=owl_global_get_current_view(&g);
3472
3473  /* bail if there's no current message */
3474  if (owl_view_get_size(v) < 1) {
3475    owl_function_error("No messages to mark");
3476    return;
3477  }
3478
3479  /* mark the message */
3480  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
3481  _owl_function_mark_message(m);
3482  owl_function_makemsg("Mark set");
3483}
3484
3485void owl_function_swap_cur_marked(void)
3486{
3487  int marked_id;
3488  const owl_message *m;
3489  const owl_view *v;
3490
3491  marked_id=owl_global_get_markedmsgid(&g);
3492  if (marked_id == -1) {
3493    owl_function_error("Mark not set.");
3494    return;
3495  }
3496
3497  v=owl_global_get_current_view(&g);
3498  /* bail if there's no current message */
3499  if (owl_view_get_size(v) < 1) {
3500    return;
3501  }
3502
3503  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
3504  _owl_function_mark_message(m);
3505  owl_global_set_curmsg(&g, owl_view_get_nearest_to_msgid(v, marked_id));
3506  owl_function_calculate_topmsg(OWL_DIRECTION_NONE);
3507  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3508  owl_global_set_direction_downwards(&g);
3509}
Note: See TracBrowser for help on using the repository browser.