source: functions.c @ 3b17b57

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