Обсуждение: initdb in C

Поиск
Список
Период
Сортировка

initdb in C

От
Bruce Momjian
Дата:
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;
  }

Re: initdb in C

От
Tom Lane
Дата:
Bruce Momjian <pgman@candle.pha.pa.us> writes:
> 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 had some concern about whether Andrew's rewrite had tracked all the
recent changes to the initdb shell-script sources.  Are you both
confident that it is up to date?

            regards, tom lane

Re: initdb in C

От
Andrew Dunstan
Дата:

Tom Lane wrote:

>Bruce Momjian <pgman@candle.pha.pa.us> writes:
>
>
>>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 had some concern about whether Andrew's rewrite had tracked all the
>recent changes to the initdb shell-script sources.  Are you both
>confident that it is up to date?
>
>
>

Yes. Bruce has picked up the one change I didn't track (revision 1.204).


cheers

andrew


Re: initdb in C

От
Andrew Dunstan
Дата:

Bruce Momjian wrote:

>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.
>
>
>

I will double check this over the weekend.

cheers

andrew


Re: initdb in C

От
Andrew Dunstan
Дата:

Bruce Momjian wrote:

>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.
>
>

My comments:

I have no problem with shelling out to rmdir - although my goal was to
avoid shelling out to anything other than postgres itself. I think
recreating the datadir if we didn't create it initially should be OK in
that case, and it makes the code simpler. Not handling dot files in the
Unix case should also be fine, as we know the directory was empty to
start with and we don't create any.

Regarding the #endif comments you removed - Peter had asked me to put
them in. See
http://archives.postgresql.org/pgsql-hackers/2003-10/msg00352.php

You put a comment on canonicalise_path() asking if it is needed. The
short answer is very much "yes". The Windows command processor will
accept suitably quoted paths with forward slashes, but barfs badly with
mixed forward and back slashes. (See recent discussion on
hackers-win32).Removing the trailing slash on a path means we never get
ugly double slashes. Feel free to put that info in as a comment if you
think it is needed.

The changes you made for the final message are not going to work for
Windows - if pgpath or pg_data contain spaces we need quotes (even on
Unix, although there most people know better than to put spaces in
names). Also see above about mixed slashes. Also, putting a \ before
pg_data will be nasty if it starts with a drive or network specifier -
or even without (you might turn a directory specifier into a network
drive specifier). It's just a message, though, and we shouldn't hold up
for that - we can patch it to get it right.

(Getting the path and slash thing working portably was by far the
biggest headache in this project - the rest was just grunt work for the
most part, although I learned a few interesting things.)

Otherwise I'm fine with this.

cheers

andrew




Re: [pgsql-hackers-win32] initdb in C

От
Bruce Momjian
Дата:
Andrew Dunstan wrote:
>
>
> Tom Lane wrote:
>
> >Bruce Momjian <pgman@candle.pha.pa.us> writes:
> >
> >
> >>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 had some concern about whether Andrew's rewrite had tracked all the
> >recent changes to the initdb shell-script sources.  Are you both
> >confident that it is up to date?
> >
> >
> >
>
> Yes. Bruce has picked up the one change I didn't track (revision 1.204).

Yes, I was concerned too that everything was in there.  I checked the
initdb.sh logs and found that the only thing not added was the checking
of the max number of connections before checking the max number of
buffers, which I added.  The other stuff was in there.  I also checked
pg_id's recent changes and those were in there too.

Andrew, I assume this was a new implementation of initdb, and not taken
from an older initdb C implementation made by a company.

This isn't really a patch but a C replacement of a critical shell
script so there is reason to double-check things.

--
  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

Re: [pgsql-hackers-win32] initdb in C

От
Andrew Dunstan
Дата:

Bruce Momjian wrote:

>Yes, I was concerned too that everything was in there.  I checked the
>initdb.sh logs and found that the only thing not added was the checking
>of the max number of connections before checking the max number of
>buffers, which I added.  The other stuff was in there.  I also checked
>pg_id's recent changes and those were in there too.
>
>Andrew, I assume this was a new implementation of initdb, and not taken
>from an older initdb C implementation made by a company.
>
>This isn't really a patch but a C replacement of a critical shell
>script so there is reason to double-check things.
>
>


Yes, I worked from initdb.sh, not from any other source. It's "all my
own work" :-) I think I started with 1.201 and later upgraded to 1.203.

I agree it needs careful checking - the more eyeballs the better.

cheers

andrew


Re: [pgsql-hackers-win32] initdb in C

От
Bruce Momjian
Дата:
Andrew Dunstan wrote:
>
>
> Bruce Momjian wrote:
>
> >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.
> >
> >
>
> My comments:
>
> I have no problem with shelling out to rmdir - although my goal was to
> avoid shelling out to anything other than postgres itself. I think
> recreating the datadir if we didn't create it initially should be OK in
> that case, and it makes the code simpler. Not handling dot files in the
> Unix case should also be fine, as we know the directory was empty to
> start with and we don't create any.

Good. I can do rmdir() in C in port/dirmod.c if we need it.  Right now
we are doing system(rm/rmdir) in dbcommands.c so we should consistent.
Let's stay with system(rm/rmdir) and if it doesn't work as we expect, we
can add your rmdir() code and put it in port/dirmod.c.

> Regarding the #endif comments you removed - Peter had asked me to put
> them in. See
> http://archives.postgresql.org/pgsql-hackers/2003-10/msg00352.php

Peter, I thought we add:

    #endif /* port... */

only when the define covers many lines of code.  This case:

    #ifdef WIN32
    some code
    #endif

seems clearer than:

    #ifdef WIN32
    some code
    #endif /* WIN32 */

Of course, if the code block is very large, a closing comment can
sometimes help.  Personally, I don't like the comments anytime, but if
people like them on long blocks, I can understand that.

> You put a comment on canonicalise_path() asking if it is needed. The
> short answer is very much "yes". The Windows command processor will
> accept suitably quoted paths with forward slashes, but barfs badly with
> mixed forward and back slashes. (See recent discussion on
> hackers-win32).Removing the trailing slash on a path means we never get
> ugly double slashes. Feel free to put that info in as a comment if you
> think it is needed.

Done.

> The changes you made for the final message are not going to work for
> Windows - if pgpath or pg_data contain spaces we need quotes (even on
> Unix, although there most people know better than to put spaces in
> names). Also see above about mixed slashes. Also, putting a \ before
> pg_data will be nasty if it starts with a drive or network specifier -
> or even without (you might turn a directory specifier into a network
> drive specifier). It's just a message, though, and we shouldn't hold up
> for that - we can patch it to get it right.

I am confused.  The only change I made was to have the quotes appear
only on WIN32.

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

    ...

        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);

(I also merged multiple \n into a single line.)  My logic was that
spaces in directory names are much more common on WIN32, so we should
display the quotes only on WIN32.  Perhaps you read "\"" as "\\"?

> (Getting the path and slash thing working portably was by far the
> biggest headache in this project - the rest was just grunt work for the
> most part, although I learned a few interesting things.)
>
> Otherwise I'm fine with this.

Thanks for reviewing my work. I am surprised how small the new initdb.c
is.  It is 50k vs 38k for initdb.sh.  Of course, system_views.sql is
another 10k, but still, it is smaller than I thought, and quite clear.

Thanks.

--
  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

Re: [pgsql-hackers-win32] initdb in C

От
Bruce Momjian
Дата:
Andrew Dunstan wrote:
>
>
> Bruce Momjian wrote:
>
> >Yes, I was concerned too that everything was in there.  I checked the
> >initdb.sh logs and found that the only thing not added was the checking
> >of the max number of connections before checking the max number of
> >buffers, which I added.  The other stuff was in there.  I also checked
> >pg_id's recent changes and those were in there too.
> >
> >Andrew, I assume this was a new implementation of initdb, and not taken
> >from an older initdb C implementation made by a company.
> >
> >This isn't really a patch but a C replacement of a critical shell
> >script so there is reason to double-check things.
> >
> >
>
>
> Yes, I worked from initdb.sh, not from any other source. It's "all my
> own work" :-) I think I started with 1.201 and later upgraded to 1.203.
>
> I agree it needs careful checking - the more eyeballs the better.

The great part is that it look so much like our code, unlike the
commerical port code I have seen for initdb in the past.  This certainly
moves us forward on Win32.

--
  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

Re: initdb in C

От
Peter Eisentraut
Дата:
Andrew Dunstan writes:

> recreating the datadir if we didn't create it initially should be OK in
> that case, and it makes the code simpler.

That should be avoided, because you'll have trouble recreating the
original directory with all its properties such as ownership, permissions,
etc., at least not without making the code anything but simpler.  There
might even be a situation were you are allowed to delete the directory but
cannot create a new one.

--
Peter Eisentraut   peter_e@gmx.net


Re: initdb in C

От
Tom Lane
Дата:
Peter Eisentraut <peter_e@gmx.net> writes:
> Andrew Dunstan writes:
>> recreating the datadir if we didn't create it initially should be OK in
>> that case, and it makes the code simpler.

> That should be avoided, because you'll have trouble recreating the
> original directory with all its properties such as ownership, permissions,
> etc., at least not without making the code anything but simpler.  There
> might even be a situation were you are allowed to delete the directory but
> cannot create a new one.

Consider also the strong likelihood that the data directory's parent
directory is owned by root, so that you do not have the ability to
delete and recreate the data directory because you don't have write
permission on its parent.  The main reason initdb is set up to be able
to start with an existing-but-empty data dir is exactly because creating
that directory may have required permissions that initdb itself hasn't
got.

            regards, tom lane

Re: [pgsql-hackers-win32] initdb in C

От
Andrew Dunstan
Дата:

Bruce Momjian wrote:

>Andrew Dunstan wrote:
>
>
>>
>>
>>My comments:
>>
>>I have no problem with shelling out to rmdir - although my goal was to
>>avoid shelling out to anything other than postgres itself. I think
>>recreating the datadir if we didn't create it initially should be OK in
>>that case, and it makes the code simpler. Not handling dot files in the
>>Unix case should also be fine, as we know the directory was empty to
>>start with and we don't create any.
>>
>>
>
>Good. I can do rmdir() in C in port/dirmod.c if we need it.  Right now
>we are doing system(rm/rmdir) in dbcommands.c so we should consistent.
>Let's stay with system(rm/rmdir) and if it doesn't work as we expect, we
>can add your rmdir() code and put it in port/dirmod.c.
>
>

In view of Peter's email of a few minutes ago I think we need to do that.

>>The changes you made for the final message are not going to work for
>>Windows - if pgpath or pg_data contain spaces we need quotes (even on
>>Unix, although there most people know better than to put spaces in
>>names). Also see above about mixed slashes. Also, putting a \ before
>>pg_data will be nasty if it starts with a drive or network specifier -
>>or even without (you might turn a directory specifier into a network
>>drive specifier). It's just a message, though, and we shouldn't hold up
>>for that - we can patch it to get it right.
>>
>>
>
>I am confused.  The only change I made was to have the quotes appear
>only on WIN32.
>
>    #ifndef WIN32
>    #define QUOTE_PATH  ""
>    #else
>    #define QUOTE_PATH  "\""
>    #endif
>
>    ...
>
>        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);
>
>(I also merged multiple \n into a single line.)  My logic was that
>spaces in directory names are much more common on WIN32, so we should
>display the quotes only on WIN32.  Perhaps you read "\"" as "\\"?
>
>

Yes, I did. Sorry about that. But we also need to quote the path (the
most obvious place to put it after all is "C:\Program
Files\PostgreSQL"). In fact, that's more important than the data
location. Unfortunately, the Windows command processor barfs on multiple
quotes strings ;-(

cheers

andrew




Re: initdb in C

От
Bruce Momjian
Дата:
Peter Eisentraut wrote:
> Andrew Dunstan writes:
>
> > recreating the datadir if we didn't create it initially should be OK in
> > that case, and it makes the code simpler.
>
> That should be avoided, because you'll have trouble recreating the
> original directory with all its properties such as ownership, permissions,
> etc., at least not without making the code anything but simpler.  There
> might even be a situation were you are allowed to delete the directory but
> cannot create a new one.

Recreating the directory only happens on WIN32, where rmdir doesn't
allow you to only delete files and subdirectories and not the parent
directory.  Non-Win32 does rm -rf dir/*.

Is that OK?

--
  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

Re: initdb in C

От
Bruce Momjian
Дата:
Tom Lane wrote:
> Peter Eisentraut <peter_e@gmx.net> writes:
> > Andrew Dunstan writes:
> >> recreating the datadir if we didn't create it initially should be OK in
> >> that case, and it makes the code simpler.
>
> > That should be avoided, because you'll have trouble recreating the
> > original directory with all its properties such as ownership, permissions,
> > etc., at least not without making the code anything but simpler.  There
> > might even be a situation were you are allowed to delete the directory but
> > cannot create a new one.
>
> Consider also the strong likelihood that the data directory's parent
> directory is owned by root, so that you do not have the ability to
> delete and recreate the data directory because you don't have write
> permission on its parent.  The main reason initdb is set up to be able
> to start with an existing-but-empty data dir is exactly because creating
> that directory may have required permissions that initdb itself hasn't
> got.

Again, this directory recreate happens only on Win32, an I thought it
would be OK there.

--
  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

Re: initdb in C

От
Tom Lane
Дата:
Bruce Momjian <pgman@candle.pha.pa.us> writes:
>> Consider also the strong likelihood that the data directory's parent
>> directory is owned by root,

> Again, this directory recreate happens only on Win32, an I thought it
> would be OK there.

Windows has no concept of directory permissions at all?  I thought the
more recent versions had at least rudimentary permissions.

            regards, tom lane

Re: initdb in C

От
Tom Lane
Дата:
Bruce Momjian <pgman@candle.pha.pa.us> writes:
> Recreating the directory only happens on WIN32, where rmdir doesn't
> allow you to only delete files and subdirectories and not the parent
> directory.  Non-Win32 does rm -rf dir/*.

I think we should forget about invoking rm as a subprocess at all, and
just do the recursive directory walk and unlinks for ourselves.  We
already have code to do this for copy in copydir.c, and unlink would not
be any longer.  We will probably be forced into implementing database
removal for ourselves rather than by 'rm' hacks anyway as soon as
tablespaces come to pass; so why contort initdb's behavior for a very
transient implementation savings?

            regards, tom lane

Re: [pgsql-hackers-win32] initdb in C

От
Bruce Momjian
Дата:
Andrew Dunstan wrote:
> >Good. I can do rmdir() in C in port/dirmod.c if we need it.  Right now
> >we are doing system(rm/rmdir) in dbcommands.c so we should consistent.
> >Let's stay with system(rm/rmdir) and if it doesn't work as we expect, we
> >can add your rmdir() code and put it in port/dirmod.c.
> >
> >
>
> In view of Peter's email of a few minutes ago I think we need to do that.

Again, recreate is only Win32.  I just figured out how to do this on
Win32.  We have to use 'del' rather than 'rmdir' to keep the directory:

New code is:

    /*
     * 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];

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

        return !system(buf);
    }

> >        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);
> >
> >(I also merged multiple \n into a single line.)  My logic was that
> >spaces in directory names are much more common on WIN32, so we should
> >display the quotes only on WIN32.  Perhaps you read "\"" as "\\"?
> >
> >
>
> Yes, I did. Sorry about that. But we also need to quote the path (the
> most obvious place to put it after all is "C:\Program
> Files\PostgreSQL"). In fact, that's more important than the data
> location. Unfortunately, the Windows command processor barfs on multiple
> quotes strings ;-(

I ran some tests using XP "CMD" and found:

    "C:\test"
and
    "\test"

works but:

    "test"

does not work.  Since I see that the output always has a leading path, I
have modified the code to do:

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

I am attaching the updated initdb.c file.

--
  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 canonicalize_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];

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

    return !system(buf);
}


/*
 * make all paths look like unix, with forward slashes
 * also strip any trailing slash.
 * The Windows command processor will accept suitably quoted paths
 * with forward slashes, but barfs badly with mixed forward and back
 * slashes. Removing the trailing slash on a path means we never get
 * ugly double slashes.
 */
static void
canonicalize_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;
    }
    canonicalize_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
                {
                    canonicalize_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);
        canonicalize_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]);
    canonicalize_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);
        }
    }

    canonicalize_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("\nSuccess. You can now start the database server using:\n\n"
           "    %s%s%s/postmaster -D %s%s%s\n"
           "or\n"
           "    %s%s%s/pg_ctl -D %s%s%s -l logfile start\n\n",
            QUOTE_PATH, pgpath, QUOTE_PATH, QUOTE_PATH, pg_data, QUOTE_PATH,
            QUOTE_PATH, pgpath, QUOTE_PATH, QUOTE_PATH, pg_data, QUOTE_PATH);

    return 0;
}

Re: initdb in C

От
Bruce Momjian
Дата:
Tom Lane wrote:
> Bruce Momjian <pgman@candle.pha.pa.us> writes:
> > Recreating the directory only happens on WIN32, where rmdir doesn't
> > allow you to only delete files and subdirectories and not the parent
> > directory.  Non-Win32 does rm -rf dir/*.
>
> I think we should forget about invoking rm as a subprocess at all, and
> just do the recursive directory walk and unlinks for ourselves.  We
> already have code to do this for copy in copydir.c, and unlink would not
> be any longer.  We will probably be forced into implementing database
> removal for ourselves rather than by 'rm' hacks anyway as soon as
> tablespaces come to pass; so why contort initdb's behavior for a very
> transient implementation savings?

If we want to do that, fine, but I don't want to force the change just
for Win32.

--
  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

Re: initdb in C

От
Bruce Momjian
Дата:
Tom Lane wrote:
> Bruce Momjian <pgman@candle.pha.pa.us> writes:
> >> Consider also the strong likelihood that the data directory's parent
> >> directory is owned by root,
>
> > Again, this directory recreate happens only on Win32, an I thought it
> > would be OK there.
>
> Windows has no concept of directory permissions at all?  I thought the
> more recent versions had at least rudimentary permissions.

I found "del" works for what we need.  I have posted the new code
already.

--
  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

Re: initdb in C

От
Andrew Dunstan
Дата:

Tom Lane wrote:

>Bruce Momjian <pgman@candle.pha.pa.us> writes:
>
>
>>>Consider also the strong likelihood that the data directory's parent
>>>directory is owned by root,
>>>
>>>
>
>
>
>>Again, this directory recreate happens only on Win32, an I thought it
>>would be OK there.
>>
>>
>
>Windows has no concept of directory permissions at all?  I thought the
>more recent versions had at least rudimentary permissions.
>


More than rudimentary on the server versions.

I found this info at http://www.cs.wayne.edu/labPages/modes.htm :


    Windows ACLs

Windows NT and Windows 2000 uses Access Control Lists or ACLs to
determine what operations a user may perform on a file.  Windows ACLs
allow one to set permissions with finer control that does the Unix file
mode.  For example, one can all[ow] a user to append data to a file as
opposed to overwiting the file.  ACLs also allow one to permit specific
users to change the permissions on a file.  Perhaps the biggest
difference is that ACLs allow us to accord permissions on a user-by-user
basis, rather than the three categories of users permitted by Unix file
systems.

This info applies to directories as well as plain files AFAIK

cheers

andrew







Re: initdb in C

От
Bruce Momjian
Дата:
Also, I see this at the top of the code:

     * 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

Can I remove the first copyright?

--
  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

Re: initdb in C

От
Andrew Dunstan
Дата:

Bruce Momjian wrote:

>Also, I see this at the top of the code:
>
>     * 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
>
>Can I remove the first copyright?
>
>

Sure. I wasn't sure what our conventions were on that.

cheers

andrew


Re: [pgsql-hackers-win32] initdb in C

От
Andrew Dunstan
Дата:

Bruce Momjian wrote:

>
>
>I ran some tests using XP "CMD" and found:
>
>    "C:\test"
>and
>    "\test"
>
>works but:
>
>    "test"
>
>does not work.  Since I see that the output always has a leading path,
>

On Windows, pgpath is guaranteed to be a full path (see call to
expanded_path() ) exactly so it works inside quotes using the Windows
command processor.

> I
>have modified the code to do:
>
>    printf("\nSuccess. You can now start the database server using:\n\n"
>           "    %s%s%s/postmaster -D %s%s%s\n"
>           "or\n"
>           "    %s%s%s/pg_ctl -D %s%s%s -l logfile start\n\n",
>            QUOTE_PATH, pgpath, QUOTE_PATH, QUOTE_PATH, pg_data, QUOTE_PATH,
>            QUOTE_PATH, pgpath, QUOTE_PATH, QUOTE_PATH, pg_data, QUOTE_PATH);
>
>I am attaching the updated initdb.c file.
>
>
>

The problem with this is that you now have 2 quoted strings. This is
exactly the problem that I solved inside initdb by passing pg_data via
the environment rather than on the command line. "help cmd" on XP gives
you this info:

If /C or /K is specified, then the remainder of the command line after
the switch is processed as a command line, where the following logic is
used to process quote (") characters:

    1.  If all of the following conditions are met, then quote characters
        on the command line are preserved:

        - no /S switch
        - exactly two quote characters
        - no special characters between the two quote characters,
          where special is one of: &<>()@^|
        - there are one or more whitespace characters between the
          the two quote characters
        - the string between the two quote characters is the name
          of an executable file.

    2.  Otherwise, old behavior is to see if the first character is
        a quote character and if so, strip the leading character and
        remove the last quote character on the command line, preserving
        any text after the last quote character.

It is amazingly brain dead and cost me hours and hours of grief trying
to work out WTF was going on.

Offhand I can't think of a simple guaranteed to work command line for
the Windows message.

In the last resort we might need to look at having initdb create a .bat
file or two for us.

:-(((

cheers

andrew


Re: [pgsql-hackers-win32] initdb in C

От
Andrew Dunstan
Дата:

Andrew Dunstan wrote:

>
>
>
> The problem with this is that you now have 2 quoted strings.


I take this back. You *can* have multiple quoted strings on the command
line - it just doesn't work in popen() because it uses the /C flag.

Sorry for my confusion.

cheers

andrew


Re: [pgsql-hackers-win32] initdb in C

От
Bruce Momjian
Дата:
Andrew Dunstan wrote:
>
>
> Andrew Dunstan wrote:
>
> >
> >
> >
> > The problem with this is that you now have 2 quoted strings.
>
>
> I take this back. You *can* have multiple quoted strings on the command
> line - it just doesn't work in popen() because it uses the /C flag.

Oh, OK, that's good.

--
  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

Re: initdb in C

От
Bruce Momjian
Дата:
Patch applied.  We now have a C version of initdb!

---------------------------------------------------------------------------

Andrew Dunstan wrote:
>
>
> Bruce Momjian wrote:
>
> >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.
> >
> >
>
> My comments:
>
> I have no problem with shelling out to rmdir - although my goal was to
> avoid shelling out to anything other than postgres itself. I think
> recreating the datadir if we didn't create it initially should be OK in
> that case, and it makes the code simpler. Not handling dot files in the
> Unix case should also be fine, as we know the directory was empty to
> start with and we don't create any.
>
> Regarding the #endif comments you removed - Peter had asked me to put
> them in. See
> http://archives.postgresql.org/pgsql-hackers/2003-10/msg00352.php
>
> You put a comment on canonicalise_path() asking if it is needed. The
> short answer is very much "yes". The Windows command processor will
> accept suitably quoted paths with forward slashes, but barfs badly with
> mixed forward and back slashes. (See recent discussion on
> hackers-win32).Removing the trailing slash on a path means we never get
> ugly double slashes. Feel free to put that info in as a comment if you
> think it is needed.
>
> The changes you made for the final message are not going to work for
> Windows - if pgpath or pg_data contain spaces we need quotes (even on
> Unix, although there most people know better than to put spaces in
> names). Also see above about mixed slashes. Also, putting a \ before
> pg_data will be nasty if it starts with a drive or network specifier -
> or even without (you might turn a directory specifier into a network
> drive specifier). It's just a message, though, and we shouldn't hold up
> for that - we can patch it to get it right.
>
> (Getting the path and slash thing working portably was by far the
> biggest headache in this project - the rest was just grunt work for the
> most part, although I learned a few interesting things.)
>
> Otherwise I'm fine with this.
>
> cheers
>
> andrew
>
>
>
>
> ---------------------------(end of broadcast)---------------------------
> TIP 6: Have you searched our list archives?
>
>                http://archives.postgresql.org
>

--
  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

Re: [pgsql-hackers-win32] initdb in C

От
Tom Lane
Дата:
What's become of initdb's initial note about who you are?

$ initdb
The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.
... etc etc...

The first two lines of printout don't seem to be in the "highly
compatible" C version.

            regards, tom lane

Re: [pgsql-hackers-win32] initdb in C

От
Andrew Dunstan
Дата:
Apologies. Here's the patch. There may be other very minor differences
in wording, too. But if that's all I missed I won't think I did too
badly :-)

cheers

andrew

Tom Lane wrote:

>What's become of initdb's initial note about who you are?
>
>$ initdb
>The files belonging to this database system will be owned by user "postgres".
>This user must also own the server process.
>... etc etc...
>
>The first two lines of printout don't seem to be in the "highly
>compatible" C version.
>
>
>
Index: initdb.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/bin/initdb/initdb.c,v
retrieving revision 1.4
diff -c -w -r1.4 initdb.c
*** initdb.c    13 Nov 2003 01:36:00 -0000    1.4
--- initdb.c    13 Nov 2003 04:15:33 -0000
***************
*** 2324,2329 ****
--- 2324,2333 ----
      pqsignal(SIGTERM, trapsig);
  #endif

+     printf("The files belonging to this database system will be owned by user %s.\n"
+            "This user must also own the server process.\n\n,",
+            efective_user);
+
      /* clear this we'll use it in a few lines */
      errno = 0;


Re: [pgsql-hackers-win32] initdb in C

От
Andrew Dunstan
Дата:
ARRGGHH! It has a typo!. new patch shortly.

Andrew Dunstan wrote:

> Apologies. Here's the patch. There may be other very minor differences
> in wording, too. But if that's all I missed I won't think I did too
> badly :-)
>
> cheers
>
> andrew
>
> Tom Lane wrote:
>
>> What's become of initdb's initial note about who you are?
>>
>> $ initdb
>> The files belonging to this database system will be owned by user
>> "postgres".
>> This user must also own the server process.
>> ... etc etc...
>>
>> The first two lines of printout don't seem to be in the "highly
>> compatible" C version.
>>
>>
>>
>
>
>


Re: [pgsql-hackers-win32] initdb in C

От
Andrew Dunstan
Дата:
OK, this works.

cheers

andrew

Andrew Dunstan wrote:

>
> ARRGGHH! It has a typo!. new patch shortly.
>
> Andrew Dunstan wrote:
>
>> Apologies. Here's the patch. There may be other very minor
>> differences in wording, too. But if that's all I missed I won't think
>> I did too badly :-)
>>
>> cheers
>>
>> andrew
>>
>> Tom Lane wrote:
>>
>>> What's become of initdb's initial note about who you are?
>>>
>>> $ initdb
>>> The files belonging to this database system will be owned by user
>>> "postgres".
>>> This user must also own the server process.
>>> ... etc etc...
>>>
>>> The first two lines of printout don't seem to be in the "highly
>>> compatible" C version.
>>>
Index: initdb.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/bin/initdb/initdb.c,v
retrieving revision 1.4
diff -c -w -r1.4 initdb.c
*** initdb.c    13 Nov 2003 01:36:00 -0000    1.4
--- initdb.c    13 Nov 2003 04:32:18 -0000
***************
*** 2324,2329 ****
--- 2324,2334 ----
      pqsignal(SIGTERM, trapsig);
  #endif

+     printf("The files belonging to this database system will be owned "
+            "by user \"%s\".\n"
+            "This user must also own the server process.\n\n,",
+            effective_user);
+
      /* clear this we'll use it in a few lines */
      errno = 0;


Re: [pgsql-hackers-win32] initdb in C

От
Andrew Dunstan
Дата:
Note to self: stop working when brain is clearly malfunctioning.

*This* patch works. Please disregard previous.

thanks

andrew

Andrew Dunstan wrote:

>
> OK, this works.
>
> cheers
>
> andrew
>
> Andrew Dunstan wrote:
>
>>
>> ARRGGHH! It has a typo!. new patch shortly.
>>
>> Andrew Dunstan wrote:
>>
>
? initdb
Index: initdb.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/bin/initdb/initdb.c,v
retrieving revision 1.4
diff -c -w -r1.4 initdb.c
*** initdb.c    13 Nov 2003 01:36:00 -0000    1.4
--- initdb.c    13 Nov 2003 12:52:21 -0000
***************
*** 2275,2280 ****
--- 2275,2285 ----
      check_input(features_file);
      check_input(system_views_file);

+     printf("The files belonging to this database system will be owned "
+            "by user \"%s\".\n"
+            "This user must also own the server process.\n\n",
+            effective_user);
+
      setlocales();

      if (strcmp(lc_ctype, lc_collate) == 0 &&

Re: [pgsql-hackers-win32] initdb in C

От
Bruce Momjian
Дата:
Patch applied.  Thanks.

---------------------------------------------------------------------------


Andrew Dunstan wrote:
> Note to self: stop working when brain is clearly malfunctioning.
>
> *This* patch works. Please disregard previous.
>
> thanks
>
> andrew
>
> Andrew Dunstan wrote:
>
> >
> > OK, this works.
> >
> > cheers
> >
> > andrew
> >
> > Andrew Dunstan wrote:
> >
> >>
> >> ARRGGHH! It has a typo!. new patch shortly.
> >>
> >> Andrew Dunstan wrote:
> >>
> >

> ? initdb
> Index: initdb.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql-server/src/bin/initdb/initdb.c,v
> retrieving revision 1.4
> diff -c -w -r1.4 initdb.c
> *** initdb.c    13 Nov 2003 01:36:00 -0000    1.4
> --- initdb.c    13 Nov 2003 12:52:21 -0000
> ***************
> *** 2275,2280 ****
> --- 2275,2285 ----
>       check_input(features_file);
>       check_input(system_views_file);
>
> +     printf("The files belonging to this database system will be owned "
> +            "by user \"%s\".\n"
> +            "This user must also own the server process.\n\n",
> +            effective_user);
> +
>       setlocales();
>
>       if (strcmp(lc_ctype, lc_collate) == 0 &&

>
> ---------------------------(end of broadcast)---------------------------
> TIP 6: Have you searched our list archives?
>
>                http://archives.postgresql.org

--
  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