Chameleon

Chameleon Svn Source Tree

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

Archive Download this file

Revision: 2539