source: functions.c @ cc36f27

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