diff -Nru clearsilver-0.9.7/INSTALL clearsilver-0.9.8/INSTALL --- clearsilver-0.9.7/INSTALL Mon Aug 18 13:24:45 2003 +++ clearsilver-0.9.8/INSTALL Tue Mar 16 01:43:46 2004 @@ -30,6 +30,7 @@ --with-java=path Set location of J2SDK --disable-csharp Disables building of csharp module --with-csharp=path Set location of csharp + --enable-gettext Enable gettext message translation --disable-compression: Currently, the CGI output code in the cgi kit automatically attempts to detect whether the remote browser can @@ -43,6 +44,10 @@ controls such as a configurable list of allowed displays, but remote debugging is disabled by default. +--enable-gettext: Enables gettext message translation. Once enabled +you have a new builtin function "_()". This function calls the +function gettext (man 3 gettext). + The rest of the --disable/--with either disable a specific module, or point ClearSilver at the right program to enable it. The configure script will simple not build any module it can't find the right versions diff -Nru clearsilver-0.9.7/Makefile clearsilver-0.9.8/Makefile --- clearsilver-0.9.7/Makefile Tue Nov 18 18:07:55 2003 +++ clearsilver-0.9.8/Makefile Mon Jan 5 02:36:11 2004 @@ -82,7 +82,7 @@ hdf: @mkdir -p docs/hdf @for mdir in $(SUBDIRS); do \ - scripts/document.py --hdf --owner "ClearSilver." --outdir docs/hdf/ $$mdir/*.h; \ + scripts/document.py --hdf --owner "ClearSilver" --outdir docs/hdf/ $$mdir/*.h; \ done changelog: diff -Nru clearsilver-0.9.7/acconfig.h clearsilver-0.9.8/acconfig.h --- clearsilver-0.9.7/acconfig.h Mon Apr 14 16:05:07 2003 +++ clearsilver-0.9.8/acconfig.h Tue Mar 16 01:43:46 2004 @@ -56,6 +56,9 @@ /* Does your system have Berkeley DB v2 ? */ #undef HAVE_DB2 +/* Enable support for gettext message translation */ +#undef ENABLE_GETTEXT + @BOTTOM@ #endif /* __CS_CONFIG_H_ */ diff -Nru clearsilver-0.9.7/cgi/cgi.c clearsilver-0.9.8/cgi/cgi.c --- clearsilver-0.9.7/cgi/cgi.c Tue Nov 18 17:54:37 2003 +++ clearsilver-0.9.8/cgi/cgi.c Thu Mar 25 23:13:11 2004 @@ -199,6 +199,7 @@ {"HTTP_USER_AGENT", "UserAgent"}, {"HTTP_IF_MODIFIED_SINCE", "IfModifiedSince"}, {"HTTP_REFERER", "Referer"}, + {"HTTP_VIA", "Via"}, /* SOAP */ {"HTTP_SOAPACTION", "Soap.Action"}, {NULL, NULL} @@ -403,12 +404,14 @@ NEOERR *err = STATUS_OK; char *t, *k, *v, *l; char buf[256]; + char unnamed[10]; + int unnamed_count = 0; HDF *obj, *child; - if (query) + if (query && *query) { k = strtok_r(query, "&", &l); - while (k) + while (k && *k) { v = strchr(k, '='); if (v == NULL) @@ -420,9 +423,23 @@ *v = '\0'; v++; } + + + /* Check for some invalid query strings */ + if (*k == 0) { + /* '?=foo' gets mapped in as Query._1=foo */ + snprintf(unnamed,sizeof(unnamed), "_%d", unnamed_count++); + k = unnamed; + } else if (*k == '.') { + /* an hdf element can't start with a period */ + *k = '_'; + } snprintf(buf, sizeof(buf), "Query.%s", cgi_url_unescape(k)); + if (!(cgi->ignore_empty_form_vars && (*v == '\0'))) { + + cgi_url_unescape(v); obj = hdf_get_obj (cgi->hdf, buf); if (obj != NULL) @@ -452,6 +469,15 @@ if (err != STATUS_OK) break; } err = hdf_set_value (cgi->hdf, buf, v); + if (nerr_match(err, NERR_ASSERT)) { + STRING str; + + string_init(&str); + nerr_error_string(err, &str); + ne_warn("Unable to set Query value: %s = %s: %s", buf, v, str.buf); + string_clear(&str); + nerr_ignore(&err); + } if (err != STATUS_OK) break; } k = strtok_r(NULL, "&", &l); @@ -545,6 +571,15 @@ if (k[0] && v[0]) { err = hdf_set_value (obj, k, v); + if (nerr_match(err, NERR_ASSERT)) { + STRING str; + + string_init(&str); + nerr_error_string(err, &str); + ne_warn("Unable to set Cookie value: %s = %s: %s", k, v, str.buf); + string_clear(&str); + nerr_ignore(&err); + } if (err) break; } k = l; @@ -1242,7 +1277,8 @@ { char *dest; static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ - unsigned long crc = 0; + char gz_buf[20]; /* gzip header/footer buffer, len of header is 10 bytes */ + unsigned int crc = 0; int len2; if (use_gzip) @@ -1260,8 +1296,14 @@ { if (use_gzip) { - err = cgiwrap_writef("%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1], - Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE); + /* I'm using sprintf instead of cgiwrap_writef since + * the wrapper writef might not handle values with + * embedded NULLs... though I should fix the python one + * now as well */ + sprintf(gz_buf, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1], + Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, + OS_CODE); + err = cgiwrap_write(gz_buf, 10); } if (err != STATUS_OK) break; err = cgiwrap_write(dest, len2); @@ -1269,9 +1311,17 @@ if (use_gzip) { - err = cgiwrap_writef("%c%c%c%c", (0xff & (crc >> 0)), (0xff & (crc >> 8)), (0xff & (crc >> 16)), (0xff & (crc >> 24))); - if (err != STATUS_OK) break; - err = cgiwrap_writef("%c%c%c%c", (0xff & (str->len >> 0)), (0xff & (str->len >> 8)), (0xff & (str->len >> 16)), (0xff & (str->len >> 24))); + /* write crc and len in network order */ + sprintf(gz_buf, "%c%c%c%c%c%c%c%c", + (0xff & (crc >> 0)), + (0xff & (crc >> 8)), + (0xff & (crc >> 16)), + (0xff & (crc >> 24)), + (0xff & (str->len >> 0)), + (0xff & (str->len >> 8)), + (0xff & (str->len >> 16)), + (0xff & (str->len >> 24))); + err = cgiwrap_write(gz_buf, 8); if (err != STATUS_OK) break; } } @@ -1449,8 +1499,6 @@ void cgi_vredirect (CGI *cgi, int uri, char *fmt, va_list ap) { - char *host; - cgiwrap_writef ("Status: 302\r\n"); cgiwrap_writef ("Content-Type: text/html\r\n"); cgiwrap_writef ("Pragma: no-cache\r\n"); @@ -1463,10 +1511,28 @@ } else { + char *host; + int https = 0; + + if (!strcmp(hdf_get_value(cgi->hdf, "CGI.HTTPS", "off"), "on")) + { + https = 1; + } + host = hdf_get_value (cgi->hdf, "HTTP.Host", NULL); if (host == NULL) host = hdf_get_value (cgi->hdf, "CGI.ServerName", NULL); - cgiwrap_writef ("Location: http://%s", host); + + cgiwrap_writef ("Location: %s://%s", https ? "https" : "http", host); + + if ((strchr(host, ':') == NULL)) { + int port = hdf_get_int_value(cgi->hdf, "CGI.ServerPort", 80); + + if (!((https && port == 443) || (!https && port == 80))) + { + cgiwrap_writef(":%d", port); + } + } } cgiwrap_writevf (fmt, ap); cgiwrap_writef ("\r\n\r\n"); @@ -1536,46 +1602,61 @@ return NULL; } +/* For more information about Cookies, see: + * The original Netscape Cookie Spec: + * http://wp.netscape.com/newsref/std/cookie_spec.html + * + * HTTP State Management Mechanism + * http://www.ietf.org/rfc/rfc2109.txt + */ + NEOERR *cgi_cookie_set (CGI *cgi, char *name, char *value, char *path, - char *domain, char *time_str, int persistent) + char *domain, char *time_str, int persistent, int secure) { + NEOERR *err; + STRING str; char my_time[256]; if (path == NULL) path = "/"; - if (persistent) - { - if (time_str == NULL) + + string_init(&str); + do { + err = string_appendf(&str, "Set-Cookie: %s=%s; path=%s", name, value, path); + if (err) break; + + if (persistent) { - /* Expires in one year */ - time_t exp_date = time(NULL) + 31536000; + if (time_str == NULL) + { + /* Expires in one year */ + time_t exp_date = time(NULL) + 31536000; - strftime (my_time, 48, "%A, %d-%b-%Y 23:59:59 GMT", - gmtime (&exp_date)); - time_str = my_time; + strftime (my_time, 48, "%A, %d-%b-%Y 23:59:59 GMT", + gmtime (&exp_date)); + time_str = my_time; + } + err = string_appendf(&str, "; expires=%s", time_str); + if (err) break; } if (domain) { - cgiwrap_writef ("Set-Cookie: %s=%s; path=%s; expires=%s; domain=%s\r\n", - name, value, path, time_str, domain); + err = string_appendf(&str, "; domain=%s", domain); + if (err) break; } - else + if (secure) { - cgiwrap_writef ("Set-Cookie: %s=%s; path=%s; expires=%s\r\n", name, - value, path, time_str); + err = string_append(&str, "; secure"); + if (err) break; } - } - else + err = string_append(&str, "\r\n"); + } while (0); + if (err) { - if (domain) - { - cgiwrap_writef ("Set-Cookie: %s=%s; path=%s; domain=%s\r\n", name, - value, path, domain); - } - else - { - cgiwrap_writef ("Set-Cookie: %s=%s; path=%s\r\n", name, value, path); - } + string_clear(&str); + return nerr_pass(err); } + cgiwrap_write(str.buf, str.len); + string_clear(&str); return STATUS_OK; } diff -Nru clearsilver-0.9.7/cgi/cgi.h clearsilver-0.9.8/cgi/cgi.h --- clearsilver-0.9.7/cgi/cgi.h Wed Oct 8 16:45:40 2003 +++ clearsilver-0.9.8/cgi/cgi.h Tue Mar 16 01:40:56 2004 @@ -421,11 +421,12 @@ * Wdy, DD-Mon-YYYY HH:MM:SS GMT. Only used if * persistent. Default is one year from time of call. * persistent - cookie will be stored by the browser between sessions + * secure - cookie will only be sent over secure connections * Output: None * Return: NERR_IO */ NEOERR *cgi_cookie_set (CGI *cgi, char *name, char *value, char *path, - char *domain, char *time_str, int persistent); + char *domain, char *time_str, int persistent, int secure); /* * Function: cgi_cookie_clear - clear browser cookie diff -Nru clearsilver-0.9.7/configure.in clearsilver-0.9.8/configure.in --- clearsilver-0.9.7/configure.in Mon Sep 22 19:09:38 2003 +++ clearsilver-0.9.8/configure.in Tue Mar 16 01:43:46 2004 @@ -437,6 +437,21 @@ fi fi +AC_ARG_ENABLE(gettext, [ --enable-gettext Enables gettext message translation], + [if test $enableval = yes; then + dnl Check for gettext + AC_CHECK_FUNC(gettext, [ + cs_cv_libintl=no + AC_CHECK_HEADER(libintl.h, [cs_cv_libintl=yes]) + if test $cs_cv_libintl = yes; then + AC_DEFINE(ENABLE_GETTEXT) + AC_MSG_RESULT(Enabling gettext message translation) + else + AC_MSG_RESULT(not found) + fi + ]) + fi]) + AC_SUBST(RANLIB) AC_SUBST(AR) diff -Nru clearsilver-0.9.7/cs/Makefile clearsilver-0.9.8/cs/Makefile --- clearsilver-0.9.7/cs/Makefile Sun Jul 27 09:26:19 2003 +++ clearsilver-0.9.8/cs/Makefile Thu Apr 22 00:41:53 2004 @@ -27,7 +27,12 @@ TARGETS = $(CS_LIB) $(CSTEST_EXE) $(CSR_EXE) test -CS_TESTS = test.cs test2.cs test3.cs test4.cs test5.cs test6.cs test7.cs test8.cs test9.cs test10.cs test11.cs test12.cs test13.cs test14.cs test15.cs test16.cs test17.cs test18.cs test_var.cs test_paren.cs test_chuck.cs test_trak1.cs test_iter.cs test_each_array.cs test_name.cs test_with.cs test_numbers.cs +CS_TESTS = test.cs test2.cs test3.cs test4.cs test5.cs test6.cs test7.cs \ + test8.cs test9.cs test10.cs test11.cs test12.cs test13.cs \ + test14.cs test15.cs test16.cs test17.cs test18.cs test_var.cs \ + test_paren.cs test_chuck.cs test_trak1.cs test_iter.cs \ + test_each_array.cs test_name.cs test_with.cs test_numbers.cs \ + test_splice.cs all: $(TARGETS) @@ -53,7 +58,7 @@ ./cstest test_tag.hdf test_tag.cs > test_tag.cs.gold @echo "Generated Gold Files" -test: $(CSTEST_EXE) $(CS_TESTS) +test: $(CSTEST_EXE) $(CS_TESTS) @echo "Running cs regression tests" @failed=0; \ for test in $(CS_TESTS); do \ diff -Nru clearsilver-0.9.7/cs/cs.h clearsilver-0.9.8/cs/cs.h --- clearsilver-0.9.7/cs/cs.h Fri Aug 8 23:54:12 2003 +++ clearsilver-0.9.8/cs/cs.h Sat Nov 29 23:24:14 2003 @@ -78,6 +78,7 @@ CS_OP_RBRACKET = (1<<20), CS_OP_DOT = (1<<21), + CS_OP_COMMA = (1<<22), /* Types */ CS_TYPE_STRING = (1<<25), diff -Nru clearsilver-0.9.7/cs/csparse.c clearsilver-0.9.8/cs/csparse.c --- clearsilver-0.9.7/cs/csparse.c Mon Aug 4 18:49:40 2003 +++ clearsilver-0.9.8/cs/csparse.c Thu Apr 22 00:44:20 2004 @@ -20,6 +20,11 @@ #include #include #include +#include + +#ifdef ENABLE_GETTEXT +#include +#endif #include "util/neo_misc.h" #include "util/neo_err.h" @@ -90,6 +95,8 @@ static NEOERR *alt_eval (CSPARSE *parse, CSTREE *node, CSTREE **next); static NEOERR *render_node (CSPARSE *parse, CSTREE *node); +static NEOERR *cs_init_internal (CSPARSE **parse, HDF *hdf, BOOL init_funcs); +static int rearrange_for_call(CSARG **args); typedef struct _cmds { @@ -719,6 +726,7 @@ { FALSE, "[", CS_OP_LBRACKET }, { FALSE, "]", CS_OP_RBRACKET }, { FALSE, ".", CS_OP_DOT }, + { FALSE, ",", CS_OP_COMMA }, { FALSE, NULL, 0 } }; @@ -874,15 +882,16 @@ } CSTOKEN_TYPE OperatorOrder[] = { - CS_OP_OR, - CS_OP_AND, - CS_OP_NOT | CS_OP_EXISTS, - CS_OP_EQUAL | CS_OP_NEQUAL, - CS_OP_GT | CS_OP_GTE | CS_OP_LT | CS_OP_LTE, - CS_OP_ADD | CS_OP_SUB, - CS_OP_MULT | CS_OP_DIV | CS_OP_MOD | CS_OP_DOT, - CS_OP_LBRACKET, - 0 + CS_OP_COMMA, + CS_OP_OR, + CS_OP_AND, + CS_OP_NOT | CS_OP_EXISTS, + CS_OP_EQUAL | CS_OP_NEQUAL, + CS_OP_GT | CS_OP_GTE | CS_OP_LT | CS_OP_LTE, + CS_OP_ADD | CS_OP_SUB, + CS_OP_MULT | CS_OP_DIV | CS_OP_MOD | CS_OP_DOT, + CS_OP_LBRACKET, + 0 }; static char *expand_token_type(CSTOKEN_TYPE t_type, int more) @@ -910,6 +919,7 @@ case CS_OP_LBRACKET: return "["; case CS_OP_RBRACKET: return "]"; case CS_OP_DOT : return "."; + case CS_OP_COMMA : return ","; case CS_TYPE_STRING: return more ? "STRING" : "s"; case CS_TYPE_NUM: return more ? "NUM" : "n"; case CS_TYPE_VAR: return more ? "VAR" : "v"; @@ -947,7 +957,6 @@ return buf; } - static NEOERR *parse_expr2 (CSPARSE *parse, CSTOKEN *tokens, int ntokens, int lvalue, CSARG *arg) { NEOERR *err; @@ -1065,23 +1074,43 @@ find_context(parse, -1, tmp, sizeof(tmp)), expand_token_type(tokens[x].type, 0)); } - if (OperatorOrder[op] & CS_OPS_UNARY) + if (tokens[x].type & OperatorOrder[op]) { - if (x == 0 && tokens[x].type & OperatorOrder[op]) + if (OperatorOrder[op] & CS_OPS_UNARY) + { + if (x == 0) + { + arg->op_type = tokens[x].type; + arg->expr1 = (CSARG *) calloc (1, sizeof (CSARG)); + if (arg->expr1 == NULL) + return nerr_raise (NERR_NOMEM, + "%s Unable to allocate memory for expression", + find_context(parse, -1, tmp, sizeof(tmp))); + err = parse_expr2(parse, tokens + 1, ntokens-1, lvalue, arg->expr1); + return nerr_pass(err); + } + } + else if (tokens[x].type == CS_OP_COMMA) { + /* Technically, comma should be a left to right, not right to + * left, so we're going to build up the arguments in reverse + * order... */ arg->op_type = tokens[x].type; + /* The actual argument is expr1 */ arg->expr1 = (CSARG *) calloc (1, sizeof (CSARG)); - if (arg->expr1 == NULL) + /* The previous argument is next */ + arg->next = (CSARG *) calloc (1, sizeof (CSARG)); + if (arg->expr1 == NULL || arg->next == NULL) return nerr_raise (NERR_NOMEM, "%s Unable to allocate memory for expression", find_context(parse, -1, tmp, sizeof(tmp))); - err = parse_expr2(parse, tokens + 1, ntokens-1, lvalue, arg->expr1); - return nerr_pass(err); + err = parse_expr2(parse, tokens + x + 1, ntokens-x-1, lvalue, arg->expr1); + if (err) return nerr_pass (err); + err = parse_expr2(parse, tokens, x, lvalue, arg->next); + if (err) return nerr_pass (err); + return STATUS_OK; } - } - else - { - if (tokens[x].type & OperatorOrder[op]) + else { arg->op_type = tokens[x].type; arg->expr2 = (CSARG *) calloc (1, sizeof (CSARG)); @@ -1143,11 +1172,12 @@ return nerr_pass(err); } - /* function call (we only handle a single arg for now) */ + /* function call */ if ((tokens[0].type & CS_TYPE_VAR) && tokens[1].type == CS_OP_LPAREN && tokens[x].type == CS_OP_RPAREN) { CS_FUNCTION *csf; + int nargs; if (tokens[0].len >= 0) tokens[0].value[tokens[0].len] = '\0'; @@ -1174,6 +1204,15 @@ "%s Unable to allocate memory for expression", find_context(parse, -1, tmp, sizeof(tmp))); err = parse_expr2(parse, tokens + 2, ntokens-3, lvalue, arg->expr1); + if (err) return nerr_pass(err); + nargs = rearrange_for_call(&(arg->expr1)); + if (nargs != arg->function->n_args) + { + return nerr_raise (NERR_PARSE, + "%s Incorrect number of arguments in call to %s, expected %d, got %d", + find_context(parse, -1, tmp, sizeof(tmp)), tokens[0].value, + arg->function->n_args, nargs); + } return nerr_pass(err); } @@ -1546,6 +1585,36 @@ return v; } +char *arg_eval_str_alloc (CSPARSE *parse, CSARG *arg) +{ + char *s = NULL; + char buf[256]; + long int n_val; + + switch ((arg->op_type & CS_TYPES)) + { + case CS_TYPE_STRING: + s = arg->s; + break; + case CS_TYPE_VAR: + s = var_lookup (parse, arg->s); + break; + case CS_TYPE_NUM: + case CS_TYPE_VAR_NUM: + s = buf; + n_val = arg_eval_num (parse, arg); + snprintf (buf, sizeof(buf), "%ld", n_val); + break; + default: + ne_warn ("Unsupported type %s in arg_eval_str_alloc", + expand_token_type(arg->op_type, 1)); + s = NULL; + break; + } + if (s) return strdup(s); + return NULL; +} + #if DEBUG_EXPR_EVAL static void expand_arg (CSPARSE *parse, int depth, char *where, CSARG *arg) { @@ -1840,6 +1909,29 @@ break; } } + else if (expr->op_type == CS_OP_COMMA) + { + /* The comma operator, like in C, we return the value of the right + * most argument, in this case that's expr1, but we still need to + * evaluate the other stuff */ + if (expr->next) + { + err = eval_expr (parse, expr->next, &arg2); +#if DEBUG_EXPR_EVAL + expand_arg(parse, _depth, "arg2", &arg2); +#endif + if (err) return nerr_pass(err); + if (arg2.alloc) free(arg2.s); + } + *result = arg1; + /* we transfer ownership of the string here.. ugh */ + if (arg1.alloc) arg1.alloc = 0; +#if DEBUG_EXPR_EVAL + expand_arg(parse, _depth, "result", result); + _depth--; +#endif + return STATUS_OK; + } else { err = eval_expr (parse, expr->expr2, &arg2); @@ -1924,7 +2016,7 @@ } else if ((arg1.op_type & (CS_TYPE_NUM | CS_TYPE_VAR_NUM)) || (arg2.op_type & (CS_TYPE_NUM | CS_TYPE_VAR_NUM)) || - (expr->op_type & (CS_OP_AND | CS_OP_OR | CS_OP_SUB | CS_OP_MULT | CS_OP_DIV | CS_OP_MOD | CS_OP_GT | CS_OP_GTE | CS_OP_LT | CS_OP_LTE ))) + (expr->op_type & (CS_OP_AND | CS_OP_OR | CS_OP_SUB | CS_OP_MULT | CS_OP_DIV | CS_OP_MOD | CS_OP_GT | CS_OP_GTE | CS_OP_LT | CS_OP_LTE))) { /* eval as num */ err = eval_expr_num(parse, &arg1, &arg2, expr->op_type, result); @@ -2016,7 +2108,7 @@ } do { - err = cs_init(&cs, parse->hdf); + err = cs_init_internal(&cs, parse->hdf, FALSE); if (err) break; cs->functions = parse->functions; err = cs_parse_string(cs, s, strlen(s)); @@ -2058,7 +2150,7 @@ { CSPARSE *cs = NULL; do { - err = cs_init(&cs, parse->hdf); + err = cs_init_internal(&cs, parse->hdf, FALSE); if (err) break; cs->functions = parse->functions; err = cs_parse_file(cs, s); @@ -2540,45 +2632,60 @@ return STATUS_OK; } -static char* get_arg(char* top) +static int rearrange_for_call(CSARG **args) { - int mode = 0; - char* p; - for (p = top; *p; p++) { - if (mode == 0) { - if (*p == ',') { - return p; - } else if (*p == '"') { - mode = 1; - } - } else { - if (*p == '"') { - mode = 0; - } - } + CSARG *larg = NULL; + CSARG *carg = *args; + CSARG *vargs = NULL; + int nargs = 0; + + /* multiple argument case, we have to walk the args and reverse + * them. Also handles single arg case since its the same as the + * last arg */ + while (carg) + { + nargs++; + if (carg->op_type != CS_OP_COMMA) + { + /* last argument */ + if (vargs) + carg->next = vargs; + vargs = carg; + break; + } + if (vargs) + carg->expr1->next = vargs; + vargs = carg->expr1; + larg = carg; + carg = carg->next; + /* dealloc comma, but not its descendents */ + larg->next = NULL; + larg->expr1 = NULL; + dealloc_arg(&larg); } - return NULL; -} + *args = vargs; + return nargs; +} static NEOERR *call_parse (CSPARSE *parse, int cmd, char *arg) { NEOERR *err; CSTREE *node; CS_MACRO *macro; - CSARG *carg, *larg = NULL; + CSARG *carg; char *s, *a = NULL; char tmp[256]; char name[256]; int x = 0; - BOOL last = FALSE; + int nargs = 0; err = alloc_node (&node); if (err) return nerr_pass(err); node->cmd = cmd; arg++; s = arg; - while (x < 256 && *s && *s != ' ' && *s != '#' && *s != '(') + while (x < sizeof(name) && *s && *s != ' ' && *s != '#' && *s != '(') { name[x++] = *s; s++; @@ -2620,54 +2727,32 @@ } *a = '\0'; - x = 0; - while (*s) + while (*s && isspace(*s)) s++; + /* No arguments case */ + if (*s == '\0') { - while (*s && isspace(*s)) s++; - /* No arguments case */ - if (*s == '\0' && x == 0) break; - /* Empty argument case */ - if (*s == '\0') - { - err =nerr_raise (NERR_PARSE, - "%s Missing argument in call %s", - find_context(parse, -1, tmp, sizeof(tmp)), arg); - break; - } - a = get_arg(s); - if (a == NULL) - { - last = TRUE; - } - else - { - *a = '\0'; - } - carg = (CSARG *) calloc (1, sizeof(CSARG)); - if (carg == NULL) - { - err = nerr_raise (NERR_NOMEM, - "%s Unable to allocate memory for CSARG in call %s", - find_context(parse, -1, tmp, sizeof(tmp)), arg); - break; - } - if (larg == NULL) + nargs = 0; + } + else + { + /* Parse arguments case */ + do { + carg = (CSARG *) calloc (1, sizeof(CSARG)); + if (carg == NULL) + { + err = nerr_raise (NERR_NOMEM, + "%s Unable to allocate memory for CSARG in call %s", + find_context(parse, -1, tmp, sizeof(tmp)), arg); + break; + } + err = parse_expr (parse, s, 0, carg); + if (err) break; + nargs = rearrange_for_call(&carg); node->vargs = carg; - larg = carg; - } - else - { - larg->next = carg; - larg = carg; - } - x++; - err = parse_expr (parse, s, 0, carg); - if (err) break; - if (last == TRUE) break; - s = a+1; + } while (0); } - if (!err && x != macro->n_args) + if (!err && nargs != macro->n_args) { err = nerr_raise (NERR_PARSE, "%s Incorrect number of arguments, expected %d, got %d in call to macro %s: %s", @@ -3093,9 +3178,6 @@ { CS_FUNCTION *csf; - if (n_args != 1) - return nerr_raise(NERR_ASSERT, "Currently, only 1 argument functions are supported"); - /* Should we validate the parseability of the name? */ csf = parse->functions; @@ -3110,12 +3192,14 @@ } csf = (CS_FUNCTION *) calloc (1, sizeof(CS_FUNCTION)); if (csf == NULL) - return nerr_raise(NERR_NOMEM, "Unable to allocate memory to register function %s", funcname); + return nerr_raise(NERR_NOMEM, + "Unable to allocate memory to register function %s", funcname); csf->name = strdup(funcname); if (csf->name == NULL) { free(csf); - return nerr_raise(NERR_NOMEM, "Unable to allocate memory to register function %s", funcname); + return nerr_raise(NERR_NOMEM, + "Unable to allocate memory to register function %s", funcname); } csf->function = function; csf->n_args = n_args; @@ -3125,7 +3209,7 @@ return STATUS_OK; } -static NEOERR * _builtin_len(CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, CSARG *result) +static NEOERR * _builtin_subcount(CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, CSARG *result) { HDF *obj; int count = 0; @@ -3147,6 +3231,32 @@ } result->n = count; } + else + { + // everything else has zero children + result->n = 0; + } + + return STATUS_OK; +} + +static NEOERR * _builtin_str_length(CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, CSARG *result) +{ + HDF *obj; + + result->op_type = CS_TYPE_NUM; + result->n = 0; + + if (args->op_type & CS_TYPE_VAR) + { + obj = var_lookup_obj (parse, args->s); + if (obj) { + char *s = hdf_obj_value(obj); + if (s) { + result->n = strlen(s); + } + } + } else if (args->op_type & CS_TYPE_STRING) { result->n = strlen(args->s); @@ -3154,6 +3264,7 @@ return STATUS_OK; } + static NEOERR * _builtin_name(CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, CSARG *result) { HDF *obj; @@ -3182,6 +3293,147 @@ return STATUS_OK; } +/* This is similar to python's PyArg_ParseTuple, : + * s - string (allocated) + * i - int + * A - arg ptr (maybe later) + */ +static NEOERR * cs_arg_parsev(CSPARSE *parse, CSARG *args, char *fmt, + va_list ap) +{ + NEOERR *err = STATUS_OK; + char **s; + long int *i; + CSARG val; + + while (*fmt || args || err) + { + memset(&val, 0, sizeof(val)); + err = eval_expr(parse, args, &val); + if (err) return nerr_pass(err); + + switch (*fmt) + { + case 's': + s = va_arg(ap, char **); + if (s == NULL) + { + err = nerr_raise(NERR_ASSERT, + "Invalid number of arguments in call to cs_arg_parse"); + break; + } + *s = arg_eval_str_alloc(parse, &val); + break; + case 'i': + i = va_arg(ap, long int *); + if (i == NULL) + { + err = nerr_raise(NERR_ASSERT, + "Invalid number of arguments in call to cs_arg_parse"); + break; + } + *i = arg_eval_num(parse, &val); + break; + default: + break; + } + fmt++; + args = args->next; + if (val.alloc) free(val.s); + } + if (err) return nerr_pass(err); + return STATUS_OK; +} + +static NEOERR * cs_arg_parse(CSPARSE *parse, CSARG *args, char *fmt, ...) +{ + NEOERR *err; + va_list ap; + + va_start(ap, fmt); + err = cs_arg_parsev(parse, args, fmt, ap); + va_end(ap); + return nerr_pass(err); +} + +static NEOERR * _builtin_str_slice (CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, CSARG *result) +{ + NEOERR *err; + char *s = NULL; + char *slice; + int b = 0; + int e = 0; + int len; + + result->op_type = CS_TYPE_STRING; + result->s = ""; + + err = cs_arg_parse(parse, args, "sii", &s, &b, &e); + if (err) return nerr_pass(err); + /* If null, return empty string */ + if (s == NULL) return STATUS_OK; + len = strlen(s); + if (b < 0 && e == 0) e = len; + if (b < 0) b += len; + if (e < 0) e += len; + if (e > len) e = len; + /* Its the whole string */ + if (b == 0 && e == len) + { + result->s = s; + result->alloc = 1; + return STATUS_OK; + } + if (e < b) b = e; + if (b == e) + { + /* If null, return empty string */ + free(s); + return STATUS_OK; + } + slice = (char *) malloc (sizeof(char) * (e-b+1)); + if (slice == NULL) + return nerr_raise(NERR_NOMEM, "Unable to allocate memory for string slice"); + strncpy(slice, s + b, e-b); + free(s); + slice[e-b] = '\0'; + + result->s = slice; + result->alloc = 1; + + return STATUS_OK; +} + +#ifdef ENABLE_GETTEXT +static NEOERR * _builtin_gettext(CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, CSARG *result) +{ + HDF *obj; + + result->op_type = CS_TYPE_STRING; + result->s = ""; + + if (args->op_type & CS_TYPE_VAR) + { + obj = var_lookup_obj (parse, args->s); + if (obj != NULL) + { + result->s = gettext(hdf_obj_value(obj)); + } + else + { + result->s = ""; + } + } + else if (args->op_type & CS_TYPE_STRING) + { + result->s = gettext(args->s); + result->alloc = args->alloc; + args->alloc = 0; + } + return STATUS_OK; +} +#endif + static NEOERR * _str_func_wrapper (CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, CSARG *result) { NEOERR *err; @@ -3224,8 +3476,11 @@ /* **** CS Initialize/Destroy ************************************ */ +NEOERR *cs_init (CSPARSE **parse, HDF *hdf) { + return nerr_pass(cs_init_internal(parse, hdf, TRUE)); +} -NEOERR *cs_init (CSPARSE **parse, HDF *hdf) +static NEOERR *cs_init_internal (CSPARSE **parse, HDF *hdf, BOOL init_funcs) { NEOERR *err = STATUS_OK; CSPARSE *my_parse; @@ -3238,7 +3493,6 @@ if (my_parse == NULL) return nerr_raise (NERR_NOMEM, "Unable to allocate memory for CSPARSE"); - err = uListInit (&(my_parse->stack), 10, 0); if (err != STATUS_OK) { @@ -3276,18 +3530,49 @@ cs_destroy(&my_parse); return nerr_pass(err); } - err = _register_function(my_parse, "len", 1, _builtin_len); - if (err) + if (init_funcs) { - cs_destroy(&my_parse); - return nerr_pass(err); + err = _register_function(my_parse, "len", 1, _builtin_subcount); + if (err) + { + cs_destroy(&my_parse); + return nerr_pass(err); + } + err = _register_function(my_parse, "subcount", 1, _builtin_subcount); + if (err) + { + cs_destroy(&my_parse); + return nerr_pass(err); + } + err = _register_function(my_parse, "name", 1, _builtin_name); + if (err) + { + cs_destroy(&my_parse); + return nerr_pass(err); + } + err = _register_function(my_parse, "string.slice", 3, _builtin_str_slice); + if (err) + { + cs_destroy(&my_parse); + return nerr_pass(err); + } + err = _register_function(my_parse, "string.length", 1, _builtin_str_length); + if (err) + { + cs_destroy(&my_parse); + return nerr_pass(err); + } + + } - err = _register_function(my_parse, "name", 1, _builtin_name); +#ifdef ENABLE_GETTEXT + err = _register_function(my_parse, "_", 1, _builtin_gettext); if (err) { cs_destroy(&my_parse); return nerr_pass(err); } +#endif my_parse->tag = hdf_get_value(hdf, "Config.TagStart", "cs"); my_parse->taglen = strlen(my_parse->tag); my_parse->hdf = hdf; diff -Nru clearsilver-0.9.7/cs/test.hdf clearsilver-0.9.8/cs/test.hdf --- clearsilver-0.9.7/cs/test.hdf Wed Jun 18 11:43:52 2003 +++ clearsilver-0.9.8/cs/test.hdf Thu Feb 26 19:32:36 2004 @@ -3,6 +3,11 @@ var = +Numbers { + hdf9 = 9 + hdf14 = 14 +} + Blah = wow Foo = Worn Out diff -Nru clearsilver-0.9.7/cs/test15.cs clearsilver-0.9.8/cs/test15.cs --- clearsilver-0.9.7/cs/test15.cs Mon Jun 10 18:46:23 2002 +++ clearsilver-0.9.8/cs/test15.cs Thu Feb 26 19:55:30 2004 @@ -1,6 +1,10 @@ test functions - == 31 +string.length == 31 + +subcount == 4 + +len (depreciated) == 4 + - == 4 diff -Nru clearsilver-0.9.7/cs/test15.cs.gold clearsilver-0.9.8/cs/test15.cs.gold --- clearsilver-0.9.7/cs/test15.cs.gold Mon Jun 10 18:46:23 2002 +++ clearsilver-0.9.8/cs/test15.cs.gold Thu Feb 26 19:55:30 2004 @@ -2,6 +2,10 @@ test functions -31 == 31 +string.length 31 == 31 + +subcount 4 == 4 + +len (depreciated) 4 == 4 + -4 == 4 diff -Nru clearsilver-0.9.7/cs/test4.cs clearsilver-0.9.8/cs/test4.cs --- clearsilver-0.9.7/cs/test4.cs Sun Jul 27 09:26:19 2003 +++ clearsilver-0.9.8/cs/test4.cs Thu Feb 26 19:32:36 2004 @@ -72,13 +72,24 @@ #14 ?> ERROR! "9" > #14 -right "9" > #14 +right "9" < #14 + + + "14" ?> -ERROR! "9" > "14" +ERROR "9" > "14" (strings) + +right "9" < "14" (strings) + + + + + Numbers.hdf14 ?> +ERROR! hdf 9 > hdf 14 -right "9" > "14" +right hdf "9" < hdf "14" diff -Nru clearsilver-0.9.7/cs/test4.cs.gold clearsilver-0.9.8/cs/test4.cs.gold --- clearsilver-0.9.7/cs/test4.cs.gold Sun Jul 27 09:59:05 2003 +++ clearsilver-0.9.8/cs/test4.cs.gold Thu Feb 26 19:32:36 2004 @@ -59,11 +59,20 @@ -right "9" > #14 +right "9" < #14 -right "9" > "14" + + + +right "9" < "14" (strings) + + + + + +right hdf "9" < hdf "14" diff -Nru clearsilver-0.9.7/cs/test_splice.cs clearsilver-0.9.8/cs/test_splice.cs --- clearsilver-0.9.7/cs/test_splice.cs Wed Dec 31 16:00:00 1969 +++ clearsilver-0.9.8/cs/test_splice.cs Thu Apr 22 00:41:53 2004 @@ -0,0 +1,11 @@ + + + + + + + + +Check end of string slice: + + diff -Nru clearsilver-0.9.7/cs/test_splice.cs.gold clearsilver-0.9.8/cs/test_splice.cs.gold --- clearsilver-0.9.7/cs/test_splice.cs.gold Wed Dec 31 16:00:00 1969 +++ clearsilver-0.9.8/cs/test_splice.cs.gold Thu Apr 22 00:41:53 2004 @@ -0,0 +1,272 @@ +Parsing test_splice.cs + +Worn Out + + +Worn Out + +Worn Out + +Worn Out + +Worn Ou + +Worn O + +Worn + +Worn + +Wor + +Wo + +W + + + + + +orn Out + +orn Out + +orn Out + +orn Ou + +orn O + +orn + +orn + +or + +o + + + + + + + +rn Out + +rn Out + +rn Out + +rn Ou + +rn O + +rn + +rn + +r + + + + + + + + + +n Out + +n Out + +n Out + +n Ou + +n O + +n + +n + + + + + + + + + + + + Out + + Out + + Out + + Ou + + O + + + + + + + + + + + + + + + +Out + +Out + +Out + +Ou + +O + + + + + + + + + + + + + + + +ut + +ut + +ut + +u + + + + + + + + + + + + + + + + + +t + +t + +t + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Check end of string slice: +n Ou +n Out diff -Nru clearsilver-0.9.7/cs_config.h.in clearsilver-0.9.8/cs_config.h.in --- clearsilver-0.9.7/cs_config.h.in Mon Sep 22 20:49:34 2003 +++ clearsilver-0.9.8/cs_config.h.in Tue Mar 16 01:43:46 2004 @@ -102,6 +102,9 @@ /* Does your system have Berkeley DB v2 ? */ #undef HAVE_DB2 +/* Enable support for gettext message translation */ +#undef ENABLE_GETTEXT + /* Define if you have the drand48 function. */ #undef HAVE_DRAND48 diff -Nru clearsilver-0.9.7/perl/ClearSilver.xs clearsilver-0.9.8/perl/ClearSilver.xs --- clearsilver-0.9.7/perl/ClearSilver.xs Mon Apr 14 16:05:10 2003 +++ clearsilver-0.9.8/perl/ClearSilver.xs Tue Mar 16 00:44:19 2004 @@ -164,6 +164,20 @@ OUTPUT: RETVAL +int +perlhdf_writeFile(hdf, filename) + ClearSilver::HDF hdf + char* filename + CODE: + hdf->err = hdf_write_file(hdf->hdf, filename); + if (hdf->err == STATUS_OK) { + RETVAL = 1; + } else { + RETVAL = 0; + } + OUTPUT: + RETVAL + ClearSilver::HDF perlhdf_getObj(hdf, name) ClearSilver::HDF hdf; diff -Nru clearsilver-0.9.7/python/Makefile clearsilver-0.9.8/python/Makefile --- clearsilver-0.9.7/python/Makefile Thu Sep 11 19:02:24 2003 +++ clearsilver-0.9.8/python/Makefile Thu Feb 26 19:34:39 2004 @@ -24,6 +24,7 @@ all: $(TARGETS) $(NEO_UTIL_SO): setup.py $(NEO_UTIL_SRC) $(DEP_LIBS) + rm -f $(NEO_UTIL_SO) $(PYTHON) setup.py build_ext --inplace OLD_NEO_UTIL_SO: diff -Nru clearsilver-0.9.7/python/examples/base/CSPage.py clearsilver-0.9.8/python/examples/base/CSPage.py --- clearsilver-0.9.7/python/examples/base/CSPage.py Sun Aug 17 22:24:27 2003 +++ clearsilver-0.9.8/python/examples/base/CSPage.py Thu Mar 25 12:53:55 2004 @@ -23,24 +23,25 @@ self.environ = os.environ class CSPage: - def __init__(self, context, pagename=0,readDefaultHDF=1,israwpage=0): - if pagename == 0: - raise NoPageName, "missing pagename" - self.pagename = pagename - self.readDefaultHDF = readDefaultHDF + def __init__(self, context, pagename=0,readDefaultHDF=1,israwpage=0,**parms): + if pagename == 0: + raise NoPageName, "missing pagename" + self.pagename = pagename + self.readDefaultHDF = readDefaultHDF self._israwpage = israwpage self.context = context + self._pageparms = parms self._error_template = None self.page_start_time = time.time() - neo_cgi.cgiWrap(context.stdin, context.stdout, context.environ) + neo_cgi.cgiWrap(context.stdin, context.stdout, context.environ) neo_cgi.IgnoreEmptyFormVars(1) - self.ncgi = neo_cgi.CGI() + self.ncgi = neo_cgi.CGI() self.ncgi.parse() self._path_num = 0 - domain = self.ncgi.hdf.getValue("CGI.ServerName","") - domain = self.ncgi.hdf.getValue("HTTP.Host", domain) + domain = self.ncgi.hdf.getValue("CGI.ServerName","") + domain = self.ncgi.hdf.getValue("HTTP.Host", domain) self.domain = domain self.subclassinit() self.setPaths([self.ncgi.hdf.getValue("CGI.DocumentRoot","")]) @@ -97,13 +98,13 @@ apply(method,[]) def start(self): - SHOULD_DISPLAY = 1 + SHOULD_DISPLAY = 1 if self._israwpage: SHOULD_DISPLAY = 0 - ncgi = self.ncgi - - if self.readDefaultHDF: + ncgi = self.ncgi + + if self.readDefaultHDF: try: if not self.pagename is None: ncgi.hdf.readFile("%s.hdf" % self.pagename) @@ -160,15 +161,21 @@ if SHOULD_DISPLAY and self.pagename: debug_output = ncgi.hdf.getIntValue("page.debug",ncgi.hdf.getIntValue("Cookie.debug",0)) + # hijack the built-in debug output method... + if ncgi.hdf.getValue("Query.debug","") == ncgi.hdf.getValue("Config.DebugPassword","1"): + ncgi.hdf.setValue("Config.DebugPassword","CSPage.py DEBUG hijack (%s)" % + ncgi.hdf.getValue("Config.DebugPassword","")) + debug_output = 1 + if not debug_output: ncgi.hdf.setValue("Config.CompressionEnabled","1") - # default display - template_name = ncgi.hdf.getValue("Content","%s.cs" % self.pagename) + # default display + template_name = ncgi.hdf.getValue("Content","%s.cs" % self.pagename) # ncgi.hdf.setValue ("cgiout.charset", "utf-8"); try: - ncgi.display(template_name) + ncgi.display(template_name) except: print "Content-Type: text/html\n\n" print "CSPage: Error occured" @@ -177,15 +184,15 @@ debug_output = 1 - # debug output - if debug_output: - print "
\n" - print "Execution Time: %5.3f

" % (etime) - print "
"
-		print neo_cgi.htmlEscape(ncgi.hdf.dump())
-		print "
" - # ncgi.hdf.setValue("hdf.DEBUG",ncgi.hdf.dump()) - # ncgi.display("debug.cs") + # debug output + if debug_output: + print "
\n" + print "CSPage Debug, Execution Time: %5.3f

" % (etime) + print "
"
+                print neo_cgi.htmlEscape(ncgi.hdf.dump())
+                print "
" + # ncgi.hdf.setValue("hdf.DEBUG",ncgi.hdf.dump()) + # ncgi.display("debug.cs") script_name = ncgi.hdf.getValue("CGI.ScriptName","") if script_name: diff -Nru clearsilver-0.9.7/python/examples/base/PassSGMLParser.py clearsilver-0.9.8/python/examples/base/PassSGMLParser.py --- clearsilver-0.9.7/python/examples/base/PassSGMLParser.py Wed Dec 31 16:00:00 1969 +++ clearsilver-0.9.8/python/examples/base/PassSGMLParser.py Tue Mar 16 13:18:18 2004 @@ -0,0 +1,65 @@ +## +## deja-vu batman, didn't I write this before? +## This parser is designed to parse an SGML document, and the default action +## is just to pass the data through. Based on TestSGMLParser from sgmllib.py +## Hmm, actually, make it a flag whether to handle unknown elements +## + +from sgmllib import SGMLParser + +class PassSGMLParser(SGMLParser): + def __init__(self, fp, pass_unknown=0, verbose=0): + self.pass_unknown = pass_unknown + self.data = "" + self.fp = fp + SGMLParser.__init__(self, verbose) + + def handle_data(self, data): + self.data = self.data + data + + def flush(self): + data = self.data + if data: + self.data = "" + self.write(data) + + def write (self, data): + return self.fp.write(data) + + def write_starttag (self, tag, attrs): + self.flush() + if not attrs: + self.write ("<%s>" % tag) + else: + self.write ("<" + tag) + for name, value in attrs: + self.write (" " + name + '=' + '"' + value + '"') + self.write (">") + + def write_endtag (self, tag): + self.flush() + self.write ("" % tag) + + def handle_comment(self, data): + # don't pass comments + pass + + def unknown_starttag(self, tag, attrs): + if self.pass_unknown: + self.write_starttag (tag, attrs) + + def unknown_endtag(self, tag): + if self.pass_unknown: + self.write_endtag(tag) + + def handle_entityref(self, ref): + self.flush() + self.write ("&%s;" % ref) + + def handle_charref(self, ref): + self.flush() + self.write ("&#%s;" % ref) + + def close(self): + SGMLParser.close(self) + self.flush() diff -Nru clearsilver-0.9.7/python/examples/base/SafeHtml.py clearsilver-0.9.8/python/examples/base/SafeHtml.py --- clearsilver-0.9.7/python/examples/base/SafeHtml.py Wed Dec 31 16:00:00 1969 +++ clearsilver-0.9.8/python/examples/base/SafeHtml.py Tue Mar 16 13:18:18 2004 @@ -0,0 +1,144 @@ +## another case of deja-vu +## this time, we want the slashdot style (what Yahoo said to do) only allow +## certain tags... we'll make it an option +## we'll have to tie this in some way to our HTML body displayer... +## +## Ok, there are basically four types of tags: +## 1) safe - ie, , , etc. +## 2) render problems - - these we either strip, +## or we have to ensure they match +## 3) definitely evil independent tags that we always strip +## 4) definitely evil tags which denote a region, we strip the entire region + +from PassSGMLParser import PassSGMLParser +from urllib import basejoin +import string, sys +import neo_cgi + +try: + from cStringIO import StringIO +except: + from StringIO import StringIO + +class SafeHtml (PassSGMLParser): + _safeTags = {"P":1, "LI":1, "DD":1, "DT":1, "EM":1, "BR":1, "CITE":1, + "DFN":1, "Q":1, "STRONG":1, "IMG":1, "HR":1, + "TR":1, "TD":1, "TH":1, "CAPTION":1, "THEAD":1, "TFOOT":1, + "TBODY":1} + _matchTags = {"TABLE":1, "OL":1, "UL":1, "DL":1, "CENTER":1, "DIV":1, "PRE":1, + "SUB":1, "SUP":1, "BIG":1, "SMALL":1, "CODE":1, + "B":1, "I":1, "A":1, "TT":1, "BLOCKQUOTE":1, "U":1, + "H1":1, "H2":1, "H3":1, "H4":1, "H5":1, "H6":1, "FONT":1} + _skipTags = {"FORM":1, "HTML":1, "BODY":1, "EMBED":1, "AREA":1, "MAP":1, + "FRAME":1, "FRAMESET":1, "IFRAME":1, "META":1} + _stripTags = {"HEAD":1, "JAVA":1, "APPLET":1, "OBJECT":1, + "JAVASCRIPT":1, "LAYER":1, "STYLE":1, "SCRIPT":1} + + def __init__ (self, fp, extra_safe=1, base=None, map_urls=None, new_window=1): + self._extra_safe = extra_safe + PassSGMLParser.__init__ (self, fp, extra_safe) + self._matchDict = {} + self._stripping = 0 + self._base = base + self._map_urls = map_urls + self._new_window = new_window + + def safe_start_strip (self): + if self._stripping == 0: + self.flush() + self._stripping = self._stripping + 1 + + def safe_end_strip (self): + self.flush() + self._stripping = self._stripping - 1 + if self._stripping < 0: self._stripping = 0 + + def write (self, data): + # sys.stderr.write("write[%d] %s\n" % (self._stripping, data)) + if self._stripping == 0: + # sys.stderr.write("write %s\n" % data) + PassSGMLParser.write(self, data) + + def cleanup_attrs (self, tag, attrs): + new_attrs = [] + tag = string.lower(tag) + if self._new_window and tag == "a": + new_attrs.append(('target', '_blank')) + for name, value in attrs: + name = string.lower(name) + if name[:2] == "on": continue ## skip any javascript events + if string.lower(value)[:11] == "javascript:": continue + if self._map_urls and name in ["action", "href", "src", "lowsrc", "background"] and value[:4] == 'cid:': + try: + value = self._map_urls[value[4:]] + except KeyError: + pass + else: + if self._base and name in ["action", "href", "src", "lowsrc", "background"]: + value = basejoin (self._base, value) + if name in ["action", "href", "src", "lowsrc", "background"]: + value = 'http://www.google.com/url?sa=D&q=%s' % (neo_cgi.urlEscape(value)) + if self._new_window and tag == "a" and name == "target": continue + new_attrs.append ((name, value)) + return new_attrs + + def unknown_starttag(self, tag, attrs): + tag = string.upper(tag) + if SafeHtml._stripTags.has_key(tag): + self.safe_start_strip() + # sys.stderr.write("Stripping tag %s: %d\n" % (tag, self._stripping)) + elif SafeHtml._skipTags.has_key(tag): + # sys.stderr.write("Skipping tag %s\n" % tag) + pass + elif SafeHtml._matchTags.has_key(tag): + # sys.stderr.write("Matching tag %s\n" % tag) + if self._matchDict.has_key(tag): + self._matchDict[tag] = self._matchDict[tag] + 1 + else: + self._matchDict[tag] = 1 + self.write_starttag (tag, self.cleanup_attrs(tag, attrs)) + elif SafeHtml._safeTags.has_key(tag): + # sys.stderr.write("Safe tag %s\n" % tag) + self.write_starttag (tag, self.cleanup_attrs(tag, attrs)) + elif not self._extra_safe: + # sys.stderr.write("Other tag %s\n" % tag) + self.write_starttag (tag, self.cleanup_attrs(tag, attrs)) + + def unknown_endtag(self, tag): + tag = string.upper(tag) + if SafeHtml._stripTags.has_key(tag): + self.safe_end_strip() + # sys.stderr.write("End Stripping tag %s: %d\n" % (tag, self._stripping)) + elif SafeHtml._skipTags.has_key(tag): + pass + elif SafeHtml._matchTags.has_key(tag): + if self._matchDict.has_key(tag): + self._matchDict[tag] = self._matchDict[tag] - 1 + self.write_endtag (tag) + elif SafeHtml._safeTags.has_key(tag): + self.write_endtag (tag) + elif not self._extra_safe: + self.write_endtag (tag) + + def close (self): + self._stripping = 0 + for tag in self._matchDict.keys(): + if self._matchDict[tag] > 0: + for x in range (self._matchDict[tag]): + self.write_endtag(tag) + PassSGMLParser.close(self) + +def SafeHtmlString (s, really_safe=1, map_urls=None): +# fp = open("/tmp/safe_html.in", "w") +# fp.write(s) +# fp.close() + fp = StringIO() + parser = SafeHtml(fp, really_safe, map_urls=map_urls) + parser.feed (s) + parser.close () + s = fp.getvalue() +# fp = open("/tmp/safe_html.out", "w") +# fp.write(s) +# fp.close() + return s + diff -Nru clearsilver-0.9.7/python/examples/base/handle_error.py clearsilver-0.9.8/python/examples/base/handle_error.py --- clearsilver-0.9.7/python/examples/base/handle_error.py Sun Jun 15 18:54:49 2003 +++ clearsilver-0.9.8/python/examples/base/handle_error.py Thu Mar 25 12:53:55 2004 @@ -2,7 +2,8 @@ import traceback, sys, string, time, socket, os import who_calls -DUMP_DIR = "/neo/data/bugs" +#DUMP_DIR = "/neo/data/bugs" +DUMP_DIR = "/tmp/bugs" Warning = "handle_error.Warning" @@ -61,6 +62,15 @@ except: handleException("Unable to dump_bug", dump = 0) +def checkPaths(): + paths = (DUMP_DIR, + os.path.join (DUMP_DIR, "tmp"), + os.path.join (DUMP_DIR, "new")) + for path in paths: + if not os.path.isdir(path): + os.mkdir(path, 0755) + + def dump_bug (level, etype, msg, location=None, nhdf=None): global DISABLE_DUMP if DISABLE_DUMP: return @@ -84,6 +94,8 @@ global Count Count = Count + 1 fname = "%d.%d_%d.%s" % (now, pid, Count, socket.gethostname()) + checkPaths() + tpath = os.path.join (DUMP_DIR, "tmp", fname) npath = os.path.join (DUMP_DIR, "new", fname) hdf.writeFile(tpath) diff -Nru clearsilver-0.9.7/python/examples/base/hdfhelp.py clearsilver-0.9.8/python/examples/base/hdfhelp.py --- clearsilver-0.9.7/python/examples/base/hdfhelp.py Sat Sep 13 03:25:00 2003 +++ clearsilver-0.9.8/python/examples/base/hdfhelp.py Thu Mar 25 12:53:55 2004 @@ -155,6 +155,95 @@ except: return "Error in CS tags: %s" % neo_cgi.htmlEscape(repr(a_cs_string)) +def childloop(hdf): + children = [] + if hdf: + hdf = hdf.child() + while hdf: + children.append(hdf) + hdf = hdf.next() + return children + +# ---------------------------- + +class HDF_Database(odb.Database): + def defaultRowClass(self): + return HdfRow + def defaultRowListClass(self): + return HdfItemList + +# ---------------------------- + + +def loopHDF(hdf, name=None): + results = [] + if name: o = hdf.getObj(name) + else: o = hdf + if o: + o = o.child() + while o: + results.append(o) + o = o.next() + return results + + +def loopKVHDF(hdf, name=None): + results = [] + if name: o = hdf.getObj(name) + else: o = hdf + if o: + o = o.child() + while o: + results.append((o.name(), o.value())) + o = o.next() + return results + + +class hdf_iterator: + def __init__(self, hdf): + self.hdf = hdf + self.node = None + if self.hdf: + self.node = self.hdf.child() + + def __iter__(self): return self + + def next(self): + if not self.node: + raise StopIteration + + ret = self.node + self.node = self.node.next() + + return ret + +class hdf_kv_iterator(hdf_iterator): + def next(self): + if not self.node: raise StopIteration + + ret = (self.node.name(), self.node.value()) + self.node = self.node.next() + + return ret + +class hdf_key_iterator(hdf_iterator): + def next(self): + if not self.node: raise StopIteration + + ret = self.node.name() + self.node = self.node.next() + + return ret + +class hdf_ko_iterator(hdf_iterator): + def next(self): + if not self.node: raise StopIteration + + ret = (self.node.name(), self.node) + self.node = self.node.next() + + return ret + # ---------------------------- def test(): diff -Nru clearsilver-0.9.7/python/examples/base/log.py clearsilver-0.9.8/python/examples/base/log.py --- clearsilver-0.9.7/python/examples/base/log.py Sun Jun 15 23:14:49 2003 +++ clearsilver-0.9.8/python/examples/base/log.py Sun Mar 14 14:24:22 2004 @@ -2,7 +2,7 @@ # log.py -import sys, time +import sys, time, string DEV = "development" DEV_UPDATE = "update queries" @@ -37,7 +37,7 @@ else: sys.stderr.write("[%s] %s\n" % (time_stamp, astr)) -def log(astr): +def orig_log(astr): if len(astr) > 1024: astr = astr[:1024] @@ -49,3 +49,82 @@ else: sys.stderr.write("[%s] %s\n" % (time_stamp, astr)) # sys.stderr.flush() + + +#---------------------------------------------------------------------- +# static functions + +_gDebug = 0 +_gFileDebug = 0 + +kBLACK = 0 +kRED = 1 +kGREEN = 2 +kYELLOW = 3 +kBLUE = 4 +kMAGENTA = 5 +kCYAN = 6 +kWHITE = 7 +kBRIGHT = 8 + +def ansicolor (str, fgcolor = None, bgcolor = None): + o = "" + if fgcolor: + if fgcolor & kBRIGHT: + bright = ';1' + else: + bright = '' + o = o + '%c[3%d%sm' % (chr(27), fgcolor & 0x7, bright) + if bgcolor: + o = o + '%c[4%dm' % (chr(27), bgcolor) + o = o + str + if fgcolor or bgcolor: + o = o + '%c[0m' % (chr(27)) + return o + + +def _log(*args): + t = time.time() + + log_line = "" + + log_line = log_line + "[" + time.strftime("%m/%d %T", time.localtime(t)) + "] " + + l = [] + for arg in args: + l.append(str(arg)) + log_line = log_line + string.join(l, " ") + "\n" + + sys.stderr.write(log_line) + +def warn(*args): + apply(_log, args) + +def warnred(*args): + args = tuple ([""] + list(args) + [""]) + apply(_log, args) + +def log(*args): + apply(_log, args) + +def logred(*args): + if _gDebug>=1: + args = tuple ([""] + list(args) + [""]) + apply(_log, args) + +def debug(*args): + if _gDebug>=2: apply(_log, args) + + +def debugfull(): + global _gDebug + _gDebug = 2 + +def debugon(): + global _gDebug + _gDebug = 1 + +def debugoff(): + global _gDebug + _gDebug = 0 + diff -Nru clearsilver-0.9.7/python/examples/base/odb.py clearsilver-0.9.8/python/examples/base/odb.py --- clearsilver-0.9.7/python/examples/base/odb.py Thu Sep 11 03:09:47 2003 +++ clearsilver-0.9.8/python/examples/base/odb.py Thu Mar 25 12:53:55 2004 @@ -1,4 +1,4 @@ -#!/neo/opt/bin/python +#!/usr/bin/env python # # odb.py # @@ -18,7 +18,6 @@ # Example: # # import odb -# import MySQLdb # # # define table # class AgentsTable(odb.Table): @@ -51,12 +50,11 @@ # list_rows = tbl.fetchRows( ('login', "foo") ) # - import string import sys, zlib from log import * -import MySQLdb +import handle_error eNoSuchColumn = "odb.eNoSuchColumn" eNonUniqueMatchSpec = "odb.eNonUniqueMatchSpec" @@ -92,17 +90,16 @@ # class Database: - def __init__(self,db): + def __init__(self, db, debug=0): self._tables = {} self.db = db self._cursor = None self.compression_enabled = 0 + self.debug = debug + self.SQLError = None - # __init__ = None - # list_tables = None # list_tables() -> ['a_table','b_table'] - # list_fields = None # list_fields(tbl_name) -> [' - # checkTable = None # checkTable - # createTable + self.__defaultRowClass = self.defaultRowClass() + self.__defaultRowListClass = self.defaultRowListClass() def defaultCursor(self): if self._cursor is None: @@ -110,16 +107,26 @@ return self._cursor def escape(self,str): - return MySQLdb.escape_string(str) + raise "Unimplemented Error" + + def getDefaultRowClass(self): return self.__defaultRowClass + def setDefaultRowClass(self, clss): self.__defaultRowClass = clss + def getDefaultRowListClass(self): return self.__defaultRowListClass + def setDefaultRowListClass(self, clss): self.__defaultRowListClass = clss + def defaultRowClass(self): - return Row + return Row def defaultRowListClass(self): # base type is list... - return list + return list - def addTable(self, attrname, tblname, tblclass, rowClass = None, check = 0, create = 0, rowListClass = None): - self._tables[attrname] = tblclass(self, tblname, rowClass=rowClass, check=check, create=create, rowListClass=rowListClass) + def addTable(self, attrname, tblname, tblclass, + rowClass = None, check = 0, create = 0, rowListClass = None): + tbl = tblclass(self, tblname, rowClass=rowClass, check=check, + create=create, rowListClass=rowListClass) + self._tables[attrname] = tbl + return tbl def close(self): for name, tbl in self._tables.items(): @@ -157,6 +164,50 @@ dlog(DEV_UPDATE,"rollback") cursor.execute("rollback") + ## + ## schema creation code + ## + + def createTables(self): + tables = self.listTables() + + for attrname, tbl in self._tables.items(): + tblname = tbl.getTableName() + + if tblname not in tables: + print "table %s does not exist" % tblname + tbl.createTable() + else: + invalidAppCols, invalidDBCols = tbl.checkTable() + +## self.alterTableToMatch(tbl) + + def createIndices(self): + indices = self.listIndices() + + for attrname, tbl in self._tables.items(): + for indexName, (columns, unique) in tbl.getIndices().items(): + if indexName in indices: continue + + tbl.createIndex(columns, indexName=indexName, unique=unique) + + def synchronizeSchema(self): + tables = self.listTables() + + for attrname, tbl in self._tables.items(): + tblname = tbl.getTableName() + self.alterTableToMatch(tbl) + + def listTables(self, cursor=None): + raise "Unimplemented Error" + + def listFieldsDict(self, table_name, cursor=None): + raise "Unimplemented Error" + + def listFields(self, table_name, cursor=None): + columns = self.listFieldsDict(table_name, cursor=cursor) + return columns.keys() + ########################################## # Table # @@ -165,45 +216,187 @@ class Table: def subclassinit(self): pass - def __init__(self,database,table_name,rowClass = None, check = 0, create = 0, rowListClass = None): - self.db = database - self.__table_name = table_name - if rowClass: - self.__defaultRowClass = rowClass - else: - self.__defaultRowClass = database.defaultRowClass() - - if rowListClass: - self.__defaultRowListClass = rowListClass - else: - self.__defaultRowListClass = database.defaultRowListClass() - - # get this stuff ready! - - self.__column_list = [] - self.__vcolumn_list = [] - self.__columns_locked = 0 - self.__has_value_column = 0 - - # this will be used during init... - self.__col_def_hash = None - self.__vcol_def_hash = None - self.__primary_key_list = None + def __init__(self,database,table_name, + rowClass = None, check = 0, create = 0, rowListClass = None): + self.db = database + self.__table_name = table_name + if rowClass: + self.__defaultRowClass = rowClass + else: + self.__defaultRowClass = database.getDefaultRowClass() + + if rowListClass: + self.__defaultRowListClass = rowListClass + else: + self.__defaultRowListClass = database.getDefaultRowListClass() + + # get this stuff ready! + + self.__column_list = [] + self.__vcolumn_list = [] + self.__columns_locked = 0 + self.__has_value_column = 0 + + self.__indices = {} + + # this will be used during init... + self.__col_def_hash = None + self.__vcol_def_hash = None + self.__primary_key_list = None self.__relations_by_table = {} - # ask the subclass to def his rows - self._defineRows() + # ask the subclass to def his rows + self._defineRows() - # get ready to run! - self.__lockColumnsAndInit() + # get ready to run! + self.__lockColumnsAndInit() self.subclassinit() - if create: - self.db.createTable(self) + if create: + self.createTable() + + if check: + self.checkTable() - if check: - self.db.checkTable(self) + def _colTypeToSQLType(self, colname, coltype, options): + + if coltype == kInteger: + coltype = "integer" + elif coltype == kFixedString: + sz = options.get('size', None) + if sz is None: coltype = 'char' + else: coltype = "char(%s)" % sz + elif coltype == kVarString: + sz = options.get('size', None) + if sz is None: coltype = 'varchar' + else: coltype = "varchar(%s)" % sz + elif coltype == kBigString: + coltype = "text" + elif coltype == kIncInteger: + coltype = "integer" + elif coltype == kDateTime: + coltype = "datetime" + elif coltype == kTimeStamp: + coltype = "timestamp" + elif coltype == kReal: + coltype = "real" + + coldef = "%s %s" % (colname, coltype) + + if options.get('notnull', 0): coldef = coldef + " NOT NULL" + if options.get('autoincrement', 0): coldef = coldef + " AUTO_INCREMENT" + if options.get('unique', 0): coldef = coldef + " UNIQUE" +# if options.get('primarykey', 0): coldef = coldef + " primary key" + if options.get('default', None) is not None: coldef = coldef + " DEFAULT %s" % options.get('default') + + return coldef + + def getTableName(self): return self.__table_name + def setTableName(self, tablename): self.__table_name = tablename + + def getIndices(self): return self.__indices + + def _createTableSQL(self): + defs = [] + for colname, coltype, options in self.__column_list: + defs.append(self._colTypeToSQLType(colname, coltype, options)) + + defs = string.join(defs, ", ") + + primarykeys = self.getPrimaryKeyList() + primarykey_str = "" + if primarykeys: + primarykey_str = ", PRIMARY KEY (" + string.join(primarykeys, ",") + ")" + + sql = "create table %s (%s %s)" % (self.__table_name, defs, primarykey_str) + return sql + + def createTable(self, cursor=None): + if cursor is None: cursor = self.db.defaultCursor() + sql = self._createTableSQL() + print "CREATING TABLE:", sql + cursor.execute(sql) + + def dropTable(self, cursor=None): + if cursor is None: cursor = self.db.defaultCursor() + try: + cursor.execute("drop table %s" % self.__table_name) # clean out the table + except self.SQLError, reason: + pass + + def renameTable(self, newTableName, cursor=None): + if cursor is None: cursor = self.db.defaultCursor() + try: + cursor.execute("rename table %s to %s" % (self.__table_name, newTableName)) + except sel.SQLError, reason: + pass + + self.setTableName(newTableName) + + def getTableColumnsFromDB(self): + return self.db.listFieldsDict(self.__table_name) + + def checkTable(self, warnflag=1): + invalidDBCols = {} + invalidAppCols = {} + + dbcolumns = self.getTableColumnsFromDB() + for coldef in self.__column_list: + colname = coldef[0] + + dbcoldef = dbcolumns.get(colname, None) + if dbcoldef is None: + invalidAppCols[colname] = 1 + + for colname, row in dbcolumns.items(): + coldef = self.__col_def_hash.get(colname, None) + if coldef is None: + invalidDBCols[colname] = 1 + + if warnflag == 1: + if invalidDBCols: + print "----- WARNING ------------------------------------------" + print " There are columns defined in the database schema that do" + print " not match the application's schema." + print " columns:", invalidDBCols.keys() + print "--------------------------------------------------------" + + if invalidAppCols: + print "----- WARNING ------------------------------------------" + print " There are new columns defined in the application schema" + print " that do not match the database's schema." + print " columns:", invalidAppCols.keys() + print "--------------------------------------------------------" + + return invalidAppCols, invalidDBCols + + + def alterTableToMatch(self): + raise "Unimplemented Error!" + + def addIndex(self, columns, indexName=None, unique=0): + if indexName is None: + indexName = self.getTableName() + "_index_" + string.join(columns, "_") + + self.__indices[indexName] = (columns, unique) + + def createIndex(self, columns, indexName=None, unique=0, cursor=None): + if cursor is None: cursor = self.db.defaultCursor() + cols = string.join(columns, ",") + + if indexName is None: + indexName = self.getTableName() + "_index_" + string.join(columns, "_") + + uniquesql = "" + if unique: + uniquesql = " unique" + sql = "create %s index %s on %s (%s)" % (uniquesql, indexName, self.getTableName(), cols) + warn("creating index", sql) + cursor.execute(sql) + + + ## Column Definition def getColumnDef(self,column_name): try: @@ -214,17 +407,18 @@ except KeyError: raise eNoSuchColumn, "no column (%s) on table %s" % (column_name,self.__table_name) - def getColumnList(self): - return self.__column_list + self.__vcolumn_list + def getColumnList(self): + return self.__column_list + self.__vcolumn_list + def getAppColumnList(self): return self.__column_list def databaseSizeForData_ColumnName_(self,data,col_name): - try: - col_def = self.__col_def_hash[col_name] - except KeyError: - try: - col_def = self.__vcol_def_hash[col_name] - except KeyError: - raise eNoSuchColumn, "no column (%s) on table %s" % (col_name,self.__table_name) + try: + col_def = self.__col_def_hash[col_name] + except KeyError: + try: + col_def = self.__vcol_def_hash[col_name] + except KeyError: + raise eNoSuchColumn, "no column (%s) on table %s" % (col_name,self.__table_name) c_name,c_type,c_options = col_def @@ -248,37 +442,37 @@ def columnType(self, col_name): - try: - col_def = self.__col_def_hash[col_name] - except KeyError: - try: - col_def = self.__vcol_def_hash[col_name] - except KeyError: - raise eNoSuchColumn, "no column (%s) on table %s" % (col_name,self.__table_name) + try: + col_def = self.__col_def_hash[col_name] + except KeyError: + try: + col_def = self.__vcol_def_hash[col_name] + except KeyError: + raise eNoSuchColumn, "no column (%s) on table %s" % (col_name,self.__table_name) - c_name,c_type,c_options = col_def + c_name,c_type,c_options = col_def return c_type def convertDataForColumn(self,data,col_name): - try: - col_def = self.__col_def_hash[col_name] - except KeyError: - try: - col_def = self.__vcol_def_hash[col_name] - except KeyError: - raise eNoSuchColumn, "no column (%s) on table %s" % (col_name,self.__table_name) + try: + col_def = self.__col_def_hash[col_name] + except KeyError: + try: + col_def = self.__vcol_def_hash[col_name] + except KeyError: + raise eNoSuchColumn, "no column (%s) on table %s" % (col_name,self.__table_name) - c_name,c_type,c_options = col_def + c_name,c_type,c_options = col_def if c_type == kIncInteger: raise eInvalidData, "invalid operation for column (%s:%s) on table (%s)" % (col_name,c_type,self.__table_name) - if c_type == kInteger: - try: + if c_type == kInteger: + try: if data is None: data = 0 else: return long(data) - except (ValueError,TypeError): - raise eInvalidData, "invalid data (%s) for col (%s:%s) on table (%s)" % (repr(data),col_name,c_type,self.__table_name) + except (ValueError,TypeError): + raise eInvalidData, "invalid data (%s) for col (%s:%s) on table (%s)" % (repr(data),col_name,c_type,self.__table_name) elif c_type == kReal: try: if data is None: data = 0.0 @@ -287,63 +481,61 @@ raise eInvalidData, "invalid data (%s) for col (%s:%s) on table (%s)" % (repr(data), col_name,c_type,self.__table_name) else: - if type(data) == type(long(0)): - return "%d" % data - else: - return str(data) + if type(data) == type(long(0)): + return "%d" % data + else: + return str(data) def getPrimaryKeyList(self): - return self.__primary_key_list + return self.__primary_key_list - def getTableName(self): - return self.__table_name def hasValueColumn(self): - return self.__has_value_column + return self.__has_value_column def hasColumn(self,name): - return self.__col_def_hash.has_key(name) + return self.__col_def_hash.has_key(name) def hasVColumn(self,name): - return self.__vcol_def_hash.has_key(name) - + return self.__vcol_def_hash.has_key(name) + def _defineRows(self): - raise "can't instantiate base odb.Table type, make a subclass and override _defineRows()" + raise "can't instantiate base odb.Table type, make a subclass and override _defineRows()" def __lockColumnsAndInit(self): - # add a 'odb_value column' before we lockdown the table def - if self.__has_value_column: - self.d_addColumn("odb_value",kBigText,default='') - - self.__columns_locked = 1 - # walk column list and make lookup hashes, primary_key_list, etc.. - - primary_key_list = [] - col_def_hash = {} - for a_col in self.__column_list: - name,type,options = a_col - col_def_hash[name] = a_col - if options.has_key('primarykey'): - primary_key_list.append(name) - - self.__col_def_hash = col_def_hash - self.__primary_key_list = primary_key_list - - # setup the value columns! - - if (not self.__has_value_column) and (len(self.__vcolumn_list) > 0): - raise "can't define vcolumns on table without ValueColumn, call d_addValueColumn() in your _defineRows()" - - vcol_def_hash = {} - for a_col in self.__vcolumn_list: - name,type,size_data,options = a_col - vcol_def_hash[name] = a_col - - self.__vcol_def_hash = vcol_def_hash - - + # add a 'odb_value column' before we lockdown the table def + if self.__has_value_column: + self.d_addColumn("odb_value",kBigText,default='') + + self.__columns_locked = 1 + # walk column list and make lookup hashes, primary_key_list, etc.. + + primary_key_list = [] + col_def_hash = {} + for a_col in self.__column_list: + name,type,options = a_col + col_def_hash[name] = a_col + if options.has_key('primarykey'): + primary_key_list.append(name) + + self.__col_def_hash = col_def_hash + self.__primary_key_list = primary_key_list + + # setup the value columns! + + if (not self.__has_value_column) and (len(self.__vcolumn_list) > 0): + raise "can't define vcolumns on table without ValueColumn, call d_addValueColumn() in your _defineRows()" + + vcol_def_hash = {} + for a_col in self.__vcolumn_list: + name,type,size_data,options = a_col + vcol_def_hash[name] = a_col + + self.__vcol_def_hash = vcol_def_hash + + def __checkColumnLock(self): - if self.__columns_locked: - raise "can't change column definitions outside of subclass' _defineRows() method!" + if self.__columns_locked: + raise "can't change column definitions outside of subclass' _defineRows() method!" # table definition methods, these are only available while inside the # subclass's _defineRows method @@ -358,28 +550,32 @@ # self.d_addColumn("type",kInteger, # enum_values = { 0 : "alive", 1 : "dead" } - def d_addColumn(self,col_name,ctype,size=None,primarykey = 0, notnull = 0,indexed=0, - default=None,unique=0,autoincrement=0,safeupdate=0,enum_values = None, - relations=None,compress_ok=0,int_date=0,no_export=0): - - self.__checkColumnLock() - - options = {} - options['default'] = default - if primarykey: - options['primarykey'] = primarykey - if indexed: - options['indexed'] = indexed - if unique: - options['unique'] = unique - if safeupdate: - options['safeupdate'] = safeupdate - if autoincrement: - options['autoincrement'] = autoincrement - if notnull: - options['notnull'] = notnull - if size: - options['size'] = size + def d_addColumn(self,col_name,ctype,size=None,primarykey = 0, + notnull = 0,indexed=0, + default=None,unique=0,autoincrement=0,safeupdate=0, + enum_values = None, + no_export = 0, + relations=None,compress_ok=0,int_date=0): + + self.__checkColumnLock() + + options = {} + options['default'] = default + if primarykey: + options['primarykey'] = primarykey + if unique: + options['unique'] = unique + if indexed: + options['indexed'] = indexed + self.addIndex((col_name,)) + if safeupdate: + options['safeupdate'] = safeupdate + if autoincrement: + options['autoincrement'] = autoincrement + if notnull: + options['notnull'] = notnull + if size: + options['size'] = size if no_export: options['no_export'] = no_export if int_date: @@ -388,15 +584,15 @@ else: options['int_date'] = int_date - if enum_values: - options['enum_values'] = enum_values - inv_enum_values = {} - for k,v in enum_values.items(): - if inv_enum_values.has_key(v): - raise eInvalidData, "enum_values paramater must be a 1 to 1 mapping for Table(%s)" % self.__table_name - else: - inv_enum_values[v] = k - options['inv_enum_values'] = inv_enum_values + if enum_values: + options['enum_values'] = enum_values + inv_enum_values = {} + for k,v in enum_values.items(): + if inv_enum_values.has_key(v): + raise eInvalidData, "enum_values paramater must be a 1 to 1 mapping for Table(%s)" % self.__table_name + else: + inv_enum_values[v] = k + options['inv_enum_values'] = inv_enum_values if relations: options['relations'] = relations for a_relation in relations: @@ -409,27 +605,26 @@ options['compress_ok'] = 1 else: raise eInvalidData, "only kBigString fields can be compress_ok=1" - - self.__column_list.append( (col_name,ctype,options) ) - + + self.__column_list.append( (col_name,ctype,options) ) def d_addValueColumn(self): - self.__checkColumnLock() - self.__has_value_column = 1 + self.__checkColumnLock() + self.__has_value_column = 1 def d_addVColumn(self,col_name,type,size=None,default=None): - self.__checkColumnLock() + self.__checkColumnLock() - if (not self.__has_value_column): - raise "can't define VColumns on table without ValueColumn, call d_addValueColumn() first" + if (not self.__has_value_column): + raise "can't define VColumns on table without ValueColumn, call d_addValueColumn() first" - options = {} - if default: - options['default'] = default - if size: - options['size'] = size + options = {} + if default: + options['default'] = default + if size: + options['size'] = size - self.__vcolumn_list.append( (col_name,type,options) ) + self.__vcolumn_list.append( (col_name,type,options) ) ##################### # _checkColMatchSpec(col_match_spec,should_match_unique_row = 0) @@ -442,26 +637,26 @@ # def _fixColMatchSpec(self,col_match_spec, should_match_unique_row = 0): - if type(col_match_spec) == type([]): - if type(col_match_spec[0]) != type((0,)): - raise eInvalidMatchSpec, "invalid types in match spec, use [(,)..] or (,)" - elif type(col_match_spec) == type((0,)): - col_match_spec = [ col_match_spec ] + if type(col_match_spec) == type([]): + if type(col_match_spec[0]) != type((0,)): + raise eInvalidMatchSpec, "invalid types in match spec, use [(,)..] or (,)" + elif type(col_match_spec) == type((0,)): + col_match_spec = [ col_match_spec ] elif type(col_match_spec) == type(None): if should_match_unique_row: raise eNonUniqueMatchSpec, "can't use a non-unique match spec (%s) here" % col_match_spec else: return None - else: - raise eInvalidMatchSpec, "invalid types in match spec, use [(,)..] or (,)" + else: + raise eInvalidMatchSpec, "invalid types in match spec, use [(,)..] or (,)" - if should_match_unique_row: + if should_match_unique_row: unique_column_lists = [] # first the primary key list - my_primary_key_list = [] - for a_key in self.__primary_key_list: - my_primary_key_list.append(a_key) + my_primary_key_list = [] + for a_key in self.__primary_key_list: + my_primary_key_list.append(a_key) # then other unique keys for a_col in self.__column_list: @@ -471,19 +666,19 @@ unique_column_lists.append( ('primary_key', my_primary_key_list) ) - - new_col_match_spec = [] - for a_col in col_match_spec: - name,val = a_col - # newname = string.lower(name) - # what is this doing?? - jeske - newname = name - if not self.__col_def_hash.has_key(newname): - raise eNoSuchColumn, "no such column in match spec: '%s'" % newname + + new_col_match_spec = [] + for a_col in col_match_spec: + name,val = a_col + # newname = string.lower(name) + # what is this doing?? - jeske + newname = name + if not self.__col_def_hash.has_key(newname): + raise eNoSuchColumn, "no such column in match spec: '%s'" % newname - new_col_match_spec.append( (newname,val) ) + new_col_match_spec.append( (newname,val) ) - if should_match_unique_row: + if should_match_unique_row: for name,a_list in unique_column_lists: try: a_list.remove(newname) @@ -491,7 +686,7 @@ # it's okay if they specify too many columns! pass - if should_match_unique_row: + if should_match_unique_row: for name,a_list in unique_column_lists: if len(a_list) == 0: # we matched at least one unique colum spec! @@ -500,10 +695,10 @@ raise eNonUniqueMatchSpec, "can't use a non-unique match spec (%s) here" % col_match_spec - return new_col_match_spec + return new_col_match_spec def __buildWhereClause (self, col_match_spec,other_clauses = None): - sql_where_list = [] + sql_where_list = [] if not col_match_spec is None: for m_col in col_match_spec: @@ -538,8 +733,8 @@ def __fetchRows(self,col_match_spec,cursor = None, where = None, order_by = None, limit_to = None, skip_to = None, join = None): - if cursor is None: - cursor = self.db.defaultCursor() + if cursor is None: + cursor = self.db.defaultCursor() # build column list sql_columns = [] @@ -567,17 +762,17 @@ eInvalidJoinSpec, "can't find table %s in defined relations for %s" % (a_table,self.__table_name) # start buildling SQL - sql = "select %s from %s" % (string.join(sql_columns,","), + sql = "select %s from %s" % (string.join(sql_columns,","), self.__table_name) # add join clause if join_clauses: sql = sql + string.join(join_clauses," ") - - # add where clause elements + + # add where clause elements sql_where_list = self.__buildWhereClause (col_match_spec,where) - if sql_where_list: - sql = sql + " where %s" % (string.join(sql_where_list," and ")) + if sql_where_list: + sql = sql + " where %s" % (string.join(sql_where_list," and ")) # add order by clause if order_by: @@ -586,7 +781,11 @@ # add limit if not limit_to is None: if not skip_to is None: - sql = sql + " limit %s, %s" % (skip_to,limit_to) +# log("limit,skip = %s,%s" % (limit_to,skip_to)) + if self.db.db.__module__ == "sqlite.main": + sql = sql + " limit %s offset %s " % (limit_to,skip_to) + else: + sql = sql + " limit %s, %s" % (skip_to,limit_to) else: sql = sql + " limit %s" % limit_to else: @@ -594,23 +793,23 @@ raise eInvalidData, "can't specify skip_to without limit_to in MySQL" dlog(DEV_SELECT,sql) - cursor.execute(sql) + cursor.execute(sql) # create defaultRowListClass instance... return_rows = self.__defaultRowListClass() - - # should do fetchmany! - all_rows = cursor.fetchall() - for a_row in all_rows: - data_dict = {} + + # should do fetchmany! + all_rows = cursor.fetchall() + for a_row in all_rows: + data_dict = {} - col_num = 0 + col_num = 0 - # for a_col in cursor.description: - # (name,type_code,display_size,internal_size,precision,scale,null_ok) = a_col + # for a_col in cursor.description: + # (name,type_code,display_size,internal_size,precision,scale,null_ok) = a_col for name in sql_columns: - if self.__col_def_hash.has_key(name) or joined_cols_hash.has_key(name): - # only include declared columns! + if self.__col_def_hash.has_key(name) or joined_cols_hash.has_key(name): + # only include declared columns! if self.__col_def_hash.has_key(name): c_name,c_type,c_options = self.__col_def_hash[name] if c_type == kBigString and c_options.get("compress_ok",0) and a_row[col_num]: @@ -620,23 +819,30 @@ a_col_data = a_row[col_num] data_dict[name] = a_col_data + elif c_type == kInteger or c_type == kIncInteger: + value = a_row[col_num] + if not value is None: + data_dict[name] = int(value) + else: + data_dict[name] = None else: data_dict[name] = a_row[col_num] else: data_dict[name] = a_row[col_num] - col_num = col_num + 1 + col_num = col_num + 1 newrowobj = self.__defaultRowClass(self,data_dict,joined_cols = joined_cols) - return_rows.append(newrowobj) - - return return_rows + + + + return return_rows def __deleteRow(self,a_row,cursor = None): - if cursor is None: - cursor = self.db.defaultCursor() + if cursor is None: + cursor = self.db.defaultCursor() # build the where clause! match_spec = a_row.getPKMatchSpec() @@ -649,17 +855,17 @@ def __updateRowList(self,a_row_list,cursor = None): - if cursor is None: - cursor = self.db.defaultCursor() + if cursor is None: + cursor = self.db.defaultCursor() - for a_row in a_row_list: - update_list = a_row.changedList() + for a_row in a_row_list: + update_list = a_row.changedList() - # build the set list! - sql_set_list = [] - for a_change in update_list: - col_name,col_val,col_inc_val = a_change - c_name,c_type,c_options = self.__col_def_hash[col_name] + # build the set list! + sql_set_list = [] + for a_change in update_list: + col_name,col_val,col_inc_val = a_change + c_name,c_type,c_options = self.__col_def_hash[col_name] if c_type != kIncInteger and col_val is None: sql_set_list.append("%s = NULL" % c_name) @@ -682,14 +888,14 @@ else: sql_set_list.append("%s = '%s'" % (c_name, self.db.escape(col_val))) - # build the where clause! - match_spec = a_row.getPKMatchSpec() + # build the where clause! + match_spec = a_row.getPKMatchSpec() sql_where_list = self.__buildWhereClause (match_spec) - if sql_set_list: - sql = "update %s set %s where %s" % (self.__table_name, - string.join(sql_set_list,","), - string.join(sql_where_list," and ")) + if sql_set_list: + sql = "update %s set %s where %s" % (self.__table_name, + string.join(sql_set_list,","), + string.join(sql_where_list," and ")) dlog(DEV_UPDATE,sql) try: @@ -698,23 +904,23 @@ if string.find(str(reason), "Duplicate entry") != -1: raise eDuplicateKey, reason raise Exception, reason - a_row.markClean() + a_row.markClean() - def __insertRow(self,a_row_obj,cursor = None): - if cursor is None: - cursor = self.db.defaultCursor() + def __insertRow(self,a_row_obj,cursor = None,replace=0): + if cursor is None: + cursor = self.db.defaultCursor() - sql_col_list = [] - sql_data_list = [] - auto_increment_column_name = None + sql_col_list = [] + sql_data_list = [] + auto_increment_column_name = None - for a_col in self.__column_list: - name,type,options = a_col + for a_col in self.__column_list: + name,type,options = a_col - try: - data = a_row_obj[name] + try: + data = a_row_obj[name] - sql_col_list.append(name) + sql_col_list.append(name) if data is None: sql_data_list.append("NULL") else: @@ -731,35 +937,47 @@ else: sql_data_list.append("'%s'" % self.db.escape(data)) - except KeyError: - if options.has_key("autoincrement"): - if auto_increment_column_name: - raise eInternalError, "two autoincrement columns (%s,%s) in table (%s)" % (auto_increment_column_name, name,self.__table_name) - else: - auto_increment_column_name = name - - - sql = "insert into %s (%s) values (%s)" % (self.__table_name, - string.join(sql_col_list,","), - string.join(sql_data_list,",")) + except KeyError: + if options.has_key("autoincrement"): + if auto_increment_column_name: + raise eInternalError, "two autoincrement columns (%s,%s) in table (%s)" % (auto_increment_column_name, name,self.__table_name) + else: + auto_increment_column_name = name + + if replace: + sql = "replace into %s (%s) values (%s)" % (self.__table_name, + string.join(sql_col_list,","), + string.join(sql_data_list,",")) + else: + sql = "insert into %s (%s) values (%s)" % (self.__table_name, + string.join(sql_col_list,","), + string.join(sql_data_list,",")) dlog(DEV_UPDATE,sql) try: cursor.execute(sql) except Exception, reason: + # sys.stderr.write("errror in statement: " + sql + "\n") + log("error in statement: " + sql + "\n") if string.find(str(reason), "Duplicate entry") != -1: raise eDuplicateKey, reason raise Exception, reason - if auto_increment_column_name: - a_row_obj[auto_increment_column_name] = cursor.insert_id() + if auto_increment_column_name: + if cursor.__module__ == "sqlite.main": + a_row_obj[auto_increment_column_name] = cursor.lastrowid + elif cursor.__module__ == "MySQLdb.cursors": + a_row_obj[auto_increment_column_name] = cursor.insert_id() + else: + # fallback to acting like mysql + a_row_obj[auto_increment_column_name] = cursor.insert_id() # ---------------------------------------------------- # Helper methods for Rows... # ---------------------------------------------------- - + ##################### # r_deleteRow(a_row_obj,cursor = None) # @@ -768,8 +986,8 @@ # def r_deleteRow(self,a_row_obj, cursor = None): - curs = cursor - self.__deleteRow(a_row_obj, cursor = curs) + curs = cursor + self.__deleteRow(a_row_obj, cursor = curs) ##################### @@ -780,8 +998,8 @@ # def r_updateRow(self,a_row_obj, cursor = None): - curs = cursor - self.__updateRowList([a_row_obj], cursor = curs) + curs = cursor + self.__updateRowList([a_row_obj], cursor = curs) ##################### # InsertRow(a_row_obj,cursor = None) @@ -790,9 +1008,9 @@ # but you can call it yourself if you want # - def r_insertRow(self,a_row_obj, cursor = None): - curs = cursor - self.__insertRow(a_row_obj, cursor = curs) + def r_insertRow(self,a_row_obj, cursor = None,replace=0): + curs = cursor + self.__insertRow(a_row_obj, cursor = curs,replace=replace) # ---------------------------------------------------- @@ -800,7 +1018,7 @@ # ---------------------------------------------------- - + ##################### # deleteRow(col_match_spec) # @@ -824,7 +1042,7 @@ dlog(DEV_UPDATE,sql) cursor.execute(sql) - + ##################### # fetchRow(col_match_spec) # @@ -836,17 +1054,17 @@ def fetchRow(self, col_match_spec, cursor = None): - n_match_spec = self._fixColMatchSpec(col_match_spec, should_match_unique_row = 1) + n_match_spec = self._fixColMatchSpec(col_match_spec, should_match_unique_row = 1) - rows = self.__fetchRows(n_match_spec, cursor = cursor) - if len(rows) == 0: - raise eNoMatchingRows, "no row matches %s" % repr(n_match_spec) + rows = self.__fetchRows(n_match_spec, cursor = cursor) + if len(rows) == 0: + raise eNoMatchingRows, "no row matches %s" % repr(n_match_spec) - if len(rows) > 1: - raise eInternalError, "unique where clause shouldn't return > 1 row" + if len(rows) > 1: + raise eInternalError, "unique where clause shouldn't return > 1 row" - return rows[0] - + return rows[0] + ##################### # fetchRows(col_match_spec) @@ -856,10 +1074,12 @@ # a_row_list = tbl.fetchRows( [ ("order_id", 1), ("enterTime", now) ] ) - def fetchRows(self, col_match_spec = None, cursor = None, where = None, order_by = None, limit_to = None, skip_to = None, join = None): - n_match_spec = self._fixColMatchSpec(col_match_spec) + def fetchRows(self, col_match_spec = None, cursor = None, + where = None, order_by = None, limit_to = None, + skip_to = None, join = None): + n_match_spec = self._fixColMatchSpec(col_match_spec) - return self.__fetchRows(n_match_spec, + return self.__fetchRows(n_match_spec, cursor = cursor, where = where, order_by = order_by, @@ -867,21 +1087,19 @@ skip_to = skip_to, join = join) - def fetchRowCount (self, col_match_spec = None, cursor = None, where = None): - n_match_spec = self._fixColMatchSpec(col_match_spec) - + def fetchRowCount (self, col_match_spec = None, + cursor = None, where = None): + n_match_spec = self._fixColMatchSpec(col_match_spec) sql_where_list = self.__buildWhereClause (n_match_spec,where) - - sql = "select count(*) from %s" % self.__table_name - if sql_where_list: - sql = "%s where %s" % (sql,string.join(sql_where_list," and ")) - + sql = "select count(*) from %s" % self.__table_name + if sql_where_list: + sql = "%s where %s" % (sql,string.join(sql_where_list," and ")) if cursor is None: cursor = self.db.defaultCursor() dlog(DEV_SELECT,sql) - cursor.execute(sql) + cursor.execute(sql) try: - count, = cursor.fetchone() + count, = cursor.fetchone() except TypeError: count = 0 return count @@ -901,8 +1119,8 @@ # else return empty list... return self.__defaultRowListClass() - def newRow(self): - row = self.__defaultRowClass(self,None,create=1) + def newRow(self,replace=0): + row = self.__defaultRowClass(self,None,create=1,replace=replace) for (cname, ctype, opts) in self.__column_list: if opts['default'] is not None and ctype is not kIncInteger: row[cname] = opts['default'] @@ -912,76 +1130,77 @@ __instance_data_locked = 0 def subclassinit(self): pass - def __init__(self,_table,data_dict,create=0,joined_cols = None): + def __init__(self,_table,data_dict,create=0,joined_cols = None,replace=0): self._inside_getattr = 0 # stop recursive __getattr__ - self._table = _table - self._should_insert = create + self._table = _table + self._should_insert = create or replace + self._should_replace = replace self._rowInactive = None self._joinedRows = [] - - self.__pk_match_spec = None - self.__vcoldata = {} + + self.__pk_match_spec = None + self.__vcoldata = {} self.__inc_coldata = {} self.__joined_cols_dict = {} for a_col in joined_cols or []: self.__joined_cols_dict[a_col] = 1 - - if create: - self.__coldata = {} - else: - if type(data_dict) != type({}): - raise eInternalError, "rowdict instantiate with bad data_dict" - self.__coldata = data_dict - self.__unpackVColumn() + + if create: + self.__coldata = {} + else: + if type(data_dict) != type({}): + raise eInternalError, "rowdict instantiate with bad data_dict" + self.__coldata = data_dict + self.__unpackVColumn() - self.markClean() + self.markClean() self.subclassinit() - self.__instance_data_locked = 1 + self.__instance_data_locked = 1 def joinRowData(self,another_row): self._joinedRows.append(another_row) def getPKMatchSpec(self): - return self.__pk_match_spec + return self.__pk_match_spec def markClean(self): - self.__vcolchanged = 0 - self.__colchanged_dict = {} + self.__vcolchanged = 0 + self.__colchanged_dict = {} for key in self.__inc_coldata.keys(): - self.__coldata[key] = self.__coldata.get(key, 0) + self.__inc_coldata[key] + self.__coldata[key] = self.__coldata.get(key, 0) + self.__inc_coldata[key] self.__inc_coldata = {} - if not self._should_insert: - # rebuild primary column match spec - new_match_spec = [] - for col_name in self._table.getPrimaryKeyList(): - try: - rdata = self[col_name] - except KeyError: - raise eInternalError, "must have primary key data filled in to save %s:Row(col:%s)" % (self._table.getTableName(),col_name) - - new_match_spec.append( (col_name, rdata) ) - self.__pk_match_spec = new_match_spec + if not self._should_insert: + # rebuild primary column match spec + new_match_spec = [] + for col_name in self._table.getPrimaryKeyList(): + try: + rdata = self[col_name] + except KeyError: + raise eInternalError, "must have primary key data filled in to save %s:Row(col:%s)" % (self._table.getTableName(),col_name) + + new_match_spec.append( (col_name, rdata) ) + self.__pk_match_spec = new_match_spec def __unpackVColumn(self): - if self._table.hasValueColumn(): - pass - + if self._table.hasValueColumn(): + pass + def __packVColumn(self): - if self._table.hasValueColumn(): - pass + if self._table.hasValueColumn(): + pass ## ----- utility stuff ---------------------------------- def __del__(self): - # check for unsaved changes - changed_list = self.changedList() - if len(changed_list): + # check for unsaved changes + changed_list = self.changedList() + if len(changed_list): info = "unsaved Row for table (%s) lost, call discard() to avoid this error. Lost changes: %s\n" % (self._table.getTableName(), repr(changed_list)[:256]) if 0: raise eUnsavedObjectLost, info @@ -990,7 +1209,7 @@ def __repr__(self): - return "Row from (%s): %s" % (self._table.getTableName(),repr(self.__coldata) + repr(self.__vcoldata)) + return "Row from (%s): %s" % (self._table.getTableName(),repr(self.__coldata) + repr(self.__vcoldata)) ## ---- class emulation -------------------------------- @@ -1010,18 +1229,18 @@ self._inside_getattr = 0 def __setattr__(self,key,val): - if not self.__instance_data_locked: - self.__dict__[key] = val - else: - my_dict = self.__dict__ - if my_dict.has_key(key): - my_dict[key] = val - else: - # try and put it into the rowdata - try: - self[key] = val - except KeyError, reason: - raise AttributeError, reason + if not self.__instance_data_locked: + self.__dict__[key] = val + else: + my_dict = self.__dict__ + if my_dict.has_key(key): + my_dict[key] = val + else: + # try and put it into the rowdata + try: + self[key] = val + except KeyError, reason: + raise AttributeError, reason ## ---- dict emulation --------------------------------- @@ -1031,7 +1250,7 @@ try: c_type = self._table.columnType(key) - except eNoSuchColumn: + except eNoSuchColumn, reason: # Ugh, this sucks, we can't determine the type for a joined # row, so we just default to kVarString and let the code below # determine if this is a joined column or not @@ -1044,9 +1263,9 @@ if i_data is None: i_data = 0 return c_data + i_data - try: - return self.__coldata[key] - except KeyError: + try: + return self.__coldata[key] + except KeyError: try: return self.__vcoldata[key] except KeyError: @@ -1061,39 +1280,39 @@ def __setitem__(self,key,data): self.checkRowActive() - try: - newdata = self._table.convertDataForColumn(data,key) - except eNoSuchColumn, reason: - raise KeyError, reason - - if self._table.hasColumn(key): - self.__coldata[key] = newdata - self.__colchanged_dict[key] = 1 - elif self._table.hasVColumn(key): - self.__vcoldata[key] = newdata - self.__vcolchanged = 1 - else: + try: + newdata = self._table.convertDataForColumn(data,key) + except eNoSuchColumn, reason: + raise KeyError, reason + + if self._table.hasColumn(key): + self.__coldata[key] = newdata + self.__colchanged_dict[key] = 1 + elif self._table.hasVColumn(key): + self.__vcoldata[key] = newdata + self.__vcolchanged = 1 + else: for a_joined_row in self._joinedRows: try: a_joined_row[key] = data return except KeyError: pass - raise KeyError, "unknown column name %s" % key + raise KeyError, "unknown column name %s" % key def __delitem__(self,key,data): self.checkRowActive() - if self.table.hasVColumn(key): - del self.__vcoldata[key] - else: + if self.table.hasVColumn(key): + del self.__vcoldata[key] + else: for a_joined_row in self._joinedRows: try: del a_joined_row[key] return except KeyError: pass - raise KeyError, "unknown column name %s" % key + raise KeyError, "unknown column name %s" % key def copyFrom(self,source): @@ -1148,7 +1367,7 @@ def __len__(self): self.checkRowActive() - my_len = len(self.__coldata) + len(self.__vcoldata) + my_len = len(self.__coldata) + len(self.__vcoldata) for a_joined_row in self._joinedRows: my_len = my_len + len(a_joined_row) @@ -1158,25 +1377,25 @@ def has_key(self,key): self.checkRowActive() - if self.__coldata.has_key(key) or self.__vcoldata.has_key(key): - return 1 - else: + if self.__coldata.has_key(key) or self.__vcoldata.has_key(key): + return 1 + else: for a_joined_row in self._joinedRows: if a_joined_row.has_key(key): return 1 - return 0 - + return 0 + def get(self,key,default = None): self.checkRowActive() - if self.__coldata.has_key(key): - return self.__coldata[key] - elif self.__vcoldata.has_key(key): - return self.__vcoldata[key] - else: + if self.__coldata.has_key(key): + return self.__coldata[key] + elif self.__vcoldata.has_key(key): + return self.__vcoldata[key] + else: for a_joined_row in self._joinedRows: try: return a_joined_row.get(key,default) @@ -1186,7 +1405,7 @@ if self._table.hasColumn(key): return default - raise eNoSuchColumn, "no such column %s" % key + raise eNoSuchColumn, "no such column %s" % key def inc(self,key,count=1): self.checkRowActive() @@ -1207,10 +1426,10 @@ def fillDefaults(self): - for field_def in self._table.fieldList(): - name,type,size,options = field_def - if options.has_key("default"): - self[name] = options["default"] + for field_def in self._table.fieldList(): + name,type,size,options = field_def + if options.has_key("default"): + self[name] = options["default"] ############### # changedList() @@ -1220,20 +1439,20 @@ # changedList() -> [ ('name', 'fred'), ('age', 20) ] def changedList(self): - if self.__vcolchanged: - self.__packVColumn() + if self.__vcolchanged: + self.__packVColumn() - changed_list = [] - for a_col in self.__colchanged_dict.keys(): - changed_list.append( (a_col,self.get(a_col,None),self.__inc_coldata.get(a_col,None)) ) + changed_list = [] + for a_col in self.__colchanged_dict.keys(): + changed_list.append( (a_col,self.get(a_col,None),self.__inc_coldata.get(a_col,None)) ) - return changed_list + return changed_list def discard(self): - self.__coldata = None - self.__vcoldata = None - self.__colchanged_dict = {} - self.__vcolchanged = 0 + self.__coldata = None + self.__vcoldata = None + self.__colchanged_dict = {} + self.__vcolchanged = 0 def delete(self,cursor = None): self.checkRowActive() @@ -1245,20 +1464,21 @@ self._rowInactive = "deleted" def save(self,cursor = None): - toTable = self._table + toTable = self._table self.checkRowActive() - if self._should_insert: - toTable.r_insertRow(self) - self._should_insert = 0 - self.markClean() # rebuild the primary key list - else: + if self._should_insert: + toTable.r_insertRow(self,replace=self._should_replace) + self._should_insert = 0 + self._should_replace = 0 + self.markClean() # rebuild the primary key list + else: curs = cursor - toTable.r_updateRow(self,cursor = curs) + toTable.r_updateRow(self,cursor = curs) - # the table will mark us clean! - # self.markClean() + # the table will mark us clean! + # self.markClean() def checkRowActive(self): if self._rowInactive: @@ -1267,229 +1487,6 @@ def databaseSizeForColumn(self,key): return self._table.databaseSizeForData_ColumnName_(self[key],key) -## ----------------------------------------------------------------------- -## T E S T S -## ----------------------------------------------------------------------- - - -def TEST(output=log): - LOGGING_STATUS[DEV_SELECT] = 1 - LOGGING_STATUS[DEV_UPDATE] = 1 - class AgentsTable(Table): - def _defineRows(self): - self.d_addColumn("agent_id",kInteger,None,primarykey = 1,autoincrement = 1) - self.d_addColumn("login",kVarString,200,notnull=1) - self.d_addColumn("ext_email",kVarString,200,notnull=1) - self.d_addColumn("hashed_pw",kVarString,20,notnull=1) - self.d_addColumn("name",kBigString,compress_ok=1) - self.d_addColumn("auth_level",kInteger,None) - self.d_addColumn("ticket_count",kIncInteger,None) - - - import MySQLdb - rdb = MySQLdb.connect(host = 'localhost',user='root', passwd = '', db='testdb') - ndb = MySQLdb.connect(host = 'localhost',user='trakken', passwd = 'trakpas', db='testdb') - db = Database(ndb) - - tbl = AgentsTable(db,"agents") - - cursor = rdb.cursor() - - # --------------------------------------------------------------- - # initialize - output("drop table agents") - try: - cursor.execute("drop table agents") # clean out the table - except MySQLdb.OperationalError: - pass - output("creating table") - cursor.execute("create table agents (agent_id integer not null primary key auto_increment, login varchar(200) not null, unique (login), ext_email varchar(200) not null, hashed_pw varchar(20) not null, name varchar(200), auth_level integer default 0, ticket_count integer default 0)") - - TEST_INSERT_COUNT = 5 - - # --------------------------------------------------------------- - # make sure we can catch a missing row - - try: - a_row = tbl.fetchRow( ("agent_id", 1000) ) - raise "test error" - except eNoMatchingRows: - pass - - output("PASSED! fetch missing row test") - - # -------------------------------------------------------------- - # create new rows and insert them - - for n in range(TEST_INSERT_COUNT): - new_id = n + 1 - - newrow = tbl.newRow() - newrow.name = "name #%d" % new_id - newrow.login = "name%d" % new_id - newrow.ext_email = "%d@name" % new_id - newrow.save() - if newrow.agent_id != new_id: - raise "new insert id (%s) does not match expected value (%d)" % (newrow.agent_id,new_id) - - output("PASSED! autoinsert test") - - # -------------------------------------------------------------- - # fetch one row - a_row = tbl.fetchRow( ("agent_id", 1) ) - - if a_row.name != "name #1": - raise "row data incorrect" - - output("PASSED! fetch one row test") - - # --------------------------------------------------------------- - # don't change and save it - # (i.e. the "dummy cursor" string should never be called!) - # - try: - a_row.save(cursor = "dummy cursor") - except AttributeError, reason: - raise "row tried to access cursor on save() when no changes were made!" - - output("PASSED! don't save when there are no changed") - - # --------------------------------------------------------------- - # change, save, load, test - - a_row.auth_level = 10 - a_row.save() - b_row = tbl.fetchRow( ("agent_id", 1) ) - if b_row.auth_level != 10: - raise "save and load failed" - - - output("PASSED! change, save, load") - - # -------------------------------------------------------------- - # access unknown attribute - try: - a = a_row.UNKNOWN_ATTRIBUTE - raise "test error" - except AttributeError, reason: - pass - - try: - a_row.UNKNOWN_ATTRIBUTE = 1 - raise "test error" - except AttributeError, reason: - pass - - output("PASSED! unknown attribute exception") - - # -------------------------------------------------------------- - # access unknown dict item - - try: - a = a_row["UNKNOWN_ATTRIBUTE"] - raise "test error" - except KeyError, reason: - pass - - try: - a_row["UNKNOWN_ATTRIBUTE"] = 1 - raise "test error" - except KeyError, reason: - pass - - output("PASSED! unknown dict item exception") - - # -------------------------------------------------------------- - # use wrong data for column type - - try: - a_row.agent_id = "this is a string" - raise "test error" - except eInvalidData, reason: - pass - - output("PASSED! invalid data for column type") - - # -------------------------------------------------------------- - # fetch 1 rows - - rows = tbl.fetchRows( ('agent_id', 1) ) - if len(rows) != 1: - raise "fetchRows() did not return 1 row!" % (TEST_INSERT_COUNT) - - output("PASSED! fetch one row") - - - # -------------------------------------------------------------- - # fetch All rows - - rows = tbl.fetchAllRows() - if len(rows) != TEST_INSERT_COUNT: - for a_row in rows: - output(repr(a_row)) - raise "fetchAllRows() did not return TEST_INSERT_COUNT(%d) rows!" % (TEST_INSERT_COUNT) - - output("PASSED! fetchall rows") - - - # -------------------------------------------------------------- - # delete row object - - row = tbl.fetchRow( ('agent_id', 1) ) - row.delete() - try: - row = tbl.fetchRow( ('agent_id', 1) ) - raise "delete failed to delete row!" - except eNoMatchingRows: - pass - - # -------------------------------------------------------------- - # table deleteRow() call - - row = tbl.fetchRow( ('agent_id',2) ) - tbl.deleteRow( ('agent_id', 2) ) - try: - row = tbl.fetchRow( ('agent_id',2) ) - raise "table delete failed" - except eNoMatchingRows: - pass - - # -------------------------------------------------------------- - # table deleteRow() call - - row = tbl.fetchRow( ('agent_id',3) ) - if row.databaseSizeForColumn('name') != len(row.name): - raise "databaseSizeForColumn('name') failed" - - # -------------------------------------------------------------- - # test inc fields - row = tbl.newRow() - new_id = 1092 - row.name = "name #%d" % new_id - row.login = "name%d" % new_id - row.ext_email = "%d@name" % new_id - row.inc('ticket_count') - row.save() - new_id = row.agent_id - - trow = tbl.fetchRow( ('agent_id',new_id) ) - if trow.ticket_count != 1: - raise "ticket_count didn't inc!" - - row.inc('ticket_count', count=2) - row.save() - trow = tbl.fetchRow( ('agent_id',new_id) ) - if trow.ticket_count != 3: - raise "ticket_count wrong, expected 3, got %d" % trow.ticket_count - - trow.inc('ticket_count') - trow.save() - if trow.ticket_count != 4: - raise "ticket_count wrong, expected 4, got %d" % trow.ticket_count - - output("\n==== ALL TESTS PASSED ====") - if __name__ == "__main__": - TEST() - + print "run odb_test.py" diff -Nru clearsilver-0.9.7/python/examples/base/odb_mysql.py clearsilver-0.9.8/python/examples/base/odb_mysql.py --- clearsilver-0.9.7/python/examples/base/odb_mysql.py Wed Dec 31 16:00:00 1969 +++ clearsilver-0.9.8/python/examples/base/odb_mysql.py Thu Mar 25 12:53:55 2004 @@ -0,0 +1,72 @@ +#! /usr/bin/env python + +""" +usage: %(progname)s [args] +""" + + +# import startscript; startscript.init(__name__) +import os, sys, string, time, getopt +from log import * + +import odb +import MySQLdb + +class Database(odb.Database): + def __init__(self,db, debug=0): + odb.Database.__init__(self, db, debug=debug) + self.SQLError = MySQLdb.Error + + def escape(self,str): + if str is None: return None + return MySQLdb.escape_string(str) + + def listTables(self, cursor=None): + if cursor is None: cursor = self.defaultCursor() + cursor.execute("show tables") + rows = cursor.fetchall() + tables = [] + for row in rows: + tables.append(row[0]) + return tables + + def listIndices(self, cursor=None): + return [] + + def listFieldsDict(self, table_name, cursor=None): + if cursor is None: cursor = self.defaultCursor() + sql = "show columns from %s" % table_name + cursor.execute(sql) + rows = cursor.fetchall() + + columns = {} + for row in rows: + colname = row[0] + columns[colname] = row + + return columns + + def alterTableToMatch(self): + invalidAppCols, invalidDBCols = self.checkTable() + if not invalidAppCols: return + + defs = [] + for colname in invalidAppCols.keys(): + col = self.getColumnDef(colname) + colname = col[0] + coltype = col[1] + options = col[2] + defs.append(self.colTypeToSQLType(colname, coltype, options)) + + defs = string.join(defs, ", ") + + sql = "alter table %s add column " % self.getTableName() + sql = sql + "(" + defs + ")" + + print sql + + cur = self.db.defaultCursor() + cur.execute(sql) + + + Binary files clearsilver-0.9.7/python/examples/base/odb_mysql.pyc and clearsilver-0.9.8/python/examples/base/odb_mysql.pyc differ diff -Nru clearsilver-0.9.7/python/examples/base/odb_sqlite.py clearsilver-0.9.8/python/examples/base/odb_sqlite.py --- clearsilver-0.9.7/python/examples/base/odb_sqlite.py Wed Dec 31 16:00:00 1969 +++ clearsilver-0.9.8/python/examples/base/odb_sqlite.py Thu Mar 25 12:53:55 2004 @@ -0,0 +1,171 @@ +#! /usr/bin/env python + +""" +usage: %(progname)s [args] +""" + + +import os, sys, string, time, getopt +from log import * + +import odb +import sqlite + +import re + +# --- these are using for removing nulls from strings +# --- because sqlite can't handle them + +def escape_string(str): + def subfn(m): + c = m.group(0) + return "%%%02X" % ord(c) + + return re.sub("('|\0|%)",subfn,str) + +def unescape_string(str): + def subfn(m): + hexnum = int(m.group(1),16) + return "%c" % hexnum + return re.sub("%(..)",subfn,str) + +class Database(odb.Database): + def __init__(self,db, debug=0): + odb.Database.__init__(self, db, debug=debug) + self.SQLError = sqlite.Error + + def escape(self,str): + if str is None: + return None + elif type(str) == type(""): + return string.replace(str,"'","''") + elif type(str) == type(1): + return str + else: + raise "unknown column data type: %s" % type(str) + + + def listTables(self, cursor=None): + if cursor is None: cursor = self.defaultCursor() + cursor.execute("select name from sqlite_master where type='table'") + rows = cursor.fetchall() + tables = [] + for row in rows: tables.append(row[0]) + return tables + + def listIndices(self, cursor=None): + if cursor is None: cursor = self.defaultCursor() + cursor.execute("select name from sqlite_master where type='index'") + rows = cursor.fetchall() + tables = [] + for row in rows: tables.append(row[0]) + return tables + + def listFieldsDict(self, table_name, cursor=None): + if cursor is None: cursor = self.defaultCursor() + sql = "pragma table_info(%s)" % table_name + cursor.execute(sql) + rows = cursor.fetchall() + + columns = {} + for row in rows: + colname = row[1] + columns[colname] = row + return columns + + def _tableCreateStatement(self, table_name, cursor=None): + if cursor is None: cursor = self.defaultCursor() + sql = "select sql from sqlite_master where type='table' and name='%s'" % table_name + print sql + cursor.execute(sql) + row = cursor.fetchone() + sqlstatement = row[0] + return sqlstatement + + + def alterTableToMatch(self, table): + tableName = table.getTableName() + tmpTableName = tableName + "_" + str(os.getpid()) + + + invalidAppCols, invalidDBCols = table.checkTable(warnflag=0) + +## if invalidAppCols or invalidDBCols: +## return + + if not invalidAppCols and not invalidDBCols: + return + + + oldcols = self.listFieldsDict(tableName) +# tmpcols = oldcols.keys() + + tmpcols = [] + newcols = table.getAppColumnList() + for colname, coltype, options in newcols: + if oldcols.has_key(colname): tmpcols.append(colname) + + tmpcolnames = string.join(tmpcols, ",") + + statements = [] + + sql = "begin transaction" + statements.append(sql) + + sql = "create temporary table %s (%s)" % (tmpTableName, tmpcolnames) + statements.append(sql) + + sql = "insert into %s select %s from %s" % (tmpTableName, tmpcolnames, tableName) + statements.append(sql) + + sql = "drop table %s" % tableName + statements.append(sql) + + sql = table._createTableSQL() + statements.append(sql) + + sql = "insert into %s(%s) select %s from %s" % (tableName, tmpcolnames, tmpcolnames, tmpTableName) + statements.append(sql) + + sql = "drop table %s" % tmpTableName + statements.append(sql) + + sql = "commit" + statements.append(sql) + + cur = self.defaultCursor() + for statement in statements: +# print statement + cur.execute(statement) + + +def test(): + pass + +def usage(progname): + print __doc__ % vars() + +def main(argv, stdout, environ): + progname = argv[0] + optlist, args = getopt.getopt(argv[1:], "", ["help", "test", "debug"]) + + testflag = 0 + if len(args) == 0: + usage(progname) + return + for (field, val) in optlist: + if field == "--help": + usage(progname) + return + elif field == "--debug": + debugfull() + elif field == "--test": + testflag = 1 + + if testflag: + test() + return + + +if __name__ == "__main__": + main(sys.argv, sys.stdout, os.environ) Binary files clearsilver-0.9.7/python/examples/base/odb_sqlite.pyc and clearsilver-0.9.8/python/examples/base/odb_sqlite.pyc differ diff -Nru clearsilver-0.9.7/python/examples/base/odb_test.py clearsilver-0.9.8/python/examples/base/odb_test.py --- clearsilver-0.9.7/python/examples/base/odb_test.py Wed Dec 31 16:00:00 1969 +++ clearsilver-0.9.8/python/examples/base/odb_test.py Thu Mar 25 12:53:55 2004 @@ -0,0 +1,296 @@ +#!/usr/bin/env python + +from odb import * + +## ----------------------------------------------------------------------- +## T E S T S +## ----------------------------------------------------------------------- + +import MySQLdb +import odb_mysql +import sqlite +import odb_sqlite + +def TEST(output=log): + LOGGING_STATUS[DEV_SELECT] = 1 + LOGGING_STATUS[DEV_UPDATE] = 1 + + print "------ TESTING MySQLdb ---------" + rdb = MySQLdb.connect(host = 'localhost',user='root', passwd = '', db='testdb') + ndb = MySQLdb.connect(host = 'localhost',user='trakken', passwd = 'trakpas', db='testdb') + cursor = rdb.cursor() + + output("drop table agents") + try: + cursor.execute("drop table agents") # clean out the table + except: + pass + output("creating table") + + SQL = """ + + create table agents ( + agent_id integer not null primary key auto_increment, + login varchar(200) not null, + unique (login), + ext_email varchar(200) not null, + hashed_pw varchar(20) not null, + name varchar(200), + auth_level integer default 0, + ticket_count integer default 0) + """ + + cursor.execute(SQL) + db = odb_mysql.Database(ndb) + TEST_DATABASE(rdb,db,output=output) + + print "------ TESTING sqlite ----------" + rdb = sqlite.connect("/tmp/test.db",autocommit=1) + cursor = rdb.cursor() + try: + cursor.execute("drop table agents") + except: + pass + SQL = """ + create table agents ( + agent_id integer primary key, + login varchar(200) not null, + ext_email varchar(200) not null, + hashed_pw varchar(20), + name varchar(200), + auth_level integer default 0, + ticket_count integer default 0)""" + cursor.execute(SQL) + rdb = sqlite.connect("/tmp/test.db",autocommit=1) + ndb = sqlite.connect("/tmp/test.db",autocommit=1) + + db = odb_sqlite.Database(ndb) + TEST_DATABASE(rdb,db,output=output,is_mysql=0) + + + + +def TEST_DATABASE(rdb,db,output=log,is_mysql=1): + + cursor = rdb.cursor() + + class AgentsTable(Table): + def _defineRows(self): + self.d_addColumn("agent_id",kInteger,None,primarykey = 1,autoincrement = 1) + self.d_addColumn("login",kVarString,200,notnull=1) + self.d_addColumn("ext_email",kVarString,200,notnull=1) + self.d_addColumn("hashed_pw",kVarString,20,notnull=1) + self.d_addColumn("name",kBigString,compress_ok=1) + self.d_addColumn("auth_level",kInteger,None) + self.d_addColumn("ticket_count",kIncInteger,None) + + tbl = AgentsTable(db,"agents") + + + + + TEST_INSERT_COUNT = 5 + + # --------------------------------------------------------------- + # make sure we can catch a missing row + + try: + a_row = tbl.fetchRow( ("agent_id", 1000) ) + raise "test error" + except eNoMatchingRows: + pass + + output("PASSED! fetch missing row test") + + # -------------------------------------------------------------- + # create new rows and insert them + + for n in range(TEST_INSERT_COUNT): + new_id = n + 1 + + newrow = tbl.newRow() + newrow.name = "name #%d" % new_id + newrow.login = "name%d" % new_id + newrow.ext_email = "%d@name" % new_id + newrow.save() + if newrow.agent_id != new_id: + raise "new insert id (%s) does not match expected value (%d)" % (newrow.agent_id,new_id) + + output("PASSED! autoinsert test") + + # -------------------------------------------------------------- + # fetch one row + a_row = tbl.fetchRow( ("agent_id", 1) ) + + if a_row.name != "name #1": + raise "row data incorrect" + + output("PASSED! fetch one row test") + + # --------------------------------------------------------------- + # don't change and save it + # (i.e. the "dummy cursor" string should never be called!) + # + try: + a_row.save(cursor = "dummy cursor") + except AttributeError, reason: + raise "row tried to access cursor on save() when no changes were made!" + + output("PASSED! don't save when there are no changed") + + # --------------------------------------------------------------- + # change, save, load, test + + a_row.auth_level = 10 + a_row.save() + b_row = tbl.fetchRow( ("agent_id", 1) ) + if b_row.auth_level != 10: + log(repr(b_row)) + raise "save and load failed" + + + output("PASSED! change, save, load") + + # --------------------------------------------------------------- + # replace + + + repl_row = tbl.newRow(replace=1) + repl_row.agent_id = a_row.agent_id + repl_row.login = a_row.login + "-" + a_row.login + repl_row.ext_email = "foo" + repl_row.save() + + b_row = tbl.fetchRow( ("agent_id", a_row.agent_id) ) + if b_row.login != repl_row.login: + raise "replace failed" + output("PASSED! replace") + + # -------------------------------------------------------------- + # access unknown dict item + + try: + a = a_row["UNKNOWN_ATTRIBUTE"] + raise "test error" + except KeyError, reason: + pass + + try: + a_row["UNKNOWN_ATTRIBUTE"] = 1 + raise "test error" + except KeyError, reason: + pass + + output("PASSED! unknown dict item exception") + + # -------------------------------------------------------------- + # access unknown attribute + try: + a = a_row.UNKNOWN_ATTRIBUTE + raise "test error" + except AttributeError, reason: + pass + + try: + a_row.UNKNOWN_ATTRIBUTE = 1 + raise "test error" + except AttributeError, reason: + pass + + output("PASSED! unknown attribute exception") + + + # -------------------------------------------------------------- + # use wrong data for column type + + try: + a_row.agent_id = "this is a string" + raise "test error" + except eInvalidData, reason: + pass + + output("PASSED! invalid data for column type") + + # -------------------------------------------------------------- + # fetch 1 rows + + rows = tbl.fetchRows( ('agent_id', 1) ) + if len(rows) != 1: + raise "fetchRows() did not return 1 row!" % (TEST_INSERT_COUNT) + + output("PASSED! fetch one row") + + + # -------------------------------------------------------------- + # fetch All rows + + rows = tbl.fetchAllRows() + if len(rows) != TEST_INSERT_COUNT: + for a_row in rows: + output(repr(a_row)) + raise "fetchAllRows() did not return TEST_INSERT_COUNT(%d) rows!" % (TEST_INSERT_COUNT) + + output("PASSED! fetchall rows") + + + # -------------------------------------------------------------- + # delete row object + + row = tbl.fetchRow( ('agent_id', 1) ) + row.delete() + try: + row = tbl.fetchRow( ('agent_id', 1) ) + raise "delete failed to delete row!" + except eNoMatchingRows: + pass + + # -------------------------------------------------------------- + # table deleteRow() call + + row = tbl.fetchRow( ('agent_id',2) ) + tbl.deleteRow( ('agent_id', 2) ) + try: + row = tbl.fetchRow( ('agent_id',2) ) + raise "table delete failed" + except eNoMatchingRows: + pass + + # -------------------------------------------------------------- + # table deleteRow() call + + row = tbl.fetchRow( ('agent_id',3) ) + if row.databaseSizeForColumn('name') != len(row.name): + raise "databaseSizeForColumn('name') failed" + + # -------------------------------------------------------------- + # test inc fields + row = tbl.newRow() + new_id = 1092 + row.name = "name #%d" % new_id + row.login = "name%d" % new_id + row.ext_email = "%d@name" % new_id + row.inc('ticket_count') + row.save() + new_id = row.agent_id + + trow = tbl.fetchRow( ('agent_id',new_id) ) + if trow.ticket_count != 1: + raise "ticket_count didn't inc!" + + row.inc('ticket_count', count=2) + row.save() + trow = tbl.fetchRow( ('agent_id',new_id) ) + if trow.ticket_count != 3: + raise "ticket_count wrong, expected 3, got %d" % trow.ticket_count + + trow.inc('ticket_count') + trow.save() + if trow.ticket_count != 4: + raise "ticket_count wrong, expected 4, got %d" % trow.ticket_count + + output("\n==== ALL TESTS PASSED ====") + + +if __name__ == "__main__": + TEST() + diff -Nru clearsilver-0.9.7/python/examples/base/sgmllib.py clearsilver-0.9.8/python/examples/base/sgmllib.py --- clearsilver-0.9.7/python/examples/base/sgmllib.py Wed Dec 31 16:00:00 1969 +++ clearsilver-0.9.8/python/examples/base/sgmllib.py Tue Mar 16 13:18:18 2004 @@ -0,0 +1,615 @@ +# A parser for SGML, using the derived class as static DTD. + +# XXX This only supports those SGML features used by HTML. + +# XXX There should be a way to distinguish between PCDATA (parsed +# character data -- the normal case), RCDATA (replaceable character +# data -- only char and entity references and end tags are special) +# and CDATA (character data -- only end tags are special). + +# sgmlop support added by fredrik@pythonware.com (April 6, 1998) + +import re +import string +import sys + +try: + from xml.parsers import sgmlop +except ImportError: + sgmlop = None + +# standard entity defs + +ENTITYDEFS = { + 'lt': '<', + 'gt': '>', + 'amp': '&', + 'quot': '"', + 'apos': '\'' + } + +# SGML parser base class -- find tags and call handler functions. +# Usage: p = SGMLParser(); p.feed(data); ...; p.close(). +# The dtd is defined by deriving a class which defines methods +# with special names to handle tags: start_foo and end_foo to handle +# and , respectively, or do_foo to handle by itself. +# (Tags are converted to lower case for this purpose.) The data +# between tags is passed to the parser by calling self.handle_data() +# with some data as argument (the data may be split up in arbutrary +# chunks). Entity references are passed by calling +# self.handle_entityref() with the entity reference as argument. + +# -------------------------------------------------------------------- +# original re-based SGML parser + +interesting = re.compile('[&<]') +incomplete = re.compile('&([a-zA-Z][a-zA-Z0-9]*|#[0-9]*)?|' + '<([a-zA-Z][^<>]*|' + '/([a-zA-Z][^<>]*)?|' + '![^<>]*)?') + +entityref = re.compile('&([a-zA-Z][a-zA-Z0-9]*)[^a-zA-Z0-9]') +charref = re.compile('&#([0-9]+)[^0-9]') + +starttagopen = re.compile('<[>a-zA-Z]') +shorttagopen = re.compile('<[a-zA-Z][a-zA-Z0-9]*/') +shorttag = re.compile('<([a-zA-Z][a-zA-Z0-9]*)/([^/]*)/') +endtagopen = re.compile('a-zA-Z]') +endbracket = re.compile('[<>]') +special = re.compile(']*>') +commentopen = re.compile('