source: functions.c @ 42ee1be

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