Chameleon

Chameleon Svn Source Tree

Root/branches/ErmaC/Enoch/i386/boot2/options.c

1/*
2 * Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Portions Copyright (c) 1999-2004 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 2.0 (the "License"). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
20 * under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25#include "boot.h"
26#include "bootstruct.h"
27#include "fdisk.h"
28#include "ramdisk.h"
29#include "gui.h"
30#include "term.h"
31#include "embedded.h"
32#include "pci.h"
33#include "modules.h"
34
35#if DEBUG
36#define DBG(x...)printf(x)
37#else
38#define DBG(x...)msglog(x)
39#endif
40
41bool showBootBanner = true; //Azi:showinfo
42static bool shouldboot = false;
43
44extern int multiboot_timeout;
45extern int multiboot_timeout_set;
46
47extern BVRef bvChain;
48//extern intmenucount;
49
50extern intgDeviceCount;
51
52intselectIndex = 0;
53MenuItem * menuItems = NULL;
54
55enum {
56 kMenuTopRow = 5,
57 kMenuMaxItems = 10,
58 kScreenLastRow = 24
59};
60
61extern char* msgbuf;
62
63void showTextBuffer(char *buf_orig, int size);
64
65//==========================================================================
66
67typedef struct {
68 int x;
69 int y;
70 int type;
71} CursorState;
72
73static void changeCursor( int col, int row, int type, CursorState * cs )
74{
75 if (cs) getCursorPositionAndType( &cs->x, &cs->y, &cs->type );
76 setCursorType( type );
77 setCursorPosition( col, row, 0 );
78}
79
80static void moveCursor( int col, int row )
81{
82 setCursorPosition( col, row, 0 );
83}
84
85static void restoreCursor( const CursorState * cs )
86{
87 setCursorPosition( cs->x, cs->y, 0 );
88 setCursorType( cs->type );
89}
90
91//==========================================================================
92
93/* Flush keyboard buffer; returns TRUE if any of the flushed
94 * characters was F8.
95 */
96
97static bool flushKeyboardBuffer(void)
98{
99 bool status = false;
100
101 while ( readKeyboardStatus() ) {
102 if (bgetc() == 0x4200) status = true;
103 }
104 return status;
105}
106
107//==========================================================================
108
109static int countdown( const char * msg, int row, int timeout )
110{
111 unsigned long time;
112 int ch = 0;
113 int col = strlen(msg) + 1;
114
115 flushKeyboardBuffer();
116
117if( bootArgs->Video.v_display == VGA_TEXT_MODE )
118{
119moveCursor( 0, row );
120printf(msg);
121
122} else {
123
124position_t p = pos( gui.screen.width / 2 + 1 , ( gui.devicelist.pos.y + 3 ) + ( ( gui.devicelist.height - gui.devicelist.iconspacing ) / 2 ) );
125
126char dummy[80];
127getBootVolumeDescription( gBootVolume, dummy, sizeof(dummy) - 1, true );
128drawDeviceIcon( gBootVolume, gui.screen.pixmap, p, true );
129drawStrCenteredAt( (char *) msg, &font_small, gui.screen.pixmap, gui.countdown.pos );
130
131// make this screen the new background
132memcpy( gui.backbuffer->pixels, gui.screen.pixmap->pixels, gui.backbuffer->width * gui.backbuffer->height * 4 );
133
134}
135
136int multi_buff = 18 * (timeout);
137 int multi = ++multi_buff;
138
139 int lasttime=0;
140
141 for ( time = time18(), timeout++; timeout > 0; )
142 {
143if( time18() > lasttime)
144{
145multi--;
146lasttime=time18();
147}
148
149 if ( (ch = readKeyboardStatus()) )
150 break;
151
152 // Count can be interrupted by holding down shift,
153 // control or alt key
154 if ( ( readKeyboardShiftFlags() & 0x0F ) != 0 )
155{
156 ch = 1;
157 break;
158 }
159
160 if ( time18() >= time )
161 {
162 time += 18;
163 timeout--;
164
165if( bootArgs->Video.v_display == VGA_TEXT_MODE )
166{
167moveCursor( col, row );
168printf("(%d) ", timeout);
169}
170 }
171
172if( bootArgs->Video.v_display != VGA_TEXT_MODE )
173{
174drawProgressBar( gui.screen.pixmap, 100, gui.progressbar.pos , ( multi * 100 / multi_buff ) );
175gui.redraw = true;
176updateVRAM();
177}
178
179 }
180
181 flushKeyboardBuffer();
182
183 return ch;
184}
185
186//==========================================================================
187
188char gBootArgs[BOOT_STRING_LEN];
189static char * gBootArgsPtr = gBootArgs;
190static char * gBootArgsEnd = gBootArgs + BOOT_STRING_LEN - 1;
191static char booterCommand[BOOT_STRING_LEN];
192static char booterParam[BOOT_STRING_LEN];
193
194static void clearBootArgs(void)
195{
196gBootArgsPtr = gBootArgs;
197memset(gBootArgs, '\0', BOOT_STRING_LEN);
198
199if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
200clearGraphicBootPrompt();
201}
202execute_hook("ClearArgs", NULL, NULL, NULL, NULL);
203}
204
205void addBootArg(const char * argStr)
206{
207if ( (gBootArgsPtr + strlen(argStr) + 1) < gBootArgsEnd)
208{
209if(gBootArgsPtr != gBootArgs) *gBootArgsPtr++ = ' ';
210strcat(gBootArgs, argStr);
211gBootArgsPtr += strlen(argStr);
212}
213}
214
215//==========================================================================
216
217static void showBootPrompt(int row, bool visible)
218{
219extern char bootPrompt[];
220extern char bootRescanPrompt[];
221
222if( bootArgs->Video.v_display == VGA_TEXT_MODE ) {
223changeCursor( 0, row, kCursorTypeUnderline, 0 );
224clearScreenRows( row, kScreenLastRow );
225}
226
227clearBootArgs();
228
229if (visible) {
230if (bootArgs->Video.v_display == VGA_TEXT_MODE) {
231if (gEnableCDROMRescan) {
232printf( bootRescanPrompt );
233} else {
234printf( bootPrompt );
235printf( gBootArgs );
236}
237}
238} else {
239if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
240clearGraphicBootPrompt();
241} else {
242printf("Press Enter to start up the foreign OS. ");
243}
244}
245}
246
247//==========================================================================
248
249static void updateBootArgs( int key )
250{
251 key = ASCII_KEY(key);
252
253 switch ( key )
254 {
255 case KEY_BKSP:
256 if ( gBootArgsPtr > gBootArgs )
257 {
258 *--gBootArgsPtr = '\0';
259
260 int x, y, t;
261 getCursorPositionAndType( &x, &y, &t );
262 if ( x == 0 && y )
263 {
264 x = 80; y--;
265 }
266 if (x) {
267x--;
268}
269
270if( bootArgs->Video.v_display == VGA_TEXT_MODE )
271{
272setCursorPosition( x, y, 0 );
273putca(' ', 0x07, 1);
274}
275 else
276 {
277 updateGraphicBootPrompt();
278 }
279 }
280break;
281
282 default:
283 if ( key >= ' ' && gBootArgsPtr < gBootArgsEnd)
284 {
285 *gBootArgsPtr++ = key;
286
287 if( bootArgs->Video.v_display != VGA_TEXT_MODE ) updateGraphicBootPrompt();
288 else if ( key >= ' ' && key < 0x7f) putchar(key);
289}
290
291break;
292 }
293}
294
295//==========================================================================
296
297static const MenuItem * gMenuItems = NULL;
298
299static int gMenuItemCount;
300static int gMenuRow;
301static int gMenuHeight;
302static int gMenuTop;
303static int gMenuBottom;
304static int gMenuSelection;
305
306static int gMenuStart;
307static int gMenuEnd;
308
309static void printMenuItem( const MenuItem * item, int highlight )
310{
311 printf(" ");
312
313 if ( highlight )
314 putca(' ', 0x70, strlen(item->name) + 4);
315 else
316 putca(' ', 0x07, 40);
317
318 printf(" %40s\n", item->name);
319}
320
321//==========================================================================
322
323static void showMenu( const MenuItem * items, int count,
324 int selection, int row, int height )
325{
326 int i;
327 CursorState cursorState;
328
329 if ( items == NULL || count == 0 )
330return;
331
332 // head and tail points to the start and the end of the list.
333 // top and bottom points to the first and last visible items
334 // in the menu window.
335
336 gMenuItems= items;
337 gMenuRow= row;
338 gMenuHeight= height;
339 gMenuItemCount= count;
340 gMenuTop= 0;
341 gMenuBottom= MIN( count, height ) - 1;
342 gMenuSelection= selection;
343
344 gMenuStart= 0;
345 gMenuEnd = MIN( count, gui.maxdevices ) - 1;
346
347// If the selected item is not visible, shift the list down.
348
349 if ( gMenuSelection > gMenuBottom )
350 {
351 gMenuTop += ( gMenuSelection - gMenuBottom );
352 gMenuBottom = gMenuSelection;
353 }
354
355if ( gMenuSelection > gMenuEnd )
356 {
357gMenuStart += ( gMenuSelection - gMenuEnd );
358 gMenuEnd = gMenuSelection;
359 }
360
361// Draw the visible items.
362
363if( bootArgs->Video.v_display != VGA_TEXT_MODE )
364
365drawDeviceList(gMenuStart, gMenuEnd, gMenuSelection);
366
367else {
368
369changeCursor( 0, row, kCursorTypeHidden, &cursorState );
370
371for ( i = gMenuTop; i <= gMenuBottom; i++ )
372{
373printMenuItem( &items[i], (i == gMenuSelection) );
374}
375
376restoreCursor( &cursorState );
377 }
378}
379
380//==========================================================================
381
382static int updateMenu( int key, void ** paramPtr )
383{
384 int moved = 0;
385
386 union {
387 struct {
388 unsigned int
389 selectionUp : 1,
390 selectionDown : 1,
391 scrollUp : 1,
392 scrollDown : 1;
393 } f;
394 unsigned int w;
395 } draw = {{0}};
396
397 if ( gMenuItems == NULL )
398return 0;
399
400if( bootArgs->Video.v_display != VGA_TEXT_MODE )
401{
402int res;
403
404// set navigation keys for horizontal layout as defaults
405int previous= 0x4B00;// left arrow
406int subsequent= 0x4D00;// right arrow
407int menu= 0x5000;// down arrow
408
409if ( gui.layout == VerticalLayout )
410{
411// set navigation keys for vertical layout
412previous= 0x4800;// up arrow
413subsequent= 0x5000;// down arrow
414menu= 0x4B00;// right arrow
415}
416
417if ( key == previous )
418{
419if ( gMenuSelection > gMenuTop )
420draw.f.selectionUp = 1;
421else if ( gMenuTop > 0 )
422draw.f.scrollDown = 1;
423
424}
425
426else if ( key == subsequent )
427{
428if ( gMenuSelection != gMenuBottom)
429draw.f.selectionDown = 1;
430else if ( gMenuBottom < ( gMenuItemCount - 1 ) )
431draw.f.scrollUp = 1;
432}
433
434else if ( key == menu )
435{
436if ( gui.menu.draw )
437updateInfoMenu(key);
438else
439drawInfoMenu();
440}
441
442else if ( gui.menu.draw )
443{
444res = updateInfoMenu(key);
445
446if ( res == CLOSE_INFO_MENU )
447gui.menu.draw = false;
448else
449{
450shouldboot = ( res != DO_NOT_BOOT );
451
452if ( shouldboot )
453gui.menu.draw = false;
454
455switch (res)
456{
457case BOOT_NORMAL:
458gVerboseMode = false;
459gBootMode = kBootModeNormal;
460break;
461
462case BOOT_VERBOSE:
463gVerboseMode = true;
464gBootMode = kBootModeNormal;
465addBootArg(kVerboseModeFlag);
466break;
467
468case BOOT_IGNORECACHE:
469gVerboseMode = false;
470gBootMode = kBootModeNormal;
471addBootArg(kIgnoreCachesFlag);
472break;
473
474case BOOT_SINGLEUSER:
475gVerboseMode = true;
476gBootMode = kBootModeNormal;
477addBootArg(kSingleUserModeFlag);
478break;
479}
480
481}
482
483}
484
485} else {
486switch ( key )
487{
488 case 0x4800: // Up Arrow
489if ( gMenuSelection != gMenuTop )
490draw.f.selectionUp = 1;
491else if ( gMenuTop > 0 )
492draw.f.scrollDown = 1;
493break;
494
495case 0x5000: // Down Arrow
496if ( gMenuSelection != gMenuBottom )
497draw.f.selectionDown = 1;
498else if ( gMenuBottom < (gMenuItemCount - 1) )
499draw.f.scrollUp = 1;
500break;
501}
502}
503
504 if ( draw.w )
505 {
506 if ( draw.f.scrollUp )
507 {
508 scollPage(0, gMenuRow, 40, gMenuRow + gMenuHeight - 1, 0x07, 1, 1);
509 gMenuTop++; gMenuBottom++;
510gMenuStart++; gMenuEnd++;
511 draw.f.selectionDown = 1;
512 }
513
514 if ( draw.f.scrollDown )
515 {
516 scollPage(0, gMenuRow, 40, gMenuRow + gMenuHeight - 1, 0x07, 1, -1);
517 gMenuTop--; gMenuBottom--;
518 gMenuStart--; gMenuEnd--;
519 draw.f.selectionUp = 1;
520 }
521
522 if ( draw.f.selectionUp || draw.f.selectionDown )
523 {
524
525CursorState cursorState;
526
527// Set cursor at current position, and clear inverse video.
528
529if( bootArgs->Video.v_display == VGA_TEXT_MODE )
530{
531changeCursor( 0, gMenuRow + gMenuSelection - gMenuTop, kCursorTypeHidden, &cursorState );
532printMenuItem( &gMenuItems[gMenuSelection], 0 );
533}
534
535if ( draw.f.selectionUp )
536{
537gMenuSelection--;
538if(( gMenuSelection - gMenuStart) == -1 )
539{
540gMenuStart--;
541gMenuEnd--;
542}
543
544} else {
545gMenuSelection++;
546if(( gMenuSelection - ( gui.maxdevices - 1) - gMenuStart) > 0 )
547{
548gMenuStart++;
549gMenuEnd++;
550}
551 }
552
553if( bootArgs->Video.v_display == VGA_TEXT_MODE )
554 {
555moveCursor( 0, gMenuRow + gMenuSelection - gMenuTop );
556printMenuItem( &gMenuItems[gMenuSelection], 1 );
557restoreCursor( &cursorState );
558
559 } else
560
561drawDeviceList (gMenuStart, gMenuEnd, gMenuSelection);
562
563}
564
565 *paramPtr = gMenuItems[gMenuSelection].param;
566 moved = 1;
567 }
568
569return moved;
570}
571
572//==========================================================================
573
574static void skipblanks( const char ** cpp )
575{
576 while ( **(cpp) == ' ' || **(cpp) == '\t' ) ++(*cpp);
577}
578
579//==========================================================================
580
581static const char * extractKernelName( char ** cpp )
582{
583 char * kn = *cpp;
584 char * cp = *cpp;
585 char c;
586
587 // Convert char to lower case.
588
589 c = *cp | 0x20;
590
591 // Must start with a letter or a '/'.
592
593 if ( (c < 'a' || c > 'z') && ( c != '/' ) )
594 return 0;
595
596 // Keep consuming characters until we hit a separator.
597
598 while ( *cp && (*cp != '=') && (*cp != ' ') && (*cp != '\t') )
599 cp++;
600
601 // Only SPACE or TAB separator is accepted.
602 // Reject everything else.
603
604 if (*cp == '=')
605 return 0;
606
607 // Overwrite the separator, and move the pointer past
608 // the kernel name.
609
610 if (*cp != '\0') *cp++ = '\0';
611 *cpp = cp;
612
613 return kn;
614}
615
616//==========================================================================
617
618static void printMemoryInfo(void)
619{
620 int line;
621 int i;
622 MemoryRange *mp = bootInfo->memoryMap;
623
624 // Activate and clear page 1
625 setActiveDisplayPage(1);
626 clearScreenRows(0, 24);
627 setCursorPosition( 0, 0, 1 );
628
629 printf("BIOS reported memory ranges:\n");
630 line = 1;
631 for (i=0; i<bootInfo->memoryMapCount; i++) {
632 printf("Base 0x%08x%08x, ",
633 (unsigned long)(mp->base >> 32),
634 (unsigned long)(mp->base));
635 printf("length 0x%08x%08x, type %d\n",
636 (unsigned long)(mp->length >> 32),
637 (unsigned long)(mp->length),
638 mp->type);
639 if (line++ > 20) {
640 pause();
641 line = 0;
642 }
643 mp++;
644 }
645 if (line > 0) {
646 pause();
647 }
648
649 setActiveDisplayPage(0);
650}
651
652char *getMemoryInfoString()
653{
654int i, bufflen;
655MemoryRange *mp = bootInfo->memoryMap;
656char *buff = malloc(sizeof(char)*1024);
657if(!buff) {
658return 0;
659}
660
661static const char info[] = "BIOS reported memory ranges:\n";
662bufflen = sprintf(buff, "%s", info);
663
664for (i = 0;
665(i < bootInfo->memoryMapCount) && (bufflen < 1024); /* prevent buffer overflow */
666i++) {
667bufflen += snprintf(buff+bufflen, 1024-bufflen, "Base 0x%08x%08x, ",
668 (unsigned long)(mp->base >> 32),
669 (unsigned long)(mp->base));
670bufflen += snprintf(buff+bufflen, 1024-bufflen, "length 0x%08x%08x, type %d\n",
671 (unsigned long)(mp->length >> 32),
672 (unsigned long)(mp->length),
673 mp->type);
674mp++;
675}
676return buff;
677}
678
679//==========================================================================
680
681void lspci(void)
682{
683if (bootArgs->Video.v_display == VGA_TEXT_MODE) {
684setActiveDisplayPage(1);
685clearScreenRows(0, 24);
686setCursorPosition(0, 0, 1);
687}
688
689dump_pci_dt(root_pci_dev->children);
690
691pause();
692
693if (bootArgs->Video.v_display == VGA_TEXT_MODE) {
694setActiveDisplayPage(0);
695}
696}
697
698//==========================================================================
699
700int getBootOptions(bool firstRun)
701{
702int i;
703int key;
704int nextRow;
705int timeout;
706int bvCount;
707BVRef bvr;
708BVRef menuBVR;
709bool showPrompt, newShowPrompt, isCDROM;
710
711// Initialize default menu selection entry.
712gBootVolume = menuBVR = selectBootVolume(bvChain);
713
714if (biosDevIsCDROM(gBIOSDev)) {
715isCDROM = true;
716} else {
717isCDROM = false;
718}
719
720// ensure we're in graphics mode if gui is setup
721if (firstRun && gui.initialised && bootArgs->Video.v_display == VGA_TEXT_MODE)
722{
723setVideoMode(GRAPHICS_MODE, 0);
724}
725
726// Clear command line boot arguments
727clearBootArgs();
728
729// Allow user to override default timeout.
730if (multiboot_timeout_set) {
731timeout = multiboot_timeout;
732} else if (!getIntForKey(kTimeoutKey, &timeout, &bootInfo->chameleonConfig)) {
733/* If there is no timeout key in the file use the default timeout
734 which is different for CDs vs. hard disks. However, if not booting
735 a CD and no config file could be loaded set the timeout
736 to zero which causes the menu to display immediately.
737 This way, if no partitions can be found, that is the disk is unpartitioned
738 or simply cannot be read) then an empty menu is displayed.
739 If some partitions are found, for example a Windows partition, then
740 these will be displayed in the menu as foreign partitions.
741 */
742if (isCDROM) {
743timeout = kCDBootTimeout;
744} else {
745timeout = sysConfigValid ? kBootTimeout : 0;
746}
747}
748
749if (timeout < 0) {
750gBootMode |= kBootModeQuiet;
751}
752
753// If the user is holding down a modifier key, enter safe mode.
754if ((readKeyboardShiftFlags() & 0x0F) != 0) {
755gBootMode |= kBootModeSafe;
756}
757
758// Checking user pressed keys
759bool f8press = false, spress = false, vpress = false;
760while (readKeyboardStatus()) {
761key = bgetc ();
762if (key == 0x4200) f8press = true;
763if ((key & 0xff) == 's' || (key & 0xff) == 'S') spress = true;
764if ((key & 0xff) == 'v' || (key & 0xff) == 'V') vpress = true;
765}
766// If user typed F8, abort quiet mode, and display the menu.
767if (f8press) {
768gBootMode &= ~kBootModeQuiet;
769timeout = 0;
770}
771// If user typed 'v' or 'V', boot in verbose mode.
772if ((gBootMode & kBootModeQuiet) && firstRun && vpress) {
773addBootArg(kVerboseModeFlag);
774}
775// If user typed 's' or 'S', boot in single user mode.
776if ((gBootMode & kBootModeQuiet) && firstRun && spress) {
777addBootArg(kSingleUserModeFlag);
778}
779
780if (bootArgs->Video.v_display == VGA_TEXT_MODE) {
781setCursorPosition(0, 0, 0);
782clearScreenRows(0, kScreenLastRow);
783if (!(gBootMode & kBootModeQuiet)) {
784// Display banner and show hardware info.
785printf(bootBanner, (bootInfo->convmem + bootInfo->extmem) / 1024);
786printf(getVBEInfoString());
787}
788changeCursor(0, kMenuTopRow, kCursorTypeUnderline, 0);
789verbose("Scanning device %x...", gBIOSDev);
790}
791
792// When booting from CD, default to hard drive boot when possible.
793if (isCDROM && firstRun) {
794const char *val;
795char *prompt = NULL;
796char *name = NULL;
797int cnt;
798int optionKey;
799
800if (getValueForKey(kCDROMPromptKey, &val, &cnt, &bootInfo->chameleonConfig)) {
801prompt = malloc(cnt + 1);
802strncat(prompt, val, cnt);
803} else {
804name = malloc(80);
805getBootVolumeDescription(gBootVolume, name, 79, false);
806prompt = malloc(256);
807sprintf(prompt, "Press any key to start up from %s, or press F8 to enter startup options.", name);
808free(name);
809}
810
811if (getIntForKey( kCDROMOptionKey, &optionKey, &bootInfo->chameleonConfig )) {
812// The key specified is a special key.
813} else {
814// Default to F8.
815optionKey = 0x4200;
816}
817
818// If the timeout is zero then it must have been set above due to the
819// early catch of F8 which means the user wants to set boot options
820// which we ought to interpret as meaning he wants to boot the CD.
821if (timeout != 0) {
822key = countdown(prompt, kMenuTopRow, timeout);
823} else {
824key = optionKey;
825}
826
827if (prompt != NULL) {
828free(prompt);
829}
830
831clearScreenRows( kMenuTopRow, kMenuTopRow + 2 );
832
833// Hit the option key ?
834if (key == optionKey) {
835gBootMode &= ~kBootModeQuiet;
836timeout = 0;
837} else {
838key = key & 0xFF;
839
840// Try booting hard disk if user pressed 'h'
841if (biosDevIsCDROM(gBIOSDev) && key == 'h') {
842BVRef bvr;
843
844// Look at partitions hosting OS X other than the CD-ROM
845for (bvr = bvChain; bvr; bvr=bvr->next) {
846if ((bvr->flags & kBVFlagSystemVolume) && bvr->biosdev != gBIOSDev) {
847gBootVolume = bvr;
848}
849}
850}
851goto done;
852}
853}
854
855if (gBootMode & kBootModeQuiet) {
856// No input allowed from user.
857goto done;
858}
859
860if (firstRun && timeout > 0 && countdown("Press any key to enter startup options.", kMenuTopRow, timeout) == 0) {
861// If the user is holding down a modifier key,
862// enter safe mode.
863if ((readKeyboardShiftFlags() & 0x0F) != 0) {
864gBootMode |= kBootModeSafe;
865}
866goto done;
867}
868
869if (gDeviceCount > 0) {
870// Allocate memory for an array of menu items.
871menuItems = malloc(sizeof(MenuItem) * gDeviceCount);
872if (menuItems == NULL) {
873goto done;
874}
875
876// Associate a menu item for each BVRef.
877for (bvr=bvChain, i=gDeviceCount-1, selectIndex=-1; bvr; bvr=bvr->next) {
878if (bvr->visible) {
879getBootVolumeDescription(bvr, menuItems[i].name, sizeof(menuItems[i].name) - 1, true);
880menuItems[i].param = (void *) bvr;
881if (bvr == menuBVR) {
882selectIndex = i;
883}
884i--;
885}
886}
887// Jief : In case the default partition (returned by selectBootVolume) is not in the menu
888if ( selectIndex == -1 )
889{
890selectIndex = 0;
891
892// gDeviceCount is actually > 0, so menuItems[selectIndex] exists
893menuBVR = (BVRef)(menuItems[selectIndex].param);
894// what happen is bvChain is empty ?
895}
896}
897
898if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
899// redraw the background buffer
900gui.logo.draw = true;
901drawBackground();
902gui.devicelist.draw = true;
903gui.redraw = true;
904if (!(gBootMode & kBootModeQuiet)) {
905
906// Check if "Boot Banner"=N switch is present in config file.
907getBoolForKey(kBootBannerKey, &showBootBanner, &bootInfo->chameleonConfig);
908if (showBootBanner) {
909// Display banner and show hardware info.
910gprintf(&gui.screen, bootBanner + 1, (bootInfo->convmem + bootInfo->extmem) / 1024);
911}
912
913// redraw background
914memcpy(gui.backbuffer->pixels, gui.screen.pixmap->pixels, gui.backbuffer->width * gui.backbuffer->height * 4);
915}
916} else {
917// Clear screen and hide the blinking cursor.
918clearScreenRows(kMenuTopRow, kMenuTopRow + 2);
919changeCursor(0, kMenuTopRow, kCursorTypeHidden, 0);
920}
921
922nextRow = kMenuTopRow;
923showPrompt = true;
924
925if (gDeviceCount) {
926if( bootArgs->Video.v_display == VGA_TEXT_MODE ) {
927printf("Use \30\31 keys to select the startup volume.");
928}
929showMenu( menuItems, gDeviceCount, selectIndex, kMenuTopRow + 2, kMenuMaxItems );
930nextRow += MIN( gDeviceCount, kMenuMaxItems ) + 3;
931}
932
933// Show the boot prompt.
934showPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot);
935showBootPrompt( nextRow, showPrompt );
936
937do {
938if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
939// redraw background
940memcpy( gui.backbuffer->pixels, gui.screen.pixmap->pixels, gui.backbuffer->width * gui.backbuffer->height * 4 );
941// reset cursor co-ords
942gui.debug.cursor = pos( gui.screen.width - 160 , 10 );
943}
944key = getchar();
945updateMenu( key, (void **) &menuBVR );
946newShowPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot);
947
948if (newShowPrompt != showPrompt) {
949showPrompt = newShowPrompt;
950showBootPrompt( nextRow, showPrompt );
951}
952
953if (showPrompt) {
954updateBootArgs(key);
955}
956
957switch (key) {
958case KEY_ENTER:
959if (gui.menu.draw) {
960key=0;
961break;
962}
963if (*gBootArgs == '?') {
964char * argPtr = gBootArgs;
965
966// Skip the leading "?" character.
967argPtr++;
968getNextArg(&argPtr, booterCommand);
969getNextArg(&argPtr, booterParam);
970
971/*
972* TODO: this needs to be refactored.
973*/
974if (strcmp( booterCommand, "video" ) == 0) {
975if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
976showInfoBox(getVBEInfoString(), getVBEModeInfoString());
977} else {
978printVBEModeInfo();
979}
980} else if ( strcmp( booterCommand, "memory" ) == 0) {
981if (bootArgs->Video.v_display != VGA_TEXT_MODE ) {
982showInfoBox("Memory Map", getMemoryInfoString());
983} else {
984printMemoryInfo();
985}
986} else if (strcmp(booterCommand, "lspci") == 0) {
987lspci();
988} else if (strcmp(booterCommand, "log") == 0) {
989 showTextBuffer(msgbuf, strlen(msgbuf));
990} else if (strcmp(booterCommand, "more") == 0) {
991showTextFile(booterParam);
992} else if (strcmp(booterCommand, "rd") == 0) {
993processRAMDiskCommand(&argPtr, booterParam);
994} else if (strcmp(booterCommand, "norescan") == 0) {
995if (gEnableCDROMRescan) {
996gEnableCDROMRescan = false;
997break;
998}
999} else {
1000showHelp();
1001}
1002key = 0;
1003showBootPrompt(nextRow, showPrompt);
1004break;
1005}
1006gBootVolume = menuBVR;
1007setRootVolume(menuBVR);
1008gBIOSDev = menuBVR->biosdev;
1009break;
1010
1011case KEY_ESC:
1012clearBootArgs();
1013break;
1014
1015case KEY_F5:
1016// New behavior:
1017// Clear gBootVolume to restart the loop
1018// if the user enabled rescanning the optical drive.
1019// Otherwise boot the default boot volume.
1020if (gEnableCDROMRescan) {
1021gBootVolume = NULL;
1022clearBootArgs();
1023}
1024break;
1025
1026case KEY_F10:
1027gScanSingleDrive = false;
1028scanDisks(gBIOSDev, &bvCount);
1029gBootVolume = NULL;
1030clearBootArgs();
1031break;
1032
1033case KEY_TAB:
1034// New behavior:
1035// Switch between text & graphic interfaces
1036// Only Permitted if started in graphics interface
1037if (useGUI) {
1038if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
1039setVideoMode(VGA_TEXT_MODE, 0);
1040
1041setCursorPosition(0, 0, 0);
1042clearScreenRows(0, kScreenLastRow);
1043
1044// Display banner and show hardware info.
1045printf(bootBanner, (bootInfo->convmem + bootInfo->extmem) / 1024);
1046printf(getVBEInfoString());
1047
1048clearScreenRows(kMenuTopRow, kMenuTopRow + 2);
1049changeCursor(0, kMenuTopRow, kCursorTypeHidden, 0);
1050
1051nextRow = kMenuTopRow;
1052showPrompt = true;
1053
1054if (gDeviceCount) {
1055printf("Use \30\31 keys to select the startup volume.");
1056showMenu(menuItems, gDeviceCount, selectIndex, kMenuTopRow + 2, kMenuMaxItems);
1057nextRow += MIN(gDeviceCount, kMenuMaxItems) + 3;
1058}
1059
1060showPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot);
1061showBootPrompt(nextRow, showPrompt);
1062//changeCursor( 0, kMenuTopRow, kCursorTypeUnderline, 0 );
1063} else {
1064gui.redraw = true;
1065setVideoMode(GRAPHICS_MODE, 0);
1066updateVRAM();
1067 updateGraphicBootPrompt();
1068}
1069}
1070key = 0;
1071break;
1072
1073default:
1074key = 0;
1075break;
1076}
1077} while (0 == key);
1078
1079done:
1080if (bootArgs->Video.v_display == VGA_TEXT_MODE) {
1081clearScreenRows(kMenuTopRow, kScreenLastRow);
1082changeCursor(0, kMenuTopRow, kCursorTypeUnderline, 0);
1083}
1084shouldboot = false;
1085gui.menu.draw = false;
1086if (menuItems) {
1087free(menuItems);
1088menuItems = NULL;
1089}
1090execute_hook("BootOptions", gBootArgs, gBootArgsPtr, NULL, NULL);
1091return 0;
1092}
1093
1094//==========================================================================
1095
1096char gBootUUIDString[32+4+1] = ""; // UUID of the boot volume e.g. 5EB1869F-C4FA-3502-BDEB-3B8ED5D87292
1097extern unsigned char chainbootdev;
1098extern unsigned char chainbootflag;
1099
1100bool copyArgument(const char *argName, const char *val, int cnt, char **argP, int *cntRemainingP)
1101{
1102int argLen = argName ? strlen(argName) : 0;
1103int len = argLen + cnt + 1; // + 1 to account for space.
1104
1105if (argName)
1106{
1107len++; // +1 to account for '='
1108}
1109
1110if (len > *cntRemainingP) {
1111error("Warning: boot arguments too long, truncating\n");
1112return false;
1113}
1114
1115if (argName)
1116{
1117strncpy(*argP, argName, argLen);
1118*argP += argLen;
1119*argP[0] = '=';
1120(*argP)++;
1121}
1122
1123strncpy(*argP, val, cnt);
1124*argP += cnt;
1125*argP[0] = ' ';
1126(*argP)++;
1127*cntRemainingP -= len;
1128
1129return true;
1130}
1131
1132//
1133// Returns TRUE if an argument was copied, FALSE otherwise
1134bool
1135processBootArgument(
1136 const char *argName, // The argument to search for
1137 const char *userString, // Typed-in boot arguments
1138 const char *kernelFlags, // Kernel flags from config table
1139 const char *configTable,
1140 char **argP, // Output value
1141 int *cntRemainingP, // Output count
1142 char *foundVal, // found value
1143 int foundValSize // max found value size
1144 ) {
1145const char *val;
1146int cnt;
1147bool found = false;
1148
1149if (getValueForBootKey(userString, argName, &val, &cnt)) {
1150// Don't copy; these values will be copied at the end of argument processing.
1151found = true;
1152} else if (getValueForBootKey(kernelFlags, argName, &val, &cnt)) {
1153// Don't copy; these values will be copied at the end of argument processing.
1154found = true;
1155} else if (getValueForKey(argName, &val, &cnt, &bootInfo->chameleonConfig)) {
1156copyArgument(argName, val, cnt, argP, cntRemainingP);
1157found = true;
1158}
1159if (found && foundVal) {
1160strlcpy(foundVal, val, foundValSize);
1161}
1162return found;
1163}
1164
1165// Maximum config table value size
1166#define VALUE_SIZE 2048
1167
1168int
1169processBootOptions()
1170{
1171const char *cp = gBootArgs;
1172const char *val = 0;
1173const char *kernel;
1174int cnt;
1175int userCnt;
1176int cntRemaining;
1177char *argP;
1178char *configKernelFlags;
1179char *valueBuffer;
1180
1181valueBuffer = malloc(VALUE_SIZE);
1182
1183skipblanks( &cp );
1184
1185// Update the unit and partition number.
1186
1187if ( gBootVolume ) {
1188if (!( gBootVolume->flags & kBVFlagNativeBoot )) {
1189readBootSector( gBootVolume->biosdev, gBootVolume->part_boff, (void *) 0x7c00 );
1190//
1191// Setup edx, and signal intention to chain load the
1192// foreign booter.
1193//
1194
1195chainbootdev = gBootVolume->biosdev;
1196chainbootflag = 1;
1197
1198return 1;
1199}
1200
1201setRootVolume(gBootVolume);
1202
1203}
1204// If no boot volume fail immediately because we're just going to fail
1205// trying to load the config file anyway.
1206else {
1207return -1;
1208}
1209
1210// Find out which version mac os we're booting.
1211strncpy(gMacOSVersion, gBootVolume->OSVersion, sizeof(gMacOSVersion));
1212
1213// Load config table specified by the user, or use the default.
1214
1215if (!getValueForBootKey(cp, "config", &val, &cnt)) {
1216val = 0;
1217cnt = 0;
1218}
1219
1220// Load com.apple.Boot.plist from the selected volume
1221// and use its contents to override default bootConfig.
1222
1223loadSystemConfig(&bootInfo->bootConfig);
1224loadChameleonConfig(&bootInfo->chameleonConfig, NULL);
1225
1226// Use the kernel name specified by the user, or fetch the name
1227// in the config table, or use the default if not specified.
1228// Specifying a kernel name on the command line, or specifying
1229// a non-default kernel name in the config file counts as
1230// overriding the kernel, which causes the kernelcache not
1231// to be used.
1232
1233gOverrideKernel = false;
1234if (( kernel = extractKernelName((char **)&cp) ))
1235{
1236strlcpy( bootInfo->bootFile, kernel, sizeof(bootInfo->bootFile) );
1237}
1238else
1239{
1240if ( getValueForKey( kKernelNameKey, &val, &cnt, &bootInfo->bootConfig ) )
1241{
1242strlcpy( bootInfo->bootFile, val, cnt+1 );
1243}
1244else
1245{
1246if( YOSEMITE ) // is 10.10
1247{
1248
1249strlcpy( bootInfo->bootFile, kOSXKernel, sizeof(bootInfo->bootFile) );
1250//printf(HEADER "/System/Library/Kernels/%s\n", bootInfo->bootFile);
1251}
1252else
1253{ // OSX is not 10.10
1254
1255strlcpy( bootInfo->bootFile, kDefaultKernel, sizeof(bootInfo->bootFile) );
1256//printf(HEADER "/%s\n", bootInfo->bootFile);
1257}
1258}
1259}
1260
1261if (!YOSEMITE) // not 10.10 so 10.9 and previus
1262{
1263if (strcmp( bootInfo->bootFile, kDefaultKernel ) != 0)
1264{
1265 //printf(HEADER "org.chameleon.Boot.plist found path for custom '%s' found!\n", bootInfo->bootFile);
1266gOverrideKernel = true;
1267}
1268}
1269else
1270{ // OSX is 10.10
1271if (strcmp( bootInfo->bootFile, kOSXKernel ) != 0)
1272{
1273 //printf(HEADER "org.chameleon.Boot.plist found path for custom '%s' found!\n", bootInfo->bootFile);
1274gOverrideKernel = true;
1275}
1276}
1277
1278// Ermac : Inject "kext-dev-mode=1" if OS X 10.10 is detected
1279if( YOSEMITE ) // is 10.10
1280{
1281addBootArg("kext-dev-mode=1");
1282}
1283
1284cntRemaining = BOOT_STRING_LEN - 2; // save 1 for NULL, 1 for space
1285argP = bootArgs->CommandLine;
1286
1287// Get config kernel flags, if not ignored.
1288if (getValueForBootKey(cp, kIgnoreBootFileFlag, &val, &cnt) ||
1289 !getValueForKey( kKernelFlagsKey, &val, &cnt, &bootInfo->bootConfig ))
1290{
1291val = "";
1292cnt = 0;
1293}
1294configKernelFlags = malloc(cnt + 1);
1295strlcpy(configKernelFlags, val, cnt + 1);
1296
1297// boot-uuid can be set either on the command-line or in the config file
1298if (!processBootArgument(kBootUUIDKey, cp, configKernelFlags, bootInfo->config,
1299 &argP, &cntRemaining, gBootUUIDString, sizeof(gBootUUIDString)))
1300{
1301//
1302// Try an alternate method for getting the root UUID on boot helper partitions.
1303//
1304if (gBootVolume->flags & kBVFlagBooter)
1305{
1306// Load the configuration store in the boot helper partition
1307if (loadHelperConfig(&bootInfo->helperConfig) == 0)
1308{
1309val = getStringForKey(kHelperRootUUIDKey, &bootInfo->helperConfig);
1310if (val != NULL)
1311{
1312strlcpy(gBootUUIDString, val, sizeof(gBootUUIDString));
1313}
1314}
1315}
1316/*
1317// Try to get the volume uuid string
1318if (!strlen(gBootUUIDString) && gBootVolume->fs_getuuid)
1319{
1320gBootVolume->fs_getuuid(gBootVolume, gBootUUIDString);
1321}
1322*/
1323// If we have the volume uuid add it to the commandline arguments
1324if (strlen(gBootUUIDString))
1325{
1326copyArgument(kBootUUIDKey, gBootUUIDString, strlen(gBootUUIDString), &argP, &cntRemaining);
1327}
1328// Try to get the volume uuid string
1329if (!strlen(gBootUUIDString) && gBootVolume->fs_getuuid)
1330{
1331gBootVolume->fs_getuuid(gBootVolume, gBootUUIDString);
1332DBG("boot-uuid: %s\n", gBootUUIDString);
1333}
1334}
1335
1336if (!processBootArgument(kRootDeviceKey, cp, configKernelFlags, bootInfo->config,
1337 &argP, &cntRemaining, gRootDevice, ROOT_DEVICE_SIZE))
1338{
1339cnt = 0;
1340if ( getValueForKey( kBootDeviceKey, &val, &cnt, &bootInfo->chameleonConfig))
1341{
1342valueBuffer[0] = '*';
1343cnt++;
1344strlcpy(valueBuffer + 1, val, cnt);
1345val = valueBuffer;
1346}
1347else
1348{ /*
1349if (strlen(gBootUUIDString))
1350{
1351val = "*uuid";
1352cnt = 5;
1353}
1354else
1355{ */
1356// Don't set "rd=.." if there is no boot device key
1357// and no UUID.
1358val = "";
1359cnt = 0;
1360/* } */
1361}
1362
1363if (cnt > 0)
1364{
1365copyArgument( kRootDeviceKey, val, cnt, &argP, &cntRemaining);
1366}
1367strlcpy( gRootDevice, val, (cnt + 1));
1368}
1369
1370/*
1371 * Removed. We don't need this anymore.
1372 *
1373if (!processBootArgument(kPlatformKey, cp, configKernelFlags, bootInfo->config,
1374 &argP, &cntRemaining, gPlatformName, sizeof(gCacheNameAdler)))
1375{
1376getPlatformName(gPlatformName);
1377copyArgument(kPlatformKey, gPlatformName, strlen(gPlatformName), &argP, &cntRemaining);
1378}
1379*/
1380
1381if (!getValueForBootKey(cp, kSafeModeFlag, &val, &cnt) &&
1382 !getValueForBootKey(configKernelFlags, kSafeModeFlag, &val, &cnt))
1383{
1384if (gBootMode & kBootModeSafe)
1385{
1386copyArgument(0, kSafeModeFlag, strlen(kSafeModeFlag), &argP, &cntRemaining);
1387}
1388}
1389
1390// Store the merged kernel flags and boot args.
1391
1392cnt = strlen(configKernelFlags);
1393if (cnt)
1394{
1395if (cnt > cntRemaining)
1396{
1397error("Warning: boot arguments too long, truncating\n");
1398cnt = cntRemaining;
1399}
1400strncpy(argP, configKernelFlags, cnt);
1401argP[cnt++] = ' ';
1402cntRemaining -= cnt;
1403}
1404userCnt = strlen(cp);
1405if (userCnt > cntRemaining)
1406{
1407error("Warning: boot arguments too long, truncating\n");
1408userCnt = cntRemaining;
1409}
1410strncpy(&argP[cnt], cp, userCnt);
1411argP[cnt+userCnt] = '\0';
1412
1413if(!shouldboot)
1414{
1415gVerboseMode = getValueForKey( kVerboseModeFlag, &val, &cnt, &bootInfo->chameleonConfig ) ||
1416getValueForKey( kSingleUserModeFlag, &val, &cnt, &bootInfo->chameleonConfig );
1417
1418gBootMode = ( getValueForKey( kSafeModeFlag, &val, &cnt, &bootInfo->chameleonConfig ) ) ?
1419kBootModeSafe : kBootModeNormal;
1420
1421if ( getValueForKey( kIgnoreCachesFlag, &val, &cnt, &bootInfo->chameleonConfig ) )
1422{
1423gBootMode = kBootModeSafe;
1424}
1425}
1426
1427if ( getValueForKey( kMKextCacheKey, &val, &cnt, &bootInfo->bootConfig ) )
1428{
1429strlcpy(gMKextName, val, cnt + 1);
1430}
1431else
1432{
1433gMKextName[0]=0;
1434}
1435
1436free(configKernelFlags);
1437free(valueBuffer);
1438
1439return 0;
1440}
1441
1442
1443//==========================================================================
1444// Load the help file and display the file contents on the screen.
1445
1446void showTextBuffer(char *buf_orig, int size)
1447{
1448char*bp;
1449char* buf;
1450intline;
1451intline_offset;
1452intc;
1453
1454if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
1455showInfoBox( "Press q to continue, space for next page.\n",buf_orig );
1456return;
1457}
1458
1459// Create a copy so that we don't mangle the original
1460buf = malloc(size + 1);
1461memcpy(buf, buf_orig, size);
1462
1463
1464 bp = buf;
1465 while (size-- > 0) {
1466if (*bp == '\n') {
1467*bp = '\0';
1468}
1469bp++;
1470 }
1471 *bp = '\1';
1472 line_offset = 0;
1473
1474 setActiveDisplayPage(1);
1475
1476 while (1) {
1477clearScreenRows(0, 24);
1478setCursorPosition(0, 0, 1);
1479bp = buf;
1480for (line = 0; *bp != '\1' && line < line_offset; line++) {
1481while (*bp != '\0') {
1482bp++;
1483}
1484bp++;
1485}
1486for (line = 0; *bp != '\1' && line < 23; line++) {
1487setCursorPosition(0, line, 1);
1488printf("%s\n", bp);
1489while (*bp != '\0') {
1490bp++;
1491}
1492bp++;
1493}
1494
1495setCursorPosition(0, 23, 1);
1496if (*bp == '\1') {
1497printf("[Type %sq or space to quit viewer]", (line_offset > 0) ? "p for previous page, " : "");
1498} else {
1499printf("[Type %s%sq to quit viewer]", (line_offset > 0) ? "p for previous page, " : "", (*bp != '\1') ? "space for next page, " : "");
1500}
1501
1502c = getchar();
1503if (c == 'q' || c == 'Q') {
1504break;
1505}
1506if ((c == 'p' || c == 'P') && line_offset > 0) {
1507line_offset -= 23;
1508}
1509if (c == ' ') {
1510if (*bp == '\1') {
1511break;
1512} else {
1513line_offset += 23;
1514}
1515}
1516 }
1517 setActiveDisplayPage(0);
1518}
1519
1520void showHelp(void)
1521{
1522if (bootArgs->Video.v_display != VGA_TEXT_MODE)
1523{
1524showInfoBox("Help. Press q to quit.\n", (char *)BootHelp_txt);
1525}
1526else
1527{
1528showTextBuffer((char *)BootHelp_txt, BootHelp_txt_len);
1529}
1530}
1531
1532void showTextFile(const char * filename)
1533{
1534#define MAX_TEXT_FILE_SIZE 65536
1535char*buf;
1536intfd;
1537intsize;
1538
1539if ((fd = open_bvdev("bt(0,0)", filename, 0)) < 0)
1540{
1541printf("\nFile not found: %s\n", filename);
1542sleep(2);
1543return;
1544}
1545
1546 size = file_size(fd);
1547 if (size > MAX_TEXT_FILE_SIZE)
1548{
1549size = MAX_TEXT_FILE_SIZE;
1550}
1551 buf = malloc(size);
1552 read(fd, buf, size);
1553 close(fd);
1554showTextBuffer(buf, size);
1555free(buf);
1556}
1557
1558// This is a very simplistic prompting scheme that just grabs two hex characters
1559// Eventually we need to do something more user-friendly like display a menu
1560// based off of the Multiboot device list
1561
1562int selectAlternateBootDevice(int bootdevice)
1563{
1564int key;
1565int newbootdevice;
1566int digitsI = 0;
1567char *end;
1568char digits[3] = {0,0,0};
1569
1570// We've already printed the current boot device so user knows what it is
1571printf("Typical boot devices are 80 (First HD), 81 (Second HD)\n");
1572printf("Enter two-digit hexadecimal boot device [%02x]: ", bootdevice);
1573do {
1574key = getchar();
1575switch (ASCII_KEY(key))
1576{
1577case KEY_BKSP:
1578if (digitsI > 0)
1579{
1580int x, y, t;
1581getCursorPositionAndType(&x, &y, &t);
1582// Assume x is not 0;
1583x--;
1584setCursorPosition(x,y,0); // back up one char
1585// Overwrite with space without moving cursor position
1586putca(' ', 0x07, 1);
1587digitsI--;
1588}
1589else
1590{
1591// TODO: Beep or something
1592}
1593break;
1594
1595case KEY_ENTER:
1596digits[digitsI] = '\0';
1597newbootdevice = strtol(digits, &end, 16);
1598if (end == digits && *end == '\0')
1599{
1600// User entered empty string
1601printf("\nUsing default boot device %x\n", bootdevice);
1602key = 0;
1603} else if(end != digits && *end == '\0') {
1604bootdevice = newbootdevice;
1605printf("\n");
1606key = 0; // We gots da boot device
1607} else {
1608printf("\nCouldn't parse. try again: ");
1609digitsI = 0;
1610}
1611break;
1612
1613default:
1614if (isxdigit(ASCII_KEY(key)) && digitsI < 2)
1615{
1616putchar(ASCII_KEY(key));
1617digits[digitsI++] = ASCII_KEY(key);
1618}
1619else
1620{
1621// TODO: Beep or something
1622}
1623break;
1624};
1625} while (key != 0);
1626
1627return bootdevice;
1628}
1629
1630bool promptForRescanOption(void)
1631{
1632printf("\nWould you like to enable media rescan option?\nPress ENTER to enable or any key to skip.\n");
1633if (getchar() == KEY_ENTER)
1634{
1635return true;
1636}
1637else
1638{
1639return false;
1640}
1641}
1642

Archive Download this file

Revision: 2501