source: functions.c @ 99b50a5

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