source: functions.c @ ca54fd6

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