source: functions.c @ 6337cb5

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