source: functions.c @ b9517cf

release-1.10release-1.9
Last change on this file since b9517cf was b9517cf, checked in by David Benjamin <davidben@mit.edu>, 13 years ago
Explicitly store whether an owl_message has a ZNotice_t We should remove it altogether in perlmessages, but in the meantime, we shouldn't be crashing on faked incoming zephyr messages. Also drop the attempted fake ZNotice_t in owl_perlconfig_hashref2message. No one could have relied on it safely today because any such message would crash on delete.
  • 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_time_to_timestr(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#ifdef HAVE_LIBZEPHYR
1382  const ZNotice_t *n;
1383#endif
1384
1385  owl_fmtext_init_null(&fm);
1386 
1387  v=owl_global_get_current_view(&g);
1388  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1389  if (!m || owl_view_get_size(v)==0) {
1390    owl_function_error("No message selected\n");
1391    return;
1392  }
1393
1394  owl_fmtext_append_bold(&fm, "General Information:\n");
1395  owl_fmtext_appendf_normal(&fm, "  Msg Id    : %i\n", owl_message_get_id(m));
1396
1397  owl_fmtext_append_normal(&fm, "  Type      : ");
1398  owl_fmtext_append_bold(&fm, owl_message_get_type(m));
1399  owl_fmtext_append_normal(&fm, "\n");
1400
1401  if (owl_message_is_direction_in(m)) {
1402    owl_fmtext_append_normal(&fm, "  Direction : in\n");
1403  } else if (owl_message_is_direction_out(m)) {
1404    owl_fmtext_append_normal(&fm, "  Direction : out\n");
1405  } else if (owl_message_is_direction_none(m)) {
1406    owl_fmtext_append_normal(&fm, "  Direction : none\n");
1407  } else {
1408    owl_fmtext_append_normal(&fm, "  Direction : unknown\n");
1409  }
1410
1411  owl_fmtext_appendf_normal(&fm, "  Time      : %s\n", owl_message_get_timestr(m));
1412
1413  if (!owl_message_is_type_admin(m)) {
1414    owl_fmtext_appendf_normal(&fm, "  Sender    : %s\n", owl_message_get_sender(m));
1415    owl_fmtext_appendf_normal(&fm, "  Recipient : %s\n", owl_message_get_recipient(m));
1416  }
1417
1418  if (owl_message_is_type_zephyr(m)) {
1419    owl_fmtext_append_bold(&fm, "\nZephyr Specific Information:\n");
1420   
1421    owl_fmtext_appendf_normal(&fm, "  Class     : %s\n", owl_message_get_class(m));
1422    owl_fmtext_appendf_normal(&fm, "  Instance  : %s\n", owl_message_get_instance(m));
1423    owl_fmtext_appendf_normal(&fm, "  Opcode    : %s\n", owl_message_get_opcode(m));
1424#ifdef HAVE_LIBZEPHYR
1425    n = owl_message_get_notice(m);
1426    if (n != NULL) {
1427      char *tmpbuff, *tmpbuff2;
1428      int i, fields;
1429
1430      if (!owl_message_is_pseudo(m)) {
1431        owl_fmtext_append_normal(&fm, "  Kind      : ");
1432        if (n->z_kind==UNSAFE) {
1433          owl_fmtext_append_normal(&fm, "UNSAFE\n");
1434        } else if (n->z_kind==UNACKED) {
1435          owl_fmtext_append_normal(&fm, "UNACKED\n");
1436        } else if (n->z_kind==ACKED) {
1437          owl_fmtext_append_normal(&fm, "ACKED\n");
1438        } else if (n->z_kind==HMACK) {
1439          owl_fmtext_append_normal(&fm, "HMACK\n");
1440        } else if (n->z_kind==HMCTL) {
1441          owl_fmtext_append_normal(&fm, "HMCTL\n");
1442        } else if (n->z_kind==SERVACK) {
1443          owl_fmtext_append_normal(&fm, "SERVACK\n");
1444        } else if (n->z_kind==SERVNAK) {
1445          owl_fmtext_append_normal(&fm, "SERVNACK\n");
1446        } else if (n->z_kind==CLIENTACK) {
1447          owl_fmtext_append_normal(&fm, "CLIENTACK\n");
1448        } else if (n->z_kind==STAT) {
1449          owl_fmtext_append_normal(&fm, "STAT\n");
1450        } else {
1451          owl_fmtext_append_normal(&fm, "ILLEGAL VALUE\n");
1452        }
1453      }
1454      owl_fmtext_appendf_normal(&fm, "  Host      : %s\n", owl_message_get_hostname(m));
1455
1456      if (!owl_message_is_pseudo(m)) {
1457        owl_fmtext_append_normal(&fm, "\n");
1458        owl_fmtext_appendf_normal(&fm, "  Port      : %i\n", ntohs(n->z_port));
1459        owl_fmtext_appendf_normal(&fm, "  Auth      : %s\n", owl_zephyr_get_authstr(n));
1460
1461        /* FIXME make these more descriptive */
1462        owl_fmtext_appendf_normal(&fm, "  Checkd Ath: %i\n", n->z_checked_auth);
1463        owl_fmtext_appendf_normal(&fm, "  Multi notc: %s\n", n->z_multinotice);
1464        owl_fmtext_appendf_normal(&fm, "  Num other : %i\n", n->z_num_other_fields);
1465        owl_fmtext_appendf_normal(&fm, "  Msg Len   : %i\n", n->z_message_len);
1466
1467        fields=owl_zephyr_get_num_fields(n);
1468        owl_fmtext_appendf_normal(&fm, "  Fields    : %i\n", fields);
1469
1470        for (i = 0; i < fields; i++) {
1471          tmpbuff = owl_zephyr_get_field_as_utf8(n, i + 1);
1472          tmpbuff2 = owl_text_indent(tmpbuff, 14, false);
1473          owl_fmtext_appendf_normal(&fm, "  Field %i   : %s\n", i + 1, tmpbuff2);
1474          g_free(tmpbuff2);
1475          g_free(tmpbuff);
1476        }
1477        tmpbuff = owl_text_indent(n->z_default_format, 14, false);
1478        owl_fmtext_appendf_normal(&fm, "  Default Fm: %s\n", tmpbuff);
1479        g_free(tmpbuff);
1480      }
1481
1482    }
1483#endif
1484  }
1485
1486  owl_fmtext_append_bold(&fm, "\nBarnOwl Message Attributes:\n");
1487  owl_message_attributes_tofmtext(m, &attrfm);
1488  owl_fmtext_append_fmtext(&fm, &attrfm);
1489 
1490  owl_function_popless_fmtext(&fm);
1491  owl_fmtext_cleanup(&fm);
1492  owl_fmtext_cleanup(&attrfm);
1493}
1494
1495/* print the current message in a popup window.
1496 * Use the 'default' style regardless of whatever
1497 * style the user may be using
1498 */
1499void owl_function_curmsg_to_popwin(void)
1500{
1501  const owl_view *v;
1502  const owl_message *m;
1503  const owl_style *s;
1504  owl_fmtext fm;
1505
1506  v=owl_global_get_current_view(&g);
1507  s=owl_global_get_style_by_name(&g, "default");
1508
1509  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1510
1511  if (!m || owl_view_get_size(v)==0) {
1512    owl_function_error("No current message");
1513    return;
1514  }
1515
1516  owl_fmtext_init_null(&fm);
1517  owl_style_get_formattext(s, &fm, m);
1518
1519  owl_function_popless_fmtext(&fm);
1520  owl_fmtext_cleanup(&fm);
1521}
1522
1523void owl_function_page_curmsg(int step)
1524{
1525  /* scroll down or up within the current message IF the message is truncated */
1526
1527  int offset, curmsg, lines;
1528  const owl_view *v;
1529  owl_message *m;
1530
1531  offset=owl_global_get_curmsg_vert_offset(&g);
1532  v=owl_global_get_current_view(&g);
1533  curmsg=owl_global_get_curmsg(&g);
1534  m=owl_view_get_element(v, curmsg);
1535  if (!m || owl_view_get_size(v)==0) return;
1536  lines=owl_message_get_numlines(m);
1537
1538  if (offset==0) {
1539    /* Bail if the curmsg isn't the last one displayed */
1540    if (curmsg != owl_mainwin_get_last_msg(owl_global_get_mainwin(&g))) {
1541      owl_function_makemsg("The entire message is already displayed");
1542      return;
1543    }
1544   
1545    /* Bail if we're not truncated */
1546    if (!owl_mainwin_is_curmsg_truncated(owl_global_get_mainwin(&g))) {
1547      owl_function_makemsg("The entire message is already displayed");
1548      return;
1549    }
1550  }
1551 
1552 
1553  /* don't scroll past the last line */
1554  if (step>0) {
1555    if (offset+step > lines-1) {
1556      owl_global_set_curmsg_vert_offset(&g, lines-1);
1557    } else {
1558      owl_global_set_curmsg_vert_offset(&g, offset+step);
1559    }
1560  }
1561
1562  /* would we be before the beginning of the message? */
1563  if (step<0) {
1564    if (offset+step<0) {
1565      owl_global_set_curmsg_vert_offset(&g, 0);
1566    } else {
1567      owl_global_set_curmsg_vert_offset(&g, offset+step);
1568    }
1569  }
1570 
1571  /* redisplay */
1572  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1573}
1574
1575void owl_function_resize_typwin(int newsize)
1576{
1577  owl_global_set_typwin_lines(&g, newsize);
1578  owl_mainpanel_layout_contents(&g.mainpanel);
1579}
1580
1581void owl_function_mainwin_pagedown(void)
1582{
1583  int i;
1584
1585  i=owl_mainwin_get_last_msg(owl_global_get_mainwin(&g));
1586  if (i<0) return;
1587  if (owl_mainwin_is_last_msg_truncated(owl_global_get_mainwin(&g))
1588      && (owl_global_get_curmsg(&g) < i)
1589      && (i>0)) {
1590    i--;
1591  }
1592  owl_global_set_curmsg(&g, i);
1593  owl_function_nextmsg();
1594}
1595
1596void owl_function_mainwin_pageup(void)
1597{
1598  owl_global_set_curmsg(&g, owl_global_get_topmsg(&g));
1599  owl_function_prevmsg();
1600}
1601
1602void owl_function_getsubs(void)
1603{
1604  char *buff;
1605
1606  buff=owl_zephyr_getsubs();
1607
1608  if (buff) {
1609    owl_function_popless_text(buff);
1610  } else {
1611    owl_function_popless_text("Error getting subscriptions");
1612  }
1613           
1614  g_free(buff);
1615}
1616
1617void owl_function_printallvars(void)
1618{
1619  const owl_variable *v;
1620  const char *name;
1621  char *var;
1622  GPtrArray *varnames;
1623  int i;
1624  GString *str   = g_string_new("");
1625
1626  g_string_append_printf(str, "%-20s = %s\n", "VARIABLE", "VALUE");
1627  g_string_append_printf(str, "%-20s   %s\n",  "--------", "-----");
1628  varnames = owl_variable_dict_get_names(owl_global_get_vardict(&g));
1629  for (i = 0; i < varnames->len; i++) {
1630    name = varnames->pdata[i];
1631    if (name && name[0]!='_') {
1632      g_string_append_printf(str, "\n%-20s = ", name);
1633      v = owl_variable_get_var(owl_global_get_vardict(&g), name);
1634      var = owl_variable_get_tostring(v);
1635      if (var) {
1636        g_string_append(str, var);
1637        g_free(var);
1638      } else {
1639        g_string_append(str, "<null>");
1640      }
1641    }
1642  }
1643  g_string_append(str, "\n");
1644  owl_ptr_array_free(varnames, g_free);
1645
1646  owl_function_popless_text(str->str);
1647  g_string_free(str, true);
1648}
1649
1650void owl_function_show_variables(void)
1651{
1652  const owl_variable *v;
1653  GPtrArray *varnames;
1654  owl_fmtext fm; 
1655  int i;
1656  const char *varname;
1657
1658  owl_fmtext_init_null(&fm);
1659  owl_fmtext_append_bold(&fm, 
1660      "Variables: (use 'show variable <name>' for details)\n");
1661  varnames = owl_variable_dict_get_names(owl_global_get_vardict(&g));
1662  for (i = 0; i < varnames->len; i++) {
1663    varname = varnames->pdata[i];
1664    if (varname && varname[0]!='_') {
1665      v = owl_variable_get_var(owl_global_get_vardict(&g), varname);
1666      owl_variable_describe(v, &fm);
1667    }
1668  }
1669  owl_ptr_array_free(varnames, g_free);
1670  owl_function_popless_fmtext(&fm);
1671  owl_fmtext_cleanup(&fm);
1672}
1673
1674void owl_function_show_variable(const char *name)
1675{
1676  const owl_variable *v;
1677  owl_fmtext fm; 
1678
1679  owl_fmtext_init_null(&fm);
1680  v = owl_variable_get_var(owl_global_get_vardict(&g), name);
1681  if (v)
1682    owl_variable_get_help(v, &fm);
1683  else
1684    owl_fmtext_append_normal(&fm, "No such variable...\n");
1685  owl_function_popless_fmtext(&fm);
1686  owl_fmtext_cleanup(&fm);
1687}
1688
1689void owl_function_delete_and_expunge_by_id(int id, bool exclaim_success)
1690{
1691  const owl_messagelist *ml = owl_global_get_msglist(&g);
1692  int msg = owl_messagelist_get_index_by_id(ml, id);
1693  if (msg < 0) {
1694    owl_function_error("No message with id %d: unable to delete", id);
1695  } else {
1696    owl_function_delete_and_expunge_message(msg);
1697    if (exclaim_success)
1698      owl_function_makemsg("Message deleted and expunged");
1699  }
1700}
1701
1702/* note: this applies to global message list, not to view.
1703 * If flag is 1, deletes.  If flag is 0, undeletes. */
1704void owl_function_delete_by_id(int id, int flag)
1705{
1706  const owl_messagelist *ml;
1707  owl_message *m;
1708  ml = owl_global_get_msglist(&g);
1709  m = owl_messagelist_get_by_id(ml, id);
1710  if (m) {
1711    if (flag == 1) {
1712      owl_message_mark_delete(m);
1713    } else if (flag == 0) {
1714      owl_message_unmark_delete(m);
1715    }
1716    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1717  } else {
1718    owl_function_error("No message with id %d: unable to mark for (un)delete",id);
1719  }
1720}
1721
1722void owl_function_delete_automsgs(void)
1723{
1724  /* mark for deletion all messages in the current view that match the
1725   * 'trash' filter */
1726
1727  int i, j, count;
1728  owl_message *m;
1729  const owl_view *v;
1730  const owl_filter *f;
1731
1732  /* get the trash filter */
1733  f=owl_global_get_filter(&g, "trash");
1734  if (!f) {
1735    owl_function_error("No trash filter defined");
1736    return;
1737  }
1738
1739  v=owl_global_get_current_view(&g);
1740
1741  count=0;
1742  j=owl_view_get_size(v);
1743  for (i=0; i<j; i++) {
1744    m=owl_view_get_element(v, i);
1745    if (owl_filter_message_match(f, m)) {
1746      count++;
1747      owl_message_mark_delete(m);
1748    }
1749  }
1750  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1751  owl_function_makemsg("%i messages marked for deletion", count);
1752}
1753
1754void owl_function_status(void)
1755{
1756  char *tmpbuff;
1757  char buff[MAXPATHLEN+1];
1758  time_t start;
1759  int up, days, hours, minutes;
1760  owl_fmtext fm;
1761
1762  owl_fmtext_init_null(&fm);
1763
1764  start=owl_global_get_starttime(&g);
1765
1766  owl_fmtext_append_normal(&fm, "General Information:\n");
1767
1768  owl_fmtext_append_normal(&fm, "  Version: ");
1769  owl_fmtext_append_normal(&fm, OWL_VERSION_STRING);
1770  owl_fmtext_append_normal(&fm, "\n");
1771
1772  owl_fmtext_append_normal(&fm, "  Startup Arguments: ");
1773  owl_fmtext_append_normal(&fm, owl_global_get_startupargs(&g));
1774  owl_fmtext_append_normal(&fm, "\n");
1775
1776  owl_fmtext_append_normal(&fm, "  Current Directory: ");
1777  if(getcwd(buff, MAXPATHLEN) == NULL) {
1778    owl_fmtext_append_normal(&fm, "<Error in getcwd>");
1779  } else {
1780    owl_fmtext_append_normal(&fm, buff);
1781  }
1782  owl_fmtext_append_normal(&fm, "\n");
1783
1784  tmpbuff = owl_util_time_to_timestr(localtime(&start));
1785  owl_fmtext_appendf_normal(&fm, "  Startup Time: %s\n", tmpbuff);
1786  g_free(tmpbuff);
1787
1788  up=owl_global_get_runtime(&g);
1789  days=up/86400;
1790  up-=days*86400;
1791  hours=up/3600;
1792  up-=hours*3600;
1793  minutes=up/60;
1794  up-=minutes*60;
1795  owl_fmtext_appendf_normal(&fm, "  Run Time: %i days %2.2i:%2.2i:%2.2i\n", days, hours, minutes, up);
1796
1797  owl_fmtext_append_normal(&fm, "\nProtocol Options:\n");
1798  owl_fmtext_append_normal(&fm, "  Zephyr included    : ");
1799  if (owl_global_is_havezephyr(&g)) {
1800    owl_fmtext_append_normal(&fm, "yes\n");
1801  } else {
1802    owl_fmtext_append_normal(&fm, "no\n");
1803  }
1804  owl_fmtext_append_normal(&fm, "  AIM included       : yes\n");
1805  owl_fmtext_append_normal(&fm, "  Loopback included  : yes\n");
1806
1807
1808  owl_fmtext_append_normal(&fm, "\nBuild Options:\n");
1809  owl_fmtext_append_normal(&fm, "  Stderr redirection : ");
1810#if OWL_STDERR_REDIR
1811  owl_fmtext_append_normal(&fm, "yes\n");
1812#else
1813  owl_fmtext_append_normal(&fm, "no\n");
1814#endif
1815 
1816
1817  owl_fmtext_append_normal(&fm, "\nAIM Status:\n");
1818  owl_fmtext_append_normal(&fm, "  Logged in: ");
1819  if (owl_global_is_aimloggedin(&g)) {
1820    owl_fmtext_append_normal(&fm, owl_global_get_aim_screenname(&g));
1821    owl_fmtext_append_normal(&fm, "\n");
1822  } else {
1823    owl_fmtext_append_normal(&fm, "(not logged in)\n");
1824  }
1825
1826  owl_fmtext_append_normal(&fm, "  Processing events: ");
1827  if (owl_global_is_doaimevents(&g)) {
1828    owl_fmtext_append_normal(&fm, "yes\n");
1829  } else {
1830    owl_fmtext_append_normal(&fm, "no\n");
1831  }
1832
1833  owl_function_popless_fmtext(&fm);
1834  owl_fmtext_cleanup(&fm);
1835}
1836
1837void owl_function_show_term(void)
1838{
1839  owl_fmtext fm;
1840
1841  owl_fmtext_init_null(&fm);
1842  owl_fmtext_appendf_normal(&fm, "Terminal Lines: %i\nTerminal Columns: %i\n",
1843          owl_global_get_lines(&g),
1844          owl_global_get_cols(&g));
1845
1846  if (has_colors()) {
1847    owl_fmtext_append_normal(&fm, "Color: Yes\n");
1848    owl_fmtext_appendf_normal(&fm, "Number of color pairs: %i\n", owl_util_get_colorpairs());
1849    owl_fmtext_appendf_normal(&fm, "Can change colors: %s\n", can_change_color() ? "yes" : "no");
1850  } else {
1851    owl_fmtext_append_normal(&fm, "Color: No\n");
1852  }
1853
1854  owl_function_popless_fmtext(&fm);
1855  owl_fmtext_cleanup(&fm);
1856}
1857
1858/* if type = 0 then normal reply.
1859 * if type = 1 then it's a reply to sender
1860 * if enter = 0 then allow the command to be edited
1861 * if enter = 1 then don't wait for editing
1862 */
1863void owl_function_reply(int type, int enter)
1864{
1865  char *buff=NULL;
1866  const owl_message *m;
1867  const owl_filter *f;
1868 
1869  if (owl_view_get_size(owl_global_get_current_view(&g))==0) {
1870    owl_function_error("No message selected");
1871  } else {
1872    char *cmd;
1873   
1874    m=owl_view_get_element(owl_global_get_current_view(&g), owl_global_get_curmsg(&g));
1875    if (!m) {
1876      owl_function_error("No message selected");
1877      return;
1878    }
1879
1880    /* first check if we catch the reply-lockout filter */
1881    f=owl_global_get_filter(&g, "reply-lockout");
1882    if (f) {
1883      if (owl_filter_message_match(f, m)) {
1884        owl_function_error("Sorry, replies to this message have been disabled by the reply-lockout filter");
1885        return;
1886      }
1887    }
1888
1889    /* then check if it's a question and just bring up the command prompt */
1890    if (owl_message_is_question(m)) {
1891      owl_function_start_command("");
1892      return;
1893    }
1894
1895    if((type == 0 &&
1896        (cmd=owl_perlconfig_message_call_method(m, "replycmd", 0, NULL))) ||
1897       (type == 1 &&
1898        (cmd=owl_perlconfig_message_call_method(m, "replysendercmd", 0, NULL)))) {
1899      buff = cmd;
1900    }
1901
1902    if(!buff) {
1903        owl_function_error("I don't know how to reply to that message.");
1904        return;
1905    }
1906
1907    if (enter) {
1908      owl_history *hist = owl_global_get_cmd_history(&g);
1909      owl_history_store(hist, buff, false);
1910      owl_function_command_norv(buff);
1911    } else {
1912      owl_function_start_command(buff);
1913    }
1914    g_free(buff);
1915  }
1916}
1917
1918void owl_function_zlocate(int argc, const char *const *argv, int auth)
1919{
1920  owl_fmtext fm;
1921  char *ptr;
1922  char *result;
1923  int i;
1924
1925  owl_fmtext_init_null(&fm);
1926
1927  for (i=0; i<argc; i++) {
1928    ptr = long_zuser(argv[i]);
1929    result = owl_zephyr_zlocate(ptr, auth);
1930    owl_fmtext_append_normal(&fm, result);
1931    g_free(result);
1932    g_free(ptr);
1933  }
1934
1935  owl_function_popless_fmtext(&fm);
1936  owl_fmtext_cleanup(&fm);
1937}
1938
1939void owl_callback_command(owl_editwin *e)
1940{
1941  char *rv;
1942  const char *line = owl_editwin_get_text(e);
1943
1944  rv = owl_function_command(line);
1945   if (rv) {
1946    owl_function_makemsg("%s", rv);
1947    g_free(rv);
1948  }
1949}
1950
1951void owl_function_start_command(const char *line)
1952{
1953  owl_editwin *tw;
1954  owl_context *ctx;
1955
1956  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
1957
1958  owl_editwin_set_locktext(tw, "command: ");
1959
1960  owl_editwin_insert_string(tw, line);
1961
1962  ctx = owl_editcontext_new(OWL_CTX_EDITLINE, tw, "editline",
1963                            owl_global_deactivate_editcontext, &g);
1964  owl_global_push_context_obj(&g, ctx);
1965  owl_editwin_set_callback(tw, owl_callback_command);
1966}
1967
1968CALLER_OWN owl_editwin *owl_function_start_question(const char *line)
1969{
1970  owl_editwin *tw;
1971  owl_context *ctx;
1972
1973  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
1974
1975  owl_editwin_set_locktext(tw, line);
1976
1977  ctx = owl_editcontext_new(OWL_CTX_EDITRESPONSE, tw, "editresponse",
1978                            owl_global_deactivate_editcontext, &g);
1979  owl_global_push_context_obj(&g, ctx);
1980  return tw;
1981}
1982
1983CALLER_OWN owl_editwin *owl_function_start_password(const char *line)
1984{
1985  owl_editwin *tw;
1986  owl_context *ctx;
1987
1988  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, NULL);
1989
1990  owl_editwin_set_echochar(tw, '*');
1991
1992  owl_editwin_set_locktext(tw, line);
1993
1994  ctx = owl_editcontext_new(OWL_CTX_EDITRESPONSE, tw, "editresponse",
1995                            owl_global_deactivate_editcontext, &g);
1996  owl_global_push_context_obj(&g, ctx);
1997  return tw;
1998}
1999
2000CALLER_OWN char *owl_function_exec(int argc, const char *const *argv, const char *buff, int type)
2001{
2002  /* if type == 1 display in a popup
2003   * if type == 2 display an admin messages
2004   * if type == 0 return output
2005   * else display in a popup
2006   */
2007  const char *redirect = " 2>&1 < /dev/null";
2008  char *newbuff;
2009  char *out;
2010  FILE *p;
2011
2012#if OWL_STDERR_REDIR
2013  redirect = " < /dev/null";
2014#endif
2015
2016  if (argc<2) {
2017    owl_function_error("Wrong number of arguments to the exec command");
2018    return NULL;
2019  }
2020
2021  buff = skiptokens(buff, 1);
2022  newbuff = g_strdup_printf("exec%s; %s", redirect, buff);
2023
2024  if (type == OWL_OUTPUT_POPUP) {
2025    owl_popexec_new(newbuff);
2026  } else {
2027    p = popen(newbuff, "r");
2028    out = owl_slurp(p);
2029    pclose(p);
2030   
2031    if (type == OWL_OUTPUT_RETURN) {
2032      g_free(newbuff);
2033      return out;
2034    } else if (type == OWL_OUTPUT_ADMINMSG) {
2035      owl_function_adminmsg(buff, out);
2036    }
2037    g_free(out);
2038  }
2039  g_free(newbuff);
2040  return NULL;
2041}
2042
2043CALLER_OWN char *owl_function_perl(int argc, const char *const *argv, const char *buff, int type)
2044{
2045  /* if type == 1 display in a popup
2046   * if type == 2 display an admin messages
2047   * if type == 0 return output
2048   * else display in a popup
2049   */
2050  char *perlout;
2051
2052  if (argc<2) {
2053    owl_function_error("Wrong number of arguments to perl command");
2054    return NULL;
2055  }
2056
2057  /* consume first token (argv[0]) */
2058  buff = skiptokens(buff, 1);
2059
2060  perlout = owl_perlconfig_execute(buff);
2061  if (perlout) { 
2062    if (type == OWL_OUTPUT_POPUP) {
2063      owl_function_popless_text(perlout);
2064    } else if (type == OWL_OUTPUT_ADMINMSG) {
2065      owl_function_adminmsg(buff, perlout);
2066    } else if (type == OWL_OUTPUT_RETURN) {
2067      return perlout;
2068    }
2069    g_free(perlout);
2070  }
2071  return NULL;
2072}
2073
2074/* Change the filter associated with the current view.
2075 * This also figures out which message in the new filter
2076 * should have the pointer.
2077 */
2078void owl_function_change_currentview_filter(const char *filtname)
2079{
2080  owl_view *v;
2081  owl_filter *f;
2082  int curid=-1, newpos, curmsg;
2083  const owl_message *curm=NULL;
2084
2085  v=owl_global_get_current_view(&g);
2086
2087  curmsg=owl_global_get_curmsg(&g);
2088  if (curmsg==-1) {
2089    owl_function_debugmsg("Hit the curmsg==-1 case in change_view");
2090  } else {
2091    curm=owl_view_get_element(v, curmsg);
2092    if (curm) {
2093      curid=owl_message_get_id(curm);
2094      owl_view_save_curmsgid(v, curid);
2095    }
2096  }
2097
2098  f=owl_global_get_filter(&g, filtname);
2099  if (!f) {
2100    owl_function_error("Unknown filter %s", filtname);
2101    return;
2102  }
2103
2104  owl_view_new_filter(v, f);
2105
2106  /* Figure out what to set the current message to.
2107   * - If the view we're leaving has messages in it, go to the closest message
2108   *   to the last message pointed to in that view.
2109   * - If the view we're leaving is empty, try to restore the position
2110   *   from the last time we were in the new view.  */
2111  if (curm) {
2112    newpos = owl_view_get_nearest_to_msgid(v, curid);
2113  } else {
2114    newpos = owl_view_get_nearest_to_saved(v);
2115  }
2116
2117  owl_global_set_curmsg(&g, newpos);
2118  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
2119  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2120  owl_global_set_direction_downwards(&g);
2121}
2122
2123/* Create a new filter, or replace an existing one
2124 * with a new definition. Returns true on success.
2125 */
2126bool owl_function_create_filter(int argc, const char *const *argv)
2127{
2128  owl_filter *f;
2129  const owl_view *v;
2130  int inuse = 0;
2131
2132  if (argc < 2) {
2133    owl_function_error("Wrong number of arguments to filter command");
2134    return false;
2135  }
2136
2137  owl_function_debugmsg("owl_function_create_filter: starting to create filter named %s", argv[1]);
2138
2139  v=owl_global_get_current_view(&g);
2140
2141  /* don't touch the all filter */
2142  if (!strcmp(argv[1], "all")) {
2143    owl_function_error("You may not change the 'all' filter.");
2144    return false;
2145  }
2146
2147  /* deal with the case of trying change the filter color */
2148  if (argc==4 && !strcmp(argv[2], "-c")) {
2149    f=owl_global_get_filter(&g, argv[1]);
2150    if (!f) {
2151      owl_function_error("The filter '%s' does not exist.", argv[1]);
2152      return false;
2153    }
2154    if (owl_util_string_to_color(argv[3])==OWL_COLOR_INVALID) {
2155      owl_function_error("The color '%s' is not available.", argv[3]);
2156      return false;
2157    }
2158    owl_filter_set_fgcolor(f, owl_util_string_to_color(argv[3]));
2159    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2160    return false;
2161  }
2162  if (argc==4 && !strcmp(argv[2], "-b")) {
2163    f=owl_global_get_filter(&g, argv[1]);
2164    if (!f) {
2165      owl_function_error("The filter '%s' does not exist.", argv[1]);
2166      return false;
2167    }
2168    if (owl_util_string_to_color(argv[3])==OWL_COLOR_INVALID) {
2169      owl_function_error("The color '%s' is not available.", argv[3]);
2170      return false;
2171    }
2172    owl_filter_set_bgcolor(f, owl_util_string_to_color(argv[3]));
2173    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2174    return true;
2175  }
2176
2177  /* create the filter and check for errors */
2178  f = owl_filter_new(argv[1], argc-2, argv+2);
2179  if (f == NULL) {
2180    owl_function_error("Invalid filter");
2181    return false;
2182  }
2183
2184  /* if the named filter is in use by the current view, remember it */
2185  if (!strcmp(owl_view_get_filtname(v), argv[1])) {
2186    inuse=1;
2187  }
2188
2189  /* if the named filter already exists, nuke it */
2190  if (owl_global_get_filter(&g, argv[1])) {
2191    owl_global_remove_filter(&g, argv[1]);
2192  }
2193
2194  /* add the filter */
2195  owl_global_add_filter(&g, f);
2196
2197  /* if it was in use by the current view then update */
2198  if (inuse) {
2199    owl_function_change_currentview_filter(argv[1]);
2200  }
2201  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2202  return true;
2203}
2204
2205/* If 'filtername' does not start with 'not-' create a filter named
2206 * 'not-<filtername>' defined as "not filter <filtername>".  If the
2207 * filter 'not-<filtername>' already exists, do not overwrite it.  If
2208 * 'filtername' begins with 'not-' and a filter 'filtername' already
2209 * exists, then do nothing.  If the filter 'filtername' does not
2210 * exist, create it and define it as 'not filter <filtername>'
2211 *
2212 * Returns the name of the negated filter, which the caller must free.
2213 */
2214CALLER_OWN char *owl_function_create_negative_filter(const char *filtername)
2215{
2216  char *newname;
2217  const owl_filter *tmpfilt;
2218  const char *argv[5];
2219
2220  owl_function_debugmsg("owl_function_create_negative_filter");
2221 
2222  if (!strncmp(filtername, "not-", 4)) {
2223    newname=g_strdup(filtername+4);
2224  } else {
2225    newname=g_strdup_printf("not-%s", filtername);
2226  }
2227
2228  tmpfilt=owl_global_get_filter(&g, newname);
2229  if (!tmpfilt) {
2230    argv[0]="filter"; /* anything is fine here */
2231    argv[1]=newname;
2232    argv[2]="not";
2233    argv[3]="filter";
2234    argv[4]=filtername;
2235    owl_function_create_filter(5, argv);
2236  }
2237
2238  owl_function_debugmsg("owl_function_create_negative_filter: returning with %s", newname);
2239  return(newname);
2240}
2241
2242void owl_function_show_filters(void)
2243{
2244  const owl_filter *f;
2245  GList *fl;
2246  owl_fmtext fm;
2247
2248  owl_fmtext_init_null(&fm);
2249
2250  owl_fmtext_append_bold(&fm, "Filters:\n");
2251
2252  for (fl = g.filterlist; fl; fl = g_list_next(fl)) {
2253    f = fl->data;
2254    owl_fmtext_append_normal(&fm, "   ");
2255    owl_fmtext_append_normal_color(&fm, owl_filter_get_name(f),
2256                                   owl_filter_get_fgcolor(f),
2257                                   owl_filter_get_bgcolor(f));
2258    owl_fmtext_append_normal(&fm, "\n");
2259  }
2260  owl_function_popless_fmtext(&fm);
2261  owl_fmtext_cleanup(&fm);
2262}
2263
2264void owl_function_show_filter(const char *name)
2265{
2266  const owl_filter *f;
2267  char *buff, *tmp;
2268
2269  f=owl_global_get_filter(&g, name);
2270  if (!f) {
2271    owl_function_error("There is no filter named %s", name);
2272    return;
2273  }
2274  tmp = owl_filter_print(f);
2275  buff = g_strdup_printf("%s: %s", owl_filter_get_name(f), tmp);
2276  owl_function_popless_text(buff);
2277  g_free(buff);
2278  g_free(tmp);
2279}
2280
2281void owl_function_show_zpunts(void)
2282{
2283  const owl_filter *f;
2284  const GPtrArray *fl;
2285  char *tmp;
2286  owl_fmtext fm;
2287  int i;
2288
2289  owl_fmtext_init_null(&fm);
2290
2291  fl=owl_global_get_puntlist(&g);
2292  owl_fmtext_append_bold(&fm, "Active zpunt filters:\n");
2293
2294  for (i = 0; i < fl->len; i++) {
2295    f = fl->pdata[i];
2296    owl_fmtext_appendf_normal(&fm, "[% 2d] ", i+1);
2297    tmp = owl_filter_print(f);
2298    owl_fmtext_append_normal(&fm, tmp);
2299    g_free(tmp);
2300  }
2301  owl_function_popless_fmtext(&fm);
2302  owl_fmtext_cleanup(&fm);
2303}
2304
2305/* Create a filter for a class, instance if one doesn't exist.  If
2306 * instance is NULL then catch all messgaes in the class.  Returns the
2307 * name of the filter or null.  The caller must free this name.
2308 * If 'related' is nonzero, encompass unclasses and .d classes as well.
2309 */
2310CALLER_OWN char *owl_function_classinstfilt(const char *c, const char *i, int related) 
2311{
2312  owl_filter *f;
2313  char *filtname;
2314  char *tmpclass, *tmpinstance = NULL;
2315  char *class, *instance = NULL;
2316  GString *buf;
2317
2318  if (related) {
2319    class = owl_util_baseclass(c);
2320    if (i) {
2321      instance = owl_util_baseclass(i);
2322    }
2323  } else {
2324    class = g_strdup(c);
2325    if (i) {
2326      instance = g_strdup(i);
2327    }
2328  }
2329
2330  /* name for the filter */
2331  if (!instance) {
2332    filtname = g_strdup_printf("%sclass-%s", related ? "related-" : "", class);
2333  } else {
2334    filtname = g_strdup_printf("%sclass-%s-instance-%s", related ? "related-" : "", class, instance);
2335  }
2336  /* downcase it */
2337  {
2338    char *temp = g_utf8_strdown(filtname, -1);
2339    if (temp) {
2340      g_free(filtname);
2341      filtname = temp;
2342    }
2343  }
2344 
2345  /* if it already exists then go with it.  This lets users override */
2346  if (owl_global_get_filter(&g, filtname)) {
2347    goto done;
2348  }
2349
2350  /* create the new filter */
2351  tmpclass=owl_text_quote(class, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2352  if (instance) {
2353    tmpinstance=owl_text_quote(instance, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2354  }
2355
2356  buf = g_string_new("");
2357  owl_string_appendf_quoted(buf,
2358                            related ? "class ^(un)*%q(\\.d)*$" : "class ^%q$",
2359                            tmpclass);
2360
2361  if (tmpinstance) {
2362    owl_string_appendf_quoted(buf,
2363                              related ?
2364                              " and ( instance ^(un)*%q(\\.d)*$ )" :
2365                              " and instance ^%q$",
2366                              tmpinstance);
2367  }
2368  g_free(tmpclass);
2369  g_free(tmpinstance);
2370
2371  f = owl_filter_new_fromstring(filtname, buf->str);
2372  g_string_free(buf, true);
2373  if (f == NULL) {
2374    /* Couldn't make a filter for some reason. Return NULL. */
2375    owl_function_error("Error creating filter '%s'", filtname);
2376    g_free(filtname);
2377    filtname = NULL;
2378    goto done;
2379  }
2380
2381  /* add it to the global list */
2382  owl_global_add_filter(&g, f);
2383
2384done:
2385  g_free(class);
2386  g_free(instance);
2387  return(filtname);
2388}
2389
2390/* Create a filter for personal zephyrs to or from the specified
2391 * zephyr user.  Includes login/logout notifications for the user.
2392 * The name of the filter will be 'user-<shortuser>'.  If a filter already
2393 * exists with this name, no new filter will be created.  This allows
2394 * the configuration to override this function.  Returns the name of
2395 * the filter, which the caller must free.
2396 */
2397CALLER_OWN char *owl_function_zuserfilt(const char *longuser)
2398{
2399  owl_filter *f;
2400  char *argbuff, *esclonguser, *shortuser, *filtname;
2401
2402  /* name for the filter */
2403  shortuser = short_zuser(longuser);
2404  filtname = g_strdup_printf("user-%s", shortuser);
2405  g_free(shortuser);
2406
2407  /* if it already exists then go with it.  This lets users override */
2408  if (owl_global_get_filter(&g, filtname)) {
2409    return filtname;
2410  }
2411
2412  /* create the new-internal filter */
2413  esclonguser = owl_text_quote(longuser, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2414
2415  argbuff=owl_string_build_quoted("( type ^zephyr$ and filter personal and "
2416      "( ( direction ^in$ and sender ^%q$ ) or ( direction ^out$ and "
2417      "recipient ^%q$ ) ) ) or ( ( class ^login$ ) and ( sender ^%q$ ) )",
2418      esclonguser, esclonguser, esclonguser);
2419  g_free(esclonguser);
2420
2421  f = owl_filter_new_fromstring(filtname, argbuff);
2422  g_free(argbuff);
2423
2424  if (f == NULL) {
2425    /* Couldn't make a filter for some reason. Return NULL. */
2426    owl_function_error("Error creating filter '%s'", filtname);
2427    g_free(filtname);
2428    return NULL;
2429  }
2430
2431  /* add it to the global list */
2432  owl_global_add_filter(&g, f);
2433
2434  return(filtname);
2435}
2436
2437/* Create a filter for AIM IM messages to or from the specified
2438 * screenname.  The name of the filter will be 'aimuser-<user>'.  If a
2439 * filter already exists with this name, no new filter will be
2440 * created.  This allows the configuration to override this function.
2441 * Returns the name of the filter, which the caller must free.
2442 */
2443CALLER_OWN char *owl_function_aimuserfilt(const char *user)
2444{
2445  owl_filter *f;
2446  char *argbuff, *filtname;
2447  char *escuser;
2448
2449  /* name for the filter */
2450  filtname=g_strdup_printf("aimuser-%s", user);
2451
2452  /* if it already exists then go with it.  This lets users override */
2453  if (owl_global_get_filter(&g, filtname)) {
2454    return filtname;
2455  }
2456
2457  /* create the new-internal filter */
2458  escuser = owl_text_quote(user, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2459
2460  argbuff = g_strdup_printf(
2461      "( type ^aim$ and ( ( sender ^%1$s$ and recipient ^%2$s$ ) or "
2462      "( sender ^%2$s$ and recipient ^%1$s$ ) ) )",
2463      escuser, owl_global_get_aim_screenname_for_filters(&g));
2464  g_free(escuser);
2465
2466  f = owl_filter_new_fromstring(filtname, argbuff);
2467  g_free(argbuff);
2468
2469  if (f == NULL) {
2470    owl_function_error("Error creating filter '%s'", filtname);
2471    g_free(filtname);
2472    return NULL;
2473  }
2474
2475  /* add it to the global list */
2476  owl_global_add_filter(&g, f);
2477
2478  return(filtname);
2479}
2480
2481CALLER_OWN char *owl_function_typefilt(const char *type)
2482{
2483  owl_filter *f;
2484  char *argbuff, *filtname, *esctype;
2485
2486  /* name for the filter */
2487  filtname=g_strdup_printf("type-%s", type);
2488
2489  /* if it already exists then go with it.  This lets users override */
2490  if (owl_global_get_filter(&g, filtname)) {
2491    return filtname;
2492  }
2493
2494  /* create the new-internal filter */
2495  esctype = owl_text_quote(type, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2496
2497  argbuff = owl_string_build_quoted("type ^%q$", esctype);
2498  g_free(esctype);
2499
2500  f = owl_filter_new_fromstring(filtname, argbuff);
2501  g_free(argbuff);
2502
2503  if (f == NULL) {
2504    owl_function_error("Error creating filter '%s'", filtname);
2505    g_free(filtname);
2506    return NULL;
2507  }
2508
2509  /* add it to the global list */
2510  owl_global_add_filter(&g, f);
2511
2512  return filtname;
2513}
2514
2515/* If flag is 1, marks for deletion.  If flag is 0,
2516 * unmarks for deletion. */
2517void owl_function_delete_curview_msgs(int flag)
2518{
2519  const owl_view *v;
2520  int i, j;
2521
2522  v=owl_global_get_current_view(&g);
2523  j=owl_view_get_size(v);
2524  for (i=0; i<j; i++) {
2525    if (flag == 1) {
2526      owl_message_mark_delete(owl_view_get_element(v, i));
2527    } else if (flag == 0) {
2528      owl_message_unmark_delete(owl_view_get_element(v, i));
2529    }
2530  }
2531
2532  owl_function_makemsg("%i messages marked for %sdeletion", j, flag?"":"un");
2533
2534  owl_mainwin_redisplay(owl_global_get_mainwin(&g)); 
2535}
2536
2537static CALLER_OWN char *owl_function_smartfilter_cc(const owl_message *m)
2538{
2539  const char *ccs;
2540  char *ccs_quoted;
2541  char *filtname;
2542  owl_filter *f;
2543  GString *buf;
2544
2545  ccs = owl_message_get_attribute_value(m, "zephyr_ccs");
2546
2547  filtname = g_strdup_printf("conversation-%s", ccs);
2548  g_strdelimit(filtname, " ", '-');
2549
2550  if (owl_global_get_filter(&g, filtname)) {
2551    return filtname;
2552  }
2553
2554  buf = g_string_new("type ^zephyr$ and filter personal and "
2555                     "zephyr_ccs ^");
2556  ccs_quoted = owl_text_quote(ccs, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2557  owl_string_append_quoted_arg(buf, ccs_quoted);
2558  g_string_append_c(buf, '$');
2559  g_free(ccs_quoted);
2560
2561  f = owl_filter_new_fromstring(filtname, buf->str);
2562  g_string_free(buf, true);
2563
2564  if (f == NULL) {
2565    owl_function_error("Error creating filter '%s'", filtname);
2566    g_free(filtname);
2567    return NULL;
2568  }
2569
2570  owl_global_add_filter(&g, f);
2571
2572  return filtname;
2573}
2574
2575/* Create a filter based on the current message.  Returns the name of
2576 * a filter or null.  The caller must free this name.
2577 *
2578 * if the curmsg is a personal zephyr return a filter name
2579 *    to the zephyr conversation with that user.
2580 * If the curmsg is a zephyr class message, instance foo, recip *,
2581 *    return a filter name to the class, inst.
2582 * If the curmsg is a zephyr class message and type==0 then
2583 *    return a filter name for just the class.
2584 * If the curmsg is a zephyr class message and type==1 then
2585 *    return a filter name for the class and instance.
2586 * If the curmsg is a personal AIM message returna  filter
2587 *    name to the AIM conversation with that user
2588 */
2589CALLER_OWN char *owl_function_smartfilter(int type, int invert_related)
2590{
2591  const owl_view *v;
2592  const owl_message *m;
2593  char *filtname = NULL;
2594  const char *argv[2], *zperson;
2595  int related = owl_global_is_narrow_related(&g) ^ invert_related;
2596
2597  v=owl_global_get_current_view(&g);
2598  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
2599
2600  if (!m || owl_view_get_size(v)==0) {
2601    owl_function_error("No message selected\n");
2602    return(NULL);
2603  }
2604
2605  /* very simple handling of admin messages for now */
2606  if (owl_message_is_type_admin(m)) {
2607    return(owl_function_typefilt("admin"));
2608  }
2609
2610  /* very simple handling of loopback messages for now */
2611  if (owl_message_is_type_loopback(m)) {
2612    return(owl_function_typefilt("loopback"));
2613  }
2614
2615  /* aim messages */
2616  if (owl_message_is_type_aim(m)) {
2617    if (owl_message_is_direction_in(m)) {
2618      filtname=owl_function_aimuserfilt(owl_message_get_sender(m));
2619    } else if (owl_message_is_direction_out(m)) {
2620      filtname=owl_function_aimuserfilt(owl_message_get_recipient(m));
2621    }
2622    return(filtname);
2623  }
2624
2625  /* narrow personal and login messages to the sender or recip as appropriate */
2626  if (owl_message_is_type_zephyr(m)) {
2627    if (owl_message_is_personal(m) || owl_message_is_loginout(m)) {
2628      if (owl_message_get_attribute_value(m, "zephyr_ccs") != NULL) {
2629        return owl_function_smartfilter_cc(m);
2630      }
2631
2632      if (owl_message_is_direction_in(m)) {
2633        zperson = owl_message_get_sender(m);
2634      } else {
2635        zperson = owl_message_get_recipient(m);
2636      }
2637      filtname = owl_function_zuserfilt(zperson);
2638      return filtname;
2639    }
2640
2641    /* narrow class MESSAGE, instance foo, recip * messages to class, inst */
2642    if (!strcasecmp(owl_message_get_class(m), "message")) {
2643      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m), related);
2644      return(filtname);
2645    }
2646
2647    /* otherwise narrow to the class */
2648    if (type==0) {
2649      filtname=owl_function_classinstfilt(owl_message_get_class(m), NULL, related);
2650    } else if (type==1) {
2651      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m), related);
2652    }
2653    return(filtname);
2654  }
2655
2656  /* pass it off to perl */
2657  argv[0] = type ? "1" : "0";
2658  argv[1] = related ? "1" : "0";
2659  return owl_perlconfig_message_call_method(m, "smartfilter", 2, argv);
2660}
2661
2662void owl_function_smartzpunt(int type)
2663{
2664  /* Starts a zpunt command based on the current class,instance pair.
2665   * If type=0, uses just class.  If type=1, uses instance as well. */
2666  const owl_view *v;
2667  const owl_message *m;
2668  const char *mclass, *minst;
2669  GString *buf;
2670 
2671  v=owl_global_get_current_view(&g);
2672  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
2673
2674  if (!m || owl_view_get_size(v)==0) {
2675    owl_function_error("No message selected\n");
2676    return;
2677  }
2678
2679  /* for now we skip admin messages. */
2680  if (owl_message_is_type_admin(m)
2681      || owl_message_is_loginout(m)
2682      || !owl_message_is_type_zephyr(m)) {
2683    owl_function_error("smartzpunt doesn't support this message type.");
2684    return;
2685  }
2686
2687  mclass = owl_message_get_class(m);
2688  minst = owl_message_get_instance(m);
2689  if (!mclass || !*mclass || *mclass==' '
2690      || (!strcasecmp(mclass, "message") && !strcasecmp(minst, "personal"))
2691      || (type && (!minst || !*minst|| *minst==' '))) {
2692    owl_function_error("smartzpunt can't safely do this for <%s,%s>",
2693                         mclass, minst);
2694  } else {
2695    buf = g_string_new("start-command zpunt ");
2696    owl_string_append_quoted_arg(buf, mclass);
2697    if (type) {
2698      g_string_append_c(buf, ' ');
2699      owl_string_append_quoted_arg(buf, minst);
2700    } else {
2701      g_string_append(buf, " *");
2702    }
2703    owl_function_command_norv(buf->str);
2704    g_string_free(buf, true);
2705  }
2706}
2707
2708/* Set the color of the current view's filter to
2709 * be 'color'
2710 */
2711void owl_function_color_current_filter(const char *fgcolor, const char *bgcolor)
2712{
2713  const char *name;
2714
2715  name=owl_view_get_filtname(owl_global_get_current_view(&g));
2716  owl_function_color_filter(name, fgcolor, bgcolor);
2717}
2718
2719/* Set the color of the filter 'filter' to be 'color'.  If the color
2720 * name does not exist, return -1, if the filter does not exist or is
2721 * the "all" filter, return -2.  Return 0 on success
2722 */
2723int owl_function_color_filter(const char *filtname, const char *fgcolor, const char *bgcolor)
2724{
2725  owl_filter *f;
2726
2727  f=owl_global_get_filter(&g, filtname);
2728  if (!f) {
2729    owl_function_error("Unknown filter");
2730    return(-2);
2731  }
2732
2733  /* don't touch the all filter */
2734  if (!strcmp(filtname, "all")) {
2735    owl_function_error("You may not change the 'all' filter.");
2736    return(-2);
2737  }
2738
2739  if (owl_util_string_to_color(fgcolor)==OWL_COLOR_INVALID) {
2740    owl_function_error("No color named '%s' avilable.", fgcolor);
2741    return(-1);
2742  }
2743
2744
2745  if (bgcolor != NULL) {
2746    if (owl_util_string_to_color(bgcolor)==OWL_COLOR_INVALID) {
2747      owl_function_error("No color named '%s' avilable.", bgcolor);
2748      return(-1);
2749    }
2750    owl_filter_set_bgcolor(f, owl_util_string_to_color(bgcolor));
2751  }
2752  owl_filter_set_fgcolor(f, owl_util_string_to_color(fgcolor));
2753 
2754  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2755  return(0);
2756}
2757
2758void owl_function_show_colors(void)
2759{
2760  owl_fmtext fm;
2761  int i; 
2762 
2763  owl_fmtext_init_null(&fm);
2764  owl_fmtext_append_normal(&fm,"default:  ");
2765  owl_fmtext_append_normal_color(&fm, "default\n", OWL_COLOR_DEFAULT, OWL_COLOR_DEFAULT);
2766
2767  owl_fmtext_append_normal(&fm,"black:    ");
2768  owl_fmtext_append_normal_color(&fm, "black\n", OWL_COLOR_BLACK, OWL_COLOR_DEFAULT);
2769
2770  owl_fmtext_append_normal(&fm,"red:      ");
2771  owl_fmtext_append_normal_color(&fm, "red\n", OWL_COLOR_RED, OWL_COLOR_DEFAULT);
2772
2773  owl_fmtext_append_normal(&fm,"green:    ");
2774  owl_fmtext_append_normal_color(&fm, "green\n", OWL_COLOR_GREEN, OWL_COLOR_DEFAULT);
2775
2776  owl_fmtext_append_normal(&fm,"yellow:   ");
2777  owl_fmtext_append_normal_color(&fm, "yellow\n", OWL_COLOR_YELLOW, OWL_COLOR_DEFAULT);
2778
2779  owl_fmtext_append_normal(&fm,"blue:     ");
2780  owl_fmtext_append_normal_color(&fm, "blue\n", OWL_COLOR_BLUE, OWL_COLOR_DEFAULT);
2781
2782  owl_fmtext_append_normal(&fm,"magenta:  ");
2783  owl_fmtext_append_normal_color(&fm, "magenta\n", OWL_COLOR_MAGENTA, OWL_COLOR_DEFAULT);
2784
2785  owl_fmtext_append_normal(&fm,"cyan:     ");
2786  owl_fmtext_append_normal_color(&fm, "cyan\n", OWL_COLOR_CYAN, OWL_COLOR_DEFAULT);
2787
2788  owl_fmtext_append_normal(&fm,"white:    ");
2789  owl_fmtext_append_normal_color(&fm, "white\n", OWL_COLOR_WHITE, OWL_COLOR_DEFAULT);
2790
2791  for(i = 8; i < COLORS; ++i) {
2792    char* str1 = g_strdup_printf("%4i:     ",i);
2793    char* str2 = g_strdup_printf("%i\n",i);
2794    owl_fmtext_append_normal(&fm,str1);
2795    owl_fmtext_append_normal_color(&fm, str2, i, OWL_COLOR_DEFAULT);
2796    g_free(str1);
2797     g_free(str2);
2798  }
2799 
2800  owl_function_popless_fmtext(&fm);
2801  owl_fmtext_cleanup(&fm);
2802}
2803
2804/* add the given class, inst, recip to the punt list for filtering.
2805 *   if direction==0 then punt
2806 *   if direction==1 then unpunt
2807 */
2808void owl_function_zpunt(const char *class, const char *inst, const char *recip, int direction)
2809{
2810  GPtrArray *argv;
2811  char *quoted;
2812
2813  argv = g_ptr_array_new();
2814  if (!strcmp(class, "*")) {
2815    g_ptr_array_add(argv, g_strdup("class"));
2816    g_ptr_array_add(argv, g_strdup(".*"));
2817  } else {
2818    quoted=owl_text_quote(class, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2819    g_ptr_array_add(argv, g_strdup("class"));
2820    g_ptr_array_add(argv, g_strdup_printf("^(un)*%s(\\.d)*$", quoted));
2821    g_free(quoted);
2822  }
2823  if (!strcmp(inst, "*")) {
2824    g_ptr_array_add(argv, g_strdup("and"));
2825    g_ptr_array_add(argv, g_strdup("instance"));
2826    g_ptr_array_add(argv, g_strdup(".*"));
2827  } else {
2828    quoted=owl_text_quote(inst, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2829    g_ptr_array_add(argv, g_strdup("and"));
2830    g_ptr_array_add(argv, g_strdup("instance"));
2831    g_ptr_array_add(argv, g_strdup_printf("^(un)*%s(\\.d)*$", quoted));
2832    g_free(quoted);
2833  }
2834  if (!strcmp(recip, "*")) {
2835    /* nothing */
2836  } else {
2837    if(!strcmp(recip, "%me%")) {
2838      recip = owl_zephyr_get_sender();
2839    }
2840    quoted=owl_text_quote(recip, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2841    g_ptr_array_add(argv, g_strdup("and"));
2842    g_ptr_array_add(argv, g_strdup("recipient"));
2843    g_ptr_array_add(argv, g_strdup_printf("^%s$", quoted));
2844    g_free(quoted);
2845  }
2846
2847  owl_function_punt(argv->len, (const char *const*) argv->pdata, direction);
2848  owl_ptr_array_free(argv, g_free);
2849}
2850
2851void owl_function_punt(int argc, const char *const *argv, int direction)
2852{
2853  owl_filter *f;
2854  GPtrArray *fl;
2855  int i;
2856  fl=owl_global_get_puntlist(&g);
2857
2858  /* first, create the filter */
2859  f = owl_filter_new("punt-filter", argc, argv);
2860  if (f == NULL) {
2861    owl_function_error("Error creating filter for zpunt");
2862    return;
2863  }
2864
2865  /* Check for an identical filter */
2866  for (i = 0; i < fl->len; i++) {
2867    if (owl_filter_equiv(f, fl->pdata[i])) {
2868      owl_function_debugmsg("found an equivalent punt filter");
2869      /* if we're punting, then just silently bow out on this duplicate */
2870      if (direction==0) {
2871        owl_filter_delete(f);
2872        return;
2873      }
2874
2875      /* if we're unpunting, then remove this filter from the puntlist */
2876      if (direction==1) {
2877        owl_filter_delete(g_ptr_array_remove_index(fl, i));
2878        owl_filter_delete(f);
2879        return;
2880      }
2881    }
2882  }
2883
2884  if (direction == 0) {
2885    owl_function_debugmsg("punting");
2886    /* If we're punting, add the filter to the global punt list */
2887    g_ptr_array_add(fl, f);
2888  } else if (direction == 1) {
2889    owl_function_makemsg("No matching punt filter");
2890 }
2891}
2892
2893void owl_function_show_keymaps(void)
2894{
2895  GPtrArray *l;
2896  owl_fmtext fm;
2897  const owl_keymap *km;
2898  const owl_keyhandler *kh;
2899  int i;
2900  const char *kmname;
2901
2902  kh = owl_global_get_keyhandler(&g);
2903  owl_fmtext_init_null(&fm);
2904  owl_fmtext_append_bold(&fm, "Keymaps:   ");
2905  owl_fmtext_append_normal(&fm, "(use 'show keymap <name>' for details)\n");
2906  l = owl_keyhandler_get_keymap_names(kh);
2907  owl_fmtext_append_list(&fm, l, "\n", owl_function_keymap_summary);
2908  owl_fmtext_append_normal(&fm, "\n");
2909
2910  for (i = 0; i < l->len; i++) {
2911    kmname = l->pdata[i];
2912    km = owl_keyhandler_get_keymap(kh, kmname);
2913    owl_fmtext_append_bold(&fm, "\n\n----------------------------------------------------------------------------------------------------\n\n");
2914    owl_keymap_get_details(km, &fm, 0);
2915  }
2916  owl_fmtext_append_normal(&fm, "\n");
2917 
2918  owl_function_popless_fmtext(&fm);
2919  owl_ptr_array_free(l, g_free);
2920  owl_fmtext_cleanup(&fm);
2921}
2922
2923CALLER_OWN char *owl_function_keymap_summary(const char *name)
2924{
2925  const owl_keymap *km
2926    = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), name);
2927  if (km) return owl_keymap_summary(km);
2928  else return(NULL);
2929}
2930
2931/* TODO: implement for real */
2932void owl_function_show_keymap(const char *name)
2933{
2934  owl_fmtext fm;
2935  const owl_keymap *km;
2936
2937  owl_fmtext_init_null(&fm);
2938  km = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), name);
2939  if (km) {
2940    owl_keymap_get_details(km, &fm, 1);
2941  } else {
2942    owl_fmtext_append_normal(&fm, "No such keymap...\n");
2943  } 
2944  owl_function_popless_fmtext(&fm);
2945  owl_fmtext_cleanup(&fm);
2946}
2947
2948void owl_function_help_for_command(const char *cmdname)
2949{
2950  owl_fmtext fm;
2951
2952  owl_fmtext_init_null(&fm);
2953  owl_cmd_get_help(owl_global_get_cmddict(&g), cmdname, &fm);
2954  owl_function_popless_fmtext(&fm); 
2955  owl_fmtext_cleanup(&fm);
2956}
2957
2958void owl_function_set_search(const char *string)
2959{
2960  owl_regex re;
2961
2962  if (string && owl_regex_create_quoted(&re, string) == 0) {
2963    owl_global_set_search_re(&g, &re);
2964    owl_regex_cleanup(&re);
2965  } else {
2966    owl_global_set_search_re(&g, NULL);
2967  }
2968}
2969
2970void owl_function_search_helper(int consider_current, int direction)
2971{
2972  /* move to a message that contains the string.  If direction is
2973   * OWL_DIRECTION_DOWNWARDS then search fowards, if direction is
2974   * OWL_DIRECTION_UPWARDS then search backwards.
2975   *
2976   * If consider_current is true then it will stay on the
2977   * current message if it contains the string.
2978   */
2979
2980  const owl_view *v;
2981  int viewsize, i, curmsg, start;
2982  owl_message *m;
2983
2984  v=owl_global_get_current_view(&g);
2985  viewsize=owl_view_get_size(v);
2986  curmsg=owl_global_get_curmsg(&g);
2987 
2988  if (viewsize==0) {
2989    owl_function_makemsg("No messages present");
2990    return;
2991  }
2992
2993  if (consider_current) {
2994    start=curmsg;
2995  } else if (direction==OWL_DIRECTION_DOWNWARDS) {
2996    start=curmsg+1;
2997  } else {
2998    start=curmsg-1;
2999  }
3000
3001  /* bounds check */
3002  if (start>=viewsize || start<0) {
3003    owl_function_makemsg("No further matches found");
3004    return;
3005  }
3006
3007  for (i=start; i<viewsize && i>=0;) {
3008    m=owl_view_get_element(v, i);
3009    if (owl_message_search(m, owl_global_get_search_re(&g))) {
3010      owl_global_set_curmsg(&g, i);
3011      owl_function_calculate_topmsg(direction);
3012      owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3013      if (direction==OWL_DIRECTION_DOWNWARDS) {
3014        owl_global_set_direction_downwards(&g);
3015      } else {
3016        owl_global_set_direction_upwards(&g);
3017      }
3018      return;
3019    }
3020    if (direction==OWL_DIRECTION_DOWNWARDS) {
3021      i++;
3022    } else {
3023      i--;
3024    }
3025    if (owl_global_take_interrupt(&g)) {
3026      owl_function_makemsg("Search interrupted!");
3027      owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3028      return;
3029    }
3030  }
3031  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3032  owl_function_makemsg("No matches found");
3033}
3034
3035/* strips formatting from ztext and returns the unformatted text.
3036 * caller is responsible for freeing. */
3037CALLER_OWN char *owl_function_ztext_stylestrip(const char *zt)
3038{
3039  owl_fmtext fm;
3040  char *plaintext;
3041
3042  owl_fmtext_init_null(&fm);
3043  owl_fmtext_append_ztext(&fm, zt);
3044  plaintext = owl_fmtext_print_plain(&fm);
3045  owl_fmtext_cleanup(&fm);
3046  return(plaintext);
3047}
3048
3049/* Popup a buddylisting.  If filename is NULL use the default .anyone */
3050void owl_function_buddylist(int aim, int zephyr, const char *filename)
3051{
3052  int i, j, idle;
3053  int interrupted = 0;
3054  owl_fmtext fm;
3055  const owl_buddylist *bl;
3056  const owl_buddy *b;
3057  char *timestr;
3058#ifdef HAVE_LIBZEPHYR
3059  int x;
3060  GPtrArray *anyone;
3061  const char *user;
3062  char *tmp;
3063  ZLocations_t location[200];
3064  int numlocs, ret;
3065#endif
3066
3067  owl_fmtext_init_null(&fm);
3068
3069  /* AIM first */
3070  if (aim && owl_global_is_aimloggedin(&g)) {
3071    bl=owl_global_get_buddylist(&g);
3072
3073    owl_fmtext_append_bold(&fm, "AIM users logged in:\n");
3074    /* we're assuming AIM for now */
3075    j=owl_buddylist_get_size(bl);
3076    for (i=0; i<j; i++) {
3077      b=owl_buddylist_get_buddy_n(bl, i);
3078      idle=owl_buddy_get_idle_time(b);
3079      if (idle!=0) {
3080        timestr=owl_util_minutes_to_timestr(idle);
3081      } else {
3082        timestr=g_strdup("");
3083      }
3084      owl_fmtext_appendf_normal(&fm, "  %-20.20s %-12.12s\n", owl_buddy_get_name(b), timestr);
3085      g_free(timestr);
3086    }
3087  }
3088
3089#ifdef HAVE_LIBZEPHYR
3090  if (zephyr) {
3091    if(!owl_global_is_havezephyr(&g)) {
3092      owl_function_error("Zephyr currently not available.");
3093    } else {
3094      owl_fmtext_append_bold(&fm, "Zephyr users logged in:\n");
3095      anyone = owl_zephyr_get_anyone_list(filename);
3096      if (anyone == NULL) {
3097        if (errno == ENOENT) {
3098          owl_fmtext_append_normal(&fm, " You have not added any zephyr buddies.  Use the\n");
3099          owl_fmtext_append_normal(&fm, " command ':addbuddy zephyr ");
3100          owl_fmtext_append_bold(  &fm, "<username>");
3101          owl_fmtext_append_normal(&fm, "'.\n");
3102        } else {
3103          owl_fmtext_append_normal(&fm, " Could not read zephyr buddies from the .anyone file.\n");
3104        }
3105      } else {
3106        for (i = 0; i < anyone->len; i++) {
3107          user = anyone->pdata[i];
3108          ret=ZLocateUser(zstr(user), &numlocs, ZAUTH);
3109
3110          if (owl_global_take_interrupt(&g)) {
3111            interrupted = 1;
3112            owl_function_makemsg("Interrupted!");
3113            break;
3114          }
3115
3116          if (ret!=ZERR_NONE) {
3117            owl_function_error("Error getting location for %s", user);
3118            continue;
3119          }
3120
3121          numlocs=200;
3122          ret=ZGetLocations(location, &numlocs);
3123          if (ret==0) {
3124            for (x=0; x<numlocs; x++) {
3125              tmp=short_zuser(user);
3126              owl_fmtext_appendf_normal(&fm, "  %-10.10s %-24.24s %-12.12s  %20.20s\n",
3127                                        tmp,
3128                                        location[x].host,
3129                                        location[x].tty,
3130                                        location[x].time);
3131              g_free(tmp);
3132            }
3133            if (numlocs>=200) {
3134              owl_fmtext_append_normal(&fm, "  Too many locations found for this user, truncating.\n");
3135            }
3136          }
3137        }
3138      }
3139      owl_ptr_array_free(anyone, g_free);
3140    }
3141  }
3142#endif
3143
3144  if (aim && zephyr) {
3145    if (owl_perlconfig_is_function("BarnOwl::Hooks::_get_blist")) {
3146      char * perlblist = owl_perlconfig_execute("BarnOwl::Hooks::_get_blist()");
3147      if (perlblist) {
3148        owl_fmtext_append_ztext(&fm, perlblist);
3149        g_free(perlblist);
3150      }
3151    }
3152  }
3153
3154  if(!interrupted) {
3155    owl_function_popless_fmtext(&fm);
3156  }
3157  owl_fmtext_cleanup(&fm);
3158}
3159
3160/* Dump messages in the current view to the file 'filename'. */
3161void owl_function_dump(const char *filename) 
3162{
3163  int i, j;
3164  owl_message *m;
3165  const owl_view *v;
3166  FILE *file;
3167  char *plaintext;
3168
3169  v=owl_global_get_current_view(&g);
3170
3171  /* in the future make it ask yes/no */
3172  /*
3173  ret=stat(filename, &sbuf);
3174  if (!ret) {
3175    ret=owl_function_askyesno("File exists, continue? [Y/n]");
3176    if (!ret) return;
3177  }
3178  */
3179
3180  file=fopen(filename, "w");
3181  if (!file) {
3182    owl_function_error("Error opening file");
3183    return;
3184  }
3185
3186  j=owl_view_get_size(v);
3187  for (i=0; i<j; i++) {
3188    m=owl_view_get_element(v, i);
3189    plaintext = owl_strip_format_chars(owl_message_get_text(m));
3190    if (plaintext) {
3191      fputs(plaintext, file);
3192      g_free(plaintext);
3193    }
3194  }
3195  fclose(file);
3196  owl_function_makemsg("Messages dumped to %s", filename);
3197}
3198
3199void owl_function_do_newmsgproc(void)
3200{
3201  if (owl_global_get_newmsgproc(&g) && strcmp(owl_global_get_newmsgproc(&g), "")) {
3202    /* if there's a process out there, we need to check on it */
3203    if (owl_global_get_newmsgproc_pid(&g)) {
3204      owl_function_debugmsg("Checking on newmsgproc pid==%i", owl_global_get_newmsgproc_pid(&g));
3205      owl_function_debugmsg("Waitpid return is %i", waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG));
3206      waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG);
3207      if (waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG)==-1) {
3208        /* it exited */
3209        owl_global_set_newmsgproc_pid(&g, 0);
3210        owl_function_debugmsg("newmsgproc exited");
3211      } else {
3212        owl_function_debugmsg("newmsgproc did not exit");
3213      }
3214    }
3215   
3216    /* if it exited, spawn a new one */
3217    if (owl_global_get_newmsgproc_pid(&g)==0) {
3218      int myargc;
3219      char **argv = owl_parseline(owl_global_get_newmsgproc(&g), &myargc);
3220      if (myargc < 0) {
3221        owl_function_debugmsg("Could not parse newmsgproc '%s': unbalanced quotes?",
3222                              owl_global_get_newmsgproc(&g));
3223      } else if (myargc > 0) {
3224        /* Spawn the child. */
3225        GPid pid;
3226        GError *error = NULL;
3227        owl_function_debugmsg("About to exec \"%s\" with %d arguments", argv[0], myargc);
3228        if (g_spawn_async(NULL, argv, NULL,
3229                          G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
3230                          NULL, NULL, &pid, &error)) {
3231          owl_global_set_newmsgproc_pid(&g, pid);
3232          owl_function_debugmsg("I'm the parent and I started a new newmsgproc with pid %i", pid);
3233        } else {
3234          owl_function_debugmsg("Cannot run newmsgproc '%s': %s",
3235                                owl_global_get_newmsgproc(&g), error->message);
3236          g_error_free(error);
3237        }
3238      }
3239      g_strfreev(argv);
3240    }
3241  }
3242}
3243
3244/* print the xterm escape sequence to raise the window */
3245void owl_function_xterm_raise(void)
3246{
3247  printf("\033[5t");
3248}
3249
3250/* print the xterm escape sequence to deiconify the window */
3251void owl_function_xterm_deiconify(void)
3252{
3253  printf("\033[1t");
3254}
3255
3256/* Add the specified command to the startup file.  Eventually this
3257 * should be clever, and rewriting settings that will obviosly
3258 * override earlier settings with 'set' 'bindkey' and 'alias'
3259 * commands.  For now though we just remove any line that would
3260 * duplicate this one and then append this line to the end of
3261 * startupfile.
3262 */
3263void owl_function_addstartup(const char *buff)
3264{
3265  FILE *file;
3266  const char *filename;
3267
3268  filename=owl_global_get_startupfile(&g);
3269
3270  /* delete earlier copies, if the file exists */
3271  if (g_file_test(filename, G_FILE_TEST_EXISTS))
3272    owl_util_file_deleteline(filename, buff, 1);
3273
3274  file=fopen(filename, "a");
3275  if (!file) {
3276    owl_function_error("Error opening startupfile for new command");
3277    return;
3278  }
3279
3280  /* add this line */
3281  fprintf(file, "%s\n", buff);
3282
3283  fclose(file);
3284}
3285
3286/* Remove the specified command from the startup file. */
3287void owl_function_delstartup(const char *buff)
3288{
3289  const char *filename;
3290  filename=owl_global_get_startupfile(&g);
3291  owl_util_file_deleteline(filename, buff, 1);
3292}
3293
3294/* Execute owl commands from the given filename.  If the filename
3295 * is NULL, use the default owl startup commands file.
3296 */
3297void owl_function_source(const char *filename)
3298{
3299  char *path;
3300  FILE *file;
3301  char *s = NULL;
3302  int fail_silent = 0;
3303
3304  if (!filename) {
3305    fail_silent = 1;
3306    path = g_strdup(owl_global_get_startupfile(&g));
3307  } else {
3308    path = owl_util_makepath(filename);
3309  }
3310  file = fopen(path, "r");
3311  g_free(path);
3312  if (!file) {
3313    if (!fail_silent) {
3314      owl_function_error("Error opening file: %s", filename);
3315    }
3316    return;
3317  }
3318  while (owl_getline_chomp(&s, file)) {
3319    if (s[0] == '\0' || s[0] == '#')
3320      continue;
3321    owl_function_command_norv(s);
3322  }
3323
3324  g_free(s);
3325  fclose(file);
3326}
3327
3328void owl_function_change_style(owl_view *v, const char *stylename)
3329{
3330  const owl_style *s;
3331
3332  s=owl_global_get_style_by_name(&g, stylename);
3333  if (!s) {
3334    owl_function_error("No style named %s", stylename);
3335    return;
3336  }
3337  owl_view_set_style(v, s);
3338  owl_messagelist_invalidate_formats(owl_global_get_msglist(&g));
3339  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
3340  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3341}
3342
3343void owl_function_toggleoneline(void)
3344{
3345  owl_view *v;
3346  const owl_style *s;
3347
3348  v=owl_global_get_current_view(&g);
3349  s=owl_view_get_style(v);
3350
3351  if (!owl_style_matches_name(s, "oneline")) {
3352    owl_function_change_style(v, "oneline");
3353  } else {
3354    owl_function_change_style(v, owl_global_get_default_style(&g));
3355  }
3356
3357  owl_messagelist_invalidate_formats(owl_global_get_msglist(&g));
3358  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
3359  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3360}
3361
3362void G_GNUC_PRINTF(1, 2) owl_function_error(const char *fmt, ...)
3363{
3364  static int in_error = 0;
3365  va_list ap;
3366  char *buff;
3367  const char *nl;
3368
3369  if (++in_error > 2) {
3370    /* More than two nested errors, bail immediately. */
3371    in_error--;
3372    return;
3373  }
3374
3375  va_start(ap, fmt);
3376  buff = g_strdup_vprintf(fmt, ap);
3377  va_end(ap);
3378
3379  owl_function_debugmsg("ERROR: %s", buff);
3380  owl_function_log_err(buff);
3381
3382  nl = strchr(buff, '\n');
3383
3384  /*
3385    Showing admin messages triggers a lot of code. If we have a
3386    recursive error call, that's the most likely candidate, so
3387    suppress the call in that case, to try to avoid infinite looping.
3388  */
3389
3390  if(nl && *(nl + 1) && in_error == 1) {
3391    /* Multiline error */
3392    owl_function_adminmsg("ERROR", buff);
3393  } else {
3394    owl_function_makemsg("[Error] %s", buff);
3395  }
3396
3397  g_free(buff);
3398
3399  in_error--;
3400}
3401
3402void owl_function_log_err(const char *string)
3403{
3404  char *date;
3405  time_t now;
3406  char *buff;
3407
3408  now = time(NULL);
3409  date = owl_util_time_to_timestr(localtime(&now));
3410
3411  buff = g_strdup_printf("%s %s", date, string);
3412
3413  owl_errqueue_append_err(owl_global_get_errqueue(&g), buff);
3414
3415  g_free(buff);
3416  g_free(date);
3417}
3418
3419void owl_function_showerrs(void)
3420{
3421  owl_fmtext fm;
3422
3423  owl_fmtext_init_null(&fm);
3424  owl_fmtext_append_normal(&fm, "Errors:\n\n");
3425  owl_errqueue_to_fmtext(owl_global_get_errqueue(&g), &fm);
3426  owl_function_popless_fmtext(&fm);
3427}
3428
3429void G_GNUC_PRINTF(1, 2) owl_function_makemsg(const char *fmt, ...)
3430{
3431  va_list ap;
3432  char *str;
3433
3434  va_start(ap, fmt);
3435  str = g_strdup_vprintf(fmt, ap);
3436  va_end(ap);
3437
3438  owl_function_debugmsg("makemsg: %s", str);
3439  owl_msgwin_set_text_nocopy(&g.msgwin, str);
3440}
3441
3442/* get locations for everyone in .anyone.  If 'notify' is '1' then
3443 * send a pseudo login or logout message for everyone not in sync with
3444 * the global zephyr buddy list.  The list is updated regardless of
3445 * the status of 'notify'.
3446 */
3447void owl_function_zephyr_buddy_check(int notify)
3448{
3449#ifdef HAVE_LIBZEPHYR
3450  int i;
3451  GPtrArray *anyone;
3452  GList **zaldlist;
3453  GList *zaldptr;
3454  ZAsyncLocateData_t *zald;
3455  const char *user;
3456
3457  if (!owl_global_is_havezephyr(&g)) return;
3458  owl_global_set_pseudologin_notify(&g, notify);
3459  zaldlist = owl_global_get_zaldlist(&g);
3460
3461  /* Clear the existing ZALDs first. */
3462  zaldptr = g_list_first(*zaldlist);
3463  while (zaldptr) {
3464    ZFreeALD(zaldptr->data);
3465    g_free(zaldptr->data);
3466    zaldptr = g_list_next(zaldptr);
3467  }
3468  g_list_free(*zaldlist);
3469  *zaldlist = NULL;
3470
3471  anyone = owl_zephyr_get_anyone_list(NULL);
3472  if (anyone != NULL) {
3473    for (i = 0; i < anyone->len; i++) {
3474      user = anyone->pdata[i];
3475      zald = g_new(ZAsyncLocateData_t, 1);
3476      if (ZRequestLocations(zstr(user), zald, UNACKED, ZAUTH) == ZERR_NONE) {
3477        *zaldlist = g_list_append(*zaldlist, zald);
3478      } else {
3479        g_free(zald);
3480      }
3481    }
3482    owl_ptr_array_free(anyone, g_free);
3483  }
3484#endif
3485}
3486
3487void owl_function_aimsearch_results(const char *email, GPtrArray *namelist)
3488{
3489  owl_fmtext fm;
3490  int i;
3491
3492  owl_fmtext_init_null(&fm);
3493  owl_fmtext_append_normal(&fm, "AIM screennames associated with ");
3494  owl_fmtext_append_normal(&fm, email);
3495  owl_fmtext_append_normal(&fm, ":\n");
3496
3497  for (i = 0; i < namelist->len; i++) {
3498    owl_fmtext_append_normal(&fm, "  ");
3499    owl_fmtext_append_normal(&fm, namelist->pdata[i]);
3500    owl_fmtext_append_normal(&fm, "\n");
3501  }
3502
3503  owl_function_popless_fmtext(&fm);
3504  owl_fmtext_cleanup(&fm);
3505}
3506
3507int owl_function_get_color_count(void)
3508{
3509     return COLORS;
3510}
3511
3512void _owl_function_mark_message(const owl_message *m)
3513{
3514  if (m) {
3515    owl_global_set_markedmsgid(&g, owl_message_get_id(m));
3516    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3517  }
3518}
3519
3520void owl_function_mark_message(void)
3521{
3522  const owl_message *m;
3523  const owl_view *v;
3524
3525  v=owl_global_get_current_view(&g);
3526
3527  /* bail if there's no current message */
3528  if (owl_view_get_size(v) < 1) {
3529    owl_function_error("No messages to mark");
3530    return;
3531  }
3532
3533  /* mark the message */
3534  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
3535  _owl_function_mark_message(m);
3536  owl_function_makemsg("Mark set");
3537}
3538
3539void owl_function_swap_cur_marked(void)
3540{
3541  int marked_id;
3542  const owl_message *m;
3543  const owl_view *v;
3544
3545  marked_id=owl_global_get_markedmsgid(&g);
3546  if (marked_id == -1) {
3547    owl_function_error("Mark not set.");
3548    return;
3549  }
3550
3551  v=owl_global_get_current_view(&g);
3552  /* bail if there's no current message */
3553  if (owl_view_get_size(v) < 1) {
3554    return;
3555  }
3556
3557  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
3558  _owl_function_mark_message(m);
3559  owl_global_set_curmsg(&g, owl_view_get_nearest_to_msgid(v, marked_id));
3560  owl_function_calculate_topmsg(OWL_DIRECTION_NONE);
3561  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3562  owl_global_set_direction_downwards(&g);
3563}
Note: See TracBrowser for help on using the repository browser.