VirtualBox

source: vbox/trunk/src/VBox/Main/BIOSSettingsImpl.cpp@ 16558

Last change on this file since 16558 was 16558, checked in by vboxsync, 16 years ago

Main: back out r42503

  • Property svn:eol-style set to native
File size: 19.8 KB
Line 
1/** @file
2 *
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22#include "BIOSSettingsImpl.h"
23#include "MachineImpl.h"
24#include "Logging.h"
25#include "GuestOSTypeImpl.h"
26#include <iprt/cpputils.h>
27
28// constructor / destructor
29/////////////////////////////////////////////////////////////////////////////
30
31HRESULT BIOSSettings::FinalConstruct()
32{
33 return S_OK;
34}
35
36void BIOSSettings::FinalRelease()
37{
38 uninit ();
39}
40
41// public initializer/uninitializer for internal purposes only
42/////////////////////////////////////////////////////////////////////////////
43
44/**
45 * Initializes the audio adapter object.
46 *
47 * @returns COM result indicator
48 */
49HRESULT BIOSSettings::init (Machine *aParent)
50{
51 LogFlowThisFuncEnter();
52 LogFlowThisFunc (("aParent: %p\n", aParent));
53
54 ComAssertRet (aParent, E_INVALIDARG);
55
56 /* Enclose the state transition NotReady->InInit->Ready */
57 AutoInitSpan autoInitSpan (this);
58 AssertReturn (autoInitSpan.isOk(), E_FAIL);
59
60 /* share the parent weakly */
61 unconst (mParent) = aParent;
62
63 mData.allocate();
64
65 autoInitSpan.setSucceeded();
66
67 LogFlowThisFuncLeave();
68 return S_OK;
69}
70
71/**
72 * Initializes the audio adapter object given another audio adapter object
73 * (a kind of copy constructor). This object shares data with
74 * the object passed as an argument.
75 *
76 * @note This object must be destroyed before the original object
77 * it shares data with is destroyed.
78 */
79HRESULT BIOSSettings::init (Machine *aParent, BIOSSettings *that)
80{
81 LogFlowThisFuncEnter();
82 LogFlowThisFunc (("aParent: %p, that: %p\n", aParent, that));
83
84 ComAssertRet (aParent && that, E_INVALIDARG);
85
86 /* Enclose the state transition NotReady->InInit->Ready */
87 AutoInitSpan autoInitSpan (this);
88 AssertReturn (autoInitSpan.isOk(), E_FAIL);
89
90 mParent = aParent;
91 mPeer = that;
92
93 AutoWriteLock thatlock (that);
94 mData.share (that->mData);
95
96 autoInitSpan.setSucceeded();
97
98 LogFlowThisFuncLeave();
99 return S_OK;
100}
101
102/**
103 * Initializes the guest object given another guest object
104 * (a kind of copy constructor). This object makes a private copy of data
105 * of the original object passed as an argument.
106 */
107HRESULT BIOSSettings::initCopy (Machine *aParent, BIOSSettings *that)
108{
109 LogFlowThisFuncEnter();
110 LogFlowThisFunc (("aParent: %p, that: %p\n", aParent, that));
111
112 ComAssertRet (aParent && that, E_INVALIDARG);
113
114 /* Enclose the state transition NotReady->InInit->Ready */
115 AutoInitSpan autoInitSpan (this);
116 AssertReturn (autoInitSpan.isOk(), E_FAIL);
117
118 mParent = aParent;
119 // mPeer is left null
120
121 AutoWriteLock thatlock (that);
122 mData.attachCopy (that->mData);
123
124 autoInitSpan.setSucceeded();
125
126 LogFlowThisFuncLeave();
127 return S_OK;
128}
129
130/**
131 * Uninitializes the instance and sets the ready flag to FALSE.
132 * Called either from FinalRelease() or by the parent when it gets destroyed.
133 */
134void BIOSSettings::uninit()
135{
136 LogFlowThisFuncEnter();
137
138 /* Enclose the state transition Ready->InUninit->NotReady */
139 AutoUninitSpan autoUninitSpan (this);
140 if (autoUninitSpan.uninitDone())
141 return;
142
143 mData.free();
144
145 mPeer.setNull();
146 mParent.setNull();
147
148 LogFlowThisFuncLeave();
149}
150
151// IBIOSSettings properties
152/////////////////////////////////////////////////////////////////////////////
153
154STDMETHODIMP BIOSSettings::COMGETTER(LogoFadeIn)(BOOL *enabled)
155{
156 if (!enabled)
157 return E_POINTER;
158
159 AutoCaller autoCaller (this);
160 CheckComRCReturnRC (autoCaller.rc());
161
162 AutoReadLock alock (this);
163
164 *enabled = mData->mLogoFadeIn;
165
166 return S_OK;
167}
168
169STDMETHODIMP BIOSSettings::COMSETTER(LogoFadeIn)(BOOL enable)
170{
171 AutoCaller autoCaller (this);
172 CheckComRCReturnRC (autoCaller.rc());
173
174 /* the machine needs to be mutable */
175 Machine::AutoMutableStateDependency adep (mParent);
176 CheckComRCReturnRC (adep.rc());
177
178 AutoWriteLock alock (this);
179
180 mData.backup();
181 mData->mLogoFadeIn = enable;
182
183 return S_OK;
184}
185
186STDMETHODIMP BIOSSettings::COMGETTER(LogoFadeOut)(BOOL *enabled)
187{
188 if (!enabled)
189 return E_POINTER;
190
191 AutoCaller autoCaller (this);
192 CheckComRCReturnRC (autoCaller.rc());
193
194 AutoReadLock alock (this);
195
196 *enabled = mData->mLogoFadeOut;
197
198 return S_OK;
199}
200
201STDMETHODIMP BIOSSettings::COMSETTER(LogoFadeOut)(BOOL enable)
202{
203 AutoCaller autoCaller (this);
204 CheckComRCReturnRC (autoCaller.rc());
205
206 /* the machine needs to be mutable */
207 Machine::AutoMutableStateDependency adep (mParent);
208 CheckComRCReturnRC (adep.rc());
209
210 AutoWriteLock alock (this);
211
212 mData.backup();
213 mData->mLogoFadeOut = enable;
214
215 return S_OK;
216}
217
218STDMETHODIMP BIOSSettings::COMGETTER(LogoDisplayTime)(ULONG *displayTime)
219{
220 if (!displayTime)
221 return E_POINTER;
222
223 AutoCaller autoCaller (this);
224 CheckComRCReturnRC (autoCaller.rc());
225
226 AutoReadLock alock (this);
227
228 *displayTime = mData->mLogoDisplayTime;
229
230 return S_OK;
231}
232
233STDMETHODIMP BIOSSettings::COMSETTER(LogoDisplayTime)(ULONG displayTime)
234{
235 AutoCaller autoCaller (this);
236 CheckComRCReturnRC (autoCaller.rc());
237
238 /* the machine needs to be mutable */
239 Machine::AutoMutableStateDependency adep (mParent);
240 CheckComRCReturnRC (adep.rc());
241
242 AutoWriteLock alock (this);
243
244 mData.backup();
245 mData->mLogoDisplayTime = displayTime;
246
247 return S_OK;
248}
249
250STDMETHODIMP BIOSSettings::COMGETTER(LogoImagePath)(BSTR *imagePath)
251{
252 if (!imagePath)
253 return E_POINTER;
254
255 AutoCaller autoCaller (this);
256 CheckComRCReturnRC (autoCaller.rc());
257
258 AutoReadLock alock (this);
259
260 mData->mLogoImagePath.cloneTo(imagePath);
261 return S_OK;
262}
263
264STDMETHODIMP BIOSSettings::COMSETTER(LogoImagePath)(IN_BSTR imagePath)
265{
266 /* empty strings are not allowed as path names */
267 if (imagePath && !(*imagePath))
268 return E_INVALIDARG;
269
270 AutoCaller autoCaller (this);
271 CheckComRCReturnRC (autoCaller.rc());
272
273 /* the machine needs to be mutable */
274 Machine::AutoMutableStateDependency adep (mParent);
275 CheckComRCReturnRC (adep.rc());
276
277 AutoWriteLock alock (this);
278
279 mData.backup();
280 mData->mLogoImagePath = imagePath;
281
282 return S_OK;
283}
284
285STDMETHODIMP BIOSSettings::COMGETTER(BootMenuMode)(BIOSBootMenuMode_T *bootMenuMode)
286{
287 if (!bootMenuMode)
288 return E_POINTER;
289
290 AutoCaller autoCaller (this);
291 CheckComRCReturnRC (autoCaller.rc());
292
293 AutoReadLock alock (this);
294
295 *bootMenuMode = mData->mBootMenuMode;
296 return S_OK;
297}
298
299STDMETHODIMP BIOSSettings::COMSETTER(BootMenuMode)(BIOSBootMenuMode_T bootMenuMode)
300{
301 AutoCaller autoCaller (this);
302 CheckComRCReturnRC (autoCaller.rc());
303
304 /* the machine needs to be mutable */
305 Machine::AutoMutableStateDependency adep (mParent);
306 CheckComRCReturnRC (adep.rc());
307
308 AutoWriteLock alock (this);
309
310 mData.backup();
311 mData->mBootMenuMode = bootMenuMode;
312
313 return S_OK;
314}
315
316STDMETHODIMP BIOSSettings::COMGETTER(ACPIEnabled)(BOOL *enabled)
317{
318 if (!enabled)
319 return E_POINTER;
320
321 AutoCaller autoCaller (this);
322 CheckComRCReturnRC (autoCaller.rc());
323
324 AutoReadLock alock (this);
325
326 *enabled = mData->mACPIEnabled;
327
328 return S_OK;
329}
330
331STDMETHODIMP BIOSSettings::COMSETTER(ACPIEnabled)(BOOL enable)
332{
333 AutoCaller autoCaller (this);
334 CheckComRCReturnRC (autoCaller.rc());
335
336 /* the machine needs to be mutable */
337 Machine::AutoMutableStateDependency adep (mParent);
338 CheckComRCReturnRC (adep.rc());
339
340 AutoWriteLock alock (this);
341
342 mData.backup();
343 mData->mACPIEnabled = enable;
344
345 return S_OK;
346}
347
348STDMETHODIMP BIOSSettings::COMGETTER(IOAPICEnabled)(BOOL *enabled)
349{
350 if (!enabled)
351 return E_POINTER;
352
353 AutoCaller autoCaller (this);
354 CheckComRCReturnRC (autoCaller.rc());
355
356 AutoReadLock alock (this);
357
358 *enabled = mData->mIOAPICEnabled;
359
360 return S_OK;
361}
362
363STDMETHODIMP BIOSSettings::COMSETTER(IOAPICEnabled)(BOOL enable)
364{
365 AutoCaller autoCaller (this);
366 CheckComRCReturnRC (autoCaller.rc());
367
368 /* the machine needs to be mutable */
369 Machine::AutoMutableStateDependency adep (mParent);
370 CheckComRCReturnRC (adep.rc());
371
372 AutoWriteLock alock (this);
373
374 mData.backup();
375 mData->mIOAPICEnabled = enable;
376
377 return S_OK;
378}
379
380STDMETHODIMP BIOSSettings::COMGETTER(PXEDebugEnabled)(BOOL *enabled)
381{
382 if (!enabled)
383 return E_POINTER;
384
385 AutoCaller autoCaller (this);
386 CheckComRCReturnRC (autoCaller.rc());
387
388 AutoReadLock alock (this);
389
390 *enabled = mData->mPXEDebugEnabled;
391
392 return S_OK;
393}
394
395STDMETHODIMP BIOSSettings::COMSETTER(PXEDebugEnabled)(BOOL enable)
396{
397 AutoCaller autoCaller (this);
398 CheckComRCReturnRC (autoCaller.rc());
399
400 /* the machine needs to be mutable */
401 Machine::AutoMutableStateDependency adep (mParent);
402 CheckComRCReturnRC (adep.rc());
403
404 AutoWriteLock alock (this);
405
406 mData.backup();
407 mData->mPXEDebugEnabled = enable;
408
409 return S_OK;
410}
411
412STDMETHODIMP BIOSSettings::COMGETTER(IDEControllerType)(IDEControllerType_T *aControllerType)
413{
414 CheckComArgOutPointerValid(aControllerType);
415
416 AutoCaller autoCaller (this);
417 CheckComRCReturnRC (autoCaller.rc());
418
419 AutoReadLock alock (this);
420
421 *aControllerType = mData->mIDEControllerType;
422
423 return S_OK;
424}
425
426STDMETHODIMP BIOSSettings::COMSETTER(IDEControllerType)(IDEControllerType_T aControllerType)
427{
428 AutoCaller autoCaller (this);
429 CheckComRCReturnRC (autoCaller.rc());
430
431 /* the machine needs to be mutable */
432 Machine::AutoMutableStateDependency adep (mParent);
433 CheckComRCReturnRC (adep.rc());
434
435 AutoWriteLock alock (this);
436
437 /* make sure the value is allowed */
438 switch (aControllerType)
439 {
440 case IDEControllerType_PIIX3:
441 case IDEControllerType_PIIX4:
442 break;
443 default:
444 return setError (E_INVALIDARG,
445 tr("Invalid IDE controller type '%d'"),
446 aControllerType);
447 }
448
449 mData.backup();
450
451 mData->mIDEControllerType = aControllerType;
452
453 return S_OK;
454}
455
456STDMETHODIMP BIOSSettings::COMGETTER(TimeOffset)(LONG64 *offset)
457{
458 if (!offset)
459 return E_POINTER;
460
461 AutoCaller autoCaller (this);
462 CheckComRCReturnRC (autoCaller.rc());
463
464 AutoReadLock alock (this);
465
466 *offset = mData->mTimeOffset;
467
468 return S_OK;
469}
470
471STDMETHODIMP BIOSSettings::COMSETTER(TimeOffset)(LONG64 offset)
472{
473 AutoCaller autoCaller (this);
474 CheckComRCReturnRC (autoCaller.rc());
475
476 /* the machine needs to be mutable */
477 Machine::AutoMutableStateDependency adep (mParent);
478 CheckComRCReturnRC (adep.rc());
479
480 AutoWriteLock alock (this);
481
482 mData.backup();
483 mData->mTimeOffset = offset;
484
485 return S_OK;
486}
487
488
489// IBIOSSettings methods
490/////////////////////////////////////////////////////////////////////////////
491
492// public methods only for internal purposes
493/////////////////////////////////////////////////////////////////////////////
494
495/**
496 * Loads settings from the given machine node.
497 * May be called once right after this object creation.
498 *
499 * @param aMachineNode <Machine> node.
500 *
501 * @note Locks this object for writing.
502 */
503HRESULT BIOSSettings::loadSettings (const settings::Key &aMachineNode)
504{
505 using namespace settings;
506
507 AssertReturn (!aMachineNode.isNull(), E_FAIL);
508
509 AutoCaller autoCaller (this);
510 AssertComRCReturnRC (autoCaller.rc());
511
512 AutoWriteLock alock (this);
513
514 /* Note: we assume that the default values for attributes of optional
515 * nodes are assigned in the Data::Data() constructor and don't do it
516 * here. It implies that this method may only be called after constructing
517 * a new BIOSSettings object while all its data fields are in the default
518 * values. Exceptions are fields whose creation time defaults don't match
519 * values that should be applied when these fields are not explicitly set
520 * in the settings file (for backwards compatibility reasons). This takes
521 * place when a setting of a newly created object must default to A while
522 * the same setting of an object loaded from the old settings file must
523 * default to B. */
524
525 /* BIOS node (required) */
526 Key biosNode = aMachineNode.key ("BIOS");
527
528 /* ACPI (required) */
529 {
530 Key acpiNode = biosNode.key ("ACPI");
531
532 mData->mACPIEnabled = acpiNode.value <bool> ("enabled");
533 }
534
535 /* IOAPIC (optional) */
536 {
537 Key ioapicNode = biosNode.findKey ("IOAPIC");
538 if (!ioapicNode.isNull())
539 mData->mIOAPICEnabled = ioapicNode.value <bool> ("enabled");
540 }
541
542 /* Logo (optional) */
543 {
544 Key logoNode = biosNode.findKey ("Logo");
545 if (!logoNode.isNull())
546 {
547 mData->mLogoFadeIn = logoNode.value <bool> ("fadeIn");
548 mData->mLogoFadeOut = logoNode.value <bool> ("fadeOut");
549 mData->mLogoDisplayTime = logoNode.value <ULONG> ("displayTime");
550 mData->mLogoImagePath = logoNode.stringValue ("imagePath");
551 }
552 }
553
554 /* boot menu (optional) */
555 {
556 Key bootMenuNode = biosNode.findKey ("BootMenu");
557 if (!bootMenuNode.isNull())
558 {
559 mData->mBootMenuMode = BIOSBootMenuMode_MessageAndMenu;
560 const char *modeStr = bootMenuNode.stringValue ("mode");
561
562 if (strcmp (modeStr, "Disabled") == 0)
563 mData->mBootMenuMode = BIOSBootMenuMode_Disabled;
564 else if (strcmp (modeStr, "MenuOnly") == 0)
565 mData->mBootMenuMode = BIOSBootMenuMode_MenuOnly;
566 else if (strcmp (modeStr, "MessageAndMenu") == 0)
567 mData->mBootMenuMode = BIOSBootMenuMode_MessageAndMenu;
568 else
569 ComAssertMsgFailedRet (("Invalid boot menu mode '%s'", modeStr),
570 E_FAIL);
571 }
572 }
573
574 /* PXE debug logging (optional) */
575 {
576 Key pxedebugNode = biosNode.findKey ("PXEDebug");
577 if (!pxedebugNode.isNull())
578 mData->mPXEDebugEnabled = pxedebugNode.value <bool> ("enabled");
579 }
580
581 /* time offset (optional) */
582 {
583 Key timeOffsetNode = biosNode.findKey ("TimeOffset");
584 if (!timeOffsetNode.isNull())
585 mData->mTimeOffset = timeOffsetNode.value <LONG64> ("value");
586 }
587
588 /* IDE controller type (optional, for old machines that lack this node,
589 * defaults to PIIX3) */
590 {
591 mData->mIDEControllerType = IDEControllerType_PIIX3;
592
593 Key ideControllerNode = biosNode.findKey ("IDEController");
594 if (!ideControllerNode.isNull())
595 {
596 const char *typeStr = ideControllerNode.stringValue ("type");
597 if (strcmp (typeStr, "PIIX3") == 0)
598 mData->mIDEControllerType = IDEControllerType_PIIX3;
599 else if (strcmp (typeStr, "PIIX4") == 0)
600 mData->mIDEControllerType = IDEControllerType_PIIX4;
601 else
602 ComAssertMsgFailedRet (("Invalid boot menu mode '%s'", typeStr),
603 E_FAIL);
604 }
605 }
606
607 return S_OK;
608}
609
610/**
611 * Saves settings to the given machine node.
612 *
613 * @param aMachineNode <Machine> node.
614 *
615 * @note Locks this object for reading.
616 */
617HRESULT BIOSSettings::saveSettings (settings::Key &aMachineNode)
618{
619 using namespace settings;
620
621 AssertReturn (!aMachineNode.isNull(), E_FAIL);
622
623 AutoCaller autoCaller (this);
624 AssertComRCReturnRC (autoCaller.rc());
625
626 AutoReadLock alock (this);
627
628 Key biosNode = aMachineNode.createKey ("BIOS");
629
630 /* ACPI */
631 {
632 Key acpiNode = biosNode.createKey ("ACPI");
633 acpiNode.setValue <bool> ("enabled", !!mData->mACPIEnabled);
634 }
635
636 /* IOAPIC */
637 {
638 Key ioapicNode = biosNode.createKey ("IOAPIC");
639 ioapicNode.setValue <bool> ("enabled", !!mData->mIOAPICEnabled);
640 }
641
642 /* BIOS logo (optional) **/
643 {
644 Key logoNode = biosNode.createKey ("Logo");
645 logoNode.setValue <bool> ("fadeIn", !!mData->mLogoFadeIn);
646 logoNode.setValue <bool> ("fadeOut", !!mData->mLogoFadeOut);
647 logoNode.setValue <ULONG> ("displayTime", mData->mLogoDisplayTime);
648 logoNode.setValueOr <Bstr> ("imagePath", mData->mLogoImagePath, Bstr::Null);
649 }
650
651 /* boot menu (optional) */
652 {
653 Key bootMenuNode = biosNode.createKey ("BootMenu");
654 const char *modeStr = NULL;
655 switch (mData->mBootMenuMode)
656 {
657 case BIOSBootMenuMode_Disabled:
658 modeStr = "Disabled";
659 break;
660 case BIOSBootMenuMode_MenuOnly:
661 modeStr = "MenuOnly";
662 break;
663 case BIOSBootMenuMode_MessageAndMenu:
664 modeStr = "MessageAndMenu";
665 break;
666 default:
667 ComAssertMsgFailedRet (("Invalid boot menu type: %d",
668 mData->mBootMenuMode),
669 E_FAIL);
670 }
671 bootMenuNode.setStringValue ("mode", modeStr);
672 }
673
674 /* time offset (optional) */
675 {
676 Key timeOffsetNode = biosNode.createKey ("TimeOffset");
677 timeOffsetNode.setValue <LONG64> ("value", mData->mTimeOffset);
678 }
679
680 /* PXE debug flag (optional) */
681 {
682 Key pxedebugNode = biosNode.createKey ("PXEDebug");
683 pxedebugNode.setValue <bool> ("enabled", !!mData->mPXEDebugEnabled);
684 }
685
686 /* IDE controller type */
687 {
688 Key ideControllerNode = biosNode.createKey ("IDEController");
689 const char *ideControllerTypeStr = NULL;
690 switch (mData->mIDEControllerType)
691 {
692 case IDEControllerType_PIIX3:
693 ideControllerTypeStr = "PIIX3";
694 break;
695 case IDEControllerType_PIIX4:
696 ideControllerTypeStr = "PIIX4";
697 break;
698 default:
699 ComAssertMsgFailedRet (("Invalid IDE Controller type: %d",
700 mData->mIDEControllerType),
701 E_FAIL);
702 }
703 ideControllerNode.setStringValue ("type", ideControllerTypeStr);
704 }
705
706 return S_OK;
707}
708
709void BIOSSettings::commit()
710{
711 /* sanity */
712 AutoCaller autoCaller (this);
713 AssertComRCReturnVoid (autoCaller.rc());
714
715 /* sanity too */
716 AutoCaller peerCaller (mPeer);
717 AssertComRCReturnVoid (peerCaller.rc());
718
719 /* lock both for writing since we modify both (mPeer is "master" so locked
720 * first) */
721 AutoMultiWriteLock2 alock (mPeer, this);
722
723 if (mData.isBackedUp())
724 {
725 mData.commit();
726 if (mPeer)
727 {
728 /* attach new data to the peer and reshare it */
729 AutoWriteLock peerlock (mPeer);
730 mPeer->mData.attach (mData);
731 }
732 }
733}
734
735void BIOSSettings::copyFrom (BIOSSettings *aThat)
736{
737 AssertReturnVoid (aThat != NULL);
738
739 /* sanity */
740 AutoCaller autoCaller (this);
741 AssertComRCReturnVoid (autoCaller.rc());
742
743 /* sanity too */
744 AutoCaller thatCaller (aThat);
745 AssertComRCReturnVoid (thatCaller.rc());
746
747 /* peer is not modified, lock it for reading (aThat is "master" so locked
748 * first) */
749 AutoMultiLock2 alock (aThat->rlock(), this->wlock());
750
751 /* this will back up current data */
752 mData.assignCopy (aThat->mData);
753}
754
755void BIOSSettings::applyDefaults (GuestOSType *aOsType)
756{
757 AssertReturnVoid (aOsType != NULL);
758
759 /* sanity */
760 AutoCaller autoCaller (this);
761 AssertComRCReturnVoid (autoCaller.rc());
762
763 AutoWriteLock alock (this);
764
765 /* Initialize default BIOS settings here */
766 mData->mIOAPICEnabled = aOsType->recommendedIOAPIC();
767}
768/* vi: set tabstop=4 shiftwidth=4 expandtab: */
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