#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <dirent.h>
#include "sqlite3.h"
static void readfileFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const char *zName;
FILE *in;
long nIn;
void *pBuf;
zName = (const char*)sqlite3_value_text(argv[0]);
if( zName==0 ) return;
in = fopen(zName, "rb");
if( in==0 ) return;
fseek(in, 0, SEEK_END);
nIn = ftell(in);
rewind(in);
pBuf = sqlite3_malloc( nIn );
if( pBuf && 1==fread(pBuf, nIn, 1, in) ){
sqlite3_result_text(context, pBuf, nIn, sqlite3_free);
}else{
sqlite3_free(pBuf);
}
fclose(in);
}
static void showHelp(const char *zArgv0){
printf("\n"
"Usage: %s SWITCHES... DB\n"
"\n"
" This program opens the database named on the command line and attempts to\n"
" create an FTS table named \"fts\" with a single column. If successful, it\n"
" recursively traverses the directory named by the -dir option and inserts\n"
" the contents of each file into the fts table. All files are assumed to\n"
" contain UTF-8 text.\n"
"\n"
"Switches are:\n"
" -fts [345] FTS version to use (default=5)\n"
" -idx [01] Create a mapping from filename to rowid (default=0)\n"
" -dir <path> Root of directory tree to load data from (default=.)\n"
" -trans <integer> Number of inserts per transaction (default=1)\n"
, zArgv0
);
exit(1);
}
static void error_out(const char *zText){
fprintf(stderr, "%s: %s\n", zText, strerror(errno));
exit(-1);
}
static void sqlite_error_out(const char *zText, sqlite3 *db){
fprintf(stderr, "%s: %s\n", zText, sqlite3_errmsg(db));
exit(-1);
}
typedef struct VisitContext VisitContext;
struct VisitContext {
int nRowPerTrans;
sqlite3 *db;
sqlite3_stmt *pInsert;
};
void visit_file(void *pCtx, const char *zPath){
int rc;
VisitContext *p = (VisitContext*)pCtx;
sqlite3_bind_text(p->pInsert, 1, zPath, -1, SQLITE_STATIC);
sqlite3_step(p->pInsert);
rc = sqlite3_reset(p->pInsert);
if( rc!=SQLITE_OK ){
sqlite_error_out("insert", p->db);
}else if( p->nRowPerTrans>0
&& (sqlite3_last_insert_rowid(p->db) % p->nRowPerTrans)==0
){
sqlite3_exec(p->db, "COMMIT ; BEGIN", 0, 0, 0);
}
}
static void traverse(
const char *zDir,
void *pCtx,
void (*xCallback)(void*, const char *zPath)
){
DIR *d;
struct dirent *e;
d = opendir(zDir);
if( d==0 ) error_out("opendir()");
for(e=readdir(d); e; e=readdir(d)){
if( strcmp(e->d_name, ".")==0 || strcmp(e->d_name, "..")==0 ) continue;
char *zPath = sqlite3_mprintf("%s/%s", zDir, e->d_name);
if (e->d_type & DT_DIR) {
traverse(zPath, pCtx, xCallback);
}else{
xCallback(pCtx, zPath);
}
sqlite3_free(zPath);
}
closedir(d);
}
int main(int argc, char **argv){
int iFts = 5;
int bMap = 0;
const char *zDir = ".";
int i;
int rc;
int nRowPerTrans = 0;
sqlite3 *db;
char *zSql;
VisitContext sCtx;
int nCmd = 0;
char **aCmd = 0;
if( argc % 2 ) showHelp(argv[0]);
for(i=1; i<(argc-1); i+=2){
char *zOpt = argv[i];
char *zArg = argv[i+1];
if( strcmp(zOpt, "-fts")==0 ){
iFts = atoi(zArg);
if( iFts!=3 && iFts!=4 && iFts!= 5) showHelp(argv[0]);
}
else if( strcmp(zOpt, "-trans")==0 ){
nRowPerTrans = atoi(zArg);
}
else if( strcmp(zOpt, "-idx")==0 ){
bMap = atoi(zArg);
if( bMap!=0 && bMap!=1 ) showHelp(argv[0]);
}
else if( strcmp(zOpt, "-dir")==0 ){
zDir = zArg;
}
else if( strcmp(zOpt, "-special")==0 ){
nCmd++;
aCmd = sqlite3_realloc(aCmd, sizeof(char*) * nCmd);
aCmd[nCmd-1] = zArg;
}
else{
showHelp(argv[0]);
}
}
rc = sqlite3_open(argv[argc-1], &db);
if( rc!=SQLITE_OK ) sqlite_error_out("sqlite3_open()", db);
rc = sqlite3_create_function(db, "readtext", 1, SQLITE_UTF8, 0,
readfileFunc, 0, 0);
if( rc!=SQLITE_OK ) sqlite_error_out("sqlite3_create_function()", db);
zSql = sqlite3_mprintf("CREATE VIRTUAL TABLE fts USING fts%d(content)", iFts);
rc = sqlite3_exec(db, zSql, 0, 0, 0);
if( rc!=SQLITE_OK ) sqlite_error_out("sqlite3_exec(1)", db);
sqlite3_free(zSql);
for(i=0; i<nCmd; i++){
zSql = sqlite3_mprintf("INSERT INTO fts(fts) VALUES(%Q)", aCmd[i]);
rc = sqlite3_exec(db, zSql, 0, 0, 0);
if( rc!=SQLITE_OK ) sqlite_error_out("sqlite3_exec(1)", db);
sqlite3_free(zSql);
}
memset(&sCtx, 0, sizeof(VisitContext));
sCtx.db = db;
sCtx.nRowPerTrans = nRowPerTrans;
rc = sqlite3_prepare_v2(db,
"INSERT INTO fts VALUES(readtext(?))", -1, &sCtx.pInsert, 0
);
if( rc!=SQLITE_OK ) sqlite_error_out("sqlite3_prepare_v2(1)", db);
if( sCtx.nRowPerTrans>0 ) sqlite3_exec(db, "BEGIN", 0, 0, 0);
traverse(zDir, (void*)&sCtx, visit_file);
if( sCtx.nRowPerTrans>0 ) sqlite3_exec(db, "COMMIT", 0, 0, 0);
sqlite3_finalize(sCtx.pInsert);
sqlite3_close(db);
sqlite3_free(aCmd);
return 0;
}