Skip to content

Commit 9b5ad09

Browse files
committed
Initial commit.
0 parents  commit 9b5ad09

7 files changed

Lines changed: 436 additions & 0 deletions

File tree

‎.gitignore‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
bin/
2+
log/
3+
obj/
4+

‎LICENSE‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
Copyright (c) 2019, Maya Posch
2+
All rights reserved.
3+
4+
Redistribution and use in source and binary forms, with or without
5+
modification, are permitted provided that the following conditions are met:
6+
* Redistributions of source code must retain the above copyright
7+
notice, this list of conditions and the following disclaimer.
8+
* Redistributions in binary form must reproduce the above copyright
9+
notice, this list of conditions and the following disclaimer in the
10+
documentation and/or other materials provided with the distribution.
11+
* Neither the name of the <organization> nor the
12+
names of its contributors may be used to endorse or promote products
13+
derived from this software without specific prior written permission.
14+
15+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18+
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
19+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

‎Makefile‎

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Makefile for the Sarge command line argument parsing project.
2+
3+
4+
export TOP := $(CURDIR)
5+
6+
GPP = g++
7+
GCC = gcc
8+
MAKEDIR = mkdir -p
9+
RM = rm -f
10+
AR = ar
11+
MAKE = make
12+
13+
BIN_OUTPUT := sarge_test
14+
INCLUDE = -I src/
15+
FLAGS +=
16+
CPPFLAGS := $(FLAGS) -g3 -std=c++14 $(INCLUDE)
17+
ifdef OS
18+
#LIBS += -lboost_filesystem-mt -lboost_program_options-mt -lboost_system-mt
19+
EXT = .exe
20+
else
21+
#LIBS += -lboost_filesystems -lboost_program_options -lboost_system
22+
endif
23+
24+
CPP_SOURCES := $(wildcard src/*.cpp)
25+
CPP_OBJECTS := $(addprefix obj/,$(notdir) $(CPP_SOURCES:.cpp=.o))
26+
27+
TEST_SOURCES := $(wildcard test/*.cpp)
28+
TEST_OBJECTS := $(addprefix obj/,$(notdir) $(TEST_SOURCES:.cpp=.o))
29+
30+
all: makedir $(CPP_OBJECTS) test
31+
32+
test: $(TEST_OBJECTS) $(BIN_OUTPUT)
33+
34+
obj/%.o: %.cpp
35+
$(GPP) -c -o $@ $< $(CPPFLAGS)
36+
37+
$(BIN_OUTPUT):
38+
$(RM) bin/$@$(EXT)
39+
$(GPP) -o bin/$@ $(CPPFLAGS) $(CPP_OBJECTS) $(TEST_OBJECTS) $(LIBS)
40+
41+
makedir:
42+
$(MAKEDIR) bin
43+
$(MAKEDIR) obj/src
44+
$(MAKEDIR) obj/test
45+
46+
clean:
47+
$(RM) $(CPP_OBJECTS) $(TEST_OBJECTS)
48+
49+
50+
51+
.PHONY: test src doc

‎README.md‎

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Sarge #
2+
3+
Sarge is a simple and powerful command line argument parser, consisting out of just 135 lines of well-commented C++ code, contained in a single class:
4+
5+
6+
github.com/AlDanial/cloc v 1.80 T=0.01 s (255.8 files/s, 28899.8 lines/s)
7+
-------------------------------------------------------------------------------
8+
Language files blank comment code
9+
-------------------------------------------------------------------------------
10+
C++ 1 37 35 103
11+
C/C++ Header 1 12 7 32
12+
-------------------------------------------------------------------------------
13+
SUM: 2 49 42 135
14+
-------------------------------------------------------------------------------
15+
16+
17+
Simply add the header file and source file to one's C++ project and use the class as in the project's test code:
18+
19+
#include "../src/sarge.h"
20+
21+
#include <iostream>
22+
23+
24+
int main(int argc, char** argv) {
25+
Sarge sarge;
26+
27+
sarge.setArgument("h", "help", "Get help.", false);
28+
sarge.setArgument("k", "kittens", "K is for kittens. Everyone needs kittens in their life.", true);
29+
sarge.setDescription("Sarge command line argument parsing testing app. For demonstration purposes and testing.");
30+
sarge.setUsage("sarge_test <options>");
31+
32+
if (!sarge.parseArguments(argc, argv)) {
33+
std::cerr << "Couldn't parse arguments..." << std::endl;
34+
return 1;
35+
}
36+
37+
std::cout << "Number of flags found: " << sarge.flagCount() << std::endl;
38+
39+
if (sarge.exists("help")) {
40+
sarge.printHelp();
41+
}
42+
else {
43+
std::cout << "No help requested..." << std::endl;
44+
}
45+
46+
std::string kittens;
47+
if (sarge.getFlag("kittens", kittens)) {
48+
std::cout << "Got kittens: " << kittens << std::endl;
49+
}
50+
51+
return 0;
52+
}
53+
54+
Only dependencies are a reasonably modern C++ compiler, capable of supporting at least C++11 (STL datastructure improvements).
55+
56+
## Compiling the test application ##
57+
58+
A Makefile has been added to the root of the project. Simply execute `make` in the folder to compile the test binary into the `bin/` folder. Execute `bin/sarge_test` to get the following output:
59+
60+
# ./bin/sarge_test.exe -hk Mew
61+
Number of flags found: 2
62+
63+
Sarge command line argument parsing testing app. For demonstration purposes and testing.
64+
65+
Usage:
66+
sarge_test <options>
67+
68+
Options:
69+
-h --help Get help.
70+
-k --kittens K is for kittens. Everyone needs kittens in their life.
71+
Got kittens: Mew
72+
73+
As you can see, no kittens were harmed in the production of this code :)
74+
75+
## Status ##
76+
77+
The project should not have any major bugs in it, but should be considered beta code. Please use with care and feel free to submit any issues you may encounter while using it.
78+
79+

‎src/sarge.cpp‎

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
/*
2+
sarge.cpp - Implementation file for the Sarge command line argument parser project.
3+
4+
Revision 0
5+
6+
Features:
7+
-
8+
9+
Notes:
10+
-
11+
12+
2019/03/16, Maya Posch
13+
14+
*/
15+
16+
17+
#include "sarge.h"
18+
19+
#include <iostream>
20+
21+
22+
// --- SET ARGUMENT ---
23+
void Sarge::setArgument(std::string arg_short, std::string arg_long, std::string desc, bool hasVal) {
24+
Argument arg;
25+
arg.arg_short = arg_short;
26+
arg.arg_long = arg_long;
27+
arg.description = desc;
28+
arg.hasValue = hasVal;
29+
arg.parsed = false;
30+
args.push_back(std::move(arg));
31+
32+
// Set up links.
33+
if (!arg_short.empty()) {
34+
argNames.insert(std::pair<std::string, Argument*>(arg_short, &(args.back())));
35+
}
36+
37+
if (!arg_long.empty()) {
38+
argNames.insert(std::pair<std::string, Argument*>(arg_long, &(args.back())));
39+
}
40+
}
41+
42+
43+
// --- SET ARGUMENTS ---
44+
void Sarge::setArguments(std::vector<Argument> args) {
45+
for (Argument a : args) {
46+
setArgument(a.arg_short, a.arg_long, a.description, a.hasValue);
47+
}
48+
}
49+
50+
51+
// --- PARSE ARGUMENTS ---
52+
bool Sarge::parseArguments(int argc, char** argv) {
53+
// The first argument is the name of the executable. After it we loop through the remaining
54+
// arguments, linking flags and values.
55+
execName = std::string(argv[0]);
56+
bool expectValue = false;
57+
std::map<std::string, Argument*>::const_iterator flag_it;
58+
for (int i = 1; i < argc; ++i) {
59+
// Each flag will start with a '-' character. Multiple flags can be joined together in the
60+
// same string if they're the short form flag type (one character per flag).
61+
std::string entry(argv[i]);
62+
63+
if (entry.compare(0, 1, "-") == 0) {
64+
// Parse flag.
65+
// First check for the long form.
66+
if (entry.compare(0, 2, "--") == 0) {
67+
// Long form of flag.
68+
entry.erase(0, 2); // Erase the double dash since we no longer need it.
69+
70+
flag_it = argNames.find(entry);
71+
if (flag_it == argNames.end()) {
72+
// Flag wasn't found. Abort.
73+
std::cerr << "Long flag " << entry << " wasn't found." << std::endl;
74+
return false;
75+
}
76+
77+
// Mark as found.
78+
flag_it->second->parsed = true;
79+
++flagCounter;
80+
81+
if (flag_it->second->hasValue) {
82+
expectValue = true; // Next argument has to be a value string.
83+
}
84+
}
85+
else {
86+
// Parse short form flag. Parse all of them sequentially. Only the last one
87+
// is allowed to have an additional value following it.
88+
entry.erase(0, 1); // Erase the dash.
89+
for (int i = 0; i < entry.length(); ++i) {
90+
std::string k(&(entry[i]), 1);
91+
flag_it = argNames.find(k);
92+
if (flag_it == argNames.end()) {
93+
// Flag wasn't found. Abort.
94+
std::cerr << "Short flag " << k << " wasn't found." << std::endl;
95+
return false;
96+
}
97+
98+
// Mark as found.
99+
flag_it->second->parsed = true;
100+
++flagCounter;
101+
102+
if (i != (entry.length() - 1) && flag_it->second->hasValue) {
103+
// Flag isn't at end, thus cannot have value.
104+
std::cerr << "Flag " << k << " needs to be followed by a value string."
105+
<< std::endl;
106+
return false;
107+
}
108+
else {
109+
expectValue = true; // Next argument has to be a value string.
110+
}
111+
}
112+
}
113+
}
114+
else if (expectValue) {
115+
// Copy value.
116+
flag_it->second->value = entry;
117+
118+
expectValue = false;
119+
}
120+
else {
121+
std::cerr << "Expected flag, not value." << std::endl;
122+
return false;
123+
}
124+
}
125+
126+
parsed = true;
127+
128+
return true;
129+
}
130+
131+
132+
// --- GET FLAG ---
133+
// Returns whether the flag was found, along with the value if relevant.
134+
bool Sarge::getFlag(std::string arg_flag, std::string &arg_value) {
135+
if (!parsed) { return false; }
136+
137+
std::map<std::string, Argument*>::const_iterator it = argNames.find(arg_flag);
138+
if (it == argNames.end()) { return false; }
139+
if (!it->second->parsed) { return false; }
140+
141+
if (it->second->hasValue) {
142+
arg_value = it->second->value;
143+
}
144+
145+
return true;
146+
}
147+
148+
149+
// --- EXISTS ---
150+
// Returns whether the flag was found.
151+
bool Sarge::exists(std::string arg_flag) {
152+
if (!parsed) { return false; }
153+
154+
std::map<std::string, Argument*>::const_iterator it = argNames.find(arg_flag);
155+
if (it == argNames.end()) { return false; }
156+
if (!it->second->parsed) { return false; }
157+
158+
return true;
159+
}
160+
161+
162+
// --- PRINT HELP ---
163+
// Prints out the application description, usage and available options.
164+
void Sarge::printHelp() {
165+
std::cout << std::endl << description << std::endl;
166+
std::cout << std::endl << "Usage:" << std::endl;
167+
std::cout << "\t" << usage << std::endl;
168+
std::cout << std::endl;
169+
std::cout << "Options: " << std::endl;
170+
171+
// Print out the options.
172+
for (Argument arg : args) {
173+
std::cout << "-" << arg.arg_short << "\t--" << arg.arg_long << "\t\t" << arg.description << std::endl;
174+
}
175+
}

‎src/sarge.h‎

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
sarge.h - Header file for the Sarge command line argument parser project.
3+
4+
Revision 0
5+
6+
Notes:
7+
-
8+
9+
2019/03/16, Maya Posch
10+
11+
*/
12+
13+
14+
#include <string>
15+
#include <vector>
16+
#include <map>
17+
18+
19+
struct Argument {
20+
Argument() : hasValue(false), parsed(false) { /* */ }
21+
std::string arg_short;
22+
std::string arg_long;
23+
std::string description;
24+
bool hasValue;
25+
std::string value;
26+
bool parsed;
27+
};
28+
29+
30+
31+
class Sarge {
32+
std::vector<Argument> args;
33+
std::map<std::string, Argument*> argNames;
34+
bool parsed = false;
35+
int flagCounter = 0;
36+
std::string execName;
37+
std::string description;
38+
std::string usage;
39+
40+
public:
41+
void setArgument(std::string arg_short, std::string arg_long, std::string desc, bool hasVal);
42+
void setArguments(std::vector<Argument> args);
43+
void setDescription(std::string desc) { this->description = desc; }
44+
void setUsage(std::string use) { this->usage = use; }
45+
bool parseArguments(int argc, char** argv);
46+
bool getFlag(std::string arg_flag, std::string &arg_value);
47+
bool exists(std::string arg_flag);
48+
void printHelp();
49+
int flagCount() { return flagCounter; }
50+
std::string executableName() { return execName; }
51+
};

0 commit comments

Comments
 (0)