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