diff --git a/src/config.hpp b/src/config.hpp index 0c6920f..1de2e1a 100644 --- a/src/config.hpp +++ b/src/config.hpp @@ -10,4 +10,4 @@ * Set to 1 to compile and run the test suite */ -#define _TESTS_ 1 +#define _TESTS_ 0 diff --git a/src/main.cpp b/src/main.cpp index f0586c5..52d974a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -32,11 +32,16 @@ int main(int argc, char** argv) { if(!feed.isOk()) exit(-1); - //display requested attributes + + //display requested attributes and items std::string output = rss_utils::rss_to_list(feed, opts); - std::cout << output << std::endl; + if(opts->items != nullptr){ + feed.getItems(); + output += "\n" + rss_utils::rss_to_items(feed, opts); + } + std::cout << output << std::endl; delete opts; return 0; diff --git a/src/options.cpp b/src/options.cpp index f3c8f8a..6997946 100644 --- a/src/options.cpp +++ b/src/options.cpp @@ -7,29 +7,49 @@ #include "options.hpp" void help(char* progName){ - std::cout << "Usage: " << progName << " [-u FEED_URI]\n"; + std::cout << "Usage: " << progName << " [-u FEED_URI] [CHANNEL FLAGS] "; + std::cout << "[-i ITEM_INDEX] [ITEM FLAGS]\n"; std::cout << "Options:\n"; std::cout << "Required Options:\n"; - std::cout << " [-u, --uri] URI of the rss stream\n\n"; + std::cout << " [-u, --uri] URI URI of the rss stream\n\n"; std::cout << "Channel information:\n"; std::cout << " [-t, --title] Get title of channel\n"; std::cout << " [-l, --link] Get link to channel\n"; - std::cout << " [-d, --description] Get Description of channel\n"; + std::cout << " [-d, --description] Get description of channel\n"; std::cout << " [-e, --language] Get language code of channel\n"; std::cout << " [-m, --webmaster] Get webMaster's email\n"; std::cout << " [-c, --copyright] Get copyright\n"; std::cout << " [-p, --pubdate] Get publishing date\n"; - std::cout << " [-q, --managingeditor] Get Managing Editor\n"; + std::cout << " [-q, --managingeditor] Get managing editor\n"; std::cout << " [-g, --generator] Get generator of this feed\n"; std::cout << " [-o, --docs] Get link to RSS documentation\n"; std::cout << " [-w, --ttl] Get ttl, time that channel can be\n"; std::cout << " cached before being updated\n"; std::cout << " [-b, --builddate] Get last time the channel's\n"; - std::cout << " content changed\n\n"; + std::cout << " content changed\n"; + std::cout << " [-i, --item] INDEX Provide index of item to display\n"; + std::cout << " If no index is provided, assume the first\n"; + std::cout << " item in the feed. All following flags will\n"; + std::cout << " be parsed as item options, till another\n"; + std::cout << " item is provided\n\n"; + + std::cout << "Item options:\n"; + std::cout << " [-t, --title] Get title of item\n"; + std::cout << " [-l, --link] Get link\n"; + std::cout << " [-d, --description] Get description\n"; + std::cout << " [-a, --author] Get author\n"; + std::cout << " [-z, --category] Get category list\n"; + std::cout << " [-f, --comments] Get link to comments\n"; + std::cout << " [-j, --guid] Get GUID\n"; + std::cout << " [-p, --pubdate] Get publishing date\n"; + std::cout << " [-s, --source] Get source of item\n\n"; std::cout << "General options:\n"; - std::cout << " [-h, --help] Show this message\n"; + std::cout << " [-h, --help] Show this message\n\n"; + + std::cout << "For more information, refer to the RSS 2.0 documentation\n"; + std::cout << "https://validator.w3.org/feed/docs/rss2.html\n"; exit(1); } @@ -37,8 +57,13 @@ void help(char* progName){ option_flags* parse_options(int argc, char** argv) { int option_index = 0; int c; + + item_flags* current_item = nullptr; option_flags* ret = new option_flags; + ret->item_count = 0; + ret->items = nullptr; + while(true){ c = getopt_long(argc, argv, optarg_string, long_options, &option_index); @@ -50,13 +75,22 @@ option_flags* parse_options(int argc, char** argv) { ret->uri = std::string(optarg); break; case 't': - ret->title ^= 1; + if(current_item == nullptr) + ret->title ^= 1; + else + current_item->title ^= 1; break; case 'l': - ret->link ^= 1; + if(current_item == nullptr) + ret->link ^= 1; + else + current_item->link ^= 1; break; case 'd': - ret->description ^= 1; + if(current_item == nullptr) + ret->description ^= 1; + else + current_item->description ^= 1; break; case 'e': ret->language ^= 1; @@ -68,7 +102,10 @@ option_flags* parse_options(int argc, char** argv) { ret->copyright ^= 1; break; case 'p': - ret->pubdate ^= 1; + if(current_item == nullptr) + ret->pubdate ^= 1; + else + current_item->pubdate ^= 1; break; case 'q': ret->managingeditor ^= 1; @@ -85,7 +122,85 @@ option_flags* parse_options(int argc, char** argv) { case 'b': ret->builddate ^= 1; break; + case 'i': + if(ret->items == nullptr){ + ret->items = new item_flags[ret->item_count + 1]; + current_item = &(ret->items[0]); + ret->item_count++; + current_item->index = optarg ? atoi(optarg) : 0; + } else { + item_flags* new_flags = new item_flags[ret->item_count + 1]; + for(unsigned int i=0;iitem_count;++i) + new_flags[i] = ret->items[i]; + delete ret->items; + ret->items = new_flags; + current_item = &(ret->items[ret->item_count]); + ret->item_count++; + current_item->index = optarg ? atoi(optarg) : 0; + } + break; + case 'a': + if(current_item == nullptr){ + std::cerr << "Invalid option in this context: [-a --author]" << std::endl; + std::cerr << "Did you provide the [-i --index] flag first?" << std::endl; + } else + current_item->author ^= 1; + break; + case 'z': + if(current_item == nullptr){ + std::cerr << "Invalid option in this context: [-z --category]" << std::endl; + std::cerr << "Did you provide the [-i --index] flag first?" << std::endl; + } else + current_item->category ^= 1; + break; + case 'f': + if(current_item == nullptr){ + std::cerr << "Invalid option in this context: [-f --comments]" << std::endl; + std::cerr << "Did you provide the [-i --index] flag first?" << std::endl; + } else + current_item->comments ^= 1; + break; + case 'j': + if(current_item == nullptr){ + std::cerr << "Invalid option in this context: [-j --guid]" << std::endl; + std::cerr << "Did you provide the [-i --index] flag first?" << std::endl; + } else + current_item->guid ^= 1; + break; + case 's': + if(current_item == nullptr){ + std::cerr << "Invalid option in this context: [-s --source]" << std::endl; + std::cerr << "Did you provide the [-i --index] flag first?" << std::endl; + } else + current_item->source ^= 1; + break; + case ':': //go here if flag that requires argument is passed, but no arg given + switch(optopt){ + case 'i': + if(ret->items == nullptr){ + ret->item_count++; + ret->items = new item_flags[ret->item_count]; + current_item = &(ret->items[0]); + current_item->index = 1; + } else { + item_flags* new_flags = new item_flags[ret->item_count + 1]; + for(unsigned int i=0;iitem_count;++i) + new_flags[i] = ret->items[i]; + delete ret->items; + ret->items = new_flags; + current_item = &(ret->items[ret->item_count]); + ret->item_count++; + current_item->index = 1; + } + break; + default: + std::cerr << "Invalid use: option -" << (char)optopt + << " requires an argument" << std::endl; + break; + } + break; case '?': + std::cerr << "Unknown option: " << (char)optopt << std::endl; case 'h': help(argv[0]); break; diff --git a/src/options.hpp b/src/options.hpp index 877dd4b..5d43be4 100644 --- a/src/options.hpp +++ b/src/options.hpp @@ -11,11 +11,12 @@ #include //cli options -constexpr char optarg_string[] = "u:tldemcpqgowbh"; +constexpr char optarg_string[] = ":u:tldemcpqgowbi::azfjsh"; static struct option long_options[] = { {"uri", required_argument, 0, 'u'}, + {"title", no_argument, 0, 't'}, {"link", no_argument, 0, 'l'}, {"description", no_argument, 0, 'd'}, @@ -28,10 +29,32 @@ static struct option long_options[] = {"docs", no_argument, 0, 'o'}, {"ttl", no_argument, 0, 'w'}, {"builddate", no_argument, 0, 'b'}, + + {"item", optional_argument, 0, 'i'}, + {"author", no_argument, 0, 'a'}, + {"category", no_argument, 0, 'z'}, + {"comments", no_argument, 0, 'f'}, + {"guid", no_argument, 0, 'j'}, + {"source", no_argument, 0, 's'}, + {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}, }; +struct item_flags { + unsigned int title : 1; + unsigned int link : 1; + unsigned int description : 1; + unsigned int author : 1; + unsigned int category : 1; + unsigned int comments : 1; + unsigned int guid : 1; + unsigned int pubdate : 1; + unsigned int source : 1; + + int index; +}; + struct option_flags { unsigned int title : 1; unsigned int link : 1; @@ -46,8 +69,11 @@ struct option_flags { unsigned int ttl : 1; unsigned int builddate : 1; + item_flags* items; + unsigned int item_count; std::string uri; }; + void help(char*); option_flags* parse_options(int, char**); diff --git a/src/rss.hpp b/src/rss.hpp index 96fe915..3daa9f4 100644 --- a/src/rss.hpp +++ b/src/rss.hpp @@ -15,7 +15,6 @@ #include #include #include -#include "tylers_utils.hpp" #include #include diff --git a/src/rss_out.cpp b/src/rss_out.cpp index d460f8f..783620e 100644 --- a/src/rss_out.cpp +++ b/src/rss_out.cpp @@ -11,42 +11,30 @@ using namespace rss_utils; std::string rss_utils::rss_to_list(const rss& rss_obj, const option_flags* flags){ std::string ret; - if(flags->title){ + if(flags->title) ret += rss_obj.getTitle() + "\n"; - } - if(flags->link){ + if(flags->link) ret += rss_obj.getLink() + "\n"; - } - if(flags->description){ + if(flags->description) ret += rss_obj.getDescription() + "\n"; - } - if(flags->language){ + if(flags->language) ret += rss_obj.getLanguage() + "\n"; - } - if(flags->webmaster){ + if(flags->webmaster) ret += rss_obj.getWebMaster() + "\n"; - } - if(flags->copyright){ + if(flags->copyright) ret += rss_obj.getCopyright() + "\n"; - } - if(flags->pubdate){ + if(flags->pubdate) ret += rss_obj.getPubDate() + "\n"; - } - if(flags->managingeditor){ + if(flags->managingeditor) ret += rss_obj.getManagingEditor() + "\n"; - } - if(flags->generator){ + if(flags->generator) ret += rss_obj.getGenerator() + "\n"; - } - if(flags->docs){ + if(flags->docs) ret += rss_obj.getDocs() + "\n"; - } - if(flags->ttl){ + if(flags->ttl) ret += rss_obj.getTTL() + "\n"; - } - if(flags->builddate){ + if(flags->builddate) ret += rss_obj.getLastBuildDate() + "\n"; - } if(ret.length() > 0) ret.pop_back(); @@ -55,3 +43,42 @@ std::string rss_utils::rss_to_list(const rss& rss_obj, const option_flags* flags return ret; } + +std::string rss_utils::rss_to_items(const rss& rss_obj, const option_flags* flags){ + item_flags* items = flags->items; + int maxItem = rss_obj.getItemCount(); + std::string ret; + + for(unsigned int i=0; i < flags->item_count; ++i, ++items){ + //if has a valid index + if(items->index >= 0 && items->index < maxItem){ + rss_utils::item cur_item = rss_obj[items->index]; + if(items->title) + ret += cur_item.getTitle() + "\n"; + if(items->link) + ret += cur_item.getLink() + "\n"; + if(items->description) + ret += cur_item.getDescription() + "\n"; + if(items->author) + ret += cur_item.getAuthor() + "\n"; + if(items->category) + ret += cur_item.getCategory() + "\n"; + if(items->comments) + ret += cur_item.getComments() + "\n"; + if(items->guid) + ret += cur_item.getGuid() + "\n"; + if(items->pubdate) + ret += cur_item.getPubDate() + "\n"; + if(items->source) + ret += cur_item.getSource() + "\n"; + } else + std::cerr << "Index on item " << i << " is not valid!" << std::endl; + } + + if(ret.length() > 0) + ret.pop_back(); + else + ret = ""; + + return ret; +} diff --git a/src/rss_out.hpp b/src/rss_out.hpp index a454452..18e8c77 100644 --- a/src/rss_out.hpp +++ b/src/rss_out.hpp @@ -13,6 +13,6 @@ namespace rss_utils { std::string rss_to_list(const rss&, const option_flags*); - //std::string rss_to_items(const rss& + std::string rss_to_items(const rss&, const option_flags*); }