Fossil SCM
Refactor the name resolution logic in name.c.
Commit
7858a39b36b5388c441eb84461a795bd9e260531
Parent
83a574b019b02b2…
1 file changed
+243
-257
+243
-257
| --- src/name.c | ||
| +++ src/name.c | ||
| @@ -22,10 +22,220 @@ | ||
| 22 | 22 | ** not necessarily in canonical form. |
| 23 | 23 | */ |
| 24 | 24 | #include "config.h" |
| 25 | 25 | #include "name.h" |
| 26 | 26 | #include <assert.h> |
| 27 | + | |
| 28 | +/* | |
| 29 | +** Return TRUE if the string begins with something that looks roughly | |
| 30 | +** like an ISO date/time string. The SQLite date/time functions will | |
| 31 | +** have the final say-so about whether or not the date/time string is | |
| 32 | +** well-formed. | |
| 33 | +*/ | |
| 34 | +static int is_date(const char *z){ | |
| 35 | + if( !fossil_isdigit(z[0]) ) return 0; | |
| 36 | + if( !fossil_isdigit(z[1]) ) return 0; | |
| 37 | + if( !fossil_isdigit(z[2]) ) return 0; | |
| 38 | + if( !fossil_isdigit(z[3]) ) return 0; | |
| 39 | + if( z[4]!='-') return 0; | |
| 40 | + if( !fossil_isdigit(z[5]) ) return 0; | |
| 41 | + if( !fossil_isdigit(z[6]) ) return 0; | |
| 42 | + if( z[7]!='-') return 0; | |
| 43 | + if( !fossil_isdigit(z[8]) ) return 0; | |
| 44 | + if( !fossil_isdigit(z[9]) ) return 0; | |
| 45 | + return 1; | |
| 46 | +} | |
| 47 | + | |
| 48 | +/* | |
| 49 | +** Convert a symbolic name into a RID. Acceptable forms: | |
| 50 | +** | |
| 51 | +** * SHA1 hash | |
| 52 | +** * SHA1 hash prefix of at least 4 characters | |
| 53 | +** * Symbolic Name | |
| 54 | +** * "tag:" + symbolic name | |
| 55 | +** * Date or date-time | |
| 56 | +** * "date:" + Date or date-time | |
| 57 | +** * symbolic-name ":" date-time | |
| 58 | +** * "tip" | |
| 59 | +** | |
| 60 | +** The following additional forms are available in local checkouts: | |
| 61 | +** | |
| 62 | +** * "current" | |
| 63 | +** * "prev" or "previous" | |
| 64 | +** * "next" | |
| 65 | +** | |
| 66 | +** Return the RID of the matching artifact. Or return 0 if the name does not | |
| 67 | +** match any known object. Or return -1 if the name is ambiguious. | |
| 68 | +** | |
| 69 | +** The zType parameter specifies the type of artifact: ci, t, w, e, g. | |
| 70 | +** If zType is NULL or "" or "*" then any type of artifact will serve. | |
| 71 | +** zType is "ci" in most use cases since we are usually searching for | |
| 72 | +** a check-in. | |
| 73 | +*/ | |
| 74 | +static int symbolic_name_to_rid(const char *zTag, const char *zType){ | |
| 75 | + int vid; | |
| 76 | + int rid = 0; | |
| 77 | + int nTag; | |
| 78 | + int i; | |
| 79 | + | |
| 80 | + if( zType==0 || zType[0]==0 ) zType = "*"; | |
| 81 | + if( zTag==0 || zTag[0]==0 ) return 0; | |
| 82 | + | |
| 83 | + /* special keyword: "tip" */ | |
| 84 | + if( fossil_strcmp(zTag, "tip")==0 && (zType[0]=='*' || zType[0]=='c') ){ | |
| 85 | + rid = db_int(0, | |
| 86 | + "SELECT objid" | |
| 87 | + " FROM event" | |
| 88 | + " WHERE type='ci'" | |
| 89 | + " ORDER BY event.mtime DESC" | |
| 90 | + ); | |
| 91 | + if( rid ) return rid; | |
| 92 | + } | |
| 93 | + | |
| 94 | + /* special keywords: "prev", "previous", "current", and "next" */ | |
| 95 | + if( g.localOpen && (vid=db_lget_int("checkout",0))!=0 ){ | |
| 96 | + if( fossil_strcmp(zTag, "current")==0 ){ | |
| 97 | + rid = vid; | |
| 98 | + }else if( fossil_strcmp(zTag, "prev")==0 | |
| 99 | + || fossil_strcmp(zTag, "previous")==0 ){ | |
| 100 | + rid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", vid); | |
| 101 | + }else if( fossil_strcmp(zTag, "next")==0 ){ | |
| 102 | + rid = db_int(0, "SELECT cid FROM plink WHERE pid=%d" | |
| 103 | + " ORDER BY isprim DESC, mtime DESC", vid); | |
| 104 | + } | |
| 105 | + if( rid ) return rid; | |
| 106 | + } | |
| 107 | + | |
| 108 | + /* Date and times */ | |
| 109 | + if( memcmp(zTag, "date:", 5)==0 ){ | |
| 110 | + rid = db_int(0, | |
| 111 | + "SELECT objid FROM event" | |
| 112 | + " WHERE mtime<=julianday(%Q) AND type GLOB '%q'" | |
| 113 | + " ORDER BY mtime DESC LIMIT 1", | |
| 114 | + &zTag[5], zType); | |
| 115 | + return rid; | |
| 116 | + } | |
| 117 | + if( is_date(zTag) ){ | |
| 118 | + rid = db_int(0, | |
| 119 | + "SELECT objid FROM event" | |
| 120 | + " WHERE mtime<=julianday(%Q) AND type GLOB '%q'" | |
| 121 | + " ORDER BY mtime DESC LIMIT 1", | |
| 122 | + zTag, zType); | |
| 123 | + if( rid) return rid; | |
| 124 | + } | |
| 125 | + | |
| 126 | + /* Deprecated date & time formats: "local:" + date-time and | |
| 127 | + ** "utc:" + date-time */ | |
| 128 | + if( memcmp(zTag, "local:", 6)==0 ){ | |
| 129 | + rid = db_int(0, | |
| 130 | + "SELECT objid FROM event" | |
| 131 | + " WHERE mtime<=julianday(%Q) AND type GLOB '%q'" | |
| 132 | + " ORDER BY mtime DESC LIMIT 1", | |
| 133 | + &zTag[6], zType); | |
| 134 | + return rid; | |
| 135 | + } | |
| 136 | + if( memcmp(zTag, "utc:", 4)==0 ){ | |
| 137 | + rid = db_int(0, | |
| 138 | + "SELECT objid FROM event" | |
| 139 | + " WHERE mtime<=julianday('%qz') AND type GLOB '%q'" | |
| 140 | + " ORDER BY mtime DESC LIMIT 1", | |
| 141 | + &zTag[4], zType); | |
| 142 | + return rid; | |
| 143 | + } | |
| 144 | + | |
| 145 | + /* "tag:" + symbolic-name */ | |
| 146 | + if( memcmp(zTag, "tag:", 4)==0 ){ | |
| 147 | + rid = db_int(0, | |
| 148 | + "SELECT event.objid" | |
| 149 | + " FROM tag, tagxref, event" | |
| 150 | + " WHERE tag.tagname='sym-%q' " | |
| 151 | + " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " | |
| 152 | + " AND event.objid=tagxref.rid " | |
| 153 | + " AND event.type GLOB '%q'" | |
| 154 | + " ORDER BY event.mtime DESC /*sort*/", | |
| 155 | + &zTag[4], zType | |
| 156 | + ); | |
| 157 | + return rid; | |
| 158 | + } | |
| 159 | + | |
| 160 | + /* symbolic-name ":" date-time */ | |
| 161 | + nTag = strlen(zTag); | |
| 162 | + for(i=0; i<nTag-10 && zTag[i]!=':'; i++){} | |
| 163 | + if( zTag[i]==':' && is_date(&zTag[i+1]) ){ | |
| 164 | + char *zDate = mprintf("%s", &zTag[i+1]); | |
| 165 | + char *zTagBase = mprintf("%.*s", i, zTag); | |
| 166 | + int nDate = strlen(zDate); | |
| 167 | + if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){ | |
| 168 | + zDate[nDate-3] = 'z'; | |
| 169 | + zDate[nDate-2] = 0; | |
| 170 | + } | |
| 171 | + rid = db_int(0, | |
| 172 | + "SELECT event.objid" | |
| 173 | + " FROM tag, tagxref, event" | |
| 174 | + " WHERE tag.tagname='sym-%q' " | |
| 175 | + " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " | |
| 176 | + " AND event.objid=tagxref.rid " | |
| 177 | + " AND event.mtime<=julianday(%Q)" | |
| 178 | + " AND event.type GLOB '%q'" | |
| 179 | + " ORDER BY event.mtime DESC /*sort*/ ", | |
| 180 | + zTagBase, zDate, zType | |
| 181 | + ); | |
| 182 | + return rid; | |
| 183 | + } | |
| 184 | + | |
| 185 | + /* SHA1 hash or prefix */ | |
| 186 | + if( nTag>=4 && nTag<=UUID_SIZE && validate16(zTag, nTag) ){ | |
| 187 | + Stmt q; | |
| 188 | + char zUuid[UUID_SIZE+1]; | |
| 189 | + memcpy(zUuid, zTag, nTag+1); | |
| 190 | + canonical16(zUuid, nTag); | |
| 191 | + rid = 0; | |
| 192 | + if( zType[0]=='*' ){ | |
| 193 | + db_prepare(&q, "SELECT rid FROM blob WHERE uuid GLOB '%s*'", zUuid); | |
| 194 | + }else{ | |
| 195 | + db_prepare(&q, | |
| 196 | + "SELECT blob.rid" | |
| 197 | + " FROM blob, event" | |
| 198 | + " WHERE blob.uuid GLOB '%s*'" | |
| 199 | + " AND event.objid=blob.rid" | |
| 200 | + " AND event.type GLOB '%q'", | |
| 201 | + zUuid, zType | |
| 202 | + ); | |
| 203 | + } | |
| 204 | + if( db_step(&q)==SQLITE_ROW ){ | |
| 205 | + rid = db_column_int(&q, 0); | |
| 206 | + if( db_step(&q)==SQLITE_ROW ) rid = -1; | |
| 207 | + } | |
| 208 | + db_finalize(&q); | |
| 209 | + if( rid ) return rid; | |
| 210 | + } | |
| 211 | + | |
| 212 | + /* Symbolic name */ | |
| 213 | + rid = db_int(0, | |
| 214 | + "SELECT event.objid" | |
| 215 | + " FROM tag, tagxref, event" | |
| 216 | + " WHERE tag.tagname='sym-%q' " | |
| 217 | + " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " | |
| 218 | + " AND event.objid=tagxref.rid " | |
| 219 | + " AND event.type GLOB '%q'" | |
| 220 | + " ORDER BY event.mtime DESC /*sort*/ ", | |
| 221 | + zTag, zType | |
| 222 | + ); | |
| 223 | + if( rid>0 ) return rid; | |
| 224 | + | |
| 225 | + /* Undocumented: numeric tags get translated directly into the RID */ | |
| 226 | + for(i=0; fossil_isdigit(zTag[i]); i++){} | |
| 227 | + if( zTag[i]==0 ){ | |
| 228 | + rid = db_int(0, | |
| 229 | + "SELECT event.objid" | |
| 230 | + " FROM event" | |
| 231 | + " WHERE event.objid=%s" | |
| 232 | + " AND event.type GLOB '%q'", zTag, zType); | |
| 233 | + } | |
| 234 | + return rid; | |
| 235 | +} | |
| 236 | + | |
| 27 | 237 | |
| 28 | 238 | /* |
| 29 | 239 | ** This routine takes a user-entered UUID which might be in mixed |
| 30 | 240 | ** case and might only be a prefix of the full UUID and converts it |
| 31 | 241 | ** into the full-length UUID in canonical form. |
| @@ -41,228 +251,23 @@ | ||
| 41 | 251 | ** |
| 42 | 252 | ** Return 0 on success. Return 1 if the name cannot be resolved. |
| 43 | 253 | ** Return 2 name is ambiguous. |
| 44 | 254 | */ |
| 45 | 255 | int name_to_uuid(Blob *pName, int iErrPriority, const char *zType){ |
| 46 | - int rc; | |
| 47 | - int sz; | |
| 48 | - sz = blob_size(pName); | |
| 49 | - if( sz>UUID_SIZE || sz<4 || !validate16(blob_buffer(pName), sz) ){ | |
| 50 | - char *zUuid; | |
| 51 | - const char *zName = blob_str(pName); | |
| 52 | - if( memcmp(zName, "tag:", 4)==0 ){ | |
| 53 | - zName += 4; | |
| 54 | - zUuid = tag_to_uuid(zName, zType); | |
| 55 | - }else{ | |
| 56 | - zUuid = tag_to_uuid(zName, zType); | |
| 57 | - if( zUuid==0 ){ | |
| 58 | - zUuid = date_to_uuid(zName, zType); | |
| 59 | - } | |
| 60 | - } | |
| 61 | - if( zUuid ){ | |
| 62 | - blob_reset(pName); | |
| 63 | - blob_append(pName, zUuid, -1); | |
| 64 | - free(zUuid); | |
| 65 | - return 0; | |
| 66 | - } | |
| 67 | - fossil_error(iErrPriority, "not a valid object name: %s", zName); | |
| 68 | - return 1; | |
| 69 | - } | |
| 70 | - blob_materialize(pName); | |
| 71 | - canonical16(blob_buffer(pName), sz); | |
| 72 | - if( sz==UUID_SIZE ){ | |
| 73 | - rc = db_int(1, "SELECT 0 FROM blob WHERE uuid=%B", pName); | |
| 74 | - if( rc ){ | |
| 75 | - fossil_error(iErrPriority, "no such artifact: %b", pName); | |
| 76 | - blob_reset(pName); | |
| 77 | - } | |
| 78 | - }else if( sz<UUID_SIZE && sz>=4 ){ | |
| 79 | - Stmt q; | |
| 80 | - db_prepare(&q, "SELECT uuid FROM blob WHERE uuid GLOB '%b*'", pName); | |
| 81 | - if( db_step(&q)!=SQLITE_ROW ){ | |
| 82 | - char *zUuid; | |
| 83 | - db_finalize(&q); | |
| 84 | - zUuid = tag_to_uuid(blob_str(pName), "*"); | |
| 85 | - if( zUuid ){ | |
| 86 | - blob_reset(pName); | |
| 87 | - blob_append(pName, zUuid, -1); | |
| 88 | - free(zUuid); | |
| 89 | - return 0; | |
| 90 | - } | |
| 91 | - fossil_error(iErrPriority, "no artifacts match the prefix \"%b\"", pName); | |
| 92 | - return 1; | |
| 93 | - } | |
| 94 | - blob_reset(pName); | |
| 95 | - blob_append(pName, db_column_text(&q, 0), db_column_bytes(&q, 0)); | |
| 96 | - if( db_step(&q)==SQLITE_ROW ){ | |
| 97 | - fossil_error(iErrPriority, | |
| 98 | - "multiple artifacts match" | |
| 99 | - ); | |
| 100 | - blob_reset(pName); | |
| 101 | - db_finalize(&q); | |
| 102 | - return 2; | |
| 103 | - } | |
| 104 | - db_finalize(&q); | |
| 105 | - rc = 0; | |
| 106 | - }else{ | |
| 107 | - rc = 0; | |
| 108 | - } | |
| 109 | - return rc; | |
| 110 | -} | |
| 111 | - | |
| 112 | -/* | |
| 113 | -** Return TRUE if the string begins with an ISO8601 date: YYYY-MM-DD. | |
| 114 | -*/ | |
| 115 | -static int is_date(const char *z){ | |
| 116 | - if( !fossil_isdigit(z[0]) ) return 0; | |
| 117 | - if( !fossil_isdigit(z[1]) ) return 0; | |
| 118 | - if( !fossil_isdigit(z[2]) ) return 0; | |
| 119 | - if( !fossil_isdigit(z[3]) ) return 0; | |
| 120 | - if( z[4]!='-') return 0; | |
| 121 | - if( !fossil_isdigit(z[5]) ) return 0; | |
| 122 | - if( !fossil_isdigit(z[6]) ) return 0; | |
| 123 | - if( z[7]!='-') return 0; | |
| 124 | - if( !fossil_isdigit(z[8]) ) return 0; | |
| 125 | - if( !fossil_isdigit(z[9]) ) return 0; | |
| 126 | - return 1; | |
| 127 | -} | |
| 128 | - | |
| 129 | -/* | |
| 130 | -** Convert a symbolic tag name into the UUID of a check-in that contains | |
| 131 | -** that tag. If the tag appears on multiple check-ins, return the UUID | |
| 132 | -** of the most recent check-in with the tag. | |
| 133 | -** | |
| 134 | -** If the input string is of the form: | |
| 135 | -** | |
| 136 | -** tag:date | |
| 137 | -** | |
| 138 | -** Then return the UUID of the oldest check-in with that tag that is | |
| 139 | -** not older than 'date'. | |
| 140 | -** | |
| 141 | -** An input of "tip" returns the most recent check-in. | |
| 142 | -** | |
| 143 | -** Memory to hold the returned string comes from malloc() and needs to | |
| 144 | -** be freed by the caller. | |
| 145 | -*/ | |
| 146 | -char *tag_to_uuid(const char *zTag, const char *zType){ | |
| 147 | - int vid; | |
| 148 | - char *zUuid; | |
| 149 | - | |
| 150 | - if( zType==0 || zType[0]==0 ) zType = "*"; | |
| 151 | - zUuid = db_text(0, | |
| 152 | - "SELECT blob.uuid" | |
| 153 | - " FROM tag, tagxref, event, blob" | |
| 154 | - " WHERE tag.tagname='sym-%q' " | |
| 155 | - " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " | |
| 156 | - " AND event.objid=tagxref.rid " | |
| 157 | - " AND blob.rid=event.objid " | |
| 158 | - " AND event.type GLOB '%q'" | |
| 159 | - " ORDER BY event.mtime DESC /*sort*/", | |
| 160 | - zTag, zType | |
| 161 | - ); | |
| 162 | - if( zUuid==0 ){ | |
| 163 | - int nTag = strlen(zTag); | |
| 164 | - int i; | |
| 165 | - for(i=0; i<nTag-10; i++){ | |
| 166 | - if( zTag[i]==':' && is_date(&zTag[i+1]) ){ | |
| 167 | - char *zDate = mprintf("%s", &zTag[i+1]); | |
| 168 | - char *zTagBase = mprintf("%.*s", i, zTag); | |
| 169 | - int nDate = strlen(zDate); | |
| 170 | - int useUtc = 0; | |
| 171 | - if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){ | |
| 172 | - nDate -= 3; | |
| 173 | - zDate[nDate] = 0; | |
| 174 | - useUtc = 1; | |
| 175 | - } | |
| 176 | - zUuid = db_text(0, | |
| 177 | - "SELECT blob.uuid" | |
| 178 | - " FROM tag, tagxref, event, blob" | |
| 179 | - " WHERE tag.tagname='sym-%q' " | |
| 180 | - " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " | |
| 181 | - " AND event.objid=tagxref.rid " | |
| 182 | - " AND blob.rid=event.objid " | |
| 183 | - " AND event.mtime<=julianday(%Q %s)" | |
| 184 | - " AND event.type GLOB '%q'" | |
| 185 | - " ORDER BY event.mtime DESC /*sort*/ ", | |
| 186 | - zTagBase, zDate, (useUtc ? "" : ",'utc'"), zType | |
| 187 | - ); | |
| 188 | - break; | |
| 189 | - } | |
| 190 | - } | |
| 191 | - if( zUuid==0 && fossil_strcmp(zTag, "tip")==0 ){ | |
| 192 | - zUuid = db_text(0, | |
| 193 | - "SELECT blob.uuid" | |
| 194 | - " FROM event, blob" | |
| 195 | - " WHERE event.type='ci'" | |
| 196 | - " AND blob.rid=event.objid" | |
| 197 | - " ORDER BY event.mtime DESC" | |
| 198 | - ); | |
| 199 | - } | |
| 200 | - if( zUuid==0 && g.localOpen && (vid=db_lget_int("checkout",0))!=0 ){ | |
| 201 | - if( fossil_strcmp(zTag, "current")==0 ){ | |
| 202 | - zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); | |
| 203 | - }else if( fossil_strcmp(zTag, "prev")==0 | |
| 204 | - || fossil_strcmp(zTag, "previous")==0 ){ | |
| 205 | - zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=" | |
| 206 | - "(SELECT pid FROM plink WHERE cid=%d AND isprim)", | |
| 207 | - vid); | |
| 208 | - }else if( fossil_strcmp(zTag, "next")==0 ){ | |
| 209 | - zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=" | |
| 210 | - "(SELECT cid FROM plink WHERE pid=%d" | |
| 211 | - " ORDER BY isprim DESC, mtime DESC)", | |
| 212 | - vid); | |
| 213 | - } | |
| 214 | - } | |
| 215 | - } | |
| 216 | - return zUuid; | |
| 217 | -} | |
| 218 | - | |
| 219 | -/* | |
| 220 | -** Convert a date/time string into a UUID. | |
| 221 | -** | |
| 222 | -** Input forms accepted: | |
| 223 | -** | |
| 224 | -** date:DATE | |
| 225 | -** local:DATE | |
| 226 | -** utc:DATE | |
| 227 | -** | |
| 228 | -** The DATE is interpreted as localtime unless the "utc:" prefix is used | |
| 229 | -** or a "utc" string appears at the end of the DATE string. | |
| 230 | -*/ | |
| 231 | -char *date_to_uuid(const char *zDate, const char *zType){ | |
| 232 | - int useUtc = 0; | |
| 233 | - int n; | |
| 234 | - char *zCopy = 0; | |
| 235 | - char *zUuid; | |
| 236 | - | |
| 237 | - if( memcmp(zDate, "date:", 5)==0 ){ | |
| 238 | - zDate += 5; | |
| 239 | - }else if( memcmp(zDate, "local:", 6)==0 ){ | |
| 240 | - zDate += 6; | |
| 241 | - }else if( memcmp(zDate, "utc:", 4)==0 ){ | |
| 242 | - zDate += 4; | |
| 243 | - useUtc = 1; | |
| 244 | - } | |
| 245 | - n = strlen(zDate); | |
| 246 | - if( n<10 || !is_date(zDate) ) return 0; | |
| 247 | - if( n>4 && sqlite3_strnicmp(&zDate[n-3], "utc", 3)==0 ){ | |
| 248 | - zCopy = mprintf("%s", zDate); | |
| 249 | - zCopy[n-3] = 0; | |
| 250 | - zDate = zCopy; | |
| 251 | - n -= 3; | |
| 252 | - useUtc = 1; | |
| 253 | - } | |
| 254 | - if( zType==0 || zType[0]==0 ) zType = "*"; | |
| 255 | - zUuid = db_text(0, | |
| 256 | - "SELECT (SELECT uuid FROM blob WHERE rid=event.objid)" | |
| 257 | - " FROM event" | |
| 258 | - " WHERE mtime<=julianday(%Q %s) AND type GLOB '%q'" | |
| 259 | - " ORDER BY mtime DESC LIMIT 1", | |
| 260 | - zDate, useUtc ? "" : ",'utc'", zType | |
| 261 | - ); | |
| 262 | - free(zCopy); | |
| 263 | - return zUuid; | |
| 256 | + char *zName = blob_str(pName); | |
| 257 | + int rid = symbolic_name_to_rid(zName, zType); | |
| 258 | + if( rid<0 ){ | |
| 259 | + fossil_error(iErrPriority, "ambiguous name: %s", zName); | |
| 260 | + return 2; | |
| 261 | + }else if( rid==0 ){ | |
| 262 | + fossil_error(iErrPriority, "not found: %s", zName); | |
| 263 | + return 1; | |
| 264 | + }else{ | |
| 265 | + blob_reset(pName); | |
| 266 | + db_blob(pName, "SELECT uuid FROM blob WHERE rid=%d", rid); | |
| 267 | + return 0; | |
| 268 | + } | |
| 264 | 269 | } |
| 265 | 270 | |
| 266 | 271 | /* |
| 267 | 272 | ** COMMAND: test-name-to-id |
| 268 | 273 | ** |
| @@ -284,41 +289,38 @@ | ||
| 284 | 289 | blob_reset(&name); |
| 285 | 290 | } |
| 286 | 291 | } |
| 287 | 292 | |
| 288 | 293 | /* |
| 289 | -** Convert a name to a rid. If the name is a small integer value then | |
| 290 | -** just use atoi() to do the conversion. If the name contains alphabetic | |
| 291 | -** characters or is not an existing rid, then use name_to_uuid then | |
| 292 | -** convert the uuid to a rid. | |
| 294 | +** Convert a name to a rid. If the name can be any of the various forms | |
| 295 | +** accepted: | |
| 296 | +** | |
| 297 | +** * SHA1 hash or prefix thereof | |
| 298 | +** * symbolic name | |
| 299 | +** * date | |
| 300 | +** * label:date | |
| 301 | +** * prev, previous | |
| 302 | +** * next | |
| 303 | +** * tip | |
| 293 | 304 | ** |
| 294 | 305 | ** This routine is used by command-line routines to resolve command-line inputs |
| 295 | 306 | ** into a rid. |
| 296 | 307 | */ |
| 297 | 308 | int name_to_typed_rid(const char *zName, const char *zType){ |
| 298 | - int i; | |
| 299 | 309 | int rid; |
| 300 | - Blob name; | |
| 301 | 310 | |
| 302 | 311 | if( zName==0 || zName[0]==0 ) return 0; |
| 303 | - blob_init(&name, zName, -1); | |
| 304 | - if( name_to_uuid(&name, -1, zType) ){ | |
| 305 | - blob_reset(&name); | |
| 306 | - for(i=0; zName[i] && fossil_isdigit(zName[i]); i++){} | |
| 307 | - if( zName[i]==0 ){ | |
| 308 | - rid = atoi(zName); | |
| 309 | - if( db_exists("SELECT 1 FROM blob WHERE rid=%d", rid) ){ | |
| 310 | - return rid; | |
| 311 | - } | |
| 312 | - } | |
| 313 | - fossil_error(1, "no such artifact: %s", zName); | |
| 312 | + rid = symbolic_name_to_rid(zName, zType); | |
| 313 | + if( rid<0 ){ | |
| 314 | + fossil_error(1, "ambiguous name: %s", zName); | |
| 315 | + return 0; | |
| 316 | + }else if( rid==0 ){ | |
| 317 | + fossil_error(1, "not found: %s", zName); | |
| 314 | 318 | return 0; |
| 315 | 319 | }else{ |
| 316 | - rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &name); | |
| 317 | - blob_reset(&name); | |
| 320 | + return rid; | |
| 318 | 321 | } |
| 319 | - return rid; | |
| 320 | 322 | } |
| 321 | 323 | int name_to_rid(const char *zName){ |
| 322 | 324 | return name_to_typed_rid(zName, "*"); |
| 323 | 325 | } |
| 324 | 326 | |
| @@ -362,32 +364,16 @@ | ||
| 362 | 364 | ** rid. If the CGI parameter is missing or is not a valid artifact tag, |
| 363 | 365 | ** return 0. If the CGI parameter is ambiguous, redirect to a page that |
| 364 | 366 | ** shows all possibilities and do not return. |
| 365 | 367 | */ |
| 366 | 368 | int name_to_rid_www(const char *zParamName){ |
| 367 | - int i, rc; | |
| 368 | 369 | int rid; |
| 369 | 370 | const char *zName = P(zParamName); |
| 370 | - Blob name; | |
| 371 | 371 | |
| 372 | 372 | if( zName==0 || zName[0]==0 ) return 0; |
| 373 | - blob_init(&name, zName, -1); | |
| 374 | - rc = name_to_uuid(&name, -1, "*"); | |
| 375 | - if( rc==1 ){ | |
| 376 | - blob_reset(&name); | |
| 377 | - for(i=0; zName[i] && fossil_isdigit(zName[i]); i++){} | |
| 378 | - if( zName[i]==0 ){ | |
| 379 | - rid = atoi(zName); | |
| 380 | - if( db_exists("SELECT 1 FROM blob WHERE rid=%d", rid) ){ | |
| 381 | - return rid; | |
| 382 | - } | |
| 383 | - } | |
| 384 | - return 0; | |
| 385 | - }else if( rc==2 ){ | |
| 373 | + rid = symbolic_name_to_rid(zName, "*"); | |
| 374 | + if( rid<0 ){ | |
| 386 | 375 | cgi_redirectf("%s/ambiguous/%T?src=%t", g.zTop, zName, g.zPath); |
| 387 | - return 0; | |
| 388 | - }else{ | |
| 389 | - rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &name); | |
| 390 | - blob_reset(&name); | |
| 376 | + rid = 0; | |
| 391 | 377 | } |
| 392 | 378 | return rid; |
| 393 | 379 | } |
| 394 | 380 |
| --- src/name.c | |
| +++ src/name.c | |
| @@ -22,10 +22,220 @@ | |
| 22 | ** not necessarily in canonical form. |
| 23 | */ |
| 24 | #include "config.h" |
| 25 | #include "name.h" |
| 26 | #include <assert.h> |
| 27 | |
| 28 | /* |
| 29 | ** This routine takes a user-entered UUID which might be in mixed |
| 30 | ** case and might only be a prefix of the full UUID and converts it |
| 31 | ** into the full-length UUID in canonical form. |
| @@ -41,228 +251,23 @@ | |
| 41 | ** |
| 42 | ** Return 0 on success. Return 1 if the name cannot be resolved. |
| 43 | ** Return 2 name is ambiguous. |
| 44 | */ |
| 45 | int name_to_uuid(Blob *pName, int iErrPriority, const char *zType){ |
| 46 | int rc; |
| 47 | int sz; |
| 48 | sz = blob_size(pName); |
| 49 | if( sz>UUID_SIZE || sz<4 || !validate16(blob_buffer(pName), sz) ){ |
| 50 | char *zUuid; |
| 51 | const char *zName = blob_str(pName); |
| 52 | if( memcmp(zName, "tag:", 4)==0 ){ |
| 53 | zName += 4; |
| 54 | zUuid = tag_to_uuid(zName, zType); |
| 55 | }else{ |
| 56 | zUuid = tag_to_uuid(zName, zType); |
| 57 | if( zUuid==0 ){ |
| 58 | zUuid = date_to_uuid(zName, zType); |
| 59 | } |
| 60 | } |
| 61 | if( zUuid ){ |
| 62 | blob_reset(pName); |
| 63 | blob_append(pName, zUuid, -1); |
| 64 | free(zUuid); |
| 65 | return 0; |
| 66 | } |
| 67 | fossil_error(iErrPriority, "not a valid object name: %s", zName); |
| 68 | return 1; |
| 69 | } |
| 70 | blob_materialize(pName); |
| 71 | canonical16(blob_buffer(pName), sz); |
| 72 | if( sz==UUID_SIZE ){ |
| 73 | rc = db_int(1, "SELECT 0 FROM blob WHERE uuid=%B", pName); |
| 74 | if( rc ){ |
| 75 | fossil_error(iErrPriority, "no such artifact: %b", pName); |
| 76 | blob_reset(pName); |
| 77 | } |
| 78 | }else if( sz<UUID_SIZE && sz>=4 ){ |
| 79 | Stmt q; |
| 80 | db_prepare(&q, "SELECT uuid FROM blob WHERE uuid GLOB '%b*'", pName); |
| 81 | if( db_step(&q)!=SQLITE_ROW ){ |
| 82 | char *zUuid; |
| 83 | db_finalize(&q); |
| 84 | zUuid = tag_to_uuid(blob_str(pName), "*"); |
| 85 | if( zUuid ){ |
| 86 | blob_reset(pName); |
| 87 | blob_append(pName, zUuid, -1); |
| 88 | free(zUuid); |
| 89 | return 0; |
| 90 | } |
| 91 | fossil_error(iErrPriority, "no artifacts match the prefix \"%b\"", pName); |
| 92 | return 1; |
| 93 | } |
| 94 | blob_reset(pName); |
| 95 | blob_append(pName, db_column_text(&q, 0), db_column_bytes(&q, 0)); |
| 96 | if( db_step(&q)==SQLITE_ROW ){ |
| 97 | fossil_error(iErrPriority, |
| 98 | "multiple artifacts match" |
| 99 | ); |
| 100 | blob_reset(pName); |
| 101 | db_finalize(&q); |
| 102 | return 2; |
| 103 | } |
| 104 | db_finalize(&q); |
| 105 | rc = 0; |
| 106 | }else{ |
| 107 | rc = 0; |
| 108 | } |
| 109 | return rc; |
| 110 | } |
| 111 | |
| 112 | /* |
| 113 | ** Return TRUE if the string begins with an ISO8601 date: YYYY-MM-DD. |
| 114 | */ |
| 115 | static int is_date(const char *z){ |
| 116 | if( !fossil_isdigit(z[0]) ) return 0; |
| 117 | if( !fossil_isdigit(z[1]) ) return 0; |
| 118 | if( !fossil_isdigit(z[2]) ) return 0; |
| 119 | if( !fossil_isdigit(z[3]) ) return 0; |
| 120 | if( z[4]!='-') return 0; |
| 121 | if( !fossil_isdigit(z[5]) ) return 0; |
| 122 | if( !fossil_isdigit(z[6]) ) return 0; |
| 123 | if( z[7]!='-') return 0; |
| 124 | if( !fossil_isdigit(z[8]) ) return 0; |
| 125 | if( !fossil_isdigit(z[9]) ) return 0; |
| 126 | return 1; |
| 127 | } |
| 128 | |
| 129 | /* |
| 130 | ** Convert a symbolic tag name into the UUID of a check-in that contains |
| 131 | ** that tag. If the tag appears on multiple check-ins, return the UUID |
| 132 | ** of the most recent check-in with the tag. |
| 133 | ** |
| 134 | ** If the input string is of the form: |
| 135 | ** |
| 136 | ** tag:date |
| 137 | ** |
| 138 | ** Then return the UUID of the oldest check-in with that tag that is |
| 139 | ** not older than 'date'. |
| 140 | ** |
| 141 | ** An input of "tip" returns the most recent check-in. |
| 142 | ** |
| 143 | ** Memory to hold the returned string comes from malloc() and needs to |
| 144 | ** be freed by the caller. |
| 145 | */ |
| 146 | char *tag_to_uuid(const char *zTag, const char *zType){ |
| 147 | int vid; |
| 148 | char *zUuid; |
| 149 | |
| 150 | if( zType==0 || zType[0]==0 ) zType = "*"; |
| 151 | zUuid = db_text(0, |
| 152 | "SELECT blob.uuid" |
| 153 | " FROM tag, tagxref, event, blob" |
| 154 | " WHERE tag.tagname='sym-%q' " |
| 155 | " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " |
| 156 | " AND event.objid=tagxref.rid " |
| 157 | " AND blob.rid=event.objid " |
| 158 | " AND event.type GLOB '%q'" |
| 159 | " ORDER BY event.mtime DESC /*sort*/", |
| 160 | zTag, zType |
| 161 | ); |
| 162 | if( zUuid==0 ){ |
| 163 | int nTag = strlen(zTag); |
| 164 | int i; |
| 165 | for(i=0; i<nTag-10; i++){ |
| 166 | if( zTag[i]==':' && is_date(&zTag[i+1]) ){ |
| 167 | char *zDate = mprintf("%s", &zTag[i+1]); |
| 168 | char *zTagBase = mprintf("%.*s", i, zTag); |
| 169 | int nDate = strlen(zDate); |
| 170 | int useUtc = 0; |
| 171 | if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){ |
| 172 | nDate -= 3; |
| 173 | zDate[nDate] = 0; |
| 174 | useUtc = 1; |
| 175 | } |
| 176 | zUuid = db_text(0, |
| 177 | "SELECT blob.uuid" |
| 178 | " FROM tag, tagxref, event, blob" |
| 179 | " WHERE tag.tagname='sym-%q' " |
| 180 | " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " |
| 181 | " AND event.objid=tagxref.rid " |
| 182 | " AND blob.rid=event.objid " |
| 183 | " AND event.mtime<=julianday(%Q %s)" |
| 184 | " AND event.type GLOB '%q'" |
| 185 | " ORDER BY event.mtime DESC /*sort*/ ", |
| 186 | zTagBase, zDate, (useUtc ? "" : ",'utc'"), zType |
| 187 | ); |
| 188 | break; |
| 189 | } |
| 190 | } |
| 191 | if( zUuid==0 && fossil_strcmp(zTag, "tip")==0 ){ |
| 192 | zUuid = db_text(0, |
| 193 | "SELECT blob.uuid" |
| 194 | " FROM event, blob" |
| 195 | " WHERE event.type='ci'" |
| 196 | " AND blob.rid=event.objid" |
| 197 | " ORDER BY event.mtime DESC" |
| 198 | ); |
| 199 | } |
| 200 | if( zUuid==0 && g.localOpen && (vid=db_lget_int("checkout",0))!=0 ){ |
| 201 | if( fossil_strcmp(zTag, "current")==0 ){ |
| 202 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); |
| 203 | }else if( fossil_strcmp(zTag, "prev")==0 |
| 204 | || fossil_strcmp(zTag, "previous")==0 ){ |
| 205 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=" |
| 206 | "(SELECT pid FROM plink WHERE cid=%d AND isprim)", |
| 207 | vid); |
| 208 | }else if( fossil_strcmp(zTag, "next")==0 ){ |
| 209 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=" |
| 210 | "(SELECT cid FROM plink WHERE pid=%d" |
| 211 | " ORDER BY isprim DESC, mtime DESC)", |
| 212 | vid); |
| 213 | } |
| 214 | } |
| 215 | } |
| 216 | return zUuid; |
| 217 | } |
| 218 | |
| 219 | /* |
| 220 | ** Convert a date/time string into a UUID. |
| 221 | ** |
| 222 | ** Input forms accepted: |
| 223 | ** |
| 224 | ** date:DATE |
| 225 | ** local:DATE |
| 226 | ** utc:DATE |
| 227 | ** |
| 228 | ** The DATE is interpreted as localtime unless the "utc:" prefix is used |
| 229 | ** or a "utc" string appears at the end of the DATE string. |
| 230 | */ |
| 231 | char *date_to_uuid(const char *zDate, const char *zType){ |
| 232 | int useUtc = 0; |
| 233 | int n; |
| 234 | char *zCopy = 0; |
| 235 | char *zUuid; |
| 236 | |
| 237 | if( memcmp(zDate, "date:", 5)==0 ){ |
| 238 | zDate += 5; |
| 239 | }else if( memcmp(zDate, "local:", 6)==0 ){ |
| 240 | zDate += 6; |
| 241 | }else if( memcmp(zDate, "utc:", 4)==0 ){ |
| 242 | zDate += 4; |
| 243 | useUtc = 1; |
| 244 | } |
| 245 | n = strlen(zDate); |
| 246 | if( n<10 || !is_date(zDate) ) return 0; |
| 247 | if( n>4 && sqlite3_strnicmp(&zDate[n-3], "utc", 3)==0 ){ |
| 248 | zCopy = mprintf("%s", zDate); |
| 249 | zCopy[n-3] = 0; |
| 250 | zDate = zCopy; |
| 251 | n -= 3; |
| 252 | useUtc = 1; |
| 253 | } |
| 254 | if( zType==0 || zType[0]==0 ) zType = "*"; |
| 255 | zUuid = db_text(0, |
| 256 | "SELECT (SELECT uuid FROM blob WHERE rid=event.objid)" |
| 257 | " FROM event" |
| 258 | " WHERE mtime<=julianday(%Q %s) AND type GLOB '%q'" |
| 259 | " ORDER BY mtime DESC LIMIT 1", |
| 260 | zDate, useUtc ? "" : ",'utc'", zType |
| 261 | ); |
| 262 | free(zCopy); |
| 263 | return zUuid; |
| 264 | } |
| 265 | |
| 266 | /* |
| 267 | ** COMMAND: test-name-to-id |
| 268 | ** |
| @@ -284,41 +289,38 @@ | |
| 284 | blob_reset(&name); |
| 285 | } |
| 286 | } |
| 287 | |
| 288 | /* |
| 289 | ** Convert a name to a rid. If the name is a small integer value then |
| 290 | ** just use atoi() to do the conversion. If the name contains alphabetic |
| 291 | ** characters or is not an existing rid, then use name_to_uuid then |
| 292 | ** convert the uuid to a rid. |
| 293 | ** |
| 294 | ** This routine is used by command-line routines to resolve command-line inputs |
| 295 | ** into a rid. |
| 296 | */ |
| 297 | int name_to_typed_rid(const char *zName, const char *zType){ |
| 298 | int i; |
| 299 | int rid; |
| 300 | Blob name; |
| 301 | |
| 302 | if( zName==0 || zName[0]==0 ) return 0; |
| 303 | blob_init(&name, zName, -1); |
| 304 | if( name_to_uuid(&name, -1, zType) ){ |
| 305 | blob_reset(&name); |
| 306 | for(i=0; zName[i] && fossil_isdigit(zName[i]); i++){} |
| 307 | if( zName[i]==0 ){ |
| 308 | rid = atoi(zName); |
| 309 | if( db_exists("SELECT 1 FROM blob WHERE rid=%d", rid) ){ |
| 310 | return rid; |
| 311 | } |
| 312 | } |
| 313 | fossil_error(1, "no such artifact: %s", zName); |
| 314 | return 0; |
| 315 | }else{ |
| 316 | rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &name); |
| 317 | blob_reset(&name); |
| 318 | } |
| 319 | return rid; |
| 320 | } |
| 321 | int name_to_rid(const char *zName){ |
| 322 | return name_to_typed_rid(zName, "*"); |
| 323 | } |
| 324 | |
| @@ -362,32 +364,16 @@ | |
| 362 | ** rid. If the CGI parameter is missing or is not a valid artifact tag, |
| 363 | ** return 0. If the CGI parameter is ambiguous, redirect to a page that |
| 364 | ** shows all possibilities and do not return. |
| 365 | */ |
| 366 | int name_to_rid_www(const char *zParamName){ |
| 367 | int i, rc; |
| 368 | int rid; |
| 369 | const char *zName = P(zParamName); |
| 370 | Blob name; |
| 371 | |
| 372 | if( zName==0 || zName[0]==0 ) return 0; |
| 373 | blob_init(&name, zName, -1); |
| 374 | rc = name_to_uuid(&name, -1, "*"); |
| 375 | if( rc==1 ){ |
| 376 | blob_reset(&name); |
| 377 | for(i=0; zName[i] && fossil_isdigit(zName[i]); i++){} |
| 378 | if( zName[i]==0 ){ |
| 379 | rid = atoi(zName); |
| 380 | if( db_exists("SELECT 1 FROM blob WHERE rid=%d", rid) ){ |
| 381 | return rid; |
| 382 | } |
| 383 | } |
| 384 | return 0; |
| 385 | }else if( rc==2 ){ |
| 386 | cgi_redirectf("%s/ambiguous/%T?src=%t", g.zTop, zName, g.zPath); |
| 387 | return 0; |
| 388 | }else{ |
| 389 | rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &name); |
| 390 | blob_reset(&name); |
| 391 | } |
| 392 | return rid; |
| 393 | } |
| 394 |
| --- src/name.c | |
| +++ src/name.c | |
| @@ -22,10 +22,220 @@ | |
| 22 | ** not necessarily in canonical form. |
| 23 | */ |
| 24 | #include "config.h" |
| 25 | #include "name.h" |
| 26 | #include <assert.h> |
| 27 | |
| 28 | /* |
| 29 | ** Return TRUE if the string begins with something that looks roughly |
| 30 | ** like an ISO date/time string. The SQLite date/time functions will |
| 31 | ** have the final say-so about whether or not the date/time string is |
| 32 | ** well-formed. |
| 33 | */ |
| 34 | static int is_date(const char *z){ |
| 35 | if( !fossil_isdigit(z[0]) ) return 0; |
| 36 | if( !fossil_isdigit(z[1]) ) return 0; |
| 37 | if( !fossil_isdigit(z[2]) ) return 0; |
| 38 | if( !fossil_isdigit(z[3]) ) return 0; |
| 39 | if( z[4]!='-') return 0; |
| 40 | if( !fossil_isdigit(z[5]) ) return 0; |
| 41 | if( !fossil_isdigit(z[6]) ) return 0; |
| 42 | if( z[7]!='-') return 0; |
| 43 | if( !fossil_isdigit(z[8]) ) return 0; |
| 44 | if( !fossil_isdigit(z[9]) ) return 0; |
| 45 | return 1; |
| 46 | } |
| 47 | |
| 48 | /* |
| 49 | ** Convert a symbolic name into a RID. Acceptable forms: |
| 50 | ** |
| 51 | ** * SHA1 hash |
| 52 | ** * SHA1 hash prefix of at least 4 characters |
| 53 | ** * Symbolic Name |
| 54 | ** * "tag:" + symbolic name |
| 55 | ** * Date or date-time |
| 56 | ** * "date:" + Date or date-time |
| 57 | ** * symbolic-name ":" date-time |
| 58 | ** * "tip" |
| 59 | ** |
| 60 | ** The following additional forms are available in local checkouts: |
| 61 | ** |
| 62 | ** * "current" |
| 63 | ** * "prev" or "previous" |
| 64 | ** * "next" |
| 65 | ** |
| 66 | ** Return the RID of the matching artifact. Or return 0 if the name does not |
| 67 | ** match any known object. Or return -1 if the name is ambiguious. |
| 68 | ** |
| 69 | ** The zType parameter specifies the type of artifact: ci, t, w, e, g. |
| 70 | ** If zType is NULL or "" or "*" then any type of artifact will serve. |
| 71 | ** zType is "ci" in most use cases since we are usually searching for |
| 72 | ** a check-in. |
| 73 | */ |
| 74 | static int symbolic_name_to_rid(const char *zTag, const char *zType){ |
| 75 | int vid; |
| 76 | int rid = 0; |
| 77 | int nTag; |
| 78 | int i; |
| 79 | |
| 80 | if( zType==0 || zType[0]==0 ) zType = "*"; |
| 81 | if( zTag==0 || zTag[0]==0 ) return 0; |
| 82 | |
| 83 | /* special keyword: "tip" */ |
| 84 | if( fossil_strcmp(zTag, "tip")==0 && (zType[0]=='*' || zType[0]=='c') ){ |
| 85 | rid = db_int(0, |
| 86 | "SELECT objid" |
| 87 | " FROM event" |
| 88 | " WHERE type='ci'" |
| 89 | " ORDER BY event.mtime DESC" |
| 90 | ); |
| 91 | if( rid ) return rid; |
| 92 | } |
| 93 | |
| 94 | /* special keywords: "prev", "previous", "current", and "next" */ |
| 95 | if( g.localOpen && (vid=db_lget_int("checkout",0))!=0 ){ |
| 96 | if( fossil_strcmp(zTag, "current")==0 ){ |
| 97 | rid = vid; |
| 98 | }else if( fossil_strcmp(zTag, "prev")==0 |
| 99 | || fossil_strcmp(zTag, "previous")==0 ){ |
| 100 | rid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", vid); |
| 101 | }else if( fossil_strcmp(zTag, "next")==0 ){ |
| 102 | rid = db_int(0, "SELECT cid FROM plink WHERE pid=%d" |
| 103 | " ORDER BY isprim DESC, mtime DESC", vid); |
| 104 | } |
| 105 | if( rid ) return rid; |
| 106 | } |
| 107 | |
| 108 | /* Date and times */ |
| 109 | if( memcmp(zTag, "date:", 5)==0 ){ |
| 110 | rid = db_int(0, |
| 111 | "SELECT objid FROM event" |
| 112 | " WHERE mtime<=julianday(%Q) AND type GLOB '%q'" |
| 113 | " ORDER BY mtime DESC LIMIT 1", |
| 114 | &zTag[5], zType); |
| 115 | return rid; |
| 116 | } |
| 117 | if( is_date(zTag) ){ |
| 118 | rid = db_int(0, |
| 119 | "SELECT objid FROM event" |
| 120 | " WHERE mtime<=julianday(%Q) AND type GLOB '%q'" |
| 121 | " ORDER BY mtime DESC LIMIT 1", |
| 122 | zTag, zType); |
| 123 | if( rid) return rid; |
| 124 | } |
| 125 | |
| 126 | /* Deprecated date & time formats: "local:" + date-time and |
| 127 | ** "utc:" + date-time */ |
| 128 | if( memcmp(zTag, "local:", 6)==0 ){ |
| 129 | rid = db_int(0, |
| 130 | "SELECT objid FROM event" |
| 131 | " WHERE mtime<=julianday(%Q) AND type GLOB '%q'" |
| 132 | " ORDER BY mtime DESC LIMIT 1", |
| 133 | &zTag[6], zType); |
| 134 | return rid; |
| 135 | } |
| 136 | if( memcmp(zTag, "utc:", 4)==0 ){ |
| 137 | rid = db_int(0, |
| 138 | "SELECT objid FROM event" |
| 139 | " WHERE mtime<=julianday('%qz') AND type GLOB '%q'" |
| 140 | " ORDER BY mtime DESC LIMIT 1", |
| 141 | &zTag[4], zType); |
| 142 | return rid; |
| 143 | } |
| 144 | |
| 145 | /* "tag:" + symbolic-name */ |
| 146 | if( memcmp(zTag, "tag:", 4)==0 ){ |
| 147 | rid = db_int(0, |
| 148 | "SELECT event.objid" |
| 149 | " FROM tag, tagxref, event" |
| 150 | " WHERE tag.tagname='sym-%q' " |
| 151 | " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " |
| 152 | " AND event.objid=tagxref.rid " |
| 153 | " AND event.type GLOB '%q'" |
| 154 | " ORDER BY event.mtime DESC /*sort*/", |
| 155 | &zTag[4], zType |
| 156 | ); |
| 157 | return rid; |
| 158 | } |
| 159 | |
| 160 | /* symbolic-name ":" date-time */ |
| 161 | nTag = strlen(zTag); |
| 162 | for(i=0; i<nTag-10 && zTag[i]!=':'; i++){} |
| 163 | if( zTag[i]==':' && is_date(&zTag[i+1]) ){ |
| 164 | char *zDate = mprintf("%s", &zTag[i+1]); |
| 165 | char *zTagBase = mprintf("%.*s", i, zTag); |
| 166 | int nDate = strlen(zDate); |
| 167 | if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){ |
| 168 | zDate[nDate-3] = 'z'; |
| 169 | zDate[nDate-2] = 0; |
| 170 | } |
| 171 | rid = db_int(0, |
| 172 | "SELECT event.objid" |
| 173 | " FROM tag, tagxref, event" |
| 174 | " WHERE tag.tagname='sym-%q' " |
| 175 | " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " |
| 176 | " AND event.objid=tagxref.rid " |
| 177 | " AND event.mtime<=julianday(%Q)" |
| 178 | " AND event.type GLOB '%q'" |
| 179 | " ORDER BY event.mtime DESC /*sort*/ ", |
| 180 | zTagBase, zDate, zType |
| 181 | ); |
| 182 | return rid; |
| 183 | } |
| 184 | |
| 185 | /* SHA1 hash or prefix */ |
| 186 | if( nTag>=4 && nTag<=UUID_SIZE && validate16(zTag, nTag) ){ |
| 187 | Stmt q; |
| 188 | char zUuid[UUID_SIZE+1]; |
| 189 | memcpy(zUuid, zTag, nTag+1); |
| 190 | canonical16(zUuid, nTag); |
| 191 | rid = 0; |
| 192 | if( zType[0]=='*' ){ |
| 193 | db_prepare(&q, "SELECT rid FROM blob WHERE uuid GLOB '%s*'", zUuid); |
| 194 | }else{ |
| 195 | db_prepare(&q, |
| 196 | "SELECT blob.rid" |
| 197 | " FROM blob, event" |
| 198 | " WHERE blob.uuid GLOB '%s*'" |
| 199 | " AND event.objid=blob.rid" |
| 200 | " AND event.type GLOB '%q'", |
| 201 | zUuid, zType |
| 202 | ); |
| 203 | } |
| 204 | if( db_step(&q)==SQLITE_ROW ){ |
| 205 | rid = db_column_int(&q, 0); |
| 206 | if( db_step(&q)==SQLITE_ROW ) rid = -1; |
| 207 | } |
| 208 | db_finalize(&q); |
| 209 | if( rid ) return rid; |
| 210 | } |
| 211 | |
| 212 | /* Symbolic name */ |
| 213 | rid = db_int(0, |
| 214 | "SELECT event.objid" |
| 215 | " FROM tag, tagxref, event" |
| 216 | " WHERE tag.tagname='sym-%q' " |
| 217 | " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " |
| 218 | " AND event.objid=tagxref.rid " |
| 219 | " AND event.type GLOB '%q'" |
| 220 | " ORDER BY event.mtime DESC /*sort*/ ", |
| 221 | zTag, zType |
| 222 | ); |
| 223 | if( rid>0 ) return rid; |
| 224 | |
| 225 | /* Undocumented: numeric tags get translated directly into the RID */ |
| 226 | for(i=0; fossil_isdigit(zTag[i]); i++){} |
| 227 | if( zTag[i]==0 ){ |
| 228 | rid = db_int(0, |
| 229 | "SELECT event.objid" |
| 230 | " FROM event" |
| 231 | " WHERE event.objid=%s" |
| 232 | " AND event.type GLOB '%q'", zTag, zType); |
| 233 | } |
| 234 | return rid; |
| 235 | } |
| 236 | |
| 237 | |
| 238 | /* |
| 239 | ** This routine takes a user-entered UUID which might be in mixed |
| 240 | ** case and might only be a prefix of the full UUID and converts it |
| 241 | ** into the full-length UUID in canonical form. |
| @@ -41,228 +251,23 @@ | |
| 251 | ** |
| 252 | ** Return 0 on success. Return 1 if the name cannot be resolved. |
| 253 | ** Return 2 name is ambiguous. |
| 254 | */ |
| 255 | int name_to_uuid(Blob *pName, int iErrPriority, const char *zType){ |
| 256 | char *zName = blob_str(pName); |
| 257 | int rid = symbolic_name_to_rid(zName, zType); |
| 258 | if( rid<0 ){ |
| 259 | fossil_error(iErrPriority, "ambiguous name: %s", zName); |
| 260 | return 2; |
| 261 | }else if( rid==0 ){ |
| 262 | fossil_error(iErrPriority, "not found: %s", zName); |
| 263 | return 1; |
| 264 | }else{ |
| 265 | blob_reset(pName); |
| 266 | db_blob(pName, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 267 | return 0; |
| 268 | } |
| 269 | } |
| 270 | |
| 271 | /* |
| 272 | ** COMMAND: test-name-to-id |
| 273 | ** |
| @@ -284,41 +289,38 @@ | |
| 289 | blob_reset(&name); |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | /* |
| 294 | ** Convert a name to a rid. If the name can be any of the various forms |
| 295 | ** accepted: |
| 296 | ** |
| 297 | ** * SHA1 hash or prefix thereof |
| 298 | ** * symbolic name |
| 299 | ** * date |
| 300 | ** * label:date |
| 301 | ** * prev, previous |
| 302 | ** * next |
| 303 | ** * tip |
| 304 | ** |
| 305 | ** This routine is used by command-line routines to resolve command-line inputs |
| 306 | ** into a rid. |
| 307 | */ |
| 308 | int name_to_typed_rid(const char *zName, const char *zType){ |
| 309 | int rid; |
| 310 | |
| 311 | if( zName==0 || zName[0]==0 ) return 0; |
| 312 | rid = symbolic_name_to_rid(zName, zType); |
| 313 | if( rid<0 ){ |
| 314 | fossil_error(1, "ambiguous name: %s", zName); |
| 315 | return 0; |
| 316 | }else if( rid==0 ){ |
| 317 | fossil_error(1, "not found: %s", zName); |
| 318 | return 0; |
| 319 | }else{ |
| 320 | return rid; |
| 321 | } |
| 322 | } |
| 323 | int name_to_rid(const char *zName){ |
| 324 | return name_to_typed_rid(zName, "*"); |
| 325 | } |
| 326 | |
| @@ -362,32 +364,16 @@ | |
| 364 | ** rid. If the CGI parameter is missing or is not a valid artifact tag, |
| 365 | ** return 0. If the CGI parameter is ambiguous, redirect to a page that |
| 366 | ** shows all possibilities and do not return. |
| 367 | */ |
| 368 | int name_to_rid_www(const char *zParamName){ |
| 369 | int rid; |
| 370 | const char *zName = P(zParamName); |
| 371 | |
| 372 | if( zName==0 || zName[0]==0 ) return 0; |
| 373 | rid = symbolic_name_to_rid(zName, "*"); |
| 374 | if( rid<0 ){ |
| 375 | cgi_redirectf("%s/ambiguous/%T?src=%t", g.zTop, zName, g.zPath); |
| 376 | rid = 0; |
| 377 | } |
| 378 | return rid; |
| 379 | } |
| 380 |