Header And Logo

PostgreSQL
| The world's most advanced open source database.

Defines | Functions

input.c File Reference

#include "postgres_fe.h"
#include <unistd.h>
#include <fcntl.h>
#include "input.h"
#include "settings.h"
#include "tab-complete.h"
#include "common.h"
Include dependency graph for input.c:

Go to the source code of this file.

Defines

#define PSQLHISTORY   ".psql_history"

Functions

static void finishInput (void)
char * gets_interactive (const char *prompt)
void pg_append_history (const char *s, PQExpBuffer history_buf)
void pg_send_history (PQExpBuffer history_buf)
char * gets_fromFile (FILE *source)
void initializeInput (int flags)
bool saveHistory (char *fname, int max_lines, bool appendFlag, bool encodeFlag)

Define Documentation

#define PSQLHISTORY   ".psql_history"

Definition at line 21 of file input.c.

Referenced by initializeInput().


Function Documentation

static void finishInput ( void   )  [static]

Definition at line 420 of file input.c.

References free, GetVariableNum(), pset, saveHistory(), and _psqlSettings::vars.

Referenced by initializeInput().

{
#ifdef USE_READLINE
    if (useHistory && psql_history)
    {
        int         hist_size;

        hist_size = GetVariableNum(pset.vars, "HISTSIZE", 500, -1, true);
        saveHistory(psql_history, hist_size, true, true);
        free(psql_history);
        psql_history = NULL;
    }
#endif
}

char* gets_fromFile ( FILE *  source  ) 

Definition at line 164 of file input.c.

References appendPQExpBufferStr(), createPQExpBuffer(), PQExpBufferData::data, PQExpBufferData::len, NULL, pg_strdup(), PQExpBufferBroken, psql_error(), resetPQExpBuffer(), sigint_interrupt_enabled, and strerror().

Referenced by exec_command(), gets_interactive(), and MainLoop().

{
    static PQExpBuffer buffer = NULL;

    char        line[1024];

    if (buffer == NULL)         /* first time through? */
        buffer = createPQExpBuffer();
    else
        resetPQExpBuffer(buffer);

    for (;;)
    {
        char       *result;

        /* Enable SIGINT to longjmp to sigint_interrupt_jmp */
        sigint_interrupt_enabled = true;

        /* Get some data */
        result = fgets(line, sizeof(line), source);

        /* Disable SIGINT again */
        sigint_interrupt_enabled = false;

        /* EOF or error? */
        if (result == NULL)
        {
            if (ferror(source))
            {
                psql_error("could not read from input file: %s\n",
                           strerror(errno));
                return NULL;
            }
            break;
        }

        appendPQExpBufferStr(buffer, line);

        if (PQExpBufferBroken(buffer))
        {
            psql_error("out of memory\n");
            return NULL;
        }

        /* EOL? */
        if (buffer->data[buffer->len - 1] == '\n')
        {
            buffer->data[buffer->len - 1] = '\0';
            return pg_strdup(buffer->data);
        }
    }

    if (buffer->len > 0)        /* EOF after reading some bufferload(s) */
        return pg_strdup(buffer->data);

    /* EOF, so return null */
    return NULL;
}

char* gets_interactive ( const char *  prompt  ) 

Definition at line 60 of file input.c.

References gets_fromFile(), and sigint_interrupt_enabled.

Referenced by MainLoop().

{
#ifdef USE_READLINE
    if (useReadline)
    {
        char       *result;

        /* Enable SIGINT to longjmp to sigint_interrupt_jmp */
        sigint_interrupt_enabled = true;

        /* On some platforms, readline is declared as readline(char *) */
        result = readline((char *) prompt);

        /* Disable SIGINT again */
        sigint_interrupt_enabled = false;

        return result;
    }
#endif

    fputs(prompt, stdout);
    fflush(stdout);
    return gets_fromFile(stdin);
}

void initializeInput ( int  flags  ) 

Definition at line 272 of file input.c.

References expand_tilde(), finishInput(), get_home_path(), GetVariable(), initialize_readline(), MAXPGPATH, NULL, pg_malloc(), pg_strdup(), pset, PSQLHISTORY, snprintf(), and _psqlSettings::vars.

Referenced by main().

{
#ifdef USE_READLINE
    if (flags & 1)
    {
        const char *histfile;
        char        home[MAXPGPATH];

        useReadline = true;
        initialize_readline();

        useHistory = true;
        using_history();
        history_lines_added = 0;

        histfile = GetVariable(pset.vars, "HISTFILE");

        if (histfile == NULL)
        {
            char       *envhist;

            envhist = getenv("PSQL_HISTORY");
            if (envhist != NULL && strlen(envhist) > 0)
                histfile = envhist;
        }

        if (histfile == NULL)
        {
            if (get_home_path(home))
            {
                psql_history = pg_malloc(strlen(home) + 1 +
                                         strlen(PSQLHISTORY) + 1);
                snprintf(psql_history, MAXPGPATH, "%s/%s", home, PSQLHISTORY);
            }
        }
        else
        {
            psql_history = pg_strdup(histfile);
            expand_tilde(&psql_history);
        }

        if (psql_history)
        {
            read_history(psql_history);
            decode_history();
        }
    }
#endif

    atexit(finishInput);
}

void pg_append_history ( const char *  s,
PQExpBuffer  history_buf 
)

Definition at line 90 of file input.c.

References appendPQExpBufferChar(), and appendPQExpBufferStr().

Referenced by MainLoop().

{
#ifdef USE_READLINE
    if (useHistory && s)
    {
        appendPQExpBufferStr(history_buf, s);
        if (!s[0] || s[strlen(s) - 1] != '\n')
            appendPQExpBufferChar(history_buf, '\n');
    }
#endif
}

void pg_send_history ( PQExpBuffer  history_buf  ) 

Definition at line 112 of file input.c.

References PQExpBufferData::data, free, hctl_ignoredups, hctl_ignorespace, _psqlSettings::histcontrol, i, pg_strdup(), pset, and resetPQExpBuffer().

Referenced by MainLoop().

{
#ifdef USE_READLINE
    static char *prev_hist = NULL;

    char       *s = history_buf->data;
    int         i;

    /* Trim any trailing \n's (OK to scribble on history_buf) */
    for (i = strlen(s) - 1; i >= 0 && s[i] == '\n'; i--)
        ;
    s[i + 1] = '\0';

    if (useHistory && s[0])
    {
        if (((pset.histcontrol & hctl_ignorespace) &&
             s[0] == ' ') ||
            ((pset.histcontrol & hctl_ignoredups) &&
             prev_hist && strcmp(s, prev_hist) == 0))
        {
            /* Ignore this line as far as history is concerned */
        }
        else
        {
            /* Save each previous line for ignoredups processing */
            if (prev_hist)
                free(prev_hist);
            prev_hist = pg_strdup(s);
            /* And send it to readline */
            add_history(s);
            /* Count lines added to history for use later */
            history_lines_added++;
        }
    }

    resetPQExpBuffer(history_buf);
#endif
}

bool saveHistory ( char *  fname,
int  max_lines,
bool  appendFlag,
bool  encodeFlag 
)

Definition at line 341 of file input.c.

References close, DEVNULL, Max, Min, PG_BINARY, psql_error(), and strerror().

Referenced by exec_command(), and finishInput().

{
#ifdef USE_READLINE

    /*
     * Suppressing the write attempt when HISTFILE is set to /dev/null may
     * look like a negligible optimization, but it's necessary on e.g. Darwin,
     * where write_history will fail because it tries to chmod the target
     * file.
     */
    if (useHistory && fname &&
        strcmp(fname, DEVNULL) != 0)
    {
        if (encodeFlag)
            encode_history();

        /*
         * On newer versions of libreadline, truncate the history file as
         * needed and then append what we've added.  This avoids overwriting
         * history from other concurrent sessions (although there are still
         * race conditions when two sessions exit at about the same time). If
         * we don't have those functions, fall back to write_history().
         *
         * Note: return value of write_history is not standardized across GNU
         * readline and libedit.  Therefore, check for errno becoming set to
         * see if the write failed.  Similarly for append_history.
         */
#if defined(HAVE_HISTORY_TRUNCATE_FILE) && defined(HAVE_APPEND_HISTORY)
        if (appendFlag)
        {
            int         nlines;
            int         fd;

            /* truncate previous entries if needed */
            if (max_lines >= 0)
            {
                nlines = Max(max_lines - history_lines_added, 0);
                (void) history_truncate_file(fname, nlines);
            }
            /* append_history fails if file doesn't already exist :-( */
            fd = open(fname, O_CREAT | O_WRONLY | PG_BINARY, 0600);
            if (fd >= 0)
                close(fd);
            /* append the appropriate number of lines */
            if (max_lines >= 0)
                nlines = Min(max_lines, history_lines_added);
            else
                nlines = history_lines_added;
            errno = 0;
            (void) append_history(nlines, fname);
            if (errno == 0)
                return true;
        }
        else
#endif
        {
            /* truncate what we have ... */
            if (max_lines >= 0)
                stifle_history(max_lines);
            /* ... and overwrite file.  Tough luck for concurrent sessions. */
            errno = 0;
            (void) write_history(fname);
            if (errno == 0)
                return true;
        }

        psql_error("could not save history to file \"%s\": %s\n",
                   fname, strerror(errno));
    }
#else
    /* only get here in \s case, so complain */
    psql_error("history is not supported by this installation\n");
#endif

    return false;
}