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

Archive Download this file

Revision: 2525