source: functions.c @ 7756dde

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