Commit fe984b91 by Konstantin Käfer

cleanup repository

parent 17cf082b
var sqlite3 = require('./lib/sqlite3');
var db = new sqlite3.Database(':memory:');
db.prepare("CREATE TABLE foo (text bar)").run().finalize(function() {
db.prepare("INSERT INTO foo VALUES('bar baz boo')").run().finalize(function() {
db.prepare("SELECT * FROM foo").run(function(err, row) {
console.log(row);
}).run(function(err, row) {
console.log(row);
});
});
});
2006-05-31 Gray Watson <>
* Version 2.1.0 released.
* Added MPOOL_ERROR_PNT_OVER to distinguish between pointer
overwrite and mpool structure overwrite.
2005-05-20 Gray Watson <>
* Version 2.0.0 released.
* First external publication of library.
Thu Mar 4 10:14:21 1999 Gray Watson <>
* Reworked the way the blocks were split up.
* Removed the round arguments. Not used.
Wed Mar 3 19:29:38 1999 Gray Watson <>
* Moved to random(). Fucking rand() was hiding a lot of problems
from me.
* Added some additional sanity checks in free().
* Added mpool_set_max_pages to the library.
Thu Feb 25 12:41:51 1999 Gray Watson <>
* Added log_function transaction callback.
Thu Feb 25 09:53:33 1999 Gray Watson <>
* Changed the default page size to 16 * getpagesize.
Wed Feb 24 17:52:52 1999 Gray Watson <>
* Major reworking of internals to simplify structures.
Fri Feb 19 12:52:55 1999 Gray Watson <>
* Made a number of changes to the internals which removed the
addr_to_block as a performance pig.
Tue Feb 16 21:11:23 1999 Gray Watson <>
* Added ability for free to look up in the free bit lists for
memory to use.
* Added mpool_clear. Good idea.
Thu Feb 11 02:53:45 1999 Gray Watson <>
* Finally a working version. Looks much better.
* Added rounding sizes so it will allocate aligned memory.
* Added minimum size to the mpool_free function to speed it up.
Wed Feb 10 23:30:48 1999 Gray Watson <>
* Version 1 with new fine grained memory resolution almost
working.
Fri May 2 02:26:28 1997 Gray Watson <>
* Moved to MAP_PRIVATE from MAP_SHARED.
* Fixed the min/max handling.
* Added additional info to mpool_stat.
Thu May 1 16:51:06 1997 Gray Watson <>
* Added page-size information request.
* Added better no-memory errors.
Thu Apr 24 01:58:41 1997 Gray Watson <>
* Added handling of null for debugging purposes.
Mon Apr 14 03:31:26 1997 Gray Watson <>
* Started the mpool routines.
#
# $Id: Makefile.all,v 1.1.1.1 2005/05/20 19:58:29 gray Exp $
#
HFLS = mpool.h
OBJS = mpool.o
CC = gcc
CFLAGS = -g -I. $(DEFINES) -fPIC
#CFLAGS = -g -I.
LDFLAGS =
RANLIB = ranlib
DESTDIR = /usr/local
TEST = mpool_t
LIBRARY = libmpool.a
all : $(LIBRARY) $(UTIL)
clean :
rm -f a.out core *.o *.t
rm -f $(LIBRARY) $(TEST)
install : $(HFLS) $(LIBRARY)
install -c -m 444 $(HFLS) $(DESTDIR)/include
install -c -m 444 $(LIBRARY) $(DESTDIR)/lib
$(RANLIB) $(DESTDIR)/libo/$(LIBRARY)
$(LIBRARY) : $(OBJS)
ar cr $(LIBRARY) $?
$(RANLIB) $@
tests : $(TEST)
$(TEST) : $(TEST).o $(LIBRARY)
rm -f $@
$(CC) $(LDFLAGS) $(TEST).o $(LIBRARY)
mv a.out $@
$(UTIL) : $(UTIL).o $(LIBRARY)
rm -f $@
$(CC) $(LDFLAGS) $(UTIL).o $(LIBRARY)
mv a.out $@
.c.o :
rm -f $@
$(CC) $(CFLAGS) -c $< -o $@
#
# Below are dependencies that are automatically generated by make
# depend. Please do not edit by hand.
#
mpool.o: mpool.c mpool.h mpool_loc.h
mpool_t.o: mpool_t.c mpool.h
-------------------------------------------------------------------------------
$Id: NEWS,v 1.2 2006/05/31 20:28:31 gray Exp $
-------------------------------------------------------------------------------
Version 2.1.0:
* Added MPOOL_ERROR_PNT_OVER to show pointer overwrites.
Version 2.0.0:
* Initial external release of library after use since 1996.
-------------------------------------------------------------------------------
$Id: README,v 1.2 2005/05/22 19:49:30 gray Exp $
-------------------------------------------------------------------------------
BACKGROUND:
This is a memory pool library which was written to allow a program to
have heaps that it could destroy without fragmenting memory. You can
have multiple heaps and reset them easily completely reclaiming the
memory (as opposed to standard heaps).
With it you can mpool_open() a new heap, then mpool_alloc(),
mpool_calloc(), mpool_realloc(), mpool_free() to your heart's content.
Once you are done with the memory-pool you can run mpool_clear() or
mpool_close() and completely remove the memory associated with the
pools. This is very handy if you are working with some large blocks
of memory and want to reset back to a clean state.
Check out the mpool.h file for more information. Sorry for minimal
docs.
-------------------------------------------------------------------------------
INSTALLATION:
1) Typing 'make' should be enough to build libskip.a.
2) Typing 'make tests' should make the mpool_t test program.
-------------------------------------------------------------------------------
REPOSITORY:
The newest versions of the library are available from:
http://256.com/sources/mpool/
-------------------------------------------------------------------------------
AUTHOR:
If you have any questions or problems feel free to send me mail.
Gray Watson
http://256.com/gray/
-------------------------------------------------------------------------------
/*
* Memory pool routines.
*
* Copyright 1996 by Gray Watson.
*
* This file is part of the mpool package.
*
* Permission to use, copy, modify, and distribute this software for
* any purpose and without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies, and that the name of Gray Watson not be used in advertising
* or publicity pertaining to distribution of the document or software
* without specific, written prior permission.
*
* Gray Watson makes no representations about the suitability of the
* software described herein for any purpose. It is provided "as is"
* without express or implied warranty.
*
* The author may be reached via http://256.com/gray/
*
* $Id: mpool.c,v 1.5 2006/05/31 20:28:31 gray Exp $
*/
/*
* Memory-pool allocation routines. I got sick of the GNU mmalloc
* library which was close to what we needed but did not exactly do
* what I wanted.
*
* The following uses mmap from /dev/zero. It allows a number of
* allocations to be made inside of a memory pool then with a clear or
* close the pool can be reset without any memory fragmentation and
* growth problems.
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#ifdef DMALLOC
#include "dmalloc.h"
#endif
#define MPOOL_MAIN
#include "mpool.h"
#include "mpool_loc.h"
#ifdef __GNUC__
#ident "$Id: mpool.c,v 1.5 2006/05/31 20:28:31 gray Exp $"
#else
static char *rcs_id = "$Id: mpool.c,v 1.5 2006/05/31 20:28:31 gray Exp $";
#endif
/* version */
static char *version = "mpool library version 2.1.0";
/* local variables */
static int enabled_b = 0; /* lib initialized? */
static unsigned int min_bit_free_next = 0; /* min size of next pnt */
static unsigned int min_bit_free_size = 0; /* min size of next + size */
static unsigned long bit_array[MAX_BITS + 1]; /* size -> bit */
/****************************** local utilities ******************************/
/*
* static void startup
*
* DESCRIPTION:
*
* Perform any library level initialization.
*
* RETURNS:
*
* None.
*
* ARGUMENTS:
*
* None.
*/
static void startup(void)
{
int bit_c;
unsigned long size = 1;
if (enabled_b) {
return;
}
/* allocate our free bit array list */
for (bit_c = 0; bit_c <= MAX_BITS; bit_c++) {
bit_array[bit_c] = size;
/*
* Note our minimum number of bits that can store a pointer. This
* is smallest address that we can have a linked list for.
*/
if (min_bit_free_next == 0 && size >= sizeof(void *)) {
min_bit_free_next = bit_c;
}
/*
* Note our minimum number of bits that can store a pointer and
* the size of the block.
*/
if (min_bit_free_size == 0 && size >= sizeof(mpool_free_t)) {
min_bit_free_size = bit_c;
}
size *= 2;
}
enabled_b = 1;
}
/*
* static int size_to_bits
*
* DESCRIPTION:
*
* Calculate the number of bits in a size.
*
* RETURNS:
*
* Number of bits.
*
* ARGUMENTS:
*
* size -> Size of memory of which to calculate the number of bits.
*/
static int size_to_bits(const unsigned long size)
{
int bit_c = 0;
for (bit_c = 0; bit_c <= MAX_BITS; bit_c++) {
if (size <= bit_array[bit_c]) {
break;
}
}
return bit_c;
}
/*
* static int size_to_free_bits
*
* DESCRIPTION:
*
* Calculate the number of bits in a size going on the free list.
*
* RETURNS:
*
* Number of bits.
*
* ARGUMENTS:
*
* size -> Size of memory of which to calculate the number of bits.
*/
static int size_to_free_bits(const unsigned long size)
{
int bit_c = 0;
if (size == 0) {
return 0;
}
for (bit_c = 0; bit_c <= MAX_BITS; bit_c++) {
if (size < bit_array[bit_c]) {
break;
}
}
return bit_c - 1;
}
/*
* static int bits_to_size
*
* DESCRIPTION:
*
* Calculate the size represented by a number of bits.
*
* RETURNS:
*
* Number of bits.
*
* ARGUMENTS:
*
* bit_n -> Number of bits
*/
static unsigned long bits_to_size(const int bit_n)
{
if (bit_n > MAX_BITS) {
return bit_array[MAX_BITS];
}
else {
return bit_array[bit_n];
}
}
/*
* static void *alloc_pages
*
* DESCRIPTION:
*
* Allocate space for a number of memory pages in the memory pool.
*
* RETURNS:
*
* Success - New pages of memory
*
* Failure - NULL
*
* ARGUMENTS:
*
* mp_p <-> Pointer to our memory pool.
*
* page_n -> Number of pages to alloc.
*
* error_p <- Pointer to integer which, if not NULL, will be set with
* a mpool error code.
*/
static void *alloc_pages(mpool_t *mp_p, const unsigned int page_n,
int *error_p)
{
void *mem, *fill_mem;
unsigned long size, fill;
int state;
/* are we over our max-pages? */
if (mp_p->mp_max_pages > 0 && mp_p->mp_page_c >= mp_p->mp_max_pages) {
SET_POINTER(error_p, MPOOL_ERROR_NO_PAGES);
return NULL;
}
size = SIZE_OF_PAGES(mp_p, page_n);
#ifdef DEBUG
(void)printf("allocating %u pages or %lu bytes\n", page_n, size);
#endif
if (BIT_IS_SET(mp_p->mp_flags, MPOOL_FLAG_USE_SBRK)) {
mem = sbrk(size);
if (mem == (void *)-1) {
SET_POINTER(error_p, MPOOL_ERROR_NO_MEM);
return NULL;
}
fill = (unsigned long)mem % mp_p->mp_page_size;
if (fill > 0) {
fill = mp_p->mp_page_size - fill;
fill_mem = sbrk(fill);
if (fill_mem == (void *)-1) {
SET_POINTER(error_p, MPOOL_ERROR_NO_MEM);
return NULL;
}
if ((char *)fill_mem != (char *)mem + size) {
SET_POINTER(error_p, MPOOL_ERROR_SBRK_CONTIG);
return NULL;
}
mem = (char *)mem + fill;
}
}
else {
state = MAP_PRIVATE;
#ifdef MAP_FILE
state |= MAP_FILE;
#endif
#ifdef MAP_VARIABLE
state |= MAP_VARIABLE;
#endif
if (BIT_IS_SET(mp_p->mp_flags, MPOOL_FLAG_USE_MAP_ANON)) {
#ifdef MAP_ANON
state |= MAP_ANON;
#elif defined MAP_ANONYMOUS
state |= MAP_ANONYMOUS;
#endif
}
/* mmap from /dev/zero */
mem = mmap((caddr_t)mp_p->mp_addr, size, PROT_READ | PROT_WRITE, state,
mp_p->mp_fd, mp_p->mp_top);
if (mem == (void *)MAP_FAILED) {
if (errno == ENOMEM) {
SET_POINTER(error_p, MPOOL_ERROR_NO_MEM);
}
else {
SET_POINTER(error_p, MPOOL_ERROR_MMAP);
}
return NULL;
}
mp_p->mp_top += size;
if (mp_p->mp_addr != NULL) {
mp_p->mp_addr = (char *)mp_p->mp_addr + size;
}
}
mp_p->mp_page_c += page_n;
SET_POINTER(error_p, MPOOL_ERROR_NONE);
return mem;
}
/*
* static int free_pages
*
* DESCRIPTION:
*
* Free previously allocated pages of memory.
*
* RETURNS:
*
* Success - MPOOL_ERROR_NONE
*
* Failure - Mpool error code
*
* ARGUMENTS:
*
* pages <-> Pointer to memory pages that we are freeing.
*
* size -> Size of the block that we are freeing.
*
* sbrk_b -> Set to one if the pages were allocated with sbrk else mmap.
*/
static int free_pages(void *pages, const unsigned long size,
const int sbrk_b)
{
if (! sbrk_b) {
(void)munmap((caddr_t)pages, size);
}
return MPOOL_ERROR_NONE;
}
/*
* static int check_magic
*
* DESCRIPTION:
*
* Check for the existance of the magic ID in a memory pointer.
*
* RETURNS:
*
* Success - MPOOL_ERROR_NONE
*
* Failure - Mpool error code
*
* ARGUMENTS:
*
* addr -> Address inside of the block that we are tryign to locate.
*
* size -> Size of the block.
*/
static int check_magic(const void *addr, const unsigned long size)
{
const unsigned char *mem_p;
/* set our starting point */
mem_p = (unsigned char *)addr + size;
if (*mem_p == FENCE_MAGIC0 && *(mem_p + 1) == FENCE_MAGIC1) {
return MPOOL_ERROR_NONE;
}
else {
return MPOOL_ERROR_PNT_OVER;
}
}
/*
* static void write_magic
*
* DESCRIPTION:
*
* Write the magic ID to the address.
*
* RETURNS:
*
* None.
*
* ARGUMENTS:
*
* addr -> Address where to write the magic.
*/
static void write_magic(const void *addr)
{
*(unsigned char *)addr = FENCE_MAGIC0;
*((unsigned char *)addr + 1) = FENCE_MAGIC1;
}
/*
* static void free_pointer
*
* DESCRIPTION:
*
* Moved a pointer into our free lists.
*
* RETURNS:
*
* Success - MPOOL_ERROR_NONE
*
* Failure - Mpool error code
*
* ARGUMENTS:
*
* mp_p <-> Pointer to the memory pool.
*
* addr <-> Address where to write the magic. We may write a next
* pointer to it.
*
* size -> Size of the address space.
*/
static int free_pointer(mpool_t *mp_p, void *addr,
const unsigned long size)
{
unsigned int bit_n;
unsigned long real_size;
mpool_free_t free_pnt;
#ifdef DEBUG
(void)printf("freeing a block at %lx of %lu bytes\n", (long)addr, size);
#endif
if (size == 0) {
return MPOOL_ERROR_NONE;
}
/*
* if the user size is larger then can fit in an entire block then
* we change the size
*/
if (size > MAX_BLOCK_USER_MEMORY(mp_p)) {
real_size = SIZE_OF_PAGES(mp_p, PAGES_IN_SIZE(mp_p, size)) -
sizeof(mpool_block_t);
}
else {
real_size = size;
}
/*
* We use a specific free bits calculation here because if we are
* freeing 10 bytes then we will be putting it into the 8-byte free
* list and not the 16 byte list. size_to_bits(10) will return 4
* instead of 3.
*/
bit_n = size_to_free_bits(real_size);
/*
* Minimal error checking. We could go all the way through the
* list however this might be prohibitive.
*/
if (mp_p->mp_free[bit_n] == addr) {
return MPOOL_ERROR_IS_FREE;
}
/* add the freed pointer to the free list */
if (bit_n < min_bit_free_next) {
/*
* Yes we know this will lose 99% of the allocations but what else
* can we do? No space for a next pointer.
*/
if (mp_p->mp_free[bit_n] == NULL) {
mp_p->mp_free[bit_n] = addr;
}
}
else if (bit_n < min_bit_free_size) {
/* we copy, not assign, to maintain the free list */
memcpy(addr, mp_p->mp_free + bit_n, sizeof(void *));
mp_p->mp_free[bit_n] = addr;
}
else {
/* setup our free list structure */
free_pnt.mf_next_p = mp_p->mp_free[bit_n];
free_pnt.mf_size = real_size;
/* we copy the structure in since we don't know about alignment */
memcpy(addr, &free_pnt, sizeof(free_pnt));
mp_p->mp_free[bit_n] = addr;
}
return MPOOL_ERROR_NONE;
}
/*
* static int split_block
*
* DESCRIPTION:
*
* When freeing space in a multi-block chunk we have to create new
* blocks out of the upper areas being freed.
*
* RETURNS:
*
* Success - MPOOL_ERROR_NONE
*
* Failure - Mpool error code
*
* ARGUMENTS:
*
* mp_p <-> Pointer to the memory pool.
*
* free_addr -> Address that we are freeing.
*
* size -> Size of the space that we are taking from address.
*/
static int split_block(mpool_t *mp_p, void *free_addr,
const unsigned long size)
{
mpool_block_t *block_p, *new_block_p;
int ret, page_n;
void *end_p;
/*
* 1st we find the block pointer from our free addr. At this point
* the pointer must be the 1st one in the block if it is spans
* multiple blocks.
*/
block_p = (mpool_block_t *)((char *)free_addr - sizeof(mpool_block_t));
if (block_p->mb_magic != BLOCK_MAGIC
|| block_p->mb_magic2 != BLOCK_MAGIC) {
return MPOOL_ERROR_POOL_OVER;
}
page_n = PAGES_IN_SIZE(mp_p, size);
/* we are creating a new block structure for the 2nd ... */
new_block_p = (mpool_block_t *)((char *)block_p +
SIZE_OF_PAGES(mp_p, page_n));
new_block_p->mb_magic = BLOCK_MAGIC;
/* New bounds is 1st block bounds. The 1st block's is reset below. */
new_block_p->mb_bounds_p = block_p->mb_bounds_p;
/* Continue the linked list. The 1st block will point to us below. */
new_block_p->mb_next_p = block_p->mb_next_p;
new_block_p->mb_magic2 = BLOCK_MAGIC;
/* bounds for the 1st block are reset to the 1st page only */
block_p->mb_bounds_p = (char *)new_block_p;
/* the next block pointer for the 1st block is now the new one */
block_p->mb_next_p = new_block_p;
/* only free the space in the 1st block if it is only 1 block in size */
if (page_n == 1) {
/* now free the rest of the 1st block block */
end_p = (char *)free_addr + size;
ret = free_pointer(mp_p, end_p,
(char *)block_p->mb_bounds_p - (char *)end_p);
if (ret != MPOOL_ERROR_NONE) {
return ret;
}
}
/* now free the rest of the block */
ret = free_pointer(mp_p, FIRST_ADDR_IN_BLOCK(new_block_p),
MEMORY_IN_BLOCK(new_block_p));
if (ret != MPOOL_ERROR_NONE) {
return ret;
}
return MPOOL_ERROR_NONE;
}
/*
* static void *get_space
*
* DESCRIPTION:
*
* Moved a pointer into our free lists.
*
* RETURNS:
*
* Success - New address that we can use.
*
* Failure - NULL
*
* ARGUMENTS:
*
* mp_p <-> Pointer to the memory pool.
*
* byte_size -> Size of the address space that we need.
*
* error_p <- Pointer to integer which, if not NULL, will be set with
* a mpool error code.
*/
static void *get_space(mpool_t *mp_p, const unsigned long byte_size,
int *error_p)
{
mpool_block_t *block_p;
mpool_free_t free_pnt;
int ret;
unsigned long size;
unsigned int bit_c, page_n, left;
void *free_addr = NULL, *free_end;
size = byte_size;
while ((size & (sizeof(void *) - 1)) > 0) {
size++;
}
/*
* First we check the free lists looking for something with enough
* pages. Maybe we should only look X bits higher in the list.
*
* XXX: this is where we'd do the best fit. We'd look for the
* closest match. We then could put the rest of the allocation that
* we did not use in a lower free list. Have a define which states
* how deep in the free list to go to find the closest match.
*/
for (bit_c = size_to_bits(size); bit_c <= MAX_BITS; bit_c++) {
if (mp_p->mp_free[bit_c] != NULL) {
free_addr = mp_p->mp_free[bit_c];
break;
}
}
/*
* If we haven't allocated any blocks or if the last block doesn't
* have enough memory then we need a new block.
*/
if (bit_c > MAX_BITS) {
/* we need to allocate more space */
page_n = PAGES_IN_SIZE(mp_p, size);
/* now we try and get the pages we need/want */
block_p = alloc_pages(mp_p, page_n, error_p);
if (block_p == NULL) {
/* error_p set in alloc_pages */
return NULL;
}
/* init the block header */
block_p->mb_magic = BLOCK_MAGIC;
block_p->mb_bounds_p = (char *)block_p + SIZE_OF_PAGES(mp_p, page_n);
block_p->mb_next_p = mp_p->mp_first_p;
block_p->mb_magic2 = BLOCK_MAGIC;
/*
* We insert it into the front of the queue. We could add it to
* the end but there is not much use.
*/
mp_p->mp_first_p = block_p;
if (mp_p->mp_last_p == NULL) {
mp_p->mp_last_p = block_p;
}
free_addr = FIRST_ADDR_IN_BLOCK(block_p);
#ifdef DEBUG
(void)printf("had to allocate space for %lx of %lu bytes\n",
(long)free_addr, size);
#endif
free_end = (char *)free_addr + size;
left = (char *)block_p->mb_bounds_p - (char *)free_end;
}
else {
if (bit_c < min_bit_free_next) {
mp_p->mp_free[bit_c] = NULL;
/* calculate the number of left over bytes */
left = bits_to_size(bit_c) - size;
}
else if (bit_c < min_bit_free_next) {
/* grab the next pointer from the freed address into our list */
memcpy(mp_p->mp_free + bit_c, free_addr, sizeof(void *));
/* calculate the number of left over bytes */
left = bits_to_size(bit_c) - size;
}
else {
/* grab the free structure from the address */
memcpy(&free_pnt, free_addr, sizeof(free_pnt));
mp_p->mp_free[bit_c] = free_pnt.mf_next_p;
/* are we are splitting up a multiblock chunk into fewer blocks? */
if (PAGES_IN_SIZE(mp_p, free_pnt.mf_size) > PAGES_IN_SIZE(mp_p, size)) {
ret = split_block(mp_p, free_addr, size);
if (ret != MPOOL_ERROR_NONE) {
SET_POINTER(error_p, ret);
return NULL;
}
/* left over memory was taken care of in split_block */
left = 0;
}
else {
/* calculate the number of left over bytes */
left = free_pnt.mf_size - size;
}
}
#ifdef DEBUG
(void)printf("found a free block at %lx of %lu bytes\n",
(long)free_addr, left + size);
#endif
free_end = (char *)free_addr + size;
}
/*
* If we have memory left over then we free it so someone else can
* use it. We do not free the space if we just allocated a
* multi-block chunk because we need to have every allocation easily
* find the start of the block. Every user address % page-size
* should take us to the start of the block.
*/
if (left > 0 && size <= MAX_BLOCK_USER_MEMORY(mp_p)) {
/* free the rest of the block */
ret = free_pointer(mp_p, free_end, left);
if (ret != MPOOL_ERROR_NONE) {
SET_POINTER(error_p, ret);
return NULL;
}
}
/* update our bounds */
if (free_addr > mp_p->mp_bounds_p) {
mp_p->mp_bounds_p = free_addr;
}
else if (free_addr < mp_p->mp_min_p) {
mp_p->mp_min_p = free_addr;
}
return free_addr;
}
/*
* static void *alloc_mem
*
* DESCRIPTION:
*
* Allocate space for bytes inside of an already open memory pool.
*
* RETURNS:
*
* Success - Pointer to the address to use.
*
* Failure - NULL
*
* ARGUMENTS:
*
* mp_p <-> Pointer to the memory pool. If NULL then it will do a
* normal malloc.
*
* byte_size -> Number of bytes to allocate in the pool. Must be >0.
*
* error_p <- Pointer to integer which, if not NULL, will be set with
* a mpool error code.
*/
static void *alloc_mem(mpool_t *mp_p, const unsigned long byte_size,
int *error_p)
{
unsigned long size, fence;
void *addr;
/* make sure we have enough bytes */
if (byte_size < MIN_ALLOCATION) {
size = MIN_ALLOCATION;
}
else {
size = byte_size;
}
if (BIT_IS_SET(mp_p->mp_flags, MPOOL_FLAG_NO_FREE)) {
fence = 0;
}
else {
fence = FENCE_SIZE;
}
/* get our free space + the space for the fence post */
addr = get_space(mp_p, size + fence, error_p);
if (addr == NULL) {
/* error_p set in get_space */
return NULL;
}
if (! BIT_IS_SET(mp_p->mp_flags, MPOOL_FLAG_NO_FREE)) {
write_magic((char *)addr + size);
}
/* maintain our stats */
mp_p->mp_alloc_c++;
mp_p->mp_user_alloc += size;
if (mp_p->mp_user_alloc > mp_p->mp_max_alloc) {
mp_p->mp_max_alloc = mp_p->mp_user_alloc;
}
SET_POINTER(error_p, MPOOL_ERROR_NONE);
return addr;
}
/*
* static int free_mem
*
* DESCRIPTION:
*
* Free an address from a memory pool.
*
* RETURNS:
*
* Success - MPOOL_ERROR_NONE
*
* Failure - Mpool error code
*
* ARGUMENTS:
*
* mp_p <-> Pointer to the memory pool. If NULL then it will do a
* normal free.
*
* addr <-> Address to free.
*
* size -> Size of the address being freed.
*/
static int free_mem(mpool_t *mp_p, void *addr, const unsigned long size)
{
unsigned long old_size, fence;
int ret;
mpool_block_t *block_p;
/*
* If the size is larger than a block then the allocation must be at
* the front of the block.
*/
if (size > MAX_BLOCK_USER_MEMORY(mp_p)) {
block_p = (mpool_block_t *)((char *)addr - sizeof(mpool_block_t));
if (block_p->mb_magic != BLOCK_MAGIC
|| block_p->mb_magic2 != BLOCK_MAGIC) {
return MPOOL_ERROR_POOL_OVER;
}
}
/* make sure we have enough bytes */
if (size < MIN_ALLOCATION) {
old_size = MIN_ALLOCATION;
}
else {
old_size = size;
}
/* if we are packing the pool smaller */
if (BIT_IS_SET(mp_p->mp_flags, MPOOL_FLAG_NO_FREE)) {
fence = 0;
}
else {
/* find the user's magic numbers if they were written */
ret = check_magic(addr, old_size);
if (ret != MPOOL_ERROR_NONE) {
return ret;
}
fence = FENCE_SIZE;
}
/* now we free the pointer */
ret = free_pointer(mp_p, addr, old_size + fence);
if (ret != MPOOL_ERROR_NONE) {
return ret;
}
mp_p->mp_user_alloc -= old_size;
/* adjust our stats */
mp_p->mp_alloc_c--;
return MPOOL_ERROR_NONE;
}
/***************************** exported routines *****************************/
/*
* mpool_t *mpool_open
*
* DESCRIPTION:
*
* Open/allocate a new memory pool.
*
* RETURNS:
*
* Success - Pool pointer which must be passed to mpool_close to
* deallocate.
*
* Failure - NULL
*
* ARGUMENTS:
*
* flags -> Flags to set attributes of the memory pool. See the top
* of mpool.h.
*
* page_size -> Set the internal memory page-size. This must be a
* multiple of the getpagesize() value. Set to 0 for the default.
*
* start_addr -> Starting address to try and allocate memory pools.
* This is ignored if the MPOOL_FLAG_USE_SBRK is enabled.
*
* error_p <- Pointer to integer which, if not NULL, will be set with
* a mpool error code.
*/
mpool_t *mpool_open(const unsigned int flags, const unsigned int page_size,
void *start_addr, int *error_p)
{
mpool_block_t *block_p;
int page_n, ret;
mpool_t mp, *mp_p;
void *free_addr;
if (! enabled_b) {
startup();
}
/* zero our temp struct */
memset(&mp, 0, sizeof(mp));
mp.mp_magic = MPOOL_MAGIC;
mp.mp_flags = flags;
mp.mp_alloc_c = 0;
mp.mp_user_alloc = 0;
mp.mp_max_alloc = 0;
mp.mp_page_c = 0;
/* mp.mp_page_size set below */
/* mp.mp_blocks_bit_n set below */
/* mp.mp_fd set below */
/* mp.mp_top set below */
/* mp.mp_addr set below */
mp.mp_log_func = NULL;
mp.mp_min_p = NULL;
mp.mp_bounds_p = NULL;
mp.mp_first_p = NULL;
mp.mp_last_p = NULL;
mp.mp_magic2 = MPOOL_MAGIC;
/* get and sanity check our page size */
if (page_size > 0) {
mp.mp_page_size = page_size;
if (mp.mp_page_size % getpagesize() != 0) {
SET_POINTER(error_p, MPOOL_ERROR_ARG_INVALID);
return NULL;
}
}
else {
mp.mp_page_size = getpagesize() * DEFAULT_PAGE_MULT;
if (mp.mp_page_size % 1024 != 0) {
SET_POINTER(error_p, MPOOL_ERROR_PAGE_SIZE);
return NULL;
}
}
if (BIT_IS_SET(flags, MPOOL_FLAG_USE_SBRK)) {
mp.mp_fd = -1;
mp.mp_addr = NULL;
mp.mp_top = 0;
}
else if (BIT_IS_SET(flags, MPOOL_FLAG_USE_MAP_ANON)) {
mp.mp_fd = -1;
mp.mp_addr = start_addr;
mp.mp_top = 0;
}
else {
/* open dev-zero for our mmaping */
mp.mp_fd = open("/dev/zero", O_RDWR, 0);
if (mp.mp_fd < 0) {
SET_POINTER(error_p, MPOOL_ERROR_OPEN_ZERO);
return NULL;
}
mp.mp_addr = start_addr;
/* we start at the front of the file */
mp.mp_top = 0;
}
/*
* Find out how many pages we need for our mpool structure.
*
* NOTE: this adds possibly unneeded space for mpool_block_t which
* may not be in this block.
*/
page_n = PAGES_IN_SIZE(&mp, sizeof(mpool_t));
/* now allocate us space for the actual struct */
mp_p = alloc_pages(&mp, page_n, error_p);
if (mp_p == NULL) {
if (mp.mp_fd >= 0) {
(void)close(mp.mp_fd);
mp.mp_fd = -1;
}
return NULL;
}
/*
* NOTE: we do not normally free the rest of the block here because
* we want to lesson the chance of an allocation overwriting the
* main structure.
*/
if (BIT_IS_SET(flags, MPOOL_FLAG_HEAVY_PACKING)) {
/* we add a block header to the front of the block */
block_p = (mpool_block_t *)mp_p;
/* init the block header */
block_p->mb_magic = BLOCK_MAGIC;
block_p->mb_bounds_p = (char *)block_p + SIZE_OF_PAGES(&mp, page_n);
block_p->mb_next_p = NULL;
block_p->mb_magic2 = BLOCK_MAGIC;
/* the mpool pointer is then the 2nd thing in the block */
mp_p = FIRST_ADDR_IN_BLOCK(block_p);
free_addr = (char *)mp_p + sizeof(mpool_t);
/* free the rest of the block */
ret = free_pointer(&mp, free_addr,
(char *)block_p->mb_bounds_p - (char *)free_addr);
if (ret != MPOOL_ERROR_NONE) {
if (mp.mp_fd >= 0) {
(void)close(mp.mp_fd);
mp.mp_fd = -1;
}
/* NOTE: after this line mp_p will be invalid */
(void)free_pages(block_p, SIZE_OF_PAGES(&mp, page_n),
BIT_IS_SET(flags, MPOOL_FLAG_USE_SBRK));
SET_POINTER(error_p, ret);
return NULL;
}
/*
* NOTE: if we are HEAVY_PACKING then the 1st block with the mpool
* header is not on the block linked list.
*/
/* now copy our tmp structure into our new memory area */
memcpy(mp_p, &mp, sizeof(mpool_t));
/* we setup min/max to our current address which is as good as any */
mp_p->mp_min_p = block_p;
mp_p->mp_bounds_p = block_p->mb_bounds_p;
}
else {
/* now copy our tmp structure into our new memory area */
memcpy(mp_p, &mp, sizeof(mpool_t));
/* we setup min/max to our current address which is as good as any */
mp_p->mp_min_p = mp_p;
mp_p->mp_bounds_p = (char *)mp_p + SIZE_OF_PAGES(mp_p, page_n);
}
SET_POINTER(error_p, MPOOL_ERROR_NONE);
return mp_p;
}
/*
* int mpool_close
*
* DESCRIPTION:
*
* Close/free a memory allocation pool previously opened with
* mpool_open.
*
* RETURNS:
*
* Success - MPOOL_ERROR_NONE
*
* Failure - Mpool error code
*
* ARGUMENTS:
*
* mp_p <-> Pointer to our memory pool.
*/
int mpool_close(mpool_t *mp_p)
{
mpool_block_t *block_p, *next_p;
void *addr;
unsigned long size;
int ret, final = MPOOL_ERROR_NONE;
/* special case, just return no-error */
if (mp_p == NULL) {
return MPOOL_ERROR_ARG_NULL;
}
if (mp_p->mp_magic != MPOOL_MAGIC) {
return MPOOL_ERROR_PNT;
}
if (mp_p->mp_magic2 != MPOOL_MAGIC) {
return MPOOL_ERROR_POOL_OVER;
}
if (mp_p->mp_log_func != NULL) {
mp_p->mp_log_func(mp_p, MPOOL_FUNC_CLOSE, 0, 0, NULL, NULL, 0);
}
/*
* NOTE: if we are HEAVY_PACKING then the 1st block with the mpool
* header is not on the linked list.
*/
/* free/invalidate the blocks */
for (block_p = mp_p->mp_first_p; block_p != NULL; block_p = next_p) {
if (block_p->mb_magic != BLOCK_MAGIC
|| block_p->mb_magic2 != BLOCK_MAGIC) {
final = MPOOL_ERROR_POOL_OVER;
break;
}
block_p->mb_magic = 0;
block_p->mb_magic2 = 0;
/* record the next pointer because it might be invalidated below */
next_p = block_p->mb_next_p;
ret = free_pages(block_p, (char *)block_p->mb_bounds_p - (char *)block_p,
BIT_IS_SET(mp_p->mp_flags, MPOOL_FLAG_USE_SBRK));
if (ret != MPOOL_ERROR_NONE) {
final = ret;
}
}
/* close /dev/zero if necessary */
if (mp_p->mp_fd >= 0) {
(void)close(mp_p->mp_fd);
mp_p->mp_fd = -1;
}
/* invalidate the mpool before we ditch it */
mp_p->mp_magic = 0;
mp_p->mp_magic2 = 0;
/* last we munmap the mpool pointer itself */
if (! BIT_IS_SET(mp_p->mp_flags, MPOOL_FLAG_USE_SBRK)) {
/* if we are heavy packing then we need to free the 1st block later */
if (BIT_IS_SET(mp_p->mp_flags, MPOOL_FLAG_HEAVY_PACKING)) {
addr = (char *)mp_p - sizeof(mpool_block_t);
}
else {
addr = mp_p;
}
size = SIZE_OF_PAGES(mp_p, PAGES_IN_SIZE(mp_p, sizeof(mpool_t)));
(void)munmap((caddr_t)addr, size);
}
return final;
}
/*
* int mpool_clear
*
* DESCRIPTION:
*
* Wipe an opened memory pool clean so we can start again.
*
* RETURNS:
*
* Success - MPOOL_ERROR_NONE
*
* Failure - Mpool error code
*
* ARGUMENTS:
*
* mp_p <-> Pointer to our memory pool.
*/
int mpool_clear(mpool_t *mp_p)
{
mpool_block_t *block_p;
int final = MPOOL_ERROR_NONE, bit_n, ret;
void *first_p;
/* special case, just return no-error */
if (mp_p == NULL) {
return MPOOL_ERROR_ARG_NULL;
}
if (mp_p->mp_magic != MPOOL_MAGIC) {
return MPOOL_ERROR_PNT;
}
if (mp_p->mp_magic2 != MPOOL_MAGIC) {
return MPOOL_ERROR_POOL_OVER;
}
if (mp_p->mp_log_func != NULL) {
mp_p->mp_log_func(mp_p, MPOOL_FUNC_CLEAR, 0, 0, NULL, NULL, 0);
}
/* reset all of our free lists */
for (bit_n = 0; bit_n <= MAX_BITS; bit_n++) {
mp_p->mp_free[bit_n] = NULL;
}
/* free the blocks */
for (block_p = mp_p->mp_first_p;
block_p != NULL;
block_p = block_p->mb_next_p) {
if (block_p->mb_magic != BLOCK_MAGIC
|| block_p->mb_magic2 != BLOCK_MAGIC) {
final = MPOOL_ERROR_POOL_OVER;
break;
}
first_p = FIRST_ADDR_IN_BLOCK(block_p);
/* free the memory */
ret = free_pointer(mp_p, first_p, MEMORY_IN_BLOCK(block_p));
if (ret != MPOOL_ERROR_NONE) {
final = ret;
}
}
return final;
}
/*
* void *mpool_alloc
*
* DESCRIPTION:
*
* Allocate space for bytes inside of an already open memory pool.
*
* RETURNS:
*
* Success - Pointer to the address to use.
*
* Failure - NULL
*
* ARGUMENTS:
*
* mp_p <-> Pointer to the memory pool. If NULL then it will do a
* normal malloc.
*
* byte_size -> Number of bytes to allocate in the pool. Must be >0.
*
* error_p <- Pointer to integer which, if not NULL, will be set with
* a mpool error code.
*/
void *mpool_alloc(mpool_t *mp_p, const unsigned long byte_size,
int *error_p)
{
void *addr;
if (mp_p == NULL) {
/* special case -- do a normal malloc */
addr = (void *)malloc(byte_size);
if (addr == NULL) {
SET_POINTER(error_p, MPOOL_ERROR_ALLOC);
return NULL;
}
else {
SET_POINTER(error_p, MPOOL_ERROR_NONE);
return addr;
}
}
if (mp_p->mp_magic != MPOOL_MAGIC) {
SET_POINTER(error_p, MPOOL_ERROR_PNT);
return NULL;
}
if (mp_p->mp_magic2 != MPOOL_MAGIC) {
SET_POINTER(error_p, MPOOL_ERROR_POOL_OVER);
return NULL;
}
if (byte_size == 0) {
SET_POINTER(error_p, MPOOL_ERROR_ARG_INVALID);
return NULL;
}
addr = alloc_mem(mp_p, byte_size, error_p);
if (mp_p->mp_log_func != NULL) {
mp_p->mp_log_func(mp_p, MPOOL_FUNC_ALLOC, byte_size, 0, addr, NULL, 0);
}
return addr;
}
/*
* void *mpool_calloc
*
* DESCRIPTION:
*
* Allocate space for elements of bytes in the memory pool and zero
* the space afterwards.
*
* RETURNS:
*
* Success - Pointer to the address to use.
*
* Failure - NULL
*
* ARGUMENTS:
*
* mp_p <-> Pointer to the memory pool. If NULL then it will do a
* normal calloc.
*
* ele_n -> Number of elements to allocate.
*
* ele_size -> Number of bytes per element being allocated.
*
* error_p <- Pointer to integer which, if not NULL, will be set with
* a mpool error code.
*/
void *mpool_calloc(mpool_t *mp_p, const unsigned long ele_n,
const unsigned long ele_size, int *error_p)
{
void *addr;
unsigned long byte_size;
if (mp_p == NULL) {
/* special case -- do a normal calloc */
addr = (void *)calloc(ele_n, ele_size);
if (addr == NULL) {
SET_POINTER(error_p, MPOOL_ERROR_ALLOC);
return NULL;
}
else {
SET_POINTER(error_p, MPOOL_ERROR_NONE);
return addr;
}
}
if (mp_p->mp_magic != MPOOL_MAGIC) {
SET_POINTER(error_p, MPOOL_ERROR_PNT);
return NULL;
}
if (mp_p->mp_magic2 != MPOOL_MAGIC) {
SET_POINTER(error_p, MPOOL_ERROR_POOL_OVER);
return NULL;
}
if (ele_n == 0 || ele_size == 0) {
SET_POINTER(error_p, MPOOL_ERROR_ARG_INVALID);
return NULL;
}
byte_size = ele_n * ele_size;
addr = alloc_mem(mp_p, byte_size, error_p);
if (addr != NULL) {
memset(addr, 0, byte_size);
}
if (mp_p->mp_log_func != NULL) {
mp_p->mp_log_func(mp_p, MPOOL_FUNC_CALLOC, ele_size, ele_n, addr, NULL, 0);
}
/* NOTE: error_p set above */
return addr;
}
/*
* int mpool_free
*
* DESCRIPTION:
*
* Free an address from a memory pool.
*
* RETURNS:
*
* Success - MPOOL_ERROR_NONE
*
* Failure - Mpool error code
*
* ARGUMENTS:
*
* mp_p <-> Pointer to the memory pool. If NULL then it will do a
* normal free.
*
* addr <-> Address to free.
*
* size -> Size of the address being freed.
*/
int mpool_free(mpool_t *mp_p, void *addr, const unsigned long size)
{
if (mp_p == NULL) {
/* special case -- do a normal free */
free(addr);
return MPOOL_ERROR_NONE;
}
if (mp_p->mp_magic != MPOOL_MAGIC) {
return MPOOL_ERROR_PNT;
}
if (mp_p->mp_magic2 != MPOOL_MAGIC) {
return MPOOL_ERROR_POOL_OVER;
}
if (mp_p->mp_log_func != NULL) {
mp_p->mp_log_func(mp_p, MPOOL_FUNC_FREE, size, 0, NULL, addr, 0);
}
if (addr == NULL) {
return MPOOL_ERROR_ARG_NULL;
}
if (size == 0) {
return MPOOL_ERROR_ARG_INVALID;
}
return free_mem(mp_p, addr, size);
}
/*
* void *mpool_resize
*
* DESCRIPTION:
*
* Reallocate an address in a mmeory pool to a new size. This is
* different from realloc in that it needs the old address' size. If
* you don't have it then you need to allocate new space, copy the
* data, and free the old pointer yourself.
*
* RETURNS:
*
* Success - Pointer to the address to use.
*
* Failure - NULL
*
* ARGUMENTS:
*
* mp_p <-> Pointer to the memory pool. If NULL then it will do a
* normal realloc.
*
* old_addr -> Previously allocated address.
*
* old_byte_size -> Size of the old address. Must be known, cannot be
* 0.
*
* new_byte_size -> New size of the allocation.
*
* error_p <- Pointer to integer which, if not NULL, will be set with
* a mpool error code.
*/
void *mpool_resize(mpool_t *mp_p, void *old_addr,
const unsigned long old_byte_size,
const unsigned long new_byte_size,
int *error_p)
{
unsigned long copy_size, new_size, old_size, fence;
void *new_addr;
mpool_block_t *block_p;
int ret;
if (mp_p == NULL) {
/* special case -- do a normal realloc */
new_addr = (void *)realloc(old_addr, new_byte_size);
if (new_addr == NULL) {
SET_POINTER(error_p, MPOOL_ERROR_ALLOC);
return NULL;
}
else {
SET_POINTER(error_p, MPOOL_ERROR_NONE);
return new_addr;
}
}
if (mp_p->mp_magic != MPOOL_MAGIC) {
SET_POINTER(error_p, MPOOL_ERROR_PNT);
return NULL;
}
if (mp_p->mp_magic2 != MPOOL_MAGIC) {
SET_POINTER(error_p, MPOOL_ERROR_POOL_OVER);
return NULL;
}
if (old_addr == NULL) {
SET_POINTER(error_p, MPOOL_ERROR_ARG_NULL);
return NULL;
}
if (old_byte_size == 0) {
SET_POINTER(error_p, MPOOL_ERROR_ARG_INVALID);
return NULL;
}
/*
* If the size is larger than a block then the allocation must be at
* the front of the block.
*/
if (old_byte_size > MAX_BLOCK_USER_MEMORY(mp_p)) {
block_p = (mpool_block_t *)((char *)old_addr - sizeof(mpool_block_t));
if (block_p->mb_magic != BLOCK_MAGIC
|| block_p->mb_magic2 != BLOCK_MAGIC) {
SET_POINTER(error_p, MPOOL_ERROR_POOL_OVER);
return NULL;
}
}
/* make sure we have enough bytes */
if (old_byte_size < MIN_ALLOCATION) {
old_size = MIN_ALLOCATION;
}
else {
old_size = old_byte_size;
}
/* verify that the size matches exactly if we can */
if (BIT_IS_SET(mp_p->mp_flags, MPOOL_FLAG_NO_FREE)) {
fence = 0;
}
else if (old_size > 0) {
ret = check_magic(old_addr, old_size);
if (ret != MPOOL_ERROR_NONE) {
SET_POINTER(error_p, ret);
return NULL;
}
fence = FENCE_SIZE;
}
/* make sure we have enough bytes */
if (new_byte_size < MIN_ALLOCATION) {
new_size = MIN_ALLOCATION;
}
else {
new_size = new_byte_size;
}
/*
* NOTE: we could here see if the size is the same or less and then
* use the current memory and free the space above. This is harder
* than it sounds if we are changing the block size of the
* allocation.
*/
/* we need to get another address */
new_addr = alloc_mem(mp_p, new_byte_size, error_p);
if (new_addr == NULL) {
/* error_p set in mpool_alloc */
return NULL;
}
if (new_byte_size > old_byte_size) {
copy_size = old_byte_size;
}
else {
copy_size = new_byte_size;
}
memcpy(new_addr, old_addr, copy_size);
/* free the old address */
ret = free_mem(mp_p, old_addr, old_byte_size);
if (ret != MPOOL_ERROR_NONE) {
/* if the old free failed, try and free the new address */
(void)free_mem(mp_p, new_addr, new_byte_size);
SET_POINTER(error_p, ret);
return NULL;
}
if (mp_p->mp_log_func != NULL) {
mp_p->mp_log_func(mp_p, MPOOL_FUNC_RESIZE, new_byte_size,
0, new_addr, old_addr, old_byte_size);
}
SET_POINTER(error_p, MPOOL_ERROR_NONE);
return new_addr;
}
/*
* int mpool_stats
*
* DESCRIPTION:
*
* Return stats from the memory pool.
*
* RETURNS:
*
* Success - MPOOL_ERROR_NONE
*
* Failure - Mpool error code
*
* ARGUMENTS:
*
* mp_p -> Pointer to the memory pool.
*
* page_size_p <- Pointer to an unsigned integer which, if not NULL,
* will be set to the page-size of the pool.
*
* num_alloced_p <- Pointer to an unsigned long which, if not NULL,
* will be set to the number of pointers currently allocated in pool.
*
* user_alloced_p <- Pointer to an unsigned long which, if not NULL,
* will be set to the number of user bytes allocated in this pool.
*
* max_alloced_p <- Pointer to an unsigned long which, if not NULL,
* will be set to the maximum number of user bytes that have been
* allocated in this pool.
*
* tot_alloced_p <- Pointer to an unsigned long which, if not NULL,
* will be set to the total amount of space (including administrative
* overhead) used by the pool.
*/
int mpool_stats(const mpool_t *mp_p, unsigned int *page_size_p,
unsigned long *num_alloced_p,
unsigned long *user_alloced_p,
unsigned long *max_alloced_p,
unsigned long *tot_alloced_p)
{
if (mp_p == NULL) {
return MPOOL_ERROR_ARG_NULL;
}
if (mp_p->mp_magic != MPOOL_MAGIC) {
return MPOOL_ERROR_PNT;
}
if (mp_p->mp_magic2 != MPOOL_MAGIC) {
return MPOOL_ERROR_POOL_OVER;
}
SET_POINTER(page_size_p, mp_p->mp_page_size);
SET_POINTER(num_alloced_p, mp_p->mp_alloc_c);
SET_POINTER(user_alloced_p, mp_p->mp_user_alloc);
SET_POINTER(max_alloced_p, mp_p->mp_max_alloc);
SET_POINTER(tot_alloced_p, SIZE_OF_PAGES(mp_p, mp_p->mp_page_c));
return MPOOL_ERROR_NONE;
}
/*
* int mpool_set_log_func
*
* DESCRIPTION:
*
* Set a logging callback function to be called whenever there was a
* memory transaction. See mpool_log_func_t.
*
* RETURNS:
*
* Success - MPOOL_ERROR_NONE
*
* Failure - Mpool error code
*
* ARGUMENTS:
*
* mp_p <-> Pointer to the memory pool.
*
* log_func -> Log function (defined in mpool.h) which will be called
* with each mpool transaction.
*/
int mpool_set_log_func(mpool_t *mp_p, mpool_log_func_t log_func)
{
if (mp_p == NULL) {
return MPOOL_ERROR_ARG_NULL;
}
if (mp_p->mp_magic != MPOOL_MAGIC) {
return MPOOL_ERROR_PNT;
}
if (mp_p->mp_magic2 != MPOOL_MAGIC) {
return MPOOL_ERROR_POOL_OVER;
}
mp_p->mp_log_func = log_func;
return MPOOL_ERROR_NONE;
}
/*
* int mpool_set_max_pages
*
* DESCRIPTION:
*
* Set the maximum number of pages that the library will use. Once it
* hits the limit it will return MPOOL_ERROR_NO_PAGES.
*
* NOTE: if the MPOOL_FLAG_HEAVY_PACKING is set then this max-pages
* value will include the page with the mpool header structure in it.
* If the flag is _not_ set then the max-pages will not include this
* first page.
*
* RETURNS:
*
* Success - MPOOL_ERROR_NONE
*
* Failure - Mpool error code
*
* ARGUMENTS:
*
* mp_p <-> Pointer to the memory pool.
*
* max_pages -> Maximum number of pages used by the library.
*/
int mpool_set_max_pages(mpool_t *mp_p, const unsigned int max_pages)
{
if (mp_p == NULL) {
return MPOOL_ERROR_ARG_NULL;
}
if (mp_p->mp_magic != MPOOL_MAGIC) {
return MPOOL_ERROR_PNT;
}
if (mp_p->mp_magic2 != MPOOL_MAGIC) {
return MPOOL_ERROR_POOL_OVER;
}
if (BIT_IS_SET(mp_p->mp_flags, MPOOL_FLAG_HEAVY_PACKING)) {
mp_p->mp_max_pages = max_pages;
}
else {
/*
* If we are not heavy-packing the pool then we don't count the
* 1st page allocated which holds the mpool header structure.
*/
mp_p->mp_max_pages = max_pages + 1;
}
return MPOOL_ERROR_NONE;
}
/*
* const char *mpool_strerror
*
* DESCRIPTION:
*
* Return the corresponding string for the error number.
*
* RETURNS:
*
* Success - String equivalient of the error.
*
* Failure - String "invalid error code"
*
* ARGUMENTS:
*
* error -> Error number that we are converting.
*/
const char *mpool_strerror(const int error)
{
switch (error) {
case MPOOL_ERROR_NONE:
return "no error";
break;
case MPOOL_ERROR_ARG_NULL:
return "function argument is null";
break;
case MPOOL_ERROR_ARG_INVALID:
return "function argument is invalid";
break;
case MPOOL_ERROR_PNT:
return "invalid mpool pointer";
break;
case MPOOL_ERROR_POOL_OVER:
return "mpool structure was overwritten";
break;
case MPOOL_ERROR_PAGE_SIZE:
return "could not get system page-size";
break;
case MPOOL_ERROR_OPEN_ZERO:
return "could not open /dev/zero";
break;
case MPOOL_ERROR_NO_MEM:
return "no memory available";
break;
case MPOOL_ERROR_MMAP:
return "problems with mmap";
break;
case MPOOL_ERROR_SIZE:
return "error processing requested size";
break;
case MPOOL_ERROR_TOO_BIG:
return "allocation exceeds pool max size";
break;
case MPOOL_ERROR_MEM:
return "invalid memory address";
break;
case MPOOL_ERROR_MEM_OVER:
return "memory lower bounds overwritten";
break;
case MPOOL_ERROR_NOT_FOUND:
return "memory block not found in pool";
break;
case MPOOL_ERROR_IS_FREE:
return "memory address has already been freed";
break;
case MPOOL_ERROR_BLOCK_STAT:
return "invalid internal block status";
break;
case MPOOL_ERROR_FREE_ADDR:
return "invalid internal free address";
break;
case MPOOL_ERROR_SBRK_CONTIG:
return "sbrk did not return contiguous memory";
break;
case MPOOL_ERROR_NO_PAGES:
return "no available pages left in pool";
break;
case MPOOL_ERROR_ALLOC:
return "system alloc function failed";
break;
case MPOOL_ERROR_PNT_OVER:
return "user pointer admin space overwritten";
break;
default:
break;
}
return "invalid error code";
}
/*
* Memory pool defines.
*
* Copyright 1996 by Gray Watson.
*
* This file is part of the mpool package.
*
* Permission to use, copy, modify, and distribute this software for
* any purpose and without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies, and that the name of Gray Watson not be used in advertising
* or publicity pertaining to distribution of the document or software
* without specific, written prior permission.
*
* Gray Watson makes no representations about the suitability of the
* software described herein for any purpose. It is provided "as is"
* without express or implied warranty.
*
* The author may be reached via http://256.com/gray/
*
* $Id: mpool.h,v 1.4 2006/05/31 20:26:11 gray Exp $
*/
#ifndef __MPOOL_H__
#define __MPOOL_H__
#include <sys/types.h>
/*
* mpool flags to mpool_alloc or mpool_set_attr
*/
/*
* Choose a best fit algorithm not first fit. This takes more CPU
* time but will result in a tighter heap.
*/
#define MPOOL_FLAG_BEST_FIT (1<<0)
/*
* By default the library adds 2 bytes onto all allocations to insert
* a magic number that it can look for to determine how large a freed
* memory chunk is. This flag indicates that few if any frees are
* going to be performed on the pool and to not waste memory on these
* bytes.
*/
#define MPOOL_FLAG_NO_FREE (1<<1)
/*
* This enables very heavy packing at the possible expense of CPU.
* This affects a number of parts of the library.
*
* By default the 1st page of memory is reserved for the main mpool
* structure. This flag will cause the rest of the 1st block to be
* available for use as user memory.
*
* By default the library looks through the memory when freed looking
* for a magic value. There is an internal max size that it will look
* and then it will give up. This flag forces it to look until it
* finds it.
*/
#define MPOOL_FLAG_HEAVY_PACKING (1<<2)
/*
* Use sbrk not mmap to allocate pages. This is not recommended for
* normal use.
*/
#define MPOOL_FLAG_USE_SBRK (1<<3)
/*
* Use MAP_ANON(YMOUS) instead of /dev/zero for mmap.
*/
#define MPOOL_FLAG_USE_MAP_ANON (1<<4)
/*
* Mpool error codes
*/
#define MPOOL_ERROR_NONE 1 /* no error */
#define MPOOL_ERROR_ARG_NULL 2 /* function argument is null */
#define MPOOL_ERROR_ARG_INVALID 3 /* function argument is invalid */
#define MPOOL_ERROR_PNT 4 /* invalid mpool pointer */
#define MPOOL_ERROR_POOL_OVER 5 /* mpool structure was overwritten */
#define MPOOL_ERROR_PAGE_SIZE 6 /* could not get system page-size */
#define MPOOL_ERROR_OPEN_ZERO 7 /* could not open /dev/zero */
#define MPOOL_ERROR_NO_MEM 8 /* no memory available */
#define MPOOL_ERROR_MMAP 9 /* problems with mmap */
#define MPOOL_ERROR_SIZE 10 /* error processing requested size */
#define MPOOL_ERROR_TOO_BIG 11 /* allocation exceeded max size */
#define MPOOL_ERROR_MEM 12 /* invalid memory address */
#define MPOOL_ERROR_MEM_OVER 13 /* memory lower bounds overwritten */
#define MPOOL_ERROR_NOT_FOUND 14 /* memory block not found in pool */
#define MPOOL_ERROR_IS_FREE 15 /* memory block already free */
#define MPOOL_ERROR_BLOCK_STAT 16 /* invalid internal block status */
#define MPOOL_ERROR_FREE_ADDR 17 /* invalid internal free address */
#define MPOOL_ERROR_SBRK_CONTIG 18 /* sbrk did not return contiguous mem*/
#define MPOOL_ERROR_NO_PAGES 19 /* ran out of pages in pool */
#define MPOOL_ERROR_ALLOC 20 /* calloc,malloc,free,realloc failed */
#define MPOOL_ERROR_PNT_OVER 21 /* pointer structure was overwritten */
/*
* Mpool function IDs for the mpool_log_func callback function.
*/
#define MPOOL_FUNC_CLOSE 1 /* mpool_close function called */
#define MPOOL_FUNC_CLEAR 2 /* mpool_clear function called */
#define MPOOL_FUNC_ALLOC 3 /* mpool_alloc function called */
#define MPOOL_FUNC_CALLOC 4 /* mpool_calloc function called */
#define MPOOL_FUNC_FREE 5 /* mpool_free function called */
#define MPOOL_FUNC_RESIZE 6 /* mpool_resize function called */
/*
* void mpool_log_func_t
*
* DESCRIPTION:
*
* Mpool transaction log function.
*
* RETURNS:
*
* None.
*
* ARGUMENT:
*
* mp_p -> Associated mpool address.
*
* func_id -> Integer function ID which identifies which mpool
* function is being called.
*
* byte_size -> Optionally specified byte size.
*
* ele_n -> Optionally specified element number. For mpool_calloc
* only.
*
* new_addr -> Optionally specified new address. For mpool_alloc,
* mpool_calloc, and mpool_resize only.
*
* old_addr -> Optionally specified old address. For mpool_resize and
* mpool_free only.
*
* old_byte_size -> Optionally specified old byte size. For
* mpool_resize only.
*/
typedef void (*mpool_log_func_t)(const void *mp_p,
const int func_id,
const unsigned long byte_size,
const unsigned long ele_n,
const void *old_addr, const void *new_addr,
const unsigned long old_byte_size);
#ifdef MPOOL_MAIN
#include "mpool_loc.h"
#else
/* generic mpool type */
typedef void mpool_t;
#endif
/*<<<<<<<<<< The below prototypes are auto-generated by fillproto */
/*
* mpool_t *mpool_open
*
* DESCRIPTION:
*
* Open/allocate a new memory pool.
*
* RETURNS:
*
* Success - Pool pointer which must be passed to mpool_close to
* deallocate.
*
* Failure - NULL
*
* ARGUMENTS:
*
* flags -> Flags to set attributes of the memory pool. See the top
* of mpool.h.
*
* page_size -> Set the internal memory page-size. This must be a
* multiple of the getpagesize() value. Set to 0 for the default.
*
* start_addr -> Starting address to try and allocate memory pools.
* This is ignored if the MPOOL_FLAG_USE_SBRK is enabled.
*
* error_p <- Pointer to integer which, if not NULL, will be set with
* a mpool error code.
*/
extern
mpool_t *mpool_open(const unsigned int flags, const unsigned int page_size,
void *start_addr, int *error_p);
/*
* int mpool_close
*
* DESCRIPTION:
*
* Close/free a memory allocation pool previously opened with
* mpool_open.
*
* RETURNS:
*
* Success - MPOOL_ERROR_NONE
*
* Failure - Mpool error code
*
* ARGUMENTS:
*
* mp_p <-> Pointer to our memory pool.
*/
extern
int mpool_close(mpool_t *mp_p);
/*
* int mpool_clear
*
* DESCRIPTION:
*
* Wipe an opened memory pool clean so we can start again.
*
* RETURNS:
*
* Success - MPOOL_ERROR_NONE
*
* Failure - Mpool error code
*
* ARGUMENTS:
*
* mp_p <-> Pointer to our memory pool.
*/
extern
int mpool_clear(mpool_t *mp_p);
/*
* void *mpool_alloc
*
* DESCRIPTION:
*
* Allocate space for bytes inside of an already open memory pool.
*
* RETURNS:
*
* Success - Pointer to the address to use.
*
* Failure - NULL
*
* ARGUMENTS:
*
* mp_p <-> Pointer to the memory pool. If NULL then it will do a
* normal malloc.
*
* byte_size -> Number of bytes to allocate in the pool. Must be >0.
*
* error_p <- Pointer to integer which, if not NULL, will be set with
* a mpool error code.
*/
extern
void *mpool_alloc(mpool_t *mp_p, const unsigned long byte_size,
int *error_p);
/*
* void *mpool_calloc
*
* DESCRIPTION:
*
* Allocate space for elements of bytes in the memory pool and zero
* the space afterwards.
*
* RETURNS:
*
* Success - Pointer to the address to use.
*
* Failure - NULL
*
* ARGUMENTS:
*
* mp_p <-> Pointer to the memory pool. If NULL then it will do a
* normal calloc.
*
* ele_n -> Number of elements to allocate.
*
* ele_size -> Number of bytes per element being allocated.
*
* error_p <- Pointer to integer which, if not NULL, will be set with
* a mpool error code.
*/
extern
void *mpool_calloc(mpool_t *mp_p, const unsigned long ele_n,
const unsigned long ele_size, int *error_p);
/*
* int mpool_free
*
* DESCRIPTION:
*
* Free an address from a memory pool.
*
* RETURNS:
*
* Success - MPOOL_ERROR_NONE
*
* Failure - Mpool error code
*
* ARGUMENTS:
*
* mp_p <-> Pointer to the memory pool. If NULL then it will do a
* normal free.
*
* addr <-> Address to free.
*
* size -> Size of the address being freed.
*/
extern
int mpool_free(mpool_t *mp_p, void *addr, const unsigned long size);
/*
* void *mpool_resize
*
* DESCRIPTION:
*
* Reallocate an address in a mmeory pool to a new size. This is
* different from realloc in that it needs the old address' size. If
* you don't have it then you need to allocate new space, copy the
* data, and free the old pointer yourself.
*
* RETURNS:
*
* Success - Pointer to the address to use.
*
* Failure - NULL
*
* ARGUMENTS:
*
* mp_p <-> Pointer to the memory pool. If NULL then it will do a
* normal realloc.
*
* old_addr -> Previously allocated address.
*
* old_byte_size -> Size of the old address. Must be known, cannot be
* 0.
*
* new_byte_size -> New size of the allocation.
*
* error_p <- Pointer to integer which, if not NULL, will be set with
* a mpool error code.
*/
extern
void *mpool_resize(mpool_t *mp_p, void *old_addr,
const unsigned long old_byte_size,
const unsigned long new_byte_size,
int *error_p);
/*
* int mpool_stats
*
* DESCRIPTION:
*
* Return stats from the memory pool.
*
* RETURNS:
*
* Success - MPOOL_ERROR_NONE
*
* Failure - Mpool error code
*
* ARGUMENTS:
*
* mp_p -> Pointer to the memory pool.
*
* page_size_p <- Pointer to an unsigned integer which, if not NULL,
* will be set to the page-size of the pool.
*
* num_alloced_p <- Pointer to an unsigned long which, if not NULL,
* will be set to the number of pointers currently allocated in pool.
*
* user_alloced_p <- Pointer to an unsigned long which, if not NULL,
* will be set to the number of user bytes allocated in this pool.
*
* max_alloced_p <- Pointer to an unsigned long which, if not NULL,
* will be set to the maximum number of user bytes that have been
* allocated in this pool.
*
* tot_alloced_p <- Pointer to an unsigned long which, if not NULL,
* will be set to the total amount of space (including administrative
* overhead) used by the pool.
*/
extern
int mpool_stats(const mpool_t *mp_p, unsigned int *page_size_p,
unsigned long *num_alloced_p,
unsigned long *user_alloced_p,
unsigned long *max_alloced_p,
unsigned long *tot_alloced_p);
/*
* int mpool_set_log_func
*
* DESCRIPTION:
*
* Set a logging callback function to be called whenever there was a
* memory transaction. See mpool_log_func_t.
*
* RETURNS:
*
* Success - MPOOL_ERROR_NONE
*
* Failure - Mpool error code
*
* ARGUMENTS:
*
* mp_p <-> Pointer to the memory pool.
*
* log_func -> Log function (defined in mpool.h) which will be called
* with each mpool transaction.
*/
extern
int mpool_set_log_func(mpool_t *mp_p, mpool_log_func_t log_func);
/*
* int mpool_set_max_pages
*
* DESCRIPTION:
*
* Set the maximum number of pages that the library will use. Once it
* hits the limit it will return MPOOL_ERROR_NO_PAGES.
*
* NOTE: if the MPOOL_FLAG_HEAVY_PACKING is set then this max-pages
* value will include the page with the mpool header structure in it.
* If the flag is _not_ set then the max-pages will not include this
* first page.
*
* RETURNS:
*
* Success - MPOOL_ERROR_NONE
*
* Failure - Mpool error code
*
* ARGUMENTS:
*
* mp_p <-> Pointer to the memory pool.
*
* max_pages -> Maximum number of pages used by the library.
*/
extern
int mpool_set_max_pages(mpool_t *mp_p, const unsigned int max_pages);
/*
* const char *mpool_strerror
*
* DESCRIPTION:
*
* Return the corresponding string for the error number.
*
* RETURNS:
*
* Success - String equivalient of the error.
*
* Failure - String "invalid error code"
*
* ARGUMENTS:
*
* error -> Error number that we are converting.
*/
extern
const char *mpool_strerror(const int error);
/*<<<<<<<<<< This is end of the auto-generated output from fillproto. */
#endif /* ! __MPOOL_H__ */
/*
* Memory pool local defines.
*
* Copyright 1996 by Gray Watson.
*
* This file is part of the mpool package.
*
* Permission to use, copy, modify, and distribute this software for
* any purpose and without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies, and that the name of Gray Watson not be used in advertising
* or publicity pertaining to distribution of the document or software
* without specific, written prior permission.
*
* Gray Watson makes no representations about the suitability of the
* software described herein for any purpose. It is provided "as is"
* without express or implied warranty.
*
* The author may be reached via http://256.com/gray/
*
* $Id: mpool_loc.h,v 1.2 2005/05/20 20:08:54 gray Exp $
*/
#ifndef __MPOOL_LOC_H__
#define __MPOOL_LOC_H__
#define MPOOL_MAGIC 0xABACABA /* magic for struct */
#define BLOCK_MAGIC 0xB1B1007 /* magic for blocks */
#define FENCE_MAGIC0 (unsigned char)(0xFAU) /* 1st magic mem byte */
#define FENCE_MAGIC1 (unsigned char)(0xD3U) /* 2nd magic mem byte */
#define FENCE_SIZE 2 /* fence space */
#define MIN_ALLOCATION (sizeof(mpool_free_t)) /* min alloc */
#define MAX_FREE_SEARCH 10240 /* max size to search */
#define MAX_FREE_LIST_SEARCH 100 /* max looking for free mem */
/*
* bitflag tools for Variable and a Flag
*/
#define BIT_FLAG(x) (1 << (x))
#define BIT_SET(v,f) (v) |= (f)
#define BIT_CLEAR(v,f) (v) &= ~(f)
#define BIT_IS_SET(v,f) ((v) & (f))
#define BIT_TOGGLE(v,f) (v) ^= (f)
#define SET_POINTER(pnt, val) \
do { \
if ((pnt) != NULL) { \
(*(pnt)) = (val); \
} \
} while(0)
#define BLOCK_FLAG_USED BIT_FLAG(0) /* block is used */
#define BLOCK_FLAG_FREE BIT_FLAG(1) /* block is free */
#define DEFAULT_PAGE_MULT 16 /* pagesize = this * getpagesize*/
/* How many pages SIZE bytes resides in. We add in the block header. */
#define PAGES_IN_SIZE(mp_p, size) (((size) + sizeof(mpool_block_t) + \
(mp_p)->mp_page_size - 1) / \
(mp_p)->mp_page_size)
#define SIZE_OF_PAGES(mp_p, page_n) ((page_n) * (mp_p)->mp_page_size)
#define MAX_BITS 30 /* we only can allocate 1gb chunks */
#define MAX_BLOCK_USER_MEMORY(mp_p) ((mp_p)->mp_page_size - \
sizeof(mpool_block_t))
#define FIRST_ADDR_IN_BLOCK(block_p) (void *)((char *)(block_p) + \
sizeof(mpool_block_t))
#define MEMORY_IN_BLOCK(block_p) ((char *)(block_p)->mb_bounds_p - \
((char *)(block_p) + \
sizeof(mpool_block_t)))
typedef struct {
unsigned int mp_magic; /* magic number for struct */
unsigned int mp_flags; /* flags for the struct */
unsigned long mp_alloc_c; /* number of allocations */
unsigned long mp_user_alloc; /* user bytes allocated */
unsigned long mp_max_alloc; /* maximum user bytes allocated */
unsigned int mp_page_c; /* number of pages allocated */
unsigned int mp_max_pages; /* maximum number of pages to use */
unsigned int mp_page_size; /* page-size of our system */
int mp_fd; /* fd for /dev/zero if mmap-ing */
off_t mp_top; /* top of our allocations in fd */
mpool_log_func_t mp_log_func; /* log callback function */
void *mp_addr; /* current address for mmaping */
void *mp_min_p; /* min address in pool for checks */
void *mp_bounds_p; /* max address in pool for checks */
struct mpool_block_st *mp_first_p; /* first memory block we are using */
struct mpool_block_st *mp_last_p; /* last memory block we are using */
struct mpool_block_st *mp_free[MAX_BITS + 1]; /* free lists based on size */
unsigned int mp_magic2; /* upper magic for overwrite sanity */
} mpool_t;
/* for debuggers to be able to interrogate the generic type in the .h file */
typedef mpool_t mpool_ext_t;
/*
* Block header structure. This structure *MUST* be long-word
* aligned.
*/
typedef struct mpool_block_st {
unsigned int mb_magic; /* magic number for block header */
void *mb_bounds_p; /* block boundary location */
struct mpool_block_st *mb_next_p; /* linked list next pointer */
unsigned int mb_magic2; /* upper magic for overwrite sanity */
} mpool_block_t;
/*
* Free list structure.
*/
typedef struct {
void *mf_next_p; /* pointer to the next free address */
unsigned long mf_size; /* size of the free block */
} mpool_free_t;
#endif /* ! __MPOOL_LOC_H__ */
/*
* Memory pool test program.
*
* Copyright 1996 by Gray Watson.
*
* This file is part of the mpool package.
*
* Permission to use, copy, modify, and distribute this software for
* any purpose and without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies, and that the name of Gray Watson not be used in advertising
* or publicity pertaining to distribution of the document or software
* without specific, written prior permission.
*
* Gray Watson makes no representations about the suitability of the
* software described herein for any purpose. It is provided "as is"
* without express or implied warranty.
*
* The author may be reached via http://256.com/gray/
*
* $Id: mpool_t.c,v 1.2 2005/05/20 20:08:55 gray Exp $
*/
/*
* Test program for the malloc library. Current it is interactive although
* should be script based.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "mpool.h"
#ifdef __GNUC__
#ident "$Id: mpool_t.c,v 1.2 2005/05/20 20:08:55 gray Exp $"
#else
static char *rcs_id = "$Id: mpool_t.c,v 1.2 2005/05/20 20:08:55 gray Exp $";
#endif
#define DEFAULT_ITERATIONS 10000
#define MAX_POINTERS 1024
#define MAX_ALLOC (1024 * 1024)
#define MIN_AVAIL 10
#define RANDOM_VALUE(x) ((random() % ((x) * 10)) / 10)
/* pointer tracking structure */
struct pnt_info_st {
long pi_crc; /* crc of storage */
long pi_size; /* size of storage */
void *pi_pnt; /* pnt to storage */
struct pnt_info_st *pi_next; /* pnt to next */
};
typedef struct pnt_info_st pnt_info_t;
static pnt_info_t *pointer_grid;
/* argument variables */
static int best_fit_b = 0; /* set best fit flag */
static int heavy_pack_b = 0; /* set heavy pack flg*/
static int interactive_b = 0; /* interactive flag */
static int log_trxn_b = 0; /* log mem trxns */
static long max_alloc = MAX_ALLOC; /* amt of mem to use */
static int max_pages_n = 0; /* max # pages */
static int use_malloc_b = 0; /* use system alloc */
static int max_pointers = MAX_POINTERS; /* # of pnts to use */
static int no_free_b = 0; /* set no free flag */
static long page_size = 0; /* mpool pagesize */
static int use_sbrk_b = 0; /* use sbrk not mmap */
static unsigned int seed_random = 0; /* random seed */
static int default_iter_n = DEFAULT_ITERATIONS; /* # of iters */
static int verbose_b = 0; /* verbose flag */
/*
* static long hex_to_long
*
* DESCRIPTION:
*
* Hexadecimal string to integer translation.
*
* RETURNS:
*
* Long value of converted hex string.
*
* ARGUMENTS:
*
* str -> Hex string we are converting.
*/
static long hex_to_long(const char *str)
{
long ret;
const char *str_p = str;
/* strip off spaces */
for (; *str_p == ' ' || *str_p == '\t'; str_p++) {
}
/* skip a leading 0[xX] */
if (*str_p == '0' && (*(str_p + 1) == 'x' || *(str_p + 1) == 'X')) {
str_p += 2;
}
for (ret = 0;; str_p++) {
if (*str_p >= '0' && *str_p <= '9') {
ret = ret * 16 + (*str_p - '0');
}
else if (*str_p >= 'a' && *str_p <= 'f') {
ret = ret * 16 + (*str_p - 'a' + 10);
}
else if (*str_p >= 'A' && *str_p <= 'F') {
ret = ret * 16 + (*str_p - 'A' + 10);
}
else {
break;
}
}
return ret;
}
/*
* static void* get_address
*
* DESCRIPTION:
*
* Read an address from the user.
*
* RETURNS:
*
* Address read in from user.
*
* ARGUMENTS:
*
* None.
*/
static void *get_address(void)
{
char line[80];
void *pnt;
do {
(void)printf("Enter a hex address: ");
if (fgets(line, sizeof(line), stdin) == NULL) {
return NULL;
}
} while (line[0] == '\0');
pnt = (void *)hex_to_long(line);
return pnt;
}
/*
* static void do_random
*
* DESCRIPTION:
*
* Try ITER_N random program iterations, returns 1 on success else 0
*
* RETURNS:
*
* None.
*
* ARGUMENTS:
*
* pool <-> Out memory pool.
*
* iter_n -> Number of iterations to run.
*/
static void do_random(mpool_t *pool, const int iter_n)
{
int iter_c, free_c, ret;
long max = max_alloc, amount;
char *chunk_p;
void *new_pnt;
pnt_info_t *free_p, *used_p = NULL;
pnt_info_t *pnt_p, *last_p;
if (use_malloc_b) {
pointer_grid = (pnt_info_t *)malloc(sizeof(pnt_info_t) * max_pointers);
}
else {
pointer_grid = (pnt_info_t *)mpool_alloc(pool,
sizeof(pnt_info_t) * max_pointers,
&ret);
}
if (pointer_grid == NULL) {
(void)printf("mpool_t: problems allocating %d pointer slots: %s\n",
max_pointers, strerror(errno));
return;
}
/* initialize free list */
free_p = pointer_grid;
for (pnt_p = pointer_grid; pnt_p < pointer_grid + max_pointers; pnt_p++) {
pnt_p->pi_size = 0;
pnt_p->pi_pnt = NULL;
pnt_p->pi_next = pnt_p + 1;
}
/* redo the last next pointer */
(pnt_p - 1)->pi_next = NULL;
free_c = max_pointers;
for (iter_c = 0; iter_c < iter_n;) {
int which;
/* special case when doing non-linear stuff, sbrk took all memory */
if (max < MIN_AVAIL && free_c == max_pointers) {
break;
}
if (free_c < max_pointers && used_p == NULL) {
(void)fprintf(stderr, "mpool_t: problem with test program free list\n");
exit(1);
}
/* decide whether to malloc a new pointer or free/realloc an existing */
which = RANDOM_VALUE(4);
/*
* < MIN_AVAIL means alloc as long as we have enough memory and
* there are free slots we do an allocation, else we free
*/
if (free_c == max_pointers
|| (free_c > 0 && which < 3 && max >= MIN_AVAIL)) {
while (1) {
amount = RANDOM_VALUE(max / 2);
if (amount > 0) {
break;
}
}
which = RANDOM_VALUE(9);
pnt_p = NULL;
switch (which) {
case 0: case 1: case 2:
pnt_p = free_p;
if (use_malloc_b) {
pnt_p->pi_pnt = malloc(amount);
}
else {
pnt_p->pi_pnt = mpool_alloc(pool, amount, &ret);
}
if (verbose_b) {
(void)printf("%d: malloc %ld (max %ld) into slot %d. got %#lx\n",
iter_c + 1, amount, max, pnt_p - pointer_grid,
(long)pnt_p->pi_pnt);
}
if (pnt_p->pi_pnt == NULL) {
(void)printf("malloc of %ld failed: %s\n",
amount,
(use_malloc_b ? strerror(errno) : mpool_strerror(ret)));
}
pnt_p->pi_size = amount;
break;
case 3: case 4: case 5:
pnt_p = free_p;
if (use_malloc_b) {
pnt_p->pi_pnt = calloc(amount, sizeof(char));
}
else {
pnt_p->pi_pnt = mpool_calloc(pool, amount, sizeof(char), &ret);
}
if (verbose_b) {
(void)printf("%d: calloc %ld (max %ld) into slot %d. got %#lx\n",
iter_c + 1, amount, max, pnt_p - pointer_grid,
(long)pnt_p->pi_pnt);
}
/* test the returned block to make sure that is has been cleared */
if (pnt_p->pi_pnt == NULL) {
(void)printf("calloc of %ld failed: %s\n",
amount,
(use_malloc_b ? strerror(errno) : mpool_strerror(ret)));
}
else {
for (chunk_p = pnt_p->pi_pnt;
chunk_p < (char *)pnt_p->pi_pnt + amount;
chunk_p++) {
if (*chunk_p != '\0') {
(void)printf("calloc of %ld not zeroed on iteration #%d\n",
amount, iter_c + 1);
break;
}
}
pnt_p->pi_size = amount;
}
break;
case 6: case 7: case 8:
if (free_c == max_pointers) {
continue;
}
which = RANDOM_VALUE(max_pointers - free_c);
for (pnt_p = used_p; which > 0; which--) {
pnt_p = pnt_p->pi_next;
}
if (use_malloc_b) {
new_pnt = realloc(pnt_p->pi_pnt, amount);
}
else {
new_pnt = mpool_resize(pool, pnt_p->pi_pnt, pnt_p->pi_size, amount,
&ret);
}
if (verbose_b) {
(void)printf("%d: resize %#lx from %ld to %ld (max %ld) slot %d. "
"got %#lx\n",
iter_c + 1, (long)pnt_p->pi_pnt, pnt_p->pi_size, amount,
max, pnt_p - pointer_grid, (long)new_pnt);
}
if (new_pnt == NULL) {
(void)printf("resize of %#lx old size %ld new size %ld failed: %s\n",
(long)pnt_p->pi_pnt, pnt_p->pi_size, amount,
(use_malloc_b ? strerror(errno) : mpool_strerror(ret)));
pnt_p->pi_pnt = NULL;
pnt_p->pi_size = 0;
}
else {
/* we effectively freed the old memory */
max += pnt_p->pi_size;
pnt_p->pi_pnt = new_pnt;
pnt_p->pi_size = amount;
}
break;
default:
break;
}
if (pnt_p != NULL && pnt_p->pi_pnt != NULL) {
if (pnt_p == free_p) {
free_p = pnt_p->pi_next;
pnt_p->pi_next = used_p;
used_p = pnt_p;
free_c--;
}
max -= amount;
iter_c++;
}
continue;
}
/*
* choose a rand slot to free and make sure it is not a free-slot
*/
which = RANDOM_VALUE(max_pointers - free_c);
/* find pnt in the used list */
last_p = NULL;
for (pnt_p = used_p, last_p = NULL;
pnt_p != NULL && which > 0;
last_p = pnt_p, pnt_p = pnt_p->pi_next, which--) {
}
if (pnt_p == NULL) {
/* huh? error here */
abort();
}
if (last_p == NULL) {
used_p = pnt_p->pi_next;
}
else {
last_p->pi_next = pnt_p->pi_next;
}
if (use_malloc_b) {
free(pnt_p->pi_pnt);
}
else {
ret = mpool_free(pool, pnt_p->pi_pnt, pnt_p->pi_size);
if (ret != MPOOL_ERROR_NONE) {
(void)printf("free error on pointer '%#lx' of size %ld: %s\n",
(long)pnt_p->pi_pnt, pnt_p->pi_size,
mpool_strerror(ret));
}
}
if (verbose_b) {
(void)printf("%d: free'd %ld bytes from slot %d (%#lx)\n",
iter_c + 1, pnt_p->pi_size, pnt_p - pointer_grid,
(long)pnt_p->pi_pnt);
}
pnt_p->pi_pnt = NULL;
pnt_p->pi_next = free_p;
free_p = pnt_p;
free_c++;
max += pnt_p->pi_size;
iter_c++;
}
/* free used pointers */
for (pnt_p = pointer_grid; pnt_p < pointer_grid + max_pointers; pnt_p++) {
if (pnt_p->pi_pnt != NULL) {
if (use_malloc_b) {
free(pnt_p->pi_pnt);
}
else {
ret = mpool_free(pool, pnt_p->pi_pnt, pnt_p->pi_size);
if (ret != MPOOL_ERROR_NONE) {
(void)printf("free error on pointer '%#lx' of size %ld: %s\n",
(long)pnt_p->pi_pnt, pnt_p->pi_size,
mpool_strerror(ret));
}
}
}
}
if (use_malloc_b) {
free(pointer_grid);
}
else {
ret = mpool_free(pool, pointer_grid, sizeof(pnt_info_t) * max_pointers);
if (ret != MPOOL_ERROR_NONE) {
(void)printf("free error on grid pointer: %s\n", mpool_strerror(ret));
}
}
}
/*
* static void do_interactive
*
* DESCRIPTION:
*
* Run the interactive section of the program.
*
* RETURNS:
*
* None.
*
* ARGUMENTS:
*
* pool <-> Out memory pool.
*/
static void do_interactive(mpool_t *pool)
{
int len, ret;
char line[128], *line_p;
void *pnt, *new_pnt;
(void)printf("Mpool test program. Type 'help' for assistance.\n");
for (;;) {
(void)printf("> ");
if (fgets(line, sizeof(line), stdin) == NULL) {
break;
}
line_p = strchr(line, '\n');
if (line_p != NULL) {
*line_p = '\0';
}
len = strlen(line);
if (len == 0) {
continue;
}
if (strncmp(line, "?", len) == 0
|| strncmp(line, "help", len) == 0) {
(void)printf("\thelp - print this message\n\n");
(void)printf("\tmalloc - allocate memory\n");
(void)printf("\tcalloc - allocate/clear memory\n");
(void)printf("\tresize - resize memory\n");
(void)printf("\tfree - deallocate memory\n\n");
(void)printf("\tclear - clear the pool\n");
(void)printf("\toverwrite - overwrite some memory to test errors\n");
(void)printf("\trandom - randomly execute a number of [de] allocs\n");
(void)printf("\tquit - quit this test program\n");
continue;
}
if (strncmp(line, "quit", len) == 0) {
break;
}
if (strncmp(line, "malloc", len) == 0) {
int size;
(void)printf("How much to malloc: ");
if (fgets(line, sizeof(line), stdin) == NULL) {
break;
}
size = atoi(line);
pnt = mpool_alloc(pool, size, &ret);
if (pnt == NULL) {
(void)printf("malloc(%d) failed: %s\n", size, mpool_strerror(ret));
}
else {
(void)printf("malloc(%d) returned '%#lx'\n", size, (long)pnt);
}
continue;
}
if (strncmp(line, "calloc", len) == 0) {
int size;
(void)printf("How much to calloc: ");
if (fgets(line, sizeof(line), stdin) == NULL) {
break;
}
size = atoi(line);
pnt = mpool_calloc(pool, size, sizeof(char), &ret);
if (pnt == NULL) {
(void)printf("calloc(%d) failed: %s\n", size, mpool_strerror(ret));
}
else {
(void)printf("calloc(%d) returned '%#lx'\n", size, (long)pnt);
}
continue;
}
if (strncmp(line, "resize", len) == 0) {
int size, old_size;
pnt = get_address();
(void)printf("Old size of allocation: ");
if (fgets(line, sizeof(line), stdin) == NULL) {
break;
}
old_size = atoi(line);
(void)printf("New size of allocation: ");
if (fgets(line, sizeof(line), stdin) == NULL) {
break;
}
size = atoi(line);
new_pnt = mpool_resize(pool, pnt, old_size, size, &ret);
if (new_pnt == NULL) {
(void)printf("resize(%#lx, %d) failed: %s\n",
(long)pnt, size, mpool_strerror(ret));
}
else {
(void)printf("resize(%#lx, %d) returned '%#lx'\n",
(long)pnt, size, (long)new_pnt);
}
continue;
}
if (strncmp(line, "free", len) == 0) {
int old_size;
pnt = get_address();
(void)printf("Old minimum size we are freeing: ");
if (fgets(line, sizeof(line), stdin) == NULL) {
break;
}
old_size = atoi(line);
ret = mpool_free(pool, pnt, old_size);
if (ret != MPOOL_ERROR_NONE) {
(void)fprintf(stderr, "free failed: %s\n", mpool_strerror(ret));
}
continue;
}
if (strncmp(line, "clear", len) == 0) {
ret = mpool_clear(pool);
if (ret == MPOOL_ERROR_NONE) {
(void)fprintf(stderr, "clear succeeded\n");
}
else {
(void)fprintf(stderr, "clear failed: %s\n", mpool_strerror(ret));
}
continue;
}
if (strncmp(line, "overwrite", len) == 0) {
char *overwrite = "OVERWRITTEN";
pnt = get_address();
memcpy((char *)pnt, overwrite, strlen(overwrite));
(void)printf("Done.\n");
continue;
}
/* do random heap hits */
if (strncmp(line, "random", len) == 0) {
int iter_n;
(void)printf("How many iterations[%d]: ", default_iter_n);
if (fgets(line, sizeof(line), stdin) == NULL) {
break;
}
if (line[0] == '\0' || line[0] == '\n') {
iter_n = default_iter_n;
}
else {
iter_n = atoi(line);
}
do_random(pool, iter_n);
continue;
}
(void)printf("Unknown command '%s'. Type 'help' for assistance.\n", line);
}
}
/*
* static void log_func
*
* DESCRIPTION:
*
* Mpool transaction log function.
*
* RETURNS:
*
* None.
*
* ARGUMENT:
*
* mp_p -> Associated mpool address.
*
* func_id -> Integer function ID which identifies which mpool
* function is being called.
*
* byte_size -> Optionally specified byte size.
*
* ele_n -> Optionally specified element number. For mpool_calloc
* only.
*
* new_addr -> Optionally specified new address. For mpool_alloc,
* mpool_calloc, and mpool_resize only.
*
* old_addr -> Optionally specified old address. For mpool_resize and
* mpool_free only.
*
* old_byte_size -> Optionally specified old byte size. For
* mpool_resize only.
*/
static void log_func(const void *mp_p, const int func_id,
const unsigned long byte_size,
const unsigned long ele_n,
const void *new_addr, const void *old_addr,
const unsigned long old_byte_size)
{
(void)printf("mp %#lx ", (long)mp_p);
switch (func_id) {
case MPOOL_FUNC_CLOSE:
(void)printf("close\n");
break;
case MPOOL_FUNC_CLEAR:
(void)printf("clear\n");
break;
case MPOOL_FUNC_ALLOC:
(void)printf("alloc %lu bytes got %#lx\n",
byte_size, (long)new_addr);
break;
case MPOOL_FUNC_CALLOC:
(void)printf("calloc %lu ele size, %lu ele number, got %#lx\n",
byte_size, ele_n, (long)new_addr);
break;
case MPOOL_FUNC_FREE:
(void)printf("free %#lx of %lu bytes\n", (long)old_addr, byte_size);
break;
case MPOOL_FUNC_RESIZE:
(void)printf("resize %#lx of %lu bytes to %lu bytes, got %#lx\n",
(long)old_addr, old_byte_size, byte_size,
(long)new_addr);
break;
default:
(void)printf("unknown function %d, %lu bytes\n", func_id, byte_size);
break;
}
}
/*
* static void usage
*
* DESCRIPTION:
*
* Print a usage message.
*
* RETURNS:
*
* None.
*
* ARGUMENTS:
*
* None.
*/
static void usage(void)
{
(void)fprintf(stderr,
"Usage: mpool_t [-bhHilMnsv] [-m size] [-p number] "
"[-P size] [-S seed] [-t times]\n");
(void)fprintf(stderr,
" -b set MPOOL_FLAG_BEST_FIT\n"
" -h set MPOOL_FLAG_NO_FREE\n"
" -H use system heap not mpool\n"
" -i turn on interactive mode\n"
" -l log memory transactions\n"
" -M max number pages in mpool\n"
" -n set MPOOL_FLAG_NO_FREE\n"
" -s use sbrk instead of mmap\n"
" -v enable verbose messages\n"
" -m size maximum allocation to test\n"
" -p max-pnts number of pointers to test\n"
" -S seed-rand seed for random function\n"
" -t interations number of iterations to run\n");
exit(1);
}
/*
* static void process_args
*
* DESCRIPTION:
*
* Process our arguments.
*
* RETURNS:
*
* None.
*
* ARGUMENTS:
*
* None.
*/
static void process_args(int argc, char ** argv)
{
argc--, argv++;
/* process the args */
for (; *argv != NULL; argv++, argc--) {
if (**argv != '-') {
continue;
}
switch (*(*argv + 1)) {
case 'b':
best_fit_b = 1;
break;
case 'h':
heavy_pack_b = 1;
break;
case 'H':
use_malloc_b = 1;
break;
case 'i':
interactive_b = 1;
break;
case 'l':
log_trxn_b = 1;
break;
case 'm':
argv++, argc--;
if (argc <= 0) {
usage();
}
max_alloc = atoi(*argv);
break;
case 'M':
max_pages_n = 1;
break;
case 'n':
no_free_b = 1;
break;
case 'p':
argv++, argc--;
if (argc <= 0) {
usage();
}
max_pointers = atoi(*argv);
break;
case 'P':
argv++, argc--;
if (argc <= 0) {
usage();
}
page_size = atoi(*argv);
break;
case 'S':
argv++, argc--;
if (argc <= 0) {
usage();
}
seed_random = atoi(*argv);
break;
case 't':
argv++, argc--;
if (argc <= 0) {
usage();
}
default_iter_n = atoi(*argv);
break;
case 'v':
verbose_b = 1;
break;
default:
usage();
break;
}
}
}
/*
* Main routine
*/
int main(int argc, char **argv)
{
int ret;
unsigned int flags = 0, pool_page_size;
unsigned long num_alloced, user_alloced, max_alloced, tot_alloced;
mpool_t *pool;
process_args(argc, argv);
/* repeat until we get a non 0 seed */
while (seed_random == 0) {
seed_random = time(0) ^ getpid();
}
(void)srandom(seed_random);
(void)printf("Random seed is %u\n", seed_random);
if (best_fit_b) {
flags |= MPOOL_FLAG_BEST_FIT;
}
if (heavy_pack_b) {
flags |= MPOOL_FLAG_HEAVY_PACKING;
}
if (no_free_b) {
flags |= MPOOL_FLAG_NO_FREE;
}
if (use_sbrk_b) {
flags |= MPOOL_FLAG_USE_SBRK;
}
/* open our memory pool */
pool = mpool_open(flags, page_size, NULL, &ret);
if (pool == NULL) {
(void)fprintf(stderr, "Error in mpool_open: %s\n", mpool_strerror(ret));
exit(1);
}
/* are we logging transactions */
if (log_trxn_b) {
ret = mpool_set_log_func(pool, log_func);
if (ret != MPOOL_ERROR_NONE) {
(void)fprintf(stderr, "Error in mpool_set_log_func: %s\n",
mpool_strerror(ret));
}
}
if (max_pages_n > 0) {
ret = mpool_set_max_pages(pool, max_pages_n);
if (ret != MPOOL_ERROR_NONE) {
(void)fprintf(stderr, "Error in mpool_set_max_pages: %s\n",
mpool_strerror(ret));
}
}
if (interactive_b) {
do_interactive(pool);
}
else {
(void)printf("Running %d tests (use -i for interactive)...\n",
default_iter_n);
(void)fflush(stdout);
do_random(pool, default_iter_n);
}
/* get stats from the pool */
ret = mpool_stats(pool, &pool_page_size, &num_alloced, &user_alloced,
&max_alloced, &tot_alloced);
if (ret == MPOOL_ERROR_NONE) {
(void)printf("Pool page size = %d. Number active allocated = %lu\n",
pool_page_size, num_alloced);
(void)printf("User bytes allocated = %lu. Max space allocated = %lu\n",
user_alloced, max_alloced);
(void)printf("Total space allocated = %lu\n", tot_alloced);
}
else {
(void)fprintf(stderr, "Error in mpool_stats: %s\n", mpool_strerror(ret));
}
/* close the pool */
ret = mpool_close(pool);
if (ret != MPOOL_ERROR_NONE) {
(void)fprintf(stderr, "Error in mpool_close: %s\n", mpool_strerror(ret));
exit(1);
}
exit(0);
}
/*
** 2001 September 15
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains code to implement the "sqlite" command line
** utility for accessing SQLite databases.
*/
#if defined(_WIN32) || defined(WIN32)
/* This needs to come before any includes for MSVC compiler */
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include "sqlite3.h"
#include <ctype.h>
#include <stdarg.h>
#if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__)
# include <signal.h>
# if !defined(__RTP__) && !defined(_WRS_KERNEL)
# include <pwd.h>
# endif
# include <unistd.h>
# include <sys/types.h>
#endif
#ifdef __OS2__
# include <unistd.h>
#endif
#if defined(HAVE_READLINE) && HAVE_READLINE==1
# include <readline/readline.h>
# include <readline/history.h>
#else
# define readline(p) local_getline(p,stdin)
# define add_history(X)
# define read_history(X)
# define write_history(X)
# define stifle_history(X)
#endif
#if defined(_WIN32) || defined(WIN32)
# include <io.h>
#define isatty(h) _isatty(h)
#define access(f,m) _access((f),(m))
#else
/* Make sure isatty() has a prototype.
*/
extern int isatty();
#endif
#if defined(_WIN32_WCE)
/* Windows CE (arm-wince-mingw32ce-gcc) does not provide isatty()
* thus we always assume that we have a console. That can be
* overridden with the -batch command line option.
*/
#define isatty(x) 1
#endif
#if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) && !defined(__RTP__) && !defined(_WRS_KERNEL)
#include <sys/time.h>
#include <sys/resource.h>
/* Saved resource information for the beginning of an operation */
static struct rusage sBegin;
/* True if the timer is enabled */
static int enableTimer = 0;
/*
** Begin timing an operation
*/
static void beginTimer(void){
if( enableTimer ){
getrusage(RUSAGE_SELF, &sBegin);
}
}
/* Return the difference of two time_structs in seconds */
static double timeDiff(struct timeval *pStart, struct timeval *pEnd){
return (pEnd->tv_usec - pStart->tv_usec)*0.000001 +
(double)(pEnd->tv_sec - pStart->tv_sec);
}
/*
** Print the timing results.
*/
static void endTimer(void){
if( enableTimer ){
struct rusage sEnd;
getrusage(RUSAGE_SELF, &sEnd);
printf("CPU Time: user %f sys %f\n",
timeDiff(&sBegin.ru_utime, &sEnd.ru_utime),
timeDiff(&sBegin.ru_stime, &sEnd.ru_stime));
}
}
#define BEGIN_TIMER beginTimer()
#define END_TIMER endTimer()
#define HAS_TIMER 1
#elif (defined(_WIN32) || defined(WIN32))
#include <windows.h>
/* Saved resource information for the beginning of an operation */
static HANDLE hProcess;
static FILETIME ftKernelBegin;
static FILETIME ftUserBegin;
typedef BOOL (WINAPI *GETPROCTIMES)(HANDLE, LPFILETIME, LPFILETIME, LPFILETIME, LPFILETIME);
static GETPROCTIMES getProcessTimesAddr = NULL;
/* True if the timer is enabled */
static int enableTimer = 0;
/*
** Check to see if we have timer support. Return 1 if necessary
** support found (or found previously).
*/
static int hasTimer(void){
if( getProcessTimesAddr ){
return 1;
} else {
/* GetProcessTimes() isn't supported in WIN95 and some other Windows versions.
** See if the version we are running on has it, and if it does, save off
** a pointer to it and the current process handle.
*/
hProcess = GetCurrentProcess();
if( hProcess ){
HINSTANCE hinstLib = LoadLibrary(TEXT("Kernel32.dll"));
if( NULL != hinstLib ){
getProcessTimesAddr = (GETPROCTIMES) GetProcAddress(hinstLib, "GetProcessTimes");
if( NULL != getProcessTimesAddr ){
return 1;
}
FreeLibrary(hinstLib);
}
}
}
return 0;
}
/*
** Begin timing an operation
*/
static void beginTimer(void){
if( enableTimer && getProcessTimesAddr ){
FILETIME ftCreation, ftExit;
getProcessTimesAddr(hProcess, &ftCreation, &ftExit, &ftKernelBegin, &ftUserBegin);
}
}
/* Return the difference of two FILETIME structs in seconds */
static double timeDiff(FILETIME *pStart, FILETIME *pEnd){
sqlite_int64 i64Start = *((sqlite_int64 *) pStart);
sqlite_int64 i64End = *((sqlite_int64 *) pEnd);
return (double) ((i64End - i64Start) / 10000000.0);
}
/*
** Print the timing results.
*/
static void endTimer(void){
if( enableTimer && getProcessTimesAddr){
FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd;
getProcessTimesAddr(hProcess, &ftCreation, &ftExit, &ftKernelEnd, &ftUserEnd);
printf("CPU Time: user %f sys %f\n",
timeDiff(&ftUserBegin, &ftUserEnd),
timeDiff(&ftKernelBegin, &ftKernelEnd));
}
}
#define BEGIN_TIMER beginTimer()
#define END_TIMER endTimer()
#define HAS_TIMER hasTimer()
#else
#define BEGIN_TIMER
#define END_TIMER
#define HAS_TIMER 0
#endif
/*
** Used to prevent warnings about unused parameters
*/
#define UNUSED_PARAMETER(x) (void)(x)
/*
** If the following flag is set, then command execution stops
** at an error if we are not interactive.
*/
static int bail_on_error = 0;
/*
** Threat stdin as an interactive input if the following variable
** is true. Otherwise, assume stdin is connected to a file or pipe.
*/
static int stdin_is_interactive = 1;
/*
** The following is the open SQLite database. We make a pointer
** to this database a static variable so that it can be accessed
** by the SIGINT handler to interrupt database processing.
*/
static sqlite3 *db = 0;
/*
** True if an interrupt (Control-C) has been received.
*/
static volatile int seenInterrupt = 0;
/*
** This is the name of our program. It is set in main(), used
** in a number of other places, mostly for error messages.
*/
static char *Argv0;
/*
** Prompt strings. Initialized in main. Settable with
** .prompt main continue
*/
static char mainPrompt[20]; /* First line prompt. default: "sqlite> "*/
static char continuePrompt[20]; /* Continuation prompt. default: " ...> " */
/*
** Write I/O traces to the following stream.
*/
#ifdef SQLITE_ENABLE_IOTRACE
static FILE *iotrace = 0;
#endif
/*
** This routine works like printf in that its first argument is a
** format string and subsequent arguments are values to be substituted
** in place of % fields. The result of formatting this string
** is written to iotrace.
*/
#ifdef SQLITE_ENABLE_IOTRACE
static void iotracePrintf(const char *zFormat, ...){
va_list ap;
char *z;
if( iotrace==0 ) return;
va_start(ap, zFormat);
z = sqlite3_vmprintf(zFormat, ap);
va_end(ap);
fprintf(iotrace, "%s", z);
sqlite3_free(z);
}
#endif
/*
** Determines if a string is a number of not.
*/
static int isNumber(const char *z, int *realnum){
if( *z=='-' || *z=='+' ) z++;
if( !isdigit(*z) ){
return 0;
}
z++;
if( realnum ) *realnum = 0;
while( isdigit(*z) ){ z++; }
if( *z=='.' ){
z++;
if( !isdigit(*z) ) return 0;
while( isdigit(*z) ){ z++; }
if( realnum ) *realnum = 1;
}
if( *z=='e' || *z=='E' ){
z++;
if( *z=='+' || *z=='-' ) z++;
if( !isdigit(*z) ) return 0;
while( isdigit(*z) ){ z++; }
if( realnum ) *realnum = 1;
}
return *z==0;
}
/*
** A global char* and an SQL function to access its current value
** from within an SQL statement. This program used to use the
** sqlite_exec_printf() API to substitue a string into an SQL statement.
** The correct way to do this with sqlite3 is to use the bind API, but
** since the shell is built around the callback paradigm it would be a lot
** of work. Instead just use this hack, which is quite harmless.
*/
static const char *zShellStatic = 0;
static void shellstaticFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
assert( 0==argc );
assert( zShellStatic );
UNUSED_PARAMETER(argc);
UNUSED_PARAMETER(argv);
sqlite3_result_text(context, zShellStatic, -1, SQLITE_STATIC);
}
/*
** This routine reads a line of text from FILE in, stores
** the text in memory obtained from malloc() and returns a pointer
** to the text. NULL is returned at end of file, or if malloc()
** fails.
**
** The interface is like "readline" but no command-line editing
** is done.
*/
static char *local_getline(char *zPrompt, FILE *in){
char *zLine;
int nLine;
int n;
int eol;
if( zPrompt && *zPrompt ){
printf("%s",zPrompt);
fflush(stdout);
}
nLine = 100;
zLine = malloc( nLine );
if( zLine==0 ) return 0;
n = 0;
eol = 0;
while( !eol ){
if( n+100>nLine ){
nLine = nLine*2 + 100;
zLine = realloc(zLine, nLine);
if( zLine==0 ) return 0;
}
if( fgets(&zLine[n], nLine - n, in)==0 ){
if( n==0 ){
free(zLine);
return 0;
}
zLine[n] = 0;
eol = 1;
break;
}
while( zLine[n] ){ n++; }
if( n>0 && zLine[n-1]=='\n' ){
n--;
if( n>0 && zLine[n-1]=='\r' ) n--;
zLine[n] = 0;
eol = 1;
}
}
zLine = realloc( zLine, n+1 );
return zLine;
}
/*
** Retrieve a single line of input text.
**
** zPrior is a string of prior text retrieved. If not the empty
** string, then issue a continuation prompt.
*/
static char *one_input_line(const char *zPrior, FILE *in){
char *zPrompt;
char *zResult;
if( in!=0 ){
return local_getline(0, in);
}
if( zPrior && zPrior[0] ){
zPrompt = continuePrompt;
}else{
zPrompt = mainPrompt;
}
zResult = readline(zPrompt);
#if defined(HAVE_READLINE) && HAVE_READLINE==1
if( zResult && *zResult ) add_history(zResult);
#endif
return zResult;
}
struct previous_mode_data {
int valid; /* Is there legit data in here? */
int mode;
int showHeader;
int colWidth[100];
};
/*
** An pointer to an instance of this structure is passed from
** the main program to the callback. This is used to communicate
** state and mode information.
*/
struct callback_data {
sqlite3 *db; /* The database */
int echoOn; /* True to echo input commands */
int statsOn; /* True to display memory stats before each finalize */
int cnt; /* Number of records displayed so far */
FILE *out; /* Write results here */
int mode; /* An output mode setting */
int writableSchema; /* True if PRAGMA writable_schema=ON */
int showHeader; /* True to show column names in List or Column mode */
char *zDestTable; /* Name of destination table when MODE_Insert */
char separator[20]; /* Separator character for MODE_List */
int colWidth[100]; /* Requested width of each column when in column mode*/
int actualWidth[100]; /* Actual width of each column */
char nullvalue[20]; /* The text to print when a NULL comes back from
** the database */
struct previous_mode_data explainPrev;
/* Holds the mode information just before
** .explain ON */
char outfile[FILENAME_MAX]; /* Filename for *out */
const char *zDbFilename; /* name of the database file */
sqlite3_stmt *pStmt; /* Current statement if any. */
FILE *pLog; /* Write log output here */
};
/*
** These are the allowed modes.
*/
#define MODE_Line 0 /* One column per line. Blank line between records */
#define MODE_Column 1 /* One record per line in neat columns */
#define MODE_List 2 /* One record per line with a separator */
#define MODE_Semi 3 /* Same as MODE_List but append ";" to each line */
#define MODE_Html 4 /* Generate an XHTML table */
#define MODE_Insert 5 /* Generate SQL "insert" statements */
#define MODE_Tcl 6 /* Generate ANSI-C or TCL quoted elements */
#define MODE_Csv 7 /* Quote strings, numbers are plain */
#define MODE_Explain 8 /* Like MODE_Column, but do not truncate data */
static const char *modeDescr[] = {
"line",
"column",
"list",
"semi",
"html",
"insert",
"tcl",
"csv",
"explain",
};
/*
** Number of elements in an array
*/
#define ArraySize(X) (int)(sizeof(X)/sizeof(X[0]))
/*
** Compute a string length that is limited to what can be stored in
** lower 30 bits of a 32-bit signed integer.
*/
static int strlen30(const char *z){
const char *z2 = z;
while( *z2 ){ z2++; }
return 0x3fffffff & (int)(z2 - z);
}
/*
** A callback for the sqlite3_log() interface.
*/
static void shellLog(void *pArg, int iErrCode, const char *zMsg){
struct callback_data *p = (struct callback_data*)pArg;
if( p->pLog==0 ) return;
fprintf(p->pLog, "(%d) %s\n", iErrCode, zMsg);
fflush(p->pLog);
}
/*
** Output the given string as a hex-encoded blob (eg. X'1234' )
*/
static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){
int i;
char *zBlob = (char *)pBlob;
fprintf(out,"X'");
for(i=0; i<nBlob; i++){ fprintf(out,"%02x",zBlob[i]); }
fprintf(out,"'");
}
/*
** Output the given string as a quoted string using SQL quoting conventions.
*/
static void output_quoted_string(FILE *out, const char *z){
int i;
int nSingle = 0;
for(i=0; z[i]; i++){
if( z[i]=='\'' ) nSingle++;
}
if( nSingle==0 ){
fprintf(out,"'%s'",z);
}else{
fprintf(out,"'");
while( *z ){
for(i=0; z[i] && z[i]!='\''; i++){}
if( i==0 ){
fprintf(out,"''");
z++;
}else if( z[i]=='\'' ){
fprintf(out,"%.*s''",i,z);
z += i+1;
}else{
fprintf(out,"%s",z);
break;
}
}
fprintf(out,"'");
}
}
/*
** Output the given string as a quoted according to C or TCL quoting rules.
*/
static void output_c_string(FILE *out, const char *z){
unsigned int c;
fputc('"', out);
while( (c = *(z++))!=0 ){
if( c=='\\' ){
fputc(c, out);
fputc(c, out);
}else if( c=='\t' ){
fputc('\\', out);
fputc('t', out);
}else if( c=='\n' ){
fputc('\\', out);
fputc('n', out);
}else if( c=='\r' ){
fputc('\\', out);
fputc('r', out);
}else if( !isprint(c) ){
fprintf(out, "\\%03o", c&0xff);
}else{
fputc(c, out);
}
}
fputc('"', out);
}
/*
** Output the given string with characters that are special to
** HTML escaped.
*/
static void output_html_string(FILE *out, const char *z){
int i;
while( *z ){
for(i=0; z[i]
&& z[i]!='<'
&& z[i]!='&'
&& z[i]!='>'
&& z[i]!='\"'
&& z[i]!='\'';
i++){}
if( i>0 ){
fprintf(out,"%.*s",i,z);
}
if( z[i]=='<' ){
fprintf(out,"&lt;");
}else if( z[i]=='&' ){
fprintf(out,"&amp;");
}else if( z[i]=='>' ){
fprintf(out,"&gt;");
}else if( z[i]=='\"' ){
fprintf(out,"&quot;");
}else if( z[i]=='\'' ){
fprintf(out,"&#39;");
}else{
break;
}
z += i + 1;
}
}
/*
** If a field contains any character identified by a 1 in the following
** array, then the string must be quoted for CSV.
*/
static const char needCsvQuote[] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
};
/*
** Output a single term of CSV. Actually, p->separator is used for
** the separator, which may or may not be a comma. p->nullvalue is
** the null value. Strings are quoted using ANSI-C rules. Numbers
** appear outside of quotes.
*/
static void output_csv(struct callback_data *p, const char *z, int bSep){
FILE *out = p->out;
if( z==0 ){
fprintf(out,"%s",p->nullvalue);
}else{
int i;
int nSep = strlen30(p->separator);
for(i=0; z[i]; i++){
if( needCsvQuote[((unsigned char*)z)[i]]
|| (z[i]==p->separator[0] &&
(nSep==1 || memcmp(z, p->separator, nSep)==0)) ){
i = 0;
break;
}
}
if( i==0 ){
putc('"', out);
for(i=0; z[i]; i++){
if( z[i]=='"' ) putc('"', out);
putc(z[i], out);
}
putc('"', out);
}else{
fprintf(out, "%s", z);
}
}
if( bSep ){
fprintf(p->out, "%s", p->separator);
}
}
#ifdef SIGINT
/*
** This routine runs when the user presses Ctrl-C
*/
static void interrupt_handler(int NotUsed){
UNUSED_PARAMETER(NotUsed);
seenInterrupt = 1;
if( db ) sqlite3_interrupt(db);
}
#endif
/*
** This is the callback routine that the shell
** invokes for each row of a query result.
*/
static int shell_callback(void *pArg, int nArg, char **azArg, char **azCol, int *aiType){
int i;
struct callback_data *p = (struct callback_data*)pArg;
switch( p->mode ){
case MODE_Line: {
int w = 5;
if( azArg==0 ) break;
for(i=0; i<nArg; i++){
int len = strlen30(azCol[i] ? azCol[i] : "");
if( len>w ) w = len;
}
if( p->cnt++>0 ) fprintf(p->out,"\n");
for(i=0; i<nArg; i++){
fprintf(p->out,"%*s = %s\n", w, azCol[i],
azArg[i] ? azArg[i] : p->nullvalue);
}
break;
}
case MODE_Explain:
case MODE_Column: {
if( p->cnt++==0 ){
for(i=0; i<nArg; i++){
int w, n;
if( i<ArraySize(p->colWidth) ){
w = p->colWidth[i];
}else{
w = 0;
}
if( w<=0 ){
w = strlen30(azCol[i] ? azCol[i] : "");
if( w<10 ) w = 10;
n = strlen30(azArg && azArg[i] ? azArg[i] : p->nullvalue);
if( w<n ) w = n;
}
if( i<ArraySize(p->actualWidth) ){
p->actualWidth[i] = w;
}
if( p->showHeader ){
fprintf(p->out,"%-*.*s%s",w,w,azCol[i], i==nArg-1 ? "\n": " ");
}
}
if( p->showHeader ){
for(i=0; i<nArg; i++){
int w;
if( i<ArraySize(p->actualWidth) ){
w = p->actualWidth[i];
}else{
w = 10;
}
fprintf(p->out,"%-*.*s%s",w,w,"-----------------------------------"
"----------------------------------------------------------",
i==nArg-1 ? "\n": " ");
}
}
}
if( azArg==0 ) break;
for(i=0; i<nArg; i++){
int w;
if( i<ArraySize(p->actualWidth) ){
w = p->actualWidth[i];
}else{
w = 10;
}
if( p->mode==MODE_Explain && azArg[i] &&
strlen30(azArg[i])>w ){
w = strlen30(azArg[i]);
}
fprintf(p->out,"%-*.*s%s",w,w,
azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": " ");
}
break;
}
case MODE_Semi:
case MODE_List: {
if( p->cnt++==0 && p->showHeader ){
for(i=0; i<nArg; i++){
fprintf(p->out,"%s%s",azCol[i], i==nArg-1 ? "\n" : p->separator);
}
}
if( azArg==0 ) break;
for(i=0; i<nArg; i++){
char *z = azArg[i];
if( z==0 ) z = p->nullvalue;
fprintf(p->out, "%s", z);
if( i<nArg-1 ){
fprintf(p->out, "%s", p->separator);
}else if( p->mode==MODE_Semi ){
fprintf(p->out, ";\n");
}else{
fprintf(p->out, "\n");
}
}
break;
}
case MODE_Html: {
if( p->cnt++==0 && p->showHeader ){
fprintf(p->out,"<TR>");
for(i=0; i<nArg; i++){
fprintf(p->out,"<TH>");
output_html_string(p->out, azCol[i]);
fprintf(p->out,"</TH>\n");
}
fprintf(p->out,"</TR>\n");
}
if( azArg==0 ) break;
fprintf(p->out,"<TR>");
for(i=0; i<nArg; i++){
fprintf(p->out,"<TD>");
output_html_string(p->out, azArg[i] ? azArg[i] : p->nullvalue);
fprintf(p->out,"</TD>\n");
}
fprintf(p->out,"</TR>\n");
break;
}
case MODE_Tcl: {
if( p->cnt++==0 && p->showHeader ){
for(i=0; i<nArg; i++){
output_c_string(p->out,azCol[i] ? azCol[i] : "");
fprintf(p->out, "%s", p->separator);
}
fprintf(p->out,"\n");
}
if( azArg==0 ) break;
for(i=0; i<nArg; i++){
output_c_string(p->out, azArg[i] ? azArg[i] : p->nullvalue);
fprintf(p->out, "%s", p->separator);
}
fprintf(p->out,"\n");
break;
}
case MODE_Csv: {
if( p->cnt++==0 && p->showHeader ){
for(i=0; i<nArg; i++){
output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1);
}
fprintf(p->out,"\n");
}
if( azArg==0 ) break;
for(i=0; i<nArg; i++){
output_csv(p, azArg[i], i<nArg-1);
}
fprintf(p->out,"\n");
break;
}
case MODE_Insert: {
p->cnt++;
if( azArg==0 ) break;
fprintf(p->out,"INSERT INTO %s VALUES(",p->zDestTable);
for(i=0; i<nArg; i++){
char *zSep = i>0 ? ",": "";
if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){
fprintf(p->out,"%sNULL",zSep);
}else if( aiType && aiType[i]==SQLITE_TEXT ){
if( zSep[0] ) fprintf(p->out,"%s",zSep);
output_quoted_string(p->out, azArg[i]);
}else if( aiType && (aiType[i]==SQLITE_INTEGER || aiType[i]==SQLITE_FLOAT) ){
fprintf(p->out,"%s%s",zSep, azArg[i]);
}else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){
const void *pBlob = sqlite3_column_blob(p->pStmt, i);
int nBlob = sqlite3_column_bytes(p->pStmt, i);
if( zSep[0] ) fprintf(p->out,"%s",zSep);
output_hex_blob(p->out, pBlob, nBlob);
}else if( isNumber(azArg[i], 0) ){
fprintf(p->out,"%s%s",zSep, azArg[i]);
}else{
if( zSep[0] ) fprintf(p->out,"%s",zSep);
output_quoted_string(p->out, azArg[i]);
}
}
fprintf(p->out,");\n");
break;
}
}
return 0;
}
/*
** This is the callback routine that the SQLite library
** invokes for each row of a query result.
*/
static int callback(void *pArg, int nArg, char **azArg, char **azCol){
/* since we don't have type info, call the shell_callback with a NULL value */
return shell_callback(pArg, nArg, azArg, azCol, NULL);
}
/*
** Set the destination table field of the callback_data structure to
** the name of the table given. Escape any quote characters in the
** table name.
*/
static void set_table_name(struct callback_data *p, const char *zName){
int i, n;
int needQuote;
char *z;
if( p->zDestTable ){
free(p->zDestTable);
p->zDestTable = 0;
}
if( zName==0 ) return;
needQuote = !isalpha((unsigned char)*zName) && *zName!='_';
for(i=n=0; zName[i]; i++, n++){
if( !isalnum((unsigned char)zName[i]) && zName[i]!='_' ){
needQuote = 1;
if( zName[i]=='\'' ) n++;
}
}
if( needQuote ) n += 2;
z = p->zDestTable = malloc( n+1 );
if( z==0 ){
fprintf(stderr,"Error: out of memory\n");
exit(1);
}
n = 0;
if( needQuote ) z[n++] = '\'';
for(i=0; zName[i]; i++){
z[n++] = zName[i];
if( zName[i]=='\'' ) z[n++] = '\'';
}
if( needQuote ) z[n++] = '\'';
z[n] = 0;
}
/* zIn is either a pointer to a NULL-terminated string in memory obtained
** from malloc(), or a NULL pointer. The string pointed to by zAppend is
** added to zIn, and the result returned in memory obtained from malloc().
** zIn, if it was not NULL, is freed.
**
** If the third argument, quote, is not '\0', then it is used as a
** quote character for zAppend.
*/
static char *appendText(char *zIn, char const *zAppend, char quote){
int len;
int i;
int nAppend = strlen30(zAppend);
int nIn = (zIn?strlen30(zIn):0);
len = nAppend+nIn+1;
if( quote ){
len += 2;
for(i=0; i<nAppend; i++){
if( zAppend[i]==quote ) len++;
}
}
zIn = (char *)realloc(zIn, len);
if( !zIn ){
return 0;
}
if( quote ){
char *zCsr = &zIn[nIn];
*zCsr++ = quote;
for(i=0; i<nAppend; i++){
*zCsr++ = zAppend[i];
if( zAppend[i]==quote ) *zCsr++ = quote;
}
*zCsr++ = quote;
*zCsr++ = '\0';
assert( (zCsr-zIn)==len );
}else{
memcpy(&zIn[nIn], zAppend, nAppend);
zIn[len-1] = '\0';
}
return zIn;
}
/*
** Execute a query statement that has a single result column. Print
** that result column on a line by itself with a semicolon terminator.
**
** This is used, for example, to show the schema of the database by
** querying the SQLITE_MASTER table.
*/
static int run_table_dump_query(
FILE *out, /* Send output here */
sqlite3 *db, /* Database to query */
const char *zSelect, /* SELECT statement to extract content */
const char *zFirstRow /* Print before first row, if not NULL */
){
sqlite3_stmt *pSelect;
int rc;
rc = sqlite3_prepare(db, zSelect, -1, &pSelect, 0);
if( rc!=SQLITE_OK || !pSelect ){
return rc;
}
rc = sqlite3_step(pSelect);
while( rc==SQLITE_ROW ){
if( zFirstRow ){
fprintf(out, "%s", zFirstRow);
zFirstRow = 0;
}
fprintf(out, "%s;\n", sqlite3_column_text(pSelect, 0));
rc = sqlite3_step(pSelect);
}
return sqlite3_finalize(pSelect);
}
/*
** Allocate space and save off current error string.
*/
static char *save_err_msg(
sqlite3 *db /* Database to query */
){
int nErrMsg = 1+strlen30(sqlite3_errmsg(db));
char *zErrMsg = sqlite3_malloc(nErrMsg);
if( zErrMsg ){
memcpy(zErrMsg, sqlite3_errmsg(db), nErrMsg);
}
return zErrMsg;
}
/*
** Display memory stats.
*/
static int display_stats(
sqlite3 *db, /* Database to query */
struct callback_data *pArg, /* Pointer to struct callback_data */
int bReset /* True to reset the stats */
){
int iCur;
int iHiwtr;
if( pArg && pArg->out ){
iHiwtr = iCur = -1;
sqlite3_status(SQLITE_STATUS_MEMORY_USED, &iCur, &iHiwtr, bReset);
fprintf(pArg->out, "Memory Used: %d (max %d) bytes\n", iCur, iHiwtr);
iHiwtr = iCur = -1;
sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &iCur, &iHiwtr, bReset);
fprintf(pArg->out, "Number of Allocations: %d (max %d)\n", iCur, iHiwtr);
/*
** Not currently used by the CLI.
** iHiwtr = iCur = -1;
** sqlite3_status(SQLITE_STATUS_PAGECACHE_USED, &iCur, &iHiwtr, bReset);
** fprintf(pArg->out, "Number of Pcache Pages Used: %d (max %d) pages\n", iCur, iHiwtr);
*/
iHiwtr = iCur = -1;
sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &iCur, &iHiwtr, bReset);
fprintf(pArg->out, "Number of Pcache Overflow Bytes: %d (max %d) bytes\n", iCur, iHiwtr);
/*
** Not currently used by the CLI.
** iHiwtr = iCur = -1;
** sqlite3_status(SQLITE_STATUS_SCRATCH_USED, &iCur, &iHiwtr, bReset);
** fprintf(pArg->out, "Number of Scratch Allocations Used: %d (max %d)\n", iCur, iHiwtr);
*/
iHiwtr = iCur = -1;
sqlite3_status(SQLITE_STATUS_SCRATCH_OVERFLOW, &iCur, &iHiwtr, bReset);
fprintf(pArg->out, "Number of Scratch Overflow Bytes: %d (max %d) bytes\n", iCur, iHiwtr);
iHiwtr = iCur = -1;
sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &iCur, &iHiwtr, bReset);
fprintf(pArg->out, "Largest Allocation: %d bytes\n", iHiwtr);
iHiwtr = iCur = -1;
sqlite3_status(SQLITE_STATUS_PAGECACHE_SIZE, &iCur, &iHiwtr, bReset);
fprintf(pArg->out, "Largest Pcache Allocation: %d bytes\n", iHiwtr);
iHiwtr = iCur = -1;
sqlite3_status(SQLITE_STATUS_SCRATCH_SIZE, &iCur, &iHiwtr, bReset);
fprintf(pArg->out, "Largest Scratch Allocation: %d bytes\n", iHiwtr);
#ifdef YYTRACKMAXSTACKDEPTH
iHiwtr = iCur = -1;
sqlite3_status(SQLITE_STATUS_PARSER_STACK, &iCur, &iHiwtr, bReset);
fprintf(pArg->out, "Deepest Parser Stack: %d (max %d)\n", iCur, iHiwtr);
#endif
}
if( pArg && pArg->out && db ){
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED, &iCur, &iHiwtr, bReset);
fprintf(pArg->out, "Lookaside Slots Used: %d (max %d)\n", iCur, iHiwtr);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset);
fprintf(pArg->out, "Pager Heap Usage: %d bytes\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset);
fprintf(pArg->out, "Schema Heap Usage: %d bytes\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHiwtr, bReset);
fprintf(pArg->out, "Statement Heap/Lookaside Usage: %d bytes\n", iCur);
}
if( pArg && pArg->out && db && pArg->pStmt ){
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP, bReset);
fprintf(pArg->out, "Fullscan Steps: %d\n", iCur);
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset);
fprintf(pArg->out, "Sort Operations: %d\n", iCur);
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX, bReset);
fprintf(pArg->out, "Autoindex Inserts: %d\n", iCur);
}
return 0;
}
/*
** Execute a statement or set of statements. Print
** any result rows/columns depending on the current mode
** set via the supplied callback.
**
** This is very similar to SQLite's built-in sqlite3_exec()
** function except it takes a slightly different callback
** and callback data argument.
*/
static int shell_exec(
sqlite3 *db, /* An open database */
const char *zSql, /* SQL to be evaluated */
int (*xCallback)(void*,int,char**,char**,int*), /* Callback function */
/* (not the same as sqlite3_exec) */
struct callback_data *pArg, /* Pointer to struct callback_data */
char **pzErrMsg /* Error msg written here */
){
sqlite3_stmt *pStmt = NULL; /* Statement to execute. */
int rc = SQLITE_OK; /* Return Code */
const char *zLeftover; /* Tail of unprocessed SQL */
if( pzErrMsg ){
*pzErrMsg = NULL;
}
while( zSql[0] && (SQLITE_OK == rc) ){
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover);
if( SQLITE_OK != rc ){
if( pzErrMsg ){
*pzErrMsg = save_err_msg(db);
}
}else{
if( !pStmt ){
/* this happens for a comment or white-space */
zSql = zLeftover;
while( isspace(zSql[0]) ) zSql++;
continue;
}
/* save off the prepared statment handle and reset row count */
if( pArg ){
pArg->pStmt = pStmt;
pArg->cnt = 0;
}
/* echo the sql statement if echo on */
if( pArg && pArg->echoOn ){
const char *zStmtSql = sqlite3_sql(pStmt);
fprintf(pArg->out, "%s\n", zStmtSql ? zStmtSql : zSql);
}
/* perform the first step. this will tell us if we
** have a result set or not and how wide it is.
*/
rc = sqlite3_step(pStmt);
/* if we have a result set... */
if( SQLITE_ROW == rc ){
/* if we have a callback... */
if( xCallback ){
/* allocate space for col name ptr, value ptr, and type */
int nCol = sqlite3_column_count(pStmt);
void *pData = sqlite3_malloc(3*nCol*sizeof(const char*) + 1);
if( !pData ){
rc = SQLITE_NOMEM;
}else{
char **azCols = (char **)pData; /* Names of result columns */
char **azVals = &azCols[nCol]; /* Results */
int *aiTypes = (int *)&azVals[nCol]; /* Result types */
int i;
assert(sizeof(int) <= sizeof(char *));
/* save off ptrs to column names */
for(i=0; i<nCol; i++){
azCols[i] = (char *)sqlite3_column_name(pStmt, i);
}
do{
/* extract the data and data types */
for(i=0; i<nCol; i++){
azVals[i] = (char *)sqlite3_column_text(pStmt, i);
aiTypes[i] = sqlite3_column_type(pStmt, i);
if( !azVals[i] && (aiTypes[i]!=SQLITE_NULL) ){
rc = SQLITE_NOMEM;
break; /* from for */
}
} /* end for */
/* if data and types extracted successfully... */
if( SQLITE_ROW == rc ){
/* call the supplied callback with the result row data */
if( xCallback(pArg, nCol, azVals, azCols, aiTypes) ){
rc = SQLITE_ABORT;
}else{
rc = sqlite3_step(pStmt);
}
}
} while( SQLITE_ROW == rc );
sqlite3_free(pData);
}
}else{
do{
rc = sqlite3_step(pStmt);
} while( rc == SQLITE_ROW );
}
}
/* print usage stats if stats on */
if( pArg && pArg->statsOn ){
display_stats(db, pArg, 0);
}
/* Finalize the statement just executed. If this fails, save a
** copy of the error message. Otherwise, set zSql to point to the
** next statement to execute. */
rc = sqlite3_finalize(pStmt);
if( rc==SQLITE_OK ){
zSql = zLeftover;
while( isspace(zSql[0]) ) zSql++;
}else if( pzErrMsg ){
*pzErrMsg = save_err_msg(db);
}
/* clear saved stmt handle */
if( pArg ){
pArg->pStmt = NULL;
}
}
} /* end while */
return rc;
}
/*
** This is a different callback routine used for dumping the database.
** Each row received by this callback consists of a table name,
** the table type ("index" or "table") and SQL to create the table.
** This routine should print text sufficient to recreate the table.
*/
static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
int rc;
const char *zTable;
const char *zType;
const char *zSql;
const char *zPrepStmt = 0;
struct callback_data *p = (struct callback_data *)pArg;
UNUSED_PARAMETER(azCol);
if( nArg!=3 ) return 1;
zTable = azArg[0];
zType = azArg[1];
zSql = azArg[2];
if( strcmp(zTable, "sqlite_sequence")==0 ){
zPrepStmt = "DELETE FROM sqlite_sequence;\n";
}else if( strcmp(zTable, "sqlite_stat1")==0 ){
fprintf(p->out, "ANALYZE sqlite_master;\n");
}else if( strncmp(zTable, "sqlite_", 7)==0 ){
return 0;
}else if( strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){
char *zIns;
if( !p->writableSchema ){
fprintf(p->out, "PRAGMA writable_schema=ON;\n");
p->writableSchema = 1;
}
zIns = sqlite3_mprintf(
"INSERT INTO sqlite_master(type,name,tbl_name,rootpage,sql)"
"VALUES('table','%q','%q',0,'%q');",
zTable, zTable, zSql);
fprintf(p->out, "%s\n", zIns);
sqlite3_free(zIns);
return 0;
}else{
fprintf(p->out, "%s;\n", zSql);
}
if( strcmp(zType, "table")==0 ){
sqlite3_stmt *pTableInfo = 0;
char *zSelect = 0;
char *zTableInfo = 0;
char *zTmp = 0;
int nRow = 0;
zTableInfo = appendText(zTableInfo, "PRAGMA table_info(", 0);
zTableInfo = appendText(zTableInfo, zTable, '"');
zTableInfo = appendText(zTableInfo, ");", 0);
rc = sqlite3_prepare(p->db, zTableInfo, -1, &pTableInfo, 0);
free(zTableInfo);
if( rc!=SQLITE_OK || !pTableInfo ){
return 1;
}
zSelect = appendText(zSelect, "SELECT 'INSERT INTO ' || ", 0);
zTmp = appendText(zTmp, zTable, '"');
if( zTmp ){
zSelect = appendText(zSelect, zTmp, '\'');
}
zSelect = appendText(zSelect, " || ' VALUES(' || ", 0);
rc = sqlite3_step(pTableInfo);
while( rc==SQLITE_ROW ){
const char *zText = (const char *)sqlite3_column_text(pTableInfo, 1);
zSelect = appendText(zSelect, "quote(", 0);
zSelect = appendText(zSelect, zText, '"');
rc = sqlite3_step(pTableInfo);
if( rc==SQLITE_ROW ){
zSelect = appendText(zSelect, ") || ',' || ", 0);
}else{
zSelect = appendText(zSelect, ") ", 0);
}
nRow++;
}
rc = sqlite3_finalize(pTableInfo);
if( rc!=SQLITE_OK || nRow==0 ){
free(zSelect);
return 1;
}
zSelect = appendText(zSelect, "|| ')' FROM ", 0);
zSelect = appendText(zSelect, zTable, '"');
rc = run_table_dump_query(p->out, p->db, zSelect, zPrepStmt);
if( rc==SQLITE_CORRUPT ){
zSelect = appendText(zSelect, " ORDER BY rowid DESC", 0);
rc = run_table_dump_query(p->out, p->db, zSelect, 0);
}
if( zSelect ) free(zSelect);
}
return 0;
}
/*
** Run zQuery. Use dump_callback() as the callback routine so that
** the contents of the query are output as SQL statements.
**
** If we get a SQLITE_CORRUPT error, rerun the query after appending
** "ORDER BY rowid DESC" to the end.
*/
static int run_schema_dump_query(
struct callback_data *p,
const char *zQuery,
char **pzErrMsg
){
int rc;
rc = sqlite3_exec(p->db, zQuery, dump_callback, p, pzErrMsg);
if( rc==SQLITE_CORRUPT ){
char *zQ2;
int len = strlen30(zQuery);
if( pzErrMsg ) sqlite3_free(*pzErrMsg);
zQ2 = malloc( len+100 );
if( zQ2==0 ) return rc;
sqlite3_snprintf(sizeof(zQ2), zQ2, "%s ORDER BY rowid DESC", zQuery);
rc = sqlite3_exec(p->db, zQ2, dump_callback, p, pzErrMsg);
free(zQ2);
}
return rc;
}
/*
** Text of a help message
*/
static char zHelp[] =
".backup ?DB? FILE Backup DB (default \"main\") to FILE\n"
".bail ON|OFF Stop after hitting an error. Default OFF\n"
".databases List names and files of attached databases\n"
".dump ?TABLE? ... Dump the database in an SQL text format\n"
" If TABLE specified, only dump tables matching\n"
" LIKE pattern TABLE.\n"
".echo ON|OFF Turn command echo on or off\n"
".exit Exit this program\n"
".explain ?ON|OFF? Turn output mode suitable for EXPLAIN on or off.\n"
" With no args, it turns EXPLAIN on.\n"
".header(s) ON|OFF Turn display of headers on or off\n"
".help Show this message\n"
".import FILE TABLE Import data from FILE into TABLE\n"
".indices ?TABLE? Show names of all indices\n"
" If TABLE specified, only show indices for tables\n"
" matching LIKE pattern TABLE.\n"
#ifdef SQLITE_ENABLE_IOTRACE
".iotrace FILE Enable I/O diagnostic logging to FILE\n"
#endif
#ifndef SQLITE_OMIT_LOAD_EXTENSION
".load FILE ?ENTRY? Load an extension library\n"
#endif
".log FILE|off Turn logging on or off. FILE can be stderr/stdout\n"
".mode MODE ?TABLE? Set output mode where MODE is one of:\n"
" csv Comma-separated values\n"
" column Left-aligned columns. (See .width)\n"
" html HTML <table> code\n"
" insert SQL insert statements for TABLE\n"
" line One value per line\n"
" list Values delimited by .separator string\n"
" tabs Tab-separated values\n"
" tcl TCL list elements\n"
".nullvalue STRING Print STRING in place of NULL values\n"
".output FILENAME Send output to FILENAME\n"
".output stdout Send output to the screen\n"
".prompt MAIN CONTINUE Replace the standard prompts\n"
".quit Exit this program\n"
".read FILENAME Execute SQL in FILENAME\n"
".restore ?DB? FILE Restore content of DB (default \"main\") from FILE\n"
".schema ?TABLE? Show the CREATE statements\n"
" If TABLE specified, only show tables matching\n"
" LIKE pattern TABLE.\n"
".separator STRING Change separator used by output mode and .import\n"
".show Show the current values for various settings\n"
".stats ON|OFF Turn stats on or off\n"
".tables ?TABLE? List names of tables\n"
" If TABLE specified, only list tables matching\n"
" LIKE pattern TABLE.\n"
".timeout MS Try opening locked tables for MS milliseconds\n"
".width NUM1 NUM2 ... Set column widths for \"column\" mode\n"
;
static char zTimerHelp[] =
".timer ON|OFF Turn the CPU timer measurement on or off\n"
;
/* Forward reference */
static int process_input(struct callback_data *p, FILE *in);
/*
** Make sure the database is open. If it is not, then open it. If
** the database fails to open, print an error message and exit.
*/
static void open_db(struct callback_data *p){
if( p->db==0 ){
sqlite3_open(p->zDbFilename, &p->db);
db = p->db;
if( db && sqlite3_errcode(db)==SQLITE_OK ){
sqlite3_create_function(db, "shellstatic", 0, SQLITE_UTF8, 0,
shellstaticFunc, 0, 0);
}
if( db==0 || SQLITE_OK!=sqlite3_errcode(db) ){
fprintf(stderr,"Error: unable to open database \"%s\": %s\n",
p->zDbFilename, sqlite3_errmsg(db));
exit(1);
}
#ifndef SQLITE_OMIT_LOAD_EXTENSION
sqlite3_enable_load_extension(p->db, 1);
#endif
}
}
/*
** Do C-language style dequoting.
**
** \t -> tab
** \n -> newline
** \r -> carriage return
** \NNN -> ascii character NNN in octal
** \\ -> backslash
*/
static void resolve_backslashes(char *z){
int i, j;
char c;
for(i=j=0; (c = z[i])!=0; i++, j++){
if( c=='\\' ){
c = z[++i];
if( c=='n' ){
c = '\n';
}else if( c=='t' ){
c = '\t';
}else if( c=='r' ){
c = '\r';
}else if( c>='0' && c<='7' ){
c -= '0';
if( z[i+1]>='0' && z[i+1]<='7' ){
i++;
c = (c<<3) + z[i] - '0';
if( z[i+1]>='0' && z[i+1]<='7' ){
i++;
c = (c<<3) + z[i] - '0';
}
}
}
}
z[j] = c;
}
z[j] = 0;
}
/*
** Interpret zArg as a boolean value. Return either 0 or 1.
*/
static int booleanValue(char *zArg){
int val = atoi(zArg);
int j;
for(j=0; zArg[j]; j++){
zArg[j] = (char)tolower(zArg[j]);
}
if( strcmp(zArg,"on")==0 ){
val = 1;
}else if( strcmp(zArg,"yes")==0 ){
val = 1;
}
return val;
}
/*
** If an input line begins with "." then invoke this routine to
** process that line.
**
** Return 1 on error, 2 to exit, and 0 otherwise.
*/
static int do_meta_command(char *zLine, struct callback_data *p){
int i = 1;
int nArg = 0;
int n, c;
int rc = 0;
char *azArg[50];
/* Parse the input line into tokens.
*/
while( zLine[i] && nArg<ArraySize(azArg) ){
while( isspace((unsigned char)zLine[i]) ){ i++; }
if( zLine[i]==0 ) break;
if( zLine[i]=='\'' || zLine[i]=='"' ){
int delim = zLine[i++];
azArg[nArg++] = &zLine[i];
while( zLine[i] && zLine[i]!=delim ){ i++; }
if( zLine[i]==delim ){
zLine[i++] = 0;
}
if( delim=='"' ) resolve_backslashes(azArg[nArg-1]);
}else{
azArg[nArg++] = &zLine[i];
while( zLine[i] && !isspace((unsigned char)zLine[i]) ){ i++; }
if( zLine[i] ) zLine[i++] = 0;
resolve_backslashes(azArg[nArg-1]);
}
}
/* Process the input line.
*/
if( nArg==0 ) return 0; /* no tokens, no error */
n = strlen30(azArg[0]);
c = azArg[0][0];
if( c=='b' && n>=3 && strncmp(azArg[0], "backup", n)==0 && nArg>1 && nArg<4){
const char *zDestFile;
const char *zDb;
sqlite3 *pDest;
sqlite3_backup *pBackup;
if( nArg==2 ){
zDestFile = azArg[1];
zDb = "main";
}else{
zDestFile = azArg[2];
zDb = azArg[1];
}
rc = sqlite3_open(zDestFile, &pDest);
if( rc!=SQLITE_OK ){
fprintf(stderr, "Error: cannot open \"%s\"\n", zDestFile);
sqlite3_close(pDest);
return 1;
}
open_db(p);
pBackup = sqlite3_backup_init(pDest, "main", p->db, zDb);
if( pBackup==0 ){
fprintf(stderr, "Error: %s\n", sqlite3_errmsg(pDest));
sqlite3_close(pDest);
return 1;
}
while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK ){}
sqlite3_backup_finish(pBackup);
if( rc==SQLITE_DONE ){
rc = 0;
}else{
fprintf(stderr, "Error: %s\n", sqlite3_errmsg(pDest));
rc = 1;
}
sqlite3_close(pDest);
}else
if( c=='b' && n>=3 && strncmp(azArg[0], "bail", n)==0 && nArg>1 && nArg<3 ){
bail_on_error = booleanValue(azArg[1]);
}else
if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 && nArg==1 ){
struct callback_data data;
char *zErrMsg = 0;
open_db(p);
memcpy(&data, p, sizeof(data));
data.showHeader = 1;
data.mode = MODE_Column;
data.colWidth[0] = 3;
data.colWidth[1] = 15;
data.colWidth[2] = 58;
data.cnt = 0;
sqlite3_exec(p->db, "PRAGMA database_list; ", callback, &data, &zErrMsg);
if( zErrMsg ){
fprintf(stderr,"Error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
rc = 1;
}
}else
if( c=='d' && strncmp(azArg[0], "dump", n)==0 && nArg<3 ){
char *zErrMsg = 0;
open_db(p);
/* When playing back a "dump", the content might appear in an order
** which causes immediate foreign key constraints to be violated.
** So disable foreign-key constraint enforcement to prevent problems. */
fprintf(p->out, "PRAGMA foreign_keys=OFF;\n");
fprintf(p->out, "BEGIN TRANSACTION;\n");
p->writableSchema = 0;
sqlite3_exec(p->db, "PRAGMA writable_schema=ON", 0, 0, 0);
if( nArg==1 ){
run_schema_dump_query(p,
"SELECT name, type, sql FROM sqlite_master "
"WHERE sql NOT NULL AND type=='table' AND name!='sqlite_sequence'", 0
);
run_schema_dump_query(p,
"SELECT name, type, sql FROM sqlite_master "
"WHERE name=='sqlite_sequence'", 0
);
run_table_dump_query(p->out, p->db,
"SELECT sql FROM sqlite_master "
"WHERE sql NOT NULL AND type IN ('index','trigger','view')", 0
);
}else{
int i;
for(i=1; i<nArg; i++){
zShellStatic = azArg[i];
run_schema_dump_query(p,
"SELECT name, type, sql FROM sqlite_master "
"WHERE tbl_name LIKE shellstatic() AND type=='table'"
" AND sql NOT NULL", 0);
run_table_dump_query(p->out, p->db,
"SELECT sql FROM sqlite_master "
"WHERE sql NOT NULL"
" AND type IN ('index','trigger','view')"
" AND tbl_name LIKE shellstatic()", 0
);
zShellStatic = 0;
}
}
if( p->writableSchema ){
fprintf(p->out, "PRAGMA writable_schema=OFF;\n");
p->writableSchema = 0;
}
sqlite3_exec(p->db, "PRAGMA writable_schema=OFF", 0, 0, 0);
if( zErrMsg ){
fprintf(stderr,"Error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}else{
fprintf(p->out, "COMMIT;\n");
}
}else
if( c=='e' && strncmp(azArg[0], "echo", n)==0 && nArg>1 && nArg<3 ){
p->echoOn = booleanValue(azArg[1]);
}else
if( c=='e' && strncmp(azArg[0], "exit", n)==0 && nArg==1 ){
rc = 2;
}else
if( c=='e' && strncmp(azArg[0], "explain", n)==0 && nArg<3 ){
int val = nArg>=2 ? booleanValue(azArg[1]) : 1;
if(val == 1) {
if(!p->explainPrev.valid) {
p->explainPrev.valid = 1;
p->explainPrev.mode = p->mode;
p->explainPrev.showHeader = p->showHeader;
memcpy(p->explainPrev.colWidth,p->colWidth,sizeof(p->colWidth));
}
/* We could put this code under the !p->explainValid
** condition so that it does not execute if we are already in
** explain mode. However, always executing it allows us an easy
** was to reset to explain mode in case the user previously
** did an .explain followed by a .width, .mode or .header
** command.
*/
p->mode = MODE_Explain;
p->showHeader = 1;
memset(p->colWidth,0,ArraySize(p->colWidth));
p->colWidth[0] = 4; /* addr */
p->colWidth[1] = 13; /* opcode */
p->colWidth[2] = 4; /* P1 */
p->colWidth[3] = 4; /* P2 */
p->colWidth[4] = 4; /* P3 */
p->colWidth[5] = 13; /* P4 */
p->colWidth[6] = 2; /* P5 */
p->colWidth[7] = 13; /* Comment */
}else if (p->explainPrev.valid) {
p->explainPrev.valid = 0;
p->mode = p->explainPrev.mode;
p->showHeader = p->explainPrev.showHeader;
memcpy(p->colWidth,p->explainPrev.colWidth,sizeof(p->colWidth));
}
}else
if( c=='h' && (strncmp(azArg[0], "header", n)==0 ||
strncmp(azArg[0], "headers", n)==0) && nArg>1 && nArg<3 ){
p->showHeader = booleanValue(azArg[1]);
}else
if( c=='h' && strncmp(azArg[0], "help", n)==0 ){
fprintf(stderr,"%s",zHelp);
if( HAS_TIMER ){
fprintf(stderr,"%s",zTimerHelp);
}
}else
if( c=='i' && strncmp(azArg[0], "import", n)==0 && nArg==3 ){
char *zTable = azArg[2]; /* Insert data into this table */
char *zFile = azArg[1]; /* The file from which to extract data */
sqlite3_stmt *pStmt = NULL; /* A statement */
int nCol; /* Number of columns in the table */
int nByte; /* Number of bytes in an SQL string */
int i, j; /* Loop counters */
int nSep; /* Number of bytes in p->separator[] */
char *zSql; /* An SQL statement */
char *zLine; /* A single line of input from the file */
char **azCol; /* zLine[] broken up into columns */
char *zCommit; /* How to commit changes */
FILE *in; /* The input file */
int lineno = 0; /* Line number of input file */
open_db(p);
nSep = strlen30(p->separator);
if( nSep==0 ){
fprintf(stderr, "Error: non-null separator required for import\n");
return 1;
}
zSql = sqlite3_mprintf("SELECT * FROM '%q'", zTable);
if( zSql==0 ){
fprintf(stderr, "Error: out of memory\n");
return 1;
}
nByte = strlen30(zSql);
rc = sqlite3_prepare(p->db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
if( rc ){
if (pStmt) sqlite3_finalize(pStmt);
fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db));
return 1;
}
nCol = sqlite3_column_count(pStmt);
sqlite3_finalize(pStmt);
pStmt = 0;
if( nCol==0 ) return 0; /* no columns, no error */
zSql = malloc( nByte + 20 + nCol*2 );
if( zSql==0 ){
fprintf(stderr, "Error: out of memory\n");
return 1;
}
sqlite3_snprintf(nByte+20, zSql, "INSERT INTO '%q' VALUES(?", zTable);
j = strlen30(zSql);
for(i=1; i<nCol; i++){
zSql[j++] = ',';
zSql[j++] = '?';
}
zSql[j++] = ')';
zSql[j] = 0;
rc = sqlite3_prepare(p->db, zSql, -1, &pStmt, 0);
free(zSql);
if( rc ){
fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db));
if (pStmt) sqlite3_finalize(pStmt);
return 1;
}
in = fopen(zFile, "rb");
if( in==0 ){
fprintf(stderr, "Error: cannot open \"%s\"\n", zFile);
sqlite3_finalize(pStmt);
return 1;
}
azCol = malloc( sizeof(azCol[0])*(nCol+1) );
if( azCol==0 ){
fprintf(stderr, "Error: out of memory\n");
fclose(in);
sqlite3_finalize(pStmt);
return 1;
}
sqlite3_exec(p->db, "BEGIN", 0, 0, 0);
zCommit = "COMMIT";
while( (zLine = local_getline(0, in))!=0 ){
char *z;
i = 0;
lineno++;
azCol[0] = zLine;
for(i=0, z=zLine; *z && *z!='\n' && *z!='\r'; z++){
if( *z==p->separator[0] && strncmp(z, p->separator, nSep)==0 ){
*z = 0;
i++;
if( i<nCol ){
azCol[i] = &z[nSep];
z += nSep-1;
}
}
} /* end for */
*z = 0;
if( i+1!=nCol ){
fprintf(stderr,
"Error: %s line %d: expected %d columns of data but found %d\n",
zFile, lineno, nCol, i+1);
zCommit = "ROLLBACK";
free(zLine);
rc = 1;
break; /* from while */
}
for(i=0; i<nCol; i++){
sqlite3_bind_text(pStmt, i+1, azCol[i], -1, SQLITE_STATIC);
}
sqlite3_step(pStmt);
rc = sqlite3_reset(pStmt);
free(zLine);
if( rc!=SQLITE_OK ){
fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db));
zCommit = "ROLLBACK";
rc = 1;
break; /* from while */
}
} /* end while */
free(azCol);
fclose(in);
sqlite3_finalize(pStmt);
sqlite3_exec(p->db, zCommit, 0, 0, 0);
}else
if( c=='i' && strncmp(azArg[0], "indices", n)==0 && nArg<3 ){
struct callback_data data;
char *zErrMsg = 0;
open_db(p);
memcpy(&data, p, sizeof(data));
data.showHeader = 0;
data.mode = MODE_List;
if( nArg==1 ){
rc = sqlite3_exec(p->db,
"SELECT name FROM sqlite_master "
"WHERE type='index' AND name NOT LIKE 'sqlite_%' "
"UNION ALL "
"SELECT name FROM sqlite_temp_master "
"WHERE type='index' "
"ORDER BY 1",
callback, &data, &zErrMsg
);
}else{
zShellStatic = azArg[1];
rc = sqlite3_exec(p->db,
"SELECT name FROM sqlite_master "
"WHERE type='index' AND tbl_name LIKE shellstatic() "
"UNION ALL "
"SELECT name FROM sqlite_temp_master "
"WHERE type='index' AND tbl_name LIKE shellstatic() "
"ORDER BY 1",
callback, &data, &zErrMsg
);
zShellStatic = 0;
}
if( zErrMsg ){
fprintf(stderr,"Error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
rc = 1;
}else if( rc != SQLITE_OK ){
fprintf(stderr,"Error: querying sqlite_master and sqlite_temp_master\n");
rc = 1;
}
}else
#ifdef SQLITE_ENABLE_IOTRACE
if( c=='i' && strncmp(azArg[0], "iotrace", n)==0 ){
extern void (*sqlite3IoTrace)(const char*, ...);
if( iotrace && iotrace!=stdout ) fclose(iotrace);
iotrace = 0;
if( nArg<2 ){
sqlite3IoTrace = 0;
}else if( strcmp(azArg[1], "-")==0 ){
sqlite3IoTrace = iotracePrintf;
iotrace = stdout;
}else{
iotrace = fopen(azArg[1], "w");
if( iotrace==0 ){
fprintf(stderr, "Error: cannot open \"%s\"\n", azArg[1]);
sqlite3IoTrace = 0;
rc = 1;
}else{
sqlite3IoTrace = iotracePrintf;
}
}
}else
#endif
#ifndef SQLITE_OMIT_LOAD_EXTENSION
if( c=='l' && strncmp(azArg[0], "load", n)==0 && nArg>=2 ){
const char *zFile, *zProc;
char *zErrMsg = 0;
zFile = azArg[1];
zProc = nArg>=3 ? azArg[2] : 0;
open_db(p);
rc = sqlite3_load_extension(p->db, zFile, zProc, &zErrMsg);
if( rc!=SQLITE_OK ){
fprintf(stderr, "Error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
rc = 1;
}
}else
#endif
if( c=='l' && strncmp(azArg[0], "log", n)==0 && nArg>=1 ){
const char *zFile = azArg[1];
if( p->pLog && p->pLog!=stdout && p->pLog!=stderr ){
fclose(p->pLog);
p->pLog = 0;
}
if( strcmp(zFile,"stdout")==0 ){
p->pLog = stdout;
}else if( strcmp(zFile, "stderr")==0 ){
p->pLog = stderr;
}else if( strcmp(zFile, "off")==0 ){
p->pLog = 0;
}else{
p->pLog = fopen(zFile, "w");
if( p->pLog==0 ){
fprintf(stderr, "Error: cannot open \"%s\"\n", zFile);
}
}
}else
if( c=='m' && strncmp(azArg[0], "mode", n)==0 && nArg==2 ){
int n2 = strlen30(azArg[1]);
if( (n2==4 && strncmp(azArg[1],"line",n2)==0)
||
(n2==5 && strncmp(azArg[1],"lines",n2)==0) ){
p->mode = MODE_Line;
}else if( (n2==6 && strncmp(azArg[1],"column",n2)==0)
||
(n2==7 && strncmp(azArg[1],"columns",n2)==0) ){
p->mode = MODE_Column;
}else if( n2==4 && strncmp(azArg[1],"list",n2)==0 ){
p->mode = MODE_List;
}else if( n2==4 && strncmp(azArg[1],"html",n2)==0 ){
p->mode = MODE_Html;
}else if( n2==3 && strncmp(azArg[1],"tcl",n2)==0 ){
p->mode = MODE_Tcl;
}else if( n2==3 && strncmp(azArg[1],"csv",n2)==0 ){
p->mode = MODE_Csv;
sqlite3_snprintf(sizeof(p->separator), p->separator, ",");
}else if( n2==4 && strncmp(azArg[1],"tabs",n2)==0 ){
p->mode = MODE_List;
sqlite3_snprintf(sizeof(p->separator), p->separator, "\t");
}else if( n2==6 && strncmp(azArg[1],"insert",n2)==0 ){
p->mode = MODE_Insert;
set_table_name(p, "table");
}else {
fprintf(stderr,"Error: mode should be one of: "
"column csv html insert line list tabs tcl\n");
rc = 1;
}
}else
if( c=='m' && strncmp(azArg[0], "mode", n)==0 && nArg==3 ){
int n2 = strlen30(azArg[1]);
if( n2==6 && strncmp(azArg[1],"insert",n2)==0 ){
p->mode = MODE_Insert;
set_table_name(p, azArg[2]);
}else {
fprintf(stderr, "Error: invalid arguments: "
" \"%s\". Enter \".help\" for help\n", azArg[2]);
rc = 1;
}
}else
if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 && nArg==2 ) {
sqlite3_snprintf(sizeof(p->nullvalue), p->nullvalue,
"%.*s", (int)ArraySize(p->nullvalue)-1, azArg[1]);
}else
if( c=='o' && strncmp(azArg[0], "output", n)==0 && nArg==2 ){
if( p->out!=stdout ){
fclose(p->out);
}
if( strcmp(azArg[1],"stdout")==0 ){
p->out = stdout;
sqlite3_snprintf(sizeof(p->outfile), p->outfile, "stdout");
}else{
p->out = fopen(azArg[1], "wb");
if( p->out==0 ){
fprintf(stderr,"Error: cannot write to \"%s\"\n", azArg[1]);
p->out = stdout;
rc = 1;
} else {
sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", azArg[1]);
}
}
}else
if( c=='p' && strncmp(azArg[0], "prompt", n)==0 && (nArg==2 || nArg==3)){
if( nArg >= 2) {
strncpy(mainPrompt,azArg[1],(int)ArraySize(mainPrompt)-1);
}
if( nArg >= 3) {
strncpy(continuePrompt,azArg[2],(int)ArraySize(continuePrompt)-1);
}
}else
if( c=='q' && strncmp(azArg[0], "quit", n)==0 && nArg==1 ){
rc = 2;
}else
if( c=='r' && n>=3 && strncmp(azArg[0], "read", n)==0 && nArg==2 ){
FILE *alt = fopen(azArg[1], "rb");
if( alt==0 ){
fprintf(stderr,"Error: cannot open \"%s\"\n", azArg[1]);
rc = 1;
}else{
rc = process_input(p, alt);
fclose(alt);
}
}else
if( c=='r' && n>=3 && strncmp(azArg[0], "restore", n)==0 && nArg>1 && nArg<4){
const char *zSrcFile;
const char *zDb;
sqlite3 *pSrc;
sqlite3_backup *pBackup;
int nTimeout = 0;
if( nArg==2 ){
zSrcFile = azArg[1];
zDb = "main";
}else{
zSrcFile = azArg[2];
zDb = azArg[1];
}
rc = sqlite3_open(zSrcFile, &pSrc);
if( rc!=SQLITE_OK ){
fprintf(stderr, "Error: cannot open \"%s\"\n", zSrcFile);
sqlite3_close(pSrc);
return 1;
}
open_db(p);
pBackup = sqlite3_backup_init(p->db, zDb, pSrc, "main");
if( pBackup==0 ){
fprintf(stderr, "Error: %s\n", sqlite3_errmsg(p->db));
sqlite3_close(pSrc);
return 1;
}
while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK
|| rc==SQLITE_BUSY ){
if( rc==SQLITE_BUSY ){
if( nTimeout++ >= 3 ) break;
sqlite3_sleep(100);
}
}
sqlite3_backup_finish(pBackup);
if( rc==SQLITE_DONE ){
rc = 0;
}else if( rc==SQLITE_BUSY || rc==SQLITE_LOCKED ){
fprintf(stderr, "Error: source database is busy\n");
rc = 1;
}else{
fprintf(stderr, "Error: %s\n", sqlite3_errmsg(p->db));
rc = 1;
}
sqlite3_close(pSrc);
}else
if( c=='s' && strncmp(azArg[0], "schema", n)==0 && nArg<3 ){
struct callback_data data;
char *zErrMsg = 0;
open_db(p);
memcpy(&data, p, sizeof(data));
data.showHeader = 0;
data.mode = MODE_Semi;
if( nArg>1 ){
int i;
for(i=0; azArg[1][i]; i++) azArg[1][i] = (char)tolower(azArg[1][i]);
if( strcmp(azArg[1],"sqlite_master")==0 ){
char *new_argv[2], *new_colv[2];
new_argv[0] = "CREATE TABLE sqlite_master (\n"
" type text,\n"
" name text,\n"
" tbl_name text,\n"
" rootpage integer,\n"
" sql text\n"
")";
new_argv[1] = 0;
new_colv[0] = "sql";
new_colv[1] = 0;
callback(&data, 1, new_argv, new_colv);
rc = SQLITE_OK;
}else if( strcmp(azArg[1],"sqlite_temp_master")==0 ){
char *new_argv[2], *new_colv[2];
new_argv[0] = "CREATE TEMP TABLE sqlite_temp_master (\n"
" type text,\n"
" name text,\n"
" tbl_name text,\n"
" rootpage integer,\n"
" sql text\n"
")";
new_argv[1] = 0;
new_colv[0] = "sql";
new_colv[1] = 0;
callback(&data, 1, new_argv, new_colv);
rc = SQLITE_OK;
}else{
zShellStatic = azArg[1];
rc = sqlite3_exec(p->db,
"SELECT sql FROM "
" (SELECT sql sql, type type, tbl_name tbl_name, name name"
" FROM sqlite_master UNION ALL"
" SELECT sql, type, tbl_name, name FROM sqlite_temp_master) "
"WHERE tbl_name LIKE shellstatic() AND type!='meta' AND sql NOTNULL "
"ORDER BY substr(type,2,1), name",
callback, &data, &zErrMsg);
zShellStatic = 0;
}
}else{
rc = sqlite3_exec(p->db,
"SELECT sql FROM "
" (SELECT sql sql, type type, tbl_name tbl_name, name name"
" FROM sqlite_master UNION ALL"
" SELECT sql, type, tbl_name, name FROM sqlite_temp_master) "
"WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%'"
"ORDER BY substr(type,2,1), name",
callback, &data, &zErrMsg
);
}
if( zErrMsg ){
fprintf(stderr,"Error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
rc = 1;
}else if( rc != SQLITE_OK ){
fprintf(stderr,"Error: querying schema information\n");
rc = 1;
}else{
rc = 0;
}
}else
if( c=='s' && strncmp(azArg[0], "separator", n)==0 && nArg==2 ){
sqlite3_snprintf(sizeof(p->separator), p->separator,
"%.*s", (int)sizeof(p->separator)-1, azArg[1]);
}else
if( c=='s' && strncmp(azArg[0], "show", n)==0 && nArg==1 ){
int i;
fprintf(p->out,"%9.9s: %s\n","echo", p->echoOn ? "on" : "off");
fprintf(p->out,"%9.9s: %s\n","explain", p->explainPrev.valid ? "on" :"off");
fprintf(p->out,"%9.9s: %s\n","headers", p->showHeader ? "on" : "off");
fprintf(p->out,"%9.9s: %s\n","mode", modeDescr[p->mode]);
fprintf(p->out,"%9.9s: ", "nullvalue");
output_c_string(p->out, p->nullvalue);
fprintf(p->out, "\n");
fprintf(p->out,"%9.9s: %s\n","output",
strlen30(p->outfile) ? p->outfile : "stdout");
fprintf(p->out,"%9.9s: ", "separator");
output_c_string(p->out, p->separator);
fprintf(p->out, "\n");
fprintf(p->out,"%9.9s: %s\n","stats", p->statsOn ? "on" : "off");
fprintf(p->out,"%9.9s: ","width");
for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) {
fprintf(p->out,"%d ",p->colWidth[i]);
}
fprintf(p->out,"\n");
}else
if( c=='s' && strncmp(azArg[0], "stats", n)==0 && nArg>1 && nArg<3 ){
p->statsOn = booleanValue(azArg[1]);
}else
if( c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0 && nArg<3 ){
char **azResult;
int nRow;
char *zErrMsg;
open_db(p);
if( nArg==1 ){
rc = sqlite3_get_table(p->db,
"SELECT name FROM sqlite_master "
"WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%' "
"UNION ALL "
"SELECT name FROM sqlite_temp_master "
"WHERE type IN ('table','view') "
"ORDER BY 1",
&azResult, &nRow, 0, &zErrMsg
);
}else{
zShellStatic = azArg[1];
rc = sqlite3_get_table(p->db,
"SELECT name FROM sqlite_master "
"WHERE type IN ('table','view') AND name LIKE shellstatic() "
"UNION ALL "
"SELECT name FROM sqlite_temp_master "
"WHERE type IN ('table','view') AND name LIKE shellstatic() "
"ORDER BY 1",
&azResult, &nRow, 0, &zErrMsg
);
zShellStatic = 0;
}
if( zErrMsg ){
fprintf(stderr,"Error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
rc = 1;
}else if( rc != SQLITE_OK ){
fprintf(stderr,"Error: querying sqlite_master and sqlite_temp_master\n");
rc = 1;
}else{
int len, maxlen = 0;
int i, j;
int nPrintCol, nPrintRow;
for(i=1; i<=nRow; i++){
if( azResult[i]==0 ) continue;
len = strlen30(azResult[i]);
if( len>maxlen ) maxlen = len;
}
nPrintCol = 80/(maxlen+2);
if( nPrintCol<1 ) nPrintCol = 1;
nPrintRow = (nRow + nPrintCol - 1)/nPrintCol;
for(i=0; i<nPrintRow; i++){
for(j=i+1; j<=nRow; j+=nPrintRow){
char *zSp = j<=nPrintRow ? "" : " ";
printf("%s%-*s", zSp, maxlen, azResult[j] ? azResult[j] : "");
}
printf("\n");
}
}
sqlite3_free_table(azResult);
}else
if( c=='t' && n>4 && strncmp(azArg[0], "timeout", n)==0 && nArg==2 ){
open_db(p);
sqlite3_busy_timeout(p->db, atoi(azArg[1]));
}else
if( HAS_TIMER && c=='t' && n>=5 && strncmp(azArg[0], "timer", n)==0 && nArg==2 ){
enableTimer = booleanValue(azArg[1]);
}else
if( c=='w' && strncmp(azArg[0], "width", n)==0 && nArg>1 ){
int j;
assert( nArg<=ArraySize(azArg) );
for(j=1; j<nArg && j<ArraySize(p->colWidth); j++){
p->colWidth[j-1] = atoi(azArg[j]);
}
}else
{
fprintf(stderr, "Error: unknown command or invalid arguments: "
" \"%s\". Enter \".help\" for help\n", azArg[0]);
rc = 1;
}
return rc;
}
/*
** Return TRUE if a semicolon occurs anywhere in the first N characters
** of string z[].
*/
static int _contains_semicolon(const char *z, int N){
int i;
for(i=0; i<N; i++){ if( z[i]==';' ) return 1; }
return 0;
}
/*
** Test to see if a line consists entirely of whitespace.
*/
static int _all_whitespace(const char *z){
for(; *z; z++){
if( isspace(*(unsigned char*)z) ) continue;
if( *z=='/' && z[1]=='*' ){
z += 2;
while( *z && (*z!='*' || z[1]!='/') ){ z++; }
if( *z==0 ) return 0;
z++;
continue;
}
if( *z=='-' && z[1]=='-' ){
z += 2;
while( *z && *z!='\n' ){ z++; }
if( *z==0 ) return 1;
continue;
}
return 0;
}
return 1;
}
/*
** Return TRUE if the line typed in is an SQL command terminator other
** than a semi-colon. The SQL Server style "go" command is understood
** as is the Oracle "/".
*/
static int _is_command_terminator(const char *zLine){
while( isspace(*(unsigned char*)zLine) ){ zLine++; };
if( zLine[0]=='/' && _all_whitespace(&zLine[1]) ){
return 1; /* Oracle */
}
if( tolower(zLine[0])=='g' && tolower(zLine[1])=='o'
&& _all_whitespace(&zLine[2]) ){
return 1; /* SQL Server */
}
return 0;
}
/*
** Return true if zSql is a complete SQL statement. Return false if it
** ends in the middle of a string literal or C-style comment.
*/
static int _is_complete(char *zSql, int nSql){
int rc;
if( zSql==0 ) return 1;
zSql[nSql] = ';';
zSql[nSql+1] = 0;
rc = sqlite3_complete(zSql);
zSql[nSql] = 0;
return rc;
}
/*
** Read input from *in and process it. If *in==0 then input
** is interactive - the user is typing it it. Otherwise, input
** is coming from a file or device. A prompt is issued and history
** is saved only if input is interactive. An interrupt signal will
** cause this routine to exit immediately, unless input is interactive.
**
** Return the number of errors.
*/
static int process_input(struct callback_data *p, FILE *in){
char *zLine = 0;
char *zSql = 0;
int nSql = 0;
int nSqlPrior = 0;
char *zErrMsg;
int rc;
int errCnt = 0;
int lineno = 0;
int startline = 0;
while( errCnt==0 || !bail_on_error || (in==0 && stdin_is_interactive) ){
fflush(p->out);
free(zLine);
zLine = one_input_line(zSql, in);
if( zLine==0 ){
break; /* We have reached EOF */
}
if( seenInterrupt ){
if( in!=0 ) break;
seenInterrupt = 0;
}
lineno++;
if( (zSql==0 || zSql[0]==0) && _all_whitespace(zLine) ) continue;
if( zLine && zLine[0]=='.' && nSql==0 ){
if( p->echoOn ) printf("%s\n", zLine);
rc = do_meta_command(zLine, p);
if( rc==2 ){ /* exit requested */
break;
}else if( rc ){
errCnt++;
}
continue;
}
if( _is_command_terminator(zLine) && _is_complete(zSql, nSql) ){
memcpy(zLine,";",2);
}
nSqlPrior = nSql;
if( zSql==0 ){
int i;
for(i=0; zLine[i] && isspace((unsigned char)zLine[i]); i++){}
if( zLine[i]!=0 ){
nSql = strlen30(zLine);
zSql = malloc( nSql+3 );
if( zSql==0 ){
fprintf(stderr, "Error: out of memory\n");
exit(1);
}
memcpy(zSql, zLine, nSql+1);
startline = lineno;
}
}else{
int len = strlen30(zLine);
zSql = realloc( zSql, nSql + len + 4 );
if( zSql==0 ){
fprintf(stderr,"Error: out of memory\n");
exit(1);
}
zSql[nSql++] = '\n';
memcpy(&zSql[nSql], zLine, len+1);
nSql += len;
}
if( zSql && _contains_semicolon(&zSql[nSqlPrior], nSql-nSqlPrior)
&& sqlite3_complete(zSql) ){
p->cnt = 0;
open_db(p);
BEGIN_TIMER;
rc = shell_exec(p->db, zSql, shell_callback, p, &zErrMsg);
END_TIMER;
if( rc || zErrMsg ){
char zPrefix[100];
if( in!=0 || !stdin_is_interactive ){
sqlite3_snprintf(sizeof(zPrefix), zPrefix,
"Error: near line %d:", startline);
}else{
sqlite3_snprintf(sizeof(zPrefix), zPrefix, "Error:");
}
if( zErrMsg!=0 ){
fprintf(stderr, "%s %s\n", zPrefix, zErrMsg);
sqlite3_free(zErrMsg);
zErrMsg = 0;
}else{
fprintf(stderr, "%s %s\n", zPrefix, sqlite3_errmsg(p->db));
}
errCnt++;
}
free(zSql);
zSql = 0;
nSql = 0;
}
}
if( zSql ){
if( !_all_whitespace(zSql) ) fprintf(stderr, "Error: incomplete SQL: %s\n", zSql);
free(zSql);
}
free(zLine);
return errCnt;
}
/*
** Return a pathname which is the user's home directory. A
** 0 return indicates an error of some kind. Space to hold the
** resulting string is obtained from malloc(). The calling
** function should free the result.
*/
static char *find_home_dir(void){
char *home_dir = NULL;
#if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) && !defined(_WIN32_WCE) && !defined(__RTP__) && !defined(_WRS_KERNEL)
struct passwd *pwent;
uid_t uid = getuid();
if( (pwent=getpwuid(uid)) != NULL) {
home_dir = pwent->pw_dir;
}
#endif
#if defined(_WIN32_WCE)
/* Windows CE (arm-wince-mingw32ce-gcc) does not provide getenv()
*/
home_dir = strdup("/");
#else
#if defined(_WIN32) || defined(WIN32) || defined(__OS2__)
if (!home_dir) {
home_dir = getenv("USERPROFILE");
}
#endif
if (!home_dir) {
home_dir = getenv("HOME");
}
#if defined(_WIN32) || defined(WIN32) || defined(__OS2__)
if (!home_dir) {
char *zDrive, *zPath;
int n;
zDrive = getenv("HOMEDRIVE");
zPath = getenv("HOMEPATH");
if( zDrive && zPath ){
n = strlen30(zDrive) + strlen30(zPath) + 1;
home_dir = malloc( n );
if( home_dir==0 ) return 0;
sqlite3_snprintf(n, home_dir, "%s%s", zDrive, zPath);
return home_dir;
}
home_dir = "c:\\";
}
#endif
#endif /* !_WIN32_WCE */
if( home_dir ){
int n = strlen30(home_dir) + 1;
char *z = malloc( n );
if( z ) memcpy(z, home_dir, n);
home_dir = z;
}
return home_dir;
}
/*
** Read input from the file given by sqliterc_override. Or if that
** parameter is NULL, take input from ~/.sqliterc
**
** Returns the number of errors.
*/
static int process_sqliterc(
struct callback_data *p, /* Configuration data */
const char *sqliterc_override /* Name of config file. NULL to use default */
){
char *home_dir = NULL;
const char *sqliterc = sqliterc_override;
char *zBuf = 0;
FILE *in = NULL;
int nBuf;
int rc = 0;
if (sqliterc == NULL) {
home_dir = find_home_dir();
if( home_dir==0 ){
#if !defined(__RTP__) && !defined(_WRS_KERNEL)
fprintf(stderr,"%s: Error: cannot locate your home directory\n", Argv0);
#endif
return 1;
}
nBuf = strlen30(home_dir) + 16;
zBuf = malloc( nBuf );
if( zBuf==0 ){
fprintf(stderr,"%s: Error: out of memory\n",Argv0);
return 1;
}
sqlite3_snprintf(nBuf, zBuf,"%s/.sqliterc",home_dir);
free(home_dir);
sqliterc = (const char*)zBuf;
}
in = fopen(sqliterc,"rb");
if( in ){
if( stdin_is_interactive ){
fprintf(stderr,"-- Loading resources from %s\n",sqliterc);
}
rc = process_input(p,in);
fclose(in);
}
free(zBuf);
return rc;
}
/*
** Show available command line options
*/
static const char zOptions[] =
" -help show this message\n"
" -init filename read/process named file\n"
" -echo print commands before execution\n"
" -[no]header turn headers on or off\n"
" -bail stop after hitting an error\n"
" -interactive force interactive I/O\n"
" -batch force batch I/O\n"
" -column set output mode to 'column'\n"
" -csv set output mode to 'csv'\n"
" -html set output mode to HTML\n"
" -line set output mode to 'line'\n"
" -list set output mode to 'list'\n"
" -separator 'x' set output field separator (|)\n"
" -stats print memory stats before each finalize\n"
" -nullvalue 'text' set text string for NULL values\n"
" -version show SQLite version\n"
;
static void usage(int showDetail){
fprintf(stderr,
"Usage: %s [OPTIONS] FILENAME [SQL]\n"
"FILENAME is the name of an SQLite database. A new database is created\n"
"if the file does not previously exist.\n", Argv0);
if( showDetail ){
fprintf(stderr, "OPTIONS include:\n%s", zOptions);
}else{
fprintf(stderr, "Use the -help option for additional information\n");
}
exit(1);
}
/*
** Initialize the state information in data
*/
static void main_init(struct callback_data *data) {
memset(data, 0, sizeof(*data));
data->mode = MODE_List;
memcpy(data->separator,"|", 2);
data->showHeader = 0;
sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data);
sqlite3_snprintf(sizeof(mainPrompt), mainPrompt,"sqlite> ");
sqlite3_snprintf(sizeof(continuePrompt), continuePrompt," ...> ");
sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);
}
int main(int argc, char **argv){
char *zErrMsg = 0;
struct callback_data data;
const char *zInitFile = 0;
char *zFirstCmd = 0;
int i;
int rc = 0;
Argv0 = argv[0];
main_init(&data);
stdin_is_interactive = isatty(0);
/* Make sure we have a valid signal handler early, before anything
** else is done.
*/
#ifdef SIGINT
signal(SIGINT, interrupt_handler);
#endif
/* Do an initial pass through the command-line argument to locate
** the name of the database file, the name of the initialization file,
** and the first command to execute.
*/
for(i=1; i<argc-1; i++){
char *z;
if( argv[i][0]!='-' ) break;
z = argv[i];
if( z[0]=='-' && z[1]=='-' ) z++;
if( strcmp(argv[i],"-separator")==0 || strcmp(argv[i],"-nullvalue")==0 ){
i++;
}else if( strcmp(argv[i],"-init")==0 ){
i++;
zInitFile = argv[i];
/* Need to check for batch mode here to so we can avoid printing
** informational messages (like from process_sqliterc) before
** we do the actual processing of arguments later in a second pass.
*/
}else if( strcmp(argv[i],"-batch")==0 ){
stdin_is_interactive = 0;
}
}
if( i<argc ){
#if defined(SQLITE_OS_OS2) && SQLITE_OS_OS2
data.zDbFilename = (const char *)convertCpPathToUtf8( argv[i++] );
#else
data.zDbFilename = argv[i++];
#endif
}else{
#ifndef SQLITE_OMIT_MEMORYDB
data.zDbFilename = ":memory:";
#else
data.zDbFilename = 0;
#endif
}
if( i<argc ){
zFirstCmd = argv[i++];
}
if( i<argc ){
fprintf(stderr,"%s: Error: too many options: \"%s\"\n", Argv0, argv[i]);
fprintf(stderr,"Use -help for a list of options.\n");
return 1;
}
data.out = stdout;
#ifdef SQLITE_OMIT_MEMORYDB
if( data.zDbFilename==0 ){
fprintf(stderr,"%s: Error: no database filename specified\n", Argv0);
return 1;
}
#endif
/* Go ahead and open the database file if it already exists. If the
** file does not exist, delay opening it. This prevents empty database
** files from being created if a user mistypes the database name argument
** to the sqlite command-line tool.
*/
if( access(data.zDbFilename, 0)==0 ){
open_db(&data);
}
/* Process the initialization file if there is one. If no -init option
** is given on the command line, look for a file named ~/.sqliterc and
** try to process it.
*/
rc = process_sqliterc(&data,zInitFile);
if( rc>0 ){
return rc;
}
/* Make a second pass through the command-line argument and set
** options. This second pass is delayed until after the initialization
** file is processed so that the command-line arguments will override
** settings in the initialization file.
*/
for(i=1; i<argc && argv[i][0]=='-'; i++){
char *z = argv[i];
if( z[1]=='-' ){ z++; }
if( strcmp(z,"-init")==0 ){
i++;
}else if( strcmp(z,"-html")==0 ){
data.mode = MODE_Html;
}else if( strcmp(z,"-list")==0 ){
data.mode = MODE_List;
}else if( strcmp(z,"-line")==0 ){
data.mode = MODE_Line;
}else if( strcmp(z,"-column")==0 ){
data.mode = MODE_Column;
}else if( strcmp(z,"-csv")==0 ){
data.mode = MODE_Csv;
memcpy(data.separator,",",2);
}else if( strcmp(z,"-separator")==0 ){
i++;
if(i>=argc){
fprintf(stderr,"%s: Error: missing argument for option: %s\n", Argv0, z);
fprintf(stderr,"Use -help for a list of options.\n");
return 1;
}
sqlite3_snprintf(sizeof(data.separator), data.separator,
"%.*s",(int)sizeof(data.separator)-1,argv[i]);
}else if( strcmp(z,"-nullvalue")==0 ){
i++;
if(i>=argc){
fprintf(stderr,"%s: Error: missing argument for option: %s\n", Argv0, z);
fprintf(stderr,"Use -help for a list of options.\n");
return 1;
}
sqlite3_snprintf(sizeof(data.nullvalue), data.nullvalue,
"%.*s",(int)sizeof(data.nullvalue)-1,argv[i]);
}else if( strcmp(z,"-header")==0 ){
data.showHeader = 1;
}else if( strcmp(z,"-noheader")==0 ){
data.showHeader = 0;
}else if( strcmp(z,"-echo")==0 ){
data.echoOn = 1;
}else if( strcmp(z,"-stats")==0 ){
data.statsOn = 1;
}else if( strcmp(z,"-bail")==0 ){
bail_on_error = 1;
}else if( strcmp(z,"-version")==0 ){
printf("%s\n", sqlite3_libversion());
return 0;
}else if( strcmp(z,"-interactive")==0 ){
stdin_is_interactive = 1;
}else if( strcmp(z,"-batch")==0 ){
stdin_is_interactive = 0;
}else if( strcmp(z,"-help")==0 || strcmp(z, "--help")==0 ){
usage(1);
}else{
fprintf(stderr,"%s: Error: unknown option: %s\n", Argv0, z);
fprintf(stderr,"Use -help for a list of options.\n");
return 1;
}
}
if( zFirstCmd ){
/* Run just the command that follows the database name
*/
if( zFirstCmd[0]=='.' ){
rc = do_meta_command(zFirstCmd, &data);
}else{
open_db(&data);
rc = shell_exec(data.db, zFirstCmd, shell_callback, &data, &zErrMsg);
if( zErrMsg!=0 ){
fprintf(stderr,"Error: %s\n", zErrMsg);
return rc!=0 ? rc : 1;
}else if( rc!=0 ){
fprintf(stderr,"Error: unable to process SQL \"%s\"\n", zFirstCmd);
return rc;
}
}
}else{
/* Run commands received from standard input
*/
if( stdin_is_interactive ){
char *zHome;
char *zHistory = 0;
int nHistory;
printf(
"SQLite version %s\n"
"Enter \".help\" for instructions\n"
"Enter SQL statements terminated with a \";\"\n",
sqlite3_libversion()
);
zHome = find_home_dir();
if( zHome ){
nHistory = strlen30(zHome) + 20;
if( (zHistory = malloc(nHistory))!=0 ){
sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome);
}
}
#if defined(HAVE_READLINE) && HAVE_READLINE==1
if( zHistory ) read_history(zHistory);
#endif
rc = process_input(&data, 0);
if( zHistory ){
stifle_history(100);
write_history(zHistory);
free(zHistory);
}
free(zHome);
}else{
rc = process_input(&data, stdin);
}
}
set_table_name(&data, 0);
if( data.db ){
if( sqlite3_close(data.db)!=SQLITE_OK ){
fprintf(stderr,"Error: cannot close database \"%s\"\n",
sqlite3_errmsg(db));
rc++;
}
}
return rc;
}
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
** 2006 June 7
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the SQLite interface for use by
** shared libraries that want to be imported as extensions into
** an SQLite instance. Shared libraries that intend to be loaded
** as extensions by SQLite should #include this file instead of
** sqlite3.h.
*/
#ifndef _SQLITE3EXT_H_
#define _SQLITE3EXT_H_
#include "sqlite3.h"
typedef struct sqlite3_api_routines sqlite3_api_routines;
/*
** The following structure holds pointers to all of the SQLite API
** routines.
**
** WARNING: In order to maintain backwards compatibility, add new
** interfaces to the end of this structure only. If you insert new
** interfaces in the middle of this structure, then older different
** versions of SQLite will not be able to load each others' shared
** libraries!
*/
struct sqlite3_api_routines {
void * (*aggregate_context)(sqlite3_context*,int nBytes);
int (*aggregate_count)(sqlite3_context*);
int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*));
int (*bind_double)(sqlite3_stmt*,int,double);
int (*bind_int)(sqlite3_stmt*,int,int);
int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64);
int (*bind_null)(sqlite3_stmt*,int);
int (*bind_parameter_count)(sqlite3_stmt*);
int (*bind_parameter_index)(sqlite3_stmt*,const char*zName);
const char * (*bind_parameter_name)(sqlite3_stmt*,int);
int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*));
int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*));
int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*);
int (*busy_handler)(sqlite3*,int(*)(void*,int),void*);
int (*busy_timeout)(sqlite3*,int ms);
int (*changes)(sqlite3*);
int (*close)(sqlite3*);
int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,int eTextRep,const char*));
int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,int eTextRep,const void*));
const void * (*column_blob)(sqlite3_stmt*,int iCol);
int (*column_bytes)(sqlite3_stmt*,int iCol);
int (*column_bytes16)(sqlite3_stmt*,int iCol);
int (*column_count)(sqlite3_stmt*pStmt);
const char * (*column_database_name)(sqlite3_stmt*,int);
const void * (*column_database_name16)(sqlite3_stmt*,int);
const char * (*column_decltype)(sqlite3_stmt*,int i);
const void * (*column_decltype16)(sqlite3_stmt*,int);
double (*column_double)(sqlite3_stmt*,int iCol);
int (*column_int)(sqlite3_stmt*,int iCol);
sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol);
const char * (*column_name)(sqlite3_stmt*,int);
const void * (*column_name16)(sqlite3_stmt*,int);
const char * (*column_origin_name)(sqlite3_stmt*,int);
const void * (*column_origin_name16)(sqlite3_stmt*,int);
const char * (*column_table_name)(sqlite3_stmt*,int);
const void * (*column_table_name16)(sqlite3_stmt*,int);
const unsigned char * (*column_text)(sqlite3_stmt*,int iCol);
const void * (*column_text16)(sqlite3_stmt*,int iCol);
int (*column_type)(sqlite3_stmt*,int iCol);
sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol);
void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
int (*complete)(const char*sql);
int (*complete16)(const void*sql);
int (*create_collation)(sqlite3*,const char*,int,void*,int(*)(void*,int,const void*,int,const void*));
int (*create_collation16)(sqlite3*,const void*,int,void*,int(*)(void*,int,const void*,int,const void*));
int (*create_function)(sqlite3*,const char*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*));
int (*create_function16)(sqlite3*,const void*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*));
int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
int (*data_count)(sqlite3_stmt*pStmt);
sqlite3 * (*db_handle)(sqlite3_stmt*);
int (*declare_vtab)(sqlite3*,const char*);
int (*enable_shared_cache)(int);
int (*errcode)(sqlite3*db);
const char * (*errmsg)(sqlite3*);
const void * (*errmsg16)(sqlite3*);
int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**);
int (*expired)(sqlite3_stmt*);
int (*finalize)(sqlite3_stmt*pStmt);
void (*free)(void*);
void (*free_table)(char**result);
int (*get_autocommit)(sqlite3*);
void * (*get_auxdata)(sqlite3_context*,int);
int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**);
int (*global_recover)(void);
void (*interruptx)(sqlite3*);
sqlite_int64 (*last_insert_rowid)(sqlite3*);
const char * (*libversion)(void);
int (*libversion_number)(void);
void *(*malloc)(int);
char * (*mprintf)(const char*,...);
int (*open)(const char*,sqlite3**);
int (*open16)(const void*,sqlite3**);
int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*);
void (*progress_handler)(sqlite3*,int,int(*)(void*),void*);
void *(*realloc)(void*,int);
int (*reset)(sqlite3_stmt*pStmt);
void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_double)(sqlite3_context*,double);
void (*result_error)(sqlite3_context*,const char*,int);
void (*result_error16)(sqlite3_context*,const void*,int);
void (*result_int)(sqlite3_context*,int);
void (*result_int64)(sqlite3_context*,sqlite_int64);
void (*result_null)(sqlite3_context*);
void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*));
void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_value)(sqlite3_context*,sqlite3_value*);
void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,const char*,const char*),void*);
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
char * (*snprintf)(int,char*,const char*,...);
int (*step)(sqlite3_stmt*);
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,char const**,char const**,int*,int*,int*);
void (*thread_cleanup)(void);
int (*total_changes)(sqlite3*);
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,sqlite_int64),void*);
void * (*user_data)(sqlite3_context*);
const void * (*value_blob)(sqlite3_value*);
int (*value_bytes)(sqlite3_value*);
int (*value_bytes16)(sqlite3_value*);
double (*value_double)(sqlite3_value*);
int (*value_int)(sqlite3_value*);
sqlite_int64 (*value_int64)(sqlite3_value*);
int (*value_numeric_type)(sqlite3_value*);
const unsigned char * (*value_text)(sqlite3_value*);
const void * (*value_text16)(sqlite3_value*);
const void * (*value_text16be)(sqlite3_value*);
const void * (*value_text16le)(sqlite3_value*);
int (*value_type)(sqlite3_value*);
char *(*vmprintf)(const char*,va_list);
/* Added ??? */
int (*overload_function)(sqlite3*, const char *zFuncName, int nArg);
/* Added by 3.3.13 */
int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
int (*clear_bindings)(sqlite3_stmt*);
/* Added by 3.4.1 */
int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,void (*xDestroy)(void *));
/* Added by 3.5.0 */
int (*bind_zeroblob)(sqlite3_stmt*,int,int);
int (*blob_bytes)(sqlite3_blob*);
int (*blob_close)(sqlite3_blob*);
int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,int,sqlite3_blob**);
int (*blob_read)(sqlite3_blob*,void*,int,int);
int (*blob_write)(sqlite3_blob*,const void*,int,int);
int (*create_collation_v2)(sqlite3*,const char*,int,void*,int(*)(void*,int,const void*,int,const void*),void(*)(void*));
int (*file_control)(sqlite3*,const char*,int,void*);
sqlite3_int64 (*memory_highwater)(int);
sqlite3_int64 (*memory_used)(void);
sqlite3_mutex *(*mutex_alloc)(int);
void (*mutex_enter)(sqlite3_mutex*);
void (*mutex_free)(sqlite3_mutex*);
void (*mutex_leave)(sqlite3_mutex*);
int (*mutex_try)(sqlite3_mutex*);
int (*open_v2)(const char*,sqlite3**,int,const char*);
int (*release_memory)(int);
void (*result_error_nomem)(sqlite3_context*);
void (*result_error_toobig)(sqlite3_context*);
int (*sleep)(int);
void (*soft_heap_limit)(int);
sqlite3_vfs *(*vfs_find)(const char*);
int (*vfs_register)(sqlite3_vfs*,int);
int (*vfs_unregister)(sqlite3_vfs*);
int (*xthreadsafe)(void);
void (*result_zeroblob)(sqlite3_context*,int);
void (*result_error_code)(sqlite3_context*,int);
int (*test_control)(int, ...);
void (*randomness)(int,void*);
sqlite3 *(*context_db_handle)(sqlite3_context*);
int (*extended_result_codes)(sqlite3*,int);
int (*limit)(sqlite3*,int,int);
sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*);
const char *(*sql)(sqlite3_stmt*);
int (*status)(int,int*,int*,int);
int (*backup_finish)(sqlite3_backup*);
sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*);
int (*backup_pagecount)(sqlite3_backup*);
int (*backup_remaining)(sqlite3_backup*);
int (*backup_step)(sqlite3_backup*,int);
const char *(*compileoption_get)(int);
int (*compileoption_used)(const char*);
int (*create_function_v2)(sqlite3*,const char*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*),void(*xDestroy)(void*));
int (*db_config)(sqlite3*,int,...);
sqlite3_mutex *(*db_mutex)(sqlite3*);
int (*db_status)(sqlite3*,int,int*,int*,int);
int (*extended_errcode)(sqlite3*);
void (*log)(int,const char*,...);
sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64);
const char *(*sourceid)(void);
int (*stmt_status)(sqlite3_stmt*,int,int);
int (*strnicmp)(const char*,const char*,int);
int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*);
int (*wal_autocheckpoint)(sqlite3*,int);
int (*wal_checkpoint)(sqlite3*,const char*);
void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*);
};
/*
** The following macros redefine the API routines so that they are
** redirected throught the global sqlite3_api structure.
**
** This header file is also used by the loadext.c source file
** (part of the main SQLite library - not an extension) so that
** it can get access to the sqlite3_api_routines structure
** definition. But the main library does not want to redefine
** the API. So the redefinition macros are only valid if the
** SQLITE_CORE macros is undefined.
*/
#ifndef SQLITE_CORE
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
#endif
#define sqlite3_bind_blob sqlite3_api->bind_blob
#define sqlite3_bind_double sqlite3_api->bind_double
#define sqlite3_bind_int sqlite3_api->bind_int
#define sqlite3_bind_int64 sqlite3_api->bind_int64
#define sqlite3_bind_null sqlite3_api->bind_null
#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count
#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index
#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name
#define sqlite3_bind_text sqlite3_api->bind_text
#define sqlite3_bind_text16 sqlite3_api->bind_text16
#define sqlite3_bind_value sqlite3_api->bind_value
#define sqlite3_busy_handler sqlite3_api->busy_handler
#define sqlite3_busy_timeout sqlite3_api->busy_timeout
#define sqlite3_changes sqlite3_api->changes
#define sqlite3_close sqlite3_api->close
#define sqlite3_collation_needed sqlite3_api->collation_needed
#define sqlite3_collation_needed16 sqlite3_api->collation_needed16
#define sqlite3_column_blob sqlite3_api->column_blob
#define sqlite3_column_bytes sqlite3_api->column_bytes
#define sqlite3_column_bytes16 sqlite3_api->column_bytes16
#define sqlite3_column_count sqlite3_api->column_count
#define sqlite3_column_database_name sqlite3_api->column_database_name
#define sqlite3_column_database_name16 sqlite3_api->column_database_name16
#define sqlite3_column_decltype sqlite3_api->column_decltype
#define sqlite3_column_decltype16 sqlite3_api->column_decltype16
#define sqlite3_column_double sqlite3_api->column_double
#define sqlite3_column_int sqlite3_api->column_int
#define sqlite3_column_int64 sqlite3_api->column_int64
#define sqlite3_column_name sqlite3_api->column_name
#define sqlite3_column_name16 sqlite3_api->column_name16
#define sqlite3_column_origin_name sqlite3_api->column_origin_name
#define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16
#define sqlite3_column_table_name sqlite3_api->column_table_name
#define sqlite3_column_table_name16 sqlite3_api->column_table_name16
#define sqlite3_column_text sqlite3_api->column_text
#define sqlite3_column_text16 sqlite3_api->column_text16
#define sqlite3_column_type sqlite3_api->column_type
#define sqlite3_column_value sqlite3_api->column_value
#define sqlite3_commit_hook sqlite3_api->commit_hook
#define sqlite3_complete sqlite3_api->complete
#define sqlite3_complete16 sqlite3_api->complete16
#define sqlite3_create_collation sqlite3_api->create_collation
#define sqlite3_create_collation16 sqlite3_api->create_collation16
#define sqlite3_create_function sqlite3_api->create_function
#define sqlite3_create_function16 sqlite3_api->create_function16
#define sqlite3_create_module sqlite3_api->create_module
#define sqlite3_create_module_v2 sqlite3_api->create_module_v2
#define sqlite3_data_count sqlite3_api->data_count
#define sqlite3_db_handle sqlite3_api->db_handle
#define sqlite3_declare_vtab sqlite3_api->declare_vtab
#define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache
#define sqlite3_errcode sqlite3_api->errcode
#define sqlite3_errmsg sqlite3_api->errmsg
#define sqlite3_errmsg16 sqlite3_api->errmsg16
#define sqlite3_exec sqlite3_api->exec
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_expired sqlite3_api->expired
#endif
#define sqlite3_finalize sqlite3_api->finalize
#define sqlite3_free sqlite3_api->free
#define sqlite3_free_table sqlite3_api->free_table
#define sqlite3_get_autocommit sqlite3_api->get_autocommit
#define sqlite3_get_auxdata sqlite3_api->get_auxdata
#define sqlite3_get_table sqlite3_api->get_table
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_global_recover sqlite3_api->global_recover
#endif
#define sqlite3_interrupt sqlite3_api->interruptx
#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid
#define sqlite3_libversion sqlite3_api->libversion
#define sqlite3_libversion_number sqlite3_api->libversion_number
#define sqlite3_malloc sqlite3_api->malloc
#define sqlite3_mprintf sqlite3_api->mprintf
#define sqlite3_open sqlite3_api->open
#define sqlite3_open16 sqlite3_api->open16
#define sqlite3_prepare sqlite3_api->prepare
#define sqlite3_prepare16 sqlite3_api->prepare16
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
#define sqlite3_profile sqlite3_api->profile
#define sqlite3_progress_handler sqlite3_api->progress_handler
#define sqlite3_realloc sqlite3_api->realloc
#define sqlite3_reset sqlite3_api->reset
#define sqlite3_result_blob sqlite3_api->result_blob
#define sqlite3_result_double sqlite3_api->result_double
#define sqlite3_result_error sqlite3_api->result_error
#define sqlite3_result_error16 sqlite3_api->result_error16
#define sqlite3_result_int sqlite3_api->result_int
#define sqlite3_result_int64 sqlite3_api->result_int64
#define sqlite3_result_null sqlite3_api->result_null
#define sqlite3_result_text sqlite3_api->result_text
#define sqlite3_result_text16 sqlite3_api->result_text16
#define sqlite3_result_text16be sqlite3_api->result_text16be
#define sqlite3_result_text16le sqlite3_api->result_text16le
#define sqlite3_result_value sqlite3_api->result_value
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
#define sqlite3_set_auxdata sqlite3_api->set_auxdata
#define sqlite3_snprintf sqlite3_api->snprintf
#define sqlite3_step sqlite3_api->step
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
#define sqlite3_total_changes sqlite3_api->total_changes
#define sqlite3_trace sqlite3_api->trace
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings
#endif
#define sqlite3_update_hook sqlite3_api->update_hook
#define sqlite3_user_data sqlite3_api->user_data
#define sqlite3_value_blob sqlite3_api->value_blob
#define sqlite3_value_bytes sqlite3_api->value_bytes
#define sqlite3_value_bytes16 sqlite3_api->value_bytes16
#define sqlite3_value_double sqlite3_api->value_double
#define sqlite3_value_int sqlite3_api->value_int
#define sqlite3_value_int64 sqlite3_api->value_int64
#define sqlite3_value_numeric_type sqlite3_api->value_numeric_type
#define sqlite3_value_text sqlite3_api->value_text
#define sqlite3_value_text16 sqlite3_api->value_text16
#define sqlite3_value_text16be sqlite3_api->value_text16be
#define sqlite3_value_text16le sqlite3_api->value_text16le
#define sqlite3_value_type sqlite3_api->value_type
#define sqlite3_vmprintf sqlite3_api->vmprintf
#define sqlite3_overload_function sqlite3_api->overload_function
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
#define sqlite3_clear_bindings sqlite3_api->clear_bindings
#define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob
#define sqlite3_blob_bytes sqlite3_api->blob_bytes
#define sqlite3_blob_close sqlite3_api->blob_close
#define sqlite3_blob_open sqlite3_api->blob_open
#define sqlite3_blob_read sqlite3_api->blob_read
#define sqlite3_blob_write sqlite3_api->blob_write
#define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2
#define sqlite3_file_control sqlite3_api->file_control
#define sqlite3_memory_highwater sqlite3_api->memory_highwater
#define sqlite3_memory_used sqlite3_api->memory_used
#define sqlite3_mutex_alloc sqlite3_api->mutex_alloc
#define sqlite3_mutex_enter sqlite3_api->mutex_enter
#define sqlite3_mutex_free sqlite3_api->mutex_free
#define sqlite3_mutex_leave sqlite3_api->mutex_leave
#define sqlite3_mutex_try sqlite3_api->mutex_try
#define sqlite3_open_v2 sqlite3_api->open_v2
#define sqlite3_release_memory sqlite3_api->release_memory
#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem
#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig
#define sqlite3_sleep sqlite3_api->sleep
#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit
#define sqlite3_vfs_find sqlite3_api->vfs_find
#define sqlite3_vfs_register sqlite3_api->vfs_register
#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister
#define sqlite3_threadsafe sqlite3_api->xthreadsafe
#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob
#define sqlite3_result_error_code sqlite3_api->result_error_code
#define sqlite3_test_control sqlite3_api->test_control
#define sqlite3_randomness sqlite3_api->randomness
#define sqlite3_context_db_handle sqlite3_api->context_db_handle
#define sqlite3_extended_result_codes sqlite3_api->extended_result_codes
#define sqlite3_limit sqlite3_api->limit
#define sqlite3_next_stmt sqlite3_api->next_stmt
#define sqlite3_sql sqlite3_api->sql
#define sqlite3_status sqlite3_api->status
#define sqlite3_backup_finish sqlite3_api->backup_finish
#define sqlite3_backup_init sqlite3_api->backup_init
#define sqlite3_backup_pagecount sqlite3_api->backup_pagecount
#define sqlite3_backup_remaining sqlite3_api->backup_remaining
#define sqlite3_backup_step sqlite3_api->backup_step
#define sqlite3_compileoption_get sqlite3_api->compileoption_get
#define sqlite3_compileoption_used sqlite3_api->compileoption_used
#define sqlite3_create_function_v2 sqlite3_api->create_function_v2
#define sqlite3_db_config sqlite3_api->db_config
#define sqlite3_db_mutex sqlite3_api->db_mutex
#define sqlite3_db_status sqlite3_api->db_status
#define sqlite3_extended_errcode sqlite3_api->extended_errcode
#define sqlite3_log sqlite3_api->log
#define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64
#define sqlite3_sourceid sqlite3_api->sourceid
#define sqlite3_stmt_status sqlite3_api->stmt_status
#define sqlite3_strnicmp sqlite3_api->strnicmp
#define sqlite3_unlock_notify sqlite3_api->unlock_notify
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
#define sqlite3_wal_hook sqlite3_api->wal_hook
#endif /* SQLITE_CORE */
#define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api = 0;
#define SQLITE_EXTENSION_INIT2(v) sqlite3_api = v;
#endif /* _SQLITE3EXT_H_ */
#ifndef NODE_SQLITE3_SRC_DEFERRED_CALL_H
#define NODE_SQLITE3_SRC_DEFERRED_CALL_H
#include <v8.h>
using namespace v8;
namespace Deferred {
enum Mode {
Concurrent,
Exclusive
};
template <typename T> class Call {
public:
Persistent<Function> callback_;
Persistent<Object> receiver_;
enum Mode mode_;
T data_;
int argc_;
Persistent<Value>* argv_;
public:
Call(const Arguments& args, const T data, Mode mode = Concurrent) {
callback_ = Persistent<Function>::New(args.Callee());
receiver_ = Persistent<Object>::New(args.This());
mode_ = mode;
data_ = data;
argc_ = args.Length();
argv_ = new Persistent<Value>[argc_];
for (int i = 0; i < argc_; i++) {
argv_[i] = Persistent<Value>::New(args[i]);
}
}
~Call() {
for (int i = 0; i < argc_; i++) {
argv_[i].Dispose();
}
delete[] argv_;
callback_.Dispose();
receiver_.Dispose();
}
inline Mode Mode() {
return mode_;
}
inline const T Data() {
return data_;
}
inline Local<Value> Invoke() {
return callback_->Call(receiver_, argc_, argv_);
}
};
}
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment