source: functions.c @ 1383b58

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