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