source: functions.c @ 385fda9

release-1.7release-1.8release-1.9
Last change on this file since 385fda9 was 385fda9, checked in by David Benjamin <davidben@mit.edu>, 11 years ago
A fairly hacky job of porting sepbar to owl_window We now use the new framework, but the sepbar isn't being marked dirty at the appropriate times. It'll want to be redone later to take advantage of the new signals. The 'in' parameter was also dropped. It was being unused. At best it provided a strange bug when resizing with appendtosepbar set. Also revert the attempt at cleaning up the cursor. We still have a hack, but using owl_window. The cursor probably needs integration with the context stack or some such.
  • Property mode set to 100644
File size: 95.2 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_full_redisplay(void)
1251{
1252  touchwin(owl_global_get_curs_recwin(&g));
1253
1254  sepbar_dirty();
1255
1256  owl_global_set_needrefresh(&g);
1257}
1258
1259void owl_function_popless_text(const char *text)
1260{
1261  owl_popwin *pw;
1262  owl_viewwin *v;
1263
1264  pw=owl_global_get_popwin(&g);
1265  v=owl_global_get_viewwin(&g);
1266
1267  owl_popwin_up(pw);
1268  owl_global_push_context(&g, OWL_CTX_POPLESS, v, "popless");
1269  owl_viewwin_init_text(v, owl_popwin_get_content(pw), text);
1270}
1271
1272void owl_function_popless_fmtext(const owl_fmtext *fm)
1273{
1274  owl_popwin *pw;
1275  owl_viewwin *v;
1276
1277  pw=owl_global_get_popwin(&g);
1278  v=owl_global_get_viewwin(&g);
1279
1280  owl_popwin_up(pw);
1281  owl_global_push_context(&g, OWL_CTX_POPLESS, v, "popless");
1282  owl_viewwin_init_fmtext(v, owl_popwin_get_content(pw), fm);
1283}
1284
1285void owl_function_popless_file(const char *filename)
1286{
1287  owl_fmtext fm;
1288  FILE *file;
1289  char *s = NULL;
1290
1291  file=fopen(filename, "r");
1292  if (!file) {
1293    owl_function_error("Could not open file: %s", filename);
1294    return;
1295  }
1296
1297  owl_fmtext_init_null(&fm);
1298  while (owl_getline(&s, file))
1299    owl_fmtext_append_normal(&fm, s);
1300  owl_free(s);
1301
1302  owl_function_popless_fmtext(&fm);
1303  owl_fmtext_cleanup(&fm);
1304  fclose(file);
1305}
1306
1307void owl_function_about(void)
1308{
1309  owl_function_popless_text(
1310    "This is barnowl version " OWL_VERSION_STRING ".\n\n"
1311    "barnowl is a fork of the Owl zephyr client, written and\n"
1312    "maintained by Alejandro Sedeno and Nelson Elhage at the\n"
1313    "Massachusetts Institute of Technology. \n"
1314    "\n"
1315    "Owl was written by James Kretchmar. The first version, 0.5, was\n"
1316    "released in March 2002.\n"
1317    "\n"
1318    "The name 'owl' was chosen in reference to the owls in the\n"
1319    "Harry Potter novels, who are tasked with carrying messages\n"
1320    "between Witches and Wizards. The name 'barnowl' was chosen\n"
1321    "because we feel our owls should live closer to our ponies.\n"
1322    "\n"
1323    "Copyright (c) 2006-2010 The BarnOwl Developers. All rights reserved.\n"
1324    "Copyright (c) 2004 James Kretchmar. All rights reserved.\n"
1325    "Copyright 2002 Massachusetts Institute of Technology\n"
1326    "\n"
1327    "This program is free software. You can redistribute it and/or\n"
1328    "modify under the terms of the Sleepycat License. Use the \n"
1329    "':show license' command to display the full license\n"
1330  );
1331}
1332
1333void owl_function_info(void)
1334{
1335  const owl_message *m;
1336  owl_fmtext fm, attrfm;
1337  const owl_view *v;
1338#ifdef HAVE_LIBZEPHYR
1339  const ZNotice_t *n;
1340#endif
1341
1342  owl_fmtext_init_null(&fm);
1343 
1344  v=owl_global_get_current_view(&g);
1345  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1346  if (!m || owl_view_get_size(v)==0) {
1347    owl_function_error("No message selected\n");
1348    return;
1349  }
1350
1351  owl_fmtext_append_bold(&fm, "General Information:\n");
1352  owl_fmtext_appendf_normal(&fm, "  Msg Id    : %i\n", owl_message_get_id(m));
1353
1354  owl_fmtext_append_normal(&fm, "  Type      : ");
1355  owl_fmtext_append_bold(&fm, owl_message_get_type(m));
1356  owl_fmtext_append_normal(&fm, "\n");
1357
1358  if (owl_message_is_direction_in(m)) {
1359    owl_fmtext_append_normal(&fm, "  Direction : in\n");
1360  } else if (owl_message_is_direction_out(m)) {
1361    owl_fmtext_append_normal(&fm, "  Direction : out\n");
1362  } else if (owl_message_is_direction_none(m)) {
1363    owl_fmtext_append_normal(&fm, "  Direction : none\n");
1364  } else {
1365    owl_fmtext_append_normal(&fm, "  Direction : unknown\n");
1366  }
1367
1368  owl_fmtext_appendf_normal(&fm, "  Time      : %s\n", owl_message_get_timestr(m));
1369
1370  if (!owl_message_is_type_admin(m)) {
1371    owl_fmtext_appendf_normal(&fm, "  Sender    : %s\n", owl_message_get_sender(m));
1372    owl_fmtext_appendf_normal(&fm, "  Recipient : %s\n", owl_message_get_recipient(m));
1373  }
1374
1375  if (owl_message_is_type_zephyr(m)) {
1376    owl_fmtext_append_bold(&fm, "\nZephyr Specific Information:\n");
1377   
1378    owl_fmtext_appendf_normal(&fm, "  Class     : %s\n", owl_message_get_class(m));
1379    owl_fmtext_appendf_normal(&fm, "  Instance  : %s\n", owl_message_get_instance(m));
1380    owl_fmtext_appendf_normal(&fm, "  Opcode    : %s\n", owl_message_get_opcode(m));
1381#ifdef HAVE_LIBZEPHYR
1382    if (owl_message_is_direction_in(m)) {
1383      char *ptr, tmpbuff[1024];
1384      int i, j, fields, len;
1385
1386      n=owl_message_get_notice(m);
1387
1388      if (!owl_message_is_pseudo(m)) {
1389        owl_fmtext_append_normal(&fm, "  Kind      : ");
1390        if (n->z_kind==UNSAFE) {
1391          owl_fmtext_append_normal(&fm, "UNSAFE\n");
1392        } else if (n->z_kind==UNACKED) {
1393          owl_fmtext_append_normal(&fm, "UNACKED\n");
1394        } else if (n->z_kind==ACKED) {
1395          owl_fmtext_append_normal(&fm, "ACKED\n");
1396        } else if (n->z_kind==HMACK) {
1397          owl_fmtext_append_normal(&fm, "HMACK\n");
1398        } else if (n->z_kind==HMCTL) {
1399          owl_fmtext_append_normal(&fm, "HMCTL\n");
1400        } else if (n->z_kind==SERVACK) {
1401          owl_fmtext_append_normal(&fm, "SERVACK\n");
1402        } else if (n->z_kind==SERVNAK) {
1403          owl_fmtext_append_normal(&fm, "SERVNACK\n");
1404        } else if (n->z_kind==CLIENTACK) {
1405          owl_fmtext_append_normal(&fm, "CLIENTACK\n");
1406        } else if (n->z_kind==STAT) {
1407          owl_fmtext_append_normal(&fm, "STAT\n");
1408        } else {
1409          owl_fmtext_append_normal(&fm, "ILLEGAL VALUE\n");
1410        }
1411      }
1412      owl_fmtext_appendf_normal(&fm, "  Host      : %s\n", owl_message_get_hostname(m));
1413
1414      if (!owl_message_is_pseudo(m)) {
1415        owl_fmtext_append_normal(&fm, "\n");
1416        owl_fmtext_appendf_normal(&fm, "  Port      : %i\n", ntohs(n->z_port));
1417        owl_fmtext_appendf_normal(&fm, "  Auth      : %s\n", owl_zephyr_get_authstr(n));
1418
1419        /* FIXME make these more descriptive */
1420        owl_fmtext_appendf_normal(&fm, "  Checkd Ath: %i\n", n->z_checked_auth);
1421        owl_fmtext_appendf_normal(&fm, "  Multi notc: %s\n", n->z_multinotice);
1422        owl_fmtext_appendf_normal(&fm, "  Num other : %i\n", n->z_num_other_fields);
1423        owl_fmtext_appendf_normal(&fm, "  Msg Len   : %i\n", n->z_message_len);
1424
1425        fields=owl_zephyr_get_num_fields(n);
1426        owl_fmtext_appendf_normal(&fm, "  Fields    : %i\n", fields);
1427
1428        for (i=0; i<fields; i++) {
1429          ptr=owl_zephyr_get_field_as_utf8(n, i+1);
1430          len=strlen(ptr);
1431          if (len<30) {
1432            strncpy(tmpbuff, ptr, len);
1433            tmpbuff[len]='\0';
1434          } else {
1435            strncpy(tmpbuff, ptr, 30);
1436            tmpbuff[30]='\0';
1437            strcat(tmpbuff, "...");
1438          }
1439          owl_free(ptr);
1440
1441          for (j=0; j<strlen(tmpbuff); j++) {
1442            if (tmpbuff[j]=='\n') tmpbuff[j]='~';
1443            if (tmpbuff[j]=='\r') tmpbuff[j]='!';
1444          }
1445
1446          owl_fmtext_appendf_normal(&fm, "  Field %i   : %s\n", i+1, tmpbuff);
1447        }
1448        owl_fmtext_appendf_normal(&fm, "  Default Fm: %s\n", n->z_default_format);
1449      }
1450
1451    }
1452#endif
1453  }
1454
1455  owl_fmtext_append_bold(&fm, "\nOwl Message Attributes:\n");
1456  owl_message_attributes_tofmtext(m, &attrfm);
1457  owl_fmtext_append_fmtext(&fm, &attrfm);
1458 
1459  owl_function_popless_fmtext(&fm);
1460  owl_fmtext_cleanup(&fm);
1461  owl_fmtext_cleanup(&attrfm);
1462}
1463
1464/* print the current message in a popup window.
1465 * Use the 'default' style regardless of whatever
1466 * style the user may be using
1467 */
1468void owl_function_curmsg_to_popwin(void)
1469{
1470  const owl_view *v;
1471  const owl_message *m;
1472  const owl_style *s;
1473  owl_fmtext fm;
1474
1475  v=owl_global_get_current_view(&g);
1476  s=owl_global_get_style_by_name(&g, "default");
1477
1478  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1479
1480  if (!m || owl_view_get_size(v)==0) {
1481    owl_function_error("No current message");
1482    return;
1483  }
1484
1485  owl_fmtext_init_null(&fm);
1486  owl_style_get_formattext(s, &fm, m);
1487
1488  owl_function_popless_fmtext(&fm);
1489  owl_fmtext_cleanup(&fm);
1490}
1491
1492void owl_function_page_curmsg(int step)
1493{
1494  /* scroll down or up within the current message IF the message is truncated */
1495
1496  int offset, curmsg, lines;
1497  const owl_view *v;
1498  owl_message *m;
1499
1500  offset=owl_global_get_curmsg_vert_offset(&g);
1501  v=owl_global_get_current_view(&g);
1502  curmsg=owl_global_get_curmsg(&g);
1503  m=owl_view_get_element(v, curmsg);
1504  if (!m || owl_view_get_size(v)==0) return;
1505  lines=owl_message_get_numlines(m);
1506
1507  if (offset==0) {
1508    /* Bail if the curmsg isn't the last one displayed */
1509    if (curmsg != owl_mainwin_get_last_msg(owl_global_get_mainwin(&g))) {
1510      owl_function_makemsg("The entire message is already displayed");
1511      return;
1512    }
1513   
1514    /* Bail if we're not truncated */
1515    if (!owl_mainwin_is_curmsg_truncated(owl_global_get_mainwin(&g))) {
1516      owl_function_makemsg("The entire message is already displayed");
1517      return;
1518    }
1519  }
1520 
1521 
1522  /* don't scroll past the last line */
1523  if (step>0) {
1524    if (offset+step > lines-1) {
1525      owl_global_set_curmsg_vert_offset(&g, lines-1);
1526    } else {
1527      owl_global_set_curmsg_vert_offset(&g, offset+step);
1528    }
1529  }
1530
1531  /* would we be before the beginning of the message? */
1532  if (step<0) {
1533    if (offset+step<0) {
1534      owl_global_set_curmsg_vert_offset(&g, 0);
1535    } else {
1536      owl_global_set_curmsg_vert_offset(&g, offset+step);
1537    }
1538  }
1539 
1540  /* redisplay */
1541  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1542}
1543
1544void owl_function_resize_typwin(int newsize)
1545{
1546  owl_global_set_typwin_lines(&g, newsize);
1547  owl_mainpanel_layout_contents(&g.mainpanel);
1548}
1549
1550void owl_function_mainwin_pagedown(void)
1551{
1552  int i;
1553
1554  i=owl_mainwin_get_last_msg(owl_global_get_mainwin(&g));
1555  if (i<0) return;
1556  if (owl_mainwin_is_last_msg_truncated(owl_global_get_mainwin(&g))
1557      && (owl_global_get_curmsg(&g) < i)
1558      && (i>0)) {
1559    i--;
1560  }
1561  owl_global_set_curmsg(&g, i);
1562  owl_function_nextmsg();
1563}
1564
1565void owl_function_mainwin_pageup(void)
1566{
1567  owl_global_set_curmsg(&g, owl_global_get_topmsg(&g));
1568  owl_function_prevmsg();
1569}
1570
1571void owl_function_getsubs(void)
1572{
1573  char *buff;
1574
1575  buff=owl_zephyr_getsubs();
1576
1577  if (buff) {
1578    owl_function_popless_text(buff);
1579  } else {
1580    owl_function_popless_text("Error getting subscriptions");
1581  }
1582           
1583  owl_free(buff);
1584}
1585
1586void owl_function_printallvars(void)
1587{
1588  const char *name;
1589  char var[LINE];
1590  owl_list varnames;
1591  int i, numvarnames;
1592  GString *str   = g_string_new("");
1593
1594  g_string_append_printf(str, "%-20s = %s\n", "VARIABLE", "VALUE");
1595  g_string_append_printf(str, "%-20s   %s\n",  "--------", "-----");
1596  owl_variable_dict_get_names(owl_global_get_vardict(&g), &varnames);
1597  numvarnames = owl_list_get_size(&varnames);
1598  for (i=0; i<numvarnames; i++) {
1599    name = owl_list_get_element(&varnames, i);
1600    if (name && name[0]!='_') {
1601      g_string_append_printf(str, "\n%-20s = ", name);
1602      owl_variable_get_tostring(owl_global_get_vardict(&g), name, var, LINE);
1603      g_string_append(str, var);
1604    }
1605  }
1606  g_string_append(str, "\n");
1607  owl_variable_dict_namelist_cleanup(&varnames);
1608
1609  owl_function_popless_text(str->str);
1610  g_string_free(str, TRUE);
1611}
1612
1613void owl_function_show_variables(void)
1614{
1615  owl_list varnames;
1616  owl_fmtext fm; 
1617  int i, numvarnames;
1618  const char *varname;
1619
1620  owl_fmtext_init_null(&fm);
1621  owl_fmtext_append_bold(&fm, 
1622      "Variables: (use 'show variable <name>' for details)\n");
1623  owl_variable_dict_get_names(owl_global_get_vardict(&g), &varnames);
1624  numvarnames = owl_list_get_size(&varnames);
1625  for (i=0; i<numvarnames; i++) {
1626    varname = owl_list_get_element(&varnames, i);
1627    if (varname && varname[0]!='_') {
1628      owl_variable_describe(owl_global_get_vardict(&g), varname, &fm);
1629    }
1630  }
1631  owl_variable_dict_namelist_cleanup(&varnames);
1632  owl_function_popless_fmtext(&fm);
1633  owl_fmtext_cleanup(&fm);
1634}
1635
1636void owl_function_show_variable(const char *name)
1637{
1638  owl_fmtext fm; 
1639
1640  owl_fmtext_init_null(&fm);
1641  owl_variable_get_help(owl_global_get_vardict(&g), name, &fm);
1642  owl_function_popless_fmtext(&fm);
1643  owl_fmtext_cleanup(&fm);
1644}
1645
1646/* note: this applies to global message list, not to view.
1647 * If flag is 1, deletes.  If flag is 0, undeletes. */
1648void owl_function_delete_by_id(int id, int flag)
1649{
1650  const owl_messagelist *ml;
1651  owl_message *m;
1652  ml = owl_global_get_msglist(&g);
1653  m = owl_messagelist_get_by_id(ml, id);
1654  if (m) {
1655    if (flag == 1) {
1656      owl_message_mark_delete(m);
1657    } else if (flag == 0) {
1658      owl_message_unmark_delete(m);
1659    }
1660    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1661  } else {
1662    owl_function_error("No message with id %d: unable to mark for (un)delete",id);
1663  }
1664}
1665
1666void owl_function_delete_automsgs(void)
1667{
1668  /* mark for deletion all messages in the current view that match the
1669   * 'trash' filter */
1670
1671  int i, j, count;
1672  owl_message *m;
1673  const owl_view *v;
1674  const owl_filter *f;
1675
1676  /* get the trash filter */
1677  f=owl_global_get_filter(&g, "trash");
1678  if (!f) {
1679    owl_function_error("No trash filter defined");
1680    return;
1681  }
1682
1683  v=owl_global_get_current_view(&g);
1684
1685  count=0;
1686  j=owl_view_get_size(v);
1687  for (i=0; i<j; i++) {
1688    m=owl_view_get_element(v, i);
1689    if (owl_filter_message_match(f, m)) {
1690      count++;
1691      owl_message_mark_delete(m);
1692    }
1693  }
1694  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1695  owl_function_makemsg("%i messages marked for deletion", count);
1696}
1697
1698void owl_function_status(void)
1699{
1700  char buff[MAXPATHLEN+1];
1701  time_t start;
1702  int up, days, hours, minutes;
1703  owl_fmtext fm;
1704
1705  owl_fmtext_init_null(&fm);
1706
1707  start=owl_global_get_starttime(&g);
1708
1709  owl_fmtext_append_normal(&fm, "General Information:\n");
1710
1711  owl_fmtext_append_normal(&fm, "  Version: ");
1712  owl_fmtext_append_normal(&fm, OWL_VERSION_STRING);
1713  owl_fmtext_append_normal(&fm, "\n");
1714
1715  owl_fmtext_append_normal(&fm, "  Startup Arguments: ");
1716  owl_fmtext_append_normal(&fm, owl_global_get_startupargs(&g));
1717  owl_fmtext_append_normal(&fm, "\n");
1718
1719  owl_fmtext_append_normal(&fm, "  Current Directory: ");
1720  if(getcwd(buff, MAXPATHLEN) == NULL) {
1721    owl_fmtext_append_normal(&fm, "<Error in getcwd>");
1722  } else {
1723    owl_fmtext_append_normal(&fm, buff);
1724  }
1725  owl_fmtext_append_normal(&fm, "\n");
1726
1727  owl_fmtext_appendf_normal(&fm, "  Startup Time: %s", ctime(&start));
1728
1729  up=owl_global_get_runtime(&g);
1730  days=up/86400;
1731  up-=days*86400;
1732  hours=up/3600;
1733  up-=hours*3600;
1734  minutes=up/60;
1735  up-=minutes*60;
1736  owl_fmtext_appendf_normal(&fm, "  Run Time: %i days %2.2i:%2.2i:%2.2i\n", days, hours, minutes, up);
1737
1738  owl_fmtext_append_normal(&fm, "\nProtocol Options:\n");
1739  owl_fmtext_append_normal(&fm, "  Zephyr included    : ");
1740  if (owl_global_is_havezephyr(&g)) {
1741    owl_fmtext_append_normal(&fm, "yes\n");
1742  } else {
1743    owl_fmtext_append_normal(&fm, "no\n");
1744  }
1745  owl_fmtext_append_normal(&fm, "  AIM included       : yes\n");
1746  owl_fmtext_append_normal(&fm, "  Loopback included  : yes\n");
1747
1748
1749  owl_fmtext_append_normal(&fm, "\nBuild Options:\n");
1750  owl_fmtext_append_normal(&fm, "  Stderr redirection : ");
1751#if OWL_STDERR_REDIR
1752  owl_fmtext_append_normal(&fm, "yes\n");
1753#else
1754  owl_fmtext_append_normal(&fm, "no\n");
1755#endif
1756 
1757
1758  owl_fmtext_append_normal(&fm, "\nAIM Status:\n");
1759  owl_fmtext_append_normal(&fm, "  Logged in: ");
1760  if (owl_global_is_aimloggedin(&g)) {
1761    owl_fmtext_append_normal(&fm, owl_global_get_aim_screenname(&g));
1762    owl_fmtext_append_normal(&fm, "\n");
1763  } else {
1764    owl_fmtext_append_normal(&fm, "(not logged in)\n");
1765  }
1766
1767  owl_fmtext_append_normal(&fm, "  Processing events: ");
1768  if (owl_global_is_doaimevents(&g)) {
1769    owl_fmtext_append_normal(&fm, "yes\n");
1770  } else {
1771    owl_fmtext_append_normal(&fm, "no\n");
1772  }
1773
1774  owl_function_popless_fmtext(&fm);
1775  owl_fmtext_cleanup(&fm);
1776}
1777
1778void owl_function_show_term(void)
1779{
1780  owl_fmtext fm;
1781
1782  owl_fmtext_init_null(&fm);
1783  owl_fmtext_appendf_normal(&fm, "Terminal Lines: %i\nTerminal Columns: %i\n",
1784          owl_global_get_lines(&g),
1785          owl_global_get_cols(&g));
1786
1787  if (owl_global_get_hascolors(&g)) {
1788    owl_fmtext_append_normal(&fm, "Color: Yes\n");
1789    owl_fmtext_appendf_normal(&fm, "Number of color pairs: %i\n", owl_global_get_colorpairs(&g));
1790    owl_fmtext_appendf_normal(&fm, "Can change colors: %s\n", can_change_color() ? "yes" : "no");
1791  } else {
1792    owl_fmtext_append_normal(&fm, "Color: No\n");
1793  }
1794
1795  owl_function_popless_fmtext(&fm);
1796  owl_fmtext_cleanup(&fm);
1797}
1798
1799/* if type = 0 then normal reply.
1800 * if type = 1 then it's a reply to sender
1801 * if enter = 0 then allow the command to be edited
1802 * if enter = 1 then don't wait for editing
1803 */
1804void owl_function_reply(int type, int enter)
1805{
1806  char *buff=NULL;
1807  const owl_message *m;
1808  const owl_filter *f;
1809 
1810  if (owl_view_get_size(owl_global_get_current_view(&g))==0) {
1811    owl_function_error("No message selected");
1812  } else {
1813    char *cmd;
1814   
1815    m=owl_view_get_element(owl_global_get_current_view(&g), owl_global_get_curmsg(&g));
1816    if (!m) {
1817      owl_function_error("No message selected");
1818      return;
1819    }
1820
1821    /* first check if we catch the reply-lockout filter */
1822    f=owl_global_get_filter(&g, "reply-lockout");
1823    if (f) {
1824      if (owl_filter_message_match(f, m)) {
1825        owl_function_error("Sorry, replies to this message have been disabled by the reply-lockout filter");
1826        return;
1827      }
1828    }
1829
1830    /* then check if it's a question and just bring up the command prompt */
1831    if (owl_message_is_question(m)) {
1832      owl_function_start_command("");
1833      return;
1834    }
1835
1836    if((type == 0 &&
1837        (cmd=owl_perlconfig_message_call_method(m, "replycmd", 0, NULL))) ||
1838       (type == 1 &&
1839        (cmd=owl_perlconfig_message_call_method(m, "replysendercmd", 0, NULL)))) {
1840      buff = cmd;
1841    }
1842
1843    if(!buff) {
1844        owl_function_error("I don't know how to reply to that message.");
1845        return;
1846    }
1847
1848    if (enter) {
1849      owl_history *hist = owl_global_get_cmd_history(&g);
1850      owl_history_store(hist, buff);
1851      owl_history_reset(hist);
1852      owl_function_command_norv(buff);
1853    } else {
1854      owl_function_start_command(buff);
1855    }
1856    owl_free(buff);
1857  }
1858}
1859
1860void owl_function_zlocate(int argc, const char *const *argv, int auth)
1861{
1862  owl_fmtext fm;
1863  char *ptr;
1864  char *result;
1865  int i;
1866
1867  owl_fmtext_init_null(&fm);
1868
1869  for (i=0; i<argc; i++) {
1870    ptr = long_zuser(argv[i]);
1871    result = owl_zephyr_zlocate(ptr, auth);
1872    owl_fmtext_append_normal(&fm, result);
1873    owl_free(result);
1874    owl_free(ptr);
1875  }
1876
1877  owl_function_popless_fmtext(&fm);
1878  owl_fmtext_cleanup(&fm);
1879}
1880
1881void owl_callback_command(owl_editwin *e)
1882{
1883  char *rv;
1884  const char *line = owl_editwin_get_text(e);
1885
1886  rv = owl_function_command(line);
1887   if (rv) {
1888    owl_function_makemsg("%s", rv);
1889    owl_free(rv);
1890  }
1891}
1892
1893void owl_function_start_command(const char *line)
1894{
1895  owl_editwin *tw;
1896
1897  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
1898
1899  owl_editwin_set_locktext(tw, "command: ");
1900
1901  owl_editwin_insert_string(tw, line);
1902
1903  owl_global_push_context(&g, OWL_CTX_EDITLINE, tw, "editline");
1904  owl_editwin_set_callback(tw, owl_callback_command);
1905}
1906
1907owl_editwin *owl_function_start_question(const char *line)
1908{
1909  owl_editwin *tw;
1910
1911  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
1912
1913  owl_editwin_set_locktext(tw, line);
1914
1915  owl_global_push_context(&g, OWL_CTX_EDITRESPONSE, tw, "editresponse");
1916  return tw;
1917}
1918
1919owl_editwin *owl_function_start_password(const char *line)
1920{
1921  owl_editwin *tw;
1922
1923  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, NULL);
1924
1925  owl_editwin_set_echochar(tw, '*');
1926
1927  owl_editwin_set_locktext(tw, line);
1928
1929  owl_global_push_context(&g, OWL_CTX_EDITRESPONSE, tw, "editresponse");
1930  return tw;
1931}
1932
1933char *owl_function_exec(int argc, const char *const *argv, const char *buff, int type)
1934{
1935  /* if type == 1 display in a popup
1936   * if type == 2 display an admin messages
1937   * if type == 0 return output
1938   * else display in a popup
1939   */
1940  const char *redirect = " 2>&1 < /dev/null";
1941  char *newbuff;
1942  char *out;
1943  FILE *p;
1944
1945#if OWL_STDERR_REDIR
1946  redirect = " < /dev/null";
1947#endif
1948
1949  if (argc<2) {
1950    owl_function_error("Wrong number of arguments to the exec command");
1951    return NULL;
1952  }
1953
1954  buff = skiptokens(buff, 1);
1955  newbuff = owl_sprintf("%s%s", buff, redirect);
1956
1957  if (type == OWL_OUTPUT_POPUP) {
1958    owl_popexec_new(newbuff);
1959  } else {
1960    p = popen(newbuff, "r");
1961    out = owl_slurp(p);
1962    pclose(p);
1963   
1964    if (type == OWL_OUTPUT_RETURN) {
1965      owl_free(newbuff);
1966      return out;
1967    } else if (type == OWL_OUTPUT_ADMINMSG) {
1968      owl_function_adminmsg(buff, out);
1969    }
1970    owl_free(out);
1971  }
1972  owl_free(newbuff);
1973  return NULL;
1974}
1975
1976char *owl_function_perl(int argc, const char *const *argv, const char *buff, int type)
1977{
1978  /* if type == 1 display in a popup
1979   * if type == 2 display an admin messages
1980   * if type == 0 return output
1981   * else display in a popup
1982   */
1983  char *perlout;
1984
1985  if (argc<2) {
1986    owl_function_error("Wrong number of arguments to perl command");
1987    return NULL;
1988  }
1989
1990  /* consume first token (argv[0]) */
1991  buff = skiptokens(buff, 1);
1992
1993  perlout = owl_perlconfig_execute(buff);
1994  if (perlout) { 
1995    if (type == OWL_OUTPUT_POPUP) {
1996      owl_function_popless_text(perlout);
1997    } else if (type == OWL_OUTPUT_ADMINMSG) {
1998      owl_function_adminmsg(buff, perlout);
1999    } else if (type == OWL_OUTPUT_RETURN) {
2000      return perlout;
2001    }
2002    owl_free(perlout);
2003  }
2004  return NULL;
2005}
2006
2007/* Change the filter associated with the current view.
2008 * This also figures out which message in the new filter
2009 * should have the pointer.
2010 */
2011void owl_function_change_currentview_filter(const char *filtname)
2012{
2013  owl_view *v;
2014  owl_filter *f;
2015  int curid=-1, newpos, curmsg;
2016  const owl_message *curm=NULL;
2017
2018  v=owl_global_get_current_view(&g);
2019
2020  curmsg=owl_global_get_curmsg(&g);
2021  if (curmsg==-1) {
2022    owl_function_debugmsg("Hit the curmsg==-1 case in change_view");
2023  } else {
2024    curm=owl_view_get_element(v, curmsg);
2025    if (curm) {
2026      curid=owl_message_get_id(curm);
2027      owl_view_save_curmsgid(v, curid);
2028    }
2029  }
2030
2031  f=owl_global_get_filter(&g, filtname);
2032  if (!f) {
2033    owl_function_error("Unknown filter %s", filtname);
2034    return;
2035  }
2036
2037  owl_view_new_filter(v, f);
2038
2039  /* Figure out what to set the current message to.
2040   * - If the view we're leaving has messages in it, go to the closest message
2041   *   to the last message pointed to in that view.
2042   * - If the view we're leaving is empty, try to restore the position
2043   *   from the last time we were in the new view.  */
2044  if (curm) {
2045    newpos = owl_view_get_nearest_to_msgid(v, curid);
2046  } else {
2047    newpos = owl_view_get_nearest_to_saved(v);
2048  }
2049
2050  owl_global_set_curmsg(&g, newpos);
2051  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
2052  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2053  owl_global_set_direction_downwards(&g);
2054}
2055
2056/* Create a new filter, or replace an existing one
2057 * with a new definition.
2058 */
2059void owl_function_create_filter(int argc, const char *const *argv)
2060{
2061  owl_filter *f;
2062  const owl_view *v;
2063  int inuse = 0;
2064
2065  if (argc < 2) {
2066    owl_function_error("Wrong number of arguments to filter command");
2067    return;
2068  }
2069
2070  owl_function_debugmsg("owl_function_create_filter: starting to create filter named %s", argv[1]);
2071
2072  v=owl_global_get_current_view(&g);
2073
2074  /* don't touch the all filter */
2075  if (!strcmp(argv[1], "all")) {
2076    owl_function_error("You may not change the 'all' filter.");
2077    return;
2078  }
2079
2080  /* deal with the case of trying change the filter color */
2081  if (argc==4 && !strcmp(argv[2], "-c")) {
2082    f=owl_global_get_filter(&g, argv[1]);
2083    if (!f) {
2084      owl_function_error("The filter '%s' does not exist.", argv[1]);
2085      return;
2086    }
2087    if (owl_util_string_to_color(argv[3])==OWL_COLOR_INVALID) {
2088      owl_function_error("The color '%s' is not available.", argv[3]);
2089      return;
2090    }
2091    owl_filter_set_fgcolor(f, owl_util_string_to_color(argv[3]));
2092    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2093    return;
2094  }
2095  if (argc==4 && !strcmp(argv[2], "-b")) {
2096    f=owl_global_get_filter(&g, argv[1]);
2097    if (!f) {
2098      owl_function_error("The filter '%s' does not exist.", argv[1]);
2099      return;
2100    }
2101    if (owl_util_string_to_color(argv[3])==OWL_COLOR_INVALID) {
2102      owl_function_error("The color '%s' is not available.", argv[3]);
2103      return;
2104    }
2105    owl_filter_set_bgcolor(f, owl_util_string_to_color(argv[3]));
2106    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2107    return;
2108  }
2109
2110  /* create the filter and check for errors */
2111  f = owl_filter_new(argv[1], argc-2, argv+2);
2112  if (f == NULL) {
2113    owl_function_error("Invalid filter");
2114    return;
2115  }
2116
2117  /* if the named filter is in use by the current view, remember it */
2118  if (!strcmp(owl_view_get_filtname(v), argv[1])) {
2119    inuse=1;
2120  }
2121
2122  /* if the named filter already exists, nuke it */
2123  if (owl_global_get_filter(&g, argv[1])) {
2124    owl_global_remove_filter(&g, argv[1]);
2125  }
2126
2127  /* add the filter */
2128  owl_global_add_filter(&g, f);
2129
2130  /* if it was in use by the current view then update */
2131  if (inuse) {
2132    owl_function_change_currentview_filter(argv[1]);
2133  }
2134  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2135}
2136
2137/* If 'filtername' does not start with 'not-' create a filter named
2138 * 'not-<filtername>' defined as "not filter <filtername>".  If the
2139 * filter 'not-<filtername>' already exists, do not overwrite it.  If
2140 * 'filtername' begins with 'not-' and a filter 'filtername' already
2141 * exists, then do nothing.  If the filter 'filtername' does not
2142 * exist, create it and define it as 'not filter <filtername>'
2143 *
2144 * Returns the name of the negated filter, which the caller must free.
2145 */
2146char *owl_function_create_negative_filter(const char *filtername)
2147{
2148  char *newname;
2149  const owl_filter *tmpfilt;
2150  const char *argv[5];
2151
2152  owl_function_debugmsg("owl_function_create_negative_filter");
2153 
2154  if (!strncmp(filtername, "not-", 4)) {
2155    newname=owl_strdup(filtername+4);
2156  } else {
2157    newname=owl_sprintf("not-%s", filtername);
2158  }
2159
2160  tmpfilt=owl_global_get_filter(&g, newname);
2161  if (!tmpfilt) {
2162    argv[0]="filter"; /* anything is fine here */
2163    argv[1]=newname;
2164    argv[2]="not";
2165    argv[3]="filter";
2166    argv[4]=filtername;
2167    owl_function_create_filter(5, argv);
2168  }
2169
2170  owl_function_debugmsg("owl_function_create_negative_filter: returning with %s", newname);
2171  return(newname);
2172}
2173
2174void owl_function_show_filters(void)
2175{
2176  const owl_filter *f;
2177  GList *fl;
2178  owl_fmtext fm;
2179
2180  owl_fmtext_init_null(&fm);
2181
2182  owl_fmtext_append_bold(&fm, "Filters:\n");
2183
2184  for (fl = g.filterlist; fl; fl = g_list_next(fl)) {
2185    f = fl->data;
2186    owl_fmtext_append_normal(&fm, "   ");
2187    if (owl_global_get_hascolors(&g)) {
2188      owl_fmtext_append_normal_color(&fm, owl_filter_get_name(f), owl_filter_get_fgcolor(f), owl_filter_get_bgcolor(f));
2189    } else {
2190      owl_fmtext_append_normal(&fm, owl_filter_get_name(f));
2191    }
2192    owl_fmtext_append_normal(&fm, "\n");
2193  }
2194  owl_function_popless_fmtext(&fm);
2195  owl_fmtext_cleanup(&fm);
2196}
2197
2198void owl_function_show_filter(const char *name)
2199{
2200  const owl_filter *f;
2201  char *buff, *tmp;
2202
2203  f=owl_global_get_filter(&g, name);
2204  if (!f) {
2205    owl_function_error("There is no filter named %s", name);
2206    return;
2207  }
2208  tmp = owl_filter_print(f);
2209  buff = owl_sprintf("%s: %s", owl_filter_get_name(f), tmp);
2210  owl_function_popless_text(buff);
2211  owl_free(buff);
2212  owl_free(tmp);
2213}
2214
2215void owl_function_show_zpunts(void)
2216{
2217  const owl_filter *f;
2218  const owl_list *fl;
2219  char buff[5000];
2220  char *tmp;
2221  owl_fmtext fm;
2222  int i, j;
2223
2224  owl_fmtext_init_null(&fm);
2225
2226  fl=owl_global_get_puntlist(&g);
2227  j=owl_list_get_size(fl);
2228  owl_fmtext_append_bold(&fm, "Active zpunt filters:\n");
2229
2230  for (i=0; i<j; i++) {
2231    f=owl_list_get_element(fl, i);
2232    snprintf(buff, sizeof(buff), "[% 2d] ", i+1);
2233    owl_fmtext_append_normal(&fm, buff);
2234    tmp = owl_filter_print(f);
2235    owl_fmtext_append_normal(&fm, tmp);
2236    owl_free(tmp);
2237  }
2238  owl_function_popless_fmtext(&fm);
2239  owl_fmtext_cleanup(&fm);
2240}
2241
2242/* Create a filter for a class, instance if one doesn't exist.  If
2243 * instance is NULL then catch all messgaes in the class.  Returns the
2244 * name of the filter, which the caller must free.
2245 * If 'related' is nonzero, encompass unclasses and .d classes as well.
2246 */
2247char *owl_function_classinstfilt(const char *c, const char *i, int related) 
2248{
2249  owl_filter *f;
2250  char *argbuff, *filtname;
2251  char *tmpclass, *tmpinstance = NULL;
2252  char *class, *instance = NULL;
2253
2254  if (related) {
2255    class = owl_util_baseclass(c);
2256    if (i) {
2257      instance = owl_util_baseclass(i);
2258    }
2259  } else {
2260    class = owl_strdup(c);
2261    if (i) {
2262      instance = owl_strdup(i);
2263    }
2264  }
2265
2266  /* name for the filter */
2267  if (!instance) {
2268    filtname = owl_sprintf("%sclass-%s", related ? "related-" : "", class);
2269  } else {
2270    filtname = owl_sprintf("%sclass-%s-instance-%s", related ? "related-" : "", class, instance);
2271  }
2272  /* downcase it */
2273  {
2274    char *temp = g_utf8_strdown(filtname, -1);
2275    if (temp) {
2276      owl_free(filtname);
2277      filtname = temp;
2278    }
2279  }
2280  /* turn spaces, single quotes, and double quotes into dots */
2281  owl_text_tr(filtname, ' ', '.');
2282  owl_text_tr(filtname, '\'', '.');
2283  owl_text_tr(filtname, '"', '.');
2284 
2285  /* if it already exists then go with it.  This lets users override */
2286  if (owl_global_get_filter(&g, filtname)) {
2287    goto done;
2288  }
2289
2290  /* create the new filter */
2291  tmpclass=owl_text_quote(class, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2292  owl_text_tr(tmpclass, ' ', '.');
2293  owl_text_tr(tmpclass, '\'', '.');
2294  owl_text_tr(tmpclass, '"', '.');
2295  if (instance) {
2296    tmpinstance=owl_text_quote(instance, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2297    owl_text_tr(tmpinstance, ' ', '.');
2298    owl_text_tr(tmpinstance, '\'', '.');
2299    owl_text_tr(tmpinstance, '"', '.');
2300  }
2301
2302  argbuff = owl_sprintf(related ? "class ^(un)*%s(\\.d)*$" : "class ^%s$", tmpclass);
2303  if (tmpinstance) {
2304    char *tmp = argbuff;
2305    argbuff = owl_sprintf(related ? "%s and ( instance ^(un)*%s(\\.d)*$ )" : "%s and instance ^%s$", tmp, tmpinstance);
2306    owl_free(tmp);
2307  }
2308  owl_free(tmpclass);
2309  if (tmpinstance) owl_free(tmpinstance);
2310
2311  f = owl_filter_new_fromstring(filtname, argbuff);
2312
2313  /* add it to the global list */
2314  owl_global_add_filter(&g, f);
2315
2316  owl_free(argbuff);
2317done:
2318  owl_free(class);
2319  if (instance) {
2320    owl_free(instance);
2321  }
2322  return(filtname);
2323}
2324
2325/* Create a filter for personal zephyrs to or from the specified
2326 * zephyr user.  Includes login/logout notifications for the user.
2327 * The name of the filter will be 'user-<user>'.  If a filter already
2328 * exists with this name, no new filter will be created.  This allows
2329 * the configuration to override this function.  Returns the name of
2330 * the filter, which the caller must free.
2331 */
2332char *owl_function_zuserfilt(const char *user)
2333{
2334  owl_filter *f;
2335  char *argbuff, *longuser, *esclonguser, *shortuser, *filtname;
2336
2337  /* stick the local realm on if it's not there */
2338  longuser=long_zuser(user);
2339  shortuser=short_zuser(user);
2340
2341  /* name for the filter */
2342  filtname=owl_sprintf("user-%s", shortuser);
2343
2344  /* if it already exists then go with it.  This lets users override */
2345  if (owl_global_get_filter(&g, filtname)) {
2346    return filtname;
2347  }
2348
2349  /* create the new-internal filter */
2350  esclonguser = owl_text_quote(longuser, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2351
2352  argbuff=owl_sprintf("( type ^zephyr$ and filter personal and "
2353      "( ( direction ^in$ and sender ^%1$s$ ) or ( direction ^out$ and "
2354      "recipient ^%1$s$ ) ) ) or ( ( class ^login$ ) and ( sender ^%1$s$ ) )",
2355      esclonguser);
2356
2357  f = owl_filter_new_fromstring(filtname, argbuff);
2358
2359  /* add it to the global list */
2360  owl_global_add_filter(&g, f);
2361
2362  /* free stuff */
2363  owl_free(argbuff);
2364  owl_free(longuser);
2365  owl_free(esclonguser);
2366  owl_free(shortuser);
2367
2368  return(filtname);
2369}
2370
2371/* Create a filter for AIM IM messages to or from the specified
2372 * screenname.  The name of the filter will be 'aimuser-<user>'.  If a
2373 * filter already exists with this name, no new filter will be
2374 * created.  This allows the configuration to override this function.
2375 * Returns the name of the filter, which the caller must free.
2376 */
2377char *owl_function_aimuserfilt(const char *user)
2378{
2379  owl_filter *f;
2380  char *argbuff, *filtname;
2381  char *escuser;
2382
2383  /* name for the filter */
2384  filtname=owl_sprintf("aimuser-%s", user);
2385
2386  /* if it already exists then go with it.  This lets users override */
2387  if (owl_global_get_filter(&g, filtname)) {
2388    return(owl_strdup(filtname));
2389  }
2390
2391  /* create the new-internal filter */
2392  escuser = owl_text_quote(user, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2393
2394  argbuff = owl_sprintf(
2395      "( type ^aim$ and ( ( sender ^%1$s$ and recipient ^%2$s$ ) or "
2396      "( sender ^%2$s$ and recipient ^%1$s$ ) ) )",
2397      escuser, owl_global_get_aim_screenname_for_filters(&g));
2398
2399  f = owl_filter_new_fromstring(filtname, argbuff);
2400
2401  /* add it to the global list */
2402  owl_global_add_filter(&g, f);
2403
2404  /* free stuff */
2405  owl_free(argbuff);
2406  owl_free(escuser);
2407
2408  return(filtname);
2409}
2410
2411char *owl_function_typefilt(const char *type)
2412{
2413  owl_filter *f;
2414  char *argbuff, *filtname, *esctype;
2415
2416  /* name for the filter */
2417  filtname=owl_sprintf("type-%s", type);
2418
2419  /* if it already exists then go with it.  This lets users override */
2420  if (owl_global_get_filter(&g, filtname)) {
2421    return filtname;
2422  }
2423
2424  /* create the new-internal filter */
2425  esctype = owl_text_quote(type, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2426
2427  argbuff = owl_sprintf("type ^%s$", esctype);
2428
2429  f = owl_filter_new_fromstring(filtname, argbuff);
2430
2431  /* add it to the global list */
2432  owl_global_add_filter(&g, f);
2433
2434  /* free stuff */
2435  owl_free(argbuff);
2436  owl_free(esctype);
2437
2438  return filtname;
2439}
2440
2441/* If flag is 1, marks for deletion.  If flag is 0,
2442 * unmarks for deletion. */
2443void owl_function_delete_curview_msgs(int flag)
2444{
2445  const owl_view *v;
2446  int i, j;
2447
2448  v=owl_global_get_current_view(&g);
2449  j=owl_view_get_size(v);
2450  for (i=0; i<j; i++) {
2451    if (flag == 1) {
2452      owl_message_mark_delete(owl_view_get_element(v, i));
2453    } else if (flag == 0) {
2454      owl_message_unmark_delete(owl_view_get_element(v, i));
2455    }
2456  }
2457
2458  owl_function_makemsg("%i messages marked for %sdeletion", j, flag?"":"un");
2459
2460  owl_mainwin_redisplay(owl_global_get_mainwin(&g)); 
2461}
2462
2463static char *owl_function_smartfilter_cc(const owl_message *m) {
2464  const char *ccs;
2465  char *filtname;
2466  char *text;
2467  owl_filter *f;
2468
2469  ccs = owl_message_get_attribute_value(m, "zephyr_ccs");
2470
2471  filtname = owl_sprintf("conversation-%s", ccs);
2472  owl_text_tr(filtname, ' ', '-');
2473
2474  if (owl_global_get_filter(&g, filtname)) {
2475    return filtname;
2476  }
2477
2478  text = owl_sprintf("type ^zephyr$ and filter personal and "
2479                     "zephyr_ccs ^%s%s%s$",
2480                     owl_getquoting(ccs), ccs, owl_getquoting(ccs));
2481
2482  f = owl_filter_new_fromstring(filtname, text);
2483
2484  owl_global_add_filter(&g, f);
2485
2486  owl_free(text);
2487
2488  return filtname;
2489}
2490
2491/* Create a filter based on the current message.  Returns the name of
2492 * a filter or null.  The caller must free this name.
2493 *
2494 * if the curmsg is a personal zephyr return a filter name
2495 *    to the zephyr conversation with that user.
2496 * If the curmsg is a zephyr class message, instance foo, recip *,
2497 *    return a filter name to the class, inst.
2498 * If the curmsg is a zephyr class message and type==0 then
2499 *    return a filter name for just the class.
2500 * If the curmsg is a zephyr class message and type==1 then
2501 *    return a filter name for the class and instance.
2502 * If the curmsg is a personal AIM message returna  filter
2503 *    name to the AIM conversation with that user
2504 */
2505char *owl_function_smartfilter(int type, int invert_related)
2506{
2507  const owl_view *v;
2508  const owl_message *m;
2509  char *zperson, *filtname=NULL;
2510  const char *argv[2];
2511  int related = owl_global_is_narrow_related(&g) ^ invert_related;
2512
2513  v=owl_global_get_current_view(&g);
2514  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
2515
2516  if (!m || owl_view_get_size(v)==0) {
2517    owl_function_error("No message selected\n");
2518    return(NULL);
2519  }
2520
2521  /* very simple handling of admin messages for now */
2522  if (owl_message_is_type_admin(m)) {
2523    return(owl_function_typefilt("admin"));
2524  }
2525
2526  /* very simple handling of loopback messages for now */
2527  if (owl_message_is_type_loopback(m)) {
2528    return(owl_function_typefilt("loopback"));
2529  }
2530
2531  /* aim messages */
2532  if (owl_message_is_type_aim(m)) {
2533    if (owl_message_is_direction_in(m)) {
2534      filtname=owl_function_aimuserfilt(owl_message_get_sender(m));
2535    } else if (owl_message_is_direction_out(m)) {
2536      filtname=owl_function_aimuserfilt(owl_message_get_recipient(m));
2537    }
2538    return(filtname);
2539  }
2540
2541  /* narrow personal and login messages to the sender or recip as appropriate */
2542  if (owl_message_is_type_zephyr(m)) {
2543    if (owl_message_is_personal(m) || owl_message_is_loginout(m)) {
2544      if (owl_message_get_attribute_value(m, "zephyr_ccs") != NULL) {
2545        return owl_function_smartfilter_cc(m);
2546      }
2547
2548      if (owl_message_is_direction_in(m)) {
2549        zperson=short_zuser(owl_message_get_sender(m));
2550      } else {
2551        zperson=short_zuser(owl_message_get_recipient(m));
2552      }
2553      filtname=owl_function_zuserfilt(zperson);
2554      owl_free(zperson);
2555      return(filtname);
2556    }
2557
2558    /* narrow class MESSAGE, instance foo, recip * messages to class, inst */
2559    if (!strcasecmp(owl_message_get_class(m), "message")) {
2560      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m), related);
2561      return(filtname);
2562    }
2563
2564    /* otherwise narrow to the class */
2565    if (type==0) {
2566      filtname=owl_function_classinstfilt(owl_message_get_class(m), NULL, related);
2567    } else if (type==1) {
2568      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m), related);
2569    }
2570    return(filtname);
2571  }
2572
2573  /* pass it off to perl */
2574  argv[0] = type ? "1" : "0";
2575  argv[1] = related ? "1" : "0";
2576  return owl_perlconfig_message_call_method(m, "smartfilter", 2, argv);
2577}
2578
2579void owl_function_smartzpunt(int type)
2580{
2581  /* Starts a zpunt command based on the current class,instance pair.
2582   * If type=0, uses just class.  If type=1, uses instance as well. */
2583  const owl_view *v;
2584  const owl_message *m;
2585  const char *cmdprefix, *mclass, *minst;
2586  char *cmd;
2587 
2588  v=owl_global_get_current_view(&g);
2589  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
2590
2591  if (!m || owl_view_get_size(v)==0) {
2592    owl_function_error("No message selected\n");
2593    return;
2594  }
2595
2596  /* for now we skip admin messages. */
2597  if (owl_message_is_type_admin(m)
2598      || owl_message_is_loginout(m)
2599      || !owl_message_is_type_zephyr(m)) {
2600    owl_function_error("smartzpunt doesn't support this message type.");
2601    return;
2602  }
2603
2604  mclass = owl_message_get_class(m);
2605  minst = owl_message_get_instance(m);
2606  if (!mclass || !*mclass || *mclass==' '
2607      || (!strcasecmp(mclass, "message") && !strcasecmp(minst, "personal"))
2608      || (type && (!minst || !*minst|| *minst==' '))) {
2609    owl_function_error("smartzpunt can't safely do this for <%s,%s>",
2610                         mclass, minst);
2611  } else {
2612    cmdprefix = "start-command zpunt ";
2613    cmd = owl_malloc(strlen(cmdprefix)+strlen(mclass)+strlen(minst)+10);
2614    strcpy(cmd, cmdprefix);
2615    strcat(cmd, owl_getquoting(mclass));
2616    strcat(cmd, mclass);
2617    strcat(cmd, owl_getquoting(mclass));
2618    if (type) {
2619      strcat(cmd, " ");
2620      strcat(cmd, owl_getquoting(minst));
2621      strcat(cmd, minst);
2622      strcat(cmd, owl_getquoting(minst));
2623    } else {
2624      strcat(cmd, " *");
2625    }
2626    owl_function_command(cmd);
2627    owl_free(cmd);
2628  }
2629}
2630
2631/* Set the color of the current view's filter to
2632 * be 'color'
2633 */
2634void owl_function_color_current_filter(const char *fgcolor, const char *bgcolor)
2635{
2636  const char *name;
2637
2638  name=owl_view_get_filtname(owl_global_get_current_view(&g));
2639  owl_function_color_filter(name, fgcolor, bgcolor);
2640}
2641
2642/* Set the color of the filter 'filter' to be 'color'.  If the color
2643 * name does not exist, return -1, if the filter does not exist or is
2644 * the "all" filter, return -2.  Return 0 on success
2645 */
2646int owl_function_color_filter(const char *filtname, const char *fgcolor, const char *bgcolor)
2647{
2648  owl_filter *f;
2649
2650  f=owl_global_get_filter(&g, filtname);
2651  if (!f) {
2652    owl_function_error("Unknown filter");
2653    return(-2);
2654  }
2655
2656  /* don't touch the all filter */
2657  if (!strcmp(filtname, "all")) {
2658    owl_function_error("You may not change the 'all' filter.");
2659    return(-2);
2660  }
2661
2662  if (owl_util_string_to_color(fgcolor)==OWL_COLOR_INVALID) {
2663    owl_function_error("No color named '%s' avilable.", fgcolor);
2664    return(-1);
2665  }
2666
2667
2668  if (bgcolor != NULL) {
2669    if (owl_util_string_to_color(bgcolor)==OWL_COLOR_INVALID) {
2670      owl_function_error("No color named '%s' avilable.", bgcolor);
2671      return(-1);
2672    }
2673    owl_filter_set_bgcolor(f, owl_util_string_to_color(bgcolor));
2674  }
2675  owl_filter_set_fgcolor(f, owl_util_string_to_color(fgcolor));
2676 
2677  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2678  return(0);
2679}
2680
2681void owl_function_show_colors(void)
2682{
2683  owl_fmtext fm;
2684  int i; 
2685 
2686  owl_fmtext_init_null(&fm);
2687  owl_fmtext_append_normal(&fm, "default: ");
2688  owl_fmtext_append_normal_color(&fm, "default\n", OWL_COLOR_DEFAULT, OWL_COLOR_DEFAULT);
2689
2690  owl_fmtext_append_normal(&fm,"red:      ");
2691  owl_fmtext_append_normal_color(&fm, "red\n", OWL_COLOR_RED, OWL_COLOR_DEFAULT);
2692
2693  owl_fmtext_append_normal(&fm,"green:    ");
2694  owl_fmtext_append_normal_color(&fm, "green\n", OWL_COLOR_GREEN, OWL_COLOR_DEFAULT);
2695
2696  owl_fmtext_append_normal(&fm,"yellow:   ");
2697  owl_fmtext_append_normal_color(&fm, "yellow\n", OWL_COLOR_YELLOW, OWL_COLOR_DEFAULT);
2698
2699  owl_fmtext_append_normal(&fm,"blue:     ");
2700  owl_fmtext_append_normal_color(&fm, "blue\n", OWL_COLOR_BLUE, OWL_COLOR_DEFAULT);
2701
2702  owl_fmtext_append_normal(&fm,"magenta:  ");
2703  owl_fmtext_append_normal_color(&fm, "magenta\n", OWL_COLOR_MAGENTA, OWL_COLOR_DEFAULT);
2704
2705  owl_fmtext_append_normal(&fm,"cyan:     ");
2706  owl_fmtext_append_normal_color(&fm, "cyan\n", OWL_COLOR_CYAN, OWL_COLOR_DEFAULT);
2707
2708  owl_fmtext_append_normal(&fm,"white:    ");
2709  owl_fmtext_append_normal_color(&fm, "white\n", OWL_COLOR_WHITE, OWL_COLOR_DEFAULT);
2710
2711  for(i = 8; i < COLORS; ++i) {
2712    char* str1 = owl_sprintf("%4i:     ",i);
2713    char* str2 = owl_sprintf("%i\n",i);
2714    owl_fmtext_append_normal(&fm,str1);
2715    owl_fmtext_append_normal_color(&fm, str2, i, OWL_COLOR_DEFAULT);
2716    owl_free(str1);
2717     owl_free(str2);
2718  }
2719 
2720  owl_function_popless_fmtext(&fm);
2721  owl_fmtext_cleanup(&fm);
2722}
2723
2724/* add the given class, inst, recip to the punt list for filtering.
2725 *   if direction==0 then punt
2726 *   if direction==1 then unpunt
2727 */
2728void owl_function_zpunt(const char *class, const char *inst, const char *recip, int direction)
2729{
2730  char *puntexpr, *classexpr, *instexpr, *recipexpr;
2731  char *quoted;
2732
2733  if (!strcmp(class, "*")) {
2734    classexpr = owl_sprintf("class .*");
2735  } else {
2736    quoted=owl_text_quote(class, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2737    owl_text_tr(quoted, ' ', '.');
2738    owl_text_tr(quoted, '\'', '.');
2739    owl_text_tr(quoted, '"', '.');
2740    classexpr = owl_sprintf("class ^(un)*%s(\\.d)*$", quoted);
2741    owl_free(quoted);
2742  }
2743  if (!strcmp(inst, "*")) {
2744    instexpr = owl_sprintf(" and instance .*");
2745  } else {
2746    quoted=owl_text_quote(inst, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2747    owl_text_tr(quoted, ' ', '.');
2748    owl_text_tr(quoted, '\'', '.');
2749    owl_text_tr(quoted, '"', '.');
2750    instexpr = owl_sprintf(" and instance ^(un)*%s(\\.d)*$", quoted);
2751    owl_free(quoted);
2752  }
2753  if (!strcmp(recip, "*")) {
2754    recipexpr = owl_sprintf("");
2755  } else {
2756    if(!strcmp(recip, "%me%")) {
2757      recip = owl_zephyr_get_sender();
2758    }
2759    quoted=owl_text_quote(recip, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2760    owl_text_tr(quoted, ' ', '.');
2761    owl_text_tr(quoted, '\'', '.');
2762    owl_text_tr(quoted, '"', '.');
2763    recipexpr = owl_sprintf(" and recipient ^%s$", quoted);
2764    owl_free(quoted);
2765  }
2766
2767  puntexpr = owl_sprintf("%s %s %s", classexpr, instexpr, recipexpr);
2768  owl_function_punt(puntexpr, direction);
2769  owl_free(puntexpr);
2770  owl_free(classexpr);
2771  owl_free(instexpr);
2772  owl_free(recipexpr);
2773}
2774
2775void owl_function_punt(const char *filter, int direction)
2776{
2777  owl_filter *f;
2778  owl_list *fl;
2779  int i, j;
2780  fl=owl_global_get_puntlist(&g);
2781
2782  /* first, create the filter */
2783  owl_function_debugmsg("About to filter %s", filter);
2784  f = owl_filter_new_fromstring("punt-filter", filter);
2785  if (f == NULL) {
2786    owl_function_error("Error creating filter for zpunt");
2787    return;
2788  }
2789
2790  /* Check for an identical filter */
2791  j=owl_list_get_size(fl);
2792  for (i=0; i<j; i++) {
2793    if (owl_filter_equiv(f, owl_list_get_element(fl, i))) {
2794      owl_function_debugmsg("found an equivalent punt filter");
2795      /* if we're punting, then just silently bow out on this duplicate */
2796      if (direction==0) {
2797        owl_filter_delete(f);
2798        return;
2799      }
2800
2801      /* if we're unpunting, then remove this filter from the puntlist */
2802      if (direction==1) {
2803        owl_filter_delete(owl_list_get_element(fl, i));
2804        owl_list_remove_element(fl, i);
2805        owl_filter_delete(f);
2806        return;
2807      }
2808    }
2809  }
2810
2811  owl_function_debugmsg("punting");
2812  /* If we're punting, add the filter to the global punt list */
2813  if (direction==0) {
2814    owl_list_append_element(fl, f);
2815  }
2816}
2817
2818void owl_function_show_keymaps(void)
2819{
2820  owl_list l;
2821  owl_fmtext fm;
2822  const owl_keymap *km;
2823  const owl_keyhandler *kh;
2824  int i, numkm;
2825  const char *kmname;
2826
2827  kh = owl_global_get_keyhandler(&g);
2828  owl_fmtext_init_null(&fm);
2829  owl_fmtext_append_bold(&fm, "Keymaps:   ");
2830  owl_fmtext_append_normal(&fm, "(use 'show keymap <name>' for details)\n");
2831  owl_keyhandler_get_keymap_names(kh, &l);
2832  owl_fmtext_append_list(&fm, &l, "\n", owl_function_keymap_summary);
2833  owl_fmtext_append_normal(&fm, "\n");
2834
2835  numkm = owl_list_get_size(&l);
2836  for (i=0; i<numkm; i++) {
2837    kmname = owl_list_get_element(&l, i);
2838    km = owl_keyhandler_get_keymap(kh, kmname);
2839    owl_fmtext_append_bold(&fm, "\n\n----------------------------------------------------------------------------------------------------\n\n");
2840    owl_keymap_get_details(km, &fm, 0);
2841  }
2842  owl_fmtext_append_normal(&fm, "\n");
2843 
2844  owl_function_popless_fmtext(&fm);
2845  owl_keyhandler_keymap_namelist_cleanup(&l);
2846  owl_fmtext_cleanup(&fm);
2847}
2848
2849char *owl_function_keymap_summary(const char *name)
2850{
2851  const owl_keymap *km
2852    = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), name);
2853  if (km) return owl_keymap_summary(km);
2854  else return(NULL);
2855}
2856
2857/* TODO: implement for real */
2858void owl_function_show_keymap(const char *name)
2859{
2860  owl_fmtext fm;
2861  const owl_keymap *km;
2862
2863  owl_fmtext_init_null(&fm);
2864  km = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), name);
2865  if (km) {
2866    owl_keymap_get_details(km, &fm, 1);
2867  } else {
2868    owl_fmtext_append_normal(&fm, "No such keymap...\n");
2869  } 
2870  owl_function_popless_fmtext(&fm);
2871  owl_fmtext_cleanup(&fm);
2872}
2873
2874void owl_function_help_for_command(const char *cmdname)
2875{
2876  owl_fmtext fm;
2877
2878  owl_fmtext_init_null(&fm);
2879  owl_cmd_get_help(owl_global_get_cmddict(&g), cmdname, &fm);
2880  owl_function_popless_fmtext(&fm); 
2881  owl_fmtext_cleanup(&fm);
2882}
2883
2884void owl_function_search_start(const char *string, int direction)
2885{
2886  /* direction is OWL_DIRECTION_DOWNWARDS or OWL_DIRECTION_UPWARDS or
2887   * OWL_DIRECTION_NONE */
2888  owl_regex re;
2889
2890  if (string && owl_regex_create_quoted(&re, string) == 0) {
2891    owl_global_set_search_re(&g, &re);
2892    owl_regex_cleanup(&re);
2893  } else {
2894    owl_global_set_search_re(&g, NULL);
2895  }
2896
2897  if (direction == OWL_DIRECTION_NONE)
2898    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2899  else
2900    owl_function_search_helper(0, direction);
2901}
2902
2903void owl_function_search_continue(int direction)
2904{
2905  /* direction is OWL_DIRECTION_DOWNWARDS or OWL_DIRECTION_UPWARDS */
2906  owl_function_search_helper(1, direction);
2907}
2908
2909void owl_function_search_helper(int mode, int direction)
2910{
2911  /* move to a message that contains the string.  If direction is
2912   * OWL_DIRECTION_DOWNWARDS then search fowards, if direction is
2913   * OWL_DIRECTION_UPWARDS then search backwards.
2914   *
2915   * If mode==0 then it will stay on the current message if it
2916   * contains the string.
2917   */
2918
2919  const owl_view *v;
2920  int viewsize, i, curmsg, start;
2921  owl_message *m;
2922
2923  v=owl_global_get_current_view(&g);
2924  viewsize=owl_view_get_size(v);
2925  curmsg=owl_global_get_curmsg(&g);
2926 
2927  if (viewsize==0) {
2928    owl_function_error("No messages present");
2929    return;
2930  }
2931
2932  if (mode==0) {
2933    start=curmsg;
2934  } else if (direction==OWL_DIRECTION_DOWNWARDS) {
2935    start=curmsg+1;
2936  } else {
2937    start=curmsg-1;
2938  }
2939
2940  /* bounds check */
2941  if (start>=viewsize || start<0) {
2942    owl_function_error("No further matches found");
2943    return;
2944  }
2945
2946  for (i=start; i<viewsize && i>=0;) {
2947    m=owl_view_get_element(v, i);
2948    if (owl_message_search(m, owl_global_get_search_re(&g))) {
2949      owl_global_set_curmsg(&g, i);
2950      owl_function_calculate_topmsg(direction);
2951      owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2952      if (direction==OWL_DIRECTION_DOWNWARDS) {
2953        owl_global_set_direction_downwards(&g);
2954      } else {
2955        owl_global_set_direction_upwards(&g);
2956      }
2957      return;
2958    }
2959    if (direction==OWL_DIRECTION_DOWNWARDS) {
2960      i++;
2961    } else {
2962      i--;
2963    }
2964    owl_function_mask_sigint(NULL);
2965    if(owl_global_is_interrupted(&g)) {
2966      owl_global_unset_interrupted(&g);
2967      owl_function_unmask_sigint(NULL);
2968      owl_function_makemsg("Search interrupted!");
2969      owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2970      return;
2971    }
2972    owl_function_unmask_sigint(NULL);
2973  }
2974  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2975  owl_function_error("No matches found");
2976}
2977
2978/* strips formatting from ztext and returns the unformatted text.
2979 * caller is responsible for freeing. */
2980char *owl_function_ztext_stylestrip(const char *zt)
2981{
2982  owl_fmtext fm;
2983  char *plaintext;
2984
2985  owl_fmtext_init_null(&fm);
2986  owl_fmtext_append_ztext(&fm, zt);
2987  plaintext = owl_fmtext_print_plain(&fm);
2988  owl_fmtext_cleanup(&fm);
2989  return(plaintext);
2990}
2991
2992/* Popup a buddylisting.  If filename is NULL use the default .anyone */
2993void owl_function_buddylist(int aim, int zephyr, const char *filename)
2994{
2995  int i, j, idle;
2996  int interrupted = 0;
2997  owl_fmtext fm;
2998  const owl_buddylist *bl;
2999  const owl_buddy *b;
3000  char *timestr;
3001#ifdef HAVE_LIBZEPHYR
3002  int x;
3003  owl_list anyone;
3004  const char *user;
3005  char *tmp;
3006  ZLocations_t location[200];
3007  int numlocs, ret;
3008#endif
3009
3010  owl_fmtext_init_null(&fm);
3011
3012  /* AIM first */
3013  if (aim && owl_global_is_aimloggedin(&g)) {
3014    bl=owl_global_get_buddylist(&g);
3015
3016    owl_fmtext_append_bold(&fm, "AIM users logged in:\n");
3017    /* we're assuming AIM for now */
3018    j=owl_buddylist_get_size(bl);
3019    for (i=0; i<j; i++) {
3020      b=owl_buddylist_get_buddy_n(bl, i);
3021      idle=owl_buddy_get_idle_time(b);
3022      if (idle!=0) {
3023        timestr=owl_util_minutes_to_timestr(idle);
3024      } else {
3025        timestr=owl_strdup("");
3026      }
3027      owl_fmtext_appendf_normal(&fm, "  %-20.20s %-12.12s\n", owl_buddy_get_name(b), timestr);
3028      owl_free(timestr);
3029    }
3030  }
3031
3032#ifdef HAVE_LIBZEPHYR
3033  if (zephyr) {
3034    if(!owl_global_is_havezephyr(&g)) {
3035      owl_function_error("Zephyr currently not available.");
3036    } else {
3037      owl_fmtext_append_bold(&fm, "Zephyr users logged in:\n");
3038      owl_list_create(&anyone);
3039      ret=owl_zephyr_get_anyone_list(&anyone, filename);
3040      if (ret) {
3041        if (errno == ENOENT) {
3042          owl_fmtext_append_normal(&fm, " You have not added any zephyr buddies.  Use the\n");
3043          owl_fmtext_append_normal(&fm, " command ':addbuddy zephyr ");
3044          owl_fmtext_append_bold(  &fm, "<username>");
3045          owl_fmtext_append_normal(&fm, "'.\n");
3046        } else {
3047          owl_fmtext_append_normal(&fm, " Could not read zephyr buddies from the .anyone file.\n");
3048        }
3049      } else {
3050        j=owl_list_get_size(&anyone);
3051        for (i=0; i<j; i++) {
3052          user=owl_list_get_element(&anyone, i);
3053          ret=ZLocateUser(zstr(user), &numlocs, ZAUTH);
3054
3055          owl_function_mask_sigint(NULL);
3056          if(owl_global_is_interrupted(&g)) {
3057            interrupted = 1;
3058            owl_global_unset_interrupted(&g);
3059            owl_function_unmask_sigint(NULL);
3060            owl_function_makemsg("Interrupted!");
3061            break;
3062          }
3063
3064          owl_function_unmask_sigint(NULL);
3065
3066          if (ret!=ZERR_NONE) {
3067            owl_function_error("Error getting location for %s", user);
3068            continue;
3069          }
3070
3071          numlocs=200;
3072          ret=ZGetLocations(location, &numlocs);
3073          if (ret==0) {
3074            for (x=0; x<numlocs; x++) {
3075              tmp=short_zuser(user);
3076              owl_fmtext_appendf_normal(&fm, "  %-10.10s %-24.24s %-12.12s  %20.20s\n",
3077                                        tmp,
3078                                        location[x].host,
3079                                        location[x].tty,
3080                                        location[x].time);
3081              owl_free(tmp);
3082            }
3083            if (numlocs>=200) {
3084              owl_fmtext_append_normal(&fm, "  Too many locations found for this user, truncating.\n");
3085            }
3086          }
3087        }
3088      }
3089      owl_list_cleanup(&anyone, owl_free);
3090    }
3091  }
3092#endif
3093
3094  if (aim && zephyr) {
3095    if (owl_perlconfig_is_function("BarnOwl::Hooks::_get_blist")) {
3096      char * perlblist = owl_perlconfig_execute("BarnOwl::Hooks::_get_blist()");
3097      if (perlblist) {
3098        owl_fmtext_append_ztext(&fm, perlblist);
3099        owl_free(perlblist);
3100      }
3101    }
3102  }
3103
3104  if(!interrupted) {
3105    owl_function_popless_fmtext(&fm);
3106  }
3107  owl_fmtext_cleanup(&fm);
3108}
3109
3110/* Dump messages in the current view to the file 'filename'. */
3111void owl_function_dump(const char *filename) 
3112{
3113  int i, j;
3114  owl_message *m;
3115  const owl_view *v;
3116  FILE *file;
3117  char *plaintext;
3118
3119  v=owl_global_get_current_view(&g);
3120
3121  /* in the future make it ask yes/no */
3122  /*
3123  ret=stat(filename, &sbuf);
3124  if (!ret) {
3125    ret=owl_function_askyesno("File exists, continue? [Y/n]");
3126    if (!ret) return;
3127  }
3128  */
3129
3130  file=fopen(filename, "w");
3131  if (!file) {
3132    owl_function_error("Error opening file");
3133    return;
3134  }
3135
3136  j=owl_view_get_size(v);
3137  for (i=0; i<j; i++) {
3138    m=owl_view_get_element(v, i);
3139    plaintext = owl_strip_format_chars(owl_message_get_text(m));
3140    if (plaintext) {
3141      fputs(plaintext, file);
3142      owl_free(plaintext);
3143    }
3144  }
3145  fclose(file);
3146  owl_function_makemsg("Messages dumped to %s", filename);
3147}
3148
3149void owl_function_do_newmsgproc(void)
3150{
3151  if (owl_global_get_newmsgproc(&g) && strcmp(owl_global_get_newmsgproc(&g), "")) {
3152    /* if there's a process out there, we need to check on it */
3153    if (owl_global_get_newmsgproc_pid(&g)) {
3154      owl_function_debugmsg("Checking on newmsgproc pid==%i", owl_global_get_newmsgproc_pid(&g));
3155      owl_function_debugmsg("Waitpid return is %i", waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG));
3156      waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG);
3157      if (waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG)==-1) {
3158        /* it exited */
3159        owl_global_set_newmsgproc_pid(&g, 0);
3160        owl_function_debugmsg("newmsgproc exited");
3161      } else {
3162        owl_function_debugmsg("newmsgproc did not exit");
3163      }
3164    }
3165   
3166    /* if it exited, fork & exec a new one */
3167    if (owl_global_get_newmsgproc_pid(&g)==0) {
3168      pid_t i;
3169      int myargc;
3170      i=fork();
3171      if (i) {
3172        /* parent set the child's pid */
3173        owl_global_set_newmsgproc_pid(&g, i);
3174        owl_function_debugmsg("I'm the parent and I started a new newmsgproc with pid %i", i);
3175      } else {
3176        /* child exec's the program */
3177        char **parsed;
3178        parsed=owl_parseline(owl_global_get_newmsgproc(&g), &myargc);
3179        if (myargc < 0) {
3180          owl_function_debugmsg("Could not parse newmsgproc '%s': unbalanced quotes?", owl_global_get_newmsgproc(&g));
3181        }
3182        if (myargc <= 0) {
3183          _exit(127);
3184        }
3185        parsed=owl_realloc(parsed, sizeof(*parsed) * (myargc+1));
3186        parsed[myargc] = NULL;
3187       
3188        owl_function_debugmsg("About to exec \"%s\" with %d arguments", parsed[0], myargc);
3189       
3190        execvp(parsed[0], parsed);
3191       
3192       
3193        /* was there an error exec'ing? */
3194        owl_function_debugmsg("Cannot run newmsgproc '%s': cannot exec '%s': %s", 
3195                              owl_global_get_newmsgproc(&g), parsed[0], strerror(errno));
3196        _exit(127);
3197      }
3198    }
3199  }
3200}
3201
3202/* print the xterm escape sequence to raise the window */
3203void owl_function_xterm_raise(void)
3204{
3205  printf("\033[5t");
3206}
3207
3208/* print the xterm escape sequence to deiconify the window */
3209void owl_function_xterm_deiconify(void)
3210{
3211  printf("\033[1t");
3212}
3213
3214/* Add the specified command to the startup file.  Eventually this
3215 * should be clever, and rewriting settings that will obviosly
3216 * override earlier settings with 'set' 'bindkey' and 'alias'
3217 * commands.  For now though we just remove any line that would
3218 * duplicate this one and then append this line to the end of
3219 * startupfile.
3220 */
3221void owl_function_addstartup(const char *buff)
3222{
3223  FILE *file;
3224  const char *filename;
3225
3226  filename=owl_global_get_startupfile(&g);
3227
3228  /* delete earlier copies */
3229  owl_util_file_deleteline(filename, buff, 1);
3230
3231  file=fopen(filename, "a");
3232  if (!file) {
3233    owl_function_error("Error opening startupfile for new command");
3234    return;
3235  }
3236
3237  /* add this line */
3238  fprintf(file, "%s\n", buff);
3239
3240  fclose(file);
3241}
3242
3243/* Remove the specified command from the startup file. */
3244void owl_function_delstartup(const char *buff)
3245{
3246  const char *filename;
3247  filename=owl_global_get_startupfile(&g);
3248  owl_util_file_deleteline(filename, buff, 1);
3249}
3250
3251/* Execute owl commands from the given filename.  If the filename
3252 * is NULL, use the default owl startup commands file.
3253 */
3254void owl_function_source(const char *filename)
3255{
3256  char *path;
3257  FILE *file;
3258  char *s = NULL;
3259  int fail_silent = 0;
3260
3261  if (!filename) {
3262    fail_silent = 1;
3263    path = owl_strdup(owl_global_get_startupfile(&g));
3264  } else {
3265    path = owl_util_makepath(filename);
3266  }
3267  file = fopen(path, "r");
3268  owl_free(path);
3269  if (!file) {
3270    if (!fail_silent) {
3271      owl_function_error("Error opening file: %s", filename);
3272    }
3273    return;
3274  }
3275  while (owl_getline_chomp(&s, file)) {
3276    if (s[0] == '\0' || s[0] == '#')
3277      continue;
3278    owl_function_command(s);
3279  }
3280
3281  owl_free(s);
3282  fclose(file);
3283}
3284
3285void owl_function_change_style(owl_view *v, const char *stylename)
3286{
3287  const owl_style *s;
3288
3289  s=owl_global_get_style_by_name(&g, stylename);
3290  if (!s) {
3291    owl_function_error("No style named %s", stylename);
3292    return;
3293  }
3294  owl_view_set_style(v, s);
3295  owl_messagelist_invalidate_formats(owl_global_get_msglist(&g));
3296  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
3297  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3298}
3299
3300void owl_function_toggleoneline(void)
3301{
3302  owl_view *v;
3303  const owl_style *s;
3304
3305  v=owl_global_get_current_view(&g);
3306  s=owl_view_get_style(v);
3307
3308  if (!owl_style_matches_name(s, "oneline")) {
3309    owl_function_change_style(v, "oneline");
3310  } else {
3311    owl_function_change_style(v, owl_global_get_default_style(&g));
3312  }
3313
3314  owl_messagelist_invalidate_formats(owl_global_get_msglist(&g));
3315  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
3316  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3317}
3318
3319void owl_function_error(const char *fmt, ...)
3320{
3321  static int in_error = 0;
3322  va_list ap;
3323  char *buff;
3324  const char *nl;
3325
3326  if (++in_error > 2) {
3327    /* More than two nested errors, bail immediately. */
3328    in_error--;
3329    return;
3330  }
3331
3332  va_start(ap, fmt);
3333  buff = g_strdup_vprintf(fmt, ap);
3334  va_end(ap);
3335
3336  owl_function_debugmsg("ERROR: %s", buff);
3337  owl_function_log_err(buff);
3338
3339  nl = strchr(buff, '\n');
3340
3341  /*
3342    Showing admin messages triggers a lot of code. If we have a
3343    recursive error call, that's the most likely candidate, so
3344    suppress the call in that case, to try to avoid infinite looping.
3345  */
3346
3347  if(nl && *(nl + 1) && in_error == 1) {
3348    /* Multiline error */
3349    owl_function_adminmsg("ERROR", buff);
3350  } else {
3351    owl_function_makemsg("[Error] %s", buff);
3352  }
3353
3354  owl_free(buff);
3355
3356  in_error--;
3357}
3358
3359void owl_function_log_err(const char *string)
3360{
3361  char *date;
3362  time_t now;
3363  char *buff;
3364
3365  now=time(NULL);
3366  date=owl_strdup(ctime(&now));
3367  date[strlen(date)-1]='\0';
3368
3369  buff = owl_sprintf("%s %s", date, string);
3370
3371  owl_errqueue_append_err(owl_global_get_errqueue(&g), buff);
3372
3373  owl_free(buff);
3374  owl_free(date);
3375}
3376
3377void owl_function_showerrs(void)
3378{
3379  owl_fmtext fm;
3380
3381  owl_fmtext_init_null(&fm);
3382  owl_fmtext_append_normal(&fm, "Errors:\n\n");
3383  owl_errqueue_to_fmtext(owl_global_get_errqueue(&g), &fm);
3384  owl_function_popless_fmtext(&fm);
3385}
3386
3387void owl_function_makemsg(const char *fmt, ...)
3388{
3389  va_list ap;
3390  char *str;
3391
3392  if (!owl_global_get_curs_msgwin(&g)) return;
3393
3394  va_start(ap, fmt);
3395  str = g_strdup_vprintf(fmt, ap);
3396  va_end(ap);
3397
3398  owl_function_debugmsg("makemsg: %s", str);
3399  owl_msgwin_set_text(&g.msgwin, str);
3400}
3401
3402/* get locations for everyone in .anyone.  If 'notify' is '1' then
3403 * send a pseudo login or logout message for everyone not in sync with
3404 * the global zephyr buddy list.  The list is updated regardless of
3405 * the status of 'notify'.
3406 */
3407void owl_function_zephyr_buddy_check(int notify)
3408{
3409#ifdef HAVE_LIBZEPHYR
3410  int i, j;
3411  owl_list anyone;
3412  owl_zbuddylist *zbl;
3413  GList **zaldlist;
3414  GList *zaldptr;
3415  ZAsyncLocateData_t *zald;
3416  const char *user;
3417
3418  if (!owl_global_is_havezephyr(&g)) return;
3419  owl_global_set_pseudologin_notify(&g, notify);
3420  zbl = owl_global_get_zephyr_buddylist(&g);
3421  zaldlist = owl_global_get_zaldlist(&g);
3422
3423  /* Clear the existing ZALDs first. */
3424  zaldptr = g_list_first(*zaldlist);
3425  while (zaldptr) {
3426    ZFreeALD(zaldptr->data);
3427    owl_free(zaldptr->data);
3428    zaldptr = g_list_next(zaldptr);
3429  }
3430  g_list_free(*zaldlist);
3431  *zaldlist = NULL;
3432
3433  owl_list_create(&anyone);
3434  owl_zephyr_get_anyone_list(&anyone, NULL);
3435  j = owl_list_get_size(&anyone);
3436  for (i = 0; i < j; i++) {
3437    user = owl_list_get_element(&anyone, i);
3438    zald = owl_malloc(sizeof(ZAsyncLocateData_t));
3439    if (ZRequestLocations(zstr(user), zald, UNACKED, ZAUTH) == ZERR_NONE) {
3440      *zaldlist = g_list_append(*zaldlist, zald);
3441    } else {
3442      owl_free(zald);
3443    }
3444  }
3445
3446  owl_list_cleanup(&anyone, owl_free);
3447#endif
3448}
3449
3450void owl_function_aimsearch_results(const char *email, owl_list *namelist)
3451{
3452  owl_fmtext fm;
3453  int i, j;
3454
3455  owl_fmtext_init_null(&fm);
3456  owl_fmtext_append_normal(&fm, "AIM screennames associated with ");
3457  owl_fmtext_append_normal(&fm, email);
3458  owl_fmtext_append_normal(&fm, ":\n");
3459
3460  j=owl_list_get_size(namelist);
3461  for (i=0; i<j; i++) {
3462    owl_fmtext_append_normal(&fm, "  ");
3463    owl_fmtext_append_normal(&fm, owl_list_get_element(namelist, i));
3464    owl_fmtext_append_normal(&fm, "\n");
3465  }
3466
3467  owl_function_popless_fmtext(&fm);
3468  owl_fmtext_cleanup(&fm);
3469}
3470
3471int owl_function_get_color_count(void)
3472{
3473     return COLORS;
3474}
3475
3476void owl_function_mask_sigint(sigset_t *oldmask) {
3477  sigset_t intr;
3478
3479  sigemptyset(&intr);
3480  sigaddset(&intr, SIGINT);
3481  sigprocmask(SIG_BLOCK, &intr, oldmask);
3482}
3483
3484void owl_function_unmask_sigint(sigset_t *oldmask) {
3485  sigset_t intr;
3486
3487  sigemptyset(&intr);
3488  sigaddset(&intr, SIGINT);
3489  sigprocmask(SIG_UNBLOCK, &intr, oldmask);
3490}
3491
3492void _owl_function_mark_message(const owl_message *m)
3493{
3494  if (m) {
3495    owl_global_set_markedmsgid(&g, owl_message_get_id(m));
3496    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3497  }
3498}
3499
3500void owl_function_mark_message(void)
3501{
3502  const owl_message *m;
3503  const owl_view *v;
3504
3505  v=owl_global_get_current_view(&g);
3506
3507  /* bail if there's no current message */
3508  if (owl_view_get_size(v) < 1) {
3509    owl_function_error("No messages to mark");
3510    return;
3511  }
3512
3513  /* mark the message */
3514  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
3515  _owl_function_mark_message(m);
3516  owl_function_makemsg("Mark set");
3517}
3518
3519void owl_function_swap_cur_marked(void)
3520{
3521  int marked_id;
3522  const owl_message *m;
3523  const owl_view *v;
3524
3525  marked_id=owl_global_get_markedmsgid(&g);
3526  if (marked_id == -1) {
3527    owl_function_error("Mark not set.");
3528    return;
3529  }
3530
3531  v=owl_global_get_current_view(&g);
3532  /* bail if there's no current message */
3533  if (owl_view_get_size(v) < 1) {
3534    return;
3535  }
3536
3537  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
3538  _owl_function_mark_message(m);
3539  owl_global_set_curmsg(&g, owl_view_get_nearest_to_msgid(v, marked_id));
3540  owl_function_calculate_topmsg(OWL_DIRECTION_NONE);
3541  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3542  owl_global_set_direction_downwards(&g);
3543}
Note: See TracBrowser for help on using the repository browser.