mirror of
https://github.com/tomasriveral/NoteWrapper.git
synced 2026-08-11 18:18:36 +02:00
Merge (#19) from tomasriveral/clangd-test CI: add clang-format check
add documentation about this test add .clang-format file add build flag -Werror add clang-tools to shell.nix format whole project
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
BasedOnStyle: LLVM
|
||||
IndentWidth: 4
|
||||
ColumnLimit: 100
|
||||
@@ -10,10 +10,18 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install clang
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y clang-format
|
||||
- name: Check format
|
||||
run: |
|
||||
clang-format --version
|
||||
find src -name "*.c" -o -name "*.h" | xargs clang-format --dry-run --Werror
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
gcc \
|
||||
make \
|
||||
|
||||
@@ -85,3 +85,12 @@ Before submitting a pull request, ensure that:
|
||||
* `make`, or
|
||||
* `nix-build` (on NixOS)
|
||||
* There are **no warnings or errors**
|
||||
|
||||
---
|
||||
|
||||
## Continuous integration
|
||||
|
||||
All PR will need to pass these checks:
|
||||
1. `clang-format`. Running `find src -name "*.c" -o -name "*.h" | xargs clang-format -i` will automatically apply those rules.
|
||||
2. The program must build without warning or error.
|
||||
3. The program must run for five seconds without crashing.
|
||||
|
||||
@@ -2,7 +2,7 @@ CC := gcc
|
||||
|
||||
VERSION := $(shell git describe --tags --always --dirty)
|
||||
|
||||
CFLAGS := -Wall -Wextra -O2 \
|
||||
CFLAGS := -Wall -Wextra -Werror -O2 \
|
||||
-DVERSION=\"$(VERSION)\" \
|
||||
$(shell pkg-config --cflags libcjson ncurses)
|
||||
|
||||
|
||||
@@ -8,5 +8,6 @@ pkgs.mkShell {
|
||||
pkgs.gdb
|
||||
pkgs.valgrind
|
||||
pkgs.kdePackages.kcachegrind
|
||||
pkgs.clang-tools
|
||||
];
|
||||
}
|
||||
|
||||
+566
-428
File diff suppressed because it is too large
Load Diff
+301
-224
@@ -1,67 +1,81 @@
|
||||
#include "notes.h"
|
||||
#include "utils.h"
|
||||
|
||||
char *getDirectoryFromVault(char *targetVault, char **vaultsArray, int vaultTotalNumber, int *vaultNumberPerDirectory, char **directoryArray, int directoryNumber, int shouldDebug) {
|
||||
debug("Searching the vault %s inside all the directories...", targetVault);
|
||||
debug("Here are how many vaults there is per directory:");
|
||||
for (int i = 0; i < directoryNumber; i++) {
|
||||
altDebug("%d for %s\n", vaultNumberPerDirectory[i], directoryArray[i]);
|
||||
}
|
||||
debug("Here are all the vaults in the order they will be searched:");
|
||||
for (int i = 0; i < vaultTotalNumber; i++) {
|
||||
altDebug("%s\n", vaultsArray[i]);
|
||||
}
|
||||
int index = 0;
|
||||
char *getDirectoryFromVault(char *targetVault, char **vaultsArray, int vaultTotalNumber,
|
||||
int *vaultNumberPerDirectory, char **directoryArray,
|
||||
int directoryNumber, int shouldDebug) {
|
||||
debug("Searching the vault %s inside all the directories...", targetVault);
|
||||
debug("Here are how many vaults there is per directory:");
|
||||
for (int i = 0; i < directoryNumber; i++) {
|
||||
altDebug("%d for %s\n", vaultNumberPerDirectory[i], directoryArray[i]);
|
||||
}
|
||||
debug("Here are all the vaults in the order they will be searched:");
|
||||
for (int i = 0; i < vaultTotalNumber; i++) {
|
||||
altDebug("%s\n", vaultsArray[i]);
|
||||
}
|
||||
int index = 0;
|
||||
|
||||
for (int i = 0; i < directoryNumber; i++) {
|
||||
for (int j = 0; j < vaultNumberPerDirectory[i]; j++) {
|
||||
if (strcmp(vaultsArray[index], targetVault) == 0) {
|
||||
debug("%s found in %s. (index %d)", targetVault, directoryArray[i], index);
|
||||
return directoryArray[i];
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
error(1, "program", "the vault %s was not found", targetVault);
|
||||
return "this makes the compiler and clangd happy. Who doesn't want GCC and clangd to be happy? Such person would be a terrible monster... One must imagine GCC and clangd happy.";
|
||||
for (int i = 0; i < directoryNumber; i++) {
|
||||
for (int j = 0; j < vaultNumberPerDirectory[i]; j++) {
|
||||
if (strcmp(vaultsArray[index], targetVault) == 0) {
|
||||
debug("%s found in %s. (index %d)", targetVault, directoryArray[i], index);
|
||||
return directoryArray[i];
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
error(1, "program", "the vault %s was not found", targetVault);
|
||||
return "this makes the compiler and clangd happy. Who doesn't want GCC and clangd to be happy? "
|
||||
"Such person would be a terrible monster... One must imagine GCC and clangd happy.";
|
||||
}
|
||||
|
||||
char **getJournalsFromVault(char *pathToVault, char *vault, char *journalRegex, int *count, int shouldDebug) {
|
||||
char **getJournalsFromVault(char *pathToVault, char *vault, char *journalRegex, int *count,
|
||||
int shouldDebug) {
|
||||
debug("Searching %s for journals", vault);
|
||||
// originally from https://www.geeksforgeeks.org/c/c-program-list-files-sub-directories-directory/
|
||||
// originally from
|
||||
// https://www.geeksforgeeks.org/c/c-program-list-files-sub-directories-directory/
|
||||
struct dirent *vaultEntry;
|
||||
char tempPath[PATH_MAX];
|
||||
snprintf(tempPath, sizeof(tempPath), "%s/%s", pathToVault, vault); // sets the full absolute path to fullPathEntry
|
||||
snprintf(tempPath, sizeof(tempPath), "%s/%s", pathToVault,
|
||||
vault); // sets the full absolute path to fullPathEntry
|
||||
DIR *vaultDirectory = opendir(tempPath);
|
||||
error(vaultDirectory==NULL, "program", "Could not open directory %s", tempPath);
|
||||
error(vaultDirectory == NULL, "program", "Could not open directory %s", tempPath);
|
||||
char **journalsArray = NULL; // will contain all the notes
|
||||
int journalsCount = 0; // we need to count how many notes there is to always readjust how many memory we alloc
|
||||
|
||||
int journalsCount =
|
||||
0; // we need to count how many notes there is to always readjust how many memory we alloc
|
||||
|
||||
// https://stackoverflow.com/a/1085120 for regex code
|
||||
regex_t regex;
|
||||
int regexReturn;
|
||||
// compiles the regex
|
||||
regexReturn = regcomp(®ex, journalRegex, 0);
|
||||
error(regexReturn, "program", "Regex could not compile. Perhaps there is an error with the regex string");
|
||||
error(regexReturn, "program",
|
||||
"Regex could not compile. Perhaps there is an error with the regex string");
|
||||
debug("Regex compiled succesfully");
|
||||
// Refer https://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
|
||||
// for readdir()
|
||||
debug("┌------------------------------------------\nDetected files and dirs from the vault");
|
||||
while ((vaultEntry = readdir(vaultDirectory)) != NULL) { // we iterate over every entry from the dir. So files and dirs (. and .. included)
|
||||
altDebug("%s ", vaultEntry->d_name);
|
||||
if (vaultEntry->d_name[0] != '.') { // if the entry don't start with a dot (so hidden dirs and hidden files)
|
||||
regexReturn = regexec(®ex, vaultEntry->d_name, 0, NULL, 0);
|
||||
if (!regexReturn) { // if the regex matches
|
||||
altDebug("matched with the regex. It is a journal.\n");
|
||||
journalsArray = realloc(journalsArray, (journalsCount + 1)*sizeof(char*)); // resize notesArray so that
|
||||
journalsArray[journalsCount] = strdup(vaultEntry->d_name); // copy the dir name into notesArray
|
||||
journalsCount++;
|
||||
while (
|
||||
(vaultEntry = readdir(vaultDirectory)) !=
|
||||
NULL) { // we iterate over every entry from the dir. So files and dirs (. and .. included)
|
||||
altDebug("%s ", vaultEntry->d_name);
|
||||
if (vaultEntry->d_name[0] !=
|
||||
'.') { // if the entry don't start with a dot (so hidden dirs and hidden files)
|
||||
regexReturn = regexec(®ex, vaultEntry->d_name, 0, NULL, 0);
|
||||
if (!regexReturn) { // if the regex matches
|
||||
altDebug("matched with the regex. It is a journal.\n");
|
||||
journalsArray =
|
||||
realloc(journalsArray,
|
||||
(journalsCount + 1) * sizeof(char *)); // resize notesArray so that
|
||||
journalsArray[journalsCount] =
|
||||
strdup(vaultEntry->d_name); // copy the dir name into notesArray
|
||||
journalsCount++;
|
||||
} else {
|
||||
altDebug("did not matched with the regex. It is a note.\n");
|
||||
}
|
||||
} else {
|
||||
altDebug("did not matched with the regex. It is a note.\n");
|
||||
altDebug(" was ignored\n");
|
||||
}
|
||||
} else {
|
||||
altDebug(" was ignored\n");
|
||||
}
|
||||
}
|
||||
altDebug("└ ------------------------------\n");
|
||||
// free's some used memory
|
||||
@@ -70,42 +84,57 @@ char **getJournalsFromVault(char *pathToVault, char *vault, char *journalRegex,
|
||||
return journalsArray;
|
||||
}
|
||||
|
||||
char** getNotesFromVault(char *pathToVault, char *vault, char *journalRegex, int *count, int shouldDebug) {
|
||||
// this function is inputed a path to a vault (which was selected before) and outpus all the suitable notes (so not the hidden ones)
|
||||
// originally from https://www.geeksforgeeks.org/c/c-program-list-files-sub-directories-directory/
|
||||
char **getNotesFromVault(char *pathToVault, char *vault, char *journalRegex, int *count,
|
||||
int shouldDebug) {
|
||||
// this function is inputed a path to a vault (which was selected before) and outpus all the
|
||||
// suitable notes (so not the hidden ones) originally from
|
||||
// https://www.geeksforgeeks.org/c/c-program-list-files-sub-directories-directory/
|
||||
debug("Searching %s for notes", vault);
|
||||
struct dirent *vaultEntry;
|
||||
struct dirent *vaultEntry;
|
||||
char tempPath[PATH_MAX];
|
||||
snprintf(tempPath, sizeof(tempPath), "%s/%s", pathToVault, vault); // sets the full absolute path to fullPathEntry
|
||||
snprintf(tempPath, sizeof(tempPath), "%s/%s", pathToVault,
|
||||
vault); // sets the full absolute path to fullPathEntry
|
||||
DIR *vaultDirectory = opendir(tempPath);
|
||||
error(vaultDirectory==NULL, "program", "Could not open directory %s", tempPath);
|
||||
error(vaultDirectory == NULL, "program", "Could not open directory %s", tempPath);
|
||||
char **notesArray = NULL; // will contain all the notes
|
||||
int notesCount = 0; // we need to count how many notes there is to always readjust how many memory we alloc
|
||||
int notesCount =
|
||||
0; // we need to count how many notes there is to always readjust how many memory we alloc
|
||||
// Refer https://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
|
||||
// for readdir()
|
||||
debug("┌------------------------------\nDetected Files and dirs from the vault:");
|
||||
while ((vaultEntry = readdir(vaultDirectory)) != NULL) {
|
||||
char *entryName = vaultEntry->d_name;
|
||||
int entryLenght = strlen(entryName);
|
||||
altDebug("%s ", entryName);
|
||||
// for the regex code https://stackoverflow.com/a/1085120
|
||||
regex_t regex;
|
||||
int regexReturn;
|
||||
// compiles the regex
|
||||
regexReturn = regcomp(®ex, journalRegex, 0);
|
||||
if (entryName[0] != '.') { // if the entry don't start with a dot (so hidden dirs and hidden files)
|
||||
char fullPathEntry[PATH_MAX]; // creates a string of size of the maximum path lenght
|
||||
snprintf(fullPathEntry, sizeof(fullPathEntry), "%s/%s/%s", pathToVault, vault, entryName); // sets the full absolute path to fullPathEntry
|
||||
// check if it matches the regex. If it does not match regexReturn != 0.
|
||||
regexReturn = regexec(®ex, entryName, 0, NULL, 0);
|
||||
struct stat metadataPathEntry;
|
||||
if (stat(fullPathEntry, &metadataPathEntry) == 0 && entryName[entryLenght - 3] == '.' && entryName[entryLenght - 2] == 'm' && entryName[entryLenght - 1] == 'd' && regexReturn && S_ISREG(metadataPathEntry.st_mode)) { // if this entry is a file ending in .md and that does no match the regex
|
||||
notesArray = realloc(notesArray, (notesCount + 1)*sizeof(char*)); // resize notesArray so that
|
||||
notesArray[notesCount] = strdup(entryName); // copy the dir name into notesArray
|
||||
notesCount++;
|
||||
altDebug("did not match with the regex. It is a note.\n");
|
||||
} else if (!regexReturn) {altDebug("matched with the regex. It is a journal.\n");}
|
||||
} else {altDebug("was ignored\n");}
|
||||
char *entryName = vaultEntry->d_name;
|
||||
int entryLenght = strlen(entryName);
|
||||
altDebug("%s ", entryName);
|
||||
// for the regex code https://stackoverflow.com/a/1085120
|
||||
regex_t regex;
|
||||
int regexReturn;
|
||||
// compiles the regex
|
||||
regexReturn = regcomp(®ex, journalRegex, 0);
|
||||
if (entryName[0] !=
|
||||
'.') { // if the entry don't start with a dot (so hidden dirs and hidden files)
|
||||
char fullPathEntry[PATH_MAX]; // creates a string of size of the maximum path lenght
|
||||
snprintf(fullPathEntry, sizeof(fullPathEntry), "%s/%s/%s", pathToVault, vault,
|
||||
entryName); // sets the full absolute path to fullPathEntry
|
||||
// check if it matches the regex. If it does not match regexReturn != 0.
|
||||
regexReturn = regexec(®ex, entryName, 0, NULL, 0);
|
||||
struct stat metadataPathEntry;
|
||||
if (stat(fullPathEntry, &metadataPathEntry) == 0 && entryName[entryLenght - 3] == '.' &&
|
||||
entryName[entryLenght - 2] == 'm' && entryName[entryLenght - 1] == 'd' &&
|
||||
regexReturn &&
|
||||
S_ISREG(metadataPathEntry.st_mode)) { // if this entry is a file ending in .md and
|
||||
// that does no match the regex
|
||||
notesArray = realloc(notesArray, (notesCount + 1) *
|
||||
sizeof(char *)); // resize notesArray so that
|
||||
notesArray[notesCount] = strdup(entryName); // copy the dir name into notesArray
|
||||
notesCount++;
|
||||
altDebug("did not match with the regex. It is a note.\n");
|
||||
} else if (!regexReturn) {
|
||||
altDebug("matched with the regex. It is a journal.\n");
|
||||
}
|
||||
} else {
|
||||
altDebug("was ignored\n");
|
||||
}
|
||||
}
|
||||
altDebug("└ ------------------------------\n");
|
||||
|
||||
@@ -115,166 +144,214 @@ char** getNotesFromVault(char *pathToVault, char *vault, char *journalRegex, int
|
||||
return notesArray;
|
||||
}
|
||||
|
||||
char **getVaultsFromDirectories(char **directoryStringArray, int directoryNumber, int *vaultsPerDirectoryNumber, int *count, int shouldDebug) {
|
||||
// to avoid having to work with a tree-dimensional array. We will use a 2d and vaultsPerDirectoryNumber will indicate the width of the directories.
|
||||
char **vaultsArray = NULL;
|
||||
int nthVault = 0; // this is only used internally to set the string into the right place in directoryStringArray.
|
||||
int previousStartIndex = 0;
|
||||
for (int i = 0; i < directoryNumber; i++) {
|
||||
debug("Opening %s", directoryStringArray[i]);
|
||||
// originally from https://www.geeksforgeeks.org/c/c-program-list-files-sub-directories-directory/
|
||||
struct dirent *vaultsDirectoryEntry;
|
||||
DIR *vaultsDirectory = opendir(directoryStringArray[i]);
|
||||
error(!vaultsDirectory, "program", "Could not open directory %s", directoryStringArray[i]);
|
||||
vaultsPerDirectoryNumber[i] = 0;
|
||||
debug("┌------------------------------\n Detected files and dirs %s:", directoryStringArray[i]);
|
||||
// Refer https://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
|
||||
// for readdir()
|
||||
while((vaultsDirectoryEntry = readdir(vaultsDirectory)) != NULL) {
|
||||
char *entryName = vaultsDirectoryEntry->d_name; // gets the entry as a string value
|
||||
altDebug("%s", entryName);
|
||||
if (entryName[0] != '.') { // if not hidden file/dir
|
||||
char tempFullEntryPath[PATH_MAX]; // we recreate the full path to check it's proprieties
|
||||
snprintf(tempFullEntryPath, PATH_MAX, "%s%s", directoryStringArray[i], entryName);
|
||||
altDebug(" (%s)", tempFullEntryPath);
|
||||
//checking the metadata to see if it is a dir
|
||||
struct stat metadataEntry;
|
||||
if (stat(tempFullEntryPath, &metadataEntry) == 0 && S_ISDIR(metadataEntry.st_mode)) { // get's the metadata (stat should return 0 if fails) and sees if it is a dir.
|
||||
altDebug(" is a vault");
|
||||
vaultsArray = realloc(vaultsArray, sizeof(char *)*(nthVault + 1)); // resize vaultsArray
|
||||
error(vaultsArray == NULL, "program", "realloc failed");
|
||||
vaultsPerDirectoryNumber[i]++; // it will be used later to know which vaults goes into which directory
|
||||
vaultsArray[nthVault] = strdup(entryName); // we use strdup and not strcpy, because memory used with opendir and readdir will be closed.
|
||||
error(vaultsArray[nthVault] == NULL, "program", "strdup failed");
|
||||
nthVault++; // it is used immediatly to set the vault into directoryStringArray
|
||||
} else {
|
||||
altDebug(" is not a vault (not a dir.)");
|
||||
char **getVaultsFromDirectories(char **directoryStringArray, int directoryNumber,
|
||||
int *vaultsPerDirectoryNumber, int *count, int shouldDebug) {
|
||||
// to avoid having to work with a tree-dimensional array. We will use a 2d and
|
||||
// vaultsPerDirectoryNumber will indicate the width of the directories.
|
||||
char **vaultsArray = NULL;
|
||||
int nthVault = 0; // this is only used internally to set the string into the right place in
|
||||
// directoryStringArray.
|
||||
int previousStartIndex = 0;
|
||||
for (int i = 0; i < directoryNumber; i++) {
|
||||
debug("Opening %s", directoryStringArray[i]);
|
||||
// originally from
|
||||
// https://www.geeksforgeeks.org/c/c-program-list-files-sub-directories-directory/
|
||||
struct dirent *vaultsDirectoryEntry;
|
||||
DIR *vaultsDirectory = opendir(directoryStringArray[i]);
|
||||
error(!vaultsDirectory, "program", "Could not open directory %s", directoryStringArray[i]);
|
||||
vaultsPerDirectoryNumber[i] = 0;
|
||||
debug("┌------------------------------\n Detected files and dirs %s:",
|
||||
directoryStringArray[i]);
|
||||
// Refer https://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
|
||||
// for readdir()
|
||||
while ((vaultsDirectoryEntry = readdir(vaultsDirectory)) != NULL) {
|
||||
char *entryName = vaultsDirectoryEntry->d_name; // gets the entry as a string value
|
||||
altDebug("%s", entryName);
|
||||
if (entryName[0] != '.') { // if not hidden file/dir
|
||||
char tempFullEntryPath[PATH_MAX]; // we recreate the full path to check it's
|
||||
// proprieties
|
||||
snprintf(tempFullEntryPath, PATH_MAX, "%s%s", directoryStringArray[i], entryName);
|
||||
altDebug(" (%s)", tempFullEntryPath);
|
||||
// checking the metadata to see if it is a dir
|
||||
struct stat metadataEntry;
|
||||
if (stat(tempFullEntryPath, &metadataEntry) == 0 &&
|
||||
S_ISDIR(metadataEntry.st_mode)) { // get's the metadata (stat should return 0 if
|
||||
// fails) and sees if it is a dir.
|
||||
altDebug(" is a vault");
|
||||
vaultsArray =
|
||||
realloc(vaultsArray, sizeof(char *) * (nthVault + 1)); // resize vaultsArray
|
||||
error(vaultsArray == NULL, "program", "realloc failed");
|
||||
vaultsPerDirectoryNumber[i]++; // it will be used later to know which vaults
|
||||
// goes into which directory
|
||||
vaultsArray[nthVault] =
|
||||
strdup(entryName); // we use strdup and not strcpy, because memory used with
|
||||
// opendir and readdir will be closed.
|
||||
error(vaultsArray[nthVault] == NULL, "program", "strdup failed");
|
||||
nthVault++; // it is used immediatly to set the vault into directoryStringArray
|
||||
} else {
|
||||
altDebug(" is not a vault (not a dir.)");
|
||||
}
|
||||
} else {
|
||||
altDebug(" is not a vault (hidden file/dir)");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
altDebug(" is not a vault (hidden file/dir)");
|
||||
}
|
||||
// we sort entries for the same vault. We can't sort them all. This would break the order.
|
||||
qsort(vaultsArray + previousStartIndex, nthVault - previousStartIndex, sizeof(const char *),
|
||||
compareString); // sorts the vaults alphabetically
|
||||
closedir(vaultsDirectory);
|
||||
previousStartIndex = nthVault;
|
||||
}
|
||||
// we sort entries for the same vault. We can't sort them all. This would break the order.
|
||||
qsort(vaultsArray + previousStartIndex, nthVault - previousStartIndex, sizeof(const char *), compareString); // sorts the vaults alphabetically
|
||||
closedir(vaultsDirectory);
|
||||
previousStartIndex = nthVault;
|
||||
}
|
||||
// we calculate once the total number of vaults to avoid recalculation every time we use it
|
||||
*count = 0;
|
||||
for (int i = 0; i < directoryNumber; i++) {
|
||||
*count += vaultsPerDirectoryNumber[i];
|
||||
}
|
||||
return vaultsArray;
|
||||
// we calculate once the total number of vaults to avoid recalculation every time we use it
|
||||
*count = 0;
|
||||
for (int i = 0; i < directoryNumber; i++) {
|
||||
*count += vaultsPerDirectoryNumber[i];
|
||||
}
|
||||
return vaultsArray;
|
||||
}
|
||||
|
||||
char *updateJournal(char *path, char *journal, char *timeFormat, int *journalWasUpdated, int shouldDebug) {
|
||||
path[PATH_MAX] = '\0'; // it assures it is a string (Most cases this does nothing). But rewriting at least one bytes make the compile happy. He doesn't want to return an unchanged input.
|
||||
debug("Handling the journal %s", path);
|
||||
char *updateJournal(char *path, char *journal, char *timeFormat, int *journalWasUpdated,
|
||||
int shouldDebug) {
|
||||
path[PATH_MAX] =
|
||||
'\0'; // it assures it is a string (Most cases this does nothing). But rewriting at least
|
||||
// one bytes make the compile happy. He doesn't want to return an unchanged input.
|
||||
debug("Handling the journal %s", path);
|
||||
|
||||
char *date = getFormatedTime(timeFormat, shouldDebug);
|
||||
debug("Formated time for the journal's entry is %s", date);
|
||||
char dateWithExtension[PATH_MAX];
|
||||
snprintf(dateWithExtension, PATH_MAX, "%s.md", date);
|
||||
sanitize(dateWithExtension);
|
||||
debug("Sanitized date: %s\n(it might be used later for a file name if the journal is divided)", dateWithExtension);
|
||||
struct stat metadata;
|
||||
stat(path, &metadata);
|
||||
if (S_ISREG(metadata.st_mode)) {
|
||||
debug("%s is a unified journal.", path);
|
||||
if (!isStringInFile(path, date, shouldDebug)) { // if there is no entry for current date
|
||||
appendToFile(path, date, shouldDebug);
|
||||
*journalWasUpdated = 1;
|
||||
} // if there is an entry do nothing
|
||||
} else if (S_ISDIR(metadata.st_mode)) {
|
||||
debug("%s is a divided journal.", path);
|
||||
struct dirent *dividedJournalEntry;
|
||||
// snprintf does not like to have the same variable as input and output so we use a buffer
|
||||
char temp[PATH_MAX];
|
||||
snprintf(temp, PATH_MAX, "%s/", path); // appends a /. For safety
|
||||
strncpy(path, temp, PATH_MAX);
|
||||
DIR *dividedJournalDirectory = opendir(path);
|
||||
error(dividedJournalDirectory==NULL, "program", "Could not open directory %s", path);
|
||||
/*char **entryArray = NULL; // will contain all the entries of the divided journal
|
||||
// This time it will be different. We must add "invert" the extraOptions to the options so that the Create new entry in journal is on top
|
||||
entryArray = realloc(entryArray, sizeof(char*));*/
|
||||
// simpler to just malloc 2 and later realloc
|
||||
const int extraOptions = 3;
|
||||
char **entryArray = malloc(extraOptions*sizeof(char*));
|
||||
char *createEntryMessage = malloc(PATH_MAX);
|
||||
snprintf(createEntryMessage, PATH_MAX, "Create new entry for the journal %s", journal);
|
||||
entryArray[0] = createEntryMessage;
|
||||
entryArray[1] = "Open random entry";
|
||||
entryArray[2] = "Search inside entries";
|
||||
int entryCount = extraOptions; // we need to count how many dirs there is to always readjust how many memory we alloc
|
||||
// Refer https://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
|
||||
// for readdir()
|
||||
debug("┌------------------------------\n Detected files and dirs from %s:", path);
|
||||
// iterates over all the entries from the dir
|
||||
while ((dividedJournalEntry = readdir(dividedJournalDirectory)) != NULL) {
|
||||
altDebug("%s\n", dividedJournalEntry->d_name);
|
||||
if (dividedJournalEntry->d_name[0] != '.') { // if the entry don't start with a dot (so hidden dirs and hidden files)
|
||||
char fullPathEntry[PATH_MAX]; // creates a string of size of the maximum path lenght
|
||||
snprintf(fullPathEntry, sizeof(fullPathEntry), "%s/%s", path, dividedJournalEntry->d_name); // sets the full absolute path to fullPathEntry
|
||||
|
||||
struct stat metadataPathEntry;
|
||||
if (stat(fullPathEntry, &metadataPathEntry) == 0 && S_ISREG(metadataPathEntry.st_mode)) { // if this entry is a directory
|
||||
entryArray = realloc(entryArray, (entryCount + 1)*sizeof(char*));
|
||||
entryArray[entryCount] = strdup(dividedJournalEntry->d_name); // copy the dir name into entryArray
|
||||
entryCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
altDebug("└------------------------------\n");
|
||||
// free's some used memory
|
||||
closedir(dividedJournalDirectory);
|
||||
qsort(entryArray + extraOptions, entryCount - extraOptions, sizeof(const char *), reverseCompareString); // sorts the journals entries alphabetically. // the + extraOptions - extraOptions is to not sort "Create new entry for the journal" or Open random entry which is the first element
|
||||
|
||||
// we must now select to create new entry or to enter in old one
|
||||
char *selectedOption = ncursesSelect(entryArray, "Create new entry or acces old entry (Use arrows or WASD, Enter to select):", extraOptions, entryCount - extraOptions, " ", " ", "", shouldDebug); // the "Create new entry for the journal %s" will be the only options. All other will be extraOptions. This is made so that "Create [...] %s" will always be on top
|
||||
debug("Selected option from journal entry selection: %s", selectedOption);
|
||||
if (strcmp(selectedOption, createEntryMessage) == 0) { // create new entry
|
||||
char temp[PATH_MAX];
|
||||
error(strlen(path)+1+strlen(dateWithExtension)+1>PATH_MAX, "Error file path too long. %s/%s must not exceed PATH_MAX", path, dateWithExtension);
|
||||
snprintf(temp, PATH_MAX, "%s/%s", path, dateWithExtension);
|
||||
strncpy(path, temp, PATH_MAX);
|
||||
if (!isStringInArray(dateWithExtension, (const char **)entryArray, entryCount)) { // it only creates it if it doesn't already exist
|
||||
// if it does exist it will just pass the full path
|
||||
debug("Creating entry %s inside %s", date, path);
|
||||
FILE *file;
|
||||
file = fopen(path, "w");
|
||||
error(file == NULL, "program", "%s couldn't be created", path);
|
||||
debug("Writing %s\\n to %s", date, path);
|
||||
fprintf(file, "%s\n", date);
|
||||
fclose(file);
|
||||
free(createEntryMessage);
|
||||
*journalWasUpdated = 1;
|
||||
} else {
|
||||
debug("Today's entry (%s) already exist. We won't create a new one.", dateWithExtension);
|
||||
}
|
||||
} else if (strcmp(selectedOption, entryArray[1]) == 0) { // if we choosed to open a random entry
|
||||
// setting up the seed for rand()
|
||||
srand(time(NULL)); // not totaly random, just pseudorandom
|
||||
int randomEntry = extraOptions + (rand() % (entryCount - extraOptions)); // we get a random number between extraOptions and entryCount (so all journal entries and not extraOptions)
|
||||
char temp[PATH_MAX];
|
||||
snprintf(temp, PATH_MAX, "%s/%s", path, entryArray[randomEntry]); // reconstruct the path
|
||||
strncpy(path, temp, PATH_MAX);
|
||||
debug("Selected random entry %s. It's path is %s.", entryArray[randomEntry], path);
|
||||
} else if (strcmp(selectedOption, entryArray[2]) == 0) { // if we choosed to search
|
||||
char *temp = fzfSelect(path, "Input text to be searched", shouldDebug); // uses ripgrep and fzf to search inside the files
|
||||
char *selectedOption = malloc(PATH_MAX); // we can't just use path as both input and output of snprintf
|
||||
snprintf(selectedOption, PATH_MAX, "%s/%s", path, temp);
|
||||
path = strdup(selectedOption);
|
||||
free(selectedOption);
|
||||
} else { // we just recreate the path to the selected entry
|
||||
char *date = getFormatedTime(timeFormat, shouldDebug);
|
||||
debug("Formated time for the journal's entry is %s", date);
|
||||
char dateWithExtension[PATH_MAX];
|
||||
snprintf(dateWithExtension, PATH_MAX, "%s.md", date);
|
||||
sanitize(dateWithExtension);
|
||||
debug("Sanitized date: %s\n(it might be used later for a file name if the journal is divided)",
|
||||
dateWithExtension);
|
||||
struct stat metadata;
|
||||
stat(path, &metadata);
|
||||
if (S_ISREG(metadata.st_mode)) {
|
||||
debug("%s is a unified journal.", path);
|
||||
if (!isStringInFile(path, date, shouldDebug)) { // if there is no entry for current date
|
||||
appendToFile(path, date, shouldDebug);
|
||||
*journalWasUpdated = 1;
|
||||
} // if there is an entry do nothing
|
||||
} else if (S_ISDIR(metadata.st_mode)) {
|
||||
debug("%s is a divided journal.", path);
|
||||
struct dirent *dividedJournalEntry;
|
||||
// snprintf does not like to have the same variable as input and output so we use a buffer
|
||||
char temp[PATH_MAX];
|
||||
snprintf(temp, PATH_MAX, "%s/%s", path, selectedOption);
|
||||
snprintf(temp, PATH_MAX, "%s/", path); // appends a /. For safety
|
||||
strncpy(path, temp, PATH_MAX);
|
||||
debug("Returning path to the selected entry: %s", path);
|
||||
}
|
||||
} else {
|
||||
error(1, "program", "%s is not a file and not a directory.", path);
|
||||
}
|
||||
|
||||
return path;
|
||||
DIR *dividedJournalDirectory = opendir(path);
|
||||
error(dividedJournalDirectory == NULL, "program", "Could not open directory %s", path);
|
||||
/*char **entryArray = NULL; // will contain all the entries of the divided journal
|
||||
// This time it will be different. We must add "invert" the extraOptions to the options so
|
||||
that the Create new entry in journal is on top entryArray = realloc(entryArray,
|
||||
sizeof(char*));*/
|
||||
// simpler to just malloc 2 and later realloc
|
||||
const int extraOptions = 3;
|
||||
char **entryArray = malloc(extraOptions * sizeof(char *));
|
||||
char *createEntryMessage = malloc(PATH_MAX);
|
||||
snprintf(createEntryMessage, PATH_MAX, "Create new entry for the journal %s", journal);
|
||||
entryArray[0] = createEntryMessage;
|
||||
entryArray[1] = "Open random entry";
|
||||
entryArray[2] = "Search inside entries";
|
||||
int entryCount = extraOptions; // we need to count how many dirs there is to always readjust
|
||||
// how many memory we alloc
|
||||
// Refer https://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
|
||||
// for readdir()
|
||||
debug("┌------------------------------\n Detected files and dirs from %s:", path);
|
||||
// iterates over all the entries from the dir
|
||||
while ((dividedJournalEntry = readdir(dividedJournalDirectory)) != NULL) {
|
||||
altDebug("%s\n", dividedJournalEntry->d_name);
|
||||
if (dividedJournalEntry->d_name[0] !=
|
||||
'.') { // if the entry don't start with a dot (so hidden dirs and hidden files)
|
||||
char fullPathEntry[PATH_MAX]; // creates a string of size of the maximum path lenght
|
||||
snprintf(
|
||||
fullPathEntry, sizeof(fullPathEntry), "%s/%s", path,
|
||||
dividedJournalEntry->d_name); // sets the full absolute path to fullPathEntry
|
||||
|
||||
struct stat metadataPathEntry;
|
||||
if (stat(fullPathEntry, &metadataPathEntry) == 0 &&
|
||||
S_ISREG(metadataPathEntry.st_mode)) { // if this entry is a directory
|
||||
entryArray = realloc(entryArray, (entryCount + 1) * sizeof(char *));
|
||||
entryArray[entryCount] =
|
||||
strdup(dividedJournalEntry->d_name); // copy the dir name into entryArray
|
||||
entryCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
altDebug("└------------------------------\n");
|
||||
// free's some used memory
|
||||
closedir(dividedJournalDirectory);
|
||||
qsort(entryArray + extraOptions, entryCount - extraOptions, sizeof(const char *),
|
||||
reverseCompareString); // sorts the journals entries alphabetically. // the +
|
||||
// extraOptions - extraOptions is to not sort "Create new entry
|
||||
// for the journal" or Open random entry which is the first
|
||||
// element
|
||||
|
||||
// we must now select to create new entry or to enter in old one
|
||||
char *selectedOption = ncursesSelect(
|
||||
entryArray,
|
||||
"Create new entry or acces old entry (Use arrows or WASD, Enter to select):",
|
||||
extraOptions, entryCount - extraOptions, " ", " ", "",
|
||||
shouldDebug); // the "Create new entry for the journal %s" will be the only options. All
|
||||
// other will be extraOptions. This is made so that "Create [...] %s" will
|
||||
// always be on top
|
||||
debug("Selected option from journal entry selection: %s", selectedOption);
|
||||
if (strcmp(selectedOption, createEntryMessage) == 0) { // create new entry
|
||||
char temp[PATH_MAX];
|
||||
error(strlen(path) + 1 + strlen(dateWithExtension) + 1 > PATH_MAX,
|
||||
"Error file path too long. %s/%s must not exceed PATH_MAX", path,
|
||||
dateWithExtension);
|
||||
snprintf(temp, PATH_MAX, "%s/%s", path, dateWithExtension);
|
||||
strncpy(path, temp, PATH_MAX);
|
||||
if (!isStringInArray(dateWithExtension, (const char **)entryArray,
|
||||
entryCount)) { // it only creates it if it doesn't already exist
|
||||
// if it does exist it will just pass the full path
|
||||
debug("Creating entry %s inside %s", date, path);
|
||||
FILE *file;
|
||||
file = fopen(path, "w");
|
||||
error(file == NULL, "program", "%s couldn't be created", path);
|
||||
debug("Writing %s\\n to %s", date, path);
|
||||
fprintf(file, "%s\n", date);
|
||||
fclose(file);
|
||||
free(createEntryMessage);
|
||||
*journalWasUpdated = 1;
|
||||
} else {
|
||||
debug("Today's entry (%s) already exist. We won't create a new one.",
|
||||
dateWithExtension);
|
||||
}
|
||||
} else if (strcmp(selectedOption, entryArray[1]) ==
|
||||
0) { // if we choosed to open a random entry
|
||||
// setting up the seed for rand()
|
||||
srand(time(NULL)); // not totaly random, just pseudorandom
|
||||
int randomEntry =
|
||||
extraOptions +
|
||||
(rand() %
|
||||
(entryCount -
|
||||
extraOptions)); // we get a random number between extraOptions and entryCount (so
|
||||
// all journal entries and not extraOptions)
|
||||
char temp[PATH_MAX];
|
||||
snprintf(temp, PATH_MAX, "%s/%s", path,
|
||||
entryArray[randomEntry]); // reconstruct the path
|
||||
strncpy(path, temp, PATH_MAX);
|
||||
debug("Selected random entry %s. It's path is %s.", entryArray[randomEntry], path);
|
||||
} else if (strcmp(selectedOption, entryArray[2]) == 0) { // if we choosed to search
|
||||
char *temp = fzfSelect(path, "Input text to be searched",
|
||||
shouldDebug); // uses ripgrep and fzf to search inside the files
|
||||
char *selectedOption =
|
||||
malloc(PATH_MAX); // we can't just use path as both input and output of snprintf
|
||||
snprintf(selectedOption, PATH_MAX, "%s/%s", path, temp);
|
||||
path = strdup(selectedOption);
|
||||
free(selectedOption);
|
||||
} else { // we just recreate the path to the selected entry
|
||||
// snprintf does not like to have the same variable as input and output so we use a
|
||||
// buffer
|
||||
char temp[PATH_MAX];
|
||||
snprintf(temp, PATH_MAX, "%s/%s", path, selectedOption);
|
||||
strncpy(path, temp, PATH_MAX);
|
||||
debug("Returning path to the selected entry: %s", path);
|
||||
}
|
||||
} else {
|
||||
error(1, "program", "%s is not a file and not a directory.", path);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
+22
-15
@@ -1,23 +1,29 @@
|
||||
#ifndef NOTES_H
|
||||
#define NOTES_H
|
||||
#include "utils.h"
|
||||
#include "ui.h"
|
||||
#include "utils.h"
|
||||
// find which directory contains targetVault
|
||||
char *getDirectoryFromVault(char *targetVault, char **vaultsArray, int vaultTotalNumber, int *vaultNumberPerDirectory, char **directoryArray, int directoryNumber, int shouldDebug);
|
||||
char *getDirectoryFromVault(char *targetVault, char **vaultsArray, int vaultTotalNumber,
|
||||
int *vaultNumberPerDirectory, char **directoryArray,
|
||||
int directoryNumber, int shouldDebug);
|
||||
// gets the journals from the vault.
|
||||
//to distinguish journals from note we use regex.
|
||||
//Note: we must handle them separatly as journals can either be a single file (which will treat specially) or a directory.
|
||||
char **getJournalsFromVault(char *pathToVault, char *vault, char *journalRegex, int *count, int shouldDebug);
|
||||
// this function is inputed a path to a vault (which was selected before) and outpus all the suitable notes (so not the hidden ones).
|
||||
// journalRegex is the regex code for the journals. If a note matches this code, it is treated as a journal and it is not outputed from this function.
|
||||
char **getNotesFromVault(char *pathToVault, char *vault, char *journalRegex, int *count, int shouldDebug);
|
||||
// function gets as input an array of directoryNumber strings. Which are the directories the function will search for vaults.
|
||||
// it returns an array of vaults.
|
||||
// the vaults are organized in order per directory.
|
||||
// vaultsPerDirectoryNumber and count should be initiallized before calling the function and the address be inputed.
|
||||
// count is the total number of vaults.
|
||||
// to distinguish journals from note we use regex.
|
||||
// Note: we must handle them separatly as journals can either be a single file (which will treat
|
||||
// specially) or a directory.
|
||||
char **getJournalsFromVault(char *pathToVault, char *vault, char *journalRegex, int *count,
|
||||
int shouldDebug);
|
||||
// this function is inputed a path to a vault (which was selected before) and outpus all the
|
||||
// suitable notes (so not the hidden ones). journalRegex is the regex code for the journals. If a
|
||||
// note matches this code, it is treated as a journal and it is not outputed from this function.
|
||||
char **getNotesFromVault(char *pathToVault, char *vault, char *journalRegex, int *count,
|
||||
int shouldDebug);
|
||||
// function gets as input an array of directoryNumber strings. Which are the directories the
|
||||
// function will search for vaults. it returns an array of vaults. the vaults are organized in order
|
||||
// per directory. vaultsPerDirectoryNumber and count should be initiallized before calling the
|
||||
// function and the address be inputed. count is the total number of vaults.
|
||||
// vaultsPerDirectoryNumber gives how many vaults each directory has.
|
||||
char **getVaultsFromDirectories(char **directoryStringArray, int directoryNumber, int *vaultsPerDirectoryNumber, int *count, int shouldDebug);
|
||||
char **getVaultsFromDirectories(char **directoryStringArray, int directoryNumber,
|
||||
int *vaultsPerDirectoryNumber, int *count, int shouldDebug);
|
||||
// path is the path to the file.
|
||||
// journal is the name of the file.
|
||||
// journalWasUpdated will be set to 1 if a new entry was created
|
||||
@@ -25,5 +31,6 @@ char **getVaultsFromDirectories(char **directoryStringArray, int directoryNumber
|
||||
// creates new entry with date.
|
||||
// for divided select if we want to acces to a new entry or a old one.
|
||||
// returns the path to the file that needs to be opened.
|
||||
char *updateJournal(char *path, char *journal, char *timeFormat, int *journalWasUpdated, int shouldDebug);
|
||||
char *updateJournal(char *path, char *journal, char *timeFormat, int *journalWasUpdated,
|
||||
int shouldDebug);
|
||||
#endif
|
||||
|
||||
@@ -1,163 +1,187 @@
|
||||
#include "ui.h"
|
||||
#include "utils.h"
|
||||
|
||||
void createNewVault(char **directoriesArray, int directoryCount, char **vaultsArray, int vaultCount, int bypass, char *bypassvalue, int shouldDebug) {
|
||||
int duplicateWarning = 0; // set to 1 later if the vault you tried to create already existed
|
||||
int emptyWarning = 0; // set to 1 later if inpted empty name
|
||||
void createNewVault(char **directoriesArray, int directoryCount, char **vaultsArray, int vaultCount,
|
||||
int bypass, char *bypassvalue, int shouldDebug) {
|
||||
int duplicateWarning = 0; // set to 1 later if the vault you tried to create already existed
|
||||
int emptyWarning = 0; // set to 1 later if inpted empty name
|
||||
input_screen:
|
||||
// choose in which directory the vault will be created
|
||||
char *dirToVault = ncursesSelect(directoriesArray, "Select a directory, in which the vault will remain (Use arrows or WASD, Enter to select)", directoryCount, 0, "", "", " ", shouldDebug);
|
||||
char *vaultName = malloc(PATH_MAX);
|
||||
if (!bypass) { // if won't bypass (if -v or --vault weren't set)
|
||||
echo();
|
||||
keypad(stdscr, FALSE);
|
||||
// color code from https://stackoverflow.com/a/73396575
|
||||
start_color();
|
||||
clear();
|
||||
use_default_colors();
|
||||
init_pair(1, COLOR_WHITE, -1); // -1 is blank
|
||||
init_pair(2, COLOR_RED, -1);
|
||||
attron(COLOR_PAIR(1));
|
||||
printw("Enter the name of the new vault: ");
|
||||
mvprintw(3, 0, "Unsafe characters such as \\ and / will be replaced by _");
|
||||
attroff(COLOR_PAIR(1));
|
||||
if (duplicateWarning) {
|
||||
attron(COLOR_PAIR(2));
|
||||
mvprintw(4, 0, "A vault with this name already exists. Please input another name");
|
||||
attroff(COLOR_PAIR(2));
|
||||
// choose in which directory the vault will be created
|
||||
char *dirToVault = ncursesSelect(
|
||||
directoriesArray,
|
||||
"Select a directory, in which the vault will remain (Use arrows or WASD, Enter to select)",
|
||||
directoryCount, 0, "", "", " ", shouldDebug);
|
||||
char *vaultName = malloc(PATH_MAX);
|
||||
if (!bypass) { // if won't bypass (if -v or --vault weren't set)
|
||||
echo();
|
||||
keypad(stdscr, FALSE);
|
||||
// color code from https://stackoverflow.com/a/73396575
|
||||
start_color();
|
||||
clear();
|
||||
use_default_colors();
|
||||
init_pair(1, COLOR_WHITE, -1); // -1 is blank
|
||||
init_pair(2, COLOR_RED, -1);
|
||||
attron(COLOR_PAIR(1));
|
||||
printw("Enter the name of the new vault: ");
|
||||
mvprintw(3, 0, "Unsafe characters such as \\ and / will be replaced by _");
|
||||
attroff(COLOR_PAIR(1));
|
||||
if (duplicateWarning) {
|
||||
attron(COLOR_PAIR(2));
|
||||
mvprintw(4, 0, "A vault with this name already exists. Please input another name");
|
||||
attroff(COLOR_PAIR(2));
|
||||
}
|
||||
if (emptyWarning) {
|
||||
attron(COLOR_PAIR(2));
|
||||
mvprintw(5, 0, "The vault's name must not be empty. Please input another name");
|
||||
attroff(COLOR_PAIR(2));
|
||||
}
|
||||
move(1, 1); // replace cursor
|
||||
wgetnstr(stdscr, vaultName, PATH_MAX - 1);
|
||||
refresh();
|
||||
endwin();
|
||||
reset_shell_mode();
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
} else {
|
||||
strncpy(vaultName, bypassvalue, PATH_MAX - 2); // -2 (and later -1) because indexing
|
||||
vaultName[PATH_MAX - 1] = '\0';
|
||||
}
|
||||
if (emptyWarning) {
|
||||
attron(COLOR_PAIR(2));
|
||||
mvprintw(5, 0, "The vault's name must not be empty. Please input another name");
|
||||
attroff(COLOR_PAIR(2));
|
||||
// check if no empty string
|
||||
if (strcmp(vaultName, "") == 0) {
|
||||
emptyWarning = 1;
|
||||
goto input_screen;
|
||||
}
|
||||
move(1, 1); // replace cursor
|
||||
wgetnstr(stdscr, vaultName, PATH_MAX-1);
|
||||
refresh();
|
||||
endwin();
|
||||
reset_shell_mode();
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
} else {
|
||||
strncpy(vaultName, bypassvalue, PATH_MAX -2); // -2 (and later -1) because indexing
|
||||
vaultName[PATH_MAX-1] = '\0';
|
||||
}
|
||||
// check if no empty string
|
||||
if (strcmp(vaultName, "") == 0) {
|
||||
emptyWarning = 1;
|
||||
goto input_screen;
|
||||
}
|
||||
debug("Inputed vaultName=%s", vaultName);
|
||||
sanitize(vaultName);
|
||||
debug("Sanitized vaultName=%s", vaultName);
|
||||
debug("Inputed vaultName=%s", vaultName);
|
||||
sanitize(vaultName);
|
||||
debug("Sanitized vaultName=%s", vaultName);
|
||||
|
||||
|
||||
if (!isStringInArray(vaultName, (const char**)vaultsArray, vaultCount)) { // if vault doesnt already exists. We avoid vaults with the same name even if they are from different directories.
|
||||
char vaultFullPath[PATH_MAX]; // recreating the full path
|
||||
sprintf(vaultFullPath, "%s/%s/", dirToVault, vaultName);
|
||||
if (!isStringInArray(vaultName, (const char **)vaultsArray,
|
||||
vaultCount)) { // if vault doesnt already exists. We avoid vaults with the
|
||||
// same name even if they are from different directories.
|
||||
char vaultFullPath[PATH_MAX]; // recreating the full path
|
||||
sprintf(vaultFullPath, "%s/%s/", dirToVault, vaultName);
|
||||
|
||||
mkdir(vaultFullPath, 0744);
|
||||
} else {
|
||||
duplicateWarning = 1;
|
||||
goto input_screen;
|
||||
}
|
||||
free(vaultName);
|
||||
mkdir(vaultFullPath, 0744);
|
||||
} else {
|
||||
duplicateWarning = 1;
|
||||
goto input_screen;
|
||||
}
|
||||
free(vaultName);
|
||||
}
|
||||
|
||||
char *createNewNote(char dirToVault[PATH_MAX], char *vaultFromDir, int bypass, char *bypassvalue, char *journalRegex, int shouldDebug) {
|
||||
// input from user for the name
|
||||
char *fileName = malloc(BUFFER_SIZE);
|
||||
if (!bypass) { // if we don't bypass. (if -n or --note weren't set.)
|
||||
echo();
|
||||
keypad(stdscr, FALSE);
|
||||
clear();
|
||||
printw("Enter the name of the new note: ");
|
||||
mvprintw(3, 0, "Unsafe characters such as \\, and / will be replaced by _");
|
||||
mvprintw(4, 0, "If the name matches with the regex for a journal (%s), it will create a journal instead of a note.", journalRegex);
|
||||
move(1,1);
|
||||
wgetnstr(stdscr, fileName, BUFFER_SIZE-4); //limits the buffer to prevent overflow (-4 to account indexing and from ".md" in case we need to add it later)
|
||||
refresh();
|
||||
endwin();
|
||||
reset_shell_mode();
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
} else { // bypasses user input if we bypass is set to 1
|
||||
strncpy(fileName, bypassvalue, BUFFER_SIZE);
|
||||
fileName[BUFFER_SIZE-1] = '\0';
|
||||
}
|
||||
error(strcmp(fileName, "") == 0, "user", "fileName is empty"); // replace this with a warning and add a warning if duplicate file and handle case where multiple warnings (if such case is possible)
|
||||
// check/sanitize the input
|
||||
debug("Inputed fileName=%s", fileName);
|
||||
sanitize(fileName);
|
||||
debug("Sanitized fileName=%s (We might append .md later", fileName);
|
||||
// we trow an error if the file already exists (previous behaviour was overwriting the file)
|
||||
char path[PATH_MAX];
|
||||
snprintf(path, PATH_MAX, "%s/%s/%s", dirToVault, vaultFromDir, fileName);
|
||||
struct stat st;
|
||||
error(stat(path, &st) == 0, "user", "%s already exists", fileName);
|
||||
// if it matches with the journalRegex we treat it as a journal instead of a note
|
||||
regex_t regex;
|
||||
int regexReturn = regcomp(®ex, journalRegex, 0);
|
||||
error(regexReturn, "program", "Regex compilation failed.");
|
||||
regexReturn = regexec(®ex, fileName, 0, NULL, 0);
|
||||
if (!regexReturn) {
|
||||
debug("%s matches with %s treating it as a journal", fileName, journalRegex);
|
||||
char **options = malloc(32); // the number of bytes is exactly what in the two strings
|
||||
options[0] = "Divided journal";
|
||||
options[1] = "Unified journal";
|
||||
char *optionSelected = ncursesSelect(options, "Select which type of journal you want to create (Use arrows or WASD, Enter to select):", 2, 0, "", "", " ", shouldDebug);
|
||||
debug("%s was selected to be a %s", fileName, optionSelected);
|
||||
if (strcmp(optionSelected, options[0]) == 0) { // if it is a divided journal
|
||||
char *fileFullPath = malloc(PATH_MAX);
|
||||
snprintf(fileFullPath, PATH_MAX, "%s/%s/%s/", dirToVault, vaultFromDir, fileName);
|
||||
struct stat st = {0};
|
||||
if (stat(fileFullPath, &st) == -1) {
|
||||
error(mkdir(fileFullPath, 0744), "program", "mkdir failed");
|
||||
} else {
|
||||
error(1, "program", "%s could not be created", fileFullPath);
|
||||
}
|
||||
free(fileFullPath);
|
||||
} else { // if it is a unified journal
|
||||
char *fileFullPath = malloc(PATH_MAX);
|
||||
snprintf(fileFullPath, PATH_MAX, "%s/%s/%s", dirToVault, vaultFromDir, fileName);
|
||||
int len = strlen(fileFullPath);
|
||||
if (fileFullPath[len-3] != '.' || fileFullPath[len-2] != 'm' || fileFullPath[len-1] != 'd') { // there might be a cleaner way to do this
|
||||
error(len > PATH_MAX - 3, "user", "%s is too big (greater than PATH_MAX-3) and we can't append .md", fileFullPath);
|
||||
strncat(fileFullPath, ".md", PATH_MAX);
|
||||
// we checked before if fileName didn't already exist.
|
||||
// we must redo it as we add a .mode
|
||||
struct stat st;
|
||||
error(stat(fileFullPath, &st) == 0, "user", "%s already exists", fileFullPath);
|
||||
}
|
||||
FILE *filePointer;
|
||||
filePointer = fopen(fileFullPath, "w"); // creates and opens the file
|
||||
error(filePointer == NULL, "program", "The %s couldn't be created.", fileFullPath);
|
||||
fprintf(filePointer, "### %s\n", fileName);
|
||||
fclose(filePointer); // closes the file so that nvim could open it
|
||||
free(fileFullPath);
|
||||
char *createNewNote(char dirToVault[PATH_MAX], char *vaultFromDir, int bypass, char *bypassvalue,
|
||||
char *journalRegex, int shouldDebug) {
|
||||
// input from user for the name
|
||||
char *fileName = malloc(BUFFER_SIZE);
|
||||
if (!bypass) { // if we don't bypass. (if -n or --note weren't set.)
|
||||
echo();
|
||||
keypad(stdscr, FALSE);
|
||||
clear();
|
||||
printw("Enter the name of the new note: ");
|
||||
mvprintw(3, 0, "Unsafe characters such as \\, and / will be replaced by _");
|
||||
mvprintw(4, 0,
|
||||
"If the name matches with the regex for a journal (%s), it will create a journal "
|
||||
"instead of a note.",
|
||||
journalRegex);
|
||||
move(1, 1);
|
||||
wgetnstr(stdscr, fileName,
|
||||
BUFFER_SIZE - 4); // limits the buffer to prevent overflow (-4 to account indexing
|
||||
// and from ".md" in case we need to add it later)
|
||||
refresh();
|
||||
endwin();
|
||||
reset_shell_mode();
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
} else { // bypasses user input if we bypass is set to 1
|
||||
strncpy(fileName, bypassvalue, BUFFER_SIZE);
|
||||
fileName[BUFFER_SIZE - 1] = '\0';
|
||||
}
|
||||
free(options); // they are useless now
|
||||
} else { // normal process for a note
|
||||
debug("%s does not match with %s treating it as a note", fileName, journalRegex);
|
||||
// if there is no .md add an .md
|
||||
int len = strlen(fileName);
|
||||
if (fileName[len-3] != '.' || fileName[len-2] != 'm' || fileName[len-1] != 'd') { // there might be a cleaner way to do this
|
||||
strcat(fileName, ".md"); // this should not cause an overflow issue as we get at most 252 chars (+'.'+'m'+'d'+'\0' makes it to 256) with wgetnstr
|
||||
error(
|
||||
strcmp(fileName, "") == 0, "user",
|
||||
"fileName is empty"); // replace this with a warning and add a warning if duplicate file and
|
||||
// handle case where multiple warnings (if such case is possible)
|
||||
// check/sanitize the input
|
||||
debug("Inputed fileName=%s", fileName);
|
||||
sanitize(fileName);
|
||||
debug("Sanitized fileName=%s (We might append .md later", fileName);
|
||||
// we trow an error if the file already exists (previous behaviour was overwriting the file)
|
||||
char path[PATH_MAX];
|
||||
snprintf(path, PATH_MAX, "%s/%s/%s", dirToVault, vaultFromDir, fileName);
|
||||
struct stat st;
|
||||
error(stat(path, &st) == 0, "user", "%s already exists", fileName);
|
||||
// if it matches with the journalRegex we treat it as a journal instead of a note
|
||||
regex_t regex;
|
||||
int regexReturn = regcomp(®ex, journalRegex, 0);
|
||||
error(regexReturn, "program", "Regex compilation failed.");
|
||||
regexReturn = regexec(®ex, fileName, 0, NULL, 0);
|
||||
if (!regexReturn) {
|
||||
debug("%s matches with %s treating it as a journal", fileName, journalRegex);
|
||||
char **options = malloc(32); // the number of bytes is exactly what in the two strings
|
||||
options[0] = "Divided journal";
|
||||
options[1] = "Unified journal";
|
||||
char *optionSelected = ncursesSelect(options,
|
||||
"Select which type of journal you want to create "
|
||||
"(Use arrows or WASD, Enter to select):",
|
||||
2, 0, "", "", " ", shouldDebug);
|
||||
debug("%s was selected to be a %s", fileName, optionSelected);
|
||||
if (strcmp(optionSelected, options[0]) == 0) { // if it is a divided journal
|
||||
char *fileFullPath = malloc(PATH_MAX);
|
||||
snprintf(fileFullPath, PATH_MAX, "%s/%s/%s/", dirToVault, vaultFromDir, fileName);
|
||||
struct stat st = {0};
|
||||
if (stat(fileFullPath, &st) == -1) {
|
||||
error(mkdir(fileFullPath, 0744), "program", "mkdir failed");
|
||||
} else {
|
||||
error(1, "program", "%s could not be created", fileFullPath);
|
||||
}
|
||||
free(fileFullPath);
|
||||
} else { // if it is a unified journal
|
||||
char *fileFullPath = malloc(PATH_MAX);
|
||||
snprintf(fileFullPath, PATH_MAX, "%s/%s/%s", dirToVault, vaultFromDir, fileName);
|
||||
int len = strlen(fileFullPath);
|
||||
if (fileFullPath[len - 3] != '.' || fileFullPath[len - 2] != 'm' ||
|
||||
fileFullPath[len - 1] != 'd') { // there might be a cleaner way to do this
|
||||
error(len > PATH_MAX - 3, "user",
|
||||
"%s is too big (greater than PATH_MAX-3) and we can't append .md",
|
||||
fileFullPath);
|
||||
strncat(fileFullPath, ".md", PATH_MAX);
|
||||
// we checked before if fileName didn't already exist.
|
||||
// we must redo it as we add a .mode
|
||||
struct stat st;
|
||||
error(stat(fileFullPath, &st) == 0, "user", "%s already exists", fileFullPath);
|
||||
}
|
||||
FILE *filePointer;
|
||||
filePointer = fopen(fileFullPath, "w"); // creates and opens the file
|
||||
error(filePointer == NULL, "program", "The %s couldn't be created.", fileFullPath);
|
||||
fprintf(filePointer, "### %s\n", fileName);
|
||||
fclose(filePointer); // closes the file so that nvim could open it
|
||||
free(fileFullPath);
|
||||
}
|
||||
free(options); // they are useless now
|
||||
} else { // normal process for a note
|
||||
debug("%s does not match with %s treating it as a note", fileName, journalRegex);
|
||||
// if there is no .md add an .md
|
||||
int len = strlen(fileName);
|
||||
if (fileName[len - 3] != '.' || fileName[len - 2] != 'm' ||
|
||||
fileName[len - 1] != 'd') { // there might be a cleaner way to do this
|
||||
strcat(fileName, ".md"); // this should not cause an overflow issue as we get at most
|
||||
// 252 chars (+'.'+'m'+'d'+'\0' makes it to 256) with wgetnstr
|
||||
}
|
||||
char *fileFullPath = malloc(PATH_MAX); // this dinamically allocated because we use it in
|
||||
// the main function to call openEditor
|
||||
sprintf(fileFullPath, "%s/%s/%s", dirToVault, vaultFromDir, fileName);
|
||||
FILE *filePointer;
|
||||
filePointer = fopen(fileFullPath, "w"); // creates and opens the file
|
||||
error(filePointer == NULL, "program", "The %s couldn't be created.", fileFullPath);
|
||||
fprintf(filePointer, "### %s\n", fileName);
|
||||
fclose(filePointer); // closes the file so that nvim could open it
|
||||
free(fileFullPath);
|
||||
}
|
||||
char *fileFullPath = malloc(PATH_MAX); // this dinamically allocated because we use it in the main function to call openEditor
|
||||
sprintf(fileFullPath, "%s/%s/%s", dirToVault, vaultFromDir, fileName);
|
||||
FILE *filePointer;
|
||||
filePointer = fopen(fileFullPath, "w"); // creates and opens the file
|
||||
error(filePointer == NULL, "program", "The %s couldn't be created.", fileFullPath);
|
||||
fprintf(filePointer, "### %s\n", fileName);
|
||||
fclose(filePointer); // closes the file so that nvim could open it
|
||||
free(fileFullPath);
|
||||
}
|
||||
return fileName;
|
||||
return fileName;
|
||||
}
|
||||
|
||||
char* fzfSelect(char *pathToFiles, char *selectText, int shouldDebug) {
|
||||
// we are gonna write each line of each files (with the filename and the line number to an index)
|
||||
char *fzfSelect(char *pathToFiles, char *selectText, int shouldDebug) {
|
||||
// we are gonna write each line of each files (with the filename and the line number to an
|
||||
// index)
|
||||
char command[CMD_BUFFER];
|
||||
char indexFile[] = "/tmp/notewrapper_index_XXXXXX";
|
||||
|
||||
@@ -170,12 +194,9 @@ char* fzfSelect(char *pathToFiles, char *selectText, int shouldDebug) {
|
||||
* Build index once into temp file
|
||||
* format: file:line:content
|
||||
*/
|
||||
snprintf(command, sizeof(command),
|
||||
"rg --line-number --no-heading --color=never . \"%s\" > %s",
|
||||
pathToFiles,
|
||||
indexFile
|
||||
);
|
||||
|
||||
snprintf(command, sizeof(command), "rg --line-number --no-heading --color=never . \"%s\" > %s",
|
||||
pathToFiles, indexFile);
|
||||
|
||||
debug("INDEX CMD: %s", command);
|
||||
|
||||
if (system(command) != 0) {
|
||||
@@ -189,47 +210,47 @@ char* fzfSelect(char *pathToFiles, char *selectText, int shouldDebug) {
|
||||
*/
|
||||
/*
|
||||
EXPLANATION:
|
||||
|
||||
|
||||
cat %s
|
||||
→ feeds prebuilt index file (format: file:line:content) into fzf
|
||||
|
||||
|
||||
fzf --delimiter ':'
|
||||
→ splits each line into fields:
|
||||
{1} = file path
|
||||
{2} = line number
|
||||
{3} = content
|
||||
|
||||
|
||||
--prompt
|
||||
→ UI prompt text shown in fzf
|
||||
|
||||
|
||||
--preview
|
||||
→ runs a shell command for selected item:
|
||||
file={1} → current file
|
||||
line={2} → line number of match
|
||||
|
||||
|
||||
nl -ba "$file"
|
||||
→ prints file with line numbers
|
||||
|
||||
|
||||
sed -n "$((line-5)),$((line+5))p"
|
||||
→ extracts a window of 5 lines above and below match
|
||||
|
||||
|
||||
sed "... -> ..."
|
||||
→ highlights the matched line (middle of window) with red arrow
|
||||
|
||||
|
||||
--preview-window=right:60%:hidden
|
||||
→ preview appears on right, 60% width, initially hidden
|
||||
|
||||
|
||||
--bind 'change:show-preview'
|
||||
→ preview only appears after first selection change (not at startup)
|
||||
*/
|
||||
snprintf(command, sizeof(command),
|
||||
"cat %s | fzf --delimiter ':' --prompt='%s' "
|
||||
"--preview 'file={1}; line={2}; nl -ba \"$file\" | sed -n \"$((line-5)),$((line+5))p\" | sed \"$((line-$(($((line-5))))+1))s/^/\\x1b[31m-> \\x1b[0m/\"' "
|
||||
"--preview-window=right:60%%:hidden "
|
||||
"--bind 'change:show-preview'",
|
||||
indexFile,
|
||||
selectText ? selectText : "> "
|
||||
);
|
||||
snprintf(
|
||||
command, sizeof(command),
|
||||
"cat %s | fzf --delimiter ':' --prompt='%s' "
|
||||
"--preview 'file={1}; line={2}; nl -ba \"$file\" | sed -n \"$((line-5)),$((line+5))p\" | "
|
||||
"sed \"$((line-$(($((line-5))))+1))s/^/\\x1b[31m-> \\x1b[0m/\"' "
|
||||
"--preview-window=right:60%%:hidden "
|
||||
"--bind 'change:show-preview'",
|
||||
indexFile, selectText ? selectText : "> ");
|
||||
debug("FZF CMD: %s", command);
|
||||
|
||||
FILE *fzfPipe = popen(command, "r");
|
||||
@@ -252,8 +273,8 @@ char* fzfSelect(char *pathToFiles, char *selectText, int shouldDebug) {
|
||||
char *token = strtok(result, "/");
|
||||
result = NULL;
|
||||
while (token != NULL) {
|
||||
result = token;
|
||||
token = strtok(NULL, "/"); //Passing NULL means “don’t start a new string, resume
|
||||
result = token;
|
||||
token = strtok(NULL, "/"); // Passing NULL means “don’t start a new string, resume
|
||||
}
|
||||
|
||||
// clean up
|
||||
@@ -263,81 +284,86 @@ char* fzfSelect(char *pathToFiles, char *selectText, int shouldDebug) {
|
||||
return result;
|
||||
}
|
||||
|
||||
char* ncursesSelect(char **options, char *optionsText, int optionsNumber, int extraOptionsNumber, char *bottomText, char *middleText, char *topText, int shouldDebug) { // TODO we should see how it handles larges strings (like large directories)
|
||||
int highlight = 0; //curently highlighted option
|
||||
char *ncursesSelect(
|
||||
char **options, char *optionsText, int optionsNumber, int extraOptionsNumber, char *bottomText,
|
||||
char *middleText, char *topText,
|
||||
int shouldDebug) { // TODO we should see how it handles larges strings (like large directories)
|
||||
int highlight = 0; // curently highlighted option
|
||||
int key;
|
||||
|
||||
cbreak(); // disable line buffering
|
||||
noecho(); // don't echo key presses
|
||||
keypad(stdscr, TRUE); // enable arrow keys
|
||||
cbreak(); // disable line buffering
|
||||
noecho(); // don't echo key presses
|
||||
keypad(stdscr, TRUE); // enable arrow keys
|
||||
start_color();
|
||||
curs_set(0); // sets the cursor to invisible. (We will emulate the cursor with a hightlight that go up and down).
|
||||
curs_set(0); // sets the cursor to invisible. (We will emulate the cursor with a hightlight that
|
||||
// go up and down).
|
||||
use_default_colors();
|
||||
init_pair(1, COLOR_WHITE, -1); // color for optionsNumber (so notes, vaults, etc.)
|
||||
init_pair(2, COLOR_BLUE, -1); // color for extraOptionsNumber (so settings, delete notes, create vault, etc.)
|
||||
init_pair(2, COLOR_BLUE,
|
||||
-1); // color for extraOptionsNumber (so settings, delete notes, create vault, etc.)
|
||||
while (1) {
|
||||
clear();
|
||||
attron(COLOR_PAIR(1));
|
||||
mvprintw(0,0, "%s", optionsText);
|
||||
int offset = 1;
|
||||
if (strcmp(bottomText, "") != 0) {
|
||||
mvprintw(offset, 1, "%s", bottomText);
|
||||
offset++;
|
||||
}
|
||||
// Print options with highlighting
|
||||
for (int i = 0; i < optionsNumber; i++) {
|
||||
if (i == highlight) {
|
||||
attron(A_REVERSE);
|
||||
} // highlight selected
|
||||
mvprintw(i+offset, 2, "%s", options[i]);
|
||||
if (i == highlight) {
|
||||
attroff(A_REVERSE);
|
||||
clear();
|
||||
attron(COLOR_PAIR(1));
|
||||
mvprintw(0, 0, "%s", optionsText);
|
||||
int offset = 1;
|
||||
if (strcmp(bottomText, "") != 0) {
|
||||
mvprintw(offset, 1, "%s", bottomText);
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
attroff(COLOR_PAIR(1));
|
||||
if (strcmp(middleText, "") != 0) {
|
||||
mvprintw(offset+optionsNumber, 1, "%s", middleText);
|
||||
offset++;
|
||||
}
|
||||
// Printf extraOptions with highlighting
|
||||
attron(COLOR_PAIR(2));
|
||||
for (int k = optionsNumber; k < optionsNumber + extraOptionsNumber; k++) {
|
||||
if (k == highlight) {
|
||||
attron(A_REVERSE);
|
||||
// Print options with highlighting
|
||||
for (int i = 0; i < optionsNumber; i++) {
|
||||
if (i == highlight) {
|
||||
attron(A_REVERSE);
|
||||
} // highlight selected
|
||||
mvprintw(i + offset, 2, "%s", options[i]);
|
||||
if (i == highlight) {
|
||||
attroff(A_REVERSE);
|
||||
}
|
||||
}
|
||||
mvprintw(k+offset, 2, "%s", options[k]);
|
||||
if (k == highlight) {
|
||||
attroff(A_REVERSE);
|
||||
attroff(COLOR_PAIR(1));
|
||||
if (strcmp(middleText, "") != 0) {
|
||||
mvprintw(offset + optionsNumber, 1, "%s", middleText);
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
attroff(COLOR_PAIR(2));
|
||||
attron(COLOR_PAIR(1));
|
||||
mvprintw(offset+optionsNumber+extraOptionsNumber, 1, "%s", topText);
|
||||
attroff(COLOR_PAIR(1));
|
||||
key = getch(); //get some int which value correspond to some key being pressed
|
||||
// Printf extraOptions with highlighting
|
||||
attron(COLOR_PAIR(2));
|
||||
for (int k = optionsNumber; k < optionsNumber + extraOptionsNumber; k++) {
|
||||
if (k == highlight) {
|
||||
attron(A_REVERSE);
|
||||
}
|
||||
mvprintw(k + offset, 2, "%s", options[k]);
|
||||
if (k == highlight) {
|
||||
attroff(A_REVERSE);
|
||||
}
|
||||
}
|
||||
attroff(COLOR_PAIR(2));
|
||||
attron(COLOR_PAIR(1));
|
||||
mvprintw(offset + optionsNumber + extraOptionsNumber, 1, "%s", topText);
|
||||
attroff(COLOR_PAIR(1));
|
||||
key = getch(); // get some int which value correspond to some key being pressed
|
||||
|
||||
switch(key) {
|
||||
switch (key) {
|
||||
case KEY_UP:
|
||||
case 'w':
|
||||
case 'W':
|
||||
highlight--;
|
||||
if (highlight < 0) {
|
||||
highlight = optionsNumber + extraOptionsNumber - 1;// can't select the -1nth option
|
||||
}
|
||||
break;
|
||||
highlight--;
|
||||
if (highlight < 0) {
|
||||
highlight = optionsNumber + extraOptionsNumber - 1; // can't select the -1nth option
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
case 'S':
|
||||
case KEY_DOWN:
|
||||
highlight++;
|
||||
if (highlight >= optionsNumber + extraOptionsNumber) {
|
||||
highlight = 0;
|
||||
}
|
||||
break;
|
||||
highlight++;
|
||||
if (highlight >= optionsNumber + extraOptionsNumber) {
|
||||
highlight = 0;
|
||||
}
|
||||
break;
|
||||
case 10: //\n
|
||||
goto end_loop;
|
||||
}
|
||||
goto end_loop;
|
||||
}
|
||||
}
|
||||
end_loop:
|
||||
end_loop:
|
||||
endwin(); // end ncurses mode
|
||||
fflush(stderr);
|
||||
debug("Selected option: %s", options[highlight]);
|
||||
|
||||
@@ -1,47 +1,47 @@
|
||||
#ifndef UI_H
|
||||
#define UI_H
|
||||
|
||||
#include "utils.h"
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <ncurses.h>
|
||||
#include <pwd.h>
|
||||
#include <regex.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ncurses.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <pwd.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <regex.h>
|
||||
#include "utils.h"
|
||||
#define CMD_BUFFER 4096 // for fzfSelect
|
||||
#include <unistd.h>
|
||||
#define CMD_BUFFER 4096 // for fzfSelect
|
||||
#define RESULT_BUFFER 1024 // for fzfSelect
|
||||
|
||||
/*Uses ncurses to get an input from the user.
|
||||
Creates a new note with this input.
|
||||
also can create a journal if the name matches with journalRegex.
|
||||
returns the path to the note.
|
||||
/*Uses ncurses to get an input from the user.
|
||||
Creates a new note with this input.
|
||||
also can create a journal if the name matches with journalRegex.
|
||||
returns the path to the note.
|
||||
If you want to bypass the input TUI set bypass to 1 and bypassvalue to the note name
|
||||
*/
|
||||
char *createNewNote(char dirToVault[PATH_MAX], char *vaultFromDir, int bypass, char *bypassvalue, char *journalRegex, int debug);
|
||||
char *createNewNote(char dirToVault[PATH_MAX], char *vaultFromDir, int bypass, char *bypassvalue,
|
||||
char *journalRegex, int debug);
|
||||
/*
|
||||
Chooses in which directory it want to create the vault.
|
||||
Uses ncurses to get an input from the user.
|
||||
Creates a new vault with this input.
|
||||
If vault already exists, prints a warning.
|
||||
Uses ncurses to get an input from the user.
|
||||
Creates a new vault with this input.
|
||||
If vault already exists, prints a warning.
|
||||
returns nothing. */
|
||||
void createNewVault(char **directoriesArray, int directoryCount, char **vaultsArray, int vaultCount, int bypass, char *bypassvalue, int debug);
|
||||
void createNewVault(char **directoriesArray, int directoryCount, char **vaultsArray, int vaultCount,
|
||||
int bypass, char *bypassvalue, int debug);
|
||||
// uses fzf and ripgrep to search inside the files to choose between all the options.
|
||||
char* fzfSelect(char *pathToFiles, char *selectText, int shouldDebug);
|
||||
/*this function is used multiple times let the user select one options from many with ncurses in a TUI.
|
||||
options is the array of strings with all the options.
|
||||
optionsText is the text that will be printed at the top (For example: "Please select ...").
|
||||
we distinguish options from extraOptions.
|
||||
char *fzfSelect(char *pathToFiles, char *selectText, int shouldDebug);
|
||||
/*this function is used multiple times let the user select one options from many with ncurses in a
|
||||
TUI. options is the array of strings with all the options. optionsText is the text that will be
|
||||
printed at the top (For example: "Please select ..."). we distinguish options from extraOptions.
|
||||
options could be the list of all notes or all vaults.
|
||||
extraOptions are options that are special and have a special color (for ex: "Delete vault", "Settings", etc.).
|
||||
note: extraOptions should be at the end of options.
|
||||
topText is printed between options text and the start of options.
|
||||
middleText (usally \n) is printed between options and extraOptions.
|
||||
bottomText is printed bellow.
|
||||
topText and middle must be exactly one line. If you want empty lines use " " and not "".
|
||||
returns the selected option. */
|
||||
char* ncursesSelect(char **options, char *optionsText, int optionsNumber, int extraOptionsNumber, char *bottomText, char *middleText, char *topText, int debug);
|
||||
extraOptions are options that are special and have a special color (for ex: "Delete vault",
|
||||
"Settings", etc.). note: extraOptions should be at the end of options. topText is printed between
|
||||
options text and the start of options. middleText (usally \n) is printed between options and
|
||||
extraOptions. bottomText is printed bellow. topText and middle must be exactly one line. If you want
|
||||
empty lines use " " and not "". returns the selected option. */
|
||||
char *ncursesSelect(char **options, char *optionsText, int optionsNumber, int extraOptionsNumber,
|
||||
char *bottomText, char *middleText, char *topText, int debug);
|
||||
#endif
|
||||
|
||||
+423
-397
@@ -11,88 +11,95 @@ int compareString(const void *a, const void *b) {
|
||||
int reverseCompareString(const void *a, const void *b) {
|
||||
const char *str1 = *(const char **)a;
|
||||
const char *str2 = *(const char **)b;
|
||||
return -1*strcmp(str1, str2);
|
||||
return -1 * strcmp(str1, str2);
|
||||
}
|
||||
|
||||
void getCurrentTime(int *hour, int *minute, int *second) {
|
||||
time_t now = time(NULL); // Get current time in seconds since epoch
|
||||
struct tm *local = localtime(&now); // Convert to local time structure
|
||||
time_t now = time(NULL); // Get current time in seconds since epoch
|
||||
struct tm *local = localtime(&now); // Convert to local time structure
|
||||
|
||||
*hour = local->tm_hour; // Extract hour
|
||||
*minute = local->tm_min; // Extract minutes
|
||||
*second = local->tm_sec; // Extract seconds
|
||||
*hour = local->tm_hour; // Extract hour
|
||||
*minute = local->tm_min; // Extract minutes
|
||||
*second = local->tm_sec; // Extract seconds
|
||||
}
|
||||
|
||||
void _debug(const int d, const char *file, const int line, const char *function, const char *message, ...) { // use for formatted debug
|
||||
if (d) {
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
va_list args; //variadic function stuff
|
||||
va_start(args, message);
|
||||
int h, m, s;
|
||||
getCurrentTime(&h, &m, &s);
|
||||
fprintf(stderr, "\e[0;32m[DEBUG -- %d:%d:%d] From file %s line %d function %s:\e[0m\n", h, m, s, file, line, function);
|
||||
vfprintf(stderr, message, args);
|
||||
fprintf(stderr, "\e[0m\n");
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
void _altDebug(const int d, const char *message, ...) { // use for less formal debuggin. (usefull if enumerating or making a list
|
||||
if (d) {
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
vfprintf(stderr, message, args);
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
|
||||
void _error(const int shouldDebug, const int condition, const char *type, const char *file, const int line, const char *function, const char *message, ...) { // used for formatted errors
|
||||
if (condition) {
|
||||
int h, m, s;
|
||||
getCurrentTime(&h, &m, &s);
|
||||
fprintf(stderr, "\e[0;31m[%s ERROR -- %d:%d:%d] From file %s line %d function %s:\n", type, h, m, s, file, line, function);
|
||||
if (errno != 0) {
|
||||
fprintf(stderr, " (System-level error message: %s)\n", strerror(errno));
|
||||
} else {
|
||||
fprintf(stderr, " (No system-level error; issue is application-level)\n");
|
||||
void _debug(const int d, const char *file, const int line, const char *function,
|
||||
const char *message, ...) { // use for formatted debug
|
||||
if (d) {
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
va_list args; // variadic function stuff
|
||||
va_start(args, message);
|
||||
int h, m, s;
|
||||
getCurrentTime(&h, &m, &s);
|
||||
fprintf(stderr, "\e[0;32m[DEBUG -- %d:%d:%d] From file %s line %d function %s:\e[0m\n", h,
|
||||
m, s, file, line, function);
|
||||
vfprintf(stderr, message, args);
|
||||
fprintf(stderr, "\e[0m\n");
|
||||
va_end(args);
|
||||
}
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
vfprintf(stderr, message, args);
|
||||
va_end(args);
|
||||
if (!shouldDebug) {
|
||||
fprintf(stderr, "\nRunning notewrapper -V might give you more information.");
|
||||
}
|
||||
void _altDebug(const int d, const char *message,
|
||||
...) { // use for less formal debuggin. (usefull if enumerating or making a list
|
||||
if (d) {
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
vfprintf(stderr, message, args);
|
||||
va_end(args);
|
||||
}
|
||||
fprintf(stderr, "\e[0m\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void copyDir(const char *source, const char *destination, const char **rsyncArgs, const int rsyncArgsNumber, const int shouldDebug) {
|
||||
void _error(const int shouldDebug, const int condition, const char *type, const char *file,
|
||||
const int line, const char *function, const char *message,
|
||||
...) { // used for formatted errors
|
||||
if (condition) {
|
||||
int h, m, s;
|
||||
getCurrentTime(&h, &m, &s);
|
||||
fprintf(stderr, "\e[0;31m[%s ERROR -- %d:%d:%d] From file %s line %d function %s:\n", type,
|
||||
h, m, s, file, line, function);
|
||||
if (errno != 0) {
|
||||
fprintf(stderr, " (System-level error message: %s)\n", strerror(errno));
|
||||
} else {
|
||||
fprintf(stderr, " (No system-level error; issue is application-level)\n");
|
||||
}
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
vfprintf(stderr, message, args);
|
||||
va_end(args);
|
||||
if (!shouldDebug) {
|
||||
fprintf(stderr, "\nRunning notewrapper -V might give you more information.");
|
||||
}
|
||||
fprintf(stderr, "\e[0m\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void copyDir(const char *source, const char *destination, const char **rsyncArgs,
|
||||
const int rsyncArgsNumber, const int shouldDebug) {
|
||||
debug("Backuping... source: %s and destination: %s", source, destination);
|
||||
debug("Rsync has %d extra arguments", rsyncArgsNumber);
|
||||
pid_t pid = fork();
|
||||
|
||||
|
||||
error(pid < 0, "program", "fork() faild. pid = %d", pid);
|
||||
|
||||
if (pid == 0) {
|
||||
// Child process: execute rsync
|
||||
char **args = malloc((4 + rsyncArgsNumber)*sizeof(char*));
|
||||
char **args = malloc((4 + rsyncArgsNumber) * sizeof(char *));
|
||||
args[0] = "rsync";
|
||||
for (int i = 0; i < rsyncArgsNumber; i++) {
|
||||
args[i+1] = (char *)rsyncArgs[i];
|
||||
if (i == rsyncArgsNumber-1) { // last loop
|
||||
args[i+2] = (char*)source;
|
||||
args[i+3] = (char*)destination;
|
||||
args[i+4] = NULL; // execvp expect last arg to be NULL
|
||||
debug("rsync command:");
|
||||
for (int k = 0; k <= i+4; k++) {
|
||||
altDebug("%s ", args[k]);
|
||||
args[i + 1] = (char *)rsyncArgs[i];
|
||||
if (i == rsyncArgsNumber - 1) { // last loop
|
||||
args[i + 2] = (char *)source;
|
||||
args[i + 3] = (char *)destination;
|
||||
args[i + 4] = NULL; // execvp expect last arg to be NULL
|
||||
debug("rsync command:");
|
||||
for (int k = 0; k <= i + 4; k++) {
|
||||
altDebug("%s ", args[k]);
|
||||
}
|
||||
altDebug("\n");
|
||||
}
|
||||
altDebug("\n");
|
||||
}
|
||||
}
|
||||
execvp("rsync", args);
|
||||
// If execvp returns, there was an error
|
||||
@@ -115,24 +122,21 @@ void initAppFilesAndDirs(const char *home, const int shouldDebug) {
|
||||
// sometimes .cache and .config doesn't exist. Such as with github actions machines
|
||||
char config_dir[PATH_MAX];
|
||||
char cache_dir[PATH_MAX];
|
||||
|
||||
snprintf(config_dir, sizeof(config_dir),
|
||||
"%s/.config/", home);
|
||||
|
||||
snprintf(cache_dir, sizeof(cache_dir),
|
||||
"%s/.cache/", home);
|
||||
snprintf(config_dir, sizeof(config_dir), "%s/.config/", home);
|
||||
|
||||
snprintf(cache_dir, sizeof(cache_dir), "%s/.cache/", home);
|
||||
|
||||
ensureDir(config_dir, shouldDebug);
|
||||
ensureDir(cache_dir, shouldDebug);
|
||||
|
||||
|
||||
strncat(config_dir, "notewrapper/", PATH_MAX);
|
||||
strncat(cache_dir, "notewrapper/", PATH_MAX);
|
||||
|
||||
ensureDir(config_dir, shouldDebug);
|
||||
ensureDir(cache_dir, shouldDebug);
|
||||
|
||||
|
||||
char config_file[PATH_MAX+12];
|
||||
char config_file[PATH_MAX + 12];
|
||||
snprintf(config_file, sizeof(config_file), "%s/config.json", config_dir);
|
||||
|
||||
FILE *f = fopen(config_file, "r");
|
||||
@@ -143,29 +147,34 @@ void initAppFilesAndDirs(const char *home, const int shouldDebug) {
|
||||
debug("Creating default config file.");
|
||||
FILE *w = fopen(config_file, "w");
|
||||
error(!w, "program", "fopen failed opening %s");
|
||||
fprintf(w, //TODO CHANGE default json
|
||||
"{\n"
|
||||
" \"directory\": [\"~/Documents/\"],\n"// I personally use ~/Documents/Notes. But the dir doesn't exist most times. So for the default user it is better to put ~/Documents
|
||||
" \"render\": true,\n"
|
||||
" \"jumpToEndOfFileOnLaunch\": true,\n"
|
||||
" \"editor\": \"neovim\",\n"
|
||||
" \"journalRegex\": \".*journal.*\",\n"
|
||||
" \"dateEntry\": \"# %%Y %%m %%d %%a\",\n"
|
||||
" \"newLineOnOpening\": true,\n"
|
||||
" \"backup\": {\n"
|
||||
" \"enable\": false,\n"
|
||||
" \"directory\": {\n"
|
||||
" \"~/Documents/\": \"path/to/backup/\"\n"
|
||||
" },\n"
|
||||
" \"interval\": \"weekly\",\n"
|
||||
" \"rsyncArgs\": [\"-Lqah\", \"--update\"]\n"
|
||||
" }\n"
|
||||
"}\n");
|
||||
fprintf(w, // TODO CHANGE default json
|
||||
"{\n"
|
||||
" \"directory\": [\"~/Documents/\"],\n" // I personally use ~/Documents/Notes. But the
|
||||
// dir doesn't exist most times. So for the
|
||||
// default user it is better to put ~/Documents
|
||||
" \"render\": true,\n"
|
||||
" \"jumpToEndOfFileOnLaunch\": true,\n"
|
||||
" \"editor\": \"neovim\",\n"
|
||||
" \"journalRegex\": \".*journal.*\",\n"
|
||||
" \"dateEntry\": \"# %%Y %%m %%d %%a\",\n"
|
||||
" \"newLineOnOpening\": true,\n"
|
||||
" \"backup\": {\n"
|
||||
" \"enable\": false,\n"
|
||||
" \"directory\": {\n"
|
||||
" \"~/Documents/\": \"path/to/backup/\"\n"
|
||||
" },\n"
|
||||
" \"interval\": \"weekly\",\n"
|
||||
" \"rsyncArgs\": [\"-Lqah\", \"--update\"]\n"
|
||||
" }\n"
|
||||
"}\n");
|
||||
|
||||
fclose(w);
|
||||
}
|
||||
|
||||
void handleBackups(char **sourceDirectoryArray, const int sourceNumber, char **destinationDirectoryArray, const char *homeDir, const int interval, const char **rsyncArguments, const int rsyncArgumentsNumber, const int shouldDebug) {
|
||||
void handleBackups(char **sourceDirectoryArray, const int sourceNumber,
|
||||
char **destinationDirectoryArray, const char *homeDir, const int interval,
|
||||
const char **rsyncArguments, const int rsyncArgumentsNumber,
|
||||
const int shouldDebug) {
|
||||
int shouldBackup = 0;
|
||||
time_t now = time(NULL);
|
||||
debug("Time since epoch is %ld", (long)now);
|
||||
@@ -202,7 +211,8 @@ void handleBackups(char **sourceDirectoryArray, const int sourceNumber, char **d
|
||||
}
|
||||
|
||||
if (difftime(now, lastBackupTime) > interval) {
|
||||
debug("(difftime) %d is greater than (interval) %d -> backuping...", difftime(now, lastBackupTime), interval);
|
||||
debug("(difftime) %d is greater than (interval) %d -> backuping...",
|
||||
difftime(now, lastBackupTime), interval);
|
||||
shouldBackup = 1;
|
||||
cacheFile = fopen(cacheFilePATH, "w");
|
||||
if (!cacheFile) {
|
||||
@@ -211,41 +221,57 @@ void handleBackups(char **sourceDirectoryArray, const int sourceNumber, char **d
|
||||
fprintf(cacheFile, "%ld\n", (long)now);
|
||||
fclose(cacheFile);
|
||||
} else {
|
||||
debug("(difftime) %d is smaller than (interval) %d -> no need to backup.", difftime(now, lastBackupTime), interval);
|
||||
debug("(difftime) %d is smaller than (interval) %d -> no need to backup.",
|
||||
difftime(now, lastBackupTime), interval);
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldBackup) {
|
||||
debug("A backup is needed");
|
||||
for (int i = 0; i < sourceNumber; i++) {
|
||||
debug("%s", destinationDirectoryArray[i]);
|
||||
if (destinationDirectoryArray[i]) { // if we don't want to backup it, it was set to NULL
|
||||
copyDir(sourceDirectoryArray[i], destinationDirectoryArray[i], rsyncArguments, rsyncArgumentsNumber, shouldDebug);
|
||||
debug("A backup is needed");
|
||||
for (int i = 0; i < sourceNumber; i++) {
|
||||
debug("%s", destinationDirectoryArray[i]);
|
||||
if (destinationDirectoryArray[i]) { // if we don't want to backup it, it was set to NULL
|
||||
copyDir(sourceDirectoryArray[i], destinationDirectoryArray[i], rsyncArguments,
|
||||
rsyncArgumentsNumber, shouldDebug);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int isEditorValid (char *editorToCheck, int useDefaultEditor, int shouldDebug) { // check if editor is supported and if it is installed
|
||||
// check if supported
|
||||
int isEditorValid(char *editorToCheck, int useDefaultEditor,
|
||||
int shouldDebug) { // check if editor is supported and if it is installed
|
||||
// check if supported
|
||||
if (useDefaultEditor) { // we use a different error message if it defaulted to $EDITOR
|
||||
error(!isStringInArray(editorToCheck, supportedEditor, numEditors), "user", "%s is not a supported editor.\n(defaulted to $EDITOR as neither \"editor\" was set in the configuration file nor -e/--editor <editor> was set.)\n See https://github.com/tomasriveral/NoteWrapper#editor-support for a list of supported editors.", editorToCheck);
|
||||
error(!isStringInArray(editorToCheck, supportedEditor, numEditors), "user",
|
||||
"%s is not a supported editor.\n(defaulted to $EDITOR as neither \"editor\" was set "
|
||||
"in the configuration file nor -e/--editor <editor> was set.)\n See "
|
||||
"https://github.com/tomasriveral/NoteWrapper#editor-support for a list of supported "
|
||||
"editors.",
|
||||
editorToCheck);
|
||||
} else {
|
||||
error(!isStringInArray(editorToCheck, supportedEditor, numEditors), "user", "%s is not a supported editor.\n See https://github.com/tomasriveral/NoteWrapper#editor-support for a list of supported editors.", editorToCheck);
|
||||
error(!isStringInArray(editorToCheck, supportedEditor, numEditors), "user",
|
||||
"%s is not a supported editor.\n See "
|
||||
"https://github.com/tomasriveral/NoteWrapper#editor-support for a list of supported "
|
||||
"editors.",
|
||||
editorToCheck);
|
||||
}
|
||||
// check if installed
|
||||
char *editor;
|
||||
if (strcmp(editorToCheck, "neovim") == 0) { // some executables are not name the same as the project
|
||||
editor = strdup("nvim"); // we must use strdup and not just copy as we would have modified editorToOpen in main
|
||||
char *editor;
|
||||
if (strcmp(editorToCheck, "neovim") ==
|
||||
0) { // some executables are not name the same as the project
|
||||
editor = strdup("nvim"); // we must use strdup and not just copy as we would have modified
|
||||
// editorToOpen in main
|
||||
} else if (strcmp(editorToCheck, "helix") == 0) {
|
||||
editor = strdup("hx");
|
||||
editor = strdup("hx");
|
||||
} else if (strcmp(editorToCheck, "kakoune") == 0) {
|
||||
editor = strdup("kak");
|
||||
editor = strdup("kak");
|
||||
} else {
|
||||
editor = strdup(editorToCheck);
|
||||
editor = strdup(editorToCheck);
|
||||
}
|
||||
char *path_env = getenv("PATH");
|
||||
error(!path_env, "program", "getenv(\"PATH\") failed to get your path. NoteWrapper is unable to check if your desired editor is installed\n");
|
||||
error(!path_env, "program",
|
||||
"getenv(\"PATH\") failed to get your path. NoteWrapper is unable to check if your "
|
||||
"desired editor is installed\n");
|
||||
debug("Your PATH is %s", path_env);
|
||||
char *paths = strdup(path_env); // duplicate because strtok modifies the string
|
||||
char *dir = strtok(paths, ":");
|
||||
@@ -266,43 +292,47 @@ int isEditorValid (char *editorToCheck, int useDefaultEditor, int shouldDebug) {
|
||||
}
|
||||
|
||||
char *getFormatedTime(char *format, int shouldDebug) {
|
||||
debug("Inputed time format is %s", format);
|
||||
time_t t = time(NULL);
|
||||
struct tm *tm = localtime(&t);
|
||||
char *buf = malloc(BUFFER_SIZE);
|
||||
strftime(buf, BUFFER_SIZE, format, tm);
|
||||
debug("Formated time is %s", buf);
|
||||
return buf;
|
||||
debug("Inputed time format is %s", format);
|
||||
time_t t = time(NULL);
|
||||
struct tm *tm = localtime(&t);
|
||||
char *buf = malloc(BUFFER_SIZE);
|
||||
strftime(buf, BUFFER_SIZE, format, tm);
|
||||
debug("Formated time is %s", buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
int isStringInArray(const char *string, const char **array, const int len) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (strcmp(string, array[i]) == 0) {
|
||||
return 1;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (strcmp(string, array[i]) == 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int isStringInFile(const char *path, const char *string, const int shouldDebug) {
|
||||
FILE *file = fopen(path, "r");
|
||||
error(file == NULL, "program", "While searching for \"%s\", we could not open file %s", string, path);
|
||||
char buf[BUFFER_SIZE];
|
||||
|
||||
while (fgets(buf, BUFFER_SIZE, file) != NULL) {
|
||||
if (strstr(buf, string) != NULL) {
|
||||
debug("%s contains the substring %s", buf, string);
|
||||
return 1;
|
||||
FILE *file = fopen(path, "r");
|
||||
error(file == NULL, "program", "While searching for \"%s\", we could not open file %s", string,
|
||||
path);
|
||||
char buf[BUFFER_SIZE];
|
||||
|
||||
while (fgets(buf, BUFFER_SIZE, file) != NULL) {
|
||||
if (strstr(buf, string) != NULL) {
|
||||
debug("%s contains the substring %s", buf, string);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
debug("The string %s is not inside %s", string, path);
|
||||
return 0;
|
||||
debug("The string %s is not inside %s", string, path);
|
||||
return 0;
|
||||
}
|
||||
void appendToFile(const char *path, const char *string, const int shouldDebug) {
|
||||
FILE *file = fopen(path, "r");
|
||||
char lastLine[1024] = {0};
|
||||
error(file == NULL, "program", "could not open %s", path);
|
||||
char buffer[BUFFER_SIZE]; // it does not matter if we have a small buffer. string is relatively small (most time \n or the name of the file). So it is under BUFFER_SIZE. If the last line is more than BUFFER_SIZE. It can't be equal to string
|
||||
char buffer[BUFFER_SIZE]; // it does not matter if we have a small buffer. string is relatively
|
||||
// small (most time \n or the name of the file). So it is under
|
||||
// BUFFER_SIZE. If the last line is more than BUFFER_SIZE. It can't be
|
||||
// equal to string
|
||||
|
||||
// Read file line by line to get the last one
|
||||
while (fgets(buffer, sizeof(buffer), file) != NULL) {
|
||||
@@ -329,17 +359,18 @@ void appendToFile(const char *path, const char *string, const int shouldDebug) {
|
||||
}
|
||||
|
||||
void sanitize(char *string) {
|
||||
size_t stringLenght = strlen(string);
|
||||
for (size_t i = 0; i < stringLenght; i++) {
|
||||
if ((!isalnum((unsigned char)string[i]) && strchr("~/\\:*?\"\'|!$[]{}<>\n\r\t", string[i]))) { // replace unwanted chars by '_'
|
||||
string[i] = '_';
|
||||
size_t stringLenght = strlen(string);
|
||||
for (size_t i = 0; i < stringLenght; i++) {
|
||||
if ((!isalnum((unsigned char)string[i]) &&
|
||||
strchr("~/\\:*?\"\'|!$[]{}<>\n\r\t", string[i]))) { // replace unwanted chars by '_'
|
||||
string[i] = '_';
|
||||
}
|
||||
}
|
||||
// removes any leading '.
|
||||
size_t i = 0;
|
||||
while (i < stringLenght && string[i] == '.') {
|
||||
string[i++] = '_';
|
||||
}
|
||||
}
|
||||
// removes any leading '.
|
||||
size_t i = 0;
|
||||
while (i < stringLenght && string[i] == '.') {
|
||||
string[i++] = '_';
|
||||
}
|
||||
}
|
||||
|
||||
// both functions are from https://stackoverflow.com/a/5467788
|
||||
@@ -347,301 +378,296 @@ void sanitize(char *string) {
|
||||
// remove() can't delete directories with files
|
||||
// so it walks the file tree and deletes it's content before removing the directory
|
||||
int unlink_cb(const char *filePath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
|
||||
(void)sb;
|
||||
(void)typeflag;
|
||||
(void)ftwbuf;
|
||||
(void)sb;
|
||||
(void)typeflag;
|
||||
(void)ftwbuf;
|
||||
|
||||
int shouldDebug = 1; //normally shouldDebug should be passed to the function. However, it's to difficult here and the best it to set it to 1.
|
||||
int shouldDebug = 1; // normally shouldDebug should be passed to the function. However, it's to
|
||||
// difficult here and the best it to set it to 1.
|
||||
|
||||
// some sanity checks to be sure that we don't delete something we shouldn't
|
||||
error(strcmp(filePath, "") == 0, "program", "filePath is empty. Refusing to delete directory");
|
||||
error(filePath[0] == '.', "program", "%s starts with \".\". For security reasons, use absolute path and not relative path.", filePath);
|
||||
// some sanity checks to be sure that we don't delete something we shouldn't
|
||||
error(strcmp(filePath, "") == 0, "program", "filePath is empty. Refusing to delete directory");
|
||||
error(filePath[0] == '.', "program",
|
||||
"%s starts with \".\". For security reasons, use absolute path and not relative path.",
|
||||
filePath);
|
||||
|
||||
int rv = remove(filePath);
|
||||
error(rv, "program", "remove() failed to delete %s", filePath);
|
||||
return rv;
|
||||
int rv = remove(filePath);
|
||||
error(rv, "program", "remove() failed to delete %s", filePath);
|
||||
return rv;
|
||||
}
|
||||
|
||||
int rmrf(char *path, int shouldDebug) {
|
||||
error(strcmp(path, "/") == 0, "program", "Refusing to delete root directory");
|
||||
return nftw(path, unlink_cb, 64, FTW_DEPTH | FTW_PHYS);
|
||||
error(strcmp(path, "/") == 0, "program", "Refusing to delete root directory");
|
||||
return nftw(path, unlink_cb, 64, FTW_DEPTH | FTW_PHYS);
|
||||
}
|
||||
|
||||
|
||||
int openEditor(char *path, char *editor, int render, int shouldJumpToEndOfFile, int shouldDebug) {
|
||||
|
||||
// this ensures that ncurses won't affect the editor behaviour
|
||||
pid_t editor_pid = fork();
|
||||
error(editor_pid < 0, "program", "fork() failed.");
|
||||
// this ensures that ncurses won't affect the editor behaviour
|
||||
pid_t editor_pid = fork();
|
||||
error(editor_pid < 0, "program", "fork() failed.");
|
||||
|
||||
// instead of reusing part of the codes, for any new editor copy an example and adapt it. This is in case we need a custom fix for an editor.
|
||||
// instead of reusing part of the codes, for any new editor copy an example and adapt it. This
|
||||
// is in case we need a custom fix for an editor.
|
||||
|
||||
if (editor_pid == 0) {
|
||||
// =========================
|
||||
// CHILD: launch editor
|
||||
// =========================
|
||||
if (editor_pid == 0) {
|
||||
// =========================
|
||||
// CHILD: launch editor
|
||||
// =========================
|
||||
|
||||
// ---- NEOVIM / VIM ----
|
||||
if (strcmp(editor, "neovim") == 0 || strcmp(editor, "vim") == 0) {
|
||||
const char *bin = (strcmp(editor, "neovim") == 0) ? "nvim" : "vim";
|
||||
// ---- NEOVIM / VIM ----
|
||||
if (strcmp(editor, "neovim") == 0 || strcmp(editor, "vim") == 0) {
|
||||
const char *bin = (strcmp(editor, "neovim") == 0) ? "nvim" : "vim";
|
||||
|
||||
if (render) {
|
||||
if (shouldJumpToEndOfFile) {
|
||||
debug("Running %s +:$ +:Vivify %s", bin, path);
|
||||
execlp(bin, bin, "+:$", "+:Vivify", path, NULL);
|
||||
} else {
|
||||
debug("Running %s +:Vivify %s", bin, path);
|
||||
execlp(bin, bin, "+:Vivify", path, NULL);
|
||||
}
|
||||
} else {
|
||||
if (shouldJumpToEndOfFile) {
|
||||
debug("Running %s +:$ %s", bin, path);
|
||||
execlp(bin, bin, "+:$", path, NULL);
|
||||
} else {
|
||||
debug("Running %s %s", bin, path);
|
||||
execlp(bin, bin, path, NULL);
|
||||
}
|
||||
}
|
||||
if (render) {
|
||||
if (shouldJumpToEndOfFile) {
|
||||
debug("Running %s +:$ +:Vivify %s", bin, path);
|
||||
execlp(bin, bin, "+:$", "+:Vivify", path, NULL);
|
||||
} else {
|
||||
debug("Running %s +:Vivify %s", bin, path);
|
||||
execlp(bin, bin, "+:Vivify", path, NULL);
|
||||
}
|
||||
} else {
|
||||
if (shouldJumpToEndOfFile) {
|
||||
debug("Running %s +:$ %s", bin, path);
|
||||
execlp(bin, bin, "+:$", path, NULL);
|
||||
} else {
|
||||
debug("Running %s %s", bin, path);
|
||||
execlp(bin, bin, path, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
error(1, "program", "execlp() failed.");
|
||||
// ---- MICRO ----
|
||||
} else if (strcmp(editor, "micro") == 0) {
|
||||
error(1, "program", "execlp() failed.");
|
||||
// ---- MICRO ----
|
||||
} else if (strcmp(editor, "micro") == 0) {
|
||||
|
||||
if (render) {
|
||||
if (shouldJumpToEndOfFile) {
|
||||
debug("Running MICRO_VIVIFY=1 micro %s +99999", path);
|
||||
setenv("MICRO_VIVIFY", "1", 1);
|
||||
execlp("micro", "micro", path, "+999999", NULL);
|
||||
} else {
|
||||
debug("Running MICRO_VIVIFY=1 micro %s", path);
|
||||
setenv("MICRO_VIVIFY", "1", 1);
|
||||
execlp("micro", "micro", path, NULL);
|
||||
}
|
||||
} else {
|
||||
if (shouldJumpToEndOfFile) {
|
||||
debug("Running micro %s +999999", path);
|
||||
execlp("micro", "micro", path, "+999999", NULL);
|
||||
} else {
|
||||
debug("Running micro %s", path);
|
||||
execlp("micro", "micro", path, NULL);
|
||||
}
|
||||
}
|
||||
if (render) {
|
||||
if (shouldJumpToEndOfFile) {
|
||||
debug("Running MICRO_VIVIFY=1 micro %s +99999", path);
|
||||
setenv("MICRO_VIVIFY", "1", 1);
|
||||
execlp("micro", "micro", path, "+999999", NULL);
|
||||
} else {
|
||||
debug("Running MICRO_VIVIFY=1 micro %s", path);
|
||||
setenv("MICRO_VIVIFY", "1", 1);
|
||||
execlp("micro", "micro", path, NULL);
|
||||
}
|
||||
} else {
|
||||
if (shouldJumpToEndOfFile) {
|
||||
debug("Running micro %s +999999", path);
|
||||
execlp("micro", "micro", path, "+999999", NULL);
|
||||
} else {
|
||||
debug("Running micro %s", path);
|
||||
execlp("micro", "micro", path, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
error(1, "program", "execlp() failed.");
|
||||
}
|
||||
|
||||
// ---- NANO ----
|
||||
else if (strcmp(editor, "nano") == 0) {
|
||||
|
||||
// If render enabled → spawn viv in parallel
|
||||
if (render) {
|
||||
debug("Running Vivify...");
|
||||
pid_t viv_pid = fork();
|
||||
error(viv_pid < 0, "program", "fork() failed.");
|
||||
|
||||
if (viv_pid == 0) {
|
||||
// GRANDCHILD → viv
|
||||
|
||||
|
||||
char viv_path[PATH_MAX];
|
||||
strncpy(viv_path, path, PATH_MAX - 1);
|
||||
viv_path[PATH_MAX - 1] = '\0';
|
||||
|
||||
if (shouldJumpToEndOfFile) {
|
||||
strncat(viv_path, ":99999",
|
||||
PATH_MAX - strlen(viv_path) - 1);
|
||||
error(1, "program", "execlp() failed.");
|
||||
}
|
||||
|
||||
debug("Running viv %s", viv_path);
|
||||
execlp("viv", "viv", viv_path, NULL);
|
||||
error(1, "program", "execlp() failed.");
|
||||
}
|
||||
// IMPORTANT: do NOT wait for viv
|
||||
}
|
||||
// ---- NANO ----
|
||||
else if (strcmp(editor, "nano") == 0) {
|
||||
|
||||
// Now run nano (this replaces the child process)
|
||||
if (shouldJumpToEndOfFile) {
|
||||
debug("Running nano + %s", path);
|
||||
execlp("nano", "nano", "+", path, NULL);
|
||||
} else {
|
||||
debug("Running nano %s", path);
|
||||
execlp("nano", "nano", path, NULL);
|
||||
}
|
||||
// If render enabled → spawn viv in parallel
|
||||
if (render) {
|
||||
debug("Running Vivify...");
|
||||
pid_t viv_pid = fork();
|
||||
error(viv_pid < 0, "program", "fork() failed.");
|
||||
|
||||
error(1, "program", "execlp() failed.");
|
||||
// ---- HELIX ----
|
||||
} else if (strcmp(editor, "helix") == 0) {
|
||||
if (viv_pid == 0) {
|
||||
// GRANDCHILD → viv
|
||||
|
||||
// If render enabled → spawn viv in parallel
|
||||
if (render) {
|
||||
debug("Running Vivify...");
|
||||
pid_t viv_pid = fork();
|
||||
error(viv_pid < 0, "program", "fork() failed.");
|
||||
char viv_path[PATH_MAX];
|
||||
strncpy(viv_path, path, PATH_MAX - 1);
|
||||
viv_path[PATH_MAX - 1] = '\0';
|
||||
|
||||
if (viv_pid == 0) {
|
||||
// GRANDCHILD → viv
|
||||
|
||||
if (shouldJumpToEndOfFile) {
|
||||
strncat(viv_path, ":99999", PATH_MAX - strlen(viv_path) - 1);
|
||||
}
|
||||
|
||||
char viv_path[PATH_MAX];
|
||||
strncpy(viv_path, path, PATH_MAX - 1);
|
||||
viv_path[PATH_MAX - 1] = '\0';
|
||||
debug("Running viv %s", viv_path);
|
||||
execlp("viv", "viv", viv_path, NULL);
|
||||
error(1, "program", "execlp() failed.");
|
||||
}
|
||||
// IMPORTANT: do NOT wait for viv
|
||||
}
|
||||
|
||||
if (shouldJumpToEndOfFile) {
|
||||
strncat(viv_path, ":99999",
|
||||
PATH_MAX - strlen(viv_path) - 1);
|
||||
// Now run nano (this replaces the child process)
|
||||
if (shouldJumpToEndOfFile) {
|
||||
debug("Running nano + %s", path);
|
||||
execlp("nano", "nano", "+", path, NULL);
|
||||
} else {
|
||||
debug("Running nano %s", path);
|
||||
execlp("nano", "nano", path, NULL);
|
||||
}
|
||||
|
||||
error(1, "program", "execlp() failed.");
|
||||
// ---- HELIX ----
|
||||
} else if (strcmp(editor, "helix") == 0) {
|
||||
|
||||
// If render enabled → spawn viv in parallel
|
||||
if (render) {
|
||||
debug("Running Vivify...");
|
||||
pid_t viv_pid = fork();
|
||||
error(viv_pid < 0, "program", "fork() failed.");
|
||||
|
||||
if (viv_pid == 0) {
|
||||
// GRANDCHILD → viv
|
||||
|
||||
char viv_path[PATH_MAX];
|
||||
strncpy(viv_path, path, PATH_MAX - 1);
|
||||
viv_path[PATH_MAX - 1] = '\0';
|
||||
|
||||
if (shouldJumpToEndOfFile) {
|
||||
strncat(viv_path, ":99999", PATH_MAX - strlen(viv_path) - 1);
|
||||
}
|
||||
|
||||
debug("Running viv %s", viv_path);
|
||||
execlp("viv", "viv", viv_path, NULL);
|
||||
error(1, "program", "execlp() failed.");
|
||||
}
|
||||
// IMPORTANT: do NOT wait for viv
|
||||
}
|
||||
|
||||
// Now run helix (this replaces the child process)
|
||||
if (shouldJumpToEndOfFile) {
|
||||
strncat(path, ":99999", PATH_MAX);
|
||||
debug("Running hx %s", path);
|
||||
execlp("hx", "hx", path, NULL);
|
||||
} else {
|
||||
debug("Running hx %s", path);
|
||||
execlp("hx", "hx", path, NULL);
|
||||
}
|
||||
|
||||
error(1, "program", "execlp() failed.");
|
||||
}
|
||||
// ---- kakoune ----
|
||||
else if (strcmp(editor, "kakoune") == 0) {
|
||||
|
||||
debug("Running viv %s", viv_path);
|
||||
execlp("viv", "viv", viv_path, NULL);
|
||||
error(1, "program", "execlp() failed.");
|
||||
}
|
||||
// IMPORTANT: do NOT wait for viv
|
||||
}
|
||||
// If render enabled → spawn viv in parallel
|
||||
if (render) {
|
||||
debug("Running Vivify...");
|
||||
pid_t viv_pid = fork();
|
||||
error(viv_pid < 0, "program", "fork() failed.");
|
||||
|
||||
// Now run helix (this replaces the child process)
|
||||
if (shouldJumpToEndOfFile) {
|
||||
strncat(path, ":99999", PATH_MAX);
|
||||
debug("Running hx %s", path);
|
||||
execlp("hx", "hx", path, NULL);
|
||||
} else {
|
||||
debug("Running hx %s", path);
|
||||
execlp("hx", "hx", path, NULL);
|
||||
}
|
||||
if (viv_pid == 0) {
|
||||
// GRANDCHILD → viv
|
||||
|
||||
error(1, "program", "execlp() failed.");
|
||||
}
|
||||
// ---- kakoune ----
|
||||
else if (strcmp(editor, "kakoune") == 0) {
|
||||
char viv_path[PATH_MAX];
|
||||
strncpy(viv_path, path, PATH_MAX - 1);
|
||||
viv_path[PATH_MAX - 1] = '\0';
|
||||
|
||||
// If render enabled → spawn viv in parallel
|
||||
if (render) {
|
||||
debug("Running Vivify...");
|
||||
pid_t viv_pid = fork();
|
||||
error(viv_pid < 0, "program", "fork() failed.");
|
||||
if (shouldJumpToEndOfFile) {
|
||||
strncat(viv_path, ":99999", PATH_MAX - strlen(viv_path) - 1);
|
||||
}
|
||||
|
||||
if (viv_pid == 0) {
|
||||
// GRANDCHILD → viv
|
||||
|
||||
debug("Running viv %s", viv_path);
|
||||
execlp("viv", "viv", viv_path, NULL);
|
||||
error(1, "program", "execlp() failed.");
|
||||
}
|
||||
// IMPORTANT: do NOT wait for viv
|
||||
}
|
||||
|
||||
char viv_path[PATH_MAX];
|
||||
strncpy(viv_path, path, PATH_MAX - 1);
|
||||
viv_path[PATH_MAX - 1] = '\0';
|
||||
// Now run nano (this replaces the child process)
|
||||
if (shouldJumpToEndOfFile) {
|
||||
debug("Running kak + %s", path);
|
||||
execlp("kak", "kak", "+", path, NULL);
|
||||
} else {
|
||||
debug("Running kak %s", path);
|
||||
execlp("kak", "kak", path, NULL);
|
||||
}
|
||||
|
||||
if (shouldJumpToEndOfFile) {
|
||||
strncat(viv_path, ":99999",
|
||||
PATH_MAX - strlen(viv_path) - 1);
|
||||
error(1, "program", "execlp() failed.");
|
||||
// ---- VI ----
|
||||
} else if (strcmp(editor, "vi") == 0) {
|
||||
|
||||
// If render enabled → spawn viv in parallel
|
||||
if (render) {
|
||||
debug("Running Vivify...");
|
||||
pid_t viv_pid = fork();
|
||||
error(viv_pid < 0, "program", "fork() failed.");
|
||||
|
||||
if (viv_pid == 0) {
|
||||
// GRANDCHILD → viv
|
||||
|
||||
char viv_path[PATH_MAX];
|
||||
strncpy(viv_path, path, PATH_MAX - 1);
|
||||
viv_path[PATH_MAX - 1] = '\0';
|
||||
|
||||
if (shouldJumpToEndOfFile) {
|
||||
strncat(viv_path, ":99999", PATH_MAX - strlen(viv_path) - 1);
|
||||
}
|
||||
|
||||
debug("Running viv %s", viv_path);
|
||||
execlp("viv", "viv", viv_path, NULL);
|
||||
error(1, "program", "execlp() failed.");
|
||||
}
|
||||
// IMPORTANT: do NOT wait for viv
|
||||
}
|
||||
|
||||
// Now run vi (this replaces the child process)
|
||||
if (shouldJumpToEndOfFile) {
|
||||
debug("Running vi +:$ %s", path);
|
||||
execlp("vi", "vi", "+:$", path, NULL);
|
||||
} else {
|
||||
debug("Running vi %s", path);
|
||||
execlp("vi", "vi", path, NULL);
|
||||
}
|
||||
|
||||
error(1, "program", "execlp() failed.");
|
||||
// ---- Jed ----
|
||||
} else if (strcmp(editor, "jed") == 0) {
|
||||
|
||||
// If render enabled → spawn viv in parallel
|
||||
if (render) {
|
||||
debug("Running Vivify...");
|
||||
pid_t viv_pid = fork();
|
||||
error(viv_pid < 0, "program", "fork() failed.");
|
||||
|
||||
if (viv_pid == 0) {
|
||||
// GRANDCHILD → viv
|
||||
|
||||
char viv_path[PATH_MAX];
|
||||
strncpy(viv_path, path, PATH_MAX - 1);
|
||||
viv_path[PATH_MAX - 1] = '\0';
|
||||
|
||||
if (shouldJumpToEndOfFile) {
|
||||
strncat(viv_path, ":99999", PATH_MAX - strlen(viv_path) - 1);
|
||||
}
|
||||
|
||||
debug("Running viv %s", viv_path);
|
||||
execlp("viv", "viv", viv_path, NULL);
|
||||
error(1, "program", "execlp() failed.");
|
||||
}
|
||||
// IMPORTANT: do NOT wait for viv
|
||||
}
|
||||
|
||||
// Now run jed (this replaces the child process)
|
||||
if (shouldJumpToEndOfFile) {
|
||||
debug("Running jed %s -g 99999999", path);
|
||||
execlp("jed", "jed", path, "-g", "99999999", NULL);
|
||||
} else {
|
||||
debug("Running jed %s", path);
|
||||
execlp("jed", "jed", path, NULL);
|
||||
}
|
||||
|
||||
error(1, "program", "execlp() failed.");
|
||||
// ---- UNKNOWN EDITOR ----
|
||||
} else {
|
||||
error(1, "program", "Unknown editor.");
|
||||
}
|
||||
|
||||
debug("Running viv %s", viv_path);
|
||||
execlp("viv", "viv", viv_path, NULL);
|
||||
error(1, "program", "execlp() failed.");
|
||||
}
|
||||
// IMPORTANT: do NOT wait for viv
|
||||
}
|
||||
|
||||
// Now run nano (this replaces the child process)
|
||||
if (shouldJumpToEndOfFile) {
|
||||
debug("Running kak + %s", path);
|
||||
execlp("kak", "kak", "+", path, NULL);
|
||||
} else {
|
||||
debug("Running kak %s", path);
|
||||
execlp("kak", "kak", path, NULL);
|
||||
}
|
||||
|
||||
error(1, "program", "execlp() failed.");
|
||||
// ---- VI ----
|
||||
} else if (strcmp(editor, "vi") == 0) {
|
||||
|
||||
// If render enabled → spawn viv in parallel
|
||||
if (render) {
|
||||
debug("Running Vivify...");
|
||||
pid_t viv_pid = fork();
|
||||
error(viv_pid < 0, "program", "fork() failed.");
|
||||
|
||||
if (viv_pid == 0) {
|
||||
// GRANDCHILD → viv
|
||||
|
||||
|
||||
char viv_path[PATH_MAX];
|
||||
strncpy(viv_path, path, PATH_MAX - 1);
|
||||
viv_path[PATH_MAX - 1] = '\0';
|
||||
|
||||
if (shouldJumpToEndOfFile) {
|
||||
strncat(viv_path, ":99999",
|
||||
PATH_MAX - strlen(viv_path) - 1);
|
||||
}
|
||||
|
||||
debug("Running viv %s", viv_path);
|
||||
execlp("viv", "viv", viv_path, NULL);
|
||||
error(1, "program", "execlp() failed.");
|
||||
}
|
||||
// IMPORTANT: do NOT wait for viv
|
||||
}
|
||||
|
||||
// Now run vi (this replaces the child process)
|
||||
if (shouldJumpToEndOfFile) {
|
||||
debug("Running vi +:$ %s", path);
|
||||
execlp("vi", "vi", "+:$", path, NULL);
|
||||
} else {
|
||||
debug("Running vi %s", path);
|
||||
execlp("vi", "vi", path, NULL);
|
||||
}
|
||||
|
||||
error(1, "program", "execlp() failed.");
|
||||
// ---- Jed ----
|
||||
} else if (strcmp(editor, "jed") == 0) {
|
||||
|
||||
// If render enabled → spawn viv in parallel
|
||||
if (render) {
|
||||
debug("Running Vivify...");
|
||||
pid_t viv_pid = fork();
|
||||
error(viv_pid < 0, "program", "fork() failed.");
|
||||
|
||||
if (viv_pid == 0) {
|
||||
// GRANDCHILD → viv
|
||||
|
||||
|
||||
char viv_path[PATH_MAX];
|
||||
strncpy(viv_path, path, PATH_MAX - 1);
|
||||
viv_path[PATH_MAX - 1] = '\0';
|
||||
|
||||
if (shouldJumpToEndOfFile) {
|
||||
strncat(viv_path, ":99999",
|
||||
PATH_MAX - strlen(viv_path) - 1);
|
||||
}
|
||||
|
||||
debug("Running viv %s", viv_path);
|
||||
execlp("viv", "viv", viv_path, NULL);
|
||||
error(1, "program", "execlp() failed.");
|
||||
}
|
||||
// IMPORTANT: do NOT wait for viv
|
||||
}
|
||||
|
||||
// Now run jed (this replaces the child process)
|
||||
if (shouldJumpToEndOfFile) {
|
||||
debug("Running jed %s -g 99999999", path);
|
||||
execlp("jed", "jed", path, "-g", "99999999", NULL);
|
||||
} else {
|
||||
debug("Running jed %s", path);
|
||||
execlp("jed", "jed", path, NULL);
|
||||
}
|
||||
|
||||
error(1, "program", "execlp() failed.");
|
||||
// ---- UNKNOWN EDITOR ----
|
||||
} else {
|
||||
error(1, "program", "Unknown editor.");
|
||||
}
|
||||
}
|
||||
|
||||
// =========================
|
||||
// PARENT: wait ONLY editor
|
||||
// =========================
|
||||
int status;
|
||||
while (waitpid(editor_pid, &status, 0) == -1) { // we can't just use waitpid(). Because when resizing the terminal, waitpid() is returned so we loop to see if there is not a problem
|
||||
if (errno != EINTR) {
|
||||
perror("waitpid");
|
||||
break;
|
||||
}
|
||||
while (waitpid(editor_pid, &status, 0) ==
|
||||
-1) { // we can't just use waitpid(). Because when resizing the terminal, waitpid() is
|
||||
// returned so we loop to see if there is not a problem
|
||||
if (errno != EINTR) {
|
||||
perror("waitpid");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
+35
-32
@@ -1,60 +1,60 @@
|
||||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
|
||||
#include <cjson/cJSON.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <ftw.h>
|
||||
#include <limits.h>
|
||||
#include <ncurses.h>
|
||||
#include <pwd.h>
|
||||
#include <regex.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <ftw.h>
|
||||
#include <pwd.h>
|
||||
#include <ncurses.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/wait.h>
|
||||
#include <limits.h>
|
||||
#include <cjson/cJSON.h>
|
||||
#include <regex.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <ftw.h>
|
||||
#include <unistd.h>
|
||||
#ifndef VERSION
|
||||
#define VERSION "dev"
|
||||
#endif
|
||||
#define BUFFER_SIZE 256 //standard buffer size.
|
||||
// the three supported values are "daily" "weekly" and "monthly" (for months we use the avarage lenght of a month in a non leap year). All values were calculated on my loyal TI-30 ECO RS
|
||||
#define BUFFER_SIZE 256 // standard buffer size.
|
||||
// the three supported values are "daily" "weekly" and "monthly" (for months we use the avarage
|
||||
// lenght of a month in a non leap year). All values were calculated on my loyal TI-30 ECO RS
|
||||
#define DAILY 86400
|
||||
#define WEEKLY 604800
|
||||
#define MONTHLY 2628000 // calculated from the average lenght of a non leap year
|
||||
#define debug(message, ...) \
|
||||
#define debug(message, ...) \
|
||||
_debug(shouldDebug, __FILE__, __LINE__, __func__, message, ##__VA_ARGS__)
|
||||
#define altDebug(message, ...) \
|
||||
_altDebug(shouldDebug, message, ##__VA_ARGS__)
|
||||
#define error(condition, type, message, ...) \
|
||||
_error(shouldDebug, condition, type, __FILE__, __LINE__, __func__, message, ##__VA_ARGS__)
|
||||
#define altDebug(message, ...) _altDebug(shouldDebug, message, ##__VA_ARGS__)
|
||||
#define error(condition, type, message, ...) \
|
||||
_error(shouldDebug, condition, type, __FILE__, __LINE__, __func__, message, ##__VA_ARGS__)
|
||||
// you must edit this two values if you want to add suport for an editor
|
||||
extern const char *supportedEditor[]; // array of supported editors
|
||||
extern const int numEditors; // number of supported editors
|
||||
extern const int numEditors; // number of supported editors
|
||||
|
||||
|
||||
//compares two strings alphabetically.
|
||||
//this function is used for qsort
|
||||
// compares two strings alphabetically.
|
||||
// this function is used for qsort
|
||||
int compareString(const void *a, const void *b);
|
||||
// compares two strings in reversed alphabetical order
|
||||
// this function is used for qsort
|
||||
int reverseCompareString(const void *a, const void *b);
|
||||
// checks if editor is supported and if it installed.
|
||||
// this basically checks all the dirs from your path for the editor. This is a safety check.
|
||||
// If the executable from an editor is not the editor name (for example neovim and nvim), you must handle at the start of the function.
|
||||
// This can return an error and stop the program.
|
||||
// If the executable from an editor is not the editor name (for example neovim and nvim), you must
|
||||
// handle at the start of the function. This can return an error and stop the program.
|
||||
int isEditorValid(char *editorToCheck, int useDefaultEditor, int debug);
|
||||
// please use the macro debug instead of _debug.
|
||||
//formated debugging.
|
||||
void _debug(const int d, const char *file, const int line, const char *function, const char *message, ...);
|
||||
//use for less formal debuggin. (usefull if enumerating or making a list).
|
||||
// formated debugging.
|
||||
void _debug(const int d, const char *file, const int line, const char *function,
|
||||
const char *message, ...);
|
||||
// use for less formal debuggin. (usefull if enumerating or making a list).
|
||||
void _altDebug(const int d, const char *message, ...);
|
||||
// formated error.
|
||||
void _error(const int shouldDebug, const int condition, const char *type, const char *file, const int line, const char *function, const char *message, ...);
|
||||
void _error(const int shouldDebug, const int condition, const char *type, const char *file,
|
||||
const int line, const char *function, const char *message, ...);
|
||||
// Returns 1 if the string is in the array.
|
||||
// Returns 0 if the string is not in the array.
|
||||
// If you want to only check the first n elements of the array, pass n as len.
|
||||
@@ -68,8 +68,8 @@ void appendToFile(const char *path, const char *string, const int shouldDebug);
|
||||
// replace unwanted chars by '_'.
|
||||
// '.' is replaced if it is only the first two chars
|
||||
void sanitize(char *string);
|
||||
//from https://stackoverflow.com/a/5467788.
|
||||
//deletes an entire directory. Use with parsimony and carefullness.
|
||||
// from https://stackoverflow.com/a/5467788.
|
||||
// deletes an entire directory. Use with parsimony and carefullness.
|
||||
int rmrf(char *path, int shouldDebug);
|
||||
// Inputs are the path to the file, the editor to open and some rendering option.
|
||||
// render: if we render the .md file with Vivify.
|
||||
@@ -84,6 +84,9 @@ char *getFormatedTime(char *format, int shouldDebug);
|
||||
If need launches in the background rsync to do the backuping.
|
||||
Each pair of source/destination will launch one rsync process.
|
||||
If a pair don't need to be backed up, the destination should be set to NULL.
|
||||
rsyncArgs is the array of arguments to be passed to rsync. Do not inclue destination or source. It will be added in the function.*/
|
||||
void handleBackups(char **sourceDirectoryArray, const int sourceDirectoryNumber, char **destinationDirectoryArray, const char *homeDir, const int interval, const char **rsyncArgs, const int rsyncArgsNumber, const int shouldDebug);
|
||||
rsyncArgs is the array of arguments to be passed to rsync. Do not inclue destination or source. It
|
||||
will be added in the function.*/
|
||||
void handleBackups(char **sourceDirectoryArray, const int sourceDirectoryNumber,
|
||||
char **destinationDirectoryArray, const char *homeDir, const int interval,
|
||||
const char **rsyncArgs, const int rsyncArgsNumber, const int shouldDebug);
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user