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