Check-in [c9fdb846fb]
Not logged in
Overview

SHA1 Hash:c9fdb846fb169e3789256688942b56e5c4fa390f
Date: 2007-08-18 02:45:47
User: drh
Comment:Add the "help" command and the "clean" command. More work is needed on the text for various help messages.
Timelines: ancestors | descendants | both | trunk
Other Links: files | ZIP archive | manifest

Tags And Properties
Changes
[hide diffs]

Modified src/add.c from [f62f7b3cc4] to [771768ca0d].

@@ -30,12 +30,13 @@
 
 
 /*
 ** COMMAND: add
 **
+** Usage: %fossil add FILE...
 ** Add one or more files to the current checkout such that these files
-** will be added to the repository at the next checkin.
+** will be inserted into the repository at the next commit.
 */
 void add_cmd(void){
   int i;
   int vid;
 
@@ -80,10 +81,12 @@
 
 /*
 ** COMMAND: rm
 ** COMMAND: del
 **
+** Usage: %fossil rm FILE...
+**    or: %fossil del FILE...
 ** Remove one or more files from the tree.
 */
 void del_cmd(void){
   int i;
   int vid;

Modified src/checkin.c from [2d5771221e] to [dfe4f317d7].

@@ -70,11 +70,13 @@
 }
 
 /*
 ** COMMAND: changes
 **
-** Report on the current status of all files.
+** Usage: %fossil changes
+** Report on the edit status of all files in the current checkout.
+** See also the "status" and "extra" commands.
 */
 void changes_cmd(void){
   Blob report;
   int vid;
   db_must_be_within_tree();
@@ -85,10 +87,12 @@
   blob_write_to_file(&report, "-");
 }
 
 /*
 ** COMMAND: status
+** Usage: %fossil status
+** Report on the status of the current checkout.
 */
 void status_cmd(void){
   int vid;
   db_must_be_within_tree();
        /* 012345678901234 */
@@ -102,12 +106,12 @@
   changes_cmd();
 }
 
 /*
 ** COMMAND: ls
-**
-** Show all files currently in the repository
+** Usage: %fossil ls
+** Show the names of all files in the current checkout
 */
 void ls_cmd(void){
   int vid;
   Stmt q;
 
@@ -134,29 +138,53 @@
   db_finalize(&q);
 }
 
 /*
 ** COMMAND: extra
-**
+** Usage: %fossil extra
 ** Print a list of all files in the source tree that are not part of
-** the project
+** the current checkout.  See also the "clean" command.
 */
 void extra_cmd(void){
   Blob path;
   Stmt q;
   db_must_be_within_tree();
   db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
   chdir(g.zLocalRoot);
   blob_zero(&path);
   vfile_scan(0, &path);
-  db_multi_exec("DELETE FROM sfile WHERE x='FOSSIL'");
   db_prepare(&q,
       "SELECT x FROM sfile"
       " WHERE x NOT IN ('manifest','_FOSSIL_')"
       " ORDER BY 1");
   while( db_step(&q)==SQLITE_ROW ){
     printf("%s\n", db_column_text(&q, 0));
+  }
+  db_finalize(&q);
+}
+
+/*
+** COMMAND: clean
+** Usage: %fossil clean
+** Delete all "extra" files in the source tree.  "Extra" files are
+** files that are not officially part of the checkout.  See also
+** the "extra" command.
+*/
+void clean_cmd(void){
+  Blob path;
+  Stmt q;
+  db_must_be_within_tree();
+  db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
+  chdir(g.zLocalRoot);
+  blob_zero(&path);
+  vfile_scan(0, &path);
+  db_prepare(&q,
+      "SELECT %Q || x FROM sfile"
+      " WHERE x NOT IN ('manifest','_FOSSIL_')"
+      " ORDER BY 1", g.zLocalRoot);
+  while( db_step(&q)==SQLITE_ROW ){
+    unlink(db_column_text(&q, 0));
   }
   db_finalize(&q);
 }
 
 /*
@@ -191,10 +219,11 @@
   }
   zFile = db_text(0, "SELECT '%qci-comment-' || hex(randomblob(6)) || '.txt'",
                    g.zLocalRoot);
   blob_write_to_file(&text, zFile);
   zCmd = mprintf("%s %s", zEditor, zFile);
+  printf("%s\n", zCmd);
   if( system(zCmd) ){
     fossil_panic("editor aborted");
   }
   blob_reset(&text);
   blob_read_from_file(&text, zFile);
@@ -255,17 +284,19 @@
 }
 
 /*
 ** COMMAND: commit
 **
-** Create a new version containing all of the changes in the current
-** checkout. A commit is a three step process:
+** Usage: %fossil commit ?-m COMMENT? ?--nosign? ?FILE...?
 **
-**   1) Add the new content to the blob table,
-**   2) Create and add the new manifest to the blob table,
-**   3) Update the vfile table,
-**   4) Run checks to make sure everything is still internally consistent.
+** Create a new version containing all of the changes in the current
+** checkout.  You will be prompted to enter a check-in comment unless
+** the "-m" option is used to specify a command line.  You will be
+** prompted for your GPG passphrase in order to sign the new manifest
+** unless the "--nosign" options is used.  All files that have
+** changed will be committed unless some subset of files is specified
+** on the command line.
 */
 void commit_cmd(void){
   int rc;
   int vid, nrid, nvid;
   Blob comment;

Modified src/checkout.c from [ce6c20911d] to [ded735af90].

@@ -109,11 +109,16 @@
 }
 
 /*
 ** COMMAND: checkout
 **
-** Check out a version specified on the command-line.
+** Usage: %fossil checkout VERSION ?-f|--force?
+** Check out a version specified on the command-line.  This command
+** will not overwrite edited files in the current checkout unless
+** the --force option appears on the command-line.
+**
+** See also the "update" command.
 */
 void checkout_cmd(void){
   int forceFlag;
   int noWrite;
   int vid, prior;
@@ -159,10 +164,11 @@
 }
 
 /*
 ** COMMAND: close
 **
+** Usage: %fossil close ?-f|--force?
 ** The opposite of "open".  Close the current database connection.
 ** Require a -f or --force flag if there are unsaved changed in the
 ** current check-out.
 */
 void close_cmd(void){

Modified src/clone.c from [31346c860f] to [9c95e6e8e6].

@@ -30,13 +30,14 @@
 
 
 /*
 ** COMMAND: clone
 **
-** Make a clone of a repository in the local directory
+** Usage: %fossil clone URL FILENAME
 **
-**    fossil clone FILE-OR-URL NEWDATABASE
+** Make a clone of a repository specified by URL in the local
+** file named FILENAME.
 */
 void clone_cmd(void){
   if( g.argc!=4 ){
     usage("FILE-OR-URL NEW-REPOSITORY");
   }

Modified src/db.c from [fafbfe094b] to [13dcb9659a].

@@ -103,12 +103,11 @@
 /*
 ** Prepare or reprepare the sqlite3 statement from the raw SQL text.
 */
 static void reprepare(Stmt *pStmt){
   sqlite3_stmt *pNew;
-  int rc;
-  if( (rc = sqlite3_prepare(g.db, blob_buffer(&pStmt->sql), -1, &pNew, 0))!=0 ){
+  if( sqlite3_prepare(g.db, blob_buffer(&pStmt->sql), -1, &pNew, 0)!=0 ){
     db_err("%s\n%s", blob_str(&pStmt->sql), sqlite3_errmsg(g.db));
   }
   if( pStmt->pStmt ){
     sqlite3_transfer_bindings(pStmt->pStmt, pNew);
     sqlite3_finalize(pStmt->pStmt);
@@ -191,11 +190,11 @@
 /*
 ** Step the SQL statement.  Return either SQLITE_ROW or an error code
 ** or SQLITE_OK if the statement finishes successfully.
 */
 int db_step(Stmt *pStmt){
-  int rc;
+  int rc = SQLITE_OK;
   int limit = 3;
   while( limit-- ){
     rc = sqlite3_step(pStmt->pStmt);
     if( rc==SQLITE_ERROR ){
       rc = sqlite3_reset(pStmt->pStmt);
@@ -647,11 +646,14 @@
 
 
 /*
 ** COMMAND: new
 **
-** Create a new repository
+** Usage: %fossil new FILENAME
+** Create a repository for a new project in the file named FILENAME.
+** This command is distinct from "clone".  The "clone" command makes
+** a copy of an existing project.  This command starts a new project.
 */
 void create_repository_cmd(void){
   char *zDate;
   char *zUser;
   Blob hash;
@@ -821,11 +823,14 @@
 }
 
 /*
 ** COMMAND: open
 **
-** Create a new local repository.
+** Usage: open FILENAME
+** Open a connection to the local repository in FILENAME.  A checkout
+** for the repository is created with its root at the working directory.
+** See also the "close" command.
 */
 void cmd_open(void){
   Blob path;
   if( g.argc!=3 ){
     usage("REPOSITORY-FILENAME");

Modified src/descendents.c from [caa863826b] to [6273899c23].

@@ -65,10 +65,11 @@
 }
 
 /*
 ** COMMAND:  leaves
 **
+** Usage: %fossil leaves ?UUID?
 ** Find all leaf descendents of the current version or of the
 ** specified version.
 */
 void leaves_cmd(void){
   Stmt q;
@@ -94,23 +95,17 @@
 }
 
 /*
 ** COMMAND:  branches
 **
+** Usage: %fossil branches
 ** Find leaves of all branches.
 */
 void branches_cmd(void){
   Stmt q;
-  int base;
 
   db_must_be_within_tree();
-  if( g.argc==2 ){
-    base = db_lget_int("checkout", 0);
-  }else{
-    base = name_to_rid(g.argv[2]);
-  }
-  if( base==0 ) return;
   db_prepare(&q,
     "SELECT blob.uuid, datetime(event.mtime,'localtime'), event.comment"
     "  FROM blob, event"
     " WHERE blob.rid IN"
     "       (SELECT cid FROM plink EXCEPT SELECT pid FROM plink)"

Modified src/diffcmd.c from [42f780093c] to [16a1f70543].

@@ -45,10 +45,15 @@
 
 
 /*
 ** COMMAND: diff
 ** COMMAND: tkdiff
+**
+** Usage: %fossil diff|tkdiff FILE...
+** Show the difference between the current version of a file (as it
+** exists on disk) and that same file as it was checked out.  Use
+** either "diff -u" or "tkdiff".
 */
 void diff_cmd(void){
   const char *zFile;
   Blob cmd;
   Blob fname;

Modified src/main.c from [18f71690c1] to [69bfd961c8].

@@ -135,11 +135,11 @@
 */
 static int name_search(
   const char *zName,       /* The name we are looking for */
   const NameMap *aMap,     /* Search in this array */
   int nMap,                /* Number of slots in aMap[] */
-  void (**pxFunc)(void)    /* Write pointer to handler function here */
+  int *pIndex              /* OUT: The index in aMap[] of the match */
 ){
   int upr, lwr, cnt, m, i;
   int n = strlen(zName);
   lwr = 0;
   upr = nMap-1;
@@ -146,11 +146,11 @@
   while( lwr<=upr ){
     int mid, c;
     mid = (upr+lwr)/2;
     c = strcmp(zName, aMap[mid].zName);
     if( c==0 ){
-      *pxFunc = aMap[mid].xFunc;
+      *pIndex = mid;
       return 0;
     }else if( c<0 ){
       upr = mid - 1;
     }else{
       lwr = mid + 1;
@@ -162,11 +162,11 @@
       m = i;
       cnt++;
     }
   }
   if( cnt==1 ){
-    *pxFunc = aMap[m].xFunc;
+    *pIndex = m;
     return 0;
   }
   return 1+(cnt>1);
 }
 
@@ -174,11 +174,11 @@
 /*
 ** This procedure runs first.
 */
 int main(int argc, char **argv){
   const char *zCmdName;
-  void (*xFunc)(void);
+  int idx;
   int rc;
 
   g.now = time(0);
   g.argc = argc;
   g.argv = argv;
@@ -192,11 +192,11 @@
     g.fSqlPrint = find_option("sqlprint", 0, 0)!=0;
     g.fHttpTrace = find_option("httptrace", 0, 0)!=0;
     g.zLogin = find_option("user", "U", 1);
     zCmdName = argv[1];
   }
-  rc = name_search(zCmdName, aCommand, count(aCommand), &xFunc);
+  rc = name_search(zCmdName, aCommand, count(aCommand), &idx);
   if( rc==1 ){
     fprintf(stderr,"%s: unknown command: %s\n"
                    "%s: use \"commands\" or \"test-commands\" for help\n",
                    argv[0], zCmdName, argv[0]);
     return 1;
@@ -204,11 +204,11 @@
     fprintf(stderr,"%s: ambiguous command prefix: %s\n"
                    "%s: use \"commands\" or \"test-commands\" for help\n",
                    argv[0], zCmdName, argv[0]);
     return 1;
   }
-  xFunc();
+  aCommand[idx].xFunc();
   return 0;
 }
 
 /*
 ** Print an error message, rollback all databases, and quit.
@@ -340,39 +340,78 @@
 }
 
 /*
 ** COMMAND: commands
 **
-** List all commands whose name does not start with "test-"
+** Usage: %fossil commands
+** List all supported commands.
 */
 void cmd_cmd_list(void){
   int i, nCmd;
   const char *aCmd[count(aCommand)];
   for(i=nCmd=0; i<count(aCommand); i++){
     if( strncmp(aCommand[i].zName,"test",4)==0 ) continue;
-    if( strcmp(aCommand[i].zName, g.argv[1])==0 ) continue;
+    /* if( strcmp(aCommand[i].zName, g.argv[1])==0 ) continue; */
     aCmd[nCmd++] = aCommand[i].zName;
   }
   multi_column_list(aCmd, nCmd);
 }
 
 /*
 ** COMMAND: test-commands
 **
-** List all commands whose name begins with "test"
+** Usage: %fossil test-commands
+** List all commands used for testing and debugging.
 */
 void cmd_test_cmd_list(void){
   int i, nCmd;
   const char *aCmd[count(aCommand)];
   for(i=nCmd=0; i<count(aCommand); i++){
     if( strncmp(aCommand[i].zName,"test",4)!=0 ) continue;
-    if( strcmp(aCommand[i].zName, g.argv[1])==0 ) continue;
+    /* if( strcmp(aCommand[i].zName, g.argv[1])==0 ) continue; */
     aCmd[nCmd++] = aCommand[i].zName;
   }
   multi_column_list(aCmd, nCmd);
 }
 
+
+/*
+** COMMAND: help
+**
+** Usage: %fossil help COMMAND
+** Display information on how to use COMMAND
+*/
+void help_cmd(void){
+  int rc, idx;
+  const char *z;
+  if( g.argc!=3 ){
+    printf("Usage: %s help <command>.\nAvailable commands:\n", g.argv[0]);
+    cmd_cmd_list();
+    return;
+  }
+  rc = name_search(g.argv[2], aCommand, count(aCommand), &idx);
+  if( rc==1 ){
+    fossil_fatal("unknown command: %s", g.argv[2]);
+  }else if( rc==2 ){
+    fossil_fatal("ambiguous command prefix: %s", g.argv[2]);
+  }
+  z = aCmdHelp[idx];
+  if( z==0 ){
+    fossil_fatal("no help available for the %s command",
+       aCommand[idx].zName);
+  }
+  while( *z ){
+    if( *z=='%' && strncmp(z, "%fossil", 7)==0 ){
+      printf("%s", g.argv[0]);
+      z += 7;
+    }else{
+      putchar(*z);
+      z++;
+    }
+  }
+  putchar('\n');
+}
 
 /*
 ** RSS feeds need to reference absolute URLs so we need to calculate
 ** the base URL onto which we add components. This is basically
 ** cgi_redirect() stripped down and always returning an absolute URL.
@@ -412,11 +451,11 @@
 ** environment variable.
 */
 static void process_one_web_page(void){
   const char *zPathInfo;
   char *zPath;
-  void (*xFunc)(void);
+  int idx;
   int i, j;
 
   /* Find the page that the user has requested, construct and deliver that
   ** page.
   */
@@ -463,17 +502,17 @@
   }
 
   /* Locate the method specified by the path and execute the function
   ** that implements that method.
   */
-  if( name_search(g.zPath, aWebpage, count(aWebpage), &xFunc) &&
-      name_search("not_found", aWebpage, count(aWebpage), &xFunc) ){
+  if( name_search(g.zPath, aWebpage, count(aWebpage), &idx) &&
+      name_search("not_found", aWebpage, count(aWebpage), &idx) ){
     cgi_set_status(404,"Not Found");
     @ <h1>Not Found</h1>
     @ <p>Page not found: %h(g.zPath)</p>
   }else{
-    xFunc();
+    aWebpage[idx].xFunc();
   }
 
   /* Return the result.
   */
   cgi_reply();

Modified src/mkindex.c from [a7cfef3b71] to [634272abcb].

@@ -55,21 +55,33 @@
 */
 typedef struct Entry {
   int eType;
   char *zFunc;
   char *zPath;
+  char *zHelp;
 } Entry;
 
 /*
 ** Maximum number of entries
 */
 #define N_ENTRY 500
 
 /*
+** Maximum size of a help message
+*/
+#define MX_HELP 10000
+
+/*
 ** Table of entries
 */
 Entry aEntry[N_ENTRY];
+
+/*
+** Current help message accumulator
+*/
+char zHelp[MX_HELP];
+int nHelp;
 
 /*
 ** How many entries are used
 */
 int nUsed;
@@ -121,11 +133,23 @@
 /*
 ** Scan a line for a function that implements a web page or command.
 */
 void scan_for_func(char *zLine){
   int i,j,k;
+  char *z;
   if( nUsed<=nFixed ) return;
+  if( strncmp(zLine, "**", 2)==0 && isspace(zLine[2])
+       && strlen(zLine)<sizeof(zHelp)-nHelp-1 && nUsed>nFixed ){
+    if( zLine[2]=='\n' ){
+      zHelp[nHelp++] = '\n';
+    }else{
+      if( strncmp(&zLine[3], "Usage: ", 6)==0 ) nHelp = 0;
+      strcpy(&zHelp[nHelp], &zLine[3]);
+      nHelp += strlen(&zHelp[nHelp]);
+    }
+    return;
+  }
   for(i=0; isspace(zLine[i]); i++){}
   if( zLine[i]==0 ) return;
   if( strncmp(&zLine[i],"void",4)!=0 ){
     if( zLine[i]!='*' ) goto page_skip;
     return;
@@ -133,17 +157,28 @@
   i += 4;
   if( !isspace(zLine[i]) ) goto page_skip;
   while( isspace(zLine[i]) ){ i++; }
   for(j=0; isalnum(zLine[i+j]) || zLine[i+j]=='_'; j++){}
   if( j==0 ) goto page_skip;
+  for(k=nHelp-1; k>=0 && isspace(zHelp[k]); k--){}
+  nHelp = k+1;
+  zHelp[nHelp] = 0;
+  for(k=0; k<nHelp && isspace(zHelp[k]); k++){}
+  if( k<nHelp ){
+    z = string_dup(&zHelp[k], nHelp-k);
+  }else{
+    z = 0;
+  }
   for(k=nFixed; k<nUsed; k++){
     aEntry[k].zFunc = string_dup(&zLine[i], j);
+    aEntry[k].zHelp = z;
   }
   i+=j;
   while( isspace(zLine[i]) ){ i++; }
   if( zLine[i]!='(' ) goto page_skip;
   nFixed = nUsed;
+  nHelp = 0;
   return;
 
 page_skip:
    for(i=nFixed; i<nUsed; i++){
       fprintf(stderr,"%s:%d: skipping page \"%s\"\n",
@@ -168,10 +203,11 @@
 /*
 ** Build the binary search table.
 */
 void build_table(void){
   int i;
+  int nType0;
 
   qsort(aEntry, nFixed, sizeof(aEntry[0]), e_compare);
   for(i=0; i<nFixed; i++){
     printf("extern void %s(void);\n", aEntry[i].zFunc);
   }
@@ -188,18 +224,49 @@
       aEntry[i].zPath, (int)(25-strlen(aEntry[i].zPath)), "",
       aEntry[i].zFunc
     );
   }
   printf("};\n");
+  nType0 = i;
   printf(
     "static const NameMap aCommand[] = {\n"
   );
-  for(; i<nFixed && aEntry[i].eType==1; i++){
+  for(i=nType0; i<nFixed && aEntry[i].eType==1; i++){
     printf("  { \"%s\",%*s %s },\n",
       aEntry[i].zPath, (int)(25-strlen(aEntry[i].zPath)), "",
       aEntry[i].zFunc
     );
+  }
+  printf("};\n");
+  for(i=nType0; i<nFixed; i++){
+    char *z = aEntry[i].zHelp;
+    if( z && z[0] ){
+      printf("static const char zHelp_%s[] = \n", aEntry[i].zFunc);
+      printf("  \"");
+      while( *z ){
+        if( *z=='\n' ){
+          printf("\\n\"\n  \"");
+        }else if( *z=='"' ){
+          printf("\\\"");
+        }else{
+          putchar(*z);
+        }
+        z++;
+      }
+      printf("\";\n");
+      aEntry[i].zHelp[0] = 0;
+    }
+  }
+  printf(
+    "static const char * const aCmdHelp[] = {\n"
+  );
+  for(i=nType0; i<nFixed; i++){
+    if( aEntry[i].zHelp==0 ){
+      printf("  0,\n");
+    }else{
+      printf("  zHelp_%s,\n", aEntry[i].zFunc);
+    }
   }
   printf("};\n");
 }
 
 /*