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"
28 #include "gl/fwriteerror.h"
29 #include "gl/xalloc.h"
31 #include "libpspp/message.h"
34 #define _(msgid) gettext (msgid)
38 char *file_name
; /* File name, for use in error mesages. */
39 FILE *file
; /* Output stream. */
41 uint16_t date
, time
; /* Date and time in MS-DOS format. */
45 /* Members already added to the file, so that we can summarize them to the
46 central directory at the end of the ZIP file. */
47 struct zip_member
*members
;
48 size_t n_members
, allocated_members
;
53 uint32_t offset
; /* Starting offset in file. */
54 uint32_t size
; /* Length of member file data, in bytes. */
55 uint32_t crc
; /* CRC-32 of member file data.. */
56 char *name
; /* Name of member file. */
60 put_bytes (struct zip_writer
*zw
, const void *p
, size_t n
)
62 fwrite (p
, 1, n
, zw
->file
);
66 put_u16 (struct zip_writer
*zw
, uint16_t x
)
68 #ifdef WORDS_BIGENDIAN
71 put_bytes (zw
, &x
, sizeof x
);
75 put_u32 (struct zip_writer
*zw
, uint32_t x
)
77 #ifdef WORDS_BIGENDIAN
80 put_bytes (zw
, &x
, sizeof x
);
83 /* Starts writing a new ZIP file named FILE_NAME. Returns a new zip_writer if
84 successful, otherwise a null pointer. */
86 zip_writer_create (const char *file_name
)
88 struct zip_writer
*zw
;
93 file
= fopen (file_name
, "wb");
96 msg_error (errno
, _("%s: error opening output file"), file_name
);
100 zw
= xmalloc (sizeof *zw
);
101 zw
->file_name
= xstrdup (file_name
);
107 tm
= localtime (&now
);
108 zw
->date
= tm
->tm_mday
+ ((tm
->tm_mon
+ 1) << 5) + ((tm
->tm_year
- 80) << 9);
109 zw
->time
= tm
->tm_sec
/ 2 + (tm
->tm_min
<< 5) + (tm
->tm_hour
<< 11);
113 zw
->allocated_members
= 0;
119 put_local_header (struct zip_writer
*zw
, const char *member_name
, uint32_t crc
,
120 uint32_t size
, int flag
)
122 put_u32 (zw
, MAGIC_LHDR
); /* local file header signature */
123 put_u16 (zw
, 10); /* version needed to extract */
124 put_u16 (zw
, flag
); /* general purpose bit flag */
125 put_u16 (zw
, 0); /* compression method */
126 put_u16 (zw
, zw
->time
); /* last mod file time */
127 put_u16 (zw
, zw
->date
); /* last mod file date */
128 put_u32 (zw
, crc
); /* crc-32 */
129 put_u32 (zw
, size
); /* compressed size */
130 put_u32 (zw
, size
); /* uncompressed size */
131 put_u16 (zw
, strlen (member_name
)); /* file name length */
132 put_u16 (zw
, 0); /* extra field length */
133 put_bytes (zw
, member_name
, strlen (member_name
));
136 /* Adds the contents of FILE, with name MEMBER_NAME, to ZW. */
138 zip_writer_add (struct zip_writer
*zw
, FILE *file
, const char *member_name
)
140 struct zip_member
*member
;
141 uint32_t offset
, size
;
146 /* Local file header. */
147 offset
= ftello (zw
->file
);
148 put_local_header (zw
, member_name
, 0, 0, 1 << 3);
152 fseeko (file
, 0, SEEK_SET
);
153 while ((bytes_read
= fread (buf
, 1, sizeof buf
, file
)) > 0)
155 put_bytes (zw
, buf
, bytes_read
);
157 crc
= crc32_update (crc
, buf
, bytes_read
);
160 /* Try to seek back to the local file header. If successful, overwrite it
161 with the correct file size and CRC. Otherwise, write data descriptor. */
162 if (fseeko (zw
->file
, offset
, SEEK_SET
) == 0)
164 put_local_header (zw
, member_name
, crc
, size
, 0);
165 if (fseeko (zw
->file
, size
, SEEK_CUR
)
168 msg_error (errno
, _("%s: error seeking in output file"), zw
->file_name
);
174 put_u32 (zw
, MAGIC_DDHD
);
180 /* Add to set of members. */
181 if (zw
->n_members
>= zw
->allocated_members
)
182 zw
->members
= x2nrealloc (zw
->members
, &zw
->allocated_members
,
183 sizeof *zw
->members
);
184 member
= &zw
->members
[zw
->n_members
++];
185 member
->offset
= offset
;
188 member
->name
= xstrdup (member_name
);
191 /* Finalizes the contents of ZW and closes it. Returns true if successful,
192 false if a write error occurred while finalizing the file or at any earlier
195 zip_writer_close (struct zip_writer
*zw
)
197 uint32_t dir_start
, dir_end
;
204 dir_start
= ftello (zw
->file
);
205 for (i
= 0; i
< zw
->n_members
; i
++)
207 struct zip_member
*m
= &zw
->members
[i
];
209 /* Central directory file header. */
210 put_u32 (zw
, MAGIC_SOCD
); /* central file header signature */
211 put_u16 (zw
, 63); /* version made by */
212 put_u16 (zw
, 10); /* version needed to extract */
213 put_u16 (zw
, 1 << 3); /* general purpose bit flag */
214 put_u16 (zw
, 0); /* compression method */
215 put_u16 (zw
, zw
->time
); /* last mod file time */
216 put_u16 (zw
, zw
->date
); /* last mod file date */
217 put_u32 (zw
, m
->crc
); /* crc-32 */
218 put_u32 (zw
, m
->size
); /* compressed size */
219 put_u32 (zw
, m
->size
); /* uncompressed size */
220 put_u16 (zw
, strlen (m
->name
)); /* file name length */
221 put_u16 (zw
, 0); /* extra field length */
222 put_u16 (zw
, 0); /* file comment length */
223 put_u16 (zw
, 0); /* disk number start */
224 put_u16 (zw
, 0); /* internal file attributes */
225 put_u32 (zw
, 0); /* external file attributes */
226 put_u32 (zw
, m
->offset
); /* relative offset of local header */
227 put_bytes (zw
, m
->name
, strlen (m
->name
));
231 dir_end
= ftello (zw
->file
);
233 /* End of central directory record. */
234 put_u32 (zw
, MAGIC_EOCD
); /* end of central dir signature */
235 put_u16 (zw
, 0); /* number of this disk */
236 put_u16 (zw
, 0); /* number of the disk with the
237 start of the central directory */
238 put_u16 (zw
, zw
->n_members
); /* total number of entries in the
239 central directory on this disk */
240 put_u16 (zw
, zw
->n_members
); /* total number of entries in
241 the central directory */
242 put_u32 (zw
, dir_end
- dir_start
); /* size of the central directory */
243 put_u32 (zw
, dir_start
); /* offset of start of central
244 directory with respect to
245 the starting disk number */
246 put_u16 (zw
, 0); /* .ZIP file comment length */
249 if (ok
&& fwriteerror (zw
->file
))
251 msg_error (errno
, _("%s: write failed"), zw
->file_name
);
255 free (zw
->file_name
);