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