source: functions.c @ 91634ec

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