Commit a45195a1 authored by Bruce Momjian's avatar Bruce Momjian

Major psql overhaul by Peter Eisentraut.

parent 2ea3b6d6
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
# Makefile.inc-- # Makefile.in--
# Makefile for bin/psql # Makefile for bin/psql
# #
# Copyright (c) 1994, Regents of the University of California # Copyright (c) 1994, Regents of the University of California
# #
# #
# IDENTIFICATION # IDENTIFICATION
# $Header: /cvsroot/pgsql/src/bin/psql/Attic/Makefile.in,v 1.15 1999/01/17 06:19:19 momjian Exp $ # $Header: /cvsroot/pgsql/src/bin/psql/Attic/Makefile.in,v 1.16 1999/11/04 21:56:01 momjian Exp $
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
...@@ -28,7 +28,9 @@ ifdef MULTIBYTE ...@@ -28,7 +28,9 @@ ifdef MULTIBYTE
CFLAGS+= $(MBFLAGS) CFLAGS+= $(MBFLAGS)
endif endif
OBJS= psql.o stringutils.o @STRDUP@ @STRERROR2@ OBJS=command.o common.o help.o input.o stringutils.o mainloop.o \
copy.o startup.o prompt.o variables.o large_obj.o print.o describe.o \
@STRDUP@ @STRERROR2@
all: submake psql all: submake psql
...@@ -38,6 +40,18 @@ psql: $(OBJS) $(LIBPQDIR)/libpq.a ...@@ -38,6 +40,18 @@ psql: $(OBJS) $(LIBPQDIR)/libpq.a
../../utils/strdup.o: ../../utils/strdup.o:
$(MAKE) -C ../../utils strdup.o $(MAKE) -C ../../utils strdup.o
OBJS:
$(CC) $(CFLAGS) -c $< -o $@
help.o: sql_help.h
ifneq ($(strip $(PERL)),)
sql_help.h: ../../../doc/src/sgml/ref/*.sgml create_help.pl
$(PERL) create_help.pl sql_help.h
else
sql_help.h:
endif
.PHONY: submake .PHONY: submake
submake: submake:
$(MAKE) -C $(LIBPQDIR) libpq.a $(MAKE) -C $(LIBPQDIR) libpq.a
...@@ -46,14 +60,18 @@ install: psql ...@@ -46,14 +60,18 @@ install: psql
$(INSTALL) $(INSTL_EXE_OPTS) psql$(X) $(BINDIR)/psql$(X) $(INSTALL) $(INSTL_EXE_OPTS) psql$(X) $(BINDIR)/psql$(X)
depend dep: depend dep:
$(CC) -MM $(CFLAGS) *.c >depend $(CC) -MM -MG $(CFLAGS) *.c >depend
clean: clean:
rm -f psql$(X) $(OBJS) rm -f psql$(X) $(OBJS)
# Some people might get in trouble if they do a make clean and the
# sql_help.h is gone, for it needs the docs in the right place to be
# regenerated. -- (pe)
distclean: clean
rm -f sql_help.h
ifeq (depend,$(wildcard depend)) ifeq (depend,$(wildcard depend))
include depend include depend
endif endif
This diff is collapsed.
#ifndef COMMAND_H
#define COMMAND_H
#include <config.h>
#include <c.h>
#include <pqexpbuffer.h>
#include "settings.h"
#include "print.h"
typedef enum _backslashResult {
CMD_UNKNOWN = 0, /* not done parsing yet (internal only) */
CMD_SEND, /* query complete; send off */
CMD_SKIP_LINE, /* keep building query */
CMD_TERMINATE, /* quit program */
CMD_NEWEDIT, /* query buffer was changed (e.g., via \e) */
CMD_ERROR /* the execution of the backslash command resulted
in an error */
} backslashResult;
backslashResult
HandleSlashCmds(PsqlSettings *pset,
const char *line,
PQExpBuffer query_buf,
const char ** end_of_cmd);
bool
do_connect(const char *new_dbname,
const char *new_user,
PsqlSettings *pset);
bool
process_file(const char *filename,
PsqlSettings *pset);
bool
do_pset(const char * param,
const char * value,
printQueryOpt * popt,
bool quiet);
#endif
This diff is collapsed.
#ifndef COMMON_H
#define COMMON_H
#include <c.h>
#include "settings.h"
char *
xstrdup(const char * string);
bool
setQFout(const char *fname, PsqlSettings *pset);
char *
simple_prompt(const char *prompt, int maxlen, bool echo);
const char *
interpolate_var(const char * name, PsqlSettings * pset);
PGresult *
PSQLexec(PsqlSettings *pset, const char *query);
bool
SendQuery(PsqlSettings *pset, const char *query);
#endif /* COMMON_H */
#include <config.h>
#include <c.h>
#include "copy.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#ifndef WIN32
#include <unistd.h> /* for isatty */
#else
#include <io.h> /* I think */
#endif
#include <libpq-fe.h>
#include "settings.h"
#include "common.h"
#include "stringutils.h"
#ifdef WIN32
#define strcasecmp(x,y) stricmp(x,y)
#endif
/*
* parse_slash_copy
* -- parses \copy command line
*
* Accepted syntax: \copy [binary] table|"table" [with oids] from|to filename|'filename' using delimiters ['<char>']
* (binary is not here yet)
*
* returns a malloc'ed structure with the options, or NULL on parsing error
*/
struct copy_options {
char * table;
char * file;
bool from;
bool binary;
bool oids;
char * delim;
};
static void
free_copy_options(struct copy_options * ptr)
{
if (!ptr)
return;
free(ptr->table);
free(ptr->file);
free(ptr->delim);
free(ptr);
}
static struct copy_options *
parse_slash_copy(const char *args)
{
struct copy_options * result;
char * line;
char * token;
bool error = false;
char quote;
line = xstrdup(args);
if (!(result = calloc(1, sizeof (struct copy_options)))) {
perror("calloc");
exit(EXIT_FAILURE);
}
token = strtokx(line, " \t", "\"", '\\', &quote, NULL);
if (!token)
error = true;
else {
if (!quote && strcasecmp(token, "binary")==0) {
result->binary = true;
token = strtokx(NULL, " \t", "\"", '\\', &quote, NULL);
if (!token)
error = true;
}
if (token)
result->table = xstrdup(token);
}
#ifdef USE_ASSERT_CHECKING
assert(error || result->table);
#endif
if (!error) {
token = strtokx(NULL, " \t", NULL, '\\', NULL, NULL);
if (!token)
error = true;
else {
if (strcasecmp(token, "with")==0) {
token = strtokx(NULL, " \t", NULL, '\\', NULL, NULL);
if (!token || strcasecmp(token, "oids")!=0)
error = true;
else
result->oids = true;
if (!error) {
token = strtokx(NULL, " \t", NULL, '\\', NULL, NULL);
if (!token)
error = true;
}
}
if (!error && strcasecmp(token, "from")==0)
result->from = true;
else if (!error && strcasecmp(token, "to")==0)
result->from = false;
else
error = true;
}
}
if (!error) {
token = strtokx(NULL, " \t", "'", '\\', NULL, NULL);
if (!token)
error = true;
else
result->file=xstrdup(token);
}
#ifdef USE_ASSERT_CHECKING
assert(error || result->file);
#endif
if (!error) {
token = strtokx(NULL, " \t", NULL, '\\', NULL, NULL);
if (token) {
if (strcasecmp(token, "using")!=0)
error = true;
else {
token = strtokx(NULL, " \t", NULL, '\\', NULL, NULL);
if (!token || strcasecmp(token, "delimiters")!=0)
error = true;
else {
token = strtokx(NULL, " \t", "'", '\\', NULL, NULL);
if (token)
result->delim = xstrdup(token);
else
error = true;
}
}
}
}
free(line);
if (error) {
fputs("Parse error at ", stderr);
if (!token)
fputs("end of line.", stderr);
else
fprintf(stderr, "'%s'.", token);
fputs("\n", stderr);
free(result);
return NULL;
}
else
return result;
}
/*
* Execute a \copy command (frontend copy). We have to open a file, then
* submit a COPY query to the backend and either feed it data from the
* file or route its response into the file.
*/
bool
do_copy(const char * args, PsqlSettings *pset)
{
char query[128 + NAMEDATALEN];
FILE *copystream;
struct copy_options *options;
PGresult *result;
bool success;
/* parse options */
options = parse_slash_copy(args);
if (!options)
return false;
strcpy(query, "COPY ");
if (options->binary)
fputs("Warning: \\copy binary is not implemented. Resorting to text output.\n", stderr);
/* strcat(query, "BINARY "); */
strcat(query, "\"");
strncat(query, options->table, NAMEDATALEN);
strcat(query, "\" ");
if (options->oids)
strcat(query, "WITH OIDS ");
if (options->from)
strcat(query, "FROM stdin");
else
strcat(query, "TO stdout");
if (options->delim) {
/* backend copy only uses the first character here,
but that might be the escape backslash
(makes me wonder though why it's called delimiterS) */
strncat(query, " USING DELIMITERS '", 2);
strcat(query, options->delim);
strcat(query, "'");
}
if (options->from)
#ifndef __CYGWIN32__
copystream = fopen(options->file, "r");
#else
copystream = fopen(options->file, "rb");
#endif
else
#ifndef __CYGWIN32__
copystream = fopen(options->file, "w");
#else
copystream = fopen(options->file, "wb");
#endif
if (!copystream) {
fprintf(stderr,
"Unable to open file %s which to copy: %s\n",
options->from ? "from" : "to", strerror(errno));
free_copy_options(options);
return false;
}
result = PSQLexec(pset, query);
switch (PQresultStatus(result))
{
case PGRES_COPY_OUT:
success = handleCopyOut(pset->db, copystream);
break;
case PGRES_COPY_IN:
success = handleCopyIn(pset->db, copystream, NULL);
break;
case PGRES_NONFATAL_ERROR:
case PGRES_FATAL_ERROR:
case PGRES_BAD_RESPONSE:
success = false;
fputs(PQerrorMessage(pset->db), stderr);
break;
default:
success = false;
fprintf(stderr, "Unexpected response (%d)\n", PQresultStatus(result));
}
PQclear(result);
if (!GetVariable(pset->vars, "quiet")) {
if (success)
puts("Successfully copied.");
else
puts("Copy failed.");
}
fclose(copystream);
free_copy_options(options);
return success;
}
#define COPYBUFSIZ BLCKSZ
/*
* handeCopyOut
* receives data as a result of a COPY ... TO stdout command
*
* If you want to use COPY TO in your application, this is the code to steal :)
*
* conn should be a database connection that you just called COPY TO on
* (and which gave you PGRES_COPY_OUT back);
* copystream is the file stream you want the output to go to
*/
bool
handleCopyOut(PGconn *conn, FILE *copystream)
{
bool copydone = false; /* haven't started yet */
char copybuf[COPYBUFSIZ];
int ret;
while (!copydone)
{
ret = PQgetline(conn, copybuf, COPYBUFSIZ);
if (copybuf[0] == '\\' &&
copybuf[1] == '.' &&
copybuf[2] == '\0')
{
copydone = true; /* we're at the end */
}
else
{
fputs(copybuf, copystream);
switch (ret)
{
case EOF:
copydone = true;
/* FALLTHROUGH */
case 0:
fputc('\n', copystream);
break;
case 1:
break;
}
}
}
fflush(copystream);
return !PQendcopy(conn);
}
/*
* handeCopyOut
* receives data as a result of a COPY ... FROM stdin command
*
* Again, if you want to use COPY FROM in your application, copy this.
*
* conn should be a database connection that you just called COPY FROM on
* (and which gave you PGRES_COPY_IN back);
* copystream is the file stream you want the input to come from
* prompt is something to display to request user input (only makes sense
* if stdin is an interactive tty)
*/
bool
handleCopyIn(PGconn *conn, FILE *copystream, const char * prompt)
{
bool copydone = false;
bool firstload;
bool linedone;
char copybuf[COPYBUFSIZ];
char *s;
int buflen;
int c = 0;
while (!copydone)
{ /* for each input line ... */
if (prompt && isatty(fileno(stdin)))
{
fputs(prompt, stdout);
fflush(stdout);
}
firstload = true;
linedone = false;
while (!linedone)
{ /* for each buffer ... */
s = copybuf;
for (buflen = COPYBUFSIZ; buflen > 1; buflen--)
{
c = getc(copystream);
if (c == '\n' || c == EOF)
{
linedone = true;
break;
}
*s++ = c;
}
*s = '\0';
if (c == EOF)
{
PQputline(conn, "\\.");
copydone = true;
break;
}
PQputline(conn, copybuf);
if (firstload)
{
if (!strcmp(copybuf, "\\."))
copydone = true;
firstload = false;
}
}
PQputline(conn, "\n");
}
return !PQendcopy(conn);
}
#ifndef COPY_H
#define COPY_H
#include <c.h>
#include <stdio.h>
#include <libpq-fe.h>
#include "settings.h"
/* handler for \copy */
bool
do_copy(const char *args, PsqlSettings *pset);
/* lower level processors for copy in/out streams */
bool
handleCopyOut(PGconn *conn, FILE *copystream);
bool
handleCopyIn(PGconn *conn, FILE *copystream, const char * prompt);
#endif
#!/usr/bin/perl
#
# This script automatically generates the help on SQL in psql from the
# SGML docs. So far the format of the docs was consistent enough that
# this worked, but this here is my no means an SGML parser.
#
# It might be a good idea that this is just done once before distribution
# so people that don't have the docs or have slightly messed up docs or
# don't have perl, etc. won't have to bother.
#
# Call: perl create_help.pl sql_help.h
# (Do not rely on this script to be executable.)
# The name of the header file doesn't matter to this script, but it sure
# does matter to the rest of the source.
#
# A rule for this is also in the psql makefile.
#
$docdir = "./../../../doc/src/sgml/ref";
$outputfile = $ARGV[0] or die "Missing required argument.\n";
$define = $outputfile;
$define =~ tr/a-z/A-Z/;
$define =~ s/\W/_/g;
opendir DIR, $docdir or die "Couldn't open documentation sources: $!\n";
open OUT, ">$outputfile" or die "Couldn't open output file '$outputfile': $!\n";
print OUT
"/*
* This file is automatically generated from the SGML documentation.
* Direct changes here will be overwritten.
*/
#ifndef $define
#define $define
struct _helpStruct
{
char *cmd; /* the command name */
char *help; /* the help associated with it */
char *syntax; /* the syntax associated with it */
};
static struct _helpStruct QL_HELP[] = {
";
foreach $file (readdir DIR) {
my ($cmdname, $cmddesc, $cmdsynopsis);
$file =~ /\.sgml$/ || next;
open FILE, "$docdir/$file" or next;
$filecontent = join('', <FILE>);
close FILE;
$filecontent =~ m!<refmiscinfo>\s*SQL - Language Statements\s*</refmiscinfo>!i
or next;
$filecontent =~ m!<refname>\s*([a-z ]+?)\s*</refname>!i && ($cmdname = $1);
$filecontent =~ m!<refpurpose>\s*(.+?)\s*</refpurpose>!i && ($cmddesc = $1);
$filecontent =~ m!<synopsis>\s*(.+?)\s*</synopsis>!is && ($cmdsynopsis = $1);
if ($cmdname && $cmddesc && $cmdsynopsis) {
$cmdname =~ s/\"/\\"/g;
$cmddesc =~ s/<\/?.+?>//sg;
$cmddesc =~ s/\n/ /g;
$cmddesc =~ s/\"/\\"/g;
$cmdsynopsis =~ s/<\/?.+?>//sg;
$cmdsynopsis =~ s/\n/\\n/g;
$cmdsynopsis =~ s/\"/\\"/g;
print OUT " { \"$cmdname\",\n \"$cmddesc\",\n \"$cmdsynopsis\" },\n\n";
}
else {
print STDERR "Couldn't parse file '$file'. (N='$cmdname' D='$cmddesc')\n";
}
}
print OUT "
{ NULL, NULL, NULL } /* End of list marker */
};
#endif /* $define */
";
close OUT;
closedir DIR;
This diff is collapsed.
#ifndef DESCRIBE_H
#define DESCRIBE_H
#include "settings.h"
/* \da */
bool
describeAggregates(const char * name, PsqlSettings * pset);
/* \df */
bool
describeFunctions(const char * name, PsqlSettings * pset);
/* \dT */
bool
describeTypes(const char * name, PsqlSettings * pset);
/* \do */
bool
describeOperators(const char * name, PsqlSettings * pset);
/* \dp (formerly \z) */
bool
permissionsList(const char * name, PsqlSettings *pset);
/* \dd */
bool
objectDescription(const char * object, PsqlSettings *pset);
/* \d foo */
bool
describeTableDetails(const char * name, PsqlSettings * pset);
/* \l */
bool
listAllDbs(PsqlSettings *pset);
/* \dt, \di, \dS, etc. */
bool
listTables(const char * infotype, const char * name, PsqlSettings * pset);
#endif /* DESCRIBE_H */
This diff is collapsed.
#ifndef HELP_H
#define HELP_H
#include "settings.h"
void usage(void);
void slashUsage(PsqlSettings *pset);
void helpSQL(const char *topic);
void print_copyright(void);
#endif
#include <config.h>
#include <c.h>
#include "input.h"
#include <pqexpbuffer.h>
/* Note that this file does not depend on any other files in psql. */
/* Runtime options for turning off readline and history */
/* (of course there is no runtime command for doing that :) */
#ifdef USE_READLINE
static bool useReadline;
#endif
#ifdef USE_HISTORY
static bool useHistory;
#endif
/*
* gets_interactive()
*
* Gets a line of interactive input, using readline of desired.
* The result is malloced.
*/
char *
gets_interactive(const char *prompt)
{
char * s;
#ifdef USE_READLINE
if (useReadline) {
s = readline(prompt);
fputc('\r', stdout);
fflush(stdout);
}
else {
#endif
fputs(prompt, stdout);
fflush(stdout);
s = gets_fromFile(stdin);
#ifdef USE_READLINE
}
#endif
#ifdef USE_HISTORY
if (useHistory && s && s[0] != '\0')
add_history(s);
#endif
return s;
}
/*
* gets_fromFile
*
* Gets a line of noninteractive input from a file (which could be stdin).
*/
char *
gets_fromFile(FILE *source)
{
PQExpBufferData buffer;
char line[1024];
initPQExpBuffer(&buffer);
while (fgets(line, 1024, source) != NULL) {
appendPQExpBufferStr(&buffer, line);
if (buffer.data[buffer.len-1] == '\n') {
buffer.data[buffer.len-1] = '\0';
return buffer.data;
}
}
if (buffer.len > 0)
return buffer.data; /* EOF after reading some bufferload(s) */
/* EOF, so return null */
termPQExpBuffer(&buffer);
return NULL;
}
/*
* Put any startup stuff related to input in here. It's good to maintain
* abstraction this way.
*
* The only "flag" right now is 1 for use readline & history.
*/
void
initializeInput(int flags)
{
#ifdef USE_READLINE
if (flags == 1) {
useReadline = true;
rl_readline_name = "psql";
}
#endif
#ifdef USE_HISTORY
if (flags == 1) {
const char * home;
useHistory = true;
using_history();
home = getenv("HOME");
if (home) {
char * psql_history = (char *) malloc(strlen(home) + 20);
if (psql_history) {
sprintf(psql_history, "%s/.psql_history", home);
read_history(psql_history);
free(psql_history);
}
}
}
#endif
}
bool
saveHistory(const char *fname)
{
#ifdef USE_HISTORY
if (useHistory) {
if (write_history(fname) != 0) {
perror(fname);
return false;
}
return true;
}
else
return false;
#else
return false;
#endif
}
void
finishInput(void)
{
#ifdef USE_HISTORY
if (useHistory) {
char * home;
char * psql_history;
home = getenv("HOME");
if (home) {
psql_history = (char *) malloc(strlen(home) + 20);
if (psql_history) {
sprintf(psql_history, "%s/.psql_history", home);
write_history(psql_history);
free(psql_history);
}
}
}
#endif
}
#ifndef INPUT_H
#define INPUT_H
#include <config.h>
#include <c.h>
#include <stdio.h>
#include "settings.h"
/* If some other file needs to have access to readline/history, include this
* file and save yourself all this work.
*
* USE_READLINE and USE_HISTORY are the definite pointers regarding existence or not.
*/
#ifdef HAVE_LIBREADLINE
#ifdef HAVE_READLINE_H
#include <readline.h>
#define USE_READLINE 1
#else
#if defined(HAVE_READLINE_READLINE_H)
#include <readline/readline.h>
#define USE_READLINE 1
#endif
#endif
#endif
#if defined(HAVE_LIBHISTORY) || (defined(HAVE_LIBREADLINE) && defined(HAVE_HISTORY_IN_READLINE))
#if defined(HAVE_HISTORY_H)
#include <history.h>
#define USE_HISTORY 1
#else
#if defined(HAVE_READLINE_HISTORY_H)
#include <readline/history.h>
#define USE_HISTORY 1
#endif
#endif
#endif
char *
gets_interactive(const char *prompt);
char *
gets_fromFile(FILE *source);
void
initializeInput(int flags);
bool
saveHistory(const char *fname);
void
finishInput(void);
#endif
#include <config.h>
#include <c.h>
#include "large_obj.h"
#include <stdio.h>
#include <string.h>
#include <libpq-fe.h>
#include <postgres.h>
#include "settings.h"
#include "variables.h"
#include "common.h"
#include "print.h"
/*
* Since all large object ops must be in a transaction, we must do some magic
* here. You can set the variable lo_transaction to one of commit|rollback|
* nothing to get your favourite behaviour regarding any transaction in
* progress. Rollback is default.
*/
static char notice[80];
static void
_my_notice_handler(void * arg, const char * message) {
(void)arg;
strncpy(notice, message, 79);
notice[79] = '\0';
}
static bool
handle_transaction(PsqlSettings * pset)
{
const char * var = GetVariable(pset->vars, "lo_transaction");
PGresult * res;
bool commit;
PQnoticeProcessor old_notice_hook;
if (var && strcmp(var, "nothing")==0)
return true;
commit = (var && strcmp(var, "commit")==0);
notice[0] = '\0';
old_notice_hook = PQsetNoticeProcessor(pset->db, _my_notice_handler, NULL);
res = PSQLexec(pset, commit ? "COMMIT" : "ROLLBACK");
if (!res)
return false;
if (notice[0]) {
if ( (!commit && strcmp(notice, "NOTICE: UserAbortTransactionBlock and not in in-progress state\n")!=0) ||
(commit && strcmp(notice, "NOTICE: EndTransactionBlock and not inprogress/abort state\n")!=0) )
fputs(notice, stderr);
}
else if (!GetVariableBool(pset->vars, "quiet")) {
if (commit)
puts("Warning: Your transaction in progress has been committed.");
else
puts("Warning: Your transaction in progress has been rolled back.");
}
PQsetNoticeProcessor(pset->db, old_notice_hook, NULL);
return true;
}
/*
* do_lo_export()
*
* Write a large object to a file
*/
bool
do_lo_export(PsqlSettings * pset, const char * loid_arg, const char * filename_arg)
{
PGresult * res;
int status;
bool own_transaction = true;
const char * var = GetVariable(pset->vars, "lo_transaction");
if (var && strcmp(var, "nothing")==0)
own_transaction = false;
if (!pset->db) {
fputs("You are not connected to a database.\n", stderr);
return false;
}
if (own_transaction) {
if (!handle_transaction(pset))
return false;
if (!(res = PSQLexec(pset, "BEGIN")))
return false;
PQclear(res);
}
status = lo_export(pset->db, atol(loid_arg), (char *)filename_arg);
if (status != 1) { /* of course this status is documented nowhere :( */
fputs(PQerrorMessage(pset->db), stderr);
if (own_transaction) {
res = PQexec(pset->db, "ROLLBACK");
PQclear(res);
}
return false;
}
if (own_transaction) {
if (!(res = PSQLexec(pset, "COMMIT"))) {
res = PQexec(pset->db, "ROLLBACK");
PQclear(res);
return false;
}
PQclear(res);
}
fprintf(pset->queryFout, "lo_export\n");
return true;
}
/*
* do_lo_import()
*
* Copy large object from file to database
*/
bool
do_lo_import(PsqlSettings * pset, const char * filename_arg, const char * comment_arg)
{
PGresult * res;
Oid loid;
char buf[1024];
unsigned int i;
bool own_transaction = true;
const char * var = GetVariable(pset->vars, "lo_transaction");
if (var && strcmp(var, "nothing")==0)
own_transaction = false;
if (!pset->db) {
fputs("You are not connected to a database.\n", stderr);
return false;
}
if (own_transaction) {
if (!handle_transaction(pset))
return false;
if (!(res = PSQLexec(pset, "BEGIN")))
return false;
PQclear(res);
}
loid = lo_import(pset->db, (char *)filename_arg);
if (loid == InvalidOid) {
fputs(PQerrorMessage(pset->db), stderr);
if (own_transaction) {
res = PQexec(pset->db, "ROLLBACK");
PQclear(res);
}
return false;
}
/* insert description if given */
if (comment_arg) {
sprintf(buf, "INSERT INTO pg_description VALUES (%d, '", loid);
for (i=0; i<strlen(comment_arg); i++)
if (comment_arg[i]=='\'')
strcat(buf, "\\'");
else
strncat(buf, &comment_arg[i], 1);
strcat(buf, "')");
if (!(res = PSQLexec(pset, buf))) {
if (own_transaction) {
res = PQexec(pset->db, "ROLLBACK");
PQclear(res);
}
return false;
}
}
if (own_transaction) {
if (!(res = PSQLexec(pset, "COMMIT"))) {
res = PQexec(pset->db, "ROLLBACK");
PQclear(res);
return false;
}
PQclear(res);
}
fprintf(pset->queryFout, "lo_import %d\n", loid);
return true;
}
/*
* do_lo_unlink()
*
* removes a large object out of the database
*/
bool do_lo_unlink(PsqlSettings * pset, const char * loid_arg)
{
PGresult * res;
int status;
Oid loid = (Oid)atol(loid_arg);
char buf[256];
bool own_transaction = true;
const char * var = GetVariable(pset->vars, "lo_transaction");
if (var && strcmp(var, "nothing")==0)
own_transaction = false;
if (!pset->db) {
fputs("You are not connected to a database.\n", stderr);
return false;
}
if (own_transaction) {
if (!handle_transaction(pset))
return false;
if (!(res = PSQLexec(pset, "BEGIN")))
return false;
PQclear(res);
}
status = lo_unlink(pset->db, loid);
if (status == -1) {
fputs(PQerrorMessage(pset->db), stderr);
if (own_transaction) {
res = PQexec(pset->db, "ROLLBACK");
PQclear(res);
}
return false;
}
/* remove the comment as well */
sprintf(buf, "DELETE FROM pg_description WHERE objoid = %d", loid);
if (!(res = PSQLexec(pset, buf))) {
if (own_transaction) {
res = PQexec(pset->db, "ROLLBACK");
PQclear(res);
}
return false;
}
if (own_transaction) {
if (!(res = PSQLexec(pset, "COMMIT"))) {
res = PQexec(pset->db, "ROLLBACK");
PQclear(res);
return false;
}
PQclear(res);
}
fprintf(pset->queryFout, "lo_unlink %d\n", loid);
return true;
}
/*
* do_lo_list()
*
* Show all large objects in database, with comments if desired
*/
bool do_lo_list(PsqlSettings * pset)
{
PGresult * res;
char descbuf[512];
printQueryOpt myopt = pset->popt;
descbuf[0] = '\0';
strcat(descbuf, "SELECT usename as \"Owner\", substring(relname from 5) as \"ID\"");
if (GetVariableBool(pset->vars, "description"))
strcat(descbuf, ",\n obj_description(pg_class.oid) as \"Description\"");
strcat(descbuf,"\nFROM pg_class, pg_user\n"
"WHERE usesysid = relowner AND relkind = 'l'\n"
"ORDER BY \"ID\"");
res = PSQLexec(pset, descbuf);
if (!res)
return false;
myopt.topt.tuples_only = false;
myopt.nullPrint = NULL;
myopt.title = "Large objects";
printQuery(res, &myopt, pset->queryFout);
PQclear(res);
return true;
}
#ifndef LARGE_OBJ_H
#define LARGE_OBJ_H
#include "settings.h"
bool do_lo_export(PsqlSettings * pset, const char * loid_arg, const char * filename_arg);
bool do_lo_import(PsqlSettings * pset, const char * filename_arg, const char * comment_arg);
bool do_lo_unlink(PsqlSettings * pset, const char * loid_arg);
bool do_lo_list(PsqlSettings * pset);
#endif /* LARGE_OBJ_H */
#include <config.h>
#include <c.h>
#include "mainloop.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pqexpbuffer.h>
#include "settings.h"
#include "prompt.h"
#include "input.h"
#include "common.h"
#include "command.h"
/* MainLoop()
* Main processing loop for reading lines of input
* and sending them to the backend.
*
* This loop is re-entrant. May be called by \i command
* which reads input from a file.
*/
int
MainLoop(PsqlSettings *pset, FILE *source)
{
PQExpBuffer query_buf; /* buffer for query being accumulated */
char *line; /* current line of input */
char *xcomment; /* start of extended comment */
int len; /* length of the line */
int successResult = EXIT_SUCCESS;
backslashResult slashCmdStatus;
bool eof = false; /* end of our command input? */
bool success;
char in_quote; /* == 0 for no in_quote */
bool was_bslash; /* backslash */
int paren_level;
unsigned int query_start;
int i, prevlen, thislen;
/* Save the prior command source */
FILE *prev_cmd_source;
bool prev_cmd_interactive;
bool die_on_error;
const char *interpol_char;
/* Save old settings */
prev_cmd_source = pset->cur_cmd_source;
prev_cmd_interactive = pset->cur_cmd_interactive;
/* Establish new source */
pset->cur_cmd_source = source;
pset->cur_cmd_interactive = ((source == stdin) && !pset->notty);
query_buf = createPQExpBuffer();
if (!query_buf) {
perror("createPQExpBuffer");
exit(EXIT_FAILURE);
}
xcomment = NULL;
in_quote = 0;
paren_level = 0;
slashCmdStatus = CMD_UNKNOWN; /* set default */
/* main loop to get queries and execute them */
while (!eof)
{
if (slashCmdStatus == CMD_NEWEDIT)
{
/*
* just returned from editing the line? then just copy to the
* input buffer
*/
line = strdup(query_buf->data);
resetPQExpBuffer(query_buf);
/* reset parsing state since we are rescanning whole query */
xcomment = NULL;
in_quote = 0;
paren_level = 0;
}
else
{
/*
* otherwise, set interactive prompt if necessary
* and get another line
*/
if (pset->cur_cmd_interactive)
{
int prompt_status;
if (in_quote && in_quote == '\'')
prompt_status = PROMPT_SINGLEQUOTE;
else if (in_quote && in_quote == '"')
prompt_status= PROMPT_DOUBLEQUOTE;
else if (xcomment != NULL)
prompt_status = PROMPT_COMMENT;
else if (query_buf->len > 0)
prompt_status = PROMPT_CONTINUE;
else
prompt_status = PROMPT_READY;
line = gets_interactive(get_prompt(pset, prompt_status));
}
else
line = gets_fromFile(source);
}
/* Setting these will not have effect until next line */
die_on_error = GetVariableBool(pset->vars, "die_on_error");
interpol_char = GetVariable(pset->vars, "sql_interpol");;
/*
* query_buf holds query already accumulated. line is the malloc'd
* new line of input (note it must be freed before looping around!)
* query_start is the next command start location within the line.
*/
/* No more input. Time to quit, or \i done */
if (line == NULL || (!pset->cur_cmd_interactive && *line == '\0'))
{
if (GetVariableBool(pset->vars, "echo") && !GetVariableBool(pset->vars, "quiet"))
puts("EOF");
eof = true;
continue;
}
/* not currently inside an extended comment? */
if (xcomment)
xcomment = line;
/* strip trailing backslashes, they don't have a clear meaning */
while (1) {
char * cp = strrchr(line, '\\');
if (cp && (*(cp + 1) == '\0'))
*cp = '\0';
else
break;
}
/* echo back if input is from file and flag is set */
if (!pset->cur_cmd_interactive && GetVariableBool(pset->vars, "echo"))
fprintf(stderr, "%s\n", line);
/* interpolate variables into SQL */
len = strlen(line);
thislen = PQmblen(line);
for (i = 0; line[i]; i += (thislen = PQmblen(&line[i])) ) {
if (interpol_char && interpol_char[0] != '\0' && interpol_char[0] == line[i]) {
size_t in_length, out_length;
const char * value;
char * new;
bool closer; /* did we have a closing delimiter or just an end of line? */
in_length = strcspn(&line[i+thislen], interpol_char);
closer = line[i + thislen + in_length] == line[i];
line[i + thislen + in_length] = '\0';
value = interpolate_var(&line[i + thislen], pset);
out_length = strlen(value);
new = malloc(len + out_length - (in_length + (closer ? 2 : 1)) + 1);
if (!new) {
perror("malloc");
exit(EXIT_FAILURE);
}
new[0] = '\0';
strncat(new, line, i);
strcat(new, value);
if (closer)
strcat(new, line + i + 2 + in_length);
free(line);
line = new;
i += out_length;
}
}
/* nothing left on line? then ignore */
if (line[0] == '\0') {
free(line);
continue;
}
slashCmdStatus = CMD_UNKNOWN;
len = strlen(line);
query_start = 0;
/*
* Parse line, looking for command separators.
*
* The current character is at line[i], the prior character at
* line[i - prevlen], the next character at line[i + thislen].
*/
prevlen = 0;
thislen = (len > 0) ? PQmblen(line) : 0;
#define ADVANCE_1 (prevlen = thislen, i += thislen, thislen = PQmblen(line+i))
success = true;
for (i = 0; i < len; ADVANCE_1) {
if (!success && die_on_error)
break;
/* was the previous character a backslash? */
if (i > 0 && line[i - prevlen] == '\\')
was_bslash = true;
else
was_bslash = false;
/* in quote? */
if (in_quote) {
/* end of quote */
if (line[i] == in_quote && !was_bslash)
in_quote = '\0';
}
/* start of quote */
else if (line[i] == '\'' || line[i] == '"')
in_quote = line[i];
/* in extended comment? */
else if (xcomment != NULL) {
if (line[i] == '*' && line[i + thislen] == '/') {
xcomment = NULL;
ADVANCE_1;
}
}
/* start of extended comment? */
else if (line[i] == '/' && line[i + thislen] == '*') {
xcomment = &line[i];
ADVANCE_1;
}
/* single-line comment? truncate line */
else if ((line[i] == '-' && line[i + thislen] == '-') ||
(line[i] == '/' && line[i + thislen] == '/'))
{
line[i] = '\0'; /* remove comment */
break;
}
/* count nested parentheses */
else if (line[i] == '(')
paren_level++;
else if (line[i] == ')' && paren_level > 0)
paren_level--;
/* semicolon? then send query */
else if (line[i] == ';' && !was_bslash && paren_level==0) {
line[i] = '\0';
/* is there anything else on the line? */
if (line[query_start + strspn(line + query_start, " \t")]!='\0') {
/* insert a cosmetic newline, if this is not the first line in the buffer */
if (query_buf->len > 0)
appendPQExpBufferChar(query_buf, '\n');
/* append the line to the query buffer */
appendPQExpBufferStr(query_buf, line + query_start);
}
/* execute query */
success = SendQuery(pset, query_buf->data);
resetPQExpBuffer(query_buf);
query_start = i + thislen;
}
/* backslash command */
else if (was_bslash) {
const char * end_of_cmd = NULL;
line[i - prevlen] = '\0'; /* overwrites backslash */
/* is there anything else on the line? */
if (line[query_start + strspn(line + query_start, " \t")]!='\0') {
/* insert a cosmetic newline, if this is not the first line in the buffer */
if (query_buf->len > 0)
appendPQExpBufferChar(query_buf, '\n');
/* append the line to the query buffer */
appendPQExpBufferStr(query_buf, line + query_start);
}
/* handle backslash command */
slashCmdStatus = HandleSlashCmds(pset, &line[i], query_buf, &end_of_cmd);
success = slashCmdStatus != CMD_ERROR;
if (slashCmdStatus == CMD_SEND) {
success = SendQuery(pset, query_buf->data);
resetPQExpBuffer(query_buf);
query_start = i + thislen;
}
/* is there anything left after the backslash command? */
if (end_of_cmd) {
i += end_of_cmd - &line[i];
query_start = i;
}
else
break;
}
}
if (!success && die_on_error && !pset->cur_cmd_interactive) {
successResult = EXIT_USER;
break;
}
if (slashCmdStatus == CMD_TERMINATE) {
successResult = EXIT_SUCCESS;
break;
}
/* Put the rest of the line in the query buffer. */
if (line[query_start + strspn(line + query_start, " \t")]!='\0') {
if (query_buf->len > 0)
appendPQExpBufferChar(query_buf, '\n');
appendPQExpBufferStr(query_buf, line + query_start);
}
free(line);
/* In single line mode, send off the query if any */
if (query_buf->data[0] != '\0' && GetVariableBool(pset->vars, "singleline")) {
success = SendQuery(pset, query_buf->data);
resetPQExpBuffer(query_buf);
}
/* Have we lost the db connection? */
if (pset->db == NULL && !pset->cur_cmd_interactive) {
successResult = EXIT_BADCONN;
break;
}
} /* while */
destroyPQExpBuffer(query_buf);
pset->cur_cmd_source = prev_cmd_source;
pset->cur_cmd_interactive = prev_cmd_interactive;
return successResult;
} /* MainLoop() */
#ifndef MAINLOOP_H
#define MAINLOOP_H
#include <stdio.h>
#include "settings.h"
int
MainLoop(PsqlSettings *pset, FILE *source);
#endif MAINLOOP_H
This diff is collapsed.
#ifndef PRINT_H
#define PRINT_H
#include <config.h>
#include <c.h>
#include <stdio.h>
#include <libpq-fe.h>
enum printFormat {
PRINT_NOTHING = 0, /* to make sure someone initializes this */
PRINT_UNALIGNED,
PRINT_ALIGNED,
PRINT_HTML,
PRINT_LATEX
/* add your favourite output format here ... */
};
typedef struct _printTableOpt {
enum printFormat format; /* one of the above */
bool expanded; /* expanded/vertical output (if supported by output format) */
bool pager; /* use pager for output (if to stdout and stdout is a tty) */
bool tuples_only; /* don't output headers, row counts, etc. */
unsigned short int border; /* Print a border around the table. 0=none, 1=dividing lines, 2=full */
char *fieldSep; /* field separator for unaligned text mode */
char *tableAttr; /* attributes for HTML <table ...> */
} printTableOpt;
/*
* Use this to print just any table in the supported formats.
* - title is just any string (NULL is fine)
* - headers is the column headings (NULL ptr terminated). It must be given and
* complete since the column count is generated from this.
* - cells are the data cells to be printed. Now you know why the correct
* column count is important
* - footers are lines to be printed below the table
* - align is an 'l' or an 'r' for every column, if the output format needs it.
* (You must specify this long enough. Otherwise anything could happen.)
*/
void
printTable(const char * title, char ** headers, char ** cells, char ** footers,
const char * align,
const printTableOpt * opt, FILE * fout);
typedef struct _printQueryOpt {
printTableOpt topt; /* the options above */
char * nullPrint; /* how to print null entities */
bool quote; /* quote all values as much as possible */
char * title; /* override title */
char ** footers; /* override footer (default is "(xx rows)") */
} printQueryOpt;
/*
* Use this to print query results
*
* It calls the printTable above with all the things set straight.
*/
void
printQuery(PGresult * result, const printQueryOpt * opt, FILE * fout);
#endif /* PRINT_H */
#include <config.h>
#include <c.h>
#include "prompt.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <libpq-fe.h>
#include "settings.h"
#include "common.h"
#ifdef WIN32
#define popen(x,y) _popen(x,y)
#define pclose(x) _pclose(x)
#endif
/*--------------------------
* get_prompt
*
* Returns a statically allocated prompt made by interpolating certain
* tcsh style escape sequences into pset->vars "prompt1|2|3".
* (might not be completely multibyte safe)
*
* Defined interpolations are:
* %M - database server hostname (or "." if not TCP/IP)
* %m - like %M but hostname truncated after first dot
* %> - database server port number (or "." if not TCP/IP)
* %n - database user name
* %/ - current database
* %~ - like %/ but "~" when database name equals user name
* %# - "#" if the username is postgres, ">" otherwise
* %R - in prompt1 normally =, or ^ if single line mode,
* or a ! if session is not connected to a database;
* in prompt2 -, *, ', or ";
* in prompt3 nothing
* %? - the error code of the last query (not yet implemented)
* %% - a percent sign
*
* %[0-9] - the character with the given decimal code
* %0[0-7] - the character with the given octal code
* %0x[0-9A-Fa-f] - the character with the given hexadecimal code
*
* %`command` - The result of executing command in /bin/sh with trailing
* newline stripped.
* %$name$ - The value of the psql/environment/magic varible 'name'
* (same rules as for, e.g., \echo $foo)
* (those will not be rescanned for more escape sequences!)
*
*
* If the application-wide prompts became NULL somehow, the returned string
* will be empty (not NULL!). Do not free() the result of this function unless
* you want trouble.
*--------------------------
*/
const char *
get_prompt(PsqlSettings *pset, promptStatus_t status)
{
#define MAX_PROMPT_SIZE 256
static char destination[MAX_PROMPT_SIZE+1];
char buf[MAX_PROMPT_SIZE+1];
bool esc = false;
const char *p;
const char * prompt_string;
if (GetVariable(pset->vars, "quiet"))
return "";
if (status == PROMPT_READY)
prompt_string = GetVariable(pset->vars, "prompt1");
else if (status == PROMPT_CONTINUE || status == PROMPT_SINGLEQUOTE || status == PROMPT_DOUBLEQUOTE || status == PROMPT_COMMENT)
prompt_string = GetVariable(pset->vars, "prompt2");
else if (status == PROMPT_COPY)
prompt_string = GetVariable(pset->vars, "prompt3");
else
prompt_string = "? ";
destination[0] = '\0';
for (p = prompt_string;
p && *p && strlen(destination)<MAX_PROMPT_SIZE;
p++)
{
MemSet(buf, 0, MAX_PROMPT_SIZE+1);
if (esc)
{
switch (*p)
{
case '%':
strcpy(buf, "%");
break;
/* Current database */
case '/':
if (pset->db)
strncpy(buf, PQdb(pset->db), MAX_PROMPT_SIZE);
break;
case '~': {
const char * var;
if (pset->db) {
if ( strcmp(PQdb(pset->db), PQuser(pset->db))==0 ||
( (var = getenv("PGDATABASE")) && strcmp(var, PQdb(pset->db))==0) )
strcpy(buf, "~");
else
strncpy(buf, PQdb(pset->db), MAX_PROMPT_SIZE);
}
break;
}
/* DB server hostname (long/short) */
case 'M':
case 'm':
if (pset->db) {
if (PQhost(pset->db)) {
strncpy(buf, PQhost(pset->db), MAX_PROMPT_SIZE);
if (*p == 'm')
buf[strcspn(buf,".")] = '\0';
}
else
buf[0] = '.';
}
break;
/* DB server port number */
case '>':
if (pset->db) {
if (PQhost(pset->db))
strncpy(buf, PQport(pset->db), MAX_PROMPT_SIZE);
else
buf[0] = '.';
}
break;
/* DB server user name */
case 'n':
if (pset->db)
strncpy(buf, PQuser(pset->db), MAX_PROMPT_SIZE);
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
{
long int l;
char * end;
l = strtol(p, &end, 0);
sprintf(buf, "%c", (unsigned char)l);
p = end-1;
break;
}
case 'R':
switch(status) {
case PROMPT_READY:
if (!pset->db)
buf[0] = '!';
else if (!GetVariableBool(pset->vars, "singleline"))
buf[0] = '=';
else
buf[0] = '^';
break;
case PROMPT_CONTINUE:
buf[0] = '-';
break;
case PROMPT_SINGLEQUOTE:
buf[0] = '\'';
break;
case PROMPT_DOUBLEQUOTE:
buf[0] = '"';
break;
case PROMPT_COMMENT:
buf[0] = '*';
break;
default:
buf[0] = '\0';
break;
}
case '?':
/* not here yet */
break;
case '#':
{
if (pset->db && strcmp(PQuser(pset->db), "postgres")==0)
buf[0] = '#';
else
buf[0] = '>';
break;
}
/* execute command */
case '`':
{
FILE * fd = NULL;
char * file = strdup(p+1);
int cmdend;
cmdend = strcspn(file, "`");
file[cmdend] = '\0';
if (file)
fd = popen(file, "r");
if (fd) {
fgets(buf, MAX_PROMPT_SIZE-1, fd);
pclose(fd);
}
if (buf[strlen(buf)-1] == '\n')
buf[strlen(buf)-1] = '\0';
free(file);
p += cmdend+1;
break;
}
/* interpolate variable */
case '$':
{
char *name;
const char *val;
int nameend;
name = strdup(p+1);
nameend = strcspn(name, "$");
name[nameend] = '\0';
val = interpolate_var(name, pset);
if (val)
strncpy(buf, val, MAX_PROMPT_SIZE);
free(name);
p += nameend+1;
break;
}
default:
buf[0] = *p;
buf[1] = '\0';
}
esc = false;
}
else if (*p == '%')
esc = true;
else
{
buf[0] = *p;
buf[1] = '\0';
esc = false;
}
if (!esc) {
strncat(destination, buf, MAX_PROMPT_SIZE-strlen(destination));
}
}
destination[MAX_PROMPT_SIZE] = '\0';
return destination;
}
#ifndef PROMPT_H
#define PROMPT_H
#include "settings.h"
typedef enum _promptStatus {
PROMPT_READY,
PROMPT_CONTINUE,
PROMPT_COMMENT,
PROMPT_SINGLEQUOTE,
PROMPT_DOUBLEQUOTE,
PROMPT_COPY
} promptStatus_t;
const char *
get_prompt(PsqlSettings *pset, promptStatus_t status);
#endif /* PROMPT_H */
#ifndef SETTINGS_H
#define SETTINGS_H
#include <stdio.h>
#include <stdlib.h>
#include <libpq-fe.h>
#include <c.h>
#include "variables.h"
#include "print.h"
#define DEFAULT_FIELD_SEP "|"
#define DEFAULT_EDITOR "/bin/vi"
#define DEFAULT_PROMPT1 "%/%R%# "
#define DEFAULT_PROMPT2 "%/%R%# "
#define DEFAULT_PROMPT3 ">> "
typedef struct _psqlSettings
{
PGconn *db; /* connection to backend */
FILE *queryFout; /* where to send the query results */
bool queryFoutPipe; /* queryFout is from a popen() */
printQueryOpt popt;
VariableSpace vars; /* "shell variable" repository */
char *gfname; /* one-shot file output argument for \g */
bool notty; /* stdin or stdout is not a tty (as determined on startup) */
bool useReadline; /* use libreadline routines */
bool useHistory;
bool getPassword; /* prompt the user for a username and
password */
FILE * cur_cmd_source; /* describe the status of the current main loop */
bool cur_cmd_interactive;
bool has_client_encoding; /* was PGCLIENTENCODING set on startup? */
} PsqlSettings;
#ifndef EXIT_SUCCESS
#define EXIT_SUCCESS 0
#endif
#ifndef EXIT_FAILURE
#define EXIT_FAILURE 1
#endif
#define EXIT_BADCONN 2
#define EXIT_USER 3
#endif
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/*-------------------------------------------------------------------------
*
* stringutils.h
*
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: stringutils.h,v 1.8 1999/02/13 23:20:42 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef STRINGUTILS_H #ifndef STRINGUTILS_H
#define STRINGUTILS_H #define STRINGUTILS_H
/* use this for memory checking of alloc and free using Tcl's memory check /* The cooler version of strtok() which knows about quotes and doesn't
package*/ * overwrite your input */
#ifdef TCL_MEM_DEBUG extern char *
#include <tcl.h> strtokx(const char *s,
#define malloc(x) ckalloc(x) const char *delim,
#define free(x) ckfree(x) const char *quote,
#define realloc(x,y) ckrealloc(x,y) char escape,
#endif char * was_quoted,
unsigned int * token_pos);
/* string fiddling utilties */
/* all routines assume null-terminated strings! as arguments */
/* removes whitespaces from the left, right and both sides of a string */
/* MODIFIES the string passed in and returns the head of it */
extern char *rightTrim(char *s);
#ifdef STRINGUTILS_TEST
extern void testStringUtils();
#endif
#ifndef NULL_STR
#define NULL_STR (char*)0
#endif
#ifndef NULL
#define NULL 0
#endif
#endif /* STRINGUTILS_H */ #endif /* STRINGUTILS_H */
This diff is collapsed.
/* This implements a sort of variable repository. One could also think of it
* as cheap version of an associative array. In each one of these
* datastructures you can store name/value pairs.
*
* All functions (should) follow the Shit-In-Shit-Out (SISO) principle, i.e.,
* you can pass them NULL pointers and the like and they will return something
* appropriate.
*/
#ifndef VARIABLES_H
#define VARIABLES_H
#include <c.h>
#define VALID_VARIABLE_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_"
struct _variable {
char * name;
char * value;
struct _variable * next;
};
typedef struct _variable * VariableSpace;
VariableSpace CreateVariableSpace(void);
const char * GetVariable(VariableSpace space, const char * name);
bool GetVariableBool(VariableSpace space, const char * name);
bool SetVariable(VariableSpace space, const char * name, const char * value);
bool DeleteVariable(VariableSpace space, const char * name);
void DestroyVariableSpace(VariableSpace space);
#endif /* VARIABLES_H */
This diff is collapsed.
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