Author: bdonlan
Date: 2005-01-01 00:15:18 -0500 (Sat, 01 Jan 2005)
New Revision: 497

Modified:
   trunk/
   trunk/clients/havercurs/TODO
   trunk/clients/havercurs/cmdline.c
   trunk/clients/havercurs/cmdline.h
Log:
 [EMAIL PROTECTED]:  bdonlan | 2005-01-01T05:15:03.142926Z
 Completed command-line trie implementation.
 
 Happy new year!
 



Property changes on: trunk
___________________________________________________________________
Name: svk:merge
   - 1f59643a-e6e5-0310-bc24-f7d4c744f460:/haver/local/trunk:10261
27e50396-46e3-0310-8b22-ae223a1f35ce:/local:212
edfcd8bd-4ce7-0310-a97e-bb1efd40edf3:/local:238
   + 1f59643a-e6e5-0310-bc24-f7d4c744f460:/haver/local/trunk:10263
27e50396-46e3-0310-8b22-ae223a1f35ce:/local:212
edfcd8bd-4ce7-0310-a97e-bb1efd40edf3:/local:238

Modified: trunk/clients/havercurs/TODO
===================================================================
--- trunk/clients/havercurs/TODO        2005-01-01 02:33:30 UTC (rev 496)
+++ trunk/clients/havercurs/TODO        2005-01-01 05:15:18 UTC (rev 497)
@@ -3,13 +3,13 @@
 * basic display
 * event loop (fd polling, timers)
 * Synchronous connection
+* Line-oriented buffered I/O
+* Hash table
 
 Debugging:
-* Line-oriented buffered I/O
+* /-command parser
 
 TODO:
-* Hash table
-* /-command parser
 * Haver protocol parser
 * Authentication
 * Chat

Modified: trunk/clients/havercurs/cmdline.c
===================================================================
--- trunk/clients/havercurs/cmdline.c   2005-01-01 02:33:30 UTC (rev 496)
+++ trunk/clients/havercurs/cmdline.c   2005-01-01 05:15:18 UTC (rev 497)
@@ -35,9 +35,15 @@
     struct trie *ptrs[TRIE_CHARS];
     cmdline_handler *handler;
     void *baton;
+    int children; /* number of commands under here - sum of ptrs[]->children,
+                   * plus one if handler is non-null
+                   */
+    struct trie *parent;
+    int depth;
+    char c;
 } trie;
 
-static trie *root = NULL;
+static trie root;
 
 /** trie_trans_char
  *
@@ -49,7 +55,7 @@
  * Return value:
  * Index of character, or -1 if not found
  */
-static inline int trie_find_char(char c) {
+static inline char trie_find_char(char c) {
     char *p = strchr(trie_chars, c);
     if (!p)
         return -1;
@@ -75,7 +81,7 @@
         int create
         )
 {
-    trie *cur = root;
+    trie *cur = &root;
     while (*prefix && cur) {
         trie *next;
         int idx = trie_find_char(*prefix);
@@ -89,6 +95,9 @@
             next = mymalloc(sizeof *next);
             next->handler = NULL;
             next->baton = NULL;
+            next->parent = cur;
+            next->c = idx;
+            next->depth = cur->depth + 1;
             for (i = 0; i < TRIE_CHARS; i++)
                 next->ptrs[i] = NULL;
             cur->ptrs[idx] = next;
@@ -98,18 +107,46 @@
     return cur;
 }
 
+/** trie_purge
+ *
+ * Remove and free all children of this node.
+ *
+ * Arguments:
+ * node - the node to act upon
+ */
+static void trie_purge(trie *node) {
+    int i;
+    for (i = 0; i < TRIE_CHARS; i++) {
+        if (node->ptrs[i]) {
+            trie_purge(node->ptrs[i]);
+            free(node->ptrs[i]);
+            node->ptrs[i] = NULL;
+        }
+    }
+    if (node->handler)
+        node->children = 1;
+    else
+        node->children = 0;
+}
+
 /** cmdline_init()
  *
  * Initializes the module. Must be called before any other functions in this 
header.
  */
-void cmdline_init(void);
+void cmdline_init(void) {
+    /* NO-OP */
+}
 
 /** cmdline_free()
  *
  * De-initializes the module. cmdline_init() must be called again after 
calling this,
  * before any other functions from this header are called
  */
-void cmdline_free(void);
+void cmdline_free(void) {
+    trie_purge(root);
+    root->handler = NULL;
+    root->children = 0;
+}
 
 /** cmdline_register()
  *
@@ -127,7 +164,21 @@
                const char *command,
                cmdline_handler *cb,
                void *baton
-               );
+               )
+{
+    cmdline_handler *old;
+    trie *node = trie_find_level(command, 1);
+    old = node->handler;
+    node->handler = cb;
+    node->baton = baton;
+    if (!old) {
+        while (node) {
+            node->children++;
+            node = node->parent;
+        }
+    }
+    return old;
+}
 
 /** cmdline_unregister()
  *
@@ -139,8 +190,55 @@
  * Return value:
  * Previous handler or NULL if none
  */
-cmdline_handler *cmdline_unregister(const char *command);
+cmdline_handler *cmdline_unregister(const char *command) {
+    trie *prev = NULL;
+    cmdline_handler *old;
+    trie *targ = trie_lookup(command, 0);
+    if (!targ)
+        return NULL;
+    old = targ->handler;
+    targ->handler = NULL;
+    
+    /* Update children counts up the trie. We keep track of the last node,
+     * and when we transition from zero to nonzero, purge the last node and
+     * free it.
+     */
+    while (targ) {
+        targ->children--;
+        if (last && !last->children && targ->children) {
+            trie_purge(last);
+            targ->ptrs[last->c] = NULL;
+            free(last);
+        }
+        last = targ;
+        targ = targ->parent;
+    }
+    
+    return old;
+}
 
+struct lookup_baton {
+    void *baton;
+    cmdline_handler *handler;
+};
+
+/** lookup_cb()
+ *
+ * Callback used by cmdline_lookup() with cmdline_enum()
+ */
+static int lookup_cb(
+        void *baton,
+        const char *command,
+        cmdline_handler *handler,
+        void *h_baton
+        )
+{
+    struct lookup_baton *lb = baton;
+    lb->baton = h_baton;
+    lb->handler = handler;
+    return 0;
+}
+
 /** cmdline_lookup()
  *
  * Obtains the handler for a given command
@@ -152,13 +250,29 @@
  * int partial  - 1 if partial matching should be attempted
  *
  * Return value:
- * Handler callback, or NULL if none
+ * Handler callback, or NULL if none or ambiguous
  */
 cmdline_handler *cmdline_lookup(
                const char *command,
                void **baton,
         int partial
-               );
+               )
+{
+    struct lookup_baton lb;
+    trie *targ = trie_lookup(command, 0);
+    if (targ) {
+        if (baton)
+            *baton = targ->baton;
+        return targ->handler;
+    }
+    if (!partial)
+        return NULL;
+    if (1 != cmdline_enum(command, lookup_cb, &lb))
+        return NULL;
+    if (baton)
+        *baton = lb.baton;
+    return lb.handler;
+}
 
 /** cmdline_process()
  *
@@ -166,7 +280,7 @@
  *
  * Arguments:
  * const char *cmdline - full command line to process
- * int particl         - 1 if partial matching should be attempted
+ * int partial         - 1 if partial matching should be attempted
  *
  * Return value:
  * 2 if command was processed with a partial match
@@ -174,8 +288,80 @@
  * 0 if no handler was found
  * -1 if multiple partial matches were found; no action will be taken
  */
-int cmdline_process(const char *cmdline);
+int cmdline_process(const char *cmdline, int partial) {
+    char *cmdstr;
+    char *endcmd = cmdline;
+    cmdline_handler *h;
+    void *baton;
+    struct lookup_baton lb;
+    size_t ret;
 
+    while (*endcmd && strchr(trie_chars, *endcmd))
+        endcmd++;
+    cmdstr = mymalloc(endcmd - cmdline + 1);
+    memcpy(cmdstr, cmdline, endcmd - cmdline);
+    cmdstr[endcmd - cmdline] = '\0';
+
+    h = cmdline_lookup(cmdstr, &baton);
+    if (h) {
+        h(baton, cmdline);
+        return 1;
+    }
+    if (!partial)
+        return 0;
+    
+    ret = cmdline_enum(cmdstr, lookup_cb, &lb);
+    if (ret == 0)
+        return 0;
+    if (ret != 1)
+        return -1;
+    lb.handler(lb.baton, cmdline);
+    return 2;
+}
+
+/** enum_recurs()
+ *
+ * Recursively scan the trie node given, returning the count of non-NULL
+ * handlers found. Call cb() on each handler found, as long as *cont is
+ * nonzero. Sets *cont to the return of cb().
+ *
+ * Arguments:
+ * trie *node - node to start from
+ * cmdline_enum_callback *cb - callback to call once a handler is found
+ * void *baton - baton to pass to *cb
+ * int *cont - flag to terminate searching
+ */
+void enum_recurs(
+        trie *node,
+        cmdline_enum_callback *cb,
+        void *baton,
+        int *cont
+        )
+{
+    int i;
+    if (node->handler && *cont) {
+        /* figure out what our name is
+         * XXX: we malloc and free far too often - should use a single buffer
+         */
+        trie *cur = node;
+        char *cmdname = mymalloc(node->depth + 1);
+        cmdname[node->depth] = '\0';
+        while (cur->parent) {
+            cmdname[cur->depth - 1] = trie_chars[cur->c];
+            cur = cur->parent;
+        }
+
+        *cont = cb(baton, cmdname, node->handler, node->baton);
+        free(cmdname);
+    }
+    for (i = 0; i < TRIE_CHARS; i++) {
+        if (!*cont)
+            return;
+        if (node->ptrs[i])
+            enum_recurs(node->ptrs[i], cb, baton, cont);
+    }
+}
+
 /** cmdline_enum()
  *
  * Obtain a complete listing of partial matches for a command prefix
@@ -192,4 +378,12 @@
         const char *prefix,
         cmdline_enum_callback *cb,
         void *baton
-        );
+        )
+{
+    trie *node = trie_find_level(prefix, 0);
+    int cont = 1;
+    if (!node)
+        return 0;
+    enum_recurs(node, cb, baton, &cont);
+    return node->children;
+}

Modified: trunk/clients/havercurs/cmdline.h
===================================================================
--- trunk/clients/havercurs/cmdline.h   2005-01-01 02:33:30 UTC (rev 496)
+++ trunk/clients/havercurs/cmdline.h   2005-01-01 05:15:18 UTC (rev 497)
@@ -108,7 +108,7 @@
  * int partial  - 1 if partial matching should be attempted
  *
  * Return value:
- * Handler callback, or NULL if none
+ * Handler callback, or NULL if none or ambiguous
  */
 cmdline_handler *cmdline_lookup(
                const char *command,


Reply via email to