]> sigrok.org Git - libsigrok.git/blob - src/output/srzip.c
Fix a few "value never read" scan-build warnings.
[libsigrok.git] / src / output / srzip.c
1 /*
2  * This file is part of the libsigrok project.
3  *
4  * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <glib.h>
26 #include <glib/gstdio.h>
27 #include <zip.h>
28 #include <libsigrok/libsigrok.h>
29 #include "libsigrok-internal.h"
30
31 #define LOG_PREFIX "output/srzip"
32
33 struct out_context {
34         gboolean zip_created;
35         uint64_t samplerate;
36         char *filename;
37 };
38
39 static int init(struct sr_output *o, GHashTable *options)
40 {
41         struct out_context *outc;
42
43         (void)options;
44
45         if (strlen(o->filename) == 0) {
46                 sr_info("srzip output module requires a file name, cannot save.");
47                 return SR_ERR_ARG;
48         }
49
50         outc = g_malloc0(sizeof(struct out_context));
51         outc->filename = g_strdup(o->filename);
52         o->priv = outc;
53
54         return SR_OK;
55 }
56
57 static int zip_create(const struct sr_output *o)
58 {
59         struct out_context *outc;
60         struct sr_channel *ch;
61         FILE *meta;
62         struct zip *zipfile;
63         struct zip_source *versrc, *metasrc;
64         GVariant *gvar;
65         GSList *l;
66         int tmpfile, ret;
67         char version[1], metafile[32], *s;
68
69         outc = o->priv;
70         if (outc->samplerate == 0) {
71                 if (sr_config_get(o->sdi->driver, o->sdi, NULL, SR_CONF_SAMPLERATE,
72                                 &gvar) == SR_OK) {
73                         outc->samplerate = g_variant_get_uint64(gvar);
74                         g_variant_unref(gvar);
75                 }
76         }
77
78         /* Quietly delete it first, libzip wants replace ops otherwise. */
79         unlink(outc->filename);
80         if (!(zipfile = zip_open(outc->filename, ZIP_CREATE, &ret)))
81                 return SR_ERR;
82
83         /* "version" */
84         version[0] = '2';
85         if (!(versrc = zip_source_buffer(zipfile, version, 1, 0)))
86                 return SR_ERR;
87         if (zip_add(zipfile, "version", versrc) == -1) {
88                 sr_info("Error saving version into zipfile: %s.",
89                         zip_strerror(zipfile));
90                 return SR_ERR;
91         }
92
93         /* init "metadata" */
94         strcpy(metafile, "sigrok-meta-XXXXXX");
95         if ((tmpfile = g_mkstemp(metafile)) == -1)
96                 return SR_ERR;
97         close(tmpfile);
98         meta = g_fopen(metafile, "wb");
99         fprintf(meta, "[global]\n");
100         fprintf(meta, "sigrok version = %s\n", SR_PACKAGE_VERSION_STRING);
101         fprintf(meta, "[device 1]\ncapturefile = logic-1\n");
102         fprintf(meta, "total probes = %d\n", g_slist_length(o->sdi->channels));
103         s = sr_samplerate_string(outc->samplerate);
104         fprintf(meta, "samplerate = %s\n", s);
105         g_free(s);
106
107         for (l = o->sdi->channels; l; l = l->next) {
108                 ch = l->data;
109                 if (ch->type != SR_CHANNEL_LOGIC)
110                         continue;
111                 if (!ch->enabled)
112                         continue;
113                 fprintf(meta, "probe%d = %s\n", ch->index + 1, ch->name);
114         }
115         fclose(meta);
116
117         if (!(metasrc = zip_source_file(zipfile, metafile, 0, -1))) {
118                 unlink(metafile);
119                 return SR_ERR;
120         }
121         if (zip_add(zipfile, "metadata", metasrc) == -1) {
122                 unlink(metafile);
123                 return SR_ERR;
124         }
125
126         if ((ret = zip_close(zipfile)) == -1) {
127                 sr_info("Error saving zipfile: %s.", zip_strerror(zipfile));
128                 unlink(metafile);
129                 return SR_ERR;
130         }
131
132         unlink(metafile);
133
134         return SR_OK;
135 }
136
137 static int zip_append(const struct sr_output *o, unsigned char *buf,
138                 int unitsize, int length)
139 {
140         struct out_context *outc;
141         struct zip *archive;
142         struct zip_source *logicsrc;
143         zip_int64_t num_files;
144         struct zip_file *zf;
145         struct zip_stat zs;
146         struct zip_source *metasrc;
147         GKeyFile *kf;
148         GError *error;
149         gsize len;
150         int chunk_num, next_chunk_num, tmpfile, ret, i;
151         const char *entry_name;
152         char *metafile, tmpname[32], chunkname[16];
153
154         outc = o->priv;
155         if (!(archive = zip_open(outc->filename, 0, &ret)))
156                 return SR_ERR;
157
158         if (zip_stat(archive, "metadata", 0, &zs) == -1)
159                 return SR_ERR;
160
161         metafile = g_malloc(zs.size);
162         zf = zip_fopen_index(archive, zs.index, 0);
163         zip_fread(zf, metafile, zs.size);
164         zip_fclose(zf);
165
166         /*
167          * If the file was only initialized but doesn't yet have any
168          * data it in, it won't have a unitsize field in metadata yet.
169          */
170         error = NULL;
171         kf = g_key_file_new();
172         if (!g_key_file_load_from_data(kf, metafile, zs.size, 0, &error)) {
173                 sr_err("Failed to parse metadata: %s.", error->message);
174                 return SR_ERR;
175         }
176         g_free(metafile);
177         tmpname[0] = '\0';
178         if (!g_key_file_has_key(kf, "device 1", "unitsize", &error)) {
179                 if (error && error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND) {
180                         sr_err("Failed to check unitsize key: %s", error ? error->message : "?");
181                         return SR_ERR;
182                 }
183                 /* Add unitsize field. */
184                 g_key_file_set_integer(kf, "device 1", "unitsize", unitsize);
185                 metafile = g_key_file_to_data(kf, &len, &error);
186                 strcpy(tmpname, "sigrok-meta-XXXXXX");
187                 if ((tmpfile = g_mkstemp(tmpname)) == -1)
188                         return SR_ERR;
189                 if (write(tmpfile, metafile, len) < 0) {
190                         sr_dbg("Failed to create new metadata: %s", g_strerror(errno));
191                         g_free(metafile);
192                         unlink(tmpname);
193                         return SR_ERR;
194                 }
195                 close(tmpfile);
196                 if (!(metasrc = zip_source_file(archive, tmpname, 0, -1))) {
197                         sr_err("Failed to create zip source for metadata.");
198                         g_free(metafile);
199                         unlink(tmpname);
200                         return SR_ERR;
201                 }
202                 if (zip_replace(archive, zs.index, metasrc) == -1) {
203                         sr_err("Failed to replace metadata file.");
204                         g_free(metafile);
205                         unlink(tmpname);
206                         return SR_ERR;
207                 }
208                 g_free(metafile);
209         }
210         g_key_file_free(kf);
211
212         next_chunk_num = 1;
213         num_files = zip_get_num_entries(archive, 0);
214         for (i = 0; i < num_files; i++) {
215                 entry_name = zip_get_name(archive, i, 0);
216                 if (strncmp(entry_name, "logic-1", 7))
217                         continue;
218                 if (strlen(entry_name) == 7) {
219                         /* This file has no extra chunks, just a single "logic-1".
220                          * Rename it to "logic-1-1" * and continue with chunk 2. */
221                         if (zip_rename(archive, i, "logic-1-1") == -1) {
222                                 sr_err("Failed to rename 'logic-1' to 'logic-1-1'.");
223                                 unlink(tmpname);
224                                 return SR_ERR;
225                         }
226                         next_chunk_num = 2;
227                         break;
228                 } else if (strlen(entry_name) > 8 && entry_name[7] == '-') {
229                         chunk_num = strtoull(entry_name + 8, NULL, 10);
230                         if (chunk_num >= next_chunk_num)
231                                 next_chunk_num = chunk_num + 1;
232                 }
233         }
234         snprintf(chunkname, 15, "logic-1-%d", next_chunk_num);
235         if (!(logicsrc = zip_source_buffer(archive, buf, length, FALSE))) {
236                 unlink(tmpname);
237                 return SR_ERR;
238         }
239         if (zip_add(archive, chunkname, logicsrc) == -1) {
240                 unlink(tmpname);
241                 return SR_ERR;
242         }
243         if ((ret = zip_close(archive)) == -1) {
244                 sr_info("error saving session file: %s", zip_strerror(archive));
245                 unlink(tmpname);
246                 return SR_ERR;
247         }
248         unlink(tmpname);
249
250         return SR_OK;
251 }
252
253 static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
254                 GString **out)
255 {
256         struct out_context *outc;
257         const struct sr_datafeed_meta *meta;
258         const struct sr_datafeed_logic *logic;
259         const struct sr_config *src;
260         GSList *l;
261
262         int ret;
263
264         *out = NULL;
265         if (!o || !o->sdi || !(outc = o->priv))
266                 return SR_ERR_ARG;
267
268         switch (packet->type) {
269         case SR_DF_META:
270                 meta = packet->payload;
271                 for (l = meta->config; l; l = l->next) {
272                         src = l->data;
273                         if (src->key != SR_CONF_SAMPLERATE)
274                                 continue;
275                         outc->samplerate = g_variant_get_uint64(src->data);
276                 }
277                 break;
278         case SR_DF_LOGIC:
279                 if (!outc->zip_created) {
280                         if ((ret = zip_create(o)) != SR_OK)
281                                 return ret;
282                         outc->zip_created = TRUE;
283                 }
284                 logic = packet->payload;
285                 ret = zip_append(o, logic->data, logic->unitsize, logic->length);
286                 if (ret != SR_OK)
287                         return ret;
288                 break;
289         }
290
291         return SR_OK;
292 }
293
294 static int cleanup(struct sr_output *o)
295 {
296         struct out_context *outc;
297
298         outc = o->priv;
299         g_free(outc->filename);
300         g_free(outc);
301         o->priv = NULL;
302
303         return SR_OK;
304 }
305
306 static struct sr_option options[] = {
307         ALL_ZERO
308 };
309
310 static const struct sr_option *get_options(void)
311 {
312         if (!options[0].def)
313                 options[0].def = g_variant_ref_sink(g_variant_new_string(""));
314
315         return options;
316 }
317
318 SR_PRIV struct sr_output_module output_srzip = {
319         .id = "srzip",
320         .name = "srzip",
321         .desc = "srzip session file",
322         .exts = (const char*[]){"sr", NULL},
323         .flags = SR_OUTPUT_INTERNAL_IO_HANDLING,
324         .options = get_options,
325         .init = init,
326         .receive = receive,
327         .cleanup = cleanup,
328 };