]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/forth/support.4th
Add UPDATING entries and bump version.
[FreeBSD/FreeBSD.git] / stand / 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 successfully 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 stand/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 \ determine if a word appears in a string, case-insensitive
205 : contains? ( addr1 len1 addr2 len2 -- 0 | -1 )
206         2 pick 0= if 2drop 2drop true exit then
207         dup 0= if 2drop 2drop false exit then
208         begin
209                 begin
210                         swap dup c@ dup 32 = over 9 = or over 10 = or
211                         over 13 = or over 44 = or swap drop
212                 while 1+ swap 1- repeat
213                 swap 2 pick 1- over <
214         while
215                 2over 2over drop over compare-insensitive 0= if
216                         2 pick over = if 2drop 2drop true exit then
217                         2 pick tuck - -rot + swap over c@ dup 32 =
218                         over 9 = or over 10 = or over 13 = or over 44 = or
219                         swap drop if 2drop 2drop true exit then
220                 then begin
221                         swap dup c@ dup 32 = over 9 = or over 10 = or
222                         over 13 = or over 44 = or swap drop
223                         if false else true then 2 pick 0> and
224                 while 1+ swap 1- repeat
225                 swap
226         repeat
227         2drop 2drop false
228 ;
229
230 : boot_serial? ( -- 0 | -1 )
231         s" console" getenv dup -1 <> if
232                 s" comconsole" 2swap contains?
233         else drop false then
234         s" boot_serial" getenv dup -1 <> if
235                 swap drop 0>
236         else drop false then
237         or \ console contains comconsole ( or ) boot_serial
238         s" boot_multicons" getenv dup -1 <> if
239                 swap drop 0>
240         else drop false then
241         or \ previous boolean ( or ) boot_multicons
242 ;
243
244 \ Private definitions
245
246 vocabulary support-functions
247 only forth also support-functions definitions
248
249 \ Some control characters constants
250
251 7 constant bell
252 8 constant backspace
253 9 constant tab
254 10 constant lf
255 13 constant <cr>
256
257 \ Read buffer size
258
259 80 constant read_buffer_size
260
261 \ Standard suffixes
262
263 : load_module_suffix            s" _load" ;
264 : module_loadname_suffix        s" _name" ;
265 : module_type_suffix            s" _type" ;
266 : module_args_suffix            s" _flags" ;
267 : module_beforeload_suffix      s" _before" ;
268 : module_afterload_suffix       s" _after" ;
269 : module_loaderror_suffix       s" _error" ;
270
271 \ Support operators
272
273 : >= < 0= ;
274 : <= > 0= ;
275
276 \ Assorted support functions
277
278 : free-memory free if EFREE throw then ;
279
280 : strget { var -- addr len } var .addr @ var .len @ ;
281
282 \ assign addr len to variable.
283 : strset  { addr len var -- } addr var .addr !  len var .len !  ;
284
285 \ free memory and reset fields
286 : strfree { var -- } var .addr @ ?dup if free-memory 0 0 var strset then ;
287
288 \ free old content, make a copy of the string and assign to variable
289 : string= { addr len var -- } var strfree addr len strdup var strset ;
290
291 : strtype ( str -- ) strget type ;
292
293 \ assign a reference to what is on the stack
294 : strref { addr len var -- addr len }
295   addr var .addr ! len var .len ! addr len
296 ;
297
298 \ unquote a string
299 : unquote ( addr len -- addr len )
300   over c@ [char] " = if 2 chars - swap char+ swap then
301 ;
302
303 \ Assignment data temporary storage
304
305 string name_buffer
306 string value_buffer
307
308 \ Line by line file reading functions
309 \
310 \ exported:
311 \       line_buffer
312 \       end_of_file?
313 \       fd
314 \       read_line
315 \       reset_line_reading
316
317 vocabulary line-reading
318 also line-reading definitions
319
320 \ File data temporary storage
321
322 string read_buffer
323 0 value read_buffer_ptr
324
325 \ File's line reading function
326
327 get-current ( -- wid ) previous definitions
328
329 string line_buffer
330 0 value end_of_file?
331 variable fd
332
333 >search ( wid -- ) definitions
334
335 : skip_newlines
336   begin
337     read_buffer .len @ read_buffer_ptr >
338   while
339     read_buffer .addr @ read_buffer_ptr + c@ lf = if
340       read_buffer_ptr char+ to read_buffer_ptr
341     else
342       exit
343     then
344   repeat
345 ;
346
347 : scan_buffer  ( -- addr len )
348   read_buffer_ptr >r
349   begin
350     read_buffer .len @ r@ >
351   while
352     read_buffer .addr @ r@ + c@ lf = if
353       read_buffer .addr @ read_buffer_ptr +  ( -- addr )
354       r@ read_buffer_ptr -                   ( -- len )
355       r> to read_buffer_ptr
356       exit
357     then
358     r> char+ >r
359   repeat
360   read_buffer .addr @ read_buffer_ptr +  ( -- addr )
361   r@ read_buffer_ptr -                   ( -- len )
362   r> to read_buffer_ptr
363 ;
364
365 : line_buffer_resize  ( len -- len )
366   dup 0= if exit then
367   >r
368   line_buffer .len @ if
369     line_buffer .addr @
370     line_buffer .len @ r@ +
371     resize if ENOMEM throw then
372   else
373     r@ allocate if ENOMEM throw then
374   then
375   line_buffer .addr !
376   r>
377 ;
378     
379 : append_to_line_buffer  ( addr len -- )
380   dup 0= if 2drop exit then
381   line_buffer strget
382   2swap strcat
383   line_buffer .len !
384   drop
385 ;
386
387 : read_from_buffer
388   scan_buffer            ( -- addr len )
389   line_buffer_resize     ( len -- len )
390   append_to_line_buffer  ( addr len -- )
391 ;
392
393 : refill_required?
394   read_buffer .len @ read_buffer_ptr =
395   end_of_file? 0= and
396 ;
397
398 : refill_buffer
399   0 to read_buffer_ptr
400   read_buffer .addr @ 0= if
401     read_buffer_size allocate if ENOMEM throw then
402     read_buffer .addr !
403   then
404   fd @ read_buffer .addr @ read_buffer_size fread
405   dup -1 = if EREAD throw then
406   dup 0= if true to end_of_file? then
407   read_buffer .len !
408 ;
409
410 get-current ( -- wid ) previous definitions >search ( wid -- )
411
412 : reset_line_reading
413   0 to read_buffer_ptr
414   0 read_buffer .len !
415 ;
416
417 : read_line
418   line_buffer strfree
419   skip_newlines
420   begin
421     read_from_buffer
422     refill_required?
423   while
424     refill_buffer
425   repeat
426 ;
427
428 only forth also support-functions definitions
429
430 \ Conf file line parser:
431 \ <line> ::= <spaces><name><spaces>'='<spaces><value><spaces>[<comment>] |
432 \            <spaces>[<comment>]
433 \ <name> ::= <letter>{<letter>|<digit>|'_'}
434 \ <value> ::= '"'{<character_set>|'\'<anything>}'"' | <name>
435 \ <character_set> ::= ASCII 32 to 126, except '\' and '"'
436 \ <comment> ::= '#'{<anything>}
437 \
438 \ exported:
439 \       line_pointer
440 \       process_conf
441
442 0 value line_pointer
443
444 vocabulary file-processing
445 also file-processing definitions
446
447 \ parser functions
448 \
449 \ exported:
450 \       get_assignment
451
452 vocabulary parser
453 also parser definitions
454
455 0 value parsing_function
456 0 value end_of_line
457
458 : end_of_line?  line_pointer end_of_line = ;
459
460 \ classifiers for various character classes in the input line
461
462 : letter?
463   line_pointer c@ >r
464   r@ [char] A >=
465   r@ [char] Z <= and
466   r@ [char] a >=
467   r> [char] z <= and
468   or
469 ;
470
471 : digit?
472   line_pointer c@ >r
473   r@ [char] - =
474   r@ [char] 0 >=
475   r> [char] 9 <= and
476   or
477 ;
478
479 : quote?  line_pointer c@ [char] " = ;
480
481 : assignment_sign?  line_pointer c@ [char] = = ;
482
483 : comment?  line_pointer c@ [char] # = ;
484
485 : space?  line_pointer c@ bl = line_pointer c@ tab = or ;
486
487 : backslash?  line_pointer c@ [char] \ = ;
488
489 : underscore?  line_pointer c@ [char] _ = ;
490
491 : dot?  line_pointer c@ [char] . = ;
492
493 \ manipulation of input line
494 : skip_character line_pointer char+ to line_pointer ;
495
496 : skip_to_end_of_line end_of_line to line_pointer ;
497
498 : eat_space
499   begin
500     end_of_line? if 0 else space? then
501   while
502     skip_character
503   repeat
504 ;
505
506 : parse_name  ( -- addr len )
507   line_pointer
508   begin
509     end_of_line? if 0 else letter? digit? underscore? dot? or or or then
510   while
511     skip_character
512   repeat
513   line_pointer over -
514   strdup
515 ;
516
517 : remove_backslashes  { addr len | addr' len' -- addr' len' }
518   len allocate if ENOMEM throw then
519   to addr'
520   addr >r
521   begin
522     addr c@ [char] \ <> if
523       addr c@ addr' len' + c!
524       len' char+ to len'
525     then
526     addr char+ to addr
527     r@ len + addr =
528   until
529   r> drop
530   addr' len'
531 ;
532
533 : parse_quote  ( -- addr len )
534   line_pointer
535   skip_character
536   end_of_line? if ESYNTAX throw then
537   begin
538     quote? 0=
539   while
540     backslash? if
541       skip_character
542       end_of_line? if ESYNTAX throw then
543     then
544     skip_character
545     end_of_line? if ESYNTAX throw then 
546   repeat
547   skip_character
548   line_pointer over -
549   remove_backslashes
550 ;
551
552 : read_name
553   parse_name            ( -- addr len )
554   name_buffer strset
555 ;
556
557 : read_value
558   quote? if
559     parse_quote         ( -- addr len )
560   else
561     parse_name          ( -- addr len )
562   then
563   value_buffer strset
564 ;
565
566 : comment
567   skip_to_end_of_line
568 ;
569
570 : white_space_4
571   eat_space
572   comment? if ['] comment to parsing_function exit then
573   end_of_line? 0= if ESYNTAX throw then
574 ;
575
576 : variable_value
577   read_value
578   ['] white_space_4 to parsing_function
579 ;
580
581 : white_space_3
582   eat_space
583   letter? digit? quote? or or if
584     ['] variable_value to parsing_function exit
585   then
586   ESYNTAX throw
587 ;
588
589 : assignment_sign
590   skip_character
591   ['] white_space_3 to parsing_function
592 ;
593
594 : white_space_2
595   eat_space
596   assignment_sign? if ['] assignment_sign to parsing_function exit then
597   ESYNTAX throw
598 ;
599
600 : variable_name
601   read_name
602   ['] white_space_2 to parsing_function
603 ;
604
605 : white_space_1
606   eat_space
607   letter?  if ['] variable_name to parsing_function exit then
608   comment? if ['] comment to parsing_function exit then
609   end_of_line? 0= if ESYNTAX throw then
610 ;
611
612 get-current ( -- wid ) previous definitions >search ( wid -- )
613
614 : get_assignment
615   line_buffer strget + to end_of_line
616   line_buffer .addr @ to line_pointer
617   ['] white_space_1 to parsing_function
618   begin
619     end_of_line? 0=
620   while
621     parsing_function execute
622   repeat
623   parsing_function ['] comment =
624   parsing_function ['] white_space_1 =
625   parsing_function ['] white_space_4 =
626   or or 0= if ESYNTAX throw then
627 ;
628
629 only forth also support-functions also file-processing definitions
630
631 \ Process line
632
633 : assignment_type?  ( addr len -- flag )
634   name_buffer strget
635   compare 0=
636 ;
637
638 : suffix_type?  ( addr len -- flag )
639   name_buffer .len @ over <= if 2drop false exit then
640   name_buffer .len @ over - name_buffer .addr @ +
641   over compare 0=
642 ;
643
644 : loader_conf_files?  s" loader_conf_files" assignment_type?  ;
645
646 : nextboot_flag?  s" nextboot_enable" assignment_type?  ;
647
648 : nextboot_conf? s" nextboot_conf" assignment_type?  ;
649
650 : verbose_flag? s" verbose_loading" assignment_type?  ;
651
652 : execute? s" exec" assignment_type?  ;
653
654 : module_load? load_module_suffix suffix_type? ;
655
656 : module_loadname?  module_loadname_suffix suffix_type?  ;
657
658 : module_type?  module_type_suffix suffix_type?  ;
659
660 : module_args?  module_args_suffix suffix_type?  ;
661
662 : module_beforeload?  module_beforeload_suffix suffix_type?  ;
663
664 : module_afterload?  module_afterload_suffix suffix_type?  ;
665
666 : module_loaderror?  module_loaderror_suffix suffix_type?  ;
667
668 \ build a 'set' statement and execute it
669 : set_environment_variable
670   name_buffer .len @ value_buffer .len @ + 5 chars + \ size of result string
671   allocate if ENOMEM throw then
672   dup 0  \ start with an empty string and append the pieces
673   s" set " strcat
674   name_buffer strget strcat
675   s" =" strcat
676   value_buffer strget strcat
677   ['] evaluate catch if
678     2drop free drop
679     ESETERROR throw
680   else
681     free-memory
682   then
683 ;
684
685 : set_conf_files
686   set_environment_variable
687   s" loader_conf_files" getenv conf_files string=
688 ;
689
690 : set_nextboot_conf
691   value_buffer strget unquote nextboot_conf_file string=
692 ;
693
694 : append_to_module_options_list  ( addr -- )
695   module_options @ 0= if
696     dup module_options !
697     last_module_option !
698   else
699     dup last_module_option @ module.next !
700     last_module_option !
701   then
702 ;
703
704 : set_module_name  { addr -- }  \ check leaks
705   name_buffer strget addr module.name string=
706 ;
707
708 : yes_value?
709   value_buffer strget   \ XXX could use unquote
710   2dup s' "YES"' compare >r
711   2dup s' "yes"' compare >r
712   2dup s" YES" compare >r
713   s" yes" compare r> r> r> and and and 0=
714 ;
715
716 : find_module_option  ( -- addr | 0 ) \ return ptr to entry matching name_buffer
717   module_options @
718   begin
719     dup
720   while
721     dup module.name strget
722     name_buffer strget
723     compare 0= if exit then
724     module.next @
725   repeat
726 ;
727
728 : new_module_option  ( -- addr )
729   sizeof module allocate if ENOMEM throw then
730   dup sizeof module erase
731   dup append_to_module_options_list
732   dup set_module_name
733 ;
734
735 : get_module_option  ( -- addr )
736   find_module_option
737   ?dup 0= if new_module_option then
738 ;
739
740 : set_module_flag
741   name_buffer .len @ load_module_suffix nip - name_buffer .len !
742   yes_value? get_module_option module.flag !
743 ;
744
745 : set_module_args
746   name_buffer .len @ module_args_suffix nip - name_buffer .len !
747   value_buffer strget unquote
748   get_module_option module.args string=
749 ;
750
751 : set_module_loadname
752   name_buffer .len @ module_loadname_suffix nip - name_buffer .len !
753   value_buffer strget unquote
754   get_module_option module.loadname string=
755 ;
756
757 : set_module_type
758   name_buffer .len @ module_type_suffix nip - name_buffer .len !
759   value_buffer strget unquote
760   get_module_option module.type string=
761 ;
762
763 : set_module_beforeload
764   name_buffer .len @ module_beforeload_suffix nip - name_buffer .len !
765   value_buffer strget unquote
766   get_module_option module.beforeload string=
767 ;
768
769 : set_module_afterload
770   name_buffer .len @ module_afterload_suffix nip - name_buffer .len !
771   value_buffer strget unquote
772   get_module_option module.afterload string=
773 ;
774
775 : set_module_loaderror
776   name_buffer .len @ module_loaderror_suffix nip - name_buffer .len !
777   value_buffer strget unquote
778   get_module_option module.loaderror string=
779 ;
780
781 : set_nextboot_flag
782   yes_value? to nextboot?
783 ;
784
785 : set_verbose
786   yes_value? to verbose?
787 ;
788
789 : execute_command
790   value_buffer strget unquote
791   ['] evaluate catch if EEXEC throw then
792 ;
793
794 : process_assignment
795   name_buffer .len @ 0= if exit then
796   loader_conf_files?    if set_conf_files exit then
797   nextboot_flag?        if set_nextboot_flag exit then
798   nextboot_conf?        if set_nextboot_conf exit then
799   verbose_flag?         if set_verbose exit then
800   execute?              if execute_command exit then
801   module_load?          if set_module_flag exit then
802   module_loadname?      if set_module_loadname exit then
803   module_type?          if set_module_type exit then
804   module_args?          if set_module_args exit then
805   module_beforeload?    if set_module_beforeload exit then
806   module_afterload?     if set_module_afterload exit then
807   module_loaderror?     if set_module_loaderror exit then
808   set_environment_variable
809 ;
810
811 \ free_buffer  ( -- )
812 \
813 \ Free some pointers if needed. The code then tests for errors
814 \ in freeing, and throws an exception if needed. If a pointer is
815 \ not allocated, it's value (0) is used as flag.
816
817 : free_buffers
818   name_buffer strfree
819   value_buffer strfree
820 ;
821
822 \ Higher level file processing
823
824 get-current ( -- wid ) previous definitions >search ( wid -- )
825
826 : process_conf
827   begin
828     end_of_file? 0=
829   while
830     free_buffers
831     read_line
832     get_assignment
833     ['] process_assignment catch
834     ['] free_buffers catch
835     swap throw throw
836   repeat
837 ;
838
839 : peek_file ( addr len -- )
840   0 to end_of_file?
841   reset_line_reading
842   O_RDONLY fopen fd !
843   fd @ -1 = if EOPEN throw then
844   free_buffers
845   read_line
846   get_assignment
847   ['] process_assignment catch
848   ['] free_buffers catch
849   fd @ fclose
850   swap throw throw
851 ;
852   
853 only forth also support-functions definitions
854
855 \ Interface to loading conf files
856
857 : load_conf  ( addr len -- )
858   0 to end_of_file?
859   reset_line_reading
860   O_RDONLY fopen fd !
861   fd @ -1 = if EOPEN throw then
862   ['] process_conf catch
863   fd @ fclose
864   throw
865 ;
866
867 : print_line line_buffer strtype cr ;
868
869 : print_syntax_error
870   line_buffer strtype cr
871   line_buffer .addr @
872   begin
873     line_pointer over <>
874   while
875     bl emit char+
876   repeat
877   drop
878   ." ^" cr
879 ;
880
881
882 \ Debugging support functions
883
884 only forth definitions also support-functions
885
886 : test-file 
887   ['] load_conf catch dup .
888   ESYNTAX = if cr print_syntax_error then
889 ;
890
891 \ find a module name, leave addr on the stack (0 if not found)
892 : find-module ( <module> -- ptr | 0 )
893   bl parse ( addr len )
894   module_options @ >r ( store current pointer )
895   begin
896     r@
897   while
898     2dup ( addr len addr len )
899     r@ module.name strget
900     compare 0= if drop drop r> exit then ( found it )
901     r> module.next @ >r
902   repeat
903   type ."  was not found" cr r>
904 ;
905
906 : show-nonempty ( addr len mod -- )
907   strget dup verbose? or if
908     2swap type type cr
909   else
910     drop drop drop drop
911   then ;
912
913 : show-one-module { addr -- addr }
914   ." Name:        " addr module.name strtype cr
915   s" Path:        " addr module.loadname show-nonempty
916   s" Type:        " addr module.type show-nonempty
917   s" Flags:       " addr module.args show-nonempty
918   s" Before load: " addr module.beforeload show-nonempty
919   s" After load:  " addr module.afterload show-nonempty
920   s" Error:       " addr module.loaderror show-nonempty
921   ." Status:      " addr module.flag @ if ." Load" else ." Don't load" then cr
922   cr
923   addr
924 ;
925
926 : show-module-options
927   module_options @
928   begin
929     ?dup
930   while
931     show-one-module
932     module.next @
933   repeat
934 ;
935
936 : free-one-module { addr -- addr }
937   addr module.name strfree
938   addr module.loadname strfree
939   addr module.type strfree
940   addr module.args strfree
941   addr module.beforeload strfree
942   addr module.afterload strfree
943   addr module.loaderror strfree
944   addr
945 ;
946
947 : free-module-options
948   module_options @
949   begin
950     ?dup
951   while
952     free-one-module
953     dup module.next @
954     swap free-memory
955   repeat
956   0 module_options !
957   0 last_module_option !
958 ;
959
960 only forth also support-functions definitions
961
962 \ Variables used for processing multiple conf files
963
964 string current_file_name_ref    \ used to print the file name
965
966 \ Indicates if any conf file was successfully read
967
968 0 value any_conf_read?
969
970 \ loader_conf_files processing support functions
971
972 : get_conf_files ( -- addr len )  \ put addr/len on stack, reset var
973   conf_files strget 0 0 conf_files strset
974 ;
975
976 : skip_leading_spaces  { addr len pos -- addr len pos' }
977   begin
978     pos len = if 0 else addr pos + c@ bl = then
979   while
980     pos char+ to pos
981   repeat
982   addr len pos
983 ;
984
985 \ return the file name at pos, or free the string if nothing left
986 : get_file_name  { addr len pos -- addr len pos' addr' len' || 0 }
987   pos len = if 
988     addr free abort" Fatal error freeing memory"
989     0 exit
990   then
991   pos >r
992   begin
993     \ stay in the loop until have chars and they are not blank
994     pos len = if 0 else addr pos + c@ bl <> then
995   while
996     pos char+ to pos
997   repeat
998   addr len pos addr r@ + pos r> -
999 ;
1000
1001 : get_next_file  ( addr len ptr -- addr len ptr' addr' len' | 0 )
1002   skip_leading_spaces
1003   get_file_name
1004 ;
1005
1006 : print_current_file
1007   current_file_name_ref strtype
1008 ;
1009
1010 : process_conf_errors
1011   dup 0= if true to any_conf_read? drop exit then
1012   >r 2drop r>
1013   dup ESYNTAX = if
1014     ." Warning: syntax error on file " print_current_file cr
1015     print_syntax_error drop exit
1016   then
1017   dup ESETERROR = if
1018     ." Warning: bad definition on file " print_current_file cr
1019     print_line drop exit
1020   then
1021   dup EREAD = if
1022     ." Warning: error reading file " print_current_file cr drop exit
1023   then
1024   dup EOPEN = if
1025     verbose? if ." Warning: unable to open file " print_current_file cr then
1026     drop exit
1027   then
1028   dup EFREE = abort" Fatal error freeing memory"
1029   dup ENOMEM = abort" Out of memory"
1030   throw  \ Unknown error -- pass ahead
1031 ;
1032
1033 \ Process loader_conf_files recursively
1034 \ Interface to loader_conf_files processing
1035
1036 : include_conf_files
1037   get_conf_files 0      ( addr len offset )
1038   begin
1039     get_next_file ?dup ( addr len 1 | 0 )
1040   while
1041     current_file_name_ref strref
1042     ['] load_conf catch
1043     process_conf_errors
1044     conf_files .addr @ if recurse then
1045   repeat
1046 ;
1047
1048 : get_nextboot_conf_file ( -- addr len )
1049   nextboot_conf_file strget
1050 ;
1051
1052 : rewrite_nextboot_file ( -- )
1053   get_nextboot_conf_file
1054   O_WRONLY fopen fd !
1055   fd @ -1 = if EOPEN throw then
1056   fd @ s' nextboot_enable="NO" ' fwrite ( fd buf len -- nwritten ) drop
1057   fd @ fclose
1058 ;
1059
1060 : include_nextboot_file ( -- )
1061   get_nextboot_conf_file
1062   ['] peek_file catch if 2drop then
1063   nextboot? if
1064     get_nextboot_conf_file
1065     current_file_name_ref strref
1066     ['] load_conf catch
1067     process_conf_errors
1068     ['] rewrite_nextboot_file catch if 2drop then
1069   then
1070 ;
1071
1072 \ Module loading functions
1073
1074 : load_parameters  { addr -- addr addrN lenN ... addr1 len1 N }
1075   addr
1076   addr module.args strget
1077   addr module.loadname .len @ if
1078     addr module.loadname strget
1079   else
1080     addr module.name strget
1081   then
1082   addr module.type .len @ if
1083     addr module.type strget
1084     s" -t "
1085     4 ( -t type name flags )
1086   else
1087     2 ( name flags )
1088   then
1089 ;
1090
1091 : before_load  ( addr -- addr )
1092   dup module.beforeload .len @ if
1093     dup module.beforeload strget
1094     ['] evaluate catch if EBEFORELOAD throw then
1095   then
1096 ;
1097
1098 : after_load  ( addr -- addr )
1099   dup module.afterload .len @ if
1100     dup module.afterload strget
1101     ['] evaluate catch if EAFTERLOAD throw then
1102   then
1103 ;
1104
1105 : load_error  ( addr -- addr )
1106   dup module.loaderror .len @ if
1107     dup module.loaderror strget
1108     evaluate  \ This we do not intercept so it can throw errors
1109   then
1110 ;
1111
1112 : pre_load_message  ( addr -- addr )
1113   verbose? if
1114     dup module.name strtype
1115     ." ..."
1116   then
1117 ;
1118
1119 : load_error_message verbose? if ." failed!" cr then ;
1120
1121 : load_successful_message verbose? if ." ok" cr then ;
1122
1123 : load_module
1124   load_parameters load
1125 ;
1126
1127 : process_module  ( addr -- addr )
1128   pre_load_message
1129   before_load
1130   begin
1131     ['] load_module catch if
1132       dup module.loaderror .len @ if
1133         load_error                      \ Command should return a flag!
1134       else 
1135         load_error_message true         \ Do not retry
1136       then
1137     else
1138       after_load
1139       load_successful_message true      \ Successful, do not retry
1140     then
1141   until
1142 ;
1143
1144 : process_module_errors  ( addr ior -- )
1145   dup EBEFORELOAD = if
1146     drop
1147     ." Module "
1148     dup module.name strtype
1149     dup module.loadname .len @ if
1150       ." (" dup module.loadname strtype ." )"
1151     then
1152     cr
1153     ." Error executing "
1154     dup module.beforeload strtype cr    \ XXX there was a typo here
1155     abort
1156   then
1157
1158   dup EAFTERLOAD = if
1159     drop
1160     ." Module "
1161     dup module.name .addr @ over module.name .len @ type
1162     dup module.loadname .len @ if
1163       ." (" dup module.loadname strtype ." )"
1164     then
1165     cr
1166     ." Error executing "
1167     dup module.afterload strtype cr
1168     abort
1169   then
1170
1171   throw  \ Don't know what it is all about -- pass ahead
1172 ;
1173
1174 \ Module loading interface
1175
1176 \ scan the list of modules, load enabled ones.
1177 : load_modules  ( -- ) ( throws: abort & user-defined )
1178   module_options @      ( list_head )
1179   begin
1180     ?dup
1181   while
1182     dup module.flag @ if
1183       ['] process_module catch
1184       process_module_errors
1185     then
1186     module.next @
1187   repeat
1188 ;
1189
1190 \ h00h00 magic used to try loading either a kernel with a given name,
1191 \ or a kernel with the default name in a directory of a given name
1192 \ (the pain!)
1193
1194 : bootpath s" /boot/" ;
1195 : modulepath s" module_path" ;
1196
1197 \ Functions used to save and restore module_path's value.
1198 : saveenv ( addr len | -1 -- addr' len | 0 -1 )
1199   dup -1 = if 0 swap exit then
1200   strdup
1201 ;
1202 : freeenv ( addr len | 0 -1 )
1203   -1 = if drop else free abort" Freeing error" then
1204 ;
1205 : restoreenv  ( addr len | 0 -1 -- )
1206   dup -1 = if ( it wasn't set )
1207     2drop
1208     modulepath unsetenv
1209   else
1210     over >r
1211     modulepath setenv
1212     r> free abort" Freeing error"
1213   then
1214 ;
1215
1216 : clip_args   \ Drop second string if only one argument is passed
1217   1 = if
1218     2swap 2drop
1219     1
1220   else
1221     2
1222   then
1223 ;
1224
1225 also builtins
1226
1227 \ Parse filename from a semicolon-separated list
1228
1229 \ replacement, not working yet
1230 : newparse-; { addr len | a1 -- a' len-x addr x }
1231   addr len [char] ; strchr dup if       ( a1 len1 )
1232     swap to a1  ( store address )
1233     1 - a1 @ 1 + swap ( remove match )
1234     addr a1 addr -
1235   else
1236     0 0 addr len
1237   then
1238 ;
1239
1240 : parse-; ( addr len -- addr' len-x addr x )
1241   over 0 2swap                  ( addr 0 addr len )
1242   begin
1243     dup 0 <>                    ( addr 0 addr len )
1244   while
1245     over c@ [char] ; <>         ( addr 0 addr len flag )
1246   while
1247     1- swap 1+ swap
1248     2swap 1+ 2swap
1249   repeat then
1250   dup 0 <> if
1251     1- swap 1+ swap
1252   then
1253   2swap
1254 ;
1255
1256 \ Try loading one of multiple kernels specified
1257
1258 : try_multiple_kernels ( addr len addr' len' args -- flag )
1259   >r
1260   begin
1261     parse-; 2>r
1262     2over 2r>
1263     r@ clip_args
1264     s" DEBUG" getenv? if
1265       s" echo Module_path: ${module_path}" evaluate
1266       ." Kernel     : " >r 2dup type r> cr
1267       dup 2 = if ." Flags      : " >r 2over type r> cr then
1268     then
1269     1 load
1270   while
1271     dup 0=
1272   until
1273     1 >r \ Failure
1274   else
1275     0 >r \ Success
1276   then
1277   2drop 2drop
1278   r>
1279   r> drop
1280 ;
1281
1282 \ Try to load a kernel; the kernel name is taken from one of
1283 \ the following lists, as ordered:
1284 \
1285 \   1. The "bootfile" environment variable
1286 \   2. The "kernel" environment variable
1287 \
1288 \ Flags are passed, if available. If not, dummy values must be given.
1289 \
1290 \ The kernel gets loaded from the current module_path.
1291
1292 : load_a_kernel ( flags len 1 | x x 0 -- flag )
1293   local args
1294   2local flags
1295   0 0 2local kernel
1296   end-locals
1297
1298   \ Check if a default kernel name exists at all, exits if not
1299   s" bootfile" getenv dup -1 <> if
1300     to kernel
1301     flags kernel args 1+ try_multiple_kernels
1302     dup 0= if exit then
1303   then
1304   drop
1305
1306   s" kernel" getenv dup -1 <> if
1307     to kernel
1308   else
1309     drop
1310     1 exit \ Failure
1311   then
1312
1313   \ Try all default kernel names
1314   flags kernel args 1+ try_multiple_kernels
1315 ;
1316
1317 \ Try to load a kernel; the kernel name is taken from one of
1318 \ the following lists, as ordered:
1319 \
1320 \   1. The "bootfile" environment variable
1321 \   2. The "kernel" environment variable
1322 \
1323 \ Flags are passed, if provided.
1324 \
1325 \ The kernel will be loaded from a directory computed from the
1326 \ path given. Two directories will be tried in the following order:
1327 \
1328 \   1. /boot/path
1329 \   2. path
1330 \
1331 \ The module_path variable is overridden if load is successful, by
1332 \ prepending the successful path.
1333
1334 : load_from_directory ( path len 1 | flags len' path len 2 -- flag )
1335   local args
1336   2local path
1337   args 1 = if 0 0 then
1338   2local flags
1339   0 0 2local oldmodulepath \ like a string
1340   0 0 2local newmodulepath \ like a string
1341   end-locals
1342
1343   \ Set the environment variable module_path, and try loading
1344   \ the kernel again.
1345   modulepath getenv saveenv to oldmodulepath
1346
1347   \ Try prepending /boot/ first
1348   bootpath nip path nip +       \ total length
1349   oldmodulepath nip dup -1 = if
1350     drop
1351   else
1352     1+ +                        \ add oldpath -- XXX why the 1+ ?
1353   then
1354   allocate if ( out of memory ) 1 exit then \ XXX throw ?
1355
1356   0
1357   bootpath strcat
1358   path strcat
1359   2dup to newmodulepath
1360   modulepath setenv
1361
1362   \ Try all default kernel names
1363   flags args 1- load_a_kernel
1364   0= if ( success )
1365     oldmodulepath nip -1 <> if
1366       newmodulepath s" ;" strcat
1367       oldmodulepath strcat
1368       modulepath setenv
1369       newmodulepath drop free-memory
1370       oldmodulepath drop free-memory
1371     then
1372     0 exit
1373   then
1374
1375   \ Well, try without the prepended /boot/
1376   path newmodulepath drop swap move
1377   newmodulepath drop path nip
1378   2dup to newmodulepath
1379   modulepath setenv
1380
1381   \ Try all default kernel names
1382   flags args 1- load_a_kernel
1383   if ( failed once more )
1384     oldmodulepath restoreenv
1385     newmodulepath drop free-memory
1386     1
1387   else
1388     oldmodulepath nip -1 <> if
1389       newmodulepath s" ;" strcat
1390       oldmodulepath strcat
1391       modulepath setenv
1392       newmodulepath drop free-memory
1393       oldmodulepath drop free-memory
1394     then
1395     0
1396   then
1397 ;
1398
1399 \ Try to load a kernel; the kernel name is taken from one of
1400 \ the following lists, as ordered:
1401 \
1402 \   1. The "bootfile" environment variable
1403 \   2. The "kernel" environment variable
1404 \   3. The "path" argument
1405 \
1406 \ Flags are passed, if provided.
1407 \
1408 \ The kernel will be loaded from a directory computed from the
1409 \ path given. Two directories will be tried in the following order:
1410 \
1411 \   1. /boot/path
1412 \   2. path
1413 \
1414 \ Unless "path" is meant to be kernel name itself. In that case, it
1415 \ will first be tried as a full path, and, next, search on the
1416 \ directories pointed by module_path.
1417 \
1418 \ The module_path variable is overridden if load is successful, by
1419 \ prepending the successful path.
1420
1421 : load_directory_or_file ( path len 1 | flags len' path len 2 -- flag )
1422   local args
1423   2local path
1424   args 1 = if 0 0 then
1425   2local flags
1426   end-locals
1427
1428   \ First, assume path is an absolute path to a directory
1429   flags path args clip_args load_from_directory
1430   dup 0= if exit else drop then
1431
1432   \ Next, assume path points to the kernel
1433   flags path args try_multiple_kernels
1434 ;
1435
1436 : initialize  ( addr len -- )
1437   strdup conf_files strset
1438 ;
1439
1440 : kernel_options ( -- addr len 1 | 0 )
1441   s" kernel_options" getenv
1442   dup -1 = if drop 0 else 1 then
1443 ;
1444
1445 : standard_kernel_search  ( flags 1 | 0 -- flag )
1446   local args
1447   args 0= if 0 0 then
1448   2local flags
1449   s" kernel" getenv
1450   dup -1 = if 0 swap then
1451   2local path
1452   end-locals
1453
1454   path nip -1 = if ( there isn't a "kernel" environment variable )
1455     flags args load_a_kernel
1456   else
1457     flags path args 1+ clip_args load_directory_or_file
1458   then
1459 ;
1460
1461 : load_kernel  ( -- ) ( throws: abort )
1462   kernel_options standard_kernel_search
1463   abort" Unable to load a kernel!"
1464 ;
1465
1466 : load_xen ( -- flag )
1467   s" xen_kernel" getenv dup -1 <> if
1468     1 1 load ( c-addr/u flag N -- flag )
1469   else
1470     drop
1471     0 ( -1 -- flag )
1472   then
1473 ;
1474
1475 : load_xen_throw ( -- ) ( throws: abort )
1476   load_xen
1477   abort" Unable to load Xen!"
1478 ;
1479
1480 : set_defaultoptions  ( -- )
1481   s" kernel_options" getenv dup -1 = if
1482     drop
1483   else
1484     s" temp_options" setenv
1485   then
1486 ;
1487
1488 \ pick the i-th argument, i starts at 0
1489 : argv[]  ( aN uN ... a1 u1 N i -- aN uN ... a1 u1 N ai+1 ui+1 )
1490   2dup = if 0 0 exit then       \ out of range
1491   dup >r
1492   1+ 2* ( skip N and ui )
1493   pick
1494   r>
1495   1+ 2* ( skip N and ai )
1496   pick
1497 ;
1498
1499 : drop_args  ( aN uN ... a1 u1 N -- )
1500   0 ?do 2drop loop
1501 ;
1502
1503 : argc
1504   dup
1505 ;
1506
1507 : queue_argv  ( aN uN ... a1 u1 N a u -- a u aN uN ... a1 u1 N+1 )
1508   >r
1509   over 2* 1+ -roll
1510   r>
1511   over 2* 1+ -roll
1512   1+
1513 ;
1514
1515 : unqueue_argv  ( aN uN ... a1 u1 N -- aN uN ... a2 u2 N-1 a1 u1 )
1516   1- -rot
1517 ;
1518
1519 \ compute the length of the buffer including the spaces between words
1520 : strlen(argv) ( aN uN .. a1 u1 N -- aN uN .. a1 u1 N len )
1521   dup 0= if 0 exit then
1522   0 >r  \ Size
1523   0 >r  \ Index
1524   begin
1525     argc r@ <>
1526   while
1527     r@ argv[]
1528     nip
1529     r> r> rot + 1+
1530     >r 1+ >r
1531   repeat
1532   r> drop
1533   r>
1534 ;
1535
1536 : concat_argv  ( aN uN ... a1 u1 N -- a u )
1537   strlen(argv) allocate if ENOMEM throw then
1538   0 2>r ( save addr 0 on return stack )
1539
1540   begin
1541     dup
1542   while
1543     unqueue_argv ( ... N a1 u1 )
1544     2r> 2swap    ( old a1 u1 )
1545     strcat
1546     s"  " strcat ( append one space ) \ XXX this gives a trailing space
1547     2>r         ( store string on the result stack )
1548   repeat
1549   drop_args
1550   2r>
1551 ;
1552
1553 : set_tempoptions  ( addrN lenN ... addr1 len1 N -- addr len 1 | 0 )
1554   \ Save the first argument, if it exists and is not a flag
1555   argc if
1556     0 argv[] drop c@ [char] - <> if
1557       unqueue_argv 2>r  \ Filename
1558       1 >r              \ Filename present
1559     else
1560       0 >r              \ Filename not present
1561     then
1562   else
1563     0 >r                \ Filename not present
1564   then
1565
1566   \ If there are other arguments, assume they are flags
1567   ?dup if
1568     concat_argv
1569     2dup s" temp_options" setenv
1570     drop free if EFREE throw then
1571   else
1572     set_defaultoptions
1573   then
1574
1575   \ Bring back the filename, if one was provided
1576   r> if 2r> 1 else 0 then
1577 ;
1578
1579 : get_arguments ( -- addrN lenN ... addr1 len1 N )
1580   0
1581   begin
1582     \ Get next word on the command line
1583     parse-word
1584   ?dup while
1585     queue_argv
1586   repeat
1587   drop ( empty string )
1588 ;
1589
1590 : load_kernel_and_modules  ( args -- flag )
1591   set_tempoptions
1592   argc >r
1593   s" temp_options" getenv dup -1 <> if
1594     queue_argv
1595   else
1596     drop
1597   then
1598   load_xen
1599   ?dup 0= if ( success )
1600     r> if ( a path was passed )
1601       load_directory_or_file
1602     else
1603       standard_kernel_search
1604     then
1605     ?dup 0= if ['] load_modules catch then
1606   then
1607 ;
1608
1609 only forth definitions