source: functions.c @ 09065ed

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