/* -*- 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/
**
*******************************************************************************
** This file implements some test/demo code for working with wiki pages.
*/
#include "fossil-scm/fossil-cli.h" /* Fossil App mini-framework */
static void fcli_local_help(){
printf("Usage:\n\t%s [options] command\n\n", fcli.appName);
puts("Commands:");
puts("\n\texport PageName [-o|--output-file=FILENAME]\n\t"
"Exports the most recent version of "
"the given page (to stdout by default). "
"PageName may optionally be specified using [-p|--page Name]."
);
puts("\n\tls [-n|--names]\n\t"
"Lists the most recent versions of all wiki pages. "
"Use -V for more info.");
puts("\n\tsave PageName InputFile "
"[-n|--dry-run] "
"[-t|--mime-type=TYPE]"
"[--new]");
puts("\t"
"Imports a file to a wiki page. "
"PageName and InputFile may optionally be specified using "
"[-p|--page Name] resp. [-f|--file NAME]. The --new option allows the "
"creation of new pages. Without that option, saving will fail for "
"non-existing pages in order to avoid that a typo fills the repo "
"with unwanted pages."
);
}
fsl_int32_t wiki_page_count(){
return fsl_db_g_int32(fsl_cx_db_repo(fcli.f), -1,
"SELECT count(*) FROM tag "
"WHERE tagname GLOB 'wiki-*'");
}
static struct {
char listNamesOnly;
char * pageName;
} WikiApp = {
0/*listNamesOnly*/,
NULL/*pageName*/
};
static int cb_f_wiki_list( fsl_cx * f, fsl_deck const * d,
void * state ){
fsl_db * db = fsl_cx_db_repo(fcli.f);
char * ts = fsl_db_julian_to_iso8601(db, d->D, 0);
int * counter = (int*)state;
if(WikiApp.listNamesOnly){
f_out("%b\n", &d->L);
}else{
ts[10] = '@';
if(0 == (*counter)++){
if(fcli.verbose){
f_out("RID ");
}
f_out("%-19s %-8s Name\n", "Timestamp", "UUID");
}
if(fcli.verbose){
f_out("%-6"FSL_ID_T_PFMT" ", d->rid);
}
f_out("%-19s %.*s %b\n",
ts, 8, d->uuid, &d->L);
fsl_free(ts);
}
#if defined(DEBUG)
{
int rc;
fsl_id_t ridCheck = 0;
rc = fsl_wiki_latest_rid(f, fsl_buffer_cstr(&d->L), &ridCheck);
assert(!rc);
if(d->rid!=ridCheck){
f_out("d->rid=%"FSL_ID_T_PFMT", ridCheck=%"FSL_ID_T_PFMT"\n",
d->rid, ridCheck);
}
assert(d->rid==ridCheck);
}
#endif
return 0;
}
static int fcmd_wiki_list(){
fsl_cx * f = fcli.f;
int counter = 0;
int rc;
WikiApp.listNamesOnly = fcli_flag2("n", "names", NULL);
if(fcli_has_unused_flags(1)){
return FSL_RC_MISUSE;
}
if(WikiApp.listNamesOnly){
fsl_list li = fsl_list_empty;
fsl_size_t i;
rc = fsl_wiki_names_get(f, &li);
for( i = 0; !rc && (i<li.used); ++i){
char const * n = (char const *)li.list[i];
f_out("%s\n", n);
}
fsl_list_clear(&li, fsl_list_v_fsl_free, NULL);
}else{
rc = fsl_wiki_foreach_page(f, cb_f_wiki_list, &counter );
}
return rc;
}
static int fcmd_wiki_export(){
fsl_cx * f = fcli.f;
char * pName = WikiApp.pageName;
char * oFile = NULL;
int rc;
fsl_deck d = fsl_deck_empty;
fcli_flag2("o", "ouput-file", &oFile);
if(fcli_has_unused_flags(1)){
rc = FSL_RC_MISUSE;
goto end;
}
if(!pName && !(pName = fcli_next_arg(1))){
rc = fcli_err_set(FSL_RC_MISUSE,
"Expecting wiki page name argument.");
goto end;
}
rc = fsl_wiki_load_latest(f, pName, &d);
if(!rc){
if(oFile){
FILE * of = fsl_fopen(oFile, "w");
if(!of){
rc = fcli_err_set(FSL_RC_IO,
"Could not open [%s] for writing.",
oFile);
}else{
fwrite(d.W.mem, d.W.used, 1, of);
fsl_fclose(of);
}
}else{
f_out("%b", &d.W);
}
}
end:
if(WikiApp.pageName != pName) fsl_free(pName);
fsl_free(oFile);
fsl_deck_finalize(&d);
return rc;
}
static int fcmd_wiki_save(){
fsl_cx * f = fcli.f;
char * pName = WikiApp.pageName;
char * iFile = NULL;
char * mimeType = NULL;
int rc;
fsl_buffer buf = fsl_buffer_empty;
fsl_db * db = fsl_cx_db_repo(f);
char const dryRun = fcli.fDryRun || fcli_flag("n", NULL);
char const allowNew = fcli_flag("new", NULL);
char pageExists;
fcli_flag2("f", "file", &iFile);
fcli_flag2("t", "mime-type", &mimeType);
if(fcli_has_unused_flags(1)){
rc = FSL_RC_MISUSE;
goto end;
}
if(!pName && !(pName = fcli_next_arg(1))){
rc = fcli_err_set(FSL_RC_MISUSE,
"Expecting wiki page name argument.");
goto end;
}
if(!iFile && !(iFile = fcli_next_arg(1))){
rc = fcli_err_set(FSL_RC_MISUSE,
"Expecting input file name.");
goto end;
}
rc = fsl_buffer_fill_from_filename(&buf, iFile);
if(rc){
rc = fcli_err_set(rc, "Error opening file: %s", iFile);
goto end;
}
pageExists = fsl_wiki_page_exists(f, pName);
if(!pageExists && !allowNew){
rc = fcli_err_set(FSL_RC_NOT_FOUND,
"Page [%s] does not exists. "
"Use --new to allow "
"creation of new pages.", pName);
goto end;
}
fsl_db_transaction_begin(db);
rc = fsl_wiki_save(f, pName, &buf, fcli.userName,
mimeType, 1);
if(!rc && dryRun){
f_out("Dry-run mode _not_ saving changes.\n");
}
fsl_db_transaction_end(db, dryRun ? 1 : 0);
end:
if(pName != WikiApp.pageName) fsl_free(pName);
fsl_free(iFile);
fsl_free(mimeType);
fsl_buffer_clear(&buf);
return rc;
}
static FossilCommand aCommandsWiki[] = {
{"export", fcmd_wiki_export},
{"ls", fcmd_wiki_list},
{"save", fcmd_wiki_save},
{NULL,NULL}/*empty sentinel is required by traversal algos*/
};
static void WikiApp_atexit(){
fsl_free(WikiApp.pageName);
WikiApp.pageName = NULL;
}
int main(int argc, char * const * argv ){
int rc;
fcli.appHelp = fcli_local_help;
rc = fcli_setup(argc, argv);
if(FSL_RC_BREAK==rc) /* --help */ return 0;
else if(!rc){
if(!fsl_cx_db_repo(fcli.f)){
rc = fcli_err_set(FSL_RC_MISUSE,
"This app requires a repository db.");
}
else{
atexit(WikiApp_atexit);
fcli_flag2("p", "page", &WikiApp.pageName);
rc = fcli_dispatch_commands(aCommandsWiki, 0);
}
}
/* fsl_free(symToTag); */
return (fcli_err_report(0)||rc) ? EXIT_FAILURE : EXIT_SUCCESS;
}