From 1e8584e8cbe0181ea5a1a55f48d92be93929b0e7 Mon Sep 17 00:00:00 2001 From: Aaron Ball Date: Thu, 29 Dec 2016 00:02:36 -0700 Subject: Refactored note_list process and all related code The old note_list process used inefficient code design. It required all of the metadata for each note in the note list to be allocated and determined before any actions were taken. This was slow, inefficient, and could potentially have required an enormous amount of memory if enough notes were available. The new process uses the file descriptor stream concept from the glibc readdir function. This enables easy traversal of the note list without having to store the entire list in memory (we only need to store the current note metadata). Also removed all occurences of "Schlemiel the painter algorithm" by replacing all occurences of strcpy/strcat/strcat/... in the note_list process with sprintf. Renamed the note_list_t type to note_list, note_t to note, and config_t to config. Moved note_edit function into common.h and renamed to file_edit, as this is a common function not specific to notes. Cleaned up old commented out code, including legacy c++ code (I can't believe this thing used to be written in c++). --- Makefile | 2 +- src/common.c | 18 +++- src/common.h | 2 + src/config.h | 2 +- src/main.c | 131 +++++++++-------------- src/note.c | 53 +++------- src/note.h | 27 ++--- src/note_list.c | 323 ++++++++++++++++++-------------------------------------- src/note_list.h | 38 ++++--- 9 files changed, 218 insertions(+), 378 deletions(-) diff --git a/Makefile b/Makefile index 0682b61..4171f93 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ warnings = -Wall -Wpedantic cc = cc all: - if [[ ! -d obj ]]; then mkdir obj; fi + if [ ! -d obj ]; then mkdir obj; fi $(cc) $(dbg) $(warnings) -std=$(std) -c src/common.c -o $(obj)common.o # $(cc) $(dbg) $(warnings) -std=$(std) -c src/path.c -o $(obj)path.o $(cc) $(dbg) $(warnings) -std=$(std) -c src/config.c -o $(obj)config.o diff --git a/src/common.c b/src/common.c index 65eb1ad..5f1ba3e 100644 --- a/src/common.c +++ b/src/common.c @@ -16,6 +16,7 @@ */ #include "common.h" + /** * Determins the text editor that should be used. Checks the environmental * variable EDITOR for this. @@ -146,7 +147,7 @@ int get_extension(char* path, char* extension) { for(int i = path_len; i > 0; i--) { // Copy if an extension delimiter (a period) is found if(path[i] == '.') { - int ext_len = path_len - i - 1; + int ext_len = path_len - i; strncpy(extension, &path[i+1], ext_len); return ext_len; } @@ -155,6 +156,20 @@ int get_extension(char* path, char* extension) { } +/** + * Opens a note using the specified editor + * + * @param string editor Path to the editor binary to use for editing. + * + * @return int Success or failure of the command + */ +int file_edit(char* path, char* editor) { + char cmd[strlen(editor) + 3 + strlen(path)]; + sprintf(cmd, "%s %s", editor, path); + return system(cmd); +} + + /** * Yup, wrote my own. * TODO: Make this work with escaped path delimiters @@ -293,6 +308,7 @@ int str_contains_case_sensitive(char* str, char* term) { return 0; } + /** * Removes the extension from the given string filename * diff --git a/src/common.h b/src/common.h index 5a3deea..262db6b 100644 --- a/src/common.h +++ b/src/common.h @@ -30,6 +30,8 @@ void get_current_date_time(char*); int get_extension(char*, char*); +int file_edit(char*, char*); + int basename(char*, char*); int str_contains(char*, char*, int); diff --git a/src/config.h b/src/config.h index d0cc9cb..e71ccec 100644 --- a/src/config.h +++ b/src/config.h @@ -19,7 +19,7 @@ #ifndef NOTELESS_CONFIG_H #define NOTELESS_CONFIG_H #define config_line_len 120 -#define config_max 128 +#define config_max 256 #include #include diff --git a/src/main.c b/src/main.c index f24a4bd..336ce15 100644 --- a/src/main.c +++ b/src/main.c @@ -1,25 +1,11 @@ -/** - * Copyright (C) 2014 Aaron Ball - * - * Noteless is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Noteless is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with noteless. If not, see . - */ -#include #include +#include +#include #include -#include "common.h" -#include "config.h" + #include "note_list.h" +#include "note.h" +#include "config.h" /** * Prints the standard help text @@ -49,90 +35,69 @@ void get_help() { * * @param list Note list whos names to enumerate */ -void list_notes(note_list_t* list) { - // List notes - for(int i = 0; i < list->count; i++) { - char name[128]; - strcpy(name, list->names[i]); - strip_extension(name); - printf("* %s\n", name); +void list_notes(note_list* list) { + note* n; + + while((n = note_list_read(list)) != NULL) { + printf("* %s\n", n->name); } } /** - * Ye olde main function + * Sets up environment variables. + * Handles loading in configuration defaults before loading the config file + * values if one is present. */ -int main(int argc, char* argv[]) { - // Print helptext if no commands specified - if(argc == 1) { - printf("\nNo command specified. Printing help text.\n"); - get_help(); - return 1; - } - +void config_setup(config_t* c) { // Get the home dir char home_path[strlen(getenv("HOME"))]; strcpy(home_path, getenv("HOME")); - /** - * Config file overrides - */ - config_t c; - // Set default note path char tmp_note_path[strlen(home_path) + 17]; strcpy(tmp_note_path, home_path); strcat(tmp_note_path, "/Documents/Notes"); - config_set(&c, "note_path", tmp_note_path); + config_set(c, "note_path", tmp_note_path); // Set the default note extension - config_set(&c, "extension", "mdown"); + config_set(c, "extension", "mdown"); // Set editor default char tmp_editor[128]; get_user_editor(tmp_editor); - config_set(&c, "editor", tmp_editor); + config_set(c, "editor", tmp_editor); // Assemble the path to the conf file char conf_path[strlen(home_path) + 23]; strcpy(conf_path, home_path); strcat(conf_path, "/.config/noteless.conf"); // Load the actual config file to override any defaults - config_load(&c, conf_path); - - // Just some literal code comments - // int config_status = config_load(&c, conf_path); - // if(config_status == -1) { - // printf("Config file could not be opened or does not exist.\n"); - // printf("Assuming defaults.\n"); - // } - - - // // If the init command was passed, we want to init before checking if the - // // note path exists, otherwise the error will display and the store will - // // never be initialized. - // if( string( argv[1] ) == "init" ) { - // path p( note_path ); - // return p.create(); - // } - - // // Check to make sure the note path exists - // path p( note_path ); - // if( ! p.exists() ) { - // string out = - // "\nThe note store path '" + p.out() + "' does not exist.\n\n" - // "If this is your first time running noteless, please run " - // "'noteless init' to\n" - // "create the note store here.\n\n" - // "Otherwise, please verify the path variable in your configuration."; - // cout << out << endl; - // return 1; - // } - - note_list_t list; - - note_list_new(&list, config_get(&c, "note_path"), config_get(&c, "extension")); + config_load(c, conf_path); +} + + +int main(int argc, char* argv []) { + // Print helptext if no commands specified + if(argc == 1) { + printf("\nNo command specified. Printing help text.\n"); + get_help(); + return 1; + } + + config_t c; + note_list list; + + // + // Config setup + // + config_setup(&c); + + note_list_new( + &list, + config_get(&c, "note_path"), + config_get(&c, "extension") + ); if(strcmp(argv[1], "ls") == 0 || strcmp(argv[1], "list") == 0) { list_notes(&list); @@ -161,16 +126,14 @@ int main(int argc, char* argv[]) { return note_list_cat_note(&list, argv[2]); } else if(strcmp(argv[1], "help") == 0) { get_help(); - } else if(note_list_get_note_id(&list, argv[1]) != -1 ) { - // Try to open the note if it exists - return note_list_edit(&list, config_get(&c, "editor"), argv[1]); } else { - printf("Error: Unknown command or note name '%s'.\n", argv[1]); - return 1; + int err = note_list_edit(&list, config_get(&c, "editor"), argv[1]); + if(err == 1) { + printf("%s is also not a valid command.\n", argv[1]); + return 1; + } } - // DANGER WILL ROBINSON!!! - // Clean up note_list_free(&list); config_free(&c); diff --git a/src/note.c b/src/note.c index 9f5c357..3be4a9a 100644 --- a/src/note.c +++ b/src/note.c @@ -19,9 +19,9 @@ /** * Creates a new empty note object since no filename was specified. */ -void note_new(note_t* note, char* path) { - strcpy(note->path, path); - basename(path, note->name); +void note_new(note* n, char* path) { + strcpy(n->path, path); + basename(path, n->name); } @@ -32,24 +32,8 @@ void note_new(note_t* note, char* path) { * * @return int Success or failure of the command */ -int note_create(note_t* note, char* editor) { - return note_edit(note, editor); -} - - -/** - * Opens a note using the specified editor - * - * @param string editor Path to the editor binary to use for editing. - * - * @return int Success or failure of the command - */ -int note_edit(note_t* note, char* editor) { - char cmd[strlen(editor) + 3 + strlen(note->path)]; - strcpy(cmd, editor); - strcat(cmd, " "); - strcat(cmd, note->path); - return system(cmd); +int note_create(note* n, char* editor) { + return file_edit(n->path, editor); } @@ -60,8 +44,8 @@ int note_edit(note_t* note, char* editor) { * 0 Success * -1 File could not be opened */ -int note_cat(note_t* note) { - FILE* f = fopen(note->path, "r"); +int note_cat(note* n) { + FILE* f = fopen(n->path, "r"); // Return early if the file could not be opened if(!f) { return -1; } @@ -85,41 +69,32 @@ int note_cat(note_t* note) { * * @return int Success or failure */ -int note_rm(note_t* note) { +int note_rm(note* n) { // Verify file exists. - FILE* f = fopen(note->path, "r"); + FILE* f = fopen(n->path, "r"); if(f) { - return remove(note->path); + return remove(n->path); fclose(f); } else { return 1; } } -/** - * Gets the filename of the current note instance without its extension. - * - * @return string Note filename without extension - */ -// string note::friendly_name() { -// return name.substr( 0, name.length() - extension.length() - 1 ); -// } /** * TODO */ -int note_search(note_t* note, char* term, int case_insensitive) { - FILE* f = fopen(note->path, "r"); +int note_search(note* n, char* term, int case_insensitive) { + FILE* f = fopen(n->path, "r"); char buf[file_width]; - int line_num = 0; + int line_num = 1; while((fgets(buf, file_width, f)) != NULL) { if(str_contains(buf, term, case_insensitive) == 1) { - printf("%s: %02d: %s", note->name, line_num, buf); + printf("%s: % 4d: %s", n->name, line_num, buf); } line_num++; } fclose(f); return 0; } - diff --git a/src/note.h b/src/note.h index 811ffe3..bb2a9f7 100644 --- a/src/note.h +++ b/src/note.h @@ -17,39 +17,30 @@ #include #include #include + #include "common.h" -//#include "path.h" #ifndef noteless_note_h #define noteless_note_h #define file_width 256 -typedef struct note { +typedef struct { char extension[32]; char path[256]; char name[256]; -} note_t; - -void note_new(note_t*, char*); - -int note_create(note_t*, char*); +} note; -int note_edit(note_t*, char*); +void note_new(note*, char*); -int note_cat(note_t*); +int note_create(note*, char*); -int note_rm(note_t*); +int note_edit(note*, char*); -int note_search(note_t*, char*, int); +int note_cat(note*); +int note_rm(note*); - -/** - * C++ code - */ -//vector body; -//string friendly_name(); -//string get_fqp(); +int note_search(note*, char*, int); #endif diff --git a/src/note_list.c b/src/note_list.c index ed277ad..19727bb 100644 --- a/src/note_list.c +++ b/src/note_list.c @@ -19,241 +19,155 @@ /** * Constructor * - * @param list List to be instantiated + * @param list List stream to be instantiated * @param path Path to the treasure... er... notes * @param ext Extension of the notes to pay attention to */ -void note_list_new(note_list_t* list, char* path, char* ext) { - list->count = 0; - strcpy(list->extension, ext); +int note_list_new(note_list* list, char* path, char* ext) { + list->cursor = 0; + // Open the dir fd + list->dirp = opendir(path); + list->noteent = malloc(sizeof(note)); strcpy(list->path, path); + strcpy(list->ext, ext); - DIR* d = opendir(path); - struct dirent* ent; + // Skip the first two, as they are always . and .. + readdir(list->dirp); + readdir(list->dirp); - int ext_len = strlen(ext); - - // First iterration to get a matching file count - while((ent = readdir(d))) { - // The start index of the extension in the current filename - int ext_start = strlen(ent->d_name) - ext_len; - - if(strncmp(&ent->d_name[ext_start], ext, ext_len) == 0) { - list->count++; - } - } + return 0; +} - // Create name list of previously discovered size - list->names = malloc(sizeof(char*) * list->count); - rewinddir(d); - int i = 0; - // Second iterration for populating the file list - while((ent = readdir(d))) { - // The start index of the extension in the current filename - int ext_start = strlen(ent->d_name) - ext_len; +void note_list_free(note_list* list) { + closedir(list->dirp); +} - // If the current file's extension matches the list extension... - if(strncmp(&ent->d_name[ext_start], ext, ext_len) == 0) { - // Allocate space for the entire filename (we'll strip the extension off - // as needed later). This will make calculating path lengths much - // simpler. - list->names[i] = malloc(sizeof(char) * (strlen(ent->d_name) + 1)); - // Copy the filename into the struct - strcpy(list->names[i], ent->d_name); +note* note_list_read(note_list* list) { + struct dirent* de; + char fext[64]; - i++; - } - } + // Iterrate over dir entities + while(1) { + // Return null if readdir also returns null + if((de = readdir(list->dirp)) == NULL) { return NULL; } + // Skip any non-regular files (DT_REG == 8) + if(de->d_type != 8) { continue; } + // Skip hidden files + if(de->d_name[0] == '.') { continue; } - closedir(d); -} + // Skip files that don't match the specified extension + get_extension(de->d_name, fext); + if(strcmp(fext, list->ext) != 0) { continue; } - -void note_list_free(note_list_t* list) { - for(int i = 0; i < list->count; i++) { - free(list->names[i]); + break; } - free(list->names); -} - -// void note_list::get_notes( string base, string ext, string sub ) { -// // Dir pointer -// DIR* dp; -// -// string search_dir = base; -// // Append the subdir if necessary -// if( sub != "" ) { -// // Open the subdir if it's specified -// search_dir = base + "/" + sub; -// } -// dp = opendir( search_dir.c_str() ); -// -// if( dp ) { -// // Prepare to iterrate through dir items -// struct dirent* item; -// while( item = readdir( dp ) ) { -// string name = item->d_name; -// -// path p( base + "/" + sub + "/" + name ); -// -// if( name[0] != '.' ) { -// if( p.is_dir() == 1 ) { -// // TODO -// // I really hate the way I'm doing this repeatedly. Going to need a -// // better way. -// if( sub != "" ) { -// get_notes( base, ext, sub + "/" + name ); -// } else { -// get_notes( base, ext, name ); -// } -// } else { -// // This will be replaced later with path.join() when it's implemented -// if( sub != "" ) { -// add( base, sub + "/" + name ); -// } else { -// add( base, name ); -// } -// } -// } -// } -// closedir( dp ); -// } -// } + // Copy in the path + sprintf(list->noteent->path, "%s/%s", list->path, de->d_name); + // Copy in the extension + strcpy(list->noteent->extension, fext); + // Copy in the filename + strcpy(list->noteent->name, de->d_name); + // Overwrite . with null byte + list->noteent->name[strlen(de->d_name) - strlen(fext) - 1] = '\0'; + + // Increment the file counter + ++list->cursor; + return list->noteent; +} /** - * Adds a new note item to the list by path. - * - * @param string path Path to the note to be added to the list + * Opens the specified note for editing * - * @return 0 + * @param list Note list the note-to-be-edited belongs to + * @param editor Path to the editor to use for editing + * @param term Search term */ -// int note_list::add( string base, string name ) { -// note n( base, name ); -// notes.push_back( n ); -// return 0; -// } - +int note_list_edit(note_list* list, char* editor, char* term) { + note* n; -/** - * Opens a new note for editing - * - * @param string editor Path to the editor to use for editing - * @param string note_name Name of the note to be created and edited - * - * @return int Success or failure (always success for now) - */ -int note_list_create_note(note_list_t* list, char* editor, char* note_name) { - char tmp_note_name[64]; - strcpy(tmp_note_name, note_name); - strcat(tmp_note_name, "."); - strcat(tmp_note_name, list->extension); + while(1) { + n = note_list_read(list); + if(n == NULL) { break; } - // Check if note already exists - for(int i = 0; i < list->count; i++) { - if(strcmp(list->names[i], tmp_note_name) == 0) { - return 1; + if(strncmp(n->name, term, strlen(term)) == 0) { + file_edit(n->path, editor); + return 0; } } - // Construct full note path - char note_path[256]; - strcpy(note_path, list->path); - strcat(note_path, "/"); - strcat(note_path, tmp_note_name); - - note_t note; - note_new(¬e, note_path); - note_edit(¬e, editor); - - return 0; + printf("No notes exist that match the search term '%s'.\n", term); + return 1; } /** - * Opens the specified note for editing + * Opens a new note for editing * - * @param list Note list the note-to-be-edited belongs to - * @param editor Path to the editor to use for editing - * @param note_name Name of the note to be edited + * @param string editor Path to the editor to use for editing + * @param string note_name Name of the note to be created and edited + * + * @return int Success or failure (always success for now) */ -int note_list_edit(note_list_t* list, char* editor, char* note_name) { - int id = note_list_get_note_id(list, note_name); - if(id != -1) { - // Construct the note path - // It's safe to assume 256 here since various other path variables also - // limit to 256. - char path[256]; - strcpy(path, list->path); - strcat(path, "/"); - strcat(path, list->names[id]); +int note_list_create_note(note_list* list, char* editor, char* name) { + char fullname[128]; + char fullpath[256]; + note* n; - note_t note; - note_new(¬e, path); - note_edit(¬e, editor); - } else { - printf("Note '%s' does not exist.\n", note_name); - return 1; + // Create full file name + sprintf(fullname, "%s.%s", name, list->ext); + + // Check if note already exists + while((n = note_list_read(list)) != NULL) { + if(strcmp(n->name, fullname) == 0) { return 1; } } + + // Construct full note path for the new note + sprintf(fullpath, "%s/%s", list->path, fullname); + file_edit(fullpath, editor); return 0; } /** - * Searches all note *names* (friendly and unfriendly) for the given name and - * returns the note id (not necissarily persistent). If note does not exist, - * returns -1. Can be used for a sort of "note_exists" method as such. - * - * Note that this does a partial comparison, so only the number of characters - * passed for note_name is the number compared, thus allowing for fuzzy-ish - * name matching. This will return the id of the first matched note name. + * Returns the contents of the requested note * - * @param list Note list to search for the given note [partial] name - * @param note_name Name of the note to search for + * @param string note_name Name of the note to get the contents for * - * @return int The integer of the note. -1 if note does not exist + * @return vector Contents of the note, broken per line into a vector. */ -int note_list_get_note_id(note_list_t* list, char* note_name) { - for(int i = 0; i < list->count; i++) { - if(strncmp(list->names[i], note_name, strlen(note_name)) == 0) { - return i; +int note_list_cat_note(note_list* list, char* term) { + note* n; + // Check for the requested note + while((n = note_list_read(list)) != NULL) { + if(strncmp(n->name, term, strlen(term)) == 0) { + note_cat(n); + return 0; } } - return -1; + return 1; } /** - * Returns the contents of the requested note - * - * @param string note_name Name of the note to get the contents for - * - * @return vector Contents of the note, broken per line into a vector. + * Searches each note in the list for the given search term. + * + * @return int Number of instances found in all notes + * TODO: This needs to be written still */ -int note_list_cat_note(note_list_t* list, char* note_name) { - // Check for the requested note - int id = note_list_get_note_id(list, note_name); - if(id != -1) { - // Construct the full note path - char path[strlen(list->path) + strlen(list->names[id]) + 3]; - strcpy(path, list->path); - strcat(path, "/"); - strcat(path, list->names[id]); - - note_t note; - note_new(¬e, path); +int note_list_search(note_list* list, char* term, int case_insensitive) { + note* n; - // Read in the note's contents - note_cat(¬e); - } else { - return 1; + while((n = note_list_read(list)) != NULL) { + note_search(n, term, case_insensitive); } return 0; } + /** * Deletes the specified note * @@ -261,45 +175,14 @@ int note_list_cat_note(note_list_t* list, char* note_name) { * * @return int Exit code (1 = error, 0 = success) */ -int note_list_rm_note(note_list_t* list, char* note_name) { - int id = note_list_get_note_id(list, note_name); - if(id == -1) { - printf("Error: There is no note matching \"%s\".\n", note_name); - return 1; - } else { - char note_path[256] = ""; - strcpy(note_path, list->path); - strcat(note_path, "/"); - strcat(note_path, list->names[id]); +int note_list_rm_note(note_list* list, char* name) { + note* n; - note_t note; - note_new(¬e, note_path); - return note_rm(¬e); + while((n = note_list_read(list)) != NULL) { + if(strncmp(n->name, name, strlen(name)) == 0) { + return note_rm(n); + } } - return 0; + return 1; } - -/** - * Searches each note in the list for the given search term. - * - * @return int Number of instances found in all notes - * TODO: This needs to be written still - */ -int note_list_search(note_list_t* list, char* term, int case_insensitive) { - for(int i = 0; i < list->count; i++) { - // TODO: My gosh this section of code needs to be turned into a function. - // I've written this far too many times. - // Construct note path - char path[256] = ""; - strcpy(path, list->path); - strcat(path, "/"); - strcat(path, list->names[i]); - - note_t note; - note_new(¬e, path); - - note_search(¬e, term, case_insensitive); - } - return 0; -} diff --git a/src/note_list.h b/src/note_list.h index d393a80..524e689 100644 --- a/src/note_list.h +++ b/src/note_list.h @@ -18,32 +18,42 @@ #include #include #include + +#include "common.h" #include "note.h" #ifndef noteless_note_list_h #define noteless_note_list_h -typedef struct note_list { - char path[256]; - char extension[32]; - char** names; - int count; -} note_list_t; +typedef struct { + DIR* dirp; + note* noteent; + char path[256]; + char ext[64]; + int cursor; +} note_list; + +//typedef struct note_list{ +// DIR* dirp; +// char path[256]; +// char ext[64]; +// int cursor; +//} note_list; -void note_list_new(note_list_t*, char*, char*); +int note_list_new(note_list*, char*, char*); -void note_list_free(note_list_t*); +void note_list_free(note_list*); -int note_list_edit(note_list_t*, char*, char*); +note* note_list_read(note_list*); -int note_list_get_note_id(note_list_t*, char*); +int note_list_edit(note_list*, char*, char*); -int note_list_cat_note(note_list_t*, char*); +int note_list_create_note(note_list*, char*, char*); -int note_list_create_note(note_list_t*, char*, char*); +int note_list_cat_note(note_list*, char*); -int note_list_rm_note(note_list_t*, char*); +int note_list_search(note_list*, char*, int); -int note_list_search(note_list_t*, char*, int); +int note_list_rm_note(note_list*, char*); #endif -- cgit v1.2.3