source: functions.c

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