source: functions.c @ 2101a50

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