source: functions.c @ 9a7b4f2

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