/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include "lint.h" #include #include #include #include "libc.h" #include "gettext.h" #include "plural_parser.h" /* * 31 28 24 20 16 12 8 4 0 * +-----+-----+-----+-----+-----+-----+-----+-----+ * |opnum| priority | operator | * +-----+-----+-----+-----+-----+-----+-----+-----+ */ static const unsigned int operator[] = { 0x00000000, /* NULL */ 0x00000001, /* INIT */ 0x00100002, /* EXP */ 0x00200003, /* NUM */ 0x00300004, /* VAR */ 0x30400005, /* CONDC */ 0x30500006, /* CONDQ */ 0x20600007, /* OR */ 0x20700008, /* AND */ 0x20800009, /* EQ */ 0x2080000a, /* NEQ */ 0x2090000b, /* GT */ 0x2090000c, /* LT */ 0x2090000d, /* GE */ 0x2090000e, /* LE */ 0x20a0000f, /* ADD */ 0x20a00010, /* SUB */ 0x20b00011, /* MUL */ 0x20b00012, /* DIV */ 0x20b00013, /* MOD */ 0x10c00014, /* NOT */ 0x00d00015, /* LPAR */ 0x00e00016, /* RPAR */ 0x00000017 /* ERR */ }; #define STACKFREE \ { \ while (stk->index > 0) \ freeexpr(stk->ptr[--stk->index]); \ free(stk->ptr); \ } #ifdef PARSE_DEBUG static const char *type_name[] = { "T_NULL", "T_INIT", "T_EXP", "T_NUM", "T_VAR", "T_CONDC", "T_CONDQ", "T_LOR", "T_LAND", "T_EQ", "T_NEQ", "T_GT", "T_LT", "T_GE", "T_LE", "T_ADD", "T_SUB", "T_MUL", "T_DIV", "T_MOD", "T_LNOT", "T_LPAR", "T_RPAR", "T_ERR" }; #endif static void freeexpr(struct expr *); static struct expr * stack_push(struct stack *stk, struct expr *exp) { #ifdef PARSE_DEBUG printf("--- stack_push ---\n"); printf(" type: %s\n", type_name[GETTYPE(exp->op)]); printf(" flag: %s\n", type_name[GETTYPE(exp->flag)]); printf("------------------\n"); #endif stk->ptr[stk->index++] = exp; if (stk->index == MAX_STACK_SIZE) { /* overflow */ freeexpr(exp); STACKFREE; return (NULL); } return (exp); } static struct expr * stack_pop(struct stack *stk, struct expr *exp_a, struct expr *exp_b) { if (stk->index == 0) { /* no item */ if (exp_a) freeexpr(exp_a); if (exp_b) freeexpr(exp_b); STACKFREE; return (NULL); } #ifdef PARSE_DEBUG printf("--- stack_pop ---\n"); printf(" type: %s\n", type_name[GETTYPE((stk->ptr[stk->index - 1])->op)]); printf(" flag: %s\n", type_name[GETTYPE((stk->ptr[stk->index - 1])->flag)]); printf("-----------------\n"); #endif return (stk->ptr[--stk->index]); } static void freeexpr(struct expr *e) { #ifdef PARSE_DEBUG printf("--- freeexpr ---\n"); printf(" type: %s\n", type_name[GETTYPE(e->op)]); printf("----------------\n"); #endif switch (GETOPNUM(e->op)) { case TRINARY: if (e->nodes[2]) freeexpr(e->nodes[2]); /* FALLTHROUGH */ case BINARY: if (e->nodes[1]) freeexpr(e->nodes[1]); /* FALLTHROUGH */ case UNARY: if (e->nodes[0]) freeexpr(e->nodes[0]); /* FALLTHROUGH */ default: break; } free(e); } static struct expr * setop1(unsigned int op, unsigned int num, struct stack *stk, unsigned int flag) { struct expr *newitem; unsigned int type; type = GETTYPE(op); #ifdef PARSE_DEBUG printf("---setop1---\n"); printf(" op type: %s\n", type_name[type]); printf("-----------\n"); #endif newitem = (struct expr *)calloc(1, sizeof (struct expr)); if (!newitem) { STACKFREE; return (NULL); } newitem->op = op; if (type == T_NUM) newitem->num = num; newitem->flag = flag; return (newitem); } static struct expr * setop_reduce(unsigned int n, unsigned int op, struct stack *stk, struct expr *exp1, struct expr *exp2, struct expr *exp3) { struct expr *newitem; #ifdef PARSE_DEBUG unsigned int type; type = GETTYPE(op); printf("---setop_reduce---\n"); printf(" n: %d\n", n); printf(" op type: %s\n", type_name[type]); switch (n) { case TRINARY: printf(" exp3 type: %s\n", type_name[GETTYPE(exp3->op)]); case BINARY: printf(" exp2 type: %s\n", type_name[GETTYPE(exp2->op)]); case UNARY: printf(" exp1 type: %s\n", type_name[GETTYPE(exp1->op)]); case NARY: break; } printf("-----------\n"); #endif newitem = (struct expr *)calloc(1, sizeof (struct expr)); if (!newitem) { if (exp1) freeexpr(exp1); if (exp2) freeexpr(exp2); if (exp3) freeexpr(exp3); STACKFREE; return (NULL); } newitem->op = op; switch (n) { case TRINARY: newitem->nodes[2] = exp3; /* FALLTHROUGH */ case BINARY: newitem->nodes[1] = exp2; /* FALLTHROUGH */ case UNARY: newitem->nodes[0] = exp1; /* FALLTHROUGH */ case NARY: break; } return (newitem); } static int reduce(struct expr **nexp, unsigned int n, struct expr *exp, struct stack *stk) { struct expr *exp_op, *exp1, *exp2, *exp3; unsigned int tmp_flag; unsigned int oop; #ifdef PARSE_DEBUG printf("---reduce---\n"); printf(" n: %d\n", n); printf("-----------\n"); #endif switch (n) { case UNARY: /* unary operator */ exp1 = exp; exp_op = stack_pop(stk, exp1, NULL); if (!exp_op) return (1); tmp_flag = exp_op->flag; oop = exp_op->op; freeexpr(exp_op); *nexp = setop_reduce(UNARY, oop, stk, exp1, NULL, NULL); if (!*nexp) return (-1); (*nexp)->flag = tmp_flag; return (0); case BINARY: /* binary operator */ exp2 = exp; exp_op = stack_pop(stk, exp2, NULL); if (!exp_op) return (1); exp1 = stack_pop(stk, exp_op, exp2); if (!exp1) return (1); tmp_flag = exp1->flag; oop = exp_op->op; freeexpr(exp_op); *nexp = setop_reduce(BINARY, oop, stk, exp1, exp2, NULL); if (!*nexp) return (-1); (*nexp)->flag = tmp_flag; return (0); case TRINARY: /* trinary operator: conditional */ exp3 = exp; exp_op = stack_pop(stk, exp3, NULL); if (!exp_op) return (1); freeexpr(exp_op); exp2 = stack_pop(stk, exp3, NULL); if (!exp2) return (1); exp_op = stack_pop(stk, exp2, exp3); if (!exp_op) return (1); if (GETTYPE(exp_op->op) != T_CONDQ) { /* parse failed */ freeexpr(exp_op); freeexpr(exp2); freeexpr(exp3); STACKFREE; return (1); } oop = exp_op->op; freeexpr(exp_op); exp1 = stack_pop(stk, exp2, exp3); if (!exp1) return (1); tmp_flag = exp1->flag; *nexp = setop_reduce(TRINARY, oop, stk, exp1, exp2, exp3); if (!*nexp) return (-1); (*nexp)->flag = tmp_flag; return (0); } /* NOTREACHED */ return (0); /* keep gcc happy */ } static unsigned int gettoken(const char **pstr, unsigned int *num, int which) { unsigned char *sp = *(unsigned char **)pstr; unsigned int n; unsigned int ret; while (*sp && ((*sp == ' ') || (*sp == '\t'))) sp++; if (!*sp) { if (which == GET_TOKEN) *pstr = (const char *)sp; return (T_NULL); } if (isdigit(*sp)) { n = *sp - '0'; sp++; while (isdigit(*sp)) { n *= 10; n += *sp - '0'; sp++; } *num = n; ret = T_NUM; } else if (*sp == 'n') { sp++; ret = T_VAR; } else if (*sp == '(') { sp++; ret = T_LPAR; } else if (*sp == ')') { sp++; ret = T_RPAR; } else if (*sp == '!') { sp++; if (*sp == '=') { sp++; ret = T_NEQ; } else { ret = T_LNOT; } } else if (*sp == '*') { sp++; ret = T_MUL; } else if (*sp == '/') { sp++; ret = T_DIV; } else if (*sp == '%') { sp++; ret = T_MOD; } else if (*sp == '+') { sp++; ret = T_ADD; } else if (*sp == '-') { sp++; ret = T_SUB; } else if (*sp == '<') { sp++; if (*sp == '=') { sp++; ret = T_LE; } else { ret = T_LT; } } else if (*sp == '>') { sp++; if (*sp == '=') { sp++; ret = T_GE; } else { ret = T_GT; } } else if (*sp == '=') { sp++; if (*sp == '=') { sp++; ret = T_EQ; } else { ret = T_ERR; } } else if (*sp == '&') { sp++; if (*sp == '&') { sp++; ret = T_LAND; } else { ret = T_ERR; } } else if (*sp == '|') { sp++; if (*sp == '|') { sp++; ret = T_LOR; } else { ret = T_ERR; } } else if (*sp == '?') { sp++; ret = T_CONDQ; } else if (*sp == ':') { sp++; ret = T_CONDC; } else if ((*sp == '\n') || (*sp == ';')) { ret = T_NULL; } else { ret = T_ERR; } if (which == GET_TOKEN) *pstr = (const char *)sp; return (operator[ret]); } /* * plural_expr * * INPUT * str: string to parse * * OUTPUT * e: parsed expression * * RETURN * -1: Error happend (malloc failed) * 1: Parse failed (invalid expression) * 0: Parse succeeded */ int plural_expr(struct expr **e, const char *plural_string) { const char *pstr = plural_string; struct stack *stk, stkbuf; struct expr *exp, *nexp, *exp_op, *ret; int par, result; unsigned int flag, ftype, fprio, fopnum, tmp_flag; unsigned int ntype, nprio, ptype, popnum; unsigned int op, nop, num, type, opnum; stk = &stkbuf; stk->index = 0; stk->ptr = (struct expr **)malloc( sizeof (struct expr *) * MAX_STACK_SIZE); if (!stk->ptr) { /* malloc failed */ return (-1); } flag = operator[T_INIT]; par = 0; while ((op = gettoken(&pstr, &num, GET_TOKEN)) != T_NULL) { type = GETTYPE(op); opnum = GETOPNUM(op); ftype = GETTYPE(flag); #ifdef PARSE_DEBUG printf("*** %s ***\n", type_name[type]); printf(" flag: %s\n", type_name[ftype]); printf(" par: %d\n", par); printf("***********\n"); #endif if (type == T_ERR) { /* parse failed */ STACKFREE; return (1); } if (opnum == BINARY) { /* binary operation */ if (ftype != T_EXP) { /* parse failed */ #ifdef PARSE_DEBUG printf("ERR: T_EXP is not followed by %s\n", type_name[type]); #endif STACKFREE; return (1); } exp = setop1(op, 0, stk, flag); if (!exp) return (-1); ret = stack_push(stk, exp); if (!ret) return (1); flag = op; continue; /* while-loop */ } if (type == T_CONDQ) { /* conditional operation: '?' */ if (ftype != T_EXP) { /* parse failed */ #ifdef PARSE_DEBUG printf("ERR: T_EXP is not followed by %s\n", type_name[type]); #endif STACKFREE; return (1); } exp = setop1(op, 0, stk, flag); if (!exp) return (-1); ret = stack_push(stk, exp); if (!ret) return (1); flag = op; continue; /* while-loop */ } if (type == T_CONDC) { /* conditional operation: ':' */ if (ftype != T_EXP) { /* parse failed */ #ifdef PARSE_DEBUG printf("ERR: T_EXP is not followed by %s\n", type_name[type]); #endif STACKFREE; return (1); } exp = setop1(op, 0, stk, flag); if (!exp) return (-1); ret = stack_push(stk, exp); if (!ret) return (1); flag = op; continue; /* while-loop */ } if (type == T_LPAR) { /* left parenthesis */ if (ftype == T_EXP) { /* parse failed */ #ifdef PARSE_DEBUG printf("ERR: T_EXP is followed by %s\n", type_name[type]); #endif STACKFREE; return (1); } exp = setop1(op, 0, stk, flag); if (!exp) return (-1); ret = stack_push(stk, exp); if (!ret) return (1); par++; flag = op; continue; /* while-loop */ } if (type == T_RPAR) { /* right parenthesis */ if (ftype != T_EXP) { /* parse failed */ #ifdef PARSE_DEBUG printf("ERR: T_EXP is not followed by %s\n", type_name[type]); #endif STACKFREE; return (1); } par--; if (par < 0) { /* parse failed */ #ifdef PARSE_DEBUG printf("ERR: too much T_RPAR\n"); #endif STACKFREE; return (1); } exp = stack_pop(stk, NULL, NULL); if (!exp) return (1); #ifdef PARSE_DEBUG printf("======================== RPAR for loop in\n"); #endif for (; ; ) { ptype = GETTYPE(exp->flag); popnum = GETOPNUM(exp->flag); #ifdef PARSE_DEBUG printf("=========== exp->flag: %s\n", type_name[ptype]); #endif if (ptype == T_LPAR) { exp_op = stack_pop(stk, exp, NULL); if (!exp_op) return (1); tmp_flag = exp_op->flag; freeexpr(exp_op); exp->flag = tmp_flag; flag = tmp_flag; break; /* break from for-loop */ } if ((popnum == BINARY) || (ptype == T_LNOT) || (ptype == T_CONDC)) { result = reduce(&nexp, popnum, exp, stk); if (result) return (result); exp = nexp; continue; /* for-loop */ } /* parse failed */ freeexpr(exp); STACKFREE; return (1); } /* for-loop */ #ifdef PARSE_DEBUG printf("========================= RPAR for loop out\n"); #endif /* * Needs to check if exp can be reduced or not */ goto exp_check; } if (type == T_LNOT) { if (ftype == T_EXP) { /* parse failed */ #ifdef PARSE_DEBUG printf("ERR: T_EXP is followed by %s\n", type_name[type]); #endif STACKFREE; return (1); } exp = setop1(op, 0, stk, flag); if (!exp) return (-1); ret = stack_push(stk, exp); if (!ret) return (1); flag = op; continue; /* while-loop */ } if ((type == T_NUM) || (type == T_VAR)) { exp = setop1(op, type == T_NUM ? num : 0, stk, flag); if (!exp) return (-1); exp_check: ftype = GETTYPE(flag); if ((ftype == T_INIT) || (ftype == T_LPAR)) { /* * if this NUM/VAR is the first EXP, * just push this */ exp->flag = flag; ret = stack_push(stk, exp); if (!ret) return (1); flag = operator[T_EXP]; continue; /* while-loop */ } if (ftype == T_EXP) { /* * parse failed * NUM/VAR cannot be seen just after * T_EXP */ freeexpr(exp); STACKFREE; return (1); } nop = gettoken(&pstr, &num, PEEK_TOKEN); if (nop != T_NULL) { ntype = GETTYPE(nop); nprio = GETPRIO(nop); } else { (void) gettoken(&pstr, &num, GET_TOKEN); ntype = T_INIT; nprio = 0; } #ifdef PARSE_DEBUG printf("========================== T_NUM/T_VAR for loop in\n"); #endif for (; ; ) { ftype = GETTYPE(flag); fopnum = GETOPNUM(flag); fprio = GETPRIO(flag); #ifdef PARSE_DEBUG printf("========= flag: %s\n", type_name[ftype]); #endif if ((ftype == T_INIT) || (ftype == T_LPAR)) { exp->flag = flag; ret = stack_push(stk, exp); if (!ret) return (1); flag = operator[T_EXP]; break; /* exit from for-loop */ } if (ftype == T_LNOT) { /* LNOT is the strongest */ result = reduce(&nexp, UNARY, exp, stk); if (result) return (result); exp = nexp; flag = nexp->flag; continue; /* for-loop */ } if (fopnum == BINARY) { /* * binary operation * T_MUL, T_ADD, T_CMP, * T_EQ, T_LAND, T_LOR */ if ((ntype == T_RPAR) || (nprio <= fprio)) { /* reduce */ result = reduce(&nexp, BINARY, exp, stk); if (result) return (result); exp = nexp; flag = nexp->flag; continue; /* for-loop */ } /* shift */ exp->flag = flag; ret = stack_push(stk, exp); if (!ret) return (1); flag = operator[T_EXP]; break; /* exit from for loop */ } if (ftype == T_CONDQ) { /* * CONDQ is the weakest * always shift */ exp->flag = flag; ret = stack_push(stk, exp); if (!ret) return (1); flag = operator[T_EXP]; break; /* exit from for loop */ } if (ftype == T_CONDC) { if (nprio <= fprio) { /* reduce */ result = reduce(&nexp, TRINARY, exp, stk); if (result) return (result); exp = nexp; flag = nexp->flag; continue; /* for-loop */ } /* shift */ exp->flag = flag; ret = stack_push(stk, exp); if (!ret) return (1); flag = operator[T_EXP]; break; /* exit from for-loop */ } /* parse failed */ freeexpr(exp); STACKFREE; return (1); } #ifdef PARSE_DEBUG printf("======================= T_NUM/T_VAR for loop out\n"); #endif continue; /* while-loop */ } /* parse failed */ STACKFREE; return (1); } /* while-loop */ if (GETTYPE(flag) != T_EXP) { /* parse failed */ #ifdef PARSE_DEBUG printf("XXXX ERROR: flag is not T_INIT\n"); printf("========= flag: %s\n", type_name[GETTYPE(flag)]); #endif STACKFREE; return (1); } else { exp = stack_pop(stk, NULL, NULL); if (!exp) return (1); if (GETTYPE(exp->flag) != T_INIT) { /* parse failed */ #ifdef PARSE_DEBUG printf("ERR: flag for the result is not T_INIT\n"); printf(" %s observed\n", type_name[GETTYPE(exp->flag)]); #endif freeexpr(exp); STACKFREE; return (1); } if (stk->index > 0) { /* * exp still remains in stack. * parse failed */ while (nexp = stack_pop(stk, NULL, NULL)) freeexpr(nexp); freeexpr(exp); return (1); } /* parse succeeded */ *e = exp; STACKFREE; return (0); } } unsigned int plural_eval(struct expr *exp, unsigned int n) { unsigned int e1, e2; unsigned int type, opnum; #ifdef GETTEXT_DEBUG (void) printf("*************** plural_eval(%p, %d)\n", exp, n); printexpr(exp, 0); #endif type = GETTYPE(exp->op); opnum = GETOPNUM(exp->op); switch (opnum) { case NARY: if (type == T_NUM) { return (exp->num); } else if (type == T_VAR) { return (n); } break; case UNARY: /* T_LNOT */ e1 = plural_eval(exp->nodes[0], n); return (!e1); case BINARY: e1 = plural_eval(exp->nodes[0], n); /* optimization for T_LOR and T_LAND */ if (type == T_LOR) { return (e1 || plural_eval(exp->nodes[1], n)); } else if (type == T_LAND) { return (e1 && plural_eval(exp->nodes[1], n)); } e2 = plural_eval(exp->nodes[1], n); switch (type) { case T_EQ: return (e1 == e2); case T_NEQ: return (e1 != e2); case T_GT: return (e1 > e2); case T_LT: return (e1 < e2); case T_GE: return (e1 >= e2); case T_LE: return (e1 <= e2); case T_ADD: return (e1 + e2); case T_SUB: return (e1 - e2); case T_MUL: return (e1 * e2); case T_DIV: if (e2 != 0) return (e1 / e2); break; case T_MOD: if (e2 != 0) return (e1 % e2); break; } break; case TRINARY: /* T_CONDQ */ e1 = plural_eval(exp->nodes[0], n); if (e1) { return (plural_eval(exp->nodes[1], n)); } else { return (plural_eval(exp->nodes[2], n)); } } /* should not be here */ return (0); }