source: functions.c @ 4a41f16

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