summaryrefslogtreecommitdiff
path: root/graphics/sketch/opts.c
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /graphics/sketch/opts.c
Initial commit
Diffstat (limited to 'graphics/sketch/opts.c')
-rw-r--r--graphics/sketch/opts.c624
1 files changed, 624 insertions, 0 deletions
diff --git a/graphics/sketch/opts.c b/graphics/sketch/opts.c
new file mode 100644
index 0000000000..72983c25aa
--- /dev/null
+++ b/graphics/sketch/opts.c
@@ -0,0 +1,624 @@
+/* opts.c
+ Copyright (C) 2005,2006,2007 Eugene K. Ressler, Jr.
+
+This file is part of Sketch, a small, simple system for making
+3d drawings with LaTeX and the PSTricks or TikZ package.
+
+Sketch 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 3, or (at your option)
+any later version.
+
+Sketch 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 Sketch; see the file COPYING.txt. If not, see
+http://www.gnu.org/copyleft */
+
+#include "opts.h"
+#include "geometry.h"
+
+DECLARE_DYNAMIC_ARRAY_FUNCS (OPT_LIST, OPT, opt_list, elt, n_elts, NO_OTHER_INIT)
+
+// ---- useful string stuff ----------------------------------------------------
+// slice a string using Perl/Python position indexing conventions
+// (position == dst_size is always at the end of the string)
+
+char *str_slice (char *dst, int dst_size, char *src, int beg, int end)
+{
+ int len;
+
+ if (dst_size > 0)
+ {
+ len = strlen (src);
+ if (beg < 0)
+ beg = len + beg;
+ else if (beg > len)
+ beg = len;
+ if (end < 0)
+ end = len + end;
+ else if (end > len)
+ end = len;
+ len = end - beg;
+ if (len <= 0)
+ {
+ dst[0] = '\0';
+ }
+ else
+ {
+ if (len >= dst_size)
+ len = dst_size - 1;
+ memcpy (dst, &src[beg], len);
+ dst[len] = '\0';
+ }
+ }
+ return dst;
+}
+
+// a modified version of C library strtok
+// uses state variable p, which should be
+// initially set to zero.
+char *
+istrtok (int *p, char *s, char sep)
+{
+ int i, r;
+
+ // advance r to next non-space character
+ for (r = *p; s[r] == ' ' || s[r] == '\t'; r++)
+ /* skip */ ;
+
+ // if we're at terminating null, return null
+ if (s[r] == '\0')
+ {
+ *p = r;
+ return NULL;
+ }
+ // look for a separator character
+ for (i = r; s[i] != '\0'; i++)
+ {
+ if (s[i] == sep)
+ {
+ // found one; set to null char,
+ // advance state variable, and
+ // return pointer to first char
+ s[i] = '\0';
+ *p = i + 1;
+ return &s[r];
+ }
+ }
+ // did not find a terminator, so this
+ // is the last token; return it
+ *p = i;
+ return &s[r];
+}
+
+int
+str_last_occurance (char *src, char *set)
+{
+ int i;
+
+ for (i = 0; src[i]; i++)
+ /* skip */ ;
+ for (--i; i >= 0 && !strchr (set, src[i]); --i)
+ /* skip */ ;
+ return i;
+}
+
+// ---- options ----------------------------------------------------------------
+
+void
+init_opts (OPTS * opts)
+{
+ init_opt_list (opts->list);
+}
+
+OPTS *
+raw_opts (void)
+{
+ OPTS *r = safe_malloc (sizeof *r);
+ init_opts (r);
+ return r;
+}
+
+void
+setup_opts (OPTS * opts, char *opts_str, SRC_LINE line)
+{
+ int p_pair, p_side;
+ char *pair, *key, *val, *buf;
+ OPT *opt;
+
+ clear_opts (opts);
+ buf = safe_strdup (opts_str);
+ p_pair = 0;
+ while ((pair = istrtok (&p_pair, buf, ',')) != NULL)
+ {
+ p_side = 0;
+ key = istrtok (&p_side, pair, '=');
+ if (key == NULL)
+ {
+ err (line, "null keyword in option");
+ key = "";
+ }
+ val = istrtok (&p_side, pair, ',');
+ if (val == NULL)
+ {
+ err (line, "null value in option");
+ val = "";
+ }
+ opt = pushed_opt_list_elt (opts->list);
+ opt->key = safe_strdup (key);
+ opt->val = safe_strdup (val);
+ }
+ safe_free (buf);
+}
+
+OPTS *
+new_opts (char *opts_str, SRC_LINE line)
+{
+ OPTS *r = raw_opts ();
+ setup_opts (r, opts_str, line);
+ return r;
+}
+
+void
+clear_opts (OPTS * opts)
+{
+ int i;
+
+ for (i = 0; i < opts->list->n_elts; ++i)
+ {
+ safe_free (opts->list->elt[i].key);
+ safe_free (opts->list->elt[i].val);
+ }
+ clear_opt_list (opts->list);
+}
+
+char *
+opt_val (OPTS * opts, char *opt)
+{
+ int i;
+
+ if (!opts)
+ return 0;
+
+ for (i = 0; i < opts->list->n_elts; i++)
+ if (strcmp (opts->list->elt[i].key, opt) == 0)
+ return opts->list->elt[i].val;
+ return NULL;
+}
+
+int
+bool_opt_p (OPTS * opts, char *opt, int default_p)
+{
+ char *r = opt_val (opts, opt);
+ if (!r)
+ return default_p;
+ return strcmp (r, "false") != 0; // all not false is true
+}
+
+typedef struct opt_desc_t
+{
+ char *opt;
+ int type;
+}
+OPT_DESC;
+
+typedef struct opt_desc_tbl_t
+{
+ OPT_DESC *key_desc;
+ int n_key_desc;
+ OPT_DESC *val_desc;
+ int n_val_desc;
+}
+OPT_DESC_TBL;
+
+static OPT_DESC key_tbl_pst[] = {
+ {"arrows", OPT_LINE},
+ {"cull", OPT_INTERNAL},
+ {"dash", OPT_LINE},
+ {"dotsep", OPT_LINE},
+ {"fillcolor", OPT_POLYGON | OPT_FILL_COLOR},
+ {"fillstyle", OPT_POLYGON | OPT_FILL_STYLE},
+ {"lay", OPT_INTERNAL},
+ {"linecolor", OPT_LINE},
+ {"linestyle", OPT_LINE | OPT_LINE_STYLE},
+ {"linewidth", OPT_LINE},
+ {"opacity", OPT_POLYGON},
+ {"showpoints", OPT_LINE | OPT_POLYGON},
+ {"split", OPT_INTERNAL},
+ {"strokeopacity", OPT_LINE },
+ {"transpalpha", OPT_POLYGON}
+};
+
+OPT_DESC_TBL opt_desc_tbl_pst[1] = { {
+ key_tbl_pst, ARRAY_SIZE (key_tbl_pst),
+ NULL, 0}
+};
+
+static OPT_DESC opt_key_tbl_tikz[] = {
+ {"arrows", OPT_LINE},
+ {"cap", OPT_LINE},
+ {"color", OPT_LINE | OPT_POLYGON | OPT_FILL_COLOR},
+ {"cull", OPT_INTERNAL},
+ {"dash pattern", OPT_LINE},
+ {"dash phase", OPT_LINE},
+ {"double distance", OPT_LINE},
+ {"draw", OPT_LINE | OPT_LINE_STYLE},
+ {"draw opacity", OPT_LINE},
+ {"fill", OPT_POLYGON | OPT_FILL_COLOR},
+ {"fill opacity", OPT_POLYGON},
+ {"fill style", OPT_POLYGON | OPT_FILL_COLOR | OPT_EMIT_VAL},
+ {"join", OPT_LINE},
+ {"lay", OPT_INTERNAL},
+ {"line style", OPT_LINE | OPT_EMIT_VAL},
+ {"line width", OPT_LINE},
+ {"miter limit", OPT_LINE},
+ {"pattern", OPT_POLYGON | OPT_FILL_COLOR},
+ {"pattern color", OPT_POLYGON},
+ {"split", OPT_INTERNAL},
+ {"style", OPT_TYPE_IN_VAL | OPT_EMIT_VAL},
+};
+
+static OPT_DESC opt_val_tbl_tikz[] = {
+ {"dashed", OPT_LINE},
+ {"densely dashed", OPT_LINE},
+ {"densely dotted", OPT_LINE},
+ {"dotted", OPT_LINE},
+ {"double", OPT_LINE},
+ {"loosely dashed", OPT_LINE},
+ {"loosely dotted", OPT_LINE},
+ {"nearly opaque", OPT_POLYGON},
+ {"nearly transparent", OPT_POLYGON},
+ {"semithick", OPT_LINE},
+ {"semitransparent", OPT_POLYGON},
+ {"solid", OPT_LINE},
+ {"thick", OPT_LINE},
+ {"thin", OPT_LINE},
+ {"transparent", OPT_POLYGON},
+ {"ultra nearly transparent", OPT_POLYGON},
+ {"ultra thick", OPT_LINE},
+ {"ultra thin", OPT_LINE},
+ {"very nearly transparent", OPT_POLYGON},
+ {"very thick", OPT_LINE},
+ {"very thin", OPT_LINE},
+};
+
+OPT_DESC_TBL opt_desc_tbl_tikz[1] = { {
+ opt_key_tbl_tikz,
+ ARRAY_SIZE (opt_key_tbl_tikz),
+ opt_val_tbl_tikz,
+ ARRAY_SIZE (opt_val_tbl_tikz),
+ }
+};
+
+int
+opt_index (char *opt, OPT_DESC * desc, int n_desc)
+{
+ int hi, lo, mid, cmp_val;
+
+ hi = n_desc - 1;
+ lo = 0;
+ while (hi >= lo)
+ {
+ mid = (hi + lo) / 2;
+ cmp_val = strcmp (opt, desc[mid].opt);
+ if (cmp_val < 0)
+ hi = mid - 1;
+ else if (cmp_val > 0)
+ lo = mid + 1;
+ else
+ return mid;
+ }
+ return -1;
+}
+
+static OPT_DESC_TBL *lang_to_opt_desc_tbl[] = {
+ opt_desc_tbl_pst,
+ opt_desc_tbl_tikz,
+ opt_desc_tbl_pst,
+ opt_desc_tbl_tikz,
+};
+
+int
+simple_opt_type (OPT * opt, int default_type, int lang)
+{
+ OPT_DESC_TBL *desc;
+ int i;
+
+ if (lang < 0)
+ return default_type;
+ desc = lang_to_opt_desc_tbl[lang];
+ i = opt_index (opt->key, desc->key_desc, desc->n_key_desc);
+ return (i < 0) ? default_type : desc->key_desc[i].type;
+}
+
+int
+opt_type (OPT * opt, int default_type, int lang)
+{
+ OPT_DESC_TBL *desc;
+ int i, type;
+
+ type = simple_opt_type (opt, default_type, lang);
+ if (type & OPT_TYPE_IN_VAL)
+ {
+ desc = lang_to_opt_desc_tbl[lang];
+ i = opt_index (opt->val, desc->val_desc, desc->n_val_desc);
+ if (i < 0)
+ return default_type;
+ type = desc->val_desc[i].type;
+ }
+ return type;
+}
+
+typedef struct opts_desc_t
+{
+ OPT *opts;
+ int n_opts;
+}
+OPTS_DESC;
+
+OPT no_edges_opts_pst[] = {
+ {"linestyle", "none"}
+};
+
+OPT no_edges_opts_tikz[] = {
+ {"draw", "none"}
+};
+
+OPTS_DESC no_edges_opts_desc_tbl[] = {
+ {no_edges_opts_pst, ARRAY_SIZE (no_edges_opts_pst)},
+ {no_edges_opts_tikz, ARRAY_SIZE (no_edges_opts_tikz)},
+ {no_edges_opts_pst, ARRAY_SIZE (no_edges_opts_pst)},
+ {no_edges_opts_tikz, ARRAY_SIZE (no_edges_opts_tikz)},
+};
+
+static int
+any_opt_p (OPTS * opts, int type, int lang)
+{
+ int i;
+
+ if (!opts)
+ return 0;
+ for (i = 0; i < opts->list->n_elts; i++)
+ if (type & opt_type (&opts->list->elt[i], OPT_NONE, lang))
+ return 1;
+ return 0;
+}
+
+static void
+add_default_opt (OPTS ** opts_ptr, OPT * default_opt, int lang)
+{
+ OPT *opt;
+ OPTS *opts;
+ int default_type;
+
+ opts = *opts_ptr;
+ default_type = opt_type (default_opt, OPT_NONE, lang) & OPT_DEFAULTS;
+ if (any_opt_p (opts, default_type, lang))
+ return;
+ if (!opts)
+ opts = raw_opts ();
+ opt = pushed_opt_list_elt (opts->list);
+ opt->key = safe_strdup (default_opt->key);
+ opt->val = safe_strdup (default_opt->val);
+ *opts_ptr = opts;
+}
+
+static void
+add_default_opts (OPTS ** opts_ptr, OPTS_DESC * opts_desc, int lang)
+{
+ int i;
+ for (i = 0; i < opts_desc->n_opts; i++)
+ add_default_opt (opts_ptr, &opts_desc->opts[i], lang);
+}
+
+void
+add_no_edges_default_opt (OPTS ** opts_ptr, int lang)
+{
+ add_default_opts (opts_ptr, &no_edges_opts_desc_tbl[lang], lang);
+}
+
+OPT solid_white_opts_pst[] = {
+ {"fillstyle", "solid"},
+ {"fillcolor", "white"}
+};
+
+OPT solid_white_opts_tikz[] = {
+ {"fill", "white"}
+};
+
+OPTS_DESC solid_white_opts_desc_tbl[] = {
+ {solid_white_opts_pst, ARRAY_SIZE (solid_white_opts_pst)},
+ {solid_white_opts_tikz, ARRAY_SIZE (solid_white_opts_tikz)},
+ {solid_white_opts_pst, ARRAY_SIZE (solid_white_opts_pst)},
+ {solid_white_opts_tikz, ARRAY_SIZE (solid_white_opts_tikz)},
+};
+
+void
+add_solid_white_default_opt (OPTS ** opts_ptr, int lang)
+{
+ add_default_opts (opts_ptr, &solid_white_opts_desc_tbl[lang], lang);
+}
+
+void
+check_opts (OPTS * opts,
+ int allowed, char *allowed_msg, int lang, SRC_LINE line)
+{
+ int i, type;
+
+ if (!opts)
+ return;
+
+ for (i = 0; i < opts->list->n_elts; i++)
+ {
+ type = opt_type (&opts->list->elt[i], OPT_NONE, lang);
+ if ((type & allowed) == 0)
+ warn (line, allowed_msg, opts->list->elt[i].key,
+ opts->list->elt[i].val);
+ }
+}
+
+// selective copy for splitting option lists by type
+OPTS *
+copy_opts (OPTS * opts, int type_mask, int lang)
+{
+ int i;
+ OPTS *r;
+ OPT *opt;
+
+ if (!opts)
+ return NULL;
+
+ r = raw_opts ();
+ for (i = 0; i < opts->list->n_elts; i++)
+ if (type_mask & opt_type (&opts->list->elt[i], OPT_NONE, lang))
+ {
+ opt = pushed_opt_list_elt (r->list);
+ opt->key = safe_strdup (opts->list->elt[i].key);
+ opt->val = safe_strdup (opts->list->elt[i].val);
+ }
+ return r;
+}
+
+OPTS *
+cat_opts (OPTS * dst, OPTS * src)
+{
+ int i;
+ OPT *opt;
+
+ for (i = 0; i < src->list->n_elts; i++)
+ {
+ opt = pushed_opt_list_elt (dst->list);
+ opt->key = safe_strdup(src->list->elt[i].key);
+ opt->val = safe_strdup(src->list->elt[i].val);
+ }
+ return dst;
+}
+
+// selective copy for splitting out line options and modifying arrows
+OPTS *
+copy_line_opts (OPTS * opts, int first_p, int last_p, int lang)
+{
+ int i;
+ OPTS *r;
+ char buf[100];
+
+ if (!opts)
+ return NULL;
+
+ // no modifications necessary if line contains first and last points
+ if (first_p && last_p)
+ return opts;
+
+ // make a clean copy and modify the arrows
+ r = copy_opts (opts, OPT_LINE, lang);
+
+ for (i = 0; i < r->list->n_elts; i++)
+ {
+ if (strcmp ("arrows", r->list->elt[i].key) == 0)
+ {
+ char *val = r->list->elt[i].val;
+ char *dash = strchr (val, '-');
+ if (!dash)
+ {
+ warn (no_line,
+ "could not find '-' while splitting arrows option");
+ continue;
+ }
+ if (first_p)
+ {
+ str_slice (buf, sizeof buf, val, 0, dash - val + 1);
+ }
+ else if (last_p)
+ {
+ str_slice (buf, sizeof buf, val, dash - val, SLICE_TO_END);
+ }
+ else
+ {
+ // could just delete option entirely, but this is good for debugging
+ str_slice (buf, sizeof buf, val, dash - val, dash - val + 1);
+ }
+ r->list->elt[i].val = safe_strdup (buf);
+ }
+ }
+ return r;
+}
+
+static int member_p(char *str, char **str_list)
+{
+ if (str_list == NULL)
+ return 0;
+
+ while (*str_list)
+ {
+ if (strcmp(str, *str_list) == 0)
+ return 1;
+ ++str_list;
+ }
+ return 0;
+}
+
+static void
+emit_opts_internal (FILE * f, OPTS * opts, char ** exceptions, int brackets_p, int lang)
+{
+ int i, n, type;
+
+ // do nothing if no options
+ if (!opts || !opts->list || opts->list->n_elts == 0)
+ return;
+
+ // do nothing if no non-excepted options
+ for (n = i = 0; i < opts->list->n_elts; i++)
+ {
+ if ( !member_p(opts->list->elt[i].key, exceptions) )
+ ++n;
+ }
+ if (n == 0)
+ return;
+
+ if (brackets_p)
+ fputc ('[', f);
+
+ for (n = i = 0; i < opts->list->n_elts; i++)
+ {
+
+ if ( member_p(opts->list->elt[i].key, exceptions) )
+ continue;
+
+ type = simple_opt_type (&opts->list->elt[i], OPT_NONE, lang);
+ if ((type & OPT_INTERNAL) == 0)
+ {
+ if (n > 0)
+ fprintf (f, ",");
+ if (type & OPT_EMIT_VAL)
+ fprintf (f, "%s", opts->list->elt[i].val);
+ else
+ fprintf (f, "%s=%s", opts->list->elt[i].key,
+ opts->list->elt[i].val);
+ ++n;
+ }
+ }
+ if (brackets_p)
+ fputc (']', f);
+}
+
+void
+emit_opts_raw (FILE * f, OPTS * opts, int lang)
+{
+ emit_opts_internal (f, opts, NULL, 0, lang);
+}
+
+void
+emit_opts (FILE * f, OPTS * opts, int lang)
+{
+ emit_opts_internal (f, opts, NULL, 1, lang);
+}
+
+void
+emit_opts_with_exceptions (FILE * f, OPTS * opts, char ** exceptions, int lang)
+{
+ emit_opts_internal (f, opts, exceptions, 1, lang);
+}