SHA1 Hash: | 76bc05d739e816553faf49245db4b26a5d9726fa |
---|---|
Date: | 2009-12-30 20:33:59 |
User: | btheado |
Comment: | merge with trunk |
Timelines: | ancestors | descendants | both | sql-func |
Other Links: | files | ZIP archive | manifest |
- branch=sql-func inherited from [f41358e7ca]
- sym-sql-func inherited from [f41358e7ca]
Modified src/checkin.c from [c1819242e8] to [43b7742fd6].
@@ -31,14 +31,22 @@ /* ** Generate text describing all changes. Prepend zPrefix to each line ** of output. ** ** We assume that vfile_check_signature has been run. +** +** If missingIsFatal is true, then any files that are missing or which +** are not true files results in a fatal error. */ -static void status_report(Blob *report, const char *zPrefix){ +static void status_report( + Blob *report, /* Append the status report here */ + const char *zPrefix, /* Prefix on each line of the report */ + int missingIsFatal /* MISSING and NOT_A_FILE are fatal errors */ +){ Stmt q; int nPrefix = strlen(zPrefix); + int nErr = 0; db_prepare(&q, "SELECT pathname, deleted, chnged, rid, coalesce(origname!=pathname,0)" " FROM vfile " " WHERE file_is_selected(id)" " AND (chnged OR deleted OR rid=0 OR pathname!=origname) ORDER BY 1" @@ -50,25 +58,37 @@ int isNew = db_column_int(&q,3)==0; int isRenamed = db_column_int(&q,4); char *zFullName = mprintf("%s/%s", g.zLocalRoot, zPathname); blob_append(report, zPrefix, nPrefix); if( isDeleted ){ - blob_appendf(report, "DELETED %s\n", zPathname); - }else if( access(zFullName, 0) ){ - blob_appendf(report, "MISSING %s\n", zPathname); + blob_appendf(report, "DELETED %s\n", zPathname); + }else if( !file_isfile(zFullName) ){ + if( access(zFullName, 0)==0 ){ + blob_appendf(report, "NOT_A_FILE %s\n", zPathname); + if( missingIsFatal ){ + fossil_warning("not a file: %s", zPathname); + nErr++; + } + }else{ + blob_appendf(report, "MISSING %s\n", zPathname); + if( missingIsFatal ){ + fossil_warning("missing file: %s", zPathname); + nErr++; + } + } }else if( isNew ){ - blob_appendf(report, "ADDED %s\n", zPathname); + blob_appendf(report, "ADDED %s\n", zPathname); }else if( isDeleted ){ - blob_appendf(report, "DELETED %s\n", zPathname); + blob_appendf(report, "DELETED %s\n", zPathname); }else if( isChnged==2 ){ blob_appendf(report, "UPDATED_BY_MERGE %s\n", zPathname); }else if( isChnged==3 ){ blob_appendf(report, "ADDED_BY_MERGE %s\n", zPathname); }else if( isChnged==1 ){ - blob_appendf(report, "EDITED %s\n", zPathname); + blob_appendf(report, "EDITED %s\n", zPathname); }else if( isRenamed ){ - blob_appendf(report, "RENAMED %s\n", zPathname); + blob_appendf(report, "RENAMED %s\n", zPathname); } free(zFullName); } db_finalize(&q); db_prepare(&q, "SELECT uuid FROM vmerge JOIN blob ON merge=rid" @@ -76,10 +96,13 @@ while( db_step(&q)==SQLITE_ROW ){ blob_append(report, zPrefix, nPrefix); blob_appendf(report, "MERGED_WITH %s\n", db_column_text(&q, 0)); } db_finalize(&q); + if( nErr ){ + fossil_fatal("aborting due to prior errors"); + } } /* ** COMMAND: changes ** @@ -92,12 +115,12 @@ Blob report; int vid; db_must_be_within_tree(); blob_zero(&report); vid = db_lget_int("checkout", 0); - vfile_check_signature(vid); - status_report(&report, ""); + vfile_check_signature(vid, 0); + status_report(&report, "", 0); blob_write_to_file(&report, "-"); } /* ** COMMAND: status @@ -121,21 +144,24 @@ } /* ** COMMAND: ls ** -** Usage: %fossil ls -** -** Show the names of all files in the current checkout +** Usage: %fossil ls [-l] +** +** Show the names of all files in the current checkout. The -l provides +** extra information about each file. */ void ls_cmd(void){ int vid; Stmt q; - + int isBrief; + + isBrief = find_option("l","l", 0)==0; db_must_be_within_tree(); vid = db_lget_int("checkout", 0); - vfile_check_signature(vid); + vfile_check_signature(vid, 0); db_prepare(&q, "SELECT pathname, deleted, rid, chnged, coalesce(origname!=pathname,0)" " FROM vfile" " ORDER BY 1" ); @@ -144,22 +170,28 @@ int isDeleted = db_column_int(&q, 1); int isNew = db_column_int(&q,2)==0; int chnged = db_column_int(&q,3); int renamed = db_column_int(&q,4); char *zFullName = mprintf("%s/%s", g.zLocalRoot, zPathname); - if( isNew ){ - printf("ADDED %s\n", zPathname); - }else if( access(zFullName, 0) ){ - printf("MISSING %s\n", zPathname); + if( isBrief ){ + printf("%s\n", zPathname); + }else if( isNew ){ + printf("ADDED %s\n", zPathname); + }else if( !file_isfile(zFullName) ){ + if( access(zFullName, 0)==0 ){ + printf("NOT_A_FILE %s\n", zPathname); + }else{ + printf("MISSING %s\n", zPathname); + } }else if( isDeleted ){ - printf("DELETED %s\n", zPathname); + printf("DELETED %s\n", zPathname); }else if( chnged ){ - printf("EDITED %s\n", zPathname); + printf("EDITED %s\n", zPathname); }else if( renamed ){ - printf("RENAMED %s\n", zPathname); + printf("RENAMED %s\n", zPathname); }else{ - printf("UNCHANGED %s\n", zPathname); + printf("UNCHANGED %s\n", zPathname); } free(zFullName); } db_finalize(&q); } @@ -256,12 +288,26 @@ ** editor specified in the global_config table or either ** the VISUAL or EDITOR environment variable. ** ** Store the final commit comment in pComment. pComment is assumed ** to be uninitialized - any prior content is overwritten. +** +** zInit is the text of the most recent failed attempt to check in +** this same change. Use zInit to reinitialize the check-in comment +** so that the user does not have to retype. +** +** zBranch is the name of a new branch that this check-in is forced into. +** zBranch might be NULL or an empty string if no forcing occurs. +** +** parent_rid is the recordid of the parent check-in. */ -static void prepare_commit_comment(Blob *pComment, char *zInit){ +static void prepare_commit_comment( + Blob *pComment, + char *zInit, + const char *zBranch, + int parent_rid +){ const char *zEditor; char *zCmd; char *zFile; Blob text, line; char *zComment; @@ -271,18 +317,24 @@ "\n" "# Enter comments on this check-in. Lines beginning with # are ignored.\n" "# The check-in comment follows wiki formatting rules.\n" "#\n", -1 ); + if( zBranch && zBranch[0] ){ + blob_appendf(&text, "# tags: %s\n#\n", zBranch); + }else{ + char *zTags = info_tags_of_checkin(parent_rid); + if( zTags ) blob_appendf(&text, "# tags: %z\n#\n", zTags); + } if( g.markPrivate ){ blob_append(&text, "# PRIVATE BRANCH: This check-in will be private and will not sync to\n" "# repositories.\n" "#\n", -1 ); } - status_report(&text, "# "); + status_report(&text, "# ", 1); zEditor = db_get("editor", 0); if( zEditor==0 ){ zEditor = getenv("VISUAL"); } if( zEditor==0 ){ @@ -553,11 +605,11 @@ }else if( zCommentFile ){ blob_zero(&comment); blob_read_from_file(&comment, zCommentFile); }else{ char *zInit = db_text(0, "SELECT value FROM vvar WHERE name='ci-comment'"); - prepare_commit_comment(&comment, zInit); + prepare_commit_comment(&comment, zInit, zBranch, vid); free(zInit); } if( blob_size(&comment)==0 ){ Blob ans; blob_zero(&ans);
Modified src/checkout.c from [093dea6b92] to [93e8f5c3cb].
@@ -39,11 +39,11 @@ int unsaved_changes(void){ int vid; db_must_be_within_tree(); vid = db_lget_int("checkout",0); if( vid==0 ) return 2; - vfile_check_signature(vid); + vfile_check_signature(vid, 1); return db_exists("SELECT 1 FROM vfile WHERE chnged" " OR coalesce(origname!=pathname,0)"); } /*
Modified src/db.c from [6a9e0f8839] to [07c27acf80].
@@ -1469,47 +1469,46 @@ ** With a value argument it changes the property for the current repository. ** ** The "unset" command clears a property setting. ** ** -** anon-login-enable-captcha-filler -** If enabled, the Login page will provide a button +** auto-captcha If enabled, the Login page will provide a button ** which uses JavaScript to fill out the captcha for -** the user. (Most bots cannot use JavaScript.) +** the "anonymous" user. (Most bots cannot use JavaScript.) ** ** autosync If enabled, automatically pull prior to ** commit or update and automatically push ** after commit or tag or branch creation. +** +** clearsign When enabled (the default), fossil will attempt to +** sign all commits with gpg. When disabled, commits will +** be unsigned. ** ** diff-command External command to run when performing a diff. ** If undefined, the internal text diff will be used. ** ** dont-push Prevent this repository from pushing from client to ** server. Useful when setting up a private branch. ** ** editor Text editor command used for check-in comments. ** +** gdiff-command External command to run when performing a graphical +** diff. If undefined, text diff will be used. +** ** http-port The TCP/IP port number to use by the "server" ** and "ui" commands. Default: 8080 -** -** gdiff-command External command to run when performing a graphical -** diff. If undefined, text diff will be used. ** ** localauth If enabled, require that HTTP connections from ** 127.0.0.1 be authenticated by password. If ** false, all HTTP requests from localhost have ** unrestricted access to the repository. ** -** clearsign When enabled (the default), fossil will attempt to -** sign all commits with gpg. When disabled, commits will -** be unsigned. +** mtime-changes Use file modification times (mtimes) to detect when +** files have been modified. ** ** pgp-command Command used to clear-sign manifests at check-in. ** The default is "gpg --clearsign -o ". -** -** mtime-changes Use file modification times (mtimes) to detect when -** files have been modified. ** ** proxy URL of the HTTP proxy. If undefined or "off" then ** the "http_proxy" environment variable is consulted. ** If the http_proxy environment variable is undefined ** then a direct HTTP connection is used. @@ -1519,21 +1518,21 @@ ** Defaults to "start" on windows, "open" on Mac, ** and "firefox" on Unix. */ void setting_cmd(void){ static const char *azName[] = { - "anon-login-enable-captcha-filler", + "auto-captcha", "autosync", + "clearsign", "diff-command", "dont-push", "editor", "gdiff-command", "http-port", "localauth", - "clearsign", - "pgp-command", "mtime-changes", + "pgp-command", "proxy", "web-browser", }; int i; int globalFlag = find_option("global","g",0)!=0;
Modified src/diff.c from [423e7bd7db] to [189451b157].
@@ -710,11 +710,11 @@ while( db_step(&q)==SQLITE_ROW ){ int pid = db_column_int(&q, 0); const char *zUuid = db_column_text(&q, 1); const char *zDate = db_column_text(&q, 2); const char *zUser = db_column_text(&q, 3); - if( g.okHistory ){ + if( webLabel ){ zLabel = mprintf("<a href='%s/info/%s'>%.10s</a> %s %9.9s", g.zBaseURL, zUuid, zUuid, zDate, zUser); }else{ zLabel = mprintf("%.10s %s %9.9s", zUuid, zDate, zUser); } @@ -744,14 +744,55 @@ if( mid==0 || fnid==0 ){ fossil_redirect_home(); } if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d AND fnid=%d",mid,fnid) ){ fossil_redirect_home(); } style_header("File Annotation"); - annotate_file(&ann, fnid, mid, 1); + annotate_file(&ann, fnid, mid, g.okHistory); @ <pre> for(i=0; i<ann.nOrig; i++){ ((char*)ann.aOrig[i].z)[ann.aOrig[i].n] = 0; @ %s(ann.aOrig[i].zSrc): %h(ann.aOrig[i].z) } @ </pre> style_footer(); +} + +/* +** COMMAND: annotate +** +** %fossil annotate FILENAME +** +** Output the text of a file with markings to show when each line of +** the file was introduced. +*/ +void annotate_cmd(void){ + int fnid; /* Filename ID */ + int fid; /* File instance ID */ + int mid; /* Manifest where file was checked in */ + Blob treename; /* FILENAME translated to canonical form */ + char *zFilename; /* Cannonical filename */ + Annotator ann; /* The annotation of the file */ + int i; /* Loop counter */ + + db_must_be_within_tree(); + if (g.argc<3) { + usage("FILENAME"); + } + file_tree_name(g.argv[2], &treename, 1); + zFilename = blob_str(&treename); + fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename); + if( fnid==0 ){ + fossil_fatal("no such file: %s", zFilename); + } + fid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q", zFilename); + if( fid==0 ){ + fossil_fatal("not part of current checkout: %s", zFilename); + } + mid = db_int(0, "SELECT mid FROM mlink WHERE fid=%d AND fnid=%d", fid, fnid); + if( mid==0 ){ + fossil_panic("unable to find manifest"); + } + annotate_file(&ann, fnid, mid, 0); + for(i=0; i<ann.nOrig; i++){ + printf("%s: %.*s\n", ann.aOrig[i].zSrc, ann.aOrig[i].n, ann.aOrig[i].z); + } }
Modified src/diffcmd.c from [20363d7b42] to [6b2be81037].
@@ -190,11 +190,11 @@ */ static void diff_one_against_disk(const char *zFrom, const char *zDiffCmd){ Blob fname; Blob content; file_tree_name(g.argv[2], &fname, 1); - historical_version_of_file(zFrom, blob_str(&fname), &content); + historical_version_of_file(zFrom, blob_str(&fname), &content, 0); diff_file(&content, g.argv[2], g.argv[2], zDiffCmd); blob_reset(&content); blob_reset(&fname); } @@ -207,11 +207,11 @@ int vid; Blob sql; Stmt q; vid = db_lget_int("checkout", 0); - vfile_check_signature(vid); + vfile_check_signature(vid, 1); blob_zero(&sql); db_begin_transaction(); if( zFrom ){ int rid = name_to_rid(zFrom); if( !is_a_version(rid) ){ @@ -295,12 +295,12 @@ char *zName; Blob fname; Blob v1, v2; file_tree_name(g.argv[2], &fname, 1); zName = blob_str(&fname); - historical_version_of_file(zFrom, zName, &v1); - historical_version_of_file(zTo, zName, &v2); + historical_version_of_file(zFrom, zName, &v1, 0); + historical_version_of_file(zTo, zName, &v2, 0); diff_file_mem(&v1, &v2, zName, zDiffCmd); blob_reset(&v1); blob_reset(&v2); blob_reset(&fname); }
Modified src/info.c from [dd6de258e1] to [461c233b78].
@@ -27,10 +27,28 @@ */ #include "config.h" #include "info.h" #include <assert.h> +/* +** Return a string (in memory obtained from malloc) holding a +** comma-separated list of tags that apply to check-in with +** record-id rid. +** +** Return NULL if there are no such tags. +*/ +char *info_tags_of_checkin(int rid){ + char *zTags; + zTags = db_text(0, "SELECT group_concat(substr(tagname, 5), ', ')" + " FROM tagxref, tag" + " WHERE tagxref.rid=%d AND tagxref.tagtype>0" + " AND tag.tagid=tagxref.tagid" + " AND tag.tagname GLOB 'sym-*'", + rid); + return zTags; +} + /* ** Print common information about a particular record. ** ** * The UUID @@ -44,16 +62,16 @@ char *zTags; char *zDate; char *zUuid; zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); if( zUuid ){ - zDate = db_text("", + zDate = db_text(0, "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d", rid ); /* 01234567890123 */ - printf("%-13s %s %s\n", zUuidName, zUuid, zDate); + printf("%-13s %s %s\n", zUuidName, zUuid, zDate ? zDate : ""); free(zUuid); free(zDate); } db_prepare(&q, "SELECT uuid, pid FROM plink JOIN blob ON pid=rid " " WHERE cid=%d", rid); @@ -77,16 +95,11 @@ ); printf("child: %s %s\n", zUuid, zDate); free(zDate); } db_finalize(&q); - zTags = db_text(0, "SELECT group_concat(substr(tagname, 5), ', ')" - " FROM tagxref, tag" - " WHERE tagxref.rid=%d AND tagxref.tagtype>0" - " AND tag.tagid=tagxref.tagid" - " AND tag.tagname GLOB 'sym-*'", - rid); + zTags = info_tags_of_checkin(rid); if( zTags && zTags[0] ){ printf("tags: %s\n", zTags); } free(zTags); if( zComment ){ @@ -233,19 +246,30 @@ ** WEBPAGE: vinfo ** WEBPAGE: ci ** URL: /ci?name=RID|ARTIFACTID ** ** Display information about a particular check-in. +** +** We also jump here from /info if the name is a version. +** +** If the /ci page is used (instead of /vinfo or /info) then the +** default behavior is to show unified diffs of all file changes. +** With /vinfo and /info, only a list of the changed files are +** shown, without diffs. This behavior is inverted if the +** "show-version-diffs" setting is turned on. */ void ci_page(void){ Stmt q; int rid; int isLeaf; + int showDiff; + const char *zName; login_check_credentials(); if( !g.okRead ){ login_needed(); return; } - rid = name_to_rid(PD("name","0")); + zName = PD("name","0"); + rid = name_to_rid(zName); if( rid==0 ){ style_header("Check-in Information Error"); @ No such object: %h(g.argv[2]) style_footer(); return; @@ -285,25 +309,24 @@ } @ </td></tr> @ <tr><th>Date:</th><td> hyperlink_to_date(zDate, "</td></tr>"); if( zEUser ){ - @ <tr><th>Edited User:</td><td> + @ <tr><th>Edited User:</th><td> hyperlink_to_user(zEUser,zDate,"</td></tr>"); @ <tr><th>Original User:</th><td> hyperlink_to_user(zUser,zDate,"</td></tr>"); }else{ - @ <tr><th>User:</td><td> + @ <tr><th>User:</th><td> hyperlink_to_user(zUser,zDate,"</td></tr>"); } if( zEComment ){ @ <tr><th>Edited Comment:</th><td>%w(zEComment)</td></tr> @ <tr><th>Original Comment:</th><td>%w(zComment)</td></tr> }else{ @ <tr><th>Comment:</th><td>%w(zComment)</td></tr> } - @ </td></tr> if( g.okAdmin ){ db_prepare(&q, "SELECT rcvfrom.ipaddr, user.login, datetime(rcvfrom.mtime)" " FROM blob JOIN rcvfrom USING(rcvid) LEFT JOIN user USING(uid)" " WHERE blob.rid=%d", @@ -355,10 +378,25 @@ login_anonymous_available(); } db_finalize(&q); showTags(rid, ""); @ <div class="section">Changes</div> + showDiff = g.zPath[0]!='c'; + if( db_get_boolean("show-version-diffs", 0)==0 ){ + showDiff = !showDiff; + if( showDiff ){ + @ <a href="%s(g.zBaseURL)/vinfo/%T(zName)">[hide diffs]</a><br/> + }else{ + @ <a href="%s(g.zBaseURL)/ci/%T(zName)">[show diffs]</a><br/> + } + }else{ + if( showDiff ){ + @ <a href="%s(g.zBaseURL)/ci/%T(zName)">[hide diffs]</a><br/> + }else{ + @ <a href="%s(g.zBaseURL)/vinfo/%T(zName)">[show diffs]</a><br/> + } + } db_prepare(&q, "SELECT pid, fid, name, substr(a.uuid,1,10), substr(b.uuid,1,10)" " FROM mlink JOIN filename ON filename.fnid=mlink.fnid" " LEFT JOIN blob a ON a.rid=pid" " LEFT JOIN blob b ON b.rid=fid" @@ -380,22 +418,28 @@ @ <p>Changes to %h(zName)</p> } }else if( zOld && zNew ){ @ <p>Modified <a href="%s(g.zBaseURL)/finfo?name=%T(zName)">%h(zName)</a> @ from <a href="%s(g.zBaseURL)/artifact/%s(zOld)">[%s(zOld)]</a> - @ to <a href="%s(g.zBaseURL)/artifact/%s(zNew)">[%s(zNew)]</a></p> + @ to <a href="%s(g.zBaseURL)/artifact/%s(zNew)">[%s(zNew)].</a> + if( !showDiff ){ + @ + @ <a href="%s(g.zBaseURL)/fdiff?v1=%d(pid)&v2=%d(fid)">[diff]</a> + } }else if( zOld ){ @ <p>Deleted <a href="%s(g.zBaseURL)/finfo?name=%T(zName)">%h(zName)</a> @ version <a href="%s(g.zBaseURL)/artifact/%s(zOld)">[%s(zOld)]</a></p> continue; }else{ @ <p>Added <a href="%s(g.zBaseURL)/finfo?name=%T(zName)">%h(zName)</a> @ version <a href="%s(g.zBaseURL)/artifact/%s(zNew)">[%s(zNew)]</a></p> } - @ <blockquote><pre> - append_diff(pid, fid); - @ </pre></blockquote> + if( showDiff ){ + @ <blockquote><pre> + append_diff(pid, fid); + @ </pre></blockquote> + } } db_finalize(&q); style_footer(); }
Modified src/login.c from [646f690c52] to [5c91402605].
@@ -79,10 +79,29 @@ fossil_redirect_home(); } } /* +** The IP address of the client is stored as part of the anonymous +** login cookie for additional security. But some clients are behind +** firewalls that shift the IP address with each HTTP request. To +** allow such (broken) clients to log in, extract just a prefix of the +** IP address. +*/ +static char *ipPrefix(const char *zIP){ + int i, j; + for(i=j=0; zIP[i]; i++){ + if( zIP[i]=='.' ){ + j++; + if( j==2 ) break; + } + } + return mprintf("%.*s", i, zIP); +} + + +/* ** Check to see if the anonymous login is valid. If it is valid, return ** the userid of the anonymous user. */ static int isValidAnonymousLogin( const char *zUsername, /* The username. Must be "anonymous" */ @@ -168,11 +187,11 @@ zIpAddr = PD("REMOTE_ADDR","nil"); zCookieName = login_cookie_name(); zNow = db_text("0", "SELECT julianday('now')"); blob_init(&b, zNow, -1); - blob_appendf(&b, "/%s/%s", zIpAddr, db_get("captcha-secret","")); + blob_appendf(&b, "/%z/%s", ipPrefix(zIpAddr), db_get("captcha-secret","")); sha1sum_blob(&b, &b); zCookie = sqlite3_mprintf("anon/%s/%s", zNow, blob_buffer(&b)); blob_reset(&b); free(zNow); cgi_set_cookie(zCookieName, zCookie, 0, 6*3600); @@ -250,22 +269,24 @@ @ "Login" button. Your user name will be stored in a browser cookie. @ You must configure your web browser to accept cookies in order for @ the login to take.</p> if( zAnonPw ){ unsigned int uSeed = captcha_seed(); - char const * zDecoded = captcha_decode(uSeed); - int iAllowPasswordFill = db_get_boolean( "anon-login-enable-captcha-filler", 0 ); + char const *zDecoded = captcha_decode(uSeed); + int bAutoCaptcha = db_get_boolean("auto-captcha", 0); char *zCaptcha = captcha_render(zDecoded); @ <input type="hidden" name="cs" value="%u(uSeed)"/> @ <p>Visitors may enter <b>anonymous</b> as the user-ID with @ the 8-character hexadecimal password shown below:</p> @ <center><table border="1" cellpadding="10"><tr><td><pre> @ %s(zCaptcha) @ </pre></td></tr></table> - if( iAllowPasswordFill ) { - @ <input type="button" value="Fill out captcha" onclick="document.getElementById('u').value='anonymous'; document.getElementById('p').value='%s(zDecoded)';"/> + if( bAutoCaptcha ) { + @ <input type="button" value="Fill out captcha" + @ onclick="document.getElementById('u').value='anonymous'; + @ document.getElementById('p').value='%s(zDecoded)';"/> } @ </center> free(zCaptcha); } if( g.zLogin ){ @@ -356,11 +377,12 @@ rTime = atof(&zCookie[5]); for(i=5; zCookie[i] && zCookie[i]!='/'; i++){} blob_init(&b, &zCookie[5], i-5); if( zCookie[i]=='/' ){ i++; } blob_append(&b, "/", 1); - blob_appendf(&b, "%s/%s", zRemoteAddr, db_get("captcha-secret","")); + blob_appendf(&b, "%z/%s", ipPrefix(zRemoteAddr), + db_get("captcha-secret","")); sha1sum_blob(&b, &b); uid = db_int(0, "SELECT uid FROM user WHERE login='anonymous'" " AND length(cap)>0" " AND length(pw)>0"
Modified src/main.c from [0e2e1d04fd] to [f14e3f8203].
@@ -67,10 +67,11 @@ int localOpen; /* True if the local database is open */ char *zLocalRoot; /* The directory holding the local database */ int minPrefix; /* Number of digits needed for a distinct UUID */ int fSqlTrace; /* True if -sqltrace flag is present */ int fSqlPrint; /* True if -sqlprint flag is present */ + int fQuiet; /* True if -quiet flag is present */ int fHttpTrace; /* Trace outbound HTTP requests */ int fNoSync; /* Do not do an autosync even. --nosync */ char *zPath; /* Name of webpage being served */ char *zExtra; /* Extra path information past the webpage name */ char *zBaseURL; /* Full text of the URL being served */ @@ -232,10 +233,11 @@ zCmdName = "cgi"; }else if( argc<2 ){ fprintf(stderr, "Usage: %s COMMAND ...\n", argv[0]); exit(1); }else{ + g.fQuiet = find_option("quiet", 0, 0)!=0; g.fSqlTrace = find_option("sqltrace", 0, 0)!=0; g.fSqlPrint = find_option("sqlprint", 0, 0)!=0; g.fHttpTrace = find_option("httptrace", 0, 0)!=0; g.zLogin = find_option("user", "U", 1); zCmdName = argv[1]; @@ -733,11 +735,11 @@ void cmd_test_http(void){ login_set_capabilities("s"); cmd_http(); } - +#ifndef __MINGW32__ #if !defined(__DARWIN__) && !defined(__APPLE__) /* ** Search for an executable on the PATH environment variable. ** Return true (1) if found and false (0) if not found. */ @@ -755,10 +757,11 @@ if( bExists==0 ) return 1; zPath += i; } return 0; } +#endif #endif /* ** COMMAND: server ** COMMAND: ui
Modified src/main.mk from [08be0ee0ca] to [0f9d767d5d].
@@ -60,10 +60,11 @@ $(SRCDIR)/schema.c \ $(SRCDIR)/search.c \ $(SRCDIR)/setup.c \ $(SRCDIR)/sha1.c \ $(SRCDIR)/shun.c \ + $(SRCDIR)/skins.c \ $(SRCDIR)/stat.c \ $(SRCDIR)/style.c \ $(SRCDIR)/sync.c \ $(SRCDIR)/tag.c \ $(SRCDIR)/th_main.c \ @@ -130,10 +131,11 @@ schema_.c \ search_.c \ setup_.c \ sha1_.c \ shun_.c \ + skins_.c \ stat_.c \ style_.c \ sync_.c \ tag_.c \ th_main_.c \ @@ -200,10 +202,11 @@ schema.o \ search.o \ setup.o \ sha1.o \ shun.o \ + skins.o \ stat.o \ style.o \ sync.o \ tag.o \ th_main.o \ @@ -261,16 +264,16 @@ # noop clean: rm -f *.o *_.c $(APPNAME) VERSION.h rm -f translate makeheaders mkindex page_index.h headers - 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 construct.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 rstats.h schema.h search.h setup.h sha1.h shun.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 + 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 construct.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 rstats.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 page_index.h: $(TRANS_SRC) mkindex ./mkindex $(TRANS_SRC) >$@ headers: page_index.h makeheaders VERSION.h - ./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 construct_.c:construct.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 rstats_.c:rstats.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.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 + ./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 construct_.c:construct.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 rstats_.c:rstats.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 touch headers headers: Makefile Makefile: add_.c: $(SRCDIR)/add.c translate ./translate $(SRCDIR)/add.c >add_.c @@ -613,10 +616,17 @@ shun.o: shun_.c shun.h $(SRCDIR)/config.h $(XTCC) -o shun.o -c shun_.c shun.h: headers +skins_.c: $(SRCDIR)/skins.c translate + ./translate $(SRCDIR)/skins.c >skins_.c + +skins.o: skins_.c skins.h $(SRCDIR)/config.h + $(XTCC) -o skins.o -c skins_.c + +skins.h: headers stat_.c: $(SRCDIR)/stat.c translate ./translate $(SRCDIR)/stat.c >stat_.c stat.o: stat_.c stat.h $(SRCDIR)/config.h $(XTCC) -o stat.o -c stat_.c
Modified src/makemake.tcl from [8696139730] to [1776157a16].
@@ -54,10 +54,11 @@ schema search setup sha1 shun + skins stat style sync tag th_main
Modified src/merge.c from [5979683d08] to [1bca64b642].
@@ -77,11 +77,11 @@ "checkout and %s", g.argv[2]); } if( pid>1 && !db_exists("SELECT 1 FROM plink WHERE cid=%d", pid) ){ fossil_panic("not a version: record #%d", mid); } - vfile_check_signature(vid); + vfile_check_signature(vid, 1); db_begin_transaction(); undo_begin(); load_vfile_from_rid(mid); load_vfile_from_rid(pid);
Modified src/merge3.c from [61efa8a333] to [bbe357cf65].
@@ -161,10 +161,12 @@ int nConflict = 0; /* Number of merge conflicts seen so far */ static const char zBegin[] = ">>>>>>> BEGIN MERGE CONFLICT\n"; static const char zMid[] = "============================\n"; static const char zEnd[] = "<<<<<<< END MERGE CONFLICT\n"; + blob_zero(pOut); /* Merge results stored in pOut */ + /* Compute the edits that occur from pPivot => pV1 (into aC1) ** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is ** an array of integer triples. Within each triple, the first integer ** is the number of lines of text to copy directly from the pivot, ** the second integer is the number of lines of text to omit from the @@ -177,11 +179,10 @@ free(aC1); free(aC2); return -1; } - blob_zero(pOut); /* Merge results stored in pOut */ blob_rewind(pV1); /* Rewind inputs: Needed to reconstruct output */ blob_rewind(pV2); blob_rewind(pPivot); /* Determine the length of the aC1[] and aC2[] change vectors */
Modified src/printf.c from [e955d82165] to [8a4feba8cf].
@@ -153,11 +153,11 @@ ** Find the length of a string as long as that length does not ** exceed N bytes. If no zero terminator is seen in the first ** N bytes then return N. If N is negative, then this routine ** is an alias for strlen(). */ -static int strnlen_(const char *z, int N){ +static int StrNLen32(const char *z, int N){ int n = 0; while( (N-- != 0) && *(z++)!=0 ){ n++; } return n; } @@ -563,11 +563,11 @@ case etPATH: { int i; int limit = flag_alternateform ? va_arg(ap,int) : -1; char *e = va_arg(ap,char*); if( e==0 ){e="";} - length = strnlen_(e, limit); + length = StrNLen32(e, limit); zExtra = bufpt = malloc(length+1); for( i=0; i<length; i++ ){ if( e[i]=='\\' ){ bufpt[i]='/'; }else{ @@ -584,11 +584,11 @@ if( bufpt==0 ){ bufpt = ""; }else if( xtype==etDYNSTRING ){ zExtra = bufpt; } - length = strnlen_(bufpt, limit); + length = StrNLen32(bufpt, limit); if( precision>=0 && precision<length ) length = precision; break; } case etBLOB: { int limit = flag_alternateform ? va_arg(ap, int) : -1;
Modified src/rebuild.c from [ab6ed5de1a] to [0c81bac41f].
@@ -91,12 +91,14 @@ static void rebuild_step_done(rid){ /* assert( bag_find(&bagDone, rid)==0 ); */ bag_insert(&bagDone, rid); if( ttyOutput ){ processCnt++; - printf("%d (%d%%)...\r", processCnt, (processCnt*100/totalSize)); - fflush(stdout); + if (!g.fQuiet) { + printf("%d (%d%%)...\r", processCnt, (processCnt*100/totalSize)); + fflush(stdout); + } } } /* ** Rebuild cross-referencing information for the artifact @@ -206,11 +208,14 @@ char *zTable; bag_init(&bagDone); ttyOutput = doOut; processCnt = 0; - printf("0 (0%%)...\r"); fflush(stdout); + if (!g.fQuiet) { + printf("0 (0%%)...\r"); + fflush(stdout); + } db_multi_exec(zSchemaUpdates); for(;;){ zTable = db_text(0, "SELECT name FROM sqlite_master" " WHERE type='table'" @@ -273,11 +278,11 @@ } } db_finalize(&s); manifest_crosslink_end(); rebuild_tag_trunk(); - if( ttyOutput ){ + if(!g.fQuiet && ttyOutput ){ printf("\n"); } return errCnt; }
Modified src/report.c from [54647a292b] to [9fa0e17130].
@@ -483,28 +483,14 @@ @ subsequent columns are shown on their own rows in the table. This might @ be useful for displaying the description of tickets. This rule does not @ hold if the column is prefixed with "_wiki_". @ </p></li> @ - @ <li><p>The <b>aux()</b> SQL function takes a parameter name as an argument - @ and returns the value that the user enters in the resulting HTML form. A - @ second optional parameter provides a default value for the field.</p></li> - @ - @ <li><p>The <b>option()</b> SQL function takes a parameter name - @ and a quoted SELECT statement as parameters. The query results are - @ presented as an HTML dropdown menu and the function returns - @ the currently selected value. Results may be a single value column or - @ two <b>value,description</b> columns. The first row is the default.</p></li> - @ @ <li><p>The <b>cgi()</b> SQL function takes a parameter name as an argument @ and returns the value of a corresponding CGI query value. If the CGI @ parameter doesn't exist, an optional second argument will be returned @ instead.</p></li> - @ - @ <li><p>The <b>search()</b> SQL function takes a keyword pattern and - @ a search text. The function returns an integer score which is - @ higher depending on how well the search went.</p></li> @ @ <li><p>The query can join other tables in the database besides TICKET. @ </p></li> @ </ul> @
Modified src/setup.c from [800f3104a6] to [e3fd99aa04].
@@ -70,10 +70,12 @@ "Configure the SCM behavior of the repository"); setup_menu_entry("Timeline", "setup_timeline", "Timeline display preferences"); setup_menu_entry("Tickets", "tktsetup", "Configure the trouble-ticketing system for this repository"); + setup_menu_entry("Skins", "setup_skin", + "Select from a menu of prepackaged \"skins\" for the web interface"); setup_menu_entry("CSS", "setup_editcss", "Edit the Cascading Style Sheet used by all pages of this repository"); setup_menu_entry("Header", "setup_header", "Edit HTML text inserted at the top of every page"); setup_menu_entry("Footer", "setup_footer", @@ -269,11 +271,10 @@ ** modified user record. After writing the user record, redirect ** to the page that displays a list of users. */ doWrite = cgi_all("login","info","pw") && !higherUser; if( doWrite ){ - char const * anonLoginCheckedbox = PD("anonymousEnableAutofill",0); char zCap[50]; int i = 0; int aa = P("aa")!=0; int ad = P("ad")!=0; int ae = P("ae")!=0; @@ -338,16 +339,10 @@ db_multi_exec( "REPLACE INTO user(uid,login,info,pw,cap) " "VALUES(nullif(%d,0),%Q,%Q,%Q,'%s')", uid, P("login"), P("info"), zPw, zCap ); - if( anonLoginCheckedbox && (*anonLoginCheckedbox) ){ - db_set( "anon-login-enable-captcha-filler", "on", 0 ); - } - else{ - db_set( "anon-login-enable-captcha-filler", "off", 0 ); - } cgi_redirect("setup_ulist"); return; } /* Load the existing information about the user, if any @@ -479,16 +474,11 @@ @ </td> @ </tr> @ <tr> @ <td align="right">Password:</td> if( strcmp(zLogin, "anonymous")==0 ){ - int enabled = db_get_boolean( "anon-login-enable-captcha-filler", 0 ); - char const * checked = enabled ? "checked=\"checked\"" : ""; - /* User the password for "anonymous" as cleartext */ - @ <td><input type="text" name="pw" value="%h(zPw)"/> - @ <br/>Enable password-filler button for anonymous login? <input type="checkbox" name="anonymousEnableAutofill" %s(checked)/><br/> - @ </td> + @ <td><input type="text" name="pw" value="%h(zPw)"></td> }else if( zPw[0] ){ /* Obscure the password for all other users */ @ <td><input type="password" name="pw" value="**********"></td> }else{ /* Show an empty password as an empty input field */ @@ -735,10 +725,19 @@ @ from the ~/.fossil database. Password login is always required @ for incoming web connections on internet addresses other than @ 127.0.0.1.</p></li> @ <hr> + onoff_attribute("Show javascript button to fill in CAPTCHA", + "auto-captcha", "autocaptcha", 0); + @ <p>When enabled, a button appears on the login screen for user + @ "anonymous" that will automatically fill in the CAPTCHA password. + @ This is less secure that forcing the user to do it manually, but is + @ probably secure enough and it is certainly more convenient for + @ anonymous users.</p> + + @ <hr> entry_attribute("Login expiration time", 6, "cookie-expire", "cex", "8766"); @ <p>The number of hours for which a login is valid. This must be a @ positive number. The default is 8760 hours which is approximately equal @ to a year.</p> @@ -783,10 +782,18 @@ "timeline-utc", "utc", 1); @ <p>Show times as UTC (also sometimes called Greenwich Mean Time (GMT) or @ Zulu) instead of in local time.</p> @ <hr> + onoff_attribute("Show version differences by default", + "show-version-diffs", "vdiff", 0); + @ <p>On the version-information pages linked from the timeline can either + @ show complete diffs of all file changes, or can just list the names of + @ the files that have changed. Users can get to either page by + @ clicking. This setting selects the default.</p> + + @ <hr> entry_attribute("Max timeline comment length", 6, "timeline-max-comment", "tmc", "0"); @ <p>The maximum length of a comment to be displayed in a timeline. @ "0" there is no length limit.</p> @@ -813,10 +820,19 @@ @ <hr> onoff_attribute("Automatically synchronize with repository", "autosync", "autosync", 1); @ <p>Automatically keeps your work in sync with a centralized server.</p> + + @ <hr> + onoff_attribute("Show javascript button to fill in CAPTCHA", + "auto-captcha", "autocaptcha", 0); + @ <p>When enabled, a button appears on the login screen for user + @ "anonymous" that will automatically fill in the CAPTCHA password. + @ This is less secure that forcing the user to do it manually, but is + @ probably secure enough and it is certainly more convenient for + @ anonymous users.</p> @ <hr> onoff_attribute("Sign all commits with GPG", "clearsign", "clearsign", 1); @ <p>When enabled (the default), fossil will attempt to @@ -939,18 +955,21 @@ textarea_attribute(0, 0, 0, "css", "css", zDefaultCSS); } style_header("Edit CSS"); @ <form action="%s(g.zBaseURL)/setup_editcss" method="POST"> login_insert_csrf_secret(); - @ Edit the CSS:<br /> + @ Edit the CSS below:<br /> textarea_attribute("", 40, 80, "css", "css", zDefaultCSS); @ <br /> @ <input type="submit" name="submit" value="Apply Changes"> @ <input type="submit" name="clear" value="Revert To Default"> @ </form> @ <hr> - @ Here is the default CSS: + @ The default CSS is shown below for reference. Other examples + @ of CSS files can be seen on the <a href="setup_skin">skins page</a>. + @ See also the <a href="setup_header">header</a> and + @ <a href="setup_footer">footer</a> editing screens. @ <blockquote><pre> @ %h(zDefaultCSS) @ </pre></blockquote> style_footer(); db_end_transaction(0); @@ -981,11 +1000,14 @@ @ <br /> @ <input type="submit" name="submit" value="Apply Changes"> @ <input type="submit" name="clear" value="Revert To Default"> @ </form> @ <hr> - @ Here is the default page header: + @ The default header is shown below for reference. Other examples + @ of headers can be seen on the <a href="setup_skin">skins page</a>. + @ See also the <a href="setup_editcss">CSS</a> and + @ <a href="setup_footer">footer</a> editing screeens. @ <blockquote><pre> @ %h(zDefaultHeader) @ </pre></blockquote> style_footer(); db_end_transaction(0); @@ -1015,11 +1037,14 @@ @ <br /> @ <input type="submit" name="submit" value="Apply Changes"> @ <input type="submit" name="clear" value="Revert To Default"> @ </form> @ <hr> - @ Here is the default page footer: + @ The default footer is shown below for reference. Other examples + @ of footers can be seen on the <a href="setup_skin">skins page</a>. + @ See also the <a href="setup_editcss">CSS</a> and + @ <a href="setup_header">header</a> editing screens. @ <blockquote><pre> @ %h(zDefaultFooter) @ </pre></blockquote> style_footer(); db_end_transaction(0); @@ -1063,15 +1088,19 @@ style_header("Edit Project Logo"); @ <p>The current project logo has a MIME-Type of <b>%h(zMime)</b> and looks @ like this:</p> @ <blockquote><img src="%s(g.zTop)/logo" alt="logo"></blockquote> @ - @ <form action="%s(g.zBaseURL)/setup_logo" method="POST" - @ enctype="multipart/form-data"> @ <p>The logo is accessible to all users at this URL: @ <a href="%s(g.zBaseURL)/logo">%s(g.zBaseURL)/logo</a>. - @ To set a new logo image, select a file to use as the logo using + @ The logo may or may not appear on each + @ page depending on the <a href="setup_editcss">CSS</a> and + @ <a href="setup_header">header setup</a>.</p> + @ + @ <form action="%s(g.zBaseURL)/setup_logo" method="POST" + @ enctype="multipart/form-data"> + @ <p>To set a new logo image, select a file to use as the logo using @ the entry box below and then press the "Change Logo" button.</p> login_insert_csrf_secret(); @ Logo Image file: @ <input type="file" name="im" size="60" accepts="image/*"><br> @ <input type="submit" name="set" value="Change Logo">
Added src/skins.c version [61096f713e]
@@ -1,1 +1,847 @@ +/* +** Copyright (c) 2009 D. Richard Hipp +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public +** License as published by the Free Software Foundation; either +** version 2 of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public +** License along with this library; if not, write to the +** Free Software Foundation, Inc., 59 Temple Place - Suite 330, +** Boston, MA 02111-1307, USA. +** +** Author contact information: +** drh@hwaci.com +** http://www.hwaci.com/drh/ +** +******************************************************************************* +** +** Implementation of the Setup page for "skins". +*/ +#include <assert.h> +#include "config.h" +#include "skins.h" + +/* @-comment: // */ +/* +** A black-and-white theme with the project title in a bar across the top +** and no logo image. +*/ +static const char zBuiltinSkin1[] = +@ REPLACE INTO config VALUES('css','/* General settings for the entire page */ +@ body { +@ margin: 0ex 1ex; +@ padding: 0px; +@ background-color: white; +@ font-family: "sans serif"; +@ } +@ +@ /* The project logo in the upper left-hand corner of each page */ +@ div.logo { +@ display: table-row; +@ text-align: center; +@ /* vertical-align: bottom;*/ +@ font-size: 2em; +@ font-weight: bold; +@ background-color: #707070; +@ color: #ffffff; +@ } +@ +@ /* The page title centered at the top of each page */ +@ div.title { +@ display: table-cell; +@ font-size: 1.5em; +@ font-weight: bold; +@ text-align: left; +@ padding: 0 0 0 10px; +@ color: #404040; +@ vertical-align: bottom; +@ width: 100%; +@ } +@ +@ /* The login status message in the top right-hand corner */ +@ div.status { +@ display: table-cell; +@ text-align: right; +@ vertical-align: bottom; +@ color: #404040; +@ font-size: 0.8em; +@ font-weight: bold; +@ } +@ +@ /* The header across the top of the page */ +@ div.header { +@ display: table; +@ width: 100%; +@ } +@ +@ /* The main menu bar that appears at the top of the page beneath +@ ** the header */ +@ div.mainmenu { +@ padding: 5px 10px 5px 10px; +@ font-size: 0.9em; +@ font-weight: bold; +@ text-align: center; +@ letter-spacing: 1px; +@ background-color: #404040; +@ color: white; +@ } +@ +@ /* The submenu bar that *sometimes* appears below the main menu */ +@ div.submenu { +@ padding: 3px 10px 3px 0px; +@ font-size: 0.9em; +@ text-align: center; +@ background-color: #606060; +@ color: white; +@ } +@ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited { +@ padding: 3px 10px 3px 10px; +@ color: white; +@ text-decoration: none; +@ } +@ div.mainmenu a:hover, div.submenu a:hover { +@ color: #404040; +@ background-color: white; +@ } +@ +@ /* All page content from the bottom of the menu or submenu down to +@ ** the footer */ +@ div.content { +@ padding: 0ex 0ex 0ex 0ex; +@ } +@ /* Hyperlink colors */ +@ div.content a { color: #604000; } +@ div.content a:link { color: #604000;} +@ div.content a:visited { color: #600000; } +@ +@ /* Some pages have section dividers */ +@ div.section { +@ margin-bottom: 0px; +@ margin-top: 1em; +@ padding: 1px 1px 1px 1px; +@ font-size: 1.2em; +@ font-weight: bold; +@ background-color: #404040; +@ color: white; +@ } +@ +@ /* The "Date" that occurs on the left hand side of timelines */ +@ div.divider { +@ background: #a0a0a0; +@ border: 2px #505050 solid; +@ font-size: 1em; font-weight: normal; +@ padding: .25em; +@ margin: .2em 0 .2em 0; +@ float: left; +@ clear: left; +@ } +@ +@ /* The footer at the very bottom of the page */ +@ div.footer { +@ font-size: 0.8em; +@ margin-top: 12px; +@ padding: 5px 10px 5px 10px; +@ text-align: right; +@ background-color: #404040; +@ color: white; +@ } +@ +@ /* The label/value pairs on (for example) the vinfo page */ +@ table.label-value th { +@ vertical-align: top; +@ text-align: right; +@ padding: 0.2ex 2ex; +@ }'); +@ REPLACE INTO config VALUES('header','<html> +@ <head> +@ <title>$<project_name>: $<title></title> +@ <link rel="alternate" type="application/rss+xml" title="RSS Feed" +@ href="$baseurl/timeline.rss"> +@ <link rel="stylesheet" href="$baseurl/style.css" type="text/css" +@ media="screen"> +@ </head> +@ <body> +@ <div class="header"> +@ <div class="logo"> +@ <nobr>$<project_name></nobr> +@ </div> +@ </div> +@ <div class="header"> +@ <div class="title">$<title></div> +@ <div class="status"><nobr><th1> +@ if {[info exists login]} { +@ puts "Logged in as $login" +@ } else { +@ puts "Not logged in" +@ } +@ </th1></nobr></div> +@ </div> +@ <div class="mainmenu"><th1> +@ html "<a href=''$baseurl$index_page''>Home</a> " +@ if {[hascap h]} { +@ html "<a href=''$baseurl/dir''>Files</a> " +@ } +@ if {[hascap o]} { +@ html "<a href=''$baseurl/leaves''>Leaves</a> " +@ html "<a href=''$baseurl/timeline''>Timeline</a> " +@ html "<a href=''$baseurl/brlist''>Branches</a> " +@ html "<a href=''$baseurl/taglist''>Tags</a> " +@ } +@ if {[hascap r]} { +@ html "<a href=''$baseurl/reportlist''>Tickets</a> " +@ } +@ if {[hascap j]} { +@ html "<a href=''$baseurl/wiki''>Wiki</a> " +@ } +@ if {[hascap s]} { +@ html "<a href=''$baseurl/setup''>Admin</a> " +@ } elseif {[hascap a]} { +@ html "<a href=''$baseurl/setup_ulist''>Users</a> " +@ } +@ if {[info exists login]} { +@ html "<a href=''$baseurl/login''>Logout</a> " +@ } else { +@ html "<a href=''$baseurl/login''>Login</a> " +@ } +@ </th1></div> +@ '); +@ REPLACE INTO config VALUES('footer','<div class="footer"> +@ Fossil version $manifest_version $manifest_date +@ </div> +@ </body></html> +@ '); +; + +/* +** A tan theme with the project title above the user identification +** and no logo image. +*/ +static const char zBuiltinSkin2[] = +@ REPLACE INTO config VALUES('css','/* General settings for the entire page */ +@ body { +@ margin: 0ex 0ex; +@ padding: 0px; +@ background-color: #fef3bc; +@ font-family: sans-serif; +@ } +@ +@ /* The project logo in the upper left-hand corner of each page */ +@ div.logo { +@ display: inline; +@ text-align: center; +@ vertical-align: bottom; +@ font-weight: bold; +@ font-size: 2.5em; +@ color: #a09048; +@ } +@ +@ /* The page title centered at the top of each page */ +@ div.title { +@ display: table-cell; +@ font-size: 2em; +@ font-weight: bold; +@ text-align: left; +@ padding: 0 0 0 5px; +@ color: #a09048; +@ vertical-align: bottom; +@ width: 100%; +@ } +@ +@ /* The login status message in the top right-hand corner */ +@ div.status { +@ display: table-cell; +@ text-align: right; +@ vertical-align: bottom; +@ color: #a09048; +@ padding: 5px 5px 0 0; +@ font-size: 0.8em; +@ font-weight: bold; +@ } +@ +@ /* The header across the top of the page */ +@ div.header { +@ display: table; +@ width: 100%; +@ } +@ +@ /* The main menu bar that appears at the top of the page beneath +@ ** the header */ +@ div.mainmenu { +@ padding: 5px 10px 5px 10px; +@ font-size: 0.9em; +@ font-weight: bold; +@ text-align: center; +@ letter-spacing: 1px; +@ background-color: #a09048; +@ color: black; +@ } +@ +@ /* The submenu bar that *sometimes* appears below the main menu */ +@ div.submenu { +@ padding: 3px 10px 3px 0px; +@ font-size: 0.9em; +@ text-align: center; +@ background-color: #c0af58; +@ color: white; +@ } +@ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited { +@ padding: 3px 10px 3px 10px; +@ color: white; +@ text-decoration: none; +@ } +@ div.mainmenu a:hover, div.submenu a:hover { +@ color: #a09048; +@ background-color: white; +@ } +@ +@ /* All page content from the bottom of the menu or submenu down to +@ ** the footer */ +@ div.content { +@ padding: 1ex 5px; +@ } +@ div.content a { color: #706532; } +@ div.content a:link { color: #706532; } +@ div.content a:visited { color: #704032; } +@ div.content a:hover { background-color: white; color: #706532; } +@ +@ /* Some pages have section dividers */ +@ div.section { +@ margin-bottom: 0px; +@ margin-top: 1em; +@ padding: 3px 3px 0 3px; +@ font-size: 1.2em; +@ font-weight: bold; +@ background-color: #a09048; +@ color: white; +@ } +@ +@ /* The "Date" that occurs on the left hand side of timelines */ +@ div.divider { +@ background: #e1d498; +@ border: 2px #a09048 solid; +@ font-size: 1em; font-weight: normal; +@ padding: .25em; +@ margin: .2em 0 .2em 0; +@ float: left; +@ clear: left; +@ } +@ +@ /* The footer at the very bottom of the page */ +@ div.footer { +@ font-size: 0.8em; +@ margin-top: 12px; +@ padding: 5px 10px 5px 10px; +@ text-align: right; +@ background-color: #a09048; +@ color: white; +@ } +@ +@ /* Hyperlink colors */ +@ div.footer a { color: white; } +@ div.footer a:link { color: white; } +@ div.footer a:visited { color: white; } +@ div.footer a:hover { background-color: white; color: #558195; } +@ +@ /* <verbatim> blocks */ +@ pre.verbatim { +@ background-color: #f5f5f5; +@ padding: 0.5em; +@ } +@ +@ /* The label/value pairs on (for example) the ci page */ +@ table.label-value th { +@ vertical-align: top; +@ text-align: right; +@ padding: 0.2ex 2ex; +@ } +@ '); +@ REPLACE INTO config VALUES('header','<html> +@ <head> +@ <title>$<project_name>: $<title></title> +@ <link rel="alternate" type="application/rss+xml" title="RSS Feed" +@ href="$baseurl/timeline.rss"> +@ <link rel="stylesheet" href="$baseurl/style.css" type="text/css" +@ media="screen"> +@ </head> +@ <body> +@ <div class="header"> +@ <div class="title">$<title></div> +@ <div class="status"> +@ <div class="logo"><nobr>$<project_name></nobr></div><br/> +@ <nobr><th1> +@ if {[info exists login]} { +@ puts "Logged in as $login" +@ } else { +@ puts "Not logged in" +@ } +@ </th1></nobr></div> +@ </div> +@ <div class="mainmenu"><th1> +@ html "<a href=''$baseurl$index_page''>Home</a> " +@ if {[hascap h]} { +@ html "<a href=''$baseurl/dir''>Files</a> " +@ } +@ if {[hascap o]} { +@ html "<a href=''$baseurl/leaves''>Leaves</a> " +@ html "<a href=''$baseurl/timeline''>Timeline</a> " +@ html "<a href=''$baseurl/brlist''>Branches</a> " +@ html "<a href=''$baseurl/taglist''>Tags</a> " +@ } +@ if {[hascap r]} { +@ html "<a href=''$baseurl/reportlist''>Tickets</a> " +@ } +@ if {[hascap j]} { +@ html "<a href=''$baseurl/wiki''>Wiki</a> " +@ } +@ if {[hascap s]} { +@ html "<a href=''$baseurl/setup''>Admin</a> " +@ } elseif {[hascap a]} { +@ html "<a href=''$baseurl/setup_ulist''>Users</a> " +@ } +@ if {[info exists login]} { +@ html "<a href=''$baseurl/login''>Logout</a> " +@ } else { +@ html "<a href=''$baseurl/login''>Login</a> " +@ } +@ </th1></div> +@ '); +@ REPLACE INTO config VALUES('footer','<div class="footer"> +@ Fossil version $manifest_version $manifest_date +@ </div> +@ </body></html> +@ '); +; + +/* +** Black letters on a white or cream background with the main menu +** stuck on the left-hand side. +*/ +static const char zBuiltinSkin3[] = +@ REPLACE INTO config VALUES('css','/* General settings for the entire page */ +@ body { +@ margin:0px 0px 0px 0px; +@ padding:0px; +@ font-family:verdana, arial, helvetica, "sans serif"; +@ color:#333; +@ background-color:white; +@ } +@ +@ /* consistent colours */ +@ h2 { +@ color: #333; +@ } +@ h3 { +@ color: #333; +@ } +@ +@ /* The project logo in the upper left-hand corner of each page */ +@ div.logo { +@ display: table-cell; +@ text-align: left; +@ vertical-align: bottom; +@ font-weight: bold; +@ color: #333; +@ } +@ +@ /* The page title centered at the top of each page */ +@ div.title { +@ display: table-cell; +@ font-size: 2em; +@ font-weight: bold; +@ text-align: center; +@ color: #333; +@ vertical-align: bottom; +@ width: 100%; +@ } +@ +@ /* The login status message in the top right-hand corner */ +@ div.status { +@ display: table-cell; +@ padding-right: 10px; +@ text-align: right; +@ vertical-align: bottom; +@ padding-bottom: 5px; +@ color: #333; +@ font-size: 0.8em; +@ font-weight: bold; +@ } +@ +@ /* The header across the top of the page */ +@ div.header { +@ margin:10px 0px 10px 0px; +@ padding:1px 0px 0px 20px; +@ border-style:solid; +@ border-color:black; +@ border-width:1px 0px; +@ background-color:#eee; +@ } +@ +@ /* The main menu bar that appears at the top left of the page beneath +@ ** the header. Width must be co-ordinated with the container below */ +@ div.mainmenu { +@ float: left; +@ margin-left: 10px; +@ margin-right: 10px; +@ font-size: 0.9em; +@ font-weight: bold; +@ padding:5px; +@ background-color:#eee; +@ border:1px solid #999; +@ width:8em; +@ } +@ +@ /* Main menu is now a list */ +@ div.mainmenu ul { +@ padding: 0; +@ list-style:none; +@ } +@ div.mainmenu a, div.mainmenu a:visited{ +@ padding: 1px 10px 1px 10px; +@ color: #333; +@ text-decoration: none; +@ } +@ div.mainmenu a:hover { +@ color: #eee; +@ background-color: #333; +@ } +@ +@ /* Container for the sub-menu and content so they don''t spread +@ ** out underneath the main menu */ +@ #container { +@ padding-left: 9em; +@ } +@ +@ /* The submenu bar that *sometimes* appears below the main menu */ +@ div.submenu { +@ padding: 3px 10px 3px 10px; +@ font-size: 0.9em; +@ text-align: center; +@ border:1px solid #999; +@ border-width:1px 0px; +@ background-color: #eee; +@ color: #333; +@ } +@ div.submenu a, div.submenu a:visited { +@ padding: 3px 10px 3px 10px; +@ color: #333; +@ text-decoration: none; +@ } +@ div.submenu a:hover { +@ color: #eee; +@ background-color: #333; +@ } +@ +@ /* All page content from the bottom of the menu or submenu down to +@ ** the footer */ +@ div.content { +@ float right; +@ padding: 2ex 1ex 0ex 2ex; +@ } +@ +@ /* Some pages have section dividers */ +@ div.section { +@ margin-bottom: 0px; +@ margin-top: 1em; +@ padding: 1px 1px 1px 1px; +@ font-size: 1.2em; +@ font-weight: bold; +@ border-style:solid; +@ border-color:#999; +@ border-width:1px 0px; +@ background-color: #eee; +@ color: #333; +@ } +@ +@ /* The "Date" that occurs on the left hand side of timelines */ +@ div.divider { +@ background: #eee; +@ border: 2px #999 solid; +@ font-size: 1em; font-weight: normal; +@ padding: .25em; +@ margin: .2em 0 .2em 0; +@ float: left; +@ clear: left; +@ color: #333 +@ } +@ +@ /* The footer at the very bottom of the page */ +@ div.footer { +@ font-size: 0.8em; +@ margin-top: 12px; +@ padding: 5px 10px 5px 10px; +@ text-align: right; +@ background-color: #eee; +@ color: #555; +@ } +@ +@ /* <verbatim> blocks */ +@ pre.verbatim { +@ background-color: #f5f5f5; +@ padding: 0.5em; +@ } +@ +@ /* The label/value pairs on (for example) the ci page */ +@ table.label-value th { +@ vertical-align: top; +@ text-align: right; +@ padding: 0.2ex 2ex; +@ }'); +@ REPLACE INTO config VALUES('header','<html> +@ <head> +@ <title>$<project_name>: $<title></title> +@ <link rel="alternate" type="application/rss+xml" title="RSS Feed" +@ href="$baseurl/timeline.rss"> +@ <link rel="stylesheet" href="$baseurl/style.css" type="text/css" +@ media="screen"> +@ </head> +@ <body> +@ <div class="header"> +@ <div class="logo"> +@ <!-- <img src="$baseurl/logo" alt="logo"> --> +@ <br><nobr>$<project_name></nobr> +@ </div> +@ <div class="title">$<title></div> +@ <div class="status"><nobr><th1> +@ if {[info exists login]} { +@ puts "Logged in as $login" +@ } else { +@ puts "Not logged in" +@ } +@ </th1></nobr></div> +@ </div> +@ <div class="mainmenu"><ul><th1> +@ html "<li><a href=''$baseurl$index_page''>Home</a></li>" +@ if {[hascap h]} { +@ html "<li><a href=''$baseurl/dir''>Files</a></li>" +@ } +@ if {[hascap o]} { +@ html "<li><a href=''$baseurl/leaves''>Leaves</a></li>" +@ html "<li><a href=''$baseurl/timeline''>Timeline</a></li>" +@ html "<li><a href=''$baseurl/brlist''>Branches</a></li>" +@ html "<li><a href=''$baseurl/taglist''>Tags</a></li>" +@ } +@ if {[hascap r]} { +@ html "<li><a href=''$baseurl/reportlist''>Tickets</a></li>" +@ } +@ if {[hascap j]} { +@ html "<li><a href=''$baseurl/wiki''>Wiki</a></li>" +@ } +@ if {[hascap s]} { +@ html "<li><a href=''$baseurl/setup''>Admin</a></li>" +@ } elseif {[hascap a]} { +@ html "<li><a href=''$baseurl/setup_ulist''>Users</a></li>" +@ } +@ if {[info exists login]} { +@ html "<li><a href=''$baseurl/login''>Logout</a></li>" +@ } else { +@ html "<li><a href=''$baseurl/login''>Login</a></li>" +@ } +@ </th1></ul></div> +@ <div id="container"> +@ '); +@ REPLACE INTO config VALUES('footer','</div> +@ <div class="footer"> +@ Fossil version $manifest_version $manifest_date +@ </div> +@ </body></html> +@ '); +; +/* +** An array of available built-in skins. +*/ +static struct BuiltinSkin { + const char *zName; + const char *zValue; +} aBuiltinSkin[] = { + { "Default", 0 /* Filled in at runtime */ }, + { "Plain Gray, No Logo", zBuiltinSkin1 }, + { "Khaki, No Logo", zBuiltinSkin2 }, + { "Black & White, Menu on Left", zBuiltinSkin3 }, +}; + +/* +** For a skin named zSkinName, compute the name of the CONFIG table +** entry where that skin is stored and return it. +** +** Return NULL if zSkinName is NULL or an empty string. +** +** If ifExists is true, and the named skin does not exist, return NULL. +*/ +static char *skinVarName(const char *zSkinName, int ifExists){ + char *z; + if( zSkinName==0 || zSkinName[0]==0 ) return 0; + z = mprintf("skin:%s", zSkinName); + if( ifExists && !db_exists("SELECT 1 FROM config WHERE name=%Q", z) ){ + free(z); + z = 0; + } + return z; +} + +/* +** Construct and return a string that represents the current skin if +** useDefault==0 or a string for the default skin if useDefault==1. +** +** Memory to hold the returned string is obtained from malloc. +*/ +static char *getSkin(int useDefault){ + Blob val; + blob_zero(&val); + blob_appendf(&val, "REPLACE INTO config VALUES('css',%Q);\n", + useDefault ? zDefaultCSS : db_get("css", (char*)zDefaultCSS) + ); + blob_appendf(&val, "REPLACE INTO config VALUES('header',%Q);\n", + useDefault ? zDefaultHeader : db_get("header", (char*)zDefaultHeader) + ); + blob_appendf(&val, "REPLACE INTO config VALUES('footer',%Q);\n", + useDefault ? zDefaultFooter : db_get("footer", (char*)zDefaultFooter) + ); + return blob_str(&val); +} + +/* +** Construct the default skin string and fill in the corresponding +** entry in aBuildinSkin[] +*/ +static void setDefaultSkin(void){ + aBuiltinSkin[0].zValue = getSkin(1); +} + +/* +** WEBPAGE: setup_skin +*/ +void setup_skin(void){ + const char *z; + char *zName; + char *zErr = 0; + const char *zCurrent; /* Current skin */ + int i; /* Loop counter */ + Stmt q; + + login_check_credentials(); + if( !g.okSetup ){ + login_needed(); + } + db_begin_transaction(); + + /* Process requests to delete a user-defined skin */ + if( P("del1") && (zName = skinVarName(P("sn"), 1))!=0 ){ + style_header("Confirm Custom Skin Delete"); + @ <form action="%s(g.zBaseURL)/setup_skin" method="POST"> + @ <p>Deletion of a custom skin is a permanent action that cannot + @ be undone. Please confirm that this is what you want to do:</p> + @ <input type="hidden" name="sn" value="%h(P("sn"))"> + @ <input type="submit" name="del2" value="Confirm - Delete The Skin"> + @ <input type="submit" name="cancel" value="Cancel - Do Not Delete"> + login_insert_csrf_secret(); + @ </form> + style_footer(); + return; + } + if( P("del2")!=0 && (zName = skinVarName(P("sn"), 1))!=0 ){ + db_multi_exec("DELETE FROM config WHERE name=%Q", zName); + } + + setDefaultSkin(); + zCurrent = getSkin(0); + + if( P("save")!=0 && (zName = skinVarName(P("save"),0))!=0 ){ + if( db_exists("SELECT 1 FROM config WHERE name=%Q", zName) + || strcmp(zName, "Default")==0 ){ + zErr = mprintf("Skin name \"%h\" already exists. " + "Choose a different name.", P("sn")); + }else{ + db_multi_exec("INSERT INTO config VALUES(%Q,%Q)", + zName, zCurrent + ); + } + } + + /* The user pressed the "Use This Skin" button. */ + if( P("load") && (z = P("sn"))!=0 && z[0] ){ + int seen = 0; + for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){ + if( strcmp(aBuiltinSkin[i].zValue, zCurrent)==0 ){ + seen = 1; + break; + } + } + if( !seen ){ + seen = db_exists("SELECT 1 FROM config WHERE name GLOB 'skin:*'" + " AND value=%Q", zCurrent); + } + if( !seen ){ + db_multi_exec( + "INSERT INTO config VALUES(" + " strftime('skin:Backup On %%Y-%%m-%%d %%H:%%M:%%S')," + " %Q)", zCurrent + ); + } + seen = 0; + for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){ + if( strcmp(aBuiltinSkin[i].zName, z)==0 ){ + seen = 1; + zCurrent = aBuiltinSkin[i].zValue; + db_multi_exec("%s", zCurrent); + break; + } + } + if( !seen ){ + zName = skinVarName(z,0); + zCurrent = db_get(zName, 0); + db_multi_exec("%s", zCurrent); + } + } + style_header("Skins"); + @ <p>A "skin" is a combination of + @ <a href="setup_editcss">CSS</a>, + @ <a href="setup_header">Header</a>, and + @ <a href="setup_footer">Footer</a> that determines the look and feel + @ of the web interface.</p> + @ + @ <h2>Available Skins:</h2> + @ <ol> + for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){ + z = aBuiltinSkin[i].zName; + if( strcmp(aBuiltinSkin[i].zValue, zCurrent)==0 ){ + @ <li><p>%h(z). <b>Currently In Use</b></p> + }else{ + @ <li><form action="%s(g.zBaseURL)/setup_skin" method="POST"> + @ %h(z). + @ <input type="hidden" name="sn" value="%h(z)"> + @ <input type="submit" name="load" value="Use This Skin"> + @ </form></li> + } + } + db_prepare(&q, + "SELECT substr(name, 6), value FROM config" + " WHERE name GLOB 'skin:*'" + " ORDER BY name" + ); + while( db_step(&q)==SQLITE_ROW ){ + const char *zN = db_column_text(&q, 0); + const char *zV = db_column_text(&q, 1); + if( strcmp(zV, zCurrent)==0 ){ + @ <li><p>%h(zN). <b>Currently In Use</b></p> + }else{ + @ <li><form action="%s(g.zBaseURL)/setup_skin" method="POST"> + @ %h(zN). + @ <input type="hidden" name="sn" value="%h(zN)"> + @ <input type="submit" name="load" value="Use This Skin"> + @ <input type="submit" name="del1" value="Delete This Skin"> + @ </form></li> + } + } + db_finalize(&q); + @ </ol> + style_footer(); + db_end_transaction(0); +}
Modified src/style.c from [c7fc56d774] to [3cc4be49bc].
@@ -360,11 +360,11 @@ @ text-align: right; @ background-color: #558195; @ color: white; @ } @ -@ /* Make the links in the footer less ugly... */ +@ /* Hyperlink colors in the footer */ @ div.footer a { color: white; } @ div.footer a:link { color: white; } @ div.footer a:visited { color: white; } @ div.footer a:hover { background-color: white; color: #558195; } @ @@ -377,25 +377,10 @@ @ /* The label/value pairs on (for example) the ci page */ @ table.label-value th { @ vertical-align: top; @ text-align: right; @ padding: 0.2ex 2ex; -@ } -@ -@ /* For marking important UI elements which shouldn't be -@ lightly dismissed. I mainly use it to mark "not yet -@ implemented" parts of a page. Whether or not to have -@ a 'border' attribute set is arguable. */ -@ .achtung { -@ color: #ff0000; -@ background: #ffff00; -@ border: 1px solid #ff0000; -@ } -@ -@ div.miniform { -@ font-size: smaller; -@ margin: 8px; @ } ; /* ** WEBPAGE: style.css
Modified src/timeline.c from [f76d6fc0d6] to [ece922611b].
@@ -28,17 +28,37 @@ #include <time.h> #include "config.h" #include "timeline.h" /* +** Shorten a UUID so that is the minimum length needed to contain +** at least one digit in the range 'a'..'f'. The minimum length is 10. +*/ +static void shorten_uuid(char *zDest, const char *zSrc){ + int i; + for(i=0; i<10 && zSrc[i]<='9'; i++){} + memcpy(zDest, zSrc, 10); + if( i==10 ){ + do{ + zDest[i] = zSrc[i]; + i++; + }while( zSrc[i-1]<='9' ); + }else{ + i = 10; + } + zDest[i] = 0; +} + + +/* ** Generate a hyperlink to a version. */ void hyperlink_to_uuid(const char *zUuid){ char zShortUuid[UUID_SIZE+1]; - sprintf(zShortUuid, "%.10s", zUuid); + shorten_uuid(zShortUuid, zUuid); if( g.okHistory ){ - @ <a href="%s(g.zBaseURL)/info/%s(zUuid)">[%s(zShortUuid)]</a> + @ <a href="%s(g.zBaseURL)/info/%s(zShortUuid)">[%s(zShortUuid)]</a> }else{ @ <b>[%s(zShortUuid)]</b> } } @@ -51,14 +71,14 @@ const char *zIn, /* Javascript proc for mouseover */ const char *zOut, /* Javascript proc for mouseout */ int id /* Argument to javascript procs */ ){ char zShortUuid[UUID_SIZE+1]; - sprintf(zShortUuid, "%.10s", zUuid); + shorten_uuid(zShortUuid, zUuid); if( g.okHistory ){ @ <a onmouseover='%s(zIn)("m%d(id)")' onmouseout='%s(zOut)("m%d(id)")' - @ href="%s(g.zBaseURL)/vinfo/%s(zUuid)">[%s(zShortUuid)]</a> + @ href="%s(g.zBaseURL)/vinfo/%s(zShortUuid)">[%s(zShortUuid)]</a> }else{ @ <b onmouseover='%s(zIn)("m%d(id)")' onmouseout='%s(zOut)("m%d(id)")'> @ [%s(zShortUuid)]</b> } } @@ -397,10 +417,11 @@ ** p=RID artifact RID and up to COUNT parents and ancestors ** d=RID artifact RID and up to COUNT descendants ** t=TAGID show only check-ins with the given tagid ** u=USER only if belonging to this user ** y=TYPE 'ci', 'w', 't' +** s=TEXT string search (comment and brief) ** ** p= and d= can appear individually or together. If either p= or d= ** appear, then u=, y=, a=, and b= are ignored. ** ** If a= and b= appear, only a= is used. If neither appear, the most @@ -419,10 +440,11 @@ const char *zType = PD("y","all"); /* Type of events. All if NULL */ const char *zAfter = P("a"); /* Events after this time */ const char *zBefore = P("b"); /* Events before this time */ const char *zCirca = P("c"); /* Events near this time */ const char *zTagName = P("t"); /* Show events with this tag */ + const char *zString = P("s"); /* String text search of comment and brief */ HQuery url; /* URL for various branch links */ int tagid; /* Tag ID */ int tmFlags; /* Timeline flags */ /* To view the timeline, must have permission to read project data. @@ -464,13 +486,13 @@ blob_appendf(&sql, " AND event.objid IN ok"); nd = 0; if( d_rid ){ compute_descendants(d_rid, nEntry+1); nd = db_int(0, "SELECT count(*)-1 FROM ok"); - if( nd>0 ){ + if( nd>=0 ){ db_multi_exec("%s", blob_str(&sql)); - blob_appendf(&desc, "%d descendants", nd); + blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s"); } timeline_add_dividers( db_text("1","SELECT datetime(mtime,'localtime') FROM event" " WHERE objid=%d", d_rid) ); @@ -523,10 +545,15 @@ } } if( zUser ){ blob_appendf(&sql, " AND event.user=%Q", zUser); url_add_parameter(&url, "u", zUser); + } + if ( zString ){ + blob_appendf(&sql, " AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')", + zString, zString); + url_add_parameter(&url, "s", zString); } if( zAfter ){ while( isspace(zAfter[0]) ){ zAfter++; } if( zAfter[0] ){ blob_appendf(&sql, @@ -827,31 +854,10 @@ ; return zBaseSql; } /* -** Equivalent to timeline_query_for_tty(), except that: -** -** a) accepts a the -type=XX flag to set the event type to filter on. -** The values of XX are the same as supported by the /timeline page. -** -** b) The returned string must be freed using free(). -*/ -char * timeline_query_for_tty_m(void){ - Blob bl; - char const * zType = 0; - blob_zero(&bl); - blob_append( &bl, timeline_query_for_tty(), -1 ); - zType = find_option( "type", "t", 1 ); - if( zType && *zType ) - { - blob_appendf( &bl, " AND event.type=%Q", zType ); - } - return blob_buffer(&bl); -} - -/* ** Return true if the input string is a date in the ISO 8601 format: ** YYYY-MM-DD. */ static int isIsoDate(const char *z){ return strlen(z)==10 @@ -894,11 +900,11 @@ int n, k; const char *zCount; const char *zType; char *zOrigin; char *zDate; - char *zSQL; + Blob sql; int objid = 0; Blob uuid; int mode = 0 ; /* 0:none 1: before 2:after 3:children 4:parents */ db_find_and_open_repository(1); zCount = find_option("count","n",1); @@ -961,31 +967,33 @@ if( isIsoDate(zOrigin) ) zShift = ",'+1 day'"; } zDate = mprintf("(SELECT julianday(%Q%s, 'utc'))", zOrigin, zShift); } if( mode==0 ) mode = 1; - zSQL = mprintf("%z AND event.mtime %s %s", - timeline_query_for_tty_m(), + blob_zero(&sql); + blob_append(&sql, timeline_query_for_tty(), -1); + blob_appendf(&sql, " AND event.mtime %s %s", (mode==1 || mode==4) ? "<=" : ">=", zDate ); + if( mode==3 || mode==4 ){ db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)"); if( mode==3 ){ compute_descendants(objid, n); }else{ compute_ancestors(objid, n); } - zSQL = mprintf("%z AND blob.rid IN ok", zSQL); + blob_appendf(&sql, " AND blob.rid IN ok"); } if( zType && (zType[0]!='a') ){ - zSQL = mprintf( "%z AND event.type=%Q ", zSQL, zType); + blob_appendf(&sql, " AND event.type=%Q ", zType); } - zSQL = mprintf("%z ORDER BY event.mtime DESC", zSQL); - db_prepare(&q, zSQL); - free( zSQL ); + blob_appendf(&sql, " ORDER BY event.mtime DESC"); + db_prepare(&q, blob_str(&sql)); + blob_reset(&sql); print_timeline(&q, n); db_finalize(&q); } /*
Modified src/undo.c from [e141d7b88f] to [0d2cc71745].
@@ -189,13 +189,13 @@ /* ** COMMAND: undo ** ** Usage: %fossil undo ?FILENAME...? ** -** Undo the most recent update or merge operation. If FILENAME is +** Undo the most recent update or merge or revert operation. If FILENAME is ** specified then restore the content of the named file(s) but otherwise -** leave the update or merge in effect. +** leave the update or merge or revert in effect. ** ** A single level of undo/redo is supported. The undo/redo stack ** is cleared by the commit and checkout commands. */ void undo_cmd(void){ @@ -228,12 +228,12 @@ /* ** COMMAND: redo ** ** Usage: %fossil redo ?FILENAME...? ** -** Redo the an update or merge operation that has been undone by the -** undo command. If FILENAME is specified then restore the changes +** Redo the an update or merge or revert operation that has been undone +** by the undo command. If FILENAME is specified then restore the changes ** associated with the named file(s) but otherwise leave the update ** or merge undone. ** ** A single level of undo/redo is supported. The undo/redo stack ** is cleared by the commit and checkout commands.
Modified src/update.c from [74a2862e7d] to [eb5c093f17].
@@ -36,53 +36,72 @@ } /* ** COMMAND: update ** -** Usage: %fossil update ?VERSION? ?--latest? +** Usage: %fossil update ?VERSION? ?FILES...? +** +** Change the version of the current checkout to VERSION. Any uncommitted +** changes are retained and applied to the new checkout. +** +** The VERSION argument can be a specific version or tag or branch name. +** If the VERSION argument is omitted, then the leaf of the the subtree +** that begins at the current version is used, if there is only a single +** leaf. VERSION can also be "current" to select the leaf of the current +** version or "latest" to select the most recent check-in. +** +** If one or more FILES are listed after the VERSION then only the +** named files are candidates to be updated. If FILES is omitted, all +** files in the current checkout are subject to be updated. ** -** The optional argument is a version that should become the current -** version. If the argument is omitted, then use the leaf of the -** tree that begins with the current version, if there is only a -** single leaf. If there are a multiple leaves, the latest is used -** if the --latest flag is present. +** The -n or --nochange option causes this command to do a "dry run". It +** prints out what would have happened but does not actually make any +** changes to the current checkout or the repository. ** -** This command is different from the "checkout" in that edits are -** not overwritten. Edits are merged into the new version. +** The -v or --verbose option prints status information about unchanged +** files in addition to those file that actually do change. */ void update_cmd(void){ int vid; /* Current version */ int tid=0; /* Target version - version we are changing to */ Stmt q; - int latestFlag; /* Pick the latest version if true */ - int forceFlag; /* True force the update */ + int latestFlag; /* --latest. Pick the latest version if true */ + int nochangeFlag; /* -n or --nochange. Do a dry run */ + int verboseFlag; /* -v or --verbose. Output extra information */ url_proxy_options(); latestFlag = find_option("latest",0, 0)!=0; - forceFlag = find_option("force","f",0)!=0; - if( g.argc!=3 && g.argc!=2 ){ - usage("?VERSION?"); - } + nochangeFlag = find_option("nochange","n",0)!=0; + verboseFlag = find_option("verbose","v",0)!=0; db_must_be_within_tree(); vid = db_lget_int("checkout", 0); if( vid==0 ){ fossil_fatal("cannot find current version"); } if( db_exists("SELECT 1 FROM vmerge") ){ fossil_fatal("cannot update an uncommitted merge"); } - if( g.argc==3 ){ - tid = name_to_rid(g.argv[2]); - if( tid==0 ){ - fossil_fatal("not a version: %s", g.argv[2]); - } - if( !is_a_version(tid) ){ - fossil_fatal("not a version: %s", g.argv[2]); + if( g.argc>=3 ){ + if( strcmp(g.argv[2], "current")==0 ){ + /* If VERSION is "current", then use the same algorithm to find the + ** target as if VERSION were omitted. */ + }else if( strcmp(g.argv[2], "latest")==0 ){ + /* If VERSION is "latest", then use the same algorithm to find the + ** target as if VERSION were omitted and the --latest flag is present. + */ + latestFlag = 1; + }else{ + tid = name_to_rid(g.argv[2]); + if( tid==0 ){ + fossil_fatal("no such version: %s", g.argv[2]); + }else if( !is_a_version(tid) ){ + fossil_fatal("no such version: %s", g.argv[2]); + } } } - autosync(AUTOSYNC_PULL); + if( !nochangeFlag ) autosync(AUTOSYNC_PULL); if( tid==0 ){ compute_leaves(vid, 1); if( !latestFlag && db_int(0, "SELECT count(*) FROM leaves")>1 ){ db_prepare(&q, @@ -99,11 +118,11 @@ " WHERE event.objid=leaves.rid" " ORDER BY event.mtime DESC"); } db_begin_transaction(); - vfile_check_signature(vid); + vfile_check_signature(vid, 1); undo_begin(); load_vfile_from_rid(tid); /* ** The record.fn field is used to match files against each other. The @@ -111,11 +130,11 @@ ** in the current checkout, the pivot, and the version being merged. */ db_multi_exec( "DROP TABLE IF EXISTS fv;" "CREATE TEMP TABLE fv(" - " fn TEXT PRIMARY KEY," /* The filename */ + " fn TEXT PRIMARY KEY," /* The filename relative to root */ " idv INTEGER," /* VFILE entry for current version */ " idt INTEGER," /* VFILE entry for target version */ " chnged BOOLEAN," /* True if current version has been edited */ " ridv INTEGER," /* Record ID for current version */ " ridt INTEGER " /* Record ID for target */ @@ -151,99 +170,141 @@ id, rid, chnged, fn ); } db_finalize(&q); + /* If FILES appear on the command-line, remove from the "fv" table + ** every entry that is not named on the command-line. + */ + if( g.argc>=4 ){ + Blob sql; /* SQL statement to purge unwanted entries */ + char *zSep = "("; /* Separator in the list of filenames */ + Blob treename; /* Normalized filename */ + int i; /* Loop counter */ + + blob_zero(&sql); + blob_append(&sql, "DELETE FROM fv WHERE fn NOT IN ", -1); + for(i=3; i<g.argc; i++){ + file_tree_name(g.argv[i], &treename, 1); + blob_appendf(&sql, "%s'%q'", zSep, blob_str(&treename)); + blob_reset(&treename); + zSep = ","; + } + blob_append(&sql, ")", -1); + db_multi_exec(blob_str(&sql)); + blob_reset(&sql); + } + db_prepare(&q, "SELECT fn, idv, ridv, idt, ridt, chnged FROM fv ORDER BY 1" ); while( db_step(&q)==SQLITE_ROW ){ - const char *zName = db_column_text(&q, 0); - int idv = db_column_int(&q, 1); - int ridv = db_column_int(&q, 2); - int idt = db_column_int(&q, 3); - int ridt = db_column_int(&q, 4); - int chnged = db_column_int(&q, 5); - + const char *zName = db_column_text(&q, 0); /* The filename from root */ + int idv = db_column_int(&q, 1); /* VFILE entry for current */ + int ridv = db_column_int(&q, 2); /* RecordID for current */ + int idt = db_column_int(&q, 3); /* VFILE entry for target */ + int ridt = db_column_int(&q, 4); /* RecordID for target */ + int chnged = db_column_int(&q, 5); /* Current is edited */ + char *zFullPath; /* Full pathname of the file */ + + zFullPath = mprintf("%s/%s", g.zLocalRoot, zName); if( idv>0 && ridv==0 && idt>0 ){ /* Conflict. This file has been added to the current checkout ** but also exists in the target checkout. Use the current version. */ printf("CONFLICT %s\n", zName); }else if( idt>0 && idv==0 ){ /* File added in the target. */ printf("ADD %s\n", zName); undo_save(zName); - vfile_to_disk(0, idt, 0); + if( !nochangeFlag ) vfile_to_disk(0, idt, 0); }else if( idt>0 && idv>0 && ridt!=ridv && chnged==0 ){ /* The file is unedited. Change it to the target version */ printf("UPDATE %s\n", zName); undo_save(zName); - vfile_to_disk(0, idt, 0); + if( !nochangeFlag ) vfile_to_disk(0, idt, 0); + }else if( idt>0 && idv>0 && file_size(zFullPath)<0 ){ + /* The file missing from the local check-out. Restore it to the + ** version that appears in the target. */ + printf("UPDATE %s\n", zName); + undo_save(zName); + if( !nochangeFlag ) vfile_to_disk(0, idt, 0); }else if( idt==0 && idv>0 ){ if( ridv==0 ){ /* Added in current checkout. Continue to hold the file as ** as an addition */ db_multi_exec("UPDATE vfile SET vid=%d WHERE id=%d", tid, idv); }else if( chnged ){ + /* Edited locally but deleted from the target. Delete it. */ printf("CONFLICT %s\n", zName); }else{ char *zFullPath; printf("REMOVE %s\n", zName); undo_save(zName); zFullPath = mprintf("%s/%s", g.zLocalRoot, zName); - unlink(zFullPath); + if( !nochangeFlag ) unlink(zFullPath); free(zFullPath); } }else if( idt>0 && idv>0 && ridt!=ridv && chnged ){ /* Merge the changes in the current tree into the target version */ Blob e, r, t, v; int rc; - char *zFullPath; printf("MERGE %s\n", zName); undo_save(zName); - zFullPath = mprintf("%s/%s", g.zLocalRoot, zName); content_get(ridt, &t); content_get(ridv, &v); blob_zero(&e); blob_read_from_file(&e, zFullPath); rc = blob_merge(&v, &e, &t, &r); if( rc>=0 ){ - blob_write_to_file(&r, zFullPath); + if( !nochangeFlag ) blob_write_to_file(&r, zFullPath); if( rc>0 ){ printf("***** %d merge conflicts in %s\n", rc, zName); } }else{ printf("***** Cannot merge binary file %s\n", zName); } - free(zFullPath); blob_reset(&v); blob_reset(&e); blob_reset(&t); blob_reset(&r); - + }else if( verboseFlag ){ + printf("UNCHANGED %s\n", zName); } + free(zFullPath); } db_finalize(&q); /* ** Clean up the mid and pid VFILE entries. Then commit the changes. */ - db_multi_exec("DELETE FROM vfile WHERE vid!=%d", tid); - manifest_to_disk(tid); - db_lset_int("checkout", tid); - db_end_transaction(0); + if( nochangeFlag ){ + db_end_transaction(1); /* With --nochange, rollback changes */ + }else{ + if( g.argc<=3 ){ + /* All files updated. Shift the current checkout to the target. */ + db_multi_exec("DELETE FROM vfile WHERE vid!=%d", tid); + manifest_to_disk(tid); + db_lset_int("checkout", tid); + }else{ + /* A subset of files have been checked out. Keep the current + ** checkout unchanged. */ + db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid); + } + db_end_transaction(0); + } } /* ** Get the contents of a file within a given revision. */ int historical_version_of_file( const char *revision, /* The baseline name containing the file */ const char *file, /* Full treename of the file */ - Blob *content /* Put the content here */ + Blob *content, /* Put the content here */ + int errCode /* Error code if file not found. Panic if 0. */ ){ Blob mfile; Manifest m; int i, rid=0; @@ -251,10 +312,11 @@ rid = name_to_rid(revision); }else{ rid = db_lget_int("checkout", 0); } if( !is_a_version(rid) ){ + if( errCode>0 ) return errCode; fossil_fatal("no such check-out: %s", revision); } content_get(rid, &mfile); if( manifest_parse(&m, &mfile) ){ @@ -262,76 +324,77 @@ if( strcmp(m.aFile[i].zName, file)==0 ){ rid = uuid_to_rid(m.aFile[i].zUuid, 0); return content_get(rid, content); } } - fossil_fatal("file %s does not exist in baseline: %s", file, revision); - }else{ + if( errCode<=0 ){ + fossil_fatal("file %s does not exist in baseline: %s", file, revision); + } + }else if( errCode<=0 ){ fossil_panic("could not parse manifest for baseline: %s", revision); } - return 0; + return errCode; } /* ** COMMAND: revert ** -** Usage: %fossil revert ?--yes? ?-r REVISION? FILE +** Usage: %fossil revert ?-r REVISION? FILE ... ** ** Revert to the current repository version of FILE, or to ** the version associated with baseline REVISION if the -r flag -** appears. This command will confirm your operation unless the -** file is missing or the --yes option is used. -**/ +** appears. +** +** If a file is reverted accidently, it can be restored using +** the "fossil undo" command. +*/ void revert_cmd(void){ - const char *zFile; + char *zFile; const char *zRevision; Blob fname; Blob record; - Blob ans; - int rid = 0, yesRevert; - - yesRevert = find_option("yes", "y", 0)!=0; + int i; + int errCode; + int rid = 0; + zRevision = find_option("revision", "r", 1); verify_all_options(); - if( g.argc!=3 ){ - usage("?OPTIONS FILE"); + if( g.argc<3 ){ + usage("?OPTIONS? FILE ..."); } db_must_be_within_tree(); - - zFile = mprintf("%/", g.argv[g.argc-1]); + db_begin_transaction(); + undo_begin(); - file_tree_name(zFile, &fname, 1); + blob_zero(&record); + for(i=2; i<g.argc; i++){ + zFile = mprintf("%/", g.argv[i]); + file_tree_name(zFile, &fname, 1); - if( access(zFile, 0) ) yesRevert = 1; - if( yesRevert==0 ){ - char *prompt = mprintf("revert file %B? this will" - " destroy local changes (y/N)? ", - &fname); - blob_zero(&ans); - prompt_user(prompt, &ans); - free( prompt ); - if( blob_str(&ans)[0]=='y' ){ - yesRevert = 1; + if( zRevision!=0 ){ + errCode = historical_version_of_file(zRevision, blob_str(&fname), + &record, 2); + }else{ + rid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%B", &fname); + if( rid==0 ){ + errCode = 2; + }else{ + content_get(rid, &record); + errCode = 0; + } } - } - if( yesRevert==1 && zRevision!=0 ){ - historical_version_of_file(zRevision, zFile, &record); - }else if( yesRevert==1 ){ - rid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%B", &fname); - if( rid==0 ){ - fossil_panic("no history for file: %b", &fname); - } - content_get(rid, &record); - } - - if( yesRevert==1 ){ - blob_write_to_file(&record, zFile); - printf("%s reverted\n", zFile); + if( errCode==2 ){ + fossil_warning("file not in repository: %s", zFile); + }else{ + undo_save(blob_str(&fname)); + blob_write_to_file(&record, zFile); + printf("%s reverted\n", zFile); + } blob_reset(&record); blob_reset(&fname); - }else{ - printf("revert canceled\n"); + free(zFile); } + db_end_transaction(0); }
Modified src/vfile.c from [5504b327f7] to [882972137d].
@@ -142,11 +142,12 @@ ** that has changed. ** ** If VFILE.DELETED is null or if VFILE.RID is zero, then we can assume ** the file has changed without having the check the on-disk image. */ -void vfile_check_signature(int vid){ +void vfile_check_signature(int vid, int notFileIsFatal){ + int nErr = 0; Stmt q; Blob fileCksum, origCksum; int checkMtime = db_get_boolean("mtime-changes", 0); db_begin_transaction(); @@ -166,11 +167,17 @@ zName = db_column_text(&q, 1); rid = db_column_int(&q, 2); isDeleted = db_column_int(&q, 3); oldChnged = db_column_int(&q, 4); oldMtime = db_column_int64(&q, 6); - if( oldChnged>=2 ){ + if( !file_isfile(zName) && file_size(zName)>=0 ){ + if( notFileIsFatal ){ + fossil_warning("not a ordinary file: %s", zName); + nErr++; + } + chnged = 1; + }else if( oldChnged>=2 ){ chnged = oldChnged; }else if( isDeleted || rid==0 ){ chnged = 1; } if( chnged!=1 ){ @@ -193,10 +200,11 @@ if( chnged!=oldChnged ){ db_multi_exec("UPDATE vfile SET chnged=%d WHERE id=%d", chnged, id); } } db_finalize(&q); + if( nErr ) fossil_fatal("abort due to prior errors"); db_end_transaction(0); } /* ** Write all files from vid to the disk. Or if vid==0 and id!=0 @@ -396,12 +404,14 @@ } /* ** Compute an aggregate MD5 checksum over the repository image of every ** file in manifest vid. The file names are part of the checksum. -** ** Return the resulting checksum in blob pOut. +** +** If pManOut is not NULL then fill it with the checksum found in the +** "R" card near the end of the manifest. */ void vfile_aggregate_checksum_manifest(int vid, Blob *pOut, Blob *pManOut){ int i, fid; Blob file, mfile; Manifest m;
Modified src/winhttp.c from [f308305b21] to [4bf909b551].
@@ -136,11 +136,11 @@ ** Start a listening socket and process incoming HTTP requests on ** that socket. */ void win32_http_server(int mnPort, int mxPort, char *zBrowser){ WSADATA wd; - SOCKET s; + SOCKET s = INVALID_SOCKET; SOCKADDR_IN addr; int idCnt = 0; int iPort = mnPort; if( WSAStartup(MAKEWORD(1,1), &wd) ){
Modified src/xfer.c from [002c451193] to [d3ef278c07].
@@ -1070,12 +1070,14 @@ if( blob_buffer(&xfer.line)[0]=='#' ){ continue; } xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken)); nCardRcvd++; - printf("\r%d", nCardRcvd); - fflush(stdout); + if (!g.fQuiet) { + printf("\r%d", nCardRcvd); + fflush(stdout); + } /* file UUID SIZE \n CONTENT ** file UUID DELTASRC SIZE \n CONTENT ** ** Receive a file transmitted from the server.