/* * comcom64 - 64bit command.com * env.c: environment handling routines * Copyright (C) 2023-2024 @stsp * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #ifdef DJ64 #include #else #include "fmemcpy.h" #include "memmem.h" #endif #include #include #include "command.h" #include "umb.h" #include "env.h" #define DP(s, o) (__dpmi_paddr){ .selector = s, .offset32 = o, } extern char **environ; static unsigned short env_selector; static unsigned short env_segment; static unsigned short env_size; struct MCB { char id; /* 0 */ unsigned short owner_psp; /* 1 */ unsigned short size; /* 3 */ char align8[3]; /* 5 */ char name[8]; /* 8 */ } __attribute__((packed)); void set_env_seg(void) { unsigned short psp = _stubinfo->psp_selector; fmemcpy1(DP(psp, 0x2c), &env_segment, 2); } void set_env_sel(void) { unsigned short psp = _stubinfo->psp_selector; fmemcpy1(DP(psp, 0x2c), &env_selector, 2); } void set_env_size(void) { unsigned short psp = _stubinfo->psp_selector; unsigned short env_sel; unsigned env_addr; struct MCB mcb; unsigned old_env_size; int err; fmemcpy2(&env_sel, DP(psp, 0x2c), 2); err = get_segment_base_address(env_sel, &env_addr); old_env_size = __dpmi_get_segment_limit(env_sel) + 1; env_size = old_env_size; if (!err && !(env_addr & 0xf) && env_addr < 0x110000 && old_env_size == 0x10000) { dosmemget(env_addr - sizeof(mcb), sizeof(mcb), &mcb); env_size = mcb.size * 16; __dpmi_set_segment_limit(env_sel, env_size - 1); } env_selector = env_sel; env_segment = env_addr >> 4; } void get_env(void) { char *dos_environ = alloca(env_size); char *cp; fmemcpy2(dos_environ, DP(env_selector, 0), env_size); dos_environ[env_size] = 0; cp = dos_environ; do { if (*cp) { char *env = strdup(cp); putenv(env); cp += strlen(env); } cp++; /* skip to next character */ } while (*cp); /* repeat until two NULs */ } /* this function replaces RM env (pointed to with env_sel) with * PM env (environ[]), leaving tail intact */ static void _put_env(unsigned short env_sel) { int env_count; int env_offs = 0; char *env; char *tail; int tail_sz = 3; env = alloca(env_size + tail_sz); /* back up full env, just for getting its tail */ fmemcpy2(env, DP(env_sel, 0), env_size); memset(&env[env_size], 0, tail_sz); tail = memchr(env, 1, env_size); if (tail && tail[1] == '\0') { tail_sz += strlen(tail + 2) + 1; tail--; } else { tail = memmem(env, env_size, "\x0\x0", 2); if (!tail) { printf("ENV block corrupted\n"); return; } tail++; if (tail - env + tail_sz > env_size || memcmp(tail, "\x0\x0\x0", 3) != 0) tail_sz = 1; /* DOS-2.0 terminator */ } /* now put entire environ[] down, overwriting prev content */ for (env_count = 0; environ[env_count]; env_count++) { int l = strlen(environ[env_count]) + 1; if (env_offs + l >= env_size - tail_sz) { printf("ENV buffer overflow (size %u, need %u, tail %i)\n", env_size, env_offs + l, tail_sz); break; } fmemcpy1(DP(env_sel, env_offs), environ[env_count], l); env_offs += l; } /* and preserve tail */ if (env_offs + tail_sz <= env_size) fmemcpy1(DP(env_sel, env_offs), tail, tail_sz); } void put_env(void) { _put_env(env_selector); } #if !SYNC_ENV static void _set_env(const char *variable, const char *value, unsigned short env_sel, unsigned env_size) { char *env; char *tail; char *cp; char *env2; int l; int len; int tail_sz = 3; /* allocate tmp buffer for env and copy them there */ env = alloca(env_size + tail_sz); fmemcpy2(env, DP(env_sel, 0), env_size); memset(&env[env_size], 0, tail_sz); cp = env2 = env; l = strlen(variable); /* Delete any existing variable with the name (var). */ while (*env2 && (env2 - env) < env_size) { if ((strncmp(variable, env2, l) == 0) && (env2[l] == '=')) { cp = env2 + strlen(env2) + 1; memmove(env2, cp, env_size - (cp - env)); } else { env2 += strlen(env2) + 1; } } tail = env2; cp = tail + 1; if (cp[0] == '\1' && cp[1] == '\0') tail_sz += strlen(cp + 2) + 1; /* If the variable fits, shovel it in at the end of the envrionment. */ len = l + (value ? strlen(value) : 0) + 2; if (value && value[0] && (env_size - (env2 - env) - tail_sz >= len)) { memmove(env2 + len, env2, tail_sz); strcpy(env2, variable); strcat(env2, "="); strcat(env2, value); } /* now put it back */ fmemcpy1(DP(env_sel, 0), env, env_size); } void set_env(const char *variable, const char *value) { _set_env(variable, value, env_selector, env_size); } void sync_env(void) { unsigned short sel; unsigned short psp = _stubinfo->psp_selector; fmemcpy2(&sel, DP(psp, 0x2c), 2); _put_env(sel); } #endif int realloc_env(unsigned new_size) { int seg, sel; unsigned int old_size = env_size; link_umb(0x80); seg = __dpmi_allocate_dos_memory(new_size >> 4, &sel); unlink_umb(); if (seg != -1) { unsigned short psp = _stubinfo->psp_selector; fmemcpy1(DP(psp, 0x2c), &sel, 2); /* copy old content to preserve tail (and maybe COMSPEC) */ fmemcpy12(DP(sel, 0), DP(env_selector, 0), old_size); __dpmi_free_dos_memory(env_selector); env_selector = sel; env_segment = seg; env_size = new_size; } else { printf("ERROR: env allocation of %i bytes failed!\n", env_size); return -1; } return 0; } int get_env_size(void) { return env_size; }