format: fix whole project

This commit is contained in:
2026-04-27 19:15:50 +02:00
parent 7bd39a08c6
commit 986618a05b
7 changed files with 1631 additions and 1354 deletions
+566 -428
View File
File diff suppressed because it is too large Load Diff
+301 -224
View File
@@ -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(&regex, 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(&regex, 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(&regex, 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(&regex, 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(&regex, 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(&regex, 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(&regex, 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
View File
@@ -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
+254 -228
View File
@@ -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(&regex, journalRegex, 0);
error(regexReturn, "program", "Regex compilation failed.");
regexReturn = regexec(&regex, 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(&regex, journalRegex, 0);
error(regexReturn, "program", "Regex compilation failed.");
regexReturn = regexec(&regex, 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 “dont start a new string, resume
result = token;
token = strtok(NULL, "/"); // Passing NULL means “dont 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]);
+30 -30
View File
@@ -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
View File
@@ -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
View File
@@ -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