cosmopolitan/libc/calls/mkntenvblock.c
Jōshin e16a7d8f3b
flip et / noet in modelines
`et` means `expandtab`.

```sh
rg 'vi: .* :vi' -l -0 | \
  xargs -0 sed -i '' 's/vi: \(.*\) et\(.*\)  :vi/vi: \1 xoet\2:vi/'
rg 'vi: .*  :vi' -l -0 | \
  xargs -0 sed -i '' 's/vi: \(.*\)noet\(.*\):vi/vi: \1et\2  :vi/'
rg 'vi: .*  :vi' -l -0 | \
  xargs -0 sed -i '' 's/vi: \(.*\)xoet\(.*\):vi/vi: \1noet\2:vi/'
```
2023-12-07 22:17:11 -05:00

165 lines
5.7 KiB
C

/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2020 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
│ above copyright notice and this permission notice appear in all copies. │
│ │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/assert.h"
#include "libc/intrin/getenv.internal.h"
#include "libc/mem/alloca.h"
#include "libc/proc/ntspawn.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
#define ToUpper(c) ((c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 'A' : (c))
struct EnvBuilder {
char *buf;
char **var;
int bufi;
int vari;
};
static inline int IsAlpha(int c) {
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
}
static textwindows int Compare(const char *l, const char *r) {
int a, b;
size_t i = 0;
for (;;) {
a = l[i] & 255;
b = r[i] & 255;
if (a == '=') a = 0;
if (b == '=') b = 0;
if (a != b || !b) break;
++i;
}
return a - b;
}
static textwindows int InsertString(struct EnvBuilder *env, const char *str) {
int c, i, cmp;
char *var, *path = 0;
if (!str) return 0;
// copy key=val to buf
var = env->buf + env->bufi;
do {
c = *str++;
if (env->bufi + 2 > 32767) return e2big();
env->buf[env->bufi++] = c;
if (c == '=' && str[0] == '/' && IsAlpha(str[1]) && str[2] == '/') {
path = env->buf + env->bufi;
}
} while (c);
// fixup key=/c/... → key=c:\...
if (path) mungentpath(path);
// append key=val to sorted list using insertion sort technique
for (i = env->vari;; --i) {
if (!i || (cmp = Compare(var, env->var[i - 1])) > 0) {
// insert entry for new key
env->var[i] = var;
env->vari++;
break;
}
if (!cmp) {
// deduplicate preferring latter
env->var[i - 1] = var;
for (; i < env->vari; ++i) {
env->var[i] = env->var[i + 1];
}
break;
}
// sift items right to create empty slot at insertion point
env->var[i] = env->var[i - 1];
}
return 0;
}
static textwindows int InsertStrings(struct EnvBuilder *env,
char *const strs[]) {
if (strs) {
for (int i = 0; strs[i]; ++i) {
if (InsertString(env, strs[i]) == -1) {
return -1;
}
}
}
return 0;
}
static textwindows int CountStrings(char *const strs[]) {
int n = 0;
if (strs) {
while (*strs++) {
++n;
}
}
return n;
}
/**
* Copies sorted environment variable block for Windows.
*
* This is designed to meet the requirements of CreateProcess().
*
* @param envblock receives sorted double-NUL terminated string list
* @param envp is an a NULL-terminated array of UTF-8 strings
* @param extravar is a VAR=val string we consider part of envp or NULL
* @return 0 on success, or -1 w/ errno
* @error E2BIG if total number of shorts (including nul) exceeded 32767
* @asyncsignalsafe
*/
textwindows int mkntenvblock(char16_t envblock[32767], char *const envp[],
char *const extravars[], char buf[32767]) {
int i, k, n;
struct Env e;
struct EnvBuilder env = {buf};
// allocate string pointer array for sorting purposes
n = (CountStrings(envp) + CountStrings(extravars) + 1) * sizeof(char *);
#pragma GCC push_options
#pragma GCC diagnostic ignored "-Walloca-larger-than="
env.var = alloca(n);
CheckLargeStackAllocation(env.var, n);
#pragma GCC pop_options
// load new environment into string pointer array and fix file paths
if (InsertStrings(&env, envp) == -1) return -1;
if (InsertStrings(&env, extravars) == -1) return -1;
if (environ) {
// https://jpassing.com/2009/12/28/the-hidden-danger-of-forgetting-to-specify-systemroot-in-a-custom-environment-block/
e = __getenv(environ, "SYSTEMROOT");
if (e.s && InsertString(&env, environ[e.i]) == -1) {
return -1;
}
}
// copy utf-8 sorted string pointer array into contiguous utf-16 block
// in other words, we're creating a double-nul terminated string list!
for (k = i = 0; i < env.vari; ++i) {
k += tprecode8to16(envblock + k, -1, env.var[i]).ax + 1;
}
unassert(k <= env.bufi);
envblock[k] = 0;
return 0;
}