source: functions.c @ e20dd769

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