VirtualBox

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

Last change on this file since 86714 was 82968, checked in by vboxsync, 5 years 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.9 KB
Line 
1/* $Id: TextScript.cpp 82968 2020-02-04 10:35:17Z 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, mpSetError->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, mpSetError->tr("'%s' isn't valid UTF-8: %Rrc"), pszFilename, vrc);
97 }
98 else
99 hrc = mpSetError->setErrorVrc(vrc, mpSetError->tr("Error reading '%s': %Rrc"), pszFilename, vrc);
100 mStrScriptFullContent.setNull();
101 }
102 else
103 hrc = mpSetError->setErrorVrc(vrc, mpSetError->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,
108 mpSetError->tr("'%s' is too big (max 16MB): %'RU64"), pszFilename, cbFile);
109 else
110 hrc = mpSetError->setErrorVrc(vrc, mpSetError->tr("RTVfsFileQuerySize failed (%Rrc)"), vrc);
111 return hrc;
112}
113
114HRESULT BaseTextScript::save(const Utf8Str &rStrFilename, bool fOverwrite)
115{
116 /*
117 * We may have to append the default filename to the
118 */
119 const char *pszFilename = rStrFilename.c_str();
120 Utf8Str strWithDefaultFilename;
121 if ( getDefaultFilename() != NULL
122 && *getDefaultFilename() != '\0'
123 && RTDirExists(rStrFilename.c_str()) )
124 {
125 try
126 {
127 strWithDefaultFilename = rStrFilename;
128 strWithDefaultFilename.append(RTPATH_SLASH);
129 strWithDefaultFilename.append(getDefaultFilename());
130 }
131 catch (std::bad_alloc &)
132 {
133 return E_OUTOFMEMORY;
134 }
135 pszFilename = strWithDefaultFilename.c_str();
136 }
137
138 /*
139 * Save the filename for later use.
140 */
141 try
142 {
143 mStrSavedPath = pszFilename;
144 }
145 catch (std::bad_alloc &)
146 {
147 return E_OUTOFMEMORY;
148 }
149
150 /*
151 * Use the saveToString method to produce the content.
152 */
153 Utf8Str strDst;
154 HRESULT hrc = saveToString(strDst);
155 if (SUCCEEDED(hrc))
156 {
157 /*
158 * Write the content.
159 */
160 RTFILE hFile;
161 uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_ALL;
162 if (fOverwrite)
163 fOpen |= RTFILE_O_CREATE_REPLACE;
164 else
165 fOpen |= RTFILE_O_CREATE;
166 int vrc = RTFileOpen(&hFile, pszFilename, fOpen);
167 if (RT_SUCCESS(vrc))
168 {
169 vrc = RTFileWrite(hFile, strDst.c_str(), strDst.length(), NULL);
170 if (RT_SUCCESS(vrc))
171 {
172 vrc = RTFileClose(hFile);
173 if (RT_SUCCESS(vrc))
174 {
175 LogRelFlow(("GeneralTextScript::save(): saved %zu bytes to '%s'\n", strDst.length(), pszFilename));
176 return S_OK;
177 }
178 }
179 RTFileClose(hFile);
180 RTFileDelete(pszFilename);
181 hrc = mpSetError->setErrorVrc(vrc, mpSetError->tr("Error writing to '%s' (%Rrc)"), pszFilename, vrc);
182 }
183 else
184 hrc = mpSetError->setErrorVrc(vrc, mpSetError->tr("Error creating/replacing '%s' (%Rrc)"), pszFilename, vrc);
185 }
186 return hrc;
187}
188
189
190
191/*********************************************************************************************************************************
192* GeneralTextScript Implementation *
193*********************************************************************************************************************************/
194
195HRESULT GeneralTextScript::parse()
196{
197// AssertReturn(!mfDataParsed, mpSetError->setErrorBoth(E_FAIL, VERR_WRONG_ORDER, "parse called more than once"));
198
199 /*
200 * Split the raw context into an array of lines.
201 */
202 try
203 {
204 mScriptContentByLines = mStrScriptFullContent.split("\n");
205 }
206 catch (std::bad_alloc &)
207 {
208 mScriptContentByLines.clear();
209 return E_OUTOFMEMORY;
210 }
211
212 mfDataParsed = true;
213 return S_OK;
214}
215
216HRESULT GeneralTextScript::saveToString(Utf8Str &rStrDst)
217{
218 AssertReturn(mfDataParsed, mpSetError->setErrorBoth(E_FAIL, VERR_WRONG_ORDER, "saveToString() called before parse()"));
219
220 /*
221 * Calc the required size first.
222 */
223 size_t const cLines = mScriptContentByLines.size();
224 size_t cbTotal = 1;
225 for (size_t iLine = 0; iLine < cLines; iLine++)
226 cbTotal = mScriptContentByLines[iLine].length() + 1;
227
228 /*
229 * Clear the output and try reserve sufficient space.
230 */
231 rStrDst.setNull();
232
233 int vrc = rStrDst.reserveNoThrow(cbTotal);
234 if (RT_FAILURE(vrc))
235 return E_OUTOFMEMORY;
236
237 /*
238 * Assemble the output.
239 */
240 for (size_t iLine = 0; iLine < cLines; iLine++)
241 {
242 try
243 {
244 rStrDst.append(mScriptContentByLines[iLine]);
245 rStrDst.append('\n');
246 }
247 catch (std::bad_alloc &)
248 {
249 return E_OUTOFMEMORY;
250 }
251 }
252
253 return S_OK;
254}
255
256const RTCString &GeneralTextScript::getContentOfLine(size_t idxLine)
257{
258 if (idxLine < mScriptContentByLines.size())
259 return mScriptContentByLines[idxLine];
260 return Utf8Str::Empty;
261}
262
263
264HRESULT GeneralTextScript::setContentOfLine(size_t idxLine, const Utf8Str &rStrNewLine)
265{
266 AssertReturn(idxLine < mScriptContentByLines.size(),
267 mpSetError->setErrorBoth(E_FAIL, VERR_OUT_OF_RANGE, "attempting to set line %zu when there are only %zu lines",
268 idxLine, mScriptContentByLines.size()));
269 try
270 {
271 mScriptContentByLines[idxLine] = rStrNewLine;
272 }
273 catch (std::bad_alloc &)
274 {
275 return E_OUTOFMEMORY;
276 }
277 return S_OK;
278}
279
280vector<size_t> GeneralTextScript::findTemplate(const Utf8Str &rStrNeedle,
281 RTCString::CaseSensitivity enmCase /*= RTCString::CaseSensitive*/)
282{
283 vector<size_t> vecHitLineNumbers;
284 size_t const cLines = mScriptContentByLines.size();
285 for (size_t iLine = 0; iLine < cLines; iLine++)
286 if (mScriptContentByLines[iLine].contains(rStrNeedle, enmCase))
287 vecHitLineNumbers.push_back(iLine);
288
289 return vecHitLineNumbers;
290}
291
292HRESULT GeneralTextScript::findAndReplace(size_t idxLine, const Utf8Str &rStrNeedle, const Utf8Str &rStrReplacement)
293{
294 AssertReturn(idxLine < mScriptContentByLines.size(),
295 mpSetError->setErrorBoth(E_FAIL, VERR_OUT_OF_RANGE,
296 "attempting search&replace in line %zu when there are only %zu lines",
297 idxLine, mScriptContentByLines.size()));
298
299 RTCString &rDstString = mScriptContentByLines[idxLine];
300 size_t const offNeedle = rDstString.find(&rStrNeedle);
301 if (offNeedle != RTCString::npos)
302 {
303 try
304 {
305 RTCString strBefore(rDstString, 0, offNeedle);
306 RTCString strAfter(rDstString, offNeedle + rStrNeedle.length());
307 rDstString = strBefore;
308 strBefore.setNull();
309 rDstString.append(rStrReplacement);
310 rDstString.append(strAfter);
311 }
312 catch (std::bad_alloc &)
313 {
314 return E_OUTOFMEMORY;
315 }
316 }
317 return S_OK;
318}
319
320HRESULT GeneralTextScript::appendToLine(size_t idxLine, const Utf8Str &rStrToAppend)
321{
322 AssertReturn(idxLine < mScriptContentByLines.size(),
323 mpSetError->setErrorBoth(E_FAIL, VERR_OUT_OF_RANGE, "appending to line %zu when there are only %zu lines",
324 idxLine, mScriptContentByLines.size()));
325
326 try
327 {
328 mScriptContentByLines[idxLine].append(rStrToAppend);
329 }
330 catch (std::bad_alloc &)
331 {
332 return E_OUTOFMEMORY;
333 }
334 return S_OK;
335}
336
337HRESULT GeneralTextScript::prependToLine(size_t idxLine, const Utf8Str &rStrToPrepend)
338{
339 AssertReturn(idxLine < mScriptContentByLines.size(),
340 mpSetError->setErrorBoth(E_FAIL, VERR_OUT_OF_RANGE, "prepending to line %zu when there are only %zu lines",
341 idxLine, mScriptContentByLines.size()));
342
343 RTCString &rDstString = mScriptContentByLines[idxLine];
344 try
345 {
346 RTCString strCopy;
347 rDstString.swap(strCopy);
348 rDstString.reserve(strCopy.length() + rStrToPrepend.length() + 1);
349 rDstString = rStrToPrepend;
350 rDstString.append(strCopy);
351 }
352 catch (std::bad_alloc &)
353 {
354 return E_OUTOFMEMORY;
355 }
356 return S_OK;
357}
358
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