source: functions.c @ a728387

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