Use new github account
[oggfilter.git] / checks.c
blobab5f0fc9340ccbcbae85b36e89fb27f294123826
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 <limits.h>
13 #include <regex.h>
14 #include <stdbool.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <vorbis/vorbisfile.h>
19 #include "checks.h"
20 #include "list.h"
22 enum {
23 COMMENTLEN = LINE_MAX
26 struct chk_context {
27 struct chk_conditions *cond;
28 struct element *regexlist;
29 iconv_t cd;
32 struct oggfile {
33 char *path;
34 OggVorbis_File ovf;
37 struct regex {
38 char *pattern;
39 regex_t *regex;
40 int invert;
43 static bool check_bitrate(struct oggfile *of, struct chk_context *ctx);
44 static bool check_comments(struct oggfile *of, struct chk_context *ctx);
45 static bool check_time(struct oggfile *of, struct chk_context *ctx);
46 static int oggfile_close(struct oggfile *of);
47 static int oggfile_open(struct oggfile *of, char *path);
49 bool
50 chk_check_file(char *path, struct chk_context *ctx)
52 assert(path != NULL);
53 assert(ctx != NULL);
55 struct oggfile of;
56 if (oggfile_open(&of, path) != 0) {
57 warn("oggfile_open \"%s\"", path);
58 return (0);
61 bool match = check_time(&of, ctx);
62 match = match && check_bitrate(&of, ctx);
63 match = match && check_comments(&of, ctx);
65 if (oggfile_close(&of) != 0)
66 warn("oggfile_close \"%s\"", path);
68 return (match);
71 void
72 chk_init_conditions(struct chk_conditions *cond)
74 assert(cond != NULL);
76 cond->min_length = -1;
77 cond->max_length = -1;
78 cond->min_bitrate = -1;
79 cond->max_bitrate = -1;
80 cond->regexlist = NULL;
83 struct chk_context *
84 chk_context_open(struct chk_conditions *cond)
86 assert(cond != NULL);
88 struct chk_context *ctx = malloc(sizeof(*ctx));
89 if (ctx == NULL)
90 err(EXIT_FAILURE, "malloc chk_context");
91 if ((ctx->cd = iconv_open("", "UTF-8")) == (iconv_t) (-1))
92 err(EXIT_FAILURE, "iconv_open");
93 ctx->cond = cond;
94 ctx->regexlist = NULL;
96 for (struct element *e = cond->regexlist; e != NULL; e = e->next) {
97 struct regex *re = malloc(sizeof(*re));
98 if (re == NULL)
99 err(EXIT_FAILURE, "malloc regex");
101 struct chk_expression *cx = e->payload;
102 re->invert = cx->invert;
103 re->pattern = cx->expression;
104 if ((re->regex = malloc(sizeof(*(re->regex))))== NULL)
105 err(EXIT_FAILURE, "malloc regex_t");
107 int flags = REG_EXTENDED;
108 if (!cond->noignorecase)
109 flags |= REG_ICASE;
111 int errcode = regcomp(re->regex, re->pattern, flags);
112 if (errcode != 0) {
113 char errstr[128];
114 regerror(errcode, re->regex, errstr, sizeof(errstr));
115 errx(EXIT_FAILURE, "regcomp \"%s\": %s", re->pattern, errstr);
118 struct element *ne = create_element(re);
119 if (ne == NULL)
120 err(EXIT_FAILURE, "create_element regex");
121 ctx->regexlist = prepend_element(ne, ctx->regexlist);
124 return (ctx);
127 void
128 chk_context_close(struct chk_context *ctx)
130 assert(ctx != NULL);
132 if (iconv_close(ctx->cd) == -1)
133 warn("iconv_close");
135 while (ctx->regexlist != NULL) {
136 struct regex *r = ctx->regexlist->payload;
137 regfree(r->regex);
138 free(r->regex);
139 ctx->regexlist = destroy_element(ctx->regexlist);
140 free(r);
143 free(ctx);
146 static int
147 oggfile_open(struct oggfile *of, char *path)
149 assert(of != NULL);
150 assert(path != NULL);
152 if (ov_fopen((char *)path, &of->ovf) != 0)
153 return (1);
155 of->path = path;
157 return (0);
160 static int
161 oggfile_close(struct oggfile *of)
163 assert(of != NULL);
165 if (ov_clear(&of->ovf) != 0)
166 return (1);
168 of->path = NULL;
170 return (0);
173 static bool
174 check_time(struct oggfile *of, struct chk_context *ctx)
176 assert(of != NULL);
177 assert(ctx != NULL);
179 double time = ov_time_total(&of->ovf, -1);
180 if (time == OV_EINVAL) {
181 warnx("ov_time_total \"%s\"", of->path);
182 return (false);
185 double min_length = ctx->cond->min_length;
186 if (min_length >= 0 && min_length > time)
187 return (false);
189 double max_length = ctx->cond->max_length;
190 if (max_length >= 0 && max_length < time)
191 return (false);
193 return (true);
196 static bool
197 check_bitrate(struct oggfile *of, struct chk_context *ctx)
199 assert(of != NULL);
200 assert(ctx != NULL);
202 vorbis_info *ovi = ov_info(&of->ovf, -1);
203 if (ovi == NULL) {
204 warnx("ov_info \"%s\"", of->path);
205 return (false);
208 long nominal = ovi->bitrate_nominal;
210 long min_bitrate = ctx->cond->min_bitrate;
211 if (min_bitrate >= 0 && min_bitrate > nominal)
212 return (false);
214 long max_bitrate = ctx->cond->max_bitrate;
215 if (max_bitrate >= 0 && max_bitrate < nominal)
216 return (false);
218 return (true);
221 static bool
222 check_comments(struct oggfile *of, struct chk_context *ctx)
224 assert(of != NULL);
225 assert(ctx != NULL);
227 vorbis_comment *ovc = NULL;
228 if ((ovc = ov_comment(&of->ovf, -1)) == NULL) {
229 warnx("ov_comment \"%s\"", of->path);
230 return (false);
232 for (struct element *e = ctx->regexlist; e != NULL; e = e->next) {
233 int match = 0;
234 struct regex *re = e->payload;
236 for (int i = 0; i < ovc->comments; i++) {
237 char *comment, *conv_comment;
238 if ((conv_comment = malloc(COMMENTLEN)) == NULL)
239 err(EXIT_FAILURE, "malloc(%d)", COMMENTLEN);
240 comment = ovc->user_comments[i];
242 const char *from = comment;
243 char *to = conv_comment;
244 size_t fromlen = strlen(comment);
245 size_t tolen = COMMENTLEN;
247 if (iconv(ctx->cd, NULL, NULL, &to, &tolen) == (size_t) (-1))
248 err(EXIT_FAILURE, "reset iconv descriptor");
249 while (fromlen > 0)
250 if (iconv(ctx->cd, &from, &fromlen, &to, &tolen) == (size_t) (-1)) {
251 warn("iconv \"%s\"", ovc->user_comments[i]);
252 free(conv_comment);
253 return (0);
255 *to = '\0';
257 if (regexec(re->regex, conv_comment, 0, NULL, 0)== 0)
258 match = 1;
260 free(conv_comment);
263 assert(match == 0 || match == 1);
264 assert(re->invert == 0 || re->invert == 1);
265 if (!match ^ re->invert)
266 return (false);
269 return (true);