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