source: functions.c @ 0480eef

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