source: functions.c @ c394de8

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