@@ -1040,44 +1040,121 @@ static char *path_lookup(const char *cmd, int exe_only)
1040
1040
return prog ;
1041
1041
}
1042
1042
1043
- static int do_putenv (char * * env , const char * name , int size , int free_old );
1043
+ static const wchar_t * wcschrnul (const wchar_t * s , wchar_t c )
1044
+ {
1045
+ while (* s && * s != c )
1046
+ s ++ ;
1047
+ return s ;
1048
+ }
1049
+
1050
+ /* Compare only keys */
1051
+ static int wenvcmp (const void * a , const void * b )
1052
+ {
1053
+ wchar_t * p = * (wchar_t * * )a , * q = * (wchar_t * * )b ;
1054
+ size_t p_len , q_len ;
1055
+
1056
+ /* Find the keys */
1057
+ p_len = wcschrnul (p , L'=' ) - p ;
1058
+ q_len = wcschrnul (q , L'=' ) - q ;
1059
+
1060
+ /* If the length differs, include the shorter key's NUL */
1061
+ if (p_len < q_len )
1062
+ p_len ++ ;
1063
+ else if (p_len > q_len )
1064
+ p_len = q_len + 1 ;
1065
+
1066
+ return _wcsnicmp (p , q , p_len );
1067
+ }
1044
1068
1045
- /* used number of elements of environ array, including terminating NULL */
1046
- static int environ_size = 0 ;
1047
- /* allocated size of environ array, in bytes */
1048
- static int environ_alloc = 0 ;
1069
+ /* We need a stable sort to convert the environment between UTF-16 <-> UTF-8 */
1070
+ #ifndef INTERNAL_QSORT
1071
+ #include "qsort.c"
1072
+ #endif
1049
1073
1050
1074
/*
1051
- * Create environment block suitable for CreateProcess. Merges current
1052
- * process environment and the supplied environment changes.
1075
+ * Build an environment block combining the inherited environment
1076
+ * merged with the given list of settings.
1077
+ *
1078
+ * Values of the form "KEY=VALUE" in deltaenv override inherited values.
1079
+ * Values of the form "KEY" in deltaenv delete inherited values.
1080
+ *
1081
+ * Multiple entries in deltaenv for the same key are explicitly allowed.
1082
+ *
1083
+ * We return a contiguous block of UNICODE strings with a final trailing
1084
+ * zero word.
1053
1085
*/
1054
1086
static wchar_t * make_environment_block (char * * deltaenv )
1055
1087
{
1056
- wchar_t * wenvblk = NULL ;
1057
- char * * tmpenv ;
1058
- int i = 0 , size = environ_size , wenvsz = 0 , wenvpos = 0 ;
1088
+ wchar_t * wenv = GetEnvironmentStringsW (), * wdeltaenv , * result , * p ;
1089
+ size_t wlen , s , delta_size , size ;
1090
+
1091
+ wchar_t * * array = NULL ;
1092
+ size_t alloc = 0 , nr = 0 , i ;
1093
+
1094
+ size = 1 ; /* for extra NUL at the end */
1095
+
1096
+ /* If there is no deltaenv to apply, simply return a copy. */
1097
+ if (!deltaenv || !* deltaenv ) {
1098
+ for (p = wenv ; p && * p ; ) {
1099
+ size_t s = wcslen (p ) + 1 ;
1100
+ size += s ;
1101
+ p += s ;
1102
+ }
1103
+
1104
+ ALLOC_ARRAY (result , size );
1105
+ memcpy (result , wenv , size * sizeof (* wenv ));
1106
+ FreeEnvironmentStringsW (wenv );
1107
+ return result ;
1108
+ }
1109
+
1110
+ /*
1111
+ * If there is a deltaenv, let's accumulate all keys into `array`,
1112
+ * sort them using the stable git_qsort() and then copy, skipping
1113
+ * duplicate keys
1114
+ */
1115
+ for (p = wenv ; p && * p ; ) {
1116
+ ALLOC_GROW (array , nr + 1 , alloc );
1117
+ s = wcslen (p ) + 1 ;
1118
+ array [nr ++ ] = p ;
1119
+ p += s ;
1120
+ size += s ;
1121
+ }
1122
+
1123
+ /* (over-)assess size needed for wchar version of deltaenv */
1124
+ for (delta_size = 0 , i = 0 ; deltaenv [i ]; i ++ )
1125
+ delta_size += strlen (deltaenv [i ]) * 2 + 1 ;
1126
+ ALLOC_ARRAY (wdeltaenv , delta_size );
1127
+
1128
+ /* convert the deltaenv, appending to array */
1129
+ for (i = 0 , p = wdeltaenv ; deltaenv [i ]; i ++ ) {
1130
+ ALLOC_GROW (array , nr + 1 , alloc );
1131
+ wlen = xutftowcs (p , deltaenv [i ], wdeltaenv + delta_size - p );
1132
+ array [nr ++ ] = p ;
1133
+ p += wlen + 1 ;
1134
+ }
1059
1135
1060
- while ( deltaenv && deltaenv [ i ])
1061
- i ++ ;
1136
+ git_qsort ( array , nr , sizeof ( * array ), wenvcmp );
1137
+ ALLOC_ARRAY ( result , size + delta_size ) ;
1062
1138
1063
- /* copy the environment, leaving space for changes */
1064
- ALLOC_ARRAY (tmpenv , size + i );
1065
- memcpy (tmpenv , environ , size * sizeof (char * ));
1139
+ for (p = result , i = 0 ; i < nr ; i ++ ) {
1140
+ /* Skip any duplicate keys; last one wins */
1141
+ while (i + 1 < nr && !wenvcmp (array + i , array + i + 1 ))
1142
+ i ++ ;
1066
1143
1067
- /* merge supplied environment changes into the temporary environment */
1068
- for ( i = 0 ; deltaenv && deltaenv [i ]; i ++ )
1069
- size = do_putenv ( tmpenv , deltaenv [ i ], size , 0 ) ;
1144
+ /* Skip "to delete" entry */
1145
+ if (! wcschr ( array [i ], L'=' ) )
1146
+ continue ;
1070
1147
1071
- /* create environment block from temporary environment */
1072
- for (i = 0 ; tmpenv [i ]; i ++ ) {
1073
- size = 2 * strlen (tmpenv [i ]) + 2 ; /* +2 for final \0 */
1074
- ALLOC_GROW (wenvblk , (wenvpos + size ) * sizeof (wchar_t ), wenvsz );
1075
- wenvpos += xutftowcs (& wenvblk [wenvpos ], tmpenv [i ], size ) + 1 ;
1148
+ size = wcslen (array [i ]) + 1 ;
1149
+ memcpy (p , array [i ], size * sizeof (* p ));
1150
+ p += size ;
1076
1151
}
1077
- /* add final \0 terminator */
1078
- wenvblk [wenvpos ] = 0 ;
1079
- free (tmpenv );
1080
- return wenvblk ;
1152
+ * p = L'\0' ;
1153
+
1154
+ free (array );
1155
+ free (wdeltaenv );
1156
+ FreeEnvironmentStringsW (wenv );
1157
+ return result ;
1081
1158
}
1082
1159
1083
1160
struct pinfo_t {
@@ -1319,87 +1396,83 @@ int mingw_kill(pid_t pid, int sig)
1319
1396
}
1320
1397
1321
1398
/*
1322
- * Compare environment entries by key (i.e. stopping at '=' or '\0').
1399
+ * UTF-8 versions of getenv(), putenv() and unsetenv().
1400
+ * Internally, they use the CRT's stock UNICODE routines
1401
+ * to avoid data loss.
1323
1402
*/
1324
- static int compareenv (const void * v1 , const void * v2 )
1403
+ char * mingw_getenv (const char * name )
1325
1404
{
1326
- const char * e1 = * (const char * * )v1 ;
1327
- const char * e2 = * (const char * * )v2 ;
1405
+ #define GETENV_MAX_RETAIN 30
1406
+ static char * values [GETENV_MAX_RETAIN ];
1407
+ static int value_counter ;
1408
+ int len_key , len_value ;
1409
+ wchar_t * w_key ;
1410
+ char * value ;
1411
+ wchar_t w_value [32768 ];
1328
1412
1329
- for (;;) {
1330
- int c1 = * e1 ++ ;
1331
- int c2 = * e2 ++ ;
1332
- c1 = (c1 == '=' ) ? 0 : tolower (c1 );
1333
- c2 = (c2 == '=' ) ? 0 : tolower (c2 );
1334
- if (c1 > c2 )
1335
- return 1 ;
1336
- if (c1 < c2 )
1337
- return -1 ;
1338
- if (c1 == 0 )
1339
- return 0 ;
1340
- }
1341
- }
1413
+ if (!name || !* name )
1414
+ return NULL ;
1342
1415
1343
- static int bsearchenv (char * * env , const char * name , size_t size )
1344
- {
1345
- unsigned low = 0 , high = size ;
1346
- while (low < high ) {
1347
- unsigned mid = low + ((high - low ) >> 1 );
1348
- int cmp = compareenv (& env [mid ], & name );
1349
- if (cmp < 0 )
1350
- low = mid + 1 ;
1351
- else if (cmp > 0 )
1352
- high = mid ;
1353
- else
1354
- return mid ;
1416
+ len_key = strlen (name ) + 1 ;
1417
+ /* We cannot use xcalloc() here because that uses getenv() itself */
1418
+ w_key = calloc (len_key , sizeof (wchar_t ));
1419
+ if (!w_key )
1420
+ die ("Out of memory, (tried to allocate %u wchar_t's)" , len_key );
1421
+ xutftowcs (w_key , name , len_key );
1422
+ len_value = GetEnvironmentVariableW (w_key , w_value , ARRAY_SIZE (w_value ));
1423
+ if (!len_value && GetLastError () == ERROR_ENVVAR_NOT_FOUND ) {
1424
+ free (w_key );
1425
+ return NULL ;
1355
1426
}
1356
- return ~low ; /* not found, return 1's complement of insert position */
1427
+ free (w_key );
1428
+
1429
+ len_value = len_value * 3 + 1 ;
1430
+ /* We cannot use xcalloc() here because that uses getenv() itself */
1431
+ value = calloc (len_value , sizeof (char ));
1432
+ if (!value )
1433
+ die ("Out of memory, (tried to allocate %u bytes)" , len_value );
1434
+ xwcstoutf (value , w_value , len_value );
1435
+
1436
+ /*
1437
+ * We return `value` which is an allocated value and the caller is NOT
1438
+ * expecting to have to free it, so we keep a round-robin array,
1439
+ * invalidating the buffer after GETENV_MAX_RETAIN getenv() calls.
1440
+ */
1441
+ free (values [value_counter ]);
1442
+ values [value_counter ++ ] = value ;
1443
+ if (value_counter >= ARRAY_SIZE (values ))
1444
+ value_counter = 0 ;
1445
+
1446
+ return value ;
1357
1447
}
1358
1448
1359
- /*
1360
- * If name contains '=', then sets the variable, otherwise it unsets it
1361
- * Size includes the terminating NULL. Env must have room for size + 1 entries
1362
- * (in case of insert). Returns the new size. Optionally frees removed entries.
1363
- */
1364
- static int do_putenv (char * * env , const char * name , int size , int free_old )
1449
+ int mingw_putenv (const char * namevalue )
1365
1450
{
1366
- int i = bsearchenv (env , name , size - 1 );
1451
+ int size ;
1452
+ wchar_t * wide , * equal ;
1453
+ BOOL result ;
1367
1454
1368
- /* optionally free removed / replaced entry */
1369
- if (i >= 0 && free_old )
1370
- free (env [i ]);
1455
+ if (!namevalue || !* namevalue )
1456
+ return 0 ;
1371
1457
1372
- if (strchr (name , '=' )) {
1373
- /* if new value ('key=value') is specified, insert or replace entry */
1374
- if (i < 0 ) {
1375
- i = ~i ;
1376
- memmove (& env [i + 1 ], & env [i ], (size - i ) * sizeof (char * ));
1377
- size ++ ;
1378
- }
1379
- env [i ] = (char * ) name ;
1380
- } else if (i >= 0 ) {
1381
- /* otherwise ('key') remove existing entry */
1382
- size -- ;
1383
- memmove (& env [i ], & env [i + 1 ], (size - i ) * sizeof (char * ));
1458
+ size = strlen (namevalue ) * 2 + 1 ;
1459
+ wide = calloc (size , sizeof (wchar_t ));
1460
+ if (!wide )
1461
+ die ("Out of memory, (tried to allocate %u wchar_t's)" , size );
1462
+ xutftowcs (wide , namevalue , size );
1463
+ equal = wcschr (wide , L'=' );
1464
+ if (!equal )
1465
+ result = SetEnvironmentVariableW (wide , NULL );
1466
+ else {
1467
+ * equal = L'\0' ;
1468
+ result = SetEnvironmentVariableW (wide , equal + 1 );
1384
1469
}
1385
- return size ;
1386
- }
1470
+ free (wide );
1387
1471
1388
- char * mingw_getenv (const char * name )
1389
- {
1390
- char * value ;
1391
- int pos = bsearchenv (environ , name , environ_size - 1 );
1392
- if (pos < 0 )
1393
- return NULL ;
1394
- value = strchr (environ [pos ], '=' );
1395
- return value ? & value [1 ] : NULL ;
1396
- }
1472
+ if (!result )
1473
+ errno = err_win_to_posix (GetLastError ());
1397
1474
1398
- int mingw_putenv (const char * namevalue )
1399
- {
1400
- ALLOC_GROW (environ , (environ_size + 1 ) * sizeof (char * ), environ_alloc );
1401
- environ_size = do_putenv (environ , namevalue , environ_size , 1 );
1402
- return 0 ;
1475
+ return result ? 0 : -1 ;
1403
1476
}
1404
1477
1405
1478
/*
@@ -2261,17 +2334,6 @@ void mingw_startup(void)
2261
2334
maxlen = wcslen (wargv [0 ]);
2262
2335
for (i = 1 ; i < argc ; i ++ )
2263
2336
maxlen = max (maxlen , wcslen (wargv [i ]));
2264
- for (i = 0 ; wenv [i ]; i ++ )
2265
- maxlen = max (maxlen , wcslen (wenv [i ]));
2266
-
2267
- /*
2268
- * nedmalloc can't free CRT memory, allocate resizable environment
2269
- * list. Note that xmalloc / xmemdupz etc. call getenv, so we cannot
2270
- * use it while initializing the environment itself.
2271
- */
2272
- environ_size = i + 1 ;
2273
- environ_alloc = alloc_nr (environ_size * sizeof (char * ));
2274
- environ = malloc_startup (environ_alloc );
2275
2337
2276
2338
/* allocate buffer (wchar_t encodes to max 3 UTF-8 bytes) */
2277
2339
maxlen = 3 * maxlen + 1 ;
@@ -2280,14 +2342,8 @@ void mingw_startup(void)
2280
2342
/* convert command line arguments and environment to UTF-8 */
2281
2343
for (i = 0 ; i < argc ; i ++ )
2282
2344
__argv [i ] = wcstoutfdup_startup (buffer , wargv [i ], maxlen );
2283
- for (i = 0 ; wenv [i ]; i ++ )
2284
- environ [i ] = wcstoutfdup_startup (buffer , wenv [i ], maxlen );
2285
- environ [i ] = NULL ;
2286
2345
free (buffer );
2287
2346
2288
- /* sort environment for O(log n) getenv / putenv */
2289
- qsort (environ , i , sizeof (char * ), compareenv );
2290
-
2291
2347
/* fix Windows specific environment settings */
2292
2348
setup_windows_environment ();
2293
2349
0 commit comments