/* * $Id: trn_spec.c,v 1.6 1999-02-08 14:46:56 danw Exp $ * * Copyright (C) 1986 by the Massachusetts Institute of Technology * * Parse a command line containing transaction specifiers and * "standard" message selection control arguments. * * parse_trans_spec "compiles" the command line arguments into * a "generator" data structure. * * The generator strucuture contains things like a trn_info for each * generated transaction. * * It is indended that the application will call "tg_next_trn" repeatedly * until it returns a non-zero error code. "tg_next_trn" returns * DSC_NO_MORE when there are no more transactions in a series, and * silently swallows things like deleted transactions (unless the * -deleted option was given...) * * It is intended that this will be the place to do things like * matching subjects based on a regexp, implementing "-by_chain", etc, * although some significant rearrangement of the FSMs will be needed * to do that. * * If this were LISP, I would wind up turning tg_next_trn into something * which called eval.. but it isn't. Sorry. * */ #ifndef lint static char *rcsid_trn_spec_c = "$Id: trn_spec.c,v 1.6 1999-02-08 14:46:56 danw Exp $"; #endif /* lint */ #include "interface.h" #include "globals.h" #include "trn_spec.h" #include "discuss_err.h" #include "dsc_et.h" #include #include #include #define max(a,b) ((a)<(b)?(b):(a)) /* * Table of legal transaction specs. Entries must be sorted in ASCII * order. */ static struct trn_ent { char *name; int first, second; } trn_table[] = { { ".", TRSPEC_CUR|TRSPEC_REM, 0 }, { "cur", TRSPEC_CUR|TRSPEC_REM, 0 }, { "n", TRSPEC_NEXT|TRSPEC_REM, 0 }, { "next", TRSPEC_NEXT|TRSPEC_REM, 0 }, { "nr", TRSPEC_NREF|TRSPEC_REM, 0 }, { "nref", TRSPEC_NREF|TRSPEC_REM, 0 }, { "p", TRSPEC_PREV|TRSPEC_REM, 0 }, { "pr", TRSPEC_PREF|TRSPEC_REM, 0 }, { "pref", TRSPEC_PREF|TRSPEC_REM, 0 }, { "prev", TRSPEC_PREV|TRSPEC_REM, 0 }, { "all", TRSPEC_FIRST|TRSPEC_REM, TRSPEC_NEXT }, { "aref", TRSPEC_FREF|TRSPEC_REM, TRSPEC_NREF }, { "first", TRSPEC_FIRST|TRSPEC_REM, 0 }, { "last", TRSPEC_LAST|TRSPEC_REM, 0}, { "$", TRSPEC_LAST|TRSPEC_REM, 0}, { "new", TRSPEC_FUNS|TRSPEC_REM, TRSPEC_NEXT }, }; int n_trn_table = (sizeof(trn_table)/sizeof(struct trn_ent)); static struct swtch { char *name; int set; } switches[] = { { "-deleted", TRSPEC_DEL }, }; int n_switches = (sizeof(switches)/sizeof(struct swtch)); /* * Parse a transaction specifier. * Returns a "generator" for the transactions specified. * Modifies (reallocs) argv to contain only the arguments it does * not understand. * * Returns a standard error code. */ int parse_trans_spec(argv_p, argc_p, environ, dfault, gen) char ***argv_p; /* Pointer to array of (char *) */ int *argc_p; /* Arg count (modified) */ struct _dsc_pub *environ; char *dfault; trans_gen **gen; /* Return */ { char **argv = *argv_p; char **nargv; int nargc = 0; int argc = *argc_p; register char **argp = argv; /* induction variable */ int code = 0; /* return error code */ trans_gen *tg; if (!environ->attending) return DISC_NO_MTG; nargv = (char **)malloc(0); tg = (trans_gen *)malloc(sizeof(*tg)); tg->flags = 0; tg->minfo = &environ->m_info; tg->nbp = &environ->nb; tg->te_last = tg->te = (trnspec_entry *)tg; tg->current = environ->current; tg->highest_seen = environ->highest_seen; --argc; ++argp; if (argc == 0) process_an_arg(tg, dfault); for ( ; argc; --argc, ++argp) { if(process_an_arg(tg, *argp)) { nargv = (char **) realloc(nargv, nargc + 1 * (sizeof(char *))); nargv[nargc] = *argp; nargc++; } else free(*argp); *argp = NULL; } free (*argv_p); *argv_p = nargv; *argc_p = nargc; *gen = tg; dsc_get_mtg_info(tg->nbp, tg->minfo, &code); if (code) return code; if (environ->current == 0) return 0; dsc_get_trn_info(tg->nbp, environ->current, &tg->tinfo, &code); return code; } int process_an_arg(tg, arg) trans_gen *tg; char *arg; { int i, val, retval; char *cp; if ((cp = strchr(arg, ',')) || (cp = strchr(arg, ':'))) { *cp = '\0'; retval = process_an_arg(tg, arg); if (!retval) { retval = process_an_arg(tg, cp+1); if (!retval) { tg->te_last->flags &= ~TRSPEC_REM; tg->te_last->flags |= TRSPEC_UNTIL; } } *cp = ','; return 0; } { register char c; int val = 0; trnspec_entry *new_te; for (cp = arg; c = *cp; cp++) { if (!isdigit(c)) goto not_number; val *= 10; val += (c - '0'); } add_new_te(tg, TRSPEC_NUM|TRSPEC_REM, val); return 0; } not_number: for (i = 0; i < n_trn_table; i++) { if(!strcmp(arg, trn_table[i].name)) { add_new_te(tg, trn_table[i].first, 0); if (trn_table[i].second) { add_new_te(tg, trn_table[i].second, 0); } return 0; } } for (i = 0; i < n_switches; i++) { if(!strcmp(arg, switches[i].name)) { tg->flags |= switches[i].set; return 0; } } return 1; } int tg_next_trn(tg) trans_gen *tg; { register trnspec_entry *te; int deleted, remove, until, flags, trn_num, unseen; int code = 0; /* Come up with next xcn number */ if(tg->te == (trnspec_entry *)tg) return DSC_NO_MORE; te=tg->te; trn_num = tg_next_trn_num(tg, te); if (trn_num == 0) return DSC_NO_MORE; if (trn_num != tg->current) dsc_get_trn_info(tg->nbp, trn_num, &tg->tinfo, &code); if (tg->flags&TRSPEC_DEL) { if (code != DELETED_TRN) { /* arg! */ deleted = -1; flags = TRSPEC_NEXT; goto again; /* end arg! */ } else code = 0; } if (code == 0) { tg->current = trn_num; } if(te->flags&TRSPEC_REM) { remque(te); te_free(te); } if (code) tg->current = 0; return code; } add_new_te(tg, flags, num) trans_gen *tg; int flags; int num; { trnspec_entry *new_te = (trnspec_entry *)malloc(sizeof(*new_te)); new_te->flags = flags; new_te->num = num; insque(new_te, tg->te_last); } /* * A Lisp programmer would call this function a special case of eval.. */ int tg_next_trn_num(tg, te) trans_gen *tg; trnspec_entry *te; { int trn_num; switch (te->flags&TRSPEC_KINDS) { case TRSPEC_UNTIL: trn_num = tg_next_trn_num(tg, te->down); if (trn_num >= tg->current) return 0; /* fall through into next... */ case TRSPEC_NEXT: if (tg->flags&TRSPEC_DEL) return tg->current + 1; else return tg->tinfo.next; case TRSPEC_PREV: if (tg->flags&TRSPEC_DEL) return tg->tinfo.current - 1; else return tg->tinfo.prev; case TRSPEC_NREF: return tg->tinfo.nref; case TRSPEC_PREF: return tg->tinfo.pref; case TRSPEC_FREF: return tg->tinfo.fref; case TRSPEC_LREF: return tg->tinfo.lref; case TRSPEC_FIRST: return tg->minfo->first; case TRSPEC_LAST: return tg->minfo->last; case TRSPEC_CUR: return te->tinfo.current; case TRSPEC_NUM: return te->num; case TRSPEC_FUNS: return tg->highest_seen + 1; default: abort(); } }