]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/forth/support.4th
amd64: use register macros for gdb_cpu_getreg()
[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   s" nextboot_enable" getenv dup -1 <> if
1061     2dup s' "YES"' compare >r
1062     2dup s' "yes"' compare >r
1063     2dup s" YES" compare >r
1064     2dup s" yes" compare r> r> r> and and and 0= to nextboot?
1065   else
1066     drop
1067     get_nextboot_conf_file
1068     ['] peek_file catch if 2drop then
1069   then
1070   nextboot? if
1071     get_nextboot_conf_file
1072     current_file_name_ref strref
1073     ['] load_conf catch
1074     process_conf_errors
1075     ['] rewrite_nextboot_file catch if 2drop then
1076   then
1077   s' "NO"' s" nextboot_enable" setenv
1078 ;
1079
1080 \ Module loading functions
1081
1082 : load_parameters  { addr -- addr addrN lenN ... addr1 len1 N }
1083   addr
1084   addr module.args strget
1085   addr module.loadname .len @ if
1086     addr module.loadname strget
1087   else
1088     addr module.name strget
1089   then
1090   addr module.type .len @ if
1091     addr module.type strget
1092     s" -t "
1093     4 ( -t type name flags )
1094   else
1095     2 ( name flags )
1096   then
1097 ;
1098
1099 : before_load  ( addr -- addr )
1100   dup module.beforeload .len @ if
1101     dup module.beforeload strget
1102     ['] evaluate catch if EBEFORELOAD throw then
1103   then
1104 ;
1105
1106 : after_load  ( addr -- addr )
1107   dup module.afterload .len @ if
1108     dup module.afterload strget
1109     ['] evaluate catch if EAFTERLOAD throw then
1110   then
1111 ;
1112
1113 : load_error  ( addr -- addr )
1114   dup module.loaderror .len @ if
1115     dup module.loaderror strget
1116     evaluate  \ This we do not intercept so it can throw errors
1117   then
1118 ;
1119
1120 : pre_load_message  ( addr -- addr )
1121   verbose? if
1122     dup module.name strtype
1123     ." ..."
1124   then
1125 ;
1126
1127 : load_error_message verbose? if ." failed!" cr then ;
1128
1129 : load_successful_message verbose? if ." ok" cr then ;
1130
1131 : load_module
1132   load_parameters load
1133 ;
1134
1135 : process_module  ( addr -- addr )
1136   pre_load_message
1137   before_load
1138   begin
1139     ['] load_module catch if
1140       dup module.loaderror .len @ if
1141         load_error                      \ Command should return a flag!
1142       else 
1143         load_error_message true         \ Do not retry
1144       then
1145     else
1146       after_load
1147       load_successful_message true      \ Successful, do not retry
1148     then
1149   until
1150 ;
1151
1152 : process_module_errors  ( addr ior -- )
1153   dup EBEFORELOAD = if
1154     drop
1155     ." Module "
1156     dup module.name strtype
1157     dup module.loadname .len @ if
1158       ." (" dup module.loadname strtype ." )"
1159     then
1160     cr
1161     ." Error executing "
1162     dup module.beforeload strtype cr    \ XXX there was a typo here
1163     abort
1164   then
1165
1166   dup EAFTERLOAD = if
1167     drop
1168     ." Module "
1169     dup module.name .addr @ over module.name .len @ type
1170     dup module.loadname .len @ if
1171       ." (" dup module.loadname strtype ." )"
1172     then
1173     cr
1174     ." Error executing "
1175     dup module.afterload strtype cr
1176     abort
1177   then
1178
1179   throw  \ Don't know what it is all about -- pass ahead
1180 ;
1181
1182 \ Module loading interface
1183
1184 \ scan the list of modules, load enabled ones.
1185 : load_modules  ( -- ) ( throws: abort & user-defined )
1186   module_options @      ( list_head )
1187   begin
1188     ?dup
1189   while
1190     dup module.flag @ if
1191       ['] process_module catch
1192       process_module_errors
1193     then
1194     module.next @
1195   repeat
1196 ;
1197
1198 \ h00h00 magic used to try loading either a kernel with a given name,
1199 \ or a kernel with the default name in a directory of a given name
1200 \ (the pain!)
1201
1202 : bootpath s" /boot/" ;
1203 : modulepath s" module_path" ;
1204
1205 \ Functions used to save and restore module_path's value.
1206 : saveenv ( addr len | -1 -- addr' len | 0 -1 )
1207   dup -1 = if 0 swap exit then
1208   strdup
1209 ;
1210 : freeenv ( addr len | 0 -1 )
1211   -1 = if drop else free abort" Freeing error" then
1212 ;
1213 : restoreenv  ( addr len | 0 -1 -- )
1214   dup -1 = if ( it wasn't set )
1215     2drop
1216     modulepath unsetenv
1217   else
1218     over >r
1219     modulepath setenv
1220     r> free abort" Freeing error"
1221   then
1222 ;
1223
1224 : clip_args   \ Drop second string if only one argument is passed
1225   1 = if
1226     2swap 2drop
1227     1
1228   else
1229     2
1230   then
1231 ;
1232
1233 also builtins
1234
1235 \ Parse filename from a semicolon-separated list
1236
1237 \ replacement, not working yet
1238 : newparse-; { addr len | a1 -- a' len-x addr x }
1239   addr len [char] ; strchr dup if       ( a1 len1 )
1240     swap to a1  ( store address )
1241     1 - a1 @ 1 + swap ( remove match )
1242     addr a1 addr -
1243   else
1244     0 0 addr len
1245   then
1246 ;
1247
1248 : parse-; ( addr len -- addr' len-x addr x )
1249   over 0 2swap                  ( addr 0 addr len )
1250   begin
1251     dup 0 <>                    ( addr 0 addr len )
1252   while
1253     over c@ [char] ; <>         ( addr 0 addr len flag )
1254   while
1255     1- swap 1+ swap
1256     2swap 1+ 2swap
1257   repeat then
1258   dup 0 <> if
1259     1- swap 1+ swap
1260   then
1261   2swap
1262 ;
1263
1264 \ Try loading one of multiple kernels specified
1265
1266 : try_multiple_kernels ( addr len addr' len' args -- flag )
1267   >r
1268   begin
1269     parse-; 2>r
1270     2over 2r>
1271     r@ clip_args
1272     s" DEBUG" getenv? if
1273       s" echo Module_path: ${module_path}" evaluate
1274       ." Kernel     : " >r 2dup type r> cr
1275       dup 2 = if ." Flags      : " >r 2over type r> cr then
1276     then
1277     1 load
1278   while
1279     dup 0=
1280   until
1281     1 >r \ Failure
1282   else
1283     0 >r \ Success
1284   then
1285   2drop 2drop
1286   r>
1287   r> drop
1288 ;
1289
1290 \ Try to load a kernel; the kernel name is taken from one of
1291 \ the following lists, as ordered:
1292 \
1293 \   1. The "bootfile" environment variable
1294 \   2. The "kernel" environment variable
1295 \
1296 \ Flags are passed, if available. If not, dummy values must be given.
1297 \
1298 \ The kernel gets loaded from the current module_path.
1299
1300 : load_a_kernel ( flags len 1 | x x 0 -- flag )
1301   local args
1302   2local flags
1303   0 0 2local kernel
1304   end-locals
1305
1306   \ Check if a default kernel name exists at all, exits if not
1307   s" bootfile" getenv dup -1 <> if
1308     to kernel
1309     flags kernel args 1+ try_multiple_kernels
1310     dup 0= if exit then
1311   then
1312   drop
1313
1314   s" kernel" getenv dup -1 <> if
1315     to kernel
1316   else
1317     drop
1318     1 exit \ Failure
1319   then
1320
1321   \ Try all default kernel names
1322   flags kernel args 1+ try_multiple_kernels
1323 ;
1324
1325 \ Try to load a kernel; the kernel name is taken from one of
1326 \ the following lists, as ordered:
1327 \
1328 \   1. The "bootfile" environment variable
1329 \   2. The "kernel" environment variable
1330 \
1331 \ Flags are passed, if provided.
1332 \
1333 \ The kernel will be loaded from a directory computed from the
1334 \ path given. Two directories will be tried in the following order:
1335 \
1336 \   1. /boot/path
1337 \   2. path
1338 \
1339 \ The module_path variable is overridden if load is successful, by
1340 \ prepending the successful path.
1341
1342 : load_from_directory ( path len 1 | flags len' path len 2 -- flag )
1343   local args
1344   2local path
1345   args 1 = if 0 0 then
1346   2local flags
1347   0 0 2local oldmodulepath \ like a string
1348   0 0 2local newmodulepath \ like a string
1349   end-locals
1350
1351   \ Set the environment variable module_path, and try loading
1352   \ the kernel again.
1353   modulepath getenv saveenv to oldmodulepath
1354
1355   \ Try prepending /boot/ first
1356   bootpath nip path nip +       \ total length
1357   oldmodulepath nip dup -1 = if
1358     drop
1359   else
1360     1+ +                        \ add oldpath -- XXX why the 1+ ?
1361   then
1362   allocate if ( out of memory ) 1 exit then \ XXX throw ?
1363
1364   0
1365   bootpath strcat
1366   path strcat
1367   2dup to newmodulepath
1368   modulepath setenv
1369
1370   \ Try all default kernel names
1371   flags args 1- load_a_kernel
1372   0= if ( success )
1373     oldmodulepath nip -1 <> if
1374       newmodulepath s" ;" strcat
1375       oldmodulepath strcat
1376       modulepath setenv
1377       newmodulepath drop free-memory
1378       oldmodulepath drop free-memory
1379     then
1380     0 exit
1381   then
1382
1383   \ Well, try without the prepended /boot/
1384   path newmodulepath drop swap move
1385   newmodulepath drop path nip
1386   2dup to newmodulepath
1387   modulepath setenv
1388
1389   \ Try all default kernel names
1390   flags args 1- load_a_kernel
1391   if ( failed once more )
1392     oldmodulepath restoreenv
1393     newmodulepath drop free-memory
1394     1
1395   else
1396     oldmodulepath nip -1 <> if
1397       newmodulepath s" ;" strcat
1398       oldmodulepath strcat
1399       modulepath setenv
1400       newmodulepath drop free-memory
1401       oldmodulepath drop free-memory
1402     then
1403     0
1404   then
1405 ;
1406
1407 \ Try to load a kernel; the kernel name is taken from one of
1408 \ the following lists, as ordered:
1409 \
1410 \   1. The "bootfile" environment variable
1411 \   2. The "kernel" environment variable
1412 \   3. The "path" argument
1413 \
1414 \ Flags are passed, if provided.
1415 \
1416 \ The kernel will be loaded from a directory computed from the
1417 \ path given. Two directories will be tried in the following order:
1418 \
1419 \   1. /boot/path
1420 \   2. path
1421 \
1422 \ Unless "path" is meant to be kernel name itself. In that case, it
1423 \ will first be tried as a full path, and, next, search on the
1424 \ directories pointed by module_path.
1425 \
1426 \ The module_path variable is overridden if load is successful, by
1427 \ prepending the successful path.
1428
1429 : load_directory_or_file ( path len 1 | flags len' path len 2 -- flag )
1430   local args
1431   2local path
1432   args 1 = if 0 0 then
1433   2local flags
1434   end-locals
1435
1436   \ First, assume path is an absolute path to a directory
1437   flags path args clip_args load_from_directory
1438   dup 0= if exit else drop then
1439
1440   \ Next, assume path points to the kernel
1441   flags path args try_multiple_kernels
1442 ;
1443
1444 : initialize  ( addr len -- )
1445   strdup conf_files strset
1446 ;
1447
1448 : kernel_options ( -- addr len 1 | 0 )
1449   s" kernel_options" getenv
1450   dup -1 = if drop 0 else 1 then
1451 ;
1452
1453 : standard_kernel_search  ( flags 1 | 0 -- flag )
1454   local args
1455   args 0= if 0 0 then
1456   2local flags
1457   s" kernel" getenv
1458   dup -1 = if 0 swap then
1459   2local path
1460   end-locals
1461
1462   path nip -1 = if ( there isn't a "kernel" environment variable )
1463     flags args load_a_kernel
1464   else
1465     flags path args 1+ clip_args load_directory_or_file
1466   then
1467 ;
1468
1469 : load_kernel  ( -- ) ( throws: abort )
1470   kernel_options standard_kernel_search
1471   abort" Unable to load a kernel!"
1472 ;
1473
1474 : load_xen ( -- flag )
1475   s" xen_kernel" getenv dup -1 <> if
1476     1 1 load ( c-addr/u flag N -- flag )
1477   else
1478     drop
1479     0 ( -1 -- flag )
1480   then
1481 ;
1482
1483 : load_xen_throw ( -- ) ( throws: abort )
1484   load_xen
1485   abort" Unable to load Xen!"
1486 ;
1487
1488 : set_defaultoptions  ( -- )
1489   s" kernel_options" getenv dup -1 = if
1490     drop
1491   else
1492     s" temp_options" setenv
1493   then
1494 ;
1495
1496 \ pick the i-th argument, i starts at 0
1497 : argv[]  ( aN uN ... a1 u1 N i -- aN uN ... a1 u1 N ai+1 ui+1 )
1498   2dup = if 0 0 exit then       \ out of range
1499   dup >r
1500   1+ 2* ( skip N and ui )
1501   pick
1502   r>
1503   1+ 2* ( skip N and ai )
1504   pick
1505 ;
1506
1507 : drop_args  ( aN uN ... a1 u1 N -- )
1508   0 ?do 2drop loop
1509 ;
1510
1511 : argc
1512   dup
1513 ;
1514
1515 : queue_argv  ( aN uN ... a1 u1 N a u -- a u aN uN ... a1 u1 N+1 )
1516   >r
1517   over 2* 1+ -roll
1518   r>
1519   over 2* 1+ -roll
1520   1+
1521 ;
1522
1523 : unqueue_argv  ( aN uN ... a1 u1 N -- aN uN ... a2 u2 N-1 a1 u1 )
1524   1- -rot
1525 ;
1526
1527 \ compute the length of the buffer including the spaces between words
1528 : strlen(argv) ( aN uN .. a1 u1 N -- aN uN .. a1 u1 N len )
1529   dup 0= if 0 exit then
1530   0 >r  \ Size
1531   0 >r  \ Index
1532   begin
1533     argc r@ <>
1534   while
1535     r@ argv[]
1536     nip
1537     r> r> rot + 1+
1538     >r 1+ >r
1539   repeat
1540   r> drop
1541   r>
1542 ;
1543
1544 : concat_argv  ( aN uN ... a1 u1 N -- a u )
1545   strlen(argv) allocate if ENOMEM throw then
1546   0 2>r ( save addr 0 on return stack )
1547
1548   begin
1549     dup
1550   while
1551     unqueue_argv ( ... N a1 u1 )
1552     2r> 2swap    ( old a1 u1 )
1553     strcat
1554     s"  " strcat ( append one space ) \ XXX this gives a trailing space
1555     2>r         ( store string on the result stack )
1556   repeat
1557   drop_args
1558   2r>
1559 ;
1560
1561 : set_tempoptions  ( addrN lenN ... addr1 len1 N -- addr len 1 | 0 )
1562   \ Save the first argument, if it exists and is not a flag
1563   argc if
1564     0 argv[] drop c@ [char] - <> if
1565       unqueue_argv 2>r  \ Filename
1566       1 >r              \ Filename present
1567     else
1568       0 >r              \ Filename not present
1569     then
1570   else
1571     0 >r                \ Filename not present
1572   then
1573
1574   \ If there are other arguments, assume they are flags
1575   ?dup if
1576     concat_argv
1577     2dup s" temp_options" setenv
1578     drop free if EFREE throw then
1579   else
1580     set_defaultoptions
1581   then
1582
1583   \ Bring back the filename, if one was provided
1584   r> if 2r> 1 else 0 then
1585 ;
1586
1587 : get_arguments ( -- addrN lenN ... addr1 len1 N )
1588   0
1589   begin
1590     \ Get next word on the command line
1591     parse-word
1592   ?dup while
1593     queue_argv
1594   repeat
1595   drop ( empty string )
1596 ;
1597
1598 : load_kernel_and_modules  ( args -- flag )
1599   set_tempoptions
1600   argc >r
1601   s" temp_options" getenv dup -1 <> if
1602     queue_argv
1603   else
1604     drop
1605   then
1606   load_xen
1607   ?dup 0= if ( success )
1608     r> if ( a path was passed )
1609       load_directory_or_file
1610     else
1611       standard_kernel_search
1612     then
1613     ?dup 0= if ['] load_modules catch then
1614   then
1615 ;
1616
1617 only forth definitions