Index: htdbm.c
===================================================================
--- htdbm.c	(revision 159730)
+++ htdbm.c	(working copy)
@@ -32,6 +32,7 @@
 #include "apr_md5.h"
 #include "apr_sha1.h"
 #include "apr_dbm.h"
+#include "apr_getopt.h"
 
 #if APR_HAVE_STDLIB_H
 #include <stdlib.h>
@@ -92,10 +93,12 @@
     char                    *username;
     char                    *userpass;
     char                    *comment;
+    char                    *groups;
     char                    *type;
     int                     create;
     int                     rdonly;
     int                     alg;
+    int                     overwrite;
 };
 
 
@@ -178,6 +181,17 @@
                             APR_OS_DEFAULT, htdbm->pool);
 }
 
+static int htdbm_user_exists(htdbm_t *htdbm){
+    apr_datum_t key;
+
+    key.dptr = htdbm->username;
+    key.dsize = strlen(htdbm->username);
+    if (apr_dbm_exists(htdbm->dbm, key))
+        return TRUE;
+    return FALSE;
+
+}
+
 static apr_status_t htdbm_save(htdbm_t *htdbm, int *changed) 
 {
     apr_datum_t key, val;
@@ -185,22 +199,41 @@
     if (!htdbm->username)
         return APR_SUCCESS;
 
+    if (htdbm_user_exists(htdbm)){
+      if (!h->overwrite){ 
+        fprintf(stderr,"User %s allready exists in %s, -o to overwrite\n",htdbm->username,htdbm->filename);
+        exit(ERR_SYNTAX);
+      }
+      else {
+        fprintf(stdout,"Overwriting user %s as requested\n",htdbm->username);
+      }
+    }
     key.dptr = htdbm->username;
     key.dsize = strlen(htdbm->username);
     if (apr_dbm_exists(htdbm->dbm, key))
         *changed = 1;
 
     val.dsize = strlen(htdbm->userpass);
-    if (!htdbm->comment)
-        val.dptr  = htdbm->userpass;
+    val.dptr  = htdbm->userpass;
+    if(htdbm->groups){
+       val.dptr = apr_pstrcat(htdbm->pool, val.dptr, ":",
+                              htdbm->groups, NULL);
+    }
     else {
-        val.dptr = apr_pstrcat(htdbm->pool, htdbm->userpass, ":",
-                               htdbm->comment, NULL);
-        val.dsize += (strlen(htdbm->comment) + 1);
+       val.dptr = apr_pstrcat(htdbm->pool, val.dptr, ":", NULL);
+
     }
+    if(htdbm->comment){
+       val.dptr = apr_pstrcat(htdbm->pool, val.dptr, ":",
+                              htdbm->comment, NULL);
+    }
+    val.dsize += (strlen(val.dptr) + 1);
+
     return apr_dbm_store(htdbm->dbm, key, val);
+
 }
 
+
 static apr_status_t htdbm_del(htdbm_t *htdbm) 
 {
     apr_datum_t key;
@@ -238,7 +271,7 @@
 {
     apr_status_t rv;
     apr_datum_t key, val;
-    char *rec, *cmnt;
+    char *rec, *grp, *cmnt, *tmpbuf;
     char kb[MAX_STRING_LEN];
     int i = 0;
 
@@ -250,7 +283,7 @@
     rec = apr_pcalloc(htdbm->pool, HUGE_STRING_LEN);
 
     fprintf(stderr, "Dumping records from database -- %s\n", htdbm->filename); 
-    fprintf(stderr, "    %-32sComment\n", "Username");    
+    fprintf(stderr, "    %-32s%-32sComment\n", "Username" , "Groups");    
     while (key.dptr != NULL) {
         rv = apr_dbm_fetch(htdbm->dbm, key, &val);
         if (rv != APR_SUCCESS) {
@@ -262,9 +295,19 @@
         fprintf(stderr, "    %-32s", kb);
         strncpy(rec, val.dptr, val.dsize);
         rec[val.dsize] = '\0';
-        cmnt = strchr(rec, ':');
-        if (cmnt)
-            fprintf(stderr, "%s", cmnt + 1);
+        grp = strchr(rec, ':');
+        if (grp ){ 
+            cmnt = strchr(grp +1, ':');
+            if (cmnt){
+              tmpbuf = apr_pstrndup(h->pool,grp,(cmnt-grp));
+              fprintf(stderr, "%-32s", tmpbuf + 1);
+              fprintf(stderr, "%s", cmnt + 1);
+            }
+            else{
+              fprintf(stderr, "%-48s", grp + 1);
+ 
+            }
+        }
         fprintf(stderr, "\n");
         rv = apr_dbm_nextkey(htdbm->dbm, &key);
         if (rv != APR_SUCCESS)
@@ -291,6 +334,7 @@
 {
     char cpw[MAX_STRING_LEN];
     char salt[9];
+  
 
     switch (htdbm->alg) {
         case ALG_APSHA:
@@ -346,30 +390,41 @@
 #define CRYPT_OPTION ""
 #endif
     fprintf(stderr, "htdbm -- program for manipulating DBM password databases.\n\n");
-    fprintf(stderr, "Usage: htdbm    [-cm"CRYPT_OPTION"pstvx] [-TDBTYPE] database username\n");
-    fprintf(stderr, "                -b[cm"CRYPT_OPTION"ptsv] [-TDBTYPE] database username password\n");
-    fprintf(stderr, "                -n[m"CRYPT_OPTION"pst]   username\n");
-    fprintf(stderr, "                -nb[m"CRYPT_OPTION"pst]  username password\n");
-    fprintf(stderr, "                -v[m"CRYPT_OPTION"ps]    [-TDBTYPE] database username\n");
-    fprintf(stderr, "                -vb[m"CRYPT_OPTION"ps]   [-TDBTYPE] database username password\n");
-    fprintf(stderr, "                -x[m"CRYPT_OPTION"ps]    [-TDBTYPE] database username\n");
-    fprintf(stderr, "                -l                       [-TDBTYPE] database\n");
-    fprintf(stderr, "Options:\n");
-    fprintf(stderr, "   -b   Use the password from the command line rather "
+    fprintf(stderr, "Usage:\n");
+    fprintf(stderr, "      add:  	htdbm [-cm"CRYPT_OPTION"ptsvo] [-TDBTYPE]  [-b password -g group1,group -t comment] [-f] database [-u] username\n");
+    fprintf(stderr, "      noact:	htdbm -n[m"CRYPT_OPTION"ps]   [-b password -g group1,group2 -t comment] [-u] username \n");
+    fprintf(stderr, "      verify:	htdbm -v[m"CRYPT_OPTION"ps]   [-TDBTYPE] [-b password] [-f] database [-u] username \n");
+    fprintf(stderr, "      delete:	htdbm -x[m"CRYPT_OPTION"ps]   [-TDBTYPE] [-f] database [-u] username\n");
+    fprintf(stderr, "      list: 	htdbm -l         [-TDBTYPE] [-f] database\n");
+
+
+    fprintf(stderr, "Options:\n\n");
+    fprintf(stderr, "   -c  \t Create a new database.\n");
+    fprintf(stderr, "   -n  \t Don't update database; display results on stdout.\n");
+    fprintf(stderr, "   -l  \t Display usernames from database on stdout.\n");
+    fprintf(stderr, "   -v  \t Verify the username/password.\n");
+    fprintf(stderr, "   -x  \t Remove the username record from database.\n");
+    fprintf(stderr, "   -o \t overwrite user if necessary.\n");
+    
+    fprintf(stderr,"\n");
+    fprintf(stderr, "   -f DATABASE \t database to use.\n");
+    fprintf(stderr, "   -u USER \t target user.\n");
+    fprintf(stderr, "   -b PASS \t Use the password from the command line rather "
                     "than prompting for it.\n");
-    fprintf(stderr, "   -c   Create a new database.\n");
-    fprintf(stderr, "   -n   Don't update database; display results on stdout.\n");
-    fprintf(stderr, "   -m   Force MD5 encryption of the password (default).\n");
+    fprintf(stderr, "   -g groups \t Groups for user e.g. \"group1,group2\"\n");
+    fprintf(stderr, "   -t comment \t Comment for user\n");
+    fprintf(stderr,"\n");
+
+    fprintf(stderr, "   -m  \t Force MD5 encryption of the password (default).\n");
 #if APR_HAVE_CRYPT_H
-    fprintf(stderr, "   -d   Force CRYPT encryption of the password (now deprecated).\n");
+    fprintf(stderr, "   -d  \t Force CRYPT encryption of the password (now deprecated).\n");
 #endif
-    fprintf(stderr, "   -p   Do not encrypt the password (plaintext).\n");
-    fprintf(stderr, "   -s   Force SHA encryption of the password.\n");
-    fprintf(stderr, "   -T   DBM Type (SDBM|GDBM|DB|default).\n");
-    fprintf(stderr, "   -l   Display usernames from database on stdout.\n");
-    fprintf(stderr, "   -t   The last param is username comment.\n");
-    fprintf(stderr, "   -v   Verify the username/password.\n");
-    fprintf(stderr, "   -x   Remove the username record from database.\n");
+    fprintf(stderr, "   -p  \t Do not encrypt the password (plaintext).\n");
+    fprintf(stderr, "   -s  \t Force SHA encryption of the password.\n");
+    fprintf(stderr, "   -T  \t DBM Type (SDBM|GDBM|DB|default).\n");
+    fprintf(stderr, "\n\n");
+    fprintf(stderr, "If positional parameters (username, database) are used they must follow all options\n\n");
+    fprintf(stderr, "Example to add a user: \nhtdbm -b mypass -g editors,reviewers -t \"Added by joe\" /home/joe/mydb user1\n\n");
     exit(ERR_SYNTAX);
 
 }
@@ -394,6 +449,12 @@
     int  i;
     int args_left = 2;
 
+    apr_getopt_t *opt;
+    char ch;
+    const char *optarg;
+    char *tmpbuff;
+
+
     apr_app_initialize(&argc, &argv, NULL);
     atexit(terminate);
 
@@ -407,49 +468,65 @@
      * three arguments, we'll do better argument checking as 
      * we parse the command line.
      */
-    if (argc < 3)
-       htdbm_usage();
     /*
      * Go through the argument list and pick out any options.  They
      * have to precede any other arguments.
-     */
-    for (i = 1; i < argc; i++) {
-        arg = argv[i];
-        if (*arg != '-')
-            break;
-        
-        while (*++arg != '\0') {
-            switch (*arg) {
+    */
+
+
+
+    rv = apr_getopt_init(&opt,h->pool,argc,argv);
+    if(rv != APR_SUCCESS){
+       fprintf(stderr,"apr_get_opt_init bombed\n");
+       return;
+    }
+
+    while (apr_getopt(opt, "b:cnmdpsT:lt:g:vxf:u:ho", &ch, &optarg) == APR_SUCCESS) {
+        switch (ch) {
             case 'b':
                 pwd_supplied = 1;
                 need_pwd = 0;
-                args_left++;
+		h->userpass=apr_pstrdup(h->pool,optarg);
                 break;
             case 'c':
                 h->create = 1;
                 break;
+            case 'o':
+                h->overwrite = 1;
+                break;
             case 'n':
                 need_file = 0;
                 cmd = HTDBM_NOFILE;
-                    args_left--;
                 break;
             case 'l':
                 need_pwd = 0;
                 need_user = 0;
                 cmd = HTDBM_LIST;
                 h->rdonly = 1;
-                args_left--;
                 break;
             case 't':
-                need_cmnt = 1;
-                args_left++;
+                h->comment = apr_pstrdup(h->pool,optarg);
+		if(strchr(h->comment,':') != NULL){
+                   fprintf(stderr,"Comment cannot have colon (':') character\n");
+                   exit(ERR_SYNTAX);
+                }
                 break;
+            case 'f':
+                h->filename= apr_pstrdup(h->pool,optarg) ;
+                break;
+            case 'u':
+                h->username= apr_pstrdup(h->pool,optarg);
+                break;
             case 'T':
-                h->type = apr_pstrdup(h->pool, ++arg);
-                while (*arg != '\0')
-                    ++arg;
-                --arg; /* so incrementing this in the loop with find a null */
+                h->type = apr_pstrdup(h->pool, optarg);
                 break;
+            case 'g':
+                h->groups= apr_pstrdup(h->pool, optarg);
+                if(strchr(h->groups,':') != NULL){
+                   fprintf(stderr,"Group cannot have colon (':') character\n");
+                   exit(ERR_SYNTAX);
+                }
+                break;
             case 'v':
                 h->rdonly = 1;
                 cmd = HTDBM_VERIFY;
@@ -472,38 +549,53 @@
                 h->alg = ALG_CRYPT;
                 break;
 #endif
+            case 'h':
+                htdbm_usage();
+                break;
             default:
                 htdbm_usage();
                 break;
             }
         }
-    }
     /*
      * Make sure we still have exactly the right number of arguments left
      * (the filename, the username, and possibly the password if -b was
      * specified).
      */
-    if ((argc - i) != args_left)
-        htdbm_usage();
 
-    if (!need_file)
-        i--;
-    else {
-        h->filename = apr_pstrdup(h->pool, argv[i]);
+    if (need_file && !h->filename){
+       if (opt->ind < argc){
+          h->filename = apr_pstrdup(h->pool,argv[opt->ind++]);
+       }
+       else {
+         fprintf(stderr,"Need to supply -f DATABASE \n\n");
+	 exit(ERR_SYNTAX);
+       }
+    } 
+
+    if (need_user && !h->username){
+        if (opt->ind < argc){
+           h->username= apr_pstrdup(h->pool,argv[opt->ind++]);
+        } 
+       else {
+          fprintf(stderr,"Need to supply -u USER \n\n");
+	 exit(ERR_SYNTAX);
+       }
+    } 
+    
+
+    if(need_file) {
             if ((rv = htdbm_open(h)) != APR_SUCCESS) {
-            fprintf(stderr, "Error opening database %s\n", argv[i]);
+            fprintf(stderr, "Error opening database %s\n", h->filename);
             apr_strerror(rv, errbuf, sizeof(errbuf));
             fprintf(stderr,"%s\n",errbuf);
             exit(ERR_FILEPERM);
         }
     }
     if (need_user) {
-        h->username = apr_pstrdup(pool, argv[i+1]);
         if (htdbm_valid_username(h) != APR_SUCCESS)
             exit(ERR_BADUSER);
     }
-    if (pwd_supplied)
-        h->userpass = apr_pstrdup(pool, argv[i+2]);
 
     if (need_pwd) {
         l = sizeof(pwc);
@@ -523,11 +615,6 @@
             
         h->userpass = apr_pstrdup(pool,  pwi);
     }
-    if (need_cmnt && pwd_supplied)
-        h->comment = apr_pstrdup(pool, argv[i+3]);
-    else if (need_cmnt)
-        h->comment = apr_pstrdup(pool, argv[i+2]);
-
     switch (cmd) {
         case HTDBM_VERIFY:
             if ((rv = htdbm_verify(h)) != APR_SUCCESS) {
@@ -568,13 +655,17 @@
                 h->create ? "created" : (changed ? "modified" : "updated"));
     }
     if (cmd == HTDBM_NOFILE) {
-        if (!need_cmnt) {
-            fprintf(stderr, "%s:%s\n", h->username, h->userpass);
-        }
-        else {
-            fprintf(stderr, "%s:%s:%s\n", h->username, h->userpass,
-                    h->comment);
-        }
+        tmpbuff = apr_pstrcat(h->pool,h->username,":",h->userpass,":",NULL);
+
+	if (h->groups) tmpbuff = apr_pstrcat(h->pool,tmpbuff,h->groups,":", NULL);
+        else tmpbuff = apr_pstrcat(h->pool,tmpbuff,":", NULL);
+
+	if (h->comment) tmpbuff = apr_pstrcat(h->pool, tmpbuff, h->comment, ":", NULL); 
+        else tmpbuff = apr_pstrcat(h->pool,tmpbuff,":", NULL);
+        
+
+        fprintf(stdout,"%s\n",tmpbuff);
+
     }
     htdbm_terminate(h);
     
