source: functions.c @ 9359e5a

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