/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* Copyright 2013-2021 The Libfossil Authors, see LICENSES/BSD-2-Clause.txt SPDX-License-Identifier: BSD-2-Clause-FreeBSD SPDX-FileCopyrightText: 2021 The Libfossil Authors SPDX-ArtifactOfProjectName: Libfossil SPDX-FileType: Code */ /** This is a template application for libfossil fcli client apps, with commentary explaining how to do various common things with the API. Copy/paste this and modify it to suit. */ #include "libfossil.h" // Only for testing/debugging.. #define MARKER(pfexp) \ do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \ printf pfexp; \ } while(0) // Global app state. struct App_ { fsl_annotate_opt opt; } App = { fsl_annotate_opt_empty_m }; #if 0 static int fsl_annotate_stepper(void * state, fsl_annotate_opt const * const * opt, fsl_annotate_step const * const * step){ static const int szHash = 10; fsl_outputer const * fout = (fsl_outputer*)state; int rc = 0; char ymd[24]; if(step->mtime>0){ fsl_julian_to_iso8601(step->mtime, &ymd[0], false); ymd[10] = 0; } switch(step->stepType){ case FSL_ANNOTATE_STEP_VERSION: rc = fsl_appendf(fout->out, fout->state, "version %3d: %s %.*s file %.*s\n", step->stepNumber+1, ymd, szHash, step->versionHash, szHash, step->fileHash); break; case FSL_ANNOTATE_STEP_FULL: if(opt->praise){ rc = fsl_appendf(fout->out, fout->state, "%.*s %s %13.13s: %.*s\n", szHash, opt->fileVersions ? step->fileHash : step->versionHash, ymd, step->username, (int)step->lineLength, step->line); }else{ rc = fsl_appendf(fout->out, fout->state, "%.*s %s %5d: %.*s\n", szHash, opt->fileVersions ? step->fileHash : step->versionHash, ymd, step->lineNumber, (int)step->lineLength, step->line); } break; case FSL_ANNOTATE_STEP_LIMITED: if(opt->praise){ rc = fsl_appendf(fout->out, fout->state, "%*s %.*s\n", szHash+26, "", (int)step->lineLength, step->line); }else{ rc = fsl_appendf(fout->out, fout->state, "%*s %5" PRIu32 ": %.*s\n", szHash+11, "", step->lineNumber, (int)step->lineLength, step->line); } break; } return rc; } #endif int main(int argc, const char * const * argv ){ bool ignoreAllSpace = false; bool ignoreEOLSpace = false; char const * zRevision = 0; char const * zOrigin = 0; fsl_buffer fnamebuf = fsl_buffer_empty; /** Set up flag handling, which is used for processing basic CLI flags and generating --help text output. */ const fcli_cliflag FCliFlags[] = { // FCLI_FLAG_xxx macros are convenience forms for initializing // these members... FCLI_FLAG_BOOL("p","praise",&App.opt.praise, "Use praise/blame mode."), FCLI_FLAG_BOOL("f","file-versions",&App.opt.fileVersions, "List file blob versions instead of checkin versions."), FCLI_FLAG_BOOL("w","ignore-all-space",&ignoreAllSpace, "Ignore all whitespace changes."), FCLI_FLAG_BOOL("Z","ignore-trailing-space",&ignoreEOLSpace, "Ignore end-of-line whitespace."), FCLI_FLAG("v","version", "string",&zRevision, "Checkin containing the input file (default=checkout)."), FCLI_FLAG("o","origin", "string", &zOrigin, "Origin checkin version (default root of " "the tree)."), FCLI_FLAG("f","file", "filename",&App.opt.filename, "Repo-relative file to annote. May be provided as the " "first non-flag argument."), FCLI_FLAG_I32("n","limit", "int>=0",(int32_t*)&App.opt.limitVersions, "Limit the number of historical versions to check. " "0=no limit."), FCLI_FLAG_I32(NULL,"ms", "int>=0",(int32_t*)&App.opt.limitMs, "Limit annotation to approx. this number of " "milliseconds of work. 0=no limit."), FCLI_FLAG_BOOL(NULL,"versions", &App.opt.dumpVersions, "Prefix output with a list of all analyzed versions."), fcli_cliflag_empty_m // list MUST end with this (or equivalent) }; const fcli_help_info FCliHelp = { "Outputs an annoted listing of a file's contents.", "[options] FILENAME", NULL // optional callback which outputs app-specific help }; int rc = fcli_setup_v2(argc, argv, FCliFlags, &FCliHelp); if(rc) goto end; fsl_cx * const f = fcli_cx(); if(!App.opt.filename){ App.opt.filename = fcli_next_arg(true); if(!App.opt.filename){ rc = fcli_err_set(FSL_RC_MISUSE, "Missing required filename argument. Try --help."); goto end; } } if(fsl_cx_db_ckout(f) && 0==fsl_stat(App.opt.filename, NULL, false)){ // We're in a checkout and the file exists. Canonicalize // the filename relative to the repo root... rc = fsl_ckout_filename_check(f, true, App.opt.filename, &fnamebuf); if(rc) goto end; App.opt.filename = fsl_buffer_cstr(&fnamebuf); } if(zRevision){ rc = fsl_sym_to_rid(f, zRevision, FSL_SATYPE_CHECKIN, &App.opt.versionRid); if(rc) goto end; }else{ fsl_ckout_version_info(f, &App.opt.versionRid, &zRevision); if(!zRevision){ rc = fcli_err_set(FSL_RC_MISUSE, "Cannot determine --revision value."); goto end; } } if(zOrigin){ rc = fsl_sym_to_rid(f, zOrigin, FSL_SATYPE_CHECKIN, &App.opt.originRid); if(rc) goto end; } if((rc=fcli_has_unused_args(false))) goto end; if(ignoreAllSpace) App.opt.spacePolicy = 1; else if(ignoreEOLSpace) App.opt.spacePolicy = -1; fsl_outputer myOut = fsl_outputer_FILE; myOut.state = stdout; App.opt.out = fsl_annotate_step_f_fossilesque; App.opt.outState = &myOut; rc = fsl_annotate(f, &App.opt); end: fsl_buffer_clear(&fnamebuf); return fcli_end_of_main(rc) /* Will report any pending error state and return either EXIT_SUCCESS or EXIT_FAILURE. */; }