@@ -1217,6 +1217,147 @@ static char *path_lookup(const char *cmd, int exe_only)
1217
1217
return prog ;
1218
1218
}
1219
1219
1220
+ #if defined(_MSC_VER )
1221
+
1222
+ /* We need a stable sort */
1223
+ #ifndef INTERNAL_QSORT
1224
+ #include "qsort.c"
1225
+ #endif
1226
+
1227
+ /* Compare only keys */
1228
+ static int wenvcmp (const void * a , const void * b )
1229
+ {
1230
+ wchar_t * p = * (wchar_t * * )a , * q = * (wchar_t * * )b ;
1231
+ size_t p_len , q_len ;
1232
+ int ret ;
1233
+
1234
+ /* Find end of keys */
1235
+ for (p_len = 0 ; p [p_len ] && p [p_len ] != L'=' ; p_len ++ )
1236
+ ; /* do nothing */
1237
+ for (q_len = 0 ; q [q_len ] && q [q_len ] != L'=' ; q_len ++ )
1238
+ ; /* do nothing */
1239
+
1240
+ /* Are keys identical (modulo case)? */
1241
+ if (p_len == q_len && !_wcsnicmp (p , q , p_len ))
1242
+ return 0 ;
1243
+
1244
+ ret = _wcsnicmp (p , q , p_len < q_len ? p_len : q_len );
1245
+ return ret ? ret : (p_len < q_len ? -1 : +1 );
1246
+ }
1247
+
1248
+ /*
1249
+ * Build an environment block combining the inherited environment
1250
+ * merged with the given list of settings.
1251
+ *
1252
+ * Values of the form "KEY=VALUE" in deltaenv override inherited values.
1253
+ * Values of the form "KEY" in deltaenv delete inherited values.
1254
+ *
1255
+ * Multiple entries in deltaenv for the same key are explicitly allowed.
1256
+ *
1257
+ * We return a contiguous block of UNICODE strings with a final trailing
1258
+ * zero word.
1259
+ */
1260
+ static wchar_t * make_environment_block (char * * deltaenv )
1261
+ {
1262
+ /*
1263
+ * The CRT (at least as of UCRT) secretly declares "_wenviron"
1264
+ * as a function that returns a pointer to a mostly static table.
1265
+ * Grab the pointer and cache it for the duration of our loop.
1266
+ */
1267
+ const wchar_t * wenv = GetEnvironmentStringsW (), * p ;
1268
+ size_t delta_size = 0 , size = 1 ; /* for extra NUL at the end */
1269
+
1270
+ wchar_t * * array = NULL ;
1271
+ size_t alloc = 0 , nr = 0 , i ;
1272
+
1273
+ const char * p2 ;
1274
+ wchar_t * wdeltaenv ;
1275
+
1276
+ wchar_t * result , * p3 ;
1277
+
1278
+ /*
1279
+ * If there is no deltaenv to apply, simply return a copy
1280
+ */
1281
+ if (!deltaenv || !* deltaenv ) {
1282
+ for (p = wenv ; p && * p ; ) {
1283
+ size_t s = wcslen (p ) + 1 ;
1284
+ size += s ;
1285
+ p += s ;
1286
+ }
1287
+
1288
+ ALLOC_ARRAY (result , size );
1289
+ memcpy (result , wenv , size * sizeof (* wenv ));
1290
+ FreeEnvironmentStringsW (wenv );
1291
+ return result ;
1292
+ }
1293
+
1294
+ /*
1295
+ * If there is a deltaenv, let's accumulate all keys into `array`,
1296
+ * sort them using the stable git_qsort() and then copy, skipping
1297
+ * duplicate keys
1298
+ */
1299
+
1300
+ for (p = wenv ; p && * p ; ) {
1301
+ size_t s = wcslen (p ) + 1 ;
1302
+ size += s ;
1303
+ ALLOC_GROW (array , nr + 1 , alloc );
1304
+ array [nr ++ ] = p ;
1305
+ p += s ;
1306
+ }
1307
+
1308
+ /* (over-)assess size needed for wchar version of deltaenv */
1309
+ for (i = 0 ; deltaenv [i ]; i ++ ) {
1310
+ size_t s = strlen (deltaenv [i ]) + 1 ;
1311
+ delta_size += s ;
1312
+ }
1313
+
1314
+ ALLOC_ARRAY (wdeltaenv , delta_size );
1315
+
1316
+ /* convert the deltaenv, appending to array */
1317
+ for (i = 0 , p3 = wdeltaenv ; deltaenv [i ]; i ++ ) {
1318
+ size_t s = strlen (deltaenv [i ]) + 1 , wlen ;
1319
+ wlen = xutftowcs (p3 , deltaenv [i ], s * 2 );
1320
+
1321
+ ALLOC_GROW (array , nr + 1 , alloc );
1322
+ array [nr ++ ] = p3 ;
1323
+
1324
+ p3 += wlen + 1 ;
1325
+ }
1326
+
1327
+ git_qsort (array , nr , sizeof (* array ), wenvcmp );
1328
+ ALLOC_ARRAY (result , size + delta_size );
1329
+
1330
+ for (p3 = result , i = 0 ; i < nr ; i ++ ) {
1331
+ wchar_t * equal = wcschr (array [i ], L'=' );;
1332
+
1333
+ /* Skip "to delete" entry */
1334
+ if (!equal )
1335
+ continue ;
1336
+
1337
+ p = array [i ];
1338
+
1339
+ /* Skip any duplicate */
1340
+ if (i + 1 < nr ) {
1341
+ wchar_t * next = array [i + 1 ];
1342
+ size_t n = equal - p ;
1343
+
1344
+ if (!_wcsnicmp (p , next , n ) && (!next [n ] || next [n ] == L'=' ))
1345
+ continue ;
1346
+ }
1347
+
1348
+ size = wcslen (p ) + 1 ;
1349
+ memcpy (p3 , p , size * sizeof (* p ));
1350
+ p3 += size ;
1351
+ }
1352
+ * p3 = L'\0' ;
1353
+
1354
+ free (array );
1355
+ FreeEnvironmentStringsW (wenv );
1356
+ return result ;
1357
+ }
1358
+
1359
+ #else
1360
+
1220
1361
static int do_putenv (char * * env , const char * name , int size , int free_old );
1221
1362
1222
1363
/* used number of elements of environ array, including terminating NULL */
@@ -1263,6 +1404,7 @@ static wchar_t *make_environment_block(char **deltaenv)
1263
1404
free (tmpenv );
1264
1405
return wenvblk ;
1265
1406
}
1407
+ #endif
1266
1408
1267
1409
static void do_unset_environment_variables (void )
1268
1410
{
@@ -1553,6 +1695,70 @@ int mingw_kill(pid_t pid, int sig)
1553
1695
return -1 ;
1554
1696
}
1555
1697
1698
+ #if defined(_MSC_VER )
1699
+
1700
+ /* UTF8 versions of getenv and putenv (and unsetenv).
1701
+ * Internally, they use the CRT's stock UNICODE routines
1702
+ * to avoid data loss.
1703
+ *
1704
+ * Unlike the mingw version, we DO NOT directly write to
1705
+ * the CRT variables. We also DO NOT try to manage/replace
1706
+ * the CRT storage.
1707
+ */
1708
+ char * msc_getenv (const char * name )
1709
+ {
1710
+ int len_key , len_value ;
1711
+ wchar_t * w_key ;
1712
+ char * value ;
1713
+ const wchar_t * w_value ;
1714
+
1715
+ if (!name || !* name )
1716
+ return NULL ;
1717
+
1718
+ len_key = strlen (name ) + 1 ;
1719
+ w_key = calloc (len_key , sizeof (wchar_t ));
1720
+ xutftowcs (w_key , name , len_key );
1721
+ w_value = _wgetenv (w_key );
1722
+ free (w_key );
1723
+
1724
+ if (!w_value )
1725
+ return NULL ;
1726
+
1727
+ len_value = wcslen (w_value ) * 3 + 1 ;
1728
+ value = calloc (len_value , sizeof (char ));
1729
+ xwcstoutf (value , w_value , len_value );
1730
+
1731
+ /* TODO Warning: We return "value" which is an allocated
1732
+ * value and the caller is NOT expecting to have to free
1733
+ * it, so we leak memory.
1734
+ */
1735
+ return value ;
1736
+ }
1737
+
1738
+ int msc_putenv (const char * name )
1739
+ {
1740
+ int len , result ;
1741
+ char * equal ;
1742
+ wchar_t * wide ;
1743
+
1744
+ if (!name || !* name )
1745
+ return 0 ;
1746
+
1747
+ len = strlen (name );
1748
+ equal = strchr (name , '=' );
1749
+ wide = calloc (len + 1 + !equal , sizeof (wchar_t ));
1750
+ xutftowcs (wide , name , len + 1 );
1751
+ if (!equal )
1752
+ wcscat (wide , L"=" );
1753
+
1754
+ result = _wputenv (wide );
1755
+
1756
+ free (wide );
1757
+ return result ;
1758
+ }
1759
+
1760
+ #else
1761
+
1556
1762
/*
1557
1763
* Compare environment entries by key (i.e. stopping at '=' or '\0').
1558
1764
*/
@@ -1681,6 +1887,8 @@ int mingw_putenv(const char *namevalue)
1681
1887
return 0 ;
1682
1888
}
1683
1889
1890
+ #endif
1891
+
1684
1892
/*
1685
1893
* Note, this isn't a complete replacement for getaddrinfo. It assumes
1686
1894
* that service contains a numerical port, or that it is null. It
0 commit comments