source: functions.c @ 38e2250

release-1.10release-1.7release-1.8release-1.9
Last change on this file since 38e2250 was fc5eef4, checked in by David Benjamin <davidben@mit.edu>, 14 years ago
Configure owl_global_set_typwin_inactive to be called on context deactivate This removes the explicit calls to the deactivate function. In particular, it means that we can set up an alternate deactivate function and edit:done needn't know about it.
  • Property mode set to 100644
File size: 95.5 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_search_start(const char *string, int direction)
2889{
2890  /* direction is OWL_DIRECTION_DOWNWARDS or OWL_DIRECTION_UPWARDS or
2891   * OWL_DIRECTION_NONE */
2892  owl_regex re;
2893
2894  if (string && owl_regex_create_quoted(&re, string) == 0) {
2895    owl_global_set_search_re(&g, &re);
2896    owl_regex_cleanup(&re);
2897  } else {
2898    owl_global_set_search_re(&g, NULL);
2899  }
2900
2901  if (direction == OWL_DIRECTION_NONE)
2902    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2903  else
2904    owl_function_search_helper(0, direction);
2905}
2906
2907void owl_function_search_continue(int direction)
2908{
2909  /* direction is OWL_DIRECTION_DOWNWARDS or OWL_DIRECTION_UPWARDS */
2910  owl_function_search_helper(1, direction);
2911}
2912
2913void owl_function_search_helper(int mode, int direction)
2914{
2915  /* move to a message that contains the string.  If direction is
2916   * OWL_DIRECTION_DOWNWARDS then search fowards, if direction is
2917   * OWL_DIRECTION_UPWARDS then search backwards.
2918   *
2919   * If mode==0 then it will stay on the current message if it
2920   * contains the string.
2921   */
2922
2923  const owl_view *v;
2924  int viewsize, i, curmsg, start;
2925  owl_message *m;
2926
2927  v=owl_global_get_current_view(&g);
2928  viewsize=owl_view_get_size(v);
2929  curmsg=owl_global_get_curmsg(&g);
2930 
2931  if (viewsize==0) {
2932    owl_function_error("No messages present");
2933    return;
2934  }
2935
2936  if (mode==0) {
2937    start=curmsg;
2938  } else if (direction==OWL_DIRECTION_DOWNWARDS) {
2939    start=curmsg+1;
2940  } else {
2941    start=curmsg-1;
2942  }
2943
2944  /* bounds check */
2945  if (start>=viewsize || start<0) {
2946    owl_function_error("No further matches found");
2947    return;
2948  }
2949
2950  for (i=start; i<viewsize && i>=0;) {
2951    m=owl_view_get_element(v, i);
2952    if (owl_message_search(m, owl_global_get_search_re(&g))) {
2953      owl_global_set_curmsg(&g, i);
2954      owl_function_calculate_topmsg(direction);
2955      owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2956      if (direction==OWL_DIRECTION_DOWNWARDS) {
2957        owl_global_set_direction_downwards(&g);
2958      } else {
2959        owl_global_set_direction_upwards(&g);
2960      }
2961      return;
2962    }
2963    if (direction==OWL_DIRECTION_DOWNWARDS) {
2964      i++;
2965    } else {
2966      i--;
2967    }
2968    owl_function_mask_sigint(NULL);
2969    if(owl_global_is_interrupted(&g)) {
2970      owl_global_unset_interrupted(&g);
2971      owl_function_unmask_sigint(NULL);
2972      owl_function_makemsg("Search interrupted!");
2973      owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2974      return;
2975    }
2976    owl_function_unmask_sigint(NULL);
2977  }
2978  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2979  owl_function_error("No matches found");
2980}
2981
2982/* strips formatting from ztext and returns the unformatted text.
2983 * caller is responsible for freeing. */
2984char *owl_function_ztext_stylestrip(const char *zt)
2985{
2986  owl_fmtext fm;
2987  char *plaintext;
2988
2989  owl_fmtext_init_null(&fm);
2990  owl_fmtext_append_ztext(&fm, zt);
2991  plaintext = owl_fmtext_print_plain(&fm);
2992  owl_fmtext_cleanup(&fm);
2993  return(plaintext);
2994}
2995
2996/* Popup a buddylisting.  If filename is NULL use the default .anyone */
2997void owl_function_buddylist(int aim, int zephyr, const char *filename)
2998{
2999  int i, j, idle;
3000  int interrupted = 0;
3001  owl_fmtext fm;
3002  const owl_buddylist *bl;
3003  const owl_buddy *b;
3004  char *timestr;
3005#ifdef HAVE_LIBZEPHYR
3006  int x;
3007  owl_list anyone;
3008  const char *user;
3009  char *tmp;
3010  ZLocations_t location[200];
3011  int numlocs, ret;
3012#endif
3013
3014  owl_fmtext_init_null(&fm);
3015
3016  /* AIM first */
3017  if (aim && owl_global_is_aimloggedin(&g)) {
3018    bl=owl_global_get_buddylist(&g);
3019
3020    owl_fmtext_append_bold(&fm, "AIM users logged in:\n");
3021    /* we're assuming AIM for now */
3022    j=owl_buddylist_get_size(bl);
3023    for (i=0; i<j; i++) {
3024      b=owl_buddylist_get_buddy_n(bl, i);
3025      idle=owl_buddy_get_idle_time(b);
3026      if (idle!=0) {
3027        timestr=owl_util_minutes_to_timestr(idle);
3028      } else {
3029        timestr=owl_strdup("");
3030      }
3031      owl_fmtext_appendf_normal(&fm, "  %-20.20s %-12.12s\n", owl_buddy_get_name(b), timestr);
3032      owl_free(timestr);
3033    }
3034  }
3035
3036#ifdef HAVE_LIBZEPHYR
3037  if (zephyr) {
3038    if(!owl_global_is_havezephyr(&g)) {
3039      owl_function_error("Zephyr currently not available.");
3040    } else {
3041      owl_fmtext_append_bold(&fm, "Zephyr users logged in:\n");
3042      owl_list_create(&anyone);
3043      ret=owl_zephyr_get_anyone_list(&anyone, filename);
3044      if (ret) {
3045        if (errno == ENOENT) {
3046          owl_fmtext_append_normal(&fm, " You have not added any zephyr buddies.  Use the\n");
3047          owl_fmtext_append_normal(&fm, " command ':addbuddy zephyr ");
3048          owl_fmtext_append_bold(  &fm, "<username>");
3049          owl_fmtext_append_normal(&fm, "'.\n");
3050        } else {
3051          owl_fmtext_append_normal(&fm, " Could not read zephyr buddies from the .anyone file.\n");
3052        }
3053      } else {
3054        j=owl_list_get_size(&anyone);
3055        for (i=0; i<j; i++) {
3056          user=owl_list_get_element(&anyone, i);
3057          ret=ZLocateUser(zstr(user), &numlocs, ZAUTH);
3058
3059          owl_function_mask_sigint(NULL);
3060          if(owl_global_is_interrupted(&g)) {
3061            interrupted = 1;
3062            owl_global_unset_interrupted(&g);
3063            owl_function_unmask_sigint(NULL);
3064            owl_function_makemsg("Interrupted!");
3065            break;
3066          }
3067
3068          owl_function_unmask_sigint(NULL);
3069
3070          if (ret!=ZERR_NONE) {
3071            owl_function_error("Error getting location for %s", user);
3072            continue;
3073          }
3074
3075          numlocs=200;
3076          ret=ZGetLocations(location, &numlocs);
3077          if (ret==0) {
3078            for (x=0; x<numlocs; x++) {
3079              tmp=short_zuser(user);
3080              owl_fmtext_appendf_normal(&fm, "  %-10.10s %-24.24s %-12.12s  %20.20s\n",
3081                                        tmp,
3082                                        location[x].host,
3083                                        location[x].tty,
3084                                        location[x].time);
3085              owl_free(tmp);
3086            }
3087            if (numlocs>=200) {
3088              owl_fmtext_append_normal(&fm, "  Too many locations found for this user, truncating.\n");
3089            }
3090          }
3091        }
3092      }
3093      owl_list_cleanup(&anyone, owl_free);
3094    }
3095  }
3096#endif
3097
3098  if (aim && zephyr) {
3099    if (owl_perlconfig_is_function("BarnOwl::Hooks::_get_blist")) {
3100      char * perlblist = owl_perlconfig_execute("BarnOwl::Hooks::_get_blist()");
3101      if (perlblist) {
3102        owl_fmtext_append_ztext(&fm, perlblist);
3103        owl_free(perlblist);
3104      }
3105    }
3106  }
3107
3108  if(!interrupted) {
3109    owl_function_popless_fmtext(&fm);
3110  }
3111  owl_fmtext_cleanup(&fm);
3112}
3113
3114/* Dump messages in the current view to the file 'filename'. */
3115void owl_function_dump(const char *filename) 
3116{
3117  int i, j;
3118  owl_message *m;
3119  const owl_view *v;
3120  FILE *file;
3121  char *plaintext;
3122
3123  v=owl_global_get_current_view(&g);
3124
3125  /* in the future make it ask yes/no */
3126  /*
3127  ret=stat(filename, &sbuf);
3128  if (!ret) {
3129    ret=owl_function_askyesno("File exists, continue? [Y/n]");
3130    if (!ret) return;
3131  }
3132  */
3133
3134  file=fopen(filename, "w");
3135  if (!file) {
3136    owl_function_error("Error opening file");
3137    return;
3138  }
3139
3140  j=owl_view_get_size(v);
3141  for (i=0; i<j; i++) {
3142    m=owl_view_get_element(v, i);
3143    plaintext = owl_strip_format_chars(owl_message_get_text(m));
3144    if (plaintext) {
3145      fputs(plaintext, file);
3146      owl_free(plaintext);
3147    }
3148  }
3149  fclose(file);
3150  owl_function_makemsg("Messages dumped to %s", filename);
3151}
3152
3153void owl_function_do_newmsgproc(void)
3154{
3155  if (owl_global_get_newmsgproc(&g) && strcmp(owl_global_get_newmsgproc(&g), "")) {
3156    /* if there's a process out there, we need to check on it */
3157    if (owl_global_get_newmsgproc_pid(&g)) {
3158      owl_function_debugmsg("Checking on newmsgproc pid==%i", owl_global_get_newmsgproc_pid(&g));
3159      owl_function_debugmsg("Waitpid return is %i", waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG));
3160      waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG);
3161      if (waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG)==-1) {
3162        /* it exited */
3163        owl_global_set_newmsgproc_pid(&g, 0);
3164        owl_function_debugmsg("newmsgproc exited");
3165      } else {
3166        owl_function_debugmsg("newmsgproc did not exit");
3167      }
3168    }
3169   
3170    /* if it exited, fork & exec a new one */
3171    if (owl_global_get_newmsgproc_pid(&g)==0) {
3172      pid_t i;
3173      int myargc;
3174      i=fork();
3175      if (i) {
3176        /* parent set the child's pid */
3177        owl_global_set_newmsgproc_pid(&g, i);
3178        owl_function_debugmsg("I'm the parent and I started a new newmsgproc with pid %i", i);
3179      } else {
3180        /* child exec's the program */
3181        char **parsed;
3182        parsed=owl_parseline(owl_global_get_newmsgproc(&g), &myargc);
3183        if (myargc < 0) {
3184          owl_function_debugmsg("Could not parse newmsgproc '%s': unbalanced quotes?", owl_global_get_newmsgproc(&g));
3185        }
3186        if (myargc <= 0) {
3187          _exit(127);
3188        }
3189        parsed=owl_realloc(parsed, sizeof(*parsed) * (myargc+1));
3190        parsed[myargc] = NULL;
3191       
3192        owl_function_debugmsg("About to exec \"%s\" with %d arguments", parsed[0], myargc);
3193       
3194        execvp(parsed[0], parsed);
3195       
3196       
3197        /* was there an error exec'ing? */
3198        owl_function_debugmsg("Cannot run newmsgproc '%s': cannot exec '%s': %s", 
3199                              owl_global_get_newmsgproc(&g), parsed[0], strerror(errno));
3200        _exit(127);
3201      }
3202    }
3203  }
3204}
3205
3206/* print the xterm escape sequence to raise the window */
3207void owl_function_xterm_raise(void)
3208{
3209  printf("\033[5t");
3210}
3211
3212/* print the xterm escape sequence to deiconify the window */
3213void owl_function_xterm_deiconify(void)
3214{
3215  printf("\033[1t");
3216}
3217
3218/* Add the specified command to the startup file.  Eventually this
3219 * should be clever, and rewriting settings that will obviosly
3220 * override earlier settings with 'set' 'bindkey' and 'alias'
3221 * commands.  For now though we just remove any line that would
3222 * duplicate this one and then append this line to the end of
3223 * startupfile.
3224 */
3225void owl_function_addstartup(const char *buff)
3226{
3227  FILE *file;
3228  const char *filename;
3229
3230  filename=owl_global_get_startupfile(&g);
3231
3232  /* delete earlier copies */
3233  owl_util_file_deleteline(filename, buff, 1);
3234
3235  file=fopen(filename, "a");
3236  if (!file) {
3237    owl_function_error("Error opening startupfile for new command");
3238    return;
3239  }
3240
3241  /* add this line */
3242  fprintf(file, "%s\n", buff);
3243
3244  fclose(file);
3245}
3246
3247/* Remove the specified command from the startup file. */
3248void owl_function_delstartup(const char *buff)
3249{
3250  const char *filename;
3251  filename=owl_global_get_startupfile(&g);
3252  owl_util_file_deleteline(filename, buff, 1);
3253}
3254
3255/* Execute owl commands from the given filename.  If the filename
3256 * is NULL, use the default owl startup commands file.
3257 */
3258void owl_function_source(const char *filename)
3259{
3260  char *path;
3261  FILE *file;
3262  char *s = NULL;
3263  int fail_silent = 0;
3264
3265  if (!filename) {
3266    fail_silent = 1;
3267    path = owl_strdup(owl_global_get_startupfile(&g));
3268  } else {
3269    path = owl_util_makepath(filename);
3270  }
3271  file = fopen(path, "r");
3272  owl_free(path);
3273  if (!file) {
3274    if (!fail_silent) {
3275      owl_function_error("Error opening file: %s", filename);
3276    }
3277    return;
3278  }
3279  while (owl_getline_chomp(&s, file)) {
3280    if (s[0] == '\0' || s[0] == '#')
3281      continue;
3282    owl_function_command(s);
3283  }
3284
3285  owl_free(s);
3286  fclose(file);
3287}
3288
3289void owl_function_change_style(owl_view *v, const char *stylename)
3290{
3291  const owl_style *s;
3292
3293  s=owl_global_get_style_by_name(&g, stylename);
3294  if (!s) {
3295    owl_function_error("No style named %s", stylename);
3296    return;
3297  }
3298  owl_view_set_style(v, s);
3299  owl_messagelist_invalidate_formats(owl_global_get_msglist(&g));
3300  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
3301  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3302}
3303
3304void owl_function_toggleoneline(void)
3305{
3306  owl_view *v;
3307  const owl_style *s;
3308
3309  v=owl_global_get_current_view(&g);
3310  s=owl_view_get_style(v);
3311
3312  if (!owl_style_matches_name(s, "oneline")) {
3313    owl_function_change_style(v, "oneline");
3314  } else {
3315    owl_function_change_style(v, owl_global_get_default_style(&g));
3316  }
3317
3318  owl_messagelist_invalidate_formats(owl_global_get_msglist(&g));
3319  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
3320  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3321}
3322
3323void owl_function_error(const char *fmt, ...)
3324{
3325  static int in_error = 0;
3326  va_list ap;
3327  char *buff;
3328  const char *nl;
3329
3330  if (++in_error > 2) {
3331    /* More than two nested errors, bail immediately. */
3332    in_error--;
3333    return;
3334  }
3335
3336  va_start(ap, fmt);
3337  buff = g_strdup_vprintf(fmt, ap);
3338  va_end(ap);
3339
3340  owl_function_debugmsg("ERROR: %s", buff);
3341  owl_function_log_err(buff);
3342
3343  nl = strchr(buff, '\n');
3344
3345  /*
3346    Showing admin messages triggers a lot of code. If we have a
3347    recursive error call, that's the most likely candidate, so
3348    suppress the call in that case, to try to avoid infinite looping.
3349  */
3350
3351  if(nl && *(nl + 1) && in_error == 1) {
3352    /* Multiline error */
3353    owl_function_adminmsg("ERROR", buff);
3354  } else {
3355    owl_function_makemsg("[Error] %s", buff);
3356  }
3357
3358  owl_free(buff);
3359
3360  in_error--;
3361}
3362
3363void owl_function_log_err(const char *string)
3364{
3365  char *date;
3366  time_t now;
3367  char *buff;
3368
3369  now=time(NULL);
3370  date=owl_strdup(ctime(&now));
3371  date[strlen(date)-1]='\0';
3372
3373  buff = owl_sprintf("%s %s", date, string);
3374
3375  owl_errqueue_append_err(owl_global_get_errqueue(&g), buff);
3376
3377  owl_free(buff);
3378  owl_free(date);
3379}
3380
3381void owl_function_showerrs(void)
3382{
3383  owl_fmtext fm;
3384
3385  owl_fmtext_init_null(&fm);
3386  owl_fmtext_append_normal(&fm, "Errors:\n\n");
3387  owl_errqueue_to_fmtext(owl_global_get_errqueue(&g), &fm);
3388  owl_function_popless_fmtext(&fm);
3389}
3390
3391void owl_function_makemsg(const char *fmt, ...)
3392{
3393  va_list ap;
3394  char *str;
3395
3396  va_start(ap, fmt);
3397  str = g_strdup_vprintf(fmt, ap);
3398  va_end(ap);
3399
3400  owl_function_debugmsg("makemsg: %s", str);
3401  owl_msgwin_set_text(&g.msgwin, str);
3402}
3403
3404/* get locations for everyone in .anyone.  If 'notify' is '1' then
3405 * send a pseudo login or logout message for everyone not in sync with
3406 * the global zephyr buddy list.  The list is updated regardless of
3407 * the status of 'notify'.
3408 */
3409void owl_function_zephyr_buddy_check(int notify)
3410{
3411#ifdef HAVE_LIBZEPHYR
3412  int i, j;
3413  owl_list anyone;
3414  owl_zbuddylist *zbl;
3415  GList **zaldlist;
3416  GList *zaldptr;
3417  ZAsyncLocateData_t *zald;
3418  const char *user;
3419
3420  if (!owl_global_is_havezephyr(&g)) return;
3421  owl_global_set_pseudologin_notify(&g, notify);
3422  zbl = owl_global_get_zephyr_buddylist(&g);
3423  zaldlist = owl_global_get_zaldlist(&g);
3424
3425  /* Clear the existing ZALDs first. */
3426  zaldptr = g_list_first(*zaldlist);
3427  while (zaldptr) {
3428    ZFreeALD(zaldptr->data);
3429    owl_free(zaldptr->data);
3430    zaldptr = g_list_next(zaldptr);
3431  }
3432  g_list_free(*zaldlist);
3433  *zaldlist = NULL;
3434
3435  owl_list_create(&anyone);
3436  owl_zephyr_get_anyone_list(&anyone, NULL);
3437  j = owl_list_get_size(&anyone);
3438  for (i = 0; i < j; i++) {
3439    user = owl_list_get_element(&anyone, i);
3440    zald = owl_malloc(sizeof(ZAsyncLocateData_t));
3441    if (ZRequestLocations(zstr(user), zald, UNACKED, ZAUTH) == ZERR_NONE) {
3442      *zaldlist = g_list_append(*zaldlist, zald);
3443    } else {
3444      owl_free(zald);
3445    }
3446  }
3447
3448  owl_list_cleanup(&anyone, owl_free);
3449#endif
3450}
3451
3452void owl_function_aimsearch_results(const char *email, owl_list *namelist)
3453{
3454  owl_fmtext fm;
3455  int i, j;
3456
3457  owl_fmtext_init_null(&fm);
3458  owl_fmtext_append_normal(&fm, "AIM screennames associated with ");
3459  owl_fmtext_append_normal(&fm, email);
3460  owl_fmtext_append_normal(&fm, ":\n");
3461
3462  j=owl_list_get_size(namelist);
3463  for (i=0; i<j; i++) {
3464    owl_fmtext_append_normal(&fm, "  ");
3465    owl_fmtext_append_normal(&fm, owl_list_get_element(namelist, i));
3466    owl_fmtext_append_normal(&fm, "\n");
3467  }
3468
3469  owl_function_popless_fmtext(&fm);
3470  owl_fmtext_cleanup(&fm);
3471}
3472
3473int owl_function_get_color_count(void)
3474{
3475     return COLORS;
3476}
3477
3478void owl_function_mask_sigint(sigset_t *oldmask) {
3479  sigset_t intr;
3480
3481  sigemptyset(&intr);
3482  sigaddset(&intr, SIGINT);
3483  sigprocmask(SIG_BLOCK, &intr, oldmask);
3484}
3485
3486void owl_function_unmask_sigint(sigset_t *oldmask) {
3487  sigset_t intr;
3488
3489  sigemptyset(&intr);
3490  sigaddset(&intr, SIGINT);
3491  sigprocmask(SIG_UNBLOCK, &intr, oldmask);
3492}
3493
3494void _owl_function_mark_message(const owl_message *m)
3495{
3496  if (m) {
3497    owl_global_set_markedmsgid(&g, owl_message_get_id(m));
3498    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3499  }
3500}
3501
3502void owl_function_mark_message(void)
3503{
3504  const owl_message *m;
3505  const owl_view *v;
3506
3507  v=owl_global_get_current_view(&g);
3508
3509  /* bail if there's no current message */
3510  if (owl_view_get_size(v) < 1) {
3511    owl_function_error("No messages to mark");
3512    return;
3513  }
3514
3515  /* mark the message */
3516  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
3517  _owl_function_mark_message(m);
3518  owl_function_makemsg("Mark set");
3519}
3520
3521void owl_function_swap_cur_marked(void)
3522{
3523  int marked_id;
3524  const owl_message *m;
3525  const owl_view *v;
3526
3527  marked_id=owl_global_get_markedmsgid(&g);
3528  if (marked_id == -1) {
3529    owl_function_error("Mark not set.");
3530    return;
3531  }
3532
3533  v=owl_global_get_current_view(&g);
3534  /* bail if there's no current message */
3535  if (owl_view_get_size(v) < 1) {
3536    return;
3537  }
3538
3539  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
3540  _owl_function_mark_message(m);
3541  owl_global_set_curmsg(&g, owl_view_get_nearest_to_msgid(v, marked_id));
3542  owl_function_calculate_topmsg(OWL_DIRECTION_NONE);
3543  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3544  owl_global_set_direction_downwards(&g);
3545}
Note: See TracBrowser for help on using the repository browser.