VirtualBox

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

Last change on this file since 91406 was 90828, checked in by vboxsync, 3 years ago

Main: bugref:1909: Added API localization

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.8 KB
Line 
1/* $Id: TextScript.cpp 90828 2021-08-24 09:44:46Z vboxsync $ */
2/** @file
3 * Classes for reading/parsing/saving text scripts (unattended installation, ++).
4 */
5
6/*
7 * Copyright (C) 2006-2020 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'"),
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, "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, "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, "attempting to set line %zu when there are only %zu lines",
267 idxLine, mScriptContentByLines.size()));
268 try
269 {
270 mScriptContentByLines[idxLine] = rStrNewLine;
271 }
272 catch (std::bad_alloc &)
273 {
274 return E_OUTOFMEMORY;
275 }
276 return S_OK;
277}
278
279vector<size_t> GeneralTextScript::findTemplate(const Utf8Str &rStrNeedle,
280 RTCString::CaseSensitivity enmCase /*= RTCString::CaseSensitive*/)
281{
282 vector<size_t> vecHitLineNumbers;
283 size_t const cLines = mScriptContentByLines.size();
284 for (size_t iLine = 0; iLine < cLines; iLine++)
285 if (mScriptContentByLines[iLine].contains(rStrNeedle, enmCase))
286 vecHitLineNumbers.push_back(iLine);
287
288 return vecHitLineNumbers;
289}
290
291HRESULT GeneralTextScript::findAndReplace(size_t idxLine, const Utf8Str &rStrNeedle, const Utf8Str &rStrReplacement)
292{
293 AssertReturn(idxLine < mScriptContentByLines.size(),
294 mpSetError->setErrorBoth(E_FAIL, VERR_OUT_OF_RANGE,
295 "attempting search&replace in line %zu when there are only %zu lines",
296 idxLine, mScriptContentByLines.size()));
297
298 RTCString &rDstString = mScriptContentByLines[idxLine];
299 size_t const offNeedle = rDstString.find(&rStrNeedle);
300 if (offNeedle != RTCString::npos)
301 {
302 try
303 {
304 RTCString strBefore(rDstString, 0, offNeedle);
305 RTCString strAfter(rDstString, offNeedle + rStrNeedle.length());
306 rDstString = strBefore;
307 strBefore.setNull();
308 rDstString.append(rStrReplacement);
309 rDstString.append(strAfter);
310 }
311 catch (std::bad_alloc &)
312 {
313 return E_OUTOFMEMORY;
314 }
315 }
316 return S_OK;
317}
318
319HRESULT GeneralTextScript::appendToLine(size_t idxLine, const Utf8Str &rStrToAppend)
320{
321 AssertReturn(idxLine < mScriptContentByLines.size(),
322 mpSetError->setErrorBoth(E_FAIL, VERR_OUT_OF_RANGE, "appending to line %zu when there are only %zu lines",
323 idxLine, mScriptContentByLines.size()));
324
325 try
326 {
327 mScriptContentByLines[idxLine].append(rStrToAppend);
328 }
329 catch (std::bad_alloc &)
330 {
331 return E_OUTOFMEMORY;
332 }
333 return S_OK;
334}
335
336HRESULT GeneralTextScript::prependToLine(size_t idxLine, const Utf8Str &rStrToPrepend)
337{
338 AssertReturn(idxLine < mScriptContentByLines.size(),
339 mpSetError->setErrorBoth(E_FAIL, VERR_OUT_OF_RANGE, "prepending to line %zu when there are only %zu lines",
340 idxLine, mScriptContentByLines.size()));
341
342 RTCString &rDstString = mScriptContentByLines[idxLine];
343 try
344 {
345 RTCString strCopy;
346 rDstString.swap(strCopy);
347 rDstString.reserve(strCopy.length() + rStrToPrepend.length() + 1);
348 rDstString = rStrToPrepend;
349 rDstString.append(strCopy);
350 }
351 catch (std::bad_alloc &)
352 {
353 return E_OUTOFMEMORY;
354 }
355 return S_OK;
356}
357
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