/* This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks. Copyright (C) 2002-2018 by Jin-Hwan Cho and Shunsaku Hirata, the dvipdfmx project team. Copyright (C) 1998, 1999 by Mark A. Wicks This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include "dpx-spc_tpic.h" #include #include #include #include #include #include #include "dpx-dpxutil.h" #include "dpx-error.h" #include "dpx-mem.h" #include "dpx-numbers.h" #include "dpx-pdfcolor.h" #include "dpx-pdfdev.h" #include "dpx-pdfdoc.h" #include "dpx-pdfdraw.h" #include "dpx-pdfobj.h" #include "dpx-specials.h" #define DEBUG 1 #define ENABLE_SPC_NAMESPACE 1 /* * Following "constant" converts milli-inches to * device (in this case PDF stream) coordinates. */ #define MI2DEV (0.072/pdf_dev_scale()) /* * Value for 'sh' command 'g' is interpreted as * * gray color value 1-g for "solid" * opacity value g for "opacity" * shape value g for "shape" */ #define TPIC_MODE__FILL_SOLID 0 #define TPIC_MODE__FILL_OPACITY 1 #define TPIC_MODE__FILL_SHAPE 2 #ifndef ISBLANK # define ISBLANK(c) ( (c) == ' ' || (c) == '\t' ) #endif static void skip_blank (const char **pp, const char *endptr) { const char *p = *pp; for ( ; p < endptr && ISBLANK(*p); p++); *pp = p; } struct spc_tpic_ { struct { int fill; } mode; /* state */ double pen_size; bool fill_shape; double fill_color; pdf_coord *points; int num_points; int max_points; }; #if 1 static struct spc_tpic_ _tpic_state; #endif /* We use pdf_doc_add_page_content() here * since we always draw isolated graphics. */ static void tpic__clear (struct spc_tpic_ *tp) { tp->points = mfree(tp->points); tp->num_points = 0; tp->max_points = 0; tp->fill_shape = 0; tp->fill_color = 0.0; } static pdf_obj * create_xgstate (double a /* alpha */, int f_ais /* alpha is shape */) { pdf_obj *dict; dict = pdf_new_dict(); pdf_add_dict(dict, pdf_new_name("Type"), pdf_new_name("ExtGState")); if (f_ais) { pdf_add_dict(dict, pdf_new_name("AIS"), pdf_new_boolean(1)); } pdf_add_dict(dict, pdf_new_name("ca"), pdf_new_number(a)); return dict; } static int check_resourcestatus (const char *category, const char *resname) { pdf_obj *dict1, *dict2; dict1 = pdf_doc_current_page_resources(); if (!dict1) return 0; dict2 = pdf_lookup_dict(dict1, category); if (dict2 && pdf_obj_typeof(dict2) == PDF_DICT) { if (pdf_lookup_dict(dict2, resname)) return 1; } return 0; } static int set_linestyle (double pn, double da) { double dp[2]; /* dash pattern */ pdf_dev_setlinejoin(1); pdf_dev_setmiterlimit(1.4); pdf_dev_setlinewidth(pn); if (da > 0.0) { dp[0] = da * 72.0; pdf_dev_setdash(1, dp, 0); pdf_dev_setlinecap(0); } else if (da < 0.0) { dp[0] = pn; dp[1] = -da * 72.0; pdf_dev_setdash(2, dp, 0); pdf_dev_setlinecap(1); } else { pdf_dev_setlinecap(0); } return 0; } static int set_fillstyle (double g, double a, int f_ais) { pdf_obj *dict; char resname[32]; char buf[256]; int alp, len = 0; if (a > 0.0) { alp = round(100.0 * a); sprintf(resname, "_Tps_a%03d_", alp); if (!check_resourcestatus("ExtGState", resname)) { dict = create_xgstate(ROUND(0.01 * alp, 0.01), f_ais); pdf_doc_add_page_resource("ExtGState", resname, pdf_ref_obj(dict)); pdf_release_obj(dict); } len += sprintf(buf + len, " /%s gs", resname); pdf_doc_add_page_content(buf, len); /* op: gs */ } { pdf_color *sc, *fc, new_fc; pdf_color_get_current (&sc, &fc); /* get stroking and fill colors */ pdf_color_brighten_color(&new_fc, fc, g); pdf_dev_set_nonstrokingcolor(&new_fc); } return 0; } static void set_styles (struct spc_tpic_ *tp, const pdf_coord *c, bool f_fs, bool f_vp, double pn, double da) { pdf_tmatrix M; pdf_setmatrix (&M, 1.0, 0.0, 0.0, -1.0, c->x, c->y); pdf_dev_concat(&M); if (f_vp) set_linestyle(pn, da); if (f_fs) { double g, a; int f_ais; if (tp->mode.fill == TPIC_MODE__FILL_SOLID || !tp->fill_color) { g = 1.0 - tp->fill_color; a = 0.0; } else { g = 0.0; a = tp->fill_color; } f_ais = (tp->mode.fill == TPIC_MODE__FILL_SHAPE) ? 1 : 0; set_fillstyle(g, a, f_ais); } } static void showpath (bool f_vp, bool f_fs) /* visible_path, fill_shape */ { if (f_vp) { if (f_fs) pdf_dev_flushpath('b', PDF_FILL_RULE_NONZERO); else { pdf_dev_flushpath('S', PDF_FILL_RULE_NONZERO); } } else { /* * Acrobat claims 'Q' as illegal operation when there are unfinished * path (a path without path-painting operator applied)? */ if (f_fs) pdf_dev_flushpath('f', PDF_FILL_RULE_NONZERO); else { pdf_dev_newpath(); } } } #define CLOSED_PATH(s) (\ (s)->points[0].x == (s)->points[(s)->num_points-1].x && \ (s)->points[0].y == (s)->points[(s)->num_points-1].y \ ) static int tpic__polyline (struct spc_tpic_ *tp, const pdf_coord *c, bool f_vp, double da) { double pn = tp->pen_size; bool f_fs = tp->fill_shape; int i, error = 0; /* Shading is applied only to closed path. */ f_fs = CLOSED_PATH(tp) ? f_fs : false; f_vp = (pn > 0.0) ? f_vp : false; if (f_vp || f_fs) { pdf_dev_gsave(); set_styles(tp, c, f_fs, f_vp, pn, da); pdf_dev_moveto(tp->points[0].x, tp->points[0].y); for (i = 0; i < tp->num_points; i++) pdf_dev_lineto(tp->points[i].x, tp->points[i].y); showpath(f_vp, f_fs); pdf_dev_grestore(); } tpic__clear(tp); return error; } /* * Accroding to * "Tpic: Pic for TEX", Tim Morgan, Original by Brian Kernighan, p.20: * * A spline is a smooth curve guided by a set of straight lines just * like the line above. It begins at the same place, ends at the same * place, and in between is tangent to the mid-point of each guiding * line. The syntax for a spline is identical to a (path) line except * for using spline instead of line. * * Spline is not a curve drawn by spline-fitting points p0, p1, ..., pn, * defined by tpic special "pa" command. Instead, a path defined by set * of points p0, p1, ... is guiding line mentioned above. * * Dvipsk draws them as a straight line from p0 to q1 = (p0 + p1)/2, * followed by a quadratic B-spline curve with starting point q1, (off- * curve) control point p1, end point q2 = (p1 + p2)/2, ..., and a * straight line from qn to pn. */ static int tpic__spline (struct spc_tpic_ *tp, const pdf_coord *c, bool f_vp, double da) { double v[6]; double pn = tp->pen_size; bool f_fs = tp->fill_shape; int i, error = 0; f_fs = CLOSED_PATH(tp) ? f_fs : false; f_vp = (pn > 0.0) ? f_vp : false; if (f_vp || f_fs) { pdf_dev_gsave(); set_styles(tp, c, f_fs, f_vp, pn, da); pdf_dev_moveto(tp->points[0].x, tp->points[0].y); v[0] = 0.5 * (tp->points[0].x + tp->points[1].x); v[1] = 0.5 * (tp->points[0].y + tp->points[1].y); pdf_dev_lineto(v[0], v[1]); for (i = 1; i < tp->num_points - 1; i++) { /* B-spline control points */ v[0] = 0.5 * (tp->points[i-1].x + tp->points[i].x); v[1] = 0.5 * (tp->points[i-1].y + tp->points[i].y); v[2] = tp->points[i].x; v[3] = tp->points[i].y; v[4] = 0.5 * (tp->points[i].x + tp->points[i+1].x); v[5] = 0.5 * (tp->points[i].y + tp->points[i+1].y); pdf_dev_bspline(v[0], v[1], v[2], v[3], v[4], v[5]); } pdf_dev_lineto(tp->points[i].x, tp->points[i].y); showpath(f_vp, f_fs); pdf_dev_grestore(); } tpic__clear(tp); return error; } static int tpic__arc (struct spc_tpic_ *tp, const pdf_coord *c, bool f_vp, double da, double *v /* 6 numbers */ ) { double pn = tp->pen_size; bool f_fs = tp->fill_shape; f_fs = (round(fabs(v[4] - v[5]) + 0.5) >= 360) ? f_fs : false; f_vp = (pn > 0.0) ? f_vp : false; if (f_vp || f_fs) { pdf_dev_gsave(); set_styles(tp, c, f_fs, f_vp, pn, da); /* The arcx operator here draws an excess straight line from current * point to the starting point of the arc if they are different, as in * PostScript language. It may cuase an unexpected behavior when DVIPS * transformation command is inserted before TPIC ar command: it invokes * moveto and sets currentpoint which may be different from the starting * point of arc to be drawn. We use newpath here to avoid drawing an * excess line. I'm not sure if it is proper TPIC implementation but this * seems to be DVIPS compatible behavior. */ pdf_dev_newpath(); pdf_dev_arcx(v[0], v[1], v[2], v[3], v[4], v[5], +1, 0.0); showpath(f_vp, f_fs); pdf_dev_grestore(); } tpic__clear(tp); return 0; } #if 1 static int spc_currentpoint (struct spc_env *spe, int *pg, pdf_coord *cp) { *pg = 0; cp->x = spe->x_user; cp->y = spe->y_user; return 0; } #endif static int spc_handler_tpic_pn (struct spc_env *spe, struct spc_arg *ap ) /* , void *dp) */ { struct spc_tpic_ *tp = &_tpic_state; char *q; assert(spe && ap && tp); skip_blank(&ap->curptr, ap->endptr); q = parse_float_decimal(&ap->curptr, ap->endptr); if (!q) { spc_warn(spe, "Invalid pen size specified?"); return -1; } tp->pen_size = atof(q) * MI2DEV; free(q); return 0; } static int spc_handler_tpic_pa (struct spc_env *spe, struct spc_arg *ap ) /* , void *dp) */ { struct spc_tpic_ *tp = &_tpic_state; char *q; int i; double v[2]; assert(spe && ap && tp); skip_blank(&ap->curptr, ap->endptr); for (i = 0; i < 2 && ap->curptr < ap->endptr; i++) { q = parse_float_decimal(&ap->curptr, ap->endptr); if (!q) { spc_warn(spe, "Missing numbers for TPIC \"pa\" command."); return -1; } v[i] = atof(q); free(q); skip_blank(&ap->curptr, ap->endptr); } if (i != 2) { spc_warn(spe, "Invalid arg for TPIC \"pa\" command."); return -1; } if (tp->num_points >= tp->max_points) { tp->max_points += 256; tp->points = RENEW(tp->points, tp->max_points, pdf_coord); } tp->points[tp->num_points].x = v[0] * MI2DEV; tp->points[tp->num_points].y = v[1] * MI2DEV; tp->num_points += 1; return 0; } static int spc_handler_tpic_fp (struct spc_env *spe, struct spc_arg *ap ) /* , void *dp) */ { struct spc_tpic_ *tp = &_tpic_state; pdf_coord cp; int pg; assert(spe && ap && tp); if (tp->num_points <= 1) { spc_warn(spe, "Too few points (< 2) for polyline path."); return -1; } spc_currentpoint(spe, &pg, &cp); return tpic__polyline(tp, &cp, 1, 0.0); } static int spc_handler_tpic_ip (struct spc_env *spe, struct spc_arg *ap ) /* , void *dp) */ { struct spc_tpic_ *tp = &_tpic_state; pdf_coord cp; int pg; assert(spe && ap && tp); if (tp->num_points <= 1) { spc_warn(spe, "Too few points (< 2) for polyline path."); return -1; } spc_currentpoint(spe, &pg, &cp); return tpic__polyline(tp, &cp, 0, 0.0); } static int spc_handler_tpic_da (struct spc_env *spe, struct spc_arg *ap ) /* , void *dp) */ { struct spc_tpic_ *tp = &_tpic_state; char *q; double da = 0.0; pdf_coord cp; int pg; assert(spe && ap && tp); skip_blank(&ap->curptr, ap->endptr); q = parse_float_decimal(&ap->curptr, ap->endptr); if (q) { da = atof(q); free(q); } if (tp->num_points <= 1) { spc_warn(spe, "Too few points (< 2) for polyline path."); return -1; } spc_currentpoint(spe, &pg, &cp); return tpic__polyline(tp, &cp, 1, da); } static int spc_handler_tpic_dt (struct spc_env *spe, struct spc_arg *ap ) /* , void *dp) */ { struct spc_tpic_ *tp = &_tpic_state; char *q; double da = 0.0; pdf_coord cp; int pg; assert(spe && ap && tp); skip_blank(&ap->curptr, ap->endptr); q = parse_float_decimal(&ap->curptr, ap->endptr); if (q) { da = -atof(q); free(q); } if (tp->num_points <= 1) { spc_warn(spe, "Too few points (< 2) for polyline path."); return -1; } spc_currentpoint(spe, &pg, &cp); return tpic__polyline(tp, &cp, 1, da); } static int spc_handler_tpic_sp (struct spc_env *spe, struct spc_arg *ap ) /* , void *dp) */ { struct spc_tpic_ *tp = &_tpic_state; char *q; double da = 0.0; pdf_coord cp; int pg; assert(spe && ap && tp); skip_blank(&ap->curptr, ap->endptr); q = parse_float_decimal(&ap->curptr, ap->endptr); if (q) { da = atof(q); free(q); } if (tp->num_points <= 2) { spc_warn(spe, "Too few points (< 3) for spline path."); return -1; } spc_currentpoint(spe, &pg, &cp); return tpic__spline(tp, &cp, 1, da); } static int spc_handler_tpic_ar (struct spc_env *spe, struct spc_arg *ap ) /* , void *dp) */ { struct spc_tpic_ *tp = &_tpic_state; double v[6]; pdf_coord cp; int pg; char *q; int i; assert(spe && ap && tp); skip_blank(&ap->curptr, ap->endptr); for (i = 0; i < 6 && ap->curptr < ap->endptr; i++) { q = parse_float_decimal(&ap->curptr, ap->endptr); if (!q) { spc_warn(spe, "Invalid args. in TPIC \"ar\" command."); return -1; } v[i] = atof(q); free(q); skip_blank(&ap->curptr, ap->endptr); } if (i != 6) { spc_warn(spe, "Invalid arg for TPIC \"ar\" command."); return -1; } v[0] *= MI2DEV; v[1] *= MI2DEV; v[2] *= MI2DEV; v[3] *= MI2DEV; v[4] *= 180.0 / M_PI; v[5] *= 180.0 / M_PI; spc_currentpoint(spe, &pg, &cp); return tpic__arc(tp, &cp, 1, 0.0, v); } static int spc_handler_tpic_ia (struct spc_env *spe, struct spc_arg *ap ) /* , void *dp) */ { struct spc_tpic_ *tp = &_tpic_state; double v[6]; pdf_coord cp; int pg; char *q; int i; assert(spe && ap && tp); skip_blank(&ap->curptr, ap->endptr); for (i = 0; i < 6 && ap->curptr < ap->endptr; i++) { q = parse_float_decimal(&ap->curptr, ap->endptr); if (!q) { spc_warn(spe, "Invalid args. in TPIC \"ia\" command."); return -1; } v[i] = atof(q); free(q); skip_blank(&ap->curptr, ap->endptr); } if (i != 6) { spc_warn(spe, "Invalid arg for TPIC \"ia\" command."); return -1; } v[0] *= MI2DEV; v[1] *= MI2DEV; v[2] *= MI2DEV; v[3] *= MI2DEV; v[4] *= 180.0 / M_PI; v[5] *= 180.0 / M_PI; spc_currentpoint(spe, &pg, &cp); return tpic__arc(tp, &cp, 0, 0.0, v); } static int spc_handler_tpic_sh (struct spc_env *spe, struct spc_arg *ap ) /* , void *dp) */ { struct spc_tpic_ *tp = &_tpic_state; char *q; assert(spe && ap && tp); tp->fill_shape = 1; tp->fill_color = 0.5; skip_blank(&ap->curptr, ap->endptr); q = parse_float_decimal(&ap->curptr, ap->endptr); if (q) { double g = atof(q); free(q); if (g >= 0.0 && g <= 1.0) tp->fill_color = g; else { dpx_warning("Invalid fill color specified: %g\n", g); return -1; } } return 0; } static int spc_handler_tpic_wh (struct spc_env *spe, struct spc_arg *ap ) /* , void *dp) */ { struct spc_tpic_ *tp = &_tpic_state; assert(spe && ap && tp); tp->fill_shape = 1; tp->fill_color = 0.0; return 0; } static int spc_handler_tpic_bk (struct spc_env *spe, struct spc_arg *ap ) /* , void *dp) */ { struct spc_tpic_ *tp = &_tpic_state; assert(spe && ap && tp); tp->fill_shape = 1; tp->fill_color = 1.0; return 0; } static int spc_handler_tpic_tx (struct spc_env *spe, struct spc_arg *ap ) /* , void *dp) */ { struct spc_tpic_ *tp = &_tpic_state; assert(spe && ap && tp); spc_warn(spe, "TPIC command \"tx\" not supported."); return -1; } static int spc_handler_tpic__init (struct spc_env *spe, void *dp) { struct spc_tpic_ *tp = dp; tp->pen_size = 1.0; tp->fill_shape = 0; tp->fill_color = 0.0; tp->points = NULL; tp->num_points = 0; tp->max_points = 0; if (tp->mode.fill != TPIC_MODE__FILL_SOLID && pdf_check_version(1, 4) < 0) { spc_warn(spe, "Tpic shading support requires PDF version 1.4."); tp->mode.fill = TPIC_MODE__FILL_SOLID; } return 0; } static int spc_handler_tpic__bophook (void *dp) { struct spc_tpic_ *tp = dp; assert(tp); tpic__clear(tp); return 0; } static int spc_handler_tpic__eophook (struct spc_env *spe, void *dp) { struct spc_tpic_ *tp = dp; assert(tp); if (tp->num_points > 0) spc_warn(spe, "Unflushed tpic path at end of the page."); tpic__clear(tp); return 0; } static int spc_handler_tpic__clean (struct spc_env *spe, void *dp) { struct spc_tpic_ *tp = dp; assert(tp); if (tp->num_points > 0) spc_warn(spe, "Unflushed tpic path at end of the document."); tpic__clear(tp); return 0; } void tpic_set_fill_mode (int mode) { struct spc_tpic_ *tp = &_tpic_state; tp->mode.fill = mode; } int spc_tpic_at_begin_page (void) { struct spc_tpic_ *tp = &_tpic_state; return spc_handler_tpic__bophook(tp); } int spc_tpic_at_end_page (void) { struct spc_tpic_ *tp = &_tpic_state; return spc_handler_tpic__eophook(NULL, tp); } int spc_tpic_at_begin_document (void) { struct spc_tpic_ *tp = &_tpic_state; return spc_handler_tpic__init(NULL, tp); } int spc_tpic_at_end_document (void) { struct spc_tpic_ *tp = &_tpic_state; return spc_handler_tpic__clean(NULL, tp); } #if DEBUG #include "dpx-pdfparse.h" /* parse_val_ident :( */ static pdf_obj * spc_parse_kvpairs (struct spc_arg *ap) { pdf_obj *dict; char *kp, *vp; int error = 0; dict = pdf_new_dict(); skip_blank(&ap->curptr, ap->endptr); while (!error && ap->curptr < ap->endptr) { kp = parse_val_ident(&ap->curptr, ap->endptr); if (!kp) break; skip_blank(&ap->curptr, ap->endptr); if (ap->curptr < ap->endptr && ap->curptr[0] == '=') { ap->curptr++; skip_blank(&ap->curptr, ap->endptr); if (ap->curptr == ap->endptr) { free(kp); error = -1; break; } vp = parse_c_string(&ap->curptr, ap->endptr); if (!vp) error = -1; else { pdf_add_dict(dict, pdf_new_name(kp), pdf_new_string(vp, strlen(vp) + 1)); /* NULL terminate */ free(vp); } } else { /* Treated as 'flag' */ pdf_add_dict(dict, pdf_new_name(kp), pdf_new_boolean(1)); } free(kp); if (!error) skip_blank(&ap->curptr, ap->endptr); } if (error) { pdf_release_obj(dict); dict = NULL; } return dict; } static int tpic_filter_getopts (pdf_obj *kp, pdf_obj *vp, void *dp) { struct spc_tpic_ *tp = dp; char *k, *v; int error = 0; assert( kp && vp && tp ); k = pdf_name_value(kp); if (streq_ptr(k, "fill-mode")) { if (pdf_obj_typeof(vp) != PDF_STRING) { dpx_warning("Invalid value for TPIC option fill-mode..."); error = -1; } else { v = pdf_string_value(vp); if (streq_ptr(v, "shape")) tp->mode.fill = TPIC_MODE__FILL_SHAPE; else if (streq_ptr(v, "opacity")) tp->mode.fill = TPIC_MODE__FILL_OPACITY; else if (streq_ptr(v, "solid")) tp->mode.fill = TPIC_MODE__FILL_SOLID; else { dpx_warning("Invalid value for TPIC option fill-mode: %s", v); error = -1; } } } else { dpx_warning("Unrecognized option for TPIC special handler: %s", k); error = -1; } return error; } static int spc_handler_tpic__setopts (struct spc_env *spe, struct spc_arg *ap) { struct spc_tpic_ *tp = &_tpic_state; pdf_obj *dict; int error = 0; dict = spc_parse_kvpairs(ap); if (!dict) return -1; error = pdf_foreach_dict(dict, tpic_filter_getopts, tp); if (!error) { if (tp->mode.fill != TPIC_MODE__FILL_SOLID && pdf_check_version(1, 4) < 0) { spc_warn(spe, "Transparent fill mode requires PDF version 1.4."); tp->mode.fill = TPIC_MODE__FILL_SOLID; } } return error; } #endif /* DEBUG */ static struct spc_handler tpic_handlers[] = { {"pn", spc_handler_tpic_pn}, {"pa", spc_handler_tpic_pa}, {"fp", spc_handler_tpic_fp}, {"ip", spc_handler_tpic_ip}, {"da", spc_handler_tpic_da}, {"dt", spc_handler_tpic_dt}, {"sp", spc_handler_tpic_sp}, {"ar", spc_handler_tpic_ar}, {"ia", spc_handler_tpic_ia}, {"sh", spc_handler_tpic_sh}, {"wh", spc_handler_tpic_wh}, {"bk", spc_handler_tpic_bk}, {"tx", spc_handler_tpic_tx} }; bool spc_tpic_check_special (const char *buf, int len) { bool istpic = false, hasnsp = false; char *q; const char *p, *endptr; size_t i; p = buf; endptr = p + len; skip_blank(&p, endptr); #if ENABLE_SPC_NAMESPACE if (p + strlen("tpic:") < endptr && !memcmp(p, "tpic:", strlen("tpic:"))) { p += strlen("tpic:"); hasnsp = true; } #endif q = parse_c_ident(&p, endptr); if (!q) istpic = false; else if (q && hasnsp && streq_ptr(q, "__setopt__")) { #if DEBUG istpic = true; #endif free(q); } else { for (i = 0; i < sizeof(tpic_handlers)/sizeof(struct spc_handler); i++) { if (streq_ptr(q, tpic_handlers[i].key)) { istpic = true; break; } } free(q); } return istpic; } int spc_tpic_setup_handler (struct spc_handler *sph, struct spc_env *spe, struct spc_arg *ap) { char *q; unsigned int i; int hasnsp = 0, error = -1; assert(sph && spe && ap); skip_blank(&ap->curptr, ap->endptr); #if ENABLE_SPC_NAMESPACE if (ap->curptr + strlen("tpic:") < ap->endptr && !memcmp(ap->curptr, "tpic:", strlen("tpic:"))) { ap->curptr += strlen("tpic:"); hasnsp = 1; } #endif q = parse_c_ident(&ap->curptr, ap->endptr); if (!q) error = -1; else if (q && hasnsp && streq_ptr(q, "__setopt__")) { #if DEBUG ap->command = "__setopt__"; sph->key = "tpic:"; sph->exec = spc_handler_tpic__setopts; skip_blank(&ap->curptr, ap->endptr); error = 0; #endif free(q); } else { for (i = 0; i < sizeof(tpic_handlers)/sizeof(struct spc_handler); i++) { if (streq_ptr(q, tpic_handlers[i].key)) { ap->command = tpic_handlers[i].key; sph->key = "tpic:"; sph->exec = tpic_handlers[i].exec; skip_blank(&ap->curptr, ap->endptr); error = 0; break; } } free(q); } return error; }