From a206758c8e08470972aef2fc622c99e5393df944 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Sat, 29 Oct 2005 23:35:55 +0000 Subject: [PATCH] Sat Oct 29 16:25:36 2005 Ben Pfaff * count.c: Major cleanups. Rename practically everything. Rewrite much of the code. Use pools for memory management. Use the new parse_num_range(). * mis-val.c: (cmd_missing_values) Use the new parse_num_range(). (parse_number) Removed. * missing-values.c: (mv_add_num_range) Don't add out-of-order ranges, e.g. where low > high. * pool.c: (pool_2nrealloc) New function. * range-prs.c: New file. (parse_num_range) New function. (parse_number) New function. --- src/ChangeLog | 18 ++ src/Makefile.am | 16 +- src/count.c | 514 +++++++++++++++---------------------------- src/mis-val.c | 76 +------ src/missing-values.c | 4 +- src/pool.c | 101 ++++++++- src/pool.h | 1 + src/range-prs.c | 111 ++++++++++ src/range-prs.h | 28 +++ 9 files changed, 450 insertions(+), 419 deletions(-) create mode 100644 src/range-prs.c create mode 100644 src/range-prs.h diff --git a/src/ChangeLog b/src/ChangeLog index cad9304c..0d65dcd8 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,21 @@ +Sat Oct 29 16:25:36 2005 Ben Pfaff + + * count.c: Major cleanups. Rename practically everything. + Rewrite much of the code. Use pools for memory management. Use + the new parse_num_range(). + + * mis-val.c: (cmd_missing_values) Use the new parse_num_range(). + (parse_number) Removed. + + * missing-values.c: (mv_add_num_range) Don't add out-of-order + ranges, e.g. where low > high. + + * pool.c: (pool_2nrealloc) New function. + + * range-prs.c: New file. + (parse_num_range) New function. + (parse_number) New function. + Fri Oct 28 22:47:48 2005 Ben Pfaff Fix up potential overflows in size calculations by replacing diff --git a/src/Makefile.am b/src/Makefile.am index 4d24e67d..15734f10 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -49,18 +49,18 @@ command.h compute.c copyleft.c copyleft.h count.c data-in.c data-in.h \ data-list.c data-list.h data-out.c date.c debug-print.h descript.c \ devind.c devind.h dfm-read.c dfm-read.h dfm-write.c dfm-write.h \ dictionary.c dictionary.h do-if.c do-ifP.h echo.c error.c error.h \ -factor_stats.c factor_stats.h file-handle-def.c file-handle-def.h \ -file-handle.h file-type.c filename.c filename.h flip.c font.h format.c \ -format-prs.c format.def format.h formats.c get.c \ -getl.c getl.h glob.c glob.h groff-font.c group.c group.h group_proc.h \ -hash.c hash.h histogram.c histogram.h html.c htmlP.h include.c \ -inpt-pgm.c lexer.c lexer.h lex-def.h lex-def.c levene.c levene.h linked-list.c \ +factor_stats.c factor_stats.h file-handle-def.c file-handle-def.h \ +file-handle.h file-type.c filename.c filename.h flip.c font.h format.c \ +format-prs.c format.def format.h formats.c get.c getl.c getl.h glob.c \ +glob.h groff-font.c group.c group.h group_proc.h hash.c hash.h \ +histogram.c histogram.h html.c htmlP.h include.c inpt-pgm.c lexer.c \ +lexer.h lex-def.h lex-def.c levene.c levene.h linked-list.c \ linked-list.h log.h loop.c magic.c magic.h main.c main.h matrix-data.c \ mis-val.c misc.c misc.h missing-values.c missing-values.h \ modify-vars.c moments.c moments.h numeric.c output.c output.h \ percentiles.c percentiles.h permissions.c pfm-read.c pfm-read.h \ -pfm-write.c pfm-write.h pool.c pool.h postscript.c print.c recode.c \ -rename-vars.c repeat.c repeat.h sample.c sel-if.c settings.h \ +pfm-write.c pfm-write.h pool.c pool.h postscript.c print.c range-prs.c \ +recode.c rename-vars.c repeat.c repeat.h sample.c sel-if.c settings.h \ sfm-read.c sfm-read.h sfm-write.c sfm-write.h sfmP.h som.c som.h \ sort.c sort.h sort-prs.c sort-prs.h split-file.c str.c str.h \ subclist.c subclist.h sysfile-info.c tab.c tab.h temporary.c mkfile.c \ diff --git a/src/count.c b/src/count.c index 876c0793..38374fa6 100644 --- a/src/count.c +++ b/src/count.c @@ -26,100 +26,63 @@ #include "dictionary.h" #include "error.h" #include "lexer.h" +#include "pool.h" +#include "range-prs.h" #include "str.h" #include "var.h" #include "gettext.h" #define _(msgid) gettext (msgid) -/* Implementation details: - - The S?SS manuals do not specify the order that COUNT subcommands are - performed in. Experiments, however, have shown that they are performed - in the order that they are specified in, rather than simultaneously. - So, with the two variables A and B, and the two cases, - - A B - 1 2 - 2 1 - - the command COUNT A=A B (1) / B=A B (2) will produce the following - results, - - A B - 1 1 - 1 0 - - rather than the results that would be produced if subcommands were - simultaneous: - - A B - 1 1 - 1 1 - - Perhaps simultaneity could be implemented as an option. On the - other hand, what good are the above commands? */ - -/* Definitions. */ - -enum +/* Value or range? */ +enum value_type { - CNT_ERROR, /* Invalid value. */ CNT_SINGLE, /* Single value. */ - CNT_HIGH, /* x >= a. */ - CNT_LOW, /* x <= a. */ - CNT_RANGE, /* a <= x <= b. */ - CNT_ANY, /* Count any. */ - CNT_SENTINEL /* List terminator. */ + CNT_RANGE /* a <= x <= b. */ }; -struct cnt_num +/* Numeric count criteria. */ +struct num_value { - int type; - double a, b; + enum value_type type; /* How to interpret a, b. */ + double a, b; /* Values to count. */ }; -struct cnt_str +struct criteria { - int type; - char *s; - }; + struct criteria *next; -struct counting - { - struct counting *next; - - /* variables to count */ - struct variable **v; - size_t n; - - /* values to count */ - int missing; /* (numeric only) - 0=don't count missing, - 1=count SYSMIS, - 2=count system- and user-missing */ - union /* Criterion values. */ + /* Variables to count. */ + struct variable **vars; + size_t var_cnt; + + /* Count special values?. */ + bool count_system_missing; /* Count system missing? */ + bool count_user_missing; /* Count user missing? */ + + /* Criterion values. */ + size_t value_cnt; + union { - struct cnt_num *n; - struct cnt_str *s; + struct num_value *num; + char **str; } - crit; + values; }; -struct cnt_var_info +struct dst_var { - struct cnt_var_info *next; - - struct variable *d; /* Destination variable. */ - char n[LONG_NAME_LEN + 1]; /* Name of dest var. */ - - struct counting *c; /* The counting specifications. */ + struct dst_var *next; + struct variable *var; /* Destination variable. */ + char *name; /* Name of dest var. */ + struct criteria *crit; /* The criteria specifications. */ }; struct count_trns { struct trns_header h; - struct cnt_var_info *specs; + struct dst_var *dst_vars; + struct pool *pool; }; /* Parser. */ @@ -127,68 +90,76 @@ struct count_trns static trns_proc_func count_trns_proc; static trns_free_func count_trns_free; -static int parse_numeric_criteria (struct counting *); -static int parse_string_criteria (struct counting *); +static bool parse_numeric_criteria (struct pool *, struct criteria *); +static bool parse_string_criteria (struct pool *, struct criteria *); int cmd_count (void) { - struct cnt_var_info *cnt; /* Specification currently being parsed. */ - struct counting *c; /* Counting currently being parsed. */ - int ret; /* Return value from parsing function. */ + struct dst_var *dv; /* Destination var being parsed. */ struct count_trns *trns; /* Transformation. */ - struct cnt_var_info *head; /* First counting in chain. */ /* Parses each slash-delimited specification. */ - head = cnt = xmalloc (sizeof *cnt); + trns = xmalloc (sizeof *trns); + trns->h.proc = count_trns_proc; + trns->h.free = count_trns_free; + trns->pool = pool_create (); + trns->dst_vars = dv = pool_alloc (trns->pool, sizeof *dv); for (;;) { - /* Initialize this struct cnt_var_info to ensure proper cleanup. */ - cnt->next = NULL; - cnt->d = NULL; - cnt->c = NULL; + struct criteria *crit; + + /* Initialize this struct dst_var to ensure proper cleanup. */ + dv->next = NULL; + dv->var = NULL; + dv->crit = NULL; /* Get destination variable, or at least its name. */ if (!lex_force_id ()) goto fail; - cnt->d = dict_lookup_var (default_dict, tokid); - if (cnt->d) - { - if (cnt->d->type == ALPHA) - { - msg (SE, _("Destination cannot be a string variable.")); - goto fail; - } - } + dv->var = dict_lookup_var (default_dict, tokid); + if (dv->var != NULL) + { + if (dv->var->type == ALPHA) + { + msg (SE, _("Destination cannot be a string variable.")); + goto fail; + } + } else - str_copy_trunc (cnt->n, sizeof cnt->n, tokid); + dv->name = pool_strdup (trns->pool, tokid); lex_get (); if (!lex_force_match ('=')) goto fail; - c = cnt->c = xmalloc (sizeof *c); + crit = dv->crit = pool_alloc (trns->pool, sizeof *crit); for (;;) { - c->next = NULL; - c->v = NULL; - if (!parse_variables (default_dict, &c->v, &c->n, + bool ok; + + crit->next = NULL; + crit->vars = NULL; + if (!parse_variables (default_dict, &crit->vars, &crit->var_cnt, PV_DUPLICATE | PV_SAME_TYPE)) goto fail; + pool_register (trns->pool, free, crit->vars); if (!lex_force_match ('(')) goto fail; - ret = (c->v[0]->type == NUMERIC - ? parse_numeric_criteria - : parse_string_criteria) (c); - if (!ret) + crit->value_cnt = 0; + if (crit->vars[0]->type == NUMERIC) + ok = parse_numeric_criteria (trns->pool, crit); + else + ok = parse_string_criteria (trns->pool, crit); + if (!ok) goto fail; if (token == '/' || token == '.') break; - c = c->next = xmalloc (sizeof *c); + crit = crit->next = pool_alloc (trns->pool, sizeof *crit); } if (token == '.') @@ -196,163 +167,95 @@ cmd_count (void) if (!lex_force_match ('/')) goto fail; - cnt = cnt->next = xmalloc (sizeof *cnt); + dv = dv->next = pool_alloc (trns->pool, sizeof *dv); } /* Create all the nonexistent destination variables. */ - for (cnt = head; cnt; cnt = cnt->next) - if (!cnt->d) + for (dv = trns->dst_vars; dv; dv = dv->next) + if (dv->var == NULL) { /* It's valid, though motivationally questionable, to count to the same dest var more than once. */ - cnt->d = dict_lookup_var (default_dict, cnt->n); + dv->var = dict_lookup_var (default_dict, dv->name); - if (cnt->d == NULL) - cnt->d = dict_create_var_assert (default_dict, cnt->n, 0); + if (dv->var == NULL) + dv->var = dict_create_var_assert (default_dict, dv->name, 0); } - trns = xmalloc (sizeof *trns); - trns->h.proc = count_trns_proc; - trns->h.free = count_trns_free; - trns->specs = head; - add_transformation ((struct trns_header *) trns); - + add_transformation (&trns->h); return CMD_SUCCESS; fail: - { - struct count_trns t; - t.specs = head; - count_trns_free ((struct trns_header *) & t); - return CMD_FAILURE; - } + count_trns_free (&trns->h); + return CMD_FAILURE; } -/* Parses a set of numeric criterion values. */ -static int -parse_numeric_criteria (struct counting *c) +/* Parses a set of numeric criterion values. Returns success. */ +static bool +parse_numeric_criteria (struct pool *pool, struct criteria *crit) { - size_t n = 0; - size_t m = 0; + size_t allocated = 0; - c->crit.n = 0; - c->missing = 0; + crit->values.num = NULL; + crit->count_system_missing = false; + crit->count_user_missing = false; for (;;) { - struct cnt_num *cur; - if (n + 1 >= m) - { - m += 16; - c->crit.n = xnrealloc (c->crit.n, m, sizeof *c->crit.n); - } - - cur = &c->crit.n[n++]; - if (lex_is_number ()) - { - cur->a = tokval; - lex_get (); - if (lex_match_id ("THRU")) - { - if (lex_is_number ()) - { - if (!lex_force_num ()) - return 0; - cur->b = tokval; - cur->type = CNT_RANGE; - lex_get (); - - if (cur->a > cur->b) - { - msg (SE, _("%g THRU %g is not a valid range. The " - "number following THRU must be at least " - "as big as the number preceding THRU."), - cur->a, cur->b); - return 0; - } - } - else if (lex_match_id ("HI") || lex_match_id ("HIGHEST")) - cur->type = CNT_HIGH; - else - { - lex_error (NULL); - return 0; - } - } - else - cur->type = CNT_SINGLE; - } - else if (lex_match_id ("LO") || lex_match_id ("LOWEST")) - { - if (!lex_force_match_id ("THRU")) - return 0; - if (lex_is_number ()) - { - cur->type = CNT_LOW; - cur->a = tokval; - lex_get (); - } - else if (lex_match_id ("HI") || lex_match_id ("HIGHEST")) - cur->type = CNT_ANY; - else - { - lex_error (NULL); - return 0; - } - } - else if (lex_match_id ("SYSMIS")) - { - if (c->missing < 1) - c->missing = 1; - } + double low, high; + + if (lex_match_id ("SYSMIS")) + crit->count_system_missing = true; else if (lex_match_id ("MISSING")) - c->missing = 2; + crit->count_user_missing = true; + else if (parse_num_range (&low, &high, NULL)) + { + struct num_value *cur; + + if (crit->value_cnt >= allocated) + crit->values.num = pool_2nrealloc (pool, crit->values.num, + &allocated, + sizeof *crit->values.num); + cur = &crit->values.num[crit->value_cnt++]; + cur->type = low == high ? CNT_SINGLE : CNT_RANGE; + cur->a = low; + cur->b = high; + } else - { - lex_error (NULL); - return 0; - } + return false; lex_match (','); if (lex_match (')')) break; } - - c->crit.n[n].type = CNT_SENTINEL; - return 1; + return true; } -/* Parses a set of string criteria values. The skeleton is the same - as parse_numeric_criteria(). */ -static int -parse_string_criteria (struct counting *c) +/* Parses a set of string criteria values. Returns success. */ +static bool +parse_string_criteria (struct pool *pool, struct criteria *crit) { int len = 0; - - size_t n = 0; - size_t m = 0; - + size_t allocated = 0; size_t i; - for (i = 0; i < c->n; i++) - if (c->v[i]->width > len) - len = c->v[i]->width; + for (i = 0; i < crit->var_cnt; i++) + if (crit->vars[i]->width > len) + len = crit->vars[i]->width; - c->crit.n = 0; + crit->values.str = NULL; for (;;) { - struct cnt_str *cur; - if (n + 1 >= m) - { - m += 16; - c->crit.s = xnrealloc (c->crit.s, m, sizeof *c->crit.s); - } + char **cur; + if (crit->value_cnt >= allocated) + crit->values.str = pool_2nrealloc (pool, crit->values.str, + &allocated, + sizeof *crit->values.str); if (!lex_force_string ()) - return 0; - cur = &c->crit.s[n++]; - cur->type = CNT_SINGLE; - cur->s = malloc (len + 1); - str_copy_rpad (cur->s, len + 1, ds_c_str (&tokstr)); + return false; + cur = &crit->values.str[crit->value_cnt++]; + *cur = pool_alloc (pool, len + 1); + str_copy_rpad (*cur, len + 1, ds_c_str (&tokstr)); lex_get (); lex_match (','); @@ -360,164 +263,93 @@ parse_string_criteria (struct counting *c) break; } - c->crit.s[n].type = CNT_SENTINEL; - return 1; + return true; } /* Transformation. */ -/* Counts the number of values in case C matching counting CNT. */ +/* Counts the number of values in case C matching CRIT. */ static inline int -count_numeric (struct counting *cnt, struct ccase *c) +count_numeric (struct criteria *crit, struct ccase *c) { int counter = 0; size_t i; - for (i = 0; i < cnt->n; i++) + for (i = 0; i < crit->var_cnt; i++) { - struct cnt_num *num; - - /* Extract the variable value and eliminate missing values. */ - double cmp = case_num (c, cnt->v[i]->fv); - if (cmp == SYSMIS) - { - if (cnt->missing >= 1) - counter++; - continue; - } - if (cnt->missing >= 2 && mv_is_num_user_missing (&cnt->v[i]->miss, cmp)) - { - counter++; - continue; - } - - /* Try to find the value in the list. */ - for (num = cnt->crit.n;; num++) - switch (num->type) - { - case CNT_ERROR: - assert (0); - break; - case CNT_SINGLE: - if (cmp != num->a) - break; - counter++; - goto done; - case CNT_HIGH: - if (cmp < num->a) - break; - counter++; - goto done; - case CNT_LOW: - if (cmp > num->a) - break; - counter++; - goto done; - case CNT_RANGE: - if (cmp < num->a || cmp > num->b) - break; - counter++; - goto done; - case CNT_ANY: - counter++; - goto done; - case CNT_SENTINEL: - goto done; - default: - assert (0); - } - done: ; + double x = case_num (c, crit->vars[i]->fv); + if (x == SYSMIS) + counter += crit->count_system_missing; + else if (crit->count_user_missing + && mv_is_num_user_missing (&crit->vars[i]->miss, x)) + counter++; + else + { + struct num_value *v; + + for (v = crit->values.num; v < crit->values.num + crit->value_cnt; + v++) + if (v->type == CNT_SINGLE ? x == v->a : x >= v->a && x <= v->b) + { + counter++; + break; + } + } } + return counter; } -/* Counts the number of values in case C matching counting CNT. */ +/* Counts the number of values in case C matching CRIT. */ static inline int -count_string (struct counting *cnt, struct ccase *c) +count_string (struct criteria *crit, struct ccase *c) { int counter = 0; size_t i; - for (i = 0; i < cnt->n; i++) + for (i = 0; i < crit->var_cnt; i++) { - struct cnt_str *str; - - /* Extract the variable value, variable width. */ - for (str = cnt->crit.s;; str++) - switch (str->type) - { - case CNT_ERROR: - assert (0); - case CNT_SINGLE: - if (memcmp (case_str (c, cnt->v[i]->fv), str->s, - cnt->v[i]->width)) - break; + char **v; + for (v = crit->values.str; v < crit->values.str + crit->value_cnt; v++) + if (!memcmp (case_str (c, crit->vars[i]->fv), *v, + crit->vars[i]->width)) + { counter++; - goto done; - case CNT_SENTINEL: - goto done; - default: - assert (0); - } - done: ; + break; + } } + return counter; } /* Performs the COUNT transformation T on case C. */ static int -count_trns_proc (struct trns_header *trns, struct ccase *c, +count_trns_proc (struct trns_header *trns_, struct ccase *c, int case_num UNUSED) { - struct cnt_var_info *info; - struct counting *cnt; - int counter; + struct count_trns *trns = (struct count_trns *) trns_; + struct dst_var *dv; - for (info = ((struct count_trns *) trns)->specs; info; info = info->next) + for (dv = trns->dst_vars; dv; dv = dv->next) { + struct criteria *crit; + int counter; + counter = 0; - for (cnt = info->c; cnt; cnt = cnt->next) - if (cnt->v[0]->type == NUMERIC) - counter += count_numeric (cnt, c); + for (crit = dv->crit; crit; crit = crit->next) + if (crit->vars[0]->type == NUMERIC) + counter += count_numeric (crit, c); else - counter += count_string (cnt, c); - case_data_rw (c, info->d->fv)->f = counter; + counter += count_string (crit, c); + case_data_rw (c, dv->var->fv)->f = counter; } return -1; } -/* Destroys all dynamic data structures associated with T. */ +/* Destroys all dynamic data structures associated with TRNS. */ static void -count_trns_free (struct trns_header *t) +count_trns_free (struct trns_header *trns_) { - struct cnt_var_info *iter, *next; - - for (iter = ((struct count_trns *) t)->specs; iter; iter = next) - { - struct counting *i, *n; - - for (i = iter->c; i; i = n) - { - if (i->n && i->v) - { - if (i->v[0]->type == NUMERIC) - free (i->crit.n); - else - { - struct cnt_str *s; - - for (s = i->crit.s; s->type != CNT_SENTINEL; s++) - free (s->s); - free (i->crit.s); - } - } - free (i->v); - - n = i->next; - free (i); - } - - next = iter->next; - free (iter); - } + struct count_trns *trns = (struct count_trns *) trns_; + pool_destroy (trns->pool); } diff --git a/src/mis-val.c b/src/mis-val.c index 3db00110..7aedc10b 100644 --- a/src/mis-val.c +++ b/src/mis-val.c @@ -25,6 +25,7 @@ #include "error.h" #include "lexer.h" #include "magic.h" +#include "range-prs.h" #include "str.h" #include "var.h" @@ -33,8 +34,6 @@ #include "debug-print.h" -static bool parse_number (double *, const struct fmt_spec *); - int cmd_missing_values (void) { @@ -80,40 +79,17 @@ cmd_missing_values (void) mv_init (&mv, 0); while (!lex_match (')')) { - double x; + double x, y; + bool ok; - if (lex_match_id ("LO") || lex_match_id ("LOWEST")) - x = LOWEST; - else if (!parse_number (&x, &v[0]->print)) + if (!parse_num_range (&x, &y, &v[0]->print)) goto done; - - if (lex_match_id ("THRU")) - { - double y; - - if (lex_match_id ("HI") || lex_match_id ("HIGHEST")) - y = HIGHEST; - else if (!parse_number (&y, &v[0]->print)) - goto done; - - if (x == LOWEST && y == HIGHEST) - { - msg (SE, _("LO THRU HI is an invalid range.")); - deferred_errors = true; - } - else if (!mv_add_num_range (&mv, x, y)) - deferred_errors = true; - } - else - { - if (x == LOWEST) - { - msg (SE, _("LO or LOWEST must be part of a range.")); - deferred_errors = true; - } - else if (!mv_add_num (&mv, x)) - deferred_errors = true; - } + + ok = (x == y + ? mv_add_num (&mv, x) + : mv_add_num_range (&mv, x, y)); + if (!ok) + deferred_errors = true; lex_match (','); } @@ -177,35 +153,3 @@ cmd_missing_values (void) return retval; } -static bool -parse_number (double *x, const struct fmt_spec *f) -{ - if (lex_is_number ()) - { - *x = lex_number (); - lex_get (); - return true; - } - else if (token == T_STRING) - { - struct data_in di; - union value v; - di.s = ds_data (&tokstr); - di.e = ds_end (&tokstr); - di.v = &v; - di.flags = 0; - di.f1 = 1; - di.f2 = ds_length (&tokstr); - di.format = *f; - data_in (&di); - lex_get (); - *x = v.f; - return true; - } - else - { - lex_error (_("expecting number or data string")); - return false; - } -} - diff --git a/src/missing-values.c b/src/missing-values.c index bda35b20..2a5b8fa4 100644 --- a/src/missing-values.c +++ b/src/missing-values.c @@ -107,11 +107,13 @@ mv_add_num (struct missing_values *mv, double d) /* Attempts to add range [LOW, HIGH] to the set of numeric missing values MV. Returns true if successful, false if MV - has no room for a range. */ + has no room for a range, or if LOW > HIGH. */ bool mv_add_num_range (struct missing_values *mv, double low, double high) { assert (mv->width == 0); + if (low > high) + return false; switch (mv->type) { case MV_NONE: diff --git a/src/pool.c b/src/pool.c index 4d34c8fe..0f6b11e0 100644 --- a/src/pool.c +++ b/src/pool.c @@ -19,10 +19,11 @@ #include #include "pool.h" -#include "command.h" -#include "error.h" #include #include "alloc.h" +#include "command.h" +#include "error.h" +#include "size_max.h" #include "str.h" /* Fast, low-overhead memory block suballocator. */ @@ -347,7 +348,7 @@ pool_strdup (struct pool *pool, const char *string) /* Allocates AMT bytes using malloc(), to be managed by POOL, and returns a pointer to the beginning of the block. If POOL is a null pointer, then allocates a normal memory block - with malloc(). */ + with xmalloc(). */ void * pool_malloc (struct pool *pool, size_t amt) { @@ -440,6 +441,100 @@ pool_nrealloc (struct pool *pool, void *p, size_t n, size_t s) return pool_realloc (pool, p, n * s); } +/* If P is null, allocate a block of at least *PN such objects; + otherwise, reallocate P so that it contains more than *PN + objects each of S bytes. *PN must be nonzero unless P is + null, and S must be nonzero. Set *PN to the new number of + objects, and return the pointer to the new block. *PN is + never set to zero, and the returned pointer is never null. + + The block returned is managed by POOL. If POOL is a null + pointer, then the block is reallocated in the usual way with + x2nrealloc(). + + Terminates the program if the memory cannot be obtained, + including the case where the memory required overflows the + range of size_t. + + Repeated reallocations are guaranteed to make progress, either by + allocating an initial block with a nonzero size, or by allocating a + larger block. + + In the following implementation, nonzero sizes are doubled so that + repeated reallocations have O(N log N) overall cost rather than + O(N**2) cost, but the specification for this function does not + guarantee that sizes are doubled. + + Here is an example of use: + + int *p = NULL; + struct pool *pool; + size_t used = 0; + size_t allocated = 0; + + void + append_int (int value) + { + if (used == allocated) + p = pool_2nrealloc (pool, p, &allocated, sizeof *p); + p[used++] = value; + } + + This causes x2nrealloc to allocate a block of some nonzero size the + first time it is called. + + To have finer-grained control over the initial size, set *PN to a + nonzero value before calling this function with P == NULL. For + example: + + int *p = NULL; + struct pool *pool; + size_t used = 0; + size_t allocated = 0; + size_t allocated1 = 1000; + + void + append_int (int value) + { + if (used == allocated) + { + p = pool_2nrealloc (pool, p, &allocated1, sizeof *p); + allocated = allocated1; + } + p[used++] = value; + } + + This function implementation is from gnulib. */ +void * +pool_2nrealloc (struct pool *pool, void *p, size_t *pn, size_t s) +{ + size_t n = *pn; + + if (p == NULL) + { + if (n == 0) + { + /* The approximate size to use for initial small allocation + requests, when the invoking code specifies an old size of + zero. 64 bytes is the largest "small" request for the + GNU C library malloc. */ + enum { DEFAULT_MXFAST = 64 }; + + n = DEFAULT_MXFAST / s; + n += !n; + } + } + else + { + if (SIZE_MAX / 2 / s < n) + xalloc_die (); + n *= 2; + } + + *pn = n; + return pool_realloc (pool, p, n * s); +} + /* Frees block P managed by POOL. If POOL is a null pointer, then the block is freed as usual with free(). */ diff --git a/src/pool.h b/src/pool.h index e2e90c26..ff589843 100644 --- a/src/pool.h +++ b/src/pool.h @@ -51,6 +51,7 @@ void *pool_malloc (struct pool *, size_t) MALLOC_LIKE; void *pool_nmalloc (struct pool *, size_t n, size_t s) MALLOC_LIKE; void *pool_realloc (struct pool *, void *, size_t); void *pool_nrealloc (struct pool *, void *, size_t n, size_t s); +void *pool_2nrealloc (struct pool *, void *, size_t *pn, size_t s); void pool_free (struct pool *, void *); /* Gizmo allocations. */ diff --git a/src/range-prs.c b/src/range-prs.c new file mode 100644 index 00000000..b4e55b9b --- /dev/null +++ b/src/range-prs.c @@ -0,0 +1,111 @@ +#include +#include "range-prs.h" +#include +#include "data-in.h" +#include "error.h" +#include "lexer.h" +#include "magic.h" +#include "str.h" +#include "val.h" + +#include "gettext.h" +#define _(msgid) gettext (msgid) +#define N_(msgid) msgid + +static bool parse_number (double *, const struct fmt_spec *); + +/* Parses and stores a numeric value, or a range of the form "x + THRU y". Open-ended ranges may be specified as "LO(WEST) THRU + y" or "x THRU HI(GHEST)". Sets *X and *Y to the range or the + value and returns success. + + Numeric values are always accepted. If F is nonnull, then + string values are also accepted, and converted to numeric + values using the specified format. */ +bool +parse_num_range (double *x, double *y, const struct fmt_spec *f) +{ + if (lex_match_id ("LO") || lex_match_id ("LOWEST")) + *x = LOWEST; + else if (!parse_number (x, f)) + return false; + + if (lex_match_id ("THRU")) + { + if (lex_match_id ("HI") || lex_match_id ("HIGHEST")) + *y = HIGHEST; + else if (!parse_number (y, f)) + return false; + + if (*y < *x) + { + double t; + msg (SW, _("Low end of range (%g) is below high end (%g). " + "The range will be treated as reversed."), + *x, *y); + t = *x; + *x = *y; + *y = t; + } + else if (*x == *y) + msg (SW, _("Ends of range are equal (%g)."), *x); + + return true; + } + else + { + if (*x == LOWEST) + { + msg (SE, _("LO or LOWEST must be part of a range.")); + return false; + } + *y = *x; + } + + return true; +} + +/* Parses a number and stores it in *X. Returns success. + + Numeric values are always accepted. If F is nonnull, then + string values are also accepted, and converted to numeric + values using the specified format. */ +static bool +parse_number (double *x, const struct fmt_spec *f) +{ + if (lex_is_number ()) + { + *x = lex_number (); + lex_get (); + return true; + } + else if (token == T_STRING && f != NULL) + { + struct data_in di; + union value v; + di.s = ds_data (&tokstr); + di.e = ds_end (&tokstr); + di.v = &v; + di.flags = 0; + di.f1 = 1; + di.f2 = ds_length (&tokstr); + di.format = *f; + data_in (&di); + lex_get (); + *x = v.f; + if (*x == SYSMIS) + { + lex_error (_("System-missing value is not valid here.")); + return false; + } + return true; + } + else + { + if (f != NULL) + lex_error (_("expecting number or data string")); + else + lex_force_num (); + return false; + } +} diff --git a/src/range-prs.h b/src/range-prs.h new file mode 100644 index 00000000..f03a7e88 --- /dev/null +++ b/src/range-prs.h @@ -0,0 +1,28 @@ +/* PSPP - computes sample statistics. + Copyright (C) 2005 Free Software Foundation, Inc. + Written by Ben Pfaff . + + 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#ifndef RANGE_PRS_H +#define RANGE_PRS_H 1 + +#include + +struct fmt_spec; +bool parse_num_range (double *x, double *y, const struct fmt_spec *fmt); + +#endif /* range-prs.h */ -- 2.30.2