[FREELDR] Diverse enhancements.
[reactos.git] / boot / freeldr / freeldr / oslist.c
1 /*
2 * FreeLoader
3 * Copyright (C) 1998-2003 Brian Palmer <brianp@sginet.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 /* INCLUDES *******************************************************************/
21
22 #include <freeldr.h>
23 #include <debug.h>
24
25 DBG_DEFAULT_CHANNEL(INIFILE);
26
27 #define TAG_OS_ITEM 'tISO'
28
29 /* FUNCTIONS ******************************************************************/
30
31 static PCSTR CopyString(PCSTR Source)
32 {
33 PSTR Dest;
34
35 if (!Source)
36 return NULL;
37
38 Dest = FrLdrHeapAlloc(strlen(Source) + 1, TAG_STRING);
39 if (Dest)
40 strcpy(Dest, Source);
41
42 return Dest;
43 }
44
45 static ULONG
46 GetDefaultOperatingSystem(
47 IN OperatingSystemItem* OperatingSystemList,
48 IN ULONG OperatingSystemCount)
49 {
50 ULONG DefaultOS = 0;
51 ULONG i;
52 ULONG_PTR SectionId;
53 PCSTR DefaultOSName;
54 CHAR DefaultOSText[80];
55
56 if (!IniOpenSection("FreeLoader", &SectionId))
57 return 0;
58
59 DefaultOSName = CmdLineGetDefaultOS();
60 if (DefaultOSName == NULL)
61 {
62 if (IniReadSettingByName(SectionId, "DefaultOS", DefaultOSText, sizeof(DefaultOSText)))
63 {
64 DefaultOSName = DefaultOSText;
65 }
66 }
67
68 if (DefaultOSName != NULL)
69 {
70 for (i = 0; i < OperatingSystemCount; ++i)
71 {
72 if (_stricmp(DefaultOSName, OperatingSystemList[i].SectionName) == 0)
73 {
74 DefaultOS = i;
75 break;
76 }
77 }
78 }
79
80 return DefaultOS;
81 }
82
83 OperatingSystemItem*
84 InitOperatingSystemList(
85 OUT PULONG OperatingSystemCount,
86 OUT PULONG DefaultOperatingSystem)
87 {
88 OperatingSystemItem* Items;
89 ULONG Count;
90 ULONG i;
91 ULONG_PTR OsSectionId, SectionId;
92 PCHAR TitleStart, TitleEnd;
93 PCSTR OsLoadOptions;
94 BOOLEAN HadSection;
95 BOOLEAN HadNoBootType;
96 CHAR SettingName[260];
97 CHAR SettingValue[260];
98 CHAR BootType[80];
99 CHAR TempBuffer[sizeof(SettingValue)/sizeof(CHAR)];
100
101 /* Open the [Operating Systems] section */
102 if (!IniOpenSection("Operating Systems", &OsSectionId))
103 return NULL;
104
105 /* Count the number of operating systems in the section */
106 Count = IniGetNumSectionItems(OsSectionId);
107
108 /* Allocate memory to hold operating system lists */
109 Items = FrLdrHeapAlloc(Count * sizeof(OperatingSystemItem), TAG_OS_ITEM);
110 if (!Items)
111 return NULL;
112
113 /* Now loop through and read the operating system section and display names */
114 for (i = 0; i < Count; ++i)
115 {
116 IniReadSettingByNumber(OsSectionId, i,
117 SettingName, sizeof(SettingName),
118 SettingValue, sizeof(SettingValue));
119 if (!*SettingName)
120 {
121 ERR("Invalid OS entry number %lu, skipping.\n", i);
122 continue;
123 }
124
125 /* Retrieve the start and end of the title */
126 TitleStart = SettingValue;
127 /* Trim any leading whitespace and quotes */
128 while (*TitleStart == ' ' || *TitleStart == '\t' || *TitleStart == '"')
129 ++TitleStart;
130 TitleEnd = TitleStart;
131 /* Go up to the first last quote */
132 while (*TitleEnd != ANSI_NULL && *TitleEnd != '"')
133 ++TitleEnd;
134
135 /* NULL-terminate the title */
136 if (*TitleEnd)
137 *TitleEnd++ = ANSI_NULL; // Skip the quote too.
138
139 /* Retrieve the options after the quoted title */
140 if (*TitleEnd)
141 {
142 /* Trim any trailing whitespace and quotes */
143 while (*TitleEnd == ' ' || *TitleEnd == '\t' || *TitleEnd == '"')
144 ++TitleEnd;
145 }
146 OsLoadOptions = (*TitleEnd ? TitleEnd : NULL);
147
148 // TRACE("\n"
149 // "SettingName = '%s'\n"
150 // "TitleStart = '%s'\n"
151 // "OsLoadOptions = '%s'\n",
152 // SettingName, TitleStart, OsLoadOptions);
153
154 /*
155 * Determine whether this is a legacy operating system entry of the form:
156 *
157 * [Operating Systems]
158 * ArcOsLoadPartition="LoadIdentifier" /List /of /Options
159 *
160 * and if so, convert it into a new operating system INI entry:
161 *
162 * [Operating Systems]
163 * SectionIdentifier="LoadIdentifier"
164 *
165 * [SectionIdentifier]
166 * BootType=...
167 * SystemPath=ArcOsLoadPartition
168 * Options=/List /of /Options
169 *
170 * The "BootType" value is heuristically determined from the form of
171 * the ArcOsLoadPartition: if this is an ARC path, the "BootType" value
172 * is "Windows", otherwise if this is a DOS path the "BootType" value
173 * is "BootSector". This ensures backwards-compatibility with NTLDR.
174 */
175
176 /* Try to open the operating system section in the .ini file */
177 HadSection = IniOpenSection(SettingName, &SectionId);
178 if (HadSection)
179 {
180 /* This is a new OS entry: try to read the boot type */
181 IniReadSettingByName(SectionId, "BootType", BootType, sizeof(BootType));
182 }
183 else
184 {
185 /* This is a legacy OS entry: no explicit BootType specified, we will infer one */
186 *BootType = ANSI_NULL;
187 }
188
189 /* Check whether we have got a BootType value; if not, try to infer one */
190 HadNoBootType = (*BootType == ANSI_NULL);
191 if (HadNoBootType)
192 {
193 #ifdef _M_IX86
194 ULONG FileId;
195 if (ArcOpen((PSTR)SettingName, OpenReadOnly, &FileId) == ESUCCESS)
196 {
197 ArcClose(FileId);
198 strcpy(BootType, "BootSector");
199 }
200 else
201 #endif
202 {
203 strcpy(BootType, "Windows");
204 }
205 }
206
207 /* This is a legacy OS entry: convert it into a new OS entry */
208 if (!HadSection)
209 {
210 TIMEINFO* TimeInfo;
211
212 /* Save the system path from the original SettingName (overwritten below) */
213 RtlStringCbCopyA(TempBuffer, sizeof(TempBuffer), SettingName);
214
215 /* Generate a unique section name */
216 TimeInfo = ArcGetTime();
217 if (_stricmp(BootType, "BootSector") == 0)
218 {
219 RtlStringCbPrintfA(SettingName, sizeof(SettingName),
220 "BootSectorFile%u%u%u%u%u%u",
221 TimeInfo->Year, TimeInfo->Day, TimeInfo->Month,
222 TimeInfo->Hour, TimeInfo->Minute, TimeInfo->Second);
223 }
224 else if (_stricmp(BootType, "Windows") == 0)
225 {
226 RtlStringCbPrintfA(SettingName, sizeof(SettingName),
227 "Windows%u%u%u%u%u%u",
228 TimeInfo->Year, TimeInfo->Day, TimeInfo->Month,
229 TimeInfo->Hour, TimeInfo->Minute, TimeInfo->Second);
230 }
231 else
232 {
233 ASSERT(FALSE);
234 }
235
236 /* Add the section */
237 if (!IniAddSection(SettingName, &SectionId))
238 {
239 ERR("Could not convert legacy OS entry! Continuing...\n");
240 continue;
241 }
242
243 /* Add the system path */
244 if (_stricmp(BootType, "BootSector") == 0)
245 {
246 if (!IniAddSettingValueToSection(SectionId, "BootSectorFile", TempBuffer))
247 {
248 ERR("Could not convert legacy OS entry! Continuing...\n");
249 continue;
250 }
251 }
252 else if (_stricmp(BootType, "Windows") == 0)
253 {
254 if (!IniAddSettingValueToSection(SectionId, "SystemPath", TempBuffer))
255 {
256 ERR("Could not convert legacy OS entry! Continuing...\n");
257 continue;
258 }
259 }
260 else
261 {
262 ASSERT(FALSE);
263 }
264
265 /* Add the OS options */
266 if (OsLoadOptions && !IniAddSettingValueToSection(SectionId, "Options", OsLoadOptions))
267 {
268 ERR("Could not convert legacy OS entry! Continuing...\n");
269 continue;
270 }
271 }
272
273 /* Add or modify the BootType if needed */
274 if (HadNoBootType && !IniModifySettingValue(SectionId, "BootType", BootType))
275 {
276 ERR("Could not fixup the BootType entry for OS '%s', ignoring.\n", SettingName);
277 }
278
279 /*
280 * If this is a new OS entry, but some options were given appended to
281 * the OS entry item, append them instead to the "Options=" value.
282 */
283 if (HadSection && OsLoadOptions && *OsLoadOptions)
284 {
285 /* Read the original "Options=" value */
286 *TempBuffer = ANSI_NULL;
287 if (!IniReadSettingByName(SectionId, "Options", TempBuffer, sizeof(TempBuffer)))
288 TRACE("No 'Options' value found for OS '%s', ignoring.\n", SettingName);
289
290 /* Concatenate the options together */
291 RtlStringCbCatA(TempBuffer, sizeof(TempBuffer), " ");
292 RtlStringCbCatA(TempBuffer, sizeof(TempBuffer), OsLoadOptions);
293
294 /* Save them */
295 if (!IniModifySettingValue(SectionId, "Options", TempBuffer))
296 ERR("Could not modify the options for OS '%s', ignoring.\n", SettingName);
297 }
298
299 /* Copy the OS section name and identifier */
300 Items[i].SectionName = CopyString(SettingName);
301 Items[i].LoadIdentifier = CopyString(TitleStart);
302 // TRACE("We did Items[%lu]: SectionName = '%s', LoadIdentifier = '%s'\n", i, Items[i].SectionName, Items[i].LoadIdentifier);
303 }
304
305 /* Return success */
306 *OperatingSystemCount = Count;
307 *DefaultOperatingSystem = GetDefaultOperatingSystem(Items, Count);
308 return Items;
309 }