static char rcsid[] = "$Id: mmaux.c,v 1.2 1992/11/08 23:29:54 mike Exp $"; /* $Log: mmaux.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 * */ #ifdef __hpux #pragma OPT_LEVEL 1 /* !!! Stupid HP-UX s300 8.0 cc doesn't like -O */ #endif /* * mmaux.c : Support 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 #include "me2.h" #include "mm.h" #include "bind.h" extern Binding bindings[]; extern Buffer *id_to_buffer(); extern char result[], /* in mm.c */ *strcpy(), *strcat(), *getenv(), *ext(), *savestr(); extern int MMask_pgm; /* in mm.c */ extern uint8 *MMglobal_vars; /* in mm.c */ extern void *MMglobal_object_table; /* in mm.c Really a *Object[] */ extern MMDatum RV, TV; /* in mm.c */ int arg_flag = FALSE, arg_prefix = 1, /* for ME commands */ pgm_flag = FALSE, pgm_prefix = 1; /* for pgms */ /* ******************************************************************** */ /* **************** tables ******************************************** */ /* ******************************************************************** */ /* holes: */ MuttCmd mutcmds[] = /* last number = 583 */ { "EoB", 505, "OS-filter", 536, "append-to-bag", 569, "arg-flag", 521, "arg-prefix", 516, "argc", 544, "argv", 545, "attached-buffer", 560, "bag-stats", 518, "bag-to-file", 532, "bag-to-string", 582, "bit-and", 527, "bit-or", 528, "bit-xor", 529, "buffer-flags", 566, "buffer-modified", 507, "buffer-name", 508, "buffer-stats", 554, "buffer-var", 578, "buffers", 509, "case-bag", 580, "clear-bag", 570, "clear-buffer", 561, "compare-marks", 540, "complete", 551, "create-bag", 537, "create-buffer", 541, "create-buffer-var", 577, "create-mark", 526, "create-process", 581, "current-buffer", 513, "current-column", 511, "current-directory", 565, "current-window", 558, "do-undo", 579, "erase-rectangle", 523, "exe-key", 503, "file-exists", 530, "file-name", 510, "file-to-bag", 535, "forward-line", 519, "free-bag", 538, "free-buffer", 543, "free-mark", 553, "free-window", 556, "get-key", 502, "get-matched", 549, "getchar", 522, "getenv", 568, "goto-line", 576, "goto-mark", 547, "insert-bag", 571, "insert-text", 524, "is-space", 525, "key-bound-to", 562, "key-pressed", 514, "key-waiting", 504, "looking-at", 548, "modify-syntax-entry", 546, "move-cursor", 557, "nth-buffer", 542, "pgm-exists", 515, "prefix-key", 567, "puts", 531, "re-search-forward", 572, "re-search-replace", 575, "re-search-reverse", 573, "re-string", 506, "region-stats", 517, "search-forward", 552, "search-replace", 574, "search-reverse", 563, "set-mark", 550, "swap-marks", 583, "sysvar", 501, "to-col", 533, "update", 534, "window-height", 520, "window-ledge", 564, "window-row", 555, "windows", 559, "yesno", 512, }; int msize = NITEMS(mutcmds); MuttCmd sysvars[] = /* last number = 13, holes: 10 */ { "HELP", 1, "beeper", 11, "case-fold-search", 5, "complete-key", 3, "horizontal-scroll", 13, "modeline-color", 9, "overstrike", 2, "screen-length", 4, "screen-width", 12, "tab-stops", 6, "text-color", 8, "word-wrap", 7, }; int svsize = NITEMS(sysvars); /* ******************************************************************** */ /* ********** Stuff called from mm.c ********************************** */ /* ******************************************************************** */ /* Run a ME2 built in command */ static void sysfcn(n) { if ((curbp->b_flags & BFVIEW) && bindings[n].prohibited) { view_mode_violation(); return; } #if 0 { mlwrite("[Program used command illegal in view mode -- aborted]"); t_beep(); MMabort_pgm(1); } #endif if ((RV.val.num = (*bindings[n].fcn)(arg_flag,arg_prefix)) == ABORT) MMabort_pgm(1); RV.type = BOOLEAN; arg_flag = FALSE; arg_prefix = 1; MMask_pgm = TRUE; } /* Run a aux function by name. * Could be a function, command or sys var. */ MMaux_fcn(name) char *name; { int i; if ((i = lookupmut(name,mutcmds,msize)) != -1) { Mdomutt(i); return TRUE; } if (lookupfcn(name,&i)) { sysfcn(i); return TRUE; } /* system fcn */ if ((i = lookupmut(name,sysvars,svsize)) != -1) { Msys_var(i,0); return TRUE; } return FALSE; /* ain't nothing I know about */ } void MMxtoken(token) { if (token < 500) sysfcn(token); else Mdomutt(token); } void MMask(prompt,buf) char *prompt, *buf; { mlreply(prompt,buf,RSIZ,0); } void MMmsg(str) char *str; { mlputs(str); } void MMbitch(msg) char *msg; { mlwrite("PGM ABORT: %s",msg); MMabort_pgm(2); } void MMmoan(msg) char *msg; { mlputs(msg); } /* ******************************************************************** */ /* **************** Helper routines *********************************** */ /* ******************************************************************** */ /* Use binary search to find system function name * returns index of token if found * -1 if not found */ int lookupmut(name,table,size) /* MUTT functions */ char *name; MuttCmd *table; int size; { register int j, lower = 0, upper = size-1, x; while (lower<=upper) { j = (lower+upper)/2; if ((x = strcmp(name,table[j].name))>0) lower = j +1; else if (x<0) upper = j -1; else return table[j].token; } return -1; } /* complain about name getting a bad arg and die */ void arg_bitch(name) char *name; { MMbitch(strcat(strcpy(result,name),": Bad arg")); } /* Get the 0th arg and put it into RV */ void get1arg(type,name) char *name; { if (!MMpull_nth_arg(&RV,0) || RV.type != type) arg_bitch(name); } /* Get the nth arg and put it into RV */ void get_nth_arg(n,type,name) char *name; { if (!MMpull_nth_arg(&RV,n) || RV.type != type) arg_bitch(name); } /* RV <= arg 0, TV <= arg 1 */ void get2args(t1,t2,name) char *name; { if (!MMpull_nth_arg(&RV,0) || RV.type != t1 || !MMpull_nth_arg(&TV,1) || TV.type != t2) arg_bitch(name); } /* Get the nth arg (if there is one) and put it into TV */ maybearg(n,type,name) char *name; { register int s; if ((s = MMpull_nth_arg(&TV,n)) && TV.type != type) arg_bitch(name); return s; } /* Get the 0th arg, make sure its a number in [a,b) * and put it into RV */ get1num(a,b,name) char *name; { get1arg(NUMBER,name); if (RV.val.num < a || b <= RV.val.num) arg_bitch(name); return (int)RV.val.num; } /* save bunches of code over macros */ void put_int32(blob,x) register unsigned char *blob; register int32 x; { PUT_INT32(blob,x); } void put_int16(blob,x) register unsigned char *blob; register int x; { PUT_INT16(blob,x); } extern Window *nthwindow(); Buffer *Mid_to_buffer(buf_id) register int buf_id; { Buffer *bp; if (buf_id == -1) return curbp; if (!(bp = id_to_buffer(buf_id))) MMbitch("Bad buffer id"); return bp; } Window *Mnth_window(n) register int n; { return (n == -1) ? curwp : nthwindow(first_window,n); } /************************************************************************* ****************** pgm management **************************************** **************************************************************************/ #define PGMMAX 300 /* Max number of loaded programs */ extern void MMblock_name(); static int lookup_block(), lookuppgm(); static void gcpgms(); /* Load Mutt code from a file. * Notes: * I really don't like this being a ME command, but if it was just * callable from Mutt, it would be a real pain in the butt to if ME * started up not being able to find code - catch 22. * There is a bit of a sleeze here. I want to (sometimes) load a file * only if it hasn't already been loaded so I (way) overload the * flags. */ load_Mutt_file(f,n) int f,n; { char fname[NFILEN]; int s; if ((s = mlreply("Load file: ", fname, NFILEN, CC_FNAME)) != TRUE) return s; if (n == 42) { char buf[NFILEN]; MMblock_name(buf,fname); if (-1 != lookup_block(buf)) return TRUE; } return MMload(fname,!f); } #if 0 ME_load(f,n) int f,n; { char fname[NFILEN]; int s; if ((s = mlreply("Load file: ", fname, NFILEN, CC_FNAME)) != TRUE) return s; return MMload(fname,TRUE); } this to mmfcn2.c Mload_Mutt_file(fname, complain, check_first) char *fname; { int s; if (check_first) { char buf[NFILEN]; MMblock_name(buf,fname); if (-1 != lookup_block(buf)) return TRUE; } return MMload(fname, complain); } #endif /* *********** Code Blocks **************************************** */ void MMblock_name(buf,fname) char *buf,*fname; /* create the block name */ { makename(buf,fname); *ext(buf) = '\0'; } /* A block is most of the contents of a .mco file. It contains Mutt * code, the global variable area used by the code, string table and * the names of the programs in the block. Blocks are created and read * in mm.c. */ #define FIRSTBLOCK 1 /* block 0 is a dummy block (the dead block). */ typedef struct { char *name; /* Name of the block (munged file name) */ uint8 *global_vars; /* Where the global vars are in this block */ maddr code; /* The start of the code in this block */ void *global_object_table; int num_global_objects; } CodeBlock; static declare_and_init_dTable(blktable,CodeBlock); static int lookup_block(block_name) char *block_name; { int j; for (j = FIRSTBLOCK; j < sizeof_dTable(&blktable); j++) if (0 == strcmp(blktable.table[j].name, block_name)) return j; return -1; } static void free_block(n) { CodeBlock *ptr; ptr = &blktable.table[n]; MMfree_block(ptr->code, ptr->global_object_table, ptr->num_global_objects); gcpgms(n); /* remove pgms attached to this block */ packkeys(); /* remove keys attached to pgms in this block */ } /* Note: a block contains both the code and the pgm names */ MMadd_block(name,code,global_vars, global_object_table,num_global_objects) char *name; maddr code; uint8 *global_vars; void *global_object_table; int num_global_objects; { CodeBlock *ptr; int j; for (j = FIRSTBLOCK; j < sizeof_dTable(&blktable); j++) { ptr = &blktable.table[j]; if (0 == strcmp(ptr->name, name)) /* block exists */ { free_block(j); goto xxx; } } /* j is max(1, 1+ the last block) */ if (!xpand_dTable(&blktable,1,10,10) || (blktable.table[j].name = savestr(name)) == NULL) { MMmoan("Can't add code block"); return -1; } sizeof_dTable(&blktable) = j+1; /* in case skipping first block */ ptr = &blktable.table[j]; xxx: ptr->code = code; ptr->global_vars = global_vars; ptr->global_object_table = global_object_table; ptr->num_global_objects = num_global_objects; return j; } /* ************** Programs ******************************************* */ /* slime note: I don't use blktable[0] so that the pgms array * is inited to all dead */ typedef struct /* program address table */ { maddr addr; /* address of routine */ short int block; /* link to programs code block. 0 => dead */ } Pgm; static Pgm pgms[PGMMAX]; PgmName pnames[PGMMAX]; int pbsize = 0; /* number of loaded programs */ maddr MMpgm_addr(n) { CodeBlock *ptr = &blktable.table[pgms[n].block]; MMglobal_vars = ptr->global_vars; MMglobal_object_table = ptr->global_object_table; return pgms[n].addr; } #define PGMDEAD(n) (pgms[n].block == 0) pgmdead(n) { return PGMDEAD(n); } #define DEL_PGM(n) (pgms[n].block = 0) static void gcpgms(block) /* garbage collect programs */ { int j,k; for (j = 0; j < PGMMAX; j++) if (pgms[j].block == block) DEL_PGM(j); /* pack the name table */ for (j = 0; j < pbsize && !PGMDEAD(pnames[j].index); j++) ; for (k = j; j < pbsize; j++) if (!PGMDEAD(pnames[j].index)) pnames[k++] = pnames[j]; pbsize = k; } MMadd_pgm(name,block,addr) char *name; int block; maddr addr; { int j,n; static int k = 0; if (lookuppgm(name,&n)) k = pnames[n].index; /* pgm exists: overwrite */ else /* new name */ { if (pbsize == PGMMAX) /* Opps - all full up */ { MMmoan("Program table full - load aborted"); free_block(block); return FALSE; } for (j = pbsize; n < j; j--) pnames[j] = pnames[j-1]; /* open hole */ pbsize++; /* find next available slot */ while (!PGMDEAD(k)) if (++k == PGMMAX) k = 0; pnames[n].index = k; } pnames[n].name = name; pgms[k].block = block; pgms[k].addr = addr; return TRUE; } /* Use binary search to find pgm. * Returns: index into pgm table, -1 if not found. */ MMpgm_lookup(name) char *name; { register int j, lower = 0, upper = pbsize-1, x; while (lower <= upper) { j = (lower+upper)/2; if ((x = strcmp(name,pnames[j].name)) > 0) lower = j +1; else if (x < 0) upper = j -1; else return pnames[j].index; } return -1; } static int lookuppgm(name,n) char *name; int *n; { register int j, x; for (j = pbsize -1; 0 <= j; j--) { if ((x = strcmp(pnames[j].name,name)) == 0) { *n = j; return TRUE; } if (x < 0) break; } *n = j +1; return FALSE; } /* ******************* External Objects ********************************** */ /* Garbage collect all mortal (temporary) objects that may have been * created by Mutt programs and not freed by them. * This is typically called (by the Mutt Machine) after a pgm has been * run or aborted to clean up stuff the programmer forgot to or was * unable to (as in the case where the pgm was aborted). * Problems with this method of GC: * I won't notice some dependences between Mutt objects and external * objects. For example, if a global Mutt object creates external * objects, the block GC code will have to be smart enough to free * the external objects. Which ain't goona happen until I add OOP * support and pass the burden back to the programmer (by having * destructers that can be called from the block GC code). * MMgc_external_objects() can only be called when the root pgm has * been terminated (and control returns from MM to the application) * to ensure objects aren't freed while they are still in use. This * means I can't GC when low on memory in hopes of getting some * memory back. This might be a real problem for an application * that just fires off MM and doesn't expect it to return. * Since this might be called quite a lot, it might be expensive to * look though all objects to try and find dead ones (especially if * the Mutt programmer was good and cleaned things up). On the * other hand, it is probably called from a wait-for-keyboard loop * and thats when we have time to burn. * Pros: * This style of GC works well with applications like ME2 - the user * won't see dead buffers and ME2 won't waste time updating dead * marks. * If you have a real GC, you can ignore this call. */ static void ME2_gc() { gc_bags(); gc_buffers(); gc_marks(); } void MMgc_external_objects() { call_me_sometime(GC_HOOK, ME2_gc); }