]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/boot/forth/support.4th
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / sys / boot / forth / support.4th
1 \ Copyright (c) 1999 Daniel C. Sobral <dcs@freebsd.org>
2 \ All rights reserved.
3
4 \ Redistribution and use in source and binary forms, with or without
5 \ modification, are permitted provided that the following conditions
6 \ are met:
7 \ 1. Redistributions of source code must retain the above copyright
8 \    notice, this list of conditions and the following disclaimer.
9 \ 2. Redistributions in binary form must reproduce the above copyright
10 \    notice, this list of conditions and the following disclaimer in the
11 \    documentation and/or other materials provided with the distribution.
12 \
13 \ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 \ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 \ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 \ ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 \ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 \ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 \ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 \ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 \ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 \ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 \ SUCH DAMAGE.
24 \
25 \ $FreeBSD$
26
27 \ Loader.rc support functions:
28 \
29 \ initialize ( addr len -- )    as above, plus load_conf_files
30 \ load_conf ( addr len -- )     load conf file given
31 \ include_conf_files ( -- )     load all conf files in load_conf_files
32 \ print_syntax_error ( -- )     print line and marker of where a syntax
33 \                               error was detected
34 \ print_line ( -- )             print last line processed
35 \ load_kernel ( -- )            load kernel
36 \ load_modules ( -- )           load modules flagged
37 \
38 \ Exported structures:
39 \
40 \ string                        counted string structure
41 \       cell .addr                      string address
42 \       cell .len                       string length
43 \ module                        module loading information structure
44 \       cell module.flag                should we load it?
45 \       string module.name              module's name
46 \       string module.loadname          name to be used in loading the module
47 \       string module.type              module's type
48 \       string module.args              flags to be passed during load
49 \       string module.beforeload        command to be executed before load
50 \       string module.afterload         command to be executed after load
51 \       string module.loaderror         command to be executed if load fails
52 \       cell module.next                list chain
53 \
54 \ Exported global variables;
55 \
56 \ string conf_files             configuration files to be loaded
57 \ cell modules_options          pointer to first module information
58 \ value verbose?                indicates if user wants a verbose loading
59 \ value any_conf_read?          indicates if a conf file was succesfully read
60 \
61 \ Other exported words:
62 \    note, strlen is internal
63 \ strdup ( addr len -- addr' len)                       similar to strdup(3)
64 \ strcat ( addr len addr' len' -- addr len+len' )       similar to strcat(3)
65 \ s' ( | string' -- addr len | )                        similar to s"
66 \ rudimentary structure support
67
68 \ Exception values
69
70 1 constant ESYNTAX
71 2 constant ENOMEM
72 3 constant EFREE
73 4 constant ESETERROR    \ error setting environment variable
74 5 constant EREAD        \ error reading
75 6 constant EOPEN
76 7 constant EEXEC        \ XXX never catched
77 8 constant EBEFORELOAD
78 9 constant EAFTERLOAD
79
80 \ I/O constants
81
82 0 constant SEEK_SET
83 1 constant SEEK_CUR
84 2 constant SEEK_END
85
86 0 constant O_RDONLY
87 1 constant O_WRONLY
88 2 constant O_RDWR
89
90 \ Crude structure support
91
92 : structure:
93   create here 0 , ['] drop , 0
94   does> create here swap dup @ allot cell+ @ execute
95 ;
96 : member: create dup , over , + does> cell+ @ + ;
97 : ;structure swap ! ;
98 : constructor! >body cell+ ! ;
99 : constructor: over :noname ;
100 : ;constructor postpone ; swap cell+ ! ; immediate
101 : sizeof ' >body @ state @ if postpone literal then ; immediate
102 : offsetof ' >body cell+ @ state @ if postpone literal then ; immediate
103 : ptr 1 cells member: ;
104 : int 1 cells member: ;
105
106 \ String structure
107
108 structure: string
109         ptr .addr
110         int .len
111         constructor:
112           0 over .addr !
113           0 swap .len !
114         ;constructor
115 ;structure
116
117
118 \ Module options linked list
119
120 structure: module
121         int module.flag
122         sizeof string member: module.name
123         sizeof string member: module.loadname
124         sizeof string member: module.type
125         sizeof string member: module.args
126         sizeof string member: module.beforeload
127         sizeof string member: module.afterload
128         sizeof string member: module.loaderror
129         ptr module.next
130 ;structure
131
132 \ Internal loader structures (preloaded_file, kernel_module, file_metadata)
133 \ must be in sync with the C struct in sys/boot/common/bootstrap.h
134 structure: preloaded_file
135         ptr pf.name
136         ptr pf.type
137         ptr pf.args
138         ptr pf.metadata \ file_metadata
139         int pf.loader
140         int pf.addr
141         int pf.size
142         ptr pf.modules  \ kernel_module
143         ptr pf.next     \ preloaded_file
144 ;structure
145
146 structure: kernel_module
147         ptr km.name
148         \ ptr km.args
149         ptr km.fp       \ preloaded_file
150         ptr km.next     \ kernel_module
151 ;structure
152
153 structure: file_metadata
154         int             md.size
155         2 member:       md.type \ this is not ANS Forth compatible (XXX)
156         ptr             md.next \ file_metadata
157         0 member:       md.data \ variable size
158 ;structure
159
160 \ end of structures
161
162 \ Global variables
163
164 string conf_files
165 string nextboot_conf_file
166 create module_options sizeof module.next allot 0 module_options !
167 create last_module_option sizeof module.next allot 0 last_module_option !
168 0 value verbose?
169 0 value nextboot?
170
171 \ Support string functions
172 : strdup { addr len -- addr' len' }
173   len allocate if ENOMEM throw then
174   addr over len move len
175 ;
176
177 : strcat  { addr len addr' len' -- addr len+len' }
178   addr' addr len + len' move
179   addr len len' +
180 ;
181
182 : strchr { addr len c -- addr' len' }
183   begin
184     len
185   while
186     addr c@ c = if addr len exit then
187     addr 1 + to addr
188     len 1 - to len
189   repeat
190   0 0
191 ;
192
193 : s' \ same as s", allows " in the string
194   [char] ' parse
195   state @ if postpone sliteral then
196 ; immediate
197
198 : 2>r postpone >r postpone >r ; immediate
199 : 2r> postpone r> postpone r> ; immediate
200 : 2r@ postpone 2r> postpone 2dup postpone 2>r ; immediate
201
202 : getenv?  getenv -1 = if false else drop true then ;
203
204 \ Private definitions
205
206 vocabulary support-functions
207 only forth also support-functions definitions
208
209 \ Some control characters constants
210
211 7 constant bell
212 8 constant backspace
213 9 constant tab
214 10 constant lf
215 13 constant <cr>
216
217 \ Read buffer size
218
219 80 constant read_buffer_size
220
221 \ Standard suffixes
222
223 : load_module_suffix            s" _load" ;
224 : module_loadname_suffix        s" _name" ;
225 : module_type_suffix            s" _type" ;
226 : module_args_suffix            s" _flags" ;
227 : module_beforeload_suffix      s" _before" ;
228 : module_afterload_suffix       s" _after" ;
229 : module_loaderror_suffix       s" _error" ;
230
231 \ Support operators
232
233 : >= < 0= ;
234 : <= > 0= ;
235
236 \ Assorted support functions
237
238 : free-memory free if EFREE throw then ;
239
240 : strget { var -- addr len } var .addr @ var .len @ ;
241
242 \ assign addr len to variable.
243 : strset  { addr len var -- } addr var .addr !  len var .len !  ;
244
245 \ free memory and reset fields
246 : strfree { var -- } var .addr @ ?dup if free-memory 0 0 var strset then ;
247
248 \ free old content, make a copy of the string and assign to variable
249 : string= { addr len var -- } var strfree addr len strdup var strset ;
250
251 : strtype ( str -- ) strget type ;
252
253 \ assign a reference to what is on the stack
254 : strref { addr len var -- addr len }
255   addr var .addr ! len var .len ! addr len
256 ;
257
258 \ unquote a string
259 : unquote ( addr len -- addr len )
260   over c@ [char] " = if 2 chars - swap char+ swap then
261 ;
262
263 \ Assignment data temporary storage
264
265 string name_buffer
266 string value_buffer
267
268 \ Line by line file reading functions
269 \
270 \ exported:
271 \       line_buffer
272 \       end_of_file?
273 \       fd
274 \       read_line
275 \       reset_line_reading
276
277 vocabulary line-reading
278 also line-reading definitions also
279
280 \ File data temporary storage
281
282 string read_buffer
283 0 value read_buffer_ptr
284
285 \ File's line reading function
286
287 support-functions definitions
288
289 string line_buffer
290 0 value end_of_file?
291 variable fd
292
293 line-reading definitions
294
295 : skip_newlines
296   begin
297     read_buffer .len @ read_buffer_ptr >
298   while
299     read_buffer .addr @ read_buffer_ptr + c@ lf = if
300       read_buffer_ptr char+ to read_buffer_ptr
301     else
302       exit
303     then
304   repeat
305 ;
306
307 : scan_buffer  ( -- addr len )
308   read_buffer_ptr >r
309   begin
310     read_buffer .len @ r@ >
311   while
312     read_buffer .addr @ r@ + c@ lf = if
313       read_buffer .addr @ read_buffer_ptr +  ( -- addr )
314       r@ read_buffer_ptr -                   ( -- len )
315       r> to read_buffer_ptr
316       exit
317     then
318     r> char+ >r
319   repeat
320   read_buffer .addr @ read_buffer_ptr +  ( -- addr )
321   r@ read_buffer_ptr -                   ( -- len )
322   r> to read_buffer_ptr
323 ;
324
325 : line_buffer_resize  ( len -- len )
326   >r
327   line_buffer .len @ if
328     line_buffer .addr @
329     line_buffer .len @ r@ +
330     resize if ENOMEM throw then
331   else
332     r@ allocate if ENOMEM throw then
333   then
334   line_buffer .addr !
335   r>
336 ;
337     
338 : append_to_line_buffer  ( addr len -- )
339   line_buffer strget
340   2swap strcat
341   line_buffer .len !
342   drop
343 ;
344
345 : read_from_buffer
346   scan_buffer            ( -- addr len )
347   line_buffer_resize     ( len -- len )
348   append_to_line_buffer  ( addr len -- )
349 ;
350
351 : refill_required?
352   read_buffer .len @ read_buffer_ptr =
353   end_of_file? 0= and
354 ;
355
356 : refill_buffer
357   0 to read_buffer_ptr
358   read_buffer .addr @ 0= if
359     read_buffer_size allocate if ENOMEM throw then
360     read_buffer .addr !
361   then
362   fd @ read_buffer .addr @ read_buffer_size fread
363   dup -1 = if EREAD throw then
364   dup 0= if true to end_of_file? then
365   read_buffer .len !
366 ;
367
368 support-functions definitions
369
370 : reset_line_reading
371   0 to read_buffer_ptr
372 ;
373
374 : read_line
375   line_buffer strfree
376   skip_newlines
377   begin
378     read_from_buffer
379     refill_required?
380   while
381     refill_buffer
382   repeat
383 ;
384
385 only forth also support-functions definitions
386
387 \ Conf file line parser:
388 \ <line> ::= <spaces><name><spaces>'='<spaces><value><spaces>[<comment>] |
389 \            <spaces>[<comment>]
390 \ <name> ::= <letter>{<letter>|<digit>|'_'}
391 \ <value> ::= '"'{<character_set>|'\'<anything>}'"' | <name>
392 \ <character_set> ::= ASCII 32 to 126, except '\' and '"'
393 \ <comment> ::= '#'{<anything>}
394 \
395 \ exported:
396 \       line_pointer
397 \       process_conf
398
399 0 value line_pointer
400
401 vocabulary file-processing
402 also file-processing definitions
403
404 \ parser functions
405 \
406 \ exported:
407 \       get_assignment
408
409 vocabulary parser
410 also parser definitions also
411
412 0 value parsing_function
413 0 value end_of_line
414
415 : end_of_line?  line_pointer end_of_line = ;
416
417 \ classifiers for various character classes in the input line
418
419 : letter?
420   line_pointer c@ >r
421   r@ [char] A >=
422   r@ [char] Z <= and
423   r@ [char] a >=
424   r> [char] z <= and
425   or
426 ;
427
428 : digit?
429   line_pointer c@ >r
430   r@ [char] - =
431   r@ [char] 0 >=
432   r> [char] 9 <= and
433   or
434 ;
435
436 : quote?  line_pointer c@ [char] " = ;
437
438 : assignment_sign?  line_pointer c@ [char] = = ;
439
440 : comment?  line_pointer c@ [char] # = ;
441
442 : space?  line_pointer c@ bl = line_pointer c@ tab = or ;
443
444 : backslash?  line_pointer c@ [char] \ = ;
445
446 : underscore?  line_pointer c@ [char] _ = ;
447
448 : dot?  line_pointer c@ [char] . = ;
449
450 \ manipulation of input line
451 : skip_character line_pointer char+ to line_pointer ;
452
453 : skip_to_end_of_line end_of_line to line_pointer ;
454
455 : eat_space
456   begin
457     end_of_line? if 0 else space? then
458   while
459     skip_character
460   repeat
461 ;
462
463 : parse_name  ( -- addr len )
464   line_pointer
465   begin
466     end_of_line? if 0 else letter? digit? underscore? dot? or or or then
467   while
468     skip_character
469   repeat
470   line_pointer over -
471   strdup
472 ;
473
474 : remove_backslashes  { addr len | addr' len' -- addr' len' }
475   len allocate if ENOMEM throw then
476   to addr'
477   addr >r
478   begin
479     addr c@ [char] \ <> if
480       addr c@ addr' len' + c!
481       len' char+ to len'
482     then
483     addr char+ to addr
484     r@ len + addr =
485   until
486   r> drop
487   addr' len'
488 ;
489
490 : parse_quote  ( -- addr len )
491   line_pointer
492   skip_character
493   end_of_line? if ESYNTAX throw then
494   begin
495     quote? 0=
496   while
497     backslash? if
498       skip_character
499       end_of_line? if ESYNTAX throw then
500     then
501     skip_character
502     end_of_line? if ESYNTAX throw then 
503   repeat
504   skip_character
505   line_pointer over -
506   remove_backslashes
507 ;
508
509 : read_name
510   parse_name            ( -- addr len )
511   name_buffer strset
512 ;
513
514 : read_value
515   quote? if
516     parse_quote         ( -- addr len )
517   else
518     parse_name          ( -- addr len )
519   then
520   value_buffer strset
521 ;
522
523 : comment
524   skip_to_end_of_line
525 ;
526
527 : white_space_4
528   eat_space
529   comment? if ['] comment to parsing_function exit then
530   end_of_line? 0= if ESYNTAX throw then
531 ;
532
533 : variable_value
534   read_value
535   ['] white_space_4 to parsing_function
536 ;
537
538 : white_space_3
539   eat_space
540   letter? digit? quote? or or if
541     ['] variable_value to parsing_function exit
542   then
543   ESYNTAX throw
544 ;
545
546 : assignment_sign
547   skip_character
548   ['] white_space_3 to parsing_function
549 ;
550
551 : white_space_2
552   eat_space
553   assignment_sign? if ['] assignment_sign to parsing_function exit then
554   ESYNTAX throw
555 ;
556
557 : variable_name
558   read_name
559   ['] white_space_2 to parsing_function
560 ;
561
562 : white_space_1
563   eat_space
564   letter?  if ['] variable_name to parsing_function exit then
565   comment? if ['] comment to parsing_function exit then
566   end_of_line? 0= if ESYNTAX throw then
567 ;
568
569 file-processing definitions
570
571 : get_assignment
572   line_buffer strget + to end_of_line
573   line_buffer .addr @ to line_pointer
574   ['] white_space_1 to parsing_function
575   begin
576     end_of_line? 0=
577   while
578     parsing_function execute
579   repeat
580   parsing_function ['] comment =
581   parsing_function ['] white_space_1 =
582   parsing_function ['] white_space_4 =
583   or or 0= if ESYNTAX throw then
584 ;
585
586 only forth also support-functions also file-processing definitions also
587
588 \ Process line
589
590 : assignment_type?  ( addr len -- flag )
591   name_buffer strget
592   compare 0=
593 ;
594
595 : suffix_type?  ( addr len -- flag )
596   name_buffer .len @ over <= if 2drop false exit then
597   name_buffer .len @ over - name_buffer .addr @ +
598   over compare 0=
599 ;
600
601 : loader_conf_files?  s" loader_conf_files" assignment_type?  ;
602
603 : nextboot_flag?  s" nextboot_enable" assignment_type?  ;
604
605 : nextboot_conf? s" nextboot_conf" assignment_type?  ;
606
607 : verbose_flag? s" verbose_loading" assignment_type?  ;
608
609 : execute? s" exec" assignment_type?  ;
610
611 : module_load? load_module_suffix suffix_type? ;
612
613 : module_loadname?  module_loadname_suffix suffix_type?  ;
614
615 : module_type?  module_type_suffix suffix_type?  ;
616
617 : module_args?  module_args_suffix suffix_type?  ;
618
619 : module_beforeload?  module_beforeload_suffix suffix_type?  ;
620
621 : module_afterload?  module_afterload_suffix suffix_type?  ;
622
623 : module_loaderror?  module_loaderror_suffix suffix_type?  ;
624
625 \ build a 'set' statement and execute it
626 : set_environment_variable
627   name_buffer .len @ value_buffer .len @ + 5 chars + \ size of result string
628   allocate if ENOMEM throw then
629   dup 0  \ start with an empty string and append the pieces
630   s" set " strcat
631   name_buffer strget strcat
632   s" =" strcat
633   value_buffer strget strcat
634   ['] evaluate catch if
635     2drop free drop
636     ESETERROR throw
637   else
638     free-memory
639   then
640 ;
641
642 : set_conf_files
643   set_environment_variable
644   s" loader_conf_files" getenv conf_files string=
645 ;
646
647 : set_nextboot_conf \ XXX maybe do as set_conf_files ?
648   value_buffer strget unquote nextboot_conf_file string=
649 ;
650
651 : append_to_module_options_list  ( addr -- )
652   module_options @ 0= if
653     dup module_options !
654     last_module_option !
655   else
656     dup last_module_option @ module.next !
657     last_module_option !
658   then
659 ;
660
661 : set_module_name  { addr -- }  \ check leaks
662   name_buffer strget addr module.name string=
663 ;
664
665 : yes_value?
666   value_buffer strget   \ XXX could use unquote
667   2dup s' "YES"' compare >r
668   2dup s' "yes"' compare >r
669   2dup s" YES" compare >r
670   s" yes" compare r> r> r> and and and 0=
671 ;
672
673 : find_module_option  ( -- addr | 0 ) \ return ptr to entry matching name_buffer
674   module_options @
675   begin
676     dup
677   while
678     dup module.name strget
679     name_buffer strget
680     compare 0= if exit then
681     module.next @
682   repeat
683 ;
684
685 : new_module_option  ( -- addr )
686   sizeof module allocate if ENOMEM throw then
687   dup sizeof module erase
688   dup append_to_module_options_list
689   dup set_module_name
690 ;
691
692 : get_module_option  ( -- addr )
693   find_module_option
694   ?dup 0= if new_module_option then
695 ;
696
697 : set_module_flag
698   name_buffer .len @ load_module_suffix nip - name_buffer .len !
699   yes_value? get_module_option module.flag !
700 ;
701
702 : set_module_args
703   name_buffer .len @ module_args_suffix nip - name_buffer .len !
704   value_buffer strget unquote
705   get_module_option module.args string=
706 ;
707
708 : set_module_loadname
709   name_buffer .len @ module_loadname_suffix nip - name_buffer .len !
710   value_buffer strget unquote
711   get_module_option module.loadname string=
712 ;
713
714 : set_module_type
715   name_buffer .len @ module_type_suffix nip - name_buffer .len !
716   value_buffer strget unquote
717   get_module_option module.type string=
718 ;
719
720 : set_module_beforeload
721   name_buffer .len @ module_beforeload_suffix nip - name_buffer .len !
722   value_buffer strget unquote
723   get_module_option module.beforeload string=
724 ;
725
726 : set_module_afterload
727   name_buffer .len @ module_afterload_suffix nip - name_buffer .len !
728   value_buffer strget unquote
729   get_module_option module.afterload string=
730 ;
731
732 : set_module_loaderror
733   name_buffer .len @ module_loaderror_suffix nip - name_buffer .len !
734   value_buffer strget unquote
735   get_module_option module.loaderror string=
736 ;
737
738 : set_nextboot_flag
739   yes_value? to nextboot?
740 ;
741
742 : set_verbose
743   yes_value? to verbose?
744 ;
745
746 : execute_command
747   value_buffer strget unquote
748   ['] evaluate catch if EEXEC throw then
749 ;
750
751 : process_assignment
752   name_buffer .len @ 0= if exit then
753   loader_conf_files?    if set_conf_files exit then
754   nextboot_flag?        if set_nextboot_flag exit then
755   nextboot_conf?        if set_nextboot_conf exit then
756   verbose_flag?         if set_verbose exit then
757   execute?              if execute_command exit then
758   module_load?          if set_module_flag exit then
759   module_loadname?      if set_module_loadname exit then
760   module_type?          if set_module_type exit then
761   module_args?          if set_module_args exit then
762   module_beforeload?    if set_module_beforeload exit then
763   module_afterload?     if set_module_afterload exit then
764   module_loaderror?     if set_module_loaderror exit then
765   set_environment_variable
766 ;
767
768 \ free_buffer  ( -- )
769 \
770 \ Free some pointers if needed. The code then tests for errors
771 \ in freeing, and throws an exception if needed. If a pointer is
772 \ not allocated, it's value (0) is used as flag.
773
774 : free_buffers
775   name_buffer strfree
776   value_buffer strfree
777 ;
778
779 \ Higher level file processing
780
781 support-functions definitions
782
783 : process_conf
784   begin
785     end_of_file? 0=
786   while
787     free_buffers
788     read_line
789     get_assignment
790     ['] process_assignment catch
791     ['] free_buffers catch
792     swap throw throw
793   repeat
794 ;
795
796 : peek_file
797   0 to end_of_file?
798   reset_line_reading
799   O_RDONLY fopen fd !
800   fd @ -1 = if EOPEN throw then
801   free_buffers
802   read_line
803   get_assignment
804   ['] process_assignment catch
805   ['] free_buffers catch
806   fd @ fclose
807 ;
808   
809 only forth also support-functions definitions
810
811 \ Interface to loading conf files
812
813 : load_conf  ( addr len -- )
814   \ ." ----- Trying conf " 2dup type cr \ debugging
815   0 to end_of_file?
816   reset_line_reading
817   O_RDONLY fopen fd !
818   fd @ -1 = if EOPEN throw then
819   ['] process_conf catch
820   fd @ fclose
821   throw
822 ;
823
824 : print_line line_buffer strtype cr ;
825
826 : print_syntax_error
827   line_buffer strtype cr
828   line_buffer .addr @
829   begin
830     line_pointer over <>
831   while
832     bl emit char+
833   repeat
834   drop
835   ." ^" cr
836 ;
837
838
839 \ Debugging support functions
840
841 only forth definitions also support-functions
842
843 : test-file 
844   ['] load_conf catch dup .
845   ESYNTAX = if cr print_syntax_error then
846 ;
847
848 \ find a module name, leave addr on the stack (0 if not found)
849 : find-module ( <module> -- ptr | 0 )
850   bl parse ( addr len )
851   module_options @ >r ( store current pointer )
852   begin
853     r@
854   while
855     2dup ( addr len addr len )
856     r@ module.name strget
857     compare 0= if drop drop r> exit then ( found it )
858     r> module.next @ >r
859   repeat
860   type ."  was not found" cr r>
861 ;
862
863 : show-nonempty ( addr len mod -- )
864   strget dup verbose? or if
865     2swap type type cr
866   else
867     drop drop drop drop
868   then ;
869
870 : show-one-module { addr -- addr }
871   ." Name:        " addr module.name strtype cr
872   s" Path:        " addr module.loadname show-nonempty
873   s" Type:        " addr module.type show-nonempty
874   s" Flags:       " addr module.args show-nonempty
875   s" Before load: " addr module.beforeload show-nonempty
876   s" After load:  " addr module.afterload show-nonempty
877   s" Error:       " addr module.loaderror show-nonempty
878   ." Status:      " addr module.flag @ if ." Load" else ." Don't load" then cr
879   cr
880   addr
881 ;
882
883 : show-module-options
884   module_options @
885   begin
886     ?dup
887   while
888     show-one-module
889     module.next @
890   repeat
891 ;
892
893 only forth also support-functions definitions
894
895 \ Variables used for processing multiple conf files
896
897 string current_file_name_ref    \ used to print the file name
898
899 \ Indicates if any conf file was succesfully read
900
901 0 value any_conf_read?
902
903 \ loader_conf_files processing support functions
904
905 : get_conf_files ( -- addr len )  \ put addr/len on stack, reset var
906   \ ." -- starting on <" conf_files strtype ." >" cr \ debugging
907   conf_files strget 0 0 conf_files strset
908 ;
909
910 : skip_leading_spaces  { addr len pos -- addr len pos' }
911   begin
912     pos len = if 0 else addr pos + c@ bl = then
913   while
914     pos char+ to pos
915   repeat
916   addr len pos
917 ;
918
919 \ return the file name at pos, or free the string if nothing left
920 : get_file_name  { addr len pos -- addr len pos' addr' len' || 0 }
921   pos len = if 
922     addr free abort" Fatal error freeing memory"
923     0 exit
924   then
925   pos >r
926   begin
927     \ stay in the loop until have chars and they are not blank
928     pos len = if 0 else addr pos + c@ bl <> then
929   while
930     pos char+ to pos
931   repeat
932   addr len pos addr r@ + pos r> -
933   \ 2dup ." get_file_name has " type cr \ debugging
934 ;
935
936 : get_next_file  ( addr len ptr -- addr len ptr' addr' len' | 0 )
937   skip_leading_spaces
938   get_file_name
939 ;
940
941 : print_current_file
942   current_file_name_ref strtype
943 ;
944
945 : process_conf_errors
946   dup 0= if true to any_conf_read? drop exit then
947   >r 2drop r>
948   dup ESYNTAX = if
949     ." Warning: syntax error on file " print_current_file cr
950     print_syntax_error drop exit
951   then
952   dup ESETERROR = if
953     ." Warning: bad definition on file " print_current_file cr
954     print_line drop exit
955   then
956   dup EREAD = if
957     ." Warning: error reading file " print_current_file cr drop exit
958   then
959   dup EOPEN = if
960     verbose? if ." Warning: unable to open file " print_current_file cr then
961     drop exit
962   then
963   dup EFREE = abort" Fatal error freeing memory"
964   dup ENOMEM = abort" Out of memory"
965   throw  \ Unknown error -- pass ahead
966 ;
967
968 \ Process loader_conf_files recursively
969 \ Interface to loader_conf_files processing
970
971 : include_conf_files
972   get_conf_files 0      ( addr len offset )
973   begin
974     get_next_file ?dup ( addr len 1 | 0 )
975   while
976     current_file_name_ref strref
977     ['] load_conf catch
978     process_conf_errors
979     conf_files .addr @ if recurse then
980   repeat
981 ;
982
983 : get_nextboot_conf_file ( -- addr len )
984   nextboot_conf_file strget strdup      \ XXX is the strdup a leak ?
985 ;
986
987 : rewrite_nextboot_file ( -- )
988   get_nextboot_conf_file
989   O_WRONLY fopen fd !
990   fd @ -1 = if EOPEN throw then
991   fd @ s' nextboot_enable="NO" ' fwrite
992   fd @ fclose
993 ;
994
995 : include_nextboot_file
996   get_nextboot_conf_file
997   ['] peek_file catch
998   nextboot? if
999     get_nextboot_conf_file
1000     ['] load_conf catch
1001     process_conf_errors
1002     ['] rewrite_nextboot_file catch
1003   then
1004 ;
1005
1006 \ Module loading functions
1007
1008 : load_parameters  { addr -- addr addrN lenN ... addr1 len1 N }
1009   addr
1010   addr module.args strget
1011   addr module.loadname .len @ if
1012     addr module.loadname strget
1013   else
1014     addr module.name strget
1015   then
1016   addr module.type .len @ if
1017     addr module.type strget
1018     s" -t "
1019     4 ( -t type name flags )
1020   else
1021     2 ( name flags )
1022   then
1023 ;
1024
1025 : before_load  ( addr -- addr )
1026   dup module.beforeload .len @ if
1027     dup module.beforeload strget
1028     ['] evaluate catch if EBEFORELOAD throw then
1029   then
1030 ;
1031
1032 : after_load  ( addr -- addr )
1033   dup module.afterload .len @ if
1034     dup module.afterload strget
1035     ['] evaluate catch if EAFTERLOAD throw then
1036   then
1037 ;
1038
1039 : load_error  ( addr -- addr )
1040   dup module.loaderror .len @ if
1041     dup module.loaderror strget
1042     evaluate  \ This we do not intercept so it can throw errors
1043   then
1044 ;
1045
1046 : pre_load_message  ( addr -- addr )
1047   verbose? if
1048     dup module.name strtype
1049     ." ..."
1050   then
1051 ;
1052
1053 : load_error_message verbose? if ." failed!" cr then ;
1054
1055 : load_succesful_message verbose? if ." ok" cr then ;
1056
1057 : load_module
1058   load_parameters load
1059 ;
1060
1061 : process_module  ( addr -- addr )
1062   pre_load_message
1063   before_load
1064   begin
1065     ['] load_module catch if
1066       dup module.loaderror .len @ if
1067         load_error                      \ Command should return a flag!
1068       else 
1069         load_error_message true         \ Do not retry
1070       then
1071     else
1072       after_load
1073       load_succesful_message true       \ Succesful, do not retry
1074     then
1075   until
1076 ;
1077
1078 : process_module_errors  ( addr ior -- )
1079   dup EBEFORELOAD = if
1080     drop
1081     ." Module "
1082     dup module.name strtype
1083     dup module.loadname .len @ if
1084       ." (" dup module.loadname strtype ." )"
1085     then
1086     cr
1087     ." Error executing "
1088     dup module.beforeload strtype cr    \ XXX there was a typo here
1089     abort
1090   then
1091
1092   dup EAFTERLOAD = if
1093     drop
1094     ." Module "
1095     dup module.name .addr @ over module.name .len @ type
1096     dup module.loadname .len @ if
1097       ." (" dup module.loadname strtype ." )"
1098     then
1099     cr
1100     ." Error executing "
1101     dup module.afterload strtype cr
1102     abort
1103   then
1104
1105   throw  \ Don't know what it is all about -- pass ahead
1106 ;
1107
1108 \ Module loading interface
1109
1110 \ scan the list of modules, load enabled ones.
1111 : load_modules  ( -- ) ( throws: abort & user-defined )
1112   module_options @      ( list_head )
1113   begin
1114     ?dup
1115   while
1116     dup module.flag @ if
1117       ['] process_module catch
1118       process_module_errors
1119     then
1120     module.next @
1121   repeat
1122 ;
1123
1124 \ h00h00 magic used to try loading either a kernel with a given name,
1125 \ or a kernel with the default name in a directory of a given name
1126 \ (the pain!)
1127
1128 : bootpath s" /boot/" ;
1129 : modulepath s" module_path" ;
1130
1131 \ Functions used to save and restore module_path's value.
1132 : saveenv ( addr len | -1 -- addr' len | 0 -1 )
1133   dup -1 = if 0 swap exit then
1134   strdup
1135 ;
1136 : freeenv ( addr len | 0 -1 )
1137   -1 = if drop else free abort" Freeing error" then
1138 ;
1139 : restoreenv  ( addr len | 0 -1 -- )
1140   dup -1 = if ( it wasn't set )
1141     2drop
1142     modulepath unsetenv
1143   else
1144     over >r
1145     modulepath setenv
1146     r> free abort" Freeing error"
1147   then
1148 ;
1149
1150 : clip_args   \ Drop second string if only one argument is passed
1151   1 = if
1152     2swap 2drop
1153     1
1154   else
1155     2
1156   then
1157 ;
1158
1159 also builtins
1160
1161 \ Parse filename from a semicolon-separated list
1162
1163 \ replacement, not working yet
1164 : newparse-; { addr len | a1 -- a' len-x addr x }
1165   addr len [char] ; strchr dup if       ( a1 len1 )
1166     swap to a1  ( store address )
1167     1 - a1 @ 1 + swap ( remove match )
1168     addr a1 addr -
1169   else
1170     0 0 addr len
1171   then
1172 ;
1173
1174 : parse-; ( addr len -- addr' len-x addr x )
1175   over 0 2swap                  ( addr 0 addr len )
1176   begin
1177     dup 0 <>                    ( addr 0 addr len )
1178   while
1179     over c@ [char] ; <>         ( addr 0 addr len flag )
1180   while
1181     1- swap 1+ swap
1182     2swap 1+ 2swap
1183   repeat then
1184   dup 0 <> if
1185     1- swap 1+ swap
1186   then
1187   2swap
1188 ;
1189
1190 \ Try loading one of multiple kernels specified
1191
1192 : try_multiple_kernels ( addr len addr' len' args -- flag )
1193   >r
1194   begin
1195     parse-; 2>r
1196     2over 2r>
1197     r@ clip_args
1198     s" DEBUG" getenv? if
1199       s" echo Module_path: ${module_path}" evaluate
1200       ." Kernel     : " >r 2dup type r> cr
1201       dup 2 = if ." Flags      : " >r 2over type r> cr then
1202     then
1203     1 load
1204   while
1205     dup 0=
1206   until
1207     1 >r \ Failure
1208   else
1209     0 >r \ Success
1210   then
1211   2drop 2drop
1212   r>
1213   r> drop
1214 ;
1215
1216 \ Try to load a kernel; the kernel name is taken from one of
1217 \ the following lists, as ordered:
1218 \
1219 \   1. The "bootfile" environment variable
1220 \   2. The "kernel" environment variable
1221 \
1222 \ Flags are passed, if available. If not, dummy values must be given.
1223 \
1224 \ The kernel gets loaded from the current module_path.
1225
1226 : load_a_kernel ( flags len 1 | x x 0 -- flag )
1227   local args
1228   2local flags
1229   0 0 2local kernel
1230   end-locals
1231
1232   \ Check if a default kernel name exists at all, exits if not
1233   s" bootfile" getenv dup -1 <> if
1234     to kernel
1235     flags kernel args 1+ try_multiple_kernels
1236     dup 0= if exit then
1237   then
1238   drop
1239
1240   s" kernel" getenv dup -1 <> if
1241     to kernel
1242   else
1243     drop
1244     1 exit \ Failure
1245   then
1246
1247   \ Try all default kernel names
1248   flags kernel args 1+ try_multiple_kernels
1249 ;
1250
1251 \ Try to load a kernel; the kernel name is taken from one of
1252 \ the following lists, as ordered:
1253 \
1254 \   1. The "bootfile" environment variable
1255 \   2. The "kernel" environment variable
1256 \
1257 \ Flags are passed, if provided.
1258 \
1259 \ The kernel will be loaded from a directory computed from the
1260 \ path given. Two directories will be tried in the following order:
1261 \
1262 \   1. /boot/path
1263 \   2. path
1264 \
1265 \ The module_path variable is overridden if load is succesful, by
1266 \ prepending the successful path.
1267
1268 : load_from_directory ( path len 1 | flags len' path len 2 -- flag )
1269   local args
1270   2local path
1271   args 1 = if 0 0 then
1272   2local flags
1273   0 0 2local oldmodulepath \ like a string
1274   0 0 2local newmodulepath \ like a string
1275   end-locals
1276
1277   \ Set the environment variable module_path, and try loading
1278   \ the kernel again.
1279   modulepath getenv saveenv to oldmodulepath
1280
1281   \ Try prepending /boot/ first
1282   bootpath nip path nip +       \ total length
1283   oldmodulepath nip dup -1 = if
1284     drop
1285   else
1286     1+ +                        \ add oldpath -- XXX why the 1+ ?
1287   then
1288   allocate if ( out of memory ) 1 exit then \ XXX throw ?
1289
1290   0
1291   bootpath strcat
1292   path strcat
1293   2dup to newmodulepath
1294   modulepath setenv
1295
1296   \ Try all default kernel names
1297   flags args 1- load_a_kernel
1298   0= if ( success )
1299     oldmodulepath nip -1 <> if
1300       newmodulepath s" ;" strcat
1301       oldmodulepath strcat
1302       modulepath setenv
1303       newmodulepath drop free-memory
1304       oldmodulepath drop free-memory
1305     then
1306     0 exit
1307   then
1308
1309   \ Well, try without the prepended /boot/
1310   path newmodulepath drop swap move
1311   newmodulepath drop path nip
1312   2dup to newmodulepath
1313   modulepath setenv
1314
1315   \ Try all default kernel names
1316   flags args 1- load_a_kernel
1317   if ( failed once more )
1318     oldmodulepath restoreenv
1319     newmodulepath drop free-memory
1320     1
1321   else
1322     oldmodulepath nip -1 <> if
1323       newmodulepath s" ;" strcat
1324       oldmodulepath strcat
1325       modulepath setenv
1326       newmodulepath drop free-memory
1327       oldmodulepath drop free-memory
1328     then
1329     0
1330   then
1331 ;
1332
1333 \ Try to load a kernel; the kernel name is taken from one of
1334 \ the following lists, as ordered:
1335 \
1336 \   1. The "bootfile" environment variable
1337 \   2. The "kernel" environment variable
1338 \   3. The "path" argument
1339 \
1340 \ Flags are passed, if provided.
1341 \
1342 \ The kernel will be loaded from a directory computed from the
1343 \ path given. Two directories will be tried in the following order:
1344 \
1345 \   1. /boot/path
1346 \   2. path
1347 \
1348 \ Unless "path" is meant to be kernel name itself. In that case, it
1349 \ will first be tried as a full path, and, next, search on the
1350 \ directories pointed by module_path.
1351 \
1352 \ The module_path variable is overridden if load is succesful, by
1353 \ prepending the successful path.
1354
1355 : load_directory_or_file ( path len 1 | flags len' path len 2 -- flag )
1356   local args
1357   2local path
1358   args 1 = if 0 0 then
1359   2local flags
1360   end-locals
1361
1362   \ First, assume path is an absolute path to a directory
1363   flags path args clip_args load_from_directory
1364   dup 0= if exit else drop then
1365
1366   \ Next, assume path points to the kernel
1367   flags path args try_multiple_kernels
1368 ;
1369
1370 : initialize  ( addr len -- )
1371   strdup conf_files strset
1372 ;
1373
1374 : kernel_options ( -- addr len 1 | 0 )
1375   s" kernel_options" getenv
1376   dup -1 = if drop 0 else 1 then
1377 ;
1378
1379 : standard_kernel_search  ( flags 1 | 0 -- flag )
1380   local args
1381   args 0= if 0 0 then
1382   2local flags
1383   s" kernel" getenv
1384   dup -1 = if 0 swap then
1385   2local path
1386   end-locals
1387
1388   path nip -1 = if ( there isn't a "kernel" environment variable )
1389     flags args load_a_kernel
1390   else
1391     flags path args 1+ clip_args load_directory_or_file
1392   then
1393 ;
1394
1395 : load_kernel  ( -- ) ( throws: abort )
1396   kernel_options standard_kernel_search
1397   abort" Unable to load a kernel!"
1398 ;
1399
1400 : set_defaultoptions  ( -- )
1401   s" kernel_options" getenv dup -1 = if
1402     drop
1403   else
1404     s" temp_options" setenv
1405   then
1406 ;
1407
1408 \ pick the i-th argument, i starts at 0
1409 : argv[]  ( aN uN ... a1 u1 N i -- aN uN ... a1 u1 N ai+1 ui+1 )
1410   2dup = if 0 0 exit then       \ out of range
1411   dup >r
1412   1+ 2* ( skip N and ui )
1413   pick
1414   r>
1415   1+ 2* ( skip N and ai )
1416   pick
1417 ;
1418
1419 : drop_args  ( aN uN ... a1 u1 N -- )
1420   0 ?do 2drop loop
1421 ;
1422
1423 : argc
1424   dup
1425 ;
1426
1427 : queue_argv  ( aN uN ... a1 u1 N a u -- a u aN uN ... a1 u1 N+1 )
1428   >r
1429   over 2* 1+ -roll
1430   r>
1431   over 2* 1+ -roll
1432   1+
1433 ;
1434
1435 : unqueue_argv  ( aN uN ... a1 u1 N -- aN uN ... a2 u2 N-1 a1 u1 )
1436   1- -rot
1437 ;
1438
1439 \ compute the length of the buffer including the spaces between words
1440 : strlen(argv) ( aN uN .. a1 u1 N -- aN uN .. a1 u1 N len )
1441   dup 0= if 0 exit then
1442   0 >r  \ Size
1443   0 >r  \ Index
1444   begin
1445     argc r@ <>
1446   while
1447     r@ argv[]
1448     nip
1449     r> r> rot + 1+
1450     >r 1+ >r
1451   repeat
1452   r> drop
1453   r>
1454 ;
1455
1456 : concat_argv  ( aN uN ... a1 u1 N -- a u )
1457   strlen(argv) allocate if ENOMEM throw then
1458   0 2>r ( save addr 0 on return stack )
1459
1460   begin
1461     dup
1462   while
1463     unqueue_argv ( ... N a1 u1 )
1464     2r> 2swap    ( old a1 u1 )
1465     strcat
1466     s"  " strcat ( append one space ) \ XXX this gives a trailing space
1467     2>r         ( store string on the result stack )
1468   repeat
1469   drop_args
1470   2r>
1471 ;
1472
1473 : set_tempoptions  ( addrN lenN ... addr1 len1 N -- addr len 1 | 0 )
1474   \ Save the first argument, if it exists and is not a flag
1475   argc if
1476     0 argv[] drop c@ [char] - <> if
1477       unqueue_argv 2>r  \ Filename
1478       1 >r              \ Filename present
1479     else
1480       0 >r              \ Filename not present
1481     then
1482   else
1483     0 >r                \ Filename not present
1484   then
1485
1486   \ If there are other arguments, assume they are flags
1487   ?dup if
1488     concat_argv
1489     2dup s" temp_options" setenv
1490     drop free if EFREE throw then
1491   else
1492     set_defaultoptions
1493   then
1494
1495   \ Bring back the filename, if one was provided
1496   r> if 2r> 1 else 0 then
1497 ;
1498
1499 : get_arguments ( -- addrN lenN ... addr1 len1 N )
1500   0
1501   begin
1502     \ Get next word on the command line
1503     parse-word
1504   ?dup while
1505     queue_argv
1506   repeat
1507   drop ( empty string )
1508 ;
1509
1510 : load_kernel_and_modules  ( args -- flag )
1511   set_tempoptions
1512   argc >r
1513   s" temp_options" getenv dup -1 <> if
1514     queue_argv
1515   else
1516     drop
1517   then
1518   r> if ( a path was passed )
1519     load_directory_or_file
1520   else
1521     standard_kernel_search
1522   then
1523   ?dup 0= if ['] load_modules catch then
1524 ;
1525
1526 \ Go back to straight forth vocabulary
1527
1528 only forth also definitions
1529