source: functions.c @ d564c3d

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