VirtualBox

Changeset 3316 in kBuild for trunk/src/kmk/incdep.c


Ignore:
Timestamp:
Mar 31, 2020 1:13:22 AM (5 years ago)
Author:
bird
Message:

kmk/incdep.c: Working on parsing filenames with spaces and what-not in them. (needs more testing)

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/kmk/incdep.c

    r3230 r3316  
    12331233}
    12341234
     1235/* Counts slashes backwards from SLASH, stopping at START. */
     1236static size_t incdep_count_slashes_backwards(const char *slash, const char *start)
     1237{
     1238  size_t slashes = 1;
     1239  assert (*slash == '\\');
     1240  while ((uintptr_t)slash > (uintptr_t)start && slash[0 - slashes] == '\\')
     1241    slashes++;
     1242  return slashes;
     1243}
     1244
     1245/* Whitespace cannot be escaped at the end of a line, there has to be
     1246   some stuff following it other than a line continuation slash.
     1247
     1248   So, we look ahead and makes sure that there is something non-whitespaced
     1249   following this allegedly escaped whitespace.
     1250
     1251   This code ASSUMES the file content is zero terminated! */
     1252static int incdep_verify_escaped_whitespace(const char *ws)
     1253{
     1254  char ch;
     1255
     1256  assert(ws[-1] == '\\');
     1257  assert(ISBLANK((unsigned int)ws[0]));
     1258
     1259  /* If the character following the '\ ' sequence is not a whitespace,
     1260     another escape character or null terminator, we're good. */
     1261  ws += 2;
     1262  ch = *ws;
     1263  if (ch != '\\' && !ISSPACE((unsigned int)ch) && ch != '\0')
     1264    return 1;
     1265
     1266  /* Otherwise we'll have to parse forward till we hit the end of the
     1267     line/file or something. */
     1268  while ((ch = *ws++) != '\0')
     1269    {
     1270      if (ch == '\\')
     1271        {
     1272           /* escaped newline? */
     1273           ch = *ws;
     1274           if (ch == '\n')
     1275             ws++;
     1276           else  if (ch == '\r' && ws[1] == '\n')
     1277             ws += 2;
     1278           else
     1279             return 1;
     1280        }
     1281      else if (ISBLANK((unsigned int)ch))
     1282      { /* contine */ }
     1283      else if (!ISSPACE((unsigned int)ch))
     1284        return 1;
     1285      else
     1286        return 0; /* newline; all trailing whitespace will be ignored. */
     1287    }
     1288
     1289  return 0;
     1290}
     1291
     1292/* Unescapes the next filename and returns cached copy.
     1293
     1294   Modifies the input string that START points to.
     1295
     1296   When NEXTP is not NULL, ASSUME target filename and that END isn't entirely
     1297   accurate in case the filename ends with a trailing backslash.  There can be
     1298   more than one filename in a this case.  NEXTP will be set to the first
     1299   character after then filename.
     1300
     1301   When NEXTP is NULL, ASSUME exactly one dependency filename and that END is
     1302   accurately deliminating the string.
     1303   */
     1304static const char *
     1305incdep_unescape_and_cache_filename(struct incdep *curdep,
     1306                                   char *start, const char *end, const char **nextp)
     1307{
     1308  int const is_dep = nextp == NULL;
     1309  unsigned const esc_mask = MAP_BLANK        /*  ' ' + '\t' */
     1310                          | MAP_COLON        /* ':' */
     1311                          | MAP_COMMENT      /* '#' */
     1312                          | MAP_EQUALS       /* '=' */
     1313                          | MAP_SEMI         /* ';' */
     1314                          | (  is_dep
     1315                             ? MAP_PIPE      /* '|' */
     1316                             : MAP_PERCENT); /* '%' */
     1317  unsigned const all_esc_mask = esc_mask | MAP_BLANK | MAP_NEWLINE;
     1318  unsigned const stop_mask = nextp ? MAP_BLANK | MAP_NEWLINE | MAP_COLON : 0;
     1319  char volatile *src;
     1320  char volatile *dst;
     1321
     1322  /*
     1323   * Skip forward to the first escaped character so we can avoid unnecessary shifting.
     1324   */
     1325#if 1
     1326  src = start;
     1327  dst = start;
     1328#elif 1
     1329  static const char s_szStop[] = "\n\r\t ";
     1330
     1331  src = memchr(start, '$', end - start);
     1332  dst = memchr(start, '\\', end - start);
     1333  if (src && ((uintptr_t)src < (uintptr_t)dst || dst == NULL))
     1334    dst = src;
     1335  else if (dst && ((uintptr_t)dst < (uintptr_t)src || src == NULL))
     1336    src = dst;
     1337  else
     1338    {
     1339      assert(src == NULL && dst == NULL);
     1340      if (nextp)
     1341        {
     1342           int i = sizeof(s_szStop);
     1343           while (i-- > 0)
     1344             {
     1345               char *stop = memchr(start, s_szStop[i], end - start);
     1346               if (stop)
     1347                 end = stop;
     1348             }
     1349            *nextp = end;
     1350        }
     1351      return incdep_dep_strcache (curdep, start, end - start);
     1352    }
     1353  if (nextp)
     1354    {
     1355      char *stop = src;
     1356      int i = sizeof(s_szStop);
     1357      while (i-- > 0)
     1358        {
     1359          char *stop2 = memchr(start, s_szStop[i], stop - start);
     1360          if (stop2)
     1361            stop = stop2;
     1362        }
     1363      if (stop != src)
     1364        {
     1365          *nextp = stop;
     1366          return incdep_dep_strcache (curdep, start, stop - start);
     1367        }
     1368    }
     1369#endif
     1370
     1371  /*
     1372   * Copy char-by-char, undoing escaping as we go along.
     1373   */
     1374  while ((uintptr_t)src < (uintptr_t)end)
     1375    {
     1376      const char ch = *src++;
     1377      if (ch != '\\' && ch != '$')
     1378        {
     1379          if (!STOP_SET (ch, stop_mask))
     1380            *dst++ = ch;
     1381          else
     1382            {
     1383              src--;
     1384              break;
     1385            }
     1386        }
     1387      else
     1388        {
     1389          const char ch2 = *src++;  /* No bounds checking to handle "/dir/file\ : ..."  when end points at " :". */
     1390          if (ch == '$')
     1391            {
     1392              if (ch2 != '$') /* $$ -> $ - Ignores secondary expansion! */
     1393                  src--;
     1394              *dst++ = ch;
     1395            }
     1396          else
     1397            {
     1398              unsigned int const ch2_map = stopchar_map[(unsigned char)ch2];
     1399              if (ch2_map & all_esc_mask)
     1400                {
     1401                  /* Count preceeding slashes, unwind half of them regardless of odd/even count. */
     1402                  size_t const max_slashes = src - start - 1;
     1403                  size_t slashes = 1;
     1404                  while (slashes < max_slashes && src[-2 - slashes] == '\\')
     1405                    slashes++;
     1406
     1407                  /* Non-whitespace is simple: Slash slashes, output or stop. */
     1408                  if (!(ch2_map & (MAP_BLANK | MAP_NEWLINE)))
     1409                    {
     1410                      assert(ch2_map & esc_mask);
     1411                      dst -= slashes / 2;
     1412                      if ((slashes & 1) || !(stop_mask & ch2_map))
     1413                        *dst++ = ch2;
     1414                      else
     1415                        {
     1416                          src--;
     1417                          break;
     1418                        }
     1419                    }
     1420                  /* Escaped blanks or newlines.
     1421
     1422                     We have to pretent that we've already replaced any escaped newlines
     1423                     and associated whitespace with a single space here.  We also have to
     1424                     pretend trailing whitespace doesn't exist when IS_DEP is non-zero.
     1425                     This makes for pretty interesting times... */
     1426                  else
     1427                    {
     1428                      char ch3;
     1429
     1430                      /* An Escaped blank is interesting because it is striped unconditionally
     1431                         at the end of a line, regardless of how many escaped newlines may
     1432                         following it.  We join the escaped newline handling if we fine one
     1433                         following us. */
     1434                      if (ch2_map & MAP_BLANK)
     1435                        {
     1436                          /* skip whitespace and check for escaped newline. */
     1437                          volatile char * const src_saved = src;
     1438                          while ((ch3 = *src) != '\0' && ISBLANK(ch3))
     1439                            src++;
     1440                          if (ch3 == '\\' && src[1] == '\n')
     1441                            src += 2; /* Escaped blank & newline joins into single space. */
     1442                          else if (ch3 == '\\' && src[1] == '\r' && src[1] == '\n')
     1443                            src += 3; /* -> Join the escaped newline code below on the next line. */
     1444                          else
     1445                            {
     1446                              src = src_saved;
     1447                              dst -= slashes / 2;
     1448                              if (slashes & 1)
     1449                                {
     1450                                  *dst++ = ch2;
     1451                                  continue;
     1452                                }
     1453                              assert(nextp);
     1454                              break;
     1455                            }
     1456                        }
     1457                      /* Escaped newlines get special treatment as they an any adjacent whitespace
     1458                         gets reduced to a single space, including subsequent escaped newlines.
     1459                         In addition, if this is the final dependency/file and there is no
     1460                         significant new characters following this escaped newline, the replacement
     1461                         space will also be stripped and we won't have anything to escape, meaning
     1462                         that the slashes will remain as is.  Finally, none of this space stuff can
     1463                         be stop characters, unless of course a newline isn't escaped. */
     1464                      else
     1465                        {
     1466                          assert(ch2_map & MAP_NEWLINE);
     1467                          if (ch2 == '\r' && *src == '\n')
     1468                            src++;
     1469                        }
     1470
     1471                      /* common space/newline code */
     1472                      for (;;)
     1473                        {
     1474                          while ((uintptr_t)src < (uintptr_t)end && ISBLANK(*src))
     1475                            src++;
     1476                          if ((uintptr_t)src >= (uintptr_t)end)
     1477                            {
     1478                              ch3 = '\0';
     1479                              break;
     1480                            }
     1481                          ch3 = *src;
     1482                          if (ch3 != '\\')
     1483                            break;
     1484                          ch3 = src[1];
     1485                          if (ch3 == '\n')
     1486                            src += 2;
     1487                          else if (ch3 == '\r' && src[1] == '\n')
     1488                            src += 3;
     1489                          else
     1490                              break;
     1491                        }
     1492
     1493                      if (!ch3 && is_dep)
     1494                        break; /* last thing on the line. */
     1495                      dst -= slashes / 2;
     1496                      if (slashes & 1)
     1497                        *dst++ = ' ';
     1498                      else
     1499                        {
     1500                          assert(nextp);
     1501                          break;
     1502                        }
     1503                    }
     1504                }
     1505              /* Just output the slash if non-escapable character: */
     1506              else
     1507                {
     1508                  src--;
     1509                  *dst++ = ch;
     1510                }
     1511            }
     1512        }
     1513    }
     1514
     1515  if (nextp)
     1516    *nextp = (const char *)src;
     1517  return incdep_dep_strcache(curdep, start, dst - start);
     1518}
    12351519
    12361520/* no nonsense dependency file including.
     
    12661550
    12671551  /* now parse the file. */
    1268   while (cur < file_end)
     1552  while ((uintptr_t)cur < (uintptr_t)file_end)
    12691553    {
    12701554      /* skip empty lines */
    1271       while (cur < file_end && ISSPACE (*cur) && *cur != '\n')
     1555      while ((uintptr_t)cur < (uintptr_t)file_end && ISSPACE (*cur) && *cur != '\n')
    12721556        ++cur;
    1273       if (cur >= file_end)
     1557      if ((uintptr_t)cur >= (uintptr_t)file_end)
    12741558        break;
    12751559      if (*cur == '#')
     
    13311615          cur = value_end = value_start = value_start + 1;
    13321616          ++line_no;
    1333           while (cur < file_end)
     1617          while ((uintptr_t)cur < (uintptr_t)file_end)
    13341618            {
    13351619              /* check for endef, don't bother with skipping leading spaces. */
     
    13381622                {
    13391623                  endp = cur + 5;
    1340                   while (endp < file_end && ISSPACE (*endp) && *endp != '\n')
     1624                  while ((uintptr_t)endp < (uintptr_t)file_end && ISSPACE (*endp) && *endp != '\n')
    13411625                    endp++;
    1342                   if (endp >= file_end || *endp == '\n')
     1626                  if ((uintptr_t)endp >= (uintptr_t)file_end || *endp == '\n')
    13431627                    {
    13441628                      found_endef = 1;
    1345                       cur = endp >= file_end ? file_end : endp + 1;
     1629                      cur = (uintptr_t)endp >= (uintptr_t)file_end ? file_end : endp + 1;
    13461630                      break;
    13471631                    }
     
    13821666                  dst += len;
    13831667                  src = endp;
    1384                   if (src + 1 < file_end && src[1] == '\n')
     1668                  if ((uintptr_t)src + 1 < (uintptr_t)file_end && src[1] == '\n')
    13851669                      src++; /* skip the '\r' */
    13861670                  if (src >= value_end)
     
    14111695          const char *eol;
    14121696
    1413           /* Look for a colon or and equal sign.  In the assignment case, we
     1697          /* Look for a colon or an equal sign.  In the assignment case, we
    14141698             require it to be on the same line as the variable name to simplify
    14151699             the code.  Because of clang, we cannot make the same assumptions
     
    14211705            eol = file_end;
    14221706          equalp = memchr (cur, '=', eol - cur);
    1423           if (equalp)
     1707          if (equalp && equalp != cur && (ISSPACE(equalp[-1]) || equalp[-1] != '\\'))
    14241708            {
    14251709              /* An assignment of some sort. */
     
    14681752              /* find the start of the value. */
    14691753              cur = equalp + 1;
    1470               while (cur < file_end && ISBLANK (*cur))
     1754              while ((uintptr_t)cur < (uintptr_t)file_end && ISBLANK (*cur))
    14711755                cur++;
    14721756              value_start = cur;
     
    14741758              /* find the end of the value / line (this isn't 101% correct). */
    14751759              value_end = cur;
    1476               while (cur < file_end)
     1760              while ((uintptr_t)cur < (uintptr_t)file_end)
    14771761                {
    14781762                  endp = value_end = memchr (cur, '\n', file_end - cur);
     
    15591843              /* Expecting: file: dependencies */
    15601844
     1845              int unescape_filename = 0;
    15611846              const char *filename;
    15621847              const char *fnnext;
     
    15731858
    15741859              colonp = memchr (cur, ':', file_end - cur);
     1860              while (   colonp
     1861                     && (   (   colonp != cur
     1862                             && colonp[-1] == '\\'
     1863                             && incdep_count_slashes_backwards (&colonp[-1], cur) & 1)
    15751864#ifdef HAVE_DOS_PATHS
    1576               while (   colonp
    1577                      && colonp + 1 < file_end
    1578                      && (colonp[1] == '/' || colonp[1] == '\\')
    1579                      && colonp > cur
    1580                      && isalpha ((unsigned char)colonp[-1])
    1581                      && (   colonp == cur + 1
    1582                          || strchr (" \t(", colonp[-2]) != 0))
     1865                         || (   colonp + 1 < file_end
     1866                             && (colonp[1] == '/' || colonp[1] == '\\')
     1867                             && colonp > cur
     1868                             && isalpha ((unsigned char)colonp[-1])
     1869                             && (   colonp == cur + 1
     1870                                 || ISBLANK ((unsigned char)colonp[-2])))
     1871#endif
     1872                        )
     1873                    )
    15831874                  colonp = memchr (colonp + 1, ':', file_end - (colonp + 1));
    1584 #endif
    15851875              if (!colonp)
    15861876                {
     
    16371927                  break;
    16381928                }
    1639               if (memchr (cur, '$', fnend - cur))
     1929              fnnext = cur;
     1930              if (   !memchr (cur, '\\', fnend - cur)
     1931                  && !memchr (cur, '$', fnend - cur))
    16401932                {
    1641                   incdep_warn (curdep, line_no, "fancy file name. (includedep)");
    1642                   break;
     1933                  while (fnnext != fnend && !ISBLANK (*fnnext))
     1934                    fnnext++;
     1935                  filename = incdep_dep_strcache (curdep, cur, fnnext - cur);
    16431936                }
    1644 
    1645               fnnext = cur;
    1646               while (fnnext != fnend && !ISBLANK (*fnnext))
    1647                 fnnext++;
    1648               filename = incdep_dep_strcache (curdep, cur, fnnext - cur);
     1937              else
     1938                {
     1939                  filename = incdep_unescape_and_cache_filename (curdep, (char *)fnnext, fnend, &fnnext);
     1940                  unescape_filename = 1;
     1941                }
    16491942
    16501943              /* parse any dependencies. */
    16511944              cur = colonp + 1;
    1652               while (cur < file_end)
     1945              while ((uintptr_t)cur < (uintptr_t)file_end)
    16531946                {
    16541947                  /* skip blanks and count lines. */
    1655                   while (cur < file_end && ISSPACE (*cur) && *cur != '\n')
     1948                  while ((uintptr_t)cur < (uintptr_t)file_end && ISSPACE (*cur) && *cur != '\n')
    16561949                    ++cur;
    1657                   if (cur >= file_end)
     1950                  if ((uintptr_t)cur >= (uintptr_t)file_end)
    16581951                    break;
    16591952                  if (*cur == '\n')
     
    16801973                  /* find the end of the filename */
    16811974                  endp = cur;
    1682                   while (endp < file_end && !ISSPACE (*endp))
     1975                  while (   (uintptr_t)endp < (uintptr_t)file_end
     1976                         && (   !ISSPACE (*endp)
     1977                             || (endp[-1] == '\\' && incdep_count_slashes_backwards(&endp[-1], cur) & 1)))
    16831978                    ++endp;
    16841979
    16851980                  /* add it to the list. */
    16861981                  *nextdep = dep = incdep_alloc_dep (curdep);
    1687                   dep->name = incdep_dep_strcache (curdep, cur, endp - cur);
    16881982                  dep->includedep = 1;
     1983                  if (   !memchr (cur, '\\', endp - cur)
     1984                      && !memchr (cur, '$', endp - cur))
     1985                    dep->name = incdep_dep_strcache(curdep, cur, endp - cur);
     1986                  else
     1987                    dep->name = incdep_unescape_and_cache_filename (curdep, (char *)cur, endp, NULL);
    16891988                  nextdep = &dep->next;
    16901989
     
    16961995
    16971996              /* More files? Record them with the same dependency list. */
    1698               if (fnnext != fnend)
     1997              if ((uintptr_t)fnnext < (uintptr_t)fnend)
    16991998                for (;;)
    17001999                  {
    17012000                    const char *filename_prev = filename;
    1702                     const char *fnstart;
    17032001                    while (fnnext != fnend && ISBLANK (*fnnext))
    17042002                      fnnext++;
     
    17062004                      break;
    17072005
    1708                     fnstart = fnnext;
    1709                     while (fnnext != fnend && !ISBLANK (*fnnext))
    1710                       fnnext++;
    1711 
    1712                     filename = incdep_dep_strcache (curdep, fnstart, fnnext - fnstart);
     2006                    if (!unescape_filename)
     2007                      {
     2008                        const char *fnstart = fnnext;
     2009                        while (fnnext != fnend && !ISBLANK (*fnnext))
     2010                          fnnext++;
     2011                        filename = incdep_dep_strcache (curdep, fnstart, fnnext - fnstart);
     2012                      }
     2013                    else
     2014                      filename = incdep_unescape_and_cache_filename (curdep, (char *)fnnext, fnend, &fnnext);
    17132015                    if (filename != filename_prev) /* clang optimization. */
    17142016                      incdep_record_file (curdep, filename, incdep_dup_dep_list (curdep, deps), f);
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette