c-strtod \
close \
count-one-bits \
+ crc \
crypto/md4 \
dirname \
environ \
src/libpspp/tmpfile.h \
src/libpspp/tower.c \
src/libpspp/tower.h \
- src/libpspp/version.h
+ src/libpspp/version.h \
+ src/libpspp/zip-writer.c \
+ src/libpspp/zip-writer.h
DISTCLEANFILES+=src/libpspp/version.c
--- /dev/null
+/* PSPP - a program for statistical analysis.
+ Copyright (C) 2010 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include "libpspp/zip-writer.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "libpspp/integer-format.h"
+
+#include "gl/crc.h"
+#include "gl/error.h"
+#include "gl/fwriteerror.h"
+#include "gl/xalloc.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+struct zip_writer
+ {
+ char *file_name; /* File name, for use in error mesages. */
+ FILE *file; /* Output stream. */
+
+ uint16_t date, time; /* Date and time in MS-DOS format. */
+
+ /* Members already added to the file, so that we can summarize them to the
+ central directory at the end of the ZIP file. */
+ struct zip_member *members;
+ size_t n_members, allocated_members;
+ };
+
+struct zip_member
+ {
+ uint32_t offset; /* Starting offset in file. */
+ uint32_t size; /* Length of member file data, in bytes. */
+ uint32_t crc; /* CRC-32 of member file data.. */
+ char *name; /* Name of member file. */
+ };
+
+static void
+put_bytes (struct zip_writer *zw, const void *p, size_t n)
+{
+ fwrite (p, 1, n, zw->file);
+}
+
+static void
+put_u16 (struct zip_writer *zw, uint16_t x)
+{
+ if (INTEGER_NATIVE != INTEGER_LSB_FIRST)
+ integer_convert (INTEGER_NATIVE, &x, INTEGER_MSB_FIRST, &x, sizeof x);
+ put_bytes (zw, &x, sizeof x);
+}
+
+static void
+put_u32 (struct zip_writer *zw, uint32_t x)
+{
+ if (INTEGER_NATIVE != INTEGER_LSB_FIRST)
+ integer_convert (INTEGER_NATIVE, &x, INTEGER_MSB_FIRST, &x, sizeof x);
+ put_bytes (zw, &x, sizeof x);
+}
+
+/* Starts writing a new ZIP file named FILE_NAME. Returns a new zip_writer if
+ successful, otherwise a null pointer. */
+struct zip_writer *
+zip_writer_create (const char *file_name)
+{
+ struct zip_writer *zw;
+ struct tm *tm;
+ time_t now;
+ FILE *file;
+
+ file = fopen (file_name, "wb");
+ if (file == NULL)
+ {
+ error (0, errno, _("%s: error opening output file"), file_name);
+ return NULL;
+ }
+
+ zw = xmalloc (sizeof *zw);
+ zw->file_name = xstrdup (file_name);
+ zw->file = file;
+
+ now = time (NULL);
+ tm = localtime (&now);
+ zw->date = tm->tm_mday + ((tm->tm_mon + 1) << 5) + ((tm->tm_year - 80) << 9);
+ zw->time = tm->tm_sec / 2 + (tm->tm_min << 5) + (tm->tm_hour << 11);
+
+ zw->members = NULL;
+ zw->n_members = 0;
+ zw->allocated_members = 0;
+
+ return zw;
+}
+
+/* Adds the contents of FILE, with name MEMBER_NAME, to ZW. */
+void
+zip_writer_add (struct zip_writer *zw, FILE *file, const char *member_name)
+{
+ struct zip_member *member;
+ uint32_t offset, size;
+ size_t bytes_read;
+ uint32_t crc;
+ char buf[4096];
+
+ /* Local file header. */
+ offset = ftell (zw->file);
+ put_u32 (zw, 0x04034b50); /* local file header signature */
+ put_u16 (zw, 10); /* version needed to extract */
+ put_u16 (zw, 1 << 3); /* general purpose bit flag */
+ put_u16 (zw, 0); /* compression method */
+ put_u16 (zw, zw->time); /* last mod file time */
+ put_u16 (zw, zw->date); /* last mod file date */
+ put_u32 (zw, 0); /* crc-32 */
+ put_u32 (zw, 0); /* compressed size */
+ put_u32 (zw, 0); /* uncompressed size */
+ put_u16 (zw, strlen (member_name)); /* file name length */
+ put_u16 (zw, 0); /* extra field length */
+ put_bytes (zw, member_name, strlen (member_name));
+
+ /* File data. */
+ size = crc = 0;
+ fseek (file, 0, SEEK_SET);
+ while ((bytes_read = fread (buf, 1, sizeof buf, file)) > 0)
+ {
+ put_bytes (zw, buf, bytes_read);
+ size += bytes_read;
+ crc = crc32_update (crc, buf, bytes_read);
+ }
+
+ /* Data descriptor. */
+ put_u32 (zw, 0x08074b50);
+ put_u32 (zw, crc);
+ put_u32 (zw, size);
+ put_u32 (zw, size);
+
+ /* Add to set of members. */
+ if (zw->n_members >= zw->allocated_members)
+ zw->members = x2nrealloc (zw->members, &zw->allocated_members,
+ sizeof *zw->members);
+ member = &zw->members[zw->n_members++];
+ member->offset = offset;
+ member->size = size;
+ member->crc = crc;
+ member->name = xstrdup (member_name);
+}
+
+/* Finalizes the contents of ZW and closes it. Returns true if successful,
+ false if a write error occurred while finalizing the file or at any earlier
+ time. */
+bool
+zip_writer_close (struct zip_writer *zw)
+{
+ uint32_t dir_start, dir_end;
+ size_t i;
+ bool ok;
+
+ if (zw == NULL)
+ return true;
+
+ dir_start = ftell (zw->file);
+ for (i = 0; i < zw->n_members; i++)
+ {
+ struct zip_member *m = &zw->members[i];
+
+ /* Central directory file header. */
+ put_u32 (zw, 0x02014b50); /* central file header signature */
+ put_u16 (zw, 63); /* version made by */
+ put_u16 (zw, 10); /* version needed to extract */
+ put_u16 (zw, 1 << 3); /* general purpose bit flag */
+ put_u16 (zw, 0); /* compression method */
+ put_u16 (zw, zw->time); /* last mod file time */
+ put_u16 (zw, zw->date); /* last mod file date */
+ put_u32 (zw, m->crc); /* crc-32 */
+ put_u32 (zw, m->size); /* compressed size */
+ put_u32 (zw, m->size); /* uncompressed size */
+ put_u16 (zw, strlen (m->name)); /* file name length */
+ put_u16 (zw, 0); /* extra field length */
+ put_u16 (zw, 0); /* file comment length */
+ put_u16 (zw, 0); /* disk number start */
+ put_u16 (zw, 0); /* internal file attributes */
+ put_u32 (zw, 0); /* external file attributes */
+ put_u32 (zw, m->offset); /* relative offset of local header */
+ put_bytes (zw, m->name, strlen (m->name));
+ free (m->name);
+ }
+ free (zw->members);
+ dir_end = ftell (zw->file);
+
+ /* End of central directory record. */
+ put_u32 (zw, 0x06054b50); /* end of central dir signature */
+ put_u16 (zw, 0); /* number of this disk */
+ put_u16 (zw, 0); /* number of the disk with the
+ start of the central directory */
+ put_u16 (zw, zw->n_members); /* total number of entries in the
+ central directory on this disk */
+ put_u16 (zw, zw->n_members); /* total number of entries in
+ the central directory */
+ put_u32 (zw, dir_end - dir_start); /* size of the central directory */
+ put_u32 (zw, dir_start); /* offset of start of central
+ directory with respect to
+ the starting disk number */
+ put_u16 (zw, 0); /* .ZIP file comment length */
+
+ if (!fwriteerror (zw->file))
+ ok = true;
+ else
+ {
+ error (0, errno, _("%s: write failed"), zw->file_name);
+ ok = false;
+ }
+
+ free (zw->file_name);
+ free (zw);
+
+ return ok;
+}
--- /dev/null
+/* PSPP - a program for statistical analysis.
+ Copyright (C) 2010 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef LIBPSPP_ZIP_WRITER_H
+#define LIBPSPP_ZIP_WRITER_H 1
+
+#include <stdbool.h>
+#include <stdio.h>
+
+struct zip_writer *zip_writer_create (const char *file_name);
+void zip_writer_add (struct zip_writer *, FILE *, const char *member_name);
+bool zip_writer_close (struct zip_writer *);
+
+#endif /* libpspp/zip-writer.h */
#include "libpspp/cast.h"
#include "libpspp/str.h"
#include "libpspp/version.h"
+#include "libpspp/zip-writer.h"
#include "output/driver-provider.h"
#include "output/message-item.h"
#include "output/options.h"
{
struct output_driver driver;
+ struct zip_writer *zip; /* ZIP file writer. */
char *file_name; /* Output file name. */
- bool debug;
- /* The name of the temporary directory used to construct the ODF */
- char *dirname;
+ /* content.xml */
+ xmlTextWriterPtr content_wtr; /* XML writer. */
+ FILE *content_file; /* Temporary file. */
- /* Writer for the content.xml file */
- xmlTextWriterPtr content_wtr;
-
- /* Writer fot the manifest.xml file */
- xmlTextWriterPtr manifest_wtr;
+ /* manifest.xml */
+ xmlTextWriterPtr manifest_wtr; /* XML writer. */
+ FILE *manifest_file; /* Temporary file. */
/* Number of tables so far. */
int table_num;
/* Create the "mimetype" file needed by ODF */
static bool
-create_mimetype (const char *dirname)
+create_mimetype (struct zip_writer *zip)
{
FILE *fp;
- struct string filename;
- ds_init_cstr (&filename, dirname);
- ds_put_cstr (&filename, "/mimetype");
- fp = fopen (ds_cstr (&filename), "w");
+ fp = tmpfile ();
if (fp == NULL)
{
- error (0, errno, _("error opening output file \"%s\""),
- ds_cstr (&filename));
- ds_destroy (&filename);
+ error (0, errno, _("error creating temporary file"));
return false;
}
- ds_destroy (&filename);
fprintf (fp, "application/vnd.oasis.opendocument.text");
+ zip_writer_add (zip, fp, "mimetype");
fclose (fp);
return true;
}
-/* Create a new XML file called FILENAME in the temp directory, and return a writer for it */
-static xmlTextWriterPtr
-create_writer (const struct odt_driver *driver, const char *filename)
+/* Creates a new temporary file and stores it in *FILE, then creates an XML
+ writer for it and stores it in *W. */
+static void
+create_writer (FILE **file, xmlTextWriterPtr *w)
{
- char *copy = NULL;
- xmlTextWriterPtr w;
- struct string str;
- ds_init_cstr (&str, driver->dirname);
- ds_put_cstr (&str, "/");
- ds_put_cstr (&str, filename);
-
- /* dirname modifies its argument, so we must copy it */
- copy = xstrdup (ds_cstr (&str));
- mkdir (dirname (copy), 0700);
- free (copy);
-
- w = xmlNewTextWriterFilename (ds_cstr (&str), 0);
-
- ds_destroy (&str);
-
- xmlTextWriterStartDocument (w, NULL, "UTF-8", NULL);
+ /* XXX this can fail */
+ *file = tmpfile ();
+ *w = xmlNewTextWriter (xmlOutputBufferCreateFile (*file, NULL));
- return w;
+ xmlTextWriterStartDocument (*w, NULL, "UTF-8", NULL);
}
static void
write_style_data (struct odt_driver *odt)
{
- xmlTextWriterPtr w = create_writer (odt, "styles.xml");
+ xmlTextWriterPtr w;
+ FILE *file;
+
+ create_writer (&file, &w);
register_file (odt, "styles.xml");
xmlTextWriterStartElement (w, _xml ("office:document-styles"));
xmlTextWriterEndDocument (w);
xmlFreeTextWriter (w);
+ zip_writer_add (odt->zip, file, "styles.xml");
+ fclose (file);
}
static void
write_meta_data (struct odt_driver *odt)
{
- xmlTextWriterPtr w = create_writer (odt, "meta.xml");
+ xmlTextWriterPtr w;
+ FILE *file;
+
+ create_writer (&file, &w);
register_file (odt, "meta.xml");
xmlTextWriterStartElement (w, _xml ("office:document-meta"));
xmlTextWriterEndElement (w);
xmlTextWriterEndDocument (w);
xmlFreeTextWriter (w);
-}
-
-enum
-{
- output_file_arg,
- boolean_arg,
-};
-
-static struct driver_option *
-opt (struct output_driver *d, struct string_map *options, const char *key,
- const char *default_value)
-{
- return driver_option_get (d, options, key, default_value);
+ zip_writer_add (odt->zip, file, "meta.xml");
+ fclose (file);
}
static struct output_driver *
odt_create (const char *file_name, enum settings_output_devices device_type,
- struct string_map *o)
+ struct string_map *o UNUSED)
{
struct output_driver *d;
struct odt_driver *odt;
+ struct zip_writer *zip;
+
+ zip = zip_writer_create (file_name);
+ if (zip == NULL)
+ return NULL;
odt = xzalloc (sizeof *odt);
d = &odt->driver;
output_driver_init (d, &odt_driver_class, file_name, device_type);
+ odt->zip = zip;
odt->file_name = xstrdup (file_name);
- odt->debug = parse_boolean (opt (d, o, "debug", "false"));
-
- odt->dirname = xstrdup ("odt-XXXXXX");
- mkdtemp (odt->dirname);
- if (!create_mimetype (odt->dirname))
+ if (!create_mimetype (zip))
{
output_driver_destroy (d);
return NULL;
}
/* Create the manifest */
- odt->manifest_wtr = create_writer (odt, "META-INF/manifest.xml");
+ create_writer (&odt->manifest_file, &odt->manifest_wtr);
xmlTextWriterStartElement (odt->manifest_wtr, _xml("manifest:manifest"));
xmlTextWriterWriteAttribute (odt->manifest_wtr, _xml("xmlns:manifest"),
write_meta_data (odt);
write_style_data (odt);
- odt->content_wtr = create_writer (odt, "content.xml");
+ create_writer (&odt->content_file, &odt->content_wtr);
register_file (odt, "content.xml");
xmlTextWriterEndElement (odt->manifest_wtr);
xmlTextWriterEndDocument (odt->manifest_wtr);
xmlFreeTextWriter (odt->manifest_wtr);
+ zip_writer_add (odt->zip, odt->manifest_file, "META-INF/manifest.xml");
+ fclose (odt->manifest_file);
return d;
}
if (odt->content_wtr != NULL)
{
- struct string zip_cmd;
-
xmlTextWriterEndElement (odt->content_wtr); /* office:text */
xmlTextWriterEndElement (odt->content_wtr); /* office:body */
xmlTextWriterEndElement (odt->content_wtr); /* office:document-content */
xmlTextWriterEndDocument (odt->content_wtr);
xmlFreeTextWriter (odt->content_wtr);
+ zip_writer_add (odt->zip, odt->content_file, "content.xml");
+ fclose (odt->content_file);
- /* Zip up the directory */
- ds_init_empty (&zip_cmd);
- ds_put_format (&zip_cmd,
- "cd %s ; rm -f ../%s; zip -q -X ../%s mimetype; zip -q -X -u -r ../%s .",
- odt->dirname, odt->file_name, odt->file_name, odt->file_name);
- system (ds_cstr (&zip_cmd));
- ds_destroy (&zip_cmd);
+ zip_writer_close (odt->zip);
}
-
- if ( !odt->debug )
- {
- /* Remove the temp dir */
- struct string rm_cmd;
-
- ds_init_empty (&rm_cmd);
- ds_put_format (&rm_cmd, "rm -r %s", odt->dirname);
- system (ds_cstr (&rm_cmd));
- ds_destroy (&rm_cmd);
- }
- else
- fprintf (stderr, "Not removing directory %s\n", odt->dirname);
-
+
free (odt->command_name);
- free (odt->dirname);
free (odt);
}