source: functions.c @ 7bfc613

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