From: Ben Pfaff Date: Sun, 29 Jan 2006 02:41:11 +0000 (+0000) Subject: Add scratch file handles. X-Git-Tag: v0.6.0~1083 X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?p=pspp-builds.git;a=commitdiff_plain;h=053e7ff6e0a45a25d5604b211e9c950fff50e75d Add scratch file handles. Now a file handle can refer to a disk file, to an in-memory structure, or to the "inline" file, instead of just to a disk file. The introduction of new categories means that special cases for the inline file in a few places could be eliminated, but it also means that code that assumed that a handle refers to a file has to check for that. Also, now file handles can be freed, so code now must be sure not to access a handle after closing it (with fh_close()). Plus some cleanups. --- diff --git a/Smake b/Smake index 0100edff..c3309624 100644 --- a/Smake +++ b/Smake @@ -7,7 +7,7 @@ GNULIB_MODULES = alloca alloca-opt assert full-read full-write \ gethostname getline getlogin_r getopt gettext memchr memcmp memmem \ memmove memset progname readlink restrict snprintf stat-macros stdbool \ stpcpy strcase strcspn strerror strftime strstr strtod strtok_r strtol \ -strtoul vsnprintf xalloc xalloc-die xreadlink +strtoul vsnprintf xalloc xalloc-die xreadlink xvasprintf all: po/POTFILES.in test -d m4 || mkdir m4 diff --git a/doc/data-io.texi b/doc/data-io.texi index 0137ad29..d4dab8af 100644 --- a/doc/data-io.texi +++ b/doc/data-io.texi @@ -8,26 +8,26 @@ Data are the focus of the PSPP language. Each datum belongs to a @dfn{case} (also called an @dfn{observation}). -Each case represents an individual or `experimental unit'. +Each case represents an individual or ``experimental unit''. For example, in the results of a survey, the names of the respondents, -their sex, age @i{etc}. and their responses are all data and the data +their sex, age, etc.@: and their responses are all data and the data pertaining to single respondent is a case. This chapter examines the PSPP commands for defining variables and reading and writing data. -@quotation -@strong{Please note:} Data is not actually read until a procedure is -executed. These commands tell PSPP how to read data, but they -do not @emph{cause} PSPP to read data. +@quotation Note +These commands tell PSPP how to read data, but the data will not +actually be read until a procedure is executed. @end quotation @menu * BEGIN DATA:: Embed data within a syntax file. * CLEAR TRANSFORMATIONS:: Clear pending transformations. +* CLOSE FILE HANDLE:: Close a file handle. * DATA LIST:: Fundamental data reading command. * END CASE:: Output the current case. * END FILE:: Terminate the current input program. -* FILE HANDLE:: Support for fixed-length records. +* FILE HANDLE:: Support for special file formats. * INPUT PROGRAM:: Support for complex input programs. * LIST:: List cases in the active file. * MATRIX DATA:: Read matrices in text format. @@ -40,7 +40,7 @@ do not @emph{cause} PSPP to read data. * WRITE:: Display values in write formats. @end menu -@node BEGIN DATA, CLEAR TRANSFORMATIONS, Data Input and Output, Data Input and Output +@node BEGIN DATA @section BEGIN DATA @vindex BEGIN DATA @vindex END DATA @@ -65,7 +65,7 @@ white space and exactly one space between the words @code{END} and END DATA. @end example -@node CLEAR TRANSFORMATIONS, DATA LIST, BEGIN DATA, Data Input and Output +@node CLEAR TRANSFORMATIONS @section CLEAR TRANSFORMATIONS @vindex CLEAR TRANSFORMATIONS @@ -77,7 +77,31 @@ CLEAR TRANSFORMATIONS. transformations. It does not cancel the current input program. It is valid only when PSPP is interactive, not in syntax files. -@node DATA LIST, END CASE, CLEAR TRANSFORMATIONS, Data Input and Output +@node CLOSE FILE HANDLE +@section CLOSE FILE HANDLE + +@display +CLOSE FILE HANDLE handle_name. +@end display + +@cmd{CLOSE FILE HANDLE} disassociates the name of a file handle with a +given file. The only specification is the name of the handle to close. +Afterward +@cmd{FILE HANDLE}. + +If the file handle name refers to a scratch file, then the storage +associated with the scratch file in memory or on disk will be freed. +If the scratch file is in use, e.g.@: it has been specified on a +@cmd{GET} command whose execution has not completed, then freeing is +delayed until it is no longer in use. + +The file named INLINE, which represents data entered between @cmd{BEGIN +DATA} and @cmd{END DATA}, cannot be closed. Attempts to close it with +@cmd{CLOSE FILE HANDLE} have no effect. + +@cmd{CLOSE FILE HANDLE} is a PSPP extension. + +@node DATA LIST @section DATA LIST @vindex DATA LIST @cindex reading data from a file @@ -104,7 +128,7 @@ Each form of @cmd{DATA LIST} is described in detail below. * DATA LIST LIST:: Each case must be on a single line. @end menu -@node DATA LIST FIXED, DATA LIST FREE, DATA LIST, DATA LIST +@node DATA LIST FIXED @subsection DATA LIST FIXED @vindex DATA LIST FIXED @cindex reading fixed-format data @@ -131,7 +155,7 @@ keyword FIXED is optional. The FILE subcommand must be used if input is to be taken from an external file. It may be used to specify a filename as a string or a -file handle (@pxref{FILE HANDLE}). If the FILE subcommand is not used, +file handle (@pxref{File Handles}). If the FILE subcommand is not used, then input is assumed to be specified within the command file using @cmd{BEGIN DATA}@dots{}@cmd{END DATA} (@pxref{BEGIN DATA}). @@ -229,7 +253,7 @@ applies to later FORTRAN and columnar specifiers. * DATA LIST FIXED Examples:: Examples of DATA LIST FIXED. @end menu -@node DATA LIST FIXED Examples, , DATA LIST FIXED, DATA LIST FIXED +@node DATA LIST FIXED Examples @unnumberedsubsubsec Examples @enumerate @@ -315,7 +339,7 @@ This example shows keywords abbreviated to their first 3 letters. @end enumerate -@node DATA LIST FREE, DATA LIST LIST, DATA LIST FIXED, DATA LIST +@node DATA LIST FREE @subsection DATA LIST FREE @vindex DATA LIST FREE @@ -367,7 +391,7 @@ preceding it are to have input/output format @samp{F8.0}. Specified field widths are ignored on input, although all normal limits on field width apply, but they are honored on output. -@node DATA LIST LIST, , DATA LIST FREE, DATA LIST +@node DATA LIST LIST @subsection DATA LIST LIST @vindex DATA LIST LIST @@ -390,7 +414,7 @@ that each input line is expected to correspond to exactly one input record. If more or fewer fields are found on an input line than expected, an appropriate diagnostic is issued. -@node END CASE, END FILE, DATA LIST, Data Input and Output +@node END CASE @section END CASE @vindex END CASE @@ -401,7 +425,7 @@ END CASE. @cmd{END CASE} is used only within @cmd{INPUT PROGRAM} to output the current case. @xref{INPUT PROGRAM}, for details. -@node END FILE, FILE HANDLE, END CASE, Data Input and Output +@node END FILE @section END FILE @vindex END FILE @@ -412,50 +436,67 @@ END FILE. @cmd{END FILE} is used only within @cmd{INPUT PROGRAM} to terminate the current input program. @xref{INPUT PROGRAM}. -@node FILE HANDLE, INPUT PROGRAM, END FILE, Data Input and Output +@node FILE HANDLE @section FILE HANDLE @vindex FILE HANDLE @display -FILE HANDLE handle_name - /NAME='filename' - /MODE=@{CHARACTER,IMAGE@} - /LRECL=rec_len - /TABWIDTH=tab_width +For text files: + FILE HANDLE handle_name + /NAME='filename' + [/MODE=CHARACTER] + /TABWIDTH=tab_width + +For binary files with fixed-length records: + FILE HANDLE handle_name + /NAME='filename' + /MODE=IMAGE + [/LRECL=rec_len] + +To explicitly declare a scratch handle: + FILE HANDLE handle_name + /MODE=SCRATCH @end display Use @cmd{FILE HANDLE} to associate a file handle name with a file and its attributes, so that later commands can refer to the file by its -handle name. Because names of text files can be specified directly on -commands that access files, @cmd{FILE HANDLE} is only needed when a +handle name. Names of text files can be specified directly on +commands that access files, so that @cmd{FILE HANDLE} is only needed when a file is not an ordinary file containing lines of text. However, @cmd{FILE HANDLE} may be used even for text files, and it may be easier to specify a file's name once and later refer to it by an abstract handle. -Specify the file handle name as an identifier. Any given identifier may -only appear once in a PSPP run. File handles may not be reassigned to a -different file. The file handle name must immediately follow the @cmd{FILE -HANDLE} command name. - -The NAME subcommand specifies the name of the file associated with the -handle. It is the only required subcommand. +Specify the file handle name as the identifier immediately following the +@cmd{FILE HANDLE} command name. The identifier INLINE is reserved for +representing data embedded in the syntax file (@pxref{BEGIN DATA}) The +file handle name must not already have been used in a previous +invocation of @cmd{FILE HANDLE}, unless it has been closed by an +intervening command (@pxref{CLOSE FILE HANDLE}). MODE specifies a file mode. In CHARACTER mode, the default, the data -file is opened in ANSI C text mode, so that local end of line -conventions are followed, and each text line is read as one record. +file is read as a text file, according to the local system's +conventions, and each text line is read as one record. In CHARACTER mode, most input programs will expand tabs to spaces (@cmd{DATA LIST FREE} with explicitly specified delimiters is an exception). By default, each tab is 4 characters wide, but an alternate width may be specified on TABWIDTH. A tab width of 0 suppresses tab expansion entirely. -By contrast, in IMAGE mode, the data file is opened in ANSI C binary -mode and records are a fixed length. In IMAGE mode, LRECL specifies -the record length in bytes, with a default of 1024. Tab characters -are never expanded to spaces in binary mode. +In IMAGE mode, the data file is opened in ANSI C binary mode and records +are fixed in length. In IMAGE mode, LRECL specifies the record length in +bytes, with a default of 1024. Tab characters are never expanded to +spaces in binary mode. + +The NAME subcommand specifies the name of the file associated with the +handle. It is required in CHARACTER and IMAGE modes. + +The SCRATCH mode designates the file handle as a scratch file handle. +Its use is usually unnecessary because file handle names that begin with +@samp{#} are assumed to refer to scratch files. @pxref{File Handles}, +for more information. -@node INPUT PROGRAM, LIST, FILE HANDLE, Data Input and Output +@node INPUT PROGRAM @section INPUT PROGRAM @vindex INPUT PROGRAM @@ -606,7 +647,7 @@ LIST/FORMAT=NUMBERED. The above example causes an active file to be created consisting of 50 random variates between 0 and 10. -@node LIST, MATRIX DATA, INPUT PROGRAM, Data Input and Output +@node LIST @section LIST @vindex LIST @@ -647,7 +688,7 @@ cannot fit on a single line, then a multi-line format will be used. @cmd{LIST} is a procedure. It causes the data to be read. -@node MATRIX DATA, NEW FILE, LIST, Data Input and Output +@node MATRIX DATA @section MATRIX DATA @vindex MATRIX DATA @@ -674,7 +715,7 @@ the matrices. You may not specify a variable named @code{VARNAME_}. You should specify VARIABLES first. Specify the file to read on FILE, either as a file name string or a file -handle (@pxref{FILE HANDLE}). If FILE is not specified then matrix data +handle (@pxref{File Handles}). If FILE is not specified then matrix data must immediately follow @cmd{MATRIX DATA} with a @cmd{BEGIN DATA}@dots{}@cmd{END DATA} construct (@pxref{BEGIN DATA}). @@ -749,7 +790,7 @@ Right now @cmd{MATRIX DATA} isn't too useful due to a lack of procedures accepting or producing related data, so these semantics aren't documented. Later, they'll be described here in detail. -@node NEW FILE, PRINT, MATRIX DATA, Data Input and Output +@node NEW FILE @section NEW FILE @vindex NEW FILE @@ -759,7 +800,7 @@ NEW FILE. @cmd{NEW FILE} command clears the current active file. -@node PRINT, PRINT EJECT, NEW FILE, Data Input and Output +@node PRINT @section PRINT @vindex PRINT @@ -785,8 +826,8 @@ invoking a procedure (@pxref{EXECUTE}). All @cmd{PRINT} subcommands are optional. The OUTFILE subcommand specifies the file to receive the output. The -file may be a file name as a string or a file handle (@pxref{FILE -HANDLE}). If OUTFILE is not present then output will be sent to PSPP's +file may be a file name as a string or a file handle (@pxref{File +Handles}). If OUTFILE is not present then output will be sent to PSPP's output listing file. The RECORDS subcommand specifies the number of lines to be output. The @@ -822,7 +863,7 @@ line, then text is written at that point on the line, the line will be truncated to that length, although additional text being added will again extend the line to that length. -@node PRINT EJECT, PRINT SPACE, PRINT, Data Input and Output +@node PRINT EJECT @section PRINT EJECT @vindex PRINT EJECT @@ -845,7 +886,7 @@ written, the current page in the listing file is ejected. @xref{PRINT}, for more information on syntax and usage. -@node PRINT SPACE, REREAD, PRINT EJECT, Data Input and Output +@node PRINT SPACE @section PRINT SPACE @vindex PRINT SPACE @@ -856,15 +897,15 @@ PRINT SPACE OUTFILE='filename' n_lines. @cmd{PRINT SPACE} prints one or more blank lines to an output file. The OUTFILE subcommand is optional. It may be used to direct output to -a file specified by file name as a string or file handle (@pxref{FILE -HANDLE}). If OUTFILE is not specified then output will be directed to +a file specified by file name as a string or file handle (@pxref{File +Handles}). If OUTFILE is not specified then output will be directed to the listing file. n_lines is also optional. If present, it is an expression (@pxref{Expressions}) specifying the number of blank lines to be printed. The expression must evaluate to a nonnegative value. -@node REREAD, REPEATING DATA, PRINT SPACE, Data Input and Output +@node REREAD @section REREAD @vindex REREAD @@ -878,8 +919,8 @@ already processed by @cmd{DATA LIST} or another input command to be re-read for further processing. The FILE subcommand, which is optional, is used to specify the file to -have its line re-read. The file must be specified in the form of a file -handle (@pxref{FILE HANDLE}). If FILE is not specified then the last +have its line re-read. The file must be specified as the name of a file +handle (@pxref{File Handles}). If FILE is not specified then the last file specified on @cmd{DATA LIST} will be assumed (last file specified lexically, not in terms of flow-of-control). @@ -892,7 +933,7 @@ are numbered from 1 at the left margin. Issuing @code{REREAD} multiple times will not back up in the data file. Instead, it will re-read the same line multiple times. -@node REPEATING DATA, WRITE, REREAD, Data Input and Output +@node REPEATING DATA @section REPEATING DATA @vindex REPEATING DATA @@ -941,7 +982,7 @@ FIXED} (@pxref{DATA LIST FIXED}). All other subcommands are optional. FILE specifies the file to read, either a file name as a string or a -file handle (@pxref{FILE HANDLE}). If FILE is not present then the +file handle (@pxref{File Handles}). If FILE is not present then the default is the last file handle used on @cmd{DATA LIST} (lexically, not in terms of flow of control). @@ -974,7 +1015,7 @@ have been declared with @cmd{NUMERIC} or another command. structure (@pxref{LOOP}). Use @cmd{DATA LIST} before, not after, @cmd{REPEATING DATA}. -@node WRITE, , REPEATING DATA, Data Input and Output +@node WRITE @section WRITE @vindex WRITE diff --git a/doc/files.texi b/doc/files.texi index ea1ff885..816a61ed 100644 --- a/doc/files.texi +++ b/doc/files.texi @@ -21,18 +21,18 @@ portable files. @vindex APPLY DICTIONARY @display -APPLY DICTIONARY FROM='filename'. +APPLY DICTIONARY FROM=@{'filename',file_handle@}. @end display @cmd{APPLY DICTIONARY} applies the variable labels, value labels, -and missing values from variables in a system file to corresponding +and missing values taken from a file to corresponding variables in the active file. In some cases it also updates the weighting variable. -Specify a system file with a file name string or as a file handle -(@pxref{FILE HANDLE}). The dictionary in the system file will be read, -but it will not replace the active file dictionary. The system file's -data will not be read. +Specify a system file, portable file, or scratch file with a file name +string or as a file handle (@pxref{File Handles}). The dictionary in the +file will be read, but it will not replace the active file dictionary. +The file's data will not be read. Only variables with names that exist in both the active file and the system file are considered. Variables with the same name but different @@ -79,9 +79,9 @@ EXPORT @end display The @cmd{EXPORT} procedure writes the active file dictionary and data to a -specified portable file. +specified portable file or scratch file. -By default, cases excluded with FILTER are written to the portable +By default, cases excluded with FILTER are written to the file. These can be excluded by specifying DELETE on the UNSELECTED subcommand. Specifying RETAIN makes the default explicit. @@ -95,8 +95,8 @@ subcommand may be used to specify the number of decimal digits of precision to write. DIGITS applies only to non-integers. The OUTFILE subcommand, which is the only required subcommand, specifies -the portable file to be written as a file name string or a file handle -(@pxref{FILE HANDLE}). +the portable file or scratch file to be written as a file name string or +a file handle (@pxref{File Handles}). DROP, KEEP, and RENAME follow the same format as the SAVE procedure (@pxref{SAVE}). @@ -114,25 +114,25 @@ The MAP subcommand is currently ignored. @display GET - /FILE='filename' + /FILE=@{'filename',file_handle@} /DROP=var_list /KEEP=var_list /RENAME=(src_names=target_names)@dots{} @end display @cmd{GET} clears the current dictionary and active file and -replaces them with the dictionary and data from a specified system file. +replaces them with the dictionary and data from a specified file. The FILE subcommand is the only required subcommand. Specify the system -file to be read as a string file name or a file handle (@pxref{FILE -HANDLE}). +file, portable file, or scratch file to be read as a string file name or +a file handle (@pxref{File Handles}). -By default, all the variables in a system file are read. The DROP +By default, all the variables in a file are read. The DROP subcommand can be used to specify a list of variables that are not to be read. By contrast, the KEEP subcommand can be used to specify variable that are to be read, with all other variables not read. -Normally variables in a system file retain the names that they were +Normally variables in a file retain the names that they were saved under. Use the RENAME subcommand to change these names. Specify, within parentheses, a list of variable names followed by an equals sign (@samp{=}) and the names that they should be renamed to. Multiple @@ -145,14 +145,17 @@ eliminated. When this is done, only a single variable may be renamed at once. For instance, @samp{/RENAME=A=B}. This alternate syntax is deprecated. -DROP, KEEP, and RENAME are performed in left-to-right order. They -each may be present any number of times. @cmd{GET} never modifies a -system file on disk. Only the active file read from the system file +DROP, KEEP, and RENAME are executed in left-to-right order. +Each may be present any number of times. @cmd{GET} never modifies a +file on disk. Only the active file read from the file is affected by these subcommands. @cmd{GET} does not cause the data to be read, only the dictionary. The data is read later, when a procedure is executed. +Use of @cmd{GET} to read a portable file or scratch file is a PSPP +extension. + @node IMPORT @section IMPORT @vindex IMPORT @@ -168,11 +171,12 @@ IMPORT The @cmd{IMPORT} transformation clears the active file dictionary and data and -replaces them with a dictionary and data from a portable file on disk. +replaces them with a dictionary and data from a system, portable file, +or scratch file. The FILE subcommand, which is the only required subcommand, specifies the portable file to be read as a file name string or a file handle -(@pxref{FILE HANDLE}). +(@pxref{File Handles}). The TYPE subcommand is currently not used. @@ -181,6 +185,9 @@ DROP, KEEP, and RENAME follow the syntax used by @cmd{GET} (@pxref{GET}). @cmd{IMPORT} does not cause the data to be read, only the dictionary. The data is read later, when a procedure is executed. +Use of @cmd{IMPORT} to read a system file or scratch file is a PSPP +extension. + @node MATCH FILES @section MATCH FILES @vindex MATCH FILES @@ -199,26 +206,28 @@ MATCH FILES /MAP @end display -@cmd{MATCH FILES} merges one or more system files, optionally +@cmd{MATCH FILES} merges one or more system, portable, or scratch files, +optionally including the active file. Records with the same values for BY variables are combined into a single record. Records with different -values are output in order. Thus, multiple sorted system files are -combined into a single sorted system file based on the value of the BY +values are output in order. Thus, multiple sorted files are +combined into a single sorted file based on the value of the BY variables. The results of the merge become the new active file. The BY subcommand specifies a list of variables that are used to match -records from each of the system files. Variables specified must exist +records from each of the files. Variables specified must exist in all the files specified on FILE and TABLE. BY should usually be specified. If TABLE or IN is used then BY is required. -Specify FILE with a system file as a file name string or file handle -(@pxref{FILE HANDLE}), or with an asterisk (@samp{*}) to +Specify FILE with a system, portable, or scratch file as a file name +string or file handle +(@pxref{File Handles}), or with an asterisk (@samp{*}) to indicate the current active file. The files specified on FILE are merged together based on the BY variables, or combined case-by-case if BY is not specified. Normally at least two FILE subcommands should be specified. -Specify TABLE with a system file to use it as a @dfn{table +Specify TABLE with a file to use it as a @dfn{table lookup file}. Records in table lookup files are not used up after they've been used once. This means that data in table lookup files can correspond to any number of records in FILE files. Table lookup files @@ -247,13 +256,16 @@ FIRST, LAST, and MAP are currently ignored. @cmd{MATCH FILES} may not be specified following @cmd{TEMPORARY} (@pxref{TEMPORARY}) if the active file is used as an input source. +Use of portable or scratch files on @cmd{MATCH FILES} is a PSPP +extension. + @node SAVE @section SAVE @vindex SAVE @display SAVE - /OUTFILE='filename' + /OUTFILE=@{'filename',file_handle@} /UNSELECTED=@{RETAIN,DELETE@} /@{COMPRESSED,UNCOMPRESSED@} /PERMISSIONS=@{WRITEABLE,READONLY@} @@ -267,11 +279,11 @@ SAVE The @cmd{SAVE} procedure causes the dictionary and data in the active file to -be written to a system file. +be written to a system file or scratch file. -OUTFILE is the only required subcommand. Specify the system -file to be written as a string file name or a file handle (@pxref{FILE -HANDLE}). +OUTFILE is the only required subcommand. Specify the system file or +scratch file to be written as a string file name or a file handle +(@pxref{File Handles}). By default, cases excluded with FILTER are written to the system file. These can be excluded by specifying DELETE on the UNSELECTED @@ -384,7 +396,7 @@ XSAVE @end display The @cmd{XSAVE} transformation writes the active file dictionary and -data to a system file stored on disk. It is similar to the @cmd{SAVE} +data to a system file or scratch file. It is similar to the @cmd{SAVE} procedure, with two differences: @itemize diff --git a/doc/language.texi b/doc/language.texi index e2565f96..36012f14 100644 --- a/doc/language.texi +++ b/doc/language.texi @@ -20,6 +20,7 @@ Later chapters will describe individual commands in detail. * Missing Observations:: Handling missing observations. * Variables:: The unit of data storage. * Files:: Files used by PSPP. +* File Handles:: How files are named. * BNF:: How command syntax is described. @end menu @@ -898,7 +899,7 @@ a scratch variable in an analysis, use @cmd{COMPUTE} (@pxref{COMPUTE}) to copy its value into an ordinary variable, then use that ordinary variable in the analysis. -@node Files, BNF, Variables, Language +@node Files @section Files Used by PSPP PSPP makes use of many files each time it runs. Some of these it @@ -914,16 +915,14 @@ most important of these files: @itemx syntax file These names (synonyms) refer to the file that contains instructions that tell PSPP what to do. The syntax file's name is specified on -the PSPP command line. Syntax files can also be pulled in with +the PSPP command line. Syntax files can also be read with @cmd{INCLUDE} (@pxref{INCLUDE}). @cindex file, data @cindex data file @item data file -Data files contain raw data in ASCII format suitable for being read in -by @cmd{DATA LIST}. Data can be embedded in the syntax -file with @cmd{BEGIN DATA} and @cmd{END DATA}: this makes the -syntax file a data file too. +Data files contain raw data in text or binary format. Data can also +be embedded in a syntax file with @cmd{BEGIN DATA} and @cmd{END DATA}. @cindex file, output @cindex output file @@ -936,13 +935,82 @@ depending on how PSPP is configured. @cindex active file @cindex file, active @item active file -The active file is the ``file'' on which all PSPP procedures -are performed. The active file contains variable definitions and -cases. The active file is not necessarily a disk file: it is stored -in memory if there is room. +The active file is the ``file'' on which all PSPP procedures are +performed. The active file consists of a dictionary and a set of cases. +The active file is not necessarily a disk file: it is stored in memory +if there is room. + +@cindex system file +@cindex file, system +@item system file +System files are binary files that store a dictionary and a set of +cases. @cmd{GET} and @cmd{SAVE} read and write system files. + +@cindex portable file +@cindex file, portable +@item portable file +Portable files are files in a text-based format that store a dictionary +and a set of cases. @cmd{IMPORT} and @cmd{EXPORT} read and write +portable files. + +@cindex scratch file +@cindex file, scratch +@item scratch file +Scratch files consist of a dictionary and cases and may be stored in +memory or on disk. Most procedures that act on a system file or +portable file can use a scratch file instead. The contents of scratch +files persist within a single PSPP session only. @cmd{GET} and +@cmd{SAVE} can be used to read and write scratch files. Scratch files +are a PSPP extension. @end table -@node BNF, , Files, Language +@node File Handles +@section File Handles +@cindex file handles + +A @dfn{file handle} is a reference to a data file, system file, portable +file, or scratch file. Most often, a file handle is specified as the +name of a file as a string, that is, enclosed within @samp{'} or +@samp{"}. + +PSPP also supports declaring named file handles with the @cmd{FILE +HANDLE} command. This command associates an identifier of your choice +(the file handle's name) with a file. Later, the file handle name can +be substituted for the name of the file. When PSPP syntax accesses a +file multiple times, declaring a named file handle simplifies updating +the syntax later to use a different file. Use of @cmd{FILE HANDLE} is +also required to read data files in binary formats. @xref{FILE HANDLE}, +for more information. + +PSPP assumes that a file handle name that begins with @samp{#} refers to +a scratch file, unless the name has already been declared on @cmd{FILE +HANDLE} to refer to another kind of file. A scratch file is similar to +a system file, except that it persists only for the duration of a given +PSPP session. Most commands that read or write a system or portable +file, such as @cmd{GET} and @cmd{SAVE}, also accept scratch file +handles. Scratch file handles may also be declared explicitly with +@cmd{FILE HANDLE}. Scratch files are a PSPP extension. + +In some circumstances, PSPP must distinguish whether a file handle +refers to a system file or a portable file. When this is necessary to +read a file, e.g.@: as an input file for @cmd{GET} or @cmd{MATCH FILES}, +PSPP uses the file's contents to decide. In the context of writing a +file, e.g.@: as an output file for @cmd{SAVE} or @cmd{AGGREGATE}, PSPP +decides based on the file's name: if it ends in @samp{.por} (with any +capitalization), then PSPP writes a portable file; otherwise, PSPP +writes a system file. + +INLINE is reserved as a file handle name. It refers to the ``data +file'' embedded into the syntax file between @cmd{BEGIN DATA} and +@cmd{END DATA}. @xref{BEGIN DATA}, for more information. + +The file to which a file handle refers may be reassigned on a later +@cmd{FILE HANDLE} command if it is first closed using @cmd{CLOSE FILE +HANDLE}. The @cmd{CLOSE FILE HANDLE} command is also useful to free the +storage associated with a scratch file. @xref{CLOSE FILE HANDLE}, for +more information. + +@node BNF @section Backus-Naur Form @cindex BNF @cindex Backus-Naur Form diff --git a/doc/transformation.texi b/doc/transformation.texi index e722b9c4..0fff3bf9 100644 --- a/doc/transformation.texi +++ b/doc/transformation.texi @@ -23,7 +23,7 @@ as a rule. @display AGGREGATE - OUTFILE=@{*,'filename'@} + OUTFILE=@{*,'filename',file_handle@} /PRESORTED /DOCUMENT /MISSING=COLUMNWISE @@ -37,9 +37,11 @@ variables called @dfn{break variables}. Several functions are available for summarizing case contents. The OUTFILE subcommand is required and must appear first. Specify a -system file by file name string or file handle (@pxref{FILE HANDLE}). +system file, portable file, or scratch file by file name or file +handle (@pxref{File Handles}). The aggregated cases are written to this file. If @samp{*} is -specified, then the aggregated cases replace the active file. +specified, then the aggregated cases replace the active file. Use of +OUTFILE to write a portable file or scratch file is a PSPP extension. By default, the active file will be sorted based on the break variables before aggregation takes place. If the active file is already sorted diff --git a/po/en_GB.po b/po/en_GB.po index 0fc25bc5..037fcf32 100644 --- a/po/en_GB.po +++ b/po/en_GB.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PSPP 0.3.1\n" "Report-Msgid-Bugs-To: pspp-dev@gnu.org\n" -"POT-Creation-Date: 2006-01-24 17:40+0800\n" +"POT-Creation-Date: 2006-01-28 17:20-0800\n" "PO-Revision-Date: 2004-01-23 13:04+0800\n" "Last-Translator: John Darrington \n" "Language-Team: John Darrington \n" @@ -16,15 +16,15 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n!=1);\n" -#: src/aggregate.c:199 +#: src/aggregate.c:200 msgid "while expecting COLUMNWISE" msgstr "" -#: src/aggregate.c:228 +#: src/aggregate.c:229 msgid "expecting BREAK" msgstr "" -#: src/aggregate.c:233 +#: src/aggregate.c:234 msgid "" "When PRESORTED is specified, specifying sorting directions with (A) or (D) " "has no effect. Output data will be sorted the same way as the input data." @@ -78,20 +78,34 @@ msgid "" "contains the aggregate variables and the break variables." msgstr "" +#: src/any-reader.c:74 +#, c-format +msgid "An error occurred while opening \"%s\": %s." +msgstr "" + +#: src/any-reader.c:129 +#, c-format +msgid "\"%s\" is not a system or portable file." +msgstr "" + +#: src/any-reader.c:135 src/any-writer.c:80 +msgid "The inline file is not allowed here." +msgstr "" + #: src/apply-dict.c:71 #, c-format msgid "Variable %s is %s in target file, but %s in source file." msgstr "" #: src/apply-dict.c:74 src/apply-dict.c:75 src/format.c:198 src/recode.c:464 -#: src/recode.c:465 src/sfm-read.c:1014 src/sfm-read.c:1159 -#: src/sfm-read.c:1160 src/vars-atr.c:40 src/vars-atr.c:48 +#: src/recode.c:465 src/sfm-read.c:1016 src/sfm-read.c:1161 +#: src/sfm-read.c:1162 src/vars-atr.c:40 src/vars-atr.c:48 msgid "string" msgstr "" #: src/apply-dict.c:74 src/apply-dict.c:75 src/format.c:198 src/recode.c:464 -#: src/recode.c:465 src/sfm-read.c:1014 src/sfm-read.c:1159 -#: src/sfm-read.c:1160 src/vars-atr.c:40 +#: src/recode.c:465 src/sfm-read.c:1016 src/sfm-read.c:1161 +#: src/sfm-read.c:1162 src/vars-atr.c:40 msgid "numeric" msgstr "" @@ -234,8 +248,8 @@ msgstr "" msgid "Source variable count (%u) does not match target variable count (%u)." msgstr "" -#: src/autorecode.c:142 src/command.c:797 src/file-handle.q:83 src/lexer.c:440 -#: src/matrix-data.c:532 src/print.c:335 src/print.c:1044 src/sel-if.c:57 +#: src/autorecode.c:142 src/command.c:797 src/lexer.c:440 +#: src/matrix-data.c:532 src/print.c:335 src/print.c:1046 src/sel-if.c:57 #: src/sel-if.c:134 src/vector.c:197 msgid "expecting end of command" msgstr "" @@ -560,7 +574,7 @@ msgstr "" #: src/crosstabs.q:1109 src/crosstabs.q:1136 src/crosstabs.q:1156 #: src/crosstabs.q:1178 src/examine.q:1131 src/frequencies.q:1142 -#: src/frequencies.q:1263 src/sysfile-info.c:518 src/vfm.c:840 +#: src/frequencies.q:1263 src/sysfile-info.c:518 src/vfm.c:845 msgid "Value" msgstr "" @@ -934,288 +948,275 @@ msgstr "" msgid "Field too long (%d characters). Truncated after character %d." msgstr "" -#: src/data-list.c:142 -msgid "" -"DATA LIST may not use a different file from that specified on its " -"surrounding FILE TYPE." +#: src/data-list.c:144 +msgid "DATA LIST must use the same file as the enclosing FILE TYPE." msgstr "" -#: src/data-list.c:161 +#: src/data-list.c:163 msgid "The END subcommand may only be specified once." msgstr "" -#: src/data-list.c:196 +#: src/data-list.c:198 msgid "Only one of FIXED, FREE, or LIST may be specified." msgstr "" -#: src/data-list.c:346 src/print.c:296 +#: src/data-list.c:348 src/print.c:296 #, c-format msgid "" "The record number specified, %ld, is before the previous record, %d. Data " "fields must be listed in order of increasing record number." msgstr "" -#: src/data-list.c:375 src/data-list.c:1735 +#: src/data-list.c:377 src/data-list.c:1727 msgid "" "SPSS-like or FORTRAN-like format specification expected after variable names." msgstr "" -#: src/data-list.c:386 +#: src/data-list.c:388 msgid "At least one variable must be specified." msgstr "" -#: src/data-list.c:391 src/print.c:328 +#: src/data-list.c:393 src/print.c:328 msgid "" "Variables are specified on records that should not exist according to " "RECORDS subcommand." msgstr "" -#: src/data-list.c:424 src/data-list.c:438 src/print.c:520 src/print.c:533 +#: src/data-list.c:426 src/data-list.c:440 src/print.c:520 src/print.c:533 msgid "Column positions for fields must be positive." msgstr "" -#: src/data-list.c:443 +#: src/data-list.c:445 msgid "The ending column for a field must be greater than the starting column." msgstr "" -#: src/data-list.c:457 +#: src/data-list.c:459 #, c-format msgid "The %d columns %d-%d can't be evenly divided into %d fields." msgstr "" -#: src/data-list.c:477 src/print.c:561 +#: src/data-list.c:479 src/print.c:561 msgid "A format specifier on this line has extra characters on the end." msgstr "" -#: src/data-list.c:492 src/print.c:577 +#: src/data-list.c:494 src/print.c:577 msgid "The value for number of decimal places must be at least 1." msgstr "" -#: src/data-list.c:506 src/print.c:590 +#: src/data-list.c:508 src/print.c:590 #, c-format msgid "Input format %s doesn't accept decimal places." msgstr "" -#: src/data-list.c:553 src/data-list.c:649 src/data-list.c:863 +#: src/data-list.c:555 src/data-list.c:651 src/data-list.c:859 #, c-format msgid "%s is a duplicate variable name." msgstr "" -#: src/data-list.c:558 +#: src/data-list.c:560 #, c-format msgid "There is already a variable %s of a different type." msgstr "" -#: src/data-list.c:565 +#: src/data-list.c:567 #, c-format msgid "There is already a string variable %s of a different width." msgstr "" -#: src/data-list.c:640 +#: src/data-list.c:642 msgid "" "The number of format specifications exceeds the given number of variable " "names." msgstr "" -#: src/data-list.c:753 src/print.c:766 +#: src/data-list.c:755 src/print.c:766 msgid "" "There aren't enough format specifications to match the number of variable " "names given." msgstr "" -#: src/data-list.c:780 src/data-list.c:904 src/descript.c:882 src/print.c:797 -#: src/sysfile-info.c:139 src/sysfile-info.c:373 src/vfm.c:839 +#: src/data-list.c:782 src/data-list.c:900 src/descript.c:882 src/print.c:797 +#: src/sysfile-info.c:139 src/sysfile-info.c:373 src/vfm.c:844 msgid "Variable" msgstr "" -#: src/data-list.c:781 src/print.c:798 +#: src/data-list.c:783 src/print.c:798 msgid "Record" msgstr "" -#: src/data-list.c:782 src/print.c:799 +#: src/data-list.c:784 src/print.c:799 msgid "Columns" msgstr "" -#: src/data-list.c:783 src/data-list.c:905 src/print.c:800 +#: src/data-list.c:785 src/data-list.c:901 src/print.c:800 msgid "Format" msgstr "" -#: src/data-list.c:799 +#: src/data-list.c:800 #, c-format -msgid "Reading %d record from file %s." -msgid_plural "Reading %d records from file %s." +msgid "Reading %d record from %s." +msgid_plural "Reading %d records from %s." msgstr[0] "" msgstr[1] "" -#: src/data-list.c:803 +#: src/data-list.c:916 #, c-format -msgid "Reading %d record from the command file." -msgid_plural "Reading %d records from the command file." -msgstr[0] "" -msgstr[1] "" - -#: src/data-list.c:921 -#, c-format -msgid "Reading free-form data from file %s." -msgstr "" - -#: src/data-list.c:924 -msgid "Reading free-form data from the command file." +msgid "Reading free-form data from %s." msgstr "" -#: src/data-list.c:975 +#: src/data-list.c:967 #, c-format msgid "Quoted string missing terminating `%c'." msgstr "" -#: src/data-list.c:1084 +#: src/data-list.c:1076 #, c-format msgid "Partial case of %d of %d records discarded." msgstr "" -#: src/data-list.c:1138 +#: src/data-list.c:1130 #, c-format msgid "Partial case discarded. The first variable missing was %s." msgstr "" -#: src/data-list.c:1182 +#: src/data-list.c:1174 #, c-format msgid "" "Missing value(s) for all variables from %s onward. These will be filled " "with the system-missing value or blanks, as appropriate." msgstr "" -#: src/data-list.c:1259 +#: src/data-list.c:1251 msgid "Attempt to read past end of file." msgstr "" -#: src/data-list.c:1398 +#: src/data-list.c:1390 msgid "" "REPEATING DATA must use the same file as its corresponding DATA LIST or FILE " "TYPE." msgstr "" -#: src/data-list.c:1408 src/data-list.c:1442 src/data-list.c:1455 -#: src/data-list.c:1468 src/data-list.c:1502 +#: src/data-list.c:1400 src/data-list.c:1434 src/data-list.c:1447 +#: src/data-list.c:1460 src/data-list.c:1494 #, c-format msgid "%s subcommand given multiple times." msgstr "" -#: src/data-list.c:1431 +#: src/data-list.c:1423 #, c-format msgid "STARTS beginning column (%d) exceeds STARTS ending column (%d)." msgstr "" -#: src/data-list.c:1488 +#: src/data-list.c:1480 #, c-format msgid "CONTINUED beginning column (%d) exceeds CONTINUED ending column (%d)." msgstr "" -#: src/data-list.c:1511 +#: src/data-list.c:1503 #, c-format msgid "ID beginning column (%ld) must be positive." msgstr "" -#: src/data-list.c:1526 +#: src/data-list.c:1518 #, c-format msgid "ID ending column (%ld) must be positive." msgstr "" -#: src/data-list.c:1532 +#: src/data-list.c:1524 #, c-format msgid "ID ending column (%ld) cannot be less than ID beginning column (%d)." msgstr "" -#: src/data-list.c:1572 +#: src/data-list.c:1564 msgid "Missing required specification STARTS." msgstr "" -#: src/data-list.c:1574 +#: src/data-list.c:1566 msgid "Missing required specification OCCURS." msgstr "" -#: src/data-list.c:1581 +#: src/data-list.c:1573 msgid "ID specified without CONTINUED." msgstr "" -#: src/data-list.c:1592 +#: src/data-list.c:1584 #, c-format msgid "" "STARTS beginning column (%d) exceeds default STARTS ending column taken from " "file's record width (%d)." msgstr "" -#: src/data-list.c:1605 +#: src/data-list.c:1597 #, c-format msgid "" "CONTINUED beginning column (%d) exceeds default CONTINUED ending column " "taken from file's record width (%d)." msgstr "" -#: src/data-list.c:1684 +#: src/data-list.c:1676 msgid "String variable not allowed here." msgstr "" -#: src/data-list.c:1694 +#: src/data-list.c:1686 #, c-format msgid "%s (%d) must be at least 1." msgstr "" -#: src/data-list.c:1700 +#: src/data-list.c:1692 #, c-format msgid "Variable or integer expected for %s." msgstr "" -#: src/data-list.c:1825 +#: src/data-list.c:1817 #, c-format msgid "Encountered mismatched record ID \"%s\" expecting \"%s\"." msgstr "" -#: src/data-list.c:1857 +#: src/data-list.c:1849 #, c-format msgid "" "Variable %s starting in column %d extends beyond physical record length of %" "d." msgstr "" -#: src/data-list.c:1924 +#: src/data-list.c:1916 #, c-format msgid "Invalid value %d for OCCURS." msgstr "" -#: src/data-list.c:1930 +#: src/data-list.c:1922 #, c-format msgid "Beginning column for STARTS (%d) must be at least 1." msgstr "" -#: src/data-list.c:1938 +#: src/data-list.c:1930 #, c-format msgid "Ending column for STARTS (%d) is less than beginning column (%d)." msgstr "" -#: src/data-list.c:1946 +#: src/data-list.c:1938 #, c-format msgid "Invalid value %d for LENGTH." msgstr "" -#: src/data-list.c:1953 +#: src/data-list.c:1945 #, c-format msgid "Beginning column for CONTINUED (%d) must be at least 1." msgstr "" -#: src/data-list.c:1961 +#: src/data-list.c:1953 #, c-format msgid "Ending column for CONTINUED (%d) is less than beginning column (%d)." msgstr "" -#: src/data-list.c:1993 +#: src/data-list.c:1985 #, c-format msgid "" "Number of repetitions specified on OCCURS (%d) exceed number of repetitions " "available in space on STARTS (%d), and CONTINUED not specified." msgstr "" -#: src/data-list.c:2011 +#: src/data-list.c:2003 #, c-format msgid "Unexpected end of file with %d repetitions remaining out of %d." msgstr "" @@ -1367,42 +1368,42 @@ msgstr "" msgid "Valid cases = %g; cases with missing value(s) = %g." msgstr "" -#: src/dfm-read.c:158 +#: src/dfm-read.c:136 #, c-format msgid "Could not open \"%s\" for reading as a data file: %s." msgstr "" -#: src/dfm-read.c:191 src/dfm-read.c:209 +#: src/dfm-read.c:168 src/dfm-read.c:186 msgid "BEGIN DATA expected." msgstr "" -#: src/dfm-read.c:218 +#: src/dfm-read.c:195 msgid "" "Unexpected end-of-file while reading data in BEGIN DATA. This probably " "indicates a missing or misformatted END DATA command. END DATA must appear " "by itself on a single line with exactly one space between words." msgstr "" -#: src/dfm-read.c:251 src/dfm-read.c:271 +#: src/dfm-read.c:227 src/dfm-read.c:247 #, c-format msgid "Error reading file %s: %s." msgstr "" -#: src/dfm-read.c:274 +#: src/dfm-read.c:250 #, c-format msgid "%s: Partial record at end of file." msgstr "" -#: src/dfm-read.c:317 +#: src/dfm-read.c:299 #, c-format msgid "Attempt to read beyond end-of-file on file %s." msgstr "" -#: src/dfm-read.c:320 +#: src/dfm-read.c:302 msgid "Attempt to read beyond END DATA." msgstr "" -#: src/dfm-read.c:467 +#: src/dfm-read.c:446 msgid "" "This command is not valid here since the current input program does not " "access the inline file." @@ -1720,67 +1721,61 @@ msgstr "" msgid "%s is a PSPP extension." msgstr "" -#: src/file-handle-def.c:229 +#: src/file-handle-def.c:303 #, c-format -msgid "Can't open %s as a %s because it is already open as a %s" +msgid "Can't open %s as a %s because it is already open as a %s." msgstr "" -#: src/file-handle-def.c:236 +#: src/file-handle-def.c:310 #, c-format -msgid "Can't open %s as a %s for %s because it is already open for %s" +msgid "Can't open %s as a %s for %s because it is already open for %s." msgstr "" -#: src/file-handle-def.c:244 +#: src/file-handle-def.c:318 #, c-format -msgid "Can't re-open %s as a %s for %s" +msgid "Can't re-open %s as a %s for %s." msgstr "" -#: src/file-handle.q:68 +#: src/file-handle.q:69 #, c-format msgid "" -"File handle %s already refers to file %s. File handles cannot be redefined " -"within a session." +"File handle %s is already defined. Use CLOSE FILE HANDLE before redefining " +"a file handle." msgstr "" -#: src/file-handle.q:89 -msgid "The FILE HANDLE required subcommand NAME is not present." -msgstr "" - -#: src/file-handle.q:104 +#: src/file-handle.q:101 #, c-format msgid "" "Fixed-length records were specified on /RECFORM, but record length was not " "specified on /LRECL. Assuming %d-character records." msgstr "" -#: src/file-handle.q:109 +#: src/file-handle.q:106 #, c-format msgid "" "Record length (%ld) must be at least one byte. Assuming %d-character " "records." msgstr "" -#: src/file-handle.q:139 -msgid "expecting a file name or handle name" +#: src/file-handle.q:152 +msgid "file" msgstr "" -#: src/filename.c:227 -#, c-format -msgid "Searching for `%s'..." +#: src/file-handle.q:154 +msgid "inline file" msgstr "" -#: src/filename.c:235 src/filename.c:267 -msgid "Search unsuccessful!" +#: src/file-handle.q:156 +msgid "scratch file" msgstr "" -#: src/filename.c:260 -#, c-format -msgid "Found `%s'." +#: src/file-handle.q:177 +msgid "expecting a file name or handle name" msgstr "" -#: src/filename.c:665 +#: src/file-handle.q:204 #, c-format -msgid "Not opening pipe file `%s' because SAFER option set." +msgid "Handle for %s not allowed here." msgstr "" #: src/file-type.c:133 @@ -1921,6 +1916,25 @@ msgstr "" msgid "Unknown record type %g." msgstr "" +#: src/filename.c:227 +#, c-format +msgid "Searching for `%s'..." +msgstr "" + +#: src/filename.c:235 src/filename.c:267 +msgid "Search unsuccessful!" +msgstr "" + +#: src/filename.c:260 +#, c-format +msgid "Found `%s'." +msgstr "" + +#: src/filename.c:677 +#, c-format +msgid "Not opening pipe file `%s' because SAFER option set." +msgstr "" + #: src/flip.c:88 msgid "" "FLIP ignores TEMPORARY. Temporary transformations will be made permanent." @@ -1982,6 +1996,29 @@ msgstr "" msgid "Unexpected end of file reading FLIP temporary file." msgstr "" +#: src/format-prs.c:66 +msgid "X and T format specifiers not allowed here." +msgstr "" + +#: src/format-prs.c:74 +#, c-format +msgid "%.*s is not a valid data format." +msgstr "" + +#: src/format-prs.c:115 +msgid "Format specifier expected." +msgstr "" + +#: src/format-prs.c:127 +#, c-format +msgid "Data format %s does not specify a width." +msgstr "" + +#: src/format-prs.c:145 +#, c-format +msgid "Data format %s is not valid." +msgstr "" + #: src/format.c:73 #, c-format msgid "Format specifies a bad type (%d)" @@ -2038,11 +2075,11 @@ msgstr "" msgid "%s variables are not compatible with %s format %s." msgstr "" -#: src/format.c:197 src/pfm-read.c:476 src/sfm-read.c:1012 src/sfm-read.c:1021 +#: src/format.c:197 src/pfm-read.c:476 src/sfm-read.c:1014 src/sfm-read.c:1023 msgid "String" msgstr "" -#: src/format.c:197 src/pfm-read.c:476 src/sfm-read.c:1012 src/sfm-read.c:1021 +#: src/format.c:197 src/pfm-read.c:476 src/sfm-read.c:1014 src/sfm-read.c:1023 msgid "Numeric" msgstr "" @@ -2051,29 +2088,6 @@ msgstr "" msgid "String variable with width %d not compatible with format %s." msgstr "" -#: src/format-prs.c:66 -msgid "X and T format specifiers not allowed here." -msgstr "" - -#: src/format-prs.c:74 -#, c-format -msgid "%.*s is not a valid data format." -msgstr "" - -#: src/format-prs.c:115 -msgid "Format specifier expected." -msgstr "" - -#: src/format-prs.c:127 -#, c-format -msgid "Data format %s does not specify a width." -msgstr "" - -#: src/format-prs.c:145 -#, c-format -msgid "Data format %s is not valid." -msgstr "" - #: src/formats.c:89 msgid "`(' expected after variable list" msgstr "" @@ -2167,16 +2181,20 @@ msgstr "" msgid "No valid data for variable %s; statistics not displayed." msgstr "" -#: src/get.c:306 src/get.c:320 src/get.c:345 +#: src/get.c:108 +msgid "expecting COMM or TAPE" +msgstr "" + +#: src/get.c:343 src/get.c:357 src/get.c:382 #, c-format msgid "expecting %s or %s" msgstr "" -#: src/get.c:552 src/print.c:179 +#: src/get.c:586 src/print.c:179 msgid "expecting a valid subcommand" msgstr "" -#: src/get.c:585 +#: src/get.c:619 #, c-format msgid "" "Cannot rename %s as %s because there already exists a variable named %s. To " @@ -2184,85 +2202,81 @@ msgid "" "as \"/RENAME (A=B)(B=C)(C=A)\", or equivalently, \"/RENAME (A B C=B C A)\"." msgstr "" -#: src/get.c:610 +#: src/get.c:644 msgid "`=' expected after variable list." msgstr "" -#: src/get.c:617 +#: src/get.c:651 #, c-format msgid "" "Number of variables on left side of `=' (%d) does not match number of " "variables on right side (%d), in parenthesized group %d of RENAME subcommand." msgstr "" -#: src/get.c:630 +#: src/get.c:664 #, c-format msgid "Requested renaming duplicates variable name %s." msgstr "" -#: src/get.c:660 +#: src/get.c:694 msgid "Cannot DROP all variables from dictionary." msgstr "" -#: src/get.c:835 +#: src/get.c:869 msgid "The active file may not be specified more than once." msgstr "" -#: src/get.c:844 +#: src/get.c:878 msgid "Cannot specify the active file since no active file has been defined." msgstr "" -#: src/get.c:852 +#: src/get.c:886 msgid "" "MATCH FILES may not be used after TEMPORARY when the active file is an input " "source. Temporary transformations will be made permanent." msgstr "" -#: src/get.c:890 +#: src/get.c:924 msgid "Multiple IN subcommands for a single FILE or TABLE." msgstr "" -#: src/get.c:910 +#: src/get.c:944 msgid "BY may appear at most once." msgstr "" -#: src/get.c:930 +#: src/get.c:964 #, c-format msgid "File %s lacks BY variable %s." msgstr "" -#: src/get.c:944 +#: src/get.c:978 msgid "FIRST may appear at most once." msgstr "" -#: src/get.c:958 +#: src/get.c:992 msgid "LAST may appear at most once." msgstr "" -#: src/get.c:999 +#: src/get.c:1033 msgid "BY is required when TABLE is specified." msgstr "" -#: src/get.c:1004 +#: src/get.c:1038 msgid "BY is required when IN is specified." msgstr "" -#: src/get.c:1032 +#: src/get.c:1066 #, c-format msgid "IN variable name %s duplicates an existing variable name." msgstr "" -#: src/get.c:1461 +#: src/get.c:1495 #, c-format msgid "" "Variable %s in file %s (%s) has different type or width from the same " "variable in earlier file (%s)." msgstr "" -#: src/get.c:1545 -msgid "expecting COMM or TAPE" -msgstr "" - #: src/getl.c:139 #, c-format msgid "Can't find `%s' in include file search path." @@ -3269,34 +3283,34 @@ msgstr "" msgid "Cannot change mode of %s: %s" msgstr "" -#: src/pfm-read.c:87 +#: src/pfm-read.c:97 #, c-format msgid "portable file %s corrupt at offset %ld: " msgstr "" -#: src/pfm-read.c:114 +#: src/pfm-read.c:124 msgid "unexpected end of file" msgstr "" -#: src/pfm-read.c:172 +#: src/pfm-read.c:182 #, c-format msgid "" "An error occurred while opening \"%s\" for reading as a portable file: %s." msgstr "" -#: src/pfm-read.c:190 +#: src/pfm-read.c:200 msgid "Data record expected." msgstr "" -#: src/pfm-read.c:298 +#: src/pfm-read.c:308 msgid "Missing numeric terminator." msgstr "" -#: src/pfm-read.c:321 +#: src/pfm-read.c:331 msgid "Invalid integer." msgstr "" -#: src/pfm-read.c:332 +#: src/pfm-read.c:342 #, c-format msgid "Bad string length %d." msgstr "" @@ -3316,12 +3330,12 @@ msgstr "" msgid "Bad time string length %d." msgstr "" -#: src/pfm-read.c:468 src/sfm-read.c:1004 +#: src/pfm-read.c:468 src/sfm-read.c:1006 #, c-format msgid "%s: Bad format specifier byte (%d)." msgstr "" -#: src/pfm-read.c:475 src/sfm-read.c:1020 +#: src/pfm-read.c:475 src/sfm-read.c:1022 #, c-format msgid "%s variable %s has invalid format specifier %s." msgstr "" @@ -3397,7 +3411,7 @@ msgstr "" msgid "%s: Writing portable file: %s." msgstr "" -#: src/pfm-write.c:468 +#: src/pfm-write.c:466 #, c-format msgid "%s: Closing portable file: %s." msgstr "" @@ -3584,19 +3598,23 @@ msgstr "" #: src/print.c:839 #, c-format -msgid "Writing %d record(s) to file %s." -msgstr "" +msgid "Writing %d record to %s." +msgid_plural "Writing %d records to %s." +msgstr[0] "" +msgstr[1] "" -#: src/print.c:842 +#: src/print.c:843 #, c-format -msgid "Writing %d record(s) to the listing file." -msgstr "" +msgid "Writing %d record." +msgid_plural "Writing %d records." +msgstr[0] "" +msgstr[1] "" -#: src/print.c:1082 +#: src/print.c:1084 msgid "The expression on PRINT SPACE evaluated to the system-missing value." msgstr "" -#: src/print.c:1085 +#: src/print.c:1087 #, c-format msgid "The expression on PRINT SPACE evaluated to %g." msgstr "" @@ -3806,6 +3824,13 @@ msgstr "" msgid "Cannot sample %d observations from a population of %d." msgstr "" +#: src/scratch-reader.c:59 +#, c-format +msgid "" +"Scratch file handle %s has not yet been written, using SAVE or another " +"procedure, so it cannot yet be used for reading." +msgstr "" + #: src/sel-if.c:103 msgid "The filter variable must be numeric." msgstr "" @@ -3951,131 +3976,131 @@ msgstr "" msgid "corrupt system file: " msgstr "" -#: src/sfm-read.c:151 src/sfm-write.c:931 +#: src/sfm-read.c:149 src/sfm-write.c:929 #, c-format msgid "%s: Closing system file: %s." msgstr "" -#: src/sfm-read.c:237 +#: src/sfm-read.c:239 #, c-format msgid "" "An error occurred while opening \"%s\" for reading as a system file: %s." msgstr "" -#: src/sfm-read.c:255 +#: src/sfm-read.c:257 #, c-format msgid "" "%s: Index of weighting variable (%d) is not between 0 and number of elements " "per case (%d)." msgstr "" -#: src/sfm-read.c:264 +#: src/sfm-read.c:266 #, c-format msgid "" "%s: Weighting variable may not be a continuation of a long string variable." msgstr "" -#: src/sfm-read.c:267 +#: src/sfm-read.c:269 #, c-format msgid "%s: Weighting variable may not be a string variable." msgstr "" -#: src/sfm-read.c:292 +#: src/sfm-read.c:294 #, c-format msgid "" "%s: Orphaned variable index record (type 4). Type 4 records must always " "immediately follow type 3 records." msgstr "" -#: src/sfm-read.c:350 +#: src/sfm-read.c:352 #, c-format msgid "%s: Invalid subrecord length. Record: 7; Subrecord: 11" msgstr "" -#: src/sfm-read.c:404 +#: src/sfm-read.c:406 #, c-format msgid "%s: Trailing garbage in long variable name map." msgstr "" -#: src/sfm-read.c:411 +#: src/sfm-read.c:413 #, c-format msgid "%s: Long variable mapping to invalid variable name `%s'." msgstr "" -#: src/sfm-read.c:421 +#: src/sfm-read.c:423 #, c-format msgid "%s: Long variable mapping for nonexistent variable %s." msgstr "" -#: src/sfm-read.c:431 +#: src/sfm-read.c:433 #, c-format msgid "%s: Duplicate long variable name `%s' within system file." msgstr "" -#: src/sfm-read.c:459 +#: src/sfm-read.c:461 #, c-format msgid "%s: Unrecognized record type 7, subtype %d encountered in system file." msgstr "" -#: src/sfm-read.c:484 +#: src/sfm-read.c:486 #, c-format msgid "%s: Unrecognized record type %d." msgstr "" -#: src/sfm-read.c:516 +#: src/sfm-read.c:518 #, c-format msgid "" "%s: Bad size (%d) or count (%d) field on record type 7, subtype 3.\tExpected " "size %d, count 8." msgstr "" -#: src/sfm-read.c:527 +#: src/sfm-read.c:529 #, c-format msgid "" "%s: Floating-point representation in system file is not IEEE-754. PSPP " "cannot convert between floating-point formats." msgstr "" -#: src/sfm-read.c:543 +#: src/sfm-read.c:545 #, c-format msgid "" "%s: File-indicated endianness (%s) does not match endianness intuited from " "file header (%s)." msgstr "" -#: src/sfm-read.c:546 src/sfm-read.c:547 +#: src/sfm-read.c:548 src/sfm-read.c:549 msgid "big-endian" msgstr "" -#: src/sfm-read.c:546 src/sfm-read.c:547 +#: src/sfm-read.c:548 src/sfm-read.c:549 msgid "little-endian" msgstr "" -#: src/sfm-read.c:548 +#: src/sfm-read.c:550 msgid "unknown" msgstr "" -#: src/sfm-read.c:552 +#: src/sfm-read.c:554 #, c-format msgid "%s: File-indicated character representation code (%s) is not ASCII." msgstr "" -#: src/sfm-read.c:556 +#: src/sfm-read.c:558 msgid "DEC Kanji" msgstr "" -#: src/sfm-read.c:556 src/sysfile-info.c:119 +#: src/sfm-read.c:558 src/sysfile-info.c:119 msgid "Unknown" msgstr "" -#: src/sfm-read.c:572 +#: src/sfm-read.c:574 #, c-format msgid "" "%s: Bad size (%d) or count (%d) field on record type 7, subtype 4.\tExpected " "size %d, count 8." msgstr "" -#: src/sfm-read.c:587 +#: src/sfm-read.c:589 #, c-format msgid "" "%s: File-indicated value is different from internal value for at least one " @@ -4083,220 +4108,220 @@ msgid "" "%g; LOWEST: %g, %g." msgstr "" -#: src/sfm-read.c:614 +#: src/sfm-read.c:616 #, c-format msgid "" "%s: Bad magic. Proper system files begin with the four characters `$FL2'. " "This file will not be read." msgstr "" -#: src/sfm-read.c:656 +#: src/sfm-read.c:658 #, c-format msgid "" "%s: File layout code has unexpected value %d. Value should be 2, in big-" "endian or little-endian format." msgstr "" -#: src/sfm-read.c:684 +#: src/sfm-read.c:686 #, c-format msgid "%s: Number of cases in file (%ld) is not between -1 and %d." msgstr "" -#: src/sfm-read.c:689 +#: src/sfm-read.c:691 #, c-format msgid "%s: Compression bias (%g) is not the usual value of 100." msgstr "" -#: src/sfm-read.c:812 +#: src/sfm-read.c:814 #, c-format msgid "" "%s: position %d: String variable does not have proper number of continuation " "records." msgstr "" -#: src/sfm-read.c:823 +#: src/sfm-read.c:825 #, c-format msgid "%s: position %d: Superfluous long string continuation record." msgstr "" -#: src/sfm-read.c:829 +#: src/sfm-read.c:831 #, c-format msgid "%s: position %d: Bad variable type code %d." msgstr "" -#: src/sfm-read.c:832 +#: src/sfm-read.c:834 #, c-format msgid "%s: position %d: Variable label indicator field is not 0 or 1." msgstr "" -#: src/sfm-read.c:836 +#: src/sfm-read.c:838 #, c-format msgid "" "%s: position %d: Missing value indicator field is not -3, -2, 0, 1, 2, or 3." msgstr "" -#: src/sfm-read.c:842 +#: src/sfm-read.c:844 #, c-format msgid "%s: position %d: Variable name begins with invalid character." msgstr "" -#: src/sfm-read.c:846 +#: src/sfm-read.c:848 #, c-format msgid "%s: position %d: Variable name begins with lowercase letter %c." msgstr "" -#: src/sfm-read.c:850 +#: src/sfm-read.c:852 #, c-format msgid "" "%s: position %d: Variable name begins with octothorpe (`#'). Scratch " "variables should not appear in system files." msgstr "" -#: src/sfm-read.c:865 +#: src/sfm-read.c:867 #, c-format msgid "%s: position %d: Variable name character %d is lowercase letter %c." msgstr "" -#: src/sfm-read.c:874 +#: src/sfm-read.c:876 #, c-format msgid "" "%s: position %d: character `\\%03o' (%c) is not valid in a variable name." msgstr "" -#: src/sfm-read.c:881 +#: src/sfm-read.c:883 #, c-format msgid "%s: Invalid variable name `%s' within system file." msgstr "" -#: src/sfm-read.c:888 +#: src/sfm-read.c:890 #, c-format msgid "%s: Duplicate variable name `%s' within system file." msgstr "" -#: src/sfm-read.c:911 +#: src/sfm-read.c:913 #, c-format msgid "%s: Variable %s indicates variable label of invalid length %d." msgstr "" -#: src/sfm-read.c:932 +#: src/sfm-read.c:934 #, c-format msgid "%s: Long string variable %s may not have missing values." msgstr "" -#: src/sfm-read.c:953 +#: src/sfm-read.c:955 #, c-format msgid "" "%s: String variable %s may not have missing values specified as a range." msgstr "" -#: src/sfm-read.c:980 +#: src/sfm-read.c:982 #, c-format msgid "%s: Long string continuation records omitted at end of dictionary." msgstr "" -#: src/sfm-read.c:985 +#: src/sfm-read.c:987 #, c-format msgid "" "%s: System file header indicates %d variable positions but %d were read from " "file." msgstr "" -#: src/sfm-read.c:1010 +#: src/sfm-read.c:1012 #, c-format msgid "%s: %s variable %s has %s format specifier %s." msgstr "" -#: src/sfm-read.c:1063 +#: src/sfm-read.c:1065 #, c-format msgid "%s: Invalid number of labels: %d. Ignoring labels." msgstr "" -#: src/sfm-read.c:1105 +#: src/sfm-read.c:1107 #, c-format msgid "" "%s: Variable index record (type 4) does not immediately follow value label " "record (type 3) as it should." msgstr "" -#: src/sfm-read.c:1116 +#: src/sfm-read.c:1118 #, c-format msgid "" "%s: Number of variables associated with a value label (%d) is not between 1 " "and the number of variables (%d)." msgstr "" -#: src/sfm-read.c:1132 +#: src/sfm-read.c:1134 #, c-format msgid "" "%s: Variable index associated with value label (%d) is not between 1 and the " "number of values (%d)." msgstr "" -#: src/sfm-read.c:1139 +#: src/sfm-read.c:1141 #, c-format msgid "" "%s: Variable index associated with value label (%d) refers to a continuation " "of a string variable, not to an actual variable." msgstr "" -#: src/sfm-read.c:1144 +#: src/sfm-read.c:1146 #, c-format msgid "%s: Value labels are not allowed on long string variables (%s)." msgstr "" -#: src/sfm-read.c:1155 +#: src/sfm-read.c:1157 #, c-format msgid "" "%s: Variables associated with value label are not all of identical type. " "Variable %s has %s type, but variable %s has %s type." msgstr "" -#: src/sfm-read.c:1196 +#: src/sfm-read.c:1198 #, c-format msgid "%s: File contains duplicate label for value %g for variable %s." msgstr "" -#: src/sfm-read.c:1200 +#: src/sfm-read.c:1202 #, c-format msgid "%s: File contains duplicate label for value `%.*s' for variable %s." msgstr "" -#: src/sfm-read.c:1242 src/sfm-read.c:1519 +#: src/sfm-read.c:1244 src/sfm-read.c:1521 #, c-format msgid "%s: Reading system file: %s." msgstr "" -#: src/sfm-read.c:1245 src/sfm-read.c:1360 src/sfm-read.c:1402 +#: src/sfm-read.c:1247 src/sfm-read.c:1362 src/sfm-read.c:1404 #, c-format msgid "%s: Unexpected end of file." msgstr "" -#: src/sfm-read.c:1260 +#: src/sfm-read.c:1262 #, c-format msgid "%s: Seeking system file: %s." msgstr "" -#: src/sfm-read.c:1275 +#: src/sfm-read.c:1277 #, c-format msgid "%s: System file contains multiple type 6 (document) records." msgstr "" -#: src/sfm-read.c:1281 +#: src/sfm-read.c:1283 #, c-format msgid "%s: Number of document lines (%ld) must be greater than 0." msgstr "" -#: src/sfm-read.c:1313 +#: src/sfm-read.c:1315 #, c-format msgid "%s: Error reading file: %s." msgstr "" -#: src/sfm-read.c:1350 +#: src/sfm-read.c:1352 #, c-format msgid "%s: Compressed data is corrupted. Data ends in partial case." msgstr "" -#: src/sfm-read.c:1522 +#: src/sfm-read.c:1524 #, c-format msgid "%s: Partial record at end of system file." msgstr "" @@ -4316,6 +4341,14 @@ msgstr "" msgid "%s: Writing system file: %s." msgstr "" +#: src/sort-prs.c:94 +msgid "`A' or `D' expected inside parentheses." +msgstr "" + +#: src/sort-prs.c:99 +msgid "`)' expected." +msgstr "" + #: src/sort.c:85 msgid "Buffer limit must be at least 2." msgstr "" @@ -4327,14 +4360,6 @@ msgid "" "each. (PSPP workspace is currently restricted to a maximum of %d KB.)" msgstr "" -#: src/sort-prs.c:94 -msgid "`A' or `D' expected inside parentheses." -msgstr "" - -#: src/sort-prs.c:99 -msgid "`)' expected." -msgstr "" - #: src/sysfile-info.c:100 msgid "File:" msgstr "" @@ -4436,7 +4461,7 @@ msgstr "" msgid "Documents in the active file:" msgstr "" -#: src/sysfile-info.c:380 src/sysfile-info.c:519 src/vfm.c:841 +#: src/sysfile-info.c:380 src/sysfile-info.c:519 src/vfm.c:846 msgid "Label" msgstr "" @@ -4467,51 +4492,6 @@ msgstr "" msgid "Vector" msgstr "" -#: src/tab.c:258 -#, c-format -msgid "bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n" -msgstr "" - -#: src/tab.c:333 -#, c-format -msgid "" -"bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n" -msgstr "" - -#: src/temporary.c:49 -msgid "This command is not valid inside DO IF or LOOP." -msgstr "" - -#: src/temporary.c:56 -msgid "" -"This command may only appear once between procedures and procedure-like " -"commands." -msgstr "" - -#: src/title.c:60 -#, c-format -msgid "%s before: %s\n" -msgstr "" - -#: src/title.c:60 -msgid "" -msgstr "" - -#: src/title.c:72 -#, c-format -msgid "%s: `.' expected after string." -msgstr "" - -#: src/title.c:88 -#, c-format -msgid "%s after: %s\n" -msgstr "" - -#: src/title.c:143 -#, c-format -msgid "Document entered %s by %s:" -msgstr "" - #: src/t-test.q:270 msgid "TESTVAL, GROUPS and PAIRS subcommands are mutually exclusive." msgstr "" @@ -4637,6 +4617,51 @@ msgstr "" msgid "%s & %s" msgstr "" +#: src/tab.c:258 +#, c-format +msgid "bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n" +msgstr "" + +#: src/tab.c:333 +#, c-format +msgid "" +"bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n" +msgstr "" + +#: src/temporary.c:49 +msgid "This command is not valid inside DO IF or LOOP." +msgstr "" + +#: src/temporary.c:56 +msgid "" +"This command may only appear once between procedures and procedure-like " +"commands." +msgstr "" + +#: src/title.c:60 +#, c-format +msgid "%s before: %s\n" +msgstr "" + +#: src/title.c:60 +msgid "" +msgstr "" + +#: src/title.c:72 +#, c-format +msgid "%s: `.' expected after string." +msgstr "" + +#: src/title.c:88 +#, c-format +msgid "%s after: %s\n" +msgstr "" + +#: src/title.c:143 +#, c-format +msgid "Document entered %s by %s:" +msgstr "" + #: src/val-labs.c:121 #, c-format msgid "" diff --git a/po/pspp.pot b/po/pspp.pot index 34e15e47..83d71993 100644 --- a/po/pspp.pot +++ b/po/pspp.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: pspp-dev@gnu.org\n" -"POT-Creation-Date: 2006-01-24 17:40+0800\n" +"POT-Creation-Date: 2006-01-28 17:20-0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,15 +17,15 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" -#: src/aggregate.c:199 +#: src/aggregate.c:200 msgid "while expecting COLUMNWISE" msgstr "" -#: src/aggregate.c:228 +#: src/aggregate.c:229 msgid "expecting BREAK" msgstr "" -#: src/aggregate.c:233 +#: src/aggregate.c:234 msgid "" "When PRESORTED is specified, specifying sorting directions with (A) or (D) " "has no effect. Output data will be sorted the same way as the input data." @@ -79,20 +79,34 @@ msgid "" "contains the aggregate variables and the break variables." msgstr "" +#: src/any-reader.c:74 +#, c-format +msgid "An error occurred while opening \"%s\": %s." +msgstr "" + +#: src/any-reader.c:129 +#, c-format +msgid "\"%s\" is not a system or portable file." +msgstr "" + +#: src/any-reader.c:135 src/any-writer.c:80 +msgid "The inline file is not allowed here." +msgstr "" + #: src/apply-dict.c:71 #, c-format msgid "Variable %s is %s in target file, but %s in source file." msgstr "" #: src/apply-dict.c:74 src/apply-dict.c:75 src/format.c:198 src/recode.c:464 -#: src/recode.c:465 src/sfm-read.c:1014 src/sfm-read.c:1159 -#: src/sfm-read.c:1160 src/vars-atr.c:40 src/vars-atr.c:48 +#: src/recode.c:465 src/sfm-read.c:1016 src/sfm-read.c:1161 +#: src/sfm-read.c:1162 src/vars-atr.c:40 src/vars-atr.c:48 msgid "string" msgstr "" #: src/apply-dict.c:74 src/apply-dict.c:75 src/format.c:198 src/recode.c:464 -#: src/recode.c:465 src/sfm-read.c:1014 src/sfm-read.c:1159 -#: src/sfm-read.c:1160 src/vars-atr.c:40 +#: src/recode.c:465 src/sfm-read.c:1016 src/sfm-read.c:1161 +#: src/sfm-read.c:1162 src/vars-atr.c:40 msgid "numeric" msgstr "" @@ -235,8 +249,8 @@ msgstr "" msgid "Source variable count (%u) does not match target variable count (%u)." msgstr "" -#: src/autorecode.c:142 src/command.c:797 src/file-handle.q:83 src/lexer.c:440 -#: src/matrix-data.c:532 src/print.c:335 src/print.c:1044 src/sel-if.c:57 +#: src/autorecode.c:142 src/command.c:797 src/lexer.c:440 +#: src/matrix-data.c:532 src/print.c:335 src/print.c:1046 src/sel-if.c:57 #: src/sel-if.c:134 src/vector.c:197 msgid "expecting end of command" msgstr "" @@ -561,7 +575,7 @@ msgstr "" #: src/crosstabs.q:1109 src/crosstabs.q:1136 src/crosstabs.q:1156 #: src/crosstabs.q:1178 src/examine.q:1131 src/frequencies.q:1142 -#: src/frequencies.q:1263 src/sysfile-info.c:518 src/vfm.c:840 +#: src/frequencies.q:1263 src/sysfile-info.c:518 src/vfm.c:845 msgid "Value" msgstr "" @@ -935,288 +949,275 @@ msgstr "" msgid "Field too long (%d characters). Truncated after character %d." msgstr "" -#: src/data-list.c:142 -msgid "" -"DATA LIST may not use a different file from that specified on its " -"surrounding FILE TYPE." +#: src/data-list.c:144 +msgid "DATA LIST must use the same file as the enclosing FILE TYPE." msgstr "" -#: src/data-list.c:161 +#: src/data-list.c:163 msgid "The END subcommand may only be specified once." msgstr "" -#: src/data-list.c:196 +#: src/data-list.c:198 msgid "Only one of FIXED, FREE, or LIST may be specified." msgstr "" -#: src/data-list.c:346 src/print.c:296 +#: src/data-list.c:348 src/print.c:296 #, c-format msgid "" "The record number specified, %ld, is before the previous record, %d. Data " "fields must be listed in order of increasing record number." msgstr "" -#: src/data-list.c:375 src/data-list.c:1735 +#: src/data-list.c:377 src/data-list.c:1727 msgid "" "SPSS-like or FORTRAN-like format specification expected after variable names." msgstr "" -#: src/data-list.c:386 +#: src/data-list.c:388 msgid "At least one variable must be specified." msgstr "" -#: src/data-list.c:391 src/print.c:328 +#: src/data-list.c:393 src/print.c:328 msgid "" "Variables are specified on records that should not exist according to " "RECORDS subcommand." msgstr "" -#: src/data-list.c:424 src/data-list.c:438 src/print.c:520 src/print.c:533 +#: src/data-list.c:426 src/data-list.c:440 src/print.c:520 src/print.c:533 msgid "Column positions for fields must be positive." msgstr "" -#: src/data-list.c:443 +#: src/data-list.c:445 msgid "The ending column for a field must be greater than the starting column." msgstr "" -#: src/data-list.c:457 +#: src/data-list.c:459 #, c-format msgid "The %d columns %d-%d can't be evenly divided into %d fields." msgstr "" -#: src/data-list.c:477 src/print.c:561 +#: src/data-list.c:479 src/print.c:561 msgid "A format specifier on this line has extra characters on the end." msgstr "" -#: src/data-list.c:492 src/print.c:577 +#: src/data-list.c:494 src/print.c:577 msgid "The value for number of decimal places must be at least 1." msgstr "" -#: src/data-list.c:506 src/print.c:590 +#: src/data-list.c:508 src/print.c:590 #, c-format msgid "Input format %s doesn't accept decimal places." msgstr "" -#: src/data-list.c:553 src/data-list.c:649 src/data-list.c:863 +#: src/data-list.c:555 src/data-list.c:651 src/data-list.c:859 #, c-format msgid "%s is a duplicate variable name." msgstr "" -#: src/data-list.c:558 +#: src/data-list.c:560 #, c-format msgid "There is already a variable %s of a different type." msgstr "" -#: src/data-list.c:565 +#: src/data-list.c:567 #, c-format msgid "There is already a string variable %s of a different width." msgstr "" -#: src/data-list.c:640 +#: src/data-list.c:642 msgid "" "The number of format specifications exceeds the given number of variable " "names." msgstr "" -#: src/data-list.c:753 src/print.c:766 +#: src/data-list.c:755 src/print.c:766 msgid "" "There aren't enough format specifications to match the number of variable " "names given." msgstr "" -#: src/data-list.c:780 src/data-list.c:904 src/descript.c:882 src/print.c:797 -#: src/sysfile-info.c:139 src/sysfile-info.c:373 src/vfm.c:839 +#: src/data-list.c:782 src/data-list.c:900 src/descript.c:882 src/print.c:797 +#: src/sysfile-info.c:139 src/sysfile-info.c:373 src/vfm.c:844 msgid "Variable" msgstr "" -#: src/data-list.c:781 src/print.c:798 +#: src/data-list.c:783 src/print.c:798 msgid "Record" msgstr "" -#: src/data-list.c:782 src/print.c:799 +#: src/data-list.c:784 src/print.c:799 msgid "Columns" msgstr "" -#: src/data-list.c:783 src/data-list.c:905 src/print.c:800 +#: src/data-list.c:785 src/data-list.c:901 src/print.c:800 msgid "Format" msgstr "" -#: src/data-list.c:799 +#: src/data-list.c:800 #, c-format -msgid "Reading %d record from file %s." -msgid_plural "Reading %d records from file %s." +msgid "Reading %d record from %s." +msgid_plural "Reading %d records from %s." msgstr[0] "" msgstr[1] "" -#: src/data-list.c:803 +#: src/data-list.c:916 #, c-format -msgid "Reading %d record from the command file." -msgid_plural "Reading %d records from the command file." -msgstr[0] "" -msgstr[1] "" - -#: src/data-list.c:921 -#, c-format -msgid "Reading free-form data from file %s." -msgstr "" - -#: src/data-list.c:924 -msgid "Reading free-form data from the command file." +msgid "Reading free-form data from %s." msgstr "" -#: src/data-list.c:975 +#: src/data-list.c:967 #, c-format msgid "Quoted string missing terminating `%c'." msgstr "" -#: src/data-list.c:1084 +#: src/data-list.c:1076 #, c-format msgid "Partial case of %d of %d records discarded." msgstr "" -#: src/data-list.c:1138 +#: src/data-list.c:1130 #, c-format msgid "Partial case discarded. The first variable missing was %s." msgstr "" -#: src/data-list.c:1182 +#: src/data-list.c:1174 #, c-format msgid "" "Missing value(s) for all variables from %s onward. These will be filled " "with the system-missing value or blanks, as appropriate." msgstr "" -#: src/data-list.c:1259 +#: src/data-list.c:1251 msgid "Attempt to read past end of file." msgstr "" -#: src/data-list.c:1398 +#: src/data-list.c:1390 msgid "" "REPEATING DATA must use the same file as its corresponding DATA LIST or FILE " "TYPE." msgstr "" -#: src/data-list.c:1408 src/data-list.c:1442 src/data-list.c:1455 -#: src/data-list.c:1468 src/data-list.c:1502 +#: src/data-list.c:1400 src/data-list.c:1434 src/data-list.c:1447 +#: src/data-list.c:1460 src/data-list.c:1494 #, c-format msgid "%s subcommand given multiple times." msgstr "" -#: src/data-list.c:1431 +#: src/data-list.c:1423 #, c-format msgid "STARTS beginning column (%d) exceeds STARTS ending column (%d)." msgstr "" -#: src/data-list.c:1488 +#: src/data-list.c:1480 #, c-format msgid "CONTINUED beginning column (%d) exceeds CONTINUED ending column (%d)." msgstr "" -#: src/data-list.c:1511 +#: src/data-list.c:1503 #, c-format msgid "ID beginning column (%ld) must be positive." msgstr "" -#: src/data-list.c:1526 +#: src/data-list.c:1518 #, c-format msgid "ID ending column (%ld) must be positive." msgstr "" -#: src/data-list.c:1532 +#: src/data-list.c:1524 #, c-format msgid "ID ending column (%ld) cannot be less than ID beginning column (%d)." msgstr "" -#: src/data-list.c:1572 +#: src/data-list.c:1564 msgid "Missing required specification STARTS." msgstr "" -#: src/data-list.c:1574 +#: src/data-list.c:1566 msgid "Missing required specification OCCURS." msgstr "" -#: src/data-list.c:1581 +#: src/data-list.c:1573 msgid "ID specified without CONTINUED." msgstr "" -#: src/data-list.c:1592 +#: src/data-list.c:1584 #, c-format msgid "" "STARTS beginning column (%d) exceeds default STARTS ending column taken from " "file's record width (%d)." msgstr "" -#: src/data-list.c:1605 +#: src/data-list.c:1597 #, c-format msgid "" "CONTINUED beginning column (%d) exceeds default CONTINUED ending column " "taken from file's record width (%d)." msgstr "" -#: src/data-list.c:1684 +#: src/data-list.c:1676 msgid "String variable not allowed here." msgstr "" -#: src/data-list.c:1694 +#: src/data-list.c:1686 #, c-format msgid "%s (%d) must be at least 1." msgstr "" -#: src/data-list.c:1700 +#: src/data-list.c:1692 #, c-format msgid "Variable or integer expected for %s." msgstr "" -#: src/data-list.c:1825 +#: src/data-list.c:1817 #, c-format msgid "Encountered mismatched record ID \"%s\" expecting \"%s\"." msgstr "" -#: src/data-list.c:1857 +#: src/data-list.c:1849 #, c-format msgid "" "Variable %s starting in column %d extends beyond physical record length of %" "d." msgstr "" -#: src/data-list.c:1924 +#: src/data-list.c:1916 #, c-format msgid "Invalid value %d for OCCURS." msgstr "" -#: src/data-list.c:1930 +#: src/data-list.c:1922 #, c-format msgid "Beginning column for STARTS (%d) must be at least 1." msgstr "" -#: src/data-list.c:1938 +#: src/data-list.c:1930 #, c-format msgid "Ending column for STARTS (%d) is less than beginning column (%d)." msgstr "" -#: src/data-list.c:1946 +#: src/data-list.c:1938 #, c-format msgid "Invalid value %d for LENGTH." msgstr "" -#: src/data-list.c:1953 +#: src/data-list.c:1945 #, c-format msgid "Beginning column for CONTINUED (%d) must be at least 1." msgstr "" -#: src/data-list.c:1961 +#: src/data-list.c:1953 #, c-format msgid "Ending column for CONTINUED (%d) is less than beginning column (%d)." msgstr "" -#: src/data-list.c:1993 +#: src/data-list.c:1985 #, c-format msgid "" "Number of repetitions specified on OCCURS (%d) exceed number of repetitions " "available in space on STARTS (%d), and CONTINUED not specified." msgstr "" -#: src/data-list.c:2011 +#: src/data-list.c:2003 #, c-format msgid "Unexpected end of file with %d repetitions remaining out of %d." msgstr "" @@ -1368,42 +1369,42 @@ msgstr "" msgid "Valid cases = %g; cases with missing value(s) = %g." msgstr "" -#: src/dfm-read.c:158 +#: src/dfm-read.c:136 #, c-format msgid "Could not open \"%s\" for reading as a data file: %s." msgstr "" -#: src/dfm-read.c:191 src/dfm-read.c:209 +#: src/dfm-read.c:168 src/dfm-read.c:186 msgid "BEGIN DATA expected." msgstr "" -#: src/dfm-read.c:218 +#: src/dfm-read.c:195 msgid "" "Unexpected end-of-file while reading data in BEGIN DATA. This probably " "indicates a missing or misformatted END DATA command. END DATA must appear " "by itself on a single line with exactly one space between words." msgstr "" -#: src/dfm-read.c:251 src/dfm-read.c:271 +#: src/dfm-read.c:227 src/dfm-read.c:247 #, c-format msgid "Error reading file %s: %s." msgstr "" -#: src/dfm-read.c:274 +#: src/dfm-read.c:250 #, c-format msgid "%s: Partial record at end of file." msgstr "" -#: src/dfm-read.c:317 +#: src/dfm-read.c:299 #, c-format msgid "Attempt to read beyond end-of-file on file %s." msgstr "" -#: src/dfm-read.c:320 +#: src/dfm-read.c:302 msgid "Attempt to read beyond END DATA." msgstr "" -#: src/dfm-read.c:467 +#: src/dfm-read.c:446 msgid "" "This command is not valid here since the current input program does not " "access the inline file." @@ -1721,67 +1722,61 @@ msgstr "" msgid "%s is a PSPP extension." msgstr "" -#: src/file-handle-def.c:229 +#: src/file-handle-def.c:303 #, c-format -msgid "Can't open %s as a %s because it is already open as a %s" +msgid "Can't open %s as a %s because it is already open as a %s." msgstr "" -#: src/file-handle-def.c:236 +#: src/file-handle-def.c:310 #, c-format -msgid "Can't open %s as a %s for %s because it is already open for %s" +msgid "Can't open %s as a %s for %s because it is already open for %s." msgstr "" -#: src/file-handle-def.c:244 +#: src/file-handle-def.c:318 #, c-format -msgid "Can't re-open %s as a %s for %s" +msgid "Can't re-open %s as a %s for %s." msgstr "" -#: src/file-handle.q:68 +#: src/file-handle.q:69 #, c-format msgid "" -"File handle %s already refers to file %s. File handles cannot be redefined " -"within a session." +"File handle %s is already defined. Use CLOSE FILE HANDLE before redefining " +"a file handle." msgstr "" -#: src/file-handle.q:89 -msgid "The FILE HANDLE required subcommand NAME is not present." -msgstr "" - -#: src/file-handle.q:104 +#: src/file-handle.q:101 #, c-format msgid "" "Fixed-length records were specified on /RECFORM, but record length was not " "specified on /LRECL. Assuming %d-character records." msgstr "" -#: src/file-handle.q:109 +#: src/file-handle.q:106 #, c-format msgid "" "Record length (%ld) must be at least one byte. Assuming %d-character " "records." msgstr "" -#: src/file-handle.q:139 -msgid "expecting a file name or handle name" +#: src/file-handle.q:152 +msgid "file" msgstr "" -#: src/filename.c:227 -#, c-format -msgid "Searching for `%s'..." +#: src/file-handle.q:154 +msgid "inline file" msgstr "" -#: src/filename.c:235 src/filename.c:267 -msgid "Search unsuccessful!" +#: src/file-handle.q:156 +msgid "scratch file" msgstr "" -#: src/filename.c:260 -#, c-format -msgid "Found `%s'." +#: src/file-handle.q:177 +msgid "expecting a file name or handle name" msgstr "" -#: src/filename.c:665 +#: src/file-handle.q:204 #, c-format -msgid "Not opening pipe file `%s' because SAFER option set." +msgid "Handle for %s not allowed here." msgstr "" #: src/file-type.c:133 @@ -1922,6 +1917,25 @@ msgstr "" msgid "Unknown record type %g." msgstr "" +#: src/filename.c:227 +#, c-format +msgid "Searching for `%s'..." +msgstr "" + +#: src/filename.c:235 src/filename.c:267 +msgid "Search unsuccessful!" +msgstr "" + +#: src/filename.c:260 +#, c-format +msgid "Found `%s'." +msgstr "" + +#: src/filename.c:677 +#, c-format +msgid "Not opening pipe file `%s' because SAFER option set." +msgstr "" + #: src/flip.c:88 msgid "" "FLIP ignores TEMPORARY. Temporary transformations will be made permanent." @@ -1983,6 +1997,29 @@ msgstr "" msgid "Unexpected end of file reading FLIP temporary file." msgstr "" +#: src/format-prs.c:66 +msgid "X and T format specifiers not allowed here." +msgstr "" + +#: src/format-prs.c:74 +#, c-format +msgid "%.*s is not a valid data format." +msgstr "" + +#: src/format-prs.c:115 +msgid "Format specifier expected." +msgstr "" + +#: src/format-prs.c:127 +#, c-format +msgid "Data format %s does not specify a width." +msgstr "" + +#: src/format-prs.c:145 +#, c-format +msgid "Data format %s is not valid." +msgstr "" + #: src/format.c:73 #, c-format msgid "Format specifies a bad type (%d)" @@ -2039,11 +2076,11 @@ msgstr "" msgid "%s variables are not compatible with %s format %s." msgstr "" -#: src/format.c:197 src/pfm-read.c:476 src/sfm-read.c:1012 src/sfm-read.c:1021 +#: src/format.c:197 src/pfm-read.c:476 src/sfm-read.c:1014 src/sfm-read.c:1023 msgid "String" msgstr "" -#: src/format.c:197 src/pfm-read.c:476 src/sfm-read.c:1012 src/sfm-read.c:1021 +#: src/format.c:197 src/pfm-read.c:476 src/sfm-read.c:1014 src/sfm-read.c:1023 msgid "Numeric" msgstr "" @@ -2052,29 +2089,6 @@ msgstr "" msgid "String variable with width %d not compatible with format %s." msgstr "" -#: src/format-prs.c:66 -msgid "X and T format specifiers not allowed here." -msgstr "" - -#: src/format-prs.c:74 -#, c-format -msgid "%.*s is not a valid data format." -msgstr "" - -#: src/format-prs.c:115 -msgid "Format specifier expected." -msgstr "" - -#: src/format-prs.c:127 -#, c-format -msgid "Data format %s does not specify a width." -msgstr "" - -#: src/format-prs.c:145 -#, c-format -msgid "Data format %s is not valid." -msgstr "" - #: src/formats.c:89 msgid "`(' expected after variable list" msgstr "" @@ -2168,16 +2182,20 @@ msgstr "" msgid "No valid data for variable %s; statistics not displayed." msgstr "" -#: src/get.c:306 src/get.c:320 src/get.c:345 +#: src/get.c:108 +msgid "expecting COMM or TAPE" +msgstr "" + +#: src/get.c:343 src/get.c:357 src/get.c:382 #, c-format msgid "expecting %s or %s" msgstr "" -#: src/get.c:552 src/print.c:179 +#: src/get.c:586 src/print.c:179 msgid "expecting a valid subcommand" msgstr "" -#: src/get.c:585 +#: src/get.c:619 #, c-format msgid "" "Cannot rename %s as %s because there already exists a variable named %s. To " @@ -2185,85 +2203,81 @@ msgid "" "as \"/RENAME (A=B)(B=C)(C=A)\", or equivalently, \"/RENAME (A B C=B C A)\"." msgstr "" -#: src/get.c:610 +#: src/get.c:644 msgid "`=' expected after variable list." msgstr "" -#: src/get.c:617 +#: src/get.c:651 #, c-format msgid "" "Number of variables on left side of `=' (%d) does not match number of " "variables on right side (%d), in parenthesized group %d of RENAME subcommand." msgstr "" -#: src/get.c:630 +#: src/get.c:664 #, c-format msgid "Requested renaming duplicates variable name %s." msgstr "" -#: src/get.c:660 +#: src/get.c:694 msgid "Cannot DROP all variables from dictionary." msgstr "" -#: src/get.c:835 +#: src/get.c:869 msgid "The active file may not be specified more than once." msgstr "" -#: src/get.c:844 +#: src/get.c:878 msgid "Cannot specify the active file since no active file has been defined." msgstr "" -#: src/get.c:852 +#: src/get.c:886 msgid "" "MATCH FILES may not be used after TEMPORARY when the active file is an input " "source. Temporary transformations will be made permanent." msgstr "" -#: src/get.c:890 +#: src/get.c:924 msgid "Multiple IN subcommands for a single FILE or TABLE." msgstr "" -#: src/get.c:910 +#: src/get.c:944 msgid "BY may appear at most once." msgstr "" -#: src/get.c:930 +#: src/get.c:964 #, c-format msgid "File %s lacks BY variable %s." msgstr "" -#: src/get.c:944 +#: src/get.c:978 msgid "FIRST may appear at most once." msgstr "" -#: src/get.c:958 +#: src/get.c:992 msgid "LAST may appear at most once." msgstr "" -#: src/get.c:999 +#: src/get.c:1033 msgid "BY is required when TABLE is specified." msgstr "" -#: src/get.c:1004 +#: src/get.c:1038 msgid "BY is required when IN is specified." msgstr "" -#: src/get.c:1032 +#: src/get.c:1066 #, c-format msgid "IN variable name %s duplicates an existing variable name." msgstr "" -#: src/get.c:1461 +#: src/get.c:1495 #, c-format msgid "" "Variable %s in file %s (%s) has different type or width from the same " "variable in earlier file (%s)." msgstr "" -#: src/get.c:1545 -msgid "expecting COMM or TAPE" -msgstr "" - #: src/getl.c:139 #, c-format msgid "Can't find `%s' in include file search path." @@ -3270,34 +3284,34 @@ msgstr "" msgid "Cannot change mode of %s: %s" msgstr "" -#: src/pfm-read.c:87 +#: src/pfm-read.c:97 #, c-format msgid "portable file %s corrupt at offset %ld: " msgstr "" -#: src/pfm-read.c:114 +#: src/pfm-read.c:124 msgid "unexpected end of file" msgstr "" -#: src/pfm-read.c:172 +#: src/pfm-read.c:182 #, c-format msgid "" "An error occurred while opening \"%s\" for reading as a portable file: %s." msgstr "" -#: src/pfm-read.c:190 +#: src/pfm-read.c:200 msgid "Data record expected." msgstr "" -#: src/pfm-read.c:298 +#: src/pfm-read.c:308 msgid "Missing numeric terminator." msgstr "" -#: src/pfm-read.c:321 +#: src/pfm-read.c:331 msgid "Invalid integer." msgstr "" -#: src/pfm-read.c:332 +#: src/pfm-read.c:342 #, c-format msgid "Bad string length %d." msgstr "" @@ -3317,12 +3331,12 @@ msgstr "" msgid "Bad time string length %d." msgstr "" -#: src/pfm-read.c:468 src/sfm-read.c:1004 +#: src/pfm-read.c:468 src/sfm-read.c:1006 #, c-format msgid "%s: Bad format specifier byte (%d)." msgstr "" -#: src/pfm-read.c:475 src/sfm-read.c:1020 +#: src/pfm-read.c:475 src/sfm-read.c:1022 #, c-format msgid "%s variable %s has invalid format specifier %s." msgstr "" @@ -3398,7 +3412,7 @@ msgstr "" msgid "%s: Writing portable file: %s." msgstr "" -#: src/pfm-write.c:468 +#: src/pfm-write.c:466 #, c-format msgid "%s: Closing portable file: %s." msgstr "" @@ -3585,19 +3599,23 @@ msgstr "" #: src/print.c:839 #, c-format -msgid "Writing %d record(s) to file %s." -msgstr "" +msgid "Writing %d record to %s." +msgid_plural "Writing %d records to %s." +msgstr[0] "" +msgstr[1] "" -#: src/print.c:842 +#: src/print.c:843 #, c-format -msgid "Writing %d record(s) to the listing file." -msgstr "" +msgid "Writing %d record." +msgid_plural "Writing %d records." +msgstr[0] "" +msgstr[1] "" -#: src/print.c:1082 +#: src/print.c:1084 msgid "The expression on PRINT SPACE evaluated to the system-missing value." msgstr "" -#: src/print.c:1085 +#: src/print.c:1087 #, c-format msgid "The expression on PRINT SPACE evaluated to %g." msgstr "" @@ -3807,6 +3825,13 @@ msgstr "" msgid "Cannot sample %d observations from a population of %d." msgstr "" +#: src/scratch-reader.c:59 +#, c-format +msgid "" +"Scratch file handle %s has not yet been written, using SAVE or another " +"procedure, so it cannot yet be used for reading." +msgstr "" + #: src/sel-if.c:103 msgid "The filter variable must be numeric." msgstr "" @@ -3952,131 +3977,131 @@ msgstr "" msgid "corrupt system file: " msgstr "" -#: src/sfm-read.c:151 src/sfm-write.c:931 +#: src/sfm-read.c:149 src/sfm-write.c:929 #, c-format msgid "%s: Closing system file: %s." msgstr "" -#: src/sfm-read.c:237 +#: src/sfm-read.c:239 #, c-format msgid "" "An error occurred while opening \"%s\" for reading as a system file: %s." msgstr "" -#: src/sfm-read.c:255 +#: src/sfm-read.c:257 #, c-format msgid "" "%s: Index of weighting variable (%d) is not between 0 and number of elements " "per case (%d)." msgstr "" -#: src/sfm-read.c:264 +#: src/sfm-read.c:266 #, c-format msgid "" "%s: Weighting variable may not be a continuation of a long string variable." msgstr "" -#: src/sfm-read.c:267 +#: src/sfm-read.c:269 #, c-format msgid "%s: Weighting variable may not be a string variable." msgstr "" -#: src/sfm-read.c:292 +#: src/sfm-read.c:294 #, c-format msgid "" "%s: Orphaned variable index record (type 4). Type 4 records must always " "immediately follow type 3 records." msgstr "" -#: src/sfm-read.c:350 +#: src/sfm-read.c:352 #, c-format msgid "%s: Invalid subrecord length. Record: 7; Subrecord: 11" msgstr "" -#: src/sfm-read.c:404 +#: src/sfm-read.c:406 #, c-format msgid "%s: Trailing garbage in long variable name map." msgstr "" -#: src/sfm-read.c:411 +#: src/sfm-read.c:413 #, c-format msgid "%s: Long variable mapping to invalid variable name `%s'." msgstr "" -#: src/sfm-read.c:421 +#: src/sfm-read.c:423 #, c-format msgid "%s: Long variable mapping for nonexistent variable %s." msgstr "" -#: src/sfm-read.c:431 +#: src/sfm-read.c:433 #, c-format msgid "%s: Duplicate long variable name `%s' within system file." msgstr "" -#: src/sfm-read.c:459 +#: src/sfm-read.c:461 #, c-format msgid "%s: Unrecognized record type 7, subtype %d encountered in system file." msgstr "" -#: src/sfm-read.c:484 +#: src/sfm-read.c:486 #, c-format msgid "%s: Unrecognized record type %d." msgstr "" -#: src/sfm-read.c:516 +#: src/sfm-read.c:518 #, c-format msgid "" "%s: Bad size (%d) or count (%d) field on record type 7, subtype 3.\tExpected " "size %d, count 8." msgstr "" -#: src/sfm-read.c:527 +#: src/sfm-read.c:529 #, c-format msgid "" "%s: Floating-point representation in system file is not IEEE-754. PSPP " "cannot convert between floating-point formats." msgstr "" -#: src/sfm-read.c:543 +#: src/sfm-read.c:545 #, c-format msgid "" "%s: File-indicated endianness (%s) does not match endianness intuited from " "file header (%s)." msgstr "" -#: src/sfm-read.c:546 src/sfm-read.c:547 +#: src/sfm-read.c:548 src/sfm-read.c:549 msgid "big-endian" msgstr "" -#: src/sfm-read.c:546 src/sfm-read.c:547 +#: src/sfm-read.c:548 src/sfm-read.c:549 msgid "little-endian" msgstr "" -#: src/sfm-read.c:548 +#: src/sfm-read.c:550 msgid "unknown" msgstr "" -#: src/sfm-read.c:552 +#: src/sfm-read.c:554 #, c-format msgid "%s: File-indicated character representation code (%s) is not ASCII." msgstr "" -#: src/sfm-read.c:556 +#: src/sfm-read.c:558 msgid "DEC Kanji" msgstr "" -#: src/sfm-read.c:556 src/sysfile-info.c:119 +#: src/sfm-read.c:558 src/sysfile-info.c:119 msgid "Unknown" msgstr "" -#: src/sfm-read.c:572 +#: src/sfm-read.c:574 #, c-format msgid "" "%s: Bad size (%d) or count (%d) field on record type 7, subtype 4.\tExpected " "size %d, count 8." msgstr "" -#: src/sfm-read.c:587 +#: src/sfm-read.c:589 #, c-format msgid "" "%s: File-indicated value is different from internal value for at least one " @@ -4084,220 +4109,220 @@ msgid "" "%g; LOWEST: %g, %g." msgstr "" -#: src/sfm-read.c:614 +#: src/sfm-read.c:616 #, c-format msgid "" "%s: Bad magic. Proper system files begin with the four characters `$FL2'. " "This file will not be read." msgstr "" -#: src/sfm-read.c:656 +#: src/sfm-read.c:658 #, c-format msgid "" "%s: File layout code has unexpected value %d. Value should be 2, in big-" "endian or little-endian format." msgstr "" -#: src/sfm-read.c:684 +#: src/sfm-read.c:686 #, c-format msgid "%s: Number of cases in file (%ld) is not between -1 and %d." msgstr "" -#: src/sfm-read.c:689 +#: src/sfm-read.c:691 #, c-format msgid "%s: Compression bias (%g) is not the usual value of 100." msgstr "" -#: src/sfm-read.c:812 +#: src/sfm-read.c:814 #, c-format msgid "" "%s: position %d: String variable does not have proper number of continuation " "records." msgstr "" -#: src/sfm-read.c:823 +#: src/sfm-read.c:825 #, c-format msgid "%s: position %d: Superfluous long string continuation record." msgstr "" -#: src/sfm-read.c:829 +#: src/sfm-read.c:831 #, c-format msgid "%s: position %d: Bad variable type code %d." msgstr "" -#: src/sfm-read.c:832 +#: src/sfm-read.c:834 #, c-format msgid "%s: position %d: Variable label indicator field is not 0 or 1." msgstr "" -#: src/sfm-read.c:836 +#: src/sfm-read.c:838 #, c-format msgid "" "%s: position %d: Missing value indicator field is not -3, -2, 0, 1, 2, or 3." msgstr "" -#: src/sfm-read.c:842 +#: src/sfm-read.c:844 #, c-format msgid "%s: position %d: Variable name begins with invalid character." msgstr "" -#: src/sfm-read.c:846 +#: src/sfm-read.c:848 #, c-format msgid "%s: position %d: Variable name begins with lowercase letter %c." msgstr "" -#: src/sfm-read.c:850 +#: src/sfm-read.c:852 #, c-format msgid "" "%s: position %d: Variable name begins with octothorpe (`#'). Scratch " "variables should not appear in system files." msgstr "" -#: src/sfm-read.c:865 +#: src/sfm-read.c:867 #, c-format msgid "%s: position %d: Variable name character %d is lowercase letter %c." msgstr "" -#: src/sfm-read.c:874 +#: src/sfm-read.c:876 #, c-format msgid "" "%s: position %d: character `\\%03o' (%c) is not valid in a variable name." msgstr "" -#: src/sfm-read.c:881 +#: src/sfm-read.c:883 #, c-format msgid "%s: Invalid variable name `%s' within system file." msgstr "" -#: src/sfm-read.c:888 +#: src/sfm-read.c:890 #, c-format msgid "%s: Duplicate variable name `%s' within system file." msgstr "" -#: src/sfm-read.c:911 +#: src/sfm-read.c:913 #, c-format msgid "%s: Variable %s indicates variable label of invalid length %d." msgstr "" -#: src/sfm-read.c:932 +#: src/sfm-read.c:934 #, c-format msgid "%s: Long string variable %s may not have missing values." msgstr "" -#: src/sfm-read.c:953 +#: src/sfm-read.c:955 #, c-format msgid "" "%s: String variable %s may not have missing values specified as a range." msgstr "" -#: src/sfm-read.c:980 +#: src/sfm-read.c:982 #, c-format msgid "%s: Long string continuation records omitted at end of dictionary." msgstr "" -#: src/sfm-read.c:985 +#: src/sfm-read.c:987 #, c-format msgid "" "%s: System file header indicates %d variable positions but %d were read from " "file." msgstr "" -#: src/sfm-read.c:1010 +#: src/sfm-read.c:1012 #, c-format msgid "%s: %s variable %s has %s format specifier %s." msgstr "" -#: src/sfm-read.c:1063 +#: src/sfm-read.c:1065 #, c-format msgid "%s: Invalid number of labels: %d. Ignoring labels." msgstr "" -#: src/sfm-read.c:1105 +#: src/sfm-read.c:1107 #, c-format msgid "" "%s: Variable index record (type 4) does not immediately follow value label " "record (type 3) as it should." msgstr "" -#: src/sfm-read.c:1116 +#: src/sfm-read.c:1118 #, c-format msgid "" "%s: Number of variables associated with a value label (%d) is not between 1 " "and the number of variables (%d)." msgstr "" -#: src/sfm-read.c:1132 +#: src/sfm-read.c:1134 #, c-format msgid "" "%s: Variable index associated with value label (%d) is not between 1 and the " "number of values (%d)." msgstr "" -#: src/sfm-read.c:1139 +#: src/sfm-read.c:1141 #, c-format msgid "" "%s: Variable index associated with value label (%d) refers to a continuation " "of a string variable, not to an actual variable." msgstr "" -#: src/sfm-read.c:1144 +#: src/sfm-read.c:1146 #, c-format msgid "%s: Value labels are not allowed on long string variables (%s)." msgstr "" -#: src/sfm-read.c:1155 +#: src/sfm-read.c:1157 #, c-format msgid "" "%s: Variables associated with value label are not all of identical type. " "Variable %s has %s type, but variable %s has %s type." msgstr "" -#: src/sfm-read.c:1196 +#: src/sfm-read.c:1198 #, c-format msgid "%s: File contains duplicate label for value %g for variable %s." msgstr "" -#: src/sfm-read.c:1200 +#: src/sfm-read.c:1202 #, c-format msgid "%s: File contains duplicate label for value `%.*s' for variable %s." msgstr "" -#: src/sfm-read.c:1242 src/sfm-read.c:1519 +#: src/sfm-read.c:1244 src/sfm-read.c:1521 #, c-format msgid "%s: Reading system file: %s." msgstr "" -#: src/sfm-read.c:1245 src/sfm-read.c:1360 src/sfm-read.c:1402 +#: src/sfm-read.c:1247 src/sfm-read.c:1362 src/sfm-read.c:1404 #, c-format msgid "%s: Unexpected end of file." msgstr "" -#: src/sfm-read.c:1260 +#: src/sfm-read.c:1262 #, c-format msgid "%s: Seeking system file: %s." msgstr "" -#: src/sfm-read.c:1275 +#: src/sfm-read.c:1277 #, c-format msgid "%s: System file contains multiple type 6 (document) records." msgstr "" -#: src/sfm-read.c:1281 +#: src/sfm-read.c:1283 #, c-format msgid "%s: Number of document lines (%ld) must be greater than 0." msgstr "" -#: src/sfm-read.c:1313 +#: src/sfm-read.c:1315 #, c-format msgid "%s: Error reading file: %s." msgstr "" -#: src/sfm-read.c:1350 +#: src/sfm-read.c:1352 #, c-format msgid "%s: Compressed data is corrupted. Data ends in partial case." msgstr "" -#: src/sfm-read.c:1522 +#: src/sfm-read.c:1524 #, c-format msgid "%s: Partial record at end of system file." msgstr "" @@ -4317,6 +4342,14 @@ msgstr "" msgid "%s: Writing system file: %s." msgstr "" +#: src/sort-prs.c:94 +msgid "`A' or `D' expected inside parentheses." +msgstr "" + +#: src/sort-prs.c:99 +msgid "`)' expected." +msgstr "" + #: src/sort.c:85 msgid "Buffer limit must be at least 2." msgstr "" @@ -4328,14 +4361,6 @@ msgid "" "each. (PSPP workspace is currently restricted to a maximum of %d KB.)" msgstr "" -#: src/sort-prs.c:94 -msgid "`A' or `D' expected inside parentheses." -msgstr "" - -#: src/sort-prs.c:99 -msgid "`)' expected." -msgstr "" - #: src/sysfile-info.c:100 msgid "File:" msgstr "" @@ -4437,7 +4462,7 @@ msgstr "" msgid "Documents in the active file:" msgstr "" -#: src/sysfile-info.c:380 src/sysfile-info.c:519 src/vfm.c:841 +#: src/sysfile-info.c:380 src/sysfile-info.c:519 src/vfm.c:846 msgid "Label" msgstr "" @@ -4468,51 +4493,6 @@ msgstr "" msgid "Vector" msgstr "" -#: src/tab.c:258 -#, c-format -msgid "bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n" -msgstr "" - -#: src/tab.c:333 -#, c-format -msgid "" -"bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n" -msgstr "" - -#: src/temporary.c:49 -msgid "This command is not valid inside DO IF or LOOP." -msgstr "" - -#: src/temporary.c:56 -msgid "" -"This command may only appear once between procedures and procedure-like " -"commands." -msgstr "" - -#: src/title.c:60 -#, c-format -msgid "%s before: %s\n" -msgstr "" - -#: src/title.c:60 -msgid "" -msgstr "" - -#: src/title.c:72 -#, c-format -msgid "%s: `.' expected after string." -msgstr "" - -#: src/title.c:88 -#, c-format -msgid "%s after: %s\n" -msgstr "" - -#: src/title.c:143 -#, c-format -msgid "Document entered %s by %s:" -msgstr "" - #: src/t-test.q:270 msgid "TESTVAL, GROUPS and PAIRS subcommands are mutually exclusive." msgstr "" @@ -4638,6 +4618,51 @@ msgstr "" msgid "%s & %s" msgstr "" +#: src/tab.c:258 +#, c-format +msgid "bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n" +msgstr "" + +#: src/tab.c:333 +#, c-format +msgid "" +"bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n" +msgstr "" + +#: src/temporary.c:49 +msgid "This command is not valid inside DO IF or LOOP." +msgstr "" + +#: src/temporary.c:56 +msgid "" +"This command may only appear once between procedures and procedure-like " +"commands." +msgstr "" + +#: src/title.c:60 +#, c-format +msgid "%s before: %s\n" +msgstr "" + +#: src/title.c:60 +msgid "" +msgstr "" + +#: src/title.c:72 +#, c-format +msgid "%s: `.' expected after string." +msgstr "" + +#: src/title.c:88 +#, c-format +msgid "%s after: %s\n" +msgstr "" + +#: src/title.c:143 +#, c-format +msgid "Document entered %s by %s:" +msgstr "" + #: src/val-labs.c:121 #, c-format msgid "" diff --git a/src/ChangeLog b/src/ChangeLog index bc4527fb..a85914ac 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,6 +1,207 @@ +Sat Jan 28 17:45:36 2006 Ben Pfaff + + Cleaner (faster?) way to compact cases. + + * dictionary.c: (dict_compact_case) Removed. + (dict_needs_compaction) New function. + (struct copy_map) New structure. + (struct dict_compactor) New structure. + (dict_make_compactor) New function. + (dict_compactor_compact) New function. + (dict_compactor_destroy) New function. + +Sat Jan 28 17:24:22 2006 Ben Pfaff + + Cleanups. + + * data-list.c: Make data_list_source_class static. + (dump_fixed_table) Use fh_get_name() to describe source of data. + (dump_free_table) Ditto. + (cmd_repeating_data) Eliminate special cases for inline file. + + * dictionary.c: (dict_contains_var) Change return value from int to + bool. + (dict_rename_vars) Ditto. + (dict_create_vector) Ditto. + +Sat Jan 28 17:20:50 2006 Ben Pfaff + + Add scratch file handles. + + Now a file handle can refer to a disk file, to an in-memory + structure, or to the "inline" file, instead of just to a disk + file. The introduction of new categories means that special cases + for the inline file in a few places could be eliminated, but it + also means that code that assumed that a handle refers to a file + has to check for that. + + Also, now file handles can be freed, so code now must be sure not + to access a handle after closing it (with fh_close()). + + * Makefile.am: Add any-reader.c, any-reader.h, any-writer.c, + any-writer.h, scratch-handle.c, scratch-handle.h, + scratch-reader.c, scratch-reader.h, scratch-writer.c, + scratch-writer.h to pspp_SOURCES. + + * any-reader.c: New file. + + * any-reader.h: New file. + + * any-writer.c: New file. + + * any-writer.h: New file. + + * scratch-handle.c: New file. + + * scratch-handle.h: New file. + + * scratch-reader.c: New file. + + * scratch-reader.h: New file. + + * scratch-writer.c: New file. + + * scratch-writer.h: New file. + + * aggregate.c: Use an any_writer instead of an sfm_writer, to add + flexibility. + + * apply-dict.c: Use an any_reader instead of an sfm_reader, to add + flexibility. + + * command.def: Add CLOSE FILE HANDLE command. + + * dfm-reader.c: Now fewer special cases for inline file. + (static var inline_open_cnt) Removed. + (static var inline_file) Removed. + (dfm_close_reader) Eliminate a special case for inline file. + Reorganize to avoid access-after-free. + (dfm_open_reader) Eliminate a special case for inline file. + (read_inline_record) Use bool instead of int. No need to + increment line number. + (read_file_record) Use bool instead of int. + (read_record) Check whether file handle is inline file, instead of + for null pointer. + (dfm_eof) Ditto. + (dfm_expand_tabs) Ditto. + (dfm_push) Ditto. + (dfm_pop) Ditto. + (cmd_begin_data) Fix inaccurate check for whether the inline file + is in use--now we can tell by checking whether the inline file's + open count is positive. + + * file-handle-def.c: (struct file_handle) Reorder members. Add + `deleted' member. Add `referent' member. Add `sh' member. + (static var default_handle) New variable. + (static var inline_file) New variable. + (fh_init) Initialize inline file. + (free_handle) New function. + (fh_done) Rewrite. + (fh_from_name) Don't return deleted handles. + (fh_from_filename) Ditto. + (fh_create) Removed. + (create_handle) New function. + (fh_create_file) New function. + (fh_create_scratch) New function. + (fh_inline_file) New function. + (fh_free) Rewrite. + (fh_open) Now requires a referent type mask and verifies it. All + references updated. + (fh_close) If open_cnt goes to 0 on a deleted handle, free it. + (fh_is_open) New function. + (fh_get_referent) New function. + (fh_get_filename) Limit to handles that refer to files. + (fh_get_mode) Ditto. + (fh_get_record_width) Limit to handles that refer to files or the + inline file. + (fh_get_tab_width) Ditto. + (fh_get_scratch_handle) New function. + (fh_set_scratch_handle) New function. + (fh_get_default_handle) New function. + (fh_set_default_handle) New function. + + * file-handle.h: (enum fh_referent) New type. + (enum fh_mode) Rename MODE_TEXT to FH_MODE_TEXT, MODE_BINARY to + FH_MODE_BINARY, and update all usages. + + * file-handle.q: Add "scratch" as a possible mode. + (cmd_file_handle) Mention CLOSE FILE HANDLE in error message. + Use lex_end_of_command(), lex_sbc_missing(). Support creating + scratch handles. + (cmd_close_file_handle) New function. + (referent_name) New function. + (fh_parse) Now takes a referent type mask to specify handles that + can be accepted. Updated all references. + + * filename.c: (fn_extension) New function. + + * get.c: Use any_reader and any_writer and thereby merge code that + has been duplicated for each kind of file. Also, we had something + here called `any_writer' before, so its name had to be changed to + `case_writer'. + (enum operation) Removed, because unused. + (struct get_pgm) Removed. + (get_pgm_free) Removed. + (get_source_destroy) Removed. + (get_source_read) Removed. + (global var get_source_class) Removed. + (static var case_reader_source_class) Removed. + (enum reader_command) New enum. + (struct case_reader_pgm) New struct. + (parse_read_command) New function. + (case_reader_pgm_free) New function. + (case_reader_source_destroy) New function. + (case_Reader_source_Read) New function. + (cmd_get) Rewrote as a call to parse_read_command(). + (cmd_import) Ditto. + (struct any_writer) Rename to case_writer. Drop `writer_type', + `writer' members in favor of an `any_writer' member named + `writer'. + (any_writer_destroy) Rename case_writer_destroy. Use + any_writer_close(). + (parse_write_command) Allow scratch files. Use any_writer. + (any_writer_write_case) Rename case_writer_write_case(). Use + any_writer_write(). +` (struct mtf_file) Use any_reader. + (cmd_match_files) Allow scratch files. Use any_reader. + (mtf_free_file) Use any_reader_close(). + (mtf_read_nonactive_records) Use any_reader_read(). + (mtf_processing) Use any_reader_read(). + (struct import_pgm) Removed. + (import_pgm_free) Removed. + (import_source_destroy) Removed. + (import_source_read) Removed. + (global var import_source_class) Removed. + + * glob.c: (global var default_handle) Removed. Replaced all + references by fh_get_default_handle() or fh_set_default_handle(). + + * pfm-read.c: (static var portable_to_local) Moved from inside + read_header() to top level. + (pfm_detect) New function. + + * pfm-write.c: (pfm_write_case) Make case argument const. + Reorganize to avoid access-after-free. + + * print.c: (dump_table) Use fh_get_name() to describe source of + data. + + * sfm-read.c: (sfm_close_reader) Reorganize to avoid + access-after-free. + (sfm_detect) New function. + + * str.c: (str_lowercase) New function. + + * vfm.c: Use new compaction interface. + (static var compaction_necessary) Removed. + (static var compactor) New variable. + (open_active_file) Initialize compactor. + (write_case) Use compactor. + (close_active_file) Free compactor. + Wed Jan 11 19:28:39 2006 Ben Pfaff - Clean up file handle code in preparation to add temporary file + Clean up file handle code in preparation to add scratch file handles. * file-handle-def.c: Lots of formatting cleanup. Added function diff --git a/src/Makefile.am b/src/Makefile.am index 72731c98..84f7c881 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -72,6 +72,10 @@ pspp_SOURCES = \ algorithm.h \ alloc.c \ alloc.h \ + any-reader.c \ + any-reader.h \ + any-writer.c \ + any-writer.h \ apply-dict.c \ ascii.c \ autorecode.c \ @@ -221,6 +225,12 @@ pspp_SOURCES = \ tab.c \ tab.h \ temporary.c \ + scratch-handle.c \ + scratch-handle.h \ + scratch-reader.c \ + scratch-reader.h \ + scratch-writer.c \ + scratch-writer.h \ mkfile.c \ mkfile.h \ title.c \ diff --git a/src/aggregate.c b/src/aggregate.c index 86f35287..67e8bb91 100644 --- a/src/aggregate.c +++ b/src/aggregate.c @@ -21,6 +21,7 @@ #include "error.h" #include #include "alloc.h" +#include "any-writer.h" #include "case.h" #include "casefile.h" #include "command.h" @@ -121,7 +122,7 @@ enum missing_treatment struct agr_proc { /* We have either an output file or a sink. */ - struct sfm_writer *writer; /* Output file, or null if none. */ + struct any_writer *writer; /* Output file, or null if none. */ struct case_sink *sink; /* Sink, or null if none. */ /* Break variables. */ @@ -181,7 +182,7 @@ cmd_aggregate (void) lex_match ('='); if (!lex_match ('*')) { - out_file = fh_parse (); + out_file = fh_parse (FH_REF_FILE | FH_REF_SCRATCH); if (out_file == NULL) goto error; } @@ -278,8 +279,7 @@ cmd_aggregate (void) } else { - agr.writer = sfm_open_writer (out_file, agr.dict, - sfm_writer_default_options ()); + agr.writer = any_writer_open (out_file, agr.dict); if (agr.writer == NULL) goto error; @@ -297,7 +297,7 @@ cmd_aggregate (void) while (casereader_read_xfer (reader, &c)) { if (aggregate_single_case (&agr, &c, &agr.agr_case)) - sfm_write_case (agr.writer, &agr.agr_case); + any_writer_write (agr.writer, &agr.agr_case); case_destroy (&c); } casereader_destroy (reader); @@ -312,7 +312,7 @@ cmd_aggregate (void) if (agr.case_cnt > 0) { dump_aggregate_info (&agr, &agr.agr_case); - sfm_write_case (agr.writer, &agr.agr_case); + any_writer_write (agr.writer, &agr.agr_case); } } @@ -652,7 +652,7 @@ agr_destroy (struct agr_proc *agr) { struct agr_var *iter, *next; - sfm_close_writer (agr->writer); + any_writer_close (agr->writer); if (agr->sort != NULL) sort_destroy_criteria (agr->sort); free (agr->break_vars); @@ -1075,7 +1075,7 @@ presorted_agr_to_sysfile (struct ccase *c, void *agr_) struct agr_proc *agr = agr_; if (aggregate_single_case (agr, c, &agr->agr_case)) - sfm_write_case (agr->writer, &agr->agr_case); + any_writer_write (agr->writer, &agr->agr_case); return 1; } diff --git a/src/any-reader.c b/src/any-reader.c new file mode 100644 index 00000000..0f6f610d --- /dev/null +++ b/src/any-reader.c @@ -0,0 +1,188 @@ +/* PSPP - computes sample statistics. + Copyright (C) 2006 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. */ + +#include +#include "any-reader.h" +#include +#include +#include +#include +#include +#include "error.h" +#include "file-handle-def.h" +#include "filename.h" +#include "pfm-read.h" +#include "sfm-read.h" +#include "str.h" +#include "scratch-reader.h" +#include "xalloc.h" + +#include "gettext.h" +#define _(msgid) gettext (msgid) + +/* Type of file backing an any_reader. */ +enum any_reader_type + { + SYSTEM_FILE, /* System file. */ + PORTABLE_FILE, /* Portable file. */ + SCRATCH_FILE /* Scratch file. */ + }; + +/* Reader for any type of case-structured file. */ +struct any_reader + { + enum any_reader_type type; /* Type of file. */ + void *private; /* Private data. */ + }; + +/* Result of type detection. */ +enum detect_result + { + YES, /* It is this type. */ + NO, /* It is not this type. */ + IO_ERROR /* File couldn't be opened. */ + }; + +/* 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 *)) +{ + FILE *file; + bool is_type; + + file = fn_open (fh_get_filename (handle), "rb"); + if (file == NULL) + { + msg (ME, _("An error occurred while opening \"%s\": %s."), + fh_get_filename (handle), strerror (errno)); + return IO_ERROR; + } + + is_type = detect (file); + + fn_close (fh_get_filename (handle), file); + + return is_type ? YES : NO; +} + +/* If PRIVATE is non-null, creates and returns a new any_reader, + initializing its fields to TYPE and PRIVATE. If PRIVATE is a + null pointer, just returns a null pointer. */ +static struct any_reader * +make_any_reader (enum any_reader_type type, void *private) +{ + if (private != NULL) + { + struct any_reader *reader = xmalloc (sizeof *reader); + reader->type = type; + reader->private = private; + return reader; + } + else + return NULL; +} + +/* Creates an any_reader for HANDLE. On success, returns the new + any_reader and stores the file's dictionary into *DICT. On + failure, returns a null pointer. */ +struct any_reader * +any_reader_open (struct file_handle *handle, struct dictionary **dict) +{ + 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 make_any_reader (SYSTEM_FILE, + sfm_open_reader (handle, dict, NULL)); + + result = try_detect (handle, pfm_detect); + if (result == IO_ERROR) + return NULL; + else if (result == YES) + return make_any_reader (PORTABLE_FILE, + pfm_open_reader (handle, dict, NULL)); + + msg (SE, _("\"%s\" is not a system or portable file."), + fh_get_filename (handle)); + return NULL; + } + + case FH_REF_INLINE: + msg (SE, _("The inline file is not allowed here.")); + return NULL; + + case FH_REF_SCRATCH: + return make_any_reader (SCRATCH_FILE, + scratch_reader_open (handle, dict)); + } + abort (); +} + +/* Reads a single case from READER into C. + Returns true if successful, false at end of file or on error. */ +bool +any_reader_read (struct any_reader *reader, struct ccase *c) +{ + switch (reader->type) + { + case SYSTEM_FILE: + return sfm_read_case (reader->private, c); + + case PORTABLE_FILE: + return pfm_read_case (reader->private, c); + + case SCRATCH_FILE: + return scratch_reader_read_case (reader->private, c); + } + abort (); +} + +/* Closes READER. */ +void +any_reader_close (struct any_reader *reader) +{ + if (reader == NULL) + return; + + switch (reader->type) + { + case SYSTEM_FILE: + sfm_close_reader (reader->private); + break; + + case PORTABLE_FILE: + pfm_close_reader (reader->private); + break; + + case SCRATCH_FILE: + scratch_reader_close (reader->private); + break; + + default: + abort (); + } +} diff --git a/src/any-reader.h b/src/any-reader.h new file mode 100644 index 00000000..d4f296ea --- /dev/null +++ b/src/any-reader.h @@ -0,0 +1,33 @@ +/* PSPP - computes sample statistics. + Copyright (C) 2006 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 ANY_READER_H +#define ANY_READER_H 1 + +#include + +struct file_handle; +struct dictionary; +struct ccase; +struct any_reader *any_reader_open (struct file_handle *, + struct dictionary **); +bool any_reader_read (struct any_reader *, struct ccase *); +void any_reader_close (struct any_reader *); + +#endif /* any-reader.h */ diff --git a/src/any-writer.c b/src/any-writer.c new file mode 100644 index 00000000..048a7205 --- /dev/null +++ b/src/any-writer.c @@ -0,0 +1,193 @@ +/* PSPP - computes sample statistics. + Copyright (C) 2006 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. */ + +#include +#include "any-writer.h" +#include +#include +#include +#include +#include +#include "error.h" +#include "file-handle-def.h" +#include "filename.h" +#include "pfm-write.h" +#include "sfm-write.h" +#include "str.h" +#include "scratch-writer.h" +#include "xalloc.h" + +#include "gettext.h" +#define _(msgid) gettext (msgid) + +/* Type of file backing an any_writer. */ +enum any_writer_type + { + SYSTEM_FILE, /* System file. */ + PORTABLE_FILE, /* Portable file. */ + SCRATCH_FILE /* Scratch file. */ + }; + +/* Writer for any type of case-structured file. */ +struct any_writer + { + enum any_writer_type type; /* Type of file. */ + void *private; /* Private data. */ + }; + +/* Creates and returns a writer for HANDLE with the given DICT. */ +struct any_writer * +any_writer_open (struct file_handle *handle, struct dictionary *dict) +{ + switch (fh_get_referent (handle)) + { + case FH_REF_FILE: + { + struct any_writer *writer; + char *extension; + + extension = fn_extension (fh_get_filename (handle)); + str_lowercase (extension); + + if (!strcmp (extension, ".por")) + writer = any_writer_from_pfm_writer ( + pfm_open_writer (handle, dict, pfm_writer_default_options ())); + else + writer = any_writer_from_sfm_writer ( + sfm_open_writer (handle, dict, sfm_writer_default_options ())); + free (extension); + + return writer; + } + + case FH_REF_INLINE: + msg (ME, _("The inline file is not allowed here.")); + return NULL; + + case FH_REF_SCRATCH: + return any_writer_from_scratch_writer (scratch_writer_open (handle, + dict)); + } + + abort (); +} + +/* If PRIVATE is non-null, creates and returns a new any_writer, + initializing its fields to TYPE and PRIVATE. If PRIVATE is a + null pointer, just returns a null pointer. */ +static struct any_writer * +make_any_writer (enum any_writer_type type, void *private) +{ + if (private != NULL) + { + struct any_writer *writer = xmalloc (sizeof *writer); + writer->type = type; + writer->private = private; + return writer; + } + else + return NULL; +} + +/* If SFM_WRITER is non-null, encapsulates SFM_WRITER in an + any_writer and returns it. If SFM_WRITER is null, just + returns a null pointer. + + Useful when you need to pass options to sfm_open_writer(). + Typical usage: + any_writer_from_sfm_writer (sfm_open_writer (fh, dict, opts)) + If you don't need to pass options, then any_writer_open() by + itself is easier and more straightforward. */ +struct any_writer * +any_writer_from_sfm_writer (struct sfm_writer *sfm_writer) +{ + return make_any_writer (SYSTEM_FILE, sfm_writer); +} + +/* If PFM_WRITER is non-null, encapsulates PFM_WRITER in an + any_writer and returns it. If PFM_WRITER is null, just + returns a null pointer. + + Useful when you need to pass options to pfm_open_writer(). + Typical usage: + any_writer_from_pfm_writer (pfm_open_writer (fh, dict, opts)) + If you don't need to pass options, then any_writer_open() by + itself is easier and more straightforward. */ +struct any_writer * +any_writer_from_pfm_writer (struct pfm_writer *pfm_writer) +{ + return make_any_writer (PORTABLE_FILE, pfm_writer); +} + +/* If SCRATCH_WRITER is non-null, encapsulates SCRATCH_WRITER in + an any_writer and returns it. If SCRATCH_WRITER is null, just + returns a null pointer. + + Not particularly useful. Included just for consistency. */ +struct any_writer * +any_writer_from_scratch_writer (struct scratch_writer *scratch_writer) +{ + return make_any_writer (SCRATCH_FILE, scratch_writer); +} + +/* Writes cases C to WRITER. + Returns true if successful, false on failure. */ +bool +any_writer_write (struct any_writer *writer, const struct ccase *c) +{ + switch (writer->type) + { + case SYSTEM_FILE: + return sfm_write_case (writer->private, c); + + case PORTABLE_FILE: + return pfm_write_case (writer->private, c); + + case SCRATCH_FILE: + scratch_writer_write_case (writer->private, c); + return true; + } + abort (); +} + +/* Closes WRITER. */ +void +any_writer_close (struct any_writer *writer) +{ + if (writer == NULL) + return; + + switch (writer->type) + { + case SYSTEM_FILE: + sfm_close_writer (writer->private); + break; + + case PORTABLE_FILE: + pfm_close_writer (writer->private); + break; + + case SCRATCH_FILE: + scratch_writer_close (writer->private); + break; + + default: + abort (); + } +} diff --git a/src/any-writer.h b/src/any-writer.h new file mode 100644 index 00000000..9603b5ed --- /dev/null +++ b/src/any-writer.h @@ -0,0 +1,40 @@ +/* PSPP - computes sample statistics. + Copyright (C) 2006 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 ANY_WRITER_H +#define ANY_WRITER_H 1 + +#include + +struct file_handle; +struct dictionary; +struct ccase; +struct sfm_writer; +struct pfm_writer; +struct scratch_writer; + +struct any_writer *any_writer_open (struct file_handle *, struct dictionary *); +struct any_writer *any_writer_from_sfm_writer (struct sfm_writer *); +struct any_writer *any_writer_from_pfm_writer (struct pfm_writer *); +struct any_writer *any_writer_from_scratch_writer (struct scratch_writer *); + +bool any_writer_write (struct any_writer *, const struct ccase *); +void any_writer_close (struct any_writer *); + +#endif /* any-writer.h */ diff --git a/src/apply-dict.c b/src/apply-dict.c index 473daf01..ccfecb9e 100644 --- a/src/apply-dict.c +++ b/src/apply-dict.c @@ -19,13 +19,13 @@ #include #include +#include "any-reader.h" #include "command.h" #include "dictionary.h" #include "error.h" #include "file-handle.h" #include "hash.h" #include "lexer.h" -#include "sfm-read.h" #include "str.h" #include "value-labels.h" #include "var.h" @@ -40,7 +40,7 @@ int cmd_apply_dictionary (void) { struct file_handle *handle; - struct sfm_reader *reader; + struct any_reader *reader; struct dictionary *dict; int n_matched = 0; @@ -49,14 +49,14 @@ cmd_apply_dictionary (void) lex_match_id ("FROM"); lex_match ('='); - handle = fh_parse (); + handle = fh_parse (FH_REF_FILE | FH_REF_SCRATCH); if (!handle) return CMD_FAILURE; - reader = sfm_open_reader (handle, &dict, NULL); + reader = any_reader_open (handle, &dict); if (dict == NULL) return CMD_FAILURE; - sfm_close_reader (reader); + any_reader_close (reader); for (i = 0; i < dict_get_var_cnt (dict); i++) { @@ -163,7 +163,7 @@ cmd_apply_dictionary (void) dict_set_weight (default_dict, new_weight); } - sfm_close_reader (reader); + any_reader_close (reader); return lex_end_of_command (); } diff --git a/src/cat.h b/src/cat.h index 47c4ba54..69125035 100644 --- a/src/cat.h +++ b/src/cat.h @@ -53,4 +53,5 @@ struct cat_vals values stored. */ }; + #endif diff --git a/src/command.def b/src/command.def index a7f3cca8..737b7eed 100644 --- a/src/command.def +++ b/src/command.def @@ -38,6 +38,7 @@ UNIMPL ("CASEPLOT", ERRO, ERRO, ERRO, ERRO, "Plot time series") UNIMPL ("CASESTOVARS", ERRO, ERRO, ERRO, ERRO, "Restructure complex data") UNIMPL ("CCF", ERRO, ERRO, ERRO, ERRO, "Time series cross correlation") DEFCMD ("CLEAR TRANSFORMATIONS", ERRO, INPU, TRAN, TRAN, cmd_clear_transformations) +DEFCMD ("CLOSE FILE HANDLE", INIT, INPU, TRAN, PROC, cmd_close_file_handle) UNIMPL ("CLUSTER", ERRO, ERRO, ERRO, ERRO, "Hierachial clustering") DEFCMD ("COMPUTE", ERRO, INPU, TRAN, TRAN, cmd_compute) UNIMPL ("CONJOINT", ERRO, ERRO, ERRO, ERRO, "Analyse full concept data") diff --git a/src/correlations.q b/src/correlations.q index f190307d..f5348748 100644 --- a/src/correlations.q +++ b/src/correlations.q @@ -137,7 +137,7 @@ cor_custom_matrix (struct cmd_correlations *cmd UNUSED) matrix_file = NULL; else { - matrix_file = fh_parse (); + matrix_file = fh_parse (FH_REF_FILE); if (matrix_file == NULL) return 0; } diff --git a/src/data-list.c b/src/data-list.c index 7ac36a1a..49fbf0d7 100644 --- a/src/data-list.c +++ b/src/data-list.c @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -95,6 +95,8 @@ struct data_list_pgm size_t delim_cnt; /* Number of delimiter, or 0 for spaces. */ }; +static const struct case_source_class data_list_source_class; + static int parse_fixed (struct data_list_pgm *); static int parse_free (struct dls_var_spec **, struct dls_var_spec **); static void dump_fixed_table (const struct dls_var_spec *, @@ -111,9 +113,9 @@ static trns_proc_func data_list_trns_proc; int cmd_data_list (void) { - struct data_list_pgm *dls; /* DATA LIST program under construction. */ + struct data_list_pgm *dls; int table = -1; /* Print table if nonzero, -1=undecided. */ - struct file_handle *fh = NULL; /* File handle of source, NULL=inline file. */ + struct file_handle *fh = fh_inline_file (); if (!case_source_is_complex (vfm_source)) discard_variables (); @@ -133,14 +135,14 @@ cmd_data_list (void) if (lex_match_id ("FILE")) { lex_match ('='); - fh = fh_parse (); + fh = fh_parse (FH_REF_FILE | FH_REF_INLINE); if (fh == NULL) goto error; if (case_source_is_class (vfm_source, &file_type_source_class) - && fh != default_handle) + && fh != fh_get_default_handle ()) { - msg (SE, _("DATA LIST may not use a different file from " - "that specified on its surrounding FILE TYPE.")); + msg (SE, _("DATA LIST must use the same file " + "as the enclosing FILE TYPE.")); goto error; } } @@ -235,7 +237,7 @@ cmd_data_list (void) } dls->case_size = dict_get_case_size (default_dict); - default_handle = fh; + fh_set_default_handle (fh); if (dls->type == -1) dls->type = DLS_FIXED; @@ -795,15 +797,9 @@ dump_fixed_table (const struct dls_var_spec *specs, fmt_to_string (&spec->input)); } - if (fh != NULL) - tab_title (t, 1, ngettext ("Reading %d record from file %s.", - "Reading %d records from file %s.", rec_cnt), - rec_cnt, fh_get_filename (fh)); - else - tab_title (t, 1, ngettext ("Reading %d record from the command file.", - "Reading %d records from the command file.", - rec_cnt), - rec_cnt); + tab_title (t, 1, ngettext ("Reading %d record from %s.", + "Reading %d records from %s.", rec_cnt), + rec_cnt, fh_get_name (fh)); tab_submit (t); } @@ -917,11 +913,7 @@ dump_free_table (const struct data_list_pgm *dls, } } - if (fh != NULL) - tab_title (t, 1, _("Reading free-form data from file %s."), - fh_get_filename (fh)); - else - tab_title (t, 1, _("Reading free-form data from the command file.")); + tab_title (t, 1, _("Reading free-form data from %s."), fh_get_name (fh)); tab_submit (t); } @@ -1309,7 +1301,7 @@ data_list_source_destroy (struct case_source *source) data_list_trns_free (source->aux); } -const struct case_source_class data_list_source_class = +static const struct case_source_class data_list_source_class = { "DATA LIST", NULL, @@ -1367,12 +1359,12 @@ cmd_repeating_data (void) bool saw_length = false; /* Saw LENGTH subcommand? */ bool saw_continued = false; /* Saw CONTINUED subcommand? */ bool saw_id = false; /* Saw ID subcommand? */ - struct file_handle *const fh = default_handle; + struct file_handle *const fh = fh_get_default_handle (); assert (case_source_is_complex (vfm_source)); rpd = xmalloc (sizeof *rpd); - rpd->reader = dfm_open_reader (default_handle); + rpd->reader = dfm_open_reader (fh); rpd->first = rpd->last = NULL; rpd->starts_beg.num = 0; rpd->starts_beg.var = NULL; @@ -1390,7 +1382,7 @@ cmd_repeating_data (void) { struct file_handle *file; lex_match ('='); - file = fh_parse (); + file = fh_parse (FH_REF_FILE | FH_REF_INLINE); if (file == NULL) goto error; if (file != fh) @@ -1585,7 +1577,7 @@ cmd_repeating_data (void) /* Calculate and check starts_end, cont_end if necessary. */ if (rpd->starts_end.num == 0 && rpd->starts_end.var == NULL) { - rpd->starts_end.num = fh != NULL ? fh_get_record_width (fh) : 80; + rpd->starts_end.num = fh_get_record_width (fh); if (rpd->starts_beg.num != 0 && rpd->starts_beg.num > rpd->starts_end.num) { @@ -1598,7 +1590,7 @@ cmd_repeating_data (void) } if (rpd->cont_end.num == 0 && rpd->cont_end.var == NULL) { - rpd->cont_end.num = fh != NULL ? fh_get_record_width (fh) : 80; + rpd->cont_end.num = fh_get_record_width (fh); if (rpd->cont_beg.num != 0 && rpd->cont_beg.num > rpd->cont_end.num) { diff --git a/src/dfm-read.c b/src/dfm-read.c index a739b5c3..6b3b3058 100644 --- a/src/dfm-read.c +++ b/src/dfm-read.c @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-2004 Free Software Foundation, Inc. + Copyright (C) 1997-2004, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -53,17 +53,14 @@ enum dfm_reader_flags struct dfm_reader { struct file_handle *fh; /* File handle. */ - struct file_ext file; /* Associated file. */ struct file_locator where; /* Current location in data file. */ struct string line; /* Current line. */ - size_t pos; /* Offset in line of current character. */ struct string scratch; /* Extra line buffer. */ enum dfm_reader_flags flags; /* Zero or more of DFM_*. */ + struct file_ext file; /* Associated file. */ + size_t pos; /* Offset in line of current character. */ }; -static int inline_open_cnt; -static struct dfm_reader *inline_file; - static void read_record (struct dfm_reader *r); /* Closes reader R opened by dfm_open_reader(). */ @@ -71,81 +68,62 @@ void dfm_close_reader (struct dfm_reader *r) { int still_open; + bool is_inline; if (r == NULL) return; - if (r->fh != NULL) - still_open = fh_close (r->fh, "data file", "rs"); - else - { - assert (inline_open_cnt > 0); - still_open = --inline_open_cnt; - - if (!still_open) - { - /* Skip any remaining data on the inline file. */ - if (r->flags & DFM_SAW_BEGIN_DATA) - while ((r->flags & DFM_EOF) == 0) - read_record (r); - inline_file = NULL; - } - } + is_inline = r->fh == fh_inline_file (); + still_open = fh_close (r->fh, "data file", "rs"); if (still_open) return; - if (r->fh != NULL && r->file.file) + if (!is_inline) { fn_close_ext (&r->file); free (r->file.filename); r->file.filename = NULL; } + else + { + /* Skip any remaining data on the inline file. */ + if (r->flags & DFM_SAW_BEGIN_DATA) + while ((r->flags & DFM_EOF) == 0) + read_record (r); + } + ds_destroy (&r->line); ds_destroy (&r->scratch); free (r); } /* Opens the file designated by file handle FH for reading as a - data file. Providing a null pointer for FH designates the + data file. Providing fh_inline_file() for FH designates the "inline file", that is, data included inline in the command - file between BEGIN FILE and END FILE. Returns nonzero only if - successful. */ + file between BEGIN FILE and END FILE. Returns a reader if + successful, or a null pointer otherwise. */ struct dfm_reader * dfm_open_reader (struct file_handle *fh) { struct dfm_reader *r; void **rp; - if (fh != NULL) - { - rp = fh_open (fh, "data file", "rs"); - if (rp == NULL) - return NULL; - if (*rp != NULL) - return *rp; - } - else - { - assert (inline_open_cnt >= 0); - if (inline_open_cnt++ > 0) - return inline_file; - rp = NULL; - } + rp = fh_open (fh, FH_REF_FILE | FH_REF_INLINE, "data file", "rs"); + if (rp == NULL) + return NULL; + if (*rp != NULL) + return *rp; r = xmalloc (sizeof *r); r->fh = fh; - if (fh != NULL) - { - r->where.filename = fh_get_filename (fh); - r->where.line_number = 0; - } - r->file.file = NULL; ds_init (&r->line, 64); ds_init (&r->scratch, 0); r->flags = DFM_ADVANCE; - - if (fh != NULL) + if (fh != fh_inline_file ()) { + r->where.filename = fh_get_filename (fh); + r->where.line_number = 0; + r->file.file = NULL; r->file.filename = xstrdup (fh_get_filename (r->fh)); r->file.mode = "rb"; r->file.file = NULL; @@ -154,24 +132,23 @@ dfm_open_reader (struct file_handle *fh) r->file.postopen = NULL; r->file.preclose = NULL; if (!fn_open_ext (&r->file)) - { - msg (ME, _("Could not open \"%s\" for reading " - "as a data file: %s."), + { + msg (ME, _("Could not open \"%s\" for reading as a data file: %s."), fh_get_filename (r->fh), strerror (errno)); err_cond_fail (); fh_close (fh,"data file", "rs"); free (r); return NULL; - } - *rp = r; + } } - else - inline_file = r; + *rp = r; return r; } -static int +/* Reads a record from the inline file into R. + Returns true if successful, false on failure. */ +static bool read_inline_record (struct dfm_reader *r) { if ((r->flags & DFM_SAW_BEGIN_DATA) == 0) @@ -208,7 +185,7 @@ read_inline_record (struct dfm_reader *r) { msg (SE, _("BEGIN DATA expected.")); lex_preprocess_line (); - return 0; + return false; } getl_prompt = GETL_PRPT_DATA; } @@ -223,25 +200,24 @@ read_inline_record (struct dfm_reader *r) err_failure (); } - if (r->fh != NULL) - r->where.line_number++; - if (ds_length (&getl_buf) >= 8 && !strncasecmp (ds_c_str (&getl_buf), "end data", 8)) { lex_set_prog (ds_c_str (&getl_buf) + ds_length (&getl_buf)); - return 0; + return false; } ds_replace (&r->line, ds_c_str (&getl_buf)); - return 1; + return true; } -static int +/* Reads a record from a disk file into R. + Returns true if successful, false on failure. */ +static bool read_file_record (struct dfm_reader *r) { - assert (r->fh != NULL); - if (fh_get_mode (r->fh) == MODE_TEXT) + assert (r->fh != fh_inline_file ()); + if (fh_get_mode (r->fh) == FH_MODE_TEXT) { ds_clear (&r->line); if (!ds_gets (&r->line, r->file.file)) @@ -252,10 +228,10 @@ read_file_record (struct dfm_reader *r) fh_get_name (r->fh), strerror (errno)); err_cond_fail (); } - return 0; + return false; } } - else if (fh_get_mode (r->fh) == MODE_BINARY) + else if (fh_get_mode (r->fh) == FH_MODE_BINARY) { size_t record_width = fh_get_record_width (r->fh); size_t amt; @@ -274,10 +250,10 @@ read_file_record (struct dfm_reader *r) msg (ME, _("%s: Partial record at end of file."), fh_get_name (r->fh)); else - return 0; + return false; err_cond_fail (); - return 0; + return false; } } else @@ -285,7 +261,7 @@ read_file_record (struct dfm_reader *r) r->where.line_number++; - return 1; + return true; } /* Reads a record from R, setting the current position to the @@ -294,7 +270,13 @@ read_file_record (struct dfm_reader *r) static void read_record (struct dfm_reader *r) { - int success = r->fh != NULL ? read_file_record (r) : read_inline_record (r); + bool success; + + if (fh_get_referent (r->fh) == FH_REF_FILE) + success = read_file_record (r); + else + success = read_inline_record (r); + if (success) r->pos = 0; else @@ -313,7 +295,7 @@ dfm_eof (struct dfm_reader *r) read_record (r); else { - if (r->fh != NULL) + if (r->fh != fh_inline_file ()) msg (SE, _("Attempt to read beyond end-of-file on file %s."), fh_get_name (r->fh)); else @@ -359,15 +341,15 @@ dfm_expand_tabs (struct dfm_reader *r) return; r->flags |= DFM_TABS_EXPANDED; - if (r->fh != NULL - && (fh_get_mode (r->fh) == MODE_BINARY + if (r->fh != fh_inline_file () + && (fh_get_mode (r->fh) == FH_MODE_BINARY || fh_get_tab_width (r->fh) == 0 || memchr (ds_c_str (&r->line), '\t', ds_length (&r->line)) == NULL)) return; /* Expand tabs from r->line into r->scratch, and figure out new value for r->pos. */ - tab_width = r->fh != NULL ? fh_get_tab_width (r->fh) : 8; + tab_width = fh_get_tab_width (r->fh); ds_clear (&r->scratch); new_pos = 0; for (ofs = 0; ofs < ds_length (&r->line); ofs++) @@ -439,7 +421,7 @@ dfm_column_start (struct dfm_reader *r) void dfm_push (struct dfm_reader *r) { - if (r->fh != NULL) + if (r->fh != fh_inline_file ()) err_push_file_locator (&r->where); } @@ -447,7 +429,7 @@ dfm_push (struct dfm_reader *r) void dfm_pop (struct dfm_reader *r) { - if (r->fh != NULL) + if (r->fh != fh_inline_file ()) err_pop_file_locator (&r->where); } @@ -459,10 +441,7 @@ cmd_begin_data (void) { struct dfm_reader *r; - /* FIXME: figure out the *exact* conditions, not these really - lenient conditions. */ - if (vfm_source == NULL - || case_source_is_class (vfm_source, &storage_source_class)) + if (!fh_is_open (fh_inline_file ())) { msg (SE, _("This command is not valid here since the current " "input program does not access the inline file.")); @@ -471,7 +450,7 @@ cmd_begin_data (void) } /* Open inline file. */ - r = dfm_open_reader (NULL); + r = dfm_open_reader (fh_inline_file ()); r->flags |= DFM_SAW_BEGIN_DATA; /* Input procedure reads from inline file. */ diff --git a/src/dfm-write.c b/src/dfm-write.c index 465e04eb..39aa7e38 100644 --- a/src/dfm-write.c +++ b/src/dfm-write.c @@ -46,7 +46,7 @@ dfm_open_writer (struct file_handle *fh) struct dfm_writer *w; void **aux; - aux = fh_open (fh, "data file", "ws"); + aux = fh_open (fh, FH_REF_FILE, "data file", "ws"); if (aux == NULL) return NULL; if (*aux != NULL) @@ -89,7 +89,7 @@ dfm_put_record (struct dfm_writer *w, const char *rec, size_t len) { assert (w != NULL); - if (fh_get_mode (w->fh) == MODE_BINARY + if (fh_get_mode (w->fh) == FH_MODE_BINARY && len < fh_get_record_width (w->fh)) { size_t rec_width = fh_get_record_width (w->fh); diff --git a/src/dictionary.c b/src/dictionary.c index f916fd0c..c11f6f41 100644 --- a/src/dictionary.c +++ b/src/dictionary.c @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -25,6 +25,7 @@ #include "alloc.h" #include "case.h" #include "cat.h" +#include "cat-routines.h" #include "error.h" #include "hash.h" #include "misc.h" @@ -406,8 +407,9 @@ dict_lookup_var_assert (const struct dictionary *d, const char *name) return v; } -/* Returns nonzero if variable V is in dictionary D. */ -int +/* Returns true if variable V is in dictionary D, + false otherwise. */ +bool dict_contains_var (const struct dictionary *d, const struct variable *v) { assert (d != NULL); @@ -593,18 +595,18 @@ dict_rename_var (struct dictionary *d, struct variable *v, /* Renames COUNT variables specified in VARS to the names given in NEW_NAMES within dictionary D. If the renaming would - result in a duplicate variable name, returns zero and stores a + result in a duplicate variable name, returns false and stores a name that would be duplicated into *ERR_NAME (if ERR_NAME is - non-null). Otherwise, the renaming is successful, and nonzero + non-null). Otherwise, the renaming is successful, and true is returned. */ -int +bool dict_rename_vars (struct dictionary *d, struct variable **vars, char **new_names, size_t count, char **err_name) { char **old_names; size_t i; - int success = 1; + bool success = true; assert (d != NULL); assert (count == 0 || vars != NULL); @@ -649,7 +651,7 @@ dict_rename_vars (struct dictionary *d, hsh_force_insert (d->name_tab, vars[i]); } - success = 0; + success = false; goto done; } } @@ -807,30 +809,6 @@ dict_compact_values (struct dictionary *d) } } -/* Copies values from SRC, which represents a case arranged - according to dictionary D, to DST, which represents a case - arranged according to the dictionary that will be produced by - dict_compact_values(D). */ -void -dict_compact_case (const struct dictionary *d, - struct ccase *dst, const struct ccase *src) -{ - size_t i; - size_t value_idx; - - value_idx = 0; - for (i = 0; i < d->var_cnt; i++) - { - struct variable *v = d->var[i]; - - if (dict_class_from_id (v->name) != DC_SCRATCH) - { - case_copy (dst, value_idx, src, v->fv, v->nv); - value_idx += v->nv; - } - } -} - /* Returns the number of values that would be used by a case if dict_compact_values() were called. */ size_t @@ -874,6 +852,112 @@ dict_get_compacted_idx_to_fv (const struct dictionary *d) return idx_to_fv; } +/* Returns true if a case for dictionary D would be smaller after + compaction, false otherwise. Compacting a case eliminates + "holes" between values and after the last value. Holes are + created by deleting variables (or by scratch variables). + + The return value may differ from whether compacting a case + from dictionary D would *change* the case: compaction could + rearrange values even if it didn't reduce space + requirements. */ +bool +dict_needs_compaction (const struct dictionary *d) +{ + return dict_get_compacted_value_cnt (d) < dict_get_next_value_idx (d); +} + +/* How to copy a contiguous range of values between cases. */ +struct copy_map + { + size_t src_idx; /* Starting value index in source case. */ + size_t dst_idx; /* Starting value index in target case. */ + size_t cnt; /* Number of values. */ + }; + +/* How to compact a case. */ +struct dict_compactor + { + struct copy_map *maps; /* Array of mappings. */ + size_t map_cnt; /* Number of mappings. */ + }; + +/* Creates and returns a dict_compactor that can be used to + compact cases for dictionary D. + + Compacting a case eliminates "holes" between values and after + the last value. Holes are created by deleting variables (or + by scratch variables). */ +struct dict_compactor * +dict_make_compactor (const struct dictionary *d) +{ + struct dict_compactor *compactor; + struct copy_map *map; + size_t map_allocated; + size_t value_idx; + size_t i; + + compactor = xmalloc (sizeof *compactor); + compactor->maps = NULL; + compactor->map_cnt = 0; + map_allocated = 0; + + value_idx = 0; + map = NULL; + for (i = 0; i < d->var_cnt; i++) + { + struct variable *v = d->var[i]; + + if (dict_class_from_id (v->name) == DC_SCRATCH) + continue; + if (map != NULL && map->src_idx + map->cnt == v->fv) + map->cnt += v->nv; + else + { + if (compactor->map_cnt == map_allocated) + compactor->maps = x2nrealloc (compactor->maps, &map_allocated, + sizeof *compactor->maps); + map = &compactor->maps[compactor->map_cnt++]; + map->src_idx = v->fv; + map->dst_idx = value_idx; + map->cnt = v->nv; + } + value_idx += v->nv; + } + + return compactor; +} + +/* Compacts SRC by copying it to DST according to the scheme in + COMPACTOR. + + Compacting a case eliminates "holes" between values and after + the last value. Holes are created by deleting variables (or + by scratch variables). */ +void +dict_compactor_compact (const struct dict_compactor *compactor, + struct ccase *dst, const struct ccase *src) +{ + size_t i; + + for (i = 0; i < compactor->map_cnt; i++) + { + const struct copy_map *map = &compactor->maps[i]; + case_copy (dst, map->dst_idx, src, map->src_idx, map->cnt); + } +} + +/* Destroys COMPACTOR. */ +void +dict_compactor_destroy (struct dict_compactor *compactor) +{ + if (compactor != NULL) + { + free (compactor->maps); + free (compactor); + } +} + /* Returns the SPLIT FILE vars (see cmd_split_file()). Call dict_get_split_cnt() to determine how many SPLIT FILE vars there are. Returns a null pointer if and only if there are no @@ -963,9 +1047,9 @@ dict_set_documents (struct dictionary *d, const char *documents) } /* Creates in D a vector named NAME that contains CNT variables - VAR (see cmd_vector()). Returns nonzero if successful, or - zero if a vector named NAME already exists in D. */ -int + VAR (see cmd_vector()). Returns true if successful, or + false if a vector named NAME already exists in D. */ +bool dict_create_vector (struct dictionary *d, const char *name, struct variable **var, size_t cnt) @@ -980,7 +1064,7 @@ dict_create_vector (struct dictionary *d, assert (cnt > 0); if (dict_lookup_vector (d, name) != NULL) - return 0; + return false; d->vector = xnrealloc (d->vector, d->vector_cnt + 1, sizeof *d->vector); vector = d->vector[d->vector_cnt] = xmalloc (sizeof *vector); @@ -994,7 +1078,7 @@ dict_create_vector (struct dictionary *d, } vector->cnt = cnt; - return 1; + return true; } /* Returns the vector in D with index IDX, which must be less diff --git a/src/dictionary.h b/src/dictionary.h index e923d52d..d0287714 100644 --- a/src/dictionary.h +++ b/src/dictionary.h @@ -20,6 +20,7 @@ #ifndef DICTIONARY_H #define DICTIONARY_H +#include #include /* Dictionary. */ @@ -50,7 +51,7 @@ struct variable *dict_clone_var_assert (struct dictionary *, struct variable *dict_lookup_var (const struct dictionary *, const char *); struct variable *dict_lookup_var_assert (const struct dictionary *, const char *); -int dict_contains_var (const struct dictionary *, const struct variable *); +bool dict_contains_var (const struct dictionary *, const struct variable *); void dict_delete_var (struct dictionary *, struct variable *); void dict_delete_vars (struct dictionary *, struct variable *const *, size_t count); @@ -60,9 +61,9 @@ void dict_reorder_var (struct dictionary *d, struct variable *v, void dict_reorder_vars (struct dictionary *, struct variable *const *, size_t count); void dict_rename_var (struct dictionary *, struct variable *, const char *); -int dict_rename_vars (struct dictionary *, - struct variable **, char **new_names, - size_t count, char **err_name); +bool dict_rename_vars (struct dictionary *, + struct variable **, char **new_names, + size_t count, char **err_name); struct ccase; struct variable *dict_get_weight (const struct dictionary *); @@ -80,10 +81,14 @@ int dict_get_next_value_idx (const struct dictionary *); size_t dict_get_case_size (const struct dictionary *); void dict_compact_values (struct dictionary *); -void dict_compact_case (const struct dictionary *, - struct ccase *, const struct ccase *); size_t dict_get_compacted_value_cnt (const struct dictionary *); int *dict_get_compacted_idx_to_fv (const struct dictionary *); +bool dict_needs_compaction (const struct dictionary *); + +struct dict_compactor *dict_make_compactor (const struct dictionary *); +void dict_compactor_compact (const struct dict_compactor *, + struct ccase *, const struct ccase *); +void dict_compactor_destroy (struct dict_compactor *); struct variable *const *dict_get_split_vars (const struct dictionary *); size_t dict_get_split_cnt (const struct dictionary *); @@ -96,9 +101,9 @@ void dict_set_label (struct dictionary *, const char *); const char *dict_get_documents (const struct dictionary *); void dict_set_documents (struct dictionary *, const char *); -int dict_create_vector (struct dictionary *, - const char *name, - struct variable **, size_t cnt); +bool dict_create_vector (struct dictionary *, + const char *name, + struct variable **, size_t cnt); const struct vector *dict_get_vector (const struct dictionary *, size_t idx); size_t dict_get_vector_cnt (const struct dictionary *); diff --git a/src/error.c b/src/error.c index 1213b1a3..3a77c872 100644 --- a/src/error.c +++ b/src/error.c @@ -31,6 +31,7 @@ #include "main.h" #include "output.h" #include "progname.h" +#include "readln.h" #include "settings.h" #include "str.h" #include "var.h" diff --git a/src/file-handle-def.c b/src/file-handle-def.c index 605d1dd9..3be48250 100644 --- a/src/file-handle-def.c +++ b/src/file-handle-def.c @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -18,23 +18,22 @@ 02110-1301, USA. */ #include -#include "file-handle.h" #include "file-handle-def.h" #include "error.h" #include #include #include #include "alloc.h" +#include "file-handle.h" #include "filename.h" #include "command.h" #include "getl.h" #include "error.h" #include "magic.h" #include "var.h" -#include "file-handle-def.h" +#include "scratch-handle.h" #include "gettext.h" - #define _(msgid) gettext (msgid) /* (headers) */ @@ -43,60 +42,78 @@ struct file_handle { struct file_handle *next; /* Next in global list. */ + int open_cnt; /* 0=not open, otherwise # of openers. */ + bool deleted; /* Destroy handle when open_cnt goes to 0? */ + char *name; /* File handle identifier. */ + const char *type; /* If open, type of file. */ + char open_mode[3]; /* "[rw][se]". */ + void *aux; /* Aux data pointer for owner if any. */ + enum fh_referent referent; /* What the file handle refers to. */ + + /* FH_REF_FILE only. */ char *filename; /* Filename as provided by user. */ struct file_identity *identity; /* For checking file identity. */ - struct file_locator where; /* Used for reporting error messages. */ enum fh_mode mode; /* File mode. */ + + /* FH_REF_FILE and FH_REF_INLINE only. */ size_t record_width; /* Length of fixed-format records. */ size_t tab_width; /* Tab width, 0=do not expand tabs. */ - int open_cnt; /* 0=not open, otherwise # of openers. */ - const char *type; /* If open, type of file. */ - char open_mode[3]; /* "[rw][se]". */ - void *aux; /* Aux data pointer for owner if any. */ + /* FH_REF_SCRATCH only. */ + struct scratch_handle *sh; /* Scratch file data. */ }; +/* List of all handles. */ static struct file_handle *file_handles; +/* Default file handle for DATA LIST, REREAD, REPEATING DATA + commands. */ +static struct file_handle *default_handle; + +/* The "file" that reads from BEGIN DATA...END DATA. */ +static struct file_handle *inline_file; + +static struct file_handle *create_handle (const char *name, enum fh_referent); + /* File handle initialization routine. */ void fh_init (void) { - /* Currently nothing to do. */ + inline_file = create_handle ("INLINE", FH_REF_INLINE); + inline_file->record_width = 80; + inline_file->tab_width = 8; } - -/* Destroy file handle. - Normally needed only if a file_handle needs to be re-assigned. - Otherwise, just let fh_done clean destroy the handle. - */ -void -fh_free(struct file_handle *fh) +/* Free HANDLE and remove it from the global list. */ +static void +free_handle (struct file_handle *handle) { - if ( !fh->name ) - return ; - - free (fh->name); - fh->name = 0; - free (fh->filename); - fn_free_identity (fh->identity); - free (fh); -} + /* Remove handle from global list. */ + if (file_handles == handle) + file_handles = handle->next; + else + { + struct file_handle *iter = file_handles; + while (iter->next != handle) + iter = iter->next; + iter->next = handle->next; + } + /* Free data. */ + free (handle->name); + free (handle->filename); + fn_free_identity (handle->identity); + scratch_handle_destroy (handle->sh); + free (handle); +} /* Frees all the file handles. */ void fh_done (void) { - struct file_handle *fh, *next; - - for (fh = file_handles; fh != NULL; fh = next) - { - next = fh->next; - fh_free(fh); - } - file_handles = NULL; + while (file_handles != NULL) + free_handle (file_handles); } /* Returns the handle named HANDLE_NAME, or a null pointer if @@ -107,7 +124,7 @@ fh_from_name (const char *handle_name) struct file_handle *iter; for (iter = file_handles; iter != NULL; iter = iter->next) - if (!strcasecmp (handle_name, iter->name)) + if (!iter->deleted && !strcasecmp (handle_name, iter->name)) return iter; return NULL; } @@ -127,7 +144,9 @@ fh_from_filename (const char *filename) if (identity != NULL) { for (iter = file_handles; iter != NULL; iter = iter->next) - if (iter->identity != NULL + if (!iter->deleted + && iter->referent == FH_REF_FILE + && iter->identity != NULL && !fn_compare_file_identities (identity, iter->identity)) { fn_free_identity (identity); @@ -138,40 +157,69 @@ fh_from_filename (const char *filename) /* Then check for a file with the same name. */ for (iter = file_handles; iter != NULL; iter = iter->next) - if (!strcmp (filename, iter->filename)) + if (!iter->deleted + && iter->referent == FH_REF_FILE && !strcmp (filename, iter->filename)) return iter; return NULL; } -/* Creates and returns a new file handle with the given values - and defaults for other values. Adds the created file handle - to the global list. */ -struct file_handle * -fh_create (const char *handle_name, const char *filename, - const struct fh_properties *properties) -{ - struct file_handle *handle; - - assert(filename); - assert(handle_name); +/* Creates a new handle with name HANDLE_NAME that refers to + REFERENT. Links the new handle into the global list. Returns + the new handle. - /* Create and initialize file handle. */ - handle = xmalloc (sizeof *handle); + The new handle is not fully initialized. The caller is + responsible for completing its initialization. */ +static struct file_handle * +create_handle (const char *handle_name, enum fh_referent referent) +{ + struct file_handle *handle = xzalloc (sizeof *handle); handle->next = file_handles; + handle->open_cnt = 0; + handle->deleted = false; handle->name = xstrdup (handle_name); + handle->type = NULL; + handle->aux = NULL; + handle->referent = referent; + file_handles = handle; + return handle; +} + +/* Returns the unique handle of referent type FH_REF_INLINE, + which refers to the "inline file" that represents character + data in the command file between BEGIN DATA and END DATA. */ +struct file_handle * +fh_inline_file (void) +{ + return inline_file; +} + +/* Creates a new file handle named HANDLE_NAME, which must not be + the name of an existing file handle. The new handle is + associated with file FILENAME and the given PROPERTIES. */ +struct file_handle * +fh_create_file (const char *handle_name, const char *filename, + const struct fh_properties *properties) +{ + struct file_handle *handle; + assert (fh_from_name (handle_name) == NULL); + handle = create_handle (handle_name, FH_REF_FILE); handle->filename = xstrdup (filename); handle->identity = fn_get_identity (filename); - handle->where.filename = handle->filename; - handle->where.line_number = 0; handle->mode = properties->mode; handle->record_width = properties->record_width; handle->tab_width = properties->tab_width; - handle->open_cnt = 0; - handle->type = NULL; - handle->aux = NULL; - file_handles = handle; + return handle; +} +/* Creates a new file handle named HANDLE_NAME, which must not be + the name of an existing file handle. The new handle is + associated with a scratch file (initially empty). */ +struct file_handle * +fh_create_scratch (const char *handle_name) +{ + struct file_handle *handle = create_handle (handle_name, FH_REF_SCRATCH); + handle->sh = NULL; return handle; } @@ -179,10 +227,31 @@ fh_create (const char *handle_name, const char *filename, const struct fh_properties * fh_default_properties (void) { - static const struct fh_properties default_properties = {MODE_TEXT, 1024, 4}; + static const struct fh_properties default_properties + = {FH_MODE_TEXT, 1024, 4}; return &default_properties; } +/* Deletes FH from the global list of file handles. Afterward, + attempts to search for it will fail. Unless the file handle + is currently open, it will be destroyed; otherwise, it will be + destroyed later when it is closed. + Normally needed only if a file_handle needs to be re-assigned. + Otherwise, just let fh_done() destroy the handle. */ +void +fh_free (struct file_handle *handle) +{ + if (handle == fh_inline_file () || handle == NULL || handle->deleted) + return; + handle->deleted = true; + + if (handle == default_handle) + default_handle = fh_inline_file (); + + if (handle->open_cnt == 0) + free_handle (handle); +} + /* Returns an English description of MODE, which is in the format of the MODE argument to fh_open(). */ static const char * @@ -196,6 +265,9 @@ mode_name (const char *mode) /* Tries to open handle H with the given TYPE and MODE. + H's referent type must be one of the bits in MASK. The caller + must verify this ahead of time; we simply assert it here. + TYPE is the sort of file, e.g. "system file". Only one given type of access is allowed on a given file handle at once. If successful, a reference to TYPE is retained, so it should @@ -213,9 +285,11 @@ mode_name (const char *mode) modes the void * will necessarily be null only if no other sharers are active. */ void ** -fh_open (struct file_handle *h, const char *type, const char *mode) +fh_open (struct file_handle *h, enum fh_referent mask UNUSED, + const char *type, const char *mode) { assert (h != NULL); + assert ((fh_get_referent (h) & mask) != 0); assert (type != NULL); assert (mode != NULL); assert (mode[0] == 'r' || mode[0] == 'w'); @@ -227,21 +301,21 @@ fh_open (struct file_handle *h, const char *type, const char *mode) if (strcmp (h->type, type)) { msg (SE, _("Can't open %s as a %s because it is " - "already open as a %s"), + "already open as a %s."), fh_get_name (h), type, h->type); return NULL; } else if (strcmp (h->open_mode, mode)) { msg (SE, _("Can't open %s as a %s for %s because it is " - "already open for %s"), + "already open for %s."), fh_get_name (h), type, mode_name (mode), mode_name (h->open_mode)); return NULL; } else if (h->open_mode[1] == 'e') { - msg (SE, _("Can't re-open %s as a %s for %s"), + msg (SE, _("Can't re-open %s as a %s for %s."), fh_get_name (h), type, mode_name (mode)); return NULL; } @@ -260,7 +334,11 @@ fh_open (struct file_handle *h, const char *type, const char *mode) /* Closes file handle H, which must have been open for the specified TYPE and MODE of access provided to fh_open(). Returns zero if the file is now closed, nonzero if it is still - open due to another reference. */ + open due to another reference. + + After fh_close() returns zero for a handle, it is unsafe to + reference that file handle again in any way, because its + storage may have been freed. */ int fh_close (struct file_handle *h, const char *type, const char *mode) { @@ -271,13 +349,23 @@ fh_close (struct file_handle *h, const char *type, const char *mode) assert (mode != NULL); assert (!strcmp (mode, h->open_mode)); - h->open_cnt--; - if (h->open_cnt == 0) + if (--h->open_cnt == 0) { h->type = NULL; h->aux = NULL; + if (h->deleted) + free_handle (h); + return 0; } - return h->open_cnt; + return 1; +} + +/* Is the file open? BEGIN DATA...END DATA uses this to detect + whether the inline file is actually in use. */ +bool +fh_is_open (const struct file_handle *handle) +{ + return handle->open_cnt > 0; } /* Returns the identifier of file HANDLE. If HANDLE was created @@ -289,15 +377,21 @@ fh_close (struct file_handle *h, const char *type, const char *mode) const char * fh_get_name (const struct file_handle *handle) { - assert (handle != NULL); return handle->name; } +/* Returns the type of object that HANDLE refers to. */ +enum fh_referent +fh_get_referent (const struct file_handle *handle) +{ + return handle->referent; +} + /* Returns the name of the file associated with HANDLE. */ const char * fh_get_filename (const struct file_handle *handle) { - assert (handle != NULL); + assert (handle->referent == FH_REF_FILE); return handle->filename; } @@ -305,7 +399,7 @@ fh_get_filename (const struct file_handle *handle) enum fh_mode fh_get_mode (const struct file_handle *handle) { - assert (handle != NULL); + assert (handle->referent == FH_REF_FILE); return handle->mode; } @@ -313,16 +407,50 @@ fh_get_mode (const struct file_handle *handle) size_t fh_get_record_width (const struct file_handle *handle) { - assert (handle != NULL); + assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE)); return handle->record_width; } /* Returns the number of characters per tab stop for HANDLE, or zero if tabs are not to be expanded. Applicable only to - MODE_TEXT files. */ + FH_MODE_TEXT files. */ size_t fh_get_tab_width (const struct file_handle *handle) { - assert (handle != NULL); + assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE)); return handle->tab_width; } + +/* Returns the scratch file handle associated with HANDLE. + Applicable to only FH_REF_SCRATCH files. */ +struct scratch_handle * +fh_get_scratch_handle (struct file_handle *handle) +{ + assert (handle->referent == FH_REF_SCRATCH); + return handle->sh; +} + +/* Sets SH to be the scratch file handle associated with HANDLE. + Applicable to only FH_REF_SCRATCH files. */ +void +fh_set_scratch_handle (struct file_handle *handle, struct scratch_handle *sh) +{ + assert (handle->referent == FH_REF_SCRATCH); + handle->sh = sh; +} + +/* Returns the current default handle. */ +struct file_handle * +fh_get_default_handle (void) +{ + return default_handle ? default_handle : fh_inline_file (); +} + +/* Sets NEW_DEFAULT_HANDLE as the default handle. */ +void +fh_set_default_handle (struct file_handle *new_default_handle) +{ + assert (new_default_handle == NULL + || (new_default_handle->referent & (FH_REF_INLINE | FH_REF_FILE))); + default_handle = new_default_handle; +} diff --git a/src/file-handle-def.h b/src/file-handle-def.h index b4ca8e27..c5c61ea8 100644 --- a/src/file-handle-def.h +++ b/src/file-handle-def.h @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000,2005 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2005, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -20,13 +20,24 @@ #ifndef FILE_HANDLE_DEF_H #define FILE_HANDLE_DEF_H -#include +#include +#include + +/* What a file handle refers to. + (Ordinarily only a single value is allowed, but fh_open() + and fh_parse() take a mask.) */ +enum fh_referent + { + FH_REF_FILE = 001, /* Ordinary file (the most common case). */ + FH_REF_INLINE = 002, /* The inline file. */ + FH_REF_SCRATCH = 004 /* Temporary dataset. */ + }; /* File modes. */ enum fh_mode { - MODE_TEXT, /* New-line delimited lines. */ - MODE_BINARY /* Fixed-length records. */ + FH_MODE_TEXT, /* New-line delimited lines. */ + FH_MODE_BINARY /* Fixed-length records. */ }; /* Properties of a file handle. */ @@ -41,27 +52,45 @@ void fh_init (void); void fh_done (void); /* Creating file handles. */ -struct file_handle *fh_create (const char *handle_name, - const char *filename, - const struct fh_properties *); -/* Destroy file handle */ -void fh_free(struct file_handle *); - +struct file_handle *fh_create_file (const char *handle_name, + const char *filename, + const struct fh_properties *); +struct file_handle *fh_create_scratch (const char *handle_name); const struct fh_properties *fh_default_properties (void); -/* Finding file handles, based on handle name or filename. */ +/* Delete file handle from global list. */ +void fh_free (struct file_handle *); + +/* Finding file handles. */ struct file_handle *fh_from_name (const char *handle_name); struct file_handle *fh_from_filename (const char *filename); +struct file_handle *fh_inline_file (void); -/* Querying properties of file handles. */ +/* Generic properties of file handles. */ const char *fh_get_name (const struct file_handle *); +enum fh_referent fh_get_referent (const struct file_handle *); + +/* Properties of FH_REF_FILE file handles. */ const char *fh_get_filename (const struct file_handle *); enum fh_mode fh_get_mode (const struct file_handle *) ; + +/* Properties of FH_REF_FILE and FH_REF_INLINE file handles. */ size_t fh_get_record_width (const struct file_handle *); size_t fh_get_tab_width (const struct file_handle *); +/* Properties of FH_REF_SCRATCH file handles. */ +struct scratch_handle *fh_get_scratch_handle (struct file_handle *); +void fh_set_scratch_handle (struct file_handle *, struct scratch_handle *); + /* Opening and closing file handles. */ -void **fh_open (struct file_handle *, const char *type, const char *mode); +void **fh_open (struct file_handle *, enum fh_referent mask, + const char *type, const char *mode); int fh_close (struct file_handle *, const char *type, const char *mode); +bool fh_is_open (const struct file_handle *); + +/* Default file handle for DATA LIST, REREAD, REPEATING DATA + commands. */ +struct file_handle *fh_get_default_handle (void); +void fh_set_default_handle (struct file_handle *); #endif diff --git a/src/file-handle.h b/src/file-handle.h index 52cf8f12..2e8de05a 100644 --- a/src/file-handle.h +++ b/src/file-handle.h @@ -22,9 +22,10 @@ /* File handles. */ +#include #include #include "file-handle-def.h" -struct file_handle *fh_parse (void); +struct file_handle *fh_parse (enum fh_referent); #endif /* !file_handle.h */ diff --git a/src/file-handle.q b/src/file-handle.q index 0eb1373f..c13be76b 100644 --- a/src/file-handle.q +++ b/src/file-handle.q @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -29,6 +29,7 @@ #include "getl.h" #include "error.h" #include "magic.h" +#include "str.h" #include "var.h" #include "linked-list.h" #include "file-handle-def.h" @@ -44,7 +45,7 @@ name=string; lrecl=integer; tabwidth=integer "x>=0" "%s must be nonnegative"; - mode=mode:!character/image. + mode=mode:!character/image/scratch. */ /* (declarations) */ /* (functions) */ @@ -65,9 +66,9 @@ cmd_file_handle (void) handle = fh_from_name (handle_name); if (handle != NULL) { - msg (SE, _("File handle %s already refers to file %s. " - "File handles cannot be redefined within a session."), - handle_name, fh_get_filename(handle)); + msg (SE, _("File handle %s is already defined. " + "Use CLOSE FILE HANDLE before redefining a file handle."), + handle_name); return CMD_FAILURE; } @@ -78,28 +79,24 @@ cmd_file_handle (void) if (!parse_file_handle (&cmd)) return CMD_FAILURE; - if (token != '.') - { - lex_error (_("expecting end of command")); - goto lossage; - } + if (lex_end_of_command () != CMD_SUCCESS) + goto lossage; - if (cmd.s_name == NULL) + if (cmd.s_name == NULL && cmd.mode != FH_SCRATCH) { - msg (SE, _("The FILE HANDLE required subcommand NAME " - "is not present.")); + lex_sbc_missing ("NAME"); goto lossage; } switch (cmd.mode) { case FH_CHARACTER: - properties.mode = MODE_TEXT; + properties.mode = FH_MODE_TEXT; if (cmd.sbc_tabwidth) properties.tab_width = cmd.n_tabwidth[0]; break; case FH_IMAGE: - properties.mode = MODE_BINARY; + properties.mode = FH_MODE_BINARY; if (cmd.n_lrecl[0] == NOT_LONG) msg (SE, _("Fixed-length records were specified on /RECFORM, but " "record length was not specified on /LRECL. " @@ -116,7 +113,10 @@ cmd_file_handle (void) assert (0); } - handle = fh_create (handle_name, cmd.s_name, &properties); + if (cmd.mode != FH_SCRATCH) + fh_create_file (handle_name, cmd.s_name, &properties); + else + fh_create_scratch (handle_name); free_file_handle (&cmd); return CMD_SUCCESS; @@ -126,36 +126,85 @@ cmd_file_handle (void) return CMD_FAILURE; } -/* Parses a file handle name, which may be a filename as a string or - a file handle name as an identifier. Returns the file handle or - NULL on failure. */ -struct file_handle * -fh_parse (void) +int +cmd_close_file_handle (void) { struct file_handle *handle; - if (token != T_ID && token != T_STRING) + if (!lex_force_id ()) + return CMD_FAILURE; + handle = fh_from_name (tokid); + if (handle == NULL) + return CMD_FAILURE; + + fh_free (handle); + + return CMD_SUCCESS; +} + +/* Returns the name for REFERENT. */ +static const char * +referent_name (enum fh_referent referent) +{ + switch (referent) { - lex_error (_("expecting a file name or handle name")); - return NULL; + case FH_REF_FILE: + return _("file"); + case FH_REF_INLINE: + return _("inline file"); + case FH_REF_SCRATCH: + return _("scratch file"); + default: + abort (); } +} - /* Check for named handles first, then go by filename. */ - handle = NULL; - if (token == T_ID) - handle = fh_from_name (tokid); - if (handle == NULL) - handle = fh_from_filename (ds_c_str (&tokstr)); - if (handle == NULL) +/* Parses a file handle name, which may be a filename as a string + or a file handle name as an identifier. The allowed types of + file handle are restricted to those in REFERENT_MASK. Returns + the file handle when successful, a null pointer on failure. */ +struct file_handle * +fh_parse (enum fh_referent referent_mask) +{ + struct file_handle *handle; + + if (lex_match_id ("INLINE")) + handle = fh_inline_file (); + else { - char *filename = ds_c_str (&tokstr); - char *handle_name = xmalloc (strlen (filename) + 3); - sprintf (handle_name, "\"%s\"", filename); - handle = fh_create (handle_name, filename, fh_default_properties ()); - free (handle_name); + if (token != T_ID && token != T_STRING) + { + lex_error (_("expecting a file name or handle name")); + return NULL; + } + + handle = NULL; + if (token == T_ID) + handle = fh_from_name (tokid); + if (handle == NULL) + handle = fh_from_filename (ds_c_str (&tokstr)); + if (handle == NULL) + { + if (token != T_ID || tokid[0] != '#' || get_syntax () != ENHANCED) + { + char *filename = ds_c_str (&tokstr); + char *handle_name = xasprintf ("\"%s\"", filename); + handle = fh_create_file (handle_name, filename, + fh_default_properties ()); + free (handle_name); + } + else + handle = fh_create_scratch (tokid); + } + lex_get (); } - lex_get (); + if (!(fh_get_referent (handle) & referent_mask)) + { + msg (SE, _("Handle for %s not allowed here."), + referent_name (fh_get_referent (handle))); + return NULL; + } return handle; } diff --git a/src/file-type.c b/src/file-type.c index ff97f8db..4c7c4a03 100644 --- a/src/file-type.c +++ b/src/file-type.c @@ -103,7 +103,7 @@ int cmd_file_type (void) { static struct file_type_pgm *fty; /* FIXME: static? WTF? */ - struct file_handle *fh = NULL; + struct file_handle *fh = fh_inline_file (); /* Initialize. */ discard_variables (); @@ -139,7 +139,7 @@ cmd_file_type (void) if (lex_match_id ("FILE")) { lex_match ('='); - fh = fh_parse (); + fh = fh_parse (FH_REF_FILE | FH_REF_INLINE); if (fh == NULL) goto error; } @@ -279,7 +279,7 @@ cmd_file_type (void) fty->reader = dfm_open_reader (fh); if (fty->reader == NULL) goto error; - default_handle = fh; + fh_set_default_handle (fh); create_col_var (&fty->record); if (fty->case_sbc.name[0]) diff --git a/src/filename.c b/src/filename.c index b78897e2..0c085776 100644 --- a/src/filename.c +++ b/src/filename.c @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -530,6 +530,18 @@ fn_basename (const char *filename) abort (); } #endif + +/* Returns the extension part of FILENAME as a malloc()'d string. + If FILENAME does not have an extension, returns an empty + string. */ +char * +fn_extension (const char *filename) +{ + const char *extension = strrchr (filename, '.'); + if (extension == NULL) + extension = ""; + return xstrdup (extension); +} #if unix /* Returns the current working directory, as a malloc()'d string. diff --git a/src/filename.h b/src/filename.h index 3da1374d..46cdd7c5 100644 --- a/src/filename.h +++ b/src/filename.h @@ -35,6 +35,7 @@ char *fn_prepend_dir (const char *filename, const char *directory); char *fn_normalize (const char *fn); char *fn_dirname (const char *fn); char *fn_basename (const char *fn); +char *fn_extension (const char *fn); char *fn_get_cwd (void); diff --git a/src/get.c b/src/get.c index 0b3c131c..deda2d57 100644 --- a/src/get.c +++ b/src/get.c @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -21,6 +21,8 @@ #include "error.h" #include #include "alloc.h" +#include "any-reader.h" +#include "any-writer.h" #include "case.h" #include "command.h" #include "dictionary.h" @@ -29,10 +31,8 @@ #include "hash.h" #include "lexer.h" #include "misc.h" -#include "pfm-read.h" #include "pfm-write.h" #include "settings.h" -#include "sfm-read.h" #include "sfm-write.h" #include "str.h" #include "value-labels.h" @@ -52,116 +52,147 @@ static void map_case (const struct case_map *, const struct ccase *, struct ccase *); static void destroy_case_map (struct case_map *); -/* Operation type. */ -enum operation +static bool parse_dict_trim (struct dictionary *); + +/* Reading system and portable files. */ + +/* Type of command. */ +enum reader_command { - OP_READ, /* GET or IMPORT. */ - OP_SAVE, /* SAVE or XSAVE. */ - OP_EXPORT /* EXPORT. */ + GET_CMD, + IMPORT_CMD }; -static bool parse_dict_trim (struct dictionary *); - -/* GET input program. */ -struct get_pgm +/* Case reader input program. */ +struct case_reader_pgm { - struct sfm_reader *reader; /* System file reader. */ - struct case_map *map; /* Map from system file to active file dict. */ + struct any_reader *reader; /* File reader. */ + struct case_map *map; /* Map from file dict to active file dict. */ struct ccase bounce; /* Bounce buffer. */ }; -static void get_pgm_free (struct get_pgm *); +static const struct case_source_class case_reader_source_class; -/* Parses the GET command. */ -int -cmd_get (void) +static void case_reader_pgm_free (struct case_reader_pgm *); + +/* Parses a GET or IMPORT command. */ +static int +parse_read_command (enum reader_command type) { - struct get_pgm *pgm = NULL; - struct file_handle *fh; + struct case_reader_pgm *pgm = NULL; + struct file_handle *fh = NULL; struct dictionary *dict = NULL; - pgm = xmalloc (sizeof *pgm); - pgm->reader = NULL; - pgm->map = NULL; - case_nullify (&pgm->bounce); + for (;;) + { + lex_match ('/'); - discard_variables (); + if (lex_match_id ("FILE") || token == T_STRING) + { + lex_match ('='); - lex_match ('/'); - if (lex_match_id ("FILE")) - lex_match ('='); - fh = fh_parse (); - if (fh == NULL) - goto error; + fh = fh_parse (FH_REF_FILE | FH_REF_SCRATCH); + if (fh == NULL) + goto error; + } + else if (type == IMPORT_CMD && lex_match_id ("TYPE")) + { + lex_match ('='); - pgm->reader = sfm_open_reader (fh, &dict, NULL); + if (lex_match_id ("COMM")) + type = PFM_COMM; + else if (lex_match_id ("TAPE")) + type = PFM_TAPE; + else + { + lex_error (_("expecting COMM or TAPE")); + goto error; + } + } + else + break; + } + + if (fh == NULL) + { + lex_sbc_missing ("FILE"); + goto error; + } + + discard_variables (); + + pgm = xmalloc (sizeof *pgm); + pgm->reader = any_reader_open (fh, &dict); + pgm->map = NULL; + case_nullify (&pgm->bounce); if (pgm->reader == NULL) goto error; - case_create (&pgm->bounce, dict_get_next_value_idx (dict)); + case_create (&pgm->bounce, dict_get_next_value_idx (dict)); + start_case_map (dict); - while (lex_match ('/')) - if (!parse_dict_trim (dict)) - goto error; - if (!lex_end_of_command ()) - return false; + while (token != '.') + { + lex_match ('/'); + if (!parse_dict_trim (dict)) + goto error; + } - dict_compact_values (dict); pgm->map = finish_case_map (dict); - + dict_destroy (default_dict); default_dict = dict; - vfm_source = create_case_source (&get_source_class, pgm); + vfm_source = create_case_source (&case_reader_source_class, pgm); return CMD_SUCCESS; error: - get_pgm_free (pgm); - if (dict != NULL) + case_reader_pgm_free (pgm); + if (dict != NULL) dict_destroy (dict); return CMD_FAILURE; } -/* Frees a struct get_pgm. */ +/* Frees a struct case_reader_pgm. */ static void -get_pgm_free (struct get_pgm *pgm) +case_reader_pgm_free (struct case_reader_pgm *pgm) { if (pgm != NULL) { - sfm_close_reader (pgm->reader); + any_reader_close (pgm->reader); destroy_case_map (pgm->map); case_destroy (&pgm->bounce); free (pgm); } } -/* Clears internal state related to GET input procedure. */ +/* Clears internal state related to case reader input procedure. */ static void -get_source_destroy (struct case_source *source) +case_reader_source_destroy (struct case_source *source) { - struct get_pgm *pgm = source->aux; - get_pgm_free (pgm); + struct case_reader_pgm *pgm = source->aux; + case_reader_pgm_free (pgm); } /* Reads all the cases from the data file into C and passes them to WRITE_CASE one by one, passing WC_DATA. */ static void -get_source_read (struct case_source *source, - struct ccase *c, - write_case_func *write_case, write_case_data wc_data) +case_reader_source_read (struct case_source *source, + struct ccase *c, + write_case_func *write_case, write_case_data wc_data) { - struct get_pgm *pgm = source->aux; + struct case_reader_pgm *pgm = source->aux; int ok; do { if (pgm->map == NULL) - ok = sfm_read_case (pgm->reader, c); + ok = any_reader_read (pgm->reader, c); else { - ok = sfm_read_case (pgm->reader, &pgm->bounce); + ok = any_reader_read (pgm->reader, &pgm->bounce); if (ok) map_case (pgm->map, &pgm->bounce, c); } @@ -172,14 +203,30 @@ get_source_read (struct case_source *source, while (ok); } -const struct case_source_class get_source_class = +static const struct case_source_class case_reader_source_class = { - "GET", + "case reader", NULL, - get_source_read, - get_source_destroy, + case_reader_source_read, + case_reader_source_destroy, }; +/* GET. */ +int +cmd_get (void) +{ + return parse_read_command (GET_CMD); +} + +/* IMPORT. */ +int +cmd_import (void) +{ + return parse_read_command (IMPORT_CMD); +} + +/* Writing system and portable files. */ + /* Type of output file. */ enum writer_type { @@ -194,11 +241,10 @@ enum command_type PROC_CMD /* Procedure. */ }; -/* Portable or system file writer plus a case map. */ -struct any_writer +/* File writer plus a case map. */ +struct case_writer { - enum writer_type writer_type; - void *writer; + struct any_writer *writer; /* File writer. */ struct case_map *map; /* Map to output file dictionary (null pointer for identity mapping). */ struct ccase bounce; /* Bounce buffer for mapping (if needed). */ @@ -206,19 +252,11 @@ struct any_writer /* Destroys AW. */ static void -any_writer_destroy (struct any_writer *aw) +case_writer_destroy (struct case_writer *aw) { if (aw != NULL) { - switch (aw->writer_type) - { - case PORFILE_WRITER: - pfm_close_writer (aw->writer); - break; - case SYSFILE_WRITER: - sfm_close_writer (aw->writer); - break; - } + any_writer_close (aw->writer); destroy_case_map (aw->map); case_destroy (&aw->bounce); free (aw); @@ -235,7 +273,7 @@ any_writer_destroy (struct any_writer *aw) included. On failure, returns a null pointer. */ -static struct any_writer * +static struct case_writer * parse_write_command (enum writer_type writer_type, enum command_type command_type, bool *retain_unselected) @@ -243,7 +281,7 @@ parse_write_command (enum writer_type writer_type, /* Common data. */ struct file_handle *handle; /* Output file. */ struct dictionary *dict; /* Dictionary for output file. */ - struct any_writer *aw; /* Writer. */ + struct case_writer *aw; /* Writer. */ /* Common options. */ bool print_map; /* Print map? TODO. */ @@ -261,7 +299,6 @@ parse_write_command (enum writer_type writer_type, handle = NULL; dict = dict_clone (default_dict); aw = xmalloc (sizeof *aw); - aw->writer_type = writer_type; aw->writer = NULL; aw->map = NULL; case_nullify (&aw->bounce); @@ -286,7 +323,7 @@ parse_write_command (enum writer_type writer_type, lex_match ('='); - handle = fh_parse (); + handle = fh_parse (FH_REF_FILE | FH_REF_SCRATCH); if (handle == NULL) goto error; } @@ -374,45 +411,42 @@ parse_write_command (enum writer_type writer_type, if (aw->map != NULL) case_create (&aw->bounce, dict_get_next_value_idx (dict)); - switch (writer_type) + if (fh_get_referent (handle) == FH_REF_FILE) { - case SYSFILE_WRITER: - aw->writer = sfm_open_writer (handle, dict, sysfile_opts); - break; - case PORFILE_WRITER: - aw->writer = pfm_open_writer (handle, dict, porfile_opts); - break; + switch (writer_type) + { + case SYSFILE_WRITER: + aw->writer = any_writer_from_sfm_writer ( + sfm_open_writer (handle, dict, sysfile_opts)); + break; + case PORFILE_WRITER: + aw->writer = any_writer_from_pfm_writer ( + pfm_open_writer (handle, dict, porfile_opts)); + break; + } } - + else + aw->writer = any_writer_open (handle, dict); dict_destroy (dict); return aw; error: - any_writer_destroy (aw); + case_writer_destroy (aw); dict_destroy (dict); return NULL; } /* Writes case C to writer AW. */ static void -any_writer_write_case (struct any_writer *aw, struct ccase *c) +case_writer_write_case (struct case_writer *aw, struct ccase *c) { if (aw->map != NULL) { map_case (aw->map, c, &aw->bounce); c = &aw->bounce; } - - switch (aw->writer_type) - { - case SYSFILE_WRITER: - sfm_write_case (aw->writer, c); - break; - case PORFILE_WRITER: - pfm_write_case (aw->writer, c); - break; - } + any_writer_write (aw->writer, c); } /* SAVE and EXPORT. */ @@ -425,7 +459,7 @@ parse_output_proc (enum writer_type writer_type) { bool retain_unselected; struct variable *saved_filter_variable; - struct any_writer *aw; + struct case_writer *aw; aw = parse_write_command (writer_type, PROC_CMD, &retain_unselected); if (aw == NULL) @@ -437,7 +471,7 @@ parse_output_proc (enum writer_type writer_type) procedure (output_proc, aw); dict_set_filter (default_dict, saved_filter_variable); - any_writer_destroy (aw); + case_writer_destroy (aw); return CMD_SUCCESS; } @@ -445,8 +479,8 @@ parse_output_proc (enum writer_type writer_type) static int output_proc (struct ccase *c, void *aw_) { - struct any_writer *aw = aw_; - any_writer_write_case (aw, c); + struct case_writer *aw = aw_; + case_writer_write_case (aw, c); return 0; } @@ -467,7 +501,7 @@ cmd_export (void) /* Transformation. */ struct output_trns { - struct any_writer *aw; /* Writer. */ + struct case_writer *aw; /* Writer. */ }; static trns_proc_func output_trns_proc; @@ -494,7 +528,7 @@ static int output_trns_proc (void *trns_, struct ccase *c, int case_num UNUSED) { struct output_trns *t = trns_; - any_writer_write_case (t->aw, c); + case_writer_write_case (t->aw, c); return -1; } @@ -506,7 +540,7 @@ output_trns_free (void *trns_) if (t != NULL) { - any_writer_destroy (t->aw); + case_writer_destroy (t->aw); free (t); } } @@ -709,7 +743,7 @@ struct mtf_file int type; /* One of MTF_*. */ struct variable **by; /* List of BY variables for this file. */ struct file_handle *handle; /* File handle. */ - struct sfm_reader *reader; /* System file reader. */ + struct any_reader *reader; /* File reader. */ struct dictionary *dict; /* Dictionary from system file. */ /* IN subcommand. */ @@ -859,11 +893,11 @@ cmd_match_files (void) } else { - file->handle = fh_parse (); + file->handle = fh_parse (FH_REF_FILE | FH_REF_SCRATCH); if (file->handle == NULL) goto error; - file->reader = sfm_open_reader (file->handle, &file->dict, NULL); + file->reader = any_reader_open (file->handle, &file->dict); if (file->reader == NULL) goto error; @@ -1151,7 +1185,7 @@ static void mtf_free_file (struct mtf_file *file) { free (file->by); - sfm_close_reader (file->reader); + any_reader_close (file->reader); if (file->dict != default_dict) dict_destroy (file->dict); case_destroy (&file->input); @@ -1225,7 +1259,7 @@ mtf_read_nonactive_records (void *mtf_) for (iter = mtf->head; iter != NULL; iter = next) { next = iter->next; - if (iter->handle && !sfm_read_case (iter->reader, &iter->input)) + if (iter->handle && !any_reader_read (iter->reader, &iter->input)) mtf_delete_file_in_place (mtf, &iter); } } @@ -1323,7 +1357,7 @@ mtf_processing (struct ccase *c, void *mtf_) { if (iter->handle == NULL) return 1; - if (sfm_read_case (iter->reader, &iter->input)) + if (any_reader_read (iter->reader, &iter->input)) continue; mtf_delete_file_in_place (mtf, &iter); } @@ -1405,7 +1439,7 @@ mtf_processing (struct ccase *c, void *mtf_) { next = iter->next_min; if (iter->reader != NULL - && !sfm_read_case (iter->reader, &iter->input)) + && !any_reader_read (iter->reader, &iter->input)) mtf_delete_file_in_place (mtf, &iter); } } @@ -1500,162 +1534,6 @@ get_master (struct variable *v) return v->aux; } -/* IMPORT command. */ - -/* IMPORT input program. */ -struct import_pgm - { - struct pfm_reader *reader; /* Portable file reader. */ - struct case_map *map; /* Map from system file to active file dict. */ - struct ccase bounce; /* Bounce buffer. */ - }; - -static void import_pgm_free (struct import_pgm *); - -/* Parses the IMPORT command. */ -int -cmd_import (void) -{ - struct import_pgm *pgm = NULL; - struct file_handle *fh = NULL; - struct dictionary *dict = NULL; - enum pfm_type type; - - lex_match ('/'); - for (;;) - { - if (pgm == NULL && (lex_match_id ("FILE") || token == T_STRING)) - { - lex_match ('='); - - fh = fh_parse (); - if (fh == NULL) - goto error; - } - else if (pgm == NULL && lex_match_id ("TYPE")) - { - lex_match ('='); - - if (lex_match_id ("COMM")) - type = PFM_COMM; - else if (lex_match_id ("TAPE")) - type = PFM_TAPE; - else - { - lex_error (_("expecting COMM or TAPE")); - goto error; - } - } - else - { - if (pgm == NULL) - { - if (fh == NULL) - { - lex_sbc_missing ("FILE"); - goto error; - } - - discard_variables (); - - pgm = xmalloc (sizeof *pgm); - pgm->reader = pfm_open_reader (fh, &dict, NULL); - pgm->map = NULL; - case_nullify (&pgm->bounce); - if (pgm->reader == NULL) - goto error; - - case_create (&pgm->bounce, dict_get_next_value_idx (dict)); - - start_case_map (dict); - } - - if (token == '.') - break; - - if (!parse_dict_trim (dict)) - goto error; - } - - lex_match ('/'); - } - if (pgm == NULL) - { - lex_error (NULL); - goto error; - } - - pgm->map = finish_case_map (dict); - - dict_destroy (default_dict); - default_dict = dict; - - vfm_source = create_case_source (&import_source_class, pgm); - - return CMD_SUCCESS; - - error: - import_pgm_free (pgm); - if (dict != NULL) - dict_destroy (dict); - return CMD_FAILURE; -} - -/* Frees a struct import_pgm. */ -static void -import_pgm_free (struct import_pgm *pgm) -{ - if (pgm != NULL) - { - pfm_close_reader (pgm->reader); - destroy_case_map (pgm->map); - case_destroy (&pgm->bounce); - free (pgm); - } -} - -/* Clears internal state related to IMPORT input procedure. */ -static void -import_source_destroy (struct case_source *source) -{ - struct import_pgm *pgm = source->aux; - import_pgm_free (pgm); -} - -/* Reads all the cases from the data file into C and passes them - to WRITE_CASE one by one, passing WC_DATA. */ -static void -import_source_read (struct case_source *source, - struct ccase *c, - write_case_func *write_case, write_case_data wc_data) -{ - struct import_pgm *pgm = source->aux; - int ok; - - do - { - if (pgm->map == NULL) - ok = pfm_read_case (pgm->reader, c); - else - { - ok = pfm_read_case (pgm->reader, &pgm->bounce); - if (ok) - map_case (pgm->map, &pgm->bounce, c); - } - - if (ok) - ok = write_case (wc_data); - } - while (ok); -} - -const struct case_source_class import_source_class = - { - "IMPORT", - NULL, - import_source_read, - import_source_destroy, - }; /* Case map. diff --git a/src/glob.c b/src/glob.c index c0b3c905..12e173d1 100644 --- a/src/glob.c +++ b/src/glob.c @@ -31,8 +31,6 @@ struct transformation *t_trns; size_t n_trns, m_trns, f_trns; int FILTER_before_TEMPORARY; - -struct file_handle *default_handle; /* Functions. */ diff --git a/src/inpt-pgm.c b/src/inpt-pgm.c index eaa19532..855b4eca 100644 --- a/src/inpt-pgm.c +++ b/src/inpt-pgm.c @@ -317,7 +317,7 @@ cmd_reread (void) struct expression *e; /* Expression for column to set. */ struct reread_trns *t; /* Created transformation. */ - fh = default_handle; + fh = fh_get_default_handle (); e = NULL; while (token != '.') { @@ -339,7 +339,7 @@ cmd_reread (void) else if (lex_match_id ("FILE")) { lex_match ('='); - fh = fh_parse (); + fh = fh_parse (FH_REF_FILE | FH_REF_INLINE); if (fh == NULL) { expr_free (e); diff --git a/src/matrix-data.c b/src/matrix-data.c index ee43d82f..994285cd 100644 --- a/src/matrix-data.c +++ b/src/matrix-data.c @@ -172,7 +172,7 @@ cmd_matrix_data (void) { struct pool *pool; struct matrix_data_pgm *mx; - struct file_handle *fh = NULL; + struct file_handle *fh = fh_inline_file (); unsigned seen = 0; @@ -258,7 +258,7 @@ cmd_matrix_data (void) else if (lex_match_id ("FILE")) { lex_match ('='); - fh = fh_parse (); + fh = fh_parse (FH_REF_FILE | FH_REF_INLINE); if (fh == NULL) goto lossage; } diff --git a/src/pfm-read.c b/src/pfm-read.c index 3dd939dd..f3987475 100644 --- a/src/pfm-read.c +++ b/src/pfm-read.c @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . Code for parsing floating-point numbers adapted from GNU C library. @@ -49,6 +49,16 @@ #include "debug-print.h" +/* portable_to_local[PORTABLE] translates the given portable + character into the local character set. */ +static const char portable_to_local[256] = + { + " " + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz ." + "<(+|&[]!$*);^-/|,%_>?`:$@'=\" ~- 0123456789 -() {}\\ " + " " + }; + /* Portable file reader. */ struct pfm_reader { @@ -149,7 +159,7 @@ pfm_open_reader (struct file_handle *fh, struct dictionary **dict, struct pfm_reader *volatile r = NULL; *dict = dict_create (); - if (!fh_open (fh, "portable file", "rs")) + if (!fh_open (fh, FH_REF_FILE, "portable file", "rs")) goto error; /* Create and initialize reader. */ @@ -353,16 +363,6 @@ read_pool_string (struct pfm_reader *r) static void read_header (struct pfm_reader *r) { - /* portable_to_local[PORTABLE] translates the given portable - character into the local character set. */ - static const char portable_to_local[256] = - { - " " - "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz ." - "<(+|&[]!$*);^-/|,%_>?`:$@'=\" ~- 0123456789 -() {}\\ " - " " - }; - char *trans; int i; @@ -687,3 +687,38 @@ pfm_read_case (struct pfm_reader *r, struct ccase *c) return true; } + +/* Returns true if FILE is an SPSS portable file, + false otherwise. */ +bool +pfm_detect (FILE *file) +{ + unsigned char header[464]; + char trans[256]; + int cooked_cnt, raw_cnt; + int i; + + cooked_cnt = raw_cnt = 0; + while (cooked_cnt < sizeof header) + { + int c = getc (file); + if (c == EOF || raw_cnt++ > 512) + return false; + else if (c != '\n' && c != '\r') + header[cooked_cnt++] = c; + } + + memset (trans, 0, 256); + for (i = 64; i < 256; i++) + { + unsigned char c = header[i + 200]; + if (trans[c] == 0) + trans[c] = portable_to_local[i]; + } + + for (i = 0; i < 8; i++) + if (trans[header[i + 456]] != "SPSSPORT"[i]) + return false; + + return true; +} diff --git a/src/pfm-read.h b/src/pfm-read.h index 3f84e160..5639816a 100644 --- a/src/pfm-read.h +++ b/src/pfm-read.h @@ -23,6 +23,7 @@ /* Portable file reading. */ #include +#include /* Information produced by pfm_read_dictionary() that doesn't fit into a dictionary struct. */ @@ -42,5 +43,6 @@ struct pfm_reader *pfm_open_reader (struct file_handle *, struct pfm_read_info *); bool pfm_read_case (struct pfm_reader *, struct ccase *); void pfm_close_reader (struct pfm_reader *); +bool pfm_detect (FILE *); #endif /* pfm-read.h */ diff --git a/src/pfm-write.c b/src/pfm-write.c index ef94831e..132216d5 100644 --- a/src/pfm-write.c +++ b/src/pfm-write.c @@ -111,7 +111,7 @@ pfm_open_writer (struct file_handle *fh, struct dictionary *dict, goto open_error; /* Open file handle. */ - if (!fh_open (fh, "portable file", "we")) + if (!fh_open (fh, FH_REF_FILE, "portable file", "we")) goto error; /* Initialize data structures. */ @@ -420,7 +420,7 @@ write_value_labels (struct pfm_writer *w, const struct dictionary *dict) /* Writes case ELEM to the portable file represented by H. Returns success. */ int -pfm_write_case (struct pfm_writer *w, struct ccase *c) +pfm_write_case (struct pfm_writer *w, const struct ccase *c) { int i; @@ -451,8 +451,6 @@ pfm_close_writer (struct pfm_writer *w) if (w == NULL) return; - fh_close (w->fh, "portable file", "we"); - if (w->file != NULL) { char buf[80]; @@ -469,6 +467,8 @@ pfm_close_writer (struct pfm_writer *w) fh_get_filename (w->fh), strerror (errno)); } + fh_close (w->fh, "portable file", "we"); + free (w->vars); free (w); } diff --git a/src/pfm-write.h b/src/pfm-write.h index e335098a..a1bb7ce9 100644 --- a/src/pfm-write.h +++ b/src/pfm-write.h @@ -46,7 +46,7 @@ struct pfm_writer *pfm_open_writer (struct file_handle *, struct dictionary *, struct pfm_write_options); struct pfm_write_options pfm_writer_default_options (void); -int pfm_write_case (struct pfm_writer *, struct ccase *); +int pfm_write_case (struct pfm_writer *, const struct ccase *); void pfm_close_writer (struct pfm_writer *); #endif /* pfm-write.h */ diff --git a/src/print.c b/src/print.c index ce14d661..966c4813 100644 --- a/src/print.c +++ b/src/print.c @@ -156,7 +156,7 @@ internal_cmd_print (int f) { lex_match ('='); - fh = fh_parse (); + fh = fh_parse (FH_REF_FILE); if (fh == NULL) goto error; } @@ -191,7 +191,7 @@ internal_cmd_print (int f) if (prt.writer == NULL) goto error; - if (fh_get_mode (fh) == MODE_BINARY) + if (fh_get_mode (fh) == FH_MODE_BINARY) prt.options |= PRT_BINARY; } @@ -836,10 +836,12 @@ dump_table (const struct file_handle *fh) } if (fh != NULL) - tab_title (t, 1, _("Writing %d record(s) to file %s."), - recno, fh_get_filename (fh)); + tab_title (t, 1, ngettext ("Writing %d record to %s.", + "Writing %d records to %s.", recno), + recno, fh_get_name (fh)); else - tab_title (t, 1, _("Writing %d record(s) to the listing file."), recno); + tab_title (t, 1, ngettext ("Writing %d record.", + "Writing %d records.", recno), recno); tab_submit (t); } @@ -1027,7 +1029,7 @@ cmd_print_space (void) { lex_match ('='); - fh = fh_parse (); + fh = fh_parse (FH_REF_FILE); if (fh == NULL) return CMD_FAILURE; lex_get (); diff --git a/src/regression.q b/src/regression.q index 29224be4..23ba49a6 100644 --- a/src/regression.q +++ b/src/regression.q @@ -687,7 +687,7 @@ regression_custom_export (struct cmd_regression *cmd) model_file = NULL; else { - model_file = fh_parse (); + model_file = fh_parse (FH_REF_FILE); if (model_file == NULL) return 0; } diff --git a/src/scratch-handle.c b/src/scratch-handle.c new file mode 100644 index 00000000..5e3b74ad --- /dev/null +++ b/src/scratch-handle.c @@ -0,0 +1,36 @@ +/* PSPP - computes sample statistics. + Copyright (C) 2006 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. */ + +#include +#include +#include "scratch-handle.h" +#include "casefile.h" +#include "dictionary.h" + +/* Destroys HANDLE. */ +void +scratch_handle_destroy (struct scratch_handle *handle) +{ + if (handle != NULL) + { + dict_destroy (handle->dictionary); + casefile_destroy (handle->casefile); + free (handle); + } +} diff --git a/src/scratch-handle.h b/src/scratch-handle.h new file mode 100644 index 00000000..34739cf2 --- /dev/null +++ b/src/scratch-handle.h @@ -0,0 +1,34 @@ +/* PSPP - computes sample statistics. + Copyright (C) 2006 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 SCRATCH_HANDLE_H +#define SCRATCH_HANDLE_H 1 + +#include + +/* A scratch file. */ +struct scratch_handle + { + struct dictionary *dictionary; /* Dictionary. */ + struct casefile *casefile; /* Cases. */ + }; + +void scratch_handle_destroy (struct scratch_handle *); + +#endif /* scratch-handle.h */ diff --git a/src/scratch-reader.c b/src/scratch-reader.c new file mode 100644 index 00000000..60355dc4 --- /dev/null +++ b/src/scratch-reader.c @@ -0,0 +1,88 @@ +/* PSPP - computes sample statistics. + Copyright (C) 2006 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. */ + +#include +#include "scratch-reader.h" +#include +#include "casefile.h" +#include "dictionary.h" +#include "error.h" +#include "file-handle-def.h" +#include "scratch-handle.h" +#include "xalloc.h" + +#include "gettext.h" +#define _(msgid) gettext (msgid) + +/* A reader for a scratch file. */ +struct scratch_reader + { + struct file_handle *fh; /* Underlying file handle. */ + struct casereader *casereader; /* Case reader. */ + }; + +/* Opens FH, which must have referent type FH_REF_SCRATCH, and + returns a scratch_reader for it, or a null pointer on + failure. Stores the dictionary for the scratch file into + *DICT. + + If you use an any_reader instead, then your code can be more + flexible without being any harder to write. */ +struct scratch_reader * +scratch_reader_open (struct file_handle *fh, struct dictionary **dict) +{ + struct scratch_handle *sh; + struct scratch_reader *reader; + + if (!fh_open (fh, FH_REF_SCRATCH, "scratch file", "rs")) + return NULL; + + sh = fh_get_scratch_handle (fh); + if (sh == NULL) + { + msg (SE, _("Scratch file handle %s has not yet been written, " + "using SAVE or another procedure, so it cannot yet " + "be used for reading."), + fh_get_name (fh)); + return NULL; + } + + *dict = dict_clone (sh->dictionary); + reader = xmalloc (sizeof *reader); + reader->fh = fh; + reader->casereader = casefile_get_reader (sh->casefile); + return reader; +} + +/* Reads a case from READER into C. + Returns true if successful, false on error or at end of file. */ +bool +scratch_reader_read_case (struct scratch_reader *reader, struct ccase *c) +{ + return casereader_read (reader->casereader, c); +} + +/* Closes READER. */ +void +scratch_reader_close (struct scratch_reader *reader) +{ + fh_close (reader->fh, "scratch file", "rs"); + casereader_destroy (reader->casereader); + free (reader); +} diff --git a/src/scratch-reader.h b/src/scratch-reader.h new file mode 100644 index 00000000..534ceb95 --- /dev/null +++ b/src/scratch-reader.h @@ -0,0 +1,33 @@ +/* PSPP - computes sample statistics. + Copyright (C) 2006 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 SCRATCH_READER_H +#define SCRATCH_READER_H 1 + +#include + +struct dictionary; +struct file_handle; +struct ccase; +struct scratch_reader *scratch_reader_open (struct file_handle *, + struct dictionary **); +bool scratch_reader_read_case (struct scratch_reader *, struct ccase *); +void scratch_reader_close (struct scratch_reader *); + +#endif /* scratch-reader.h */ diff --git a/src/scratch-writer.c b/src/scratch-writer.c new file mode 100644 index 00000000..e5ac0467 --- /dev/null +++ b/src/scratch-writer.c @@ -0,0 +1,112 @@ +/* PSPP - computes sample statistics. + Copyright (C) 2006 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. */ + +#include +#include "scratch-writer.h" +#include +#include "case.h" +#include "casefile.h" +#include "dictionary.h" +#include "file-handle-def.h" +#include "scratch-handle.h" +#include "xalloc.h" + +/* A scratch file writer. */ +struct scratch_writer + { + struct scratch_handle *handle; /* Underlying scratch handle. */ + struct file_handle *fh; /* Underlying file handle. */ + struct dict_compactor *compactor; /* Compacts into handle->dictionary. */ + }; + +/* Opens FH, which must have referent type FH_REF_SCRATCH, and + returns a scratch_writer for it, or a null pointer on + failure. Cases stored in the scratch_writer will be expected + to be drawn from DICTIONARY. + + If you use an any_writer instead, then your code can be more + flexible without being any harder to write. */ +struct scratch_writer * +scratch_writer_open (struct file_handle *fh, + const struct dictionary *dictionary) +{ + struct scratch_handle *sh; + struct scratch_writer *writer; + struct dictionary *scratch_dict; + struct dict_compactor *compactor; + + if (!fh_open (fh, FH_REF_SCRATCH, "scratch file", "we")) + return NULL; + + /* Destroy previous contents of handle. */ + sh = fh_get_scratch_handle (fh); + if (sh != NULL) + scratch_handle_destroy (sh); + + /* Copy the dictionary and compact if needed. */ + scratch_dict = dict_clone (dictionary); + if (dict_needs_compaction (scratch_dict)) + { + compactor = dict_make_compactor (scratch_dict); + dict_compact_values (scratch_dict); + } + else + compactor = NULL; + + /* Create new contents. */ + sh = xmalloc (sizeof *sh); + sh->dictionary = scratch_dict; + sh->casefile = casefile_create (dict_get_next_value_idx (sh->dictionary)); + + /* Create writer. */ + writer = xmalloc (sizeof *writer); + writer->handle = sh; + writer->fh = fh; + writer->compactor = compactor; + + fh_set_scratch_handle (fh, sh); + return writer; +} + +/* Writes case C to WRITER. */ +void +scratch_writer_write_case (struct scratch_writer *writer, + const struct ccase *c) +{ + struct scratch_handle *handle = writer->handle; + if (writer->compactor) + { + struct ccase tmp_case; + case_create (&tmp_case, dict_get_next_value_idx (handle->dictionary)); + dict_compactor_compact (writer->compactor, &tmp_case, c); + casefile_append_xfer (handle->casefile, &tmp_case); + } + else + casefile_append (handle->casefile, c); +} + +/* Closes WRITER. */ +void +scratch_writer_close (struct scratch_writer *writer) +{ + struct scratch_handle *handle = writer->handle; + casefile_mode_reader (handle->casefile); + fh_close (writer->fh, "scratch file", "we"); + free (writer); +} diff --git a/src/scratch-writer.h b/src/scratch-writer.h new file mode 100644 index 00000000..33e3e147 --- /dev/null +++ b/src/scratch-writer.h @@ -0,0 +1,33 @@ +/* PSPP - computes sample statistics. + Copyright (C) 2006 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 SCRATCH_WRITER_H +#define SCRATCH_WRITER_H 1 + +#include + +struct dictionary; +struct file_handle; +struct ccase; +struct scratch_writer *scratch_writer_open (struct file_handle *, + const struct dictionary *); +void scratch_writer_write_case (struct scratch_writer *, const struct ccase *); +void scratch_writer_close (struct scratch_writer *); + +#endif /* scratch-writer.h */ diff --git a/src/sfm-read.c b/src/sfm-read.c index 9e25f741..986dede9 100644 --- a/src/sfm-read.c +++ b/src/sfm-read.c @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -143,15 +143,17 @@ sfm_close_reader (struct sfm_reader *r) if (r == NULL) return; + if (r->file) + { + if (fn_close (fh_get_filename (r->fh), r->file) == EOF) + msg (ME, _("%s: Closing system file: %s."), + fh_get_filename (r->fh), strerror (errno)); + r->file = NULL; + } + if (r->fh != NULL) fh_close (r->fh, "system file", "rs"); - if ( r->file ) { - if (fn_close (fh_get_filename (r->fh), r->file) == EOF) - msg (ME, _("%s: Closing system file: %s."), - fh_get_filename (r->fh), strerror (errno)); - r->file = NULL; - } free (r->vars); free (r->buf); free (r); @@ -206,7 +208,7 @@ sfm_open_reader (struct file_handle *fh, struct dictionary **dict, struct variable **var_by_idx = NULL; *dict = dict_create (); - if (!fh_open (fh, "system file", "rs")) + if (!fh_open (fh, FH_REF_FILE, "system file", "rs")) goto error; /* Create and initialize reader. */ @@ -1524,3 +1526,17 @@ fread_ok (struct sfm_reader *r, void *buffer, size_t byte_cnt) return 0; } } + +/* Returns true if FILE is an SPSS system file, + false otherwise. */ +bool +sfm_detect (FILE *file) +{ + struct sysfile_header hdr; + + if (fread (&hdr, sizeof hdr, 1, file) != 1) + return false; + if (strncmp ("$FL2", hdr.rec_type, 4)) + return false; + return true; +} diff --git a/src/sfm-read.h b/src/sfm-read.h index 3abb1e16..d471ad7b 100644 --- a/src/sfm-read.h +++ b/src/sfm-read.h @@ -20,6 +20,9 @@ #ifndef SFM_READ_H #define SFM_READ_H 1 +#include +#include + /* Reading system files. */ /* System file info that doesn't fit in struct dictionary. */ @@ -41,5 +44,6 @@ struct sfm_reader *sfm_open_reader (struct file_handle *, struct sfm_read_info *); int sfm_read_case (struct sfm_reader *, struct ccase *); void sfm_close_reader (struct sfm_reader *); +bool sfm_detect (FILE *); #endif /* sfm-read.h */ diff --git a/src/sfm-write.c b/src/sfm-write.c index 128d5e4b..dcdab0b4 100644 --- a/src/sfm-write.c +++ b/src/sfm-write.c @@ -156,7 +156,7 @@ sfm_open_writer (struct file_handle *fh, struct dictionary *d, goto open_error; /* Open file handle. */ - if (!fh_open (fh, "system file", "we")) + if (!fh_open (fh, FH_REF_FILE, "system file", "we")) goto error; /* Create and initialize writer. */ @@ -904,8 +904,6 @@ sfm_close_writer (struct sfm_writer *w) if (w == NULL) return; - fh_close (w->fh, "system file", "we"); - if (w->file != NULL) { /* Flush buffer. */ @@ -932,6 +930,8 @@ sfm_close_writer (struct sfm_writer *w) fh_get_filename (w->fh), strerror (errno)); } + fh_close (w->fh, "system file", "we"); + free (w->buf); free (w->vars); free (w); diff --git a/src/str.c b/src/str.c index 081ffea5..eaa9cdff 100644 --- a/src/str.c +++ b/src/str.c @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -270,6 +270,14 @@ str_uppercase (char *s) for (; *s != '\0'; s++) *s = toupper ((unsigned char) *s); } + +/* Converts each character in S to lowercase. */ +void +str_lowercase (char *s) +{ + for (; *s != '\0'; s++) + *s = tolower ((unsigned char) *s); +} /* Initializes ST with initial contents S. */ void diff --git a/src/str.h b/src/str.h index db961959..19fe779a 100644 --- a/src/str.h +++ b/src/str.h @@ -34,6 +34,7 @@ #include "strstr.h" #include "strtok_r.h" #include "vsnprintf.h" +#include "xvasprintf.h" #ifndef HAVE_STRCHR #define strchr index @@ -108,6 +109,7 @@ void str_copy_rpad (char *, size_t, const char *); void str_copy_trunc (char *, size_t, const char *); void str_copy_buf_trunc (char *, size_t, const char *, size_t); void str_uppercase (char *); +void str_lowercase (char *); /* Fixed-length strings. */ struct fixed_string diff --git a/src/sysfile-info.c b/src/sysfile-info.c index a9f074ab..c12bd100 100644 --- a/src/sysfile-info.c +++ b/src/sysfile-info.c @@ -86,7 +86,7 @@ cmd_sysfile_info (void) lex_match_id ("FILE"); lex_match ('='); - h = fh_parse (); + h = fh_parse (FH_REF_FILE); if (!h) return CMD_FAILURE; diff --git a/src/var.h b/src/var.h index 3aeb9a63..f4930eea 100644 --- a/src/var.h +++ b/src/var.h @@ -129,10 +129,6 @@ extern struct dictionary *default_dict; /* Transformation state. */ -/* Default file handle for DATA LIST, REREAD, REPEATING DATA - commands. */ -extern struct file_handle *default_handle; - /* PROCESS IF expression. */ extern struct expression *process_if_expr; diff --git a/src/vfm.c b/src/vfm.c index 3d8f03ae..06cf67ec 100644 --- a/src/vfm.c +++ b/src/vfm.c @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -35,6 +35,7 @@ #include "ctl-stack.h" #include "error.h" #include "expressions/public.h" +#include "file-handle-def.h" #include "misc.h" #include "settings.h" #include "som.h" @@ -75,9 +76,9 @@ struct case_source *vfm_source; /* The replacement active file, to which cases are written. */ struct case_sink *vfm_sink; -/* Nonzero if the case needs to have values deleted before being - stored, zero otherwise. */ -static int compaction_necessary; +/* The compactor used to compact a compact, if necessary; + otherwise a null pointer. */ +static struct dict_compactor *compactor; /* Time at which vfm was last invoked. */ static time_t last_vfm_invocation; @@ -229,8 +230,9 @@ open_active_file (void) } /* Figure out compaction. */ - compaction_necessary = (dict_get_next_value_idx (temp_dict) - != dict_get_compacted_value_cnt (temp_dict)); + compactor = (dict_needs_compaction (temp_dict) + ? dict_make_compactor (temp_dict) + : NULL); /* Prepare sink. */ if (vfm_sink == NULL) @@ -279,10 +281,10 @@ write_case (struct write_case_data *wc_data) /* Write case to replacement active file. */ if (vfm_sink->class->write != NULL) { - if (compaction_necessary) + if (compactor != NULL) { - dict_compact_case (temp_dict, &wc_data->sink_case, - &wc_data->trns_case); + dict_compactor_compact (compactor, &wc_data->sink_case, + &wc_data->trns_case); vfm_sink->class->write (vfm_sink, &wc_data->sink_case); } else @@ -425,8 +427,11 @@ close_active_file (void) } /* Finish compaction. */ - if (compaction_necessary) - dict_compact_values (default_dict); + if (compactor != NULL) + { + dict_compactor_destroy (compactor); + dict_compact_values (default_dict); + } /* Free data source. */ free_case_source (vfm_source); @@ -944,7 +949,7 @@ void discard_variables (void) { dict_clear (default_dict); - default_handle = NULL; + fh_set_default_handle (NULL); n_lag = 0; diff --git a/src/vfm.h b/src/vfm.h index d72568ce..cfd639a9 100644 --- a/src/vfm.h +++ b/src/vfm.h @@ -56,12 +56,8 @@ struct case_source_class }; extern const struct case_source_class storage_source_class; -extern const struct case_source_class data_list_source_class; extern const struct case_source_class file_type_source_class; extern const struct case_source_class input_program_source_class; -extern const struct case_source_class get_source_class; -extern const struct case_source_class import_source_class; -extern const struct case_source_class sort_source_class; struct dictionary; struct case_source *create_case_source (const struct case_source_class *, diff --git a/tests/bugs/agg-crash-2.sh b/tests/bugs/agg-crash-2.sh index 39d0ac0a..d4864f2c 100755 --- a/tests/bugs/agg-crash-2.sh +++ b/tests/bugs/agg-crash-2.sh @@ -71,7 +71,7 @@ if [ $? -ne 0 ] ; then no_result ; fi perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list diff -b -w $TEMPDIR/pspp.list - << EOF -1.1 DATA LIST. Reading free-form data from the command file. +1.1 DATA LIST. Reading free-form data from INLINE. +--------+------+ |Variable|Format| #========#======# diff --git a/tests/bugs/compute-lv.sh b/tests/bugs/compute-lv.sh index 558ea854..941fcca6 100755 --- a/tests/bugs/compute-lv.sh +++ b/tests/bugs/compute-lv.sh @@ -73,7 +73,7 @@ if [ $? -ne 0 ] ; then fail ; fi activity="compare output" perl -pi -e 's/^\s*$//g' pspp.list diff -b pspp.list - << EOF -1.1 DATA LIST. Reading free-form data from the command file. +1.1 DATA LIST. Reading free-form data from INLINE. +----------------+------+ | Variable |Format| #================#======# diff --git a/tests/bugs/computebug.out b/tests/bugs/computebug.out index 73640d46..61fc7d07 100644 --- a/tests/bugs/computebug.out +++ b/tests/bugs/computebug.out @@ -1,4 +1,4 @@ -1.1 DATA LIST. Reading free-form data from the command file. +1.1 DATA LIST. Reading free-form data from INLINE. +--------+------+ |Variable|Format| #========#======# diff --git a/tests/bugs/crosstabs-crash.sh b/tests/bugs/crosstabs-crash.sh index e50ed5f7..b3cae373 100755 --- a/tests/bugs/crosstabs-crash.sh +++ b/tests/bugs/crosstabs-crash.sh @@ -66,7 +66,7 @@ if [ $? -ne 0 ] ; then no_result ; fi perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list diff -b -w $TEMPDIR/pspp.list - << EOF -1.1 DATA LIST. Reading free-form data from the command file. +1.1 DATA LIST. Reading free-form data from INLINE. +--------+------+ |Variable|Format| #========#======# diff --git a/tests/bugs/match-files-scratch.sh b/tests/bugs/match-files-scratch.sh index c36bc9ad..a55a3246 100755 --- a/tests/bugs/match-files-scratch.sh +++ b/tests/bugs/match-files-scratch.sh @@ -76,7 +76,7 @@ if [ $? -ne 0 ] ; then no_result ; fi perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list diff -b -w $TEMPDIR/pspp.list - << EOF -1.1 DATA LIST. Reading free-form data from the command file. +1.1 DATA LIST. Reading free-form data from INLINE. +--------+------+ |Variable|Format| #========#======# diff --git a/tests/bugs/multipass.sh b/tests/bugs/multipass.sh index 98ad523b..8dfbacb5 100755 --- a/tests/bugs/multipass.sh +++ b/tests/bugs/multipass.sh @@ -76,7 +76,7 @@ if [ $? -ne 0 ] ; then no_result ; fi perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list diff -b -w $TEMPDIR/pspp.list - << EOF -1.1 DATA LIST. Reading free-form data from the command file. +1.1 DATA LIST. Reading free-form data from INLINE. +--------+------+ |Variable|Format| #========#======# diff --git a/tests/bugs/recode-copy-bug-1.out b/tests/bugs/recode-copy-bug-1.out index 47113b9b..659eba5f 100644 --- a/tests/bugs/recode-copy-bug-1.out +++ b/tests/bugs/recode-copy-bug-1.out @@ -1,4 +1,4 @@ -1.1 DATA LIST. Reading free-form data from the command file. +1.1 DATA LIST. Reading free-form data from INLINE. +--------+------+ |Variable|Format| #========#======# diff --git a/tests/bugs/recode-copy-bug-2.out b/tests/bugs/recode-copy-bug-2.out index 88674322..50899a81 100644 --- a/tests/bugs/recode-copy-bug-2.out +++ b/tests/bugs/recode-copy-bug-2.out @@ -1,4 +1,4 @@ -1.1 DATA LIST. Reading free-form data from the command file. +1.1 DATA LIST. Reading free-form data from INLINE. +--------+------+ |Variable|Format| #========#======# diff --git a/tests/bugs/t-test-alpha.sh b/tests/bugs/t-test-alpha.sh index 7d1c3ed0..79a28e35 100755 --- a/tests/bugs/t-test-alpha.sh +++ b/tests/bugs/t-test-alpha.sh @@ -80,7 +80,7 @@ if [ $? -ne 0 ] ; then no_result ; fi activity="compare output" perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list diff -b $TEMPDIR/pspp.list - < $name.pspp diff --git a/tests/command/autorecod.sh b/tests/command/autorecod.sh index b9f6e50d..dc304350 100755 --- a/tests/command/autorecod.sh +++ b/tests/command/autorecod.sh @@ -83,7 +83,7 @@ if [ $? -ne 0 ] ; then no_result ; fi activity="test output" perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list diff -b $TEMPDIR/pspp.list - <