source: functions.c @ 7a70e26

release-1.10release-1.7release-1.8release-1.9
Last change on this file since 7a70e26 was 4e33cb2, checked in by David Benjamin <davidben@mit.edu>, 14 years ago
Punt a lot of needrefreshes Move the actual needrefreshes into the functions actually dealing with curses. This will, of course, later get moved out of the paint events and no one will call it. But that's later.
  • Property mode set to 100644
File size: 95.7 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_typwin(&g));
1265  touchwin(owl_global_get_curs_msgwin(&g));
1266
1267  sepbar("");
1268  owl_function_makemsg("");
1269
1270  owl_global_set_needrefresh(&g);
1271}
1272
1273void owl_function_popless_text(const char *text)
1274{
1275  owl_popwin *pw;
1276  owl_viewwin *v;
1277
1278  pw=owl_global_get_popwin(&g);
1279  v=owl_global_get_viewwin(&g);
1280
1281  owl_popwin_up(pw);
1282  owl_global_push_context(&g, OWL_CTX_POPLESS, v, "popless");
1283  owl_viewwin_init_text(v, owl_popwin_get_content(pw), text);
1284}
1285
1286void owl_function_popless_fmtext(const owl_fmtext *fm)
1287{
1288  owl_popwin *pw;
1289  owl_viewwin *v;
1290
1291  pw=owl_global_get_popwin(&g);
1292  v=owl_global_get_viewwin(&g);
1293
1294  owl_popwin_up(pw);
1295  owl_global_push_context(&g, OWL_CTX_POPLESS, v, "popless");
1296  owl_viewwin_init_fmtext(v, owl_popwin_get_content(pw), fm);
1297}
1298
1299void owl_function_popless_file(const char *filename)
1300{
1301  owl_fmtext fm;
1302  FILE *file;
1303  char *s = NULL;
1304
1305  file=fopen(filename, "r");
1306  if (!file) {
1307    owl_function_error("Could not open file: %s", filename);
1308    return;
1309  }
1310
1311  owl_fmtext_init_null(&fm);
1312  while (owl_getline(&s, file))
1313    owl_fmtext_append_normal(&fm, s);
1314  owl_free(s);
1315
1316  owl_function_popless_fmtext(&fm);
1317  owl_fmtext_cleanup(&fm);
1318  fclose(file);
1319}
1320
1321void owl_function_about(void)
1322{
1323  owl_function_popless_text(
1324    "This is barnowl version " OWL_VERSION_STRING ".\n\n"
1325    "barnowl is a fork of the Owl zephyr client, written and\n"
1326    "maintained by Alejandro Sedeno and Nelson Elhage at the\n"
1327    "Massachusetts Institute of Technology. \n"
1328    "\n"
1329    "Owl was written by James Kretchmar. The first version, 0.5, was\n"
1330    "released in March 2002.\n"
1331    "\n"
1332    "The name 'owl' was chosen in reference to the owls in the\n"
1333    "Harry Potter novels, who are tasked with carrying messages\n"
1334    "between Witches and Wizards. The name 'barnowl' was chosen\n"
1335    "because we feel our owls should live closer to our ponies.\n"
1336    "\n"
1337    "Copyright (c) 2006-2010 The BarnOwl Developers. All rights reserved.\n"
1338    "Copyright (c) 2004 James Kretchmar. All rights reserved.\n"
1339    "Copyright 2002 Massachusetts Institute of Technology\n"
1340    "\n"
1341    "This program is free software. You can redistribute it and/or\n"
1342    "modify under the terms of the Sleepycat License. Use the \n"
1343    "':show license' command to display the full license\n"
1344  );
1345}
1346
1347void owl_function_info(void)
1348{
1349  const owl_message *m;
1350  owl_fmtext fm, attrfm;
1351  const owl_view *v;
1352#ifdef HAVE_LIBZEPHYR
1353  const ZNotice_t *n;
1354#endif
1355
1356  owl_fmtext_init_null(&fm);
1357 
1358  v=owl_global_get_current_view(&g);
1359  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1360  if (!m || owl_view_get_size(v)==0) {
1361    owl_function_error("No message selected\n");
1362    return;
1363  }
1364
1365  owl_fmtext_append_bold(&fm, "General Information:\n");
1366  owl_fmtext_appendf_normal(&fm, "  Msg Id    : %i\n", owl_message_get_id(m));
1367
1368  owl_fmtext_append_normal(&fm, "  Type      : ");
1369  owl_fmtext_append_bold(&fm, owl_message_get_type(m));
1370  owl_fmtext_append_normal(&fm, "\n");
1371
1372  if (owl_message_is_direction_in(m)) {
1373    owl_fmtext_append_normal(&fm, "  Direction : in\n");
1374  } else if (owl_message_is_direction_out(m)) {
1375    owl_fmtext_append_normal(&fm, "  Direction : out\n");
1376  } else if (owl_message_is_direction_none(m)) {
1377    owl_fmtext_append_normal(&fm, "  Direction : none\n");
1378  } else {
1379    owl_fmtext_append_normal(&fm, "  Direction : unknown\n");
1380  }
1381
1382  owl_fmtext_appendf_normal(&fm, "  Time      : %s\n", owl_message_get_timestr(m));
1383
1384  if (!owl_message_is_type_admin(m)) {
1385    owl_fmtext_appendf_normal(&fm, "  Sender    : %s\n", owl_message_get_sender(m));
1386    owl_fmtext_appendf_normal(&fm, "  Recipient : %s\n", owl_message_get_recipient(m));
1387  }
1388
1389  if (owl_message_is_type_zephyr(m)) {
1390    owl_fmtext_append_bold(&fm, "\nZephyr Specific Information:\n");
1391   
1392    owl_fmtext_appendf_normal(&fm, "  Class     : %s\n", owl_message_get_class(m));
1393    owl_fmtext_appendf_normal(&fm, "  Instance  : %s\n", owl_message_get_instance(m));
1394    owl_fmtext_appendf_normal(&fm, "  Opcode    : %s\n", owl_message_get_opcode(m));
1395#ifdef HAVE_LIBZEPHYR
1396    if (owl_message_is_direction_in(m)) {
1397      char *ptr, tmpbuff[1024];
1398      int i, j, fields, len;
1399
1400      n=owl_message_get_notice(m);
1401
1402      if (!owl_message_is_pseudo(m)) {
1403        owl_fmtext_append_normal(&fm, "  Kind      : ");
1404        if (n->z_kind==UNSAFE) {
1405          owl_fmtext_append_normal(&fm, "UNSAFE\n");
1406        } else if (n->z_kind==UNACKED) {
1407          owl_fmtext_append_normal(&fm, "UNACKED\n");
1408        } else if (n->z_kind==ACKED) {
1409          owl_fmtext_append_normal(&fm, "ACKED\n");
1410        } else if (n->z_kind==HMACK) {
1411          owl_fmtext_append_normal(&fm, "HMACK\n");
1412        } else if (n->z_kind==HMCTL) {
1413          owl_fmtext_append_normal(&fm, "HMCTL\n");
1414        } else if (n->z_kind==SERVACK) {
1415          owl_fmtext_append_normal(&fm, "SERVACK\n");
1416        } else if (n->z_kind==SERVNAK) {
1417          owl_fmtext_append_normal(&fm, "SERVNACK\n");
1418        } else if (n->z_kind==CLIENTACK) {
1419          owl_fmtext_append_normal(&fm, "CLIENTACK\n");
1420        } else if (n->z_kind==STAT) {
1421          owl_fmtext_append_normal(&fm, "STAT\n");
1422        } else {
1423          owl_fmtext_append_normal(&fm, "ILLEGAL VALUE\n");
1424        }
1425      }
1426      owl_fmtext_appendf_normal(&fm, "  Host      : %s\n", owl_message_get_hostname(m));
1427
1428      if (!owl_message_is_pseudo(m)) {
1429        owl_fmtext_append_normal(&fm, "\n");
1430        owl_fmtext_appendf_normal(&fm, "  Port      : %i\n", ntohs(n->z_port));
1431        owl_fmtext_appendf_normal(&fm, "  Auth      : %s\n", owl_zephyr_get_authstr(n));
1432
1433        /* FIXME make these more descriptive */
1434        owl_fmtext_appendf_normal(&fm, "  Checkd Ath: %i\n", n->z_checked_auth);
1435        owl_fmtext_appendf_normal(&fm, "  Multi notc: %s\n", n->z_multinotice);
1436        owl_fmtext_appendf_normal(&fm, "  Num other : %i\n", n->z_num_other_fields);
1437        owl_fmtext_appendf_normal(&fm, "  Msg Len   : %i\n", n->z_message_len);
1438
1439        fields=owl_zephyr_get_num_fields(n);
1440        owl_fmtext_appendf_normal(&fm, "  Fields    : %i\n", fields);
1441
1442        for (i=0; i<fields; i++) {
1443          ptr=owl_zephyr_get_field_as_utf8(n, i+1);
1444          len=strlen(ptr);
1445          if (len<30) {
1446            strncpy(tmpbuff, ptr, len);
1447            tmpbuff[len]='\0';
1448          } else {
1449            strncpy(tmpbuff, ptr, 30);
1450            tmpbuff[30]='\0';
1451            strcat(tmpbuff, "...");
1452          }
1453          owl_free(ptr);
1454
1455          for (j=0; j<strlen(tmpbuff); j++) {
1456            if (tmpbuff[j]=='\n') tmpbuff[j]='~';
1457            if (tmpbuff[j]=='\r') tmpbuff[j]='!';
1458          }
1459
1460          owl_fmtext_appendf_normal(&fm, "  Field %i   : %s\n", i+1, tmpbuff);
1461        }
1462        owl_fmtext_appendf_normal(&fm, "  Default Fm: %s\n", n->z_default_format);
1463      }
1464
1465    }
1466#endif
1467  }
1468
1469  owl_fmtext_append_bold(&fm, "\nOwl Message Attributes:\n");
1470  owl_message_attributes_tofmtext(m, &attrfm);
1471  owl_fmtext_append_fmtext(&fm, &attrfm);
1472 
1473  owl_function_popless_fmtext(&fm);
1474  owl_fmtext_cleanup(&fm);
1475  owl_fmtext_cleanup(&attrfm);
1476}
1477
1478/* print the current message in a popup window.
1479 * Use the 'default' style regardless of whatever
1480 * style the user may be using
1481 */
1482void owl_function_curmsg_to_popwin(void)
1483{
1484  const owl_view *v;
1485  const owl_message *m;
1486  const owl_style *s;
1487  owl_fmtext fm;
1488
1489  v=owl_global_get_current_view(&g);
1490  s=owl_global_get_style_by_name(&g, "default");
1491
1492  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1493
1494  if (!m || owl_view_get_size(v)==0) {
1495    owl_function_error("No current message");
1496    return;
1497  }
1498
1499  owl_fmtext_init_null(&fm);
1500  owl_style_get_formattext(s, &fm, m);
1501
1502  owl_function_popless_fmtext(&fm);
1503  owl_fmtext_cleanup(&fm);
1504}
1505
1506void owl_function_page_curmsg(int step)
1507{
1508  /* scroll down or up within the current message IF the message is truncated */
1509
1510  int offset, curmsg, lines;
1511  const owl_view *v;
1512  owl_message *m;
1513
1514  offset=owl_global_get_curmsg_vert_offset(&g);
1515  v=owl_global_get_current_view(&g);
1516  curmsg=owl_global_get_curmsg(&g);
1517  m=owl_view_get_element(v, curmsg);
1518  if (!m || owl_view_get_size(v)==0) return;
1519  lines=owl_message_get_numlines(m);
1520
1521  if (offset==0) {
1522    /* Bail if the curmsg isn't the last one displayed */
1523    if (curmsg != owl_mainwin_get_last_msg(owl_global_get_mainwin(&g))) {
1524      owl_function_makemsg("The entire message is already displayed");
1525      return;
1526    }
1527   
1528    /* Bail if we're not truncated */
1529    if (!owl_mainwin_is_curmsg_truncated(owl_global_get_mainwin(&g))) {
1530      owl_function_makemsg("The entire message is already displayed");
1531      return;
1532    }
1533  }
1534 
1535 
1536  /* don't scroll past the last line */
1537  if (step>0) {
1538    if (offset+step > lines-1) {
1539      owl_global_set_curmsg_vert_offset(&g, lines-1);
1540    } else {
1541      owl_global_set_curmsg_vert_offset(&g, offset+step);
1542    }
1543  }
1544
1545  /* would we be before the beginning of the message? */
1546  if (step<0) {
1547    if (offset+step<0) {
1548      owl_global_set_curmsg_vert_offset(&g, 0);
1549    } else {
1550      owl_global_set_curmsg_vert_offset(&g, offset+step);
1551    }
1552  }
1553 
1554  /* redisplay */
1555  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1556}
1557
1558void owl_function_resize_typwin(int newsize)
1559{
1560  owl_global_set_typwin_lines(&g, newsize);
1561  owl_global_set_relayout_pending(&g);
1562}
1563
1564void owl_function_mainwin_pagedown(void)
1565{
1566  int i;
1567
1568  i=owl_mainwin_get_last_msg(owl_global_get_mainwin(&g));
1569  if (i<0) return;
1570  if (owl_mainwin_is_last_msg_truncated(owl_global_get_mainwin(&g))
1571      && (owl_global_get_curmsg(&g) < i)
1572      && (i>0)) {
1573    i--;
1574  }
1575  owl_global_set_curmsg(&g, i);
1576  owl_function_nextmsg();
1577}
1578
1579void owl_function_mainwin_pageup(void)
1580{
1581  owl_global_set_curmsg(&g, owl_global_get_topmsg(&g));
1582  owl_function_prevmsg();
1583}
1584
1585void owl_function_getsubs(void)
1586{
1587  char *buff;
1588
1589  buff=owl_zephyr_getsubs();
1590
1591  if (buff) {
1592    owl_function_popless_text(buff);
1593  } else {
1594    owl_function_popless_text("Error getting subscriptions");
1595  }
1596           
1597  owl_free(buff);
1598}
1599
1600void owl_function_printallvars(void)
1601{
1602  const char *name;
1603  char var[LINE];
1604  owl_list varnames;
1605  int i, numvarnames;
1606  GString *str   = g_string_new("");
1607
1608  g_string_append_printf(str, "%-20s = %s\n", "VARIABLE", "VALUE");
1609  g_string_append_printf(str, "%-20s   %s\n",  "--------", "-----");
1610  owl_variable_dict_get_names(owl_global_get_vardict(&g), &varnames);
1611  numvarnames = owl_list_get_size(&varnames);
1612  for (i=0; i<numvarnames; i++) {
1613    name = owl_list_get_element(&varnames, i);
1614    if (name && name[0]!='_') {
1615      g_string_append_printf(str, "\n%-20s = ", name);
1616      owl_variable_get_tostring(owl_global_get_vardict(&g), name, var, LINE);
1617      g_string_append(str, var);
1618    }
1619  }
1620  g_string_append(str, "\n");
1621  owl_variable_dict_namelist_cleanup(&varnames);
1622
1623  owl_function_popless_text(str->str);
1624  g_string_free(str, TRUE);
1625}
1626
1627void owl_function_show_variables(void)
1628{
1629  owl_list varnames;
1630  owl_fmtext fm; 
1631  int i, numvarnames;
1632  const char *varname;
1633
1634  owl_fmtext_init_null(&fm);
1635  owl_fmtext_append_bold(&fm, 
1636      "Variables: (use 'show variable <name>' for details)\n");
1637  owl_variable_dict_get_names(owl_global_get_vardict(&g), &varnames);
1638  numvarnames = owl_list_get_size(&varnames);
1639  for (i=0; i<numvarnames; i++) {
1640    varname = owl_list_get_element(&varnames, i);
1641    if (varname && varname[0]!='_') {
1642      owl_variable_describe(owl_global_get_vardict(&g), varname, &fm);
1643    }
1644  }
1645  owl_variable_dict_namelist_cleanup(&varnames);
1646  owl_function_popless_fmtext(&fm);
1647  owl_fmtext_cleanup(&fm);
1648}
1649
1650void owl_function_show_variable(const char *name)
1651{
1652  owl_fmtext fm; 
1653
1654  owl_fmtext_init_null(&fm);
1655  owl_variable_get_help(owl_global_get_vardict(&g), name, &fm);
1656  owl_function_popless_fmtext(&fm);
1657  owl_fmtext_cleanup(&fm);
1658}
1659
1660/* note: this applies to global message list, not to view.
1661 * If flag is 1, deletes.  If flag is 0, undeletes. */
1662void owl_function_delete_by_id(int id, int flag)
1663{
1664  const owl_messagelist *ml;
1665  owl_message *m;
1666  ml = owl_global_get_msglist(&g);
1667  m = owl_messagelist_get_by_id(ml, id);
1668  if (m) {
1669    if (flag == 1) {
1670      owl_message_mark_delete(m);
1671    } else if (flag == 0) {
1672      owl_message_unmark_delete(m);
1673    }
1674    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1675  } else {
1676    owl_function_error("No message with id %d: unable to mark for (un)delete",id);
1677  }
1678}
1679
1680void owl_function_delete_automsgs(void)
1681{
1682  /* mark for deletion all messages in the current view that match the
1683   * 'trash' filter */
1684
1685  int i, j, count;
1686  owl_message *m;
1687  const owl_view *v;
1688  const owl_filter *f;
1689
1690  /* get the trash filter */
1691  f=owl_global_get_filter(&g, "trash");
1692  if (!f) {
1693    owl_function_error("No trash filter defined");
1694    return;
1695  }
1696
1697  v=owl_global_get_current_view(&g);
1698
1699  count=0;
1700  j=owl_view_get_size(v);
1701  for (i=0; i<j; i++) {
1702    m=owl_view_get_element(v, i);
1703    if (owl_filter_message_match(f, m)) {
1704      count++;
1705      owl_message_mark_delete(m);
1706    }
1707  }
1708  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1709  owl_function_makemsg("%i messages marked for deletion", count);
1710}
1711
1712void owl_function_status(void)
1713{
1714  char buff[MAXPATHLEN+1];
1715  time_t start;
1716  int up, days, hours, minutes;
1717  owl_fmtext fm;
1718
1719  owl_fmtext_init_null(&fm);
1720
1721  start=owl_global_get_starttime(&g);
1722
1723  owl_fmtext_append_normal(&fm, "General Information:\n");
1724
1725  owl_fmtext_append_normal(&fm, "  Version: ");
1726  owl_fmtext_append_normal(&fm, OWL_VERSION_STRING);
1727  owl_fmtext_append_normal(&fm, "\n");
1728
1729  owl_fmtext_append_normal(&fm, "  Startup Arguments: ");
1730  owl_fmtext_append_normal(&fm, owl_global_get_startupargs(&g));
1731  owl_fmtext_append_normal(&fm, "\n");
1732
1733  owl_fmtext_append_normal(&fm, "  Current Directory: ");
1734  if(getcwd(buff, MAXPATHLEN) == NULL) {
1735    owl_fmtext_append_normal(&fm, "<Error in getcwd>");
1736  } else {
1737    owl_fmtext_append_normal(&fm, buff);
1738  }
1739  owl_fmtext_append_normal(&fm, "\n");
1740
1741  owl_fmtext_appendf_normal(&fm, "  Startup Time: %s", ctime(&start));
1742
1743  up=owl_global_get_runtime(&g);
1744  days=up/86400;
1745  up-=days*86400;
1746  hours=up/3600;
1747  up-=hours*3600;
1748  minutes=up/60;
1749  up-=minutes*60;
1750  owl_fmtext_appendf_normal(&fm, "  Run Time: %i days %2.2i:%2.2i:%2.2i\n", days, hours, minutes, up);
1751
1752  owl_fmtext_append_normal(&fm, "\nProtocol Options:\n");
1753  owl_fmtext_append_normal(&fm, "  Zephyr included    : ");
1754  if (owl_global_is_havezephyr(&g)) {
1755    owl_fmtext_append_normal(&fm, "yes\n");
1756  } else {
1757    owl_fmtext_append_normal(&fm, "no\n");
1758  }
1759  owl_fmtext_append_normal(&fm, "  AIM included       : yes\n");
1760  owl_fmtext_append_normal(&fm, "  Loopback included  : yes\n");
1761
1762
1763  owl_fmtext_append_normal(&fm, "\nBuild Options:\n");
1764  owl_fmtext_append_normal(&fm, "  Stderr redirection : ");
1765#if OWL_STDERR_REDIR
1766  owl_fmtext_append_normal(&fm, "yes\n");
1767#else
1768  owl_fmtext_append_normal(&fm, "no\n");
1769#endif
1770 
1771
1772  owl_fmtext_append_normal(&fm, "\nAIM Status:\n");
1773  owl_fmtext_append_normal(&fm, "  Logged in: ");
1774  if (owl_global_is_aimloggedin(&g)) {
1775    owl_fmtext_append_normal(&fm, owl_global_get_aim_screenname(&g));
1776    owl_fmtext_append_normal(&fm, "\n");
1777  } else {
1778    owl_fmtext_append_normal(&fm, "(not logged in)\n");
1779  }
1780
1781  owl_fmtext_append_normal(&fm, "  Processing events: ");
1782  if (owl_global_is_doaimevents(&g)) {
1783    owl_fmtext_append_normal(&fm, "yes\n");
1784  } else {
1785    owl_fmtext_append_normal(&fm, "no\n");
1786  }
1787
1788  owl_function_popless_fmtext(&fm);
1789  owl_fmtext_cleanup(&fm);
1790}
1791
1792void owl_function_show_term(void)
1793{
1794  owl_fmtext fm;
1795
1796  owl_fmtext_init_null(&fm);
1797  owl_fmtext_appendf_normal(&fm, "Terminal Lines: %i\nTerminal Columns: %i\n",
1798          owl_global_get_lines(&g),
1799          owl_global_get_cols(&g));
1800
1801  if (owl_global_get_hascolors(&g)) {
1802    owl_fmtext_append_normal(&fm, "Color: Yes\n");
1803    owl_fmtext_appendf_normal(&fm, "Number of color pairs: %i\n", owl_global_get_colorpairs(&g));
1804    owl_fmtext_appendf_normal(&fm, "Can change colors: %s\n", can_change_color() ? "yes" : "no");
1805  } else {
1806    owl_fmtext_append_normal(&fm, "Color: No\n");
1807  }
1808
1809  owl_function_popless_fmtext(&fm);
1810  owl_fmtext_cleanup(&fm);
1811}
1812
1813/* if type = 0 then normal reply.
1814 * if type = 1 then it's a reply to sender
1815 * if enter = 0 then allow the command to be edited
1816 * if enter = 1 then don't wait for editing
1817 */
1818void owl_function_reply(int type, int enter)
1819{
1820  char *buff=NULL;
1821  const owl_message *m;
1822  const owl_filter *f;
1823 
1824  if (owl_view_get_size(owl_global_get_current_view(&g))==0) {
1825    owl_function_error("No message selected");
1826  } else {
1827    char *cmd;
1828   
1829    m=owl_view_get_element(owl_global_get_current_view(&g), owl_global_get_curmsg(&g));
1830    if (!m) {
1831      owl_function_error("No message selected");
1832      return;
1833    }
1834
1835    /* first check if we catch the reply-lockout filter */
1836    f=owl_global_get_filter(&g, "reply-lockout");
1837    if (f) {
1838      if (owl_filter_message_match(f, m)) {
1839        owl_function_error("Sorry, replies to this message have been disabled by the reply-lockout filter");
1840        return;
1841      }
1842    }
1843
1844    /* then check if it's a question and just bring up the command prompt */
1845    if (owl_message_is_question(m)) {
1846      owl_function_start_command("");
1847      return;
1848    }
1849
1850    if((type == 0 &&
1851        (cmd=owl_perlconfig_message_call_method(m, "replycmd", 0, NULL))) ||
1852       (type == 1 &&
1853        (cmd=owl_perlconfig_message_call_method(m, "replysendercmd", 0, NULL)))) {
1854      buff = cmd;
1855    }
1856
1857    if(!buff) {
1858        owl_function_error("I don't know how to reply to that message.");
1859        return;
1860    }
1861
1862    if (enter) {
1863      owl_history *hist = owl_global_get_cmd_history(&g);
1864      owl_history_store(hist, buff);
1865      owl_history_reset(hist);
1866      owl_function_command_norv(buff);
1867    } else {
1868      owl_function_start_command(buff);
1869    }
1870    owl_free(buff);
1871  }
1872}
1873
1874void owl_function_zlocate(int argc, const char *const *argv, int auth)
1875{
1876  owl_fmtext fm;
1877  char *ptr;
1878  char *result;
1879  int i;
1880
1881  owl_fmtext_init_null(&fm);
1882
1883  for (i=0; i<argc; i++) {
1884    ptr = long_zuser(argv[i]);
1885    result = owl_zephyr_zlocate(ptr, auth);
1886    owl_fmtext_append_normal(&fm, result);
1887    owl_free(result);
1888    owl_free(ptr);
1889  }
1890
1891  owl_function_popless_fmtext(&fm);
1892  owl_fmtext_cleanup(&fm);
1893}
1894
1895void owl_callback_command(owl_editwin *e)
1896{
1897  char *rv;
1898  const char *line = owl_editwin_get_text(e);
1899
1900  rv = owl_function_command(line);
1901   if (rv) {
1902    owl_function_makemsg("%s", rv);
1903    owl_free(rv);
1904  }
1905}
1906
1907void owl_function_start_command(const char *line)
1908{
1909  owl_editwin *tw;
1910
1911  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
1912
1913  owl_editwin_set_locktext(tw, "command: ");
1914
1915  owl_editwin_insert_string(tw, line);
1916  owl_editwin_redisplay(tw);
1917
1918  owl_global_push_context(&g, OWL_CTX_EDITLINE, tw, "editline");
1919  owl_editwin_set_callback(tw, owl_callback_command);
1920}
1921
1922owl_editwin *owl_function_start_question(const char *line)
1923{
1924  owl_editwin *tw;
1925
1926  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
1927
1928  owl_editwin_set_locktext(tw, line);
1929
1930  owl_global_push_context(&g, OWL_CTX_EDITRESPONSE, tw, "editresponse");
1931  return tw;
1932}
1933
1934owl_editwin *owl_function_start_password(const char *line)
1935{
1936  owl_editwin *tw;
1937
1938  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, NULL);
1939
1940  owl_editwin_set_echochar(tw, '*');
1941
1942  owl_editwin_set_locktext(tw, line);
1943
1944  owl_editwin_redisplay(tw);
1945
1946  owl_global_push_context(&g, OWL_CTX_EDITRESPONSE, tw, "editresponse");
1947  return tw;
1948}
1949
1950char *owl_function_exec(int argc, const char *const *argv, const char *buff, int type)
1951{
1952  /* if type == 1 display in a popup
1953   * if type == 2 display an admin messages
1954   * if type == 0 return output
1955   * else display in a popup
1956   */
1957  const char *redirect = " 2>&1 < /dev/null";
1958  char *newbuff;
1959  char *out;
1960  FILE *p;
1961
1962#if OWL_STDERR_REDIR
1963  redirect = " < /dev/null";
1964#endif
1965
1966  if (argc<2) {
1967    owl_function_error("Wrong number of arguments to the exec command");
1968    return NULL;
1969  }
1970
1971  buff = skiptokens(buff, 1);
1972  newbuff = owl_sprintf("%s%s", buff, redirect);
1973
1974  if (type == OWL_OUTPUT_POPUP) {
1975    owl_popexec_new(newbuff);
1976  } else {
1977    p = popen(newbuff, "r");
1978    out = owl_slurp(p);
1979    pclose(p);
1980   
1981    if (type == OWL_OUTPUT_RETURN) {
1982      owl_free(newbuff);
1983      return out;
1984    } else if (type == OWL_OUTPUT_ADMINMSG) {
1985      owl_function_adminmsg(buff, out);
1986    }
1987    owl_free(out);
1988  }
1989  owl_free(newbuff);
1990  return NULL;
1991}
1992
1993char *owl_function_perl(int argc, const char *const *argv, const char *buff, int type)
1994{
1995  /* if type == 1 display in a popup
1996   * if type == 2 display an admin messages
1997   * if type == 0 return output
1998   * else display in a popup
1999   */
2000  char *perlout;
2001
2002  if (argc<2) {
2003    owl_function_error("Wrong number of arguments to perl command");
2004    return NULL;
2005  }
2006
2007  /* consume first token (argv[0]) */
2008  buff = skiptokens(buff, 1);
2009
2010  perlout = owl_perlconfig_execute(buff);
2011  if (perlout) { 
2012    if (type == OWL_OUTPUT_POPUP) {
2013      owl_function_popless_text(perlout);
2014    } else if (type == OWL_OUTPUT_ADMINMSG) {
2015      owl_function_adminmsg(buff, perlout);
2016    } else if (type == OWL_OUTPUT_RETURN) {
2017      return perlout;
2018    }
2019    owl_free(perlout);
2020  }
2021  return NULL;
2022}
2023
2024/* Change the filter associated with the current view.
2025 * This also figures out which message in the new filter
2026 * should have the pointer.
2027 */
2028void owl_function_change_currentview_filter(const char *filtname)
2029{
2030  owl_view *v;
2031  owl_filter *f;
2032  int curid=-1, newpos, curmsg;
2033  const owl_message *curm=NULL;
2034
2035  v=owl_global_get_current_view(&g);
2036
2037  curmsg=owl_global_get_curmsg(&g);
2038  if (curmsg==-1) {
2039    owl_function_debugmsg("Hit the curmsg==-1 case in change_view");
2040  } else {
2041    curm=owl_view_get_element(v, curmsg);
2042    if (curm) {
2043      curid=owl_message_get_id(curm);
2044      owl_view_save_curmsgid(v, curid);
2045    }
2046  }
2047
2048  f=owl_global_get_filter(&g, filtname);
2049  if (!f) {
2050    owl_function_error("Unknown filter %s", filtname);
2051    return;
2052  }
2053
2054  owl_view_new_filter(v, f);
2055
2056  /* Figure out what to set the current message to.
2057   * - If the view we're leaving has messages in it, go to the closest message
2058   *   to the last message pointed to in that view.
2059   * - If the view we're leaving is empty, try to restore the position
2060   *   from the last time we were in the new view.  */
2061  if (curm) {
2062    newpos = owl_view_get_nearest_to_msgid(v, curid);
2063  } else {
2064    newpos = owl_view_get_nearest_to_saved(v);
2065  }
2066
2067  owl_global_set_curmsg(&g, newpos);
2068  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
2069  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2070  owl_global_set_direction_downwards(&g);
2071}
2072
2073/* Create a new filter, or replace an existing one
2074 * with a new definition.
2075 */
2076void owl_function_create_filter(int argc, const char *const *argv)
2077{
2078  owl_filter *f;
2079  const owl_view *v;
2080  int inuse = 0;
2081
2082  if (argc < 2) {
2083    owl_function_error("Wrong number of arguments to filter command");
2084    return;
2085  }
2086
2087  owl_function_debugmsg("owl_function_create_filter: starting to create filter named %s", argv[1]);
2088
2089  v=owl_global_get_current_view(&g);
2090
2091  /* don't touch the all filter */
2092  if (!strcmp(argv[1], "all")) {
2093    owl_function_error("You may not change the 'all' filter.");
2094    return;
2095  }
2096
2097  /* deal with the case of trying change the filter color */
2098  if (argc==4 && !strcmp(argv[2], "-c")) {
2099    f=owl_global_get_filter(&g, argv[1]);
2100    if (!f) {
2101      owl_function_error("The filter '%s' does not exist.", argv[1]);
2102      return;
2103    }
2104    if (owl_util_string_to_color(argv[3])==OWL_COLOR_INVALID) {
2105      owl_function_error("The color '%s' is not available.", argv[3]);
2106      return;
2107    }
2108    owl_filter_set_fgcolor(f, owl_util_string_to_color(argv[3]));
2109    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2110    return;
2111  }
2112  if (argc==4 && !strcmp(argv[2], "-b")) {
2113    f=owl_global_get_filter(&g, argv[1]);
2114    if (!f) {
2115      owl_function_error("The filter '%s' does not exist.", argv[1]);
2116      return;
2117    }
2118    if (owl_util_string_to_color(argv[3])==OWL_COLOR_INVALID) {
2119      owl_function_error("The color '%s' is not available.", argv[3]);
2120      return;
2121    }
2122    owl_filter_set_bgcolor(f, owl_util_string_to_color(argv[3]));
2123    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2124    return;
2125  }
2126
2127  /* create the filter and check for errors */
2128  f = owl_filter_new(argv[1], argc-2, argv+2);
2129  if (f == NULL) {
2130    owl_function_error("Invalid filter");
2131    return;
2132  }
2133
2134  /* if the named filter is in use by the current view, remember it */
2135  if (!strcmp(owl_view_get_filtname(v), argv[1])) {
2136    inuse=1;
2137  }
2138
2139  /* if the named filter already exists, nuke it */
2140  if (owl_global_get_filter(&g, argv[1])) {
2141    owl_global_remove_filter(&g, argv[1]);
2142  }
2143
2144  /* add the filter */
2145  owl_global_add_filter(&g, f);
2146
2147  /* if it was in use by the current view then update */
2148  if (inuse) {
2149    owl_function_change_currentview_filter(argv[1]);
2150  }
2151  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2152}
2153
2154/* If 'filtername' does not start with 'not-' create a filter named
2155 * 'not-<filtername>' defined as "not filter <filtername>".  If the
2156 * filter 'not-<filtername>' already exists, do not overwrite it.  If
2157 * 'filtername' begins with 'not-' and a filter 'filtername' already
2158 * exists, then do nothing.  If the filter 'filtername' does not
2159 * exist, create it and define it as 'not filter <filtername>'
2160 *
2161 * Returns the name of the negated filter, which the caller must free.
2162 */
2163char *owl_function_create_negative_filter(const char *filtername)
2164{
2165  char *newname;
2166  const owl_filter *tmpfilt;
2167  const char *argv[5];
2168
2169  owl_function_debugmsg("owl_function_create_negative_filter");
2170 
2171  if (!strncmp(filtername, "not-", 4)) {
2172    newname=owl_strdup(filtername+4);
2173  } else {
2174    newname=owl_sprintf("not-%s", filtername);
2175  }
2176
2177  tmpfilt=owl_global_get_filter(&g, newname);
2178  if (!tmpfilt) {
2179    argv[0]="filter"; /* anything is fine here */
2180    argv[1]=newname;
2181    argv[2]="not";
2182    argv[3]="filter";
2183    argv[4]=filtername;
2184    owl_function_create_filter(5, argv);
2185  }
2186
2187  owl_function_debugmsg("owl_function_create_negative_filter: returning with %s", newname);
2188  return(newname);
2189}
2190
2191void owl_function_show_filters(void)
2192{
2193  const owl_filter *f;
2194  GList *fl;
2195  owl_fmtext fm;
2196
2197  owl_fmtext_init_null(&fm);
2198
2199  owl_fmtext_append_bold(&fm, "Filters:\n");
2200
2201  for (fl = g.filterlist; fl; fl = g_list_next(fl)) {
2202    f = fl->data;
2203    owl_fmtext_append_normal(&fm, "   ");
2204    if (owl_global_get_hascolors(&g)) {
2205      owl_fmtext_append_normal_color(&fm, owl_filter_get_name(f), owl_filter_get_fgcolor(f), owl_filter_get_bgcolor(f));
2206    } else {
2207      owl_fmtext_append_normal(&fm, owl_filter_get_name(f));
2208    }
2209    owl_fmtext_append_normal(&fm, "\n");
2210  }
2211  owl_function_popless_fmtext(&fm);
2212  owl_fmtext_cleanup(&fm);
2213}
2214
2215void owl_function_show_filter(const char *name)
2216{
2217  const owl_filter *f;
2218  char *buff, *tmp;
2219
2220  f=owl_global_get_filter(&g, name);
2221  if (!f) {
2222    owl_function_error("There is no filter named %s", name);
2223    return;
2224  }
2225  tmp = owl_filter_print(f);
2226  buff = owl_sprintf("%s: %s", owl_filter_get_name(f), tmp);
2227  owl_function_popless_text(buff);
2228  owl_free(buff);
2229  owl_free(tmp);
2230}
2231
2232void owl_function_show_zpunts(void)
2233{
2234  const owl_filter *f;
2235  const owl_list *fl;
2236  char buff[5000];
2237  char *tmp;
2238  owl_fmtext fm;
2239  int i, j;
2240
2241  owl_fmtext_init_null(&fm);
2242
2243  fl=owl_global_get_puntlist(&g);
2244  j=owl_list_get_size(fl);
2245  owl_fmtext_append_bold(&fm, "Active zpunt filters:\n");
2246
2247  for (i=0; i<j; i++) {
2248    f=owl_list_get_element(fl, i);
2249    snprintf(buff, sizeof(buff), "[% 2d] ", i+1);
2250    owl_fmtext_append_normal(&fm, buff);
2251    tmp = owl_filter_print(f);
2252    owl_fmtext_append_normal(&fm, tmp);
2253    owl_free(tmp);
2254  }
2255  owl_function_popless_fmtext(&fm);
2256  owl_fmtext_cleanup(&fm);
2257}
2258
2259/* Create a filter for a class, instance if one doesn't exist.  If
2260 * instance is NULL then catch all messgaes in the class.  Returns the
2261 * name of the filter, which the caller must free.
2262 * If 'related' is nonzero, encompass unclasses and .d classes as well.
2263 */
2264char *owl_function_classinstfilt(const char *c, const char *i, int related) 
2265{
2266  owl_filter *f;
2267  char *argbuff, *filtname;
2268  char *tmpclass, *tmpinstance = NULL;
2269  char *class, *instance = NULL;
2270
2271  if (related) {
2272    class = owl_util_baseclass(c);
2273    if (i) {
2274      instance = owl_util_baseclass(i);
2275    }
2276  } else {
2277    class = owl_strdup(c);
2278    if (i) {
2279      instance = owl_strdup(i);
2280    }
2281  }
2282
2283  /* name for the filter */
2284  if (!instance) {
2285    filtname = owl_sprintf("%sclass-%s", related ? "related-" : "", class);
2286  } else {
2287    filtname = owl_sprintf("%sclass-%s-instance-%s", related ? "related-" : "", class, instance);
2288  }
2289  /* downcase it */
2290  {
2291    char *temp = g_utf8_strdown(filtname, -1);
2292    if (temp) {
2293      owl_free(filtname);
2294      filtname = temp;
2295    }
2296  }
2297  /* turn spaces, single quotes, and double quotes into dots */
2298  owl_text_tr(filtname, ' ', '.');
2299  owl_text_tr(filtname, '\'', '.');
2300  owl_text_tr(filtname, '"', '.');
2301 
2302  /* if it already exists then go with it.  This lets users override */
2303  if (owl_global_get_filter(&g, filtname)) {
2304    goto done;
2305  }
2306
2307  /* create the new filter */
2308  tmpclass=owl_text_quote(class, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2309  owl_text_tr(tmpclass, ' ', '.');
2310  owl_text_tr(tmpclass, '\'', '.');
2311  owl_text_tr(tmpclass, '"', '.');
2312  if (instance) {
2313    tmpinstance=owl_text_quote(instance, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2314    owl_text_tr(tmpinstance, ' ', '.');
2315    owl_text_tr(tmpinstance, '\'', '.');
2316    owl_text_tr(tmpinstance, '"', '.');
2317  }
2318
2319  argbuff = owl_sprintf(related ? "class ^(un)*%s(\\.d)*$" : "class ^%s$", tmpclass);
2320  if (tmpinstance) {
2321    char *tmp = argbuff;
2322    argbuff = owl_sprintf(related ? "%s and ( instance ^(un)*%s(\\.d)*$ )" : "%s and instance ^%s$", tmp, tmpinstance);
2323    owl_free(tmp);
2324  }
2325  owl_free(tmpclass);
2326  if (tmpinstance) owl_free(tmpinstance);
2327
2328  f = owl_filter_new_fromstring(filtname, argbuff);
2329
2330  /* add it to the global list */
2331  owl_global_add_filter(&g, f);
2332
2333  owl_free(argbuff);
2334done:
2335  owl_free(class);
2336  if (instance) {
2337    owl_free(instance);
2338  }
2339  return(filtname);
2340}
2341
2342/* Create a filter for personal zephyrs to or from the specified
2343 * zephyr user.  Includes login/logout notifications for the user.
2344 * The name of the filter will be 'user-<user>'.  If a filter already
2345 * exists with this name, no new filter will be created.  This allows
2346 * the configuration to override this function.  Returns the name of
2347 * the filter, which the caller must free.
2348 */
2349char *owl_function_zuserfilt(const char *user)
2350{
2351  owl_filter *f;
2352  char *argbuff, *longuser, *esclonguser, *shortuser, *filtname;
2353
2354  /* stick the local realm on if it's not there */
2355  longuser=long_zuser(user);
2356  shortuser=short_zuser(user);
2357
2358  /* name for the filter */
2359  filtname=owl_sprintf("user-%s", shortuser);
2360
2361  /* if it already exists then go with it.  This lets users override */
2362  if (owl_global_get_filter(&g, filtname)) {
2363    return filtname;
2364  }
2365
2366  /* create the new-internal filter */
2367  esclonguser = owl_text_quote(longuser, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2368
2369  argbuff=owl_sprintf("( type ^zephyr$ and filter personal and "
2370      "( ( direction ^in$ and sender ^%1$s$ ) or ( direction ^out$ and "
2371      "recipient ^%1$s$ ) ) ) or ( ( class ^login$ ) and ( sender ^%1$s$ ) )",
2372      esclonguser);
2373
2374  f = owl_filter_new_fromstring(filtname, argbuff);
2375
2376  /* add it to the global list */
2377  owl_global_add_filter(&g, f);
2378
2379  /* free stuff */
2380  owl_free(argbuff);
2381  owl_free(longuser);
2382  owl_free(esclonguser);
2383  owl_free(shortuser);
2384
2385  return(filtname);
2386}
2387
2388/* Create a filter for AIM IM messages to or from the specified
2389 * screenname.  The name of the filter will be 'aimuser-<user>'.  If a
2390 * filter already exists with this name, no new filter will be
2391 * created.  This allows the configuration to override this function.
2392 * Returns the name of the filter, which the caller must free.
2393 */
2394char *owl_function_aimuserfilt(const char *user)
2395{
2396  owl_filter *f;
2397  char *argbuff, *filtname;
2398  char *escuser;
2399
2400  /* name for the filter */
2401  filtname=owl_sprintf("aimuser-%s", user);
2402
2403  /* if it already exists then go with it.  This lets users override */
2404  if (owl_global_get_filter(&g, filtname)) {
2405    return(owl_strdup(filtname));
2406  }
2407
2408  /* create the new-internal filter */
2409  escuser = owl_text_quote(user, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2410
2411  argbuff = owl_sprintf(
2412      "( type ^aim$ and ( ( sender ^%1$s$ and recipient ^%2$s$ ) or "
2413      "( sender ^%2$s$ and recipient ^%1$s$ ) ) )",
2414      escuser, owl_global_get_aim_screenname_for_filters(&g));
2415
2416  f = owl_filter_new_fromstring(filtname, argbuff);
2417
2418  /* add it to the global list */
2419  owl_global_add_filter(&g, f);
2420
2421  /* free stuff */
2422  owl_free(argbuff);
2423  owl_free(escuser);
2424
2425  return(filtname);
2426}
2427
2428char *owl_function_typefilt(const char *type)
2429{
2430  owl_filter *f;
2431  char *argbuff, *filtname, *esctype;
2432
2433  /* name for the filter */
2434  filtname=owl_sprintf("type-%s", type);
2435
2436  /* if it already exists then go with it.  This lets users override */
2437  if (owl_global_get_filter(&g, filtname)) {
2438    return filtname;
2439  }
2440
2441  /* create the new-internal filter */
2442  esctype = owl_text_quote(type, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2443
2444  argbuff = owl_sprintf("type ^%s$", esctype);
2445
2446  f = owl_filter_new_fromstring(filtname, argbuff);
2447
2448  /* add it to the global list */
2449  owl_global_add_filter(&g, f);
2450
2451  /* free stuff */
2452  owl_free(argbuff);
2453  owl_free(esctype);
2454
2455  return filtname;
2456}
2457
2458/* If flag is 1, marks for deletion.  If flag is 0,
2459 * unmarks for deletion. */
2460void owl_function_delete_curview_msgs(int flag)
2461{
2462  const owl_view *v;
2463  int i, j;
2464
2465  v=owl_global_get_current_view(&g);
2466  j=owl_view_get_size(v);
2467  for (i=0; i<j; i++) {
2468    if (flag == 1) {
2469      owl_message_mark_delete(owl_view_get_element(v, i));
2470    } else if (flag == 0) {
2471      owl_message_unmark_delete(owl_view_get_element(v, i));
2472    }
2473  }
2474
2475  owl_function_makemsg("%i messages marked for %sdeletion", j, flag?"":"un");
2476
2477  owl_mainwin_redisplay(owl_global_get_mainwin(&g)); 
2478}
2479
2480static char *owl_function_smartfilter_cc(const owl_message *m) {
2481  const char *ccs;
2482  char *filtname;
2483  char *text;
2484  owl_filter *f;
2485
2486  ccs = owl_message_get_attribute_value(m, "zephyr_ccs");
2487
2488  filtname = owl_sprintf("conversation-%s", ccs);
2489  owl_text_tr(filtname, ' ', '-');
2490
2491  if (owl_global_get_filter(&g, filtname)) {
2492    return filtname;
2493  }
2494
2495  text = owl_sprintf("type ^zephyr$ and filter personal and "
2496                     "zephyr_ccs ^%s%s%s$",
2497                     owl_getquoting(ccs), ccs, owl_getquoting(ccs));
2498
2499  f = owl_filter_new_fromstring(filtname, text);
2500
2501  owl_global_add_filter(&g, f);
2502
2503  owl_free(text);
2504
2505  return filtname;
2506}
2507
2508/* Create a filter based on the current message.  Returns the name of
2509 * a filter or null.  The caller must free this name.
2510 *
2511 * if the curmsg is a personal zephyr return a filter name
2512 *    to the zephyr conversation with that user.
2513 * If the curmsg is a zephyr class message, instance foo, recip *,
2514 *    return a filter name to the class, inst.
2515 * If the curmsg is a zephyr class message and type==0 then
2516 *    return a filter name for just the class.
2517 * If the curmsg is a zephyr class message and type==1 then
2518 *    return a filter name for the class and instance.
2519 * If the curmsg is a personal AIM message returna  filter
2520 *    name to the AIM conversation with that user
2521 */
2522char *owl_function_smartfilter(int type, int invert_related)
2523{
2524  const owl_view *v;
2525  const owl_message *m;
2526  char *zperson, *filtname=NULL;
2527  const char *argv[2];
2528  int related = owl_global_is_narrow_related(&g) ^ invert_related;
2529
2530  v=owl_global_get_current_view(&g);
2531  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
2532
2533  if (!m || owl_view_get_size(v)==0) {
2534    owl_function_error("No message selected\n");
2535    return(NULL);
2536  }
2537
2538  /* very simple handling of admin messages for now */
2539  if (owl_message_is_type_admin(m)) {
2540    return(owl_function_typefilt("admin"));
2541  }
2542
2543  /* very simple handling of loopback messages for now */
2544  if (owl_message_is_type_loopback(m)) {
2545    return(owl_function_typefilt("loopback"));
2546  }
2547
2548  /* aim messages */
2549  if (owl_message_is_type_aim(m)) {
2550    if (owl_message_is_direction_in(m)) {
2551      filtname=owl_function_aimuserfilt(owl_message_get_sender(m));
2552    } else if (owl_message_is_direction_out(m)) {
2553      filtname=owl_function_aimuserfilt(owl_message_get_recipient(m));
2554    }
2555    return(filtname);
2556  }
2557
2558  /* narrow personal and login messages to the sender or recip as appropriate */
2559  if (owl_message_is_type_zephyr(m)) {
2560    if (owl_message_is_personal(m) || owl_message_is_loginout(m)) {
2561      if (owl_message_get_attribute_value(m, "zephyr_ccs") != NULL) {
2562        return owl_function_smartfilter_cc(m);
2563      }
2564
2565      if (owl_message_is_direction_in(m)) {
2566        zperson=short_zuser(owl_message_get_sender(m));
2567      } else {
2568        zperson=short_zuser(owl_message_get_recipient(m));
2569      }
2570      filtname=owl_function_zuserfilt(zperson);
2571      owl_free(zperson);
2572      return(filtname);
2573    }
2574
2575    /* narrow class MESSAGE, instance foo, recip * messages to class, inst */
2576    if (!strcasecmp(owl_message_get_class(m), "message")) {
2577      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m), related);
2578      return(filtname);
2579    }
2580
2581    /* otherwise narrow to the class */
2582    if (type==0) {
2583      filtname=owl_function_classinstfilt(owl_message_get_class(m), NULL, related);
2584    } else if (type==1) {
2585      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m), related);
2586    }
2587    return(filtname);
2588  }
2589
2590  /* pass it off to perl */
2591  argv[0] = type ? "1" : "0";
2592  argv[1] = related ? "1" : "0";
2593  return owl_perlconfig_message_call_method(m, "smartfilter", 2, argv);
2594}
2595
2596void owl_function_smartzpunt(int type)
2597{
2598  /* Starts a zpunt command based on the current class,instance pair.
2599   * If type=0, uses just class.  If type=1, uses instance as well. */
2600  const owl_view *v;
2601  const owl_message *m;
2602  const char *cmdprefix, *mclass, *minst;
2603  char *cmd;
2604 
2605  v=owl_global_get_current_view(&g);
2606  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
2607
2608  if (!m || owl_view_get_size(v)==0) {
2609    owl_function_error("No message selected\n");
2610    return;
2611  }
2612
2613  /* for now we skip admin messages. */
2614  if (owl_message_is_type_admin(m)
2615      || owl_message_is_loginout(m)
2616      || !owl_message_is_type_zephyr(m)) {
2617    owl_function_error("smartzpunt doesn't support this message type.");
2618    return;
2619  }
2620
2621  mclass = owl_message_get_class(m);
2622  minst = owl_message_get_instance(m);
2623  if (!mclass || !*mclass || *mclass==' '
2624      || (!strcasecmp(mclass, "message") && !strcasecmp(minst, "personal"))
2625      || (type && (!minst || !*minst|| *minst==' '))) {
2626    owl_function_error("smartzpunt can't safely do this for <%s,%s>",
2627                         mclass, minst);
2628  } else {
2629    cmdprefix = "start-command zpunt ";
2630    cmd = owl_malloc(strlen(cmdprefix)+strlen(mclass)+strlen(minst)+10);
2631    strcpy(cmd, cmdprefix);
2632    strcat(cmd, owl_getquoting(mclass));
2633    strcat(cmd, mclass);
2634    strcat(cmd, owl_getquoting(mclass));
2635    if (type) {
2636      strcat(cmd, " ");
2637      strcat(cmd, owl_getquoting(minst));
2638      strcat(cmd, minst);
2639      strcat(cmd, owl_getquoting(minst));
2640    } else {
2641      strcat(cmd, " *");
2642    }
2643    owl_function_command(cmd);
2644    owl_free(cmd);
2645  }
2646}
2647
2648/* Set the color of the current view's filter to
2649 * be 'color'
2650 */
2651void owl_function_color_current_filter(const char *fgcolor, const char *bgcolor)
2652{
2653  const char *name;
2654
2655  name=owl_view_get_filtname(owl_global_get_current_view(&g));
2656  owl_function_color_filter(name, fgcolor, bgcolor);
2657}
2658
2659/* Set the color of the filter 'filter' to be 'color'.  If the color
2660 * name does not exist, return -1, if the filter does not exist or is
2661 * the "all" filter, return -2.  Return 0 on success
2662 */
2663int owl_function_color_filter(const char *filtname, const char *fgcolor, const char *bgcolor)
2664{
2665  owl_filter *f;
2666
2667  f=owl_global_get_filter(&g, filtname);
2668  if (!f) {
2669    owl_function_error("Unknown filter");
2670    return(-2);
2671  }
2672
2673  /* don't touch the all filter */
2674  if (!strcmp(filtname, "all")) {
2675    owl_function_error("You may not change the 'all' filter.");
2676    return(-2);
2677  }
2678
2679  if (owl_util_string_to_color(fgcolor)==OWL_COLOR_INVALID) {
2680    owl_function_error("No color named '%s' avilable.", fgcolor);
2681    return(-1);
2682  }
2683
2684
2685  if (bgcolor != NULL) {
2686    if (owl_util_string_to_color(bgcolor)==OWL_COLOR_INVALID) {
2687      owl_function_error("No color named '%s' avilable.", bgcolor);
2688      return(-1);
2689    }
2690    owl_filter_set_bgcolor(f, owl_util_string_to_color(bgcolor));
2691  }
2692  owl_filter_set_fgcolor(f, owl_util_string_to_color(fgcolor));
2693 
2694  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2695  return(0);
2696}
2697
2698void owl_function_show_colors(void)
2699{
2700  owl_fmtext fm;
2701  int i; 
2702 
2703  owl_fmtext_init_null(&fm);
2704  owl_fmtext_append_normal(&fm, "default: ");
2705  owl_fmtext_append_normal_color(&fm, "default\n", OWL_COLOR_DEFAULT, OWL_COLOR_DEFAULT);
2706
2707  owl_fmtext_append_normal(&fm,"red:      ");
2708  owl_fmtext_append_normal_color(&fm, "red\n", OWL_COLOR_RED, OWL_COLOR_DEFAULT);
2709
2710  owl_fmtext_append_normal(&fm,"green:    ");
2711  owl_fmtext_append_normal_color(&fm, "green\n", OWL_COLOR_GREEN, OWL_COLOR_DEFAULT);
2712
2713  owl_fmtext_append_normal(&fm,"yellow:   ");
2714  owl_fmtext_append_normal_color(&fm, "yellow\n", OWL_COLOR_YELLOW, OWL_COLOR_DEFAULT);
2715
2716  owl_fmtext_append_normal(&fm,"blue:     ");
2717  owl_fmtext_append_normal_color(&fm, "blue\n", OWL_COLOR_BLUE, OWL_COLOR_DEFAULT);
2718
2719  owl_fmtext_append_normal(&fm,"magenta:  ");
2720  owl_fmtext_append_normal_color(&fm, "magenta\n", OWL_COLOR_MAGENTA, OWL_COLOR_DEFAULT);
2721
2722  owl_fmtext_append_normal(&fm,"cyan:     ");
2723  owl_fmtext_append_normal_color(&fm, "cyan\n", OWL_COLOR_CYAN, OWL_COLOR_DEFAULT);
2724
2725  owl_fmtext_append_normal(&fm,"white:    ");
2726  owl_fmtext_append_normal_color(&fm, "white\n", OWL_COLOR_WHITE, OWL_COLOR_DEFAULT);
2727
2728  for(i = 8; i < COLORS; ++i) {
2729    char* str1 = owl_sprintf("%4i:     ",i);
2730    char* str2 = owl_sprintf("%i\n",i);
2731    owl_fmtext_append_normal(&fm,str1);
2732    owl_fmtext_append_normal_color(&fm, str2, i, OWL_COLOR_DEFAULT);
2733    owl_free(str1);
2734     owl_free(str2);
2735  }
2736 
2737  owl_function_popless_fmtext(&fm);
2738  owl_fmtext_cleanup(&fm);
2739}
2740
2741/* add the given class, inst, recip to the punt list for filtering.
2742 *   if direction==0 then punt
2743 *   if direction==1 then unpunt
2744 */
2745void owl_function_zpunt(const char *class, const char *inst, const char *recip, int direction)
2746{
2747  char *puntexpr, *classexpr, *instexpr, *recipexpr;
2748  char *quoted;
2749
2750  if (!strcmp(class, "*")) {
2751    classexpr = owl_sprintf("class .*");
2752  } else {
2753    quoted=owl_text_quote(class, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2754    owl_text_tr(quoted, ' ', '.');
2755    owl_text_tr(quoted, '\'', '.');
2756    owl_text_tr(quoted, '"', '.');
2757    classexpr = owl_sprintf("class ^(un)*%s(\\.d)*$", quoted);
2758    owl_free(quoted);
2759  }
2760  if (!strcmp(inst, "*")) {
2761    instexpr = owl_sprintf(" and instance .*");
2762  } else {
2763    quoted=owl_text_quote(inst, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2764    owl_text_tr(quoted, ' ', '.');
2765    owl_text_tr(quoted, '\'', '.');
2766    owl_text_tr(quoted, '"', '.');
2767    instexpr = owl_sprintf(" and instance ^(un)*%s(\\.d)*$", quoted);
2768    owl_free(quoted);
2769  }
2770  if (!strcmp(recip, "*")) {
2771    recipexpr = owl_sprintf("");
2772  } else {
2773    if(!strcmp(recip, "%me%")) {
2774      recip = owl_zephyr_get_sender();
2775    }
2776    quoted=owl_text_quote(recip, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2777    owl_text_tr(quoted, ' ', '.');
2778    owl_text_tr(quoted, '\'', '.');
2779    owl_text_tr(quoted, '"', '.');
2780    recipexpr = owl_sprintf(" and recipient ^%s$", quoted);
2781    owl_free(quoted);
2782  }
2783
2784  puntexpr = owl_sprintf("%s %s %s", classexpr, instexpr, recipexpr);
2785  owl_function_punt(puntexpr, direction);
2786  owl_free(puntexpr);
2787  owl_free(classexpr);
2788  owl_free(instexpr);
2789  owl_free(recipexpr);
2790}
2791
2792void owl_function_punt(const char *filter, int direction)
2793{
2794  owl_filter *f;
2795  owl_list *fl;
2796  int i, j;
2797  fl=owl_global_get_puntlist(&g);
2798
2799  /* first, create the filter */
2800  owl_function_debugmsg("About to filter %s", filter);
2801  f = owl_filter_new_fromstring("punt-filter", filter);
2802  if (f == NULL) {
2803    owl_function_error("Error creating filter for zpunt");
2804    return;
2805  }
2806
2807  /* Check for an identical filter */
2808  j=owl_list_get_size(fl);
2809  for (i=0; i<j; i++) {
2810    if (owl_filter_equiv(f, owl_list_get_element(fl, i))) {
2811      owl_function_debugmsg("found an equivalent punt filter");
2812      /* if we're punting, then just silently bow out on this duplicate */
2813      if (direction==0) {
2814        owl_filter_delete(f);
2815        return;
2816      }
2817
2818      /* if we're unpunting, then remove this filter from the puntlist */
2819      if (direction==1) {
2820        owl_filter_delete(owl_list_get_element(fl, i));
2821        owl_list_remove_element(fl, i);
2822        owl_filter_delete(f);
2823        return;
2824      }
2825    }
2826  }
2827
2828  owl_function_debugmsg("punting");
2829  /* If we're punting, add the filter to the global punt list */
2830  if (direction==0) {
2831    owl_list_append_element(fl, f);
2832  }
2833}
2834
2835void owl_function_show_keymaps(void)
2836{
2837  owl_list l;
2838  owl_fmtext fm;
2839  const owl_keymap *km;
2840  const owl_keyhandler *kh;
2841  int i, numkm;
2842  const char *kmname;
2843
2844  kh = owl_global_get_keyhandler(&g);
2845  owl_fmtext_init_null(&fm);
2846  owl_fmtext_append_bold(&fm, "Keymaps:   ");
2847  owl_fmtext_append_normal(&fm, "(use 'show keymap <name>' for details)\n");
2848  owl_keyhandler_get_keymap_names(kh, &l);
2849  owl_fmtext_append_list(&fm, &l, "\n", owl_function_keymap_summary);
2850  owl_fmtext_append_normal(&fm, "\n");
2851
2852  numkm = owl_list_get_size(&l);
2853  for (i=0; i<numkm; i++) {
2854    kmname = owl_list_get_element(&l, i);
2855    km = owl_keyhandler_get_keymap(kh, kmname);
2856    owl_fmtext_append_bold(&fm, "\n\n----------------------------------------------------------------------------------------------------\n\n");
2857    owl_keymap_get_details(km, &fm, 0);
2858  }
2859  owl_fmtext_append_normal(&fm, "\n");
2860 
2861  owl_function_popless_fmtext(&fm);
2862  owl_keyhandler_keymap_namelist_cleanup(&l);
2863  owl_fmtext_cleanup(&fm);
2864}
2865
2866char *owl_function_keymap_summary(const char *name)
2867{
2868  const owl_keymap *km
2869    = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), name);
2870  if (km) return owl_keymap_summary(km);
2871  else return(NULL);
2872}
2873
2874/* TODO: implement for real */
2875void owl_function_show_keymap(const char *name)
2876{
2877  owl_fmtext fm;
2878  const owl_keymap *km;
2879
2880  owl_fmtext_init_null(&fm);
2881  km = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), name);
2882  if (km) {
2883    owl_keymap_get_details(km, &fm, 1);
2884  } else {
2885    owl_fmtext_append_normal(&fm, "No such keymap...\n");
2886  } 
2887  owl_function_popless_fmtext(&fm);
2888  owl_fmtext_cleanup(&fm);
2889}
2890
2891void owl_function_help_for_command(const char *cmdname)
2892{
2893  owl_fmtext fm;
2894
2895  owl_fmtext_init_null(&fm);
2896  owl_cmd_get_help(owl_global_get_cmddict(&g), cmdname, &fm);
2897  owl_function_popless_fmtext(&fm); 
2898  owl_fmtext_cleanup(&fm);
2899}
2900
2901void owl_function_search_start(const char *string, int direction)
2902{
2903  /* direction is OWL_DIRECTION_DOWNWARDS or OWL_DIRECTION_UPWARDS or
2904   * OWL_DIRECTION_NONE */
2905  owl_regex re;
2906
2907  if (string && owl_regex_create_quoted(&re, string) == 0) {
2908    owl_global_set_search_re(&g, &re);
2909    owl_regex_cleanup(&re);
2910  } else {
2911    owl_global_set_search_re(&g, NULL);
2912  }
2913
2914  if (direction == OWL_DIRECTION_NONE)
2915    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2916  else
2917    owl_function_search_helper(0, direction);
2918}
2919
2920void owl_function_search_continue(int direction)
2921{
2922  /* direction is OWL_DIRECTION_DOWNWARDS or OWL_DIRECTION_UPWARDS */
2923  owl_function_search_helper(1, direction);
2924}
2925
2926void owl_function_search_helper(int mode, int direction)
2927{
2928  /* move to a message that contains the string.  If direction is
2929   * OWL_DIRECTION_DOWNWARDS then search fowards, if direction is
2930   * OWL_DIRECTION_UPWARDS then search backwards.
2931   *
2932   * If mode==0 then it will stay on the current message if it
2933   * contains the string.
2934   */
2935
2936  const owl_view *v;
2937  int viewsize, i, curmsg, start;
2938  owl_message *m;
2939
2940  v=owl_global_get_current_view(&g);
2941  viewsize=owl_view_get_size(v);
2942  curmsg=owl_global_get_curmsg(&g);
2943 
2944  if (viewsize==0) {
2945    owl_function_error("No messages present");
2946    return;
2947  }
2948
2949  if (mode==0) {
2950    start=curmsg;
2951  } else if (direction==OWL_DIRECTION_DOWNWARDS) {
2952    start=curmsg+1;
2953  } else {
2954    start=curmsg-1;
2955  }
2956
2957  /* bounds check */
2958  if (start>=viewsize || start<0) {
2959    owl_function_error("No further matches found");
2960    return;
2961  }
2962
2963  for (i=start; i<viewsize && i>=0;) {
2964    m=owl_view_get_element(v, i);
2965    if (owl_message_search(m, owl_global_get_search_re(&g))) {
2966      owl_global_set_curmsg(&g, i);
2967      owl_function_calculate_topmsg(direction);
2968      owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2969      if (direction==OWL_DIRECTION_DOWNWARDS) {
2970        owl_global_set_direction_downwards(&g);
2971      } else {
2972        owl_global_set_direction_upwards(&g);
2973      }
2974      return;
2975    }
2976    if (direction==OWL_DIRECTION_DOWNWARDS) {
2977      i++;
2978    } else {
2979      i--;
2980    }
2981    owl_function_mask_sigint(NULL);
2982    if(owl_global_is_interrupted(&g)) {
2983      owl_global_unset_interrupted(&g);
2984      owl_function_unmask_sigint(NULL);
2985      owl_function_makemsg("Search interrupted!");
2986      owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2987      return;
2988    }
2989    owl_function_unmask_sigint(NULL);
2990  }
2991  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2992  owl_function_error("No matches found");
2993}
2994
2995/* strips formatting from ztext and returns the unformatted text.
2996 * caller is responsible for freeing. */
2997char *owl_function_ztext_stylestrip(const char *zt)
2998{
2999  owl_fmtext fm;
3000  char *plaintext;
3001
3002  owl_fmtext_init_null(&fm);
3003  owl_fmtext_append_ztext(&fm, zt);
3004  plaintext = owl_fmtext_print_plain(&fm);
3005  owl_fmtext_cleanup(&fm);
3006  return(plaintext);
3007}
3008
3009/* Popup a buddylisting.  If filename is NULL use the default .anyone */
3010void owl_function_buddylist(int aim, int zephyr, const char *filename)
3011{
3012  int i, j, idle;
3013  int interrupted = 0;
3014  owl_fmtext fm;
3015  const owl_buddylist *bl;
3016  const owl_buddy *b;
3017  char *timestr;
3018#ifdef HAVE_LIBZEPHYR
3019  int x;
3020  owl_list anyone;
3021  const char *user;
3022  char *tmp;
3023  ZLocations_t location[200];
3024  int numlocs, ret;
3025#endif
3026
3027  owl_fmtext_init_null(&fm);
3028
3029  /* AIM first */
3030  if (aim && owl_global_is_aimloggedin(&g)) {
3031    bl=owl_global_get_buddylist(&g);
3032
3033    owl_fmtext_append_bold(&fm, "AIM users logged in:\n");
3034    /* we're assuming AIM for now */
3035    j=owl_buddylist_get_size(bl);
3036    for (i=0; i<j; i++) {
3037      b=owl_buddylist_get_buddy_n(bl, i);
3038      idle=owl_buddy_get_idle_time(b);
3039      if (idle!=0) {
3040        timestr=owl_util_minutes_to_timestr(idle);
3041      } else {
3042        timestr=owl_strdup("");
3043      }
3044      owl_fmtext_appendf_normal(&fm, "  %-20.20s %-12.12s\n", owl_buddy_get_name(b), timestr);
3045      owl_free(timestr);
3046    }
3047  }
3048
3049#ifdef HAVE_LIBZEPHYR
3050  if (zephyr) {
3051    if(!owl_global_is_havezephyr(&g)) {
3052      owl_function_error("Zephyr currently not available.");
3053    } else {
3054      owl_fmtext_append_bold(&fm, "Zephyr users logged in:\n");
3055      owl_list_create(&anyone);
3056      ret=owl_zephyr_get_anyone_list(&anyone, filename);
3057      if (ret) {
3058        if (errno == ENOENT) {
3059          owl_fmtext_append_normal(&fm, " You have not added any zephyr buddies.  Use the\n");
3060          owl_fmtext_append_normal(&fm, " command ':addbuddy zephyr ");
3061          owl_fmtext_append_bold(  &fm, "<username>");
3062          owl_fmtext_append_normal(&fm, "'.\n");
3063        } else {
3064          owl_fmtext_append_normal(&fm, " Could not read zephyr buddies from the .anyone file.\n");
3065        }
3066      } else {
3067        j=owl_list_get_size(&anyone);
3068        for (i=0; i<j; i++) {
3069          user=owl_list_get_element(&anyone, i);
3070          ret=ZLocateUser(zstr(user), &numlocs, ZAUTH);
3071
3072          owl_function_mask_sigint(NULL);
3073          if(owl_global_is_interrupted(&g)) {
3074            interrupted = 1;
3075            owl_global_unset_interrupted(&g);
3076            owl_function_unmask_sigint(NULL);
3077            owl_function_makemsg("Interrupted!");
3078            break;
3079          }
3080
3081          owl_function_unmask_sigint(NULL);
3082
3083          if (ret!=ZERR_NONE) {
3084            owl_function_error("Error getting location for %s", user);
3085            continue;
3086          }
3087
3088          numlocs=200;
3089          ret=ZGetLocations(location, &numlocs);
3090          if (ret==0) {
3091            for (x=0; x<numlocs; x++) {
3092              tmp=short_zuser(user);
3093              owl_fmtext_appendf_normal(&fm, "  %-10.10s %-24.24s %-12.12s  %20.20s\n",
3094                                        tmp,
3095                                        location[x].host,
3096                                        location[x].tty,
3097                                        location[x].time);
3098              owl_free(tmp);
3099            }
3100            if (numlocs>=200) {
3101              owl_fmtext_append_normal(&fm, "  Too many locations found for this user, truncating.\n");
3102            }
3103          }
3104        }
3105      }
3106      owl_list_cleanup(&anyone, owl_free);
3107    }
3108  }
3109#endif
3110
3111  if (aim && zephyr) {
3112    if (owl_perlconfig_is_function("BarnOwl::Hooks::_get_blist")) {
3113      char * perlblist = owl_perlconfig_execute("BarnOwl::Hooks::_get_blist()");
3114      if (perlblist) {
3115        owl_fmtext_append_ztext(&fm, perlblist);
3116        owl_free(perlblist);
3117      }
3118    }
3119  }
3120
3121  if(!interrupted) {
3122    owl_function_popless_fmtext(&fm);
3123  }
3124  owl_fmtext_cleanup(&fm);
3125}
3126
3127/* Dump messages in the current view to the file 'filename'. */
3128void owl_function_dump(const char *filename) 
3129{
3130  int i, j;
3131  owl_message *m;
3132  const owl_view *v;
3133  FILE *file;
3134  char *plaintext;
3135
3136  v=owl_global_get_current_view(&g);
3137
3138  /* in the future make it ask yes/no */
3139  /*
3140  ret=stat(filename, &sbuf);
3141  if (!ret) {
3142    ret=owl_function_askyesno("File exists, continue? [Y/n]");
3143    if (!ret) return;
3144  }
3145  */
3146
3147  file=fopen(filename, "w");
3148  if (!file) {
3149    owl_function_error("Error opening file");
3150    return;
3151  }
3152
3153  j=owl_view_get_size(v);
3154  for (i=0; i<j; i++) {
3155    m=owl_view_get_element(v, i);
3156    plaintext = owl_strip_format_chars(owl_message_get_text(m));
3157    if (plaintext) {
3158      fputs(plaintext, file);
3159      owl_free(plaintext);
3160    }
3161  }
3162  fclose(file);
3163  owl_function_makemsg("Messages dumped to %s", filename);
3164}
3165
3166void owl_function_do_newmsgproc(void)
3167{
3168  if (owl_global_get_newmsgproc(&g) && strcmp(owl_global_get_newmsgproc(&g), "")) {
3169    /* if there's a process out there, we need to check on it */
3170    if (owl_global_get_newmsgproc_pid(&g)) {
3171      owl_function_debugmsg("Checking on newmsgproc pid==%i", owl_global_get_newmsgproc_pid(&g));
3172      owl_function_debugmsg("Waitpid return is %i", waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG));
3173      waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG);
3174      if (waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG)==-1) {
3175        /* it exited */
3176        owl_global_set_newmsgproc_pid(&g, 0);
3177        owl_function_debugmsg("newmsgproc exited");
3178      } else {
3179        owl_function_debugmsg("newmsgproc did not exit");
3180      }
3181    }
3182   
3183    /* if it exited, fork & exec a new one */
3184    if (owl_global_get_newmsgproc_pid(&g)==0) {
3185      pid_t i;
3186      int myargc;
3187      i=fork();
3188      if (i) {
3189        /* parent set the child's pid */
3190        owl_global_set_newmsgproc_pid(&g, i);
3191        owl_function_debugmsg("I'm the parent and I started a new newmsgproc with pid %i", i);
3192      } else {
3193        /* child exec's the program */
3194        char **parsed;
3195        parsed=owl_parseline(owl_global_get_newmsgproc(&g), &myargc);
3196        if (myargc < 0) {
3197          owl_function_debugmsg("Could not parse newmsgproc '%s': unbalanced quotes?", owl_global_get_newmsgproc(&g));
3198        }
3199        if (myargc <= 0) {
3200          _exit(127);
3201        }
3202        parsed=owl_realloc(parsed, sizeof(*parsed) * (myargc+1));
3203        parsed[myargc] = NULL;
3204       
3205        owl_function_debugmsg("About to exec \"%s\" with %d arguments", parsed[0], myargc);
3206       
3207        execvp(parsed[0], parsed);
3208       
3209       
3210        /* was there an error exec'ing? */
3211        owl_function_debugmsg("Cannot run newmsgproc '%s': cannot exec '%s': %s", 
3212                              owl_global_get_newmsgproc(&g), parsed[0], strerror(errno));
3213        _exit(127);
3214      }
3215    }
3216  }
3217}
3218
3219/* print the xterm escape sequence to raise the window */
3220void owl_function_xterm_raise(void)
3221{
3222  printf("\033[5t");
3223}
3224
3225/* print the xterm escape sequence to deiconify the window */
3226void owl_function_xterm_deiconify(void)
3227{
3228  printf("\033[1t");
3229}
3230
3231/* Add the specified command to the startup file.  Eventually this
3232 * should be clever, and rewriting settings that will obviosly
3233 * override earlier settings with 'set' 'bindkey' and 'alias'
3234 * commands.  For now though we just remove any line that would
3235 * duplicate this one and then append this line to the end of
3236 * startupfile.
3237 */
3238void owl_function_addstartup(const char *buff)
3239{
3240  FILE *file;
3241  const char *filename;
3242
3243  filename=owl_global_get_startupfile(&g);
3244
3245  /* delete earlier copies */
3246  owl_util_file_deleteline(filename, buff, 1);
3247
3248  file=fopen(filename, "a");
3249  if (!file) {
3250    owl_function_error("Error opening startupfile for new command");
3251    return;
3252  }
3253
3254  /* add this line */
3255  fprintf(file, "%s\n", buff);
3256
3257  fclose(file);
3258}
3259
3260/* Remove the specified command from the startup file. */
3261void owl_function_delstartup(const char *buff)
3262{
3263  const char *filename;
3264  filename=owl_global_get_startupfile(&g);
3265  owl_util_file_deleteline(filename, buff, 1);
3266}
3267
3268/* Execute owl commands from the given filename.  If the filename
3269 * is NULL, use the default owl startup commands file.
3270 */
3271void owl_function_source(const char *filename)
3272{
3273  char *path;
3274  FILE *file;
3275  char *s = NULL;
3276  int fail_silent = 0;
3277
3278  if (!filename) {
3279    fail_silent = 1;
3280    path = owl_strdup(owl_global_get_startupfile(&g));
3281  } else {
3282    path = owl_util_makepath(filename);
3283  }
3284  file = fopen(path, "r");
3285  owl_free(path);
3286  if (!file) {
3287    if (!fail_silent) {
3288      owl_function_error("Error opening file: %s", filename);
3289    }
3290    return;
3291  }
3292  while (owl_getline_chomp(&s, file)) {
3293    if (s[0] == '\0' || s[0] == '#')
3294      continue;
3295    owl_function_command(s);
3296  }
3297
3298  owl_free(s);
3299  fclose(file);
3300}
3301
3302void owl_function_change_style(owl_view *v, const char *stylename)
3303{
3304  const owl_style *s;
3305
3306  s=owl_global_get_style_by_name(&g, stylename);
3307  if (!s) {
3308    owl_function_error("No style named %s", stylename);
3309    return;
3310  }
3311  owl_view_set_style(v, s);
3312  owl_messagelist_invalidate_formats(owl_global_get_msglist(&g));
3313  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
3314  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3315}
3316
3317void owl_function_toggleoneline(void)
3318{
3319  owl_view *v;
3320  const owl_style *s;
3321
3322  v=owl_global_get_current_view(&g);
3323  s=owl_view_get_style(v);
3324
3325  if (!owl_style_matches_name(s, "oneline")) {
3326    owl_function_change_style(v, "oneline");
3327  } else {
3328    owl_function_change_style(v, owl_global_get_default_style(&g));
3329  }
3330
3331  owl_messagelist_invalidate_formats(owl_global_get_msglist(&g));
3332  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
3333  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3334}
3335
3336void owl_function_error(const char *fmt, ...)
3337{
3338  static int in_error = 0;
3339  va_list ap;
3340  char *buff;
3341  const char *nl;
3342
3343  if (++in_error > 2) {
3344    /* More than two nested errors, bail immediately. */
3345    in_error--;
3346    return;
3347  }
3348
3349  va_start(ap, fmt);
3350  buff = g_strdup_vprintf(fmt, ap);
3351  va_end(ap);
3352
3353  owl_function_debugmsg("ERROR: %s", buff);
3354  owl_function_log_err(buff);
3355
3356  nl = strchr(buff, '\n');
3357
3358  /*
3359    Showing admin messages triggers a lot of code. If we have a
3360    recursive error call, that's the most likely candidate, so
3361    suppress the call in that case, to try to avoid infinite looping.
3362  */
3363
3364  if(nl && *(nl + 1) && in_error == 1) {
3365    /* Multiline error */
3366    owl_function_adminmsg("ERROR", buff);
3367  } else {
3368    owl_function_makemsg("[Error] %s", buff);
3369  }
3370
3371  owl_free(buff);
3372
3373  in_error--;
3374}
3375
3376void owl_function_log_err(const char *string)
3377{
3378  char *date;
3379  time_t now;
3380  char *buff;
3381
3382  now=time(NULL);
3383  date=owl_strdup(ctime(&now));
3384  date[strlen(date)-1]='\0';
3385
3386  buff = owl_sprintf("%s %s", date, string);
3387
3388  owl_errqueue_append_err(owl_global_get_errqueue(&g), buff);
3389
3390  owl_free(buff);
3391  owl_free(date);
3392}
3393
3394void owl_function_showerrs(void)
3395{
3396  owl_fmtext fm;
3397
3398  owl_fmtext_init_null(&fm);
3399  owl_fmtext_append_normal(&fm, "Errors:\n\n");
3400  owl_errqueue_to_fmtext(owl_global_get_errqueue(&g), &fm);
3401  owl_function_popless_fmtext(&fm);
3402}
3403
3404void owl_function_makemsg(const char *fmt, ...)
3405{
3406  va_list ap;
3407  char buff[2048];
3408
3409  if (!owl_global_get_curs_msgwin(&g)) return;
3410
3411  va_start(ap, fmt);
3412  werase(owl_global_get_curs_msgwin(&g));
3413 
3414  vsnprintf(buff, 2048, fmt, ap);
3415  owl_function_debugmsg("makemsg: %s", buff);
3416  waddstr(owl_global_get_curs_msgwin(&g), buff); 
3417  owl_global_set_needrefresh(&g);
3418  va_end(ap);
3419}
3420
3421/* get locations for everyone in .anyone.  If 'notify' is '1' then
3422 * send a pseudo login or logout message for everyone not in sync with
3423 * the global zephyr buddy list.  The list is updated regardless of
3424 * the status of 'notify'.
3425 */
3426void owl_function_zephyr_buddy_check(int notify)
3427{
3428#ifdef HAVE_LIBZEPHYR
3429  int i, j;
3430  owl_list anyone;
3431  owl_zbuddylist *zbl;
3432  GList **zaldlist;
3433  GList *zaldptr;
3434  ZAsyncLocateData_t *zald;
3435  const char *user;
3436
3437  if (!owl_global_is_havezephyr(&g)) return;
3438  owl_global_set_pseudologin_notify(&g, notify);
3439  zbl = owl_global_get_zephyr_buddylist(&g);
3440  zaldlist = owl_global_get_zaldlist(&g);
3441
3442  /* Clear the existing ZALDs first. */
3443  zaldptr = g_list_first(*zaldlist);
3444  while (zaldptr) {
3445    ZFreeALD(zaldptr->data);
3446    owl_free(zaldptr->data);
3447    zaldptr = g_list_next(zaldptr);
3448  }
3449  g_list_free(*zaldlist);
3450  *zaldlist = NULL;
3451
3452  owl_list_create(&anyone);
3453  owl_zephyr_get_anyone_list(&anyone, NULL);
3454  j = owl_list_get_size(&anyone);
3455  for (i = 0; i < j; i++) {
3456    user = owl_list_get_element(&anyone, i);
3457    zald = owl_malloc(sizeof(ZAsyncLocateData_t));
3458    if (ZRequestLocations(zstr(user), zald, UNACKED, ZAUTH) == ZERR_NONE) {
3459      *zaldlist = g_list_append(*zaldlist, zald);
3460    } else {
3461      owl_free(zald);
3462    }
3463  }
3464
3465  owl_list_cleanup(&anyone, owl_free);
3466#endif
3467}
3468
3469void owl_function_aimsearch_results(const char *email, owl_list *namelist)
3470{
3471  owl_fmtext fm;
3472  int i, j;
3473
3474  owl_fmtext_init_null(&fm);
3475  owl_fmtext_append_normal(&fm, "AIM screennames associated with ");
3476  owl_fmtext_append_normal(&fm, email);
3477  owl_fmtext_append_normal(&fm, ":\n");
3478
3479  j=owl_list_get_size(namelist);
3480  for (i=0; i<j; i++) {
3481    owl_fmtext_append_normal(&fm, "  ");
3482    owl_fmtext_append_normal(&fm, owl_list_get_element(namelist, i));
3483    owl_fmtext_append_normal(&fm, "\n");
3484  }
3485
3486  owl_function_popless_fmtext(&fm);
3487  owl_fmtext_cleanup(&fm);
3488}
3489
3490int owl_function_get_color_count(void)
3491{
3492     return COLORS;
3493}
3494
3495void owl_function_mask_sigint(sigset_t *oldmask) {
3496  sigset_t intr;
3497
3498  sigemptyset(&intr);
3499  sigaddset(&intr, SIGINT);
3500  sigprocmask(SIG_BLOCK, &intr, oldmask);
3501}
3502
3503void owl_function_unmask_sigint(sigset_t *oldmask) {
3504  sigset_t intr;
3505
3506  sigemptyset(&intr);
3507  sigaddset(&intr, SIGINT);
3508  sigprocmask(SIG_UNBLOCK, &intr, oldmask);
3509}
3510
3511void _owl_function_mark_message(const owl_message *m)
3512{
3513  if (m) {
3514    owl_global_set_markedmsgid(&g, owl_message_get_id(m));
3515    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3516  }
3517}
3518
3519void owl_function_mark_message(void)
3520{
3521  const owl_message *m;
3522  const owl_view *v;
3523
3524  v=owl_global_get_current_view(&g);
3525
3526  /* bail if there's no current message */
3527  if (owl_view_get_size(v) < 1) {
3528    owl_function_error("No messages to mark");
3529    return;
3530  }
3531
3532  /* mark the message */
3533  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
3534  _owl_function_mark_message(m);
3535  owl_function_makemsg("Mark set");
3536}
3537
3538void owl_function_swap_cur_marked(void)
3539{
3540  int marked_id;
3541  const owl_message *m;
3542  const owl_view *v;
3543
3544  marked_id=owl_global_get_markedmsgid(&g);
3545  if (marked_id == -1) {
3546    owl_function_error("Mark not set.");
3547    return;
3548  }
3549
3550  v=owl_global_get_current_view(&g);
3551  /* bail if there's no current message */
3552  if (owl_view_get_size(v) < 1) {
3553    return;
3554  }
3555
3556  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
3557  _owl_function_mark_message(m);
3558  owl_global_set_curmsg(&g, owl_view_get_nearest_to_msgid(v, marked_id));
3559  owl_function_calculate_topmsg(OWL_DIRECTION_NONE);
3560  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3561  owl_global_set_direction_downwards(&g);
3562}
Note: See TracBrowser for help on using the repository browser.