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 "notes.h"
#include "utils.h" #include "utils.h"
char *getDirectoryFromVault(char *targetVault, char **vaultsArray, int vaultTotalNumber, int *vaultNumberPerDirectory, char **directoryArray, int directoryNumber, int shouldDebug) { char *getDirectoryFromVault(char *targetVault, char **vaultsArray, int vaultTotalNumber,
debug("Searching the vault %s inside all the directories...", targetVault); int *vaultNumberPerDirectory, char **directoryArray,
debug("Here are how many vaults there is per directory:"); int directoryNumber, int shouldDebug) {
for (int i = 0; i < directoryNumber; i++) { debug("Searching the vault %s inside all the directories...", targetVault);
altDebug("%d for %s\n", vaultNumberPerDirectory[i], directoryArray[i]); debug("Here are how many vaults there is per directory:");
} for (int i = 0; i < directoryNumber; i++) {
debug("Here are all the vaults in the order they will be searched:"); altDebug("%d for %s\n", vaultNumberPerDirectory[i], directoryArray[i]);
for (int i = 0; i < vaultTotalNumber; i++) { }
altDebug("%s\n", vaultsArray[i]); debug("Here are all the vaults in the order they will be searched:");
} for (int i = 0; i < vaultTotalNumber; i++) {
int index = 0; altDebug("%s\n", vaultsArray[i]);
}
int index = 0;
for (int i = 0; i < directoryNumber; i++) { for (int i = 0; i < directoryNumber; i++) {
for (int j = 0; j < vaultNumberPerDirectory[i]; j++) { for (int j = 0; j < vaultNumberPerDirectory[i]; j++) {
if (strcmp(vaultsArray[index], targetVault) == 0) { if (strcmp(vaultsArray[index], targetVault) == 0) {
debug("%s found in %s. (index %d)", targetVault, directoryArray[i], index); debug("%s found in %s. (index %d)", targetVault, directoryArray[i], index);
return directoryArray[i]; return directoryArray[i];
} }
index++; index++;
} }
} }
error(1, "program", "the vault %s was not found", targetVault); 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."; 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); 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; struct dirent *vaultEntry;
char tempPath[PATH_MAX]; 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); 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 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 // https://stackoverflow.com/a/1085120 for regex code
regex_t regex; regex_t regex;
int regexReturn; int regexReturn;
// compiles the regex // compiles the regex
regexReturn = regcomp(&regex, journalRegex, 0); 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"); debug("Regex compiled succesfully");
// Refer https://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html // Refer https://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
// for readdir() // for readdir()
debug("┌------------------------------------------\nDetected files and dirs from the vault"); 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) while (
altDebug("%s ", vaultEntry->d_name); (vaultEntry = readdir(vaultDirectory)) !=
if (vaultEntry->d_name[0] != '.') { // if the entry don't start with a dot (so hidden dirs and hidden files) NULL) { // we iterate over every entry from the dir. So files and dirs (. and .. included)
regexReturn = regexec(&regex, vaultEntry->d_name, 0, NULL, 0); altDebug("%s ", vaultEntry->d_name);
if (!regexReturn) { // if the regex matches if (vaultEntry->d_name[0] !=
altDebug("matched with the regex. It is a journal.\n"); '.') { // if the entry don't start with a dot (so hidden dirs and hidden files)
journalsArray = realloc(journalsArray, (journalsCount + 1)*sizeof(char*)); // resize notesArray so that regexReturn = regexec(&regex, vaultEntry->d_name, 0, NULL, 0);
journalsArray[journalsCount] = strdup(vaultEntry->d_name); // copy the dir name into notesArray if (!regexReturn) { // if the regex matches
journalsCount++; 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 { } else {
altDebug("did not matched with the regex. It is a note.\n"); altDebug(" was ignored\n");
} }
} else {
altDebug(" was ignored\n");
}
} }
altDebug("└ ------------------------------\n"); altDebug("└ ------------------------------\n");
// free's some used memory // free's some used memory
@@ -70,42 +84,57 @@ char **getJournalsFromVault(char *pathToVault, char *vault, char *journalRegex,
return journalsArray; return journalsArray;
} }
char** getNotesFromVault(char *pathToVault, char *vault, char *journalRegex, int *count, int shouldDebug) { char **getNotesFromVault(char *pathToVault, char *vault, char *journalRegex, int *count,
// this function is inputed a path to a vault (which was selected before) and outpus all the suitable notes (so not the hidden ones) int shouldDebug) {
// originally from https://www.geeksforgeeks.org/c/c-program-list-files-sub-directories-directory/ // 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); debug("Searching %s for notes", vault);
struct dirent *vaultEntry; struct dirent *vaultEntry;
char tempPath[PATH_MAX]; 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); 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 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 // Refer https://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
// for readdir() // for readdir()
debug("┌------------------------------\nDetected Files and dirs from the vault:"); debug("┌------------------------------\nDetected Files and dirs from the vault:");
while ((vaultEntry = readdir(vaultDirectory)) != NULL) { while ((vaultEntry = readdir(vaultDirectory)) != NULL) {
char *entryName = vaultEntry->d_name; char *entryName = vaultEntry->d_name;
int entryLenght = strlen(entryName); int entryLenght = strlen(entryName);
altDebug("%s ", entryName); altDebug("%s ", entryName);
// for the regex code https://stackoverflow.com/a/1085120 // for the regex code https://stackoverflow.com/a/1085120
regex_t regex; regex_t regex;
int regexReturn; int regexReturn;
// compiles the regex // compiles the regex
regexReturn = regcomp(&regex, journalRegex, 0); regexReturn = regcomp(&regex, journalRegex, 0);
if (entryName[0] != '.') { // if the entry don't start with a dot (so hidden dirs and hidden files) if (entryName[0] !=
char fullPathEntry[PATH_MAX]; // creates a string of size of the maximum path lenght '.') { // if the entry don't start with a dot (so hidden dirs and hidden files)
snprintf(fullPathEntry, sizeof(fullPathEntry), "%s/%s/%s", pathToVault, vault, entryName); // sets the full absolute path to fullPathEntry char fullPathEntry[PATH_MAX]; // creates a string of size of the maximum path lenght
// check if it matches the regex. If it does not match regexReturn != 0. snprintf(fullPathEntry, sizeof(fullPathEntry), "%s/%s/%s", pathToVault, vault,
regexReturn = regexec(&regex, entryName, 0, NULL, 0); entryName); // sets the full absolute path to fullPathEntry
struct stat metadataPathEntry; // check if it matches the regex. If it does not match regexReturn != 0.
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 regexReturn = regexec(&regex, entryName, 0, NULL, 0);
notesArray = realloc(notesArray, (notesCount + 1)*sizeof(char*)); // resize notesArray so that struct stat metadataPathEntry;
notesArray[notesCount] = strdup(entryName); // copy the dir name into notesArray if (stat(fullPathEntry, &metadataPathEntry) == 0 && entryName[entryLenght - 3] == '.' &&
notesCount++; entryName[entryLenght - 2] == 'm' && entryName[entryLenght - 1] == 'd' &&
altDebug("did not match with the regex. It is a note.\n"); regexReturn &&
} else if (!regexReturn) {altDebug("matched with the regex. It is a journal.\n");} S_ISREG(metadataPathEntry.st_mode)) { // if this entry is a file ending in .md and
} else {altDebug("was ignored\n");} // 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"); altDebug("└ ------------------------------\n");
@@ -115,166 +144,214 @@ char** getNotesFromVault(char *pathToVault, char *vault, char *journalRegex, int
return notesArray; return notesArray;
} }
char **getVaultsFromDirectories(char **directoryStringArray, int directoryNumber, int *vaultsPerDirectoryNumber, int *count, int shouldDebug) { char **getVaultsFromDirectories(char **directoryStringArray, int directoryNumber,
// to avoid having to work with a tree-dimensional array. We will use a 2d and vaultsPerDirectoryNumber will indicate the width of the directories. int *vaultsPerDirectoryNumber, int *count, int shouldDebug) {
char **vaultsArray = NULL; // to avoid having to work with a tree-dimensional array. We will use a 2d and
int nthVault = 0; // this is only used internally to set the string into the right place in directoryStringArray. // vaultsPerDirectoryNumber will indicate the width of the directories.
int previousStartIndex = 0; char **vaultsArray = NULL;
for (int i = 0; i < directoryNumber; i++) { int nthVault = 0; // this is only used internally to set the string into the right place in
debug("Opening %s", directoryStringArray[i]); // directoryStringArray.
// originally from https://www.geeksforgeeks.org/c/c-program-list-files-sub-directories-directory/ int previousStartIndex = 0;
struct dirent *vaultsDirectoryEntry; for (int i = 0; i < directoryNumber; i++) {
DIR *vaultsDirectory = opendir(directoryStringArray[i]); debug("Opening %s", directoryStringArray[i]);
error(!vaultsDirectory, "program", "Could not open directory %s", directoryStringArray[i]); // originally from
vaultsPerDirectoryNumber[i] = 0; // https://www.geeksforgeeks.org/c/c-program-list-files-sub-directories-directory/
debug("┌------------------------------\n Detected files and dirs %s:", directoryStringArray[i]); struct dirent *vaultsDirectoryEntry;
// Refer https://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html DIR *vaultsDirectory = opendir(directoryStringArray[i]);
// for readdir() error(!vaultsDirectory, "program", "Could not open directory %s", directoryStringArray[i]);
while((vaultsDirectoryEntry = readdir(vaultsDirectory)) != NULL) { vaultsPerDirectoryNumber[i] = 0;
char *entryName = vaultsDirectoryEntry->d_name; // gets the entry as a string value debug("┌------------------------------\n Detected files and dirs %s:",
altDebug("%s", entryName); directoryStringArray[i]);
if (entryName[0] != '.') { // if not hidden file/dir // Refer https://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
char tempFullEntryPath[PATH_MAX]; // we recreate the full path to check it's proprieties // for readdir()
snprintf(tempFullEntryPath, PATH_MAX, "%s%s", directoryStringArray[i], entryName); while ((vaultsDirectoryEntry = readdir(vaultsDirectory)) != NULL) {
altDebug(" (%s)", tempFullEntryPath); char *entryName = vaultsDirectoryEntry->d_name; // gets the entry as a string value
//checking the metadata to see if it is a dir altDebug("%s", entryName);
struct stat metadataEntry; if (entryName[0] != '.') { // if not hidden file/dir
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. char tempFullEntryPath[PATH_MAX]; // we recreate the full path to check it's
altDebug(" is a vault"); // proprieties
vaultsArray = realloc(vaultsArray, sizeof(char *)*(nthVault + 1)); // resize vaultsArray snprintf(tempFullEntryPath, PATH_MAX, "%s%s", directoryStringArray[i], entryName);
error(vaultsArray == NULL, "program", "realloc failed"); altDebug(" (%s)", tempFullEntryPath);
vaultsPerDirectoryNumber[i]++; // it will be used later to know which vaults goes into which directory // checking the metadata to see if it is a dir
vaultsArray[nthVault] = strdup(entryName); // we use strdup and not strcpy, because memory used with opendir and readdir will be closed. struct stat metadataEntry;
error(vaultsArray[nthVault] == NULL, "program", "strdup failed"); if (stat(tempFullEntryPath, &metadataEntry) == 0 &&
nthVault++; // it is used immediatly to set the vault into directoryStringArray S_ISDIR(metadataEntry.st_mode)) { // get's the metadata (stat should return 0 if
} else { // fails) and sees if it is a dir.
altDebug(" is not a vault (not 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 { // we sort entries for the same vault. We can't sort them all. This would break the order.
altDebug(" is not a vault (hidden file/dir)"); 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. // we calculate once the total number of vaults to avoid recalculation every time we use it
qsort(vaultsArray + previousStartIndex, nthVault - previousStartIndex, sizeof(const char *), compareString); // sorts the vaults alphabetically *count = 0;
closedir(vaultsDirectory); for (int i = 0; i < directoryNumber; i++) {
previousStartIndex = nthVault; *count += vaultsPerDirectoryNumber[i];
} }
// we calculate once the total number of vaults to avoid recalculation every time we use it return vaultsArray;
*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) { char *updateJournal(char *path, char *journal, char *timeFormat, int *journalWasUpdated,
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. int shouldDebug) {
debug("Handling the journal %s", path); 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); char *date = getFormatedTime(timeFormat, shouldDebug);
debug("Formated time for the journal's entry is %s", date); debug("Formated time for the journal's entry is %s", date);
char dateWithExtension[PATH_MAX]; char dateWithExtension[PATH_MAX];
snprintf(dateWithExtension, PATH_MAX, "%s.md", date); snprintf(dateWithExtension, PATH_MAX, "%s.md", date);
sanitize(dateWithExtension); sanitize(dateWithExtension);
debug("Sanitized date: %s\n(it might be used later for a file name if the journal is divided)", dateWithExtension); debug("Sanitized date: %s\n(it might be used later for a file name if the journal is divided)",
struct stat metadata; dateWithExtension);
stat(path, &metadata); struct stat metadata;
if (S_ISREG(metadata.st_mode)) { stat(path, &metadata);
debug("%s is a unified journal.", path); if (S_ISREG(metadata.st_mode)) {
if (!isStringInFile(path, date, shouldDebug)) { // if there is no entry for current date debug("%s is a unified journal.", path);
appendToFile(path, date, shouldDebug); if (!isStringInFile(path, date, shouldDebug)) { // if there is no entry for current date
*journalWasUpdated = 1; appendToFile(path, date, shouldDebug);
} // if there is an entry do nothing *journalWasUpdated = 1;
} else if (S_ISDIR(metadata.st_mode)) { } // if there is an entry do nothing
debug("%s is a divided journal.", path); } else if (S_ISDIR(metadata.st_mode)) {
struct dirent *dividedJournalEntry; debug("%s is a divided journal.", path);
// snprintf does not like to have the same variable as input and output so we use a buffer struct dirent *dividedJournalEntry;
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
// snprintf does not like to have the same variable as input and output so we use a buffer // snprintf does not like to have the same variable as input and output so we use a buffer
char temp[PATH_MAX]; 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); strncpy(path, temp, PATH_MAX);
debug("Returning path to the selected entry: %s", path); DIR *dividedJournalDirectory = opendir(path);
} error(dividedJournalDirectory == NULL, "program", "Could not open directory %s", path);
} else { /*char **entryArray = NULL; // will contain all the entries of the divided journal
error(1, "program", "%s is not a file and not a directory.", path); // 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*));*/
return path; // 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 #ifndef NOTES_H
#define NOTES_H #define NOTES_H
#include "utils.h"
#include "ui.h" #include "ui.h"
#include "utils.h"
// find which directory contains targetVault // 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. // gets the journals from the vault.
//to distinguish journals from note we use regex. // 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. // Note: we must handle them separatly as journals can either be a single file (which will treat
char **getJournalsFromVault(char *pathToVault, char *vault, char *journalRegex, int *count, int shouldDebug); // specially) or a directory.
// this function is inputed a path to a vault (which was selected before) and outpus all the suitable notes (so not the hidden ones). char **getJournalsFromVault(char *pathToVault, char *vault, char *journalRegex, int *count,
// 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. int shouldDebug);
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
// function gets as input an array of directoryNumber strings. Which are the directories the function will search for vaults. // suitable notes (so not the hidden ones). journalRegex is the regex code for the journals. If a
// it returns an array of vaults. // note matches this code, it is treated as a journal and it is not outputed from this function.
// the vaults are organized in order per directory. char **getNotesFromVault(char *pathToVault, char *vault, char *journalRegex, int *count,
// vaultsPerDirectoryNumber and count should be initiallized before calling the function and the address be inputed. int shouldDebug);
// count is the total number of vaults. // 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. // 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. // path is the path to the file.
// journal is the name of the file. // journal is the name of the file.
// journalWasUpdated will be set to 1 if a new entry was created // 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. // creates new entry with date.
// for divided select if we want to acces to a new entry or a old one. // 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. // 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 #endif
+254 -228
View File
@@ -1,163 +1,187 @@
#include "ui.h" #include "ui.h"
#include "utils.h" #include "utils.h"
void createNewVault(char **directoriesArray, int directoryCount, char **vaultsArray, int vaultCount, int bypass, char *bypassvalue, int shouldDebug) { void createNewVault(char **directoriesArray, int directoryCount, char **vaultsArray, int vaultCount,
int duplicateWarning = 0; // set to 1 later if the vault you tried to create already existed int bypass, char *bypassvalue, int shouldDebug) {
int emptyWarning = 0; // set to 1 later if inpted empty name 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: input_screen:
// choose in which directory the vault will be created // 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 *dirToVault = ncursesSelect(
char *vaultName = malloc(PATH_MAX); directoriesArray,
if (!bypass) { // if won't bypass (if -v or --vault weren't set) "Select a directory, in which the vault will remain (Use arrows or WASD, Enter to select)",
echo(); directoryCount, 0, "", "", " ", shouldDebug);
keypad(stdscr, FALSE); char *vaultName = malloc(PATH_MAX);
// color code from https://stackoverflow.com/a/73396575 if (!bypass) { // if won't bypass (if -v or --vault weren't set)
start_color(); echo();
clear(); keypad(stdscr, FALSE);
use_default_colors(); // color code from https://stackoverflow.com/a/73396575
init_pair(1, COLOR_WHITE, -1); // -1 is blank start_color();
init_pair(2, COLOR_RED, -1); clear();
attron(COLOR_PAIR(1)); use_default_colors();
printw("Enter the name of the new vault: "); init_pair(1, COLOR_WHITE, -1); // -1 is blank
mvprintw(3, 0, "Unsafe characters such as \\ and / will be replaced by _"); init_pair(2, COLOR_RED, -1);
attroff(COLOR_PAIR(1)); attron(COLOR_PAIR(1));
if (duplicateWarning) { printw("Enter the name of the new vault: ");
attron(COLOR_PAIR(2)); mvprintw(3, 0, "Unsafe characters such as \\ and / will be replaced by _");
mvprintw(4, 0, "A vault with this name already exists. Please input another name"); attroff(COLOR_PAIR(1));
attroff(COLOR_PAIR(2)); 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) { // check if no empty string
attron(COLOR_PAIR(2)); if (strcmp(vaultName, "") == 0) {
mvprintw(5, 0, "The vault's name must not be empty. Please input another name"); emptyWarning = 1;
attroff(COLOR_PAIR(2)); goto input_screen;
} }
move(1, 1); // replace cursor debug("Inputed vaultName=%s", vaultName);
wgetnstr(stdscr, vaultName, PATH_MAX-1); sanitize(vaultName);
refresh(); debug("Sanitized vaultName=%s", vaultName);
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);
if (!isStringInArray(vaultName, (const char **)vaultsArray,
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. vaultCount)) { // if vault doesnt already exists. We avoid vaults with the
char vaultFullPath[PATH_MAX]; // recreating the full path // same name even if they are from different directories.
sprintf(vaultFullPath, "%s/%s/", dirToVault, vaultName); char vaultFullPath[PATH_MAX]; // recreating the full path
sprintf(vaultFullPath, "%s/%s/", dirToVault, vaultName);
mkdir(vaultFullPath, 0744); mkdir(vaultFullPath, 0744);
} else { } else {
duplicateWarning = 1; duplicateWarning = 1;
goto input_screen; goto input_screen;
} }
free(vaultName); free(vaultName);
} }
char *createNewNote(char dirToVault[PATH_MAX], char *vaultFromDir, int bypass, char *bypassvalue, char *journalRegex, int shouldDebug) { char *createNewNote(char dirToVault[PATH_MAX], char *vaultFromDir, int bypass, char *bypassvalue,
// input from user for the name char *journalRegex, int shouldDebug) {
char *fileName = malloc(BUFFER_SIZE); // input from user for the name
if (!bypass) { // if we don't bypass. (if -n or --note weren't set.) char *fileName = malloc(BUFFER_SIZE);
echo(); if (!bypass) { // if we don't bypass. (if -n or --note weren't set.)
keypad(stdscr, FALSE); echo();
clear(); keypad(stdscr, FALSE);
printw("Enter the name of the new note: "); clear();
mvprintw(3, 0, "Unsafe characters such as \\, and / will be replaced by _"); printw("Enter the name of the new note: ");
mvprintw(4, 0, "If the name matches with the regex for a journal (%s), it will create a journal instead of a note.", journalRegex); mvprintw(3, 0, "Unsafe characters such as \\, and / will be replaced by _");
move(1,1); mvprintw(4, 0,
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) "If the name matches with the regex for a journal (%s), it will create a journal "
refresh(); "instead of a note.",
endwin(); journalRegex);
reset_shell_mode(); move(1, 1);
fflush(stdout); wgetnstr(stdscr, fileName,
fflush(stderr); BUFFER_SIZE - 4); // limits the buffer to prevent overflow (-4 to account indexing
} else { // bypasses user input if we bypass is set to 1 // and from ".md" in case we need to add it later)
strncpy(fileName, bypassvalue, BUFFER_SIZE); refresh();
fileName[BUFFER_SIZE-1] = '\0'; endwin();
} reset_shell_mode();
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) fflush(stdout);
// check/sanitize the input fflush(stderr);
debug("Inputed fileName=%s", fileName); } else { // bypasses user input if we bypass is set to 1
sanitize(fileName); strncpy(fileName, bypassvalue, BUFFER_SIZE);
debug("Sanitized fileName=%s (We might append .md later", fileName); fileName[BUFFER_SIZE - 1] = '\0';
// 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 error(
} else { // normal process for a note strcmp(fileName, "") == 0, "user",
debug("%s does not match with %s treating it as a note", fileName, journalRegex); "fileName is empty"); // replace this with a warning and add a warning if duplicate file and
// if there is no .md add an .md // handle case where multiple warnings (if such case is possible)
int len = strlen(fileName); // check/sanitize the input
if (fileName[len-3] != '.' || fileName[len-2] != 'm' || fileName[len-1] != 'd') { // there might be a cleaner way to do this debug("Inputed fileName=%s", fileName);
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 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 return fileName;
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;
} }
char* fzfSelect(char *pathToFiles, char *selectText, int shouldDebug) { 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) // we are gonna write each line of each files (with the filename and the line number to an
// index)
char command[CMD_BUFFER]; char command[CMD_BUFFER];
char indexFile[] = "/tmp/notewrapper_index_XXXXXX"; char indexFile[] = "/tmp/notewrapper_index_XXXXXX";
@@ -170,12 +194,9 @@ char* fzfSelect(char *pathToFiles, char *selectText, int shouldDebug) {
* Build index once into temp file * Build index once into temp file
* format: file:line:content * format: file:line:content
*/ */
snprintf(command, sizeof(command), snprintf(command, sizeof(command), "rg --line-number --no-heading --color=never . \"%s\" > %s",
"rg --line-number --no-heading --color=never . \"%s\" > %s", pathToFiles, indexFile);
pathToFiles,
indexFile
);
debug("INDEX CMD: %s", command); debug("INDEX CMD: %s", command);
if (system(command) != 0) { if (system(command) != 0) {
@@ -189,47 +210,47 @@ char* fzfSelect(char *pathToFiles, char *selectText, int shouldDebug) {
*/ */
/* /*
EXPLANATION: EXPLANATION:
cat %s cat %s
→ feeds prebuilt index file (format: file:line:content) into fzf → feeds prebuilt index file (format: file:line:content) into fzf
fzf --delimiter ':' fzf --delimiter ':'
→ splits each line into fields: → splits each line into fields:
{1} = file path {1} = file path
{2} = line number {2} = line number
{3} = content {3} = content
--prompt --prompt
→ UI prompt text shown in fzf → UI prompt text shown in fzf
--preview --preview
→ runs a shell command for selected item: → runs a shell command for selected item:
file={1} → current file file={1} → current file
line={2} → line number of match line={2} → line number of match
nl -ba "$file" nl -ba "$file"
→ prints file with line numbers → prints file with line numbers
sed -n "$((line-5)),$((line+5))p" sed -n "$((line-5)),$((line+5))p"
→ extracts a window of 5 lines above and below match → extracts a window of 5 lines above and below match
sed "... -> ..." sed "... -> ..."
→ highlights the matched line (middle of window) with red arrow → highlights the matched line (middle of window) with red arrow
--preview-window=right:60%:hidden --preview-window=right:60%:hidden
→ preview appears on right, 60% width, initially hidden → preview appears on right, 60% width, initially hidden
--bind 'change:show-preview' --bind 'change:show-preview'
→ preview only appears after first selection change (not at startup) → preview only appears after first selection change (not at startup)
*/ */
snprintf(command, sizeof(command), snprintf(
"cat %s | fzf --delimiter ':' --prompt='%s' " command, sizeof(command),
"--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/\"' " "cat %s | fzf --delimiter ':' --prompt='%s' "
"--preview-window=right:60%%:hidden " "--preview 'file={1}; line={2}; nl -ba \"$file\" | sed -n \"$((line-5)),$((line+5))p\" | "
"--bind 'change:show-preview'", "sed \"$((line-$(($((line-5))))+1))s/^/\\x1b[31m-> \\x1b[0m/\"' "
indexFile, "--preview-window=right:60%%:hidden "
selectText ? selectText : "> " "--bind 'change:show-preview'",
); indexFile, selectText ? selectText : "> ");
debug("FZF CMD: %s", command); debug("FZF CMD: %s", command);
FILE *fzfPipe = popen(command, "r"); FILE *fzfPipe = popen(command, "r");
@@ -252,8 +273,8 @@ char* fzfSelect(char *pathToFiles, char *selectText, int shouldDebug) {
char *token = strtok(result, "/"); char *token = strtok(result, "/");
result = NULL; result = NULL;
while (token != NULL) { while (token != NULL) {
result = token; result = token;
token = strtok(NULL, "/"); //Passing NULL means “dont start a new string, resume token = strtok(NULL, "/"); // Passing NULL means “dont start a new string, resume
} }
// clean up // clean up
@@ -263,81 +284,86 @@ char* fzfSelect(char *pathToFiles, char *selectText, int shouldDebug) {
return result; 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) char *ncursesSelect(
int highlight = 0; //curently highlighted option 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; int key;
cbreak(); // disable line buffering cbreak(); // disable line buffering
noecho(); // don't echo key presses noecho(); // don't echo key presses
keypad(stdscr, TRUE); // enable arrow keys keypad(stdscr, TRUE); // enable arrow keys
start_color(); 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(); use_default_colors();
init_pair(1, COLOR_WHITE, -1); // color for optionsNumber (so notes, vaults, etc.) 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) { while (1) {
clear(); clear();
attron(COLOR_PAIR(1)); attron(COLOR_PAIR(1));
mvprintw(0,0, "%s", optionsText); mvprintw(0, 0, "%s", optionsText);
int offset = 1; int offset = 1;
if (strcmp(bottomText, "") != 0) { if (strcmp(bottomText, "") != 0) {
mvprintw(offset, 1, "%s", bottomText); mvprintw(offset, 1, "%s", bottomText);
offset++; 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);
} }
} // Print options with highlighting
attroff(COLOR_PAIR(1)); for (int i = 0; i < optionsNumber; i++) {
if (strcmp(middleText, "") != 0) { if (i == highlight) {
mvprintw(offset+optionsNumber, 1, "%s", middleText); attron(A_REVERSE);
offset++; } // highlight selected
} mvprintw(i + offset, 2, "%s", options[i]);
// Printf extraOptions with highlighting if (i == highlight) {
attron(COLOR_PAIR(2)); attroff(A_REVERSE);
for (int k = optionsNumber; k < optionsNumber + extraOptionsNumber; k++) { }
if (k == highlight) {
attron(A_REVERSE);
} }
mvprintw(k+offset, 2, "%s", options[k]); attroff(COLOR_PAIR(1));
if (k == highlight) { if (strcmp(middleText, "") != 0) {
attroff(A_REVERSE); mvprintw(offset + optionsNumber, 1, "%s", middleText);
offset++;
} }
} // Printf extraOptions with highlighting
attroff(COLOR_PAIR(2)); attron(COLOR_PAIR(2));
attron(COLOR_PAIR(1)); for (int k = optionsNumber; k < optionsNumber + extraOptionsNumber; k++) {
mvprintw(offset+optionsNumber+extraOptionsNumber, 1, "%s", topText); if (k == highlight) {
attroff(COLOR_PAIR(1)); attron(A_REVERSE);
key = getch(); //get some int which value correspond to some key being pressed }
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 KEY_UP:
case 'w': case 'w':
case 'W': case 'W':
highlight--; highlight--;
if (highlight < 0) { if (highlight < 0) {
highlight = optionsNumber + extraOptionsNumber - 1;// can't select the -1nth option highlight = optionsNumber + extraOptionsNumber - 1; // can't select the -1nth option
} }
break; break;
case 's': case 's':
case 'S': case 'S':
case KEY_DOWN: case KEY_DOWN:
highlight++; highlight++;
if (highlight >= optionsNumber + extraOptionsNumber) { if (highlight >= optionsNumber + extraOptionsNumber) {
highlight = 0; highlight = 0;
} }
break; break;
case 10: //\n case 10: //\n
goto end_loop; goto end_loop;
} }
} }
end_loop: end_loop:
endwin(); // end ncurses mode endwin(); // end ncurses mode
fflush(stderr); fflush(stderr);
debug("Selected option: %s", options[highlight]); debug("Selected option: %s", options[highlight]);
+30 -30
View File
@@ -1,47 +1,47 @@
#ifndef UI_H #ifndef UI_H
#define 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 <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <ncurses.h>
#include <unistd.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <pwd.h> #include <unistd.h>
#include <ctype.h> #define CMD_BUFFER 4096 // for fzfSelect
#include <limits.h>
#include <regex.h>
#include "utils.h"
#define CMD_BUFFER 4096 // for fzfSelect
#define RESULT_BUFFER 1024 // for fzfSelect #define RESULT_BUFFER 1024 // for fzfSelect
/*Uses ncurses to get an input from the user. /*Uses ncurses to get an input from the user.
Creates a new note with this input. Creates a new note with this input.
also can create a journal if the name matches with journalRegex. also can create a journal if the name matches with journalRegex.
returns the path to the note. returns the path to the note.
If you want to bypass the input TUI set bypass to 1 and bypassvalue to the note name 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. Chooses in which directory it want to create the vault.
Uses ncurses to get an input from the user. Uses ncurses to get an input from the user.
Creates a new vault with this input. Creates a new vault with this input.
If vault already exists, prints a warning. If vault already exists, prints a warning.
returns nothing. */ 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. // uses fzf and ripgrep to search inside the files to choose between all the options.
char* fzfSelect(char *pathToFiles, char *selectText, int shouldDebug); 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. /*this function is used multiple times let the user select one options from many with ncurses in a
options is the array of strings with all the options. TUI. options is the array of strings with all the options. optionsText is the text that will be
optionsText is the text that will be printed at the top (For example: "Please select ..."). printed at the top (For example: "Please select ..."). we distinguish options from extraOptions.
we distinguish options from extraOptions.
options could be the list of all notes or all vaults. 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.). extraOptions are options that are special and have a special color (for ex: "Delete vault",
note: extraOptions should be at the end of options. "Settings", etc.). note: extraOptions should be at the end of options. topText is printed between
topText is printed between options text and the start of options. options text and the start of options. middleText (usally \n) is printed between options and
middleText (usally \n) is printed between options and extraOptions. extraOptions. bottomText is printed bellow. topText and middle must be exactly one line. If you want
bottomText is printed bellow. empty lines use " " and not "". returns the selected option. */
topText and middle must be exactly one line. If you want empty lines use " " and not "". char *ncursesSelect(char **options, char *optionsText, int optionsNumber, int extraOptionsNumber,
returns the selected option. */ char *bottomText, char *middleText, char *topText, int debug);
char* ncursesSelect(char **options, char *optionsText, int optionsNumber, int extraOptionsNumber, char *bottomText, char *middleText, char *topText, int debug);
#endif #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) { int reverseCompareString(const void *a, const void *b) {
const char *str1 = *(const char **)a; const char *str1 = *(const char **)a;
const char *str2 = *(const char **)b; const char *str2 = *(const char **)b;
return -1*strcmp(str1, str2); return -1 * strcmp(str1, str2);
} }
void getCurrentTime(int *hour, int *minute, int *second) { void getCurrentTime(int *hour, int *minute, int *second) {
time_t now = time(NULL); // Get current time in seconds since epoch time_t now = time(NULL); // Get current time in seconds since epoch
struct tm *local = localtime(&now); // Convert to local time structure struct tm *local = localtime(&now); // Convert to local time structure
*hour = local->tm_hour; // Extract hour *hour = local->tm_hour; // Extract hour
*minute = local->tm_min; // Extract minutes *minute = local->tm_min; // Extract minutes
*second = local->tm_sec; // Extract seconds *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 void _debug(const int d, const char *file, const int line, const char *function,
if (d) { const char *message, ...) { // use for formatted debug
fflush(stdout); if (d) {
fflush(stderr); fflush(stdout);
va_list args; //variadic function stuff fflush(stderr);
va_start(args, message); va_list args; // variadic function stuff
int h, m, s; va_start(args, message);
getCurrentTime(&h, &m, &s); int 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); getCurrentTime(&h, &m, &s);
vfprintf(stderr, message, args); fprintf(stderr, "\e[0;32m[DEBUG -- %d:%d:%d] From file %s line %d function %s:\e[0m\n", h,
fprintf(stderr, "\e[0m\n"); m, s, file, line, function);
va_end(args); 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");
} }
va_list args; }
va_start(args, message); void _altDebug(const int d, const char *message,
vfprintf(stderr, message, args); ...) { // use for less formal debuggin. (usefull if enumerating or making a list
va_end(args); if (d) {
if (!shouldDebug) { fflush(stdout);
fprintf(stderr, "\nRunning notewrapper -V might give you more information."); 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("Backuping... source: %s and destination: %s", source, destination);
debug("Rsync has %d extra arguments", rsyncArgsNumber); debug("Rsync has %d extra arguments", rsyncArgsNumber);
pid_t pid = fork(); pid_t pid = fork();
error(pid < 0, "program", "fork() faild. pid = %d", pid); error(pid < 0, "program", "fork() faild. pid = %d", pid);
if (pid == 0) { if (pid == 0) {
// Child process: execute rsync // Child process: execute rsync
char **args = malloc((4 + rsyncArgsNumber)*sizeof(char*)); char **args = malloc((4 + rsyncArgsNumber) * sizeof(char *));
args[0] = "rsync"; args[0] = "rsync";
for (int i = 0; i < rsyncArgsNumber; i++) { for (int i = 0; i < rsyncArgsNumber; i++) {
args[i+1] = (char *)rsyncArgs[i]; args[i + 1] = (char *)rsyncArgs[i];
if (i == rsyncArgsNumber-1) { // last loop if (i == rsyncArgsNumber - 1) { // last loop
args[i+2] = (char*)source; args[i + 2] = (char *)source;
args[i+3] = (char*)destination; args[i + 3] = (char *)destination;
args[i+4] = NULL; // execvp expect last arg to be NULL args[i + 4] = NULL; // execvp expect last arg to be NULL
debug("rsync command:"); debug("rsync command:");
for (int k = 0; k <= i+4; k++) { for (int k = 0; k <= i + 4; k++) {
altDebug("%s ", args[k]); altDebug("%s ", args[k]);
}
altDebug("\n");
} }
altDebug("\n");
}
} }
execvp("rsync", args); execvp("rsync", args);
// If execvp returns, there was an error // 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 // sometimes .cache and .config doesn't exist. Such as with github actions machines
char config_dir[PATH_MAX]; char config_dir[PATH_MAX];
char cache_dir[PATH_MAX]; char cache_dir[PATH_MAX];
snprintf(config_dir, sizeof(config_dir),
"%s/.config/", home);
snprintf(cache_dir, sizeof(cache_dir), snprintf(config_dir, sizeof(config_dir), "%s/.config/", home);
"%s/.cache/", home);
snprintf(cache_dir, sizeof(cache_dir), "%s/.cache/", home);
ensureDir(config_dir, shouldDebug); ensureDir(config_dir, shouldDebug);
ensureDir(cache_dir, shouldDebug); ensureDir(cache_dir, shouldDebug);
strncat(config_dir, "notewrapper/", PATH_MAX); strncat(config_dir, "notewrapper/", PATH_MAX);
strncat(cache_dir, "notewrapper/", PATH_MAX); strncat(cache_dir, "notewrapper/", PATH_MAX);
ensureDir(config_dir, shouldDebug); ensureDir(config_dir, shouldDebug);
ensureDir(cache_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); snprintf(config_file, sizeof(config_file), "%s/config.json", config_dir);
FILE *f = fopen(config_file, "r"); FILE *f = fopen(config_file, "r");
@@ -143,29 +147,34 @@ void initAppFilesAndDirs(const char *home, const int shouldDebug) {
debug("Creating default config file."); debug("Creating default config file.");
FILE *w = fopen(config_file, "w"); FILE *w = fopen(config_file, "w");
error(!w, "program", "fopen failed opening %s"); error(!w, "program", "fopen failed opening %s");
fprintf(w, //TODO CHANGE default json fprintf(w, // TODO CHANGE default json
"{\n" "{\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 " \"directory\": [\"~/Documents/\"],\n" // I personally use ~/Documents/Notes. But the
" \"render\": true,\n" // dir doesn't exist most times. So for the
" \"jumpToEndOfFileOnLaunch\": true,\n" // default user it is better to put ~/Documents
" \"editor\": \"neovim\",\n" " \"render\": true,\n"
" \"journalRegex\": \".*journal.*\",\n" " \"jumpToEndOfFileOnLaunch\": true,\n"
" \"dateEntry\": \"# %%Y %%m %%d %%a\",\n" " \"editor\": \"neovim\",\n"
" \"newLineOnOpening\": true,\n" " \"journalRegex\": \".*journal.*\",\n"
" \"backup\": {\n" " \"dateEntry\": \"# %%Y %%m %%d %%a\",\n"
" \"enable\": false,\n" " \"newLineOnOpening\": true,\n"
" \"directory\": {\n" " \"backup\": {\n"
" \"~/Documents/\": \"path/to/backup/\"\n" " \"enable\": false,\n"
" },\n" " \"directory\": {\n"
" \"interval\": \"weekly\",\n" " \"~/Documents/\": \"path/to/backup/\"\n"
" \"rsyncArgs\": [\"-Lqah\", \"--update\"]\n" " },\n"
" }\n" " \"interval\": \"weekly\",\n"
"}\n"); " \"rsyncArgs\": [\"-Lqah\", \"--update\"]\n"
" }\n"
"}\n");
fclose(w); 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; int shouldBackup = 0;
time_t now = time(NULL); time_t now = time(NULL);
debug("Time since epoch is %ld", (long)now); 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) { 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; shouldBackup = 1;
cacheFile = fopen(cacheFilePATH, "w"); cacheFile = fopen(cacheFilePATH, "w");
if (!cacheFile) { if (!cacheFile) {
@@ -211,41 +221,57 @@ void handleBackups(char **sourceDirectoryArray, const int sourceNumber, char **d
fprintf(cacheFile, "%ld\n", (long)now); fprintf(cacheFile, "%ld\n", (long)now);
fclose(cacheFile); fclose(cacheFile);
} else { } 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) { if (shouldBackup) {
debug("A backup is needed"); debug("A backup is needed");
for (int i = 0; i < sourceNumber; i++) { for (int i = 0; i < sourceNumber; i++) {
debug("%s", destinationDirectoryArray[i]); debug("%s", destinationDirectoryArray[i]);
if (destinationDirectoryArray[i]) { // if we don't want to backup it, it was set to NULL if (destinationDirectoryArray[i]) { // if we don't want to backup it, it was set to NULL
copyDir(sourceDirectoryArray[i], destinationDirectoryArray[i], rsyncArguments, rsyncArgumentsNumber, shouldDebug); 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 int isEditorValid(char *editorToCheck, int useDefaultEditor,
// check if supported 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 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 { } 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 // check if installed
char *editor; char *editor;
if (strcmp(editorToCheck, "neovim") == 0) { // some executables are not name the same as the project if (strcmp(editorToCheck, "neovim") ==
editor = strdup("nvim"); // we must use strdup and not just copy as we would have modified editorToOpen in main 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) { } else if (strcmp(editorToCheck, "helix") == 0) {
editor = strdup("hx"); editor = strdup("hx");
} else if (strcmp(editorToCheck, "kakoune") == 0) { } else if (strcmp(editorToCheck, "kakoune") == 0) {
editor = strdup("kak"); editor = strdup("kak");
} else { } else {
editor = strdup(editorToCheck); editor = strdup(editorToCheck);
} }
char *path_env = getenv("PATH"); 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); debug("Your PATH is %s", path_env);
char *paths = strdup(path_env); // duplicate because strtok modifies the string char *paths = strdup(path_env); // duplicate because strtok modifies the string
char *dir = strtok(paths, ":"); char *dir = strtok(paths, ":");
@@ -266,43 +292,47 @@ int isEditorValid (char *editorToCheck, int useDefaultEditor, int shouldDebug) {
} }
char *getFormatedTime(char *format, int shouldDebug) { char *getFormatedTime(char *format, int shouldDebug) {
debug("Inputed time format is %s", format); debug("Inputed time format is %s", format);
time_t t = time(NULL); time_t t = time(NULL);
struct tm *tm = localtime(&t); struct tm *tm = localtime(&t);
char *buf = malloc(BUFFER_SIZE); char *buf = malloc(BUFFER_SIZE);
strftime(buf, BUFFER_SIZE, format, tm); strftime(buf, BUFFER_SIZE, format, tm);
debug("Formated time is %s", buf); debug("Formated time is %s", buf);
return buf; return buf;
} }
int isStringInArray(const char *string, const char **array, const int len) { int isStringInArray(const char *string, const char **array, const int len) {
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
if (strcmp(string, array[i]) == 0) { if (strcmp(string, array[i]) == 0) {
return 1; return 1;
}
} }
} return 0;
return 0;
} }
int isStringInFile(const char *path, const char *string, const int shouldDebug) { int isStringInFile(const char *path, const char *string, const int shouldDebug) {
FILE *file = fopen(path, "r"); FILE *file = fopen(path, "r");
error(file == NULL, "program", "While searching for \"%s\", we could not open file %s", string, path); error(file == NULL, "program", "While searching for \"%s\", we could not open file %s", string,
char buf[BUFFER_SIZE]; path);
char buf[BUFFER_SIZE];
while (fgets(buf, BUFFER_SIZE, file) != NULL) {
if (strstr(buf, string) != NULL) { while (fgets(buf, BUFFER_SIZE, file) != NULL) {
debug("%s contains the substring %s", buf, string); if (strstr(buf, string) != NULL) {
return 1; debug("%s contains the substring %s", buf, string);
return 1;
}
} }
} debug("The string %s is not inside %s", string, path);
debug("The string %s is not inside %s", string, path); return 0;
return 0;
} }
void appendToFile(const char *path, const char *string, const int shouldDebug) { void appendToFile(const char *path, const char *string, const int shouldDebug) {
FILE *file = fopen(path, "r"); FILE *file = fopen(path, "r");
char lastLine[1024] = {0}; char lastLine[1024] = {0};
error(file == NULL, "program", "could not open %s", path); 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 // Read file line by line to get the last one
while (fgets(buffer, sizeof(buffer), file) != NULL) { 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) { void sanitize(char *string) {
size_t stringLenght = strlen(string); size_t stringLenght = strlen(string);
for (size_t i = 0; i < stringLenght; i++) { for (size_t i = 0; i < stringLenght; i++) {
if ((!isalnum((unsigned char)string[i]) && strchr("~/\\:*?\"\'|!$[]{}<>\n\r\t", string[i]))) { // replace unwanted chars by '_' if ((!isalnum((unsigned char)string[i]) &&
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 // both functions are from https://stackoverflow.com/a/5467788
@@ -347,301 +378,296 @@ void sanitize(char *string) {
// remove() can't delete directories with files // remove() can't delete directories with files
// so it walks the file tree and deletes it's content before removing the directory // 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) { int unlink_cb(const char *filePath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
(void)sb; (void)sb;
(void)typeflag; (void)typeflag;
(void)ftwbuf; (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 // 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(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); error(filePath[0] == '.', "program",
"%s starts with \".\". For security reasons, use absolute path and not relative path.",
filePath);
int rv = remove(filePath); int rv = remove(filePath);
error(rv, "program", "remove() failed to delete %s", filePath); error(rv, "program", "remove() failed to delete %s", filePath);
return rv; return rv;
} }
int rmrf(char *path, int shouldDebug) { int rmrf(char *path, int shouldDebug) {
error(strcmp(path, "/") == 0, "program", "Refusing to delete root directory"); error(strcmp(path, "/") == 0, "program", "Refusing to delete root directory");
return nftw(path, unlink_cb, 64, FTW_DEPTH | FTW_PHYS); return nftw(path, unlink_cb, 64, FTW_DEPTH | FTW_PHYS);
} }
int openEditor(char *path, char *editor, int render, int shouldJumpToEndOfFile, int shouldDebug) { int openEditor(char *path, char *editor, int render, int shouldJumpToEndOfFile, int shouldDebug) {
// this ensures that ncurses won't affect the editor behaviour // this ensures that ncurses won't affect the editor behaviour
pid_t editor_pid = fork(); pid_t editor_pid = fork();
error(editor_pid < 0, "program", "fork() failed."); 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) { if (editor_pid == 0) {
// ========================= // =========================
// CHILD: launch editor // CHILD: launch editor
// ========================= // =========================
// ---- NEOVIM / VIM ---- // ---- NEOVIM / VIM ----
if (strcmp(editor, "neovim") == 0 || strcmp(editor, "vim") == 0) { if (strcmp(editor, "neovim") == 0 || strcmp(editor, "vim") == 0) {
const char *bin = (strcmp(editor, "neovim") == 0) ? "nvim" : "vim"; const char *bin = (strcmp(editor, "neovim") == 0) ? "nvim" : "vim";
if (render) { if (render) {
if (shouldJumpToEndOfFile) { if (shouldJumpToEndOfFile) {
debug("Running %s +:$ +:Vivify %s", bin, path); debug("Running %s +:$ +:Vivify %s", bin, path);
execlp(bin, bin, "+:$", "+:Vivify", path, NULL); execlp(bin, bin, "+:$", "+:Vivify", path, NULL);
} else { } else {
debug("Running %s +:Vivify %s", bin, path); debug("Running %s +:Vivify %s", bin, path);
execlp(bin, bin, "+:Vivify", path, NULL); execlp(bin, bin, "+:Vivify", path, NULL);
} }
} else { } else {
if (shouldJumpToEndOfFile) { if (shouldJumpToEndOfFile) {
debug("Running %s +:$ %s", bin, path); debug("Running %s +:$ %s", bin, path);
execlp(bin, bin, "+:$", path, NULL); execlp(bin, bin, "+:$", path, NULL);
} else { } else {
debug("Running %s %s", bin, path); debug("Running %s %s", bin, path);
execlp(bin, bin, path, NULL); execlp(bin, bin, path, NULL);
} }
} }
error(1, "program", "execlp() failed."); error(1, "program", "execlp() failed.");
// ---- MICRO ---- // ---- MICRO ----
} else if (strcmp(editor, "micro") == 0) { } else if (strcmp(editor, "micro") == 0) {
if (render) { if (render) {
if (shouldJumpToEndOfFile) { if (shouldJumpToEndOfFile) {
debug("Running MICRO_VIVIFY=1 micro %s +99999", path); debug("Running MICRO_VIVIFY=1 micro %s +99999", path);
setenv("MICRO_VIVIFY", "1", 1); setenv("MICRO_VIVIFY", "1", 1);
execlp("micro", "micro", path, "+999999", NULL); execlp("micro", "micro", path, "+999999", NULL);
} else { } else {
debug("Running MICRO_VIVIFY=1 micro %s", path); debug("Running MICRO_VIVIFY=1 micro %s", path);
setenv("MICRO_VIVIFY", "1", 1); setenv("MICRO_VIVIFY", "1", 1);
execlp("micro", "micro", path, NULL); execlp("micro", "micro", path, NULL);
} }
} else { } else {
if (shouldJumpToEndOfFile) { if (shouldJumpToEndOfFile) {
debug("Running micro %s +999999", path); debug("Running micro %s +999999", path);
execlp("micro", "micro", path, "+999999", NULL); execlp("micro", "micro", path, "+999999", NULL);
} else { } else {
debug("Running micro %s", path); debug("Running micro %s", path);
execlp("micro", "micro", path, NULL); execlp("micro", "micro", path, NULL);
} }
} }
error(1, "program", "execlp() failed."); 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);
} }
debug("Running viv %s", viv_path); // ---- NANO ----
execlp("viv", "viv", viv_path, NULL); else if (strcmp(editor, "nano") == 0) {
error(1, "program", "execlp() failed.");
}
// IMPORTANT: do NOT wait for viv
}
// Now run nano (this replaces the child process) // If render enabled → spawn viv in parallel
if (shouldJumpToEndOfFile) { if (render) {
debug("Running nano + %s", path); debug("Running Vivify...");
execlp("nano", "nano", "+", path, NULL); pid_t viv_pid = fork();
} else { error(viv_pid < 0, "program", "fork() failed.");
debug("Running nano %s", path);
execlp("nano", "nano", path, NULL);
}
error(1, "program", "execlp() failed."); if (viv_pid == 0) {
// ---- HELIX ---- // GRANDCHILD → viv
} else if (strcmp(editor, "helix") == 0) {
// If render enabled → spawn viv in parallel char viv_path[PATH_MAX];
if (render) { strncpy(viv_path, path, PATH_MAX - 1);
debug("Running Vivify..."); viv_path[PATH_MAX - 1] = '\0';
pid_t viv_pid = fork();
error(viv_pid < 0, "program", "fork() failed.");
if (viv_pid == 0) { if (shouldJumpToEndOfFile) {
// GRANDCHILD → viv strncat(viv_path, ":99999", PATH_MAX - strlen(viv_path) - 1);
}
char viv_path[PATH_MAX]; debug("Running viv %s", viv_path);
strncpy(viv_path, path, PATH_MAX - 1); execlp("viv", "viv", viv_path, NULL);
viv_path[PATH_MAX - 1] = '\0'; error(1, "program", "execlp() failed.");
}
// IMPORTANT: do NOT wait for viv
}
if (shouldJumpToEndOfFile) { // Now run nano (this replaces the child process)
strncat(viv_path, ":99999", if (shouldJumpToEndOfFile) {
PATH_MAX - strlen(viv_path) - 1); 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); // If render enabled → spawn viv in parallel
execlp("viv", "viv", viv_path, NULL); if (render) {
error(1, "program", "execlp() failed."); debug("Running Vivify...");
} pid_t viv_pid = fork();
// IMPORTANT: do NOT wait for viv error(viv_pid < 0, "program", "fork() failed.");
}
// Now run helix (this replaces the child process) if (viv_pid == 0) {
if (shouldJumpToEndOfFile) { // GRANDCHILD → viv
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."); char viv_path[PATH_MAX];
} strncpy(viv_path, path, PATH_MAX - 1);
// ---- kakoune ---- viv_path[PATH_MAX - 1] = '\0';
else if (strcmp(editor, "kakoune") == 0) {
// If render enabled → spawn viv in parallel if (shouldJumpToEndOfFile) {
if (render) { strncat(viv_path, ":99999", PATH_MAX - strlen(viv_path) - 1);
debug("Running Vivify..."); }
pid_t viv_pid = fork();
error(viv_pid < 0, "program", "fork() failed.");
if (viv_pid == 0) { debug("Running viv %s", viv_path);
// GRANDCHILD → viv execlp("viv", "viv", viv_path, NULL);
error(1, "program", "execlp() failed.");
}
// IMPORTANT: do NOT wait for viv
}
char viv_path[PATH_MAX]; // Now run nano (this replaces the child process)
strncpy(viv_path, path, PATH_MAX - 1); if (shouldJumpToEndOfFile) {
viv_path[PATH_MAX - 1] = '\0'; debug("Running kak + %s", path);
execlp("kak", "kak", "+", path, NULL);
} else {
debug("Running kak %s", path);
execlp("kak", "kak", path, NULL);
}
if (shouldJumpToEndOfFile) { error(1, "program", "execlp() failed.");
strncat(viv_path, ":99999", // ---- VI ----
PATH_MAX - strlen(viv_path) - 1); } 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 // PARENT: wait ONLY editor
// ========================= // =========================
int status; 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 while (waitpid(editor_pid, &status, 0) ==
if (errno != EINTR) { -1) { // we can't just use waitpid(). Because when resizing the terminal, waitpid() is
perror("waitpid"); // returned so we loop to see if there is not a problem
break; if (errno != EINTR) {
} perror("waitpid");
break;
}
} }
return 0; return 0;
} }
+35 -32
View File
@@ -1,60 +1,60 @@
#ifndef UTILS_H #ifndef UTILS_H
#define 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 <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <dirent.h>
#include <sys/stat.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 <sys/wait.h>
#include <limits.h>
#include <cjson/cJSON.h>
#include <regex.h>
#include <errno.h>
#include <time.h> #include <time.h>
#include <ftw.h> #include <unistd.h>
#ifndef VERSION #ifndef VERSION
#define VERSION "dev" #define VERSION "dev"
#endif #endif
#define BUFFER_SIZE 256 //standard buffer size. #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 // 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 DAILY 86400
#define WEEKLY 604800 #define WEEKLY 604800
#define MONTHLY 2628000 // calculated from the average lenght of a non leap year #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__) _debug(shouldDebug, __FILE__, __LINE__, __func__, message, ##__VA_ARGS__)
#define altDebug(message, ...) \ #define altDebug(message, ...) _altDebug(shouldDebug, message, ##__VA_ARGS__)
_altDebug(shouldDebug, message, ##__VA_ARGS__) #define error(condition, type, message, ...) \
#define error(condition, type, message, ...) \ _error(shouldDebug, condition, type, __FILE__, __LINE__, __func__, message, ##__VA_ARGS__)
_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 // 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 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.
//compares two strings alphabetically. // this function is used for qsort
//this function is used for qsort
int compareString(const void *a, const void *b); int compareString(const void *a, const void *b);
// compares two strings in reversed alphabetical order // compares two strings in reversed alphabetical order
// this function is used for qsort // this function is used for qsort
int reverseCompareString(const void *a, const void *b); int reverseCompareString(const void *a, const void *b);
// checks if editor is supported and if it installed. // 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. // 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. // If the executable from an editor is not the editor name (for example neovim and nvim), you must
// This can return an error and stop the program. // handle at the start of the function. This can return an error and stop the program.
int isEditorValid(char *editorToCheck, int useDefaultEditor, int debug); int isEditorValid(char *editorToCheck, int useDefaultEditor, int debug);
// please use the macro debug instead of _debug. // please use the macro debug instead of _debug.
//formated debugging. // formated debugging.
void _debug(const int d, const char *file, const int line, const char *function, const char *message, ...); void _debug(const int d, const char *file, const int line, const char *function,
//use for less formal debuggin. (usefull if enumerating or making a list). const char *message, ...);
// use for less formal debuggin. (usefull if enumerating or making a list).
void _altDebug(const int d, const char *message, ...); void _altDebug(const int d, const char *message, ...);
// formated error. // 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 1 if the string is in the array.
// Returns 0 if the string is not 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. // 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 '_'. // replace unwanted chars by '_'.
// '.' is replaced if it is only the first two chars // '.' is replaced if it is only the first two chars
void sanitize(char *string); void sanitize(char *string);
//from https://stackoverflow.com/a/5467788. // from https://stackoverflow.com/a/5467788.
//deletes an entire directory. Use with parsimony and carefullness. // deletes an entire directory. Use with parsimony and carefullness.
int rmrf(char *path, int shouldDebug); int rmrf(char *path, int shouldDebug);
// Inputs are the path to the file, the editor to open and some rendering option. // Inputs are the path to the file, the editor to open and some rendering option.
// render: if we render the .md file with Vivify. // 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. If need launches in the background rsync to do the backuping.
Each pair of source/destination will launch one rsync process. 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. 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.*/ rsyncArgs is the array of arguments to be passed to rsync. Do not inclue destination or source. It
void handleBackups(char **sourceDirectoryArray, const int sourceDirectoryNumber, char **destinationDirectoryArray, const char *homeDir, const int interval, const char **rsyncArgs, const int rsyncArgsNumber, const int shouldDebug); 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 #endif