Compare commits

...

3 Commits

13 changed files with 545 additions and 128 deletions
+1 -1
View File
@@ -610,7 +610,7 @@ int HawkStd::open_pio (Pipe& io)
break; break;
} }
pio = hawk_pio_open((hawk_gem_t*)*this, 0, io.getName(), flags); pio = hawk_pio_open((hawk_gem_t*)*this, 0, io.getName(), flags, HAWK_NULL, HAWK_NULL);
if (!pio) return -1; if (!pio) return -1;
#if defined(HAWK_OOCH_IS_UCH) #if defined(HAWK_OOCH_IS_UCH)
+9 -2
View File
@@ -250,6 +250,7 @@ struct hawk_htb_t
hawk_oow_t size; hawk_oow_t size;
hawk_oow_t capa; hawk_oow_t capa;
hawk_oow_t threshold; hawk_oow_t threshold;
hawk_oow_t rev;
hawk_htb_pair_t** bucket; hawk_htb_pair_t** bucket;
}; };
@@ -284,13 +285,15 @@ typedef struct hawk_htb_itr_t hawk_htb_itr_t;
/** /**
* The HAWK_HTB_SIZE() macro returns the number of pairs in a hash table. * The HAWK_HTB_SIZE() macro returns the number of pairs in a hash table.
*/ */
#define HAWK_HTB_SIZE(m) (*(const hawk_oow_t*)&(m)->size) #define HAWK_HTB_SIZE(m) ((const hawk_oow_t)(m)->size)
/** /**
* The HAWK_HTB_CAPA() macro returns the maximum number of pairs that can be * The HAWK_HTB_CAPA() macro returns the maximum number of pairs that can be
* stored in a hash table without further reorganization. * stored in a hash table without further reorganization.
*/ */
#define HAWK_HTB_CAPA(m) (*(const hawk_oow_t*)&(m)->capa) #define HAWK_HTB_CAPA(m) ((const hawk_oow_t)(m)->capa)
#define HAWK_HTB_REV(m) ((const hawk_oow_t)(m)->rev)
#define HAWK_HTB_FACTOR(m) (*(const int*)&(m)->factor) #define HAWK_HTB_FACTOR(m) (*(const int*)&(m)->factor)
#define HAWK_HTB_KSCALE(m) (*(const int*)&(m)->scale[HAWK_HTB_KEY]) #define HAWK_HTB_KSCALE(m) (*(const int*)&(m)->scale[HAWK_HTB_KEY])
@@ -406,6 +409,10 @@ HAWK_EXPORT hawk_oow_t hawk_htb_getcapa (
const hawk_htb_t* htb /**< hash table */ const hawk_htb_t* htb /**< hash table */
); );
HAWK_EXPORT hawk_oow_t hawk_htb_getrev (
const hawk_htb_t* htb /**< hash table */
);
/** /**
* The hawk_htb_search() function searches a hash table to find a pair with a * The hawk_htb_search() function searches a hash table to find a pair with a
* matching key. It returns the pointer to the pair found. If it fails * matching key. It returns the pointer to the pair found. If it fails
+4
View File
@@ -45,6 +45,7 @@
# define hawk_map_getxtn(map) hawk_htb_getxtn(map) # define hawk_map_getxtn(map) hawk_htb_getxtn(map)
# define hawk_map_getsize(map) hawk_htb_getsize(map) # define hawk_map_getsize(map) hawk_htb_getsize(map)
# define hawk_map_getcapa(map) hawk_htb_getcapa(map) # define hawk_map_getcapa(map) hawk_htb_getcapa(map)
# define hawk_map_getrev(map) hawk_htb_getrev(map)
# define hawk_map_getstyle(map) hawk_htb_getstyle(map) # define hawk_map_getstyle(map) hawk_htb_getstyle(map)
# define hawk_map_setstyle(map,cbs) hawk_htb_setstyle(map,cbs) # define hawk_map_setstyle(map,cbs) hawk_htb_setstyle(map,cbs)
# define hawk_map_search(map,kptr,klen) hawk_htb_search(map,kptr,klen) # define hawk_map_search(map,kptr,klen) hawk_htb_search(map,kptr,klen)
@@ -80,6 +81,7 @@
# define HAWK_MAP_SIZER_DEFAULT HAWK_HTB_SIZER_DEFAULT # define HAWK_MAP_SIZER_DEFAULT HAWK_HTB_SIZER_DEFAULT
# define HAWK_MAP_HASHER_DEFAULT HAWK_HTB_HASHER_DEFAULT # define HAWK_MAP_HASHER_DEFAULT HAWK_HTB_HASHER_DEFAULT
# define HAWK_MAP_SIZE(map) HAWK_HTB_SIZE(map) # define HAWK_MAP_SIZE(map) HAWK_HTB_SIZE(map)
# define HAWK_MAP_REV(map) HAWK_HTB_REV(map)
# define HAWK_MAP_KCOPIER(map) HAWK_HTB_KCOPIER(map) # define HAWK_MAP_KCOPIER(map) HAWK_HTB_KCOPIER(map)
# define HAWK_MAP_VCOPIER(map) HAWK_HTB_VCOPIER(map) # define HAWK_MAP_VCOPIER(map) HAWK_HTB_VCOPIER(map)
# define HAWK_MAP_KFREEER(map) HAWK_HTB_KFREEER(map) # define HAWK_MAP_KFREEER(map) HAWK_HTB_KFREEER(map)
@@ -108,6 +110,7 @@
# define hawk_map_getxtn(map) hawk_rbt_getxtn(map) # define hawk_map_getxtn(map) hawk_rbt_getxtn(map)
# define hawk_map_getsize(map) hawk_rbt_getsize(map) # define hawk_map_getsize(map) hawk_rbt_getsize(map)
# define hawk_map_getcapa(map) hawk_rbt_getsize(map) # define hawk_map_getcapa(map) hawk_rbt_getsize(map)
# define hawk_map_getrev(map) hawk_rbt_getrev(map)
# define hawk_map_getstyle(map) hawk_rbt_getstyle(map) # define hawk_map_getstyle(map) hawk_rbt_getstyle(map)
# define hawk_map_setstyle(map,cbs) hawk_rbt_setstyle(map,cbs) # define hawk_map_setstyle(map,cbs) hawk_rbt_setstyle(map,cbs)
# define hawk_map_search(map,kptr,klen) hawk_rbt_search(map,kptr,klen) # define hawk_map_search(map,kptr,klen) hawk_rbt_search(map,kptr,klen)
@@ -143,6 +146,7 @@
# define HAWK_MAP_SIZER_DEFAULT (HAWK_NULL) # define HAWK_MAP_SIZER_DEFAULT (HAWK_NULL)
# define HAWK_MAP_HASHER_DEFAULT (HAWK_NULL) # define HAWK_MAP_HASHER_DEFAULT (HAWK_NULL)
# define HAWK_MAP_SIZE(map) HAWK_RBT_SIZE(map) # define HAWK_MAP_SIZE(map) HAWK_RBT_SIZE(map)
# define HAWK_MAP_REV(map) HAWK_RBT_REV(map)
# define HAWK_MAP_KCOPIER(map) HAWK_RBT_KCOPIER(map) # define HAWK_MAP_KCOPIER(map) HAWK_RBT_KCOPIER(map)
# define HAWK_MAP_VCOPIER(map) HAWK_RBT_VCOPIER(map) # define HAWK_MAP_VCOPIER(map) HAWK_RBT_VCOPIER(map)
# define HAWK_MAP_KFREEER(map) HAWK_RBT_KFREEER(map) # define HAWK_MAP_KFREEER(map) HAWK_RBT_KFREEER(map)
+22 -9
View File
@@ -120,8 +120,21 @@ enum hawk_pio_hid_t
}; };
typedef enum hawk_pio_hid_t hawk_pio_hid_t; typedef enum hawk_pio_hid_t hawk_pio_hid_t;
enum hawk_pio_env_mk_type_t
{
HAWK_PIO_ENV_MK_BCH_PP, /* P1 ... PN HAWK_NUL where P1 .. PN points to K=V */
HAWK_PIO_ENV_MK_BCH_PN, /* K1=V\0K2=V\0\0 */
HAWK_PIO_ENV_MK_UCH_PP,
};
typedef int (*hawk_pio_fncptr_t) (void* ctx, char** envp); typedef enum hawk_pio_env_mk_type_t hawk_pio_env_mk_type_t;
typedef void* (*hawk_pio_env_mk_t) (
hawk_pio_env_mk_type_t type,
void* ctx
);
typedef int (*hawk_pio_fncptr_t) (void* ctx);
/** /**
* The hawk_pio_fnc_t type defines a structure to point to the function * The hawk_pio_fnc_t type defines a structure to point to the function
@@ -170,7 +183,6 @@ struct hawk_pio_pin_t
hawk_pio_t* self; hawk_pio_t* self;
}; };
/** /**
* The hawk_pio_t type defines a structure to store status for piped I/O * The hawk_pio_t type defines a structure to store status for piped I/O
* to a child process. The hawk_pio_xxx() funtions are written around this * to a child process. The hawk_pio_xxx() funtions are written around this
@@ -198,18 +210,17 @@ extern "C" {
* pipes to it. #HAWK_PIO_SHELL causes the function to execute \a cmd via * pipes to it. #HAWK_PIO_SHELL causes the function to execute \a cmd via
* the default shell of an underlying system: /bin/sh on *nix, cmd.exe on win32. * the default shell of an underlying system: /bin/sh on *nix, cmd.exe on win32.
* On *nix systems, a full path to the command is needed if it is not specified. * On *nix systems, a full path to the command is needed if it is not specified.
* If \a env is #HAWK_NULL, the environment of \a cmd inherits that of the * If #HAWK_PIO_BCSTRCMD is specified in \a flags, \a cmd is treated as a
* calling process. If you want to pass an empty environment, you can pass * multi-byte string whose character type is #hawk_bch_t.
* an empty \a env object with no items inserted. If #HAWK_PIO_BCSTRCMD is
* specified in \a flags, \a cmd is treated as a multi-byte string whose
* character type is #hawk_bch_t.
* \return #hawk_pio_t object on success, #HAWK_NULL on failure * \return #hawk_pio_t object on success, #HAWK_NULL on failure
*/ */
HAWK_EXPORT hawk_pio_t* hawk_pio_open ( HAWK_EXPORT hawk_pio_t* hawk_pio_open (
hawk_gem_t* gem, /**< gem */ hawk_gem_t* gem, /**< gem */
hawk_oow_t ext, /**< extension size */ hawk_oow_t ext, /**< extension size */
const hawk_ooch_t* cmd, /**< command to execute */ const hawk_ooch_t* cmd, /**< command to execute */
int flags /**< 0 or a number OR'ed of the #hawk_pio_flag_t enumerators*/ int flags, /**< 0 or a number OR'ed of the #hawk_pio_flag_t enumerators*/
hawk_pio_env_mk_t env_mk,
void* env_cx
); );
/** /**
@@ -230,7 +241,9 @@ HAWK_EXPORT int hawk_pio_init (
hawk_pio_t* pio, /**< pio object */ hawk_pio_t* pio, /**< pio object */
hawk_gem_t* gem, /**< gem */ hawk_gem_t* gem, /**< gem */
const hawk_ooch_t* cmd, /**< command to execute */ const hawk_ooch_t* cmd, /**< command to execute */
int flags /**< 0 or a number OR'ed of the #hawk_pio_flag_t enumerators*/ int flags, /**< 0 or a number OR'ed of the #hawk_pio_flag_t enumerators*/
hawk_pio_env_mk_t env_mk,
void* env_cx
); );
/** /**
+2
View File
@@ -254,6 +254,7 @@ struct hawk_rbt_t
hawk_rbt_pair_t xnil; /**< internal nil node */ hawk_rbt_pair_t xnil; /**< internal nil node */
hawk_oow_t size; /**< number of pairs */ hawk_oow_t size; /**< number of pairs */
hawk_rbt_pair_t* root; /**< root pair */ hawk_rbt_pair_t* root; /**< root pair */
hawk_oow_t rev; /**< revision */
#if defined(HAWK_ENABLE_RBT_ITR_PROTECTION) #if defined(HAWK_ENABLE_RBT_ITR_PROTECTION)
hawk_rbt_itr_t _prot_itr; /**< protected iterators */ hawk_rbt_itr_t _prot_itr; /**< protected iterators */
#endif #endif
@@ -280,6 +281,7 @@ struct hawk_rbt_t
* The HAWK_RBT_SIZE() macro returns the number of pairs in red-black tree. * The HAWK_RBT_SIZE() macro returns the number of pairs in red-black tree.
*/ */
#define HAWK_RBT_SIZE(m) ((const hawk_oow_t)(m)->size) #define HAWK_RBT_SIZE(m) ((const hawk_oow_t)(m)->size)
#define HAWK_RBT_REV(m) ((const hawk_oow_t)(m)->rev)
#define HAWK_RBT_KSCALE(m) ((const int)(m)->scale[HAWK_RBT_KEY]) #define HAWK_RBT_KSCALE(m) ((const int)(m)->scale[HAWK_RBT_KEY])
#define HAWK_RBT_VSCALE(m) ((const int)(m)->scale[HAWK_RBT_VAL]) #define HAWK_RBT_VSCALE(m) ((const int)(m)->scale[HAWK_RBT_VAL])
+19 -5
View File
@@ -311,6 +311,7 @@ int hawk_htb_init (hawk_htb_t* htb, hawk_gem_t* gem, hawk_oow_t capa, int factor
htb->capa = capa; htb->capa = capa;
htb->threshold = htb->capa * htb->factor / 100; htb->threshold = htb->capa * htb->factor / 100;
if (htb->capa > 0 && htb->threshold <= 0) htb->threshold = 1; if (htb->capa > 0 && htb->threshold <= 0) htb->threshold = 1;
htb->rev = 0;
htb->style = &style[0]; htb->style = &style[0];
return 0; return 0;
@@ -343,6 +344,11 @@ hawk_oow_t hawk_htb_getcapa (const hawk_htb_t* htb)
return htb->capa; return htb->capa;
} }
hawk_oow_t hawk_htb_getrev (const hawk_htb_t* htb)
{
return htb->rev;
}
pair_t* hawk_htb_search (const hawk_htb_t* htb, const void* kptr, hawk_oow_t klen) pair_t* hawk_htb_search (const hawk_htb_t* htb, const void* kptr, hawk_oow_t klen)
{ {
pair_t* pair; pair_t* pair;
@@ -451,7 +457,7 @@ static HAWK_INLINE pair_t* insert (hawk_htb_t* htb, void* kptr, hawk_oow_t klen,
case UPSERT: case UPSERT:
case UPDATE: case UPDATE:
p = change_pair_val(htb, pair, vptr, vlen); p = change_pair_val(htb, pair, vptr, vlen);
if (p == HAWK_NULL) if (!p)
{ {
/* error in changing the value */ /* error in changing the value */
return HAWK_NULL; return HAWK_NULL;
@@ -465,6 +471,7 @@ static HAWK_INLINE pair_t* insert (hawk_htb_t* htb, void* kptr, hawk_oow_t klen,
else NEXT(prev) = p; else NEXT(prev) = p;
NEXT(p) = next; NEXT(p) = next;
} }
htb->rev++;
return p; return p;
case ENSERT: case ENSERT:
@@ -507,6 +514,7 @@ static HAWK_INLINE pair_t* insert (hawk_htb_t* htb, void* kptr, hawk_oow_t klen,
htb->bucket[hc] = pair; htb->bucket[hc] = pair;
htb->size++; htb->size++;
htb->rev++;
return pair; /* new key added */ return pair; /* new key added */
} }
@@ -525,7 +533,6 @@ pair_t* hawk_htb_insert (hawk_htb_t* htb, void* kptr, hawk_oow_t klen, void* vpt
return insert(htb, kptr, klen, vptr, vlen, INSERT); return insert(htb, kptr, klen, vptr, vlen, INSERT);
} }
pair_t* hawk_htb_update (hawk_htb_t* htb, void* kptr, hawk_oow_t klen, void* vptr, hawk_oow_t vlen) pair_t* hawk_htb_update (hawk_htb_t* htb, void* kptr, hawk_oow_t klen, void* vptr, hawk_oow_t vlen)
{ {
return insert(htb, kptr, klen, vptr, vlen, UPDATE); return insert(htb, kptr, klen, vptr, vlen, UPDATE);
@@ -548,7 +555,7 @@ pair_t* hawk_htb_cbsert (hawk_htb_t* htb, void* kptr, hawk_oow_t klen, cbserter_
{ {
/* found a pair with a matching key */ /* found a pair with a matching key */
p = cbserter(htb, pair, kptr, klen, ctx); p = cbserter(htb, pair, kptr, klen, ctx);
if (p == HAWK_NULL) if (!p)
{ {
/* error returned by the callback function */ /* error returned by the callback function */
return HAWK_NULL; return HAWK_NULL;
@@ -562,6 +569,7 @@ pair_t* hawk_htb_cbsert (hawk_htb_t* htb, void* kptr, hawk_oow_t klen, cbserter_
else NEXT(prev) = p; else NEXT(prev) = p;
NEXT(p) = next; NEXT(p) = next;
} }
htb->rev++;
return p; return p;
} }
@@ -587,6 +595,7 @@ pair_t* hawk_htb_cbsert (hawk_htb_t* htb, void* kptr, hawk_oow_t klen, cbserter_
NEXT(pair) = htb->bucket[hc]; NEXT(pair) = htb->bucket[hc];
htb->bucket[hc] = pair; htb->bucket[hc] = pair;
htb->size++; htb->size++;
htb->rev++;
return pair; /* new key added */ return pair; /* new key added */
} }
@@ -610,6 +619,7 @@ int hawk_htb_delete (hawk_htb_t* htb, const void* kptr, hawk_oow_t klen)
hawk_htb_freepair(htb, pair); hawk_htb_freepair(htb, pair);
htb->size--; htb->size--;
htb->rev++;
return 0; return 0;
} }
@@ -624,14 +634,15 @@ int hawk_htb_delete (hawk_htb_t* htb, const void* kptr, hawk_oow_t klen)
void hawk_htb_clear (hawk_htb_t* htb) void hawk_htb_clear (hawk_htb_t* htb)
{ {
hawk_oow_t i; hawk_oow_t i, sz;
pair_t* pair, * next; pair_t* pair, * next;
sz = htb->size;
for (i = 0; i < htb->capa; i++) for (i = 0; i < htb->capa; i++)
{ {
pair = htb->bucket[i]; pair = htb->bucket[i];
while (pair != HAWK_NULL) while (pair)
{ {
next = NEXT(pair); next = NEXT(pair);
hawk_htb_freepair(htb, pair); hawk_htb_freepair(htb, pair);
@@ -641,6 +652,9 @@ void hawk_htb_clear (hawk_htb_t* htb)
htb->bucket[i] = HAWK_NULL; htb->bucket[i] = HAWK_NULL;
} }
HAWK_ASSERT(htb->size == 0);
if (sz) htb->rev++;
} }
void hawk_htb_walk (hawk_htb_t* htb, walker_t walker, void* ctx) void hawk_htb_walk (hawk_htb_t* htb, walker_t walker, void* ctx)
+3 -1
View File
@@ -6688,15 +6688,17 @@ static int unsetenv(const char *name)
/* check if the entry starts with name + '=' */ /* check if the entry starts with name + '=' */
if (hawk_comp_bcstr_limited(*ep, name, len, 0) == 0 && (*ep)[len] == '=') if (hawk_comp_bcstr_limited(*ep, name, len, 0) == 0 && (*ep)[len] == '=')
{ {
char* old;
/* found it. shift remaining pointers down. */ /* found it. shift remaining pointers down. */
char** next = ep; char** next = ep;
old = *ep;
while (*next) while (*next)
{ {
*next = *(next + 1); *next = *(next + 1);
next++; next++;
} }
free(*ep); free(old);
break; break;
} }
} }
+92 -94
View File
@@ -101,7 +101,7 @@ static int get_highest_fd (hawk_pio_t* pio)
} }
} }
HAWK_CLOSEDIR (d); HAWK_CLOSEDIR(d);
return maxfd; return maxfd;
} }
@@ -175,7 +175,7 @@ static int close_open_fds_using_proc (hawk_pio_t* pio, int* excepts, hawk_oow_t
if (fd == excepts[i]) goto skip_close; if (fd == excepts[i]) goto skip_close;
} }
HAWK_CLOSE (fd); HAWK_CLOSE(fd);
skip_close: skip_close:
; ;
@@ -183,7 +183,7 @@ static int close_open_fds_using_proc (hawk_pio_t* pio, int* excepts, hawk_oow_t
} }
} }
HAWK_CLOSEDIR (d); HAWK_CLOSEDIR(d);
return 0; return 0;
} }
@@ -191,19 +191,19 @@ static int close_open_fds_using_proc (hawk_pio_t* pio, int* excepts, hawk_oow_t
} }
#endif #endif
hawk_pio_t* hawk_pio_open (hawk_gem_t* gem, hawk_oow_t xtnsize, const hawk_ooch_t* cmd, int flags) hawk_pio_t* hawk_pio_open (hawk_gem_t* gem, hawk_oow_t xtnsize, const hawk_ooch_t* cmd, int flags, hawk_pio_env_mk_t env_mk, void* env_ctx)
{ {
hawk_pio_t* pio; hawk_pio_t* pio;
pio = (hawk_pio_t*)hawk_gem_allocmem(gem, HAWK_SIZEOF(hawk_pio_t) + xtnsize); pio = (hawk_pio_t*)hawk_gem_allocmem(gem, HAWK_SIZEOF(hawk_pio_t) + xtnsize);
if (pio) if (pio)
{ {
if (hawk_pio_init(pio, gem, cmd, flags) <= -1) if (hawk_pio_init(pio, gem, cmd, flags, env_mk, env_ctx) <= -1)
{ {
hawk_gem_freemem (gem, pio); hawk_gem_freemem(gem, pio);
pio = HAWK_NULL; pio = HAWK_NULL;
} }
else HAWK_MEMSET (pio + 1, 0, xtnsize); else HAWK_MEMSET(pio + 1, 0, xtnsize);
} }
return pio; return pio;
@@ -212,7 +212,7 @@ hawk_pio_t* hawk_pio_open (hawk_gem_t* gem, hawk_oow_t xtnsize, const hawk_ooch_
void hawk_pio_close (hawk_pio_t* pio) void hawk_pio_close (hawk_pio_t* pio)
{ {
hawk_pio_fini (pio); hawk_pio_fini (pio);
hawk_gem_freemem (pio->gem, pio); hawk_gem_freemem(pio->gem, pio);
} }
#if !defined(_WIN32) && !defined(__OS2__) && !defined(__DOS__) #if !defined(_WIN32) && !defined(__OS2__) && !defined(__DOS__)
@@ -233,8 +233,8 @@ typedef struct param_t param_t;
static void free_param (hawk_pio_t* pio, param_t* param) static void free_param (hawk_pio_t* pio, param_t* param)
{ {
if (param->argv && param->argv != param->fixed_argv) if (param->argv && param->argv != param->fixed_argv)
hawk_gem_freemem (pio->gem, param->argv); hawk_gem_freemem(pio->gem, param->argv);
if (param->mcmd) hawk_gem_freemem (pio->gem, param->mcmd); if (param->mcmd) hawk_gem_freemem(pio->gem, param->mcmd);
} }
static int make_param (hawk_pio_t* pio, const hawk_ooch_t* cmd, int flags, param_t* param) static int make_param (hawk_pio_t* pio, const hawk_ooch_t* cmd, int flags, param_t* param)
@@ -247,7 +247,7 @@ static int make_param (hawk_pio_t* pio, const hawk_ooch_t* cmd, int flags, param
#endif #endif
int fcnt = 0; int fcnt = 0;
HAWK_MEMSET (param, 0, HAWK_SIZEOF(*param)); HAWK_MEMSET(param, 0, HAWK_SIZEOF(*param));
#if defined(HAWK_OOCH_IS_BCH) #if defined(HAWK_OOCH_IS_BCH)
if (flags & HAWK_PIO_SHELL) mcmd = (hawk_ooch_t*)cmd; if (flags & HAWK_PIO_SHELL) mcmd = (hawk_ooch_t*)cmd;
@@ -357,7 +357,7 @@ static int make_param (hawk_pio_t* pio, const hawk_ooch_t* cmd, int flags, param
/* hawk_wcsntombsn() doesn't null-terminate mcmd */ /* hawk_wcsntombsn() doesn't null-terminate mcmd */
mcmd[mn] = '\0'; mcmd[mn] = '\0';
hawk_gem_freemem (pio->gem, wcmd); hawk_gem_freemem(pio->gem, wcmd);
wcmd = HAWK_NULL; wcmd = HAWK_NULL;
} }
} }
@@ -407,11 +407,11 @@ static int make_param (hawk_pio_t* pio, const hawk_ooch_t* cmd, int flags, param
oops: oops:
#if defined(HAWK_OOCH_IS_BCH) #if defined(HAWK_OOCH_IS_BCH)
if (mcmd && mcmd != cmd) hawk_gem_freemem (pio->gem, mcmd); if (mcmd && mcmd != cmd) hawk_gem_freemem(pio->gem, mcmd);
#else #else
if (mcmd && mcmd != (hawk_bch_t*)cmd && if (mcmd && mcmd != (hawk_bch_t*)cmd &&
mcmd != param->fixed_mbuf) hawk_gem_freemem (pio->gem, mcmd); mcmd != param->fixed_mbuf) hawk_gem_freemem(pio->gem, mcmd);
if (wcmd) hawk_gem_freemem (pio->gem, wcmd); if (wcmd) hawk_gem_freemem(pio->gem, wcmd);
#endif #endif
return -1; return -1;
} }
@@ -444,12 +444,12 @@ static int assert_executable (hawk_pio_t* pio, const hawk_bch_t* path)
static HAWK_INLINE int is_fd_valid (int fd) static HAWK_INLINE int is_fd_valid (int fd)
{ {
return HAWK_FCNTL (fd, F_GETFD, 0) != -1 || errno != EBADF; return HAWK_FCNTL(fd, F_GETFD, 0) != -1 || errno != EBADF;
} }
static HAWK_INLINE int is_fd_valid_and_nocloexec (int fd) static HAWK_INLINE int is_fd_valid_and_nocloexec (int fd)
{ {
int flags = HAWK_FCNTL (fd, F_GETFD, 0); int flags = HAWK_FCNTL(fd, F_GETFD, 0);
if (flags == -1) if (flags == -1)
{ {
if (errno == EBADF) return 0; /* invalid. return false */ if (errno == EBADF) return 0; /* invalid. return false */
@@ -458,16 +458,10 @@ static HAWK_INLINE int is_fd_valid_and_nocloexec (int fd)
return !(flags & FD_CLOEXEC)? 1: 0; return !(flags & FD_CLOEXEC)? 1: 0;
} }
static hawk_pio_pid_t standard_fork_and_exec (hawk_pio_t* pio, int pipes[], param_t* param, char** envp) static hawk_pio_pid_t standard_fork_and_exec (hawk_pio_t* pio, int pipes[], param_t* param, char*const* envp)
{ {
hawk_pio_pid_t pid; hawk_pio_pid_t pid;
#if defined(HAVE_CRT_EXTERNS_H)
# define environ (*(_NSGetEnviron()))
#else
extern char** environ;
#endif
pid = HAWK_FORK(); pid = HAWK_FORK();
if (pid <= -1) if (pid <= -1)
{ {
@@ -523,7 +517,7 @@ static hawk_pio_pid_t standard_fork_and_exec (hawk_pio_t* pio, int pipes[], para
if (HAWK_DUP2(pipes[3], 2) <= -1) goto child_oops; if (HAWK_DUP2(pipes[3], 2) <= -1) goto child_oops;
} }
HAWK_CLOSE (pipes[3]); HAWK_CLOSE(pipes[3]);
pipes[3] = HAWK_PIO_HND_NIL; pipes[3] = HAWK_PIO_HND_NIL;
} }
@@ -574,7 +568,6 @@ static hawk_pio_pid_t standard_fork_and_exec (hawk_pio_t* pio, int pipes[], para
if (pio->flags & HAWK_PIO_DROPOUT) HAWK_CLOSE(1); if (pio->flags & HAWK_PIO_DROPOUT) HAWK_CLOSE(1);
if (pio->flags & HAWK_PIO_DROPERR) HAWK_CLOSE(2); if (pio->flags & HAWK_PIO_DROPERR) HAWK_CLOSE(2);
if (pio->flags & HAWK_PIO_FNCCMD) if (pio->flags & HAWK_PIO_FNCCMD)
{ {
/* ----------------------------------------------- /* -----------------------------------------------
@@ -583,7 +576,10 @@ static hawk_pio_pid_t standard_fork_and_exec (hawk_pio_t* pio, int pipes[], para
hawk_pio_fnc_t* fnc = (hawk_pio_fnc_t*)param; hawk_pio_fnc_t* fnc = (hawk_pio_fnc_t*)param;
int retx; int retx;
retx = fnc->ptr(fnc->ctx, envp); /* when the function pointer is a worker function,
* the function must use the context(fnc->ctx) passed
* in as a paramter to find out actual environmen values (e.g. ENVIRON) */
retx = fnc->ptr(fnc->ctx);
if (devnull >= 0) HAWK_CLOSE(devnull); if (devnull >= 0) HAWK_CLOSE(devnull);
HAWK_EXIT(retx); HAWK_EXIT(retx);
} }
@@ -619,7 +615,7 @@ static int set_pipe_nonblock (hawk_pio_t* pio, hawk_pio_hnd_t fd, int enabled)
} }
int hawk_pio_init (hawk_pio_t* pio, hawk_gem_t* gem, const hawk_ooch_t* cmd, int flags) int hawk_pio_init (hawk_pio_t* pio, hawk_gem_t* gem, const hawk_ooch_t* cmd, int flags, hawk_pio_env_mk_t env_mk, void* env_ctx)
{ {
hawk_pio_hnd_t handle[6] /*= hawk_pio_hnd_t handle[6] /*=
{ {
@@ -673,32 +669,17 @@ int hawk_pio_init (hawk_pio_t* pio, hawk_gem_t* gem, const hawk_ooch_t* cmd, int
posix_spawnattr_t psattr; posix_spawnattr_t psattr;
hawk_pio_pid_t pid; hawk_pio_pid_t pid;
param_t param; param_t param;
#if defined(HAVE_CRT_EXTERNS_H)
#define environ (*(_NSGetEnviron()))
#else
extern char** environ;
#endif
#elif defined(HAWK_SYSCALL0) && defined(SYS_vfork) #elif defined(HAWK_SYSCALL0) && defined(SYS_vfork)
hawk_pio_pid_t pid; hawk_pio_pid_t pid;
param_t param; param_t param;
#if defined(HAVE_CRT_EXTERNS_H)
#define environ (*(_NSGetEnviron()))
#else
extern char** environ;
#endif
int highest_fd; int highest_fd;
int dummy; int dummy;
#else #else
hawk_pio_pid_t pid; hawk_pio_pid_t pid;
param_t param; param_t param;
#if defined(HAVE_CRT_EXTERNS_H)
#define environ (*(_NSGetEnviron()))
#else
extern char** environ;
#endif
#endif #endif
HAWK_MEMSET (pio, 0, HAWK_SIZEOF(*pio)); HAWK_MEMSET(pio, 0, HAWK_SIZEOF(*pio));
pio->gem = gem; pio->gem = gem;
pio->flags = flags; pio->flags = flags;
@@ -819,8 +800,8 @@ int hawk_pio_init (hawk_pio_t* pio, hawk_gem_t* gem, const hawk_ooch_t* cmd, int
} }
} }
HAWK_MEMSET (&procinfo, 0, HAWK_SIZEOF(procinfo)); HAWK_MEMSET(&procinfo, 0, HAWK_SIZEOF(procinfo));
HAWK_MEMSET (&startup, 0, HAWK_SIZEOF(startup)); HAWK_MEMSET(&startup, 0, HAWK_SIZEOF(startup));
startup.cb = HAWK_SIZEOF(startup); startup.cb = HAWK_SIZEOF(startup);
@@ -942,7 +923,7 @@ create_process:
&procinfo /* LPPROCESS_INFORMATION lpProcessInformation */ &procinfo /* LPPROCESS_INFORMATION lpProcessInformation */
); );
hawk_gem_freemem (pio->gem, dupcmd); hawk_gem_freemem(pio->gem, dupcmd);
if (apiret == FALSE) if (apiret == FALSE)
{ {
DWORD e = GetLastError(); DWORD e = GetLastError();
@@ -962,27 +943,27 @@ create_process:
if (windevnul != INVALID_HANDLE_VALUE) if (windevnul != INVALID_HANDLE_VALUE)
{ {
CloseHandle (windevnul); CloseHandle(windevnul);
windevnul = INVALID_HANDLE_VALUE; windevnul = INVALID_HANDLE_VALUE;
} }
if (flags & HAWK_PIO_WRITEIN) if (flags & HAWK_PIO_WRITEIN)
{ {
CloseHandle (handle[0]); CloseHandle(handle[0]);
handle[0] = HAWK_PIO_HND_NIL; handle[0] = HAWK_PIO_HND_NIL;
} }
if (flags & HAWK_PIO_READOUT) if (flags & HAWK_PIO_READOUT)
{ {
CloseHandle (handle[3]); CloseHandle(handle[3]);
handle[3] = HAWK_PIO_HND_NIL; handle[3] = HAWK_PIO_HND_NIL;
} }
if (flags & HAWK_PIO_READERR) if (flags & HAWK_PIO_READERR)
{ {
CloseHandle (handle[5]); CloseHandle(handle[5]);
handle[5] = HAWK_PIO_HND_NIL; handle[5] = HAWK_PIO_HND_NIL;
} }
CloseHandle (procinfo.hThread); CloseHandle(procinfo.hThread);
pio->child = procinfo.hProcess; pio->child = procinfo.hProcess;
#elif defined(__OS2__) #elif defined(__OS2__)
@@ -1117,15 +1098,15 @@ create_process:
if (rc != NO_ERROR) if (rc != NO_ERROR)
{ {
hawk_gem_seterrnum(pio->gem, HAWK_NULL, hawk_syserr_to_errnum(rc)); hawk_gem_seterrnum(pio->gem, HAWK_NULL, hawk_syserr_to_errnum(rc));
DosClose (old_in); old_in = HAWK_PIO_HND_NIL; DosClose(old_in); old_in = HAWK_PIO_HND_NIL;
goto oops; goto oops;
} }
rc = DosDupHandle(std_err, &old_err); rc = DosDupHandle(std_err, &old_err);
if (rc != NO_ERROR) if (rc != NO_ERROR)
{ {
hawk_gem_seterrnum(pio->gem, HAWK_NULL, hawk_syserr_to_errnum(rc)); hawk_gem_seterrnum(pio->gem, HAWK_NULL, hawk_syserr_to_errnum(rc));
DosClose (old_out); old_out = HAWK_PIO_HND_NIL; DosClose(old_out); old_out = HAWK_PIO_HND_NIL;
DosClose (old_in); old_in = HAWK_PIO_HND_NIL; DosClose(old_in); old_in = HAWK_PIO_HND_NIL;
goto oops; goto oops;
} }
@@ -1143,7 +1124,7 @@ create_process:
/* the parent writes to handle[1] but does not read from handle[0]. /* the parent writes to handle[1] but does not read from handle[0].
* so we close it */ * so we close it */
DosClose (handle[0]); handle[0] = HAWK_PIO_HND_NIL; DosClose(handle[0]); handle[0] = HAWK_PIO_HND_NIL;
} }
if (flags & HAWK_PIO_READOUT) if (flags & HAWK_PIO_READOUT)
@@ -1154,14 +1135,14 @@ create_process:
if (flags & HAWK_PIO_ERRTOOUT) DOS_DUP_HANDLE (handle[3], &std_err); if (flags & HAWK_PIO_ERRTOOUT) DOS_DUP_HANDLE (handle[3], &std_err);
/* the parent reads from handle[2] but does not write to handle[3]. /* the parent reads from handle[2] but does not write to handle[3].
* so we close it */ * so we close it */
DosClose (handle[3]); handle[3] = HAWK_PIO_HND_NIL; DosClose(handle[3]); handle[3] = HAWK_PIO_HND_NIL;
} }
if (flags & HAWK_PIO_READERR) if (flags & HAWK_PIO_READERR)
{ {
DOS_DUP_HANDLE (handle[5], &std_err); DOS_DUP_HANDLE (handle[5], &std_err);
if (flags & HAWK_PIO_OUTTOERR) DOS_DUP_HANDLE (handle[5], &std_out); if (flags & HAWK_PIO_OUTTOERR) DOS_DUP_HANDLE (handle[5], &std_out);
DosClose (handle[5]); handle[5] = HAWK_PIO_HND_NIL; DosClose(handle[5]); handle[5] = HAWK_PIO_HND_NIL;
} }
if (flags & HAWK_PIO_INTONUL) DOS_DUP_HANDLE (os2devnul, &std_in); if (flags & HAWK_PIO_INTONUL) DOS_DUP_HANDLE (os2devnul, &std_in);
@@ -1171,16 +1152,16 @@ create_process:
if (os2devnul != HAWK_PIO_HND_NIL) if (os2devnul != HAWK_PIO_HND_NIL)
{ {
/* close NUL early as we've duplicated it already */ /* close NUL early as we've duplicated it already */
DosClose (os2devnul); DosClose(os2devnul);
os2devnul = HAWK_PIO_HND_NIL; os2devnul = HAWK_PIO_HND_NIL;
} }
/* at this moment, stdin/out/err are already redirected to pipes /* at this moment, stdin/out/err are already redirected to pipes
* if proper flags have been set. we close them selectively if * if proper flags have been set. we close them selectively if
* dropping is requested */ * dropping is requested */
if (flags & HAWK_PIO_DROPIN) DosClose (std_in); if (flags & HAWK_PIO_DROPIN) DosClose(std_in);
if (flags & HAWK_PIO_DROPOUT) DosClose (std_out); if (flags & HAWK_PIO_DROPOUT) DosClose(std_out);
if (flags & HAWK_PIO_DROPERR) DosClose (std_err); if (flags & HAWK_PIO_DROPERR) DosClose(std_err);
if (flags & HAWK_PIO_SHELL) if (flags & HAWK_PIO_SHELL)
{ {
@@ -1276,7 +1257,7 @@ create_process:
HAWK_SIZEOF(load_error), HAWK_SIZEOF(load_error),
EXEC_ASYNCRESULT, EXEC_ASYNCRESULT,
cmd_line, cmd_line,
HAWK_NULL, HAWK_NULL, /* environment - e.g. "PATH=/bin\0HOME=/tmp\0FOO=bar\0\0" */
&child_rc, &child_rc,
cmd_file cmd_file
); );
@@ -1307,7 +1288,6 @@ create_process:
return -1; return -1;
#else #else
if (flags & HAWK_PIO_WRITEIN) if (flags & HAWK_PIO_WRITEIN)
{ {
if (HAWK_PIPE(&handle[0]) <= -1) if (HAWK_PIPE(&handle[0]) <= -1)
@@ -1350,12 +1330,32 @@ create_process:
{ {
/* i know i'm abusing typecasting here. /* i know i'm abusing typecasting here.
* cmd is supposed to be hawk_pio_fnc_t*, anyway */ * cmd is supposed to be hawk_pio_fnc_t*, anyway */
pid = standard_fork_and_exec(pio, handle, (param_t*)cmd, environ); pid = standard_fork_and_exec(pio, handle, (param_t*)cmd, HAWK_NULL);
if (pid <= -1) goto oops; if (pid <= -1) goto oops;
pio->child = pid; pio->child = pid;
} }
else else
{ {
char*const* envp;
if (env_mk)
{
envp = (char*const*)env_mk(HAWK_PIO_ENV_MK_BCH_PP, env_ctx);
if (HAWK_UNLIKELY(!envp)) goto oops;
/* pio doesn't free the memory block returned by the callback function.
* there is no callback triggered for dealloction either.
* the caller side must do track heap memory chunks allocated for this environment */
}
else
{
#if defined(HAVE_CRT_EXTERNS_H)
#define environ (*(_NSGetEnviron()))
#else
extern char** environ;
#endif
envp = environ;
}
#if defined(HAVE_POSIX_SPAWN) && !(defined(HAWK_SYSCALL0) && defined(SYS_vfork)) #if defined(HAVE_POSIX_SPAWN) && !(defined(HAWK_SYSCALL0) && defined(SYS_vfork))
if ((pserr = posix_spawn_file_actions_init(&fa)) != 0) if ((pserr = posix_spawn_file_actions_init(&fa)) != 0)
@@ -1531,7 +1531,7 @@ create_process:
posix_spawnattr_setflags (&psattr, POSIX_SPAWN_USEVFORK); posix_spawnattr_setflags (&psattr, POSIX_SPAWN_USEVFORK);
#endif #endif
pserr = posix_spawn(&pid, param.argv[0], &fa, &psattr, param.argv, environ); pserr = posix_spawn(&pid, param.argv[0], &fa, &psattr, param.argv, envp);
#if defined(__linux) #if defined(__linux)
posix_spawnattr_destroy (&psattr); posix_spawnattr_destroy (&psattr);
@@ -1683,7 +1683,7 @@ create_process:
if (flags & HAWK_PIO_DROPOUT) HAWK_SYSCALL1(dummy, SYS_close, 1); if (flags & HAWK_PIO_DROPOUT) HAWK_SYSCALL1(dummy, SYS_close, 1);
if (flags & HAWK_PIO_DROPERR) HAWK_SYSCALL1(dummy, SYS_close, 2); if (flags & HAWK_PIO_DROPERR) HAWK_SYSCALL1(dummy, SYS_close, 2);
HAWK_SYSCALL3(dummy, SYS_execve, param.argv[0], param.argv, environ); HAWK_SYSCALL3(dummy, SYS_execve, param.argv[0], param.argv, envp);
/*free_param(pio, &param); don't free this in the vfork version */ /*free_param(pio, &param); don't free this in the vfork version */
child_oops: child_oops:
@@ -1696,7 +1696,6 @@ create_process:
pio->child = pid; pio->child = pid;
#else #else
if (make_param(pio, cmd, flags, &param) <= -1) goto oops; if (make_param(pio, cmd, flags, &param) <= -1) goto oops;
/* check if the command(the command requested or /bin/sh) is /* check if the command(the command requested or /bin/sh) is
@@ -1708,7 +1707,7 @@ create_process:
goto oops; goto oops;
} }
pid = standard_fork_and_exec(pio, handle, &param, environ); pid = standard_fork_and_exec(pio, handle, &param, envp);
if (pid <= -1) if (pid <= -1)
{ {
free_param(pio, &param); free_param(pio, &param);
@@ -1719,7 +1718,6 @@ create_process:
free_param(pio, &param); free_param(pio, &param);
pio->child = pid; pio->child = pid;
#endif #endif
} }
if (flags & HAWK_PIO_WRITEIN) if (flags & HAWK_PIO_WRITEIN)
@@ -1730,7 +1728,7 @@ create_process:
* X * X
* WRITE => 1 * WRITE => 1
*/ */
HAWK_CLOSE (handle[0]); HAWK_CLOSE(handle[0]);
handle[0] = HAWK_PIO_HND_NIL; handle[0] = HAWK_PIO_HND_NIL;
} }
@@ -1742,7 +1740,7 @@ create_process:
* X * X
* READ => 2 * READ => 2
*/ */
HAWK_CLOSE (handle[3]); HAWK_CLOSE(handle[3]);
handle[3] = HAWK_PIO_HND_NIL; handle[3] = HAWK_PIO_HND_NIL;
} }
@@ -1754,7 +1752,7 @@ create_process:
* X * X
* READ => 4 * READ => 4
*/ */
HAWK_CLOSE (handle[5]); HAWK_CLOSE(handle[5]);
handle[5] = HAWK_PIO_HND_NIL; handle[5] = HAWK_PIO_HND_NIL;
} }
#endif #endif
@@ -1807,39 +1805,39 @@ create_process:
oops: oops:
#if defined(_WIN32) #if defined(_WIN32)
if (windevnul != INVALID_HANDLE_VALUE) CloseHandle (windevnul); if (windevnul != INVALID_HANDLE_VALUE) CloseHandle(windevnul);
#elif defined(__OS2__) #elif defined(__OS2__)
if (cmd_line) hawk_gem_freemem (pio->gem, cmd_line); if (cmd_line) hawk_gem_freemem(pio->gem, cmd_line);
if (old_in != HAWK_PIO_HND_NIL) if (old_in != HAWK_PIO_HND_NIL)
{ {
DosDupHandle (old_in, &std_in); DosDupHandle(old_in, &std_in);
DosClose (old_in); DosClose(old_in);
} }
if (old_out != HAWK_PIO_HND_NIL) if (old_out != HAWK_PIO_HND_NIL)
{ {
DosDupHandle (old_out, &std_out); DosDupHandle(old_out, &std_out);
DosClose (old_out); DosClose(old_out);
} }
if (old_err != HAWK_PIO_HND_NIL) if (old_err != HAWK_PIO_HND_NIL)
{ {
DosDupHandle (old_err, &std_err); DosDupHandle(old_err, &std_err);
DosClose (old_err); DosClose(old_err);
} }
if (os2devnul != HAWK_PIO_HND_NIL) DosClose (os2devnul); if (os2devnul != HAWK_PIO_HND_NIL) DosClose(os2devnul);
#endif #endif
for (i = 0; i < HAWK_COUNTOF(tio); i++) for (i = 0; i < HAWK_COUNTOF(tio); i++)
{ {
if (tio[i]) hawk_tio_close (tio[i]); if (tio[i]) hawk_tio_close(tio[i]);
} }
#if defined(_WIN32) #if defined(_WIN32)
for (i = minidx; i < maxidx; i++) CloseHandle (handle[i]); for (i = minidx; i < maxidx; i++) CloseHandle(handle[i]);
#elif defined(__OS2__) #elif defined(__OS2__)
for (i = minidx; i < maxidx; i++) for (i = minidx; i < maxidx; i++)
{ {
if (handle[i] != HAWK_PIO_HND_NIL) DosClose (handle[i]); if (handle[i] != HAWK_PIO_HND_NIL) DosClose(handle[i]);
} }
#elif defined(__DOS__) #elif defined(__DOS__)
@@ -1852,17 +1850,17 @@ oops:
} }
for (i = minidx; i < maxidx; i++) for (i = minidx; i < maxidx; i++)
{ {
if (handle[i] != HAWK_PIO_HND_NIL) HAWK_CLOSE (handle[i]); if (handle[i] != HAWK_PIO_HND_NIL) HAWK_CLOSE(handle[i]);
} }
#elif defined(HAWK_SYSCALL0) && defined(SYS_vfork) #elif defined(HAWK_SYSCALL0) && defined(SYS_vfork)
for (i = minidx; i < maxidx; i++) for (i = minidx; i < maxidx; i++)
{ {
if (handle[i] != HAWK_PIO_HND_NIL) HAWK_CLOSE (handle[i]); if (handle[i] != HAWK_PIO_HND_NIL) HAWK_CLOSE(handle[i]);
} }
#else #else
for (i = minidx; i < maxidx; i++) for (i = minidx; i < maxidx; i++)
{ {
if (handle[i] != HAWK_PIO_HND_NIL) HAWK_CLOSE (handle[i]); if (handle[i] != HAWK_PIO_HND_NIL) HAWK_CLOSE(handle[i]);
} }
#endif #endif
@@ -2115,13 +2113,13 @@ void hawk_pio_end (hawk_pio_t* pio, hawk_pio_hid_t hid)
if (pio->pin[hid].handle != HAWK_PIO_HND_NIL) if (pio->pin[hid].handle != HAWK_PIO_HND_NIL)
{ {
#if defined(_WIN32) #if defined(_WIN32)
CloseHandle (pio->pin[hid].handle); CloseHandle(pio->pin[hid].handle);
#elif defined(__OS2__) #elif defined(__OS2__)
DosClose (pio->pin[hid].handle); DosClose(pio->pin[hid].handle);
#elif defined(__DOS__) #elif defined(__DOS__)
close (pio->pin[hid].handle); close (pio->pin[hid].handle);
#else #else
HAWK_CLOSE (pio->pin[hid].handle); HAWK_CLOSE(pio->pin[hid].handle);
#endif #endif
pio->pin[hid].handle = HAWK_PIO_HND_NIL; pio->pin[hid].handle = HAWK_PIO_HND_NIL;
} }
@@ -2161,13 +2159,13 @@ int hawk_pio_wait (hawk_pio_t* pio)
/* close the handle anyway to prevent further /* close the handle anyway to prevent further
* errors when this function is called again */ * errors when this function is called again */
hawk_gem_seterrnum(pio->gem, HAWK_NULL, hawk_syserr_to_errnum(GetLastError())); hawk_gem_seterrnum(pio->gem, HAWK_NULL, hawk_syserr_to_errnum(GetLastError()));
CloseHandle (pio->child); CloseHandle(pio->child);
pio->child = HAWK_PIO_PID_NIL; pio->child = HAWK_PIO_PID_NIL;
return -1; return -1;
} }
/* close handle here to emulate waitpid() as much as possible. */ /* close handle here to emulate waitpid() as much as possible. */
CloseHandle (pio->child); CloseHandle(pio->child);
pio->child = HAWK_PIO_PID_NIL; pio->child = HAWK_PIO_PID_NIL;
if (ecode == STILL_ACTIVE) if (ecode == STILL_ACTIVE)
@@ -2213,7 +2211,7 @@ int hawk_pio_wait (hawk_pio_t* pio)
} }
/* close handle here to emulate waitpid() as much as possible. */ /* close handle here to emulate waitpid() as much as possible. */
/*DosClose (pio->child);*/ /*DosClose(pio->child);*/
pio->child = HAWK_PIO_PID_NIL; pio->child = HAWK_PIO_PID_NIL;
return (child_rc.codeTerminate == TC_EXIT)? return (child_rc.codeTerminate == TC_EXIT)?
+14 -2
View File
@@ -221,6 +221,7 @@ int hawk_rbt_init (hawk_rbt_t* rbt, hawk_gem_t* gem, int kscale, int vscale)
rbt->scale[HAWK_RBT_KEY] = (kscale < 1)? 1: kscale; rbt->scale[HAWK_RBT_KEY] = (kscale < 1)? 1: kscale;
rbt->scale[HAWK_RBT_VAL] = (vscale < 1)? 1: vscale; rbt->scale[HAWK_RBT_VAL] = (vscale < 1)? 1: vscale;
rbt->size = 0; rbt->size = 0;
rbt->rev = 0;
rbt->style = &style[0]; rbt->style = &style[0];
@@ -262,6 +263,11 @@ hawk_oow_t hawk_rbt_getsize (const hawk_rbt_t* rbt)
return rbt->size; return rbt->size;
} }
hawk_oow_t hawk_rbt_getrev (const hawk_rbt_t* rbt)
{
return rbt->rev;
}
hawk_rbt_pair_t* hawk_rbt_search (const hawk_rbt_t* rbt, const void* kptr, hawk_oow_t klen) hawk_rbt_pair_t* hawk_rbt_search (const hawk_rbt_t* rbt, const void* kptr, hawk_oow_t klen)
{ {
hawk_rbt_pair_t* pair = rbt->root; hawk_rbt_pair_t* pair = rbt->root;
@@ -497,7 +503,7 @@ static hawk_rbt_pair_t* insert (hawk_rbt_t* rbt, void* kptr, hawk_oow_t klen, vo
hawk_rbt_pair_t* x_par = HAWK_NULL; hawk_rbt_pair_t* x_par = HAWK_NULL;
hawk_rbt_pair_t* x_new; hawk_rbt_pair_t* x_new;
while (!IS_NIL(rbt,x_cur)) while (!IS_NIL(rbt, x_cur))
{ {
int n = rbt->style->comper(rbt, kptr, klen, KPTR(x_cur), KLEN(x_cur)); int n = rbt->style->comper(rbt, kptr, klen, KPTR(x_cur), KLEN(x_cur));
if (n == 0) if (n == 0)
@@ -506,7 +512,9 @@ static hawk_rbt_pair_t* insert (hawk_rbt_t* rbt, void* kptr, hawk_oow_t klen, vo
{ {
case UPSERT: case UPSERT:
case UPDATE: case UPDATE:
return change_pair_val(rbt, x_cur, vptr, vlen); x_cur = change_pair_val(rbt, x_cur, vptr, vlen);
if (HAWK_LIKELY(x_cur)) rbt->rev++;
return x_cur;
case ENSERT: case ENSERT:
/* return existing pair */ /* return existing pair */
@@ -561,6 +569,7 @@ static hawk_rbt_pair_t* insert (hawk_rbt_t* rbt, void* kptr, hawk_oow_t klen, vo
rbt->root->color = HAWK_RBT_BLACK; rbt->root->color = HAWK_RBT_BLACK;
rbt->size++; rbt->size++;
rbt->rev++;
return x_new; return x_new;
} }
@@ -639,6 +648,7 @@ hawk_rbt_pair_t* hawk_rbt_cbsert (hawk_rbt_t* rbt, void* kptr, hawk_oow_t klen,
if (x_cur == rbt->root) rbt->root = x_new; if (x_cur == rbt->root) rbt->root = x_new;
} }
rbt->rev++;
return x_new; return x_new;
} }
@@ -678,6 +688,7 @@ hawk_rbt_pair_t* hawk_rbt_cbsert (hawk_rbt_t* rbt, void* kptr, hawk_oow_t klen,
rbt->root->color = HAWK_RBT_BLACK; rbt->root->color = HAWK_RBT_BLACK;
rbt->size++; rbt->size++;
rbt->rev++;
return x_new; return x_new;
} }
@@ -837,6 +848,7 @@ static void delete_pair (hawk_rbt_t* rbt, hawk_rbt_pair_t* pair)
} }
rbt->size--; rbt->size--;
rbt->rev++;
#if defined(HAWK_ENABLE_RBT_ITR_PROTECTION) #if defined(HAWK_ENABLE_RBT_ITR_PROTECTION)
/* an iterator set by hawk_rbt_getfirstpair() or hawk_rbt_getnextpair(), if deleted, gets invalidated. /* an iterator set by hawk_rbt_getfirstpair() or hawk_rbt_getnextpair(), if deleted, gets invalidated.
+247 -12
View File
@@ -189,6 +189,10 @@ typedef struct rxtn_t
hawk_htb_t cmgrtab; hawk_htb_t cmgrtab;
hawk_rtx_ecb_t ecb; hawk_rtx_ecb_t ecb;
void* envp; /* cached environment value */
hawk_map_t* env_map;
hawk_oow_t env_map_rev;
} rxtn_t; } rxtn_t;
typedef struct ioattr_t typedef struct ioattr_t
@@ -208,6 +212,14 @@ static HAWK_INLINE rxtn_t* GET_RXTN(hawk_rtx_t* rtx) { return (rxtn_t*)((hawk_ui
/* ========================================================================= */ /* ========================================================================= */
/* TODO: use wenviron where it's available */
typedef hawk_bch_t env_char_t;
#define ENV_CHAR_IS_BCH
static env_char_t** commit_environ (hawk_rtx_t* rtx, int gbl_id);
/* ========================================================================= */
static void* sys_alloc (hawk_mmgr_t* mmgr, hawk_oow_t size) static void* sys_alloc (hawk_mmgr_t* mmgr, hawk_oow_t size)
{ {
return malloc(size); return malloc(size);
@@ -2036,6 +2048,58 @@ static int parse_rwpipe_uri (const hawk_ooch_t* uri, int* flags, hawk_nwad_t* nw
} }
#endif #endif
static void* envp_maker (hawk_pio_env_mk_type_t env_mk_type, void* ctx)
{
hawk_rtx_t* rtx = ctx;
hawk_t* hawk = hawk_rtx_gethawk(rtx);
if (env_mk_type == HAWK_PIO_ENV_MK_BCH_PP)
{
hawk_val_t* v_env;
hawk_map_t* env_map;
hawk_oow_t env_map_rev;
env_char_t** envp;
xtn_t* xtn;
rxtn_t* rxtn;
xtn = GET_XTN(hawk);
rxtn = GET_RXTN(rtx);
v_env = hawk_rtx_getgbl(rtx, xtn->gbl_environ);
HAWK_ASSERT(v_env != HAWK_NULL);
if (HAWK_RTX_GETVALTYPE(rtx, v_env) == HAWK_VAL_MAP)
{
env_map = ((hawk_val_map_t*)v_env)->map;
HAWK_ASSERT(env_map != HAWK_NULL);
env_map_rev = HAWK_MAP_REV(env_map);
}
else
{
env_map = HAWK_NULL;
env_map_rev = 0;
}
if (!rxtn->envp ||
rxtn->env_map != env_map ||
(env_map && rxtn->env_map_rev != env_map_rev))
{
envp = commit_environ(rtx, xtn->gbl_environ);
if (!envp) return HAWK_NULL;
if (rxtn->envp) hawk_rtx_freemem(rtx, rxtn->envp);
rxtn->envp = envp;
rxtn->env_map = env_map;
rxtn->env_map_rev = env_map_rev;
}
return rxtn->envp;
}
/* TODO: support more types */
/* unsupported env_mk_type */
return HAWK_NULL;
}
static hawk_ooi_t pio_handler_open (hawk_rtx_t* rtx, hawk_rio_arg_t* riod) static hawk_ooi_t pio_handler_open (hawk_rtx_t* rtx, hawk_rio_arg_t* riod)
{ {
hawk_pio_t* handle; hawk_pio_t* handle;
@@ -2074,9 +2138,11 @@ static hawk_ooi_t pio_handler_open (hawk_rtx_t* rtx, hawk_rio_arg_t* riod)
hawk_rtx_getgem(rtx), hawk_rtx_getgem(rtx),
0, 0,
riod->name, riod->name,
flags | HAWK_PIO_SHELL | HAWK_PIO_TEXT | HAWK_PIO_IGNOREECERR flags | HAWK_PIO_SHELL | HAWK_PIO_TEXT | HAWK_PIO_IGNOREECERR,
envp_maker,
rtx
); );
if (handle == HAWK_NULL) return -1; if (!handle) return -1;
#if defined(HAWK_OOCH_IS_UCH) #if defined(HAWK_OOCH_IS_UCH)
{ {
@@ -2776,6 +2842,14 @@ static void fini_rxtn (hawk_rtx_t* rtx, void* ctx)
hawk_htb_fini(&rxtn->cmgrtab); hawk_htb_fini(&rxtn->cmgrtab);
rxtn->cmgrtab_inited = 0; rxtn->cmgrtab_inited = 0;
} }
if (rxtn->envp)
{
hawk_rtx_freemem(rtx, rxtn->envp);
rxtn->envp = HAWK_NULL;
}
rxtn->env_map = HAWK_NULL;
rxtn->env_map_rev = 0;
} }
static int build_argcv (hawk_rtx_t* rtx, int argc_id, int argv_id, const hawk_ooch_t* id, hawk_ooch_t* icf[]) static int build_argcv (hawk_rtx_t* rtx, int argc_id, int argv_id, const hawk_ooch_t* id, hawk_ooch_t* icf[])
@@ -2876,16 +2950,6 @@ static int build_argcv (hawk_rtx_t* rtx, int argc_id, int argv_id, const hawk_oo
return 0; return 0;
} }
/* TODO: use wenviron where it's available */
typedef hawk_bch_t env_char_t;
#define ENV_CHAR_IS_BCH
#if defined(HAVE_CRT_EXTERNS_H)
#define environ (*(_NSGetEnviron()))
#else
extern char** environ;
#endif
static int build_environ (hawk_rtx_t* rtx, int gbl_id, env_char_t* envarr[]) static int build_environ (hawk_rtx_t* rtx, int gbl_id, env_char_t* envarr[])
{ {
hawk_val_t* v_env; hawk_val_t* v_env;
@@ -3015,8 +3079,179 @@ static int build_environ (hawk_rtx_t* rtx, int gbl_id, env_char_t* envarr[])
return 0; return 0;
} }
static env_char_t** commit_environ (hawk_rtx_t* rtx, int gbl_id)
{
hawk_val_t* v_env;
hawk_map_t* map;
hawk_map_itr_t itr;
hawk_map_pair_t* pair;
env_char_t** envp;
env_char_t* ptr;
hawk_oow_t count, ptr_bytes, str_bytes;
v_env = hawk_rtx_getgbl(rtx, gbl_id);
HAWK_ASSERT(v_env != HAWK_NULL);
if (HAWK_RTX_GETVALTYPE(rtx, v_env) != HAWK_VAL_MAP)
{
/* it may be nil. but we can't prevent ENVIRON from being set to
* other scalar types, we should handle this case gracefully */
envp = (env_char_t**)hawk_rtx_allocmem(rtx, HAWK_SIZEOF(*envp) * 1);
if (HAWK_UNLIKELY(!envp)) return HAWK_NULL;
envp[0] = HAWK_NULL;
return envp;
}
map = ((hawk_val_map_t*)v_env)->map;
HAWK_ASSERT(map != HAWK_NULL);
count = 0;
str_bytes = 0;
hawk_init_map_itr(&itr, 0);
pair = hawk_map_getfirstpair(map, &itr);
while (pair)
{
hawk_oow_t klen, vlen;
klen = HAWK_MAP_KLEN(pair);
#if defined(ENV_CHAR_IS_BCH) && defined(HAWK_OOCH_IS_UCH)
{
hawk_oow_t tmp_ulen, tmp_blen;
tmp_ulen = klen;
if (hawk_rtx_convutobchars(rtx, HAWK_MAP_KPTR(pair), &tmp_ulen, HAWK_NULL, &tmp_blen) <= -1) return HAWK_NULL;
klen = tmp_blen;
}
#elif defined(ENV_CHAR_IS_UCH) && defined(HAWK_OOCH_IS_BCH)
{
hawk_oow_t tmp_blen, tmp_ulen;
tmp_blen = klen;
if (hawk_rtx_convbtouchars(rtx, HAWK_MAP_KPTR(pair), &tmp_blen, HAWK_NULL, &tmp_ulen, 1) <= -1) return HAWK_NULL;
klen = tmp_ulen;
}
#endif
#if defined(ENV_CHAR_IS_BCH)
{
hawk_bch_t* vptr;
vptr = hawk_rtx_getvalbcstr(rtx, HAWK_MAP_VPTR(pair), &vlen);
if (HAWK_UNLIKELY(!vptr)) return HAWK_NULL;
hawk_rtx_freevalbcstr(rtx, HAWK_MAP_VPTR(pair), vptr);
}
#else
{
hawk_uch_t* vptr;
vptr = hawk_rtx_getvalucstr(rtx, HAWK_MAP_VPTR(pair), &vlen);
if (HAWK_UNLIKELY(!vptr)) return HAWK_NULL;
hawk_rtx_freevalucstr(rtx, HAWK_MAP_VPTR(pair), vptr);
}
#endif
str_bytes += klen + 1 + vlen + 1;
count++;
pair = hawk_map_getnextpair(map, &itr);
}
ptr_bytes = HAWK_SIZEOF(*envp) * (count + 1);
envp = (env_char_t**)hawk_rtx_allocmem(rtx, ptr_bytes + (str_bytes * HAWK_SIZEOF(*ptr)));
if (HAWK_UNLIKELY(!envp)) return HAWK_NULL;
ptr = (env_char_t*)((hawk_uint8_t*)envp + ptr_bytes);
count = 0;
hawk_init_map_itr(&itr, 0);
pair = hawk_map_getfirstpair(map, &itr);
while (pair)
{
hawk_oow_t klen, vlen;
envp[count++] = ptr;
klen = HAWK_MAP_KLEN(pair);
#if ((defined(ENV_CHAR_IS_BCH) && defined(HAWK_OOCH_IS_BCH)) || \
(defined(ENV_CHAR_IS_UCH) && defined(HAWK_OOCH_IS_UCH)))
HAWK_MEMCPY(ptr, HAWK_MAP_KPTR(pair), klen);
ptr += klen;
#elif defined(ENV_CHAR_IS_BCH)
{
hawk_oow_t tmp_ulen, tmp_blen;
tmp_ulen = klen;
tmp_blen = str_bytes;
if (hawk_rtx_convutobchars(rtx, HAWK_MAP_KPTR(pair), &tmp_ulen, ptr, &tmp_blen) <= -1)
{
hawk_rtx_freemem(rtx, envp);
return HAWK_NULL;
}
ptr += tmp_blen;
klen = tmp_blen;
}
#else
{
hawk_oow_t tmp_blen, tmp_ulen;
tmp_blen = klen;
tmp_ulen = str_bytes;
if (hawk_rtx_convbtouchars(rtx, HAWK_MAP_KPTR(pair), &tmp_blen, ptr, &tmp_ulen, 1) <= -1)
{
hawk_rtx_freemem(rtx, envp);
return HAWK_NULL;
}
ptr += tmp_ulen;
klen = tmp_ulen;
}
#endif
*ptr++ = '=';
#if defined(ENV_CHAR_IS_BCH)
{
hawk_bch_t* vptr;
vptr = hawk_rtx_getvalbcstr(rtx, HAWK_MAP_VPTR(pair), &vlen);
if (HAWK_UNLIKELY(!vptr))
{
hawk_rtx_freemem(rtx, envp);
return HAWK_NULL;
}
HAWK_MEMCPY(ptr, vptr, vlen);
ptr += vlen;
hawk_rtx_freevalbcstr(rtx, HAWK_MAP_VPTR(pair), vptr);
}
#else
{
hawk_uch_t* vptr;
vptr = hawk_rtx_getvalucstr(rtx, HAWK_MAP_VPTR(pair), &vlen);
if (HAWK_UNLIKELY(!vptr))
{
hawk_rtx_freemem(rtx, envp);
return HAWK_NULL;
}
HAWK_MEMCPY(ptr, vptr, vlen);
ptr += vlen;
hawk_rtx_freevalucstr(rtx, HAWK_MAP_VPTR(pair), vptr);
}
#endif
*ptr++ = '\0';
pair = hawk_map_getnextpair(map, &itr);
}
envp[count] = HAWK_NULL;
return envp;
}
static int make_additional_globals (hawk_rtx_t* rtx, xtn_t* xtn, const hawk_ooch_t* id, hawk_ooch_t* icf[]) static int make_additional_globals (hawk_rtx_t* rtx, xtn_t* xtn, const hawk_ooch_t* id, hawk_ooch_t* icf[])
{ {
#if defined(HAVE_CRT_EXTERNS_H)
#define environ (*(_NSGetEnviron()))
#else
extern char** environ;
#endif
if (build_argcv(rtx, xtn->gbl_argc, xtn->gbl_argv, id, icf) <= -1 || if (build_argcv(rtx, xtn->gbl_argc, xtn->gbl_argv, id, icf) <= -1 ||
build_environ(rtx, xtn->gbl_environ, environ) <= -1) return -1; build_environ(rtx, xtn->gbl_environ, environ) <= -1) return -1;
return 0; return 0;
+1 -1
View File
@@ -22,7 +22,7 @@ check_SCRIPTS += h-003.hawk h-004.hawk h-009.hawk h-010.hawk \
h-016.hawk h-017.hawk h-018.hawk h-019.hawk h-020.hawk \ h-016.hawk h-017.hawk h-018.hawk h-019.hawk h-020.hawk \
h-021.hawk h-022.hawk h-023.hawk h-024.hawk h-021.hawk h-022.hawk h-023.hawk h-024.hawk
check_SCRIPTS += regress-filename.sh regress-extra-info.sh check_SCRIPTS += regress-filename.sh regress-extra-info.sh regress-environ.sh
check_ERRORS = e-001.err check_ERRORS = e-001.err
+1 -1
View File
@@ -659,7 +659,7 @@ check_SCRIPTS = $(am__append_1) h-003.hawk h-004.hawk h-009.hawk \
h-010.hawk h-011.hawk h-012.hawk h-013.hawk h-014.hawk \ h-010.hawk h-011.hawk h-012.hawk h-013.hawk h-014.hawk \
h-015.hawk h-016.hawk h-017.hawk h-018.hawk h-019.hawk \ h-015.hawk h-016.hawk h-017.hawk h-018.hawk h-019.hawk \
h-020.hawk h-021.hawk h-022.hawk h-023.hawk h-024.hawk \ h-020.hawk h-021.hawk h-022.hawk h-023.hawk h-024.hawk \
regress-filename.sh regress-extra-info.sh regress-filename.sh regress-extra-info.sh regress-environ.sh
check_ERRORS = e-001.err check_ERRORS = e-001.err
EXTRA_DIST = $(check_SCRIPTS) $(check_ERRORS) tap.inc err.sh \ EXTRA_DIST = $(check_SCRIPTS) $(check_ERRORS) tap.inc err.sh \
journal-toc.hawk journal-toc.in journal-toc.out journal-toc-html.out \ journal-toc.hawk journal-toc.in journal-toc.out journal-toc-html.out \
+130
View File
@@ -0,0 +1,130 @@
#!/bin/sh
[ $# -ge 1 ] && HAWK_BIN="$1"
[ -z "$HAWK_BIN" ] && HAWK_BIN="hawk"
set -u
tmp_prog="/tmp/hawk-regress-environ-$$.hawk"
tmp_pipe="/tmp/hawk-regress-environ-$$.pipe"
trap 'rm -f "$tmp_prog" "$tmp_pipe"' EXIT
cat > "$tmp_prog" <<'EOF'
function emit_env(tag, cmd)
{
cmd = "sh -c 'printf \"" tag ":%s\\n\" \"${HAWK_TEST_ENV_PIPE-unset}\" >&3'"
print "" | cmd
close(cmd)
}
BEGIN {
print hawk::typename(ENVIRON["HAWK_TEST_ENV_INT"]) ":" ENVIRON["HAWK_TEST_ENV_INT"];
print hawk::typename(ENVIRON["HAWK_TEST_ENV_NEG"]) ":" ENVIRON["HAWK_TEST_ENV_NEG"];
print hawk::typename(ENVIRON["HAWK_TEST_ENV_FLT"]) ":" sprintf("%.2f", ENVIRON["HAWK_TEST_ENV_FLT"]);
print hawk::typename(ENVIRON["HAWK_TEST_ENV_STR"]) ":" ENVIRON["HAWK_TEST_ENV_STR"];
print hawk::typename(ENVIRON["HAWK_TEST_ENV_EMPTY"]) ":" length(ENVIRON["HAWK_TEST_ENV_EMPTY"]);
print hawk::typename(ENVIRON["HAWK_TEST_ENV_MISSING"]) ":" hawk::isnil(ENVIRON["HAWK_TEST_ENV_MISSING"]);
ENVIRON["HAWK_TEST_ENV_NEW"] = 456;
print hawk::typename(ENVIRON["HAWK_TEST_ENV_NEW"]) ":" ENVIRON["HAWK_TEST_ENV_NEW"];
ENVIRON["HAWK_TEST_ENV_NEW"] = 7.5;
print hawk::typename(ENVIRON["HAWK_TEST_ENV_NEW"]) ":" sprintf("%.1f", ENVIRON["HAWK_TEST_ENV_NEW"]);
ENVIRON["HAWK_TEST_ENV_NEW"] = "xyz";
print hawk::typename(ENVIRON["HAWK_TEST_ENV_NEW"]) ":" ENVIRON["HAWK_TEST_ENV_NEW"];
delete ENVIRON["HAWK_TEST_ENV_STR"];
print hawk::typename(ENVIRON["HAWK_TEST_ENV_STR"]) ":" hawk::isnil(ENVIRON["HAWK_TEST_ENV_STR"]);
emit_env("initial");
ENVIRON["HAWK_TEST_ENV_PIPE"] = "changed";
emit_env("changed");
delete ENVIRON["HAWK_TEST_ENV_PIPE"];
emit_env("deleted");
ENVIRON = @{};
ENVIRON["HAWK_TEST_ENV_PIPE"] = "replaced";
emit_env("replaced");
ENVIRON = 123;
emit_env("scalar");
}
EOF
test_no=0
failed=0
ok() {
test_no=$((test_no + 1))
echo "ok $test_no - $1"
}
not_ok() {
test_no=$((test_no + 1))
failed=1
echo "not ok $test_no - $1"
echo "# expected: $2"
echo "# actual: $3"
}
check_eq() {
desc="$1"
expected="$2"
actual="$3"
if [ "x$actual" = "x$expected" ]
then
ok "$desc"
else
not_ok "$desc" "$expected" "$actual"
fi
}
echo "1..16"
if out=$(env -i \
HAWK_TEST_ENV_INT=123 \
HAWK_TEST_ENV_NEG=-7 \
HAWK_TEST_ENV_FLT=1.25 \
HAWK_TEST_ENV_STR=abc123 \
HAWK_TEST_ENV_EMPTY= \
HAWK_TEST_ENV_PIPE=initial \
"$HAWK_BIN" -f "$tmp_prog" 3>"$tmp_pipe" 2>&1)
then
ok "run environ regression"
else
not_ok "run environ regression" "exit code 0" "command failed: $out"
out=""
fi
line1=$(printf '%s\n' "$out" | sed -n '1p')
line2=$(printf '%s\n' "$out" | sed -n '2p')
line3=$(printf '%s\n' "$out" | sed -n '3p')
line4=$(printf '%s\n' "$out" | sed -n '4p')
line5=$(printf '%s\n' "$out" | sed -n '5p')
line6=$(printf '%s\n' "$out" | sed -n '6p')
line7=$(printf '%s\n' "$out" | sed -n '7p')
line8=$(printf '%s\n' "$out" | sed -n '8p')
line9=$(printf '%s\n' "$out" | sed -n '9p')
line10=$(printf '%s\n' "$out" | sed -n '10p')
pipe1=$(sed -n '1p' "$tmp_pipe")
pipe2=$(sed -n '2p' "$tmp_pipe")
pipe3=$(sed -n '3p' "$tmp_pipe")
pipe4=$(sed -n '4p' "$tmp_pipe")
pipe5=$(sed -n '5p' "$tmp_pipe")
check_eq "int env imported as int" "int:123" "$line1"
check_eq "negative int env imported as int" "int:-7" "$line2"
check_eq "float env imported as flt" "flt:1.25" "$line3"
check_eq "string env imported as str" "str:abc123" "$line4"
check_eq "empty env imported as empty string" "str:0" "$line5"
check_eq "missing env yields nil" "nil:1" "$line6"
check_eq "assigned int remains int" "int:456" "$line7"
check_eq "assigned float remains flt" "flt:7.5" "$line8"
check_eq "assigned string remains str" "str:xyz" "$line9"
check_eq "deleted env entry becomes nil" "nil:1" "$line10"
check_eq "pipe sees initial imported env" "initial:initial" "$pipe1"
check_eq "pipe sees in-place env update" "changed:changed" "$pipe2"
check_eq "pipe sees deleted env entry" "deleted:unset" "$pipe3"
check_eq "pipe sees replaced environ map" "replaced:replaced" "$pipe4"
check_eq "pipe sees scalar environ as empty" "scalar:unset" "$pipe5"
exit "$failed"