Fossil SCM
Merge recent changes into the dual-license branch.
Commit
14c19fbc1c39554d869dfecfbb6c1721abc7f007
Parent
1942d581bb18956…
26 files changed
+4
+4
-8
-8
+19
-5
+1
-1
+23
-85
+23
-85
+4
-1
+2
-1
+5
+177
-54
+12
-2
+12
-2
+1
+1
+58
-1
+30
-2
+12
+12
+1
+220
-100
+1
-1
+29
+12
-3
+1
-1
~
src/add.c
~
src/add.c
~
src/cgi.c
~
src/cgi.c
~
src/clone.c
~
src/configure.c
~
src/db.c
~
src/db.c
~
src/delta.c
~
src/file.c
~
src/graph.c
~
src/main.c
~
src/main.mk
~
src/main.mk
~
src/makemake.tcl
~
src/makemake.tcl
~
src/name.c
~
src/rss.c
~
src/setup.c
~
src/setup.c
~
src/sha1.c
~
src/timeline.c
~
src/update.c
~
src/wiki.c
~
src/winhttp.c
~
www/index.wiki
+4
| --- src/add.c | ||
| +++ src/add.c | ||
| @@ -204,10 +204,14 @@ | ||
| 204 | 204 | char *zName; |
| 205 | 205 | char *zPath; |
| 206 | 206 | Blob pathname; |
| 207 | 207 | |
| 208 | 208 | zName = mprintf("%/", g.argv[i]); |
| 209 | + if( file_isdir(zName) ){ | |
| 210 | + fossil_fatal("cannot remove directories -" | |
| 211 | + " remove individual files instead"); | |
| 212 | + } | |
| 209 | 213 | file_tree_name(zName, &pathname, 1); |
| 210 | 214 | zPath = blob_str(&pathname); |
| 211 | 215 | if( !db_exists( |
| 212 | 216 | "SELECT 1 FROM vfile WHERE pathname=%Q AND NOT deleted", zPath) ){ |
| 213 | 217 | fossil_fatal("not in the repository: %s", zName); |
| 214 | 218 |
| --- src/add.c | |
| +++ src/add.c | |
| @@ -204,10 +204,14 @@ | |
| 204 | char *zName; |
| 205 | char *zPath; |
| 206 | Blob pathname; |
| 207 | |
| 208 | zName = mprintf("%/", g.argv[i]); |
| 209 | file_tree_name(zName, &pathname, 1); |
| 210 | zPath = blob_str(&pathname); |
| 211 | if( !db_exists( |
| 212 | "SELECT 1 FROM vfile WHERE pathname=%Q AND NOT deleted", zPath) ){ |
| 213 | fossil_fatal("not in the repository: %s", zName); |
| 214 |
| --- src/add.c | |
| +++ src/add.c | |
| @@ -204,10 +204,14 @@ | |
| 204 | char *zName; |
| 205 | char *zPath; |
| 206 | Blob pathname; |
| 207 | |
| 208 | zName = mprintf("%/", g.argv[i]); |
| 209 | if( file_isdir(zName) ){ |
| 210 | fossil_fatal("cannot remove directories -" |
| 211 | " remove individual files instead"); |
| 212 | } |
| 213 | file_tree_name(zName, &pathname, 1); |
| 214 | zPath = blob_str(&pathname); |
| 215 | if( !db_exists( |
| 216 | "SELECT 1 FROM vfile WHERE pathname=%Q AND NOT deleted", zPath) ){ |
| 217 | fossil_fatal("not in the repository: %s", zName); |
| 218 |
+4
| --- src/add.c | ||
| +++ src/add.c | ||
| @@ -204,10 +204,14 @@ | ||
| 204 | 204 | char *zName; |
| 205 | 205 | char *zPath; |
| 206 | 206 | Blob pathname; |
| 207 | 207 | |
| 208 | 208 | zName = mprintf("%/", g.argv[i]); |
| 209 | + if( file_isdir(zName) ){ | |
| 210 | + fossil_fatal("cannot remove directories -" | |
| 211 | + " remove individual files instead"); | |
| 212 | + } | |
| 209 | 213 | file_tree_name(zName, &pathname, 1); |
| 210 | 214 | zPath = blob_str(&pathname); |
| 211 | 215 | if( !db_exists( |
| 212 | 216 | "SELECT 1 FROM vfile WHERE pathname=%Q AND NOT deleted", zPath) ){ |
| 213 | 217 | fossil_fatal("not in the repository: %s", zName); |
| 214 | 218 |
| --- src/add.c | |
| +++ src/add.c | |
| @@ -204,10 +204,14 @@ | |
| 204 | char *zName; |
| 205 | char *zPath; |
| 206 | Blob pathname; |
| 207 | |
| 208 | zName = mprintf("%/", g.argv[i]); |
| 209 | file_tree_name(zName, &pathname, 1); |
| 210 | zPath = blob_str(&pathname); |
| 211 | if( !db_exists( |
| 212 | "SELECT 1 FROM vfile WHERE pathname=%Q AND NOT deleted", zPath) ){ |
| 213 | fossil_fatal("not in the repository: %s", zName); |
| 214 |
| --- src/add.c | |
| +++ src/add.c | |
| @@ -204,10 +204,14 @@ | |
| 204 | char *zName; |
| 205 | char *zPath; |
| 206 | Blob pathname; |
| 207 | |
| 208 | zName = mprintf("%/", g.argv[i]); |
| 209 | if( file_isdir(zName) ){ |
| 210 | fossil_fatal("cannot remove directories -" |
| 211 | " remove individual files instead"); |
| 212 | } |
| 213 | file_tree_name(zName, &pathname, 1); |
| 214 | zPath = blob_str(&pathname); |
| 215 | if( !db_exists( |
| 216 | "SELECT 1 FROM vfile WHERE pathname=%Q AND NOT deleted", zPath) ){ |
| 217 | fossil_fatal("not in the repository: %s", zName); |
| 218 |
-8
| --- src/cgi.c | ||
| +++ src/cgi.c | ||
| @@ -695,20 +695,12 @@ | ||
| 695 | 695 | process_multipart_form_data(z, len); |
| 696 | 696 | } |
| 697 | 697 | }else if( strcmp(zType, "application/x-fossil")==0 ){ |
| 698 | 698 | blob_read_from_channel(&g.cgiIn, g.httpIn, len); |
| 699 | 699 | blob_uncompress(&g.cgiIn, &g.cgiIn); |
| 700 | - /* If the content type is application/x-fossil, then ignore | |
| 701 | - ** the path in the first line of the HTTP header and always | |
| 702 | - ** use the /xfer method since the /xfer method is the only | |
| 703 | - ** method that understands the application/x-fossil content | |
| 704 | - ** type. | |
| 705 | - */ | |
| 706 | - cgi_replace_parameter("PATH_INFO", "/xfer"); | |
| 707 | 700 | }else if( strcmp(zType, "application/x-fossil-debug")==0 ){ |
| 708 | 701 | blob_read_from_channel(&g.cgiIn, g.httpIn, len); |
| 709 | - cgi_replace_parameter("PATH_INFO", "/xfer"); /* See comment above */ | |
| 710 | 702 | } |
| 711 | 703 | } |
| 712 | 704 | |
| 713 | 705 | z = (char*)P("HTTP_COOKIE"); |
| 714 | 706 | if( z ){ |
| 715 | 707 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -695,20 +695,12 @@ | |
| 695 | process_multipart_form_data(z, len); |
| 696 | } |
| 697 | }else if( strcmp(zType, "application/x-fossil")==0 ){ |
| 698 | blob_read_from_channel(&g.cgiIn, g.httpIn, len); |
| 699 | blob_uncompress(&g.cgiIn, &g.cgiIn); |
| 700 | /* If the content type is application/x-fossil, then ignore |
| 701 | ** the path in the first line of the HTTP header and always |
| 702 | ** use the /xfer method since the /xfer method is the only |
| 703 | ** method that understands the application/x-fossil content |
| 704 | ** type. |
| 705 | */ |
| 706 | cgi_replace_parameter("PATH_INFO", "/xfer"); |
| 707 | }else if( strcmp(zType, "application/x-fossil-debug")==0 ){ |
| 708 | blob_read_from_channel(&g.cgiIn, g.httpIn, len); |
| 709 | cgi_replace_parameter("PATH_INFO", "/xfer"); /* See comment above */ |
| 710 | } |
| 711 | } |
| 712 | |
| 713 | z = (char*)P("HTTP_COOKIE"); |
| 714 | if( z ){ |
| 715 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -695,20 +695,12 @@ | |
| 695 | process_multipart_form_data(z, len); |
| 696 | } |
| 697 | }else if( strcmp(zType, "application/x-fossil")==0 ){ |
| 698 | blob_read_from_channel(&g.cgiIn, g.httpIn, len); |
| 699 | blob_uncompress(&g.cgiIn, &g.cgiIn); |
| 700 | }else if( strcmp(zType, "application/x-fossil-debug")==0 ){ |
| 701 | blob_read_from_channel(&g.cgiIn, g.httpIn, len); |
| 702 | } |
| 703 | } |
| 704 | |
| 705 | z = (char*)P("HTTP_COOKIE"); |
| 706 | if( z ){ |
| 707 |
-8
| --- src/cgi.c | ||
| +++ src/cgi.c | ||
| @@ -695,20 +695,12 @@ | ||
| 695 | 695 | process_multipart_form_data(z, len); |
| 696 | 696 | } |
| 697 | 697 | }else if( strcmp(zType, "application/x-fossil")==0 ){ |
| 698 | 698 | blob_read_from_channel(&g.cgiIn, g.httpIn, len); |
| 699 | 699 | blob_uncompress(&g.cgiIn, &g.cgiIn); |
| 700 | - /* If the content type is application/x-fossil, then ignore | |
| 701 | - ** the path in the first line of the HTTP header and always | |
| 702 | - ** use the /xfer method since the /xfer method is the only | |
| 703 | - ** method that understands the application/x-fossil content | |
| 704 | - ** type. | |
| 705 | - */ | |
| 706 | - cgi_replace_parameter("PATH_INFO", "/xfer"); | |
| 707 | 700 | }else if( strcmp(zType, "application/x-fossil-debug")==0 ){ |
| 708 | 701 | blob_read_from_channel(&g.cgiIn, g.httpIn, len); |
| 709 | - cgi_replace_parameter("PATH_INFO", "/xfer"); /* See comment above */ | |
| 710 | 702 | } |
| 711 | 703 | } |
| 712 | 704 | |
| 713 | 705 | z = (char*)P("HTTP_COOKIE"); |
| 714 | 706 | if( z ){ |
| 715 | 707 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -695,20 +695,12 @@ | |
| 695 | process_multipart_form_data(z, len); |
| 696 | } |
| 697 | }else if( strcmp(zType, "application/x-fossil")==0 ){ |
| 698 | blob_read_from_channel(&g.cgiIn, g.httpIn, len); |
| 699 | blob_uncompress(&g.cgiIn, &g.cgiIn); |
| 700 | /* If the content type is application/x-fossil, then ignore |
| 701 | ** the path in the first line of the HTTP header and always |
| 702 | ** use the /xfer method since the /xfer method is the only |
| 703 | ** method that understands the application/x-fossil content |
| 704 | ** type. |
| 705 | */ |
| 706 | cgi_replace_parameter("PATH_INFO", "/xfer"); |
| 707 | }else if( strcmp(zType, "application/x-fossil-debug")==0 ){ |
| 708 | blob_read_from_channel(&g.cgiIn, g.httpIn, len); |
| 709 | cgi_replace_parameter("PATH_INFO", "/xfer"); /* See comment above */ |
| 710 | } |
| 711 | } |
| 712 | |
| 713 | z = (char*)P("HTTP_COOKIE"); |
| 714 | if( z ){ |
| 715 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -695,20 +695,12 @@ | |
| 695 | process_multipart_form_data(z, len); |
| 696 | } |
| 697 | }else if( strcmp(zType, "application/x-fossil")==0 ){ |
| 698 | blob_read_from_channel(&g.cgiIn, g.httpIn, len); |
| 699 | blob_uncompress(&g.cgiIn, &g.cgiIn); |
| 700 | }else if( strcmp(zType, "application/x-fossil-debug")==0 ){ |
| 701 | blob_read_from_channel(&g.cgiIn, g.httpIn, len); |
| 702 | } |
| 703 | } |
| 704 | |
| 705 | z = (char*)P("HTTP_COOKIE"); |
| 706 | if( z ){ |
| 707 |
+19
-5
| --- src/clone.c | ||
| +++ src/clone.c | ||
| @@ -30,25 +30,39 @@ | ||
| 30 | 30 | |
| 31 | 31 | |
| 32 | 32 | /* |
| 33 | 33 | ** COMMAND: clone |
| 34 | 34 | ** |
| 35 | -** Usage: %fossil clone URL FILENAME | |
| 35 | +** Usage: %fossil clone ?OPTIONS? URL FILENAME | |
| 36 | 36 | ** |
| 37 | 37 | ** Make a clone of a repository specified by URL in the local |
| 38 | 38 | ** file named FILENAME. |
| 39 | +** | |
| 40 | +** By default, your current login name is used to create the default | |
| 41 | +** admin user. This can be overridden using the -A|--admin-user | |
| 42 | +** parameter. | |
| 43 | +** | |
| 44 | +** Options: | |
| 45 | +** | |
| 46 | +** --admin-user|-A USERNAME | |
| 47 | +** | |
| 39 | 48 | */ |
| 40 | 49 | void clone_cmd(void){ |
| 41 | 50 | char *zPassword; |
| 51 | + const char *zDefaultUser; /* Optional name of the default user */ | |
| 52 | + | |
| 42 | 53 | url_proxy_options(); |
| 43 | - if( g.argc!=4 ){ | |
| 44 | - usage("FILE-OR-URL NEW-REPOSITORY"); | |
| 54 | + if( g.argc < 4 ){ | |
| 55 | + usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY"); | |
| 45 | 56 | } |
| 46 | 57 | db_open_config(0); |
| 47 | 58 | if( file_size(g.argv[3])>0 ){ |
| 48 | 59 | fossil_panic("file already exists: %s", g.argv[3]); |
| 49 | 60 | } |
| 61 | + | |
| 62 | + zDefaultUser = find_option("admin-user","A",1); | |
| 63 | + | |
| 50 | 64 | url_parse(g.argv[2]); |
| 51 | 65 | if( g.urlIsFile ){ |
| 52 | 66 | file_copy(g.urlName, g.argv[3]); |
| 53 | 67 | db_close(); |
| 54 | 68 | db_open_repository(g.argv[3]); |
| @@ -66,19 +80,19 @@ | ||
| 66 | 80 | "DELETE FROM private;" |
| 67 | 81 | ); |
| 68 | 82 | shun_artifacts(); |
| 69 | 83 | g.zLogin = db_text(0, "SELECT login FROM user WHERE cap LIKE '%%s%%'"); |
| 70 | 84 | if( g.zLogin==0 ){ |
| 71 | - db_create_default_users(1); | |
| 85 | + db_create_default_users(1,zDefaultUser); | |
| 72 | 86 | } |
| 73 | 87 | printf("Repository cloned into %s\n", g.argv[3]); |
| 74 | 88 | }else{ |
| 75 | 89 | db_create_repository(g.argv[3]); |
| 76 | 90 | db_open_repository(g.argv[3]); |
| 77 | 91 | db_begin_transaction(); |
| 78 | 92 | db_record_repository_filename(g.argv[3]); |
| 79 | - db_initial_setup(0, 0); | |
| 93 | + db_initial_setup(0, zDefaultUser, 0); | |
| 80 | 94 | user_select(); |
| 81 | 95 | db_set("content-schema", CONTENT_SCHEMA, 0); |
| 82 | 96 | db_set("aux-schema", AUX_SCHEMA, 0); |
| 83 | 97 | db_set("last-sync-url", g.argv[2], 0); |
| 84 | 98 | db_multi_exec( |
| 85 | 99 |
| --- src/clone.c | |
| +++ src/clone.c | |
| @@ -30,25 +30,39 @@ | |
| 30 | |
| 31 | |
| 32 | /* |
| 33 | ** COMMAND: clone |
| 34 | ** |
| 35 | ** Usage: %fossil clone URL FILENAME |
| 36 | ** |
| 37 | ** Make a clone of a repository specified by URL in the local |
| 38 | ** file named FILENAME. |
| 39 | */ |
| 40 | void clone_cmd(void){ |
| 41 | char *zPassword; |
| 42 | url_proxy_options(); |
| 43 | if( g.argc!=4 ){ |
| 44 | usage("FILE-OR-URL NEW-REPOSITORY"); |
| 45 | } |
| 46 | db_open_config(0); |
| 47 | if( file_size(g.argv[3])>0 ){ |
| 48 | fossil_panic("file already exists: %s", g.argv[3]); |
| 49 | } |
| 50 | url_parse(g.argv[2]); |
| 51 | if( g.urlIsFile ){ |
| 52 | file_copy(g.urlName, g.argv[3]); |
| 53 | db_close(); |
| 54 | db_open_repository(g.argv[3]); |
| @@ -66,19 +80,19 @@ | |
| 66 | "DELETE FROM private;" |
| 67 | ); |
| 68 | shun_artifacts(); |
| 69 | g.zLogin = db_text(0, "SELECT login FROM user WHERE cap LIKE '%%s%%'"); |
| 70 | if( g.zLogin==0 ){ |
| 71 | db_create_default_users(1); |
| 72 | } |
| 73 | printf("Repository cloned into %s\n", g.argv[3]); |
| 74 | }else{ |
| 75 | db_create_repository(g.argv[3]); |
| 76 | db_open_repository(g.argv[3]); |
| 77 | db_begin_transaction(); |
| 78 | db_record_repository_filename(g.argv[3]); |
| 79 | db_initial_setup(0, 0); |
| 80 | user_select(); |
| 81 | db_set("content-schema", CONTENT_SCHEMA, 0); |
| 82 | db_set("aux-schema", AUX_SCHEMA, 0); |
| 83 | db_set("last-sync-url", g.argv[2], 0); |
| 84 | db_multi_exec( |
| 85 |
| --- src/clone.c | |
| +++ src/clone.c | |
| @@ -30,25 +30,39 @@ | |
| 30 | |
| 31 | |
| 32 | /* |
| 33 | ** COMMAND: clone |
| 34 | ** |
| 35 | ** Usage: %fossil clone ?OPTIONS? URL FILENAME |
| 36 | ** |
| 37 | ** Make a clone of a repository specified by URL in the local |
| 38 | ** file named FILENAME. |
| 39 | ** |
| 40 | ** By default, your current login name is used to create the default |
| 41 | ** admin user. This can be overridden using the -A|--admin-user |
| 42 | ** parameter. |
| 43 | ** |
| 44 | ** Options: |
| 45 | ** |
| 46 | ** --admin-user|-A USERNAME |
| 47 | ** |
| 48 | */ |
| 49 | void clone_cmd(void){ |
| 50 | char *zPassword; |
| 51 | const char *zDefaultUser; /* Optional name of the default user */ |
| 52 | |
| 53 | url_proxy_options(); |
| 54 | if( g.argc < 4 ){ |
| 55 | usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY"); |
| 56 | } |
| 57 | db_open_config(0); |
| 58 | if( file_size(g.argv[3])>0 ){ |
| 59 | fossil_panic("file already exists: %s", g.argv[3]); |
| 60 | } |
| 61 | |
| 62 | zDefaultUser = find_option("admin-user","A",1); |
| 63 | |
| 64 | url_parse(g.argv[2]); |
| 65 | if( g.urlIsFile ){ |
| 66 | file_copy(g.urlName, g.argv[3]); |
| 67 | db_close(); |
| 68 | db_open_repository(g.argv[3]); |
| @@ -66,19 +80,19 @@ | |
| 80 | "DELETE FROM private;" |
| 81 | ); |
| 82 | shun_artifacts(); |
| 83 | g.zLogin = db_text(0, "SELECT login FROM user WHERE cap LIKE '%%s%%'"); |
| 84 | if( g.zLogin==0 ){ |
| 85 | db_create_default_users(1,zDefaultUser); |
| 86 | } |
| 87 | printf("Repository cloned into %s\n", g.argv[3]); |
| 88 | }else{ |
| 89 | db_create_repository(g.argv[3]); |
| 90 | db_open_repository(g.argv[3]); |
| 91 | db_begin_transaction(); |
| 92 | db_record_repository_filename(g.argv[3]); |
| 93 | db_initial_setup(0, zDefaultUser, 0); |
| 94 | user_select(); |
| 95 | db_set("content-schema", CONTENT_SCHEMA, 0); |
| 96 | db_set("aux-schema", AUX_SCHEMA, 0); |
| 97 | db_set("last-sync-url", g.argv[2], 0); |
| 98 | db_multi_exec( |
| 99 |
+1
-1
| --- src/configure.c | ||
| +++ src/configure.c | ||
| @@ -487,11 +487,11 @@ | ||
| 487 | 487 | if( (aConfig[i].groupMask & mask)==0 ) continue; |
| 488 | 488 | if( zName[0]!='@' ){ |
| 489 | 489 | db_multi_exec("DELETE FROM config WHERE name=%Q", zName); |
| 490 | 490 | }else if( strcmp(zName,"@user")==0 ){ |
| 491 | 491 | db_multi_exec("DELETE FROM user"); |
| 492 | - db_create_default_users(0); | |
| 492 | + db_create_default_users(0, 0); | |
| 493 | 493 | }else if( strcmp(zName,"@concealed")==0 ){ |
| 494 | 494 | db_multi_exec("DELETE FROM concealed"); |
| 495 | 495 | }else if( strcmp(zName,"@shun")==0 ){ |
| 496 | 496 | db_multi_exec("DELETE FROM shun"); |
| 497 | 497 | }else if( strcmp(zName,"@reportfmt")==0 ){ |
| 498 | 498 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -487,11 +487,11 @@ | |
| 487 | if( (aConfig[i].groupMask & mask)==0 ) continue; |
| 488 | if( zName[0]!='@' ){ |
| 489 | db_multi_exec("DELETE FROM config WHERE name=%Q", zName); |
| 490 | }else if( strcmp(zName,"@user")==0 ){ |
| 491 | db_multi_exec("DELETE FROM user"); |
| 492 | db_create_default_users(0); |
| 493 | }else if( strcmp(zName,"@concealed")==0 ){ |
| 494 | db_multi_exec("DELETE FROM concealed"); |
| 495 | }else if( strcmp(zName,"@shun")==0 ){ |
| 496 | db_multi_exec("DELETE FROM shun"); |
| 497 | }else if( strcmp(zName,"@reportfmt")==0 ){ |
| 498 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -487,11 +487,11 @@ | |
| 487 | if( (aConfig[i].groupMask & mask)==0 ) continue; |
| 488 | if( zName[0]!='@' ){ |
| 489 | db_multi_exec("DELETE FROM config WHERE name=%Q", zName); |
| 490 | }else if( strcmp(zName,"@user")==0 ){ |
| 491 | db_multi_exec("DELETE FROM user"); |
| 492 | db_create_default_users(0, 0); |
| 493 | }else if( strcmp(zName,"@concealed")==0 ){ |
| 494 | db_multi_exec("DELETE FROM concealed"); |
| 495 | }else if( strcmp(zName,"@shun")==0 ){ |
| 496 | db_multi_exec("DELETE FROM shun"); |
| 497 | }else if( strcmp(zName,"@reportfmt")==0 ){ |
| 498 |
M
src/db.c
+23
-85
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -506,86 +506,10 @@ | ||
| 506 | 506 | z = mprintf("%s", sqlite3_column_text(s.pStmt, 0)); |
| 507 | 507 | } |
| 508 | 508 | db_finalize(&s); |
| 509 | 509 | return z; |
| 510 | 510 | } |
| 511 | - | |
| 512 | -#ifdef __MINGW32__ | |
| 513 | -/* | |
| 514 | -** These routines (copied out of the os_win.c driver for SQLite) convert | |
| 515 | -** character strings in various microsoft multi-byte character formats | |
| 516 | -** into UTF-8. Fossil and SQLite always use only UTF-8 internally. These | |
| 517 | -** routines are needed in order to convert from the default character set | |
| 518 | -** currently in use by windows into UTF-8 when strings are imported from | |
| 519 | -** the outside world. | |
| 520 | -*/ | |
| 521 | -/* | |
| 522 | -** Convert microsoft unicode to UTF-8. Space to hold the returned string is | |
| 523 | -** obtained from malloc(). | |
| 524 | -** Copied from sqlite3.c as is (petr) | |
| 525 | -*/ | |
| 526 | -static char *unicodeToUtf8(const WCHAR *zWideFilename){ | |
| 527 | - int nByte; | |
| 528 | - char *zFilename; | |
| 529 | - | |
| 530 | - nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0); | |
| 531 | - zFilename = malloc( nByte ); | |
| 532 | - if( zFilename==0 ){ | |
| 533 | - return 0; | |
| 534 | - } | |
| 535 | - nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte, | |
| 536 | - 0, 0); | |
| 537 | - if( nByte == 0 ){ | |
| 538 | - free(zFilename); | |
| 539 | - zFilename = 0; | |
| 540 | - } | |
| 541 | - return zFilename; | |
| 542 | -} | |
| 543 | -/* | |
| 544 | -** Convert an ansi string to microsoft unicode, based on the | |
| 545 | -** current codepage settings for file apis. | |
| 546 | -** | |
| 547 | -** Space to hold the returned string is obtained | |
| 548 | -** from malloc. | |
| 549 | -*/ | |
| 550 | -static WCHAR *mbcsToUnicode(const char *zFilename){ | |
| 551 | - int nByte; | |
| 552 | - WCHAR *zMbcsFilename; | |
| 553 | - int codepage = CP_ACP; | |
| 554 | - | |
| 555 | - nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, NULL,0)*sizeof(WCHAR); | |
| 556 | - zMbcsFilename = malloc( nByte*sizeof(zMbcsFilename[0]) ); | |
| 557 | - if( zMbcsFilename==0 ){ | |
| 558 | - return 0; | |
| 559 | - } | |
| 560 | - | |
| 561 | - nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, zMbcsFilename, nByte); | |
| 562 | - if( nByte==0 ){ | |
| 563 | - free(zMbcsFilename); | |
| 564 | - zMbcsFilename = 0; | |
| 565 | - } | |
| 566 | - return zMbcsFilename; | |
| 567 | -} | |
| 568 | -/* | |
| 569 | -** Convert multibyte character string to UTF-8. Space to hold the | |
| 570 | -** returned string is obtained from malloc(). | |
| 571 | -*/ | |
| 572 | -static char *mbcsToUtf8(const char *zFilename){ | |
| 573 | - char *zFilenameUtf8; | |
| 574 | - WCHAR *zTmpWide; | |
| 575 | - | |
| 576 | - zTmpWide = mbcsToUnicode(zFilename); | |
| 577 | - if( zTmpWide==0 ){ | |
| 578 | - return 0; | |
| 579 | - } | |
| 580 | - | |
| 581 | - zFilenameUtf8 = unicodeToUtf8(zTmpWide); | |
| 582 | - free(zTmpWide); | |
| 583 | - return zFilenameUtf8; | |
| 584 | -} | |
| 585 | -#endif /* __MINGW32__ */ | |
| 586 | - | |
| 587 | 511 | |
| 588 | 512 | /* |
| 589 | 513 | ** Initialize a new database file with the given schema. If anything |
| 590 | 514 | ** goes wrong, call db_err() to exit. |
| 591 | 515 | */ |
| @@ -598,11 +522,11 @@ | ||
| 598 | 522 | int rc; |
| 599 | 523 | const char *zSql; |
| 600 | 524 | va_list ap; |
| 601 | 525 | |
| 602 | 526 | #ifdef __MINGW32__ |
| 603 | - zFileName = mbcsToUtf8(zFileName); | |
| 527 | + zFileName = sqlite3_win32_mbcs_to_utf8(zFileName); | |
| 604 | 528 | #endif |
| 605 | 529 | rc = sqlite3_open(zFileName, &db); |
| 606 | 530 | if( rc!=SQLITE_OK ){ |
| 607 | 531 | db_err(sqlite3_errmsg(db)); |
| 608 | 532 | } |
| @@ -633,11 +557,11 @@ | ||
| 633 | 557 | const char *zVfs; |
| 634 | 558 | sqlite3 *db; |
| 635 | 559 | |
| 636 | 560 | zVfs = getenv("FOSSIL_VFS"); |
| 637 | 561 | #ifdef __MINGW32__ |
| 638 | - zDbName = mbcsToUtf8(zDbName); | |
| 562 | + zDbName = sqlite3_win32_mbcs_to_utf8(zDbName); | |
| 639 | 563 | #endif |
| 640 | 564 | rc = sqlite3_open_v2( |
| 641 | 565 | zDbName, &db, |
| 642 | 566 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, |
| 643 | 567 | zVfs |
| @@ -659,11 +583,11 @@ | ||
| 659 | 583 | if( !g.db ){ |
| 660 | 584 | g.db = openDatabase(zDbName); |
| 661 | 585 | db_connection_init(); |
| 662 | 586 | }else{ |
| 663 | 587 | #ifdef __MINGW32__ |
| 664 | - zDbName = mbcsToUtf8(zDbName); | |
| 588 | + zDbName = sqlite3_win32_mbcs_to_utf8(zDbName); | |
| 665 | 589 | #endif |
| 666 | 590 | db_multi_exec("ATTACH DATABASE %Q AS %s", zDbName, zLabel); |
| 667 | 591 | } |
| 668 | 592 | } |
| 669 | 593 | |
| @@ -866,11 +790,11 @@ | ||
| 866 | 790 | if( g.repositoryOpen ){ |
| 867 | 791 | return; |
| 868 | 792 | } |
| 869 | 793 | rep_not_found: |
| 870 | 794 | if( errIfNotFound ){ |
| 871 | - fossil_fatal("use --repository or -R to specific the repository database"); | |
| 795 | + fossil_fatal("use --repository or -R to specify the repository database"); | |
| 872 | 796 | } |
| 873 | 797 | } |
| 874 | 798 | |
| 875 | 799 | /* |
| 876 | 800 | ** Open the local database. If unable, exit with an error. |
| @@ -916,13 +840,16 @@ | ||
| 916 | 840 | } |
| 917 | 841 | |
| 918 | 842 | /* |
| 919 | 843 | ** Create the default user accounts in the USER table. |
| 920 | 844 | */ |
| 921 | -void db_create_default_users(int setupUserOnly){ | |
| 845 | +void db_create_default_users(int setupUserOnly, const char *zDefaultUser){ | |
| 922 | 846 | const char *zUser; |
| 923 | 847 | zUser = db_get("default-user", 0); |
| 848 | + if( zUser==0 ){ | |
| 849 | + zUser = zDefaultUser; | |
| 850 | + } | |
| 924 | 851 | if( zUser==0 ){ |
| 925 | 852 | #ifdef __MINGW32__ |
| 926 | 853 | zUser = getenv("USERNAME"); |
| 927 | 854 | #else |
| 928 | 855 | zUser = getenv("USER"); |
| @@ -958,11 +885,11 @@ | ||
| 958 | 885 | ** The zInitialDate parameter determines the date of the initial check-in |
| 959 | 886 | ** that is automatically created. If zInitialDate is 0 then no initial |
| 960 | 887 | ** check-in is created. The makeServerCodes flag determines whether or |
| 961 | 888 | ** not server and project codes are invented for this repository. |
| 962 | 889 | */ |
| 963 | -void db_initial_setup (const char *zInitialDate, int makeServerCodes){ | |
| 890 | +void db_initial_setup (const char *zInitialDate, const char *zDefaultUser, int makeServerCodes){ | |
| 964 | 891 | char *zDate; |
| 965 | 892 | Blob hash; |
| 966 | 893 | Blob manifest; |
| 967 | 894 | |
| 968 | 895 | db_set("content-schema", CONTENT_SCHEMA, 0); |
| @@ -975,11 +902,11 @@ | ||
| 975 | 902 | " VALUES('project-code', lower(hex(randomblob(20))));" |
| 976 | 903 | ); |
| 977 | 904 | } |
| 978 | 905 | if( !db_is_global("autosync") ) db_set_int("autosync", 1, 0); |
| 979 | 906 | if( !db_is_global("localauth") ) db_set_int("localauth", 0, 0); |
| 980 | - db_create_default_users(0); | |
| 907 | + db_create_default_users(0, zDefaultUser); | |
| 981 | 908 | user_select(); |
| 982 | 909 | |
| 983 | 910 | if( zInitialDate ){ |
| 984 | 911 | int rid; |
| 985 | 912 | blob_zero(&manifest); |
| @@ -1002,30 +929,41 @@ | ||
| 1002 | 929 | } |
| 1003 | 930 | |
| 1004 | 931 | /* |
| 1005 | 932 | ** COMMAND: new |
| 1006 | 933 | ** |
| 1007 | -** Usage: %fossil new FILENAME | |
| 934 | +** Usage: %fossil new ?OPTIONS? FILENAME | |
| 1008 | 935 | ** |
| 1009 | 936 | ** Create a repository for a new project in the file named FILENAME. |
| 1010 | 937 | ** This command is distinct from "clone". The "clone" command makes |
| 1011 | 938 | ** a copy of an existing project. This command starts a new project. |
| 939 | +** | |
| 940 | +** By default, your current login name is used to create the default | |
| 941 | +** admin user. This can be overridden using the -A|--admin-user | |
| 942 | +** parameter. | |
| 943 | +** | |
| 944 | +** Options: | |
| 945 | +** | |
| 946 | +** --admin-user|-A USERNAME | |
| 947 | +** | |
| 1012 | 948 | */ |
| 1013 | 949 | void create_repository_cmd(void){ |
| 1014 | 950 | char *zPassword; |
| 1015 | 951 | const char *zDate; /* Date of the initial check-in */ |
| 952 | + const char *zDefaultUser; /* Optional name of the default user */ | |
| 1016 | 953 | |
| 1017 | 954 | zDate = find_option("date-override",0,1); |
| 955 | + zDefaultUser = find_option("admin-user","A",1); | |
| 1018 | 956 | if( zDate==0 ) zDate = "now"; |
| 1019 | 957 | if( g.argc!=3 ){ |
| 1020 | 958 | usage("REPOSITORY-NAME"); |
| 1021 | 959 | } |
| 1022 | 960 | db_create_repository(g.argv[2]); |
| 1023 | 961 | db_open_repository(g.argv[2]); |
| 1024 | 962 | db_open_config(0); |
| 1025 | 963 | db_begin_transaction(); |
| 1026 | - db_initial_setup(zDate, 1); | |
| 964 | + db_initial_setup(zDate, zDefaultUser, 1); | |
| 1027 | 965 | db_end_transaction(0); |
| 1028 | 966 | printf("project-id: %s\n", db_get("project-code", 0)); |
| 1029 | 967 | printf("server-id: %s\n", db_get("server-code", 0)); |
| 1030 | 968 | zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin); |
| 1031 | 969 | printf("admin-user: %s (initial password is \"%s\")\n", g.zLogin, zPassword); |
| 1032 | 970 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -506,86 +506,10 @@ | |
| 506 | z = mprintf("%s", sqlite3_column_text(s.pStmt, 0)); |
| 507 | } |
| 508 | db_finalize(&s); |
| 509 | return z; |
| 510 | } |
| 511 | |
| 512 | #ifdef __MINGW32__ |
| 513 | /* |
| 514 | ** These routines (copied out of the os_win.c driver for SQLite) convert |
| 515 | ** character strings in various microsoft multi-byte character formats |
| 516 | ** into UTF-8. Fossil and SQLite always use only UTF-8 internally. These |
| 517 | ** routines are needed in order to convert from the default character set |
| 518 | ** currently in use by windows into UTF-8 when strings are imported from |
| 519 | ** the outside world. |
| 520 | */ |
| 521 | /* |
| 522 | ** Convert microsoft unicode to UTF-8. Space to hold the returned string is |
| 523 | ** obtained from malloc(). |
| 524 | ** Copied from sqlite3.c as is (petr) |
| 525 | */ |
| 526 | static char *unicodeToUtf8(const WCHAR *zWideFilename){ |
| 527 | int nByte; |
| 528 | char *zFilename; |
| 529 | |
| 530 | nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0); |
| 531 | zFilename = malloc( nByte ); |
| 532 | if( zFilename==0 ){ |
| 533 | return 0; |
| 534 | } |
| 535 | nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte, |
| 536 | 0, 0); |
| 537 | if( nByte == 0 ){ |
| 538 | free(zFilename); |
| 539 | zFilename = 0; |
| 540 | } |
| 541 | return zFilename; |
| 542 | } |
| 543 | /* |
| 544 | ** Convert an ansi string to microsoft unicode, based on the |
| 545 | ** current codepage settings for file apis. |
| 546 | ** |
| 547 | ** Space to hold the returned string is obtained |
| 548 | ** from malloc. |
| 549 | */ |
| 550 | static WCHAR *mbcsToUnicode(const char *zFilename){ |
| 551 | int nByte; |
| 552 | WCHAR *zMbcsFilename; |
| 553 | int codepage = CP_ACP; |
| 554 | |
| 555 | nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, NULL,0)*sizeof(WCHAR); |
| 556 | zMbcsFilename = malloc( nByte*sizeof(zMbcsFilename[0]) ); |
| 557 | if( zMbcsFilename==0 ){ |
| 558 | return 0; |
| 559 | } |
| 560 | |
| 561 | nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, zMbcsFilename, nByte); |
| 562 | if( nByte==0 ){ |
| 563 | free(zMbcsFilename); |
| 564 | zMbcsFilename = 0; |
| 565 | } |
| 566 | return zMbcsFilename; |
| 567 | } |
| 568 | /* |
| 569 | ** Convert multibyte character string to UTF-8. Space to hold the |
| 570 | ** returned string is obtained from malloc(). |
| 571 | */ |
| 572 | static char *mbcsToUtf8(const char *zFilename){ |
| 573 | char *zFilenameUtf8; |
| 574 | WCHAR *zTmpWide; |
| 575 | |
| 576 | zTmpWide = mbcsToUnicode(zFilename); |
| 577 | if( zTmpWide==0 ){ |
| 578 | return 0; |
| 579 | } |
| 580 | |
| 581 | zFilenameUtf8 = unicodeToUtf8(zTmpWide); |
| 582 | free(zTmpWide); |
| 583 | return zFilenameUtf8; |
| 584 | } |
| 585 | #endif /* __MINGW32__ */ |
| 586 | |
| 587 | |
| 588 | /* |
| 589 | ** Initialize a new database file with the given schema. If anything |
| 590 | ** goes wrong, call db_err() to exit. |
| 591 | */ |
| @@ -598,11 +522,11 @@ | |
| 598 | int rc; |
| 599 | const char *zSql; |
| 600 | va_list ap; |
| 601 | |
| 602 | #ifdef __MINGW32__ |
| 603 | zFileName = mbcsToUtf8(zFileName); |
| 604 | #endif |
| 605 | rc = sqlite3_open(zFileName, &db); |
| 606 | if( rc!=SQLITE_OK ){ |
| 607 | db_err(sqlite3_errmsg(db)); |
| 608 | } |
| @@ -633,11 +557,11 @@ | |
| 633 | const char *zVfs; |
| 634 | sqlite3 *db; |
| 635 | |
| 636 | zVfs = getenv("FOSSIL_VFS"); |
| 637 | #ifdef __MINGW32__ |
| 638 | zDbName = mbcsToUtf8(zDbName); |
| 639 | #endif |
| 640 | rc = sqlite3_open_v2( |
| 641 | zDbName, &db, |
| 642 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, |
| 643 | zVfs |
| @@ -659,11 +583,11 @@ | |
| 659 | if( !g.db ){ |
| 660 | g.db = openDatabase(zDbName); |
| 661 | db_connection_init(); |
| 662 | }else{ |
| 663 | #ifdef __MINGW32__ |
| 664 | zDbName = mbcsToUtf8(zDbName); |
| 665 | #endif |
| 666 | db_multi_exec("ATTACH DATABASE %Q AS %s", zDbName, zLabel); |
| 667 | } |
| 668 | } |
| 669 | |
| @@ -866,11 +790,11 @@ | |
| 866 | if( g.repositoryOpen ){ |
| 867 | return; |
| 868 | } |
| 869 | rep_not_found: |
| 870 | if( errIfNotFound ){ |
| 871 | fossil_fatal("use --repository or -R to specific the repository database"); |
| 872 | } |
| 873 | } |
| 874 | |
| 875 | /* |
| 876 | ** Open the local database. If unable, exit with an error. |
| @@ -916,13 +840,16 @@ | |
| 916 | } |
| 917 | |
| 918 | /* |
| 919 | ** Create the default user accounts in the USER table. |
| 920 | */ |
| 921 | void db_create_default_users(int setupUserOnly){ |
| 922 | const char *zUser; |
| 923 | zUser = db_get("default-user", 0); |
| 924 | if( zUser==0 ){ |
| 925 | #ifdef __MINGW32__ |
| 926 | zUser = getenv("USERNAME"); |
| 927 | #else |
| 928 | zUser = getenv("USER"); |
| @@ -958,11 +885,11 @@ | |
| 958 | ** The zInitialDate parameter determines the date of the initial check-in |
| 959 | ** that is automatically created. If zInitialDate is 0 then no initial |
| 960 | ** check-in is created. The makeServerCodes flag determines whether or |
| 961 | ** not server and project codes are invented for this repository. |
| 962 | */ |
| 963 | void db_initial_setup (const char *zInitialDate, int makeServerCodes){ |
| 964 | char *zDate; |
| 965 | Blob hash; |
| 966 | Blob manifest; |
| 967 | |
| 968 | db_set("content-schema", CONTENT_SCHEMA, 0); |
| @@ -975,11 +902,11 @@ | |
| 975 | " VALUES('project-code', lower(hex(randomblob(20))));" |
| 976 | ); |
| 977 | } |
| 978 | if( !db_is_global("autosync") ) db_set_int("autosync", 1, 0); |
| 979 | if( !db_is_global("localauth") ) db_set_int("localauth", 0, 0); |
| 980 | db_create_default_users(0); |
| 981 | user_select(); |
| 982 | |
| 983 | if( zInitialDate ){ |
| 984 | int rid; |
| 985 | blob_zero(&manifest); |
| @@ -1002,30 +929,41 @@ | |
| 1002 | } |
| 1003 | |
| 1004 | /* |
| 1005 | ** COMMAND: new |
| 1006 | ** |
| 1007 | ** Usage: %fossil new FILENAME |
| 1008 | ** |
| 1009 | ** Create a repository for a new project in the file named FILENAME. |
| 1010 | ** This command is distinct from "clone". The "clone" command makes |
| 1011 | ** a copy of an existing project. This command starts a new project. |
| 1012 | */ |
| 1013 | void create_repository_cmd(void){ |
| 1014 | char *zPassword; |
| 1015 | const char *zDate; /* Date of the initial check-in */ |
| 1016 | |
| 1017 | zDate = find_option("date-override",0,1); |
| 1018 | if( zDate==0 ) zDate = "now"; |
| 1019 | if( g.argc!=3 ){ |
| 1020 | usage("REPOSITORY-NAME"); |
| 1021 | } |
| 1022 | db_create_repository(g.argv[2]); |
| 1023 | db_open_repository(g.argv[2]); |
| 1024 | db_open_config(0); |
| 1025 | db_begin_transaction(); |
| 1026 | db_initial_setup(zDate, 1); |
| 1027 | db_end_transaction(0); |
| 1028 | printf("project-id: %s\n", db_get("project-code", 0)); |
| 1029 | printf("server-id: %s\n", db_get("server-code", 0)); |
| 1030 | zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin); |
| 1031 | printf("admin-user: %s (initial password is \"%s\")\n", g.zLogin, zPassword); |
| 1032 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -506,86 +506,10 @@ | |
| 506 | z = mprintf("%s", sqlite3_column_text(s.pStmt, 0)); |
| 507 | } |
| 508 | db_finalize(&s); |
| 509 | return z; |
| 510 | } |
| 511 | |
| 512 | /* |
| 513 | ** Initialize a new database file with the given schema. If anything |
| 514 | ** goes wrong, call db_err() to exit. |
| 515 | */ |
| @@ -598,11 +522,11 @@ | |
| 522 | int rc; |
| 523 | const char *zSql; |
| 524 | va_list ap; |
| 525 | |
| 526 | #ifdef __MINGW32__ |
| 527 | zFileName = sqlite3_win32_mbcs_to_utf8(zFileName); |
| 528 | #endif |
| 529 | rc = sqlite3_open(zFileName, &db); |
| 530 | if( rc!=SQLITE_OK ){ |
| 531 | db_err(sqlite3_errmsg(db)); |
| 532 | } |
| @@ -633,11 +557,11 @@ | |
| 557 | const char *zVfs; |
| 558 | sqlite3 *db; |
| 559 | |
| 560 | zVfs = getenv("FOSSIL_VFS"); |
| 561 | #ifdef __MINGW32__ |
| 562 | zDbName = sqlite3_win32_mbcs_to_utf8(zDbName); |
| 563 | #endif |
| 564 | rc = sqlite3_open_v2( |
| 565 | zDbName, &db, |
| 566 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, |
| 567 | zVfs |
| @@ -659,11 +583,11 @@ | |
| 583 | if( !g.db ){ |
| 584 | g.db = openDatabase(zDbName); |
| 585 | db_connection_init(); |
| 586 | }else{ |
| 587 | #ifdef __MINGW32__ |
| 588 | zDbName = sqlite3_win32_mbcs_to_utf8(zDbName); |
| 589 | #endif |
| 590 | db_multi_exec("ATTACH DATABASE %Q AS %s", zDbName, zLabel); |
| 591 | } |
| 592 | } |
| 593 | |
| @@ -866,11 +790,11 @@ | |
| 790 | if( g.repositoryOpen ){ |
| 791 | return; |
| 792 | } |
| 793 | rep_not_found: |
| 794 | if( errIfNotFound ){ |
| 795 | fossil_fatal("use --repository or -R to specify the repository database"); |
| 796 | } |
| 797 | } |
| 798 | |
| 799 | /* |
| 800 | ** Open the local database. If unable, exit with an error. |
| @@ -916,13 +840,16 @@ | |
| 840 | } |
| 841 | |
| 842 | /* |
| 843 | ** Create the default user accounts in the USER table. |
| 844 | */ |
| 845 | void db_create_default_users(int setupUserOnly, const char *zDefaultUser){ |
| 846 | const char *zUser; |
| 847 | zUser = db_get("default-user", 0); |
| 848 | if( zUser==0 ){ |
| 849 | zUser = zDefaultUser; |
| 850 | } |
| 851 | if( zUser==0 ){ |
| 852 | #ifdef __MINGW32__ |
| 853 | zUser = getenv("USERNAME"); |
| 854 | #else |
| 855 | zUser = getenv("USER"); |
| @@ -958,11 +885,11 @@ | |
| 885 | ** The zInitialDate parameter determines the date of the initial check-in |
| 886 | ** that is automatically created. If zInitialDate is 0 then no initial |
| 887 | ** check-in is created. The makeServerCodes flag determines whether or |
| 888 | ** not server and project codes are invented for this repository. |
| 889 | */ |
| 890 | void db_initial_setup (const char *zInitialDate, const char *zDefaultUser, int makeServerCodes){ |
| 891 | char *zDate; |
| 892 | Blob hash; |
| 893 | Blob manifest; |
| 894 | |
| 895 | db_set("content-schema", CONTENT_SCHEMA, 0); |
| @@ -975,11 +902,11 @@ | |
| 902 | " VALUES('project-code', lower(hex(randomblob(20))));" |
| 903 | ); |
| 904 | } |
| 905 | if( !db_is_global("autosync") ) db_set_int("autosync", 1, 0); |
| 906 | if( !db_is_global("localauth") ) db_set_int("localauth", 0, 0); |
| 907 | db_create_default_users(0, zDefaultUser); |
| 908 | user_select(); |
| 909 | |
| 910 | if( zInitialDate ){ |
| 911 | int rid; |
| 912 | blob_zero(&manifest); |
| @@ -1002,30 +929,41 @@ | |
| 929 | } |
| 930 | |
| 931 | /* |
| 932 | ** COMMAND: new |
| 933 | ** |
| 934 | ** Usage: %fossil new ?OPTIONS? FILENAME |
| 935 | ** |
| 936 | ** Create a repository for a new project in the file named FILENAME. |
| 937 | ** This command is distinct from "clone". The "clone" command makes |
| 938 | ** a copy of an existing project. This command starts a new project. |
| 939 | ** |
| 940 | ** By default, your current login name is used to create the default |
| 941 | ** admin user. This can be overridden using the -A|--admin-user |
| 942 | ** parameter. |
| 943 | ** |
| 944 | ** Options: |
| 945 | ** |
| 946 | ** --admin-user|-A USERNAME |
| 947 | ** |
| 948 | */ |
| 949 | void create_repository_cmd(void){ |
| 950 | char *zPassword; |
| 951 | const char *zDate; /* Date of the initial check-in */ |
| 952 | const char *zDefaultUser; /* Optional name of the default user */ |
| 953 | |
| 954 | zDate = find_option("date-override",0,1); |
| 955 | zDefaultUser = find_option("admin-user","A",1); |
| 956 | if( zDate==0 ) zDate = "now"; |
| 957 | if( g.argc!=3 ){ |
| 958 | usage("REPOSITORY-NAME"); |
| 959 | } |
| 960 | db_create_repository(g.argv[2]); |
| 961 | db_open_repository(g.argv[2]); |
| 962 | db_open_config(0); |
| 963 | db_begin_transaction(); |
| 964 | db_initial_setup(zDate, zDefaultUser, 1); |
| 965 | db_end_transaction(0); |
| 966 | printf("project-id: %s\n", db_get("project-code", 0)); |
| 967 | printf("server-id: %s\n", db_get("server-code", 0)); |
| 968 | zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin); |
| 969 | printf("admin-user: %s (initial password is \"%s\")\n", g.zLogin, zPassword); |
| 970 |
M
src/db.c
+23
-85
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -506,86 +506,10 @@ | ||
| 506 | 506 | z = mprintf("%s", sqlite3_column_text(s.pStmt, 0)); |
| 507 | 507 | } |
| 508 | 508 | db_finalize(&s); |
| 509 | 509 | return z; |
| 510 | 510 | } |
| 511 | - | |
| 512 | -#ifdef __MINGW32__ | |
| 513 | -/* | |
| 514 | -** These routines (copied out of the os_win.c driver for SQLite) convert | |
| 515 | -** character strings in various microsoft multi-byte character formats | |
| 516 | -** into UTF-8. Fossil and SQLite always use only UTF-8 internally. These | |
| 517 | -** routines are needed in order to convert from the default character set | |
| 518 | -** currently in use by windows into UTF-8 when strings are imported from | |
| 519 | -** the outside world. | |
| 520 | -*/ | |
| 521 | -/* | |
| 522 | -** Convert microsoft unicode to UTF-8. Space to hold the returned string is | |
| 523 | -** obtained from malloc(). | |
| 524 | -** Copied from sqlite3.c as is (petr) | |
| 525 | -*/ | |
| 526 | -static char *unicodeToUtf8(const WCHAR *zWideFilename){ | |
| 527 | - int nByte; | |
| 528 | - char *zFilename; | |
| 529 | - | |
| 530 | - nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0); | |
| 531 | - zFilename = malloc( nByte ); | |
| 532 | - if( zFilename==0 ){ | |
| 533 | - return 0; | |
| 534 | - } | |
| 535 | - nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte, | |
| 536 | - 0, 0); | |
| 537 | - if( nByte == 0 ){ | |
| 538 | - free(zFilename); | |
| 539 | - zFilename = 0; | |
| 540 | - } | |
| 541 | - return zFilename; | |
| 542 | -} | |
| 543 | -/* | |
| 544 | -** Convert an ansi string to microsoft unicode, based on the | |
| 545 | -** current codepage settings for file apis. | |
| 546 | -** | |
| 547 | -** Space to hold the returned string is obtained | |
| 548 | -** from malloc. | |
| 549 | -*/ | |
| 550 | -static WCHAR *mbcsToUnicode(const char *zFilename){ | |
| 551 | - int nByte; | |
| 552 | - WCHAR *zMbcsFilename; | |
| 553 | - int codepage = CP_ACP; | |
| 554 | - | |
| 555 | - nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, NULL,0)*sizeof(WCHAR); | |
| 556 | - zMbcsFilename = malloc( nByte*sizeof(zMbcsFilename[0]) ); | |
| 557 | - if( zMbcsFilename==0 ){ | |
| 558 | - return 0; | |
| 559 | - } | |
| 560 | - | |
| 561 | - nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, zMbcsFilename, nByte); | |
| 562 | - if( nByte==0 ){ | |
| 563 | - free(zMbcsFilename); | |
| 564 | - zMbcsFilename = 0; | |
| 565 | - } | |
| 566 | - return zMbcsFilename; | |
| 567 | -} | |
| 568 | -/* | |
| 569 | -** Convert multibyte character string to UTF-8. Space to hold the | |
| 570 | -** returned string is obtained from malloc(). | |
| 571 | -*/ | |
| 572 | -static char *mbcsToUtf8(const char *zFilename){ | |
| 573 | - char *zFilenameUtf8; | |
| 574 | - WCHAR *zTmpWide; | |
| 575 | - | |
| 576 | - zTmpWide = mbcsToUnicode(zFilename); | |
| 577 | - if( zTmpWide==0 ){ | |
| 578 | - return 0; | |
| 579 | - } | |
| 580 | - | |
| 581 | - zFilenameUtf8 = unicodeToUtf8(zTmpWide); | |
| 582 | - free(zTmpWide); | |
| 583 | - return zFilenameUtf8; | |
| 584 | -} | |
| 585 | -#endif /* __MINGW32__ */ | |
| 586 | - | |
| 587 | 511 | |
| 588 | 512 | /* |
| 589 | 513 | ** Initialize a new database file with the given schema. If anything |
| 590 | 514 | ** goes wrong, call db_err() to exit. |
| 591 | 515 | */ |
| @@ -598,11 +522,11 @@ | ||
| 598 | 522 | int rc; |
| 599 | 523 | const char *zSql; |
| 600 | 524 | va_list ap; |
| 601 | 525 | |
| 602 | 526 | #ifdef __MINGW32__ |
| 603 | - zFileName = mbcsToUtf8(zFileName); | |
| 527 | + zFileName = sqlite3_win32_mbcs_to_utf8(zFileName); | |
| 604 | 528 | #endif |
| 605 | 529 | rc = sqlite3_open(zFileName, &db); |
| 606 | 530 | if( rc!=SQLITE_OK ){ |
| 607 | 531 | db_err(sqlite3_errmsg(db)); |
| 608 | 532 | } |
| @@ -633,11 +557,11 @@ | ||
| 633 | 557 | const char *zVfs; |
| 634 | 558 | sqlite3 *db; |
| 635 | 559 | |
| 636 | 560 | zVfs = getenv("FOSSIL_VFS"); |
| 637 | 561 | #ifdef __MINGW32__ |
| 638 | - zDbName = mbcsToUtf8(zDbName); | |
| 562 | + zDbName = sqlite3_win32_mbcs_to_utf8(zDbName); | |
| 639 | 563 | #endif |
| 640 | 564 | rc = sqlite3_open_v2( |
| 641 | 565 | zDbName, &db, |
| 642 | 566 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, |
| 643 | 567 | zVfs |
| @@ -659,11 +583,11 @@ | ||
| 659 | 583 | if( !g.db ){ |
| 660 | 584 | g.db = openDatabase(zDbName); |
| 661 | 585 | db_connection_init(); |
| 662 | 586 | }else{ |
| 663 | 587 | #ifdef __MINGW32__ |
| 664 | - zDbName = mbcsToUtf8(zDbName); | |
| 588 | + zDbName = sqlite3_win32_mbcs_to_utf8(zDbName); | |
| 665 | 589 | #endif |
| 666 | 590 | db_multi_exec("ATTACH DATABASE %Q AS %s", zDbName, zLabel); |
| 667 | 591 | } |
| 668 | 592 | } |
| 669 | 593 | |
| @@ -866,11 +790,11 @@ | ||
| 866 | 790 | if( g.repositoryOpen ){ |
| 867 | 791 | return; |
| 868 | 792 | } |
| 869 | 793 | rep_not_found: |
| 870 | 794 | if( errIfNotFound ){ |
| 871 | - fossil_fatal("use --repository or -R to specific the repository database"); | |
| 795 | + fossil_fatal("use --repository or -R to specify the repository database"); | |
| 872 | 796 | } |
| 873 | 797 | } |
| 874 | 798 | |
| 875 | 799 | /* |
| 876 | 800 | ** Open the local database. If unable, exit with an error. |
| @@ -916,13 +840,16 @@ | ||
| 916 | 840 | } |
| 917 | 841 | |
| 918 | 842 | /* |
| 919 | 843 | ** Create the default user accounts in the USER table. |
| 920 | 844 | */ |
| 921 | -void db_create_default_users(int setupUserOnly){ | |
| 845 | +void db_create_default_users(int setupUserOnly, const char *zDefaultUser){ | |
| 922 | 846 | const char *zUser; |
| 923 | 847 | zUser = db_get("default-user", 0); |
| 848 | + if( zUser==0 ){ | |
| 849 | + zUser = zDefaultUser; | |
| 850 | + } | |
| 924 | 851 | if( zUser==0 ){ |
| 925 | 852 | #ifdef __MINGW32__ |
| 926 | 853 | zUser = getenv("USERNAME"); |
| 927 | 854 | #else |
| 928 | 855 | zUser = getenv("USER"); |
| @@ -958,11 +885,11 @@ | ||
| 958 | 885 | ** The zInitialDate parameter determines the date of the initial check-in |
| 959 | 886 | ** that is automatically created. If zInitialDate is 0 then no initial |
| 960 | 887 | ** check-in is created. The makeServerCodes flag determines whether or |
| 961 | 888 | ** not server and project codes are invented for this repository. |
| 962 | 889 | */ |
| 963 | -void db_initial_setup (const char *zInitialDate, int makeServerCodes){ | |
| 890 | +void db_initial_setup (const char *zInitialDate, const char *zDefaultUser, int makeServerCodes){ | |
| 964 | 891 | char *zDate; |
| 965 | 892 | Blob hash; |
| 966 | 893 | Blob manifest; |
| 967 | 894 | |
| 968 | 895 | db_set("content-schema", CONTENT_SCHEMA, 0); |
| @@ -975,11 +902,11 @@ | ||
| 975 | 902 | " VALUES('project-code', lower(hex(randomblob(20))));" |
| 976 | 903 | ); |
| 977 | 904 | } |
| 978 | 905 | if( !db_is_global("autosync") ) db_set_int("autosync", 1, 0); |
| 979 | 906 | if( !db_is_global("localauth") ) db_set_int("localauth", 0, 0); |
| 980 | - db_create_default_users(0); | |
| 907 | + db_create_default_users(0, zDefaultUser); | |
| 981 | 908 | user_select(); |
| 982 | 909 | |
| 983 | 910 | if( zInitialDate ){ |
| 984 | 911 | int rid; |
| 985 | 912 | blob_zero(&manifest); |
| @@ -1002,30 +929,41 @@ | ||
| 1002 | 929 | } |
| 1003 | 930 | |
| 1004 | 931 | /* |
| 1005 | 932 | ** COMMAND: new |
| 1006 | 933 | ** |
| 1007 | -** Usage: %fossil new FILENAME | |
| 934 | +** Usage: %fossil new ?OPTIONS? FILENAME | |
| 1008 | 935 | ** |
| 1009 | 936 | ** Create a repository for a new project in the file named FILENAME. |
| 1010 | 937 | ** This command is distinct from "clone". The "clone" command makes |
| 1011 | 938 | ** a copy of an existing project. This command starts a new project. |
| 939 | +** | |
| 940 | +** By default, your current login name is used to create the default | |
| 941 | +** admin user. This can be overridden using the -A|--admin-user | |
| 942 | +** parameter. | |
| 943 | +** | |
| 944 | +** Options: | |
| 945 | +** | |
| 946 | +** --admin-user|-A USERNAME | |
| 947 | +** | |
| 1012 | 948 | */ |
| 1013 | 949 | void create_repository_cmd(void){ |
| 1014 | 950 | char *zPassword; |
| 1015 | 951 | const char *zDate; /* Date of the initial check-in */ |
| 952 | + const char *zDefaultUser; /* Optional name of the default user */ | |
| 1016 | 953 | |
| 1017 | 954 | zDate = find_option("date-override",0,1); |
| 955 | + zDefaultUser = find_option("admin-user","A",1); | |
| 1018 | 956 | if( zDate==0 ) zDate = "now"; |
| 1019 | 957 | if( g.argc!=3 ){ |
| 1020 | 958 | usage("REPOSITORY-NAME"); |
| 1021 | 959 | } |
| 1022 | 960 | db_create_repository(g.argv[2]); |
| 1023 | 961 | db_open_repository(g.argv[2]); |
| 1024 | 962 | db_open_config(0); |
| 1025 | 963 | db_begin_transaction(); |
| 1026 | - db_initial_setup(zDate, 1); | |
| 964 | + db_initial_setup(zDate, zDefaultUser, 1); | |
| 1027 | 965 | db_end_transaction(0); |
| 1028 | 966 | printf("project-id: %s\n", db_get("project-code", 0)); |
| 1029 | 967 | printf("server-id: %s\n", db_get("server-code", 0)); |
| 1030 | 968 | zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin); |
| 1031 | 969 | printf("admin-user: %s (initial password is \"%s\")\n", g.zLogin, zPassword); |
| 1032 | 970 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -506,86 +506,10 @@ | |
| 506 | z = mprintf("%s", sqlite3_column_text(s.pStmt, 0)); |
| 507 | } |
| 508 | db_finalize(&s); |
| 509 | return z; |
| 510 | } |
| 511 | |
| 512 | #ifdef __MINGW32__ |
| 513 | /* |
| 514 | ** These routines (copied out of the os_win.c driver for SQLite) convert |
| 515 | ** character strings in various microsoft multi-byte character formats |
| 516 | ** into UTF-8. Fossil and SQLite always use only UTF-8 internally. These |
| 517 | ** routines are needed in order to convert from the default character set |
| 518 | ** currently in use by windows into UTF-8 when strings are imported from |
| 519 | ** the outside world. |
| 520 | */ |
| 521 | /* |
| 522 | ** Convert microsoft unicode to UTF-8. Space to hold the returned string is |
| 523 | ** obtained from malloc(). |
| 524 | ** Copied from sqlite3.c as is (petr) |
| 525 | */ |
| 526 | static char *unicodeToUtf8(const WCHAR *zWideFilename){ |
| 527 | int nByte; |
| 528 | char *zFilename; |
| 529 | |
| 530 | nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0); |
| 531 | zFilename = malloc( nByte ); |
| 532 | if( zFilename==0 ){ |
| 533 | return 0; |
| 534 | } |
| 535 | nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte, |
| 536 | 0, 0); |
| 537 | if( nByte == 0 ){ |
| 538 | free(zFilename); |
| 539 | zFilename = 0; |
| 540 | } |
| 541 | return zFilename; |
| 542 | } |
| 543 | /* |
| 544 | ** Convert an ansi string to microsoft unicode, based on the |
| 545 | ** current codepage settings for file apis. |
| 546 | ** |
| 547 | ** Space to hold the returned string is obtained |
| 548 | ** from malloc. |
| 549 | */ |
| 550 | static WCHAR *mbcsToUnicode(const char *zFilename){ |
| 551 | int nByte; |
| 552 | WCHAR *zMbcsFilename; |
| 553 | int codepage = CP_ACP; |
| 554 | |
| 555 | nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, NULL,0)*sizeof(WCHAR); |
| 556 | zMbcsFilename = malloc( nByte*sizeof(zMbcsFilename[0]) ); |
| 557 | if( zMbcsFilename==0 ){ |
| 558 | return 0; |
| 559 | } |
| 560 | |
| 561 | nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, zMbcsFilename, nByte); |
| 562 | if( nByte==0 ){ |
| 563 | free(zMbcsFilename); |
| 564 | zMbcsFilename = 0; |
| 565 | } |
| 566 | return zMbcsFilename; |
| 567 | } |
| 568 | /* |
| 569 | ** Convert multibyte character string to UTF-8. Space to hold the |
| 570 | ** returned string is obtained from malloc(). |
| 571 | */ |
| 572 | static char *mbcsToUtf8(const char *zFilename){ |
| 573 | char *zFilenameUtf8; |
| 574 | WCHAR *zTmpWide; |
| 575 | |
| 576 | zTmpWide = mbcsToUnicode(zFilename); |
| 577 | if( zTmpWide==0 ){ |
| 578 | return 0; |
| 579 | } |
| 580 | |
| 581 | zFilenameUtf8 = unicodeToUtf8(zTmpWide); |
| 582 | free(zTmpWide); |
| 583 | return zFilenameUtf8; |
| 584 | } |
| 585 | #endif /* __MINGW32__ */ |
| 586 | |
| 587 | |
| 588 | /* |
| 589 | ** Initialize a new database file with the given schema. If anything |
| 590 | ** goes wrong, call db_err() to exit. |
| 591 | */ |
| @@ -598,11 +522,11 @@ | |
| 598 | int rc; |
| 599 | const char *zSql; |
| 600 | va_list ap; |
| 601 | |
| 602 | #ifdef __MINGW32__ |
| 603 | zFileName = mbcsToUtf8(zFileName); |
| 604 | #endif |
| 605 | rc = sqlite3_open(zFileName, &db); |
| 606 | if( rc!=SQLITE_OK ){ |
| 607 | db_err(sqlite3_errmsg(db)); |
| 608 | } |
| @@ -633,11 +557,11 @@ | |
| 633 | const char *zVfs; |
| 634 | sqlite3 *db; |
| 635 | |
| 636 | zVfs = getenv("FOSSIL_VFS"); |
| 637 | #ifdef __MINGW32__ |
| 638 | zDbName = mbcsToUtf8(zDbName); |
| 639 | #endif |
| 640 | rc = sqlite3_open_v2( |
| 641 | zDbName, &db, |
| 642 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, |
| 643 | zVfs |
| @@ -659,11 +583,11 @@ | |
| 659 | if( !g.db ){ |
| 660 | g.db = openDatabase(zDbName); |
| 661 | db_connection_init(); |
| 662 | }else{ |
| 663 | #ifdef __MINGW32__ |
| 664 | zDbName = mbcsToUtf8(zDbName); |
| 665 | #endif |
| 666 | db_multi_exec("ATTACH DATABASE %Q AS %s", zDbName, zLabel); |
| 667 | } |
| 668 | } |
| 669 | |
| @@ -866,11 +790,11 @@ | |
| 866 | if( g.repositoryOpen ){ |
| 867 | return; |
| 868 | } |
| 869 | rep_not_found: |
| 870 | if( errIfNotFound ){ |
| 871 | fossil_fatal("use --repository or -R to specific the repository database"); |
| 872 | } |
| 873 | } |
| 874 | |
| 875 | /* |
| 876 | ** Open the local database. If unable, exit with an error. |
| @@ -916,13 +840,16 @@ | |
| 916 | } |
| 917 | |
| 918 | /* |
| 919 | ** Create the default user accounts in the USER table. |
| 920 | */ |
| 921 | void db_create_default_users(int setupUserOnly){ |
| 922 | const char *zUser; |
| 923 | zUser = db_get("default-user", 0); |
| 924 | if( zUser==0 ){ |
| 925 | #ifdef __MINGW32__ |
| 926 | zUser = getenv("USERNAME"); |
| 927 | #else |
| 928 | zUser = getenv("USER"); |
| @@ -958,11 +885,11 @@ | |
| 958 | ** The zInitialDate parameter determines the date of the initial check-in |
| 959 | ** that is automatically created. If zInitialDate is 0 then no initial |
| 960 | ** check-in is created. The makeServerCodes flag determines whether or |
| 961 | ** not server and project codes are invented for this repository. |
| 962 | */ |
| 963 | void db_initial_setup (const char *zInitialDate, int makeServerCodes){ |
| 964 | char *zDate; |
| 965 | Blob hash; |
| 966 | Blob manifest; |
| 967 | |
| 968 | db_set("content-schema", CONTENT_SCHEMA, 0); |
| @@ -975,11 +902,11 @@ | |
| 975 | " VALUES('project-code', lower(hex(randomblob(20))));" |
| 976 | ); |
| 977 | } |
| 978 | if( !db_is_global("autosync") ) db_set_int("autosync", 1, 0); |
| 979 | if( !db_is_global("localauth") ) db_set_int("localauth", 0, 0); |
| 980 | db_create_default_users(0); |
| 981 | user_select(); |
| 982 | |
| 983 | if( zInitialDate ){ |
| 984 | int rid; |
| 985 | blob_zero(&manifest); |
| @@ -1002,30 +929,41 @@ | |
| 1002 | } |
| 1003 | |
| 1004 | /* |
| 1005 | ** COMMAND: new |
| 1006 | ** |
| 1007 | ** Usage: %fossil new FILENAME |
| 1008 | ** |
| 1009 | ** Create a repository for a new project in the file named FILENAME. |
| 1010 | ** This command is distinct from "clone". The "clone" command makes |
| 1011 | ** a copy of an existing project. This command starts a new project. |
| 1012 | */ |
| 1013 | void create_repository_cmd(void){ |
| 1014 | char *zPassword; |
| 1015 | const char *zDate; /* Date of the initial check-in */ |
| 1016 | |
| 1017 | zDate = find_option("date-override",0,1); |
| 1018 | if( zDate==0 ) zDate = "now"; |
| 1019 | if( g.argc!=3 ){ |
| 1020 | usage("REPOSITORY-NAME"); |
| 1021 | } |
| 1022 | db_create_repository(g.argv[2]); |
| 1023 | db_open_repository(g.argv[2]); |
| 1024 | db_open_config(0); |
| 1025 | db_begin_transaction(); |
| 1026 | db_initial_setup(zDate, 1); |
| 1027 | db_end_transaction(0); |
| 1028 | printf("project-id: %s\n", db_get("project-code", 0)); |
| 1029 | printf("server-id: %s\n", db_get("server-code", 0)); |
| 1030 | zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin); |
| 1031 | printf("admin-user: %s (initial password is \"%s\")\n", g.zLogin, zPassword); |
| 1032 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -506,86 +506,10 @@ | |
| 506 | z = mprintf("%s", sqlite3_column_text(s.pStmt, 0)); |
| 507 | } |
| 508 | db_finalize(&s); |
| 509 | return z; |
| 510 | } |
| 511 | |
| 512 | /* |
| 513 | ** Initialize a new database file with the given schema. If anything |
| 514 | ** goes wrong, call db_err() to exit. |
| 515 | */ |
| @@ -598,11 +522,11 @@ | |
| 522 | int rc; |
| 523 | const char *zSql; |
| 524 | va_list ap; |
| 525 | |
| 526 | #ifdef __MINGW32__ |
| 527 | zFileName = sqlite3_win32_mbcs_to_utf8(zFileName); |
| 528 | #endif |
| 529 | rc = sqlite3_open(zFileName, &db); |
| 530 | if( rc!=SQLITE_OK ){ |
| 531 | db_err(sqlite3_errmsg(db)); |
| 532 | } |
| @@ -633,11 +557,11 @@ | |
| 557 | const char *zVfs; |
| 558 | sqlite3 *db; |
| 559 | |
| 560 | zVfs = getenv("FOSSIL_VFS"); |
| 561 | #ifdef __MINGW32__ |
| 562 | zDbName = sqlite3_win32_mbcs_to_utf8(zDbName); |
| 563 | #endif |
| 564 | rc = sqlite3_open_v2( |
| 565 | zDbName, &db, |
| 566 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, |
| 567 | zVfs |
| @@ -659,11 +583,11 @@ | |
| 583 | if( !g.db ){ |
| 584 | g.db = openDatabase(zDbName); |
| 585 | db_connection_init(); |
| 586 | }else{ |
| 587 | #ifdef __MINGW32__ |
| 588 | zDbName = sqlite3_win32_mbcs_to_utf8(zDbName); |
| 589 | #endif |
| 590 | db_multi_exec("ATTACH DATABASE %Q AS %s", zDbName, zLabel); |
| 591 | } |
| 592 | } |
| 593 | |
| @@ -866,11 +790,11 @@ | |
| 790 | if( g.repositoryOpen ){ |
| 791 | return; |
| 792 | } |
| 793 | rep_not_found: |
| 794 | if( errIfNotFound ){ |
| 795 | fossil_fatal("use --repository or -R to specify the repository database"); |
| 796 | } |
| 797 | } |
| 798 | |
| 799 | /* |
| 800 | ** Open the local database. If unable, exit with an error. |
| @@ -916,13 +840,16 @@ | |
| 840 | } |
| 841 | |
| 842 | /* |
| 843 | ** Create the default user accounts in the USER table. |
| 844 | */ |
| 845 | void db_create_default_users(int setupUserOnly, const char *zDefaultUser){ |
| 846 | const char *zUser; |
| 847 | zUser = db_get("default-user", 0); |
| 848 | if( zUser==0 ){ |
| 849 | zUser = zDefaultUser; |
| 850 | } |
| 851 | if( zUser==0 ){ |
| 852 | #ifdef __MINGW32__ |
| 853 | zUser = getenv("USERNAME"); |
| 854 | #else |
| 855 | zUser = getenv("USER"); |
| @@ -958,11 +885,11 @@ | |
| 885 | ** The zInitialDate parameter determines the date of the initial check-in |
| 886 | ** that is automatically created. If zInitialDate is 0 then no initial |
| 887 | ** check-in is created. The makeServerCodes flag determines whether or |
| 888 | ** not server and project codes are invented for this repository. |
| 889 | */ |
| 890 | void db_initial_setup (const char *zInitialDate, const char *zDefaultUser, int makeServerCodes){ |
| 891 | char *zDate; |
| 892 | Blob hash; |
| 893 | Blob manifest; |
| 894 | |
| 895 | db_set("content-schema", CONTENT_SCHEMA, 0); |
| @@ -975,11 +902,11 @@ | |
| 902 | " VALUES('project-code', lower(hex(randomblob(20))));" |
| 903 | ); |
| 904 | } |
| 905 | if( !db_is_global("autosync") ) db_set_int("autosync", 1, 0); |
| 906 | if( !db_is_global("localauth") ) db_set_int("localauth", 0, 0); |
| 907 | db_create_default_users(0, zDefaultUser); |
| 908 | user_select(); |
| 909 | |
| 910 | if( zInitialDate ){ |
| 911 | int rid; |
| 912 | blob_zero(&manifest); |
| @@ -1002,30 +929,41 @@ | |
| 929 | } |
| 930 | |
| 931 | /* |
| 932 | ** COMMAND: new |
| 933 | ** |
| 934 | ** Usage: %fossil new ?OPTIONS? FILENAME |
| 935 | ** |
| 936 | ** Create a repository for a new project in the file named FILENAME. |
| 937 | ** This command is distinct from "clone". The "clone" command makes |
| 938 | ** a copy of an existing project. This command starts a new project. |
| 939 | ** |
| 940 | ** By default, your current login name is used to create the default |
| 941 | ** admin user. This can be overridden using the -A|--admin-user |
| 942 | ** parameter. |
| 943 | ** |
| 944 | ** Options: |
| 945 | ** |
| 946 | ** --admin-user|-A USERNAME |
| 947 | ** |
| 948 | */ |
| 949 | void create_repository_cmd(void){ |
| 950 | char *zPassword; |
| 951 | const char *zDate; /* Date of the initial check-in */ |
| 952 | const char *zDefaultUser; /* Optional name of the default user */ |
| 953 | |
| 954 | zDate = find_option("date-override",0,1); |
| 955 | zDefaultUser = find_option("admin-user","A",1); |
| 956 | if( zDate==0 ) zDate = "now"; |
| 957 | if( g.argc!=3 ){ |
| 958 | usage("REPOSITORY-NAME"); |
| 959 | } |
| 960 | db_create_repository(g.argv[2]); |
| 961 | db_open_repository(g.argv[2]); |
| 962 | db_open_config(0); |
| 963 | db_begin_transaction(); |
| 964 | db_initial_setup(zDate, zDefaultUser, 1); |
| 965 | db_end_transaction(0); |
| 966 | printf("project-id: %s\n", db_get("project-code", 0)); |
| 967 | printf("server-id: %s\n", db_get("server-code", 0)); |
| 968 | zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin); |
| 969 | printf("admin-user: %s (initial password is \"%s\")\n", g.zLogin, zPassword); |
| 970 |
+4
-1
| --- src/delta.c | ||
| +++ src/delta.c | ||
| @@ -30,10 +30,11 @@ | ||
| 30 | 30 | */ |
| 31 | 31 | #include <stdio.h> |
| 32 | 32 | #include <assert.h> |
| 33 | 33 | #include <stdlib.h> |
| 34 | 34 | #include <string.h> |
| 35 | +#include "delta.h" | |
| 35 | 36 | |
| 36 | 37 | /* |
| 37 | 38 | ** Macros for turning debugging printfs on and off |
| 38 | 39 | */ |
| 39 | 40 | #if 0 |
| @@ -62,11 +63,11 @@ | ||
| 62 | 63 | } |
| 63 | 64 | #else |
| 64 | 65 | # define DEBUG2(X) |
| 65 | 66 | #endif |
| 66 | 67 | |
| 67 | - | |
| 68 | +#if INTERFACE | |
| 68 | 69 | /* |
| 69 | 70 | ** The "u32" type must be an unsigned 32-bit integer. Adjust this |
| 70 | 71 | */ |
| 71 | 72 | typedef unsigned int u32; |
| 72 | 73 | |
| @@ -73,10 +74,12 @@ | ||
| 73 | 74 | /* |
| 74 | 75 | ** Must be a 16-bit value |
| 75 | 76 | */ |
| 76 | 77 | typedef short int s16; |
| 77 | 78 | typedef unsigned short int u16; |
| 79 | + | |
| 80 | +#endif /* INTERFACE */ | |
| 78 | 81 | |
| 79 | 82 | /* |
| 80 | 83 | ** The width of a hash window in bytes. The algorithm only works if this |
| 81 | 84 | ** is a power of 2. |
| 82 | 85 | */ |
| 83 | 86 |
| --- src/delta.c | |
| +++ src/delta.c | |
| @@ -30,10 +30,11 @@ | |
| 30 | */ |
| 31 | #include <stdio.h> |
| 32 | #include <assert.h> |
| 33 | #include <stdlib.h> |
| 34 | #include <string.h> |
| 35 | |
| 36 | /* |
| 37 | ** Macros for turning debugging printfs on and off |
| 38 | */ |
| 39 | #if 0 |
| @@ -62,11 +63,11 @@ | |
| 62 | } |
| 63 | #else |
| 64 | # define DEBUG2(X) |
| 65 | #endif |
| 66 | |
| 67 | |
| 68 | /* |
| 69 | ** The "u32" type must be an unsigned 32-bit integer. Adjust this |
| 70 | */ |
| 71 | typedef unsigned int u32; |
| 72 | |
| @@ -73,10 +74,12 @@ | |
| 73 | /* |
| 74 | ** Must be a 16-bit value |
| 75 | */ |
| 76 | typedef short int s16; |
| 77 | typedef unsigned short int u16; |
| 78 | |
| 79 | /* |
| 80 | ** The width of a hash window in bytes. The algorithm only works if this |
| 81 | ** is a power of 2. |
| 82 | */ |
| 83 |
| --- src/delta.c | |
| +++ src/delta.c | |
| @@ -30,10 +30,11 @@ | |
| 30 | */ |
| 31 | #include <stdio.h> |
| 32 | #include <assert.h> |
| 33 | #include <stdlib.h> |
| 34 | #include <string.h> |
| 35 | #include "delta.h" |
| 36 | |
| 37 | /* |
| 38 | ** Macros for turning debugging printfs on and off |
| 39 | */ |
| 40 | #if 0 |
| @@ -62,11 +63,11 @@ | |
| 63 | } |
| 64 | #else |
| 65 | # define DEBUG2(X) |
| 66 | #endif |
| 67 | |
| 68 | #if INTERFACE |
| 69 | /* |
| 70 | ** The "u32" type must be an unsigned 32-bit integer. Adjust this |
| 71 | */ |
| 72 | typedef unsigned int u32; |
| 73 | |
| @@ -73,10 +74,12 @@ | |
| 74 | /* |
| 75 | ** Must be a 16-bit value |
| 76 | */ |
| 77 | typedef short int s16; |
| 78 | typedef unsigned short int u16; |
| 79 | |
| 80 | #endif /* INTERFACE */ |
| 81 | |
| 82 | /* |
| 83 | ** The width of a hash window in bytes. The algorithm only works if this |
| 84 | ** is a power of 2. |
| 85 | */ |
| 86 |
+2
-1
| --- src/file.c | ||
| +++ src/file.c | ||
| @@ -101,11 +101,11 @@ | ||
| 101 | 101 | int file_isdir(const char *zFilename){ |
| 102 | 102 | int rc; |
| 103 | 103 | |
| 104 | 104 | if( zFilename ){ |
| 105 | 105 | char *zFN = mprintf("%s", zFilename); |
| 106 | - file_simplify_name(zFN, strlen(zFN)); | |
| 106 | + file_simplify_name(zFN, -1); | |
| 107 | 107 | rc = getStat(zFN); |
| 108 | 108 | free(zFN); |
| 109 | 109 | }else{ |
| 110 | 110 | rc = getStat(0); |
| 111 | 111 | } |
| @@ -229,10 +229,11 @@ | ||
| 229 | 229 | ** |
| 230 | 230 | ** Changes are made in-place. Return the new name length. |
| 231 | 231 | */ |
| 232 | 232 | int file_simplify_name(char *z, int n){ |
| 233 | 233 | int i, j; |
| 234 | + if( n<0 ) n = strlen(z); | |
| 234 | 235 | #ifdef __MINGW32__ |
| 235 | 236 | for(i=0; i<n; i++){ |
| 236 | 237 | if( z[i]=='\\' ) z[i] = '/'; |
| 237 | 238 | } |
| 238 | 239 | #endif |
| 239 | 240 | |
| 240 | 241 | ADDED src/graph.c |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -101,11 +101,11 @@ | |
| 101 | int file_isdir(const char *zFilename){ |
| 102 | int rc; |
| 103 | |
| 104 | if( zFilename ){ |
| 105 | char *zFN = mprintf("%s", zFilename); |
| 106 | file_simplify_name(zFN, strlen(zFN)); |
| 107 | rc = getStat(zFN); |
| 108 | free(zFN); |
| 109 | }else{ |
| 110 | rc = getStat(0); |
| 111 | } |
| @@ -229,10 +229,11 @@ | |
| 229 | ** |
| 230 | ** Changes are made in-place. Return the new name length. |
| 231 | */ |
| 232 | int file_simplify_name(char *z, int n){ |
| 233 | int i, j; |
| 234 | #ifdef __MINGW32__ |
| 235 | for(i=0; i<n; i++){ |
| 236 | if( z[i]=='\\' ) z[i] = '/'; |
| 237 | } |
| 238 | #endif |
| 239 | |
| 240 | DDED src/graph.c |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -101,11 +101,11 @@ | |
| 101 | int file_isdir(const char *zFilename){ |
| 102 | int rc; |
| 103 | |
| 104 | if( zFilename ){ |
| 105 | char *zFN = mprintf("%s", zFilename); |
| 106 | file_simplify_name(zFN, -1); |
| 107 | rc = getStat(zFN); |
| 108 | free(zFN); |
| 109 | }else{ |
| 110 | rc = getStat(0); |
| 111 | } |
| @@ -229,10 +229,11 @@ | |
| 229 | ** |
| 230 | ** Changes are made in-place. Return the new name length. |
| 231 | */ |
| 232 | int file_simplify_name(char *z, int n){ |
| 233 | int i, j; |
| 234 | if( n<0 ) n = strlen(z); |
| 235 | #ifdef __MINGW32__ |
| 236 | for(i=0; i<n; i++){ |
| 237 | if( z[i]=='\\' ) z[i] = '/'; |
| 238 | } |
| 239 | #endif |
| 240 | |
| 241 | DDED src/graph.c |
+5
| --- a/src/graph.c | ||
| +++ b/src/graph.c | ||
| @@ -0,0 +1,5 @@ | ||
| 1 | +!=isLeaf /* True if tRailrailMap)!=isLeaf,!=isLeaf = isLeafBag allRids; | |
| 2 | + Bag nnRowpRow->isLeaf )else!=isLeaf /* True if tRailrailMap)!=isLeaf,!=isLeaf = isLeafBag allRids; | |
| 3 | + Bag ntop, int btm, u32 inUseMask; | |
| 4 | + int i; return i; | |
| 5 | +=isLeaf /* True if tRailrailMap tRailrailMap)!=isLeaf,!=!=isLeaf /* True if tRailrailMapassert( pDesc!=0 ); |
| --- a/src/graph.c | |
| +++ b/src/graph.c | |
| @@ -0,0 +1,5 @@ | |
| --- a/src/graph.c | |
| +++ b/src/graph.c | |
| @@ -0,0 +1,5 @@ | |
| 1 | !=isLeaf /* True if tRailrailMap)!=isLeaf,!=isLeaf = isLeafBag allRids; |
| 2 | Bag nnRowpRow->isLeaf )else!=isLeaf /* True if tRailrailMap)!=isLeaf,!=isLeaf = isLeafBag allRids; |
| 3 | Bag ntop, int btm, u32 inUseMask; |
| 4 | int i; return i; |
| 5 | =isLeaf /* True if tRailrailMap tRailrailMap)!=isLeaf,!=!=isLeaf /* True if tRailrailMapassert( pDesc!=0 ); |
+177
-54
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -453,11 +453,11 @@ | ||
| 453 | 453 | printf("\n"); |
| 454 | 454 | } |
| 455 | 455 | } |
| 456 | 456 | |
| 457 | 457 | /* |
| 458 | -** COM MAND: commands | |
| 458 | +** COM -off- MAND: commands | |
| 459 | 459 | ** |
| 460 | 460 | ** Usage: %fossil commands |
| 461 | 461 | ** List all supported commands. |
| 462 | 462 | */ |
| 463 | 463 | void cmd_cmd_list(void){ |
| @@ -541,11 +541,11 @@ | ||
| 541 | 541 | putchar('\n'); |
| 542 | 542 | } |
| 543 | 543 | |
| 544 | 544 | /* |
| 545 | 545 | ** Set the g.zBaseURL value to the full URL for the toplevel of |
| 546 | -** the fossil tree. Set g.zHomeURL to g.zBaseURL without the | |
| 546 | +** the fossil tree. Set g.zTop to g.zBaseURL without the | |
| 547 | 547 | ** leading "http://" and the host and port. |
| 548 | 548 | */ |
| 549 | 549 | void set_base_url(void){ |
| 550 | 550 | int i; |
| 551 | 551 | const char *zHost = PD("HTTP_HOST",""); |
| @@ -567,31 +567,125 @@ | ||
| 567 | 567 | ** Send an HTTP redirect back to the designated Index Page. |
| 568 | 568 | */ |
| 569 | 569 | void fossil_redirect_home(void){ |
| 570 | 570 | cgi_redirectf("%s%s", g.zBaseURL, db_get("index-page", "/index")); |
| 571 | 571 | } |
| 572 | + | |
| 573 | +/* | |
| 574 | +** If running as root, chroot to the directory containing the | |
| 575 | +** repository zRepo and then drop root privileges. Return the | |
| 576 | +** new repository name. | |
| 577 | +** | |
| 578 | +** zRepo might be a directory itself. In that case chroot into | |
| 579 | +** the directory zRepo. | |
| 580 | +** | |
| 581 | +** Assume the user-id and group-id of the repository, or if zRepo | |
| 582 | +** is a directory, of that directory. | |
| 583 | +*/ | |
| 584 | +static char *enter_chroot_jail(char *zRepo){ | |
| 585 | +#if !defined(__MINGW32__) | |
| 586 | + if( getuid()==0 ){ | |
| 587 | + int i; | |
| 588 | + struct stat sStat; | |
| 589 | + Blob dir; | |
| 590 | + char *zDir; | |
| 591 | + | |
| 592 | + file_canonical_name(zRepo, &dir); | |
| 593 | + zDir = blob_str(&dir); | |
| 594 | + if( file_isdir(zDir)==1 ){ | |
| 595 | + chdir(zDir); | |
| 596 | + chroot(zDir); | |
| 597 | + zRepo = "/"; | |
| 598 | + }else{ | |
| 599 | + for(i=strlen(zDir)-1; i>0 && zDir[i]!='/'; i--){} | |
| 600 | + if( zDir[i]!='/' ) fossil_panic("bad repository name: %s", zRepo); | |
| 601 | + zDir[i] = 0; | |
| 602 | + chdir(zDir); | |
| 603 | + chroot(zDir); | |
| 604 | + zDir[i] = '/'; | |
| 605 | + zRepo = &zDir[i]; | |
| 606 | + } | |
| 607 | + if( stat(zRepo, &sStat)!=0 ){ | |
| 608 | + fossil_fatal("cannot stat() repository: %s", zRepo); | |
| 609 | + } | |
| 610 | + setgid(sStat.st_gid); | |
| 611 | + setuid(sStat.st_uid); | |
| 612 | + } | |
| 613 | +#endif | |
| 614 | + return zRepo; | |
| 615 | +} | |
| 572 | 616 | |
| 573 | 617 | /* |
| 574 | 618 | ** Preconditions: |
| 575 | 619 | ** |
| 576 | -** * Environment variables are set up according to the CGI standard. | |
| 577 | -** * The respository database has been located and opened. | |
| 620 | +** * Environment variables are set up according to the CGI standard. | |
| 621 | +** | |
| 622 | +** If the repository is known, it has already been opened. If unknown, | |
| 623 | +** then g.zRepositoryName holds the directory that contains the repository | |
| 624 | +** and the actual repository is taken from the first element of PATH_INFO. | |
| 578 | 625 | ** |
| 579 | 626 | ** Process the webpage specified by the PATH_INFO or REQUEST_URI |
| 580 | 627 | ** environment variable. |
| 581 | 628 | */ |
| 582 | -static void process_one_web_page(void){ | |
| 629 | +static void process_one_web_page(const char *zNotFound){ | |
| 583 | 630 | const char *zPathInfo; |
| 584 | 631 | char *zPath = NULL; |
| 585 | 632 | int idx; |
| 586 | 633 | int i; |
| 634 | + | |
| 635 | + /* If the repository has not been opened already, then find the | |
| 636 | + ** repository based on the first element of PATH_INFO and open it. | |
| 637 | + */ | |
| 638 | + zPathInfo = P("PATH_INFO"); | |
| 639 | + if( !g.repositoryOpen ){ | |
| 640 | + char *zRepo; | |
| 641 | + const char *zOldScript = PD("SCRIPT_NAME", ""); | |
| 642 | + char *zNewScript; | |
| 643 | + int j, k; | |
| 644 | + | |
| 645 | + i = 1; | |
| 646 | + while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; } | |
| 647 | + zRepo = mprintf("%s%.*s.fossil",g.zRepositoryName,i,zPathInfo); | |
| 648 | + | |
| 649 | + /* To avoid mischief, make sure the repository basename contains no | |
| 650 | + ** characters other than alphanumerics, "-", and "_". | |
| 651 | + */ | |
| 652 | + for(j=strlen(g.zRepositoryName)+1, k=0; k<i-1; j++, k++){ | |
| 653 | + if( !isalnum(zRepo[j]) && zRepo[j]!='-' ) zRepo[j] = '_'; | |
| 654 | + } | |
| 655 | + | |
| 656 | + if( file_size(zRepo)<1024 ){ | |
| 657 | + if( zNotFound ){ | |
| 658 | + cgi_redirect(zNotFound); | |
| 659 | + }else{ | |
| 660 | + @ <h1>Not Found</h1> | |
| 661 | + cgi_set_status(404, "not found"); | |
| 662 | + cgi_reply(); | |
| 663 | + } | |
| 664 | + return; | |
| 665 | + } | |
| 666 | + zNewScript = mprintf("%s%.*s", zOldScript, i, zPathInfo); | |
| 667 | + cgi_replace_parameter("PATH_INFO", &zPathInfo[i+1]); | |
| 668 | + zPathInfo += i; | |
| 669 | + cgi_replace_parameter("SCRIPT_NAME", zNewScript); | |
| 670 | + db_open_repository(zRepo); | |
| 671 | + if( g.fHttpTrace ){ | |
| 672 | + fprintf(stderr, | |
| 673 | + "# repository: [%s]\n" | |
| 674 | + "# new PATH_INFO = [%s]\n" | |
| 675 | + "# new SCRIPT_NAME = [%s]\n", | |
| 676 | + zRepo, zPathInfo, zNewScript); | |
| 677 | + } | |
| 678 | + } | |
| 587 | 679 | |
| 588 | 680 | /* Find the page that the user has requested, construct and deliver that |
| 589 | 681 | ** page. |
| 590 | 682 | */ |
| 683 | + if( g.zContentType && memcmp(g.zContentType, "application/x-fossil", 20)==0 ){ | |
| 684 | + zPathInfo = "/xfer"; | |
| 685 | + } | |
| 591 | 686 | set_base_url(); |
| 592 | - zPathInfo = P("PATH_INFO"); | |
| 593 | 687 | if( zPathInfo==0 || zPathInfo[0]==0 |
| 594 | 688 | || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){ |
| 595 | 689 | fossil_redirect_home(); |
| 596 | 690 | }else{ |
| 597 | 691 | zPath = mprintf("%s", zPathInfo); |
| @@ -660,10 +754,11 @@ | ||
| 660 | 754 | ** the repository, fossil will generate a webpage on stdout based on |
| 661 | 755 | ** the values of standard CGI environment variables. |
| 662 | 756 | */ |
| 663 | 757 | void cmd_cgi(void){ |
| 664 | 758 | const char *zFile; |
| 759 | + const char *zNotFound = 0; | |
| 665 | 760 | Blob config, line, key, value; |
| 666 | 761 | if( g.argc==3 && strcmp(g.argv[1],"cgi")==0 ){ |
| 667 | 762 | zFile = g.argv[2]; |
| 668 | 763 | }else{ |
| 669 | 764 | zFile = g.argv[1]; |
| @@ -697,19 +792,59 @@ | ||
| 697 | 792 | continue; |
| 698 | 793 | } |
| 699 | 794 | if( blob_eq(&key, "repository:") && blob_token(&line, &value) ){ |
| 700 | 795 | db_open_repository(blob_str(&value)); |
| 701 | 796 | blob_reset(&value); |
| 702 | - blob_reset(&config); | |
| 703 | - break; | |
| 797 | + continue; | |
| 798 | + } | |
| 799 | + if( blob_eq(&key, "directory:") && blob_token(&line, &value) ){ | |
| 800 | + db_close(); | |
| 801 | + g.zRepositoryName = mprintf("%s", blob_str(&value)); | |
| 802 | + blob_reset(&value); | |
| 803 | + continue; | |
| 804 | + } | |
| 805 | + if( blob_eq(&key, "notfound:") && blob_token(&line, &value) ){ | |
| 806 | + zNotFound = mprintf("%s", blob_str(&value)); | |
| 807 | + blob_reset(&value); | |
| 808 | + continue; | |
| 704 | 809 | } |
| 705 | 810 | } |
| 706 | - if( g.db==0 ){ | |
| 811 | + blob_reset(&config); | |
| 812 | + if( g.db==0 && g.zRepositoryName==0 ){ | |
| 707 | 813 | cgi_panic("Unable to find or open the project repository"); |
| 708 | 814 | } |
| 709 | 815 | cgi_init(); |
| 710 | - process_one_web_page(); | |
| 816 | + process_one_web_page(zNotFound); | |
| 817 | +} | |
| 818 | + | |
| 819 | +/* | |
| 820 | +** If g.argv[2] exists then it is either the name of a repository | |
| 821 | +** that will be used by a server, or else it is a directory that | |
| 822 | +** contains multiple repositories that can be served. If g.argv[2] | |
| 823 | +** is a directory, the repositories it contains must be named | |
| 824 | +** "*.fossil". If g.argv[2] does not exists, then we must be within | |
| 825 | +** a check-out and the repository to be served is the repository of | |
| 826 | +** that check-out. | |
| 827 | +** | |
| 828 | +** Open the respository to be served if it is known. If g.argv[2] is | |
| 829 | +** a directory full of repositories, then set g.zRepositoryName to | |
| 830 | +** the name of that directory and the specific repository will be | |
| 831 | +** opened later by process_one_web_page() based on the content of | |
| 832 | +** the PATH_INFO variable. | |
| 833 | +** | |
| 834 | +** If disallowDir is set, then the directory full of repositories method | |
| 835 | +** is disallowed. | |
| 836 | +*/ | |
| 837 | +static void find_server_repository(int disallowDir){ | |
| 838 | + if( g.argc<3 ){ | |
| 839 | + db_must_be_within_tree(); | |
| 840 | + }else if( !disallowDir && file_isdir(g.argv[2])==1 ){ | |
| 841 | + g.zRepositoryName = mprintf("%s", g.argv[2]); | |
| 842 | + file_simplify_name(g.zRepositoryName, -1); | |
| 843 | + }else{ | |
| 844 | + db_open_repository(g.argv[2]); | |
| 845 | + } | |
| 711 | 846 | } |
| 712 | 847 | |
| 713 | 848 | /* |
| 714 | 849 | ** undocumented format: |
| 715 | 850 | ** |
| @@ -717,41 +852,30 @@ | ||
| 717 | 852 | ** |
| 718 | 853 | ** The argv==6 form is used by the win32 server only. |
| 719 | 854 | ** |
| 720 | 855 | ** COMMAND: http |
| 721 | 856 | ** |
| 722 | -** Usage: %fossil http REPOSITORY | |
| 857 | +** Usage: %fossil http REPOSITORY [--notfound URL] | |
| 723 | 858 | ** |
| 724 | 859 | ** Handle a single HTTP request appearing on stdin. The resulting webpage |
| 725 | 860 | ** is delivered on stdout. This method is used to launch an HTTP request |
| 726 | 861 | ** handler from inetd, for example. The argument is the name of the |
| 727 | 862 | ** repository. |
| 863 | +** | |
| 864 | +** If REPOSITORY is a directory that contains one or more respositories | |
| 865 | +** with names of the form "*.fossil" then the first element of the URL | |
| 866 | +** pathname selects among the various repositories. If the pathname does | |
| 867 | +** not select a valid repository and the --notfound option is available, | |
| 868 | +** then the server redirects (HTTP code 302) to the URL of --notfound. | |
| 728 | 869 | */ |
| 729 | 870 | void cmd_http(void){ |
| 730 | 871 | const char *zIpAddr; |
| 872 | + const char *zNotFound; | |
| 873 | + zNotFound = find_option("notfound", 0, 1); | |
| 731 | 874 | if( g.argc!=2 && g.argc!=3 && g.argc!=6 ){ |
| 732 | 875 | cgi_panic("no repository specified"); |
| 733 | 876 | } |
| 734 | -#if !defined(__MINGW32__) | |
| 735 | - if( g.argc==3 && getuid()==0 ){ | |
| 736 | - int i; | |
| 737 | - char *zRepo = g.argv[2]; | |
| 738 | - struct stat sStat; | |
| 739 | - for(i=strlen(zRepo)-1; i>0 && zRepo[i]!='/'; i--){} | |
| 740 | - if( zRepo[i]=='/' ){ | |
| 741 | - zRepo[i] = 0; | |
| 742 | - chdir(g.argv[2]); | |
| 743 | - chroot(g.argv[2]); | |
| 744 | - g.argv[2] = &zRepo[i+1]; | |
| 745 | - } | |
| 746 | - if( stat(g.argv[2], &sStat)!=0 ){ | |
| 747 | - fossil_fatal("cannot stat() repository: %s", g.argv[2]); | |
| 748 | - } | |
| 749 | - setgid(sStat.st_gid); | |
| 750 | - setuid(sStat.st_uid); | |
| 751 | - } | |
| 752 | -#endif | |
| 753 | 877 | g.cgiPanic = 1; |
| 754 | 878 | g.fullHttpReply = 1; |
| 755 | 879 | if( g.argc==6 ){ |
| 756 | 880 | g.httpIn = fopen(g.argv[3], "rb"); |
| 757 | 881 | g.httpOut = fopen(g.argv[4], "wb"); |
| @@ -759,17 +883,14 @@ | ||
| 759 | 883 | }else{ |
| 760 | 884 | g.httpIn = stdin; |
| 761 | 885 | g.httpOut = stdout; |
| 762 | 886 | zIpAddr = 0; |
| 763 | 887 | } |
| 764 | - if( g.argc>=3 ){ | |
| 765 | - db_open_repository(g.argv[2]); | |
| 766 | - }else{ | |
| 767 | - db_must_be_within_tree(); | |
| 768 | - } | |
| 888 | + find_server_repository(0); | |
| 889 | + g.zRepositoryName = enter_chroot_jail(g.zRepositoryName); | |
| 769 | 890 | cgi_handle_http_request(zIpAddr); |
| 770 | - process_one_web_page(); | |
| 891 | + process_one_web_page(zNotFound); | |
| 771 | 892 | } |
| 772 | 893 | |
| 773 | 894 | /* |
| 774 | 895 | ** COMMAND: test-http |
| 775 | 896 | ** Works like the http command but gives setup permission to all users. |
| @@ -817,16 +938,23 @@ | ||
| 817 | 938 | ** The repository argument may be omitted if the working directory is |
| 818 | 939 | ** within an open checkout. |
| 819 | 940 | ** |
| 820 | 941 | ** The "ui" command automatically starts a web browser after initializing |
| 821 | 942 | ** the web server. |
| 943 | +** | |
| 944 | +** In the "server" command, the REPOSITORY can be a directory (aka folder) | |
| 945 | +** that contains one or more respositories with names ending in ".fossil". | |
| 946 | +** In that case, the first element of the URL is used to select among the | |
| 947 | +** various repositories. | |
| 822 | 948 | */ |
| 823 | 949 | void cmd_webserver(void){ |
| 824 | - int iPort, mxPort; | |
| 825 | - const char *zPort; | |
| 826 | - char *zBrowser; | |
| 827 | - char *zBrowserCmd = 0; | |
| 950 | + int iPort, mxPort; /* Range of TCP ports allowed */ | |
| 951 | + const char *zPort; /* Value of the --port option */ | |
| 952 | + char *zBrowser; /* Name of web browser program */ | |
| 953 | + char *zBrowserCmd = 0; /* Command to launch the web browser */ | |
| 954 | + int isUiCmd; /* True if command is "ui", not "server' */ | |
| 955 | + const char *zNotFound; /* The --notfound option or NULL */ | |
| 828 | 956 | |
| 829 | 957 | #ifdef __MINGW32__ |
| 830 | 958 | const char *zStopperFile; /* Name of file used to terminate server */ |
| 831 | 959 | zStopperFile = find_option("stopper", 0, 1); |
| 832 | 960 | #endif |
| @@ -834,25 +962,23 @@ | ||
| 834 | 962 | g.thTrace = find_option("th-trace", 0, 0)!=0; |
| 835 | 963 | if( g.thTrace ){ |
| 836 | 964 | blob_zero(&g.thLog); |
| 837 | 965 | } |
| 838 | 966 | zPort = find_option("port", "P", 1); |
| 967 | + zNotFound = find_option("notfound", 0, 1); | |
| 839 | 968 | if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?"); |
| 840 | - if( g.argc==2 ){ | |
| 841 | - db_must_be_within_tree(); | |
| 842 | - }else{ | |
| 843 | - db_open_repository(g.argv[2]); | |
| 844 | - } | |
| 969 | + isUiCmd = g.argv[1][0]=='u'; | |
| 970 | + find_server_repository(isUiCmd); | |
| 845 | 971 | if( zPort ){ |
| 846 | 972 | iPort = mxPort = atoi(zPort); |
| 847 | 973 | }else{ |
| 848 | 974 | iPort = db_get_int("http-port", 8080); |
| 849 | 975 | mxPort = iPort+100; |
| 850 | 976 | } |
| 851 | 977 | #ifndef __MINGW32__ |
| 852 | 978 | /* Unix implementation */ |
| 853 | - if( g.argv[1][0]=='u' ){ | |
| 979 | + if( isUiCmd ){ | |
| 854 | 980 | #if !defined(__DARWIN__) && !defined(__APPLE__) |
| 855 | 981 | zBrowser = db_get("web-browser", 0); |
| 856 | 982 | if( zBrowser==0 ){ |
| 857 | 983 | static char *azBrowserProg[] = { "xdg-open", "gnome-open", "firefox" }; |
| 858 | 984 | int i; |
| @@ -877,22 +1003,19 @@ | ||
| 877 | 1003 | g.httpOut = stdout; |
| 878 | 1004 | if( g.fHttpTrace || g.fSqlTrace ){ |
| 879 | 1005 | fprintf(stderr, "====== SERVER pid %d =======\n", getpid()); |
| 880 | 1006 | } |
| 881 | 1007 | g.cgiPanic = 1; |
| 882 | - if( g.argc==2 ){ | |
| 883 | - db_must_be_within_tree(); | |
| 884 | - }else{ | |
| 885 | - db_open_repository(g.argv[2]); | |
| 886 | - } | |
| 1008 | + find_server_repository(isUiCmd); | |
| 1009 | + g.zRepositoryName = enter_chroot_jail(g.zRepositoryName); | |
| 887 | 1010 | cgi_handle_http_request(0); |
| 888 | - process_one_web_page(); | |
| 1011 | + process_one_web_page(zNotFound); | |
| 889 | 1012 | #else |
| 890 | 1013 | /* Win32 implementation */ |
| 891 | - if( g.argv[1][0]=='u' ){ | |
| 1014 | + if( isUiCmd ){ | |
| 892 | 1015 | zBrowser = db_get("web-browser", "start"); |
| 893 | 1016 | zBrowserCmd = mprintf("%s http://127.0.0.1:%%d/", zBrowser); |
| 894 | 1017 | } |
| 895 | 1018 | db_close(); |
| 896 | - win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile); | |
| 1019 | + win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile, zNotFound); | |
| 897 | 1020 | #endif |
| 898 | 1021 | } |
| 899 | 1022 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -453,11 +453,11 @@ | |
| 453 | printf("\n"); |
| 454 | } |
| 455 | } |
| 456 | |
| 457 | /* |
| 458 | ** COM MAND: commands |
| 459 | ** |
| 460 | ** Usage: %fossil commands |
| 461 | ** List all supported commands. |
| 462 | */ |
| 463 | void cmd_cmd_list(void){ |
| @@ -541,11 +541,11 @@ | |
| 541 | putchar('\n'); |
| 542 | } |
| 543 | |
| 544 | /* |
| 545 | ** Set the g.zBaseURL value to the full URL for the toplevel of |
| 546 | ** the fossil tree. Set g.zHomeURL to g.zBaseURL without the |
| 547 | ** leading "http://" and the host and port. |
| 548 | */ |
| 549 | void set_base_url(void){ |
| 550 | int i; |
| 551 | const char *zHost = PD("HTTP_HOST",""); |
| @@ -567,31 +567,125 @@ | |
| 567 | ** Send an HTTP redirect back to the designated Index Page. |
| 568 | */ |
| 569 | void fossil_redirect_home(void){ |
| 570 | cgi_redirectf("%s%s", g.zBaseURL, db_get("index-page", "/index")); |
| 571 | } |
| 572 | |
| 573 | /* |
| 574 | ** Preconditions: |
| 575 | ** |
| 576 | ** * Environment variables are set up according to the CGI standard. |
| 577 | ** * The respository database has been located and opened. |
| 578 | ** |
| 579 | ** Process the webpage specified by the PATH_INFO or REQUEST_URI |
| 580 | ** environment variable. |
| 581 | */ |
| 582 | static void process_one_web_page(void){ |
| 583 | const char *zPathInfo; |
| 584 | char *zPath = NULL; |
| 585 | int idx; |
| 586 | int i; |
| 587 | |
| 588 | /* Find the page that the user has requested, construct and deliver that |
| 589 | ** page. |
| 590 | */ |
| 591 | set_base_url(); |
| 592 | zPathInfo = P("PATH_INFO"); |
| 593 | if( zPathInfo==0 || zPathInfo[0]==0 |
| 594 | || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){ |
| 595 | fossil_redirect_home(); |
| 596 | }else{ |
| 597 | zPath = mprintf("%s", zPathInfo); |
| @@ -660,10 +754,11 @@ | |
| 660 | ** the repository, fossil will generate a webpage on stdout based on |
| 661 | ** the values of standard CGI environment variables. |
| 662 | */ |
| 663 | void cmd_cgi(void){ |
| 664 | const char *zFile; |
| 665 | Blob config, line, key, value; |
| 666 | if( g.argc==3 && strcmp(g.argv[1],"cgi")==0 ){ |
| 667 | zFile = g.argv[2]; |
| 668 | }else{ |
| 669 | zFile = g.argv[1]; |
| @@ -697,19 +792,59 @@ | |
| 697 | continue; |
| 698 | } |
| 699 | if( blob_eq(&key, "repository:") && blob_token(&line, &value) ){ |
| 700 | db_open_repository(blob_str(&value)); |
| 701 | blob_reset(&value); |
| 702 | blob_reset(&config); |
| 703 | break; |
| 704 | } |
| 705 | } |
| 706 | if( g.db==0 ){ |
| 707 | cgi_panic("Unable to find or open the project repository"); |
| 708 | } |
| 709 | cgi_init(); |
| 710 | process_one_web_page(); |
| 711 | } |
| 712 | |
| 713 | /* |
| 714 | ** undocumented format: |
| 715 | ** |
| @@ -717,41 +852,30 @@ | |
| 717 | ** |
| 718 | ** The argv==6 form is used by the win32 server only. |
| 719 | ** |
| 720 | ** COMMAND: http |
| 721 | ** |
| 722 | ** Usage: %fossil http REPOSITORY |
| 723 | ** |
| 724 | ** Handle a single HTTP request appearing on stdin. The resulting webpage |
| 725 | ** is delivered on stdout. This method is used to launch an HTTP request |
| 726 | ** handler from inetd, for example. The argument is the name of the |
| 727 | ** repository. |
| 728 | */ |
| 729 | void cmd_http(void){ |
| 730 | const char *zIpAddr; |
| 731 | if( g.argc!=2 && g.argc!=3 && g.argc!=6 ){ |
| 732 | cgi_panic("no repository specified"); |
| 733 | } |
| 734 | #if !defined(__MINGW32__) |
| 735 | if( g.argc==3 && getuid()==0 ){ |
| 736 | int i; |
| 737 | char *zRepo = g.argv[2]; |
| 738 | struct stat sStat; |
| 739 | for(i=strlen(zRepo)-1; i>0 && zRepo[i]!='/'; i--){} |
| 740 | if( zRepo[i]=='/' ){ |
| 741 | zRepo[i] = 0; |
| 742 | chdir(g.argv[2]); |
| 743 | chroot(g.argv[2]); |
| 744 | g.argv[2] = &zRepo[i+1]; |
| 745 | } |
| 746 | if( stat(g.argv[2], &sStat)!=0 ){ |
| 747 | fossil_fatal("cannot stat() repository: %s", g.argv[2]); |
| 748 | } |
| 749 | setgid(sStat.st_gid); |
| 750 | setuid(sStat.st_uid); |
| 751 | } |
| 752 | #endif |
| 753 | g.cgiPanic = 1; |
| 754 | g.fullHttpReply = 1; |
| 755 | if( g.argc==6 ){ |
| 756 | g.httpIn = fopen(g.argv[3], "rb"); |
| 757 | g.httpOut = fopen(g.argv[4], "wb"); |
| @@ -759,17 +883,14 @@ | |
| 759 | }else{ |
| 760 | g.httpIn = stdin; |
| 761 | g.httpOut = stdout; |
| 762 | zIpAddr = 0; |
| 763 | } |
| 764 | if( g.argc>=3 ){ |
| 765 | db_open_repository(g.argv[2]); |
| 766 | }else{ |
| 767 | db_must_be_within_tree(); |
| 768 | } |
| 769 | cgi_handle_http_request(zIpAddr); |
| 770 | process_one_web_page(); |
| 771 | } |
| 772 | |
| 773 | /* |
| 774 | ** COMMAND: test-http |
| 775 | ** Works like the http command but gives setup permission to all users. |
| @@ -817,16 +938,23 @@ | |
| 817 | ** The repository argument may be omitted if the working directory is |
| 818 | ** within an open checkout. |
| 819 | ** |
| 820 | ** The "ui" command automatically starts a web browser after initializing |
| 821 | ** the web server. |
| 822 | */ |
| 823 | void cmd_webserver(void){ |
| 824 | int iPort, mxPort; |
| 825 | const char *zPort; |
| 826 | char *zBrowser; |
| 827 | char *zBrowserCmd = 0; |
| 828 | |
| 829 | #ifdef __MINGW32__ |
| 830 | const char *zStopperFile; /* Name of file used to terminate server */ |
| 831 | zStopperFile = find_option("stopper", 0, 1); |
| 832 | #endif |
| @@ -834,25 +962,23 @@ | |
| 834 | g.thTrace = find_option("th-trace", 0, 0)!=0; |
| 835 | if( g.thTrace ){ |
| 836 | blob_zero(&g.thLog); |
| 837 | } |
| 838 | zPort = find_option("port", "P", 1); |
| 839 | if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?"); |
| 840 | if( g.argc==2 ){ |
| 841 | db_must_be_within_tree(); |
| 842 | }else{ |
| 843 | db_open_repository(g.argv[2]); |
| 844 | } |
| 845 | if( zPort ){ |
| 846 | iPort = mxPort = atoi(zPort); |
| 847 | }else{ |
| 848 | iPort = db_get_int("http-port", 8080); |
| 849 | mxPort = iPort+100; |
| 850 | } |
| 851 | #ifndef __MINGW32__ |
| 852 | /* Unix implementation */ |
| 853 | if( g.argv[1][0]=='u' ){ |
| 854 | #if !defined(__DARWIN__) && !defined(__APPLE__) |
| 855 | zBrowser = db_get("web-browser", 0); |
| 856 | if( zBrowser==0 ){ |
| 857 | static char *azBrowserProg[] = { "xdg-open", "gnome-open", "firefox" }; |
| 858 | int i; |
| @@ -877,22 +1003,19 @@ | |
| 877 | g.httpOut = stdout; |
| 878 | if( g.fHttpTrace || g.fSqlTrace ){ |
| 879 | fprintf(stderr, "====== SERVER pid %d =======\n", getpid()); |
| 880 | } |
| 881 | g.cgiPanic = 1; |
| 882 | if( g.argc==2 ){ |
| 883 | db_must_be_within_tree(); |
| 884 | }else{ |
| 885 | db_open_repository(g.argv[2]); |
| 886 | } |
| 887 | cgi_handle_http_request(0); |
| 888 | process_one_web_page(); |
| 889 | #else |
| 890 | /* Win32 implementation */ |
| 891 | if( g.argv[1][0]=='u' ){ |
| 892 | zBrowser = db_get("web-browser", "start"); |
| 893 | zBrowserCmd = mprintf("%s http://127.0.0.1:%%d/", zBrowser); |
| 894 | } |
| 895 | db_close(); |
| 896 | win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile); |
| 897 | #endif |
| 898 | } |
| 899 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -453,11 +453,11 @@ | |
| 453 | printf("\n"); |
| 454 | } |
| 455 | } |
| 456 | |
| 457 | /* |
| 458 | ** COM -off- MAND: commands |
| 459 | ** |
| 460 | ** Usage: %fossil commands |
| 461 | ** List all supported commands. |
| 462 | */ |
| 463 | void cmd_cmd_list(void){ |
| @@ -541,11 +541,11 @@ | |
| 541 | putchar('\n'); |
| 542 | } |
| 543 | |
| 544 | /* |
| 545 | ** Set the g.zBaseURL value to the full URL for the toplevel of |
| 546 | ** the fossil tree. Set g.zTop to g.zBaseURL without the |
| 547 | ** leading "http://" and the host and port. |
| 548 | */ |
| 549 | void set_base_url(void){ |
| 550 | int i; |
| 551 | const char *zHost = PD("HTTP_HOST",""); |
| @@ -567,31 +567,125 @@ | |
| 567 | ** Send an HTTP redirect back to the designated Index Page. |
| 568 | */ |
| 569 | void fossil_redirect_home(void){ |
| 570 | cgi_redirectf("%s%s", g.zBaseURL, db_get("index-page", "/index")); |
| 571 | } |
| 572 | |
| 573 | /* |
| 574 | ** If running as root, chroot to the directory containing the |
| 575 | ** repository zRepo and then drop root privileges. Return the |
| 576 | ** new repository name. |
| 577 | ** |
| 578 | ** zRepo might be a directory itself. In that case chroot into |
| 579 | ** the directory zRepo. |
| 580 | ** |
| 581 | ** Assume the user-id and group-id of the repository, or if zRepo |
| 582 | ** is a directory, of that directory. |
| 583 | */ |
| 584 | static char *enter_chroot_jail(char *zRepo){ |
| 585 | #if !defined(__MINGW32__) |
| 586 | if( getuid()==0 ){ |
| 587 | int i; |
| 588 | struct stat sStat; |
| 589 | Blob dir; |
| 590 | char *zDir; |
| 591 | |
| 592 | file_canonical_name(zRepo, &dir); |
| 593 | zDir = blob_str(&dir); |
| 594 | if( file_isdir(zDir)==1 ){ |
| 595 | chdir(zDir); |
| 596 | chroot(zDir); |
| 597 | zRepo = "/"; |
| 598 | }else{ |
| 599 | for(i=strlen(zDir)-1; i>0 && zDir[i]!='/'; i--){} |
| 600 | if( zDir[i]!='/' ) fossil_panic("bad repository name: %s", zRepo); |
| 601 | zDir[i] = 0; |
| 602 | chdir(zDir); |
| 603 | chroot(zDir); |
| 604 | zDir[i] = '/'; |
| 605 | zRepo = &zDir[i]; |
| 606 | } |
| 607 | if( stat(zRepo, &sStat)!=0 ){ |
| 608 | fossil_fatal("cannot stat() repository: %s", zRepo); |
| 609 | } |
| 610 | setgid(sStat.st_gid); |
| 611 | setuid(sStat.st_uid); |
| 612 | } |
| 613 | #endif |
| 614 | return zRepo; |
| 615 | } |
| 616 | |
| 617 | /* |
| 618 | ** Preconditions: |
| 619 | ** |
| 620 | ** * Environment variables are set up according to the CGI standard. |
| 621 | ** |
| 622 | ** If the repository is known, it has already been opened. If unknown, |
| 623 | ** then g.zRepositoryName holds the directory that contains the repository |
| 624 | ** and the actual repository is taken from the first element of PATH_INFO. |
| 625 | ** |
| 626 | ** Process the webpage specified by the PATH_INFO or REQUEST_URI |
| 627 | ** environment variable. |
| 628 | */ |
| 629 | static void process_one_web_page(const char *zNotFound){ |
| 630 | const char *zPathInfo; |
| 631 | char *zPath = NULL; |
| 632 | int idx; |
| 633 | int i; |
| 634 | |
| 635 | /* If the repository has not been opened already, then find the |
| 636 | ** repository based on the first element of PATH_INFO and open it. |
| 637 | */ |
| 638 | zPathInfo = P("PATH_INFO"); |
| 639 | if( !g.repositoryOpen ){ |
| 640 | char *zRepo; |
| 641 | const char *zOldScript = PD("SCRIPT_NAME", ""); |
| 642 | char *zNewScript; |
| 643 | int j, k; |
| 644 | |
| 645 | i = 1; |
| 646 | while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; } |
| 647 | zRepo = mprintf("%s%.*s.fossil",g.zRepositoryName,i,zPathInfo); |
| 648 | |
| 649 | /* To avoid mischief, make sure the repository basename contains no |
| 650 | ** characters other than alphanumerics, "-", and "_". |
| 651 | */ |
| 652 | for(j=strlen(g.zRepositoryName)+1, k=0; k<i-1; j++, k++){ |
| 653 | if( !isalnum(zRepo[j]) && zRepo[j]!='-' ) zRepo[j] = '_'; |
| 654 | } |
| 655 | |
| 656 | if( file_size(zRepo)<1024 ){ |
| 657 | if( zNotFound ){ |
| 658 | cgi_redirect(zNotFound); |
| 659 | }else{ |
| 660 | @ <h1>Not Found</h1> |
| 661 | cgi_set_status(404, "not found"); |
| 662 | cgi_reply(); |
| 663 | } |
| 664 | return; |
| 665 | } |
| 666 | zNewScript = mprintf("%s%.*s", zOldScript, i, zPathInfo); |
| 667 | cgi_replace_parameter("PATH_INFO", &zPathInfo[i+1]); |
| 668 | zPathInfo += i; |
| 669 | cgi_replace_parameter("SCRIPT_NAME", zNewScript); |
| 670 | db_open_repository(zRepo); |
| 671 | if( g.fHttpTrace ){ |
| 672 | fprintf(stderr, |
| 673 | "# repository: [%s]\n" |
| 674 | "# new PATH_INFO = [%s]\n" |
| 675 | "# new SCRIPT_NAME = [%s]\n", |
| 676 | zRepo, zPathInfo, zNewScript); |
| 677 | } |
| 678 | } |
| 679 | |
| 680 | /* Find the page that the user has requested, construct and deliver that |
| 681 | ** page. |
| 682 | */ |
| 683 | if( g.zContentType && memcmp(g.zContentType, "application/x-fossil", 20)==0 ){ |
| 684 | zPathInfo = "/xfer"; |
| 685 | } |
| 686 | set_base_url(); |
| 687 | if( zPathInfo==0 || zPathInfo[0]==0 |
| 688 | || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){ |
| 689 | fossil_redirect_home(); |
| 690 | }else{ |
| 691 | zPath = mprintf("%s", zPathInfo); |
| @@ -660,10 +754,11 @@ | |
| 754 | ** the repository, fossil will generate a webpage on stdout based on |
| 755 | ** the values of standard CGI environment variables. |
| 756 | */ |
| 757 | void cmd_cgi(void){ |
| 758 | const char *zFile; |
| 759 | const char *zNotFound = 0; |
| 760 | Blob config, line, key, value; |
| 761 | if( g.argc==3 && strcmp(g.argv[1],"cgi")==0 ){ |
| 762 | zFile = g.argv[2]; |
| 763 | }else{ |
| 764 | zFile = g.argv[1]; |
| @@ -697,19 +792,59 @@ | |
| 792 | continue; |
| 793 | } |
| 794 | if( blob_eq(&key, "repository:") && blob_token(&line, &value) ){ |
| 795 | db_open_repository(blob_str(&value)); |
| 796 | blob_reset(&value); |
| 797 | continue; |
| 798 | } |
| 799 | if( blob_eq(&key, "directory:") && blob_token(&line, &value) ){ |
| 800 | db_close(); |
| 801 | g.zRepositoryName = mprintf("%s", blob_str(&value)); |
| 802 | blob_reset(&value); |
| 803 | continue; |
| 804 | } |
| 805 | if( blob_eq(&key, "notfound:") && blob_token(&line, &value) ){ |
| 806 | zNotFound = mprintf("%s", blob_str(&value)); |
| 807 | blob_reset(&value); |
| 808 | continue; |
| 809 | } |
| 810 | } |
| 811 | blob_reset(&config); |
| 812 | if( g.db==0 && g.zRepositoryName==0 ){ |
| 813 | cgi_panic("Unable to find or open the project repository"); |
| 814 | } |
| 815 | cgi_init(); |
| 816 | process_one_web_page(zNotFound); |
| 817 | } |
| 818 | |
| 819 | /* |
| 820 | ** If g.argv[2] exists then it is either the name of a repository |
| 821 | ** that will be used by a server, or else it is a directory that |
| 822 | ** contains multiple repositories that can be served. If g.argv[2] |
| 823 | ** is a directory, the repositories it contains must be named |
| 824 | ** "*.fossil". If g.argv[2] does not exists, then we must be within |
| 825 | ** a check-out and the repository to be served is the repository of |
| 826 | ** that check-out. |
| 827 | ** |
| 828 | ** Open the respository to be served if it is known. If g.argv[2] is |
| 829 | ** a directory full of repositories, then set g.zRepositoryName to |
| 830 | ** the name of that directory and the specific repository will be |
| 831 | ** opened later by process_one_web_page() based on the content of |
| 832 | ** the PATH_INFO variable. |
| 833 | ** |
| 834 | ** If disallowDir is set, then the directory full of repositories method |
| 835 | ** is disallowed. |
| 836 | */ |
| 837 | static void find_server_repository(int disallowDir){ |
| 838 | if( g.argc<3 ){ |
| 839 | db_must_be_within_tree(); |
| 840 | }else if( !disallowDir && file_isdir(g.argv[2])==1 ){ |
| 841 | g.zRepositoryName = mprintf("%s", g.argv[2]); |
| 842 | file_simplify_name(g.zRepositoryName, -1); |
| 843 | }else{ |
| 844 | db_open_repository(g.argv[2]); |
| 845 | } |
| 846 | } |
| 847 | |
| 848 | /* |
| 849 | ** undocumented format: |
| 850 | ** |
| @@ -717,41 +852,30 @@ | |
| 852 | ** |
| 853 | ** The argv==6 form is used by the win32 server only. |
| 854 | ** |
| 855 | ** COMMAND: http |
| 856 | ** |
| 857 | ** Usage: %fossil http REPOSITORY [--notfound URL] |
| 858 | ** |
| 859 | ** Handle a single HTTP request appearing on stdin. The resulting webpage |
| 860 | ** is delivered on stdout. This method is used to launch an HTTP request |
| 861 | ** handler from inetd, for example. The argument is the name of the |
| 862 | ** repository. |
| 863 | ** |
| 864 | ** If REPOSITORY is a directory that contains one or more respositories |
| 865 | ** with names of the form "*.fossil" then the first element of the URL |
| 866 | ** pathname selects among the various repositories. If the pathname does |
| 867 | ** not select a valid repository and the --notfound option is available, |
| 868 | ** then the server redirects (HTTP code 302) to the URL of --notfound. |
| 869 | */ |
| 870 | void cmd_http(void){ |
| 871 | const char *zIpAddr; |
| 872 | const char *zNotFound; |
| 873 | zNotFound = find_option("notfound", 0, 1); |
| 874 | if( g.argc!=2 && g.argc!=3 && g.argc!=6 ){ |
| 875 | cgi_panic("no repository specified"); |
| 876 | } |
| 877 | g.cgiPanic = 1; |
| 878 | g.fullHttpReply = 1; |
| 879 | if( g.argc==6 ){ |
| 880 | g.httpIn = fopen(g.argv[3], "rb"); |
| 881 | g.httpOut = fopen(g.argv[4], "wb"); |
| @@ -759,17 +883,14 @@ | |
| 883 | }else{ |
| 884 | g.httpIn = stdin; |
| 885 | g.httpOut = stdout; |
| 886 | zIpAddr = 0; |
| 887 | } |
| 888 | find_server_repository(0); |
| 889 | g.zRepositoryName = enter_chroot_jail(g.zRepositoryName); |
| 890 | cgi_handle_http_request(zIpAddr); |
| 891 | process_one_web_page(zNotFound); |
| 892 | } |
| 893 | |
| 894 | /* |
| 895 | ** COMMAND: test-http |
| 896 | ** Works like the http command but gives setup permission to all users. |
| @@ -817,16 +938,23 @@ | |
| 938 | ** The repository argument may be omitted if the working directory is |
| 939 | ** within an open checkout. |
| 940 | ** |
| 941 | ** The "ui" command automatically starts a web browser after initializing |
| 942 | ** the web server. |
| 943 | ** |
| 944 | ** In the "server" command, the REPOSITORY can be a directory (aka folder) |
| 945 | ** that contains one or more respositories with names ending in ".fossil". |
| 946 | ** In that case, the first element of the URL is used to select among the |
| 947 | ** various repositories. |
| 948 | */ |
| 949 | void cmd_webserver(void){ |
| 950 | int iPort, mxPort; /* Range of TCP ports allowed */ |
| 951 | const char *zPort; /* Value of the --port option */ |
| 952 | char *zBrowser; /* Name of web browser program */ |
| 953 | char *zBrowserCmd = 0; /* Command to launch the web browser */ |
| 954 | int isUiCmd; /* True if command is "ui", not "server' */ |
| 955 | const char *zNotFound; /* The --notfound option or NULL */ |
| 956 | |
| 957 | #ifdef __MINGW32__ |
| 958 | const char *zStopperFile; /* Name of file used to terminate server */ |
| 959 | zStopperFile = find_option("stopper", 0, 1); |
| 960 | #endif |
| @@ -834,25 +962,23 @@ | |
| 962 | g.thTrace = find_option("th-trace", 0, 0)!=0; |
| 963 | if( g.thTrace ){ |
| 964 | blob_zero(&g.thLog); |
| 965 | } |
| 966 | zPort = find_option("port", "P", 1); |
| 967 | zNotFound = find_option("notfound", 0, 1); |
| 968 | if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?"); |
| 969 | isUiCmd = g.argv[1][0]=='u'; |
| 970 | find_server_repository(isUiCmd); |
| 971 | if( zPort ){ |
| 972 | iPort = mxPort = atoi(zPort); |
| 973 | }else{ |
| 974 | iPort = db_get_int("http-port", 8080); |
| 975 | mxPort = iPort+100; |
| 976 | } |
| 977 | #ifndef __MINGW32__ |
| 978 | /* Unix implementation */ |
| 979 | if( isUiCmd ){ |
| 980 | #if !defined(__DARWIN__) && !defined(__APPLE__) |
| 981 | zBrowser = db_get("web-browser", 0); |
| 982 | if( zBrowser==0 ){ |
| 983 | static char *azBrowserProg[] = { "xdg-open", "gnome-open", "firefox" }; |
| 984 | int i; |
| @@ -877,22 +1003,19 @@ | |
| 1003 | g.httpOut = stdout; |
| 1004 | if( g.fHttpTrace || g.fSqlTrace ){ |
| 1005 | fprintf(stderr, "====== SERVER pid %d =======\n", getpid()); |
| 1006 | } |
| 1007 | g.cgiPanic = 1; |
| 1008 | find_server_repository(isUiCmd); |
| 1009 | g.zRepositoryName = enter_chroot_jail(g.zRepositoryName); |
| 1010 | cgi_handle_http_request(0); |
| 1011 | process_one_web_page(zNotFound); |
| 1012 | #else |
| 1013 | /* Win32 implementation */ |
| 1014 | if( isUiCmd ){ |
| 1015 | zBrowser = db_get("web-browser", "start"); |
| 1016 | zBrowserCmd = mprintf("%s http://127.0.0.1:%%d/", zBrowser); |
| 1017 | } |
| 1018 | db_close(); |
| 1019 | win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile, zNotFound); |
| 1020 | #endif |
| 1021 | } |
| 1022 |
+12
-2
| --- src/main.mk | ||
| +++ src/main.mk | ||
| @@ -36,10 +36,11 @@ | ||
| 36 | 36 | $(SRCDIR)/diffcmd.c \ |
| 37 | 37 | $(SRCDIR)/doc.c \ |
| 38 | 38 | $(SRCDIR)/encode.c \ |
| 39 | 39 | $(SRCDIR)/file.c \ |
| 40 | 40 | $(SRCDIR)/finfo.c \ |
| 41 | + $(SRCDIR)/graph.c \ | |
| 41 | 42 | $(SRCDIR)/http.c \ |
| 42 | 43 | $(SRCDIR)/http_socket.c \ |
| 43 | 44 | $(SRCDIR)/http_transport.c \ |
| 44 | 45 | $(SRCDIR)/info.c \ |
| 45 | 46 | $(SRCDIR)/login.c \ |
| @@ -105,10 +106,11 @@ | ||
| 105 | 106 | diffcmd_.c \ |
| 106 | 107 | doc_.c \ |
| 107 | 108 | encode_.c \ |
| 108 | 109 | file_.c \ |
| 109 | 110 | finfo_.c \ |
| 111 | + graph_.c \ | |
| 110 | 112 | http_.c \ |
| 111 | 113 | http_socket_.c \ |
| 112 | 114 | http_transport_.c \ |
| 113 | 115 | info_.c \ |
| 114 | 116 | login_.c \ |
| @@ -174,10 +176,11 @@ | ||
| 174 | 176 | diffcmd.o \ |
| 175 | 177 | doc.o \ |
| 176 | 178 | encode.o \ |
| 177 | 179 | file.o \ |
| 178 | 180 | finfo.o \ |
| 181 | + graph.o \ | |
| 179 | 182 | http.o \ |
| 180 | 183 | http_socket.o \ |
| 181 | 184 | http_transport.o \ |
| 182 | 185 | info.o \ |
| 183 | 186 | login.o \ |
| @@ -258,16 +261,16 @@ | ||
| 258 | 261 | # noop |
| 259 | 262 | |
| 260 | 263 | clean: |
| 261 | 264 | rm -f *.o *_.c $(APPNAME) VERSION.h |
| 262 | 265 | rm -f translate makeheaders mkindex page_index.h headers |
| 263 | - rm -f add.h allrepo.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h file.h finfo.h http.h http_socket.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h pqueue.h printf.h rebuild.h report.h rss.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h | |
| 266 | + rm -f add.h allrepo.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h file.h finfo.h graph.h http.h http_socket.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h pqueue.h printf.h rebuild.h report.h rss.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h | |
| 264 | 267 | |
| 265 | 268 | page_index.h: $(TRANS_SRC) mkindex |
| 266 | 269 | ./mkindex $(TRANS_SRC) >$@ |
| 267 | 270 | headers: page_index.h makeheaders VERSION.h |
| 268 | - ./makeheaders add_.c:add.h allrepo_.c:allrepo.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h file_.c:file.h finfo_.c:finfo.h http_.c:http.h http_socket_.c:http_socket.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h VERSION.h | |
| 271 | + ./makeheaders add_.c:add.h allrepo_.c:allrepo.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h VERSION.h | |
| 269 | 272 | touch headers |
| 270 | 273 | headers: Makefile |
| 271 | 274 | Makefile: |
| 272 | 275 | add_.c: $(SRCDIR)/add.c translate |
| 273 | 276 | ./translate $(SRCDIR)/add.c >add_.c |
| @@ -442,10 +445,17 @@ | ||
| 442 | 445 | |
| 443 | 446 | finfo.o: finfo_.c finfo.h $(SRCDIR)/config.h |
| 444 | 447 | $(XTCC) -o finfo.o -c finfo_.c |
| 445 | 448 | |
| 446 | 449 | finfo.h: headers |
| 450 | +graph_.c: $(SRCDIR)/graph.c translate | |
| 451 | + ./translate $(SRCDIR)/graph.c >graph_.c | |
| 452 | + | |
| 453 | +graph.o: graph_.c graph.h $(SRCDIR)/config.h | |
| 454 | + $(XTCC) -o graph.o -c graph_.c | |
| 455 | + | |
| 456 | +graph.h: headers | |
| 447 | 457 | http_.c: $(SRCDIR)/http.c translate |
| 448 | 458 | ./translate $(SRCDIR)/http.c >http_.c |
| 449 | 459 | |
| 450 | 460 | http.o: http_.c http.h $(SRCDIR)/config.h |
| 451 | 461 | $(XTCC) -o http.o -c http_.c |
| 452 | 462 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -36,10 +36,11 @@ | |
| 36 | $(SRCDIR)/diffcmd.c \ |
| 37 | $(SRCDIR)/doc.c \ |
| 38 | $(SRCDIR)/encode.c \ |
| 39 | $(SRCDIR)/file.c \ |
| 40 | $(SRCDIR)/finfo.c \ |
| 41 | $(SRCDIR)/http.c \ |
| 42 | $(SRCDIR)/http_socket.c \ |
| 43 | $(SRCDIR)/http_transport.c \ |
| 44 | $(SRCDIR)/info.c \ |
| 45 | $(SRCDIR)/login.c \ |
| @@ -105,10 +106,11 @@ | |
| 105 | diffcmd_.c \ |
| 106 | doc_.c \ |
| 107 | encode_.c \ |
| 108 | file_.c \ |
| 109 | finfo_.c \ |
| 110 | http_.c \ |
| 111 | http_socket_.c \ |
| 112 | http_transport_.c \ |
| 113 | info_.c \ |
| 114 | login_.c \ |
| @@ -174,10 +176,11 @@ | |
| 174 | diffcmd.o \ |
| 175 | doc.o \ |
| 176 | encode.o \ |
| 177 | file.o \ |
| 178 | finfo.o \ |
| 179 | http.o \ |
| 180 | http_socket.o \ |
| 181 | http_transport.o \ |
| 182 | info.o \ |
| 183 | login.o \ |
| @@ -258,16 +261,16 @@ | |
| 258 | # noop |
| 259 | |
| 260 | clean: |
| 261 | rm -f *.o *_.c $(APPNAME) VERSION.h |
| 262 | rm -f translate makeheaders mkindex page_index.h headers |
| 263 | rm -f add.h allrepo.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h file.h finfo.h http.h http_socket.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h pqueue.h printf.h rebuild.h report.h rss.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h |
| 264 | |
| 265 | page_index.h: $(TRANS_SRC) mkindex |
| 266 | ./mkindex $(TRANS_SRC) >$@ |
| 267 | headers: page_index.h makeheaders VERSION.h |
| 268 | ./makeheaders add_.c:add.h allrepo_.c:allrepo.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h file_.c:file.h finfo_.c:finfo.h http_.c:http.h http_socket_.c:http_socket.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h VERSION.h |
| 269 | touch headers |
| 270 | headers: Makefile |
| 271 | Makefile: |
| 272 | add_.c: $(SRCDIR)/add.c translate |
| 273 | ./translate $(SRCDIR)/add.c >add_.c |
| @@ -442,10 +445,17 @@ | |
| 442 | |
| 443 | finfo.o: finfo_.c finfo.h $(SRCDIR)/config.h |
| 444 | $(XTCC) -o finfo.o -c finfo_.c |
| 445 | |
| 446 | finfo.h: headers |
| 447 | http_.c: $(SRCDIR)/http.c translate |
| 448 | ./translate $(SRCDIR)/http.c >http_.c |
| 449 | |
| 450 | http.o: http_.c http.h $(SRCDIR)/config.h |
| 451 | $(XTCC) -o http.o -c http_.c |
| 452 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -36,10 +36,11 @@ | |
| 36 | $(SRCDIR)/diffcmd.c \ |
| 37 | $(SRCDIR)/doc.c \ |
| 38 | $(SRCDIR)/encode.c \ |
| 39 | $(SRCDIR)/file.c \ |
| 40 | $(SRCDIR)/finfo.c \ |
| 41 | $(SRCDIR)/graph.c \ |
| 42 | $(SRCDIR)/http.c \ |
| 43 | $(SRCDIR)/http_socket.c \ |
| 44 | $(SRCDIR)/http_transport.c \ |
| 45 | $(SRCDIR)/info.c \ |
| 46 | $(SRCDIR)/login.c \ |
| @@ -105,10 +106,11 @@ | |
| 106 | diffcmd_.c \ |
| 107 | doc_.c \ |
| 108 | encode_.c \ |
| 109 | file_.c \ |
| 110 | finfo_.c \ |
| 111 | graph_.c \ |
| 112 | http_.c \ |
| 113 | http_socket_.c \ |
| 114 | http_transport_.c \ |
| 115 | info_.c \ |
| 116 | login_.c \ |
| @@ -174,10 +176,11 @@ | |
| 176 | diffcmd.o \ |
| 177 | doc.o \ |
| 178 | encode.o \ |
| 179 | file.o \ |
| 180 | finfo.o \ |
| 181 | graph.o \ |
| 182 | http.o \ |
| 183 | http_socket.o \ |
| 184 | http_transport.o \ |
| 185 | info.o \ |
| 186 | login.o \ |
| @@ -258,16 +261,16 @@ | |
| 261 | # noop |
| 262 | |
| 263 | clean: |
| 264 | rm -f *.o *_.c $(APPNAME) VERSION.h |
| 265 | rm -f translate makeheaders mkindex page_index.h headers |
| 266 | rm -f add.h allrepo.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h file.h finfo.h graph.h http.h http_socket.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h pqueue.h printf.h rebuild.h report.h rss.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h |
| 267 | |
| 268 | page_index.h: $(TRANS_SRC) mkindex |
| 269 | ./mkindex $(TRANS_SRC) >$@ |
| 270 | headers: page_index.h makeheaders VERSION.h |
| 271 | ./makeheaders add_.c:add.h allrepo_.c:allrepo.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h VERSION.h |
| 272 | touch headers |
| 273 | headers: Makefile |
| 274 | Makefile: |
| 275 | add_.c: $(SRCDIR)/add.c translate |
| 276 | ./translate $(SRCDIR)/add.c >add_.c |
| @@ -442,10 +445,17 @@ | |
| 445 | |
| 446 | finfo.o: finfo_.c finfo.h $(SRCDIR)/config.h |
| 447 | $(XTCC) -o finfo.o -c finfo_.c |
| 448 | |
| 449 | finfo.h: headers |
| 450 | graph_.c: $(SRCDIR)/graph.c translate |
| 451 | ./translate $(SRCDIR)/graph.c >graph_.c |
| 452 | |
| 453 | graph.o: graph_.c graph.h $(SRCDIR)/config.h |
| 454 | $(XTCC) -o graph.o -c graph_.c |
| 455 | |
| 456 | graph.h: headers |
| 457 | http_.c: $(SRCDIR)/http.c translate |
| 458 | ./translate $(SRCDIR)/http.c >http_.c |
| 459 | |
| 460 | http.o: http_.c http.h $(SRCDIR)/config.h |
| 461 | $(XTCC) -o http.o -c http_.c |
| 462 |
+12
-2
| --- src/main.mk | ||
| +++ src/main.mk | ||
| @@ -36,10 +36,11 @@ | ||
| 36 | 36 | $(SRCDIR)/diffcmd.c \ |
| 37 | 37 | $(SRCDIR)/doc.c \ |
| 38 | 38 | $(SRCDIR)/encode.c \ |
| 39 | 39 | $(SRCDIR)/file.c \ |
| 40 | 40 | $(SRCDIR)/finfo.c \ |
| 41 | + $(SRCDIR)/graph.c \ | |
| 41 | 42 | $(SRCDIR)/http.c \ |
| 42 | 43 | $(SRCDIR)/http_socket.c \ |
| 43 | 44 | $(SRCDIR)/http_transport.c \ |
| 44 | 45 | $(SRCDIR)/info.c \ |
| 45 | 46 | $(SRCDIR)/login.c \ |
| @@ -105,10 +106,11 @@ | ||
| 105 | 106 | diffcmd_.c \ |
| 106 | 107 | doc_.c \ |
| 107 | 108 | encode_.c \ |
| 108 | 109 | file_.c \ |
| 109 | 110 | finfo_.c \ |
| 111 | + graph_.c \ | |
| 110 | 112 | http_.c \ |
| 111 | 113 | http_socket_.c \ |
| 112 | 114 | http_transport_.c \ |
| 113 | 115 | info_.c \ |
| 114 | 116 | login_.c \ |
| @@ -174,10 +176,11 @@ | ||
| 174 | 176 | diffcmd.o \ |
| 175 | 177 | doc.o \ |
| 176 | 178 | encode.o \ |
| 177 | 179 | file.o \ |
| 178 | 180 | finfo.o \ |
| 181 | + graph.o \ | |
| 179 | 182 | http.o \ |
| 180 | 183 | http_socket.o \ |
| 181 | 184 | http_transport.o \ |
| 182 | 185 | info.o \ |
| 183 | 186 | login.o \ |
| @@ -258,16 +261,16 @@ | ||
| 258 | 261 | # noop |
| 259 | 262 | |
| 260 | 263 | clean: |
| 261 | 264 | rm -f *.o *_.c $(APPNAME) VERSION.h |
| 262 | 265 | rm -f translate makeheaders mkindex page_index.h headers |
| 263 | - rm -f add.h allrepo.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h file.h finfo.h http.h http_socket.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h pqueue.h printf.h rebuild.h report.h rss.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h | |
| 266 | + rm -f add.h allrepo.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h file.h finfo.h graph.h http.h http_socket.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h pqueue.h printf.h rebuild.h report.h rss.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h | |
| 264 | 267 | |
| 265 | 268 | page_index.h: $(TRANS_SRC) mkindex |
| 266 | 269 | ./mkindex $(TRANS_SRC) >$@ |
| 267 | 270 | headers: page_index.h makeheaders VERSION.h |
| 268 | - ./makeheaders add_.c:add.h allrepo_.c:allrepo.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h file_.c:file.h finfo_.c:finfo.h http_.c:http.h http_socket_.c:http_socket.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h VERSION.h | |
| 271 | + ./makeheaders add_.c:add.h allrepo_.c:allrepo.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h VERSION.h | |
| 269 | 272 | touch headers |
| 270 | 273 | headers: Makefile |
| 271 | 274 | Makefile: |
| 272 | 275 | add_.c: $(SRCDIR)/add.c translate |
| 273 | 276 | ./translate $(SRCDIR)/add.c >add_.c |
| @@ -442,10 +445,17 @@ | ||
| 442 | 445 | |
| 443 | 446 | finfo.o: finfo_.c finfo.h $(SRCDIR)/config.h |
| 444 | 447 | $(XTCC) -o finfo.o -c finfo_.c |
| 445 | 448 | |
| 446 | 449 | finfo.h: headers |
| 450 | +graph_.c: $(SRCDIR)/graph.c translate | |
| 451 | + ./translate $(SRCDIR)/graph.c >graph_.c | |
| 452 | + | |
| 453 | +graph.o: graph_.c graph.h $(SRCDIR)/config.h | |
| 454 | + $(XTCC) -o graph.o -c graph_.c | |
| 455 | + | |
| 456 | +graph.h: headers | |
| 447 | 457 | http_.c: $(SRCDIR)/http.c translate |
| 448 | 458 | ./translate $(SRCDIR)/http.c >http_.c |
| 449 | 459 | |
| 450 | 460 | http.o: http_.c http.h $(SRCDIR)/config.h |
| 451 | 461 | $(XTCC) -o http.o -c http_.c |
| 452 | 462 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -36,10 +36,11 @@ | |
| 36 | $(SRCDIR)/diffcmd.c \ |
| 37 | $(SRCDIR)/doc.c \ |
| 38 | $(SRCDIR)/encode.c \ |
| 39 | $(SRCDIR)/file.c \ |
| 40 | $(SRCDIR)/finfo.c \ |
| 41 | $(SRCDIR)/http.c \ |
| 42 | $(SRCDIR)/http_socket.c \ |
| 43 | $(SRCDIR)/http_transport.c \ |
| 44 | $(SRCDIR)/info.c \ |
| 45 | $(SRCDIR)/login.c \ |
| @@ -105,10 +106,11 @@ | |
| 105 | diffcmd_.c \ |
| 106 | doc_.c \ |
| 107 | encode_.c \ |
| 108 | file_.c \ |
| 109 | finfo_.c \ |
| 110 | http_.c \ |
| 111 | http_socket_.c \ |
| 112 | http_transport_.c \ |
| 113 | info_.c \ |
| 114 | login_.c \ |
| @@ -174,10 +176,11 @@ | |
| 174 | diffcmd.o \ |
| 175 | doc.o \ |
| 176 | encode.o \ |
| 177 | file.o \ |
| 178 | finfo.o \ |
| 179 | http.o \ |
| 180 | http_socket.o \ |
| 181 | http_transport.o \ |
| 182 | info.o \ |
| 183 | login.o \ |
| @@ -258,16 +261,16 @@ | |
| 258 | # noop |
| 259 | |
| 260 | clean: |
| 261 | rm -f *.o *_.c $(APPNAME) VERSION.h |
| 262 | rm -f translate makeheaders mkindex page_index.h headers |
| 263 | rm -f add.h allrepo.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h file.h finfo.h http.h http_socket.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h pqueue.h printf.h rebuild.h report.h rss.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h |
| 264 | |
| 265 | page_index.h: $(TRANS_SRC) mkindex |
| 266 | ./mkindex $(TRANS_SRC) >$@ |
| 267 | headers: page_index.h makeheaders VERSION.h |
| 268 | ./makeheaders add_.c:add.h allrepo_.c:allrepo.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h file_.c:file.h finfo_.c:finfo.h http_.c:http.h http_socket_.c:http_socket.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h VERSION.h |
| 269 | touch headers |
| 270 | headers: Makefile |
| 271 | Makefile: |
| 272 | add_.c: $(SRCDIR)/add.c translate |
| 273 | ./translate $(SRCDIR)/add.c >add_.c |
| @@ -442,10 +445,17 @@ | |
| 442 | |
| 443 | finfo.o: finfo_.c finfo.h $(SRCDIR)/config.h |
| 444 | $(XTCC) -o finfo.o -c finfo_.c |
| 445 | |
| 446 | finfo.h: headers |
| 447 | http_.c: $(SRCDIR)/http.c translate |
| 448 | ./translate $(SRCDIR)/http.c >http_.c |
| 449 | |
| 450 | http.o: http_.c http.h $(SRCDIR)/config.h |
| 451 | $(XTCC) -o http.o -c http_.c |
| 452 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -36,10 +36,11 @@ | |
| 36 | $(SRCDIR)/diffcmd.c \ |
| 37 | $(SRCDIR)/doc.c \ |
| 38 | $(SRCDIR)/encode.c \ |
| 39 | $(SRCDIR)/file.c \ |
| 40 | $(SRCDIR)/finfo.c \ |
| 41 | $(SRCDIR)/graph.c \ |
| 42 | $(SRCDIR)/http.c \ |
| 43 | $(SRCDIR)/http_socket.c \ |
| 44 | $(SRCDIR)/http_transport.c \ |
| 45 | $(SRCDIR)/info.c \ |
| 46 | $(SRCDIR)/login.c \ |
| @@ -105,10 +106,11 @@ | |
| 106 | diffcmd_.c \ |
| 107 | doc_.c \ |
| 108 | encode_.c \ |
| 109 | file_.c \ |
| 110 | finfo_.c \ |
| 111 | graph_.c \ |
| 112 | http_.c \ |
| 113 | http_socket_.c \ |
| 114 | http_transport_.c \ |
| 115 | info_.c \ |
| 116 | login_.c \ |
| @@ -174,10 +176,11 @@ | |
| 176 | diffcmd.o \ |
| 177 | doc.o \ |
| 178 | encode.o \ |
| 179 | file.o \ |
| 180 | finfo.o \ |
| 181 | graph.o \ |
| 182 | http.o \ |
| 183 | http_socket.o \ |
| 184 | http_transport.o \ |
| 185 | info.o \ |
| 186 | login.o \ |
| @@ -258,16 +261,16 @@ | |
| 261 | # noop |
| 262 | |
| 263 | clean: |
| 264 | rm -f *.o *_.c $(APPNAME) VERSION.h |
| 265 | rm -f translate makeheaders mkindex page_index.h headers |
| 266 | rm -f add.h allrepo.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h file.h finfo.h graph.h http.h http_socket.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h pqueue.h printf.h rebuild.h report.h rss.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h |
| 267 | |
| 268 | page_index.h: $(TRANS_SRC) mkindex |
| 269 | ./mkindex $(TRANS_SRC) >$@ |
| 270 | headers: page_index.h makeheaders VERSION.h |
| 271 | ./makeheaders add_.c:add.h allrepo_.c:allrepo.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h VERSION.h |
| 272 | touch headers |
| 273 | headers: Makefile |
| 274 | Makefile: |
| 275 | add_.c: $(SRCDIR)/add.c translate |
| 276 | ./translate $(SRCDIR)/add.c >add_.c |
| @@ -442,10 +445,17 @@ | |
| 445 | |
| 446 | finfo.o: finfo_.c finfo.h $(SRCDIR)/config.h |
| 447 | $(XTCC) -o finfo.o -c finfo_.c |
| 448 | |
| 449 | finfo.h: headers |
| 450 | graph_.c: $(SRCDIR)/graph.c translate |
| 451 | ./translate $(SRCDIR)/graph.c >graph_.c |
| 452 | |
| 453 | graph.o: graph_.c graph.h $(SRCDIR)/config.h |
| 454 | $(XTCC) -o graph.o -c graph_.c |
| 455 | |
| 456 | graph.h: headers |
| 457 | http_.c: $(SRCDIR)/http.c translate |
| 458 | ./translate $(SRCDIR)/http.c >http_.c |
| 459 | |
| 460 | http.o: http_.c http.h $(SRCDIR)/config.h |
| 461 | $(XTCC) -o http.o -c http_.c |
| 462 |
+1
| --- src/makemake.tcl | ||
| +++ src/makemake.tcl | ||
| @@ -30,10 +30,11 @@ | ||
| 30 | 30 | diffcmd |
| 31 | 31 | doc |
| 32 | 32 | encode |
| 33 | 33 | file |
| 34 | 34 | finfo |
| 35 | + graph | |
| 35 | 36 | http |
| 36 | 37 | http_socket |
| 37 | 38 | http_transport |
| 38 | 39 | info |
| 39 | 40 | login |
| 40 | 41 |
| --- src/makemake.tcl | |
| +++ src/makemake.tcl | |
| @@ -30,10 +30,11 @@ | |
| 30 | diffcmd |
| 31 | doc |
| 32 | encode |
| 33 | file |
| 34 | finfo |
| 35 | http |
| 36 | http_socket |
| 37 | http_transport |
| 38 | info |
| 39 | login |
| 40 |
| --- src/makemake.tcl | |
| +++ src/makemake.tcl | |
| @@ -30,10 +30,11 @@ | |
| 30 | diffcmd |
| 31 | doc |
| 32 | encode |
| 33 | file |
| 34 | finfo |
| 35 | graph |
| 36 | http |
| 37 | http_socket |
| 38 | http_transport |
| 39 | info |
| 40 | login |
| 41 |
+1
| --- src/makemake.tcl | ||
| +++ src/makemake.tcl | ||
| @@ -30,10 +30,11 @@ | ||
| 30 | 30 | diffcmd |
| 31 | 31 | doc |
| 32 | 32 | encode |
| 33 | 33 | file |
| 34 | 34 | finfo |
| 35 | + graph | |
| 35 | 36 | http |
| 36 | 37 | http_socket |
| 37 | 38 | http_transport |
| 38 | 39 | info |
| 39 | 40 | login |
| 40 | 41 |
| --- src/makemake.tcl | |
| +++ src/makemake.tcl | |
| @@ -30,10 +30,11 @@ | |
| 30 | diffcmd |
| 31 | doc |
| 32 | encode |
| 33 | file |
| 34 | finfo |
| 35 | http |
| 36 | http_socket |
| 37 | http_transport |
| 38 | info |
| 39 | login |
| 40 |
| --- src/makemake.tcl | |
| +++ src/makemake.tcl | |
| @@ -30,10 +30,11 @@ | |
| 30 | diffcmd |
| 31 | doc |
| 32 | encode |
| 33 | file |
| 34 | finfo |
| 35 | graph |
| 36 | http |
| 37 | http_socket |
| 38 | http_transport |
| 39 | info |
| 40 | login |
| 41 |
+58
-1
| --- src/name.c | ||
| +++ src/name.c | ||
| @@ -36,10 +36,16 @@ | ||
| 36 | 36 | ** case and might only be a prefix of the full UUID and converts it |
| 37 | 37 | ** into the full-length UUID in canonical form. |
| 38 | 38 | ** |
| 39 | 39 | ** If the input is not a UUID or a UUID prefix, then try to resolve |
| 40 | 40 | ** the name as a tag. If multiple tags match, pick the latest. |
| 41 | +** If the input name matches "tag:*" then always resolve as a tag. | |
| 42 | +** | |
| 43 | +** If the input is not a tag, then try to match it as an ISO-8601 date | |
| 44 | +** string YYYY-MM-DD HH:MM:SS and pick the nearest check-in to that date. | |
| 45 | +** If the input is of the form "date:*" or "localtime:*" or "utc:*" then | |
| 46 | +** always resolve the name as a date. | |
| 41 | 47 | ** |
| 42 | 48 | ** Return the number of errors. |
| 43 | 49 | */ |
| 44 | 50 | int name_to_uuid(Blob *pName, int iErrPriority){ |
| 45 | 51 | int rc; |
| @@ -48,12 +54,17 @@ | ||
| 48 | 54 | if( sz>UUID_SIZE || sz<4 || !validate16(blob_buffer(pName), sz) ){ |
| 49 | 55 | char *zUuid; |
| 50 | 56 | const char *zName = blob_str(pName); |
| 51 | 57 | if( memcmp(zName, "tag:", 4)==0 ){ |
| 52 | 58 | zName += 4; |
| 59 | + zUuid = tag_to_uuid(zName); | |
| 60 | + }else{ | |
| 61 | + zUuid = tag_to_uuid(zName); | |
| 62 | + if( zUuid==0 ){ | |
| 63 | + zUuid = date_to_uuid(zName); | |
| 64 | + } | |
| 53 | 65 | } |
| 54 | - zUuid = tag_to_uuid(zName); | |
| 55 | 66 | if( zUuid ){ |
| 56 | 67 | blob_reset(pName); |
| 57 | 68 | blob_append(pName, zUuid, -1); |
| 58 | 69 | free(zUuid); |
| 59 | 70 | return 0; |
| @@ -123,10 +134,56 @@ | ||
| 123 | 134 | " ORDER BY event.mtime DESC ", |
| 124 | 135 | zTag |
| 125 | 136 | ); |
| 126 | 137 | return zUuid; |
| 127 | 138 | } |
| 139 | + | |
| 140 | +/* | |
| 141 | +** Convert a date/time string into a UUID. | |
| 142 | +** | |
| 143 | +** Input forms accepted: | |
| 144 | +** | |
| 145 | +** date:DATE | |
| 146 | +** local:DATE | |
| 147 | +** utc:DATE | |
| 148 | +** | |
| 149 | +** The DATE is interpreted as localtime unless the "utc:" prefix is used | |
| 150 | +** or a "utc" string appears at the end of the DATE string. | |
| 151 | +*/ | |
| 152 | +char *date_to_uuid(const char *zDate){ | |
| 153 | + int useUtc = 0; | |
| 154 | + int n; | |
| 155 | + char *zCopy = 0; | |
| 156 | + char *zUuid; | |
| 157 | + | |
| 158 | + if( memcmp(zDate, "date:", 5)==0 ){ | |
| 159 | + zDate += 5; | |
| 160 | + }else if( memcmp(zDate, "local:", 6)==0 ){ | |
| 161 | + zDate += 6; | |
| 162 | + }else if( memcmp(zDate, "utc:", 4)==0 ){ | |
| 163 | + zDate += 4; | |
| 164 | + useUtc = 1; | |
| 165 | + } | |
| 166 | + n = strlen(zDate); | |
| 167 | + if( n<10 || zDate[4]!='-' || zDate[7]!='-' ) return 0; | |
| 168 | + if( n>4 && sqlite3_strnicmp(&zDate[n-3], "utc", 3)==0 ){ | |
| 169 | + zCopy = mprintf("%s", zDate); | |
| 170 | + zCopy[n-3] = 0; | |
| 171 | + zDate = zCopy; | |
| 172 | + n -= 3; | |
| 173 | + useUtc = 1; | |
| 174 | + } | |
| 175 | + zUuid = db_text(0, | |
| 176 | + "SELECT (SELECT uuid FROM blob WHERE rid=event.objid)" | |
| 177 | + " FROM event" | |
| 178 | + " WHERE mtime<=julianday(%Q %s) AND type='ci'" | |
| 179 | + " ORDER BY mtime DESC LIMIT 1", | |
| 180 | + zDate, useUtc ? "" : ",'utc'" | |
| 181 | + ); | |
| 182 | + free(zCopy); | |
| 183 | + return zUuid; | |
| 184 | +} | |
| 128 | 185 | |
| 129 | 186 | /* |
| 130 | 187 | ** COMMAND: test-name-to-id |
| 131 | 188 | ** |
| 132 | 189 | ** Convert a name to a full artifact ID. |
| 133 | 190 |
| --- src/name.c | |
| +++ src/name.c | |
| @@ -36,10 +36,16 @@ | |
| 36 | ** case and might only be a prefix of the full UUID and converts it |
| 37 | ** into the full-length UUID in canonical form. |
| 38 | ** |
| 39 | ** If the input is not a UUID or a UUID prefix, then try to resolve |
| 40 | ** the name as a tag. If multiple tags match, pick the latest. |
| 41 | ** |
| 42 | ** Return the number of errors. |
| 43 | */ |
| 44 | int name_to_uuid(Blob *pName, int iErrPriority){ |
| 45 | int rc; |
| @@ -48,12 +54,17 @@ | |
| 48 | if( sz>UUID_SIZE || sz<4 || !validate16(blob_buffer(pName), sz) ){ |
| 49 | char *zUuid; |
| 50 | const char *zName = blob_str(pName); |
| 51 | if( memcmp(zName, "tag:", 4)==0 ){ |
| 52 | zName += 4; |
| 53 | } |
| 54 | zUuid = tag_to_uuid(zName); |
| 55 | if( zUuid ){ |
| 56 | blob_reset(pName); |
| 57 | blob_append(pName, zUuid, -1); |
| 58 | free(zUuid); |
| 59 | return 0; |
| @@ -123,10 +134,56 @@ | |
| 123 | " ORDER BY event.mtime DESC ", |
| 124 | zTag |
| 125 | ); |
| 126 | return zUuid; |
| 127 | } |
| 128 | |
| 129 | /* |
| 130 | ** COMMAND: test-name-to-id |
| 131 | ** |
| 132 | ** Convert a name to a full artifact ID. |
| 133 |
| --- src/name.c | |
| +++ src/name.c | |
| @@ -36,10 +36,16 @@ | |
| 36 | ** case and might only be a prefix of the full UUID and converts it |
| 37 | ** into the full-length UUID in canonical form. |
| 38 | ** |
| 39 | ** If the input is not a UUID or a UUID prefix, then try to resolve |
| 40 | ** the name as a tag. If multiple tags match, pick the latest. |
| 41 | ** If the input name matches "tag:*" then always resolve as a tag. |
| 42 | ** |
| 43 | ** If the input is not a tag, then try to match it as an ISO-8601 date |
| 44 | ** string YYYY-MM-DD HH:MM:SS and pick the nearest check-in to that date. |
| 45 | ** If the input is of the form "date:*" or "localtime:*" or "utc:*" then |
| 46 | ** always resolve the name as a date. |
| 47 | ** |
| 48 | ** Return the number of errors. |
| 49 | */ |
| 50 | int name_to_uuid(Blob *pName, int iErrPriority){ |
| 51 | int rc; |
| @@ -48,12 +54,17 @@ | |
| 54 | if( sz>UUID_SIZE || sz<4 || !validate16(blob_buffer(pName), sz) ){ |
| 55 | char *zUuid; |
| 56 | const char *zName = blob_str(pName); |
| 57 | if( memcmp(zName, "tag:", 4)==0 ){ |
| 58 | zName += 4; |
| 59 | zUuid = tag_to_uuid(zName); |
| 60 | }else{ |
| 61 | zUuid = tag_to_uuid(zName); |
| 62 | if( zUuid==0 ){ |
| 63 | zUuid = date_to_uuid(zName); |
| 64 | } |
| 65 | } |
| 66 | if( zUuid ){ |
| 67 | blob_reset(pName); |
| 68 | blob_append(pName, zUuid, -1); |
| 69 | free(zUuid); |
| 70 | return 0; |
| @@ -123,10 +134,56 @@ | |
| 134 | " ORDER BY event.mtime DESC ", |
| 135 | zTag |
| 136 | ); |
| 137 | return zUuid; |
| 138 | } |
| 139 | |
| 140 | /* |
| 141 | ** Convert a date/time string into a UUID. |
| 142 | ** |
| 143 | ** Input forms accepted: |
| 144 | ** |
| 145 | ** date:DATE |
| 146 | ** local:DATE |
| 147 | ** utc:DATE |
| 148 | ** |
| 149 | ** The DATE is interpreted as localtime unless the "utc:" prefix is used |
| 150 | ** or a "utc" string appears at the end of the DATE string. |
| 151 | */ |
| 152 | char *date_to_uuid(const char *zDate){ |
| 153 | int useUtc = 0; |
| 154 | int n; |
| 155 | char *zCopy = 0; |
| 156 | char *zUuid; |
| 157 | |
| 158 | if( memcmp(zDate, "date:", 5)==0 ){ |
| 159 | zDate += 5; |
| 160 | }else if( memcmp(zDate, "local:", 6)==0 ){ |
| 161 | zDate += 6; |
| 162 | }else if( memcmp(zDate, "utc:", 4)==0 ){ |
| 163 | zDate += 4; |
| 164 | useUtc = 1; |
| 165 | } |
| 166 | n = strlen(zDate); |
| 167 | if( n<10 || zDate[4]!='-' || zDate[7]!='-' ) return 0; |
| 168 | if( n>4 && sqlite3_strnicmp(&zDate[n-3], "utc", 3)==0 ){ |
| 169 | zCopy = mprintf("%s", zDate); |
| 170 | zCopy[n-3] = 0; |
| 171 | zDate = zCopy; |
| 172 | n -= 3; |
| 173 | useUtc = 1; |
| 174 | } |
| 175 | zUuid = db_text(0, |
| 176 | "SELECT (SELECT uuid FROM blob WHERE rid=event.objid)" |
| 177 | " FROM event" |
| 178 | " WHERE mtime<=julianday(%Q %s) AND type='ci'" |
| 179 | " ORDER BY mtime DESC LIMIT 1", |
| 180 | zDate, useUtc ? "" : ",'utc'" |
| 181 | ); |
| 182 | free(zCopy); |
| 183 | return zUuid; |
| 184 | } |
| 185 | |
| 186 | /* |
| 187 | ** COMMAND: test-name-to-id |
| 188 | ** |
| 189 | ** Convert a name to a full artifact ID. |
| 190 |
+30
-2
| --- src/rss.c | ||
| +++ src/rss.c | ||
| @@ -47,15 +47,43 @@ | ||
| 47 | 47 | @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim), |
| 48 | 48 | @ (SELECT count(*) FROM plink WHERE cid=blob.rid) |
| 49 | 49 | @ FROM event, blob |
| 50 | 50 | @ WHERE blob.rid=event.objid |
| 51 | 51 | ; |
| 52 | + | |
| 53 | + login_check_credentials(); | |
| 54 | + if( !g.okRead && !g.okRdTkt && !g.okRdWiki ){ | |
| 55 | + return; | |
| 56 | + } | |
| 57 | + | |
| 52 | 58 | blob_zero(&bSQL); |
| 53 | 59 | blob_append( &bSQL, zSQL1, -1 ); |
| 54 | 60 | |
| 55 | 61 | if( zType[0]!='a' ){ |
| 56 | - blob_appendf(&bSQL, " AND event.type=%Q", zType); | |
| 62 | + if( zType[0]=='c' && !g.okRead ) zType = "x"; | |
| 63 | + if( zType[0]=='w' && !g.okRdWiki ) zType = "x"; | |
| 64 | + if( zType[0]=='t' && !g.okRdTkt ) zType = "x"; | |
| 65 | + blob_appendf(&bSQL, " AND event.type=%Q", zType); | |
| 66 | + }else{ | |
| 67 | + if( !g.okRead ){ | |
| 68 | + if( g.okRdTkt && g.okRdWiki ){ | |
| 69 | + blob_append(&bSQL, " AND event.type!='ci'", -1); | |
| 70 | + }else if( g.okRdTkt ){ | |
| 71 | + blob_append(&bSQL, " AND event.type=='t'", -1); | |
| 72 | + }else{ | |
| 73 | + blob_append(&bSQL, " AND event.type=='w'", -1); | |
| 74 | + } | |
| 75 | + }else if( !g.okRdWiki ){ | |
| 76 | + if( g.okRdTkt ){ | |
| 77 | + blob_append(&bSQL, " AND event.type!='w'", -1); | |
| 78 | + }else{ | |
| 79 | + blob_append(&bSQL, " AND event.type=='ci'", -1); | |
| 80 | + } | |
| 81 | + }else if( !g.okRdTkt ){ | |
| 82 | + assert( !g.okRdTkt &&& g.okRead && g.okRdWiki ); | |
| 83 | + blob_append(&bSQL, " AND event.type!='t'", -1); | |
| 84 | + } | |
| 57 | 85 | } |
| 58 | 86 | |
| 59 | 87 | blob_append( &bSQL, " ORDER BY event.mtime DESC", -1 ); |
| 60 | 88 | |
| 61 | 89 | cgi_set_content_type("application/rss+xml"); |
| @@ -78,11 +106,11 @@ | ||
| 78 | 106 | @ <title>%h(zProjectName)</title> |
| 79 | 107 | @ <link>%s(g.zBaseURL)</link> |
| 80 | 108 | @ <description>%h(zProjectDescr)</description> |
| 81 | 109 | @ <pubDate>%s(zPubDate)</pubDate> |
| 82 | 110 | @ <generator>Fossil version %s(MANIFEST_VERSION) %s(MANIFEST_DATE)</generator> |
| 83 | - db_prepare(&q, blob_buffer(&bSQL)); | |
| 111 | + db_prepare(&q, blob_str(&bSQL)); | |
| 84 | 112 | blob_reset( &bSQL ); |
| 85 | 113 | while( db_step(&q)==SQLITE_ROW && nLine<=20 ){ |
| 86 | 114 | const char *zId = db_column_text(&q, 1); |
| 87 | 115 | const char *zCom = db_column_text(&q, 3); |
| 88 | 116 | const char *zAuthor = db_column_text(&q, 4); |
| 89 | 117 |
| --- src/rss.c | |
| +++ src/rss.c | |
| @@ -47,15 +47,43 @@ | |
| 47 | @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim), |
| 48 | @ (SELECT count(*) FROM plink WHERE cid=blob.rid) |
| 49 | @ FROM event, blob |
| 50 | @ WHERE blob.rid=event.objid |
| 51 | ; |
| 52 | blob_zero(&bSQL); |
| 53 | blob_append( &bSQL, zSQL1, -1 ); |
| 54 | |
| 55 | if( zType[0]!='a' ){ |
| 56 | blob_appendf(&bSQL, " AND event.type=%Q", zType); |
| 57 | } |
| 58 | |
| 59 | blob_append( &bSQL, " ORDER BY event.mtime DESC", -1 ); |
| 60 | |
| 61 | cgi_set_content_type("application/rss+xml"); |
| @@ -78,11 +106,11 @@ | |
| 78 | @ <title>%h(zProjectName)</title> |
| 79 | @ <link>%s(g.zBaseURL)</link> |
| 80 | @ <description>%h(zProjectDescr)</description> |
| 81 | @ <pubDate>%s(zPubDate)</pubDate> |
| 82 | @ <generator>Fossil version %s(MANIFEST_VERSION) %s(MANIFEST_DATE)</generator> |
| 83 | db_prepare(&q, blob_buffer(&bSQL)); |
| 84 | blob_reset( &bSQL ); |
| 85 | while( db_step(&q)==SQLITE_ROW && nLine<=20 ){ |
| 86 | const char *zId = db_column_text(&q, 1); |
| 87 | const char *zCom = db_column_text(&q, 3); |
| 88 | const char *zAuthor = db_column_text(&q, 4); |
| 89 |
| --- src/rss.c | |
| +++ src/rss.c | |
| @@ -47,15 +47,43 @@ | |
| 47 | @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim), |
| 48 | @ (SELECT count(*) FROM plink WHERE cid=blob.rid) |
| 49 | @ FROM event, blob |
| 50 | @ WHERE blob.rid=event.objid |
| 51 | ; |
| 52 | |
| 53 | login_check_credentials(); |
| 54 | if( !g.okRead && !g.okRdTkt && !g.okRdWiki ){ |
| 55 | return; |
| 56 | } |
| 57 | |
| 58 | blob_zero(&bSQL); |
| 59 | blob_append( &bSQL, zSQL1, -1 ); |
| 60 | |
| 61 | if( zType[0]!='a' ){ |
| 62 | if( zType[0]=='c' && !g.okRead ) zType = "x"; |
| 63 | if( zType[0]=='w' && !g.okRdWiki ) zType = "x"; |
| 64 | if( zType[0]=='t' && !g.okRdTkt ) zType = "x"; |
| 65 | blob_appendf(&bSQL, " AND event.type=%Q", zType); |
| 66 | }else{ |
| 67 | if( !g.okRead ){ |
| 68 | if( g.okRdTkt && g.okRdWiki ){ |
| 69 | blob_append(&bSQL, " AND event.type!='ci'", -1); |
| 70 | }else if( g.okRdTkt ){ |
| 71 | blob_append(&bSQL, " AND event.type=='t'", -1); |
| 72 | }else{ |
| 73 | blob_append(&bSQL, " AND event.type=='w'", -1); |
| 74 | } |
| 75 | }else if( !g.okRdWiki ){ |
| 76 | if( g.okRdTkt ){ |
| 77 | blob_append(&bSQL, " AND event.type!='w'", -1); |
| 78 | }else{ |
| 79 | blob_append(&bSQL, " AND event.type=='ci'", -1); |
| 80 | } |
| 81 | }else if( !g.okRdTkt ){ |
| 82 | assert( !g.okRdTkt &&& g.okRead && g.okRdWiki ); |
| 83 | blob_append(&bSQL, " AND event.type!='t'", -1); |
| 84 | } |
| 85 | } |
| 86 | |
| 87 | blob_append( &bSQL, " ORDER BY event.mtime DESC", -1 ); |
| 88 | |
| 89 | cgi_set_content_type("application/rss+xml"); |
| @@ -78,11 +106,11 @@ | |
| 106 | @ <title>%h(zProjectName)</title> |
| 107 | @ <link>%s(g.zBaseURL)</link> |
| 108 | @ <description>%h(zProjectDescr)</description> |
| 109 | @ <pubDate>%s(zPubDate)</pubDate> |
| 110 | @ <generator>Fossil version %s(MANIFEST_VERSION) %s(MANIFEST_DATE)</generator> |
| 111 | db_prepare(&q, blob_str(&bSQL)); |
| 112 | blob_reset( &bSQL ); |
| 113 | while( db_step(&q)==SQLITE_ROW && nLine<=20 ){ |
| 114 | const char *zId = db_column_text(&q, 1); |
| 115 | const char *zCom = db_column_text(&q, 3); |
| 116 | const char *zAuthor = db_column_text(&q, 4); |
| 117 |
+12
| --- src/setup.c | ||
| +++ src/setup.c | ||
| @@ -84,10 +84,12 @@ | ||
| 84 | 84 | "Show artifacts that are shunned by this repository"); |
| 85 | 85 | setup_menu_entry("Log", "rcvfromlist", |
| 86 | 86 | "A record of received artifacts and their sources"); |
| 87 | 87 | setup_menu_entry("Stats", "stat", |
| 88 | 88 | "Display repository statistics"); |
| 89 | + setup_menu_entry("Sync now", "setup_sync", | |
| 90 | + "Sync this repository with the 'remote-url' it was set up with"); | |
| 89 | 91 | @ </table> |
| 90 | 92 | |
| 91 | 93 | style_footer(); |
| 92 | 94 | } |
| 93 | 95 | |
| @@ -1045,5 +1047,15 @@ | ||
| 1045 | 1047 | @ changing the logo to provoke your browser to reload the new logo image. |
| 1046 | 1048 | @ </p> |
| 1047 | 1049 | style_footer(); |
| 1048 | 1050 | db_end_transaction(0); |
| 1049 | 1051 | } |
| 1052 | + | |
| 1053 | +/* | |
| 1054 | +** WEBPAGE: setup_sync | |
| 1055 | +*/ | |
| 1056 | +void setup_sync(void){ | |
| 1057 | + sync_cmd(); | |
| 1058 | + style_header("Synchronized"); | |
| 1059 | + @ <p>The project has been synchronized</p> | |
| 1060 | + style_footer(); | |
| 1061 | +} | |
| 1050 | 1062 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -84,10 +84,12 @@ | |
| 84 | "Show artifacts that are shunned by this repository"); |
| 85 | setup_menu_entry("Log", "rcvfromlist", |
| 86 | "A record of received artifacts and their sources"); |
| 87 | setup_menu_entry("Stats", "stat", |
| 88 | "Display repository statistics"); |
| 89 | @ </table> |
| 90 | |
| 91 | style_footer(); |
| 92 | } |
| 93 | |
| @@ -1045,5 +1047,15 @@ | |
| 1045 | @ changing the logo to provoke your browser to reload the new logo image. |
| 1046 | @ </p> |
| 1047 | style_footer(); |
| 1048 | db_end_transaction(0); |
| 1049 | } |
| 1050 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -84,10 +84,12 @@ | |
| 84 | "Show artifacts that are shunned by this repository"); |
| 85 | setup_menu_entry("Log", "rcvfromlist", |
| 86 | "A record of received artifacts and their sources"); |
| 87 | setup_menu_entry("Stats", "stat", |
| 88 | "Display repository statistics"); |
| 89 | setup_menu_entry("Sync now", "setup_sync", |
| 90 | "Sync this repository with the 'remote-url' it was set up with"); |
| 91 | @ </table> |
| 92 | |
| 93 | style_footer(); |
| 94 | } |
| 95 | |
| @@ -1045,5 +1047,15 @@ | |
| 1047 | @ changing the logo to provoke your browser to reload the new logo image. |
| 1048 | @ </p> |
| 1049 | style_footer(); |
| 1050 | db_end_transaction(0); |
| 1051 | } |
| 1052 | |
| 1053 | /* |
| 1054 | ** WEBPAGE: setup_sync |
| 1055 | */ |
| 1056 | void setup_sync(void){ |
| 1057 | sync_cmd(); |
| 1058 | style_header("Synchronized"); |
| 1059 | @ <p>The project has been synchronized</p> |
| 1060 | style_footer(); |
| 1061 | } |
| 1062 |
+12
| --- src/setup.c | ||
| +++ src/setup.c | ||
| @@ -84,10 +84,12 @@ | ||
| 84 | 84 | "Show artifacts that are shunned by this repository"); |
| 85 | 85 | setup_menu_entry("Log", "rcvfromlist", |
| 86 | 86 | "A record of received artifacts and their sources"); |
| 87 | 87 | setup_menu_entry("Stats", "stat", |
| 88 | 88 | "Display repository statistics"); |
| 89 | + setup_menu_entry("Sync now", "setup_sync", | |
| 90 | + "Sync this repository with the 'remote-url' it was set up with"); | |
| 89 | 91 | @ </table> |
| 90 | 92 | |
| 91 | 93 | style_footer(); |
| 92 | 94 | } |
| 93 | 95 | |
| @@ -1045,5 +1047,15 @@ | ||
| 1045 | 1047 | @ changing the logo to provoke your browser to reload the new logo image. |
| 1046 | 1048 | @ </p> |
| 1047 | 1049 | style_footer(); |
| 1048 | 1050 | db_end_transaction(0); |
| 1049 | 1051 | } |
| 1052 | + | |
| 1053 | +/* | |
| 1054 | +** WEBPAGE: setup_sync | |
| 1055 | +*/ | |
| 1056 | +void setup_sync(void){ | |
| 1057 | + sync_cmd(); | |
| 1058 | + style_header("Synchronized"); | |
| 1059 | + @ <p>The project has been synchronized</p> | |
| 1060 | + style_footer(); | |
| 1061 | +} | |
| 1050 | 1062 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -84,10 +84,12 @@ | |
| 84 | "Show artifacts that are shunned by this repository"); |
| 85 | setup_menu_entry("Log", "rcvfromlist", |
| 86 | "A record of received artifacts and their sources"); |
| 87 | setup_menu_entry("Stats", "stat", |
| 88 | "Display repository statistics"); |
| 89 | @ </table> |
| 90 | |
| 91 | style_footer(); |
| 92 | } |
| 93 | |
| @@ -1045,5 +1047,15 @@ | |
| 1045 | @ changing the logo to provoke your browser to reload the new logo image. |
| 1046 | @ </p> |
| 1047 | style_footer(); |
| 1048 | db_end_transaction(0); |
| 1049 | } |
| 1050 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -84,10 +84,12 @@ | |
| 84 | "Show artifacts that are shunned by this repository"); |
| 85 | setup_menu_entry("Log", "rcvfromlist", |
| 86 | "A record of received artifacts and their sources"); |
| 87 | setup_menu_entry("Stats", "stat", |
| 88 | "Display repository statistics"); |
| 89 | setup_menu_entry("Sync now", "setup_sync", |
| 90 | "Sync this repository with the 'remote-url' it was set up with"); |
| 91 | @ </table> |
| 92 | |
| 93 | style_footer(); |
| 94 | } |
| 95 | |
| @@ -1045,5 +1047,15 @@ | |
| 1047 | @ changing the logo to provoke your browser to reload the new logo image. |
| 1048 | @ </p> |
| 1049 | style_footer(); |
| 1050 | db_end_transaction(0); |
| 1051 | } |
| 1052 | |
| 1053 | /* |
| 1054 | ** WEBPAGE: setup_sync |
| 1055 | */ |
| 1056 | void setup_sync(void){ |
| 1057 | sync_cmd(); |
| 1058 | style_header("Synchronized"); |
| 1059 | @ <p>The project has been synchronized</p> |
| 1060 | style_footer(); |
| 1061 | } |
| 1062 |
+1
| --- src/sha1.c | ||
| +++ src/sha1.c | ||
| @@ -607,10 +607,11 @@ | ||
| 607 | 607 | int i; |
| 608 | 608 | Blob in; |
| 609 | 609 | Blob cksum; |
| 610 | 610 | |
| 611 | 611 | for(i=2; i<g.argc; i++){ |
| 612 | + blob_init(&cksum, "************** not found ***************", -1); | |
| 612 | 613 | if( g.argv[i][0]=='-' && g.argv[i][1]==0 ){ |
| 613 | 614 | blob_read_from_channel(&in, stdin, -1); |
| 614 | 615 | sha1sum_blob(&in, &cksum); |
| 615 | 616 | }else{ |
| 616 | 617 | sha1sum_file(g.argv[i], &cksum); |
| 617 | 618 |
| --- src/sha1.c | |
| +++ src/sha1.c | |
| @@ -607,10 +607,11 @@ | |
| 607 | int i; |
| 608 | Blob in; |
| 609 | Blob cksum; |
| 610 | |
| 611 | for(i=2; i<g.argc; i++){ |
| 612 | if( g.argv[i][0]=='-' && g.argv[i][1]==0 ){ |
| 613 | blob_read_from_channel(&in, stdin, -1); |
| 614 | sha1sum_blob(&in, &cksum); |
| 615 | }else{ |
| 616 | sha1sum_file(g.argv[i], &cksum); |
| 617 |
| --- src/sha1.c | |
| +++ src/sha1.c | |
| @@ -607,10 +607,11 @@ | |
| 607 | int i; |
| 608 | Blob in; |
| 609 | Blob cksum; |
| 610 | |
| 611 | for(i=2; i<g.argc; i++){ |
| 612 | blob_init(&cksum, "************** not found ***************", -1); |
| 613 | if( g.argv[i][0]=='-' && g.argv[i][1]==0 ){ |
| 614 | blob_read_from_channel(&in, stdin, -1); |
| 615 | sha1sum_blob(&in, &cksum); |
| 616 | }else{ |
| 617 | sha1sum_file(g.argv[i], &cksum); |
| 618 |
+220
-100
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -153,10 +153,11 @@ | ||
| 153 | 153 | */ |
| 154 | 154 | #if INTERFACE |
| 155 | 155 | #define TIMELINE_ARTID 0x0001 /* Show artifact IDs on non-check-in lines */ |
| 156 | 156 | #define TIMELINE_LEAFONLY 0x0002 /* Show "Leaf", but not "Merge", "Fork" etc */ |
| 157 | 157 | #define TIMELINE_BRIEF 0x0004 /* Combine adjacent elements of same object */ |
| 158 | +#define TIMELINE_GRAPH 0x0008 /* Compute a graph */ | |
| 158 | 159 | #endif |
| 159 | 160 | |
| 160 | 161 | /* |
| 161 | 162 | ** Output a timeline in the web format given a query. The query |
| 162 | 163 | ** should return these columns: |
| @@ -184,18 +185,23 @@ | ||
| 184 | 185 | int mxWikiLen; |
| 185 | 186 | Blob comment; |
| 186 | 187 | int prevTagid = 0; |
| 187 | 188 | int suppressCnt = 0; |
| 188 | 189 | char zPrevDate[20]; |
| 190 | + GraphContext *pGraph = 0; | |
| 189 | 191 | |
| 190 | 192 | zPrevDate[0] = 0; |
| 191 | 193 | mxWikiLen = db_get_int("timeline-max-comment", 0); |
| 192 | 194 | if( db_get_boolean("timeline-block-markup", 0) ){ |
| 193 | 195 | wikiFlags = WIKI_INLINE; |
| 194 | 196 | }else{ |
| 195 | 197 | wikiFlags = WIKI_INLINE | WIKI_NOBLOCK; |
| 196 | 198 | } |
| 199 | + if( tmFlags & TIMELINE_GRAPH ){ | |
| 200 | + pGraph = graph_init(); | |
| 201 | + @ <div id="canvas" style="position:relative;width:1px;height:1px;"></div> | |
| 202 | + } | |
| 197 | 203 | |
| 198 | 204 | db_multi_exec( |
| 199 | 205 | "CREATE TEMP TABLE IF NOT EXISTS seen(rid INTEGER PRIMARY KEY);" |
| 200 | 206 | "DELETE FROM seen;" |
| 201 | 207 | ); |
| @@ -212,10 +218,11 @@ | ||
| 212 | 218 | const char *zType = db_column_text(pQuery, 9); |
| 213 | 219 | const char *zUser = db_column_text(pQuery, 4); |
| 214 | 220 | const char *zTagList = db_column_text(pQuery, 10); |
| 215 | 221 | int tagid = db_column_int(pQuery, 11); |
| 216 | 222 | int commentColumn = 3; /* Column containing comment text */ |
| 223 | + char zTime[8]; | |
| 217 | 224 | if( tagid ){ |
| 218 | 225 | if( tagid==prevTagid ){ |
| 219 | 226 | if( tmFlags & TIMELINE_BRIEF ){ |
| 220 | 227 | suppressCnt++; |
| 221 | 228 | continue; |
| @@ -236,27 +243,29 @@ | ||
| 236 | 243 | continue; |
| 237 | 244 | } |
| 238 | 245 | db_multi_exec("INSERT OR IGNORE INTO seen VALUES(%d)", rid); |
| 239 | 246 | if( memcmp(zDate, zPrevDate, 10) ){ |
| 240 | 247 | sprintf(zPrevDate, "%.10s", zDate); |
| 241 | - @ <tr><td colspan=3> | |
| 242 | - @ <div class="divider">%s(zPrevDate)</div> | |
| 248 | + @ <tr><td> | |
| 249 | + @ <div class="divider"><nobr>%s(zPrevDate)</nobr></div> | |
| 243 | 250 | @ </td></tr> |
| 244 | 251 | } |
| 252 | + memcpy(zTime, &zDate[11], 5); | |
| 253 | + zTime[5] = 0; | |
| 245 | 254 | @ <tr> |
| 246 | - @ <td valign="top">%s(&zDate[11])</td> | |
| 255 | + @ <td valign="top" align="right">%s(zTime)</td> | |
| 247 | 256 | @ <td width="20" align="center" valign="top"> |
| 248 | - @ <font id="m%d(rid)" size="+1" color="white">*</font></td> | |
| 257 | + @ <div id="m%d(rid)"></div> | |
| 249 | 258 | if( zBgClr && zBgClr[0] ){ |
| 250 | 259 | @ <td valign="top" align="left" bgcolor="%h(zBgClr)"> |
| 251 | 260 | }else{ |
| 252 | 261 | @ <td valign="top" align="left"> |
| 253 | 262 | } |
| 254 | 263 | if( zType[0]=='c' ){ |
| 255 | 264 | const char *azTag[5]; |
| 256 | 265 | int nTag = 0; |
| 257 | - hyperlink_to_uuid_with_mouseover(zUuid, "xin", "xout", rid); | |
| 266 | + hyperlink_to_uuid(zUuid); | |
| 258 | 267 | if( (tmFlags & TIMELINE_LEAFONLY)==0 ){ |
| 259 | 268 | if( nParent>1 ){ |
| 260 | 269 | azTag[nTag++] = "Merge"; |
| 261 | 270 | } |
| 262 | 271 | if( nPChild>1 ){ |
| @@ -280,10 +289,37 @@ | ||
| 280 | 289 | int i; |
| 281 | 290 | for(i=0; i<nTag; i++){ |
| 282 | 291 | @ <b>%s(azTag[i])%s(i==nTag-1?"":",")</b> |
| 283 | 292 | } |
| 284 | 293 | } |
| 294 | + if( pGraph ){ | |
| 295 | + int nParent = 0; | |
| 296 | + int aParent[32]; | |
| 297 | + const char *zBr; | |
| 298 | + static Stmt qparent; | |
| 299 | + static Stmt qbranch; | |
| 300 | + db_static_prepare(&qparent, | |
| 301 | + "SELECT pid FROM plink WHERE cid=:rid ORDER BY isprim DESC" | |
| 302 | + ); | |
| 303 | + db_static_prepare(&qbranch, | |
| 304 | + "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid", | |
| 305 | + TAG_BRANCH | |
| 306 | + ); | |
| 307 | + db_bind_int(&qparent, ":rid", rid); | |
| 308 | + while( db_step(&qparent)==SQLITE_ROW && nParent<32 ){ | |
| 309 | + aParent[nParent++] = db_column_int(&qparent, 0); | |
| 310 | + } | |
| 311 | + db_reset(&qparent); | |
| 312 | + db_bind_int(&qbranch, ":rid", rid); | |
| 313 | + if( db_step(&qbranch)==SQLITE_ROW ){ | |
| 314 | + zBr = db_column_text(&qbranch, 0); | |
| 315 | + }else{ | |
| 316 | + zBr = "trunk"; | |
| 317 | + } | |
| 318 | + graph_add_row(pGraph, rid, isLeaf, nParent, aParent, zBr); | |
| 319 | + db_reset(&qbranch); | |
| 320 | + } | |
| 285 | 321 | }else if( (tmFlags & TIMELINE_ARTID)!=0 ){ |
| 286 | 322 | hyperlink_to_uuid(zUuid); |
| 287 | 323 | } |
| 288 | 324 | db_column_blob(pQuery, commentColumn, &comment); |
| 289 | 325 | if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){ |
| @@ -310,12 +346,188 @@ | ||
| 310 | 346 | if( suppressCnt ){ |
| 311 | 347 | @ <tr><td><td><td> |
| 312 | 348 | @ <small><i>... %d(suppressCnt) similar |
| 313 | 349 | @ event%s(suppressCnt>1?"s":"") omitted.</i></small></tr> |
| 314 | 350 | suppressCnt = 0; |
| 351 | + } | |
| 352 | + if( pGraph ){ | |
| 353 | + graph_finish(pGraph); | |
| 354 | + if( pGraph->nErr ){ | |
| 355 | + graph_free(pGraph); | |
| 356 | + pGraph = 0; | |
| 357 | + }else{ | |
| 358 | + @ <tr><td><td><div style="width:%d(pGraph->mxRail*20+30)px;"></div> | |
| 359 | + } | |
| 315 | 360 | } |
| 316 | 361 | @ </table> |
| 362 | + if( pGraph && pGraph->nErr==0 ){ | |
| 363 | + GraphRow *pRow; | |
| 364 | + int i; | |
| 365 | + char cSep; | |
| 366 | + @ <script type="text/JavaScript"> | |
| 367 | + cgi_printf("var rowinfo = [\n"); | |
| 368 | + for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){ | |
| 369 | + cgi_printf("{id:\"m%d\",r:%d,d:%d,mo:%d,mu:%d,u:%d,au:", | |
| 370 | + pRow->rid, | |
| 371 | + pRow->iRail, | |
| 372 | + pRow->bDescender, | |
| 373 | + pRow->mergeOut, | |
| 374 | + pRow->mergeUpto, | |
| 375 | + pRow->aiRaiser[pRow->iRail] | |
| 376 | + ); | |
| 377 | + cSep = '['; | |
| 378 | + for(i=0; i<GR_MAX_RAIL; i++){ | |
| 379 | + if( i==pRow->iRail ) continue; | |
| 380 | + if( pRow->aiRaiser[i]>0 ){ | |
| 381 | + cgi_printf("%c%d,%d", cSep, pGraph->railMap[i], pRow->aiRaiser[i]); | |
| 382 | + cSep = ','; | |
| 383 | + } | |
| 384 | + } | |
| 385 | + if( cSep=='[' ) cgi_printf("["); | |
| 386 | + cgi_printf("],mi:"); | |
| 387 | + cSep = '['; | |
| 388 | + for(i=0; i<GR_MAX_RAIL; i++){ | |
| 389 | + if( pRow->mergeIn & (1<<i) ){ | |
| 390 | + cgi_printf("%c%d", cSep, pGraph->railMap[i]); | |
| 391 | + cSep = ','; | |
| 392 | + } | |
| 393 | + } | |
| 394 | + if( cSep=='[' ) cgi_printf("["); | |
| 395 | + cgi_printf("]}%s", pRow->pNext ? ",\n" : "];\n"); | |
| 396 | + } | |
| 397 | + cgi_printf("var nrail = %d\n", pGraph->mxRail+1); | |
| 398 | + graph_free(pGraph); | |
| 399 | + @ var canvasDiv = document.getElementById("canvas"); | |
| 400 | + @ function drawBox(color,x0,y0,x1,y1){ | |
| 401 | + @ var n = document.createElement("div"); | |
| 402 | + @ if( x0>x1 ){ var t=x0; x0=x1; x1=t; } | |
| 403 | + @ if( y0>y1 ){ var t=y0; y0=y1; y1=t; } | |
| 404 | + @ var w = x1-x0+1; | |
| 405 | + @ var h = y1-y0+1; | |
| 406 | + @ n.setAttribute("style", | |
| 407 | + @ "position:absolute;"+ | |
| 408 | + @ "left:"+x0+"px;"+ | |
| 409 | + @ "top:"+y0+"px;"+ | |
| 410 | + @ "width:"+w+"px;"+ | |
| 411 | + @ "height:"+h+"px;"+ | |
| 412 | + @ "background-color:"+color+";" | |
| 413 | + @ ); | |
| 414 | + @ canvasDiv.appendChild(n); | |
| 415 | + @ } | |
| 416 | + @ function absoluteY(id){ | |
| 417 | + @ var obj = document.getElementById(id); | |
| 418 | + @ if( !obj ) return; | |
| 419 | + @ var top = 0; | |
| 420 | + @ if( obj.offsetParent ){ | |
| 421 | + @ do{ | |
| 422 | + @ top += obj.offsetTop; | |
| 423 | + @ }while( obj = obj.offsetParent ); | |
| 424 | + @ } | |
| 425 | + @ return top; | |
| 426 | + @ } | |
| 427 | + @ function absoluteX(id){ | |
| 428 | + @ var obj = document.getElementById(id); | |
| 429 | + @ if( !obj ) return; | |
| 430 | + @ var left = 0; | |
| 431 | + @ if( obj.offsetParent ){ | |
| 432 | + @ do{ | |
| 433 | + @ left += obj.offsetLeft; | |
| 434 | + @ }while( obj = obj.offsetParent ); | |
| 435 | + @ } | |
| 436 | + @ return left; | |
| 437 | + @ } | |
| 438 | + @ function drawUpArrow(x,y0,y1){ | |
| 439 | + @ drawBox("black",x,y0,x+1,y1); | |
| 440 | + @ if( y0+8>=y1 ){ | |
| 441 | + @ drawBox("black",x-1,y0+1,x+2,y0+2); | |
| 442 | + @ drawBox("black",x-2,y0+3,x+3,y0+4); | |
| 443 | + @ }else{ | |
| 444 | + @ drawBox("black",x-1,y0+2,x+2,y0+4); | |
| 445 | + @ drawBox("black",x-2,y0+5,x+3,y0+7); | |
| 446 | + @ } | |
| 447 | + @ } | |
| 448 | + @ function drawThinArrow(y,xFrom,xTo){ | |
| 449 | + @ if( xFrom<xTo ){ | |
| 450 | + @ drawBox("black",xFrom,y,xTo,y); | |
| 451 | + @ drawBox("black",xTo-4,y-1,xTo-2,y+1); | |
| 452 | + @ if( xTo>xFrom-8 ) drawBox("black",xTo-6,y-2,xTo-5,y+2); | |
| 453 | + @ }else{ | |
| 454 | + @ drawBox("black",xTo,y,xFrom,y); | |
| 455 | + @ drawBox("black",xTo+2,y-1,xTo+4,y+1); | |
| 456 | + @ if( xTo+8<xFrom ) drawBox("black",xTo+5,y-2,xTo+6,y+2); | |
| 457 | + @ } | |
| 458 | + @ } | |
| 459 | + @ function drawThinLine(x0,y0,x1,y1){ | |
| 460 | + @ drawBox("black",x0,y0,x1,y1); | |
| 461 | + @ } | |
| 462 | + @ function drawNode(p, left, btm){ | |
| 463 | + @ drawBox("black",p.x-5,p.y-5,p.x+6,p.y+6); | |
| 464 | + @ drawBox("white",p.x-4,p.y-4,p.x+5,p.y+5); | |
| 465 | + @ if( p.u>0 ){ | |
| 466 | + @ var u = rowinfo[p.u-1]; | |
| 467 | + @ drawUpArrow(p.x, u.y+6, p.y-5); | |
| 468 | + @ } | |
| 469 | + @ if( p.d ){ | |
| 470 | + @ drawUpArrow(p.x, p.y+6, btm); | |
| 471 | + @ } | |
| 472 | + @ if( p.mo>=0 ){ | |
| 473 | + @ var x1 = p.mo*20 + left; | |
| 474 | + @ var y1 = p.y-3; | |
| 475 | + @ var x0 = x1>p.x ? p.x+7 : p.x-6; | |
| 476 | + @ var u = rowinfo[p.mu-1]; | |
| 477 | + @ var y0 = u.y+5; | |
| 478 | + @ drawThinLine(x0,y1,x1,y1); | |
| 479 | + @ drawThinLine(x1,y0,x1,y1); | |
| 480 | + @ } | |
| 481 | + @ var n = p.au.length; | |
| 482 | + @ for(var i=0; i<n; i+=2){ | |
| 483 | + @ var x1 = p.au[i]*20 + left; | |
| 484 | + @ var x0 = x1>p.x ? p.x+7 : p.x-6; | |
| 485 | + @ drawBox("black",x0,p.y,x1,p.y+1); | |
| 486 | + @ var u = rowinfo[p.au[i+1]-1]; | |
| 487 | + @ drawUpArrow(x1, u.y+6, p.y); | |
| 488 | + @ } | |
| 489 | + @ for(var j in p.mi){ | |
| 490 | + @ var y0 = p.y+5; | |
| 491 | + @ var mx = p.mi[j]*20 + left; | |
| 492 | + @ if( mx>p.x ){ | |
| 493 | + @ drawThinArrow(y0,mx,p.x+5); | |
| 494 | + @ }else{ | |
| 495 | + @ drawThinArrow(y0,mx,p.x-5); | |
| 496 | + @ } | |
| 497 | + @ } | |
| 498 | + @ } | |
| 499 | + @ function renderGraph(){ | |
| 500 | + @ var canvasDiv = document.getElementById("canvas"); | |
| 501 | + @ for(var i=canvasDiv.childNodes.length-1; i>=0; i--){ | |
| 502 | + @ var c = canvasDiv.childNodes[i]; | |
| 503 | + @ delete canvasDiv.removeChild(c); | |
| 504 | + @ } | |
| 505 | + @ var canvasY = absoluteY("canvas"); | |
| 506 | + @ var left = absoluteX(rowinfo[0].id) - absoluteX("canvas") + 15; | |
| 507 | + @ for(var i in rowinfo){ | |
| 508 | + @ rowinfo[i].y = absoluteY(rowinfo[i].id) + 10 - canvasY; | |
| 509 | + @ rowinfo[i].x = left + rowinfo[i].r*20; | |
| 510 | + @ } | |
| 511 | + @ var btm = rowinfo[rowinfo.length-1].y + 20; | |
| 512 | + @ for(var i in rowinfo){ | |
| 513 | + @ drawNode(rowinfo[i], left, btm); | |
| 514 | + @ } | |
| 515 | + @ } | |
| 516 | + @ var lastId = rowinfo[rowinfo.length-1].id; | |
| 517 | + @ var lastY = 0; | |
| 518 | + @ function checkHeight(){ | |
| 519 | + @ var h = absoluteY(lastId); | |
| 520 | + @ if( h!=lastY ){ | |
| 521 | + @ renderGraph(); | |
| 522 | + @ lastY = h; | |
| 523 | + @ } | |
| 524 | + @ setTimeout("checkHeight();", 1000); | |
| 525 | + @ } | |
| 526 | + @ checkHeight(); | |
| 527 | + @ </script> | |
| 528 | + } | |
| 317 | 529 | } |
| 318 | 530 | |
| 319 | 531 | /* |
| 320 | 532 | ** Create a temporary table suitable for storing timeline data. |
| 321 | 533 | */ |
| @@ -461,13 +673,13 @@ | ||
| 461 | 673 | tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'", zTagName); |
| 462 | 674 | }else{ |
| 463 | 675 | tagid = 0; |
| 464 | 676 | } |
| 465 | 677 | if( zType[0]=='a' ){ |
| 466 | - tmFlags = TIMELINE_BRIEF; | |
| 678 | + tmFlags = TIMELINE_BRIEF | TIMELINE_GRAPH; | |
| 467 | 679 | }else{ |
| 468 | - tmFlags = 0; | |
| 680 | + tmFlags = TIMELINE_GRAPH; | |
| 469 | 681 | } |
| 470 | 682 | |
| 471 | 683 | style_header("Timeline"); |
| 472 | 684 | login_anonymous_available(); |
| 473 | 685 | timeline_temp_table(); |
| @@ -645,10 +857,11 @@ | ||
| 645 | 857 | if( zUser ){ |
| 646 | 858 | blob_appendf(&desc, " by user %h", zUser); |
| 647 | 859 | } |
| 648 | 860 | if( tagid>0 ){ |
| 649 | 861 | blob_appendf(&desc, " tagged with \"%h\"", zTagName); |
| 862 | + tmFlags &= ~TIMELINE_GRAPH; | |
| 650 | 863 | } |
| 651 | 864 | if( zAfter ){ |
| 652 | 865 | blob_appendf(&desc, " occurring on or after %h.<br>", zAfter); |
| 653 | 866 | }else if( zBefore ){ |
| 654 | 867 | blob_appendf(&desc, " occurring on or before %h.<br>", zBefore); |
| @@ -691,103 +904,10 @@ | ||
| 691 | 904 | db_prepare(&q, "SELECT * FROM timeline ORDER BY timestamp DESC"); |
| 692 | 905 | @ <h2>%b(&desc)</h2> |
| 693 | 906 | blob_reset(&desc); |
| 694 | 907 | www_print_timeline(&q, tmFlags, 0); |
| 695 | 908 | db_finalize(&q); |
| 696 | - | |
| 697 | - @ <script> | |
| 698 | - @ var parentof = new Object(); | |
| 699 | - @ var childof = new Object(); | |
| 700 | - db_prepare(&q, "SELECT rid FROM seen"); | |
| 701 | - while( db_step(&q)==SQLITE_ROW ){ | |
| 702 | - int rid = db_column_int(&q, 0); | |
| 703 | - Stmt q2; | |
| 704 | - const char *zSep; | |
| 705 | - Blob *pOut = cgi_output_blob(); | |
| 706 | - | |
| 707 | - db_prepare(&q2, "SELECT pid FROM plink WHERE cid=%d", rid); | |
| 708 | - zSep = ""; | |
| 709 | - blob_appendf(pOut, "parentof[\"m%d\"] = [", rid); | |
| 710 | - while( db_step(&q2)==SQLITE_ROW ){ | |
| 711 | - int pid = db_column_int(&q2, 0); | |
| 712 | - blob_appendf(pOut, "%s\"m%d\"", zSep, pid); | |
| 713 | - zSep = ","; | |
| 714 | - } | |
| 715 | - db_finalize(&q2); | |
| 716 | - blob_appendf(pOut, "];\n"); | |
| 717 | - db_prepare(&q2, "SELECT cid FROM plink WHERE pid=%d", rid); | |
| 718 | - zSep = ""; | |
| 719 | - blob_appendf(pOut, "childof[\"m%d\"] = [", rid); | |
| 720 | - while( db_step(&q2)==SQLITE_ROW ){ | |
| 721 | - int pid = db_column_int(&q2, 0); | |
| 722 | - blob_appendf(pOut, "%s\"m%d\"", zSep, pid); | |
| 723 | - zSep = ","; | |
| 724 | - } | |
| 725 | - db_finalize(&q2); | |
| 726 | - blob_appendf(pOut, "];\n"); | |
| 727 | - } | |
| 728 | - db_finalize(&q); | |
| 729 | - @ function setall(value){ | |
| 730 | - @ for(var x in parentof){ | |
| 731 | - @ setone(x,value); | |
| 732 | - @ } | |
| 733 | - @ } | |
| 734 | - @ setall("#ffffff"); | |
| 735 | - @ function setone(id, clr){ | |
| 736 | - @ if( parentof[id]==null ) return 0; | |
| 737 | - @ var w = document.getElementById(id); | |
| 738 | - @ if( w.style.color==clr ){ | |
| 739 | - @ return 0 | |
| 740 | - @ }else{ | |
| 741 | - @ w.style.color = clr | |
| 742 | - @ return 1 | |
| 743 | - @ } | |
| 744 | - @ } | |
| 745 | - @ function xin(id) { | |
| 746 | - @ setall("#ffffff"); | |
| 747 | - @ setone(id,"#ff0000"); | |
| 748 | - @ set_children(id, "#b0b0b0"); | |
| 749 | - @ set_parents(id, "#b0b0b0"); | |
| 750 | - @ for(var x in parentof[id]){ | |
| 751 | - @ var pid = parentof[id][x] | |
| 752 | - @ var w = document.getElementById(pid); | |
| 753 | - @ if( w!=null ){ | |
| 754 | - @ w.style.color = "#000000"; | |
| 755 | - @ } | |
| 756 | - @ } | |
| 757 | - @ for(var x in childof[id]){ | |
| 758 | - @ var cid = childof[id][x] | |
| 759 | - @ var w = document.getElementById(cid); | |
| 760 | - @ if( w!=null ){ | |
| 761 | - @ w.style.color = "#000000"; | |
| 762 | - @ } | |
| 763 | - @ } | |
| 764 | - @ } | |
| 765 | - @ function xout(id) { | |
| 766 | - @ /* setall("#000000"); */ | |
| 767 | - @ } | |
| 768 | - @ function set_parents(id, clr){ | |
| 769 | - @ var plist = parentof[id]; | |
| 770 | - @ if( plist==null ) return; | |
| 771 | - @ for(var x in plist){ | |
| 772 | - @ var pid = plist[x]; | |
| 773 | - @ if( setone(pid,clr)==1 ){ | |
| 774 | - @ set_parents(pid,clr); | |
| 775 | - @ } | |
| 776 | - @ } | |
| 777 | - @ } | |
| 778 | - @ function set_children(id,clr){ | |
| 779 | - @ var clist = childof[id]; | |
| 780 | - @ if( clist==null ) return; | |
| 781 | - @ for(var x in clist){ | |
| 782 | - @ var cid = clist[x]; | |
| 783 | - @ if( setone(cid,clr)==1 ){ | |
| 784 | - @ set_children(cid,clr); | |
| 785 | - @ } | |
| 786 | - @ } | |
| 787 | - @ } | |
| 788 | - @ </script> | |
| 789 | 909 | style_footer(); |
| 790 | 910 | } |
| 791 | 911 | |
| 792 | 912 | /* |
| 793 | 913 | ** The input query q selects various records. Print a human-readable |
| 794 | 914 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -153,10 +153,11 @@ | |
| 153 | */ |
| 154 | #if INTERFACE |
| 155 | #define TIMELINE_ARTID 0x0001 /* Show artifact IDs on non-check-in lines */ |
| 156 | #define TIMELINE_LEAFONLY 0x0002 /* Show "Leaf", but not "Merge", "Fork" etc */ |
| 157 | #define TIMELINE_BRIEF 0x0004 /* Combine adjacent elements of same object */ |
| 158 | #endif |
| 159 | |
| 160 | /* |
| 161 | ** Output a timeline in the web format given a query. The query |
| 162 | ** should return these columns: |
| @@ -184,18 +185,23 @@ | |
| 184 | int mxWikiLen; |
| 185 | Blob comment; |
| 186 | int prevTagid = 0; |
| 187 | int suppressCnt = 0; |
| 188 | char zPrevDate[20]; |
| 189 | |
| 190 | zPrevDate[0] = 0; |
| 191 | mxWikiLen = db_get_int("timeline-max-comment", 0); |
| 192 | if( db_get_boolean("timeline-block-markup", 0) ){ |
| 193 | wikiFlags = WIKI_INLINE; |
| 194 | }else{ |
| 195 | wikiFlags = WIKI_INLINE | WIKI_NOBLOCK; |
| 196 | } |
| 197 | |
| 198 | db_multi_exec( |
| 199 | "CREATE TEMP TABLE IF NOT EXISTS seen(rid INTEGER PRIMARY KEY);" |
| 200 | "DELETE FROM seen;" |
| 201 | ); |
| @@ -212,10 +218,11 @@ | |
| 212 | const char *zType = db_column_text(pQuery, 9); |
| 213 | const char *zUser = db_column_text(pQuery, 4); |
| 214 | const char *zTagList = db_column_text(pQuery, 10); |
| 215 | int tagid = db_column_int(pQuery, 11); |
| 216 | int commentColumn = 3; /* Column containing comment text */ |
| 217 | if( tagid ){ |
| 218 | if( tagid==prevTagid ){ |
| 219 | if( tmFlags & TIMELINE_BRIEF ){ |
| 220 | suppressCnt++; |
| 221 | continue; |
| @@ -236,27 +243,29 @@ | |
| 236 | continue; |
| 237 | } |
| 238 | db_multi_exec("INSERT OR IGNORE INTO seen VALUES(%d)", rid); |
| 239 | if( memcmp(zDate, zPrevDate, 10) ){ |
| 240 | sprintf(zPrevDate, "%.10s", zDate); |
| 241 | @ <tr><td colspan=3> |
| 242 | @ <div class="divider">%s(zPrevDate)</div> |
| 243 | @ </td></tr> |
| 244 | } |
| 245 | @ <tr> |
| 246 | @ <td valign="top">%s(&zDate[11])</td> |
| 247 | @ <td width="20" align="center" valign="top"> |
| 248 | @ <font id="m%d(rid)" size="+1" color="white">*</font></td> |
| 249 | if( zBgClr && zBgClr[0] ){ |
| 250 | @ <td valign="top" align="left" bgcolor="%h(zBgClr)"> |
| 251 | }else{ |
| 252 | @ <td valign="top" align="left"> |
| 253 | } |
| 254 | if( zType[0]=='c' ){ |
| 255 | const char *azTag[5]; |
| 256 | int nTag = 0; |
| 257 | hyperlink_to_uuid_with_mouseover(zUuid, "xin", "xout", rid); |
| 258 | if( (tmFlags & TIMELINE_LEAFONLY)==0 ){ |
| 259 | if( nParent>1 ){ |
| 260 | azTag[nTag++] = "Merge"; |
| 261 | } |
| 262 | if( nPChild>1 ){ |
| @@ -280,10 +289,37 @@ | |
| 280 | int i; |
| 281 | for(i=0; i<nTag; i++){ |
| 282 | @ <b>%s(azTag[i])%s(i==nTag-1?"":",")</b> |
| 283 | } |
| 284 | } |
| 285 | }else if( (tmFlags & TIMELINE_ARTID)!=0 ){ |
| 286 | hyperlink_to_uuid(zUuid); |
| 287 | } |
| 288 | db_column_blob(pQuery, commentColumn, &comment); |
| 289 | if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){ |
| @@ -310,12 +346,188 @@ | |
| 310 | if( suppressCnt ){ |
| 311 | @ <tr><td><td><td> |
| 312 | @ <small><i>... %d(suppressCnt) similar |
| 313 | @ event%s(suppressCnt>1?"s":"") omitted.</i></small></tr> |
| 314 | suppressCnt = 0; |
| 315 | } |
| 316 | @ </table> |
| 317 | } |
| 318 | |
| 319 | /* |
| 320 | ** Create a temporary table suitable for storing timeline data. |
| 321 | */ |
| @@ -461,13 +673,13 @@ | |
| 461 | tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'", zTagName); |
| 462 | }else{ |
| 463 | tagid = 0; |
| 464 | } |
| 465 | if( zType[0]=='a' ){ |
| 466 | tmFlags = TIMELINE_BRIEF; |
| 467 | }else{ |
| 468 | tmFlags = 0; |
| 469 | } |
| 470 | |
| 471 | style_header("Timeline"); |
| 472 | login_anonymous_available(); |
| 473 | timeline_temp_table(); |
| @@ -645,10 +857,11 @@ | |
| 645 | if( zUser ){ |
| 646 | blob_appendf(&desc, " by user %h", zUser); |
| 647 | } |
| 648 | if( tagid>0 ){ |
| 649 | blob_appendf(&desc, " tagged with \"%h\"", zTagName); |
| 650 | } |
| 651 | if( zAfter ){ |
| 652 | blob_appendf(&desc, " occurring on or after %h.<br>", zAfter); |
| 653 | }else if( zBefore ){ |
| 654 | blob_appendf(&desc, " occurring on or before %h.<br>", zBefore); |
| @@ -691,103 +904,10 @@ | |
| 691 | db_prepare(&q, "SELECT * FROM timeline ORDER BY timestamp DESC"); |
| 692 | @ <h2>%b(&desc)</h2> |
| 693 | blob_reset(&desc); |
| 694 | www_print_timeline(&q, tmFlags, 0); |
| 695 | db_finalize(&q); |
| 696 | |
| 697 | @ <script> |
| 698 | @ var parentof = new Object(); |
| 699 | @ var childof = new Object(); |
| 700 | db_prepare(&q, "SELECT rid FROM seen"); |
| 701 | while( db_step(&q)==SQLITE_ROW ){ |
| 702 | int rid = db_column_int(&q, 0); |
| 703 | Stmt q2; |
| 704 | const char *zSep; |
| 705 | Blob *pOut = cgi_output_blob(); |
| 706 | |
| 707 | db_prepare(&q2, "SELECT pid FROM plink WHERE cid=%d", rid); |
| 708 | zSep = ""; |
| 709 | blob_appendf(pOut, "parentof[\"m%d\"] = [", rid); |
| 710 | while( db_step(&q2)==SQLITE_ROW ){ |
| 711 | int pid = db_column_int(&q2, 0); |
| 712 | blob_appendf(pOut, "%s\"m%d\"", zSep, pid); |
| 713 | zSep = ","; |
| 714 | } |
| 715 | db_finalize(&q2); |
| 716 | blob_appendf(pOut, "];\n"); |
| 717 | db_prepare(&q2, "SELECT cid FROM plink WHERE pid=%d", rid); |
| 718 | zSep = ""; |
| 719 | blob_appendf(pOut, "childof[\"m%d\"] = [", rid); |
| 720 | while( db_step(&q2)==SQLITE_ROW ){ |
| 721 | int pid = db_column_int(&q2, 0); |
| 722 | blob_appendf(pOut, "%s\"m%d\"", zSep, pid); |
| 723 | zSep = ","; |
| 724 | } |
| 725 | db_finalize(&q2); |
| 726 | blob_appendf(pOut, "];\n"); |
| 727 | } |
| 728 | db_finalize(&q); |
| 729 | @ function setall(value){ |
| 730 | @ for(var x in parentof){ |
| 731 | @ setone(x,value); |
| 732 | @ } |
| 733 | @ } |
| 734 | @ setall("#ffffff"); |
| 735 | @ function setone(id, clr){ |
| 736 | @ if( parentof[id]==null ) return 0; |
| 737 | @ var w = document.getElementById(id); |
| 738 | @ if( w.style.color==clr ){ |
| 739 | @ return 0 |
| 740 | @ }else{ |
| 741 | @ w.style.color = clr |
| 742 | @ return 1 |
| 743 | @ } |
| 744 | @ } |
| 745 | @ function xin(id) { |
| 746 | @ setall("#ffffff"); |
| 747 | @ setone(id,"#ff0000"); |
| 748 | @ set_children(id, "#b0b0b0"); |
| 749 | @ set_parents(id, "#b0b0b0"); |
| 750 | @ for(var x in parentof[id]){ |
| 751 | @ var pid = parentof[id][x] |
| 752 | @ var w = document.getElementById(pid); |
| 753 | @ if( w!=null ){ |
| 754 | @ w.style.color = "#000000"; |
| 755 | @ } |
| 756 | @ } |
| 757 | @ for(var x in childof[id]){ |
| 758 | @ var cid = childof[id][x] |
| 759 | @ var w = document.getElementById(cid); |
| 760 | @ if( w!=null ){ |
| 761 | @ w.style.color = "#000000"; |
| 762 | @ } |
| 763 | @ } |
| 764 | @ } |
| 765 | @ function xout(id) { |
| 766 | @ /* setall("#000000"); */ |
| 767 | @ } |
| 768 | @ function set_parents(id, clr){ |
| 769 | @ var plist = parentof[id]; |
| 770 | @ if( plist==null ) return; |
| 771 | @ for(var x in plist){ |
| 772 | @ var pid = plist[x]; |
| 773 | @ if( setone(pid,clr)==1 ){ |
| 774 | @ set_parents(pid,clr); |
| 775 | @ } |
| 776 | @ } |
| 777 | @ } |
| 778 | @ function set_children(id,clr){ |
| 779 | @ var clist = childof[id]; |
| 780 | @ if( clist==null ) return; |
| 781 | @ for(var x in clist){ |
| 782 | @ var cid = clist[x]; |
| 783 | @ if( setone(cid,clr)==1 ){ |
| 784 | @ set_children(cid,clr); |
| 785 | @ } |
| 786 | @ } |
| 787 | @ } |
| 788 | @ </script> |
| 789 | style_footer(); |
| 790 | } |
| 791 | |
| 792 | /* |
| 793 | ** The input query q selects various records. Print a human-readable |
| 794 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -153,10 +153,11 @@ | |
| 153 | */ |
| 154 | #if INTERFACE |
| 155 | #define TIMELINE_ARTID 0x0001 /* Show artifact IDs on non-check-in lines */ |
| 156 | #define TIMELINE_LEAFONLY 0x0002 /* Show "Leaf", but not "Merge", "Fork" etc */ |
| 157 | #define TIMELINE_BRIEF 0x0004 /* Combine adjacent elements of same object */ |
| 158 | #define TIMELINE_GRAPH 0x0008 /* Compute a graph */ |
| 159 | #endif |
| 160 | |
| 161 | /* |
| 162 | ** Output a timeline in the web format given a query. The query |
| 163 | ** should return these columns: |
| @@ -184,18 +185,23 @@ | |
| 185 | int mxWikiLen; |
| 186 | Blob comment; |
| 187 | int prevTagid = 0; |
| 188 | int suppressCnt = 0; |
| 189 | char zPrevDate[20]; |
| 190 | GraphContext *pGraph = 0; |
| 191 | |
| 192 | zPrevDate[0] = 0; |
| 193 | mxWikiLen = db_get_int("timeline-max-comment", 0); |
| 194 | if( db_get_boolean("timeline-block-markup", 0) ){ |
| 195 | wikiFlags = WIKI_INLINE; |
| 196 | }else{ |
| 197 | wikiFlags = WIKI_INLINE | WIKI_NOBLOCK; |
| 198 | } |
| 199 | if( tmFlags & TIMELINE_GRAPH ){ |
| 200 | pGraph = graph_init(); |
| 201 | @ <div id="canvas" style="position:relative;width:1px;height:1px;"></div> |
| 202 | } |
| 203 | |
| 204 | db_multi_exec( |
| 205 | "CREATE TEMP TABLE IF NOT EXISTS seen(rid INTEGER PRIMARY KEY);" |
| 206 | "DELETE FROM seen;" |
| 207 | ); |
| @@ -212,10 +218,11 @@ | |
| 218 | const char *zType = db_column_text(pQuery, 9); |
| 219 | const char *zUser = db_column_text(pQuery, 4); |
| 220 | const char *zTagList = db_column_text(pQuery, 10); |
| 221 | int tagid = db_column_int(pQuery, 11); |
| 222 | int commentColumn = 3; /* Column containing comment text */ |
| 223 | char zTime[8]; |
| 224 | if( tagid ){ |
| 225 | if( tagid==prevTagid ){ |
| 226 | if( tmFlags & TIMELINE_BRIEF ){ |
| 227 | suppressCnt++; |
| 228 | continue; |
| @@ -236,27 +243,29 @@ | |
| 243 | continue; |
| 244 | } |
| 245 | db_multi_exec("INSERT OR IGNORE INTO seen VALUES(%d)", rid); |
| 246 | if( memcmp(zDate, zPrevDate, 10) ){ |
| 247 | sprintf(zPrevDate, "%.10s", zDate); |
| 248 | @ <tr><td> |
| 249 | @ <div class="divider"><nobr>%s(zPrevDate)</nobr></div> |
| 250 | @ </td></tr> |
| 251 | } |
| 252 | memcpy(zTime, &zDate[11], 5); |
| 253 | zTime[5] = 0; |
| 254 | @ <tr> |
| 255 | @ <td valign="top" align="right">%s(zTime)</td> |
| 256 | @ <td width="20" align="center" valign="top"> |
| 257 | @ <div id="m%d(rid)"></div> |
| 258 | if( zBgClr && zBgClr[0] ){ |
| 259 | @ <td valign="top" align="left" bgcolor="%h(zBgClr)"> |
| 260 | }else{ |
| 261 | @ <td valign="top" align="left"> |
| 262 | } |
| 263 | if( zType[0]=='c' ){ |
| 264 | const char *azTag[5]; |
| 265 | int nTag = 0; |
| 266 | hyperlink_to_uuid(zUuid); |
| 267 | if( (tmFlags & TIMELINE_LEAFONLY)==0 ){ |
| 268 | if( nParent>1 ){ |
| 269 | azTag[nTag++] = "Merge"; |
| 270 | } |
| 271 | if( nPChild>1 ){ |
| @@ -280,10 +289,37 @@ | |
| 289 | int i; |
| 290 | for(i=0; i<nTag; i++){ |
| 291 | @ <b>%s(azTag[i])%s(i==nTag-1?"":",")</b> |
| 292 | } |
| 293 | } |
| 294 | if( pGraph ){ |
| 295 | int nParent = 0; |
| 296 | int aParent[32]; |
| 297 | const char *zBr; |
| 298 | static Stmt qparent; |
| 299 | static Stmt qbranch; |
| 300 | db_static_prepare(&qparent, |
| 301 | "SELECT pid FROM plink WHERE cid=:rid ORDER BY isprim DESC" |
| 302 | ); |
| 303 | db_static_prepare(&qbranch, |
| 304 | "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid", |
| 305 | TAG_BRANCH |
| 306 | ); |
| 307 | db_bind_int(&qparent, ":rid", rid); |
| 308 | while( db_step(&qparent)==SQLITE_ROW && nParent<32 ){ |
| 309 | aParent[nParent++] = db_column_int(&qparent, 0); |
| 310 | } |
| 311 | db_reset(&qparent); |
| 312 | db_bind_int(&qbranch, ":rid", rid); |
| 313 | if( db_step(&qbranch)==SQLITE_ROW ){ |
| 314 | zBr = db_column_text(&qbranch, 0); |
| 315 | }else{ |
| 316 | zBr = "trunk"; |
| 317 | } |
| 318 | graph_add_row(pGraph, rid, isLeaf, nParent, aParent, zBr); |
| 319 | db_reset(&qbranch); |
| 320 | } |
| 321 | }else if( (tmFlags & TIMELINE_ARTID)!=0 ){ |
| 322 | hyperlink_to_uuid(zUuid); |
| 323 | } |
| 324 | db_column_blob(pQuery, commentColumn, &comment); |
| 325 | if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){ |
| @@ -310,12 +346,188 @@ | |
| 346 | if( suppressCnt ){ |
| 347 | @ <tr><td><td><td> |
| 348 | @ <small><i>... %d(suppressCnt) similar |
| 349 | @ event%s(suppressCnt>1?"s":"") omitted.</i></small></tr> |
| 350 | suppressCnt = 0; |
| 351 | } |
| 352 | if( pGraph ){ |
| 353 | graph_finish(pGraph); |
| 354 | if( pGraph->nErr ){ |
| 355 | graph_free(pGraph); |
| 356 | pGraph = 0; |
| 357 | }else{ |
| 358 | @ <tr><td><td><div style="width:%d(pGraph->mxRail*20+30)px;"></div> |
| 359 | } |
| 360 | } |
| 361 | @ </table> |
| 362 | if( pGraph && pGraph->nErr==0 ){ |
| 363 | GraphRow *pRow; |
| 364 | int i; |
| 365 | char cSep; |
| 366 | @ <script type="text/JavaScript"> |
| 367 | cgi_printf("var rowinfo = [\n"); |
| 368 | for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){ |
| 369 | cgi_printf("{id:\"m%d\",r:%d,d:%d,mo:%d,mu:%d,u:%d,au:", |
| 370 | pRow->rid, |
| 371 | pRow->iRail, |
| 372 | pRow->bDescender, |
| 373 | pRow->mergeOut, |
| 374 | pRow->mergeUpto, |
| 375 | pRow->aiRaiser[pRow->iRail] |
| 376 | ); |
| 377 | cSep = '['; |
| 378 | for(i=0; i<GR_MAX_RAIL; i++){ |
| 379 | if( i==pRow->iRail ) continue; |
| 380 | if( pRow->aiRaiser[i]>0 ){ |
| 381 | cgi_printf("%c%d,%d", cSep, pGraph->railMap[i], pRow->aiRaiser[i]); |
| 382 | cSep = ','; |
| 383 | } |
| 384 | } |
| 385 | if( cSep=='[' ) cgi_printf("["); |
| 386 | cgi_printf("],mi:"); |
| 387 | cSep = '['; |
| 388 | for(i=0; i<GR_MAX_RAIL; i++){ |
| 389 | if( pRow->mergeIn & (1<<i) ){ |
| 390 | cgi_printf("%c%d", cSep, pGraph->railMap[i]); |
| 391 | cSep = ','; |
| 392 | } |
| 393 | } |
| 394 | if( cSep=='[' ) cgi_printf("["); |
| 395 | cgi_printf("]}%s", pRow->pNext ? ",\n" : "];\n"); |
| 396 | } |
| 397 | cgi_printf("var nrail = %d\n", pGraph->mxRail+1); |
| 398 | graph_free(pGraph); |
| 399 | @ var canvasDiv = document.getElementById("canvas"); |
| 400 | @ function drawBox(color,x0,y0,x1,y1){ |
| 401 | @ var n = document.createElement("div"); |
| 402 | @ if( x0>x1 ){ var t=x0; x0=x1; x1=t; } |
| 403 | @ if( y0>y1 ){ var t=y0; y0=y1; y1=t; } |
| 404 | @ var w = x1-x0+1; |
| 405 | @ var h = y1-y0+1; |
| 406 | @ n.setAttribute("style", |
| 407 | @ "position:absolute;"+ |
| 408 | @ "left:"+x0+"px;"+ |
| 409 | @ "top:"+y0+"px;"+ |
| 410 | @ "width:"+w+"px;"+ |
| 411 | @ "height:"+h+"px;"+ |
| 412 | @ "background-color:"+color+";" |
| 413 | @ ); |
| 414 | @ canvasDiv.appendChild(n); |
| 415 | @ } |
| 416 | @ function absoluteY(id){ |
| 417 | @ var obj = document.getElementById(id); |
| 418 | @ if( !obj ) return; |
| 419 | @ var top = 0; |
| 420 | @ if( obj.offsetParent ){ |
| 421 | @ do{ |
| 422 | @ top += obj.offsetTop; |
| 423 | @ }while( obj = obj.offsetParent ); |
| 424 | @ } |
| 425 | @ return top; |
| 426 | @ } |
| 427 | @ function absoluteX(id){ |
| 428 | @ var obj = document.getElementById(id); |
| 429 | @ if( !obj ) return; |
| 430 | @ var left = 0; |
| 431 | @ if( obj.offsetParent ){ |
| 432 | @ do{ |
| 433 | @ left += obj.offsetLeft; |
| 434 | @ }while( obj = obj.offsetParent ); |
| 435 | @ } |
| 436 | @ return left; |
| 437 | @ } |
| 438 | @ function drawUpArrow(x,y0,y1){ |
| 439 | @ drawBox("black",x,y0,x+1,y1); |
| 440 | @ if( y0+8>=y1 ){ |
| 441 | @ drawBox("black",x-1,y0+1,x+2,y0+2); |
| 442 | @ drawBox("black",x-2,y0+3,x+3,y0+4); |
| 443 | @ }else{ |
| 444 | @ drawBox("black",x-1,y0+2,x+2,y0+4); |
| 445 | @ drawBox("black",x-2,y0+5,x+3,y0+7); |
| 446 | @ } |
| 447 | @ } |
| 448 | @ function drawThinArrow(y,xFrom,xTo){ |
| 449 | @ if( xFrom<xTo ){ |
| 450 | @ drawBox("black",xFrom,y,xTo,y); |
| 451 | @ drawBox("black",xTo-4,y-1,xTo-2,y+1); |
| 452 | @ if( xTo>xFrom-8 ) drawBox("black",xTo-6,y-2,xTo-5,y+2); |
| 453 | @ }else{ |
| 454 | @ drawBox("black",xTo,y,xFrom,y); |
| 455 | @ drawBox("black",xTo+2,y-1,xTo+4,y+1); |
| 456 | @ if( xTo+8<xFrom ) drawBox("black",xTo+5,y-2,xTo+6,y+2); |
| 457 | @ } |
| 458 | @ } |
| 459 | @ function drawThinLine(x0,y0,x1,y1){ |
| 460 | @ drawBox("black",x0,y0,x1,y1); |
| 461 | @ } |
| 462 | @ function drawNode(p, left, btm){ |
| 463 | @ drawBox("black",p.x-5,p.y-5,p.x+6,p.y+6); |
| 464 | @ drawBox("white",p.x-4,p.y-4,p.x+5,p.y+5); |
| 465 | @ if( p.u>0 ){ |
| 466 | @ var u = rowinfo[p.u-1]; |
| 467 | @ drawUpArrow(p.x, u.y+6, p.y-5); |
| 468 | @ } |
| 469 | @ if( p.d ){ |
| 470 | @ drawUpArrow(p.x, p.y+6, btm); |
| 471 | @ } |
| 472 | @ if( p.mo>=0 ){ |
| 473 | @ var x1 = p.mo*20 + left; |
| 474 | @ var y1 = p.y-3; |
| 475 | @ var x0 = x1>p.x ? p.x+7 : p.x-6; |
| 476 | @ var u = rowinfo[p.mu-1]; |
| 477 | @ var y0 = u.y+5; |
| 478 | @ drawThinLine(x0,y1,x1,y1); |
| 479 | @ drawThinLine(x1,y0,x1,y1); |
| 480 | @ } |
| 481 | @ var n = p.au.length; |
| 482 | @ for(var i=0; i<n; i+=2){ |
| 483 | @ var x1 = p.au[i]*20 + left; |
| 484 | @ var x0 = x1>p.x ? p.x+7 : p.x-6; |
| 485 | @ drawBox("black",x0,p.y,x1,p.y+1); |
| 486 | @ var u = rowinfo[p.au[i+1]-1]; |
| 487 | @ drawUpArrow(x1, u.y+6, p.y); |
| 488 | @ } |
| 489 | @ for(var j in p.mi){ |
| 490 | @ var y0 = p.y+5; |
| 491 | @ var mx = p.mi[j]*20 + left; |
| 492 | @ if( mx>p.x ){ |
| 493 | @ drawThinArrow(y0,mx,p.x+5); |
| 494 | @ }else{ |
| 495 | @ drawThinArrow(y0,mx,p.x-5); |
| 496 | @ } |
| 497 | @ } |
| 498 | @ } |
| 499 | @ function renderGraph(){ |
| 500 | @ var canvasDiv = document.getElementById("canvas"); |
| 501 | @ for(var i=canvasDiv.childNodes.length-1; i>=0; i--){ |
| 502 | @ var c = canvasDiv.childNodes[i]; |
| 503 | @ delete canvasDiv.removeChild(c); |
| 504 | @ } |
| 505 | @ var canvasY = absoluteY("canvas"); |
| 506 | @ var left = absoluteX(rowinfo[0].id) - absoluteX("canvas") + 15; |
| 507 | @ for(var i in rowinfo){ |
| 508 | @ rowinfo[i].y = absoluteY(rowinfo[i].id) + 10 - canvasY; |
| 509 | @ rowinfo[i].x = left + rowinfo[i].r*20; |
| 510 | @ } |
| 511 | @ var btm = rowinfo[rowinfo.length-1].y + 20; |
| 512 | @ for(var i in rowinfo){ |
| 513 | @ drawNode(rowinfo[i], left, btm); |
| 514 | @ } |
| 515 | @ } |
| 516 | @ var lastId = rowinfo[rowinfo.length-1].id; |
| 517 | @ var lastY = 0; |
| 518 | @ function checkHeight(){ |
| 519 | @ var h = absoluteY(lastId); |
| 520 | @ if( h!=lastY ){ |
| 521 | @ renderGraph(); |
| 522 | @ lastY = h; |
| 523 | @ } |
| 524 | @ setTimeout("checkHeight();", 1000); |
| 525 | @ } |
| 526 | @ checkHeight(); |
| 527 | @ </script> |
| 528 | } |
| 529 | } |
| 530 | |
| 531 | /* |
| 532 | ** Create a temporary table suitable for storing timeline data. |
| 533 | */ |
| @@ -461,13 +673,13 @@ | |
| 673 | tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'", zTagName); |
| 674 | }else{ |
| 675 | tagid = 0; |
| 676 | } |
| 677 | if( zType[0]=='a' ){ |
| 678 | tmFlags = TIMELINE_BRIEF | TIMELINE_GRAPH; |
| 679 | }else{ |
| 680 | tmFlags = TIMELINE_GRAPH; |
| 681 | } |
| 682 | |
| 683 | style_header("Timeline"); |
| 684 | login_anonymous_available(); |
| 685 | timeline_temp_table(); |
| @@ -645,10 +857,11 @@ | |
| 857 | if( zUser ){ |
| 858 | blob_appendf(&desc, " by user %h", zUser); |
| 859 | } |
| 860 | if( tagid>0 ){ |
| 861 | blob_appendf(&desc, " tagged with \"%h\"", zTagName); |
| 862 | tmFlags &= ~TIMELINE_GRAPH; |
| 863 | } |
| 864 | if( zAfter ){ |
| 865 | blob_appendf(&desc, " occurring on or after %h.<br>", zAfter); |
| 866 | }else if( zBefore ){ |
| 867 | blob_appendf(&desc, " occurring on or before %h.<br>", zBefore); |
| @@ -691,103 +904,10 @@ | |
| 904 | db_prepare(&q, "SELECT * FROM timeline ORDER BY timestamp DESC"); |
| 905 | @ <h2>%b(&desc)</h2> |
| 906 | blob_reset(&desc); |
| 907 | www_print_timeline(&q, tmFlags, 0); |
| 908 | db_finalize(&q); |
| 909 | style_footer(); |
| 910 | } |
| 911 | |
| 912 | /* |
| 913 | ** The input query q selects various records. Print a human-readable |
| 914 |
+1
-1
| --- src/update.c | ||
| +++ src/update.c | ||
| @@ -78,10 +78,11 @@ | ||
| 78 | 78 | fossil_fatal("cannot find current version"); |
| 79 | 79 | } |
| 80 | 80 | if( db_exists("SELECT 1 FROM vmerge") ){ |
| 81 | 81 | fossil_fatal("cannot update an uncommitted merge"); |
| 82 | 82 | } |
| 83 | + if( !nochangeFlag ) autosync(AUTOSYNC_PULL); | |
| 83 | 84 | |
| 84 | 85 | if( g.argc>=3 ){ |
| 85 | 86 | if( strcmp(g.argv[2], "current")==0 ){ |
| 86 | 87 | /* If VERSION is "current", then use the same algorithm to find the |
| 87 | 88 | ** target as if VERSION were omitted. */ |
| @@ -97,11 +98,10 @@ | ||
| 97 | 98 | }else if( !is_a_version(tid) ){ |
| 98 | 99 | fossil_fatal("no such version: %s", g.argv[2]); |
| 99 | 100 | } |
| 100 | 101 | } |
| 101 | 102 | } |
| 102 | - if( !nochangeFlag ) autosync(AUTOSYNC_PULL); | |
| 103 | 103 | |
| 104 | 104 | if( tid==0 ){ |
| 105 | 105 | compute_leaves(vid, 1); |
| 106 | 106 | if( !latestFlag && db_int(0, "SELECT count(*) FROM leaves")>1 ){ |
| 107 | 107 | db_prepare(&q, |
| 108 | 108 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -78,10 +78,11 @@ | |
| 78 | fossil_fatal("cannot find current version"); |
| 79 | } |
| 80 | if( db_exists("SELECT 1 FROM vmerge") ){ |
| 81 | fossil_fatal("cannot update an uncommitted merge"); |
| 82 | } |
| 83 | |
| 84 | if( g.argc>=3 ){ |
| 85 | if( strcmp(g.argv[2], "current")==0 ){ |
| 86 | /* If VERSION is "current", then use the same algorithm to find the |
| 87 | ** target as if VERSION were omitted. */ |
| @@ -97,11 +98,10 @@ | |
| 97 | }else if( !is_a_version(tid) ){ |
| 98 | fossil_fatal("no such version: %s", g.argv[2]); |
| 99 | } |
| 100 | } |
| 101 | } |
| 102 | if( !nochangeFlag ) autosync(AUTOSYNC_PULL); |
| 103 | |
| 104 | if( tid==0 ){ |
| 105 | compute_leaves(vid, 1); |
| 106 | if( !latestFlag && db_int(0, "SELECT count(*) FROM leaves")>1 ){ |
| 107 | db_prepare(&q, |
| 108 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -78,10 +78,11 @@ | |
| 78 | fossil_fatal("cannot find current version"); |
| 79 | } |
| 80 | if( db_exists("SELECT 1 FROM vmerge") ){ |
| 81 | fossil_fatal("cannot update an uncommitted merge"); |
| 82 | } |
| 83 | if( !nochangeFlag ) autosync(AUTOSYNC_PULL); |
| 84 | |
| 85 | if( g.argc>=3 ){ |
| 86 | if( strcmp(g.argv[2], "current")==0 ){ |
| 87 | /* If VERSION is "current", then use the same algorithm to find the |
| 88 | ** target as if VERSION were omitted. */ |
| @@ -97,11 +98,10 @@ | |
| 98 | }else if( !is_a_version(tid) ){ |
| 99 | fossil_fatal("no such version: %s", g.argv[2]); |
| 100 | } |
| 101 | } |
| 102 | } |
| 103 | |
| 104 | if( tid==0 ){ |
| 105 | compute_leaves(vid, 1); |
| 106 | if( !latestFlag && db_int(0, "SELECT count(*) FROM leaves")>1 ){ |
| 107 | db_prepare(&q, |
| 108 |
+29
| --- src/wiki.c | ||
| +++ src/wiki.c | ||
| @@ -151,10 +151,13 @@ | ||
| 151 | 151 | if( g.okNewWiki ){ |
| 152 | 152 | @ <li> Create a <a href="%s(g.zBaseURL)/wikinew">new wiki page</a>.</li> |
| 153 | 153 | } |
| 154 | 154 | @ <li> <a href="%s(g.zBaseURL)/wcontent">List of All Wiki Pages</a> |
| 155 | 155 | @ available on this server.</li> |
| 156 | + @ <li> <form method="GET" action="%s(g.zBaseURL)/wfind"> | |
| 157 | + @ Search wiki titles: <input type="text" name="title"/> <input type="submit" /> | |
| 158 | + @ </li> | |
| 156 | 159 | @ </ul> |
| 157 | 160 | style_footer(); |
| 158 | 161 | return; |
| 159 | 162 | } |
| 160 | 163 | if( check_name(zPageName) ) return; |
| @@ -614,10 +617,36 @@ | ||
| 614 | 617 | @ <ul> |
| 615 | 618 | db_prepare(&q, |
| 616 | 619 | "SELECT substr(tagname, 6, 1000) FROM tag WHERE tagname GLOB 'wiki-*'" |
| 617 | 620 | " ORDER BY lower(tagname)" |
| 618 | 621 | ); |
| 622 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 623 | + const char *zName = db_column_text(&q, 0); | |
| 624 | + @ <li><a href="%s(g.zBaseURL)/wiki?name=%T(zName)">%h(zName)</a></li> | |
| 625 | + } | |
| 626 | + db_finalize(&q); | |
| 627 | + @ </ul> | |
| 628 | + style_footer(); | |
| 629 | +} | |
| 630 | + | |
| 631 | +/* | |
| 632 | +** WEBPAGE: wfind | |
| 633 | +** | |
| 634 | +** URL: /wfind?title=TITLE | |
| 635 | +** List all wiki pages whose titles contain the search text | |
| 636 | +*/ | |
| 637 | +void wfind_page(void){ | |
| 638 | + Stmt q; | |
| 639 | + const char * zTitle; | |
| 640 | + login_check_credentials(); | |
| 641 | + if( !g.okRdWiki ){ login_needed(); return; } | |
| 642 | + zTitle = PD("title","*"); | |
| 643 | + style_header("Wiki Pages Found"); | |
| 644 | + @ <ul> | |
| 645 | + db_prepare(&q, | |
| 646 | + "SELECT substr(tagname, 6, 1000) FROM tag WHERE tagname like 'wiki-%%%q%%' ORDER BY lower(tagname)" , | |
| 647 | + zTitle); | |
| 619 | 648 | while( db_step(&q)==SQLITE_ROW ){ |
| 620 | 649 | const char *zName = db_column_text(&q, 0); |
| 621 | 650 | @ <li><a href="%s(g.zBaseURL)/wiki?name=%T(zName)">%h(zName)</a></li> |
| 622 | 651 | } |
| 623 | 652 | db_finalize(&q); |
| 624 | 653 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -151,10 +151,13 @@ | |
| 151 | if( g.okNewWiki ){ |
| 152 | @ <li> Create a <a href="%s(g.zBaseURL)/wikinew">new wiki page</a>.</li> |
| 153 | } |
| 154 | @ <li> <a href="%s(g.zBaseURL)/wcontent">List of All Wiki Pages</a> |
| 155 | @ available on this server.</li> |
| 156 | @ </ul> |
| 157 | style_footer(); |
| 158 | return; |
| 159 | } |
| 160 | if( check_name(zPageName) ) return; |
| @@ -614,10 +617,36 @@ | |
| 614 | @ <ul> |
| 615 | db_prepare(&q, |
| 616 | "SELECT substr(tagname, 6, 1000) FROM tag WHERE tagname GLOB 'wiki-*'" |
| 617 | " ORDER BY lower(tagname)" |
| 618 | ); |
| 619 | while( db_step(&q)==SQLITE_ROW ){ |
| 620 | const char *zName = db_column_text(&q, 0); |
| 621 | @ <li><a href="%s(g.zBaseURL)/wiki?name=%T(zName)">%h(zName)</a></li> |
| 622 | } |
| 623 | db_finalize(&q); |
| 624 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -151,10 +151,13 @@ | |
| 151 | if( g.okNewWiki ){ |
| 152 | @ <li> Create a <a href="%s(g.zBaseURL)/wikinew">new wiki page</a>.</li> |
| 153 | } |
| 154 | @ <li> <a href="%s(g.zBaseURL)/wcontent">List of All Wiki Pages</a> |
| 155 | @ available on this server.</li> |
| 156 | @ <li> <form method="GET" action="%s(g.zBaseURL)/wfind"> |
| 157 | @ Search wiki titles: <input type="text" name="title"/> <input type="submit" /> |
| 158 | @ </li> |
| 159 | @ </ul> |
| 160 | style_footer(); |
| 161 | return; |
| 162 | } |
| 163 | if( check_name(zPageName) ) return; |
| @@ -614,10 +617,36 @@ | |
| 617 | @ <ul> |
| 618 | db_prepare(&q, |
| 619 | "SELECT substr(tagname, 6, 1000) FROM tag WHERE tagname GLOB 'wiki-*'" |
| 620 | " ORDER BY lower(tagname)" |
| 621 | ); |
| 622 | while( db_step(&q)==SQLITE_ROW ){ |
| 623 | const char *zName = db_column_text(&q, 0); |
| 624 | @ <li><a href="%s(g.zBaseURL)/wiki?name=%T(zName)">%h(zName)</a></li> |
| 625 | } |
| 626 | db_finalize(&q); |
| 627 | @ </ul> |
| 628 | style_footer(); |
| 629 | } |
| 630 | |
| 631 | /* |
| 632 | ** WEBPAGE: wfind |
| 633 | ** |
| 634 | ** URL: /wfind?title=TITLE |
| 635 | ** List all wiki pages whose titles contain the search text |
| 636 | */ |
| 637 | void wfind_page(void){ |
| 638 | Stmt q; |
| 639 | const char * zTitle; |
| 640 | login_check_credentials(); |
| 641 | if( !g.okRdWiki ){ login_needed(); return; } |
| 642 | zTitle = PD("title","*"); |
| 643 | style_header("Wiki Pages Found"); |
| 644 | @ <ul> |
| 645 | db_prepare(&q, |
| 646 | "SELECT substr(tagname, 6, 1000) FROM tag WHERE tagname like 'wiki-%%%q%%' ORDER BY lower(tagname)" , |
| 647 | zTitle); |
| 648 | while( db_step(&q)==SQLITE_ROW ){ |
| 649 | const char *zName = db_column_text(&q, 0); |
| 650 | @ <li><a href="%s(g.zBaseURL)/wiki?name=%T(zName)">%h(zName)</a></li> |
| 651 | } |
| 652 | db_finalize(&q); |
| 653 |
+12
-3
| --- src/winhttp.c | ||
| +++ src/winhttp.c | ||
| @@ -36,10 +36,11 @@ | ||
| 36 | 36 | typedef struct HttpRequest HttpRequest; |
| 37 | 37 | struct HttpRequest { |
| 38 | 38 | int id; /* ID counter */ |
| 39 | 39 | SOCKET s; /* Socket on which to receive data */ |
| 40 | 40 | SOCKADDR_IN addr; /* Address from which data is coming */ |
| 41 | + const char *zNotFound; /* --notfound option, or an empty string */ | |
| 41 | 42 | }; |
| 42 | 43 | |
| 43 | 44 | /* |
| 44 | 45 | ** Prefix for a temporary file. |
| 45 | 46 | */ |
| @@ -109,13 +110,13 @@ | ||
| 109 | 110 | } |
| 110 | 111 | wanted -= got; |
| 111 | 112 | } |
| 112 | 113 | fclose(out); |
| 113 | 114 | out = 0; |
| 114 | - sprintf(zCmd, "\"%s\" http \"%s\" %s %s %s", | |
| 115 | + sprintf(zCmd, "\"%s\" http \"%s\" %s %s %s%s", | |
| 115 | 116 | g.argv[0], g.zRepositoryName, zRequestFName, zReplyFName, |
| 116 | - inet_ntoa(p->addr.sin_addr) | |
| 117 | + inet_ntoa(p->addr.sin_addr), p->zNotFound | |
| 117 | 118 | ); |
| 118 | 119 | portable_system(zCmd); |
| 119 | 120 | in = fopen(zReplyFName, "rb"); |
| 120 | 121 | if( in ){ |
| 121 | 122 | while( (got = fread(zHdr, 1, sizeof(zHdr), in))>0 ){ |
| @@ -137,19 +138,26 @@ | ||
| 137 | 138 | ** that socket. |
| 138 | 139 | */ |
| 139 | 140 | void win32_http_server( |
| 140 | 141 | int mnPort, int mxPort, /* Range of allowed TCP port numbers */ |
| 141 | 142 | char *zBrowser, /* Command to launch browser. (Or NULL) */ |
| 142 | - char *zStopper /* Stop server when this file is exists (Or NULL) */ | |
| 143 | + char *zStopper, /* Stop server when this file is exists (Or NULL) */ | |
| 144 | + char *zNotFound /* The --notfound option, or NULL */ | |
| 143 | 145 | ){ |
| 144 | 146 | WSADATA wd; |
| 145 | 147 | SOCKET s = INVALID_SOCKET; |
| 146 | 148 | SOCKADDR_IN addr; |
| 147 | 149 | int idCnt = 0; |
| 148 | 150 | int iPort = mnPort; |
| 151 | + char *zNotFoundOption; | |
| 149 | 152 | |
| 150 | 153 | if( zStopper ) unlink(zStopper); |
| 154 | + if( zNotFound ){ | |
| 155 | + zNotFoundOption = mprintf(" --notfound %s", zNotFound); | |
| 156 | + }else{ | |
| 157 | + zNotFoundOption = ""; | |
| 158 | + } | |
| 151 | 159 | if( WSAStartup(MAKEWORD(1,1), &wd) ){ |
| 152 | 160 | fossil_fatal("unable to initialize winsock"); |
| 153 | 161 | } |
| 154 | 162 | while( iPort<=mxPort ){ |
| 155 | 163 | s = socket(AF_INET, SOCK_STREAM, 0); |
| @@ -206,12 +214,13 @@ | ||
| 206 | 214 | fossil_fatal("out of memory"); |
| 207 | 215 | } |
| 208 | 216 | p->id = ++idCnt; |
| 209 | 217 | p->s = client; |
| 210 | 218 | p->addr = client_addr; |
| 219 | + p->zNotFound = zNotFoundOption; | |
| 211 | 220 | _beginthread(win32_process_one_http_request, 0, (void*)p); |
| 212 | 221 | } |
| 213 | 222 | closesocket(s); |
| 214 | 223 | WSACleanup(); |
| 215 | 224 | } |
| 216 | 225 | |
| 217 | 226 | #endif /* __MINGW32__ -- This code is for win32 only */ |
| 218 | 227 |
| --- src/winhttp.c | |
| +++ src/winhttp.c | |
| @@ -36,10 +36,11 @@ | |
| 36 | typedef struct HttpRequest HttpRequest; |
| 37 | struct HttpRequest { |
| 38 | int id; /* ID counter */ |
| 39 | SOCKET s; /* Socket on which to receive data */ |
| 40 | SOCKADDR_IN addr; /* Address from which data is coming */ |
| 41 | }; |
| 42 | |
| 43 | /* |
| 44 | ** Prefix for a temporary file. |
| 45 | */ |
| @@ -109,13 +110,13 @@ | |
| 109 | } |
| 110 | wanted -= got; |
| 111 | } |
| 112 | fclose(out); |
| 113 | out = 0; |
| 114 | sprintf(zCmd, "\"%s\" http \"%s\" %s %s %s", |
| 115 | g.argv[0], g.zRepositoryName, zRequestFName, zReplyFName, |
| 116 | inet_ntoa(p->addr.sin_addr) |
| 117 | ); |
| 118 | portable_system(zCmd); |
| 119 | in = fopen(zReplyFName, "rb"); |
| 120 | if( in ){ |
| 121 | while( (got = fread(zHdr, 1, sizeof(zHdr), in))>0 ){ |
| @@ -137,19 +138,26 @@ | |
| 137 | ** that socket. |
| 138 | */ |
| 139 | void win32_http_server( |
| 140 | int mnPort, int mxPort, /* Range of allowed TCP port numbers */ |
| 141 | char *zBrowser, /* Command to launch browser. (Or NULL) */ |
| 142 | char *zStopper /* Stop server when this file is exists (Or NULL) */ |
| 143 | ){ |
| 144 | WSADATA wd; |
| 145 | SOCKET s = INVALID_SOCKET; |
| 146 | SOCKADDR_IN addr; |
| 147 | int idCnt = 0; |
| 148 | int iPort = mnPort; |
| 149 | |
| 150 | if( zStopper ) unlink(zStopper); |
| 151 | if( WSAStartup(MAKEWORD(1,1), &wd) ){ |
| 152 | fossil_fatal("unable to initialize winsock"); |
| 153 | } |
| 154 | while( iPort<=mxPort ){ |
| 155 | s = socket(AF_INET, SOCK_STREAM, 0); |
| @@ -206,12 +214,13 @@ | |
| 206 | fossil_fatal("out of memory"); |
| 207 | } |
| 208 | p->id = ++idCnt; |
| 209 | p->s = client; |
| 210 | p->addr = client_addr; |
| 211 | _beginthread(win32_process_one_http_request, 0, (void*)p); |
| 212 | } |
| 213 | closesocket(s); |
| 214 | WSACleanup(); |
| 215 | } |
| 216 | |
| 217 | #endif /* __MINGW32__ -- This code is for win32 only */ |
| 218 |
| --- src/winhttp.c | |
| +++ src/winhttp.c | |
| @@ -36,10 +36,11 @@ | |
| 36 | typedef struct HttpRequest HttpRequest; |
| 37 | struct HttpRequest { |
| 38 | int id; /* ID counter */ |
| 39 | SOCKET s; /* Socket on which to receive data */ |
| 40 | SOCKADDR_IN addr; /* Address from which data is coming */ |
| 41 | const char *zNotFound; /* --notfound option, or an empty string */ |
| 42 | }; |
| 43 | |
| 44 | /* |
| 45 | ** Prefix for a temporary file. |
| 46 | */ |
| @@ -109,13 +110,13 @@ | |
| 110 | } |
| 111 | wanted -= got; |
| 112 | } |
| 113 | fclose(out); |
| 114 | out = 0; |
| 115 | sprintf(zCmd, "\"%s\" http \"%s\" %s %s %s%s", |
| 116 | g.argv[0], g.zRepositoryName, zRequestFName, zReplyFName, |
| 117 | inet_ntoa(p->addr.sin_addr), p->zNotFound |
| 118 | ); |
| 119 | portable_system(zCmd); |
| 120 | in = fopen(zReplyFName, "rb"); |
| 121 | if( in ){ |
| 122 | while( (got = fread(zHdr, 1, sizeof(zHdr), in))>0 ){ |
| @@ -137,19 +138,26 @@ | |
| 138 | ** that socket. |
| 139 | */ |
| 140 | void win32_http_server( |
| 141 | int mnPort, int mxPort, /* Range of allowed TCP port numbers */ |
| 142 | char *zBrowser, /* Command to launch browser. (Or NULL) */ |
| 143 | char *zStopper, /* Stop server when this file is exists (Or NULL) */ |
| 144 | char *zNotFound /* The --notfound option, or NULL */ |
| 145 | ){ |
| 146 | WSADATA wd; |
| 147 | SOCKET s = INVALID_SOCKET; |
| 148 | SOCKADDR_IN addr; |
| 149 | int idCnt = 0; |
| 150 | int iPort = mnPort; |
| 151 | char *zNotFoundOption; |
| 152 | |
| 153 | if( zStopper ) unlink(zStopper); |
| 154 | if( zNotFound ){ |
| 155 | zNotFoundOption = mprintf(" --notfound %s", zNotFound); |
| 156 | }else{ |
| 157 | zNotFoundOption = ""; |
| 158 | } |
| 159 | if( WSAStartup(MAKEWORD(1,1), &wd) ){ |
| 160 | fossil_fatal("unable to initialize winsock"); |
| 161 | } |
| 162 | while( iPort<=mxPort ){ |
| 163 | s = socket(AF_INET, SOCK_STREAM, 0); |
| @@ -206,12 +214,13 @@ | |
| 214 | fossil_fatal("out of memory"); |
| 215 | } |
| 216 | p->id = ++idCnt; |
| 217 | p->s = client; |
| 218 | p->addr = client_addr; |
| 219 | p->zNotFound = zNotFoundOption; |
| 220 | _beginthread(win32_process_one_http_request, 0, (void*)p); |
| 221 | } |
| 222 | closesocket(s); |
| 223 | WSACleanup(); |
| 224 | } |
| 225 | |
| 226 | #endif /* __MINGW32__ -- This code is for win32 only */ |
| 227 |
+1
-1
| --- www/index.wiki | ||
| +++ www/index.wiki | ||
| @@ -56,11 +56,11 @@ | ||
| 56 | 56 | 3. <b>Autosync</b> - |
| 57 | 57 | Fossil supports [./concepts.wiki#workflow | "autosync" mode] |
| 58 | 58 | which helps to keep projects moving |
| 59 | 59 | forward by reducing the amount of needless |
| 60 | 60 | [./branching.wiki | forking and merging] often |
| 61 | - associated distributed projects. | |
| 61 | + associated with distributed projects. | |
| 62 | 62 | |
| 63 | 63 | 4. <b>Self-Contained</b> - |
| 64 | 64 | Fossil is a single stand-alone executable that contains everything |
| 65 | 65 | needed to do configuration management. |
| 66 | 66 | Installation is trivial: simply download a |
| 67 | 67 |
| --- www/index.wiki | |
| +++ www/index.wiki | |
| @@ -56,11 +56,11 @@ | |
| 56 | 3. <b>Autosync</b> - |
| 57 | Fossil supports [./concepts.wiki#workflow | "autosync" mode] |
| 58 | which helps to keep projects moving |
| 59 | forward by reducing the amount of needless |
| 60 | [./branching.wiki | forking and merging] often |
| 61 | associated distributed projects. |
| 62 | |
| 63 | 4. <b>Self-Contained</b> - |
| 64 | Fossil is a single stand-alone executable that contains everything |
| 65 | needed to do configuration management. |
| 66 | Installation is trivial: simply download a |
| 67 |
| --- www/index.wiki | |
| +++ www/index.wiki | |
| @@ -56,11 +56,11 @@ | |
| 56 | 3. <b>Autosync</b> - |
| 57 | Fossil supports [./concepts.wiki#workflow | "autosync" mode] |
| 58 | which helps to keep projects moving |
| 59 | forward by reducing the amount of needless |
| 60 | [./branching.wiki | forking and merging] often |
| 61 | associated with distributed projects. |
| 62 | |
| 63 | 4. <b>Self-Contained</b> - |
| 64 | Fossil is a single stand-alone executable that contains everything |
| 65 | needed to do configuration management. |
| 66 | Installation is trivial: simply download a |
| 67 |