source: functions.c @ 987cf3f

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