/* ** Copy the content of a file from one place to another. */ void file_copy(const char *zFrom, const char *zTo) { FILE *in, *out; int got; char zBuf[8192]; in = fossil_fopen(zFrom, "rb"); if( in==0 ) fossil_fatal("cannot open \"%s\" for reading", zFrom); out = fossil_fopen(zTo, "wb"); if( out==0 ) fossil_fatal("cannot open \"%s\" for writing", zTo); while( (got=fread(zBuf, 1, sizeof(zBuf), in))>0 ) { fwrite(zBuf, 1, got, out); } fclose(in); fclose(out); }
/* ** Write the content of a blob into a file. ** ** If the filename is blank or "-" then write to standard output. ** ** Return the number of bytes written. */ int blob_write_to_file(Blob *pBlob, const char *zFilename){ FILE *out; int nWrote; if( zFilename[0]==0 || (zFilename[0]=='-' && zFilename[1]==0) ){ nWrote = blob_size(pBlob); #if defined(_WIN32) if( fossil_utf8_to_console(blob_buffer(pBlob), nWrote, 0) >= 0 ){ return nWrote; } #endif fwrite(blob_buffer(pBlob), 1, nWrote, stdout); }else{ file_mkfolder(zFilename, 1); out = fossil_fopen(zFilename, "wb"); if( out==0 ){ fossil_fatal_recursive("unable to open file \"%s\" for writing", zFilename); return 0; } blob_is_init(pBlob); nWrote = fwrite(blob_buffer(pBlob), 1, blob_size(pBlob), out); fclose(out); if( nWrote!=blob_size(pBlob) ){ fossil_fatal_recursive("short write: %d of %d bytes to %s", nWrote, blob_size(pBlob), zFilename); } } return nWrote; }
/* ** Initialize a blob to be the content of a file. If the filename ** is blank or "-" then read from standard input. ** ** Any prior content of the blob is discarded, not freed. ** ** Return the number of bytes read. Calls fossil_fatal() error (i.e. ** it exit()s and does not return). */ int blob_read_from_file(Blob *pBlob, const char *zFilename){ int size, got; FILE *in; if( zFilename==0 || zFilename[0]==0 || (zFilename[0]=='-' && zFilename[1]==0) ){ return blob_read_from_channel(pBlob, stdin, -1); } size = file_wd_size(zFilename); blob_zero(pBlob); if( size<0 ){ fossil_fatal("no such file: %s", zFilename); } if( size==0 ){ return 0; } blob_resize(pBlob, size); in = fossil_fopen(zFilename, "rb"); if( in==0 ){ fossil_fatal("cannot open %s for reading", zFilename); } got = fread(blob_buffer(pBlob), 1, size, in); fclose(in); if( got<size ){ blob_resize(pBlob, got); } return got; }
/* ** Open a connection to the server. The server is defined by the following ** variables: ** ** pUrlData->name Name of the server. Ex: www.fossil-scm.org ** pUrlData->port TCP/IP port. Ex: 80 ** pUrlData->isHttps Use TLS for the connection ** ** Return the number of errors. */ int transport_open(UrlData *pUrlData){ int rc = 0; if( transport.isOpen==0 ){ if( pUrlData->isSsh ){ rc = transport_ssh_open(pUrlData); if( rc==0 ) transport.isOpen = 1; }else if( pUrlData->isHttps ){ #ifdef FOSSIL_ENABLE_SSL rc = ssl_open(pUrlData); if( rc==0 ) transport.isOpen = 1; #else socket_set_errmsg("HTTPS: Fossil has been compiled without SSL support"); rc = 1; #endif }else if( pUrlData->isFile ){ sqlite3_uint64 iRandId; sqlite3_randomness(sizeof(iRandId), &iRandId); transport.zOutFile = mprintf("%s-%llu-out.http", g.zRepositoryName, iRandId); transport.zInFile = mprintf("%s-%llu-in.http", g.zRepositoryName, iRandId); transport.pFile = fossil_fopen(transport.zOutFile, "wb"); if( transport.pFile==0 ){ fossil_fatal("cannot output temporary file: %s", transport.zOutFile); } transport.isOpen = 1; }else{ rc = socket_open(pUrlData); if( rc==0 ) transport.isOpen = 1; } } return rc; }
/* ** This routine is called when the outbound message is complete and ** it is time to being receiving a reply. */ void transport_flip(UrlData *pUrlData){ if( pUrlData->isFile ){ char *zCmd; fclose(transport.pFile); zCmd = mprintf("\"%s\" http \"%s\" \"%s\" \"%s\" 127.0.0.1 --localauth", g.nameOfExe, pUrlData->name, transport.zOutFile, transport.zInFile ); fossil_system(zCmd); free(zCmd); transport.pFile = fossil_fopen(transport.zInFile, "rb"); } }
/* ** Process a single incoming HTTP request. */ static void win32_process_one_http_request(void *pAppData){ HttpRequest *p = (HttpRequest*)pAppData; FILE *in = 0, *out = 0; int amt, got; int wanted = 0; char *z; char zRequestFName[MAX_PATH]; char zReplyFName[MAX_PATH]; char zCmd[2000]; /* Command-line to process the request */ char zHdr[2000]; /* The HTTP request header */ sqlite3_snprintf(MAX_PATH, zRequestFName, "%s_in%d.txt", zTempPrefix, p->id); sqlite3_snprintf(MAX_PATH, zReplyFName, "%s_out%d.txt", zTempPrefix, p->id); amt = 0; while( amt<sizeof(zHdr) ){ got = recv(p->s, &zHdr[amt], sizeof(zHdr)-1-amt, 0); if( got==SOCKET_ERROR ) goto end_request; if( got==0 ){ wanted = 0; break; } amt += got; zHdr[amt] = 0; z = strstr(zHdr, "\r\n\r\n"); if( z ){ wanted = find_content_length(zHdr) + (&z[4]-zHdr) - amt; break; } } if( amt>=sizeof(zHdr) ) goto end_request; out = fossil_fopen(zRequestFName, "wb"); if( out==0 ) goto end_request; fwrite(zHdr, 1, amt, out); while( wanted>0 ){ got = recv(p->s, zHdr, sizeof(zHdr), 0); if( got==SOCKET_ERROR ) goto end_request; if( got ){ fwrite(zHdr, 1, got, out); }else{ break; } wanted -= got; } fclose(out); out = 0; sqlite3_snprintf(sizeof(zCmd), zCmd, "\"%s\" http \"%s\" %s %s %s --nossl%s", g.nameOfExe, g.zRepositoryName, zRequestFName, zReplyFName, inet_ntoa(p->addr.sin_addr), p->zOptions ); fossil_system(zCmd); in = fossil_fopen(zReplyFName, "rb"); if( in ){ while( (got = fread(zHdr, 1, sizeof(zHdr), in))>0 ){ send(p->s, zHdr, got, 0); } } end_request: if( out ) fclose(out); if( in ) fclose(in); closesocket(p->s); file_delete(zRequestFName); file_delete(zReplyFName); free(p); }
/* ** Write the content of a blob into a file. ** ** If the filename is blank or "-" then write to standard output. ** ** Return the number of bytes written. */ int blob_write_to_file(Blob *pBlob, const char *zFilename){ FILE *out; int nWrote; if( zFilename[0]==0 || (zFilename[0]=='-' && zFilename[1]==0) ){ nWrote = blob_size(pBlob); #if defined(_WIN32) if( fossil_utf8_to_console(blob_buffer(pBlob), nWrote, 0) >= 0 ){ return nWrote; } #endif fwrite(blob_buffer(pBlob), 1, nWrote, stdout); }else{ int i, nName; char *zName, zBuf[1000]; nName = strlen(zFilename); if( nName>=sizeof(zBuf) ){ zName = mprintf("%s", zFilename); }else{ zName = zBuf; memcpy(zName, zFilename, nName+1); } nName = file_simplify_name(zName, nName, 0); for(i=1; i<nName; i++){ if( zName[i]=='/' ){ zName[i] = 0; #if defined(_WIN32) || defined(__CYGWIN__) /* ** On Windows, local path looks like: C:/develop/project/file.txt ** The if stops us from trying to create a directory of a drive letter ** C: in this example. */ if( !(i==2 && zName[1]==':') ){ #endif if( file_mkdir(zName, 1) && file_isdir(zName)!=1 ){ fossil_fatal_recursive("unable to create directory %s", zName); return 0; } #if defined(_WIN32) || defined(__CYGWIN__) } #endif zName[i] = '/'; } } out = fossil_fopen(zName, "wb"); if( out==0 ){ fossil_fatal_recursive("unable to open file \"%s\" for writing", zName); return 0; } if( zName!=zBuf ) free(zName); blob_is_init(pBlob); nWrote = fwrite(blob_buffer(pBlob), 1, blob_size(pBlob), out); fclose(out); if( nWrote!=blob_size(pBlob) ){ fossil_fatal_recursive("short write: %d of %d bytes to %s", nWrote, blob_size(pBlob), zFilename); } } return nWrote; }
/* ** COMMAND: import ** ** Usage: %fossil import --git ?OPTIONS? NEW-REPOSITORY ** ** Read text generated by the git-fast-export command and use it to ** construct a new Fossil repository named by the NEW-REPOSITORY ** argument. The git-fast-export text is read from standard input. ** ** The git-fast-export file format is currently the only VCS interchange ** format that is understood, though other interchange formats may be added ** in the future. ** ** The --incremental option allows an existing repository to be extended ** with new content. ** ** Options: ** --incremental allow importing into an existing repository ** ** See also: export */ void git_import_cmd(void){ char *zPassword; FILE *pIn; Stmt q; int forceFlag = find_option("force", "f", 0)!=0; int incrFlag = find_option("incremental", "i", 0)!=0; find_option("git",0,0); /* Skip the --git option for now */ verify_all_options(); if( g.argc!=3 && g.argc!=4 ){ usage("REPOSITORY-NAME"); } if( g.argc==4 ){ pIn = fossil_fopen(g.argv[3], "rb"); }else{ pIn = stdin; fossil_binary_mode(pIn); } if( !incrFlag ){ if( forceFlag ) file_delete(g.argv[2]); db_create_repository(g.argv[2]); } db_open_repository(g.argv[2]); db_open_config(0); /* The following temp-tables are used to hold information needed for ** the import. ** ** The XMARK table provides a mapping from fast-import "marks" and symbols ** into artifact ids (UUIDs - the 40-byte hex SHA1 hash of artifacts). ** Given any valid fast-import symbol, the corresponding fossil rid and ** uuid can found by searching against the xmark.tname field. ** ** The XBRANCH table maps commit marks and symbols into the branch those ** commits belong to. If xbranch.tname is a fast-import symbol for a ** checkin then xbranch.brnm is the branch that checkin is part of. ** ** The XTAG table records information about tags that need to be applied ** to various branches after the import finishes. The xtag.tcontent field ** contains the text of an artifact that will add a tag to a check-in. ** The git-fast-export file format might specify the same tag multiple ** times but only the last tag should be used. And we do not know which ** occurrence of the tag is the last until the import finishes. */ db_multi_exec( "CREATE TEMP TABLE xmark(tname TEXT UNIQUE, trid INT, tuuid TEXT);" "CREATE TEMP TABLE xbranch(tname TEXT UNIQUE, brnm TEXT);" "CREATE TEMP TABLE xtag(tname TEXT UNIQUE, tcontent TEXT);" ); db_begin_transaction(); if( !incrFlag ) db_initial_setup(0, 0, 0, 1); git_fast_import(pIn); db_prepare(&q, "SELECT tcontent FROM xtag"); while( db_step(&q)==SQLITE_ROW ){ Blob record; db_ephemeral_blob(&q, 0, &record); fast_insert_content(&record, 0, 0); import_reset(0); } db_finalize(&q); db_end_transaction(0); db_begin_transaction(); fossil_print("Rebuilding repository meta-data...\n"); rebuild_db(0, 1, !incrFlag); verify_cancel(); db_end_transaction(0); fossil_print("Vacuuming..."); fflush(stdout); db_multi_exec("VACUUM"); fossil_print(" ok\n"); if( !incrFlag ){ fossil_print("project-id: %s\n", db_get("project-code", 0)); fossil_print("server-id: %s\n", db_get("server-code", 0)); zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin); fossil_print("admin-user: %s (password is \"%s\")\n", g.zLogin, zPassword); } }
/* ** Compute an aggregate MD5 checksum over the disk image of every ** file in vid. The file names are part of the checksum. The resulting ** checksum is the same as is expected on the R-card of a manifest. ** ** This function operates differently if the Global.aCommitFile ** variable is not NULL. In that case, the disk image is used for ** each file in aCommitFile[] and the repository image ** is used for all others). ** ** Newly added files that are not contained in the repository are ** omitted from the checksum if they are not in Global.aCommitFile[]. ** ** Newly deleted files are included in the checksum if they are not ** part of Global.aCommitFile[] ** ** Renamed files use their new name if they are in Global.aCommitFile[] ** and their original name if they are not in Global.aCommitFile[] ** ** Return the resulting checksum in blob pOut. */ void vfile_aggregate_checksum_disk(int vid, Blob *pOut){ FILE *in; Stmt q; char zBuf[4096]; db_must_be_within_tree(); db_prepare(&q, "SELECT %Q || pathname, pathname, origname, is_selected(id), rid" " FROM vfile" " WHERE (NOT deleted OR NOT is_selected(id)) AND vid=%d" " ORDER BY if_selected(id, pathname, origname) /*scan*/", g.zLocalRoot, vid ); md5sum_init(); while( db_step(&q)==SQLITE_ROW ){ const char *zFullpath = db_column_text(&q, 0); const char *zName = db_column_text(&q, 1); int isSelected = db_column_int(&q, 3); if( isSelected ){ md5sum_step_text(zName, -1); if( file_wd_islink(zFullpath) ){ /* Instead of file content, use link destination path */ Blob pathBuf; sqlite3_snprintf(sizeof(zBuf), zBuf, " %ld\n", blob_read_link(&pathBuf, zFullpath)); md5sum_step_text(zBuf, -1); md5sum_step_text(blob_str(&pathBuf), -1); blob_reset(&pathBuf); }else{ in = fossil_fopen(zFullpath,"rb"); if( in==0 ){ md5sum_step_text(" 0\n", -1); continue; } fseek(in, 0L, SEEK_END); sqlite3_snprintf(sizeof(zBuf), zBuf, " %ld\n", ftell(in)); fseek(in, 0L, SEEK_SET); md5sum_step_text(zBuf, -1); /*printf("%s %s %s",md5sum_current_state(),zName,zBuf); fflush(stdout);*/ for(;;){ int n; n = fread(zBuf, 1, sizeof(zBuf), in); if( n<=0 ) break; md5sum_step_text(zBuf, n); } fclose(in); } }else{ int rid = db_column_int(&q, 4); const char *zOrigName = db_column_text(&q, 2); char zBuf[100]; Blob file; if( zOrigName ) zName = zOrigName; if( rid>0 ){ md5sum_step_text(zName, -1); blob_zero(&file); content_get(rid, &file); sqlite3_snprintf(sizeof(zBuf), zBuf, " %d\n", blob_size(&file)); md5sum_step_text(zBuf, -1); md5sum_step_blob(&file); blob_reset(&file); } } } db_finalize(&q); md5sum_finish(pOut); }
/* ** 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); } } }