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"
30 #include "gl/fwriteerror.h"
31 #include "gl/xalloc.h"
33 #include "libpspp/message.h"
34 #include "libpspp/temp-file.h"
37 #define _(msgid) gettext (msgid)
41 char *file_name; /* File name, for use in error mesages. */
42 FILE *file; /* Output stream. */
43 uint32_t offset; /* Offset in output stream. */
45 uint16_t date, time; /* Date and time in MS-DOS format. */
49 /* Member being added to the file. */
55 /* Members already added to the file, so that we can summarize them to the
56 central directory at the end of the ZIP file. */
57 struct zip_member *members;
58 size_t n_members, allocated_members;
63 uint32_t offset; /* Starting offset in file. */
64 uint32_t size; /* Length of member file data, in bytes. */
65 uint32_t crc; /* CRC-32 of member file data.. */
66 char *name; /* Name of member file. */
70 put_bytes (struct zip_writer *zw, const void *p, size_t n)
72 fwrite (p, 1, n, zw->file);
77 put_u16 (struct zip_writer *zw, uint16_t x)
79 #ifdef WORDS_BIGENDIAN
82 put_bytes (zw, &x, sizeof x);
86 put_u32 (struct zip_writer *zw, uint32_t x)
88 #ifdef WORDS_BIGENDIAN
91 put_bytes (zw, &x, sizeof x);
94 /* Starts writing a new ZIP file named FILE_NAME. Returns a new zip_writer if
95 successful, otherwise a null pointer. */
97 zip_writer_create (const char *file_name)
100 if (strcmp (file_name, "-"))
102 file = fopen (file_name, "wb");
105 msg_error (errno, _("%s: error opening output file"), file_name);
111 if (isatty (STDOUT_FILENO))
113 msg (ME, _("%s: not writing ZIP file to terminal"), file_name);
120 time_t now = time (NULL);
121 struct tm *tm = localtime (&now);
123 struct zip_writer *zw = xmalloc (sizeof *zw);
124 *zw = (struct zip_writer) {
125 .file_name = xstrdup (file_name),
128 .date = tm->tm_mday + ((tm->tm_mon + 1) << 5) + ((tm->tm_year - 80) << 9),
129 .time = tm->tm_sec / 2 + (tm->tm_min << 5) + (tm->tm_hour << 11),
135 put_local_header (struct zip_writer *zw, const char *member_name, uint32_t crc,
136 uint32_t size, int flag)
138 put_u32 (zw, MAGIC_LHDR); /* local file header signature */
139 put_u16 (zw, 10); /* version needed to extract */
140 put_u16 (zw, flag); /* general purpose bit flag */
141 put_u16 (zw, 0); /* compression method */
142 put_u16 (zw, zw->time); /* last mod file time */
143 put_u16 (zw, zw->date); /* last mod file date */
144 put_u32 (zw, crc); /* crc-32 */
145 put_u32 (zw, size); /* compressed size */
146 put_u32 (zw, size); /* uncompressed size */
147 put_u16 (zw, strlen (member_name)); /* file name length */
148 put_u16 (zw, 0); /* extra field length */
149 put_bytes (zw, member_name, strlen (member_name));
152 /* Start adding a new member, named MEMBER_NAME, to ZW. Add content to the
153 member by calling zip_writer_add_write() possibly multiple times, then
154 finish it off with zip_writer_add_finish().
156 Only one member may be open at a time. */
158 zip_writer_add_start (struct zip_writer *zw, const char *member_name)
160 assert (!zw->m_name);
161 zw->m_name = xstrdup (member_name);
162 zw->m_start = zw->offset;
165 put_local_header (zw, member_name, 0, 0, 1 << 3);
168 /* Adds the N bytes in BUF to the currently open member in ZW. */
170 zip_writer_add_write (struct zip_writer *zw, const void *buf, size_t n)
173 put_bytes (zw, buf, n);
175 zw->m_crc = crc32_update (zw->m_crc, buf, n);
178 /* Finishes writing the currently open member in ZW. */
180 zip_writer_add_finish (struct zip_writer *zw)
184 /* Try to seek back to the local file header. If successful, overwrite it
185 with the correct file size and CRC. Otherwise, write data descriptor. */
186 if (fseeko (zw->file, zw->m_start, SEEK_SET) == 0)
188 uint32_t save_offset = zw->offset;
189 put_local_header (zw, zw->m_name, zw->m_crc, zw->m_size, 0);
190 if (fseeko (zw->file, zw->m_size, SEEK_CUR)
193 msg_error (errno, _("%s: error seeking in output file"), zw->file_name);
196 zw->offset = save_offset;
200 put_u32 (zw, MAGIC_DDHD);
201 put_u32 (zw, zw->m_crc);
202 put_u32 (zw, zw->m_size);
203 put_u32 (zw, zw->m_size);
206 /* Add to set of members. */
207 if (zw->n_members >= zw->allocated_members)
208 zw->members = x2nrealloc (zw->members, &zw->allocated_members,
209 sizeof *zw->members);
210 struct zip_member *member = &zw->members[zw->n_members++];
211 member->offset = zw->m_start;
212 member->size = zw->m_size;
213 member->crc = zw->m_crc;
214 member->name = zw->m_name;
217 zw->m_start = zw->m_size = zw->m_crc = 0;
220 /* Adds the contents of FILE, with name MEMBER_NAME, to ZW. */
222 zip_writer_add (struct zip_writer *zw, FILE *file, const char *member_name)
224 zip_writer_add_start (zw, member_name);
226 fseeko (file, 0, SEEK_SET);
230 size_t n = fread (buf, 1, sizeof buf, file);
233 zip_writer_add_write (zw, buf, n);
235 zip_writer_add_finish (zw);
238 /* Adds a member named MEMBER_NAME whose contents is the null-terminated string
241 zip_writer_add_string (struct zip_writer *zw, const char *member_name,
244 zip_writer_add_memory (zw, member_name, content, strlen (content));
247 /* Adds a member named MEMBER_NAME whose contents is the SIZE bytes of
250 zip_writer_add_memory (struct zip_writer *zw, const char *member_name,
251 const void *content, size_t size)
253 zip_writer_add_start (zw, member_name);
254 zip_writer_add_write (zw, content, size);
255 zip_writer_add_finish (zw);
258 /* Finalizes the contents of ZW and closes it. Returns true if successful,
259 false if a write error occurred while finalizing the file or at any earlier
262 zip_writer_close (struct zip_writer *zw)
264 uint32_t dir_start, dir_end;
271 assert (!zw->m_name);
273 dir_start = zw->offset;
274 for (i = 0; i < zw->n_members; i++)
276 struct zip_member *m = &zw->members[i];
278 /* Central directory file header. */
279 put_u32 (zw, MAGIC_SOCD); /* central file header signature */
280 put_u16 (zw, 63); /* version made by */
281 put_u16 (zw, 10); /* version needed to extract */
282 put_u16 (zw, 1 << 3); /* general purpose bit flag */
283 put_u16 (zw, 0); /* compression method */
284 put_u16 (zw, zw->time); /* last mod file time */
285 put_u16 (zw, zw->date); /* last mod file date */
286 put_u32 (zw, m->crc); /* crc-32 */
287 put_u32 (zw, m->size); /* compressed size */
288 put_u32 (zw, m->size); /* uncompressed size */
289 put_u16 (zw, strlen (m->name)); /* file name length */
290 put_u16 (zw, 0); /* extra field length */
291 put_u16 (zw, 0); /* file comment length */
292 put_u16 (zw, 0); /* disk number start */
293 put_u16 (zw, 0); /* internal file attributes */
294 put_u32 (zw, 0); /* external file attributes */
295 put_u32 (zw, m->offset); /* relative offset of local header */
296 put_bytes (zw, m->name, strlen (m->name));
300 dir_end = zw->offset;
302 /* End of central directory record. */
303 put_u32 (zw, MAGIC_EOCD); /* end of central dir signature */
304 put_u16 (zw, 0); /* number of this disk */
305 put_u16 (zw, 0); /* number of the disk with the
306 start of the central directory */
307 put_u16 (zw, zw->n_members); /* total number of entries in the
308 central directory on this disk */
309 put_u16 (zw, zw->n_members); /* total number of entries in
310 the central directory */
311 put_u32 (zw, dir_end - dir_start); /* size of the central directory */
312 put_u32 (zw, dir_start); /* offset of start of central
313 directory with respect to
314 the starting disk number */
315 put_u16 (zw, 0); /* .ZIP file comment length */
318 if (ok && zw->file != stdout && fwriteerror (zw->file))
320 msg_error (errno, _("%s: write failed"), zw->file_name);
324 free (zw->file_name);