VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/parse/parse-bourne-shell-like.cpp@ 106455

Last change on this file since 106455 was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.6 KB
Line 
1/* $Id: parse-bourne-shell-like.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * IPRT - RTParseShell - Bourne-like Shell Parser.
4 */
5
6/*
7 * Copyright (C) 2022-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include "internal/iprt.h"
42#include <iprt/parseshell.h>
43
44#include <iprt/file.h>
45#include <iprt/mem.h>
46#include <iprt/string.h>
47
48
49/*********************************************************************************************************************************
50* Structures and Typedefs *
51*********************************************************************************************************************************/
52typedef struct RTPARSESRCFILE
53{
54 /** For looking up by the filename string (case sensitive). */
55 RTSTRSPACECORE ByName;
56 /** For looking up by the file ID. */
57 AVLU32NODECORE ById;
58 /** The filename. */
59 char szFilename[RT_FLEXIBLE_ARRAY];
60} RTPARSESRCFILE;
61typedef RTPARSESRCFILE *PRTPARSESRCFILE;
62
63typedef struct RTPARSESRCMGR
64{
65 /** String space managing the filenames. */
66 RTSTRSPACE hStrSpace;
67 /** By ID AVL tree root. */
68 AVLU32TREE IdTreeRoot;
69 /** The next filename ID. */
70 uint32_t idNext;
71} RTPARSESRCMGR;
72typedef RTPARSESRCMGR *PRTPARSESRCMGR;
73
74
75
76/**
77 * Instance data for a bourne-like shell parser.
78 */
79typedef struct RTPARSESHELLINT
80{
81 /** Magic value (RTPARSESHELLINT_MAGIC). */
82 uint32_t uMagic;
83 /** Reference count. */
84 uint32_t volatile cRefs;
85 /** Parser flags, RTPARSESHELL_F_XXX. */
86 uint64_t fFlags;
87 /** Source name manager. */
88 RTPARSESRCMGR SrcMgr;
89 /** Parser instance name (for logging/whatever). */
90 char szName[RT_FLEXIBLE_ARRAY];
91} RTPARSESHELLINT;
92
93/** Magic value for RTPARSESHELLINT. */
94#define RTPARSESHELLINT_MAGIC UINT32_C(0x18937566)
95
96
97typedef enum RTPARSESHELLTOKEN
98{
99 RTPARSESHELLTOKEN_INVALID = 0,
100 RTPARSESHELLTOKEN_EOF,
101 RTPARSESHELLTOKEN_NEWLINE, /**< Newline (\n) */
102 RTPARSESHELLTOKEN_NOT, /**< ! cmd */
103 RTPARSESHELLTOKEN_BACKGROUND, /**< cmd & */
104 RTPARSESHELLTOKEN_AND, /**< cmd1 && cmd2 */
105 RTPARSESHELLTOKEN_PIPE, /**< cmd1 | cmd2 */
106 RTPARSESHELLTOKEN_OR, /**< cmd1 || cmd2 */
107 RTPARSESHELLTOKEN_SEMICOLON, /**< cmd ; */
108 RTPARSESHELLTOKEN_END_CASE /**< pattern) cmd ;; */
109};
110
111/**
112 * The parser state.
113 */
114typedef struct RTPARSESHELLSTATE
115{
116 /** Pointer to the buffer of bytes to parse. */
117 const char *pchSrc;
118 /** Number of characters (bytes) to parse starting with pchSrc. */
119 size_t cchSrc;
120 /** Current source offset. */
121 size_t offSrc;
122 /** Offset relative to pchSrc of the start of the current line. */
123 size_t offSrcLineStart;
124 /** Current line number. */
125 uint32_t iLine;
126 /** The source file ID. */
127 uint32_t idSrcFile;
128 /** The parser instance. */
129 PRTPARSESHELLSTATE pParser;
130} RTPARSESHELLSTATE;
131typedef RTPARSESHELLSTATE *PRTPARSESHELLSTATE;
132
133
134
135static void rtParseSrcMgrInit(PRTPARSESRCMGR pSrcMgr)
136{
137 pSrcMgr->hStrSpace = NULL;
138 pSrcMgr->IdTreeRoot = NULL;
139 pSrcMgr->idNext = 1;
140}
141
142
143/**
144 * @callback_method_impl{AVLU32CALLBACK, Worker for rtParseSrcMgrDelete.}
145 */
146static DECLCALLBACK(int) AVLU32CALLBACK,(PAVLU32NODECORE pNode, void *pvUser)
147{
148 RT_NOREF(pvUser);
149 PRTPARSESRCFILE pSrcFile = RT_FROM_MEMBER(pNode, RTPARSESRCFILE, ById);
150 RTMemFree(pSrcFile);
151}
152
153
154static void rtParseSrcMgrDelete(PRTPARSESRCMGR pSrcMgr)
155{
156 RTAvlU32Destroy(pSrcMgr->IdTreeRoot, rtParseSrcMgrDeleteSrcFile, NULL);
157 pSrcMgr->hStrSpace = NULL;
158 pSrcMgr->IdTreeRoot = NULL;
159 pSrcMgr->idNext = 1;
160}
161
162
163static int32_t rtParseSrcMgrFilenameToIdx(PRTPARSESRCMGR pSrcMgr, const char *pszFilename)
164{
165 /*
166 * Look it up.
167 */
168 RTSTRSPACECORE pStrCore = RTStrSpaceGet(pSrcMgr->hStrSpace, pszFilename);
169 if (pStrCore)
170 {
171 PRTPARSESRCFILE pSrcFile = RT_FROM_MEMBER(pNode, RTPARSESRCFILE, ByName);
172 AssertMsg(pSrcFile->ById.Key > 0 && pSrcFile->ById.Key < UINT16_MAX, ("%#x\n", pSrcFile->ById.Key));
173 return pSrcFile->ById.Key;
174 }
175
176 /*
177 * Add it.
178 */
179 uint32_t const idFile = pSrcMgr->idNext;
180 AssertReturn(idFile < UINT16_MAX, VERR_TOO_MANY_OPEN_FILES);
181
182 size_t const cchFilename = strlen(pszFilename);
183 PRTPARSESRCFILE pSrcFile = (PRTPARSESRCFILE)RTMemAllocZVar(RT_UOFFSETOF_DYN(RTPARSESRCFILE, szFilename[cchFilename + 1]));
184 if (pSrcFile)
185 {
186 memcpy(&pSrcFile->szFilename[0], pszFilename, cchFilename);
187 if (RTStrSpaceInsert(pSrcMgr->hStrSpace, &pSrcFile->ByName))
188 {
189 pSrcFile->ById.Key = idFile;
190 if (RTAvlU32Insert(&pSrcMgr->IdTreeRoot, &pSrcFile->ById))
191 {
192 pSrcMgr->idNext = idFile + 1;
193 return (int32_t)idFile;
194 }
195 AssertFailed();
196 RTStrSpaceRemove(pSrcMgr->hStrSpace, &pSrcFile->szFilename);
197 }
198 else
199 AssertFailed();
200 RTMemFree(pSrcFile);
201 return VERR_INTERNAL_ERROR_2;
202 }
203 return VERR_NO_MEMORY;
204}
205
206
207RTDECL(int) RTParseShellCreate(PRTPARSESHELL phParser, uint64_t fFlags, const char *pszName)
208{
209 /*
210 * Validate input.
211 */
212 AssertPtrReturn(phParser, VERR_INVALID_POINTER);
213 *phParser = NIL_RTPARSESHELL;
214
215 AssertPtrReturn(!(fFlags & ~0), VERR_INVALID_FLAGS);
216
217 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
218 size_t const cchName = strlen(pszName);
219 AssertReturn(cchName > 0 && cchName < 1024, VERR_OUT_OF_RANGE);
220
221 /*
222 * Allocate and initialize the parser instance.
223 */
224 RTPARSESHELLINT *pThis = (RTPARSESHELLINT *)RTMemAllocZVar(RT_UOFFSETOF_DYN(RTPARSESHELLINT, szName[cchName + 1]));
225 AssertReturn(pThis, VERR_NO_MEMORY);
226
227 pThis->uMagic = RTPARSESHELLINT_MAGIC;
228 pThis->cRefs = 1;
229 pThis->fFlags = fFlags;
230 memcpy(pThis->szName, pszName, cchName);
231
232 rtParseSrcMgrInit(&pThis->SrcMgr);
233
234 /*
235 * Return success.
236 */
237 *phParser = pThis;
238 return VINF_SUCCESS;
239}
240
241
242RTDECL(uint32_t) RTParseShellRelease(RTPARSESHELL hParser)
243{
244 if (hParser == NIL_RTPARSESHELL)
245 return 0;
246
247 RTPARSESHELLINT * const pThis = hParser;
248 AssertPtrReturn(pThis, UINT32_MAX);
249 AssertPtrReturn(pThis->uMagic == RTPARSESHELLINT_MAGIC, UINT32_MAX);
250
251 uint32_t const cRefs = ASMAtomicDecU32(&pThis->cRefs);
252 Assert(cRefs < _4K);
253 if (cRefs == 0)
254 {
255 pThis->uMagic = ~RTPARSESHELLINT_MAGIC;
256 pThis->szName[0] = '~';
257 rtParseSrcMgrDelete(&pThis->SrcMgr);
258 RTMemFree(pThis);
259 }
260 return cRefs;
261}
262
263
264RTDECL(uint32_t) RTParseShellRetain(RTPARSESHELL hParser)
265{
266 RTPARSESHELLINT * const pThis = hParser;
267 AssertPtrReturn(pThis, UINT32_MAX);
268 AssertPtrReturn(pThis->uMagic == RTPARSESHELLINT_MAGIC, UINT32_MAX);
269
270 uint32_t const cRefs = ASMAtomicIncU32(&pThis->cRefs);
271 Assert(cRefs > 1);
272 Assert(cRefs < _4K);
273 return cRefs;
274}
275
276
277static rtParseShellGetToken(P RTPARSESHELLSTATE pState)
278{
279
280}
281
282
283static int rtParseShellStringInt(RTPARSESHELLINT *pThis, const char *pchString, size_t cchString,
284 const char *pszFilename, uint32_t iLine, uint32_t fFlags,
285 PRTLISTANCHOR pNodeList, PRTERRINFO pErrInfo)
286{
287 /*
288 * Add the source file to the source manager.
289 */
290 int32_t const idFile = rtParseSrcMgrFilenameToIdx(&pThis->SrcMgr, pszFilename);
291 AssertReturn(idFile > 0, idFile);
292
293 /*
294 *
295 */
296
297
298}
299
300
301static void rtParseTreeDestroyList(PRTLISTANCHOR pNodeList)
302{
303 RT_NOREF(pNodeList);
304}
305
306
307
308RTDECL(int) RTParseShellString(RTPARSESHELL hParser, const char *pchString, size_t cchString,
309 const char *pszFilename, uint32_t iStartLine, uint32_t fFlags,
310 PRTLISTANCHOR pNodeList, PRTERRINFO pErrInfo)
311{
312 /*
313 * Validate input.
314 */
315 AssertPtrReturn(pNodeList, VERR_INVALID_POINTER);
316 RTListInit(pNodeList);
317
318 RTPARSESHELLINT * const pThis = hParser;
319 AssertPtrReturn(pThis, UINT32_MAX);
320 AssertPtrReturn(pThis->uMagic == RTPARSESHELLINT_MAGIC, UINT32_MAX);
321
322 AssertPtrReturn(pchString, VERR_INVALID_POINTER);
323 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
324 AssertPtrReturn(fFlags & ~(UINT32_C(0)), VERR_INVALID_FLAGS);
325
326 /*
327 * Call common function for doing the parsing.
328 */
329 int rc = rtParseShellStringInt(pThis, pchString, cchString, pszFilename, iStartLine, fFlags, pNodeList, pErrInfo);
330 if (RT_FAILURE(rc))
331 rtParseTreeDestroyList(pNodeList);
332 return rc;
333}
334
335
336RTDECL(uint32_t) RTParseShellFile(RTPARSESHELL hParser, const char *pszFilename, uint32_t fFlags,
337 PRTLISTANCHOR pNodeList, PRTERRINFO pErrInfo)
338{
339 /*
340 * Validate input.
341 */
342 AssertPtrReturn(pNodeList, VERR_INVALID_POINTER);
343 RTListInit(pNodeList);
344
345 RTPARSESHELLINT * const pThis = hParser;
346 AssertPtrReturn(pThis, UINT32_MAX);
347 AssertPtrReturn(pThis->uMagic == RTPARSESHELLINT_MAGIC, UINT32_MAX);
348
349 AssertPtrReturn(pchString, VERR_INVALID_POINTER);
350 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
351 AssertPtrReturn(fFlags & ~(UINT32_C(0)), VERR_INVALID_FLAGS);
352
353 /*
354 * Read the file into memory and hand it over to the common parser function.
355 */
356 void *pvFile = NULL;
357 size_t cbFile = 0;
358 int rc = RTFileReadAll(pszFilename, &pvFile, &cbFile)
359 if (RT_SUCCESS(rc))
360 {
361 rc = RTStrValidateEncodingEx((char *)pvFile, cbFile, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
362 if (RT_SUCCESS(rc))
363 {
364 /*
365 * Call common function for doing the parsing.
366 */
367 rc = rtParseShellStringInt(pThis, pchString, cchString, pszFilename, 1 /*iStartLine*/, fFlags, pNodeList, pErrInfo);
368 if (RT_FAILURE(rc))
369 rtParseTreeDestroyList(pNodeList);
370 }
371 else
372 rc = RTErrInfoSetF(pErrInfo, rc, "RTStrValidateEncodingEx failed on %s: %Rrc", pszFilename, rc);
373 RTFileReadAllFree(pvFile, cbFile);
374 }
375 return rc;
376}
377
Note: See TracBrowser for help on using the repository browser.

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