source: functions.c @ 42a0c8b

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