source: functions.c @ c474a97

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