@@ -1312,13 +1312,42 @@ static char *path_lookup(const char *cmd, int exe_only)
13121312}
13131313
13141314#if defined(_MSC_VER )
1315+
1316+ /* We need a stable sort */
1317+ #ifndef INTERNAL_QSORT
1318+ #include "qsort.c"
1319+ #endif
1320+
1321+ /* Compare only keys */
1322+ static int wenvcmp (const void * a , const void * b )
1323+ {
1324+ wchar_t * p = * (wchar_t * * )a , * q = * (wchar_t * * )b ;
1325+ size_t p_len , q_len ;
1326+ int ret ;
1327+
1328+ /* Find end of keys */
1329+ for (p_len = 0 ; p [p_len ] && p [p_len ] != L'=' ; p_len ++ )
1330+ ; /* do nothing */
1331+ for (q_len = 0 ; q [q_len ] && q [q_len ] != L'=' ; q_len ++ )
1332+ ; /* do nothing */
1333+
1334+ /* Are keys identical (modulo case)? */
1335+ if (p_len == q_len && !_wcsnicmp (p , q , p_len ))
1336+ return 0 ;
1337+
1338+ ret = _wcsnicmp (p , q , p_len < q_len ? p_len : q_len );
1339+ return ret ? ret : (p_len < q_len ? -1 : +1 );
1340+ }
1341+
13151342/*
13161343 * Build an environment block combining the inherited environment
13171344 * merged with the given list of settings.
13181345 *
13191346 * Values of the form "KEY=VALUE" in deltaenv override inherited values.
13201347 * Values of the form "KEY" in deltaenv delete inherited values.
13211348 *
1349+ * Multiple entries in deltaenv for the same key are explicitly allowed.
1350+ *
13221351 * We return a contiguous block of UNICODE strings with a final trailing
13231352 * zero word.
13241353 */
@@ -1329,155 +1358,96 @@ static wchar_t *make_environment_block(char **deltaenv)
13291358 * as a function that returns a pointer to a mostly static table.
13301359 * Grab the pointer and cache it for the duration of our loop.
13311360 */
1332- extern wchar_t * * _wenviron ;
1333- const wchar_t * * my_wenviron = _wenviron ;
1361+ const wchar_t * wenv = GetEnvironmentStringsW (), * p ;
1362+ size_t delta_size = 0 , size = 1 ; /* for extra NUL at the end */
13341363
1335- /*
1336- * Internally, we create normal 'C' arrays of strings (pointing
1337- * into the blocks) to help with some of the de-dup work.
1338- */
1339- wchar_t * * wptrs_ins = NULL ;
1340- wchar_t * * wptrs_del = NULL ;
1341- wchar_t * wblock_ins = NULL ;
1342- wchar_t * wblock_del = NULL ;
1343- wchar_t * wend_ins ;
1344- wchar_t * wend_del ;
1345- wchar_t * w_ins ;
1346- wchar_t * w_del ;
1347-
1348- int maxlen = 0 ;
1349- int nr_delta = 0 ;
1350- int nr_delta_ins = 0 ;
1351- int nr_delta_del = 0 ;
1352- int nr_wenv = 0 ;
1353- int j , k , k_ins , k_del ;
1364+ wchar_t * * array = NULL ;
1365+ size_t alloc = 0 , nr = 0 , i ;
1366+
1367+ const char * p2 ;
1368+ wchar_t * wdeltaenv ;
1369+
1370+ wchar_t * result , * p3 ;
13541371
13551372 /*
1356- * Count the number of inserts and deletes in the deltaenv list.
1357- * Allocate 'C' arrays for inserts and deletes.
1358- * Also estimate the block size of our results.
1373+ * If there is no deltaenv to apply, simply return a copy
13591374 */
1360- if (deltaenv && deltaenv [0 ] && * deltaenv [0 ]) {
1361- for (k = 0 ; deltaenv && deltaenv [k ] && * deltaenv [k ]; k ++ ) {
1362- if (strchr (deltaenv [k ], '=' ) == NULL )
1363- nr_delta_del ++ ;
1364- else {
1365- maxlen += strlen (deltaenv [k ]) + 1 ;
1366- nr_delta_ins ++ ;
1367- }
1375+ if (!deltaenv || !* deltaenv ) {
1376+ for (p = wenv ; p && * p ; ) {
1377+ size_t s = wcslen (p ) + 1 ;
1378+ size += s ;
1379+ p += s ;
13681380 }
13691381
1370- if (nr_delta_ins )
1371- wptrs_ins = (wchar_t * * )calloc (nr_delta_ins + 1 , sizeof (wchar_t * ));
1372- if (nr_delta_del )
1373- wptrs_del = (wchar_t * * )calloc (nr_delta_del + 1 , sizeof (wchar_t * ));
1374-
1375- nr_delta = nr_delta_ins + nr_delta_del ;
1382+ ALLOC_ARRAY (result , size );
1383+ memcpy (result , wenv , size * sizeof (* wenv ));
1384+ FreeEnvironmentStringsW (wenv );
1385+ return result ;
13761386 }
1377- while (my_wenviron && my_wenviron [nr_wenv ] && * my_wenviron [nr_wenv ])
1378- maxlen += wcslen (my_wenviron [nr_wenv ++ ]) + 1 ;
1379- maxlen ++ ;
13801387
13811388 /*
1382- * Allocate blocks for inserted and deleted items.
1383- * The individual pointers in the 'C' arrays will point into here.
1384- * We will use the wblock_ins as the final result.
1389+ * If there is a deltaenv, let's accumulate all keys into `array`,
1390+ * sort them using the stable git_qsort() and then copy, skipping
1391+ * duplicate keys
13851392 */
1386- if (nr_delta_del ) {
1387- wblock_del = (wchar_t * )calloc (maxlen , sizeof (wchar_t ));
1388- wend_del = wblock_del + maxlen ;
1389- w_del = wblock_del ;
1393+
1394+ for (p = wenv ; p && * p ; ) {
1395+ size_t s = wcslen (p ) + 1 ;
1396+ size += s ;
1397+ ALLOC_GROW (array , nr + 1 , alloc );
1398+ array [nr ++ ] = p ;
1399+ p += s ;
13901400 }
1391- wblock_ins = (wchar_t * )calloc (maxlen , sizeof (wchar_t ));
1392- wend_ins = wblock_ins + maxlen ;
1393- w_ins = wblock_ins ;
13941401
1395- /*
1396- * deltaenv values override inherited environment, so put them
1397- * in the result list first (so that we can de-dup using the
1398- * wide versions of them.
1399- *
1400- * Items in the deltaenv list that DO NOT contain an "=" are
1401- * treated as unsetenv.
1402- *
1403- * Care needs to be taken to handle entries that are added first, and
1404- * then deleted.
1405- */
1406- k_ins = 0 ;
1407- k_del = 0 ;
1408- for (k = 0 ; k < nr_delta ; k ++ ) {
1409- if (strchr (deltaenv [k ], '=' ) == NULL ) {
1410- wchar_t * save = w_del ;
1411- wptrs_del [k_del ++ ] = w_del ;
1412- w_del += xutftowcs (w_del , deltaenv [k ], (wend_del - w_del ));
1413- * w_del ++ = L'=' ; /* append '=' to make lookup easier in next step. */
1414- * w_del ++ = 0 ;
1415-
1416- /* If we added this key, we have to remove it again */
1417- for (j = 0 ; j < k_ins ; j ++ )
1418- if (!wcsnicmp (wptrs_ins [j ], save , w_del - save - 1 )) {
1419- if (j + 1 < k_ins ) {
1420- int delta = sizeof (wchar_t ) * (wptrs_ins [j + 1 ] - wptrs_ins [j ]), i ;
1421- memmove (wptrs_ins [j ], wptrs_ins [j + 1 ], sizeof (wchar_t ) * (w_ins - wptrs_ins [j + 1 ]));
1422- for (i = j ; i < -- k_ins ; i ++ )
1423- wptrs_ins [i ] = wptrs_ins [i + 1 ] - delta ;
1424- w_ins -= delta ;
1425- } else
1426- w_ins = wptrs_ins [j ];
1427- k_ins -- ;
1428- j -- ;
1429- }
1430- } else {
1431- wptrs_ins [k_ins ++ ] = w_ins ;
1432- w_ins += xutftowcs (w_ins , deltaenv [k ], (wend_ins - w_ins )) + 1 ;
1433- }
1402+ /* (over-)assess size needed for wchar version of deltaenv */
1403+ for (i = 0 ; deltaenv [i ]; i ++ ) {
1404+ size_t s = strlen (deltaenv [i ]) + 1 ;
1405+ delta_size += s ;
14341406 }
1435- assert (k_ins <= nr_delta_ins );
1436- assert (k_del == nr_delta_del );
14371407
1438- /*
1439- * Walk the inherited environment and copy over unique, non-deleted
1440- * ones into the result set. Note that we only have to de-dup WRT
1441- * the values from deltaenv, because the inherited set should be unique.
1442- */
1443- for (j = 0 ; j < nr_wenv ; j ++ ) {
1444- const wchar_t * v_j = my_wenviron [j ];
1445- wchar_t * v_j_eq = wcschr (v_j , L'=' );
1446- int len_j_eq , len_j ;
1447-
1448- if (!v_j_eq )
1449- continue ; /* should not happen */
1450- len_j_eq = v_j_eq + 1 - v_j ; /* length(v_j) including '=' */
1451-
1452- /* lookup v_j in list of to-delete vars */
1453- for (k_del = 0 ; k_del < nr_delta_del ; k_del ++ ) {
1454- if (wcsnicmp (v_j , wptrs_del [k_del ], len_j_eq ) == 0 )
1455- goto skip_it ;
1456- }
1408+ ALLOC_ARRAY (wdeltaenv , delta_size );
14571409
1458- /* lookup v_j in deltaenv portion of result set */
1459- for (k_ins = 0 ; k_ins < nr_delta_ins ; k_ins ++ ) {
1460- if (wcsnicmp (v_j , wptrs_ins [k_ins ], len_j_eq ) == 0 )
1461- goto skip_it ;
1462- }
1410+ /* convert the deltaenv, appending to array */
1411+ for (i = 0 , p3 = wdeltaenv ; deltaenv [i ]; i ++ ) {
1412+ size_t s = strlen (deltaenv [i ]) + 1 , wlen ;
1413+ wlen = xutftowcs (p3 , deltaenv [i ], s * 2 );
14631414
1464- /* item is unique, add it to results. */
1465- len_j = wcslen (v_j );
1466- memcpy (w_ins , v_j , len_j * sizeof (wchar_t ));
1467- w_ins += len_j + 1 ;
1415+ ALLOC_GROW (array , nr + 1 , alloc );
1416+ array [nr ++ ] = p3 ;
14681417
1469- skip_it :
1470- ;
1418+ p3 += wlen + 1 ;
14711419 }
14721420
1473- if (wptrs_ins )
1474- free (wptrs_ins );
1475- if (wptrs_del )
1476- free (wptrs_del );
1477- if (wblock_del )
1478- free (wblock_del );
1421+ git_qsort (array , nr , sizeof (* array ), wenvcmp );
1422+ ALLOC_ARRAY (result , size + delta_size );
1423+
1424+ for (p3 = result , i = 0 ; i < nr ; i ++ ) {
1425+ wchar_t * equal = wcschr (array [i ], L'=' );;
14791426
1480- return wblock_ins ;
1427+ /* Skip "to delete" entry */
1428+ if (!equal )
1429+ continue ;
1430+
1431+ p = array [i ];
1432+
1433+ /* Skip any duplicate */
1434+ if (i + 1 < nr ) {
1435+ wchar_t * next = array [i + 1 ];
1436+ size_t n = equal - p ;
1437+
1438+ if (!_wcsnicmp (p , next , n ) && (!next [n ] || next [n ] == L'=' ))
1439+ continue ;
1440+ }
1441+
1442+ size = wcslen (p ) + 1 ;
1443+ memcpy (p3 , p , size * sizeof (* p ));
1444+ p3 += size ;
1445+ }
1446+ * p3 = L'\0' ;
1447+
1448+ free (array );
1449+ FreeEnvironmentStringsW (wenv );
1450+ return result ;
14811451}
14821452
14831453#else
0 commit comments