source: functions.c @ bf66f4e

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