source: functions.c @ 258a3bf

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