Skip to content

Commit 3fa7384

Browse files
committed
Argument parsing
Added an argument parser (Thanks @qookei) which supports the following flags: --file/-f --midi/-m --list/-l
1 parent 6bc21d9 commit 3fa7384

File tree

5 files changed

+120
-20
lines changed

5 files changed

+120
-20
lines changed

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "modules/conflict"]
2+
path = modules/conflict
3+
url = https://github.com/qookei/conflict

config.mk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ SRC_DIR=src
99
SRCS=$(basename $(subst $(SRC_DIR),$(BUILD_DIR),$(wildcard $(SRC_DIR)/*.cpp)))
1010

1111
# Libraries to include and link
12-
INC=-Isrc/
12+
INC=-Isrc/ -Imodules/conflict/include/
1313
LIBS=-ljack
1414

1515
# Flags

modules/conflict

Submodule conflict added at f8386aa

src/cane.cpp

Lines changed: 106 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,87 @@
11
#include <iostream>
2+
#include <sstream>
3+
#include <fstream>
24
#include <string>
35
#include <string_view>
46
#include <chrono>
57
#include <thread>
8+
#include <filesystem>
69

710
#include <report.hpp>
811
#include <view.hpp>
912
#include <lib.hpp>
1013

14+
#include <conflict/conflict.hpp>
15+
1116
extern "C" {
1217
#include <jack/jack.h>
1318
#include <jack/midiport.h>
1419
#include <jack/ringbuffer.h>
1520
}
1621

17-
int main(int, const char*[]) {
18-
std::string device = "j2a";
22+
enum {
23+
OPT_HELP = 0b01,
24+
OPT_LIST = 0b10,
25+
};
1926

27+
inline std::string read_file(std::filesystem::path path) {
2028
try {
21-
std::istreambuf_iterator<char> begin { std::cin }, end;
22-
std::string in { begin, end };
29+
std::filesystem::path cur = path;
2330

24-
cane::View src { &*in.begin(), &*in.end() };
25-
cane::Lexer lx { src };
31+
while (std::filesystem::is_symlink(cur)) {
32+
std::filesystem::path tmp = std::filesystem::read_symlink(cur);
2633

27-
if (not cane::utf_validate(src))
28-
lx.error(cane::Phases::ENCODING, src, cane::STR_ENCODING);
34+
if (tmp == cur)
35+
cane::general_error(cane::STR_SYMLINK_ERROR, path);
2936

30-
namespace time = std::chrono;
37+
cur = tmp;
38+
}
3139

32-
using clock = time::steady_clock;
33-
using unit = time::microseconds;
40+
if (std::filesystem::is_directory(cur) or std::filesystem::is_other(cur))
41+
cane::general_error(cane::STR_NOT_FILE_ERROR, path);
3442

35-
auto t1 = clock::now();
36-
cane::Context ctx = cane::compile(lx);
37-
auto t2 = clock::now();
43+
if (not std::filesystem::exists(cur))
44+
cane::general_error(cane::STR_FILE_NOT_FOUND_ERROR, path);
3845

39-
time::duration<double, std::micro> t = t2 - t1;
40-
CANE_LOG(cane::LOG_SUCC, CANE_ANSI_FG_YELLOW "took: {}µs" CANE_ANSI_RESET, t.count());
46+
std::ifstream is(cur, std::ios::binary);
4147

42-
if (ctx.timeline.empty())
48+
if (not is.is_open())
49+
cane::general_error(cane::STR_FILE_READ_ERROR, path);
50+
51+
std::stringstream ss;
52+
ss << is.rdbuf();
53+
54+
return ss.str();
55+
}
56+
57+
catch (const std::filesystem::filesystem_error&) {
58+
cane::general_error(cane::STR_FILE_READ_ERROR, path);
59+
}
60+
}
61+
62+
int main(int argc, const char* argv[]) {
63+
std::string_view device;
64+
std::string_view filename;
65+
uint64_t flags;
66+
67+
auto parser = conflict::parser {
68+
conflict::option { { 'h', "help", "show help" }, flags, OPT_HELP },
69+
conflict::option { { 'l', "list", "list available midi devices" }, flags, OPT_LIST },
70+
71+
conflict::string_option { { 'f', "file", "input file" }, "filename", filename },
72+
conflict::string_option { { 'm', "midi", "midi device to connect to" }, "device", device },
73+
};
74+
75+
parser.apply_defaults();
76+
auto st = parser.parse(argc - 1, argv + 1);
77+
78+
try {
79+
conflict::default_report(st);
80+
81+
if (flags & OPT_HELP) {
82+
parser.print_help();
4383
return 0;
84+
}
4485

4586

4687
// Setup JACK
@@ -92,21 +133,67 @@ int main(int, const char*[]) {
92133
cane::general_error(cane::STR_MIDI_ACTIVATE_ERROR);
93134

94135

136+
// If no device is specified _and_ `-l` is not passed,
137+
// throw an error. It's perfectly valid to give an empty
138+
// string to JACK here and it will give us back a list
139+
// of ports regardless.
140+
if (device.empty() and (flags & OPT_LIST) != OPT_LIST)
141+
cane::general_error(cane::STR_MIDI_NO_DEVICE);
142+
143+
95144
// Get an array of all MIDI input ports that we could potentially connect to.
96145
const char** ports = nullptr;
97146

98-
if (not (ports = jack_get_ports(midi.client, device.c_str(), JACK_DEFAULT_MIDI_TYPE, JackPortIsInput)))
147+
if (not (ports = jack_get_ports(midi.client, std::string{ device }.c_str(), JACK_DEFAULT_MIDI_TYPE, JackPortIsInput)))
99148
cane::general_error(cane::STR_MIDI_GET_PORTS_ERROR);
100149

101150
if (*ports == nullptr) // No MIDI input ports.
102151
cane::general_error(cane::STR_MIDI_NOT_FOUND, device);
103152

153+
if (flags & OPT_LIST) {
154+
for (; *ports != nullptr; ++ports)
155+
cane::general_notice(cane::STR_MIDI_DEVICE, *ports);
156+
157+
return 0;
158+
}
159+
104160
cane::general_notice(cane::STR_MIDI_FOUND, *ports);
105161

106162
if (jack_connect(midi.client, jack_port_name(midi.port), *ports))
107163
cane::general_error(cane::STR_MIDI_PATCH_ERROR);
108164

109-
jack_free(ports);
165+
jack_free(ports); // TODO: free this on error.
166+
167+
168+
// Compiler
169+
if (filename.empty())
170+
cane::general_error(cane::STR_NO_FILE);
171+
172+
auto path = std::filesystem::current_path() / std::filesystem::path { filename };
173+
std::filesystem::current_path(path.parent_path());
174+
175+
std::string in = read_file(path);
176+
177+
cane::View src { &*in.begin(), &*in.end() };
178+
cane::Lexer lx { src };
179+
180+
if (not cane::utf_validate(src))
181+
lx.error(cane::Phases::ENCODING, src, cane::STR_ENCODING);
182+
183+
namespace time = std::chrono;
184+
185+
using clock = time::steady_clock;
186+
using unit = time::microseconds;
187+
188+
auto t1 = clock::now();
189+
cane::Context ctx = cane::compile(lx);
190+
auto t2 = clock::now();
191+
192+
time::duration<double, std::micro> t = t2 - t1;
193+
CANE_LOG(cane::LOG_SUCC, CANE_ANSI_FG_YELLOW "took: {}µs" CANE_ANSI_RESET, t.count());
194+
195+
if (ctx.timeline.empty())
196+
return 0;
110197

111198

112199
// Sequencer

src/locale.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ namespace cane {
5252

5353
constexpr View STR_DEBUG = "`{}`(x{}) @{}bpm/{}s"_sv;
5454

55+
constexpr View STR_NO_FILE = "no file specified"_sv;
56+
57+
constexpr View STR_MIDI_NO_DEVICE = "no MIDI device specified"_sv;
58+
constexpr View STR_MIDI_DEVICE = "device `{}`"_sv;
5559
constexpr View STR_MIDI_FOUND = "found port `{}`"_sv;
5660
constexpr View STR_MIDI_NOT_FOUND = "port `{}` not found"_sv;
5761
constexpr View STR_MIDI_CONNECT_ERROR = "could not connect to the JACK server"_sv;
@@ -61,6 +65,11 @@ namespace cane {
6165
constexpr View STR_MIDI_GET_PORTS_ERROR = "could not get MIDI input ports from JACK"_sv;
6266
constexpr View STR_MIDI_PATCH_ERROR = "could not connect to port `{}`"_sv;
6367

68+
constexpr View STR_SYMLINK_ERROR = "symlink `{}` resolves to itself"_sv;
69+
constexpr View STR_NOT_FILE_ERROR = "`{}` is not a file"_sv;
70+
constexpr View STR_FILE_NOT_FOUND_ERROR = "file `{}` not found"_sv;
71+
constexpr View STR_FILE_READ_ERROR = "cannot read `{}`"_sv;
72+
6473
}
6574

6675
#endif

0 commit comments

Comments
 (0)