source: functions.c @ 1c6c4d3

barnowl_perlaimdebianowlrelease-1.4release-1.5release-1.6release-1.7release-1.8release-1.9
Last change on this file since 1c6c4d3 was 1c6c4d3, checked in by Erik Nygren <nygren@mit.edu>, 19 years ago
Added owl_sprintf which returns the formatted string, or NULL. The caller must free this string. This will allocate enough memory and thus avoid potential some buffer overrun situations. Started fixing some potential buffer overrun situations. Simple implementation of 'zwrite -m' (doesn't yet log an outgoing message as having been sent.) The "Not logged in or subscribing to messages" error now includes the name of the recipient.
  • Property mode set to 100644
File size: 53.4 KB
Line 
1#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4#include <signal.h>
5#include <string.h>
6#include <com_err.h>
7#include <time.h>
8#include "owl.h"
9
10static const char fileIdent[] = "$Id$";
11
12void owl_function_noop(void) {
13  return;
14}
15
16char *owl_function_command(char *cmdbuff) {
17  owl_function_debugmsg("executing command: %s", cmdbuff);
18  return owl_cmddict_execute(owl_global_get_cmddict(&g), 
19                             owl_global_get_context(&g), cmdbuff);
20}
21
22void owl_function_command_norv(char *cmdbuff) {
23  char *rv;
24  rv = owl_function_command(cmdbuff);
25  if (rv) owl_free(rv);
26}
27
28void owl_function_command_alias(char *alias_from, char *alias_to) {
29  owl_cmddict_add_alias(owl_global_get_cmddict(&g), alias_from, alias_to);
30}
31
32owl_cmd *owl_function_get_cmd(char *name) {
33  return owl_cmddict_find(owl_global_get_cmddict(&g), name);
34}
35
36void owl_function_show_commands() {
37  owl_list l;
38  owl_fmtext fm;
39
40  owl_fmtext_init_null(&fm);
41  owl_fmtext_append_bold(&fm, "Commands:  ");
42  owl_fmtext_append_normal(&fm, "(use 'show command <name>' for details)\n");
43  owl_cmddict_get_names(owl_global_get_cmddict(&g), &l);
44  owl_fmtext_append_list(&fm, &l, "\n", owl_function_cmd_describe);
45  owl_fmtext_append_normal(&fm, "\n");
46  owl_function_popless_fmtext(&fm);
47  owl_cmddict_namelist_free(&l);
48  owl_fmtext_free(&fm);
49}
50
51char *owl_function_cmd_describe(void *name) {
52  owl_cmd *cmd = owl_cmddict_find(owl_global_get_cmddict(&g), name);
53  if (cmd) return owl_cmd_describe(cmd);
54  else return(NULL);
55}
56
57void owl_function_show_command(char *name) {
58  owl_function_help_for_command(name);
59}
60
61void owl_function_adminmsg(char *header, char *body) {
62  owl_message *m;
63  int followlast;
64
65  followlast=owl_global_should_followlast(&g);
66  m=owl_malloc(sizeof(owl_message));
67  owl_message_create_admin(m, header, body);
68  owl_messagelist_append_element(owl_global_get_msglist(&g), m);
69  owl_view_consider_message(owl_global_get_current_view(&g), m);
70
71  if (followlast) owl_function_lastmsg_noredisplay();
72
73  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
74  if (owl_popwin_is_active(owl_global_get_popwin(&g))) {
75    owl_popwin_refresh(owl_global_get_popwin(&g));
76  }
77 
78  wnoutrefresh(owl_global_get_curs_recwin(&g));
79  owl_global_set_needrefresh(&g);
80}
81
82void owl_function_adminmsg_outgoing(char *header, char *body, char *zwriteline) {
83  owl_message *m;
84  int followlast;
85 
86  followlast=owl_global_should_followlast(&g);
87  m=owl_malloc(sizeof(owl_message));
88  owl_message_create_admin(m, header, body);
89  owl_message_set_admin_outgoing(m, zwriteline);
90  owl_messagelist_append_element(owl_global_get_msglist(&g), m);
91  owl_view_consider_message(owl_global_get_current_view(&g), m);
92
93  if (followlast) owl_function_lastmsg_noredisplay();
94
95  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
96  if (owl_popwin_is_active(owl_global_get_popwin(&g))) {
97    owl_popwin_refresh(owl_global_get_popwin(&g));
98  }
99 
100  wnoutrefresh(owl_global_get_curs_recwin(&g));
101  owl_global_set_needrefresh(&g);
102}
103
104void owl_function_zwrite_setup(char *line) {
105  owl_editwin *e;
106  char buff[1024];
107  owl_zwrite z;
108  int ret;
109
110  /* check the arguments */
111  ret=owl_zwrite_create_from_line(&z, line);
112  if (ret) {
113    owl_function_makemsg("Error in zwrite arugments");
114    owl_zwrite_free(&z);
115    return;
116  }
117
118  /* send a ping if necessary */
119  if (owl_global_is_txping(&g)) {
120    owl_zwrite_send_ping(&z);
121  }
122  owl_zwrite_free(&z);
123
124  /* create and setup the editwin */
125  e=owl_global_get_typwin(&g);
126  owl_editwin_new_style(e, OWL_EDITWIN_STYLE_MULTILINE);
127
128  if (!owl_global_is_lockout_ctrld(&g)) {
129    owl_function_makemsg("Type your zephyr below.  End with ^D or a dot on a line by itself.  ^C will quit.");
130  } else {
131    owl_function_makemsg("Type your zephyr below.  End with a dot on a line by itself.  ^C will quit.");
132  }
133
134  owl_editwin_clear(e);
135  owl_editwin_set_dotsend(e);
136  strcpy(buff, "----> ");
137  strcat(buff, line);
138  strcat(buff, "\n");
139  owl_editwin_set_locktext(e, buff);
140
141  /* make it active */
142  owl_global_set_typwin_active(&g);
143}
144
145void owl_function_zwrite(char *line) {
146  char *tmpbuff, buff[1024];
147  owl_zwrite z;
148  int i, j;
149
150  /* create the zwrite and send the message */
151  owl_zwrite_create_from_line(&z, line);
152  owl_zwrite_send_message(&z, owl_editwin_get_text(owl_global_get_typwin(&g)));
153  owl_function_makemsg("Waiting for ack...");
154
155  /* display the message as an admin message in the receive window */
156  if (owl_global_is_displayoutgoing(&g) && owl_zwrite_is_personal(&z)) {
157    owl_zwrite_get_recipstr(&z, buff);
158    tmpbuff = owl_sprintf("Message sent to %s", buff);
159    owl_function_adminmsg_outgoing(tmpbuff, owl_editwin_get_text(owl_global_get_typwin(&g)), line);
160    owl_free(tmpbuff);
161  }
162
163  /* log it if we have logging turned on */
164  if (owl_global_is_logging(&g) && owl_zwrite_is_personal(&z)) {
165    j=owl_zwrite_get_numrecips(&z);
166    for (i=0; i<j; i++) {
167      owl_log_outgoing(owl_zwrite_get_recip_n(&z, i),
168                       owl_editwin_get_text(owl_global_get_typwin(&g)));
169    }
170  }
171
172  /* free the zwrite */
173  owl_zwrite_free(&z);
174}
175
176
177/* If filter is non-null, looks for the next message matching
178 * that filter.  If skip_deleted, skips any deleted messages.
179 * If last_if_none, will stop at the last message in the view
180 * if no matching messages are found.  */
181void owl_function_nextmsg_full(char *filter, int skip_deleted, int last_if_none) {
182  int curmsg, i, viewsize, found;
183  owl_view *v;
184  owl_filter *f = NULL;
185  owl_message *m;
186
187  v=owl_global_get_current_view(&g);
188
189  if (filter) {
190    f=owl_global_get_filter(&g, filter);
191    if (!f) {
192      owl_function_makemsg("No %s filter defined", filter);
193      return;
194    }
195  }
196
197  curmsg=owl_global_get_curmsg(&g);
198  viewsize=owl_view_get_size(v);
199  found=0;
200
201  /* just check to make sure we're in bounds... */
202  if (curmsg>viewsize-1) curmsg=viewsize-1;
203  if (curmsg<0) curmsg=0;
204
205  for (i=curmsg+1; i<viewsize; i++) {
206    m=owl_view_get_element(v, i);
207    if (skip_deleted && owl_message_is_delete(m)) continue;
208    if (f && !owl_filter_message_match(f, m)) continue;
209    found = 1;
210    break;
211  }
212
213  if (i>owl_view_get_size(v)-1) i=owl_view_get_size(v)-1;
214
215  if (!found) {
216    owl_function_makemsg("already at last%s message%s%s",
217                         skip_deleted?" non-deleted":"",
218                         filter?" in ":"", filter?filter:"");
219    if (!skip_deleted) owl_function_beep();
220  }
221
222  if (last_if_none || found) {
223    owl_global_set_curmsg(&g, i);
224    owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
225    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
226    owl_global_set_direction_downwards(&g);
227  }
228}
229
230void owl_function_prevmsg_full(char *filter, int skip_deleted, int first_if_none) {
231  int curmsg, i, viewsize, found;
232  owl_view *v;
233  owl_filter *f = NULL;
234  owl_message *m;
235
236  v=owl_global_get_current_view(&g);
237
238  if (filter) {
239    f=owl_global_get_filter(&g, filter);
240    if (!f) {
241      owl_function_makemsg("No %s filter defined", filter);
242      return;
243    }
244  }
245
246  curmsg=owl_global_get_curmsg(&g);
247  viewsize=owl_view_get_size(v);
248  found=0;
249
250  /* just check to make sure we're in bounds... */
251  if (curmsg>viewsize-1) curmsg=viewsize-1;
252  if (curmsg<0) curmsg=0;
253
254  for (i=curmsg-1; i>=0; i--) {
255    m=owl_view_get_element(v, i);
256    if (skip_deleted && owl_message_is_delete(m)) continue;
257    if (f && !owl_filter_message_match(f, m)) continue;
258    found = 1;
259    break;
260  }
261
262  if (i<0) i=0;
263
264  if (!found) {
265    owl_function_makemsg("already at first%s message%s%s",
266                         skip_deleted?" non-deleted":"",
267                         filter?" in ":"", filter?filter:"");
268    if (!skip_deleted) owl_function_beep();
269  }
270
271  if (first_if_none || found) {
272    owl_global_set_curmsg(&g, i);
273    owl_function_calculate_topmsg(OWL_DIRECTION_UPWARDS);
274    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
275    owl_global_set_direction_upwards(&g);
276  }
277}
278
279void owl_function_nextmsg() {
280  owl_function_nextmsg_full(NULL, 0, 1);
281}
282
283
284void owl_function_prevmsg() {
285  owl_function_prevmsg_full(NULL, 0, 1);
286}
287
288void owl_function_nextmsg_notdeleted() {
289  owl_function_nextmsg_full(NULL, 1, 1);
290}
291
292
293void owl_function_prevmsg_notdeleted() {
294  owl_function_prevmsg_full(NULL, 1, 1);
295}
296
297
298void owl_function_nextmsg_personal() {
299  owl_function_nextmsg_full("personal", 0, 0);
300}
301
302void owl_function_prevmsg_personal() {
303  owl_function_prevmsg_full("personal", 0, 0);
304}
305
306
307/* if move_after is 1, moves after the delete */
308void owl_function_deletecur(int move_after) {
309  int curmsg;
310  owl_view *v;
311
312  v=owl_global_get_current_view(&g);
313
314  /* bail if there's no current message */
315  if (owl_view_get_size(v) < 1) {
316    owl_function_makemsg("No current message to delete");
317    return;
318  }
319
320  /* mark the message for deletion */
321  curmsg=owl_global_get_curmsg(&g);
322  owl_view_delete_element(v, curmsg);
323
324  if (move_after) {
325    /* move the poiner in the appropriate direction
326     * to the next undeleted msg */
327    if (owl_global_get_direction(&g)==OWL_DIRECTION_UPWARDS) {
328      owl_function_prevmsg_notdeleted();
329    } else {
330      owl_function_nextmsg_notdeleted();
331    }
332  }
333}
334
335
336void owl_function_undeletecur(int move_after) {
337  int curmsg;
338  owl_view *v;
339
340  v=owl_global_get_current_view(&g);
341 
342  if (owl_view_get_size(v) < 1) {
343    owl_function_makemsg("No current message to undelete");
344    return;
345  }
346  curmsg=owl_global_get_curmsg(&g);
347
348  owl_view_undelete_element(v, curmsg);
349
350  if (move_after) {
351    if (owl_global_get_direction(&g)==OWL_DIRECTION_UPWARDS) {
352      if (curmsg>0) {
353        owl_function_prevmsg();
354      } else {
355        owl_function_nextmsg();
356      }
357    } else {
358      owl_function_nextmsg();
359    }
360  }
361
362  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
363}
364
365
366void owl_function_expunge() {
367  int curmsg;
368  owl_message *m;
369  owl_messagelist *ml;
370  owl_view *v;
371  int i, j;
372
373  curmsg=owl_global_get_curmsg(&g);
374  v=owl_global_get_current_view(&g);
375  ml=owl_global_get_msglist(&g);
376
377  /* first try to move to an undeleted message in the view*/
378  m=owl_view_get_element(v, curmsg);
379  if (owl_message_is_delete(m)) {
380    /* try to find the next undeleted message */
381    j=owl_view_get_size(v);
382    for (i=curmsg; i<j; i++) {
383      if (!owl_message_is_delete(owl_view_get_element(v, i))) {
384        owl_global_set_curmsg(&g, i);
385        break;
386      }
387    }
388
389    /* if we weren't successful try to find one backwards */
390    curmsg=owl_global_get_curmsg(&g);
391    if (owl_message_is_delete(owl_view_get_element(v, curmsg))) {
392      for (i=curmsg; i>0; i--) {
393        if (!owl_message_is_delete(owl_view_get_element(v, i))) {
394          owl_global_set_curmsg(&g, i);
395          break;
396        }
397      }
398    }
399  }
400
401  /* expunge the message list */
402  owl_messagelist_expunge(ml);
403
404  /* update all views (we only have one right now) */
405  owl_view_recalculate(v);
406
407  if (curmsg>owl_view_get_size(v)-1) {
408    owl_global_set_curmsg(&g, owl_view_get_size(v)-1);
409    if (owl_global_get_curmsg(&g)<0) {
410      owl_global_set_curmsg(&g, 0);
411    }
412    owl_function_calculate_topmsg(OWL_DIRECTION_NONE);
413  }
414
415  /* if there are no messages set the direction to down in case we
416     delete everything upwards */
417  owl_global_set_direction_downwards(&g);
418 
419  owl_function_makemsg("Messages expunged");
420  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
421}
422
423
424void owl_function_firstmsg() {
425  owl_global_set_curmsg(&g, 0);
426  owl_global_set_topmsg(&g, 0);
427  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
428  owl_global_set_direction_downwards(&g);
429}
430
431void owl_function_lastmsg_noredisplay() {
432  int curmsg;
433  owl_view *v;
434
435  v=owl_global_get_current_view(&g);
436 
437  curmsg=owl_view_get_size(v)-1;
438  if (curmsg<0) curmsg=0;
439  owl_global_set_curmsg(&g, curmsg);
440  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
441  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
442  owl_global_set_direction_downwards(&g);
443}
444
445void owl_function_lastmsg() {
446  owl_function_lastmsg_noredisplay();
447  owl_mainwin_redisplay(owl_global_get_mainwin(&g)); 
448}
449
450void owl_function_shift_right() {
451  owl_global_set_rightshift(&g, owl_global_get_rightshift(&g)+10);
452  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
453  owl_global_set_needrefresh(&g);
454}
455
456
457void owl_function_shift_left() {
458  int shift;
459
460  shift=owl_global_get_rightshift(&g);
461  if (shift>=10) {
462    owl_global_set_rightshift(&g, shift-10);
463    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
464    owl_global_set_needrefresh(&g);
465  } else {
466    owl_function_beep();
467    owl_function_makemsg("Already full left");
468  }
469}
470
471
472void owl_function_unsuball() {
473  unsuball();
474  owl_function_makemsg("Unsubscribed from all messages.");
475}
476
477void owl_function_loadsubs(char *file) {
478  int ret;
479  ret=owl_zephyr_loadsubs(file);
480  if (ret==0) {
481    owl_function_makemsg("Subscribed to messages from file.");
482  } else if (ret==-1) {
483    owl_function_makemsg("Could not open file.");
484  } else {
485    owl_function_makemsg("Error subscribing to messages from file.");
486  }
487}
488
489void owl_function_suspend() {
490  endwin();
491  printf("\n");
492  kill(getpid(), SIGSTOP);
493
494  /* resize to reinitialize all the windows when we come back */
495  owl_command_resize();
496}
497
498void owl_function_zaway_toggle() {
499  if (!owl_global_is_zaway(&g)) {
500    owl_global_set_zaway_msg(&g, owl_global_get_zaway_msg_default(&g));
501    owl_function_zaway_on();
502  } else {
503    owl_function_zaway_off();
504  }
505}
506
507void owl_function_zaway_on() {
508  owl_global_set_zaway_on(&g);
509  owl_function_makemsg("zaway set (%s)", owl_global_get_zaway_msg(&g));
510}
511
512void owl_function_zaway_off() {
513  owl_global_set_zaway_off(&g);
514  owl_function_makemsg("zaway off");
515}
516
517void owl_function_quit() {
518  char *ret;
519 
520  /* zlog out if we need to */
521  if (owl_global_is_shutdownlogout(&g)) {
522    owl_function_zlog_out();
523  }
524
525  /* execute the commands in shutdown */
526  ret = owl_config_execute("owl::shutdown();");
527  if (ret) owl_free(ret);
528
529  /* final clean up */
530  unsuball();
531  ZClosePort();
532  endwin();
533  owl_function_debugmsg("Quitting Owl");
534  exit(0);
535}
536
537
538void owl_function_zlog_in() {
539  char *exposure, *eset;
540  int ret;
541
542  eset=EXPOSE_REALMVIS;
543  exposure=ZGetVariable("exposure");
544  if (exposure==NULL) {
545    eset=EXPOSE_REALMVIS;
546  } else if (!strcasecmp(exposure,EXPOSE_NONE)) {
547    eset = EXPOSE_NONE;
548  } else if (!strcasecmp(exposure,EXPOSE_OPSTAFF)) {
549    eset = EXPOSE_OPSTAFF;
550  } else if (!strcasecmp(exposure,EXPOSE_REALMVIS)) {
551    eset = EXPOSE_REALMVIS;
552  } else if (!strcasecmp(exposure,EXPOSE_REALMANN)) {
553    eset = EXPOSE_REALMANN;
554  } else if (!strcasecmp(exposure,EXPOSE_NETVIS)) {
555    eset = EXPOSE_NETVIS;
556  } else if (!strcasecmp(exposure,EXPOSE_NETANN)) {
557    eset = EXPOSE_NETANN;
558  }
559   
560  ret=ZSetLocation(eset);
561  if (ret != ZERR_NONE) {
562    /*
563      char buff[LINE];
564      sprintf(buff, "Error setting location: %s", error_message(ret));
565      owl_function_makemsg(buff);
566    */
567  }
568}
569
570void owl_function_zlog_out() {
571  int ret;
572 
573  ret=ZUnsetLocation();
574  if (ret != ZERR_NONE) {
575    /*
576      char buff[LINE];
577      sprintf(buff, "Error unsetting location: %s", error_message(ret));
578      owl_function_makemsg(buff);
579    */
580  }
581}
582
583
584void owl_function_makemsg(char *fmt, ...) {
585  va_list ap;
586  char buff[2048];
587
588  va_start(ap, fmt);
589  werase(owl_global_get_curs_msgwin(&g));
590 
591  vsnprintf(buff, 2048, fmt, ap);
592  owl_function_debugmsg("makemsg: %s", buff);
593  waddstr(owl_global_get_curs_msgwin(&g), buff); 
594  wnoutrefresh(owl_global_get_curs_msgwin(&g));
595  owl_global_set_needrefresh(&g);
596  va_end(ap);
597}
598
599void owl_function_errormsg(char *fmt, ...) {
600  va_list ap;
601  char buff[2048];
602
603  va_start(ap, fmt);
604  werase(owl_global_get_curs_msgwin(&g));
605 
606  vsnprintf(buff, 2048, fmt, ap);
607  owl_function_debugmsg("ERROR: %s", buff);
608  waddstr(owl_global_get_curs_msgwin(&g), buff); 
609  waddstr(owl_global_get_curs_msgwin(&g), "ERROR: "); 
610  wnoutrefresh(owl_global_get_curs_msgwin(&g));
611  owl_global_set_needrefresh(&g);
612  va_end(ap);
613}
614
615
616void owl_function_openurl() {
617  /* visit the first url in the current message */
618  owl_message *m;
619  owl_view *v;
620  char *ptr1, *ptr2, *text, url[LINE], tmpbuff[LINE];
621  int webbrowser;
622
623  webbrowser = owl_global_get_webbrowser(&g);
624
625  if (webbrowser < 0 || webbrowser == OWL_WEBBROWSER_NONE) {
626    owl_function_makemsg("No browser selected");
627    return;
628  }
629
630  v=owl_global_get_current_view(&g);
631 
632  if (owl_view_get_size(v)==0) {
633    owl_function_makemsg("No current message selected");
634    return;
635  }
636
637  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
638  text=owl_message_get_text(m);
639
640  /* First look for a good URL */ 
641  if ((ptr1=strstr(text, "http://"))!=NULL) {
642    ptr2=strpbrk(ptr1, " \n\t");
643    if (ptr2) {
644      strncpy(url, ptr1, ptr2-ptr1+1);
645      url[ptr2-ptr1+1]='\0';
646    } else {
647      strcpy(url, ptr1);
648    }
649
650    /* if we had <http strip a trailing > */
651    if (ptr1>text && ptr1[-1]=='<') {
652      if (url[strlen(url)-1]=='>') {
653        url[strlen(url)-1]='\0';
654      }
655    }
656  } else if ((ptr1=strstr(text, "https://"))!=NULL) {
657    /* Look for an https URL */ 
658    ptr2=strpbrk(ptr1, " \n\t");
659    if (ptr2) {
660      strncpy(url, ptr1, ptr2-ptr1+1);
661      url[ptr2-ptr1+1]='\0';
662    } else {
663      strcpy(url, ptr1);
664    }
665   
666    /* if we had <http strip a trailing > */
667    if (ptr1>text && ptr1[-1]=='<') {
668      if (url[strlen(url)-1]=='>') {
669        url[strlen(url)-1]='\0';
670      }
671    }
672  } else if ((ptr1=strstr(text, "www."))!=NULL) {
673    /* if we can't find a real url look for www.something */
674    ptr2=strpbrk(ptr1, " \n\t");
675    if (ptr2) {
676      strncpy(url, ptr1, ptr2-ptr1+1);
677      url[ptr2-ptr1+1]='\0';
678    } else {
679      strcpy(url, ptr1);
680    }
681  } else {
682    owl_function_beep();
683    owl_function_makemsg("Could not find URL to open.");
684    return;
685  }
686
687  /* Make sure there aren't any quotes or \'s in the url */
688  for (ptr1 = url; *ptr1; ptr1++) {
689    if (*ptr1 == '"' || *ptr1 == '\\') {
690      owl_function_beep();
691      owl_function_makemsg("URL contains invalid characters.");
692      return;
693    }
694  }
695 
696  /* NOTE: There are potentially serious security issues here... */
697
698  /* open the page */
699  owl_function_makemsg("Opening %s", url);
700  if (webbrowser == OWL_WEBBROWSER_NETSCAPE) {
701    snprintf(tmpbuff, LINE, "netscape -remote \"openURL(%s)\" > /dev/null 2> /dev/null", url);
702    system(tmpbuff); 
703  } else if (webbrowser == OWL_WEBBROWSER_GALEON) {
704    snprintf(tmpbuff, LINE, "galeon \"%s\" > /dev/null 2> /dev/null &", url);
705    system(tmpbuff); 
706  }
707}
708
709void owl_function_calculate_topmsg(int direction) {
710  int recwinlines, y, savey, i, j, topmsg, curmsg, foo;
711  owl_mainwin *mw;
712  owl_view *v;
713
714  mw=owl_global_get_mainwin(&g);
715
716  topmsg=owl_global_get_topmsg(&g);
717  curmsg=owl_global_get_curmsg(&g);
718  v=owl_global_get_current_view(&g);
719  recwinlines=owl_global_get_recwin_lines(&g);
720
721  if (owl_view_get_size(v) < 1) {
722    return;
723  }
724 
725  /* Find number of lines from top to bottom of curmsg (store in savey) */
726  savey=0;
727  for (i=topmsg; i<=curmsg; i++) {
728    savey+=owl_message_get_numlines(owl_view_get_element(v, i));
729  }
730
731  /* If the direction is DOWNWARDS but we're off the bottom of the
732   *  screen, then set the topmsg to curmsg and scroll UPWARDS
733   */
734  if (direction == OWL_DIRECTION_DOWNWARDS) {
735    if (savey > recwinlines) {
736      topmsg=curmsg;
737      savey=owl_message_get_numlines(owl_view_get_element(v, curmsg));
738      direction=OWL_DIRECTION_UPWARDS;
739    }
740  }
741
742  /* If our bottom line is less than 1/4 down the screen then scroll up */
743  if (direction == OWL_DIRECTION_UPWARDS || direction == OWL_DIRECTION_NONE) {
744    if (savey < (recwinlines / 4)) {
745      y=0;
746      for (j=curmsg; j>=0; j--) {
747        foo=owl_message_get_numlines(owl_view_get_element(v, j));
748        /* will we run the curmsg off the screen? */
749        if ((foo+y) >= recwinlines) {
750          j++;
751          if (j>curmsg) j=curmsg;
752          break;
753        }
754        /* have saved 1/2 the screen space? */
755        y+=foo;
756        if (y > (recwinlines / 2)) break;
757      }
758      if (j<0) j=0;
759      owl_global_set_topmsg(&g, j);
760      return;
761    }
762  }
763
764  if (direction == OWL_DIRECTION_DOWNWARDS || direction == OWL_DIRECTION_NONE) {
765    /* If curmsg bottom line is more than 3/4 down the screen then scroll down */
766    if (savey > ((recwinlines * 3)/4)) {
767      y=0;
768      /* count lines from the top until we can save 1/2 the screen size */
769      for (j=topmsg; j<curmsg; j++) {
770        y+=owl_message_get_numlines(owl_view_get_element(v, j));
771        if (y > (recwinlines / 2)) break;
772      }
773      if (j==curmsg) {
774        j--;
775      }
776      owl_global_set_topmsg(&g, j+1);
777      return;
778    }
779  }
780}
781
782
783void owl_function_resize() {
784  owl_global_set_resize_pending(&g);
785}
786
787
788void owl_function_run_buffercommand() {
789  char *buff;
790
791  buff=owl_global_get_buffercommand(&g);
792  if (!strncmp(buff, "zwrite ", 7)) {
793
794    owl_function_zwrite(buff);
795  }
796}
797
798void owl_function_debugmsg(char *fmt, ...) {
799  FILE *file;
800  time_t now;
801  char buff1[LINE], buff2[LINE];
802  va_list ap;
803  va_start(ap, fmt);
804
805  if (!owl_global_is_debug_fast(&g)) return;
806
807  file=fopen(owl_global_get_debug_file(&g), "a");
808  if (!file) return;
809
810  now=time(NULL);
811  strcpy(buff1, ctime(&now));
812  buff1[strlen(buff1)-1]='\0';
813
814  owl_global_get_runtime_string(&g, buff2);
815 
816  fprintf(file, "[%i -  %s - %s]: ", (int) getpid(), buff1, buff2);
817  vfprintf(file, fmt, ap);
818  fprintf(file, "\n");
819  fclose(file);
820
821  va_end(ap);
822}
823
824
825void owl_function_refresh() {
826  owl_function_resize();
827}
828
829void owl_function_beep() {
830  if (owl_global_is_bell(&g)) {
831    beep();
832  }
833}
834
835
836void owl_function_subscribe(char *class, char *inst, char *recip) {
837  int ret;
838
839  ret=owl_zephyr_sub(class, inst, recip);
840  if (ret) {
841    owl_function_makemsg("Error subscribing.");
842  } else {
843    owl_function_makemsg("Subscribed.");
844  }
845}
846
847
848void owl_function_unsubscribe(char *class, char *inst, char *recip) {
849  int ret;
850
851  ret=owl_zephyr_unsub(class, inst, recip);
852  if (ret) {
853    owl_function_makemsg("Error subscribing.");
854  } else {
855    owl_function_makemsg("Unsubscribed.");
856  }
857}
858
859
860void owl_function_set_cursor(WINDOW *win) {
861  wnoutrefresh(win);
862}
863
864
865void owl_function_full_redisplay() {
866  redrawwin(owl_global_get_curs_recwin(&g));
867  redrawwin(owl_global_get_curs_sepwin(&g));
868  redrawwin(owl_global_get_curs_typwin(&g));
869  redrawwin(owl_global_get_curs_msgwin(&g));
870
871  wnoutrefresh(owl_global_get_curs_recwin(&g));
872  wnoutrefresh(owl_global_get_curs_sepwin(&g));
873  wnoutrefresh(owl_global_get_curs_typwin(&g));
874  wnoutrefresh(owl_global_get_curs_msgwin(&g));
875 
876  sepbar("");
877  owl_function_makemsg("");
878
879  owl_global_set_needrefresh(&g);
880}
881
882
883void owl_function_popless_text(char *text) {
884  owl_popwin *pw;
885  owl_viewwin *v;
886
887  pw=owl_global_get_popwin(&g);
888  v=owl_global_get_viewwin(&g);
889
890  owl_popwin_up(pw);
891  owl_viewwin_init_text(v, owl_popwin_get_curswin(pw),
892                        owl_popwin_get_lines(pw), owl_popwin_get_cols(pw),
893                        text);
894  owl_popwin_refresh(pw);
895  owl_viewwin_redisplay(v, 0);
896  owl_global_set_needrefresh(&g);
897}
898
899
900void owl_function_popless_fmtext(owl_fmtext *fm) {
901  owl_popwin *pw;
902  owl_viewwin *v;
903
904  pw=owl_global_get_popwin(&g);
905  v=owl_global_get_viewwin(&g);
906
907  owl_popwin_up(pw);
908  owl_viewwin_init_fmtext(v, owl_popwin_get_curswin(pw),
909                   owl_popwin_get_lines(pw), owl_popwin_get_cols(pw),
910                   fm);
911  owl_popwin_refresh(pw);
912  owl_viewwin_redisplay(v, 0);
913  owl_global_set_needrefresh(&g);
914}
915
916void owl_function_about() {
917  char buff[5000];
918
919  sprintf(buff, "This is owl version %s\n", OWL_VERSION_STRING);
920  strcat(buff, "\nOwl was written by James Kretchmar at the Massachusetts\n");
921  strcat(buff, "Institute of Technology.  The first version, 0.5, was\n");
922  strcat(buff, "released in March 2002\n");
923  strcat(buff, "\n");
924  strcat(buff, "The name 'owl' was chosen in reference to the owls in the\n");
925  strcat(buff, "Harry Potter novels, who are tasked with carrying messages\n");
926  strcat(buff, "between Witches and Wizards.\n");
927  strcat(buff, "\n");
928  strcat(buff, "Copyright 2002 Massachusetts Institute of Technology\n");
929  strcat(buff, "\n");
930  strcat(buff, "Permission to use, copy, modify, and distribute this\n");
931  strcat(buff, "software and its documentation for any purpose and without\n");
932  strcat(buff, "fee is hereby granted, provided that the above copyright\n");
933  strcat(buff, "notice and this permission notice appear in all copies\n");
934  strcat(buff, "and in supporting documentation.  No representation is\n");
935  strcat(buff, "made about the suitability of this software for any\n");
936  strcat(buff, "purpose.  It is provided \"as is\" without express\n");
937  strcat(buff, "or implied warranty.\n");
938  owl_function_popless_text(buff);
939}
940
941void owl_function_info() {
942  owl_message *m;
943  ZNotice_t *n;
944  char buff[2048], tmpbuff[1024];
945  char *ptr;
946  int i, j, fields, len;
947  owl_view *v;
948
949  v=owl_global_get_current_view(&g);
950 
951  if (owl_view_get_size(v)==0) {
952    owl_function_makemsg("No message selected\n");
953    return;
954  }
955
956  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
957  if (!owl_message_is_zephyr(m)) {
958    sprintf(buff,   "Owl Message Id: %i\n", owl_message_get_id(m));
959    sprintf(buff, "%sTime          : %s\n", buff, owl_message_get_timestr(m));
960    owl_function_popless_text(buff);
961    return;
962  }
963
964  n=owl_message_get_notice(m);
965
966  sprintf(buff,   "Owl Msg ID: %i\n", owl_message_get_id(m));
967  sprintf(buff, "%sClass     : %s\n", buff, n->z_class);
968  sprintf(buff, "%sInstance  : %s\n", buff, n->z_class_inst);
969  sprintf(buff, "%sSender    : %s\n", buff, n->z_sender);
970  sprintf(buff, "%sRecip     : %s\n", buff, n->z_recipient);
971  sprintf(buff, "%sOpcode    : %s\n", buff, n->z_opcode);
972  strcat(buff,    "Kind      : ");
973  if (n->z_kind==UNSAFE) {
974    strcat(buff, "UNSAFE\n");
975  } else if (n->z_kind==UNACKED) {
976    strcat(buff, "UNACKED\n");
977  } else if (n->z_kind==ACKED) {
978    strcat(buff, "ACKED\n");
979  } else if (n->z_kind==HMACK) {
980    strcat(buff, "HMACK\n");
981  } else if (n->z_kind==HMCTL) {
982    strcat(buff, "HMCTL\n");
983  } else if (n->z_kind==SERVACK) {
984    strcat(buff, "SERVACK\n");
985  } else if (n->z_kind==SERVNAK) {
986    strcat(buff, "SERVNAK\n");
987  } else if (n->z_kind==CLIENTACK) {
988    strcat(buff, "CLIENTACK\n");
989  } else if (n->z_kind==STAT) {
990    strcat(buff, "STAT\n");
991  } else {
992    strcat(buff, "ILLEGAL VALUE\n");
993  }
994  sprintf(buff, "%sTime      : %s\n", buff, owl_message_get_timestr(m));
995  sprintf(buff, "%sHost      : %s\n", buff, owl_message_get_hostname(m));
996  sprintf(buff, "%sPort      : %i\n", buff, n->z_port);
997  strcat(buff,    "Auth      : ");
998  if (n->z_auth == ZAUTH_FAILED) {
999    strcat(buff, "FAILED\n");
1000  } else if (n->z_auth == ZAUTH_NO) {
1001    strcat(buff, "NO\n");
1002  } else if (n->z_auth == ZAUTH_YES) {
1003    strcat(buff, "YES\n");
1004  } else {
1005    sprintf(buff, "%sUnknown State (%i)\n", buff, n->z_auth);
1006  }
1007  sprintf(buff, "%sCheckd Ath: %i\n", buff, n->z_checked_auth);
1008  sprintf(buff, "%sMulti notc: %s\n", buff, n->z_multinotice);
1009  sprintf(buff, "%sNum other : %i\n", buff, n->z_num_other_fields);
1010  sprintf(buff, "%sMsg Len   : %i\n", buff, n->z_message_len);
1011
1012  sprintf(buff, "%sFields    : %i\n", buff, owl_zephyr_get_num_fields(n));
1013
1014  fields=owl_zephyr_get_num_fields(n);
1015  for (i=0; i<fields; i++) {
1016    sprintf(buff, "%sField %i   : ", buff, i+1);
1017
1018    ptr=owl_zephyr_get_field(n, i+1, &len);
1019    if (!ptr) break;
1020    if (len<30) {
1021      strncpy(tmpbuff, ptr, len);
1022      tmpbuff[len]='\0';
1023    } else {
1024      strncpy(tmpbuff, ptr, 30);
1025      tmpbuff[30]='\0';
1026      strcat(tmpbuff, "...");
1027    }
1028
1029    /* just for testing for now */
1030    for (j=0; j<strlen(tmpbuff); j++) {
1031      if (tmpbuff[j]=='\n') tmpbuff[j]='~';
1032      if (tmpbuff[j]=='\r') tmpbuff[j]='!';
1033    }
1034
1035    strcat(buff, tmpbuff);
1036    strcat(buff, "\n");
1037  }
1038  sprintf(buff, "%sDefault Fm: %s\n", buff, n->z_default_format);
1039       
1040  owl_function_popless_text(buff);
1041}
1042
1043
1044void owl_function_curmsg_to_popwin() {
1045  owl_popwin *pw;
1046  owl_view *v;
1047  owl_message *m;
1048
1049  v = owl_global_get_current_view(&g);
1050
1051  pw=owl_global_get_popwin(&g);
1052
1053  if (owl_view_get_size(v)==0) {
1054    owl_function_makemsg("No current message");
1055    return;
1056  }
1057
1058  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1059  owl_function_popless_fmtext(owl_message_get_fmtext(m));
1060}
1061
1062
1063void owl_function_page_curmsg(int step) {
1064  /* scroll down or up within the current message IF the message is truncated */
1065
1066  int offset, curmsg, lines;
1067  owl_view *v;
1068  owl_message *m;
1069
1070  offset=owl_global_get_curmsg_vert_offset(&g);
1071  v=owl_global_get_current_view(&g);
1072  if (owl_view_get_size(v)==0) return;
1073  curmsg=owl_global_get_curmsg(&g);
1074  m=owl_view_get_element(v, curmsg);
1075  lines=owl_message_get_numlines(m);
1076
1077  if (offset==0) {
1078    /* Bail if the curmsg isn't the last one displayed */
1079    if (curmsg != owl_mainwin_get_last_msg(owl_global_get_mainwin(&g))) {
1080      owl_function_makemsg("The entire message is already displayed");
1081      return;
1082    }
1083   
1084    /* Bail if we're not truncated */
1085    if (!owl_mainwin_is_curmsg_truncated(owl_global_get_mainwin(&g))) {
1086      owl_function_makemsg("The entire message is already displayed");
1087      return;
1088    }
1089  }
1090 
1091 
1092  /* don't scroll past the last line */
1093  if (step>0) {
1094    if (offset+step > lines-1) {
1095      owl_global_set_curmsg_vert_offset(&g, lines-1);
1096    } else {
1097      owl_global_set_curmsg_vert_offset(&g, offset+step);
1098    }
1099  }
1100
1101  /* would we be before the beginning of the message? */
1102  if (step<0) {
1103    if (offset+step<0) {
1104      owl_global_set_curmsg_vert_offset(&g, 0);
1105    } else {
1106      owl_global_set_curmsg_vert_offset(&g, offset+step);
1107    }
1108  }
1109 
1110  /* redisplay */
1111  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1112  owl_global_set_needrefresh(&g);
1113}
1114
1115void owl_function_resize_typwin(int newsize) {
1116  owl_global_set_typwin_lines(&g, newsize);
1117  owl_function_resize();
1118}
1119
1120void owl_function_typwin_grow() {
1121  int i;
1122
1123  i=owl_global_get_typwin_lines(&g);
1124  owl_function_resize_typwin(i+1);
1125}
1126
1127void owl_function_typwin_shrink() {
1128  int i;
1129
1130  i=owl_global_get_typwin_lines(&g);
1131  if (i>2) {
1132    owl_function_resize_typwin(i-1);
1133  }
1134}
1135
1136void owl_function_mainwin_pagedown() {
1137  int i;
1138
1139  i=owl_mainwin_get_last_msg(owl_global_get_mainwin(&g));
1140  if (i<0) return;
1141
1142  owl_global_set_curmsg(&g, i);
1143  owl_function_nextmsg();
1144}
1145
1146void owl_function_mainwin_pageup() {
1147  owl_global_set_curmsg(&g, owl_global_get_topmsg(&g));
1148  owl_function_prevmsg();
1149}
1150
1151void owl_function_getsubs() {
1152  int ret, num, i, one;
1153  ZSubscription_t sub;
1154  char *buff;
1155
1156  one = 1;
1157
1158  ret=ZRetrieveSubscriptions(0, &num);
1159  if (ret == ZERR_TOOMANYSUBS) {
1160
1161  }
1162
1163  buff=owl_malloc(num*200);
1164  strcpy(buff, "");
1165  for (i=0; i<num; i++) {
1166    if ((ret = ZGetSubscriptions(&sub, &one)) != ZERR_NONE) {
1167      /* deal with error */
1168    } else {
1169      sprintf(buff, "%s<%s,%s,%s>\n", buff, sub.zsub_class, sub.zsub_classinst, sub.zsub_recipient);
1170    }
1171  }
1172
1173  owl_function_popless_text(buff);
1174  owl_free(buff);
1175  ZFlushSubscriptions();
1176}
1177
1178#define PABUFLEN 5000
1179void owl_function_printallvars() {
1180  char buff[PABUFLEN], *pos, *name;
1181  owl_list varnames;
1182  int i, numvarnames, rem;
1183
1184  pos = buff;
1185  pos += sprintf(pos, "%-20s = %s\n", "VARIABLE", "VALUE");
1186  pos += sprintf(pos, "%-20s   %s\n",  "--------", "-----");
1187  owl_variable_dict_get_names(owl_global_get_vardict(&g), &varnames);
1188  rem = (buff+PABUFLEN)-pos-1;
1189  numvarnames = owl_list_get_size(&varnames);
1190  for (i=0; i<numvarnames; i++) {
1191    name = owl_list_get_element(&varnames, i);
1192    if (name && name[0]!='_') {
1193      rem = (buff+PABUFLEN)-pos-1;   
1194      pos += snprintf(pos, rem, "\n%-20s = ", name);
1195      rem = (buff+PABUFLEN)-pos-1;   
1196      owl_variable_get_tostring(owl_global_get_vardict(&g), name, pos, rem);
1197      pos = buff+strlen(buff);
1198    }
1199  }
1200  rem = (buff+PABUFLEN)-pos-1;   
1201  snprintf(pos, rem, "\n");
1202  owl_variable_dict_namelist_free(&varnames);
1203 
1204  owl_function_popless_text(buff);
1205}
1206
1207void owl_function_show_variables() {
1208  owl_list varnames;
1209  owl_fmtext fm; 
1210  int i, numvarnames;
1211  char *varname;
1212
1213  owl_fmtext_init_null(&fm);
1214  owl_fmtext_append_bold(&fm, 
1215      "Variables: (use 'show variable <name>' for details)\n");
1216  owl_variable_dict_get_names(owl_global_get_vardict(&g), &varnames);
1217  owl_variable_get_summaryheader(&fm);
1218  numvarnames = owl_list_get_size(&varnames);
1219  for (i=0; i<numvarnames; i++) {
1220    varname = owl_list_get_element(&varnames, i);
1221    if (varname && varname[0]!='_') {
1222      owl_variable_get_summary(owl_global_get_vardict(&g), varname, &fm);
1223    }
1224  }
1225  owl_variable_dict_namelist_free(&varnames);
1226  owl_function_popless_fmtext(&fm);
1227  owl_fmtext_free(&fm);
1228}
1229
1230void owl_function_show_variable(char *name) {
1231  owl_fmtext fm; 
1232
1233  owl_fmtext_init_null(&fm);
1234  owl_variable_get_help(owl_global_get_vardict(&g), name, &fm);
1235  owl_function_popless_fmtext(&fm);
1236  owl_fmtext_free(&fm); 
1237}
1238
1239/* note: this applies to global message list, not to view.
1240 * If flag is 1, deletes.  If flag is 0, undeletes. */
1241void owl_function_delete_by_id(int id, int flag) {
1242  owl_messagelist *ml;
1243  owl_message *m;
1244  ml = owl_global_get_msglist(&g);
1245  m = owl_messagelist_get_by_id(ml, id);
1246  if (m) {
1247    if (flag == 1) {
1248      owl_message_mark_delete(m);
1249    } else if (flag == 0) {
1250      owl_message_unmark_delete(m);
1251    }
1252    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1253    owl_global_set_needrefresh(&g);
1254  } else {
1255    owl_function_makemsg("No message with id %d: unable to mark for (un)delete",id);
1256  }
1257}
1258
1259void owl_function_delete_automsgs() {
1260  /* mark for deletion all messages in the current view that match the
1261   * 'trash' filter */
1262
1263  int i, j, count;
1264  owl_message *m;
1265  owl_view *v;
1266  owl_filter *f;
1267
1268  /* get the trash filter */
1269  f=owl_global_get_filter(&g, "trash");
1270  if (!f) {
1271    owl_function_makemsg("No trash filter defined");
1272    return;
1273  }
1274
1275  v=owl_global_get_current_view(&g);
1276
1277  count=0;
1278  j=owl_view_get_size(v);
1279  for (i=0; i<j; i++) {
1280    m=owl_view_get_element(v, i);
1281    if (owl_filter_message_match(f, m)) {
1282      count++;
1283      owl_message_mark_delete(m);
1284    }
1285  }
1286  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1287  owl_function_makemsg("%i messages marked for deletion", count);
1288  owl_global_set_needrefresh(&g);
1289}
1290
1291
1292void owl_function_status() {
1293  char buff[5000];
1294  time_t start;
1295  int up, days, hours, minutes;
1296
1297  start=owl_global_get_starttime(&g);
1298
1299  sprintf(buff, "Version: %s\n", OWL_VERSION_STRING);
1300  sprintf(buff, "%sScreen size: %i lines, %i columns\n", buff, owl_global_get_lines(&g), owl_global_get_cols(&g));
1301  sprintf(buff, "%sStartup Arugments: %s\n", buff, owl_global_get_startupargs(&g));
1302  sprintf(buff, "%sStartup Time: %s", buff, ctime(&start));
1303
1304  up=owl_global_get_runtime(&g);
1305  days=up/86400;
1306  up-=days*86400;
1307  hours=up/3600;
1308  up-=hours*3600;
1309  minutes=up/60;
1310  up-=minutes*60;
1311  sprintf(buff, "%sRun Time: %i days %2.2i:%2.2i:%2.2i\n", buff, days, hours, minutes, up);
1312
1313  if (owl_global_get_hascolors(&g)) {
1314    sprintf(buff, "%sColor: Yes, %i color pairs.\n", buff, owl_global_get_colorpairs(&g));
1315  } else {
1316    strcat(buff, "Color: No.\n");
1317  }
1318 
1319  owl_function_popless_text(buff);
1320}
1321
1322void owl_function_show_term() {
1323  owl_fmtext fm;
1324  char buff[LINE];
1325
1326  owl_fmtext_init_null(&fm);
1327  sprintf(buff, "Terminal Lines: %i\nTerminal Columns: %i\n",
1328          owl_global_get_lines(&g),
1329          owl_global_get_cols(&g));
1330  owl_fmtext_append_normal(&fm, buff);
1331
1332  if (owl_global_get_hascolors(&g)) {
1333    owl_fmtext_append_normal(&fm, "Color: Yes\n");
1334    sprintf(buff, "Number of color pairs: %i\n", owl_global_get_colorpairs(&g));
1335    owl_fmtext_append_normal(&fm, buff);
1336    sprintf(buff, "Can change colors: %s\n", can_change_color() ? "yes" : "no");
1337    owl_fmtext_append_normal(&fm, buff);
1338  } else {
1339    owl_fmtext_append_normal(&fm, "Color: No\n");
1340  }
1341
1342  owl_function_popless_fmtext(&fm);
1343  owl_fmtext_free(&fm);
1344}
1345
1346
1347void owl_function_reply(int type, int enter) {
1348  /* if type = 0 then normal reply.
1349   * if type = 1 then it's a reply to sender
1350   * if enter = 0 then allow the command to be edited
1351   * if enter = 1 then don't wait for editing
1352   */
1353  char buff[1024];
1354  owl_message *m;
1355  owl_filter *f;
1356 
1357  if (owl_view_get_size(owl_global_get_current_view(&g))==0) {
1358    owl_function_makemsg("No message selected");
1359  } else {
1360    char *class, *inst, *to;
1361   
1362    m=owl_view_get_element(owl_global_get_current_view(&g), owl_global_get_curmsg(&g));
1363
1364    /* first check if we catch the reply-lockout filter */
1365    f=owl_global_get_filter(&g, "reply-lockout");
1366    if (f) {
1367      if (owl_filter_message_match(f, m)) {
1368        owl_function_makemsg("Sorry, replies to this message have been disabled by the reply-lockout filter");
1369        return;
1370      }
1371    }
1372   
1373    if (owl_message_is_admin(m)) {
1374      if (owl_message_get_admintype(m)==OWL_MESSAGE_ADMINTYPE_OUTGOING) {
1375        owl_function_zwrite_setup(owl_message_get_zwriteline(m));
1376        owl_global_set_buffercommand(&g, owl_message_get_zwriteline(m));
1377      } else {
1378        owl_function_makemsg("You cannot reply to this admin message");
1379      }
1380    } else {
1381      if (owl_message_is_login(m)) {
1382        class="MESSAGE";
1383        inst="PERSONAL";
1384        to=owl_message_get_sender(m);
1385      } else if (type==1) {
1386        class="MESSAGE";
1387        inst="PERSONAL";
1388        to=owl_message_get_sender(m);
1389      } else {
1390        class=owl_message_get_class(m);
1391        inst=owl_message_get_instance(m);
1392        to=owl_message_get_recipient(m);
1393        if (!strcmp(to, "") || !strcmp(to, "*")) {
1394          to="";
1395        } else if (to[0]=='@') {
1396          /* leave it, to get the realm */
1397        } else {
1398          to=owl_message_get_sender(m);
1399        }
1400      }
1401     
1402      /* create the command line */
1403      strcpy(buff, "zwrite");
1404      if (strcasecmp(class, "message")) {
1405        sprintf(buff, "%s -c %s%s%s", buff, owl_getquoting(class), class, owl_getquoting(class));
1406      }
1407      if (strcasecmp(inst, "personal")) {
1408        sprintf(buff, "%s -i %s%s%s", buff, owl_getquoting(inst), inst, owl_getquoting(inst));
1409      }
1410      if (*to != '\0') {
1411        char *tmp;
1412        tmp=pretty_sender(to);
1413        sprintf(buff, "%s %s", buff, tmp);
1414        owl_free(tmp);
1415      }
1416
1417      if (enter) {
1418        owl_history_store(owl_global_get_history(&g), buff);
1419        owl_function_zwrite_setup(buff);
1420        owl_global_set_buffercommand(&g, buff);
1421      } else {
1422        owl_function_start_command(buff);
1423      }
1424    }
1425  }
1426}
1427
1428void owl_function_zlocate(char *user, int auth) {
1429  char buff[LINE], myuser[LINE];
1430  char *ptr;
1431
1432  strcpy(myuser, user);
1433  ptr=strchr(myuser, '@');
1434  if (!ptr) {
1435    strcat(myuser, "@");
1436    strcat(myuser, ZGetRealm());
1437  }
1438
1439  owl_zephyr_zlocate(myuser, buff, auth);
1440  owl_function_popless_text(buff);
1441}
1442
1443void owl_function_start_command(char *line) {
1444  int i, j;
1445  owl_editwin *tw;
1446
1447  tw=owl_global_get_typwin(&g);
1448  owl_global_set_typwin_active(&g);
1449  owl_editwin_set_locktext(tw, "command: ");
1450  owl_global_set_needrefresh(&g);
1451
1452  j=strlen(line);
1453  for (i=0; i<j; i++) {
1454    owl_editwin_process_char(tw, line[i]);
1455  }
1456  owl_editwin_redisplay(tw, 0);
1457}
1458
1459char *owl_function_exec(int argc, char **argv, char *buff, int type) {
1460  /* if type == 1 display in a popup
1461   * if type == 2 display an admin messages
1462   * if type == 0 return output
1463   * else display in a popup
1464   */
1465  char *newbuff, *redirect = " 2>&1 < /dev/null";
1466  char *out, buff2[1024];
1467  int size;
1468  FILE *p;
1469
1470  if (argc<2) {
1471    owl_function_makemsg("Wrong number of arguments to the pexec command");
1472    return NULL;
1473  }
1474
1475  buff = skiptokens(buff, 1);
1476  newbuff = owl_malloc(strlen(buff)+strlen(redirect)+1);
1477  strcpy(newbuff, buff);
1478  strcat(newbuff, redirect);
1479
1480  p=popen(newbuff, "r");
1481  out=owl_malloc(1024);
1482  size=1024;
1483  strcpy(out, "");
1484  while (fgets(buff2, 1024, p)!=NULL) {
1485    size+=1024;
1486    out=owl_realloc(out, size);
1487    strcat(out, buff2);
1488  }
1489  pclose(p);
1490
1491  if (type==1) {
1492    owl_function_popless_text(out);
1493  } else if (type==0) {
1494    return out;
1495  } else if (type==2) {
1496    owl_function_adminmsg(buff, out);
1497  } else {
1498    owl_function_popless_text(out);
1499  }
1500  owl_free(out);
1501  return NULL;
1502}
1503
1504
1505char *owl_function_perl(int argc, char **argv, char *buff, int type) {
1506  /* if type == 1 display in a popup
1507   * if type == 2 display an admin messages
1508   * if type == 0 return output
1509   * else display in a popup
1510   */
1511  char *perlout;
1512
1513  if (argc<2) {
1514    owl_function_makemsg("Wrong number of arguments to perl command");
1515    return NULL;
1516  }
1517
1518  /* consume first token (argv[0]) */
1519  buff = skiptokens(buff, 1);
1520
1521  perlout = owl_config_execute(buff);
1522  if (perlout) { 
1523    if (type==1) {
1524      owl_function_popless_text(perlout);
1525    } else if (type==2) {
1526      owl_function_adminmsg(buff, perlout);
1527    } else if (type==0) {
1528      return perlout;
1529    } else {
1530      owl_function_popless_text(perlout);
1531    }
1532    owl_free(perlout);
1533  }
1534  return NULL;
1535}
1536
1537
1538void owl_function_change_view(char *filtname) {
1539  owl_view *v;
1540  owl_filter *f;
1541
1542  v=owl_global_get_current_view(&g);
1543  f=owl_global_get_filter(&g, filtname);
1544  if (!f) {
1545    owl_function_makemsg("Unknown filter");
1546    return;
1547  }
1548
1549  owl_view_free(v);
1550  owl_view_create(v, f);
1551
1552  owl_global_set_curmsg(&g, 0);
1553  owl_global_set_curmsg_vert_offset(&g, 0);
1554  owl_global_set_direction_downwards(&g);
1555  owl_function_calculate_topmsg(OWL_DIRECTION_NONE);
1556  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1557}
1558
1559void owl_function_create_filter(int argc, char **argv) {
1560  owl_filter *f;
1561  owl_view *v;
1562  int ret, inuse=0;
1563
1564  if (argc < 2) {
1565    owl_function_makemsg("Wrong number of arguments to filter command");
1566    return;
1567  }
1568
1569  v=owl_global_get_current_view(&g);
1570
1571  /* don't touch the all filter */
1572  if (!strcmp(argv[1], "all")) {
1573    owl_function_makemsg("You may not change the 'all' filter.");
1574    return;
1575  }
1576
1577  /* deal with the case of trying change the filter color */
1578  if (argc==4 && !strcmp(argv[2], "-c")) {
1579    f=owl_global_get_filter(&g, argv[1]);
1580    if (!f) {
1581      owl_function_makemsg("The filter '%s' does not exist.", argv[1]);
1582      return;
1583    }
1584    owl_filter_set_color(f, owl_util_string_to_color(argv[3]));
1585    owl_global_set_needrefresh(&g);
1586    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1587    return;
1588  }
1589
1590  /* create the filter and check for errors */
1591  f=owl_malloc(sizeof(owl_filter));
1592  ret=owl_filter_init(f, argv[1], argc-2, argv+2);
1593  if (ret==-1) {
1594    owl_free(f);
1595    owl_function_makemsg("Invalid filter syntax");
1596    return;
1597  }
1598
1599  /* if the named filter is in use by the current view, remember it */
1600  if (!strcmp(owl_view_get_filtname(v), argv[1])) {
1601    inuse=1;
1602  }
1603
1604  /* if the named filter already exists, nuke it */
1605  if (owl_global_get_filter(&g, argv[1])) {
1606    owl_global_remove_filter(&g, argv[1]);
1607  }
1608
1609  /* add the filter */
1610  owl_global_add_filter(&g, f);
1611
1612  /* if it was in use by the current view then update */
1613  if (inuse) {
1614    owl_function_change_view(argv[1]);
1615  }
1616  owl_global_set_needrefresh(&g);
1617  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1618}
1619
1620void owl_function_show_filters() {
1621  owl_list *l;
1622  owl_filter *f;
1623  int i, j;
1624  owl_fmtext fm;
1625
1626  owl_fmtext_init_null(&fm);
1627
1628  l=owl_global_get_filterlist(&g);
1629  j=owl_list_get_size(l);
1630
1631  owl_fmtext_append_bold(&fm, "Filters:\n");
1632
1633  for (i=0; i<j; i++) {
1634    f=owl_list_get_element(l, i);
1635    owl_fmtext_append_normal(&fm, "   ");
1636    if (owl_global_get_hascolors(&g)) {
1637      owl_fmtext_append_normal_color(&fm, owl_filter_get_name(f), owl_filter_get_color(f));
1638    } else {
1639      owl_fmtext_append_normal(&fm, owl_filter_get_name(f));
1640    }
1641    owl_fmtext_append_normal(&fm, "\n");
1642  }
1643  owl_function_popless_fmtext(&fm);
1644  owl_fmtext_free(&fm);
1645}
1646
1647void owl_function_show_filter(char *name) {
1648  owl_filter *f;
1649  char buff[5000];
1650
1651  f=owl_global_get_filter(&g, name);
1652  if (!f) {
1653    owl_function_makemsg("There is no filter with that name");
1654    return;
1655  }
1656  owl_filter_print(f, buff);
1657  owl_function_popless_text(buff);
1658}
1659
1660void owl_function_show_zpunts() {
1661  owl_filter *f;
1662  owl_list *fl;
1663  char buff[5000];
1664  owl_fmtext fm;
1665  int i, j;
1666
1667  owl_fmtext_init_null(&fm);
1668
1669  fl=owl_global_get_puntlist(&g);
1670  j=owl_list_get_size(fl);
1671  owl_fmtext_append_bold(&fm, "Active zpunt filters:\n");
1672
1673  for (i=0; i<j; i++) {
1674    f=owl_list_get_element(fl, i);
1675    owl_filter_print(f, buff);
1676    owl_fmtext_append_normal(&fm, buff);
1677  }
1678  owl_function_popless_fmtext(&fm);
1679  owl_fmtext_free(&fm);
1680}
1681
1682char *owl_function_fastclassinstfilt(char *class, char *instance) {
1683  /* creates a filter for a class, instance if one doesn't exist.
1684   * If instance is null then apply for all messgaes in the class.
1685   * returns the name of the filter, which the caller must free.*/
1686  owl_list *fl;
1687  owl_filter *f;
1688  char *argbuff, *filtname;
1689  int len;
1690
1691  fl=owl_global_get_filterlist(&g);
1692
1693  /* name for the filter */
1694  len=strlen(class)+30;
1695  if (instance) len+=strlen(instance);
1696  filtname=owl_malloc(len);
1697  if (!instance) {
1698    sprintf(filtname, "class-%s", class);
1699  } else {
1700    sprintf(filtname, "class-%s-instance-%s", class, instance);
1701  }
1702  downstr(filtname);
1703
1704  /* if it already exists then go with it.  This lets users override */
1705  if (owl_global_get_filter(&g, filtname)) {
1706    return filtname;
1707  }
1708
1709  /* create the new filter */
1710  argbuff=owl_malloc(len+20);
1711  sprintf(argbuff, "( class ^%s$ )", class);
1712  if (instance) {
1713    sprintf(argbuff, "%s and ( instance ^%s$ )", argbuff, instance);
1714  }
1715
1716  f=owl_malloc(sizeof(owl_filter));
1717  owl_filter_init_fromstring(f, filtname, argbuff);
1718
1719  /* add it to the global list */
1720  owl_global_add_filter(&g, f);
1721
1722  owl_free(argbuff);
1723  return filtname;
1724}
1725
1726char *owl_function_fastuserfilt(char *user) {
1727  owl_filter *f;
1728  char *argbuff, *longuser, *shortuser, *filtname;
1729
1730  /* stick the local realm on if it's not there */
1731  longuser=long_sender(user);
1732  shortuser=pretty_sender(user);
1733
1734  /* name for the filter */
1735  filtname=owl_malloc(strlen(shortuser)+20);
1736  sprintf(filtname, "user-%s", shortuser);
1737
1738  /* if it already exists then go with it.  This lets users override */
1739  if (owl_global_get_filter(&g, filtname)) {
1740    return filtname;
1741  }
1742
1743  /* create the new-internal filter */
1744  f=owl_malloc(sizeof(owl_filter));
1745
1746  argbuff=owl_malloc(strlen(longuser)+200);
1747  sprintf(argbuff, "( ( class ^message$ ) and ( instance ^personal$ ) and ( sender ^%s$ ) )", longuser);
1748  sprintf(argbuff, "%s or ( ( type ^admin$ ) and ( recipient %s ) )", argbuff, shortuser);
1749  sprintf(argbuff, "%s or ( ( class ^login$ ) and ( sender ^%s$ ) )", argbuff, longuser);
1750
1751  owl_filter_init_fromstring(f, filtname, argbuff);
1752
1753  /* add it to the global list */
1754  owl_global_add_filter(&g, f);
1755
1756  /* free stuff */
1757  owl_free(argbuff);
1758  owl_free(longuser);
1759  owl_free(shortuser);
1760
1761  return filtname;
1762}
1763
1764/* If flag is 1, marks for deletion.  If flag is 0,
1765 * unmarks for deletion. */
1766void owl_function_delete_curview_msgs(int flag) {
1767  owl_view *v;
1768  int i, j;
1769
1770  v=owl_global_get_current_view(&g);
1771  j=owl_view_get_size(v);
1772  for (i=0; i<j; i++) {
1773    if (flag == 1) {
1774      owl_message_mark_delete(owl_view_get_element(v, i));
1775    } else if (flag == 0) {
1776      owl_message_unmark_delete(owl_view_get_element(v, i));
1777    }
1778  }
1779
1780  owl_function_makemsg("%i messages marked for %sdeletion", j, flag?"":"un");
1781
1782  owl_mainwin_redisplay(owl_global_get_mainwin(&g)); 
1783}
1784
1785char *owl_function_smartfilter(int type) {
1786  /* Returns the name of a filter, or null.  The caller
1787   * must free this name.  */
1788  /* if the curmsg is a personal message return a filter name
1789   *    to the converstaion with that user.
1790   * If the curmsg is a class message, instance foo, recip *
1791   *    message, return a filter name to the class, inst.
1792   * If the curmsg is a class message and type==0 then
1793   *    return a filter name for just the class.
1794   * If the curmsg is a class message and type==1 then
1795   *    return a filter name for the class and instance.
1796   */
1797  owl_view *v;
1798  owl_message *m;
1799  char *sender, *filtname=NULL;
1800 
1801  v=owl_global_get_current_view(&g);
1802  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1803
1804  if (owl_view_get_size(v)==0) {
1805    owl_function_makemsg("No message selected\n");
1806    return NULL;
1807  }
1808
1809  /* for now we skip admin messages. */
1810  if (owl_message_is_admin(m)) {
1811    owl_function_makemsg("Narrowing on an admin message has not been implemented yet.  Check back soon.");
1812    return NULL;
1813  }
1814
1815  /* narrow personal and login messages to the sender */
1816  if (owl_message_is_personal(m) || owl_message_is_login(m)) {
1817    if (owl_message_is_zephyr(m)) {
1818      sender=pretty_sender(owl_message_get_sender(m));
1819      filtname = owl_function_fastuserfilt(sender);
1820      owl_free(sender);
1821      return filtname;
1822    }
1823    return NULL;
1824  }
1825
1826  /* narrow class MESSAGE, instance foo, recip * messages to class, inst */
1827  if (!strcasecmp(owl_message_get_class(m), "message") &&
1828      !owl_message_is_personal(m)) {
1829    filtname = owl_function_fastclassinstfilt(owl_message_get_class(m), owl_message_get_instance(m));
1830    return filtname;
1831  }
1832
1833  /* otherwise narrow to the class */
1834  if (type==0) {
1835    filtname = owl_function_fastclassinstfilt(owl_message_get_class(m), NULL);
1836  } else if (type==1) {
1837    filtname = owl_function_fastclassinstfilt(owl_message_get_class(m), owl_message_get_instance(m));
1838  }
1839  return filtname;
1840}
1841
1842void owl_function_smartzpunt(int type) {
1843  /* Starts a zpunt command based on the current class,instance pair.
1844   * If type=0, uses just class.  If type=1, uses instance as well. */
1845  owl_view *v;
1846  owl_message *m;
1847  char *cmd, *cmdprefix, *mclass, *minst;
1848 
1849  v=owl_global_get_current_view(&g);
1850  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1851
1852  if (owl_view_get_size(v)==0) {
1853    owl_function_makemsg("No message selected\n");
1854    return;
1855  }
1856
1857  /* for now we skip admin messages. */
1858  if (owl_message_is_admin(m)
1859      || owl_message_is_login(m)
1860      || !owl_message_is_zephyr(m)) {
1861    owl_function_makemsg("smartzpunt doesn't support this message type.");
1862    return;
1863  }
1864
1865  mclass = owl_message_get_class(m);
1866  minst = owl_message_get_instance(m);
1867  if (!mclass || !*mclass || *mclass==' '
1868      || (!strcasecmp(mclass, "message") && !strcasecmp(minst, "personal"))
1869      || (type && (!minst || !*minst|| *minst==' '))) {
1870    owl_function_makemsg("smartzpunt can't safely do this for <%s,%s>",
1871                         mclass, minst);
1872  } else {
1873    cmdprefix = "start-command zpunt ";
1874    cmd = owl_malloc(strlen(cmdprefix)+strlen(mclass)+strlen(minst)+3);
1875    strcpy(cmd, cmdprefix);
1876    strcat(cmd, mclass);
1877    if (type) {
1878      strcat(cmd, " ");
1879      strcat(cmd, minst);
1880    } else {
1881      strcat(cmd, " *");
1882    }
1883    owl_function_command(cmd);
1884    owl_free(cmd);
1885  }
1886}
1887
1888
1889
1890void owl_function_color_current_filter(char *color) {
1891  owl_filter *f;
1892  char *name;
1893
1894  name=owl_view_get_filtname(owl_global_get_current_view(&g));
1895  f=owl_global_get_filter(&g, name);
1896  if (!f) {
1897    owl_function_makemsg("Unknown filter");
1898    return;
1899  }
1900
1901  /* don't touch the all filter */
1902  if (!strcmp(name, "all")) {
1903    owl_function_makemsg("You may not change the 'all' filter.");
1904    return;
1905  }
1906
1907  /* deal with the case of trying change the filter color */
1908  owl_filter_set_color(f, owl_util_string_to_color(color));
1909  owl_global_set_needrefresh(&g);
1910  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1911}
1912
1913void owl_function_show_colors() {
1914  owl_fmtext fm;
1915
1916  owl_fmtext_init_null(&fm);
1917  owl_fmtext_append_normal_color(&fm, "default\n", OWL_COLOR_DEFAULT);
1918  owl_fmtext_append_normal_color(&fm, "red\n", OWL_COLOR_RED);
1919  owl_fmtext_append_normal_color(&fm, "green\n", OWL_COLOR_GREEN);
1920  owl_fmtext_append_normal_color(&fm, "yellow\n", OWL_COLOR_YELLOW);
1921  owl_fmtext_append_normal_color(&fm, "blue\n", OWL_COLOR_BLUE);
1922  owl_fmtext_append_normal_color(&fm, "magenta\n", OWL_COLOR_MAGENTA);
1923  owl_fmtext_append_normal_color(&fm, "cyan\n", OWL_COLOR_CYAN);
1924  owl_fmtext_append_normal_color(&fm, "white\n", OWL_COLOR_WHITE);
1925
1926  owl_function_popless_fmtext(&fm);
1927  owl_fmtext_free(&fm);
1928}
1929
1930void owl_function_zpunt(char *class, char *inst, char *recip, int direction) {
1931  /* add the given class, inst, recip to the punt list for filtering.
1932   *   if direction==0 then punt
1933   *   if direction==1 then unpunt */
1934  owl_filter *f;
1935  owl_list *fl;
1936  char *buff;
1937  int ret, i, j;
1938
1939  fl=owl_global_get_puntlist(&g);
1940
1941  /* first, create the filter */
1942  f=malloc(sizeof(owl_filter));
1943  buff=malloc(strlen(class)+strlen(inst)+strlen(recip)+100);
1944  if (!strcmp(recip, "*")) {
1945    sprintf(buff, "class ^%s$ and instance ^%s$", class, inst);
1946  } else {
1947    sprintf(buff, "class ^%s$ and instance ^%s$ and recipient %s", class, inst, recip);
1948  }
1949  owl_function_debugmsg("About to filter %s", buff);
1950  ret=owl_filter_init_fromstring(f, "punt-filter", buff);
1951  owl_free(buff);
1952  if (ret) {
1953    owl_function_makemsg("Error creating filter for zpunt");
1954    owl_filter_free(f);
1955    return;
1956  }
1957
1958  /* Check for an identical filter */
1959  j=owl_list_get_size(fl);
1960  for (i=0; i<j; i++) {
1961    if (owl_filter_equiv(f, owl_list_get_element(fl, i))) {
1962      /* if we're punting, then just silently bow out on this duplicate */
1963      if (direction==0) {
1964        owl_filter_free(f);
1965        return;
1966      }
1967
1968      /* if we're unpunting, then remove this filter from the puntlist */
1969      if (direction==1) {
1970        owl_filter_free(owl_list_get_element(fl, i));
1971        owl_list_remove_element(fl, i);
1972        return;
1973      }
1974    }
1975  }
1976
1977  /* If we're punting, add the filter to the global punt list */
1978  if (direction==0) {
1979    owl_list_append_element(fl, f);
1980  }
1981}
1982
1983void owl_function_activate_keymap(char *keymap) {
1984  if (!owl_keyhandler_activate(owl_global_get_keyhandler(&g), keymap)) {
1985    owl_function_makemsg("Unable to activate keymap '%s'", keymap);
1986  }
1987}
1988
1989
1990void owl_function_show_keymaps() {
1991  owl_list l;
1992  owl_fmtext fm;
1993  owl_keymap *km;
1994  owl_keyhandler *kh;
1995  int i, numkm;
1996  char *kmname;
1997
1998  kh = owl_global_get_keyhandler(&g);
1999  owl_fmtext_init_null(&fm);
2000  owl_fmtext_append_bold(&fm, "Keymaps:   ");
2001  owl_fmtext_append_normal(&fm, "(use 'show keymap <name>' for details)\n");
2002  owl_keyhandler_get_keymap_names(kh, &l);
2003  owl_fmtext_append_list(&fm, &l, "\n", owl_function_keymap_summary);
2004  owl_fmtext_append_normal(&fm, "\n");
2005
2006  numkm = owl_list_get_size(&l);
2007  for (i=0; i<numkm; i++) {
2008    kmname = owl_list_get_element(&l, i);
2009    km = owl_keyhandler_get_keymap(kh, kmname);
2010    owl_fmtext_append_bold(&fm, "\n\n----------------------------------------------------------------------------------------------------\n\n");
2011    owl_keymap_get_details(km, &fm);   
2012  }
2013  owl_fmtext_append_normal(&fm, "\n");
2014 
2015  owl_function_popless_fmtext(&fm);
2016  owl_keyhandler_keymap_namelist_free(&l);
2017  owl_fmtext_free(&fm);
2018}
2019
2020char *owl_function_keymap_summary(void *name) {
2021  owl_keymap *km
2022    = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), name);
2023  if (km) return owl_keymap_summary(km);
2024  else return(NULL);
2025}
2026
2027/* TODO: implement for real */
2028void owl_function_show_keymap(char *name) {
2029  owl_fmtext  fm;
2030  owl_keymap *km;
2031
2032  owl_fmtext_init_null(&fm);
2033  km = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), name);
2034  if (km) {
2035    owl_keymap_get_details(km, &fm);
2036  } else {
2037    owl_fmtext_append_normal(&fm, "No such keymap...\n");
2038  } 
2039  owl_function_popless_fmtext(&fm);
2040  owl_fmtext_free(&fm);
2041}
2042
2043
2044void owl_function_help_for_command(char *cmdname) {
2045  owl_fmtext   fm;
2046
2047  owl_fmtext_init_null(&fm);
2048  owl_cmd_get_help(owl_global_get_cmddict(&g), cmdname, &fm);
2049  owl_function_popless_fmtext(&fm); 
2050  owl_fmtext_free(&fm);
2051}
Note: See TracBrowser for help on using the repository browser.