static char rcsid[] = "$Id: mmfcn.c,v 1.2 1992/11/08 23:29:54 mike Exp $";

/* $Log: mmfcn.c,v $
 * Revision 1.2  1992/11/08  23:29:54  mike
 * - Added view mode.
 *
 * Revision 1.1  1992/09/05  01:13:32  mike
 * Initial revision
 *
 */

/*
 * mmfcn.c : Main loop for external Mutt (ME2) functions.
 * Craig Durland 6/87
 */

/* Copyright 1990, 1991, 1992 Craig Durland
 *   Distributed under the terms of the GNU General Public License.
 *   Distributed "as is", without warranties of any kind, but comments,
 *     suggestions and bug reports are welcome.
 */

#include <char.h>
#include "me2.h"
#include "mm.h"
#include "bind.h"

extern Bag *id_to_bag();
extern Buffer *bfind(), *Mid_to_buffer();
extern char
  current_directory[],				/* in os.c */
  result[],					/* in mm.c */
  **zargv,					/* in main.c */
  *strcpy(), *getenv();
extern int
  currow, zargc, MMask_pgm,			/* in mm.c */
  arg_flag, arg_prefix, pgm_flag, pgm_prefix,	/* in mmaux.c */
  svsize;					/* in mmaux.c */
extern KeyCode key_pressed, to_keycode();
extern MuttCmd sysvars[];			/* in mmaux.c */
extern PKey pkeys[];				/* in bind.c */
extern MMDatum RV, TV;				/* in mm.c */
extern Window *Mnth_window(), *nthwindow();





#define BIT_AND	0
#define BIT_OR	1
#define BIT_XOR	2

static void bitop(op)				       /* (bit-op x [y ...] */
{
  int j;

  get1arg(NUMBER,"bit-");
  for (j = 1; maybearg(j++,NUMBER,"bit-"); )
  {
    switch (op)
    {
      case BIT_XOR: RV.val.num ^= TV.val.num; break;
      case BIT_AND: RV.val.num &= TV.val.num; break;
      case BIT_OR : RV.val.num |= TV.val.num; break;
    }
  }
}

void Mdomutt(t)
{
  Bag *bag;
  Buffer *bp;
  char *ptr;
  int x, n;
  KeyCode kc;
  Window *wp;

  switch(t)
  {
/* ************ Misc ************************** */
    case 579:						       /* (do-undo) */
      if (curbp->b_flags & BFVIEW)
      {
        view_mode_violation();
	RV.val.num = 2;		/* Problems */
      }
      else
        RV.val.num = undo();
      RV.type = NUMBER;
      break;
    case 546:				     /* (modify-syntax-entry "???") */
      get1arg(STRING,"modify-syntax-entry");
      {
	char c = *RV.val.str;
	if (c == '\0') MMbitch("invalid string format");
	for (ptr = RV.val.str+1; *ptr; ptr++)
	  switch (c)
	  {
	    case 'w':    add_cinfo(*ptr,_W); break;
	    case 'W': remove_cinfo(*ptr,_W); break;
	  }
      }
      break;
    case 501:					     /* (sysvar name [val]) */
      get1arg(STRING,"sysvar");
      if ((n = lookupmut(RV.val.str,sysvars,svsize)) == -1)
		MMbitch("That ain't no sysvar");
      Msys_var(n,1);
      break;
    case 524:					      /* (insert-text text) */
      if (curbp->b_flags & BFVIEW)
        view_mode_violation();
      else
      {
	MMconcat();
	if (!insert_text(result,strlen(result))) MMabort_pgm(2);
      }
      RV.type = VOID;
      break;
    case 533:						      /* (to-col n) */
      get1arg(NUMBER,"to-col"); to_col((int)RV.val.num -1); break;
    case 525:				      /* (is-space), newline!=space */
      RV.val.num = (the_dot->offset == llength(the_dot->line)) ?
	FALSE : isspace(lgetc(the_dot->line,the_dot->offset));
      RV.type = BOOLEAN;
      break;
    case 516:						/* (arg-prefix [n]) */
      if (maybearg(0,NUMBER,"arg-prefix"))
        { arg_flag = pgm_flag = TRUE; arg_prefix = pgm_prefix = TV.val.num; }
      RV.type = NUMBER; RV.val.num = pgm_prefix;
      break;
    case 521:						      /* (arg-flag) */
      RV.type = BOOLEAN; RV.val.num = pgm_flag; break;
    case 530:					      /* (file-exists name) */
      get1arg(STRING,"file-exists");
      RV.type = BOOLEAN; RV.val.num = file_exists(RV.val.str);
      break;
    case 515:					       /* (pgm-exists name) */
      get1arg(STRING,"pgm-exists");
      RV.type = BOOLEAN;
      RV.val.num = (-1 != MMpgm_lookup(RV.val.str));
      break;
/* *********** Math ************************************* */
    case 527: bitop(BIT_AND); break;		      /* (bit-and x [y ...] */
    case 528: bitop(BIT_OR);  break;		      /* (bit-or  x [y ...] */
    case 529: bitop(BIT_XOR); break;		      /* (bit-xor x [y ...] */
/* *********** Query the User *************************** */
    case 551:				      /* (complete selector prompt) */
      get2args(NUMBER,STRING,"complete");
      MMset_ask_frame();
      mlreply(TV.val.str,result,RSIZ,(int)RV.val.num);
      RV.type = STRING; RV.val.str = result;
      MMreset_ask_frame();
      break;
    case 512:					       /* (yesno prompt[s]) */
      MMconcat();
      MMset_ask_frame(); RV.val.num = mlyesno(result); MMreset_ask_frame();
      RV.type = BOOLEAN;
      break;
    case 522:						       /* (getchar) */
      get_McKc(&kc,FALSE); *result = kc; *(result+1) = '\0';
      RV.type = STRING; RV.val.str = result;
      break;
/* ***************** Cursor related stuff ******************* */
    case 511:					    /* (current-column [n]) */
      if (maybearg(0,NUMBER,"current-column"))
      {
	the_dot->offset = getgoal(the_dot->line,(int)TV.val.num -1);
      }
      RV.val.num = getccol() +1; RV.type = NUMBER;
      break;
    case 519:						/* (forward-line n) */
      get1arg(NUMBER,"forward-line");
      RV.val.num = next_line((int)RV.val.num);
      RV.type = BOOLEAN;
      break;
    case 576:						   /* (goto-line n) */
      get1arg(NUMBER,"goto-line");
      RV.val.num = goto_line((int)RV.val.num);
      RV.type = BOOLEAN;
      break;
    case 557:					   /* (move-cursor row col) */
      get2args(NUMBER,NUMBER,"move-cursor");
      t_move((int)RV.val.num,(int)TV.val.num);
      RV.type = VOID; break;
    case 505:							   /* (EoB) */
      RV.type = BOOLEAN;
      RV.val.num = (the_dot->line == BUFFER_LAST_LINE(curbp));
      break;
/* ******** Marks ********************** */
    case 526:					/* (create-mark [immortal]) */
      x = FALSE; if (maybearg(0,BOOLEAN,"create-mark")) x = TV.val.num;
      if (-1 == (RV.val.num = alloc_buffer_mark(x))) MMbitch("No memory!");
      RV.type = NUMBER;
      break;
    case 553:					 /* (free-mark mark-id ...) */
      n = 0;
      while (maybearg(n++,NUMBER,"free-mark"))
	free_buffer_mark((int)TV.val.num);
      RV.type = VOID;
      break;
    case 550:					    /* (set-mark [mark-id]) */
      n = THE_MARK;
      if (maybearg(0,NUMBER,"set-mark")) n = TV.val.num;
#if 0	/* ??? die on bad mark??? */
      RV.val.num = set_mark(n);
      RV.type = BOOLEAN;
#else
      if (!set_mark(n)) MMbitch("set-mark:  Bad mark.");
#endif
      break;
    case 547:					     /* (goto-mark mark-id) */
      get1arg(NUMBER,"goto-mark");
      RV.val.num = goto_mark((int)RV.val.num);
      RV.type = BOOLEAN;	/* ??? die if bad mark? */
      break;
    case 583: Mswap_marks(); break;	 /* (swap-marks [mark-id mark-id])) */
    case 540: Mcompare_marks(); break; /* (compare-marks [mark-id mark-id]) */
/* ******** Keys ********************** */
    case 514:					      /* (key-pressed [kc]) */
/*???  if (maybearg(0,NUMBER,"key-pressed")) key_pressed = TV.val.num; */
      RV.val.num = key_pressed; RV.type = NUMBER;
      break;
    case 502:						       /* (get-key) */
      get_McKc(&kc,TRUE); RV.type = NUMBER; RV.val.num = kc; break;
    case 503:						   /* (exe-key int) */
      get1arg(NUMBER,"exe-key");
      n = MMask_pgm; MMask_pgm = FALSE;
      RV.val.num = do_key((KeyCode)RV.val.num,arg_flag,arg_prefix);
      MMask_pgm = n; arg_flag = FALSE; arg_prefix = 1;
      if (RV.val.num == ABORT) MMabort_pgm(0);
      RV.type = BOOLEAN;
      break;
    case 504:					 /* (key-waiting [seconds]) */
      n = 0;
      if (maybearg(0,NUMBER,"key-waiting")) n = TV.val.num;
      RV.type = BOOLEAN; RV.val.num = wait_for_key(n);
      break;
    case 562:				       /* (key-bound-to key-string) */
      get1arg(STRING,"key-bound-to"); dscrib_key(RV.val.str,result);
      RV.val.str = result;
      break;
    case 567:					 /* (prefix-key n [string]) */
      n = get1num(0,PKEYS,"prefix-key");
      if (maybearg(1,STRING,"prefix-key"))
	pkeys[n] = to_keycode(TV.val.str,FALSE);
      RV.type = STRING; RV.val.str = result;
      *result = '\0'; xpandkey(result,pkeys[n]);
      break;
/* **************** Windows ************************** */
    case 559:						     /* (windows) */
      RV.type = NUMBER; RV.val.num = cntwindows((Window *)NULL); break;
    case 558:					    /* (current-window [n]) */
      if (maybearg(0,NUMBER,"current-window"))	/* move to nth window */
	use_window(nthwindow(first_window,(int)TV.val.num));
      RV.type = NUMBER; RV.val.num = cntwindows(curwp);
      break;
    case 555:						    /* (window-row) */
      RV.type = NUMBER; RV.val.num = currow -curwp->w_toprow +1; break;
    case 556:						 /* (free-window n) */
      get1arg(NUMBER,"free-window");
      RV.type = BOOLEAN;
      RV.val.num = free_window(Mnth_window((int)RV.val.num));
      break;
    case 564:				    /* (window-ledge n [left-edge]) */
      get1arg(NUMBER,"window-ledge");
      wp = Mnth_window((int)RV.val.num);
      if (maybearg(1,NUMBER,"window-ledge"))
	{ wp->lmargin = imax(0,(int)TV.val.num); wp->w_flag |= WFHARD; }
      RV.val.num = wp->lmargin;
      break;
    case 520:					/* (window-height n [rows]) */
      get1arg(NUMBER,"window-height");
      wp = Mnth_window((int)RV.val.num);
      if (maybearg(1,NUMBER,"window-height"))
	change_window_size(wp,(int)TV.val.num);
      RV.val.num = wp->w_ntrows;
      break;
    case 534:					/* (update [update-screen]) */
      sync_dot();
      n = TRUE;
      if (maybearg(0,BOOLEAN,"update")) n = TV.val.num;
      if (n) update();
      RV.type = VOID;
      break;
/* ******************** Buffers *********************** */
    case 543:					 /* (free-buffer buffer-id) */
      get1arg(NUMBER,"free-buffer");
      free_buffer(Mid_to_buffer((int)RV.val.num));
      break;
    case 541: Mcreate_buffer(); break;	    /* (create-buffer name [flags]) */
    case 542: Mnth_buffer(); break;    /* (nth-buffer n [id [flags]]) => id */
    case 560:	       /* (attached-buffer window-id | "name") => buffer-id */
      if (!MMpull_nth_arg(&RV,0) || (RV.type != NUMBER && RV.type != STRING))
        arg_bitch("attached-buffer");
      if (RV.type == NUMBER)
	  RV.val.num = Mnth_window((int)RV.val.num)->wbuffer->id;
      else RV.val.num = (bp = bfind(RV.val.str,FALSE,0)) ? (int)bp->id : -2;
      RV.type = NUMBER;
      break;
    case 554:		    /* (buffer-stats buffer-id (array INT stats 6)) */
      Mbuffer_stats(); break;
    case 508:				  /* (buffer-name buffer-id [name]) */
      get1arg(NUMBER,"buffer-name");
      RV.val.str = strcpy(result,
	get_dString(Mid_to_buffer((int)RV.val.num)->b_bname));
      RV.type = STRING;
      break;
    case 507:			    /* (buffer-modified [buffer-id [bool]]) */
#if 0
      bp =
	maybearg(0,NUMBER,"buffer-modified") ?
		Mid_to_buffer((int)TV.val.num) : curbp;
#else
      get1arg(NUMBER,"buffer-modified");
      bp = Mid_to_buffer((int)RV.val.num);
#endif
      if (maybearg(1,BOOLEAN,"buffer-modified"))
	set_buffer_modified(bp, (int)TV.val.num);
      RV.type = BOOLEAN; RV.val.num = is_buffer_modified(bp);
      break;
    case 566:				/* (buffer-flags buffer-id [flags]) */
      get1arg(NUMBER,"buffer-flags");
      bp = Mid_to_buffer((int)RV.val.num);
      if (maybearg(1,NUMBER,"buffer-flags"))
	set_buffer_flags(bp,TV.val.num);
      RV.type = NUMBER; RV.val.num = bp->b_flags;
      break;
    case 509:					  /* (buffers [skip-flags]) */
      RV.type = NUMBER; RV.val.num = cntbufs((Buffer *)NULL); break;
    case 513:		       /* (current-buffer [buffer-id [display-it]]) */
      if (maybearg(0,NUMBER,"current-buffer"))
      {
	bp = Mid_to_buffer((int)TV.val.num);
	x = FALSE;
	if (maybearg(1,BOOLEAN,"current-buffer")) x = TV.val.num;
	use_buffer(bp,x);
      }
      RV.val.num = curbp->id;
      RV.type = NUMBER;
      break;
    case 561:						  /* (clear-buffer) */
      if (curbp->b_flags & BFVIEW)
        view_mode_violation();
      else
        clear_buffer(curbp);
      RV.type = VOID;
      break;
    case 510:				    /* (file-name buffer-id [name]) */
      get1arg(NUMBER,"file-name");
      bp = Mid_to_buffer((int)RV.val.num);
      if (maybearg(1,STRING,"file-name"))	       /* rename the file */
	{ RV.type = BOOLEAN; RV.val.num = newfilename(bp,TV.val.str); }
      else	/* return the name of the file attached to the buffer */
      {
	RV.type = STRING;
	RV.val.str = strcpy(result,get_dString(bp->b_fname));
      }
      break;
/* ******************** Buffer Variables *********************** */
    case 577:			       /* (create-buffer-var type name ...) */
      get1arg(NUMBER,"create-buffer-var");	/* type */
      for (n = 0; maybearg(++n,STRING,"create-buffer-var"); )
	if (!alloc_bvar(TV.val.str,(int)RV.val.num))
		MMbitch("create-buffer-var: bad type or no memory.");
      RV.type = VOID;
      break;
    case 578:				  /* (buffer-var name [val]) */
      get1arg(STRING,"buffer-var"); TV = RV;
      x = MMpull_nth_arg(&RV,1) ? TRUE : FALSE;
      if (!access_bvar(TV.val.str,x))	/* sets RV */
		MMbitch("buffer-var: bad name or type mismatch.");
      break;
/* ******************* Regular Expressions ******************** */
    case 548:					 /* (looking-at RE-pattern) */
      MMconcat();
      if ((RV.val.num = looking_at(result)) == ABORT) MMabort_pgm(0);
      RV.type = BOOLEAN;
      break;
    case 506:				   /* (re-string re-pattern string) */
      get2args(STRING,STRING,"re-string");
      if ((RV.val.num = REstring(RV.val.str,TV.val.str)) ==ABORT)
		MMabort_pgm(0);
      RV.type = BOOLEAN;
      break;
    case 549:					    /* (get-matched RE-sub) */
      get1arg(STRING,"get-matched");
      if (!re_subs(RV.val.str,result)) MMbitch("get-matched failed.");
      RV.val.str = result;
      break;
/* *************** Bags ********************************** */
    case 537:					 /* (create-bag [immortal]) */
      x = maybearg(0,BOOLEAN,"create-bag") ? TV.val.num : FALSE;
      if (!(bag = alloc_bag(x))) MMbitch("Create-bag: out of memory");
      RV.type = NUMBER;
      RV.val.num = bag->id;
      break;
    case 538:					   /* (free-bag bag-id ...) */
      n = 0;
      while (maybearg(n++,NUMBER,"free-bag"))
      {
	if (!(bag = id_to_bag((int)TV.val.num))) arg_bitch("free-bag");
	free_bag(bag);
      }
      TV.type = VOID;
      break;
    case 518:					 /* (bag-stats bag-id blob) */
      get2args(NUMBER,BLOB,"bag-stats");
      if (!(bag = id_to_bag((int)RV.val.num))) arg_bitch("bag-stats");
      PUT_UINT8(TV.val.blob,   bag_type(bag));
      put_int16(TV.val.blob+1, bag->width);
      put_int16(TV.val.blob+3, bag->height);
      put_int32(TV.val.blob+5, (int32)sizeof_dTable(&bag->text));
      RV.type = VOID;
      break;
    case 569: Mappend_to_bag(); break;	   /* (append-to-bag bag-id [text]) */
    case 570:					      /* (clear-bag bag-id) */
      get1arg(NUMBER,"clear-bag");
      if (!(bag = id_to_bag((int)RV.val.num))) arg_bitch("clear-bag");
      clear_bag(bag);
      break;
    case 571:					     /* (insert-bag bag-id) */
      if (curbp->b_flags & BFVIEW)
        view_mode_violation();
      else
      {
	get1arg(NUMBER,"clear-bag");
	if (!(bag = id_to_bag((int)RV.val.num))) arg_bitch("insert-bag");
	if (!insert_bag(bag)) MMabort_pgm(0);
      }
      RV.type = VOID;
      break;
    case 532:				  /* (bag-to-file bag-id file-name) */
      get2args(NUMBER,STRING,"bag-to-file");
      if (!(bag = id_to_bag((int)RV.val.num))) arg_bitch("bag-to-file");
      RV.val.num = bag_to_file(bag,TV.val.str);
      RV.type = BOOLEAN;
      break;
    case 535:				  /* (file-to-bag file-name bag-id) */
      get2args(STRING,NUMBER,"register-to-file");
      if (!(bag = id_to_bag((int)TV.val.num))) arg_bitch("file-to-bag");
      RV.val.num = file_to_bag(RV.val.str,bag);
      RV.type = BOOLEAN;
      break;
    case 580:					/* (case-bag op bag-id ...) */
      get2args(NUMBER,NUMBER,"case-bag");
      if (!(bag = id_to_bag((int)TV.val.num))) arg_bitch("case-bag");
      case_text(bag->text.table, sizeof_dTable(&bag->text), (int)RV.val.num);

      RV.type = VOID;
      break;
    case 582: Mbag_to_string(); break;		  /* (bag-to-string bag-id) */
/* *************** Rectangles **************************** */
    case 523:				     /* (erase-rectangle delete-it) */
      if (curbp->b_flags & BFVIEW)
        view_mode_violation();
      else
      {
        get1arg(BOOLEAN,"erase-rectangle");
        if (!erase_rect((int)RV.val.num)) MMabort_pgm(0);
      }
      RV.type = VOID;
      break;
/* *************** Regions ********************************* */
    case 517: Mregion_stats(); break;		     /* (region-stats blob) */
/* ************* OS related stuff ************************* */
    case 531:			       /* (puts strings) string to terminal */
      MMconcat(); t_puts(result);
      RV.type = STRING; RV.val.str = result;
      break;
    case 544: RV.type = NUMBER; RV.val.num = zargc; break;	  /* (argc) */
    case 545:							/* (argv n) */
      RV.val.str = zargv[get1num(0,zargc,"argv")]; RV.type = STRING; break;
    case 565:				    /* (current-directory [newdir]) */
      if (maybearg(0,STRING,"current-directory"))
      {
        RV.type = BOOLEAN;
	RV.val.num = change_directory(TV.val.str);
      }
      else
      {
	strcpy(result, current_directory);
        RV.type = STRING; RV.val.str = result;
      }
      break;
    case 568:						   /* (getenv name) */
      get1arg(STRING,"getenv");
      if ((ptr = getenv(RV.val.str)) == NULL) *result = '\0';
      else strcpy(result,ptr);
      RV.type = STRING; RV.val.str = result;
      break;
    case 536: Mfilter(); break;				 /* (OS-filter ...) */
    case 581:		      /* (create-process <command> <arg> <arg> ...) */
      create_process();
      break;
/* ****************** Searching *************************** */
    case 552:					/* (search-forward pattern) */
      get1arg(STRING,"search-forward");
      RV.val.num = search_forward(RV.val.str);
      RV.type = BOOLEAN;
      break;
    case 563:					/* (search-reverse pattern) */
      get1arg(STRING,"search-reverse");
      RV.val.num = search_reverse(RV.val.str);
      RV.type = BOOLEAN;
      break;
    case 572:				     /* (re-search-forward pattern) */
      get1arg(STRING,"re-search-forward");
      RV.val.num = re_search_forward(RV.val.str);
      if (RV.val.num == ABORT) MMabort_pgm(0);
      RV.type = BOOLEAN;
      break;
    case 573:				     /* (re-search-reverse pattern) */
      get1arg(STRING,"re-search-reverse");
      RV.val.num = re_search_reverse(RV.val.str);
      if (RV.val.num == ABORT) MMabort_pgm(0);
      RV.type = BOOLEAN;
      break;
    case 574:			   /* (search-replace pattern replace-with) */
      if (curbp->b_flags & BFVIEW)
      {
        view_mode_violation();
	RV.val.num = FALSE;
      }
      else
      {
	get2args(STRING,STRING,"search-replace");
	RV.val.num = search_replace(RV.val.str,TV.val.str);
	if (RV.val.num == ABORT) MMabort_pgm(0);
      }
      RV.type = BOOLEAN;
      break;
    case 575:			/* (re-search-replace pattern replace-with) */
      if (curbp->b_flags & BFVIEW)
      {
        view_mode_violation();
	RV.val.num = FALSE;
      }
      else
      {
	get2args(STRING,STRING,"re-search-replace");
	RV.val.num = re_search_replace(RV.val.str,TV.val.str);
	if (RV.val.num == ABORT) MMabort_pgm(0);
      }
      RV.type = BOOLEAN;
      break;



    default: MMbitch("Invalid Mutt token");
  }
}
