/* ** Implement the "fossil bundle append BUNDLE FILE..." command. Add ** the named files into the BUNDLE. Create the BUNDLE if it does not ** alraedy exist. */ static void bundle_append_cmd(void){ Blob content, hash; int i; Stmt q; verify_all_options(); bundle_attach_file(g.argv[3], "b1", 1); db_prepare(&q, "INSERT INTO bblob(blobid, uuid, sz, delta, data, notes) " "VALUES(NULL, $uuid, $sz, NULL, $data, $filename)"); db_begin_transaction(); for(i=4; i<g.argc; i++){ int sz; blob_read_from_file(&content, g.argv[i]); sz = blob_size(&content); sha1sum_blob(&content, &hash); blob_compress(&content, &content); db_bind_text(&q, "$uuid", blob_str(&hash)); db_bind_int(&q, "$sz", sz); db_bind_blob(&q, "$data", &content); db_bind_text(&q, "$filename", g.argv[i]); db_step(&q); db_reset(&q); blob_reset(&content); blob_reset(&hash); } db_end_transaction(0); db_finalize(&q); }
/* Find or create a record the value of a column and return the id */ DBint64 id_of_with_insert(DB *db, const char *table, const char *column, const char *value, int *inserted) { DBStatement *select, *insert; DBint64 id; char sql[256]; snprintf(sql, sizeof(sql), "select id from %s where %s = ?;", table, column); select = db_prep(db, sql); db_bind_text(select, 1, value); if (db_step(select) == DB_DONE) { snprintf(sql, sizeof(sql), "insert into %s (%s) values (?);", table, column); insert = db_prep(db, sql); db_bind_text(insert, 1, value); db_step(insert); id = db_last_insert_rowid(db); db_finalize(insert); *inserted = TRUE; } else { id = db_column_int64(select, 0); *inserted = FALSE; } db_finalize(select); return id; }
void registry_set_text(Logbook *logbook, const char *path, const char *key, const char *text) { DBStatement *stmt; if (registry_key_exists(logbook, path, key)) { stmt = logbook->registry_update; } else { stmt = logbook->registry_insert; } db_bind_text(stmt, 1, text); db_bind_text(stmt, 2, path); db_bind_text(stmt, 3, key); db_stp_res_clr(stmt); }
int registry_key_exists(Logbook *logbook, const char *path, const char *key) { int value = 1; DBStatement *stmt = logbook->registry_select_by_path_key; db_bind_text(stmt, 1, path); db_bind_text(stmt, 2, key); if (db_step(stmt) == DB_DONE) { value = 0; } db_reset(stmt); db_clear_bindings(stmt); return value; }
int registry_get_int(Logbook *logbook, const char *path, const char *key) { int value = 0; DBStatement *stmt = logbook->registry_select_by_path_key; db_bind_text(stmt, 1, path); db_bind_text(stmt, 2, key); if (db_step(stmt) == DB_ROW) { value = db_column_int(stmt, 0); } db_reset(stmt); db_clear_bindings(stmt); return value; }
/* ** Load a vfile from a record ID. */ void load_vfile_from_rid(int vid){ int rid, size; Stmt ins, ridq; Manifest *p; ManifestFile *pFile; if( db_exists("SELECT 1 FROM vfile WHERE vid=%d", vid) ){ return; } db_begin_transaction(); p = manifest_get(vid, CFTYPE_MANIFEST, 0); if( p==0 ) { db_end_transaction(1); return; } db_prepare(&ins, "INSERT INTO vfile(vid,isexe,islink,rid,mrid,pathname) " " VALUES(:vid,:isexe,:islink,:id,:id,:name)"); db_prepare(&ridq, "SELECT rid,size FROM blob WHERE uuid=:uuid"); db_bind_int(&ins, ":vid", vid); manifest_file_rewind(p); while( (pFile = manifest_file_next(p,0))!=0 ){ if( pFile->zUuid==0 || uuid_is_shunned(pFile->zUuid) ) continue; db_bind_text(&ridq, ":uuid", pFile->zUuid); if( db_step(&ridq)==SQLITE_ROW ){ rid = db_column_int(&ridq, 0); size = db_column_int(&ridq, 1); }else{ rid = 0; size = 0; } db_reset(&ridq); if( rid==0 || size<0 ){ fossil_warning("content missing for %s", pFile->zName); continue; } db_bind_int(&ins, ":isexe", ( manifest_file_mperm(pFile)==PERM_EXE )); db_bind_int(&ins, ":id", rid); db_bind_text(&ins, ":name", pFile->zName); db_bind_int(&ins, ":islink", ( manifest_file_mperm(pFile)==PERM_LNK )); db_step(&ins); db_reset(&ins); } db_finalize(&ridq); db_finalize(&ins); manifest_destroy(p); db_end_transaction(0); }
char *registry_get_text(Logbook *logbook, const char *path, const char *key) { char *value = NULL; DBStatement *stmt = logbook->registry_select_by_path_key; db_bind_text(stmt, 1, path); db_bind_text(stmt, 2, key); if (db_step(stmt) == DB_ROW) { strncpy(logbook->registry_value, (char*)db_column_text(stmt, 0), REGISTRY_BUF_VALUE); logbook->registry_value[REGISTRY_BUF_VALUE-1] = '\0'; value = logbook->registry_value; } db_reset(stmt); db_clear_bindings(stmt); return value; }
/* ** Create a new phantom with the given UUID and return its artifact ID. */ int content_new(const char *zUuid, int isPrivate){ int rid; static Stmt s1, s2, s3; assert( g.repositoryOpen ); db_begin_transaction(); if( uuid_is_shunned(zUuid) ){ db_end_transaction(0); return 0; } db_static_prepare(&s1, "INSERT INTO blob(rcvid,size,uuid,content)" "VALUES(0,-1,:uuid,NULL)" ); db_bind_text(&s1, ":uuid", zUuid); db_exec(&s1); rid = db_last_insert_rowid(); db_static_prepare(&s2, "INSERT INTO phantom VALUES(:rid)" ); db_bind_int(&s2, ":rid", rid); db_exec(&s2); if( g.markPrivate || isPrivate ){ db_multi_exec("INSERT INTO private VALUES(%d)", rid); }else{ db_static_prepare(&s3, "INSERT INTO unclustered VALUES(:rid)" ); db_bind_int(&s3, ":rid", rid); db_exec(&s3); } bag_insert(&contentCache.missing, rid); db_end_transaction(0); return rid; }
/* This is a helper routine for test-artifacts. ** ** Check to see that artifact zUuid exists in the repository. If it does, ** return 0. If it does not, generate an error message and return 1. */ static int check_exists( const char *zUuid, /* The artifact we are checking for */ unsigned flags, /* Flags */ Manifest *p, /* The control artifact that references zUuid */ const char *zRole, /* Role of zUuid in p */ const char *zDetail /* Additional information, such as a filename */ ){ static Stmt q; int rc = 0; db_static_prepare(&q, "SELECT size FROM blob WHERE uuid=:uuid"); if( zUuid==0 || zUuid[0]==0 ) return 0; db_bind_text(&q, ":uuid", zUuid); if( db_step(&q)==SQLITE_ROW ){ int size = db_column_int(&q, 0); if( size<0 ) rc = 2; }else{ rc = 1; } db_reset(&q); if( rc ){ const char *zCFType = "control artifact"; char *zSrc; char *zDate; char *zErrType = "MISSING"; if( db_exists("SELECT 1 FROM shun WHERE uuid=%Q", zUuid) ){ if( flags & MISSING_SHUNNED ) return 0; zErrType = "SHUNNED"; } switch( p->type ){ case CFTYPE_MANIFEST: zCFType = "check-in"; break; case CFTYPE_CLUSTER: zCFType = "cluster"; break; case CFTYPE_CONTROL: zCFType = "tag"; break; case CFTYPE_WIKI: zCFType = "wiki"; break; case CFTYPE_TICKET: zCFType = "ticket"; break; case CFTYPE_ATTACHMENT: zCFType = "attachment"; break; case CFTYPE_EVENT: zCFType = "event"; break; } zSrc = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", p->rid); if( p->rDate>0.0 ){ zDate = db_text(0, "SELECT datetime(%.17g)", p->rDate); }else{ zDate = db_text(0, "SELECT datetime(rcvfrom.mtime)" " FROM blob, rcvfrom" " WHERE blob.rcvid=rcvfrom.rcvid" " AND blob.rid=%d", p->rid); } fossil_print("%s: %s\n %s %s %S (%d) %s\n", zErrType, zUuid, zRole, zCFType, zSrc, p->rid, zDate); if( zDetail && zDetail[0] ){ fossil_print(" %s\n", zDetail); } fossil_free(zSrc); fossil_free(zDate); rc = 1; } return rc; }
/* ** The input is guaranteed to be a 40-character well-formed UUID. ** Find its rid. */ int fast_uuid_to_rid(const char *zUuid){ static Stmt q; int rid; db_static_prepare(&q, "SELECT rid FROM blob WHERE uuid=:uuid"); db_bind_text(&q, ":uuid", zUuid); if( db_step(&q)==SQLITE_ROW ){ rid = db_column_int(&q, 0); }else{ rid = 0; } db_reset(&q); return rid; }
DBint64 roles_write_entries(const gchar *id, Logbook *logbook) { const gchar *ident, *name; gboolean pic, sic, fe, solo, dual, instruct, total; DBStatement *stmt; ident = gtk_entry_get_text(GTK_ENTRY(logbook->roles_ident)); name = gtk_entry_get_text(GTK_ENTRY(logbook->roles_name)); pic = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(logbook->roles_pic)); sic = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(logbook->roles_sic)); fe = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(logbook->roles_fe)); solo = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(logbook->roles_solo)); dual = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(logbook->roles_dual)); instruct = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(logbook->roles_instruct)); total = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(logbook->roles_total)); /* Write entries to database */ if (id) { stmt = logbook->roles_update; db_bind_text(stmt, ROLES_WRITE_ID, id); } else { stmt = logbook->roles_insert; } db_bind_nonempty_text_else_null(stmt, ROLES_WRITE_IDENT, ident); db_bind_nonempty_text_else_null(stmt, ROLES_WRITE_NAME, name); db_bind_int(stmt, ROLES_WRITE_PIC, pic ? 1 : 0); db_bind_int(stmt, ROLES_WRITE_SIC, sic ? 1 : 0); db_bind_int(stmt, ROLES_WRITE_FE, fe ? 1 : 0); db_bind_int(stmt, ROLES_WRITE_SOLO, solo ? 1 : 0); db_bind_int(stmt, ROLES_WRITE_DUAL, dual ? 1 : 0); db_bind_int(stmt, ROLES_WRITE_INSTRUCT, instruct ? 1 : 0); db_bind_int(stmt, ROLES_WRITE_TOTAL, total ? 1 : 0); db_stp_res_clr(stmt); if (id) { return 0; } else { return db_last_insert_rowid(logbook->db); } }
int unique_but_for(DB *db, const char *table, const char *col, const char *value, const char *butcol, const char *butvalue) { DBStatement *stmt; int ret; char sql[512]; /* Might want to use 'like' query for case insensitivity */ snprintf(sql, sizeof(sql), "select * from %s where %s != ? and %s = ?;", table, butcol, col); stmt = db_prep(db, sql); db_bind_text(stmt, 1, butvalue); db_bind_text(stmt, 2, value); if (db_step(stmt) == DB_DONE) { ret = 1; } else { ret = 0; } db_finalize(stmt); return ret; }
int row_exists(DB *db, const char *table, const char *column, const char *value) { DBStatement *select; char sql[256]; int ret; snprintf(sql, sizeof(sql), "select %s from %s where %s = ?;", column, table, column); select = db_prep(db, sql); db_bind_text(select, 1, value); ret = (db_step(select) != DB_DONE); db_finalize(select); return ret; }
/* Returns 1 if ident found, otherwise 0 - places utc as default value */ int tz_of_airport(DBStatement *select, const char *key, char *tz, int tz_bufsize) { int found; db_bind_text(select, 1, key); found = (db_step(select) == DB_ROW); if (found) { tz[tz_bufsize - 1] = 0; strncpy(tz, EMPTY_IF_NULL((char *)db_column_text(select, 0)), tz_bufsize); if (tz[tz_bufsize - 1] != 0) { fprintf(stderr, "Timezone buffer passed to tz_of_airport() too small\n"); exit(1); } } db_stp_res_clr(select); return found; }
int find_row_id(DB *db, const char *table, const char *column, const char *value, DBint64 *r_id) { DBStatement *select; char sql[256]; snprintf(sql, sizeof(sql), "select id from %s where %s = ?;", table, column); select = db_prep(db, sql); db_bind_text(select, 1, value); if (db_step(select) == DB_DONE) { /* No row found */ return 0; } else { *r_id = db_column_int64(select, 0); } db_finalize(select); return 1; }
void on_reports_title_changed(GtkWidget *entry, Logbook *logbook) { if (GTK_WIDGET_HAS_FOCUS(entry)) { reports_set_modified(logbook); gtk_expander_set_expanded(GTK_EXPANDER(logbook->reports_sql_expander), TRUE); } else { db_bind_text(logbook->reports_sql_by_title, 1, gtk_entry_get_text(GTK_ENTRY(logbook->reports_title))); db_step(logbook->reports_sql_by_title); fprintf(stderr, "Error's from here ...\n"); text_view_set_text(GTK_TEXT_VIEW(logbook->reports_sql_text), (char*)db_column_text(logbook->reports_sql_by_title, 0)); fprintf(stderr, "... to here during delete.\n"); db_reset(logbook->reports_sql_by_title); db_clear_bindings(logbook->reports_sql_by_title); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(logbook->reports_armdel_btn), FALSE); gtk_widget_set_sensitive(logbook->reports_armdel_btn, TRUE); gtk_widget_set_sensitive(logbook->reports_del_btn, FALSE); gtk_widget_set_sensitive(logbook->reports_save_btn, FALSE); } }
/* ** Insert an artifact into the BLOB table if it isn't there already. ** If zMark is not zero, create a cross-reference from that mark back ** to the newly inserted artifact. ** ** If saveUuid is true, then pContent is a commit record. Record its ** UUID in gg.zPrevCheckin. */ static int fast_insert_content(Blob *pContent, const char *zMark, int saveUuid){ Blob hash; Blob cmpr; int rid; sha1sum_blob(pContent, &hash); rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &hash); if( rid==0 ){ static Stmt ins; db_static_prepare(&ins, "INSERT INTO blob(uuid, size, content) VALUES(:uuid, :size, :content)" ); db_bind_text(&ins, ":uuid", blob_str(&hash)); db_bind_int(&ins, ":size", gg.nData); blob_compress(pContent, &cmpr); db_bind_blob(&ins, ":content", &cmpr); db_step(&ins); db_reset(&ins); blob_reset(&cmpr); rid = db_last_insert_rowid(); } if( zMark ){ db_multi_exec( "INSERT OR IGNORE INTO xmark(tname, trid, tuuid)" "VALUES(%Q,%d,%B)", zMark, rid, &hash ); db_multi_exec( "INSERT OR IGNORE INTO xmark(tname, trid, tuuid)" "VALUES(%B,%d,%B)", &hash, rid, &hash ); } if( saveUuid ){ fossil_free(gg.zPrevCheckin); gg.zPrevCheckin = fossil_strdup(blob_str(&hash)); } blob_reset(&hash); return rid; }
/* ** Load into table SFILE the name of every ordinary file in ** the directory pPath. Omit the first nPrefix characters of ** of pPath when inserting into the SFILE table. ** ** Subdirectories are scanned recursively. ** Omit files named in VFILE. ** ** Files whose names begin with "." are omitted unless the SCAN_ALL ** flag is set. ** ** Any files or directories that match the glob patterns pIgnore* ** are excluded from the scan. Name matching occurs after the ** first nPrefix characters are elided from the filename. */ void vfile_scan( Blob *pPath, /* Directory to be scanned */ int nPrefix, /* Number of bytes in directory name */ unsigned scanFlags, /* Zero or more SCAN_xxx flags */ Glob *pIgnore1, /* Do not add files that match this GLOB */ Glob *pIgnore2 /* Omit files matching this GLOB too */ ){ DIR *d; int origSize; struct dirent *pEntry; int skipAll = 0; static Stmt ins; static int depth = 0; void *zNative; origSize = blob_size(pPath); if( pIgnore1 || pIgnore2 ){ blob_appendf(pPath, "/"); if( glob_match(pIgnore1, &blob_str(pPath)[nPrefix+1]) ) skipAll = 1; if( glob_match(pIgnore2, &blob_str(pPath)[nPrefix+1]) ) skipAll = 1; blob_resize(pPath, origSize); } if( skipAll ) return; if( depth==0 ){ db_prepare(&ins, "INSERT OR IGNORE INTO sfile(x) SELECT :file" " WHERE NOT EXISTS(SELECT 1 FROM vfile WHERE" " pathname=:file %s)", filename_collation() ); } depth++; zNative = fossil_utf8_to_filename(blob_str(pPath)); d = opendir(zNative); if( d ){ while( (pEntry=readdir(d))!=0 ){ char *zPath; char *zUtf8; if( pEntry->d_name[0]=='.' ){ if( (scanFlags & SCAN_ALL)==0 ) continue; if( pEntry->d_name[1]==0 ) continue; if( pEntry->d_name[1]=='.' && pEntry->d_name[2]==0 ) continue; } zUtf8 = fossil_filename_to_utf8(pEntry->d_name); blob_appendf(pPath, "/%s", zUtf8); zPath = blob_str(pPath); if( glob_match(pIgnore1, &zPath[nPrefix+1]) || glob_match(pIgnore2, &zPath[nPrefix+1]) ){ /* do nothing */ }else if( file_wd_isdir(zPath)==1 ){ if( !vfile_top_of_checkout(zPath) ){ vfile_scan(pPath, nPrefix, scanFlags, pIgnore1, pIgnore2); } }else if( file_wd_isfile_or_link(zPath) ){ if( (scanFlags & SCAN_TEMP)==0 || is_temporary_file(zUtf8) ){ db_bind_text(&ins, ":file", &zPath[nPrefix+1]); db_step(&ins); db_reset(&ins); } } fossil_filename_free(zUtf8); blob_resize(pPath, origSize); } closedir(d); } fossil_filename_free(zNative); depth--; if( depth==0 ){ db_finalize(&ins); } }
/* ** Impl of /json/dir. 98% of it was taken directly ** from browse.c::page_dir() */ static cson_value * json_page_dir_list(){ cson_object * zPayload = NULL; /* return value */ cson_array * zEntries = NULL; /* accumulated list of entries. */ cson_object * zEntry = NULL; /* a single dir/file entry. */ cson_array * keyStore = NULL; /* garbage collector for shared strings. */ cson_string * zKeyName = NULL; cson_string * zKeySize = NULL; cson_string * zKeyIsDir = NULL; cson_string * zKeyUuid = NULL; cson_string * zKeyTime = NULL; cson_string * zKeyRaw = NULL; char * zD = NULL; char const * zDX = NULL; int nD; char * zUuid = NULL; char const * zCI = NULL; Manifest * pM = NULL; Stmt q = empty_Stmt; int rid = 0; if( !g.perm.Read ){ json_set_err(FSL_JSON_E_DENIED, "Requires 'o' permissions."); return NULL; } zCI = json_find_option_cstr("checkin",NULL,"ci" ); /* If a specific check-in is requested, fetch and parse it. If the ** specific check-in does not exist, clear zCI. zCI==0 will cause all ** files from all check-ins to be displayed. */ if( zCI && *zCI ){ pM = manifest_get_by_name(zCI, &rid); if( pM ){ zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); }else{ json_set_err(FSL_JSON_E_UNRESOLVED_UUID, "Checkin name [%s] is unresolved.", zCI); return NULL; } } /* Jump through some hoops to find the directory name... */ zDX = json_find_option_cstr("name",NULL,NULL); if(!zDX && !g.isHTTP){ zDX = json_command_arg(g.json.dispatchDepth+1); } if(zDX && (!*zDX || (0==strcmp(zDX,"/")))){ zDX = NULL; } zD = zDX ? fossil_strdup(zDX) : NULL; nD = zD ? strlen(zD)+1 : 0; while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; } sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0, pathelementFunc, 0, 0); /* Compute the temporary table "localfiles" containing the names ** of all files and subdirectories in the zD[] directory. ** ** Subdirectory names begin with "/". This causes them to sort ** first and it also gives us an easy way to distinguish files ** from directories in the loop that follows. */ if( zCI ){ Stmt ins; ManifestFile *pFile; ManifestFile *pPrev = 0; int nPrev = 0; int c; db_multi_exec( "CREATE TEMP TABLE json_dir_files(" " n UNIQUE NOT NULL," /* file name */ " fn UNIQUE NOT NULL," /* full file name */ " u DEFAULT NULL," /* file uuid */ " sz DEFAULT -1," /* file size */ " mtime DEFAULT NULL" /* file mtime in unix epoch format */ ");" ); db_prepare(&ins, "INSERT OR IGNORE INTO json_dir_files (n,fn,u,sz,mtime) " "SELECT" " pathelement(:path,0)," " CASE WHEN %Q IS NULL THEN '' ELSE %Q||'/' END ||:abspath," " a.uuid," " a.size," " CAST(strftime('%%s',e.mtime) AS INTEGER) " "FROM" " mlink m, " " event e," " blob a," " blob b " "WHERE" " e.objid=m.mid" " AND a.rid=m.fid"/*FILE artifact*/ " AND b.rid=m.mid"/*CHECKIN artifact*/ " AND a.uuid=:uuid", zD, zD ); manifest_file_rewind(pM); while( (pFile = manifest_file_next(pM,0))!=0 ){ if( nD>0 && ((pFile->zName[nD-1]!='/') || (0!=memcmp(pFile->zName, zD, nD-1))) ){ continue; } /*printf("zD=%s, nD=%d, pFile->zName=%s\n", zD, nD, pFile->zName);*/ if( pPrev && memcmp(&pFile->zName[nD],&pPrev->zName[nD],nPrev)==0 && (pFile->zName[nD+nPrev]==0 || pFile->zName[nD+nPrev]=='/') ){ continue; } db_bind_text( &ins, ":path", &pFile->zName[nD] ); db_bind_text( &ins, ":abspath", &pFile->zName[nD] ); db_bind_text( &ins, ":uuid", pFile->zUuid ); db_step(&ins); db_reset(&ins); pPrev = pFile; for(nPrev=0; (c=pPrev->zName[nD+nPrev]) && c!='/'; nPrev++){} if( c=='/' ) nPrev++; } db_finalize(&ins); }else if( zD && *zD ){ db_multi_exec( "CREATE TEMP VIEW json_dir_files AS" " SELECT DISTINCT(pathelement(name,%d)) AS n," " %Q||'/'||name AS fn," " NULL AS u, NULL AS sz, NULL AS mtime" " FROM filename" " WHERE name GLOB '%q/*'" " GROUP BY n", nD, zD, zD ); }else{ db_multi_exec( "CREATE TEMP VIEW json_dir_files" " AS SELECT DISTINCT(pathelement(name,0)) AS n, NULL AS fn" " FROM filename" ); } if(zCI){ db_prepare( &q, "SELECT" " n as name," " fn as fullname," " u as uuid," " sz as size," " mtime as mtime " "FROM json_dir_files ORDER BY n"); }else{/* UUIDs are all NULL. */ db_prepare( &q, "SELECT n, fn FROM json_dir_files ORDER BY n"); } zKeyName = cson_new_string("name",4); zKeyUuid = cson_new_string("uuid",4); zKeyIsDir = cson_new_string("isDir",5); keyStore = cson_new_array(); cson_array_append( keyStore, cson_string_value(zKeyName) ); cson_array_append( keyStore, cson_string_value(zKeyUuid) ); cson_array_append( keyStore, cson_string_value(zKeyIsDir) ); if( zCI ){ zKeySize = cson_new_string("size",4); cson_array_append( keyStore, cson_string_value(zKeySize) ); zKeyTime = cson_new_string("timestamp",9); cson_array_append( keyStore, cson_string_value(zKeyTime) ); zKeyRaw = cson_new_string("downloadPath",12); cson_array_append( keyStore, cson_string_value(zKeyRaw) ); } zPayload = cson_new_object(); cson_object_set_s( zPayload, zKeyName, json_new_string((zD&&*zD) ? zD : "/") ); if( zUuid ){ cson_object_set( zPayload, "checkin", json_new_string(zUuid) ); } while( (SQLITE_ROW==db_step(&q)) ){ cson_value * name = NULL; char const * n = db_column_text(&q,0); char const isDir = ('/'==*n); zEntry = cson_new_object(); if(!zEntries){ zEntries = cson_new_array(); cson_object_set( zPayload, "entries", cson_array_value(zEntries) ); } cson_array_append(zEntries, cson_object_value(zEntry) ); if(isDir){ name = json_new_string( n+1 ); cson_object_set_s(zEntry, zKeyIsDir, cson_value_true() ); } else{ name = json_new_string( n ); } cson_object_set_s(zEntry, zKeyName, name ); if( zCI && !isDir){ /* Don't add the uuid/size for dir entries - that data refers to one of the files in that directory :/. Entries with no --checkin may refer to N versions, and therefore we cannot associate a single size and uuid with them (and fetching all would be overkill for most use cases). */ char const * fullName = db_column_text(&q,1); char const * u = db_column_text(&q,2); sqlite_int64 const sz = db_column_int64(&q,3); sqlite_int64 const ts = db_column_int64(&q,4); cson_object_set_s(zEntry, zKeyUuid, json_new_string( u ) ); cson_object_set_s(zEntry, zKeySize, cson_value_new_integer( (cson_int_t)sz )); cson_object_set_s(zEntry, zKeyTime, cson_value_new_integer( (cson_int_t)ts )); cson_object_set_s(zEntry, zKeyRaw, json_new_string_f("/raw/%T?name=%t", fullName, u)); } } db_finalize(&q); if(pM){ manifest_destroy(pM); } cson_free_array( keyStore ); free( zUuid ); free( zD ); return cson_object_value(zPayload); }
/* ** Check to see if the directory named in zPath is the top of a checkout. ** In other words, check to see if directory pPath contains a file named ** "_vcs_" or ".fslckout". Return true or false. */ int vfile_top_of_checkout(const char *zPath){ char *zFile; int fileFound = 0; zFile = mprintf("%s/_vcs_", zPath); fileFound = file_size(zFile)>=1024; vcs_free(zFile); if( !fileFound ){ zFile = mprintf("%s/.fslckout", zPath); fileFound = file_size(zFile)>=1024; vcs_free(zFile); } /* ** Load into table SFILE the name of every ordinary file in ** the directory pPath. Omit the first nPrefix characters of ** of pPath when inserting into the SFILE table. ** ** Subdirectories are scanned recursively. ** Omit files named in VFILE. ** ** Files whose names begin with "." are omitted unless allFlag is true. ** ** Any files or directories that match the glob pattern pIgnore are ** excluded from the scan. Name matching occurs after the first ** nPrefix characters are elided from the filename. */ void vfile_scan(Blob *pPath, int nPrefix, int allFlag, Glob *pIgnore){ DIR *d; int origSize; const char *zDir; struct dirent *pEntry; int skipAll = 0; static Stmt ins; static int depth = 0; char *zMbcs; origSize = blob_size(pPath); if( pIgnore ){ blob_appendf(pPath, "/"); if( glob_match(pIgnore, &blob_str(pPath)[nPrefix+1]) ) skipAll = 1; blob_resize(pPath, origSize); } if( skipAll ) return; if( depth==0 ){ db_prepare(&ins, "INSERT OR IGNORE INTO sfile(x) SELECT :file" " WHERE NOT EXISTS(SELECT 1 FROM vfile WHERE pathname=:file)" ); } depth++; zDir = blob_str(pPath); zMbcs = vcs_utf8_to_mbcs(zDir); d = opendir(zMbcs); if( d ){ while( (pEntry=readdir(d))!=0 ){ char *zPath; char *zUtf8; if( pEntry->d_name[0]=='.' ){ if( !allFlag ) continue; if( pEntry->d_name[1]==0 ) continue; if( pEntry->d_name[1]=='.' && pEntry->d_name[2]==0 ) continue; } zUtf8 = vcs_mbcs_to_utf8(pEntry->d_name); blob_appendf(pPath, "/%s", zUtf8); vcs_mbcs_free(zUtf8); zPath = blob_str(pPath); if( glob_match(pIgnore, &zPath[nPrefix+1]) ){ /* do nothing */ }else if( file_wd_isdir(zPath)==1 ){ if( !vfile_top_of_checkout(zPath) ){ vfile_scan(pPath, nPrefix, allFlag, pIgnore); } }else if( file_wd_isfile_or_link(zPath) ){ db_bind_text(&ins, ":file", &zPath[nPrefix+1]); db_step(&ins); db_reset(&ins); } blob_resize(pPath, origSize); } closedir(d); } vcs_mbcs_free(zMbcs); depth--; if( depth==0 ){ db_finalize(&ins); } }
/* ** COMMAND: export ** ** Usage: %fossil export --git ?OPTIONS? ?REPOSITORY? ** ** Write an export of all check-ins to standard output. The export is ** written in the git-fast-export file format assuming the --git option is ** provided. The git-fast-export format is currently the only VCS ** interchange format supported, though other formats may be added in ** the future. ** ** Run this command within a checkout. Or use the -R or --repository ** option to specify a Fossil repository to be exported. ** ** Only check-ins are exported using --git. Git does not support tickets ** or wiki or events or attachments, so none of those are exported. ** ** If the "--import-marks FILE" option is used, it contains a list of ** rids to skip. ** ** If the "--export-marks FILE" option is used, the rid of all commits and ** blobs written on exit for use with "--import-marks" on the next run. ** ** Options: ** --export-marks FILE export rids of exported data to FILE ** --import-marks FILE read rids of data to ignore from FILE ** --repository|-R REPOSITORY export the given REPOSITORY ** ** See also: import */ void export_cmd(void){ Stmt q, q2, q3; int i; Bag blobs, vers; const char *markfile_in; const char *markfile_out; bag_init(&blobs); bag_init(&vers); find_option("git", 0, 0); /* Ignore the --git option for now */ markfile_in = find_option("import-marks", 0, 1); markfile_out = find_option("export-marks", 0, 1); db_find_and_open_repository(0, 2); verify_all_options(); if( g.argc!=2 && g.argc!=3 ){ usage("--git ?REPOSITORY?"); } db_multi_exec("CREATE TEMPORARY TABLE oldblob(rid INTEGER PRIMARY KEY)"); db_multi_exec("CREATE TEMPORARY TABLE oldcommit(rid INTEGER PRIMARY KEY)"); if( markfile_in!=0 ){ Stmt qb,qc; char line[100]; FILE *f; f = fossil_fopen(markfile_in, "r"); if( f==0 ){ fossil_fatal("cannot open %s for reading", markfile_in); } db_prepare(&qb, "INSERT OR IGNORE INTO oldblob VALUES (:rid)"); db_prepare(&qc, "INSERT OR IGNORE INTO oldcommit VALUES (:rid)"); while( fgets(line, sizeof(line), f)!=0 ){ if( *line == 'b' ){ db_bind_text(&qb, ":rid", line + 1); db_step(&qb); db_reset(&qb); bag_insert(&blobs, atoi(line + 1)); }else if( *line == 'c' ){ db_bind_text(&qc, ":rid", line + 1); db_step(&qc); db_reset(&qc); bag_insert(&vers, atoi(line + 1)); }else{ fossil_fatal("bad input from %s: %s", markfile_in, line); } } db_finalize(&qb); db_finalize(&qc); fclose(f); } /* Step 1: Generate "blob" records for every artifact that is part ** of a check-in */ fossil_binary_mode(stdout); db_multi_exec("CREATE TEMP TABLE newblob(rid INTEGER KEY, srcid INTEGER)"); db_multi_exec("CREATE INDEX newblob_src ON newblob(srcid)"); db_multi_exec( "INSERT INTO newblob" " SELECT DISTINCT fid," " CASE WHEN EXISTS(SELECT 1 FROM delta" " WHERE rid=fid" " AND NOT EXISTS(SELECT 1 FROM oldblob" " WHERE srcid=fid))" " THEN (SELECT srcid FROM delta WHERE rid=fid)" " ELSE 0" " END" " FROM mlink" " WHERE fid>0 AND NOT EXISTS(SELECT 1 FROM oldblob WHERE rid=fid)"); db_prepare(&q, "SELECT DISTINCT fid FROM mlink" " WHERE fid>0 AND NOT EXISTS(SELECT 1 FROM oldblob WHERE rid=fid)"); db_prepare(&q2, "INSERT INTO oldblob VALUES (:rid)"); db_prepare(&q3, "SELECT rid FROM newblob WHERE srcid= (:srcid)"); while( db_step(&q)==SQLITE_ROW ){ int rid = db_column_int(&q, 0); Blob content; while( !bag_find(&blobs, rid) ){ content_get(rid, &content); db_bind_int(&q2, ":rid", rid); db_step(&q2); db_reset(&q2); printf("blob\nmark :%d\ndata %d\n", BLOBMARK(rid), blob_size(&content)); bag_insert(&blobs, rid); fwrite(blob_buffer(&content), 1, blob_size(&content), stdout); printf("\n"); blob_reset(&content); db_bind_int(&q3, ":srcid", rid); if( db_step(&q3) != SQLITE_ROW ){ db_reset(&q3); break; } rid = db_column_int(&q3, 0); db_reset(&q3); } } db_finalize(&q); db_finalize(&q2); db_finalize(&q3); /* Output the commit records. */ db_prepare(&q, "SELECT strftime('%%s',mtime), objid, coalesce(comment,ecomment)," " coalesce(user,euser)," " (SELECT value FROM tagxref WHERE rid=objid AND tagid=%d)" " FROM event" " WHERE type='ci' AND NOT EXISTS (SELECT 1 FROM oldcommit WHERE objid=rid)" " ORDER BY mtime ASC", TAG_BRANCH ); db_prepare(&q2, "INSERT INTO oldcommit VALUES (:rid)"); while( db_step(&q)==SQLITE_ROW ){ Stmt q4; const char *zSecondsSince1970 = db_column_text(&q, 0); int ckinId = db_column_int(&q, 1); const char *zComment = db_column_text(&q, 2); const char *zUser = db_column_text(&q, 3); const char *zBranch = db_column_text(&q, 4); char *zBr; bag_insert(&vers, ckinId); db_bind_int(&q2, ":rid", ckinId); db_step(&q2); db_reset(&q2); if( zBranch==0 ) zBranch = "trunk"; zBr = mprintf("%s", zBranch); for(i=0; zBr[i]; i++){ if( !fossil_isalnum(zBr[i]) ) zBr[i] = '_'; } printf("commit refs/heads/%s\nmark :%d\n", zBr, COMMITMARK(ckinId)); free(zBr); printf("committer"); print_person(zUser); printf(" %s +0000\n", zSecondsSince1970); if( zComment==0 ) zComment = "null comment"; printf("data %d\n%s\n", (int)strlen(zComment), zComment); db_prepare(&q3, "SELECT pid FROM plink" " WHERE cid=%d AND isprim" " AND pid IN (SELECT objid FROM event)", ckinId ); if( db_step(&q3) == SQLITE_ROW ){ printf("from :%d\n", COMMITMARK(db_column_int(&q3, 0))); db_prepare(&q4, "SELECT pid FROM plink" " WHERE cid=%d AND NOT isprim" " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)" " ORDER BY pid", ckinId); while( db_step(&q4)==SQLITE_ROW ){ printf("merge :%d\n", COMMITMARK(db_column_int(&q4,0))); } db_finalize(&q4); }else{ printf("deleteall\n"); } db_prepare(&q4, "SELECT filename.name, mlink.fid, mlink.mperm FROM mlink" " JOIN filename ON filename.fnid=mlink.fnid" " WHERE mlink.mid=%d", ckinId ); while( db_step(&q4)==SQLITE_ROW ){ const char *zName = db_column_text(&q4,0); int zNew = db_column_int(&q4,1); int mPerm = db_column_int(&q4,2); if( zNew==0) printf("D %s\n", zName); else if( bag_find(&blobs, zNew) ) { const char *zPerm; switch( mPerm ){ case PERM_LNK: zPerm = "120000"; break; case PERM_EXE: zPerm = "100755"; break; default: zPerm = "100644"; break; } printf("M %s :%d %s\n", zPerm, BLOBMARK(zNew), zName); } } db_finalize(&q4); db_finalize(&q3); printf("\n"); } db_finalize(&q2); db_finalize(&q); bag_clear(&blobs); manifest_cache_clear(); /* Output tags */ db_prepare(&q, "SELECT tagname, rid, strftime('%%s',mtime)" " FROM tagxref JOIN tag USING(tagid)" " WHERE tagtype=1 AND tagname GLOB 'sym-*'" ); while( db_step(&q)==SQLITE_ROW ){ const char *zTagname = db_column_text(&q, 0); char *zEncoded = 0; int rid = db_column_int(&q, 1); const char *zSecSince1970 = db_column_text(&q, 2); int i; if( rid==0 || !bag_find(&vers, rid) ) continue; zTagname += 4; zEncoded = mprintf("%s", zTagname); for(i=0; zEncoded[i]; i++){ if( !fossil_isalnum(zEncoded[i]) ) zEncoded[i] = '_'; } printf("tag %s\n", zEncoded); printf("from :%d\n", COMMITMARK(rid)); printf("tagger <tagger> %s +0000\n", zSecSince1970); printf("data 0\n"); fossil_free(zEncoded); } db_finalize(&q); bag_clear(&vers); if( markfile_out!=0 ){ FILE *f; f = fossil_fopen(markfile_out, "w"); if( f == 0 ){ fossil_fatal("cannot open %s for writing", markfile_out); } db_prepare(&q, "SELECT rid FROM oldblob"); while( db_step(&q)==SQLITE_ROW ){ fprintf(f, "b%d\n", db_column_int(&q, 0)); } db_finalize(&q); db_prepare(&q, "SELECT rid FROM oldcommit"); while( db_step(&q)==SQLITE_ROW ){ fprintf(f, "c%d\n", db_column_int(&q, 0)); } db_finalize(&q); if( ferror(f)!=0 || fclose(f)!=0 ) { fossil_fatal("error while writing %s", markfile_out); } } }
/* ** Send "config" cards using the new format for all elements of a group ** that have recently changed. ** ** Output goes into pOut. The groupMask identifies the group(s) to be sent. ** Send only entries whose timestamp is later than or equal to iStart. ** ** Return the number of cards sent. */ int configure_send_group( Blob *pOut, /* Write output here */ int groupMask, /* Mask of groups to be send */ sqlite3_int64 iStart /* Only write values changed since this time */ ) { Stmt q; Blob rec; int ii; int nCard = 0; blob_zero(&rec); if( groupMask & CONFIGSET_SHUN ) { db_prepare(&q, "SELECT mtime, quote(uuid), quote(scom) FROM shun" " WHERE mtime>=%lld", iStart); while( db_step(&q)==SQLITE_ROW ) { blob_appendf(&rec,"%s %s scom %s", db_column_text(&q, 0), db_column_text(&q, 1), db_column_text(&q, 2) ); blob_appendf(pOut, "config /shun %d\n%s\n", blob_size(&rec), blob_str(&rec)); nCard++; blob_reset(&rec); } db_finalize(&q); } if( groupMask & CONFIGSET_USER ) { db_prepare(&q, "SELECT mtime, quote(login), quote(pw), quote(cap)," " quote(info), quote(photo) FROM user" " WHERE mtime>=%lld", iStart); while( db_step(&q)==SQLITE_ROW ) { blob_appendf(&rec,"%s %s pw %s cap %s info %s photo %s", db_column_text(&q, 0), db_column_text(&q, 1), db_column_text(&q, 2), db_column_text(&q, 3), db_column_text(&q, 4), db_column_text(&q, 5) ); blob_appendf(pOut, "config /user %d\n%s\n", blob_size(&rec), blob_str(&rec)); nCard++; blob_reset(&rec); } db_finalize(&q); } if( groupMask & CONFIGSET_TKT ) { db_prepare(&q, "SELECT mtime, quote(title), quote(owner), quote(cols)," " quote(sqlcode) FROM reportfmt" " WHERE mtime>=%lld", iStart); while( db_step(&q)==SQLITE_ROW ) { blob_appendf(&rec,"%s %s owner %s cols %s sqlcode %s", db_column_text(&q, 0), db_column_text(&q, 1), db_column_text(&q, 2), db_column_text(&q, 3), db_column_text(&q, 4) ); blob_appendf(pOut, "config /reportfmt %d\n%s\n", blob_size(&rec), blob_str(&rec)); nCard++; blob_reset(&rec); } db_finalize(&q); } if( groupMask & CONFIGSET_ADDR ) { db_prepare(&q, "SELECT mtime, quote(hash), quote(content) FROM concealed" " WHERE mtime>=%lld", iStart); while( db_step(&q)==SQLITE_ROW ) { blob_appendf(&rec,"%s %s content %s", db_column_text(&q, 0), db_column_text(&q, 1), db_column_text(&q, 2) ); blob_appendf(pOut, "config /concealed %d\n%s\n", blob_size(&rec), blob_str(&rec)); nCard++; blob_reset(&rec); } db_finalize(&q); } db_prepare(&q, "SELECT mtime, quote(name), quote(value) FROM config" " WHERE name=:name AND mtime>=%lld", iStart); for(ii=0; ii<count(aConfig); ii++) { if( (aConfig[ii].groupMask & groupMask)!=0 && aConfig[ii].zName[0]!='@' ) { db_bind_text(&q, ":name", aConfig[ii].zName); while( db_step(&q)==SQLITE_ROW ) { blob_appendf(&rec,"%s %s value %s", db_column_text(&q, 0), db_column_text(&q, 1), db_column_text(&q, 2) ); blob_appendf(pOut, "config /config %d\n%s\n", blob_size(&rec), blob_str(&rec)); nCard++; blob_reset(&rec); } db_reset(&q); } } db_finalize(&q); return nCard; }
/* ** Output a "committer" record for the given user. */ static void print_person(const char *zUser){ static Stmt q; const char *zContact; char *zName; char *zEmail; int i, j; if( zUser==0 ){ printf(" <unknown>"); return; } db_static_prepare(&q, "SELECT info FROM user WHERE login=:user"); db_bind_text(&q, ":user", zUser); if( db_step(&q)!=SQLITE_ROW ){ db_reset(&q); for(i=0; zUser[i] && zUser[i]!='>' && zUser[i]!='<'; i++){} if( zUser[i]==0 ){ printf(" %s <%s>", zUser, zUser); return; } zName = mprintf("%s", zUser); for(i=j=0; zName[i]; i++){ if( zName[i]!='<' && zName[i]!='>' ){ zName[j++] = zName[i]; } } zName[j] = 0; printf(" %s <%s>", zName, zUser); free(zName); return; } /* ** We have contact information. ** It may or may not contain an email address. */ zContact = db_column_text(&q, 0); for(i=0; zContact[i] && zContact[i]!='>' && zContact[i]!='<'; i++){} if( zContact[i]==0 ){ /* No email address found. Take as user info if not empty */ printf(" %s <%s>", zContact[0] ? zContact : zUser, zUser); db_reset(&q); return; } if( zContact[i]=='<' ){ /* ** Found beginning of email address. Look for the end and extract ** the part. */ zEmail = mprintf("%s", &zContact[i]); for(i=0; zEmail[i] && zEmail[i]!='>'; i++){} if( zEmail[i]=='>' ) zEmail[i+1] = 0; }else{ /* ** Found an end marker for email, but nothing else. */ zEmail = mprintf("<%s>", zUser); } /* ** Here zContact[i] either '<' or '>'. Extract the string _before_ ** either as user name. */ zName = mprintf("%.*s", i-1, zContact); for(i=j=0; zName[i]; i++){ if( zName[i]!='"' ) zName[j++] = zName[i]; } zName[j] = 0; printf(" %s %s", zName, zEmail); free(zName); free(zEmail); db_reset(&q); }
/* ** WEBPAGE: doc ** URL: /doc?name=BASELINE/PATH ** URL: /doc/BASELINE/PATH ** ** BASELINE can be either a baseline uuid prefix or magic words "tip" ** to mean the most recently checked in baseline or "ckout" to mean the ** content of the local checkout, if any. PATH is the relative pathname ** of some file. This method returns the file content. ** ** If PATH matches the patterns *.wiki or *.txt then formatting content ** is added before returning the file. For all other names, the content ** is returned straight without any interpretation or processing. */ void doc_page(void){ const char *zName; /* Argument to the /doc page */ const char *zMime; /* Document MIME type */ int vid = 0; /* Artifact of baseline */ int rid = 0; /* Artifact of file */ int i; /* Loop counter */ Blob filebody; /* Content of the documentation file */ char zBaseline[UUID_SIZE+1]; /* Baseline UUID */ login_check_credentials(); if( !g.perm.Read ){ login_needed(); return; } zName = PD("name", "tip/index.wiki"); for(i=0; zName[i] && zName[i]!='/'; i++){} if( zName[i]==0 || i>UUID_SIZE ){ zName = "index.html"; goto doc_not_found; } memcpy(zBaseline, zName, i); zBaseline[i] = 0; zName += i; while( zName[0]=='/' ){ zName++; } if( !file_is_simple_pathname(zName) ){ int n = strlen(zName); if( n>0 && zName[n-1]=='/' ){ zName = mprintf("%sindex.html", zName); if( !file_is_simple_pathname(zName) ){ goto doc_not_found; } }else{ goto doc_not_found; } } if( fossil_strcmp(zBaseline,"ckout")==0 && db_open_local()==0 ){ sqlite3_snprintf(sizeof(zBaseline), zBaseline, "tip"); } if( fossil_strcmp(zBaseline,"ckout")==0 ){ /* Read from the local checkout */ char *zFullpath; db_must_be_within_tree(); zFullpath = mprintf("%s/%s", g.zLocalRoot, zName); if( !file_isfile(zFullpath) ){ goto doc_not_found; } if( blob_read_from_file(&filebody, zFullpath)<0 ){ goto doc_not_found; } }else{ db_begin_transaction(); if( fossil_strcmp(zBaseline,"tip")==0 ){ vid = db_int(0, "SELECT objid FROM event WHERE type='ci'" " ORDER BY mtime DESC LIMIT 1"); }else{ vid = name_to_typed_rid(zBaseline, "ci"); } /* Create the baseline cache if it does not already exist */ db_multi_exec( "CREATE TABLE IF NOT EXISTS vcache(\n" " vid INTEGER, -- baseline ID\n" " fname TEXT, -- filename\n" " rid INTEGER, -- artifact ID\n" " UNIQUE(vid,fname,rid)\n" ")" ); /* Check to see if the documentation file artifact ID is contained ** in the baseline cache */ rid = db_int(0, "SELECT rid FROM vcache" " WHERE vid=%d AND fname=%Q", vid, zName); if( rid==0 && db_exists("SELECT 1 FROM vcache WHERE vid=%d", vid) ){ goto doc_not_found; } if( rid==0 ){ Stmt s; Manifest *pM; ManifestFile *pFile; /* Add the vid baseline to the cache */ if( db_int(0, "SELECT count(*) FROM vcache")>10000 ){ db_multi_exec("DELETE FROM vcache"); } pM = manifest_get(vid, CFTYPE_MANIFEST); if( pM==0 ){ goto doc_not_found; } db_prepare(&s, "INSERT INTO vcache(vid,fname,rid)" " SELECT %d, :fname, rid FROM blob" " WHERE uuid=:uuid", vid ); manifest_file_rewind(pM); while( (pFile = manifest_file_next(pM,0))!=0 ){ db_bind_text(&s, ":fname", pFile->zName); db_bind_text(&s, ":uuid", pFile->zUuid); db_step(&s); db_reset(&s); } db_finalize(&s); manifest_destroy(pM); /* Try again to find the file */ rid = db_int(0, "SELECT rid FROM vcache" " WHERE vid=%d AND fname=%Q", vid, zName); } if( rid==0 ){ goto doc_not_found; } /* Get the file content */ if( content_get(rid, &filebody)==0 ){ goto doc_not_found; } db_end_transaction(0); } /* The file is now contained in the filebody blob. Deliver the ** file to the user */ zMime = P("mimetype"); if( zMime==0 ){ zMime = mimetype_from_name(zName); } Th_Store("doc_name", zName); Th_Store("doc_version", db_text(0, "SELECT '[' || substr(uuid,1,10) || ']'" " FROM blob WHERE rid=%d", vid)); Th_Store("doc_date", db_text(0, "SELECT datetime(mtime) FROM event" " WHERE objid=%d AND type='ci'", vid)); if( fossil_strcmp(zMime, "application/x-fossil-wiki")==0 ){ Blob title, tail; if( wiki_find_title(&filebody, &title, &tail) ){ style_header(blob_str(&title)); wiki_convert(&tail, 0, 0); }else{ style_header("Documentation"); wiki_convert(&filebody, 0, 0); } style_footer(); }else if( fossil_strcmp(zMime, "text/plain")==0 ){ style_header("Documentation"); @ <blockquote><pre>
/* ** Scans the specified base directory for any directories within it, while ** keeping a count of how many files they each contains, either directly or ** indirectly. ** ** Subdirectories are scanned recursively. ** Omit files named in VFILE. ** ** Directories whose names begin with "." are omitted unless the SCAN_ALL ** flag is set. ** ** Any directories that match the glob patterns pIgnore* are excluded from ** the scan. Name matching occurs after the first nPrefix characters are ** elided from the filename. ** ** Returns the total number of files found. */ int vfile_dir_scan( Blob *pPath, /* Base directory to be scanned */ int nPrefix, /* Number of bytes in base directory name */ unsigned scanFlags, /* Zero or more SCAN_xxx flags */ Glob *pIgnore1, /* Do not add directories that match this GLOB */ Glob *pIgnore2, /* Omit directories matching this GLOB too */ Glob *pIgnore3 /* Omit directories matching this GLOB too */ ){ int result = 0; DIR *d; int origSize; struct dirent *pEntry; int skipAll = 0; static Stmt ins; static Stmt upd; static int depth = 0; void *zNative; origSize = blob_size(pPath); if( pIgnore1 || pIgnore2 || pIgnore3 ){ blob_appendf(pPath, "/"); if( glob_match(pIgnore1, &blob_str(pPath)[nPrefix+1]) ) skipAll = 1; if( glob_match(pIgnore2, &blob_str(pPath)[nPrefix+1]) ) skipAll = 1; if( glob_match(pIgnore3, &blob_str(pPath)[nPrefix+1]) ) skipAll = 1; blob_resize(pPath, origSize); } if( skipAll ) return result; if( depth==0 ){ db_multi_exec("DROP TABLE IF EXISTS dscan_temp;" "CREATE TEMP TABLE dscan_temp(" " x TEXT PRIMARY KEY %s, y INTEGER)", filename_collation()); db_prepare(&ins, "INSERT OR IGNORE INTO dscan_temp(x, y) SELECT :file, :count" " WHERE NOT EXISTS(SELECT 1 FROM vfile WHERE" " pathname GLOB :file || '/*' %s)", filename_collation() ); db_prepare(&upd, "UPDATE OR IGNORE dscan_temp SET y = coalesce(y, 0) + 1" " WHERE x=:file %s", filename_collation() ); } depth++; zNative = fossil_utf8_to_filename(blob_str(pPath)); d = opendir(zNative); if( d ){ while( (pEntry=readdir(d))!=0 ){ char *zOrigPath; char *zPath; char *zUtf8; if( pEntry->d_name[0]=='.' ){ if( (scanFlags & SCAN_ALL)==0 ) continue; if( pEntry->d_name[1]==0 ) continue; if( pEntry->d_name[1]=='.' && pEntry->d_name[2]==0 ) continue; } zOrigPath = mprintf("%s", blob_str(pPath)); zUtf8 = fossil_filename_to_utf8(pEntry->d_name); blob_appendf(pPath, "/%s", zUtf8); zPath = blob_str(pPath); if( glob_match(pIgnore1, &zPath[nPrefix+1]) || glob_match(pIgnore2, &zPath[nPrefix+1]) || glob_match(pIgnore3, &zPath[nPrefix+1]) ){ /* do nothing */ }else if( file_wd_isdir(zPath)==1 ){ if( (scanFlags & SCAN_NESTED) || !vfile_top_of_checkout(zPath) ){ char *zSavePath = mprintf("%s", zPath); int count = vfile_dir_scan(pPath, nPrefix, scanFlags, pIgnore1, pIgnore2, pIgnore3); db_bind_text(&ins, ":file", &zSavePath[nPrefix+1]); db_bind_int(&ins, ":count", count); db_step(&ins); db_reset(&ins); fossil_free(zSavePath); result += count; /* found X normal files? */ } }else if( file_wd_isfile_or_link(zPath) ){ db_bind_text(&upd, ":file", zOrigPath); db_step(&upd); db_reset(&upd); result++; /* found 1 normal file */ } fossil_filename_free(zUtf8); blob_resize(pPath, origSize); fossil_free(zOrigPath); } closedir(d); } fossil_filename_free(zNative); depth--; if( depth==0 ){ db_finalize(&upd); db_finalize(&ins); } return result; }