Skip to content

Commit 5dae274

Browse files
committed
Merge branch 'dev'
2 parents be62c47 + a08999d commit 5dae274

File tree

3 files changed

+163
-20
lines changed

3 files changed

+163
-20
lines changed

CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#----------------------- PROJECT CONFIGURATION -------------------------
22
cmake_minimum_required(VERSION 3.10)
3-
project(cpp-dotenv VERSION 0.1.0)
3+
project(cpp-dotenv VERSION 0.2.0)
44

55
set(CMAKE_CXX_STANDARD 11)
66
set(CMAKE_CXX_STANDARD_REQUIRED ON)

README.md

+90-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# cpp-dotenv
22

3-
C++ implementation of nodejs [dotenv](https://github.com/motdotla/dotenv) project. Loads environment variables from .env for C++ projects.
3+
![version 0.2.0](https://img.shields.io/badge/version-0.2.0-blue)
4+
5+
C++ implementation of NodeJS [dotenv](https://github.com/motdotla/dotenv) project. Loads environment variables from `.env` for C++ projects.
46

57
**Please take into account this is still a developing project.**
68

@@ -12,6 +14,9 @@ C++ implementation of nodejs [dotenv](https://github.com/motdotla/dotenv) projec
1214
2. [Usage](#usage)
1315
1. [CMake](#cmake)
1416
3. [Examples](#examples)
17+
1. [Basic usage](#basic-usage)
18+
2. [Reference renaming](#reference-renaming)
19+
3. [Several dotenv files](#several-dotenv-files)
1520
4. [Grammar](#grammar)
1621

1722
## Dependencies
@@ -34,6 +39,8 @@ using namespace dotenv;
3439

3540
For convenience, **cpp-dotenv** auto-configures a class object (which is instance of the singleton class `dotenv`) by calling the `load_dotenv()` method at the very beginning of your file (just right before the end of `dotenv.h`) and trying to load a `.env` file, although if you need to add-in your own files (like `.myenv`), simply re-run the loading step passing the file name as parameter; everything new will show up on the `dotenv` instances.
3641

42+
By default, already-defined environment variables are not overwritten even if redefined in some of the loaded files. This behavior can be changed, however, by calling the `load_config()` function with the `overwrite` parameter set to `true`. For an example, take a look at [this one](#several-dotenv-files).
43+
3744
Also for convenience, there is a namespace-global pre-loaded reference variable to the `dotenv` singleton class instance named `env`. Simply use it as you would use a dotenv object on NodeJS, or you can define your own references:
3845

3946
```cpp
@@ -56,6 +63,8 @@ After this, you might use the library as described in [usage](#usage); no extra
5663

5764
## Examples
5865

66+
### Basic usage
67+
5968
Assume the following `.env` file:
6069

6170
```env
@@ -80,8 +89,34 @@ using namespace std;
8089

8190
int main()
8291
{
83-
auto& dotenv = env; // Reference re-naming
92+
cout << "DB_NAME: " << env["DB_NAME"] << endl;
93+
cout << "eval \"" << env["COMMAND"] << " " << env["HOST"] << "\"" << endl;
94+
}
95+
```
8496

97+
would produce the following output:
98+
99+
```shell
100+
$ ./main
101+
DB_NAME: DontDoThisAtHome
102+
eval "ping 8.8.8.8"
103+
```
104+
105+
### Reference renaming
106+
107+
Assuming the same `.env` file as in the [previous case](#basic-usage), the predefined `env` reference can be easily renamed and used just exactly as the original one.
108+
109+
The following code:
110+
111+
```cpp
112+
#include "dotenv.h"
113+
#include <iostream>
114+
115+
using namespace std;
116+
117+
int main()
118+
{
119+
auto& dotenv = dotenv::env;
85120
cout << "DB_NAME: " << dotenv["DB_NAME"] << endl;
86121
cout << "eval \"" << dotenv["COMMAND"] << " " << dotenv["HOST"] << "\"" << endl;
87122
}
@@ -95,6 +130,59 @@ $ ./main
95130
eval "ping 8.8.8.8"
96131
```
97132

133+
### Several dotenv files
134+
135+
The situation of having several different dotenv files is no stranger one (`.env` for private configuration variables, `.pubenv` for public variables, etc.). Loading several files in addition to the default one and overwritting any variables that are redefined on the files can be done as follows:
136+
137+
Assume the following `.env` file:
138+
139+
```env
140+
# DB THINGS
141+
DB_NAME=DontDoThisAtHome
142+
DB_PASS=such_security
143+
```
144+
145+
And the following `.pubenv` file:
146+
147+
```env
148+
# CONNECTIONS THINGS
149+
COMMAND=ping
150+
HOST=8.8.8.8
151+
MESSAGE="Hey buddy!"
152+
```
153+
154+
The following source file:
155+
156+
```cpp
157+
#include "dotenv.h"
158+
#include <iostream>
159+
160+
using namespace dotenv;
161+
using namespace std;
162+
163+
int main()
164+
{
165+
env.load_dotenv(".env", true);
166+
env.load_dotenv(".pubenv", true);
167+
cout << "DB_NAME: " << env["DB_NAME"] << endl;
168+
cout << "eval \"" << env["COMMAND"] << " " << env["HOST"] << "\"" << endl;
169+
}
170+
```
171+
172+
would produce the following output:
173+
174+
```shell
175+
$ ./main
176+
DB_NAME: DontDoThisAtHome
177+
eval "ping 8.8.8.8"
178+
```
179+
98180
## Grammar
99181

100182
For the geeks, you can check the grammar I've implemented on the `grammar/env.g4` file. Despite being written in an ANTLR4 fashion, I've implemented a simple recursive parser myself given the basic nature of the language. The parser and its methods are publicly available under the `dotenv::parser` class.
183+
184+
## Known issues
185+
186+
The complete list of issues con be consulted at the [issues page](https://github.com/adeharo9/cpp-dotenv/issues).
187+
188+
1. [Variable resolution on values not yet vailable](https://github.com/adeharo9/cpp-dotenv/issues/3)

dotenv.h

+72-17
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#include <vector>
99

1010

11-
#ifdef _DEBUG
11+
#if defined(_DEBUG) || defined(__DEBUG)
1212
#include <iostream>
1313

1414
#define DEBUG_ENTER(x) std::cerr << "ENTER " << row_count << ":" << col_count << " " << #x << std::endl
@@ -255,33 +255,59 @@ namespace dotenv
255255
inline void SINGLE_UNQUOTED_STRING()
256256
{
257257
DEBUG_ENTER(SINGLE_UNQUOTED_STRING);
258-
while (not token_is(SQ_C)) { next(); }
258+
while (not token_is(SQ_C))
259+
{
260+
if (token_is(BS_C))
261+
{
262+
possible_escaped_sequence();
263+
}
264+
else
265+
{
266+
next();
267+
}
268+
}
259269
DEBUG_EXIT(SINGLE_UNQUOTED_STRING);
260270
}
261271

262272
inline void DOUBLE_UNQUOTED_STRING()
263273
{
264274
DEBUG_ENTER(DOUBLE_UNQUOTED_STRING);
265-
while (not token_is(DQ_C)) { next(); }
275+
while (not token_is(DQ_C))
276+
{
277+
if (token_is(BS_C))
278+
{
279+
possible_escaped_sequence();
280+
}
281+
else
282+
{
283+
next();
284+
}
285+
}
266286
DEBUG_EXIT(DOUBLE_UNQUOTED_STRING);
267287
}
268288

269289
inline void UNQUOTED_KEY()
270290
{
291+
DEBUG_ENTER(UNQUOTED_KEY);
271292
match(UNQUOTED_KEY_CHAR);
272293
while (token_is(UNQUOTED_KEY_CHAR)) { match(UNQUOTED_KEY_CHAR); }
294+
DEBUG_EXIT(UNQUOTED_KEY);
273295
}
274296

275297
inline void UNQUOTED_VALUE()
276298
{
299+
DEBUG_ENTER(UNQUOTED_VALUE);
277300
match(UNQUOTED_VALUE_CHAR);
278301
while (token_is(UNQUOTED_VALUE_CHAR)) { match(UNQUOTED_VALUE_CHAR); }
302+
DEBUG_EXIT(UNQUOTED_VALUE);
279303
}
280304

281305
inline void UNQUOTED_COMMENT()
282306
{
307+
DEBUG_ENTER(UNQUOTED_COMMENT);
283308
match(UNQUOTED_COMMENT_CHAR);
284309
while (token_is(UNQUOTED_COMMENT_CHAR)) { match(UNQUOTED_COMMENT_CHAR); }
310+
DEBUG_EXIT(UNQUOTED_COMMENT);
285311
}
286312

287313
inline void NL()
@@ -290,6 +316,24 @@ namespace dotenv
290316
match(NL_C);
291317
}
292318

319+
inline void possible_escaped_sequence()
320+
{
321+
match(BS_C);
322+
for (auto& equivalence: ESCAPED_EQUIVALENCES)
323+
{
324+
if (equivalence.first == token)
325+
{
326+
if (bond and binded != nullptr) { binded -> pop_back(); }
327+
else if (bond and binded == nullptr) { throw std::runtime_error("something weird happened to this pointer"); }
328+
329+
token = equivalence.second;
330+
next();
331+
332+
break;
333+
}
334+
}
335+
}
336+
293337
inline void eof()
294338
{
295339
if (not is.eof()) { syntax_err(); }
@@ -382,18 +426,20 @@ namespace dotenv
382426

383427
private:
384428

385-
static const char CS_C = '#';
386-
static const char EQ_C = '=';
387-
static const char SP_C = ' ';
388-
static const char SQ_C = '\'';
389-
static const char DQ_C = '\"';
390-
static const char TB_C = '\t';
391-
static const char NL_C = '\n';
392-
static const char CR_C = '\r';
429+
static const char CS_C = '#'; // Comment (sharp)
430+
static const char EQ_C = '='; // Equal sign
431+
static const char SP_C = ' '; // Space
432+
static const char SQ_C = '\''; // Single quote
433+
static const char DQ_C = '\"'; // Double quote
434+
static const char TB_C = '\t'; // Tabulator
435+
static const char NL_C = '\n'; // Newline
436+
static const char CR_C = '\r'; // Carriage return
437+
static const char BS_C = '\\'; // Backslash
393438
static const container SP;
394439
static const container UNQUOTED_KEY_CHAR;
395440
static const container UNQUOTED_VALUE_CHAR;
396441
static const container UNQUOTED_COMMENT_CHAR;
442+
static const std::vector<std::pair<char, char>> ESCAPED_EQUIVALENCES;
397443

398444
};
399445

@@ -440,6 +486,21 @@ namespace dotenv
440486
CR_C
441487
};
442488

489+
const std::vector<std::pair<char, char>> parser::ESCAPED_EQUIVALENCES
490+
{
491+
{ '?' , '?' },
492+
{ '\'', '\'' },
493+
{ '"' , '"' },
494+
{ '\\', '\\' },
495+
{ 'a' , '\a' },
496+
{ 'b' , '\b' },
497+
{ 'f' , '\f' },
498+
{ 'n' , '\n' },
499+
{ 'r' , '\r' },
500+
{ 't' , '\t' },
501+
{ 'v' , '\v' }
502+
};
503+
443504
class dotenv
444505
{
445506
private:
@@ -460,8 +521,6 @@ namespace dotenv
460521
env_file.close();
461522
}
462523

463-
_config = true;
464-
465524
return *this;
466525
}
467526

@@ -473,8 +532,6 @@ namespace dotenv
473532

474533
inline const value_type operator[](const key_type& k) const
475534
{
476-
if (not _config) { throw std::logic_error(config_err); }
477-
478535
const char* value = std::getenv(k.c_str());
479536

480537
if (value == nullptr)
@@ -508,14 +565,12 @@ namespace dotenv
508565
bool _config = false;
509566

510567
static const std::string env_filename;
511-
static const std::string config_err;
512568
static dotenv _instance;
513569

514570
};
515571

516572

517573
const std::string dotenv::env_filename = ".env";
518-
const std::string dotenv::config_err = "config() method must be called first";
519574
dotenv dotenv::_instance;
520575

521576

0 commit comments

Comments
 (0)