1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2010 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"
25 #include "libpspp/integer-format.h"
29 #include "gl/fwriteerror.h"
30 #include "gl/xalloc.h"
33 #define _(msgid) gettext (msgid)
37 char *file_name; /* File name, for use in error mesages. */
38 FILE *file; /* Output stream. */
40 uint16_t date, time; /* Date and time in MS-DOS format. */
42 /* Members already added to the file, so that we can summarize them to the
43 central directory at the end of the ZIP file. */
44 struct zip_member *members;
45 size_t n_members, allocated_members;
50 uint32_t offset; /* Starting offset in file. */
51 uint32_t size; /* Length of member file data, in bytes. */
52 uint32_t crc; /* CRC-32 of member file data.. */
53 char *name; /* Name of member file. */
57 put_bytes (struct zip_writer *zw, const void *p, size_t n)
59 fwrite (p, 1, n, zw->file);
63 put_u16 (struct zip_writer *zw, uint16_t x)
65 if (INTEGER_NATIVE != INTEGER_LSB_FIRST)
66 integer_convert (INTEGER_NATIVE, &x, INTEGER_MSB_FIRST, &x, sizeof x);
67 put_bytes (zw, &x, sizeof x);
71 put_u32 (struct zip_writer *zw, uint32_t x)
73 if (INTEGER_NATIVE != INTEGER_LSB_FIRST)
74 integer_convert (INTEGER_NATIVE, &x, INTEGER_MSB_FIRST, &x, sizeof x);
75 put_bytes (zw, &x, sizeof x);
78 /* Starts writing a new ZIP file named FILE_NAME. Returns a new zip_writer if
79 successful, otherwise a null pointer. */
81 zip_writer_create (const char *file_name)
83 struct zip_writer *zw;
88 file = fopen (file_name, "wb");
91 error (0, errno, _("%s: error opening output file"), file_name);
95 zw = xmalloc (sizeof *zw);
96 zw->file_name = xstrdup (file_name);
100 tm = localtime (&now);
101 zw->date = tm->tm_mday + ((tm->tm_mon + 1) << 5) + ((tm->tm_year - 80) << 9);
102 zw->time = tm->tm_sec / 2 + (tm->tm_min << 5) + (tm->tm_hour << 11);
106 zw->allocated_members = 0;
111 /* Adds the contents of FILE, with name MEMBER_NAME, to ZW. */
113 zip_writer_add (struct zip_writer *zw, FILE *file, const char *member_name)
115 struct zip_member *member;
116 uint32_t offset, size;
121 /* Local file header. */
122 offset = ftello (zw->file);
123 put_u32 (zw, 0x04034b50); /* local file header signature */
124 put_u16 (zw, 10); /* version needed to extract */
125 put_u16 (zw, 1 << 3); /* general purpose bit flag */
126 put_u16 (zw, 0); /* compression method */
127 put_u16 (zw, zw->time); /* last mod file time */
128 put_u16 (zw, zw->date); /* last mod file date */
129 put_u32 (zw, 0); /* crc-32 */
130 put_u32 (zw, 0); /* compressed size */
131 put_u32 (zw, 0); /* uncompressed size */
132 put_u16 (zw, strlen (member_name)); /* file name length */
133 put_u16 (zw, 0); /* extra field length */
134 put_bytes (zw, member_name, strlen (member_name));
138 fseeko (file, 0, SEEK_SET);
139 while ((bytes_read = fread (buf, 1, sizeof buf, file)) > 0)
141 put_bytes (zw, buf, bytes_read);
143 crc = crc32_update (crc, buf, bytes_read);
146 /* Data descriptor. */
147 put_u32 (zw, 0x08074b50);
152 /* Add to set of members. */
153 if (zw->n_members >= zw->allocated_members)
154 zw->members = x2nrealloc (zw->members, &zw->allocated_members,
155 sizeof *zw->members);
156 member = &zw->members[zw->n_members++];
157 member->offset = offset;
160 member->name = xstrdup (member_name);
163 /* Finalizes the contents of ZW and closes it. Returns true if successful,
164 false if a write error occurred while finalizing the file or at any earlier
167 zip_writer_close (struct zip_writer *zw)
169 uint32_t dir_start, dir_end;
176 dir_start = ftello (zw->file);
177 for (i = 0; i < zw->n_members; i++)
179 struct zip_member *m = &zw->members[i];
181 /* Central directory file header. */
182 put_u32 (zw, 0x02014b50); /* central file header signature */
183 put_u16 (zw, 63); /* version made by */
184 put_u16 (zw, 10); /* version needed to extract */
185 put_u16 (zw, 1 << 3); /* general purpose bit flag */
186 put_u16 (zw, 0); /* compression method */
187 put_u16 (zw, zw->time); /* last mod file time */
188 put_u16 (zw, zw->date); /* last mod file date */
189 put_u32 (zw, m->crc); /* crc-32 */
190 put_u32 (zw, m->size); /* compressed size */
191 put_u32 (zw, m->size); /* uncompressed size */
192 put_u16 (zw, strlen (m->name)); /* file name length */
193 put_u16 (zw, 0); /* extra field length */
194 put_u16 (zw, 0); /* file comment length */
195 put_u16 (zw, 0); /* disk number start */
196 put_u16 (zw, 0); /* internal file attributes */
197 put_u32 (zw, 0); /* external file attributes */
198 put_u32 (zw, m->offset); /* relative offset of local header */
199 put_bytes (zw, m->name, strlen (m->name));
203 dir_end = ftello (zw->file);
205 /* End of central directory record. */
206 put_u32 (zw, 0x06054b50); /* end of central dir signature */
207 put_u16 (zw, 0); /* number of this disk */
208 put_u16 (zw, 0); /* number of the disk with the
209 start of the central directory */
210 put_u16 (zw, zw->n_members); /* total number of entries in the
211 central directory on this disk */
212 put_u16 (zw, zw->n_members); /* total number of entries in
213 the central directory */
214 put_u32 (zw, dir_end - dir_start); /* size of the central directory */
215 put_u32 (zw, dir_start); /* offset of start of central
216 directory with respect to
217 the starting disk number */
218 put_u16 (zw, 0); /* .ZIP file comment length */
220 if (!fwriteerror (zw->file))
224 error (0, errno, _("%s: write failed"), zw->file_name);
228 free (zw->file_name);