/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* Copyright (c) 2013 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/ ***************************************************************************** A simple tool for dumping blobs to stdout. */ #include "fossil-scm/fossil-cli.h" #include "fossil-scm/fossil-internal.h" static void fcli_local_help(){ printf("Usage:\n\t%s [options]\n\n", fcli.appName); puts("Outputs status info for the current checkout. Options:\n"); puts("\n\t-f|--no-files disables the status check for local files.\n"); puts("\n\t--utc enables UTC timestamps (default is local time).\n"); puts("\n\t-v|--version specifies the checkin version to show " "(default is checkout version).\n"); puts("\n\t--no-reset is an internal debugging flag which tells this app " "not to re-set the vfile contents to be those of the current checkout. " "For compatibility with fossil(1), do not use it. It is only for testing.\n"); } static char const * fsl_ckout_change_label(fsl_checkout_change_t change){ switch(change){ case FSL_CKOUT_CHANGE_NONE: return NULL; case FSL_CKOUT_CHANGE_MOD: return "MODIFIED"; case FSL_CKOUT_CHANGE_MERGE_MOD: return "MERGE-MOD"; case FSL_CKOUT_CHANGE_MERGE_ADD: return "MERGE-ADD"; case FSL_CKOUT_CHANGE_INTEGRATE_MOD: return "INTEGRATE-MOD"; case FSL_CKOUT_CHANGE_INTEGRATE_ADD: return "INTEGRATE-ADD"; case FSL_CKOUT_CHANGE_ADDED: return "ADDED"; case FSL_CKOUT_CHANGE_REMOVED: return "REMOVED"; case FSL_CKOUT_CHANGE_MISSING: return "MISSING"; case FSL_CKOUT_CHANGE_RENAMED: return "RENAMED"; case FSL_CKOUT_CHANGE_CONFLICT: return "CONFLICT"; case FSL_CKOUT_CHANGE_NOT_A_FILE: return "NOT-A-FILE"; case FSL_CKOUT_CHANGE_CHERRYPICK: return "CHERRYPICK"; case FSL_CKOUT_CHANGE_BACKOUT: return "BACKOUT"; default: return "?!NO WAY!?"; } } /** A fsl_checkout_changes_f() impl which outputs change status to f_out(). */ static int fsl_checkout_changes_fapp(void * state, fsl_checkout_change_t change, char const * filename, char const * origName){ if(1==++*((int*)state)){ f_out("\nLocal changes compared to this version:\n\n"); } f_out("%-15s", fsl_ckout_change_label(change)); if(FSL_CKOUT_CHANGE_RENAMED==change){ assert(origName && *origName); f_out("%s\n%14s %s\n", origName, "to", filename); }else{ f_out("%s\n", filename); } return 0; } static int fapp_show_local_changes(fsl_cx * f, fsl_id_t vid, char doScan){ int rc; if(doScan) rc = fsl_checkout_changes_scan(f); if(!rc){ int counter = 0; rc = fsl_checkout_changes_visit(f, vid, 0, fsl_checkout_changes_fapp, &counter); if(!rc){ f_out("%s\n", counter ? "" : "\nNo local changes."); } } return rc; } /** I */ static int fapp_show_info(fsl_cx * f, fsl_id_t rid, char useUtc){ int rc = 0; fsl_stmt st = fsl_stmt_empty; fsl_db * dbR = fsl_cx_db_repo(f); fsl_db * dbC = fsl_cx_db_checkout(f); int lblWidth = -20; assert(dbR); assert(dbC); { fsl_id_t ckoutRid = 0; fsl_uuid_cstr ckoutUuid = NULL; fsl_checkout_version_info(f, &ckoutUuid, &ckoutRid); assert(ckoutUuid && (ckoutRid>0)); } f_out("%*s %s\n", lblWidth, "repository-db:", fsl_cx_db_file_repo(f, NULL)); f_out("%*s %s\n", lblWidth, "checkout-root:", fsl_cx_dir_name_checkout(f, NULL)); rc = fsl_db_prepare(dbR, &st, "SELECT " /*0*/"datetime(event.mtime%s) AS timestampString, " /*1*/"coalesce(euser, user) AS user, " /*2*/"(SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref " "WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid " "AND tagxref.rid=blob.rid AND tagxref.tagtype>0) as tags, " /*3*/"coalesce(ecomment, comment) AS comment, " /*4*/"uuid AS uuid " "FROM event JOIN blob " "WHERE " "event.type='ci' " "AND blob.rid=%"FSL_ID_T_PFMT" " "AND blob.rid=event.objid " "ORDER BY event.mtime DESC", useUtc ? "" : ", 'localtime'", (fsl_id_t)rid); if(rc) goto dberr; if( FSL_RC_STEP_ROW != fsl_stmt_step(&st)){ fcli_err_set(FSL_RC_ERROR, "Event data for checkout not found."); goto end; } f_out("%*s %s %s %s (RID %"FSL_ID_T_PFMT")\n", lblWidth, "checkout-version:", fsl_stmt_g_text(&st, 4, NULL), fsl_stmt_g_text(&st, 0, NULL), useUtc ? "UTC" : "local", (fsl_id_t)rid ); { /* list parent(s) */ fsl_stmt stP = fsl_stmt_empty; rc = fsl_db_prepare(dbR, &stP, "SELECT " "uuid, pid, isprim " "FROM plink JOIN blob ON pid=rid " "WHERE cid=%"FSL_ID_T_PFMT" " "ORDER BY isprim DESC, mtime DESC /*sort*/", (fsl_id_t)rid); if(rc) goto dberr; while( FSL_RC_STEP_ROW == fsl_stmt_step(&stP) ){ char const * zLabel = fsl_stmt_g_int32(&stP,2) ? "parent:" : "merged-from:"; f_out("%*s %s\n", lblWidth, zLabel, fsl_stmt_g_text(&stP, 0, NULL)); } fsl_stmt_finalize(&stP); } { /* list children */ fsl_stmt stC = fsl_stmt_empty; rc = fsl_db_prepare(dbR, &stC, "SELECT " "uuid, cid, isprim " "FROM plink JOIN blob ON cid=rid " "WHERE pid=%"FSL_ID_T_PFMT" " "ORDER BY isprim DESC, mtime DESC /*sort*/", (fsl_id_t)rid); if(rc) goto dberr; while( FSL_RC_STEP_ROW == fsl_stmt_step(&stC) ){ char const * zLabel = fsl_stmt_g_int32(&stC,2) ? "child:" : "merged-into:"; f_out("%*s %s\n", lblWidth, zLabel, fsl_stmt_g_text(&stC, 0, NULL)); } fsl_stmt_finalize(&stC); } f_out("%*s %s\n", lblWidth, "user:", fsl_stmt_g_text(&st, 1, NULL)); f_out("%*s %s\n", lblWidth, "tags:", fsl_stmt_g_text(&st, 2, NULL)); f_out("%*s %s\n", lblWidth, "comment:", fsl_stmt_g_text(&st, 3, NULL)); dberr: if(rc){ fsl_cx_uplift_db_error(f, dbR); } end: fsl_stmt_finalize(&st); return rc; } int main(int argc, char * const * argv ){ int rc = 0; fsl_cx * f; fsl_db * db; fsl_id_t rid = 0; char skipFiles; char useUtc; char * zVersion = NULL; fsl_id_t ckoutRid = 0; char resetVfile; fcli.appHelp = fcli_local_help; rc = fcli_setup(argc, argv); if(FSL_RC_BREAK==rc) /* --help */ return 0; else if(rc) goto end; f = fcli.f; db = fsl_cx_db_checkout(f); if(!db){ rc = fcli_err_set( FSL_RC_NOT_A_CHECKOUT, "This app requires a checkout db."); goto end; } skipFiles = fcli_flag2("f","no-files", NULL); useUtc = fcli_flag("utc", NULL); fcli_flag2("v","version", &zVersion); resetVfile = !fcli_flag("no-reset",NULL); if(fcli_has_unused_flags(0)) goto end; fsl_checkout_version_info(f, NULL, &ckoutRid); if(zVersion){ rc = fsl_sym_to_rid(f, zVersion, FSL_CATYPE_CHECKIN, &rid); if(rc) goto end; } if(!rid){ rid = ckoutRid; } if( !fsl_rid_is_a_checkin(f, rid) ) { rc = fcli_err_set(rc, "%s does not refer to a checkin version.", zVersion ? zVersion : "RID"); goto end; } rc = fapp_show_info(f, rid, useUtc); if(!rc && !skipFiles){ rc = fapp_show_local_changes(f, rid, resetVfile); } end: fsl_free(zVersion); return fcli_err_report(1) ? EXIT_FAILURE : EXIT_SUCCESS; }