Fossil SCM

fossil-scm / src / json_dir.c
Source Blame History 294 lines
318a0ac… stephan 1 #ifdef FOSSIL_ENABLE_JSON
318a0ac… stephan 2 /*
c19f34c… drh 3 ** Copyright (c) 2011 D. Richard Hipp
318a0ac… stephan 4 **
318a0ac… stephan 5 ** This program is free software; you can redistribute it and/or
318a0ac… stephan 6 ** modify it under the terms of the Simplified BSD License (also
318a0ac… stephan 7 ** known as the "2-Clause License" or "FreeBSD License".)
318a0ac… stephan 8 **
318a0ac… stephan 9 ** This program is distributed in the hope that it will be useful,
318a0ac… stephan 10 ** but without any warranty; without even the implied warranty of
318a0ac… stephan 11 ** merchantability or fitness for a particular purpose.
318a0ac… stephan 12 **
318a0ac… stephan 13 ** Author contact information:
318a0ac… stephan 14 ** [email protected]
318a0ac… stephan 15 ** http://www.hwaci.com/drh/
318a0ac… stephan 16 **
318a0ac… stephan 17 */
318a0ac… stephan 18 #include "VERSION.h"
318a0ac… stephan 19 #include "config.h"
318a0ac… stephan 20 #include "json_dir.h"
318a0ac… stephan 21
318a0ac… stephan 22 #if INTERFACE
318a0ac… stephan 23 #include "json_detail.h"
318a0ac… stephan 24 #endif
318a0ac… stephan 25
a80f274… stephan 26 static cson_value * json_page_dir_list(void);
318a0ac… stephan 27 /*
318a0ac… stephan 28 ** Mapping of /json/wiki/XXX commands/paths to callbacks.
318a0ac… stephan 29 */
a61922e… mistachkin 30 #if 0 /* TODO: Not used? */
318a0ac… stephan 31 static const JsonPageDef JsonPageDefs_Dir[] = {
318a0ac… stephan 32 /* Last entry MUST have a NULL name. */
318a0ac… stephan 33 {NULL,NULL,0}
318a0ac… stephan 34 };
a61922e… mistachkin 35 #endif
318a0ac… stephan 36
43631b0… mistachkin 37 #if 0 /* TODO: Not used? */
a80f274… stephan 38 static char const * json_dir_path_extra(void){
318a0ac… stephan 39 static char const * zP = NULL;
318a0ac… stephan 40 if( !zP ){
318a0ac… stephan 41 zP = g.zExtra;
857a6d9… stephan 42 while(zP && *zP && ('/'==*zP)){
318a0ac… stephan 43 ++zP;
318a0ac… stephan 44 }
318a0ac… stephan 45 }
318a0ac… stephan 46 return zP;
318a0ac… stephan 47 }
43631b0… mistachkin 48 #endif
318a0ac… stephan 49
318a0ac… stephan 50 /*
318a0ac… stephan 51 ** Impl of /json/dir. 98% of it was taken directly
318a0ac… stephan 52 ** from browse.c::page_dir()
318a0ac… stephan 53 */
a80f274… stephan 54 static cson_value * json_page_dir_list(void){
517d0ef… stephan 55 cson_object * zPayload = NULL; /* return value */
517d0ef… stephan 56 cson_array * zEntries = NULL; /* accumulated list of entries. */
517d0ef… stephan 57 cson_object * zEntry = NULL; /* a single dir/file entry. */
517d0ef… stephan 58 cson_array * keyStore = NULL; /* garbage collector for shared strings. */
318a0ac… stephan 59 cson_string * zKeyName = NULL;
7b89c7b… stephan 60 cson_string * zKeySize = NULL;
318a0ac… stephan 61 cson_string * zKeyIsDir = NULL;
318a0ac… stephan 62 cson_string * zKeyUuid = NULL;
a81b34b… stephan 63 cson_string * zKeyTime = NULL;
517d0ef… stephan 64 cson_string * zKeyRaw = NULL;
318a0ac… stephan 65 char * zD = NULL;
318a0ac… stephan 66 char const * zDX = NULL;
318a0ac… stephan 67 int nD;
318a0ac… stephan 68 char * zUuid = NULL;
318a0ac… stephan 69 char const * zCI = NULL;
318a0ac… stephan 70 Manifest * pM = NULL;
318a0ac… stephan 71 Stmt q = empty_Stmt;
318a0ac… stephan 72 int rid = 0;
625ed00… stephan 73 if( !g.perm.Read ){
625ed00… stephan 74 json_set_err(FSL_JSON_E_DENIED, "Requires 'o' permissions.");
318a0ac… stephan 75 return NULL;
318a0ac… stephan 76 }
318a0ac… stephan 77 zCI = json_find_option_cstr("checkin",NULL,"ci" );
318a0ac… stephan 78
318a0ac… stephan 79 /* If a specific check-in is requested, fetch and parse it. If the
318a0ac… stephan 80 ** specific check-in does not exist, clear zCI. zCI==0 will cause all
318a0ac… stephan 81 ** files from all check-ins to be displayed.
318a0ac… stephan 82 */
318a0ac… stephan 83 if( zCI && *zCI ){
318a0ac… stephan 84 pM = manifest_get_by_name(zCI, &rid);
318a0ac… stephan 85 if( pM ){
318a0ac… stephan 86 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
318a0ac… stephan 87 }else{
318a0ac… stephan 88 json_set_err(FSL_JSON_E_UNRESOLVED_UUID,
30c107e… mistachkin 89 "Check-in name [%s] is unresolved.",
318a0ac… stephan 90 zCI);
318a0ac… stephan 91 return NULL;
318a0ac… stephan 92 }
318a0ac… stephan 93 }
517d0ef… stephan 94
517d0ef… stephan 95 /* Jump through some hoops to find the directory name... */
34fc6f0… stephan 96 zDX = json_find_option_cstr("name",NULL,NULL);
b804326… stephan 97 if(!zDX && !g.isHTTP){
34fc6f0… stephan 98 zDX = json_command_arg(g.json.dispatchDepth+1);
857a6d9… stephan 99 }
b804326… stephan 100 if(zDX && (!*zDX || (0==strcmp(zDX,"/")))){
b804326… stephan 101 zDX = NULL;
b804326… stephan 102 }
b804326… stephan 103 zD = zDX ? fossil_strdup(zDX) : NULL;
318a0ac… stephan 104 nD = zD ? strlen(zD)+1 : 0;
318a0ac… stephan 105 while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; }
318a0ac… stephan 106
318a0ac… stephan 107 sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
318a0ac… stephan 108 pathelementFunc, 0, 0);
318a0ac… stephan 109
318a0ac… stephan 110 /* Compute the temporary table "localfiles" containing the names
4c5c96c… jan.nijtmans 111 ** of all files and subdirectories in the zD[] directory.
318a0ac… stephan 112 **
318a0ac… stephan 113 ** Subdirectory names begin with "/". This causes them to sort
318a0ac… stephan 114 ** first and it also gives us an easy way to distinguish files
318a0ac… stephan 115 ** from directories in the loop that follows.
318a0ac… stephan 116 */
318a0ac… stephan 117
318a0ac… stephan 118 if( zCI ){
318a0ac… stephan 119 Stmt ins;
318a0ac… stephan 120 ManifestFile *pFile;
318a0ac… stephan 121 ManifestFile *pPrev = 0;
318a0ac… stephan 122 int nPrev = 0;
318a0ac… stephan 123 int c;
318a0ac… stephan 124
517d0ef… stephan 125 db_multi_exec(
517d0ef… stephan 126 "CREATE TEMP TABLE json_dir_files("
4c5c96c… jan.nijtmans 127 " n UNIQUE NOT NULL," /* file name */
4c5c96c… jan.nijtmans 128 " fn UNIQUE NOT NULL," /* full file name */
517d0ef… stephan 129 " u DEFAULT NULL," /* file uuid */
517d0ef… stephan 130 " sz DEFAULT -1," /* file size */
517d0ef… stephan 131 " mtime DEFAULT NULL" /* file mtime in unix epoch format */
4c5c96c… jan.nijtmans 132 ");"
517d0ef… stephan 133 );
4c5c96c… jan.nijtmans 134
318a0ac… stephan 135 db_prepare(&ins,
517d0ef… stephan 136 "INSERT OR IGNORE INTO json_dir_files (n,fn,u,sz,mtime) "
a81b34b… stephan 137 "SELECT"
a81b34b… stephan 138 " pathelement(:path,0),"
517d0ef… stephan 139 " CASE WHEN %Q IS NULL THEN '' ELSE %Q||'/' END ||:abspath,"
a81b34b… stephan 140 " a.uuid,"
a81b34b… stephan 141 " a.size,"
a81b34b… stephan 142 " CAST(strftime('%%s',e.mtime) AS INTEGER) "
a81b34b… stephan 143 "FROM"
a81b34b… stephan 144 " mlink m, "
a81b34b… stephan 145 " event e,"
a81b34b… stephan 146 " blob a,"
a81b34b… stephan 147 " blob b "
a81b34b… stephan 148 "WHERE"
a81b34b… stephan 149 " e.objid=m.mid"
a81b34b… stephan 150 " AND a.rid=m.fid"/*FILE artifact*/
a81b34b… stephan 151 " AND b.rid=m.mid"/*CHECKIN artifact*/
517d0ef… stephan 152 " AND a.uuid=:uuid",
517d0ef… stephan 153 zD, zD
a81b34b… stephan 154 );
318a0ac… stephan 155 manifest_file_rewind(pM);
318a0ac… stephan 156 while( (pFile = manifest_file_next(pM,0))!=0 ){
4c5c96c… jan.nijtmans 157 if( nD>0
b804326… stephan 158 && ((pFile->zName[nD-1]!='/') || (0!=memcmp(pFile->zName, zD, nD-1)))
318a0ac… stephan 159 ){
318a0ac… stephan 160 continue;
318a0ac… stephan 161 }
b804326… stephan 162 /*printf("zD=%s, nD=%d, pFile->zName=%s\n", zD, nD, pFile->zName);*/
318a0ac… stephan 163 if( pPrev
318a0ac… stephan 164 && memcmp(&pFile->zName[nD],&pPrev->zName[nD],nPrev)==0
318a0ac… stephan 165 && (pFile->zName[nD+nPrev]==0 || pFile->zName[nD+nPrev]=='/')
318a0ac… stephan 166 ){
318a0ac… stephan 167 continue;
318a0ac… stephan 168 }
a81b34b… stephan 169 db_bind_text( &ins, ":path", &pFile->zName[nD] );
517d0ef… stephan 170 db_bind_text( &ins, ":abspath", &pFile->zName[nD] );
a81b34b… stephan 171 db_bind_text( &ins, ":uuid", pFile->zUuid );
318a0ac… stephan 172 db_step(&ins);
318a0ac… stephan 173 db_reset(&ins);
318a0ac… stephan 174 pPrev = pFile;
318a0ac… stephan 175 for(nPrev=0; (c=pPrev->zName[nD+nPrev]) && c!='/'; nPrev++){}
318a0ac… stephan 176 if( c=='/' ) nPrev++;
318a0ac… stephan 177 }
318a0ac… stephan 178 db_finalize(&ins);
b804326… stephan 179 }else if( zD && *zD ){
4c5c96c… jan.nijtmans 180 db_multi_exec(
4c5c96c… jan.nijtmans 181 "CREATE TEMP VIEW json_dir_files AS"
4c5c96c… jan.nijtmans 182 " SELECT DISTINCT(pathelement(name,%d)) AS n,"
4c5c96c… jan.nijtmans 183 " %Q||'/'||name AS fn,"
4c5c96c… jan.nijtmans 184 " NULL AS u, NULL AS sz, NULL AS mtime"
4c5c96c… jan.nijtmans 185 " FROM filename"
4c5c96c… jan.nijtmans 186 " WHERE name GLOB '%q/*'"
4c5c96c… jan.nijtmans 187 " GROUP BY n",
4c5c96c… jan.nijtmans 188 nD, zD, zD
4c5c96c… jan.nijtmans 189 );
517d0ef… stephan 190 }else{
517d0ef… stephan 191 db_multi_exec(
517d0ef… stephan 192 "CREATE TEMP VIEW json_dir_files"
517d0ef… stephan 193 " AS SELECT DISTINCT(pathelement(name,0)) AS n, NULL AS fn"
517d0ef… stephan 194 " FROM filename"
318a0ac… stephan 195 );
318a0ac… stephan 196 }
318a0ac… stephan 197
318a0ac… stephan 198 if(zCI){
a81b34b… stephan 199 db_prepare( &q, "SELECT"
a81b34b… stephan 200 " n as name,"
517d0ef… stephan 201 " fn as fullname,"
a81b34b… stephan 202 " u as uuid,"
a81b34b… stephan 203 " sz as size,"
a81b34b… stephan 204 " mtime as mtime "
a81b34b… stephan 205 "FROM json_dir_files ORDER BY n");
318a0ac… stephan 206 }else{/* UUIDs are all NULL. */
517d0ef… stephan 207 db_prepare( &q, "SELECT n, fn FROM json_dir_files ORDER BY n");
318a0ac… stephan 208 }
318a0ac… stephan 209
318a0ac… stephan 210 zKeyName = cson_new_string("name",4);
7b89c7b… stephan 211 zKeyUuid = cson_new_string("uuid",4);
7b89c7b… stephan 212 zKeyIsDir = cson_new_string("isDir",5);
a81b34b… stephan 213 keyStore = cson_new_array();
a81b34b… stephan 214 cson_array_append( keyStore, cson_string_value(zKeyName) );
a81b34b… stephan 215 cson_array_append( keyStore, cson_string_value(zKeyUuid) );
a81b34b… stephan 216 cson_array_append( keyStore, cson_string_value(zKeyIsDir) );
a81b34b… stephan 217
7b89c7b… stephan 218 if( zCI ){
7b89c7b… stephan 219 zKeySize = cson_new_string("size",4);
a81b34b… stephan 220 cson_array_append( keyStore, cson_string_value(zKeySize) );
a81b34b… stephan 221 zKeyTime = cson_new_string("timestamp",9);
a81b34b… stephan 222 cson_array_append( keyStore, cson_string_value(zKeyTime) );
517d0ef… stephan 223 zKeyRaw = cson_new_string("downloadPath",12);
517d0ef… stephan 224 cson_array_append( keyStore, cson_string_value(zKeyRaw) );
7b89c7b… stephan 225 }
7b89c7b… stephan 226 zPayload = cson_new_object();
7b89c7b… stephan 227 cson_object_set_s( zPayload, zKeyName,
7b89c7b… stephan 228 json_new_string((zD&&*zD) ? zD : "/") );
c1963c4… stephan 229 if( zUuid ){
c1963c4… stephan 230 cson_object_set( zPayload, "checkin", json_new_string(zUuid) );
b804326… stephan 231 }
318a0ac… stephan 232
318a0ac… stephan 233 while( (SQLITE_ROW==db_step(&q)) ){
726a6f7… stephan 234 cson_value * name = NULL;
318a0ac… stephan 235 char const * n = db_column_text(&q,0);
7b89c7b… stephan 236 char const isDir = ('/'==*n);
318a0ac… stephan 237 zEntry = cson_new_object();
726a6f7… stephan 238 if(!zEntries){
726a6f7… stephan 239 zEntries = cson_new_array();
726a6f7… stephan 240 cson_object_set( zPayload, "entries", cson_array_value(zEntries) );
726a6f7… stephan 241 }
7b89c7b… stephan 242 cson_array_append(zEntries, cson_object_value(zEntry) );
7b89c7b… stephan 243 if(isDir){
726a6f7… stephan 244 name = json_new_string( n+1 );
318a0ac… stephan 245 cson_object_set_s(zEntry, zKeyIsDir, cson_value_true() );
318a0ac… stephan 246 } else{
726a6f7… stephan 247 name = json_new_string( n );
318a0ac… stephan 248 }
726a6f7… stephan 249 cson_object_set_s(zEntry, zKeyName, name );
c1963c4… stephan 250 if( zCI && !isDir){
070b755… stephan 251 /* Don't add the uuid/size for dir entries - that data refers to
070b755… stephan 252 one of the files in that directory :/. Entries with no
070b755… stephan 253 --checkin may refer to N versions, and therefore we cannot
070b755… stephan 254 associate a single size and uuid with them (and fetching all
070b755… stephan 255 would be overkill for most use cases).
070b755… stephan 256 */
517d0ef… stephan 257 char const * fullName = db_column_text(&q,1);
517d0ef… stephan 258 char const * u = db_column_text(&q,2);
517d0ef… stephan 259 sqlite_int64 const sz = db_column_int64(&q,3);
517d0ef… stephan 260 sqlite_int64 const ts = db_column_int64(&q,4);
070b755… stephan 261 cson_object_set_s(zEntry, zKeyUuid, json_new_string( u ) );
c1963c4… stephan 262 cson_object_set_s(zEntry, zKeySize,
c1963c4… stephan 263 cson_value_new_integer( (cson_int_t)sz ));
a81b34b… stephan 264 cson_object_set_s(zEntry, zKeyTime,
a81b34b… stephan 265 cson_value_new_integer( (cson_int_t)ts ));
517d0ef… stephan 266 cson_object_set_s(zEntry, zKeyRaw,
bb05bf6… stephan 267 json_new_string_f("/raw/%T?name=%t",
517d0ef… stephan 268 fullName, u));
7b89c7b… stephan 269 }
318a0ac… stephan 270 }
318a0ac… stephan 271 db_finalize(&q);
318a0ac… stephan 272 if(pM){
318a0ac… stephan 273 manifest_destroy(pM);
318a0ac… stephan 274 }
a81b34b… stephan 275 cson_free_array( keyStore );
4c5c96c… jan.nijtmans 276
318a0ac… stephan 277 free( zUuid );
318a0ac… stephan 278 free( zD );
318a0ac… stephan 279 return cson_object_value(zPayload);
318a0ac… stephan 280 }
318a0ac… stephan 281
318a0ac… stephan 282 /*
318a0ac… stephan 283 ** Implements the /json/dir family of pages/commands.
318a0ac… stephan 284 **
318a0ac… stephan 285 */
a80f274… stephan 286 cson_value * json_page_dir(void){
318a0ac… stephan 287 #if 1
318a0ac… stephan 288 return json_page_dir_list();
318a0ac… stephan 289 #else
318a0ac… stephan 290 return json_page_dispatch_helper(&JsonPageDefs_Dir[0]);
318a0ac… stephan 291 #endif
318a0ac… stephan 292 }
318a0ac… stephan 293
318a0ac… stephan 294 #endif /* FOSSIL_ENABLE_JSON */

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button