initdb in C - Mailing list pgsql-patches

From Bruce Momjian
Subject initdb in C
Date
Msg-id 200311080515.hA85FgT21744@candle.pha.pa.us
Whole thread Raw
Responses Re: initdb in C
Re: initdb in C
Re: initdb in C
List pgsql-patches
Andrew Dunstan wrote:
>
> Bruce,
>
> I think this is now ready to import into the WIN32 branch. I have ironed out
> the cygwin dependencies, and posted a note about what we'll need to do
> inside postgres to handle the environment values for setlocale() on Windows.
>
> If you want to rip out the unlink stuff (see previous email thread) we'll
> need a version of dirmod.c that is compiled with -DFRONTEND - I guess libpq
> is the obvious place for that.
>
> Apart from initdb.c we'll need the file system_views.sql, which can be taken
> without change (unless someone want to add comments to it), and Makefile
> modifications (see my Makefile on the web for an example, but it probably
> doesn't comply with our standard way of doing things).
>
> Anything broken can then be fixed with patches - I have about 7 versions on
> 5 machines now and my head is starting to swim -).
>
> pg_id and pg_encoding now become redundant in their entirety, as does
> initdb.sh.

Here is a slightly modified version of Andrew's great work in making a C
version of initdb.  Other than minor cleanups, the only big change was
to remove rmdir handling because we using rm -r and rmdir /s in
commands/dbcommands.c, so we might as use the same thing for initdb.c
rather than having code that traverses the directory tree doing 'rm'.

The other change was to remove the unlink code and instead use
port/dirmod.c's version.

It passes all the regression tests.  I have also included a diff against
Andrew's version so you can see my changes.  It seems Andrew had a very
current version of initdb.  The only update he missed was the change to
test the number of connections before shared buffers --- I made that
change myself.

I would like to apply this in the next few days to HEAD before initdb.sh
drifts.  We are no longer using the WIN32_DEV branch because all our
work is now in 7.5 head.

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073
/*-------------------------------------------------------------------------
 *
 * initdb
 *
 * author: Andrew Dunstan       mailto:andrew@dunslane.net
 *
 * Copyright (C) 2003 Andrew Dunstan
 * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * This code is released under the terms of the PostgreSQL License.
 *
 * This is a C implementation of the previous shell script for setting up a
 * PostgreSQL cluster location, and should be highly compatible with it.
 *
 * $Header$
 *
 * TODO:
 *     - clean up find_postgres code and return values
 *
 * Note:
 *     The program has some memory leakage - it isn't worth cleaning it up.
 *     Even before the code was put in to free most of the dynamic memory
 *     used it ran around 500Kb used + malloc overhead. It should now use
 *     far less than that (around 240Kb - the size of the BKI file).
 *     If we can't load this much data into memory how will we ever run
 *     postgres anyway?
 *
 *-------------------------------------------------------------------------
 */

#include "postgres_fe.h"

#include "getopt_long.h"
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <locale.h>

#include <signal.h>

#include "libpq/pqsignal.h"
#include "mb/pg_wchar.h"


/* version string we expect back from postgres */
#define PG_VERSIONSTR "postgres (PostgreSQL) " PG_VERSION "\n"

/* values passed in by makefile define */

char       *bindir = PGBINDIR;
char       *datadir = PGDATADIR;

/* values to be obtained from arguments */
char       *pg_data = "";
char       *encoding = "";
char       *locale = "";
char       *lc_collate = "";
char       *lc_ctype = "";
char       *lc_monetary = "";
char       *lc_numeric = "";
char       *lc_time = "";
char       *lc_messages = "";
char       *username = "";
bool        pwprompt = false;
bool        debug = false;
bool        noclean = false;
bool        show_help = false;
bool        show_version = false;
bool        show_setting = false;


/* internal vars */
char       *progname;
char       *self_path;
char       *postgres;
char       *encodingid = "0";
char       *bki_file;
char       *desc_file;
char       *hba_file;
char       *ident_file;
char       *conf_file;
char       *conversion_file;
char       *info_schema_file;
char       *features_file;
char       *system_views_file;
char       *effective_user;
bool        testpath = true;
bool        made_new_pgdata = false;
char        infoversion[100];
bool        not_ok = false;

/* defaults */
int            n_connections = 10;
int            n_buffers = 50;


/* platform specific path stuff */
#if defined(__CYGWIN__) || defined(WIN32)
#define EXE ".exe"
#define DEVNULL "nul"
#else
#define EXE ""
#define DEVNULL "/dev/null"
#endif

#ifdef WIN32
#define PATHSEP ';'
#else
#define PATHSEP ':'
#endif

/* detected path to postgres and (we assume) friends */
char       *pgpath;

/* forward declare all our functions */
static bool rmtree(char *, bool);
static void exit_nicely(void);
static void canonicalise_path(char *);

#ifdef WIN32
static char *expanded_path(char *);
#else
#define expanded_path(x) (x)
#endif

static char **readfile(char *);
static void writefile(char *, char **);
static char *get_id(void);
static char *get_encoding_id(char *);
static char *get_short_version(void);
static int    mkdir_p(char *, mode_t);
static bool check_data_dir(void);
static bool mkdatadir(char *);
static bool chklocale(const char *);
static void setlocales(void);
static void set_input(char **, char *);
static void check_input(char *path);
static int    find_postgres(char *);
static int    set_paths(void);
static char **replace_token(char **, char *, char *);
static void set_short_version(char *, char *);
static void set_null_conf(void);
static void test_buffers(void);
static void test_connections(void);
static void setup_config(void);
static void bootstrap_template1(char *);
static void setup_shadow(void);
static void get_set_pwd(void);
static void unlimit_systables(void);
static void setup_depend(void);
static void setup_sysviews(void);
static void setup_description(void);
static void setup_conversion(void);
static void setup_privileges(void);
static void set_info_version(void);
static void setup_schema(void);
static void vacuum_db(void);
static void make_template0(void);
static void usage(void);
static void trapsig(int);
static void check_ok(void);
static char *xstrdup(const char *);
static void *xmalloc(size_t);

/*
 * macros for running pipes to postgres
 */
#define PG_CMD_DECL        char cmd[MAXPGPATH]; char ** line ; FILE * pg
#define PG_CMD_DECL_NOLINE           char cmd[MAXPGPATH]; FILE * pg

#define PG_CMD_OPEN \
do { \
          pg = popen(cmd,PG_BINARY_W); \
          if (pg == NULL)  \
            exit_nicely(); \
} while (0)

#define PG_CMD_CLOSE \
do { \
         if(pclose(pg) >> 8 & 0xff) \
            exit_nicely(); \
} while (0)

#define PG_CMD_PUTLINE \
do { \
         if (fputs(*line, pg) < 0) \
           exit_nicely(); \
         fflush(pg); \
} while (0)

#ifndef WIN32
#define QUOTE_PATH    ""
#else
#define QUOTE_PATH    "\""
#endif

/*
 * routines to check mem allocations and fail noisily.
 * Note that we can't call exit_nicely() on a memory failure, as it calls
 * rmtree() which needs memory allocation. So we just exit with a bang.
 *
 */
static void *
xmalloc(size_t size)
{
    void       *result;

    result = malloc(size);
    if (!result)
    {
        fputs("malloc failure - bailing out\n", stderr);
        exit(1);
    }
    return result;
}

static char *
xstrdup(const char *s)
{
    char       *result;

    result = strdup(s);
    if (!result)
    {
        fputs("strdup failure - bailing out\n", stderr);
        exit(1);
    }
    return result;
}

/*
 * delete a directory tree recursively
 * assumes path points to a valid directory
 * deletes everything under path
 * if rmtopdir is true deletes the directory too
 *
 */
static bool
rmtree(char *path, bool rmtopdir)
{
    char        buf[MAXPGPATH + 64];
    int            ret = 1;

#ifndef WIN32
    /* doesn't handle .* files */
    snprintf(buf, sizeof(buf), "rm -rf '%s%s'", path,
             (rmtopdir) ? "" : "/*");
#else
    snprintf(buf, sizeof(buf), "rmdir /s /q \"%s\"", path);
#endif

    if (system(buf) != 0)
        ret = 0;

#ifdef WIN32
    if (ret == 1 && !rmtopdir)
        ret = !mkdir(path);        /* recreate, no permission issues? */
#endif
    return ret;
}


/*
 * make all paths look like unix, with forward slashes
 * also strip any trailing slash
 * needed?
 */
static void
canonicalise_path(char *path)
{
    char       *p;

    for (p = path; *p; p++)
    {
#ifdef WIN32
        if (*p == '\\')
            *p = '/';
#endif
    }
    if (p != path && *--p == '/')
        *p = '\0';
}

/*
 * make a copy of the array of lines, with token replaced by replacement
 * the first time it occurs on each line.
 * This does most of what sed was used for in the shell script, but
 * doesn't need any regexp stuff.
 */
static char **
replace_token(char **lines, char *token, char *replacement)
{
    int            numlines = 1;
    int            i;
    char      **result;
    int            toklen,
                replen,
                diff;

    for (i = 0; lines[i]; i++)
        numlines++;

    result = (char **) xmalloc(numlines * sizeof(char *));

    toklen = strlen(token);
    replen = strlen(replacement);
    diff = replen - toklen;

    for (i = 0; i < numlines; i++)
    {
        char       *where;
        char       *newline;
        int            pre;

        /* just copy pointer if NULL or no change needed */

        if (lines[i] == NULL || (where = strstr(lines[i], token)) == NULL)
        {
            result[i] = lines[i];
            continue;
        }

        /* if we get here a change is needed - set up new line */

        newline = (char *) xmalloc(strlen(lines[i]) + diff + 1);

        pre = where - lines[i];

        strncpy(newline, lines[i], pre);

        strcpy(newline + pre, replacement);

        strcpy(newline + pre + replen, lines[i] + pre + toklen);

        result[i] = newline;

    }

    return result;

}

/*
 * get the lines from a text file
 *
 */
static char **
readfile(char *path)
{
    FILE       *infile;
    int            maxlength = 0,
                linelen = 0;
    int            nlines = 0;
    char      **result;
    char       *buffer;
    int            c;

    if ((infile = fopen(path, "r")) == NULL)
    {
        fprintf(stderr, "could not read %s ... ", path);
        exit_nicely();
    }

    /* pass over the file twice - the first time to size the result */

    while ((c = fgetc(infile)) != EOF)
    {
        linelen++;
        if (c == '\n')
        {
            nlines++;
            if (linelen > maxlength)
                maxlength = linelen;
            linelen = 0;
        }
    }

    /* handle last line without a terminating newline (yuck) */

    if (linelen)
        nlines++;
    if (linelen > maxlength)
        maxlength = linelen;

    /* set up the result and the line buffer */

    result = (char **) xmalloc((nlines + 2) * sizeof(char *));
    buffer = (char *) xmalloc(maxlength + 2);

    /* now reprocess the file and store the lines */

    rewind(infile);
    nlines = 0;
    while (fgets(buffer, maxlength + 1, infile) != NULL)
    {
        result[nlines] = xstrdup(buffer);
        nlines++;
    }

    fclose(infile);
    result[nlines] = NULL;

    return result;
}

/*
 * write an array of lines to a file
 *
 */
static void
writefile(char *path, char **lines)
{
    FILE       *out_file;
    char      **line;

    ;
    if ((out_file = fopen(path, PG_BINARY_W)) == NULL)
    {
        fprintf(stderr, "could not write %s ... ", path);
        exit_nicely();
    }
    for (line = lines; *line != NULL; line++)
    {
        if (fputs(*line, out_file) < 0)
            exit_nicely();
        free(*line);
    }
    if (fclose(out_file))
        exit_nicely();
}

/* source stolen from FreeBSD /src/bin/mkdir/mkdir.c and adapted */

/*
 * this tries to build all the elements of a path to a directory a la mkdir -p
 * we assume the path is in canonical form, i.e. uses / as the separator
 * we also assume it isn't null.
 *
 */
static int
mkdir_p(char *path, mode_t omode)
{
    struct stat sb;
    mode_t        numask,
                oumask;
    int            first,
                last,
                retval;
    char       *p;

    p = path;
    oumask = 0;
    retval = 0;

#ifdef WIN32
    /* skip network and drive specifiers for win32 */
    if (strlen(p) >= 2)
    {
        if (p[0] == '/' && p[1] == '/')
        {
            /* network drive */
            p = strstr(p + 2, "/");
            if (p == NULL)
                return 1;
        }
        else if (p[1] == ':' &&
                 ((p[0] >= 'a' && p[0] <= 'z') ||
                  (p[0] >= 'A' && p[0] <= 'Z')))
        {
            /* local drive */
            p += 2;
        }
    }
#endif

    if (p[0] == '/')            /* Skip leading '/'. */
        ++p;
    for (first = 1, last = 0; !last; ++p)
    {
        if (p[0] == '\0')
            last = 1;
        else if (p[0] != '/')
            continue;
        *p = '\0';
        if (p[1] == '\0')
            last = 1;
        if (first)
        {
            /*
             * POSIX 1003.2: For each dir operand that does not name an
             * existing directory, effects equivalent to those cased by
             * the following command shall occcur:
             *
             * mkdir -p -m $(umask -S),u+wx $(dirname dir) && mkdir [-m mode]
             * dir
             *
             * We change the user's umask and then restore it, instead of
             * doing chmod's.
             */
            oumask = umask(0);
            numask = oumask & ~(S_IWUSR | S_IXUSR);
            (void) umask(numask);
            first = 0;
        }
        if (last)
            (void) umask(oumask);
        if (mkdir(path, last ? omode : S_IRWXU | S_IRWXG | S_IRWXO) < 0)
        {
            if (errno == EEXIST || errno == EISDIR)
            {
                if (stat(path, &sb) < 0)
                {
                    retval = 1;
                    break;
                }
                else if (!S_ISDIR(sb.st_mode))
                {
                    if (last)
                        errno = EEXIST;
                    else
                        errno = ENOTDIR;
                    retval = 1;
                    break;
                }
            }
            else
            {
                retval = 1;
                break;
            }
        }
        if (!last)
            *p = '/';
    }
    if (!first && !last)
        (void) umask(oumask);
    return retval;
}

/*
 * clean up any files we created on failure
 * if we created the data directory remove it too
 *
 */
static void
exit_nicely(void)
{
    fprintf(stderr, "%s: failed\n", progname);

    if (!noclean)
    {
        if (made_new_pgdata)
        {
            fprintf(stderr, "%s: removing data directory \"%s\"\n",
                    progname, pg_data);
            if (!rmtree(pg_data, true))
                fprintf(stderr, "%s: failed\n", progname);
        }
        else
        {
            fprintf(stderr,
                    "%s: removing contents of data directory \"%s\"\n",
                    progname, pg_data);
            if (!rmtree(pg_data, false))
                fprintf(stderr, "%s: failed\n", progname);
        }
    }
    exit(1);
}

/*
 * find the current user using code lifted from pg_id.c
 * on unix make sure it isn't really root
 *
 */
static char *
get_id(void)
{
#ifndef WIN32

    struct passwd *pw;

    pw = getpwuid(getuid());

#ifndef __BEOS__                /* no root check on BEOS */

    if (!geteuid())                /* 0 is root's uid */
    {
        fprintf(stderr,
                "%s: cannot be run as root\n"
             "Please log in (using, e.g., \"su\") as the (unprivileged) "
                "user that will\n" "own the server process.\n",
                progname);
        exit(1);
    }
#endif

#else                            /* the windows code */

    struct passwd_win32
    {
        int            pw_uid;
        char        pw_name[128];
    }            pass_win32;
    struct passwd_win32 *pw = &pass_win32;
    DWORD        pwname_size = sizeof(pass_win32.pw_name) - 1;

    pw->pw_uid = 1;
    GetUserName(pw->pw_name, &pwname_size);
#endif

    return xstrdup(pw->pw_name);
}

/*
 * get the encoding id for a given encoding name
 *
 */
static char *
get_encoding_id(char *encoding_name)
{
    int            enc;
    char        result[20];

    if (encoding_name && *encoding_name)
    {
        if ((enc = pg_char_to_encoding(encoding_name)) >= 0 &&
            pg_valid_server_encoding(encoding_name) >= 0)
        {
            sprintf(result, "%d", enc);
            return xstrdup(result);
        }
    }
    fprintf(stderr, "%s: \"%s\" is not a valid server encoding name\n",
            progname, encoding_name ? encoding_name : "(null)");
    exit(1);
}

/*
 * get short version of VERSION
 *
 */
static char *
get_short_version(void)
{
    bool        gotdot = false;
    int            end;
    char       *vr;

    vr = xstrdup(PG_VERSION);

    for (end = 0; vr[end] != '\0'; end++)
    {
        if (vr[end] == '.')
        {
            if (end == 0)
                return NULL;
            else if (gotdot)
                break;
            else
                gotdot = true;
        }
        else if (vr[end] < '0' || vr[end] > '9')
        {
            /* gone past digits and dots */
            break;
        }
    }
    if (end == 0 || vr[end - 1] == '.' || !gotdot)
        return NULL;

    vr[end] = '\0';
    return vr;
}

/*
 * make sure the data directory either doesn't exist or is empty
 *
 */
static bool
check_data_dir(void)
{
    DIR           *chkdir;
    struct dirent *file;
    bool        empty = true;

    chkdir = opendir(pg_data);

    if (!chkdir)
        return (errno == ENOENT);

    while ((file = readdir(chkdir)) != NULL)
    {
        if (strcmp(".", file->d_name) == 0 || strcmp("..", file->d_name) == 0)
        {
            /* skip this and parent directory */
            continue;
        }
        else
        {
            empty = false;
            break;
        }
    }

    closedir(chkdir);

    return empty;
}

/*
 * make the data directory (or one of its subdirectories if subdir is not NULL)
 *
 */
static bool
mkdatadir(char *subdir)
{
    char       *path;
    int            res;

    path = xmalloc(strlen(pg_data) + 2 +
                   (subdir == NULL ? 0 : strlen(subdir)));

    if (subdir != NULL)
        sprintf(path, "%s/%s", pg_data, subdir);
    else
        strcpy(path, pg_data);

    res = mkdir(path, 0700);
    if (res == 0)
        return true;
    else if (subdir == NULL || errno != ENOENT)
        return false;
    else
        return !mkdir_p(path, 0700);
}


/*
 * set name of given input file variable under data directory
 *
 */
static void
set_input(char **dest, char *filename)
{
    *dest = xmalloc(strlen(datadir) + strlen(filename) + 2);
    sprintf(*dest, "%s/%s", datadir, filename);
}

/*
 * check that given input file exists
 *
 */
static void
check_input(char *path)
{
    struct stat statbuf;

    if (stat(path, &statbuf) != 0 || !S_ISREG(statbuf.st_mode))
    {
        fprintf(stderr,
                "%s: file \"%s\" not found\n"
           "This means you have a corrupted installation or identified\n"
                "the wrong directory with the invocation option -L.\n",
                progname, path);
        exit(1);
    }

}

/*
 * TODO - clean this up and handle the errors properly
 * don't overkill
 *
 */
#define FIND_SUCCESS 0
#define FIND_NOT_FOUND 1
#define FIND_STAT_ERR 2
#define FIND_NOT_REGFILE 3
#define FIND_BAD_PERM 4
#define FIND_EXEC_ERR 5
#define FIND_WRONG_VERSION 6

/*
 * see if there is a postgres executable in the given path, and giving the
 * right version number
 *
 */
static int
find_postgres(char *path)
{
    char        fn[MAXPGPATH];
    char        cmd[MAXPGPATH];
    char        line[100];

#ifndef WIN32
    int            permmask = S_IROTH | S_IXOTH;
#endif

    struct stat statbuf;
    FILE       *pgver;
    int            plen = strlen(path);

    if (path[plen - 1] != '/')
        snprintf(fn, MAXPGPATH, "%s/postgres%s", path, EXE);
    else
        snprintf(fn, MAXPGPATH, "%spostgres%s", path, EXE);

    if (stat(fn, &statbuf) != 0)
    {
        if (errno == ENOENT)
            return FIND_NOT_FOUND;
        else
            return FIND_STAT_ERR;
    }
    if (!S_ISREG(statbuf.st_mode))
        return FIND_NOT_REGFILE;

#ifndef WIN32
    /*
     * Only unix requires this test, on WIN32 an .exe file should be
     * executable
     */
    if ((statbuf.st_mode & permmask) != permmask)
        return FIND_BAD_PERM;
#endif

    snprintf(cmd, MAXPGPATH, "\"%s/postgres\" -V 2>%s", path, DEVNULL);

    if ((pgver = popen(cmd, "r")) == NULL)
        return FIND_EXEC_ERR;

    if (fgets(line, sizeof(line), pgver) == NULL)
        perror("fgets failure");

    pclose(pgver);

    if (strcmp(line, PG_VERSIONSTR) != 0)
        return FIND_WRONG_VERSION;

    return FIND_SUCCESS;
}

/*
 * Windows doesn't like relative paths to executables (other things work fine)
 * so we call its builtin function to expand them. Elsewhere this is a NOOP
 *
 */
#ifdef WIN32
static char *
expanded_path(char *path)
{
    char        abspath[MAXPGPATH];

    if (_fullpath(abspath, path, MAXPGPATH) == NULL)
    {
        perror("expanded path");
        return path;
    }
    canonicalise_path(abspath);
    return xstrdup(abspath);
}
#endif

/*
 * set the paths pointing to postgres
 * look for it in the same place we found this program, or in the environment
 * path, or in the configured bindir.
 *
 */
static int
set_paths(void)
{
    if (testpath && !self_path)
    {

        char       *path,
                   *cursor;
        int            pathlen,
                    i,
                    pathsegs;
        char      **pathbits;
        char        buf[MAXPGPATH];
        struct stat statbuf;

        path = xstrdup(getenv("PATH"));
        pathlen = strlen(path);

        for (i = 0, pathsegs = 1; i < pathlen; i++)
        {
            if (path[i] == PATHSEP)
                pathsegs++;
        }

        pathbits = (char **) xmalloc(pathsegs * sizeof(char *));
        for (i = 0, pathsegs = 0, cursor = path; i <= pathlen; i++)
        {
            if (path[i] == PATHSEP || path[i] == 0)
            {
                path[i] = 0;
                if (strlen(cursor) == 0)
                {
                    /* empty path segment means current directory */
                    pathbits[pathsegs] = xstrdup(".");
                }
                else
                {
                    canonicalise_path(cursor);
                    pathbits[pathsegs] = cursor;
                }
                pathsegs++;
                cursor = path + i + 1;
            }
        }

        for (i = 0; i < pathsegs; i++)
        {
            snprintf(buf, MAXPGPATH, "%s/%s%s", pathbits[i], progname, EXE);
            if (stat(buf, &statbuf) == 0 && S_ISREG(statbuf.st_mode))
            {
                self_path = pathbits[i];
                break;
            }
        }
    }

    if (testpath && self_path &&
        (find_postgres(expanded_path(self_path)) == 0))
    {
        /* we found postgres on out own path */
        pgpath = expanded_path(self_path);
    }
    else
    {
        /* look in the hardcoded bindir */
        int            res;
        char       *cbindir;

        cbindir = xstrdup(bindir);
        canonicalise_path(cbindir);
        res = find_postgres(expanded_path(cbindir));
        if (res == 0)
            pgpath = expanded_path(cbindir);
        else
            return 1;
    }

    return 0;
}

/*
 * write out the PG_VERSION file in the data dir, or its subdirectory
 * if extrapath is not NULL
 *
 */
static void
set_short_version(char *short_version, char *extrapath)
{
    FILE       *version_file;
    char       *path;

    if (extrapath == NULL)
    {
        path = xmalloc(strlen(pg_data) + 12);
        sprintf(path, "%s/PG_VERSION", pg_data);
    }
    else
    {
        path = xmalloc(strlen(pg_data) + strlen(extrapath) + 13);
        sprintf(path, "%s/%s/PG_VERSION", pg_data, extrapath);
    }
    version_file = fopen(path, PG_BINARY_W);
    fprintf(version_file, "%s\n", short_version);
    fclose(version_file);
}

/*
 * set up an empty config file so we can check buffers and connections
 *
 */
static void
set_null_conf(void)
{
    FILE       *conf_file;
    char       *path;

    path = xmalloc(strlen(pg_data) + 17);
    sprintf(path, "%s/postgresql.conf", pg_data);
    conf_file = fopen(path, PG_BINARY_W);
    fclose(conf_file);
}

/*
 * check how many connections we can sustain
 *
 */
static void
test_connections(void)
{
    char       *format =
    "\"%s/postgres\" -boot -x 0 -F "
    "-c shared_buffers=%d -c max_connections=%d template1 <%s >%s 2>&1";
    char        cmd[MAXPGPATH];
    int            conns[] = {100, 50, 40, 30, 20, 10};
    int            len = sizeof(conns) / sizeof(int);
    int            i,
                status;

    for (i = 0; i < len; i++)
    {
        snprintf(cmd, sizeof(cmd), format,
                 pgpath, conns[i] * 5, conns[i], DEVNULL, DEVNULL);
        status = system(cmd);
        if (status == 0)
            break;
    }
    if (i >= len)
        i = len - 1;
    n_connections = conns[i];
    printf("connections set to %d\n", n_connections);
}

/*
 * check how many buffers we can run with
 *
 */
static void
test_buffers(void)
{
    char       *format =
    "\"%s/postgres\"  -boot -x 0 -F "
    "-c shared_buffers=%d -c max_connections=%d template1 <%s >%s 2>&1";
    char        cmd[MAXPGPATH];
    int            bufs[] = {1000, 900, 800, 700, 600, 500, 400, 300, 200, 100, 50};
    int            len = sizeof(bufs) / sizeof(int);
    int            i,
                status;

    for (i = 0; i < len; i++)
    {
        snprintf(cmd, sizeof(cmd), format, pgpath, bufs[i], n_connections,
            DEVNULL, DEVNULL);
        status = system(cmd);
        if (status == 0)
            break;
    }
    if (i >= len)
        i = len - 1;
    n_buffers = bufs[i];
    printf("buffers set to %d\n", n_buffers);
}

/*
 * set up all the config files
 *
 */
static void
setup_config(void)
{

    char      **conflines;
    char        repltok[100];
    char        path[MAXPGPATH];

    fputs("creating configuration files ... ", stdout);

    /* postgresql.conf */

    conflines = readfile(conf_file);

    snprintf(repltok, sizeof(repltok), "max_connections = %d", n_connections);
    conflines = replace_token(conflines, "#max_connections = 100", repltok);

    snprintf(repltok, sizeof(repltok), "shared_buffers = %d", n_buffers);
    conflines = replace_token(conflines, "#shared_buffers = 1000", repltok);

    snprintf(repltok, sizeof(repltok), "lc_messages = '%s'", lc_messages);
    conflines = replace_token(conflines, "#lc_messages = 'C'", repltok);

    snprintf(repltok, sizeof(repltok), "lc_monetary = '%s'", lc_monetary);
    conflines = replace_token(conflines, "#lc_monetary = 'C'", repltok);

    snprintf(repltok, sizeof(repltok), "lc_numeric = '%s'", lc_numeric);

    conflines = replace_token(conflines, "#lc_numeric = 'C'", repltok);

    snprintf(repltok, sizeof(repltok), "lc_time = '%s'", lc_time);
    conflines = replace_token(conflines, "#lc_time = 'C'", repltok);

    snprintf(path, MAXPGPATH, "%s/postgresql.conf", pg_data);

    writefile(path, conflines);
    chmod(path, 0600);

    free(conflines);


    /* pg_hba.conf */

    conflines = readfile(hba_file);

#ifndef HAVE_IPV6
    conflines = replace_token(conflines,
                              "host    all         all         ::1",
                              "#host    all         all         ::1");
#endif

    snprintf(path, MAXPGPATH, "%s/pg_hba.conf", pg_data);

    writefile(path, conflines);
    chmod(path, 0600);

    free(conflines);

    /* pg_ident.conf */

    conflines = readfile(ident_file);

    snprintf(path, MAXPGPATH, "%s/pg_ident.conf", pg_data);

    writefile(path, conflines);
    chmod(path, 0600);

    free(conflines);

    check_ok();
}


/*
 * run the bootstrap code
 *
 */
static void
bootstrap_template1(char *short_version)
{
    char       *talkargs = "";
    char      **bki_lines;
    char        headerline[MAXPGPATH];

    PG_CMD_DECL;

    printf("creating template1 database in %s/base/1 ... ", pg_data);

    if (debug)
        talkargs = "-d 5";

    bki_lines = readfile(bki_file);

    snprintf(headerline, MAXPGPATH, "# PostgreSQL %s\n", short_version);

    if (strcmp(headerline, *bki_lines) != 0)
    {
        fprintf(stderr,
                "%s: input file \"%s\" does not belong to PostgreSQL %s\n"
                "Check your installation or specify the correct path "
                "using the option -L.\n",
                progname, bki_file, PG_VERSION);
        exit_nicely();

    }

    bki_lines = replace_token(bki_lines, "POSTGRES", effective_user);

    bki_lines = replace_token(bki_lines, "ENCODING", encodingid);

    /*
     * we could save the old environment here, and restore it afterwards,
     * but there doesn't seem to be any point, especially as we have
     * already called setlocale().
     *
     */
    snprintf(cmd, MAXPGPATH, "LC_COLLATE=%s", lc_collate);
    putenv(xstrdup(cmd));

    snprintf(cmd, MAXPGPATH, "LC_CTYPE=%s", lc_ctype);
    putenv(xstrdup(cmd));

    putenv("LC_ALL");

    snprintf(cmd, MAXPGPATH,
        " \"%s/postgres\"  -boot -x1 -F %s template1", pgpath, talkargs);

    PG_CMD_OPEN;

    for (line = bki_lines; *line != NULL; line++)
    {
        PG_CMD_PUTLINE;
        free(*line);
    }

    PG_CMD_CLOSE;

    free(bki_lines);

    check_ok();
}

/*
 * set up the shadow password table
 *
 */
static void
setup_shadow(void)
{

    char       *pg_shadow_setup[] = {
        /*
         * Create a trigger so that direct updates to pg_shadow will be
         * written to the flat password/group files pg_pwd and pg_group
         */
        "CREATE TRIGGER pg_sync_pg_pwd "
        "  AFTER INSERT OR UPDATE OR DELETE ON pg_shadow "
        "  FOR EACH ROW EXECUTE PROCEDURE update_pg_pwd_and_pg_group();\n",
        "CREATE TRIGGER pg_sync_pg_group "
        "  AFTER INSERT OR UPDATE OR DELETE ON pg_group "
        "  FOR EACH ROW EXECUTE PROCEDURE update_pg_pwd_and_pg_group();\n",

        /*
         * needs to be done before alter user, because alter user checks
         * that pg_shadow is secure ...
         */
        "REVOKE ALL on pg_shadow FROM public;\n",
        NULL
    };

    PG_CMD_DECL;

    fputs("initializing pg_shadow ... ", stdout);

    snprintf(cmd, MAXPGPATH,
             "\"%s/postgres\" -F -O -c search_path=pg_catalog "
             "-c exit_on_error=true template1 >%s",
             pgpath, DEVNULL);

    PG_CMD_OPEN;

    for (line = pg_shadow_setup; *line != NULL; line++)
        PG_CMD_PUTLINE;

    PG_CMD_CLOSE;

    check_ok();
}

/*
 * get the superuser password if required, and call postgres to set it
 *
 */
static void
get_set_pwd(void)
{
    PG_CMD_DECL_NOLINE;

    char       *pwd1,
               *pwd2;
    char        pwdpath[MAXPGPATH];
    struct stat statbuf;

    pwd1 = simple_prompt("Enter new superuser password: ", 100, false);
    pwd2 = simple_prompt("Enter it again: ", 100, false);
    if (strcmp(pwd1, pwd2) != 0)
    {
        fprintf(stderr, "Passwords didn't match.\n");
        exit_nicely();
    }
    free(pwd2);

    printf("storing the password ... ");

    snprintf(cmd, MAXPGPATH,
             "\"%s/postgres\" -F -O -c search_path=pg_catalog "
             "-c exit_on_error=true template1 >%s", pgpath, DEVNULL);


    PG_CMD_OPEN;

    if (fprintf(
       pg, "ALTER USER \"%s\" WITH PASSWORD '%s';\n", username, pwd1) < 0)
    {
        /* write failure */
        exit_nicely();
    }
    fflush(pg);

    PG_CMD_CLOSE;

    snprintf(pwdpath, MAXPGPATH, "%s/global/pg_pwd", pg_data);
    if (stat(pwdpath, &statbuf) != 0 || !S_ISREG(statbuf.st_mode))
    {
        fprintf(stderr,
                "%s: The password file was not generated - "
                "please report this problem\n",
                progname);
        exit_nicely();
    }

    check_ok();
}

/*
 * toast sys tables
 *
 */
static void
unlimit_systables(void)
{
    char       *systables_setup[] = {
        "ALTER TABLE pg_attrdef CREATE TOAST TABLE;\n",
        "ALTER TABLE pg_constraint CREATE TOAST TABLE;\n",
        "ALTER TABLE pg_database CREATE TOAST TABLE;\n",
        "ALTER TABLE pg_description CREATE TOAST TABLE;\n",
        "ALTER TABLE pg_group CREATE TOAST TABLE;\n",
        "ALTER TABLE pg_proc CREATE TOAST TABLE;\n",
        "ALTER TABLE pg_rewrite CREATE TOAST TABLE;\n",
        "ALTER TABLE pg_shadow CREATE TOAST TABLE;\n",
        "ALTER TABLE pg_statistic CREATE TOAST TABLE;\n",
        NULL
    };

    PG_CMD_DECL;

    fputs("enabling unlimited row size for system tables ... ", stdout);

    snprintf(cmd, MAXPGPATH,
             "\"%s/postgres\" -F -O -c search_path=pg_catalog "
             "-c exit_on_error=true template1 >%s", pgpath, DEVNULL);

    PG_CMD_OPEN;

    for (line = systables_setup; *line != NULL; line++)
        PG_CMD_PUTLINE;

    PG_CMD_CLOSE;

    check_ok();
}

/*
 * set up pg_depend
 *
 */
static void
setup_depend(void)
{
    char       *pg_depend_setup[] = {
        /*
         * Make PIN entries in pg_depend for all objects made so far in
         * the tables that the dependency code handles.  This is overkill
         * (the system doesn't really depend on having every last weird
         * datatype, for instance) but generating only the minimum
         * required set of dependencies seems hard. Note that we
         * deliberately do not pin the system views. First delete any
         * already-made entries; PINs override all else, and must be the
         * only entries for their objects.
         */
        "DELETE FROM pg_depend;\n",
        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
        " FROM pg_class;\n",
        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
        " FROM pg_proc;\n",
        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
        " FROM pg_type;\n",
        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
        " FROM pg_cast;\n",
        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
        " FROM pg_constraint;\n",
        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
        " FROM pg_attrdef;\n",
        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
        " FROM pg_language;\n",
        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
        " FROM pg_operator;\n",
        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
        " FROM pg_opclass;\n",
        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
        " FROM pg_rewrite;\n",
        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
        " FROM pg_trigger;\n",

        /*
         * restriction here to avoid pinning the public namespace
         */
        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
        " FROM pg_namespace "
        "    WHERE nspname LIKE 'pg%';\n",
        NULL
    };

    PG_CMD_DECL;

    fputs("initializing pg_depend ... ", stdout);

    snprintf(cmd, MAXPGPATH,
             "\"%s/postgres\" -F -O -c search_path=pg_catalog "
             "-c exit_on_error=true template1 >%s",
             pgpath, DEVNULL);

    PG_CMD_OPEN;

    for (line = pg_depend_setup; *line != NULL; line++)
        PG_CMD_PUTLINE;

    PG_CMD_CLOSE;

    check_ok();
}

/*
 * set up system views
 *
 */
static void
setup_sysviews(void)
{
    PG_CMD_DECL;

    char      **sysviews_setup;

    fputs("creating system views ... ", stdout);

    sysviews_setup = readfile(system_views_file);

    snprintf(cmd, MAXPGPATH,
             "\"%s/postgres\" -F -N -O -c search_path=pg_catalog "
             "-c exit_on_error=true template1 >%s",
             pgpath, DEVNULL);

    PG_CMD_OPEN;

    for (line = sysviews_setup; *line != NULL; line++)
    {
        PG_CMD_PUTLINE;
        free(*line);
    }

    PG_CMD_CLOSE;

    free(sysviews_setup);

    check_ok();
}

/*
 * load description data
 *
 */
static void
setup_description(void)
{
    char       *pg_description_setup1[] = {
        "CREATE TEMP TABLE tmp_pg_description ( "
        "    objoid oid, "
        "    classname name, "
        "    objsubid int4, "
        "    description text) WITHOUT OIDS;\n",
        "COPY tmp_pg_description FROM STDIN;\n",
        NULL
    };

    char       *pg_description_setup2[] = {
        "\\.\n",
        "INSERT INTO pg_description "
        " SELECT t.objoid, c.oid, t.objsubid, t.description "
        "  FROM tmp_pg_description t, pg_class c "
        "    WHERE c.relname = t.classname;\n",
        NULL
    };


    PG_CMD_DECL;

    char      **desc_lines;

    fputs("loading pg_description ... ", stdout);

    snprintf(cmd, MAXPGPATH,
             "\"%s/postgres\" -F -O -c search_path=pg_catalog "
             "-c exit_on_error=true template1 >%s",
             pgpath, DEVNULL);

    PG_CMD_OPEN;

    for (line = pg_description_setup1; *line != NULL; line++)
        PG_CMD_PUTLINE;

    desc_lines = readfile(desc_file);
    for (line = desc_lines; *line != NULL; line++)
    {
        PG_CMD_PUTLINE;
        free(*line);
    }

    free(desc_lines);


    for (line = pg_description_setup2; *line != NULL; line++)
        PG_CMD_PUTLINE;

    PG_CMD_CLOSE;

    check_ok();
}

/*
 * load conversion functions
 *
 */
static void
setup_conversion(void)
{
    PG_CMD_DECL;

    char      **conv_lines;

    fputs("creating conversions ... ", stdout);

    snprintf(cmd, MAXPGPATH,
             "\"%s/postgres\" -F -O -c search_path=pg_catalog "
             "-c exit_on_error=true template1 >%s",
             pgpath, DEVNULL);

    PG_CMD_OPEN;

    conv_lines = readfile(conversion_file);
    for (line = conv_lines; *line != NULL; line++)
    {
        if (strstr(*line, "DROP CONVERSION") != *line)
            PG_CMD_PUTLINE;

        free(*line);
    }

    free(conv_lines);

    PG_CMD_CLOSE;

    check_ok();
}

/*
 * run privileges script
 *
 */
static void
setup_privileges(void)
{
    char       *privileges_setup[] = {
        "UPDATE pg_class "
        "  SET relacl = '{\"=r/\\\\\"$POSTGRES_SUPERUSERNAME\\\\\"\"}' "
        "  WHERE relkind IN ('r', 'v', 'S') AND relacl IS NULL;\n",
        "UPDATE pg_proc "
        "  SET proacl = '{\"=X/\\\\\"$POSTGRES_SUPERUSERNAME\\\\\"\"}' "
        "  WHERE proacl IS NULL;\n",
        "UPDATE pg_language "
        "  SET lanacl = '{\"=U/\\\\\"$POSTGRES_SUPERUSERNAME\\\\\"\"}' "
        "  WHERE lanpltrusted;\n",
        "GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n",
        "GRANT CREATE, USAGE ON SCHEMA public TO PUBLIC;\n",
        NULL
    };

    PG_CMD_DECL;

    char      **priv_lines;

    fputs("setting privileges on builtin objects ... ", stdout);

    snprintf(cmd, MAXPGPATH,
             "\"%s/postgres\" -F -O -c search_path=pg_catalog "
             "-c exit_on_error=true template1 >%s",
             pgpath, DEVNULL);

    PG_CMD_OPEN;

    priv_lines = replace_token(privileges_setup,
                               "$POSTGRES_SUPERUSERNAME", username);
    for (line = priv_lines; *line != NULL; line++)
        PG_CMD_PUTLINE;

    PG_CMD_CLOSE;

    check_ok();
}

/*
 * extract the strange version of version required for info schema
 *
 */
static void
set_info_version(void)
{
    char       *letterversion;
    long        major = 0,
                minor = 0,
                micro = 0;
    char       *endptr;
    char       *vstr = xstrdup(PG_VERSION);
    char       *ptr;

    ptr = vstr + (strlen(vstr) - 1);
    while (ptr != vstr && (*ptr < '0' || *ptr > '9'))
        ptr--;
    letterversion = ptr + 1;
    major = strtol(vstr, &endptr, 10);
    if (*endptr)
        minor = strtol(endptr + 1, &endptr, 10);
    if (*endptr)
        micro = strtol(endptr + 1, &endptr, 10);
    snprintf(infoversion, sizeof(infoversion), "%02ld.%02ld.%04ld%s",
             major, minor, micro, letterversion);
}

/*
 * load info schema and populate from features file
 *
 */
static void
setup_schema(void)
{

    PG_CMD_DECL;

    char      **lines;
    int            fres;

    fputs("creating information schema ... ", stdout);

    lines = readfile(info_schema_file);

    /*
     * note that here we don't run in single line mode, unlike other
     * places
     */
    snprintf(cmd, MAXPGPATH,
             "\"%s/postgres\" -F -O -c search_path=pg_catalog "
             "-c exit_on_error=true -N template1 >%s",
             pgpath, DEVNULL);

    PG_CMD_OPEN;

    for (line = lines; *line != NULL; line++)
    {
        PG_CMD_PUTLINE;
        free(*line);
    }

    free(lines);

    PG_CMD_CLOSE;

    lines = readfile(features_file);

    /*
     * strip CR before NL this is the only place we do this  (following
     * the shell script) - we could do it universally in readfile() if
     * necessary
     *
     */
    lines = replace_token(lines, "\r\n", "\n");

    snprintf(cmd, MAXPGPATH,
             "\"%s/postgres\" -F -O -c search_path=pg_catalog "
             "-c exit_on_error=true template1 >%s",
             pgpath, DEVNULL);

    PG_CMD_OPEN;

    fres = fprintf(pg,
                   "UPDATE information_schema.sql_implementation_info "
                   "  SET character_value = '%s' "
                   "  WHERE implementation_info_name = 'DBMS VERSION';\n",
                   infoversion);

    if (fres < 0)
        exit_nicely();

    fres = fputs("COPY information_schema.sql_features "
                 "  (feature_id, feature_name, sub_feature_id, "
                 "  sub_feature_name, is_supported, comments) "
                 "FROM STDIN;\n",
                 pg);

    if (fres < 0)
        exit_nicely();

    for (line = lines; *line != NULL; line++)
    {
        PG_CMD_PUTLINE;
        free(*line);
    }

    free(lines);

    if (fputs("\\.\n", pg) < 0)
        exit_nicely();
    fflush(pg);

    PG_CMD_CLOSE;

    check_ok();
}

/*
 * clean everything up in template1
 *
 */
static void
vacuum_db(void)
{
    PG_CMD_DECL_NOLINE;

    fputs("vacuuming database template1 ... ", stdout);

    snprintf(cmd, MAXPGPATH,
             "\"%s/postgres\" -F -O -c search_path=pg_catalog "
             "-c exit_on_error=true template1 >%s",
             pgpath, DEVNULL);

    PG_CMD_OPEN;

    if (fputs("ANALYSE;\nVACUUM FULL FREEZE;\n", pg) < 0)
        exit_nicely();
    fflush(pg);

    PG_CMD_CLOSE;

    check_ok();

}

/*
 * copy template1 to template0
 *
 */
static void
make_template0(void)
{
    char       *template0_setup[] = {
        "CREATE DATABASE template0;\n",
        "UPDATE pg_database SET "
        "    datistemplate = 't', "
        "    datallowconn = 'f' "
        "    WHERE datname = 'template0';\n",

        /*
         * We use the OID of template0 to determine lastsysoid
         *
         */
        "UPDATE pg_database SET datlastsysoid = "
        "    (SELECT oid::int4 - 1 FROM pg_database "
        "    WHERE datname = 'template0');\n",

        /*
         * Explicitly revoke public create-schema and create-temp-table
         * privileges in template1 and template0; else the latter would be
         * on by default
         */
        "REVOKE CREATE,TEMPORARY ON DATABASE template1 FROM public;\n",
        "REVOKE CREATE,TEMPORARY ON DATABASE template0 FROM public;\n",

        /*
         * Finally vacuum to clean up dead rows in pg_database
         */
        "VACUUM FULL pg_database;\n",
        NULL
    };

    PG_CMD_DECL;

    fputs("copying template1 to template0 ... ", stdout);

    snprintf(cmd, MAXPGPATH,
             "\"%s/postgres\" -F -O -c search_path=pg_catalog "
             "-c exit_on_error=true template1 >%s",
             pgpath, DEVNULL);

    PG_CMD_OPEN;

    for (line = template0_setup; *line; line++)
        PG_CMD_PUTLINE;

    PG_CMD_CLOSE;

    check_ok();
}


/*
 * signal handler in case we are interrupted.
 *
 * The Windows runtime docs at
 * http://msdn.microsoft.com/library/en-us/vclib/html/_crt_signal.asp
 * specifically forbid a number of things being done from a signal handler,
 * including IO, memory allocation and system calls, and only allow jmpbuf
 * if you are handling SIGFPE.
 *
 * I avoided doing the forbidden things by setting a flag instead of calling
 * exit_nicely() directly.
 *
 * Also note the behaviour of Windows with SIGINT, which says this:
 *     Note    SIGINT is not supported for any Win32 application, including
 *     Windows 98/Me and Windows NT/2000/XP. When a CTRL+C interrupt occurs,
 *     Win32 operating systems generate a new thread to specifically handle
 *     that interrupt. This can cause a single-thread application such as UNIX,
 *     to become multithreaded, resulting in unexpected behavior.
 *
 * I have no idea how to handle this. (Strange they call UNIX an application!)
 * So this will need some testing on Windows.
 *
 */
static void
trapsig(int signum)
{
    /* handle systems that reset the handler, like Windows (grr) */
    pqsignal(signum, trapsig);
    not_ok = true;
}

/*
 * call exit_nicely() if we got a signal, or else output "ok".
 *
 */
static void
check_ok()
{
    if (not_ok)
    {
        puts("Caught Signal.");
        exit_nicely();
    }
    else
    {
        /* no signal caught */
        puts("ok");
    }
}


/*
 * check if given string is a valid locle specifier
 * based on some code given to me by Peter Eisentraut
 * (but I take responsibility for it :-)
 */
static bool
chklocale(const char *locale)
{
    bool        ret;
    int            category = LC_CTYPE;
    char       *save;

    save = setlocale(category, NULL);
    if (!save)
        return false;            /* should not happen; */

    save = xstrdup(save);

    ret = (setlocale(category, locale) != NULL);

    setlocale(category, save);
    free(save);

    /* should we exit here? */
    if (!ret)
        fprintf(stderr, "%s: invalid locale name \"%s\"\n", progname, locale);

    return ret;
}

/*
 * set up the locale variables
 * assumes we have called setlocale(LC_ALL,"")
 *
 */
static void
setlocales(void)
{
    /* set empty lc_* values to locale config if set */

    if (strlen(locale) > 0)
    {
        if (strlen(lc_ctype) == 0)
            lc_ctype = locale;
        if (strlen(lc_collate) == 0)
            lc_collate = locale;
        if (strlen(lc_numeric) == 0)
            lc_numeric = locale;
        if (strlen(lc_time) == 0)
            lc_time = locale;
        if (strlen(lc_monetary) == 0)
            lc_monetary = locale;
        if (strlen(lc_messages) == 0)
            lc_messages = locale;
    }

    /*
     * override absent/invalid config settings from initdb's locale
     * settings
     */

    if (strlen(lc_ctype) == 0 || !chklocale(lc_ctype))
        lc_ctype = xstrdup(setlocale(LC_CTYPE, NULL));
    if (strlen(lc_collate) == 0 || !chklocale(lc_collate))
        lc_collate = xstrdup(setlocale(LC_COLLATE, NULL));
    if (strlen(lc_numeric) == 0 || !chklocale(lc_numeric))
        lc_numeric = xstrdup(setlocale(LC_NUMERIC, NULL));
    if (strlen(lc_time) == 0 || !chklocale(lc_time))
        lc_time = xstrdup(setlocale(LC_TIME, NULL));
    if (strlen(lc_monetary) == 0 || !chklocale(lc_monetary))
        lc_monetary = xstrdup(setlocale(LC_MONETARY, NULL));
    if (strlen(lc_messages) == 0 || !chklocale(lc_messages))
#ifdef LC_MESSAGES
    {
        /* when available get the current locale setting */
        lc_messages = xstrdup(setlocale(LC_MESSAGES, NULL));
    }
#else
    {
        /* when not available, get the CTYPE setting */
        lc_messages = xstrdup(setlocale(LC_CTYPE, NULL));
    }
#endif

}

/*
 * help text data
 *
 */
char       *usage_text[] = {
    "$CMDNAME initializes a PostgreSQL database cluster.\n",
    "\n",
    "Usage:\n",
    "  $CMDNAME [OPTION]... [DATADIR]\n",
    "\n",
    "Options:\n",
    " [-D, --pgdata=]DATADIR     location for this database cluster\n",
    "  -E, --encoding=ENCODING   set default encoding for new databases\n",
    "  --locale=LOCALE           initialize database cluster with given locale\n",
    "  --lc-collate, --lc-ctype, --lc-messages=LOCALE\n",
    "  --lc-monetary, --lc-numeric, --lc-time=LOCALE\n",
    "                            initialize database cluster with given locale\n",
    "                            in the respective category (default taken from\n",
    "                            environment)\n",
    "  --no-locale               equivalent to --locale=C\n",
    "  -U, --username=NAME       database superuser name\n",
    "  -W, --pwprompt            prompt for a password for the new superuser\n",
    "  -?, --help                show this help, then exit\n",
    "  -V, --version             output version information, then exit\n",
    "\n",
    "Less commonly used options: \n",
    "  -d, --debug               generate lots of debugging output\n",
    "  -s, --show                show internal settings\n",
    "  -L DIRECTORY              where to find the input files\n",
    "  -n, --noclean             do not clean up after errors\n",
    "\n",
    "If the data directory is not specified, the environment variable PGDATA\n",
    "is used.\n",
    "\n",
    "Report bugs to <pgsql-bugs@postgresql.org>.\n",
    NULL
};



/*
 * print help text
 *
 */
static void
usage(void)
{

    int            i;
    char      **newtext;

    newtext = replace_token(usage_text, "$CMDNAME", progname);

    for (i = 0; newtext[i]; i++)
        fputs(newtext[i], stdout);        /* faster than printf */
}

int
main(int argc, char *argv[])
{
    /*
     * options with no short version return a low integer, the rest return
     * their short version value
     *
     */
    static struct option long_options[] = {
        {"pgdata", required_argument, NULL, 'D'},
        {"encoding", required_argument, NULL, 'E'},
        {"locale", required_argument, NULL, 1},
        {"lc-collate", required_argument, NULL, 2},
        {"lc-ctype", required_argument, NULL, 3},
        {"lc-monetary", required_argument, NULL, 4},
        {"lc-numeric", required_argument, NULL, 5},
        {"lc-time", required_argument, NULL, 6},
        {"lc-messages", required_argument, NULL, 7},
        {"no-locale", no_argument, NULL, 8},
        {"pwprompt", no_argument, NULL, 'W'},
        {"username", required_argument, NULL, 'U'},
        {"help", no_argument, NULL, '?'},
        {"version", no_argument, NULL, 'V'},
        {"debug", no_argument, NULL, 'd'},
        {"show", no_argument, NULL, 's'},
        {"noclean", no_argument, NULL, 'n'},
        {0, 0, 0, 0}
    };

    int            c,
                i;
    int            option_index;
    char       *short_version;
    char       *pgdenv;            /* PGDATA value got from sent to
                                 * environment */
    char       *subdirs[] =
    {"global", "pg_xlog", "pg_clog", "base", "base/1"};

    char       *lastsep;

    /* parse argv[0] - detect explicit path if there was one */

    char       *carg0;

#if defined(__CYGWIN__) || defined(WIN32)
    char       *exe;            /* location of exe suffix in progname */
#endif

    setlocale(LC_ALL, "");

    carg0 = xstrdup(argv[0]);
    canonicalise_path(carg0);

    lastsep = strrchr(carg0, '/');
    progname = lastsep ? xstrdup(lastsep + 1) : carg0;

#if defined(__CYGWIN__) || defined(WIN32)
    if (strlen(progname) > 4 &&
        (exe = progname + (strlen(progname) - 4)) &&
        stricmp(exe, EXE) == 0)
    {
        /* strip .exe suffix, regardless of case */
        *exe = '\0';
    }
#endif

    if (lastsep)
    {
        self_path = carg0;
        *lastsep = '\0';
    }
    else
    {
        /* no path known to ourselves from argv[0] */
        self_path = NULL;
    }

    /* process options */

    while (1)
    {
        /*
         * a : as the first option char here lets us use ? as a short
         * option
         */
        c = getopt_long(argc, argv, ":D:E:WU:?sVdnL:",
                        long_options, &option_index);

        if (c == -1)
            break;

        switch (c)
        {
            case 'D':
                pg_data = xstrdup(optarg);
                break;
            case 'E':
                encoding = xstrdup(optarg);
                break;
            case 'W':
                pwprompt = true;
                break;
            case 'U':
                username = xstrdup(optarg);
                break;
            case 'd':
                debug = true;
                break;
            case 'n':
                noclean = true;
                break;
            case 'L':
                datadir = xstrdup(optarg);
                break;
            case 1:
                locale = xstrdup(optarg);
                break;
            case 2:
                lc_collate = xstrdup(optarg);
                break;
            case 3:
                lc_ctype = xstrdup(optarg);
                break;
            case 4:
                lc_monetary = xstrdup(optarg);
                break;
            case 5:
                lc_numeric = xstrdup(optarg);
                break;
            case 6:
                lc_time = xstrdup(optarg);
                break;
            case 7:
                lc_messages = xstrdup(optarg);
                break;
            case 8:
                locale = "C";
                break;
            case '?':
                show_help = true;
                break;
            case 's':
                show_setting = true;
                break;
            case 'V':
                show_version = true;
                break;
            default:
                show_help = true;
                printf("Unrecognized option: %c\n", c);
        }

    }

    if (optind < argc)
    {
        pg_data = xstrdup(argv[optind]);
        optind++;
    }

    set_info_version();

    if (strlen(pg_data) == 0 && !(show_help || show_setting))
    {
        pgdenv = getenv("PGDATA");
        if (pgdenv && strlen(pgdenv))
        {
            /* PGDATA found */
            pg_data = xstrdup(pgdenv);
        }
        else
        {
            fprintf(stderr,
                    "%s: no data directory specified\n"
               "You must identify the directory where the data for this "
                    "database system\n"
               "will reside.  Do this with either the invocation option "
                    "-D or the\n" "environment variable PGDATA.\n",
                    progname);
        }
    }

    canonicalise_path(pg_data);

    /*
     * we have to set PGDATA for postgres rather than pass it on the
     * commnd line to avoid dumb quoting problems on Windows, and we would
     * expecially need quotes otherwise on Windows because paths there are
     * most likely to have embedded spaces.
     *
     */
    pgdenv = xmalloc(8 + strlen(pg_data));
    sprintf(pgdenv, "PGDATA=%s", pg_data);
    putenv(pgdenv);

    if (optind < argc)
        show_help = true;

    if (show_version)
    {
        /* hard coded name here, in case they rename executable */
        printf("initdb (PostgreSQL) %s\n", PG_VERSION);
        exit(0);
    }

    if (show_help)
    {
        usage();
        exit(0);
    }

    if (set_paths() != 0)
    {
        fprintf(stderr,
                "The program \"postgres\" is needed by %s "
                "but was not found in \n"
                "the directory \"%s\". Check your installation.\n",
                progname, bindir);
        exit(1);

    }


    if ((short_version = get_short_version()) == NULL)
    {
        fprintf(stderr, "%s: could not get valid short version\n", progname);
        exit(1);
    }

    effective_user = get_id();
    if (!strlen(username))
        username = effective_user;

    if (strlen(encoding))
        encodingid = get_encoding_id(encoding);

    set_input(&bki_file, "postgres.bki");
    set_input(&desc_file, "postgres.description");
    set_input(&hba_file, "pg_hba.conf.sample");
    set_input(&ident_file, "pg_ident.conf.sample");
    set_input(&conf_file, "postgresql.conf.sample");
    set_input(&conversion_file, "conversion_create.sql");
    set_input(&info_schema_file, "information_schema.sql");
    set_input(&features_file, "sql_features.txt");
    set_input(&system_views_file, "system_views.sql");

    if (show_setting || debug)
    {
        fprintf(stderr,
                "VERSION=%s\n"
                "PGDATA=%s\ndatadir=%s\nPGPATH=%s\n"
                "ENCODING=%s\nENCODINGID=%s\n"
                "POSTGRES_SUPERUSERNAME=%s\nPOSTGRES_BKI=%s\n"
                "POSTGRES_DESCR=%s\nPOSTGRESQL_CONF_SAMPLE=%s\n"
                "PG_HBA_SAMPLE=%s\nPG_IDENT_SAMPLE=%s\n",
                PG_VERSION,
                pg_data, datadir, pgpath,
                encoding, encodingid,
                username, bki_file,
                desc_file, conf_file,
                hba_file, ident_file);
    }

    if (show_setting)
        exit(0);

    check_input(bki_file);
    check_input(desc_file);
    check_input(hba_file);
    check_input(ident_file);
    check_input(conf_file);
    check_input(conversion_file);
    check_input(info_schema_file);
    check_input(features_file);
    check_input(system_views_file);

    setlocales();

    if (strcmp(lc_ctype, lc_collate) == 0 &&
        strcmp(lc_ctype, lc_time) == 0 &&
        strcmp(lc_ctype, lc_numeric) == 0 &&
        strcmp(lc_ctype, lc_monetary) == 0 &&
        strcmp(lc_ctype, lc_messages) == 0)
    {
        printf("The database cluster will be initialized with locale %s\n",
               lc_ctype);
    }
    else
    {
        printf("The database cluster will be initialized with locales\n"
               "  COLLATE:  %s\n"
               "  CTYPE:    %s\n"
               "  MESSAGES: %s\n"
               "  MONETARY: %s\n"
               "  NUMERIC:  %s\n"
               "  TIME:     %s\n",
               lc_collate,
               lc_ctype,
               lc_messages,
               lc_monetary,
               lc_numeric,
               lc_time);
    }

    umask(077);

    /*
     * now we are starting to do real work, trap signals so we can clean
     * up
     */

    /* some of these are not valid on Windows */
#ifdef SIGHUP
    pqsignal(SIGHUP, trapsig);
#endif
#ifdef SIGINT
    pqsignal(SIGINT, trapsig);
#endif
#ifdef SIGQUIT
    pqsignal(SIGQUIT, trapsig);
#endif
#ifdef SIGTERM
    pqsignal(SIGTERM, trapsig);
#endif

    /* clear this we'll use it in a few lines */
    errno = 0;

    if (!check_data_dir())
    {
        fprintf(stderr,
                "%s: directory \"%s\" exists but is not empty\n"
                "If you want to create a new database system, either "
                "remove or empty\n"
                "the directory \"$PGDATA\" or run $CMDNAME with an "
                "argument other than\n"
                "\"%s\".\n",
                progname, pg_data, pg_data);
        exit(1);
    }

    /*
     * check_data_dir() called opendir - the errno should still be hanging
     * around
     */
    if (errno == ENOENT)
    {
        printf("creating directory \"%s\" ... ", pg_data);

        if (!mkdatadir(NULL))
            exit_nicely();
        else
            check_ok();

        made_new_pgdata = true;
    }

    for (i = 0; i < (sizeof(subdirs) / sizeof(char *)); i++)
    {
        printf("creating directory %s/%s ... ", pg_data, subdirs[i]);

        if (!mkdatadir(subdirs[i]))
            exit_nicely();
        else
            check_ok();
    }

    set_short_version(short_version, NULL);

    set_null_conf();

    /* test connections first because it has more constraints */
    test_connections();
    test_buffers();

    setup_config();

    bootstrap_template1(short_version);

    set_short_version(short_version, "base/1");

    setup_shadow();
    if (pwprompt)
        get_set_pwd();

    unlimit_systables();

    setup_depend();

    setup_sysviews();

    setup_description();

    setup_conversion();

    setup_privileges();

    setup_schema();

    vacuum_db();

    make_template0();

    printf("\n"
           "Success. You can now start the database server using:\n\n"
           "    %s/postmaster -D %s%s%s\n"
           "or\n"
           "    %s/pg_ctl -D %s%s%s -l logfile start\n\n",
            pgpath, QUOTE_PATH, pg_data, QUOTE_PATH,
            pgpath, QUOTE_PATH, pg_data, QUOTE_PATH);

    return 0;
}



CREATE VIEW pg_user AS
    SELECT
        usename,
        usesysid,
        usecreatedb,
        usesuper,
        usecatupd,
        '********'::text as passwd,
        valuntil,
        useconfig
    FROM pg_shadow;

CREATE VIEW pg_rules AS
    SELECT
        N.nspname AS schemaname,
        C.relname AS tablename,
        R.rulename AS rulename,
        pg_get_ruledef(R.oid) AS definition
    FROM (pg_rewrite R JOIN pg_class C ON (C.oid = R.ev_class))
        LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
    WHERE R.rulename != '_RETURN';

CREATE VIEW pg_views AS
    SELECT
        N.nspname AS schemaname,
        C.relname AS viewname,
        pg_get_userbyid(C.relowner) AS viewowner,
        pg_get_viewdef(C.oid) AS definition
    FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
    WHERE C.relkind = 'v';

CREATE VIEW pg_tables AS
    SELECT
        N.nspname AS schemaname,
        C.relname AS tablename,
        pg_get_userbyid(C.relowner) AS tableowner,
        C.relhasindex AS hasindexes,
        C.relhasrules AS hasrules,
        (C.reltriggers > 0) AS hastriggers
    FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
    WHERE C.relkind = 'r';

CREATE VIEW pg_indexes AS
    SELECT
        N.nspname AS schemaname,
        C.relname AS tablename,
        I.relname AS indexname,
        pg_get_indexdef(I.oid) AS indexdef
    FROM pg_index X JOIN pg_class C ON (C.oid = X.indrelid)
         JOIN pg_class I ON (I.oid = X.indexrelid)
         LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
    WHERE C.relkind = 'r' AND I.relkind = 'i';

CREATE VIEW pg_stats AS
    SELECT
        nspname AS schemaname,
        relname AS tablename,
        attname AS attname,
        stanullfrac AS null_frac,
        stawidth AS avg_width,
        stadistinct AS n_distinct,
        CASE 1
            WHEN stakind1 THEN stavalues1
            WHEN stakind2 THEN stavalues2
            WHEN stakind3 THEN stavalues3
            WHEN stakind4 THEN stavalues4
        END AS most_common_vals,
        CASE 1
            WHEN stakind1 THEN stanumbers1
            WHEN stakind2 THEN stanumbers2
            WHEN stakind3 THEN stanumbers3
            WHEN stakind4 THEN stanumbers4
        END AS most_common_freqs,
        CASE 2
            WHEN stakind1 THEN stavalues1
            WHEN stakind2 THEN stavalues2
            WHEN stakind3 THEN stavalues3
            WHEN stakind4 THEN stavalues4
        END AS histogram_bounds,
        CASE 3
            WHEN stakind1 THEN stanumbers1[1]
            WHEN stakind2 THEN stanumbers2[1]
            WHEN stakind3 THEN stanumbers3[1]
            WHEN stakind4 THEN stanumbers4[1]
        END AS correlation
    FROM pg_statistic s JOIN pg_class c ON (c.oid = s.starelid)
         JOIN pg_attribute a ON (c.oid = attrelid AND attnum = s.staattnum)
         LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace)
    WHERE has_table_privilege(c.oid, 'select');

REVOKE ALL on pg_statistic FROM public;

CREATE VIEW pg_stat_all_tables AS
    SELECT
            C.oid AS relid,
            N.nspname AS schemaname,
            C.relname AS relname,
            pg_stat_get_numscans(C.oid) AS seq_scan,
            pg_stat_get_tuples_returned(C.oid) AS seq_tup_read,
            sum(pg_stat_get_numscans(I.indexrelid)) AS idx_scan,
            sum(pg_stat_get_tuples_fetched(I.indexrelid)) AS idx_tup_fetch,
            pg_stat_get_tuples_inserted(C.oid) AS n_tup_ins,
            pg_stat_get_tuples_updated(C.oid) AS n_tup_upd,
            pg_stat_get_tuples_deleted(C.oid) AS n_tup_del
    FROM pg_class C LEFT JOIN
         pg_index I ON C.oid = I.indrelid
         LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
    WHERE C.relkind = 'r'
    GROUP BY C.oid, N.nspname, C.relname;

CREATE VIEW pg_stat_sys_tables AS
    SELECT * FROM pg_stat_all_tables
    WHERE schemaname IN ('pg_catalog', 'pg_toast');

CREATE VIEW pg_stat_user_tables AS
    SELECT * FROM pg_stat_all_tables
    WHERE schemaname NOT IN ('pg_catalog', 'pg_toast');

CREATE VIEW pg_statio_all_tables AS
    SELECT
            C.oid AS relid,
            N.nspname AS schemaname,
            C.relname AS relname,
            pg_stat_get_blocks_fetched(C.oid) -
                    pg_stat_get_blocks_hit(C.oid) AS heap_blks_read,
            pg_stat_get_blocks_hit(C.oid) AS heap_blks_hit,
            sum(pg_stat_get_blocks_fetched(I.indexrelid) -
                    pg_stat_get_blocks_hit(I.indexrelid)) AS idx_blks_read,
            sum(pg_stat_get_blocks_hit(I.indexrelid)) AS idx_blks_hit,
            pg_stat_get_blocks_fetched(T.oid) -
                    pg_stat_get_blocks_hit(T.oid) AS toast_blks_read,
            pg_stat_get_blocks_hit(T.oid) AS toast_blks_hit,
            pg_stat_get_blocks_fetched(X.oid) -
                    pg_stat_get_blocks_hit(X.oid) AS tidx_blks_read,
            pg_stat_get_blocks_hit(X.oid) AS tidx_blks_hit
    FROM pg_class C LEFT JOIN
            pg_index I ON C.oid = I.indrelid LEFT JOIN
            pg_class T ON C.reltoastrelid = T.oid LEFT JOIN
            pg_class X ON T.reltoastidxid = X.oid
            LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
    WHERE C.relkind = 'r'
    GROUP BY C.oid, N.nspname, C.relname, T.oid, X.oid;

CREATE VIEW pg_statio_sys_tables AS
    SELECT * FROM pg_statio_all_tables
    WHERE schemaname IN ('pg_catalog', 'pg_toast');

CREATE VIEW pg_statio_user_tables AS
    SELECT * FROM pg_statio_all_tables
    WHERE schemaname NOT IN ('pg_catalog', 'pg_toast');

CREATE VIEW pg_stat_all_indexes AS
    SELECT
            C.oid AS relid,
            I.oid AS indexrelid,
            N.nspname AS schemaname,
            C.relname AS relname,
            I.relname AS indexrelname,
            pg_stat_get_numscans(I.oid) AS idx_scan,
            pg_stat_get_tuples_returned(I.oid) AS idx_tup_read,
            pg_stat_get_tuples_fetched(I.oid) AS idx_tup_fetch
    FROM pg_class C JOIN
            pg_index X ON C.oid = X.indrelid JOIN
            pg_class I ON I.oid = X.indexrelid
            LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
    WHERE C.relkind = 'r';

CREATE VIEW pg_stat_sys_indexes AS
    SELECT * FROM pg_stat_all_indexes
    WHERE schemaname IN ('pg_catalog', 'pg_toast');

CREATE VIEW pg_stat_user_indexes AS
    SELECT * FROM pg_stat_all_indexes
    WHERE schemaname NOT IN ('pg_catalog', 'pg_toast');

CREATE VIEW pg_statio_all_indexes AS
    SELECT
            C.oid AS relid,
            I.oid AS indexrelid,
            N.nspname AS schemaname,
            C.relname AS relname,
            I.relname AS indexrelname,
            pg_stat_get_blocks_fetched(I.oid) -
                    pg_stat_get_blocks_hit(I.oid) AS idx_blks_read,
            pg_stat_get_blocks_hit(I.oid) AS idx_blks_hit
    FROM pg_class C JOIN
            pg_index X ON C.oid = X.indrelid JOIN
            pg_class I ON I.oid = X.indexrelid
            LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
    WHERE C.relkind = 'r';

CREATE VIEW pg_statio_sys_indexes AS
    SELECT * FROM pg_statio_all_indexes
    WHERE schemaname IN ('pg_catalog', 'pg_toast');

CREATE VIEW pg_statio_user_indexes AS
    SELECT * FROM pg_statio_all_indexes
    WHERE schemaname NOT IN ('pg_catalog', 'pg_toast');

CREATE VIEW pg_statio_all_sequences AS
    SELECT
            C.oid AS relid,
            N.nspname AS schemaname,
            C.relname AS relname,
            pg_stat_get_blocks_fetched(C.oid) -
                    pg_stat_get_blocks_hit(C.oid) AS blks_read,
            pg_stat_get_blocks_hit(C.oid) AS blks_hit
    FROM pg_class C
            LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
    WHERE C.relkind = 'S';

CREATE VIEW pg_statio_sys_sequences AS
    SELECT * FROM pg_statio_all_sequences
    WHERE schemaname IN ('pg_catalog', 'pg_toast');

CREATE VIEW pg_statio_user_sequences AS
    SELECT * FROM pg_statio_all_sequences
    WHERE schemaname NOT IN ('pg_catalog', 'pg_toast');

CREATE VIEW pg_stat_activity AS
    SELECT
            D.oid AS datid,
            D.datname AS datname,
            pg_stat_get_backend_pid(S.backendid) AS procpid,
            pg_stat_get_backend_userid(S.backendid) AS usesysid,
            U.usename AS usename,
            pg_stat_get_backend_activity(S.backendid) AS current_query,
            pg_stat_get_backend_activity_start(S.backendid) AS query_start
    FROM pg_database D,
            (SELECT pg_stat_get_backend_idset() AS backendid) AS S,
            pg_shadow U
    WHERE pg_stat_get_backend_dbid(S.backendid) = D.oid AND
            pg_stat_get_backend_userid(S.backendid) = U.usesysid;

CREATE VIEW pg_stat_database AS
    SELECT
            D.oid AS datid,
            D.datname AS datname,
            pg_stat_get_db_numbackends(D.oid) AS numbackends,
            pg_stat_get_db_xact_commit(D.oid) AS xact_commit,
            pg_stat_get_db_xact_rollback(D.oid) AS xact_rollback,
            pg_stat_get_db_blocks_fetched(D.oid) -
                    pg_stat_get_db_blocks_hit(D.oid) AS blks_read,
            pg_stat_get_db_blocks_hit(D.oid) AS blks_hit
    FROM pg_database D;

CREATE VIEW pg_locks AS
    SELECT *
    FROM pg_lock_status() AS L(relation oid, database oid,
    transaction xid, pid int4, mode text, granted boolean);

CREATE VIEW pg_settings AS
    SELECT *
    FROM pg_show_all_settings() AS A
    (name text, setting text, context text, vartype text,
     source text, min_val text, max_val text);

CREATE RULE pg_settings_u AS
    ON UPDATE TO pg_settings
    WHERE new.name = old.name DO
    SELECT set_config(old.name, new.setting, 'f');

CREATE RULE pg_settings_n AS
    ON UPDATE TO pg_settings
    DO INSTEAD NOTHING;


Index: src/bin/initdb/Makefile
===================================================================
RCS file: /cvsroot/pgsql-server/src/bin/initdb/Makefile,v
retrieving revision 1.29
diff -c -c -r1.29 Makefile
*** src/bin/initdb/Makefile    7 Sep 2003 03:36:03 -0000    1.29
--- src/bin/initdb/Makefile    8 Nov 2003 04:57:44 -0000
***************
*** 13,37 ****
  top_builddir = ../../..
  include $(top_builddir)/src/Makefile.global

! all: initdb

! initdb: initdb.sh $(top_builddir)/src/Makefile.global
!     sed -e 's/@VERSION@/$(VERSION)/g' \
!         -e 's,@SHELL@,$(SHELL),g' \
!         -e 's,@HAVE_IPV6@,$(HAVE_IPV6),g' \
!         -e 's,@bindir@,$(bindir),g' \
!         -e 's,@datadir@,$(datadir),g' \
!       $< >$@
!     chmod a+x $@

  install: all installdirs
!     $(INSTALL_SCRIPT) initdb $(DESTDIR)$(bindir)/initdb

  installdirs:
      $(mkinstalldirs) $(DESTDIR)$(bindir)

  uninstall:
!     rm -f $(DESTDIR)$(bindir)/initdb

! clean distclean maintainer-clean:
!     rm -f initdb
--- 13,42 ----
  top_builddir = ../../..
  include $(top_builddir)/src/Makefile.global

! override CPPFLAGS := -DPGBINDIR=\"$(bindir)\" -DPGDATADIR=\"$(datadir)\" -DFRONTEND -I$(libpq_srcdir) $(CPPFLAGS)

! OBJS=    initdb.o sprompt.o
! ifeq ($(PORTNAME), win32)
! OBJS+=dirmod.o
! endif
!
! all: submake-libpq submake-libpgport initdb
!
! initdb: $(OBJS) $(libpq_builddir)/libpq.a
!     $(CC) $(CFLAGS) $(OBJS) $(libpq) $(LDFLAGS) $(LIBS) -o $@
!
! dirmod.c sprompt.c: % : $(top_srcdir)/src/port/%
!     rm -f $@ && $(LN_S) $< .

  install: all installdirs
!     $(INSTALL_PROGRAM) initdb$(X) $(DESTDIR)$(bindir)/initdb$(X)
!     $(INSTALL_DATA) $(srcdir)/system_views.sql $(DESTDIR)$(datadir)/system_views.sql

  installdirs:
      $(mkinstalldirs) $(DESTDIR)$(bindir)

  uninstall:
!     rm -f $(DESTDIR)$(bindir)/initdb$(X) $(DESTDIR)$(datadir)/system_views.sql

! clean distclean:
!     rm -f initdb$(X) $(OBJS)
*** /laptop/initdb.c    Sat Nov  8 00:10:58 2003
--- initdb.c    Fri Nov  7 23:55:07 2003
***************
*** 1,4 ****
-
  /*-------------------------------------------------------------------------
   *
   * initdb
--- 1,3 ----
***************
*** 53,59 ****
  char       *datadir = PGDATADIR;

  /* values to be obtained from arguments */
-
  char       *pg_data = "";
  char       *encoding = "";
  char       *locale = "";
--- 52,57 ----
***************
*** 93,100 ****
  bool        not_ok = false;

  /* defaults */
- int            n_buffers = 50;
  int            n_connections = 10;


  /* platform specific path stuff */
--- 91,98 ----
  bool        not_ok = false;

  /* defaults */
  int            n_connections = 10;
+ int            n_buffers = 50;


  /* platform specific path stuff */
***************
*** 104,116 ****
  #else
  #define EXE ""
  #define DEVNULL "/dev/null"
! #endif   /* defined(__CYGWIN__) || defined(WIN32) */

  #ifdef WIN32
  #define PATHSEP ';'
  #else
  #define PATHSEP ':'
! #endif   /* WIN32 */

  /* detected path to postgres and (we assume) friends */
  char       *pgpath;
--- 102,114 ----
  #else
  #define EXE ""
  #define DEVNULL "/dev/null"
! #endif

  #ifdef WIN32
  #define PATHSEP ';'
  #else
  #define PATHSEP ':'
! #endif

  /* detected path to postgres and (we assume) friends */
  char       *pgpath;
***************
*** 122,140 ****

  #ifdef WIN32
  static char *expanded_path(char *);
- static int    init_unlink(const char *);
-
  #else
! #define expanded_path(x) ( x )
! #define init_unlink(x) unlink( (x) )
! #endif   /* WIN32 */

  static char **readfile(char *);
  static void writefile(char *, char **);
  static char *get_id(void);
  static char *get_encoding_id(char *);
  static char *get_short_version(void);
! static int    build_path(char *, mode_t);
  static bool check_data_dir(void);
  static bool mkdatadir(char *);
  static bool chklocale(const char *);
--- 120,135 ----

  #ifdef WIN32
  static char *expanded_path(char *);
  #else
! #define expanded_path(x) (x)
! #endif

  static char **readfile(char *);
  static void writefile(char *, char **);
  static char *get_id(void);
  static char *get_encoding_id(char *);
  static char *get_short_version(void);
! static int    mkdir_p(char *, mode_t);
  static bool check_data_dir(void);
  static bool mkdatadir(char *);
  static bool chklocale(const char *);
***************
*** 151,157 ****
  static void setup_config(void);
  static void bootstrap_template1(char *);
  static void setup_shadow(void);
! static void get_set_pw(void);
  static void unlimit_systables(void);
  static void setup_depend(void);
  static void setup_sysviews(void);
--- 146,152 ----
  static void setup_config(void);
  static void bootstrap_template1(char *);
  static void setup_shadow(void);
! static void get_set_pwd(void);
  static void unlimit_systables(void);
  static void setup_depend(void);
  static void setup_sysviews(void);
***************
*** 171,230 ****
  /*
   * macros for running pipes to postgres
   */
-
  #define PG_CMD_DECL        char cmd[MAXPGPATH]; char ** line ; FILE * pg
  #define PG_CMD_DECL_NOLINE           char cmd[MAXPGPATH]; FILE * pg
- #define PG_CMD_OPEN        \
-     do {\
-           pg = popen(cmd,PG_BINARY_W);\
-           if (pg == NULL) \
-             exit_nicely();\
-     } while (0)
- #define PG_CMD_CLOSE    \
-     do {\
-          if(pclose(pg)>>8 &0xff)\
-             exit_nicely();\
-     } while (0)
- #define PG_CMD_PUTLINE    \
-     do {\
-          if (fputs(*line, pg) < 0) \
-            exit_nicely(); \
-          fflush(pg);\
-     } while (0)
-
-


! #ifdef WIN32
!
! /* workaround for win32 unlink bug, not using logging like in port/dirmod.c */
!
! /* make sure we call the real unlink from MSVCRT */

! #ifdef unlink
! #undef unlink
  #endif

- static int
- init_unlink(const char *path)
- {
-     while (unlink(path))
-     {
-         if (errno != EACCES)
-             return -1;
-         Sleep(100);                /* ms */
-     }
-     return 0;
- }
- #endif   /* WIN32 */
-
  /*
!  * routines to check mem allocations and fail noisily
   * Note that we can't call exit_nicely() on a memory failure, as it calls
   * rmtree() which needs memory allocation. So we just exit with a bang.
   *
   */
-
  static void *
  xmalloc(size_t size)
  {
--- 166,206 ----
  /*
   * macros for running pipes to postgres
   */
  #define PG_CMD_DECL        char cmd[MAXPGPATH]; char ** line ; FILE * pg
  #define PG_CMD_DECL_NOLINE           char cmd[MAXPGPATH]; FILE * pg

+ #define PG_CMD_OPEN \
+ do { \
+           pg = popen(cmd,PG_BINARY_W); \
+           if (pg == NULL)  \
+             exit_nicely(); \
+ } while (0)
+
+ #define PG_CMD_CLOSE \
+ do { \
+          if(pclose(pg) >> 8 & 0xff) \
+             exit_nicely(); \
+ } while (0)

! #define PG_CMD_PUTLINE \
! do { \
!          if (fputs(*line, pg) < 0) \
!            exit_nicely(); \
!          fflush(pg); \
! } while (0)

! #ifndef WIN32
! #define QUOTE_PATH    ""
! #else
! #define QUOTE_PATH    "\""
  #endif

  /*
!  * routines to check mem allocations and fail noisily.
   * Note that we can't call exit_nicely() on a memory failure, as it calls
   * rmtree() which needs memory allocation. So we just exit with a bang.
   *
   */
  static void *
  xmalloc(size_t size)
  {
***************
*** 263,344 ****
  static bool
  rmtree(char *path, bool rmtopdir)
  {
!     char        filepath[MAXPGPATH];
!     DIR           *dir;
!     struct dirent *file;
!     char      **filenames;
!     char      **filename;
!     int            numnames = 0;
!     struct stat statbuf;

!     /*
!      * we copy all the names out of the directory before we start
!      * modifying it.
!      *
!      */
!
!     dir = opendir(path);
!     if (dir == NULL)
!         return false;
!
!     while ((file = readdir(dir)) != NULL)
!     {
!         if (strcmp(file->d_name, ".") != 0 && strcmp(file->d_name, "..") != 0)
!             numnames++;
!     }
!
!     rewinddir(dir);
!
!     filenames = xmalloc((numnames + 2) * sizeof(char *));
!     numnames = 0;
!
!     while ((file = readdir(dir)) != NULL)
!     {
!         if (strcmp(file->d_name, ".") != 0 && strcmp(file->d_name, "..") != 0)
!             filenames[numnames++] = xstrdup(file->d_name);
!     }
!
!     filenames[numnames] = NULL;
!
!     closedir(dir);
!
!     /* now we have the names we can start removing things */
!
!     for (filename = filenames; *filename; filename++)
!     {
!         snprintf(filepath, MAXPGPATH, "%s/%s", path, *filename);
!
!         if (stat(filepath, &statbuf) != 0)
!             return false;
!
!         if (S_ISDIR(statbuf.st_mode))
!         {
!             /* call ourselves recursively for a directory */
!             if (!rmtree(filepath, true))
!                 return false;
!         }
!         else
!         {
!             if (init_unlink(filepath) != 0)
!                 return false;
!         }
!     }

!     if (rmtopdir)
!     {
!         if (rmdir(path) != 0)
!             return false;
!     }

!     return true;
  }


  /*
   * make all paths look like unix, with forward slashes
   * also strip any trailing slash
   */
-
  static void
  canonicalise_path(char *path)
  {
--- 239,271 ----
  static bool
  rmtree(char *path, bool rmtopdir)
  {
!     char        buf[MAXPGPATH + 64];
!     int            ret = 1;

! #ifndef WIN32
!     /* doesn't handle .* files */
!     snprintf(buf, sizeof(buf), "rm -rf '%s%s'", path,
!              (rmtopdir) ? "" : "/*");
! #else
!     snprintf(buf, sizeof(buf), "rmdir /s /q \"%s\"", path);
! #endif

!     if (system(buf) != 0)
!         ret = 0;

! #ifdef WIN32
!     if (ret == 1 && !rmtopdir)
!         ret = !mkdir(path);        /* recreate, no permission issues? */
! #endif
!     return ret;
  }


  /*
   * make all paths look like unix, with forward slashes
   * also strip any trailing slash
+  * needed?
   */
  static void
  canonicalise_path(char *path)
  {
***************
*** 349,355 ****
  #ifdef WIN32
          if (*p == '\\')
              *p = '/';
! #endif   /* WIN32 */
      }
      if (p != path && *--p == '/')
          *p = '\0';
--- 276,282 ----
  #ifdef WIN32
          if (*p == '\\')
              *p = '/';
! #endif
      }
      if (p != path && *--p == '/')
          *p = '\0';
***************
*** 361,367 ****
   * This does most of what sed was used for in the shell script, but
   * doesn't need any regexp stuff.
   */
-
  static char **
  replace_token(char **lines, char *token, char *replacement)
  {
--- 288,293 ----
***************
*** 372,378 ****
                  replen,
                  diff;

-
      for (i = 0; lines[i]; i++)
          numlines++;

--- 298,303 ----
***************
*** 431,439 ****
      char       *buffer;
      int            c;

!     infile = fopen(path, "r");
!
!     if (!infile)
      {
          fprintf(stderr, "could not read %s ... ", path);
          exit_nicely();
--- 356,362 ----
      char       *buffer;
      int            c;

!     if ((infile = fopen(path, "r")) == NULL)
      {
          fprintf(stderr, "could not read %s ... ", path);
          exit_nicely();
***************
*** 491,498 ****
      FILE       *out_file;
      char      **line;

!     out_file = fopen(path, PG_BINARY_W);
!     if (out_file == NULL)
      {
          fprintf(stderr, "could not write %s ... ", path);
          exit_nicely();
--- 414,421 ----
      FILE       *out_file;
      char      **line;

!     ;
!     if ((out_file = fopen(path, PG_BINARY_W)) == NULL)
      {
          fprintf(stderr, "could not write %s ... ", path);
          exit_nicely();
***************
*** 515,523 ****
   * we also assume it isn't null.
   *
   */
-
  static int
! build_path(char *path, mode_t omode)
  {
      struct stat sb;
      mode_t        numask,
--- 438,445 ----
   * we also assume it isn't null.
   *
   */
  static int
! mkdir_p(char *path, mode_t omode)
  {
      struct stat sb;
      mode_t        numask,
***************
*** 532,538 ****
      retval = 0;

  #ifdef WIN32
-
      /* skip network and drive specifiers for win32 */
      if (strlen(p) >= 2)
      {
--- 454,459 ----
***************
*** 551,557 ****
              p += 2;
          }
      }
! #endif   /* WIN32 */

      if (p[0] == '/')            /* Skip leading '/'. */
          ++p;
--- 472,478 ----
              p += 2;
          }
      }
! #endif

      if (p[0] == '/')            /* Skip leading '/'. */
          ++p;
***************
*** 614,620 ****
      }
      if (!first && !last)
          (void) umask(oumask);
!     return (retval);
  }

  /*
--- 535,541 ----
      }
      if (!first && !last)
          (void) umask(oumask);
!     return retval;
  }

  /*
***************
*** 673,679 ****
                  progname);
          exit(1);
      }
! #endif   /* __BEOS__ */

  #else                            /* the windows code */

--- 594,600 ----
                  progname);
          exit(1);
      }
! #endif

  #else                            /* the windows code */

***************
*** 687,693 ****

      pw->pw_uid = 1;
      GetUserName(pw->pw_name, &pwname_size);
! #endif   /* ! WIN32 */

      return xstrdup(pw->pw_name);
  }
--- 608,614 ----

      pw->pw_uid = 1;
      GetUserName(pw->pw_name, &pwname_size);
! #endif

      return xstrdup(pw->pw_name);
  }
***************
*** 785,791 ****

      closedir(chkdir);

!     return (empty);
  }

  /*
--- 706,712 ----

      closedir(chkdir);

!     return empty;
  }

  /*
***************
*** 812,818 ****
      else if (subdir == NULL || errno != ENOENT)
          return false;
      else
!         return !build_path(path, 0700);
  }


--- 733,739 ----
      else if (subdir == NULL || errno != ENOENT)
          return false;
      else
!         return !mkdir_p(path, 0700);
  }


***************
*** 853,859 ****
   * don't overkill
   *
   */
-
  #define FIND_SUCCESS 0
  #define FIND_NOT_FOUND 1
  #define FIND_STAT_ERR 2
--- 774,779 ----
***************
*** 875,885 ****
      char        line[100];

  #ifndef WIN32
-
      int            permmask = S_IROTH | S_IXOTH;
  #endif

-
      struct stat statbuf;
      FILE       *pgver;
      int            plen = strlen(path);
--- 795,803 ----
***************
*** 900,911 ****
          return FIND_NOT_REGFILE;

  #ifndef WIN32
!
!     /* on windows a .exe file should be executable - this is the unix test */
!
      if ((statbuf.st_mode & permmask) != permmask)
          return FIND_BAD_PERM;
! #endif   /* ! WIN32 */

      snprintf(cmd, MAXPGPATH, "\"%s/postgres\" -V 2>%s", path, DEVNULL);

--- 818,830 ----
          return FIND_NOT_REGFILE;

  #ifndef WIN32
!     /*
!      * Only unix requires this test, on WIN32 an .exe file should be
!      * executable
!      */
      if ((statbuf.st_mode & permmask) != permmask)
          return FIND_BAD_PERM;
! #endif

      snprintf(cmd, MAXPGPATH, "\"%s/postgres\" -V 2>%s", path, DEVNULL);

***************
*** 913,923 ****
          return FIND_EXEC_ERR;

      if (fgets(line, sizeof(line), pgver) == NULL)
-     {
          perror("fgets failure");

-     }
-
      pclose(pgver);

      if (strcmp(line, PG_VERSIONSTR) != 0)
--- 832,839 ----
***************
*** 926,939 ****
      return FIND_SUCCESS;
  }

- #ifdef WIN32
-
  /*
   * Windows doesn't like relative paths to executables (other things work fine)
   * so we call its builtin function to expand them. Elsewhere this is a NOOP
   *
   */
!
  static char *
  expanded_path(char *path)
  {
--- 842,853 ----
      return FIND_SUCCESS;
  }

  /*
   * Windows doesn't like relative paths to executables (other things work fine)
   * so we call its builtin function to expand them. Elsewhere this is a NOOP
   *
   */
! #ifdef WIN32
  static char *
  expanded_path(char *path)
  {
***************
*** 947,953 ****
      canonicalise_path(abspath);
      return xstrdup(abspath);
  }
! #endif   /* WIN32 */

  /*
   * set the paths pointing to postgres
--- 861,867 ----
      canonicalise_path(abspath);
      return xstrdup(abspath);
  }
! #endif

  /*
   * set the paths pointing to postgres
***************
*** 1078,1140 ****
  }

  /*
!  * check how many buffers we can run with
   *
   */
  static void
! test_buffers(void)
  {
      char       *format =
!     "\"%s/postgres\"  -boot -x 0 -F "
!     "-c shared_buffers=%d -c max_connections=5 template1 <%s >%s 2>&1";
      char        cmd[MAXPGPATH];
!     int            bufs[] =
!     {1000, 900, 800, 700, 600, 500, 400, 300, 200, 100, 50};
!     int            len = sizeof(bufs) / sizeof(int);
      int            i,
                  status;

      for (i = 0; i < len; i++)
      {
!         snprintf(cmd, sizeof(cmd), format, pgpath, bufs[i], DEVNULL, DEVNULL);
          status = system(cmd);
          if (status == 0)
              break;
      }
      if (i >= len)
          i = len - 1;
!     n_buffers = bufs[i];
!     printf("buffers set to %d\n", n_buffers);
  }

  /*
!  * check how many connections we can sustain
   *
   */
  static void
! test_connections(void)
  {
      char       *format =
!     "\"%s/postgres\" -boot -x 0 -F "
      "-c shared_buffers=%d -c max_connections=%d template1 <%s >%s 2>&1";
      char        cmd[MAXPGPATH];
!     int            conns[] = {100, 50, 40, 30, 20, 10};
!     int            len = sizeof(conns) / sizeof(int);
      int            i,
                  status;

      for (i = 0; i < len; i++)
      {
!         snprintf(cmd, sizeof(cmd), format,
!                  pgpath, n_buffers, conns[i], DEVNULL, DEVNULL);
          status = system(cmd);
          if (status == 0)
              break;
      }
      if (i >= len)
          i = len - 1;
!     n_connections = conns[i];
!     printf("connections set to %d\n", n_connections);
  }

  /*
--- 992,1054 ----
  }

  /*
!  * check how many connections we can sustain
   *
   */
  static void
! test_connections(void)
  {
      char       *format =
!     "\"%s/postgres\" -boot -x 0 -F "
!     "-c shared_buffers=%d -c max_connections=%d template1 <%s >%s 2>&1";
      char        cmd[MAXPGPATH];
!     int            conns[] = {100, 50, 40, 30, 20, 10};
!     int            len = sizeof(conns) / sizeof(int);
      int            i,
                  status;

      for (i = 0; i < len; i++)
      {
!         snprintf(cmd, sizeof(cmd), format,
!                  pgpath, conns[i] * 5, conns[i], DEVNULL, DEVNULL);
          status = system(cmd);
          if (status == 0)
              break;
      }
      if (i >= len)
          i = len - 1;
!     n_connections = conns[i];
!     printf("connections set to %d\n", n_connections);
  }

  /*
!  * check how many buffers we can run with
   *
   */
  static void
! test_buffers(void)
  {
      char       *format =
!     "\"%s/postgres\"  -boot -x 0 -F "
      "-c shared_buffers=%d -c max_connections=%d template1 <%s >%s 2>&1";
      char        cmd[MAXPGPATH];
!     int            bufs[] = {1000, 900, 800, 700, 600, 500, 400, 300, 200, 100, 50};
!     int            len = sizeof(bufs) / sizeof(int);
      int            i,
                  status;

      for (i = 0; i < len; i++)
      {
!         snprintf(cmd, sizeof(cmd), format, pgpath, bufs[i], n_connections,
!             DEVNULL, DEVNULL);
          status = system(cmd);
          if (status == 0)
              break;
      }
      if (i >= len)
          i = len - 1;
!     n_buffers = bufs[i];
!     printf("buffers set to %d\n", n_buffers);
  }

  /*
***************
*** 1155,1166 ****

      conflines = readfile(conf_file);

-     snprintf(repltok, sizeof(repltok), "shared_buffers = %d", n_buffers);
-     conflines = replace_token(conflines, "#shared_buffers = 1000", repltok);
-
      snprintf(repltok, sizeof(repltok), "max_connections = %d", n_connections);
      conflines = replace_token(conflines, "#max_connections = 100", repltok);

      snprintf(repltok, sizeof(repltok), "lc_messages = '%s'", lc_messages);
      conflines = replace_token(conflines, "#lc_messages = 'C'", repltok);

--- 1069,1080 ----

      conflines = readfile(conf_file);

      snprintf(repltok, sizeof(repltok), "max_connections = %d", n_connections);
      conflines = replace_token(conflines, "#max_connections = 100", repltok);

+     snprintf(repltok, sizeof(repltok), "shared_buffers = %d", n_buffers);
+     conflines = replace_token(conflines, "#shared_buffers = 1000", repltok);
+
      snprintf(repltok, sizeof(repltok), "lc_messages = '%s'", lc_messages);
      conflines = replace_token(conflines, "#lc_messages = 'C'", repltok);

***************
*** 1190,1196 ****
      conflines = replace_token(conflines,
                                "host    all         all         ::1",
                                "#host    all         all         ::1");
! #endif   /* ! HAVE_IPV6 */

      snprintf(path, MAXPGPATH, "%s/pg_hba.conf", pg_data);

--- 1104,1110 ----
      conflines = replace_token(conflines,
                                "host    all         all         ::1",
                                "#host    all         all         ::1");
! #endif

      snprintf(path, MAXPGPATH, "%s/pg_hba.conf", pg_data);

***************
*** 1211,1217 ****
      free(conflines);

      check_ok();
-
  }


--- 1125,1130 ----
***************
*** 1258,1264 ****
       * already called setlocale().
       *
       */
-
      snprintf(cmd, MAXPGPATH, "LC_COLLATE=%s", lc_collate);
      putenv(xstrdup(cmd));

--- 1171,1176 ----
***************
*** 1337,1359 ****
   *
   */
  static void
! get_set_pw(void)
  {
      PG_CMD_DECL_NOLINE;

!     char       *pw1,
!                *pw2;
!     char        pwpath[MAXPGPATH];
      struct stat statbuf;

!     pw1 = simple_prompt("Enter new superuser password: ", 100, false);
!     pw2 = simple_prompt("Enter it again: ", 100, false);
!     if (strcmp(pw1, pw2) != 0)
      {
          fprintf(stderr, "Passwords didn't match.\n");
          exit_nicely();
      }
!     free(pw2);

      printf("storing the password ... ");

--- 1249,1271 ----
   *
   */
  static void
! get_set_pwd(void)
  {
      PG_CMD_DECL_NOLINE;

!     char       *pwd1,
!                *pwd2;
!     char        pwdpath[MAXPGPATH];
      struct stat statbuf;

!     pwd1 = simple_prompt("Enter new superuser password: ", 100, false);
!     pwd2 = simple_prompt("Enter it again: ", 100, false);
!     if (strcmp(pwd1, pwd2) != 0)
      {
          fprintf(stderr, "Passwords didn't match.\n");
          exit_nicely();
      }
!     free(pwd2);

      printf("storing the password ... ");

***************
*** 1365,1371 ****
      PG_CMD_OPEN;

      if (fprintf(
!        pg, "ALTER USER \"%s\" WITH PASSWORD '%s';\n", username, pw1) < 0)
      {
          /* write failure */
          exit_nicely();
--- 1277,1283 ----
      PG_CMD_OPEN;

      if (fprintf(
!        pg, "ALTER USER \"%s\" WITH PASSWORD '%s';\n", username, pwd1) < 0)
      {
          /* write failure */
          exit_nicely();
***************
*** 1374,1381 ****

      PG_CMD_CLOSE;

!     snprintf(pwpath, MAXPGPATH, "%s/global/pg_pwd", pg_data);
!     if (stat(pwpath, &statbuf) != 0 || !S_ISREG(statbuf.st_mode))
      {
          fprintf(stderr,
                  "%s: The password file was not generated - "
--- 1286,1293 ----

      PG_CMD_CLOSE;

!     snprintf(pwdpath, MAXPGPATH, "%s/global/pg_pwd", pg_data);
!     if (stat(pwdpath, &statbuf) != 0 || !S_ISREG(statbuf.st_mode))
      {
          fprintf(stderr,
                  "%s: The password file was not generated - "
***************
*** 1898,1904 ****
   * So this will need some testing on Windows.
   *
   */
-
  static void
  trapsig(int signum)
  {
--- 1810,1815 ----
***************
*** 2009,2015 ****
          /* when not available, get the CTYPE setting */
          lc_messages = xstrdup(setlocale(LC_CTYPE, NULL));
      }
! #endif   /* LC_MESSAGES */

  }

--- 1920,1926 ----
          /* when not available, get the CTYPE setting */
          lc_messages = xstrdup(setlocale(LC_CTYPE, NULL));
      }
! #endif

  }

***************
*** 2017,2023 ****
   * help text data
   *
   */
-
  char       *usage_text[] = {
      "$CMDNAME initializes a PostgreSQL database cluster.\n",
      "\n",
--- 1928,1933 ----
***************
*** 2117,2123 ****

  #if defined(__CYGWIN__) || defined(WIN32)
      char       *exe;            /* location of exe suffix in progname */
! #endif   /* defined(__CYGWIN__) || defined(WIN32) */

      setlocale(LC_ALL, "");

--- 2027,2033 ----

  #if defined(__CYGWIN__) || defined(WIN32)
      char       *exe;            /* location of exe suffix in progname */
! #endif

      setlocale(LC_ALL, "");

***************
*** 2135,2141 ****
          /* strip .exe suffix, regardless of case */
          *exe = '\0';
      }
! #endif   /* defined(__CYGWIN__) || defined(WIN32) */

      if (lastsep)
      {
--- 2045,2051 ----
          /* strip .exe suffix, regardless of case */
          *exe = '\0';
      }
! #endif

      if (lastsep)
      {
***************
*** 2317,2323 ****
      set_input(&features_file, "sql_features.txt");
      set_input(&system_views_file, "system_views.sql");

-
      if (show_setting || debug)
      {
          fprintf(stderr,
--- 2227,2232 ----
***************
*** 2335,2345 ****
                  hba_file, ident_file);
      }

-
      if (show_setting)
          exit(0);

-
      check_input(bki_file);
      check_input(desc_file);
      check_input(hba_file);
--- 2244,2252 ----
***************
*** 2388,2404 ****
      /* some of these are not valid on Windows */
  #ifdef SIGHUP
      pqsignal(SIGHUP, trapsig);
! #endif   /* SIGHUP */
  #ifdef SIGINT
      pqsignal(SIGINT, trapsig);
! #endif   /* SIGINT */
  #ifdef SIGQUIT
      pqsignal(SIGQUIT, trapsig);
! #endif   /* SIGQUIT */
  #ifdef SIGTERM
      pqsignal(SIGTERM, trapsig);
! #endif   /* SIGTERM */
!

      /* clear this we'll use it in a few lines */
      errno = 0;
--- 2295,2310 ----
      /* some of these are not valid on Windows */
  #ifdef SIGHUP
      pqsignal(SIGHUP, trapsig);
! #endif
  #ifdef SIGINT
      pqsignal(SIGINT, trapsig);
! #endif
  #ifdef SIGQUIT
      pqsignal(SIGQUIT, trapsig);
! #endif
  #ifdef SIGTERM
      pqsignal(SIGTERM, trapsig);
! #endif

      /* clear this we'll use it in a few lines */
      errno = 0;
***************
*** 2420,2426 ****
       * check_data_dir() called opendir - the errno should still be hanging
       * around
       */
-
      if (errno == ENOENT)
      {
          printf("creating directory \"%s\" ... ", pg_data);
--- 2326,2331 ----
***************
*** 2447,2455 ****

      set_null_conf();

!     test_buffers();
!
      test_connections();

      setup_config();

--- 2352,2360 ----

      set_null_conf();

!     /* test connections first because it has more constraints */
      test_connections();
+     test_buffers();

      setup_config();

***************
*** 2458,2466 ****
      set_short_version(short_version, "base/1");

      setup_shadow();
-
      if (pwprompt)
!         get_set_pw();

      unlimit_systables();

--- 2363,2370 ----
      set_short_version(short_version, "base/1");

      setup_shadow();
      if (pwprompt)
!         get_set_pwd();

      unlimit_systables();

***************
*** 2481,2493 ****
      make_template0();

      printf("\n"
!            "Success. You can now start the database server using:\n"
!            "\n"
!            "    \"%s/postmaster\" -D \"%s\"\n"
             "or\n"
!            "    \"%s/pg_ctl\" -D \"%s\" -l logfile start\n"
!            "\n",
!            pgpath, pg_data, pgpath, pg_data);

      return 0;
  }
--- 2385,2396 ----
      make_template0();

      printf("\n"
!            "Success. You can now start the database server using:\n\n"
!            "    %s/postmaster -D %s%s%s\n"
             "or\n"
!            "    %s/pg_ctl -D %s%s%s -l logfile start\n\n",
!             pgpath, QUOTE_PATH, pg_data, QUOTE_PATH,
!             pgpath, QUOTE_PATH, pg_data, QUOTE_PATH);

      return 0;
  }

pgsql-patches by date:

Previous
From: Christopher Kings-Lynne
Date:
Subject: FAQ_MSWIN update
Next
From: Tom Lane
Date:
Subject: Re: initdb in C