Skip to content

Commit

Permalink
Merge pull request #2 from harshfeudal/develop
Browse files Browse the repository at this point in the history
[Merge]: Done fixing
  • Loading branch information
harshfeudal authored Mar 11, 2023
2 parents f12092c + e0f63ee commit 74fda81
Show file tree
Hide file tree
Showing 9 changed files with 214 additions and 137 deletions.
4 changes: 2 additions & 2 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto
*.h linguist-language=C++
*.cpp linguist-language=C++
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
#

cmake_minimum_required(VERSION 3.15)
project(Dotenv VERSION 1.1.0 DESCRIPTION "A simple C++ dotenv file reader for reading .env file in simplest way")
project(Dotenv VERSION 1.2.0 DESCRIPTION "A simple C++ dotenv file reader for reading .env file in simplest way")

# Define an INTERFACE library for the header-only library
add_library(${PROJECT_NAME} INTERFACE)
Expand Down
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,23 @@ With this library features, your comment will be ignored after the `#` mark! Com

3. This library just include __only a header file (`dotenv.h`)__!

4. Very similar with what Javascript does; The expansion engine roughly has the following rules:

- `$KEY` will expand any env with the name `KEY`
- `${KEY}` will expand any env with the name `KEY`
- `\$KEY` will escape the $KEY rather than expand
- `${KEY:-default}` will first attempt to expand any env with the name KEY. If not one, then it will return default

5. If you decide to make an empty variable like this:
```env
EMPTY=
```

... so remember to have a space like this:
```env
EMPTY= # A space after the '=' symbol
```

### 👷 Support platform
- Microsoft Windows 10 `x64/x86`
- Visual Studio platform `x64/x86`
Expand Down
11 changes: 11 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Security Policy

## Supported Versions

| Name | Version | Supported |
| ---------------------- | ------------ | ------------------ |
| dotenv.h | 1.2.0 | [x] |

## Reporting a Vulnerability

To report a vulnerability please contact the development team by using Github Issue or Discord server. We will discuss and fix it ASAP. Once the fix has been put into a release version, the vulnerability will be made public.
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: 1.1.0.{build}
version: 1.2.0.{build}
image: Visual Studio 2022
environment:
matrix:
Expand Down
129 changes: 117 additions & 12 deletions include/dotenv/dotenv.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <fstream>
#include <regex>
#include <string>
#include <iostream>
#include <unordered_map>

namespace dotenv {
Expand Down Expand Up @@ -63,20 +64,124 @@ namespace dotenv {
}
map[key] = value;
}
else {
// If the value is an empty string, check if the variable already exists
char* env_value = std::getenv(key.c_str());
if (env_value == nullptr) {
// If the variable does not exist, unset it
_putenv((key + "=").c_str());
}
else {
// If the variable exists, set it with an empty string value
_putenv((key + "=" + value).c_str());
}
}
}

// Substitute environment variables
for (auto& pair : map) {
std::string value = pair.second;
size_t pos = 0;

while ((pos = value.find("$", pos)) != std::string::npos) {
if (pos + 1 < value.size()) {
if (value[pos + 1] == '$') {
// Handle escaped $
value.erase(pos, 1);
++pos;
}
else if (value[pos + 1] == '{') {
// Substitution with curly braces
size_t end = value.find("}", pos + 2);
if (end != std::string::npos) {
std::string key_with_default = value.substr(pos + 2, end - pos - 2);
size_t colon_pos = key_with_default.find(":");
std::string key, default_value;

if (colon_pos == std::string::npos) {
// No default value provided
key = key_with_default;
if (map.find(key) != map.end()) {
// Key found in map, substitute with value
value.replace(pos, end - pos + 1, map[key]);
pos += map[key].size();
}
else {
// Key not found in map, remove substitution
value.erase(pos, end - pos + 1);
}
}
else {
// Default value provided
key = key_with_default.substr(0, colon_pos);
std::string default_value = key_with_default.substr(colon_pos + 1);
if (map.find(key) != map.end()) {
// Key found in map, substitute with value
value.replace(pos, end - pos + 1, map[key]);
pos += map[key].size();
}
else {
// Key not found in map, substitute with default value
value.replace(pos, end - pos + 1, default_value);
pos += default_value.size();
}
}
}
else {
// No matching } found, remove substitution
value.erase(pos, 2);
}
}
else {
// Substitution without curly braces
std::string key = value.substr(pos + 1, 1);
if (map.find(key) != map.end()) {
// Key found in map, substitute with value
value.replace(pos, 2, map[key]);
pos += map[key].size();
}
else {
// Key not found in map, remove substitution
value.erase(pos, 2);
}
}
}
else {
// $ is at end of string, remove substitution
value.erase(pos, 1);
}
}
pair.second = value;
}
return map;
}

// Load .env file and populate environment variables
void load(const std::string& path) {
std::ifstream file(path);
if (file.is_open()) {
std::string contents((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
std::unordered_map<std::string, std::string> map = parse(contents);
for (const auto& pair : map) {
_putenv((pair.first + "=" + pair.second).c_str());
}
}
}
}
// Load .env file and populate environment variables
void load(const std::string& path) {
std::ifstream file(path);
if (file.is_open()) {
std::string contents((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
std::unordered_map<std::string, std::string> map = parse(contents);
for (const auto& pair : map) {
std::string key = pair.first;
std::string value = pair.second;
if (value.empty()) {
// If the value is an empty string, set it to the default value if available
size_t colon_pos = key.find(":");
std::string env_key, default_value;
if (colon_pos != std::string::npos) {
env_key = key.substr(0, colon_pos);
default_value = key.substr(colon_pos + 1);
}
else {
env_key = key;
}
if (!default_value.empty()) {
value = default_value;
}
}
_putenv((key + "=" + value).c_str());
}
}
}
}
14 changes: 12 additions & 2 deletions test/.env
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
# Normal test:
MY_BOOL=true
MY_INT=42
MY_LONG=-123456789012345
MY_STRING="Hello, world!"
MY_STRING='Hello World!'
MY_EMPTY_STRING= # with a space

# Special string:
MY_HARSHFEUDAL="\n-----Harshfeudal-----\n\t\"Hello\"\n-----Complete!-----\n" # This is comment, it will not log
MY_HASH="something-with-a-#-hash"
MY_SPACING="\nNew Line\tTabs\n-------Hey!\r\"What\""
MY_SPACING="\nNew Line\tTabs\n-------Hey!\r\"What\""

# Complex:
MY_VAR_1="Hi"
MY_VAR_2="Harshfeudal"

MY_COMPLEX=${MY_VAR_1} ${MY_VAR_2}
149 changes: 30 additions & 119 deletions test/test.cpp
Original file line number Diff line number Diff line change
@@ -1,123 +1,34 @@
#include <dotenv/dotenv.h>

#include <iostream>
#include <chrono>

class Timer
{
public:
Timer() { m_StartTimepoint = std::chrono::high_resolution_clock::now(); }
~Timer() { Stop(); }

void Stop() {
auto endTimepoint = std::chrono::high_resolution_clock::now();

auto start = std::chrono::time_point_cast<std::chrono::microseconds>(m_StartTimepoint).time_since_epoch().count();
auto end = std::chrono::time_point_cast<std::chrono::microseconds>(endTimepoint).time_since_epoch().count();

auto duration = end - start;
double ms = duration * 0.001;

std::cout << duration << "us (" << ms << "ms)\n";
}

private:
std::chrono::time_point<std::chrono::high_resolution_clock> m_StartTimepoint;
};
#include "test.h"

void run_test(const std::string& name) {
std::cout << name << " test: " << std::endl;
{
Timer timer;
const char* my_var = std::getenv(("MY_" + name).c_str());
if (my_var) {
std::cout << "MY_RESULT=" << my_var << std::endl;
}
else {
std::cerr << "MY_RESULT not found in environment" << std::endl;
}
}
std::cout << std::endl;
}

int main() {
dotenv::load("./test/.env");

std::cout << "Bool test: " << std::endl;
{
Timer timer;
const char* my_var = std::getenv("MY_BOOL");
if (my_var) {
std::cout << "MY_RESULT=" << my_var << std::endl;
}
else {
std::cerr << "MY_RESULT not found in environment" << std::endl;
}
}
std::cout << std::endl;

std::cout << "Int test: " << std::endl;
{
Timer timer;
const char* my_var = std::getenv("MY_INT");
if (my_var) {
std::cout << "MY_RESULT=" << my_var << std::endl;
}
else {
std::cerr << "MY_RESULT not found in environment" << std::endl;
}
}
std::cout << std::endl;

std::cout << "Long test: " << std::endl;
{
Timer timer;
const char* my_var = std::getenv("MY_LONG");
if (my_var) {
std::cout << "MY_RESULT=" << my_var << std::endl;
}
else {
std::cerr << "MY_RESULT not found in environment" << std::endl;
}
}
std::cout << std::endl;

std::cout << "String test: " << std::endl;
{
Timer timer;
const char* my_var = std::getenv("MY_STRING");
if (my_var) {
std::cout << "MY_RESULT=" << my_var << std::endl;
}
else {
std::cerr << "MY_RESULT not found in environment" << std::endl;
}
}
std::cout << std::endl;

std::cout << "Harshfeudal test: " << std::endl;
{
Timer timer;
const char* my_var = std::getenv("MY_HARSHFEUDAL");
if (my_var) {
std::cout << "MY_RESULT=" << my_var << std::endl;
}
else {
std::cerr << "MY_RESULT not found in environment" << std::endl;
}
}
std::cout << std::endl;

std::cout << "Hash test: " << std::endl;
{
Timer timer;
const char* my_var = std::getenv("MY_HASH");
if (my_var) {
std::cout << "MY_RESULT=" << my_var << std::endl;
}
else {
std::cerr << "MY_RESULT not found in environment" << std::endl;
}
}
std::cout << std::endl;

std::cout << "Spacing test: " << std::endl;
{
Timer timer;
const char* my_var = std::getenv("MY_SPACING");
if (my_var) {
std::cout << "MY_RESULT=" << my_var << std::endl;
}
else {
std::cerr << "MY_RESULT not found in environment" << std::endl;
}
}
std::cout << std::endl;

return 0;
}
dotenv::load("./test/.env");

run_test("BOOL");
run_test("INT");
run_test("LONG");
run_test("STRING");
run_test("HARSHFEUDAL");
run_test("HASH");
run_test("SPACING");
run_test("COMPLEX");
run_test("EMPTY_STRING");

return 0;
}
Loading

0 comments on commit 74fda81

Please sign in to comment.