X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fflip.c;h=204982357b043ddb45eddec19502b43c9bbf79f0;hb=d594340cb1ae007a92d094ae67116f9f622f2b5d;hp=de02119eb485166e7b45d57b2bc89880b59acd5b;hpb=43a6f9c7bb02e24dfb565babcb52ddf3d6a0fe52;p=pspp-builds.git diff --git a/src/flip.c b/src/flip.c index de02119e..20498235 100644 --- a/src/flip.c +++ b/src/flip.c @@ -14,104 +14,172 @@ 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. */ + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. */ -#include -#include +#include "config.h" +#include "error.h" #include #include #include #include #include +#include "algorithm.h" #include "alloc.h" +#include "case.h" #include "command.h" +#include "dictionary.h" #include "error.h" #include "lexer.h" #include "misc.h" +#include "settings.h" #include "str.h" +#include "val.h" #include "var.h" #include "vfm.h" -/* Variables to transpose. */ -static struct variable **var; -static int nvar; +#ifdef HAVE_SYS_TYPES_H +#include +#endif -/* Variable containing new variable names. */ -static struct variable *newnames; +#include "gettext.h" +#define _(msgid) gettext (msgid) /* List of variable names. */ struct varname { struct varname *next; - char name[9]; + char name[SHORT_NAME_LEN + 1]; + }; + +/* Represents a FLIP input program. */ +struct flip_pgm + { + struct variable **var; /* Variables to transpose. */ + int *idx_to_fv; /* var[]->index to compacted sink case fv. */ + size_t var_cnt; /* Number of elements in `var'. */ + int case_cnt; /* Pre-flip case count. */ + size_t case_size; /* Post-flip bytes per case. */ + + struct variable *new_names; /* Variable containing new variable names. */ + struct varname *new_names_head; /* First new variable. */ + struct varname *new_names_tail; /* Last new variable. */ + + FILE *file; /* Temporary file containing data. */ }; -/* New variable names. */ -static struct varname *new_names_head, *new_names_tail; -static int case_count; +static void destroy_flip_pgm (struct flip_pgm *); +static struct case_sink *flip_sink_create (struct flip_pgm *); +static struct case_source *flip_source_create (struct flip_pgm *); +static void flip_file (struct flip_pgm *); +static int build_dictionary (struct flip_pgm *); -static int build_dictionary (void); +static const struct case_source_class flip_source_class; +static const struct case_sink_class flip_sink_class; /* Parses and executes FLIP. */ int cmd_flip (void) { - lex_match_id ("FLIP"); + struct flip_pgm *flip; + + if (temporary != 0) + { + msg (SM, _("FLIP ignores TEMPORARY. " + "Temporary transformations will be made permanent.")); + cancel_temporary (); + } + + flip = xmalloc (sizeof *flip); + flip->var = NULL; + flip->idx_to_fv = dict_get_compacted_idx_to_fv (default_dict); + flip->var_cnt = 0; + flip->case_cnt = 0; + flip->new_names = NULL; + flip->new_names_head = NULL; + flip->new_names_tail = NULL; + flip->file = NULL; + lex_match ('/'); if (lex_match_id ("VARIABLES")) { lex_match ('='); - if (!parse_variables (default_dict, &var, &nvar, PV_NO_DUPLICATE)) + if (!parse_variables (default_dict, &flip->var, &flip->var_cnt, PV_NO_DUPLICATE)) return CMD_FAILURE; lex_match ('/'); } else - dict_get_vars (default_dict, &var, &nvar, 1u << DC_SYSTEM); + dict_get_vars (default_dict, &flip->var, &flip->var_cnt, 1u << DC_SYSTEM); lex_match ('/'); if (lex_match_id ("NEWNAMES")) { lex_match ('='); - newnames = parse_variable (); - if (!newnames) - { - free (var); - return CMD_FAILURE; - } + flip->new_names = parse_variable (); + if (!flip->new_names) + goto error; } else - newnames = dict_lookup_var (default_dict, "CASE_LBL"); + flip->new_names = dict_lookup_var (default_dict, "CASE_LBL"); - if (newnames) + if (flip->new_names) { - int i; + size_t i; - for (i = 0; i < nvar; i++) - if (var[i] == newnames) + for (i = 0; i < flip->var_cnt; i++) + if (flip->var[i] == flip->new_names) { - memmove (&var[i], &var[i + 1], sizeof *var * (nvar - i - 1)); - nvar--; + remove_element (flip->var, flip->var_cnt, sizeof *flip->var, i); + flip->var_cnt--; break; } } - case_count = 0; + /* Read the active file into a flip_sink. */ + flip->case_cnt = 0; temp_trns = temporary = 0; - vfm_sink = &flip_stream; - new_names_tail = NULL; - procedure (NULL, NULL, NULL); + vfm_sink = flip_sink_create (flip); + flip->new_names_tail = NULL; + procedure (NULL, NULL); + + /* Flip the data we read. */ + flip_file (flip); + /* Flip the dictionary. */ dict_clear (default_dict); - if (!build_dictionary ()) + if (!build_dictionary (flip)) { discard_variables (); - free (var); - return CMD_FAILURE; + goto error; } + flip->case_size = dict_get_case_size (default_dict); + + /* Set up flipped data for reading. */ + vfm_source = flip_source_create (flip); - free (var); return lex_end_of_command (); + + error: + destroy_flip_pgm (flip); + return CMD_FAILURE; +} + +/* Destroys FLIP. */ +static void +destroy_flip_pgm (struct flip_pgm *flip) +{ + struct varname *iter, *next; + + free (flip->var); + free (flip->idx_to_fv); + for (iter = flip->new_names_head; iter != NULL; iter = next) + { + next = iter->next; + free (iter); + } + if (flip->file != NULL) + fclose (flip->file); + free (flip); } /* Make a new variable with base name NAME, which is bowdlerized and @@ -119,33 +187,35 @@ cmd_flip (void) static int make_new_var (char name[]) { + char *cp; + + /* Trim trailing spaces. */ + cp = strchr (name, '\0'); + while (cp > name && isspace ((unsigned char) cp[-1])) + *--cp = '\0'; + /* Fix invalid characters. */ - { - char *cp; - - for (cp = name; *cp && !isspace (*cp); cp++) + for (cp = name; *cp && cp < name + SHORT_NAME_LEN; cp++) + if (cp == name) { - *cp = toupper ((unsigned char) *cp); - if (!isalpha (*cp) && *cp != '@' && *cp != '#' - && (cp == name || (*cp != '.' && *cp != '$' && *cp != '_' - && !isdigit (*cp)))) - { - if (cp == name) - *cp = 'V'; /* _ not valid in first position. */ - else - *cp = '_'; - } + if (!CHAR_IS_ID1 (*cp) || *cp == '$') + *cp = 'V'; } - *cp = 0; - } + else + { + if (!CHAR_IS_IDN (*cp)) + *cp = '_'; + } + *cp = '\0'; + str_uppercase (name); if (dict_create_var (default_dict, name, 0)) return 1; /* Add numeric extensions until acceptable. */ { - int len = (int) strlen (name); - char n[9]; + const int len = (int) strlen (name); + char n[SHORT_NAME_LEN + 1]; int i; for (i = 1; i < 10000000; i++) @@ -165,154 +235,92 @@ make_new_var (char name[]) /* Make a new dictionary for all the new variable names. */ static int -build_dictionary (void) +build_dictionary (struct flip_pgm *flip) { - if (!dict_create_var (default_dict, "CASE_LBL", 8)) - assert (0); + dict_create_var_assert (default_dict, "CASE_LBL", 8); - if (!new_names_tail) + if (flip->new_names_head == NULL) { int i; - if (case_count > 99999) + if (flip->case_cnt > 99999) { msg (SE, _("Cannot create more than 99999 variable names.")); return 0; } - for (i = 0; i < case_count; i++) + for (i = 0; i < flip->case_cnt; i++) { struct variable *v; - char s[9]; + char s[SHORT_NAME_LEN + 1]; sprintf (s, "VAR%03d", i); - v = dict_create_var (default_dict, s, 0); - assert (v != NULL); + v = dict_create_var_assert (default_dict, s, 0); } } else { - struct varname *v, *n; + struct varname *v; - new_names_tail->next = NULL; - for (v = new_names_head; v; v = n) - { - n = v->next; - if (!make_new_var (v->name)) - { - for (; v; v = n) - { - n = v->next; - free (v); - } - return 0; - } - free (v); - } + for (v = flip->new_names_head; v; v = v->next) + if (!make_new_var (v->name)) + return 0; } return 1; } - -/* Each case to be transposed. */ -struct flip_case +/* Cases during transposition. */ +struct flip_sink_info { - struct flip_case *next; - double v[1]; + struct flip_pgm *flip; /* FLIP program. */ + union value *output_buf; /* Case output buffer. */ }; -/* Sink: Cases during transposition. */ -static int internal; /* Internal vs. external flipping. */ -static char *sink_old_names; /* Old variable names. */ -static unsigned long sink_cases; /* Number of cases. */ -static struct flip_case *head, *tail; /* internal == 1: Cases. */ -static FILE *sink_file; /* internal == 0: Temporary file. */ - -/* Source: Cases after transposition. */ -static struct flip_case *src; /* Internal transposition records. */ -static char *src_old_names; /* Old variable names. */ -static unsigned long src_cases; /* Number of cases. */ -static FILE *src_file; /* src == NULL: Temporary file. */ - -/* Initialize the FLIP stream. */ -static void -flip_stream_init (void) +/* Creates a flip sink based on FLIP. */ +static struct case_sink * +flip_sink_create (struct flip_pgm *flip) { - internal = 1; - sink_cases = 0; - tail = NULL; - - { - size_t n = nvar; - char *p; - int i; - - for (i = 0; i < nvar; i++) - n += strlen (var[i]->name); - p = sink_old_names = xmalloc (n); - for (i = 0; i < nvar; i++) - p = stpcpy (p, var[i]->name) + 1; - } -} + struct flip_sink_info *info = xmalloc (sizeof *info); + size_t i; -/* Reads the FLIP stream and passes it to write_case(). */ -static void -flip_stream_read (void) -{ - if (src || (src == NULL && src_file == NULL)) - { - /* Internal transposition, or empty file. */ - int i, j; - char *p = src_old_names; - - for (i = 0; i < nvar; i++) - { - struct flip_case *iter; - - st_bare_pad_copy (temp_case->data[0].s, p, 8); - p = strchr (p, 0) + 1; + info->flip = flip; + info->output_buf = xnmalloc (flip->var_cnt, sizeof *info->output_buf); - for (iter = src, j = 1; iter; iter = iter->next, j++) - temp_case->data[j].f = iter->v[i]; + flip->file = tmpfile (); + if (!flip->file) + msg (FE, _("Could not create temporary file for FLIP.")); - if (!write_case ()) - return; - } - } - else - { - int i; - char *p = src_old_names; - - for (i = 0; i < nvar; i++) - { - st_bare_pad_copy (temp_case->data[0].s, p, 8); - p = strchr (p, 0) + 1; + /* Write variable names as first case. */ + for (i = 0; i < flip->var_cnt; i++) + buf_copy_str_rpad (info->output_buf[i].s, MAX_SHORT_STRING, + flip->var[i]->name); + if (fwrite (info->output_buf, sizeof *info->output_buf, + flip->var_cnt, flip->file) != (size_t) flip->var_cnt) + msg (FE, _("Error writing FLIP file: %s."), strerror (errno)); - if (fread (&temp_case->data[1], sizeof (double), src_cases, - src_file) != src_cases) - msg (FE, _("Error reading FLIP source file: %s."), - strerror (errno)); + flip->case_cnt = 1; - if (!write_case ()) - return; - } - } + return create_case_sink (&flip_sink_class, default_dict, info); } -/* Writes temp_case to the FLIP stream. */ +/* Writes case C to the FLIP sink. */ static void -flip_stream_write (void) +flip_sink_write (struct case_sink *sink, const struct ccase *c) { - sink_cases++; + struct flip_sink_info *info = sink->aux; + struct flip_pgm *flip = info->flip; + size_t i; + + flip->case_cnt++; - if (newnames) + if (flip->new_names != NULL) { - struct varname *v = xmalloc (sizeof (struct varname)); - if (newnames->type == NUMERIC) + struct varname *v = xmalloc (sizeof *v); + v->next = NULL; + if (flip->new_names->type == NUMERIC) { - double f = temp_case->data[newnames->fv].f; + double f = case_num (c, flip->idx_to_fv[flip->new_names->index]); if (f == SYSMIS) strcpy (v->name, "VSYSMIS"); @@ -324,242 +332,212 @@ flip_stream_write (void) { char name[INT_DIGITS + 2]; sprintf (name, "V%d", (int) f); - strncpy (v->name, name, 8); - name[8] = 0; + str_copy_trunc (v->name, sizeof v->name, name); } } else { - int width = min (newnames->width, 8); - memcpy (v->name, temp_case->data[newnames->fv].s, width); + int width = min (flip->new_names->width, MAX_SHORT_STRING); + memcpy (v->name, case_str (c, flip->idx_to_fv[flip->new_names->index]), + width); v->name[width] = 0; } - if (new_names_tail == NULL) - new_names_head = v; + if (flip->new_names_head == NULL) + flip->new_names_head = v; else - new_names_tail->next = v; - new_names_tail = v; + flip->new_names_tail->next = v; + flip->new_names_tail = v; } - else - case_count++; - if (internal) + /* Write to external file. */ + for (i = 0; i < flip->var_cnt; i++) { -#if 0 - flip_case *c = malloc (sizeof (flip_case) - + sizeof (double) * (nvar - 1)); + double out; - if (c != NULL) - { - /* Write to internal file. */ - int i; - - for (i = 0; i < nvar; i++) - if (var[i]->type == NUMERIC) - c->v[i] = temp_case->data[var[i]->fv].f; - else - c->v[i] = SYSMIS; - - if (tail == NULL) - head = c; - else - tail->next = c; - tail = c; - - return; - } + if (flip->var[i]->type == NUMERIC) + out = case_num (c, flip->idx_to_fv[flip->var[i]->index]); else -#endif - { - /* Initialize external file. */ - struct flip_case *iter, *next; - - internal = 0; - - sink_file = tmpfile (); - if (!sink_file) - msg (FE, _("Could not create temporary file for FLIP.")); - - if (tail) - tail->next = NULL; - for (iter = head; iter; iter = next) - { - next = iter->next; - - if (fwrite (iter->v, sizeof (double), nvar, sink_file) - != (size_t) nvar) - msg (FE, _("Error writing FLIP file: %s."), - strerror (errno)); - free (iter); - } - } + out = SYSMIS; + info->output_buf[i].f = out; } - - /* Write to external file. */ - { - double *d = local_alloc (sizeof *d * nvar); - int i; - - for (i = 0; i < nvar; i++) - if (var[i]->type == NUMERIC) - d[i] = temp_case->data[var[i]->fv].f; - else - d[i] = SYSMIS; - if (fwrite (d, sizeof *d, nvar, sink_file) != (size_t) nvar) - msg (FE, _("Error writing FLIP file: %s."), - strerror (errno)); - - local_free (d); - } + if (fwrite (info->output_buf, sizeof *info->output_buf, + flip->var_cnt, flip->file) != (size_t) flip->var_cnt) + msg (FE, _("Error writing FLIP file: %s."), strerror (errno)); } - -/* Transpose the external file. */ + +/* Transposes the external file into a new file. */ static void -transpose_external_file (void) +flip_file (struct flip_pgm *flip) { - unsigned long n_cases; - unsigned long cur_case; - double *case_buf, *temp_buf; - - n_cases = 4 * 1024 * 1024 / ((nvar + 1) * sizeof *case_buf); - if (n_cases < 2) - n_cases = 2; + size_t case_bytes; + size_t case_capacity; + size_t case_idx; + union value *input_buf, *output_buf; + FILE *input_file, *output_file; + + /* Allocate memory for many cases. */ + case_bytes = flip->var_cnt * sizeof *input_buf; + case_capacity = get_workspace () / case_bytes; + if (case_capacity > flip->case_cnt * 2) + case_capacity = flip->case_cnt * 2; + if (case_capacity < 2) + case_capacity = 2; for (;;) { - assert (n_cases >= 2 /* 1 */); - case_buf = ((n_cases <= 2 ? xmalloc : (void *(*)(size_t)) malloc) - ((nvar + 1) * sizeof *case_buf * n_cases)); - if (case_buf) + size_t bytes = case_bytes * case_capacity; + if (case_capacity > 2) + input_buf = malloc (bytes); + else + input_buf = xmalloc (bytes); + if (input_buf != NULL) break; - n_cases /= 2; - if (n_cases < 2) - n_cases = 2; + case_capacity /= 2; + if (case_capacity < 2) + case_capacity = 2; } - /* A temporary buffer that holds n_cases elements. */ - temp_buf = &case_buf[nvar * n_cases]; + /* Use half the allocated memory for input_buf, half for + output_buf. */ + case_capacity /= 2; + output_buf = input_buf + flip->var_cnt * case_capacity; - src_file = tmpfile (); - if (!src_file) - msg (FE, _("Error creating FLIP source file.")); - - if (fseek (sink_file, 0, SEEK_SET) != 0) + input_file = flip->file; + if (fseek (input_file, 0, SEEK_SET) != 0) msg (FE, _("Error rewinding FLIP file: %s."), strerror (errno)); - for (cur_case = 0; cur_case < sink_cases; ) + output_file = tmpfile (); + if (output_file == NULL) + msg (FE, _("Error creating FLIP source file.")); + + for (case_idx = 0; case_idx < flip->case_cnt; ) { - unsigned long read_cases = min (sink_cases - cur_case, n_cases); - int i; + unsigned long read_cases = min (flip->case_cnt - case_idx, + case_capacity); + size_t i; - if (read_cases != fread (case_buf, sizeof *case_buf * nvar, - read_cases, sink_file)) + if (read_cases != fread (input_buf, case_bytes, read_cases, input_file)) msg (FE, _("Error reading FLIP file: %s."), strerror (errno)); - for (i = 0; i < nvar; i++) + for (i = 0; i < flip->var_cnt; i++) { unsigned long j; for (j = 0; j < read_cases; j++) - temp_buf[j] = case_buf[i + j * nvar]; + output_buf[j] = input_buf[i + j * flip->var_cnt]; - if (fseek (src_file, - sizeof *case_buf * (cur_case + i * sink_cases), - SEEK_SET) != 0) +#ifndef HAVE_FSEEKO +#define fseeko fseek +#endif + +#ifndef HAVE_OFF_T +#define off_t long int +#endif + + if (fseeko (output_file, + sizeof *input_buf * (case_idx + + (off_t) i * flip->case_cnt), + SEEK_SET) != 0) msg (FE, _("Error seeking FLIP source file: %s."), strerror (errno)); - if (fwrite (temp_buf, sizeof *case_buf, read_cases, src_file) + if (fwrite (output_buf, sizeof *output_buf, read_cases, output_file) != read_cases) msg (FE, _("Error writing FLIP source file: %s."), strerror (errno)); } - cur_case += read_cases; + case_idx += read_cases; } - if (fseek (src_file, 0, SEEK_SET) != 0) + fclose (input_file); + free (input_buf); + + if (fseek (output_file, 0, SEEK_SET) != 0) msg (FE, _("Error rewind FLIP source file: %s."), strerror (errno)); + flip->file = output_file; +} - fclose (sink_file); +/* Destroy sink's internal data. */ +static void +flip_sink_destroy (struct case_sink *sink) +{ + struct flip_sink_info *info = sink->aux; - free (case_buf); + free (info->output_buf); + free (info); } -/* Change the FLIP stream from sink to source mode. */ -static void -flip_stream_mode (void) +/* FLIP sink class. */ +static const struct case_sink_class flip_sink_class = + { + "FLIP", + NULL, + flip_sink_write, + flip_sink_destroy, + NULL, + }; + +/* Creates and returns a FLIP source based on PGM, + which should have already been used as a sink. */ +static struct case_source * +flip_source_create (struct flip_pgm *pgm) { - src_cases = sink_cases; - src_old_names = sink_old_names; - sink_old_names = NULL; - - if (internal) - { - if (tail) - { - tail->next = NULL; - src = head; - } - else - { - src = NULL; - src_file = NULL; - } - } - else - { - src = NULL; - transpose_external_file (); - } + return create_case_source (&flip_source_class, pgm); } -/* Destroy source's internal data. */ +/* Reads the FLIP stream. Copies each case into C and calls + WRITE_CASE passing WC_DATA. */ static void -flip_stream_destroy_source (void) +flip_source_read (struct case_source *source, + struct ccase *c, + write_case_func *write_case, write_case_data wc_data) { - free (src_old_names); - if (internal) + struct flip_pgm *flip = source->aux; + union value *input_buf; + size_t i; + + input_buf = xnmalloc (flip->case_cnt, sizeof *input_buf); + for (i = 0; i < flip->var_cnt; i++) { - struct flip_case *iter, *next; + size_t j; + + if (fread (input_buf, sizeof *input_buf, flip->case_cnt, + flip->file) != flip->case_cnt) + { + if (ferror (flip->file)) + msg (SE, _("Error reading FLIP temporary file: %s."), + strerror (errno)); + else if (feof (flip->file)) + msg (SE, _("Unexpected end of file reading FLIP temporary file.")); + else + assert (0); + break; + } - for (iter = src; iter; iter = next) - { - next = iter->next; - free (iter); - } + for (j = 0; j < flip->case_cnt; j++) + case_data_rw (c, j)->f = input_buf[j].f; + if (!write_case (wc_data)) + break; } - else - fclose (src_file); + free (input_buf); } -/* Destroy sink's internal data. */ +/* Destroy internal data in SOURCE. */ static void -flip_stream_destroy_sink (void) +flip_source_destroy (struct case_source *source) { - struct flip_case *iter, *next; - - free (sink_old_names); - if (tail == NULL) - return; + struct flip_pgm *flip = source->aux; - tail->next = NULL; - for (iter = head; iter; iter = next) - { - next = iter->next; - free (iter); - } + destroy_flip_pgm (flip); } -struct case_stream flip_stream = +static const struct case_source_class flip_source_class = { - flip_stream_init, - flip_stream_read, - flip_stream_write, - flip_stream_mode, - flip_stream_destroy_source, - flip_stream_destroy_sink, "FLIP", + NULL, + flip_source_read, + flip_source_destroy };