-
Notifications
You must be signed in to change notification settings - Fork 1
/
amded.cpp
301 lines (277 loc) · 7.88 KB
/
amded.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
/*
* Copyright (c) 2013-2019 amded workers, All rights reserved.
* Terms for redistribution and use can be found in LICENCE.
*/
/**
* @file amded.cpp
* @brief amded's main() and command line option handling.
*/
#include <cstdlib>
#include <cstring>
#include <iostream>
#include "amded.h"
#include "cmdline.h"
#include "file-spec.h"
#include "info.h"
#include "list-human.h"
#include "list-json.h"
#include "list-machine.h"
#include "mode.h"
#include "setup.h"
#include "strip.h"
#include "tag.h"
#include "bsdgetopt.c"
/**
* ASCII End-Of-Transmission character code. Used to seperate output for
* different files in machine readable mode.
*/
#define ASCII_EOT ((char)0x04)
/* Short-hand to address items from enum class OperationMode */
using AmdedMode = Amded::OperationMode;
/** global variable describing amded's operation mode */
static Amded::Mode amded_mode{};
static void
amded_failure(void)
{
std::cout << PROJECT
<< ": -m, -j, -l and -t/-d may *not* be used at the same time.\n";
exit(EXIT_FAILURE);
}
/**
* Check that -m, -l and -t are not used with one another
*
* @return void
* @sideeffects Exists with EXIT_FAILURE on failure.
*/
static inline void
check_singlemode_ok(void)
{
if (!amded_mode.singlemode_ok()) {
amded_failure();
}
}
/**
* Mode sanity check for tagging mode
*
* @return void
* @sideeffects see check_singlemode_ok()
*/
static inline void
check_multimode_ok(void)
{
if (!amded_mode.multimode_ok()) {
amded_failure();
}
}
static void
verify_tag_name(enum tag_type type, const std::string &name)
{
if (type == TAG_INVALID) {
std::cerr << PROJECT << ": Invalid tag name: " << '"' << name << "\"\n";
exit(EXIT_FAILURE);
}
}
/**
* Parsing command line options
*
* Uses NetBSD's getopt() defined in bsdgetopt.c, which is a
* traditional getopt implementation that supports short options
* and doesn't do any permutations.
*
* This function should not perform any actions itself, but set
* amded_mode accordingly and let main()'s main loop handle everything.
*
* @param argc number of entries in *argv[]
* @param argv[] list of arguments at startup.
*
* @return void
* @sideeffects Exists using EXIT_SUCCESS or EXIT_FAILURE when appropriate.
*/
static void
parse_options(int argc, char *argv[])
{
int opt;
std::pair<std::string, std::string> tag;
enum tag_type type;
Value tagval;
while ((opt = bsd_getopt(argc, argv, "d:hjLlmo:R:Ss:t:VW:")) != -1) {
switch (opt) {
case 'h':
amded_usage();
exit(EXIT_SUCCESS);
case 'j':
check_singlemode_ok();
amded_mode.set(AmdedMode::LIST_JSON);
break;
case 'L':
amded_licence();
exit(EXIT_SUCCESS);
case 'l':
check_singlemode_ok();
amded_mode.set(AmdedMode::LIST_HUMAN);
break;
case 'm':
check_singlemode_ok();
amded_mode.set(AmdedMode::LIST_MACHINE);
break;
case 'o':
amded_parameters(optarg);
break;
case 'R':
setup_readmap(optarg);
break;
case 's':
if (strcmp(optarg, "tags") == 0) {
list_tags();
} else if (strcmp(optarg, "file-extensions") == 0) {
list_extensions();
} else {
std::cerr << PROJECT << ": Unknown aspect `"
<< optarg << "'." << std::endl;
}
exit(EXIT_SUCCESS);
case 'd':
/* ‘-d’ is a special case of the TAG mode. */
check_multimode_ok();
amded_mode.set(AmdedMode::TAG);
type = tag_to_type(optarg);
verify_tag_name(type, optarg);
tagval.set_invalid();
add_tag(tag_to_id(optarg), tagval);
break;
case 't':
check_multimode_ok();
amded_mode.set(AmdedMode::TAG);
/* First check if the definition looks like "foo=bar" */
try {
tag = tag_arg_to_pair(optarg);
}
catch (amded_broken_tag_def) {
std::cerr << PROJECT << ": Broken tag definition: "
<< '"' << optarg << '"'
<< std::endl;
exit(EXIT_FAILURE);
}
/* Make sure ‘foo’ in "foo=bar" is a supported tag name */
type = tag_to_type(tag.first);
verify_tag_name(type, tag.first);
/* Make sure ‘bar’ in "foo=bar" makes sense as a value for ‘foo’ */
tagval = tag_value_from_value(type, tag.second);
if (tagval.get_type() == TAG_INVALID) {
std::cerr << PROJECT << ": Invalid tag value ["
<< tag.second
<< "] for tag " << '"'
<< tag.first
<< '"' << '!'
<< std::endl;
exit(EXIT_FAILURE);
}
/* Looks good. Add the tag. */
add_tag(tag_to_id(tag.first), tagval);
unset_only_tag_delete();
break;
case 'S':
check_singlemode_ok();
amded_mode.set(AmdedMode::STRIP);
break;
case 'V':
amded_version();
exit(EXIT_SUCCESS);
case 'W':
setup_writemap(optarg);
break;
default:
amded_usage();
exit(EXIT_FAILURE);
}
}
}
/**
* amded: command line utility for listing and modifying meta
* information in audio files
*
* Interfacing KDE's taglib:
* <http://taglib.github.io>
*
* @param argc number of entries in *argv[]
* @param argv[] list of arguments at startup.
*
* @return EXIT_SUCCESS on normal execution;
* EXIT_FAILURE upon failure.
* @sideeffects none
*/
int
main(int argc, char *argv[])
{
if (argc < 2) {
amded_usage();
return EXIT_FAILURE;
}
parse_options(argc, argv);
if (optind == argc) {
amded_usage();
return EXIT_FAILURE;
}
if (amded_mode.is_list_mode()) {
if (read_map.empty()) {
setup_readmap("");
}
} else if (amded_mode.is_write_mode()) {
if (write_map.empty()) {
setup_writemap("");
}
}
bool first = true;
for (int i = optind; i < argc; ++i) {
struct amded_file file;
file.name = argv[i];
file.type = get_ext_type(argv[i]);
if (file.type.get_id() == FILE_T_INVALID) {
std::cerr << PROJECT ": Unsupported filetype: `"
<< file.name << "'" << std::endl;
continue;
}
if (!amded_open(file)) {
continue;
}
switch (amded_mode.get()) {
case AmdedMode::LIST_HUMAN:
if (!first) {
std::cout << std::endl;
} else {
first = false;
}
amded_list_human(file);
break;
case AmdedMode::LIST_JSON:
if (first) {
first = false;
}
amded_push_json(file);
break;
case AmdedMode::LIST_MACHINE:
if (!first) {
std::cout << ASCII_EOT;
} else {
first = false;
}
amded_list_machine(file);
break;
case AmdedMode::TAG:
amded_tag(file);
break;
case AmdedMode::STRIP:
amded_strip(file);
break;
default:
std::cout << "Please use one action option (-m, -l, -t or -S)."
<< std::endl;
return EXIT_FAILURE;
}
delete file.fh;
}
if (amded_mode.get() == AmdedMode::LIST_JSON) {
amded_list_json();
}
return EXIT_SUCCESS;
}