/* ** Copyright (c) 2016 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code used to implement unversioned file interfaces. */ #include "config.h" #include #if defined(FOSSIL_ENABLE_MINIZ) # define MINIZ_HEADER_FILE_ONLY # include "miniz.c" #else # include #endif #include "unversioned.h" #include /* ** SQL code to implement the tables needed by the unversioned. */ static const char zUnversionedInit[] = @ CREATE TABLE IF NOT EXISTS "%w".unversioned( @ name TEXT PRIMARY KEY, -- Name of the uv file @ rcvid INTEGER, -- Where received from @ mtime DATETIME, -- timestamp. Seconds since 1970. @ hash TEXT, -- Content hash. NULL if a delete marker @ sz INTEGER, -- size of content after decompression @ encoding INT, -- 0: plaintext. 1: zlib compressed @ content BLOB -- content of the file. NULL if oversized @ ) WITHOUT ROWID; ; /* ** Make sure the unversioned table exists in the repository. */ void unversioned_schema(void){ if( !db_table_exists("repository", "unversioned") ){ db_multi_exec(zUnversionedInit /*works-like:"%w"*/, db_name("repository")); } } /* ** Return a string which is the hash of the unversioned content. ** This is the hash used by repositories to compare content before ** exchanging a catalog. So all repositories must compute this hash ** in exactly the same way. ** ** If debugFlag is set, force the value to be recomputed and write ** the text of the hashed string to stdout. */ const char *unversioned_content_hash(int debugFlag){ const char *zHash = debugFlag ? 0 : db_get("uv-hash", 0); if( zHash==0 ){ Stmt q; db_prepare(&q, "SELECT printf('%%s %%s %%s\n',name,datetime(mtime,'unixepoch'),hash)" " FROM unversioned" " WHERE hash IS NOT NULL" " ORDER BY name" ); while( db_step(&q)==SQLITE_ROW ){ const char *z = db_column_text(&q, 0); if( debugFlag ) fossil_print("%s", z); sha1sum_step_text(z,-1); } db_finalize(&q); db_set("uv-hash", sha1sum_finish(0), 0); zHash = db_get("uv-hash",0); } return zHash; } /* ** Initialize pContent to be the content of an unversioned file zName. ** ** Return 0 on success. Return 1 if zName is not found. */ int unversioned_content(const char *zName, Blob *pContent){ Stmt q; int rc = 1; blob_init(pContent, 0, 0); db_prepare(&q, "SELECT encoding, content FROM unversioned WHERE name=%Q", zName); if( db_step(&q)==SQLITE_ROW ){ db_column_blob(&q, 1, pContent); if( db_column_int(&q, 0)==1 ){ blob_uncompress(pContent, pContent); } rc = 0; } db_finalize(&q); return rc; } /* ** Check the status of unversioned file zName. Return an integer status ** code as follows: ** ** 0: zName does not exist in the unversioned table. ** 1: zName exists and should be replaced by mtime/zHash. ** 2: zName exists and is the same as zHash but has a older mtime ** 3: zName exists and is identical to mtime/zHash in all respects. ** 4: zName exists and is the same as zHash but has a newer mtime. ** 5: zName exists and should override mtime/zHash. */ int unversioned_status(const char *zName, sqlite3_int64 mtime, const char *zHash){ int iStatus = 0; Stmt q; db_prepare(&q, "SELECT mtime, hash FROM unversioned WHERE name=%Q", zName); if( db_step(&q)==SQLITE_ROW ){ const char *zLocalHash = db_column_text(&q, 1); int hashCmp; sqlite3_int64 iLocalMtime = db_column_int64(&q, 0); int mtimeCmp = iLocalMtime=3 ? g.argv[2] : "x"; nCmd = (int)strlen(zCmd); if( zMtime==0 ){ mtime = time(0); }else{ mtime = db_int(0, "SELECT strftime('%%s',%Q)", zMtime); if( mtime<=0 ) fossil_fatal("bad timestamp: %Q", zMtime); } if( memcmp(zCmd, "add", nCmd)==0 ){ const char *zIn; const char *zAs; Blob file; Blob hash; Blob compressed; Stmt ins; int i; zAs = find_option("as",0,1); if( zAs && g.argc!=4 ) usage("add DISKFILE --as UVFILE"); verify_all_options(); db_begin_transaction(); content_rcvid_init(); db_prepare(&ins, "REPLACE INTO unversioned(name,rcvid,mtime,hash,sz,encoding,content)" " VALUES(:name,:rcvid,:mtime,:hash,:sz,:encoding,:content)" ); for(i=3; i1 && zCmd[1]=='i'); verify_all_options(); if( !longFlag ){ if( allFlag ){ db_prepare(&q, "SELECT name FROM unversioned ORDER BY name"); }else{ db_prepare(&q, "SELECT name FROM unversioned WHERE hash IS NOT NULL" " ORDER BY name"); } while( db_step(&q)==SQLITE_ROW ){ fossil_print("%s\n", db_column_text(&q,0)); } }else{ db_prepare(&q, "SELECT hash, datetime(mtime,'unixepoch'), sz, length(content), name" " FROM unversioned" " ORDER BY name;" ); while( db_step(&q)==SQLITE_ROW ){ const char *zHash = db_column_text(&q, 0); const char *zNoContent = ""; if( zHash==0 ){ if( !allFlag ) continue; zHash = "(deleted)"; }else if( db_column_type(&q,3)==SQLITE_NULL ){ zNoContent = " (no content)"; } fossil_print("%12.12s %s %8d %8d %s%s\n", zHash, db_column_text(&q,1), db_column_int(&q,2), db_column_int(&q,3), db_column_text(&q,4), zNoContent ); } } db_finalize(&q); }else if( memcmp(zCmd, "revert", nCmd)==0 ){ fossil_fatal("not yet implemented..."); }else if( memcmp(zCmd, "rm", nCmd)==0 ){ int i; verify_all_options(); db_begin_transaction(); for(i=3; i @ @ @ @ while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); sqlite3_int64 mtime = db_column_int(&q, 1); int isDeleted = db_column_int(&q, 2); int fullSize = db_column_int(&q, 3); char *zAge = human_readable_age((iNow - mtime)/86400.0); if( isDeleted ){ sqlite3_snprintf(sizeof(zSzName), zSzName, "Deleted"); fullSize = 0; }else{ approxSizeName(sizeof(zSzName), zSzName, fullSize); iTotalSz += fullSize; cnt++; } @ @ @ @ @ fossil_free(zAge); } approxSizeName(sizeof(zSzName), zSzName, iTotalSz); @ @ @
Name @ Age @ Size @
%h(zName) %s(zAge) %s(zSzName)
Total over %d(cnt) files%s(zSzName)
db_finalize(&q); output_table_sorting_javascript("uvtab","tKk",1); style_footer(); }