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
);