diff options
author | Aaron Ball <nullspoon@iohq.net> | 2015-02-20 13:56:02 -0700 |
---|---|---|
committer | Aaron Ball <nullspoon@iohq.net> | 2015-02-20 13:56:02 -0700 |
commit | cb7c1c8994e0990de338ea789332e347ac14d3eb (patch) | |
tree | fadd3fbc0ad552e2327cc4e0cb8e4f90be491e27 | |
parent | 44d8d50ddf14a6741328a7651e0a5deafc0fa17d (diff) | |
download | noteless-cb7c1c8994e0990de338ea789332e347ac14d3eb.tar.gz noteless-cb7c1c8994e0990de338ea789332e347ac14d3eb.tar.xz |
Implemented config parsing
Created config struct and complimentary functions. Can check if a config
variable is set and can get values along with a variable count. Also
successfully handles blank (or empty) lines as well as lines that are commented
out and lines that have inline comments.
Updated main calls to old c++ config class to work with the new c struct and
related functions.
Moved get_user_editor function into common.c and created appropriate header
file.
-rw-r--r-- | Makefile | 10 | ||||
-rw-r--r-- | src/common.c | 34 | ||||
-rw-r--r-- | src/common.h | 26 | ||||
-rw-r--r-- | src/config.c | 269 | ||||
-rw-r--r-- | src/config.h | 50 | ||||
-rw-r--r-- | src/main.c | 283 |
6 files changed, 446 insertions, 226 deletions
@@ -6,11 +6,13 @@ cc = cc all: if [[ ! -d obj ]]; then mkdir obj; fi - $(cc) $(dbg) $(warnings) -std=$(std) -c src/path.c -o $(obj)path.o + $(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 - $(cc) $(dbg) $(warnings) -std=$(std) -c src/note.c -o $(obj)note.o - $(cc) $(dbg) $(warnings) -std=$(std) -c src/note_list.c -o $(obj)note_list.o - $(cc) $(dbg) $(warnings) -std=$(std) src/main.c $(obj)*.o -o $(out) + # $(cc) $(dbg) $(warnings) -std=$(std) -c src/note.c -o $(obj)note.o + # $(cc) $(dbg) $(warnings) -std=$(std) -c src/note_list.c -o $(obj)note_list.o + # $(cc) $(dbg) $(warnings) -std=$(std) src/main.c $(obj)*.o -o $(out) + $(cc) $(dbg) $(warnings) -std=$(std) src/main.c $(obj)/* -o $(out) debug: make all dbg="-g" diff --git a/src/common.c b/src/common.c new file mode 100644 index 0000000..ad40025 --- /dev/null +++ b/src/common.c @@ -0,0 +1,34 @@ +/** + * Copyright (C) 2015 Aaron Ball <nullspoon@iohq.net> + * + * 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 <http://www.gnu.org/licenses/>. + */ +#include "common.h" + +/** + * Determins the text editor that should be used. Checks the environmental + * variable EDITOR for this. + * + * @return char* Path to the editor binary to be used (stored in heap) + */ +void get_user_editor(char* editor) { + if(getenv("EDITOR") != NULL) { + /* If the EDITOR variable is set, use that */ + //int editor_len = strlen(getenv("EDITOR")); + // *editor = calloc(editor_len, sizeof(char)); + strcpy(editor, getenv("EDITOR")); + } else { + strcpy(editor, "vi"); + } +} diff --git a/src/common.h b/src/common.h new file mode 100644 index 0000000..b11fd8c --- /dev/null +++ b/src/common.h @@ -0,0 +1,26 @@ +/** + * Copyright (C) 2015 Aaron Ball <nullspoon@iohq.net> + * + * 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 <http://www.gnu.org/licenses/>. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifndef noteless_common +#define noteless_common + +void get_user_editor(char*); + +#endif diff --git a/src/config.c b/src/config.c index dfb78ca..631b580 100644 --- a/src/config.c +++ b/src/config.c @@ -24,83 +24,222 @@ * * @param const char* p Path to the config file to be read in */ -config::config( const char* p ) { - path = p; +int config_new(config_t* conf, char* p) { + // TODO + strcpy(conf->path, p); - vector<string> lines; - ifstream fs( path ); + conf->count = config_valid_line_count(p); - // Verify the file handle opened sucessfully - if( ! fs.is_open() ) { - cout << "Could not open file at " << path << "." << endl; - exit( 1 ); + conf->keys = calloc(conf->count, sizeof(char*)); + conf->values = calloc(conf->count, sizeof(char*)); + + FILE* f; + f = fopen(conf->path, "r"); + + // Verify the file could be opened + if(!f) { + printf("Could not open config file at %s.\n", conf->path); + return -1; } - // Continue - string line; - while( getline( fs, line ) ) { - parse_line( line ); + + // Set the maximum read width and read buffer array + int len = 120; + char buf[len]; + + char key[32]; + char value[256]; + + int i = 0; + while(fgets(buf, len, f) != NULL) { + int start = 0; + // This loop allows for lines to not start at the beginning + while(buf[start] != '\0') { + if(buf[start] != ' ') { + break; + } else { + start++; + } + } + + // Skip all lines starting with a comment char + if(buf[start] == '#') { continue; } + // Skip empty lines + if(buf[start] == '\n') { continue; } + + int end = start; + while(buf[end] != '\0') { + // This allows for inline comment chars. The line ends when the first + // comment char is found. + if(buf[end] == '#' || buf[end] == '\n') { break; } + end++; + } + + char tmp_line[end - start + 10]; + // Temp variable to store the text between index start and index end + strncpy(tmp_line, &buf[start], end - start + 1); + // Make sure it has a trailing newline so sscanf works + strcat(tmp_line, "\n"); + + // Parse the line into temp variables + sscanf(tmp_line, "%s %s", key, value); + + // Allocate heap memory for values since these need to persist. + // Note that here we are using the temp variables to establish string + // length for when we allocate heap memory. + conf->keys[i] = malloc(sizeof(char) * strlen(key)); + conf->values[i] = malloc(sizeof(char) * strlen(value)); + + // Copy in the temp values + strcpy(conf->keys[i], key); + strcpy(conf->values[i], value); + + // Increment the counter for tracking keys and values in their respective + // arrays + i++; } - fs.close(); + + fclose(f); + + return 0; } + /** - * Parses one line of the config file. Strips illegal characters first, then - * sets key to the text on the left of the equals sign and value to the text on - * the right. - * - * string line - Config file line to be parsed and added to the config object + * Destructor for config struct. * - * return int Success or failure + * @param conf Config_t pointer to be freed from memory */ -int config::parse_line( string line ) { - // The two vars that will be appended to the key value vector - string key; - string value; - int split; - - // Clean spaces and other illegal chars - string clean_line; - for( int i = 0; i < line.length(); i++ ) { - if( line[i] == ' ' ) { - // Ignore, it's a space - continue; - } else { - clean_line += line[i]; - } +void config_free(config_t* conf) { + for(int i = 0; i < conf->count; i++) { + free(conf->keys[i]); + free(conf->values[i]); } + free(conf->keys); + free(conf->values); +} - // Line was empty, skip 'er - if( clean_line.length() == 0 ) { return 0; } - // One character at a time - for( int i = 0; i < clean_line.length(); i++ ) { - if( clean_line[i] == '=' ) { - // Set the key - key = clean_line.substr( 0, i ); - split = i; - break; - } +/** + * Performs a line count of valid lines in the specified config file. This is + * useful for determining the size of an array to store keys or values in. + * A valid line is considered to be a line that is not empty or commented out. + * + * @param path Path to the config file to be parsed + * + * @return int Count of lines that are not commented out or empty + */ +int config_valid_line_count(char* path) { + int count = 0; + + FILE* f; + f = fopen(path, "r"); + + int len = 120; + char buf[len]; + + while(fgets(buf, len, f) != NULL) { + int start = 0; + + // Find the first char + while(buf[start] == ' ') { start++; } + + // Confirm the line is not a comment or empty + if(buf[start] != '#' && buf[start] != '\n') { count++; } } - // Set the value - value = clean_line.substr( split + 1, clean_line.length() ); + fclose(f); + + return count; +} - // Append to class global values - values.push_back( key ); - values.push_back( value ); - // Success! +/** + * Checks the configuration struct for the given key's value. + * + * @param conf Config_t instance pointer containing keys and values to check + * @param key Key to check if exists + * + * @return int Key value exists (1) or not (0) + */ +int config_isset(config_t* conf, char* key) { + for(int i = 0; i < conf->count; i++) { + if(strcmp(conf->keys[i], key) == 0) { + return 1; + } + } return 0; } -bool config::isset( string key ) { - for( int i = 0; i < values.size(); i+=2 ) { - if( values[i] == key ) { - return true; +/** + * TODO + */ +char* config_get(config_t* conf, char* key) { + for(int i = 0; i < conf->count; i++) { + if(strcmp(conf->keys[i], key) == 0) { + return conf->values[i]; } } - return false; + return NULL; } + +/** + * Parses one line of the config file. Strips illegal characters first, then + * sets key to the text on the left of the equals sign and value to the text on + * the right. + * + * string line - Config file line to be parsed and added to the config object + * + * return int Success or failure + */ +// int config::parse_line( string line ) { +// // The two vars that will be appended to the key value vector +// string key; +// string value; +// int split; +// +// // Clean spaces and other illegal chars +// string clean_line; +// for( int i = 0; i < line.length(); i++ ) { +// if( line[i] == ' ' ) { +// // Ignore, it's a space +// continue; +// } else { +// clean_line += line[i]; +// } +// } +// +// // Line was empty, skip 'er +// if( clean_line.length() == 0 ) { return 0; } +// +// // One character at a time +// for( int i = 0; i < clean_line.length(); i++ ) { +// if( clean_line[i] == '=' ) { +// // Set the key +// key = clean_line.substr( 0, i ); +// split = i; +// break; +// } +// } +// // Set the value +// value = clean_line.substr( split + 1, clean_line.length() ); +// +// // Append to class global values +// values.push_back( key ); +// values.push_back( value ); +// +// // Success! +// return 0; +// } + +// bool config::isset( string key ) { +// for( int i = 0; i < values.size(); i+=2 ) { +// if( values[i] == key ) { +// return true; +// } +// } +// return false; +// } + /** * Returns the value for the specified key * @@ -108,13 +247,13 @@ bool config::isset( string key ) { * * return string The value of the specified key */ -string config::get( string key ) { - // Hop skip and jump - // Evens are keys, odds are values (even + 1) - for( int i = 0; i < values.size(); i+=2 ) { - if( values[i] == key ) { - return values[i+1]; - } - } - return "Unknown"; -} +// string config::get( string key ) { +// // Hop skip and jump +// // Evens are keys, odds are values (even + 1) +// for( int i = 0; i < values.size(); i+=2 ) { +// if( values[i] == key ) { +// return values[i+1]; +// } +// } +// return "Unknown"; +// } diff --git a/src/config.h b/src/config.h index 8a52bd7..89d1bdd 100644 --- a/src/config.h +++ b/src/config.h @@ -16,35 +16,37 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -using namespace std; +#ifndef NOTELESS_CONFIG_H +#define NOTELESS_CONFIG_H + #include <stdlib.h> -#include <iostream> -#include <vector> -#include <string> -#include <fstream> -#ifndef LIB_CONFIG_H -#define LIB_CONFIG_H +#include <stdio.h> +#include <string.h> /** * Represents a config file in memory. Provides a friendly interface for * checking configuration variables without having to parse multiple times. */ -class config { -public: - // Class global variables - vector<string> values; - const char* path; - - // Constructors - config( const char* ); - - // Public helper methods - string get( string ); - bool isset( string ); - -private: - // Private helper methods - int parse_line( string ); -}; +typedef struct config { + int count; + char path[256]; + char note_path[256]; + char extension[16]; + char** keys; + char** values; +} config_t; + +int config_new(config_t*, char*); +void config_free(config_t*); + +int config_isset(config_t*, char*); + +char* config_get(config_t*, char*); + +int config_valid_line_count(char*); + +// get(char*, char*); +// int isset(char*); +// int parse_line(char*); #endif @@ -14,16 +14,17 @@ * You should have received a copy of the GNU General Public License * along with noteless. If not, see <http://www.gnu.org/licenses/>. */ -using namespace std; #include <stdlib.h> +#include <stdio.h> #include <string.h> +#include "common.h" #include "config.h" -#include "note.h" -#include "note_list.h" -#include "path.h" +// #include "note.h" +// #include "note_list.h" +// #include "path.h" int get_help() { - string out = "\nNoteless provides a simple command line interface for note " + char out[] = "\nNoteless provides a simple command line interface for note " "taking.\n\n" "What makes this different than simply using a command line text editor is\n" "there is no need to type paths or file extensions. It handles all of the\n" @@ -38,154 +39,170 @@ int get_help() { " given search term.\n" " help Displays this help text\n" " ls,list Lists all notes in note directory.\n"; - cout << out << endl; + printf("%s\n", out); return 0; } /** * Prints names of all notes */ -void list_notes( note_list list ) { - // List notes - vector<string> names = list.enum_names(); - cout << endl; - for( int i = 0; i < names.size(); i++ ) { - cout << "* " << names[i] << endl; - } - cout << endl; -} - -/** - * Searches all note contents for the specified text - */ -int search_notes( note_list list, string term ) { - vector<string> matches = list.find( true, term ); - for( int i = 0; i < matches.size(); i++ ) { - cout << matches[i] << endl; - } - if( matches.size() == 0 ) { - return 1; - } - return 0; -} - -int cat_note( note_list list, string name ) { - vector<string> body; - if( list.cat_note( name, &body ) == 0 ) { - // If list returns true, the note exists - for( int i = 0; i < body.size(); i++ ) { - cout << body[i] << endl; - } - } else { - // List cat_note returned false. Note doesn't exist - cout << "Note " << name << " could not be found." << endl; - return 1; - } - return 0; -} - -int main( int argc, char** argv ) { +// void list_notes( note_list list ) { +// // List notes +// vector<string> names = list.enum_names(); +// cout << endl; +// for( int i = 0; i < names.size(); i++ ) { +// cout << "* " << names[i] << endl; +// } +// cout << endl; +// } +// +// /** +// * Searches all note contents for the specified text +// */ +// int search_notes( note_list list, string term ) { +// vector<string> matches = list.find( true, term ); +// for( int i = 0; i < matches.size(); i++ ) { +// cout << matches[i] << endl; +// } +// if( matches.size() == 0 ) { +// return 1; +// } +// return 0; +// } +// +// int cat_note( note_list list, string name ) { +// vector<string> body; +// if( list.cat_note( name, &body ) == 0 ) { +// // If list returns true, the note exists +// for( int i = 0; i < body.size(); i++ ) { +// cout << body[i] << endl; +// } +// } else { +// // List cat_note returned false. Note doesn't exist +// cout << "Note " << name << " could not be found." << endl; +// return 1; +// } +// return 0; +// } + +int main(int argc, char* argv[]) { /** - * Config variables definitions + * Config variables */ - // Default path to the note store - string note_path = getenv( "HOME" ); - note_path += "/Documents/Notes"; + char home_path[strlen(getenv("HOME"))]; + strcpy(home_path, getenv("HOME")); - // Default note extension - string note_ext = "mdown"; + /* Default path to the note store */ + char* note_path = NULL; - // A little editor detection - string editor; - if( getenv( "EDITOR" ) ) { - editor = getenv( "EDITOR" ); - } else { - editor = "vi"; - } + /* Default note extension */ + char* note_ext = "mdown"; + + /* A little editor detection */ + char* editor = NULL; /** * Config file overrides */ - string conf_path = getenv( "HOME" ); - conf_path += "/.config/noteless.conf"; - path pconf( conf_path ); - config conf(); - if( pconf.exists() ) { - config conf( conf_path.c_str() ); - - // Override where notes are to be stored - if( conf.isset( "path" ) ) { - note_path = conf.get( "path" ); - } - - // Override the extension used by the notes - if( conf.isset( "extension" ) ) { - note_ext = conf.get( "extension" ); - } - - // Override editor settings - if( conf.isset( "editor" ) ) { - editor = conf.get( "editor" ); - } - } + char conf_path[strlen(home_path) + 23]; + strcpy(conf_path, home_path); + strcat(conf_path, "/.config/noteless.conf"); - if( argc == 1 ) { - cout << "\nNo command specified. Printing help text.\n" << endl; - return get_help(); - } + config_t c; + config_new(&c, conf_path); + + // Override where notes are to be stored + // The local version of note path, in case it isn't set in the config + char tmp_note_path[strlen(home_path) + 17]; - // 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(); + if(config_isset(&c, "path") == 1) { + note_path = config_get(&c, "path"); + } else { + strcpy(tmp_note_path, home_path); + strcat(tmp_note_path, "/Documents/Notes"); + note_path = tmp_note_path; } - // 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 " - "'[1mnoteless init[0m' to\n" - "create the note store here.\n\n" - "Otherwise, please verify the path variable in your configuration."; - cout << out << endl; - return 1; + // Override the extension used by the notes + if(config_isset(&c, "extension")) { + note_ext = config_get(&c, "extension"); } - // Create new list - note_list list( note_path, note_ext ); - - for( int i = 1; i < argc; i++ ) { - string arg = argv[i]; - if( arg == "ls" || arg == "list" ) { - list_notes( list ); - } else if( arg == "new" ) { - string name = argv[i + 1]; - return list.create( editor, name ); - } else if( arg == "edit" ) { - string name = argv[i + 1]; - return list.edit( editor, name ); - } else if( arg == "rm" ) { - string name = argv[i + 1]; - return list.rm( name ); - } else if( arg == "find" ) { - string term = argv[i + 1]; - return search_notes( list, term ); - } else if( arg == "cat" ) { - string name = argv[i + 1]; - return cat_note( list, name ); - } else if( arg == "help" ) { - return get_help(); - } else if( list.find_note_id( arg ) != -1 ) { - // Try to open the note if it exists - return list.edit( editor, arg ); - } else { - cout << "Error: Unknown command or note name '" << arg << "'." << endl; - return 1; - } + // Override editor settings + char tmp_editor[128]; + if(config_isset(&c, "editor")) { + editor = config_get(&c, "editor"); + } else { + get_user_editor(tmp_editor); + editor = tmp_editor; } + + + // Verbose variable outputs + printf("Note Path: %s\n", note_path); + printf("Note Ext: %s\n", note_ext); + printf("Conf Path: %s\n", conf_path); + printf("Editor: %s\n", editor); + + config_free(&c); + + // if( argc == 1 ) { + // cout << "\nNo command specified. Printing help text.\n" << endl; + // return get_help(); + // } + + // // 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 " + // "'[1mnoteless init[0m' to\n" + // "create the note store here.\n\n" + // "Otherwise, please verify the path variable in your configuration."; + // cout << out << endl; + // return 1; + // } + + // // Create new list + // note_list list( note_path, note_ext ); + + // for( int i = 1; i < argc; i++ ) { + // string arg = argv[i]; + // if( arg == "ls" || arg == "list" ) { + // list_notes( list ); + // } else if( arg == "new" ) { + // string name = argv[i + 1]; + // return list.create( editor, name ); + // } else if( arg == "edit" ) { + // string name = argv[i + 1]; + // return list.edit( editor, name ); + // } else if( arg == "rm" ) { + // string name = argv[i + 1]; + // return list.rm( name ); + // } else if( arg == "find" ) { + // string term = argv[i + 1]; + // return search_notes( list, term ); + // } else if( arg == "cat" ) { + // string name = argv[i + 1]; + // return cat_note( list, name ); + // } else if( arg == "help" ) { + // return get_help(); + // } else if( list.find_note_id( arg ) != -1 ) { + // // Try to open the note if it exists + // return list.edit( editor, arg ); + // } else { + // cout << "Error: Unknown command or note name '" << arg << "'." << endl; + // return 1; + // } + // } return 0; } |