1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2010, 2012, 2014 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "libpspp/zip-writer.h"
20 #include "libpspp/zip-private.h"
29 #include "gl/fwriteerror.h"
30 #include "gl/xalloc.h"
32 #include "libpspp/message.h"
33 #include "libpspp/temp-file.h"
36 #define _(msgid) gettext (msgid)
40 char *file_name; /* File name, for use in error mesages. */
41 FILE *file; /* Output stream. */
42 uint32_t offset; /* Offset in output stream. */
44 uint16_t date, time; /* Date and time in MS-DOS format. */
48 /* Members already added to the file, so that we can summarize them to the
49 central directory at the end of the ZIP file. */
50 struct zip_member *members;
51 size_t n_members, allocated_members;
56 uint32_t offset; /* Starting offset in file. */
57 uint32_t size; /* Length of member file data, in bytes. */
58 uint32_t crc; /* CRC-32 of member file data.. */
59 char *name; /* Name of member file. */
63 put_bytes (struct zip_writer *zw, const void *p, size_t n)
65 fwrite (p, 1, n, zw->file);
70 put_u16 (struct zip_writer *zw, uint16_t x)
72 #ifdef WORDS_BIGENDIAN
75 put_bytes (zw, &x, sizeof x);
79 put_u32 (struct zip_writer *zw, uint32_t x)
81 #ifdef WORDS_BIGENDIAN
84 put_bytes (zw, &x, sizeof x);
87 /* Starts writing a new ZIP file named FILE_NAME. Returns a new zip_writer if
88 successful, otherwise a null pointer. */
90 zip_writer_create (const char *file_name)
92 struct zip_writer *zw;
97 if (strcmp (file_name, "-"))
99 file = fopen (file_name, "wb");
102 msg_error (errno, _("%s: error opening output file"), file_name);
108 if (isatty (STDOUT_FILENO))
110 msg (ME, _("%s: not writing ZIP file to terminal"), file_name);
117 zw = xmalloc (sizeof *zw);
118 zw->file_name = xstrdup (file_name);
125 tm = localtime (&now);
126 zw->date = tm->tm_mday + ((tm->tm_mon + 1) << 5) + ((tm->tm_year - 80) << 9);
127 zw->time = tm->tm_sec / 2 + (tm->tm_min << 5) + (tm->tm_hour << 11);
131 zw->allocated_members = 0;
137 put_local_header (struct zip_writer *zw, const char *member_name, uint32_t crc,
138 uint32_t size, int flag)
140 put_u32 (zw, MAGIC_LHDR); /* local file header signature */
141 put_u16 (zw, 10); /* version needed to extract */
142 put_u16 (zw, flag); /* general purpose bit flag */
143 put_u16 (zw, 0); /* compression method */
144 put_u16 (zw, zw->time); /* last mod file time */
145 put_u16 (zw, zw->date); /* last mod file date */
146 put_u32 (zw, crc); /* crc-32 */
147 put_u32 (zw, size); /* compressed size */
148 put_u32 (zw, size); /* uncompressed size */
149 put_u16 (zw, strlen (member_name)); /* file name length */
150 put_u16 (zw, 0); /* extra field length */
151 put_bytes (zw, member_name, strlen (member_name));
154 /* Adds the contents of FILE, with name MEMBER_NAME, to ZW. */
156 zip_writer_add (struct zip_writer *zw, FILE *file, const char *member_name)
158 struct zip_member *member;
164 /* Local file header. */
165 uint32_t offset = zw->offset;
166 put_local_header (zw, member_name, 0, 0, 1 << 3);
170 fseeko (file, 0, SEEK_SET);
171 while ((bytes_read = fread (buf, 1, sizeof buf, file)) > 0)
173 put_bytes (zw, buf, bytes_read);
175 crc = crc32_update (crc, buf, bytes_read);
178 /* Try to seek back to the local file header. If successful, overwrite it
179 with the correct file size and CRC. Otherwise, write data descriptor. */
180 if (fseeko (zw->file, offset, SEEK_SET) == 0)
182 uint32_t save_offset = zw->offset;
183 put_local_header (zw, member_name, crc, size, 0);
184 if (fseeko (zw->file, size, SEEK_CUR)
187 msg_error (errno, _("%s: error seeking in output file"), zw->file_name);
190 zw->offset = save_offset;
194 put_u32 (zw, MAGIC_DDHD);
200 /* Add to set of members. */
201 if (zw->n_members >= zw->allocated_members)
202 zw->members = x2nrealloc (zw->members, &zw->allocated_members,
203 sizeof *zw->members);
204 member = &zw->members[zw->n_members++];
205 member->offset = offset;
208 member->name = xstrdup (member_name);
211 /* Adds a member named MEMBER_NAME whose contents is the null-terminated string
214 zip_writer_add_string (struct zip_writer *zw, const char *member_name,
217 zip_writer_add_memory (zw, member_name, content, strlen (content));
220 /* Adds a member named MEMBER_NAME whose contents is the SIZE bytes of
223 zip_writer_add_memory (struct zip_writer *zw, const char *member_name,
224 const void *content, size_t size)
226 FILE *fp = create_temp_file ();
229 msg_error (errno, _("error creating temporary file"));
233 fwrite (content, size, 1, fp);
234 zip_writer_add (zw, fp, member_name);
235 close_temp_file (fp);
238 /* Finalizes the contents of ZW and closes it. Returns true if successful,
239 false if a write error occurred while finalizing the file or at any earlier
242 zip_writer_close (struct zip_writer *zw)
244 uint32_t dir_start, dir_end;
251 dir_start = zw->offset;
252 for (i = 0; i < zw->n_members; i++)
254 struct zip_member *m = &zw->members[i];
256 /* Central directory file header. */
257 put_u32 (zw, MAGIC_SOCD); /* central file header signature */
258 put_u16 (zw, 63); /* version made by */
259 put_u16 (zw, 10); /* version needed to extract */
260 put_u16 (zw, 1 << 3); /* general purpose bit flag */
261 put_u16 (zw, 0); /* compression method */
262 put_u16 (zw, zw->time); /* last mod file time */
263 put_u16 (zw, zw->date); /* last mod file date */
264 put_u32 (zw, m->crc); /* crc-32 */
265 put_u32 (zw, m->size); /* compressed size */
266 put_u32 (zw, m->size); /* uncompressed size */
267 put_u16 (zw, strlen (m->name)); /* file name length */
268 put_u16 (zw, 0); /* extra field length */
269 put_u16 (zw, 0); /* file comment length */
270 put_u16 (zw, 0); /* disk number start */
271 put_u16 (zw, 0); /* internal file attributes */
272 put_u32 (zw, 0); /* external file attributes */
273 put_u32 (zw, m->offset); /* relative offset of local header */
274 put_bytes (zw, m->name, strlen (m->name));
278 dir_end = zw->offset;
280 /* End of central directory record. */
281 put_u32 (zw, MAGIC_EOCD); /* end of central dir signature */
282 put_u16 (zw, 0); /* number of this disk */
283 put_u16 (zw, 0); /* number of the disk with the
284 start of the central directory */
285 put_u16 (zw, zw->n_members); /* total number of entries in the
286 central directory on this disk */
287 put_u16 (zw, zw->n_members); /* total number of entries in
288 the central directory */
289 put_u32 (zw, dir_end - dir_start); /* size of the central directory */
290 put_u32 (zw, dir_start); /* offset of start of central
291 directory with respect to
292 the starting disk number */
293 put_u16 (zw, 0); /* .ZIP file comment length */
296 if (ok && zw->file != stdout && fwriteerror (zw->file))
298 msg_error (errno, _("%s: write failed"), zw->file_name);
302 free (zw->file_name);