VirtualBox

source: vbox/trunk/src/VBox/Main/src-all/TextScript.cpp

Last change on this file was 98103, checked in by vboxsync, 16 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: 12.9 KB
Line 
1/* $Id: TextScript.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * Classes for reading/parsing/saving text scripts (unattended installation, ++).
4 */
5
6/*
7 * Copyright (C) 2006-2023 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 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_MAIN_UNATTENDED
33#include "LoggingNew.h"
34#include "TextScript.h"
35
36#include <VBox/err.h>
37
38#include <iprt/ctype.h>
39#include <iprt/file.h>
40#include <iprt/vfs.h>
41#include <iprt/path.h>
42
43using namespace std;
44
45
46/*********************************************************************************************************************************
47* BaseTextScript Implementation *
48*********************************************************************************************************************************/
49
50HRESULT BaseTextScript::read(const Utf8Str &rStrFilename)
51{
52 /*
53 * Open the file for reading and figure it's size. Capping the size
54 * at 16MB so we don't exaust the heap on bad input.
55 */
56 HRESULT hrc;
57 RTVFSFILE hVfsFile;
58 int vrc = RTVfsFileOpenNormal(rStrFilename.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE, &hVfsFile);
59 if (RT_SUCCESS(vrc))
60 {
61 hrc = readFromHandle(hVfsFile, rStrFilename.c_str());
62 RTVfsFileRelease(hVfsFile);
63 }
64 else
65 hrc = mpSetError->setErrorVrc(vrc, tr("Failed to open '%s' (%Rrc)"), rStrFilename.c_str(), vrc);
66 return hrc;
67}
68
69HRESULT BaseTextScript::readFromHandle(RTVFSFILE hVfsFile, const char *pszFilename)
70{
71 /*
72 * Open the file for reading and figure it's size. Capping the size
73 * at 16MB so we don't exaust the heap on bad input.
74 */
75 HRESULT hrc;
76 uint64_t cbFile = 0;
77 int vrc = RTVfsFileQuerySize(hVfsFile, &cbFile);
78 if ( RT_SUCCESS(vrc)
79 && cbFile < _16M)
80 {
81 /*
82 * Exploint the jolt() feature of RTCString and read the content directly into
83 * its storage buffer.
84 */
85 vrc = mStrScriptFullContent.reserveNoThrow((size_t)cbFile + 1);
86 if (RT_SUCCESS(vrc))
87 {
88 char *pszDst = mStrScriptFullContent.mutableRaw();
89 vrc = RTVfsFileReadAt(hVfsFile, 0 /*off*/, pszDst, (size_t)cbFile, NULL);
90 pszDst[(size_t)cbFile] = '\0';
91 if (RT_SUCCESS(vrc))
92 {
93 /*
94 * We must validate the encoding or we'll be subject to potential security trouble.
95 * If this turns out to be problematic, we will need to implement codeset
96 * conversion coping mechanisms.
97 */
98 vrc = RTStrValidateEncodingEx(pszDst, (size_t)cbFile + 1,
99 RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED | RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
100 if (RT_SUCCESS(vrc))
101 {
102 mStrScriptFullContent.jolt();
103 return S_OK;
104 }
105
106 hrc = mpSetError->setErrorVrc(vrc, tr("'%s' isn't valid UTF-8: %Rrc"), pszFilename, vrc);
107 }
108 else
109 hrc = mpSetError->setErrorVrc(vrc, tr("Error reading '%s': %Rrc"), pszFilename, vrc);
110 mStrScriptFullContent.setNull();
111 }
112 else
113 hrc = mpSetError->setErrorVrc(vrc, tr("Failed to allocate memory (%'RU64 bytes) for '%s'", "", cbFile),
114 cbFile, pszFilename);
115 }
116 else if (RT_SUCCESS(vrc))
117 hrc = mpSetError->setErrorVrc(VERR_FILE_TOO_BIG, tr("'%s' is too big (max 16MB): %'RU64"), pszFilename, cbFile);
118 else
119 hrc = mpSetError->setErrorVrc(vrc, tr("RTVfsFileQuerySize failed (%Rrc)"), vrc);
120 return hrc;
121}
122
123HRESULT BaseTextScript::save(const Utf8Str &rStrFilename, bool fOverwrite)
124{
125 /*
126 * We may have to append the default filename to the
127 */
128 const char *pszFilename = rStrFilename.c_str();
129 Utf8Str strWithDefaultFilename;
130 if ( getDefaultFilename() != NULL
131 && *getDefaultFilename() != '\0'
132 && RTDirExists(rStrFilename.c_str()) )
133 {
134 try
135 {
136 strWithDefaultFilename = rStrFilename;
137 strWithDefaultFilename.append(RTPATH_SLASH);
138 strWithDefaultFilename.append(getDefaultFilename());
139 }
140 catch (std::bad_alloc &)
141 {
142 return E_OUTOFMEMORY;
143 }
144 pszFilename = strWithDefaultFilename.c_str();
145 }
146
147 /*
148 * Save the filename for later use.
149 */
150 try
151 {
152 mStrSavedPath = pszFilename;
153 }
154 catch (std::bad_alloc &)
155 {
156 return E_OUTOFMEMORY;
157 }
158
159 /*
160 * Use the saveToString method to produce the content.
161 */
162 Utf8Str strDst;
163 HRESULT hrc = saveToString(strDst);
164 if (SUCCEEDED(hrc))
165 {
166 /*
167 * Write the content.
168 */
169 RTFILE hFile;
170 uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_ALL;
171 if (fOverwrite)
172 fOpen |= RTFILE_O_CREATE_REPLACE;
173 else
174 fOpen |= RTFILE_O_CREATE;
175 int vrc = RTFileOpen(&hFile, pszFilename, fOpen);
176 if (RT_SUCCESS(vrc))
177 {
178 vrc = RTFileWrite(hFile, strDst.c_str(), strDst.length(), NULL);
179 if (RT_SUCCESS(vrc))
180 {
181 vrc = RTFileClose(hFile);
182 if (RT_SUCCESS(vrc))
183 {
184 LogRelFlow(("GeneralTextScript::save(): saved %zu bytes to '%s'\n", strDst.length(), pszFilename));
185 return S_OK;
186 }
187 }
188 RTFileClose(hFile);
189 RTFileDelete(pszFilename);
190 hrc = mpSetError->setErrorVrc(vrc, tr("Error writing to '%s' (%Rrc)"), pszFilename, vrc);
191 }
192 else
193 hrc = mpSetError->setErrorVrc(vrc, tr("Error creating/replacing '%s' (%Rrc)"), pszFilename, vrc);
194 }
195 return hrc;
196}
197
198
199
200/*********************************************************************************************************************************
201* GeneralTextScript Implementation *
202*********************************************************************************************************************************/
203
204HRESULT GeneralTextScript::parse()
205{
206 AssertReturn(!mfDataParsed, mpSetError->setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("parse called more than once")));
207
208 /*
209 * Split the raw context into an array of lines.
210 */
211 try
212 {
213 mScriptContentByLines = mStrScriptFullContent.split("\n");
214 }
215 catch (std::bad_alloc &)
216 {
217 mScriptContentByLines.clear();
218 return E_OUTOFMEMORY;
219 }
220
221 mfDataParsed = true;
222 return S_OK;
223}
224
225HRESULT GeneralTextScript::saveToString(Utf8Str &rStrDst)
226{
227 AssertReturn(mfDataParsed, mpSetError->setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("saveToString() called before parse()")));
228
229 /*
230 * Calc the required size first.
231 */
232 size_t const cLines = mScriptContentByLines.size();
233 size_t cbTotal = 1;
234 for (size_t iLine = 0; iLine < cLines; iLine++)
235 cbTotal = mScriptContentByLines[iLine].length() + 1;
236
237 /*
238 * Clear the output and try reserve sufficient space.
239 */
240 rStrDst.setNull();
241
242 int vrc = rStrDst.reserveNoThrow(cbTotal);
243 if (RT_FAILURE(vrc))
244 return E_OUTOFMEMORY;
245
246 /*
247 * Assemble the output.
248 */
249 for (size_t iLine = 0; iLine < cLines; iLine++)
250 {
251 try
252 {
253 rStrDst.append(mScriptContentByLines[iLine]);
254 rStrDst.append('\n');
255 }
256 catch (std::bad_alloc &)
257 {
258 return E_OUTOFMEMORY;
259 }
260 }
261
262 return S_OK;
263}
264
265const RTCString &GeneralTextScript::getContentOfLine(size_t idxLine)
266{
267 if (idxLine < mScriptContentByLines.size())
268 return mScriptContentByLines[idxLine];
269 return Utf8Str::Empty;
270}
271
272
273HRESULT GeneralTextScript::setContentOfLine(size_t idxLine, const Utf8Str &rStrNewLine)
274{
275 AssertReturn(idxLine < mScriptContentByLines.size(),
276 mpSetError->setErrorBoth(E_FAIL, VERR_OUT_OF_RANGE,
277 tr("attempting to set line %zu when there are only %zu lines", "",
278 mScriptContentByLines.size()),
279 idxLine, mScriptContentByLines.size()));
280 try
281 {
282 mScriptContentByLines[idxLine] = rStrNewLine;
283 }
284 catch (std::bad_alloc &)
285 {
286 return E_OUTOFMEMORY;
287 }
288 return S_OK;
289}
290
291vector<size_t> GeneralTextScript::findTemplate(const Utf8Str &rStrNeedle,
292 RTCString::CaseSensitivity enmCase /*= RTCString::CaseSensitive*/)
293{
294 vector<size_t> vecHitLineNumbers;
295 size_t const cLines = mScriptContentByLines.size();
296 for (size_t iLine = 0; iLine < cLines; iLine++)
297 if (mScriptContentByLines[iLine].contains(rStrNeedle, enmCase))
298 vecHitLineNumbers.push_back(iLine);
299
300 return vecHitLineNumbers;
301}
302
303HRESULT GeneralTextScript::findAndReplace(size_t idxLine, const Utf8Str &rStrNeedle, const Utf8Str &rStrReplacement)
304{
305 AssertReturn(idxLine < mScriptContentByLines.size(),
306 mpSetError->setErrorBoth(E_FAIL, VERR_OUT_OF_RANGE,
307 tr("attempting search&replace in line %zu when there are only %zu lines", "",
308 mScriptContentByLines.size()),
309 idxLine, mScriptContentByLines.size()));
310
311 RTCString &rDstString = mScriptContentByLines[idxLine];
312 size_t const offNeedle = rDstString.find(&rStrNeedle);
313 if (offNeedle != RTCString::npos)
314 {
315 try
316 {
317 RTCString strBefore(rDstString, 0, offNeedle);
318 RTCString strAfter(rDstString, offNeedle + rStrNeedle.length());
319 rDstString = strBefore;
320 strBefore.setNull();
321 rDstString.append(rStrReplacement);
322 rDstString.append(strAfter);
323 }
324 catch (std::bad_alloc &)
325 {
326 return E_OUTOFMEMORY;
327 }
328 }
329 return S_OK;
330}
331
332HRESULT GeneralTextScript::appendToLine(size_t idxLine, const Utf8Str &rStrToAppend)
333{
334 AssertReturn(idxLine < mScriptContentByLines.size(),
335 mpSetError->setErrorBoth(E_FAIL, VERR_OUT_OF_RANGE,
336 tr("appending to line %zu when there are only %zu lines", "",
337 mScriptContentByLines.size()),
338 idxLine, mScriptContentByLines.size()));
339
340 try
341 {
342 mScriptContentByLines[idxLine].append(rStrToAppend);
343 }
344 catch (std::bad_alloc &)
345 {
346 return E_OUTOFMEMORY;
347 }
348 return S_OK;
349}
350
351HRESULT GeneralTextScript::prependToLine(size_t idxLine, const Utf8Str &rStrToPrepend)
352{
353 AssertReturn(idxLine < mScriptContentByLines.size(),
354 mpSetError->setErrorBoth(E_FAIL, VERR_OUT_OF_RANGE,
355 tr("prepending to line %zu when there are only %zu lines", "",
356 mScriptContentByLines.size()),
357 idxLine, mScriptContentByLines.size()));
358
359 RTCString &rDstString = mScriptContentByLines[idxLine];
360 try
361 {
362 RTCString strCopy;
363 rDstString.swap(strCopy);
364 rDstString.reserve(strCopy.length() + rStrToPrepend.length() + 1);
365 rDstString = rStrToPrepend;
366 rDstString.append(strCopy);
367 }
368 catch (std::bad_alloc &)
369 {
370 return E_OUTOFMEMORY;
371 }
372 return S_OK;
373}
374
375HRESULT GeneralTextScript::appendLine(const Utf8Str &rStrLineToAppend)
376{
377 AssertReturn(mfDataParsed, mpSetError->setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("appendLine() called before parse()")));
378
379 try
380 {
381 mScriptContentByLines.append(rStrLineToAppend);
382 }
383 catch (std::bad_alloc &)
384 {
385 return E_OUTOFMEMORY;
386 }
387 return S_OK;
388}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use