log

Minimalist command line note manager
git clone https://codeberg.org/night0721/log
Log | Files | Refs | README | LICENSE

commit 8d140e53cc3777b33dfecfb8c89f1e1d400b9f1c
parent 9edb6b65382af3d06385ef6900940ded0fa5da02
Author: night0721 <[email protected]>
Date:   Fri,  5 Jul 2024 10:52:27 +0100

Rebrand to log and big haul to makefile

Diffstat:
MMakefile | 76++++++++++++++++++++++++++++++++++++++--------------------------------------
MREADME.md | 19+++++++++----------
Alog.1 | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Alog.c | 386+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dnoted.1 | 52----------------------------------------------------
Dnoted.c | 386-------------------------------------------------------------------------------
6 files changed, 485 insertions(+), 486 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,45 +1,45 @@ -CC=gcc +.POSIX: +.SUFFIXES: +CC = cc VERSION = 1.0 -PREFIX = /usr/local -MANPREFIX = ${PREFIX}/share/man +TARGET = ccc +MANPAGE = $(TARGET).1 +CONF = config.h +PREFIX ?= /usr/local +BINDIR = $(PREFIX)/bin +MANDIR = $(PREFIX)/share/man/man1 + +# Flags +CFLAGS = -O3 -march=native -mtune=native -pipe -s -flto -std=c99 -pedantic -Wall -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 + +SRC = $(TARGET).c + +$(TARGET): $(SRC) $(CONF) + $(CC) $(SRC) -o $@ $(CFLAGS) + +dist: install + mkdir -p $(TARGET)-$(VERSION) + cp -R README.md $(MANPAGE) $(TARGET) $(TARGET)-$(VERSION) + tar -cf $(TARGET)-$(VERSION).tar $(TARGET)-$(VERSION) + gzip $(TARGET)-$(VERSION).tar + rm -rf $(TARGET)-$(VERSION) + +install: $(TARGET) + mkdir -p $(DESTDIR)$(BINDIR) + mkdir -p $(DESTDIR)$(MANDIR) + cp -p $(TARGET) $(DESTDIR)$(BINDIR)/$(TARGET) + chmod 755 $(DESTDIR)$(BINDIR)/$(TARGET) + sed "s/VERSION/${VERSION}/g" < $(MANPAGE) > $(DESTDIR)/$(MANDIR)/$(MANPAGE) + chmod 644 $(DESTDIR)$(MANDIR)/$(MANPAGE) -CFLAGS = -std=gnu11 -O0 -Wall - -SRC = noted.c config.h -OBJ = ${SRC:.c=.o} - -.c.o: - ${CC} -c ${CFLAGS} $< - -noted: ${OBJ} - ${CC} -o $@ ${OBJ} - strip noted +uninstall: + $(RM) $(DESTDIR)$(BINDIR)/$(TARGET) + $(RM) $(DESTDIR)$(MANDIR)/$(MANPAGE) clean: - rm -rf noted - -version: - sed -i 's/#define VERSION "[^"]*"/#define VERSION "${VERSION}"/' config.h - -dist: version noted - mkdir -p noted-${VERSION} - cp -R LICENSE README.md noted.1 noted noted-${VERSION} - tar -cf noted-${VERSION}.tar noted-${VERSION} - gzip noted-${VERSION}.tar - rm -rf noted-${VERSION} - -install: all - mkdir -p ${DESTDIR}${PREFIX}/bin - cp -f noted ${DESTDIR}${PREFIX}/bin - chmod 755 ${DESTDIR}${PREFIX}/bin/noted - mkdir -p ${DESTDIR}${MANPREFIX}/man1 - sed "s/VERSION/${VERSION}/g" < noted.1 > ${DESTDIR}${MANPREFIX}/man1/noted.1 - chmod 644 ${DESTDIR}${MANPREFIX}/man1/noted.1 + $(RM) $(TARGET) -uninstall: - rm -f ${DESTDIR}${PREFIX}/bin/noted\ - ${DESTDIR}${MANPREFIX}/man1/noted.1 -all: noted +all: $(TARGET) -.PHONY: all clean dist install uninstall noted +.PHONY: all dist install uninstall clean diff --git a/README.md b/README.md @@ -1,14 +1,14 @@ -# noted -noted is a minimalistic command line note manager. +# log +log is a minimalistic command line note manager. # Preview ![](https://r2.e-z.host/3c62bb3a-a8a9-43f6-afd6-553646f51dc4/lzwqimtg.png) # Usage ```sh -Usage: ./noted [-l] [-i] [-v] [-h] - ./noted [-I|-Q|-R|-S] [filename] -noted is a minimalistic command line note manager written in C. +Usage: ./log [-l] [-i] [-v] [-h] + ./log [-I|-Q|-R|-S] [filename] +log is a minimalistic command line note manager written in C. Options: -l Lists the notes in the directory in a tree format. -i Prints the directory where the notes are stored. @@ -30,7 +30,6 @@ Options: - Generate boilerplate for notes for different formats # Dependencies - None # Building @@ -41,16 +40,16 @@ $ make ``` # Notes -noted will use the editor defined in the `EDITOR` environment variable to open notes. If `EDITOR` is not defined, you will need to define it in your shell configuration file. +log will use the editor defined in the `EDITOR` environment variable to open notes. If `EDITOR` is not defined, you will need to define it in your shell configuration file. Default file extension is `.md` and it can be changed in `config.h` file as you like. Once you change the extension, you need to recompile the program to apply the changes and the new notes will have different boilerplates. However, all commands will not work for old notes with the previous extension. -When adding note, you can add a slash to indicate a subdirectory. For example, `noted -I maths/ch1` will create a note called `ch1` in `maths` folder. **Note**: You can't create a note in a non-existing folder. You need to create the folder first manually. +When adding note, you can add a slash to indicate a subdirectory. For example, `log -I maths/ch1` will create a note called `ch1` in `maths` folder. **Note**: You can't create a note in a non-existing folder. You need to create the folder first manually. -Unlike pass, you don't need to use `noted init` to initialise the store, it is automatically created once you use any command. The store is either in HOME or XDG_CONFIG_HOME if it is defined. +Unlike pass, you don't need to use `log init` to initialise the store, it is automatically created once you use any command. The store is either in HOME or XDG_CONFIG_HOME if it is defined. # Contributions Contributions are welcomed, feel free to open a pull request. # License -This project is licensed under the GNU Public License v3.0. See [LICENSE](https://github.com/night0721/noted/blob/master/LICENSE) for more information. +This project is licensed under the GNU Public License v3.0. See [LICENSE](https://github.com/night0721/log/blob/master/LICENSE) for more information. diff --git a/log.1 b/log.1 @@ -0,0 +1,52 @@ +.TH log 1 log\-VERSION +.SH NAME +log \- Minimalistic note manager +.SH SYNOPSIS +.B log +.RB [ \-l ] +.RB [ \-i ] +.RB [ \-v ] +.RB [ \-h ] +.RB [ \-I ] +.RB [ \-Q ] +.RB [ \-R ] + +.SH DESCRIPTION +log is a minimalistic command line note manager written in C. It uses EDITOR environment variable to open notes and respects XDG variables to determine the store directory. It also supports markdown and plaintext notes. It is a simple and easy to use note manager with providing basic functionalities like adding, removing, and listing notes. +.SH OPTIONS + +.TP +.B \-l +Lists the notes in the directory in a tree format. + +.TP +.B \-i +Prints the directory where the notes are stored. + +.TP +.B \-v +Prints the version of the program. + +.TP +.B \-h +Shows the help message. + +.TP +.B \-I [filename] +Creates a new note with the name specified in the filename. + +.TP +.B \-Q [filename] +Searches for the note with specified name and prints its path to the stdout. + +.TP +.B \-R [filename] +Removes the note specified in the filename. + +.TP +.B \-S [filename] +Prints the content of the note specified in the filename. + +.SH AUTHOR +Made by Night Kaly +.B <[email protected]> diff --git a/log.c b/log.c @@ -0,0 +1,386 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ftw.h> +#include <unistd.h> +#include <time.h> +#include <dirent.h> +#include <sys/stat.h> +#include <libgen.h> +#include <linux/limits.h> + +#include "config.h" + +char *get_log_dir() { + char *xdg_data_home = getenv("XDG_DATA_HOME"); + char *dirname = "log"; + char *log_dir = NULL; + if (xdg_data_home == NULL) { + xdg_data_home = getenv("HOME"); + if (xdg_data_home == NULL) { + fprintf(stderr, "log: HOME and XDG_DATA_HOME environment variable not set\n"); + exit(EXIT_FAILURE); + } + } + log_dir = malloc(sizeof(char) * (strlen(xdg_data_home) + strlen(dirname) + 2)); + if (log_dir == NULL) { + fprintf(stderr, "log: Error allocating memory\n"); + exit(EXIT_FAILURE); + } + sprintf(log_dir, "%s/%s", xdg_data_home, dirname); + struct stat dir_stat; + if (!((stat(log_dir, &dir_stat) == 0) && S_ISDIR(dir_stat.st_mode))) { // check defined path is directory + if (mkdir(log_dir, S_IRWXU)) { // 700 + fprintf(stderr, "log: Cannot create log directory\n"); + exit(EXIT_FAILURE); + } + } + return log_dir; +} + +int compare(const void *a, const void *b) { + return strcmp(*(const char **)a, *(const char **)b); +} + +void tree(const char *basepath, int depth) { + char path[1000]; + struct dirent *dp; + DIR *dir = opendir(basepath); + + if (!dir) + return; + + char *files[1000]; + int file_count = 0; + + while ((dp = readdir(dir)) != NULL) { + if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0) { + files[file_count] = strdup(dp->d_name); + file_count++; + } + } + + qsort(files, file_count, sizeof(char *), compare); + + for (int i = 0; i < file_count; i++) { + for (int j = 0; j < depth - 1; j++) { + printf("│ "); + } + + if (depth > 0) { + printf("├── "); + } + + printf("%s\n", files[i]); + + strcpy(path, basepath); + strcat(path, "/"); + strcat(path, files[i]); + + tree(path, depth + 1); + + free(files[i]); + } + + closedir(dir); +} + +int list_dir(const char *dir) { + if (dir == NULL || *dir == '\0') { + fprintf(stderr, "log: Invalid directory path\n"); + return 1; + } + struct stat dir_stat; + if (stat(dir, &dir_stat) == -1) { + perror("log"); + return errno; + } + if (S_ISDIR(dir_stat.st_mode)) { + tree(dir, 0); + return 0; + + } else { + fprintf(stderr, "log: %s is not a directory\n", dir); + return 1; + } +} + +void find_note(const char *basepath, const char *filename, char **filepaths, int *filecount) { + DIR *dir; + struct dirent *entry; + + if (!(dir = opendir(basepath))) { + return; + } + + while ((entry = readdir(dir)) != NULL) { + if (entry->d_type == DT_DIR) { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { + continue; + } + char path[1024]; + snprintf(path, sizeof(path), "%s/%s", basepath, entry->d_name); + find_note(path, filename, filepaths, filecount); + } else { + char *ext = strrchr(entry->d_name, '.'); + if (ext == NULL) { + continue; + } + // remove extension + char *bname = strdup(entry->d_name); + bname[strlen(bname) - strlen(DEFAULT_EXT)] = '\0'; + if (strcmp(bname, filename) == 0) { + int count = *filecount; + size_t len = strlen(basepath) + strlen(bname) + 2 + strlen(DEFAULT_EXT); + filepaths[count] = malloc(sizeof(char) * len); + snprintf(filepaths[count], len, "%s/%s%s", basepath, bname, DEFAULT_EXT); + *filecount += 1; + } + } + } + closedir(dir); +} + +// check if note exists, if yes return the path +char *check_note_exist(char *filename) { + char *log_dir = get_log_dir(); + char **file_paths = malloc(sizeof(char *) * 128); // max 128 same file names + int file_count = 0; + find_note(log_dir, (const char *) filename, file_paths, &file_count); + if (file_count == 0) { + free(log_dir); + return NULL; + } else if (file_count == 1) { + char *filepath = malloc(sizeof(char) * (strlen(file_paths[0]) + 1)); + if (filepath == NULL) { + fprintf(stderr, "log: Error allocating memory\n"); + free(log_dir); + exit(EXIT_FAILURE); + } + strcpy(filepath, file_paths[0]); + free(log_dir); + return filepath; + } else { + printf("Multiple files found with the same name:\n"); + for (int i = 0; i < file_count; i++) { + printf("%d. %s\n", i + 1, file_paths[i]); + } + printf("Enter the number corresponding to the file you want to use: "); + int choice; + scanf("%d", &choice); + if (choice < 1 || choice > file_count) { + fprintf(stderr, "log: Invalid choice\n"); + free(log_dir); + return NULL; + } + char *filepath = malloc(sizeof(char) * (strlen(file_paths[choice - 1]) + 1)); + if (filepath == NULL) { + fprintf(stderr, "log: Error allocating memory\n"); + free(log_dir); + exit(EXIT_FAILURE); + } + strcpy(filepath, file_paths[choice - 1]); + free(log_dir); + return filepath; + } +} + +void add_boiler_plate(FILE *file, const char *filename, const char *ext) { + time_t t = time(NULL); + struct tm tm = *localtime(&t); + if (strcmp(ext, ".md") == 0) { + fprintf(file, "---\n"); + fprintf(file, "title: %s\n", filename); + fprintf(file, "description: \n"); + fprintf(file, "tags: \n"); + fprintf(file, "created: \"%d-%02d-%02d\"\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); + fprintf(file, "lastmod: \"%d-%02d-%02d\"\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); + fprintf(file, "---\n"); + } else if (strcmp(ext, ".txt") == 0) { + fprintf(file, "Title: %s\n", filename); + fprintf(file, "Description: \n"); + fprintf(file, "Tags: \n"); + fprintf(file, "Created: %d-%02d-%02d %02d:%02d:%02d\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); + fprintf(file, "Last Modified: %d-%02d-%02d %02d:%02d:%02d\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); + } else if (strcmp(ext, ".org") == 0) { + fprintf(file, "#+TITLE: %s\n", filename); + fprintf(file, "#+DESCRIPTION: \n"); + fprintf(file, "#+TAGS: \n"); + fprintf(file, "#+CREATED: %d-%02d-%02d %02d:%02d:%02d\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); + fprintf(file, "#+LASTMOD: %d-%02d-%02d %02d:%02d:%02d\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); + } else if (strcmp(ext, ".html") == 0) { + fprintf(file, "<!DOCTYPE html>\n"); + fprintf(file, "<html>\n"); + fprintf(file, "\t<head>\n"); + fprintf(file, "\t\t<title>%s</title>\n", filename); + fprintf(file, "\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"); + fprintf(file, "\t\t<meta charset=\"UTF-8\">\n"); + fprintf(file, "\t</head>\n"); + fprintf(file, "\t<body>\n"); + fprintf(file, "\t\t<h1>%s</h1>\n", filename); + fprintf(file, "\t\t<p>Description: </p>\n"); + fprintf(file, "\t\t<p>Tags: </p>\n"); + fprintf(file, "\t\t<p>Created: %d-%02d-%02d %02d:%02d:%02d</p>\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); + fprintf(file, "\t\t<p>Last Modified: %d-%02d-%02d %02d:%02d:%02d</p>\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); + fprintf(file, "\t</body>\n"); + fprintf(file, "</html>\n"); + } else { + fprintf(stderr, "log: Unsupported file extension\n"); + } +} +void print_note(char *filename) { + if (filename == NULL) { + fprintf(stderr, "log: filename is required\n"); + return; + } + char *filepath = check_note_exist(filename); + if (filepath != NULL) { + FILE *file = fopen(filepath, "r"); + if (file == NULL) { + perror("log"); + free(filepath); + return; + } + char *line = NULL; + size_t len = 0; + while (getline(&line, &len, file) != -1) { + printf("%s", line); + } + free(line); + fclose(file); + } else { + fprintf(stderr, "log: %s is not in log\n", filename); + } +} + +void edit_note(char *filename) { + char *filepath = check_note_exist(filename); + char *editor = getenv("EDITOR"); + if (editor == NULL) { + fprintf(stderr, "log: EDITOR environment variable must be set\n"); + free(filepath); + return; + } + if (filepath != NULL) { + char *cmd = malloc(sizeof(char) * (strlen(editor) + strlen(filepath) + 2)); + if (cmd == NULL) { + fprintf(stderr, "log: Error allocating memory\n"); + free(filepath); + return; + } + sprintf(cmd, "%s %s", editor, filepath); + system(cmd); + free(cmd); + } else { + char *log_dir = get_log_dir(); + filepath = malloc(sizeof(char) * (strlen(log_dir) + strlen(filename) + 2 + strlen(DEFAULT_EXT))); + if (filepath == NULL) { + fprintf(stderr, "log: Error allocating memory\n"); + free(log_dir); + return; + } + sprintf(filepath, "%s/%s%s", log_dir, filename, DEFAULT_EXT); + FILE *file = fopen(filepath, "w"); + if (file == NULL) { + perror("log"); + free(filepath); + return; + } + add_boiler_plate(file, filename, DEFAULT_EXT); + fclose(file); + char *cmd = malloc(sizeof(char) * (strlen(editor) + strlen(filepath) + 2)); + if (cmd == NULL) { + fprintf(stderr, "log: Error allocating memory\n"); + free(filepath); + return; + } + sprintf(cmd, "%s %s", editor, filepath); + system(cmd); + free(cmd); + } +} + +void remove_note(char *filename) { + char *filepath = check_note_exist(filename); + if (filepath != NULL) { + if (remove(filepath) == -1) { + perror("log"); + } else { + printf("log: %s is now removed from log\n", filename); + } + } else { + fprintf(stderr, "log: %s is not in log\n", filename); + } +} + +void search_note(char *filename) { + char *filepath = check_note_exist(filename); + if (filepath != NULL) { + printf("Path of %s: %s\n", filename, filepath); + } else { + fprintf(stderr, "log: %s is not in log\n", filename); + } +} + +void usage(char **argv) { + fprintf(stderr, "Usage: %s [-l] [-i] [-v] [-h]\n", argv[0]); + fprintf(stderr, " %s [-I|-Q|-R|-S] [filename]\n", argv[0]); +} + +int main(int argc, char **argv) { + int res; + char *log_dir = get_log_dir(); + if (log_dir == NULL) { + return 1; + } + + if (argc == 2) { + if (strcmp(argv[1], "-l") == 0) { + // read dir and print recursively + res = list_dir(log_dir); + free(log_dir); + return res; + } else if (strcmp(argv[1], "-i") == 0) { + printf("log directory: %s\n", log_dir); + } else if (strcmp(argv[1], "-v") == 0) { + printf("log: %s\n", VERSION); + } else if (strcmp(argv[1], "-h") == 0) { + usage(argv); + printf("log is a minimalistic command line note manager written in C.\n"); + printf("Options:\n"); + printf(" -l\t\tLists the notes in the directory in a tree format.\n"); + printf(" -i\t\tPrints the directory where the notes are stored.\n"); + printf(" -v\t\tPrints the version of the program.\n"); + printf(" -h\t\tShow the help message.\n"); + printf(" -I [filename]\tCreates a new note with the name specified in the filename.\n"); + printf(" -Q [filename]\tSearches for the note with specified name and prints its path to the stdout.\n"); + printf(" -R [filename]\tRemoves the note specified in the filename.\n"); + printf(" -S [filename]\tPrints the content of the note specified in the filename.\n"); + } else { + usage(argv); + return 0; + } + } else if (argc == 3) { + if (strcmp(argv[1], "-I") == 0) { + if (argv[2] != NULL && argv[2][0] != '-') { + edit_note(argv[2]); + } else { + printf("log: filename cannot be start with '-'\n"); + usage(argv); + } + } else if (strcmp(argv[1], "-Q") == 0) { + search_note(argv[2]); + } else if (strcmp(argv[1], "-R") == 0) { + remove_note(argv[2]); + } else if (strcmp(argv[1], "-S") == 0) { + print_note(argv[2]); + } else { + usage(argv); + return 0; + } + } else { + usage(argv); + return 0; + } +} diff --git a/noted.1 b/noted.1 @@ -1,52 +0,0 @@ -.TH noted 1 noted\-VERSION -.SH NAME -noted \- Minimalistic note manager -.SH SYNOPSIS -.B noted -.RB [ \-l ] -.RB [ \-i ] -.RB [ \-v ] -.RB [ \-h ] -.RB [ \-I ] -.RB [ \-Q ] -.RB [ \-R ] - -.SH DESCRIPTION -noted is a minimalistic command line note manager written in C. It uses EDITOR environment variable to open notes and respects XDG variables to determine the store directory. It also supports markdown and plaintext notes. It is a simple and easy to use note manager with providing basic functionalities like adding, removing, and listing notes. -.SH OPTIONS - -.TP -.B \-l -Lists the notes in the directory in a tree format. - -.TP -.B \-i -Prints the directory where the notes are stored. - -.TP -.B \-v -Prints the version of the program. - -.TP -.B \-h -Shows the help message. - -.TP -.B \-I [filename] -Creates a new note with the name specified in the filename. - -.TP -.B \-Q [filename] -Searches for the note with specified name and prints its path to the stdout. - -.TP -.B \-R [filename] -Removes the note specified in the filename. - -.TP -.B \-S [filename] -Prints the content of the note specified in the filename. - -.SH AUTHOR -Made by Night Kaly -.B <[email protected]> diff --git a/noted.c b/noted.c @@ -1,386 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <ftw.h> -#include <unistd.h> -#include <time.h> -#include <dirent.h> -#include <sys/stat.h> -#include <libgen.h> -#include <linux/limits.h> - -#include "config.h" - -char *get_noted_dir() { - char *xdg_data_home = getenv("XDG_DATA_HOME"); - char *dirname = "noted"; - char *noted_dir = NULL; - if (xdg_data_home == NULL) { - xdg_data_home = getenv("HOME"); - if (xdg_data_home == NULL) { - fprintf(stderr, "noted: HOME and XDG_DATA_HOME environment variable not set\n"); - exit(EXIT_FAILURE); - } - } - noted_dir = malloc(sizeof(char) * (strlen(xdg_data_home) + strlen(dirname) + 2)); - if (noted_dir == NULL) { - fprintf(stderr, "noted: Error allocating memory\n"); - exit(EXIT_FAILURE); - } - sprintf(noted_dir, "%s/%s", xdg_data_home, dirname); - struct stat dir_stat; - if (!((stat(noted_dir, &dir_stat) == 0) && S_ISDIR(dir_stat.st_mode))) { // check defined path is directory - if (mkdir(noted_dir, S_IRWXU)) { // 700 - fprintf(stderr, "bunker: Cannot create bunker directory\n"); - exit(EXIT_FAILURE); - } - } - return noted_dir; -} - -int compare(const void *a, const void *b) { - return strcmp(*(const char **)a, *(const char **)b); -} - -void tree(const char *basepath, int depth) { - char path[1000]; - struct dirent *dp; - DIR *dir = opendir(basepath); - - if (!dir) - return; - - char *files[1000]; - int file_count = 0; - - while ((dp = readdir(dir)) != NULL) { - if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0) { - files[file_count] = strdup(dp->d_name); - file_count++; - } - } - - qsort(files, file_count, sizeof(char *), compare); - - for (int i = 0; i < file_count; i++) { - for (int j = 0; j < depth - 1; j++) { - printf("│ "); - } - - if (depth > 0) { - printf("├── "); - } - - printf("%s\n", files[i]); - - strcpy(path, basepath); - strcat(path, "/"); - strcat(path, files[i]); - - tree(path, depth + 1); - - free(files[i]); - } - - closedir(dir); -} - -int list_dir(const char *dir) { - if (dir == NULL || *dir == '\0') { - fprintf(stderr, "noted: Invalid directory path\n"); - return 1; - } - struct stat dir_stat; - if (stat(dir, &dir_stat) == -1) { - perror("noted"); - return errno; - } - if (S_ISDIR(dir_stat.st_mode)) { - tree(dir, 0); - return 0; - - } else { - fprintf(stderr, "noted: %s is not a directory\n", dir); - return 1; - } -} - -void find_note(const char *basepath, const char *filename, char **filepaths, int *filecount) { - DIR *dir; - struct dirent *entry; - - if (!(dir = opendir(basepath))) { - return; - } - - while ((entry = readdir(dir)) != NULL) { - if (entry->d_type == DT_DIR) { - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { - continue; - } - char path[1024]; - snprintf(path, sizeof(path), "%s/%s", basepath, entry->d_name); - find_note(path, filename, filepaths, filecount); - } else { - char *ext = strrchr(entry->d_name, '.'); - if (ext == NULL) { - continue; - } - // remove extension - char *bname = strdup(entry->d_name); - bname[strlen(bname) - strlen(DEFAULT_EXT)] = '\0'; - if (strcmp(bname, filename) == 0) { - int count = *filecount; - size_t len = strlen(basepath) + strlen(bname) + 2 + strlen(DEFAULT_EXT); - filepaths[count] = malloc(sizeof(char) * len); - snprintf(filepaths[count], len, "%s/%s%s", basepath, bname, DEFAULT_EXT); - *filecount += 1; - } - } - } - closedir(dir); -} - -// check if note exists, if yes return the path -char *check_note_exist(char *filename) { - char *noted_dir = get_noted_dir(); - char **file_paths = malloc(sizeof(char *) * 128); // max 128 same file names - int file_count = 0; - find_note(noted_dir, (const char *) filename, file_paths, &file_count); - if (file_count == 0) { - free(noted_dir); - return NULL; - } else if (file_count == 1) { - char *filepath = malloc(sizeof(char) * (strlen(file_paths[0]) + 1)); - if (filepath == NULL) { - fprintf(stderr, "noted: Error allocating memory\n"); - free(noted_dir); - exit(EXIT_FAILURE); - } - strcpy(filepath, file_paths[0]); - free(noted_dir); - return filepath; - } else { - printf("Multiple files found with the same name:\n"); - for (int i = 0; i < file_count; i++) { - printf("%d. %s\n", i + 1, file_paths[i]); - } - printf("Enter the number corresponding to the file you want to use: "); - int choice; - scanf("%d", &choice); - if (choice < 1 || choice > file_count) { - fprintf(stderr, "noted: Invalid choice\n"); - free(noted_dir); - return NULL; - } - char *filepath = malloc(sizeof(char) * (strlen(file_paths[choice - 1]) + 1)); - if (filepath == NULL) { - fprintf(stderr, "noted: Error allocating memory\n"); - free(noted_dir); - exit(EXIT_FAILURE); - } - strcpy(filepath, file_paths[choice - 1]); - free(noted_dir); - return filepath; - } -} - -void add_boiler_plate(FILE *file, const char *filename, const char *ext) { - time_t t = time(NULL); - struct tm tm = *localtime(&t); - if (strcmp(ext, ".md") == 0) { - fprintf(file, "---\n"); - fprintf(file, "title: %s\n", filename); - fprintf(file, "description: \n"); - fprintf(file, "tags: \n"); - fprintf(file, "created: \"%d-%02d-%02d\"\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); - fprintf(file, "lastmod: \"%d-%02d-%02d\"\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); - fprintf(file, "---\n"); - } else if (strcmp(ext, ".txt") == 0) { - fprintf(file, "Title: %s\n", filename); - fprintf(file, "Description: \n"); - fprintf(file, "Tags: \n"); - fprintf(file, "Created: %d-%02d-%02d %02d:%02d:%02d\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); - fprintf(file, "Last Modified: %d-%02d-%02d %02d:%02d:%02d\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); - } else if (strcmp(ext, ".org") == 0) { - fprintf(file, "#+TITLE: %s\n", filename); - fprintf(file, "#+DESCRIPTION: \n"); - fprintf(file, "#+TAGS: \n"); - fprintf(file, "#+CREATED: %d-%02d-%02d %02d:%02d:%02d\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); - fprintf(file, "#+LASTMOD: %d-%02d-%02d %02d:%02d:%02d\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); - } else if (strcmp(ext, ".html") == 0) { - fprintf(file, "<!DOCTYPE html>\n"); - fprintf(file, "<html>\n"); - fprintf(file, "\t<head>\n"); - fprintf(file, "\t\t<title>%s</title>\n", filename); - fprintf(file, "\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"); - fprintf(file, "\t\t<meta charset=\"UTF-8\">\n"); - fprintf(file, "\t</head>\n"); - fprintf(file, "\t<body>\n"); - fprintf(file, "\t\t<h1>%s</h1>\n", filename); - fprintf(file, "\t\t<p>Description: </p>\n"); - fprintf(file, "\t\t<p>Tags: </p>\n"); - fprintf(file, "\t\t<p>Created: %d-%02d-%02d %02d:%02d:%02d</p>\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); - fprintf(file, "\t\t<p>Last Modified: %d-%02d-%02d %02d:%02d:%02d</p>\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); - fprintf(file, "\t</body>\n"); - fprintf(file, "</html>\n"); - } else { - fprintf(stderr, "noted: Unsupported file extension\n"); - } -} -void print_note(char *filename) { - if (filename == NULL) { - fprintf(stderr, "noted: filename is required\n"); - return; - } - char *filepath = check_note_exist(filename); - if (filepath != NULL) { - FILE *file = fopen(filepath, "r"); - if (file == NULL) { - perror("noted"); - free(filepath); - return; - } - char *line = NULL; - size_t len = 0; - while (getline(&line, &len, file) != -1) { - printf("%s", line); - } - free(line); - fclose(file); - } else { - fprintf(stderr, "noted: %s is not in noted\n", filename); - } -} - -void edit_note(char *filename) { - char *filepath = check_note_exist(filename); - char *editor = getenv("EDITOR"); - if (editor == NULL) { - fprintf(stderr, "noted: EDITOR environment variable must be set\n"); - free(filepath); - return; - } - if (filepath != NULL) { - char *cmd = malloc(sizeof(char) * (strlen(editor) + strlen(filepath) + 2)); - if (cmd == NULL) { - fprintf(stderr, "noted: Error allocating memory\n"); - free(filepath); - return; - } - sprintf(cmd, "%s %s", editor, filepath); - system(cmd); - free(cmd); - } else { - char *noted_dir = get_noted_dir(); - filepath = malloc(sizeof(char) * (strlen(noted_dir) + strlen(filename) + 2 + strlen(DEFAULT_EXT))); - if (filepath == NULL) { - fprintf(stderr, "noted: Error allocating memory\n"); - free(noted_dir); - return; - } - sprintf(filepath, "%s/%s%s", noted_dir, filename, DEFAULT_EXT); - FILE *file = fopen(filepath, "w"); - if (file == NULL) { - perror("noted"); - free(filepath); - return; - } - add_boiler_plate(file, filename, DEFAULT_EXT); - fclose(file); - char *cmd = malloc(sizeof(char) * (strlen(editor) + strlen(filepath) + 2)); - if (cmd == NULL) { - fprintf(stderr, "noted: Error allocating memory\n"); - free(filepath); - return; - } - sprintf(cmd, "%s %s", editor, filepath); - system(cmd); - free(cmd); - } -} - -void remove_note(char *filename) { - char *filepath = check_note_exist(filename); - if (filepath != NULL) { - if (remove(filepath) == -1) { - perror("noted"); - } else { - printf("noted: %s is now removed from noted\n", filename); - } - } else { - fprintf(stderr, "noted: %s is not in noted\n", filename); - } -} - -void search_note(char *filename) { - char *filepath = check_note_exist(filename); - if (filepath != NULL) { - printf("Path of %s: %s\n", filename, filepath); - } else { - fprintf(stderr, "noted: %s is not in noted\n", filename); - } -} - -void usage(char **argv) { - fprintf(stderr, "Usage: %s [-l] [-i] [-v] [-h]\n", argv[0]); - fprintf(stderr, " %s [-I|-Q|-R|-S] [filename]\n", argv[0]); -} - -int main(int argc, char **argv) { - int res; - char *noted_dir = get_noted_dir(); - if (noted_dir == NULL) { - return 1; - } - - if (argc == 2) { - if (strcmp(argv[1], "-l") == 0) { - // read dir and print recursively - res = list_dir(noted_dir); - free(noted_dir); - return res; - } else if (strcmp(argv[1], "-i") == 0) { - printf("noted directory: %s\n", noted_dir); - } else if (strcmp(argv[1], "-v") == 0) { - printf("noted: %s\n", VERSION); - } else if (strcmp(argv[1], "-h") == 0) { - usage(argv); - printf("noted is a minimalistic command line note manager written in C.\n"); - printf("Options:\n"); - printf(" -l\t\tLists the notes in the directory in a tree format.\n"); - printf(" -i\t\tPrints the directory where the notes are stored.\n"); - printf(" -v\t\tPrints the version of the program.\n"); - printf(" -h\t\tShow the help message.\n"); - printf(" -I [filename]\tCreates a new note with the name specified in the filename.\n"); - printf(" -Q [filename]\tSearches for the note with specified name and prints its path to the stdout.\n"); - printf(" -R [filename]\tRemoves the note specified in the filename.\n"); - printf(" -S [filename]\tPrints the content of the note specified in the filename.\n"); - } else { - usage(argv); - return 0; - } - } else if (argc == 3) { - if (strcmp(argv[1], "-I") == 0) { - if (argv[2] != NULL && argv[2][0] != '-') { - edit_note(argv[2]); - } else { - printf("noted: filename cannot be start with '-'\n"); - usage(argv); - } - } else if (strcmp(argv[1], "-Q") == 0) { - search_note(argv[2]); - } else if (strcmp(argv[1], "-R") == 0) { - remove_note(argv[2]); - } else if (strcmp(argv[1], "-S") == 0) { - print_note(argv[2]); - } else { - usage(argv); - return 0; - } - } else { - usage(argv); - return 0; - } -}