ReactOS 0.4.17-dev-1343-g8fe3cee
parser.c File Reference
#include "precomp.h"
Include dependency graph for parser.c:

Go to the source code of this file.

Macros

#define MSCMD_ECHO_COMMAND_COMPAT
 
#define MSCMD_PARSER_BUGS
 
#define MSCMD_REDIR_PARSE_BUGS
 
#define C_OP_LOWEST   C_MULTI
 
#define C_OP_HIGHEST   C_PIPE
 
#define IF_MAX_UNARY   IF_DEFINED
 
#define IF_MAX_COMPARISON   IF_GEQ
 
#define DUMP(Command, Pad)
 
#define CHAR(Char)
 
#define STRING(String)
 
#define PRINTF(Format, ...)
 
#define RECURSE(Subcommand)
 

Typedefs

typedef enum _TOK_TYPE TOK_TYPE
 

Enumerations

enum  _TOK_TYPE {
  TOK_END , TOK_NORMAL , TOK_OPERATOR , TOK_REDIRECTION ,
  TOK_BEGIN_BLOCK , TOK_END_BLOCK
}
 

Functions

static __inline BOOL IsSeparator (TCHAR Char)
 
static VOID ResetParser (IN PTCHAR Pos)
 
static VOID UnParseToken (VOID)
 
static VOID InitParser (VOID)
 
static TCHAR ParseChar (VOID)
 
VOID ParseErrorEx (IN PCTSTR s)
 
static __inline VOID ParseError (VOID)
 
static TOK_TYPE ParseTokenEx (IN TCHAR PrefixOperator OPTIONAL, IN TCHAR ExtraEnd OPTIONAL, IN PCTSTR Separators OPTIONAL, IN BOOL bHandleContinuations)
 
static __inline INT ParseToken (IN TCHAR ExtraEnd OPTIONAL, IN PCTSTR Separators OPTIONAL)
 
static PARSED_COMMAND * AllocCommand (IN COMMAND_TYPE Type, IN PCTSTR CmdHead OPTIONAL, IN PCTSTR CmdTail OPTIONAL)
 
VOID FreeCommand (IN OUT PARSED_COMMAND *Cmd)
 
static BOOL ParseRedirection (IN OUT REDIRECTION **List)
 
static __inline PARSED_COMMAND * ParseCommandOp (IN COMMAND_TYPE OpType)
 
static PARSED_COMMAND * ParseBlock (IN OUT REDIRECTION **RedirList)
 
static PARSED_COMMAND * ParseIf (VOID)
 
static PARSED_COMMAND * ParseFor (VOID)
 
static PARSED_COMMAND * ParseRem (VOID)
 
static PARSED_COMMAND * ParseCommandPart (IN OUT REDIRECTION **RedirList)
 
static PARSED_COMMAND * ParsePrimary (VOID)
 
static PARSED_COMMAND * ParseCommandBinaryOp (IN COMMAND_TYPE OpType)
 
PARSED_COMMAND * ParseCommand (IN PCTSTR Line)
 
static VOID DumpRedir (IN REDIRECTION *Redirections)
 
VOID DumpCommand (IN PARSED_COMMAND *Cmd, IN ULONG SpacePad)
 
VOID EchoCommand (IN PARSED_COMMAND *Cmd)
 
PTCHAR UnparseCommand (IN PARSED_COMMAND *Cmd, OUT PTCHAR Out, IN PTCHAR OutEnd)
 

Variables

BOOLEAN fDumpTokens = FALSE
 
BOOLEAN fDumpParse = FALSE
 
static const TCHAR OpString [][3] = { _T("&"), _T("||"), _T("&&"), _T("|") }
 
static const TCHAR RedirString [][3] = { _T("<"), _T(">"), _T(">>") }
 
static const TCHAR *const IfOperatorString []
 
static TCHAR TempBuf [CMDLINE_LENGTH]
 
BOOL bParseError
 
static BOOL bLineContinuations
 
TCHAR ParseLine [CMDLINE_LENGTH]
 
static PTCHAR ParsePos
 
static PTCHAR OldParsePos
 
BOOL bIgnoreParserComments = TRUE
 
BOOL bHandleContinuations = TRUE
 
static TCHAR CurrentToken [CMDLINE_LENGTH]
 
static TOK_TYPE CurrentTokenType = TOK_END
 
static INT InsideBlock = 0
 

Macro Definition Documentation

◆ C_OP_HIGHEST

#define C_OP_HIGHEST   C_PIPE

Definition at line 39 of file parser.c.

◆ C_OP_LOWEST

#define C_OP_LOWEST   C_MULTI

Definition at line 38 of file parser.c.

◆ CHAR

#define CHAR (   Char)
Value:
do { \
if (Out == OutEnd) return NULL; \
*Out++ = Char; \
} while (0)
@ Out
#define NULL
Definition: types.h:112

◆ DUMP

#define DUMP (   Command,
  Pad 
)
Value:
do { \
Cmd = (Command); \
SpacePad = (Pad); \
goto dump; \
} while (0)
macro TEXTAREA section xr p2align endm macro DATAAREA section dw endm macro RODATAAREA section dr endm macro XDATAAREA section dr p2align endm macro PDATAAREA section dr p2align endm macro DCRVA Symbol rva Symbol endm macro EXPORT Attribute global Name endm macro IMPORT Target ifc WEAK weak Name set Target endif endm set macro SET_COMDAT_ALIGNMENT Alignment set Alignment endm macro NESTED_ENTRY Name global Name p2align xr p2align __ComdatAlignment NESTED_ENTRY Name endm macro ALTERNATE_ENTRY Name global Name Params endm macro ALIGN Pad
Definition: kxarm64.h:189
static void dump(const void *ptr, unsigned len)
Definition: msc.c:95
@ Cmd
Definition: sacdrv.h:278
Definition: shell.h:41

◆ IF_MAX_COMPARISON

#define IF_MAX_COMPARISON   IF_GEQ

◆ IF_MAX_UNARY

#define IF_MAX_UNARY   IF_DEFINED

◆ MSCMD_ECHO_COMMAND_COMPAT

#define MSCMD_ECHO_COMMAND_COMPAT

Definition at line 12 of file parser.c.

◆ MSCMD_PARSER_BUGS

#define MSCMD_PARSER_BUGS

Definition at line 15 of file parser.c.

◆ MSCMD_REDIR_PARSE_BUGS

#define MSCMD_REDIR_PARSE_BUGS

Definition at line 21 of file parser.c.

◆ PRINTF

#define PRINTF (   Format,
  ... 
)
Value:
do { \
UINT Len = _sntprintf(Out, OutEnd - Out, Format, __VA_ARGS__); \
if (Len > (UINT)(OutEnd - Out)) return NULL; \
Out += Len; \
} while (0)
#define _sntprintf
Definition: _sntprintf.h:29
#define Len
Definition: deflate.h:82
unsigned int UINT
Definition: sysinfo.c:13

◆ RECURSE

#define RECURSE (   Subcommand)
Value:
do { \
Out = UnparseCommand(Subcommand, Out, OutEnd); \
if (!Out) return NULL; \
} while (0)
PTCHAR UnparseCommand(IN PARSED_COMMAND *Cmd, OUT PTCHAR Out, IN PTCHAR OutEnd)
Definition: parser.c:1965

◆ STRING

Value:
do { \
if (Out + _tcslen(String) > OutEnd) return NULL; \
} while (0)
LPTSTR _stpcpy(LPTSTR, LPCTSTR)
Definition: misc.c:474
#define _tcslen
Definition: tchar.h:626
_Must_inspect_result_ _In_ WDFDEVICE _In_ WDFSTRING String
Definition: wdfdevice.h:2439

Typedef Documentation

◆ TOK_TYPE

Enumeration Type Documentation

◆ _TOK_TYPE

Enumerator
TOK_END 
TOK_NORMAL 
TOK_OPERATOR 
TOK_REDIRECTION 
TOK_BEGIN_BLOCK 
TOK_END_BLOCK 

Definition at line 77 of file parser.c.

78{
79 TOK_END,
85} TOK_TYPE;
enum _TOK_TYPE TOK_TYPE
@ TOK_REDIRECTION
Definition: parser.c:82
@ TOK_OPERATOR
Definition: parser.c:81
@ TOK_NORMAL
Definition: parser.c:80
@ TOK_END_BLOCK
Definition: parser.c:84
@ TOK_BEGIN_BLOCK
Definition: parser.c:83
@ TOK_END
Definition: parser.c:79

Function Documentation

◆ AllocCommand()

static PARSED_COMMAND * AllocCommand ( IN COMMAND_TYPE  Type,
IN PCTSTR CmdHead  OPTIONAL,
IN PCTSTR CmdTail  OPTIONAL 
)
static

Definition at line 459 of file parser.c.

463{
465
466 switch (Type)
467 {
468 case C_COMMAND:
469 case C_REM:
470 {
471 SIZE_T CmdHeadLen = _tcslen(CmdHead) + 1;
472 SIZE_T CmdTailLen = _tcslen(CmdTail) + 1;
473
475 Command.First[CmdHeadLen + CmdTailLen]));
476 if (!Cmd)
477 return NULL;
478
479 Cmd->Type = Type;
480 Cmd->Next = NULL;
481 Cmd->Subcommands = NULL;
482 Cmd->Redirections = NULL; /* Is assigned by the calling function */
483 memcpy(Cmd->Command.First, CmdHead, CmdHeadLen * sizeof(TCHAR));
484 Cmd->Command.Rest = Cmd->Command.First + CmdHeadLen;
485 memcpy(Cmd->Command.Rest, CmdTail, CmdTailLen * sizeof(TCHAR));
486 return Cmd;
487 }
488
489 case C_QUIET:
490 case C_BLOCK:
491 case C_MULTI:
492 case C_OR:
493 case C_AND:
494 case C_PIPE:
495 {
496 Cmd = cmd_alloc(sizeof(PARSED_COMMAND));
497 if (!Cmd)
498 return NULL;
499
500 Cmd->Type = Type;
501 Cmd->Next = NULL;
502 Cmd->Subcommands = NULL;
503 Cmd->Redirections = NULL; /* For C_BLOCK only: is assigned by the calling function */
504 return Cmd;
505 }
506
507 case C_FOR:
508 case C_IF:
509 {
510 Cmd = cmd_alloc(sizeof(PARSED_COMMAND));
511 if (!Cmd)
512 return NULL;
513
514 memset(Cmd, 0, sizeof(PARSED_COMMAND));
515 Cmd->Type = Type;
516 return Cmd;
517 }
518
519 default:
520 ERR("Unknown command type 0x%x\n", Type);
521 ASSERT(FALSE);
522 return NULL;
523 }
524}
Type
Definition: Type.h:7
@ C_QUIET
Definition: cmd.h:356
@ C_OR
Definition: cmd.h:360
@ C_COMMAND
Definition: cmd.h:354
@ C_REM
Definition: cmd.h:362
@ C_AND
Definition: cmd.h:360
@ C_MULTI
Definition: cmd.h:360
@ C_IF
Definition: cmd.h:362
@ C_PIPE
Definition: cmd.h:360
@ C_FOR
Definition: cmd.h:362
#define ERR(fmt,...)
Definition: precomp.h:57
#define cmd_alloc(size)
Definition: cmddbg.h:29
#define FALSE
Definition: types.h:117
char TCHAR
Definition: tchar.h:1402
#define memcpy(s1, s2, n)
Definition: mkisofs.h:878
#define ASSERT(a)
Definition: mode.c:44
#define C_BLOCK
Definition: rsym.h:130
#define memset(x, y, z)
Definition: compat.h:39
#define FIELD_OFFSET(t, f)
Definition: typedefs.h:255
ULONG_PTR SIZE_T
Definition: typedefs.h:80

Referenced by ParseBlock(), ParseCommandBinaryOp(), ParseCommandPart(), ParseFor(), ParseIf(), ParsePrimary(), and ParseRem().

◆ DumpCommand()

VOID DumpCommand ( IN PARSED_COMMAND *  Cmd,
IN ULONG  SpacePad 
)

Definition at line 1533 of file parser.c.

1536{
1537/*
1538 * This macro is like DumpCommand(Cmd, Pad);
1539 * but avoids an extra recursive level.
1540 * Note that it can be used ONLY for terminating commands!
1541 */
1542#define DUMP(Command, Pad) \
1543do { \
1544 Cmd = (Command); \
1545 SpacePad = (Pad); \
1546 goto dump; \
1547} while (0)
1548
1549 PARSED_COMMAND* Sub;
1550
1551dump:
1552 if (!Cmd)
1553 return;
1554
1555 /* Space padding */
1556 ConOutPrintf(_T("%*s"), SpacePad, _T(""));
1557
1558 switch (Cmd->Type)
1559 {
1560 case C_COMMAND:
1561 case C_REM:
1562 {
1563 /* Generic command name, and Type */
1564#ifndef MSCMD_ECHO_COMMAND_COMPAT
1565 ConOutPrintf(_T("Cmd: %s Type: %x"),
1566 Cmd->Command.First, Cmd->Type);
1567#else
1568 ConOutPrintf(_T("Cmd: %s Type: %x "),
1569 Cmd->Command.First, Cmd->Type);
1570#endif
1571 /* Arguments */
1572 if (Cmd->Command.Rest && *(Cmd->Command.Rest))
1573#ifndef MSCMD_ECHO_COMMAND_COMPAT
1574 ConOutPrintf(_T(" Args: `%s'"), Cmd->Command.Rest);
1575#else
1576 ConOutPrintf(_T("Args: `%s' "), Cmd->Command.Rest);
1577#endif
1578 /* Redirections */
1579 DumpRedir(Cmd->Redirections);
1580
1581 ConOutChar(_T('\n'));
1582 return;
1583 }
1584
1585 case C_QUIET:
1586 {
1587#ifndef MSCMD_ECHO_COMMAND_COMPAT
1588 ConOutChar(_T('@'));
1589#else
1590 ConOutPuts(_T("@ "));
1591#endif
1592 DumpRedir(Cmd->Redirections); // FIXME: Can we have leading redirections??
1593 ConOutChar(_T('\n'));
1594
1595 /*DumpCommand*/DUMP(Cmd->Subcommands, SpacePad + 2);
1596 return;
1597 }
1598
1599 case C_BLOCK:
1600 {
1601#ifndef MSCMD_ECHO_COMMAND_COMPAT
1602 ConOutChar(_T('('));
1603#else
1604 ConOutPuts(_T("( "));
1605#endif
1606 DumpRedir(Cmd->Redirections);
1607 ConOutChar(_T('\n'));
1608
1609 SpacePad += 2;
1610
1611 for (Sub = Cmd->Subcommands; Sub; Sub = Sub->Next)
1612 {
1613#if defined(MSCMD_ECHO_COMMAND_COMPAT) && defined(MSCMD_PARSER_BUGS)
1614 /*
1615 * We will emulate Windows' CMD handling of "CRLF" and "&" multi-command
1616 * enumeration within parenthesized command blocks.
1617 */
1618
1619 if (!Sub->Next)
1620 {
1621 DumpCommand(Sub, SpacePad);
1622 continue;
1623 }
1624
1625 if (Sub->Type != C_MULTI)
1626 {
1627 ConOutPrintf(_T("%*s"), SpacePad, _T(""));
1628 ConOutPuts(_T("CRLF \n"));
1629 DumpCommand(Sub, SpacePad);
1630 continue;
1631 }
1632
1633 /* Now, Sub->Type == C_MULTI */
1634
1635 Cmd = Sub;
1636
1637 ConOutPrintf(_T("%*s"), SpacePad, _T(""));
1638 ConOutPrintf(_T("%s \n"), OpString[Cmd->Type - C_OP_LOWEST]);
1639 // FIXME: Can we have redirections on these operator-type commands?
1640
1641 SpacePad += 2;
1642
1643 Cmd = Cmd->Subcommands;
1644 DumpCommand(Cmd, SpacePad);
1645 ConOutPrintf(_T("%*s"), SpacePad, _T(""));
1646 ConOutPuts(_T("CRLF \n"));
1647 DumpCommand(Cmd->Next, SpacePad);
1648
1649 // NOTE: Next commands will remain indented.
1650
1651#else
1652
1653 /*
1654 * If this command is followed by another one, first display "CRLF".
1655 * This also emulates the CRLF placement "bug" of Windows' CMD
1656 * for the last two commands.
1657 */
1658 if (Sub->Next)
1659 {
1660 ConOutPrintf(_T("%*s"), SpacePad, _T(""));
1661#ifndef MSCMD_ECHO_COMMAND_COMPAT
1662 ConOutPuts(_T("CRLF\n"));
1663#else
1664 ConOutPuts(_T("CRLF \n"));
1665#endif
1666 }
1667 DumpCommand(Sub, SpacePad);
1668
1669#endif // defined(MSCMD_ECHO_COMMAND_COMPAT) && defined(MSCMD_PARSER_BUGS)
1670 }
1671
1672 return;
1673 }
1674
1675 case C_MULTI:
1676 case C_OR:
1677 case C_AND:
1678 case C_PIPE:
1679 {
1680#ifndef MSCMD_ECHO_COMMAND_COMPAT
1681 ConOutPrintf(_T("%s\n"), OpString[Cmd->Type - C_OP_LOWEST]);
1682#else
1683 ConOutPrintf(_T("%s \n"), OpString[Cmd->Type - C_OP_LOWEST]);
1684#endif
1685 // FIXME: Can we have redirections on these operator-type commands?
1686
1687 SpacePad += 2;
1688
1689 Sub = Cmd->Subcommands;
1690 DumpCommand(Sub, SpacePad);
1691 /*DumpCommand*/DUMP(Sub->Next, SpacePad);
1692 return;
1693 }
1694
1695 case C_FOR:
1696 {
1697 ConOutPuts(_T("for"));
1698 /* NOTE: FOR cannot have leading redirections */
1699
1700 if (Cmd->For.Switches & FOR_DIRS) ConOutPuts(_T(" /D"));
1701 if (Cmd->For.Switches & FOR_F) ConOutPuts(_T(" /F"));
1702 if (Cmd->For.Switches & FOR_LOOP) ConOutPuts(_T(" /L"));
1703 if (Cmd->For.Switches & FOR_RECURSIVE) ConOutPuts(_T(" /R"));
1704 if (Cmd->For.Params)
1705 ConOutPrintf(_T(" %s"), Cmd->For.Params);
1706 ConOutPrintf(_T(" %%%c in (%s) do\n"), Cmd->For.Variable, Cmd->For.List);
1707 /*DumpCommand*/DUMP(Cmd->Subcommands, SpacePad + 2);
1708 return;
1709 }
1710
1711 case C_IF:
1712 {
1713 ConOutPuts(_T("if"));
1714 /* NOTE: IF cannot have leading redirections */
1715
1716 if (Cmd->If.Flags & IFFLAG_IGNORECASE)
1717 ConOutPuts(_T(" /I"));
1718
1719 ConOutChar(_T('\n'));
1720
1721 SpacePad += 2;
1722
1723 /*
1724 * Show the IF command condition as a command.
1725 * If it is negated, indent the command more.
1726 */
1727 if (Cmd->If.Flags & IFFLAG_NEGATE)
1728 {
1729 ConOutPrintf(_T("%*s"), SpacePad, _T(""));
1730 ConOutPuts(_T("not\n"));
1731 SpacePad += 2;
1732 }
1733
1734 ConOutPrintf(_T("%*s"), SpacePad, _T(""));
1735
1736 /*
1737 * Command name:
1738 * - Unary operator: its name is the command name, and its argument is the command argument.
1739 * - Binary operator: its LHS is the command name, its RHS is the command argument.
1740 *
1741 * Type:
1742 * Windows' CMD (Win2k3 / Win7-10) values are as follows:
1743 * CMDEXTVERSION Type: 0x32 / 0x34
1744 * ERRORLEVEL Type: 0x33 / 0x35
1745 * DEFINED Type: 0x34 / 0x36
1746 * EXIST Type: 0x35 / 0x37
1747 * == Type: 0x37 / 0x39 (String Comparison)
1748 *
1749 * For the following command:
1750 * NOT Type: 0x36 / 0x38
1751 * Windows only prints it without any type / redirection.
1752 *
1753 * For the following command:
1754 * EQU, NEQ, etc. Type: 0x38 / 0x3a (Generic Comparison)
1755 * Windows displays it as command of unknown type.
1756 */
1757#ifndef MSCMD_ECHO_COMMAND_COMPAT
1758 ConOutPrintf(_T("Cmd: %s Type: %x"),
1759 (Cmd->If.Operator <= IF_MAX_UNARY) ?
1760 IfOperatorString[Cmd->If.Operator] :
1761 Cmd->If.LeftArg,
1762 Cmd->If.Operator);
1763#else
1764 ConOutPrintf(_T("Cmd: %s Type: %x "),
1765 (Cmd->If.Operator <= IF_MAX_UNARY) ?
1766 IfOperatorString[Cmd->If.Operator] :
1767 Cmd->If.LeftArg,
1768 Cmd->If.Operator);
1769#endif
1770 /* Arguments */
1771#ifndef MSCMD_ECHO_COMMAND_COMPAT
1772 ConOutPrintf(_T(" Args: `%s'"), Cmd->If.RightArg);
1773#else
1774 ConOutPrintf(_T("Args: `%s' "), Cmd->If.RightArg);
1775#endif
1776
1777 ConOutChar(_T('\n'));
1778
1779 if (Cmd->If.Flags & IFFLAG_NEGATE)
1780 {
1781 SpacePad -= 2;
1782 }
1783
1784 Sub = Cmd->Subcommands;
1785 DumpCommand(Sub, SpacePad);
1786 if (Sub->Next)
1787 {
1788 ConOutPrintf(_T("%*s"), SpacePad - 2, _T(""));
1789 ConOutPuts(_T("else\n"));
1790 DumpCommand(Sub->Next, SpacePad);
1791 }
1792 return;
1793 }
1794
1795 default:
1796 ConOutPrintf(_T("*** Unknown type: %x\n"), Cmd->Type);
1797 break;
1798 }
1799
1800#undef DUMP
1801}
#define IFFLAG_IGNORECASE
Definition: cmd.h:262
#define IFFLAG_NEGATE
Definition: cmd.h:261
#define FOR_F
Definition: cmd.h:236
#define FOR_LOOP
Definition: cmd.h:237
#define FOR_DIRS
Definition: cmd.h:235
#define FOR_RECURSIVE
Definition: cmd.h:238
#define ConOutPrintf(szStr,...)
Definition: console.h:41
#define ConOutPuts(szStr)
Definition: console.h:29
#define DUMP(Command, Pad)
static const TCHAR *const IfOperatorString[]
Definition: parser.c:44
static VOID DumpRedir(IN REDIRECTION *Redirections)
Definition: parser.c:1514
VOID DumpCommand(IN PARSED_COMMAND *Cmd, IN ULONG SpacePad)
Definition: parser.c:1533
#define C_OP_LOWEST
Definition: parser.c:38
static const TCHAR OpString[][3]
Definition: parser.c:40
#define IF_MAX_UNARY
VOID ConOutChar(TCHAR c)
Definition: util.c:234
COMMAND_TYPE Type
Definition: cmd.h:375
struct _PARSED_COMMAND * Next
Definition: cmd.h:373
#define _T(x)
Definition: vfdio.h:22

Referenced by DumpCommand(), and ParseCommand().

◆ DumpRedir()

static VOID DumpRedir ( IN REDIRECTION *  Redirections)
static

Definition at line 1514 of file parser.c.

1516{
1517 REDIRECTION* Redir;
1518
1519 if (Redirections)
1520#ifndef MSCMD_ECHO_COMMAND_COMPAT
1521 ConOutPuts(_T(" Redir: "));
1522#else
1523 ConOutPuts(_T("Redir: "));
1524#endif
1525 for (Redir = Redirections; Redir; Redir = Redir->Next)
1526 {
1527 ConOutPrintf(_T(" %x %s%s"), Redir->Number,
1528 RedirString[Redir->Mode], Redir->Filename);
1529 }
1530}
static const TCHAR RedirString[][3]
Definition: parser.c:42
REDIR_MODE Mode
Definition: cmd.h:455
BYTE Number
Definition: cmd.h:454
TCHAR Filename[]
Definition: cmd.h:456
struct _REDIRECTION * Next
Definition: cmd.h:452

Referenced by DumpCommand().

◆ EchoCommand()

VOID EchoCommand ( IN PARSED_COMMAND *  Cmd)

Definition at line 1808 of file parser.c.

1810{
1811 PARSED_COMMAND* Sub;
1812 REDIRECTION* Redir;
1813
1814 if (!Cmd)
1815 return;
1816
1817 switch (Cmd->Type)
1818 {
1819 case C_COMMAND:
1820 case C_REM:
1821 {
1822 if (SubstituteForVars(Cmd->Command.First, TempBuf))
1823 ConOutPrintf(_T("%s"), TempBuf);
1824 if (SubstituteForVars(Cmd->Command.Rest, TempBuf))
1825 {
1826 ConOutPrintf(_T("%s"), TempBuf);
1827#ifdef MSCMD_ECHO_COMMAND_COMPAT
1828 /* NOTE: For Windows compatibility, add a trailing space after printing the command parameter, if present */
1829 if (*TempBuf) ConOutChar(_T(' '));
1830#endif
1831 }
1832 break;
1833 }
1834
1835 case C_QUIET:
1836 return;
1837
1838 case C_BLOCK:
1839 {
1840 BOOLEAN bIsFirstCmdCRLF;
1841
1842 ConOutChar(_T('('));
1843
1844 Sub = Cmd->Subcommands;
1845
1846 bIsFirstCmdCRLF = (Sub && Sub->Next);
1847
1848#if defined(MSCMD_ECHO_COMMAND_COMPAT) && defined(MSCMD_PARSER_BUGS)
1849 /*
1850 * We will emulate Windows' CMD handling of "CRLF" and "&" multi-command
1851 * enumeration within parenthesized command blocks.
1852 */
1853 bIsFirstCmdCRLF = bIsFirstCmdCRLF && (Sub->Type != C_MULTI);
1854#endif
1855
1856 /*
1857 * Single-command block: display all on one line.
1858 * Multi-command block: display commands on separate lines.
1859 */
1860 if (bIsFirstCmdCRLF)
1861 ConOutChar(_T('\n'));
1862
1863 for (; Sub; Sub = Sub->Next)
1864 {
1865 EchoCommand(Sub);
1866 if (Sub->Next)
1867#ifdef MSCMD_ECHO_COMMAND_COMPAT
1868 ConOutPuts(_T(" \n "));
1869#else
1870 ConOutChar(_T('\n'));
1871#endif
1872 }
1873
1874 if (bIsFirstCmdCRLF)
1875 ConOutChar(_T('\n'));
1876
1877#ifdef MSCMD_ECHO_COMMAND_COMPAT
1878 /* NOTE: For Windows compatibility, add a trailing space after printing the closing parenthesis */
1879 ConOutPuts(_T(") "));
1880#else
1881 ConOutChar(_T(')'));
1882#endif
1883 break;
1884 }
1885
1886 case C_MULTI:
1887 case C_OR:
1888 case C_AND:
1889 case C_PIPE:
1890 {
1891 Sub = Cmd->Subcommands;
1892 EchoCommand(Sub);
1893 ConOutPrintf(_T(" %s "), OpString[Cmd->Type - C_OP_LOWEST]);
1894 EchoCommand(Sub->Next);
1895 break;
1896 }
1897
1898 case C_FOR:
1899 {
1900 ConOutPuts(_T("for"));
1901 if (Cmd->For.Switches & FOR_DIRS) ConOutPuts(_T(" /D"));
1902 if (Cmd->For.Switches & FOR_F) ConOutPuts(_T(" /F"));
1903 if (Cmd->For.Switches & FOR_LOOP) ConOutPuts(_T(" /L"));
1904 if (Cmd->For.Switches & FOR_RECURSIVE) ConOutPuts(_T(" /R"));
1905 if (Cmd->For.Params)
1906 ConOutPrintf(_T(" %s"), Cmd->For.Params);
1907 if (Cmd->For.List && SubstituteForVars(Cmd->For.List, TempBuf))
1908 ConOutPrintf(_T(" %%%c in (%s) do "), Cmd->For.Variable, TempBuf);
1909 else
1910 ConOutPrintf(_T(" %%%c in (%s) do "), Cmd->For.Variable, Cmd->For.List);
1911 EchoCommand(Cmd->Subcommands);
1912 break;
1913 }
1914
1915 case C_IF:
1916 {
1917 ConOutPuts(_T("if"));
1918 if (Cmd->If.Flags & IFFLAG_IGNORECASE)
1919 ConOutPuts(_T(" /I"));
1920 if (Cmd->If.Flags & IFFLAG_NEGATE)
1921 ConOutPuts(_T(" not"));
1922 if (Cmd->If.LeftArg && SubstituteForVars(Cmd->If.LeftArg, TempBuf))
1923 ConOutPrintf(_T(" %s"), TempBuf);
1924 ConOutPrintf(_T(" %s"), IfOperatorString[Cmd->If.Operator]);
1925 if (SubstituteForVars(Cmd->If.RightArg, TempBuf))
1926 ConOutPrintf(_T(" %s "), TempBuf);
1927 Sub = Cmd->Subcommands;
1928 EchoCommand(Sub);
1929 if (Sub->Next)
1930 {
1931 ConOutPuts(_T(" else "));
1932 EchoCommand(Sub->Next);
1933 }
1934 break;
1935 }
1936
1937 default:
1938 ASSERT(FALSE);
1939 break;
1940 }
1941
1942 for (Redir = Cmd->Redirections; Redir; Redir = Redir->Next)
1943 {
1944 if (SubstituteForVars(Redir->Filename, TempBuf))
1945 {
1946#ifdef MSCMD_ECHO_COMMAND_COMPAT
1947 ConOutPrintf(_T("%c%s%s "),
1948 _T('0') + Redir->Number,
1949 RedirString[Redir->Mode], TempBuf);
1950#else
1951 ConOutPrintf(_T(" %c%s%s"),
1952 _T('0') + Redir->Number,
1953 RedirString[Redir->Mode], TempBuf);
1954#endif
1955 }
1956 }
1957}
unsigned char BOOLEAN
Definition: actypes.h:127
BOOL SubstituteForVars(IN PCTSTR Src, OUT PTSTR Dest)
Definition: cmd.c:1596
VOID EchoCommand(IN PARSED_COMMAND *Cmd)
Definition: parser.c:1808
static TCHAR TempBuf[CMDLINE_LENGTH]
Definition: parser.c:88

Referenced by EchoCommand(), and ExecuteCommandWithEcho().

◆ FreeCommand()

VOID FreeCommand ( IN OUT PARSED_COMMAND *  Cmd)

Definition at line 527 of file parser.c.

529{
530 if (Cmd->Subcommands)
531 FreeCommand(Cmd->Subcommands);
532 if (Cmd->Next)
533 FreeCommand(Cmd->Next);
534 FreeRedirection(Cmd->Redirections);
535 if (Cmd->Type == C_FOR)
536 {
537 cmd_free(Cmd->For.Params);
538 cmd_free(Cmd->For.List);
539 }
540 else if (Cmd->Type == C_IF)
541 {
542 cmd_free(Cmd->If.LeftArg);
543 cmd_free(Cmd->If.RightArg);
544 }
545 cmd_free(Cmd);
546}
VOID FreeRedirection(REDIRECTION *)
Definition: redir.c:153
VOID FreeCommand(IN OUT PARSED_COMMAND *Cmd)
Definition: parser.c:527
#define cmd_free(ptr)
Definition: cmddbg.h:31

Referenced by Batch(), cmd_call(), FreeCommand(), ParseBlock(), ParseCommand(), ParseCommandBinaryOp(), ParseCommandLine(), ParseFor(), ParseIf(), and ProcessInput().

◆ InitParser()

static VOID InitParser ( VOID  )
static

Definition at line 173 of file parser.c.

174{
175 *CurrentToken = 0;
177 InsideBlock = 0;
178
179#ifndef MSCMD_PARSER_BUGS
180 bReparseToken = FALSE;
181 LastCurTokPos = NULL;
182#endif
183
185}
static TOK_TYPE CurrentTokenType
Definition: parser.c:100
static TCHAR CurrentToken[CMDLINE_LENGTH]
Definition: parser.c:99
static INT InsideBlock
Definition: parser.c:105
TCHAR ParseLine[CMDLINE_LENGTH]
Definition: parser.c:92
static VOID ResetParser(IN PTCHAR Pos)
Definition: parser.c:107

Referenced by ParseCommand().

◆ IsSeparator()

static __inline BOOL IsSeparator ( TCHAR  Char)
static

Definition at line 72 of file parser.c.

73{
74 return _istspace(Char) || (Char && !!_tcschr(STANDARD_SEPS, Char));
75}
#define STANDARD_SEPS
Definition: cmd.h:349
#define _istspace
Definition: tchar.h:1504
#define _tcschr
Definition: tchar.h:1406

Referenced by ParseTokenEx().

◆ ParseBlock()

static PARSED_COMMAND * ParseBlock ( IN OUT REDIRECTION **  RedirList)
static

Definition at line 649 of file parser.c.

651{
652 PARSED_COMMAND *Cmd, *Sub, **NextPtr;
653
655 if (!Cmd)
656 {
657 WARN("Cannot allocate memory for Cmd!\n");
658 ParseError();
659 return NULL;
660 }
661
662 /* Read the block contents */
663 NextPtr = &Cmd->Subcommands;
664 ++InsideBlock;
665 while (TRUE)
666 {
667 /*
668 * Windows' CMD compatibility: Strip leading newlines in the block.
669 *
670 * Note that this behaviour is buggy, especially when MSCMD_PARSER_BUGS is defined!
671 * For example:
672 * (foo^\n
673 * bar)
674 * would be parsed ultimately as: '(', 'bar', ')' because the "foo^"
675 * part would be discarded due to the UnParseToken() call, since this
676 * function doesn't work across line continuations.
677 */
678 while (ParseToken(0, STANDARD_SEPS) == TOK_END && *CurrentToken == _T('\n'))
679 ;
680 if (*CurrentToken && *CurrentToken != _T('\n'))
681 UnParseToken();
682
683 /* Break early if we have nothing else to read. We will also fail
684 * due to the fact we haven't encountered any closing parenthesis. */
685 if (!*CurrentToken /* || *CurrentToken == _T('\n') */)
686 {
688 break;
689 }
690
691 /*
692 * NOTE: Windows' CMD uses a "CRLF" operator when dealing with
693 * newlines in parenthesized blocks, as an alternative to the
694 * '&' command-separation operator.
695 */
696
698 if (Sub)
699 {
700 *NextPtr = Sub;
701 NextPtr = &Sub->Next;
702 }
703 else if (bParseError)
704 {
705 --InsideBlock;
707 return NULL;
708 }
709
711 break;
712
713 /* Skip past the \n */
714 }
715 --InsideBlock;
716
717 /* Fail if the block was not terminated, or if we have
718 * an empty block, i.e. "( )", considered invalid. */
719 if ((CurrentTokenType != TOK_END_BLOCK) || (Cmd->Subcommands == NULL))
720 {
721 ParseError();
723 return NULL;
724 }
725
726 /* Process any trailing redirections and append them to the list */
727#ifndef MSCMD_REDIR_PARSE_BUGS
729 {
730 if (!ParseRedirection(RedirList))
731 {
733 return NULL;
734 }
735 }
736#else
737 while (ParseToken(0, STANDARD_SEPS) != TOK_END)
738 {
739 if (!ParseRedirection(RedirList))
740 {
741 /* If an actual error happened in ParseRedirection(), bail out */
742 if (bParseError)
743 {
745 return NULL;
746 }
747 /* Otherwise it just returned FALSE because the current token
748 * is not a redirection. Unparse the token and refetch it. */
749 break;
750 }
751 }
752#endif
754 {
755 /*
756 * Windows' CMD compatibility: Unparse the current token.
757 *
758 * Note that this behaviour is buggy, especially when MSCMD_PARSER_BUGS is defined!
759 * For example:
760 * (foo^\n
761 * bar)
762 * would be parsed ultimately as: '(', 'bar', ')' because the "foo^"
763 * part would be discarded due to the UnParseToken() call, since this
764 * function doesn't work across line continuations.
765 */
766 UnParseToken();
767
768 /*
769 * Since it is expected that when ParseBlock() returns, the next
770 * token is already fetched, call ParseToken() again to compensate.
771 */
773 }
774
775 return Cmd;
776}
static __inline PARSED_COMMAND * ParseCommandOp(IN COMMAND_TYPE OpType)
Definition: parser.c:1447
BOOL bParseError
Definition: parser.c:90
static __inline VOID ParseError(VOID)
Definition: parser.c:235
static __inline INT ParseToken(IN TCHAR ExtraEnd OPTIONAL, IN PCTSTR Separators OPTIONAL)
Definition: parser.c:450
static BOOL ParseRedirection(IN OUT REDIRECTION **List)
Definition: parser.c:551
static VOID UnParseToken(VOID)
Definition: parser.c:160
static PARSED_COMMAND * AllocCommand(IN COMMAND_TYPE Type, IN PCTSTR CmdHead OPTIONAL, IN PCTSTR CmdTail OPTIONAL)
Definition: parser.c:459
#define WARN(fmt,...)
Definition: precomp.h:61
#define TRUE
Definition: types.h:120

Referenced by ParsePrimary().

◆ ParseChar()

static TCHAR ParseChar ( VOID  )
static

Definition at line 187 of file parser.c.

188{
189 TCHAR Char;
190
191 if (bParseError)
192 return 0;
193
194restart:
195 /*
196 * Although CRs can be injected into a line via an environment
197 * variable substitution, the parser ignores them - they won't
198 * even separate tokens.
199 */
200 do
201 {
202 Char = *ParsePos++;
203 }
204 while (Char == _T('\r'));
205
206 if (!Char) --ParsePos;
207 if (!Char && bLineContinuations)
208 {
209 if (!ReadLine(ParseLine, TRUE))
210 {
211 /* ^C pressed, or line was too long */
212 //
213 // FIXME: Distinguish with respect to BATCH end of file !!
214 //
216 }
217 else
218 {
220 if (*ParsePos)
221 goto restart;
222 }
223 }
224 return Char;
225}
void restart(int argc, const char *argv[])
Definition: cmds.c:2115
BOOL ReadLine(TCHAR *commandline, BOOL bMore)
Definition: cmd.c:1721
static PTCHAR ParsePos
Definition: parser.c:93
static BOOL bLineContinuations
Definition: parser.c:91

Referenced by ParseTokenEx().

◆ ParseCommand()

PARSED_COMMAND * ParseCommand ( IN PCTSTR  Line)

Definition at line 1461 of file parser.c.

1463{
1465
1466 if (Line)
1467 {
1468 if (!SubstituteVars(Line, ParseLine, _T('%')))
1469 return NULL;
1471 }
1472 else
1473 {
1474 if (!ReadLine(ParseLine, FALSE))
1475 return NULL;
1477 }
1478
1479 InitParser();
1480
1482 if (Cmd)
1483 {
1485
1486 if ((CurrentTokenType != TOK_END) &&
1487 (_tcscmp(CurrentToken, _T("\n")) != 0))
1488 {
1489 ParseError();
1490 }
1491 if (bParseError)
1492 {
1494 return NULL;
1495 }
1496
1497 /* Debugging support */
1498 if (fDumpParse)
1499 DumpCommand(Cmd, 0);
1500 }
1501 else
1502 {
1503 bIgnoreEcho = TRUE;
1504 }
1505 return Cmd;
1506}
BOOL SubstituteVars(IN PCTSTR Src, OUT PTSTR Dest, IN TCHAR Delim)
Definition: cmd.c:1514
BOOL bIgnoreEcho
Definition: cmd.c:155
static VOID InitParser(VOID)
Definition: parser.c:173
BOOLEAN fDumpParse
Definition: parser.c:36
#define _tcscmp
Definition: tchar.h:1424
Definition: ncftp.h:79

Referenced by Batch(), cmd_call(), ParseCommandLine(), and ProcessInput().

◆ ParseCommandBinaryOp()

static PARSED_COMMAND * ParseCommandBinaryOp ( IN COMMAND_TYPE  OpType)
static

bc &&

Definition at line 1295 of file parser.c.

1297{
1299
1300 if (OpType == C_OP_LOWEST) // i.e. CP_MULTI
1301 {
1302 /* Ignore any parser-level comments */
1303 if (bIgnoreParserComments && (*CurrentToken == _T(':')))
1304 {
1305 /* Ignore the rest of the line, including line continuations */
1306 while (ParseToken(0, NULL) != TOK_END)
1307 ;
1308#ifdef MSCMD_PARENS_PARSE_BUGS
1309 /*
1310 * Return NULL in case we are NOT inside a parenthesized block,
1311 * otherwise continue. The effects can be observed as follows:
1312 * within a parenthesized block, every second ':'-prefixed command
1313 * is not ignored, while the first of each "pair" is ignored.
1314 * This first command **MUST NOT** be followed by an empty line,
1315 * otherwise a syntax error is raised.
1316 */
1317 if (InsideBlock == 0)
1318 {
1319#endif
1320 return NULL;
1321#ifdef MSCMD_PARENS_PARSE_BUGS
1322 }
1323 /* Get the next token */
1324 ParseToken(0, NULL);
1325#endif
1326 }
1327
1328 /*
1329 * Ignore single closing parenthesis outside of command blocks,
1330 * thus interpreted as a command. This very specific situation
1331 * can happen e.g. while running in batch mode, when jumping to
1332 * a label present inside a command block.
1333 *
1334 * NOTE: If necessary, this condition can be restricted to only
1335 * when a batch context 'bc' is active.
1336 *
1337 * NOTE 2: For further security, Windows checks that we are NOT
1338 * currently inside a parenthesized block, and also, ignores
1339 * explicitly everything (ParseToken() loop) on the same line
1340 * (including line continuations) after this closing parenthesis.
1341 *
1342 * Why doing so? Consider the following batch:
1343 *
1344 * IF 1==1 (
1345 * :label
1346 * echo A
1347 * ) ^
1348 * ELSE (
1349 * echo B
1350 * exit /b
1351 * )
1352 * GOTO :label
1353 *
1354 * First the IF block is executed. Since the condition is trivially
1355 * true, only the first block "echo A" is executed, then execution
1356 * goes after the IF block, that is, at the GOTO. Here, the GOTO
1357 * jumps within the first IF-block, however, the running context now
1358 * is NOT an IF. So parsing and execution will go through each command,
1359 * starting with 'echo A'. But then one gets the ') ^\n ELSE (' part !!
1360 * If we want to make sense of this without bailing out due to
1361 * parsing error, we should ignore this line, **including** the line
1362 * continuation. Hence we need to loop over all the tokens following
1363 * the closing parenthesis, instead of just returning NULL straight ahead.
1364 * Then execution continues with the other commands, 'echo B' and
1365 * 'exit /b' (here to stop the code loop). Execution would also
1366 * continue (if 'exit' was replaced by something else) and encounter
1367 * the lone closing parenthesis ')', that should again be ignored.
1368 *
1369 * Note that this feature has been introduced in Win2k+.
1370 */
1371 if ( (_tcscmp(CurrentToken, _T(")")) == 0) &&
1373 {
1374 ASSERT(InsideBlock == 0);
1375
1376 /* Ignore the rest of the line, including line continuations */
1377 while (ParseToken(0, NULL) != TOK_END)
1378 ;
1379 return NULL;
1380 }
1381
1382#ifdef MSCMD_PARENS_PARSE_BUGS
1383 /* Check whether we have an empty line only if we are not inside
1384 * a parenthesized block, and return NULL if so, otherwise do not
1385 * do anything; a syntax error will be raised later. */
1386 if (InsideBlock == 0)
1387#endif
1388 if (!*CurrentToken || *CurrentToken == _T('\n'))
1389 {
1391 return NULL;
1392 }
1393 }
1394
1395 if (OpType == C_OP_HIGHEST)
1396 Cmd = ParsePrimary();
1397 else
1399
1401 {
1402 PARSED_COMMAND* Left = Cmd;
1403 PARSED_COMMAND* Right;
1404
1405 Right = ParseCommandOp(OpType);
1406 if (!Right)
1407 {
1408 /*
1409 * The '&' operator is allowed to have an empty RHS.
1410 * In this case, we directly return the LHS only.
1411 * Note that Windows' CMD prefers building a '&'
1412 * command with an empty RHS.
1413 */
1414 if (!bParseError && (OpType != C_MULTI))
1415 ParseError();
1416 if (bParseError)
1417 {
1418 FreeCommand(Left);
1419 return NULL;
1420 }
1421
1422#ifndef MSCMD_MULTI_EMPTY_RHS
1423 return Left;
1424#endif
1425 }
1426
1428 if (!Cmd)
1429 {
1430 WARN("Cannot allocate memory for Cmd!\n");
1431 ParseError();
1432 FreeCommand(Left);
1433 FreeCommand(Right);
1434 return NULL;
1435 }
1436 Cmd->Subcommands = Left;
1437 Left->Next = Right;
1438#ifdef MSCMD_MULTI_EMPTY_RHS
1439 if (Right)
1440#endif
1441 Right->Next = NULL;
1442 }
1443
1444 return Cmd;
1445}
#define C_OP_HIGHEST
Definition: parser.c:39
static PARSED_COMMAND * ParsePrimary(VOID)
Definition: parser.c:1209
BOOL bIgnoreParserComments
Definition: parser.c:96
static PARSED_COMMAND * ParseCommandBinaryOp(IN COMMAND_TYPE OpType)
Definition: parser.c:1295
OpType
Definition: simd.h:223

Referenced by ParseCommandBinaryOp(), and ParseCommandOp().

◆ ParseCommandOp()

static __inline PARSED_COMMAND * ParseCommandOp ( IN COMMAND_TYPE  OpType)
static

Definition at line 1447 of file parser.c.

1449{
1450 /* Start parsing: initialize the first token */
1451
1452 /* Parse the prefix "quiet" operator '@' as a separate command.
1453 * Thus, @@foo@bar is parsed as: '@', '@', 'foo@bar'. */
1455
1457}
BOOL bHandleContinuations
Definition: parser.c:97
static TOK_TYPE ParseTokenEx(IN TCHAR PrefixOperator OPTIONAL, IN TCHAR ExtraEnd OPTIONAL, IN PCTSTR Separators OPTIONAL, IN BOOL bHandleContinuations)
Definition: parser.c:241

Referenced by ParseBlock(), ParseCommand(), ParseCommandBinaryOp(), ParseFor(), ParseIf(), and ParsePrimary().

◆ ParseCommandPart()

static PARSED_COMMAND * ParseCommandPart ( IN OUT REDIRECTION **  RedirList)
static

Definition at line 1051 of file parser.c.

1053{
1055 PARSED_COMMAND* (*Func)(VOID);
1056
1057 /* Use the scratch buffer */
1058 PTSTR ParsedLine = TempBuf;
1059
1060 /* We need to copy the current token because it's going to be changed below by the ParseToken() calls */
1061 PTCHAR Pos = _stpcpy(ParsedLine, CurrentToken) + 1;
1062 SIZE_T TailOffset = Pos - ParsedLine;
1063
1064 /* Check for special forms */
1065 if ((Func = ParseFor, _tcsicmp(ParsedLine, _T("FOR")) == 0) ||
1066 (Func = ParseIf, _tcsicmp(ParsedLine, _T("IF")) == 0) ||
1067 (Func = ParseRem, _tcsicmp(ParsedLine, _T("REM")) == 0))
1068 {
1069 PTCHAR pHelp;
1070
1072
1073 if ((pHelp = _tcsstr(CurrentToken, _T("/?"))) &&
1074 (Func == ParseIf ? (pHelp[2] == _T('/') || pHelp[2] == 0) : TRUE))
1075 {
1076 /* /? was found within the first token */
1078 }
1079 else
1080 {
1081 pHelp = NULL;
1082 }
1083 if (pHelp && (CurrentTokenType == TOK_NORMAL))
1084 {
1085 /* We encountered /? first, but is followed
1086 * by another token: that's an error. */
1087 ParseError();
1088 return NULL;
1089 }
1090
1091 /* Do actual parsing only if no help is present */
1092 if (!pHelp)
1093 {
1094 /* FOR and IF commands cannot have leading redirection, but REM can */
1095 if (*RedirList && ((Func == ParseFor) || (Func == ParseIf)))
1096 {
1097 /* Display the culprit command and fail */
1098 ParseErrorEx(ParsedLine);
1099 return NULL;
1100 }
1101
1102 return Func();
1103 }
1104
1105 /* Otherwise, run FOR,IF,REM as regular commands only for help support */
1106 if (Pos + _tcslen(_T("/?")) >= &ParsedLine[CMDLINE_LENGTH])
1107 {
1108 ParseError();
1109 return NULL;
1110 }
1111 Pos = _stpcpy(Pos, _T("/?"));
1112 }
1113 else
1114 {
1115 ParseToken(0, NULL);
1116 }
1117
1118 /* Now get the tail */
1119 while (CurrentTokenType != TOK_END)
1120 {
1122 {
1123 if (Pos + _tcslen(CurrentToken) >= &ParsedLine[CMDLINE_LENGTH])
1124 {
1125 ParseError();
1126 return NULL;
1127 }
1129 }
1130#ifndef MSCMD_REDIR_PARSE_BUGS
1132 {
1133 /* Process any trailing redirections and append them to the list */
1135 {
1136 if (!ParseRedirection(RedirList))
1137 return NULL;
1138
1140 }
1142 break;
1143
1144 /* Unparse the current token, and reparse it below with no separators */
1145 UnParseToken();
1146 }
1147 else
1148 {
1149 /* There is no need to do a UnParseToken() / ParseToken() cycle */
1150 break;
1151 }
1152#else
1153 else
1154 {
1155 /* Process any trailing redirections and append them to the list */
1156 BOOL bSuccess = FALSE;
1157
1159
1160 while (CurrentTokenType != TOK_END)
1161 {
1162 if (!ParseRedirection(RedirList))
1163 {
1164 /* If an actual error happened in ParseRedirection(), bail out */
1165 if (bParseError)
1166 return NULL;
1167
1168 /* Otherwise it just returned FALSE because the current token
1169 * is not a redirection. Unparse the token and refetch it. */
1170 break;
1171 }
1172 bSuccess = TRUE;
1173
1175 }
1177 break;
1178
1179 /* Unparse the current token, and reparse it below with no separators */
1180 UnParseToken();
1181
1182 /* If bSuccess == FALSE, we know that it's still the old fetched token, but
1183 * it has been unparsed, so we need to refetch it before quitting the loop. */
1184 if (!bSuccess)
1185 {
1186 ParseToken(0, NULL);
1187 break;
1188 }
1189 }
1190#endif
1191
1192 ParseToken(0, NULL);
1193 }
1194 *Pos = _T('\0');
1195
1197 ParsedLine,
1198 ParsedLine + TailOffset);
1199 if (!Cmd)
1200 {
1201 WARN("Cannot allocate memory for Cmd!\n");
1202 ParseError();
1203 return NULL;
1204 }
1205 return Cmd;
1206}
#define VOID
Definition: acefi.h:82
#define CMDLINE_LENGTH
Definition: help.h:12
static PARSED_COMMAND * ParseIf(VOID)
Definition: parser.c:780
static PARSED_COMMAND * ParseRem(VOID)
Definition: parser.c:1003
VOID ParseErrorEx(IN PCTSTR s)
Definition: parser.c:227
static PARSED_COMMAND * ParseFor(VOID)
Definition: parser.c:875
ush Pos
Definition: deflate.h:92
unsigned int BOOL
Definition: ntddk_ex.h:94
#define _tcsstr
Definition: tchar.h:1415
#define _tcsicmp
Definition: tchar.h:1425
LPSTR PTSTR
Definition: ntbasedef.h:499
char * PTCHAR
Definition: ntbasedef.h:488
void(* Func)(int)

Referenced by ParsePrimary().

◆ ParseError()

◆ ParseErrorEx()

VOID ParseErrorEx ( IN PCTSTR  s)

Definition at line 227 of file parser.c.

228{
229 /* Only display the first error we encounter */
230 if (!bParseError)
233}
VOID error_syntax(PCTSTR s)
GLdouble s
Definition: gl.h:2039

Referenced by cmd_for(), cmd_if(), GetBatchVar(), ParseCommandPart(), and ParseError().

◆ ParseFor()

static PARSED_COMMAND * ParseFor ( VOID  )
static

Definition at line 875 of file parser.c.

876{
878
879 /* Use the scratch buffer */
881 PTCHAR Pos = List;
882
884 if (!Cmd)
885 {
886 WARN("Cannot allocate memory for Cmd!\n");
887 ParseError();
888 return NULL;
889 }
890
891 /* Skip the extended FOR syntax if extensions are disabled */
893 goto parseForBody;
894
895 while (TRUE)
896 {
897 if (_tcsicmp(CurrentToken, _T("/D")) == 0)
898 {
899 Cmd->For.Switches |= FOR_DIRS;
900 }
901 else if (_tcsicmp(CurrentToken, _T("/F")) == 0)
902 {
903 Cmd->For.Switches |= FOR_F;
904 if (!Cmd->For.Params)
905 {
907 if (CurrentToken[0] == _T('/') || CurrentToken[0] == _T('%'))
908 break;
909 Cmd->For.Params = cmd_dup(CurrentToken);
910 }
911 }
912 else if (_tcsicmp(CurrentToken, _T("/L")) == 0)
913 {
914 Cmd->For.Switches |= FOR_LOOP;
915 }
916 else if (_tcsicmp(CurrentToken, _T("/R")) == 0)
917 {
918 Cmd->For.Switches |= FOR_RECURSIVE;
919 if (!Cmd->For.Params)
920 {
922 if (CurrentToken[0] == _T('/') || CurrentToken[0] == _T('%'))
923 break;
925 Cmd->For.Params = cmd_dup(CurrentToken);
926 }
927 }
928 else
929 {
930 break;
931 }
932
934 }
935
936 /* Make sure there aren't two different switches specified
937 * at the same time, unless they're /D and /R */
938 if ((Cmd->For.Switches & (Cmd->For.Switches - 1)) != 0
939 && Cmd->For.Switches != (FOR_DIRS | FOR_RECURSIVE))
940 {
941 goto error;
942 }
943
944parseForBody:
945
946 /* Variable name should be % and just one other character */
947 if (CurrentToken[0] != _T('%') || _tcslen(CurrentToken) != 2)
948 goto error;
949 Cmd->For.Variable = CurrentToken[1];
950
952 if (_tcsicmp(CurrentToken, _T("in")) != 0)
953 goto error;
954
956 goto error;
957
958 while (TRUE)
959 {
960 /* Pretend we're inside a block so the tokenizer will stop on ')' */
961 ++InsideBlock;
963 --InsideBlock;
964
966 break;
967
968 /* Skip past the \n */
969 if ((CurrentTokenType == TOK_END) && *CurrentToken == _T('\n'))
970 continue;
971
973 goto error;
974
975 if (Pos != List)
976 *Pos++ = _T(' ');
977
979 goto error;
981 }
982 *Pos = _T('\0');
983 Cmd->For.List = cmd_dup(List);
984
986 if (_tcsicmp(CurrentToken, _T("do")) != 0)
987 goto error;
988
989 Cmd->Subcommands = ParseCommandOp(C_OP_LOWEST);
990 if (Cmd->Subcommands == NULL)
991 goto error;
992
993 return Cmd;
994
995error:
997 ParseError();
998 return NULL;
999}
static VOID StripQuotes(LPSTR in)
Definition: cmdcons.c:116
#define cmd_dup(str)
Definition: cmddbg.h:32
#define error(str)
Definition: mkdosfs.c:1605
BOOL bEnableExtensions
Definition: more.c:52
_Must_inspect_result_ _In_ WDFCMRESLIST List
Definition: wdfresource.h:550

Referenced by ParseCommandPart().

◆ ParseIf()

static PARSED_COMMAND * ParseIf ( VOID  )
static

Definition at line 780 of file parser.c.

781{
783
785 if (!Cmd)
786 {
787 WARN("Cannot allocate memory for Cmd!\n");
788 ParseError();
789 return NULL;
790 }
791
792 if (bEnableExtensions && (_tcsicmp(CurrentToken, _T("/I")) == 0))
793 {
794 Cmd->If.Flags |= IFFLAG_IGNORECASE;
796 }
797 if (_tcsicmp(CurrentToken, _T("not")) == 0)
798 {
799 Cmd->If.Flags |= IFFLAG_NEGATE;
801 }
802
804 goto error;
805
806 /* Check for unary operators */
807 for (; Cmd->If.Operator <= IF_MAX_UNARY; Cmd->If.Operator++)
808 {
809 /* Skip the extended operators if the extensions are disabled */
810 if (!bEnableExtensions && (Cmd->If.Operator >= IF_CMDEXTVERSION))
811 continue;
812
813 if (_tcsicmp(CurrentToken, IfOperatorString[Cmd->If.Operator]) == 0)
814 {
816 goto error;
817 Cmd->If.RightArg = cmd_dup(CurrentToken);
818 goto condition_done;
819 }
820 }
821
822 /* It must be a two-argument (comparison) operator. It could be ==, so
823 * the equals sign can't be treated as whitespace here. */
824 Cmd->If.LeftArg = cmd_dup(CurrentToken);
825 ParseToken(0, _T(",;"));
826
827 /* The right argument can come immediately after == */
828 if (_tcsnicmp(CurrentToken, _T("=="), 2) == 0 && CurrentToken[2])
829 {
830 Cmd->If.RightArg = cmd_dup(&CurrentToken[2]);
831 goto condition_done;
832 }
833
834 // Cmd->If.Operator == IF_MAX_UNARY + 1;
835 for (; Cmd->If.Operator <= IF_MAX_COMPARISON; Cmd->If.Operator++)
836 {
837 /* Skip the extended operators if the extensions are disabled */
838 if (!bEnableExtensions && (Cmd->If.Operator >= IF_EQU)) // (Cmd->If.Operator > IF_STRINGEQ)
839 continue;
840
841 if (_tcsicmp(CurrentToken, IfOperatorString[Cmd->If.Operator]) == 0)
842 {
844 goto error;
845 Cmd->If.RightArg = cmd_dup(CurrentToken);
846 goto condition_done;
847 }
848 }
849 goto error;
850
851condition_done:
852 Cmd->Subcommands = ParseCommandOp(C_OP_LOWEST);
853 if (Cmd->Subcommands == NULL)
854 goto error;
855 if (_tcsicmp(CurrentToken, _T("else")) == 0)
856 {
857 Cmd->Subcommands->Next = ParseCommandOp(C_OP_LOWEST);
858 if (Cmd->Subcommands->Next == NULL)
859 goto error;
860 }
861
862 return Cmd;
863
864error:
866 ParseError();
867 return NULL;
868}
@ IF_CMDEXTVERSION
Definition: cmd.h:269
@ IF_EQU
Definition: cmd.h:275
#define IF_MAX_COMPARISON
#define _tcsnicmp
Definition: tchar.h:1431

Referenced by ParseCommandPart().

◆ ParsePrimary()

static PARSED_COMMAND * ParsePrimary ( VOID  )
static

Definition at line 1209 of file parser.c.

1210{
1212 REDIRECTION* RedirList = NULL;
1213
1214 /* In this context, '@' is considered as a separate token */
1215 if ((*CurrentToken == _T('@')) && (CurrentTokenType == TOK_OPERATOR))
1216 {
1218 if (!Cmd)
1219 {
1220 WARN("Cannot allocate memory for Cmd!\n");
1221 ParseError();
1222 return NULL;
1223 }
1224 /* @ acts like a unary operator with low precedence,
1225 * so call the top-level parser */
1226 Cmd->Subcommands = ParseCommandOp(C_OP_LOWEST);
1227 return Cmd;
1228 }
1229
1230 /* Process leading redirections and get the head of the command */
1231#ifndef MSCMD_REDIR_PARSE_BUGS
1233 {
1234 if (!ParseRedirection(&RedirList))
1235 return NULL;
1236
1238 }
1239#else
1240 {
1241 BOOL bSuccess = FALSE;
1242 while (CurrentTokenType != TOK_END)
1243 {
1244 if (!ParseRedirection(&RedirList))
1245 {
1246 /* If an actual error happened in ParseRedirection(), bail out */
1247 if (bParseError)
1248 return NULL;
1249
1250 /* Otherwise it just returned FALSE because
1251 * the current token is not a redirection. */
1252 break;
1253 }
1254 bSuccess = TRUE;
1255
1257 }
1258 if (bSuccess)
1259 {
1260 /* Unparse the current token, and reparse it with support for parenthesis */
1262 UnParseToken();
1263
1265 }
1266 }
1267#endif
1268
1270 Cmd = ParseCommandPart(&RedirList);
1272 Cmd = ParseBlock(&RedirList);
1273 else if (CurrentTokenType == TOK_END_BLOCK && !RedirList)
1274 return NULL;
1275
1276 if (Cmd)
1277 {
1278 /* FOR and IF commands cannot have leading redirection
1279 * (checked by ParseCommandPart(), errors out if so). */
1280 ASSERT(!RedirList || (Cmd->Type != C_FOR && Cmd->Type != C_IF));
1281
1282 /* Save the redirection list in the command */
1283 Cmd->Redirections = RedirList;
1284
1285 /* Return the new command */
1286 return Cmd;
1287 }
1288
1289 ParseError();
1290 FreeRedirection(RedirList);
1291 return NULL;
1292}
static PARSED_COMMAND * ParseBlock(IN OUT REDIRECTION **RedirList)
Definition: parser.c:649
static PARSED_COMMAND * ParseCommandPart(IN OUT REDIRECTION **RedirList)
Definition: parser.c:1051

Referenced by ParseCommandBinaryOp().

◆ ParseRedirection()

static BOOL ParseRedirection ( IN OUT REDIRECTION **  List)
static

Definition at line 551 of file parser.c.

553{
554 PTSTR Tok = CurrentToken;
555 REDIRECTION* Redir;
556 REDIR_MODE RedirMode;
557 BYTE Number;
558
559 if ( !(*Tok == _T('<') || *Tok == _T('>')) &&
560 !(_istdigit(*Tok) &&
561 (Tok[1] == _T('<') || Tok[1] == _T('>')) ) )
562 {
564 return FALSE;
565 }
568
569 if (_istdigit(*Tok))
570 Number = *Tok++ - _T('0');
571 else
572 Number = *Tok == _T('<') ? 0 : 1;
573
574 if (*Tok++ == _T('<'))
575 {
576 RedirMode = REDIR_READ;
577 /* Forbid '<<' */
578 if (*Tok == _T('<'))
579 goto fail;
580 }
581 else
582 {
583 RedirMode = REDIR_WRITE;
584 if (*Tok == _T('>'))
585 {
586 RedirMode = REDIR_APPEND;
587 Tok++;
588 }
589 }
590
591 if (*Tok == _T('&'))
592 {
593 /* This is a handle redirection: the next character must be one single digit */
594 if (!(_istdigit(Tok[1]) && !Tok[2]))
595 goto fail;
596 }
597 else
598#ifndef MSCMD_REDIR_PARSE_BUGS
599 if (!*Tok)
600 /* The file name was not part of this token, so it will be the next one */
601#else
602 /* Get rid of what possibly remains in the token, and retrieve the next one */
603#endif
604 {
606 goto fail;
607 Tok = CurrentToken;
608 }
609
610 /* If a redirection for this handle number already exists, delete it */
611 while ((Redir = *List))
612 {
613 if (Redir->Number == Number)
614 {
615 *List = Redir->Next;
616 cmd_free(Redir);
617 continue;
618 }
619 List = &Redir->Next;
620 }
621
623 if (!Redir)
624 {
625 WARN("Cannot allocate memory for Redir!\n");
626 goto fail;
627 }
628 Redir->Next = NULL;
630 Redir->Number = Number;
631 Redir->Mode = RedirMode;
632 _tcscpy(Redir->Filename, Tok);
633 *List = Redir;
634 return TRUE;
635
636fail:
637 ParseError();
639 *List = NULL;
640 return FALSE;
641}
enum _REDIR_MODE REDIR_MODE
@ REDIR_WRITE
Definition: cmd.h:447
@ REDIR_READ
Definition: cmd.h:446
@ REDIR_APPEND
Definition: cmd.h:448
#define INVALID_HANDLE_VALUE
Definition: compat.h:731
IN PVCB IN PBCB OUT PDIRENT IN USHORT IN POEM_STRING Filename
Definition: fatprocs.h:940
#define _istdigit
Definition: tchar.h:1494
#define _tcscpy
Definition: tchar.h:623
_In_ ULONG Number
Definition: haltypes.h:1940
HANDLE OldHandle
Definition: cmd.h:453
unsigned char BYTE
Definition: xxhash.c:193

Referenced by ParseBlock(), ParseCommandPart(), and ParsePrimary().

◆ ParseRem()

static PARSED_COMMAND * ParseRem ( VOID  )
static

Definition at line 1003 of file parser.c.

1004{
1006
1007 /* The common scratch buffer already contains the name of the command */
1008 PTSTR ParsedLine = TempBuf;
1009
1010 PTCHAR Pos = ParsedLine + _tcslen(ParsedLine) + 1;
1011 SIZE_T TailOffset = Pos - ParsedLine;
1012
1013 /* Build a minimal command for REM, so that it can still get through the batch echo unparsing */
1014
1015 /* Unparse the current token, so as to emulate the REM command parsing
1016 * behaviour of Windows' CMD, that discards everything before the last
1017 * line continuation. */
1018 UnParseToken();
1019
1020 /*
1021 * Ignore the rest of the line, without any line continuation (but eat the caret).
1022 * We cannot simply set bLineContinuations to TRUE or FALSE, because we want (only
1023 * for the REM command), even when bLineContinuations == FALSE, to get the caret,
1024 * otherwise it would be ignored.
1025 */
1026 while (ParseTokenEx(0, 0, NULL, FALSE) != TOK_END)
1027 {
1028 if (Pos + _tcslen(CurrentToken) >= &ParsedLine[CMDLINE_LENGTH])
1029 {
1030 ParseError();
1031 return NULL;
1032 }
1034 }
1035 *Pos = _T('\0');
1036
1038 ParsedLine,
1039 ParsedLine + TailOffset);
1040 if (!Cmd)
1041 {
1042 WARN("Cannot allocate memory for Cmd!\n");
1043 ParseError();
1044 return NULL;
1045 }
1046 return Cmd;
1047}

Referenced by ParseCommandPart().

◆ ParseToken()

static __inline INT ParseToken ( IN TCHAR ExtraEnd  OPTIONAL,
IN PCTSTR Separators  OPTIONAL 
)
static

Definition at line 450 of file parser.c.

453{
454 return ParseTokenEx(0, ExtraEnd, Separators, bHandleContinuations);
455}

Referenced by ParseBlock(), ParseCommandBinaryOp(), ParseCommandPart(), ParseFor(), ParseIf(), ParsePrimary(), and ParseRedirection().

◆ ParseTokenEx()

static TOK_TYPE ParseTokenEx ( IN TCHAR PrefixOperator  OPTIONAL,
IN TCHAR ExtraEnd  OPTIONAL,
IN PCTSTR Separators  OPTIONAL,
IN BOOL  bHandleContinuations 
)
static

Definition at line 241 of file parser.c.

246{
248 PTCHAR CurrentTokStart = CurrentToken;
249 PTCHAR Out = CurrentTokStart;
250 TCHAR Char;
251 BOOL bInQuote = FALSE;
252
253#ifndef MSCMD_PARSER_BUGS
254 if (bReparseToken)
255 {
256 bReparseToken = FALSE;
257
258 /*
259 * We will append the part to be reparsed to the old one
260 * (still present in CurrentToken).
261 */
262 CurrentTokStart = LastCurTokPos;
263 Out = CurrentTokStart;
264 }
265 else
266 {
267 LastCurTokPos = CurrentToken;
268 }
269#endif
270
271 /* Start with what we have at current ParsePos */
273
274 for (Char = ParseChar(); Char && Char != _T('\n'); Char = ParseChar())
275 {
276 bInQuote ^= (Char == _T('"'));
277 if (!bInQuote)
278 {
279 if (Separators != NULL)
280 {
281 if (_istspace(Char) || !!_tcschr(Separators, Char))
282 {
283 /* Skip leading separators */
284 if (Out == CurrentTokStart)
285 continue;
286 break;
287 }
288 }
289
290 /* Check for prefix operator */
291 if ((Out == CurrentTokStart) && (Char == PrefixOperator))
292 break;
293
294 /*
295 * Check for numbered redirection.
296 *
297 * For this purpose, we check whether this is a number, that is
298 * in first position in the current parsing buffer (remember that
299 * ParsePos points to the next character) or is preceded by a
300 * whitespace-like separator, including standard command operators
301 * (excepting '@' !) and double-quotes.
302 */
303 if ( _istdigit(Char) &&
304 (ParsePos == &OldParsePos[1] ||
305 IsSeparator(ParsePos[-2]) ||
306 !!_tcschr(_T("()&|\""), ParsePos[-2])) &&
307 (*ParsePos == _T('<') || *ParsePos == _T('>')) )
308 {
309 break;
310 }
311
312 /* Check for other delimiters / operators */
313 if (Char == ExtraEnd)
314 break;
315 if (InsideBlock && Char == _T(')'))
316 break;
317 if (_tcschr(_T("&|<>"), Char))
318 break;
319
320 if (bHandleContinuations && (Char == _T('^')))
321 {
322 Char = ParseChar();
323 /* Eat up a \n, allowing line continuation */
324 if (Char == _T('\n'))
325 {
326#ifndef MSCMD_PARSER_BUGS
327 LastCurTokPos = Out;
328#endif
329 Char = ParseChar();
330 }
331 /* Next character is a forced literal */
332
333 if (Out == CurrentTokStart)
334 {
335 /* Ignore any prefix operator if we don't start a new command block */
337 PrefixOperator = 0;
338 }
339 }
340 }
341 if (Out == &CurrentToken[CMDLINE_LENGTH - 1])
342 break;
343 *Out++ = Char;
344
345 // PrefixOperator = 0;
346 }
347
348 /*
349 * We exited the parsing loop. If the current character is the first one
350 * (Out == CurrentTokStart), interpret it as an operator. Otherwise,
351 * terminate the current token (type TOK_NORMAL) and keep the current
352 * character so that it can be refetched as an operator at the next call.
353 */
354
355 if (Out != CurrentTokStart)
356 {
358 }
359 /*
360 * Else we have an operator.
361 */
362 else if (Char == _T('@'))
363 {
364 Type = TOK_OPERATOR; // TOK_QUIET / TOK_PREFIX_OPERATOR
365 *Out++ = Char;
366 Char = ParseChar();
367 }
368 else if (Char == _T('('))
369 {
371 *Out++ = Char;
372 Char = ParseChar();
373 }
374 else if (Char == _T(')'))
375 {
377 *Out++ = Char;
378 Char = ParseChar();
379 }
380 else if (Char == _T('&') || Char == _T('|'))
381 {
383 *Out++ = Char;
384 Char = ParseChar();
385 /* Check for '&&' or '||' */
386 if (Char == Out[-1])
387 {
388 *Out++ = Char;
389 Char = ParseChar();
390 }
391 }
392 else if ( _istdigit(Char) ||
393 (Char == _T('<') || Char == _T('>')) )
394 {
396 if (_istdigit(Char))
397 {
398 *Out++ = Char;
399 Char = ParseChar();
400 }
401 /* By construction (see the while-loop above),
402 * the next character must be a redirection. */
403 ASSERT(Char == _T('<') || Char == _T('>'));
404 *Out++ = Char;
405 Char = ParseChar();
406 if (Char == Out[-1])
407 {
408 /* Strangely, the tokenizer allows << as well as >>... (it
409 * will cause an error when trying to parse it though) */
410 *Out++ = Char;
411 Char = ParseChar();
412 }
413 if (Char == _T('&'))
414 {
415 *Out++ = Char;
416 while (IsSeparator(Char = ParseChar()))
417 ;
418 if (_istdigit(Char))
419 {
420 *Out++ = Char;
421 Char = ParseChar();
422 }
423 }
424 }
425 else
426 {
427 Type = TOK_END;
428 *Out++ = Char;
429 }
430 *Out = _T('\0');
431
432 /*
433 * Rewind the parsing position, so that the current character can be
434 * refetched later on. However do this only if it is not NULL and if
435 * this is not TOK_END, since we do not want to reparse later the line
436 * termination (we could enter into infinite loops, or, in case of line
437 * continuation, get unwanted "More?" prompts).
438 */
439 if (Char != 0 && Type != TOK_END)
440 --ParsePos;
441
442 /* Debugging support */
443 if (fDumpTokens)
444 ConOutPrintf(_T("ParseToken: (%d) '%s'\n"), Type, CurrentToken);
445
446 return (CurrentTokenType = Type);
447}
static __inline BOOL IsSeparator(TCHAR Char)
Definition: parser.c:72
static TCHAR ParseChar(VOID)
Definition: parser.c:187
static PTCHAR OldParsePos
Definition: parser.c:94
BOOLEAN fDumpTokens
Definition: parser.c:35

Referenced by ParseCommandOp(), ParseRem(), and ParseToken().

◆ ResetParser()

static VOID ResetParser ( IN PTCHAR  Pos)
static

Definition at line 107 of file parser.c.

108{
110 ParsePos = Pos;
112}

Referenced by InitParser(), and ParseChar().

◆ UnparseCommand()

PTCHAR UnparseCommand ( IN PARSED_COMMAND *  Cmd,
OUT PTCHAR  Out,
IN PTCHAR  OutEnd 
)

Definition at line 1965 of file parser.c.

1969{
1970/*
1971 * Since this function has the annoying requirement that it must avoid
1972 * overflowing the supplied buffer, define some helper macros to make
1973 * this less painful.
1974 */
1975#define CHAR(Char) \
1976do { \
1977 if (Out == OutEnd) return NULL; \
1978 *Out++ = Char; \
1979} while (0)
1980#define STRING(String) \
1981do { \
1982 if (Out + _tcslen(String) > OutEnd) return NULL; \
1983 Out = _stpcpy(Out, String); \
1984} while (0)
1985#define PRINTF(Format, ...) \
1986do { \
1987 UINT Len = _sntprintf(Out, OutEnd - Out, Format, __VA_ARGS__); \
1988 if (Len > (UINT)(OutEnd - Out)) return NULL; \
1989 Out += Len; \
1990} while (0)
1991#define RECURSE(Subcommand) \
1992do { \
1993 Out = UnparseCommand(Subcommand, Out, OutEnd); \
1994 if (!Out) return NULL; \
1995} while (0)
1996
1997 PARSED_COMMAND* Sub;
1998 REDIRECTION* Redir;
1999
2000 if (!Cmd)
2001 return Out;
2002
2003 switch (Cmd->Type)
2004 {
2005 case C_COMMAND:
2006 case C_REM:
2007 {
2008 /* This is fragile since there could be special characters, but
2009 * Windows doesn't bother escaping them, so for compatibility
2010 * we probably shouldn't do it either */
2011 if (!SubstituteForVars(Cmd->Command.First, TempBuf)) return NULL;
2012 STRING(TempBuf);
2013 if (!SubstituteForVars(Cmd->Command.Rest, TempBuf)) return NULL;
2014 STRING(TempBuf);
2015 break;
2016 }
2017
2018 case C_QUIET:
2019 {
2020 CHAR(_T('@'));
2021 RECURSE(Cmd->Subcommands);
2022 break;
2023 }
2024
2025 case C_BLOCK:
2026 {
2027 CHAR(_T('('));
2028 for (Sub = Cmd->Subcommands; Sub; Sub = Sub->Next)
2029 {
2030 RECURSE(Sub);
2031 if (Sub->Next)
2032 CHAR(_T('&'));
2033 }
2034 CHAR(_T(')'));
2035 break;
2036 }
2037
2038 case C_MULTI:
2039 case C_OR:
2040 case C_AND:
2041 case C_PIPE:
2042 {
2043 Sub = Cmd->Subcommands;
2044 RECURSE(Sub);
2045 PRINTF(_T(" %s "), OpString[Cmd->Type - C_OP_LOWEST]);
2046 RECURSE(Sub->Next);
2047 break;
2048 }
2049
2050 case C_FOR:
2051 {
2052 STRING(_T("for"));
2053 if (Cmd->For.Switches & FOR_DIRS) STRING(_T(" /D"));
2054 if (Cmd->For.Switches & FOR_F) STRING(_T(" /F"));
2055 if (Cmd->For.Switches & FOR_LOOP) STRING(_T(" /L"));
2056 if (Cmd->For.Switches & FOR_RECURSIVE) STRING(_T(" /R"));
2057 if (Cmd->For.Params)
2058 PRINTF(_T(" %s"), Cmd->For.Params);
2059 if (Cmd->For.List && SubstituteForVars(Cmd->For.List, TempBuf))
2060 PRINTF(_T(" %%%c in (%s) do "), Cmd->For.Variable, TempBuf);
2061 else
2062 PRINTF(_T(" %%%c in (%s) do "), Cmd->For.Variable, Cmd->For.List);
2063 RECURSE(Cmd->Subcommands);
2064 break;
2065 }
2066
2067 case C_IF:
2068 {
2069 STRING(_T("if"));
2070 if (Cmd->If.Flags & IFFLAG_IGNORECASE)
2071 STRING(_T(" /I"));
2072 if (Cmd->If.Flags & IFFLAG_NEGATE)
2073 STRING(_T(" not"));
2074 if (Cmd->If.LeftArg && SubstituteForVars(Cmd->If.LeftArg, TempBuf))
2075 PRINTF(_T(" %s"), TempBuf);
2076 PRINTF(_T(" %s"), IfOperatorString[Cmd->If.Operator]);
2077 if (!SubstituteForVars(Cmd->If.RightArg, TempBuf)) return NULL;
2078 PRINTF(_T(" %s "), TempBuf);
2079 Sub = Cmd->Subcommands;
2080 RECURSE(Sub);
2081 if (Sub->Next)
2082 {
2083 STRING(_T(" else "));
2084 RECURSE(Sub->Next);
2085 }
2086 break;
2087 }
2088
2089 default:
2090 ASSERT(FALSE);
2091 break;
2092 }
2093
2094 for (Redir = Cmd->Redirections; Redir; Redir = Redir->Next)
2095 {
2096 if (!SubstituteForVars(Redir->Filename, TempBuf))
2097 return NULL;
2098 PRINTF(_T(" %c%s%s"), _T('0') + Redir->Number,
2099 RedirString[Redir->Mode], TempBuf);
2100 }
2101 return Out;
2102
2103#undef CHAR
2104#undef STRING
2105#undef PRINTF
2106#undef RECURSE
2107}
#define PRINTF(Format,...)
#define CHAR(Char)
#define RECURSE(Subcommand)
#define STRING(String)

Referenced by ExecuteAsync().

◆ UnParseToken()

static VOID UnParseToken ( VOID  )
static

Definition at line 160 of file parser.c.

161{
163
164 /* Debugging support */
165 if (fDumpTokens)
166 ConOutPrintf(_T("Ungetting: '%s'\n"), ParsePos);
167
168#ifndef MSCMD_PARSER_BUGS
169 bReparseToken = TRUE;
170#endif
171}

Referenced by ParseBlock(), ParseCommandPart(), ParsePrimary(), and ParseRem().

Variable Documentation

◆ bHandleContinuations

BOOL bHandleContinuations = TRUE

Definition at line 97 of file parser.c.

Referenced by cmd_call(), ParseCommandOp(), ParseToken(), and ParseTokenEx().

◆ bIgnoreParserComments

BOOL bIgnoreParserComments = TRUE

Definition at line 96 of file parser.c.

Referenced by cmd_call(), and ParseCommandBinaryOp().

◆ bLineContinuations

BOOL bLineContinuations
static

Definition at line 91 of file parser.c.

Referenced by ParseChar(), and ParseCommand().

◆ bParseError

◆ CurrentToken

◆ CurrentTokenType

◆ fDumpParse

BOOLEAN fDumpParse = FALSE

Definition at line 36 of file parser.c.

Referenced by ParseCommand().

◆ fDumpTokens

BOOLEAN fDumpTokens = FALSE

Definition at line 35 of file parser.c.

Referenced by ParseTokenEx(), and UnParseToken().

◆ IfOperatorString

const TCHAR* const IfOperatorString[]
static
Initial value:
=
{
_T("errorlevel"),
_T("exist"),
_T("cmdextversion"),
_T("defined"),
#define IF_MAX_UNARY
_T("=="),
_T("equ"),
_T("neq"),
_T("lss"),
_T("leq"),
_T("gtr"),
_T("geq"),
#define IF_MAX_COMPARISON
}

Definition at line 44 of file parser.c.

Referenced by DumpCommand(), EchoCommand(), ParseIf(), and UnparseCommand().

◆ InsideBlock

INT InsideBlock = 0
static

Definition at line 105 of file parser.c.

Referenced by InitParser(), ParseBlock(), ParseCommandBinaryOp(), ParseFor(), and ParseTokenEx().

◆ OldParsePos

PTCHAR OldParsePos
static

Definition at line 94 of file parser.c.

Referenced by ParseTokenEx(), ResetParser(), and UnParseToken().

◆ OpString

const TCHAR OpString[][3] = { _T("&"), _T("||"), _T("&&"), _T("|") }
static

Definition at line 40 of file parser.c.

Referenced by DumpCommand(), EchoCommand(), ParseCommandBinaryOp(), and UnparseCommand().

◆ ParseLine

TCHAR ParseLine[CMDLINE_LENGTH]

Definition at line 92 of file parser.c.

Referenced by Batch(), InitParser(), ParseChar(), and ParseCommand().

◆ ParsePos

PTCHAR ParsePos
static

Definition at line 93 of file parser.c.

Referenced by ParseChar(), ParseTokenEx(), ResetParser(), and UnParseToken().

◆ RedirString

const TCHAR RedirString[][3] = { _T("<"), _T(">"), _T(">>") }
static

Definition at line 42 of file parser.c.

Referenced by DumpRedir(), EchoCommand(), and UnparseCommand().

◆ TempBuf

TCHAR TempBuf[CMDLINE_LENGTH]
static

Definition at line 88 of file parser.c.

Referenced by EchoCommand(), ParseCommandPart(), ParseFor(), ParseRem(), and UnparseCommand().