X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fdata%2Fany-reader.c;h=5a20ccef8360d6cd7f6c225afc5b3feb7a972330;hb=refs%2Fheads%2Fctables7;hp=009e42270d5403866c431c8f4f6b171ed8b18702;hpb=92c09e564002d356d20fc1e2e131027ef89f6748;p=pspp diff --git a/src/data/any-reader.c b/src/data/any-reader.c index 009e42270d..5a20ccef83 100644 --- a/src/data/any-reader.c +++ b/src/data/any-reader.c @@ -1,108 +1,266 @@ -/* PSPP - computes sample statistics. - Copyright (C) 2006 Free Software Foundation, Inc. +/* PSPP - a program for statistical analysis. + Copyright (C) 2006, 2010, 2011, 2012, 2014 Free Software Foundation, Inc. - 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 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 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. + 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. */ + along with this program. If not, see . */ #include -#include "any-reader.h" + +#include "data/any-reader.h" + #include #include #include #include #include -#include -#include -#include "file-handle-def.h" -#include "file-name.h" -#include "por-file-reader.h" -#include "sys-file-reader.h" -#include -#include "scratch-reader.h" -#include "xalloc.h" + +#include "data/casereader.h" +#include "data/dataset.h" +#include "data/dictionary.h" +#include "data/file-handle-def.h" +#include "data/file-name.h" +#include "libpspp/assertion.h" +#include "libpspp/cast.h" +#include "libpspp/message.h" +#include "libpspp/str.h" + +#include "gl/xalloc.h" #include "gettext.h" #define _(msgid) gettext (msgid) +#define N_(msgid) (msgid) -/* Result of type detection. */ -enum detect_result +static const struct any_reader_class dataset_reader_class; + +static const struct any_reader_class *classes[] = { - YES, /* It is this type. */ - NO, /* It is not this type. */ - IO_ERROR /* File couldn't be opened. */ + &sys_file_reader_class, + &por_file_reader_class, + &pcp_file_reader_class, }; +enum { N_CLASSES = sizeof classes / sizeof *classes }; -/* Tries to detect whether HANDLE represents a given type of - file, by opening the file and passing it to DETECT, and - returns a detect_result. */ -static enum detect_result -try_detect (struct file_handle *handle, bool (*detect) (FILE *)) +int +any_reader_detect (const struct file_handle *file_handle, + const struct any_reader_class **classp) { + struct detector + { + enum any_type type; + int (*detect) (FILE *); + }; + FILE *file; - bool is_type; + int retval; + + if (classp) + *classp = NULL; - file = fn_open (fh_get_file_name (handle), "rb"); + file = fn_open (file_handle, "rb"); if (file == NULL) { - msg (ME, _("An error occurred while opening \"%s\": %s."), - fh_get_file_name (handle), strerror (errno)); - return IO_ERROR; + msg (ME, _("An error occurred while opening `%s': %s."), + fh_get_file_name (file_handle), strerror (errno)); + return -errno; } - - is_type = detect (file); - - fn_close (fh_get_file_name (handle), file); - return is_type ? YES : NO; + retval = 0; + for (int i = 0; i < N_CLASSES; i++) + { + int rc = classes[i]->detect (file); + if (rc == 1) + { + retval = 1; + if (classp) + *classp = classes[i]; + break; + } + else if (rc < 0) + retval = rc; + } + + if (retval < 0) + msg (ME, _("Error reading `%s': %s."), fh_get_file_name (file_handle), strerror (-retval)); + + fn_close (file_handle, file); + + return retval; } -/* Returns a casereader for HANDLE. On success, returns the new - casereader and stores the file's dictionary into *DICT. On - failure, returns a null pointer. */ -struct casereader * -any_reader_open (struct file_handle *handle, struct dictionary **dict) +struct any_reader * +any_reader_open (struct file_handle *handle) { - switch (fh_get_referent (handle)) + switch (fh_get_referent (handle)) { case FH_REF_FILE: { - enum detect_result result; - - result = try_detect (handle, sfm_detect); - if (result == IO_ERROR) - return NULL; - else if (result == YES) - return sfm_open_reader (handle, dict, NULL); - - result = try_detect (handle, pfm_detect); - if (result == IO_ERROR) - return NULL; - else if (result == YES) - return pfm_open_reader (handle, dict, NULL); - - msg (SE, _("\"%s\" is not a system or portable file."), - fh_get_file_name (handle)); - return NULL; + const struct any_reader_class *class; + int retval; + + retval = any_reader_detect (handle, &class); + if (retval <= 0) + { + if (retval == 0) + msg (SE, _("`%s' is not a system or portable file."), + fh_get_file_name (handle)); + return NULL; + } + + return class->open (handle); } case FH_REF_INLINE: msg (SE, _("The inline file is not allowed here.")); return NULL; - case FH_REF_SCRATCH: - return scratch_reader_open (handle, dict); + case FH_REF_DATASET: + return dataset_reader_class.open (handle); } NOT_REACHED (); } + +/* gnulib on some systems defines "close" as something else, + which causes problems for this code. So undefine it here. */ +#undef close + +bool +any_reader_close (struct any_reader *any_reader) +{ + return any_reader ? any_reader->klass->close (any_reader) : true; +} + +struct casereader * +any_reader_decode (struct any_reader *any_reader, + const char *encoding, + struct dictionary **dictp, + struct any_read_info *info) +{ + const struct any_reader_class *class = any_reader->klass; + struct casereader *reader; + + reader = any_reader->klass->decode (any_reader, encoding, dictp, info); + if (reader && info) + info->klass = class; + return reader; +} + +size_t +any_reader_get_strings (const struct any_reader *any_reader, struct pool *pool, + char ***labels, bool **ids, char ***values) +{ + return (any_reader->klass->get_strings + ? any_reader->klass->get_strings (any_reader, pool, labels, ids, + values) + : 0); +} + +struct casereader * +any_reader_open_and_decode (struct file_handle *handle, + const char *encoding, + struct dictionary **dictp, + struct any_read_info *info) +{ + struct any_reader *any_reader = any_reader_open (handle); + return (any_reader + ? any_reader_decode (any_reader, encoding, dictp, info) + : NULL); +} + +struct dataset_reader + { + struct any_reader any_reader; + struct dictionary *dict; + struct casereader *reader; + }; + +/* Opens FH, which must have referent type FH_REF_DATASET, and returns a + dataset_reader for it, or a null pointer on failure. Stores a copy of the + dictionary for the dataset file into *DICT. The caller takes ownership of + the casereader and the dictionary. */ +static struct any_reader * +dataset_reader_open (struct file_handle *fh) +{ + struct dataset_reader *reader; + struct dataset *ds; + + /* We don't bother doing fh_lock or fh_ref on the file handle, + as there's no advantage in this case, and doing these would + require us to keep track of the "struct file_handle" and + "struct fh_lock" and undo our work later. */ + assert (fh_get_referent (fh) == FH_REF_DATASET); + + ds = fh_get_dataset (fh); + if (ds == NULL || !dataset_has_source (ds)) + { + msg (SE, _("Cannot read from dataset %s because no dictionary or data " + "has been written to it yet."), + fh_get_name (fh)); + return NULL; + } + + reader = xmalloc (sizeof *reader); + reader->any_reader.klass = &dataset_reader_class; + reader->dict = dict_clone (dataset_dict (ds)); + reader->reader = casereader_clone (dataset_source (ds)); + return &reader->any_reader; +} + +static struct dataset_reader * +dataset_reader_cast (const struct any_reader *r_) +{ + assert (r_->klass == &dataset_reader_class); + return UP_CAST (r_, struct dataset_reader, any_reader); +} + +static bool +dataset_reader_close (struct any_reader *r_) +{ + struct dataset_reader *r = dataset_reader_cast (r_); + dict_unref (r->dict); + casereader_destroy (r->reader); + free (r); + + return true; +} + +static struct casereader * +dataset_reader_decode (struct any_reader *r_, const char *encoding UNUSED, + struct dictionary **dictp, struct any_read_info *info) +{ + struct dataset_reader *r = dataset_reader_cast (r_); + struct casereader *reader; + + *dictp = r->dict; + reader = r->reader; + if (info) + { + memset (info, 0, sizeof *info); + info->integer_format = INTEGER_NATIVE; + info->float_format = FLOAT_NATIVE_DOUBLE; + info->compression = ANY_COMP_NONE; + info->n_cases = casereader_get_n_cases (reader); + } + free (r); + + return reader; +} + +static const struct any_reader_class dataset_reader_class = + { + N_("Dataset"), + NULL, + dataset_reader_open, + dataset_reader_close, + dataset_reader_decode, + NULL, + };