source: functions.c @ 438409f

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