source: functions.c @ 08263a8

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