예제 #1
0
파일: filter.c 프로젝트: rubenerd/sc-im
// Add a filter to filters structure
void add_filter(char * criteria) {
    int cp = 0;
    char c;

    while (criteria[cp]) {
        int pos = exists_freed_filter(); // we check if there exists a freed filter
        if (pos == -1) {            // if not we alloc a new one
            filters = (struct filter_item *) scxrealloc((char *) filters, (howmany++ + 1) * (sizeof(struct filter_item)));
            pos = howmany-1;
        }

        filters[pos].eval = (char *) scxmalloc(sizeof(char) * strlen(criteria) + 1);
        filters[pos].eval[0] = '\0';

        while (criteria[cp] && criteria[cp] != ';' && criteria[cp] != '\n') {
            c = criteria[cp];
            if (c == '"') { cp++; continue; }
            if (criteria[cp++] == '\'') c ='"';
            sprintf(filters[pos].eval + strlen(filters[pos].eval), "%c", c);
        }

        if (criteria[cp] == ';') cp++;
    }
    return;
}
예제 #2
0
파일: xmalloc.c 프로젝트: n-t-roff/sc
/* we make sure realloc will do a malloc if needed */
void *
scxrealloc(void *ptr, size_t n)
{
	if (ptr == NULL)
		return(scxmalloc(n));

	ptr -= sizeof(double);
	if (*((double *) ptr) != MAGIC)
		fatal("scxrealloc: storage not scxmalloc'ed");

	if ((ptr = realloc(ptr, n + sizeof(double))) == NULL)
		fatal("scxmalloc: no memory");
	*((double *) ptr) = MAGIC;		/* magic number */
	return(ptr + sizeof(double));
}
예제 #3
0
파일: color.c 프로젝트: dkastner/sc
void
add_crange(struct ent *r_left, struct ent *r_right, int pair)
{
    struct crange *r;
    int minr, minc, maxr, maxc;

    minr = r_left->row < r_right->row ? r_left->row : r_right->row;
    minc = r_left->col < r_right->col ? r_left->col : r_right->col;
    maxr = r_left->row > r_right->row ? r_left->row : r_right->row;
    maxc = r_left->col > r_right->col ? r_left->col : r_right->col;

    if (!pair) {
	if (color_base)
	    for (r = color_base; r; r = r->r_next)
		if (    (r->r_left->row == r_left->row) &&
			(r->r_left->col == r_left->col) &&
			(r->r_right->row == r_right->row) &&
			(r->r_right->col == r_right->col)) {
		    if (r->r_next)
			r->r_next->r_prev = r->r_prev;
		    if (r->r_prev)
			r->r_prev->r_next = r->r_next;
		    else
			color_base = r->r_next;
		    scxfree((char *)r);
		    modflg++;
		    FullUpdate++;
		    return;
		}
	error("Color range not defined");
	return;
    }

    r = (struct crange *)scxmalloc((unsigned)sizeof(struct crange));
    r->r_left = lookat(minr, minc);
    r->r_right = lookat(maxr, maxc);
    r->r_color = pair;

    r->r_next = color_base;
    r->r_prev = (struct crange *)0;
    if (color_base)
	color_base->r_prev = r;
    color_base = r;

    modflg++;
    FullUpdate++;
}
예제 #4
0
파일: color.c 프로젝트: dkastner/sc
void
change_color(int pair, struct enode *e)
{
    int v;

    if ((--pair) < 0 || pair > 7) {
	error("Invalid color number");
	return;
    }

    v = (int) eval(e);

    if (!cpairs[pair])
	cpairs[pair] =
	    (struct colorpair *)scxmalloc((unsigned)sizeof(struct colorpair));
    cpairs[pair]->fg = v & 7;
    cpairs[pair]->bg = (v >> 3) & 7;
    cpairs[pair]->expr = e;
    if (color && has_colors())
	init_pair(pair + 1, cpairs[pair]->fg, cpairs[pair]->bg);

    modflg++;
    FullUpdate++;
}
예제 #5
0
파일: frame.c 프로젝트: Distrotech/sc
void
add_frange(struct ent *or_left, struct ent *or_right, struct ent *ir_left,
	struct ent *ir_right, int toprows, int bottomrows, int leftcols,
	int rightcols)
{
    struct frange *r;
    int minr, minc, maxr, maxc;

    minr = or_left->row < or_right->row ? or_left->row : or_right->row;
    minc = or_left->col < or_right->col ? or_left->col : or_right->col;
    maxr = or_left->row > or_right->row ? or_left->row : or_right->row;
    maxc = or_left->col > or_right->col ? or_left->col : or_right->col;

    or_left = lookat(minr, minc);
    or_right = lookat(maxr, maxc);

    if (ir_left) {
	minr = ir_left->row < ir_right->row ? ir_left->row : ir_right->row;
	minc = ir_left->col < ir_right->col ? ir_left->col : ir_right->col;
	maxr = ir_left->row > ir_right->row ? ir_left->row : ir_right->row;
	maxc = ir_left->col > ir_right->col ? ir_left->col : ir_right->col;

	ir_left = lookat(minr, minc);
	ir_right = lookat(maxr, maxc);

	if (ir_left->row < or_left->row ||
		ir_left->col < or_left->col ||
		ir_right->row > or_right->row ||
		ir_right->col > or_right->col) {
	    error("Invalid parameters");
	    return;
	}
    }

    if (frame_base) {
	/*
	 * Has this frange already been created?  If so, any negative
	 * parameters mean "don't change this value."
	 */
	for (r = frame_base; r; r = r->r_next) {
	    if ((r->or_left == or_left) && (r->or_right == or_right)) {
		if (ir_left) {
		    r->ir_left = ir_left;
		    r->ir_right = ir_right;
		} else {
		    if (toprows < 0)
			toprows = r->ir_left->row - r->or_left->row;
		    if (bottomrows < 0)
			bottomrows = r->or_right->row - r->ir_right->row;
		    if (leftcols < 0)
			leftcols = r->ir_left->col - r->or_left->col;
		    if (rightcols < 0)
			rightcols = r->or_right->col - r->ir_right->col;
		    r->ir_left = lookat(r->or_left->row + toprows,
			    r->or_left->col + leftcols);
		    r->ir_right = lookat(r->or_right->row - bottomrows,
			    r->or_right->col - rightcols);
		}

		/* If all frame sides are 0, delete the frange */
		if (r->ir_left == r->or_left && r->ir_right == r->or_right) {
		    if (r->r_next)
			r->r_next->r_prev = r->r_prev;
		    if (r->r_prev)
			r->r_prev->r_next = r->r_next;
		    else
			frame_base = r->r_next;
		    scxfree((char *)r);
		}
		modflg++;
		FullUpdate++;
		return;
	    }
	}
	/*
	 * See if the specified range overlaps any previously created frange.
	 */
	for (r = frame_base; r; r = r->r_next) {
	    if (  !(r->or_left->row  > or_right->row ||
		    r->or_right->row < or_left->row  ||
		    r->or_left->col  > or_right->col ||
		    r->or_right->col < or_left->col)) {
		error("Framed ranges may not be nested or overlapping");
		return;
	    }
	}
    }

    if (ir_left != or_left || ir_right != or_right) {
	r = (struct frange *)scxmalloc((unsigned)sizeof(struct frange));
	r->or_left = or_left;
	r->or_right = or_right;

	if (ir_left) {
	    r->ir_left  = ir_left;
	    r->ir_right = ir_right;
	} else {
	    if (toprows    < 0) toprows    = 0;
	    if (bottomrows < 0) bottomrows = 0;
	    if (leftcols   < 0) leftcols   = 0;
	    if (rightcols  < 0) rightcols  = 0;
	    r->ir_left = lookat(r->or_left->row + toprows,
		    r->or_left->col + leftcols);
	    r->ir_right = lookat(r->or_right->row - bottomrows,
		    r->or_right->col - rightcols);
	}

	r->r_next = frame_base;
	r->r_prev = (struct frange *)0;
	if (frame_base)
	    frame_base->r_prev = r;
	frame_base = r;
	modflg++;
	FullUpdate++;
    }
}
예제 #6
0
파일: color.c 프로젝트: dkastner/sc
void
initcolor(int colornum)
{
    if (!colornum) {
	int i;

	for (i = 0; i < 8; i++)	cpairs[i] =
	    (struct colorpair *)scxmalloc((unsigned)sizeof(struct colorpair));
    }

/* default colors */
    if (!colornum || colornum == 1) {
	cpairs[0]->fg = COLOR_WHITE;
	cpairs[0]->bg = COLOR_BLUE;
	cpairs[0]->expr = NULL;
	init_pair(1, cpairs[0]->fg, cpairs[0]->bg);
    }

/* default for negative numbers */
    if (!colornum || colornum == 2) {
	cpairs[1]->fg = COLOR_RED;
	cpairs[1]->bg = COLOR_WHITE;
	cpairs[1]->expr = NULL;
	init_pair(2, cpairs[1]->fg, cpairs[1]->bg);
    }

/* default for cells with errors */
    if (!colornum || colornum == 3) {
	cpairs[2]->fg = COLOR_WHITE;
	cpairs[2]->bg = COLOR_RED;
	cpairs[2]->expr = NULL;
	init_pair(3, cpairs[2]->fg, cpairs[2]->bg);
    }

/* default for '*' marking cells with attached notes */
    if (!colornum || colornum == 4) {
	cpairs[3]->fg = COLOR_BLACK;
	cpairs[3]->bg = COLOR_YELLOW;
	cpairs[3]->expr = NULL;
	init_pair(4, cpairs[3]->fg, cpairs[3]->bg);
    }

    if (!colornum || colornum == 5) {
	cpairs[4]->fg = COLOR_BLACK;
	cpairs[4]->bg = COLOR_CYAN;
	cpairs[4]->expr = NULL;
	init_pair(5, cpairs[4]->fg, cpairs[4]->bg);
    }

    if (!colornum || colornum == 6) {
	cpairs[5]->fg = COLOR_RED;
	cpairs[5]->bg = COLOR_CYAN;
	cpairs[5]->expr = NULL;
	init_pair(6, cpairs[5]->fg, cpairs[5]->bg);
    }

    if (!colornum || colornum == 7) {
	cpairs[6]->fg = COLOR_WHITE;
	cpairs[6]->bg = COLOR_BLACK;
	cpairs[6]->expr = NULL;
	init_pair(7, cpairs[6]->fg, cpairs[6]->bg);
    }

    if (!colornum || colornum == 8) {
	cpairs[7]->fg = COLOR_RED;
	cpairs[7]->bg = COLOR_BLACK;
	cpairs[7]->expr = NULL;
	init_pair(8, cpairs[7]->fg, cpairs[7]->bg);
    }

    if (color && has_colors())
	color_set(1, NULL);
}
예제 #7
0
파일: lex.c 프로젝트: recalcc/sc
int
yylex()
{
    char *p = line + linelim;
    int ret=0;
    static int isfunc = 0;
    static bool isgoto = 0;
    static bool colstate = 0;
    static int dateflag;
    static char *tokenst = NULL;
    static int tokenl;

    while (isspace(*p)) p++;
    if (*p == '\0') {
	isfunc = isgoto = 0;
	ret = -1;
    } else if (isalpha(*p) || (*p == '_')) {
	register char *la;	/* lookahead pointer */
	register struct key *tblp;

	if (!tokenst) {
	    tokenst = p;
	    tokenl = 0;
	}
	/*
	 *  This picks up either 1 or 2 alpha characters (a column) or
	 *  tokens made up of alphanumeric chars and '_' (a function or
	 *  token or command or a range name)
	 */
	while (isalpha(*p) && isascii(*p)) {
	    p++;
	    tokenl++;
	}
	la = p;
	while (isdigit(*la) || (*la == '$'))
	    la++;
	/*
	 * A COL is 1 or 2 char alpha with nothing but digits following
	 * (no alpha or '_')
	 */
	if (!isdigit(*tokenst) && tokenl && tokenl <= 2 && (colstate ||
		(isdigit(*(la-1)) && !(isalpha(*la) || (*la == '_'))))) {
	    ret = COL;
	    yylval.ival = atocol(tokenst, tokenl);
	} else {
	    while (isalpha(*p) || (*p == '_') || isdigit(*p)) {
		p++;
		tokenl++;
	    }
	    ret = WORD;
	    if (!linelim || isfunc) {
		if (isfunc) isfunc--;
		for (tblp = linelim ? experres : statres; tblp->key; tblp++)
		    if (((tblp->key[0]^tokenst[0])&0137)==0
			    && tblp->key[tokenl]==0) {
			int i = 1;
			while (i<tokenl && ((tokenst[i]^tblp->key[i])&0137)==0)
			    i++;
			if (i >= tokenl) {
			    ret = tblp->val;
			    colstate = (ret <= S_FORMAT);
			    if (isgoto) {
				isfunc = isgoto = 0;
				if (ret != K_ERROR && ret != K_INVALID)
				    ret = WORD;
			    }
			    break;
			}
		    }
	    }
	    if (ret == WORD) {
		struct range *r;
		char *path;
		if (!find_range(tokenst, tokenl,
			(struct ent *)0, (struct ent *)0, &r)) {
		    yylval.rval.left = r->r_left;
		    yylval.rval.right = r->r_right;
		    if (r->r_is_range)
		        ret = RANGE;
		    else
			ret = VAR;
		} else if ((path = scxmalloc((unsigned)PATHLEN)) &&
			plugin_exists(tokenst, tokenl, path)) {
		    strcat(path, p);
		    yylval.sval = path;
		    ret = PLUGIN;
		} else {
		    scxfree(path);
		    linelim = p-line;
		    yyerror("Unintelligible word");
		}
	    }
	}
    } else if ((*p == '.') || isdigit(*p)) {
#ifdef SIGVOID
	void (*sig_save)();
#else
	int (*sig_save)();
#endif
	double v = 0.0;
	int temp;
	char *nstart = p;

	sig_save = signal(SIGFPE, fpe_trap);
	if (setjmp(fpe_buf)) {
	    (void) signal(SIGFPE, sig_save);
	    yylval.fval = v;
	    error("Floating point exception\n");
	    isfunc = isgoto = 0;
	    tokenst = NULL;
	    return FNUMBER;
	}

	if (*p=='.' && dateflag) {  /* .'s in dates are returned as tokens. */
	    ret = *p++;
	    dateflag--;
	} else {
	    if (*p != '.') {
		tokenst = p;
		tokenl = 0;
		do {
		    v = v*10.0 + (double) ((unsigned) *p - '0');
		    tokenl++;
		} while (isdigit(*++p));
		if (dateflag) {
		    ret = NUMBER;
		    yylval.ival = (int)v;
		/*
		 *  If a string of digits is followed by two .'s separated by
		 *  one or two digits, assume this is a date and return the
		 *  .'s as tokens instead of interpreting them as decimal
		 *  points.  dateflag counts the .'s as they're returned.
		 */
		} else if (*p=='.' && isdigit(*(p+1)) && (*(p+2)=='.' ||
			(isdigit(*(p+2)) && *(p+3)=='.'))) {
		    ret = NUMBER;
		    yylval.ival = (int)v;
		    dateflag = 2;
		} else if (*p == 'e' || *p == 'E') {
		    while (isdigit(*++p)) /* */;
		    if (isalpha(*p) || *p == '_') {
			linelim = p - line;
			return (yylex());
		    } else
			ret = FNUMBER;
		} else if (isalpha(*p) || *p == '_') {
		    linelim = p - line;
		    return (yylex());
		}
	    }
	    if ((!dateflag && *p=='.') || ret == FNUMBER) {
		ret = FNUMBER;
		yylval.fval = strtod(nstart, &p);
		if (!finite(yylval.fval))
		    ret = K_ERR;
		else
		    decimal = TRUE;
	    } else {
		/* A NUMBER must hold at least MAXROW and MAXCOL */
		/* This is consistent with a short row and col in struct ent */
		if (v > (double)32767 || v < (double)-32768) {
		    ret = FNUMBER;
		    yylval.fval = v;
		} else {
		    temp = (int)v;
		    if((double)temp != v) {
			ret = FNUMBER;
			yylval.fval = v;
		    } else {
			ret = NUMBER;
			yylval.ival = temp;
		    }
		}
	    }
	}
	(void) signal(SIGFPE, sig_save);
    } else if (*p=='"') {
	char *ptr;
        ptr = p+1;	/* "string" or "string\"quoted\"" */
        while (*ptr && ((*ptr != '"') || (*(ptr-1) == '\\')))
	    ptr++;
        ptr = scxmalloc((unsigned)(ptr-p));
	yylval.sval = ptr;
	p++;
	while (*p && ((*p != '"') ||
		(*(p-1) == '\\' && *(p+1) != '\0' && *(p+1) != '\n')))
	    *ptr++ = *p++;
	*ptr = '\0';
	if (*p)
	    p++;
	ret = STRING;
    } else if (*p=='[') {
	while (*p && *p!=']')
	    p++;
	if (*p)
	    p++;
	linelim = p-line;
	tokenst = NULL;
	return yylex();
    } else ret = *p++;
    linelim = p-line;
    if (!isfunc) isfunc = ((ret == '@') + (ret == S_GOTO) - (ret == S_SET));
    if (ret == S_GOTO) isgoto = TRUE;
    tokenst = NULL;
    return ret;
}
예제 #8
0
파일: range.c 프로젝트: recalcc/sc
void
add_range(char *name, struct ent_ptr left, struct ent_ptr right, int is_range)
{
    struct range *r;
    register char *p;
    int minr, minc, maxr, maxc;
    int minrf, mincf, maxrf, maxcf;
    register struct ent *rcp;
    struct range *prev = 0;

    if (left.vp->row < right.vp->row) {
	minr = left.vp->row; minrf = left.vf & FIX_ROW;
	maxr = right.vp->row; maxrf = right.vf & FIX_ROW;
    } else {
	minr = right.vp->row; minrf = right.vf & FIX_ROW;
	maxr = left.vp->row; maxrf = right.vf & FIX_ROW;
    } 

    if (left.vp->col < right.vp->col) {
	minc = left.vp->col; mincf = left.vf & FIX_COL;
	maxc = right.vp->col; maxcf = right.vf & FIX_COL;
    } else {
	minc = right.vp->col; mincf = right.vf & FIX_COL;
	maxc = left.vp->col; maxcf = left.vf & FIX_COL;
    } 

    left.vp = lookat(minr, minc);
    left.vf = minrf | mincf;
    right.vp = lookat(maxr, maxc);
    right.vf = maxrf | maxcf;

    if (!find_range(name, strlen(name), (struct ent *)0, (struct ent *)0,
	    &prev)) {
	error("Error: range name \"%s\" already defined", name);
	scxfree(name);
	return;
    }

    for (p = name; *p; p++)
	if (!(isalpha(*p) || isdigit(*p) || *p == '_')) {
	    error("Invalid range name \"%s\" - illegal combination", name);
	    scxfree(name);
	    return;
	}
 
    p = name;
    if (isdigit(*p) || (isalpha(*p++) && (isdigit(*p) ||
		(isalpha(*p++) && isdigit(*p))))) {
	if (*name == '0' && (name[1] == 'x' || name[1] == 'X')) {
	    ++p;
	    while (isxdigit(*++p)) /* */;
	    if (*p == 'p' || *p == 'P')
		while (isxdigit(*++p)) /* */;
	} else {
	    while (isdigit(*++p)) /* */;
	    if (isdigit(*name) && (*p == 'e' || *p == 'E'))
		while (isdigit(*++p)) /* */;
	}
	if (!(*p)) {
	    error("Invalid range name \"%s\" - ambiguous", name);
	    scxfree(name);
	    return;
	}
    }
 
    if (autolabel && minc>0 && !is_range) {
	rcp = lookat(minr, minc-1);
	if (rcp->label==0 && rcp->expr==0 && rcp->v==0)
		label(rcp, name, 0);
    }

    r = (struct range *)scxmalloc((unsigned)sizeof(struct range));
    r->r_name = name;
    r->r_left = left;
    r->r_right = right;
    r->r_is_range = is_range;
    if (prev) {
	r->r_next = prev->r_next;
	r->r_prev = prev;
	prev->r_next = r;
	if (r->r_next)
	    r->r_next->r_prev = r;
    } else {
	r->r_next = rng_base;
	r->r_prev = (struct range *)0;
	if (rng_base)
	    rng_base->r_prev = r;
	rng_base = r;
    }
    modflg++;
}
예제 #9
0
void sortrange (struct ent *left, struct ent *right, char *criteria)
{
    int minr, minc, maxr, maxc, r, c;
    int *rows, col = 0;
    int cp = 0;
    struct ent *p;

    minr = left->row < right->row ? left->row : right->row;
    minc = left->col < right->col ? left->col : right->col;
    maxr = left->row > right->row ? left->row : right->row;
    maxc = left->col > right->col ? left->col : right->col;

    sort = (struct sortcrit *)scxmalloc((2 * sizeof(struct sortcrit)));
    rows = (int *)scxmalloc((maxr - minr + 1) * sizeof(int));
    for (r = minr, c = 0; r <= maxr; r++, c++)
	rows[c] = r;

    if (!criteria) {
	sort[0].direction = 1;
	sort[0].type = 1;
	sort[0].column = minc;
	sort[1].direction = 1;
	sort[1].type = 0;
	sort[1].column = minc;
	howmany = 2;
    } else {
	for (howmany = 0; criteria[cp]; howmany++) {
	    if (howmany > 1)
		sort = (struct sortcrit *)scxrealloc((char *)sort,
			(howmany + 1) * (sizeof(struct sortcrit)));
	    switch (criteria[cp++]) {
		case '+':
		    sort[howmany].direction = 1;
		    break;
		case '-':
		    sort[howmany].direction = -1;
		    break;
		default:
		    error("Invalid sort criteria");
		    return;
	    }
	    switch (criteria[cp++]) {
		case '#':
		    sort[howmany].type = 0;
		    break;
		case '$':
		    sort[howmany].type = 1;
		    break;
		default:
		    error("Invalid sort criteria");
		    return;
	    }
	    if (criteria[cp])
		col = toupper(criteria[cp++]) - 'A';
	    else {
		error("Invalid sort criteria");
		return;
	    }
	    if (criteria[cp] && criteria[cp] != '+' && criteria[cp] != '-')
		col = (col + 1) * 26 + toupper(criteria[cp++]) - 'A';
	    sort[howmany].column = col;
	    if (col < minc || col > maxc) {
		error("Invalid sort criteria");
		return;
	    }
	}
    }

    qsort(rows, maxr - minr + 1, sizeof(int), compare);
    erase_area(minr, minc, maxr, maxc, 1);
    sync_ranges();
    for (c = 0, p = delbuf[dbidx]; p; p = p->next) {
	if (rows[c] != p->row) {
	    for (c = 0; c <= maxr - minr && rows[c] != p->row; c++) ;
	    if (c > maxr - minr) {
		error("sort error");
		return;
	    }
	}
	p->row = minr + c;
    }
    scxfree((char *)sort);
    scxfree((char *)rows);
    if (criteria) scxfree(criteria);

    r = currow;
    c = curcol;
    currow = minr;
    curcol = minc;

    pullcells('m');
    flush_saved();

    currow = r;
    curcol = c;
}
예제 #10
0
파일: abbrev.c 프로젝트: n-t-roff/sc
void
add_abbr(char *string)
{
    struct abbrev *a;
    register char *p;
    struct abbrev *prev = NULL;
    char *expansion;
    
    if (!string || *string == '\0') {
	if (!are_abbrevs()) {
	    error("No abbreviations defined");
	    return;
	} else {
	    FILE *f;
	    int pid;
	    char px[MAXCMD];
	    char *pager;
	    struct abbrev *a;
	    struct abbrev *nexta;

	    strlcpy(px, "| ", sizeof px);
	    if (!(pager = getenv("PAGER")))
		pager = DFLT_PAGER;
	    strlcat(px, pager, sizeof px);
	    f = openfile(px, sizeof px, &pid, NULL);
	    if (!f) {
		error("Can't open pipe to %s", pager);
		return;
	    }
	    (void) fprintf(f, "\n%-15s %s\n","Abbreviation","Expanded");
	    if (!brokenpipe) (void) fprintf(f, "%-15s %s\n", "------------",
		    "--------");

	    for (a = nexta = abbr_base; nexta; a = nexta, nexta = a->a_next)
		;
	    while (a) {
		(void) fprintf(f, "%-15s %s\n", a->abbr, a->exp);
		if (brokenpipe) return;
		a = a->a_prev;
	    }
	    closefile(f, pid, 0);
	    return;
	}
    }

    if ((expansion = strchr(string, ' ')))
	*expansion++ = '\0';

    if (isalpha((int)*string) || isdigit((int)*string) || *string == '_') {
	for (p = string; *p; p++)
	    if (!(isalpha((int)*p) || isdigit((int)*p) || *p == '_')) {
		error("Invalid abbreviation: %s", string);
		scxfree(string);
		return;
	    }
    } else {
	for (p = string; *p; p++)
	    if ((isalpha((int)*p) || isdigit((int)*p) || *p == '_') && *(p+1)) {
		error("Invalid abbreviation: %s", string);
		scxfree(string);
		return;
	    }
    }
    
    if (expansion == NULL) {
	if ((a = find_abbr(string, strlen(string), &prev))) {
	    error("abbrev \"%s %s\"", a->abbr, a->exp);
	    return;
	} else {
	    error("abreviation \"%s\" doesn't exist", string);
	    return;
	}
    }

    if (find_abbr(string, strlen(string), &prev))
	del_abbr(string);

    a = scxmalloc(sizeof(struct abbrev));
    a->abbr = string;
    a->exp = expansion;

    if (prev) {
	a->a_next = prev->a_next;
	a->a_prev = prev;
	prev->a_next = a;
	if (a->a_next)
	    a->a_next->a_prev = a;
    } else {
	a->a_next = abbr_base;
	a->a_prev = NULL;
	if (abbr_base)
	    abbr_base->a_prev = a;
	abbr_base = a;
    }
}