VirtualBox

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

Last change on this file since 94884 was 94349, checked in by vboxsync, 3 years ago

Main/Unattended: ​​bugref:9781. Removing a rogue line.

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