Handle already exited ogg123(1) gracefully
[oggquiz.git] / oggfile.c
blob0ac55ab5144d4d0384555f148e6f533659bf060c
1 /*-
2 * "THE BEER-WARE LICENSE" (Revision 42):
3 * <tobias.rehbein@web.de> wrote this file. As long as you retain this notice
4 * you can do whatever you want with this stuff. If we meet some day, and you
5 * think this stuff is worth it, you can buy me a beer in return.
6 * Tobias Rehbein
7 */
9 #include <assert.h>
10 #include <err.h>
11 #include <iconv.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sysexits.h>
15 #include <vorbis/vorbisfile.h>
17 #include "oggfile.h"
19 #define SAFE_STRNCPY(dst, src, len) do { \
20 strncpy(dst, src, (len)-1); \
21 dst[(len)-1] = '\0'; \
22 } while (0)
24 struct ogg_context {
25 iconv_t cd;
28 static int fill_comments(struct ogg_context *ctx, struct ogg_oggfile *ogg);
29 static void do_iconv(struct ogg_context *ctx, char *in, char *out, size_t outlen);
31 int
32 ogg_oggfile_create(struct ogg_context *ctx, struct ogg_oggfile *ogg, char *filename)
34 assert(ctx != NULL);
35 assert(ogg != NULL);
36 assert(filename != NULL);
38 SAFE_STRNCPY(ogg->filename, filename, sizeof(ogg->filename));
39 if (fill_comments(ctx, ogg) != 0)
40 return (1);
42 return (0);
45 static int
46 fill_comments(struct ogg_context *ctx, struct ogg_oggfile *ogg)
48 OggVorbis_File ovf;
49 vorbis_comment *ovc;
50 char *key, *value;
51 int i;
53 assert(ctx != NULL);
54 assert(ogg != NULL);
56 if (ov_fopen(ogg->filename, &ovf) != 0) {
57 warnx("could not open file: %s", ogg->filename);
58 return (1);
60 if ((ovc = ov_comment(&ovf, -1)) == NULL) {
61 warnx("could not read comments for file: %s", ogg->filename);
62 return (1);
64 for (i = 0; i < ovc->comments; i++) {
65 value = ovc->user_comments[i];
66 key = strsep(&value, "=");
67 if (strcasecmp(key, "artist") == 0)
68 do_iconv(ctx, value, ogg->artist, sizeof(ogg->artist));
69 else if (strcasecmp(key, "album") == 0)
70 do_iconv(ctx, value, ogg->album, sizeof(ogg->album));
71 else if (strcasecmp(key, "title") == 0)
72 do_iconv(ctx, value, ogg->title, sizeof(ogg->title));
73 if (ogg->artist == NULL || ogg->album == NULL || ogg->title == NULL) {
74 warnx("insufficient comments for file: %s", ogg->filename);
75 return (1);
79 if (ov_clear(&ovf) != 0)
80 warnx("could not close file: %s", ogg->filename);
82 return (0);
85 struct ogg_context *
86 ogg_context_open()
88 struct ogg_context *ctx;
89 iconv_t cd;
91 if ((cd = iconv_open("char", "UTF-8")) == (iconv_t) (-1))
92 return (NULL);
93 if ((ctx = malloc(sizeof(*ctx))) == NULL)
94 return (NULL);
96 ctx->cd = cd;
98 return (ctx);
102 ogg_context_close(struct ogg_context *ctx)
104 assert(ctx != NULL);
106 if (iconv_close(ctx->cd) == -1)
107 return (1);
108 free(ctx);
110 return (0);
113 static void
114 do_iconv(struct ogg_context *ctx, char *in, char *out, size_t outlen)
116 char **inp;
117 char **outp;
118 size_t inlen = strlen(in);
120 assert(ctx != NULL);
121 assert(in != NULL);
122 assert(out != NULL);
123 assert(outlen > 0);
125 inp = &in;
126 outp = &out;
128 if (iconv(ctx->cd, NULL, NULL, outp, &outlen) == (size_t) (-1))
129 errx(EX_SOFTWARE, "could not set initial conversion state");
131 while (inlen > 0) {
132 if (iconv(ctx->cd, (const char **)inp, &inlen, outp, &outlen) == (size_t) (-1))
133 errx(EX_SOFTWARE, "string conversion failed");
136 *outp[0] = '\0';