{"thread":{"id":"25715","subject":"[PATCH, maint] setup: make sure git_dir path is in a permanent buffer, getenv(3) case","startedAt":"2010-11-11T18:08:23Z","lastAt":"2010-11-12T18:59:49Z","messageCount":6,"participants":["Kirill Smelkov","Jonathan Nieder"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"155721","messageId":"1289498903-18413-1-git-send-email-kirr@mns.spb.ru","threadId":"25715","inReplyTo":null,"subject":"[PATCH, maint] setup: make sure git_dir path is in a permanent buffer, getenv(3) case","fromName":"Kirill Smelkov","fromEmail":"kirr@mns.spb.ru","sentAt":"2010-11-11T18:08:23Z","receivedAt":"2010-11-11T18:08:23Z","isPatch":true,"sender":{"key":"kirr@navytux.spb.ru","avatar":"https://gravatar.com/avatar/cf3445fdad1849941e17ab25bf1ee7c5ea1be2deb444be25ff5f36b0e50a985f?d=mp&s=160"},"body":"getenv(3) returns not-permanent buffer which may be changed by e.g.\nputenv(3) call (*).\n\nIn practice I've noticed this when trying to do `git commit -m abc`\ninside msysgit under wine, getting\n\n    $ git commit -m abc\n    fatal: could not open 'DIR=.git/COMMIT_EDITMSG': No such file or directory\n                           ^^^^\n    (notice introduced 'DIR=' artifact.)\n\nThe problem was showing itself only with -m option, and actually, as\ndebugging showed, originally\n\n    git_dir = getenv(\"GIT_DIR\")\n\nreturned pointer to\n\n        \"GIT_DIR=.git\\0\"\n                 ^\n               git_dir\n\n, we stored it in git_dir, than, after processing -m git-commit option,\nwe did setenv(\"GIT_EDITOR\", \":\") which as (*) says changed environment\nvariables memory layout - something like this\n\n       \"...\\0GIT_DIR=.git\\0\"\n                 ^\n               git_dir\n\nand oops - we got wrong git_dir.\n\nAvoid that by strdupping getenv(\"GIT_DIR\") result like we did in 06f354\n(setup: make sure git dir path is in a permanent buffer). Unfortunately\nthis also shows that other getenv usage inside git needs auditing...\n\n(*) from man 3 getenv:\n\n       The implementation of getenv() is not required to  be  reentrant.   The\n       string  pointed  to  by  the return value of getenv() may be statically\n       allocated, and can be  modified  by  a  subsequent  call  to  getenv(),\n       putenv(3), setenv(3), or unsetenv(3).\n\nCc: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Kirill Smelkov <kirr@mns.spb.ru>\n---\n environment.c |    1 +\n 1 files changed, 1 insertions(+), 0 deletions(-)\n\ndiff --git a/environment.c b/environment.c\nindex eaf908b..d5021e8 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -88,6 +88,7 @@ const char * const local_repo_env[LOCAL_REPO_ENV_SIZE + 1] = {\n static void setup_git_env(void)\n {\n \tgit_dir = getenv(GIT_DIR_ENVIRONMENT);\n+\tgit_dir = git_dir ? xstrdup(git_dir) : NULL;\n \tif (!git_dir) {\n \t\tgit_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);\n \t\tgit_dir = git_dir ? xstrdup(git_dir) : NULL;\n-- \n1.7.3.2.161.g3089c\n"},{"id":"155723","messageId":"20101111181728.GF16972@burratino","threadId":"25715","inReplyTo":"1289498903-18413-1-git-send-email-kirr@mns.spb.ru","subject":"Re: [PATCH, maint] setup: make sure git_dir path is in a permanent buffer, getenv(3) case","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-11-11T18:17:28Z","receivedAt":"2010-11-11T18:17:28Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Kirill Smelkov wrote:\n\n> getenv(3) returns not-permanent buffer which may be changed by e.g.\n> putenv(3) call (*).\n\nYikes.  Thanks for the example.\n\n> --- a/environment.c\n> +++ b/environment.c\n> @@ -88,6 +88,7 @@ const char * const local_repo_env[LOCAL_REPO_ENV_SIZE + 1] = {\n>  static void setup_git_env(void)\n>  {\n>  \tgit_dir = getenv(GIT_DIR_ENVIRONMENT);\n> +\tgit_dir = git_dir ? xstrdup(git_dir) : NULL;\n>  \tif (!git_dir) {\n>  \t\tgit_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);\n>  \t\tgit_dir = git_dir ? xstrdup(git_dir) : NULL;\n\nMaybe we can avoid (some) repetition like this?\n\ndiff --git a/environment.c b/environment.c\nindex de5581f..942f1e4 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -87,25 +87,31 @@ const char * const local_repo_env[LOCAL_REPO_ENV_SIZE + 1] = {\n static void setup_git_env(void)\n {\n \tgit_dir = getenv(GIT_DIR_ENVIRONMENT);\n-\tif (!git_dir) {\n-\t\tgit_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);\n-\t\tgit_dir = git_dir ? xstrdup(git_dir) : NULL;\n-\t}\n \tif (!git_dir)\n+\t\tgit_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);\n+\tif (git_dir)\n+\t\tgit_dir = xstrdup(git_dir);\n+\telse\n \t\tgit_dir = DEFAULT_GIT_DIR_ENVIRONMENT;\n+\n \tgit_object_dir = getenv(DB_ENVIRONMENT);\n-\tif (!git_object_dir) {\n-\t\tgit_object_dir = xmalloc(strlen(git_dir) + 9);\n-\t\tsprintf(git_object_dir, \"%s/objects\", git_dir);\n-\t}\n+\tif (git_object_dir)\n+\t\tgit_object_dir = xstrdup(git_object_dir);\n+\telse\n+\t\tgit_object_dir = git_pathdup(\"objects\");\n+\n \tgit_index_file = getenv(INDEX_ENVIRONMENT);\n-\tif (!git_index_file) {\n-\t\tgit_index_file = xmalloc(strlen(git_dir) + 7);\n-\t\tsprintf(git_index_file, \"%s/index\", git_dir);\n-\t}\n+\tif (git_index_file)\n+\t\tgit_index_file = xstrdup(git_index_file);\n+\telse\n+\t\tgit_index_file = git_pathdup(\"index\");\n+\n \tgit_graft_file = getenv(GRAFT_ENVIRONMENT);\n-\tif (!git_graft_file)\n+\tif (git_graft_file)\n+\t\tgit_graft_file = xstrdup(git_graft_file);\n+\telse\n \t\tgit_graft_file = git_pathdup(\"info/grafts\");\n+\n \tif (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))\n \t\tread_replace_refs = 0;\n }\n"},{"id":"155779","messageId":"20101112140329.GA29604@tugrik.mns.mnsspb.ru","threadId":"25715","inReplyTo":"20101111181728.GF16972@burratino","subject":"Re: [PATCH, maint] setup: make sure git_dir path is in a permanent buffer, getenv(3) case","fromName":"Kirill Smelkov","fromEmail":"kirr@mns.spb.ru","sentAt":"2010-11-12T14:03:29Z","receivedAt":"2010-11-12T14:03:29Z","isPatch":true,"sender":{"key":"kirr@navytux.spb.ru","avatar":"https://gravatar.com/avatar/cf3445fdad1849941e17ab25bf1ee7c5ea1be2deb444be25ff5f36b0e50a985f?d=mp&s=160"},"body":"On Thu, Nov 11, 2010 at 12:17:28PM -0600, Jonathan Nieder wrote:\n> Kirill Smelkov wrote:\n> \n> > getenv(3) returns not-permanent buffer which may be changed by e.g.\n> > putenv(3) call (*).\n> \n> Yikes.  Thanks for the example.\n\nNevermind. However it was not so fun to debug :)\n\n> > --- a/environment.c\n> > +++ b/environment.c\n> > @@ -88,6 +88,7 @@ const char * const local_repo_env[LOCAL_REPO_ENV_SIZE + 1] = {\n> >  static void setup_git_env(void)\n> >  {\n> >  \tgit_dir = getenv(GIT_DIR_ENVIRONMENT);\n> > +\tgit_dir = git_dir ? xstrdup(git_dir) : NULL;\n> >  \tif (!git_dir) {\n> >  \t\tgit_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);\n> >  \t\tgit_dir = git_dir ? xstrdup(git_dir) : NULL;\n> \n> Maybe we can avoid (some) repetition like this?\n> \n> diff --git a/environment.c b/environment.c\n> index de5581f..942f1e4 100644\n> --- a/environment.c\n> +++ b/environment.c\n> @@ -87,25 +87,31 @@ const char * const local_repo_env[LOCAL_REPO_ENV_SIZE + 1] = {\n>  static void setup_git_env(void)\n>  {\n>  \tgit_dir = getenv(GIT_DIR_ENVIRONMENT);\n> -\tif (!git_dir) {\n> -\t\tgit_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);\n> -\t\tgit_dir = git_dir ? xstrdup(git_dir) : NULL;\n> -\t}\n>  \tif (!git_dir)\n> +\t\tgit_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);\n> +\tif (git_dir)\n> +\t\tgit_dir = xstrdup(git_dir);\n> +\telse\n>  \t\tgit_dir = DEFAULT_GIT_DIR_ENVIRONMENT;\n> +\n>  \tgit_object_dir = getenv(DB_ENVIRONMENT);\n> -\tif (!git_object_dir) {\n> -\t\tgit_object_dir = xmalloc(strlen(git_dir) + 9);\n> -\t\tsprintf(git_object_dir, \"%s/objects\", git_dir);\n> -\t}\n> +\tif (git_object_dir)\n> +\t\tgit_object_dir = xstrdup(git_object_dir);\n> +\telse\n> +\t\tgit_object_dir = git_pathdup(\"objects\");\n> +\n>  \tgit_index_file = getenv(INDEX_ENVIRONMENT);\n> -\tif (!git_index_file) {\n> -\t\tgit_index_file = xmalloc(strlen(git_dir) + 7);\n> -\t\tsprintf(git_index_file, \"%s/index\", git_dir);\n> -\t}\n> +\tif (git_index_file)\n> +\t\tgit_index_file = xstrdup(git_index_file);\n> +\telse\n> +\t\tgit_index_file = git_pathdup(\"index\");\n> +\n>  \tgit_graft_file = getenv(GRAFT_ENVIRONMENT);\n> -\tif (!git_graft_file)\n> +\tif (git_graft_file)\n> +\t\tgit_graft_file = xstrdup(git_graft_file);\n> +\telse\n>  \t\tgit_graft_file = git_pathdup(\"info/grafts\");\n> +\n\nTo me it gets hairy and we don't cover all and even most getenv cases.\nLook e.g. in commit.c:\n\n    static void determine_author_info(void)\n    {\n            char *name, *email, *date;\n    \n            name = getenv(\"GIT_AUTHOR_NAME\");\n            email = getenv(\"GIT_AUTHOR_EMAIL\");\n            date = getenv(\"GIT_AUTHOR_DATE\");\n\n            /* ... */\n\n            if (signoff) {\n                    struct strbuf sob = STRBUF_INIT;\n                    int i;\n    \n                    strbuf_addstr(&sob, sign_off_header);\n                    strbuf_addstr(&sob, fmt_name(getenv(\"GIT_COMMITTER_NAME\"),\n                                                 getenv(\"GIT_COMMITTER_EMAIL\")));\n\n            /* ... */\n\nnotes.c:\n\n    struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd)\n    {\n            struct notes_rewrite_cfg *c = xmalloc(sizeof(struct notes_rewrite_cfg));\n            const char *rewrite_mode_env = getenv(GIT_NOTES_REWRITE_MODE_ENVIRONMENT);\n            const char *rewrite_refs_env = getenv(GIT_NOTES_REWRITE_REF_ENVIRONMENT);\n\n\neditor.c:\n\n    const char *git_editor(void)\n    {\n            const char *editor = getenv(\"GIT_EDITOR\");\n            const char *terminal = getenv(\"TERM\");\n\nhttp-backend.c:\n\n    static void run_service(const char **argv)\n    {\n            const char *encoding = getenv(\"HTTP_CONTENT_ENCODING\");\n            const char *user = getenv(\"REMOTE_USER\");\n            const char *host = getenv(\"REMOTE_ADDR\");\n\n\netc...\n\n\nTo me, it's very unfortunate that subsequent getenv() could overwrite\nprevious getenv() result, but according to `man 3 getenv` all these\nplaces are buggy.\n\nMaybe we'll need something like our own xgetenv() which will keep vars\nin some kind of hash tab so that get/put on other vars do not interfere\nwith what was originally returned by xgetenv().\n\nI don't know.\n\nUnfortunately I can't afford myself to dive into all this, so please\nchoose what you like more.\n\n\nThanks,\nKirill\n"},{"id":"155786","messageId":"20101112160332.GB15141@burratino","threadId":"25715","inReplyTo":"20101112140329.GA29604@tugrik.mns.mnsspb.ru","subject":"Re: [PATCH, maint] setup: make sure git_dir path is in a permanent buffer, getenv(3) case","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-11-12T16:03:32Z","receivedAt":"2010-11-12T16:03:32Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Kirill Smelkov wrote:\n\n>     static void run_service(const char **argv)\n>     {\n>             const char *encoding = getenv(\"HTTP_CONTENT_ENCODING\");\n>             const char *user = getenv(\"REMOTE_USER\");\n>             const char *host = getenv(\"REMOTE_ADDR\");\n> \n> \n> etc...\n> \n> \n> To me, it's very unfortunate that subsequent getenv() could overwrite\n> previous getenv() result, but according to `man 3 getenv` all these\n> places are buggy.\n\nRight, but do we know of any platforms that work that way currently?\nWe could make getenv() rotate between a few buffers on such platforms\n(probably 10 or so would take care of the longest runs).\n\n> Maybe we'll need something like our own xgetenv() which will keep vars\n> in some kind of hash tab so that get/put on other vars do not interfere\n> with what was originally returned by xgetenv().\n\nFor examples that store the result like you pointed out (which store the\nresult from getenv), something like that would be needed if we want\nthem to work on platforms where putenv shifts everything.\n\n> Unfortunately I can't afford myself to dive into all this, so please\n> choose what you like more.\n\nI think we ought to fix this properly in the end.  But if you want a\nquick workaround, maybe the vcs-svn/string_pool lib could help you.\n\nHope that helps,\nJonathan\n"},{"id":"155791","messageId":"20101112172028.GA3941@tugrik.mns.mnsspb.ru","threadId":"25715","inReplyTo":"20101112160332.GB15141@burratino","subject":"Re: [PATCH, maint] setup: make sure git_dir path is in a permanent buffer, getenv(3) case","fromName":"Kirill Smelkov","fromEmail":"kirr@mns.spb.ru","sentAt":"2010-11-12T17:20:28Z","receivedAt":"2010-11-12T17:20:28Z","isPatch":true,"sender":{"key":"kirr@navytux.spb.ru","avatar":"https://gravatar.com/avatar/cf3445fdad1849941e17ab25bf1ee7c5ea1be2deb444be25ff5f36b0e50a985f?d=mp&s=160"},"body":"On Fri, Nov 12, 2010 at 10:03:32AM -0600, Jonathan Nieder wrote:\n> Kirill Smelkov wrote:\n> \n> >     static void run_service(const char **argv)\n> >     {\n> >             const char *encoding = getenv(\"HTTP_CONTENT_ENCODING\");\n> >             const char *user = getenv(\"REMOTE_USER\");\n> >             const char *host = getenv(\"REMOTE_ADDR\");\n> > \n> > \n> > etc...\n> > \n> > \n> > To me, it's very unfortunate that subsequent getenv() could overwrite\n> > previous getenv() result, but according to `man 3 getenv` all these\n> > places are buggy.\n> \n> Right, but do we know of any platforms that work that way currently?\n\nI don't. Actually I was really surprised after reading getenv manual\nabout that.\n\n> We could make getenv() rotate between a few buffers on such platforms\n> (probably 10 or so would take care of the longest runs).\n\nI think it would be hard to get right (is 10 enough? on which platform?\nthis rarely happens after all...), and also why introduce special case?\n\n> > Maybe we'll need something like our own xgetenv() which will keep vars\n> > in some kind of hash tab so that get/put on other vars do not interfere\n> > with what was originally returned by xgetenv().\n> \n> For examples that store the result like you pointed out (which store the\n> result from getenv), something like that would be needed if we want\n> them to work on platforms where putenv shifts everything.\n> \n> > Unfortunately I can't afford myself to dive into all this, so please\n> > choose what you like more.\n> \n> I think we ought to fix this properly in the end.  But if you want a\n> quick workaround, maybe the vcs-svn/string_pool lib could help you.\n\nNo, I'm not in a hurry - better to fix this properly. Though personally,\nI've already scratched my itch here.\n\n\nThanks,\nKirill\n"},{"id":"155793","messageId":"20101112185949.GA18064@burratino","threadId":"25715","inReplyTo":"20101112172028.GA3941@tugrik.mns.mnsspb.ru","subject":"[PATCH] tests: add GETENV_POISON option to simulate unfriendly getenv()","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-11-12T18:59:49Z","receivedAt":"2010-11-12T18:59:49Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Kirill Smelkov wrote:\n> On Fri, Nov 12, 2010 at 10:03:32AM -0600, Jonathan Nieder wrote:\n\n>> Right, but do we know of any platforms that work that way currently?\n>\n> I don't. Actually I was really surprised after reading getenv manual\n> about that.\n\nHere's an artificial one.  If someone shows up interested in cleaning\nthe getenv() usage, something like this could make it easier to\nmaintain the result.\n\nBefore then, it provides a chance to see how invasive the changes\nwould need to be to support such a theoretical unfriendly platform.\nThe results don't look so good.\n\n> No, I'm not in a hurry - better to fix this properly. Though personally,\n> I've already scratched my itch here.\n\nThanks for reporting.\n\n-- 8< --\nSubject: add GETENV_POISON option to simulate unfriendly getenv()\n\nTraditionally, getenv() returns a pointer into the environment structure,\nand on typical platforms the pointed-to value remains valid until that\nenvironment variable gets a new value.\n\nOn some platforms (e.g., wine), unfortunately, it does not remain valid\neven after an unrelated setenv() call.\n\nThe standard even allows getenv to return its result in a static buffer\n(meaning it would not remain valid after another getenv() call).  So if\nwe want to be maximally portable, we should always copy the return\nvalue from getenv() before fetching another value from the environment.\n\nThis patch adds a GETENV_POISON option to demonstrate how hard that\nwould be.  When GETENV_POISON is set, getenv is replaced with a wrapper\nthat clobbers its old return value after each call, in the hope that\nbroken callers might notice.\n\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n Makefile               |    4 ++++\n cache.h                |   20 --------------------\n compat/getenv-poison.c |   44 ++++++++++++++++++++++++++++++++++++++++++++\n git-compat-util.h      |   25 +++++++++++++++++++++++++\n 4 files changed, 73 insertions(+), 20 deletions(-)\n create mode 100644 compat/getenv-poison.c\n\ndiff --git a/Makefile b/Makefile\nindex 1f1ce04..e16d10e 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1443,6 +1443,10 @@ ifdef INTERNAL_QSORT\n \tCOMPAT_CFLAGS += -DINTERNAL_QSORT\n \tCOMPAT_OBJS += compat/qsort.o\n endif\n+ifdef GETENV_POISON\n+\tCOMPAT_CFLAGS += -DGETENV_POISON\n+\tCOMPAT_OBJS += compat/getenv-poison.o\n+endif\n ifdef RUNTIME_PREFIX\n \tCOMPAT_CFLAGS += -DRUNTIME_PREFIX\n endif\ndiff --git a/cache.h b/cache.h\nindex 33decd9..574dc8f 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -438,26 +438,6 @@ extern void verify_non_filename(const char *prefix, const char *name);\n \n extern int init_db(const char *template_dir, unsigned int flags);\n \n-#define alloc_nr(x) (((x)+16)*3/2)\n-\n-/*\n- * Realloc the buffer pointed at by variable 'x' so that it can hold\n- * at least 'nr' entries; the number of entries currently allocated\n- * is 'alloc', using the standard growing factor alloc_nr() macro.\n- *\n- * DO NOT USE any expression with side-effect for 'x', 'nr', or 'alloc'.\n- */\n-#define ALLOC_GROW(x, nr, alloc) \\\n-\tdo { \\\n-\t\tif ((nr) > alloc) { \\\n-\t\t\tif (alloc_nr(alloc) < (nr)) \\\n-\t\t\t\talloc = (nr); \\\n-\t\t\telse \\\n-\t\t\t\talloc = alloc_nr(alloc); \\\n-\t\t\tx = xrealloc((x), alloc * sizeof(*(x))); \\\n-\t\t} \\\n-\t} while (0)\n-\n /* Initialize and use the cache information */\n extern int read_index(struct index_state *);\n extern int read_index_preload(struct index_state *, const char **pathspec);\ndiff --git a/compat/getenv-poison.c b/compat/getenv-poison.c\nnew file mode 100644\nindex 0000000..a88ec85\n--- /dev/null\n+++ b/compat/getenv-poison.c\n@@ -0,0 +1,44 @@\n+/*\n+ * getenv(3) says:\n+ *\tThe implementation of getenv() is not required to be reentrant.\n+ *\tThe string pointed to by the return value of getenv() may be\n+ *\tstatically allocated, and can be modified by a subsequent call\n+ *\tto getenv(), putenv(3), setenv(3), or unsetenv(3).\n+ *\n+ * This file provides an unpleasant but conformant getenv()\n+ * implementation, for tests.\n+ */\n+#include \"../git-compat-util.h\"\n+#undef getenv\n+\n+static void poison_buffer(char *buf, size_t buflen)\n+{\n+\tif (!buflen)\n+\t\treturn;\n+\tmemset(buf, '\\xa5', buflen - 1);\n+\tbuf[buflen - 1] = '\\0';\n+}\n+\n+static void fill_buffer(char **buf, size_t *alloc, const char *str)\n+{\n+\tsize_t len = strlen(str) + 1;\n+\tALLOC_GROW(*buf, len, *alloc);\n+\tmemcpy(*buf, str, len);\n+}\n+\n+char *gitgetenv(const char *name)\n+{\n+\tstatic char *envvar_array[2];\n+\tstatic size_t envvar_len[2];\n+\tstatic unsigned int index;\n+\tconst char *value;\n+\n+\tpoison_buffer(envvar_array[index], envvar_len[index]);\n+\tindex = (index + 1) % 2;\n+\n+\tvalue = getenv(name);\n+\tif (!value)\n+\t\treturn NULL;\n+\tfill_buffer(&envvar_array[index], &envvar_len[index], value);\n+\treturn envvar_array[index];\n+}\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 2af8d3e..1f6a2ce 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -298,6 +298,11 @@ extern ssize_t read_in_full(int fd, void *buf, size_t count);\n extern int gitsetenv(const char *, const char *, int);\n #endif\n \n+#ifdef GETENV_POISON\n+#define getenv gitgetenv\n+extern char *gitgetenv(const char *name);\n+#endif\n+\n #ifdef NO_MKDTEMP\n #define mkdtemp gitmkdtemp\n extern char *gitmkdtemp(char *);\n@@ -421,6 +426,26 @@ static inline int has_extension(const char *filename, const char *ext)\n \treturn len > extlen && !memcmp(filename + len - extlen, ext, extlen);\n }\n \n+#define alloc_nr(x) (((x)+16)*3/2)\n+\n+/*\n+ * Realloc the buffer pointed at by variable 'x' so that it can hold\n+ * at least 'nr' entries; the number of entries currently allocated\n+ * is 'alloc', using the standard growing factor alloc_nr() macro.\n+ *\n+ * DO NOT USE any expression with side-effect for 'x', 'nr', or 'alloc'.\n+ */\n+#define ALLOC_GROW(x, nr, alloc) \\\n+\tdo { \\\n+\t\tif ((nr) > alloc) { \\\n+\t\t\tif (alloc_nr(alloc) < (nr)) \\\n+\t\t\t\talloc = (nr); \\\n+\t\t\telse \\\n+\t\t\t\talloc = alloc_nr(alloc); \\\n+\t\t\tx = xrealloc((x), alloc * sizeof(*(x))); \\\n+\t\t} \\\n+\t} while (0)\n+\n /* Sane ctype - no locale, and works with signed chars */\n #undef isascii\n #undef isspace\n-- \n1.7.2.3.557.gab647.dirty\n"}]}