Initial sources
This commit is contained in:
parent
a96993ac4d
commit
84d73a69d7
56
Makefile
Normal file
56
Makefile
Normal file
@ -0,0 +1,56 @@
|
||||
|
||||
MAKE=gmake
|
||||
|
||||
DEFS=
|
||||
|
||||
SRCS= usqlite.c
|
||||
|
||||
HDRS=
|
||||
|
||||
OBJS= $(SRCS:.c=.o)
|
||||
CC=gcc
|
||||
#CC=mipsel-linux-gcc
|
||||
DEFS=-D_XOPEN_SOURCE=500 -DTGT_SQLITE_VERSION_NUMBER=3007002
|
||||
CFLAGS=-g -std=c99
|
||||
INCS=
|
||||
LIBS=
|
||||
#CFLAGS=-g -O3 -fno-strict-aliasing
|
||||
#WARN=-pedantic -Wall -Wnested-externs -Wpointer-arith -Werror -Wno-unused
|
||||
WARN=-pedantic -Wall -W -Wnested-externs -Wpointer-arith -Wno-long-long
|
||||
|
||||
PLATFORM=$(shell uname -s | cut -d- -f1)
|
||||
ifeq ($(PLATFORM),Linux)
|
||||
LIBS=-lsqlite3
|
||||
endif
|
||||
|
||||
all: usqlite
|
||||
|
||||
install: usqlite
|
||||
strip $<
|
||||
cp $< /mod/bin/
|
||||
|
||||
usqlite: ${OBJS}
|
||||
@echo "Linking..."
|
||||
@-[ -f $@ ] && mv $@ $@~ || exit 0
|
||||
${CC} -static-libgcc \
|
||||
${WARN} \
|
||||
${DEFS} \
|
||||
${CFLAGS} -o $@ \
|
||||
${OBJS} \
|
||||
${LIBS}
|
||||
@echo "Done..."
|
||||
|
||||
clean:
|
||||
@-touch core
|
||||
rm -f usqlite usqlite~ core ${OBJS}
|
||||
|
||||
#tags:
|
||||
# @-ctags *.[ch] 2>> /dev/null
|
||||
|
||||
.c.o:
|
||||
@echo " $<"
|
||||
@$(CC) $(CFLAGS) ${WARN} ${DEFS} ${INCS} -c $< -o $@
|
||||
|
||||
${OBJS}: ${HDRS}
|
||||
|
||||
|
473
usqlite.c
Normal file
473
usqlite.c
Normal file
@ -0,0 +1,473 @@
|
||||
/* printf, scanf, stdin, stderr */
|
||||
#include <stdio.h>
|
||||
/* malloc, free */
|
||||
#include <stdlib.h>
|
||||
/* size_t */
|
||||
#include <sys/types.h>
|
||||
/* strdup, strtok_r, strcmp, strncmp */
|
||||
#include <string.h>
|
||||
/* basename */
|
||||
#include <libgen.h>
|
||||
/* sqlite3_* */
|
||||
#include <sqlite3.h>
|
||||
|
||||
/* ABI version required by the code */
|
||||
#ifndef REQD_SQLITE_VERSION_NUMBER
|
||||
#define REQD_SQLITE_VERSION_NUMBER 3007015
|
||||
#endif
|
||||
/* ABI version available on target platform */
|
||||
#ifndef TGT_SQLITE_VERSION_NUMBER
|
||||
#define TGT_SQLITE_VERSION_NUMBER SQLITE_VERSION_NUMBER
|
||||
#endif
|
||||
|
||||
static const char *zVersionNo = "0.3 (" __DATE__ " " __TIME__ ")";
|
||||
static int fDbg = 0;
|
||||
static int fCols = 0;
|
||||
static char *zColSep = "|";
|
||||
|
||||
#if 0
|
||||
/* readlink */
|
||||
#include <unistd.h>
|
||||
/* PAtH_MAX */
|
||||
#include <limits.h>
|
||||
|
||||
static int get_pathname(FILE *f, char *buf, size_t bufsiz)
|
||||
{
|
||||
char fnmbuf[sizeof "/proc/self/fd/0123456789"];
|
||||
size_t nr;
|
||||
if (sizeof "/proc/self/fd/0" - 1 > snprintf(fnmbuf, sizeof fnmbuf, "/proc/self/fd/%d", fileno(f)) ||
|
||||
0 > (nr = readlink(fnmbuf, buf, bufsiz)) || nr >= bufsiz) {
|
||||
return -1;
|
||||
}
|
||||
buf[nr]='\0';
|
||||
return nr;
|
||||
}
|
||||
|
||||
static int line_callback(void *parm, int argc, char **argv, char **azColName) {
|
||||
int i;
|
||||
(void *) parm;
|
||||
|
||||
for (i=0; i<argc; i++) {
|
||||
printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
|
||||
}
|
||||
printf("\n");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* provide missing functions from newer ABI if needed */
|
||||
#if (TGT_SQLITE_VERSION_NUMBER < REQD_SQLITE_VERSION_NUMBER)
|
||||
#ifndef ArraySize
|
||||
#define ArraySize(a) ((int)(sizeof(a) / sizeof(*(a))))
|
||||
#endif
|
||||
#ifndef ALWAYS
|
||||
#define ALWAYS(a) (a)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Return a static string that describes the kind of error specified in the
|
||||
** argument.
|
||||
*/
|
||||
static const char *sqlite3ErrStr(int rc){
|
||||
static const char* const aMsg[] = {
|
||||
/* SQLITE_OK */ "not an error",
|
||||
/* SQLITE_ERROR */ "SQL logic error",
|
||||
/* SQLITE_INTERNAL */ 0,
|
||||
/* SQLITE_PERM */ "access permission denied",
|
||||
/* SQLITE_ABORT */ "query aborted",
|
||||
/* SQLITE_BUSY */ "database is locked",
|
||||
/* SQLITE_LOCKED */ "database table is locked",
|
||||
/* SQLITE_NOMEM */ "out of memory",
|
||||
/* SQLITE_READONLY */ "attempt to write a readonly database",
|
||||
/* SQLITE_INTERRUPT */ "interrupted",
|
||||
/* SQLITE_IOERR */ "disk I/O error",
|
||||
/* SQLITE_CORRUPT */ "database disk image is malformed",
|
||||
/* SQLITE_NOTFOUND */ "unknown operation",
|
||||
/* SQLITE_FULL */ "database or disk is full",
|
||||
/* SQLITE_CANTOPEN */ "unable to open database file",
|
||||
/* SQLITE_PROTOCOL */ "locking protocol",
|
||||
/* SQLITE_EMPTY */ 0,
|
||||
/* SQLITE_SCHEMA */ "database schema has changed",
|
||||
/* SQLITE_TOOBIG */ "string or blob too big",
|
||||
/* SQLITE_CONSTRAINT */ "constraint failed",
|
||||
/* SQLITE_MISMATCH */ "datatype mismatch",
|
||||
/* SQLITE_MISUSE */ "bad parameter or other API misuse",
|
||||
#ifdef SQLITE_DISABLE_LFS
|
||||
/* SQLITE_NOLFS */ "large file support is disabled",
|
||||
#else
|
||||
/* SQLITE_NOLFS */ 0,
|
||||
#endif
|
||||
/* SQLITE_AUTH */ "authorization denied",
|
||||
/* SQLITE_FORMAT */ 0,
|
||||
/* SQLITE_RANGE */ "column index out of range",
|
||||
/* SQLITE_NOTADB */ "file is not a database",
|
||||
/* SQLITE_NOTICE */ "notification message",
|
||||
/* SQLITE_WARNING */ "warning message",
|
||||
};
|
||||
const char *zErr = "unknown error";
|
||||
switch( rc ){
|
||||
case SQLITE_ABORT_ROLLBACK: {
|
||||
zErr = "abort due to ROLLBACK";
|
||||
break;
|
||||
}
|
||||
case SQLITE_ROW: {
|
||||
zErr = "another row available";
|
||||
break;
|
||||
}
|
||||
case SQLITE_DONE: {
|
||||
zErr = "no more rows available";
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
rc &= 0xff;
|
||||
if( ALWAYS(rc>=0) && rc<ArraySize(aMsg) && aMsg[rc]!=0 ){
|
||||
zErr = aMsg[rc];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return zErr;
|
||||
}
|
||||
#define sqlite3_errstr(a) sqlite3ErrStr(a)
|
||||
#endif
|
||||
|
||||
static void ok(void) {
|
||||
fprintf( stderr, "OK\n");
|
||||
}
|
||||
|
||||
static void eof(void) {
|
||||
fprintf( stderr, "EOF on input\n");
|
||||
}
|
||||
|
||||
static int col_callback(void *parm, int argc, char **argv, char **azColName) {
|
||||
|
||||
if (parm && *(int*)parm) {
|
||||
char ** azHeader = (char **) malloc(argc*sizeof(char *));
|
||||
for (int i=0; i<argc; i++) {
|
||||
printf("%s%s", (i > 0? zColSep:""), azColName[i]);
|
||||
azHeader[i] = strdup(azColName[i]);
|
||||
memset(azHeader[i],'=',strlen(azHeader[i]));
|
||||
}
|
||||
printf("\n");
|
||||
for (int i=0; i<argc; i++) {
|
||||
printf("%s%s", (i > 0? " ":""), azHeader[i]);
|
||||
free(azHeader[i]);
|
||||
}
|
||||
free(azHeader);
|
||||
printf("\n");
|
||||
*(int*)parm = 0;
|
||||
}
|
||||
|
||||
for (int i=0; i<argc; i++) {
|
||||
printf("%s%s", (i > 0? zColSep:""), argv[i]);
|
||||
}
|
||||
printf("\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sql_exec( sqlite3 * db, const char *sql ) {
|
||||
char *zErrMsg = 0;
|
||||
int cb = /*fDbg ||*/ fCols;
|
||||
|
||||
if (fDbg) fprintf( stderr, "About to run SQL ---\n%s\n---\n", sql);
|
||||
int rc = sqlite3_exec(db, sql, col_callback, &cb, &zErrMsg);
|
||||
if ( rc != SQLITE_OK ) {
|
||||
fprintf(stderr, "SQL error: %s\n", zErrMsg);
|
||||
sqlite3_free(zErrMsg);
|
||||
return 1;
|
||||
}
|
||||
else if (fDbg) ok();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmdline_exec( sqlite3 *db, int argc, char *argv[]) {
|
||||
int rc = 0;
|
||||
|
||||
for (int ii=1; ii < argc; ++ii) {
|
||||
rc = sql_exec(db, argv[ii]);
|
||||
if ( rc != 0 ) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int stream_exec( FILE * ff, sqlite3 *db) {
|
||||
size_t bsiz = 4096;
|
||||
char * zStmt = (char *)malloc(bsiz);
|
||||
size_t nStmt;
|
||||
size_t nStmtEnd = 0;
|
||||
int rc = 0;
|
||||
/*
|
||||
* escaping " %n%<size>[^;]%n%c%n":
|
||||
* maybe trim whitespace, non-; string; possible ;
|
||||
*/
|
||||
const char fmt[] = "%s%%n%%%u[^;]%%n%%c%%n";
|
||||
char sfmt[32];
|
||||
|
||||
/* alloc failed? */
|
||||
if (!zStmt) return 1;
|
||||
/* empty text */
|
||||
*zStmt = '\0';
|
||||
for (char *pStmt = zStmt; rc == 0; pStmt += (nStmtEnd > 0? nStmtEnd: nStmt)) {
|
||||
/* if the allocation is full (include final \0), increase */
|
||||
if (pStmt+1 >= zStmt+bsiz) {
|
||||
char *zz = (char *)sqlite3_realloc(zStmt,bsiz+=4096);
|
||||
if (!zz) {
|
||||
rc = 1; break;
|
||||
}
|
||||
pStmt = zz + (pStmt - zStmt);
|
||||
zStmt = zz;
|
||||
}
|
||||
/* set the text field width, allowing for extra \0 */
|
||||
rc = snprintf( sfmt, sizeof sfmt, fmt, (pStmt==zStmt? " ":""), bsiz-(pStmt-zStmt)-1 );
|
||||
if (rc < 0 || rc > (int)(sizeof sfmt)) {
|
||||
if (fDbg) fprintf( stderr, "Can't set format to read SQL: rc=%d\n", rc);
|
||||
rc = 1; break;
|
||||
}
|
||||
nStmt = nStmtEnd = 0;
|
||||
/* reading possible non-; text followed by possible ; */
|
||||
char scln;
|
||||
rc = fscanf( ff, ";%n", (int *) &nStmtEnd );
|
||||
if (rc >= 0) {
|
||||
if (nStmtEnd == 0) { /* not an empty statement */
|
||||
int nStart = 0;
|
||||
rc = fscanf( ff, sfmt, &nStart, pStmt, &nStmt, &scln, &nStmtEnd );
|
||||
if (rc > 0) {
|
||||
/* nStmt may randomly be set to 1024 - why?? */
|
||||
if (zStmt[0]) {
|
||||
if (fDbg) fprintf(stderr, "Adjusting nStmt = %u", nStmt);
|
||||
nStmt = strlen(pStmt);
|
||||
if (fDbg) fprintf(stderr, " to %u\n", nStmt);
|
||||
}
|
||||
else if (nStmtEnd > nStmt) {
|
||||
nStmt -= nStart;
|
||||
}
|
||||
nStmtEnd -= nStart;
|
||||
}
|
||||
}
|
||||
else
|
||||
scln = ';';
|
||||
}
|
||||
/* err, EOF or read nothing: fail on error */
|
||||
if (rc <= 0 && ferror(ff)) {
|
||||
if (fDbg) {
|
||||
if (EOF == rc)
|
||||
eof();
|
||||
else
|
||||
fprintf( stderr, "SQL read error: rc=%d\n", rc);
|
||||
}
|
||||
rc = 1;
|
||||
break;
|
||||
}
|
||||
if (rc == EOF) {
|
||||
if (zStmt[0]) {
|
||||
/* read some stuff but no ; ? */
|
||||
scln = ';';
|
||||
nStmtEnd = nStmt+1;
|
||||
if (fDbg) fprintf( stderr, "Faking final ; for unterminated input\n");
|
||||
}
|
||||
else {
|
||||
if (fDbg) eof();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fDbg) fprintf( stderr, "Read %u characters of SQL (rc=%d)\n", nStmt, rc);
|
||||
|
||||
/* read or faked a possible ; - add it */
|
||||
if (nStmtEnd > nStmt) {
|
||||
if (fDbg) fprintf( stderr, "Adding final ;\n");
|
||||
pStmt[nStmt] = scln;
|
||||
pStmt[nStmtEnd] = '\0';
|
||||
}
|
||||
/* no final ;, try again */
|
||||
if (nStmtEnd == 0 || pStmt[nStmt] != ';') {
|
||||
if (fDbg) fprintf( stderr, "No final ;: reading more\n");
|
||||
if (rc > 0) rc = 0; continue;
|
||||
}
|
||||
/* potential command: what does sqlite say? -> 1, 0, SQLITE_NOMEM */
|
||||
if (fDbg) fprintf( stderr, "About to check SQL ---\n%s\n---\n", zStmt);
|
||||
int src = sqlite3_complete(zStmt);
|
||||
switch (src) {
|
||||
case 1: { /* complete-ish - try it */
|
||||
if (fDbg) ok();
|
||||
src = sql_exec(db, zStmt);
|
||||
if (rc >= 0) rc = src;
|
||||
if (src != 0) break;
|
||||
|
||||
/* now ready for the next one */
|
||||
pStmt = zStmt;
|
||||
nStmt = nStmtEnd = 0;
|
||||
zStmt[0] = '\0';
|
||||
break;
|
||||
}
|
||||
|
||||
case 0: { /* incomplete - get more */
|
||||
int nn = 0;
|
||||
sscanf( zStmt, " ;%n", &nn);
|
||||
if (nn == 0) {
|
||||
/* non-empty statement */
|
||||
if (fDbg) fprintf( stderr, "Incomplete, reading more\n");
|
||||
rc = src;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (fDbg) fprintf( stderr, "Error: %s\n", sqlite3_errstr(src));
|
||||
rc = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(zStmt);
|
||||
|
||||
return rc == EOF? 0: rc;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int strcmpany( const char* str, const char* strtest[] ) {
|
||||
for (size_t i = 0; strtest[i]; ++i) {
|
||||
if (0 == strcmp(str, strtest[i])) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int optcmp( const char* str, const char* strtest[], char** val ) {
|
||||
|
||||
for (size_t i = 0; strtest[i]; ++i) {
|
||||
if (strtest[i][1] == '-') { /* --opt */
|
||||
if (val && str[1] == '-') { /* expecting --opt= */
|
||||
char *save = NULL;
|
||||
char *tmpStr = strdup(str);
|
||||
if (tmpStr && (0 == strcmp(strtok_r(tmpStr, "=", &save), strtest[i]))) {
|
||||
*val = strtok_r( NULL, "=", &save);
|
||||
return 0;
|
||||
}
|
||||
free(tmpStr);
|
||||
}
|
||||
else if (0 == strcmp(str, strtest[i])) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else { /* -o */
|
||||
if (0 == strncmp(str, strtest[i], 2)) {
|
||||
if (val) *val = strdup(str+2);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void usage(const char* progname) {
|
||||
fprintf( stderr, "Usage: %s [OPTIONS] [\"SQL command;\" ...] [< SQL_script]\n", progname);
|
||||
}
|
||||
|
||||
static void help(const char* progname) {
|
||||
usage(progname);
|
||||
fprintf( stderr, "\nExecute SQL commands on SQLite databases\n\n"
|
||||
"Options:\n"
|
||||
" -C, --show-columns Show column names (if any) as headers of \n"
|
||||
" query results\n"
|
||||
" -S, String to separate columns in query \n"
|
||||
" --column-separator=SEP results (default \"|\")\n"
|
||||
" -v, --verbose Log details to stderr\n"
|
||||
" -V, --version, Print program version details to stderr\n"
|
||||
" -- Subsequent parameters are not options\n"
|
||||
" -h, --help, -? Print this help to stderr and exit\n"
|
||||
"\nTo cancel reading from stdin in a POSIX shell, use the input \nredirection \"<&-\".\n" );
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
const char zDummyDB[] = ":memory:";
|
||||
const char *zHelp[] = { "--help", "-h", "-?", 0 };
|
||||
const char *zVersion[] = { "--version", "-V", 0 };
|
||||
const char *zVerbose[] = { "--verbose", "-v", 0 };
|
||||
const char *zCols[] = { "--show-columns", "-C", 0 };
|
||||
const char *zColSepOpt[] = { "--column-separator", "-S", 0 };
|
||||
const char *zCont[] = { "--", 0 };
|
||||
const char *zProgName = argv[0];
|
||||
|
||||
/* process options */
|
||||
while (argc >= 2 && (*(argv[1]) == '-')) {
|
||||
char * tmpVal = NULL;
|
||||
if (0 == optcmp(argv[1], zHelp, NULL)) {
|
||||
help(zProgName);
|
||||
return 0;
|
||||
}
|
||||
else if (0 == optcmp(argv[1], zCols, NULL)) {
|
||||
fCols = 1;
|
||||
}
|
||||
else if (0 == optcmp(argv[1], zColSepOpt, &tmpVal)) {
|
||||
if (tmpVal && *tmpVal) {
|
||||
zColSep = tmpVal;
|
||||
}
|
||||
else if ((argv[1][1] != '-') && (argc > 2)) {
|
||||
tmpVal = zColSep = argv[2];
|
||||
--argc; ++argv;
|
||||
}
|
||||
if (!(tmpVal && *tmpVal)) {
|
||||
fprintf( stderr, "Bad or empty separator option value: %s\n", argv[1]);
|
||||
}
|
||||
}
|
||||
else if (0 == optcmp(argv[1], zVersion, NULL)) {
|
||||
/* basename() may modify its argument */
|
||||
fprintf( stderr, "%s SQLite exec wrapper version %s\n",
|
||||
basename(strdup(zProgName)), zVersionNo);
|
||||
}
|
||||
else if (0 == optcmp(argv[1], zVerbose, NULL)) {
|
||||
fDbg = 1;
|
||||
}
|
||||
else if (0 == optcmp(argv[1], zCont, NULL)) {
|
||||
--argc; ++argv;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
fprintf( stderr, "Unknown option: %s\n", argv[1]);
|
||||
}
|
||||
/* single-char option with no value: next char may be an option */
|
||||
if ((argv[1][1] != '-') && !(tmpVal && *tmpVal) && argv[1][2]) {
|
||||
(argv[1])++;
|
||||
argv[1][0] = '-';
|
||||
}
|
||||
else {
|
||||
--argc; ++argv;
|
||||
}
|
||||
}
|
||||
|
||||
if (fDbg) {
|
||||
fprintf( stderr, "Options: %s", zVerbose[0]);
|
||||
if (fCols) fprintf( stderr, " %s", zCols[0]);
|
||||
fprintf( stderr, " separator = '%s'\n", zColSep);
|
||||
}
|
||||
|
||||
if (fDbg) fprintf( stderr, "About to configure SQLITE_CONFIG_SINGLETHREAD\n");
|
||||
int rc = sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);
|
||||
if (SQLITE_OK != rc) {
|
||||
fprintf( stderr, "Can't set single-thread: %s\n", sqlite3_errstr(rc));
|
||||
return 1;
|
||||
}
|
||||
else if (fDbg) ok();
|
||||
|
||||
|
||||
sqlite3 *db;
|
||||
if (fDbg) fprintf( stderr, "About to open dummy database %s\n", zDummyDB);
|
||||
rc = sqlite3_open(zDummyDB, &db);
|
||||
if (SQLITE_OK != rc) {
|
||||
fprintf(stderr, "Can't open database: %s\n", sqlite3_errstr(rc));
|
||||
}
|
||||
else {
|
||||
if (fDbg) ok();
|
||||
|
||||
rc = cmdline_exec( db, argc, argv);
|
||||
|
||||
if (rc == 0)
|
||||
rc = stream_exec( stdin, db);
|
||||
}
|
||||
|
||||
sqlite3_close(db);
|
||||
return rc;
|
||||
}
|
Loading…
Reference in New Issue
Block a user