source: functions.c @ 0071c88

Last change on this file since 0071c88 was 0071c88, 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.2 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_noredisplay();
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_noredisplay(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_lastmsg(void)
833{
834  owl_function_lastmsg_noredisplay();
835  owl_mainwin_redisplay(owl_global_get_mainwin(&g)); 
836}
837
838void owl_function_shift_right(void)
839{
840  owl_global_set_rightshift(&g, owl_global_get_rightshift(&g)+10);
841}
842
843void owl_function_shift_left(void)
844{
845  int shift;
846
847  shift=owl_global_get_rightshift(&g);
848  if (shift > 0) {
849    owl_global_set_rightshift(&g, MAX(shift - 10, 0));
850  } else {
851    owl_function_beep();
852    owl_function_makemsg("Already full left");
853  }
854}
855
856void owl_function_unsuball(void)
857{
858  unsuball();
859  owl_function_makemsg("Unsubscribed from all messages.");
860}
861
862
863/* Load zephyr subscriptions from the named 'file' and load zephyr's
864 * default subscriptions as well.  An error message is printed if
865 * 'file' can't be opened or if zephyr reports an error in
866 * subscribing.
867 *
868 * If 'file' is NULL, this look for the default filename
869 * $HOME/.zephyr.subs.  If the file can not be opened in this case
870 * only, no error message is printed.
871 */
872void owl_function_loadsubs(const char *file)
873{
874  int ret, ret2;
875  const char *foo;
876  char *path;
877
878  if (file==NULL) {
879    ret=owl_zephyr_loadsubs(NULL, 0);
880  } else {
881    path = owl_util_makepath(file);
882    ret=owl_zephyr_loadsubs(path, 1);
883    g_free(path);
884  }
885
886  /* for backwards compatibility for now */
887  ret2=owl_zephyr_loaddefaultsubs();
888
889  if (!owl_context_is_interactive(owl_global_get_context(&g))) return;
890
891  foo=file?file:"file";
892  if (ret==0 && ret2==0) {
893    if (!file) {
894      owl_function_makemsg("Subscribed to messages.");
895    } else {
896      owl_function_makemsg("Subscribed to messages from %s", file);
897    }
898  } else if (ret==-1) {
899    owl_function_error("Could not read %s", foo);
900  } else {
901    owl_function_error("Error subscribing to messages");
902  }
903}
904
905void owl_function_loadloginsubs(const char *file)
906{
907  int ret;
908
909  ret=owl_zephyr_loadloginsubs(file);
910
911  if (!owl_context_is_interactive(owl_global_get_context(&g))) return;
912  if (ret==0) {
913  } else if (ret==-1) {
914    owl_function_error("Could not open file for login subscriptions.");
915  } else {
916    owl_function_error("Error subscribing to login messages from file.");
917  }
918}
919
920void owl_callback_aimlogin(owl_editwin *e) {
921  char *user = owl_editwin_get_cbdata(e);
922  owl_function_aimlogin(user,
923                        owl_editwin_get_text(e));
924}
925
926void owl_function_aimlogin(const char *user, const char *passwd) {
927  int ret;
928
929  /* clear the buddylist */
930  owl_buddylist_clear(owl_global_get_buddylist(&g));
931
932  /* try to login */
933  ret=owl_aim_login(user, passwd);
934  if (ret) owl_function_makemsg("Warning: login for %s failed.\n", user);
935}
936
937void owl_function_suspend(void)
938{
939  endwin();
940  printf("\n");
941  kill(getpid(), SIGSTOP);
942
943  /* resize to reinitialize all the windows when we come back */
944  owl_command_resize();
945}
946
947void owl_function_zaway_toggle(void)
948{
949  if (!owl_global_is_zaway(&g)) {
950    owl_global_set_zaway_msg(&g, owl_global_get_zaway_msg_default(&g));
951    owl_function_zaway_on();
952  } else {
953    owl_function_zaway_off();
954  }
955}
956
957void owl_function_zaway_on(void)
958{
959  owl_global_set_zaway_on(&g);
960  owl_function_makemsg("zaway set (%s)", owl_global_get_zaway_msg(&g));
961}
962
963void owl_function_zaway_off(void)
964{
965  owl_global_set_zaway_off(&g);
966  owl_function_makemsg("zaway off");
967}
968
969void owl_function_aaway_toggle(void)
970{
971  if (!owl_global_is_aaway(&g)) {
972    owl_global_set_aaway_msg(&g, owl_global_get_aaway_msg_default(&g));
973    owl_function_aaway_on();
974  } else {
975    owl_function_aaway_off();
976  }
977}
978
979void owl_function_aaway_on(void)
980{
981  owl_global_set_aaway_on(&g);
982  /* owl_aim_set_awaymsg(owl_global_get_zaway_msg(&g)); */
983  owl_function_makemsg("AIM away set (%s)", owl_global_get_aaway_msg(&g));
984}
985
986void owl_function_aaway_off(void)
987{
988  owl_global_set_aaway_off(&g);
989  /* owl_aim_set_awaymsg(""); */
990  owl_function_makemsg("AIM away off");
991}
992
993void owl_function_quit(void)
994{
995  char *ret;
996 
997  /* zlog out if we need to */
998  if (owl_global_is_havezephyr(&g) &&
999      owl_global_is_shutdownlogout(&g)) {
1000    owl_zephyr_zlog_out();
1001  }
1002
1003  /* execute the commands in shutdown */
1004  ret = owl_perlconfig_execute("BarnOwl::Hooks::_shutdown();");
1005  g_free(ret);
1006
1007  /* signal our child process, if any */
1008  if (owl_global_get_newmsgproc_pid(&g)) {
1009    kill(owl_global_get_newmsgproc_pid(&g), SIGHUP);
1010  }
1011 
1012  /* Quit AIM */
1013  if (owl_global_is_aimloggedin(&g)) {
1014    owl_aim_logout();
1015  }
1016
1017  owl_function_debugmsg("Quitting BarnOwl");
1018  owl_select_quit_loop();
1019}
1020
1021void owl_function_calculate_topmsg(int direction)
1022{
1023  int recwinlines, topmsg, curmsg;
1024  const owl_view *v;
1025
1026  v=owl_global_get_current_view(&g);
1027  curmsg=owl_global_get_curmsg(&g);
1028  topmsg=owl_global_get_topmsg(&g);
1029  recwinlines=owl_global_get_recwin_lines(&g);
1030
1031  /*
1032  if (owl_view_get_size(v) < 1) {
1033    return;
1034  }
1035  */
1036
1037  switch (owl_global_get_scrollmode(&g)) {
1038  case OWL_SCROLLMODE_TOP:
1039    topmsg = owl_function_calculate_topmsg_top(direction, v, curmsg, topmsg, recwinlines);
1040    break;
1041  case OWL_SCROLLMODE_NEARTOP:
1042    topmsg = owl_function_calculate_topmsg_neartop(direction, v, curmsg, topmsg, recwinlines);
1043    break;
1044  case OWL_SCROLLMODE_CENTER:
1045    topmsg = owl_function_calculate_topmsg_center(direction, v, curmsg, topmsg, recwinlines);
1046    break;
1047  case OWL_SCROLLMODE_PAGED:
1048    topmsg = owl_function_calculate_topmsg_paged(direction, v, curmsg, topmsg, recwinlines, 0);
1049    break;
1050  case OWL_SCROLLMODE_PAGEDCENTER:
1051    topmsg = owl_function_calculate_topmsg_paged(direction, v, curmsg, topmsg, recwinlines, 1);
1052    break;
1053  case OWL_SCROLLMODE_NORMAL:
1054  default:
1055    topmsg = owl_function_calculate_topmsg_normal(direction, v, curmsg, topmsg, recwinlines);
1056  }
1057  owl_function_debugmsg("Calculated a topmsg of %i", topmsg);
1058  owl_global_set_topmsg(&g, topmsg);
1059}
1060
1061/* Returns what the new topmsg should be. 
1062 * Passed the last direction of movement,
1063 * the current view,
1064 * the current message number in the view,
1065 * the top message currently being displayed,
1066 * and the number of lines in the recwin.
1067 */
1068int owl_function_calculate_topmsg_top(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
1069{
1070  return(curmsg);
1071}
1072
1073int owl_function_calculate_topmsg_neartop(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
1074{
1075  if (curmsg>0 
1076      && (owl_message_get_numlines(owl_view_get_element(v, curmsg-1))
1077          <  recwinlines/2)) {
1078    return(curmsg-1);
1079  } else {
1080    return(curmsg);
1081  }
1082}
1083 
1084int owl_function_calculate_topmsg_center(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
1085{
1086  int i, last, lines;
1087
1088  last = curmsg;
1089  lines = 0;
1090  for (i=curmsg-1; i>=0; i--) {
1091    lines += owl_message_get_numlines(owl_view_get_element(v, i));
1092    if (lines > recwinlines/2) break;
1093    last = i;
1094  }
1095  return(last);
1096}
1097 
1098int owl_function_calculate_topmsg_paged(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines, int center_on_page)
1099{
1100  int i, last, lines, savey;
1101 
1102  /* If we're off the top of the screen, scroll up such that the
1103   * curmsg is near the botton of the screen. */
1104  if (curmsg < topmsg) {
1105    last = curmsg;
1106    lines = 0;
1107    for (i=curmsg; i>=0; i--) {
1108      lines += owl_message_get_numlines(owl_view_get_element(v, i));
1109      if (lines > recwinlines) break;
1110    last = i;
1111    }
1112    if (center_on_page) {
1113      return(owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines));
1114    } else {
1115      return(last);
1116    }
1117  }
1118
1119  /* Find number of lines from top to bottom of curmsg (store in savey) */
1120  savey=0;
1121  for (i=topmsg; i<=curmsg; i++) {
1122    savey+=owl_message_get_numlines(owl_view_get_element(v, i));
1123  }
1124
1125  /* if we're off the bottom of the screen, scroll down */
1126  if (savey > recwinlines) {
1127    if (center_on_page) {
1128      return(owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines));
1129    } else {
1130      return(curmsg);
1131    }
1132  }
1133
1134  /* else just stay as we are... */
1135  return(topmsg);
1136}
1137
1138int owl_function_calculate_topmsg_normal(int direction, const owl_view *v, int curmsg, int topmsg, int recwinlines)
1139{
1140  int savey, i, foo, y;
1141
1142  if (curmsg<0) return(topmsg);
1143   
1144  /* If we're off the top of the screen then center */
1145  if (curmsg<topmsg) {
1146    topmsg=owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines);
1147  }
1148
1149  /* If curmsg is so far past topmsg that there are more messages than
1150     lines, skip the line counting that follows because we're
1151     certainly off screen.  */
1152  savey=curmsg-topmsg;
1153  if (savey <= recwinlines) {
1154    /* Find number of lines from top to bottom of curmsg (store in savey) */
1155    savey = 0;
1156    for (i=topmsg; i<=curmsg; i++) {
1157      savey+=owl_message_get_numlines(owl_view_get_element(v, i));
1158    }
1159  }
1160
1161  /* If we're off the bottom of the screen, set the topmsg to curmsg
1162   * and scroll upwards */
1163  if (savey > recwinlines) {
1164    topmsg=curmsg;
1165    savey=owl_message_get_numlines(owl_view_get_element(v, curmsg));
1166    direction=OWL_DIRECTION_UPWARDS;
1167  }
1168 
1169  /* If our bottom line is less than 1/4 down the screen then scroll up */
1170  if (direction == OWL_DIRECTION_UPWARDS || direction == OWL_DIRECTION_NONE) {
1171    if (savey < (recwinlines / 4)) {
1172      y=0;
1173      for (i=curmsg; i>=0; i--) {
1174        foo=owl_message_get_numlines(owl_view_get_element(v, i));
1175        /* will we run the curmsg off the screen? */
1176        if ((foo+y) >= recwinlines) {
1177          i++;
1178          if (i>curmsg) i=curmsg;
1179          break;
1180        }
1181        /* have saved 1/2 the screen space? */
1182        y+=foo;
1183        if (y > (recwinlines / 2)) break;
1184      }
1185      if (i<0) i=0;
1186      return(i);
1187    }
1188  }
1189
1190  if (direction == OWL_DIRECTION_DOWNWARDS || direction == OWL_DIRECTION_NONE) {
1191    /* If curmsg bottom line is more than 3/4 down the screen then scroll down */
1192    if (savey > ((recwinlines * 3)/4)) {
1193      y=0;
1194      /* count lines from the top until we can save 1/2 the screen size */
1195      for (i=topmsg; i<curmsg; i++) {
1196        y+=owl_message_get_numlines(owl_view_get_element(v, i));
1197        if (y > (recwinlines / 2)) break;
1198      }
1199      if (i==curmsg) {
1200        i--;
1201      }
1202      return(i+1);
1203    }
1204  }
1205
1206  return(topmsg);
1207}
1208
1209void owl_function_resize(void)
1210{
1211  owl_global_set_resize_pending(&g);
1212}
1213
1214void G_GNUC_PRINTF(1, 2) owl_function_debugmsg(const char *fmt, ...)
1215{
1216  char *tmpbuff;
1217  FILE *file;
1218  time_t now;
1219  va_list ap;
1220  va_start(ap, fmt);
1221
1222  if (!owl_global_is_debug_fast(&g))
1223    return;
1224
1225  file = owl_global_get_debug_file_handle(&g);
1226  if (!file) /* XXX should report this */
1227    return;
1228
1229  now = time(NULL);
1230
1231  tmpbuff = owl_util_format_time(localtime(&now));
1232  fprintf(file, "[%d -  %s - %lds]: ",
1233          (int) getpid(), tmpbuff, now - owl_global_get_starttime(&g));
1234  g_free(tmpbuff);
1235  vfprintf(file, fmt, ap);
1236  putc('\n', file);
1237  fflush(file);
1238
1239  va_end(ap);
1240}
1241
1242void owl_function_beep(void)
1243{
1244  if (owl_global_is_bell(&g)) {
1245    beep();
1246  }
1247}
1248
1249int owl_function_subscribe(const char *class, const char *inst, const char *recip)
1250{
1251  int ret;
1252
1253  ret=owl_zephyr_sub(class, inst, recip);
1254  if (ret) {
1255    owl_function_error("Error subscribing.");
1256  } else {
1257    owl_function_makemsg("Subscribed.");
1258  }
1259  return(ret);
1260}
1261
1262void owl_function_unsubscribe(const char *class, const char *inst, const char *recip)
1263{
1264  int ret;
1265
1266  ret=owl_zephyr_unsub(class, inst, recip);
1267  if (ret) {
1268    owl_function_error("Error subscribing.");
1269  } else {
1270    owl_function_makemsg("Unsubscribed.");
1271  }
1272}
1273
1274static void _dirty_everything(gpointer data, gpointer user_data) {
1275  owl_window *w = data;
1276  if (!owl_window_is_realized(w))
1277    return;
1278  owl_window_dirty(w);
1279  owl_window_children_foreach(w, _dirty_everything, NULL);
1280}
1281
1282void owl_function_full_redisplay(void)
1283{
1284  /* Ask every widget to redraw itself. */
1285  _dirty_everything(owl_window_get_screen(), NULL);
1286  /* Force ncurses to redisplay everything. */
1287  clearok(stdscr, TRUE);
1288}
1289
1290void owl_function_popless_text(const char *text)
1291{
1292  owl_popwin *pw;
1293  owl_viewwin *v;
1294
1295  if (owl_global_get_popwin(&g) || owl_global_get_viewwin(&g)) {
1296    owl_function_error("Popwin already in use.");
1297    return;
1298  }
1299  pw = owl_popwin_new();
1300  owl_global_set_popwin(&g, pw);
1301  owl_popwin_up(pw);
1302
1303  v = owl_viewwin_new_text(owl_popwin_get_content(pw), text);
1304  owl_global_set_viewwin(&g, v);
1305
1306  owl_global_push_context(&g, OWL_CTX_POPLESS, v, "popless", NULL);
1307}
1308
1309void owl_function_popless_fmtext(const owl_fmtext *fm)
1310{
1311  owl_popwin *pw;
1312  owl_viewwin *v;
1313
1314  if (owl_global_get_popwin(&g) || owl_global_get_viewwin(&g)) {
1315    owl_function_error("Popwin already in use.");
1316    return;
1317  }
1318  pw = owl_popwin_new();
1319  owl_global_set_popwin(&g, pw);
1320  owl_popwin_up(pw);
1321
1322  v = owl_viewwin_new_fmtext(owl_popwin_get_content(pw), fm);
1323  owl_global_set_viewwin(&g, v);
1324
1325  owl_global_push_context(&g, OWL_CTX_POPLESS, v, "popless", NULL);
1326}
1327
1328void owl_function_popless_file(const char *filename)
1329{
1330  owl_fmtext fm;
1331  FILE *file;
1332  char *s = NULL;
1333
1334  file=fopen(filename, "r");
1335  if (!file) {
1336    owl_function_error("Could not open file: %s", filename);
1337    return;
1338  }
1339
1340  owl_fmtext_init_null(&fm);
1341  while (owl_getline(&s, file))
1342    owl_fmtext_append_normal(&fm, s);
1343  g_free(s);
1344
1345  owl_function_popless_fmtext(&fm);
1346  owl_fmtext_cleanup(&fm);
1347  fclose(file);
1348}
1349
1350void owl_function_about(void)
1351{
1352  owl_function_popless_text(
1353    "This is BarnOwl version " OWL_VERSION_STRING ".\n\n"
1354    "BarnOwl is a fork of the Owl zephyr client, written and\n"
1355    "maintained by Alejandro Sedeno and Nelson Elhage at the\n"
1356    "Massachusetts Institute of Technology. \n"
1357    "\n"
1358    "Owl was written by James Kretchmar. The first version, 0.5, was\n"
1359    "released in March 2002.\n"
1360    "\n"
1361    "The name 'owl' was chosen in reference to the owls in the\n"
1362    "Harry Potter novels, who are tasked with carrying messages\n"
1363    "between Witches and Wizards. The name 'BarnOwl' was chosen\n"
1364    "because we feel our owls should live closer to our ponies.\n"
1365    "\n"
1366    "Copyright (c) 2006-2011 The BarnOwl Developers. All rights reserved.\n"
1367    "Copyright (c) 2004 James Kretchmar. All rights reserved.\n"
1368    "Copyright 2002 Massachusetts Institute of Technology\n"
1369    "\n"
1370    "This program is free software. You can redistribute it and/or\n"
1371    "modify under the terms of the Sleepycat License. Use the \n"
1372    "':show license' command to display the full license\n"
1373  );
1374}
1375
1376void owl_function_info(void)
1377{
1378  const owl_message *m;
1379  owl_fmtext fm, attrfm;
1380  const owl_view *v;
1381  char *time;
1382#ifdef HAVE_LIBZEPHYR
1383  const ZNotice_t *n;
1384#endif
1385
1386  owl_fmtext_init_null(&fm);
1387 
1388  v=owl_global_get_current_view(&g);
1389  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1390  if (!m || owl_view_get_size(v)==0) {
1391    owl_function_error("No message selected\n");
1392    return;
1393  }
1394
1395  owl_fmtext_append_bold(&fm, "General Information:\n");
1396  owl_fmtext_appendf_normal(&fm, "  Msg Id    : %i\n", owl_message_get_id(m));
1397
1398  owl_fmtext_append_normal(&fm, "  Type      : ");
1399  owl_fmtext_append_bold(&fm, owl_message_get_type(m));
1400  owl_fmtext_append_normal(&fm, "\n");
1401
1402  if (owl_message_is_direction_in(m)) {
1403    owl_fmtext_append_normal(&fm, "  Direction : in\n");
1404  } else if (owl_message_is_direction_out(m)) {
1405    owl_fmtext_append_normal(&fm, "  Direction : out\n");
1406  } else if (owl_message_is_direction_none(m)) {
1407    owl_fmtext_append_normal(&fm, "  Direction : none\n");
1408  } else {
1409    owl_fmtext_append_normal(&fm, "  Direction : unknown\n");
1410  }
1411
1412  time = owl_message_format_time(m);
1413  owl_fmtext_appendf_normal(&fm, "  Time      : %s\n", time);
1414  g_free(time);
1415
1416  if (!owl_message_is_type_admin(m)) {
1417    owl_fmtext_appendf_normal(&fm, "  Sender    : %s\n", owl_message_get_sender(m));
1418    owl_fmtext_appendf_normal(&fm, "  Recipient : %s\n", owl_message_get_recipient(m));
1419  }
1420
1421  if (owl_message_is_type_zephyr(m)) {
1422    owl_fmtext_append_bold(&fm, "\nZephyr Specific Information:\n");
1423   
1424    owl_fmtext_appendf_normal(&fm, "  Class     : %s\n", owl_message_get_class(m));
1425    owl_fmtext_appendf_normal(&fm, "  Instance  : %s\n", owl_message_get_instance(m));
1426    owl_fmtext_appendf_normal(&fm, "  Opcode    : %s\n", owl_message_get_opcode(m));
1427#ifdef HAVE_LIBZEPHYR
1428    n = owl_message_get_notice(m);
1429    if (n != NULL) {
1430      char *tmpbuff, *tmpbuff2;
1431      int i, fields;
1432
1433      if (!owl_message_is_pseudo(m)) {
1434        owl_fmtext_append_normal(&fm, "  Kind      : ");
1435        if (n->z_kind==UNSAFE) {
1436          owl_fmtext_append_normal(&fm, "UNSAFE\n");
1437        } else if (n->z_kind==UNACKED) {
1438          owl_fmtext_append_normal(&fm, "UNACKED\n");
1439        } else if (n->z_kind==ACKED) {
1440          owl_fmtext_append_normal(&fm, "ACKED\n");
1441        } else if (n->z_kind==HMACK) {
1442          owl_fmtext_append_normal(&fm, "HMACK\n");
1443        } else if (n->z_kind==HMCTL) {
1444          owl_fmtext_append_normal(&fm, "HMCTL\n");
1445        } else if (n->z_kind==SERVACK) {
1446          owl_fmtext_append_normal(&fm, "SERVACK\n");
1447        } else if (n->z_kind==SERVNAK) {
1448          owl_fmtext_append_normal(&fm, "SERVNACK\n");
1449        } else if (n->z_kind==CLIENTACK) {
1450          owl_fmtext_append_normal(&fm, "CLIENTACK\n");
1451        } else if (n->z_kind==STAT) {
1452          owl_fmtext_append_normal(&fm, "STAT\n");
1453        } else {
1454          owl_fmtext_append_normal(&fm, "ILLEGAL VALUE\n");
1455        }
1456      }
1457      owl_fmtext_appendf_normal(&fm, "  Host      : %s\n", owl_message_get_hostname(m));
1458
1459      if (!owl_message_is_pseudo(m)) {
1460        owl_fmtext_append_normal(&fm, "\n");
1461        owl_fmtext_appendf_normal(&fm, "  Port      : %i\n", ntohs(n->z_port));
1462        owl_fmtext_appendf_normal(&fm, "  Auth      : %s\n", owl_zephyr_get_authstr(n));
1463
1464        /* FIXME make these more descriptive */
1465        owl_fmtext_appendf_normal(&fm, "  Checkd Ath: %i\n", n->z_checked_auth);
1466        owl_fmtext_appendf_normal(&fm, "  Multi notc: %s\n", n->z_multinotice);
1467        owl_fmtext_appendf_normal(&fm, "  Num other : %i\n", n->z_num_other_fields);
1468        owl_fmtext_appendf_normal(&fm, "  Msg Len   : %i\n", n->z_message_len);
1469
1470        fields=owl_zephyr_get_num_fields(n);
1471        owl_fmtext_appendf_normal(&fm, "  Fields    : %i\n", fields);
1472
1473        for (i = 0; i < fields; i++) {
1474          tmpbuff = owl_zephyr_get_field_as_utf8(n, i + 1);
1475          tmpbuff2 = owl_text_indent(tmpbuff, 14, false);
1476          owl_fmtext_appendf_normal(&fm, "  Field %i   : %s\n", i + 1, tmpbuff2);
1477          g_free(tmpbuff2);
1478          g_free(tmpbuff);
1479        }
1480        tmpbuff = owl_text_indent(n->z_default_format, 14, false);
1481        owl_fmtext_appendf_normal(&fm, "  Default Fm: %s\n", tmpbuff);
1482        g_free(tmpbuff);
1483      }
1484
1485    }
1486#endif
1487  }
1488
1489  owl_fmtext_append_bold(&fm, "\nBarnOwl Message Attributes:\n");
1490  owl_message_attributes_tofmtext(m, &attrfm);
1491  owl_fmtext_append_fmtext(&fm, &attrfm);
1492 
1493  owl_function_popless_fmtext(&fm);
1494  owl_fmtext_cleanup(&fm);
1495  owl_fmtext_cleanup(&attrfm);
1496}
1497
1498/* print the current message in a popup window.
1499 * Use the 'default' style regardless of whatever
1500 * style the user may be using
1501 */
1502void owl_function_curmsg_to_popwin(void)
1503{
1504  const owl_view *v;
1505  const owl_message *m;
1506  const owl_style *s;
1507  owl_fmtext fm;
1508
1509  v=owl_global_get_current_view(&g);
1510  s=owl_global_get_style_by_name(&g, "default");
1511
1512  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1513
1514  if (!m || owl_view_get_size(v)==0) {
1515    owl_function_error("No current message");
1516    return;
1517  }
1518
1519  owl_fmtext_init_null(&fm);
1520  owl_style_get_formattext(s, &fm, m);
1521
1522  owl_function_popless_fmtext(&fm);
1523  owl_fmtext_cleanup(&fm);
1524}
1525
1526void owl_function_page_curmsg(int step)
1527{
1528  /* scroll down or up within the current message IF the message is truncated */
1529
1530  int offset, curmsg, lines;
1531  const owl_view *v;
1532  owl_message *m;
1533
1534  offset=owl_global_get_curmsg_vert_offset(&g);
1535  v=owl_global_get_current_view(&g);
1536  curmsg=owl_global_get_curmsg(&g);
1537  m=owl_view_get_element(v, curmsg);
1538  if (!m || owl_view_get_size(v)==0) return;
1539  lines=owl_message_get_numlines(m);
1540
1541  if (offset==0) {
1542    /* Bail if the curmsg isn't the last one displayed */
1543    if (curmsg != owl_mainwin_get_last_msg(owl_global_get_mainwin(&g))) {
1544      owl_function_makemsg("The entire message is already displayed");
1545      return;
1546    }
1547   
1548    /* Bail if we're not truncated */
1549    if (!owl_mainwin_is_curmsg_truncated(owl_global_get_mainwin(&g))) {
1550      owl_function_makemsg("The entire message is already displayed");
1551      return;
1552    }
1553  }
1554 
1555 
1556  /* don't scroll past the last line */
1557  if (step>0) {
1558    if (offset+step > lines-1) {
1559      owl_global_set_curmsg_vert_offset(&g, lines-1);
1560    } else {
1561      owl_global_set_curmsg_vert_offset(&g, offset+step);
1562    }
1563  }
1564
1565  /* would we be before the beginning of the message? */
1566  if (step<0) {
1567    if (offset+step<0) {
1568      owl_global_set_curmsg_vert_offset(&g, 0);
1569    } else {
1570      owl_global_set_curmsg_vert_offset(&g, offset+step);
1571    }
1572  }
1573 
1574  /* redisplay */
1575  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1576}
1577
1578void owl_function_resize_typwin(int newsize)
1579{
1580  owl_global_set_typwin_lines(&g, newsize);
1581  owl_mainpanel_layout_contents(&g.mainpanel);
1582}
1583
1584void owl_function_mainwin_pagedown(void)
1585{
1586  int i;
1587
1588  i=owl_mainwin_get_last_msg(owl_global_get_mainwin(&g));
1589  if (i<0) return;
1590  if (owl_mainwin_is_last_msg_truncated(owl_global_get_mainwin(&g))
1591      && (owl_global_get_curmsg(&g) < i)
1592      && (i>0)) {
1593    i--;
1594  }
1595  owl_global_set_curmsg(&g, i);
1596  owl_function_nextmsg();
1597}
1598
1599void owl_function_mainwin_pageup(void)
1600{
1601  owl_global_set_curmsg(&g, owl_global_get_topmsg(&g));
1602  owl_function_prevmsg();
1603}
1604
1605void owl_function_getsubs(void)
1606{
1607  char *buff;
1608
1609  buff=owl_zephyr_getsubs();
1610
1611  if (buff) {
1612    owl_function_popless_text(buff);
1613  } else {
1614    owl_function_popless_text("Error getting subscriptions");
1615  }
1616           
1617  g_free(buff);
1618}
1619
1620void owl_function_printallvars(void)
1621{
1622  const owl_variable *v;
1623  const char *name;
1624  char *var;
1625  GPtrArray *varnames;
1626  int i;
1627  GString *str   = g_string_new("");
1628
1629  g_string_append_printf(str, "%-20s = %s\n", "VARIABLE", "VALUE");
1630  g_string_append_printf(str, "%-20s   %s\n",  "--------", "-----");
1631  varnames = owl_variable_dict_get_names(owl_global_get_vardict(&g));
1632  for (i = 0; i < varnames->len; i++) {
1633    name = varnames->pdata[i];
1634    if (name && name[0]!='_') {
1635      g_string_append_printf(str, "\n%-20s = ", name);
1636      v = owl_variable_get_var(owl_global_get_vardict(&g), name);
1637      var = owl_variable_get_tostring(v);
1638      if (var) {
1639        g_string_append(str, var);
1640        g_free(var);
1641      } else {
1642        g_string_append(str, "<null>");
1643      }
1644    }
1645  }
1646  g_string_append(str, "\n");
1647  owl_ptr_array_free(varnames, g_free);
1648
1649  owl_function_popless_text(str->str);
1650  g_string_free(str, true);
1651}
1652
1653void owl_function_show_variables(void)
1654{
1655  const owl_variable *v;
1656  GPtrArray *varnames;
1657  owl_fmtext fm; 
1658  int i;
1659  const char *varname;
1660
1661  owl_fmtext_init_null(&fm);
1662  owl_fmtext_append_bold(&fm, 
1663      "Variables: (use 'show variable <name>' for details)\n");
1664  varnames = owl_variable_dict_get_names(owl_global_get_vardict(&g));
1665  for (i = 0; i < varnames->len; i++) {
1666    varname = varnames->pdata[i];
1667    if (varname && varname[0]!='_') {
1668      v = owl_variable_get_var(owl_global_get_vardict(&g), varname);
1669      owl_variable_describe(v, &fm);
1670    }
1671  }
1672  owl_ptr_array_free(varnames, g_free);
1673  owl_function_popless_fmtext(&fm);
1674  owl_fmtext_cleanup(&fm);
1675}
1676
1677void owl_function_show_variable(const char *name)
1678{
1679  const owl_variable *v;
1680  owl_fmtext fm; 
1681
1682  owl_fmtext_init_null(&fm);
1683  v = owl_variable_get_var(owl_global_get_vardict(&g), name);
1684  if (v)
1685    owl_variable_get_help(v, &fm);
1686  else
1687    owl_fmtext_append_normal(&fm, "No such variable...\n");
1688  owl_function_popless_fmtext(&fm);
1689  owl_fmtext_cleanup(&fm);
1690}
1691
1692void owl_function_delete_and_expunge_by_id(int id, bool exclaim_success)
1693{
1694  const owl_messagelist *ml = owl_global_get_msglist(&g);
1695  int msg = owl_messagelist_get_index_by_id(ml, id);
1696  if (msg < 0) {
1697    owl_function_error("No message with id %d: unable to delete", id);
1698  } else {
1699    owl_function_delete_and_expunge_message(msg);
1700    if (exclaim_success)
1701      owl_function_makemsg("Message deleted and expunged");
1702  }
1703}
1704
1705/* note: this applies to global message list, not to view.
1706 * If flag is 1, deletes.  If flag is 0, undeletes. */
1707void owl_function_delete_by_id(int id, int flag)
1708{
1709  const owl_messagelist *ml;
1710  owl_message *m;
1711  ml = owl_global_get_msglist(&g);
1712  m = owl_messagelist_get_by_id(ml, id);
1713  if (m) {
1714    if (flag == 1) {
1715      owl_message_mark_delete(m);
1716    } else if (flag == 0) {
1717      owl_message_unmark_delete(m);
1718    }
1719    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1720  } else {
1721    owl_function_error("No message with id %d: unable to mark for (un)delete",id);
1722  }
1723}
1724
1725void owl_function_delete_automsgs(void)
1726{
1727  /* mark for deletion all messages in the current view that match the
1728   * 'trash' filter */
1729
1730  int i, j, count;
1731  owl_message *m;
1732  const owl_view *v;
1733  const owl_filter *f;
1734
1735  /* get the trash filter */
1736  f=owl_global_get_filter(&g, "trash");
1737  if (!f) {
1738    owl_function_error("No trash filter defined");
1739    return;
1740  }
1741
1742  v=owl_global_get_current_view(&g);
1743
1744  count=0;
1745  j=owl_view_get_size(v);
1746  for (i=0; i<j; i++) {
1747    m=owl_view_get_element(v, i);
1748    if (owl_filter_message_match(f, m)) {
1749      count++;
1750      owl_message_mark_delete(m);
1751    }
1752  }
1753  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1754  owl_function_makemsg("%i messages marked for deletion", count);
1755}
1756
1757void owl_function_status(void)
1758{
1759  char *tmpbuff;
1760  char buff[MAXPATHLEN+1];
1761  time_t start;
1762  int up, days, hours, minutes;
1763  owl_fmtext fm;
1764
1765  owl_fmtext_init_null(&fm);
1766
1767  start=owl_global_get_starttime(&g);
1768
1769  owl_fmtext_append_normal(&fm, "General Information:\n");
1770
1771  owl_fmtext_append_normal(&fm, "  Version: ");
1772  owl_fmtext_append_normal(&fm, OWL_VERSION_STRING);
1773  owl_fmtext_append_normal(&fm, "\n");
1774
1775  owl_fmtext_append_normal(&fm, "  Startup Arguments: ");
1776  owl_fmtext_append_normal(&fm, owl_global_get_startupargs(&g));
1777  owl_fmtext_append_normal(&fm, "\n");
1778
1779  owl_fmtext_append_normal(&fm, "  Current Directory: ");
1780  if(getcwd(buff, MAXPATHLEN) == NULL) {
1781    owl_fmtext_append_normal(&fm, "<Error in getcwd>");
1782  } else {
1783    owl_fmtext_append_normal(&fm, buff);
1784  }
1785  owl_fmtext_append_normal(&fm, "\n");
1786
1787  tmpbuff = owl_util_format_time(localtime(&start));
1788  owl_fmtext_appendf_normal(&fm, "  Startup Time: %s\n", tmpbuff);
1789  g_free(tmpbuff);
1790
1791  up=owl_global_get_runtime(&g);
1792  days=up/86400;
1793  up-=days*86400;
1794  hours=up/3600;
1795  up-=hours*3600;
1796  minutes=up/60;
1797  up-=minutes*60;
1798  owl_fmtext_appendf_normal(&fm, "  Run Time: %i days %2.2i:%2.2i:%2.2i\n", days, hours, minutes, up);
1799
1800  owl_fmtext_append_normal(&fm, "\nProtocol Options:\n");
1801  owl_fmtext_append_normal(&fm, "  Zephyr included    : ");
1802  if (owl_global_is_havezephyr(&g)) {
1803    owl_fmtext_append_normal(&fm, "yes\n");
1804  } else {
1805    owl_fmtext_append_normal(&fm, "no\n");
1806  }
1807  owl_fmtext_append_normal(&fm, "  AIM included       : yes\n");
1808  owl_fmtext_append_normal(&fm, "  Loopback included  : yes\n");
1809
1810
1811  owl_fmtext_append_normal(&fm, "\nBuild Options:\n");
1812  owl_fmtext_append_normal(&fm, "  Stderr redirection : ");
1813#if OWL_STDERR_REDIR
1814  owl_fmtext_append_normal(&fm, "yes\n");
1815#else
1816  owl_fmtext_append_normal(&fm, "no\n");
1817#endif
1818 
1819
1820  owl_fmtext_append_normal(&fm, "\nAIM Status:\n");
1821  owl_fmtext_append_normal(&fm, "  Logged in: ");
1822  if (owl_global_is_aimloggedin(&g)) {
1823    owl_fmtext_append_normal(&fm, owl_global_get_aim_screenname(&g));
1824    owl_fmtext_append_normal(&fm, "\n");
1825  } else {
1826    owl_fmtext_append_normal(&fm, "(not logged in)\n");
1827  }
1828
1829  owl_fmtext_append_normal(&fm, "  Processing events: ");
1830  if (owl_global_is_doaimevents(&g)) {
1831    owl_fmtext_append_normal(&fm, "yes\n");
1832  } else {
1833    owl_fmtext_append_normal(&fm, "no\n");
1834  }
1835
1836  owl_function_popless_fmtext(&fm);
1837  owl_fmtext_cleanup(&fm);
1838}
1839
1840void owl_function_show_term(void)
1841{
1842  owl_fmtext fm;
1843
1844  owl_fmtext_init_null(&fm);
1845  owl_fmtext_appendf_normal(&fm, "Terminal Lines: %i\nTerminal Columns: %i\n",
1846          owl_global_get_lines(&g),
1847          owl_global_get_cols(&g));
1848
1849  if (has_colors()) {
1850    owl_fmtext_append_normal(&fm, "Color: Yes\n");
1851    owl_fmtext_appendf_normal(&fm, "Number of color pairs: %i\n", owl_util_get_colorpairs());
1852    owl_fmtext_appendf_normal(&fm, "Can change colors: %s\n", can_change_color() ? "yes" : "no");
1853  } else {
1854    owl_fmtext_append_normal(&fm, "Color: No\n");
1855  }
1856
1857  owl_function_popless_fmtext(&fm);
1858  owl_fmtext_cleanup(&fm);
1859}
1860
1861/* if type = 0 then normal reply.
1862 * if type = 1 then it's a reply to sender
1863 * if enter = 0 then allow the command to be edited
1864 * if enter = 1 then don't wait for editing
1865 */
1866void owl_function_reply(int type, int enter)
1867{
1868  char *buff=NULL;
1869  const owl_message *m;
1870  const owl_filter *f;
1871 
1872  if (owl_view_get_size(owl_global_get_current_view(&g))==0) {
1873    owl_function_error("No message selected");
1874  } else {
1875    char *cmd;
1876   
1877    m=owl_view_get_element(owl_global_get_current_view(&g), owl_global_get_curmsg(&g));
1878    if (!m) {
1879      owl_function_error("No message selected");
1880      return;
1881    }
1882
1883    /* first check if we catch the reply-lockout filter */
1884    f=owl_global_get_filter(&g, "reply-lockout");
1885    if (f) {
1886      if (owl_filter_message_match(f, m)) {
1887        owl_function_error("Sorry, replies to this message have been disabled by the reply-lockout filter");
1888        return;
1889      }
1890    }
1891
1892    /* then check if it's a question and just bring up the command prompt */
1893    if (owl_message_is_question(m)) {
1894      owl_function_start_command("");
1895      return;
1896    }
1897
1898    if((type == 0 &&
1899        (cmd=owl_perlconfig_message_call_method(m, "replycmd", 0, NULL))) ||
1900       (type == 1 &&
1901        (cmd=owl_perlconfig_message_call_method(m, "replysendercmd", 0, NULL)))) {
1902      buff = cmd;
1903    }
1904
1905    if(!buff) {
1906        owl_function_error("I don't know how to reply to that message.");
1907        return;
1908    }
1909
1910    if (enter) {
1911      owl_history *hist = owl_global_get_cmd_history(&g);
1912      owl_history_store(hist, buff, false);
1913      owl_function_command_norv(buff);
1914    } else {
1915      owl_function_start_command(buff);
1916    }
1917    g_free(buff);
1918  }
1919}
1920
1921void owl_function_zlocate(int argc, const char *const *argv, int auth)
1922{
1923  owl_fmtext fm;
1924  char *ptr;
1925  char *result;
1926  int i;
1927
1928  owl_fmtext_init_null(&fm);
1929
1930  for (i=0; i<argc; i++) {
1931    ptr = long_zuser(argv[i]);
1932    result = owl_zephyr_zlocate(ptr, auth);
1933    owl_fmtext_append_normal(&fm, result);
1934    g_free(result);
1935    g_free(ptr);
1936  }
1937
1938  owl_function_popless_fmtext(&fm);
1939  owl_fmtext_cleanup(&fm);
1940}
1941
1942void owl_callback_command(owl_editwin *e)
1943{
1944  char *rv;
1945  const char *line = owl_editwin_get_text(e);
1946
1947  rv = owl_function_command(line);
1948   if (rv) {
1949    owl_function_makemsg("%s", rv);
1950    g_free(rv);
1951  }
1952}
1953
1954void owl_function_start_command(const char *line)
1955{
1956  owl_editwin *tw;
1957  owl_context *ctx;
1958
1959  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
1960
1961  owl_editwin_set_locktext(tw, "command: ");
1962
1963  owl_editwin_insert_string(tw, line);
1964
1965  ctx = owl_editcontext_new(OWL_CTX_EDITLINE, tw, "editline",
1966                            owl_global_deactivate_editcontext, &g);
1967  owl_global_push_context_obj(&g, ctx);
1968  owl_editwin_set_callback(tw, owl_callback_command);
1969}
1970
1971CALLER_OWN owl_editwin *owl_function_start_question(const char *line)
1972{
1973  owl_editwin *tw;
1974  owl_context *ctx;
1975
1976  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
1977
1978  owl_editwin_set_locktext(tw, line);
1979
1980  ctx = owl_editcontext_new(OWL_CTX_EDITRESPONSE, tw, "editresponse",
1981                            owl_global_deactivate_editcontext, &g);
1982  owl_global_push_context_obj(&g, ctx);
1983  return tw;
1984}
1985
1986CALLER_OWN owl_editwin *owl_function_start_password(const char *line)
1987{
1988  owl_editwin *tw;
1989  owl_context *ctx;
1990
1991  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, NULL);
1992
1993  owl_editwin_set_echochar(tw, '*');
1994
1995  owl_editwin_set_locktext(tw, line);
1996
1997  ctx = owl_editcontext_new(OWL_CTX_EDITRESPONSE, tw, "editresponse",
1998                            owl_global_deactivate_editcontext, &g);
1999  owl_global_push_context_obj(&g, ctx);
2000  return tw;
2001}
2002
2003CALLER_OWN char *owl_function_exec(int argc, const char *const *argv, const char *buff, int type)
2004{
2005  /* if type == 1 display in a popup
2006   * if type == 2 display an admin messages
2007   * if type == 0 return output
2008   * else display in a popup
2009   */
2010  const char *redirect = " 2>&1 < /dev/null";
2011  char *newbuff;
2012  char *out;
2013  FILE *p;
2014
2015#if OWL_STDERR_REDIR
2016  redirect = " < /dev/null";
2017#endif
2018
2019  if (argc<2) {
2020    owl_function_error("Wrong number of arguments to the exec command");
2021    return NULL;
2022  }
2023
2024  buff = skiptokens(buff, 1);
2025  newbuff = g_strdup_printf("exec%s; %s", redirect, buff);
2026
2027  if (type == OWL_OUTPUT_POPUP) {
2028    owl_popexec_new(newbuff);
2029  } else {
2030    p = popen(newbuff, "r");
2031    out = owl_slurp(p);
2032    pclose(p);
2033   
2034    if (type == OWL_OUTPUT_RETURN) {
2035      g_free(newbuff);
2036      return out;
2037    } else if (type == OWL_OUTPUT_ADMINMSG) {
2038      owl_function_adminmsg(buff, out);
2039    }
2040    g_free(out);
2041  }
2042  g_free(newbuff);
2043  return NULL;
2044}
2045
2046CALLER_OWN char *owl_function_perl(int argc, const char *const *argv, const char *buff, int type)
2047{
2048  /* if type == 1 display in a popup
2049   * if type == 2 display an admin messages
2050   * if type == 0 return output
2051   * else display in a popup
2052   */
2053  char *perlout;
2054
2055  if (argc<2) {
2056    owl_function_error("Wrong number of arguments to perl command");
2057    return NULL;
2058  }
2059
2060  /* consume first token (argv[0]) */
2061  buff = skiptokens(buff, 1);
2062
2063  perlout = owl_perlconfig_execute(buff);
2064  if (perlout) { 
2065    if (type == OWL_OUTPUT_POPUP) {
2066      owl_function_popless_text(perlout);
2067    } else if (type == OWL_OUTPUT_ADMINMSG) {
2068      owl_function_adminmsg(buff, perlout);
2069    } else if (type == OWL_OUTPUT_RETURN) {
2070      return perlout;
2071    }
2072    g_free(perlout);
2073  }
2074  return NULL;
2075}
2076
2077/* Change the filter associated with the current view.
2078 * This also figures out which message in the new filter
2079 * should have the pointer.
2080 */
2081void owl_function_change_currentview_filter(const char *filtname)
2082{
2083  owl_view *v;
2084  owl_filter *f;
2085  int curid=-1, newpos, curmsg;
2086  const owl_message *curm=NULL;
2087
2088  v=owl_global_get_current_view(&g);
2089
2090  curmsg=owl_global_get_curmsg(&g);
2091  if (curmsg==-1) {
2092    owl_function_debugmsg("Hit the curmsg==-1 case in change_view");
2093  } else {
2094    curm=owl_view_get_element(v, curmsg);
2095    if (curm) {
2096      curid=owl_message_get_id(curm);
2097      owl_view_save_curmsgid(v, curid);
2098    }
2099  }
2100
2101  f=owl_global_get_filter(&g, filtname);
2102  if (!f) {
2103    owl_function_error("Unknown filter %s", filtname);
2104    return;
2105  }
2106
2107  owl_view_new_filter(v, f);
2108
2109  /* Figure out what to set the current message to.
2110   * - If the view we're leaving has messages in it, go to the closest message
2111   *   to the last message pointed to in that view.
2112   * - If the view we're leaving is empty, try to restore the position
2113   *   from the last time we were in the new view.  */
2114  if (curm) {
2115    newpos = owl_view_get_nearest_to_msgid(v, curid);
2116  } else {
2117    newpos = owl_view_get_nearest_to_saved(v);
2118  }
2119
2120  owl_global_set_curmsg(&g, newpos);
2121  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
2122  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2123  owl_global_set_direction_downwards(&g);
2124}
2125
2126/* Create a new filter, or replace an existing one
2127 * with a new definition. Returns true on success.
2128 */
2129bool owl_function_create_filter(int argc, const char *const *argv)
2130{
2131  owl_filter *f;
2132  const owl_view *v;
2133  int inuse = 0;
2134
2135  if (argc < 2) {
2136    owl_function_error("Wrong number of arguments to filter command");
2137    return false;
2138  }
2139
2140  owl_function_debugmsg("owl_function_create_filter: starting to create filter named %s", argv[1]);
2141
2142  v=owl_global_get_current_view(&g);
2143
2144  /* don't touch the all filter */
2145  if (!strcmp(argv[1], "all")) {
2146    owl_function_error("You may not change the 'all' filter.");
2147    return false;
2148  }
2149
2150  /* deal with the case of trying change the filter color */
2151  if (argc==4 && !strcmp(argv[2], "-c")) {
2152    f=owl_global_get_filter(&g, argv[1]);
2153    if (!f) {
2154      owl_function_error("The filter '%s' does not exist.", argv[1]);
2155      return false;
2156    }
2157    if (owl_util_string_to_color(argv[3])==OWL_COLOR_INVALID) {
2158      owl_function_error("The color '%s' is not available.", argv[3]);
2159      return false;
2160    }
2161    owl_filter_set_fgcolor(f, owl_util_string_to_color(argv[3]));
2162    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2163    return false;
2164  }
2165  if (argc==4 && !strcmp(argv[2], "-b")) {
2166    f=owl_global_get_filter(&g, argv[1]);
2167    if (!f) {
2168      owl_function_error("The filter '%s' does not exist.", argv[1]);
2169      return false;
2170    }
2171    if (owl_util_string_to_color(argv[3])==OWL_COLOR_INVALID) {
2172      owl_function_error("The color '%s' is not available.", argv[3]);
2173      return false;
2174    }
2175    owl_filter_set_bgcolor(f, owl_util_string_to_color(argv[3]));
2176    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2177    return true;
2178  }
2179
2180  /* create the filter and check for errors */
2181  f = owl_filter_new(argv[1], argc-2, argv+2);
2182  if (f == NULL) {
2183    owl_function_error("Invalid filter");
2184    return false;
2185  }
2186
2187  /* if the named filter is in use by the current view, remember it */
2188  if (!strcmp(owl_view_get_filtname(v), argv[1])) {
2189    inuse=1;
2190  }
2191
2192  /* if the named filter already exists, nuke it */
2193  if (owl_global_get_filter(&g, argv[1])) {
2194    owl_global_remove_filter(&g, argv[1]);
2195  }
2196
2197  /* add the filter */
2198  owl_global_add_filter(&g, f);
2199
2200  /* if it was in use by the current view then update */
2201  if (inuse) {
2202    owl_function_change_currentview_filter(argv[1]);
2203  }
2204  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2205  return true;
2206}
2207
2208/* If 'filtername' does not start with 'not-' create a filter named
2209 * 'not-<filtername>' defined as "not filter <filtername>".  If the
2210 * filter 'not-<filtername>' already exists, do not overwrite it.  If
2211 * 'filtername' begins with 'not-' and a filter 'filtername' already
2212 * exists, then do nothing.  If the filter 'filtername' does not
2213 * exist, create it and define it as 'not filter <filtername>'
2214 *
2215 * Returns the name of the negated filter, which the caller must free.
2216 */
2217CALLER_OWN char *owl_function_create_negative_filter(const char *filtername)
2218{
2219  char *newname;
2220  const owl_filter *tmpfilt;
2221  const char *argv[5];
2222
2223  owl_function_debugmsg("owl_function_create_negative_filter");
2224 
2225  if (!strncmp(filtername, "not-", 4)) {
2226    newname=g_strdup(filtername+4);
2227  } else {
2228    newname=g_strdup_printf("not-%s", filtername);
2229  }
2230
2231  tmpfilt=owl_global_get_filter(&g, newname);
2232  if (!tmpfilt) {
2233    argv[0]="filter"; /* anything is fine here */
2234    argv[1]=newname;
2235    argv[2]="not";
2236    argv[3]="filter";
2237    argv[4]=filtername;
2238    owl_function_create_filter(5, argv);
2239  }
2240
2241  owl_function_debugmsg("owl_function_create_negative_filter: returning with %s", newname);
2242  return(newname);
2243}
2244
2245void owl_function_show_filters(void)
2246{
2247  const owl_filter *f;
2248  GList *fl;
2249  owl_fmtext fm;
2250
2251  owl_fmtext_init_null(&fm);
2252
2253  owl_fmtext_append_bold(&fm, "Filters:\n");
2254
2255  for (fl = g.filterlist; fl; fl = g_list_next(fl)) {
2256    f = fl->data;
2257    owl_fmtext_append_normal(&fm, "   ");
2258    owl_fmtext_append_normal_color(&fm, owl_filter_get_name(f),
2259                                   owl_filter_get_fgcolor(f),
2260                                   owl_filter_get_bgcolor(f));
2261    owl_fmtext_append_normal(&fm, "\n");
2262  }
2263  owl_function_popless_fmtext(&fm);
2264  owl_fmtext_cleanup(&fm);
2265}
2266
2267void owl_function_show_filter(const char *name)
2268{
2269  const owl_filter *f;
2270  char *buff, *tmp;
2271
2272  f=owl_global_get_filter(&g, name);
2273  if (!f) {
2274    owl_function_error("There is no filter named %s", name);
2275    return;
2276  }
2277  tmp = owl_filter_print(f);
2278  buff = g_strdup_printf("%s: %s", owl_filter_get_name(f), tmp);
2279  owl_function_popless_text(buff);
2280  g_free(buff);
2281  g_free(tmp);
2282}
2283
2284void owl_function_show_zpunts(void)
2285{
2286  const owl_filter *f;
2287  const GPtrArray *fl;
2288  char *tmp;
2289  owl_fmtext fm;
2290  int i;
2291
2292  owl_fmtext_init_null(&fm);
2293
2294  fl=owl_global_get_puntlist(&g);
2295  owl_fmtext_append_bold(&fm, "Active zpunt filters:\n");
2296
2297  for (i = 0; i < fl->len; i++) {
2298    f = fl->pdata[i];
2299    owl_fmtext_appendf_normal(&fm, "[% 2d] ", i+1);
2300    tmp = owl_filter_print(f);
2301    owl_fmtext_append_normal(&fm, tmp);
2302    g_free(tmp);
2303  }
2304  owl_function_popless_fmtext(&fm);
2305  owl_fmtext_cleanup(&fm);
2306}
2307
2308/* Create a filter for a class, instance if one doesn't exist.  If
2309 * instance is NULL then catch all messgaes in the class.  Returns the
2310 * name of the filter or null.  The caller must free this name.
2311 * If 'related' is nonzero, encompass unclasses and .d classes as well.
2312 */
2313CALLER_OWN char *owl_function_classinstfilt(const char *c, const char *i, int related) 
2314{
2315  owl_filter *f;
2316  char *filtname;
2317  char *tmpclass, *tmpinstance = NULL;
2318  char *class, *instance = NULL;
2319  GString *buf;
2320
2321  if (related) {
2322    class = owl_util_baseclass(c);
2323    if (i) {
2324      instance = owl_util_baseclass(i);
2325    }
2326  } else {
2327    class = g_strdup(c);
2328    if (i) {
2329      instance = g_strdup(i);
2330    }
2331  }
2332
2333  /* name for the filter */
2334  if (!instance) {
2335    filtname = g_strdup_printf("%sclass-%s", related ? "related-" : "", class);
2336  } else {
2337    filtname = g_strdup_printf("%sclass-%s-instance-%s", related ? "related-" : "", class, instance);
2338  }
2339  /* downcase it */
2340  {
2341    char *temp = g_utf8_strdown(filtname, -1);
2342    if (temp) {
2343      g_free(filtname);
2344      filtname = temp;
2345    }
2346  }
2347 
2348  /* if it already exists then go with it.  This lets users override */
2349  if (owl_global_get_filter(&g, filtname)) {
2350    goto done;
2351  }
2352
2353  /* create the new filter */
2354  tmpclass=owl_text_quote(class, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2355  if (instance) {
2356    tmpinstance=owl_text_quote(instance, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2357  }
2358
2359  buf = g_string_new("");
2360  owl_string_appendf_quoted(buf,
2361                            related ? "class ^(un)*%q(\\.d)*$" : "class ^%q$",
2362                            tmpclass);
2363
2364  if (tmpinstance) {
2365    owl_string_appendf_quoted(buf,
2366                              related ?
2367                              " and ( instance ^(un)*%q(\\.d)*$ )" :
2368                              " and instance ^%q$",
2369                              tmpinstance);
2370  }
2371  g_free(tmpclass);
2372  g_free(tmpinstance);
2373
2374  f = owl_filter_new_fromstring(filtname, buf->str);
2375  g_string_free(buf, true);
2376  if (f == NULL) {
2377    /* Couldn't make a filter for some reason. Return NULL. */
2378    owl_function_error("Error creating filter '%s'", filtname);
2379    g_free(filtname);
2380    filtname = NULL;
2381    goto done;
2382  }
2383
2384  /* add it to the global list */
2385  owl_global_add_filter(&g, f);
2386
2387done:
2388  g_free(class);
2389  g_free(instance);
2390  return(filtname);
2391}
2392
2393/* Create a filter for personal zephyrs to or from the specified
2394 * zephyr user.  Includes login/logout notifications for the user.
2395 * The name of the filter will be 'user-<shortuser>'.  If a filter already
2396 * exists with this name, no new filter will be created.  This allows
2397 * the configuration to override this function.  Returns the name of
2398 * the filter, which the caller must free.
2399 */
2400CALLER_OWN char *owl_function_zuserfilt(const char *longuser)
2401{
2402  owl_filter *f;
2403  char *argbuff, *esclonguser, *shortuser, *filtname;
2404
2405  /* name for the filter */
2406  shortuser = short_zuser(longuser);
2407  filtname = g_strdup_printf("user-%s", shortuser);
2408  g_free(shortuser);
2409
2410  /* if it already exists then go with it.  This lets users override */
2411  if (owl_global_get_filter(&g, filtname)) {
2412    return filtname;
2413  }
2414
2415  /* create the new-internal filter */
2416  esclonguser = owl_text_quote(longuser, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2417
2418  argbuff=owl_string_build_quoted("( type ^zephyr$ and filter personal and "
2419      "( ( direction ^in$ and sender ^%q$ ) or ( direction ^out$ and "
2420      "recipient ^%q$ ) ) ) or ( ( class ^login$ ) and ( sender ^%q$ ) )",
2421      esclonguser, esclonguser, esclonguser);
2422  g_free(esclonguser);
2423
2424  f = owl_filter_new_fromstring(filtname, argbuff);
2425  g_free(argbuff);
2426
2427  if (f == NULL) {
2428    /* Couldn't make a filter for some reason. Return NULL. */
2429    owl_function_error("Error creating filter '%s'", filtname);
2430    g_free(filtname);
2431    return NULL;
2432  }
2433
2434  /* add it to the global list */
2435  owl_global_add_filter(&g, f);
2436
2437  return(filtname);
2438}
2439
2440/* Create a filter for AIM IM messages to or from the specified
2441 * screenname.  The name of the filter will be 'aimuser-<user>'.  If a
2442 * filter already exists with this name, no new filter will be
2443 * created.  This allows the configuration to override this function.
2444 * Returns the name of the filter, which the caller must free.
2445 */
2446CALLER_OWN char *owl_function_aimuserfilt(const char *user)
2447{
2448  owl_filter *f;
2449  char *argbuff, *filtname;
2450  char *escuser;
2451
2452  /* name for the filter */
2453  filtname=g_strdup_printf("aimuser-%s", user);
2454
2455  /* if it already exists then go with it.  This lets users override */
2456  if (owl_global_get_filter(&g, filtname)) {
2457    return filtname;
2458  }
2459
2460  /* create the new-internal filter */
2461  escuser = owl_text_quote(user, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2462
2463  argbuff = g_strdup_printf(
2464      "( type ^aim$ and ( ( sender ^%1$s$ and recipient ^%2$s$ ) or "
2465      "( sender ^%2$s$ and recipient ^%1$s$ ) ) )",
2466      escuser, owl_global_get_aim_screenname_for_filters(&g));
2467  g_free(escuser);
2468
2469  f = owl_filter_new_fromstring(filtname, argbuff);
2470  g_free(argbuff);
2471
2472  if (f == NULL) {
2473    owl_function_error("Error creating filter '%s'", filtname);
2474    g_free(filtname);
2475    return NULL;
2476  }
2477
2478  /* add it to the global list */
2479  owl_global_add_filter(&g, f);
2480
2481  return(filtname);
2482}
2483
2484CALLER_OWN char *owl_function_typefilt(const char *type)
2485{
2486  owl_filter *f;
2487  char *argbuff, *filtname, *esctype;
2488
2489  /* name for the filter */
2490  filtname=g_strdup_printf("type-%s", type);
2491
2492  /* if it already exists then go with it.  This lets users override */
2493  if (owl_global_get_filter(&g, filtname)) {
2494    return filtname;
2495  }
2496
2497  /* create the new-internal filter */
2498  esctype = owl_text_quote(type, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2499
2500  argbuff = owl_string_build_quoted("type ^%q$", esctype);
2501  g_free(esctype);
2502
2503  f = owl_filter_new_fromstring(filtname, argbuff);
2504  g_free(argbuff);
2505
2506  if (f == NULL) {
2507    owl_function_error("Error creating filter '%s'", filtname);
2508    g_free(filtname);
2509    return NULL;
2510  }
2511
2512  /* add it to the global list */
2513  owl_global_add_filter(&g, f);
2514
2515  return filtname;
2516}
2517
2518/* If flag is 1, marks for deletion.  If flag is 0,
2519 * unmarks for deletion. */
2520void owl_function_delete_curview_msgs(int flag)
2521{
2522  const owl_view *v;
2523  int i, j;
2524
2525  v=owl_global_get_current_view(&g);
2526  j=owl_view_get_size(v);
2527  for (i=0; i<j; i++) {
2528    if (flag == 1) {
2529      owl_message_mark_delete(owl_view_get_element(v, i));
2530    } else if (flag == 0) {
2531      owl_message_unmark_delete(owl_view_get_element(v, i));
2532    }
2533  }
2534
2535  owl_function_makemsg("%i messages marked for %sdeletion", j, flag?"":"un");
2536
2537  owl_mainwin_redisplay(owl_global_get_mainwin(&g)); 
2538}
2539
2540static CALLER_OWN char *owl_function_smartfilter_cc(const owl_message *m)
2541{
2542  const char *ccs;
2543  char *ccs_quoted;
2544  char *filtname;
2545  owl_filter *f;
2546  GString *buf;
2547
2548  ccs = owl_message_get_attribute_value(m, "zephyr_ccs");
2549
2550  filtname = g_strdup_printf("conversation-%s", ccs);
2551  g_strdelimit(filtname, " ", '-');
2552
2553  if (owl_global_get_filter(&g, filtname)) {
2554    return filtname;
2555  }
2556
2557  buf = g_string_new("type ^zephyr$ and filter personal and "
2558                     "zephyr_ccs ^");
2559  ccs_quoted = owl_text_quote(ccs, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2560  owl_string_append_quoted_arg(buf, ccs_quoted);
2561  g_string_append_c(buf, '$');
2562  g_free(ccs_quoted);
2563
2564  f = owl_filter_new_fromstring(filtname, buf->str);
2565  g_string_free(buf, true);
2566
2567  if (f == NULL) {
2568    owl_function_error("Error creating filter '%s'", filtname);
2569    g_free(filtname);
2570    return NULL;
2571  }
2572
2573  owl_global_add_filter(&g, f);
2574
2575  return filtname;
2576}
2577
2578/* Create a filter based on the current message.  Returns the name of
2579 * a filter or null.  The caller must free this name.
2580 *
2581 * if the curmsg is a personal zephyr return a filter name
2582 *    to the zephyr conversation with that user.
2583 * If the curmsg is a zephyr class message, instance foo, recip *,
2584 *    return a filter name to the class, inst.
2585 * If the curmsg is a zephyr class message and type==0 then
2586 *    return a filter name for just the class.
2587 * If the curmsg is a zephyr class message and type==1 then
2588 *    return a filter name for the class and instance.
2589 * If the curmsg is a personal AIM message returna  filter
2590 *    name to the AIM conversation with that user
2591 */
2592CALLER_OWN char *owl_function_smartfilter(int type, int invert_related)
2593{
2594  const owl_view *v;
2595  const owl_message *m;
2596  char *filtname = NULL;
2597  const char *argv[2], *zperson;
2598  int related = owl_global_is_narrow_related(&g) ^ invert_related;
2599
2600  v=owl_global_get_current_view(&g);
2601  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
2602
2603  if (!m || owl_view_get_size(v)==0) {
2604    owl_function_error("No message selected\n");
2605    return(NULL);
2606  }
2607
2608  /* very simple handling of admin messages for now */
2609  if (owl_message_is_type_admin(m)) {
2610    return(owl_function_typefilt("admin"));
2611  }
2612
2613  /* very simple handling of loopback messages for now */
2614  if (owl_message_is_type_loopback(m)) {
2615    return(owl_function_typefilt("loopback"));
2616  }
2617
2618  /* aim messages */
2619  if (owl_message_is_type_aim(m)) {
2620    if (owl_message_is_direction_in(m)) {
2621      filtname=owl_function_aimuserfilt(owl_message_get_sender(m));
2622    } else if (owl_message_is_direction_out(m)) {
2623      filtname=owl_function_aimuserfilt(owl_message_get_recipient(m));
2624    }
2625    return(filtname);
2626  }
2627
2628  /* narrow personal and login messages to the sender or recip as appropriate */
2629  if (owl_message_is_type_zephyr(m)) {
2630    if (owl_message_is_personal(m) || owl_message_is_loginout(m)) {
2631      if (owl_message_get_attribute_value(m, "zephyr_ccs") != NULL) {
2632        return owl_function_smartfilter_cc(m);
2633      }
2634
2635      if (owl_message_is_direction_in(m)) {
2636        zperson = owl_message_get_sender(m);
2637      } else {
2638        zperson = owl_message_get_recipient(m);
2639      }
2640      filtname = owl_function_zuserfilt(zperson);
2641      return filtname;
2642    }
2643
2644    /* narrow class MESSAGE, instance foo, recip * messages to class, inst */
2645    if (!strcasecmp(owl_message_get_class(m), "message")) {
2646      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m), related);
2647      return(filtname);
2648    }
2649
2650    /* otherwise narrow to the class */
2651    if (type==0) {
2652      filtname=owl_function_classinstfilt(owl_message_get_class(m), NULL, related);
2653    } else if (type==1) {
2654      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m), related);
2655    }
2656    return(filtname);
2657  }
2658
2659  /* pass it off to perl */
2660  argv[0] = type ? "1" : "0";
2661  argv[1] = related ? "1" : "0";
2662  return owl_perlconfig_message_call_method(m, "smartfilter", 2, argv);
2663}
2664
2665void owl_function_smartzpunt(int type)
2666{
2667  /* Starts a zpunt command based on the current class,instance pair.
2668   * If type=0, uses just class.  If type=1, uses instance as well. */
2669  const owl_view *v;
2670  const owl_message *m;
2671  const char *mclass, *minst;
2672  GString *buf;
2673 
2674  v=owl_global_get_current_view(&g);
2675  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
2676
2677  if (!m || owl_view_get_size(v)==0) {
2678    owl_function_error("No message selected\n");
2679    return;
2680  }
2681
2682  /* for now we skip admin messages. */
2683  if (owl_message_is_type_admin(m)
2684      || owl_message_is_loginout(m)
2685      || !owl_message_is_type_zephyr(m)) {
2686    owl_function_error("smartzpunt doesn't support this message type.");
2687    return;
2688  }
2689
2690  mclass = owl_message_get_class(m);
2691  minst = owl_message_get_instance(m);
2692  if (!mclass || !*mclass || *mclass==' '
2693      || (!strcasecmp(mclass, "message") && !strcasecmp(minst, "personal"))
2694      || (type && (!minst || !*minst|| *minst==' '))) {
2695    owl_function_error("smartzpunt can't safely do this for <%s,%s>",
2696                         mclass, minst);
2697  } else {
2698    buf = g_string_new("start-command zpunt ");
2699    owl_string_append_quoted_arg(buf, mclass);
2700    if (type) {
2701      g_string_append_c(buf, ' ');
2702      owl_string_append_quoted_arg(buf, minst);
2703    } else {
2704      g_string_append(buf, " *");
2705    }
2706    owl_function_command_norv(buf->str);
2707    g_string_free(buf, true);
2708  }
2709}
2710
2711/* Set the color of the current view's filter to
2712 * be 'color'
2713 */
2714void owl_function_color_current_filter(const char *fgcolor, const char *bgcolor)
2715{
2716  const char *name;
2717
2718  name=owl_view_get_filtname(owl_global_get_current_view(&g));
2719  owl_function_color_filter(name, fgcolor, bgcolor);
2720}
2721
2722/* Set the color of the filter 'filter' to be 'color'.  If the color
2723 * name does not exist, return -1, if the filter does not exist or is
2724 * the "all" filter, return -2.  Return 0 on success
2725 */
2726int owl_function_color_filter(const char *filtname, const char *fgcolor, const char *bgcolor)
2727{
2728  owl_filter *f;
2729
2730  f=owl_global_get_filter(&g, filtname);
2731  if (!f) {
2732    owl_function_error("Unknown filter");
2733    return(-2);
2734  }
2735
2736  /* don't touch the all filter */
2737  if (!strcmp(filtname, "all")) {
2738    owl_function_error("You may not change the 'all' filter.");
2739    return(-2);
2740  }
2741
2742  if (owl_util_string_to_color(fgcolor)==OWL_COLOR_INVALID) {
2743    owl_function_error("No color named '%s' avilable.", fgcolor);
2744    return(-1);
2745  }
2746
2747
2748  if (bgcolor != NULL) {
2749    if (owl_util_string_to_color(bgcolor)==OWL_COLOR_INVALID) {
2750      owl_function_error("No color named '%s' avilable.", bgcolor);
2751      return(-1);
2752    }
2753    owl_filter_set_bgcolor(f, owl_util_string_to_color(bgcolor));
2754  }
2755  owl_filter_set_fgcolor(f, owl_util_string_to_color(fgcolor));
2756 
2757  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2758  return(0);
2759}
2760
2761void owl_function_show_colors(void)
2762{
2763  owl_fmtext fm;
2764  int i; 
2765 
2766  owl_fmtext_init_null(&fm);
2767  owl_fmtext_append_normal(&fm,"default:  ");
2768  owl_fmtext_append_normal_color(&fm, "default\n", OWL_COLOR_DEFAULT, OWL_COLOR_DEFAULT);
2769
2770  owl_fmtext_append_normal(&fm,"black:    ");
2771  owl_fmtext_append_normal_color(&fm, "black\n", OWL_COLOR_BLACK, OWL_COLOR_DEFAULT);
2772
2773  owl_fmtext_append_normal(&fm,"red:      ");
2774  owl_fmtext_append_normal_color(&fm, "red\n", OWL_COLOR_RED, OWL_COLOR_DEFAULT);
2775
2776  owl_fmtext_append_normal(&fm,"green:    ");
2777  owl_fmtext_append_normal_color(&fm, "green\n", OWL_COLOR_GREEN, OWL_COLOR_DEFAULT);
2778
2779  owl_fmtext_append_normal(&fm,"yellow:   ");
2780  owl_fmtext_append_normal_color(&fm, "yellow\n", OWL_COLOR_YELLOW, OWL_COLOR_DEFAULT);
2781
2782  owl_fmtext_append_normal(&fm,"blue:     ");
2783  owl_fmtext_append_normal_color(&fm, "blue\n", OWL_COLOR_BLUE, OWL_COLOR_DEFAULT);
2784
2785  owl_fmtext_append_normal(&fm,"magenta:  ");
2786  owl_fmtext_append_normal_color(&fm, "magenta\n", OWL_COLOR_MAGENTA, OWL_COLOR_DEFAULT);
2787
2788  owl_fmtext_append_normal(&fm,"cyan:     ");
2789  owl_fmtext_append_normal_color(&fm, "cyan\n", OWL_COLOR_CYAN, OWL_COLOR_DEFAULT);
2790
2791  owl_fmtext_append_normal(&fm,"white:    ");
2792  owl_fmtext_append_normal_color(&fm, "white\n", OWL_COLOR_WHITE, OWL_COLOR_DEFAULT);
2793
2794  for(i = 8; i < COLORS; ++i) {
2795    char* str1 = g_strdup_printf("%4i:     ",i);
2796    char* str2 = g_strdup_printf("%i\n",i);
2797    owl_fmtext_append_normal(&fm,str1);
2798    owl_fmtext_append_normal_color(&fm, str2, i, OWL_COLOR_DEFAULT);
2799    g_free(str1);
2800     g_free(str2);
2801  }
2802 
2803  owl_function_popless_fmtext(&fm);
2804  owl_fmtext_cleanup(&fm);
2805}
2806
2807/* add the given class, inst, recip to the punt list for filtering.
2808 *   if direction==0 then punt
2809 *   if direction==1 then unpunt
2810 */
2811void owl_function_zpunt(const char *class, const char *inst, const char *recip, int direction)
2812{
2813  GPtrArray *argv;
2814  char *quoted;
2815
2816  argv = g_ptr_array_new();
2817  if (!strcmp(class, "*")) {
2818    g_ptr_array_add(argv, g_strdup("class"));
2819    g_ptr_array_add(argv, g_strdup(".*"));
2820  } else {
2821    quoted=owl_text_quote(class, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2822    g_ptr_array_add(argv, g_strdup("class"));
2823    g_ptr_array_add(argv, g_strdup_printf("^(un)*%s(\\.d)*$", quoted));
2824    g_free(quoted);
2825  }
2826  if (!strcmp(inst, "*")) {
2827    g_ptr_array_add(argv, g_strdup("and"));
2828    g_ptr_array_add(argv, g_strdup("instance"));
2829    g_ptr_array_add(argv, g_strdup(".*"));
2830  } else {
2831    quoted=owl_text_quote(inst, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2832    g_ptr_array_add(argv, g_strdup("and"));
2833    g_ptr_array_add(argv, g_strdup("instance"));
2834    g_ptr_array_add(argv, g_strdup_printf("^(un)*%s(\\.d)*$", quoted));
2835    g_free(quoted);
2836  }
2837  if (!strcmp(recip, "*")) {
2838    /* nothing */
2839  } else {
2840    if(!strcmp(recip, "%me%")) {
2841      recip = owl_zephyr_get_sender();
2842    }
2843    quoted=owl_text_quote(recip, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2844    g_ptr_array_add(argv, g_strdup("and"));
2845    g_ptr_array_add(argv, g_strdup("recipient"));
2846    g_ptr_array_add(argv, g_strdup_printf("^%s$", quoted));
2847    g_free(quoted);
2848  }
2849
2850  owl_function_punt(argv->len, (const char *const*) argv->pdata, direction);
2851  owl_ptr_array_free(argv, g_free);
2852}
2853
2854void owl_function_punt(int argc, const char *const *argv, int direction)
2855{
2856  owl_filter *f;
2857  GPtrArray *fl;
2858  int i;
2859  fl=owl_global_get_puntlist(&g);
2860
2861  /* first, create the filter */
2862  f = owl_filter_new("punt-filter", argc, argv);
2863  if (f == NULL) {
2864    owl_function_error("Error creating filter for zpunt");
2865    return;
2866  }
2867
2868  /* Check for an identical filter */
2869  for (i = 0; i < fl->len; i++) {
2870    if (owl_filter_equiv(f, fl->pdata[i])) {
2871      owl_function_debugmsg("found an equivalent punt filter");
2872      /* if we're punting, then just silently bow out on this duplicate */
2873      if (direction==0) {
2874        owl_filter_delete(f);
2875        return;
2876      }
2877
2878      /* if we're unpunting, then remove this filter from the puntlist */
2879      if (direction==1) {
2880        owl_filter_delete(g_ptr_array_remove_index(fl, i));
2881        owl_filter_delete(f);
2882        return;
2883      }
2884    }
2885  }
2886
2887  if (direction == 0) {
2888    owl_function_debugmsg("punting");
2889    /* If we're punting, add the filter to the global punt list */
2890    g_ptr_array_add(fl, f);
2891  } else if (direction == 1) {
2892    owl_function_makemsg("No matching punt filter");
2893 }
2894}
2895
2896void owl_function_show_keymaps(void)
2897{
2898  GPtrArray *l;
2899  owl_fmtext fm;
2900  const owl_keymap *km;
2901  const owl_keyhandler *kh;
2902  int i;
2903  const char *kmname;
2904
2905  kh = owl_global_get_keyhandler(&g);
2906  owl_fmtext_init_null(&fm);
2907  owl_fmtext_append_bold(&fm, "Keymaps:   ");
2908  owl_fmtext_append_normal(&fm, "(use 'show keymap <name>' for details)\n");
2909  l = owl_keyhandler_get_keymap_names(kh);
2910  owl_fmtext_append_list(&fm, l, "\n", owl_function_keymap_summary);
2911  owl_fmtext_append_normal(&fm, "\n");
2912
2913  for (i = 0; i < l->len; i++) {
2914    kmname = l->pdata[i];
2915    km = owl_keyhandler_get_keymap(kh, kmname);
2916    owl_fmtext_append_bold(&fm, "\n\n----------------------------------------------------------------------------------------------------\n\n");
2917    owl_keymap_get_details(km, &fm, 0);
2918  }
2919  owl_fmtext_append_normal(&fm, "\n");
2920 
2921  owl_function_popless_fmtext(&fm);
2922  owl_ptr_array_free(l, g_free);
2923  owl_fmtext_cleanup(&fm);
2924}
2925
2926CALLER_OWN char *owl_function_keymap_summary(const char *name)
2927{
2928  const owl_keymap *km
2929    = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), name);
2930  if (km) return owl_keymap_summary(km);
2931  else return(NULL);
2932}
2933
2934/* TODO: implement for real */
2935void owl_function_show_keymap(const char *name)
2936{
2937  owl_fmtext fm;
2938  const owl_keymap *km;
2939
2940  owl_fmtext_init_null(&fm);
2941  km = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), name);
2942  if (km) {
2943    owl_keymap_get_details(km, &fm, 1);
2944  } else {
2945    owl_fmtext_append_normal(&fm, "No such keymap...\n");
2946  } 
2947  owl_function_popless_fmtext(&fm);
2948  owl_fmtext_cleanup(&fm);
2949}
2950
2951void owl_function_help_for_command(const char *cmdname)
2952{
2953  owl_fmtext fm;
2954
2955  owl_fmtext_init_null(&fm);
2956  owl_cmd_get_help(owl_global_get_cmddict(&g), cmdname, &fm);
2957  owl_function_popless_fmtext(&fm); 
2958  owl_fmtext_cleanup(&fm);
2959}
2960
2961void owl_function_set_search(const char *string)
2962{
2963  owl_regex re;
2964
2965  if (string && owl_regex_create_quoted(&re, string) == 0) {
2966    owl_global_set_search_re(&g, &re);
2967    owl_regex_cleanup(&re);
2968  } else {
2969    owl_global_set_search_re(&g, NULL);
2970  }
2971}
2972
2973void owl_function_search_helper(int consider_current, int direction)
2974{
2975  /* move to a message that contains the string.  If direction is
2976   * OWL_DIRECTION_DOWNWARDS then search fowards, if direction is
2977   * OWL_DIRECTION_UPWARDS then search backwards.
2978   *
2979   * If consider_current is true then it will stay on the
2980   * current message if it contains the string.
2981   */
2982
2983  const owl_view *v;
2984  int viewsize, i, curmsg, start;
2985  owl_message *m;
2986
2987  v=owl_global_get_current_view(&g);
2988  viewsize=owl_view_get_size(v);
2989  curmsg=owl_global_get_curmsg(&g);
2990 
2991  if (viewsize==0) {
2992    owl_function_makemsg("No messages present");
2993    return;
2994  }
2995
2996  if (consider_current) {
2997    start=curmsg;
2998  } else if (direction==OWL_DIRECTION_DOWNWARDS) {
2999    start=curmsg+1;
3000  } else {
3001    start=curmsg-1;
3002  }
3003
3004  /* bounds check */
3005  if (start>=viewsize || start<0) {
3006    owl_function_makemsg("No further matches found");
3007    return;
3008  }
3009
3010  for (i=start; i<viewsize && i>=0;) {
3011    m=owl_view_get_element(v, i);
3012    if (owl_message_search(m, owl_global_get_search_re(&g))) {
3013      owl_global_set_curmsg(&g, i);
3014      owl_function_calculate_topmsg(direction);
3015      owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3016      if (direction==OWL_DIRECTION_DOWNWARDS) {
3017        owl_global_set_direction_downwards(&g);
3018      } else {
3019        owl_global_set_direction_upwards(&g);
3020      }
3021      return;
3022    }
3023    if (direction==OWL_DIRECTION_DOWNWARDS) {
3024      i++;
3025    } else {
3026      i--;
3027    }
3028    if (owl_global_take_interrupt(&g)) {
3029      owl_function_makemsg("Search interrupted!");
3030      owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3031      return;
3032    }
3033  }
3034  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3035  owl_function_makemsg("No matches found");
3036}
3037
3038/* strips formatting from ztext and returns the unformatted text.
3039 * caller is responsible for freeing. */
3040CALLER_OWN char *owl_function_ztext_stylestrip(const char *zt)
3041{
3042  owl_fmtext fm;
3043  char *plaintext;
3044
3045  owl_fmtext_init_null(&fm);
3046  owl_fmtext_append_ztext(&fm, zt);
3047  plaintext = owl_fmtext_print_plain(&fm);
3048  owl_fmtext_cleanup(&fm);
3049  return(plaintext);
3050}
3051
3052/* Popup a buddylisting.  If filename is NULL use the default .anyone */
3053void owl_function_buddylist(int aim, int zephyr, const char *filename)
3054{
3055  int i, j, idle;
3056  int interrupted = 0;
3057  owl_fmtext fm;
3058  const owl_buddylist *bl;
3059  const owl_buddy *b;
3060  char *timestr;
3061#ifdef HAVE_LIBZEPHYR
3062  int x;
3063  GPtrArray *anyone;
3064  const char *user;
3065  char *tmp;
3066  ZLocations_t location[200];
3067  int numlocs, ret;
3068#endif
3069
3070  owl_fmtext_init_null(&fm);
3071
3072  /* AIM first */
3073  if (aim && owl_global_is_aimloggedin(&g)) {
3074    bl=owl_global_get_buddylist(&g);
3075
3076    owl_fmtext_append_bold(&fm, "AIM users logged in:\n");
3077    /* we're assuming AIM for now */
3078    j=owl_buddylist_get_size(bl);
3079    for (i=0; i<j; i++) {
3080      b=owl_buddylist_get_buddy_n(bl, i);
3081      idle=owl_buddy_get_idle_time(b);
3082      if (idle!=0) {
3083        timestr=owl_util_format_minutes(idle);
3084      } else {
3085        timestr=g_strdup("");
3086      }
3087      owl_fmtext_appendf_normal(&fm, "  %-20.20s %-12.12s\n", owl_buddy_get_name(b), timestr);
3088      g_free(timestr);
3089    }
3090  }
3091
3092#ifdef HAVE_LIBZEPHYR
3093  if (zephyr) {
3094    if(!owl_global_is_havezephyr(&g)) {
3095      owl_function_error("Zephyr currently not available.");
3096    } else {
3097      owl_fmtext_append_bold(&fm, "Zephyr users logged in:\n");
3098      anyone = owl_zephyr_get_anyone_list(filename);
3099      if (anyone == NULL) {
3100        if (errno == ENOENT) {
3101          owl_fmtext_append_normal(&fm, " You have not added any zephyr buddies.  Use the\n");
3102          owl_fmtext_append_normal(&fm, " command ':addbuddy zephyr ");
3103          owl_fmtext_append_bold(  &fm, "<username>");
3104          owl_fmtext_append_normal(&fm, "'.\n");
3105        } else {
3106          owl_fmtext_append_normal(&fm, " Could not read zephyr buddies from the .anyone file.\n");
3107        }
3108      } else {
3109        for (i = 0; i < anyone->len; i++) {
3110          user = anyone->pdata[i];
3111          ret=ZLocateUser(zstr(user), &numlocs, ZAUTH);
3112
3113          if (owl_global_take_interrupt(&g)) {
3114            interrupted = 1;
3115            owl_function_makemsg("Interrupted!");
3116            break;
3117          }
3118
3119          if (ret!=ZERR_NONE) {
3120            owl_function_error("Error getting location for %s", user);
3121            continue;
3122          }
3123
3124          numlocs=200;
3125          ret=ZGetLocations(location, &numlocs);
3126          if (ret==0) {
3127            for (x=0; x<numlocs; x++) {
3128              tmp=short_zuser(user);
3129              owl_fmtext_appendf_normal(&fm, "  %-10.10s %-24.24s %-12.12s  %20.20s\n",
3130                                        tmp,
3131                                        location[x].host,
3132                                        location[x].tty,
3133                                        location[x].time);
3134              g_free(tmp);
3135            }
3136            if (numlocs>=200) {
3137              owl_fmtext_append_normal(&fm, "  Too many locations found for this user, truncating.\n");
3138            }
3139          }
3140        }
3141      }
3142      owl_ptr_array_free(anyone, g_free);
3143    }
3144  }
3145#endif
3146
3147  if (aim && zephyr) {
3148    if (owl_perlconfig_is_function("BarnOwl::Hooks::_get_blist")) {
3149      char * perlblist = owl_perlconfig_execute("BarnOwl::Hooks::_get_blist()");
3150      if (perlblist) {
3151        owl_fmtext_append_ztext(&fm, perlblist);
3152        g_free(perlblist);
3153      }
3154    }
3155  }
3156
3157  if(!interrupted) {
3158    owl_function_popless_fmtext(&fm);
3159  }
3160  owl_fmtext_cleanup(&fm);
3161}
3162
3163/* Dump messages in the current view to the file 'filename'. */
3164void owl_function_dump(const char *filename) 
3165{
3166  int i, j;
3167  owl_message *m;
3168  const owl_view *v;
3169  FILE *file;
3170  char *plaintext;
3171
3172  v=owl_global_get_current_view(&g);
3173
3174  /* in the future make it ask yes/no */
3175  /*
3176  ret=stat(filename, &sbuf);
3177  if (!ret) {
3178    ret=owl_function_askyesno("File exists, continue? [Y/n]");
3179    if (!ret) return;
3180  }
3181  */
3182
3183  file=fopen(filename, "w");
3184  if (!file) {
3185    owl_function_error("Error opening file");
3186    return;
3187  }
3188
3189  j=owl_view_get_size(v);
3190  for (i=0; i<j; i++) {
3191    m=owl_view_get_element(v, i);
3192    plaintext = owl_strip_format_chars(owl_message_get_text(m));
3193    if (plaintext) {
3194      fputs(plaintext, file);
3195      g_free(plaintext);
3196    }
3197  }
3198  fclose(file);
3199  owl_function_makemsg("Messages dumped to %s", filename);
3200}
3201
3202void owl_function_do_newmsgproc(void)
3203{
3204  if (owl_global_get_newmsgproc(&g) && strcmp(owl_global_get_newmsgproc(&g), "")) {
3205    /* if there's a process out there, we need to check on it */
3206    if (owl_global_get_newmsgproc_pid(&g)) {
3207      owl_function_debugmsg("Checking on newmsgproc pid==%i", owl_global_get_newmsgproc_pid(&g));
3208      owl_function_debugmsg("Waitpid return is %i", waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG));
3209      waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG);
3210      if (waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG)==-1) {
3211        /* it exited */
3212        owl_global_set_newmsgproc_pid(&g, 0);
3213        owl_function_debugmsg("newmsgproc exited");
3214      } else {
3215        owl_function_debugmsg("newmsgproc did not exit");
3216      }
3217    }
3218   
3219    /* if it exited, spawn a new one */
3220    if (owl_global_get_newmsgproc_pid(&g)==0) {
3221      int myargc;
3222      char **argv = owl_parseline(owl_global_get_newmsgproc(&g), &myargc);
3223      if (myargc < 0) {
3224        owl_function_debugmsg("Could not parse newmsgproc '%s': unbalanced quotes?",
3225                              owl_global_get_newmsgproc(&g));
3226      } else if (myargc > 0) {
3227        /* Spawn the child. */
3228        GPid pid;
3229        GError *error = NULL;
3230        owl_function_debugmsg("About to exec \"%s\" with %d arguments", argv[0], myargc);
3231        if (g_spawn_async(NULL, argv, NULL,
3232                          G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
3233                          NULL, NULL, &pid, &error)) {
3234          owl_global_set_newmsgproc_pid(&g, pid);
3235          owl_function_debugmsg("I'm the parent and I started a new newmsgproc with pid %i", pid);
3236        } else {
3237          owl_function_debugmsg("Cannot run newmsgproc '%s': %s",
3238                                owl_global_get_newmsgproc(&g), error->message);
3239          g_error_free(error);
3240        }
3241      }
3242      g_strfreev(argv);
3243    }
3244  }
3245}
3246
3247/* print the xterm escape sequence to raise the window */
3248void owl_function_xterm_raise(void)
3249{
3250  printf("\033[5t");
3251}
3252
3253/* print the xterm escape sequence to deiconify the window */
3254void owl_function_xterm_deiconify(void)
3255{
3256  printf("\033[1t");
3257}
3258
3259/* Add the specified command to the startup file.  Eventually this
3260 * should be clever, and rewriting settings that will obviosly
3261 * override earlier settings with 'set' 'bindkey' and 'alias'
3262 * commands.  For now though we just remove any line that would
3263 * duplicate this one and then append this line to the end of
3264 * startupfile.
3265 */
3266void owl_function_addstartup(const char *buff)
3267{
3268  FILE *file;
3269  const char *filename;
3270
3271  filename=owl_global_get_startupfile(&g);
3272
3273  /* delete earlier copies, if the file exists */
3274  if (g_file_test(filename, G_FILE_TEST_EXISTS))
3275    owl_util_file_deleteline(filename, buff, 1);
3276
3277  file=fopen(filename, "a");
3278  if (!file) {
3279    owl_function_error("Error opening startupfile for new command");
3280    return;
3281  }
3282
3283  /* add this line */
3284  fprintf(file, "%s\n", buff);
3285
3286  fclose(file);
3287}
3288
3289/* Remove the specified command from the startup file. */
3290void owl_function_delstartup(const char *buff)
3291{
3292  const char *filename;
3293  filename=owl_global_get_startupfile(&g);
3294  owl_util_file_deleteline(filename, buff, 1);
3295}
3296
3297/* Execute owl commands from the given filename.  If the filename
3298 * is NULL, use the default owl startup commands file.
3299 */
3300void owl_function_source(const char *filename)
3301{
3302  char *path;
3303  FILE *file;
3304  char *s = NULL;
3305  int fail_silent = 0;
3306
3307  if (!filename) {
3308    fail_silent = 1;
3309    path = g_strdup(owl_global_get_startupfile(&g));
3310  } else {
3311    path = owl_util_makepath(filename);
3312  }
3313  file = fopen(path, "r");
3314  g_free(path);
3315  if (!file) {
3316    if (!fail_silent) {
3317      owl_function_error("Error opening file: %s", filename);
3318    }
3319    return;
3320  }
3321  while (owl_getline_chomp(&s, file)) {
3322    if (s[0] == '\0' || s[0] == '#')
3323      continue;
3324    owl_function_command_norv(s);
3325  }
3326
3327  g_free(s);
3328  fclose(file);
3329}
3330
3331void owl_function_change_style(owl_view *v, const char *stylename)
3332{
3333  const owl_style *s;
3334
3335  s=owl_global_get_style_by_name(&g, stylename);
3336  if (!s) {
3337    owl_function_error("No style named %s", stylename);
3338    return;
3339  }
3340  owl_view_set_style(v, s);
3341  owl_messagelist_invalidate_formats(owl_global_get_msglist(&g));
3342  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
3343  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3344}
3345
3346void owl_function_toggleoneline(void)
3347{
3348  owl_view *v;
3349  const owl_style *s;
3350
3351  v=owl_global_get_current_view(&g);
3352  s=owl_view_get_style(v);
3353
3354  if (!owl_style_matches_name(s, "oneline")) {
3355    owl_function_change_style(v, "oneline");
3356  } else {
3357    owl_function_change_style(v, owl_global_get_default_style(&g));
3358  }
3359
3360  owl_messagelist_invalidate_formats(owl_global_get_msglist(&g));
3361  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
3362  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3363}
3364
3365void G_GNUC_PRINTF(1, 2) owl_function_error(const char *fmt, ...)
3366{
3367  static int in_error = 0;
3368  va_list ap;
3369  char *buff;
3370  const char *nl;
3371
3372  if (++in_error > 2) {
3373    /* More than two nested errors, bail immediately. */
3374    in_error--;
3375    return;
3376  }
3377
3378  va_start(ap, fmt);
3379  buff = g_strdup_vprintf(fmt, ap);
3380  va_end(ap);
3381
3382  owl_function_debugmsg("ERROR: %s", buff);
3383  owl_function_log_err(buff);
3384
3385  nl = strchr(buff, '\n');
3386
3387  /*
3388    Showing admin messages triggers a lot of code. If we have a
3389    recursive error call, that's the most likely candidate, so
3390    suppress the call in that case, to try to avoid infinite looping.
3391  */
3392
3393  if(nl && *(nl + 1) && in_error == 1) {
3394    /* Multiline error */
3395    owl_function_adminmsg("ERROR", buff);
3396  } else {
3397    owl_function_makemsg("[Error] %s", buff);
3398  }
3399
3400  g_free(buff);
3401
3402  in_error--;
3403}
3404
3405void owl_function_log_err(const char *string)
3406{
3407  char *date;
3408  time_t now;
3409  char *buff;
3410
3411  now = time(NULL);
3412  date = owl_util_format_time(localtime(&now));
3413
3414  buff = g_strdup_printf("%s %s", date, string);
3415
3416  owl_errqueue_append_err(owl_global_get_errqueue(&g), buff);
3417
3418  g_free(buff);
3419  g_free(date);
3420}
3421
3422void owl_function_showerrs(void)
3423{
3424  owl_fmtext fm;
3425
3426  owl_fmtext_init_null(&fm);
3427  owl_fmtext_append_normal(&fm, "Errors:\n\n");
3428  owl_errqueue_to_fmtext(owl_global_get_errqueue(&g), &fm);
3429  owl_function_popless_fmtext(&fm);
3430}
3431
3432void G_GNUC_PRINTF(1, 2) owl_function_makemsg(const char *fmt, ...)
3433{
3434  va_list ap;
3435  char *str;
3436
3437  va_start(ap, fmt);
3438  str = g_strdup_vprintf(fmt, ap);
3439  va_end(ap);
3440
3441  owl_function_debugmsg("makemsg: %s", str);
3442  owl_msgwin_set_text_nocopy(&g.msgwin, str);
3443}
3444
3445/* get locations for everyone in .anyone.  If 'notify' is '1' then
3446 * send a pseudo login or logout message for everyone not in sync with
3447 * the global zephyr buddy list.  The list is updated regardless of
3448 * the status of 'notify'.
3449 */
3450void owl_function_zephyr_buddy_check(int notify)
3451{
3452#ifdef HAVE_LIBZEPHYR
3453  int i;
3454  GPtrArray *anyone;
3455  GList **zaldlist;
3456  GList *zaldptr;
3457  ZAsyncLocateData_t *zald;
3458  const char *user;
3459
3460  if (!owl_global_is_havezephyr(&g)) return;
3461  owl_global_set_pseudologin_notify(&g, notify);
3462  zaldlist = owl_global_get_zaldlist(&g);
3463
3464  /* Clear the existing ZALDs first. */
3465  zaldptr = g_list_first(*zaldlist);
3466  while (zaldptr) {
3467    ZFreeALD(zaldptr->data);
3468    g_free(zaldptr->data);
3469    zaldptr = g_list_next(zaldptr);
3470  }
3471  g_list_free(*zaldlist);
3472  *zaldlist = NULL;
3473
3474  anyone = owl_zephyr_get_anyone_list(NULL);
3475  if (anyone != NULL) {
3476    for (i = 0; i < anyone->len; i++) {
3477      user = anyone->pdata[i];
3478      zald = g_new(ZAsyncLocateData_t, 1);
3479      if (ZRequestLocations(zstr(user), zald, UNACKED, ZAUTH) == ZERR_NONE) {
3480        *zaldlist = g_list_append(*zaldlist, zald);
3481      } else {
3482        g_free(zald);
3483      }
3484    }
3485    owl_ptr_array_free(anyone, g_free);
3486  }
3487#endif
3488}
3489
3490void owl_function_aimsearch_results(const char *email, GPtrArray *namelist)
3491{
3492  owl_fmtext fm;
3493  int i;
3494
3495  owl_fmtext_init_null(&fm);
3496  owl_fmtext_append_normal(&fm, "AIM screennames associated with ");
3497  owl_fmtext_append_normal(&fm, email);
3498  owl_fmtext_append_normal(&fm, ":\n");
3499
3500  for (i = 0; i < namelist->len; i++) {
3501    owl_fmtext_append_normal(&fm, "  ");
3502    owl_fmtext_append_normal(&fm, namelist->pdata[i]);
3503    owl_fmtext_append_normal(&fm, "\n");
3504  }
3505
3506  owl_function_popless_fmtext(&fm);
3507  owl_fmtext_cleanup(&fm);
3508}
3509
3510int owl_function_get_color_count(void)
3511{
3512     return COLORS;
3513}
3514
3515void _owl_function_mark_message(const owl_message *m)
3516{
3517  if (m) {
3518    owl_global_set_markedmsgid(&g, owl_message_get_id(m));
3519    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3520  }
3521}
3522
3523void owl_function_mark_message(void)
3524{
3525  const owl_message *m;
3526  const owl_view *v;
3527
3528  v=owl_global_get_current_view(&g);
3529
3530  /* bail if there's no current message */
3531  if (owl_view_get_size(v) < 1) {
3532    owl_function_error("No messages to mark");
3533    return;
3534  }
3535
3536  /* mark the message */
3537  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
3538  _owl_function_mark_message(m);
3539  owl_function_makemsg("Mark set");
3540}
3541
3542void owl_function_swap_cur_marked(void)
3543{
3544  int marked_id;
3545  const owl_message *m;
3546  const owl_view *v;
3547
3548  marked_id=owl_global_get_markedmsgid(&g);
3549  if (marked_id == -1) {
3550    owl_function_error("Mark not set.");
3551    return;
3552  }
3553
3554  v=owl_global_get_current_view(&g);
3555  /* bail if there's no current message */
3556  if (owl_view_get_size(v) < 1) {
3557    return;
3558  }
3559
3560  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
3561  _owl_function_mark_message(m);
3562  owl_global_set_curmsg(&g, owl_view_get_nearest_to_msgid(v, marked_id));
3563  owl_function_calculate_topmsg(OWL_DIRECTION_NONE);
3564  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3565  owl_global_set_direction_downwards(&g);
3566}
Note: See TracBrowser for help on using the repository browser.