Changeset 306

Show
Ignore:
Timestamp:
04/03/05 23:32:57 (4 years ago)
Author:
athomas
Message:

Deprecated used of time_t in favour of Time. Still working out a few kinks
though. Updated makeclass templates for new module design.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • todo2/trunk/configure.in

    r300 r306  
    103103 
    104104LIBS="${LIBS} ${LIBLTDL}" 
    105 CPPFLAGS="${CPPFLAGS} ${LTDLINCL} -Wall -DSYSCONFDIR=\\\"${sysconfdir}\\\"" 
     105CPPFLAGS="${CPPFLAGS} ${LTDLINCL} -Wall -Wold-style-cast -DSYSCONFDIR=\\\"${sysconfdir}\\\"" 
    106106 
    107107AC_CONFIG_HEADERS([src/config.h]) 
  • todo2/trunk/README

    r295 r306  
    11                                                                vim: syntax=txt 
    2 !!! devtodo2 
     2= Developer Todo 2 = 
    33 
    4 !! Modular Components 
     4== Modular Components == 
    55 
    6 ! Overview 
     6There are four modular components to DT2. 
    77 
    8 There are four modular components to devtodo2. 
    9  
    10 1. FrontEnd 
     8=== FrontEnd === 
    119 
    1210Front-ends are responsible for almost all user interaction, with the caveat 
     
    1412will be performed using the system console. 
    1513 
    16 The de-facto FrontEnd is Console, which uses colour textual output to represent  
    17 the database. There are plans for GTK and QT FrontEnds. 
     14The default frontend module is "console" which performs all user interaction via 
     15command line arguments and console input. This is similar to DT1, however the 
     16arguments have been improved and cleaned up. 
    1817 
    19 2. BackEnd 
     18There are plans for GTK and QT !FrontEnds. 
    2019 
    21 Back-ends perform all loading, locking and saving of the devtodo2 databases.  
     20=== BackEnd === 
    2221 
    23 The de-facto BackEnd is XML which stores the hierarchical database in an XML  
    24 format. I would like to have a MySQL BackEnd, and possibly an SSH-based BackEnd  
    25 for doing secure remote database changes, though I'm not exactly sure how  
    26 workable the latter is. 
     22Back-ends perform all loading, locking and saving of the DT2 databases.  
    2723 
    28 3. Filter 
     24The default backend 
     25 
     26I would like to have a MySQL !BackEnd, and possibly an SSH-based BackEnd for  
     27doing secure remote database changes, though I'm not exactly sure how workable  
     28the latter is. 
     29 
     30=== Filter === 
    2931 
    3032A set of filters are provided which perform various transformations on the  
     
    3335and filtering based on user supplied expressions. 
    3436 
    35 4. CommChannel 
     37=== ContactMethod === 
    3638 
    37 The final modular component is communication channels. When an item is  
    38 modified, fellow collaborators can be informed; the CommChannel modules handle  
    39 this communication. 
     39The final modular component is contact methods. When an item is modified,  
     40fellow collaborators can be informed; the !ContactMethod modules handle this  
     41communication. 
    4042 
    41 The default, and currently only, CommChannel is Mail which informs  
    42 interested parties via the local Unix system MDA. 
     43The default !ContactMethod is "mail" which informs interested parties via the  
     44local Unix system MDA. There is one other default !ContactMethod  
     45"uncontactable" which is the default if users are not in the address book. 
    4346 
    44 ! Command-Line Arguments 
     47== Address Book == 
    4548 
    46 Each module can register command-line arguments with devtodo2. These will show  
     49DT2 has the concept of an address book, which maps user id's (eg. athomas) to 
     50contact information. 
     51 
     52If a user does not have an address book entry a default entry will be created 
     53with "uncontactable" as their contact information. 
     54 
     55== Command-Line Arguments == 
     56 
     57Each module registers a set of command-line arguments with DT2. These will show  
    4758up in a --help and the module will be called upon to parse these arguments. 
    4859 
    49 !! Format 
     60Some useful debugging arguments are --dump-config (dump the configuration that 
     61DT2 is using), --list-modules (list all successfully loaded modules) and -D99 
     62(enable full debugging). 
    5063 
    51 devtodo databases are collections of key/value pairs, with some specific  
    52 exclusions. category, address, assigned are all mult-key values. 
     64== Database == 
     65 
     66The DT2 database consists of a hierarchy of items, and per-database users and 
     67categories. 
     68 
     69Each item in a DT2 database is a collection of key/value pairs. Some default  
     70keys are used by DT2 internally while others can be used by specific modules 
     71to perform extra logic. For example, the colour key might be used by the  
     72Console backend to override the default display colour for an item. 
    5373 
    5474All date/time values are stored as UNIX timestamps in UTC to avoid issues with  
    55 sharing data between timezones. 
     75sharing data between time zones. 
  • todo2/trunk/src/backend/.makeclass.cc

    r295 r306  
    33using namespace std; 
    44 
    5 ${CLASSNAME}::${CLASSNAME}()
     5${CLASSNAME}::${CLASSNAME}() : BackEnd("${LCLASSNAME}")
    66} 
    77 
     
    99} 
    1010 
    11 /* 
    12 bool ${CLASSNAME}::arg(crash::Args::iterator argument) { 
    13         switch (argument.option()) { 
    14         } 
    15         return false; 
     11bool ${CLASSNAME}::loadable(std::string path) { 
    1612} 
    17 */ 
     13 
     14Handle *${CLASSNAME}::prepare(std::string path) { 
     15
     16 
     17/////////////////////////// ${CLASSNAME}::Handle 
     18  
     19${CLASSNAME}::Handle::Handle(BackEnd *backend, std::string path) : BackEnd::Handle(backend, path) { 
     20
     21 
     22${CLASSNAME}::Handle::~Handle() { 
     23
     24 
     25bool ${CLASSNAME}::Handle::lock(unsigned mode) { 
     26
     27 
     28void ${CLASSNAME}::Handle::unlock() { 
     29
     30 
     31Database *${CLASSNAME}::Handle::load() { 
     32
     33 
     34void ${CLASSNAME}::Handle::save(Database *database) { 
     35
     36 
  • todo2/trunk/src/backend/.makeclass.h

    r295 r306  
    1212                SUBCLASS_MODULE_EXCEPTION(BackEnd::Exception) 
    1313 
     14                class Handle : public BackEnd::Handle { 
     15                        public : 
     16                                Handle(BackEnd *backend, std::string path); 
     17                                ~Handle(); 
     18 
     19                                bool lock(unsigned mode); 
     20                                //bool lock(Item *item, unsigned mode); 
     21                                void unlock(); 
     22                                //void unlock(Item *item); 
     23                                Database *load(); 
     24                                void save(Database *database); 
     25                        private : 
     26                }; 
     27 
    1428                ${CLASSNAME}(); 
    1529                ~${CLASSNAME}(); 
    1630 
    17                 //bool arg(crash::Args::iterator argument); 
     31                virtual bool loadable(std::string path); 
     32                virtual Handle *prepare(std::string path); 
     33 
    1834        private : 
    1935}; 
  • todo2/trunk/src/backend/XML.cc

    r305 r306  
    1818        method to report errors. */ 
    1919static void loadableErrorHandler(void *arg, const char * msg, xmlParserSeverities severity, xmlTextReaderLocatorPtr locator) { 
    20 string &s = *reinterpret_cast<string*>(arg); 
     20string &s = *static_cast<string*>(arg); 
    2121 
    2222        s = "line " + to_string(xmlTextReaderLocatorLineNumber(locator)) + ": " + msg; 
     
    3939} 
    4040 
     41//////////////////// XML::Handle 
    4142  
    42 //////////////////// XML::Handle 
    4343XML::Handle::Handle(BackEnd *backend, std::string path) : BackEnd::Handle(backend, path) { 
    4444} 
     
    6969                if (i->type == XML_ELEMENT_NODE) 
    7070                        if (!i->children) 
    71                                 props.set((const char*)i->name, ""); 
     71                                props.set(reinterpret_cast<const char*>(i->name), ""); 
    7272                        else 
    7373                        if (i->children->type != XML_TEXT_NODE) 
    74                                 throw ParseError(node, _("properties must be in the form <prop>content</prop>, '") + string((const char*)i->name) + _("' is not")); 
     74                                throw ParseError(node, _("properties must be in the form <prop>content</prop>, '") + string(reinterpret_cast<const char*>(i->name)) + _("' is not")); 
    7575                        else 
    76                                 props.set((const char*)i->name, (const char*)i->children->content); 
     76                                props.set(reinterpret_cast<const char*>(i->name), reinterpret_cast<const char*>(i->children->content)); 
    7777        return props; 
    7878} 
     
    8181        if (!node) throw Exception(backend(), _("can't parse NULL node (this should not occur and is to be considered a BUG)")); 
    8282 
    83         if (node->type == XML_ELEMENT_NODE && !xmlStrcmp(node->name, (const xmlChar*)"item")) { 
     83        if (node->type == XML_ELEMENT_NODE && !xmlStrcmp(node->name, reinterpret_cast<const xmlChar*>("item"))) { 
    8484        Item *item = new Item(); 
    85  
    86                 item->set<time_t>("created", time(0)); 
    87                 item->set<time_t>("modified", time(0)); 
    8885 
    8986                // We have reached an item node, parse out meta-data 
     
    9289                        string name = reinterpret_cast<const char*>(attribute->name); 
    9390 
    94                                 // Skip items on this parse 
     91                                // Skip child items on this parse 
    9592                                if (name == "item") continue; 
    9693 
     94                                // Comment 
     95                                if (name == "comment") { 
     96                                const char *author = reinterpret_cast<const char*>(xmlGetProp(attribute, reinterpret_cast<const xmlChar*>("author"))); 
     97                                const char *time = reinterpret_cast<const char*>(xmlGetProp(attribute, reinterpret_cast<const xmlChar*>("time"))); 
     98 
     99                                        if (!author) throw ParseError(attribute, "attribute '" + name + "' does not have the required property 'author'"); 
     100                                        if (!time) throw ParseError(attribute, "attribute '" + name + "' does not have the required property 'time'"); 
     101                                        if (attribute->children && attribute->children->type == XML_TEXT_NODE) { 
     102                                        const User *user = MANAGER.getUser(author); 
     103 
     104                                                item->addComment(Item::Comment( 
     105                                                        reinterpret_cast<const char*>(attribute->children->content), 
     106                                                        user, 
     107                                                        from_string<time_t>(time) 
     108                                                        )); 
     109                                        } else 
     110                                                throw ParseError(attribute, "no comment body found"); 
     111                                } else 
    97112                                // timestamps 
    98113                                if (name == "created" || name == "start" || name == "end" || 
    99114                                        name == "modified" || name == "completed") { 
    100                                 const char *value = (const char*)xmlGetProp(attribute, (const xmlChar*)"time"); 
     115                                const char *value = reinterpret_cast<const char*>(xmlGetProp(attribute, reinterpret_cast<const xmlChar*>("time"))); 
    101116 
    102117                                        if (!value) 
    103118                                                throw ParseError(attribute, "attribute '" + name + "' does not have the required property 'time'"); 
    104  
    105                                         try { 
    106                                                 item->set<time_t>(name, MANAGER.utc2local(from_string<time_t>(value))); 
    107                                         } catch (BadCast) { 
    108                                                 throw ParseError(attribute, _("'time' attribute is in an invalid format '") + string(value) + _("'")); 
    109                                         } 
     119                                        item->set(name, value); 
    110120                                } else 
    111121                                if (name == "category") { 
    112                                 const char *value = (const char*)xmlGetProp(attribute, (const xmlChar*)"id"); 
     122                                const char *value = reinterpret_cast<const char*>(xmlGetProp(attribute, reinterpret_cast<const xmlChar*>("id"))); 
    113123 
    114124                                        if (!value) 
     
    121131                                } else 
    122132                                if (name == "owner" || name == "assigned") { 
    123                                 const char *value = (const char*)xmlGetProp(attribute, (const xmlChar*)"id"); 
     133                                const char *value = reinterpret_cast<const char*>(xmlGetProp(attribute, reinterpret_cast<const xmlChar*>("id"))); 
    124134 
    125135                                        if (!value) 
     
    137147                                        if (attribute->children) 
    138148                                                if (attribute->children->type == XML_TEXT_NODE) 
    139                                                         content = (const char*)attribute->children->content
     149                                                        content = reinterpret_cast<const char*>(attribute->children->content)
    140150                                                else 
    141151                                                        throw ParseError(attribute, "'" + name + _("' should either be empty or have content consisting solely of text")); 
     
    183193                        // Do top-level meta-data parse 
    184194                        for (xmlNode *top = xroot; top != 0; top = top->next) { 
    185                                 if (xmlStrcmp(top->name, (const xmlChar*)"todo2")) 
    186                                         throw Exception(backend(), _("unexpected top-level XML node '") + string((const char*)top->name) + "'"); 
     195                                if (xmlStrcmp(top->name, reinterpret_cast<const xmlChar*>("todo2"))) 
     196                                        throw Exception(backend(), _("unexpected top-level XML node '") + string(reinterpret_cast<const char*>(top->name)) + "'"); 
    187197                                xitems = top->children; 
    188198                                for (xmlNode *node = top->children; node != 0; node = node->next) { 
    189199                                        if (node->type == XML_ELEMENT_NODE) { 
    190                                                 if (!xmlStrcmp(node->name, (const xmlChar*)"user")) { 
     200                                                if (!xmlStrcmp(node->name, reinterpret_cast<const xmlChar*>("user"))) { 
    191201                                                        MANAGER.addUser(loadProperties(node)); 
    192202                                                } else 
    193                                                 if (!xmlStrcmp(node->name, (const xmlChar*)"category")) { 
     203                                                if (!xmlStrcmp(node->name, reinterpret_cast<const xmlChar*>("category"))) { 
    194204                                                        MANAGER.addCategory(loadProperties(node)); 
    195205                                                } else 
    196                                                 if (xmlStrcmp(node->name, (const xmlChar*)"item")) 
    197                                                         throw Exception(backend(), _("unexpected top-level XML node '") + string((const char*)node->name) + "'"); 
     206                                                if (xmlStrcmp(node->name, reinterpret_cast<const xmlChar*>("item"))) 
     207                                                        throw Exception(backend(), _("unexpected top-level XML node '") + string(reinterpret_cast<const char*>(node->name)) + "'"); 
    198208                                        } 
    199209                                } 
     
    251261} 
    252262 
     263///////////////////// XML 
    253264  
    254 ///////////////////// XML 
    255265XML::XML() : BackEnd("xml") { 
    256266} 
  • todo2/trunk/src/ContactMethod.cc

    r305 r306  
    11#include "ContactMethod.h" 
     2#include "Uncontactable.h" 
     3 
     4using namespace std; 
     5using namespace crash; 
    26 
    37ModuleProxy<ContactMethod>::List 
    48        ModuleProxy<ContactMethod>::mproxies; 
     9 
     10// Built-in modules 
     11REGISTER_CONTACTMETHOD(Uncontactable); 
    512 
    613ContactMethod::ContactMethod(const std::string &name) : Module(name) { 
  • todo2/trunk/src/contactmethod/.makeclass.cc

    r295 r306  
    55using namespace crash; 
    66 
    7 REGISTER_COMMCHANNEL(Mail
     7REGISTER_CONTACTMETHOD(${CLASSNAME}
    88 
    9 ${CLASSNAME}::${CLASSNAME}()
     9${CLASSNAME}::${CLASSNAME}() : ContactMethod("${LCLASSNAME}")
    1010} 
    1111 
     
    1616        throw Exception("${CLASSNAME}::send() not implemented yet"); 
    1717} 
     18 
     19void ${CLASSNAME}::args(crash::Args &args) { 
     20} 
     21 
     22bool ${CLASSNAME}::arg(crash::Args::iterator arg) { 
     23} 
  • todo2/trunk/src/contactmethod/.makeclass.h

    r295 r306  
    22#define ${UCLASSNAME}_H__ 
    33 
    4 #include "CommChannel.h" 
     4#include "ContactMethod.h" 
    55 
    66/** 
     
    88*/ 
    99 
    10 class ${CLASSNAME} : public CommChannel
     10class ${CLASSNAME} : public ContactMethod
    1111        public : 
    12                 SUBCLASS_EXCEPTION(CommChannel::Exception) 
     12                SUBCLASS_MODULE_EXCEPTION(ContactMethod::Exception) 
    1313 
    14                 explicit ${CLASSNAME}(); 
     14                ${CLASSNAME}(); 
    1515                ~${CLASSNAME}(); 
    1616 
    17                 static const char *name() { return "${LCLASSNAME}"; } 
    18                 CommChannel *instantiate() { return new ${CLASSNAME}(); } 
     17                void args(crash::Args &args); 
     18                bool arg(crash::Args::iterator arg); 
    1919 
    2020                void send(User *user, const std::string &subject, const std::string &message); 
  • todo2/trunk/src/Database.cc

    r295 r306  
    2222 
    2323Item *Database::checkout(Item::Index index) { 
    24         if (mflags & READONLY) throw Exception("attempted to modify item " + (string)index + " in read-only database"); 
     24        if (mflags & READONLY) throw Exception("attempted to modify item " + string(index) + " in read-only database"); 
    2525 
    2626Item *item = find(index); 
     
    2828 
    2929        if (i != mtransactions.end()) 
    30                 throw Exception("item " + (string)index + " is already checked out"); 
     30                throw Exception("item " + string(index) + " is already checked out"); 
    3131        onCheckout(this, item); 
    3232        mtransactions[item] = index; 
     
    3838 
    3939        if (i == mtransactions.end()) throw Exception("no open transaction for item, can't commit"); 
    40         if (mflags & READONLY) throw Exception("attempted to commit item " + (string)i->second + " to read-only database"); 
     40        if (mflags & READONLY) throw Exception("attempted to commit item " + string(i->second) + " to read-only database"); 
    4141        onCommit(this, item); 
    4242        mtransactions.erase(i); 
  • todo2/trunk/src/filter/Expression.cc

    r305 r306  
    1313 
    1414Expression::Expression() : Filter("expression"), musedefaultvalue(false), mconstructingfilter(false) { 
    15         setUnlessDefined("default-value", musedefaultvalue); 
    16         setUnlessDefined("filter", "!completed"); 
    17         MANAGER.setUnlessDefined("default-argument-handler", "expression"); 
     15        setDefault("default-value", musedefaultvalue); 
     16        setDefault("filter", "!completed"); 
    1817 
    1918        // Load aliases 
     
    259258                                return execute(item, alias->second, depth + 1); 
    260259                         
    261                         if (ev == "children") return (double)item->children(); 
    262                         if (ev == "index") return (double)item->index(); 
    263                         if (ev == "absindex") return (string)item->absIndex(); 
    264                         if (ev == "depth") return (double)item->depth(); 
     260                        if (ev == "children") return double(item->children()); 
     261                        if (ev == "index") return double(item->index()); 
     262                        if (ev == "absindex") return string(item->absIndex()); 
     263                        if (ev == "depth") return double(item->depth()); 
    265264                        if (ev == "true") return 1.0; 
    266265                        if (ev == "false") return 0.0; 
    267                         if (ev == "active") return (double)item->active(); 
    268                         if (ev == "created") return (double)item->created(); 
    269                         if (ev == "modified") return (double)item->modified(); 
     266                        if (ev == "active") return double(item->active()); 
     267                        if (ev == "created") return double(item->created()); 
     268                        if (ev == "modified") return double(item->modified()); 
    270269                        if (ev == "owner") return item->owner()->id(); 
    271                         if (ev == "type") return (double)item->type(); 
    272                         if (ev == "now") return Manager::time(); 
    273                         if (ev == "lifetime") return (double)item->lifetime(); 
     270                        if (ev == "type") return double(item->type()); 
     271                        if (ev == "now") return to_string(Time()); 
     272                        if (ev == "lifetime") return double(item->lifetime()); 
    274273                        if (ev == "username") return MANAGER.get("username"); 
    275274                        if (ev == "hostname") return MANAGER.get("hostname"); 
    276275 
    277                         if (MANAGER.havePriority(ev)) return (double)MANAGER.priority(ev); 
     276                        if (MANAGER.havePriority(ev)) return double(MANAGER.priority(ev)); 
    278277 
    279278                        // Search attributes of the item first 
     
    310309                                if (expr->parameters() != 1) 
    311310                                        throw Exception(this, _("category(catid) requires category id as its only argument")); 
    312                                 return (bool)MANAGER.getCategory(execute(item, expr->parameter(0), depth + 1)); 
     311                                return bool(MANAGER.getCategory(execute(item, expr->parameter(0), depth + 1))); 
    313312                        } 
    314313 
     
    316315                                if (expr->parameters() != 1) 
    317316                                        throw Exception(this, _("user(userid) requires user id as its only argument")); 
    318                                 return (bool)MANAGER.getUser(execute(item, expr->parameter(0), depth + 1)); 
     317                                return bool(MANAGER.getUser(execute(item, expr->parameter(0), depth + 1))); 
    319318                        } 
    320319                         
  • todo2/trunk/src/frontend/Console.cc

    r305 r306  
     1#include <cstdlib> 
    12#include <unistd.h> 
     3#include <sys/types.h> 
     4#include <sys/stat.h> 
    25#include <crash/TermColour.h> 
    36#include <crash/Input.h> 
     7#include <crash/Crash.h> 
    48#include "Manager.h" 
    59#include "ModuleProxy.h" 
     
    1115REGISTER_FRONTEND(Console) 
    1216 
    13 Console::Console() : FrontEnd("console"), mterm(), mtermios(mterm), mpriority(0) { 
     17Console::Console() : FrontEnd("console"), mterm(), mtermios(mterm), mpriority(0), mmode(DEFAULT), mactions(0), mcomplete(0) { 
     18        setDefault("colour", "always"); 
     19        setDefault("editor", "internal"); 
     20 
    1421        // Are we outputting to a TTY? 
    15         mcolour = mtty = isatty(1); 
    16         if (exists("colour")) { 
    17         string cc = get("colour"); 
    18  
    19                 if (cc == "always") 
    20                         mcolour = true; 
    21                 else 
    22                 if (cc != "auto") 
    23                         mcolour = get<bool>("colour", true); 
    24         } 
    25         colour(mcolour); 
     22        mtty = isatty(1); 
     23        colour(get("colour")); 
    2624} 
    2725 
     
    2927} 
    3028 
    31 void Console::colour(bool state) { 
    32         if (state) { 
     29void Console::colour(std::string state) { 
     30bool colour = true; 
     31 
     32        if (state == "auto") 
     33                colour = isatty(1); 
     34        else 
     35        if (state == "never") 
     36                colour = false; 
     37        else 
     38        if (state == "always") 
     39                colour = true; 
     40        else 
     41                throw Exception(this, "unknown colour mode '" + state + "'"); 
     42 
     43        mcolour = colour; 
     44 
     45        if (colour) { 
    3346                BLACK = foreground(crash::BLACK); 
    3447                RED = foreground(crash::RED); 
     
    5770        args.add('m', "move", Args::OPTIONAL, _("Move item to underneath <parent>, or root if <parent> is not specified."), _("<parent>")); 
    5871        args.add('c', "complete", Args::OPTIONAL, _("Mark an item as 100% complete or optionally specify the percentage."), _("<percent>")); 
    59         args.add('S', "start", Args::REQUIRED, _("Mark start of a work period on an item.")); 
     72        args.add('S', "start", Args::NONE, _("Mark start of a work period on an item.")); 
    6073        args.add('E', "end", Args::OPTIONAL, _("Mark end of a work period on an item."), _("<text>")); 
    6174        ARG_SCHEDULED_START = args.add(Args::ALLOCATE, "scheduled-start", Args::REQUIRED, _("Specify the scheduled start time for an item."), _("<time>")); 
    6275        ARG_SCHEDULED_END = args.add(Args::ALLOCATE, "scheduled-end", Args::REQUIRED, _("Specify the scheduled end time for an item."), _("<time>")); 
    63         ARG_DELETE = args.add(Args::ALLOCATE, "delete", Args::REQUIRED, _("Delete the specified index."), _("<index>")); 
    6476} 
    6577 
    6678bool Console::arg(crash::Args::iterator arg) { 
     79        // DEFAULT, ADD, REMOVE, EDIT, MOVE 
    6780        switch (arg.option()) { 
    68                 case 0 : if (!defaultArgumentHandler()) break; 
     81                case 0 : 
     82                        if (mmode != DEFAULT) 
     83                                try { 
     84                                        mindex = arg.parameter(); 
     85                                } catch (BadCast) { 
     86                                        DEBUG(9, "rejected '" << arg.parameter() << "' as default item index"); 
     87                                        return false; 
     88                                } 
     89                        else 
     90                        if (!defaultArgumentHandler()) 
     91                                return false; 
     92                break; 
    6993                case 'i' : 
    70                         throw Exception(this, "-i not implemented"); 
     94                        mindex = arg.parameter(); 
    7195                break; 
    7296                case 'e' : 
    73                         throw Exception(this, "-e not implemented"); 
    74                 break; 
    75                 case 'd' : 
    76                         throw Exception(this, "-d not implemented"); 
     97                        mmode = EDIT; 
    7798                break; 
    7899                case 'r' : 
    79                         throw Exception(this, "-r not implemented")
     100                        mmode = REMOVE
    80101                break; 
    81102                case 'm' : 
    82                         throw Exception(this, "-m not implemented"); 
     103                        mmode = MOVE; 
     104                        if (arg.hasParameter()) 
     105                                mmoveindex = arg.parameter(); 
    83106                break; 
    84107                case 'c' : 
    85                         throw Exception(this, "-c not implemented"); 
     108                        if (arg.hasParameter()) 
     109                                mcomplete = from_string<double>(arg.parameter()) / 100.0; 
     110                        else 
     111                                mcomplete = 100; 
     112                        mactions |= COMPLETE; 
    86113                break; 
    87114                case 'a' : 
    88                         throw Exception(this, "-a not implemented"); 
     115                        mmode = ADD; 
     116                        if (arg.hasParameter()) 
     117                                maddtext = arg.parameter(); 
    89118                break; 
    90119                case 'p' : 
     
    92121                break; 
    93122                case 'S' : 
    94                         throw Exception(this, "-S not implemented")
     123                        mactions |= START
    95124                break; 
    96125                case 'E' : 
    97                         throw Exception(this, "-E not implemented"); 
     126                        mactions |= END; 
     127                        if (arg.hasParameter()) 
     128                                mendtext = arg.parameter(); 
    98129                break; 
    99130                default : 
    100131                        if (arg.option() == ARG_COLOUR || arg.option() == ARG_COLOR) { 
    101                                 if (arg.hasParameter()) { 
    102                                 const string &mode = arg.parameter(); 
    103  
    104                                         if (mode == "always") 
    105                                                 colour(mcolour = true); 
    106                                         else 
    107                                         if (mode == "never" || mode == "none") 
    108                                                 colour(mcolour = false); 
    109                                         else 
    110                                         if (mode == "auto") 
    111                                                 colour(mcolour = isatty(1)); 
    112                                         else 
    113                                                 throw Exception(this, "unknown colour mode '" + mode + "'"); 
    114                                 } else 
    115                                         colour(mcolour = true); 
     132                                if (arg.hasParameter()) 
     133                                        colour(arg.parameter()); 
     134                                else 
     135                                        colour("always"); 
    116136                        } else 
    117                         if (arg.option() == ARG_SCHEDULED_START) 
    118                                 throw Exception(this, "--scheduled-start not implemented"); 
    119                         else 
    120                         if (arg.option() == ARG_SCHEDULED_END) 
    121                                 throw Exception(this, "--scheduled-end not implemented"); 
    122                         else 
    123                         if (arg.option() == ARG_DELETE) 
    124                                 throw Exception(this, "--delete not implemented"); 
    125                         else 
     137                        if (arg.option() == ARG_SCHEDULED_START) { 
     138                                mscheduledstart = from_string<time_t>(arg.parameter()); 
     139                                mactions |= SCHEDULED_START; 
     140                        } else 
     141                        if (arg.option() == ARG_SCHEDULED_END) { 
     142                                mscheduledend = from_string<time_t>(arg.parameter()); 
     143                                mactions |= SCHEDULED_END; 
     144                        } else 
    126145                                return false; 
    127146                break; 
    128147        } 
    129         return false; 
     148        return true; 
    130149} 
    131150 
     
    175194 
    176195std::string Console::input(const std::string &prompt, const std::string &title, bool password) { 
    177         if (title != "") cout << title << endl; 
    178 Input input(mterm, mtermios); 
    179         if (prompt != "") input.prompt(prompt); 
    180         input.echo(!password); 
    181         return input.get(); 
    182 
     196        if (get("editor") == "internal") { 
     197                if (title != "") cout << title << endl; 
     198        Input input(mterm, mtermios); 
     199                if (prompt != "") input.prompt(prompt); 
     200                input.echo(!password); 
     201                return input.get(); 
     202        } else { 
     203        string editor = get("editor"); 
     204        char temp[PATH_MAX] = "/tmp/todo2.console.XXXXXX"; 
     205         
     206                if (editor == "environment") { 
     207                        if (!getenv("EDITOR")) 
     208                                throw Exception(this, "module.console.editor set to environment, but EDITOR envar not set"); 
     209                        editor = getenv("EDITOR"); 
     210                } 
     211 
     212        mode_t mask = umask(0600); 
     213        int tempfd; 
     214        string input; 
     215 
     216                try { 
     217                        if ((tempfd = mkstemp(temp)) == -1) 
     218                                throw Exception(this, "failed to create temporar file '" + string(temp) + "'"); 
     219                        system((editor + " " + temp).c_str()); 
     220                        input = readfile(temp); 
     221                } catch (...) { 
     222                        umask(mask); 
     223                        throw; 
     224                } 
     225                umask(mask); 
     226                return input; 
     227        } 
     228
  • todo2/trunk/src/frontend/Console.h

    r299 r306  
    1717                SUBCLASS_MODULE_EXCEPTION(FrontEnd::Exception) 
    1818 
    19                 explicit Console(); 
     19                Console(); 
    2020                ~Console(); 
    2121 
     
    3636 
    3737        private : 
    38                 void colour(bool state); 
     38                void colour(std::string state); 
     39 
     40                enum Mode { 
     41                        DEFAULT, ADD, REMOVE, EDIT, MOVE, 
     42                }; 
     43 
     44                enum Actions { 
     45                        COMPLETE = 1, 
     46                        START = 2, 
     47                        END = 4, 
     48                        SCHEDULED_START = 8, 
     49                        SCHEDULED_END = 16, 
     50                }; 
    3951 
    4052                std::string BOLD, NORMAL, RESET, BLACK, RED, GREEN, BROWN, BLUE, MAGENTA, CYAN, WHITE; 
    4153 
    42                 int ARG_COLOR, ARG_COLOUR, ARG_DELETE, ARG_SCHEDULED_START, ARG_SCHEDULED_END; 
     54                int ARG_COLOR, ARG_COLOUR, ARG_SCHEDULED_START, ARG_SCHEDULED_END; 
    4355                bool mtty, mcolour; 
    4456                crash::Terminal mterm; 
     
    4658                Database *mdatabase; 
    4759                int mpriority;