source: functions.c @ e294783

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