Discussion:
ZSAV format support [ZCOMPRESSED subcommand]
Hugo Alejandro
2013-10-02 17:03:27 UTC
Permalink
Hi all, first of all thanks for the great work, it has been noticed in
recent versions.

A few days ago I was recruited to work in the analysis of large surveys,
what caught my attention is the use of the format *. zsav above *.sav.

Apparently this file format supports higher compression ratio and is more
efficient with large databases to reduce their size on disk and be faster to
compress-decompress to create a ZIP file (or other format) with a *.sav file
.

This file type is very recent, included in SPSS version 21 and improved in the
current version 22.

Personally, I can tell that the file type is very efficient in production
environments (using large databases and powerful hardware).

It would be interesting to PSPP support this file type in the future.


Hugo Valencia

note: I have tried to find information about the file specification, but I
have not found much (maybe I did not know where to look), so I attached a
sample file in that format created with SPSS 21 converting one of the
sample files included in PSPP.
Ben Pfaff
2013-10-03 04:40:03 UTC
Permalink
Post by Hugo Alejandro
A few days ago I was recruited to work in the analysis of large surveys,
what caught my attention is the use of the format *. zsav above *.sav.
Apparently this file format supports higher compression ratio and is more
efficient with large databases to reduce their size on disk and be faster to
compress-decompress to create a ZIP file (or other format) with a *.sav file
.
This file type is very recent, included in SPSS version 21 and improved in the
current version 22.
This is very interesting. Thank you for bringing this to our
attention.

The .zsav file format appears to be the same as .sav format up to the
data portion of the file, except that the "magic" at the beginning of
the file is $FL3 instead of $FL2.

The data portion of the file starts at offset 837 (0x345). Its
contents, with my speculation about their meaning, is:

00000345 45 03 00 00 00 00 00 00 - Byte offset of this block, 0x345.
0000034d 14 07 00 00 00 00 00 00 - byte offset of the next block, 0x714.
00000355 30 00 00 00 00 00 00 00 - Length of next block's header, 0x30 bytes.

It is followed by 951 (0x3b7) bytes of data compressed with the
"deflate" algorithm. When inflated, these expand to 1120 (0x460) bytes
that exactly match the data portion of the original physiology.sav,
which starts at offset 729 (0x2d9) in the original file.

The file ends with an additional 48 (0x30) bytes starting at offset 1812
(0x714). Their contents, with my speculation about their meaning, are:

00000714 9c ff ff ff ff ff ff ff - Value -100, dunno why (compression bias?)
0000071c 00 00 00 00 00 00 00 00 - ?
00000724 00 f0 3f 00 01 00 00 00 - ?
0000072c 45 03 00 00 00 00 00 00 - Starting offset of previous block, 0x345.
00000734 5d 03 00 00 00 00 00 00 - Starting offset of data block, 0x35d.
0000073c 60 04 00 00 - Inflated data size, 0x460 bytes.
00000740 b7 03 00 00 - Compressed data size, 0x3b7 bytes.
soumalya ray
2013-10-03 11:20:02 UTC
Permalink
________________________________
Sent: Thursday, 3 October 2013 10:10 AM
Subject: Re: ZSAV format support [ZCOMPRESSED subcommand]
Post by Hugo Alejandro
A few days ago I was recruited to work in the analysis of large surveys,
what caught my attention is the use of the format *. zsav above *.sav.
Apparently this file format supports higher compression ratio and is more
efficient with large databases to reduce their size on disk and be faster to
compress-decompress to create a ZIP file (or other format) with a *.
Bastián Díaz
2013-10-03 13:36:02 UTC
Permalink
Looks interesting if indeed the file format is more efficient with large data files.

Surfing the net, I have also come to savReaderWriter, but unfortunately this program uses SPSS I/O module to function, it is not your own implementation or fully open source.

Reviewing the websites of other statistical packages (SAS, Stata, etc.) do not support the *.Zsav file format currently, but will support in their next versions, especially Stata is working to extend the command -usespss- for version 14.
 
--
Bastián Díaz

interestingly, there is a program already available which could read this format-
savReaderWriter. i did not try it myself, but according to the main page (http://pythonhosted.org/savReaderWriter/#savwriter-write-spss-system-files), zsav was created using the ZLIB compression scheme. 

searching for more materials.


regards,

 Dr Soumalya Ray <drsoumalya-***@yahoo.co.in>
MBBS,MD(PGT-C.Medicine),Ex-HP(Medicine)
skype:som3776|twitter:@docbkp
Hugo Alejandro
2013-10-03 15:16:49 UTC
Permalink
Attached larger files.

*.sav file = 4.6 MB in disk
*.zsav file = 234.6 kb in disk

The compression ratio is not always proportional, but here a sample that can do
a lot (I'm very surprised).

I have not been able to make objective evidence, however, I have been
working with files up to 1 GB of disk size (zsav), and really not noticeable
performance down to use the same file (sav format almost 2 GB).

Thank you for your attention.
Post by Bastián Díaz
Looks interesting if indeed the file format is more efficient with large data files.
Surfing the net, I have also come to savReaderWriter, but unfortunately this
program uses SPSS I/O module to function, it is not your own
implementation or fully open source.
Reviewing the websites of other statistical packages (SAS, Stata, etc.) do
not support the *.Zsav file format currently, but will support in their next
versions, especially Stata is working to extend the command -usespss- for
version 14.
--
Bastián Díaz
interestingly, there is a program already available which could read this
format- savReaderWriter. i did not try it myself, but according to the main
page (
http://pythonhosted.org/savReaderWriter/#savwriter-write-spss-system-files),
zsav was created using the ZLIB compression scheme.
searching for more materials.
regards,
Dr Soumalya Ray <http://drsoumalya.blogspot.com/> <
MBBS,MD(PGT-C.Medicine),Ex-HP(Medicine)
_______________________________________________
Pspp-users mailing list
https://lists.gnu.org/mailman/listinfo/pspp-users
_______________________________________________
Pspp-users mailing list
https://lists.gnu.org/mailman/listinfo/pspp-users
Ben Pfaff
2013-10-03 15:32:54 UTC
Permalink
Post by Hugo Alejandro
Attached larger files.
*.sav file = 4.6 MB in disk
*.zsav file = 234.6 kb in disk
Thanks a lot, I'll take a closer look tonight.
Ben Pfaff
2013-10-04 04:38:57 UTC
Permalink
Post by Ben Pfaff
Post by Hugo Alejandro
A few days ago I was recruited to work in the analysis of large surveys,
what caught my attention is the use of the format *. zsav above *.sav.
Apparently this file format supports higher compression ratio and is more
efficient with large databases to reduce their size on disk and be faster to
compress-decompress to create a ZIP file (or other format) with a *.sav file
.
This file type is very recent, included in SPSS version 21 and improved in the
current version 22.
This is very interesting. Thank you for bringing this to our
attention.
The .zsav file format appears to be the same as .sav format up to the
data portion of the file, except that the "magic" at the beginning of
the file is $FL3 instead of $FL2.
The data portion of the file starts at offset 837 (0x345). Its
00000345 45 03 00 00 00 00 00 00 - Byte offset of this block, 0x345.
0000034d 14 07 00 00 00 00 00 00 - byte offset of the next block, 0x714.
00000355 30 00 00 00 00 00 00 00 - Length of next block's header, 0x30 bytes.
It is followed by 951 (0x3b7) bytes of data compressed with the
"deflate" algorithm. When inflated, these expand to 1120 (0x460) bytes
that exactly match the data portion of the original physiology.sav,
which starts at offset 729 (0x2d9) in the original file.
The file ends with an additional 48 (0x30) bytes starting at offset 1812
00000714 9c ff ff ff ff ff ff ff - Value -100, dunno why (compression bias?)
0000071c 00 00 00 00 00 00 00 00 - ?
00000724 00 f0 3f 00 01 00 00 00 - ?
0000072c 45 03 00 00 00 00 00 00 - Starting offset of previous block, 0x345.
00000734 5d 03 00 00 00 00 00 00 - Starting offset of data block, 0x35d.
0000073c 60 04 00 00 - Inflated data size, 0x460 bytes.
00000740 b7 03 00 00 - Compressed data size, 0x3b7 bytes.
Bastián Díaz
2013-10-04 05:09:23 UTC
Permalink
During the afternoon I was looking at the file sent by Hugo, and apparently he used to repeat several times the case information. I'm no expert, but I have understood that some compression algorithms work by creating chain codes of redundant blocks within a file.
Can it be that the case?

I I have a large file that I can convert with SPSSto zsav file, but first I have to remove some information.

Regards
 
--
Bastián Díaz
Hugo Alejandro
2013-10-04 05:30:34 UTC
Permalink
I can ensure that both files contain the same information. As mentioned
Bastián, I copied and pasted the same information repeatedly inflating the file
size. Perhaps that is the cause of strange behavior.

Tomorrow during the afternoon send another file.

Hugo Valencia
Post by Bastián Díaz
During the afternoon I was looking at the file sent by Hugo, and
apparently he used to repeat several times the case information. I'm no
expert, but I have understood that some compression algorithms work by
creating chain codes of redundant blocks within a file.
Can it be that the case?
I I have a large file that I can convert with SPSSto zsav file, but first
I have to remove some information.
Regards
--
Bastián Díaz
Ben Pfaff
2013-10-04 06:12:04 UTC
Permalink
Post by Hugo Alejandro
I can ensure that both files contain the same information. As
mentioned Basti?n, I copied and pasted the same information repeatedly
inflating the file size. Perhaps that is the cause of strange
behavior.
Thanks for confirming.
Post by Hugo Alejandro
Tomorrow during the afternoon send another file.
That would also be helpful. Thank you.
Ben Pfaff
2013-10-04 06:11:32 UTC
Permalink
Post by Bastián Díaz
I have a large file that I can convert with SPSSto zsav file, but
first I have to remove some information.
Thanks. More files give me a better chance of understanding the
format. I think I'm pretty close.
Bastián Díaz
2013-10-04 18:30:51 UTC
Permalink
Ben, I send the files that I promised.

I hope you find the files useful.

Regards
 
--
Bastián Díaz

Thanks.  More files give me a better chance of understanding the
format.  I think I'm pretty close.



+ https://www.dropbox.com/s/miaoxv4giuuow8d/BDI_II_SAV.sav

+ https://www.dropbox.com/s/tclue1jdarvusar/BDI_II_ZSAV.zsav
Ben Pfaff
2013-10-08 04:58:21 UTC
Permalink
Post by Bastián Díaz
Ben, I send the files that I promised.
I hope you find the files useful.
This file, plus the first small one provided by Hugo, plus the ones that
you uploaded to PSPP-dev-utilities, all have a similar consistent
format. The large one provided by Hugo is different from the rest.

I think I'll work on writing a reader for the format I see for most of
the files. I don't understand the format well enough to read the other
format, and not well enough to make PSPP try to write it, either.

Thanks,

Ben.
Hugo Alejandro
2013-10-08 11:05:57 UTC
Permalink
Ok. Anyway, my apologies for not providing another file.

However, I have written to IBM as a client to know if there is documentation
for data files *.sav and *. zsav. I did the same with Stata (*.dta), and to
my surprise the response was immediate: http://www.stata.com/help.cgi?dta

I hope that IBM be so kind as Stata service.

Hugo Valencia
Post by Ben Pfaff
Post by Bastián Díaz
Ben, I send the files that I promised.
I hope you find the files useful.
This file, plus the first small one provided by Hugo, plus the ones that
you uploaded to PSPP-dev-utilities, all have a similar consistent
format. The large one provided by Hugo is different from the rest.
I think I'll work on writing a reader for the format I see for most of
the files. I don't understand the format well enough to read the other
format, and not well enough to make PSPP try to write it, either.
Thanks,
Ben.
Bastián Díaz
2013-10-08 11:20:56 UTC
Permalink
However, I have written to IBM as a client to know if there is documentation for data files *.sav and
http://www.stata.com/help.cgi?dta
I hope that IBM be so kind as Stata service.
Hugo Valencia
Interestingly, he had managed to get to the same information through a forum. Anyway, not so simple to get to that information.

I hope IBM do the same.

Thanks Hugo.
Ben Pfaff
2013-10-08 06:54:23 UTC
Permalink
000035a 5a 03 00 00 00 00 00 00 - Byte offset of this block, 0x35a
0000362 12 94 03 00 00 00 00 00 - Byte offset of the next block, 0x39412.
000036a 48 00 00 00 00 00 00 00 - Length of next block's header, 0x48 bytes.
...then compressed data, then...
0039412 9c ff ff ff ff ff ff ff - Value -100, dunno why (compression bias?)
003941a 00 00 00 00 00 00 00 00 - ?
0039422 00 f0 3f 00 02 00 00 00 - ?
003942a 5a 03 00 00 00 00 00 00 - Starting offset of previous block, 0x35a.
0039432 72 03 00 00 00 00 00 00 - Starting offset of data block, 0x372.
003943a 00 f0 3f 00 - Inflated data size, 0x3ff000 bytes.
003943e 49 7c 03 00 - Compressed data size, 0x37c49 bytes.
0039442 5a f3 3f 00 00 00 00 00 - 0x3ff35a = 0x35a + 0x3ff000
= current byte offset if no compression
003944a bb 7f 03 00 00 00 00 00 - ?
0039452 00 bf 06 00 - ?
0039456 57 14 00 00 - ?
I figured out part of the mystery. This file contains two compressed
data blocks.

000035a 5a 03 00 00 00 00 00 00 - Byte offset of this block, 0x35a
0000362 12 94 03 00 00 00 00 00 - Byte offset of the next block, 0x39412.
000036a 48 00 00 00 00 00 00 00 - Length of next block's header, 0x48 bytes.

0000372 0x37c49 bytes of compressed data that inflate to 0x3ff000 bytes
0037fbb 0x1457 bytes of compressed data that inflate to 0x6bf00 bytes

0039412 9c ff ff ff ff ff ff ff - Value -100, dunno why (compression bias?)
003941a 00 00 00 00 00 00 00 00 - ?
0039422 00 f0 3f 00 - Inflated data block size
0034926 02 00 00 00 - Number of compressed data blocks
003942a 5a 03 00 00 00 00 00 00 - Starting offset of previous block, 0x35a.

First compressed data block descriptor:
0039432 72 03 00 00 00 00 00 00 - Starting offset of data block, 0x372.
003943a 00 f0 3f 00 - Inflated data size, 0x3ff000 bytes.
003943e 49 7c 03 00 - Compressed data size, 0x37c49 bytes.

Second compressed data block descriptor:
0039442 5a f3 3f 00 00 00 00 00 - 0x3ff35a = 0x35a + 0x3ff000
= starting byte offset of data in block 2 if no zlib compression
003944a bb 7f 03 00 00 00 00 00 - Starting offset of data block, 0x37fbb.
0039452 00 bf 06 00 - Inflated data size, 0x6bf00 bytes.
0039456 57 14 00 00 - Deflated data size, 0x1457 bytes.
Ben Pfaff
2013-10-09 04:29:59 UTC
Permalink
I modified to pspp-dump-sav to interpret the descriptors in zsav
compressed files. So far this modified pspp-dump-sav interprets,
without complaint, all seven of the .zsav files I have.

The next step is to modify the PSPP sav file reader to actually read the
data.

--8<--------------------------cut here-------------------------->8--


diff --git a/utilities/pspp-dump-sav.c b/utilities/pspp-dump-sav.c
index c6b5823..6ca45bc 100644
--- a/utilities/pspp-dump-sav.c
+++ b/utilities/pspp-dump-sav.c
@@ -14,6 +14,34 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */

+/*
+000035a 5a 03 00 00 00 00 00 00 - Byte offset of this block, 0x35a
+0000362 12 94 03 00 00 00 00 00 - Byte offset of the next block, 0x39412.
+000036a 48 00 00 00 00 00 00 00 - Length of next block's header, 0x48 bytes.
+
+0000372 0x37c49 bytes of compressed data that inflate to 0x3ff000 bytes
+0037fbb 0x1457 bytes of compressed data that inflate to 0x6bf00 bytes
+
+0039412 9c ff ff ff ff ff ff ff - Value -100, dunno why (compression bias?)
+003941a 00 00 00 00 00 00 00 00 - ?
+0039422 00 f0 3f 00 - Inflated data block size
+0034926 02 00 00 00 - Number of compressed data blocks
+
+First compressed data block descriptor:
+003942a 5a 03 00 00 00 00 00 00
+ = starting byte offset of data in block 1 if no zlib compression
+0039432 72 03 00 00 00 00 00 00 - Starting offset of data block, 0x372.
+003943a 00 f0 3f 00 - Inflated data size, 0x3ff000 bytes.
+003943e 49 7c 03 00 - Compressed data size, 0x37c49 bytes.
+
+Second compressed data block descriptor:
+0039442 5a f3 3f 00 00 00 00 00 - 0x3ff35a = 0x35a + 0x3ff000
+ = starting byte offset of data in block 2 if no zlib compression
+003944a bb 7f 03 00 00 00 00 00 - Starting offset of data block, 0x37fbb.
+0039452 00 bf 06 00 - Inflated data size, 0x6bf00 bytes.
+0039456 57 14 00 00 - Deflated data size, 0x1457 bytes.
+*/
+
#include <config.h>

#include <ctype.h>
@@ -39,6 +67,13 @@

#define ID_MAX_LEN 64

+enum compression
+ {
+ COMP_NONE,
+ COMP_SIMPLE,
+ COMP_ZLIB
+ };
+
struct sfm_reader
{
const char *file_name;
@@ -52,7 +87,7 @@ struct sfm_reader
enum integer_format integer_format;
enum float_format float_format;

- bool compressed;
+ enum compression compression;
double bias;
};

@@ -87,7 +122,8 @@ static void read_long_string_missing_values (struct sfm_reader *r,
size_t size, size_t count);
static void read_unknown_extension (struct sfm_reader *,
size_t size, size_t count);
-static void read_compressed_data (struct sfm_reader *, int max_cases);
+static void read_simple_compressed_data (struct sfm_reader *, int max_cases);
+static void read_zlib_compressed_data (struct sfm_reader *);

static struct text_record *open_text_record (
struct sfm_reader *, size_t size);
@@ -180,7 +216,7 @@ main (int argc, char *argv[])
r.n_var_widths = 0;
r.allocated_var_widths = 0;
r.var_widths = 0;
- r.compressed = false;
+ r.compression = COMP_NONE;

if (argc - optind > 1)
printf ("Reading \"%s\":\n", r.file_name);
@@ -218,8 +254,13 @@ main (int argc, char *argv[])
(long long int) ftello (r.file),
(long long int) ftello (r.file) + 4);

- if (r.compressed && max_cases > 0)
- read_compressed_data (&r, max_cases);
+ if (r.compression == COMP_SIMPLE)
+ {
+ if (max_cases > 0)
+ read_simple_compressed_data (&r, max_cases);
+ }
+ else if (r.compression == COMP_ZLIB)
+ read_zlib_compressed_data (&r);

fclose (r.file);
}
@@ -245,7 +286,11 @@ read_header (struct sfm_reader *r)
read_string (r, rec_type, sizeof rec_type);
read_string (r, eye_catcher, sizeof eye_catcher);

- if (strcmp ("$FL2", rec_type) != 0)
+ if (!strcmp ("$FL2", rec_type))
+ r->compression = COMP_NONE;
+ else if (!strcmp ("$FL3", rec_type))
+ r->compression = COMP_ZLIB;
+ else
sys_error (r, "This is not an SPSS system file.");

/* Identify integer format. */
@@ -265,7 +310,20 @@ read_header (struct sfm_reader *r)
weight_index = read_int (r);
ncases = read_int (r);

- r->compressed = compressed != 0;
+ if (r->compression == COMP_NONE)
+ {
+ if (compressed == 1)
+ r->compression = COMP_SIMPLE;
+ else if (compressed != 0)
+ sys_error (r, "SAV file header has invalid compression value "
+ "%"PRId32".", compressed);
+ }
+ else
+ {
+ if (compressed != 2)
+ sys_error (r, "ZSAV file header has invalid compression value "
+ "%"PRId32".", compressed);
+ }

/* Identify floating-point format and obtain compression bias. */
read_bytes (r, raw_bias, sizeof raw_bias);
@@ -289,7 +347,12 @@ read_header (struct sfm_reader *r)
printf ("File header record:\n");
printf ("\t%17s: %s\n", "Product name", eye_catcher);
printf ("\t%17s: %"PRId32"\n", "Layout code", layout_code);
- printf ("\t%17s: %"PRId32"\n", "Compressed", compressed);
+ printf ("\t%17s: %"PRId32" (%s)\n", "Compressed",
+ compressed,
+ r->compression == COMP_NONE ? "no compression"
+ : r->compression == COMP_SIMPLE ? "simple compression"
+ : r->compression == COMP_ZLIB ? "ZLIB compression"
+ : "<error>");
printf ("\t%17s: %"PRId32"\n", "Weight index", weight_index);
printf ("\t%17s: %"PRId32"\n", "Number of cases", ncases);
printf ("\t%17s: %g\n", "Compression bias", r->bias);
@@ -1170,7 +1233,7 @@ read_variable_attributes (struct sfm_reader *r, size_t size, size_t count)
}

static void
-read_compressed_data (struct sfm_reader *r, int max_cases)
+read_simple_compressed_data (struct sfm_reader *r, int max_cases)
{
enum { N_OPCODES = 8 };
uint8_t opcodes[N_OPCODES];
@@ -1258,6 +1321,82 @@ read_compressed_data (struct sfm_reader *r, int max_cases)
}
}
}
+
+static void
+read_zlib_compressed_data (struct sfm_reader *r)
+{
+ long long int ofs;
+ long long int this_ofs, next_ofs, next_len;
+ long long int bias, zero;
+ unsigned int block_size, n_blocks;
+ unsigned int i;
+
+ read_int (r);
+ ofs = ftello (r->file);
+ printf ("\n%08llx: ZLIB compressed data header:\n", ofs);
+
+ this_ofs = read_int64 (r);
+ next_ofs = read_int64 (r);
+ next_len = read_int64 (r);
+
+ printf ("\tHeader offset: 0x%llx\n", this_ofs);
+ if (this_ofs != ofs)
+ printf ("\t\t(This was expected to be 0x%llx.)\n", ofs);
+ printf ("\tTrailer offset: 0x%llx\n", next_ofs);
+ printf ("\tTrailer length: %lld\n", next_len);
+ if (next_len < 24 || next_len % 24)
+ printf ("\t\t(Trailer length is not a positive multiple of 24.)\n");
+
+ printf ("\n%08llx: 0x%llx bytes of ZLIB compressed data\n",
+ ofs + 8 * 3, next_ofs - (ofs + 8 * 3));
+
+ skip_bytes (r, next_ofs - (ofs + 8 * 3));
+
+ printf ("\n%08llx: ZLIB compressed data trailer:\n", next_ofs);
+ bias = read_int64 (r);
+ zero = read_int64 (r);
+ block_size = read_int (r);
+ n_blocks = read_int (r);
+ printf ("\tCompression bias: %lld\n", bias);
+ printf ("\tZero: 0x%llx\n", zero);
+ if (zero != 0)
+ printf ("\t\t(This was expected to be 0.)\n");
+ printf ("\tBlock size: 0x%x\n", block_size);
+ if (block_size != 0x3ff000)
+ printf ("\t\t(Block size is ordinarily 0x3ff000.)\n");
+ printf ("\tNumber of blocks: %u\n", n_blocks);
+ if (n_blocks != next_len / 24 - 1)
+ printf ("\t\t(Expected %llu blocks.)\n", next_len / 24 - 1);
+
+ for (i = 0; i < n_blocks; i++)
+ {
+ long long int blockinfo_ofs = ftello (r->file);
+ unsigned long long int uncompressed_ofs = read_int64 (r);
+ unsigned long long int compressed_ofs = read_int64 (r);
+ unsigned int inflated_size = read_int (r);
+ unsigned int deflated_size = read_int (r);
+
+ printf ("\n%08llx: Block info for block %d of %d\n",
+ blockinfo_ofs, i + 1, n_blocks);
+
+ printf ("\tOffset if ZLIB were turned off: 0x%llx\n", uncompressed_ofs);
+ if (i == 0 && uncompressed_ofs != ofs)
+ printf ("\t\t(This was expected to be 0x%llx.)\n", ofs);
+
+ printf ("\tOffset of ZLIB compressed data: 0x%llx\n", compressed_ofs);
+ if (i == 0 && compressed_ofs != ofs + 24)
+ printf ("\t\t(This was expected to be 0x%llx.)\n", ofs + 24);
+
+ printf ("\tDeflated data length: 0x%x\n", deflated_size);
+ if (i == n_blocks - 1 && compressed_ofs + deflated_size != next_ofs)
+ printf ("\t\t(This was expected to be 0x%llx.)\n",
+ next_ofs - deflated_size);
+
+ printf ("\tInflated data length: 0x%x\n", inflated_size);
+ if (i < n_blocks - 1 && inflated_size != block_size)
+ printf ("\t\t(This was expected to be 0x%x.)\n", block_size);
+ }
+}

/* Helpers for reading records that consist of structured text
strings. */
Hugo Alejandro
2013-10-09 12:36:52 UTC
Permalink
Great, I'm surprised your ability to decipher the file type.
When PSPP fully support ZSAV will be a big improvement in handling large
databases.

Thank you

--
Hugo Valencia
Post by Ben Pfaff
I modified to pspp-dump-sav to interpret the descriptors in zsav
compressed files. So far this modified pspp-dump-sav interprets,
without complaint, all seven of the .zsav files I have.
The next step is to modify the PSPP sav file reader to actually read the
data.
--8<--------------------------cut here-------------------------->8--
diff --git a/utilities/pspp-dump-sav.c b/utilities/pspp-dump-sav.c
index c6b5823..6ca45bc 100644
--- a/utilities/pspp-dump-sav.c
+++ b/utilities/pspp-dump-sav.c
@@ -14,6 +14,34 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/*
+000035a 5a 03 00 00 00 00 00 00 - Byte offset of this block, 0x35a
+0000362 12 94 03 00 00 00 00 00 - Byte offset of the next block, 0x39412.
+000036a 48 00 00 00 00 00 00 00 - Length of next block's header, 0x48 bytes.
+
+0000372 0x37c49 bytes of compressed data that inflate to 0x3ff000 bytes
+0037fbb 0x1457 bytes of compressed data that inflate to 0x6bf00 bytes
+
+0039412 9c ff ff ff ff ff ff ff - Value -100, dunno why (compression bias?)
+003941a 00 00 00 00 00 00 00 00 - ?
+0039422 00 f0 3f 00 - Inflated data block size
+0034926 02 00 00 00 - Number of compressed data blocks
+
+003942a 5a 03 00 00 00 00 00 00
+ = starting byte offset of data in block 1 if no zlib compression
+0039432 72 03 00 00 00 00 00 00 - Starting offset of data block, 0x372.
+003943a 00 f0 3f 00 - Inflated data size, 0x3ff000 bytes.
+003943e 49 7c 03 00 - Compressed data size, 0x37c49 bytes.
+
+0039442 5a f3 3f 00 00 00 00 00 - 0x3ff35a = 0x35a + 0x3ff000
+ = starting byte offset of data in block 2 if no zlib compression
+003944a bb 7f 03 00 00 00 00 00 - Starting offset of data block, 0x37fbb.
+0039452 00 bf 06 00 - Inflated data size, 0x6bf00 bytes.
+0039456 57 14 00 00 - Deflated data size, 0x1457 bytes.
+*/
+
#include <config.h>
#include <ctype.h>
@@ -39,6 +67,13 @@
#define ID_MAX_LEN 64
+enum compression
+ {
+ COMP_NONE,
+ COMP_SIMPLE,
+ COMP_ZLIB
+ };
+
struct sfm_reader
{
const char *file_name;
@@ -52,7 +87,7 @@ struct sfm_reader
enum integer_format integer_format;
enum float_format float_format;
- bool compressed;
+ enum compression compression;
double bias;
};
@@ -87,7 +122,8 @@ static void read_long_string_missing_values (struct sfm_reader *r,
size_t size, size_t count);
static void read_unknown_extension (struct sfm_reader *,
size_t size, size_t count);
-static void read_compressed_data (struct sfm_reader *, int max_cases);
+static void read_simple_compressed_data (struct sfm_reader *, int max_cases);
+static void read_zlib_compressed_data (struct sfm_reader *);
static struct text_record *open_text_record (
struct sfm_reader *, size_t size);
@@ -180,7 +216,7 @@ main (int argc, char *argv[])
r.n_var_widths = 0;
r.allocated_var_widths = 0;
r.var_widths = 0;
- r.compressed = false;
+ r.compression = COMP_NONE;
if (argc - optind > 1)
printf ("Reading \"%s\":\n", r.file_name);
@@ -218,8 +254,13 @@ main (int argc, char *argv[])
(long long int) ftello (r.file),
(long long int) ftello (r.file) + 4);
- if (r.compressed && max_cases > 0)
- read_compressed_data (&r, max_cases);
+ if (r.compression == COMP_SIMPLE)
+ {
+ if (max_cases > 0)
+ read_simple_compressed_data (&r, max_cases);
+ }
+ else if (r.compression == COMP_ZLIB)
+ read_zlib_compressed_data (&r);
fclose (r.file);
}
@@ -245,7 +286,11 @@ read_header (struct sfm_reader *r)
read_string (r, rec_type, sizeof rec_type);
read_string (r, eye_catcher, sizeof eye_catcher);
- if (strcmp ("$FL2", rec_type) != 0)
+ if (!strcmp ("$FL2", rec_type))
+ r->compression = COMP_NONE;
+ else if (!strcmp ("$FL3", rec_type))
+ r->compression = COMP_ZLIB;
+ else
sys_error (r, "This is not an SPSS system file.");
/* Identify integer format. */
@@ -265,7 +310,20 @@ read_header (struct sfm_reader *r)
weight_index = read_int (r);
ncases = read_int (r);
- r->compressed = compressed != 0;
+ if (r->compression == COMP_NONE)
+ {
+ if (compressed == 1)
+ r->compression = COMP_SIMPLE;
+ else if (compressed != 0)
+ sys_error (r, "SAV file header has invalid compression value "
+ "%"PRId32".", compressed);
+ }
+ else
+ {
+ if (compressed != 2)
+ sys_error (r, "ZSAV file header has invalid compression value "
+ "%"PRId32".", compressed);
+ }
/* Identify floating-point format and obtain compression bias. */
read_bytes (r, raw_bias, sizeof raw_bias);
@@ -289,7 +347,12 @@ read_header (struct sfm_reader *r)
printf ("File header record:\n");
printf ("\t%17s: %s\n", "Product name", eye_catcher);
printf ("\t%17s: %"PRId32"\n", "Layout code", layout_code);
- printf ("\t%17s: %"PRId32"\n", "Compressed", compressed);
+ printf ("\t%17s: %"PRId32" (%s)\n", "Compressed",
+ compressed,
+ r->compression == COMP_NONE ? "no compression"
+ : r->compression == COMP_SIMPLE ? "simple compression"
+ : r->compression == COMP_ZLIB ? "ZLIB compression"
+ : "<error>");
printf ("\t%17s: %"PRId32"\n", "Weight index", weight_index);
printf ("\t%17s: %"PRId32"\n", "Number of cases", ncases);
printf ("\t%17s: %g\n", "Compression bias", r->bias);
@@ -1170,7 +1233,7 @@ read_variable_attributes (struct sfm_reader *r,
size_t size, size_t count)
}
static void
-read_compressed_data (struct sfm_reader *r, int max_cases)
+read_simple_compressed_data (struct sfm_reader *r, int max_cases)
{
enum { N_OPCODES = 8 };
uint8_t opcodes[N_OPCODES];
@@ -1258,6 +1321,82 @@ read_compressed_data (struct sfm_reader *r, int max_cases)
}
}
}
+
+static void
+read_zlib_compressed_data (struct sfm_reader *r)
+{
+ long long int ofs;
+ long long int this_ofs, next_ofs, next_len;
+ long long int bias, zero;
+ unsigned int block_size, n_blocks;
+ unsigned int i;
+
+ read_int (r);
+ ofs = ftello (r->file);
+ printf ("\n%08llx: ZLIB compressed data header:\n", ofs);
+
+ this_ofs = read_int64 (r);
+ next_ofs = read_int64 (r);
+ next_len = read_int64 (r);
+
+ printf ("\tHeader offset: 0x%llx\n", this_ofs);
+ if (this_ofs != ofs)
+ printf ("\t\t(This was expected to be 0x%llx.)\n", ofs);
+ printf ("\tTrailer offset: 0x%llx\n", next_ofs);
+ printf ("\tTrailer length: %lld\n", next_len);
+ if (next_len < 24 || next_len % 24)
+ printf ("\t\t(Trailer length is not a positive multiple of 24.)\n");
+
+ printf ("\n%08llx: 0x%llx bytes of ZLIB compressed data\n",
+ ofs + 8 * 3, next_ofs - (ofs + 8 * 3));
+
+ skip_bytes (r, next_ofs - (ofs + 8 * 3));
+
+ printf ("\n%08llx: ZLIB compressed data trailer:\n", next_ofs);
+ bias = read_int64 (r);
+ zero = read_int64 (r);
+ block_size = read_int (r);
+ n_blocks = read_int (r);
+ printf ("\tCompression bias: %lld\n", bias);
+ printf ("\tZero: 0x%llx\n", zero);
+ if (zero != 0)
+ printf ("\t\t(This was expected to be 0.)\n");
+ printf ("\tBlock size: 0x%x\n", block_size);
+ if (block_size != 0x3ff000)
+ printf ("\t\t(Block size is ordinarily 0x3ff000.)\n");
+ printf ("\tNumber of blocks: %u\n", n_blocks);
+ if (n_blocks != next_len / 24 - 1)
+ printf ("\t\t(Expected %llu blocks.)\n", next_len / 24 - 1);
+
+ for (i = 0; i < n_blocks; i++)
+ {
+ long long int blockinfo_ofs = ftello (r->file);
+ unsigned long long int uncompressed_ofs = read_int64 (r);
+ unsigned long long int compressed_ofs = read_int64 (r);
+ unsigned int inflated_size = read_int (r);
+ unsigned int deflated_size = read_int (r);
+
+ printf ("\n%08llx: Block info for block %d of %d\n",
+ blockinfo_ofs, i + 1, n_blocks);
+
+ printf ("\tOffset if ZLIB were turned off: 0x%llx\n",
uncompressed_ofs);
+ if (i == 0 && uncompressed_ofs != ofs)
+ printf ("\t\t(This was expected to be 0x%llx.)\n", ofs);
+
+ printf ("\tOffset of ZLIB compressed data: 0x%llx\n",
compressed_ofs);
+ if (i == 0 && compressed_ofs != ofs + 24)
+ printf ("\t\t(This was expected to be 0x%llx.)\n", ofs + 24);
+
+ printf ("\tDeflated data length: 0x%x\n", deflated_size);
+ if (i == n_blocks - 1 && compressed_ofs + deflated_size != next_ofs)
+ printf ("\t\t(This was expected to be 0x%llx.)\n",
+ next_ofs - deflated_size);
+
+ printf ("\tInflated data length: 0x%x\n", inflated_size);
+ if (i < n_blocks - 1 && inflated_size != block_size)
+ printf ("\t\t(This was expected to be 0x%x.)\n", block_size);
+ }
+}
/* Helpers for reading records that consist of structured text
strings. */
Ben Pfaff
2013-10-23 06:08:00 UTC
Permalink
I committed support for the ZSAV format just now. It will be in the
tomorrow morning's daily build.
Hugo Alejandro
2013-10-23 17:10:46 UTC
Permalink
Has great performance. PSPP has full support, or just reading?, I ask
because I have not been able to save a file ZSAV with PSPP (at least own
commands using SPSS).
Also even without integration into the GUI, psppire opens ZSAV files very
quickly.

At least in the tests I've done so far in working with large databases, I
had no problems reading and performance of PSPP is far, far superior to spss
(on the same computer).

Thank you very much, great job.

Hugo
Post by Ben Pfaff
I committed support for the ZSAV format just now. It will be in the
tomorrow morning's daily build.
Ben Pfaff
2013-10-23 17:16:19 UTC
Permalink
Post by Hugo Alejandro
Has great performance. PSPP has full support, or just reading?, I ask
because I have not been able to save a file ZSAV with PSPP (at least own
commands using SPSS).
Saving should work too, with the ZCOMPRESSED option on SAVE and XSAVE.
Is that what you are doing, or do you expect some other way to save to
ZSAV?
Post by Hugo Alejandro
At least in the tests I've done so far in working with large databases, I
had no problems reading and performance of PSPP is far, far superior to spss
(on the same computer).
That is good to know.

Thanks,

Ben.
Hugo Alejandro
2013-10-23 18:50:26 UTC
Permalink
My apologies. I made a procedural error when trying to save a file from a
production job, rather than run the syntax directly.
ZCOMPRESSED command works perfectly.

Are there plans to integrate it into psppire?, My students would appreciate.

Thank you again.

Hugo
Post by Ben Pfaff
Post by Hugo Alejandro
Has great performance. PSPP has full support, or just reading?, I ask
because I have not been able to save a file ZSAV with PSPP (at least own
commands using SPSS).
Saving should work too, with the ZCOMPRESSED option on SAVE and XSAVE.
Is that what you are doing, or do you expect some other way to save to
ZSAV?
Post by Hugo Alejandro
At least in the tests I've done so far in working with large databases, I
had no problems reading and performance of PSPP is far, far superior to
spss
Post by Hugo Alejandro
(on the same computer).
That is good to know.
Thanks,
Ben.
Ben Pfaff
2013-10-24 05:41:49 UTC
Permalink
Post by Hugo Alejandro
Are there plans to integrate it into psppire?, My students would appreciate.
I'm working on that.
Ben Pfaff
2013-10-24 05:58:24 UTC
Permalink
Post by Ben Pfaff
Post by Hugo Alejandro
Are there plans to integrate it into psppire?, My students would appreciate.
I'm working on that.
I came up with the patch below, but I'm too sleepy to verify that it
works and commit it tonight.

--8<--------------------------cut here-------------------------->8--

diff --git a/src/ui/gui/aggregate-dialog.c b/src/ui/gui/aggregate-dialog.c
index 5d477d3..60a8d9f 100644
--- a/src/ui/gui/aggregate-dialog.c
+++ b/src/ui/gui/aggregate-dialog.c
@@ -1,5 +1,5 @@
/* PSPPIRE - a graphical user interface for PSPP.
- Copyright (C) 2010, 2011, 2012 Free Software Foundation
+ Copyright (C) 2010, 2011, 2012, 2013 Free Software Foundation

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
@@ -168,6 +168,11 @@ choose_filename (struct aggregate *fd)
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);

filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name (filter, _("Compressed System Files (*.zsav)"));
+ gtk_file_filter_add_pattern (filter, "*.zsav");
+ gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
+
+ filter = gtk_file_filter_new ();
gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
gtk_file_filter_add_mime_type (filter, "application/x-spss-por");
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
diff --git a/src/ui/gui/main.c b/src/ui/gui/main.c
index eb44e81..6bd6d1b 100644
--- a/src/ui/gui/main.c
+++ b/src/ui/gui/main.c
@@ -1,5 +1,5 @@
/* PSPPIRE - a graphical user interface for PSPP.
- Copyright (C) 2004, 2005, 2006, 2010, 2011, 2012 Free Software Foundation
+ Copyright (C) 2004, 2005, 2006, 2010, 2011, 2012, 2013 Free Software Foundation

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
@@ -113,8 +113,8 @@ Informative output:\n\
-h, --help display this help and exit\n\
-V, --version output version information and exit\n\
\n\
-A non-option argument is interpreted as a .sav file, a .por file or a syntax\n\
-file to load.\n"),
+A non-option argument is interpreted as a data file in .sav or .zsav or .por\n\
+format or a syntax file to load.\n"),
program_name, gtk_help, inc_path);

free (inc_path);
diff --git a/src/ui/gui/psppire-data-window.c b/src/ui/gui/psppire-data-window.c
index e820e9d..f3e85d0 100644
--- a/src/ui/gui/psppire-data-window.c
+++ b/src/ui/gui/psppire-data-window.c
@@ -368,6 +368,17 @@ load_file (PsppireWindow *de, const gchar *file_name, gpointer syn)
return ok;
}

+static const char *
+psppire_data_window_format_to_string (enum PsppireDataWindowFormat format)
+{
+ if (format == PSPPIRE_DATA_WINDOW_SAV)
+ return ".sav";
+ else if (format == PSPPIRE_DATA_WINDOW_ZSAV)
+ return ".zsav";
+ else
+ return ".por";
+}
+
/* Save DE to file */
static void
save_file (PsppireWindow *w)
@@ -384,12 +395,7 @@ save_file (PsppireWindow *w)
fnx = g_string_new (file_name);

if ( ! name_has_suffix (fnx->str))
- {
- if ( de->save_as_portable)
- g_string_append (fnx, ".por");
- else
- g_string_append (fnx, ".sav");
- }
+ g_string_append (fnx, psppire_data_window_format_to_string (de->format));

ds_init_empty (&filename);

@@ -400,9 +406,13 @@ save_file (PsppireWindow *w)
syntax_gen_string (&filename, ss_cstr (utf8_file_name));
g_free (utf8_file_name);

- syntax = g_strdup_printf ("%s OUTFILE=%s.",
- de->save_as_portable ? "EXPORT" : "SAVE",
- ds_cstr (&filename));
+ if (de->format == PSPPIRE_DATA_WINDOW_SAV)
+ syntax = g_strdup_printf ("SAVE OUTFILE=%s.", ds_cstr (&filename));
+ else if (de->format == PSPPIRE_DATA_WINDOW_ZSAV)
+ syntax = g_strdup_printf ("SAVE /ZCOMPRESSED /OUTFILE=%s.",
+ ds_cstr (&filename));
+ else
+ syntax = g_strdup_printf ("EXPORT OUTFILE=%s.", ds_cstr (&filename));

ds_destroy (&filename);

@@ -450,9 +460,11 @@ sysfile_info (PsppireDataWindow *de)
static void
data_pick_filename (PsppireWindow *window)
{
+ GtkListStore *list_store;
+ GtkWidget *combo_box;
+
PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (window);
- GtkFileFilter *filter = gtk_file_filter_new ();
- GtkWidget *button_sys;
+ GtkFileFilter *filter;
GtkWidget *dialog =
gtk_file_chooser_dialog_new (_("Save"),
GTK_WINDOW (de),
@@ -463,11 +475,17 @@ data_pick_filename (PsppireWindow *window)

g_object_set (dialog, "local-only", FALSE, NULL);

+ filter = gtk_file_filter_new ();
gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
gtk_file_filter_add_mime_type (filter, "application/x-spss-sav");
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);

filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name (filter, _("Compressed System Files (*.zsav)"));
+ gtk_file_filter_add_pattern (filter, "*.zsav");
+ gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
+
+ filter = gtk_file_filter_new ();
gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
gtk_file_filter_add_mime_type (filter, "application/x-spss-por");
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
@@ -478,22 +496,46 @@ data_pick_filename (PsppireWindow *window)
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);

{
- GtkWidget *button_por;
- GtkWidget *vbox = gtk_vbox_new (TRUE, 5);
- button_sys =
- gtk_radio_button_new_with_label (NULL, _("System File"));
-
- button_por =
- gtk_radio_button_new_with_label
- (gtk_radio_button_get_group (GTK_RADIO_BUTTON(button_sys)),
- _("Portable File"));
-
- psppire_box_pack_start_defaults (GTK_BOX (vbox), button_sys);
- psppire_box_pack_start_defaults (GTK_BOX (vbox), button_por);
-
- gtk_widget_show_all (vbox);
-
- gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(dialog), vbox);
+ GtkCellRenderer *cell;
+ GtkWidget *label;
+ GtkTreeIter iter;
+ GtkWidget *hbox;
+
+ list_store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
+ combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (list_store));
+
+ gtk_list_store_append (list_store, &iter);
+ gtk_list_store_set (list_store, &iter,
+ 0, PSPPIRE_DATA_WINDOW_SAV,
+ 1, _("System File"),
+ -1);
+ gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo_box), &iter);
+
+ gtk_list_store_append (list_store, &iter);
+ gtk_list_store_set (list_store, &iter,
+ 0, PSPPIRE_DATA_WINDOW_ZSAV,
+ 1, _("Compressed System File"),
+ -1);
+
+ gtk_list_store_append (list_store, &iter);
+ gtk_list_store_set (list_store, &iter,
+ 0, PSPPIRE_DATA_WINDOW_POR,
+ 1, _("Portable File"),
+ -1);
+
+ label = gtk_label_new (_("Format:"));
+
+ cell = gtk_cell_renderer_text_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, FALSE);
+ gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box), cell,
+ "text", 1);
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), combo_box, FALSE, FALSE, 0);
+ gtk_widget_show_all (hbox);
+
+ gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(dialog), hbox);
}

gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
@@ -509,16 +551,16 @@ data_pick_filename (PsppireWindow *window)
gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog))
);

- de->save_as_portable =
- ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_sys));
+ GtkTreeIter iter;
+ int format;
+
+ gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo_box), &iter);
+ gtk_tree_model_get (GTK_TREE_MODEL (list_store), &iter, 0, &format);
+ de->format = format;

if ( ! name_has_suffix (filename->str))
- {
- if ( de->save_as_portable)
- g_string_append (filename, ".por");
- else
- g_string_append (filename, ".sav");
- }
+ g_string_append (filename,
+ psppire_data_window_format_to_string (format));

psppire_window_set_filename (PSPPIRE_WINDOW (de), filename->str);

diff --git a/src/ui/gui/psppire-data-window.h b/src/ui/gui/psppire-data-window.h
index 63510b3..a0e80fc 100644
--- a/src/ui/gui/psppire-data-window.h
+++ b/src/ui/gui/psppire-data-window.h
@@ -1,5 +1,5 @@
/* PSPPIRE - a graphical user interface for PSPP.
- Copyright (C) 2008, 2010, 2011, 2012 Free Software Foundation
+ Copyright (C) 2008, 2010, 2011, 2012, 2013 Free Software Foundation

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
@@ -52,6 +52,12 @@ typedef struct _PsppireDataWindow PsppireDataWindow;
typedef struct _PsppireDataWindowClass PsppireDataWindowClass;


+enum PsppireDataWindowFormat {
+ PSPPIRE_DATA_WINDOW_SAV,
+ PSPPIRE_DATA_WINDOW_ZSAV,
+ PSPPIRE_DATA_WINDOW_POR
+};
+
struct _PsppireDataWindow
{
PsppireWindow parent;
@@ -65,7 +71,7 @@ struct _PsppireDataWindow
struct dataset *dataset;
PsppireDataStore *data_store;

- gboolean save_as_portable;
+ enum PsppireDataWindowFormat format;

struct ll ll; /* In global 'all_data_windows' list. */
unsigned long int lazy_serial;
Ben Pfaff
2013-10-26 04:47:23 UTC
Permalink
Post by Ben Pfaff
Post by Ben Pfaff
Post by Hugo Alejandro
Are there plans to integrate it into psppire?, My students would appreciate.
I'm working on that.
I came up with the patch below, but I'm too sleepy to verify that it
works and commit it tonight.
I tested it, fixed a bug, and pushed it. Now there is a "Format"
dropdown in the "save as" dialog. If you select "Compressed System
File", you get .zsav.
Hugo Alejandro
2013-10-26 05:00:00 UTC
Permalink
Thank you very much. Tomorrow I'll try when the daily snapshot.

Cheers


Hugo
Post by Hugo Alejandro
Post by Ben Pfaff
Post by Ben Pfaff
Post by Hugo Alejandro
Are there plans to integrate it into psppire?, My students would
appreciate.
Post by Ben Pfaff
Post by Ben Pfaff
I'm working on that.
I came up with the patch below, but I'm too sleepy to verify that it
works and commit it tonight.
I tested it, fixed a bug, and pushed it. Now there is a "Format"
dropdown in the "save as" dialog. If you select "Compressed System
File", you get .zsav.
Hugo Alejandro
2013-10-26 16:52:45 UTC
Permalink
Ben, ZSAV file support in saving dialog works well, however, in the file
open dialog, it is not listed and you have to use the "All Files" to choose
them.

Hugo
Post by Hugo Alejandro
Thank you very much. Tomorrow I'll try when the daily snapshot.
Cheers
Hugo
Post by Hugo Alejandro
Post by Ben Pfaff
Post by Ben Pfaff
Post by Hugo Alejandro
Are there plans to integrate it into psppire?, My students would
appreciate.
Post by Ben Pfaff
Post by Ben Pfaff
I'm working on that.
I came up with the patch below, but I'm too sleepy to verify that it
works and commit it tonight.
I tested it, fixed a bug, and pushed it. Now there is a "Format"
dropdown in the "save as" dialog. If you select "Compressed System
File", you get .zsav.
Ben Pfaff
2013-10-26 19:33:39 UTC
Permalink
Thanks for pointing out the omission. I pushed a fix.
Post by Hugo Alejandro
Ben, ZSAV file support in saving dialog works well, however, in the file
open dialog, it is not listed and you have to use the "All Files" to choose
them.
Hugo
Post by Hugo Alejandro
Thank you very much. Tomorrow I'll try when the daily snapshot.
Cheers
Hugo
Post by Hugo Alejandro
Post by Ben Pfaff
Post by Ben Pfaff
Post by Hugo Alejandro
Are there plans to integrate it into psppire?, My students would
appreciate.
Post by Ben Pfaff
Post by Ben Pfaff
I'm working on that.
I came up with the patch below, but I'm too sleepy to verify that it
works and commit it tonight.
I tested it, fixed a bug, and pushed it. Now there is a "Format"
dropdown in the "save as" dialog. If you select "Compressed System
File", you get .zsav.
Hugo Alejandro
2013-10-24 06:12:14 UTC
Permalink
Thanks, I appreciate your work.

Hugo
Post by Hugo Alejandro
Post by Hugo Alejandro
Are there plans to integrate it into psppire?, My students would
appreciate.
I'm working on that.
Loading...