The code displayed on this page is not copyrighted by me but by the owners of respective repositories as also mentioned in the headers of the various files.
Executable seed!
Executable seed!
7F 45 4C 46 01 01 01 03 00 00 00 00 00 00 00 00 02 00 03 00 01 00 00 00 54 80 04 08 34 00 00 00 00 00 00 00 00 00 00 00 34 00 20 00 01 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 80 04 08 00 80 04 08 80 02 00 00 80 02 00 00 07 00 00 00 01 00 00 00 58 5B 5B 85 DB 75 06 50 BB 3A 82 04 08 31 C9 6A 05 58 CD 80 85 C0 7E 6C A3 68 82 04 08 58 89 E5 6A 2D 58 31 DB CD 80 A3 78 82 04 08 A1 70 82 04 08 E8 3F 01 00 00 89 C3 89 D9 31 C0 A3 7C 82 04 08 A3 6C 82 04 08 E8 95 00 00 00 85 C0 74 05 89 01 83 C1 04 A1 6C 82 04 08 85 C0 74 E9 39 CB 74 CB E8 4F 00 00 00 8B 03 85 C0 74 18 53 6A 02 58 CD 80 5B 85 C0 7C 0D 75 12 6A 0B 58 89 EA 89 D9 8B 1B CD 80 6A 01 5B 89 D8 CD 80 89 C3 B9 7C 82 04 08 31 D2 6A 07 58 CD 80 A1 7C 82 04 08 85 C0 74 8A B8 48 82 04 08 E8 07 01 00 00 EB D6 31 DB 6A 01 58 CD 80 53 B8 43 82 04 08 E8 F3 00 00 00 8B 03 E8 EC 00 00 00 83 C3 04 6A 20 58 E8 FE 00 00 00 39 CB 75 EA 6A 0A 58 E8 F2 00 00 00 5B C3 53 51 A1 74 82 04 08 E8 89 00 00 00 89 C3 89 C1 E8 9F 00 00 00 3C FC 74 B5 3C 20 74 42 3C 09 74 3E 3C 0A 75 0A 6A 01 58 A3 6C 82 04 08 EB 30 3C 22 75 07 E8 32 00 00 00 EB 25 3C 23 75 0F E8 40 00 00 00 6A 01 58 A3 6C 82 04 08 EB 12 3C 5C 75 07 E8 5E 00 00 00 EB 07 88 01 83 C1 01 EB B1 39 CB 75 02 31 DB 89 D8 59 5B C3 E8 45 00 00 00 3C FC 0F 84 2D FF FF FF 3C 22 74 07 88 01 83 C1 01 EB E8 C3 E8 2C 00 00 00 3C FC 0F 84 14 FF FF FF 3C 0A 75 EF C3 53 51 52 8B 1D 78 82 04 08 01 C3 6A 2D 58 CD 80 A1 78 82 04 08 89 1D 78 82 04 08 5A 59 5B C3 53 51 52 6A FC 58 50 8D 0C 24 8B 1D 68 82 04 08 6A 03 58 6A 01 5A CD 80 58 3C FC 5A 59 5B C3 53 51 89 C3 85 C0 74 12 31 C0 8A 03 85 C0 74 0A E8 08 00 00 00 83 C3 01 EB EE 59 5B C3 53 51 52 50 8D 0C 24 6A 01 5B 6A 04 58 89 DA CD 80 58 5A 59 5B C3 6B 61 65 6D 2E 78 38 36 00 20 2B 3E 20 00 53 75 62 70 72 6F 63 65 73 73 20 65 72 72 6F 72 0A 41 42 4F 52 54 49 4E 47 20 48 41 52 44 0A 00 00 00 00 00 00 00 00 00 00 04 00 00 00 10 00 00 00 00 00 00 00 00 00 00
7F 45 4C 46 01 01 01 03 00 00 00 00 00 00 00 00 02 00 03 00 01 00 00 00 54 80 04 08 34 00 00 00 00 00 00 00 00 00 00 00 34 00 20 00 01 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 80 04 08 00 80 04 08 00 01 00 00 00 01 00 00 07 00 00 00 01 00 00 00 58 5B 5B 31 C9 31 D2 6A 05 58 CD 80 89 C6 5B 66 B9 41 02 66 BA C0 01 6A 05 58 CD 80 89 C2 6A FF 5D 31 FF E8 68 00 00 00 E8 1B 00 00 00 85 C0 7C F2 85 ED 7D 06 89 C7 31 ED EB E8 C1 E7 04 01 F8 4D E8 39 00 00 00 EB DB 3C 23 74 1E 3C 3B 74 1A 3C 30 7C 1F 3C 3A 7C 1F 3C 41 7C 17 3C 47 7C 1C 3C 61 7C 0F 3C 67 7C 12 EB 09 E8 21 00 00 00 3C 0A 75 F7 6A FF 58 C3 2C 30 C3 2C 20 2C 37 C3 89 D3 52 6A 01 5A 50 89 E1 6A 04 58 CD 80 5B 5A C3 52 6A 01 5A 53 89 E1 89 F3 6A 03 58 CD 80 85 C0 74 03 58 5A C3 31 DB 6A 01 58 CD 80
## Copyright (C) 2019 Jeremiah Orians
## Copyright (C) 2022 Andrius Å tikonas
#
# SPDX-License-Identifier: GPL-3.0-or-later
## ELF Header
#:ELF_base
7F 45 4C 46 # e_ident[EI_MAG0-3] ELF's magic number
01 # e_ident[EI_CLASS] Indicating 32 bit
01 # e_ident[EI_DATA] Indicating little endianness
01 # e_ident[EI_VERSION] Indicating original elf
03 # e_ident[EI_OSABI] Set at 3 because FreeBSD is strict
00 # e_ident[EI_ABIVERSION] Set at 0 because no one cares
00 00 00 00 00 00 00 # e_ident[EI_PAD]
02 00 # e_type Indicating Executable
03 00 # e_machine Indicating x86
01 00 00 00 # e_version Indicating original elf
54 80 04 08 # e_entry Address of the entry point
34 00 00 00 # e_phoff Address of program header table
00 00 00 00 # e_shoff Address of section header table
00 00 00 00 # e_flags
34 00 # e_ehsize Indicating our 52 Byte header
20 00 # e_phentsize size of a program header table
01 00 # e_phnum number of entries in program table
00 00 # e_shentsize size of a section header table
00 00 # e_shnum number of entries in section table
00 00 # e_shstrndx index of the section names
## Program Header
#:ELF_program_headers
#:ELF_program_header__text
01 00 00 00 # ph_type: PT-LOAD = 1
00 00 00 00 # ph_offset
00 80 04 08 # ph_vaddr
00 80 04 08 # ph_physaddr
00 01 00 00 # ph_filesz
00 01 00 00 # ph_memsz
07 00 00 00 # ph_flags: PF-X|PF-W|PF-R = 7
01 00 00 00 # ph_align
#:ELF_text
# Where the ELF Header is going to hit
# Simply jump to _start
# Our main function
# :_start ; (0x8048054)
58 ; pop_eax # Get the number of arguments
5B ; pop_ebx # Get the program name
5B ; pop_ebx # Get the actual input name
31C9 ; xor_ecx,ecx # prepare read_only, ecx = 0
31D2 ; xor_edx,edx # Extra sure, edx = 0
6A 05 ; push !5 # prepare to set eax to 5
58 ; pop_eax # the syscall number for open()
CD 80 ; int !0x80 # Now open that damn file
89C6 ; mov_esi,eax # Preserve the file pointer we were given
5B ; pop_ebx # Get the actual output name
66B9 4102 ; mov_cx, @577 # Prepare file as O_WRONLY|O_CREAT|O_TRUNC
66BA C001 ; mov_dx, @448 # Prepare file as RWX for owner only (700 in octal)
6A 05 ; push !5 # prepare to set eax to 5
58 ; pop_eax # the syscall number for open()
CD 80 ; int !0x80 # Now open that damn file
89C2 ; mov_edx,eax # Preserve the file pointer we were given
# Our flag for byte processing
6A FF ; push !-1
5D ; pop_ebp # ebp = -1
# temp storage for the sum
31FF ; xor_edi,edi # edi = 0
#:loop ; (0x8048077)
# Read a byte
E8 68000000 ; call %Read_byte
# process byte
E8 1B000000 ; call %hex
# Deal with -1 values
85C0 ; test_eax,eax
7C F2 ; jl !loop
# deal with toggle
85ED ; test_ebp,ebp # jump if ebp >= 0
7D 06 ; jge !print
# process first byte of pair
89C7 ; mov_edi,eax
31ED ; xor_ebp,ebp # ebp = 0
EB E8 ; jmp !loop
# process second byte of pair
#:print ; (0x804808F)
# update the sum and store in output
C1E7 04 ; shl_edi, !4
01F8 ; add_eax,edi
# flip the toggle
4D ; DEC_EBP # ebp = -1
E8 39000000 ; call %write_byte
EB DB ; jmp !loop
#:hex ; (0x804809C)
# Purge Comment Lines (#)
3C 23 ; cmp_al, !35
74 1E ; je !purge_comment
# Purge Comment Lines (;)
3C 3B ; cmp_al, !59
74 1A ; je !purge_comment
# deal all ascii less than 0
3C 30 ; cmp_al, !48
7C 1F ; jl !ascii_other
# deal with 0-9
3C 3A ; cmp_al, !58
7C 1F ; jl !ascii_num
# deal with all ascii less than A
3C 41 ; cmp_al, !65
7C 17 ; jl !ascii_other
# deal with A-F
3C 47 ; cmp_al, !71
7C 1C ; jl !ascii_high
# deal with all ascii less than a
3C 61 ; cmp_al, !97
7C 0F ; jl !ascii_other
# deal with a-f
3C 67 ; cmp_al, !103
7C 12 ; jl !ascii_low
# The rest that remains needs to be ignored
EB 09 ; jmp !ascii_other
#:purge_comment ; (0x80480BE)
# Read a byte
E8 21000000 ; call %Read_byte
# Loop if not LF
3C 0A ; cmp_al, !10
75 F7 ; jne !purge_comment
# Otherwise return -1
#:ascii_other ; (0x80480C7)
6A FF ; push !-1
58 ; pop_eax # return -1
C3 ; ret
#:ascii_num ; (0x80480CB)
2C 30 ; sub_al, !48
C3 ; ret
#:ascii_low ; (0x80480CE)
2C 20 ; sub_al, !32 # convert to uppercase
#:ascii_high ; (0x80480D0)
2C 37 ; sub_al, !55
C3 ; ret
# Writes byte stored in al
#:write_byte ; (0x80480D3)
# Print our Hex
89D3 ; mov_ebx,edx # Where are we writing to
52 ; push_edx # protect fout
6A 01 ; push !1 # prepare to set edx to 1
5A ; pop_edx # set the size of chars we want
50 ; push_eax # Move output to stack
89E1 ; mov_ecx,esp # What we are writing
6A 04 ; push !4 # prepare to set eax to 4
58 ; pop_eax # the syscall number for write
CD 80 ; int !0x80 # call the Kernel
5B ; pop_ebx # deallocate stack
5A ; pop_edx # restore fout
C3 ; ret
#:Read_byte ; (0x80480E4)
# Attempt to read 1 byte from Input file
52 ; push_edx # protect fout
6A 01 ; push !1 # prepare to set edx to 1
5A ; pop_edx # set the size of chars we want
53 ; push_ebx # allocate stack
89E1 ; mov_ecx,esp # Where to put it
89F3 ; mov_ebx,esi # Where are we reading from
6A 03 ; push !3 # prepare to set eax to 3
58 ; pop_eax # the syscall number for read
CD 80 ; int !0x80 # call the Kernel
85C0 ; test_eax,eax # check what we got
74 03 ; je !Done # Got EOF call it done
# load byte
58 ; pop_eax # load char
5A ; pop_edx # restore fout
C3 ; ret
#:Done ; (0x80480F9)
# program completed Successfully
31DB ; xor_ebx,ebx # All is well, ebx = 0
6A 01 ; push !1
58 ; pop_eax # put the exit syscall number in eax
CD 80 ; int !0x80 # Call it a good day
#:ELF_end
# SPDX-FileCopyrightText: 2020 Jeremiah Orians
# SPDX-FileCopyrightText: 2022 Andrius Å tikonas
#
# SPDX-License-Identifier: GPL-3.0-or-later
## ELF Header
# :ELF_base ; (0x8048000)
7F 45 4C 46 # e_ident[EI_MAG0-3] ELF's magic number
01 # e_ident[EI_CLASS] Indicating 32 bit
01 # e_ident[EI_DATA] Indicating little endianness
01 # e_ident[EI_VERSION] Indicating original elf
03 # e_ident[EI_OSABI] Set at 3 because FreeBSD is strict
00 # e_ident[EI_ABIVERSION] Set at 0 because none cares
00 00 00 00 00 00 00 # e_ident[EI_PAD]
02 00 # e_type Indicating Executable
03 00 # e_machine Indicating x86
01 00 00 00 # e_version Indicating original elf
54 80 04 08 # e_entry Address of the entry point
34 00 00 00 # e_phoff Address of program header table
00 00 00 00 # e_shoff Address of section header table
00 00 00 00 # e_flags
34 00 # e_ehsize Indicating our 52 Byte header
20 00 # e_phentsize size of a program header table
01 00 # e_phnum number of entries in program table
00 00 # e_shentsize size of a section header table
00 00 # e_shnum number of entries in section table
00 00 # e_shstrndx index of the section names
## Program Header
# :ELF_program_headers
# :ELF_program_header__text
01 00 00 00 # ph_type: PT-LOAD = 1
00 00 00 00 # ph_offset
00 80 04 08 # ph_vaddr
00 80 04 08 # ph_physaddr
80 02 00 00 # ph_filesz
80 02 00 00 # ph_memsz
07 00 00 00 # ph_flags: PF-X|PF-W|PF-R = 7
01 00 00 00 # ph_align
# :ELF_text
# :_start ; (0x8048054)
58 ; pop_eax # Get the number of arguments
5B ; pop_ebx # Get the program name
5B ; pop_ebx # Get the actual input name
85DB ; test_ebx,ebx # Check for missing output
75 06 ; jne !_start_out # Have real input
50 ; push_eax # Need to adjust stack
BB 3A820408 ; mov_ebx, &default_file # Use "kaem.x86"
# :_start_out ; (0x8048061)
31C9 ; xor_ecx,ecx # prepare read_only
6A 05 ; push !5
58 ; pop_eax # the syscall number for open()
CD80 ; int !0x80 # Now open that damn file
85C0 ; test_eax,eax # IF NULL We couldn't open the file
7E 6C ; jle !Exit_Failure # Abort hard
A3 68820408 ; mov_[DWORD],eax &script # Set input pointer
58 ; pop_eax # Get start of envp
89E5 ; mov_ebp,esp # Protect envp
6A 2D ; push !45
58 ; pop_eax # the Syscall # for SYS_BRK
31DB ; xor_ebx,ebx # Get current brk
CD80 ; int !0x80 # Let the kernel do the work
A3 78820408 ; mov_[DWORD],eax &MALLOC # Set our malloc pointer
# Where the main work gets done
# Using EBX for tokens and ECX for tokens[i]
# :main_loop ; (0x8048080)
A1 70820408 ; mov_eax,[DWORD] &max_args # Using 256 char* of space
E8 3F010000 ; call %malloc # get it
89C3 ; mov_ebx,eax # set tokens
89D9 ; mov_ecx,ebx # I = 0
31C0 ; xor_eax,eax # Using 0
A3 7C820408 ; mov_[DWORD],eax &status # status = 0
A3 6C820408 ; mov_[DWORD],eax &command_done # command_done = 0
# Using EAX for result and EBX for tokens[i]
# :collect_command ; (0x804809A)
E8 95000000 ; call %collect_token # Get another token
85C0 ; test_eax,eax # if NULL == result
74 05 ; je !collect_command_comment # It is a comment, don't store
8901 ; mov_[ecx],eax # tokens[i] = result
83C1 04 ; add_ecx, !4 # i = i + 1 (adjusted for char* size)
# :collect_command_comment ; (0x80480A8)
A1 6C820408 ; mov_eax,[DWORD] &command_done # Using command_done
85C0 ; test_eax,eax # IF 0 == command_done
74 E9 ; je !collect_command # keep looping
# Deal with line comments
39CB ; cmp_ebx,ecx # if 0 < i
74 CB ; je !main_loop # It was a comment
E8 4F000000 ; call %print_command # print the command
8B03 ; mov_eax,[ebx] # program = tokens[0]
85C0 ; test_eax,eax # IF NULL == program
74 18 ; je !Exit_Failure # Some shit went down, abort
53 ; push_ebx # Protect Tokens
6A 02 ; push !2
58 ; pop_eax # FORKing
CD 80 ; int !0x80 # int f = FORK()
5B ; pop_ebx # Restore Tokens
85C0 ; test_eax,eax # Check fork
7C 0D ; jl !Exit_Failure # IF f == -1 abort hard
75 12 ; jne !collect_command_parent # IF f == 0 it is child
# Deal with child case
6A 0B ; push !11
58 ; pop_eax # EXECVE
89EA ; mov_edx,ebp # third arg = envp
89D9 ; mov_ecx,ebx # second arg = tokens
8B1B ; mov_ebx,[ebx] # program = tokens[0]
CD 80 ; int !0x80 # execve(program, tokens, envp)
# return error
# Exit_Failure function
# Receives nothing
# And aborts hard
# Does NOT return
# :Exit_Failure ; (0x80480D8)
6A 01 ; push !1
5B ; pop_ebx # All is wrong
89D8 ; mov_eax,ebx # put the exit syscall number in eax
CD 80 ; int !0x80 # Call it a bad day
# :collect_command_parent ; (0x80480DF)
89C3 ; mov_ebx,eax # first arg = f
B9 7C820408 ; mov_ecx, &status # second arg = &status
31D2 ; xor_edx,edx # third arg = NULL
6A 07 ; push !7
58 ; pop_eax # WAITPID
CD 80 ; int !0x80 # waitpid(f, &status, 0)
A1 7C820408 ; mov_eax,[DWORD] &status # Using status
85C0 ; test_eax,eax # IF 0 == status
74 8A ; je !main_loop # Loop forever
# Deal with very unhappy case
B8 48820408 ; mov_eax, &hard # Using "Subprocess error\nABORTING HARD\n"
E8 07010000 ; call %File_Print # Print it
EB D6 ; jmp !Exit_Failure # return error
# :Done ; (0x8048102)
# program completed Successfully
31DB ; xor_ebx,ebx # All is well
6A 01 ; push !1
58 ; pop_eax # put the exit syscall number in eax
CD 80 ; int !0x80 # Call it a good day
# print_command function
# Receives tokens[j] in EBX and tokens[i] in ECX
# Modifies EAX
# :print_command ; (0x8048109)
53 ; push_ebx # Protect EBX
B8 43820408 ; mov_eax, &prefix # using " +> "
E8 F3000000 ; call %File_Print # print it
# :print_command_loop ; (0x8048114)
8B03 ; mov_eax,[ebx] # using tokens[j]
E8 EC000000 ; call %File_Print # print it
83C3 04 ; add_ebx, !4 # j = j + 1
6A 20 ; push !32
58 ; pop_eax # using ' '
E8 FE000000 ; call %fputc # print it
39CB ; cmp_ebx,ecx # IF j < i
75 EA ; jne !print_command_loop # otherwise keep looping
6A 0A ; push !10
58 ; pop_eax # using '\n'
E8 F2000000 ; call %fputc # print it
5B ; pop_ebx # Restore EBX
C3 ; ret
# collect_token function
# Receives nothing
# Overwrites EAX
# Uses EAX as C, EBX as token and ECX as token[i]
# :collect_token ; (0x8048134)
53 ; push_ebx # Protect EBX
51 ; push_ecx # Protect ECX
A1 74820408 ; mov_eax,[DWORD] &max_string # Using max_string
E8 89000000 ; call %malloc # allocate space
89C3 ; mov_ebx,eax # token = malloc(max_string)
89C1 ; mov_ecx,eax # i = 0; set token[i]
# :collect_token_loop ; (0x8048144)
E8 9F000000 ; call %fgetc # c = fgetc(input)
3C FC ; cmp_al, !-4 # if C == EOF
74 B5 ; je !Done # We are done
3C 20 ; cmp_al, !32 # IF C == ' '
74 42 ; je !collect_token_done # Space terminates token
3C 09 ; cmp_al, !9 # IF C == '\t'
74 3E ; je !collect_token_done # tab terminates token
3C 0A ; cmp_al, !10 # IF C == '\n'
75 0A ; jne !collect_token_string # otherwise check next
# It is a newline
6A 01 ; push !1
58 ; pop_eax # Using 1
A3 6C820408 ; mov_[DWORD],eax &command_done # Set command_done = TRUE
EB 30 ; jmp !collect_token_done # Be done
# :collect_token_string ; (0x8048163)
3C 22 ; cmp_al, !34 # IF C == '\"'
75 07 ; jne !collect_token_comment # otherwise check next
# It is a RAW STRING
E8 32000000 ; call %collect_string # Get the rest of the string
EB 25 ; jmp !collect_token_done # Be done
# :collect_token_comment ; (0x804816E)
3C 23 ; cmp_al, !35 # IF C == '#'
75 0F ; jne !collect_token_escape # otherwise check next
# It is a line comment
E8 40000000 ; call %collect_comment # Read it all
6A 01 ; push !1
58 ; pop_eax # Using 1
A3 6C820408 ; mov_[DWORD],eax &command_done # Set command_done = TRUE
EB 12 ; jmp !collect_token_done # Be done
# :collect_token_escape ; (0x8048181)
3C 5C ; cmp_al, !92 # IF C == '\\'
75 07 ; jne !collect_token_other # otherwise just store it
# It is an escape char
E8 5E000000 ; call %fgetc # Read the char to drop
EB 07 ; jmp !collect_token_done # Be done
# :collect_token_other ; (0x804818C)
8801 ; mov_[ecx],al # token[i] = C
83C1 01 ; add_ecx, !1 # i = i + 1
EB B1 ; jmp !collect_token_loop # Keep going
# :collect_token_done ; (0x8048193)
39CB ; cmp_ebx,ecx # IF i == 0
75 02 ; jne !collect_token_good # otherwise return the token
31DB ; xor_ebx,ebx # token = NULL
# :collect_token_good ; (0x8048199)
89D8 ; mov_eax,ebx # Return token
59 ; pop_ecx # Restore ECX
5B ; pop_ebx # Restore EBX
C3 ; ret
# collect_string function
# Receives target[index] in ECX
# Modifies EAX
# Uses EAX as C
# :collect_string ; (0x804819E)
E8 45000000 ; call %fgetc # C = fgetc(input)
3C FC ; cmp_al, !-4 # if C == EOF
0F84 2DFFFFFF ; je32 %Exit_Failure # Something went horribly wrong
3C 22 ; cmp_al, !34 # IF C == '\"'
74 07 ; je !collect_string_done # be done
# deal with inside of string
8801 ; mov_[ecx],al # target[index] = C
83C1 01 ; add_ecx, !1 # index = index + 1
EB E8 ; jmp !collect_string # Keep going
# :collect_string_done ; (0x80481B6)
C3 ; ret
# collect_comment function
# Receives nothing
# Modifies EAX
# uses EAX as Int C
# Just throws away everything it reads
# :collect_comment ; (0x80481B7)
E8 2C000000 ; call %fgetc # C = fgetc(input)
3C FC ; cmp_al, !-4 # IF C == EOF
0F84 14FFFFFF ; je32 %Exit_Failure # abort hard
3C 0A ; cmp_al, !10 # IF C == '\n'
75 EF ; jne !collect_comment # otherwise keep looping
C3 ; ret
;; Malloc isn't actually required if the program being built fits in the initial memory
;; However, it doesn't take much to add it.
;; Requires [MALLOC] to be initialized and EAX to have the number of desired bytes
# :malloc ; (0x80481C9)
53 ; push_ebx # Protect EBX
51 ; push_ecx # Protect ECX
52 ; push_edx # Protect EDX
8B1D 78820408 ; mov_ebx,[DWORD] &MALLOC # Using the current pointer
01C3 ; add_ebx,eax # Request the number of desired bytes
6A 2D ; push !45
58 ; pop_eax # the Syscall # for SYS_BRK
CD 80 ; int !0x80 # call the Kernel
A1 78820408 ; mov_eax,[DWORD] &MALLOC # Return pointer
891D 78820408 ; mov_[DWORD],ebx &MALLOC # Update pointer
5A ; pop_edx # Restore EDX
59 ; pop_ecx # Restore ECX
5B ; pop_ebx # Restore EBX
C3 ; ret
# fgetc function
# Loads FILE* from [script]
# Returns -4 (EOF) or char in AL
# :fgetc ; (0x80481E8)
53 ; push_ebx # Protect EBX
51 ; push_ecx # Protect ECX
52 ; push_edx # Protect EDX
6A FC ; push !-4
58 ; pop_eax # Put EOF in eax
50 ; push_eax # Assume bad (If nothing read, value will remain EOF)
8D0C24 ; lea_ecx,[esp] # Get stack address
8B1D 68820408 ; mov_ebx,[DWORD] &script # Where are we reading from
6A 03 ; push !3
58 ; pop_eax # the syscall number for read
6A 01 ; push !1
5A ; pop_edx # set the size of chars we want
CD 80 ; int !0x80 # call the Kernel
58 ; pop_eax # Get either char or EOF
3C FC ; cmp_al, !-4 # Check for EOF
# :fgetc_done ; (0x8048203)
5A ; pop_edx # Restore EDX
59 ; pop_ecx # Restore ECX
5B ; pop_ebx # Restore EBX
C3 ; ret
# File_Print function
# Receives CHAR* in EAX
# calls fputc for every non-null char
# :File_Print ; (0x8048207)
53 ; push_ebx # Protect EBX
51 ; push_ecx # Protect ECX
89C3 ; mov_ebx,eax # Protect S
85C0 ; test_eax,eax # Protect against nulls
74 12 ; je !File_Print_Done # Simply don't try to print them
# :File_Print_Loop ; (0x804820F)
31C0 ; xor_eax,eax # Zero eax
8A03 ; mov_al,[ebx] # Read byte
85C0 ; test_eax,eax # Check for NULL
74 0A ; je !File_Print_Done # Stop at NULL
E8 08000000 ; call %fputc # write it
83C3 01 ; add_ebx, !1 # S = S + 1
EB EE ; jmp !File_Print_Loop # Keep going
# :File_Print_Done ; (0x8048221)
59 ; pop_ecx # Restore ECX
5B ; pop_ebx # Restore EBX
C3 ; ret
# fputc function
# receives CHAR in EAX and load FILE* from stdout
# writes char and returns
# :fputc ; (0x8048224)
53 ; push_ebx # Protect EBX
51 ; push_ecx # Protect ECX
52 ; push_edx # Protect EDX
50 ; push_eax # We are writing eax
8D0C24 ; lea_ecx,[esp] # Get stack address
6A 01 ; push !1
5B ; pop_ebx # Write to target file
6A 04 ; push !4
58 ; pop_eax # the syscall number for write
89DA ; mov_edx,ebx # set the size of chars we want
CD 80 ; int !0x80 # call the Kernel
58 ; pop_eax # Restore stack
5A ; pop_edx # Restore EDX
59 ; pop_ecx # Restore ECX
5B ; pop_ebx # Restore EBX
C3 ; ret
# :default_file ; (0x804823A)
6B61656D2E78383600 # "kaem.x86"
# :prefix ; (0x8048243)
202B3E2000 # " +> "
# :hard ; (0x8048248)
53756270726F63657373206572726F720A41424F5254494E4720484152440A00 # "Subprocess error\nABORTING HARD\n"
# :script ; (0x8048268)
00000000
# :command_done ; (0x804826C)
00000000
# :max_args ; (0x8048270)
00040000
# :max_string ; (0x8048274)
00100000
# :MALLOC ; (0x8048278)
00000000
# :status ; (0x804827C)
00000000
# :ELF_end ; (0x8048280)
### Copyright (C) 2016 Jeremiah Orians
### This file is part of stage0.
###
### stage0 is free software: you can redistribute it and/or modify
### it under the terms of the GNU General Public License as published by
### the Free Software Foundation, either version 3 of the License, or
### (at your option) any later version.
###
### stage0 is distributed in the hope that it will be useful,
### but WITHOUT ANY WARRANTY; without even the implied warranty of
### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
### GNU General Public License for more details.
###
### You should have received a copy of the GNU General Public License
### along with stage0. If not, see <http://www.gnu.org/licenses/>.
## ELF Header
#:ELF_base
7F 45 4C 46 # e_ident[EI_MAG0-3] ELF's magic number
01 # e_ident[EI_CLASS] Indicating 32 bit
01 # e_ident[EI_DATA] Indicating little endianness
01 # e_ident[EI_VERSION] Indicating original elf
03 # e_ident[EI_OSABI] Set at 3 because FreeBSD is strict
00 # e_ident[EI_ABIVERSION] Set at 0 because none cares
00 00 00 00 00 00 00 # e_ident[EI_PAD]
02 00 # e_type Indicating Executable
03 00 # e_machine Indicating x86
01 00 00 00 # e_version Indicating original elf
54 80 04 08 # e_entry Address of the entry point
34 00 00 00 # e_phoff Address of program header table
00 00 00 00 # e_shoff Address of section header table
00 00 00 00 # e_flags
34 00 # e_ehsize Indicating our 52 Byte header
20 00 # e_phentsize size of a program header table
01 00 # e_phnum number of entries in program table
00 00 # e_shentsize size of a section header table
00 00 # e_shnum number of entries in section table
00 00 # e_shstrndx index of the section names
## Program Header
#:ELF_program_headers
#:ELF_program_header__text
01 00 00 00 # ph_type: PT-LOAD = 1
00 00 00 00 # ph_offset
00 80 04 08 # ph_vaddr
00 80 04 08 # ph_physaddr
B1 02 00 00 # ph_filesz
B1 02 00 00 # ph_memsz
07 00 00 00 # ph_flags: PF-X|PF-W|PF-R = 7
01 00 00 00 # ph_align
#:ELF_text
#:_start ; (0x8048054)
58 # POP_EAX ; Get the number of arguments
5B # POP_EBX ; Get the program name
5B # POP_EBX ; Get the actual input name
B9 00000000 # LOADI32_ECX %0 ; prepare read_only
BA 00000000 # LOADI32_EDX %0 ; extra sure
B8 05000000 # LOADI32_EAX %5 ; the syscall number for open()
CD80 # INT_80 ; Now open that damn file
A3 A9820408 # STORE32_Absolute32_eax &fin ; Preserve the file pointer we were given
5B # POP_EBX ; Get the actual output name
B9 41020000 # LOADI32_ECX %577 ; Prepare file as O_WRONLY|O_CREAT|O_TRUNC
BA C0010000 # LOADI32_EDX %448 ; Prepare file as RWX for owner only (700 in octal)
B8 05000000 # LOADI32_EAX %5 ; the syscall number for open()
CD80 # INT_80 ; Now open that damn file
A3 AD820408 # STORE32_Absolute32_eax &fout ; Preserve the file pointer we were given
BD FFFFFFFF # LOADI32_EBP %-1 ; Our flag for byte processing
BE 00000000 # LOADI32_ESI %0 ; temp storage for the sum
BF 00000000 # LOADI32_EDI %0 ; Our starting IP
E8 30000000 # CALLI32 %First_pass ; Process it
; rewind input file
8B1D A9820408 # LOAD32_Absolute32_ebx &fin ; Using our input file
B9 00000000 # LOADI32_ECX %0 ; Offset Zero
BA 00000000 # LOADI32_EDX %0 ; Whence Zero
B8 13000000 # LOADI32_EAX %19 ; lseek
CD80 # INT_80
BD FFFFFFFF # LOADI32_EBP %-1 ; Our flag for byte processing
BE 00000000 # LOADI32_ESI %0 ; temp storage for the sum
BF 00000000 # LOADI32_EDI %0 ; Our starting IP
E8 B8000000 # CALLI32 %Second_pass ; Process it
E9 62010000 # JMP32 %Done
#:First_pass ; (0x80480C8)
E8 69010000 # CALLI32 %Read_byte
; Deal with EOF
83F8 FC # CMPI8_EAX !-4
0F84 4E000000 # JE32 %First_pass_done
; Check for :
83F8 3A # CMPI8_EAX !58
0F85 05000000 # JNE32 %First_pass_0
; Deal with label
E8 A1010000 # CALLI32 %StoreLabel
#:First_pass_0 ; (0x80480E4)
; Check for %
83F8 25 # CMPI8_EAX !37
0F84 2A000000 # JE32 %First_pass_pointer
; Deal with everything else
E8 33000000 # CALLI32 %hex ; Process our char
; Deal with EOF
83F8 FC # CMPI8_EAX !-4
0F84 29000000 # JE32 %First_pass_done
; deal with -1 values
83F8 00 # CMPI8_EAX !0
0F8C C4FFFFFF # JL32 %First_pass
; deal with toggle
83FD 00 # CMPI8_EBP !0
0F84 03000000 # JE32 %First_pass_1
83C7 01 # ADDI8_EDI !1 ; Increment IP
#:First_pass_1 ; (0x8048110)
F7D5 # NOT_EBP
E9 B1FFFFFF # JMP32 %First_pass
#:First_pass_pointer ; (0x8048117)
; Deal with Pointer to label
E8 1A010000 # CALLI32 %Read_byte ; Drop the char
83C7 04 # ADDI8_EDI !4 ; Increment IP
E9 A4FFFFFF # JMP32 %First_pass ; Loop again
#:First_pass_done ; (0x8048124)
C3 # RET
#:hex ; (0x8048125)
; deal with EOF
83F8 FC # CMPI8_EAX !-4
0F84 AE000000 # JE32 %EOF
; deal with line comments starting with #
83F8 23 # CMPI8_EAX !35
0F84 B8000000 # JE32 %ascii_comment
; deal with line comments starting with ;
83F8 3B # CMPI8_EAX !59
0F84 AF000000 # JE32 %ascii_comment
; deal all ascii less than 0
83F8 30 # CMPI8_EAX !48
0F8C A0000000 # JL32 %ascii_other
; deal with 0-9
83F8 3A # CMPI8_EAX !58
0F8C 8B000000 # JL32 %ascii_num
; deal with all ascii less than A
83F8 41 # CMPI8_EAX !65
0F8C 8E000000 # JL32 %ascii_other
; deal with A-F
83F8 47 # CMPI8_EAX !71
0F8C 81000000 # JL32 %ascii_high
;deal with all ascii less than a
83F8 61 # CMPI8_EAX !97
0F8C 7C000000 # JL32 %ascii_other
;deal with a-f
83F8 67 # CMPI8_EAX !103
0F8C 6B000000 # JL32 %ascii_low
; The rest that remains needs to be ignored
E9 6E000000 # JMP32 %ascii_other
#:Second_pass ; (0x804817B)
E8 B6000000 # CALLI32 %Read_byte
; Deal with EOF
83F8 FC # CMPI8_EAX !-4
0F84 52000000 # JE32 %Second_pass_done
; Simply drop the label
83F8 3A # CMPI8_EAX !58
0F85 0A000000 # JNE32 %Second_pass_0
E8 9F000000 # CALLI32 %Read_byte
E9 DFFFFFFF # JMP32 %Second_pass
#:Second_pass_0 ; (0x804819C)
; Deal with pointer
83F8 25 # CMPI8_EAX !37
0F85 0A000000 # JNE32 %Second_pass_1
E8 E3000000 # CALLI32 %StorePointer
E9 CCFFFFFF # JMP32 %Second_pass
#:Second_pass_1 ; (0x80481AF)
; Deal with everything else
E8 71FFFFFF # CALLI32 %hex ; Process our char
; Deal with EOF
83F8 FC # CMPI8_EAX !-4
0F84 1E000000 # JE32 %Second_pass_done
; deal with -1 values
83F8 00 # CMPI8_EAX !0
0F8C B5FFFFFF # JL32 %Second_pass
; deal with toggle
83FD 00 # CMPI8_EBP !0
0F84 3D000000 # JE32 %print
; process first byte of pair
89C6 # COPY_EAX_to_ESI
BD 00000000 # LOADI32_EBP %0
E9 A0FFFFFF # JMP32 %Second_pass
#:Second_pass_done ; (0x80481DB)
C3 # RET
#:EOF ; (0x80481DC)
C3 # RET
#:ascii_num ; (0x80481DD)
83E8 30 # SUBI8_EAX !48
C3 # RET
#:ascii_low ; (0x80481E1)
83E8 57 # SUBI8_EAX !87
C3 # RET
#:ascii_high ; (0x80481E5)
83E8 37 # SUBI8_EAX !55
C3 # RET
#:ascii_other ; (0x80481E9)
B8 FFFFFFFF # LOADI32_EAX %-1
C3 # RET
#:ascii_comment ; (0x80481EF)
E8 42000000 # CALLI32 %Read_byte
83F8 0D # CMPI8_EAX !13
0F84 09000000 # JE32 %ascii_comment_cr
83F8 0A # CMPI8_EAX !10
0F85 E9FFFFFF # JNE32 %ascii_comment
#:ascii_comment_cr ; (0x8048206)
B8 FFFFFFFF # LOADI32_EAX %-1
C3 # RET
; process second byte of pair
#:print ; (0x804820C)
; update the sum and store in output
C1E6 04 # SHLI8_ESI !4
01F0 # ADD_ESI_to_EAX
A2 B1820408 # STORE8_Absolute32_al &table
; flip the toggle
F7D5 # NOT_EBP
; Print our first Hex
BA 01000000 # LOADI32_EDX %1 ; set the size of chars we want
E8 42000000 # CALLI32 %print_chars
83C7 01 # ADDI8_EDI !1 ; Increment IP
E9 51FFFFFF # JMP32 %Second_pass
#:Done ; (0x804822A)
; program completed Successfully
BB 00000000 # LOADI32_EBX %0 ; All is well
B8 01000000 # LOADI32_EAX %1 ; put the exit syscall number in eax
CD80 # INT_80 ; Call it a good day
#:Read_byte ; (0x8048236)
; Attempt to read 1 byte from STDIN
BA 01000000 # LOADI32_EDX %1 ; set the size of chars we want
B9 B1820408 # LOADI32_ECX &table ; Where to put it
8B1D A9820408 # LOAD32_Absolute32_ebx &fin ; Where are we reading from
B8 03000000 # LOADI32_EAX %3 ; the syscall number for read
CD80 # INT_80 ; call the Kernel
85C0 # TEST ; check what we got
0F84 09000000 # JE32 %Read_byte_1 ; Got EOF call it done
; load byte
A0 B1820408 # LOAD8_Absolute32_al &table ; load char
0FB6C0 # MOVZX ; We have to zero extend it to use it
C3 # RET
; Deal with EOF
#:Read_byte_1 ; (0x804825E)
B8 FCFFFFFF # LOADI32_EAX %-4 ; Put EOF in eax
C3 # RET
#:print_chars ; (0x8048264)
B9 B1820408 # LOADI32_ECX &table ; What we are writing
8B1D AD820408 # LOAD32_Absolute32_ebx &fout ; Write to target file
B8 04000000 # LOADI32_EAX %4 ; the syscall number for write
CD80 # INT_80 ; call the Kernel
C3 # RET
#:Get_table_target ; (0x8048277)
E8 BAFFFFFF # CALLI32 %Read_byte ; Get single char label
C1E0 02 # SHLI8_EAX !2 ; Each label in table takes 4 bytes to store
05 B1820408 # ADDI32_EAX &table ; Calculate offset
C3 # RET
#:StoreLabel ; (0x8048285)
E8 EDFFFFFF # CALLI32 %Get_table_target
8938 # STORE32_EDI_into_Address_EAX ; Write out pointer to table
C3 # RET
#:StorePointer ; (0x804828D)
83C7 04 # ADDI8_EDI !4 ; Increment IP
E8 E2FFFFFF # CALLI32 %Get_table_target ; Get address of pointer
8B00 # LOAD32_Address_EAX_into_EAX ; Get pointer
29F8 # SUB_EDI_from_EAX ; target - ip
A3 B1820408 # STORE32_Absolute32_eax &table ; put value in output
BA 04000000 # LOADI32_EDX %4 ; set the size of chars we want
E8 BCFFFFFF # CALLI32 %print_chars
C3 # RET
#:fin ; (0x80482A9)
00000000 # NULL
#:fout ; (0x80482AD)
00000000 # NULL
#:table ; (0x80482B1)
#:ELF_end
;; Copyright (C) 2017 Jeremiah Orians
;; This file is part of stage0.
;;
;; stage0 is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; stage0 is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with stage0. If not, see <http://www.gnu.org/licenses/>.
## ELF Header
7F 45 4C 46 # e_ident[EI_MAG0-3] ELF's magic number
01 # e_ident[EI_CLASS] Indicating 32 bit
01 # e_ident[EI_DATA] Indicating little endianness
01 # e_ident[EI_VERSION] Indicating original elf
03 # e_ident[EI_OSABI] Set at 3 because FreeBSD is strict
00 # e_ident[EI_ABIVERSION] Set at 0 because none cares
00 00 00 00 00 00 00 # e_ident[EI_PAD]
02 00 # e_type Indicating Executable
03 00 # e_machine Indicating x86
01 00 00 00 # e_version Indicating original elf
54 80 04 08 # e_entry Address of the entry point
34 00 00 00 # e_phoff Address of program header table
00 00 00 00 # e_shoff Address of section header table
00 00 00 00 # e_flags
34 00 # e_ehsize Indicating our 52 Byte header
20 00 # e_phentsize size of a program header table
01 00 # e_phnum number of entries in program table
00 00 # e_shentsize size of a section header table
00 00 # e_shnum number of entries in section table
00 00 # e_shstrndx index of the section names
## Program Header
01 00 00 00 # ph_type: PT-LOAD = 1
00 00 00 00 # ph_offset
00 80 04 08 # ph_vaddr
00 80 04 08 # ph_physaddr
7E 05 00 00 # ph_filesz
7E 05 00 00 # ph_memsz
07 00 00 00 # ph_flags: PF-X|PF-W|PF-R = 7
01 00 00 00 # ph_align
;; Register usage:
;; EAX, EDX, ECX, EBX => Temps
;; EDI => IP
;; EBP => MALLOC
;; ESI => HEAD
;; Struct format: (size 24)
;; NEXT => 0
;; TARGET => 8
;; NAME => 16
; Where the ELF Header is going to hit
; Simply jump to _start
; Our main function
#:_start
BB 00000000 ; LOADI32_EBX %0 ; Get current pointer
E8 %x ; CALL32 %malloc ; Get current HEAP
89C3 ; COPY_EAX_to_EBX ; Using current
89C5 ; COPY_EAX_to_EBP ; Setup MALLOC
81C3 0000C000 ; ADDI32_EBX %12582912 ; Create space for temp [12M]
E8 %x ; CALL32 %malloc ; Give ourselves 8192000 bytes to work with
58 ; POP_EAX ; Get the number of arguments
5B ; POP_EBX ; Get the program name
5B ; POP_EBX ; Get the actual input name
B9 00000000 ; LOADI32_ECX %0 ; prepare read_only
BA 00000000 ; LOADI32_EDX %0 ; Really sure
B8 05000000 ; LOADI32_EAX %5 ; the syscall number for open()
CD80 ; INT_80 ; Now open that damn file
A3 6E850408 ; STORE32_Absolute32_eax &Input ; Preserve the file pointer we were given
5B ; POP_EBX ; Get the actual output name
B9 41020000 ; LOADI32_ECX %577 ; Prepare file as O_WRONLY|O_CREAT|O_TRUNC
BA C0010000 ; LOADI32_EDX %448 ; Prepare file as RWX for owner only (700 in octal)
B8 05000000 ; LOADI32_EAX %5 ; the syscall number for open()
CD80 ; INT_80 ; Now open that damn file
83F8 00 ; CMPI8_EAX !0 ; Check for missing output
0F8F %a ; JG32 %_start_out ; Have real input
B8 01000000 ; LOADI32_EAX %1 ; Use stdout
:a #:_start_out
A3 72850408 ; STORE32_Absolute32_eax &Output ; Preserve the file pointer we were given
E8 %I ; CALL32 %ClearScratch ; Zero scratch
B8 FFFFFFFF ; LOADI32_EAX %-1 ; Our flag for byte processing
A3 6A850408 ; STORE32_Absolute32_eax &Flag ; Set
B8 00000000 ; LOADI32_EAX %0 ; temp storage for the sum
A3 66850408 ; STORE32_Absolute32_eax &High ; Set
BF 00800408 ; LOADI32_EDI %0x8048000 ; Our starting IP
BE 00000000 ; LOADI32_ESI %0 ; HEAD = NULL
E8 %b ; CALL32 %First_pass ; Process it
; rewind input file
8B1D 6E850408 ; LOAD32_Absolute32_ebx &Input ; Using our input file
B9 00000000 ; LOADI32_ECX %0 ; Offset Zero
BA 00000000 ; LOADI32_EDX %0 ; Whence Zero
B8 13000000 ; LOADI32_EAX %19 ; lseek
56 ; PUSH_ESI ; Protect HEAD
CD80 ; INT_80
5E ; POP_ESI ; Restore HEAD
B8 FFFFFFFF ; LOADI32_EAX %-1 ; Our flag for byte processing
A3 6A850408 ; STORE32_Absolute32_eax &Flag ; Set
B8 00000000 ; LOADI32_EAX %0 ; temp storage for the sum
A3 66850408 ; STORE32_Absolute32_eax &High ; Set
BF 00800408 ; LOADI32_EDI %0x8048000 ; Our starting IP
E8 %l ; CALL32 %Second_pass ; Process it
E9 %w ; JMP32 %Done
:b #:First_pass
E8 %y ; CALL32 %Read_byte
; Deal with EOF
83F8 FC ; CMPI8_EAX !-4
0F84 %j ; JE32 %First_pass_done
; Check for :
83F8 3A ; CMPI8_EAX !0x3A
0F85 %c ; JNE32 %First_pass_0
; Deal with label
E9 %D ; JMP32 %StoreLabel
:c #:First_pass_0
; Check for !
83F8 21 ; CMPI8_EAX !0x21
0F84 %i ; JE32 %First_pass_pointer
; Check for @
83F8 40 ; CMPI8_EAX !0x40
0F84 %i ; JE32 %First_pass_pointer
; Check for $
83F8 24 ; CMPI8_EAX !0x24
0F84 %i ; JE32 %First_pass_pointer
; Check for %
83F8 25 ; CMPI8_EAX !0x25
0F84 %i ; JE32 %First_pass_pointer
; Check for &
83F8 26 ; CMPI8_EAX !0x26
0F84 %i ; JE32 %First_pass_pointer
; Deal with everything else
E8 %k ; CALL32 %hex ; Process our char
; Deal with EOF
83F8 FC ; CMPI8_EAX !-4
0F84 %j ; JE32 %First_pass_done
; deal with -1 values
83F8 00 ; CMPI8_EAX !0
0F8C %b ; JL32 %First_pass
; deal with toggle
A1 6A850408 ; LOAD32_Absolute32_eax &Flag
83F8 00 ; CMPI8_EAX !0
0F84 %d ; JE32 %First_pass_1
83C7 01 ; ADDI8_EDI !1 ; Increment IP
:d #:First_pass_1
F7D0 ; NOT_EAX
A3 6A850408 ; STORE32_Absolute32_eax &Flag
E9 %b ; JMP32 %First_pass
:e #:Update_Pointer
; Check for !
83F8 21 ; CMPI8_EAX !0x21
0F84 %h ; JE32 %Update_Pointer_1
; Check for @
83F8 40 ; CMPI8_EAX !0x40
0F84 %g ; JE32 %Update_Pointer_2
; Check for $
83F8 24 ; CMPI8_EAX !0x24
0F84 %g ; JE32 %Update_Pointer_2
; Check for %
83F8 25 ; CMPI8_EAX !0x25
0F84 %f ; JE32 %Update_Pointer_4
; Check for &
83F8 26 ; CMPI8_EAX !0x26
0F84 %f ; JE32 %Update_Pointer_4
;; deal with bad input
E8 %R ; CALL32 %fail
:f #:Update_Pointer_4
83C7 02 ; ADDI8_EDI !2 ; Increment IP
:g #:Update_Pointer_2
83C7 01 ; ADDI8_EDI !1 ; Increment IP
:h #:Update_Pointer_1
83C7 01 ; ADDI8_EDI !1 ; Increment IP
C3 ; RET
:i #:First_pass_pointer
; Deal with Pointer to label
E8 %e ; CALL32 %Update_Pointer ; Increment IP
BB 7A850408 ; LOADI32_EBX &table ; Using scratch
E8 %B ; CALL32 %consume_token ; Read token
E8 %I ; CALL32 %ClearScratch ; Throw away token
83F8 3E ; CMPI8_EAX !0x3E ; check for '>'
0F85 %b ; JNE32 %First_pass ; Loop again
;; Deal with %label>label case
BB 7A850408 ; LOADI32_EBX &table ; Write to scratch
E8 %B ; CALL32 %consume_token ; get token
E8 %I ; CALL32 %ClearScratch ; Clean up after ourselves
E9 %b ; JMP32 %First_pass ; Loop again
:j # :First_pass_done
C3 ; RET
:k #:hex
; deal with EOF
83F8 FC ; CMPI8_EAX !-4
0F84 %o ; JE32 %EOF
; deal with line comments starting with #
83F8 23 ; CMPI8_EAX !0x23
0F84 %t ; JE32 %ascii_comment
; deal with line comments starting with ;
83F8 3B ; CMPI8_EAX !0x3B
0F84 %t ; JE32 %ascii_comment
; deal all ascii less than 0
83F8 30 ; CMPI8_EAX !0x30
0F8C %s ; JL32 %ascii_other
; deal with 0-9
83F8 3A ; CMPI8_EAX !0x3A
0F8C %p ; JL32 %ascii_num
; deal with all ascii less than A
83F8 41 ; CMPI8_EAX !0x41
0F8C %s ; JL32 %ascii_other
; deal with A-F
83F8 47 ; CMPI8_EAX !0x47
0F8C %r ; JL32 %ascii_high
;deal with all ascii less than a
83F8 61 ; CMPI8_EAX !0x61
0F8C %s ; JL32 %ascii_other
;deal with a-f
83F8 67 ; CMPI8_EAX !0x67
0F8C %q ; JL32 %ascii_low
; The rest that remains needs to be ignored
E9 %s ; JMP32 %ascii_other
:l #:Second_pass
E8 %y ; CALL32 %Read_byte
; Deal with EOF
83F8 FC ; CMPI8_EAX !-4
0F84 %n ; JE32 %Second_pass_done
; Simply drop the label
83F8 3A ; CMPI8_EAX !0x3A
0F85 %m ; JNE32 %Second_pass_0
BB 7A850408 ; LOADI32_EBX &table ; Using scratch
E8 %B ; CALL32 %consume_token ; Read token
E8 %I ; CALL32 %ClearScratch ; Throw away token
E9 %l ; JMP32 %Second_pass
:m #:Second_pass_0
; Deal with % pointer
83F8 25 ; CMPI8_EAX !0x25
0F84 %M ; JE32 %StorePointer_rel4
; Deal with @ pointer
83F8 40 ; CMPI8_EAX !0x40
0F84 %N ; JE32 %StorePointer_rel2
; Deal with ! pointer
83F8 21 ; CMPI8_EAX !0x21
0F84 %O ; JE32 %StorePointer_rel1
; Deal with & pointer
83F8 26 ; CMPI8_EAX !0x26
0F84 %P ; JE32 %StorePointer_abs4
; Deal with $ pointer
83F8 24 ; CMPI8_EAX !0x24
0F84 %Q ; JE32 %StorePointer_abs2
#:Second_pass_1
; Deal with everything else
E8 %k ; CALL32 %hex ; Process our char
; Deal with EOF
83F8 FC ; CMPI8_EAX !-4
0F84 %n ; JE32 %Second_pass_done
; deal with -1 values
83F8 00 ; CMPI8_EAX !0
0F8C %l ; JL32 %Second_pass
; deal with toggle
8B1D 6A850408 ; LOAD32_Absolute32_ebx &Flag
83FB 00 ; CMPI8_EBX !0
0F84 %v ; JE32 %print
; process first byte of pair
C1E0 04 ; SHLI8_EAX !4
A3 66850408 ; STORE32_Absolute32_eax &High
B8 00000000 ; LOADI32_EAX %0
A3 6A850408 ; STORE32_Absolute32_eax &Flag
E9 %l ; JMP32 %Second_pass
:n #:Second_pass_done
C3 ; RET
:o #:EOF
C3 ; RET
:p #:ascii_num
83E8 30 ; SUBI8_EAX !0x30
C3 ; RET
:q #:ascii_low
83E8 57 ; SUBI8_EAX !0x57
C3 ; RET
:r #:ascii_high
83E8 37 ; SUBI8_EAX !0x37
C3 ; RET
:s #:ascii_other
B8 FFFFFFFF ; LOADI32_EAX %-1
C3 ; RET
:t #:ascii_comment
E8 %y ; CALL32 %Read_byte
83F8 0D ; CMPI8_EAX !0x0D
0F84 %u ; JE32 %ascii_comment_cr
83F8 0A ; CMPI8_EAX !0x0A
0F85 %t ; JNE32 %ascii_comment
:u #:ascii_comment_cr
B8 FFFFFFFF ; LOADI32_EAX %-1
C3 ; RET
; process second byte of pair
:v #:print
; update the sum and store in output
0305 66850408 ; ADD32_Absolute32_eax &High
; Print our first Hex
BA 01000000 ; LOADI32_EDX %1 ; set the size of chars we want
E8 %A ; CALL32 %print_chars
; flip the toggle
A1 6A850408 ; LOAD32_Absolute32_eax &Flag
F7D0 ; NOT_EAX
A3 6A850408 ; STORE32_Absolute32_eax &Flag
83C7 01 ; ADDI8_EDI !1 ; Increment IP
E9 %l ; JMP32 %Second_pass
:w #:Done
; program completed Successfully
BB 00000000 ; LOADI32_EBX %0 ; All is well
B8 01000000 ; LOADI32_EAX %1 ; put the exit syscall number in eax
CD80 ; INT_80 ; Call it a good day
;; Malloc isn't actually required if the program being built fits in the initial memory
;; However, it doesn't take much to add it.
;; Requires a value in EBX
:x #:malloc
B8 2D000000 ; LOADI32_EAX %45 ; the Syscall # for SYS_BRK
56 ; PUSH_ESI ; Protect esi
57 ; PUSH_EDI ; Protect edi
CD80 ; INT_80 ; call the Kernel
5F ; POP_EDI ; Restore edi
5E ; POP_ESI ; Restore esi
C3 ; RET
:y #:Read_byte
52 ; PUSH_EDX ; Protect EDX
51 ; PUSH_ECX ; Protect ECX
53 ; PUSH_EBX ; Protect EBX
B8 FCFFFFFF ; LOADI32_EAX %-4 ; Put EOF in eax
50 ; PUSH_EAX ; Assume bad (If nothing read, value will remain EOF)
8D0C24 ; LEA32_ECX_from_esp ; Get stack address
8B1D 6E850408 ; LOAD32_Absolute32_ebx &Input ; Where are we reading from
B8 03000000 ; LOADI32_EAX %3 ; the syscall number for read
BA 01000000 ; LOADI32_EDX %1 ; set the size of chars we want
CD80 ; INT_80 ; call the Kernel
85C0 ; TEST ; check what we got
58 ; POP_EAX ; Get either char or EOF
0F84 %z ; JE32 %Read_byte_1 ; Got EOF call it done
0FB6C0 ; MOVZX_al ; We have to zero extend it to use it
5B ; POP_EBX ; Restore EBX
59 ; POP_ECX ; Restore ECX
5A ; POP_EDX ; Restore EDX
C3 ; RET
; Deal with EOF
:z #:Read_byte_1
5B
59
5A
C3 ; RET
:A #:print_chars
52 ; PUSH_EDX ; Protect EDX
51 ; PUSH_ECX ; protect ECX
53 ; PUSH_EBX ; protect EBX
50 ; PUSH_EAX ; We are writing eax
8D0C24 ; LEA32_ECX_from_esp ; Get stack address
8B1D 72850408 ; LOAD32_Absolute32_ebx &Output ; Write to target file
B8 04000000 ; LOADI32_EAX %4 ; the syscall number for write
;; EDX contains the number of bytes to write
CD80 ; INT_80 ; call the Kernel
58 ; POP_EAX ; Restore stack
5B ; POP_EBX ; Restore EBX
59 ; POP_ECX ; Restore ECX
5A ; POP_EDX ; Restore EDX
C3 ; RET
;; Receives pointer in EBX
;; Writes out char and updates EBX
:B #:consume_token
E8 %y ; CALL32 %Read_byte ; Consume_token
; Check for \t
83F8 09 ; CMPI8_EAX !0x09
0F84 %C ; JE32 %consume_token_done
; Check for \n
83F8 0A ; CMPI8_EAX !0x0A
0F84 %C ; JE32 %consume_token_done
; Check for ' '
83F8 20 ; CMPI8_EAX !0x20
0F84 %C ; JE32 %consume_token_done
; Check for '>'
83F8 3E ; CMPI8_EAX !0x3E
0F84 %C ; JE32 %consume_token_done
;; Looks like we are still reading token
8803 ; STORE8_al_into_Address_EBX ; Store char
83C3 01 ; ADDI8_EBX !1 ; Point to next spot
E9 %B ; JMP32 %consume_token ; loop until done
:C #:consume_token_done
B9 00000000 ; LOADI32_ECX %0 ; Pad with nulls
890B ; STORE32_ECX_into_Address_EBX
83C3 04 ; ADDI8_EBX !4
C3 ; RET
:D #:StoreLabel
89E8 ; COPY_EBP_to_EAX ; ENTRY
83C5 18 ; ADDI8_EBP !24 ; CALLOC
8978 08 ; STORE32_EDI_into_Address_EAX_Immediate8 !8 ; ENTRY->TARGET = IP
8930 ; STORE32_ESI_into_Address_EAX ; ENTRY->NEXT = JUMP_TABLE
89C6 ; COPY_EAX_to_ESI ; JUMP_TABLE = ENTRY
896E 10 ; STORE32_EBP_into_Address_ESI_Immediate8 !16 ; ENTRY->NAME = TOKEN
89EB ; COPY_EBP_to_EBX ; Write Starting after struct
E8 %B ; CALL32 %consume_token ; Collect whole string
89DD ; COPY_EBX_to_EBP ; Update HEAP
E9 %b ; JMP32 %First_pass
:E #:GetTarget
53 ; PUSH_EBX ; protect ebx
51 ; PUSH_ECX ; protect ecx
52 ; PUSH_EDX ; protect edx
56 ; PUSH_ESI ; protect JUMP_TABLE
B9 7A850408 ; LOADI32_ECX &table ; Reset scratch
8B56 10 ; LOAD32_EDX_from_ESI_Immediate8 !16 ; I->NAME
:F #:GetTarget_loop
8A01 ; LOAD8_al_from_ECX ; I->NAME[0]
8A1A ; LOAD8_bl_from_EDX ; scratch[0]
0FB6DB ; MOVZX_bl ; Zero extend
0FB6C0 ; MOVZX_al ; Zero extend
38D8 ; CMP_al_bl ; IF TOKEN == I->NAME
0F85 %G ; JNE32 %GetTarget_miss ; Oops
83C1 01 ; ADDI8_ECX !1
83C2 01 ; ADDI8_EDX !1
3C 00 ; CMPI8_al !0
0F85 %F ; JNE32 %GetTarget_loop ; Loop until
E9 %H ; JMP32 %GetTarget_done ; Match
;; Miss
:G #:GetTarget_miss
8B36 ; LOAD32_ESI_from_ESI ; I = I->NEXT
83FE 00 ; CMPI8_ESI !0 ; IF NULL == I
0F84 %R ; JE32 %fail ; Abort hard
8B56 10 ; LOAD32_EDX_from_ESI_Immediate8 !16 ; I->NAME
B9 7A850408 ; LOADI32_ECX &table ; Reset scratch
E9 %F ; JMP32 %GetTarget_loop
:H #:GetTarget_done
8B46 08 ; LOAD32_EAX_from_ESI_Immediate8 !8 ; Get address
5E ; POP_ESI ; Restore JUMP_TABLE
5A ; POP_EDX ; Restore EDX
59 ; POP_ECX ; Restore ECX
5B ; POP_EBX ; Restore EBX
C3 ; RET
:I #:ClearScratch
50 ; PUSH_EAX ; Protect against changes
53 ; PUSH_EBX ; And overwrites
51 ; PUSH_ECX ; While we work
BB 7A850408 ; LOADI32_EBX &table ; Where our table is
B8 00000000 ; LOADI32_EAX %0 ; Using null
:J #:ClearScratch_loop
8B0B ; LOAD32_ECX_from_EBX ; Get current value
8803 ; STORE8_al_into_Address_EBX ; Because we want null
83C3 01 ; ADDI8_EBX !1 ; Increment
83F9 00 ; CMPI8_ECX !0 ; Check if we hit null
0F85 %J ; JNE32 %ClearScratch_loop ; Keep looping
59 ; POP_ECX ; Restore
5B ; POP_EBX ; Damage
58 ; POP_EAX ; Entirely
C3 ; RET
:K #:StorePointer
E8 %e ; CALL32 %Update_Pointer ; Increment IP
BB 7A850408 ; LOADI32_EBX &table ; Write to scratch
E8 %B ; CALL32 %consume_token ; get token
50 ; PUSH_EAX ; Protect base_sep_p
B8 7A850408 ; LOADI32_EAX &table ; Pointer to scratch
E8 %E ; CALL32 %GetTarget ; Get address of pointer
E8 %I ; CALL32 %ClearScratch ; Clean up after ourselves
89FA ; COPY_EDI_to_EDX ; base = IP
5B ; POP_EBX ; Restore base_sep_p
83FB 3E ; CMPI8_EBX !0x3E ; If base_sep_p == '>'
0F85 %L ; JNE32 %StorePointer_done ; If not
;; Deal with %label>label case
50 ; PUSH_EAX ; We need to preserve main target
BB 7A850408 ; LOADI32_EBX &table ; Write to scratch
E8 %B ; CALL32 %consume_token ; get token
B8 7A850408 ; LOADI32_EAX &table ; Pointer to scratch
E8 %E ; CALL32 %GetTarget ; Get address of pointer
E8 %I ; CALL32 %ClearScratch ; Clean up after ourselves
89C2 ; COPY_EAX_to_EDX ; Use our new base
58 ; POP_EAX ; Restore main target
:L #:StorePointer_done
C3 ; RET
:M #:StorePointer_rel4
E8 %K ; CALL32 %StorePointer ; Do Common
29D0 ; SUB_EDX_from_EAX ; target - ip
BA 04000000 ; LOADI32_EDX %4 ; set the size of chars we want
E8 %A ; CALL32 %print_chars
E8 %I ; CALL32 %ClearScratch ; Clean up after ourselves
E9 %l ; JMP32 %Second_pass
:N #:StorePointer_rel2
E8 %K ; CALL32 %StorePointer ; Do Common
29D0 ; SUB_EDX_from_EAX ; target - ip
BA 02000000 ; LOADI32_EDX %2 ; set the size of chars we want
E8 %A ; CALL32 %print_chars
E8 %I ; CALL32 %ClearScratch ; Clean up after ourselves
E9 %l ; JMP32 %Second_pass
:O #:StorePointer_rel1
E8 %K ; CALL32 %StorePointer ; Do Common
29D0 ; SUB_EDX_from_EAX ; target - ip
BA 01000000 ; LOADI32_EDX %1 ; set the size of chars we want
E8 %A ; CALL32 %print_chars
E8 %I ; CALL32 %ClearScratch ; Clean up after ourselves
E9 %l ; JMP32 %Second_pass
:P #:StorePointer_abs4
E8 %K ; CALL32 %StorePointer ; Do Common
BA 04000000 ; LOADI32_EDX %4 ; set the size of chars we want
E8 %A ; CALL32 %print_chars
E8 %I ; CALL32 %ClearScratch ; Clean up after ourselves
E9 %l ; JMP32 %Second_pass
:Q #:StorePointer_abs2
E8 %K ; CALL32 %StorePointer ; Do Common
BA 02000000 ; LOADI32_EDX %2 ; set the size of chars we want
E8 %A ; CALL32 %print_chars
E8 %I ; CALL32 %ClearScratch ; Clean up after ourselves
E9 %l ; JMP32 %Second_pass
:R #:fail
; Some shit went wrong
BB 01000000 ; LOADI32_EBX %1 ; All is wrong
B8 01000000 ; LOADI32_EAX %1 ; put the exit syscall number in eax
CD80 ; INT_80 ; Call it a good day
#:High (0x8048566)
00000000 ; NULL
#:Flag (0x804856A)
00000000 ; NULL
#:Input (0x804856E)
00000000 ; NULL
#:Output (0x8048572)
00000000 ; NULL
#:write (0x8048576)
00000000 ; NULL
#:table (0x804857A)
00000000 ; NULL
#:ELF_end
;; Copyright (C) 2019 Jeremiah Orians
;; This file is part of mescc-tools.
;;
;; mescc-tools is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; mescc-tools is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with mescc-tools. If not, see <http://www.gnu.org/licenses/>.
;; Register usage:
;; EBP => OUTPUT
;; EDI => Buffer
;; ESI => INPUT
## ELF Header
:ELF_base
7F 45 4C 46 # e_ident[EI_MAG0-3] ELF's magic number
01 # e_ident[EI_CLASS] Indicating 32 bit
01 # e_ident[EI_DATA] Indicating little endianness
01 # e_ident[EI_VERSION] Indicating original elf
03 # e_ident[EI_OSABI] Set at 3 because FreeBSD is strict
00 # e_ident[EI_ABIVERSION] Set at 0 because none cares
00 00 00 00 00 00 00 # e_ident[EI_PAD]
02 00 # e_type Indicating Executable
03 00 # e_machine Indicating x86
01 00 00 00 # e_version Indicating original elf
&_start # e_entry Address of the entry point
%ELF_program_headers>ELF_base # e_phoff Address of program header table
00 00 00 00 # e_shoff Address of section header table
00 00 00 00 # e_flags
34 00 # e_ehsize Indicating our 52 Byte header
20 00 # e_phentsize size of a program header table
01 00 # e_phnum number of entries in program table
00 00 # e_shentsize size of a section header table
00 00 # e_shnum number of entries in section table
00 00 # e_shstrndx index of the section names
## Program Header
:ELF_program_headers
:ELF_program_header__text
01 00 00 00 # ph_type: PT-LOAD = 1
00 00 00 00 # ph_offset
&ELF_base # ph_vaddr
&ELF_base # ph_physaddr
%ELF_end>ELF_base # ph_filesz
%ELF_end>ELF_base # ph_memsz
07 00 00 00 # ph_flags: PF-X|PF-W|PF-R = 7
01 00 00 00 # ph_align
:ELF_text
; Where the ELF Header is going to hit
; Simply jump to _start
; Our main function
:_start
58 # POP_EAX ; Get the number of arguments
5B # POP_EBX ; Get the program name
5B # POP_EBX ; Get the actual output name
66B9 4102 # LOADI16_CX @577 ; Prepare file as O_WRONLY|O_CREAT|O_TRUNC
66BA 8001 # LOADI16_DX @384 ; Prepare file as RW for owner only (600 in octal)
6A 05 # PUSH !5 ; prepare to set eax to 5
58 # POP_EAX ; the syscall number for open()
CD80 # INT_80 ; Now open that file
89C5 # COPY_EAX_to_EBP ; Preserve the file pointer we were given
6A 2D # PUSH !45
58 # POP_EAX ; the Syscall # for SYS_BRK
31DB # XOR_EBX_EBX ; Get current brk
CD80 # INT_80 ; Let the kernel do the work
89C7 # COPY_EAX_to_EDI ; Set our malloc pointer
6A 2D # PUSH !45
58 # POP_EAX ; the Syscall # for SYS_BRK
89FB # COPY_EDI_to_EBX ; Using current pointer
81C3 00001000 # ADDI32_EBX %0x100000 ; Allocate 1MB
CD80 # INT_80 ; Let the kernel do the work
:core
5B # POP_EBX ; Get the actual input name
85DB # TEST_EBX_EBX ; Check for null string
74 !done # JE8 !done ; Hit null be done
31C9 # XOR_ECX_ECX ; prepare read_only
31D2 # XOR_EDX_EDX ; prevent any interactions
6A 05 # PUSH !5
58 # POP_EAX ; the syscall number for open()
CD80 # INT_80 ; Now open that damn file
89C6 # COPY_EAX_to_ESI ; Protect INPUT
:keep
BA 00001000 # LOADI32_EDX %0x100000 ; set the size of chars we want
89F9 # COPY_EDI_to_ECX ; Where to put it
89F3 # COPY_ESI_to_EBX ; Where are we reading from
6A03 # PUSH !3
58 # POP_EAX ; the syscall number for read
CD80 # INT_80 ; call the Kernel
50 # PUSH_EAX ; Protect the number of bytes read
89C2 # COPY_EAX_to_EDX ; Number of bytes to write
89F9 # COPY_EDI_to_ECX ; What we are writing
89EB # COPY_EBP_to_EBX ; Write to target file
6A 04 # PUSH !4
58 # POP_EAX ; the syscall number for write
CD80 # INT_80 ; call the Kernel
58 # POP_EAX ; Get bytes read
3D 00001000 # CMPI32_EAX %0x100000 ; Check if buffer was fully used
74 !keep # JE8 !keep ; Keep looping if was full
EB !core # JMP8 !core ; Otherwise move to next file
:done
; program completed Successfully
31DB # XOR_EBX_EBX ; All is well
6A 01 # PUSH !1
58 # POP_EAX ; put the exit syscall number in eax
CD80 # INT_80 ; Call it a good day
:ELF_end
### Copyright (C) 2016 Jeremiah Orians ### Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org> ### This file is part of stage0. ### ### stage0 is free software: you can redistribute it and/or modify ### it under the terms of the GNU General Public License as published by ### the Free Software Foundation, either version 3 of the License, or ### (at your option) any later version. ### ### stage0 is distributed in the hope that it will be useful, ### but WITHOUT ANY WARRANTY; without even the implied warranty of ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ### GNU General Public License for more details. ### ### You should have received a copy of the GNU General Public License ### along with stage0. If not, see <http://www.gnu.org/licenses/>. ### stage0's hex2 format ### !<label> 1 byte relative ### $<label> 2 byte address ### @<label> 2 byte relative ### &<label> 4 byte address ### %<label> 4 byte relative ### elf32.hex2: 32 bit elf header in hex2 ### if you wish to use this header, you need to add :ELF_end to the end of your ### M1 or hex2 files. ## ELF Header :ELF_base 7F 45 4C 46 # e_ident[EI_MAG0-3] ELF's magic number 01 # e_ident[EI_CLASS] Indicating 32 bit 01 # e_ident[EI_DATA] Indicating little endianness 01 # e_ident[EI_VERSION] Indicating original elf 03 # e_ident[EI_OSABI] Set at 3 because FreeBSD is strict 00 # e_ident[EI_ABIVERSION] Set at 0 because none cares 00 00 00 00 00 00 00 # e_ident[EI_PAD] 02 00 # e_type Indicating Executable 03 00 # e_machine Indicating x86 01 00 00 00 # e_version Indicating original elf &_start # e_entry Address of the entry point %ELF_program_headers>ELF_base # e_phoff Address of program header table 00 00 00 00 # e_shoff Address of section header table 00 00 00 00 # e_flags 34 00 # e_ehsize Indicating our 52 Byte header 20 00 # e_phentsize size of a program header table 01 00 # e_phnum number of entries in program table 00 00 # e_shentsize size of a section header table 00 00 # e_shnum number of entries in section table 00 00 # e_shstrndx index of the section names ## Program Header :ELF_program_headers :ELF_program_header__text 01 00 00 00 # ph_type: PT-LOAD = 1 00 00 00 00 # ph_offset &ELF_base # ph_vaddr &ELF_base # ph_physaddr %ELF_end>ELF_base # ph_filesz %ELF_end>ELF_base # ph_memsz 07 00 00 00 # ph_flags: PF-X|PF-W|PF-R = 7 01 00 00 00 # ph_align :ELF_text
;; Copyright (C) 2017 Jeremiah Orians
;; This file is part of stage0.
;;
;; stage0 is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; stage0 is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with stage0. If not, see <http://www.gnu.org/licenses/>.
;; Register usage:
;; EAX, ECX, EBX => Temps
;; EDI => MALLOC
;; EBP => HEAD
;; [Output] => Output_file
;; [Input] => Input_file
;; Struct format: (size 32)
;; NEXT => 0
;; TYPE => 8
;; TEXT => 16
;; EXPRESSION => 24
;; Types
;; None => 0
;; MACRO => 1
;; STRING => 2
; Where the ELF Header is going to hit
; Simply jump to _start
; Our main function
:_start
58 ; POP_EAX ; Get the number of arguments
5B ; POP_EBX ; Get the program name
5B ; POP_EBX ; Get the actual input name
B9 00000000 ; LOADI32_ECX %0 ; prepare read_only
B8 05000000 ; LOADI32_EAX %5 ; the syscall number for open()
CD80 ; INT_80 ; Now open that damn file
A3 &Input ; STORE32_Absolute32_eax &Input ; Preserve the file pointer we were given
5B ; POP_EBX ; Get the actual output name
B9 41020000 ; LOADI32_ECX %577 ; Prepare file as O_WRONLY|O_CREAT|O_TRUNC
BA 80010000 ; LOADI32_EDX %384 ; Prepare file as RW for owner only (600 in octal)
B8 05000000 ; LOADI32_EAX %5 ; the syscall number for open()
CD80 ; INT_80 ; Now open that damn file
83F8 00 ; CMPI8_EAX !0 ; Check for missing output
0F8F %_start_out ; JG32 %_start_out ; Have real input
B8 01000000 ; LOADI32_EAX %1 ; Use stdout
:_start_out
A3 &Output ; STORE32_Absolute32_eax &Output ; Preserve the file pointer we were given
B8 2D000000 ; LOADI32_EAX %45 ; the Syscall # for SYS_BRK
BB 00000000 ; LOADI32_EBX %0 ; Get current brk
CD80 ; INT_80 ; Let the kernel do the work
89C7 ; COPY_EAX_to_EDI ; Set our malloc pointer
E8 %Tokenize_Line ; CALL32 %Tokenize_Line ; Get all lines
89E8 ; COPY_EBP_to_EAX ; prepare for Reverse_List
E8 %Reverse_List ; CALL32 %Reverse_List ; Correct order
89C5 ; COPY_EAX_to_EBP ; Update HEAD
E8 %Identify_Macros ; CALL32 %Identify_Macros ; Find the DEFINEs
E8 %Line_Macro ; CALL32 %Line_Macro ; Apply the DEFINEs
E8 %Process_String ; CALL32 %Process_String ; Handle strings
E8 %Eval_Immediates ; CALL32 %Eval_Immediates ; Handle Numbers
E8 %Preserve_Other ; CALL32 %Preserve_Other ; Collect the remaining
E8 %Print_Hex ; CALL32 %Print_Hex ; Output our results
:Done
; program completed Successfully
BB 00000000 ; LOADI32_EBX %0 ; All is well
B8 01000000 ; LOADI32_EAX %1 ; put the exit syscall number in eax
CD80 ; INT_80 ; Call it a good day
;; Tokenize_Line Function
;; Using input file [Input] and Head EBP
;; Creates a linked list of structs
;; Uses EBX for in_set strings, ECX for Int C and EDX for Struct Token* p
:Tokenize_Line
53 ; PUSH_EBX ; Protect EBX
51 ; PUSH_ECX ; Protect ECX
52 ; PUSH_EDX ; Protect EDX
:restart
E8 %fgetc ; CALL32 %fgetc ; Read a char
83F8 FC ; CMPI8_EAX !-4 ; Check for EOF
0F84 %done ; JE32 %done ; File is collected
0FB6C0 ; MOVZX_al ; We have to zero extend it to use it
89C1 ; COPY_EAX_to_ECX ; Protect C
BB &comments ; LOADI32_EBX &comments ; Get pointer to "#;"
E8 %In_Set ; CALL32 %In_Set ; Check for comments
83F8 01 ; CMPI8_EAX !1 ; If comments
0F84 %Purge_LineComment ; JE32 %Purge_LineComment ; try again
89C8 ; COPY_ECX_to_EAX ; put C in place for check
BB &terminators ; LOADI32_EBX &terminators ; Get pointer to "\n\t "
E8 %In_Set ; CALL32 %In_Set ; Check for terminators
83F8 01 ; CMPI8_EAX !1 ; If terminator
0F84 %restart ; JE32 %restart ; try again
B8 20000000 ; LOADI32_EAX %32 ; Malloc the struct P
E8 %malloc ; CALL32 %malloc ; Get pointer to P
89C2 ; COPY_EAX_to_EDX ; Protect P
892A ; STORE32_EBP_into_Address_EDX ; P->NEXT = HEAD
89D5 ; COPY_EDX_to_EBP ; HEAD = P
89C8 ; COPY_ECX_to_EAX ; put C in place for check
BB &string_char ; LOADI32_EBX &string_char ; Get pointer to "\"'"
E8 %In_Set ; CALL32 %In_Set ; Check for string chars
83F8 01 ; CMPI8_EAX !1 ; If string char
0F84 %Store_String ; JE32 %Store_String ; Get string
E8 %Store_Atom ; CALL32 %Store_Atom ; Get whole token
E9 %restart ; JMP32 %restart
:done
5A ; POP_EDX ; Restore EDX
59 ; POP_ECX ; Restore ECX
5B ; POP_EBX ; Restore EBX
C3 ; RET
;; fgetc function
;; Receives FILE* in [Input]
;; Returns -4 (EOF) or char in EAX
:fgetc
52 ; PUSH_EDX ; Protect EDX
51 ; PUSH_ECX ; Protect ECX
53 ; PUSH_EBX ; Protect EBX
B8 FCFFFFFF ; LOADI32_EAX %-4 ; Put EOF in eax
50 ; PUSH_EAX ; Assume bad (If nothing read, value will remain EOF)
8D0C24 ; LEA32_ECX_from_esp ; Get stack address
8B1D &Input ; LOAD32_Absolute32_ebx &Input ; Where are we reading from
B8 03000000 ; LOADI32_EAX %3 ; the syscall number for read
BA 01000000 ; LOADI32_EDX %1 ; set the size of chars we want
CD80 ; INT_80 ; call the Kernel
58 ; POP_EAX ; Get either char or EOF
5B ; POP_EBX ; Restore EBX
59 ; POP_ECX ; Restore ECX
5A ; POP_EDX ; Restore EDX
C3 ; RET
;; Malloc isn't actually required if the program being built fits in the initial memory
;; However, it doesn't take much to add it.
;; Requires EDI to be initialized and EAX to have the number of desired bytes
:malloc
52 ; PUSH_EDX ; Protect EDX
51 ; PUSH_ECX ; Protect ECX
53 ; PUSH_EBX ; Protect EBX
89FB ; COPY_EDI_to_EBX ; Using the current pointer
01C3 ; ADD_EAX_to_EBX ; Request the number of desired bytes
B8 2D000000 ; LOADI32_EAX %45 ; the Syscall # for SYS_BRK
CD80 ; INT_80 ; call the Kernel
89F8 ; COPY_EDI_to_EAX ; Return pointer
89DF ; COPY_EBX_to_EDI ; Update pointer
5B ; POP_EBX ; Restore EBX
59 ; POP_ECX ; Restore ECX
5A ; POP_EDX ; Restore EDX
C3 ; RET
;; Purge_LineComment function
;; Reads chars until LF and jumps to restart
:Purge_LineComment
E8 %fgetc ; CALL32 %fgetc ; Get a char
0FB6C0 ; MOVZX_al ; Zero extend
83F8 0A ; CMPI8_EAX !10 ; While not LF
0F85 %Purge_LineComment ; JNE32 %Purge_LineComment ; Keep reading
E9 %restart ; JMP32 %restart
;; Store_String Function
;; Receives C in ECX, HEAD in EDX and Input file in [Output]
;; Uses EBX for terminator, ECX for C and EDX for string
:Store_String
53 ; PUSH_EBX ; Protect EBX
51 ; PUSH_ECX ; Protect ECX
52 ; PUSH_EDX ; Protect EDX
B8 02000000 ; LOADI32_EAX %2 ; Using TYPE STRING
8942 08 ; STORE32_EAX_into_Address_EDX_Immediate8 !8 ; HEAD->TYPE = STRING
B8 00010000 ; LOADI32_EAX %256 ; Malloc the string
E8 %malloc ; CALL32 %malloc ; Get pointer to P
8942 10 ; STORE32_EAX_into_Address_EDX_Immediate8 !16 ; HEAD->TEXT = STRING
89CB ; COPY_ECX_to_EBX ; Protect terminator
89C2 ; COPY_EAX_to_EDX ; Protect string pointer
:Store_String_Loop
880A ; STORE8_cl_into_Address_EDX ; write byte
E8 %fgetc ; CALL32 %fgetc ; read next char
0FB6C0 ; MOVZX_al ; Zero extend it
89C1 ; COPY_EAX_to_ECX ; Update C
83C2 01 ; ADDI8_EDX !1 ; STRING = STRING + 1
39D9 ; CMP_EBX_ECX ; See if we hit terminator
0F85 %Store_String_Loop ; JNE32 %Store_String_Loop ; Otherwise keep looping
5A ; POP_EDX ; Restore EDX
59 ; POP_ECX ; Restore ECX
5B ; POP_EBX ; Restore EBX
89D0 ; COPY_EDX_to_EAX ; return HEAD
E9 %restart ; JMP32 %restart
;; Store_Atom Function
;; Receives C in ECX, HEAD in EDX and Input file in [Input]
;; Uses EBX for in_set strings, ECX for C and EDX for string
:Store_Atom
53 ; PUSH_EBX ; Protect EBX
51 ; PUSH_ECX ; Protect ECX
52 ; PUSH_EDX ; Protect EDX
B8 00010000 ; LOADI32_EAX %256 ; Malloc the string
E8 %malloc ; CALL32 %malloc ; Get pointer to P
8942 10 ; STORE32_EAX_into_Address_EDX_Immediate8 !16 ; HEAD->TEXT = STRING
BB &terminators ; LOADI32_EBX &terminators ; Get pointer to "\n\t "
89C2 ; COPY_EAX_to_EDX ; Protect string pointer
:Store_Atom_loop
880A ; STORE8_cl_into_Address_EDX ; write byte
E8 %fgetc ; CALL32 %fgetc ; read next char
0FB6C0 ; MOVZX_al ; Zero extend it
89C1 ; COPY_EAX_to_ECX ; Update C
83C2 01 ; ADDI8_EDX !1 ; STRING = STRING + 1
E8 %In_Set ; CALL32 %In_Set ; Check for terminators
83F8 00 ; CMPI8_EAX !0 ; Check for "\n\t "
0F84 %Store_Atom_loop ; JE32 %Store_Atom_loop ; Loop otherwise
5A ; POP_EDX ; Restore EDX
59 ; POP_ECX ; Restore ECX
5B ; POP_EBX ; Restore EBX
89D0 ; COPY_EDX_to_EAX ; return HEAD
C3 ; RET
;; In_Set function
;; Receives Char C in EAX and CHAR* in EBX
;; Returns 1 if true, zero if false in EAX
:In_Set
53 ; PUSH_EBX ; Protect EBX
51 ; PUSH_ECX ; Protect ECX
:In_Set_loop
8A0B ; LOAD8_cl_from_EBX ; Read char
0FB6C9 ; MOVZX_cl ; Zero extend it
39C8 ; CMP_EAX_ECX ; See if they match
0F84 %In_Set_True ; JE32 %In_Set_True ; return true
83F9 00 ; CMPI8_ECX !0 ; Check for NULL
0F84 %In_Set_False ; JE32 %In_Set_False ; return false
83C3 01 ; ADDI8_EBX !1 ; s = s + 1
E9 %In_Set_loop ; JMP32 %In_Set_loop ; Keep looping
:In_Set_True
B8 01000000 ; LOADI32_EAX %1 ; Set True
59 ; POP_ECX ; Restore ECX
5B ; POP_EBX ; Restore EBX
C3 ; RET
:In_Set_False
B8 00000000 ; LOADI32_EAX %0 ; Set FALSE
59 ; POP_ECX ; Restore ECX
5B ; POP_EBX ; Restore EBX
C3 ; RET
;; Char sets
:terminators
0A092000 ; "\n\t "
:comments
3B2300 ; ";#"
:string_char
22 27 00 ; "\"\'"
;; Reverse_List function
;; Receives List in EAX
;; Returns the list reversed in EAX
:Reverse_List
53 ; PUSH_EBX ; Protect EBX
51 ; PUSH_ECX ; Protect ECX
89C3 ; COPY_EAX_to_EBX ; Set HEAD
B8 00000000 ; LOADI32_EAX %0 ; ROOT = NULL
:Reverse_List_Loop
83FB 00 ; CMPI8_EBX !0 ; WHILE HEAD != NULL
0F84 %Reverse_List_Done ; JE32 %Reverse_List_Done ; Stop otherwise
8B0B ; LOAD32_ECX_from_EBX ; NEXT = HEAD->NEXT
8903 ; STORE32_EAX_into_Address_EBX ; HEAD->NEXT = ROOT
89D8 ; COPY_EBX_to_EAX ; ROOT = HEAD
89CB ; COPY_ECX_to_EBX ; HEAD = NEXT
E9 %Reverse_List_Loop ; JMP32 %Reverse_List_Loop ; Keep Going
:Reverse_List_Done
59 ; POP_ECX ; Restore ECX
5B ; POP_EBX ; Restore EBX
C3 ; RET
;; Identify_Macros function
;; Receives List in EAX
;; Updates the list in place; does not modify registers
;; Uses EBX for DEFINE, ECX for I
:Identify_Macros
50 ; PUSH_EAX ; Protect EAX
53 ; PUSH_EBX ; Protect EBX
51 ; PUSH_ECX ; Protect ECX
52 ; PUSH_EDX ; Protect EDX
BB &DEFINE_str ; LOADI32_EBX &DEFINE_str ; Setup define string
89C1 ; COPY_EAX_to_ECX ; I = HEAD
:Identify_Macros_Loop
8B41 10 ; LOAD32_EAX_from_ECX_Immediate8 !16 ; I->TEXT
E8 %match ; CALL32 %match ; IF "DEFINE" == I->TEXT
83F8 00 ; CMPI8_EAX !0 ; Check if match
0F85 %Identify_Macros_Next ; JNE32 %Identify_Macros_Next ; Skip the work
;; Deal with MACRO
B8 01000000 ; LOADI32_EAX %1 ; Using MACRO
8941 08 ; STORE32_EAX_into_Address_ECX_Immediate8 !8 ; I->TYPE = MACRO
8B01 ; LOAD32_EAX_from_ECX ; I->NEXT
8B40 10 ; LOAD32_EAX_from_EAX_Immediate8 !16 ; I->NEXT->TEXT
8941 10 ; STORE32_EAX_into_Address_ECX_Immediate8 !16 ; I->TEXT = I->NEXT->TEXT
8B01 ; LOAD32_EAX_from_ECX ; I->NEXT
8B00 ; LOAD32_EAX_from_EAX ; I->NEXT->NEXT
8B40 10 ; LOAD32_EAX_from_EAX_Immediate8 !16 ; I->NEXT->NEXT->TEXT
8941 18 ; STORE32_EAX_into_Address_ECX_Immediate8 !24 ; I->EXPRESSION = I->NEXT->NEXT->TEXT
8B01 ; LOAD32_EAX_from_ECX ; I->NEXT
8B00 ; LOAD32_EAX_from_EAX ; I->NEXT->NEXT
8B00 ; LOAD32_EAX_from_EAX ; I->NEXT->NEXT->NEXT
8901 ; STORE32_EAX_into_Address_ECX ; I->NEXT = I->NEXT->NEXT->NEXT
:Identify_Macros_Next
8B09 ; LOAD32_ECX_from_ECX ; I = I->NEXT
83F9 00 ; CMPI8_ECX !0 ; Check for NULL
0F85 %Identify_Macros_Loop ; JNE32 %Identify_Macros_Loop ; Keep looping otherwise
5A ; POP_EDX ; Restore EDX
59 ; POP_ECX ; Restore ECX
5B ; POP_EBX ; Restore EBX
58 ; POP_EAX ; Restore EAX
C3 ; RET
:DEFINE_str
444546494E4500 ; "DEFINE"
;; match function
;; Receives CHAR* in EAX and CHAR* in EBX
;; Returns 0 (TRUE) or 1 (FALSE) in EAX
:match
53 ; PUSH_EBX ; Protect EBX
51 ; PUSH_ECX ; Protect ECX
52 ; PUSH_EDX ; Protect EDX
89C1 ; COPY_EAX_to_ECX ; S1 in place
89DA ; COPY_EBX_to_EDX ; S2 in place
:match_Loop
8A01 ; LOAD8_al_from_ECX ; S1[0]
0FB6C0 ; MOVZX_al ; Make it useful
8A1A ; LOAD8_bl_from_EDX ; S2[0]
0FB6DB ; MOVZX_bl ; Make it useful
39D8 ; CMP_EAX_EBX ; See if they match
0F85 %match_False ; JNE32 %match_False ; If not
83C1 01 ; ADDI8_ECX !1 ; S1 = S1 + 1
83C2 01 ; ADDI8_EDX !1 ; S2 = S2 + 1
83F8 00 ; CMPI8_EAX !0 ; If reached end of string
0F84 %match_Done ; JE32 %match_Done ; Perfect match
E9 %match_Loop ; JMP32 %match_Loop ; Otherwise keep looping
:match_False
B8 01000000 ; LOADI32_EAX %1 ; Return false
:match_Done
5A ; POP_EDX ; Restore EDX
59 ; POP_ECX ; Restore ECX
5B ; POP_EBX ; Restore EBX
C3 ; RET
;; Line_Macro function
;; Receives List in EAX
;; Updates the list in place; does not modify registers
;; Uses EAX for I, EBX for I->TEXT, ECX for I->EXPRESSION
:Line_Macro
50 ; PUSH_EAX ; Protect EAX
53 ; PUSH_EBX ; Protect EBX
51 ; PUSH_ECX ; Protect ECX
52 ; PUSH_EDX ; Protect EDX
:Line_Macro_Loop
8B58 08 ; LOAD32_EBX_from_EAX_Immediate8 !8 ; I->TYPE
83FB 01 ; CMPI8_EBX !1 ; IF MACRO == I->TYPE
0F85 %Line_Macro_Next ; JNE32 %Line_Macro_Next ; Otherwise move on
;; Is a macro apply
8B58 10 ; LOAD32_EBX_from_EAX_Immediate8 !16 ; I->TEXT
8B48 18 ; LOAD32_ECX_from_EAX_Immediate8 !24 ; I->EXPRESSION
8B00 ; LOAD32_EAX_from_EAX ; I->NEXT
E8 %Set_Expression ; CALL32 %Set_Expression ; Apply it
E9 %Line_Macro_Loop ; JMP32 %Line_Macro_Loop ; Move on to next
:Line_Macro_Next
8B00 ; LOAD32_EAX_from_EAX ; I->NEXT
83F8 00 ; CMPI8_EAX !0 ; Check for NULL
0F85 %Line_Macro_Loop ; JNE32 %Line_Macro_Loop ; Keep going
5A ; POP_EDX ; Restore EDX
59 ; POP_ECX ; Restore ECX
5B ; POP_EBX ; Restore EBX
58 ; POP_EAX ; Restore EAX
C3 ; RET
;; Set_Expression function
;; Receives List in EAX, CHAR* in EBX and CHAR* in ECX
;; Updates the list in place; does not modify registers
;; Uses EBX for C, ECX for EXP and EDX for I
:Set_Expression
50 ; PUSH_EAX ; Protect EAX
53 ; PUSH_EBX ; Protect EBX
51 ; PUSH_ECX ; Protect ECX
52 ; PUSH_EDX ; Protect EDX
89C2 ; COPY_EAX_to_EDX ; Set I
:Set_Expression_Loop
8B42 08 ; LOAD32_EAX_from_EDX_Immediate8 !8 ; I->TYPE
83F8 01 ; CMPI8_EAX !1 ; IF MACRO == I->TYPE
0F84 %Set_Expression_Next ; JE32 %Set_Expression_Next ; Ignore and move on
8B42 10 ; LOAD32_EAX_from_EDX_Immediate8 !16 ; I->TEXT
E8 %match ; CALL32 %match ; Check for match
83F8 00 ; CMPI8_EAX !0 ; If match
0F85 %Set_Expression_Next ; JNE32 %Set_Expression_Next ; Otherwise next
;; We have a non-macro match
894A 18 ; STORE32_ECX_into_Address_EDX_Immediate8 !24 ; I->EXPRESSION = EXP
:Set_Expression_Next
8B12 ; LOAD32_EDX_from_EDX ; I = I->NEXT
83FA 00 ; CMPI8_EDX !0 ; IF NULL == I
0F85 %Set_Expression_Loop ; JNE32 %Set_Expression_Loop ; Otherwise keep looping
5A ; POP_EDX ; Restore EDX
59 ; POP_ECX ; Restore ECX
5B ; POP_EBX ; Restore EBX
58 ; POP_EAX ; Restore EAX
C3 ; RET
;; Process_String function
;; Receives List in EAX
;; Update the list in place; does not modify registers
;; Uses EBX for I->TEXT, ECX for I and EDX for S
:Process_String
50 ; PUSH_EAX ; Protect EAX
53 ; PUSH_EBX ; Protect EBX
51 ; PUSH_ECX ; Protect ECX
52 ; PUSH_EDX ; Protect EDX
89C1 ; COPY_EAX_to_ECX ; I = HEAD
:Process_String_loop
8B41 08 ; LOAD32_EAX_from_ECX_Immediate8 !8 ; I->TYPE
83F8 02 ; CMPI8_EAX !2 ; IF STRING == I->TYPE
0F85 %Process_String_Next ; JNE32 %Process_String_Next ; Skip to next
8B59 10 ; LOAD32_EBX_from_ECX_Immediate8 !16 ; I->TEXT
8A03 ; LOAD8_al_from_EBX ; I->TEXT[0]
0FB6C0 ; MOVZX_al ; make it useful
83F8 27 ; CMPI8_EAX !39 ; IF '\'' == I->TEXT[0]
0F85 %Process_String_Raw ; JNE32 %Process_String_Raw ; Deal with '\"'
;; Deal with '\''
83C3 01 ; ADDI8_EBX !1 ; I->TEXT + 1
8959 18 ; STORE32_EBX_into_Address_ECX_Immediate8 !24 ; I->EXPRESSION = I->TEXT + 1
E9 %Process_String_Next ; JMP32 %Process_String_Next ; Move on to next
:Process_String_Raw
89D8 ; COPY_EBX_to_EAX ; Get length of I->TEXT
E8 %string_length ; CALL32 %string_length ; Do it
C1E8 02 ; SHRI8_EAX !2 ; LENGTH = LENGTH >> 2
83C0 01 ; ADDI8_EAX !1 ; LENGTH = LENGTH + 1
C1E0 03 ; SHLI8_EAX !3 ; LENGTH = LENGTH << 3
E8 %malloc ; CALL32 %malloc ; Get string
89DA ; COPY_EBX_to_EDX ; S = I->TEXT
83C2 01 ; ADDI8_EDX !1 ; S = S + 1
8941 18 ; STORE32_EAX_into_Address_ECX_Immediate8 !24 ; I->EXPRESSION = hexify
89C3 ; COPY_EAX_to_EBX ; Put hexify buffer in ebx
:Process_String_Raw_Loop
8A02 ; LOAD8_al_from_EDX ; Read 1 chars
0FB6C0 ; MOVZX_al ; Make it useful
83C2 01 ; ADDI8_EDX !1 ; S = S + 1
3C 00 ; CMPI8_AL !0 ; Check for NULL
9C ; PUSH_FLAGS ; Protect condition
E8 %hex8 ; CALL32 %hex8 ; write them all
9D ; POP_FLAGS ; restore condition
0F85 %Process_String_Raw_Loop ; JNE32 %Process_String_Raw_Loop ; Keep looping
:Process_String_Next
8B09 ; LOAD32_ECX_from_ECX ; I = I->NEXT
83F9 00 ; CMPI8_ECX !0 ; IF NULL == I
0F85 %Process_String_loop ; JNE32 %Process_String_loop ; Otherwise keep looping
5A ; POP_EDX ; Restore EDX
59 ; POP_ECX ; Restore ECX
5B ; POP_EBX ; Restore EBX
58 ; POP_EAX ; Restore EAX
C3 ; RET
;; string_length function
;; Receives CHAR* in EAX
;; Returns INT in EAX
;; Uses EAX for CH, EBX for S and ECX for INDEX
:string_length
53 ; PUSH_EBX ; Protect EBX
51 ; PUSH_ECX ; Protect ECX
89C3 ; COPY_EAX_to_EBX ; Set S
B9 00000000 ; LOADI32_ECX %0 ; INDEX = 0
:string_length_loop
8A040B ; LOAD8_al_from_EBX_indexed_ECX ; S[0]
0FB6C0 ; MOVZX_al ; make it useful
83F8 00 ; CMPI8_EAX !0 ; IF NULL == S[0]
0F84 %string_length_done ; JE32 %string_length_done ; Stop
83C1 01 ; ADDI8_ECX !1 ; INDEX = INDEX + 1
E9 %string_length_loop ; JMP32 %string_length_loop ; Keep going
:string_length_done
89C8 ; COPY_ECX_to_EAX ; RETURN INDEX
59 ; POP_ECX ; Restore ECX
5B ; POP_EBX ; Restore EBX
C3 ; RET
;; Eval_Immediates function
;; Receives List in EAX
;; Updates the list in place; does not modify registers
;; Uses EBX for I->TEXT[0], ECX for I->TEXT[1] and EDX for I
:Eval_Immediates
50 ; PUSH_EAX ; Protect EAX
53 ; PUSH_EBX ; Protect EBX
51 ; PUSH_ECX ; Protect ECX
52 ; PUSH_EDX ; Protect EDX
89C2 ; COPY_EAX_to_EDX ; I = HEAD
:Eval_Immediates_Loop
;; Check for MACRO
8B42 08 ; LOAD32_EAX_from_EDX_Immediate8 !8 ; I->TYPE
83F8 01 ; CMPI8_EAX !1 ; IF MACRO == I-TYPE
0F84 %Eval_Immediates_Next ; JE32 %Eval_Immediates_Next ; Skip to next
;; Check for NULL EXPRESSION
8B42 18 ; LOAD32_EAX_from_EDX_Immediate8 !24 ; I->EXPRESSION
83F8 00 ; CMPI8_EAX !0 ; IF NULL == I->EXPRESSION
0F85 %Eval_Immediates_Next ; JNE32 %Eval_Immediates_Next ; Skip to next
;; Check if number
8B42 10 ; LOAD32_EAX_from_EDX_Immediate8 !16 ; I->TEXT
8A18 ; LOAD8_bl_from_EAX ; I->TEXT[0]
0FB6DB ; MOVZX_bl ; Extend to use
83C0 01 ; ADDI8_EAX !1 ; I->TEXT + 1
8A08 ; LOAD8_cl_from_EAX ; I->TEXT[1]
0FB6C9 ; MOVZX_cl ; Extend to use
E8 %numerate_string ; CALL32 %numerate_string ; Convert string to INT
83F8 00 ; CMPI8_EAX !0 ; IF 0 == numerate_number(I->TEXT + 1)
0F85 %Eval_Immediates_value ; JNE32 %Eval_Immediates_value ; Has a value
;; Last chance for Immediate
83F9 30 ; CMPI8_ECX !48 ; If '0' == I->TEXT[1]
0F85 %Eval_Immediates_Next ; JNE32 %Eval_Immediates_Next ; Skip to next
:Eval_Immediates_value
E8 %express_number ; CALL32 %express_number ; Convert value to hex string
8942 18 ; STORE32_EAX_into_Address_EDX_Immediate8 !24 ; I->EXPRESSION = express_number(value, I-TEXT[0])
:Eval_Immediates_Next
8B12 ; LOAD32_EDX_from_EDX ; I = I->NEXT
83FA 00 ; CMPI8_EDX !0 ; IF NULL == I
0F85 %Eval_Immediates_Loop ; JNE32 %Eval_Immediates_Loop ; Otherwise keep looping
5A ; POP_EDX ; Restore EDX
59 ; POP_ECX ; Restore ECX
5B ; POP_EBX ; Restore EBX
58 ; POP_EAX ; Restore EAX
C3 ; RET
;; numerate_string function
;; Receives CHAR* in EAX
;; Returns value of CHAR* in EAX
;; Uses EAX for VALUE, EBX for S, ECX for CH and EDI for NEGATIVE?
:numerate_string
53 ; PUSH_EBX ; Protect EBX
51 ; PUSH_ECX ; Protect ECX
52 ; PUSH_EDX ; Protect EDX
57 ; PUSH_EDI ; Protect EDI
89C3 ; COPY_EAX_to_EBX ; put S in correct place
B8 00000000 ; LOADI32_EAX %0 ; Initialize to Zero
:numerate_string_loop
8A4B 01 ; LOAD8_cl_from_EBX_Immediate8 !1 ; S[1]
0FB6C9 ; MOVZX_cl ; make it useful
83F9 78 ; CMPI8_ECX !120 ; IF 'x' == S[1]
0F84 %numerate_hex ; JE32 %numerate_hex ; Deal with hex input
;; Assume decimal input
B9 00000000 ; LOADI32_ECX %0 ; Assume no negation
8A0B ; LOAD8_cl_from_EBX ; S[0]
0FB6C9 ; MOVZX_cl ; make it useful
83F9 2D ; CMPI8_ECX !45 ; IF '-' == S[0]
0F85 %numerate_decimal ; JNE32 %numerate_decimal ; Skip negation
BF 01000000 ; LOADI32_EDI %1 ; Set FLAG
83C3 01 ; ADDI8_EBX !1 ; S = S + 1
:numerate_decimal
8A0B ; LOAD8_cl_from_EBX ; S[0]
0FB6C9 ; MOVZX_cl ; make it useful
83F9 00 ; CMPI8_ECX !0 ; IF NULL == S[0]
0F84 %numerate_decimal_done ; JE32 %numerate_decimal_done ; We are done
6BC0 0A ; IMULI8_EAX !10 ; VALUE = VALUE * 10
83E9 30 ; SUBI8_ECX !48 ; CH = CH - '0'
83F9 09 ; CMPI8_ECX !9 ; Check for illegal
0F8F %numerate_string_fail ; JG32 %numerate_string_fail ; If CH > '9'
83F9 00 ; CMPI8_ECX !0 ; Check for illegal
0F8C %numerate_string_fail ; JL32 %numerate_string_fail ; IF CH < 0
01C8 ; ADD_ECX_to_EAX ; VALUE = VALUE + CH
83C3 01 ; ADDI8_EBX !1 ; S = S + 1
E9 %numerate_decimal ; JMP32 %numerate_decimal ; Keep looping
:numerate_decimal_done
83FF 01 ; CMPI8_EDI !1 ; Check if need to negate
0F85 %numerate_string_done ; JNE32 %numerate_string_done ; Nope
6BC0 FF ; IMULI8_EAX !-1 ; VALUE = VALUE * -1
E9 %numerate_string_done ; JMP32 %numerate_string_done ; Done
:numerate_hex
83C3 02 ; ADDI8_EBX !2 ; S = S + 2
:numerate_hex_loop
8A0B ; LOAD8_cl_from_EBX ; S[0]
0FB6C9 ; MOVZX_cl ; make it useful
83F9 00 ; CMPI8_ECX !0 ; IF NULL == S[0]
0F84 %numerate_string_done ; JE32 %numerate_string_done ; We are done
C1E0 04 ; SHLI8_EAX !4 ; VALUE = VALUE << 4
83E9 30 ; SUBI8_ECX !48 ; CH = CH - '0'
83F9 0A ; CMPI8_ECX !10 ; IF 10 >= CH
0F8C %numerate_hex_digit ; JL32 %numerate_hex_digit ; NO
83E9 07 ; SUBI8_ECX !7 ; Push A-F into range
:numerate_hex_digit
83F9 0F ; CMPI8_ECX !15 ; Check for illegal
0F8F %numerate_string_fail ; JG32 %numerate_string_fail ; If CH > 'F'
83F9 00 ; CMPI8_ECX !0 ; Check for illegal
0F8C %numerate_string_fail ; JL32 %numerate_string_fail ; IF CH < 0
01C8 ; ADD_ECX_to_EAX ; VALUE = VALUE + CH
83C3 01 ; ADDI8_EBX !1 ; S = S + 1
E9 %numerate_hex_loop ; JMP32 %numerate_hex_loop ; Keep looping
:numerate_string_fail
B8 00000000 ; LOADI32_EAX %0 ; return ZERO
:numerate_string_done
5F ; POP_EDI ; Restore EDI
5A ; POP_EDX ; Restore EDX
59 ; POP_ECX ; Restore ECX
5B ; POP_EBX ; Restore EBX
C3 ; RET
;; express_number function
;; Receives INT in EAX and CHAR in EBX
;; Allocates a string and expresses the value in hex
;; Returns string in EAX
;; Uses EAX for VALUE, EBX for S and ECX for CH
:express_number
53 ; PUSH_EBX ; Protect EBX
51 ; PUSH_ECX ; Protect ECX
52 ; PUSH_EDX ; Protect EDX
89D9 ; COPY_EBX_to_ECX ; Put CH in right place
89C3 ; COPY_EAX_to_EBX ; Protect VALUE
83F9 25 ; CMPI8_ECX !37 ; IF '%' == CH
0F85 %express_number2 ; JNE32 %express_number2 ; Otherwise try @
B8 09000000 ; LOADI32_EAX %9 ; We need 3bytes
E8 %malloc ; CALL32 %malloc ; Get S pointer
93 ; XCHG_EAX_EBX ; Put S and VALUE in place
53 ; PUSH_EBX ; Protect S
E8 %hex32l ; CALL32 %hex32l ; Store 32bits
E9 %express_number_done ; JMP32 %express_number_done ; done
:express_number2
83F9 40 ; CMPI8_ECX !64 ; IF '@' == CH
0F85 %express_number1 ; JNE32 %express_number1 ; Othrewise try !
B8 05000000 ; LOADI32_EAX %5 ; We need 3bytes
E8 %malloc ; CALL32 %malloc ; Get S pointer
93 ; XCHG_EAX_EBX ; Put S and VALUE in place
53 ; PUSH_EBX ; Protect S
E8 %hex16l ; CALL32 %hex16l ; Store 16bits
E9 %express_number_done ; JMP32 %express_number_done ; done
:express_number1
B8 03000000 ; LOADI32_EAX %3 ; We need 3bytes
E8 %malloc ; CALL32 %malloc ; Get S pointer
93 ; XCHG_EAX_EBX ; Put S and VALUE in place
53 ; PUSH_EBX ; Protect S
E8 %hex8 ; CALL32 %hex8 ; Store 8bit
:express_number_done
58 ; POP_EAX ; Restore S
5A ; POP_EDX ; Restore EDX
59 ; POP_ECX ; Restore ECX
5B ; POP_EBX ; Restore EBX
C3 ; RET
;; HEX to ascii routine
;; Receives INT in EAX and CHAR* in EBX
;; Stores ascii of INT in CHAR*
;; Returns only modifying EAX
:hex64l
50 ; PUSH_EAX ; Protect top 32
E8 %hex32l ; CALL32 %hex32l ; Store it
58 ; POP_EAX ; do top 32
C1E8 20 ; SHRI8_EAX !32 ; do bottom 32 first
:hex32l
50 ; PUSH_EAX ; Protect top 16
E8 %hex16l ; CALL32 %hex16l ; Store it
58 ; POP_EAX ; do top 16
C1E8 10 ; SHRI8_EAX !16 ; do bottom 16 first
:hex16l
50 ; PUSH_EAX ; Protect top byte
E8 %hex8 ; CALL32 %hex8 ; Store it
58 ; POP_EAX ; do high byte
C1E8 08 ; SHRI8_EAX !8 ; do bottom byte first
:hex8
50 ; PUSH_EAX ; Protect bottom nibble
C1E8 04 ; SHRI8_EAX !4 ; do high nibble first
E8 %hex4 ; CALL32 %hex4 ; Store it
58 ; POP_EAX ; do low nibble
:hex4
83E0 0F ; ANDI8_EAX !0xF ; isolate nibble
04 30 ; ADDI8_AL !48 ; convert to ascii
3C 39 ; CMPI8_AL !57 ; valid digit?
0F86 %hex1 ; JBE32 %hex1 ; yes
04 07 ; ADDI8_AL !7 ; use alpha range
:hex1
8803 ; STORE8_al_into_Address_EBX ; store result
83C3 01 ; ADDI8_EBX !1 ; next position
C3 ; RET
;; Preserve_Other function
;; Receives List in EAX
;; Updates the list in place; does not modify registers
;; Uses EAX for I, EBX for I->TEXT
:Preserve_Other
50 ; PUSH_EAX ; Protect EAX
53 ; PUSH_EBX ; Protect EBX
51 ; PUSH_ECX ; Protect ECX
52 ; PUSH_EDX ; Protect EDX
:Preserve_Other_Loop
8B58 18 ; LOAD32_EBX_from_EAX_Immediate8 !24 ; I->EXPRESSION
83FB 00 ; CMPI8_EBX !0 ; IF NULL == I->EXPRESSION
0F85 %Preserve_Other_Next ; JNE32 %Preserve_Other_Next ; Otherwise next
;; Needs preserving
8B58 10 ; LOAD32_EBX_from_EAX_Immediate8 !16 ; I->TEXT
8958 18 ; STORE32_EBX_into_Address_EAX_Immediate8 !24 ; I->EXPRESSION = I->TEXT
:Preserve_Other_Next
8B00 ; LOAD32_EAX_from_EAX ; I = I->NEXT
83F8 00 ; CMPI8_EAX !0 ; IF NULL == I
0F85 %Preserve_Other_Loop ; JNE32 %Preserve_Other_Loop ; Otherwise keep looping
5A ; POP_EDX ; Restore EDX
59 ; POP_ECX ; Restore ECX
5B ; POP_EBX ; Restore EBX
58 ; POP_EAX ; Restore EAX
C3 ; RET
;; Print_Hex function
;; Receives list in EAX
;; walks the list and prints the I->EXPRESSION for all nodes followed by newline
;; Uses EBX for I
:Print_Hex
53 ; PUSH_EBX ; Protect EBX
51 ; PUSH_ECX ; Protect ECX
89EB ; COPY_EBP_to_EBX ; I = Head
:Print_Hex_Loop
8B43 08 ; LOAD32_EAX_from_EBX_Immediate8 !8 ; I->TYPE
83F8 01 ; CMPI8_EAX !1 ; IF MACRO == I->TYPE
0F84 %Print_Hex_Next ; JE32 %Print_Hex_Next ; Skip
8B43 18 ; LOAD32_EAX_from_EBX_Immediate8 !24 ; Using EXPRESSION
E8 %File_Print ; CALL32 %File_Print ; Print it
B8 0A000000 ; LOADI32_EAX %10 ; NEWLINE
E8 %fputc ; CALL32 %fputc ; Append it
:Print_Hex_Next
8B1B ; LOAD32_EBX_from_EBX ; Iterate to next Token
83FB 00 ; CMPI8_EBX !0 ; Check for NULL
0F85 %Print_Hex_Loop ; JNE32 %Print_Hex_Loop ; Otherwise keep looping
59 ; POP_ECX ; Restore ECX
5B ; POP_EBX ; Restore EBX
C3 ; RET
;; File_Print function
;; Receives CHAR* in EAX
;; calls fputc for every non-null char
:File_Print
53 ; PUSH_EBX ; Protect EBX
51 ; PUSH_ECX ; Protect ECX
89C3 ; COPY_EAX_to_EBX ; Protect S
83F8 00 ; CMPI8_EAX !0 ; Protect against nulls
0F84 %File_Print_Done ; JE32 %File_Print_Done ; Simply don't try to print them
:File_Print_Loop
8A03 ; LOAD8_al_from_EBX ; Read byte
0FB6C0 ; MOVZX_al ; zero extend
83F8 00 ; CMPI8_EAX !0 ; Check for NULL
0F84 %File_Print_Done ; JE32 %File_Print_Done ; Stop at NULL
E8 %fputc ; CALL32 %fputc ; write it
83C3 01 ; ADDI8_EBX !1 ; S = S + 1
E9 %File_Print_Loop ; JMP32 %File_Print_Loop ; Keep going
:File_Print_Done
59 ; POP_ECX ; Restore ECX
5B ; POP_EBX ; Restore EBX
C3 ; RET
;; fputc function
;; receives CHAR in EAX and FILE* in [Output]
;; writes char and returns
:fputc
52 ; PUSH_EDX ; Protect EDX
51 ; PUSH_ECX ; protect ECX
53 ; PUSH_EBX ; protect EBX
50 ; PUSH_EAX ; We are writing eax
8D0C24 ; LEA32_ECX_from_esp ; Get stack address
8B1D &Output ; LOAD32_Absolute32_ebx &Output ; Write to target file
B8 04000000 ; LOADI32_EAX %4 ; the syscall number for write
BA 01000000 ; LOADI32_EDX %1 ; set the size of chars we want
CD80 ; INT_80 ; call the Kernel
58 ; POP_EAX ; Restore stack
5B ; POP_EBX ; Restore EBX
59 ; POP_ECX ; Restore ECX
5A ; POP_EDX ; Restore EDX
C3 ; RET
:Output
00000000 ; NULL
:Input
00000000 ; NULL
:ELF_end
;; Copyright (C) 2017 Jeremiah Orians
;; This file is part of stage0.
;;
;; stage0 is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; stage0 is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with stage0. If not, see <http://www.gnu.org/licenses/>.
DEFINE ADDI8_EAX 83C0
DEFINE ADDI8_EBX 83C3
DEFINE ADDI8_ECX 83C1
DEFINE ADDI8_EDX 83C2
DEFINE ADDI8_ESI 83C6
DEFINE ADD_eax_into_ebx 01C3
DEFINE ADD_ebx_into_eax 01D8
DEFINE ADD_ecx_into_eax 01C8
DEFINE ADD_edi_into_ecx 01F9
DEFINE AND_EAX_EBX 21D8
DEFINE ANDI32_EAX 25
DEFINE CALL_EAX FFD0
DEFINE CALL32 E8
DEFINE CMPI8_EAX 83F8
DEFINE CMPI8_EBP 83fd
DEFINE CMPI8_EBX 83FB
DEFINE CMPI8_ECX 83F9
DEFINE CMPI8_EDX 83FA
DEFINE CMPI8_ESI 83FE
DEFINE CMP_EAX_EBX 39D8
DEFINE CMP_EAX_ECX 39C8
DEFINE CMP_EBX_ECX 39D9
DEFINE CMP_ECX_EBX 39CB
DEFINE CMP_EDI_ESI 39FE
DEFINE CMP_EBX_EDX 39D3
DEFINE COPY_EAX_to_EBP 89C5
DEFINE COPY_EAX_to_EBX 89C3
DEFINE COPY_EAX_to_ECX 89C1
DEFINE COPY_EAX_to_EDX 89C2
DEFINE COPY_EAX_to_ESI 89C6
DEFINE COPY_EBP_to_EAX 89E8
DEFINE COPY_EBX_to_EAX 89D8
DEFINE COPY_EBX_to_ECX 89D9
DEFINE COPY_EBX_to_EDX 89DA
DEFINE COPY_ECX_to_EAX 89C8
DEFINE COPY_ECX_to_EBX 89CB
DEFINE COPY_EDI_to_ESI 89FE
DEFINE COPY_EDX_to_EAX 89D0
DEFINE COPY_EDX_to_EBP 89D5
DEFINE COPY_EDX_to_EBX 89D3
DEFINE COPY_ESI_to_EAX 89F0
DEFINE COPY_ESI_to_EDI 89F7
DEFINE IDIV_EBX F7FB
DEFINE IMUL_EAX_by_EBX 0FAFC3
DEFINE IMULI8_EAX 6BC0
DEFINE IMULI8_EBP 6BED
DEFINE INT_80 CD80
DEFINE JBE8 76
DEFINE JE32 0F84
DEFINE JG32 0F8F
DEFINE JG8 7F
DEFINE JL32 0F8C
DEFINE JLE32 0F8E
DEFINE JMP32 E9
DEFINE JNE32 0F85
DEFINE LEA32_ECX_from_esp 8D0C24
DEFINE LOAD32_Absolute32_eax A1
DEFINE LOAD32_Absolute32_ebx 8B1D
DEFINE LOAD32_Absolute32_ecx 8B0D
DEFINE LOAD32_EAX_from_EAX 8B00
DEFINE LOAD32_EAX_from_EAX_Immediate8 8B40
DEFINE LOAD32_EAX_from_EBP_Immediate8 8B45
DEFINE LOAD32_EAX_from_EBX 8B03
DEFINE LOAD32_EAX_from_EBX_Immediate8 8B43
DEFINE LOAD32_EAX_from_ECX_Immediate8 8B41
DEFINE LOAD32_EAX_from_EDX_Immediate8 8B42
DEFINE LOAD32_EBP_from_EBP 8B6D00
DEFINE LOAD32_EBX_from_EAX_Immediate8 8B58
DEFINE LOAD32_EBX_from_EBX 8B1B
DEFINE LOAD32_EBX_from_EBX_Immediate8 8B5B
DEFINE LOAD32_EBX_from_ECX_Immediate8 8B59
DEFINE LOAD32_ECX_from_EAX_Immediate8 8B48
DEFINE LOAD32_ECX_from_EBX 8B0B
DEFINE LOAD32_ECX_from_ECX 8B09
DEFINE LOAD32_ECX_from_ECX_Immediate8 8B49
DEFINE LOAD32_ECX_from_EDX_Immediate8 8B4A
DEFINE LOAD32_EDI_from_EDX_Immediate8 8B7A
DEFINE LOAD32_EDX_from_EDX_Immediate8 8B52
DEFINE LOAD8_al_from_EAX 8A00
DEFINE LOAD8_al_from_EBX 8A03
DEFINE LOAD8_al_from_ECX 8A01
DEFINE LOAD8_al_from_EDX 8A02
DEFINE LOAD8_bl_from_EBX 8A1B
DEFINE LOAD8_bl_from_ECX 8A19
DEFINE LOAD8_bl_from_EDX 8A1A
DEFINE LOAD8_cl_from_EBX 8A0B
DEFINE LOAD8_cl_from_EBX_Immediate8 8A4B
DEFINE LOADI32_EAX B8
DEFINE LOADI32_EBX BB
DEFINE LOADI32_ECX B9
DEFINE LOADI32_EDI BF
DEFINE LOADI32_EDX BA
DEFINE LOADI32_ESI BE
DEFINE MOVZX_al 0FB6C0
DEFINE MOVZX_bl 0FB6DB
DEFINE MOVZX_cl 0FB6C9
DEFINE NULL 00000000
DEFINE POP_EAX 58
DEFINE POP_EBP 5D
DEFINE POP_EBX 5B
DEFINE POP_ECX 59
DEFINE POP_EDI 5F
DEFINE POP_EDX 5A
DEFINE POP_ESI 5E
DEFINE PUSH_EAX 50
DEFINE PUSH_EBP 55
DEFINE PUSH_EBX 53
DEFINE PUSH_ECX 51
DEFINE PUSH_EDI 57
DEFINE PUSH_EDX 52
DEFINE PUSH_ESI 56
DEFINE RETURN C3
DEFINE SALI8_EAX C1E0
DEFINE SHRI8_EAX C1E8
DEFINE SHRI8_EBX C1EB
DEFINE STORE32_Absolute32_eax A3
DEFINE STORE32_Absolute32_ebx 891D
DEFINE STORE32_Absolute32_ecx 890D
DEFINE STORE32_Absolute32_edx 8915
DEFINE STORE32_EAX_into_EBP_Immediate8 8945
DEFINE STORE32_EAX_into_EBX 8903
DEFINE STORE32_EAX_into_ECX_Immediate8 8941
DEFINE STORE32_EAX_into_EDX 8902
DEFINE STORE32_EAX_into_EDX_Immediate8 8942
DEFINE STORE32_EBP_into_EDX_Immediate8 896A
DEFINE STORE32_EBX_into_EAX 8918
DEFINE STORE32_EBX_into_EAX_Immediate8 8958
DEFINE STORE32_EBX_into_EDX_Immediate8 895A
DEFINE STORE32_ECX_into_EAX 8908
DEFINE STORE32_ECX_into_EAX_Immediate8 8948
DEFINE STORE32_ECX_into_EDX_Immediate8 894A
DEFINE STORE32_EDX_into_EAX_Immediate8 8950
DEFINE STORE32_EDX_into_EBP_Immediate8 8955
DEFINE STORE32_ESI_into_EBP_Immedate8 8975
DEFINE STORE32_ESI_into_EDX_Immedate8 8972
DEFINE STORE8_al_into_Address_EBX 8803
DEFINE STORE8_al_into_Address_ECX 8801
DEFINE STORE8_al_into_Address_ESI 8806
DEFINE STORE8_bl_into_Address_ECX 8819
DEFINE SUBI8_EAX 83E8
DEFINE SUBI8_ECX 83E9
DEFINE SUBI8_ESI 83EE
DEFINE SWAP_EAX_EBX 93
;; Register usage:
;; EAX => Temps
;; Struct TYPE format: (size 28)
;; NEXT => 0
;; SIZE => 4
;; OFFSET => 8
;; INDIRECT => 12
;; MEMBERS => 16
;; TYPE => 20
;; NAME => 24
;; Struct TOKEN_LIST format: (size 20)
;; NEXT => 0
;; LOCALS/PREV => 4
;; S => 8
;; TYPE => 12
;; ARGS/DEPTH => 16
; Where the ELF Header is going to hit
; Simply jump to _start
; Our main function
:_start
POP_EAX ; Get the number of arguments
POP_EBX ; Get the program name
POP_EBX ; Get the actual input name
LOADI32_ECX %0 ; prepare read_only
LOADI32_EAX %5 ; the syscall number for open()
INT_80 ; Now open that damn file
STORE32_Absolute32_eax &Input_file ; Preserve the file pointer we were given
POP_EBX ; Get the actual output name
LOADI32_ECX %577 ; Prepare file as O_WRONLY|O_CREAT|O_TRUNC
LOADI32_EDX %384 ; Prepare file as RW for owner only (600 in octal)
LOADI32_EAX %5 ; the syscall number for open()
INT_80 ; Now open that damn file
CMPI8_EAX !0 ; Check for missing output
JG32 %_start_out ; Have real input
LOADI32_EAX %1 ; Use stdout
:_start_out
STORE32_Absolute32_eax &Output_file ; Preserve the file pointer we were given
LOADI32_EAX %45 ; the Syscall # for SYS_BRK
LOADI32_EBX %0 ; Get current brk
INT_80 ; Let the kernel do the work
STORE32_Absolute32_eax &MALLOC ; Set our malloc pointer
LOADI32_EAX %0 ; HEAD = NULL
CALL32 %read_all_tokens ; Read all tokens
CALL32 %Reverse_List ; Reverse order
; CALL32 %debug_list ; Try to figure out what is wrong
STORE32_Absolute32_eax &global_token ; Set global_token
CALL32 %program ; Convert into program
LOADI32_EAX &header_string1 ; Our header string
CALL32 %File_Print ; Print it
LOAD32_Absolute32_eax &output_list ; Our output_list
CALL32 %recursive_output ; Print core program
; LOADI32_EAX &header_string2 ; Our Enable debug
; CALL32 %File_Print ; Print it
LOADI32_EAX &header_string3 ; Our second label
CALL32 %File_Print ; Print it
LOAD32_Absolute32_eax &globals_list ; Our globals
CALL32 %recursive_output ; Get them
LOADI32_EAX &header_string4 ; Our final header
CALL32 %File_Print ; Print it
LOAD32_Absolute32_eax &strings_list ; Our strings
CALL32 %recursive_output ; Get them
LOADI32_EAX &header_string5 ; Make this a bare assembly
CALL32 %File_Print ; Print it
:Done
; program completed Successfully
LOADI32_EBX %0 ; All is well
LOADI32_EAX %1 ; put the exit syscall number in eax
INT_80 ; Call it a good day
:header_string1 "
# Core program
"
:header_string2 "
:ELF_data
"
:header_string3 "
# Program global variables
"
:header_string4 "
# Program strings
"
:header_string5 "
:ELF_end
"
;; read_all_tokens function
;; Receives Token_List* in EAX
;; Tokenizes all input and returns updated list in EAX
;; Returns TOKEN in EAX
;; Uses EAX for C
:read_all_tokens
STORE32_Absolute32_eax &Token
CALL32 %fgetc
:read_all_tokens_loop
CMPI8_EAX !-4 ; Check for EOF
JE32 %read_all_tokens_done ; Stop if found
CALL32 %get_token ; Read all tokens
JMP32 %read_all_tokens_loop ; Loop
:read_all_tokens_done
LOAD32_Absolute32_eax &Token
RETURN
;; get_token function
;; Receives INT in EAX
;; Makes a list of TOKEN_LIST
;; C and STRING_INDEX are stored in memory, ECX is used for S and EDX is used for current
;; Returns C in EAX
:get_token
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
PUSH_EDX ; Protect EDX
STORE32_Absolute32_eax &C ; Set C
LOADI32_EAX %20 ; Malloc CURRENT
CALL32 %malloc ; Get Pointer
COPY_EAX_to_EDX ; Set CURRENT
LOADI32_EAX %256 ; Malloc the string
CALL32 %malloc ; Get pointer to S
COPY_EAX_to_ECX ; Set S
STORE32_ECX_into_EDX_Immediate8 !8 ; CURRENT->S = S
:reset
STORE32_Absolute32_ecx &string_index ; S[0]
LOAD32_Absolute32_eax &C ; Using C
CALL32 %clear_white_space ; Clear WhiteSpace
STORE32_Absolute32_eax &C ; Set C
CMPI8_EAX !-4 ; Check for EOF
JE32 %get_token_abort ; if EOF abort
CMPI8_EAX !35 ; Check for '#'
JNE32 %get_token_alpha ; Nope
;; Deal with # line comments
CALL32 %purge_macro ; Let it handle it
STORE32_Absolute32_eax &C ; Set C
JMP32 %reset ; Try again
:get_token_alpha
LOAD32_Absolute32_eax &C ; Send C
LOADI32_EBX &alphas ; Get alphanumerics
CALL32 %In_Set ; See if in set
CMPI8_EAX !1 ; IF TRUE
JNE32 %get_token_symbol ; Otherwise
;; Store keywords
LOAD32_Absolute32_eax &C ; Send C
CALL32 %preserve_keyword ; Store
STORE32_Absolute32_eax &C ; Set C
JMP32 %get_token_done ; Be done with this token
:get_token_symbol
LOAD32_Absolute32_eax &C ; Send C
LOADI32_EBX &symbols ; Get symbols
CALL32 %In_Set ; See if in set
CMPI8_EAX !1 ; IF TRUE
JNE32 %get_token_strings ; Otherwise
;; Store symbols
LOAD32_Absolute32_eax &C ; Send C
CALL32 %preserve_symbol ; Store
STORE32_Absolute32_eax &C ; Set C
JMP32 %get_token_done ; Be done with this token
:get_token_strings
LOAD32_Absolute32_eax &C ; Send C
LOADI32_EBX &strings ; Get strings
CALL32 %In_Set ; See if in set
CMPI8_EAX !1 ; IF TRUE
JNE32 %get_token_comment ; Otherwise
;; Store String
LOAD32_Absolute32_eax &C ; Send C
CALL32 %consume_word ; Store
STORE32_Absolute32_eax &C ; Set C
JMP32 %get_token_done ; Be done with this token
:get_token_comment
LOAD32_Absolute32_eax &C ; Send C
CMPI8_EAX !47 ; IF '/' == C
JNE32 %get_token_else ; Otherwise
CALL32 %consume_byte ; Hope it just is '/'
STORE32_Absolute32_eax &C ; Set C
CMPI8_EAX !42 ; IF '*' we have '/*'
JNE32 %get_token_comment_line ; Check for '//'
;; Deal with /* block comments */
CALL32 %fgetc ; get next C
STORE32_Absolute32_eax &C ; Set C
:get_token_comment_block_outer
LOAD32_Absolute32_eax &C ; Using C
CMPI8_EAX !47 ; IF '/' != C
JE32 %get_token_comment_block_done ; be done
:get_token_comment_block_inner
LOAD32_Absolute32_eax &C ; Using C
CMPI8_EAX !42 ; IF '*' != C
JE32 %get_token_comment_block_iter ; jump over
;; Deal with inner loop
CALL32 %fgetc ; get next C
STORE32_Absolute32_eax &C ; Set C
JMP32 %get_token_comment_block_inner ; keep going
:get_token_comment_block_iter
CALL32 %fgetc ; get next C
STORE32_Absolute32_eax &C ; Set C
JMP32 %get_token_comment_block_outer
:get_token_comment_block_done
CALL32 %fgetc ; get next C
STORE32_Absolute32_eax &C ; Set C
JMP32 %reset ; throw away, try again
:get_token_comment_line
CMPI8_EAX !47 ; IF '/' we have //
JNE32 %get_token_done ; keep if just '/'
;; Deal with // line comment
CALL32 %fgetc ; drop to match
STORE32_Absolute32_eax &C ; Set C
JMP32 %reset ; throw away, try again
:get_token_else
LOAD32_Absolute32_eax &C ; Send C
CALL32 %consume_byte
STORE32_Absolute32_eax &C ; Set C
:get_token_done
LOAD32_Absolute32_eax &Token ; TOKEN
STORE32_EAX_into_EDX_Immediate8 !4 ; CURRENT->PREV = TOKEN
STORE32_EAX_into_EDX ; CURRENT->NEXT = TOKEN
STORE32_Absolute32_edx &Token ; TOKEN = CURRENT
:get_token_abort
POP_EDX ; Restore EDX
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
LOAD32_Absolute32_eax &C ; Return C
RETURN
;; Malloc isn't actually required if the program being built fits in the initial memory
;; However, it doesn't take much to add it.
;; Requires [MALLOC] to be initialized and EAX to have the number of desired bytes
:malloc
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
PUSH_EDX ; Protect EDX
LOAD32_Absolute32_ebx &MALLOC ; Using the current pointer
ADD_eax_into_ebx ; Request the number of desired bytes
LOADI32_EAX %45 ; the Syscall # for SYS_BRK
INT_80 ; call the Kernel
LOAD32_Absolute32_eax &MALLOC ; Return pointer
STORE32_Absolute32_ebx &MALLOC ; Update pointer
POP_EDX ; Restore EDX
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
;; clear_white_space function
;; Receives INT C in EAX
;; Returns first non-whitespace char in EAX
:clear_white_space
CMPI8_EAX !32 ; Check for ' '
JE32 %clear_white_space_wipe ; wipe it out
CMPI8_EAX !10 ; Check for '\n'
JE32 %clear_white_space_wipe ; wipe it output
CMPI8_EAX !9 ; Check for '\t'
JNE32 %clear_white_space_done ; looks like non-whitespace
:clear_white_space_wipe
CALL32 %fgetc ; Read a new byte
CMPI8_EAX !-4 ; Check for EOF
JE32 %clear_white_space_done ; Short circuit
JMP32 %clear_white_space ; iterate
:clear_white_space_done
RETURN
;; In_Set function
;; Receives Char C in EAX and CHAR* in EBX
;; Returns 1 if true, zero if false in EAX
:In_Set
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
:In_Set_loop
LOAD8_cl_from_EBX ; Read char
MOVZX_cl ; Zero extend it
CMP_EAX_ECX ; See if they match
JE32 %In_Set_True ; return true
CMPI8_ECX !0 ; Check for NULL
JE32 %In_Set_False ; return false
ADDI8_EBX !1 ; s = s + 1
JMP32 %In_Set_loop ; Keep looping
:In_Set_True
LOADI32_EAX %1 ; Set True
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:In_Set_False
LOADI32_EAX %0 ; Set FALSE
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:alphas "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"
:symbols "<=>|&!-"
:strings '22 27 00'
;; purge_macro function
;; Receives CH in EAX
;; Reads chars until Line feed is read
;; returns line feed
:purge_macro
CALL32 %fgetc ; read next char
CMPI8_EAX !10 ; Check for '\n'
JNE32 %purge_macro ; Keep going
RETURN
;; preserve_keyword function
;; Receives INT C in EAX
;; collects all chars in keyword
;; Returns C in EAX
;; Uses ECX for INT C
:preserve_keyword
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
COPY_EAX_to_ECX ; Setup C
LOADI32_EBX &alphas ; Concerning ourselves with "abc.."
:preserve_keyword_loop
CALL32 %In_Set ; Check if alphanumerics
CMPI8_EAX !1 ; IF TRUE
JNE32 %preserve_keyword_label ; Otherwise check for label
COPY_ECX_to_EAX ; Pass C
CALL32 %consume_byte ; consume that byte
COPY_EAX_to_ECX ; Update C
JMP32 %preserve_keyword_loop ; keep looping
:preserve_keyword_label
COPY_ECX_to_EAX ; Fix return
CMPI8_EAX !58 ; Check for ':'
JNE32 %preserve_keyword_done ; be done
;; Fix our goto label
CALL32 %fixup_label ; Fix the label
LOADI32_EAX %32 ; Return Whitespace
:preserve_keyword_done
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
;; preserve_symbol function
;; Receives INT C in EAX
;; collects all chars in symbol
;; Returns C in EAX
;; Uses ECX for INT C
:preserve_symbol
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
COPY_EAX_to_ECX ; Setup C
LOADI32_EBX &symbols ; Concerning ourselves with "<=>.."
:preserve_symbol_loop
CALL32 %In_Set ; Check if symbol
CMPI8_EAX !1 ; IF TRUE
JNE32 %preserve_symbol_done ; Otherwise be done
COPY_ECX_to_EAX ; Pass C
CALL32 %consume_byte ; consume that byte
COPY_EAX_to_ECX ; Update C
JMP32 %preserve_symbol_loop ; keep looping
:preserve_symbol_done
COPY_ECX_to_EAX ; Fix return
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
;; consume_word function
;; receives INT C in EAX
;; returns INT C in EAX
;; Uses EAX for C, EBX for FREQ and ECX for ESCAPE
:consume_word
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
COPY_EAX_to_EBX ; FREQ = C
LOADI32_ECX %0 ; ESCAPE = FALSE
:consume_word_loop
CMPI8_ECX !0 ; IF !ESCAPE
JNE32 %consume_word_escape ; Enable escape
CMPI8_EAX !92 ; if '\\'
JNE32 %consume_word_iter ; keep state
LOADI32_ECX %1 ; ESCAPE = TRUE
JMP32 %consume_word_iter ; keep going
:consume_word_escape
LOADI32_ECX %0 ; ESCAPE = FALSE
:consume_word_iter
CALL32 %consume_byte ; read next char
CMPI8_ECX !0 ; IF ESCAPE
JNE32 %consume_word_loop ; keep looping
CMP_EAX_EBX ; IF C != FREQ
JNE32 %consume_word_loop ; keep going
CALL32 %fgetc ; return next char
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
;; consume_byte function
;; Receives INT C in EAX
;; Inserts C into string S, updates String S
;; Returns Next char in EAX
:consume_byte
PUSH_EBX ; Protect EBX
LOAD32_Absolute32_ebx &string_index ; S[0]
STORE8_al_into_Address_EBX ; S[0] = C
ADDI8_EBX !1 ; S = S + 1
STORE32_Absolute32_ebx &string_index ; Update S
CALL32 %fgetc
POP_EBX ; Restore EBX
RETURN
;; fixup_label function
;; Receives S in ECX
;; prepends ':' to string and returns registers unchanged
;; Uses EAX for HOLD, EBX for PREV and ECX for S[0]
:fixup_label
PUSH_EAX ; Protect EAX
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
LOADI32_EAX %58 ; HOLD = ':'
LOAD32_ECX_from_EDX_Immediate8 !8 ; HOLD_STRING[0]
:fixup_label_loop
COPY_EAX_to_EBX ; PREV = HOLD
LOAD8_al_from_ECX ; HOLD = HOLD_STRING[I]
MOVZX_al ; make useful
STORE8_bl_into_Address_ECX ; HOLD_STRING[I] = PREV
ADDI8_ECX !1 ; I = I + 1
CMPI8_EAX !0 ; IF NULL == HOLD
JNE32 %fixup_label_loop ; Keep looping
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
POP_EAX ; Restore EAX
RETURN
;; fgetc function
;; Loads FILE* from [INPUT_FILE]
;; Returns -4 (EOF) or char in EAX
:fgetc
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
PUSH_EDX ; Protect EDX
LOADI32_EAX %-4 ; Put EOF in eax
PUSH_EAX ; Assume bad (If nothing read, value will remain EOF)
LEA32_ECX_from_esp ; Get stack address
LOAD32_Absolute32_ebx &Input_file ; Where are we reading from
LOADI32_EAX %3 ; the syscall number for read
LOADI32_EDX %1 ; set the size of chars we want
INT_80 ; call the Kernel
POP_EAX ; Get either char or EOF
CMPI8_EAX !-4 ; Check for EOF
JE32 %fgetc_done ; Return as is
MOVZX_al ; Make it useful
:fgetc_done
POP_EDX ; Restore EDX
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
;; Reverse_List function
;; Receives List in EAX
;; Returns the list reversed in EAX
:Reverse_List
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
COPY_EAX_to_EBX ; Set HEAD
LOADI32_EAX %0 ; ROOT = NULL
:Reverse_List_Loop
CMPI8_EBX !0 ; WHILE HEAD != NULL
JE32 %Reverse_List_Done ; Stop otherwise
LOAD32_ECX_from_EBX ; NEXT = HEAD->NEXT
STORE32_EAX_into_EBX ; HEAD->NEXT = ROOT
COPY_EBX_to_EAX ; ROOT = HEAD
COPY_ECX_to_EBX ; HEAD = NEXT
JMP32 %Reverse_List_Loop ; Keep Going
:Reverse_List_Done
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
;; recursive_output function
;; Receives list in EAX
;; walks the list and prints the I->S for all nodes backwards
;; Uses EBX for I
:recursive_output
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
CMPI8_EAX !0 ; Check for NULL
JE32 %recursive_output_done ; Skip the work
COPY_EAX_to_EBX ; I = Head
LOAD32_EAX_from_EBX ; Iterate to next Token
CALL32 %recursive_output ; Recurse
LOAD32_EAX_from_EBX_Immediate8 !8 ; Using S
CALL32 %File_Print ; Print it
:recursive_output_done
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
;; File_Print function
;; Receives CHAR* in EAX
;; calls fputc for every non-null char
:File_Print
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
COPY_EAX_to_EBX ; Protect S
CMPI8_EAX !0 ; Protect against nulls
JE32 %File_Print_Done ; Simply don't try to print them
:File_Print_Loop
LOAD8_al_from_EBX ; Read byte
MOVZX_al ; zero extend
CMPI8_EAX !0 ; Check for NULL
JE32 %File_Print_Done ; Stop at NULL
CALL32 %fputc ; write it
ADDI8_EBX !1 ; S = S + 1
JMP32 %File_Print_Loop ; Keep going
:File_Print_Done
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
;; fputc function
;; receives CHAR in EAX and load FILE* from [OUTPUT_FILE]
;; writes char and returns
:fputc
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
PUSH_EDX ; Protect EDX
PUSH_EAX ; We are writing eax
LEA32_ECX_from_esp ; Get stack address
LOAD32_Absolute32_ebx &Output_file ; Write to target file
LOADI32_EAX %4 ; the syscall number for write
LOADI32_EDX %1 ; set the size of chars we want
INT_80 ; call the Kernel
POP_EAX ; Restore stack
POP_EDX ; Restore EDX
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
;; program function
;; receives nothing, returns nothing
;; Uses EAX for type_size
:program
;; The binary initialized the globals to null, so we can skip those steps
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
:new_type
LOAD32_Absolute32_eax &global_token ; Using global_token
CMPI8_EAX !0 ; Check if NULL
JE32 %program_done ; Be done if null
LOAD32_EBX_from_EAX_Immediate8 !8 ; GLOBAL_TOKEN->S
LOADI32_EAX &constant ; "CONSTANT"
CALL32 %match ; IF GLOBAL_TOKEN->S == "CONSTANT"
CMPI8_EAX !0 ; If true
JNE32 %program_else ; Looks like not a constant
;; Deal with minimal constant case
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->next
STORE32_Absolute32_eax &global_token ; global_token = global_token->next
LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S
LOADI32_EBX %0 ; NULL
LOAD32_Absolute32_ecx &global_constant_list ; global_constant_list
CALL32 %sym_declare ; Declare that constant
STORE32_Absolute32_eax &global_constant_list ; global_constant_list = sym_declare(global_token->s, NULL, global_constant_list);
LOAD32_Absolute32_ebx &global_token ; Using global_token
LOAD32_EBX_from_EBX ; global_token->next
STORE32_EBX_into_EAX_Immediate8 !16 ; global_constant_list->arguments = global_token->next
LOAD32_EBX_from_EBX ; global_token->next->next
STORE32_Absolute32_ebx &global_token ; global_token = global_token->next->next
JMP32 %new_type ; go around again
:program_else
CALL32 %type_name ; Figure out the type_size
CMPI8_EAX !0 ; IF NULL == type_size
JE32 %new_type ; it was a new type
;; Add to global symbol table
COPY_EAX_to_EBX ; put type_size in the right spot
LOAD32_Absolute32_eax &global_token ; Using global token
LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S
LOAD32_Absolute32_ecx &global_symbol_list ; Using global_symbol_list
CALL32 %sym_declare ; Declare symbol
STORE32_Absolute32_eax &global_symbol_list ; global_symbol_list = sym_declare(global_token->s, type_size, global_symbol_list);
LOAD32_Absolute32_ebx &global_token ; Using global token
LOAD32_EBX_from_EBX ; global_token->next
STORE32_Absolute32_ebx &global_token ; global_token = global_token->next
LOAD32_Absolute32_ebx &global_token ; Using global token
LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S
LOADI32_EAX &semicolon ; ";"
CALL32 %match ; if(match(";", global_token->s))
CMPI8_EAX !0 ; If true
JNE32 %program_function ; looks like not a match
;; Deal with the global variable
LOAD32_Absolute32_ebx &globals_list ; Using globals_list
LOADI32_EAX &program_string_0 ; ":GLOBAL_"
CALL32 %emit ; Emit it
COPY_EAX_to_EBX ; update globals_list
LOAD32_Absolute32_eax &global_token ; Using global token
LOAD32_EAX_from_EAX_Immediate8 !4 ; global token->prev
LOAD32_EAX_from_EAX_Immediate8 !8 ; global token->prev->s
CALL32 %emit ; Emit it
COPY_EAX_to_EBX ; update globals_list
LOADI32_EAX &program_string_1 ; "\nNULL\n"
CALL32 %emit ; Emit it
STORE32_Absolute32_eax &globals_list ; update globals_list
LOAD32_Absolute32_eax &global_token ; Using global token
LOAD32_EAX_from_EAX ; global_token->next
STORE32_Absolute32_eax &global_token ; global_token = global_token->next
JMP32 %new_type ; go around again
:program_function
LOAD32_Absolute32_ebx &global_token ; Using global token
LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S
LOADI32_EAX &open_paren ; "("
CALL32 %match ; if(match(";", global_token->s))
CMPI8_EAX !0 ; If true
JNE32 %program_error ; Otherwise deal with error case
;; Deal with function definition
CALL32 %declare_function ; Lets get the parsing rolling
JMP32 %new_type ; Keep looping through functions
:program_error
;; Deal with the case of something we don't support
;; NOT IMPLEMENTED
:program_done
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
;; Strings needed by the program function
:program_string_0 ":GLOBAL_"
:program_string_1 "
NULL
"
;; declare_function function
;; Receives nothing and returns nothing
;; Sets current function and adds it to the global function list
:declare_function
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
LOADI32_EAX %0 ; Using NULL
STORE32_Absolute32_eax ¤t_count ; current_count = 0
LOAD32_Absolute32_eax &global_token ; Using global token
LOAD32_EAX_from_EAX_Immediate8 !4 ; global token->prev
LOAD32_EAX_from_EAX_Immediate8 !8 ; global token->prev->s
LOADI32_EBX %0 ; NULL
LOAD32_Absolute32_ecx &global_function_list ; global_function_list
CALL32 %sym_declare ; sym_declare(global_token->prev->s, NULL, global_function_list);
STORE32_Absolute32_eax &function ; function = sym_declare(global_token->prev->s, NULL, global_function_list);
STORE32_Absolute32_eax &global_function_list ; global_function_list = function
CALL32 %collect_arguments ; collect all of the function arguments
LOAD32_Absolute32_eax &global_token ; Using global token
LOAD32_EAX_from_EAX_Immediate8 !8 ; global token->s
LOADI32_EBX &semicolon ; ";"
CALL32 %match ; IF global token->s == ";"
CMPI8_EAX !0 ; If true
JNE32 %declare_function_full ; It was a prototype
;; Deal with prototypes
LOAD32_Absolute32_eax &global_token ; Using global token
LOAD32_EAX_from_EAX ; global token->next
STORE32_Absolute32_eax &global_token ; global token = global token->next
JMP32 %declare_function_done ; Move on
:declare_function_full
;; Deal with full function definitions
LOADI32_EAX &declare_function_string_0 ; "# Defining function "
CALL32 %emit_out ; emit it
LOAD32_Absolute32_eax &function ; function
LOAD32_EAX_from_EAX_Immediate8 !8 ; function->s
CALL32 %emit_out ; emit it
LOADI32_EAX &declare_function_string_1 ; "\n:FUNCTION_"
CALL32 %emit_out ; emit it
LOAD32_Absolute32_eax &function ; function
LOAD32_EAX_from_EAX_Immediate8 !8 ; function->s
CALL32 %emit_out ; emit it
LOADI32_EAX &declare_function_string_3 ; "\n"
CALL32 %emit_out ; emit it
CALL32 %statement ; Recursively get the function pieces
LOAD32_Absolute32_eax &output_list ; output
LOAD32_EAX_from_EAX_Immediate8 !8 ; output->s
LOADI32_EBX &declare_function_string_2 ; "ret\n"
CALL32 %match ; IF output->s == "ret\n"
CMPI8_EAX !0 ; If true we can skip adding it
JE32 %declare_function_done ; otherwise we need to add it
;; Add the return to the end of a function lacking a return;
LOADI32_EAX &declare_function_string_2 ; "ret\n"
CALL32 %emit_out ; emit it
:declare_function_done
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:declare_function_string_0 "# Defining function "
:declare_function_string_1 "
:FUNCTION_"
:declare_function_string_2 "ret
"
:declare_function_string_3 "
"
;; collect_arguments function
;; Receives nothing
;; Returns Nothing
;; Adds arguments to the function definition
;; holds struct type* type_size in ECX, then replace with struct token_list* a in ECX when type_size is used
:collect_arguments
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->next
STORE32_Absolute32_eax &global_token ; global_token = global_token->next
:collect_arguments_loop
LOAD32_Absolute32_ebx &global_token ; Using global_token
LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S
LOADI32_EAX &close_paren ; ")"
CALL32 %match ; IF global_token->S == ")"
CMPI8_EAX !0 ; we reached the end
JE32 %collect_arguments_done ; be done
;; deal with the case of there are arguments
CALL32 %type_name ; Get the type
COPY_EAX_to_ECX ; put type_size safely out of the way
LOAD32_Absolute32_ebx &global_token ; Using global_token
LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S
LOADI32_EAX &close_paren ; ")"
CALL32 %match ; IF global_token->S == ")"
CMPI8_EAX !0 ; is a foo(int, char,void) case
JE32 %collect_arguments_common ; deal with commas
;; Trying second else
LOAD32_Absolute32_ebx &global_token ; Using global_token
LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S
LOADI32_EAX &comma ; ","
CALL32 %match ; IF global_token->S == ","
CMPI8_EAX !0 ; then deal with the common
JE32 %collect_arguments_common ; case of commas between arguments
;; deal with foo(int a, char b)
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S
COPY_ECX_to_EBX ; put type_size in the right place
LOAD32_Absolute32_ecx &function ; Using function
LOAD32_ECX_from_ECX_Immediate8 !16 ; function->args
CALL32 %sym_declare ; sym_declare(global_token->s, type_size, function->arguments);
COPY_EAX_to_ECX ; put a in a safe place
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !16 ; function->args
CMPI8_EAX !0 ; IF function->args == NULL
JNE32 %collect_arguments_another ; otherwise it isn't the first
;; Deal with the case of first argument in the function
LOADI32_EAX %-4 ; -4
STORE32_EAX_into_ECX_Immediate8 !16 ; a->depth = -4
JMP32 %collect_arguments_next ; get to next
:collect_arguments_another
;; deal with the case of non-first arguments
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !16 ; function->args
LOAD32_EAX_from_EAX_Immediate8 !16 ; function->args->depth
SUBI8_EAX !4 ; function->args->depth - 4
STORE32_EAX_into_ECX_Immediate8 !16 ; a->depth = function->args->depth - 4
:collect_arguments_next
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->next
STORE32_Absolute32_eax &global_token ; global_token = global_token->next
LOAD32_Absolute32_eax &function ; Using function
STORE32_ECX_into_EAX_Immediate8 !16 ; function->args = a
:collect_arguments_common
LOAD32_Absolute32_ebx &global_token ; Using global_token
LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S
LOADI32_EAX &comma ; ","
CALL32 %match ; IF global_token->S == ","
CMPI8_EAX !0 ; then deal with the comma
JNE32 %collect_arguments_loop ; otherwise loop
;; keep foo(bar(), 1) expressions working
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->next
STORE32_Absolute32_eax &global_token ; global_token = global_token->next
JMP32 %collect_arguments_loop ; keep going
:collect_arguments_done
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->next
STORE32_Absolute32_eax &global_token ; global_token = global_token->next
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
;; statement function
;; Receives nothing
;; Returns nothing
;; Walks down global_token recursively to collect the contents of the function
:statement
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
LOAD32_Absolute32_ebx &global_token ; Using global_token
LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S
LOADI32_EAX &open_curly_brace ; "{"
CALL32 %match ; IF global_token->S == "{"
JNE32 %statement_label ; otherwise try label
;; deal with { statement }
CALL32 %recursive_statement ; Statements inside of statements for days
JMP32 %statement_done ; Be done
:statement_label
LOAD8_al_from_EBX ; global_token->S[0]
MOVZX_al ; make it useful
CMPI8_EAX !58 ; IF global_token->S == ':'
JNE32 %statement_local ; otherwise try locals
;; deal with labels
COPY_EBX_to_EAX ; put global_token->S in the right spot
CALL32 %emit_out ; emit it
LOADI32_EAX &statement_string_0 ; Using "\t#C goto label\n"
CALL32 %emit_out ; emit it
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->next
STORE32_Absolute32_eax &global_token ; global_token = global_token->next
JMP32 %statement_done ; be done
:statement_local
COPY_EBX_to_EAX ; put global_token->S in the right place
LOADI32_EBX &prim_types ; pointer to primative types
CALL32 %lookup_type ; See if found
CMPI8_EAX !0 ; IF NULL == lookup_type(global_token->S, prim_types)
JNE32 %statement_local_success ; Sweet a new local
;; Second chance
LOAD32_Absolute32_ebx &global_token ; Using global_token
LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S
LOADI32_EAX &struct ; "struct"
CALL32 %match ; IF global_token->S == "struct"
CMPI8_EAX !0 ; then we are a local
JNE32 %statement_if ; otherwise try IF
:statement_local_success
CALL32 %collect_local ; Grab those locals
JMP32 %statement_done ; be done
:statement_if
LOADI32_EAX &if_string ; Using "if"
CALL32 %match ; IF global_token->S == "if"
CMPI8_EAX !0 ; then we have an if statement
JNE32 %statement_do ; otherwise try DO
;; Deal with IF statement
CALL32 %process_if ; DO IT
JMP32 %statement_done ; be done
:statement_do
LOADI32_EAX &do_string ; Using "do"
CALL32 %match ; IF global_token->S == "do"
CMPI8_EAX !0 ; then we have a do statement
JNE32 %statement_while ; otherwise try WHILE
;; Deal with DO statement
CALL32 %process_do ; DO IT
JMP32 %statement_done ; be done
:statement_while
LOADI32_EAX &while_string ; Using "while"
CALL32 %match ; IF global_token->S == "while"
CMPI8_EAX !0 ; then we have a while statement
JNE32 %statement_for ; otherwise try FOR
;; Deal with WHILE statement
CALL32 %process_while ; DO IT
JMP32 %statement_done ; be done
:statement_for
LOADI32_EAX &for_string ; Using "for"
CALL32 %match ; IF global_token->S == "for"
CMPI8_EAX !0 ; then we have a for statement
JNE32 %statement_asm ; otherwise try ASM
;; Deal with FOR statement
CALL32 %process_for ; DO IT
JMP32 %statement_done ; be done
:statement_asm
LOADI32_EAX &asm_string ; Using "asm"
CALL32 %match ; IF global_token->S == "asm"
CMPI8_EAX !0 ; then we have an asm statement
JNE32 %statement_goto ; otherwise try GOTO
;; Deal with ASM statement
CALL32 %process_asm ; Hit it
JMP32 %statement_done ; be done
:statement_goto
LOADI32_EAX &goto_string ; Using "goto"
CALL32 %match ; IF global_token->S == "goto"
CMPI8_EAX !0 ; then we have a goto statement
JNE32 %statement_return ; Otherwise try RETURN
;; Deal with GOTO statement
LOADI32_EAX &statement_string_1 ; Using "jmp %"
CALL32 %emit_out ; emit it
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->next
STORE32_Absolute32_eax &global_token ; global_token = global_token->next
LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S
CALL32 %emit_out ; emit it
LOADI32_EAX &statement_string_2 ; Using "\n"
CALL32 %emit_out ; emit it
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->next
STORE32_Absolute32_eax &global_token ; global_token = global_token->next
LOADI32_EAX &statement_string_4 ; Using "ERROR in statement\nMissing ;\n"
LOADI32_EBX &semicolon ; Using ";"
CALL32 %require_match ; Make sure it has the required
JMP32 %statement_done ; Be done
:statement_return
LOADI32_EAX &return_string ; Using "return"
CALL32 %match ; IF global_token->S == "return"
CMPI8_EAX !0 ; then we have a return statement
JNE32 %statement_break ; Otherwise try BREAK
;; Deal with RETURN Statement
CALL32 %return_result ; Return anything they want
JMP32 %statement_done ; be done
:statement_break
LOADI32_EAX &break_string ; Using "break"
CALL32 %match ; IF global_token->S == "break"
CMPI8_EAX !0 ; then we have a break statement
JNE32 %statement_continue ; Otherwise try CONTINUE
;; Deal with BREAK statement
CALL32 %process_break ; Lets do some damage
JMP32 %statement_done ; be done
:statement_continue
LOADI32_EAX &continue_string ; Using "continue"
CALL32 %match ; IF global_token->S == "continue"
CMPI8_EAX !0 ; then we have a continue statement
JNE32 %statement_else ; Otherwise we are punting to an expression
;; Deal with CONTINUE statement
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->next
STORE32_Absolute32_eax &global_token ; global_token = global_token->next
LOADI32_EAX &statement_string_3 ; Using "\n#continue statement\n"
CALL32 %emit_out ; emit it
LOADI32_EAX &statement_string_4 ; Using "ERROR in statement\nMissing ;\n"
LOADI32_EBX &semicolon ; Using ";"
CALL32 %require_match ; Don't forget the ";"
JMP32 %statement_done ; Be done
:statement_else
CALL32 %expression ; Collect expression
LOADI32_EAX &statement_string_4 ; Using "ERROR in statement\nMissing ;\n"
LOADI32_EBX &semicolon ; Using ";"
CALL32 %require_match ; make sure we have it
:statement_done
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:statement_string_0 " #C goto label
"
:statement_string_1 "jmp %"
:statement_string_2 "
"
:statement_string_3 "
#continue statement
"
:statement_string_4 "ERROR in statement
Missing ;
"
;; recursive_statement function
;; Receives nothing
;; Returns nothing
;; Walks the global_token list to build the contents of statements
;; Uses struct token_list* frame in ECX
:recursive_statement
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->next
STORE32_Absolute32_eax &global_token ; global_token = global_token->next
LOAD32_Absolute32_ecx &function ; Using function
LOAD32_ECX_from_ECX_Immediate8 !4 ; frame = function->locals
:recursive_statement_loop
LOAD32_Absolute32_ebx &global_token ; Using global_token
LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S
LOADI32_EAX &close_curly_brace ; Using "}"
CALL32 %match ; IF global_token->S == "}"
CMPI8_EAX !0 ; Then we are done recursing
JE32 %recursive_statement_cleanup ; and then we clean up
;; Deal with the recursive calls
CALL32 %statement ; Deal with another statement
JMP32 %recursive_statement_loop ; loop some more
:recursive_statement_cleanup
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->next
STORE32_Absolute32_eax &global_token ; global_token = global_token->next
LOADI32_EAX &recursive_statement_string_0 ; Using "ret\n"
LOAD32_Absolute32_ebx &output_list ; Using output
LOAD32_EBX_from_EBX_Immediate8 !8 ; output->S
CALL32 %match ; IF output->S == "ret\n"
CMPI8_EAX !0 ; Then we can skip the clean up
JE32 %recursive_statement_done ; and be done
;; Deal with cleanup
LOAD32_Absolute32_ebx &function ; Using function
LOAD32_EBX_from_EBX_Immediate8 !4 ; i = function->locals
LOADI32_EAX &recursive_statement_string_1 ; Using "pop_ebx\t# _recursive_statement_locals\n"
:recursive_statement_locals
CMP_ECX_EBX ; IF frame != i
JE32 %recursive_statement_done ; Otherwise be done
;; Lets emit
CALL32 %emit_out ; emit it
LOAD32_EBX_from_EBX ; i = i->next
JMP32 %recursive_statement_locals ; keep going
:recursive_statement_done
LOAD32_Absolute32_eax &function ; Using function
STORE32_ECX_into_EAX_Immediate8 !4 ; function->locals = frame
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:recursive_statement_string_0 "ret
"
:recursive_statement_string_1 "pop_ebx # _recursive_statement_locals
"
;; return_result function
;; Receives nothing
;; Returns nothing
;; Cleans up function and generates return
;; Also handles returning expressions
:return_result
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->next
STORE32_Absolute32_eax &global_token ; global_token = global_token->next
LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S
LOAD8_al_from_EAX ; global_token->S[0]
MOVZX_al ; make it useful
CMPI8_EAX !59 ; If global_token->S[0] == ';'
JE32 %return_result_cleanup ; Go straight to cleanup
CALL32 %expression ; get the expression we are returning
:return_result_cleanup
LOADI32_EAX &return_result_string_0 ; Using "ERROR in return_result\nMISSING ;\n"
LOADI32_EBX &semicolon ; Using ";"
CALL32 %require_match ; Make sure we have it
LOAD32_Absolute32_ebx &function ; Using function
LOAD32_EBX_from_EBX_Immediate8 !4 ; function->locals
LOADI32_EAX &return_result_string_1 ; Using "pop_ebx\t# _return_result_locals\n"
:return_result_locals
CMPI8_EBX !0 ; IF NULL == i
JE32 %return_result_done ; Be done
CALL32 %emit_out ; Emit out pop
LOAD32_EBX_from_EBX ; i = i->NEXT
JMP32 %return_result_locals ; Keep going
:return_result_done
LOADI32_EAX &return_result_string_2 ; Using "ret\n"
CALL32 %emit_out ; Emit it
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:return_result_string_0 "ERROR in return_result
MISSING ;
"
:return_result_string_1 "pop_ebx # _return_result_locals
"
:return_result_string_2 "ret
"
;; collect_local function
;; Receives nothing
;; Returns nothing
;; Walks global_token list to create function locals
;; Uses ECX for struct token_list* A
:collect_local
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
CALL32 %type_name ; Get the local's type
COPY_EAX_to_EBX ; Put struct type* type_size in the right place
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S
LOAD32_Absolute32_ecx &function ; Using function
LOAD32_ECX_from_ECX_Immediate8 !4 ; function->locals
CALL32 %sym_declare ; Declare it
COPY_EAX_to_ECX ; put it away safely
;; Try for main
LOADI32_EAX &main_string ; Using "main"
LOAD32_Absolute32_ebx &function ; Using function
LOAD32_EBX_from_EBX_Immediate8 !8 ; function->S
CALL32 %match ; IF match("main", function->s)
CMPI8_EAX !0 ; possible
JNE32 %collect_local_fresh ; try to see if fresh function
;; Ok we are in main, now to see if main is fresh
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !4 ; function->locals
CMPI8_EAX !0 ; IF NULL == function->locals
JNE32 %collect_local_fresh ; try to see if fresh function
;; Sweet we are in a fresh main
LOADI32_EAX %-20 ; We start at -20
STORE32_EAX_into_ECX_Immediate8 !16 ; a->DEPTH = -20
JMP32 %collect_local_common ; Go to the commons
:collect_local_fresh
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !16 ; function->args
CMPI8_EAX !0 ; IF NULL == function->args
JNE32 %collect_local_first ; Otherwise see if first
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !4 ; function->locals
CMPI8_EAX !0 ; IF NULL == function->locals
JNE32 %collect_local_first ; Otherwise try first
;; Sweet we are in a fresh function
LOADI32_EAX %-8 ; We start at -8
STORE32_EAX_into_ECX_Immediate8 !16 ; a->DEPTH = -8
JMP32 %collect_local_common ; Go to the commons
:collect_local_first
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !4 ; function->locals
CMPI8_EAX !0 ; IF NULL == function->locals
JNE32 %collect_local_else ; Looks like we are just another local
;; Ok we are the first local
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !16 ; function->args
LOAD32_EAX_from_EAX_Immediate8 !16 ; function->args->depth
SUBI8_EAX !8 ; function->arguments->depth - 8
STORE32_EAX_into_ECX_Immediate8 !16 ; a->DEPTH = function->arguments->depth - 8
JMP32 %collect_local_common ; Go to the commons
:collect_local_else
;; Always the last to know
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !4 ; function->locals
LOAD32_EAX_from_EAX_Immediate8 !16 ; function->locals->depth
SUBI8_EAX !4 ; function->locals->depth - 4
STORE32_EAX_into_ECX_Immediate8 !16 ; a->DEPTH = function->locals->depth - 4
:collect_local_common
LOAD32_Absolute32_eax &function ; Using function
STORE32_ECX_into_EAX_Immediate8 !4 ; function->locals = a
LOAD32_ECX_from_ECX_Immediate8 !8 ; a->S
LOADI32_EAX &collect_local_string_0 ; Using "# Defining local "
CALL32 %emit_out ; emit it
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S
CALL32 %emit_out ; emit it
LOADI32_EAX &collect_local_string_1 ; Using "\n"
CALL32 %emit_out ; emit it
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->NEXT
STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT
LOAD32_EBX_from_EAX_Immediate8 !8 ; global_token->S
LOADI32_EAX &equal ; Using "="
CALL32 %match ; IF match("=", global_token->s)
CMPI8_EAX !0 ; Deal with assignment
JNE32 %collect_local_done ; Otherwise finish it
;; Deal with assignment
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->NEXT
STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT
CALL32 %expression ; Recurse
:collect_local_done
LOADI32_EAX &collect_local_string_2 ; Using "ERROR in collect_local\nMissing ;\n"
LOADI32_EBX &semicolon ; Using ";"
CALL32 %require_match ; Make sure we have it
LOADI32_EAX &collect_local_string_3 ; Using "push_eax\t#"
CALL32 %emit_out ; emit it
COPY_ECX_to_EAX ; put A->S where it belongs
CALL32 %emit_out ; emit it
LOADI32_EAX &collect_local_string_1 ; Using "\n"
CALL32 %emit_out ; emit it
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:collect_local_string_0 "# Defining local "
:collect_local_string_1 "
"
:collect_local_string_2 "ERROR in collect_local
Missing ;
"
:collect_local_string_3 "push_eax #"
;; process_asm function
;; Receives nothing
;; Returns nothing
;; Simply inlines the asm statements
;; Uses EBX for global_token temp storage
:process_asm
PUSH_EBX ; Protect EBX
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->NEXT
STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT
LOADI32_EAX &process_asm_string_0 ; Using "ERROR in process_asm\nMISSING (\n"
LOADI32_EBX &open_paren ; Using "("
CALL32 %require_match ; Make sure we have it
LOAD32_Absolute32_ebx &global_token ; Using global_token
:process_asm_iter
LOAD32_EAX_from_EBX_Immediate8 !8 ; global_token->S
LOAD8_al_from_EAX ; global_token->S[0]
MOVZX_al ; Make it useful
CMPI8_EAX !34 ; IF global_token->S[0] == '\"'
JNE32 %process_asm_done ; Otherwise be done
LOAD32_EAX_from_EBX_Immediate8 !8 ; global_token->S
ADDI8_EAX !1 ; global_token->S + 1
CALL32 %emit_out ; Emit it
LOADI32_EAX &process_asm_string_1 ; Using "\n"
CALL32 %emit_out ; Emit it
LOAD32_EBX_from_EBX ; global_token->NEXT
STORE32_Absolute32_ebx &global_token ; global_token = global_token->NEXT
JMP32 %process_asm_iter ; keep going
:process_asm_done
LOADI32_EAX &process_asm_string_2 ; Using "ERROR in process_asm\nMISSING )\n"
LOADI32_EBX &close_paren ; Using ")"
CALL32 %require_match ; Make sure we have it
LOADI32_EAX &process_asm_string_3 ; Using "ERROR in process_asm\nMISSING ;\n"
LOADI32_EBX &semicolon ; Using ";"
CALL32 %require_match ; Make sure we have it
POP_EBX ; Restore EBX
RETURN
:process_asm_string_0 "ERROR in process_asm
MISSING (
"
:process_asm_string_1 "
"
:process_asm_string_2 "ERROR in process_asm
MISSING )
"
:process_asm_string_3 "ERROR in process_asm
MISSING ;
"
;; process_if function
;; Receives nothing
;; Returns Nothing
;; Increments current_count recurses into expression + statement
;; Uses ECX for char* NUMBER_STRING
:process_if
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
LOAD32_Absolute32_eax ¤t_count ; Using current count
COPY_EAX_to_EBX ; Preparing for update
ADDI8_EBX !1 ; current_count + 1
STORE32_Absolute32_ebx ¤t_count ; current_count = current_count + 1
CALL32 %numerate_number ; convert to string
COPY_EAX_to_ECX ; put NUMBER_STRING in place
LOADI32_EAX &process_if_string_0 ; Using "# IF_"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S
COPY_ECX_to_EBX ; Passing NUMBER_STRING
CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string)
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->NEXT
STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT
LOADI32_EAX &process_if_string_1 ; Using "ERROR in process_if\nMISSING (\n"
LOADI32_EBX &open_paren ; Using "("
CALL32 %require_match ; Make sure we have it
CALL32 %expression ; Recurse to get the IF(...) part
LOADI32_EAX &process_if_string_2 ; Using "test_eax,eax\nje %ELSE_"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S
COPY_ECX_to_EBX ; Passing NUMBER_STRING
CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string)
LOADI32_EAX &process_if_string_3 ; Using "ERROR in process_if\nMISSING )\n"
LOADI32_EBX &close_paren ; Using ")"
CALL32 %require_match ; Make sure we have it
CALL32 %statement ; Recursive to get the IF(){...} part
LOADI32_EAX &process_if_string_4 ; Using "jmp %_END_IF_"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S
COPY_ECX_to_EBX ; Passing NUMBER_STRING
CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string)
LOADI32_EAX &process_if_string_5 ; Using ":ELSE_"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S
CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string)
LOAD32_Absolute32_ebx &global_token ; Using global_token
LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S
LOADI32_EAX &else_string ; Using "else"
CALL32 %match ; IF global_token->S == "else"
CMPI8_EAX !0 ; Then we need to collect the else too
JNE32 %process_if_done ; Otherwise finish up
;; deal with else statement
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->NEXT
STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT
CALL32 %statement ; Recurse to get the ELSE {...} part
:process_if_done
LOADI32_EAX &process_if_string_6 ; Using ":_END_IF_"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S
COPY_ECX_to_EBX ; Passing NUMBER_STRING
CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string)
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:process_if_string_0 "# IF_"
:process_if_string_1 "ERROR in process_if
MISSING (
"
:process_if_string_2 "test_eax,eax
je %ELSE_"
:process_if_string_3 "ERROR in process_if
MISSING )
"
:process_if_string_4 "jmp %_END_IF_"
:process_if_string_5 ":ELSE_"
:process_if_string_6 ":_END_IF_"
;; save_break_frame microfunction
;; Overwrites EAX and EBX
;; Saves break frame on stack
;; Returns to caller
:save_break_frame
POP_EBX ; Save return Address
LOAD32_Absolute32_eax &break_frame ; Get break_frame
PUSH_EAX ; Store as nested_locals
LOAD32_Absolute32_eax &break_target_head ; Get break_target_head
PUSH_EAX ; Store as nested_break_head
LOAD32_Absolute32_eax &break_target_func ; Get break_target_func
PUSH_EAX ; Store as nested_break_func
LOAD32_Absolute32_eax &break_target_num ; Get break_target_num
PUSH_EAX ; Store as nested_break_num
PUSH_EBX ; Put return back in place
RETURN ; Return to caller
;; restore_break_frame microfunction
;; Overwrites EAX and EBX
;; Restores break frame from stack
;; Returns to caller
:restore_break_frame
POP_EBX ; Save return Address
POP_EAX ; Get nested_break_num
STORE32_Absolute32_eax &break_target_num ; Restore break_target_num
POP_EAX ; Get nested_break_func
STORE32_Absolute32_eax &break_target_func ; Restore break_target_func
POP_EAX ; Get nested_break_head
STORE32_Absolute32_eax &break_target_head ; Restore break_target_head
POP_EAX ; Get nested_locals
STORE32_Absolute32_eax &break_frame ; Restore break_frame
PUSH_EBX ; Put return back in place
RETURN ; Return to caller
;; set_break_frame microfunction
;; Receives char* head in EAX and char* num in EBX
;; Overwrites EAX and EBX
;; Returns to calling function
:set_break_frame
STORE32_Absolute32_eax &break_target_head ; update break_target_head
STORE32_Absolute32_ebx &break_target_num ; update break_target_num
LOAD32_Absolute32_ebx &function ; Using function
LOAD32_EAX_from_EBX_Immediate8 !4 ; function->LOCALS
STORE32_Absolute32_eax &break_frame ; break_frame = function->LOCALS
LOAD32_EAX_from_EBX_Immediate8 !8 ; function->S
STORE32_Absolute32_eax &break_target_func ; break_target_func = function->S
RETURN ; Return to sender
;; process_do function
;; Receives Nothing
;; Returns Nothing
;; Increments current_count and leverages save/restore_break_frame pieces
;; Uses ECX for char* NUMBER_STRING
:process_do
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
CALL32 %save_break_frame ; Save the frame
LOAD32_Absolute32_eax ¤t_count ; Using current count
COPY_EAX_to_EBX ; Preparing for update
ADDI8_EBX !1 ; current_count + 1
STORE32_Absolute32_ebx ¤t_count ; current_count = current_count + 1
CALL32 %numerate_number ; convert to string
COPY_EAX_to_ECX ; put NUMBER_STRING in place
LOADI32_EAX &process_do_string_0 ; Using "DO_END_"
COPY_ECX_to_EBX ; Passing NUMBER_STRING
CALL32 %set_break_frame ; Set the frame
LOADI32_EAX &process_do_string_1 ; Using ":DO_"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S
COPY_ECX_to_EBX ; Passing NUMBER_STRING
CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string)
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->NEXT
STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT
CALL32 %statement ; Do the DO {...} part
LOADI32_EAX &process_do_string_2 ; Using "ERROR in process_do\nMISSING while\n"
LOADI32_EBX &while_string ; Using "while"
CALL32 %require_match ; Make sure we have it
LOADI32_EAX &process_do_string_3 ; Using "ERROR in process_do\nMISSING (\n"
LOADI32_EBX &open_paren ; Using "("
CALL32 %require_match ; Make sure we have it
CALL32 %expression ; Do the WHILE (...) part
LOADI32_EAX &process_do_string_4 ; Using "ERROR in process_do\nMISSING )\n"
LOADI32_EBX &close_paren ; Using ")"
CALL32 %require_match ; Make sure we have it
LOADI32_EAX &process_do_string_5 ; Using "ERROR in process_do\nMISSING ;\n"
LOADI32_EBX &semicolon ; Using ";"
CALL32 %require_match ; Make sure we have it
LOADI32_EAX &process_do_string_6 ; Using "test_eax,eax\njne %DO_"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S
COPY_ECX_to_EBX ; Passing NUMBER_STRING
CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string)
LOADI32_EAX &process_do_string_7 ; Using ":DO_END_"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S
CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string)
CALL32 %restore_break_frame ; Restore the old break frame
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:process_do_string_0 "DO_END_"
:process_do_string_1 ":DO_"
:process_do_string_2 "ERROR in process_do
MISSING while
"
:process_do_string_3 "ERROR in process_do
MISSING (
"
:process_do_string_4 "ERROR in process_do
MISSING )
"
:process_do_string_5 "ERROR in process_do
MISSING ;
"
:process_do_string_6 "test_eax,eax
jne %DO_"
:process_do_string_7 ":DO_END_"
;; process_while function
;; Receives nothing
;; Returns nothing
;; Increments current_count and leverages save/restore_break_frame pieces
;; Uses ECX for char* NUMBER_STRING
:process_while
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
CALL32 %save_break_frame ; Save break_frame
LOAD32_Absolute32_eax ¤t_count ; Using current count
COPY_EAX_to_EBX ; Preparing for update
ADDI8_EBX !1 ; current_count + 1
STORE32_Absolute32_ebx ¤t_count ; current_count = current_count + 1
CALL32 %numerate_number ; convert to string
COPY_EAX_to_ECX ; put NUMBER_STRING in place
LOADI32_EAX &process_while_string_0 ; Using "END_WHILE_"
COPY_ECX_to_EBX ; Passing NUMBER_STRING
CALL32 %set_break_frame ; Set it and forget it
LOADI32_EAX &process_while_string_1 ; Using ":WHILE_"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S
COPY_ECX_to_EBX ; Passing NUMBER_STRING
CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string)
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->NEXT
STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT
LOADI32_EAX &process_while_string_2 ; Using "ERROR in process_while\nMISSING (\n"
LOADI32_EBX &open_paren ; Using "("
CALL32 %require_match ; Make sure we have it
CALL32 %expression ; Deal with the WHILE (...) part
LOADI32_EAX &process_while_string_3 ; Using "test_eax,eax\nje %END_WHILE_"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S
COPY_ECX_to_EBX ; Passing NUMBER_STRING
CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string)
LOADI32_EAX &process_while_string_4 ; Using "# THEN_while_"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S
CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string)
LOADI32_EAX &process_while_string_5 ; Using "ERROR in process_while\nMISSING )\n"
LOADI32_EBX &close_paren ; Using ")"
CALL32 %require_match ; Make sure we have it
CALL32 %statement ; Deal with the {....} part
LOADI32_EAX &process_while_string_6 ; Using "jmp %WHILE_"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S
COPY_ECX_to_EBX ; Passing NUMBER_STRING
CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string)
LOADI32_EAX &process_while_string_7 ; Using ":END_WHILE_"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S
CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string)
CALL32 %restore_break_frame ; Restore the old break frame
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:process_while_string_0 "END_WHILE_"
:process_while_string_1 ":WHILE_"
:process_while_string_2 "ERROR in process_while
MISSING (
"
:process_while_string_3 "test_eax,eax
je %END_WHILE_"
:process_while_string_4 "# THEN_while_"
:process_while_string_5 "ERROR in process_while
MISSING )
"
:process_while_string_6 "jmp %WHILE_"
:process_while_string_7 ":END_WHILE_"
;; process_for function
;; Receives Nothing
;; Returns Nothing
;; Increments current_count and leverages save/restore_break_frame pieces
;; Uses ECX for char* NUMBER_STRING
:process_for
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
CALL32 %save_break_frame ; Save the frame
LOAD32_Absolute32_eax ¤t_count ; Using current count
COPY_EAX_to_EBX ; Preparing for update
ADDI8_EBX !1 ; current_count + 1
STORE32_Absolute32_ebx ¤t_count ; current_count = current_count + 1
CALL32 %numerate_number ; convert to string
COPY_EAX_to_ECX ; put NUMBER_STRING in place
LOADI32_EAX &process_for_string_0 ; Using "FOR_END_"
COPY_ECX_to_EBX ; Passing NUMBER_STRING
CALL32 %set_break_frame ; Set it and forget it
LOADI32_EAX &process_for_string_1 ; Using "# FOR_initialization_"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S
COPY_ECX_to_EBX ; Passing NUMBER_STRING
CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string)
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->NEXT
STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT
LOADI32_EAX &process_for_string_2 ; Using "ERROR in process_for\nMISSING (\n"
LOADI32_EBX &open_paren ; Using "("
CALL32 %require_match ; Make Sure we have it
LOAD32_Absolute32_ebx &global_token ; Using global_token
LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S
LOADI32_EAX &semicolon ; Using ";"
CALL32 %match ; IF global_token->S == ";"
CMPI8_EAX !0 ; Then no initializer
JE32 %process_for_terminator ; And skip getting the expression
;; Deal with FOR (...; case
CALL32 %expression ; Get the FOR ( ... ; part
:process_for_terminator
LOADI32_EAX &process_for_string_3 ; Using ":FOR_"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S
COPY_ECX_to_EBX ; Passing NUMBER_STRING
CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string)
LOADI32_EAX &process_for_string_4 ; Using "ERROR in process_for\nMISSING ;1\n"
LOADI32_EBX &semicolon ; Using ";"
CALL32 %require_match ; Make sure we have it
CALL32 %expression ; Get the FOR ( ; ... ; Part
LOADI32_EAX &process_for_string_5 ; Using "test_eax,eax\nje %FOR_END_"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S
COPY_ECX_to_EBX ; Passing NUMBER_STRING
CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string)
LOADI32_EAX &process_for_string_6 ; Using "jmp %FOR_THEN_"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S
CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string)
LOADI32_EAX &process_for_string_7 ; Using ":FOR_ITER_"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S
CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string)
LOADI32_EAX &process_for_string_8 ; Using "ERROR in process_for\nMISSING ;2\n"
LOADI32_EBX &semicolon ; Using ";"
CALL32 %require_match ; Make sure we have it
CALL32 %expression ; Get the FOR (;;...) part
LOADI32_EAX &process_for_string_9 ; Using "jmp %FOR_"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S
COPY_ECX_to_EBX ; Passing NUMBER_STRING
CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string)
LOADI32_EAX &process_for_string_10 ; Using ":FOR_THEN_"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S
CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string)
LOADI32_EAX &process_for_string_11 ; Using "ERROR in process_for\nMISSING )\n"
LOADI32_EBX &close_paren ; Using ")"
CALL32 %require_match ; Make sure we have it
CALL32 %statement ; Get FOR (;;) {...} part
LOADI32_EAX &process_for_string_12 ; Using "jmp %FOR_ITER_"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S
COPY_ECX_to_EBX ; Passing NUMBER_STRING
CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string)
LOADI32_EAX &process_for_string_13 ; Using ":FOR_END_"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S
CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string)
CALL32 %restore_break_frame ; Restore the old break frame
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:process_for_string_0 "FOR_END_"
:process_for_string_1 "# FOR_initialization_"
:process_for_string_2 "ERROR in process_for
MISSING (
"
:process_for_string_3 ":FOR_"
:process_for_string_4 "ERROR in process_for
MISSING ;1
"
:process_for_string_5 "test_eax,eax
je %FOR_END_"
:process_for_string_6 "jmp %FOR_THEN_"
:process_for_string_7 ":FOR_ITER_"
:process_for_string_8 "ERROR in process_for
MISSING ;2
"
:process_for_string_9 "jmp %FOR_"
:process_for_string_10 ":FOR_THEN_"
:process_for_string_11 "ERROR in process_for
MISSING )
"
:process_for_string_12 "jmp %FOR_ITER_"
:process_for_string_13 ":FOR_END_"
;; process_break function
;; Receives nothing
;; Returns nothing
;; Handles the break out of loops case
;; Uses EBX for struct token_list* break_frame and ECX for struct token_list* I
:process_break
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
LOAD32_Absolute32_eax &break_target_head ; Catch big error
CMPI8_EAX !0 ; IF(NULL == break_target_head)
JE32 %process_break_bad ; I'm sorry Mr White but you have stage-3 lung cancer
LOAD32_Absolute32_eax &function ; Using function
LOAD32_ECX_from_EAX_Immediate8 !4 ; I = function->LOCALS
LOAD32_Absolute32_ebx &break_frame ; Put break_frame in the right spot
LOADI32_EAX &process_break_string_1 ; Using "pop_ebx\t# break_cleanup_locals\n"
:process_break_iter
CMPI8_ECX !0 ; IF (NULL == I)
JE32 %process_break_cleaned ; We are done
CMP_EBX_ECX ; IF I != break_frame
JE32 %process_break_cleaned ; We are done
CALL32 %emit_out ; Emit it
LOAD32_ECX_from_ECX ; I = I->NEXT
JMP32 %process_break_iter ; Keep looping
:process_break_cleaned
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->NEXT
STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT
LOADI32_EAX &process_break_string_2 ; Using "jmp %"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &break_target_head ; Get what we are in
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &break_target_func ; Get what function we are in
CALL32 %emit_out ; Emit it
LOADI32_EAX &underline ; Using "_"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &break_target_num ; Get dem digits
CALL32 %emit_out ; Emit it
LOADI32_EAX &process_break_string_3 ; Using "\n"
CALL32 %emit_out ; Emit it
LOADI32_EAX &process_break_string_4 ; Using "ERROR in break statement\nMissing ;\n"
LOADI32_EBX &semicolon ; Using ";"
CALL32 %require_match ; Make sure we have it
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:process_break_bad
;; Breaking badly
LOADI32_EAX %2 ; Using standard error
STORE32_Absolute32_eax &Output_file ; write to standard error
; CALL32 %line_error ; Write useful debug info
COPY_ECX_to_EAX ; put S in the right place
CALL32 %File_Print ; print it
LOADI32_EAX &process_break_string_0 ; Ending string
CALL32 %File_Print ; print it
JMP32 %Exit_Failure ; Abort Hard
:process_break_string_0 "Not inside of a loop or case statement"
:process_break_string_1 "pop_ebx # break_cleanup_locals
"
:process_break_string_2 "jmp %"
:process_break_string_3 "
"
:process_break_string_4 "ERROR in break statement
Missing ;
"
;; expression function
;; Receives Nothing
;; Returns Nothing
;; Walks global_token and updates output_list
;; Uses EAX and EBX for match and ECX for char* store
:expression
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
CALL32 %bitwise_expr ; Collect bitwise expressions
LOAD32_Absolute32_ebx &global_token ; Using global_token
LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S
LOADI32_EAX &equal ; "="
CALL32 %match ; IF global_token->S == "="
CMPI8_EAX !0 ; We have to deal with assignment
JNE32 %expression_done ; Looks like nope
;; Deal with possible assignment
LOADI32_ECX &expression_string_1 ; Assume "mov_[ebx],al\n" by default
LOAD32_Absolute32_ebx &global_token ; Using global_token
LOAD32_EBX_from_EBX_Immediate8 !4 ; global_token->PREV
LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->PREV->S
LOADI32_EAX &close_bracket ; Using "]"
CALL32 %match ; IF global_token->S == "]"
CMPI8_EAX !0 ; Then we might have a char
JNE32 %expression_int ; Otherwise INT
LOAD32_Absolute32_ebx ¤t_target ; Using current_target
LOAD32_EBX_from_EBX_Immediate8 !24 ; current_target->NAME
LOADI32_EAX &type_char_indirect_name ; Using "char*"
CALL32 %match ; Intentional inefficiency because I feel like it
CMPI8_EAX !0 ; IF current_target->NAME == "char*"
JNE32 %expression_int ; Do char anyway
JMP32 %expression_common ; Looks like we have to use "mov_[ebx],al\n"
:expression_int
LOADI32_ECX &expression_string_0 ; Use "mov_[ebx],eax\n"
:expression_common
LOADI32_EAX &expression ; Passing expression
CALL32 %common_recursion ; Recurse
COPY_ECX_to_EAX ; Using Store
CALL32 %emit_out ; Emit it
LOADI32_EAX %0 ; Using NULL
STORE32_Absolute32_eax ¤t_target ; current_target = NULL
:expression_done
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:expression_string_0 "mov_[ebx],eax
"
:expression_string_1 "mov_[ebx],al
"
;; bitwise_expr function
;; Receives nothing
;; Returns nothing
;; Walks global_token list and updates output list
;; Just calls other functions
:bitwise_expr
CALL32 %relational_expr ; Walk up the tree
CALL32 %bitwise_expr_stub ; Let general recursion do the work
RETURN
;; bitwise_expr_stub function
;; Receives nothing
;; Returns Nothing
;; Just calls general_recursion a bunch
;; Uses EAX, EBX, ECX and EDX for passing constants to general recursion
:bitwise_expr_stub
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
PUSH_EDX ; Protect EDX
LOADI32_EAX &relational_expr ; Using relational_expr
LOADI32_EBX &bitwise_expr_stub_string_0 ; Using "and_eax,ebx\n"
LOADI32_ECX &bitwise_and ; Using "&"
LOADI32_EDX &bitwise_expr_stub ; And recurse
CALL32 %general_recursion ; Hit it
LOADI32_EAX &relational_expr ; Using relational_expr
LOADI32_EBX &bitwise_expr_stub_string_0 ; Using "and_eax,ebx\n"
LOADI32_ECX &logical_and ; Using "&&"
LOADI32_EDX &bitwise_expr_stub ; And recurse
CALL32 %general_recursion ; Hit it
LOADI32_EAX &relational_expr ; Using relational_expr
LOADI32_EBX &bitwise_expr_stub_string_1 ; Using "or_eax,ebx\n"
LOADI32_ECX &bitwise_or ; Using "|"
LOADI32_EDX &bitwise_expr_stub ; And recurse
CALL32 %general_recursion ; Hit it
LOADI32_EAX &relational_expr ; Using relational_expr
LOADI32_EBX &bitwise_expr_stub_string_1 ; Using "or_eax,ebx\n"
LOADI32_ECX &logical_or ; Using "||"
LOADI32_EDX &bitwise_expr_stub ; And recurse
CALL32 %general_recursion ; Hit it
LOADI32_EAX &relational_expr ; Using relational_expr
LOADI32_EBX &bitwise_expr_stub_string_2 ; Using "xor_eax,ebx\n"
LOADI32_ECX &bitwise_xor ; Using "^"
LOADI32_EDX &bitwise_expr_stub ; And recurse
CALL32 %general_recursion ; Hit it
POP_EDX ; Restore EDX
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:bitwise_expr_stub_string_0 "and_eax,ebx
"
:bitwise_expr_stub_string_1 "or_eax,ebx
"
:bitwise_expr_stub_string_2 "xor_eax,ebx
"
;; relational_expr function
;; Receives nothing
;; Returns Nothing
;; Walks global_token list and updates output list
;; just calls other function
:relational_expr
CALL32 %additive_expr ; Walk up the tree
CALL32 %relational_expr_stub ; Recurse
RETURN
;; relational_expr_stub function
;; Receives nothing
;; Returns Nothing
;; Just calls general_recursion a bunch
;; Uses EAX, EBX, ECX and EDX for passing constants to general recursion
:relational_expr_stub
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
PUSH_EDX ; Protect EDX
LOADI32_EAX &additive_expr ; Using additive_expr
LOADI32_EBX &relational_expr_stub_string_0 ; Using "cmp\nsetl_al\nmovzx_eax,al\n"
LOADI32_ECX &less_than_string ; Using "<"
LOADI32_EDX &relational_expr_stub ; And recurse
CALL32 %general_recursion ; Hit it
LOADI32_EAX &additive_expr ; Using additive_expr
LOADI32_EBX &relational_expr_stub_string_1 ; Using "cmp\nsetle_al\nmovzx_eax,al\n"
LOADI32_ECX &less_than_equal_string ; Using "<="
LOADI32_EDX &relational_expr_stub ; And recurse
CALL32 %general_recursion ; Hit it
LOADI32_EAX &additive_expr ; Using additive_expr
LOADI32_EBX &relational_expr_stub_string_2 ; Using "cmp\nsetge_al\nmovzx_eax,al\n"
LOADI32_ECX &greater_than_equal_string ; Using ">="
LOADI32_EDX &relational_expr_stub ; And recurse
CALL32 %general_recursion ; Hit it
LOADI32_EAX &additive_expr ; Using additive_expr
LOADI32_EBX &relational_expr_stub_string_3 ; Using "cmp\nsetg_al\nmovzx_eax,al\n"
LOADI32_ECX &greater_than_string ; Using ">"
LOADI32_EDX &relational_expr_stub ; And recurse
CALL32 %general_recursion ; Hit it
LOADI32_EAX &additive_expr ; Using additive_expr
LOADI32_EBX &relational_expr_stub_string_4 ; Using "cmp\nsete_al\nmovzx_eax,al\n"
LOADI32_ECX &equal_to_string ; Using "=="
LOADI32_EDX &relational_expr_stub ; And recurse
CALL32 %general_recursion ; Hit it
LOADI32_EAX &additive_expr ; Using additive_expr
LOADI32_EBX &relational_expr_stub_string_5 ; Using "cmp\nsetne_al\nmovzx_eax,al\n"
LOADI32_ECX ¬_equal_string ; Using "!="
LOADI32_EDX &relational_expr_stub ; And recurse
CALL32 %general_recursion ; Hit it
POP_EDX ; Restore EDX
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:relational_expr_stub_string_0 "cmp
setl_al
movzx_eax,al
"
:relational_expr_stub_string_1 "cmp
setle_al
movzx_eax,al
"
:relational_expr_stub_string_2 "cmp
setge_al
movzx_eax,al
"
:relational_expr_stub_string_3 "cmp
setg_al
movzx_eax,al
"
:relational_expr_stub_string_4 "cmp
sete_al
movzx_eax,al
"
:relational_expr_stub_string_5 "cmp
setne_al
movzx_eax,al
"
;; additive_expr function
;; Receives nothing
;; Returns Nothing
;; Walks global_token list and updates output list
;; just calls other function
:additive_expr
CALL32 %postfix_expr ; Walk up the tree
CALL32 %additive_expr_stub ; Recurse
RETURN
;; additive_expr_stub function
;; Receives nothing
;; Returns Nothing
;; Just calls general_recursion a bunch
;; Uses EAX, EBX, ECX and EDX for passing constants to general recursion
:additive_expr_stub
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
PUSH_EDX ; Protect EDX
LOADI32_EAX &postfix_expr ; Using postfix_expr
LOADI32_EBX &additive_expr_stub_string_0 ; Using "add_eax,ebx\n"
LOADI32_ECX &plus_string ; Using "+"
LOADI32_EDX &additive_expr_stub ; And recurse
CALL32 %general_recursion ; Hit it
LOADI32_EAX &postfix_expr ; Using postfix_expr
LOADI32_EBX &additive_expr_stub_string_1 ; Using "sub_ebx,eax\nmov_eax,ebx\n"
LOADI32_ECX &minus_string ; Using "-"
LOADI32_EDX &additive_expr_stub ; And recurse
CALL32 %general_recursion ; Hit it
LOADI32_EAX &postfix_expr ; Using postfix_expr
LOADI32_EBX &additive_expr_stub_string_2 ; Using "mul_ebx\n"
LOADI32_ECX &multiply_string ; Using "*"
LOADI32_EDX &additive_expr_stub ; And recurse
CALL32 %general_recursion ; Hit it
LOADI32_EAX &postfix_expr ; Using postfix_expr
LOADI32_EBX &additive_expr_stub_string_3 ; Using "xchg_ebx,eax\nmov_edx, %0\ndiv_ebx\n"
LOADI32_ECX ÷_string ; Using "/"
LOADI32_EDX &additive_expr_stub ; And recurse
CALL32 %general_recursion ; Hit it
LOADI32_EAX &postfix_expr ; Using postfix_expr
LOADI32_EBX &additive_expr_stub_string_4 ; Using "xchg_ebx,eax\nmov_edx, %0\ndiv_ebx\nmov_eax,edx\n"
LOADI32_ECX &modulus_string ; Using "%"
LOADI32_EDX &additive_expr_stub ; And recurse
CALL32 %general_recursion ; Hit it
LOADI32_EAX &postfix_expr ; Using postfix_expr
LOADI32_EBX &additive_expr_stub_string_5 ; Using "mov_ecx,eax\nmov_eax,ebx\nsal_eax,cl\n"
LOADI32_ECX &left_shift_string ; Using "<<"
LOADI32_EDX &additive_expr_stub ; And recurse
CALL32 %general_recursion ; Hit it
LOADI32_EAX &postfix_expr ; Using postfix_expr
LOADI32_EBX &additive_expr_stub_string_6 ; Using "mov_ecx,eax\nmov_eax,ebx\nsar_eax,cl\n"
LOADI32_ECX &right_shift_string ; Using ">>"
LOADI32_EDX &additive_expr_stub ; And recurse
CALL32 %general_recursion ; Hit it
POP_EDX ; Restore EDX
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:additive_expr_stub_string_0 "add_eax,ebx
"
:additive_expr_stub_string_1 "sub_ebx,eax
mov_eax,ebx
"
:additive_expr_stub_string_2 "mul_ebx
"
:additive_expr_stub_string_3 "xchg_ebx,eax
mov_edx, %0
div_ebx
"
:additive_expr_stub_string_4 "xchg_ebx,eax
mov_edx, %0
div_ebx
mov_eax,edx
"
:additive_expr_stub_string_5 "mov_ecx,eax
mov_eax,ebx
sal_eax,cl
"
:additive_expr_stub_string_6 "mov_ecx,eax
mov_eax,ebx
sar_eax,cl
"
;; postfix_expr function
;; Receives nothing
;; Returns Nothing
;; Walks global_token list and updates output list
;; just calls other function
:postfix_expr
CALL32 %primary_expr ; Walk up the tree
CALL32 %postfix_expr_stub ; Recurse
RETURN
;; postfix_expr_stub function
;; Receives nothing
;; Returns Nothing
;; Checks for "[" and "->" and deals with them otherwise does nothing
;; Uses EAX, EBX, ECX and EDX for passing constants to general recursion
:postfix_expr_stub
PUSH_EBX ; Protect EBX
LOAD32_Absolute32_ebx &global_token ; Using global_token
LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S
LOADI32_EAX &open_bracket ; Using "["
CALL32 %match ; IF global_token->S == "["
CMPI8_EAX !0 ; then we have an array
JNE32 %postfix_expr_stub_arrow ; Otherwise try arrow
;; Deal with array
CALL32 %postfix_expr_array ; Get it
CALL32 %postfix_expr_stub ; Recurse
:postfix_expr_stub_arrow
LOADI32_EAX &arrow_string ; Using "->"
CALL32 %match ; IF global_token->S == "->"
CMPI8_EAX !0 ; Then we need to deal with struct offsets
JNE32 %postfix_expr_stub_done ; Otherwise be done
;; Deal with arrow
CALL32 %postfix_expr_arrow ; Get it
CALL32 %postfix_expr_stub ; Recurse
:postfix_expr_stub_done
POP_EBX ; Restore EBX
RETURN
;; unary_expr_sizeof function
;; Receives nothing
;; Returns nothing
;; Uses ECX for A->SIZE
:unary_expr_sizeof
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->NEXT
STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT
LOADI32_EAX &unary_expr_sizeof_string_0 ; Using "ERROR in unary_expr\nMissing (\n"
LOADI32_EBX &open_paren ; Using "("
CALL32 %require_match ; Make sure we have it
CALL32 %type_name ; Get the type
LOAD32_ECX_from_EAX_Immediate8 !4 ; Set A->TYPE
LOADI32_EAX &unary_expr_sizeof_string_1 ; Using "ERROR in unary_expr\nMissing )\n"
LOADI32_EBX &close_paren ; Using ")"
CALL32 %require_match ; Make sure we have it
LOADI32_EAX &unary_expr_sizeof_string_2 ; Using "mov_eax, %"
CALL32 %emit_out ; Emit it
COPY_ECX_to_EAX ; Put A->SIZE in the right place
CALL32 %numerate_number ; Turn into string
CALL32 %emit_out ; Emit it
LOADI32_EAX &unary_expr_sizeof_string_3 ; Using "\n"
CALL32 %emit_out ; Emit it
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:unary_expr_sizeof_string_0 "ERROR in unary_expr
Missing (
"
:unary_expr_sizeof_string_1 "ERROR in unary_expr
Missing )
"
:unary_expr_sizeof_string_2 "mov_eax, %"
:unary_expr_sizeof_string_3 "
"
;; postfix_expr_array function
;; Receives Nothing
;; Returns Nothing
;; Uses EBX for struct type* ARRAY and ECX for char* ASSIGN
:postfix_expr_array
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
LOAD32_Absolute32_eax ¤t_target ; ARRAY = current_target
PUSH_EAX ; Protect it
LOADI32_EAX &expression ; Using expression
CALL32 %common_recursion ; Recurse
POP_EBX ; Restore array
STORE32_Absolute32_ebx ¤t_target ; current_target = ARRAY
LOADI32_ECX &postfix_expr_array_string_0 ; ASSIGN = "mov_eax,[eax]\n"
LOADI32_EAX &type_char_indirect_name ; Using "char*"
LOAD32_EBX_from_EBX_Immediate8 !24 ; current_target->NAME
CALL32 %match ; IF current_target->NAME == "char*"
CMPI8_EAX !0 ; load a byte
JNE32 %postfix_expr_array_large ; Otherwise adjust
;; Deal with loading byte
LOADI32_ECX &postfix_expr_array_string_1 ; ASSIGN = "movsx_eax,BYTE_PTR_[eax]\n"
JMP32 %postfix_expr_array_common ; Do the next bit
:postfix_expr_array_large
;; deal with arrays made of things other than chars
LOADI32_EAX &postfix_expr_array_string_2 ; Using "sal_eax, !"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax ¤t_target ; Using current_target
LOAD32_EAX_from_EAX_Immediate8 !12 ; current_target->INDIRECT
LOAD32_EAX_from_EAX_Immediate8 !4 ; current_target->INDIRECT->SIZE
CALL32 %ceil_log2 ; ceil_log2(current_target->indirect->size)
CALL32 %numerate_number ; numerate_number(ceil_log2(current_target->indirect->size))
CALL32 %emit_out ; Emit it
LOADI32_EAX &postfix_expr_array_string_3 ; Using "\n"
CALL32 %emit_out ; Emit it
:postfix_expr_array_common
LOADI32_EAX &postfix_expr_array_string_4 ; Using "add_eax,ebx\n"
CALL32 %emit_out ; Emit it
LOADI32_EAX &postfix_expr_array_string_5 ; Using "ERROR in postfix_expr\nMissing ]\n"
LOADI32_EBX &close_bracket ; Using "]"
CALL32 %require_match ; Make sure we have it
LOAD32_Absolute32_ebx &global_token ; Using global_token
LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S
LOADI32_EAX &equal ; Using "="
CALL32 %match ; IF global_token->S == "="
CMPI8_EAX !0 ; We need to preserve address
JNE32 %postfix_expr_array_done ; Otherwise be done
;; Clearing out assign
LOADI32_ECX &postfix_expr_array_string_6 ; ASSIGN = ""
:postfix_expr_array_done
COPY_ECX_to_EAX ; Using ASSIGN
CALL32 %emit_out ; Emit it
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:postfix_expr_array_string_0 "mov_eax,[eax]
"
:postfix_expr_array_string_1 "movsx_eax,BYTE_PTR_[eax]
"
:postfix_expr_array_string_2 "sal_eax, !"
:postfix_expr_array_string_3 "
"
:postfix_expr_array_string_4 "add_eax,ebx
"
:postfix_expr_array_string_5 "ERROR in postfix_expr
Missing ]
"
:postfix_expr_array_string_6 ""
;; ceil_log2 function
;; Receives int a in EAX
;; Performs log2 on A and
;; Returns result in EAX
;; Uses EBX for INT A and ECX for INT RESULT
:ceil_log2
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
LOADI32_ECX %0 ; RESULT = 0
COPY_EAX_to_EBX ; put A in right place
SUBI8_EAX !1 ; (A - 1)
AND_EAX_EBX ; A & (A - 1)
CMPI8_EAX !0 ; IF 0 == (A & (A - 1))
JNE32 %ceil_log2_iter ; Starting from -1
LOADI32_ECX %-1 ; RESULT = -1
:ceil_log2_iter
CMPI8_EBX !0 ; IF A > 0
JLE32 %ceil_log2_done ; Otherwise be done
ADDI8_ECX !1 ; RESULT = RESULT + 1
SHRI8_EBX !1 ; A = A >> 1
JMP32 %ceil_log2_iter ; Keep looping
:ceil_log2_done
COPY_ECX_to_EAX ; Return RESULT
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
;; postfix_expr_arrow function
;; Receives nothing
;; Returns nothing
;; Emits a bunch and updates current_target
;; Uses EBX for struct type* I
:postfix_expr_arrow
PUSH_EBX ; Protect EBX
LOADI32_EAX &postfix_expr_arrow_string_0 ; Using "# looking up offset\n"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->NEXT
STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT
LOAD32_EBX_from_EAX_Immediate8 !8 ; Using global_token->S
LOAD32_Absolute32_eax ¤t_target ; Using current_target
CALL32 %lookup_member ; lookup_member(current_target, global_token->s)
COPY_EAX_to_EBX ; struct type* I = lookup_member(current_target, global_token->s)
LOAD32_EAX_from_EAX_Immediate8 !20 ; I->TYPE
STORE32_Absolute32_eax ¤t_target ; current_target = I->TYPE
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->NEXT
STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT
LOAD32_EAX_from_EBX_Immediate8 !8 ; I->OFFSET
CMPI8_EAX !0 ; IF 0 != I->OFFSET
JE32 %postfix_expr_arrow_first ; Then we don't need to do an offset
;; Deal with needing an offset
LOADI32_EAX &postfix_expr_arrow_string_1 ; Using "# -> offset calculation\nmov_ebx, %"
CALL32 %emit_out ; Emit it
LOAD32_EAX_from_EBX_Immediate8 !8 ; I->OFFSET
CALL32 %numerate_number ; Convert to string
CALL32 %emit_out ; Emit it
LOADI32_EAX &postfix_expr_arrow_string_2 ; Using "\nadd_eax,ebx\n"
CALL32 %emit_out ; Emit it
:postfix_expr_arrow_first
LOAD32_EAX_from_EBX_Immediate8 !4 ; I->SIZE
CMPI8_EAX !4 ; IF I->SIZE >= 4
JL32 %postfix_expr_arrow_done ; Otherwise be done
;; Last chance for load
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EBX_from_EAX_Immediate8 !8 ; global_token->S
LOADI32_EAX &equal ; Using "="
CALL32 %match ; IF global_token->S == "="
CMPI8_EAX !0 ; Then we have assignment and should not load
JE32 %postfix_expr_arrow_done ; Be done
;; Deal with load case
LOADI32_EAX &postfix_expr_arrow_string_3 ; Using "mov_eax,[eax]\n"
CALL32 %emit_out ; Emit it
:postfix_expr_arrow_done
POP_EBX ; Restore EBX
RETURN
:postfix_expr_arrow_string_0 "# looking up offset
"
:postfix_expr_arrow_string_1 "# -> offset calculation
mov_ebx, %"
:postfix_expr_arrow_string_2 "
add_eax,ebx
"
:postfix_expr_arrow_string_3 "mov_eax,[eax]
"
;; primary_expr function
;; Receives nothing
;; Returns nothing
:primary_expr
PUSH_EBX ; Protect EBX
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EBX_from_EAX_Immediate8 !8 ; global_token->S
LOADI32_EAX &sizeof_string ; Using "sizeof"
CALL32 %match ; See if match
CMPI8_EAX !0 ; IF match
JNE32 %primary_expr_neg ; Otherwise try negatives
;; Deal with sizeof
CALL32 %unary_expr_sizeof ; Lets do this
JMP32 %primary_expr_done ; Be done
:primary_expr_neg
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S
LOAD8_al_from_EAX ; global_token->S[0]
MOVZX_al ; Make it useful
CMPI8_EAX !45 ; IF global_token->S[0] == "-"
JNE32 %primary_expr_not ; Otherwise try logical NOT
;; Deal with negative numbers
LOADI32_EAX &primary_expr_string_0 ; Using "mov_eax, %0\n"
CALL32 %emit_out ; Emit it
LOADI32_EAX &postfix_expr ; Passing postfix_expr
CALL32 %common_recursion ; Get what it is notting
LOADI32_EAX &primary_expr_string_1 ; Using "sub_ebx,eax\nmov_eax,ebx\n"
CALL32 %emit_out ; Emit it
JMP32 %primary_expr_done ; Be done
:primary_expr_not
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S
LOAD8_al_from_EAX ; global_token->S[0]
MOVZX_al ; Make it useful
CMPI8_EAX !33 ; IF global_token->S[0] == "!"
JNE32 %primary_expr_bin ; Otherwise try '~'
;; Deal with logical not
LOADI32_EAX &primary_expr_string_2 ; Using "mov_eax, %1\n"
CALL32 %emit_out ; Emit it
LOADI32_EAX &postfix_expr ; Passing postfix_expr
CALL32 %common_recursion ; Get what it is notting
LOADI32_EAX &primary_expr_string_3 ; Using "xor_eax,ebx\n"
CALL32 %emit_out ; Emit it
JMP32 %primary_expr_done ; Be done
:primary_expr_bin
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S
LOAD8_al_from_EAX ; global_token->S[0]
MOVZX_al ; Make it useful
CMPI8_EAX !126 ; IF global_token->S[0] == "~"
JNE32 %primary_expr_paren ; Otherwise try paren
;; Deal with binary NOT
LOADI32_EAX &postfix_expr ; Passing postfix_expr
CALL32 %common_recursion ; Get what it is notting
LOADI32_EAX &primary_expr_string_4 ; Using "not_eax\n"
CALL32 %emit_out ; Emit it
JMP32 %primary_expr_done ; Be done
:primary_expr_paren
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S
LOAD8_al_from_EAX ; global_token->S[0]
MOVZX_al ; Make it useful
CMPI8_EAX !40 ; IF global_token->S[0] == "("
JNE32 %primary_expr_ch ; Otherwise try char
;; deal with nesting
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->NEXT
STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT
CALL32 %expression ; Lets recurse
LOADI32_EAX &primary_expr_string_5 ; Using "Error in Primary expression\nDidn't get )\n"
LOADI32_EBX &close_paren ; Using ")"
CALL32 %require_match ; Make sure we have it
JMP32 %primary_expr_done ; Be done
:primary_expr_ch
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S
LOAD8_al_from_EAX ; global_token->S[0]
MOVZX_al ; Make it useful
CMPI8_EAX !39 ; Using "'"
JNE32 %primary_expr_str ; Otherwise try string
;; Deal with chars
CALL32 %primary_expr_char ; Handle that char
JMP32 %primary_expr_done ; Be done
:primary_expr_str
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S
LOAD8_al_from_EAX ; global_token->S[0]
MOVZX_al ; Make it useful
CMPI8_EAX !34 ; Using '\"'
JNE32 %primary_expr_var ; Otherwise try a variable
;; Deal with strings
CALL32 %primary_expr_string ; Handle that string
JMP32 %primary_expr_done ; Be done
:primary_expr_var
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S
LOAD8_al_from_EAX ; global_token->S[0]
MOVZX_al ; Make it useful
LOADI32_EBX &primary_expr_string_6 ; Using "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"
CALL32 %In_Set ; See if we have a match
CMPI8_EAX !1 ; IF match
JNE32 %primary_expr_num ; otherwise try number
;; Deal with variables
CALL32 %primary_expr_variable ; Deal with variable
JMP32 %primary_expr_done ; Be done
:primary_expr_num
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S
LOAD8_al_from_EAX ; global_token->S[0]
MOVZX_al ; Make it useful
LOADI32_EBX &primary_expr_string_7 ; Using "0123456789"
CALL32 %In_Set ; See if we have a match
CMPI8_EAX !1 ; IF match
JNE32 %primary_expr_fail ; otherwise we failed hard
;; Deal with numbers
CALL32 %primary_expr_number ; Collect the number
JMP32 %primary_expr_done ; Be done
:primary_expr_fail
;; looks like we hit bad input
;; abort before it gets bad
CALL32 %primary_expr_failure ; No match means failure
:primary_expr_done
POP_EBX ; Restore EBX
RETURN
:primary_expr_string_0 "mov_eax, %0
"
:primary_expr_string_1 "sub_ebx,eax
mov_eax,ebx
"
:primary_expr_string_2 "mov_eax, %1
"
:primary_expr_string_3 "xor_eax,ebx
"
:primary_expr_string_4 "not_eax
"
:primary_expr_string_5 "Error in Primary expression
Didn't get )
"
:primary_expr_string_6 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"
:primary_expr_string_7 "0123456789"
;; primary_expr_variable function
;; Receives nothing
;; Returns nothing
;; Walks global and updates output
;; Uses EAX for struct token_list* a and ECX for char* S
:primary_expr_variable
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_ECX_from_EAX_Immediate8 !8 ; S = global_token->S
LOAD32_EAX_from_EAX ; global_token->NEXT
STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT
COPY_ECX_to_EAX ; Using S
LOAD32_Absolute32_ebx &global_constant_list ; Using global_constant_list
CALL32 %sym_lookup ; sym_lookup(s, global_constant_list)
CMPI8_EAX !0 ; IF NULL == sym_lookup(s, global_constant_list)
JE32 %primary_expr_variable_local ; Try locals next
;; Deal with constant load
LOAD32_EBX_from_EAX_Immediate8 !16 ; a->ARGS
LOADI32_EAX &primary_expr_variable_string_2 ; Using "mov_eax, %"
CALL32 %emit_out ; Emit it
LOAD32_EAX_from_EBX_Immediate8 !8 ; a->ARGS->S
CALL32 %emit_out ; Emit it
LOADI32_EAX &primary_expr_variable_string_1 ; Using "\n"
CALL32 %emit_out ; Emit it
JMP32 %primary_expr_variable_done ; Be done
:primary_expr_variable_local
COPY_ECX_to_EAX ; Using S
LOAD32_Absolute32_ebx &function ; Using function
LOAD32_EBX_from_EBX_Immediate8 !4 ; function->locals
CALL32 %sym_lookup ; sym_lookup(s, function->locals)
CMPI8_EAX !0 ; IF NULL == sym_lookup(s, function->locals)
JE32 %primary_expr_variable_arguments ; try arguments next
;; Deal with local load
CALL32 %variable_load ; Collect it
JMP32 %primary_expr_variable_done ; Be done
:primary_expr_variable_arguments
COPY_ECX_to_EAX ; Using S
LOAD32_Absolute32_ebx &function ; Using function
LOAD32_EBX_from_EBX_Immediate8 !16 ; function->args
CALL32 %sym_lookup ; sym_lookup(s, function->args)
CMPI8_EAX !0 ; IF NULL == sym_lookup(s, global_constant_list)
JE32 %primary_expr_variable_function ; try functions next
;; Deal with argument load
CALL32 %variable_load ; Collect it
JMP32 %primary_expr_variable_done ; Be done
:primary_expr_variable_function
COPY_ECX_to_EAX ; Using S
LOAD32_Absolute32_ebx &global_function_list ; Using global_function_list
CALL32 %sym_lookup ; sym_lookup(s, global_function_list)
CMPI8_EAX !0 ; IF NULL == sym_lookup(s, global_function_list)
JE32 %primary_expr_variable_global ; try globals next
;; Deal with functions
CALL32 %function_load ; Deal with the function
JMP32 %primary_expr_variable_done ; Be done
:primary_expr_variable_global
COPY_ECX_to_EAX ; Using S
LOAD32_Absolute32_ebx &global_symbol_list ; Using global_symbol_list
CALL32 %sym_lookup ; sym_lookup(s, global_symbol_list)
CMPI8_EAX !0 ; IF NULL == sym_lookup(s, global_symbol_list)
JE32 %primary_expr_variable_error ; Give up
;; Deal with globals
CALL32 %global_load ; Collect that global
JMP32 %primary_expr_variable_done ; Be done
:primary_expr_variable_error
LOADI32_EAX %2 ; Using standard error
STORE32_Absolute32_eax &Output_file ; write to standard error
; CALL32 %line_error ; Write useful debug info
COPY_ECX_to_EAX ; put S in the right place
CALL32 %File_Print ; print it
LOADI32_EAX &primary_expr_variable_string_0 ; Ending string
CALL32 %File_Print ; print it
JMP32 %Exit_Failure ; Abort Hard
:primary_expr_variable_done
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:primary_expr_variable_string_0 " is not a defined symbol
"
:primary_expr_variable_string_1 "
"
:primary_expr_variable_string_2 "mov_eax, %"
;; function_call function
;; Receives char* S in EAX and int BOOL in EBX
;; Builds stack frames before and tears them down after function calls
;; Uses ECX for char* S, EDX for int BOOL, ESI for PASSED
:function_call
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
PUSH_EDX ; Protect EDX
PUSH_ESI ; Protect ESI
COPY_EAX_to_ECX ; Put S in place
COPY_EBX_to_EDX ; Put BOOL in place
LOADI32_ESI %0 ; PASSED = 0
LOADI32_EAX &function_call_string_0 ; Using "ERROR in process_expression_list\nNo ( was found\n"
LOADI32_EBX &open_paren ; Using "("
CALL32 %require_match ; Make sure we have it
LOADI32_EAX &function_call_string_1 ; Using "push_edi\t# Prevent overwriting in recursion\n"
CALL32 %emit_out ; Emit it
LOADI32_EAX &function_call_string_2 ; Using "push_ebp\t# Protect the old base pointer\n"
CALL32 %emit_out ; Emit it
LOADI32_EAX &function_call_string_3 ; Using "mov_edi,esp\t# Copy new base pointer\n"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S
LOAD8_al_from_EAX ; global_token->S[0]
MOVZX_al ; Make it useful
CMPI8_EAX !41 ; IF global_token->S[0] == ")"
JE32 %function_call_gen_done ; Then no arguments to send
;; looks like we have arguments to collect
CALL32 %expression ; Collect the argument
LOADI32_EAX &function_call_string_4 ; Using "push_eax\t#_process_expression1\n"
CALL32 %emit_out ; Emit it
LOADI32_ESI %1 ; PASSED = 1
:function_call_gen_iter
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S
LOAD8_al_from_EAX ; global_token->S[0]
MOVZX_al ; Make it useful
CMPI8_EAX !44 ; IF global_token->S[0] == ","
JNE32 %function_call_gen_done ; Otherwise we are done
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->NEXT
STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT
CALL32 %expression ; Collect the argument
LOADI32_EAX &function_call_string_5 ; Using "push_eax\t#_process_expression2\n"
CALL32 %emit_out ; Emit it
ADDI8_ESI !1 ; PASSED = PASSED + 1
JMP32 %function_call_gen_iter ; Keep trying
:function_call_gen_done
;; All is collected
LOADI32_EAX &function_call_string_6 ; Using "ERROR in process_expression_list\nNo ) was found\n"
LOADI32_EBX &close_paren ; Using ")"
CALL32 %require_match ; Make sure we have it
CMPI8_EDX !0 ; IF(BOOL == TRUE)
JNE32 %function_call_static ; Otherwise it is a static call
;; Deal with a passed function pointer
LOADI32_EAX &function_call_string_7 ; Using "lea_eax,[ebp+DWORD] %"
CALL32 %emit_out ; Emit it
COPY_ECX_to_EAX ; Using S
CALL32 %emit_out ; Emit it
LOADI32_EAX &function_call_string_8 ; Using "\nmov_eax,[eax]\n"
CALL32 %emit_out ; Emit it
LOADI32_EAX &function_call_string_9 ; Using "mov_ebp,edi\n"
CALL32 %emit_out ; Emit it
LOADI32_EAX &function_call_string_10 ; Using "call_eax\n"
CALL32 %emit_out ; Emit it
LOADI32_EAX &function_call_string_13 ; Using "pop_ebx\t# _process_expression_locals\n"
JMP32 %function_call_cleanup ; Clean up
:function_call_static
;; Deal with fixed function name
LOADI32_EAX &function_call_string_9 ; Using "mov_ebp,edi\n"
CALL32 %emit_out ; Emit it
LOADI32_EAX &function_call_string_11 ; Using "call %FUNCTION_"
CALL32 %emit_out ; Emit it
COPY_ECX_to_EAX ; Using S
CALL32 %emit_out ; Emit it
LOADI32_EAX &function_call_string_12 ; Using "\n"
CALL32 %emit_out ; Emit it
LOADI32_EAX &function_call_string_13 ; Using "pop_ebx\t# _process_expression_locals\n"
:function_call_cleanup
CMPI8_ESI !0 ; IF PASSED > 0
JLE32 %function_call_done ; Otherwise be done
;; The desired string is already in EAX
CALL32 %emit_out ; Emit it
SUBI8_ESI !1 ; PASSED = PASSED - 1
JMP32 %function_call_cleanup ; Keep going
:function_call_done
LOADI32_EAX &function_call_string_14 ; Using "pop_ebp\t# Restore old base pointer\n"
CALL32 %emit_out ; Emit it
LOADI32_EAX &function_call_string_15 ; Using "pop_edi\t# Prevent overwrite\n"
CALL32 %emit_out ; Emit it
POP_ESI ; Restore ESI
POP_EDX ; Restore EDX
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:function_call_string_0 "ERROR in process_expression_list
No ( was found
"
:function_call_string_1 "push_edi # Prevent overwriting in recursion
"
:function_call_string_2 "push_ebp # Protect the old base pointer
"
:function_call_string_3 "mov_edi,esp # Copy new base pointer
"
:function_call_string_4 "push_eax #_process_expression1
"
:function_call_string_5 "push_eax #_process_expression2
"
:function_call_string_6 "ERROR in process_expression_list
No ) was found
"
:function_call_string_7 "lea_eax,[ebp+DWORD] %"
:function_call_string_8 "
mov_eax,[eax]
"
:function_call_string_9 "mov_ebp,edi
"
:function_call_string_10 "call_eax
"
:function_call_string_11 "call %FUNCTION_"
:function_call_string_12 "
"
:function_call_string_13 "pop_ebx # _process_expression_locals
"
:function_call_string_14 "pop_ebp # Restore old base pointer
"
:function_call_string_15 "pop_edi # Prevent overwrite
"
;; variable_load function
;; Receives struct token_list* A in EAX
;; Returns nothing
;; Updates output and current_target
;; Uses ECX for A
:variable_load
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
COPY_EAX_to_ECX ; Protect A
LOAD32_Absolute32_ebx &global_token ; Using global_token
LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S
LOADI32_EAX &open_paren ; Using "("
CALL32 %match ; IF global_token->S == "("
CMPI8_EAX !0 ; Then it might be a function
JNE32 %variable_load_regular ; Otherwise it is regular
LOAD32_EBX_from_ECX_Immediate8 !12 ; A->TYPE
LOAD32_EBX_from_EBX_Immediate8 !24 ; A->TYPE->NAME
LOADI32_EAX &type_function_name ; Using "FUNCTION"
CALL32 %match ; IF A->TYPE->NAME == "FUNCTION"
CMPI8_EAX !0 ; Then it must be a function
JNE32 %variable_load_regular ; otherwise just another regular
;; deal with function
LOAD32_EAX_from_ECX_Immediate8 !16 ; A->DEPTH
CALL32 %numerate_number ; Convert to string
LOADI32_EBX %0 ; pass 0 for true
CALL32 %function_call ; Create the function call
JMP32 %variable_load_done ; Be done
:variable_load_regular
LOAD32_EAX_from_ECX_Immediate8 !12 ; A->TYPE
STORE32_Absolute32_eax ¤t_target ; current_target = A->TYPE
LOADI32_EAX &variable_load_string_0 ; Using "lea_eax,[ebp+DWORD] %"
CALL32 %emit_out ; Emit it
LOAD32_EAX_from_ECX_Immediate8 !16 ; A->DEPTH
CALL32 %numerate_number ; Convert to string
CALL32 %emit_out ; Emit it
LOADI32_EAX &variable_load_string_1 ; Using "\n"
CALL32 %emit_out ; Emit it
;; Check for special case of assignment
LOAD32_Absolute32_ebx &global_token ; Using global_token
LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S
LOADI32_EAX &equal ; Using "="
CALL32 %match ; IF global_token->S == "="
CMPI8_EAX !0 ; Then we skip loading
JE32 %variable_load_done ; And be done
;; Deal with common case
LOADI32_EAX &variable_load_string_2 ; Using "mov_eax,[eax]\n"
CALL32 %emit_out ; Emit it
:variable_load_done
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:variable_load_string_0 "lea_eax,[ebp+DWORD] %"
:variable_load_string_1 "
"
:variable_load_string_2 "mov_eax,[eax]
"
;; function_load function
;; Receives struct token_list* a in EAX
;; Returns nothing
;; Uses ECX to hold A->S
:function_load
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
LOAD32_EAX_from_EAX_Immediate8 !8 ; A->S
COPY_EAX_to_ECX ; Protect A->S
LOAD32_Absolute32_ebx &global_token ; Using global_token
LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S
LOADI32_EAX &open_paren ; Using "("
CALL32 %match ; IF global_token->S == "("
CMPI8_EAX !0 ; The we need to do a function call
JNE32 %function_load_regular ; Otherwise just load it's address
;; Deal with function call
COPY_ECX_to_EAX ; Using A->S
LOADI32_EBX %1 ; Using FALSE
CALL32 %function_call ; Deal with it
JMP32 %function_load_done ; Be done
:function_load_regular
LOADI32_EAX &function_load_string_0 ; Using "mov_eax, &FUNCTION_"
CALL32 %emit_out ; Emit it
COPY_ECX_to_EAX ; Using A->S
CALL32 %emit_out ; Emit it
LOADI32_EAX &function_load_string_1 ; Using "\n"
CALL32 %emit_out ; Emit it
:function_load_done
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:function_load_string_0 "mov_eax, &FUNCTION_"
:function_load_string_1 "
"
;; global_load function
;; Receives struct token_list* A in EAX
;; Returns nothing
;; Uses EBX to hold A->S
:global_load
PUSH_EBX ; Protect EBX
COPY_EAX_to_EBX ; Set as A
LOAD32_EBX_from_EBX_Immediate8 !8 ; Set as A->S
LOAD32_EAX_from_EAX_Immediate8 !12 ; A->TYPE
STORE32_Absolute32_eax ¤t_target ; current_target = A->TYPE
LOADI32_EAX &global_load_string_0 ; Using "mov_eax, &GLOBAL_"
CALL32 %emit_out ; Emit it
COPY_EBX_to_EAX ; Using A->S
CALL32 %emit_out ; Emit it
LOADI32_EAX &global_load_string_1 ; Using "\n"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_ebx &global_token ; Using global_token
LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S
LOADI32_EAX &equal ; "="
CALL32 %match ; IF global_token->S == "="
CMPI8_EAX !0 ; We need to skip for assignment
JE32 %global_load_done ; and be done
;; Otherwise we are loading the contents
LOADI32_EAX &global_load_string_2 ; Using "mov_eax,[eax]\n"
CALL32 %emit_out ; Emit it
:global_load_done
POP_EBX ; Restore EBX
RETURN
:global_load_string_0 "mov_eax, &GLOBAL_"
:global_load_string_1 "
"
:global_load_string_2 "mov_eax,[eax]
"
;; sym_lookup function
;; Receives char* S in EAX and struct token_list* symbol_list in EBX
;; Uses I->S in EAX, S in EBX and I in ECX
;; Returns match or NULL
:sym_lookup
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
COPY_EBX_to_ECX ; I = symbol_list
COPY_EAX_to_EBX ; Put S in the right place
:sym_lookup_iter
CMPI8_ECX !0 ; IF NULL == I
JE32 %sym_lookup_done ; We failed to find match
LOAD32_EAX_from_ECX_Immediate8 !8 ; Using I->S
CALL32 %match ; IF I->S == S
CMPI8_EAX !0 ; then be done
JE32 %sym_lookup_done ; Failed
LOAD32_ECX_from_ECX ; I = I->NEXT
JMP32 %sym_lookup_iter ; otherwise keep looping
:sym_lookup_done
COPY_ECX_to_EAX ; Return I
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
;; primary_expr_number function
;; Receives nothing
;; Returns nothing
;; Simply uses current global token to update output and then steps to next global_token
:primary_expr_number
LOADI32_EAX &primary_expr_number_string_0 ; Using "mov_eax, %"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S
CALL32 %emit_out ; Emit it
LOADI32_EAX &primary_expr_number_string_1 ; Using "\n"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->NEXT
STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT
RETURN
:primary_expr_number_string_0 "mov_eax, %"
:primary_expr_number_string_1 "
"
;; primary_expr_string function
;; receives nothing
;; Returns nothing
;; creates entries for string and calls to generate string output
;; uses ECX for char* number_string
:primary_expr_string
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
LOAD32_Absolute32_ebx ¤t_count ; Using current_count
COPY_EBX_to_EAX ; And putting it in the right place
CALL32 %numerate_number ; Get the string
COPY_EAX_to_ECX ; protect number_string
ADDI8_EBX !1 ; current_count + 1
STORE32_Absolute32_ebx ¤t_count ; current_count = current_count + 1
LOADI32_EAX &primary_expr_string_string_0 ; Using "mov_eax, &STRING_"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S
COPY_ECX_to_EBX ; Put number_string in the right place
CALL32 %uniqueID_out ; Make it unique
;; Generate the target
LOADI32_EAX &primary_expr_string_string_1 ; Using ":STRING_"
LOAD32_Absolute32_ebx &strings_list ; Using strings_list
CALL32 %emit ; Emit it
COPY_EAX_to_EBX ; put new strings_list in place
LOAD32_Absolute32_eax &function ; Using function
LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S
CALL32 %uniqueID ; Make it unique
COPY_EAX_to_EBX ; put new strings_list in place
;; Parse the string
LOAD32_Absolute32_eax &global_token ; Using global token
LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S
CALL32 %parse_string ; convert to useful form
CALL32 %emit ; Emit it
STORE32_Absolute32_eax &strings_list ; Update Strings _list
LOAD32_Absolute32_eax &global_token ; Using global token
LOAD32_EAX_from_EAX ; global_token->NEXT
STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:primary_expr_string_string_0 "mov_eax, &STRING_"
:primary_expr_string_string_1 ":STRING_"
;; primary_expr_char function
;; Receives nothing
;; Returns nothing
;; Updates output_list using global_token
:primary_expr_char
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
LOADI32_EAX &primary_expr_char_string_0 ; Using "mov_eax, %"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &global_token ; Using global token
LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S
ADDI8_EAX !1 ; global_token->S + 1
CALL32 %escape_lookup ; Get the char
CALL32 %numerate_number ; Convert to string
CALL32 %emit_out ; emit it
LOADI32_EAX &primary_expr_char_string_1 ; Using "\n"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &global_token ; Using global token
LOAD32_EAX_from_EAX ; global_token->NEXT
STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:primary_expr_char_string_0 "mov_eax, %"
:primary_expr_char_string_1 "
"
;; primary_expr_failure function
;; Receives nothing
;; Does not return but aborts hard
;; Complains about the bad input
:primary_expr_failure
; CALL32 %line_error ; Get line of issue
LOADI32_EAX %2 ; Using Standard error
STORE32_Absolute32_eax &Output_file ; write to standard error
LOADI32_EAX &primary_expr_failure_string_0 ; Using "Received "
CALL32 %File_Print ; Print it
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S
CALL32 %File_Print ; Print it
LOADI32_EAX &primary_expr_failure_string_1 ; Using " in primary_expr\n"
CALL32 %File_Print ; Print it
JMP32 %Exit_Failure ; Abort Hard
:primary_expr_failure_string_0 "Received "
:primary_expr_failure_string_1 " in primary_expr
"
;; general_recursion function
;; Receives FUNCTION F in EAX, char* S in EBX, char* name in ECX and FUNCTION iterate in EDX
;; Returns nothing
;; Uses ECX for char* S, EDX for FUNCTION iterate and EBP for FUNCTION F
;; But generally recurses a shitload
:general_recursion
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
PUSH_EDX ; Protect EDX
PUSH_EBP ; Protect EBP
COPY_EAX_to_EBP ; Protect F
COPY_ECX_to_EAX ; Put name in the right place
COPY_EBX_to_ECX ; Protect S
LOAD32_Absolute32_ebx &global_token ; Using global_token
LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S
CALL32 %match ; IF match(name, global_token->s)
CMPI8_EAX !0 ; If true we do
JNE32 %general_recursion_done ; Otherwise skip it
;; Deal with the recursion
COPY_EBP_to_EAX ; Put F in the right place
CALL32 %common_recursion ; Recurse
COPY_ECX_to_EAX ; Put S in the right place
CALL32 %emit_out ; Emit it
COPY_EDX_to_EAX ; Put iterate in the right place
CALL_EAX ; Down the rabbit hole
:general_recursion_done
POP_EBP ; Restore EBP
POP_EDX ; Restore EDX
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
;; promote_type function
;; Receives struct type* a in EAX and struct type* b in EBX
;; Returns the most recent type in EAX
;; Uses EAX for struct type* I, ECX for struct type* A and EDX for struct type* B
:promote_type
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
PUSH_EDX ; Protect EDX
CMPI8_EBX !0 ; IF NULL == B
JE32 %promote_type_done ; Just return A
COPY_EAX_to_ECX ; Put A in place
COPY_EBX_to_EDX ; Put B in place
COPY_EBX_to_EAX ; IF NULL == A
CMPI8_ECX !0 ; Then we just return B
JE32 %promote_type_done ; Be done
;; Looks like we need to walk the list
LOAD32_ECX_from_ECX_Immediate8 !24 ; A->NAME
LOAD32_EDX_from_EDX_Immediate8 !24 ; B->NAME
LOAD32_Absolute32_eax &global_types ; I = global_types
:promote_type_iter
CMPI8_EAX !0 ; IF NULL == I
JE32 %promote_type_done ; Just be done
LOAD32_EBX_from_EAX_Immediate8 !24 ; I->NAME
CMP_ECX_EBX ; IF(A->NAME == I->NAME)
JE32 %promote_type_done ; Be done
CMP_EBX_EDX ; IF(B->NAME == I->NAME)
JE32 %promote_type_done ; Be done
LOAD32_EBX_from_EAX_Immediate8 !12 ; I->INDIRECT
LOAD32_EBX_from_EBX_Immediate8 !24 ; I->INDIRECT->NAME
CMP_ECX_EBX ; IF(A->NAME == I->INDIRECT->NAME)
JE32 %promote_type_done ; Be done
CMP_EBX_EDX ; IF(B->NAME == I->INDIRECT->NAME)
JE32 %promote_type_done ; Be done
LOAD32_EAX_from_EAX ; I = I->NEXT
JMP32 %promote_type_iter ; Keep going
:promote_type_done
POP_EDX ; Restore EDX
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
;; common_recursion function
;; Receives FUNCTION F in EAX
;; Returns Nothing
;; Walks global_token list and update output_list
;; Updates current_target
;; Uses EBX to hold FUNCTION F and struct type* last_type
:common_recursion
PUSH_EBX ; Protect EBX
COPY_EAX_to_EBX ; Put FUNCTION F safely out of the way
LOADI32_EAX &common_recursion_string_0 ; Using "push_eax\t#_common_recursion\n"
CALL32 %emit_out ; Emit it
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->NEXT
STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT
COPY_EBX_to_EAX ; Prepare for function call
LOAD32_Absolute32_ebx ¤t_target ; Get last type
CALL_EAX ; F();
LOAD32_Absolute32_eax ¤t_target ; Get current_target
CALL32 %promote_type ; get the right type
STORE32_Absolute32_eax ¤t_target ; Set new current_target
LOADI32_EAX &common_recursion_string_1 ; Using "pop_ebx\t# _common_recursion\n"
CALL32 %emit_out ; Emit it
POP_EBX ; Restore EBX
RETURN
:common_recursion_string_0 "push_eax #_common_recursion
"
:common_recursion_string_1 "pop_ebx # _common_recursion
"
;; require_match function
;; Receives char* message in EAX and char* required in EBX
;; Returns nothing
;; Uses ECX to hold message and updates global_token
:require_match
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
COPY_EAX_to_ECX ; put the message somewhere safe
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S
CALL32 %match ; IF required == global_token->S
CMPI8_EAX !0 ; we are fine
JE32 %require_match_good ; otherwise pain
;; Deal will bad times
; CALL32 %line_error ; Tell user what went wrong
LOADI32_EAX %2 ; Using standard error
STORE32_Absolute32_eax &Output_file ; write to standard error
COPY_ECX_to_EAX ; using our message
CALL32 %File_Print ; Print it
JMP32 %Exit_Failure ; Abort HARD
:require_match_good
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->next
STORE32_Absolute32_eax &global_token ; global_token = global_token->next
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
;; uniqueID Function
;; Receives char *S in EAX, struct token_list* l in EBX and char* num in ECX
;; Returns updated struct token_list* L in EAX
:uniqueID
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
CALL32 %emit ; emit(s, l)
COPY_EAX_to_EBX ; Put L in correct place
LOADI32_EAX &underline ; Using "_"
CALL32 %emit ; emit("_", l)
COPY_EAX_to_EBX ; Put L in correct place
COPY_ECX_to_EAX ; Put num in correct place
CALL32 %emit ; emit(num, l)
COPY_EAX_to_EBX ; Put L in correct place
LOADI32_EAX &uniqueID_string_0 ; Using "\n"
CALL32 %emit ; emit("\n", l)
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:uniqueID_string_0 "
"
;; uniqueID_out function
;; Receives char* S in EAX and char* num in EBX
;; Returns nothing
:uniqueID_out
PUSH_EAX ; Protect EAX
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
COPY_EBX_to_ECX ; Put num in right spot
LOAD32_Absolute32_ebx &output_list ; Using output_list
CALL32 %uniqueID ; Get updated list
STORE32_Absolute32_eax &output_list ; output_list = uniqueID(s, output_list, num)
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
POP_EAX ; Restore EAX
RETURN
;; emit_out function
;; Receives char* S in EAX
;; Returns nothing
;; Updates output_list
;; MUST NOT ALTER REGISTERS
:emit_out
PUSH_EAX ; Protect EAX
PUSH_EBX ; Protect EBX
LOAD32_Absolute32_ebx &output_list ; Using output_list
CALL32 %emit ; emit it
STORE32_Absolute32_eax &output_list ; update it
POP_EBX ; Restore EBX
POP_EAX ; Restore EAX
RETURN
;; emit function
;; Receives char *s in EAX and struct token_list* head in EBX
;; Returns struct token_list* T in EAX
:emit
PUSH_ECX ; Protect ECX
COPY_EAX_to_ECX ; put S out of the way
LOADI32_EAX %20 ; sizeof(struct token_list)
CALL32 %malloc ; get T
STORE32_EBX_into_EAX ; t->next = head;
STORE32_ECX_into_EAX_Immediate8 !8 ; t->s = s;
POP_ECX ; Restore ECX
RETURN
;; escape_lookup function
;; Receives char* c in EAX
;; Returns integer value of char in EAX
;; Aborts hard if unknown escape is received
;; Uses ECX to hold char* C
:escape_lookup
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
COPY_EAX_to_ECX ; Put char* C in safe place
LOAD8_al_from_ECX ; Load c[0]
MOVZX_al ; make it useful
CMPI8_EAX !92 ; If '\\' != c[0]
JNE32 %escape_lookup_done ; Be done
COPY_ECX_to_EBX ; Prepare for walk
ADDI8_EBX !1 ; increment
LOAD8_bl_from_EBX ; load c[1]
MOVZX_bl ; make it useful
CMPI8_EBX !120 ; Check if \x??
JE32 %escape_lookup_hex ; Deal with hex
;; Deal with \? escapes
LOADI32_EAX %10 ; Guess "\n"
CMPI8_EBX !110 ; If n
JE32 %escape_lookup_done ; Be done
LOADI32_EAX %9 ; Guess "\t"
CMPI8_EBX !116 ; If t
JE32 %escape_lookup_done ; Be done
COPY_EBX_to_EAX ; "\\", "'" and '\"' all encode as themselves
CMPI8_EBX !92 ; If "\\"
JE32 %escape_lookup_done ; Be done
CMPI8_EBX !39 ; IF "'"
JE32 %escape_lookup_done ; Be done
CMPI8_EBX !34 ; IF '\"'
JE32 %escape_lookup_done ; Be done
LOADI32_EAX %13 ; Guess "\r"
CMPI8_EBX !114 ; IF r
JE32 %escape_lookup_done ; Be done
;; Looks like we have no clue what we are doing
;; Aborting hard
LOADI32_EAX %2 ; Using Standard error
STORE32_Absolute32_eax &Output_file ; write to standard error
LOADI32_EAX &escape_lookup_string_0 ; Using "Unknown escape received: "
CALL32 %File_Print ; Print it
COPY_ECX_to_EAX ; Using C
CALL32 %File_Print ; Print it
LOADI32_EAX &escape_lookup_string_1 ; Using " Unable to process\n"
CALL32 %File_Print ; Print it
JMP32 %Exit_Failure ; Abort Hard
:escape_lookup_done
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:escape_lookup_hex
;; Give up on C and just assume they know what they are doing
ADDI8_ECX !2 ; increment
LOAD8_al_from_ECX ; c[2]
MOVZX_al ; make it useful
ADDI8_ECX !1 ; increment
CALL32 %char2hex ; Get the hex value
SALI8_EAX !4 ; c << 4
LOAD8_bl_from_ECX ; c[3]
MOVZX_bl ; make it useful
SWAP_EAX_EBX ; protect c << 4
CALL32 %char2hex ; Get the hex value
ADD_ebx_into_eax ; hex(c[2]) << 4 + hex(c[3])
JMP32 %escape_lookup_done ; Be done
:escape_lookup_string_0 "Unknown escape received: "
:escape_lookup_string_1 " Unable to process
"
;; char2hex function
;; Receives char in EAX
;; Returns hex or aborts hard
:char2hex
SUBI8_EAX !48 ; Try 0-9
CMPI8_EAX !10 ; Otherwise fun times
JL32 %char2hex_done ; Be done
;; Deal with A-F
ANDI32_EAX %0xDF ; Unset High bit turning a-f into A-F
SUBI8_EAX !7 ; Shift down into position
CMPI8_EAX !10 ; Everything below A is bad
JL32 %char2hex_fail ; And should fail
CMPI8_EAX !16 ; Make sure we are below F
JL32 %char2hex_done ; If so be done
:char2hex_fail
;; Time to fail hard
LOADI32_EAX %2 ; Using Standard error
STORE32_Absolute32_eax &Output_file ; write to standard error
LOADI32_EAX &char2hex_string_0 ; Using "Tried to print non-hex number\n"
CALL32 %File_Print ; Print it
JMP32 %Exit_Failure ; Abort Hard
:char2hex_done
RETURN
:char2hex_string_0 "Tried to print non-hex number
"
;; parse_string function
;; Receives char* string in EAX
;; Returns cleaned up string
;; Protects char* string in EBX
:parse_string
PUSH_EBX ; Protect EBX
COPY_EAX_to_EBX ; Protect char* string
CALL32 %weird ; Determine if we have a weird string
CMPI8_EAX !0 ; If weird
JE32 %parse_string_weird ; Deal with it
;; Dealing with regular string
COPY_EBX_to_EAX ; Passing Char* string
CALL32 %collect_regular_string ; Collect it
JMP32 %parse_string_done ; Be done
:parse_string_weird
COPY_EBX_to_EAX ; Passing Char* string
CALL32 %collect_weird_string ; Collect it
:parse_string_done
POP_EBX ; Restore EBX
RETURN
;; weird function
;; Receives char* string in EAX
;; Returns true(0) or false(1) in EAX
;; Uses ECX to hold char* string
:weird
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
COPY_EAX_to_ECX ; Place string in safe place
ADDI8_ECX !1 ; increment past the '\"'
:weird_reset
LOAD8_al_from_ECX ; Load a char
MOVZX_al ; Make it useful
CMPI8_EAX !0 ; IF NULL == C
JE32 %weird_false ; Nothing weird found
CMPI8_EAX !92 ; IF '\\'
JNE32 %weird_escaped ; Deal with escaping
;; Deal with escape
COPY_ECX_to_EAX ; We are passing the string
CALL32 %escape_lookup ; to look it up
ADDI8_ECX !1 ; string = string + 1
LOAD8_bl_from_ECX ; get string[1]
MOVZX_bl ; make it useful
CMPI8_EBX !120 ; IF 'x' == string[1]
JNE32 %weird_escaped ; otherwise skip the gap
ADDI8_ECX !2 ; string = string + 2
:weird_escaped
PUSH_EAX ; Protect C in case we need it
LOADI32_EBX &weird_string_0 ; Use "\t\n !#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
CALL32 %In_Set ; To find if weird
CMPI8_EAX !1 ; IF TRUE
POP_EAX ; Restore C
JNE32 %weird_true ; Then not weird
ADDI8_ECX !1 ; string = string + 1
;; Last chance for weird
LOADI32_EBX &weird_string_1 ; Use "\t\n\r "
CALL32 %In_Set ; Check for special case
CMPI8_EAX !1 ; IF TRUE
JNE32 %weird_reset ; Otherwise not in the special case
;; Deal with possible special case
LOAD8_al_from_ECX ; Load string[1]
MOVZX_al ; Make it useful
CMPI8_EAX !58 ; IF string[1] == ":"
JE32 %weird_true ; Then we hit the special case
JMP32 %weird_reset ; Keep trying
:weird_done
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:weird_true
LOADI32_EAX %0 ; Return true
JMP32 %weird_done ; Be done
:weird_false
LOADI32_EAX %1 ; Return false
JMP32 %weird_done ; Be done
:weird_string_0 "
!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
:weird_string_1 '09 0A 0D 20' ; "\t\n\r "
;; collect_regular_string function
;; Receives char* string in EAX
;; Malloc and creates new string to return in EAX
;; Uses ECX for return string and EDX for passed string
:collect_regular_string
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
PUSH_EDX ; Protect EDX
COPY_EAX_to_EDX ; Protect our passed string
LOADI32_EAX %256 ; We need 256bytes of storage
CALL32 %malloc ; Get our new pointer
COPY_EAX_to_ECX ; put it in place
PUSH_EAX ; protect until done
:collect_regular_string_reset
LOAD8_al_from_EDX ; string[0]
MOVZX_al ; Make it useful
CMPI8_EAX !0 ; See if we hit the end
JE32 %collect_regular_string_done ; And be done
CMPI8_EAX !92 ; IF string[0] == '\\'
JE32 %collect_regular_string_escaped ; Deal with that mess
;; deal with boring char
STORE8_al_into_Address_ECX ; hold_string[index] = string[0]
ADDI8_ECX !1 ; Increment it
ADDI8_EDX !1 ; Increment it
JMP32 %collect_regular_string_reset ; And keep going
:collect_regular_string_escaped
COPY_EDX_to_EAX ; Using string
CALL32 %escape_lookup ; Get the char
STORE8_al_into_Address_ECX ; hold_string[index] = escape_lookup(string)
ADDI8_EDX !1 ; Increment it
ADDI8_ECX !1 ; Increment it
LOAD8_al_from_EDX ; string[0]
MOVZX_al ; Make it useful
ADDI8_EDX !1 ; Increment it
CMPI8_EAX !120 ; IF 'x' == string[1]
JNE32 %collect_regular_string_reset ; Otherwise keep going
ADDI8_EDX !2 ; Increment it
JMP32 %collect_regular_string_reset ; Keep going
:collect_regular_string_done
LOADI32_EAX %34 ; Using '\"'
STORE8_al_into_Address_ECX ; hold_string[index] = '\"'
ADDI8_ECX !1 ; Increment it
LOADI32_EAX %10 ; Using "\n"
STORE8_al_into_Address_ECX ; hold_string[index] = '\n'
POP_EAX ; Return our new string
POP_EDX ; Restore EDX
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
;; collect_weird_string function
;; Receives char* string in EAX
;; Mallocs and returns char* hold in EAX
;; Uses ECX for char* hold and EDX for char* string
:collect_weird_string
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
PUSH_EDX ; Protect EDX
COPY_EAX_to_EDX ; Protect our passed string
LOADI32_EAX %512 ; We need 512bytes of storage
CALL32 %malloc ; Get our new pointer
COPY_EAX_to_ECX ; put it in place
PUSH_EAX ; protect until done
LOADI32_EAX %39 ; Using "'"
STORE8_al_into_Address_ECX ; hold_string[index] = "'"
ADDI8_ECX !1 ; Increment it
ADDI8_EDX !1 ; Increment it
:collect_weird_string_reset
LOAD8_al_from_EDX ; Read a byte
MOVZX_al ; Make it useful
CMPI8_EAX !0 ; IF NULL == string[0]
JE32 %collect_weird_string_done ; Be done
LOADI32_EAX %32 ; Using ' '
STORE8_al_into_Address_ECX ; hold_string[index] = ' '
ADDI8_ECX !1 ; Increment it
COPY_EDX_to_EAX ; Using string
CALL32 %escape_lookup ; Get the char
CALL32 %hex8 ; Update ECX
LOAD8_al_from_EDX ; Read a byte
MOVZX_al ; Make it useful
ADDI8_EDX !1 ; Increment it
CMPI8_EAX !92 ; IF string[0] == '\\'
JNE32 %collect_weird_string_reset ; Otherwise keep going
LOAD8_al_from_EDX ; Read a byte
MOVZX_al ; Make it useful
ADDI8_EDX !1 ; Increment it
CMPI8_EAX !120 ; IF 'x' == string[1]
JNE32 %collect_weird_string_reset ; Otherwise keep going
ADDI8_EDX !2 ; Increment it
JMP32 %collect_weird_string_reset ; Keep going
:collect_weird_string_done
LOADI32_EAX %32 ; Using ' '
STORE8_al_into_Address_ECX ; hold_string[index] = ' '
ADDI8_ECX !1 ; Increment it
LOADI32_EAX %48 ; Using '0'
STORE8_al_into_Address_ECX ; hold_string[index] = '0'
ADDI8_ECX !1 ; Increment it
STORE8_al_into_Address_ECX ; hold_string[index] = '0'
ADDI8_ECX !1 ; Increment it
LOADI32_EAX %39 ; Using "'"
STORE8_al_into_Address_ECX ; hold_string[index] = "'"
ADDI8_ECX !1 ; Increment it
LOADI32_EAX %10 ; Using "\n"
STORE8_al_into_Address_ECX ; hold_string[index] = '\n'
POP_EAX ; Return our new string
POP_EDX ; Restore EDX
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
;; HEX to ascii routine
;; Receives INT in EAX and CHAR* in ECX
;; Stores ascii of INT in CHAR*
;; Returns only modifying EAX and ECX
:hex8
PUSH_EAX ; Protect bottom nibble
SHRI8_EAX !4 ; do high nibble first
CALL32 %hex4 ; Store it
POP_EAX ; do low nibble
:hex4
ANDI32_EAX %0xF ; isolate nibble
ADDI8_EAX !48 ; convert to ascii
CMPI8_EAX !57 ; valid digit?
JBE8 !hex1 ; yes
ADDI8_EAX !7 ; use alpha range
:hex1
STORE8_al_into_Address_ECX ; store result
ADDI8_ECX !1 ; next position
RETURN
;; type_name function
;; Receives nothing
;; Returns type_size in EAX
;; Uses ECX for STRUCT TYPE* RET
:type_name
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
LOAD32_Absolute32_ebx &global_token ; Using global_token
LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S
LOADI32_EAX &struct ; Using "struct"
CALL32 %match ; IF global_token->S == "struct"
COPY_EAX_to_ECX ; Protect structure
CMPI8_EAX !0 ; need to skip over "struct"
JNE32 %type_name_native ; otherwise keep going
;; Deal with possible STRUCTs
LOAD32_Absolute32_ebx &global_token ; Using global_token
LOAD32_EBX_from_EBX ; global_token->next
STORE32_Absolute32_ebx &global_token ; global_token = global_token->next
LOAD32_EAX_from_EBX_Immediate8 !8 ; global_token->S
LOAD32_Absolute32_ebx &global_types ; get all known types
CALL32 %lookup_type ; Find type if possible
COPY_EAX_to_ECX ; Set ret
CMPI8_EAX !0 ; IF NULL == ret
JNE32 %type_name_common ; We have to create struct
;; Create a struct
CALL32 %create_struct ; Create a new struct
LOADI32_ECX %0 ; We wish to return NULL
JMP32 %type_name_done ; be done
:type_name_native
;; Deal only with native types
COPY_EBX_to_EAX ; Put global_token->S in the right place
LOAD32_Absolute32_ebx &global_types ; get all known types
CALL32 %lookup_type ; Find the type if possible
COPY_EAX_to_ECX ; Set ret
CMPI8_EAX !0 ; IF NULL == ret
JNE32 %type_name_common ; We need to abort hard
;; Aborting hard
LOADI32_EAX %2 ; Using Standard error
STORE32_Absolute32_eax &Output_file ; write to standard error
LOADI32_EAX &type_name_string_0 ; Print header
CALL32 %File_Print ; Print it
LOAD32_Absolute32_eax &global_token ; Using global token
LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S
CALL32 %File_Print ; Print it
LOADI32_EAX &type_name_string_1 ; Print footer
CALL32 %File_Print ; Print it
; CALL32 %line_error ; Give details
JMP32 %Exit_Failure ; Abort
:type_name_common
LOAD32_Absolute32_ebx &global_token ; Using global_token
LOAD32_EBX_from_EBX ; global_token->next
STORE32_Absolute32_ebx &global_token ; global_token = global_token->next
:type_name_iter
LOAD32_EAX_from_EBX_Immediate8 !8 ; global_token->S
LOAD8_al_from_EAX ; global_token->S[0]
MOVZX_al ; make it useful
CMPI8_EAX !42 ; IF global_token->S[0] == '*'
JNE32 %type_name_done ; recurse
;; Deal with char**
LOAD32_ECX_from_ECX_Immediate8 !12 ; ret = ret->indirect
LOAD32_Absolute32_ebx &global_token ; Using global_token
LOAD32_EBX_from_EBX ; global_token->next
STORE32_Absolute32_ebx &global_token ; global_token = global_token->next
JMP32 %type_name_iter ; keep looping
:type_name_done
COPY_ECX_to_EAX ; put ret in the right place
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:type_name_string_0 "Unknown type "
:type_name_string_1 "
"
;; lookup_type function
;; Receives char* s in EAX and struct type* start in EBX
;; Returns struct type* in EAX
;; Uses EBX for S and ECX for I
:lookup_type
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
COPY_EBX_to_ECX ; I = Start
COPY_EAX_to_EBX ; Put S in place
:lookup_type_iter
CMPI8_ECX !0 ; Check if I == NULL
JE32 %lookup_type_done ; return NULL
LOAD32_EAX_from_ECX_Immediate8 !24 ; I->NAME
CALL32 %match ; Check if matching
CMPI8_EAX !0 ; IF I->NAME == S
JE32 %lookup_type_done ; return it
LOAD32_ECX_from_ECX ; Otherwise I = I->NEXT
JMP32 %lookup_type_iter ; And keep looping
:lookup_type_done
COPY_ECX_to_EAX ; return either I or NULL
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
;; create_struct function
;; Receives nothing
;; Returns nothing
;; Uses global_token to malloc a struct's definition
;; Uses ECX for int OFFSET, EDX for struct type* head, EBP for struct type* I,
;; EDI for member_size (Which is passed) and ESI for LAST
;; EAX and EBX are used for scratch
:create_struct
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
PUSH_EDX ; Protect EDX
PUSH_EBP ; Protect EBP
PUSH_EDI ; Protect EDI
PUSH_ESI ; Protect ESI
LOADI32_ECX %0 ; OFFSET = 0
LOADI32_EDI %0 ; member_size = 0
LOADI32_EAX %28 ; sizeof(struct type)
CALL32 %malloc ; malloc(sizeof(struct type))
COPY_EAX_to_EDX ; Set HEAD
LOADI32_EAX %28 ; sizeof(struct type)
CALL32 %malloc ; malloc(sizeof(struct type))
COPY_EAX_to_EBP ; Set I
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S
STORE32_EAX_into_EDX_Immediate8 !24 ; HEAD->NAME = global_token->S
STORE32_EAX_into_EBP_Immediate8 !24 ; I->NAME = global_token->S
STORE32_EBP_into_EDX_Immediate8 !12 ; HEAD->INDIRECT = I
STORE32_EDX_into_EBP_Immediate8 !12 ; I->INDIRECT = HEAD
LOAD32_Absolute32_eax &global_types ; Using global_types
STORE32_EAX_into_EDX ; HEAD->NEXT = global_types
STORE32_Absolute32_edx &global_types ; global_types = HEAD
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->NEXT
STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT
LOADI32_EAX %4 ; Using register size
STORE32_EAX_into_EBP_Immediate8 !4 ; I->SIZE = register size
LOADI32_EAX &create_struct_string_0 ; Using "ERROR in create_struct\n Missing {\n"
LOADI32_EBX &open_curly_brace ; Using "{"
CALL32 %require_match ; Make sure we have it
LOADI32_ESI %0 ; LAST = NULL
:create_struct_iter
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S
LOAD8_al_from_EAX ; global_token->S[0]
MOVZX_al ; Make it useful
CMPI8_EAX !125 ; IF global_token->S[0] == "}"
JE32 %create_struct_done ; be done
;; Looks like we are adding members
;; Lets see if it is a union
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S
LOADI32_EBX &union ; Using "union"
CALL32 %match ; IF match(global_token->s, "union")
CMPI8_EAX !0 ; Deal with union
JNE32 %create_struct_single ; Otherwise deal with singles
;; Deal with union
COPY_ESI_to_EAX ; Put last in right place
COPY_ECX_to_EBX ; put offset in right place
CALL32 %build_union ; ASSEMBLE
COPY_EAX_to_ESI ; last = build_union(last, offset)
ADD_edi_into_ecx ; offset = offset + member_size
LOADI32_EAX &create_struct_string_1 ; Using "ERROR in create_struct\n Missing ;\n"
LOADI32_EBX &semicolon ; Using ";"
CALL32 %require_match ; Make sure we have it
JMP32 %create_struct_iter ; keep going
:create_struct_single
;; deal with singles
COPY_ESI_to_EAX ; Put last in right place
COPY_ECX_to_EBX ; put offset in right place
CALL32 %build_member ; ASSEMBLE
COPY_EAX_to_ESI ; last = build_union(last, offset)
ADD_edi_into_ecx ; offset = offset + member_size
LOADI32_EAX &create_struct_string_1 ; Using "ERROR in create_struct\n Missing ;\n"
LOADI32_EBX &semicolon ; Using ";"
CALL32 %require_match ; Make sure we have it
JMP32 %create_struct_iter ; keep going
:create_struct_done
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->NEXT
STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT
LOADI32_EAX &create_struct_string_1 ; Using "ERROR in create_struct\n Missing ;\n"
LOADI32_EBX &semicolon ; Using ";"
CALL32 %require_match ; Make sure we have it
STORE32_ECX_into_EDX_Immediate8 !4 ; HEAD->SIZE = OFFSET
STORE32_ESI_into_EDX_Immedate8 !16 ; HEAD->MEMBERS = LAST
STORE32_ESI_into_EBP_Immedate8 !16 ; I->MEMBERS = LAST
POP_ESI ; Restore ESI
POP_EDI ; Restore EDI
POP_EBP ; Restore EBP
POP_EDX ; Restore EDX
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:create_struct_string_0 "ERROR in create_struct
Missing {
"
:create_struct_string_1 "ERROR in create_struct
Missing ;
"
;; lookup_member function
;; Receives struct type* parent in EAX and char* name in EBX
;; Returns struct type* I in EAX
;; Uses char* NAME in EBX, ECX for struct type* I and EDX to hold parent for errors
;; Aborts hard if not found
:lookup_member
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
PUSH_EDX ; Protect EDX
COPY_EAX_to_EDX ; Protect Parent
LOAD32_ECX_from_EAX_Immediate8 !16 ; struct type* I = parent->MEMBERS
:lookup_member_iter
CMPI8_ECX !0 ; IF I == NULL
JE32 %lookup_member_fail ; Abort HARD
LOAD32_EAX_from_ECX_Immediate8 !24 ; Using I->NAME
CALL32 %match ; IF I->NAME == NAME
CMPI8_EAX !0 ; Then we have found the member
COPY_ECX_to_EAX ; Prepare for return
LOAD32_ECX_from_ECX_Immediate8 !16 ; Prepare for loop I = I->MEMBERS
JNE32 %lookup_member_iter ; Looks like we are looping
;; I is already in EAX
POP_EDX ; Restore EDX
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:lookup_member_fail
LOADI32_EAX %2 ; Using Standard error
STORE32_Absolute32_eax &Output_file ; write to standard error
LOADI32_EAX &lookup_member_string_0 ; Using "ERROR in lookup_member "
CALL32 %File_Print ; print it
LOAD32_EAX_from_EDX_Immediate8 !24 ; PARENT->NAME
CALL32 %File_Print ; print it
LOADI32_EAX &arrow_string ; Using "->"
CALL32 %File_Print ; print it
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S
CALL32 %File_Print ; print it
LOADI32_EAX &lookup_member_string_1 ; Using " does not exist\n"
CALL32 %File_Print ; print it
; CALL32 %line_error ; Write useful debug info
LOADI32_EAX &lookup_member_string_2 ; Using "\n"
CALL32 %File_Print ; print it
JMP32 %Exit_Failure ; Abort Hard
:lookup_member_string_0 "ERROR in lookup_member "
:lookup_member_string_1 " does not exist
"
:lookup_member_string_2 "
"
;; build_member function
;; Receives struct type* last in EAX, int offset in EBX and global member_size in EDI
;; Updates member_size in EDI and returns struct type* I in EAX
;; Uses ECX for struct type* member_type and EDX for struct type* I
:build_member
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
PUSH_EDX ; Protect EDX
COPY_EAX_to_EDX ; Put last out of the way
LOADI32_EAX %28 ; Allocate type
CALL32 %malloc ; Get I
STORE32_EDX_into_EAX_Immediate8 !16 ; I->MEMBERS = LAST
STORE32_EBX_into_EAX_Immediate8 !8 ; I->OFFSET = OFFSET
COPY_EAX_to_EDX ; Put I in place
CALL32 %type_name ; Get member_type
COPY_EAX_to_ECX ; Put in place
STORE32_ECX_into_EDX_Immediate8 !20 ; I->TYPE = MEMBER_TYPE
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EBX_from_EAX_Immediate8 !8 ; global_token->S
STORE32_EBX_into_EDX_Immediate8 !24 ; I->NAME = global_token->S
LOAD32_EAX_from_EAX ; global_token->NEXT
STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT
;; Check if we have an array
LOAD32_EBX_from_EAX_Immediate8 !8 ; global_token->S
LOADI32_EAX &open_bracket ; Using "["
CALL32 %match ; IF global_token->S == "["
CMPI8_EAX !0 ; Then we have to deal with arrays in our structs
JE32 %build_member_array ; So deal with that pain
;; Deal with non-array case
LOAD32_EAX_from_ECX_Immediate8 !4 ; member_type->SIZE
STORE32_EAX_into_EDX_Immediate8 !4 ; I->SIZE = member_type->SIZE
JMP32 %build_member_done ; Be done
:build_member_array
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->NEXT
STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT
LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S
CALL32 %numerate_string ; convert number
LOAD32_EBX_from_ECX_Immediate8 !20 ; member_type->TYPE
LOAD32_EBX_from_EBX_Immediate8 !4 ; member_type->TYPE->SIZE
IMUL_EAX_by_EBX ; member_type->type->size * numerate_string(global_token->s)
STORE32_EAX_into_EDX_Immediate8 !4 ; I->SIZE = member_type->type->size * numerate_string(global_token->s)
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->NEXT
STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT
LOADI32_EAX &build_member_string_0 ; Using "Struct only supports [num] form\n"
LOADI32_EBX &close_bracket ; Using "]"
CALL32 %require_match ; Make sure we have it
:build_member_done
LOAD32_EDI_from_EDX_Immediate8 !4 ; MEMBER_SIZE = I->SIZE
STORE32_ECX_into_EDX_Immediate8 !20 ; I->TYPE = MEMBER_TYPE
COPY_EDX_to_EAX ; Return I
POP_EDX ; Restore EDX
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:build_member_string_0 "Struct only supports [num] form
"
;; build_union function
;; Receives struct type* last in EAX, int offset in EBX and global member_size in EDI
;; Updates member_size in EDI and returns struct type* LAST in EAX
;; Uses ECX for struct type* last, EDX for int offset, ESI for int size and EDI for int member_size
:build_union
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
PUSH_EDX ; Protect EDX
PUSH_ESI ; Protect ESI
COPY_EAX_to_ECX ; Put LAST in right spot
COPY_EBX_to_EDX ; Put OFFSET in right spot
LOADI32_ESI %0 ; SIZE = 0
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->NEXT
STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT
LOADI32_EAX &build_union_string_0 ; Using "ERROR in build_union\nMissing {\n"
LOADI32_EBX &open_curly_brace ; Using "{"
CALL32 %require_match ; Make sure we have it
:build_union_iter
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S
LOAD8_al_from_EAX ; global_token->S[0]
MOVZX_al ; make it useful
CMPI8_EAX !125 ; IF global_token->S[0] == "}"
JE32 %build_union_done ; Be done
;; Collect union member
COPY_ECX_to_EAX ; Passing LAST
COPY_EDX_to_EBX ; Passing offset
CALL32 %build_member ; build_member(last, offset)
COPY_EAX_to_ECX ; last = build_member(last, offset)
CMP_EDI_ESI ; IF member_size > size
JG32 %build_union_size ; Then update size
;; deal with member_size > size
COPY_EDI_to_ESI ; SIZE = MEMBER_SIZE
:build_union_size
LOADI32_EAX &build_union_string_1 ; Using "ERROR in build_union\nMissing ;\n"
LOADI32_EBX &semicolon ; Using ";"
CALL32 %require_match ; Make sure we have it
JMP32 %build_union_iter ; Keep going
:build_union_done
COPY_ESI_to_EDI ; MEMBER_SIZE = SIZE
LOAD32_Absolute32_eax &global_token ; Using global_token
LOAD32_EAX_from_EAX ; global_token->NEXT
STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT
COPY_ECX_to_EAX ; Return last
POP_ESI ; Restore ESI
POP_EDX ; Restore EDX
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:build_union_string_0 "ERROR in build_union
Missing {
"
:build_union_string_1 "ERROR in build_union
Missing ;
"
;; sym_declare function
;; Receives char *s in EAX, struct type* t in EBX, and struct token_list* list in ECX
;; Returns struct token_list* in EAX
;; Uses EAX for A
:sym_declare
PUSH_EDX ; Protect EDX
COPY_EAX_to_EDX ; Get char *S safely out of the way
LOADI32_EAX %20 ; Using sizeof(struct token_list)
CALL32 %malloc ; Get pointer to A
STORE32_ECX_into_EAX ; A->NEXT = LIST
STORE32_EDX_into_EAX_Immediate8 !8 ; A->S = S
STORE32_EBX_into_EAX_Immediate8 !12 ; A->TYPE = T
POP_EDX ; Restore EDX
RETURN
;; match function
;; Receives CHAR* in EAX and CHAR* in EBX
;; Returns 0 (TRUE) or 1 (FALSE) in EAX
:match
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
PUSH_EDX ; Protect EDX
COPY_EAX_to_ECX ; S1 in place
COPY_EBX_to_EDX ; S2 in place
:match_Loop
LOAD8_al_from_ECX ; S1[0]
MOVZX_al ; Make it useful
LOAD8_bl_from_EDX ; S2[0]
MOVZX_bl ; Make it useful
CMP_EAX_EBX ; See if they match
JNE32 %match_False ; If not
ADDI8_ECX !1 ; S1 = S1 + 1
ADDI8_EDX !1 ; S2 = S2 + 1
CMPI8_EAX !0 ; If reached end of string
JE32 %match_Done ; Perfect match
JMP32 %match_Loop ; Otherwise keep looping
:match_False
LOADI32_EAX %1 ; Return false
:match_Done
POP_EDX ; Restore EDX
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
;; numerate_number function
;; Receives an INT A in EAX
;; Returns char* result in EAX
;; Allocates 16 bytes of memory
;; Behaves badly when given a negative number too large
;; Uses EAX for temp, EBX for DIVISOR, EDX for mod/0, ESI for result[i] and EBP for A
:numerate_number
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
PUSH_EDX ; Protect EDX
PUSH_ESI ; Protect ESI
PUSH_EBP ; Protect EBP
COPY_EAX_to_EBP ; Protect A
LOADI32_EAX %16 ; 16bytes
CALL32 %malloc ; Get our pointer
PUSH_EAX ; Protect our pointer
COPY_EAX_to_ESI ; put pointer in right place
LOADI32_EBX %0x3B9ACA00 ; Set divisor to largest positive number that fits in 32bits
CMPI8_EBP !0 ; Deal with 0 case
JE32 %numerate_number_ZERO ; If it is
JG32 %numerate_number_positive ; If it is positive
;; Deal with negative case
LOADI32_EAX %45 ; Using "-"
STORE8_al_into_Address_ESI ; Write it
ADDI8_ESI !1 ; increment
IMULI8_EBP !-1 ; A = A * -1
:numerate_number_positive
LOADI32_EDX %0 ; Set top to 0
COPY_EBP_to_EAX ; Using A as bottom
IDIV_EBX ; edx:eax % ebx -> edx + edx:eax / ebx -> eax [Even if we don't want it]
CMPI8_EAX !0 ; IF 0 == (a / divisor)
JNE32 %numerate_number_iter ; Clean up those leading Zeros
LOADI32_EDX %0 ; Set top to 0
COPY_EBX_to_EAX ; Using Divisor for bottom
LOADI32_EBX %10 ; Make this shit work because idiv 10 doesn't work
IDIV_EBX ; edx:eax % 10 -> edx + edx:eax / 10 -> eax [Even if we don't want it]
COPY_EAX_to_EBX ; Update divisor
JMP32 %numerate_number_positive ; Keep collecting
:numerate_number_iter
CMPI8_EBX !0 ; IF DIVISOR < 0
JLE32 %numerate_number_done ; Be done
LOADI32_EDX %0 ; Set top to 0
COPY_EBP_to_EAX ; Using A as bottom
IDIV_EBX ; edx:eax % ebx -> edx + edx:eax / ebx -> eax [Even if we don't want it]
ADDI8_EAX !48 ; ((a / divisor) + 48)
STORE8_al_into_Address_ESI ; Write it
COPY_EDX_to_EBP ; a = a % divisor
LOADI32_EDX %0 ; Set top to 0
COPY_EBX_to_EAX ; Using Divisor for bottom
LOADI32_EBX %10 ; Make this shit work because idiv 10 doesn't work
IDIV_EBX ; edx:eax % 10 -> edx + edx:eax / 10 -> eax [Even if we don't want it]
COPY_EAX_to_EBX ; Update divisor
ADDI8_ESI !1 ; increment
JMP32 %numerate_number_iter ; Keep going
:numerate_number_done
POP_EAX ; Restore our result
POP_EBP ; Restore EBP
POP_ESI ; Restore ESI
POP_EDX ; Restore EDX
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
:numerate_number_ZERO
LOADI32_EAX %48 ; Using '0'
STORE8_al_into_Address_ESI ; Write it
ADDI8_ESI !1 ; increment
JMP32 %numerate_number_done ; Be done
;; numerate_string function
;; Receives CHAR* in EAX
;; Returns value of CHAR* in EAX
;; Uses EAX for VALUE, EBX for S, ECX for CH and ESI for NEGATIVE?
:numerate_string
PUSH_EBX ; Protect EBX
PUSH_ECX ; Protect ECX
PUSH_EDX ; Protect EDX
PUSH_ESI ; Protect ESI
COPY_EAX_to_EBX ; put S in correct place
LOADI32_EAX %0 ; Initialize to Zero
:numerate_string_loop
LOAD8_cl_from_EBX_Immediate8 !1 ; S[1]
MOVZX_cl ; make it useful
CMPI8_ECX !120 ; IF 'x' == S[1]
JE32 %numerate_hex ; Deal with hex input
;; Assume decimal input
LOADI32_ESI %0 ; Assume no negation
LOAD8_cl_from_EBX ; S[0]
MOVZX_cl ; make it useful
CMPI8_ECX !45 ; IF '-' == S[0]
JNE32 %numerate_decimal ; Skip negation
LOADI32_ESI %1 ; Set FLAG
ADDI8_EBX !1 ; S = S + 1
:numerate_decimal
LOAD8_cl_from_EBX ; S[0]
MOVZX_cl ; make it useful
CMPI8_ECX !0 ; IF NULL == S[0]
JE32 %numerate_decimal_done ; We are done
IMULI8_EAX !10 ; VALUE = VALUE * 10
SUBI8_ECX !48 ; CH = CH - '0'
CMPI8_ECX !9 ; Check for illegal
JG32 %numerate_string_fail ; If CH > '9'
CMPI8_ECX !0 ; Check for illegal
JL32 %numerate_string_fail ; IF CH < 0
ADD_ecx_into_eax ; VALUE = VALUE + CH
ADDI8_EBX !1 ; S = S + 1
JMP32 %numerate_decimal ; Keep looping
:numerate_decimal_done
CMPI8_ESI !1 ; Check if need to negate
JNE32 %numerate_string_done ; Nope
IMULI8_EAX !-1 ; VALUE = VALUE * -1
JMP32 %numerate_string_done ; Done
:numerate_hex
ADDI8_EBX !2 ; S = S + 2
:numerate_hex_loop
LOAD8_cl_from_EBX ; S[0]
MOVZX_cl ; make it useful
CMPI8_ECX !0 ; IF NULL == S[0]
JE32 %numerate_string_done ; We are done
SALI8_EAX !4 ; VALUE = VALUE << 4
SUBI8_ECX !48 ; CH = CH - '0'
CMPI8_ECX !10 ; IF 10 >= CH
JL32 %numerate_hex_digit ; NO
SUBI8_ECX !7 ; Push A-F into range
:numerate_hex_digit
CMPI8_ECX !15 ; Check for illegal
JG32 %numerate_string_fail ; If CH > 'F'
CMPI8_ECX !0 ; Check for illegal
JL32 %numerate_string_fail ; IF CH < 0
ADD_ecx_into_eax ; VALUE = VALUE + CH
ADDI8_EBX !1 ; S = S + 1
JMP32 %numerate_hex_loop ; Keep looping
:numerate_string_fail
LOADI32_EAX %0 ; return ZERO
:numerate_string_done
POP_ESI ; Restore ESI
POP_EDX ; Restore EDX
POP_ECX ; Restore ECX
POP_EBX ; Restore EBX
RETURN
;; Exit_Failure function
;; Receives nothing
;; And aborts hard
;; Does NOT return
:Exit_Failure
LOADI32_EBX %1 ; All is wrong
LOADI32_EAX %1 ; put the exit syscall number in eax
INT_80 ; Call it a bad day
;; Keywords
:union "union"
:struct "struct"
:constant "CONSTANT"
:main_string "main"
:argc_string "argc"
:argv_string "argv"
:if_string "if"
:else_string "else"
:do_string "do"
:while_string "while"
:for_string "for"
:asm_string "asm"
:goto_string "goto"
:return_string "return"
:break_string "break"
:continue_string "continue"
:sizeof_string "sizeof"
:plus_string "+"
:minus_string "-"
:multiply_string "*"
:divide_string "/"
:modulus_string "%"
:left_shift_string "<<"
:right_shift_string ">>"
:less_than_string "<"
:less_than_equal_string "<="
:greater_than_equal_string ">="
:greater_than_string ">"
:equal_to_string "=="
:not_equal_string "!="
:bitwise_and "&"
:logical_and "&&"
:bitwise_or "|"
:logical_or "||"
:bitwise_xor "^"
:arrow_string "->"
;; Frequently Used strings
;; Generally used by require_match
:open_curly_brace "{"
:close_curly_brace "}"
:open_paren "("
:close_paren ")"
:open_bracket "["
:close_bracket "]"
:comma ","
:semicolon ";"
:equal "="
:percent "%"
:newline "\n"
:underline "_"
:prim_types
:type_void
&type_int ; NEXT
%4 ; SIZE
%0 ; OFFSET
&type_void ; INDIRECT
%0 ; MEMBERS
&type_void ; TYPE
&type_void_name ; NAME
:type_void_name "void"
:type_int
&type_char ; NEXT
%4 ; SIZE
%0 ; OFFSET
&type_int ; INDIRECT
%0 ; MEMBERS
&type_int ; TYPE
&type_int_name ; NAME
:type_int_name "int"
:type_char
&type_file ; NEXT
%1 ; SIZE
%0 ; OFFSET
&type_char_indirect ; INDIRECT
%0 ; MEMBERS
&type_char ; TYPE
&type_char_name ; NAME
:type_char_name "char"
:type_char_indirect
&type_file ; NEXT
%4 ; SIZE
%0 ; OFFSET
&type_char_double_indirect ; INDIRECT
%0 ; MEMBERS
&type_char_indirect ; TYPE
&type_char_indirect_name ; NAME
:type_char_indirect_name "char*"
:type_char_double_indirect
&type_file ; NEXT
%4 ; SIZE
%0 ; OFFSET
&type_char_double_indirect ; INDIRECT
%0 ; MEMBERS
&type_char_indirect ; TYPE
&type_char_double_indirect_name ; NAME
:type_char_double_indirect_name "char**"
:type_file
&type_function ; NEXT
%4 ; SIZE
%0 ; OFFSET
&type_file ; INDIRECT
%0 ; MEMBERS
&type_file ; TYPE
&type_file_name ; NAME
:type_file_name "FILE"
:type_function
&type_unsigned ; NEXT
%4 ; SIZE
%0 ; OFFSET
&type_function ; INDIRECT
%0 ; MEMBERS
&type_function ; TYPE
&type_function_name ; NAME
:type_function_name "FUNCTION"
:type_unsigned
&type_long ; NEXT
%4 ; SIZE
%0 ; OFFSET
&type_unsigned ; INDIRECT
%0 ; MEMBERS
&type_unsigned ; TYPE
&type_unsigned_name ; NAME
:type_unsigned_name "unsigned"
:type_long
%0 ; NEXT
%4 ; SIZE
%0 ; OFFSET
&type_long ; INDIRECT
%0 ; MEMBERS
&type_long ; TYPE
&type_long_name ; NAME
:type_long_name "long"
;; debug_list function
;; Receives struct token_list* in EAX
;; Prints contents of list and exits
;; Does NOT return
:debug_list
COPY_EAX_to_EBP ; Protect the list pointer
LOADI32_EAX %2 ; Using Standard error
STORE32_Absolute32_eax &Output_file ; write to standard error
:debug_list_iter
;; Header
LOADI32_EAX &debug_list_string0 ; Using our first string
CALL32 %File_Print ; Print it
COPY_EBP_to_EAX ; Use address of pointer
CALL32 %numerate_number ; Convert it into string
CALL32 %File_Print ; Print it
;; NEXT
LOADI32_EAX &debug_list_string1 ; Using our second string
CALL32 %File_Print ; Print it
LOAD32_EAX_from_EBP_Immediate8 !0 ; Use address of pointer
CALL32 %numerate_number ; Convert it into string
CALL32 %File_Print ; Print it
;; PREV
LOADI32_EAX &debug_list_string2 ; Using our third string
CALL32 %File_Print ; Print it
LOAD32_EAX_from_EBP_Immediate8 !4 ; Use address of pointer
CALL32 %numerate_number ; Convert it into string
CALL32 %File_Print ; Print it
;; S
LOADI32_EAX &debug_list_string3 ; Using our fourth string
CALL32 %File_Print ; Print it
LOAD32_EAX_from_EBP_Immediate8 !8 ; Use address of pointer
CALL32 %numerate_number ; Convert it into string
CALL32 %File_Print ; Print it
;; S Contents
LOADI32_EAX &debug_list_string4 ; Using our fifth string
CALL32 %File_Print ; Print it
LOAD32_EAX_from_EBP_Immediate8 !8 ; Use address of string
CMPI8_EAX !0 ; IF NULL Pointer
JNE32 %debug_list_null ; otherwise display
LOADI32_EAX &debug_list_string_null ; Give meaningful message instead
:debug_list_null
CALL32 %File_Print ; Print it
;; TYPE
LOADI32_EAX &debug_list_string5 ; Using our sixth string
CALL32 %File_Print ; Print it
LOAD32_EAX_from_EBP_Immediate8 !12 ; Use address of pointer
CALL32 %numerate_number ; Convert it into string
CALL32 %File_Print ; Print it
;; ARGS/DEPTH
LOADI32_EAX &debug_list_string6 ; Using our seventh string
CALL32 %File_Print ; Print it
LOAD32_EAX_from_EBP_Immediate8 !16 ; Use address of pointer
CALL32 %numerate_number ; Convert it into string
CALL32 %File_Print ; Print it
LOADI32_EAX %10 ; Add "\n"
CALL32 %fputc ; print it
CALL32 %fputc ; print it
LOAD32_EBP_from_EBP ; TOKEN = TOKEN->NEXT
CMPI8_EBP !0 ; Check if NULL
JNE32 %debug_list_iter ; iterate otherwise
LOADI32_EBX %666 ; All is HELL
LOADI32_EAX %1 ; put the exit syscall number in eax
INT_80 ; Call it a bad day
:debug_list_string0 "Token_list node at address: "
:debug_list_string1 "
NEXT address: "
:debug_list_string2 "
PREV address: "
:debug_list_string3 "
S address: "
:debug_list_string4 "
The contents of S are: "
:debug_list_string5 "
TYPE address: "
:debug_list_string6 "
ARGUMENTS address: "
:debug_list_string_null ">::<NULL>::<"
:Address_of
NULL
:C
NULL
:Input_file
NULL
:MALLOC
NULL
:Output_file
NULL
:Token
NULL
:break_frame
NULL
:break_target_func
NULL
:break_target_head
NULL
:break_target_num
NULL
:current_count
NULL
:current_target
NULL
:function
NULL
:global_constant_list
NULL
:global_function_list
NULL
:global_symbol_list
NULL
:global_token
NULL
:global_types
&prim_types
:globals_list
NULL
:output_list
NULL
:string_index
NULL
:strings_list
NULL
:ELF_end
/* Copyright (C) 2016 Jeremiah Orians
* This file is part of M2-Planet.
*
* M2-Planet is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* M2-Planet is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with M2-Planet. If not, see <http://www.gnu.org/licenses/>.
*/
// CONSTANT stdin 0
// CONSTANT stdout 1
// CONSTANT stderr 2
// CONSTANT EOF 0xFFFFFFFF
// CONSTANT NULL 0
// CONSTANT EXIT_FAILURE 1
// CONSTANT EXIT_SUCCESS 0
// CONSTANT TRUE 1
// CONSTANT FALSE 0
int fgetc(FILE* f)
{
asm("mov_eax, %3"
"lea_ebx,[esp+DWORD] %4"
"mov_ebx,[ebx]"
"push_ebx"
"mov_ecx,esp"
"mov_edx, %1"
"int !0x80"
"test_eax,eax"
"pop_eax"
"jne %FUNCTION_fgetc_Done"
"mov_eax, %-1"
":FUNCTION_fgetc_Done");
}
void fputc(char s, FILE* f)
{
asm("mov_eax, %4"
"lea_ebx,[esp+DWORD] %4"
"mov_ebx,[ebx]"
"lea_ecx,[esp+DWORD] %8"
"mov_edx, %1"
"int !0x80");
}
void fputs(char* s, FILE* f)
{
while(0 != s[0])
{
fputc(s[0], f);
s = s + 1;
}
}
FILE* open(char* name, int flag, int mode)
{
asm("lea_ebx,[esp+DWORD] %12"
"mov_ebx,[ebx]"
"lea_ecx,[esp+DWORD] %8"
"mov_ecx,[ecx]"
"lea_edx,[esp+DWORD] %4"
"mov_edx,[edx]"
"mov_eax, %5"
"int !0x80");
}
FILE* fopen(char* filename, char* mode)
{
FILE* f;
if('w' == mode[0])
{ /* 577 is O_WRONLY|O_CREAT|O_TRUNC, 384 is 600 in octal */
f = open(filename, 577 , 384);
}
else
{ /* Everything else is a read */
f = open(filename, 0, 0);
}
/* Negative numbers are error codes */
if(0 > f)
{
return 0;
}
return f;
}
int close(int fd)
{
asm("lea_ebx,[esp+DWORD] %4"
"mov_ebx,[ebx]"
"mov_eax, %6"
"int !0x80");
}
int fclose(FILE* stream)
{
int error = close(stream);
return error;
}
int brk(void *addr)
{
asm("mov_eax,[esp+DWORD] %4"
"push_eax"
"mov_eax, %45"
"pop_ebx"
"int !0x80");
}
long _malloc_ptr;
long _brk_ptr;
void* malloc(int size)
{
if(NULL == _brk_ptr)
{
_brk_ptr = brk(0);
_malloc_ptr = _brk_ptr;
}
if(_brk_ptr < _malloc_ptr + size)
{
_brk_ptr = brk(_malloc_ptr + size);
if(-1 == _brk_ptr) return 0;
}
long old_malloc = _malloc_ptr;
_malloc_ptr = _malloc_ptr + size;
return old_malloc;
}
int strlen(char* str )
{
int i = 0;
while(0 != str[i]) i = i + 1;
return i;
}
void* memset(void* ptr, int value, int num)
{
char* s;
for(s = ptr; 0 < num; num = num - 1)
{
s[0] = value;
s = s + 1;
}
}
void* calloc(int count, int size)
{
void* ret = malloc(count * size);
if(NULL == ret) return NULL;
memset(ret, 0, (count * size));
return ret;
}
void free(void* l)
{
return;
}
void exit(int value)
{
asm("pop_ebx"
"pop_ebx"
"mov_eax, %1"
"int !0x80");
}
/* Copyright (C) 2016 Jeremiah Orians
* Copyright (C) 2020 deesix <deesix@tuta.io>
* This file is part of M2-Planet.
*
* M2-Planet is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* M2-Planet is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with M2-Planet. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
// CONSTANT FALSE 0
#define FALSE 0
// CONSTANT TRUE 1
#define TRUE 1
// CONSTANT KNIGHT_NATIVE 1
#define KNIGHT_NATIVE 1
// CONSTANT KNIGHT_POSIX 2
#define KNIGHT_POSIX 2
// CONSTANT X86 3
#define X86 3
// CONSTANT AMD64 4
#define AMD64 4
// CONSTANT ARMV7L 5
#define ARMV7L 5
// CONSTANT AARCH64 6
#define AARCH64 6
// CONSTANT RISCV32 7
#define RISCV32 7
// CONSTANT RISCV64 8
#define RISCV64 8
void copy_string(char* target, char* source, int max);
int in_set(int c, char* s);
int match(char* a, char* b);
void require(int bool, char* error);
void reset_hold_string();
struct type
{
struct type* next;
int size;
int offset;
int is_signed;
struct type* indirect;
struct type* members;
struct type* type;
char* name;
};
struct token_list
{
struct token_list* next;
union
{
struct token_list* locals;
struct token_list* prev;
};
char* s;
union
{
struct type* type;
char* filename;
};
union
{
struct token_list* arguments;
int depth;
int linenumber;
};
};
#include "cc_globals.h"
/* Copyright (C) 2016 Jeremiah Orians
* This file is part of M2-Planet.
*
* M2-Planet is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* M2-Planet is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with M2-Planet. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0
void require(int bool, char* error)
{
if(!bool)
{
fputs(error, stderr);
exit(EXIT_FAILURE);
}
}
int match(char* a, char* b)
{
if((NULL == a) && (NULL == b)) return TRUE;
if(NULL == a) return FALSE;
if(NULL == b) return FALSE;
int i = -1;
do
{
i = i + 1;
if(a[i] != b[i])
{
return FALSE;
}
} while((0 != a[i]) && (0 !=b[i]));
return TRUE;
}
int in_set(int c, char* s)
{
/* NULL set is always false */
if(NULL == s) return FALSE;
while(0 != s[0])
{
if(c == s[0]) return TRUE;
s = s + 1;
}
return FALSE;
}
/* INTERNAL ONLY */
int __index_number(char* s, char c)
{
int i = 0;
while(s[i] != c)
{
i = i + 1;
if(0 == s[i]) return -1;
}
return i;
}
/* INTERNAL ONLY */
int __toupper(int c)
{
if(in_set(c, "abcdefghijklmnopqrstuvwxyz")) return (c & 0xDF);
return c;
}
/* INTERNAL ONLY */
int __set_reader(char* set, int mult, char* input)
{
int n = 0;
int i = 0;
int hold;
int negative_p = FALSE;
if(input[0] == '-')
{
negative_p = TRUE;
i = i + 1;
}
while(in_set(input[i], set))
{
n = n * mult;
hold = __index_number(set, __toupper(input[i]));
/* Input managed to change between in_set and index_number */
if(-1 == hold) return 0;
n = n + hold;
i = i + 1;
}
/* loop exited before NULL and thus invalid input */
if(0 != input[i]) return 0;
if(negative_p)
{
n = 0 - n;
}
return n;
}
int strtoint(char *a)
{
int result = 0;
/* If NULL string */
if(0 == a[0])
{
result = 0;
}
/* Deal with binary */
else if ('0' == a[0] && 'b' == a[1])
{
result = __set_reader("01", 2, a+2);
}
/* Deal with hex */
else if ('0' == a[0] && 'x' == a[1])
{
result = __set_reader("0123456789ABCDEFabcdef", 16, a+2);
}
/* Deal with octal */
else if('0' == a[0])
{
result = __set_reader("01234567", 8, a+1);
}
/* Deal with decimal */
else
{
result = __set_reader("0123456789", 10, a);
}
/* Deal with sign extension for 64bit hosts */
if(0 != (0x80000000 & result)) result = (0xFFFFFFFF << 31) | result;
return result;
}
char* int2str(int x, int base, int signed_p)
{
require(1 < base, "int2str doesn't support a base less than 2\n");
require(37 > base, "int2str doesn't support a base more than 36\n");
/* Be overly conservative and save space for 32binary digits and padding null */
char* p = calloc(34, sizeof(char));
/* if calloc fails return null to let calling code deal with it */
if(NULL == p) return p;
p = p + 32;
unsigned i;
int sign_p = FALSE;
char* table = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if(signed_p && (10 == base) && (0 != (x & 0x80000000)))
{
/* Truncate to 31bits */
i = -x & 0x7FFFFFFF;
if(0 == i) return "-2147483648";
sign_p = TRUE;
} /* Truncate to 32bits */
else i = x & (0x7FFFFFFF | (1 << 31));
do
{
p[0] = table[i % base];
p = p - 1;
i = i / base;
} while(0 < i);
if(sign_p)
{
p[0] = '-';
p = p - 1;
}
return p + 1;
}
/* Copyright (C) 2016 Jeremiah Orians * Copyright (C) 2020 deesix <deesix@tuta.io> * This file is part of M2-Planet. * * M2-Planet is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * M2-Planet is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with M2-Planet. If not, see <http://www.gnu.org/licenses/>. */ /* What types we have */ struct type* global_types; struct type* prim_types; /* What we are currently working on */ struct token_list* global_token; /* Output reorder collections*/ struct token_list* output_list; struct token_list* strings_list; struct token_list* globals_list; /* Make our string collection more efficient */ char* hold_string; int string_index; /* Our Target Architecture */ int Architecture; int register_size; int MAX_STRING; struct type* integer; /* enable bootstrap-mode */ int BOOTSTRAP_MODE; /* enable preprocessor-only mode */ int PREPROCESSOR_MODE;
/* Copyright (C) 2016 Jeremiah Orians
* Copyright (C) 2021 Andrius Å tikonas <andrius@stikonas.eu>
* This file is part of M2-Planet.
*
* M2-Planet is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* M2-Planet is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with M2-Planet. If not, see <http://www.gnu.org/licenses/>.
*/
#include "cc.h"
int strtoint(char *a);
/* Globals */
FILE* input;
struct token_list* token;
int line;
char* file;
int grab_byte()
{
int c = fgetc(input);
if(10 == c) line = line + 1;
return c;
}
int clearWhiteSpace(int c)
{
if((32 == c) || (9 == c)) return clearWhiteSpace(grab_byte());
return c;
}
int consume_byte(int c)
{
hold_string[string_index] = c;
string_index = string_index + 1;
require(MAX_STRING > string_index, "Token exceeded MAX_STRING char limit\nuse --max-string number to increase\n");
return grab_byte();
}
int preserve_string(int c)
{
int frequent = c;
int escape = FALSE;
do
{
if(!escape && '\\' == c ) escape = TRUE;
else escape = FALSE;
c = consume_byte(c);
require(EOF != c, "Unterminated string\n");
} while(escape || (c != frequent));
return grab_byte();
}
void copy_string(char* target, char* source, int max)
{
int i = 0;
while(0 != source[i])
{
target[i] = source[i];
i = i + 1;
if(i == max) break;
}
}
void fixup_label()
{
int hold = ':';
int prev;
int i = 0;
do
{
prev = hold;
hold = hold_string[i];
hold_string[i] = prev;
i = i + 1;
} while(0 != hold);
}
int preserve_keyword(int c, char* S)
{
while(in_set(c, S))
{
c = consume_byte(c);
}
return c;
}
void reset_hold_string()
{
int i = MAX_STRING;
while(0 <= i)
{
hold_string[i] = 0;
i = i - 1;
}
string_index = 0;
}
/* note if this is the first token in the list, head needs fixing up */
struct token_list* eat_token(struct token_list* token)
{
if(NULL != token->prev)
{
token->prev->next = token->next;
}
/* update backlinks */
if(NULL != token->next)
{
token->next->prev = token->prev;
}
return token->next;
}
struct token_list* eat_until_newline(struct token_list* head)
{
while (NULL != head)
{
if('\n' == head->s[0])
{
return head;
}
else
{
head = eat_token(head);
}
}
return NULL;
}
struct token_list* remove_line_comments(struct token_list* head)
{
struct token_list* first = NULL;
while (NULL != head)
{
if(match("//", head->s))
{
head = eat_until_newline(head);
}
else
{
if(NULL == first)
{
first = head;
}
head = head->next;
}
}
return first;
}
struct token_list* remove_line_comment_tokens(struct token_list* head)
{
struct token_list* first = NULL;
while (NULL != head)
{
if(match("//", head->s))
{
head = eat_token(head);
}
else
{
if(NULL == first)
{
first = head;
}
head = head->next;
}
}
return first;
}
struct token_list* remove_preprocessor_directives(struct token_list* head)
{
struct token_list* first = NULL;
while (NULL != head)
{
if('#' == head->s[0])
{
head = eat_until_newline(head);
}
else
{
if(NULL == first)
{
first = head;
}
head = head->next;
}
}
return first;
}
void new_token(char* s, int size)
{
struct token_list* current = calloc(1, sizeof(struct token_list));
require(NULL != current, "Exhausted memory while getting token\n");
/* More efficiently allocate memory for string */
current->s = calloc(size, sizeof(char));
require(NULL != current->s, "Exhausted memory while trying to copy a token\n");
copy_string(current->s, s, MAX_STRING);
current->prev = token;
current->next = token;
current->linenumber = line;
current->filename = file;
token = current;
}
int get_token(int c)
{
struct token_list* current = calloc(1, sizeof(struct token_list));
require(NULL != current, "Exhausted memory while getting token\n");
reset:
reset_hold_string();
string_index = 0;
c = clearWhiteSpace(c);
if(c == EOF)
{
free(current);
return c;
}
else if('#' == c)
{
c = consume_byte(c);
c = preserve_keyword(c, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_");
}
else if(in_set(c, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"))
{
c = preserve_keyword(c, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_");
if(':' == c)
{
fixup_label();
c = ' ';
}
}
else if(in_set(c, "<=>|&!^%"))
{
c = preserve_keyword(c, "<=>|&!^%");
}
else if(in_set(c, "'\""))
{
c = preserve_string(c);
}
else if(c == '/')
{
c = consume_byte(c);
if(c == '*')
{
c = grab_byte();
while(c != '/')
{
while(c != '*')
{
c = grab_byte();
require(EOF != c, "Hit EOF inside of block comment\n");
}
c = grab_byte();
require(EOF != c, "Hit EOF inside of block comment\n");
}
c = grab_byte();
goto reset;
}
else if(c == '/')
{
c = consume_byte(c);
}
else if(c == '=')
{
c = consume_byte(c);
}
}
else if (c == '\n')
{
c = consume_byte(c);
}
else if(c == '*')
{
c = consume_byte(c);
if(c == '=')
{
c = consume_byte(c);
}
}
else if(c == '+')
{
c = consume_byte(c);
if(c == '=')
{
c = consume_byte(c);
}
if(c == '+')
{
c = consume_byte(c);
}
}
else if(c == '-')
{
c = consume_byte(c);
if(c == '=')
{
c = consume_byte(c);
}
if(c == '>')
{
c = consume_byte(c);
}
if(c == '-')
{
c = consume_byte(c);
}
}
else
{
c = consume_byte(c);
}
new_token(hold_string, string_index + 2);
return c;
}
int consume_filename(int c)
{
reset_hold_string();
int done = FALSE;
while(!done)
{
if(c == EOF)
{
fputs("we don't support EOF as a filename in #FILENAME statements\n", stderr);
exit(EXIT_FAILURE);
}
else if((32 == c) || (9 == c) || (c == '\n'))
{
c = grab_byte();
}
else
{
do
{
c = consume_byte(c);
require(EOF != c, "Unterminated filename in #FILENAME\n");
} while((32 != c) && (9 != c) && ('\n' != c));
done = TRUE;
}
}
/* with just a little extra to put in the matching at the end */
new_token(hold_string, string_index + 3);
return c;
}
int change_filename(int ch)
{
require(EOF != ch, "#FILENAME failed to receive filename\n");
/* Remove the #FILENAME */
token = token->next;
/* Get new filename */
ch = consume_filename(ch);
file = token->s;
/* Remove it from the processing list */
token = token->next;
require(EOF != ch, "#FILENAME failed to receive filename\n");
/* Get new line number */
ch = get_token(ch);
line = strtoint(token->s);
if(0 == line)
{
if('0' != token->s[0])
{
fputs("non-line number: ", stderr);
fputs(token->s, stderr);
fputs(" provided to #FILENAME\n", stderr);
exit(EXIT_FAILURE);
}
}
/* Remove it from the processing list */
token = token->next;
return ch;
}
struct token_list* reverse_list(struct token_list* head)
{
struct token_list* root = NULL;
struct token_list* next;
while(NULL != head)
{
next = head->next;
head->next = root;
root = head;
head = next;
}
return root;
}
struct token_list* read_all_tokens(FILE* a, struct token_list* current, char* filename)
{
input = a;
line = 1;
file = filename;
token = current;
int ch = grab_byte();
while(EOF != ch)
{
ch = get_token(ch);
require(NULL != token, "Empty files don't need to be compiled\n");
if(match("#FILENAME", token->s)) ch = change_filename(ch);
}
return token;
}
/* Copyright (C) 2016 Jeremiah Orians
* Copyright (C) 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
* This file is part of M2-Planet.
*
* M2-Planet is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* M2-Planet is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with M2-Planet. If not, see <http://www.gnu.org/licenses/>.
*/
#include "cc.h"
#include <stdint.h>
struct token_list* emit(char *s, struct token_list* head);
void require(int bool, char* error);
char upcase(char a)
{
if(in_set(a, "abcdefghijklmnopqrstuvwxyz"))
{
a = a - 32;
}
return a;
}
int char2hex(int c)
{
if (c >= '0' && c <= '9') return (c - 48);
else if (c >= 'a' && c <= 'f') return (c - 87);
else if (c >= 'A' && c <= 'F') return (c - 55);
else return -1;
}
int hexify(int c, int high)
{
int i = char2hex(c);
if(0 > i)
{
fputs("Tried to print non-hex number\n", stderr);
exit(EXIT_FAILURE);
}
if(high)
{
i = i << 4;
}
return i;
}
int escape_lookup(char* c);
int weird(char* string)
{
int c;
string = string + 1;
weird_reset:
c = string[0];
if(0 == c) return FALSE;
if('\\' == c)
{
c = escape_lookup(string);
if('x' == string[1]) string = string + 2;
string = string + 1;
}
if(!in_set(c, "\t\n !#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~")) return TRUE;
if(in_set(c, " \t\n\r") && (':' == string[1])) return TRUE;
string = string + 1;
goto weird_reset;
}
/* Lookup escape values */
int escape_lookup(char* c)
{
if('\\' != c[0]) return c[0];
if(c[1] == 'x')
{
int t1 = hexify(c[2], TRUE);
int t2 = hexify(c[3], FALSE);
return t1 + t2;
}
else if(c[1] == '0') return 0;
else if(c[1] == 'a') return 7;
else if(c[1] == 'b') return 8;
else if(c[1] == 't') return 9;
else if(c[1] == 'n') return 10;
else if(c[1] == 'v') return 11;
else if(c[1] == 'f') return 12;
else if(c[1] == 'r') return 13;
else if(c[1] == 'e') return 27;
else if(c[1] == '"') return 34;
else if(c[1] == '\'') return 39;
else if(c[1] == '\\') return 92;
fputs("Unknown escape received: ", stderr);
fputs(c, stderr);
fputs(" Unable to process\n", stderr);
exit(EXIT_FAILURE);
}
/* Deal with human strings */
char* collect_regular_string(char* string)
{
string_index = 0;
collect_regular_string_reset:
require((MAX_STRING - 3) > string_index, "Attempt at parsing regular string exceeds max length\n");
if(string[0] == '\\')
{
hold_string[string_index] = escape_lookup(string);
if (string[1] == 'x') string = string + 2;
string = string + 2;
}
else
{
hold_string[string_index] = string[0];
string = string + 1;
}
string_index = string_index + 1;
if(string[0] != 0) goto collect_regular_string_reset;
hold_string[string_index] = '"';
hold_string[string_index + 1] = '\n';
char* message = calloc(string_index + 3, sizeof(char));
require(NULL != message, "Exhausted memory while storing regular string\n");
copy_string(message, hold_string, string_index + 2);
reset_hold_string();
return message;
}
/* Deal with non-human strings */
char* collect_weird_string(char* string)
{
string_index = 1;
int temp;
char* table = "0123456789ABCDEF";
hold_string[0] = '\'';
collect_weird_string_reset:
require((MAX_STRING - 6) > string_index, "Attempt at parsing weird string exceeds max length\n");
string = string + 1;
hold_string[string_index] = ' ';
temp = escape_lookup(string) & 0xFF;
hold_string[string_index + 1] = table[(temp >> 4)];
hold_string[string_index + 2] = table[(temp & 15)];
if(string[0] == '\\')
{
if(string[1] == 'x') string = string + 2;
string = string + 1;
}
string_index = string_index + 3;
if(string[1] != 0) goto collect_weird_string_reset;
hold_string[string_index] = ' ';
hold_string[string_index + 1] = '0';
hold_string[string_index + 2] = '0';
hold_string[string_index + 3] = '\'';
hold_string[string_index + 4] = '\n';
char* hold = calloc(string_index + 6, sizeof(char));
require(NULL != hold, "Exhausted available memory while attempting to collect a weird string\n");
copy_string(hold, hold_string, string_index + 5);
reset_hold_string();
return hold;
}
/* Parse string to deal with hex characters*/
char* parse_string(char* string)
{
/* the string */
if(weird(string)) return collect_weird_string(string);
else return collect_regular_string(string);
}
/* Copyright (C) 2016 Jeremiah Orians
* Copyright (C) 2020 deesix <deesix@tuta.io>
* This file is part of M2-Planet.
*
* M2-Planet is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* M2-Planet is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with M2-Planet. If not, see <http://www.gnu.org/licenses/>.
*/
#include "cc.h"
/* Imported functions */
int strtoint(char *a);
void line_error();
void require(int bool, char* error);
/* enable easy primitive extension */
struct type* add_primitive(struct type* a)
{
if(NULL == prim_types) return a;
struct type* i = prim_types;
while(NULL != i->next)
{
i = i->next;
}
i->next = a;
return prim_types;
}
/* enable easy primitive creation */
struct type* new_primitive(char* name0, char* name1, char* name2, int size, int sign)
{
/* Create type** */
struct type* a = calloc(1, sizeof(struct type));
require(NULL != a, "Exhausted memory while declaring new primitive**\n");
a->name = name2;
a->size = register_size;
a->indirect = a;
a->is_signed = sign;
/* Create type* */
struct type* b = calloc(1, sizeof(struct type));
require(NULL != b, "Exhausted memory while declaring new primitive*\n");
b->name = name1;
b->size = register_size;
b->is_signed = sign;
b->indirect = a;
a->type = b;
struct type* r = calloc(1, sizeof(struct type));
require(NULL != r, "Exhausted memory while declaring new primitive\n");
r->name = name0;
r->size = size;
r->is_signed = sign;
r->indirect = b;
r->type = r;
b->type = r;
return r;
}
/* Initialize default types */
void initialize_types()
{
if(AMD64 == Architecture || AARCH64 == Architecture || RISCV64 == Architecture) register_size = 8;
else register_size = 4;
/* Define void */
struct type* hold = new_primitive("void", "void*", "void**", register_size, FALSE);
prim_types = add_primitive(hold);
/* Define unsigned LONG */
hold = new_primitive("SCM","SCM*", "SCM**", register_size, FALSE);
prim_types = add_primitive(hold);
/* Define LONG */
hold = new_primitive("long", "long*", "long**", register_size, TRUE);
prim_types = add_primitive(hold);
/* Define UNSIGNED */
hold = new_primitive("unsigned", "unsigned*", "unsigned**", register_size, FALSE);
prim_types = add_primitive(hold);
/* Define int */
integer = new_primitive("int", "int*", "int**", register_size, TRUE);
prim_types = add_primitive(integer);
/* Define uint32_t */
hold = new_primitive("uint32_t", "uint32_t*", "uint32_t**", 4, FALSE);
prim_types = add_primitive(hold);
/* Define int32_t */
hold = new_primitive("int32_t", "int32_t*", "int32_t**", 4, TRUE);
prim_types = add_primitive(hold);
/* Define uint16_t */
hold = new_primitive("uint16_t", "uint16_t*", "uint16_t**", 2, FALSE);
prim_types = add_primitive(hold);
/* Define int16_t */
hold = new_primitive("int16_t", "int16_t*", "int16_t**", 2, TRUE);
prim_types = add_primitive(hold);
/* Define uint8_t */
hold = new_primitive("uint8_t", "uint8_t*", "uint8_t**", 1, FALSE);
prim_types = add_primitive(hold);
/* Define int8_t */
hold = new_primitive("int8_t", "int8_t*", "int8_t**", 1, TRUE);
prim_types = add_primitive(hold);
/* Define char */
hold = new_primitive("char", "char*", "char**", 1, TRUE);
prim_types = add_primitive(hold);
/* Define FUNCTION */
hold = new_primitive("FUNCTION", "FUNCTION*", "FUNCTION**", register_size, FALSE);
prim_types = add_primitive(hold);
if(BOOTSTRAP_MODE)
{
/* Define FILE */
hold = new_primitive("FILE", "FILE*", "FILE**", register_size, TRUE);
prim_types = add_primitive(hold);
/* Primitives mes.c wanted */
hold = new_primitive("size_t", "size_t*", "size_t**", register_size, FALSE);
prim_types = add_primitive(hold);
hold = new_primitive("ssize_t", "ssize_t*", "ssize_t**", register_size, FALSE);
prim_types = add_primitive(hold);
}
global_types = prim_types;
}
struct type* lookup_type(char* s, struct type* start)
{
struct type* i;
for(i = start; NULL != i; i = i->next)
{
if(match(i->name, s))
{
return i;
}
}
return NULL;
}
struct type* lookup_member(struct type* parent, char* name)
{
struct type* i;
require(NULL != parent, "Not a valid struct type\n");
for(i = parent->members; NULL != i; i = i->members)
{
if(match(i->name, name)) return i;
}
fputs("ERROR in lookup_member ", stderr);
fputs(parent->name, stderr);
fputs("->", stderr);
fputs(global_token->s, stderr);
fputs(" does not exist\n", stderr);
line_error();
fputs("\n", stderr);
exit(EXIT_FAILURE);
}
struct type* type_name();
void require_match(char* message, char* required);
int member_size;
struct type* build_member(struct type* last, int offset)
{
struct type* i = calloc(1, sizeof(struct type));
require(NULL != i, "Exhausted memory while building a struct member\n");
i->members = last;
i->offset = offset;
struct type* member_type = type_name();
require(NULL != member_type, "struct member type can not be invalid\n");
i->type = member_type;
i->name = global_token->s;
global_token = global_token->next;
require(NULL != global_token, "struct member can not be EOF terminated\n");
/* Check to see if array */
if(match( "[", global_token->s))
{
global_token = global_token->next;
require(NULL != global_token, "struct member arrays can not be EOF sized\n");
i->size = member_type->type->size * strtoint(global_token->s);
if(0 == i->size)
{
fputs("Struct only supports [num] form\n", stderr);
exit(EXIT_FAILURE);
}
global_token = global_token->next;
require_match("Struct only supports [num] form\n", "]");
}
else
{
i->size = member_type->size;
}
member_size = i->size;
return i;
}
struct type* build_union(struct type* last, int offset)
{
int size = 0;
global_token = global_token->next;
require_match("ERROR in build_union\nMissing {\n", "{");
while('}' != global_token->s[0])
{
last = build_member(last, offset);
if(member_size > size)
{
size = member_size;
}
require_match("ERROR in build_union\nMissing ;\n", ";");
require(NULL != global_token, "Unterminated union\n");
}
member_size = size;
global_token = global_token->next;
return last;
}
void create_struct()
{
int offset = 0;
member_size = 0;
struct type* head = calloc(1, sizeof(struct type));
require(NULL != head, "Exhausted memory while creating a struct\n");
struct type* i = calloc(1, sizeof(struct type));
require(NULL != i, "Exhausted memory while creating a struct indirection\n");
struct type* ii = calloc(1, sizeof(struct type));
require(NULL != ii, "Exhausted memory while creating a struct double indirection\n");
head->name = global_token->s;
head->type = head;
head->indirect = i;
head->next = global_types;
i->name = global_token->s;
i->type = head;
i->indirect = ii;
i->size = register_size;
ii->name = global_token->s;
ii->type = i;
ii->indirect = ii;
ii->size = register_size;
global_types = head;
global_token = global_token->next;
require_match("ERROR in create_struct\n Missing {\n", "{");
struct type* last = NULL;
require(NULL != global_token, "Incomplete struct definition at end of file\n");
while('}' != global_token->s[0])
{
if(match(global_token->s, "union"))
{
last = build_union(last, offset);
}
else
{
last = build_member(last, offset);
}
offset = offset + member_size;
require_match("ERROR in create_struct\n Missing ;\n", ";");
require(NULL != global_token, "Unterminated struct\n");
}
global_token = global_token->next;
require_match("ERROR in create_struct\n Missing ;\n", ";");
head->size = offset;
head->members = last;
i->members = last;
}
struct type* type_name()
{
struct type* ret;
require(NULL != global_token, "Received EOF instead of type name\n");
if(match("extern", global_token->s))
{
global_token = global_token->next;
require(NULL != global_token, "unfinished type definition in extern\n");
}
if(match("struct", global_token->s))
{
global_token = global_token->next;
require(NULL != global_token, "structs can not have a EOF type name\n");
ret = lookup_type(global_token->s, global_types);
if(NULL == ret)
{
create_struct();
return NULL;
}
}
else
{
ret = lookup_type(global_token->s, global_types);
if(NULL == ret)
{
fputs("Unknown type ", stderr);
fputs(global_token->s, stderr);
fputs("\n", stderr);
line_error();
fputs("\n", stderr);
exit(EXIT_FAILURE);
}
}
global_token = global_token->next;
require(NULL != global_token, "unfinished type definition\n");
if(match("const", global_token->s))
{
global_token = global_token->next;
require(NULL != global_token, "unfinished type definition in const\n");
}
while(global_token->s[0] == '*')
{
ret = ret->indirect;
global_token = global_token->next;
require(NULL != global_token, "unfinished type definition in indirection\n");
}
return ret;
}
struct type* mirror_type(struct type* source, char* name)
{
struct type* head = calloc(1, sizeof(struct type));
require(NULL != head, "Exhausted memory while creating a struct\n");
struct type* i = calloc(1, sizeof(struct type));
require(NULL != i, "Exhausted memory while creating a struct indirection\n");
head->name = name;
i->name = name;
head->size = source->size;
i->size = source->indirect->size;
head->offset = source->offset;
i->offset = source->indirect->offset;
head->is_signed = source->is_signed;
i->is_signed = source->indirect->is_signed;
head->indirect = i;
i->indirect = head;
head->members = source->members;
i->members = source->indirect->members;
head->type = head;
i->type = i;
return head;
}
/* Copyright (C) 2016 Jeremiah Orians
* Copyright (C) 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
* Copyright (C) 2020 deesix <deesix@tuta.io>
* Copyright (C) 2021 Andrius Å tikonas <andrius@stikonas.eu>
* This file is part of M2-Planet.
*
* M2-Planet is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* M2-Planet is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with M2-Planet. If not, see <http://www.gnu.org/licenses/>.
*/
#include "cc.h"
#include "gcc_req.h"
#include <stdint.h>
/* Global lists */
struct token_list* global_symbol_list;
struct token_list* global_function_list;
struct token_list* global_constant_list;
/* Core lists for this file */
struct token_list* function;
/* What we are currently working on */
struct type* current_target;
char* break_target_head;
char* break_target_func;
char* break_target_num;
char* continue_target_head;
struct token_list* break_frame;
int current_count;
int Address_of;
/* Imported functions */
char* int2str(int x, int base, int signed_p);
int strtoint(char *a);
char* parse_string(char* string);
int escape_lookup(char* c);
void require(int bool, char* error);
struct token_list* reverse_list(struct token_list* head);
struct type* mirror_type(struct type* source, char* name);
struct type* add_primitive(struct type* a);
struct token_list* emit(char *s, struct token_list* head)
{
struct token_list* t = calloc(1, sizeof(struct token_list));
require(NULL != t, "Exhausted memory while generating token to emit\n");
t->next = head;
t->s = s;
return t;
}
void emit_out(char* s)
{
output_list = emit(s, output_list);
}
struct token_list* uniqueID(char* s, struct token_list* l, char* num)
{
l = emit("\n", emit(num, emit("_", emit(s, l))));
return l;
}
void uniqueID_out(char* s, char* num)
{
output_list = uniqueID(s, output_list, num);
}
struct token_list* sym_declare(char *s, struct type* t, struct token_list* list)
{
struct token_list* a = calloc(1, sizeof(struct token_list));
require(NULL != a, "Exhausted memory while attempting to declare a symbol\n");
a->next = list;
a->s = s;
a->type = t;
return a;
}
struct token_list* sym_lookup(char *s, struct token_list* symbol_list)
{
struct token_list* i;
for(i = symbol_list; NULL != i; i = i->next)
{
if(match(i->s, s)) return i;
}
return NULL;
}
void line_error_token(struct token_list *token)
{
if(NULL == token)
{
fputs("EOF reached inside of line_error\n", stderr);
fputs("problem at end of file\n", stderr);
return;
}
fputs(token->filename, stderr);
fputs(":", stderr);
fputs(int2str(token->linenumber, 10, TRUE), stderr);
fputs(":", stderr);
}
void line_error()
{
line_error_token(global_token);
}
void require_match(char* message, char* required)
{
if(NULL == global_token)
{
line_error();
fputs("EOF reached inside of require match\n", stderr);
fputs("problem at end of file\n", stderr);
fputs(message, stderr);
exit(EXIT_FAILURE);
}
if(!match(global_token->s, required))
{
line_error();
fputs(message, stderr);
exit(EXIT_FAILURE);
}
global_token = global_token->next;
}
void maybe_bootstrap_error(char* feature)
{
if (BOOTSTRAP_MODE)
{
line_error();
fputs(feature, stderr);
fputs(" is not supported in --bootstrap-mode\n", stderr);
exit(EXIT_FAILURE);
}
}
void expression();
void function_call(char* s, int bool)
{
require_match("ERROR in process_expression_list\nNo ( was found\n", "(");
require(NULL != global_token, "Improper function call\n");
int passed = 0;
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
{
emit_out("PUSHR R13 R15\t# Prevent overwriting in recursion\n");
emit_out("PUSHR R14 R15\t# Protect the old base pointer\n");
emit_out("COPY R13 R15\t# Copy new base pointer\n");
}
else if(X86 == Architecture)
{
emit_out("push_edi\t# Prevent overwriting in recursion\n");
emit_out("push_ebp\t# Protect the old base pointer\n");
emit_out("mov_edi,esp\t# Copy new base pointer\n");
}
else if(AMD64 == Architecture)
{
emit_out("push_rdi\t# Prevent overwriting in recursion\n");
emit_out("push_rbp\t# Protect the old base pointer\n");
emit_out("mov_rdi,rsp\t# Copy new base pointer\n");
}
else if(ARMV7L == Architecture)
{
emit_out("{R11} PUSH_ALWAYS\t# Prevent overwriting in recursion\n");
emit_out("{BP} PUSH_ALWAYS\t# Protect the old base pointer\n");
emit_out("'0' SP R11 NO_SHIFT MOVE_ALWAYS\t# Copy new base pointer\n");
}
else if(AARCH64 == Architecture)
{
emit_out("PUSH_X16\t# Protect a tmp register we're going to use\n");
emit_out("PUSH_LR\t# Protect the old return pointer (link)\n");
emit_out("PUSH_BP\t# Protect the old base pointer\n");
emit_out("SET_X16_FROM_SP\t# The base pointer to-be\n");
}
else if(RISCV32 == Architecture)
{
emit_out("RD_SP RS1_SP !-12 ADDI\t# Allocate stack\n");
emit_out("RS1_SP RS2_RA @4 SW\t# Protect the old return pointer\n");
emit_out("RS1_SP RS2_FP SW\t# Protect the old frame pointer\n");
emit_out("RS1_SP RS2_TP @8 SW\t# Protect temp register we are going to use\n");
emit_out("RD_TP RS1_SP MV\t# The base pointer to-be\n");
}
else if(RISCV64 == Architecture)
{
emit_out("RD_SP RS1_SP !-24 ADDI\t# Allocate stack\n");
emit_out("RS1_SP RS2_RA @8 SD\t# Protect the old return pointer\n");
emit_out("RS1_SP RS2_FP SD\t# Protect the old frame pointer\n");
emit_out("RS1_SP RS2_TP @16 SD\t# Protect temp register we are going to use\n");
emit_out("RD_TP RS1_SP MV\t# The base pointer to-be\n");
}
if(global_token->s[0] != ')')
{
expression();
require(NULL != global_token, "incomplete function call, received EOF instead of )\n");
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("PUSHR R0 R15\t#_process_expression1\n");
else if(X86 == Architecture) emit_out("push_eax\t#_process_expression1\n");
else if(AMD64 == Architecture) emit_out("push_rax\t#_process_expression1\n");
else if(ARMV7L == Architecture) emit_out("{R0} PUSH_ALWAYS\t#_process_expression1\n");
else if(AARCH64 == Architecture) emit_out("PUSH_X0\t#_process_expression1\n");
else if(RISCV32 == Architecture) emit_out("RD_SP RS1_SP !-4 ADDI\nRS1_SP RS2_A0 SW\t#_process_expression1\n");
else if(RISCV64 == Architecture) emit_out("RD_SP RS1_SP !-8 ADDI\nRS1_SP RS2_A0 SD\t#_process_expression1\n");
passed = 1;
while(global_token->s[0] == ',')
{
global_token = global_token->next;
require(NULL != global_token, "incomplete function call, received EOF instead of argument\n");
expression();
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("PUSHR R0 R15\t#_process_expression2\n");
else if(X86 == Architecture) emit_out("push_eax\t#_process_expression2\n");
else if(AMD64 == Architecture) emit_out("push_rax\t#_process_expression2\n");
else if(ARMV7L == Architecture) emit_out("{R0} PUSH_ALWAYS\t#_process_expression2\n");
else if(AARCH64 == Architecture) emit_out("PUSH_X0\t#_process_expression2\n");
else if(RISCV32 == Architecture) emit_out("RD_SP RS1_SP !-4 ADDI\nRS1_SP RS2_A0 SW\t#_process_expression2\n");
else if(RISCV64 == Architecture) emit_out("RD_SP RS1_SP !-8 ADDI\nRS1_SP RS2_A0 SD\t#_process_expression2\n");
passed = passed + 1;
}
}
require_match("ERROR in process_expression_list\nNo ) was found\n", ")");
if(TRUE == bool)
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
{
emit_out("LOAD R0 R14 ");
emit_out(s);
emit_out("\nMOVE R14 R13\n");
emit_out("CALL R0 R15\n");
}
else if(X86 == Architecture)
{
emit_out("lea_eax,[ebp+DWORD] %");
emit_out(s);
emit_out("\nmov_eax,[eax]\n");
emit_out("mov_ebp,edi\n");
emit_out("call_eax\n");
}
else if(AMD64 == Architecture)
{
emit_out("lea_rax,[rbp+DWORD] %");
emit_out(s);
emit_out("\nmov_rax,[rax]\n");
emit_out("mov_rbp,rdi\n");
emit_out("call_rax\n");
}
else if(ARMV7L == Architecture)
{
emit_out("!");
emit_out(s);
emit_out(" R0 SUB BP ARITH_ALWAYS\n");
emit_out("!0 R0 LOAD32 R0 MEMORY\n");
emit_out("{LR} PUSH_ALWAYS\t# Protect the old link register\n");
emit_out("'0' R11 BP NO_SHIFT MOVE_ALWAYS\n");
emit_out("'3' R0 CALL_REG_ALWAYS\n");
emit_out("{LR} POP_ALWAYS\t# Prevent overwrite\n");
}
else if(AARCH64 == Architecture)
{
emit_out("SET_X0_FROM_BP\n");
emit_out("LOAD_W1_AHEAD\nSKIP_32_DATA\n%");
emit_out(s);
emit_out("\nSUB_X0_X0_X1\n");
emit_out("DEREF_X0\n");
emit_out("SET_BP_FROM_X16\n");
emit_out("SET_X16_FROM_X0\n");
emit_out("BLR_X16\n");
}
else if(RISCV32 == Architecture)
{
emit_out("RD_A0 RS1_FP !");
emit_out(s);
emit_out(" ADDI\n");
emit_out("RD_A0 RS1_A0 LW\n");
emit_out("RD_FP RS1_TP MV\n");
emit_out("RD_RA RS1_A0 JALR\n");
}
else if(RISCV64 == Architecture)
{
emit_out("RD_A0 RS1_FP !");
emit_out(s);
emit_out(" ADDI\n");
emit_out("RD_A0 RS1_A0 LD\n");
emit_out("RD_FP RS1_TP MV\n");
emit_out("RD_RA RS1_A0 JALR\n");
}
}
else
{
if((KNIGHT_NATIVE == Architecture) || (KNIGHT_POSIX == Architecture))
{
emit_out("MOVE R14 R13\n");
emit_out("LOADR R0 4\nJUMP 4\n&FUNCTION_");
emit_out(s);
emit_out("\nCALL R0 R15\n");
}
else if(X86 == Architecture)
{
emit_out("mov_ebp,edi\n");
emit_out("call %FUNCTION_");
emit_out(s);
emit_out("\n");
}
else if(AMD64 == Architecture)
{
emit_out("mov_rbp,rdi\n");
emit_out("call %FUNCTION_");
emit_out(s);
emit_out("\n");
}
else if(ARMV7L == Architecture)
{
emit_out("{LR} PUSH_ALWAYS\t# Protect the old link register\n");
emit_out("'0' R11 BP NO_SHIFT MOVE_ALWAYS\n");
emit_out("^~FUNCTION_");
emit_out(s);
emit_out(" CALL_ALWAYS\n");
emit_out("{LR} POP_ALWAYS\t# Restore the old link register\n");
}
else if(AARCH64 == Architecture)
{
emit_out("SET_BP_FROM_X16\n");
emit_out("LOAD_W16_AHEAD\nSKIP_32_DATA\n&FUNCTION_");
emit_out(s);
emit_out("\n");
emit_out("BLR_X16\n");
}
else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
{
emit_out("RD_FP RS1_TP MV\n");
emit_out("RD_RA $FUNCTION_");
emit_out(s);
emit_out(" JAL\n");
}
}
for(; passed > 0; passed = passed - 1)
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("POPR R1 R15\t# _process_expression_locals\n");
else if(X86 == Architecture) emit_out("pop_ebx\t# _process_expression_locals\n");
else if(AMD64 == Architecture) emit_out("pop_rbx\t# _process_expression_locals\n");
else if(ARMV7L == Architecture) emit_out("{R1} POP_ALWAYS\t# _process_expression_locals\n");
else if(AARCH64 == Architecture) emit_out("POP_X1\t# _process_expression_locals\n");
else if(RISCV32 == Architecture) emit_out("RD_A1 RS1_SP LW\t# _process_expression_locals\nRD_SP RS1_SP !4 ADDI\n");
else if(RISCV64 == Architecture) emit_out("RD_A1 RS1_SP LD\t# _process_expression_locals\nRD_SP RS1_SP !8 ADDI\n");
}
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
{
emit_out("POPR R14 R15\t# Restore old base pointer\n");
emit_out("POPR R13 R15\t# Prevent overwrite\n");
}
else if(X86 == Architecture)
{
emit_out("pop_ebp\t# Restore old base pointer\n");
emit_out("pop_edi\t# Prevent overwrite\n");
}
else if(AMD64 == Architecture)
{
emit_out("pop_rbp\t# Restore old base pointer\n");
emit_out("pop_rdi\t# Prevent overwrite\n");
}
else if(ARMV7L == Architecture)
{
emit_out("{BP} POP_ALWAYS\t# Restore old base pointer\n");
emit_out("{R11} POP_ALWAYS\t# Prevent overwrite\n");
}
else if(AARCH64 == Architecture)
{
emit_out("POP_BP\t# Restore the old base pointer\n");
emit_out("POP_LR\t# Restore the old return pointer (link)\n");
emit_out("POP_X16\t# Restore a register we used as tmp\n");
}
else if(RISCV32 == Architecture)
{
emit_out("RD_FP RS1_SP LW\t# Restore old frame pointer\n");
emit_out("RD_TP RS1_SP !8 LW\t# Restore temp register\n");
emit_out("RD_RA RS1_SP !4 LW\t# Restore return address\n");
emit_out("RD_SP RS1_SP !12 ADDI\t# Deallocate stack\n");
}
else if(RISCV64 == Architecture)
{
emit_out("RD_FP RS1_SP LD\t# Restore old frame pointer\n");
emit_out("RD_TP RS1_SP !16 LD\t# Restore temp register\n");
emit_out("RD_RA RS1_SP !8 LD\t# Restore return address\n");
emit_out("RD_SP RS1_SP !24 ADDI\t# Deallocate stack\n");
}
}
void constant_load(struct token_list* a)
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("LOADI R0 ");
else if(X86 == Architecture) emit_out("mov_eax, %");
else if(AMD64 == Architecture) emit_out("mov_rax, %");
else if(ARMV7L == Architecture) emit_out("!0 R0 LOAD32 R15 MEMORY\n~0 JUMP_ALWAYS\n%");
else if(AARCH64 == Architecture) emit_out("LOAD_W0_AHEAD\nSKIP_32_DATA\n%");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
{
emit_out("RD_A0 ~");
emit_out(a->arguments->s);
emit_out(" LUI\nRD_A0 RS1_A0 !");
}
emit_out(a->arguments->s);
if(RISCV32 == Architecture) emit_out(" ADDI\n");
else if(RISCV64 == Architecture) emit_out(" ADDIW\n");
emit_out("\n");
}
char* load_value_signed(unsigned size)
{
if(size == 1)
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) return "LOAD8 R0 R0 0\n";
else if(X86 == Architecture) return "movsx_eax,BYTE_PTR_[eax]\n";
else if(AMD64 == Architecture) return "movsx_rax,BYTE_PTR_[rax]\n";
else if(ARMV7L == Architecture) return "LOADS8 R0 LOAD R0 HALF_MEMORY\n";
else if(AARCH64 == Architecture) return "LDRSB_X0_[X0]\n";
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) return "RD_A0 RS1_A0 LB\n";
}
else if(size == 2)
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) return "LOAD16 R0 R0 0\n";
else if(X86 == Architecture) return "movsx_eax,WORD_PTR_[eax]\n";
else if(AMD64 == Architecture) return "movsx_rax,WORD_PTR_[rax]\n";
else if(ARMV7L == Architecture) return "LOADS16 R0 LOAD R0 HALF_MEMORY\n";
else if(AARCH64 == Architecture) return "LDRSH_X0_[X0]\n";
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) return "RD_A0 RS1_A0 LH\n";
}
else if(size == 4)
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) return "LOAD R0 R0 0\n";
else if(X86 == Architecture) return "mov_eax,[eax]\n";
else if(AMD64 == Architecture) return "movsx_rax,DWORD_PTR_[rax]\n";
else if(ARMV7L == Architecture) return "!0 R0 LOAD32 R0 MEMORY\n";
else if(AARCH64 == Architecture) return "LDR_W0_[X0]\n";
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) return "RD_A0 RS1_A0 LW\n";
}
else if(size == 8)
{
if(AMD64 == Architecture) return "mov_rax,[rax]\n";
else if(AARCH64 == Architecture) return "DEREF_X0\n";
else if(RISCV64 == Architecture) return "RD_A0 RS1_A0 LD\n";
}
line_error();
fputs(" Got unsupported size ", stderr);
fputs(int2str(size, 10, TRUE), stderr);
fputs(" when trying to load value.\n", stderr);
exit(EXIT_FAILURE);
}
char* load_value_unsigned(unsigned size)
{
if(size == 1)
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) return "LOADU8 R0 R0 0\n";
else if(X86 == Architecture) return "movzx_eax,BYTE_PTR_[eax]\n";
else if(AMD64 == Architecture) return "movzx_rax,BYTE_PTR_[rax]\n";
else if(ARMV7L == Architecture) return "!0 R0 LOAD R0 MEMORY\n";
else if(AARCH64 == Architecture) return "DEREF_X0_BYTE\n";
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) return "RD_A0 RS1_A0 LBU\n";
}
else if(size == 2)
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) return "LOADU16 R0 R0 0\n";
else if(X86 == Architecture) return "movzx_eax,WORD_PTR_[eax]\n";
else if(AMD64 == Architecture) return "movzx_rax,WORD_PTR_[rax]\n";
else if(ARMV7L == Architecture) return "NO_OFFSET R0 LOAD R0 HALF_MEMORY\n";
else if(AARCH64 == Architecture) return "LDRH_W0_[X0]\n";
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) return "RD_A0 RS1_A0 LHU\n";
}
else if(size == 4)
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) return "LOAD R0 R0 0\n";
else if(X86 == Architecture) return "mov_eax,[eax]\n";
else if(AMD64 == Architecture) return "mov_eax,[rax]\n";
else if(ARMV7L == Architecture) return "!0 R0 LOAD32 R0 MEMORY\n";
else if(AARCH64 == Architecture) return "LDR_W0_[X0]\n";
else if(RISCV32 == Architecture) return "RD_A0 RS1_A0 LW\n";
else if(RISCV64 == Architecture) return "RD_A0 RS1_A0 LWU\n";
}
else if(size == 8)
{
if(AMD64 == Architecture) return "mov_rax,[rax]\n";
else if(AARCH64 == Architecture) return "DEREF_X0\n";
else if(RISCV64 == Architecture) return "RD_A0 RS1_A0 LD\n";
}
line_error();
fputs(" Got unsupported size ", stderr);
fputs(int2str(size, 10, TRUE), stderr);
fputs(" when trying to load value.\n", stderr);
exit(EXIT_FAILURE);
}
char* load_value(unsigned size, int is_signed)
{
if(is_signed) return load_value_signed(size);
return load_value_unsigned(size);
}
char* store_value(unsigned size)
{
if(size == 1)
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) return "STORE8 R0 R1 0\n";
else if(X86 == Architecture) return "mov_[ebx],al\n";
else if(AMD64 == Architecture) return "mov_[rbx],al\n";
else if(ARMV7L == Architecture) return "!0 R0 STORE8 R1 MEMORY\n";
else if(AARCH64 == Architecture) return "STR_BYTE_W0_[X1]\n";
else if(RISCV32 == Architecture) return "RS1_A1 RS2_A0 SB\n";
else if(RISCV64 == Architecture) return "RS1_A1 RS2_A0 SB\n";
}
else if(size == 2)
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) return "STORE16 R0 R1 0\n";
else if(X86 == Architecture) return "mov_[ebx],ax\n";
else if(AMD64 == Architecture) return "mov_[rbx],ax\n";
else if(ARMV7L == Architecture) return "NO_OFFSET R0 STORE16 R1 HALF_MEMORY\n";
else if(AARCH64 == Architecture) return "STRH_W0_[X1]\n";
else if(RISCV32 == Architecture || RISCV64 == Architecture) return "RS1_A1 RS2_A0 SH\n";
}
else if(size == 4)
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) return "STORE R0 R1 0\n";
else if(X86 == Architecture) return "mov_[ebx],eax\n";
else if(AMD64 == Architecture) return "mov_[rbx],eax\n";
else if(ARMV7L == Architecture) return "!0 R0 STORE32 R1 MEMORY\n";
else if(AARCH64 == Architecture) return "STR_W0_[X1]\n";
else if(RISCV32 == Architecture || RISCV64 == Architecture) return "RS1_A1 RS2_A0 SW\n";
}
else if(size == 8)
{
if(AMD64 == Architecture) return "mov_[rbx],rax\n";
else if(AARCH64 == Architecture) return "STR_X0_[X1]\n";
else if(RISCV64 == Architecture) return "RS1_A1 RS2_A0 SD\n";
}
/* Should not happen but print error message. */
fputs("Got unsupported size ", stderr);
fputs(int2str(size, 10, TRUE), stderr);
fputs(" when storing number in register.\n", stderr);
line_error();
exit(EXIT_FAILURE);
}
int is_compound_assignment(char* token)
{
if(match("+=", token)) return TRUE;
else if(match("-=", token)) return TRUE;
else if(match("*=", token)) return TRUE;
else if(match("/=", token)) return TRUE;
else if(match("%=", token)) return TRUE;
else if(match("<<=", token)) return TRUE;
else if(match(">>=", token)) return TRUE;
else if(match("&=", token)) return TRUE;
else if(match("^=", token)) return TRUE;
else if(match("|=", token)) return TRUE;
return FALSE;
}
void postfix_expr_stub();
void variable_load(struct token_list* a, int num_dereference)
{
require(NULL != global_token, "incomplete variable load received\n");
if((match("FUNCTION", a->type->name) || match("FUNCTION*", a->type->name)) && match("(", global_token->s))
{
function_call(int2str(a->depth, 10, TRUE), TRUE);
return;
}
current_target = a->type;
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("ADDI R0 R14 ");
else if(X86 == Architecture) emit_out("lea_eax,[ebp+DWORD] %");
else if(AMD64 == Architecture) emit_out("lea_rax,[rbp+DWORD] %");
else if(ARMV7L == Architecture) emit_out("!");
else if(AARCH64 == Architecture) emit_out("SET_X0_FROM_BP\nLOAD_W1_AHEAD\nSKIP_32_DATA\n%");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RD_A0 RS1_FP !");
emit_out(int2str(a->depth, 10, TRUE));
if(ARMV7L == Architecture) emit_out(" R0 SUB BP ARITH_ALWAYS");
else if(AARCH64 == Architecture) emit_out("\nSUB_X0_X0_X1\n");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out(" ADDI");
emit_out("\n");
if(TRUE == Address_of) return;
if(match(".", global_token->s))
{
postfix_expr_stub();
return;
}
if(!match("=", global_token->s) && !is_compound_assignment(global_token->s))
{
emit_out(load_value(current_target->size, current_target->is_signed));
}
while (num_dereference > 0)
{
current_target = current_target->type;
emit_out(load_value(current_target->size, current_target->is_signed));
num_dereference = num_dereference - 1;
}
}
void function_load(struct token_list* a)
{
require(NULL != global_token, "incomplete function load\n");
if(match("(", global_token->s))
{
function_call(a->s, FALSE);
return;
}
if((KNIGHT_NATIVE == Architecture) || (KNIGHT_POSIX == Architecture)) emit_out("LOADR R0 4\nJUMP 4\n&FUNCTION_");
else if(X86 == Architecture) emit_out("mov_eax, &FUNCTION_");
else if(AMD64 == Architecture) emit_out("lea_rax,[rip+DWORD] %FUNCTION_");
else if(ARMV7L == Architecture) emit_out("!0 R0 LOAD32 R15 MEMORY\n~0 JUMP_ALWAYS\n&FUNCTION_");
else if(AARCH64 == Architecture) emit_out("LOAD_W0_AHEAD\nSKIP_32_DATA\n&FUNCTION_");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RD_A0 ~FUNCTION_");
emit_out(a->s);
if(RISCV32 == Architecture)
{
emit_out(" AUIPC\n");
emit_out("RD_A0 RS1_A0 !FUNCTION_");
emit_out(a->s);
emit_out(" ADDI");
}
else if(RISCV64 == Architecture)
{
emit_out(" AUIPC\n");
emit_out("RD_A0 RS1_A0 !FUNCTION_");
emit_out(a->s);
emit_out(" ADDIW");
}
emit_out("\n");
}
void global_load(struct token_list* a)
{
current_target = a->type;
if((KNIGHT_NATIVE == Architecture) || (KNIGHT_POSIX == Architecture)) emit_out("LOADR R0 4\nJUMP 4\n&GLOBAL_");
else if(X86 == Architecture) emit_out("mov_eax, &GLOBAL_");
else if(AMD64 == Architecture) emit_out("lea_rax,[rip+DWORD] %GLOBAL_");
else if(ARMV7L == Architecture) emit_out("!0 R0 LOAD32 R15 MEMORY\n~0 JUMP_ALWAYS\n&GLOBAL_");
else if(AARCH64 == Architecture) emit_out("LOAD_W0_AHEAD\nSKIP_32_DATA\n&GLOBAL_");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RD_A0 ~GLOBAL_");
emit_out(a->s);
if((RISCV32 == Architecture) || (RISCV64 == Architecture))
{
emit_out(" AUIPC\n");
emit_out("RD_A0 RS1_A0 !GLOBAL_");
emit_out(a->s);
emit_out(" ADDI");
}
emit_out("\n");
require(NULL != global_token, "unterminated global load\n");
if(TRUE == Address_of) return;
if(match(".", global_token->s))
{
postfix_expr_stub();
return;
}
if(match("=", global_token->s) || is_compound_assignment(global_token->s)) return;
emit_out(load_value(register_size, current_target->is_signed));
}
/*
* primary-expr:
* FAILURE
* "String"
* 'Char'
* [0-9]*
* [a-z,A-Z]*
* ( expression )
*/
void primary_expr_failure()
{
require(NULL != global_token, "hit EOF when expecting primary expression\n");
line_error();
fputs("Received ", stderr);
fputs(global_token->s, stderr);
fputs(" in primary_expr\n", stderr);
exit(EXIT_FAILURE);
}
void primary_expr_string()
{
char* number_string = int2str(current_count, 10, TRUE);
current_count = current_count + 1;
if((KNIGHT_NATIVE == Architecture) || (KNIGHT_POSIX == Architecture)) emit_out("LOADR R0 4\nJUMP 4\n&STRING_");
else if(X86 == Architecture) emit_out("mov_eax, &STRING_");
else if(AMD64 == Architecture) emit_out("lea_rax,[rip+DWORD] %STRING_");
else if(ARMV7L == Architecture) emit_out("!0 R0 LOAD32 R15 MEMORY\n~0 JUMP_ALWAYS\n&STRING_");
else if(AARCH64 == Architecture) emit_out("LOAD_W0_AHEAD\nSKIP_32_DATA\n&STRING_");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RD_A0 ~STRING_");
uniqueID_out(function->s, number_string);
if((RISCV32 == Architecture) || (RISCV64 == Architecture))
{
emit_out("AUIPC\n");
emit_out("RD_A0 RS1_A0 !STRING_");
uniqueID_out(function->s, number_string);
emit_out("ADDI\n");
}
/* The target */
strings_list = emit(":STRING_", strings_list);
strings_list = uniqueID(function->s, strings_list, number_string);
/* catch case of just "foo" from segfaulting */
require(NULL != global_token->next, "a string by itself is not valid C\n");
/* Parse the string */
if('"' != global_token->next->s[0])
{
strings_list = emit(parse_string(global_token->s), strings_list);
global_token = global_token->next;
}
else
{
char* s = calloc(MAX_STRING, sizeof(char));
/* prefix leading string */
s[0] = '"';
int i = 1;
int j;
while('"' == global_token->s[0])
{
/* Step past the leading '"' */
j = 1;
/* Copy the rest of the string as is */
while(0 != global_token->s[j])
{
require(i < MAX_STRING, "concat string exceeded max string length\n");
s[i] = global_token->s[j];
i = i + 1;
j = j + 1;
}
/* Move on to the next token */
global_token = global_token->next;
require(NULL != global_token, "multi-string null is not valid C\n");
}
/* Now use it */
strings_list = emit(parse_string(s), strings_list);
}
}
void primary_expr_char()
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("LOADI R0 ");
else if(X86 == Architecture) emit_out("mov_eax, %");
else if(AMD64 == Architecture) emit_out("mov_rax, %");
else if(ARMV7L == Architecture) emit_out("!");
else if(AARCH64 == Architecture) emit_out("LOAD_W0_AHEAD\nSKIP_32_DATA\n%");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RD_A0 !");
emit_out(int2str(escape_lookup(global_token->s + 1), 10, TRUE));
if(ARMV7L == Architecture) emit_out(" R0 LOADI8_ALWAYS");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out(" ADDI");
emit_out("\n");
global_token = global_token->next;
}
int hex2char(int c)
{
if((c >= 0) && (c <= 9)) return (c + 48);
else if((c >= 10) && (c <= 15)) return (c + 55);
else return -1;
}
char* number_to_hex(int a, int bytes)
{
require(bytes > 0, "number to hex must have a positive number of bytes greater than zero\n");
char* result = calloc(1 + (bytes << 1), sizeof(char));
if(NULL == result)
{
fputs("calloc failed in number_to_hex\n", stderr);
exit(EXIT_FAILURE);
}
int i = 0;
int divisor = (bytes << 3);
require(divisor > 0, "unexpected wrap around in number_to_hex\n");
/* Simply collect numbers until divisor is gone */
while(0 != divisor)
{
divisor = divisor - 4;
result[i] = hex2char((a >> divisor) & 0xF);
i = i + 1;
}
return result;
}
void primary_expr_number()
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
{
int size = strtoint(global_token->s);
if((32767 > size) && (size > -32768))
{
emit_out("LOADI R0 ");
emit_out(global_token->s);
}
else
{
emit_out("LOADR R0 4\nJUMP 4\n'");
emit_out(number_to_hex(size, register_size));
emit_out("'");
}
}
else if(X86 == Architecture)
{
emit_out("mov_eax, %");
emit_out(global_token->s);
}
else if(AMD64 == Architecture)
{
emit_out("mov_rax, %");
emit_out(global_token->s);
}
else if(ARMV7L == Architecture)
{
emit_out("!0 R0 LOAD32 R15 MEMORY\n~0 JUMP_ALWAYS\n%");
emit_out(global_token->s);
}
else if(AARCH64 == Architecture)
{
emit_out("LOAD_W0_AHEAD\nSKIP_32_DATA\n%");
emit_out(global_token->s);
}
else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
{
int size = strtoint(global_token->s);
if((2047 > size) && (size > -2048))
{
emit_out("RD_A0 !");
emit_out(global_token->s);
emit_out(" ADDI");
}
else if (0 == (size >> 30))
{
emit_out("RD_A0 ~");
emit_out(global_token->s);
emit_out(" LUI\n");
emit_out("RD_A0 RS1_A0 !");
emit_out(global_token->s);
emit_out(" ADDI");
}
else
{
int high = size >> 30;
int low = ((size >> 30) << 30) ^ size;
emit_out("RD_A0 ~");
emit_out(int2str(high, 10, TRUE));
emit_out(" LUI\n");
emit_out("RD_A0 RS1_A0 !");
emit_out(int2str(high, 10, TRUE));
emit_out(" ADDI\n");
emit_out("RD_A0 RS1_A0 RS2_X30 SLLI\n");
emit_out("RD_T1 ~");
emit_out(int2str(low, 10, TRUE));
emit_out(" LUI\n");
emit_out("RD_T1 RS1_T1 !");
emit_out(int2str(low, 10, TRUE));
emit_out(" ADDI\n");
emit_out("RD_A0 RS1_A0 RS2_T1 OR\n");
}
}
emit_out("\n");
global_token = global_token->next;
}
void primary_expr_variable()
{
int num_dereference = 0;
while(global_token->s[0] == '*') {
global_token = global_token->next;
require(NULL != global_token, "Walked off the end of a variable dereference\n");
num_dereference = num_dereference + 1;
}
char* s = global_token->s;
global_token = global_token->next;
struct token_list* a = sym_lookup(s, global_constant_list);
if(NULL != a)
{
constant_load(a);
return;
}
a = sym_lookup(s, function->locals);
if(NULL != a)
{
variable_load(a, num_dereference);
return;
}
a = sym_lookup(s, function->arguments);
if(NULL != a)
{
variable_load(a, num_dereference);
return;
}
a = sym_lookup(s, global_function_list);
if(NULL != a)
{
function_load(a);
return;
}
a = sym_lookup(s, global_symbol_list);
if(NULL != a)
{
global_load(a);
return;
}
line_error();
fputs(s ,stderr);
fputs(" is not a defined symbol\n", stderr);
exit(EXIT_FAILURE);
}
void primary_expr();
struct type* promote_type(struct type* a, struct type* b)
{
require(NULL != b, "impossible case 1 in promote_type\n");
require(NULL != a, "impossible case 2 in promote_type\n");
if(a == b) return a;
struct type* i;
for(i = global_types; NULL != i; i = i->next)
{
if(a->name == i->name) break;
if(b->name == i->name) break;
if(a->name == i->indirect->name) break;
if(b->name == i->indirect->name) break;
if(a->name == i->indirect->indirect->name) break;
if(b->name == i->indirect->indirect->name) break;
}
require(NULL != i, "impossible case 3 in promote_type\n");
return i;
}
void common_recursion(FUNCTION f)
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("PUSHR R0 R15\t#_common_recursion\n");
else if(X86 == Architecture) emit_out("push_eax\t#_common_recursion\n");
else if(AMD64 == Architecture) emit_out("push_rax\t#_common_recursion\n");
else if(ARMV7L == Architecture) emit_out("{R0} PUSH_ALWAYS\t#_common_recursion\n");
else if(AARCH64 == Architecture) emit_out("PUSH_X0\t#_common_recursion\n");
else if(RISCV32 == Architecture) emit_out("RD_SP RS1_SP !-4 ADDI\t# _common_recursion\nRS1_SP RS2_A0 SW\n");
else if(RISCV64 == Architecture) emit_out("RD_SP RS1_SP !-8 ADDI\t# _common_recursion\nRS1_SP RS2_A0 SD\n");
struct type* last_type = current_target;
global_token = global_token->next;
require(NULL != global_token, "Received EOF in common_recursion\n");
f();
current_target = promote_type(current_target, last_type);
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("POPR R1 R15\t# _common_recursion\n");
else if(X86 == Architecture) emit_out("pop_ebx\t# _common_recursion\n");
else if(AMD64 == Architecture) emit_out("pop_rbx\t# _common_recursion\n");
else if(ARMV7L == Architecture) emit_out("{R1} POP_ALWAYS\t# _common_recursion\n");
else if(AARCH64 == Architecture) emit_out("POP_X1\t# _common_recursion\n");
else if(RISCV32 == Architecture) emit_out("RD_A1 RS1_SP LW\nRD_SP RS1_SP !4 ADDI\t# _common_recursion\n");
else if(RISCV64 == Architecture) emit_out("RD_A1 RS1_SP LD\nRD_SP RS1_SP !8 ADDI\t# _common_recursion\n");
}
void general_recursion(FUNCTION f, char* s, char* name, FUNCTION iterate)
{
require(NULL != global_token, "Received EOF in general_recursion\n");
if(match(name, global_token->s))
{
common_recursion(f);
emit_out(s);
iterate();
}
}
void arithmetic_recursion(FUNCTION f, char* s1, char* s2, char* name, FUNCTION iterate)
{
require(NULL != global_token, "Received EOF in arithmetic_recursion\n");
if(match(name, global_token->s))
{
common_recursion(f);
if(NULL == current_target)
{
emit_out(s1);
}
else if(current_target->is_signed)
{
emit_out(s1);
}
else
{
emit_out(s2);
}
iterate();
}
}
/*
* postfix-expr:
* primary-expr
* postfix-expr [ expression ]
* postfix-expr ( expression-list-opt )
* postfix-expr -> member
* postfix-expr . member
*/
struct type* lookup_member(struct type* parent, char* name);
void postfix_expr_arrow()
{
emit_out("# looking up offset\n");
global_token = global_token->next;
require(NULL != global_token, "naked -> not allowed\n");
struct type* i = lookup_member(current_target, global_token->s);
current_target = i->type;
global_token = global_token->next;
require(NULL != global_token, "Unterminated -> expression not allowed\n");
if(0 != i->offset)
{
emit_out("# -> offset calculation\n");
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
{
emit_out("ADDUI R0 R0 ");
emit_out(int2str(i->offset, 10, TRUE));
emit_out("\n");
}
else if(X86 == Architecture)
{
emit_out("mov_ebx, %");
emit_out(int2str(i->offset, 10, TRUE));
emit_out("\nadd_eax,ebx\n");
}
else if(AMD64 == Architecture)
{
emit_out("mov_rbx, %");
emit_out(int2str(i->offset, 10, TRUE));
emit_out("\nadd_rax,rbx\n");
}
else if(ARMV7L == Architecture)
{
emit_out("!0 R1 LOAD32 R15 MEMORY\n~0 JUMP_ALWAYS\n%");
emit_out(int2str(i->offset, 10, TRUE));
emit_out("\n'0' R0 R0 ADD R1 ARITH2_ALWAYS\n");
}
else if(AARCH64 == Architecture)
{
emit_out("LOAD_W1_AHEAD\nSKIP_32_DATA\n%");
emit_out(int2str(i->offset, 10, TRUE));
emit_out("\nADD_X0_X1_X0\n");
}
else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
{
emit_out("RD_A1 !");
emit_out(int2str(i->offset, 10, TRUE));
emit_out(" ADDI\n");
emit_out("RD_A0 RS1_A1 RS2_A0 ADD\n");
}
}
/* We don't yet support assigning structs to structs */
if((!match("=", global_token->s) && !is_compound_assignment(global_token->s) && (register_size >= i->size)))
{
emit_out(load_value(i->size, i->is_signed));
}
}
void postfix_expr_dot()
{
maybe_bootstrap_error("Member access using .");
emit_out("# looking up offset\n");
global_token = global_token->next;
require(NULL != global_token, "naked . not allowed\n");
struct type* i = lookup_member(current_target, global_token->s);
current_target = i->type;
global_token = global_token->next;
require(NULL != global_token, "Unterminated . expression not allowed\n");
if(0 != i->offset)
{
emit_out("# . offset calculation\n");
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
{
emit_out("ADDUI R0 R0 ");
emit_out(int2str(i->offset, 10, TRUE));
emit_out("\n");
}
else if(X86 == Architecture)
{
emit_out("mov_ebx, %");
emit_out(int2str(i->offset, 10, TRUE));
emit_out("\nadd_eax,ebx\n");
}
else if(AMD64 == Architecture)
{
emit_out("mov_rbx, %");
emit_out(int2str(i->offset, 10, TRUE));
emit_out("\nadd_rax,rbx\n");
}
else if(ARMV7L == Architecture)
{
emit_out("!0 R1 LOAD32 R15 MEMORY\n~0 JUMP_ALWAYS\n%");
emit_out(int2str(i->offset, 10, TRUE));
emit_out("\n'0' R0 R0 ADD R1 ARITH2_ALWAYS\n");
}
else if(AARCH64 == Architecture)
{
emit_out("LOAD_W1_AHEAD\nSKIP_32_DATA\n%");
emit_out(int2str(i->offset, 10, TRUE));
emit_out("\nADD_X0_X1_X0\n");
}
else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
{
emit_out("RD_A1 !");
emit_out(int2str(i->offset, 10, TRUE));
emit_out(" ADDI\n");
emit_out("RD_A0 RS1_A1 RS2_A0 ADD\n");
}
}
if(match("=", global_token->s) || is_compound_assignment(global_token->s)) return;
if(match("[", global_token->s)) return;
emit_out(load_value(current_target->size, current_target->is_signed));
}
void postfix_expr_array()
{
struct type* array = current_target;
common_recursion(expression);
current_target = array;
require(NULL != current_target, "Arrays only apply to variables\n");
char* assign = load_value(register_size, current_target->is_signed);
/* Add support for Ints */
if(match("char*", current_target->name))
{
assign = load_value(1, TRUE);
}
else
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("PUSHR R1 R15\nLOADI R1 ");
else if(X86 == Architecture) emit_out("push_ebx\nmov_ebx, %");
else if(AMD64 == Architecture) emit_out("push_rbx\nmov_rbx, %");
else if(ARMV7L == Architecture) emit_out("{R1} PUSH_ALWAYS\n!0 R1 LOAD32 R15 MEMORY\n~0 JUMP_ALWAYS\n%");
else if(AARCH64 == Architecture) emit_out("PUSH_X1\nLOAD_W1_AHEAD\nSKIP_32_DATA\n%");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RD_A2 RS1_A1 ADDI\nRD_A1 !");
emit_out(int2str(current_target->type->size, 10, TRUE));
if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out(" ADDI");
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("\nMULU R0 R1 R0\nPOPR R1 R15\n");
else if(X86 == Architecture) emit_out("\nmul_ebx\npop_ebx\n");
else if(AMD64 == Architecture) emit_out("\nmul_rbx\npop_rbx\n");
else if(ARMV7L == Architecture) emit_out("\n'9' R0 '0' R1 MUL R0 ARITH2_ALWAYS\n{R1} POP_ALWAYS\n");
else if(AARCH64 == Architecture) emit_out("\nMUL_X0_X1_X0\nPOP_X1\n");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("\nRD_A0 RS1_A1 RS2_A0 MUL\nRD_A1 RS1_A2 ADDI\n");
}
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("ADD R0 R0 R1\n");
else if(X86 == Architecture) emit_out("add_eax,ebx\n");
else if(AMD64 == Architecture) emit_out("add_rax,rbx\n");
else if(ARMV7L == Architecture) emit_out("'0' R0 R0 ADD R1 ARITH2_ALWAYS\n");
else if(AARCH64 == Architecture) emit_out("ADD_X0_X1_X0\n");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RD_A0 RS1_A1 RS2_A0 ADD\n");
require_match("ERROR in postfix_expr\nMissing ]\n", "]");
require(NULL != global_token, "truncated array expression\n");
if(match("=", global_token->s) || is_compound_assignment(global_token->s) || match(".", global_token->s))
{
assign = "";
}
if(match("[", global_token->s))
{
current_target = current_target->type;
}
emit_out(assign);
}
/*
* unary-expr:
* &postfix-expr
* - postfix-expr
* !postfix-expr
* sizeof ( type )
*/
struct type* type_name();
void unary_expr_sizeof()
{
global_token = global_token->next;
require(NULL != global_token, "Received EOF when starting sizeof\n");
require_match("ERROR in unary_expr\nMissing (\n", "(");
struct type* a = type_name();
require_match("ERROR in unary_expr\nMissing )\n", ")");
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("LOADUI R0 ");
else if(X86 == Architecture) emit_out("mov_eax, %");
else if(AMD64 == Architecture) emit_out("mov_rax, %");
else if(ARMV7L == Architecture) emit_out("!");
else if(AARCH64 == Architecture) emit_out("LOAD_W0_AHEAD\nSKIP_32_DATA\n%");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RD_A0 !");
emit_out(int2str(a->size, 10, TRUE));
if(ARMV7L == Architecture) emit_out(" R0 LOADI8_ALWAYS");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out(" ADDI");
emit_out("\n");
}
void postfix_expr_stub()
{
require(NULL != global_token, "Unexpected EOF, improperly terminated primary expression\n");
if(match("[", global_token->s))
{
postfix_expr_array();
postfix_expr_stub();
}
if(match("->", global_token->s))
{
postfix_expr_arrow();
postfix_expr_stub();
}
if(match(".", global_token->s))
{
postfix_expr_dot();
postfix_expr_stub();
}
}
void postfix_expr()
{
primary_expr();
postfix_expr_stub();
}
/*
* additive-expr:
* postfix-expr
* additive-expr * postfix-expr
* additive-expr / postfix-expr
* additive-expr % postfix-expr
* additive-expr + postfix-expr
* additive-expr - postfix-expr
* additive-expr << postfix-expr
* additive-expr >> postfix-expr
*/
void additive_expr_stub()
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
{
arithmetic_recursion(postfix_expr, "ADD R0 R1 R0\n", "ADDU R0 R1 R0\n", "+", additive_expr_stub);
arithmetic_recursion(postfix_expr, "SUB R0 R1 R0\n", "SUBU R0 R1 R0\n", "-", additive_expr_stub);
arithmetic_recursion(postfix_expr, "MUL R0 R1 R0\n", "MULU R0 R1 R0\n", "*", additive_expr_stub);
arithmetic_recursion(postfix_expr, "DIV R0 R1 R0\n", "DIVU R0 R1 R0\n", "/", additive_expr_stub);
arithmetic_recursion(postfix_expr, "MOD R0 R1 R0\n", "MODU R0 R1 R0\n", "%", additive_expr_stub);
arithmetic_recursion(postfix_expr, "SAL R0 R1 R0\n", "SL0 R0 R1 R0\n", "<<", additive_expr_stub);
arithmetic_recursion(postfix_expr, "SAR R0 R1 R0\n", "SR0 R0 R1 R0\n", ">>", additive_expr_stub);
}
else if(X86 == Architecture)
{
arithmetic_recursion(postfix_expr, "add_eax,ebx\n", "add_eax,ebx\n", "+", additive_expr_stub);
arithmetic_recursion(postfix_expr, "sub_ebx,eax\nmov_eax,ebx\n", "sub_ebx,eax\nmov_eax,ebx\n", "-", additive_expr_stub);
arithmetic_recursion(postfix_expr, "imul_ebx\n", "mul_ebx\n", "*", additive_expr_stub);
arithmetic_recursion(postfix_expr, "xchg_ebx,eax\ncdq\nidiv_ebx\n", "xchg_ebx,eax\nmov_edx, %0\ndiv_ebx\n", "/", additive_expr_stub);
arithmetic_recursion(postfix_expr, "xchg_ebx,eax\ncdq\nidiv_ebx\nmov_eax,edx\n", "xchg_ebx,eax\nmov_edx, %0\ndiv_ebx\nmov_eax,edx\n", "%", additive_expr_stub);
arithmetic_recursion(postfix_expr, "mov_ecx,eax\nmov_eax,ebx\nsal_eax,cl\n", "mov_ecx,eax\nmov_eax,ebx\nshl_eax,cl\n", "<<", additive_expr_stub);
arithmetic_recursion(postfix_expr, "mov_ecx,eax\nmov_eax,ebx\nsar_eax,cl\n", "mov_ecx,eax\nmov_eax,ebx\nshr_eax,cl\n", ">>", additive_expr_stub);
}
else if(AMD64 == Architecture)
{
arithmetic_recursion(postfix_expr, "add_rax,rbx\n", "add_rax,rbx\n", "+", additive_expr_stub);
arithmetic_recursion(postfix_expr, "sub_rbx,rax\nmov_rax,rbx\n", "sub_rbx,rax\nmov_rax,rbx\n", "-", additive_expr_stub);
arithmetic_recursion(postfix_expr, "imul_rbx\n", "mul_rbx\n", "*", additive_expr_stub);
arithmetic_recursion(postfix_expr, "xchg_rbx,rax\ncqo\nidiv_rbx\n", "xchg_rbx,rax\nmov_rdx, %0\ndiv_rbx\n", "/", additive_expr_stub);
arithmetic_recursion(postfix_expr, "xchg_rbx,rax\ncqo\nidiv_rbx\nmov_rax,rdx\n", "xchg_rbx,rax\nmov_rdx, %0\ndiv_rbx\nmov_rax,rdx\n", "%", additive_expr_stub);
arithmetic_recursion(postfix_expr, "mov_rcx,rax\nmov_rax,rbx\nsal_rax,cl\n", "mov_rcx,rax\nmov_rax,rbx\nshl_rax,cl\n", "<<", additive_expr_stub);
arithmetic_recursion(postfix_expr, "mov_rcx,rax\nmov_rax,rbx\nsar_rax,cl\n", "mov_rcx,rax\nmov_rax,rbx\nshr_rax,cl\n", ">>", additive_expr_stub);
}
else if(ARMV7L == Architecture)
{
arithmetic_recursion(postfix_expr, "'0' R0 R0 ADD R1 ARITH2_ALWAYS\n", "'0' R0 R0 ADD R1 ARITH2_ALWAYS\n", "+", additive_expr_stub);
arithmetic_recursion(postfix_expr, "'0' R0 R0 SUB R1 ARITH2_ALWAYS\n", "'0' R0 R0 SUB R1 ARITH2_ALWAYS\n", "-", additive_expr_stub);
arithmetic_recursion(postfix_expr, "'9' R0 '0' R1 MULS R0 ARITH2_ALWAYS\n", "'9' R0 '0' R1 MUL R0 ARITH2_ALWAYS\n", "*", additive_expr_stub);
arithmetic_recursion(postfix_expr, "{LR} PUSH_ALWAYS\n^~divides CALL_ALWAYS\n{LR} POP_ALWAYS\n", "{LR} PUSH_ALWAYS\n^~divide CALL_ALWAYS\n{LR} POP_ALWAYS\n", "/", additive_expr_stub);
arithmetic_recursion(postfix_expr, "{LR} PUSH_ALWAYS\n^~moduluss CALL_ALWAYS\n{LR} POP_ALWAYS\n", "{LR} PUSH_ALWAYS\n^~modulus CALL_ALWAYS\n{LR} POP_ALWAYS\n", "%", additive_expr_stub);
arithmetic_recursion(postfix_expr, "LEFT R1 R0 R0 SHIFT AUX_ALWAYS\n", "LEFT R1 R0 R0 SHIFT AUX_ALWAYS\n", "<<", additive_expr_stub);
arithmetic_recursion(postfix_expr, "ARITH_RIGHT R1 R0 R0 SHIFT AUX_ALWAYS\n", "RIGHT R1 R0 R0 SHIFT AUX_ALWAYS\n", ">>", additive_expr_stub);
}
else if(AARCH64 == Architecture)
{
general_recursion(postfix_expr, "ADD_X0_X1_X0\n", "+", additive_expr_stub);
general_recursion(postfix_expr, "SUB_X0_X1_X0\n", "-", additive_expr_stub);
general_recursion(postfix_expr, "MUL_X0_X1_X0\n", "*", additive_expr_stub);
arithmetic_recursion(postfix_expr, "SDIV_X0_X1_X0\n", "UDIV_X0_X1_X0\n", "/", additive_expr_stub);
arithmetic_recursion(postfix_expr, "SDIV_X2_X1_X0\nMSUB_X0_X0_X2_X1\n", "UDIV_X2_X1_X0\nMSUB_X0_X0_X2_X1\n", "%", additive_expr_stub);
general_recursion(postfix_expr, "LSHIFT_X0_X1_X0\n", "<<", additive_expr_stub);
arithmetic_recursion(postfix_expr, "ARITH_RSHIFT_X0_X1_X0\n", "LOGICAL_RSHIFT_X0_X1_X0\n", ">>", additive_expr_stub);
}
else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
{
general_recursion(postfix_expr, "RD_A0 RS1_A1 RS2_A0 ADD\n", "+", additive_expr_stub);
general_recursion(postfix_expr, "RD_A0 RS1_A1 RS2_A0 SUB\n", "-", additive_expr_stub);
general_recursion(postfix_expr, "RD_A0 RS1_A1 RS2_A0 MUL\n", "*", additive_expr_stub);
arithmetic_recursion(postfix_expr, "RD_A0 RS1_A1 RS2_A0 DIV\n", "RD_A0 RS1_A1 RS2_A0 DIVU\n", "/", additive_expr_stub);
arithmetic_recursion(postfix_expr, "RD_A0 RS1_A1 RS2_A0 REM\n", "RD_A0 RS1_A1 RS2_A0 REMU\n", "%", additive_expr_stub);
general_recursion(postfix_expr, "RD_A0 RS1_A1 RS2_A0 SLL\n", "<<", additive_expr_stub);
arithmetic_recursion(postfix_expr, "RD_A0 RS1_A1 RS2_A0 SRA\n", "RD_A0 RS1_A1 RS2_A0 SRL\n", ">>", additive_expr_stub);
}
}
void additive_expr()
{
postfix_expr();
additive_expr_stub();
}
/*
* relational-expr:
* additive_expr
* relational-expr < additive_expr
* relational-expr <= additive_expr
* relational-expr >= additive_expr
* relational-expr > additive_expr
*/
void relational_expr_stub()
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
{
arithmetic_recursion(additive_expr, "CMP R0 R1 R0\nSET.L R0 R0 1\n", "CMPU R0 R1 R0\nSET.L R0 R0 1\n", "<", relational_expr_stub);
arithmetic_recursion(additive_expr, "CMP R0 R1 R0\nSET.LE R0 R0 1\n", "CMPU R0 R1 R0\nSET.LE R0 R0 1\n", "<=", relational_expr_stub);
arithmetic_recursion(additive_expr, "CMP R0 R1 R0\nSET.GE R0 R0 1\n", "CMPU R0 R1 R0\nSET.GE R0 R0 1\n", ">=", relational_expr_stub);
arithmetic_recursion(additive_expr, "CMP R0 R1 R0\nSET.G R0 R0 1\n", "CMPU R0 R1 R0\nSET.G R0 R0 1\n", ">", relational_expr_stub);
arithmetic_recursion(additive_expr, "CMP R0 R1 R0\nSET.E R0 R0 1\n", "CMPU R0 R1 R0\nSET.E R0 R0 1\n", "==", relational_expr_stub);
arithmetic_recursion(additive_expr, "CMP R0 R1 R0\nSET.NE R0 R0 1\n", "CMPU R0 R1 R0\nSET.NE R0 R0 1\n", "!=", relational_expr_stub);
}
else if(X86 == Architecture)
{
arithmetic_recursion(additive_expr, "cmp\nsetl_al\nmovzx_eax,al\n", "cmp\nsetb_al\nmovzx_eax,al\n", "<", relational_expr_stub);
arithmetic_recursion(additive_expr, "cmp\nsetle_al\nmovzx_eax,al\n", "cmp\nsetbe_al\nmovzx_eax,al\n", "<=", relational_expr_stub);
arithmetic_recursion(additive_expr, "cmp\nsetge_al\nmovzx_eax,al\n", "cmp\nsetae_al\nmovzx_eax,al\n", ">=", relational_expr_stub);
arithmetic_recursion(additive_expr, "cmp\nsetg_al\nmovzx_eax,al\n", "cmp\nseta_al\nmovzx_eax,al\n", ">", relational_expr_stub);
general_recursion(additive_expr, "cmp\nsete_al\nmovzx_eax,al\n", "==", relational_expr_stub);
general_recursion(additive_expr, "cmp\nsetne_al\nmovzx_eax,al\n", "!=", relational_expr_stub);
}
else if(AMD64 == Architecture)
{
arithmetic_recursion(additive_expr, "cmp_rbx,rax\nsetl_al\nmovzx_rax,al\n", "cmp_rbx,rax\nsetb_al\nmovzx_rax,al\n", "<", relational_expr_stub);
arithmetic_recursion(additive_expr, "cmp_rbx,rax\nsetle_al\nmovzx_rax,al\n", "cmp_rbx,rax\nsetbe_al\nmovzx_rax,al\n", "<=", relational_expr_stub);
arithmetic_recursion(additive_expr, "cmp_rbx,rax\nsetge_al\nmovzx_rax,al\n", "cmp_rbx,rax\nsetae_al\nmovzx_rax,al\n", ">=", relational_expr_stub);
arithmetic_recursion(additive_expr, "cmp_rbx,rax\nsetg_al\nmovzx_rax,al\n", "cmp_rbx,rax\nseta_al\nmovzx_rax,al\n", ">", relational_expr_stub);
general_recursion(additive_expr, "cmp_rbx,rax\nsete_al\nmovzx_rax,al\n", "==", relational_expr_stub);
general_recursion(additive_expr, "cmp_rbx,rax\nsetne_al\nmovzx_rax,al\n", "!=", relational_expr_stub);
}
else if(ARMV7L == Architecture)
{
arithmetic_recursion(additive_expr, "'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_L\n", "'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_LO\n", "<", relational_expr_stub);
arithmetic_recursion(additive_expr, "'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_LE\n", "'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_LS\n", "<=", relational_expr_stub);
arithmetic_recursion(additive_expr, "'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_GE\n", "'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_HS\n", ">=", relational_expr_stub);
arithmetic_recursion(additive_expr, "'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_G\n", "'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_HI\n", ">", relational_expr_stub);
general_recursion(additive_expr, "'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_EQUAL\n", "==", relational_expr_stub);
general_recursion(additive_expr, "'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_NE\n", "!=", relational_expr_stub);
}
else if(AARCH64 == Architecture)
{
arithmetic_recursion(additive_expr, "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_LT\nSET_X0_TO_0\n", "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_LO\nSET_X0_TO_0\n", "<", relational_expr_stub);
arithmetic_recursion(additive_expr, "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_LE\nSET_X0_TO_0\n", "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_LS\nSET_X0_TO_0\n", "<=", relational_expr_stub);
arithmetic_recursion(additive_expr, "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_GE\nSET_X0_TO_0\n", "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_HS\nSET_X0_TO_0\n", ">=", relational_expr_stub);
arithmetic_recursion(additive_expr, "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_GT\nSET_X0_TO_0\n", "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_HI\nSET_X0_TO_0\n", ">", relational_expr_stub);
general_recursion(additive_expr, "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_EQ\nSET_X0_TO_0\n", "==", relational_expr_stub);
general_recursion(additive_expr, "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_NE\nSET_X0_TO_0\n", "!=", relational_expr_stub);
}
else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
{
arithmetic_recursion(additive_expr, "RD_A0 RS1_A1 RS2_A0 SLT\n", "RD_A0 RS1_A1 RS2_A0 SLTU\n", "<", relational_expr_stub);
arithmetic_recursion(additive_expr, "RD_A0 RS1_A0 RS2_A1 SLT\nRD_A0 RS1_A0 !1 XORI\n", "RD_A0 RS1_A0 RS2_A1 SLTU\nRD_A0 RS1_A0 !1 XORI\n", "<=", relational_expr_stub);
arithmetic_recursion(additive_expr, "RD_A0 RS1_A1 RS2_A0 SLT\nRD_A0 RS1_A0 !1 XORI\n", "RD_A0 RS1_A1 RS2_A0 SLTU\nRD_A0 RS1_A0 !1 XORI\n", ">=", relational_expr_stub);
arithmetic_recursion(additive_expr, "RD_A0 RS1_A0 RS2_A1 SLT\n", "RD_A0 RS1_A0 RS2_A1 SLTU\n", ">", relational_expr_stub);
general_recursion(additive_expr, "RD_A0 RS1_A0 RS2_A1 SUB\nRD_A0 RS1_A0 !1 SLTIU\n", "==", relational_expr_stub);
general_recursion(additive_expr, "RD_A0 RS1_A0 RS2_A1 SUB\nRD_A0 RS2_A0 SLTU\n", "!=", relational_expr_stub);
}
}
void relational_expr()
{
additive_expr();
relational_expr_stub();
}
/*
* bitwise-expr:
* relational-expr
* bitwise-expr & bitwise-expr
* bitwise-expr && bitwise-expr
* bitwise-expr | bitwise-expr
* bitwise-expr || bitwise-expr
* bitwise-expr ^ bitwise-expr
*/
void bitwise_expr_stub()
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
{
general_recursion(relational_expr, "AND R0 R0 R1\n", "&", bitwise_expr_stub);
general_recursion(relational_expr, "AND R0 R0 R1\n", "&&", bitwise_expr_stub);
general_recursion(relational_expr, "OR R0 R0 R1\n", "|", bitwise_expr_stub);
general_recursion(relational_expr, "OR R0 R0 R1\n", "||", bitwise_expr_stub);
general_recursion(relational_expr, "XOR R0 R0 R1\n", "^", bitwise_expr_stub);
}
else if(X86 == Architecture)
{
general_recursion(relational_expr, "and_eax,ebx\n", "&", bitwise_expr_stub);
general_recursion(relational_expr, "and_eax,ebx\n", "&&", bitwise_expr_stub);
general_recursion(relational_expr, "or_eax,ebx\n", "|", bitwise_expr_stub);
general_recursion(relational_expr, "or_eax,ebx\n", "||", bitwise_expr_stub);
general_recursion(relational_expr, "xor_eax,ebx\n", "^", bitwise_expr_stub);
}
else if(AMD64 == Architecture)
{
general_recursion(relational_expr, "and_rax,rbx\n", "&", bitwise_expr_stub);
general_recursion(relational_expr, "and_rax,rbx\n", "&&", bitwise_expr_stub);
general_recursion(relational_expr, "or_rax,rbx\n", "|", bitwise_expr_stub);
general_recursion(relational_expr, "or_rax,rbx\n", "||", bitwise_expr_stub);
general_recursion(relational_expr, "xor_rax,rbx\n", "^", bitwise_expr_stub);
}
else if(ARMV7L == Architecture)
{
general_recursion(relational_expr, "NO_SHIFT R0 R0 AND R1 ARITH2_ALWAYS\n", "&", bitwise_expr_stub);
general_recursion(relational_expr, "NO_SHIFT R0 R0 AND R1 ARITH2_ALWAYS\n", "&&", bitwise_expr_stub);
general_recursion(relational_expr, "NO_SHIFT R0 R0 OR R1 AUX_ALWAYS\n", "|", bitwise_expr_stub);
general_recursion(relational_expr, "NO_SHIFT R0 R0 OR R1 AUX_ALWAYS\n", "||", bitwise_expr_stub);
general_recursion(relational_expr, "'0' R0 R0 XOR R1 ARITH2_ALWAYS\n", "^", bitwise_expr_stub);
}
else if(AARCH64 == Architecture)
{
general_recursion(relational_expr, "AND_X0_X1_X0\n", "&", bitwise_expr_stub);
general_recursion(relational_expr, "AND_X0_X1_X0\n", "&&", bitwise_expr_stub);
general_recursion(relational_expr, "OR_X0_X1_X0\n", "|", bitwise_expr_stub);
general_recursion(relational_expr, "OR_X0_X1_X0\n", "||", bitwise_expr_stub);
general_recursion(relational_expr, "XOR_X0_X1_X0\n", "^", bitwise_expr_stub);
}
else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
{
general_recursion(relational_expr, "RD_A0 RS1_A1 RS2_A0 AND\n", "&", bitwise_expr_stub);
general_recursion(relational_expr, "RD_A0 RS1_A1 RS2_A0 AND\n", "&&", bitwise_expr_stub);
general_recursion(relational_expr, "RD_A0 RS1_A1 RS2_A0 OR\n", "|", bitwise_expr_stub);
general_recursion(relational_expr, "RD_A0 RS1_A1 RS2_A0 OR\n", "||", bitwise_expr_stub);
general_recursion(relational_expr, "RD_A0 RS1_A1 RS2_A0 XOR\n", "^", bitwise_expr_stub);
}
}
void bitwise_expr()
{
relational_expr();
bitwise_expr_stub();
}
/*
* expression:
* bitwise-or-expr
* bitwise-or-expr = expression
*/
void primary_expr()
{
require(NULL != global_token, "Received EOF where primary expression expected\n");
if(match("&", global_token->s))
{
Address_of = TRUE;
global_token = global_token->next;
require(NULL != global_token, "Received EOF after & where primary expression expected\n");
}
else
{
Address_of = FALSE;
}
if(match("sizeof", global_token->s)) unary_expr_sizeof();
else if('-' == global_token->s[0])
{
if(X86 == Architecture) emit_out("mov_eax, %0\n");
else if(AMD64 == Architecture) emit_out("mov_rax, %0\n");
else if(ARMV7L == Architecture) emit_out("!0 R0 LOADI8_ALWAYS\n");
else if(AARCH64 == Architecture) emit_out("SET_X0_TO_0\n");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RD_A0 MV\n");
common_recursion(primary_expr);
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("NEG R0 R0\n");
else if(X86 == Architecture) emit_out("sub_ebx,eax\nmov_eax,ebx\n");
else if(AMD64 == Architecture) emit_out("sub_rbx,rax\nmov_rax,rbx\n");
else if(ARMV7L == Architecture) emit_out("'0' R0 R0 SUB R1 ARITH2_ALWAYS\n");
else if(AARCH64 == Architecture) emit_out("SUB_X0_X1_X0\n");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RD_A0 RS1_A1 RS2_A0 SUB\n");
}
else if('!' == global_token->s[0])
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("LOADI R0 1\n");
else if(X86 == Architecture) emit_out("mov_eax, %1\n");
else if(AMD64 == Architecture) emit_out("mov_rax, %1\n");
else if(ARMV7L == Architecture) emit_out("!1 R0 LOADI8_ALWAYS\n");
else if(AARCH64 == Architecture) emit_out("SET_X0_TO_1\n");
common_recursion(postfix_expr);
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("CMPU R0 R1 R0\nSET.G R0 R0 1\n");
else if(X86 == Architecture) emit_out("cmp\nseta_al\nmovzx_eax,al\n");
else if(AMD64 == Architecture) emit_out("cmp_rbx,rax\nseta_al\nmovzx_rax,al\n");
else if(ARMV7L == Architecture) emit_out("'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_HI\n");
else if(AARCH64 == Architecture) emit_out("CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_HI\nSET_X0_TO_0\n");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RD_A0 RS1_A0 !1 SLTIU\n");
}
else if('~' == global_token->s[0])
{
common_recursion(postfix_expr);
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("NOT R0 R0\n");
else if(X86 == Architecture) emit_out("not_eax\n");
else if(AMD64 == Architecture) emit_out("not_rax\n");
else if(ARMV7L == Architecture) emit_out("'0' R0 R0 MVN_ALWAYS\n");
else if(AARCH64 == Architecture) emit_out("MVN_X0\n");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RD_A0 RS1_A0 NOT\n");
}
else if(global_token->s[0] == '(')
{
global_token = global_token->next;
expression();
require_match("Error in Primary expression\nDidn't get )\n", ")");
}
else if(global_token->s[0] == '\'') primary_expr_char();
else if(global_token->s[0] == '"') primary_expr_string();
else if(in_set(global_token->s[0], "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_")) primary_expr_variable();
else if(global_token->s[0] == '*') primary_expr_variable();
else if(in_set(global_token->s[0], "0123456789")) primary_expr_number();
else primary_expr_failure();
}
char* compound_operation(char* operator, int is_signed)
{
char* operation = "";
if(match("+=", operator))
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
{
if(is_signed) operation = "ADD R0 R1 R0\n";
else operation = "ADDU R0 R1 R0\n";
}
else if(X86 == Architecture) operation = "add_eax,ebx\n";
else if(AMD64 == Architecture) operation = "add_rax,rbx\n";
else if(ARMV7L == Architecture) operation = "'0' R0 R0 ADD R1 ARITH2_ALWAYS\n";
else if(AARCH64 == Architecture) operation = "ADD_X0_X1_X0\n";
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) operation = "RD_A0 RS1_A1 RS2_A0 ADD\n";
}
else if(match("-=", operator))
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
{
if(is_signed) operation = "SUB R0 R1 R0\n";
else operation = "SUBU R0 R1 R0\n";
}
else if(X86 == Architecture) operation = "sub_ebx,eax\nmov_eax,ebx\n";
else if(AMD64 == Architecture) operation = "sub_rbx,rax\nmov_rax,rbx\n";
else if(ARMV7L == Architecture) operation = "'0' R0 R0 SUB R1 ARITH2_ALWAYS\n";
else if(AARCH64 == Architecture) operation = "SUB_X0_X1_X0\n";
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) operation = "RD_A0 RS1_A1 RS2_A0 SUB\n";
}
else if(match("*=", operator))
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
{
if(is_signed) operation = "MUL R0 R1 R0\n";
else operation = "MULU R0 R1 R0\n";
}
else if(X86 == Architecture)
{
if(is_signed) operation = "imul_ebx\n";
else operation = "mul_ebx\n";
}
else if(AMD64 == Architecture)
{
if(is_signed) operation = "imul_rbx\n";
else operation = "mul_rbx\n";
}
else if(ARMV7L == Architecture) operation = "'9' R0 '0' R1 MULS R0 ARITH2_ALWAYS\n";
else if(AARCH64 == Architecture) operation = "MUL_X0_X1_X0\n";
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) operation = "RD_A0 RS1_A1 RS2_A0 MUL\n";
}
else if(match("/=", operator))
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
{
if(is_signed) operation = "DIV R0 R1 R0\n";
else operation = "DIVU R0 R1 R0\n";
}
else if(X86 == Architecture)
{
if (is_signed) operation = "xchg_ebx,eax\ncdq\nidiv_ebx\n";
else operation = "xchg_ebx,eax\nmov_edx, %0\ndiv_ebx\n";
}
else if(AMD64 == Architecture)
{
if(is_signed) operation = "xchg_rbx,rax\ncqo\nidiv_rbx\n";
else operation = "xchg_rbx,rax\nmov_rdx, %0\ndiv_rbx\n";
}
else if(ARMV7L == Architecture)
{
if(is_signed) operation = "{LR} PUSH_ALWAYS\n^~divides CALL_ALWAYS\n{LR} POP_ALWAYS\n";
else operation = "{LR} PUSH_ALWAYS\n^~divide CALL_ALWAYS\n{LR} POP_ALWAYS\n";
}
else if(AARCH64 == Architecture)
{
if(is_signed) operation = "SDIV_X0_X1_X0\n";
else operation = "UDIV_X0_X1_X0\n";
}
else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
{
if(is_signed) operation = "RD_A0 RS1_A1 RS2_A0 DIV\n";
else operation = "RD_A0 RS1_A1 RS2_A0 DIVU\n";
}
}
else if(match("%=", operator))
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
{
if(is_signed) operation = "MOD R0 R1 R0\n";
else operation = "MODU R0 R1 R0\n";
}
else if(X86 == Architecture)
{
if(is_signed) operation = "xchg_ebx,eax\ncdq\nidiv_ebx\nmov_eax,edx\n";
else operation = "xchg_ebx,eax\nmov_edx, %0\ndiv_ebx\nmov_eax,edx\n";
}
else if(AMD64 == Architecture)
{
if(is_signed) operation = "xchg_rbx,rax\ncqo\nidiv_rbx\nmov_rax,rdx\n";
else operation = "xchg_rbx,rax\nmov_rdx, %0\ndiv_rbx\nmov_rax,rdx\n";
}
else if(ARMV7L == Architecture)
{
if(is_signed) operation = "{LR} PUSH_ALWAYS\n^~moduluss CALL_ALWAYS\n{LR} POP_ALWAYS\n";
else operation = "{LR} PUSH_ALWAYS\n^~modulus CALL_ALWAYS\n{LR} POP_ALWAYS\n";
}
else if(AARCH64 == Architecture)
{
if(is_signed) operation = "SDIV_X2_X1_X0\nMSUB_X0_X0_X2_X1\n";
else operation = "UDIV_X2_X1_X0\nMSUB_X0_X0_X2_X1\n";
}
else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
{
if(is_signed) operation = "RD_A0 RS1_A1 RS2_A0 REM\n";
else operation = "RD_A0 RS1_A1 RS2_A0 REMU\n";
}
}
else if(match("<<=", operator))
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
{
if(is_signed) operation = "SAL R0 R1 R0\n";
else operation = "SL0 R0 R1 R0\n";
}
else if(X86 == Architecture)
{
if(is_signed) operation = "mov_ecx,eax\nmov_eax,ebx\nsal_eax,cl\n";
else operation = "mov_ecx,eax\nmov_eax,ebx\nshl_eax,cl\n";
}
else if(AMD64 == Architecture)
{
if(is_signed) operation = "mov_rcx,rax\nmov_rax,rbx\nsal_rax,cl\n";
else operation = "mov_rcx,rax\nmov_rax,rbx\nshl_rax,cl\n";
}
else if(ARMV7L == Architecture) operation = "LEFT R1 R0 R0 SHIFT AUX_ALWAYS\n";
else if(AARCH64 == Architecture) operation = "LSHIFT_X0_X1_X0\n";
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) operation = "RD_A0 RS1_A1 RS2_A0 SLL\n";
}
else if(match(">>=", operator))
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
{
if(is_signed) operation = "SAR R0 R1 R0\n";
else operation = "SR0 R0 R1 R0\n";
}
else if(X86 == Architecture)
{
if(is_signed) operation = "mov_ecx,eax\nmov_eax,ebx\nsar_eax,cl\n";
else operation = "mov_ecx,eax\nmov_eax,ebx\nshr_eax,cl\n";
}
else if(AMD64 == Architecture)
{
if(is_signed) operation = "mov_rcx,rax\nmov_rax,rbx\nsar_rax,cl\n";
else operation = "mov_rcx,rax\nmov_rax,rbx\nshr_rax,cl\n";
}
else if(ARMV7L == Architecture)
{
if(is_signed) operation = "ARITH_RIGHT R1 R0 R0 SHIFT AUX_ALWAYS\n";
else operation = "RIGHT R1 R0 R0 SHIFT AUX_ALWAYS\n";
}
else if(AARCH64 == Architecture)
{
if(is_signed) operation = "ARITH_RSHIFT_X0_X1_X0\n";
else operation = "LOGICAL_RSHIFT_X0_X1_X0\n";
}
else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
{
if(is_signed) operation = "RD_A0 RS1_A1 RS2_A0 SRA\n";
else operation = "RD_A0 RS1_A1 RS2_A0 SRL\n";
}
}
else if(match("&=", operator))
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) operation = "AND R0 R0 R1\n";
else if(X86 == Architecture) operation = "and_eax,ebx\n";
else if(AMD64 == Architecture) operation = "and_rax,rbx\n";
else if(ARMV7L == Architecture) operation = "NO_SHIFT R0 R0 AND R1 ARITH2_ALWAYS\n";
else if(AARCH64 == Architecture) operation = "AND_X0_X1_X0\n";
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) operation = "RD_A0 RS1_A1 RS2_A0 AND\n";
}
else if(match("^=", operator))
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) operation = "XOR R0 R0 R1\n";
else if(X86 == Architecture) operation = "xor_eax,ebx\n";
else if(AMD64 == Architecture) operation = "xor_rax,rbx\n";
else if(ARMV7L == Architecture) operation = "'0' R0 R0 XOR R1 ARITH2_ALWAYS\n";
else if(AARCH64 == Architecture) operation = "XOR_X0_X1_X0\n";
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) operation = "RD_A0 RS1_A1 RS2_A0 XOR\n";
}
else if(match("|=", operator))
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) operation = "OR R0 R0 R1\n";
else if(X86 == Architecture) operation = "or_eax,ebx\n";
else if(AMD64 == Architecture) operation = "or_rax,rbx\n";
else if(ARMV7L == Architecture) operation = "NO_SHIFT R0 R0 OR R1 AUX_ALWAYS\n";
else if(AARCH64 == Architecture) operation = "OR_X0_X1_X0\n";
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) operation = "RD_A0 RS1_A1 RS2_A0 OR\n";
}
else
{
fputs("Found illegal compound assignment operator: ", stderr);
fputs(operator, stderr);
fputc('\n', stderr);
exit(EXIT_FAILURE);
}
return operation;
}
void expression()
{
bitwise_expr();
if(match("=", global_token->s))
{
char* store = "";
if(match("]", global_token->prev->s))
{
store = store_value(current_target->type->size);
}
else
{
store = store_value(current_target->size);
}
common_recursion(expression);
emit_out(store);
current_target = integer;
}
else if(is_compound_assignment(global_token->s))
{
maybe_bootstrap_error("compound operator");
char* push = "";
char* load = "";
char* operation = "";
char* pop = "";
char* store = "";
struct type* last_type = current_target;
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) push = "PUSHR R1 R15\n";
else if(X86 == Architecture) push = "push_ebx\n";
else if(AMD64 == Architecture) push = "push_rbx\n";
else if(ARMV7L == Architecture) push = "{R1} PUSH_ALWAYS\n";
else if(AARCH64 == Architecture) push = "PUSH_X1\n";
else if(RISCV32 == Architecture) push = "RS1_SP RS2_A1 @-4 SW\n";
else if(RISCV64 == Architecture) push = "RS1_SP RS2_A1 @-8 SD\n";
if(!match("]", global_token->prev->s) || !match("char*", current_target->name))
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) load = "LOAD R1 R1 0\n";
else if(X86 == Architecture) load = "mov_ebx,[ebx]\n";
else if(AMD64 == Architecture) load = "mov_rbx,[rbx]\n";
else if(ARMV7L == Architecture) load = "!0 R1 LOAD32 R1 MEMORY\n";
else if(AARCH64 == Architecture) load = "DEREF_X1\n";
else if(RISCV32 == Architecture) load = "RD_A1 RS1_A1 LW\n";
else if(RISCV64 == Architecture) load = "RD_A1 RS1_A1 LD\n";
}
else
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) load = "LOAD8 R1 R1 0\n";
else if(X86 == Architecture) load = "movsx_ebx,BYTE_PTR_[ebx]\n";
else if(AMD64 == Architecture) load = "movsx_rbx,BYTE_PTR_[rbx]\n";
else if(ARMV7L == Architecture) load = "LOADU8 R1 LOAD R1 MEMORY\n";
else if(AARCH64 == Architecture) load = "DEREF_X1_BYTE\n";
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) load = "RD_A1 RS1_A1 LBU\n";
}
char *operator = global_token->s;
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) pop = "POPR R1 R15\n";
else if(X86 == Architecture) pop = "pop_ebx\n";
else if(AMD64 == Architecture) pop = "pop_rbx\n";
else if(ARMV7L == Architecture) pop = "{R1} POP_ALWAYS\n";
else if(AARCH64 == Architecture) pop = "POP_X1\n";
else if(RISCV32 == Architecture) pop = "RD_A1 RS1_SP !-4 LW\n";
else if(RISCV64 == Architecture) pop = "RD_A1 RS1_SP !-8 LD\n";
if(match("]", global_token->prev->s))
{
store = store_value(current_target->type->size);
}
else
{
store = store_value(current_target->size);
}
common_recursion(expression);
current_target = promote_type(current_target, last_type);
emit_out(push);
emit_out(load);
operation = compound_operation(operator, current_target->is_signed);
emit_out(operation);
emit_out(pop);
emit_out(store);
current_target = integer;
}
}
int iskeywordp(char* s)
{
if(match("auto", s)) return TRUE;
if(match("break", s)) return TRUE;
if(match("case", s)) return TRUE;
if(match("char", s)) return TRUE;
if(match("const", s)) return TRUE;
if(match("continue", s)) return TRUE;
if(match("default", s)) return TRUE;
if(match("do", s)) return TRUE;
if(match("double", s)) return TRUE;
if(match("else", s)) return TRUE;
if(match("enum", s)) return TRUE;
if(match("extern", s)) return TRUE;
if(match("float", s)) return TRUE;
if(match("for", s)) return TRUE;
if(match("goto", s)) return TRUE;
if(match("if", s)) return TRUE;
if(match("int", s)) return TRUE;
if(match("long", s)) return TRUE;
if(match("register", s)) return TRUE;
if(match("return", s)) return TRUE;
if(match("short", s)) return TRUE;
if(match("signed", s)) return TRUE;
if(match("sizeof", s)) return TRUE;
if(match("static", s)) return TRUE;
if(match("struct", s)) return TRUE;
if(match("switch", s)) return TRUE;
if(match("typedef", s)) return TRUE;
if(match("union", s)) return TRUE;
if(match("unsigned", s)) return TRUE;
if(match("void", s)) return TRUE;
if(match("volatile", s)) return TRUE;
if(match("while", s)) return TRUE;
return FALSE;
}
/* Similar to integer division a / b but rounds up */
unsigned ceil_div(unsigned a, unsigned b)
{
return (a + b - 1) / b;
}
/* Process local variable */
void collect_local()
{
if(NULL != break_target_func)
{
fputs("Local variable initialized inside of loop in file: ", stderr);
line_error();
fputs("\nMove the variable outside of the loop to resolve\n", stderr);
fputs("Otherwise the binary will segfault while running\n", stderr);
exit(EXIT_FAILURE);
}
struct type* type_size = type_name();
require(NULL != global_token, "Received EOF while collecting locals\n");
require(!in_set(global_token->s[0], "[{(<=>)}]|&!^%;:'\""), "forbidden character in local variable name\n");
require(!iskeywordp(global_token->s), "You are not allowed to use a keyword as a local variable name\n");
require(NULL != type_size, "Must have non-null type\n");
struct token_list* a = sym_declare(global_token->s, type_size, function->locals);
if(match("main", function->s) && (NULL == function->locals))
{
if(KNIGHT_NATIVE == Architecture) a->depth = register_size;
else if(KNIGHT_POSIX == Architecture) a->depth = 20;
else if(X86 == Architecture) a->depth = -20;
else if(AMD64 == Architecture) a->depth = -40;
else if(ARMV7L == Architecture) a->depth = 16;
else if(AARCH64 == Architecture) a->depth = 32; /* argc, argv, envp and the local (8 bytes each) */
else if(RISCV32 == Architecture) a->depth = -16;
else if(RISCV64 == Architecture) a->depth = -32;
}
else if((NULL == function->arguments) && (NULL == function->locals))
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) a->depth = register_size;
else if(X86 == Architecture) a->depth = -8;
else if(AMD64 == Architecture) a->depth = -16;
else if(ARMV7L == Architecture) a->depth = 8;
else if(AARCH64 == Architecture) a->depth = register_size;
else if(RISCV32 == Architecture) a->depth = -4;
else if(RISCV64 == Architecture) a->depth = -8;
}
else if(NULL == function->locals)
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) a->depth = function->arguments->depth + 8;
else if(X86 == Architecture) a->depth = function->arguments->depth - 8;
else if(AMD64 == Architecture) a->depth = function->arguments->depth - 16;
else if(ARMV7L == Architecture) a->depth = function->arguments->depth + 8;
else if(AARCH64 == Architecture) a->depth = function->arguments->depth + register_size;
else if(RISCV32 == Architecture) a->depth = function->arguments->depth - 4;
else if(RISCV64 == Architecture) a->depth = function->arguments->depth - 8;
}
else
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) a->depth = function->locals->depth + register_size;
else if(X86 == Architecture) a->depth = function->locals->depth - register_size;
else if(AMD64 == Architecture) a->depth = function->locals->depth - register_size;
else if(ARMV7L == Architecture) a->depth = function->locals->depth + register_size;
else if(AARCH64 == Architecture) a->depth = function->locals->depth + register_size;
else if(RISCV32 == Architecture) a->depth = function->locals->depth - register_size;
else if(RISCV64 == Architecture) a->depth = function->locals->depth - register_size;
}
/* Adjust the depth of local structs. When stack grows downwards, we want them to
start at the bottom of allocated space. */
unsigned struct_depth_adjustment = (ceil_div(a->type->size, register_size) - 1) * register_size;
if(KNIGHT_POSIX == Architecture) a->depth = a->depth + struct_depth_adjustment;
else if(KNIGHT_NATIVE == Architecture) a->depth = a->depth + struct_depth_adjustment;
else if(X86 == Architecture) a->depth = a->depth - struct_depth_adjustment;
else if(AMD64 == Architecture) a->depth = a->depth - struct_depth_adjustment;
else if(ARMV7L == Architecture) a->depth = a->depth + struct_depth_adjustment;
else if(AARCH64 == Architecture) a->depth = a->depth + struct_depth_adjustment;
else if(RISCV32 == Architecture) a->depth = a->depth - struct_depth_adjustment;
else if(RISCV64 == Architecture) a->depth = a->depth - struct_depth_adjustment;
function->locals = a;
emit_out("# Defining local ");
emit_out(global_token->s);
emit_out("\n");
global_token = global_token->next;
require(NULL != global_token, "incomplete local missing name\n");
if(match("=", global_token->s))
{
global_token = global_token->next;
require(NULL != global_token, "incomplete local assignment\n");
expression();
}
require_match("ERROR in collect_local\nMissing ;\n", ";");
unsigned i = (a->type->size + register_size - 1) / register_size;
while(i != 0)
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("PUSHR R0 R15\t#");
else if(X86 == Architecture) emit_out("push_eax\t#");
else if(AMD64 == Architecture) emit_out("push_rax\t#");
else if(ARMV7L == Architecture) emit_out("{R0} PUSH_ALWAYS\t#");
else if(AARCH64 == Architecture) emit_out("PUSH_X0\t#");
else if(RISCV32 == Architecture) emit_out("RD_SP RS1_SP !-4 ADDI\nRS1_SP RS2_A0 SW\t#");
else if(RISCV64 == Architecture) emit_out("RD_SP RS1_SP !-8 ADDI\nRS1_SP RS2_A0 SD\t#");
emit_out(a->s);
emit_out("\n");
i = i - 1;
}
}
void statement();
/* Evaluate if statements */
void process_if()
{
char* number_string = int2str(current_count, 10, TRUE);
current_count = current_count + 1;
emit_out("# IF_");
uniqueID_out(function->s, number_string);
global_token = global_token->next;
require_match("ERROR in process_if\nMISSING (\n", "(");
expression();
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP.Z R0 @ELSE_");
else if(X86 == Architecture) emit_out("test_eax,eax\nje %ELSE_");
else if(AMD64 == Architecture) emit_out("test_rax,rax\nje %ELSE_");
else if(ARMV7L == Architecture) emit_out("!0 CMPI8 R0 IMM_ALWAYS\n^~ELSE_");
else if(AARCH64 == Architecture) emit_out("CBNZ_X0_PAST_BR\nLOAD_W16_AHEAD\nSKIP_32_DATA\n&ELSE_");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RS1_A0 @8 BNEZ\n$ELSE_");
uniqueID_out(function->s, number_string);
if(ARMV7L == Architecture) emit_out(" JUMP_EQUAL\n");
else if(AARCH64 == Architecture) emit_out("\nBR_X16\n");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("JAL\n");
require_match("ERROR in process_if\nMISSING )\n", ")");
statement();
require(NULL != global_token, "Reached EOF inside of function\n");
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP @_END_IF_");
else if(X86 == Architecture) emit_out("jmp %_END_IF_");
else if(AMD64 == Architecture) emit_out("jmp %_END_IF_");
else if(ARMV7L == Architecture) emit_out("^~_END_IF_");
else if(AARCH64 == Architecture) emit_out("LOAD_W16_AHEAD\nSKIP_32_DATA\n&_END_IF_");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("$_END_IF_");
uniqueID_out(function->s, number_string);
if(ARMV7L == Architecture) emit_out(" JUMP_ALWAYS\n");
else if(AARCH64 == Architecture) emit_out("\nBR_X16\n");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("JAL\n");
emit_out(":ELSE_");
uniqueID_out(function->s, number_string);
if(match("else", global_token->s))
{
global_token = global_token->next;
require(NULL != global_token, "Received EOF where an else statement expected\n");
statement();
require(NULL != global_token, "Reached EOF inside of function\n");
}
emit_out(":_END_IF_");
uniqueID_out(function->s, number_string);
}
void process_for()
{
struct token_list* nested_locals = break_frame;
char* nested_break_head = break_target_head;
char* nested_break_func = break_target_func;
char* nested_break_num = break_target_num;
char* nested_continue_head = continue_target_head;
char* number_string = int2str(current_count, 10, TRUE);
current_count = current_count + 1;
break_target_head = "FOR_END_";
continue_target_head = "FOR_ITER_";
break_target_num = number_string;
break_frame = function->locals;
break_target_func = function->s;
emit_out("# FOR_initialization_");
uniqueID_out(function->s, number_string);
global_token = global_token->next;
require_match("ERROR in process_for\nMISSING (\n", "(");
if(!match(";",global_token->s))
{
expression();
}
emit_out(":FOR_");
uniqueID_out(function->s, number_string);
require_match("ERROR in process_for\nMISSING ;1\n", ";");
expression();
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP.Z R0 @FOR_END_");
else if(X86 == Architecture) emit_out("test_eax,eax\nje %FOR_END_");
else if(AMD64 == Architecture) emit_out("test_rax,rax\nje %FOR_END_");
else if(ARMV7L == Architecture) emit_out("!0 CMPI8 R0 IMM_ALWAYS\n^~FOR_END_");
else if(AARCH64 == Architecture) emit_out("CBNZ_X0_PAST_BR\nLOAD_W16_AHEAD\nSKIP_32_DATA\n&FOR_END_");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RS1_A0 @8 BNEZ\n$FOR_END_");
uniqueID_out(function->s, number_string);
if(ARMV7L == Architecture) emit_out(" JUMP_EQUAL\n");
else if(AARCH64 == Architecture) emit_out("\nBR_X16\n");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("JAL\n");
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP @FOR_THEN_");
else if(X86 == Architecture) emit_out("jmp %FOR_THEN_");
else if(AMD64 == Architecture) emit_out("jmp %FOR_THEN_");
else if(ARMV7L == Architecture) emit_out("^~FOR_THEN_");
else if(AARCH64 == Architecture) emit_out("LOAD_W16_AHEAD\nSKIP_32_DATA\n&FOR_THEN_");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("$FOR_THEN_");
uniqueID_out(function->s, number_string);
if(ARMV7L == Architecture) emit_out(" JUMP_ALWAYS\n");
else if(AARCH64 == Architecture) emit_out("\nBR_X16\n");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("JAL\n");
emit_out(":FOR_ITER_");
uniqueID_out(function->s, number_string);
require_match("ERROR in process_for\nMISSING ;2\n", ";");
expression();
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP @FOR_");
else if(X86 == Architecture) emit_out("jmp %FOR_");
else if(AMD64 == Architecture) emit_out("jmp %FOR_");
else if(ARMV7L == Architecture) emit_out("^~FOR_");
else if(AARCH64 == Architecture) emit_out("LOAD_W16_AHEAD\nSKIP_32_DATA\n&FOR_");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("$FOR_");
uniqueID_out(function->s, number_string);
if(ARMV7L == Architecture) emit_out(" JUMP_ALWAYS\n");
else if(AARCH64 == Architecture) emit_out("\nBR_X16\n");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("JAL\n");
emit_out(":FOR_THEN_");
uniqueID_out(function->s, number_string);
require_match("ERROR in process_for\nMISSING )\n", ")");
statement();
require(NULL != global_token, "Reached EOF inside of function\n");
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP @FOR_ITER_");
else if(X86 == Architecture) emit_out("jmp %FOR_ITER_");
else if(AMD64 == Architecture) emit_out("jmp %FOR_ITER_");
else if(ARMV7L == Architecture) emit_out("^~FOR_ITER_");
else if(AARCH64 == Architecture) emit_out("LOAD_W16_AHEAD\nSKIP_32_DATA\n&FOR_ITER_");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("$FOR_ITER_");
uniqueID_out(function->s, number_string);
if(ARMV7L == Architecture) emit_out(" JUMP_ALWAYS\n");
else if(AARCH64 == Architecture) emit_out("\nBR_X16\n");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("JAL\n");
emit_out(":FOR_END_");
uniqueID_out(function->s, number_string);
break_target_head = nested_break_head;
break_target_func = nested_break_func;
break_target_num = nested_break_num;
continue_target_head = nested_continue_head;
break_frame = nested_locals;
}
/* Process Assembly statements */
void process_asm()
{
global_token = global_token->next;
require_match("ERROR in process_asm\nMISSING (\n", "(");
while('"' == global_token->s[0])
{
emit_out((global_token->s + 1));
emit_out("\n");
global_token = global_token->next;
require(NULL != global_token, "Received EOF inside asm statement\n");
}
require_match("ERROR in process_asm\nMISSING )\n", ")");
require_match("ERROR in process_asm\nMISSING ;\n", ";");
}
/* Process do while loops */
void process_do()
{
struct token_list* nested_locals = break_frame;
char* nested_break_head = break_target_head;
char* nested_break_func = break_target_func;
char* nested_break_num = break_target_num;
char* nested_continue_head = continue_target_head;
char* number_string = int2str(current_count, 10, TRUE);
current_count = current_count + 1;
break_target_head = "DO_END_";
continue_target_head = "DO_TEST_";
break_target_num = number_string;
break_frame = function->locals;
break_target_func = function->s;
emit_out(":DO_");
uniqueID_out(function->s, number_string);
global_token = global_token->next;
require(NULL != global_token, "Received EOF where do statement is expected\n");
statement();
require(NULL != global_token, "Reached EOF inside of function\n");
emit_out(":DO_TEST_");
uniqueID_out(function->s, number_string);
require_match("ERROR in process_do\nMISSING while\n", "while");
require_match("ERROR in process_do\nMISSING (\n", "(");
expression();
require_match("ERROR in process_do\nMISSING )\n", ")");
require_match("ERROR in process_do\nMISSING ;\n", ";");
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP.NZ R0 @DO_");
else if(X86 == Architecture) emit_out("test_eax,eax\njne %DO_");
else if(AMD64 == Architecture) emit_out("test_rax,rax\njne %DO_");
else if(ARMV7L == Architecture) emit_out("!0 CMPI8 R0 IMM_ALWAYS\n^~DO_");
else if(AARCH64 == Architecture) emit_out("CBZ_X0_PAST_BR\nLOAD_W16_AHEAD\nSKIP_32_DATA\n&DO_");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RS1_A0 @DO_END_");
uniqueID_out(function->s, number_string);
if(ARMV7L == Architecture) emit_out(" JUMP_NE\n");
else if(AARCH64 == Architecture) emit_out("\nBR_X16\n");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
{
emit_out("BEQZ\n$DO_");
uniqueID_out(function->s, number_string);
emit_out("JAL\n");
}
emit_out(":DO_END_");
uniqueID_out(function->s, number_string);
break_frame = nested_locals;
break_target_head = nested_break_head;
break_target_func = nested_break_func;
break_target_num = nested_break_num;
continue_target_head = nested_continue_head;
}
/* Process while loops */
void process_while()
{
struct token_list* nested_locals = break_frame;
char* nested_break_head = break_target_head;
char* nested_break_func = break_target_func;
char* nested_break_num = break_target_num;
char* nested_continue_head = continue_target_head;
char* number_string = int2str(current_count, 10, TRUE);
current_count = current_count + 1;
break_target_head = "END_WHILE_";
continue_target_head = "WHILE_";
break_target_num = number_string;
break_frame = function->locals;
break_target_func = function->s;
emit_out(":WHILE_");
uniqueID_out(function->s, number_string);
global_token = global_token->next;
require_match("ERROR in process_while\nMISSING (\n", "(");
expression();
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP.Z R0 @END_WHILE_");
else if(X86 == Architecture) emit_out("test_eax,eax\nje %END_WHILE_");
else if(AMD64 == Architecture) emit_out("test_rax,rax\nje %END_WHILE_");
else if(ARMV7L == Architecture) emit_out("!0 CMPI8 R0 IMM_ALWAYS\n^~END_WHILE_");
else if(AARCH64 == Architecture) emit_out("CBNZ_X0_PAST_BR\nLOAD_W16_AHEAD\nSKIP_32_DATA\n&END_WHILE_");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RS1_A0 @8 BNEZ\n$END_WHILE_");
uniqueID_out(function->s, number_string);
if(ARMV7L == Architecture) emit_out(" JUMP_EQUAL\t");
else if(AARCH64 == Architecture) emit_out("\nBR_X16\n");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("JAL\n");
emit_out("# THEN_while_");
uniqueID_out(function->s, number_string);
require_match("ERROR in process_while\nMISSING )\n", ")");
statement();
require(NULL != global_token, "Reached EOF inside of function\n");
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP @WHILE_");
else if(X86 == Architecture) emit_out("jmp %WHILE_");
else if(AMD64 == Architecture) emit_out("jmp %WHILE_");
else if(ARMV7L == Architecture) emit_out("^~WHILE_");
else if(AARCH64 == Architecture) emit_out("LOAD_W16_AHEAD\nSKIP_32_DATA\n&WHILE_");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("$WHILE_");
uniqueID_out(function->s, number_string);
if(ARMV7L == Architecture) emit_out(" JUMP_ALWAYS\n");
else if(AARCH64 == Architecture) emit_out("\nBR_X16\n");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("JAL\n");
emit_out(":END_WHILE_");
uniqueID_out(function->s, number_string);
break_target_head = nested_break_head;
break_target_func = nested_break_func;
break_target_num = nested_break_num;
continue_target_head = nested_continue_head;
break_frame = nested_locals;
}
/* Ensure that functions return */
void return_result()
{
global_token = global_token->next;
require(NULL != global_token, "Incomplete return statement received\n");
if(global_token->s[0] != ';') expression();
require_match("ERROR in return_result\nMISSING ;\n", ";");
struct token_list* i;
unsigned size_local_var;
for(i = function->locals; NULL != i; i = i->next)
{
size_local_var = ceil_div(i->type->size, register_size);
while(size_local_var != 0)
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("POPR R1 R15\t# _return_result_locals\n");
else if(X86 == Architecture) emit_out("pop_ebx\t# _return_result_locals\n");
else if(AMD64 == Architecture) emit_out("pop_rbx\t# _return_result_locals\n");
else if(ARMV7L == Architecture) emit_out("{R1} POP_ALWAYS\t# _return_result_locals\n");
else if(AARCH64 == Architecture) emit_out("POP_X1\t# _return_result_locals\n");
else if(RISCV32 == Architecture) emit_out("RD_A1 RS1_SP LW # _return_result_locals\nRD_SP RS1_SP !4 ADDI\n");
else if(RISCV64 == Architecture) emit_out("RD_A1 RS1_SP LD # _return_result_locals\nRD_SP RS1_SP !8 ADDI\n");
size_local_var = size_local_var - 1;
}
}
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("RET R15\n");
else if(X86 == Architecture) emit_out("ret\n");
else if(AMD64 == Architecture) emit_out("ret\n");
else if(ARMV7L == Architecture) emit_out("'1' LR RETURN\n");
else if(AARCH64 == Architecture) emit_out("RETURN\n");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RETURN\n");
}
void process_break()
{
if(NULL == break_target_head)
{
line_error();
fputs("Not inside of a loop or case statement\n", stderr);
exit(EXIT_FAILURE);
}
struct token_list* i = function->locals;
while(i != break_frame)
{
if(NULL == i) break;
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("POPR R1 R15\t# break_cleanup_locals\n");
else if(X86 == Architecture) emit_out("pop_ebx\t# break_cleanup_locals\n");
else if(AMD64 == Architecture) emit_out("pop_rbx\t# break_cleanup_locals\n");
else if(ARMV7L == Architecture) emit_out("{R1} POP_ALWAYS\t# break_cleanup_locals\n");
else if(AARCH64 == Architecture) emit_out("POP_X1\t# break_cleanup_locals\n");
else if(RISCV32 == Architecture) emit_out("RD_A1 RS1_SP LW\t# break_cleanup_locals\nRD_SP RS1_SP !4 ADDI\n");
else if(RISCV64 == Architecture) emit_out("RD_A1 RS1_SP LD\t# break_cleanup_locals\nRD_SP RS1_SP !8 ADDI\n");
i = i->next;
}
global_token = global_token->next;
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP @");
else if(X86 == Architecture) emit_out("jmp %");
else if(AMD64 == Architecture) emit_out("jmp %");
else if(ARMV7L == Architecture) emit_out("^~");
else if(AARCH64 == Architecture) emit_out("LOAD_W16_AHEAD\nSKIP_32_DATA\n&");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("$");
emit_out(break_target_head);
emit_out(break_target_func);
emit_out("_");
emit_out(break_target_num);
if(ARMV7L == Architecture) emit_out(" JUMP_ALWAYS");
else if(AARCH64 == Architecture) emit_out("\nBR_X16");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out(" JAL");
emit_out("\n");
require_match("ERROR in break statement\nMissing ;\n", ";");
}
void process_continue()
{
if(NULL == continue_target_head)
{
line_error();
fputs("Not inside of a loop\n", stderr);
exit(EXIT_FAILURE);
}
global_token = global_token->next;
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP @");
else if(X86 == Architecture) emit_out("jmp %");
else if(AMD64 == Architecture) emit_out("jmp %");
else if(ARMV7L == Architecture) emit_out("^~");
else if(AARCH64 == Architecture) emit_out("LOAD_W16_AHEAD\nSKIP_32_DATA\n&");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("$");
emit_out(continue_target_head);
emit_out(break_target_func);
emit_out("_");
emit_out(break_target_num);
if(ARMV7L == Architecture) emit_out(" JUMP_ALWAYS");
else if(AARCH64 == Architecture) emit_out("\nBR_X16");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out(" JAL");
emit_out("\n");
require_match("ERROR in continue statement\nMissing ;\n", ";");
}
void recursive_statement()
{
global_token = global_token->next;
require(NULL != global_token, "Received EOF in recursive statement\n");
struct token_list* frame = function->locals;
while(!match("}", global_token->s))
{
statement();
require(NULL != global_token, "Received EOF in recursive statement prior to }\n");
}
global_token = global_token->next;
/* Clean up any locals added */
if(((X86 == Architecture) && !match("ret\n", output_list->s)) ||
((AMD64 == Architecture) && !match("ret\n", output_list->s)) ||
(((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) && !match("RET R15\n", output_list->s)) ||
((ARMV7L == Architecture) && !match("'1' LR RETURN\n", output_list->s)) ||
((AARCH64 == Architecture) && !match("RETURN\n", output_list->s)) ||
(((RISCV32 == Architecture) || (RISCV64 == Architecture)) && !match("RETURN\n", output_list->s)))
{
struct token_list* i;
for(i = function->locals; frame != i; i = i->next)
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("POPR R1 R15\t# _recursive_statement_locals\n");
else if(X86 == Architecture) emit_out( "pop_ebx\t# _recursive_statement_locals\n");
else if(AMD64 == Architecture) emit_out("pop_rbx\t# _recursive_statement_locals\n");
else if(ARMV7L == Architecture) emit_out("{R1} POP_ALWAYS\t# _recursive_statement_locals\n");
else if(AARCH64 == Architecture) emit_out("POP_X1\t# _recursive_statement_locals\n");
else if(RISCV32 == Architecture) emit_out("RD_A1 RS1_SP LW\t# _recursive_statement_locals\nRD_SP RS1_SP !4 ADDI\n");
else if(RISCV64 == Architecture) emit_out("RD_A1 RS1_SP LD\t# _recursive_statement_locals\nRD_SP RS1_SP !8 ADDI\n");
}
}
function->locals = frame;
}
/*
* statement:
* { statement-list-opt }
* type-name identifier ;
* type-name identifier = expression;
* if ( expression ) statement
* if ( expression ) statement else statement
* do statement while ( expression ) ;
* while ( expression ) statement
* for ( expression ; expression ; expression ) statement
* asm ( "assembly" ... "assembly" ) ;
* goto label ;
* label:
* return ;
* break ;
* expr ;
*/
struct type* lookup_type(char* s, struct type* start);
void statement()
{
require(NULL != global_token, "expected a C statement but received EOF\n");
/* Always an integer until told otherwise */
current_target = integer;
if(global_token->s[0] == '{')
{
recursive_statement();
}
else if(':' == global_token->s[0])
{
emit_out(global_token->s);
emit_out("\t#C goto label\n");
global_token = global_token->next;
}
else if((NULL != lookup_type(global_token->s, prim_types)) ||
match("struct", global_token->s))
{
collect_local();
}
else if(match("if", global_token->s))
{
process_if();
}
else if(match("do", global_token->s))
{
process_do();
}
else if(match("while", global_token->s))
{
process_while();
}
else if(match("for", global_token->s))
{
process_for();
}
else if(match("asm", global_token->s))
{
process_asm();
}
else if(match("goto", global_token->s))
{
global_token = global_token->next;
require(NULL != global_token, "naked goto is not supported\n");
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP @");
else if(X86 == Architecture) emit_out("jmp %");
else if(AMD64 == Architecture) emit_out("jmp %");
else if(ARMV7L == Architecture) emit_out("^~");
else if(AARCH64 == Architecture) emit_out("LOAD_W16_AHEAD\nSKIP_32_DATA\n&");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("$");
emit_out(global_token->s);
if(ARMV7L == Architecture) emit_out(" JUMP_ALWAYS");
else if(AARCH64 == Architecture) emit_out("\nBR_X16");
else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out(" JAL");
emit_out("\n");
global_token = global_token->next;
require_match("ERROR in statement\nMissing ;\n", ";");
}
else if(match("return", global_token->s))
{
return_result();
}
else if(match("break", global_token->s))
{
process_break();
}
else if(match("continue", global_token->s))
{
process_continue();
}
else
{
expression();
require_match("ERROR in statement\nMISSING ;\n", ";");
}
}
/* Collect function arguments */
void collect_arguments()
{
global_token = global_token->next;
require(NULL != global_token, "Received EOF when attempting to collect arguments\n");
struct type* type_size;
struct token_list* a;
while(!match(")", global_token->s))
{
type_size = type_name();
require(NULL != global_token, "Received EOF when attempting to collect arguments\n");
require(NULL != type_size, "Must have non-null type\n");
if(global_token->s[0] == ')')
{
/* foo(int,char,void) doesn't need anything done */
continue;
}
else if(global_token->s[0] != ',')
{
/* deal with foo(int a, char b) */
require(!in_set(global_token->s[0], "[{(<=>)}]|&!^%;:'\""), "forbidden character in argument variable name\n");
require(!iskeywordp(global_token->s), "You are not allowed to use a keyword as a argument variable name\n");
a = sym_declare(global_token->s, type_size, function->arguments);
if(NULL == function->arguments)
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) a->depth = 0;
else if(X86 == Architecture) a->depth = -4;
else if(AMD64 == Architecture) a->depth = -8;
else if(ARMV7L == Architecture) a->depth = 4;
else if(AARCH64 == Architecture) a->depth = register_size;
else if(RISCV32 == Architecture) a->depth = -4;
else if(RISCV64 == Architecture) a->depth = -8;
}
else
{
if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) a->depth = function->arguments->depth + register_size;
else if(X86 == Architecture) a->depth = function->arguments->depth - register_size;
else if(AMD64 == Architecture) a->depth = function->arguments->depth - register_size;
else if(ARMV7L == Architecture) a->depth = function->arguments->depth + register_size;
else if(AARCH64 == Architecture) a->depth = function->arguments->depth + register_size;
else if(RISCV32 == Architecture) a->depth = function->arguments->depth - register_size;
else if(RISCV64 == Architecture) a->depth = function->arguments->depth - register_size;
}
global_token = global_token->next;
require(NULL != global_token, "Incomplete argument list\n");
function->arguments = a;
}
/* ignore trailing comma (needed for foo(bar(), 1); expressions*/
if(global_token->s[0] == ',')
{
global_token = global_token->next;
require(NULL != global_token, "naked comma in collect arguments\n");
}
require(NULL != global_token, "Argument list never completed\n");
}
global_token = global_token->next;
}
void declare_function()
{
current_count = 0;
function = sym_declare(global_token->prev->s, NULL, global_function_list);
/* allow previously defined functions to be looked up */
global_function_list = function;
if((KNIGHT_NATIVE == Architecture) && match("main", function->s))
{
require_match("Impossible error ( vanished\n", "(");
require_match("Reality ERROR (USING KNIGHT-NATIVE)\nHardware does not support arguments\nthus neither can main on this architecture\ntry tape_01 and tape_02 instead\n", ")");
}
else collect_arguments();
require(NULL != global_token, "Function definitions either need to be prototypes or full\n");
/* If just a prototype don't waste time */
if(global_token->s[0] == ';') global_token = global_token->next;
else
{
emit_out("# Defining function ");
emit_out(function->s);
emit_out("\n");
emit_out(":FUNCTION_");
emit_out(function->s);
emit_out("\n");
statement();
/* Prevent duplicate RETURNS */
if(((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) && !match("RET R15\n", output_list->s)) emit_out("RET R15\n");
else if((X86 == Architecture) && !match("ret\n", output_list->s)) emit_out("ret\n");
else if((AMD64 == Architecture) && !match("ret\n", output_list->s)) emit_out("ret\n");
else if((ARMV7L == Architecture) && !match("'1' LR RETURN\n", output_list->s)) emit_out("'1' LR RETURN\n");
else if((AARCH64 == Architecture) && !match("RETURN\n", output_list->s)) emit_out("RETURN\n");
else if((RISCV32 == Architecture) && !match("RETURN\n", output_list->s)) emit_out("RETURN\n");
else if((RISCV64 == Architecture) && !match("RETURN\n", output_list->s)) emit_out("RETURN\n");
}
}
void global_constant()
{
global_token = global_token->next;
require(NULL != global_token, "CONSTANT lacks a name\n");
global_constant_list = sym_declare(global_token->s, NULL, global_constant_list);
require(NULL != global_token->next, "CONSTANT lacks a value\n");
if(match("sizeof", global_token->next->s))
{
global_token = global_token->next->next;
require_match("ERROR in CONSTANT with sizeof\nMissing (\n", "(");
struct type* a = type_name();
require_match("ERROR in CONSTANT with sizeof\nMissing )\n", ")");
global_token->prev->s = int2str(a->size, 10, TRUE);
global_constant_list->arguments = global_token->prev;
}
else
{
global_constant_list->arguments = global_token->next;
global_token = global_token->next->next;
}
}
struct type* global_typedef()
{
struct type* type_size;
/* typedef $TYPE $NAME; */
global_token = global_token->next;
type_size = type_name();
require(NULL != global_token, "Received EOF while reading typedef\n");
type_size = mirror_type(type_size, global_token->s);
add_primitive(type_size);
global_token = global_token->next;
require_match("ERROR in typedef statement\nMissing ;\n", ";");
return type_size;
}
void global_static_array(struct type* type_size, struct token_list* name)
{
int size;
maybe_bootstrap_error("global array definitions");
globals_list = emit(":GLOBAL_", globals_list);
globals_list = emit(name->s, globals_list);
globals_list = emit("\n&GLOBAL_STORAGE_", globals_list);
globals_list = emit(name->s, globals_list);
if (AARCH64 == Architecture || AMD64 == Architecture || RISCV64 == Architecture)
{
globals_list = emit(" %0", globals_list);
}
globals_list = emit("\n:GLOBAL_STORAGE_", globals_list);
globals_list = emit(name->s, globals_list);
require(NULL != global_token->next, "Unterminated global\n");
global_token = global_token->next;
/* Make sure not negative */
if(match("-", global_token->s))
{
line_error();
fputs("Negative values are not supported for allocated arrays\n", stderr);
exit(EXIT_FAILURE);
}
/* length */
size = strtoint(global_token->s) * type_size->size;
/* Stop bad states */
if((size < 0) || (size > 0x100000))
{
line_error();
fputs("M2-Planet is very inefficient so you probably don't want to allocate over 1MB into your binary for NULLs\n", stderr);
exit(EXIT_FAILURE);
}
/* Ensure properly closed */
global_token = global_token->next;
require_match("missing close bracket\n", "]");
require_match("missing ;\n", ";");
globals_list = emit("\n'", globals_list);
while (0 != size)
{
globals_list = emit(" 00", globals_list);
size = size - 1;
}
globals_list = emit("'\n", globals_list);
}
void global_assignment()
{
/* Store the global's value*/
globals_list = emit(":GLOBAL_", globals_list);
globals_list = emit(global_token->prev->s, globals_list);
globals_list = emit("\n", globals_list);
global_token = global_token->next;
require(NULL != global_token, "Global locals value in assignment\n");
if(in_set(global_token->s[0], "0123456789"))
{ /* Assume Int */
globals_list = emit("%", globals_list);
globals_list = emit(global_token->s, globals_list);
globals_list = emit("\n", globals_list);
}
else if(('"' == global_token->s[0]))
{ /* Assume a string*/
globals_list = emit("&GLOBAL_", globals_list);
globals_list = emit(global_token->prev->prev->s, globals_list);
globals_list = emit("_contents\n", globals_list);
globals_list = emit(":GLOBAL_", globals_list);
globals_list = emit(global_token->prev->prev->s, globals_list);
globals_list = emit("_contents\n", globals_list);
globals_list = emit(parse_string(global_token->s), globals_list);
}
else
{
line_error();
fputs("Received ", stderr);
fputs(global_token->s, stderr);
fputs(" in program\n", stderr);
exit(EXIT_FAILURE);
}
global_token = global_token->next;
require_match("ERROR in Program\nMissing ;\n", ";");
}
/*
* program:
* declaration
* declaration program
*
* declaration:
* CONSTANT identifer value
* typedef identifer type;
* type-name identifier ;
* type-name identifier = value ;
* type-name identifier [ value ];
* type-name identifier ( parameter-list ) ;
* type-name identifier ( parameter-list ) statement
*
* parameter-list:
* parameter-declaration
* parameter-list, parameter-declaration
*
* parameter-declaration:
* type-name identifier-opt
*/
void program()
{
unsigned i;
function = NULL;
Address_of = FALSE;
struct type* type_size;
new_type:
/* Deal with garbage input */
if (NULL == global_token) return;
require('#' != global_token->s[0], "unhandled macro directive\n");
require(!match("\n", global_token->s), "unexpected newline token\n");
/* Handle cc_* CONSTANT statements */
if(match("CONSTANT", global_token->s))
{
global_constant();
goto new_type;
}
/* Handle c typedef statements */
if(match("typedef", global_token->s))
{
type_size = global_typedef();
goto new_type;
}
type_size = type_name();
/* Deal with case of struct definitions */
if(NULL == type_size) goto new_type;
require(NULL != global_token->next, "Unterminated global\n");
/* Add to global symbol table */
global_symbol_list = sym_declare(global_token->s, type_size, global_symbol_list);
global_token = global_token->next;
/* Deal with global variables */
if(match(";", global_token->s))
{
/* Ensure enough bytes are allocated to store global variable.
In some cases it allocates too much but that is harmless. */
globals_list = emit(":GLOBAL_", globals_list);
globals_list = emit(global_token->prev->s, globals_list);
/* round up division */
i = ceil_div(type_size->size, register_size);
globals_list = emit("\n", globals_list);
while(i != 0)
{
globals_list = emit("NULL\n", globals_list);
i = i - 1;
}
global_token = global_token->next;
goto new_type;
}
/* Deal with global functions */
if(match("(", global_token->s))
{
declare_function();
goto new_type;
}
/* Deal with assignment to a global variable */
if(match("=", global_token->s))
{
global_assignment();
goto new_type;
}
/* Deal with global static arrays */
if(match("[", global_token->s))
{
global_static_array(type_size, global_token->prev);
goto new_type;
}
/* Everything else is just an error */
line_error();
fputs("Received ", stderr);
fputs(global_token->s, stderr);
fputs(" in program\n", stderr);
exit(EXIT_FAILURE);
}
void recursive_output(struct token_list* head, FILE* out)
{
struct token_list* i = reverse_list(head);
while(NULL != i)
{
fputs(i->s, out);
i = i->next;
}
}
void output_tokens(struct token_list *i, FILE* out)
{
while(NULL != i)
{
fputs(i->s, out);
fputs(" ", out);
i = i->next;
}
}
/* Copyright (C) 2021 Sanne Wouda
* Copyright (C) 2021 Andrius Å tikonas <andrius@stikonas.eu>
* Copyright (C) 2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
* This file is part of M2-Planet.
*
* M2-Planet is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* M2-Planet is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with M2-Planet. If not, see <http://www.gnu.org/licenses/>.
*/
#include "cc.h"
#include "gcc_req.h"
void require(int bool, char* error);
int strtoint(char* a);
void line_error_token(struct token_list* list);
struct token_list* eat_token(struct token_list* head);
struct conditional_inclusion
{
struct conditional_inclusion* prev;
int include; /* 1 == include, 0 == skip */
int previous_condition_matched; /* 1 == all subsequent conditions treated as FALSE */
};
struct macro_list
{
struct macro_list* next;
char* symbol;
struct token_list* expansion;
};
struct macro_list* macro_env;
struct conditional_inclusion* conditional_inclusion_top;
/* point where we are currently modifying the global_token list */
struct token_list* macro_token;
void init_macro_env(char* sym, char* value, char* source, int num)
{
struct macro_list* hold = macro_env;
macro_env = calloc(1, sizeof(struct macro_list));
macro_env->symbol = sym;
macro_env->next = hold;
macro_env->expansion = calloc(1, sizeof(struct token_list));
macro_env->expansion->s = value;
macro_env->expansion->filename = source;
macro_env->expansion->linenumber = num;
}
void eat_current_token()
{
int update_global_token = FALSE;
if (macro_token == global_token)
update_global_token = TRUE;
macro_token = eat_token(macro_token);
if(update_global_token)
global_token = macro_token;
}
void eat_newline_tokens()
{
macro_token = global_token;
while(TRUE)
{
if(NULL == macro_token) return;
if(match("\n", macro_token->s))
{
eat_current_token();
}
else
{
macro_token = macro_token->next;
}
}
}
/* returns the first token inserted; inserts *before* point */
struct token_list* insert_tokens(struct token_list* point, struct token_list* token)
{
struct token_list* copy;
struct token_list* first = NULL;
while (NULL != token)
{
copy = calloc(1, sizeof(struct token_list));
copy->s = token->s;
copy->filename = token->filename;
copy->linenumber = token->linenumber;
if(NULL == first)
{
first = copy;
}
copy->next = point;
if (NULL != point)
{
copy->prev = point->prev;
if(NULL != point->prev)
{
point->prev->next = copy;
}
point->prev = copy;
}
token = token->next;
}
return first;
}
struct macro_list* lookup_macro(struct token_list* token)
{
if(NULL == token)
{
line_error_token(macro_token);
fputs("null token received in lookup_macro\n", stderr);
exit(EXIT_FAILURE);
}
struct macro_list* hold = macro_env;
while (NULL != hold)
{
if (match(token->s, hold->symbol))
{
/* found! */
return hold;
}
hold = hold->next;
}
/* not found! */
return NULL;
}
void remove_macro(struct token_list* token)
{
if(NULL == token)
{
line_error_token(macro_token);
fputs("received a null in remove_macro\n", stderr);
exit(EXIT_FAILURE);
}
struct macro_list* hold = macro_env;
struct macro_list* temp;
/* Deal with the first element */
if (match(token->s, hold->symbol)) {
macro_env = hold->next;
free(hold);
return;
}
/* Remove element form the middle of linked list */
while (NULL != hold->next)
{
if (match(token->s, hold->next->symbol))
{
temp = hold->next;
hold->next = hold->next->next;
free(temp);
return;
}
hold = hold->next;
}
/* nothing to undefine */
return;
}
int macro_expression();
int macro_variable()
{
int value = 0;
struct macro_list* hold = lookup_macro(macro_token);
if (NULL != hold)
{
if(NULL == hold->expansion)
{
line_error_token(macro_token);
fputs("hold->expansion is a null\n", stderr);
exit(EXIT_FAILURE);
}
value = strtoint(hold->expansion->s);
}
eat_current_token();
return value;
}
int macro_number()
{
int result = strtoint(macro_token->s);
eat_current_token();
return result;
}
int macro_primary_expr()
{
int defined_has_paren = FALSE;
int hold;
require(NULL != macro_token, "got an EOF terminated macro primary expression\n");
if('-' == macro_token->s[0])
{
eat_current_token();
return -macro_primary_expr();
}
else if('!' == macro_token->s[0])
{
eat_current_token();
return !macro_primary_expr();
}
else if('(' == macro_token->s[0])
{
eat_current_token();
hold = macro_expression();
require(')' == macro_token->s[0], "missing ) in macro expression\n");
eat_current_token();
return hold;
}
else if(match("defined", macro_token->s))
{
eat_current_token();
require(NULL != macro_token, "got an EOF terminated macro defined expression\n");
if('(' == macro_token->s[0])
{
defined_has_paren = TRUE;
eat_current_token();
}
if (NULL != lookup_macro(macro_token))
{
hold = TRUE;
}
else
{
hold = FALSE;
}
eat_current_token();
if(TRUE == defined_has_paren)
{
if(NULL == macro_token)
{
line_error_token(macro_token);
fputs("unterminated define ( statement\n", stderr);
exit(EXIT_FAILURE);
}
require(')' == macro_token->s[0], "missing close parenthesis for defined()\n");
eat_current_token();
}
return hold;
}
else if(in_set(macro_token->s[0], "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"))
{
return macro_variable();
}
else if(in_set(macro_token->s[0], "0123456789"))
{
return macro_number();
}
else
{
return 0; /* FIXME: error handling */
}
}
int macro_additive_expr()
{
int lhs = macro_primary_expr();
int hold;
require(NULL != macro_token, "got an EOF terminated macro additive expression\n");
if(match("+", macro_token->s))
{
eat_current_token();
return lhs + macro_additive_expr();
}
else if(match("-", macro_token->s))
{
eat_current_token();
return lhs - macro_additive_expr();
}
else if(match("*", macro_token->s))
{
eat_current_token();
return lhs * macro_additive_expr();
}
else if(match("/", macro_token->s))
{
eat_current_token();
hold = macro_additive_expr();
require(0 != hold, "divide by zero not valid even in C macros\n");
return lhs / hold;
}
else if(match("%", macro_token->s))
{
eat_current_token();
hold = macro_additive_expr();
require(0 != hold, "modulus by zero not valid even in C macros\n");
return lhs % hold;
}
else if(match(">>", macro_token->s))
{
eat_current_token();
return lhs >> macro_additive_expr();
}
else if(match("<<", macro_token->s))
{
eat_current_token();
return lhs << macro_additive_expr();
}
else
{
return lhs;
}
}
int macro_relational_expr()
{
int lhs = macro_additive_expr();
if(match("<", macro_token->s))
{
eat_current_token();
return lhs < macro_relational_expr();
}
else if(match("<=", macro_token->s))
{
eat_current_token();
return lhs <= macro_relational_expr();
}
else if(match(">=", macro_token->s))
{
eat_current_token();
return lhs >= macro_relational_expr();
}
else if(match(">", macro_token->s))
{
eat_current_token();
return lhs > macro_relational_expr();
}
else if(match("==", macro_token->s))
{
eat_current_token();
return lhs == macro_relational_expr();
}
else if(match("!=", macro_token->s))
{
eat_current_token();
return lhs != macro_relational_expr();
}
else
{
return lhs;
}
}
int macro_bitwise_expr()
{
int rhs;
int lhs = macro_relational_expr();
if(match("&", macro_token->s))
{
eat_current_token();
return lhs & macro_bitwise_expr();
}
else if(match("&&", macro_token->s))
{
eat_current_token();
rhs = macro_bitwise_expr();
return lhs && rhs;
}
else if(match("|", macro_token->s))
{
eat_current_token();
rhs = macro_bitwise_expr();
return lhs | rhs;
}
else if(match("||", macro_token->s))
{
eat_current_token();
rhs = macro_bitwise_expr();
return lhs || rhs;
}
else if(match("^", macro_token->s))
{
eat_current_token();
rhs = macro_bitwise_expr();
return lhs ^ rhs;
}
else
{
return lhs;
}
}
int macro_expression()
{
return macro_bitwise_expr();
}
void handle_define()
{
struct macro_list* hold;
struct token_list* expansion_end = NULL;
/* don't use #define statements from non-included blocks */
int conditional_define = TRUE;
if(NULL != conditional_inclusion_top)
{
if(FALSE == conditional_inclusion_top->include)
{
conditional_define = FALSE;
}
}
eat_current_token();
require(NULL != macro_token, "got an EOF terminated #define\n");
require('\n' != macro_token->s[0], "unexpected newline after #define\n");
/* insert new macro */
hold = calloc(1, sizeof(struct macro_list));
hold->symbol = macro_token->s;
hold->next = macro_env;
/* provided it isn't in a non-included block */
if(conditional_define) macro_env = hold;
/* discard the macro name */
eat_current_token();
while (TRUE)
{
require(NULL != macro_token, "got an EOF terminated #define\n");
if ('\n' == macro_token->s[0])
{
if(NULL == expansion_end)
{
hold->expansion = NULL;
expansion_end = macro_token;
return;
}
expansion_end->next = NULL;
return;
}
require(NULL != hold, "#define got something it can't handle\n");
expansion_end = macro_token;
/* in the first iteration, we set the first token of the expansion, if
it exists */
if (NULL == hold->expansion)
{
hold->expansion = macro_token;
}
/* throw away if not used */
if(!conditional_define && (NULL != hold))
{
free(hold);
hold = NULL;
}
eat_current_token();
}
}
void handle_undef()
{
eat_current_token();
remove_macro(macro_token);
eat_current_token();
}
void handle_error(int warning_p)
{
/* don't use #error statements from non-included blocks */
int conditional_error = TRUE;
if(NULL != conditional_inclusion_top)
{
if(FALSE == conditional_inclusion_top->include)
{
conditional_error = FALSE;
}
}
eat_current_token();
/* provided it isn't in a non-included block */
if(conditional_error)
{
line_error_token(macro_token);
if(warning_p) fputs(" warning: #warning ", stderr);
else fputs(" error: #error ", stderr);
while (TRUE)
{
require(NULL != macro_token, "\nFailed to properly terminate error message with \\n\n");
if ('\n' == macro_token->s[0]) break;
fputs(macro_token->s, stderr);
macro_token = macro_token->next;
fputs(" ", stderr);
}
fputs("\n", stderr);
if(!warning_p) exit(EXIT_FAILURE);
}
while (TRUE)
{
require(NULL != macro_token, "\nFailed to properly terminate error message with \\n\n");
/* discard the error */
if ('\n' == macro_token->s[0])
{
return;
}
eat_current_token();
}
}
void eat_block();
void macro_directive()
{
struct conditional_inclusion *t;
int result;
/* FIXME: whitespace is allowed between "#"" and "if" */
if(match("#if", macro_token->s))
{
eat_current_token();
/* evaluate constant integer expression */
result = macro_expression();
/* push conditional inclusion */
t = calloc(1, sizeof(struct conditional_inclusion));
t->prev = conditional_inclusion_top;
conditional_inclusion_top = t;
t->include = TRUE;
if(FALSE == result)
{
t->include = FALSE;
eat_block();
}
t->previous_condition_matched = t->include;
}
else if(match("#ifdef", macro_token->s))
{
eat_current_token();
require(NULL != macro_token, "got an EOF terminated macro defined expression\n");
if (NULL != lookup_macro(macro_token))
{
result = TRUE;
eat_current_token();
}
else
{
result = FALSE;
eat_block();
}
/* push conditional inclusion */
t = calloc(1, sizeof(struct conditional_inclusion));
t->prev = conditional_inclusion_top;
conditional_inclusion_top = t;
t->include = TRUE;
if(FALSE == result)
{
t->include = FALSE;
}
t->previous_condition_matched = t->include;
}
else if(match("#ifndef", macro_token->s))
{
eat_current_token();
require(NULL != macro_token, "got an EOF terminated macro defined expression\n");
if (NULL != lookup_macro(macro_token))
{
result = FALSE;
}
else
{
result = TRUE;
eat_current_token();
}
/* push conditional inclusion */
t = calloc(1, sizeof(struct conditional_inclusion));
t->prev = conditional_inclusion_top;
conditional_inclusion_top = t;
t->include = TRUE;
if(FALSE == result)
{
t->include = FALSE;
eat_block();
}
t->previous_condition_matched = t->include;
}
else if(match("#elif", macro_token->s))
{
eat_current_token();
result = macro_expression();
require(NULL != conditional_inclusion_top, "#elif without leading #if\n");
conditional_inclusion_top->include = result && !conditional_inclusion_top->previous_condition_matched;
conditional_inclusion_top->previous_condition_matched =
conditional_inclusion_top->previous_condition_matched || conditional_inclusion_top->include;
if(FALSE == result)
{
eat_block();
}
}
else if(match("#else", macro_token->s))
{
eat_current_token();
require(NULL != conditional_inclusion_top, "#else without leading #if\n");
conditional_inclusion_top->include = !conditional_inclusion_top->previous_condition_matched;
if(FALSE == conditional_inclusion_top->include)
{
eat_block();
}
}
else if(match("#endif", macro_token->s))
{
if(NULL == conditional_inclusion_top)
{
line_error_token(macro_token);
fputs("unexpected #endif\n", stderr);
exit(EXIT_FAILURE);
}
eat_current_token();
/* pop conditional inclusion */
t = conditional_inclusion_top;
conditional_inclusion_top = conditional_inclusion_top->prev;
free(t);
}
else if(match("#define", macro_token->s))
{
handle_define();
}
else if(match("#undef", macro_token->s))
{
handle_undef();
}
else if(match("#error", macro_token->s))
{
handle_error(FALSE);
}
else if(match("#warning", macro_token->s))
{
handle_error(TRUE);
}
else
{
if(!match("#include", macro_token->s))
{
/* Put a big fat warning but see if we can just ignore */
fputs(">>WARNING<<\n>>WARNING<<\n", stderr);
line_error_token(macro_token);
fputs("feature: ", stderr);
fputs(macro_token->s, stderr);
fputs(" unsupported in M2-Planet\nIgnoring line, may result in bugs\n>>WARNING<<\n>>WARNING<<\n\n", stderr);
}
/* unhandled macro directive; let's eat until a newline; om nom nom */
while(TRUE)
{
if(NULL == macro_token)
{
return;
}
if('\n' == macro_token->s[0])
{
return;
}
eat_current_token();
}
}
}
void eat_until_endif()
{
/* This #if block is nested inside of an #if block that needs to be dropped, lose EVERYTHING */
do
{
require(NULL != macro_token, "Unterminated #if block\n");
if(match("#if", macro_token->s) || match("#ifdef", macro_token->s) || match("#ifndef", macro_token->s))
{
eat_current_token();
eat_until_endif();
}
eat_current_token();
require(NULL != macro_token, "Unterminated #if block\n");
} while(!match("#endif", macro_token->s));
}
void eat_block()
{
/* This conditional #if block is wrong, drop everything until the #elif/#else/#endif */
do
{
if(match("#if", macro_token->s) || match("#ifdef", macro_token->s) || match("#ifndef", macro_token->s))
{
eat_current_token();
eat_until_endif();
}
eat_current_token();
require(NULL != macro_token, "Unterminated #if block\n");
if(match("#elif", macro_token->s)) break;
if(match("#else", macro_token->s)) break;
if(match("#endif", macro_token->s)) break;
} while(TRUE);
require(NULL != macro_token->prev, "impossible #if block\n");
/* rewind the newline */
if(match("\n", macro_token->prev->s)) macro_token = macro_token->prev;
}
struct token_list* maybe_expand(struct token_list* token)
{
if(NULL == token)
{
line_error_token(macro_token);
fputs("maybe_expand passed a null token\n", stderr);
exit(EXIT_FAILURE);
}
struct macro_list* hold = lookup_macro(token);
struct token_list* hold2;
if(NULL == token->next)
{
line_error_token(macro_token);
fputs("we can't expand a null token: ", stderr);
fputs(token->s, stderr);
fputc('\n', stderr);
exit(EXIT_FAILURE);
}
if (NULL == hold)
{
return token->next;
}
token = eat_token(token);
if (NULL == hold->expansion)
{
return token->next;
}
hold2 = insert_tokens(token, hold->expansion);
return hold2->next;
}
void preprocess()
{
int start_of_line = TRUE;
macro_token = global_token;
while(NULL != macro_token)
{
if(start_of_line && '#' == macro_token->s[0])
{
macro_directive();
if(macro_token)
{
if('\n' != macro_token->s[0])
{
line_error_token(macro_token);
fputs("newline expected at end of macro directive\n", stderr);
fputs("found: '", stderr);
fputs(macro_token->s, stderr);
fputs("'\n", stderr);
exit(EXIT_FAILURE);
}
}
}
else if('\n' == macro_token->s[0])
{
start_of_line = TRUE;
macro_token = macro_token->next;
}
else
{
start_of_line = FALSE;
if(NULL == conditional_inclusion_top)
{
macro_token = maybe_expand(macro_token);
}
else if(!conditional_inclusion_top->include)
{
/* rewrite the token stream to exclude the current token */
eat_block();
start_of_line = TRUE;
}
else
{
macro_token = maybe_expand(macro_token);
}
}
}
}
/* Copyright (C) 2016 Jeremiah Orians
* Copyright (C) 2020 deesix <deesix@tuta.io>
* This file is part of M2-Planet.
*
* M2-Planet is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* M2-Planet is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with M2-Planet. If not, see <http://www.gnu.org/licenses/>.
*/
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include"cc.h"
/* The core functions */
void initialize_types();
struct token_list* read_all_tokens(FILE* a, struct token_list* current, char* filename);
struct token_list* reverse_list(struct token_list* head);
struct token_list* remove_line_comments(struct token_list* head);
struct token_list* remove_line_comment_tokens(struct token_list* head);
struct token_list* remove_preprocessor_directives(struct token_list* head);
void eat_newline_tokens();
void init_macro_env(char* sym, char* value, char* source, int num);
void preprocess();
void program();
void recursive_output(struct token_list* i, FILE* out);
void output_tokens(struct token_list *i, FILE* out);
int strtoint(char *a);
int main(int argc, char** argv)
{
MAX_STRING = 4096;
BOOTSTRAP_MODE = FALSE;
PREPROCESSOR_MODE = FALSE;
int DEBUG = FALSE;
FILE* in = stdin;
FILE* destination_file = stdout;
Architecture = 0; /* catch unset */
init_macro_env("__M2__", "42", "__INTERNAL_M2__", 0); /* Setup __M2__ */
char* arch;
char* name;
char* hold;
int env=0;
char* val;
int i = 1;
while(i <= argc)
{
if(NULL == argv[i])
{
i = i + 1;
}
else if(match(argv[i], "-f") || match(argv[i], "--file"))
{
if(NULL == hold_string)
{
hold_string = calloc(MAX_STRING + 4, sizeof(char));
require(NULL != hold_string, "Impossible Exhaustion has occurred\n");
}
name = argv[i + 1];
if(NULL == name)
{
fputs("did not receive a file name\n", stderr);
exit(EXIT_FAILURE);
}
in = fopen(name, "r");
if(NULL == in)
{
fputs("Unable to open for reading file: ", stderr);
fputs(name, stderr);
fputs("\n Aborting to avoid problems\n", stderr);
exit(EXIT_FAILURE);
}
global_token = read_all_tokens(in, global_token, name);
fclose(in);
i = i + 2;
}
else if(match(argv[i], "-o") || match(argv[i], "--output"))
{
destination_file = fopen(argv[i + 1], "w");
if(NULL == destination_file)
{
fputs("Unable to open for writing file: ", stderr);
fputs(argv[i + 1], stderr);
fputs("\n Aborting to avoid problems\n", stderr);
exit(EXIT_FAILURE);
}
i = i + 2;
}
else if(match(argv[i], "-A") || match(argv[i], "--architecture"))
{
arch = argv[i + 1];
if(match("knight-native", arch)) {
Architecture = KNIGHT_NATIVE;
init_macro_env("__knight__", "1", "--architecture", env);
env = env + 1;
}
else if(match("knight-posix", arch)) {
Architecture = KNIGHT_POSIX;
init_macro_env("__knight_posix__", "1", "--architecture", env);
env = env + 1;
}
else if(match("x86", arch))
{
Architecture = X86;
init_macro_env("__i386__", "1", "--architecture", env);
env = env + 1;
}
else if(match("amd64", arch))
{
Architecture = AMD64;
init_macro_env("__x86_64__", "1", "--architecture", env);
env = env + 1;
}
else if(match("armv7l", arch))
{
Architecture = ARMV7L;
init_macro_env("__arm__", "1", "--architecture", env);
env = env + 1;
}
else if(match("aarch64", arch))
{
Architecture = AARCH64;
init_macro_env("__aarch64__", "1", "--architecture", env);
env = env + 1;
}
else if(match("riscv32", arch))
{
Architecture = RISCV32;
init_macro_env("__riscv", "1", "--architecture", env);
init_macro_env("__riscv_xlen", "32", "--architecture", env + 1);
env = env + 2;
}
else if(match("riscv64", arch))
{
Architecture = RISCV64;
init_macro_env("__riscv", "1", "--architecture", env);
init_macro_env("__riscv_xlen", "64", "--architecture", env + 1);
env = env + 2;
}
else
{
fputs("Unknown architecture: ", stderr);
fputs(arch, stderr);
fputs(" know values are: knight-native, knight-posix, x86, amd64, armv7l, aarch64, riscv32 and riscv64\n", stderr);
exit(EXIT_FAILURE);
}
i = i + 2;
}
else if(match(argv[i], "--max-string"))
{
hold = argv[i+1];
if(NULL == hold)
{
fputs("--max-string requires a numeric argument\n", stderr);
exit(EXIT_FAILURE);
}
MAX_STRING = strtoint(hold);
require(0 < MAX_STRING, "Not a valid string size\nAbort and fix your --max-string\n");
i = i + 2;
}
else if(match(argv[i], "--bootstrap-mode"))
{
BOOTSTRAP_MODE = TRUE;
i = i + 1;
}
else if(match(argv[i], "-g") || match(argv[i], "--debug"))
{
DEBUG = TRUE;
i = i + 1;
}
else if(match(argv[i], "-h") || match(argv[i], "--help"))
{
fputs(" -f input file\n -o output file\n --help for this message\n --version for file version\n", stdout);
exit(EXIT_SUCCESS);
}
else if(match(argv[i], "-E"))
{
PREPROCESSOR_MODE = TRUE;
i = i + 1;
}
else if(match(argv[i], "-D"))
{
val = argv[i+1];
if(NULL == val)
{
fputs("-D requires an argument", stderr);
exit(EXIT_FAILURE);
}
while(0 != val[0])
{
if('=' == val[0])
{
val[0] = 0;
val = val + 1;
break;
}
val = val + 1;
}
init_macro_env(argv[i+1], val, "__ARGV__", env);
env = env + 1;
i = i + 2;
}
else if(match(argv[i], "-V") || match(argv[i], "--version"))
{
fputs("M2-Planet v1.10.0\n", stderr);
exit(EXIT_SUCCESS);
}
else
{
fputs("UNKNOWN ARGUMENT\n", stdout);
exit(EXIT_FAILURE);
}
}
/* Deal with special case of architecture not being set */
if(0 == Architecture)
{
Architecture = KNIGHT_NATIVE;
init_macro_env("__knight__", "1", "--architecture", env);
}
/* Deal with special case of wanting to read from standard input */
if(stdin == in)
{
hold_string = calloc(MAX_STRING + 4, sizeof(char));
require(NULL != hold_string, "Impossible Exhaustion has occurred\n");
global_token = read_all_tokens(in, global_token, "STDIN");
}
if(NULL == global_token)
{
fputs("Either no input files were given or they were empty\n", stderr);
exit(EXIT_FAILURE);
}
global_token = reverse_list(global_token);
if (BOOTSTRAP_MODE)
{
global_token = remove_line_comment_tokens(global_token);
global_token = remove_preprocessor_directives(global_token);
}
else
{
global_token = remove_line_comments(global_token);
preprocess();
}
if (PREPROCESSOR_MODE)
{
fputs("\n/* Preprocessed source */\n", destination_file);
output_tokens(global_token, destination_file);
goto exit_success;
}
/* the main parser doesn't know how to handle newline tokens */
eat_newline_tokens();
initialize_types();
reset_hold_string();
output_list = NULL;
program();
/* Output the program we have compiled */
fputs("\n# Core program\n", destination_file);
recursive_output(output_list, destination_file);
if(KNIGHT_NATIVE == Architecture) fputs("\n", destination_file);
else if(DEBUG) fputs("\n:ELF_data\n", destination_file);
fputs("\n# Program global variables\n", destination_file);
recursive_output(globals_list, destination_file);
fputs("\n# Program strings\n", destination_file);
recursive_output(strings_list, destination_file);
if(KNIGHT_NATIVE == Architecture) fputs("\n:STACK\n", destination_file);
else if(!DEBUG) fputs("\n:ELF_end\n", destination_file);
exit_success:
if (destination_file != stdout)
{
fclose(destination_file);
}
return EXIT_SUCCESS;
}
## Copyright (C) 2017 Jeremiah Orians ## This file is part of M2-Planet. ## ## M2-Planet is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## M2-Planet is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with M2-Planet. If not, see <http://www.gnu.org/licenses/>. DEFINE add_eax, 81C0 DEFINE add_ebp, 81C5 DEFINE add_ebx,eax 01C3 DEFINE add_eax,ebp 01E8 DEFINE add_eax,ebx 01D8 DEFINE and_eax,ebx 21D8 DEFINE call E8 DEFINE call_eax FFD0 DEFINE cmp 39C3 DEFINE cdq 99 DEFINE div_ebx F7F3 DEFINE idiv_ebx F7FB DEFINE int CD DEFINE je 0F84 DEFINE jne 0F85 DEFINE jmp E9 DEFINE lea_eax,[ebp+DWORD] 8D85 DEFINE lea_eax,[esp+DWORD] 8D8424 DEFINE lea_ebx,[esp+DWORD] 8D9C24 DEFINE lea_ecx,[esp+DWORD] 8D8C24 DEFINE lea_edx,[esp+DWORD] 8D9424 DEFINE mov_eax,[esp+DWORD] 8B8424 DEFINE mov_eax,ebp 89E8 DEFINE mov_eax,ebx 89D8 DEFINE mov_eax,ebx 89D8 DEFINE mov_eax,edx 89D0 DEFINE mov_ebx,eax 89C3 DEFINE mov_ecx,eax 89C1 DEFINE mov_ecx,esp 89E1 DEFINE mov_edi,esp 89E7 DEFINE mov_ebp,edi 89fd DEFINE mov_ebp,esp 89E5 DEFINE mov_eax, B8 DEFINE mov_ebx, BB DEFINE mov_edx, BA DEFINE mov_eax,[eax] 8B00 DEFINE mov_ebx,[ebx] 8B1B DEFINE mov_ecx,[ecx] 8B09 DEFINE mov_edx,[edx] 8B12 DEFINE mov_[ebx],al 8803 DEFINE mov_[ebx],eax 8903 DEFINE movsx_eax,BYTE_PTR_[eax] 0FBE00 DEFINE movsx_ebx,BYTE_PTR_[ebx] 0FBE1B DEFINE movzx_eax,al 0FB6C0 DEFINE mul_ebx F7E3 DEFINE imul_ebx F7EB DEFINE NULL 00000000 DEFINE not_eax F7D0 DEFINE or_eax,ebx 09D8 DEFINE pop_eax 58 DEFINE pop_ebx 5B DEFINE pop_ebp 5D DEFINE pop_edi 5F DEFINE push_eax 50 DEFINE push_ebx 53 DEFINE push_ebp 55 DEFINE push_edi 57 DEFINE ret C3 DEFINE sal_eax, C1E0 DEFINE sal_eax,cl D3F0 DEFINE shl_eax,cl D3E0 DEFINE sar_eax,cl D3F8 DEFINE shr_eax,cl D3E8 DEFINE seta_al 0F97C0 DEFINE setae_al 0F93C0 DEFINE setb_al 0F92C0 DEFINE setbe_al 0F96C0 DEFINE sete_al 0F94C0 DEFINE setle_al 0F9EC0 DEFINE setl_al 0F9CC0 DEFINE setge_al 0F9DC0 DEFINE setg_al 0F9FC0 DEFINE setne_al 0F95C0 DEFINE sub_ebx,eax 29C3 DEFINE test_eax,eax 85C0 DEFINE xchg_ebx,eax 93 DEFINE xor_eax,ebx 31D8
## Copyright (C) 2016 Jeremiah Orians
## This file is part of stage0.
##
## stage0 is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## stage0 is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with stage0. If not, see <http://www.gnu.org/licenses/>.
:_start
mov_ebp,esp ; Protect esp
;; Prepare argv
lea_eax,[ebp+DWORD] %4 ; ARGV_address = EBP + 4
push_eax ; Put argv on the stack
;; Prepare envp
mov_eax,ebp ; Address we need to load from
mov_eax,[eax] ; Get ARGC
add_eax, %2 ; OFFSET = ARGC + 2
sal_eax, !2 ; OFFSET = OFFSET * WORDSIZE
add_eax,ebp ; ENVP_address = ESP + OFFSET
push_eax ; Put envp on the stack
;; Stack offset
add_ebp, %4 ; Fix ebp
;; Perform the main loop
call %FUNCTION_main
;; Exit to kernel
mov_ebx,eax ; Using the return code given by main
mov_eax, %1 ; Syscall exit
int !0x80 ; Exit with that code
/* -*- c-file-style: "linux";indent-tabs-mode:t -*- */
/* Copyright (C) 2016 Jeremiah Orians
* Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org>
* This file is part of mescc-tools.
*
* mescc-tools is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* mescc-tools is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with mescc-tools. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
// CONSTANT HEX 16
#define HEX 16
// CONSTANT OCTAL 8
#define OCTAL 8
// CONSTANT BINARY 2
#define BINARY 2
/***********************************************************
* Needed for current implementation of little endian *
* Can be used to support little bit endian instruction *
* sets if we ever find one that might be useful *
* But I seriously doubt it *
***********************************************************/
void reverseBitOrder(char* c, int ByteMode)
{
if(NULL == c) return;
if(0 == c[1]) return;
int hold = c[0];
if(HEX == ByteMode)
{
c[0] = c[1];
c[1] = hold;
reverseBitOrder(c+2, ByteMode);
}
else if(OCTAL == ByteMode)
{
c[0] = c[2];
c[2] = hold;
reverseBitOrder(c+3, ByteMode);
}
else if(BINARY == ByteMode)
{
c[0] = c[7];
c[7] = hold;
hold = c[1];
c[1] = c[6];
c[6] = hold;
hold = c[2];
c[2] = c[5];
c[5] = hold;
hold = c[3];
c[3] = c[4];
c[4] = hold;
reverseBitOrder(c+8, ByteMode);
}
}
void LittleEndian(char* start, int ByteMode)
{
char* end = start;
char* c = start;
while(0 != end[0]) end = end + 1;
int hold;
for(end = end - 1; start < end; start = start + 1)
{
hold = start[0];
start[0] = end[0];
end[0] = hold;
end = end - 1;
}
/* The above makes a reversed bit order */
reverseBitOrder(c, ByteMode);
}
int hex2char(int c)
{
if((c >= 0) && (c <= 9)) return (c + 48);
else if((c >= 10) && (c <= 15)) return (c + 55);
else return -1;
}
int stringify(char* s, int digits, int divisor, int value, int shift)
{
int i = value;
if(digits > 1)
{
i = stringify(s+1, (digits - 1), divisor, value, shift);
}
s[0] = hex2char(i & (divisor - 1));
return (i >> shift);
}
/* -*- c-file-style: "linux";indent-tabs-mode:t -*- */
/* Copyright (C) 2017 Jeremiah Orians
* Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org>
* This file is part of mescc-tools
*
* mescc-tools is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* mescc-tools is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with mescc-tools. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include "M2libc/bootstrappable.h"
// CONSTANT max_string 4096
#define max_string 4096
int BITSIZE;
int BigEndian;
// CONSTANT HEX 16
#define HEX 16
// CONSTANT OCTAL 8
#define OCTAL 8
// CONSTANT BINARY 2
#define BINARY 2
/* Strings needed for constants */
char* zero_8;
char* zero_16;
char* zero_32;
char* one_16;
char* one_32;
char* two_8;
char* two_32;
char* three_32;
char* six_32;
char* sixteen_32;
char* twentyfour_32;
/* Imported from stringify.c */
int stringify(char* s, int digits, int divisor, int value, int shift);
void LittleEndian(char* start, int ByteMode);
struct entry
{
struct entry* next;
char* name;
};
FILE* output;
struct entry* jump_table;
int count;
char* entry;
void consume_token(FILE* source_file, char* s)
{
int i = 0;
int c = fgetc(source_file);
require(EOF != c, "Can not have an EOF token\n");
do
{
s[i] = c;
i = i + 1;
require(max_string > i, "Token exceeds token length restriction\n");
c = fgetc(source_file);
if(EOF == c) break;
} while(!in_set(c, " \t\n>"));
}
void storeLabel(FILE* source_file)
{
struct entry* entry = calloc(1, sizeof(struct entry));
/* Prepend to list */
entry->next = jump_table;
jump_table = entry;
/* Store string */
entry->name = calloc((max_string + 1), sizeof(char));
consume_token(source_file, entry->name);
count = count + 1;
}
void line_Comment(FILE* source_file)
{
int c = fgetc(source_file);
while(!in_set(c, "\n\r"))
{
if(EOF == c) break;
c = fgetc(source_file);
}
}
void purge_string(FILE* source_file)
{
int c = fgetc(source_file);
while((EOF != c) && ('"' != c))
{
c = fgetc(source_file);
}
}
void first_pass(struct entry* input)
{
if(NULL == input) return;
first_pass(input->next);
FILE* source_file = fopen(input->name, "r");
if(NULL == source_file)
{
fputs("The file: ", stderr);
fputs(input->name, stderr);
fputs(" can not be opened!\n", stderr);
exit(EXIT_FAILURE);
}
int c;
for(c = fgetc(source_file); EOF != c; c = fgetc(source_file))
{
/* Check for and deal with label */
if(58 == c)
{
storeLabel(source_file);
}
/* Check for and deal with line comments */
else if (c == '#' || c == ';')
{
line_Comment(source_file);
}
else if ('"' == c)
{
purge_string(source_file);
}
}
fclose(source_file);
}
void output_string_table(struct entry* node)
{
fputs("\n# Generated string table\n:ELF_str\n", output);
fputs(zero_8, output);
fputs("\t# NULL string\n", output);
struct entry* i;
for(i = node; NULL != i; i = i->next)
{
fputs(":ELF_str_", output);
fputs(i->name, output);
fputs("\t\"", output);
fputs(i->name, output);
fputs("\"\n", output);
}
fputs("# END Generated string table\n\n", output);
}
void output_symbol_table(struct entry* node)
{
fputs("\n# Generated symbol table\n:ELF_sym\n# Required NULL symbol entry\n", output);
if(64 == BITSIZE)
{
fputs(zero_32, output);
fputs("\t# st_name\n", output);
fputs(zero_8, output);
fputs("\t# st_info\n", output);
fputs(zero_8, output);
fputs("\t# st_other\n", output);
fputs(one_16, output);
fputs("\t# st_shndx\n", output);
fputs(zero_32, output);
fputc(' ', output);
fputs(zero_32, output);
fputs("\t# st_value\n", output);
fputs(zero_32, output);
fputc(' ', output);
fputs(zero_32, output);
fputs("\t# st_size\n\n", output);
}
else
{
fputs(zero_32, output);
fputs("\t# st_name\n", output);
fputs(zero_32, output);
fputs("\t# st_value\n", output);
fputs(zero_32, output);
fputs("\t# st_size\n", output);
fputs(zero_8, output);
fputs("\t# st_info\n", output);
fputs(zero_8, output);
fputs("\t# st_other\n", output);
fputs(one_16, output);
fputs("\t# st_shndx\n\n", output);
}
struct entry* i;
for(i = node; NULL != i; i = i->next)
{
fputs("%ELF_str_", output);
fputs(i->name, output);
fputs(">ELF_str\t# st_name\n", output);
if(64 == BITSIZE)
{
fputs(two_8, output);
fputs("\t# st_info (FUNC)\n", output);
if(('_' == i->name[0]) && !match(entry, i->name))
{
fputs(two_8, output);
fputs("\t# st_other (hidden)\n", output);
}
else
{
fputs(zero_8, output);
fputs("\t# st_other (other)\n", output);
}
fputs(one_16, output);
fputs("\t# st_shndx\n", output);
fputs("&", output);
fputs(i->name, output);
fputc(' ', output);
fputs(zero_32, output);
fputs("\t# st_value\n", output);
fputs(zero_32, output);
fputc(' ', output);
fputs(zero_32, output);
fputs("\t# st_size (unknown size)\n\n", output);
}
else
{
fputs("&", output);
fputs(i->name, output);
fputs("\t#st_value\n", output);
fputs(zero_32, output);
fputs("\t# st_size (unknown size)\n", output);
fputs(two_8, output);
fputs("\t# st_info (FUNC)\n", output);
if(('_' == i->name[0]) && !match(entry, i->name))
{
fputs(two_8, output);
fputs("\t# st_other (hidden)\n", output);
}
else
{
fputs(zero_8, output);
fputs("\t# st_other (default)\n", output);
}
fputs(one_16, output);
fputs("\t# st_shndx\n\n", output);
}
}
fputs("# END Generated symbol table\n", output);
}
struct entry* reverse_list(struct entry* head)
{
struct entry* root = NULL;
struct entry* next;
while(NULL != head)
{
next = head->next;
head->next = root;
root = head;
head = next;
}
return root;
}
void write_int(char* field, char* label)
{
fputs(field, output);
fputs("\t#", output);
fputs(label, output);
fputc('\n', output);
}
void write_register(char* field, char* label)
{
/* $field section in the section headers are different size for 32 and 64bits */
/* The below is broken for BigEndian */
fputs(field, output);
if(64 == BITSIZE)
{
fputc(' ', output);
fputs(zero_32, output);
}
fputs("\t#", output);
fputs(label, output);
fputc('\n', output);
}
void write_section(char* label, char* name, char* type, char* flags, char* address, char* offset, char* size, char* link, char* info, char* entry)
{
/* Write label */
fputc('\n', output);
fputs(label, output);
fputc('\n', output);
write_int(name, "sh_name");
write_int(type, "sh_type");
write_register(flags, "sh_flags");
write_register(address, "sh_addr");
write_register(offset, "sh_offset");
write_register(size, "sh_size");
write_int(link, "sh_link");
/* Deal with the ugly case of stubs */
fputs(info, output);
fputs("\t#sh_info\n", output);
/* Alignment section in the section headers are different size for 32 and 64bits */
/* The below is broken for BigEndian */
if(64 == BITSIZE)
{
fputs(one_32, output);
fputc(' ', output);
fputs(zero_32, output);
fputs("\t#sh_addralign\n", output);
}
else
{
fputs(one_32, output);
fputs("\t#sh_addralign\n", output);
}
write_register(entry, "sh_entsize");
}
char* get_string(int value, int size, int ByteMode, int shift)
{
char* ch = calloc(42, sizeof(char));
require(NULL != ch, "Exhausted available memory\n");
ch[0] = '\'';
stringify(ch+1, size, ByteMode, value, shift);
if(!BigEndian) LittleEndian(ch+1, ByteMode);
int i = 0;
while(0 != ch[i])
{
i = i + 1;
}
ch[i] = '\'';
return ch;
}
char* setup_string(int value, int number_of_bytes, int ByteMode)
{
int shift;
int size;
if(HEX == ByteMode)
{
size = 2;
shift = 4;
}
else if(OCTAL == ByteMode)
{
size = 3;
shift = 3;
}
else if(BINARY == ByteMode)
{
size = 8;
shift = 1;
}
else
{
fputs("reached impossible mode\n", stderr);
exit(EXIT_FAILURE);
}
return get_string(value, number_of_bytes *size, ByteMode, shift);
}
void setup_strings(int ByteMode)
{
zero_8 = setup_string(0, 1, ByteMode);
zero_16 = setup_string(0, 2, ByteMode);
zero_32 = setup_string(0, 4, ByteMode);
one_16 = setup_string(1, 2, ByteMode);
one_32 = setup_string(1, 4, ByteMode);
two_8 = setup_string(2, 1, ByteMode);
two_32 = setup_string(2, 4, ByteMode);
three_32 = setup_string(3, 4, ByteMode);
six_32 = setup_string(6, 4, ByteMode);
sixteen_32 = setup_string(16, 4, ByteMode);
twentyfour_32 = setup_string(24, 4, ByteMode);
}
/* Standard C main program */
int main(int argc, char **argv)
{
jump_table = NULL;
struct entry* input = NULL;
output = stdout;
char* output_file = "";
entry = "";
BITSIZE = 32;
count = 1;
BigEndian = TRUE;
int ByteMode = HEX;
int set = FALSE;
struct entry* temp;
struct entry* head;
int option_index = 1;
while(option_index <= argc)
{
if(NULL == argv[option_index])
{
option_index = option_index + 1;
}
else if(match(argv[option_index], "-h") || match(argv[option_index], "--help"))
{
fputs("Usage: ", stderr);
fputs(argv[0], stderr);
fputs(" --file FILENAME1 {--file FILENAME2} --output FILENAME\n", stderr);
exit(EXIT_SUCCESS);
}
else if(match(argv[option_index], "--64"))
{
BITSIZE = 64;
option_index = option_index + 1;
}
else if(match(argv[option_index], "-f") || match(argv[option_index], "--file"))
{
temp = calloc(1, sizeof(struct entry));
temp->name = argv[option_index + 1];
temp->next = input;
input = temp;
option_index = option_index + 2;
}
else if(match(argv[option_index], "-o") || match(argv[option_index], "--output"))
{
output_file = argv[option_index + 1];
output = fopen(output_file, "w");
if(NULL == output)
{
fputs("The file: ", stderr);
fputs(input->name, stderr);
fputs(" can not be opened!\n", stderr);
exit(EXIT_FAILURE);
}
option_index = option_index + 2;
}
else if(match(argv[option_index], "-b") || match(argv[option_index], "--binary"))
{
ByteMode = BINARY;
option_index = option_index + 1;
}
else if(match(argv[option_index], "-O") || match(argv[option_index], "--octal"))
{
ByteMode = OCTAL;
option_index = option_index + 1;
}
else if(match(argv[option_index], "-X") || match(argv[option_index], "--hex"))
{
ByteMode = HEX;
option_index = option_index + 1;
}
else if(match(argv[option_index], "--big-endian"))
{
BigEndian = TRUE;
set = TRUE;
option_index = option_index + 1;
}
else if(match(argv[option_index], "--little-endian"))
{
BigEndian = FALSE;
set = TRUE;
option_index = option_index + 1;
}
else if(match(argv[option_index], "-V") || match(argv[option_index], "--version"))
{
fputs("blood-elf 2.0.0\n(Basically Launches Odd Object Dump ExecutabLe Files\n", stdout);
exit(EXIT_SUCCESS);
}
else if(match(argv[option_index], "--entry"))
{
head = calloc(1, sizeof(struct entry));
/* Include _start or any other entry from your .hex2 */
head->next = jump_table;
jump_table = head;
jump_table->name = argv[option_index + 1];
/* However only the last one will be exempt from the _name hidden rule */
entry = argv[option_index + 1];
option_index = option_index + 2;
count = count + 1;
}
else
{
fputs("Unknown option\n", stderr);
exit(EXIT_FAILURE);
}
}
/* Make sure we have a program tape to run */
if (NULL == input)
{
return EXIT_FAILURE;
}
/* Force setting of endianness */
if(!set)
{
fputs("either --little-endian or --big-endian MUST be set\n", stderr);
return EXIT_FAILURE;
}
/* Setup the ugly formating because RISC-V sucks */
setup_strings(ByteMode);
/* Get all of the labels */
first_pass(input);
/* Reverse their order */
jump_table = reverse_list(jump_table);
/* Create sections */
/* Create string names for sections */
fputs("# Generated sections\n:ELF_shstr\n", output);
fputs(zero_8, output);
fputs("\t# NULL\n", output);
fputs(":ELF_shstr__text\n\".text\"\n", output);
fputs(":ELF_shstr__shstr\n\".shstrtab\"\n", output);
fputs(":ELF_shstr__sym\n\".symtab\"\n", output);
fputs(":ELF_shstr__str\n\".strtab\"\n", output);
/* Create NULL section header as is required by the Spec. So dumb and waste of bytes*/
write_section(":ELF_section_headers", zero_32, zero_32, zero_32, zero_32, zero_32, zero_32, zero_32, zero_32, zero_32);
write_section(":ELF_section_header_text", "%ELF_shstr__text>ELF_shstr", one_32, six_32, "&ELF_text", "%ELF_text>ELF_base", "%ELF_data>ELF_text", zero_32, zero_32, zero_32);
write_section(":ELF_section_header_shstr", "%ELF_shstr__shstr>ELF_shstr", three_32, zero_32, "&ELF_shstr", "%ELF_shstr>ELF_base", "%ELF_section_headers>ELF_shstr", zero_32, zero_32, zero_32);
write_section(":ELF_section_header_str", "%ELF_shstr__str>ELF_shstr", three_32, zero_32, "&ELF_str", "%ELF_str>ELF_base", "%ELF_sym>ELF_str", zero_32, zero_32, zero_32);
if(64 == BITSIZE) write_section(":ELF_section_header_sym", "%ELF_shstr__sym>ELF_shstr", two_32, zero_32, "&ELF_sym", "%ELF_sym>ELF_base", "%ELF_end>ELF_sym", three_32, setup_string(count, 4, ByteMode), twentyfour_32);
else write_section(":ELF_section_header_sym", "%ELF_shstr__sym>ELF_shstr", two_32, zero_32, "&ELF_sym", "%ELF_sym>ELF_base", "%ELF_end>ELF_sym", three_32, setup_string(count, 4, ByteMode), sixteen_32);
/* Create dwarf stubs needed for objdump -d to get function names */
output_string_table(jump_table);
output_symbol_table(jump_table);
fputs("\n:ELF_end\n", output);
/* Close output file */
fclose(output);
return EXIT_SUCCESS;
}
## Copyright (C) 2017 Jeremiah Orians ## This file is part of M2-Planet. ## ## M2-Planet is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## M2-Planet is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with M2-Planet. If not, see <http://www.gnu.org/licenses/>. DEFINE add_eax, 81C0 DEFINE add_ebp, 81C5 DEFINE add_ebx,eax 01C3 DEFINE add_eax,ebp 01E8 DEFINE add_eax,ebx 01D8 DEFINE and_eax,ebx 21D8 DEFINE call E8 DEFINE call_eax FFD0 DEFINE cmp 39C3 DEFINE cdq 99 DEFINE div_ebx F7F3 DEFINE idiv_ebx F7FB DEFINE int CD DEFINE je 0F84 DEFINE jne 0F85 DEFINE jmp E9 DEFINE lea_eax,[ebp+DWORD] 8D85 DEFINE lea_eax,[esp+DWORD] 8D8424 DEFINE lea_ebx,[esp+DWORD] 8D9C24 DEFINE lea_ecx,[esp+DWORD] 8D8C24 DEFINE lea_edx,[esp+DWORD] 8D9424 DEFINE mov_eax,[esp+DWORD] 8B8424 DEFINE mov_eax,ebp 89E8 DEFINE mov_eax,ebx 89D8 DEFINE mov_eax,ebx 89D8 DEFINE mov_eax,edx 89D0 DEFINE mov_ebx,eax 89C3 DEFINE mov_ecx,eax 89C1 DEFINE mov_ecx,esp 89E1 DEFINE mov_edi,esp 89E7 DEFINE mov_ebp,edi 89fd DEFINE mov_ebp,esp 89E5 DEFINE mov_eax, B8 DEFINE mov_ebx, BB DEFINE mov_edx, BA DEFINE mov_eax,[eax] 8B00 DEFINE mov_ebx,[ebx] 8B1B DEFINE mov_ecx,[ecx] 8B09 DEFINE mov_edx,[edx] 8B12 DEFINE mov_[ebx],al 8803 DEFINE mov_[ebx],ax 668903 DEFINE mov_[ebx],eax 8903 DEFINE movsx_eax,BYTE_PTR_[eax] 0FBE00 DEFINE movsx_ebx,BYTE_PTR_[ebx] 0FBE1B DEFINE movsx_eax,WORD_PTR_[eax] 0FBF00 DEFINE movzx_eax,BYTE_PTR_[eax] 0FB600 DEFINE movzx_eax,WORD_PTR_[eax] 0FB700 DEFINE movzx_eax,al 0FB6C0 DEFINE mul_ebx F7E3 DEFINE imul_ebx F7EB DEFINE NULL 00000000 DEFINE not_eax F7D0 DEFINE or_eax,ebx 09D8 DEFINE pop_eax 58 DEFINE pop_ebx 5B DEFINE pop_ebp 5D DEFINE pop_edi 5F DEFINE push_eax 50 DEFINE push_ebx 53 DEFINE push_ebp 55 DEFINE push_edi 57 DEFINE ret C3 DEFINE sal_eax, C1E0 DEFINE sal_eax,cl D3F0 DEFINE shl_eax,cl D3E0 DEFINE sar_eax,cl D3F8 DEFINE shr_eax,cl D3E8 DEFINE seta_al 0F97C0 DEFINE setae_al 0F93C0 DEFINE setb_al 0F92C0 DEFINE setbe_al 0F96C0 DEFINE sete_al 0F94C0 DEFINE setle_al 0F9EC0 DEFINE setl_al 0F9CC0 DEFINE setge_al 0F9DC0 DEFINE setg_al 0F9FC0 DEFINE setne_al 0F95C0 DEFINE sub_ebx,eax 29C3 DEFINE test_eax,eax 85C0 DEFINE xchg_ebx,eax 93 DEFINE xor_eax,ebx 31D8
## Copyright (C) 2016 Jeremiah Orians
## This file is part of M2-Planet.
##
## M2-Planet is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## M2-Planet is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with M2-Planet. If not, see <http://www.gnu.org/licenses/>.
:_start
mov_ebp,esp ; Protect esp
;; Prepare argv
lea_eax,[ebp+DWORD] %4 ; ARGV_address = EBP + 4
push_eax ; Put argv on the stack
;; Prepare envp
mov_eax,ebp ; Address we need to load from
mov_eax,[eax] ; Get ARGC
add_eax, %2 ; OFFSET = ARGC + 2
sal_eax, !2 ; OFFSET = OFFSET * WORDSIZE
add_eax,ebp ; ENVP_address = ESP + OFFSET
push_eax ; Put envp on the stack
;; Stack offset
add_ebp, %4 ; Fix ebp
;; Perform the main loop
call %FUNCTION_main
push_eax ; Put return on stack
push_eax ; so that _exit gets the value
;; Exit to kernel
:FUNCTION_exit
:FUNCTION__exit
mov_ebx,eax ; Using the return code given by main
mov_eax, %1 ; Syscall exit
int !0x80 ; Exit with that code
### Copyright (C) 2016 Jeremiah Orians ### Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org> ### This file is part of M2-Planet. ### ### M2-Planet is free software: you can redistribute it and/or modify ### it under the terms of the GNU General Public License as published by ### the Free Software Foundation, either version 3 of the License, or ### (at your option) any later version. ### ### M2-Planet is distributed in the hope that it will be useful, ### but WITHOUT ANY WARRANTY; without even the implied warranty of ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ### GNU General Public License for more details. ### ### You should have received a copy of the GNU General Public License ### along with M2-Planet. If not, see <http://www.gnu.org/licenses/>. ### stage0's hex2 format ### !<label> 1 byte relative ### $<label> 2 byte address ### @<label> 2 byte relative ### &<label> 4 byte address ### %<label> 4 byte relative ### if you wish to use this header, you need to add :ELF_end to the end of your ### M1 or hex2 files. ## ELF Header :ELF_base 7F 45 4C 46 # e_ident[EI_MAG0-3] ELF's magic number 01 # e_ident[EI_CLASS] Indicating 32 bit 01 # e_ident[EI_DATA] Indicating little endianness 01 # e_ident[EI_VERSION] Indicating original elf 03 # e_ident[EI_OSABI] Set at 3 because FreeBSD is strict 00 # e_ident[EI_ABIVERSION] See above 00 00 00 00 00 00 00 # e_ident[EI_PAD] 02 00 # e_type Indicating Executable 03 00 # e_machine Indicating 386 01 00 00 00 # e_version Indicating original elf &_start # e_entry Address of the entry point %ELF_program_headers>ELF_base # e_phoff Address of program header table 00 00 00 00 # e_shoff Address of section header table 00 00 00 00 # e_flags 34 00 # e_ehsize Indicating our 52 Byte header 20 00 # e_phentsize size of a program header table 01 00 # e_phnum number of entries in program table 00 00 # e_shentsize size of a section header table 00 00 # e_shnum number of entries in section table 00 00 # e_shstrndx index of the section names :ELF_program_headers :ELF_program_header__text 01 00 00 00 # ph_type: PT-LOAD = 1 00 00 00 00 # ph_offset &ELF_base # ph_vaddr &ELF_base # ph_physaddr %ELF_end>ELF_base # ph_filesz %ELF_end>ELF_base # ph_memsz 07 00 00 00 # ph_flags: PF-X|PF-W|PF-R = 7 01 00 00 00 # ph_alignment :ELF_text
/* -*- c-file-style: "linux";indent-tabs-mode:t -*- */
/* Copyright (C) 2016 Jeremiah Orians
* Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org>
* This file is part of mescc-tools.
*
* mescc-tools is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* mescc-tools is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with mescc-tools. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "M2libc/bootstrappable.h"
/* Internal processing Constants */
// CONSTANT max_string 4096
#define max_string 4096
// CONSTANT PROCESSED 1
#define PROCESSED 1
// CONSTANT STR 2
#define STR 2
// CONSTANT NEWLINE 3
#define NEWLINE 3
/* Unique code for each architecture */
// CONSTANT KNIGHT 0
#define KNIGHT 0
// CONSTANT X86 3
#define X86 0x03
// CONSTANT AMD64 62
#define AMD64 0x3E
// CONSTANT ARMV7L 40
#define ARMV7L 0x28
// CONSTANT AARM64 183
#define AARM64 0xB7
// CONSTANT PPC64LE 21
#define PPC64LE 0x15
// CONSTANT RISCV32 243
#define RISCV32 0xF3
// CONSTANT RISCV64 65779
#define RISCV64 0x100F3 /* Because RISC-V unlike all other architectures does get a seperate e_machine when changing from 32 to 64bit */
/* How do you want that output? */
// CONSTANT HEX 16
#define HEX 16
// CONSTANT OCTAL 8
#define OCTAL 8
// CONSTANT BINARY 2
#define BINARY 2
/* Imported from stringify.c */
int stringify(char* s, int digits, int divisor, int value, int shift);
void LittleEndian(char* start, int ByteMode);
struct blob
{
struct blob* next;
int type;
char* Text;
char* Expression;
struct blob* hash_next;
};
struct Token
{
struct Token* next;
struct blob* contents;
char* filename;
int linenumber;
};
/* Globals */
FILE* source_file;
FILE* destination_file;
int BigEndian;
int ByteMode;
int Architecture;
int linenumber;
struct Token* token_list;
struct blob* blob_list;
struct blob* define_blob;
struct blob* newline_blob;
int blob_count;
char* SCRATCH;
struct blob** hash_table;
void line_error(char* filename, int linenumber)
{
fputs(filename, stderr);
fputs(":", stderr);
fputs(int2str(linenumber,10, FALSE), stderr);
fputs(" :", stderr);
}
void ClearScratch()
{
int i = 0;
int c = SCRATCH[i];
while(0 != c)
{
SCRATCH[i] = 0;
i = i + 1;
c = SCRATCH[i];
}
}
int GetHash(char* s)
{
int i = 5381;
while(0 != s[0])
{
i = (i << 5) + i + s[0];
s = s + 1;
}
return i & 0xFFFF;
}
struct blob* FindBlob()
{
int hash = GetHash(SCRATCH);
struct blob* i = hash_table[hash];
while(NULL != i)
{
if(match(SCRATCH, i->Text)) return i;
i = i->hash_next;
}
return NULL;
}
void AddHash(struct blob* a, char* s)
{
int i = GetHash(s);
a->hash_next = hash_table[i];
hash_table[i] = a;
}
void NewBlob(int size)
{
blob_count = blob_count + 1;
struct blob* a = calloc(1, sizeof(struct blob));
require(NULL != a, "Exhausted available memory\n");
a->Text = calloc(size + 1, sizeof(char));
require(NULL != a->Text, "Exhausted available memory\n");
int i = 0;
while(i <= size)
{
a->Text[i] = SCRATCH[i];
i = i + 1;
}
a->next = blob_list;
blob_list = a;
AddHash(a, SCRATCH);
}
struct Token* newToken(char* filename, int linenumber)
{
struct Token* p;
p = calloc (1, sizeof (struct Token));
require(NULL != p, "Exhausted available memory\n");
p->filename = filename;
p->linenumber = linenumber;
return p;
}
struct Token* reverse_list(struct Token* head)
{
struct Token* root = NULL;
struct Token* next;
while(NULL != head)
{
next = head->next;
head->next = root;
root = head;
head = next;
}
return root;
}
void purge_lineComment()
{
int c = fgetc(source_file);
while(!in_set(c, "\n\r"))
{
if(EOF == c) break;
c = fgetc(source_file);
}
}
struct Token* append_newline(struct Token* head, char* filename)
{
linenumber = linenumber + 1;
if(NULL == head) return NULL;
if(NEWLINE == head->contents->type)
{/* Don't waste whitespace*/
return head;
}
struct Token* lf = newToken(filename, linenumber);
lf->contents = newline_blob;
lf->next = head;
return lf;
}
struct Token* store_atom(struct Token* head, char c, char* filename)
{
ClearScratch();
int ch = c;
int i = 0;
do
{
SCRATCH[i] = ch;
ch = fgetc(source_file);
i = i + 1;
if(i >= max_string)
{
fputs("storing atom of size larger than max_string\n", stderr);
line_error(filename, linenumber);
fputc('\n', stderr);
exit(EXIT_FAILURE);
}
if(EOF == ch) break;
} while (!in_set(ch, "\t\n "));
head->contents = FindBlob();
if(NULL == head->contents)
{
NewBlob(i);
head->contents = blob_list;
}
if('\n' == ch)
{
return append_newline(head, filename);
}
return head;
}
struct blob* store_string(char c, char* filename)
{
ClearScratch();
int ch = c;
int i = 0;
do
{
SCRATCH[i] = ch;
i = i + 1;
if('\n' == ch) linenumber = linenumber + 1;
ch = fgetc(source_file);
require(EOF != ch, "Unmatched \"!\n");
if(max_string == i)
{
line_error(filename, linenumber);
fputs("String: ", stderr);
fputs(SCRATCH, stderr);
fputs(" exceeds max string size\n", stderr);
exit(EXIT_FAILURE);
}
} while(ch != c);
struct blob* a = FindBlob();
if(NULL == a)
{
NewBlob(i);
a = blob_list;
a->type = STR;
}
return a;
}
struct Token* Tokenize_Line(struct Token* head, char* filename)
{
int c;
struct Token* p;
linenumber = 1;
do
{
restart:
c = fgetc(source_file);
if(in_set(c, ";#"))
{
purge_lineComment();
head = append_newline(head, filename);
goto restart;
}
if(in_set(c, "\t "))
{
goto restart;
}
if('\n' == c)
{
head = append_newline(head, filename);
goto restart;
}
if(EOF == c)
{
head = append_newline(head, filename);
goto done;
}
p = newToken(filename, linenumber);
p->next = head;
if(in_set(c, "'\""))
{
p->contents = store_string(c, filename);
}
else
{
p = store_atom(p, c, filename);
}
head = p;
} while(TRUE);
done:
return head;
}
void line_macro(struct Token* p)
{
struct Token* i;
for(i = p; NULL != i; i = i->next)
{
if(define_blob == i->contents)
{
require(NULL != i->next, "Macro name must exist\n");
require(NULL != i->next->next, "Macro value must exist\n");
if(PROCESSED == i->next->contents->type)
{
line_error(i->filename, i->linenumber);
fputs("Multiple definitions for macro ", stderr);
fputs(i->next->contents->Text, stderr);
fputs("\n", stderr);
exit(EXIT_FAILURE);
}
i->contents = newline_blob;
if (STR == i->next->next->contents->type)
{
i->contents->Expression = i->next->next->contents->Text + 1;
}
else
{
i->next->contents->Expression = i->next->next->contents->Text;
}
i->next = i->next->next->next;
}
}
}
void hexify_string(struct blob* p)
{
char* table = "0123456789ABCDEF";
int i = strlen(p->Text);
int size;
if(HEX == ByteMode) size = (((i << 1) + i) + 12);
else if(OCTAL == ByteMode) size = (i << 2) + 1;
else if(BINARY == ByteMode) size = (i << 3) + i + 1;
else size = 1;
require(1 != size, "hexify_string lacked a valid bytemode\n");
char* d = calloc(size, sizeof(char));
require(NULL != d, "Exhausted available memory\n");
p->Expression = d;
char* S = p->Text;
if((KNIGHT == Architecture) && (HEX == ByteMode))
{
i = (((((i - 1) >> 2) + 1) << 3) + i);
while( 0 < i)
{
i = i - 1;
d[i] = '0';
}
}
if(HEX == ByteMode)
{
while(0 != S[0])
{
S = S + 1;
d[0] = table[S[0] >> 4];
d[1] = table[S[0] & 0xF];
d[2] = ' ';
d = d + 3;
}
}
else if(OCTAL == ByteMode)
{
while(0 != S[0])
{
S = S + 1;
d[0] = table[S[0] >> 6];
d[1] = table[(S[0] >> 3) & 0x7];
d[2] = table[S[0] & 0x7];
d[3] = ' ';
d = d + 4;
}
}
else if(BINARY == ByteMode)
{
while(0 != S[0])
{
S = S + 1;
d[0] = table[S[0] >> 7];
d[1] = table[(S[0] >> 6) & 0x1];
d[2] = table[(S[0] >> 5) & 0x1];
d[3] = table[(S[0] >> 4) & 0x1];
d[4] = table[(S[0] >> 3) & 0x1];
d[5] = table[(S[0] >> 2) & 0x1];
d[6] = table[(S[0] >> 1) & 0x1];
d[7] = table[S[0] & 0x1];
d[8] = ' ';
d = d + 9;
}
}
}
void process_string(struct blob* p)
{
struct blob* i;
for(i = p; NULL != i; i = i->next)
{
if(STR == i->type)
{
if('\'' == i->Text[0])
{
i->Expression = i->Text + 1;
}
else if('"' == i->Text[0])
{
hexify_string(i);
}
}
}
}
char* pad_nulls(int size, char* nil)
{
if(0 == size) return nil;
require(size > 0, "negative null padding not possible\n");
if(HEX == ByteMode) size = size * 2;
else if (OCTAL == ByteMode) size = size * 3;
else if (BINARY == ByteMode) size = size * 8;
char* s = calloc(size + 1, sizeof(char));
require(NULL != s, "Exhausted available memory\n");
int i = 0;
while(i < size)
{
s[i] = '0';
i = i + 1;
}
return s;
}
void preserve_other(struct blob* p)
{
struct blob* i;
char c;
for(i = p; NULL != i; i = i->next)
{
if((NULL == i->Expression) && !(i->type & PROCESSED))
{
c = i->Text[0];
if(in_set(c, "!@$~%&:^"))
{
i->Expression = i->Text;
}
else if('<' == c)
{
i->Expression = pad_nulls(strtoint(i->Text + 1), i->Text);
}
}
}
}
void bound_values(int displacement, int number_of_bytes, int low, int high)
{
if((high < displacement) || (displacement < low))
{
fputs("A displacement of ", stderr);
fputs(int2str(displacement, 10, TRUE), stderr);
fputs(" does not fit in ", stderr);
fputs(int2str(number_of_bytes, 10, TRUE), stderr);
fputs(" bytes\n", stderr);
exit(EXIT_FAILURE);
}
}
void range_check(int displacement, int number_of_bytes)
{
if(4 == number_of_bytes) return;
else if(3 == number_of_bytes)
{
bound_values(displacement, number_of_bytes, -8388608, 16777216);
return;
}
else if(2 == number_of_bytes)
{
bound_values(displacement, number_of_bytes, -32768, 65535);
return;
}
else if(1 == number_of_bytes)
{
bound_values(displacement, number_of_bytes, -128, 255);
return;
}
fputs("Received an invalid number of bytes in range_check\n", stderr);
exit(EXIT_FAILURE);
}
char* express_number(int value, char c)
{
char* ch = calloc(42, sizeof(char));
require(NULL != ch, "Exhausted available memory\n");
int size;
int number_of_bytes;
int shift;
if('!' == c)
{
number_of_bytes = 1;
value = value & 0xFF;
}
else if('@' == c)
{
number_of_bytes = 2;
value = value & 0xFFFF;
}
else if('~' == c)
{
number_of_bytes = 3;
value = value & 0xFFFFFF;
}
else if('%' == c)
{
number_of_bytes = 4;
value = value & 0xFFFFFFFF;
}
else
{
fputs("Given symbol ", stderr);
fputc(c, stderr);
fputs(" to express immediate value ", stderr);
fputs(int2str(value, 10, TRUE), stderr);
fputc('\n', stderr);
exit(EXIT_FAILURE);
}
range_check(value, number_of_bytes);
if(HEX == ByteMode)
{
size = number_of_bytes * 2;
shift = 4;
}
else if(OCTAL == ByteMode)
{
size = number_of_bytes * 3;
shift = 3;
}
else if(BINARY == ByteMode)
{
size = number_of_bytes * 8;
shift = 1;
}
else
{
fputs("Got invalid ByteMode in express_number\n", stderr);
exit(EXIT_FAILURE);
}
stringify(ch, size, ByteMode, value, shift);
if(!BigEndian) LittleEndian(ch, ByteMode);
return ch;
}
char* express_word(int value, char c)
{
char* s = calloc(43, sizeof(char));
s[0] = '.';
char* ch = s + 1;
require(NULL != ch, "Exhausted available memory\n");
int size;
int shift;
int immediate;
if('!' == c)
{
/* Corresponds to RISC-V I format */
immediate = (value & 0xFFF) << 20;
}
else if('@' == c)
{
/* Corresponds to RISC-V S format */
immediate = ((value & 0x1F) << 7) | ((value & 0xFE0) << (31 - 11));
}
else if('~' == c)
{
/* Corresponds with RISC-V U format */
if ((value & 0xFFF) < 0x800)
{
immediate = value & 0xFFFFF000;
}
else
{
immediate = (value & 0xFFFFF000) + 0x1000;
}
}
else if('%' == c)
{
/* provides an option for 32bit immediate constants */
immediate = value & 0xFFFFFFFF;
/* Drop the leading . */
ch = s;
}
else
{
fputs("Given symbol ", stderr);
fputc(c, stderr);
fputs(" to express immediate value ", stderr);
fputs(int2str(value, 10, TRUE), stderr);
fputc('\n', stderr);
exit(EXIT_FAILURE);
}
if(HEX == ByteMode)
{
size = 4 * 2;
shift = 4;
}
else if(OCTAL == ByteMode)
{
size = 4 * 3;
shift = 3;
}
else if(BINARY == ByteMode)
{
size = 4 * 8;
shift = 1;
}
else
{
fputs("Got invalid ByteMode in express_number\n", stderr);
exit(EXIT_FAILURE);
}
stringify(ch, size, ByteMode, immediate, shift);
if(!BigEndian) LittleEndian(ch, ByteMode);
return s;
}
void eval_immediates(struct blob* p)
{
struct blob* i;
int value;
for(i = p; NULL != i; i = i->next)
{
if(PROCESSED == i->type) continue;
else if(NEWLINE == i->type) continue;
else if('<' == i->Text[0]) continue;
else if(NULL == i->Expression)
{
if((X86 == Architecture) || (AMD64 == Architecture) || (ARMV7L == Architecture) || (AARM64 == Architecture) || (PPC64LE == Architecture))
{
if(in_set(i->Text[0], "%~@!"))
{
value = strtoint(i->Text + 1);
if(('0' == i->Text[1]) || (0 != value))
{
i->Expression = express_number(value, i->Text[0]);
}
}
}
else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
{
if(in_set(i->Text[0], "%~@!"))
{
value = strtoint(i->Text + 1);
if(('0' == i->Text[1]) || (0 != value))
{
i->Expression = express_word(value, i->Text[0]);
}
}
}
else if(KNIGHT == Architecture)
{
value = strtoint(i->Text);
if(('0' == i->Text[0]) || (0 != value))
{
i->Expression = express_number(value, '@');
}
}
else
{
fputs("Unknown architecture received in eval_immediates\n", stderr);
exit(EXIT_FAILURE);
}
}
}
}
void print_hex(struct Token* p)
{
struct Token* i;
for(i = p; NULL != i; i = i->next)
{
if(NEWLINE == i->contents->type)
{
if(NULL == i->next) fputc('\n', destination_file);
else if(NEWLINE != i->next->contents->type) fputc('\n', destination_file);
}
else if(NULL != i->contents->Expression)
{
fputs(i->contents->Expression, destination_file);
if(NEWLINE != i->next->contents->type) fputc(' ', destination_file);
}
else
{
line_error(i->filename, i->linenumber);
fputs("Received invalid other; ", stderr);
fputs(i->contents->Text, stderr);
fputs("\n", stderr);
exit(EXIT_FAILURE);
}
}
}
/* Standard C main program */
int main(int argc, char **argv)
{
BigEndian = TRUE;
Architecture = KNIGHT;
destination_file = stdout;
ByteMode = HEX;
char* filename;
char* arch;
blob_count = 2;
hash_table = calloc(65537, sizeof(struct blob*));
require(NULL != hash_table, "failed to allocate hash_table\n");
/* Create newline blob */
newline_blob = calloc(1, sizeof(struct blob));
require(NULL != newline_blob, "failed to allocate newline_blob\n");
newline_blob->Text = "\n";
newline_blob->Expression = "\n";
newline_blob->type = NEWLINE;
AddHash(newline_blob, "\n");
/* Start the blob list with DEFINE and newline */
blob_list = calloc(1, sizeof(struct blob));
require(NULL != blob_list, "failed to allocate DEFINE blob\n");
blob_list->Text = "DEFINE";
define_blob = blob_list;
blob_list->next = newline_blob;
AddHash(define_blob, "DEFINE");
/* Initialize scratch */
SCRATCH = calloc(max_string + 1, sizeof(char));
require(NULL != SCRATCH, "failed to allocate SCRATCH buffer");
int option_index = 1;
while(option_index <= argc)
{
if(NULL == argv[option_index])
{
option_index = option_index + 1;
}
else if(match(argv[option_index], "--big-endian"))
{
BigEndian = TRUE;
option_index = option_index + 1;
}
else if(match(argv[option_index], "--little-endian"))
{
BigEndian = FALSE;
option_index = option_index + 1;
}
else if(match(argv[option_index], "-A") || match(argv[option_index], "--architecture"))
{
arch = argv[option_index + 1];
if(match("knight-native", arch) || match("knight-posix", arch)) Architecture = KNIGHT;
else if(match("x86", arch)) Architecture = X86;
else if(match("amd64", arch)) Architecture = AMD64;
else if(match("armv7l", arch)) Architecture = ARMV7L;
else if(match("aarch64", arch)) Architecture = AARM64;
else if(match("ppc64le", arch)) Architecture = PPC64LE;
else if(match("riscv32", arch)) Architecture = RISCV32;
else if(match("riscv64", arch)) Architecture = RISCV64;
else
{
fputs("Unknown architecture: ", stderr);
fputs(arch, stderr);
fputs(" know values are: knight-native, knight-posix, x86, amd64, armv7l, aarch64, ppc64le, riscv32 and riscv64", stderr);
exit(EXIT_FAILURE);
}
option_index = option_index + 2;
}
else if(match(argv[option_index], "-b") || match(argv[option_index], "--binary"))
{
ByteMode = BINARY;
option_index = option_index + 1;
}
else if(match(argv[option_index], "-h") || match(argv[option_index], "--help"))
{
fputs("Usage: ", stderr);
fputs(argv[0], stderr);
fputs(" --file FILENAME1 {-f FILENAME2} (--big-endian|--little-endian) ", stderr);
fputs("[--architecture name]\nArchitectures: knight-native, knight-posix, x86, amd64, armv7, riscv32 and riscv64\n", stderr);
fputs("To leverage octal or binary output: --octal, --binary\n", stderr);
exit(EXIT_SUCCESS);
}
else if(match(argv[option_index], "-f") || match(argv[option_index], "--file"))
{
filename = argv[option_index + 1];
source_file = fopen(filename, "r");
if(NULL == source_file)
{
fputs("The file: ", stderr);
fputs(argv[option_index + 1], stderr);
fputs(" can not be opened!\n", stderr);
exit(EXIT_FAILURE);
}
token_list = Tokenize_Line(token_list, filename);
fclose(source_file);
option_index = option_index + 2;
}
else if(match(argv[option_index], "-o") || match(argv[option_index], "--output"))
{
destination_file = fopen(argv[option_index + 1], "w");
if(NULL == destination_file)
{
fputs("The file: ", stderr);
fputs(argv[option_index + 1], stderr);
fputs(" can not be opened!\n", stderr);
exit(EXIT_FAILURE);
}
option_index = option_index + 2;
}
else if(match(argv[option_index], "-O") || match(argv[option_index], "--octal"))
{
ByteMode = OCTAL;
option_index = option_index + 1;
}
else if(match(argv[option_index], "-V") || match(argv[option_index], "--version"))
{
fputs("M1 1.4.0\n", stdout);
exit(EXIT_SUCCESS);
}
else
{
fputs("Unknown option\n", stderr);
exit(EXIT_FAILURE);
}
}
if(NULL == token_list)
{
fputs("Either no input files were given or they were empty\n", stderr);
exit(EXIT_FAILURE);
}
token_list = reverse_list(token_list);
line_macro(token_list);
process_string(blob_list);
eval_immediates(blob_list);
preserve_other(blob_list);
print_hex(token_list);
fclose(destination_file);
return EXIT_SUCCESS;
}
### Copyright (C) 2016 Jeremiah Orians ### Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org> ### This file is part of M2-Planet. ### ### M2-Planet is free software: you can redistribute it and/or modify ### it under the terms of the GNU General Public License as published by ### the Free Software Foundation, either version 3 of the License, or ### (at your option) any later version. ### ### M2-Planet is distributed in the hope that it will be useful, ### but WITHOUT ANY WARRANTY; without even the implied warranty of ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ### GNU General Public License for more details. ### ### You should have received a copy of the GNU General Public License ### along with M2-Planet. If not, see <http://www.gnu.org/licenses/>. ### stage0's hex2 format ### !<label> 1 byte relative ### $<label> 2 byte address ### @<label> 2 byte relative ### &<label> 4 byte address ### %<label> 4 byte relative ### if you wish to use this header, you need to add :ELF_end to the end of your ### M1 or hex2 files. ## ELF Header :ELF_base 7F 45 4C 46 # e_ident[EI_MAG0-3] ELF's magic number 01 # e_ident[EI_CLASS] Indicating 32 bit 01 # e_ident[EI_DATA] Indicating little endianness 01 # e_ident[EI_VERSION] Indicating original elf 03 # e_ident[EI_OSABI] Set at 3 because FreeBSD is strict 00 # e_ident[EI_ABIVERSION] See above 00 00 00 00 00 00 00 # e_ident[EI_PAD] 02 00 # e_type Indicating Executable 03 00 # e_machine Indicating 386 01 00 00 00 # e_version Indicating original elf &_start # e_entry Address of the entry point %ELF_program_headers>ELF_base # e_phoff Address of program header table %ELF_section_headers>ELF_base # e_shoff Address of section header table 00 00 00 00 # e_flags 34 00 # e_ehsize Indicating our 52 Byte header 20 00 # e_phentsize size of a program header table 01 00 # e_phnum number of entries in program table 28 00 # e_shentsize size of a section header table 05 00 # e_shnum number of entries in section table 02 00 # e_shstrndx index of the section names :ELF_program_headers :ELF_program_header__text 01 00 00 00 # ph_type: PT-LOAD = 1 00 00 00 00 # ph_offset &ELF_base # ph_vaddr &ELF_base # ph_physaddr %ELF_end>ELF_base # ph_filesz %ELF_end>ELF_base # ph_memsz 07 00 00 00 # ph_flags: PF-X|PF-W|PF-R = 7 01 00 00 00 # ph_alignment :ELF_text
/* Copyright (C) 2016 Jeremiah Orians * This file is part of M2-Planet. * * M2-Planet is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * M2-Planet is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with M2-Planet. If not, see <http://www.gnu.org/licenses/>. */ #ifndef _SYS_TYPES_H #define _SYS_TYPES_H #ifndef __M2__ #include "../gcc_req.h" #endif typedef SCM ulong; typedef long ssize_t; typedef int pid_t; typedef long intptr_t; typedef ulong uintptr_t; typedef long clock_t; typedef int mode_t; typedef long dev_t; #endif
/* Copyright (C) 2016 Jeremiah Orians * This file is part of M2-Planet. * * M2-Planet is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * M2-Planet is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with M2-Planet. If not, see <http://www.gnu.org/licenses/>. */ #ifndef _STDDEF_H #define _STDDEF_H #include <sys/types.h> #define NULL 0 typedef long ptrdiff_t; typedef ulong size_t; #endif
/* Copyright (C) 2020 Jeremiah Orians
* This file is part of M2-Planet.
*
* M2-Planet is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* M2-Planet is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with M2-Planet. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _UNISTD_C
#define _UNISTD_C
#define NULL 0
#define __PATH_MAX 4096
void* malloc(unsigned size);
int access(char* pathname, int mode)
{
asm("lea_ebx,[esp+DWORD] %8"
"mov_ebx,[ebx]"
"lea_ecx,[esp+DWORD] %4"
"mov_ecx,[ecx]"
"mov_eax, %33"
"int !0x80");
}
int chdir(char* path)
{
asm("lea_ebx,[esp+DWORD] %4"
"mov_ebx,[ebx]"
"mov_eax, %12"
"int !0x80");
}
int fchdir(int fd)
{
asm("lea_ebx,[esp+DWORD] %4"
"mov_ebx,[ebx]"
"mov_eax, %133"
"int !0x80");
}
/* Defined in the libc */
void _exit(int value);
int fork()
{
asm("mov_eax, %2"
"mov_ebx, %0"
"int !0x80");
}
int waitpid (int pid, int* status_ptr, int options)
{
asm("lea_ebx,[esp+DWORD] %12"
"mov_ebx,[ebx]"
"lea_ecx,[esp+DWORD] %8"
"mov_ecx,[ecx]"
"lea_edx,[esp+DWORD] %4"
"mov_edx,[edx]"
"mov_eax, %7"
"int !0x80");
}
int execve(char* file_name, char** argv, char** envp)
{
asm("lea_ebx,[esp+DWORD] %12"
"mov_ebx,[ebx]"
"lea_ecx,[esp+DWORD] %8"
"mov_ecx,[ecx]"
"lea_edx,[esp+DWORD] %4"
"mov_edx,[edx]"
"mov_eax, %11"
"int !0x80");
}
int read(int fd, char* buf, unsigned count) {
asm("lea_ebx,[esp+DWORD] %12"
"mov_ebx,[ebx]"
"lea_ecx,[esp+DWORD] %8"
"mov_ecx,[ecx]"
"lea_edx,[esp+DWORD] %4"
"mov_edx,[edx]"
"mov_eax, %3"
"int !0x80");
}
int write(int fd, char* buf, unsigned count) {
asm("lea_ebx,[esp+DWORD] %12"
"mov_ebx,[ebx]"
"lea_ecx,[esp+DWORD] %8"
"mov_ecx,[ecx]"
"lea_edx,[esp+DWORD] %4"
"mov_edx,[edx]"
"mov_eax, %4"
"int !0x80");
}
int lseek(int fd, int offset, int whence)
{
asm("lea_ebx,[esp+DWORD] %12"
"mov_ebx,[ebx]"
"lea_ecx,[esp+DWORD] %8"
"mov_ecx,[ecx]"
"lea_edx,[esp+DWORD] %4"
"mov_edx,[edx]"
"mov_eax, %19"
"int !0x80");
}
int close(int fd)
{
asm("lea_ebx,[esp+DWORD] %4"
"mov_ebx,[ebx]"
"mov_eax, %6"
"int !0x80");
}
int unlink (char *filename)
{
asm("lea_ebx,[esp+DWORD] %4"
"mov_ebx,[ebx]"
"mov_eax, %10"
"int !0x80");
}
int _getcwd(char* buf, int size)
{
asm("lea_ebx,[esp+DWORD] %8"
"mov_ebx,[ebx]"
"lea_ecx,[esp+DWORD] %4"
"mov_ecx,[ecx]"
"mov_eax, %183"
"int !0x80");
}
char* getcwd(char* buf, unsigned size)
{
int c = _getcwd(buf, size);
if(0 == c) return NULL;
return buf;
}
char* getwd(char* buf)
{
return getcwd(buf, __PATH_MAX);
}
char* get_current_dir_name()
{
return getcwd(malloc(__PATH_MAX), __PATH_MAX);
}
int brk(void *addr)
{
asm("mov_eax,[esp+DWORD] %4"
"push_eax"
"mov_eax, %45"
"pop_ebx"
"int !0x80");
}
struct utsname
{
char sysname[65]; /* Operating system name (e.g., "Linux") */
char nodename[65]; /* Name within "some implementation-defined network" */
char release[65]; /* Operating system release (e.g., "2.6.28") */
char version[65]; /* Operating system version */
char machine[65]; /* Hardware identifier */
};
int uname(struct utsname* unameData)
{
asm("lea_ebx,[esp+DWORD] %4"
"mov_ebx,[ebx]"
"mov_eax, %109"
"int !0x80");
}
#endif
/* Copyright (C) 2016 Jeremiah Orians
* This file is part of M2-Planet.
*
* M2-Planet is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* M2-Planet is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with M2-Planet. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __FCNTL_C
#define __FCNTL_C
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#define O_CREAT 00100
#define O_EXCL 00200
#define O_TRUNC 001000
#define O_APPEND 002000
#define S_IXUSR 00100
#define S_IWUSR 00200
#define S_IRUSR 00400
#define S_IRWXU 00700
int _open(char* name, int flag, int mode)
{
asm("lea_ebx,[esp+DWORD] %12"
"mov_ebx,[ebx]"
"lea_ecx,[esp+DWORD] %8"
"mov_ecx,[ecx]"
"lea_edx,[esp+DWORD] %4"
"mov_edx,[edx]"
"mov_eax, %5"
"int !0x80");
}
#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
#endif
/* Copyright (C) 2016 Jeremiah Orians
* This file is part of M2-Planet.
*
* M2-Planet is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* M2-Planet is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with M2-Planet. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _FCNTL_C
#define _FCNTL_C
#ifdef __M2__
#if __uefi__
#include <uefi/fcntl.c>
#elif __i386__
#include <x86/linux/fcntl.c>
#elif __x86_64__
#include <amd64/linux/fcntl.c>
#elif __arm__
#include <armv7l/linux/fcntl.c>
#elif __aarch64__
#include <aarch64/linux/fcntl.c>
#elif __riscv && __riscv_xlen==32
#include <riscv32/linux/fcntl.c>
#elif __riscv && __riscv_xlen==64
#include <riscv64/linux/fcntl.c>
#elif __knight_posix__
#include <knight/linux/fcntl.c>
#elif __knight__
#include <knight/native/fcntl.c>
#else
#error arch not supported
#endif
#else
extern int _open(char* name, int flag, int mode);
#endif
int errno;
int open(char* name, int flag, int mode)
{
int fd = _open(name, flag, mode);
if(0 > fd)
{
errno = -fd;
fd = -1;
}
return fd;
}
#endif
/* Copyright (C) 2020 Jeremiah Orians
* This file is part of M2-Planet.
*
* M2-Planet is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* M2-Planet is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with M2-Planet. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _SYS_STAT_C
#define _SYS_STAT_C
#include <sys/types.h>
#define S_IRWXU 00700
#define S_IXUSR 00100
#define S_IWUSR 00200
#define S_IRUSR 00400
#define S_ISUID 04000
#define S_ISGID 02000
#define S_IXGRP 00010
#define S_IXOTH 00001
#define S_IRGRP 00040
#define S_IROTH 00004
#define S_IWGRP 00020
#define S_IWOTH 00002
#define S_IRWXG 00070
#define S_IRWXO 00007
int chmod(char *pathname, int mode)
{
asm("lea_ebx,[esp+DWORD] %8"
"mov_ebx,[ebx]"
"lea_ecx,[esp+DWORD] %4"
"mov_ecx,[ecx]"
"mov_eax, %15"
"int !0x80");
}
int fchmod(int a, mode_t b)
{
asm("lea_ebx,[esp+DWORD] %8"
"mov_ebx,[ebx]"
"lea_ecx,[esp+DWORD] %4"
"mov_ecx,[ecx]"
"mov_eax, %94"
"int !0x80");
}
int mkdir(char const* a, mode_t b)
{
asm("lea_ebx,[esp+DWORD] %8"
"mov_ebx,[ebx]"
"lea_ecx,[esp+DWORD] %4"
"mov_ecx,[ecx]"
"mov_eax, %39"
"int !0x80");
}
int mknod(char const* a, mode_t b, dev_t c)
{
asm("lea_ebx,[esp+DWORD] %12"
"mov_ebx,[ebx]"
"lea_ecx,[esp+DWORD] %8"
"mov_ecx,[ecx]"
"lea_edx,[esp+DWORD] %4"
"mov_edx,[edx]"
"mov_eax, %14"
"int !0x80");
}
mode_t umask(mode_t m)
{
asm("lea_ebx,[esp+DWORD] %4"
"mov_ebx,[ebx]"
"mov_eax, %60"
"int !0x80");
}
#endif
/* Copyright (C) 2016 Jeremiah Orians
* This file is part of M2-Planet.
*
* M2-Planet is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* M2-Planet is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with M2-Planet. If not, see <http://www.gnu.org/licenses/>.
*/
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#define EXIT_FAILURE 1
#define EXIT_SUCCESS 0
#define _IN_USE 1
#define _NOT_IN_USE 0
typedef char wchar_t;
void exit(int value);
struct _malloc_node
{
struct _malloc_node *next;
void* block;
size_t size;
int used;
};
struct _malloc_node* _allocated_list;
struct _malloc_node* _free_list;
/********************************
* The core POSIX malloc *
********************************/
long _malloc_ptr;
long _brk_ptr;
void* _malloc_brk(unsigned size)
{
if(NULL == _brk_ptr)
{
_brk_ptr = brk(0);
_malloc_ptr = _brk_ptr;
}
if(_brk_ptr < _malloc_ptr + size)
{
_brk_ptr = brk(_malloc_ptr + size);
if(-1 == _brk_ptr) return 0;
}
long old_malloc = _malloc_ptr;
_malloc_ptr = _malloc_ptr + size;
return old_malloc;
}
void __init_malloc()
{
_free_list = NULL;
_allocated_list = NULL;
return;
}
/************************************************************************
* Handle with the tricky insert behaviors for our nodes *
* As free lists must be sorted from smallest to biggest to enable *
* cheap first fit logic *
* The free function however is rarely called, so it can kick sand and *
* do things the hard way *
************************************************************************/
void _malloc_insert_block(struct _malloc_node* n, int used)
{
/* Allocated block doesn't care about order */
if(_IN_USE == used)
{
/* Literally just be done as fast as possible */
n->next = _allocated_list;
_allocated_list = n;
return;
}
/* sanity check garbage */
if(_NOT_IN_USE != used) exit(EXIT_FAILURE);
if(_NOT_IN_USE != n->used) exit(EXIT_FAILURE);
if(NULL != n->next) exit(EXIT_FAILURE);
/* Free block really does care about order */
struct _malloc_node* i = _free_list;
struct _malloc_node* last = NULL;
while(NULL != i)
{
/* sort smallest to largest */
if(n->size <= i->size)
{
/* Connect */
n->next = i;
/* If smallest yet */
if(NULL == last) _free_list = n;
/* or just another average block */
else last->next = n;
return;
}
/* iterate */
last = i;
i = i->next;
}
/* looks like we are the only one */
if(NULL == last) _free_list = n;
/* or we are the biggest yet */
else last->next = n;
}
/************************************************************************
* We only mark a block as unused, we don't actually deallocate it here *
* But rather shove it into our _free_list *
************************************************************************/
void free(void* ptr)
{
/* just in case someone needs to quickly turn it off */
#ifndef _MALLOC_DISABLE_FREE
struct _malloc_node* i = _allocated_list;
struct _malloc_node* last = NULL;
/* walk the whole freaking list if needed to do so */
while(NULL != i)
{
/* did we find it? */
if(i->block == ptr)
{
/* detach the block */
if(NULL == last) _allocated_list = i->next;
/* in a way that doesn't break the allocated list */
else last->next = i->next;
/* insert into free'd list */
i->used = _NOT_IN_USE;
i->next = NULL;
_malloc_insert_block(i, _NOT_IN_USE);
return;
}
/* iterate */
last = i;
i = i->next;
}
/* we received a pointer to a block that wasn't allocated */
/* Bail *HARD* because I don't want to cover this edge case */
exit(EXIT_FAILURE);
#endif
/* if free is disabled, there is nothing to do */
return;
}
/************************************************************************
* find if there is any "FREED" blocks big enough to sit on our memory *
* budget's face and ruin its life. Respectfully of course *
************************************************************************/
void* _malloc_find_free(unsigned size)
{
struct _malloc_node* i = _free_list;
struct _malloc_node* last = NULL;
/* Walk the whole list if need be */
while(NULL != i)
{
/* see if anything in it is equal or bigger than what I need */
if((_NOT_IN_USE == i->used) && (i->size > size))
{
/* disconnect from list ensuring we don't break free doing so */
if(NULL == last) _free_list = i->next;
else last->next = i->next;
/* insert into allocated list */
i->used = _IN_USE;
i->next = NULL;
_malloc_insert_block(i, _IN_USE);
return i->block;
}
/* iterate (will loop forever if you get this wrong) */
last = i;
i = i->next;
}
/* Couldn't find anything big enough */
return NULL;
}
/************************************************************************
* Well we couldn't find any memory good enough to satisfy our needs so *
* we are going to have to go beg for some memory on the street corner *
************************************************************************/
void* _malloc_add_new(unsigned size)
{
struct _malloc_node* n;
#ifdef __uefi__
n = _malloc_uefi(sizeof(struct _malloc_node));
/* Check if we were beaten */
if(NULL == n) return NULL;
n->block = _malloc_uefi(size);
#else
n = _malloc_brk(sizeof(struct _malloc_node));
/* Check if we were beaten */
if(NULL == n) return NULL;
n->block = _malloc_brk(size);
#endif
/* check if we were robbed */
if(NULL == n->block) return NULL;
/* Looks like we made it home safely */
n->size = size;
n->next = NULL;
n->used = _IN_USE;
/* lets pop the cork and party */
_malloc_insert_block(n, _IN_USE);
return n->block;
}
/************************************************************************
* Safely iterates over all malloc nodes and frees them *
************************************************************************/
void __malloc_node_iter(struct _malloc_node* node, FUNCTION _free)
{
struct _malloc_node* current;
while(node != NULL)
{
current = node;
node = node->next;
_free(current->block);
_free(current);
}
}
/************************************************************************
* Runs a callback with all previously allocated nodes. *
* This can be useful if operating system does not do any clean up. *
************************************************************************/
void* _malloc_release_all(FUNCTION _free)
{
__malloc_node_iter(_allocated_list, _free);
__malloc_node_iter(_free_list, _free);
}
/************************************************************************
* Provide a POSIX standardish malloc function to keep things working *
************************************************************************/
void* malloc(unsigned size)
{
/* skip allocating nothing */
if(0 == size) return NULL;
/* use one of the standard block sizes */
size_t max = 1 << 30;
size_t used = 256;
while(used < size)
{
used = used << 1;
/* fail big allocations */
if(used > max) return NULL;
}
/* try the cabinets around the house */
void* ptr = _malloc_find_free(used);
/* looks like we need to get some more from the street corner */
if(NULL == ptr)
{
ptr = _malloc_add_new(used);
}
/* hopefully you can handle NULL pointers, good luck */
return ptr;
}
/************************************************************************
* Provide a POSIX standardish memset function to keep things working *
************************************************************************/
void* memset(void* ptr, int value, int num)
{
char* s;
/* basically walk the block 1 byte at a time and set it to any value you want */
for(s = ptr; 0 < num; num = num - 1)
{
s[0] = value;
s = s + 1;
}
return ptr;
}
/************************************************************************
* Provide a POSIX standardish calloc function to keep things working *
************************************************************************/
void* calloc(int count, int size)
{
/* if things get allocated, we are good*/
void* ret = malloc(count * size);
/* otherwise good luck */
if(NULL == ret) return NULL;
memset(ret, 0, (count * size));
return ret;
}
/* USED EXCLUSIVELY BY MKSTEMP */
void __set_name(char* s, int i)
{
s[5] = '0' + (i % 10);
i = i / 10;
s[4] = '0' + (i % 10);
i = i / 10;
s[3] = '0' + (i % 10);
i = i / 10;
s[2] = '0' + (i % 10);
i = i / 10;
s[1] = '0' + (i % 10);
i = i / 10;
s[0] = '0' + i;
}
/************************************************************************
* Provide a POSIX standardish mkstemp function to keep things working *
************************************************************************/
int mkstemp(char *template)
{
/* get length of template */
int i = 0;
while(0 != template[i]) i = i + 1;
i = i - 1;
/* String MUST be more than 6 characters in length */
if(i < 6) return -1;
/* Sanity check the string matches the template requirements */
int count = 6;
int c;
while(count > 0)
{
c = template[i];
/* last 6 chars must be X */
if('X' != c) return -1;
template[i] = '0';
i = i - 1;
count = count - 1;
}
int fd = -1;
count = -1;
/* open will return -17 or other values */
while(0 > fd)
{
/* Just give up after the planet has blown up */
if(9000 < count) return -1;
/* Try up to 9000 unique filenames before stopping */
count = count + 1;
__set_name(template+i+1, count);
/* Pray we can */
fd = open(template, O_RDWR | O_CREAT | O_EXCL, 00600);
}
/* well that only took count many tries */
return fd;
}
/************************************************************************
* wcstombs - convert a wide-character string to a multibyte string *
* because seriously UEFI??? UTF-16 is a bad design choice but I guess *
* they were drinking pretty hard when they designed UEFI; it is DOS *
* but somehow they magically found ways of making it worse *
************************************************************************/
size_t wcstombs(char* dest, char* src, size_t n)
{
int i = 0;
do
{
/* UTF-16 is 2bytes per char and that first byte maps good enough to ASCII */
dest[i] = src[2 * i];
if(dest[i] == 0)
{
break;
}
i = i + 1;
n = n - 1;
} while (n != 0);
return i;
}
/************************************************************************
* getenv - get an environmental variable *
************************************************************************/
size_t _strlen(char const* str)
{
size_t i = 0;
while(0 != str[i]) i = i + 1;
return i;
}
int _strncmp(char const* lhs, char const* rhs, size_t count)
{
size_t i = 0;
while(count > i)
{
if(0 == lhs[i]) break;
if(lhs[i] != rhs[i]) return lhs[i] - rhs[i];
i = i + 1;
}
return 0;
}
char** _envp;
char* getenv (char const* name)
{
char** p = _envp;
char* q;
int length = _strlen(name);
while (p[0] != 0)
{
if(_strncmp(name, p[0], length) == 0)
{
q = p[0] + length;
if(q[0] == '=')
return q + 1;
}
p += sizeof(char**); /* M2 pointer arithemtic */
}
return 0;
}
/************************************************************************
* setenv - set an environmental variable *
************************************************************************/
char* _strcpy(char* dest, char const* src)
{
int i = 0;
while (0 != src[i])
{
dest[i] = src[i];
i = i + 1;
}
dest[i] = 0;
return dest;
}
int setenv(char const *s, char const *v, int overwrite_p)
{
char** p = _envp;
int length = _strlen(s);
char* q;
while (p[0] != 0)
{
if (_strncmp (s, p[0], length) == 0)
{
q = p[0] + length;
if (q[0] == '=')
break;
}
p += sizeof(char**); /* M2 pointer arithemtic */
}
char *entry = malloc (length + _strlen(v) + 2);
int end_p = p[0] == 0;
p[0] = entry;
_strcpy(entry, s);
_strcpy(entry + length, "=");
_strcpy(entry + length + 1, v);
entry[length + _strlen(v) + 2] = 0;
if (end_p != 0)
p[1] = 0;
return 0;
}
/* Copyright (C) 2016 Jeremiah Orians
* This file is part of M2-Planet.
*
* M2-Planet is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* M2-Planet is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with M2-Planet. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _STDIO_H
#define _STDIO_H
#ifdef __M2__
/* Actual format of FILE */
struct __IO_FILE
{
int fd;
int bufmode; /* O_RDONLY = 0, O_WRONLY = 1 */
int bufpos;
int file_pos;
int buflen;
char* buffer;
struct __IO_FILE* next;
struct __IO_FILE* prev;
};
/* Now give us the FILE we all love */
typedef struct __IO_FILE FILE;
#include <stdio.c>
#else
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
/* Required constants */
/* For file I/O*/
#define EOF -1
#define BUFSIZ 4096
/* For lseek */
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
/* Actual format of FILE */
struct __IO_FILE
{
int fd;
int bufmode; /* 0 = no buffer, 1 = read, 2 = write */
int bufpos;
int buflen;
char* buffer;
};
/* Now give us the FILE we all love */
typedef struct __IO_FILE FILE;
/* Required variables */
extern FILE* stdin;
extern FILE* stdout;
extern FILE* stderr;
/* Standard C functions */
/* Getting */
extern int fgetc(FILE* f);
extern int getchar();
extern char* fgets(char* str, int count, FILE* stream);
extern size_t fread( void* buffer, size_t size, size_t count, FILE* stream );
/* Putting */
extern void fputc(char s, FILE* f);
extern void putchar(char s);
extern int fputs(char const* str, FILE* stream);
extern int puts(char const* str);
extern size_t fwrite(void const* buffer, size_t size, size_t count, FILE* stream );
/* File management */
extern FILE* fopen(char const* filename, char const* mode);
extern int fclose(FILE* stream);
extern int fflush(FILE* stream);
/* File Positioning */
extern int ungetc(int ch, FILE* stream);
extern long ftell(FILE* stream);
extern int fseek(FILE* f, long offset, int whence);
extern void rewind(FILE* f);
#endif
#endif
/* Copyright (C) 2016 Jeremiah Orians
* This file is part of M2-Planet.
*
* M2-Planet is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* M2-Planet is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with M2-Planet. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stddef.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
/* Required constants */
/* For file I/O*/
#define EOF 0xFFFFFFFF
#define BUFSIZ 0x1000
/* For lseek */
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
/* Required variables */
FILE* stdin;
FILE* stdout;
FILE* stderr;
FILE* __list;
void __init_io()
{
__list = NULL;
stdin = calloc(1, sizeof(FILE));
stdin->fd = STDIN_FILENO;
stdin->bufmode = O_RDONLY;
stdin->buflen = 1;
stdin->buffer = calloc(2, sizeof(char));
stdout = calloc(1, sizeof(FILE));
stdout->fd = STDOUT_FILENO;
stdout->bufmode = O_WRONLY;
stdout->buflen = 512;
stdout->buffer = calloc(514, sizeof(char));
stderr = calloc(1, sizeof(FILE));
stderr->fd = STDERR_FILENO;
stderr->bufmode = O_WRONLY;
stderr->buflen = 512;
stderr->buffer = calloc(514, sizeof(char));
}
/* Flush all IO on exit */
int fflush(FILE* stream);
void __kill_io()
{
fflush(stdout);
fflush(stderr);
while(NULL != __list)
{
fflush(__list);
__list = __list->next;
}
}
/* Standard C functions */
/* Getting */
int read(int fd, char* buf, unsigned count);
int fgetc(FILE* f)
{
/* Only read on read buffers */
if(O_WRONLY == f->bufmode) return EOF;
/* Deal with stdin */
if(STDIN_FILENO == f->fd)
{
f->bufpos = 0;
int r = read(f->fd, f->buffer, 1);
/* Catch special case of STDIN gets nothing (AN EOF) */
if(0 == r) return EOF;
}
/* Catch EOF */
if(f->buflen <= f->bufpos) return EOF;
/* Deal with standard case */
int ret = f->buffer[f->bufpos];
f->bufpos = f->bufpos + 1;
/* Ensure 0xFF doesn't return EOF */
return (ret & 0xFF);
}
size_t fread( void* buffer, size_t size, size_t count, FILE* stream )
{
if(0 == size) return 0;
if(0 == count) return 0;
long n = size + count - 1;
char* p = buffer;
long i;
unsigned c;
for(i = 0; i < n; i = i + 1)
{
c = fgetc(stream);
if(EOF == c) return (i/size);
p[i] = c;
}
return (i/size);
}
int getchar()
{
return fgetc(stdin);
}
char* fgets(char* str, int count, FILE* stream)
{
int i = 0;
int ch;
while(i < count)
{
ch = fgetc(stream);
if(EOF == ch) break;
str[i] = ch;
i = i + 1;
if('\n' == ch) break;
}
return str;
}
/* Putting */
void fputc(char s, FILE* f)
{
/* Only write on write buffers */
if(O_RDONLY == f->bufmode) return;
/* Add to buffer */
f->buffer[f->bufpos] = s;
f->bufpos = f->bufpos + 1;
/* Flush if full or '\n' */
if(f->bufpos == f->buflen) fflush(f);
else if(('\n' == s) && (2 >= f->fd)) fflush(f);
}
size_t fwrite(void const* buffer, size_t size, size_t count, FILE* stream )
{
long n = size * count;
if(0 == n) return 0;
char* p = buffer;
int c;
long i;
for(i=0; i < n; i = i + 1)
{
c = p[i];
fputc(c, stream);
}
return (i/size);
}
void putchar(char s)
{
fputc(s, stdout);
}
int fputs(char const* str, FILE* stream)
{
while(0 != str[0])
{
fputc(str[0], stream);
str = str + 1;
}
return 0;
}
int puts(char const* str)
{
fputs(str, stdout);
fputc('\n', stdout);
return 0;
}
int lseek(int fd, int offset, int whence);
/* File management */
FILE* fopen(char const* filename, char const* mode)
{
int f;
FILE* fi = calloc(1, sizeof(FILE));
fi->next = __list;
if(NULL != __list) __list->prev = fi;
__list = fi;
int size;
if('w' == mode[0]) f = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 00600);
else f = open(filename, 0, 0); /* Everything else is a read */
/* Negative numbers are error codes */
if(0 > f)
{
return 0;
}
if('w' == mode[0])
{
/* Buffer as much as possible */
fi->buffer = malloc(BUFSIZ * sizeof(char));
fi->buflen = BUFSIZ;
fi->bufmode = O_WRONLY;
}
else
{
/* Get enough buffer to read it all */
size = lseek(f, 0, SEEK_END);
fi->buffer = malloc((size + 1) * sizeof(char));
fi->buflen = size;
fi->bufmode = O_RDONLY;
/* Now read it all */
lseek(f, 0, SEEK_SET);
read(f, fi->buffer, size);
}
fi->fd = f;
return fi;
}
FILE* fdopen(int fd, char* mode)
{
FILE* fi = calloc(1, sizeof(FILE));
fi->next = __list;
if(NULL != __list) __list->prev = fi;
__list = fi;
int size;
if('w' == mode[0])
{
/* Buffer as much as possible */
fi->buffer = malloc(BUFSIZ * sizeof(char));
fi->buflen = BUFSIZ;
fi->bufmode = O_WRONLY;
}
else
{
/* Get enough buffer to read it all */
size = lseek(fd, 0, SEEK_END);
fi->buffer = malloc((size + 1) * sizeof(char));
fi->buflen = size;
fi->bufmode = O_RDONLY;
/* Now read it all */
lseek(fd, 0, SEEK_SET);
read(fd, fi->buffer, size);
}
fi->fd = fd;
return fi;
}
int write(int fd, char* buf, unsigned count);
int fflush(FILE* stream)
{
/* We only need to flush on writes */
if(O_RDONLY == stream->bufmode) return 0;
/* If nothing to flush */
if(0 ==stream->bufpos) return 0;
/* The actual flushing */
int error = write(stream->fd, stream->buffer, stream->bufpos);
/* Keep track of position */
stream->file_pos = stream->file_pos + stream->bufpos;
stream->bufpos = 0;
return error;
}
int close(int fd);
int fclose(FILE* stream)
{
/* Deal with STDIN, STDOUT and STDERR */
/* No close for you */
if(2 >= stream->fd) return 0;
/* We only need to flush on writes */
if(O_WRONLY == stream->bufmode)
{
fflush(stream);
}
/* Need to keep the File Descriptor for a moment */
int fd = stream->fd;
/* Remove from __list */
if(NULL != stream->prev) stream->prev->next = stream->next;
if(NULL != stream->next) stream->next->prev = stream->prev;
/* Deal with special case of first node in __list */
if (__list == stream) __list = __list->next;
/* Free up the buffer and struct used for FILE */
free(stream->buffer);
free(stream);
/* Do the actual closing */
return close(fd);
}
int unlink(char* filename);
/* File Removal */
int remove(char *pathname)
{
return unlink(pathname);
}
/* File Positioning */
int ungetc(int ch, FILE* stream)
{
/* Deal with STDIN, STDOUT and STDERR */
/* No ungetc for you */
if(2 >= stream->fd) return EOF;
/* You can't unget on a write stream! */
if(O_WRONLY == stream->bufmode) return EOF;
/* Don't underflow */
if(0 == stream->bufpos) return EOF;
/* Don't let crap be shoved into read stream */
if(stream->buffer[stream->bufpos - 1] != ch) return EOF;
stream->bufpos = stream->bufpos - 1;
return ch;
}
long ftell(FILE* stream)
{
/* Deal with STDIN, STDOUT and STDERR */
/* No ftell for you */
if(2 >= stream->fd) return 0;
/* Deal with buffered output */
if(O_WRONLY == stream->bufmode) return stream->file_pos + stream->bufpos;
/* Deal with read */
return stream->bufpos;
}
int fseek(FILE* f, long offset, int whence)
{
/* Deal with STDIN, STDOUT and STDERR */
/* No seek and destroy missions */
if(2 >= f->fd) return 0;
/* Deal with ugly case */
if(O_WRONLY == f->bufmode)
{
fflush(f);
return lseek(f->fd, offset, whence);
}
/* Deal with read mode */
int pos;
if(SEEK_SET == whence)
{
pos = offset;
}
else if(SEEK_CUR == whence)
{
pos = f->bufpos + offset;
}
else if(SEEK_END == whence)
{
pos = f->buflen + offset;
}
else return -1;
if(pos < 0) return -1;
if(pos > f->buflen) return -1;
f->bufpos = pos;
return pos;
}
void rewind(FILE* f)
{
fseek(f, 0, SEEK_SET);
}
/* -*- c-file-style: "linux";indent-tabs-mode:t -*- */
/* Copyright (C) 2017 Jeremiah Orians
* Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org>
* This file is part of mescc-tools
*
* mescc-tools is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* mescc-tools is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with mescc-tools. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include "M2libc/bootstrappable.h"
#define max_string 4096
#define TRUE 1
#define FALSE 0
#define KNIGHT 0
#define X86 0x03
#define AMD64 0x3E
#define ARMV7L 0x28
#define AARM64 0xB7
#define PPC64LE 0x15
#define RISCV32 0xF3
#define RISCV64 0x100F3 /* Because RISC-V unlike all other architectures does get a seperate e_machine when changing from 32 to 64bit */
#define HEX 16
#define OCTAL 8
#define BINARY 2
struct input_files
{
struct input_files* next;
char* filename;
};
struct entry
{
struct entry* next;
unsigned target;
char* name;
};
/* -*- c-file-style: "linux";indent-tabs-mode:t -*- */
/* Copyright (C) 2017 Jeremiah Orians
* Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org>
* This file is part of mescc-tools
*
* mescc-tools is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* mescc-tools is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with mescc-tools. If not, see <http://www.gnu.org/licenses/>.
*/
#include "hex2_globals.h"
/* Globals */
FILE* output;
struct entry** jump_tables;
int BigEndian;
int Base_Address;
int Architecture;
int ByteMode;
int exec_enable;
int ip;
char* scratch;
char* filename;
int linenumber;
int ALIGNED;
/* For processing bytes */
int hold;
int toggle;
void line_error()
{
fputs(filename, stderr);
fputs(":", stderr);
fputs(int2str(linenumber, 10, FALSE), stderr);
fputs(" :", stderr);
}
int consume_token(FILE* source_file)
{
int i = 0;
int c = fgetc(source_file);
while(!in_set(c, " \t\n>"))
{
scratch[i] = c;
i = i + 1;
c = fgetc(source_file);
require(max_string > i, "Consumed token exceeds length restriction\n");
if(EOF == c) break;
}
return c;
}
int Throwaway_token(FILE* source_file)
{
int c;
do
{
c = fgetc(source_file);
if(EOF == c) break;
} while(!in_set(c, " \t\n>"));
return c;
}
int length(char* s)
{
int i = 0;
while(0 != s[i]) i = i + 1;
return i;
}
void Clear_Scratch(char* s)
{
do
{
s[0] = 0;
s = s + 1;
} while(0 != s[0]);
}
void Copy_String(char* a, char* b)
{
while(0 != a[0])
{
b[0] = a[0];
a = a + 1;
b = b + 1;
}
}
int GetHash(char* s)
{
int i = 5381;
while(0 != s[0])
{
i = i * 31 + s[0];
s = s + 1;
}
return (i & 0xFFFF);
}
unsigned GetTarget(char* c)
{
struct entry* i;
for(i = jump_tables[GetHash(c)]; NULL != i; i = i->next)
{
if(match(c, i->name))
{
return i->target;
}
}
fputs("Target label ", stderr);
fputs(c, stderr);
fputs(" is not valid\n", stderr);
exit(EXIT_FAILURE);
}
int storeLabel(FILE* source_file, int ip)
{
struct entry* entry = calloc(1, sizeof(struct entry));
require(NULL != entry, "failed to allocate entry\n");
/* Ensure we have target address */
entry->target = ip;
/* Store string */
int c = consume_token(source_file);
entry->name = calloc(length(scratch) + 1, sizeof(char));
require(NULL != entry->name, "failed to allocate entry->name\n");
Copy_String(scratch, entry->name);
Clear_Scratch(scratch);
/* Prepend to list */
int h = GetHash(entry->name);
entry->next = jump_tables[h];
jump_tables[h] = entry;
return c;
}
void range_check(int displacement, int number_of_bytes)
{
if(4 == number_of_bytes) return;
else if (3 == number_of_bytes)
{
if((8388607 < displacement) || (displacement < -8388608))
{
fputs("A displacement of ", stderr);
fputs(int2str(displacement, 10, TRUE), stderr);
fputs(" does not fit in 3 bytes\n", stderr);
exit(EXIT_FAILURE);
}
return;
}
else if (2 == number_of_bytes)
{
if((32767 < displacement) || (displacement < -32768))
{
fputs("A displacement of ", stderr);
fputs(int2str(displacement, 10, TRUE), stderr);
fputs(" does not fit in 2 bytes\n", stderr);
exit(EXIT_FAILURE);
}
return;
}
else if (1 == number_of_bytes)
{
if((127 < displacement) || (displacement < -128))
{
fputs("A displacement of ", stderr);
fputs(int2str(displacement, 10, TRUE), stderr);
fputs(" does not fit in 1 byte\n", stderr);
exit(EXIT_FAILURE);
}
return;
}
fputs("Invalid number of bytes given\n", stderr);
exit(EXIT_FAILURE);
}
void outputPointer(int displacement, int number_of_bytes)
{
unsigned value = displacement;
/* HALT HARD if we are going to do something BAD*/
range_check(displacement, number_of_bytes);
if(BigEndian)
{ /* Deal with BigEndian */
if(4 == number_of_bytes) fputc((value >> 24), output);
if(3 <= number_of_bytes) fputc(((value >> 16)%256), output);
if(2 <= number_of_bytes) fputc(((value >> 8)%256), output);
if(1 <= number_of_bytes) fputc((value % 256), output);
}
else
{ /* Deal with LittleEndian */
unsigned byte;
while(number_of_bytes > 0)
{
byte = value % 256;
value = value / 256;
fputc(byte, output);
number_of_bytes = number_of_bytes - 1;
}
}
}
int Architectural_displacement(int target, int base)
{
if(KNIGHT == Architecture) return (target - base);
else if(X86 == Architecture) return (target - base);
else if(AMD64 == Architecture) return (target - base);
else if(ALIGNED && (ARMV7L == Architecture))
{
ALIGNED = FALSE;
/* Note: Branch displacements on ARM are in number of instructions to skip, basically. */
if (target & 3)
{
line_error();
fputs("error: Unaligned branch target: ", stderr);
fputs(scratch, stderr);
fputs(", aborting\n", stderr);
exit(EXIT_FAILURE);
}
/*
* The "fetch" stage already moved forward by 8 from the
* beginning of the instruction because it is already
* prefetching the next instruction.
* Compensate for it by subtracting the space for
* two instructions (including the branch instruction).
* and the size of the aligned immediate.
*/
return (((target - base + (base & 3)) >> 2) - 2);
}
else if(ARMV7L == Architecture)
{
/*
* The size of the offset is 8 according to the spec but that value is
* based on the end of the immediate, which the documentation gets wrong
* and needs to be adjusted to the size of the immediate.
* Eg 1byte immediate => -8 + 1 = -7
*/
return ((target - base) - 8 + (3 & base));
}
else if(ALIGNED && (AARM64 == Architecture))
{
ALIGNED = FALSE;
return (target - (~3 & base)) >> 2;
}
else if (AARM64 == Architecture)
{
return ((target - base) - 8 + (3 & base));
}
else if(ALIGNED && (PPC64LE == Architecture))
{
ALIGNED = FALSE;
/* set Link register with branch */
return (target - (base & 0xFFFFFFFC )) | 1;
}
else if(PPC64LE == Architecture)
{
/* DO *NOT* set link register with branch */
return (target - (base & 0xFFFFFFFC));
}
else if(RISCV32 == Architecture || RISCV64 == Architecture) return (target - base);
fputs("Unknown Architecture, aborting before harm is done\n", stderr);
exit(EXIT_FAILURE);
}
void Update_Pointer(char ch)
{
/* Calculate pointer size*/
if(in_set(ch, "%&")) ip = ip + 4; /* Deal with % and & */
else if(in_set(ch, "@$")) ip = ip + 2; /* Deal with @ and $ */
else if('~' == ch) ip = ip + 3; /* Deal with ~ */
else if('!' == ch) ip = ip + 1; /* Deal with ! */
else
{
line_error();
fputs("storePointer given unknown\n", stderr);
exit(EXIT_FAILURE);
}
}
void storePointer(char ch, FILE* source_file)
{
/* Get string of pointer */
Clear_Scratch(scratch);
Update_Pointer(ch);
int base_sep_p = consume_token(source_file);
/* Lookup token */
int target = GetTarget(scratch);
int displacement;
int base = ip;
/* Change relative base address to :<base> */
if ('>' == base_sep_p)
{
Clear_Scratch(scratch);
consume_token (source_file);
base = GetTarget (scratch);
/* Force universality of behavior */
displacement = (target - base);
}
else
{
displacement = Architectural_displacement(target, base);
}
/* output calculated difference */
if('!' == ch) outputPointer(displacement, 1); /* Deal with ! */
else if('$' == ch) outputPointer(target, 2); /* Deal with $ */
else if('@' == ch) outputPointer(displacement, 2); /* Deal with @ */
else if('~' == ch) outputPointer(displacement, 3); /* Deal with ~ */
else if('&' == ch) outputPointer(target, 4); /* Deal with & */
else if('%' == ch) outputPointer(displacement, 4); /* Deal with % */
else
{
line_error();
fputs("error: storePointer reached impossible case: ch=", stderr);
fputc(ch, stderr);
fputs("\n", stderr);
exit(EXIT_FAILURE);
}
}
void line_Comment(FILE* source_file)
{
int c = fgetc(source_file);
while(!in_set(c, "\n\r"))
{
if(EOF == c) break;
c = fgetc(source_file);
}
linenumber = linenumber + 1;
}
int hex(int c, FILE* source_file)
{
if (in_set(c, "0123456789")) return (c - 48);
else if (in_set(c, "abcdef")) return (c - 87);
else if (in_set(c, "ABCDEF")) return (c - 55);
else if (in_set(c, "#;")) line_Comment(source_file);
else if ('\n' == c) linenumber = linenumber + 1;
return -1;
}
int octal(int c, FILE* source_file)
{
if (in_set(c, "01234567")) return (c - 48);
else if (in_set(c, "#;")) line_Comment(source_file);
else if ('\n' == c) linenumber = linenumber + 1;
return -1;
}
int binary(int c, FILE* source_file)
{
if (in_set(c, "01")) return (c - 48);
else if (in_set(c, "#;")) line_Comment(source_file);
else if ('\n' == c) linenumber = linenumber + 1;
return -1;
}
void process_byte(char c, FILE* source_file, int write)
{
if(HEX == ByteMode)
{
if(0 <= hex(c, source_file))
{
if(toggle)
{
if(write) fputc(((hold * 16)) + hex(c, source_file), output);
ip = ip + 1;
hold = 0;
}
else
{
hold = hex(c, source_file);
}
toggle = !toggle;
}
}
else if(OCTAL ==ByteMode)
{
if(0 <= octal(c, source_file))
{
if(2 == toggle)
{
if(write) fputc(((hold * 8)) + octal(c, source_file), output);
ip = ip + 1;
hold = 0;
toggle = 0;
}
else if(1 == toggle)
{
hold = ((hold * 8) + octal(c, source_file));
toggle = 2;
}
else
{
hold = octal(c, source_file);
toggle = 1;
}
}
}
else if(BINARY == ByteMode)
{
if(0 <= binary(c, source_file))
{
if(7 == toggle)
{
if(write) fputc((hold * 2) + binary(c, source_file), output);
ip = ip + 1;
hold = 0;
toggle = 0;
}
else
{
hold = ((hold * 2) + binary(c, source_file));
toggle = toggle + 1;
}
}
}
}
void pad_to_align(int write)
{
if((ARMV7L == Architecture) || (AARM64 == Architecture) || (RISCV32 == Architecture) || (RISCV64 == Architecture))
{
if(1 == (ip & 0x1))
{
ip = ip + 1;
if(write) fputc('\0', output);
}
if(2 == (ip & 0x2))
{
ip = ip + 2;
if(write)
{
fputc('\0', output);
fputc('\0', output);
}
}
}
}
void first_pass(struct input_files* input)
{
if(NULL == input) return;
first_pass(input->next);
filename = input->filename;
linenumber = 1;
FILE* source_file = fopen(filename, "r");
if(NULL == source_file)
{
fputs("The file: ", stderr);
fputs(input->filename, stderr);
fputs(" can not be opened!\n", stderr);
exit(EXIT_FAILURE);
}
toggle = FALSE;
int c;
for(c = fgetc(source_file); EOF != c; c = fgetc(source_file))
{
/* Check for and deal with label */
if(':' == c)
{
c = storeLabel(source_file, ip);
}
/* check for and deal with relative/absolute pointers to labels */
if(in_set(c, "!@$~%&"))
{ /* deal with 1byte pointer !; 2byte pointers (@ and $); 3byte pointers ~; 4byte pointers (% and &) */
Update_Pointer(c);
c = Throwaway_token(source_file);
if ('>' == c)
{ /* deal with label>base */
c = Throwaway_token(source_file);
}
}
else if('<' == c)
{
pad_to_align(FALSE);
}
else if('^' == c)
{
/* Just ignore */
continue;
}
else process_byte(c, source_file, FALSE);
}
fclose(source_file);
}
void second_pass(struct input_files* input)
{
if(NULL == input) return;
second_pass(input->next);
filename = input->filename;
linenumber = 1;
FILE* source_file = fopen(filename, "r");
/* Something that should never happen */
if(NULL == source_file)
{
fputs("The file: ", stderr);
fputs(input->filename, stderr);
fputs(" can not be opened!\nWTF-pass2\n", stderr);
exit(EXIT_FAILURE);
}
toggle = FALSE;
hold = 0;
int c;
for(c = fgetc(source_file); EOF != c; c = fgetc(source_file))
{
if(':' == c) c = Throwaway_token(source_file); /* Deal with : */
else if(in_set(c, "!@$~%&")) storePointer(c, source_file); /* Deal with !, @, $, ~, % and & */
else if('<' == c) pad_to_align(TRUE);
else if('^' == c) ALIGNED = TRUE;
else process_byte(c, source_file, TRUE);
}
fclose(source_file);
}
/* -*- c-file-style: "linux";indent-tabs-mode:t -*- */
/* Copyright (C) 2017 Jeremiah Orians
* Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org>
* This file is part of mescc-tools
*
* mescc-tools is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* mescc-tools is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with mescc-tools. If not, see <http://www.gnu.org/licenses/>.
*/
#include "hex2_globals.h"
unsigned shiftregister;
unsigned tempword;
int updates;
void outOfRange(char* s, int value)
{
line_error();
fputs("error: value ", stderr);
fputs(int2str(value, 10, TRUE), stderr);
fputs(" out of range for field type ", stderr);
fputs(s, stderr);
fputs("\n", stderr);
exit(EXIT_FAILURE);
}
void UpdateShiftRegister(char ch, int value)
{
if ('.' == ch)
{
unsigned swap;
/* Assume the user knows what they are doing */
if(!BigEndian)
{
/* Swap from big-endian to little endian order */
swap = (((value >> 24) & 0xFF) |
((value << 8) & 0xFF0000) |
((value >> 8) & 0xFF00) |
((value & 0xFF) << 24));
}
else
{
/* Big endian needs no change */
swap = value;
}
/* we just take the 4 bytes after the . and shove in the shift register */
swap = swap & ((0xFFFF << 16) | 0xFFFF);
shiftregister = shiftregister ^ swap;
}
else if ('!' == ch)
{
/* Corresponds to RISC-V I format */
/* Will need architecture specific logic if more architectures go this route */
/* no range check because it needs to work with labels for lui/addi + AUIPC combos */
/* !label is used in the second instruction of AUIPC combo but we want an offset from */
/* the first instruction */
value = value + 4;
tempword = (value & 0xFFF) << 20;
/* Update shift register */
tempword = tempword & ((0xFFFF << 16) | 0xFFFF);
shiftregister = shiftregister ^ tempword;
}
else if ('@' == ch)
{
/* Corresponds to RISC-V B format (formerly known as SB) */
/* Will need architecture specific logic if more architectures go this route */
if ((value < -0x1000 || value > 0xFFF) || (value & 1)) outOfRange("B", value);
/* Prepare the immediate's word */
tempword = ((value & 0x1E) << 7)
| ((value & 0x7E0) << (31 - 11))
| ((value & 0x800) >> 4)
| ((value & 0x1000) << (31 - 12));
tempword = tempword & ((0xFFFF << 16) | 0xFFFF);
/* Update shift register */
shiftregister = shiftregister ^ tempword;
}
else if ('$' == ch)
{
/* Corresponds with RISC-V J format (formerly known as UJ) */
/* Will need architecture specific logic if more architectures go this route */
if ((value < -0x100000 || value > 0xFFFFF) || (value & 1)) outOfRange("J", value);
tempword = ((value & 0x7FE) << (30 - 10))
| ((value & 0x800) << (20 - 11))
| ((value & 0xFF000))
| ((value & 0x100000) << (31 - 20));
tempword = tempword & ((0xFFFF << 16) | 0xFFFF);
shiftregister = shiftregister ^ tempword;
}
else if ('~' == ch)
{
/* Corresponds with RISC-V U format */
/* Will need architecture specific logic if more architectures go this route */
if ((value & 0xFFF) < 0x800) tempword = value & (0xFFFFF << 12);
else tempword = (value & (0xFFFFF << 12)) + 0x1000;
tempword = tempword & ((0xFFFF << 16) | 0xFFFF);
shiftregister = shiftregister ^ tempword;
}
else
{
line_error();
fputs("error: UpdateShiftRegister reached impossible case: ch=", stderr);
fputc(ch, stderr);
fputs("\n", stderr);
exit(EXIT_FAILURE);
}
}
void WordStorePointer(char ch, FILE* source_file)
{
/* Get string of pointer */
ip = ip + 4;
Clear_Scratch(scratch);
int base_sep_p = consume_token(source_file);
/* Lookup token */
int target = GetTarget(scratch);
int displacement;
int base = ip;
/* Change relative base address to :<base> */
if ('>' == base_sep_p)
{
Clear_Scratch(scratch);
consume_token (source_file);
base = GetTarget (scratch);
/* Force universality of behavior */
displacement = (target - base);
}
else
{
displacement = Architectural_displacement(target, base);
}
/* output calculated difference */
if('&' == ch) outputPointer(target, 4); /* Deal with & */
else if('%' == ch) outputPointer(displacement, 4); /* Deal with % */
else
{
line_error();
fputs("error: WordStorePointer reached impossible case: ch=", stderr);
fputc(ch, stderr);
fputs("\n", stderr);
exit(EXIT_FAILURE);
}
}
unsigned sr_nextb()
{
unsigned rv = shiftregister & 0xff;
shiftregister = shiftregister >> 8;
return rv;
}
void DoByte(char c, FILE* source_file, int write, int update)
{
if(HEX == ByteMode)
{
if(0 <= hex(c, source_file))
{
if(toggle)
{
if(write) fputc(((hold * 16)) + hex(c, source_file) ^ sr_nextb(), output);
ip = ip + 1;
if(update)
{
hold = (hold * 16) + hex(c, source_file);
tempword = (tempword << 8) ^ hold;
updates = updates + 1;
}
hold = 0;
}
else
{
hold = hex(c, source_file);
}
toggle = !toggle;
}
}
else if(OCTAL ==ByteMode)
{
if(0 <= octal(c, source_file))
{
if(2 == toggle)
{
if(write) fputc(((hold * 8)) + octal(c, source_file) ^ sr_nextb(), output);
ip = ip + 1;
if(update)
{
hold = ((hold * 8) + octal(c, source_file));
tempword = (tempword << 8) ^ hold;
updates = updates + 1;
}
hold = 0;
toggle = 0;
}
else if(1 == toggle)
{
hold = ((hold * 8) + octal(c, source_file));
toggle = 2;
}
else
{
hold = octal(c, source_file);
toggle = 1;
}
}
}
else if(BINARY == ByteMode)
{
if(0 <= binary(c, source_file))
{
if(7 == toggle)
{
if(write) fputc((hold * 2) + binary(c, source_file) ^ sr_nextb(), output);
ip = ip + 1;
if(update)
{
hold = ((hold * 2) + binary(c, source_file));
tempword = (tempword << 8) ^ hold;
updates = updates + 1;
}
hold = 0;
toggle = 0;
}
else
{
hold = ((hold * 2) + binary(c, source_file));
toggle = toggle + 1;
}
}
}
}
void WordFirstPass(struct input_files* input)
{
if(NULL == input) return;
WordFirstPass(input->next);
filename = input->filename;
linenumber = 1;
FILE* source_file = fopen(filename, "r");
if(NULL == source_file)
{
fputs("The file: ", stderr);
fputs(input->filename, stderr);
fputs(" can not be opened!\n", stderr);
exit(EXIT_FAILURE);
}
toggle = FALSE;
int c;
for(c = fgetc(source_file); EOF != c; c = fgetc(source_file))
{
/* Check for and deal with label */
if(':' == c)
{
c = storeLabel(source_file, ip);
}
/* check for and deal with relative/absolute pointers to labels */
if('.' == c)
{
/* Read architecture specific number of bytes for what is defined as a word */
/* 4bytes in RISC-V's case */
updates = 0;
tempword = 0;
while (updates < 4)
{
c = fgetc(source_file);
DoByte(c, source_file, FALSE, TRUE);
}
ip = ip - 4;
}
else if(in_set(c, "!@$~"))
{
/* Don't update IP */
c = Throwaway_token(source_file);
}
else if(in_set(c, "%&"))
{
ip = ip + 4;
c = Throwaway_token(source_file);
if ('>' == c)
{ /* deal with label>base */
c = Throwaway_token(source_file);
}
}
else if('<' == c)
{
pad_to_align(FALSE);
}
else if('^' == c)
{
/* Just ignore */
continue;
}
else DoByte(c, source_file, FALSE, FALSE);
}
fclose(source_file);
}
void WordSecondPass(struct input_files* input)
{
shiftregister = 0;
tempword = 0;
if(NULL == input) return;
WordSecondPass(input->next);
filename = input->filename;
linenumber = 1;
FILE* source_file = fopen(filename, "r");
/* Something that should never happen */
if(NULL == source_file)
{
fputs("The file: ", stderr);
fputs(input->filename, stderr);
fputs(" can not be opened!\nWTF-pass2\n", stderr);
exit(EXIT_FAILURE);
}
toggle = FALSE;
hold = 0;
int c;
for(c = fgetc(source_file); EOF != c; c = fgetc(source_file))
{
if(':' == c) c = Throwaway_token(source_file); /* Deal with : */
else if('.' == c)
{
/* Read architecture specific number of bytes for what is defined as a word */
/* 4bytes in RISC-V's case */
updates = 0;
tempword = 0;
while (updates < 4)
{
c = fgetc(source_file);
DoByte(c, source_file, FALSE, TRUE);
}
UpdateShiftRegister('.', tempword);
ip = ip - 4;
}
else if(in_set(c, "%&")) WordStorePointer(c, source_file); /* Deal with % and & */
else if(in_set(c, "!@$~"))
{
Clear_Scratch(scratch);
consume_token(source_file);
UpdateShiftRegister(c, Architectural_displacement(GetTarget(scratch), ip)); /* Play with shift register */
}
else if('<' == c) pad_to_align(TRUE);
else if('^' == c) ALIGNED = TRUE;
else DoByte(c, source_file, TRUE, FALSE);
}
fclose(source_file);
}
/* -*- c-file-style: "linux";indent-tabs-mode:t -*- */
/* Copyright (C) 2017 Jeremiah Orians
* Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org>
* This file is part of mescc-tools
*
* mescc-tools is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* mescc-tools is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with mescc-tools. If not, see <http://www.gnu.org/licenses/>.
*/
#include "hex2_globals.h"
/* The essential functions */
void first_pass(struct input_files* input);
void second_pass(struct input_files* input);
void WordFirstPass(struct input_files* input);
void WordSecondPass(struct input_files* input);
/* Standard C main program */
int main(int argc, char **argv)
{
int InsaneArchitecture = FALSE;
ALIGNED = FALSE;
BigEndian = TRUE;
jump_tables = calloc(65537, sizeof(struct entry*));
require(NULL != jump_tables, "Failed to allocate our jump_tables\n");
Architecture = KNIGHT;
Base_Address = 0;
struct input_files* input = NULL;
output = stdout;
char* output_file = "";
exec_enable = TRUE;
ByteMode = HEX;
scratch = calloc(max_string + 1, sizeof(char));
require(NULL != scratch, "failed to allocate our scratch buffer\n");
char* arch;
struct input_files* temp;
int option_index = 1;
while(option_index <= argc)
{
if(NULL == argv[option_index])
{
option_index = option_index + 1;
}
else if(match(argv[option_index], "--big-endian"))
{
BigEndian = TRUE;
option_index = option_index + 1;
}
else if(match(argv[option_index], "--little-endian"))
{
BigEndian = FALSE;
option_index = option_index + 1;
}
else if(match(argv[option_index], "--non-executable"))
{
exec_enable = FALSE;
option_index = option_index + 1;
}
else if(match(argv[option_index], "-A") || match(argv[option_index], "--architecture"))
{
arch = argv[option_index + 1];
if(match("knight-native", arch) || match("knight-posix", arch)) Architecture = KNIGHT;
else if(match("x86", arch)) Architecture = X86;
else if(match("amd64", arch)) Architecture = AMD64;
else if(match("armv7l", arch)) Architecture = ARMV7L;
else if(match("aarch64", arch)) Architecture = AARM64;
else if(match("ppc64le", arch)) Architecture = PPC64LE;
else if(match("riscv32", arch)) Architecture = RISCV32;
else if(match("riscv64", arch)) Architecture = RISCV64;
else
{
fputs("Unknown architecture: ", stderr);
fputs(arch, stderr);
fputs(" know values are: knight-native, knight-posix, x86, amd64, armv7l, riscv32 and riscv64", stderr);
}
option_index = option_index + 2;
}
else if(match(argv[option_index], "-b") || match(argv[option_index], "--binary"))
{
ByteMode = BINARY;
option_index = option_index + 1;
}
else if(match(argv[option_index], "-B") || match(argv[option_index], "--base-address"))
{
Base_Address = strtoint(argv[option_index + 1]);
option_index = option_index + 2;
}
else if(match(argv[option_index], "-h") || match(argv[option_index], "--help"))
{
fputs("Usage: ", stderr);
fputs(argv[0], stderr);
fputs(" --file FILENAME1 {-f FILENAME2} (--big-endian|--little-endian)", stderr);
fputs(" [--base-address 0x12345] [--architecture name]\nArchitecture:", stderr);
fputs(" knight-native, knight-posix, x86, amd64, armv7l, aarch64, riscv32 and riscv64\n", stderr);
fputs("To leverage octal or binary input: --octal, --binary\n", stderr);
exit(EXIT_SUCCESS);
}
else if(match(argv[option_index], "-f") || match(argv[option_index], "--file"))
{
temp = calloc(1, sizeof(struct input_files));
require(NULL != temp, "failed to allocate file for processing\n");
temp->filename = argv[option_index + 1];
temp->next = input;
input = temp;
option_index = option_index + 2;
}
else if(match(argv[option_index], "-o") || match(argv[option_index], "--output"))
{
output_file = argv[option_index + 1];
output = fopen(output_file, "w");
if(NULL == output)
{
fputs("The file: ", stderr);
fputs(argv[option_index + 1], stderr);
fputs(" can not be opened!\n", stderr);
exit(EXIT_FAILURE);
}
option_index = option_index + 2;
}
else if(match(argv[option_index], "-O") || match(argv[option_index], "--octal"))
{
ByteMode = OCTAL;
option_index = option_index + 1;
}
else if(match(argv[option_index], "-V") || match(argv[option_index], "--version"))
{
fputs("hex2 1.4.0\n", stdout);
exit(EXIT_SUCCESS);
}
else
{
fputs("Unknown option\n", stderr);
exit(EXIT_FAILURE);
}
}
if((Architecture == RISCV32) || (Architecture == RISCV64))
{
/* Forcing me to use words instead of just byting into the problem */
InsaneArchitecture = TRUE;
}
/* Catch a common mistake */
if((KNIGHT != Architecture) && (0 == Base_Address))
{
fputs(">> WARNING <<\n>> WARNING <<\n>> WARNING <<\n", stderr);
fputs("If you are not generating a ROM image this binary will likely not work\n", stderr);
}
/* Catch implicitly false assumptions */
if(BigEndian && ((X86 == Architecture) || ( AMD64 == Architecture) || (ARMV7L == Architecture) || (AARM64 == Architecture) || (RISCV32 == Architecture) || (RISCV64 == Architecture)))
{
fputs(">> WARNING <<\n>> WARNING <<\n>> WARNING <<\n", stderr);
fputs("You have specified big endian output on likely a little endian processor\n", stderr);
fputs("if this is a mistake please pass --little-endian next time\n", stderr);
}
/* Make sure we have a program tape to run */
if (NULL == input)
{
return EXIT_FAILURE;
}
/* Get all of the labels */
ip = Base_Address;
if(InsaneArchitecture) WordFirstPass(input);
else first_pass(input);
/* Fix all the references*/
ip = Base_Address;
if(InsaneArchitecture) WordSecondPass(input);
else second_pass(input);
/* flush all writes */
fflush(output);
/* Set file as executable */
if(exec_enable && (output != stdout))
{
/* Close output file */
fclose(output);
if(0 != chmod(output_file, 0750))
{
fputs("Unable to change permissions\n", stderr);
exit(EXIT_FAILURE);
}
}
return EXIT_SUCCESS;
}
## Copyright (C) 2016 Jeremiah Orians
## This file is part of M2-Planet.
##
## M2-Planet is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## M2-Planet is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with M2-Planet. If not, see <http://www.gnu.org/licenses/>.
:_start
mov_ebp,esp ; Protect esp
;; Prepare argv
lea_eax,[ebp+DWORD] %4 ; ARGV_address = EBP + 4
push_eax ; Put argv on the stack
;; Prepare envp
mov_eax,ebp ; Address we need to load from
mov_eax,[eax] ; Get ARGC
add_eax, %2 ; OFFSET = ARGC + 2
sal_eax, !2 ; OFFSET = OFFSET * WORDSIZE
add_eax,ebp ; ENVP_address = ESP + OFFSET
push_eax ; Put envp on the stack
mov_ebx, &GLOBAL__envp ; Get _envp global
mov_[ebx],eax ; Save environment to _envp
;; Stack offset
add_ebp, %4 ; Fix ebp
;; Setup for malloc
call %FUNCTION___init_malloc
;; Setup for FILE*
call %FUNCTION___init_io
;; Perform the main loop
call %FUNCTION_main
push_eax ; Put return on stack
push_eax ; so that _exit gets the value
:FUNCTION_exit
call %FUNCTION___kill_io
:FUNCTION__exit
pop_ebx
pop_ebx
mov_eax, %1
int !0x80
:GLOBAL__envp
NULL
/* Copyright (C) 2016 Jeremiah Orians
* This file is part of M2-Planet.
*
* M2-Planet is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* M2-Planet is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with M2-Planet. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stddef.h>
char* strcpy(char* dest, char const* src)
{
int i = 0;
while (0 != src[i])
{
dest[i] = src[i];
i = i + 1;
}
dest[i] = 0;
return dest;
}
char* strncpy(char* dest, char const* src, size_t count)
{
if(0 == count) return dest;
size_t i = 0;
while(0 != src[i])
{
dest[i] = src[i];
i = i + 1;
if(count == i) return dest;
}
while(i <= count)
{
dest[i] = 0;
i = i + 1;
}
return dest;
}
char* strcat(char* dest, char const* src)
{
int i = 0;
int j = 0;
while(0 != dest[i]) i = i + 1;
while(0 != src[j])
{
dest[i] = src[j];
i = i + 1;
j = j + 1;
}
dest[i] = 0;
return dest;
}
char* strncat(char* dest, char const* src, size_t count)
{
size_t i = 0;
size_t j = 0;
while(0 != dest[i]) i = i + 1;
while(0 != src[j])
{
if(count == j)
{
dest[i] = 0;
return dest;
}
dest[i] = src[j];
i = i + 1;
j = j + 1;
}
dest[i] = 0;
return dest;
}
size_t strlen(char const* str )
{
size_t i = 0;
while(0 != str[i]) i = i + 1;
return i;
}
size_t strnlen_s(char const* str, size_t strsz )
{
size_t i = 0;
while(0 != str[i])
{
if(strsz == i) return i;
i = i + 1;
}
return i;
}
int strcmp(char const* lhs, char const* rhs )
{
int i = 0;
while(0 != lhs[i])
{
if(lhs[i] != rhs[i]) return lhs[i] - rhs[i];
i = i + 1;
}
return lhs[i] - rhs[i];
}
int strncmp(char const* lhs, char const* rhs, size_t count)
{
size_t i = 0;
while(count > i)
{
if(0 == lhs[i]) break;
if(lhs[i] != rhs[i]) return lhs[i] - rhs[i];
i = i + 1;
}
return 0;
}
char* strchr(char const* str, int ch)
{
char* p = str;
while(ch != p[0])
{
if(0 == p[0]) return NULL;
p = p + 1;
}
if(0 == p[0]) return NULL;
return p;
}
char* strrchr(char const* str, int ch)
{
char* p = str;
int i = 0;
while(0 != p[i]) i = i + 1;
while(ch != p[i])
{
if(0 == i) return NULL;
i = i - 1;
}
return (p + i);
}
size_t strspn(char const* dest, char const* src)
{
if(0 == dest[0]) return 0;
int i = 0;
while(NULL != strchr(src, dest[i])) i = i + 1;
return i;
}
size_t strcspn(char const* dest, char const* src)
{
int i = 0;
while(NULL == strchr(src, dest[i])) i = i + 1;
return i;
}
char* strpbrk(char const* dest, char const* breakset)
{
char* p = dest;
char* s;
while(0 != p[0])
{
s = strchr(breakset, p[0]);
if(NULL != s) return strchr(p, s[0]);
p = p + 1;
}
return p;
}
void* memset(void* dest, int ch, size_t count)
{
if(NULL == dest) return dest;
size_t i = 0;
char* s = dest;
while(i < count)
{
s[i] = ch;
i = i + 1;
}
return dest;
}
void* memcpy(void* dest, void const* src, size_t count)
{
if(NULL == dest) return dest;
if(NULL == src) return NULL;
char* s1 = dest;
char const* s2 = src;
size_t i = 0;
while(i < count)
{
s1[i] = s2[i];
i = i + 1;
}
return dest;
}
void* memmove(void* dest, void const* src, size_t count)
{
if (dest < src) return memcpy (dest, src, count);
char *p = dest;
char const *q = src;
count = count - 1;
while (count >= 0)
{
p[count] = q[count];
count = count - 1;
}
return dest;
}
int memcmp(void const* lhs, void const* rhs, size_t count)
{
if(0 == count) return 0;
size_t i = 0;
count = count - 1;
char const* s1 = lhs;
char const* s2 = rhs;
while(i < count)
{
if(s1[i] != s2[i]) break;
i = i + 1;
}
return (s1[i] - s2[i]);
}
/* Copyright (C) 2016-2020 Jeremiah Orians
* Copyright (C) 2020 fosslinux
* This file is part of mescc-tools.
*
* mescc-tools is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* mescc-tools is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with mescc-tools. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include "../M2libc/bootstrappable.h"
/*
* DEFINES
*/
#define FALSE 0
#define TRUE 1
// CONSTANT SUCCESS 0
#define SUCCESS 0
// CONSTANT FAILURE 1
#define FAILURE 1
#define MAX_STRING 4096
#define MAX_ARRAY 256
/*
* Here is the token struct. It is used for both the token linked-list and
* env linked-list.
*/
struct Token
{
/*
* For the token linked-list, this stores the token; for the env linked-list
* this stores the value of the variable.
*/
char* value;
/*
* Used only for the env linked-list. It holds a string containing the
* name of the var.
*/
char* var;
/*
* This struct stores a node of a singly linked list, store the pointer to
* the next node.
*/
struct Token* next;
};
#include "kaem_globals.h"
/*
* Copyright (C) 2020 fosslinux
* This file is part of mescc-tools.
*
* mescc-tools is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* mescc-tools is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with mescc-tools. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include "kaem.h"
/* Prototypes from other files */
int array_length(char** array);
char* env_lookup(char* variable);
/*
* VARIABLE HANDLING FUNCTIONS
*/
/* Substitute a variable into n->value */
int run_substitution(char* var_name, struct Token* n)
{
char* value = env_lookup(var_name);
/* If there is nothing to substitute, don't substitute anything! */
if(value != NULL)
{
char* s = calloc(MAX_STRING, sizeof(char));
s = strcat(s, n->value);
s = strcat(s, value);
n->value = s;
return TRUE;
}
return FALSE;
}
/* Handle ${var:-text} format of variables - i.e. ifset format */
int variable_substitute_ifset(char* input, struct Token* n, int index)
{
/*
* In ${var:-text} format, we evaluate like follows.
* If var is set as an envar, then we substitute the contents of that
* envar. If it is not set, we substitute alternative text.
*
* In this function, we assume that input is the raw token,
* n->value is everything already done in variable_substitute,
* index is where we are up to in input. offset is for n->value.
*/
/*
* Check if we should even be performing this function.
* We perform this function when we come across ${var:-text} syntax.
*/
int index_old = index;
int perform = FALSE;
int input_length = strlen(input);
while(index < input_length)
{ /* Loop over each character */
if(input[index] == ':' && input[index + 1] == '-')
{ /* Yes, this is (most likely) ${var:-text} format. */
perform = TRUE;
break;
}
index = index + 1;
}
/* Don't perform it if we shouldn't */
if(perform == FALSE) return index_old;
index = index_old;
/*
* Get offset.
* offset is the difference between the index of the variable we write to
* in the following blocks and input.
* This stays relatively constant.
*/
int offset = index;
/* Get the variable name */
char* var_name = calloc(MAX_STRING, sizeof(char));
require(var_name != NULL, "Memory initialization of var_name in variable_substitute_ifset failed\n");
while(input[index] != ':')
{ /* Copy into var_name until :- */
var_name[index - offset] = input[index];
index = index + 1;
}
/* Skip over :- */
index = index + 2;
offset = index;
/* Get the alternative text */
char* text = calloc(MAX_STRING, sizeof(char));
require(text != NULL, "Memory initialization of text in variable_substitute_ifset failed\n");
while(input[index] != '}')
{ /* Copy into text until } */
require(input_length > index, "IMPROPERLY TERMINATED VARIABLE\nABORTING HARD\n");
text[index - offset] = input[index];
index = index + 1;
}
/* Do the substitution */
if(run_substitution(var_name, n) == FALSE)
{ /* The variable was not found. Substitute the alternative text. */
char* s = calloc(MAX_STRING, sizeof(char));
s = strcat(s, n->value);
s = strcat(s, text);
n->value = s;
}
return index;
}
/* Controls substitution for ${variable} and derivatives */
int variable_substitute(char* input, struct Token* n, int index)
{
/* NOTE: index is the pos of input */
index = index + 1; /* We don't want the { */
/*
* Check for "special" types
* If we do find a special type we delegate the substitution to it
* and return here; as we are done... there's nothing more do do in
* that case.
*/
int index_old = index;
index = variable_substitute_ifset(input, n, index);
if(index != index_old) return index;
/* Reset index */
index = index_old;
/*
* If we reach here it is a normal substitution
* Let's do it!
*/
/* Initialize var_name and offset */
char* var_name = calloc(MAX_STRING, sizeof(char));
require(var_name != NULL, "Memory initialization of var_name in variable_substitute failed\n");
int offset = index;
/* Get the variable name */
int substitute_done = FALSE;
char c;
while(substitute_done == FALSE)
{
c = input[index];
require(MAX_STRING > index, "LINE IS TOO LONG\nABORTING HARD\n");
if(EOF == c || '\n' == c || index > strlen(input))
{ /* We never should hit EOF, EOL or run past the end of the line
while collecting a variable */
fputs("IMPROPERLY TERMINATED VARIABLE!\nABORTING HARD\n", stderr);
exit(EXIT_FAILURE);
}
else if('\\' == c)
{ /* Drop the \ - poor mans escaping. */
index = index + 1;
}
else if('}' == c)
{ /* End of variable name */
substitute_done = TRUE;
}
else
{
var_name[index - offset] = c;
index = index + 1;
}
}
/* Substitute the variable */
run_substitution(var_name, n);
return index;
}
/* Function to concatenate all command line arguments */
void variable_all(char** argv, struct Token* n)
{
fflush(stdout);
/* index refernences the index of n->value, unlike other functions */
int index = 0;
int argv_length = array_length(argv);
int i = 0;
char* argv_element = calloc(MAX_STRING, sizeof(char));
char* hold = argv[i];
n->value = argv_element;
/* Assuming the form kaem -f script or kaem -f script -- 123 we want matching results to bash, so skip the kaem, -f and script */
while(!match("--", hold))
{
i = i + 1;
hold = argv[i];
if(argv_length == i) break;
}
/* put i = i + 1 in the for initialization to skip past the -- */
for(; i < argv_length; i = i + 1)
{
/* Ends up with (n->value) (argv[i]) */
/* If we don't do this we get jumbled results in M2-Planet */
hold = argv[i];
strcpy(argv_element + index, hold);
index = index + strlen(hold);
/* Add space on the end */
n->value[index] = ' ';
index = index + 1;
}
/* Remove trailing space */
index = index - 1;
n->value[index] = 0;
}
/* Function controlling substitution of variables */
void handle_variables(char** argv, struct Token* n)
{
/* NOTE: index is the position of input */
int index = 0;
/* Create input */
char* input = calloc(MAX_STRING, sizeof(char));
require(input != NULL, "Memory initialization of input in collect_variable failed\n");
strcpy(input, n->value);
/* Reset n->value */
n->value = calloc(MAX_STRING, sizeof(char));
require(n->value != NULL, "Memory initialization of n->value in collect_variable failed\n");
/* Copy everything up to the $ */
/*
* TODO: Not need allocation of input before this check if there is no
* variable in it.
*/
while(input[index] != '$')
{
if(input[index] == 0)
{ /* No variable in it */
n->value = input;
return; /* We don't need to do anything more */
}
n->value[index] = input[index];
index = index + 1;
}
/* Must be outside the loop */
int offset;
substitute:
index = index + 1; /* We are uninterested in the $ */
/* Run the substitution */
if(input[index] == '{')
{ /* Handle everything ${ related */
index = variable_substitute(input, n, index);
index = index + 1; /* We don't want the closing } */
}
else if(input[index] == '@')
{ /* Handles $@ */
index = index + 1; /* We don't want the @ */
variable_all(argv, n);
}
else
{ /* We don't know that */
fputs("IMPROPERLY USED VARIABLE!\nOnly ${foo} and $@ format are accepted at this time.\nABORTING HARD\n", stderr);
exit(EXIT_FAILURE);
}
offset = strlen(n->value) - index;
/* Copy everything from the end of the variable to the end of the token */
while(input[index] != 0)
{
if(input[index] == '$')
{ /* We have found another variable */
fflush(stdout);
goto substitute;
}
n->value[index + offset] = input[index];
index = index + 1;
}
}
/* Copyright (C) 2016-2020 Jeremiah Orians * Copyright (C) 2020 fosslinux * This file is part of mescc-tools. * * mescc-tools is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * mescc-tools is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with mescc-tools. If not, see <http://www.gnu.org/licenses/>. */ #include "kaem.h" int command_done; int VERBOSE; int VERBOSE_EXIT; int STRICT; int INIT_MODE; int FUZZING; int WARNINGS; char* KAEM_BINARY; char* PATH; /* Token linked-list; stores the tokens of each line */ struct Token* token; /* Env linked-list; stores the environment variables */ struct Token* env; /* Alias linked-list; stores the aliases */ struct Token* alias;
/* Copyright (C) 2016-2020 Jeremiah Orians
* Copyright (C) 2020 fosslinux
* Copyright (C) 2021 Andrius Å tikonas
* This file is part of mescc-tools.
*
* mescc-tools is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* mescc-tools is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with mescc-tools. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include "kaem.h"
/* Prototypes from other files */
void handle_variables(char** argv, struct Token* n);
/*
* UTILITY FUNCTIONS
*/
/* Function to find a character in a string */
char* find_char(char* string, char a)
{
if(0 == string[0])
{
return NULL;
}
while(a != string[0])
{
string = string + 1;
if(0 == string[0])
{
return string;
}
}
return string;
}
/* Function to find the length of a char**; an array of strings */
int array_length(char** array)
{
int length = 0;
while(array[length] != NULL)
{
length = length + 1;
}
return length;
}
/* Search for a variable in the token linked-list */
char* token_lookup(char* variable, struct Token* token)
{
/* Start at the head */
struct Token* n = token;
/* Loop over the linked-list */
while(n != NULL)
{
if(match(variable, n->var))
{
/* We have found the correct node */
return n->value; /* Done */
}
/* Nope, try the next */
n = n->next;
}
/* We didn't find anything! */
return NULL;
}
/* Search for a variable in the env linked-list */
char* env_lookup(char* variable)
{
return token_lookup(variable, env);
}
/* Search for a variable in the alias linked-list */
char* alias_lookup(char* variable)
{
return token_lookup(variable, alias);
}
/* Find the full path to an executable */
char* find_executable(char* name)
{
if(match("", name))
{
return NULL;
}
if(('.' == name[0]) || ('/' == name[0]))
{
/* assume names that start with . or / are relative or absolute */
return name;
}
char* trial = calloc(MAX_STRING, sizeof(char));
char* MPATH = calloc(MAX_STRING, sizeof(char)); /* Modified PATH */
require(MPATH != NULL, "Memory initialization of MPATH in find_executable failed\n");
strcpy(MPATH, PATH);
FILE* t;
char* next = find_char(MPATH, ':');
int index;
int offset;
int mpath_length;
int name_length;
int trial_length;
while(NULL != next)
{
/* Reset trial */
trial_length = strlen(trial);
for(index = 0; index < trial_length; index = index + 1)
{
trial[index] = 0;
}
next[0] = 0;
/* prepend_string(MPATH, prepend_string("/", name)) */
mpath_length = strlen(MPATH);
for(index = 0; index < mpath_length; index = index + 1)
{
require(MAX_STRING > index, "Element of PATH is too long\n");
trial[index] = MPATH[index];
}
trial[index] = '/';
offset = strlen(trial);
name_length = strlen(name);
for(index = 0; index < name_length; index = index + 1)
{
require(MAX_STRING > index, "Element of PATH is too long\n");
trial[index + offset] = name[index];
}
/* Try the trial */
require(strlen(trial) < MAX_STRING, "COMMAND TOO LONG!\nABORTING HARD\n");
t = fopen(trial, "r");
if(NULL != t)
{
fclose(t);
return trial;
}
MPATH = next + 1;
next = find_char(MPATH, ':');
}
return NULL;
}
/* Function to convert a Token linked-list into an array of strings */
char** list_to_array(struct Token* s)
{
struct Token* n;
n = s;
char** array = calloc(MAX_ARRAY, sizeof(char*));
require(array != NULL, "Memory initialization of array in conversion of list to array failed\n");
char* element = calloc(MAX_STRING, sizeof(char));
require(element != NULL, "Memory initialization of element in conversion of list to array failed\n");
int index = 0;
int i;
int value_length;
int var_length;
int offset;
while(n != NULL)
{
/* Loop through each node and assign it to an array index */
array[index] = calloc(MAX_STRING, sizeof(char));
require(array[index] != NULL, "Memory initialization of array[index] in conversion of list to array failed\n");
/* Bounds checking */
/* No easy way to tell which it is, output generic message */
require(index < MAX_ARRAY, "SCRIPT TOO LONG or TOO MANY ENVARS\nABORTING HARD\n");
if(n->var == NULL)
{
/* It is a line */
array[index] = n->value;
}
else
{
/* It is a var */
/* prepend_string(n->var, prepend_string("=", n->value)) */
var_length = strlen(n->var);
for(i = 0; i < var_length; i = i + 1)
{
element[i] = n->var[i];
}
element[i] = '=';
i = i + 1;
offset = i;
value_length = strlen(n->value);
for(i = 0; i < value_length; i = i + 1)
{
element[i + offset] = n->value[i];
}
}
/* Insert elements if not empty */
if(!match("", element))
{
strcpy(array[index], element);
}
n = n->next;
index = index + 1;
/* Reset element */
for(i = 0; i < MAX_STRING; i = i + 1)
{
element[i] = 0;
}
}
return array;
}
/* Function to handle the correct options for escapes */
int handle_escape(int c)
{
if(c == '\n')
{
/* Do nothing - eat up the newline */
return -1;
}
else if('n' == c)
{
/* Add a newline to the token */
return '\n';
}
else if('r' == c)
{
/* Add a return to the token */
return '\r';
}
else if('\\' == c)
{
/* Add a real backslash to the token */
return '\\';
}
else
{
/* Just add it to the token (eg, quotes) */
return c;
}
}
/*
* TOKEN COLLECTION FUNCTIONS
*/
/* Function for skipping over line comments */
void collect_comment(FILE* input)
{
int c;
/* Eat up the comment, one character at a time */
/*
* Sanity check that the comment ends with \n.
* Remove the comment from the FILE*
*/
do
{
c = fgetc(input);
/* We reached an EOF!! */
require(EOF != c, "IMPROPERLY TERMINATED LINE COMMENT!\nABORTING HARD\n");
} while('\n' != c); /* We can now be sure it ended with \n -- and have purged the comment */
}
/* Function for collecting strings and removing the "" pair that goes with them */
int collect_string(FILE* input, char* n, int index)
{
int string_done = FALSE;
int c;
do
{
/* Bounds check */
require(MAX_STRING > index, "LINE IS TOO LONG\nABORTING HARD\n");
c = fgetc(input);
require(EOF != c, "IMPROPERLY TERMINATED STRING!\nABORTING HARD\n");
if('\\' == c)
{
/* We are escaping the next character */
/* This correctly handles escaped quotes as it just returns the quote */
c = fgetc(input);
c = handle_escape(c);
n[index] = c;
index = index + 1;
}
else if('"' == c)
{
/* End of string */
string_done = TRUE;
}
else
{
n[index] = c;
index = index + 1;
}
} while(string_done == FALSE);
return index;
}
/* Function to parse and assign token->value */
int collect_token(FILE* input, char* n, int last_index)
{
int c;
int cc;
int token_done = FALSE;
int index = 0;
do
{
/* Loop over each character in the token */
c = fgetc(input);
/* Bounds checking */
require(MAX_STRING > index, "LINE IS TOO LONG\nABORTING HARD\n");
if(EOF == c)
{
/* End of file -- this means script complete */
/* We don't actually exit here. This logically makes more sense;
* let the code follow its natural path of execution and exit
* sucessfuly at the end of main().
*/
token_done = TRUE;
command_done = TRUE;
return -1;
}
else if((' ' == c) || ('\t' == c))
{
/* Space and tab are token separators */
token_done = TRUE;
}
else if(('\n' == c) || (';' == c))
{
/* Command terminates at the end of a line or at semicolon */
command_done = TRUE;
token_done = TRUE;
if(0 == index)
{
index = last_index;
}
}
else if('"' == c)
{
/* Handle strings -- everything between a pair of "" */
index = collect_string(input, n, index);
token_done = TRUE;
}
else if('#' == c)
{
/* Handle line comments */
collect_comment(input);
command_done = TRUE;
token_done = TRUE;
if(0 == index)
{
index = last_index;
}
}
else if('\\' == c)
{
/* Support for escapes */
c = fgetc(input); /* Skips over \, gets the next char */
cc = handle_escape(c);
if(-1 != cc)
{
/* We need to put it into the token */
n[index] = cc;
}
index = index + 1;
}
else if(0 == c)
{
/* We have come to the end of the token */
token_done = TRUE;
}
else
{
/* It's a character to assign */
n[index] = c;
index = index + 1;
}
} while(token_done == FALSE);
return index;
}
/* Function to parse string and assign token->value */
int collect_alias_token(char* input, char* n, int index)
{
int c;
int cc;
int token_done = FALSE;
int output_index = 0;
do
{
/* Loop over each character in the token */
c = input[index];
index = index + 1;
if((' ' == c) || ('\t' == c))
{
/* Space and tab are token separators */
token_done = TRUE;
}
else if('\\' == c)
{
/* Support for escapes */
c = input[index];
index = index + 1;
cc = handle_escape(c);
/* We need to put it into the token */
n[output_index] = cc;
output_index = output_index + 1;
}
else if(0 == c)
{
/* We have come to the end of the token */
token_done = TRUE;
index = 0;
}
else
{
/* It's a character to assign */
n[output_index] = c;
output_index = output_index + 1;
}
} while(token_done == FALSE);
return index;
}
/*
* EXECUTION FUNCTIONS
* Note: All of the builtins return SUCCESS (0) when they exit successfully
* and FAILURE (1) when they fail.
*/
/* Function to check if the token is an envar */
int is_envar(char* token)
{
int i = 0;
int token_length = strlen(token);
while(i < token_length)
{
if(token[i] == '=')
{
return FAILURE;
}
i = i + 1;
}
return SUCCESS;
}
/* Add an envar */
void add_envar()
{
/* Pointers to strings we want */
char* name = calloc(strlen(token->value) + 4, sizeof(char));
char* value = token->value;
char* newvalue;
int i = 0;
/* Isolate the name */
while('=' != value[i])
{
name[i] = value[i];
i = i + 1;
}
/* Isolate the value */
newvalue = name + i + 2;
value = value + i + 1;
i = 0;
require(0 != value[i], "add_envar received improper variable\n");
while(0 != value[i])
{
newvalue[i] = value[i];
i = i + 1;
}
/* If we are in init-mode and this is the first var env == NULL, rectify */
if(env == NULL)
{
env = calloc(1, sizeof(struct Token));
require(env != NULL, "Memory initialization of env failed\n");
env->var = name; /* Add our first variable */
}
/*
* If the name of the envar is PATH, then we need to set our (internal)
* global PATH value.
*/
if(match(name, "PATH"))
{
strcpy(PATH, newvalue);
}
struct Token* n = env;
/* Find match if possible */
while(!match(name, n->var))
{
if(NULL == n->next)
{
n->next = calloc(1, sizeof(struct Token));
require(n->next != NULL, "Memory initialization of next env node in add_envar failed\n");
n->next->var = name;
} /* Loop will match and exit */
n = n->next;
}
/* Since we found the variable we need only to set it to its new value */
n->value = newvalue;
}
/* Add an alias */
void add_alias()
{
token = token->next; /* Skip the actual alias */
if(token->next == NULL)
{
/* No arguments */
char** array = list_to_array(alias);
int index = 0;
while(array[index] != NULL) {
fputs(array[index], stdout);
fputc('\n', stdout);
index = index + 1;
}
fflush(stdout);
return;
}
if(!is_envar(token->value)) {
char** array = list_to_array(token);
int index = 0;
while(array[index] != NULL) {
fputs(array[index], stdout);
fputc(' ', stdout);
index = index + 1;
}
fputc('\n', stdout);
fflush(stdout);
return;
}
/* Pointers to strings we want */
char* name = calloc(strlen(token->value) + 4, sizeof(char));
char* value = token->value;
char* newvalue;
int i = 0;
/* Isolate the name */
while('=' != value[i])
{
name[i] = value[i];
i = i + 1;
}
/* Isolate the value */
newvalue = name + i + 2;
value = value + i + 1;
i = 0;
require(0 != value[i], "add_alias received improper variable\n");
while(0 != value[i])
{
newvalue[i] = value[i];
i = i + 1;
}
/* If this is the first alias, rectify */
if(alias == NULL)
{
alias = calloc(1, sizeof(struct Token));
require(alias != NULL, "Memory initialization of alias failed\n");
alias->var = name; /* Add our first variable */
}
struct Token* n = alias;
/* Find match if possible */
while(!match(name, n->var))
{
if(NULL == n->next)
{
n->next = calloc(1, sizeof(struct Token));
require(n->next != NULL, "Memory initialization of next alias node in alias failed\n");
n->next->var = name;
} /* Loop will match and exit */
n = n->next;
}
/* Since we found the variable we need only to set it to its new value */
n->value = newvalue;
}
/* cd builtin */
int cd()
{
if(NULL == token->next)
{
return FAILURE;
}
token = token->next;
if(NULL == token->value)
{
return FAILURE;
}
int ret = chdir(token->value);
if(0 > ret)
{
return FAILURE;
}
return SUCCESS;
}
/* pwd builtin */
int pwd()
{
char* path = calloc(MAX_STRING, sizeof(char));
require(path != NULL, "Memory initialization of path in pwd failed\n");
getcwd(path, MAX_STRING);
require(!match("", path), "getcwd() failed\n");
fputs(path, stdout);
fputs("\n", stdout);
return SUCCESS;
}
/* set builtin */
int set()
{
/* Get the options */
int i;
if(NULL == token->next)
{
goto cleanup_set;
}
token = token->next;
if(NULL == token->value)
{
goto cleanup_set;
}
char* options = calloc(MAX_STRING, sizeof(char));
require(options != NULL, "Memory initialization of options in set failed\n");
int last_position = strlen(token->value) - 1;
for(i = 0; i < last_position; i = i + 1)
{
options[i] = token->value[i + 1];
}
/* Parse the options */
int options_length = strlen(options);
for(i = 0; i < options_length; i = i + 1)
{
if(options[i] == 'a')
{
/* set -a is on by default and cannot be disabled at this time */
if(WARNINGS)
{
fputs("set -a is on by default and cannot be disabled\n", stdout);
}
continue;
}
else if(options[i] == 'e')
{
/* Fail on failure */
STRICT = TRUE;
}
else if(options[i] == 'x')
{
/* Show commands as executed */
/* TODO: this currently behaves like -v. Make it do what it should */
VERBOSE = TRUE;
/*
* Output the set -x because VERBOSE didn't catch it before.
* We don't do just -x because we support multiple options in one command,
* eg set -ex.
*/
fputs(" +> set -", stdout);
fputs(options, stdout);
fputs("\n", stdout);
fflush(stdout);
}
else
{
/* Invalid */
fputc(options[i], stderr);
fputs(" is an invalid set option!\n", stderr);
exit(EXIT_FAILURE);
}
}
return SUCCESS;
cleanup_set:
return FAILURE;
}
/* echo builtin */
void echo()
{
if(token->next == NULL)
{
/* No arguments */
fputs("\n", stdout);
return;
}
if(token->next->value == NULL)
{
/* No arguments */
fputs("\n", stdout);
return;
}
token = token->next; /* Skip the actual echo */
while(token != NULL)
{
/* Output each argument to echo to stdout */
if(token->value == NULL)
{
break;
}
fputs(token->value, stdout);
if(NULL != token->next)
{
/* M2-Planet doesn't short circuit */
if(NULL != token->next->value) fputc(' ', stdout);
}
token = token->next;
}
fputs("\n", stdout);
}
/* unset builtin */
void unset()
{
struct Token* e;
/* We support multiple variables on the same line */
struct Token* t;
t = token->next;
while(t != NULL)
{
e = env;
/* Look for the variable; we operate on ->next because we need to remove ->next */
while(e->next != NULL)
{
if(NULL == t->value)
{
break;
}
if(match(e->next->var, t->value))
{
break;
}
e = e->next;
}
t = t->next;
/* If it's NULL nothing was found */
if(e->next == NULL)
{
continue;
}
/* Otherwise there is something to unset */
e->next = e->next->next;
}
}
void execute(FILE* script, char** argv);
int _execute(FILE* script, char** argv);
int collect_command(FILE* script, char** argv);
/* if builtin */
void if_cmd(FILE* script, char** argv)
{
int index;
int old_VERBOSE;
token = token->next; /* Skip the actual if */
/* Do not check for successful exit status */
int if_status = _execute(script, argv);
old_VERBOSE = VERBOSE;
VERBOSE = VERBOSE && !if_status;
do
{
index = collect_command(script, argv);
require(index != -1, "Unexpected EOF, improperly terminated if statement.\n");
if(0 == index)
{
continue;
}
if(0 == if_status)
{
/* Stuff to exec */
execute(script, argv);
}
if(match(token->value, "else"))
{
if_status = !if_status;
}
} while(!match(token->value, "fi"));
VERBOSE = old_VERBOSE;
}
int what_exit(char* program, int status)
{
/***********************************************************************************
* If the low-order 8 bits of w_status are equal to 0x7F or zero, the child *
* process has stopped. If the low-order 8 bits of w_status are non-zero and are *
* not equal to 0x7F, the child process terminated due to a signal otherwise, the *
* child process terminated due to an exit() call. *
* *
* In the event it was a signal that stopped the process the top 8 bits of *
* w_status contain the signal that caused the process to stop. *
* *
* In the event it was terminated the bottom 7 bits of w_status contain the *
* terminating error number for the process. *
* *
* If bit 0x80 of w_status is set, a core dump was produced. *
***********************************************************************************/
int WIFEXITED = !(status & 0x7F);
int WEXITSTATUS = (status & 0xFF00) >> 8;
int WTERMSIG = status & 0x7F;
int WCOREDUMP = status & 0x80;
int WIFSIGNALED = !((0x7F == WTERMSIG) || (0 == WTERMSIG));
int WIFSTOPPED = ((0x7F == WTERMSIG) && (0 == WCOREDUMP));
if(WIFEXITED)
{
if(VERBOSE_EXIT)
{
fputc('\n', stderr);
fputs(program, stderr);
fputs(" normal termination, exit status = ", stderr);
fputs(int2str(WEXITSTATUS, 10, TRUE), stderr);
fputs("\n\n\n", stderr);
}
return WEXITSTATUS;
}
else if (WIFSIGNALED)
{
fputc('\n', stderr);
fputs(program, stderr);
fputs(" abnormal termination, signal number = ", stderr);
fputs(int2str(WTERMSIG, 10, TRUE), stderr);
fputc('\n', stderr);
if(WCOREDUMP) fputs("core dumped\n", stderr);
return WTERMSIG;
}
else if(WIFSTOPPED)
{
fputc('\n', stderr);
fputs(program, stderr);
fputs(" child stopped, signal number = ", stderr);
fputs(int2str(WEXITSTATUS, 10, TRUE), stderr);
fputc('\n', stderr);
return WEXITSTATUS;
}
fputc('\n', stderr);
fputs(program, stderr);
fputs(" :: something crazy happened with execve\nI'm just gonna get the hell out of here\n", stderr);
exit(EXIT_FAILURE);
}
/* Execute program and check for error */
void execute(FILE* script, char** argv)
{
int status = _execute(script, argv);
if(STRICT == TRUE && (0 != status))
{
/* Clearly the script hit an issue that should never have happened */
fputs("Subprocess error ", stderr);
fputs(int2str(status, 10, TRUE), stderr);
fputs("\nABORTING HARD\n", stderr);
exit(EXIT_FAILURE);
}
}
/* Execute program */
int _execute(FILE* script, char** argv)
{
/* Run the command */
/* rc = return code */
int rc;
/* exec without forking */
int exec = FALSE;
/* Actually do the execution */
if(is_envar(token->value) == TRUE)
{
add_envar();
return 0;
}
else if(match(token->value, "cd"))
{
rc = cd();
if(STRICT)
{
require(rc == SUCCESS, "cd failed!\n");
}
return 0;
}
else if(match(token->value, "set"))
{
rc = set();
if(STRICT)
{
require(rc == SUCCESS, "set failed!\n");
}
return 0;
}
else if(match(token->value, "alias"))
{
add_alias();
return 0;
}
else if(match(token->value, "pwd"))
{
rc = pwd();
if(STRICT)
{
require(rc == SUCCESS, "pwd failed!\n");
}
return 0;
}
else if(match(token->value, "echo"))
{
echo();
return 0;
}
else if(match(token->value, "unset"))
{
unset();
return 0;
}
else if(match(token->value, "exec"))
{
token = token->next; /* Skip the actual exec */
exec = TRUE;
}
else if(match(token->value, "if"))
{
if_cmd(script, argv);
return 0;
}
else if(match(token->value, "then"))
{
/* ignore */
return 0;
}
else if(match(token->value, "else"))
{
/* ignore */
return 0;
}
else if(match(token->value, "fi"))
{
/* ignore */
return 0;
}
/* If it is not a builtin, run it as an executable */
int status; /* i.e. return code */
char** array;
char** envp;
/* Get the full path to the executable */
char* program = find_executable(token->value);
/* Check we can find the executable */
if(NULL == program)
{
if(STRICT == TRUE)
{
fputs("WHILE EXECUTING ", stderr);
fputs(token->value, stderr);
fputs(" NOT FOUND!\nABORTING HARD\n", stderr);
exit(EXIT_FAILURE);
}
/* If we are not strict simply return */
return 0;
}
int f = 0;
#ifdef __uefi__
array = list_to_array(token);
envp = list_to_array(env);
return spawn(program, array, envp);
#else
if(!exec)
{
f = fork();
}
/* Ensure fork succeeded */
if(f == -1)
{
fputs("WHILE EXECUTING ", stderr);
fputs(token->value, stderr);
fputs(" fork() FAILED\nABORTING HARD\n", stderr);
exit(EXIT_FAILURE);
}
else if(f == 0)
{
/* Child */
/**************************************************************
* Fuzzing produces random stuff; we don't want it running *
* dangerous commands. So we just don't execve. *
* But, we still do the list_to_array calls to check for *
* segfaults. *
**************************************************************/
array = list_to_array(token);
envp = list_to_array(env);
if(FALSE == FUZZING)
{
/* We are not fuzzing */
/* execve() returns only on error */
execve(program, array, envp);
}
/* Prevent infinite loops */
_exit(EXIT_FAILURE);
}
/* Otherwise we are the parent */
/* And we should wait for it to complete */
waitpid(f, &status, 0);
return what_exit(program, status);
#endif
}
int collect_command(FILE* script, char** argv)
{
command_done = FALSE;
/* Initialize token */
struct Token* n;
n = calloc(1, sizeof(struct Token));
require(n != NULL, "Memory initialization of token in collect_command failed\n");
char* s = calloc(MAX_STRING, sizeof(char));
require(s != NULL, "Memory initialization of token in collect_command failed\n");
token = n;
int index = 0;
int alias_index;
char* alias_string;
/* Get the tokens */
while(command_done == FALSE)
{
index = collect_token(script, s, index);
/* Don't allocate another node if the current one yielded nothing, OR
* if we are done.
*/
if(match(s, ""))
{
continue;
}
alias_string = alias_lookup(s);
alias_index = 0;
do
{
if(alias_string != NULL)
{
alias_index = collect_alias_token(alias_string, s, alias_index);
}
/* add to token */
n->value = s;
s = calloc(MAX_STRING, sizeof(char));
require(s != NULL, "Memory initialization of next token node in collect_command failed\n");
/* Deal with variables */
handle_variables(argv, n);
/* If the variable expands into nothing */
if(match(n->value, " "))
{
n->value = NULL;
continue;
}
/* Prepare for next loop */
n->next = calloc(1, sizeof(struct Token));
require(n->next != NULL, "Memory initialization of next token node in collect_command failed\n");
n = n->next;
}
while(alias_index != 0);
}
/* -1 means the script is done */
if(EOF == index)
{
return index;
}
/* Output the command if verbose is set */
/* Also if there is nothing in the command skip over */
if(VERBOSE && !match(token->value, "") && !match(token->value, NULL))
{
n = token;
fputs(" +>", stdout);
while(n != NULL)
{
/* Print out each token token */
fputs(" ", stdout);
/* M2-Planet doesn't let us do this in the while */
if(n->value != NULL)
{
if(!match(n->value, ""))
{
fputs(n->value, stdout);
}
}
n = n->next;
}
fputc('\n', stdout);
fflush(stdout);
}
return index;
}
/* Function for executing our programs with desired arguments */
void run_script(FILE* script, char** argv)
{
int index;
while(TRUE)
{
/*
* Tokens has to be reset each time, as we need a new linked-list for
* each line.
* See, the program flows like this as a high level overview:
* Get line -> Sanitize line and perform variable replacement etc ->
* Execute line -> Next.
* We don't need the previous lines once they are done with, so tokens
* are hence for each line.
*/
index = collect_command(script, argv);
/* -1 means the script is done */
if(EOF == index)
{
break;
}
if(0 == index)
{
continue;
}
/* Stuff to exec */
execute(script, argv);
}
}
/* Function to populate env */
void populate_env(char** envp)
{
/* You can't populate a NULL environment */
if(NULL == envp)
{
return;
}
/* avoid empty arrays */
int max = array_length(envp);
if(0 == max)
{
return;
}
/* Initialize env and n */
env = calloc(1, sizeof(struct Token));
require(env != NULL, "Memory initialization of env failed\n");
struct Token* n;
n = env;
int i;
int j;
int k;
char* envp_line;
for(i = 0; i < max; i = i + 1)
{
n->var = calloc(MAX_STRING, sizeof(char));
require(n->var != NULL, "Memory initialization of n->var in population of env failed\n");
n->value = calloc(MAX_STRING, sizeof(char));
require(n->value != NULL, "Memory initialization of n->var in population of env failed\n");
j = 0;
/*
* envp is weird.
* When referencing envp[i]'s characters directly, they were all jumbled.
* So just copy envp[i] to envp_line, and work with that - that seems
* to fix it.
*/
envp_line = calloc(MAX_STRING, sizeof(char));
require(envp_line != NULL, "Memory initialization of envp_line in population of env failed\n");
strcpy(envp_line, envp[i]);
while(envp_line[j] != '=')
{
/* Copy over everything up to = to var */
n->var[j] = envp_line[j];
j = j + 1;
}
/* If we get strange input, we need to ignore it */
if(n->var == NULL)
{
continue;
}
j = j + 1; /* Skip over = */
k = 0; /* As envp[i] will continue as j but n->value begins at 0 */
while(envp_line[j] != 0)
{
/* Copy everything else to value */
n->value[k] = envp_line[j];
j = j + 1;
k = k + 1;
}
/* Sometimes, we get lines like VAR=, indicating nothing is in the variable */
if(n->value == NULL)
{
n->value = "";
}
/* Advance to next part of linked list */
n->next = calloc(1, sizeof(struct Token));
require(n->next != NULL, "Memory initialization of n->next in population of env failed\n");
n = n->next;
}
/* Get rid of node on the end */
n = NULL;
/* Also destroy the n->next reference */
n = env;
while(n->next->var != NULL)
{
n = n->next;
}
n->next = NULL;
}
int main(int argc, char** argv, char** envp)
{
VERBOSE = FALSE;
VERBOSE_EXIT = FALSE;
STRICT = TRUE;
FUZZING = FALSE;
WARNINGS = FALSE;
char* filename = "kaem.run";
FILE* script = NULL;
/* Initalize structs */
token = calloc(1, sizeof(struct Token));
require(token != NULL, "Memory initialization of token failed\n");
if(NULL != argv[0]) KAEM_BINARY = argv[0];
else KAEM_BINARY = "./bin/kaem";
int i = 1;
/* Loop over arguments */
while(i <= argc)
{
if(NULL == argv[i])
{
/* Ignore the argument */
i = i + 1;
}
else if(match(argv[i], "-h") || match(argv[i], "--help"))
{
/* Help information */
fputs("Usage: ", stdout);
fputs(argv[0], stdout);
fputs(" [-h | --help] [-V | --version] [--file filename | -f filename] [-i | --init-mode] [-v | --verbose] [--non-strict] [--warn] [--fuzz]\n", stdout);
exit(EXIT_SUCCESS);
}
else if(match(argv[i], "-f") || match(argv[i], "--file"))
{
/* Set the filename */
if(argv[i + 1] != NULL)
{
filename = argv[i + 1];
}
i = i + 2;
}
else if(match(argv[i], "-i") || match(argv[i], "--init-mode"))
{
/* init mode does not populate env */
INIT_MODE = TRUE;
i = i + 1;
}
else if(match(argv[i], "-V") || match(argv[i], "--version"))
{
/* Output version */
fputs("kaem version 1.4.0\n", stdout);
exit(EXIT_SUCCESS);
}
else if(match(argv[i], "-v") || match(argv[i], "--verbose"))
{
/* Set verbose */
VERBOSE = TRUE;
i = i + 1;
}
else if(match(argv[i], "--strict"))
{
/* it is a NOP */
STRICT = TRUE;
i = i + 1;
}
else if(match(argv[i], "--non-strict"))
{
/* Set strict */
STRICT = FALSE;
i = i + 1;
}
else if(match(argv[i], "--warn"))
{
/* Set warnings */
WARNINGS = TRUE;
i = i + 1;
}
else if(match(argv[i], "--fuzz"))
{
/* Set fuzzing */
FUZZING = TRUE;
i = i + 1;
}
else if(match(argv[i], "--show-exit-codes"))
{
/* show exit codes */
VERBOSE_EXIT = TRUE;
i = i + 1;
}
else if(match(argv[i], "--"))
{
/* Nothing more after this */
break;
}
else
{
/* We don't know this argument */
fputs("UNKNOWN ARGUMENT\n", stdout);
exit(EXIT_FAILURE);
}
}
/* Populate env */
if(INIT_MODE == FALSE)
{
populate_env(envp);
}
/* make sure SHELL is set */
if(NULL == env_lookup("SHELL"))
{
struct Token* shell = calloc(1, sizeof(struct Token));
require(NULL != shell, "unable to create SHELL environment variable\n");
shell->next = env;
shell->var = "SHELL";
shell->value= KAEM_BINARY;
env = shell;
}
/* Populate PATH variable
* We don't need to calloc() because env_lookup() does this for us.
*/
PATH = env_lookup("PATH");
/* Populate USERNAME variable */
char* USERNAME = env_lookup("LOGNAME");
/* Handle edge cases */
if((NULL == PATH) && (NULL == USERNAME))
{
/* We didn't find either of PATH or USERNAME -- use a generic PATH */
PATH = calloc(MAX_STRING, sizeof(char));
require(PATH != NULL, "Memory initialization of PATH failed\n");
strcpy(PATH, "/root/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin");
}
else if(NULL == PATH)
{
/* We did find a username but not a PATH -- use a generic PATH but with /home/USERNAME */
PATH = calloc(MAX_STRING, sizeof(char));
PATH = strcat(PATH, "/home/");
PATH = strcat(PATH, USERNAME);
PATH = strcat(PATH, "/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games");
}
/* Open the script */
script = fopen(filename, "r");
if(NULL == script)
{
fputs("The file: ", stderr);
fputs(filename, stderr);
fputs(" can not be opened!\n", stderr);
exit(EXIT_FAILURE);
}
/* Run the commands */
run_script(script, argv);
/* Cleanup */
fclose(script);
return EXIT_SUCCESS;
}
/* Copyright (C) 2016 Jeremiah Orians
* Copyright (C) 2020 deesix <deesix@tuta.io>
* This file is part of M2-Planet.
*
* M2-Planet is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* M2-Planet is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with M2-Planet. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define FALSE 0
#define TRUE 1
int in_set(int c, char* s);
int match(char* a, char* b);
void require(int bool, char* error);
char* int2str(int x, int base, int signed_p);
void reset_hold_string();
struct type
{
struct type* next;
int size;
int offset;
int is_signed;
struct type* indirect;
struct type* members;
struct type* type;
char* name;
};
struct token_list
{
struct token_list* next;
union
{
struct token_list* locals;
struct token_list* prev;
};
char* s;
union
{
struct type* type;
char* filename;
};
union
{
struct token_list* arguments;
struct token_list* expansion;
int depth;
int linenumber;
};
};
#include "cc_globals.h"
/* Copyright (C) 2016 Jeremiah Orians * Copyright (C) 2020 deesix <deesix@tuta.io> * This file is part of M2-Planet. * * M2-Planet is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * M2-Planet is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with M2-Planet. If not, see <http://www.gnu.org/licenses/>. */ /* What types we have */ struct type* global_types; struct type* prim_types; /* What we are currently working on */ struct token_list* global_token; /* Output reorder collections*/ struct token_list* output_list; struct token_list* strings_list; struct token_list* globals_list; /* Make our string collection more efficient */ char* hold_string; int string_index; int MAX_STRING; /* enable preprocessor-only mode */ int PREPROCESSOR_MODE; /* enable spawn behavior to be effective */ char* M2LIBC_PATH; char* Architecture; char* OperatingSystem; int WORDSIZE; int ENDIAN; char* BASEADDRESS; int STDIO_USED; char* TEMPDIR; /* So we don't shoot ourself in the face */ int FUZZING; int DIRTY_MODE; int DEBUG_LEVEL;
/* Copyright (C) 2016 Jeremiah Orians
* Copyright (C) 2020 deesix <deesix@tuta.io>
* This file is part of M2-Planet.
*
* M2-Planet is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* M2-Planet is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with M2-Planet. If not, see <http://www.gnu.org/licenses/>.
*/
#include"cc.h"
#include <sys/utsname.h>
void init_macro_env(char* sym, char* value, char* source, int num);
char* env_lookup(char* variable);
void clear_string(char* s);
struct utsname* get_uname_data()
{
struct utsname* unameData = calloc(1, sizeof(struct utsname));
require(NULL != unameData, "unameData calloc failed\n");
uname(unameData);
if(4 <= DEBUG_LEVEL)
{
fputs("utsname details: ", stderr);
fputs(unameData->sysname, stderr);
fputc(' ', stderr);
fputs(unameData->machine, stderr);
fputc('\n', stderr);
}
return unameData;
}
void setup_env()
{
if(2 <= DEBUG_LEVEL) fputs("Starting setup_env\n", stderr);
char* ARCH;
if(NULL != Architecture)
{
ARCH = Architecture;
}
else
{
ARCH = NULL;
struct utsname* unameData = get_uname_data();
if(match("i386", unameData->machine) ||
match("i486", unameData->machine) ||
match("i586", unameData->machine) ||
match("i686", unameData->machine) ||
match("i686-pae", unameData->machine)) ARCH = "x86";
else if(match("x86_64", unameData->machine)) ARCH = "amd64";
else ARCH = unameData->machine;
if(3 <= DEBUG_LEVEL)
{
fputs("Architecture selected: ", stderr);
fputs(ARCH, stderr);
fputc('\n', stderr);
}
/* Check for override */
char* hold = env_lookup("ARCHITECTURE_OVERRIDE");
if(NULL != hold)
{
ARCH = hold;
if(3 <= DEBUG_LEVEL)
{
fputs("environmental override for ARCH: ", stderr);
fputs(ARCH, stderr);
fputc('\n', stderr);
}
}
free(unameData);
}
/* Set desired architecture */
WORDSIZE = 32;
ENDIAN = FALSE;
BASEADDRESS = "0x0";
if(match("knight-native", ARCH))
{
if(4 <= DEBUG_LEVEL) fputs("Using knight-native architecture\n", stderr);
ENDIAN = TRUE;
Architecture = "knight-native";
}
else if(match("knight-posix", ARCH))
{
if(4 <= DEBUG_LEVEL) fputs("Using knight-posix architecture\n", stderr);
ENDIAN = TRUE;
Architecture = "knight-posix";
}
else if(match("x86", ARCH))
{
if(4 <= DEBUG_LEVEL) fputs("Using x86 architecture\n", stderr);
BASEADDRESS = "0x8048000";
Architecture = "x86";
init_macro_env("__i386__", "1", "--architecture", 0);
}
else if(match("amd64", ARCH))
{
if(4 <= DEBUG_LEVEL) fputs("Using amd64 architecture\n", stderr);
BASEADDRESS = "0x00600000";
Architecture = "amd64";
WORDSIZE = 64;
init_macro_env("__x86_64__", "1", "--architecture", 0);
}
else if(match("armv7l", ARCH))
{
if(4 <= DEBUG_LEVEL) fputs("Using armv7l architecture\n", stderr);
BASEADDRESS = "0x10000";
Architecture = "armv7l";
init_macro_env("__arm__", "1", "--architecture", 0);
}
else if(match("aarch64", ARCH))
{
if(4 <= DEBUG_LEVEL) fputs("Using aarch64 architecture\n", stderr);
BASEADDRESS = "0x400000";
Architecture = "aarch64";
WORDSIZE = 64;
init_macro_env("__aarch64__", "1", "--architecture", 0);
}
else if(match("riscv32", ARCH))
{
if(4 <= DEBUG_LEVEL) fputs("Using riscv32 architecture\n", stderr);
BASEADDRESS = "0x600000";
Architecture = "riscv32";
init_macro_env("__riscv", "1", "--architecture", 0);
init_macro_env("__riscv_xlen", "32", "--architecture", 1);
}
else if(match("riscv64", ARCH))
{
if(4 <= DEBUG_LEVEL) fputs("Using riscv64 architecture\n", stderr);
BASEADDRESS = "0x600000";
Architecture = "riscv64";
WORDSIZE = 64;
init_macro_env("__riscv", "1", "--architecture", 0);
init_macro_env("__riscv_xlen", "64", "--architecture", 1);
}
else
{
fputs("Unknown architecture: ", stderr);
fputs(ARCH, stderr);
fputs(" know values are: knight-native, knight-posix, x86, amd64, armv7l, aarch64, riscv32 and riscv64\n", stderr);
exit(EXIT_FAILURE);
}
/* Setup Operating System */
if(NULL == OperatingSystem)
{
OperatingSystem = "Linux";
if(3 <= DEBUG_LEVEL)
{
fputs("Operating System selected: ", stderr);
fputs(OperatingSystem, stderr);
fputc('\n', stderr);
}
/* Check for override */
char* hold = env_lookup("OS_OVERRIDE");
if(NULL != hold)
{
OperatingSystem = hold;
if(3 <= DEBUG_LEVEL)
{
fputs("environmental override for OS: ", stderr);
fputs(OperatingSystem, stderr);
fputc('\n', stderr);
}
}
}
if(match("UEFI", OperatingSystem))
{
if(4 <= DEBUG_LEVEL) fputs("Using UEFI\n", stderr);
BASEADDRESS = "0x0";
OperatingSystem = "UEFI";
init_macro_env("__uefi__", "1", "--os", 0);
}
if(2 <= DEBUG_LEVEL) fputs("setup_env successful\n", stderr);
}
struct Token
{
/*
* For the token linked-list, this stores the token; for the env linked-list
* this stores the value of the variable.
*/
char* value;
/*
* Used only for the env linked-list. It holds a string containing the
* name of the var.
*/
char* var;
/*
* This struct stores a node of a singly linked list, store the pointer to
* the next node.
*/
struct Token* next;
};
struct Token* env;
int array_length(char** array)
{
int length = 0;
while(array[length] != NULL)
{
length = length + 1;
}
return length;
}
/* Search for a variable in the token linked-list */
char* token_lookup(char* variable, struct Token* token)
{
if(6 <= DEBUG_LEVEL)
{
fputs("in token_lookup\nLooking for: ", stderr);
fputs(variable, stderr);
fputc('\n', stderr);
}
/* Start at the head */
struct Token* n = token;
/* Loop over the linked-list */
while(n != NULL)
{
if(15 <= DEBUG_LEVEL)
{
fputs(n->var, stderr);
fputc('\n', stderr);
}
if(match(variable, n->var))
{
if(6 <= DEBUG_LEVEL) fputs("match found in token_lookup\n", stderr);
/* We have found the correct node */
return n->value; /* Done */
}
/* Nope, try the next */
n = n->next;
}
/* We didn't find anything! */
return NULL;
}
/* Search for a variable in the env linked-list */
char* env_lookup(char* variable)
{
return token_lookup(variable, env);
}
char* envp_hold;
int envp_index;
void reset_envp_hold()
{
clear_string(envp_hold);
envp_index = 0;
}
void push_env_byte(int c)
{
envp_hold[envp_index] = c;
envp_index = envp_index + 1;
require(4096 > envp_index, "Token exceeded 4096 char envp limit\n");
}
struct Token* process_env_variable(char* envp_line, struct Token* n)
{
struct Token* node = calloc(1, sizeof(struct Token));
require(node != NULL, "Memory initialization of node failed\n");
reset_envp_hold();
int i = 0;
while(envp_line[i] != '=')
{
/* Copy over everything up to = to var */
push_env_byte(envp_line[i]);
i = i + 1;
}
node->var = calloc(i + 2, sizeof(char));
require(node->var != NULL, "Memory initialization of n->var in population of env failed\n");
strcpy(node->var, envp_hold);
i = i + 1; /* Skip over = */
reset_envp_hold();
while(envp_line[i] != 0)
{
/* Copy everything else to value */
push_env_byte(envp_line[i]);
i = i + 1;
}
/* Sometimes, we get lines like VAR=, indicating nothing is in the variable */
if(0 == strlen(envp_hold))
{
node->value = "";
}
else
{
/* but looks like we got something so, lets use it */
node->value = calloc(strlen(envp_hold) + 2, sizeof(char));
require(node->value != NULL, "Memory initialization of n->var in population of env failed\n");
strcpy(node->value, envp_hold);
}
node->next = n;
return node;
}
void populate_env(char** envp)
{
if(2 <= DEBUG_LEVEL) fputs("populate_env started\n", stderr);
/* You can't populate a NULL environment */
if(NULL == envp)
{
if(3 <= DEBUG_LEVEL) fputs("NULL envp\n", stderr);
return;
}
/* avoid empty arrays */
int max = array_length(envp);
if(0 == max)
{
if(3 <= DEBUG_LEVEL) fputs("Empty envp\n", stderr);
return;
}
/* Initialize env and n */
env = NULL;
int i;
envp_hold = calloc(4096, sizeof(char));
require(envp_hold != NULL, "Memory initialization of envp_hold in population of env failed\n");
char* envp_line = calloc(4096, sizeof(char));
require(envp_line != NULL, "Memory initialization of envp_line in population of env failed\n");
if(3 <= DEBUG_LEVEL) fputs("starting env loop\n", stderr);
for(i = 0; i < max; i = i + 1)
{
/*
* envp is weird.
* When referencing envp[i]'s characters directly, they were all jumbled.
* So just copy envp[i] to envp_line, and work with that - that seems
* to fix it.
*/
clear_string(envp_line);
require(4096 > strlen(envp[i]), "envp line exceeds 4096byte limit\n");
strcpy(envp_line, envp[i]);
if(9 <= DEBUG_LEVEL)
{
fputs("trying envp_line: ", stderr);
fputs(envp_line, stderr);
fputc('\n', stderr);
}
env = process_env_variable(envp_line, env);
if(9 <= DEBUG_LEVEL)
{
fputs("got var of: ", stderr);
fputs(env->var, stderr);
fputs("\nAnd value of: ", stderr);
fputs(env->value, stderr);
fputc('\n', stderr);
}
}
free(envp_line);
free(envp_hold);
if(3 <= DEBUG_LEVEL)
{
fputs("\n\nenv loop successful\n", stderr);
fputs(int2str(i, 10, FALSE), stderr);
fputs(" envp records processed\n\n", stderr);
}
require(NULL != env, "can't have an empty environment from the creation of a non-null environment\n");
if(2 <= DEBUG_LEVEL) fputs("populate_env successful\n", stderr);
}
/* Copyright (C) 2016 Jeremiah Orians
* Copyright (C) 2021 Andrius Å tikonas <andrius@stikonas.eu>
* This file is part of M2-Planet.
*
* M2-Planet is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* M2-Planet is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with M2-Planet. If not, see <http://www.gnu.org/licenses/>.
*/
#include "cc.h"
char* env_lookup(char* variable);
char* int2str(int x, int base, int signed_p);
struct visited
{
struct visited* prev;
char* name;
};
/* Globals */
FILE* input;
struct token_list* token;
int line;
char* file;
struct visited* vision;
int previously_seen(char* s)
{
struct visited* v = vision;
while(NULL != v)
{
if(match(v->name, s)) return TRUE;
v = v->prev;
}
return FALSE;
}
void just_seen(char* s)
{
struct visited* hold = calloc(1, sizeof(struct visited));
hold->prev = vision;
hold->name = s;
vision = hold;
}
int grab_byte()
{
int c = fgetc(input);
if(10 == c) line = line + 1;
return c;
}
void push_byte(int c)
{
hold_string[string_index] = c;
string_index = string_index + 1;
require(MAX_STRING > string_index, "Token exceeded MAX_STRING char limit\nuse --max-string number to increase\n");
}
int consume_byte(int c)
{
push_byte(c);
return grab_byte();
}
int preserve_string(int c)
{
int frequent = c;
int escape = FALSE;
do
{
if(!escape && '\\' == c ) escape = TRUE;
else escape = FALSE;
c = consume_byte(c);
require(EOF != c, "Unterminated string\n");
} while(escape || (c != frequent));
c = consume_byte(frequent);
return c;
}
void copy_string(char* target, char* source, int max)
{
int i = 0;
while(0 != source[i])
{
target[i] = source[i];
i = i + 1;
if(i == max) break;
}
}
int preserve_keyword(int c, char* S)
{
while(in_set(c, S))
{
c = consume_byte(c);
}
return c;
}
void clear_string(char* s)
{
int i = 0;
while(0 != s[i])
{
s[i] = 0;
i = i + 1;
require(i < MAX_STRING, "string exceeded max string size while clearing string\n");
}
}
void reset_hold_string()
{
clear_string(hold_string);
string_index = 0;
}
/* note if this is the first token in the list, head needs fixing up */
struct token_list* eat_token(struct token_list* token)
{
if(NULL != token->prev)
{
token->prev->next = token->next;
}
/* update backlinks */
if(NULL != token->next)
{
token->next->prev = token->prev;
}
return token->next;
}
void new_token(char* s, int size)
{
struct token_list* current = calloc(1, sizeof(struct token_list));
require(NULL != current, "Exhausted memory while getting token\n");
/* More efficiently allocate memory for string */
current->s = calloc(size, sizeof(char));
require(NULL != current->s, "Exhausted memory while trying to copy a token\n");
copy_string(current->s, s, MAX_STRING);
current->prev = token;
current->next = token;
current->linenumber = line;
current->filename = file;
token = current;
}
int get_token(int c)
{
reset_hold_string();
if(c == EOF)
{
return c;
}
else if((32 == c) || (9 == c) || (c == '\n'))
{
c = consume_byte(c);
}
else if('#' == c)
{
c = consume_byte(c);
c = preserve_keyword(c, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_");
}
else if(in_set(c, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"))
{
c = preserve_keyword(c, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_:");
}
else if(in_set(c, "<=>|&!^%"))
{
c = preserve_keyword(c, "<=>|&!^%");
}
else if(in_set(c, "'\""))
{
c = preserve_string(c);
}
else if(c == '/')
{
c = consume_byte(c);
if(c == '*')
{
c = consume_byte(c);
while(c != '/')
{
while(c != '*')
{
c = consume_byte(c);
require(EOF != c, "Hit EOF inside of block comment\n");
}
c = consume_byte(c);
require(EOF != c, "Hit EOF inside of block comment\n");
}
c = consume_byte(c);
}
else if(c == '/')
{
while(c != '\n')
{
c = consume_byte(c);
require(EOF != c, "Hit EOF inside of line comment\n");
}
c = consume_byte(c);
}
else if(c == '=')
{
c = consume_byte(c);
}
}
else if(c == '*')
{
c = consume_byte(c);
if(c == '=')
{
c = consume_byte(c);
}
}
else if(c == '+')
{
c = consume_byte(c);
if(c == '=')
{
c = consume_byte(c);
}
if(c == '+')
{
c = consume_byte(c);
}
}
else if(c == '-')
{
c = consume_byte(c);
if(c == '=')
{
c = consume_byte(c);
}
if(c == '>')
{
c = consume_byte(c);
}
if(c == '-')
{
c = consume_byte(c);
}
}
else
{
c = consume_byte(c);
}
return c;
}
struct token_list* reverse_list(struct token_list* head)
{
struct token_list* root = NULL;
struct token_list* next;
while(NULL != head)
{
next = head->next;
head->next = root;
root = head;
head = next;
}
return root;
}
int read_include(int c)
{
reset_hold_string();
int done = FALSE;
int ch;
while(!done)
{
if(c == EOF)
{
fputs("we don't support EOF as a filename in #include statements\n", stderr);
exit(EXIT_FAILURE);
}
else if((32 == c) || (9 == c) || (c == '\n'))
{
c = grab_byte();
}
else if(('"' == c) || ('<' == c))
{
if('<' == c) c = '>';
ch = c;
do
{
c = consume_byte(c);
require(EOF != c, "Unterminated filename in #include\n");
} while(c != ch);
if('>' == ch) hold_string[0] = '<';
done = TRUE;
}
}
return c;
}
void insert_file_header(char* name, int line)
{
char* hold_line = int2str(line, 10, FALSE);
reset_hold_string();
strcat(hold_string, "// #FILENAME ");
strcat(hold_string, name);
strcat(hold_string, " ");
strcat(hold_string, hold_line);
new_token(hold_string, strlen(hold_string)+2);
new_token("\n", 3);
}
struct token_list* read_all_tokens(FILE* a, struct token_list* current, char* filename, int include);
int include_file(int ch, int include_file)
{
/* The old state to restore to */
char* hold_filename = file;
FILE* hold_input = input;
int hold_number;
/* The new file to load */
char* new_filename;
FILE* new_file;
require(EOF != ch, "#include failed to receive filename\n");
/* Remove the #include */
token = token->next;
/* Get new filename */
read_include(ch);
/* with just a little extra to put in the matching at the end */
new_token(hold_string, string_index + 3);
ch = '\n';
new_filename = token->s;
/* Remove name from stream */
token = token->next;
/* Try to open the file */
if('<' == new_filename[0])
{
if(match("stdio.h", new_filename + 1)) STDIO_USED = TRUE;
reset_hold_string();
strcat(hold_string, M2LIBC_PATH);
strcat(hold_string, "/");
strcat(hold_string, new_filename + 1);
strcat(new_filename, ">");
new_file = fopen(hold_string, "r");
}
else
{
if(match("M2libc/bootstrappable.h", new_filename+1))
{
reset_hold_string();
strcat(hold_string, M2LIBC_PATH);
strcat(hold_string, "/bootstrappable.h");
new_file = fopen(hold_string, "r");
}
else new_file = fopen(new_filename+1, "r");
strcat(new_filename, "\"");
}
/* prevent multiple visits */
if(previously_seen(new_filename)) return ch;
just_seen(new_filename);
/* special case this compatibility crap */
if(match("\"../gcc_req.h\"", new_filename) || match("\"gcc_req.h\"", new_filename)) return ch;
if(include_file)
{
fputs("reading file: ", stderr);
fputs(new_filename, stderr);
fputc('\n', stderr);
}
/* catch garbage input */
if(NULL == new_file)
{
fputs("unable to read file: ", stderr);
fputs(new_filename, stderr);
fputs("\nAborting hard!\n", stderr);
exit(EXIT_FAILURE);
}
/* protect our current line number */
hold_number = line + 1;
/* Read the new file */
if(include_file) read_all_tokens(new_file, token, new_filename, include_file);
/* put back old file info */
insert_file_header(hold_filename, hold_number);
/* resume reading old file */
input = hold_input;
line = hold_number;
file = hold_filename;
return ch;
}
struct token_list* read_all_tokens(FILE* a, struct token_list* current, char* filename, int include)
{
token = current;
insert_file_header(filename, 1);
input = a;
line = 1;
file = filename;
int ch = grab_byte();
while(EOF != ch)
{
ch = get_token(ch);
new_token(hold_string, string_index + 2);
if(match("#include", token->s)) ch = include_file(ch, include);
}
return token;
}
/* Copyright (C) 2016 Jeremiah Orians
* Copyright (C) 2020 deesix <deesix@tuta.io>
* This file is part of M2-Planet.
*
* M2-Planet is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* M2-Planet is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with M2-Planet. If not, see <http://www.gnu.org/licenses/>.
*/
#include"cc.h"
#include <unistd.h>
#include <sys/wait.h>
#define MAX_ARRAY 256
char* env_lookup(char* variable);
/* Function to find a character in a string */
char* find_char(char* string, char a)
{
if(0 == string[0])
{
return NULL;
}
while(a != string[0])
{
string = string + 1;
if(0 == string[0])
{
return string;
}
}
return string;
}
/* Find the full path to an executable */
char* find_executable(char* name)
{
char* PATH = env_lookup("PATH");
require(NULL != PATH, "No PATH found\nAborting\n");
if(match("", name))
{
return NULL;
}
if(('.' == name[0]) || ('/' == name[0]))
{
/* assume names that start with . or / are relative or absolute */
return name;
}
char* trial = calloc(MAX_STRING, sizeof(char));
char* MPATH = calloc(MAX_STRING, sizeof(char)); /* Modified PATH */
require(MPATH != NULL, "Memory initialization of MPATH in find_executable failed\n");
strcpy(MPATH, PATH);
FILE* t;
char* next = find_char(MPATH, ':');
int index;
int offset;
int mpath_length;
int name_length;
int trial_length;
while(NULL != next)
{
/* Reset trial */
trial_length = strlen(trial);
for(index = 0; index < trial_length; index = index + 1)
{
trial[index] = 0;
}
next[0] = 0;
/* prepend_string(MPATH, prepend_string("/", name)) */
mpath_length = strlen(MPATH);
for(index = 0; index < mpath_length; index = index + 1)
{
require(MAX_STRING > index, "Element of PATH is too long\n");
trial[index] = MPATH[index];
}
trial[index] = '/';
offset = strlen(trial);
name_length = strlen(name);
for(index = 0; index < name_length; index = index + 1)
{
require(MAX_STRING > index, "Element of PATH is too long\n");
trial[index + offset] = name[index];
}
/* Try the trial */
trial_length = strlen(trial);
require(trial_length < MAX_STRING, "COMMAND TOO LONG!\nABORTING HARD\n");
t = fopen(trial, "r");
if(NULL != t)
{
fclose(t);
return trial;
}
MPATH = next + 1;
next = find_char(MPATH, ':');
}
return NULL;
}
void sanity_command_check(char** array)
{
int i = 0;
char* s = array[0];
while(NULL != s)
{
fputs(s, stderr);
fputc(' ', stderr);
i = i + 1;
s = array[i];
}
fputc('\n', stderr);
}
int what_exit(char* program, int status)
{
/***********************************************************************************
* If the low-order 8 bits of w_status are equal to 0x7F or zero, the child *
* process has stopped. If the low-order 8 bits of w_status are non-zero and are *
* not equal to 0x7F, the child process terminated due to a signal otherwise, the *
* child process terminated due to an exit() call. *
* *
* In the event it was a signal that stopped the process the top 8 bits of *
* w_status contain the signal that caused the process to stop. *
* *
* In the event it was terminated the bottom 7 bits of w_status contain the *
* terminating error number for the process. *
* *
* If bit 0x80 of w_status is set, a core dump was produced. *
***********************************************************************************/
if(DEBUG_LEVEL > 6)
{
fputs("in what_exit with char* program of: ", stderr);
fputs(program, stderr);
fputs("\nAnd int status of: 0x", stderr);
fputs(int2str(status, 16, FALSE), stderr);
fputc('\n', stderr);
}
int WIFEXITED = !(status & 0x7F);
int WEXITSTATUS = (status & 0xFF00) >> 8;
int WTERMSIG = status & 0x7F;
int WCOREDUMP = status & 0x80;
int WIFSIGNALED = !((0x7F == WTERMSIG) || (0 == WTERMSIG));
int WIFSTOPPED = ((0x7F == WTERMSIG) && (0 == WCOREDUMP));
if(WIFEXITED)
{
if(DEBUG_LEVEL > 2)
{
fputc('\n', stderr);
fputs(program, stderr);
fputs(" normal termination, exit status = ", stderr);
fputs(int2str(WEXITSTATUS, 10, TRUE), stderr);
fputc('\n', stderr);
}
return WEXITSTATUS;
}
else if (WIFSIGNALED)
{
fputc('\n', stderr);
fputs(program, stderr);
fputs(" abnormal termination, signal number = ", stderr);
fputs(int2str(WTERMSIG, 10, TRUE), stderr);
fputc('\n', stderr);
if(WCOREDUMP) fputs("core dumped\n", stderr);
return WTERMSIG;
}
else if(WIFSTOPPED)
{
fputc('\n', stderr);
fputs(program, stderr);
fputs(" child stopped, signal number = ", stderr);
fputs(int2str(WEXITSTATUS, 10, TRUE), stderr);
fputc('\n', stderr);
return WEXITSTATUS;
}
fputc('\n', stderr);
fputs(program, stderr);
fputs(" :: something crazy happened with execve\nI'm just gonna get the hell out of here\n", stderr);
exit(EXIT_FAILURE);
}
void _execute(char* name, char** array, char** envp)
{
int status; /* i.e. return code */
/* Get the full path to the executable */
char* program = find_executable(name);
/* Check we can find the executable */
if(NULL == program)
{
fputs("WHILE EXECUTING ", stderr);
fputs(name, stderr);
fputs(" NOT FOUND!\nABORTING HARD\n", stderr);
exit(EXIT_FAILURE);
}
sanity_command_check(array);
int result;
#ifdef __uefi__
result = spawn(program, array, envp);
#else
int f = fork();
/* Ensure fork succeeded */
if(f == -1)
{
fputs("WHILE EXECUTING ", stderr);
fputs(name, stderr);
fputs("fork() FAILED\nABORTING HARD\n", stderr);
exit(EXIT_FAILURE);
}
else if(f == 0)
{
/* Child */
/**************************************************************
* Fuzzing produces random stuff; we don't want it running *
* dangerous commands. So we just don't execve. *
**************************************************************/
if(FALSE == FUZZING)
{
/* We are not fuzzing */
/* execve() returns only on error */
execve(program, array, envp);
fputs("Unable to execute: ", stderr);
fputs(program, stderr);
fputs("\nPlease check file permissions and that it is a valid binary\n", stderr);
}
/* Prevent infinite loops */
_exit(EXIT_FAILURE);
}
/* Otherwise we are the parent */
/* And we should wait for it to complete */
waitpid(f, &status, 0);
result = what_exit(program, status);
#endif
if(0 != result)
{
fputs("Subprocess: ", stderr);
fputs(program, stderr);
fputs(" error\nAborting for safety\n", stderr);
exit(result);
}
}
void insert_array(char** array, int index, char* string)
{
int size = strlen(string);
array[index] = calloc(size+2, sizeof(char));
strcpy(array[index], string);
}
void spawn_hex2(char* input, char* output, char* architecture, char** envp, int debug)
{
char* hex2;
#ifdef __uefi__
hex2 = "hex2.efi";
#else
hex2 = "hex2";
#endif
char* elf_header = calloc(MAX_STRING, sizeof(char));
elf_header = strcat(elf_header, M2LIBC_PATH);
elf_header = strcat(elf_header, "/");
elf_header = strcat(elf_header, architecture);
if(match("UEFI", OperatingSystem)) elf_header = strcat(elf_header, "/uefi/PE32-");
else elf_header = strcat(elf_header, "/ELF-");
elf_header = strcat(elf_header, architecture);
if(debug)
{
elf_header = strcat(elf_header, "-debug.hex2");
}
else
{
elf_header = strcat(elf_header, ".hex2");
}
fputs("# starting hex2 linking\n", stdout);
char** array = calloc(MAX_ARRAY, sizeof(char*));
insert_array(array, 0, hex2);
insert_array(array, 1, "--file");
insert_array(array, 2, elf_header);
insert_array(array, 3, "--file");
insert_array(array, 4, input);
insert_array(array, 5, "--output");
insert_array(array, 6, output);
insert_array(array, 7, "--architecture");
insert_array(array, 8, architecture);
insert_array(array, 9, "--base-address");
insert_array(array, 10, BASEADDRESS);
if(ENDIAN)
{
insert_array(array, 11, "--big-endian");
}
else
{
insert_array(array, 11, "--little-endian");
}
_execute(hex2, array, envp);
}
void spawn_M1(char* input, char* debug_file, char* output, char* architecture, char** envp, int debug_flag)
{
char* M1;
#ifdef __uefi__
M1 = "M1.efi";
#else
M1 = "M1";
#endif
fputs("# starting M1 assembly\n", stdout);
char* definitions = calloc(MAX_STRING, sizeof(char));
definitions = strcat(definitions, M2LIBC_PATH);
definitions = strcat(definitions, "/");
definitions = strcat(definitions, architecture);
definitions = strcat(definitions, "/");
definitions = strcat(definitions, architecture);
definitions = strcat(definitions, "_defs.M1");
char* libc = calloc(MAX_STRING, sizeof(char));
libc = strcat(libc, M2LIBC_PATH);
libc = strcat(libc, "/");
libc = strcat(libc, architecture);
if(match("UEFI", OperatingSystem))
{
libc = strcat(libc, "/uefi/libc-full.M1");
}
else if(STDIO_USED)
{
libc = strcat(libc, "/libc-full.M1");
}
else
{
libc = strcat(libc, "/libc-core.M1");
}
char** array = calloc(MAX_ARRAY, sizeof(char*));
insert_array(array, 0, M1);
insert_array(array, 1, "--file");
insert_array(array, 2, definitions);
insert_array(array, 3, "--file");
insert_array(array, 4, libc);
insert_array(array, 5, "--file");
insert_array(array, 6, input);
if(ENDIAN)
{
insert_array(array, 7, "--big-endian");
}
else
{
insert_array(array, 7, "--little-endian");
}
insert_array(array, 8, "--architecture");
insert_array(array, 9, architecture);
if(debug_flag)
{
insert_array(array, 10, "--file");
insert_array(array, 11, debug_file);
insert_array(array, 12, "--output");
insert_array(array, 13, output);
}
else
{
insert_array(array, 10, "--output");
insert_array(array, 11, output);
}
_execute(M1, array, envp);
}
void spawn_blood_elf(char* input, char* output, char** envp, int large_flag)
{
char* blood_elf;
#ifdef __uefi__
blood_elf = "blood-elf.efi";
#else
blood_elf = "blood-elf";
#endif
fputs("# starting Blood-elf stub generation\n", stdout);
char** array = calloc(MAX_ARRAY, sizeof(char*));
insert_array(array, 0,blood_elf);
insert_array(array, 1, "--file");
insert_array(array, 2, input);
if(ENDIAN)
{
insert_array(array, 3, "--big-endian");
}
else
{
insert_array(array, 3, "--little-endian");
}
insert_array(array, 4, "--output");
insert_array(array, 5, output);
if(large_flag) insert_array(array, 6, "--64");
_execute(blood_elf, array, envp);
}
void spawn_M2(char* input, char* output, char* architecture, char** envp, int debug_flag)
{
char* M2_Planet;
#ifdef __uefi__
M2_Planet = "M2-Planet.efi";
#else
M2_Planet = "M2-Planet";
#endif
fputs("# starting M2-Planet build\n", stdout);
char** array = calloc(MAX_ARRAY, sizeof(char*));
insert_array(array, 0, M2_Planet);
insert_array(array, 1, "--file");
insert_array(array, 2, input);
insert_array(array, 3, "--output");
insert_array(array, 4, output);
insert_array(array, 5, "--architecture");
insert_array(array, 6, architecture);
if(debug_flag) insert_array(array, 7, "--debug");
_execute(M2_Planet, array, envp);
}
void spawn_processes(int debug_flag, char* prefix, char* preprocessed_file, char* destination, char** envp)
{
int large_flag = FALSE;
if(WORDSIZE > 32) large_flag = TRUE;
if(match("UEFI", OperatingSystem))
{
debug_flag = FALSE;
}
char* M2_output = calloc(100, sizeof(char));
strcpy(M2_output, prefix);
strcat(M2_output, "/M2-Planet-XXXXXX");
int i = mkstemp(M2_output);
if(-1 != i)
{
close(i);
spawn_M2(preprocessed_file, M2_output, Architecture, envp, debug_flag);
}
else
{
fputs("unable to get a tempfile for M2-Planet output\n", stderr);
exit(EXIT_FAILURE);
}
char* blood_output = "";
if(debug_flag)
{
blood_output = calloc(100, sizeof(char));
strcpy(blood_output, prefix);
strcat(blood_output, "/blood-elf-XXXXXX");
i = mkstemp(blood_output);
if(-1 != i)
{
close(i);
spawn_blood_elf(M2_output, blood_output, envp, large_flag);
}
else
{
fputs("unable to get a tempfile for blood-elf output\n", stderr);
exit(EXIT_FAILURE);
}
}
char* M1_output = calloc(100, sizeof(char));
strcpy(M1_output, prefix);
strcat(M1_output, "/M1-macro-XXXXXX");
i = mkstemp(M1_output);
if(-1 != i)
{
close(i);
spawn_M1(M2_output, blood_output, M1_output, Architecture, envp, debug_flag);
}
else
{
fputs("unable to get a tempfile for M1 output\n", stderr);
exit(EXIT_FAILURE);
}
/* We no longer need the M2-Planet tempfile output */
if(!DIRTY_MODE) remove(M2_output);
/* Nor the blood-elf output anymore if it exists */
if(!match("", blood_output))
{
if(!DIRTY_MODE) remove(blood_output);
}
/* Build the final binary */
spawn_hex2(M1_output, destination, Architecture, envp, debug_flag);
/* clean up after ourselves*/
if(!DIRTY_MODE) remove(M1_output);
}
/* Copyright (C) 2016 Jeremiah Orians
* Copyright (C) 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
* Copyright (C) 2020 deesix <deesix@tuta.io>
* This file is part of M2-Planet.
*
* M2-Planet is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* M2-Planet is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with M2-Planet. If not, see <http://www.gnu.org/licenses/>.
*/
#include "cc.h"
#include "gcc_req.h"
#include <stdint.h>
/* Imported functions */
char* int2str(int x, int base, int signed_p);
void line_error_token(struct token_list *token)
{
if(NULL == token)
{
fputs("EOF reached inside of line_error\n", stderr);
fputs("problem at end of file\n", stderr);
return;
}
fputs(token->filename, stderr);
fputs(":", stderr);
fputs(int2str(token->linenumber, 10, TRUE), stderr);
fputs(":", stderr);
}
void line_error()
{
line_error_token(global_token);
}
void require_match(char* message, char* required)
{
require(NULL != global_token, "EOF reached inside of require match\n");
if(!match(global_token->s, required))
{
line_error();
fputs(message, stderr);
exit(EXIT_FAILURE);
}
global_token = global_token->next;
require(NULL != global_token, "EOF after require match occurred\n");
}
void output_tokens(struct token_list *i, FILE* out)
{
while(NULL != i)
{
fputs(i->s, out);
i = i->next;
}
}
/* Copyright (C) 2021 Sanne Wouda
* Copyright (C) 2021 Andrius Å tikonas <andrius@stikonas.eu>
* Copyright (C) 2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
* This file is part of M2-Planet.
*
* M2-Planet is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* M2-Planet is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with M2-Planet. If not, see <http://www.gnu.org/licenses/>.
*/
#include "cc.h"
#include "gcc_req.h"
void require(int bool, char* error);
int strtoint(char* a);
void line_error_token(struct token_list* list);
struct token_list* eat_token(struct token_list* head);
struct token_list* reverse_list(struct token_list* head);
struct conditional_inclusion
{
struct conditional_inclusion* prev;
int include; /* 1 == include, 0 == skip */
int previous_condition_matched; /* 1 == all subsequent conditions treated as FALSE */
};
struct macro_list
{
struct macro_list* next;
char* symbol;
struct token_list* expansion;
struct token_list* arguments;
};
struct macro_list* macro_env;
struct conditional_inclusion* conditional_inclusion_top;
/* point where we are currently modifying the global_token list */
struct token_list* macro_token;
void init_macro_env(char* sym, char* value, char* source, int num)
{
struct macro_list* hold = macro_env;
macro_env = calloc(1, sizeof(struct macro_list));
macro_env->symbol = sym;
macro_env->next = hold;
macro_env->expansion = calloc(1, sizeof(struct token_list));
macro_env->expansion->s = value;
macro_env->expansion->filename = source;
macro_env->expansion->linenumber = num;
}
void _eat_current_token(int eat_whitespace)
{
int update_global_token = FALSE;
if (macro_token == global_token)
update_global_token = TRUE;
macro_token = eat_token(macro_token);
if(eat_whitespace)
{
while (macro_token->s[0] == ' ')
macro_token = eat_token(macro_token);
}
if(update_global_token)
global_token = macro_token;
}
void eat_current_token()
{
_eat_current_token(TRUE);
}
void eat_current_token_without_space()
{
_eat_current_token(FALSE);
}
struct token_list* lookup_token(struct token_list* token, struct token_list* arguments)
{
if(NULL == token)
{
fputs("null token received in token\n", stderr);
exit(EXIT_FAILURE);
}
struct token_list* hold = arguments;
while (NULL != hold)
{
if (match(token->s, hold->s))
{
/* found! */
return hold->expansion;
}
hold = hold->next;
}
/* not found! */
return NULL;
}
/* returns the first token inserted; inserts *before* point */
struct token_list* insert_tokens(struct token_list* point, struct token_list* token)
{
struct token_list* copy;
struct token_list* first = NULL;
while (NULL != token)
{
copy = calloc(1, sizeof(struct token_list));
copy->s = token->s;
copy->filename = token->filename;
copy->linenumber = token->linenumber;
if(NULL == first)
{
first = copy;
}
copy->next = point;
if (NULL != point)
{
copy->prev = point->prev;
if(NULL != point->prev)
{
point->prev->next = copy;
}
point->prev = copy;
}
token = token->next;
}
return first;
}
/* returns the first token inserted; inserts *before* point */
struct token_list* copy_list(struct token_list* token)
{
struct token_list* copy;
struct token_list* prev = NULL;
while (NULL != token)
{
copy = calloc(1, sizeof(struct token_list));
copy->s = token->s;
copy->next = prev;
copy->prev = prev;
prev = copy;
token = token->next;
}
copy = reverse_list(copy);
return copy;
}
struct macro_list* lookup_macro(struct token_list* token)
{
if(NULL == token)
{
line_error_token(macro_token);
fputs("null token received in lookup_macro\n", stderr);
exit(EXIT_FAILURE);
}
struct macro_list* hold = macro_env;
while (NULL != hold)
{
if (match(token->s, hold->symbol))
{
/* found! */
return hold;
}
hold = hold->next;
}
/* not found! */
return NULL;
}
void remove_macro(struct token_list* token)
{
if(NULL == token)
{
line_error_token(macro_token);
fputs("received a null in remove_macro\n", stderr);
exit(EXIT_FAILURE);
}
struct macro_list* hold = macro_env;
struct macro_list* temp;
/* Deal with the first element */
if (match(token->s, hold->symbol)) {
macro_env = hold->next;
free(hold);
return;
}
/* Remove element form the middle of linked list */
while (NULL != hold->next)
{
if (match(token->s, hold->next->symbol))
{
temp = hold->next;
hold->next = hold->next->next;
free(temp);
return;
}
hold = hold->next;
}
/* nothing to undefine */
return;
}
int macro_expression();
int macro_variable()
{
int value = 0;
struct macro_list* hold = lookup_macro(macro_token);
if (NULL != hold)
{
if(NULL == hold->expansion)
{
line_error_token(macro_token);
fputs("hold->expansion is a null\n", stderr);
exit(EXIT_FAILURE);
}
value = strtoint(hold->expansion->s);
}
eat_current_token();
return value;
}
int macro_number()
{
int result = strtoint(macro_token->s);
eat_current_token();
return result;
}
int macro_primary_expr()
{
int defined_has_paren = FALSE;
int hold;
require(NULL != macro_token, "got an EOF terminated macro primary expression\n");
if('-' == macro_token->s[0])
{
eat_current_token();
return -macro_primary_expr();
}
else if('!' == macro_token->s[0])
{
eat_current_token();
return !macro_primary_expr();
}
else if('(' == macro_token->s[0])
{
eat_current_token();
hold = macro_expression();
require(')' == macro_token->s[0], "missing ) in macro expression\n");
eat_current_token();
return hold;
}
else if(match("defined", macro_token->s))
{
eat_current_token();
require(NULL != macro_token, "got an EOF terminated macro defined expression\n");
if('(' == macro_token->s[0])
{
defined_has_paren = TRUE;
eat_current_token();
}
if (NULL != lookup_macro(macro_token))
{
hold = TRUE;
}
else
{
hold = FALSE;
}
eat_current_token();
if(TRUE == defined_has_paren)
{
if(NULL == macro_token)
{
line_error_token(macro_token);
fputs("unterminated define ( statement\n", stderr);
exit(EXIT_FAILURE);
}
require(')' == macro_token->s[0], "missing close parenthesis for defined()\n");
eat_current_token();
}
return hold;
}
else if(in_set(macro_token->s[0], "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"))
{
return macro_variable();
}
else if(in_set(macro_token->s[0], "0123456789"))
{
return macro_number();
}
else
{
return 0; /* FIXME: error handling */
}
}
int macro_additive_expr()
{
int lhs = macro_primary_expr();
int hold;
require(NULL != macro_token, "got an EOF terminated macro additive expression\n");
if(match("+", macro_token->s))
{
eat_current_token();
return lhs + macro_additive_expr();
}
else if(match("-", macro_token->s))
{
eat_current_token();
return lhs - macro_additive_expr();
}
else if(match("*", macro_token->s))
{
eat_current_token();
return lhs * macro_additive_expr();
}
else if(match("/", macro_token->s))
{
eat_current_token();
hold = macro_additive_expr();
require(0 != hold, "divide by zero not valid even in C macros\n");
return lhs / hold;
}
else if(match("%", macro_token->s))
{
eat_current_token();
hold = macro_additive_expr();
require(0 != hold, "modulus by zero not valid even in C macros\n");
return lhs % hold;
}
else if(match(">>", macro_token->s))
{
eat_current_token();
return lhs >> macro_additive_expr();
}
else if(match("<<", macro_token->s))
{
eat_current_token();
return lhs << macro_additive_expr();
}
else
{
return lhs;
}
}
int macro_relational_expr()
{
int lhs = macro_additive_expr();
if(match("<", macro_token->s))
{
eat_current_token();
return lhs < macro_relational_expr();
}
else if(match("<=", macro_token->s))
{
eat_current_token();
return lhs <= macro_relational_expr();
}
else if(match(">=", macro_token->s))
{
eat_current_token();
return lhs >= macro_relational_expr();
}
else if(match(">", macro_token->s))
{
eat_current_token();
return lhs > macro_relational_expr();
}
else if(match("==", macro_token->s))
{
eat_current_token();
return lhs == macro_relational_expr();
}
else if(match("!=", macro_token->s))
{
eat_current_token();
return lhs != macro_relational_expr();
}
else
{
return lhs;
}
}
int macro_bitwise_expr()
{
int rhs;
int lhs = macro_relational_expr();
if(match("&", macro_token->s))
{
eat_current_token();
return lhs & macro_bitwise_expr();
}
else if(match("&&", macro_token->s))
{
eat_current_token();
rhs = macro_bitwise_expr();
return lhs && rhs;
}
else if(match("|", macro_token->s))
{
eat_current_token();
rhs = macro_bitwise_expr();
return lhs | rhs;
}
else if(match("||", macro_token->s))
{
eat_current_token();
rhs = macro_bitwise_expr();
return lhs || rhs;
}
else if(match("^", macro_token->s))
{
eat_current_token();
rhs = macro_bitwise_expr();
return lhs ^ rhs;
}
else
{
return lhs;
}
}
int macro_expression()
{
return macro_bitwise_expr();
}
void handle_define()
{
struct macro_list* hold;
struct token_list* arg;
struct token_list* expansion_end = NULL;
/* don't use #define statements from non-included blocks */
int conditional_define = TRUE;
if(NULL != conditional_inclusion_top)
{
if(FALSE == conditional_inclusion_top->include)
{
conditional_define = FALSE;
}
}
eat_current_token();
require(NULL != macro_token, "got an EOF terminated #define\n");
require('\n' != macro_token->s[0], "unexpected newline after #define\n");
/* insert new macro */
hold = calloc(1, sizeof(struct macro_list));
hold->symbol = macro_token->s;
hold->next = macro_env;
/* provided it isn't in a non-included block */
if(conditional_define) macro_env = hold;
/* discard the macro name */
eat_current_token_without_space();
/* Handle macro arguments */
if(macro_token->s[0] == '(')
{
/* discard ( */
eat_current_token();
require(NULL != macro_token, "got an EOF terminated #define\n");
if(macro_token->s[0] != ')')
{
arg = calloc(1, sizeof(struct token_list));
arg->s = macro_token->s;
hold->arguments = arg;
eat_current_token();
require(NULL != macro_token, "incomplete macro call\n");
while(macro_token->s[0] == ',')
{
eat_current_token();
require(NULL != macro_token, "incomplete macro call, got an EOF instead of an argument\n");
arg = calloc(1, sizeof(struct token_list));
arg->s = macro_token->s;
arg->next = hold->arguments;
hold->arguments = arg;
eat_current_token();
require(NULL != macro_token, "incomplete macro call\n");
}
}
eat_current_token();
/* Reverse argument list */
hold->arguments = reverse_list(hold->arguments);
require(NULL != macro_token, "got an EOF terminated #define\n");
}
else if(macro_token->s[0] == ' ')
{
eat_current_token();
}
while (TRUE)
{
require(NULL != macro_token, "got an EOF terminated #define\n");
if ('\n' == macro_token->s[0])
{
if(NULL == expansion_end)
{
hold->expansion = NULL;
expansion_end = macro_token;
return;
}
expansion_end->next = NULL;
return;
}
else if(('/' == macro_token->s[0]) && ('*' == macro_token->s[1]))
{
eat_current_token();
continue;
}
else if(('/' == macro_token->s[0]) && ('/' == macro_token->s[1]))
{
macro_token->s = "\n";
if(NULL == expansion_end)
{
hold->expansion = NULL;
expansion_end = macro_token;
return;
}
expansion_end->next = NULL;
return;
}
if(NULL == hold)
{
eat_current_token();
continue;
}
expansion_end = macro_token;
/* in the first iteration, we set the first token of the expansion, if
it exists */
if (NULL == hold->expansion)
{
hold->expansion = macro_token;
}
/* throw away if not used */
if(!conditional_define && (NULL != hold))
{
free(hold);
hold = NULL;
}
eat_current_token();
}
}
void handle_undef()
{
eat_current_token();
remove_macro(macro_token);
eat_current_token();
}
void handle_error(int warning_p)
{
/* don't use #error statements from non-included blocks */
int conditional_error = TRUE;
if(NULL != conditional_inclusion_top)
{
if(FALSE == conditional_inclusion_top->include)
{
conditional_error = FALSE;
}
}
eat_current_token();
/* provided it isn't in a non-included block */
if(conditional_error)
{
line_error_token(macro_token);
if(warning_p) fputs(" warning: #warning ", stderr);
else fputs(" error: #error ", stderr);
while (TRUE)
{
if ('\n' == macro_token->s[0]) break;
fputs(macro_token->s, stderr);
macro_token = macro_token->next;
}
fputs("\n", stderr);
if(!warning_p) exit(EXIT_FAILURE);
}
while (TRUE)
{
/* discard the error */
if ('\n' == macro_token->s[0])
{
return;
}
eat_current_token();
}
}
void macro_directive()
{
struct conditional_inclusion *t;
int result;
/* FIXME: whitespace is allowed between "#"" and "if" */
if(match("#if", macro_token->s))
{
eat_current_token();
/* evaluate constant integer expression */
result = macro_expression();
/* push conditional inclusion */
t = calloc(1, sizeof(struct conditional_inclusion));
t->prev = conditional_inclusion_top;
conditional_inclusion_top = t;
t->include = TRUE;
if(FALSE == result)
{
t->include = FALSE;
}
t->previous_condition_matched = t->include;
}
else if(match("#ifdef", macro_token->s))
{
eat_current_token();
require(NULL != macro_token, "got an EOF terminated macro defined expression\n");
if (NULL != lookup_macro(macro_token))
{
result = TRUE;
}
else
{
result = FALSE;
}
eat_current_token();
/* push conditional inclusion */
t = calloc(1, sizeof(struct conditional_inclusion));
t->prev = conditional_inclusion_top;
conditional_inclusion_top = t;
t->include = TRUE;
if(FALSE == result)
{
t->include = FALSE;
}
t->previous_condition_matched = t->include;
}
else if(match("#ifndef", macro_token->s))
{
eat_current_token();
require(NULL != macro_token, "got an EOF terminated macro defined expression\n");
if (NULL != lookup_macro(macro_token))
{
result = FALSE;
}
else
{
result = TRUE;
}
eat_current_token();
/* push conditional inclusion */
t = calloc(1, sizeof(struct conditional_inclusion));
t->prev = conditional_inclusion_top;
conditional_inclusion_top = t;
t->include = TRUE;
if(FALSE == result)
{
t->include = FALSE;
}
t->previous_condition_matched = t->include;
}
else if(match("#elif", macro_token->s))
{
eat_current_token();
result = macro_expression();
require(NULL != conditional_inclusion_top, "#elif without leading #if\n");
conditional_inclusion_top->include = result && !conditional_inclusion_top->previous_condition_matched;
conditional_inclusion_top->previous_condition_matched =
conditional_inclusion_top->previous_condition_matched || conditional_inclusion_top->include;
}
else if(match("#else", macro_token->s))
{
eat_current_token();
require(NULL != conditional_inclusion_top, "#else without leading #if\n");
conditional_inclusion_top->include = !conditional_inclusion_top->previous_condition_matched;
}
else if(match("#endif", macro_token->s))
{
if(NULL == conditional_inclusion_top)
{
line_error_token(macro_token);
fputs("unexpected #endif\n", stderr);
exit(EXIT_FAILURE);
}
eat_current_token();
/* pop conditional inclusion */
t = conditional_inclusion_top;
conditional_inclusion_top = conditional_inclusion_top->prev;
free(t);
}
else if(match("#define", macro_token->s))
{
handle_define();
}
else if(match("#undef", macro_token->s))
{
handle_undef();
}
else if(match("#error", macro_token->s))
{
handle_error(FALSE);
}
else if(match("#warning", macro_token->s))
{
handle_error(TRUE);
}
else if(match("#FILENAME", macro_token->s))
{
while(TRUE)
{
if(NULL == macro_token)
{
return;
}
if('\n' == macro_token->s[0])
{
return;
}
eat_current_token();
}
}
else
{
/* Put a big fat warning but see if we can just ignore */
fputs(">>WARNING<<\n>>WARNING<<\n", stderr);
line_error_token(macro_token);
fputs("feature: ", stderr);
fputs(macro_token->s, stderr);
fputs(" unsupported in M2-Planet\nIgnoring line, may result in bugs\n>>WARNING<<\n>>WARNING<<\n\n", stderr);
/* unhandled macro directive; let's eat until a newline; om nom nom */
while(TRUE)
{
if(NULL == macro_token)
{
return;
}
if('\n' == macro_token->s[0])
{
return;
}
eat_current_token();
}
}
}
struct token_list* expand_macro_functions(struct token_list* expansion, struct token_list* arguments)
{
struct token_list* expanded_token;
struct token_list* head;
struct token_list* hold; /* Same as head unless head == NULL */
head = copy_list(expansion);
while(NULL != head)
{
expanded_token = lookup_token(head, arguments);
hold = head;
if(NULL != expanded_token)
{
insert_tokens(head, expanded_token);
hold = head->prev;
head = eat_token(head);
}
else
{
head = head->next;
}
}
while(NULL != hold->prev) hold = hold->prev;
return hold;
}
void eat_until_endif()
{
/* This #if block is nested inside of an #if block that needs to be dropped, lose EVERYTHING */
do
{
if(match("#if", macro_token->s) || match("#ifdef", macro_token->s) || match("#ifndef", macro_token->s))
{
eat_current_token();
eat_until_endif();
}
eat_current_token();
require(NULL != macro_token, "Unterminated #if block\n");
} while(!match("#endif", macro_token->s));
}
void eat_block()
{
/* This conditional #if block is wrong, drop everything until the #elif/#else/#endif */
do
{
if(match("#if", macro_token->s) || match("#ifdef", macro_token->s) || match("#ifndef", macro_token->s))
{
eat_current_token();
eat_until_endif();
}
eat_current_token();
require(NULL != macro_token, "Unterminated #if block\n");
} while(!match("#elif", macro_token->s) && !match("#else", macro_token->s) && !match("#endif", macro_token->s));
}
struct token_list* maybe_expand(struct token_list* token)
{
if(NULL == token)
{
line_error_token(macro_token);
fputs("maybe_expand passed a null token\n", stderr);
exit(EXIT_FAILURE);
}
struct macro_list* hold = lookup_macro(token);
struct token_list* hold2;
struct token_list* hold3;
struct token_list* hold4;
if(NULL == token->next)
{
line_error_token(macro_token);
fputs("we can't expand a null token: ", stderr);
fputs(token->s, stderr);
fputc('\n', stderr);
exit(EXIT_FAILURE);
}
if (NULL == hold)
{
return token->next;
}
if(match("__M2__", token->s)) return token->next;
token = eat_token(token);
if (NULL == hold->expansion)
{
return token->next;
}
/* Match macro arguments with stored names */
hold3 = hold->arguments;
if(NULL != hold3)
{
if(token->s[0] == ' ')
{
token = eat_token(token);
}
require('(' == token->s[0], "missing open parenthesis for macro function\n");
token = eat_token(token);
require(NULL != token, "got an EOF terminated macro function\n");
do
{
hold2 = calloc(1, sizeof(struct token_list));
hold2->s = token->s;
hold2->next = hold->arguments->expansion;
hold->arguments->expansion = hold2;
token = eat_token(token);
require(NULL != token, "incomplete macro call\n");
if(token->s[0] == ',')
{
hold->arguments->expansion = reverse_list(hold->arguments->expansion);
hold->arguments = hold->arguments->next;
require(NULL != hold->arguments, "too many arguments in macro call\n");
token = eat_token(token);
require(NULL != token, "incomplete macro call\n");
}
} while(token->s[0] != ')');
hold->arguments->expansion = reverse_list(hold->arguments->expansion);
hold->arguments = hold3;
token = eat_token(token);
}
hold4 = expand_macro_functions(hold->expansion, hold->arguments);
hold4 = insert_tokens(token, hold4);
return hold4;
}
void preprocess()
{
int start_of_line = TRUE;
macro_token = global_token;
while(NULL != macro_token)
{
if(start_of_line && '#' == macro_token->s[0])
{
macro_directive();
if(macro_token)
{
if('\n' != macro_token->s[0])
{
line_error_token(macro_token);
fputs("newline expected at end of macro directive\n", stderr);
fputs("found: '", stderr);
fputs(macro_token->s, stderr);
fputs("'\n", stderr);
exit(EXIT_FAILURE);
}
}
}
else if('\n' == macro_token->s[0])
{
start_of_line = TRUE;
macro_token = macro_token->next;
}
else
{
start_of_line = FALSE;
if(NULL == conditional_inclusion_top)
{
macro_token = maybe_expand(macro_token);
}
else if(!conditional_inclusion_top->include)
{
/* rewrite the token stream to exclude the current token */
eat_block();
start_of_line = TRUE;
}
else
{
macro_token = maybe_expand(macro_token);
}
}
}
}
/* Copyright (C) 2016, 2021 Jeremiah Orians
* Copyright (C) 2020 deesix <deesix@tuta.io>
* Copyright (C) 2020 Gabriel Wicki
* This file is part of M2-Planet.
*
* M2-Planet is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* M2-Planet is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with M2-Planet. If not, see <http://www.gnu.org/licenses/>.
*/
#include"cc.h"
#include <unistd.h>
/* The core functions */
void populate_env(char** envp);
void setup_env();
char* env_lookup(char* variable);
void initialize_types();
struct token_list* read_all_tokens(FILE* a, struct token_list* current, char* filename, int include);
struct token_list* reverse_list(struct token_list* head);
void init_macro_env(char* sym, char* value, char* source, int num);
void preprocess();
void output_tokens(struct token_list *i, FILE* out);
int strtoint(char *a);
void spawn_processes(int debug_flag, char* prefix, char* preprocessed_file, char* destination, char** envp);
int follow_includes;
void prechecks(int argc, char** argv)
{
int env = 0;
char* hold;
int i = 1;
while(i <= argc)
{
if(NULL == argv[i])
{
i += 1;
}
else if(match(argv[i], "--debug-mode"))
{
hold = argv[i+1];
require(NULL != hold, "--debug-mode requires an argument\n");
DEBUG_LEVEL = strtoint(hold);
if(0 == DEBUG_LEVEL)
{
require(match("0", hold), "--debug-mode values must be numbers\n"
"and level 0 needed to be expressed as 0\n");
}
fputs("DEBUG_LEVEL set to: ", stderr);
fputs(hold, stderr);
fputc('\n', stderr);
i+= 2;
}
else if(match(argv[i], "-A") || match(argv[i], "--architecture"))
{
hold = argv[i+1];
require(NULL != hold, "--architecture needs to be passed an architecture\n");
Architecture = hold;
i += 2;
}
else if(match(argv[i], "--os") || match(argv[i], "--operating-system"))
{
hold = argv[i+1];
require(NULL != hold, "--operating-system needs to be passed an operating system\n");
OperatingSystem = hold;
i += 2;
}
else if(match(argv[i], "--max-string"))
{
hold = argv[i+1];
require(NULL != hold, "--max-string requires a numeric argument\n");
MAX_STRING = strtoint(hold);
require(0 < MAX_STRING, "Not a valid string size\nAbort and fix your --max-string\n");
i += 2;
}
else if(match(argv[i], "--no-includes"))
{
follow_includes = FALSE;
i+= 1;
}
else if(match(argv[i], "-I"))
{
hold = argv[i+1];
if(NULL == hold)
{
fputs("-I requires a PATH\n", stderr);
exit(EXIT_FAILURE);
}
if(1 <= DEBUG_LEVEL)
{
fputs("M2LIBC_PATH set by -I to ", stderr);
fputs(hold, stderr);
fputc('\n', stderr);
}
M2LIBC_PATH = hold;
i += 2;
}
else if(match(argv[i], "-D"))
{
hold = argv[i+1];
if(NULL == hold)
{
fputs("-D requires an argument", stderr);
exit(EXIT_FAILURE);
}
while(0 != hold[0])
{
if('=' == hold[0])
{
hold[0] = 0;
hold = hold + 1;
break;
}
hold = hold + 1;
}
init_macro_env(argv[i+1], hold, "__ARGV__", env);
env = env + 1;
i += 2;
}
else
{
i += 1;
}
}
}
int main(int argc, char** argv, char** envp)
{
/****************************************************************************
* Zero means no debugging messages and larger positive values means more *
* chatty output. Level 15 means EVERYTHING but 7 should cover most magic *
****************************************************************************/
DEBUG_LEVEL = 0;
/* Setup __M2__ (It is very very special *DO NOT MESS WITH IT* ) */
init_macro_env("__M2__", "__M2__", "__INTERNAL_M2__", 0);
/* Our fun globals */
FUZZING = FALSE;
MAX_STRING = 65536;
PREPROCESSOR_MODE = FALSE;
STDIO_USED = FALSE;
DIRTY_MODE = FALSE;
Architecture = NULL;
OperatingSystem = NULL;
/* Our fun locals */
int debug_flag = TRUE;
FILE* in = stdin;
FILE* tempfile;
char* destination_name = "a.out";
FILE* destination_file = stdout;
char* name;
int DUMP_MODE = FALSE;
follow_includes = TRUE;
/* Try to get our needed updates */
prechecks(argc, argv);
/* Get the environmental bits */
if(1 <= DEBUG_LEVEL) fputs("Starting to setup Environment\n", stderr);
populate_env(envp);
setup_env();
if(1 <= DEBUG_LEVEL) fputs("Environment setup\n", stderr);
M2LIBC_PATH = env_lookup("M2LIBC_PATH");
if(NULL == M2LIBC_PATH) M2LIBC_PATH = "./M2libc";
else if(1 <= DEBUG_LEVEL)
{
fputs("M2LIBC_PATH set by environment variable to ", stderr);
fputs(M2LIBC_PATH, stderr);
fputc('\n', stderr);
}
TEMPDIR = env_lookup("TMPDIR");
if(NULL == TEMPDIR) TEMPDIR = "/tmp";
else if(1 <= DEBUG_LEVEL)
{
fputs("TEMPDIR set by environment variable to ", stderr);
fputs(TEMPDIR, stderr);
fputc('\n', stderr);
}
int i = 1;
while(i <= argc)
{
if(NULL == argv[i])
{
i += 1;
}
else if(match(argv[i], "-E") || match(argv[i], "--preprocess-only"))
{
PREPROCESSOR_MODE = TRUE;
i += 1;
}
else if(match(argv[i], "--dump-mode"))
{
DUMP_MODE = TRUE;
i+= 1;
}
else if(match(argv[i], "--dirty-mode"))
{
DIRTY_MODE = TRUE;
i+= 1;
}
else if(match(argv[i], "--no-includes"))
{
/* Handled by precheck*/
i+= 1;
}
else if(match(argv[i], "--debug-mode"))
{
/* Handled by precheck */
i+= 2;
}
else if(match(argv[i], "-A") || match(argv[i], "--architecture"))
{
/* Handled by precheck */
i += 2;
}
else if(match(argv[i], "--os") || match(argv[i], "--operating-system"))
{
/* Handled by precheck */
i += 2;
}
else if(match(argv[i], "-f") || match(argv[i], "--file"))
{
if(NULL == hold_string)
{
hold_string = calloc(MAX_STRING + 4, sizeof(char));
require(NULL != hold_string, "Impossible Exhaustion has occured\n");
}
name = argv[i + 1];
if(NULL == name)
{
fputs("did not receive a file name\n", stderr);
exit(EXIT_FAILURE);
}
in = fopen(name, "r");
if(NULL == in)
{
fputs("Unable to open for reading file: ", stderr);
fputs(name, stderr);
fputs("\n Aborting to avoid problems\n", stderr);
exit(EXIT_FAILURE);
}
global_token = read_all_tokens(in, global_token, name, follow_includes);
fclose(in);
i += 2;
}
else if(match(argv[i], "-o") || match(argv[i], "--output"))
{
destination_name = argv[i + 1];
require(NULL != destination_name, "--output option requires a filename to follow\n");
destination_file = fopen(destination_name, "w");
if(NULL == destination_file)
{
fputs("Unable to open for writing file: ", stderr);
fputs(argv[i + 1], stderr);
fputs("\n Aborting to avoid problems\n", stderr);
exit(EXIT_FAILURE);
}
i += 2;
}
else if(match(argv[i], "--max-string"))
{
/* handled by precheck */
i += 2;
}
else if(match(argv[i], "-I"))
{
/* Handled by precheck */
i += 2;
}
else if(match(argv[i], "-D"))
{
/* Handled by precheck */
i += 2;
}
else if(match(argv[i], "-h") || match(argv[i], "--help"))
{
fputs(" -f input file\n -o output file\n --help for this message\n --version for file version\n-E or --preprocess-only\n--max-string N (N is a number)\n--fuzz\n--no-debug\n", stdout);
exit(EXIT_SUCCESS);
}
else if(match(argv[i], "-V") || match(argv[i], "--version"))
{
fputs("M2-Mesoplanet v1.10.0\n", stderr);
exit(EXIT_SUCCESS);
}
else if(match(argv[i], "--fuzz"))
{
/* Set fuzzing */
FUZZING = TRUE;
i += 1;
}
else if(match(argv[i], "--no-debug"))
{
/* strip things down */
debug_flag = FALSE;
i += 1;
}
else if(match(argv[i], "--temp-directory"))
{
name = argv[i+1];
if(NULL == name)
{
fputs("--temp-directory requires a PATH\n", stderr);
exit(EXIT_FAILURE);
}
if(1 <= DEBUG_LEVEL)
{
fputs("TEMPDIR set by --temp-directory to ", stderr);
fputs(name, stderr);
fputc('\n', stderr);
}
TEMPDIR = name;
i += 2;
}
else
{
if(5 <= DEBUG_LEVEL)
{
fputs("on index: ", stderr);
fputs(int2str(i, 10, TRUE), stderr);
fputc('\n', stderr);
}
fputs("UNKNOWN ARGUMENT: ", stdout);
fputs(argv[i], stdout);
fputc('\n', stdout);
exit(EXIT_FAILURE);
}
}
if(1 <= DEBUG_LEVEL) fputs("READ all files\n", stderr);
/* Deal with special case of wanting to read from standard input */
if(stdin == in)
{
hold_string = calloc(MAX_STRING, sizeof(char));
require(NULL != hold_string, "Impossible Exhaustion has occured\n");
global_token = read_all_tokens(in, global_token, "STDIN", follow_includes);
}
if(NULL == global_token)
{
fputs("Either no input files were given or they were empty\n", stderr);
exit(EXIT_FAILURE);
}
if(1 <= DEBUG_LEVEL) fputs("Start to reverse list\n", stderr);
global_token = reverse_list(global_token);
if(1 <= DEBUG_LEVEL) fputs("List reversed\n", stderr);
if(DUMP_MODE)
{
output_tokens(global_token, destination_file);
exit(EXIT_SUCCESS);
}
preprocess();
if(PREPROCESSOR_MODE)
{
fputs("/* M2-Mesoplanet Preprocessed source */\n", destination_file);
output_tokens(global_token, destination_file);
fclose(destination_file);
}
else
{
/* Ensure we can write to the temp directory */
int permissions = access(TEMPDIR, 0);
if(0 != permissions)
{
fputs("unable to access: ", stderr);
fputs(TEMPDIR, stderr);
fputs(" for use as a temp directory\nPlease use --temp-directory to set a directory you can use or set the TMPDIR variable\n", stderr);
exit(EXIT_FAILURE);
}
name = calloc(100, sizeof(char));
strcpy(name, TEMPDIR);
strcat(name, "/M2-Mesoplanet-XXXXXX");
i = mkstemp(name);
tempfile = fdopen(i, "w");
if(NULL != tempfile)
{
/* Our preprocessed crap */
output_tokens(global_token, tempfile);
fclose(tempfile);
/* Make me a real binary */
spawn_processes(debug_flag, TEMPDIR, name, destination_name, envp);
/* And clean up the donkey */
if(!DIRTY_MODE) remove(name);
}
else
{
fputs("unable to get a tempfile for M2-Mesoplanet output\n", stderr);
exit(EXIT_FAILURE);
}
}
return EXIT_SUCCESS;
}
/* -*- c-file-style: "linux";indent-tabs-mode:t -*- */
/* Copyright (C) 2017 Jeremiah Orians
* This file is part of mescc-tools.
*
* mescc-tools is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* mescc-tools is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with mescc-tools. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/utsname.h>
int match(char* a, char* b);
#define TRUE 1
//CONSTANT TRUE 1
#define FALSE 0
//CONSTANT FALSE 0
/* Standard C main program */
int main(int argc, char **argv)
{
int exact = FALSE;
int override = FALSE;
char* override_string;
int option_index = 1;
struct utsname* unameData = calloc(1, sizeof(struct utsname));
uname(unameData);
while(option_index <= argc)
{
if(NULL == argv[option_index])
{
option_index = option_index + 1;
}
else if(match(argv[option_index], "--exact"))
{
exact = TRUE;
option_index = option_index + 1;
}
else if(match(argv[option_index], "--override"))
{
override = TRUE;
if((option_index + 1) < argc)
{
override_string = argv[option_index + 1];
option_index = option_index + 2;
}
else
{
fputs("--override requires an actual override string\n", stderr);
exit(EXIT_FAILURE);
}
}
else if(match(argv[option_index], "--os") || match(argv[option_index], "--OS"))
{
if(override) fputs(override_string, stdout);
else fputs(unameData->sysname, stdout);
fputc('\n', stdout);
exit(EXIT_SUCCESS);
}
else if(match(argv[option_index], "--blood"))
{
if(override) fputs(override_string, stdout);
else if(match("aarch64", unameData->machine)
|| match("amd64", unameData->machine)
|| match("ppc64le", unameData->machine)
|| match("riscv64", unameData->machine)
|| match("x86_64", unameData->machine)) fputs("--64", stdout);
fputc('\n', stdout);
exit(EXIT_SUCCESS);
}
else if(match(argv[option_index], "--endian"))
{
if(override) fputs(override_string, stdout);
else if(match("aarch64", unameData->machine)
|| match("amd64", unameData->machine)
|| match("ppc64le", unameData->machine)
|| match("riscv64", unameData->machine)
|| match("x86_64", unameData->machine)
|| match("i386", unameData->machine)
|| match("i486", unameData->machine)
|| match("i586", unameData->machine)
|| match("i686", unameData->machine)
|| match("i686-pae", unameData->machine))fputs("--little-endian", stdout);
else fputs("--big-endian", stdout);
fputc('\n', stdout);
exit(EXIT_SUCCESS);
}
else if(match(argv[option_index], "--hex2"))
{
if(override) fputs(override_string, stdout);
else if(match("aarch64", unameData->machine)) fputs("0x400000", stdout);
else if(match("armv7l", unameData->machine)) fputs("0x10000", stdout);
else if(match("amd64", unameData->machine)
|| match("x86_64", unameData->machine)) fputs("0x600000", stdout);
else if(match("ppc64le", unameData->machine)) fputs("0x10000", stdout);
else if(match("riscv64", unameData->machine)) fputs("0x600000", stdout);
else if(match("i386", unameData->machine)
|| match("i486", unameData->machine)
|| match("i586", unameData->machine)
|| match("i686", unameData->machine)
|| match("i686-pae", unameData->machine)) fputs("0x08048000", stdout);
else fputs("0x0", stdout);
fputc('\n', stdout);
exit(EXIT_SUCCESS);
}
else if(match(argv[option_index], "-V") || match(argv[option_index], "--version"))
{
fputs("get_machine 1.4.0\n", stdout);
exit(EXIT_SUCCESS);
}
else if(match(argv[option_index], "-h") || match(argv[option_index], "--help"))
{
fputs("If you want exact architecture use --exact\n", stderr);
fputs("If you want to know the Operating system use --os\n", stderr);
fputs("If you wish to override the output to anything you want use --override\n", stderr);
exit(EXIT_SUCCESS);
}
else
{
fputs("Unknown option\n", stderr);
exit(EXIT_FAILURE);
}
}
if(override) fputs(override_string, stdout);
else if(!exact)
{
if(match("i386", unameData->machine) ||
match("i486", unameData->machine) ||
match("i586", unameData->machine) ||
match("i686", unameData->machine) ||
match("i686-pae", unameData->machine)) fputs("x86", stdout);
else if(match("x86_64", unameData->machine)) fputs("amd64", stdout);
else fputs(unameData->machine, stdout);
}
else fputs(unameData->machine, stdout);
fputs("\n", stdout);
return EXIT_SUCCESS;
}
/* Copyright (C) 2021 Bastian Bittorf <bb@npl.de>
* Copyright (C) 2021 Alain Mosnier <alain@wanamoon.net>
* Copyright (C) 2017-2021 Jan Venekamp
* Copyright (C) 2021 Jeremiah Orians
* This file is part of mescc-tools
*
* mescc-tools is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* mescc-tools is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with mescc-tools. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "M2libc/bootstrappable.h"
#define CHUNK_SIZE 64
#define TOTAL_LEN_LEN 8
int mask;
/*
* Initialize array of round constants:
* (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311):
*/
unsigned* init_k()
{
unsigned* k = calloc(65, sizeof(unsigned));
k[0] = 0x428a2f98;
k[1] = 0x71374491;
k[2] = 0xb5c0fbcf;
k[3] = 0xe9b5dba5;
k[4] = 0x3956c25b;
k[5] = 0x59f111f1;
k[6] = 0x923f82a4;
k[7] = 0xab1c5ed5;
k[8] = 0xd807aa98;
k[9] = 0x12835b01;
k[10] = 0x243185be;
k[11] = 0x550c7dc3;
k[12] = 0x72be5d74;
k[13] = 0x80deb1fe;
k[14] = 0x9bdc06a7;
k[15] = 0xc19bf174;
k[16] = 0xe49b69c1;
k[17] = 0xefbe4786;
k[18] = 0x0fc19dc6;
k[19] = 0x240ca1cc;
k[20] = 0x2de92c6f;
k[21] = 0x4a7484aa;
k[22] = 0x5cb0a9dc;
k[23] = 0x76f988da;
k[24] = 0x983e5152;
k[25] = 0xa831c66d;
k[26] = 0xb00327c8;
k[27] = 0xbf597fc7;
k[28] = 0xc6e00bf3;
k[29] = 0xd5a79147;
k[30] = 0x06ca6351;
k[31] = 0x14292967;
k[32] = 0x27b70a85;
k[33] = 0x2e1b2138;
k[34] = 0x4d2c6dfc;
k[35] = 0x53380d13;
k[36] = 0x650a7354;
k[37] = 0x766a0abb;
k[38] = 0x81c2c92e;
k[39] = 0x92722c85;
k[40] = 0xa2bfe8a1;
k[41] = 0xa81a664b;
k[42] = 0xc24b8b70;
k[43] = 0xc76c51a3;
k[44] = 0xd192e819;
k[45] = 0xd6990624;
k[46] = 0xf40e3585;
k[47] = 0x106aa070;
k[48] = 0x19a4c116;
k[49] = 0x1e376c08;
k[50] = 0x2748774c;
k[51] = 0x34b0bcb5;
k[52] = 0x391c0cb3;
k[53] = 0x4ed8aa4a;
k[54] = 0x5b9cca4f;
k[55] = 0x682e6ff3;
k[56] = 0x748f82ee;
k[57] = 0x78a5636f;
k[58] = 0x84c87814;
k[59] = 0x8cc70208;
k[60] = 0x90befffa;
k[61] = 0xa4506ceb;
k[62] = 0xbef9a3f7;
k[63] = 0xc67178f2;
return k;
}
unsigned* init_h()
{
unsigned* h = calloc(9, sizeof(unsigned));
h[0] = 0x6a09e667;
h[1] = 0xbb67ae85;
h[2] = 0x3c6ef372;
h[3] = 0xa54ff53a;
h[4] = 0x510e527f;
h[5] = 0x9b05688c;
h[6] = 0x1f83d9ab;
h[7] = 0x5be0cd19;
return h;
}
struct buffer_state
{
char* p;
size_t len;
size_t total_len;
int single_one_delivered; /* bool */
int total_len_delivered; /* bool */
};
unsigned right_rot(unsigned value, unsigned count)
{
/*
* Defined behaviour in standard C for all count where 0 < count < 32,
* which is what we need here.
*/
value &= mask;
int hold1 = (value >> count) & mask;
int hold2 = (value << (32 - count)) & mask;
int hold = (hold1 | hold2) & mask;
return hold;
}
void init_buf_state(struct buffer_state * state, char* input, size_t len)
{
state->p = input;
state->len = len;
state->total_len = len;
state->single_one_delivered = 0;
state->total_len_delivered = 0;
}
/* Return value: bool */
int calc_chunk(char* chunk, struct buffer_state * state)
{
size_t space_in_chunk;
if(state->total_len_delivered)
{
return 0;
}
if(state->len >= CHUNK_SIZE)
{
memcpy(chunk, state->p, CHUNK_SIZE);
state->p += CHUNK_SIZE;
state->len -= CHUNK_SIZE;
return 1;
}
memcpy(chunk, state->p, state->len);
chunk += state->len;
space_in_chunk = CHUNK_SIZE - state->len;
state->p += state->len;
state->len = 0;
/* If we are here, space_in_chunk is one at minimum. */
if(!state->single_one_delivered)
{
chunk[0] = 0x80;
chunk += 1;
space_in_chunk -= 1;
state->single_one_delivered = 1;
}
/*
* Now:
* - either there is enough space left for the total length, and we can conclude,
* - or there is too little space left, and we have to pad the rest of this chunk with zeroes.
* In the latter case, we will conclude at the next invocation of this function.
*/
if(space_in_chunk >= TOTAL_LEN_LEN)
{
size_t left = space_in_chunk - TOTAL_LEN_LEN;
size_t len = state->total_len;
int i;
memset(chunk, 0x00, left);
chunk += left;
/* Storing of len * 8 as a big endian 64-bit without overflow. */
chunk[7] = (len << 3);
len >>= 5;
for(i = 6; i >= 0; i -= 1)
{
chunk[i] = len;
len >>= 8;
}
state->total_len_delivered = 1;
}
else
{
memset(chunk, 0x00, space_in_chunk);
}
return 1;
}
/*
* Limitations:
* - Since input is a pointer in RAM, the data to hash should be in RAM, which could be a problem
* for large data sizes.
* - SHA algorithms theoretically operate on bit strings. However, this implementation has no support
* for bit string lengths that are not multiples of eight, and it really operates on arrays of bytes.
* In particular, the len parameter is a number of bytes.
*/
void calc_sha_256(char* hash, char* input, size_t len)
{
/*
* Note 1: All integers (expect indexes) are 32-bit unsigned integers and addition is calculated modulo 2^32.
* Note 2: For each round, there is one round constant k[i] and one entry in the message schedule array w[i], 0 = i = 63
* Note 3: The compression function uses 8 working variables, a through h
* Note 4: Big-endian convention is used when expressing the constants in this pseudocode,
* and when parsing message block data from bytes to words, for example,
* the first word of the input message "abc" after padding is 0x61626380
*/
/*
* Initialize hash values:
* (first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19):
*/
unsigned* k = init_k();
unsigned* h = init_h();
unsigned i;
unsigned j;
unsigned hold1;
unsigned hold2;
/* 512-bit chunks is what we will operate on. */
char* chunk = calloc(65, sizeof(char));
struct buffer_state* state = calloc(1, sizeof(struct buffer_state));
init_buf_state(state, input, len);
unsigned* ah = calloc(9, sizeof(unsigned));
char *p;
unsigned* w = calloc(17, sizeof(unsigned));
unsigned s0;
unsigned s1;
unsigned ch;
unsigned temp1;
unsigned temp2;
unsigned maj;
while(calc_chunk(chunk, state))
{
p = chunk;
/* Initialize working variables to current hash value: */
for(i = 0; i < 8; i += 1)
{
ah[i] = h[i];
}
/* Compression function main loop: */
for(i = 0; i < 4; i += 1)
{
/*
* The w-array is really w[64], but since we only need
* 16 of them at a time, we save stack by calculating
* 16 at a time.
*
* This optimization was not there initially and the
* rest of the comments about w[64] are kept in their
* initial state.
*/
/*
* create a 64-entry message schedule array w[0..63] of 32-bit words
* (The initial values in w[0..63] don't matter, so many implementations zero them here)
* copy chunk into first 16 words w[0..15] of the message schedule array
*/
for(j = 0; j < 16; j += 1)
{
if(i == 0)
{
w[j] = ((p[0] & 0xFF) << 24) | ((p[1] & 0xFF) << 16) | ((p[2] & 0xFF) << 8) | (p[3] & 0xFF);
p += 4;
}
else
{
/* Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array: */
hold1 = (j + 1) & 0xf;
hold2 = w[hold1];
s0 = right_rot(hold2, 7) ^ right_rot(hold2, 18) ^ ((hold2 & mask) >> 3);
hold1 = (j + 14) & 0xf;
hold2 = w[hold1];
s1 = right_rot(hold2, 17) ^ right_rot(hold2, 19) ^ ((hold2 & mask) >> 10);
w[j] += s0 + w[(j + 9) & 0xf] + s1;
}
s1 = right_rot(ah[4], 6) ^ right_rot(ah[4], 11) ^ right_rot(ah[4], 25);
ch = (ah[4] & ah[5]) ^ (~ah[4] & ah[6]);
temp1 = ah[7] + s1 + ch + k[i << 4 | j] + w[j];
s0 = right_rot(ah[0], 2) ^ right_rot(ah[0], 13) ^ right_rot(ah[0], 22);
maj = (ah[0] & ah[1]) ^ (ah[0] & ah[2]) ^ (ah[1] & ah[2]);
temp2 = s0 + maj;
ah[7] = ah[6];
ah[6] = ah[5];
ah[5] = ah[4];
ah[4] = ah[3] + temp1;
ah[3] = ah[2];
ah[2] = ah[1];
ah[1] = ah[0];
ah[0] = temp1 + temp2;
}
}
/* Add the compressed chunk to the current hash value: */
for(i = 0; i < 8; i += 1)
{
h[i] += ah[i];
}
}
/* Produce the final hash value (big-endian): */
i = 0;
for(j = 0; i < 8; i += 1)
{
hash[j] = ((h[i] >> 24) & 0xFF);
j += 1;
hash[j] = ((h[i] >> 16) & 0xFF);
j += 1;
hash[j] = ((h[i] >> 8) & 0xFF);
j += 1;
hash[j] = (h[i] & 0xFF);
j += 1;
}
}
struct list
{
int found;
char* name;
FILE* f;
size_t size;
char* buffer;
char* hash;
struct list* next;
};
void bad_checkfile(char* filename)
{
fputs(filename, stdout);
puts(": no properly formatted SHA256 checksum lines found");
}
int hex2int(char c, char* filename)
{
if((c >= '0') && (c <= '9')) return (c - 48);
else if((c >= 'a') && (c <= 'f')) return (c - 87);
else if ((c >= 'F') && (c <= 'F')) return (c - 55);
bad_checkfile(filename);
exit(EXIT_FAILURE);
}
char* hash_to_string(char* a)
{
char* table = "0123456789abcdef";
char* r = calloc(66, sizeof(char));
int i;
int j = 0;
int c;
for(i = 0; i < 32; i += 1)
{
c = a[i] & 0xFF;
r[j] = table[(c >> 4)];
j += 1;
r[j] = table[(c & 0xF)];
j += 1;
}
return r;
}
int check_file(char* b, char* filename)
{
int r = TRUE;
size_t i;
int hold1;
int hold2;
FILE* f;
char* name = calloc(4097, sizeof(char));
char* hash = calloc(33, sizeof(char));
char* hash2 = calloc(33, sizeof(char));
size_t size;
char* buffer;
go_again:
for(i = 0; i < 32; i += 1)
{
hold1 = hex2int(b[0], filename);
hold2 = hex2int(b[1], filename);
hash[i] = (hold1 << 4) + hold2;
b += 2;
}
if((' ' != b[0]) || (' ' != b[1]))
{
bad_checkfile(filename);
exit(EXIT_FAILURE);
}
b += 2;
for(i = 0; i < 4096; i += 1)
{
if('\n' == b[0])
{
name[i] = 0;
b += 1;
break;
}
name[i] = b[0];
b += 1;
}
f = fopen(name, "r");
if(NULL == f)
{
fputs(name, stdout);
puts(": No such file or directory");
exit(EXIT_FAILURE);
}
else
{
fseek(f, 0, SEEK_END);
size = ftell(f);
rewind(f);
buffer = calloc(size + 1, sizeof(char));
fread(buffer, sizeof(char), size, f);
calc_sha_256(hash2, buffer, size);
if(match(hash_to_string(hash), hash_to_string(hash2)))
{
fputs(name, stdout);
puts(": OK");
}
else
{
fputs(name, stdout);
fputs(": FAILED\nWanted: ", stdout);
fputs(hash_to_string(hash), stdout);
fputs("\nReceived: ", stdout);
puts(hash_to_string(hash2));
r = FALSE;
}
}
if(0 == b[0]) return r;
goto go_again;
}
/* reverse the linked list */
void reverse(struct list** head)
{
struct list* prev = NULL;
struct list* current = *head;
struct list* next = NULL;
while (current != NULL)
{
next = current->next;
current->next = prev;
prev = current;
current = next;
}
*head = prev;
}
int main(int argc, char **argv)
{
struct list* l = NULL;
struct list* t = NULL;
size_t read;
int check = FALSE;
int r = TRUE;
char* output_file = "";
FILE* output = stdout;
mask = (0x7FFFFFFF << 1) | 0x1;
int i = 1;
while(i <= argc)
{
if(NULL == argv[i])
{
i += 1;
}
else if(match(argv[i], "-c") || match(argv[i], "--check"))
{
check = TRUE;
i += 1;
}
else if (match(argv[i], "-o") || match(argv[i], "--output"))
{
output_file = argv[i + 1];
i += 2;
if (output != stdout) {
fclose(output);
}
output = fopen(output_file, "w");
require(output != NULL, "Output file cannot be opened!\n");
}
else if(match(argv[i], "-h") || match(argv[i], "--help"))
{
puts("Usage: sha256sum <file> [--check]");
exit(EXIT_SUCCESS);
}
else
{
t = calloc(1, sizeof(struct list));
t->hash = calloc(33, sizeof(char));
t->name = argv[i];
t->f = fopen(t->name, "r");
if(NULL != t->f)
{
t->found = TRUE;
fseek(t->f, 0, SEEK_END);
t->size = ftell(t->f);
rewind(t->f);
t->buffer = calloc(t->size + 1, sizeof(char));
read = fread(t->buffer, sizeof(char), t->size, t->f);
}
t->next = l;
l = t;
i += 1;
}
}
reverse(&l);
if(check)
{
while(NULL != l)
{
if(l->found)
{
if(!check_file(l->buffer, l->name)) r = FALSE;
}
else
{
fputs(l->name, stdout);
puts(": No such file or directory");
exit(EXIT_FAILURE);
}
l = l->next;
}
}
else
{
while(NULL != l)
{
if(l->found)
{
calc_sha_256(l->hash, l->buffer, l->size);
fputs(hash_to_string(l->hash), output);
fputs(" ", output);
fputs(l->name, output);
fputc('\n', output);
}
else
{
fputs(l->name, output);
fputs(": No such file or directory\n", output);
exit(EXIT_FAILURE);
}
l = l->next;
}
}
if (output != stdout) {
fclose(output);
}
if(r) return 0;
else return 1;
}
/* Copyright (C) 2016 Jeremiah Orians * This file is part of M2-Planet. * * M2-Planet is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * M2-Planet is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with M2-Planet. If not, see <http://www.gnu.org/licenses/>. */ #ifndef _STRING_H #define _STRING_H #ifdef __M2__ #include <string.c> #else #include <stddef.h> /* String manipulation */ char* strcpy(char* dest, char const* src); char* strncpy(char* dest, char const* src, size_t count); char* strcat(char* dest, char const* src); char* strncat(char* dest, char const* src, size_t count); /* String examination */ size_t strlen(char const* str ); size_t strnlen_s(char const* str, size_t strsz ); int strcmp(char const* lhs, char const* rhs ); int strncmp(char const* lhs, char const* rhs, size_t count); char* strchr(char const* str, int ch); char* strrchr(char const* str, int ch); size_t strspn(char const* dest, char const* src); size_t strcspn(char const* dest, char const* src); char* strpbrk(char const* dest, char const* breakset); /* Memory manipulation */ void* memset(void* dest, int ch, size_t count); void* memcpy(void* dest, void const* src, size_t count); void* memmove(void* dest, void const* src, size_t count); int memcmp(void const* lhs, void const* rhs, size_t count); void* memchr(void const* ptr, int ch, size_t count); #endif #endif
/* Copyright (C) 2020 Jeremiah Orians * This file is part of M2-Planet. * * M2-Planet is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * M2-Planet is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with M2-Planet. If not, see <http://www.gnu.org/licenses/>. */ #ifndef _SYS_STAT_H #define _SYS_STAT_H #ifdef __M2__ #if __uefi__ #include <uefi/sys/stat.c> #elif __i386__ #include <x86/linux/sys/stat.c> #elif __x86_64__ #include <amd64/linux/sys/stat.c> #elif __arm__ #include <armv7l/linux/sys/stat.c> #elif __aarch64__ #include <aarch64/linux/sys/stat.c> #elif __riscv && __riscv_xlen==32 #include <riscv32/linux/sys/stat.c> #elif __riscv && __riscv_xlen==64 #include <riscv64/linux/sys/stat.c> #else #error arch not supported #endif #else #include <sys/types.h> #define S_IRWXU 00700 #define S_IXUSR 00100 #define S_IWUSR 00200 #define S_IRUSR 00400 #define S_ISUID 04000 #define S_ISGID 02000 #define S_IXGRP 00010 #define S_IXOTH 00001 #define S_IRGRP 00040 #define S_IROTH 00004 #define S_IWGRP 00020 #define S_IWOTH 00002 #define S_IRWXG 00070 #define S_IRWXO 00007 int chmod(char *pathname, int mode); int fchmod(int a, mode_t b); int mkdir(char const* a, mode_t b); int mknod(char const* a, mode_t b, dev_t c); mode_t umask(mode_t m); #endif #endif
/* Copyright (C) 2016 Jeremiah Orians * This file is part of M2-Planet. * * M2-Planet is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * M2-Planet is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with M2-Planet. If not, see <http://www.gnu.org/licenses/>. */ #ifndef _FCNTL_H #define _FCNTL_H #ifdef __M2__ #include <fcntl.c> #else #define O_RDONLY 0 #define O_WRONLY 1 #define O_RDWR 2 #define O_CREAT 00100 #define O_EXCL 00200 #define O_TRUNC 001000 #define O_APPEND 002000 #define S_IXUSR 00100 #define S_IWUSR 00200 #define S_IRUSR 00400 #define S_IRWXU 00700 extern int open(char* name, int flag, int mode); #define STDIN_FILENO 0 #define STDOUT_FILENO 1 #define STDERR_FILENO 2 #endif #endif
/* Copyright (C) 2020 Jeremiah Orians
* This file is part of M2-Planet.
*
* M2-Planet is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* M2-Planet is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with M2-Planet. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _UNISTD_H
#define _UNISTD_H
#ifdef __M2__
#if __uefi__
#include <uefi/unistd.c>
#elif __i386__
#include <x86/linux/unistd.c>
#elif __x86_64__
#include <amd64/linux/unistd.c>
#elif __arm__
#include <armv7l/linux/unistd.c>
#elif __aarch64__
#include <aarch64/linux/unistd.c>
#elif __riscv && __riscv_xlen==32
#include <riscv32/linux/unistd.c>
#elif __riscv && __riscv_xlen==64
#include <riscv64/linux/unistd.c>
#else
#error arch not supported
#endif
#else
#define NULL 0
#define __PATH_MAX 4096
void* malloc(unsigned size);
int access(char* pathname, int mode);
int chdir(char* path);
int fchdir(int fd);
void _exit(int value);
int fork();
int waitpid (int pid, int* status_ptr, int options);
int execve(char* file_name, char** argv, char** envp);
int read(int fd, char* buf, unsigned count);
int write(int fd, char* buf, unsigned count);
int lseek(int fd, int offset, int whence);
int close(int fd);
int unlink (char *filename);
int _getcwd(char* buf, int size);
char* getcwd(char* buf, unsigned size);
char* getwd(char* buf);
char* get_current_dir_name();
int brk(void *addr);
struct utsname
{
char sysname[65]; /* Operating system name (e.g., "Linux") */
char nodename[65]; /* Name within "some implementation-defined network" */
char release[65]; /* Operating system release (e.g., "2.6.28") */
char version[65]; /* Operating system version */
char machine[65]; /* Hardware identifier */
};
int uname(struct utsname* unameData);
#endif
#endif
/* Copyright (C) 2016 Jeremiah Orians * This file is part of M2-Planet. * * M2-Planet is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * M2-Planet is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with M2-Planet. If not, see <http://www.gnu.org/licenses/>. */ #ifndef _STDLIB_H #define _STDLIB_H #include <unistd.h> #ifdef __M2__ #include <stdlib.c> #else #define EXIT_FAILURE 1 #define EXIT_SUCCESS 0 extern void exit(int value); extern long _malloc_ptr; extern long _brk_ptr; extern void free(void* l); extern void* malloc(unsigned size); extern void* memset(void* ptr, int value, int num); extern void* calloc(int count, int size); extern char *getenv(const char *name); size_t wcstombs(char* dest, const wchar_t* src, size_t n); #endif #endif
/* Copyright (C) 2016 Jeremiah Orians * This file is part of M2-Planet. * * M2-Planet is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * M2-Planet is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with M2-Planet. If not, see <http://www.gnu.org/licenses/>. */ #ifndef _BOOTSTRAPPABLE_H #define _BOOTSTRAPPABLE_H /* Essential common CONSTANTS*/ #define TRUE 1 #define FALSE 0 #ifdef __M2__ #include <bootstrappable.c> #else /* Universally useful functions */ void require(int bool, char* error); int match(char* a, char* b); int in_set(int c, char* s); int strtoint(char *a); char* int2str(int x, int base, int signed_p); #endif #endif
/* Copyright (C) 2021 Andrius Å tikonas
* This file is part of mescc-tools-extra
*
* mescc-tools-extra is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* mescc-tools-extra is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with mescc-tools-extra. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* "match" can be used to compare strings. It is useful to write conditional
* code in kaem.
*
* Usage: match string1 string2
* Returns: 0 if strings match
*/
#include <stdio.h>
#include <string.h>
#include "M2libc/bootstrappable.h"
int main(int argc, char **argv)
{
if(argc != 3)
{
fputs("match needs exactly 2 arguments.\n", stderr);
return 2;
}
return !match(argv[1], argv[2]);
}
/* Copyright (C) 2009 Tim Kientzle
* Copyright (C) 2021 Jeremiah Orians
* Copyright (C) 2021 Andrius Å tikonas
* This file is part of mescc-tools-extra
*
* mescc-tools-extra is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* mescc-tools-extra is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with mescc-tools-extra. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* "mkdir" can be used to create empty directories. It can also create
* required parent directories.
*
* Usage: mkdir <dir1>/<dir2> <dir3>
*
* These are all highly standard and portable headers. */
#include <stdio.h>
#include <string.h>
/* This is for mkdir(); this may need to be changed for some platforms. */
#include <sys/stat.h> /* For mkdir() */
#include <stdlib.h>
#include "M2libc/bootstrappable.h"
int parents;
/* Create a directory, including parent directories as necessary. */
void create_dir(char *pathname, int mode)
{
char *p;
int r;
/* Strip trailing '/' */
if(pathname[strlen(pathname) - 1] == '/')
{
pathname[strlen(pathname) - 1] = '\0';
}
/* Try creating the directory. */
r = mkdir(pathname, mode);
if((r != 0) && parents)
{
/* On failure, try creating parent directory. */
p = strrchr(pathname, '/');
if(p != NULL)
{
p[0] = '\0';
create_dir(pathname, 0755);
p[0] = '/';
r = mkdir(pathname, mode);
}
}
if((r != 0) && !parents)
{
fputs("Could not create directory ", stderr);
fputs(pathname, stderr);
fputc('\n', stderr);
exit(EXIT_FAILURE);
}
}
int main(int argc, char **argv)
{
/* This adds some quasi-compatibility with GNU coreutils' mkdir. */
parents = FALSE;
int i;
for(i = 1; argc > i; i = i + 1)
{
if(match(argv[i], "-p")) parents = TRUE;
else create_dir(argv[i], 0755);
}
return 0;
}
/* Copyright (C) 2009 Tim Kientzle
* Copyright (C) 2021 Jeremiah Orians
* This file is part of mescc-tools-extra
*
* mescc-tools-extra is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* mescc-tools-extra is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with mescc-tools-extra. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* "untar" is an extremely simple tar extractor:
* * A single C source file, so it should be easy to compile
* and run on any system with a C compiler.
* * Extremely portable standard C. The only non-ANSI function
* used is mkdir().
* * Reads basic ustar tar archives.
* * Does not require libarchive or any other special library.
*
* To compile: cc -o untar untar.c
*
* Usage: untar <archive>
*
* In particular, this program should be sufficient to extract the
* distribution for libarchive, allowing people to bootstrap
* libarchive on systems that do not already have a tar program.
*
* To unpack libarchive-x.y.z.tar.gz:
* * gunzip libarchive-x.y.z.tar.gz
* * untar libarchive-x.y.z.tar
*
* Written by Tim Kientzle, March 2009.
*
* Released into the public domain.
*/
/* These are all highly standard and portable headers. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* This is for mkdir(); this may need to be changed for some platforms. */
#include <sys/stat.h> /* For mkdir() */
#include "M2libc/bootstrappable.h"
int FUZZING;
int VERBOSE;
int STRICT;
/* Parse an octal number, ignoring leading and trailing nonsense. */
int parseoct(char const* p, size_t n)
{
int i = 0;
int h;
while(((p[0] < '0') || (p[0] > '7')) && (n > 0))
{
p = p + 1;
n = n - 1;
}
while((p[0] >= '0') && (p[0] <= '7') && (n > 0))
{
i = i << 3;
h = p[0];
i = i + h - 48;
p = p + 1;
n = n - 1;
}
return i;
}
/* Returns true if this is 512 zero bytes. */
int is_end_of_archive(char const* p)
{
int n;
for(n = 511; n >= 0; n = n - 1)
{
if(p[n] != 0)
{
return FALSE;
}
}
return TRUE;
}
/* Create a directory, including parent directories as necessary. */
void create_dir(char *pathname, int mode)
{
char *p;
int r;
/* Strip trailing '/' */
if(pathname[strlen(pathname) - 1] == '/')
{
pathname[strlen(pathname) - 1] = '\0';
}
/* Try creating the directory. */
if(!FUZZING)
{
r = mkdir(pathname, mode);
if(r != 0)
{
/* On failure, try creating parent directory. */
p = strrchr(pathname, '/');
if(p != NULL)
{
p[0] = '\0';
create_dir(pathname, 0755);
p[0] = '/';
r = mkdir(pathname, mode);
}
}
if(r != 0)
{
fputs("Could not create directory ", stderr);
fputs(pathname, stderr);
fputc('\n', stderr);
}
}
}
/* Create a file, including parent directory as necessary. */
FILE* create_file(char *pathname)
{
if(FUZZING) return NULL;
FILE* f;
f = fopen(pathname, "w");
if(f == NULL)
{
/* Try creating parent dir and then creating file. */
char *p = strrchr(pathname, '/');
if(p != NULL)
{
p[0] = '\0';
create_dir(pathname, 0755);
p[0] = '/';
f = fopen(pathname, "w");
}
}
return f;
}
/* Verify the tar checksum. */
int verify_checksum(char const* p)
{
int n;
int u = 0;
unsigned h;
for(n = 0; n < 512; n = n + 1)
{
/* Standard tar checksum adds unsigned bytes. */
if((n < 148) || (n > 155))
{
h = p[n];
u = u + h;
}
else
{
u = u + 0x20;
}
}
int r = parseoct(p + 148, 8);
return (u == r);
}
/* Extract a tar archive. */
int untar(FILE *a, char const* path)
{
char* buff = calloc(514, sizeof(char));
FILE* f = NULL;
size_t bytes_read;
size_t bytes_written;
int filesize;
int op;
if(VERBOSE)
{
fputs("Extracting from ", stdout);
puts(path);
}
while(TRUE)
{
memset(buff, 0, 514);
bytes_read = fread(buff, sizeof(char), 512, a);
if(bytes_read < 512)
{
fputs("Short read on ", stderr);
fputs(path, stderr);
fputs(": expected 512, got ", stderr);
fputs(int2str(bytes_read, 10, TRUE), stderr);
fputc('\n', stderr);
return FALSE;
}
if(is_end_of_archive(buff))
{
if(VERBOSE)
{
fputs("End of ", stdout);
puts(path);
}
return TRUE;
}
if(!verify_checksum(buff))
{
fputs("Checksum failure\n", stderr);
return FALSE;
}
filesize = parseoct(buff + 124, 12);
op = buff[156];
if('1' == op)
{
if(STRICT)
{
fputs("unable to create hardlinks\n", stderr);
exit(EXIT_FAILURE);
}
fputs(" Ignoring hardlink ", stdout);
puts(buff);
}
else if('2' == op)
{
if(STRICT)
{
fputs("unable to create symlinks\n", stderr);
exit(EXIT_FAILURE);
}
fputs(" Ignoring symlink ", stdout);
puts(buff);
}
else if('3' == op)
{
if(STRICT)
{
fputs("unable to create character devices\n", stderr);
exit(EXIT_FAILURE);
}
fputs(" Ignoring character device ", stdout);
puts(buff);
}
else if('4' == op)
{
if(STRICT)
{
fputs("unable to create block devices\n", stderr);
exit(EXIT_FAILURE);
}
fputs(" Ignoring block device ", stdout);
puts(buff);
}
else if('5' == op)
{
if(VERBOSE)
{
fputs(" Extracting dir ", stdout);
puts(buff);
}
create_dir(buff, parseoct(buff + 100, 8));
filesize = 0;
}
else if('6' == op)
{
if(STRICT)
{
fputs("unable to create FIFO\n", stderr);
exit(EXIT_FAILURE);
}
fputs(" Ignoring FIFO ", stdout);
puts(buff);
}
else
{
if(VERBOSE)
{
fputs(" Extracting file ", stdout);
puts(buff);
}
f = create_file(buff);
}
while(filesize > 0)
{
bytes_read = fread(buff, 1, 512, a);
if(bytes_read < 512)
{
fputs("Short read on ", stderr);
fputs(path, stderr);
fputs(": Expected 512, got ", stderr);
puts(int2str(bytes_read, 10, TRUE));
return FALSE;
}
if(filesize < 512)
{
bytes_read = filesize;
}
if(f != NULL)
{
if(!FUZZING)
{
bytes_written = fwrite(buff, 1, bytes_read, f);
if(bytes_written != bytes_read)
{
fputs("Failed write\n", stderr);
fclose(f);
f = NULL;
}
}
}
filesize = filesize - bytes_read;
}
if(f != NULL)
{
fclose(f);
f = NULL;
}
}
return TRUE;
}
struct files_queue
{
char* name;
FILE* f;
struct files_queue* next;
};
int main(int argc, char **argv)
{
struct files_queue* list = NULL;
struct files_queue* a;
STRICT = TRUE;
FUZZING = FALSE;
int r;
int i = 1;
while (i < argc)
{
if(NULL == argv[i])
{
i = i + 1;
}
else if(match(argv[i], "-f") || match(argv[i], "--file"))
{
a = calloc(1, sizeof(struct files_queue));
require(NULL != a, "failed to allocate enough memory to even get the file name\n");
a->next = list;
a->name = argv[i+1];
require(NULL != a->name, "the --file option requires a filename to be given\n");
a->f = fopen(a->name, "r");
if(a->f == NULL)
{
fputs("Unable to open ", stderr);
fputs(a->name, stderr);
fputc('\n', stderr);
if(STRICT) exit(EXIT_FAILURE);
}
list = a;
i = i + 2;
}
else if(match(argv[i], "--chaos") || match(argv[i], "--fuzz-mode") || match(argv[i], "--fuzzing"))
{
FUZZING = TRUE;
fputs("fuzz-mode enabled, preparing for chaos\n", stderr);
i = i + 1;
}
else if(match(argv[i], "-v") || match(argv[i], "--verbose"))
{
VERBOSE = TRUE;
i = i + 1;
}
else if(match(argv[i], "--non-strict") || match(argv[i], "--bad-decisions-mode") || match(argv[i], "--drunk-mode"))
{
STRICT = FALSE;
fputs("non-strict mode enabled, preparing for chaos\n", stderr);
i = i + 1;
}
else if(match(argv[i], "-h") || match(argv[i], "--help"))
{
fputs("Usage: ", stderr);
fputs(argv[0], stderr);
fputs(" --file $input.gz\n", stderr);
fputs("--verbose to print list of extracted files\n", stderr);
fputs("--help to get this message\n", stderr);
fputs("--fuzz-mode if you wish to fuzz this application safely\n", stderr);
fputs("--non-strict if you wish to just ignore files not existing\n", stderr);
exit(EXIT_SUCCESS);
}
else
{
fputs("Unknown option:", stderr);
fputs(argv[i], stderr);
fputs("\nAborting to avoid problems\n", stderr);
exit(EXIT_FAILURE);
}
}
/* Process the queue one file at a time */
while(NULL != list)
{
r = untar(list->f, list->name);
fputs("The extraction of ", stderr);
fputs(list->name, stderr);
if(r) fputs(" was successful\n", stderr);
else fputs(" produced errors\n", stderr);
fclose(list->f);
list = list->next;
}
return 0;
}
/* Copyright (C) 2002-2013 Mark Adler, all rights reserved
* Copyright (C) 2021 Jeremiah Orians
* This file is part of mescc-tools-extra
*
* mescc-tools-extra is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* mescc-tools-extra is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with mescc-tools-extra. If not, see <http://www.gnu.org/licenses/>.
*/
/* puff.c
* Copyright (C) 2002-2013 Mark Adler, all rights reserved
* version 2.3, 21 Jan 2013
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the author be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
* Mark Adler madler@alumni.caltech.edu
*/
/* ungz.c is a gz file decompression utility that leverages puff.c to provide
* the deflate algorithm with multiple modifications to enable being built by
* M2-Planet with M2libc.
*
*
* puff.c is a simple inflate written to be an unambiguous way to specify the
* deflate format. It is not written for speed but rather simplicity. As a
* side benefit, this code might actually be useful when small code is more
* important than speed, such as bootstrap applications. For typical deflate
* data, zlib's inflate() is about four times as fast as puff(). zlib's
* inflate compiles to around 20K on my machine, whereas puff.c compiles to
* around 4K on my machine (a PowerPC using GNU cc). If the faster decode()
* function here is used, then puff() is only twice as slow as zlib's
* inflate().
*
* All dynamically allocated memory comes from the stack. The stack required
* is less than 2K bytes. This code is compatible with 16-bit int's and
* assumes that long's are at least 32 bits. puff.c uses the short data type,
* assumed to be 16 bits, for arrays in order to conserve memory. The code
* works whether integers are stored big endian or little endian.
*
* In the comments below are "Format notes" that describe the inflate process
* and document some of the less obvious aspects of the format. This source
* code is meant to supplement RFC 1951, which formally describes the deflate
* format:
*
* http://www.zlib.org/rfc-deflate.html
*/
/*
* Change history:
*
* 1.0 10 Feb 2002 - First version
* 1.1 17 Feb 2002 - Clarifications of some comments and notes
* - Update puff() dest and source pointers on negative
* errors to facilitate debugging deflators
* - Remove longest from struct huffman -- not needed
* - Simplify offs[] index in construct()
* - Add input size and checking, using longjmp() to
* maintain easy readability
* - Use short data type for large arrays
* - Use pointers instead of long to specify source and
* destination sizes to avoid arbitrary 4 GB limits
* 1.2 17 Mar 2002 - Add faster version of decode(), doubles speed (!),
* but leave simple version for readabilty
* - Make sure invalid distances detected if pointers
* are 16 bits
* - Fix fixed codes table error
* - Provide a scanning mode for determining size of
* uncompressed data
* 1.3 20 Mar 2002 - Go back to lengths for puff() parameters [Gailly]
* - Add a puff.h file for the interface
* - Add braces in puff() for else do [Gailly]
* - Use indexes instead of pointers for readability
* 1.4 31 Mar 2002 - Simplify construct() code set check
* - Fix some comments
* - Add FIXLCODES #define
* 1.5 6 Apr 2002 - Minor comment fixes
* 1.6 7 Aug 2002 - Minor format changes
* 1.7 3 Mar 2003 - Added test code for distribution
* - Added zlib-like license
* 1.8 9 Jan 2004 - Added some comments on no distance codes case
* 1.9 21 Feb 2008 - Fix bug on 16-bit integer architectures [Pohland]
* - Catch missing end-of-block symbol error
* 2.0 25 Jul 2008 - Add #define to permit distance too far back
* - Add option in TEST code for puff to write the data
* - Add option in TEST code to skip input bytes
* - Allow TEST code to read from piped stdin
* 2.1 4 Apr 2010 - Avoid variable initialization for happier compilers
* - Avoid unsigned comparisons for even happier compilers
* 2.2 25 Apr 2010 - Fix bug in variable initializations [Oberhumer]
* - Add const where appropriate [Oberhumer]
* - Split if's and ?'s for coverage testing
* - Break out test code to separate file
* - Move NIL to puff.h
* - Allow incomplete code only if single code length is 1
* - Add full code coverage test to Makefile
* 2.3 21 Jan 2013 - Check for invalid code length codes in dynamic blocks
* ?? 22 May 2021 - Convert to M2-Planet C subset for bootstrapping purposes.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "M2libc/bootstrappable.h"
/*
* Maximums for allocations and loops. It is not useful to change these --
* they are fixed by the deflate format.
*/
#define MAXBITS 15 /* maximum bits in a code */
#define MAXLCODES 286 /* maximum number of literal/length codes */
#define MAXDCODES 30 /* maximum number of distance codes */
#define MAXCODES 316 /* maximum codes lengths to read (MAXLCODES+MAXDCODES) */
#define FIXLCODES 288 /* number of fixed literal/length codes */
/* input and output state */
struct state {
/* output state */
char *out; /* output buffer */
size_t outlen; /* available space at out */
size_t outcnt; /* bytes written to out so far */
/* input state */
char *in; /* input buffer */
size_t inlen; /* available input at in */
size_t incnt; /* bytes read so far */
int bitbuf; /* bit buffer */
int bitcnt; /* number of bits in bit buffer */
};
/*
* Return need bits from the input stream. This always leaves less than
* eight bits in the buffer. bits() works properly for need == 0.
*
* Format notes:
*
* - Bits are stored in bytes from the least significant bit to the most
* significant bit. Therefore bits are dropped from the bottom of the bit
* buffer, using shift right, and new bytes are appended to the top of the
* bit buffer, using shift left.
*/
int bits(struct state *s, int need)
{
long val; /* bit accumulator (can use up to 20 bits) */
long hold;
/* load at least need bits into val */
val = s->bitbuf;
while (s->bitcnt < need)
{
if (s->incnt == s->inlen)
{
fputs("out of input\n", stderr);
exit(EXIT_FAILURE);
}
hold = (s->in[s->incnt] & 0xFF);
s->incnt = s->incnt + 1;
val = val | (hold << s->bitcnt); /* load eight bits */
s->bitcnt = s->bitcnt + 8;
}
/* drop need bits and update buffer, always zero to seven bits left */
s->bitbuf = (val >> need);
s->bitcnt = s->bitcnt - need;
/* return need bits, zeroing the bits above that */
val = (val & ((1 << need) - 1));
#if defined(DEBUG)
fputs(int2str(val, 16, FALSE), stderr);
fputs(" : bits\n", stderr);
#endif
return val;
}
/*
* Process a stored block.
*
* Format notes:
*
* - After the two-bit stored block type (00), the stored block length and
* stored bytes are byte-aligned for fast copying. Therefore any leftover
* bits in the byte that has the last bit of the type, as many as seven, are
* discarded. The value of the discarded bits are not defined and should not
* be checked against any expectation.
*
* - The second inverted copy of the stored block length does not have to be
* checked, but it's probably a good idea to do so anyway.
*
* - A stored block can have zero length. This is sometimes used to byte-align
* subsets of the compressed data for random access or partial recovery.
*/
int stored(struct state *s)
{
unsigned len; /* length of stored block */
/* discard leftover bits from current byte (assumes s->bitcnt < 8) */
s->bitbuf = 0;
s->bitcnt = 0;
/* get length and check against its one's complement */
if ((s->incnt + 4) > s->inlen) return 2; /* not enough input */
len = s->in[s->incnt];
s->incnt = s->incnt + 1;
len = len | (s->in[s->incnt] << 8);
s->incnt = s->incnt + 1;
if(s->in[s->incnt] != (~len & 0xff)) return -2; /* didn't match complement! */
s->incnt = s->incnt + 1;
if(s->in[s->incnt] != ((~len >> 8) & 0xff)) return -2; /* didn't match complement! */
s->incnt = s->incnt + 1;
/* copy len bytes from in to out */
if ((s->incnt + len) > s->inlen) return 2; /* not enough input */
if (s->out != 0)
{
if ((s->outcnt + len) > s->outlen) return 1; /* not enough output space */
while (0 != len)
{
len = len - 1;
s->out[s->outcnt] = s->in[s->incnt];
s->outcnt = s->outcnt + 1;
s->incnt = s->incnt + 1;
}
}
else
{ /* just scanning */
s->outcnt = s->outcnt + len;
s->incnt = s->incnt + len;
}
/* done with a valid stored block */
return 0;
}
/*
* Huffman code decoding tables. count[1..MAXBITS] is the number of symbols of
* each length, which for a canonical code are stepped through in order.
* symbol[] are the symbol values in canonical order, where the number of
* entries is the sum of the counts in count[]. The decoding process can be
* seen in the function decode() below.
*/
struct huffman
{
int *count; /* number of symbols of each length */
int *symbol; /* canonically ordered symbols */
};
/*
* Decode a code from the stream s using huffman table h. Return the symbol or
* a negative value if there is an error. If all of the lengths are zero, i.e.
* an empty code, or if the code is incomplete and an invalid code is received,
* then -10 is returned after reading MAXBITS bits.
*
* Format notes:
*
* - The codes as stored in the compressed data are bit-reversed relative to
* a simple integer ordering of codes of the same lengths. Hence below the
* bits are pulled from the compressed data one at a time and used to
* build the code value reversed from what is in the stream in order to
* permit simple integer comparisons for decoding. A table-based decoding
* scheme (as used in zlib) does not need to do this reversal.
*
* - The first code for the shortest length is all zeros. Subsequent codes of
* the same length are simply integer increments of the previous code. When
* moving up a length, a zero bit is appended to the code. For a complete
* code, the last code of the longest length will be all ones.
*
* - Incomplete codes are handled by this decoder, since they are permitted
* in the deflate format. See the format notes for fixed() and dynamic().
*/
int decode(struct state *s, struct huffman *h)
{
int len; /* current number of bits in code */
int code = 0; /* len bits being decoded */
int first = 0; /* first code of length len */
int count; /* number of codes of length len */
int index = 0; /* index of first code of length len in symbol table */
long hold;
for (len = 1; len <= MAXBITS; len = len + 1)
{
hold = bits(s, 1); /* get next bit */
code = code | hold;
count = h->count[len];
if ((code - count) < first)
{
hold = index + (code - first);
return h->symbol[hold]; /* if length len, return symbol */
}
index = index + count; /* else update for next length */
first = first + count;
first = first << 1;
code = code << 1;
}
return -10; /* ran out of codes */
}
/*
* Given the list of code lengths length[0..n-1] representing a canonical
* Huffman code for n symbols, construct the tables required to decode those
* codes. Those tables are the number of codes of each length, and the symbols
* sorted by length, retaining their original order within each length. The
* return value is zero for a complete code set, negative for an over-
* subscribed code set, and positive for an incomplete code set. The tables
* can be used if the return value is zero or positive, but they cannot be used
* if the return value is negative. If the return value is zero, it is not
* possible for decode() using that table to return an error--any stream of
* enough bits will resolve to a symbol. If the return value is positive, then
* it is possible for decode() using that table to return an error for received
* codes past the end of the incomplete lengths.
*
* Not used by decode(), but used for error checking, h->count[0] is the number
* of the n symbols not in the code. So n - h->count[0] is the number of
* codes. This is useful for checking for incomplete codes that have more than
* one symbol, which is an error in a dynamic block.
*
* Assumption: for all i in 0..n-1, 0 <= length[i] <= MAXBITS
* This is assured by the construction of the length arrays in dynamic() and
* fixed() and is not verified by construct().
*
* Format notes:
*
* - Permitted and expected examples of incomplete codes are one of the fixed
* codes and any code with a single symbol which in deflate is coded as one
* bit instead of zero bits. See the format notes for fixed() and dynamic().
*
* - Within a given code length, the symbols are kept in ascending order for
* the code bits definition.
*/
int construct(struct huffman *h, int *length, int n)
{
int symbol; /* current symbol when stepping through length[] */
int len; /* current length when stepping through h->count[] */
int left; /* number of possible codes left of current length */
int* offs; /* offsets in symbol table for each length */
offs = calloc(MAXBITS+1, sizeof(int));
long hold;
#if defined(DEBUG)
int i;
fputs(int2str(n, 16, FALSE), stderr);
fputs(" : construct 0\n", stderr);
for(i = 0; i < n; i = i + 1)
{
fputs(int2str(length[i], 16, FALSE), stderr);
fputs(" : construct 2\n", stderr);
}
#endif
/* count number of codes of each length */
for (len = 0; len <= MAXBITS; len = len + 1)
{
h->count[len] = 0;
}
for (symbol = 0; symbol < n; symbol = symbol + 1)
{
hold = length[symbol];
h->count[hold] = h->count[hold] + 1; /* assumes lengths are within bounds */
}
if (h->count[0] == n) return 0; /* no codes! complete, but decode() will fail */
/* check for an over-subscribed or incomplete set of lengths */
left = 1; /* one possible code of zero length */
for (len = 1; len <= MAXBITS; len = len + 1)
{
left = left << 1; /* one more bit, double codes left */
left = left - h->count[len]; /* deduct count from possible codes */
if (left < 0) return left; /* over-subscribed--return negative */
} /* left > 0 means incomplete */
/* generate offsets into symbol table for each length for sorting */
offs[1] = 0;
for (len = 1; len < MAXBITS; len = len + 1)
{
offs[len + 1] = offs[len] + h->count[len];
}
/*
* put symbols in table sorted by length, by symbol order within each
* length
*/
for (symbol = 0; symbol < n; symbol = symbol + 1)
{
if (length[symbol] != 0)
{
hold = length[symbol];
hold = offs[hold];
h->symbol[hold] = symbol;
hold = length[symbol];
offs[hold] = offs[hold] + 1;
}
}
/* return zero for complete set, positive for incomplete set */
return left;
}
/*
* Decode literal/length and distance codes until an end-of-block code.
*
* Format notes:
*
* - Compressed data that is after the block type if fixed or after the code
* description if dynamic is a combination of literals and length/distance
* pairs terminated by and end-of-block code. Literals are simply Huffman
* coded bytes. A length/distance pair is a coded length followed by a
* coded distance to represent a string that occurs earlier in the
* uncompressed data that occurs again at the current location.
*
* - Literals, lengths, and the end-of-block code are combined into a single
* code of up to 286 symbols. They are 256 literals (0..255), 29 length
* symbols (257..285), and the end-of-block symbol (256).
*
* - There are 256 possible lengths (3..258), and so 29 symbols are not enough
* to represent all of those. Lengths 3..10 and 258 are in fact represented
* by just a length symbol. Lengths 11..257 are represented as a symbol and
* some number of extra bits that are added as an integer to the base length
* of the length symbol. The number of extra bits is determined by the base
* length symbol. These are in the static arrays below, lens[] for the base
* lengths and lext[] for the corresponding number of extra bits.
*
* - The reason that 258 gets its own symbol is that the longest length is used
* often in highly redundant files. Note that 258 can also be coded as the
* base value 227 plus the maximum extra value of 31. While a good deflate
* should never do this, it is not an error, and should be decoded properly.
*
* - If a length is decoded, including its extra bits if any, then it is
* followed a distance code. There are up to 30 distance symbols. Again
* there are many more possible distances (1..32768), so extra bits are added
* to a base value represented by the symbol. The distances 1..4 get their
* own symbol, but the rest require extra bits. The base distances and
* corresponding number of extra bits are below in the static arrays dist[]
* and dext[].
*
* - Literal bytes are simply written to the output. A length/distance pair is
* an instruction to copy previously uncompressed bytes to the output. The
* copy is from distance bytes back in the output stream, copying for length
* bytes.
*
* - Distances pointing before the beginning of the output data are not
* permitted.
*
* - Overlapped copies, where the length is greater than the distance, are
* allowed and common. For example, a distance of one and a length of 258
* simply copies the last byte 258 times. A distance of four and a length of
* twelve copies the last four bytes three times. A simple forward copy
* ignoring whether the length is greater than the distance or not implements
* this correctly. You should not use memcpy() since its behavior is not
* defined for overlapped arrays. You should not use memmove() or bcopy()
* since though their behavior -is- defined for overlapping arrays, it is
* defined to do the wrong thing in this case.
*/
int* codes_lens()
{
/* Size base for length codes 257..285 */
int* r = calloc(30, sizeof(int));
r[0] = 3;
r[1] = 4;
r[2] = 5;
r[3] = 6;
r[4] = 7;
r[5] = 8;
r[6] = 9;
r[7] = 10;
r[8] = 11;
r[9] = 13;
r[10] = 15;
r[11] = 17;
r[12] = 19;
r[13] = 23;
r[14] = 27;
r[15] = 31;
r[16] = 35;
r[17] = 43;
r[18] = 51;
r[19] = 59;
r[20] = 67;
r[21] = 83;
r[22] = 99;
r[23] = 115;
r[24] = 131;
r[25] = 163;
r[26] = 195;
r[27] = 227;
r[28] = 258;
return r;
}
int* codes_lext()
{
/* Extra bits for length codes 257..285 */
int* r = calloc(30, sizeof(int));
r[0] = 0;
r[1] = 0;
r[2] = 0;
r[3] = 0;
r[4] = 0;
r[5] = 0;
r[6] = 0;
r[7] = 0;
r[8] = 1;
r[9] = 1;
r[10] = 1;
r[11] = 1;
r[12] = 2;
r[13] = 2;
r[14] = 2;
r[15] = 2;
r[16] = 3;
r[17] = 3;
r[18] = 3;
r[19] = 3;
r[20] = 4;
r[21] = 4;
r[22] = 4;
r[23] = 4;
r[24] = 5;
r[25] = 5;
r[26] = 5;
r[27] = 5;
r[28] = 0;
return r;
}
int* codes_dists()
{
/* Offset base for distance codes 0..29 */
int* r = calloc(31, sizeof(int));
r[0] = 1;
r[1] = 2;
r[2] = 3;
r[3] = 4;
r[4] = 5;
r[5] = 7;
r[6] = 9;
r[7] = 13;
r[8] = 17;
r[9] = 25;
r[10] = 33;
r[11] = 49;
r[12] = 65;
r[13] = 97;
r[14] = 129;
r[15] = 193;
r[16] = 257;
r[17] = 385;
r[18] = 513;
r[19] = 769;
r[20] = 1025;
r[21] = 1537;
r[22] = 2049;
r[23] = 3073;
r[24] = 4097;
r[25] = 6145;
r[26] = 8193;
r[27] = 12289;
r[28] = 16385;
r[29] = 24577;
return r;
}
int* codes_dext()
{
/* Extra bits for distance codes 0..29 */
int* r = calloc(31, sizeof(int));
r[0] = 0;
r[1] = 0;
r[2] = 0;
r[3] = 0;
r[4] = 1;
r[5] = 1;
r[6] = 2;
r[7] = 2;
r[8] = 3;
r[9] = 3;
r[10] = 4;
r[11] = 4;
r[12] = 5;
r[13] = 5;
r[14] = 6;
r[15] = 6;
r[16] = 7;
r[17] = 7;
r[18] = 8;
r[19] = 8;
r[20] = 9;
r[21] = 9;
r[22] = 10;
r[23] = 10;
r[24] = 11;
r[25] = 11;
r[26] = 12;
r[27] = 12;
r[28] = 13;
r[29] = 13;
return r;
}
int codes(struct state *s, struct huffman *lencode, struct huffman *distcode)
{
int symbol; /* decoded symbol */
int len; /* length for copy */
unsigned dist; /* distance for copy */
int* lens = codes_lens();
int* lext = codes_lext();
int* dists = codes_dists();
int* dext = codes_dext();
/* decode literals and length/distance pairs */
do
{
symbol = decode(s, lencode);
if (symbol < 0) return symbol; /* invalid symbol */
if (symbol < 256) /* literal: symbol is the byte */
{
/* write out the literal */
if (s->out != 0)
{
if (s->outcnt == s->outlen) return 1;
s->out[s->outcnt] = symbol;
}
s->outcnt = s->outcnt + 1;
}
else if (symbol > 256) /* length */
{
/* get and compute length */
symbol = symbol - 257;
if (symbol >= 29) return -10; /* invalid fixed code */
len = lens[symbol] + bits(s, lext[symbol]);
/* get and check distance */
symbol = decode(s, distcode);
if (symbol < 0) return symbol; /* invalid symbol */
dist = dists[symbol] + bits(s, dext[symbol]);
if (dist > s->outcnt) return -11; /* distance too far back */
/* copy length bytes from distance bytes back */
if (s->out != 0)
{
if (s->outcnt + len > s->outlen) return 1;
while (0 != len)
{
len = len - 1;
if(dist > s->outcnt) s->out[s->outcnt] = 0;
else s->out[s->outcnt] = s->out[s->outcnt - dist];
s->outcnt = s->outcnt + 1;
}
}
else s->outcnt = s->outcnt + len;
}
} while (symbol != 256); /* end of block symbol */
/* done with a valid fixed or dynamic block */
return 0;
}
/*
* Process a fixed codes block.
*
* Format notes:
*
* - This block type can be useful for compressing small amounts of data for
* which the size of the code descriptions in a dynamic block exceeds the
* benefit of custom codes for that block. For fixed codes, no bits are
* spent on code descriptions. Instead the code lengths for literal/length
* codes and distance codes are fixed. The specific lengths for each symbol
* can be seen in the "for" loops below.
*
* - The literal/length code is complete, but has two symbols that are invalid
* and should result in an error if received. This cannot be implemented
* simply as an incomplete code since those two symbols are in the "middle"
* of the code. They are eight bits long and the longest literal/length\
* code is nine bits. Therefore the code must be constructed with those
* symbols, and the invalid symbols must be detected after decoding.
*
* - The fixed distance codes also have two invalid symbols that should result
* in an error if received. Since all of the distance codes are the same
* length, this can be implemented as an incomplete code. Then the invalid
* codes are detected while decoding.
*/
int fixed(struct state *s)
{
int* lencnt = calloc((MAXBITS + 1), sizeof(int));
int* lensym = calloc(FIXLCODES, sizeof(int));
int* distcnt = calloc((MAXBITS + 1), sizeof(int));
int* distsym = calloc(MAXDCODES, sizeof(int));
struct huffman* lencode = calloc(1, sizeof(struct huffman));
struct huffman* distcode = calloc(1, sizeof(struct huffman));
int hold;
/* build fixed huffman tables if first call (may not be thread safe) */
int symbol;
int* lengths = calloc(FIXLCODES, sizeof(int));
/* construct lencode and distcode */
lencode->count = lencnt;
lencode->symbol = lensym;
distcode->count = distcnt;
distcode->symbol = distsym;
/* literal/length table */
for (symbol = 0; symbol < 144; symbol = symbol + 1)
{
lengths[symbol] = 8;
}
while(symbol < 256)
{
lengths[symbol] = 9;
symbol = symbol + 1;
}
while(symbol < 280)
{
lengths[symbol] = 7;
symbol = symbol + 1;
}
while(symbol < FIXLCODES)
{
lengths[symbol] = 8;
symbol = symbol + 1;
}
construct(lencode, lengths, FIXLCODES);
/* distance table */
for (symbol = 0; symbol < MAXDCODES; symbol = symbol + 1)
{
lengths[symbol] = 5;
}
construct(distcode, lengths, MAXDCODES);
/* decode data until end-of-block code */
hold = codes(s, lencode, distcode);
return hold;
}
/*
* Process a dynamic codes block.
*
* Format notes:
*
* - A dynamic block starts with a description of the literal/length and
* distance codes for that block. New dynamic blocks allow the compressor to
* rapidly adapt to changing data with new codes optimized for that data.
*
* - The codes used by the deflate format are "canonical", which means that
* the actual bits of the codes are generated in an unambiguous way simply
* from the number of bits in each code. Therefore the code descriptions
* are simply a list of code lengths for each symbol.
*
* - The code lengths are stored in order for the symbols, so lengths are
* provided for each of the literal/length symbols, and for each of the
* distance symbols.
*
* - If a symbol is not used in the block, this is represented by a zero as
* as the code length. This does not mean a zero-length code, but rather
* that no code should be created for this symbol. There is no way in the
* deflate format to represent a zero-length code.
*
* - The maximum number of bits in a code is 15, so the possible lengths for
* any code are 1..15.
*
* - The fact that a length of zero is not permitted for a code has an
* interesting consequence. Normally if only one symbol is used for a given
* code, then in fact that code could be represented with zero bits. However
* in deflate, that code has to be at least one bit. So for example, if
* only a single distance base symbol appears in a block, then it will be
* represented by a single code of length one, in particular one 0 bit. This
* is an incomplete code, since if a 1 bit is received, it has no meaning,
* and should result in an error. So incomplete distance codes of one symbol
* should be permitted, and the receipt of invalid codes should be handled.
*
* - It is also possible to have a single literal/length code, but that code
* must be the end-of-block code, since every dynamic block has one. This
* is not the most efficient way to create an empty block (an empty fixed
* block is fewer bits), but it is allowed by the format. So incomplete
* literal/length codes of one symbol should also be permitted.
*
* - If there are only literal codes and no lengths, then there are no distance
* codes. This is represented by one distance code with zero bits.
*
* - The list of up to 286 length/literal lengths and up to 30 distance lengths
* are themselves compressed using Huffman codes and run-length encoding. In
* the list of code lengths, a 0 symbol means no code, a 1..15 symbol means
* that length, and the symbols 16, 17, and 18 are run-length instructions.
* Each of 16, 17, and 18 are follwed by extra bits to define the length of
* the run. 16 copies the last length 3 to 6 times. 17 represents 3 to 10
* zero lengths, and 18 represents 11 to 138 zero lengths. Unused symbols
* are common, hence the special coding for zero lengths.
*
* - The symbols for 0..18 are Huffman coded, and so that code must be
* described first. This is simply a sequence of up to 19 three-bit values
* representing no code (0) or the code length for that symbol (1..7).
*
* - A dynamic block starts with three fixed-size counts from which is computed
* the number of literal/length code lengths, the number of distance code
* lengths, and the number of code length code lengths (ok, you come up with
* a better name!) in the code descriptions. For the literal/length and
* distance codes, lengths after those provided are considered zero, i.e. no
* code. The code length code lengths are received in a permuted order (see
* the order[] array below) to make a short code length code length list more
* likely. As it turns out, very short and very long codes are less likely
* to be seen in a dynamic code description, hence what may appear initially
* to be a peculiar ordering.
*
* - Given the number of literal/length code lengths (nlen) and distance code
* lengths (ndist), then they are treated as one long list of nlen + ndist
* code lengths. Therefore run-length coding can and often does cross the
* boundary between the two sets of lengths.
*
* - So to summarize, the code description at the start of a dynamic block is
* three counts for the number of code lengths for the literal/length codes,
* the distance codes, and the code length codes. This is followed by the
* code length code lengths, three bits each. This is used to construct the
* code length code which is used to read the remainder of the lengths. Then
* the literal/length code lengths and distance lengths are read as a single
* set of lengths using the code length codes. Codes are constructed from
* the resulting two sets of lengths, and then finally you can start
* decoding actual compressed data in the block.
*
* - For reference, a "typical" size for the code description in a dynamic
* block is around 80 bytes.
*/
int* dynamic_order()
{
/* permutation of code length codes */
int* r = calloc(20, sizeof(int));
r[0] = 16;
r[1] = 17;
r[2] = 18;
r[3] = 0;
r[4] = 8;
r[5] = 7;
r[6] = 9;
r[7] = 6;
r[8] = 10;
r[9] = 5;
r[10] = 11;
r[11] = 4;
r[12] = 12;
r[13] = 3;
r[14] = 13;
r[15] = 2;
r[16] = 14;
r[17] = 1;
r[18] = 15;
return r;
}
int dynamic(struct state *s)
{
#if defined(__M2__)
int array = sizeof(int);
#else
int array = 1;
#endif
int nlen;
int ndist;
int ncode; /* number of lengths in descriptor */
int index; /* index of lengths[] */
int err; /* construct() return value */
int* lengths = calloc(MAXCODES, sizeof(int)); /* descriptor code lengths */
int* lencnt = calloc((MAXBITS + 1), sizeof(int));
int* lensym = calloc(MAXLCODES, sizeof(int)); /* lencode memory */
int* distcnt = calloc((MAXBITS + 1), sizeof(int));
int* distsym = calloc(MAXDCODES, sizeof(int)); /* distcode memory */
struct huffman* lencode = calloc(1, sizeof(struct huffman));
struct huffman* distcode = calloc(1, sizeof(struct huffman));
int* order = dynamic_order();
long hold;
int* set;
/* construct lencode and distcode */
lencode->count = lencnt;
lencode->symbol = lensym;
distcode->count = distcnt;
distcode->symbol = distsym;
/* get number of lengths in each table, check lengths */
nlen = bits(s, 5) + 257;
ndist = bits(s, 5) + 1;
ncode = bits(s, 4) + 4;
if (nlen > MAXLCODES) return -3; /* bad counts */
if(ndist > MAXDCODES) return -3; /* bad counts */
/* read code length code lengths (really), missing lengths are zero */
for (index = 0; index < ncode; index = index + 1)
{
hold = order[index];
lengths[hold] = bits(s, 3);
}
while(index < 19)
{
hold = order[index];
lengths[hold] = 0;
index = index + 1;
}
/* build huffman table for code lengths codes (use lencode temporarily) */
err = construct(lencode, lengths, 19);
if (err != 0) return -4; /* require complete code set here */
/* read length/literal and distance code length tables */
index = 0;
int symbol; /* decoded value */
int len; /* last length to repeat */
while (index < (nlen + ndist))
{
symbol = decode(s, lencode);
if (symbol < 0) return symbol; /* invalid symbol */
if (symbol < 16) /* length in 0..15 */
{
lengths[index] = symbol;
index = index + 1;
}
else /* repeat instruction */
{
len = 0; /* assume repeating zeros */
if (symbol == 16) /* repeat last length 3..6 times */
{
if (index == 0) return -5; /* no last length! */
len = lengths[index - 1]; /* last length */
symbol = 3 + bits(s, 2);
}
else if (symbol == 17) symbol = 3 + bits(s, 3); /* repeat zero 3..10 times */
else symbol = 11 + bits(s, 7); /* == 18, repeat zero 11..138 times */
if ((index + symbol) > (nlen + ndist)) return -6; /* too many lengths! */
while(0 != symbol) /* repeat last or zero symbol times */
{
lengths[index] = len;
index = index + 1;
symbol = symbol - 1;
}
}
}
/* check for end-of-block code -- there better be one! */
if (lengths[256] == 0) return -9;
/* build huffman table for literal/length codes */
err = construct(lencode, lengths, nlen);
/* incomplete code ok only for single length 1 code */
if (err < 0) return -7;
if((0 != err) && (nlen != (lencode->count[0] + lencode->count[1]))) return -7;
/* build huffman table for distance codes */
set = lengths + (nlen * array);
err = construct(distcode, set, ndist);
/* incomplete code ok only for single length 1 code */
if (err < 0) return -8;
if((0 != err) && (ndist != (distcode->count[0] + distcode->count[1]))) return -8;
/* decode data until end-of-block code */
hold = codes(s, lencode, distcode);
return hold;
}
/*
* Inflate source to dest. On return, destlen and sourcelen are updated to the
* size of the uncompressed data and the size of the deflate data respectively.
* On success, the return value of puff() is zero. If there is an error in the
* source data, i.e. it is not in the deflate format, then a negative value is
* returned. If there is not enough input available or there is not enough
* output space, then a positive error is returned. In that case, destlen and
* sourcelen are not updated to facilitate retrying from the beginning with the
* provision of more input data or more output space. In the case of invalid
* inflate data (a negative error), the dest and source pointers are updated to
* facilitate the debugging of deflators.
*
* puff() also has a mode to determine the size of the uncompressed output with
* no output written. For this dest must be (unsigned char *)0. In this case,
* the input value of *destlen is ignored, and on return *destlen is set to the
* size of the uncompressed output.
*
* The return codes are:
*
* 2: available inflate data did not terminate
* 1: output space exhausted before completing inflate
* 0: successful inflate
* -1: invalid block type (type == 3)
* -2: stored block length did not match one's complement
* -3: dynamic block code description: too many length or distance codes
* -4: dynamic block code description: code lengths codes incomplete
* -5: dynamic block code description: repeat lengths with no first length
* -6: dynamic block code description: repeat more than specified lengths
* -7: dynamic block code description: invalid literal/length code lengths
* -8: dynamic block code description: invalid distance code lengths
* -9: dynamic block code description: missing end-of-block code
* -10: invalid literal/length or distance code in fixed or dynamic block
* -11: distance is too far back in fixed or dynamic block
*
* Format notes:
*
* - Three bits are read for each block to determine the kind of block and
* whether or not it is the last block. Then the block is decoded and the
* process repeated if it was not the last block.
*
* - The leftover bits in the last byte of the deflate data after the last
* block (if it was a fixed or dynamic block) are undefined and have no
* expected values to check.
*/
struct puffer
{
int error;
size_t destlen;
size_t sourcelen;
};
struct puffer* puff(char* dest, size_t destlen, char* source, size_t sourcelen)
{
struct state* s = calloc(1, sizeof(struct state)); /* input/output state */
int last;
int type; /* block information */
int err; /* return value */
/* initialize output state */
s->out = dest;
s->outlen = destlen; /* ignored if dest is NIL */
s->outcnt = 0;
/* initialize input state */
s->in = source;
s->inlen = sourcelen;
s->incnt = 0;
s->bitbuf = 0;
s->bitcnt = 0;
/* process blocks until last block or error */
do
{
last = bits(s, 1); /* one if last block */
type = bits(s, 2); /* block type 0..3 */
if(0 == type)
{
err = stored(s);
}
else if(1 == type)
{
err = fixed(s);
}
else if(2 == type)
{
err = dynamic(s);
}
else err = -1;
if (err != 0) break; /* return with error */
} while (!last);
/* update the lengths and return */
struct puffer* r = calloc(1, sizeof(struct puffer));
r->error = err;
r->destlen = s->outcnt;
r->sourcelen = s->incnt;
return r;
}
void write_blob(char* s, int start, int len, FILE* f)
{
char* table = "0123456789ABCDEF";
if(start > len) return;
int i = s[start] & 0xFF;
fputc(table[(i >> 4)], f);
fputc(table[(i & 0xF)], f);
fputc(' ', f);
if(start == len) fputc('\n', f);
else fputc(' ', f);
write_blob(s, start + 1, len, f);
}
#define FTEXT 0x01
#define FHCRC 0x02
#define FEXTRA 0x04
#define FNAME 0x08
#define FCOMMENT 0x10
struct gz
{
char* HEADER;
int ID;
int CM;
int FLG;
int MTIME;
int XFL;
int OS;
int XLEN;
char* FLG_FEXTRA;
char* FLG_FNAME;
char* FLG_FCOMMENT;
int CRC16;
char* FLG_FHCRC;
char* block;
int CRC32;
size_t ISIZE;
size_t file_size;
};
/* Read the input file *name, or stdin if name is NULL, into allocated memory.
Reallocate to larger buffers until the entire file is read in. Return a
pointer to the allocated data, or NULL if there was a memory allocation
failure. *len is the number of bytes of data read from the input file (even
if load() returns NULL). If the input file was empty or could not be opened
or read, *len is zero. */
struct gz* load(char* name)
{
struct gz* r = calloc(1, sizeof(struct gz));
char* scratch = calloc(5, sizeof(char));
FILE* f = fopen(name, "r");
int count;
int ID1;
int ID2;
int count1;
int count2;
int count3;
int count4;
int c;
int i;
char* s = calloc(11, sizeof(char));
if(NULL == f)
{
fputs("unable to open file: ", stderr);
fputs(name, stderr);
fputs("\nfor reading\n", stderr);
return NULL;
}
fseek(f, 0, SEEK_END);
r->file_size = ftell(f);
fseek(f, 0, SEEK_SET);
count = fread(s, sizeof(char), 10, f);
if(10 != count)
{
fputs("incomplete gzip header\n", stderr);
return NULL;
}
/* Verify header */
r->HEADER = s;
#if defined(DEBUG)
write_blob(s, 0, 10, stderr);
#endif
ID1 = (s[0] & 0xFF);
ID2 = (s[1] & 0xFF);
r->ID = ((ID1 << 8) | ID2);
if(0x1f8b != r->ID)
{
fputs("bad header\n", stderr);
return NULL;
}
/* Verify Compression */
r->CM = (r->HEADER[2] & 0xFF);
if(8 != r->CM)
{
fputs("NOT DEFLATE COMPRESSION\n", stderr);
return NULL;
}
/* Get specials specified in flag bits */
r->FLG = (r->HEADER[3] & 0xFF);
if(0 != (FEXTRA & r->FLG))
{
count = fread(scratch, sizeof(char), 4, f);
count1 = (scratch[0] & 0xFF);
count2 = (scratch[1] & 0xFF);
count3 = (scratch[2] & 0xFF);
count4 = (scratch[3] & 0xFF);
count = (count1 << 24) | (count2 << 16) | (count3 << 8) | count4;
require(0 < count, "FEXTRA field needs to be a positive number of bytes in size\n");
require(100000000 > count, "we don't support FEXTRA fields greater than 100MB in size\n");
r->FLG_FEXTRA = calloc(count + 1, sizeof(char));
fread(r->FLG_FEXTRA, sizeof(char), count, f);
}
if(0 != (FNAME & r->FLG))
{
r->FLG_FNAME = calloc(r->file_size, sizeof(char));
i = 0;
do
{
c = fgetc(f);
require(0 <= c, "received a non-null terminated filename in the file\n");
r->FLG_FNAME[i] = c;
i = i + 1;
} while(0 != c);
}
if(0 != (FCOMMENT & r->FLG))
{
r->FLG_FCOMMENT = calloc(r->file_size, sizeof(char));
i = 0;
do
{
c = fgetc(f);
require(0 <= c, "received a non-null terminated comment in the file\n");
r->FLG_FCOMMENT[i] = c;
i = i + 1;
} while(0 != c);
}
if(0 != (FHCRC & r->FLG))
{
/* Not implemented */
fputs("FHCRC is not implemented at this time\n", stderr);
return NULL;
}
if(NULL == r->FLG_FNAME)
{
count = strlen(name) - 3;
r->FLG_FNAME = calloc(count + 4, sizeof(char));
i = 0;
while(i < count)
{
r->FLG_FNAME[i] = name[i];
i = i + 1;
}
}
r->block = calloc(r->file_size, sizeof(char));
count = fread(r->block, sizeof(char), r->file_size, f);
r->ISIZE = count;
fclose(f);
return r;
}
int main(int argc, char **argv)
{
struct puffer* ret;
char* name;
char* buffer;
char *dest;
struct gz* in;
FILE* out;
int FUZZING = FALSE;
/* process arguments */
int i = 1;
while (i < argc)
{
if(NULL == argv[i])
{
i = i + 1;
}
else if(match(argv[i], "-f") || match(argv[i], "--file"))
{
name = argv[i+1];
require(NULL != name, "the --file option requires a filename to be given\n");
i = i + 2;
}
else if(match(argv[i], "-o") || match(argv[i], "--output"))
{
dest = argv[i+1];
require(NULL != dest, "the --output option requires a filename to be given\n");
i = i + 2;
}
else if(match(argv[i], "--chaos") || match(argv[i], "--fuzz-mode") || match(argv[i], "--fuzzing"))
{
FUZZING = TRUE;
fputs("fuzz-mode enabled, preparing for chaos\n", stderr);
i = i + 1;
}
else if(match(argv[i], "-h") || match(argv[i], "--help"))
{
fputs("Usage: ", stderr);
fputs(argv[0], stderr);
fputs(" --file $input.gz", stderr);
fputs(" [--output $output] (or it'll use the internal filename)\n", stderr);
fputs("--help to get this message\n", stderr);
fputs("--fuzz-mode if you wish to fuzz this application safely\n", stderr);
exit(EXIT_SUCCESS);
}
else
{
fputs("Unknown option:", stderr);
fputs(argv[i], stderr);
fputs("\nAborting to avoid problems\n", stderr);
exit(EXIT_FAILURE);
}
}
in = load(name);
if (in == NULL)
{
fputs("memory allocation failure\nDidn't read file\n", stderr);
exit(1);
}
ret = puff(0, 0, in->block, in->ISIZE);
if(NULL == dest)
{
dest = in->FLG_FNAME;
}
fputs(name, stderr);
fputs(" => ", stderr);
fputs(dest, stderr);
if (0 != ret->error)
{
fputs("puff() failed with return code ", stderr);
fputs(int2str(ret->error, 10, TRUE), stderr);
fputc('\n', stderr);
exit(3);
}
else
{
fputs(": succeeded uncompressing ", stderr);
fputs(int2str(ret->destlen, 10, FALSE), stderr);
fputs(" bytes\n", stderr);
}
buffer = malloc(ret->destlen);
if (buffer == NULL)
{
fputs("memory allocation failure\n", stderr);
return 4;
}
ret = puff(buffer, ret->destlen, in->block, in->ISIZE);
if(!FUZZING)
{
out = fopen(dest, "w");
fwrite(buffer, 1, ret->destlen, out);
}
else
{
fputs("skipped write to file due to --fuzz-mode flag\n", stderr);
}
free(buffer);
/* clean up */
return 0;
}
/* Copyright (C) 2003, 2007 Rob Landley <rob@landley.net>
* Copyright (C) 2022 Paul Dersey <pdersey@gmail.com>
* This file is part of mescc-tools-extra
*
* mescc-tools-extra is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* mescc-tools-extra is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with mescc-tools-extra. If not, see <http://www.gnu.org/licenses/>.
*/
/* bzcat.c - bzip2 decompression
*
* Copyright 2003, 2007 Rob Landley <rob@landley.net>
*
* Based on a close reading (but not the actual code) of the original bzip2
* decompression code by Julian R Seward (jseward@acm.org), which also
* acknowledges contributions by Mike Burrows, David Wheeler, Peter Fenwick,
* Alistair Moffat, Radford Neal, Ian H. Witten, Robert Sedgewick, and
* Jon L. Bentley.
*
* No standard.
*/
/* unbz2.c is a bz2 file decompression utility based on bzcat.c with
* modifications to enable being built by M2-Planet with M2libc.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include "M2libc/bootstrappable.h"
// Constants for huffman coding
#define MAX_GROUPS 6
#define GROUP_SIZE 50 /* 64 would have been more efficient */
#define MAX_HUFCODE_BITS 20 /* Longest huffman code allowed */
#define MAX_SYMBOLS 258 /* 256 literals + RUNA + RUNB */
#define SYMBOL_RUNA 0
#define SYMBOL_RUNB 1
// Other housekeeping constants
#define IOBUF_SIZE 4096
// Status return values
#define RETVAL_LAST_BLOCK (-100)
#define RETVAL_NOT_BZIP_DATA (-1)
#define RETVAL_DATA_ERROR (-2)
#define RETVAL_OBSOLETE_INPUT (-3)
#define INT_MAX 2147483647
// This is what we know about each huffman coding group
struct group_data
{
int *limit;
int *base;
int *permute;
char minLen;
char maxLen;
};
// Data for burrows wheeler transform
struct bwdata
{
unsigned origPtr;
int *byteCount;
// State saved when interrupting output
int writePos;
int writeRun;
int writeCount;
int writeCurrent;
unsigned dataCRC;
unsigned headerCRC;
unsigned *dbuf;
};
// Structure holding all the housekeeping data, including IO buffers and
// memory that persists between calls to bunzip
struct bunzip_data
{
// Input stream, input buffer, input bit buffer
int in_fd;
int inbufCount;
int inbufPos;
char *inbuf;
unsigned inbufBitCount;
unsigned inbufBits;
// Output buffer
char *outbuf;
int outbufPos;
unsigned totalCRC;
// First pass decompression data (Huffman and MTF decoding)
char *selectors; // nSelectors=15 bits
struct group_data *groups; // huffman coding tables
int symTotal;
int groupCount;
int nSelectors;
unsigned *symToByte;
unsigned *mtfSymbol;
// The CRC values stored in the block header and calculated from the data
unsigned *crc32Table;
// Second pass decompression data (burrows-wheeler transform)
unsigned dbufSize;
struct bwdata* bwdata;
};
int FUZZING;
void crc_init(unsigned *crc_table, int little_endian)
{
unsigned i;
unsigned j;
unsigned c;
// Init the CRC32 table (big endian)
for(i = 0; i < 256; i += 1)
{
if(little_endian)
{
c = i;
}
else
{
c = i << 24;
}
for(j = 8; j > 0; j -= 1)
{
if(little_endian)
{
if(c & 1)
{
c = (c >> 1) ^ 0xEDB88320;
}
else
{
c = c >> 1;
}
}
else
{
if(c & 0x80000000)
{
c = (c << 1) ^ 0x04C11DB7;
#if defined(__M2__)
// & 0xFFFFFFFF not working
if(sizeof(unsigned) == 8)
{
c <<= 32;
c >>= 32;
}
#endif
}
else
{
c = c << 1;
}
}
}
crc_table[i] = c;
}
}
// Return the next nnn bits of input. All reads from the compressed input
// are done through this function. All reads are big endian.
unsigned get_bits(struct bunzip_data *bd, char bits_wanted)
{
unsigned bits = 0;
// If we need to get more data from the byte buffer, do so. (Loop getting
// one byte at a time to enforce endianness and avoid unaligned access.)
while(bd->inbufBitCount < bits_wanted)
{
// If we need to read more data from file into byte buffer, do so
if(bd->inbufPos == bd->inbufCount)
{
if(0 >= (bd->inbufCount = read(bd->in_fd, bd->inbuf, IOBUF_SIZE)))
{
exit(1);
}
bd->inbufPos = 0;
}
// Avoid 32-bit overflow (dump bit buffer to top of output)
if(bd->inbufBitCount >= 24)
{
bits = bd->inbufBits & ((1 << bd->inbufBitCount) - 1);
bits_wanted = bits_wanted - bd->inbufBitCount;
bits = bits << bits_wanted;
bd->inbufBitCount = 0;
}
// Grab next 8 bits of input from buffer.
bd->inbufBits = (bd->inbufBits << 8) | (bd->inbuf[bd->inbufPos] & 0xFF);
bd->inbufPos = bd->inbufPos + 1;
bd->inbufBitCount = bd->inbufBitCount + 8;
}
// Calculate result
bd->inbufBitCount = bd->inbufBitCount - bits_wanted;
bits = bits | ((bd->inbufBits >> bd->inbufBitCount) & ((1 << bits_wanted) - 1));
return bits;
}
/* Read block header at start of a new compressed data block. Consists of:
*
* 48 bits : Block signature, either pi (data block) or e (EOF block).
* 32 bits : bw->headerCRC
* 1 bit : obsolete feature flag.
* 24 bits : origPtr (Burrows-wheeler unwind index, only 20 bits ever used)
* 16 bits : Mapping table index.
*[16 bits]: symToByte[symTotal] (Mapping table. For each bit set in mapping
* table index above, read another 16 bits of mapping table data.
* If correspondig bit is unset, all bits in that mapping table
* section are 0.)
* 3 bits : groupCount (how many huffman tables used to encode, anywhere
* from 2 to MAX_GROUPS)
* variable: hufGroup[groupCount] (MTF encoded huffman table data.)
*/
int read_block_header(struct bunzip_data *bd, struct bwdata *bw)
{
struct group_data *hufGroup;
int hh;
int ii;
int jj;
int kk;
int symCount;
int *base;
int *limit;
unsigned uc;
unsigned *length = calloc(MAX_SYMBOLS, sizeof(unsigned));
unsigned *temp = calloc(MAX_HUFCODE_BITS + 1, sizeof(unsigned));
size_t minLen;
size_t maxLen;
int pp;
#if defined(__M2__)
int int_array = sizeof(int);
int group_data_array = sizeof(struct group_data);
#else
int int_array = 1;
int group_data_array = 1;
#endif
size_t hold;
// Read in header signature and CRC (which is stored big endian)
ii = get_bits(bd, 24);
jj = get_bits(bd, 24);
bw->headerCRC = get_bits(bd, 32);
// Is this the EOF block with CRC for whole file? (Constant is "e")
if(ii == 0x177245 && jj == 0x385090)
{
free(length);
free(temp);
return RETVAL_LAST_BLOCK;
}
// Is this a valid data block? (Constant is "pi".)
if(ii != 0x314159 || jj != 0x265359)
{
return RETVAL_NOT_BZIP_DATA;
}
// We can add support for blockRandomised if anybody complains.
if(get_bits(bd, 1))
{
return RETVAL_OBSOLETE_INPUT;
}
if((bw->origPtr = get_bits(bd, 24)) > bd->dbufSize)
{
return RETVAL_DATA_ERROR;
}
// mapping table: if some byte values are never used (encoding things
// like ascii text), the compression code removes the gaps to have fewer
// symbols to deal with, and writes a sparse bitfield indicating which
// values were present. We make a translation table to convert the symbols
// back to the corresponding bytes.
hh = get_bits(bd, 16);
bd->symTotal = 0;
for(ii = 0; ii < 16; ii += 1)
{
if(hh & (1 << (15 - ii)))
{
kk = get_bits(bd, 16);
for(jj = 0; jj < 16; jj += 1)
{
if(kk & (1 << (15 - jj)))
{
bd->symToByte[bd->symTotal] = (16 * ii) + jj;
bd->symTotal += 1;
}
}
}
}
// How many different huffman coding groups does this block use?
bd->groupCount = get_bits(bd, 3);
if(bd->groupCount < 2 || bd->groupCount > MAX_GROUPS)
{
return RETVAL_DATA_ERROR;
}
// nSelectors: Every GROUP_SIZE many symbols we switch huffman coding
// tables. Each group has a selector, which is an index into the huffman
// coding table arrays.
//
// Read in the group selector array, which is stored as MTF encoded
// bit runs. (MTF = Move To Front. Every time a symbol occurs its moved
// to the front of the table, so it has a shorter encoding next time.)
if(!(bd->nSelectors = get_bits(bd, 15)))
{
return RETVAL_DATA_ERROR;
}
for(ii = 0; ii < bd->groupCount; ii += 1)
{
bd->mtfSymbol[ii] = ii;
}
for(ii = 0; ii < bd->nSelectors; ii += 1)
{
// Get next value
for(jj = 0; get_bits(bd, 1); jj += 1)
if(jj >= bd->groupCount)
{
return RETVAL_DATA_ERROR;
}
// Decode MTF to get the next selector, and move it to the front.
uc = bd->mtfSymbol[jj];
while(jj)
{
jj = jj - 1;
bd->mtfSymbol[jj + 1] = bd->mtfSymbol[jj];
}
bd->mtfSymbol[0] = bd->selectors[ii] = uc;
}
// Read the huffman coding tables for each group, which code for symTotal
// literal symbols, plus two run symbols (RUNA, RUNB)
symCount = bd->symTotal + 2;
for(jj = 0; jj < bd->groupCount; jj += 1)
{
// Read lengths
hh = get_bits(bd, 5);
for(ii = 0; ii < symCount; ii += 1)
{
while(TRUE)
{
// !hh || hh > MAX_HUFCODE_BITS in one test.
if(MAX_HUFCODE_BITS - 1 < hh - 1)
{
return RETVAL_DATA_ERROR;
}
// Grab 2 bits instead of 1 (slightly smaller/faster). Stop if
// first bit is 0, otherwise second bit says whether to
// increment or decrement.
kk = get_bits(bd, 2);
if(kk & 2)
{
hh += (1 - ((kk & 1) << 1));
}
else
{
bd->inbufBitCount += 1;
break;
}
}
length[ii] = hh;
}
// Find largest and smallest lengths in this group
minLen = maxLen = length[0];
for(ii = 1; ii < symCount; ii += 1)
{
hold = length[ii];
if(hold > maxLen)
{
maxLen = hold;
}
else if(hold < minLen)
{
minLen = hold;
}
}
/* Calculate permute[], base[], and limit[] tables from length[].
*
* permute[] is the lookup table for converting huffman coded symbols
* into decoded symbols. It contains symbol values sorted by length.
*
* base[] is the amount to subtract from the value of a huffman symbol
* of a given length when using permute[].
*
* limit[] indicates the largest numerical value a symbol with a given
* number of bits can have. It lets us know when to stop reading.
*
* To use these, keep reading bits until value <= limit[bitcount] or
* youve read over 20 bits (error). Then the decoded symbol
* equals permute[hufcode_value - base[hufcode_bitcount]].
*/
hufGroup = bd->groups + (group_data_array * jj);
require(minLen > 0, "hufGroup minLen can't have negative values\n");
require(minLen <= MAX_HUFCODE_BITS, "hufGroup minLen can't exceed MAX_HUFCODE_BITS\n");
hufGroup->minLen = minLen;
require(maxLen > 0, "hufGroup maxLen can't have negative values\n");
require(maxLen <= MAX_HUFCODE_BITS, "hufGroup maxLen can't exceed MAX_HUFCODE_BITS\n");
hufGroup->maxLen = maxLen;
// Note that minLen cant be smaller than 1, so we adjust the base
// and limit array pointers so were not always wasting the first
// entry. We do this again when using them (during symbol decoding).
base = hufGroup->base - (int_array * 1);
require(0 <= base, "can't have a negative hufGroup->base\n");
limit = hufGroup->limit - (int_array * 1);
// zero temp[] and limit[], and calculate permute[]
pp = 0;
for(ii = minLen; ii <= maxLen; ii += 1)
{
require(MAX_HUFCODE_BITS >= ii, "Invalid HUFCODE_BITS length\n");
temp[ii] = 0;
limit[ii] = 0;
for(hh = 0; hh < symCount; hh += 1)
{
if(length[hh] == ii)
{
require(MAX_SYMBOLS >= pp, "pp exceeded MAX_SYMBOLS\n");
hufGroup->permute[pp] = hh;
pp += 1;
}
}
}
// Count symbols coded for at each bit length
for(ii = 0; ii < symCount; ii += 1)
{
hold = length[ii];
require(MAX_HUFCODE_BITS >= hold, "Invalid HUFCODE_BITS length\n");
temp[hold] += 1;
}
/* Calculate limit[] (the largest symbol-coding value at each bit
* length, which is (previous limit<<1)+symbols at this level), and
* base[] (number of symbols to ignore at each bit length, which is
* limit minus the cumulative count of symbols coded for already). */
pp = hh = 0;
for(ii = minLen; ii < maxLen; ii += 1)
{
pp += temp[ii];
limit[ii] = pp - 1;
pp = pp << 1;
hh += temp[ii];
base[ii + 1] = pp - hh;
}
limit[maxLen] = pp + temp[maxLen] - 1;
limit[maxLen + 1] = INT_MAX;
base[minLen] = 0;
}
free(length);
free(temp);
return 0;
}
/* First pass, read blocks symbols into dbuf[dbufCount].
*
* This undoes three types of compression: huffman coding, run length encoding,
* and move to front encoding. We have to undo all those to know when weve
* read enough input.
*/
int read_huffman_data(struct bunzip_data *bd, struct bwdata *bw)
{
struct group_data *hufGroup;
int ii;
int jj;
int kk;
int runPos;
int dbufCount;
int symCount;
int selector;
int nextSym;
int *byteCount;
int *base;
int *limit;
unsigned hh;
unsigned *dbuf = bw->dbuf;
unsigned uc;
#if defined(__M2__)
int int_array = sizeof(int);
int group_data_array = sizeof(struct group_data);
#else
int int_array = 1;
int group_data_array = 1;
#endif
// Weve finished reading and digesting the block header. Now read this
// blocks huffman coded symbols from the file and undo the huffman coding
// and run length encoding, saving the result into dbuf[dbufCount++] = uc
// Initialize symbol occurrence counters and symbol mtf table
byteCount = bw->byteCount;
for(ii = 0; ii < 256; ii += 1)
{
byteCount[ii] = 0;
bd->mtfSymbol[ii] = ii;
}
// Loop through compressed symbols. This is the first "tight inner loop"
// that needs to be micro-optimized for speed. (This one fills out dbuf[]
// linearly, staying in cache more, so isnt as limited by DRAM access.)
runPos = 0;
dbufCount = 0;
symCount = 0;
selector = 0;
// Some unnecessary initializations to shut gcc up.
base = 0;
limit = 0;
hufGroup = 0;
hh = 0;
while(TRUE)
{
// Have we reached the end of this huffman group?
if(!(symCount))
{
// Determine which huffman coding group to use.
symCount = GROUP_SIZE - 1;
if(selector >= bd->nSelectors)
{
return RETVAL_DATA_ERROR;
}
hufGroup = bd->groups + (group_data_array * bd->selectors[selector]);
selector += 1;
base = hufGroup->base - (int_array * 1);
require(0 <= base, "can't have negative hufGroup->base\n");
limit = hufGroup->limit - (int_array * 1);
}
else
{
symCount -= 1;
}
// Read next huffman-coded symbol (into jj).
ii = hufGroup->minLen;
jj = get_bits(bd, ii);
while(jj > limit[ii])
{
// if (ii > hufGroup->maxLen) return RETVAL_DATA_ERROR;
ii += 1;
// Unroll get_bits() to avoid a function call when the datas in
// the buffer already.
if(bd->inbufBitCount)
{
bd->inbufBitCount -= 1;
kk = (bd->inbufBits >> bd->inbufBitCount) & 1;
}
else
{
kk = get_bits(bd, 1);
}
jj = (jj << 1) | kk;
}
// Huffman decode jj into nextSym (with bounds checking)
jj -= base[ii];
if(ii > hufGroup->maxLen || jj >= MAX_SYMBOLS)
{
return RETVAL_DATA_ERROR;
}
nextSym = hufGroup->permute[jj];
// If this is a repeated run, loop collecting data
if(nextSym <= SYMBOL_RUNB)
{
// If this is the start of a new run, zero out counter
if(!runPos)
{
runPos = 1;
hh = 0;
}
/* Neat trick that saves 1 symbol: instead of or-ing 0 or 1 at
each bit position, add 1 or 2 instead. For example,
1011 is 1<<0 + 1<<1 + 2<<2. 1010 is 2<<0 + 2<<1 + 1<<2.
You can make any bit pattern that way using 1 less symbol than
the basic or 0/1 method (except all bits 0, which would use no
symbols, but a run of length 0 doesnt mean anything in this
context). Thus space is saved. */
hh += (runPos << nextSym); // +runPos if RUNA; +2*runPos if RUNB
runPos = runPos << 1;
continue;
}
/* When we hit the first non-run symbol after a run, we now know
how many times to repeat the last literal, so append that many
copies to our buffer of decoded symbols (dbuf) now. (The last
literal used is the one at the head of the mtfSymbol array.) */
if(runPos)
{
runPos = 0;
// Check for integer overflow
if(hh > bd->dbufSize || dbufCount + hh > bd->dbufSize)
{
return RETVAL_DATA_ERROR;
}
uc = bd->symToByte[bd->mtfSymbol[0]];
byteCount[uc] += hh;
while(hh)
{
hh -= 1;
dbuf[dbufCount] = uc;
dbufCount += 1;
}
}
// Is this the terminating symbol?
if(nextSym > bd->symTotal)
{
break;
}
/* At this point, the symbol we just decoded indicates a new literal
character. Subtract one to get the position in the MTF array
at which this literal is currently to be found. (Note that the
result cant be -1 or 0, because 0 and 1 are RUNA and RUNB.
Another instance of the first symbol in the mtf array, position 0,
would have been handled as part of a run.) */
if(dbufCount >= bd->dbufSize)
{
return RETVAL_DATA_ERROR;
}
ii = nextSym - 1;
uc = bd->mtfSymbol[ii];
// On my laptop, unrolling this memmove() into a loop shaves 3.5% off
// the total running time.
while(ii)
{
ii -= 1;
bd->mtfSymbol[ii + 1] = bd->mtfSymbol[ii];
}
bd->mtfSymbol[0] = uc;
uc = bd->symToByte[uc];
// We have our literal byte. Save it into dbuf.
byteCount[uc] += 1;
dbuf[dbufCount] = uc;
dbufCount += 1;
}
// Now we know what dbufCount is, do a better sanity check on origPtr.
if(bw->origPtr >= (bw->writeCount = dbufCount))
{
return RETVAL_DATA_ERROR;
}
return 0;
}
// Flush output buffer to disk
void flush_bunzip_outbuf(struct bunzip_data *bd, int out_fd)
{
if(bd->outbufPos)
{
if(write(out_fd, bd->outbuf, bd->outbufPos) != bd->outbufPos)
{
exit(1);
}
bd->outbufPos = 0;
}
}
void burrows_wheeler_prep(struct bunzip_data *bd, struct bwdata *bw)
{
int ii;
int jj;
int kk;
unsigned *dbuf = bw->dbuf;
int *byteCount = bw->byteCount;
unsigned uc;
// Turn byteCount into cumulative occurrence counts of 0 to n-1.
jj = 0;
for(ii = 0; ii < 256; ii += 1)
{
kk = jj + byteCount[ii];
byteCount[ii] = jj;
jj = kk;
}
// Use occurrence counts to quickly figure out what order dbuf would be in
// if we sorted it.
for(ii = 0; ii < bw->writeCount; ii += 1)
{
uc = dbuf[ii] & 0xFF;
dbuf[byteCount[uc]] = dbuf[byteCount[uc]] | (ii << 8);
byteCount[uc] += 1;
}
// blockRandomised support would go here.
// Using ii as position, jj as previous character, hh as current character,
// and uc as run count.
bw->dataCRC = 0xffffffff;
/* Decode first byte by hand to initialize "previous" byte. Note that it
doesnt get output, and if the first three characters are identical
it doesnt qualify as a run (hence uc=255, which will either wrap
to 1 or get reset). */
if(bw->writeCount)
{
bw->writePos = dbuf[bw->origPtr];
bw->writeCurrent = bw->writePos;
bw->writePos = bw->writePos >> 8;
bw->writeRun = -1;
}
}
// Decompress a block of text to intermediate buffer
int read_bunzip_data(struct bunzip_data *bd)
{
int rc = read_block_header(bd, bd->bwdata);
if(!rc)
{
rc = read_huffman_data(bd, bd->bwdata);
}
// First thing that can be done by a background thread.
burrows_wheeler_prep(bd, bd->bwdata);
return rc;
}
// Undo burrows-wheeler transform on intermediate buffer to produce output.
// If !len, write up to len bytes of data to buf. Otherwise write to out_fd.
// Returns len ? bytes written : 0. Notice all errors are negative #s.
//
// Burrows-wheeler transform is described at:
// http://dogma.net/markn/articles/bwt/bwt.htm
// http://marknelson.us/1996/09/01/bwt/
int write_bunzip_data(struct bunzip_data *bd, struct bwdata *bw,
int out_fd, char *outbuf, int len)
{
unsigned *dbuf = bw->dbuf;
int count;
int pos;
int current;
int run;
int copies;
int outbyte;
int previous;
int gotcount = 0;
int i;
int crc_index;
while(TRUE)
{
// If last read was short due to end of file, return last block now
if(bw->writeCount < 0)
{
return bw->writeCount;
}
// If we need to refill dbuf, do it.
if(!bw->writeCount)
{
i = read_bunzip_data(bd);
if(i)
{
if(i == RETVAL_LAST_BLOCK)
{
bw->writeCount = i;
return gotcount;
}
else
{
return i;
}
}
}
// loop generating output
count = bw->writeCount;
pos = bw->writePos;
current = bw->writeCurrent;
run = bw->writeRun;
while(count)
{
// If somebody (like tar) wants a certain number of bytes of
// data from memory instead of written to a file, humor them.
if(len && bd->outbufPos >= len)
{
goto dataus_interruptus;
}
count -= 1;
// Follow sequence vector to undo Burrows-Wheeler transform.
previous = current;
pos = dbuf[pos];
current = pos & 0xff;
pos = pos >> 8;
// Whenever we see 3 consecutive copies of the same byte,
// the 4th is a repeat count
if(run == 3)
{
run += 1;
copies = current;
outbyte = previous;
current = -1;
}
else
{
run += 1;
copies = 1;
outbyte = current;
}
// Output bytes to buffer, flushing to file if necessary
while(copies)
{
copies -= 1;
if(bd->outbufPos == IOBUF_SIZE)
{
flush_bunzip_outbuf(bd, out_fd);
}
bd->outbuf[bd->outbufPos] = outbyte;
bd->outbufPos += 1;
crc_index = ((bw->dataCRC >> 24) ^ outbyte) & 0xFF;
bw->dataCRC = (bw->dataCRC << 8) ^ bd->crc32Table[crc_index];
}
if(current != previous)
{
run = 0;
}
}
// decompression of this block completed successfully
bw->dataCRC = ~(bw->dataCRC);
#if defined(__M2__)
// & 0xFFFFFFFF not working
if(sizeof(unsigned) == 8)
{
bw->dataCRC <<= 32;
bw->dataCRC >>= 32;
}
#endif
bd->totalCRC = ((bd->totalCRC << 1) | (bd->totalCRC >> 31)) ^ bw->dataCRC;
// if this block had a crc error, force file level crc error.
if(bw->dataCRC != bw->headerCRC)
{
bd->totalCRC = bw->headerCRC + 1;
return RETVAL_LAST_BLOCK;
}
dataus_interruptus:
bw->writeCount = count;
if(len)
{
gotcount += bd->outbufPos;
memcpy(outbuf, bd->outbuf, len);
// If we got enough data, checkpoint loop state and return
len -= bd->outbufPos;
if(len < 1)
{
bd->outbufPos -= len;
if(bd->outbufPos)
{
memmove(bd->outbuf, bd->outbuf + len, bd->outbufPos);
}
bw->writePos = pos;
bw->writeCurrent = current;
bw->writeRun = run;
return gotcount;
}
}
}
}
// Allocate the structure, read file header. If !len, src_fd contains
// filehandle to read from. Else inbuf contains data.
int start_bunzip(struct bunzip_data **bdp, int src_fd)
{
struct bunzip_data *bd;
unsigned i;
// Figure out how much data to allocate.
i = sizeof(struct bunzip_data);
// Allocate bunzip_data. Most fields initialize to zero.
*bdp = malloc(i);
bd = *bdp;
memset(bd, 0, i);
bd->inbuf = calloc(IOBUF_SIZE, sizeof(char));
bd->outbuf = calloc(IOBUF_SIZE, sizeof(char));
bd->selectors = calloc(32768, sizeof(char));
bd->groups = calloc(MAX_GROUPS, sizeof(struct group_data));
for(i = 0; i < MAX_GROUPS; i += 1)
{
bd->groups[i].limit = calloc(MAX_HUFCODE_BITS + 1, sizeof(int));
bd->groups[i].base = calloc(MAX_HUFCODE_BITS, sizeof(int));
bd->groups[i].permute = calloc(MAX_SYMBOLS, sizeof(int));
}
bd->symToByte = calloc(256, sizeof(unsigned));
bd->mtfSymbol = calloc(256, sizeof(unsigned));
bd->crc32Table = calloc(256, sizeof(unsigned));
bd->bwdata = calloc(1, sizeof(struct bwdata));
bd->bwdata->byteCount = calloc(256, sizeof(int));
unsigned *crc32Table;
bd->in_fd = src_fd;
crc_init(bd->crc32Table, 0);
// Ensure that file starts with "BZh".
char *header = "BZh";
for(i = 0; i < 3; i += 1) if(get_bits(bd, 8) != header[i])
{
return RETVAL_NOT_BZIP_DATA;
}
// Next byte ascii 1-9, indicates block size in units of 100k of
// uncompressed data. Allocate intermediate buffer for block.
i = get_bits(bd, 8);
if(i < 49 || i > 57)
{
return RETVAL_NOT_BZIP_DATA;
}
bd->dbufSize = 100000 * (i - 48);
bd->bwdata[0].dbuf = malloc(bd->dbufSize * sizeof(int));
return 0;
}
// Example usage: decompress src_fd to dst_fd. (Stops at end of bzip data,
// not end of file.)
int bunzipStream(int src_fd, int dst_fd)
{
struct bunzip_data *bd;
int i;
int j;
if(!(i = start_bunzip(&bd, src_fd)))
{
i = write_bunzip_data(bd, bd->bwdata, dst_fd, 0, 0);
if(i == RETVAL_LAST_BLOCK)
{
if(bd->bwdata[0].headerCRC == bd->totalCRC)
{
i = 0;
}
else
{
i = RETVAL_DATA_ERROR;
}
}
}
flush_bunzip_outbuf(bd, dst_fd);
free(bd->bwdata[0].dbuf);
free(bd->inbuf);
free(bd->outbuf);
free(bd->selectors);
for(j = 0; j < MAX_GROUPS; j += 1)
{
free(bd->groups[j].limit);
free(bd->groups[j].base);
free(bd->groups[j].permute);
}
free(bd->groups);
free(bd->symToByte);
free(bd->mtfSymbol);
free(bd->crc32Table);
free(bd->bwdata->byteCount);
free(bd->bwdata);
free(bd);
return -i;
}
void do_bunzip2(int in_fd, int out_fd)
{
int err = bunzipStream(in_fd, out_fd);
if(err)
{
exit(1);
}
}
int main(int argc, char **argv)
{
char *name = NULL;
char *dest = NULL;
FUZZING = FALSE;
/* process arguments */
int i = 1;
while(i < argc)
{
if(NULL == argv[i])
{
i += 1;
}
else if(match(argv[i], "-f") || match(argv[i], "--file"))
{
name = argv[i + 1];
require(NULL != name, "the --file option requires a filename to be given\n");
i += 2;
}
else if(match(argv[i], "-o") || match(argv[i], "--output"))
{
dest = argv[i + 1];
require(NULL != dest, "the --output option requires a filename to be given\n");
i += 2;
}
else if(match(argv[i], "--fuzzing-mode"))
{
FUZZING = TRUE;
i += 1;
}
else if(match(argv[i], "-h") || match(argv[i], "--help"))
{
fputs("Usage: ", stderr);
fputs(argv[0], stderr);
fputs(" --file $input.bz2", stderr);
fputs(" --output $output\n", stderr);
fputs("--help to get this message\n", stderr);
exit(EXIT_SUCCESS);
}
else
{
fputs("Unknown option:", stderr);
fputs(argv[i], stderr);
fputs("\nAborting to avoid problems\n", stderr);
exit(EXIT_FAILURE);
}
}
/* Deal with no input */
if(NULL == name)
{
fputs("an input file (--file $name) must be provided\n", stderr);
exit(EXIT_FAILURE);
}
int in_fd = open(name, 0, 0);
if(in_fd < 0)
{
fputs("Unable to open input file\n", stderr);
exit(EXIT_FAILURE);
}
/* If an output name isn't provided */
if(NULL == dest)
{
int length = strlen(name);
require(length > 4, "file name length not sufficient, please provide output name with --output $filename\n");
}
int out_fd;
if(FUZZING)
{
/* Dump to /dev/null the garbage data produced during fuzzing */
out_fd = open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0600);
}
else
{
out_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, 0600);
}
if(out_fd < 0)
{
fputs("Unable to open output file for writing\n", stderr);
exit(EXIT_FAILURE);
}
do_bunzip2(in_fd, out_fd);
close(in_fd);
close(out_fd);
exit(0);
}
/* Copyright (C) 2019 Jeremiah Orians
* This file is part of mescc-tools
*
* mescc-tools is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* mescc-tools is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with mescc-tools. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
// CONSTANT BUFFER_SIZE 4096
#define BUFFER_SIZE 4096
int main(int argc, char** argv)
{
if(2 > argc)
{
fputs("catm requires 2 or more arguments\n", stderr);
exit(EXIT_FAILURE);
}
int output = open(argv[1], 577 , 384);
if(-1 == output)
{
fputs("The file: ", stderr);
fputs(argv[1], stderr);
fputs(" is not a valid output file name\n", stderr);
exit(EXIT_FAILURE);
}
int i;
int bytes;
char* buffer = calloc(BUFFER_SIZE + 1, sizeof(char));
int input;
for(i = 2; i < argc ; i = i + 1)
{
input = open(argv[i], 0, 0);
if(-1 == input)
{
fputs("The file: ", stderr);
fputs(argv[i], stderr);
fputs(" is not a valid input file name\n", stderr);
exit(EXIT_FAILURE);
}
keep:
bytes = read(input, buffer, BUFFER_SIZE);
write(output, buffer, bytes);
if(BUFFER_SIZE == bytes) goto keep;
}
free(buffer);
return EXIT_SUCCESS;
}
/* Copyright (C) 2020 fosslinux
* This file is part of mescc-tools
*
* mescc-tools is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* mescc-tools is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with mescc-tools. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include "M2libc/bootstrappable.h"
/* Define all of the constants */
// CONSTANT FALSE 0
#define FALSE 0
// CONSTANT TRUE 1
#define TRUE 1
// CONSTANT MAX_STRING 4096
#define MAX_STRING 4096
// CONSTANT MAX_ARRAY 256
#define MAX_ARRAY 256
/* Prototypes for external funcs */
/* Globals */
int verbose;
/* UTILITY FUNCTIONS */
/* Function to find a character's position in a string (last match) */
int find_last_char_pos(char* string, char a)
{
int i = strlen(string) - 1;
if(i < 0) return i;
while(i >= 0)
{
/*
* This conditional should be in the while conditional but we are
* running into the M2-Planet short-circuit bug.
*/
if(a == string[i]) break;
i = i - 1;
}
return i;
}
/* Function to find the length of a char**; an array of strings */
int array_length(char** array)
{
int length = 0;
while(array[length] != NULL)
{
length = length + 1;
}
return length;
}
/* PROCESSING FUNCTIONS */
char* directory_dest(char* dest, char* source, int require_directory)
{
/*
* First, check if it is a directory to copy to.
* We have two ways of knowing this:
* - If the destination ends in a slash, the user has explicitly said
* it is a directory.
* - Normally we would use stat() but we don't want to force support for
* that syscall onto the kernel, so we just attempt to chdir() into it
* and if it works then it must be a directory. A bit hacky, bit it
* works.
*/
int isdirectory = FALSE;
if(dest[strlen(dest) - 1] == '/')
{
isdirectory = TRUE;
}
if(!isdirectory)
{ /* Use the other testing method */
/*
* Get the current path so that we can chdir back to it if it does
* chdir successfully.
*/
char* current_path = calloc(MAX_STRING, sizeof(char));
require(current_path != NULL, "Memory initialization of current_path in directory_dest failed\n");
getcwd(current_path, MAX_STRING);
require(!match("", current_path), "getcwd() failed\n");
/*
* chdir expects an absolute path.
* If the first character is / then it is already absolute, otherwise
* it is relative and needs to be changed (by appending current_path
* to the dest path).
*/
char* chdir_dest = calloc(MAX_STRING, sizeof(char));
require(chdir_dest != NULL, "Memory initialization of chdir_dest in directory_dest failed\n");
if(dest[0] != '/')
{ /* The path is relative, append current_path */
strcat(chdir_dest, current_path);
strcat(chdir_dest, "/");
strcat(chdir_dest, dest);
}
else
{ /* The path is absolute */
strcpy(chdir_dest, dest);
}
if(0 <= chdir(chdir_dest))
{ /* chdir returned successfully */
/*
* But because of M2-Planet, that dosen't mean anything actually
* happened, check that before we go any further.
*/
char* new_path = calloc(MAX_STRING, sizeof(char));
require(new_path != NULL, "Memory initialization of new_path in directory_dest failed\n");
getcwd(new_path, MAX_STRING);
if(!match(current_path, new_path))
{
isdirectory = TRUE;
chdir(current_path);
}
}
free(chdir_dest);
free(current_path);
}
/*
* If it isn't a directory, and we require one, error out.
* Otherwise, just return what we were given, we're done here.
*/
if(require_directory) require(isdirectory, "Provide a directory destination for multiple source files\n");
if(!isdirectory) return dest;
/* If it is, we need to make dest a full path */
/* 1. Get the basename of source */
char* basename = calloc(MAX_STRING, sizeof(char));
require(basename != NULL, "Memory initialization of basename in directory_dest failed\n");
int last_slash_pos = find_last_char_pos(source, '/');
if(last_slash_pos >= 0)
{ /* Yes, there is a slash in it, copy over everything after that pos */
unsigned spos; /* source pos */
unsigned bpos = 0; /* basename pos */
/* Do the actual copy */
for(spos = last_slash_pos + 1; spos < strlen(source); spos = spos + 1)
{
basename[bpos] = source[spos];
bpos = bpos + 1;
}
}
else
{ /* No, there is no slash in it, hence the basename is just the source */
strcpy(basename, source);
}
/* 2. Ensure our dest (which is a directory) has a trailing slash */
if(dest[strlen(dest) - 1] != '/')
{
strcat(dest, "/");
}
/* 3. Add the basename to the end of the directory */
strcat(dest, basename);
free(basename);
/* Now we have a returnable path! */
return dest;
}
void copy_file(char* source, char* dest)
{
if(verbose)
{ /* Output message */
/* Of the form 'source' -> 'dest' */
fputs("'", stdout);
fputs(source, stdout);
fputs("' -> '", stdout);
fputs(dest, stdout);
fputs("'\n", stdout);
}
/* Open source and dest as FILE*s */
FILE* fsource = fopen(source, "r");
if(fsource == NULL)
{
fputs("Error opening source file ", stderr);
fputs(source, stderr);
fputc('\n', stderr);
exit(EXIT_FAILURE);
}
FILE* fdest = fopen(dest, "w");
if(fdest < 0)
{
fputs("Error opening destination file", stderr);
fputs(dest, stderr);
fputc('\n', stderr);
exit(EXIT_FAILURE);
}
/*
* The following loop reads a character from the source and writes it to the
* dest file. This is all M2-Planet supports.
*/
int c = fgetc(fsource);
while(c != EOF)
{
fputc(c, fdest);
c = fgetc(fsource);
}
/* Cleanup */
fclose(fsource);
fclose(fdest);
}
int main(int argc, char** argv)
{
/* Initialize variables */
char** sources = calloc(MAX_ARRAY, sizeof(char*));
require(sources != NULL, "Memory initialization of sources failed\n");
int sources_index = 0;
char* dest = NULL;
/* Set defaults */
verbose = FALSE;
int i = 1;
int j;
int args_found;
/* Loop arguments */
while(i <= argc)
{
if(NULL == argv[i])
{ /* Ignore and continue */
i = i + 1;
}
else if(match(argv[i], "-h") || match(argv[i], "--help"))
{
fputs("Usage: ", stdout);
fputs(argv[0], stdout);
fputs(" [-h | --help] [-V | --version] [-v | --verbose] source1 source2 sourcen destination\n", stdout);
exit(EXIT_SUCCESS);
}
else if(match(argv[i], "-V") || match(argv[i], "--version"))
{ /* Output version */
fputs("cp version 1.2.1\n", stdout);
exit(EXIT_SUCCESS);
}
else if(match(argv[i], "-v") || match(argv[i], "--verbose"))
{
verbose = TRUE;
i = i + 1;
}
else if(argv[i][0] != '-')
{ /* It is not an option */
/*
* We can tell if this is the source file or the destination file
* through looking *ahead*. If it is the last of this type of argument then
* it must be the destination. (1 destination, many sources).
*/
j = i + 1;
args_found = 0;
while(j < array_length(argv))
{
if(argv[j][0] != '-')
{ /* It's one of these type of arguments */
args_found = args_found + 1;
}
j = j + 1;
}
if(args_found == 0)
{ /* We are setting the destination (there are no more left after this) */
dest = calloc(MAX_STRING, sizeof(char));
require(dest != NULL, "Memory initialization of dest failed\n");
strcpy(dest, argv[i]);
}
else
{ /* We are setting a source */
require(sources_index < MAX_ARRAY, "Too many files\n");
sources[sources_index] = calloc(MAX_STRING, sizeof(char));
require(sources[sources_index] != NULL, "Memory initialization of sources[source_index] failed\n");
strcpy(sources[sources_index], argv[i]);
sources_index = sources_index + 1;
}
i = i + 1;
}
else
{ /* Unknown argument */
fputs("UNKNOWN_ARGUMENT\n", stderr);
exit(EXIT_FAILURE);
}
}
/* Sanitize values */
/* Ensure the two values have values */
/* Another workaround for short-circuit bug */
int error = FALSE;
if(sources[0] == NULL) error = TRUE;
if(error == FALSE) if(match(sources[0], "")) error = TRUE;
require(!error, "Provide a source file\n");
error = FALSE;
if(dest == NULL) error = TRUE;
if(error == FALSE) if(match(dest, "")) error = TRUE;
require(!error, "Provide a destination file\n");
/* Loop through all of the sources, copying each one */
char* this_dest;
for(i = 0; i < array_length(sources); i = i + 1)
{
/* Convert the dest variable to a full path if it's a directory copying to */
/*
* Also, if there is more than one source, we have to be copying to
* a directory destination...
*/
if(array_length(sources) == 1)
{
dest = directory_dest(dest, sources[i], FALSE);
copy_file(sources[i], dest);
}
else
{
this_dest = calloc(MAX_STRING, sizeof(char));
require(this_dest != NULL, "Memory initalization of this_dest failed\n");
this_dest = directory_dest(dest, sources[i], TRUE);
copy_file(sources[i], this_dest);
}
/* Perform the actual copy */
free(sources[i]);
}
free(sources);
free(dest);
return EXIT_SUCCESS;
}
/* Copyright (C) 2020 fosslinux
* This file is part of mescc-tools
*
* mescc-tools is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* mescc-tools is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with mescc-tools. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "M2libc/bootstrappable.h"
/* Define all of the constants */
// CONSTANT FALSE 0
#define FALSE 0
// CONSTANT TRUE 1
#define TRUE 1
// CONSTANT MAX_STRING 4096
#define MAX_STRING 4096
// CONSTANT MAX_ARRAY 256
#define MAX_ARRAY 256
struct files
{
char* name;
struct files* next;
};
/* Globals */
int verbose;
/* PROCESSING FUNCTIONS */
int main(int argc, char** argv)
{
/* Initialize variables */
char* mode = NULL;
struct files* f = NULL;
struct files* n;
int ok;
/* Set defaults */
verbose = FALSE;
int i = 1;
/* Loop arguments */
while(i <= argc)
{
if(NULL == argv[i])
{ /* Ignore and continue */
i = i + 1;
}
else if(match(argv[i], "-h") || match(argv[i], "--help"))
{
fputs("Usage: ", stdout);
fputs(argv[0], stdout);
fputs(" [-h | --help] [-V | --version] [-v | --verbose]\n", stdout);
exit(EXIT_SUCCESS);
}
else if(match(argv[i], "-V") || match(argv[i], "--version"))
{ /* Output version */
fputs("chmod version 1.2.1\n", stdout);
exit(EXIT_SUCCESS);
}
else if(match(argv[i], "-v") || match(argv[i], "--verbose"))
{
verbose = TRUE;
i = i + 1;
}
else
{ /* It must be the file or the mode */
if(mode == NULL)
{ /* Mode always comes first */
mode = calloc(MAX_STRING, sizeof(char));
require(mode != NULL, "Memory initialization of mode failed\n");
/* We need to indicate it is octal */
strcat(mode, "0");
strcat(mode, argv[i]);
}
else
{ /* It's a file, as the mode is already done */
n = calloc(1, sizeof(struct files));
require(n != NULL, "Memory initialization of files failed\n");
n->next = f;
f = n;
f->name = argv[i];
}
i = i + 1;
}
}
/* Ensure the two values have values */
require(mode != NULL, "Provide a mode\n");
require(f != NULL, "Provide a file\n");
/* Convert the mode str into octal */
int omode = strtoint(mode);
/* Loop over files to be operated on */
while(NULL != f)
{
/* Make sure the file can be opened */
ok = access(f->name, 0);
if(ok != 0)
{
fputs("The file: ", stderr);
fputs(f->name, stderr);
fputs(" does not exist\n", stderr);
exit(EXIT_FAILURE);
}
/* Verbose message */
if(verbose)
{
fputs("mode of '", stdout);
fputs(f->name, stdout);
fputs("' changed to ", stdout);
fputs(mode, stdout);
fputs("\n", stdout);
}
/* Perform the chmod */
chmod(f->name, omode);
f = f->next;
}
}
/* Copyright (C) 2021 Jeremiah Orians
* This file is part of mescc-tools-extra
*
* mescc-tools-extra is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* mescc-tools-extra is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with mescc-tools-extra. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* "rm" can be used to delete files. It can also delete
* parent directories.
*
* Usage: rm <dir1>/<file1> <file2>
*
* These are all highly standard and portable headers. */
#include <stdio.h>
#include <string.h>
/* This is for unlink() ; this may need to be changed for some platforms. */
#include <unistd.h> /* For unlink() */
#include <stdlib.h>
#include "M2libc/bootstrappable.h"
void delete_dir(char* name)
{
int r = unlink(name);
if(0 != r)
{
fputs("unable to delete file: ", stderr);
fputs(name, stderr);
fputs(" !!!\n", stderr);
}
}
int main(int argc, char **argv)
{
int i;
for(i = 1; argc > i; i = i + 1)
{
delete_dir(argv[i]);
}
return 0;
}
/* Copyright (C) 2019 Jeremiah Orians
* This file is part of mescc-tools
*
* mescc-tools is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* mescc-tools is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with mescc-tools. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "M2libc/bootstrappable.h"
char* input_name;
FILE* input;
char* output_name;
FILE* output;
char* pattern;
size_t pattern_length;
char* replacement;
char* buffer;
size_t buffer_index;
char* hold;
void read_next_byte()
{
int c= hold[0];
size_t i = 0;
while(i < pattern_length)
{
hold[i] = hold[i+1];
i = i + 1;
}
hold[pattern_length-1] = buffer[buffer_index];
buffer_index = buffer_index + 1;
/* NEVER WRITE NULLS!!! */
if(0 != c) fputc(c, output);
}
void clear_hold()
{
/* FILL hold with NULLS */
size_t i = 0;
while(i < pattern_length)
{
hold[i] = 0;
i = i + 1;
}
}
void check_match()
{
/* Do the actual replacing */
if(match(pattern, hold))
{
fputs(replacement, output);
clear_hold();
}
}
int main(int argc, char** argv)
{
output_name = "/dev/stdout";
pattern = NULL;
replacement = NULL;
buffer_index = 0;
int i = 1;
while (i < argc)
{
if(NULL == argv[i])
{
i = i + 1;
}
else if(match(argv[i], "-f") || match(argv[i], "--file"))
{
input_name = argv[i+1];
require(NULL != input_name, "the --file option requires a filename to be given\n");
i = i + 2;
}
else if(match(argv[i], "-o") || match(argv[i], "--output"))
{
output_name = argv[i+1];
require(NULL != output_name, "the --output option requires a filename to be given\n");
i = i + 2;
}
else if(match(argv[i], "-m") || match(argv[i], "--match-on"))
{
pattern = argv[i+1];
require(NULL != pattern, "the --match-on option requires a string to be given\n");
i = i + 2;
}
else if(match(argv[i], "-r") || match(argv[i], "--replace-with"))
{
replacement = argv[i+1];
require(NULL != replacement, "the --replace-with option requires a string to be given\n");
i = i + 2;
}
else if(match(argv[i], "-h") || match(argv[i], "--help"))
{
fputs("Usage: ", stderr);
fputs(argv[0], stderr);
fputs(" --file $input", stderr);
fputs(" --match-on $string", stderr);
fputs(" --replace-with $string", stderr);
fputs(" [--output $output] (or it'll dump to stdout)\n", stderr);
fputs("--help to get this message\n", stderr);
exit(EXIT_SUCCESS);
}
else
{
fputs("Unknown option:", stderr);
fputs(argv[i], stderr);
fputs("\nAborting to avoid problems\n", stderr);
exit(EXIT_FAILURE);
}
}
/* Sanity check that we got everything we need */
require(NULL != input_name, "You need to pass an input file with --file\n");
require(NULL != output_name, "You need to pass an output file with --output\n");
require(NULL != pattern, "You can't do a replacement without something to match on\n");
require(NULL != replacement, "You can't do a replacement without something to replace it with\n");
input = fopen(input_name, "r");
require(NULL != input, "unable to open requested input file!\n");
/* Get enough buffer to read it all */
fseek(input, 0, SEEK_END);
size_t size = ftell(input);
buffer = malloc((size + 8) * sizeof(char));
/* Save ourself work if the input file is too small */
pattern_length = strlen(pattern);
require(pattern_length < size, "input file is to small for pattern\n");
/* Now read it all into buffer */
fseek(input, 0, SEEK_SET);
size_t r = fread(buffer,sizeof(char), size, input);
require(r == size, "incomplete read of input\n");
fclose(input);
/* Now we can safely open the output (which could have been the same as the input */
output = fopen(output_name, "w");
require(NULL != input, "unable to open requested output file!\n");
/* build our match buffer */
hold = calloc(pattern_length + 4, sizeof(char));
require(NULL != hold, "temp memory allocation failed\n");
/* Replace it all */
while((size + pattern_length + 4) >= buffer_index)
{
read_next_byte();
check_match();
}
fclose(output);
}
275d135eea485d900553def672a53a1d3c627010660da3e5834658154bc9b8e6 x86/bin/blood-elf dee788ac575fbaea8695c5f2f79fda5593fa23b5aae14420b74df0a6fa15be45 x86/bin/catm 7a2bd22b80a88d28d88df2193d71d506dbd09713b412287a71449ef8f727d90a x86/bin/chmod 7b4e27df73125e37b2ca6fac8fec5ad14a6ffe3d9efdebbddeb675c59175ca25 x86/bin/cp 941863a4dc7188c44a38cf312b7259f4cd32dfe1b75775db108dd09ccc1595ca x86/bin/get_machine 92b2414feba8744bc41ad4b165004ce42a4c47208094868d0b60e9ac6cdf229a x86/bin/hex2 967caba7eb0f3223d685f67341e0df072c9700fed3dd61e892a3cce1f4404cb4 x86/bin/kaem b7e7e05714748e919e2ec8f985afd4e5c8a08dca6384f3179721a2ca5845a57a x86/bin/M1 f8c09c42c203fb21c3bb8ff6537df7864b85007a72380bd06cd1bfb5acd92588 x86/bin/M2-Mesoplanet e33eb9c487d1e34bcca70e0d55c158cc24ed5b671fba72db8102182daa0d35a8 x86/bin/M2-Planet 481ad71972d5fece4de35dd2c357910d3740fbd5d18bcab1ae0353fcde45d846 x86/bin/match 1e3f266c93d56e4ee05fec8422f92e9261c674a09b22efa31b4198172be07b6f x86/bin/mkdir 47bcca1d7f9a07df1256c6372fbcd14afb98b0e53f4cd3a42eb73bcef1468aa2 x86/bin/replace 91d2a629a99f9b23c66acdc5f156a21bf1b26306031ca2ad3da4869e142d29d8 x86/bin/rm 130cdb574ea4e78b703182387d610d27bff3fb5bc9f19c8f6a22d3b88e47a796 x86/bin/sha256sum c43dc403413cdd11e5d10159219a6984c3571fd3bc025f23123257b6d03641b5 x86/bin/ungz caf4adc1d50b35147f16a03cbca4b45339b11d3aec260d13eb21050185438d3a x86/bin/unbz2 b8339525443c02be31b0b6d55cbc0cdebb7921ce641eff1596800a1be1a874e5 x86/bin/untar
/*
* SPDX-FileCopyrightText: 2022 fosslinux <fosslinux@aussies.space>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <bootstrappable.h>
#include <unistd.h>
#define MAX_STRING 4096
#define MAX_TOKENS 3
char *get_distfiles(char **envp) {
char *envvar = "distfiles=";
int i = 0;
while (envp[i] != NULL && strncmp(envp[i], envvar, strlen(envvar)) != 0) i += 1;
// Now we have distfiles= - get just the part we want.
require(envp[i] != NULL, "Unable to find distfiles environment variable");
return envp[i] + strlen(envvar);
}
int main(int argc, char **argv, char **envp) {
// Random file things
require(argc == 2, "Usage: checksum-transcriber FILENAME");
char *input = argv[1];
FILE *in = fopen(input, "r");
require(in != NULL, "File does not exist");
char *output = calloc(MAX_STRING, sizeof(char));
require(strcpy(output, input) != NULL, "Failed copying string");
require(strcat(output, ".SHA256SUM") != NULL, "Failed concating string");
FILE *out = fopen(output, "w+");
require(out != NULL, "Failed opening output file");
char *orig_line;
char *line = calloc(MAX_STRING, sizeof(char));
require(line != NULL, "Failed allocating string");
char **tokens;
char *new_line;
char *checksum;
char *filename;
int i;
fgets(line, MAX_STRING, in);
while (strlen(line) != 0) {
// Split each line into tokens
orig_line = line;
tokens = calloc(MAX_TOKENS, sizeof(char*));
i = 0;
while (i < MAX_TOKENS) {
tokens[i] = line;
new_line = strchr(line, ' ');
// Occurs when there are only two tokens
if (new_line == NULL) break;
line = new_line;
line[0] = '\0';
line += 1;
i += 1;
}
line = strchr(line, '\n');
line[0] = '\0';
// Get checksum and filename
checksum = tokens[1];
if (tokens[2] != NULL) {
filename = tokens[2];
} else {
filename = strrchr(tokens[0], '/');
filename += 1;
}
// Put it all together
fputs(checksum, out);
fputs(" ", out);
fputs(get_distfiles(envp), out);
fputc('/', out);
fputs(filename, out);
fputc('\n', out);
// Cleanup
i = 0;
free(orig_line);
free(tokens);
line = calloc(MAX_STRING, sizeof(char));
require(line != NULL, "Failed allocating string");
fgets(line, MAX_STRING, in);
}
// Clean up
fclose(in);
fclose(out);
}
216fe50dd8e70928f382d40ced1d9a2c167f7a5aafd555901194c5559af8c526 /usr/bin/checksum-transcriber
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "M2libc/bootstrappable.h"
/*
SPDX-FileCopyrightText: 2023 Richard Masters <grick23@gmail.com>
SPDX-License-Identifier: MIT
Simple Patch program.
This program is written in a subset of C called M2, which is from the
stage0-posix bootstrap project.
Example usage:
./simple-patch file_to_patch before_pattern_file after_pattern_file
*/
// function prototypes
void read_file_or_die(char *file_name, char **buffer, int *file_size);
void patch_buffer_or_die(char *patch_file_before_buffer, int patch_file_before_size,
char *before_pattern_buffer, int before_pattern_size,
char *after_pattern_buffer, int after_pattern_size,
char *patch_file_after_buffer);
void writestr_fd(int fd, char *str);
int memsame(char *search_buffer, int search_size,
char *pattern_buffer, int pattern_size);
int main(int argc, char **argv) {
char *patch_file_before_buffer;
int patch_file_before_size;
char *before_pattern_buffer;
int before_pattern_size;
char *after_pattern_buffer;
int after_pattern_size;
int patch_file_after_size;
char *patch_file_after_buffer;
int patch_file_fd;
read_file_or_die(argv[1], &patch_file_before_buffer, &patch_file_before_size);
read_file_or_die(argv[2], &before_pattern_buffer, &before_pattern_size);
read_file_or_die(argv[3], &after_pattern_buffer, &after_pattern_size);
patch_file_after_size = patch_file_before_size - before_pattern_size + after_pattern_size;
patch_file_after_buffer = calloc(patch_file_after_size, sizeof(char));
patch_buffer_or_die(patch_file_before_buffer, patch_file_before_size,
before_pattern_buffer, before_pattern_size,
after_pattern_buffer, after_pattern_size,
patch_file_after_buffer);
patch_file_fd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0);
write(patch_file_fd, patch_file_after_buffer, patch_file_after_size);
close(patch_file_fd);
return EXIT_SUCCESS;
}
void read_file_or_die(char *file_name, char **buffer, int *file_size) {
int file_fd;
int num_bytes_read;
file_fd = open(file_name, O_RDONLY, 0);
if (file_fd == -1) {
writestr_fd(2, "Could not open file: ");
writestr_fd(2, file_name);
writestr_fd(2, "\n");
exit(1);
}
// determine file size
*file_size = lseek(file_fd, 0, SEEK_END);
// go back to beginning of file
lseek(file_fd, 0, SEEK_SET);
// alloc a buffer to read the entire file
*buffer = calloc(*file_size, sizeof(char));
// read the entire patch file
num_bytes_read = read(file_fd, *buffer, *file_size);
if (num_bytes_read != *file_size) {
writestr_fd(2, "Could not read file: ");
writestr_fd(2, file_name);
writestr_fd(2, "\n");
exit(1);
}
close(file_fd);
}
void patch_buffer_or_die(char *patch_file_before_buffer, int patch_file_before_size,
char *before_pattern_buffer, int before_pattern_size,
char *after_pattern_buffer, int after_pattern_size,
char *patch_file_after_buffer) {
char *pos = patch_file_before_buffer;
int prefix_len = 0;
// look for the pattern at every offset
while (prefix_len < patch_file_before_size) {
// if we find the pattern, replace it and return
if (memsame(pos, patch_file_before_size - prefix_len, before_pattern_buffer, before_pattern_size)) {
memcpy(patch_file_after_buffer, patch_file_before_buffer, prefix_len);
memcpy(patch_file_after_buffer + prefix_len, after_pattern_buffer, after_pattern_size);
memcpy(patch_file_after_buffer + prefix_len + after_pattern_size,
patch_file_before_buffer + prefix_len + before_pattern_size,
patch_file_before_size - (prefix_len + before_pattern_size));
return;
}
pos = pos + 1;
prefix_len = prefix_len + 1;
}
/* if we don't find the pattern, something is wrong, so exit with error */
exit(1);
}
/*
Write the string to the given file descriptor.
*/
void writestr_fd(int fd, char *str) {
write(fd, str, strlen(str));
}
/*
Is the pattern located at the start of the search buffer
(and not exceeding the length of the search buffer)?
*/
int memsame(char *search_buffer, int search_size,
char *pattern_buffer, int pattern_size) {
int check_offset = 0;
if (pattern_size > search_size) {
return FALSE;
}
while (check_offset < pattern_size) {
if (search_buffer[check_offset] != pattern_buffer[check_offset]) {
return FALSE;
}
check_offset = check_offset + 1;
}
return TRUE;
}
https://mirrors.kernel.org/gnu/mes/mes-0.24.2.tar.gz 7ddae0854e46ebfa18c13ab37e64839a7b86ea88aeed366a8d017efd11dae86e https://download.savannah.gnu.org/releases/nyacc/nyacc-1.00.2.tar.gz f36e4fb7dd524dc3f4b354d3d5313f69e7ce5a6ae93711e8cf6d51eaa8d2b318
// SPDX-FileCopyrightText: 2020 fosslinux <fosslinux@aussies.space> // SPDX-FileCopyrightText: 2021 Andrius Å tikonas <andrius@stikonas.eu> // // SPDX-License-Identifier: GPL-3.0-or-later #undef SYSTEM_LIBC #define MES_VERSION "0.24.2"
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_LINUX_X86_SYSCALL_H #define __MES_LINUX_X86_SYSCALL_H 1 /* libc-mini */ #ifndef SYS_exit // CONSTANT SYS_exit 0x01 #define SYS_exit 0x01 #endif #ifndef SYS_write // CONSTANT SYS_write 0x04 #define SYS_write 0x04 #endif /* libc */ // CONSTANT SYS_fork 0x02 #define SYS_fork 0x02 // CONSTANT SYS_read 0x03 #define SYS_read 0x03 // CONSTANT SYS_open 0x05 #define SYS_open 0x05 // CONSTANT SYS_waitpid 0x07 #define SYS_waitpid 0x07 // CONSTANT SYS_wait4 0x72 #define SYS_wait4 0x72 // CONSTANT SYS_execve 0x0b #define SYS_execve 0x0b // CONSTANT SYS_chmod 0x0f #define SYS_chmod 0x0f // CONSTANT SYS_access 0x21 #define SYS_access 0x21 // CONSTANT SYS_brk 0x2d #define SYS_brk 0x2d // CONSTANT SYS_ioctl 0x36 #define SYS_ioctl 0x36 // CONSTANT SYS_fsync 0x76 #define SYS_fsync 0x76 // CONSTANT SYS_getcwd 0xb7 #define SYS_getcwd 0xb7 // CONSTANT SYS_dup 0x29 #define SYS_dup 0x29 // CONSTANT SYS_dup2 0x3f #define SYS_dup2 0x3f // CONSTANT SYS_unlink 0x0a #define SYS_unlink 0x0a // CONSTANT SYS_gettimeofday 0x4e #define SYS_gettimeofday 0x4e // CONSTANT SYS_clock_gettime 0x109 #define SYS_clock_gettime 0x109 // CONSTANT SYS_time 0x0d #define SYS_time 0x0d /* libc+tcc */ #define SYS_close 0x06 #define SYS_lseek 0x13 #define SYS_rmdir 0x28 #define SYS_stat 0x6a /* libc+gnu */ #define SYS_chdir 0x0c #define SYS_link 0x09 #define SYS_getpid 0x14 #define SYS_getuid 0x18 #define SYS_kill 0x25 #define SYS_rename 0x26 #define SYS_mkdir 0x27 #define SYS_pipe 0x2a #define SYS_getgid 0x2f #define SYS_signal 0x30 #define SYS_sigaction 0x43 #define SYS_rt_sigaction 0xae #define SYS_signal 0x30 #define SYS_fcntl 0x37 #define SYS_getrusage 0x4d #define SYS_lstat 0x6b #define SYS_setitimer 0x68 #define SYS_fstat 0x6c #define SYS_nanosleep 0xa2 #define SYS_getdents 0x8d /* bash */ #define SYS_setuid 0x17 #define SYS_geteuid 0x31 #define SYS_getegid 0x32 #define SYS_setgid 0x3e #define SYS_getppid 0x40 /* make+POSIX */ #define SYS_sigprocmask 0x7e /* tar */ #define SYS_symlink 0x53 #define SYS_readlink 0x55 #define SYS_mknod 0x0e #if __SIZEOF_LONG_LONG__ == 8 #define SYS_stat64 0xc3 #define SYS_lstat64 0xc4 #define SYS_fstat64 0xc5 #define SYS_fcntl64 0xdd #define SYS_getdents64 0xdc #undef SYS_stat #define SYS_stat SYS_stat64 #undef SYS_lstat #define SYS_lstat SYS_lstat64 #undef SYS_fstat #define SYS_fstat SYS_fstat64 #endif // __SIZEOF_LONG_LONG__ == 8 #endif /* __MES_LINUX_X86_SYSCALL_H */
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __MES_LINUX_X86_KERNEL_STAT_H
#define __MES_LINUX_X86_KERNEL_STAT_H 1
// https://github.com/torvalds/linux/blob/master/arch/x86/include/uapi/asm/stat.h
#include <arch/syscall.h>
#if __SIZEOF_LONG_LONG__ != 8
// *INDENT-OFF*
struct stat
{
unsigned long st_dev;
unsigned long st_ino;
unsigned short st_mode;
unsigned short st_nlink;
unsigned short st_uid;
unsigned short st_gid;
unsigned long st_rdev;
unsigned long st_size;
unsigned long st_blksize;
unsigned long st_blocks;
unsigned long st_atime;
unsigned long st_atime_usec;
unsigned long st_mtime;
unsigned long st_mtime_usec;
unsigned long st_ctime;
unsigned long st_ctime_usec;
unsigned long __pad0;
unsigned long __pad1;
};
#else // __SIZEOF_LONG_LONG__ == 8
struct stat
{
unsigned long long st_dev;
unsigned char __pad0[4];
unsigned long __st_ino;
unsigned int st_mode;
unsigned int st_nlink;
unsigned long st_uid;
unsigned long st_gid;
unsigned long long st_rdev;
unsigned char __pad3[4];
long long st_size;
unsigned long st_blksize;
unsigned long long st_blocks;
unsigned long st_atime;
unsigned long st_atime_nsec;
unsigned long st_mtime;
unsigned int st_mtime_nsec;
unsigned long st_ctime;
unsigned long st_ctime_nsec;
unsigned long long st_ino;
};
#endif // __SIZEOF_LONG_LONG__ == 8
#endif // __MES_LINUX_X86_KERNEL_STAT_H
;;; -*-scheme-*-
;;; GNU Mes --- Maxwell Equations of Software
;;; Copyright (C) 2016, 2017, 2018 Free Software Foundation, Inc.
;;;
;;; This file is part of GNU Mes.
;;;
;;; GNU Mes is free software; you can redistribute it and/or modify it
;;; under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 3 of the License, or (at
;;; your option) any later version.
;;;
;;; GNU Mes is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;;; GNU General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;;; This file is generated from psyntax.ss.
;;; Code:
(letrec ((syntmp-lambda-var-list-167
(lambda (syntmp-vars-552)
(let syntmp-lvl-553 ((syntmp-vars-554 syntmp-vars-552)
(syntmp-ls-555 (quote ()))
(syntmp-w-556 (quote (()))))
(cond ((pair? syntmp-vars-554)
(syntmp-lvl-553
(cdr syntmp-vars-554)
(cons (syntmp-wrap-146
(car syntmp-vars-554)
syntmp-w-556)
syntmp-ls-555)
syntmp-w-556))
((syntmp-id?-118 syntmp-vars-554)
(cons (syntmp-wrap-146 syntmp-vars-554 syntmp-w-556)
syntmp-ls-555))
((null? syntmp-vars-554) syntmp-ls-555)
((syntmp-syntax-object?-104 syntmp-vars-554)
(syntmp-lvl-553
(syntmp-syntax-object-expression-105
syntmp-vars-554)
syntmp-ls-555
(syntmp-join-wraps-137
syntmp-w-556
(syntmp-syntax-object-wrap-106 syntmp-vars-554))))
((syntmp-annotation?-92 syntmp-vars-554)
(syntmp-lvl-553
(annotation-expression syntmp-vars-554)
syntmp-ls-555
syntmp-w-556))
(else (cons syntmp-vars-554 syntmp-ls-555))))))
(syntmp-gen-var-166
(lambda (syntmp-id-557)
(let ((syntmp-id-558
(if (syntmp-syntax-object?-104 syntmp-id-557)
(syntmp-syntax-object-expression-105
syntmp-id-557)
syntmp-id-557)))
(if (syntmp-annotation?-92 syntmp-id-558)
(gensym
(symbol->string
(annotation-expression syntmp-id-558)))
(gensym (symbol->string syntmp-id-558))))))
(syntmp-strip-165
(lambda (syntmp-x-559 syntmp-w-560)
(if (memq 'top
(syntmp-wrap-marks-121 syntmp-w-560))
(if (or (syntmp-annotation?-92 syntmp-x-559)
(and (pair? syntmp-x-559)
(syntmp-annotation?-92 (car syntmp-x-559))))
(syntmp-strip-annotation-164 syntmp-x-559 #f)
syntmp-x-559)
(let syntmp-f-561 ((syntmp-x-562 syntmp-x-559))
(cond ((syntmp-syntax-object?-104 syntmp-x-562)
(syntmp-strip-165
(syntmp-syntax-object-expression-105
syntmp-x-562)
(syntmp-syntax-object-wrap-106 syntmp-x-562)))
((pair? syntmp-x-562)
(let ((syntmp-a-563 (syntmp-f-561 (car syntmp-x-562)))
(syntmp-d-564 (syntmp-f-561 (cdr syntmp-x-562))))
(if (and (eq? syntmp-a-563 (car syntmp-x-562))
(eq? syntmp-d-564 (cdr syntmp-x-562)))
syntmp-x-562
(cons syntmp-a-563 syntmp-d-564))))
((vector? syntmp-x-562)
(let ((syntmp-old-565 (vector->list syntmp-x-562)))
(let ((syntmp-new-566
(map syntmp-f-561 syntmp-old-565)))
(if (andmap eq? syntmp-old-565 syntmp-new-566)
syntmp-x-562
(list->vector syntmp-new-566)))))
(else syntmp-x-562))))))
(syntmp-strip-annotation-164
(lambda (syntmp-x-567 syntmp-parent-568)
(cond ((pair? syntmp-x-567)
(let ((syntmp-new-569 (cons #f #f)))
(begin
(when syntmp-parent-568
(set-annotation-stripped!
syntmp-parent-568
syntmp-new-569))
(set-car!
syntmp-new-569
(syntmp-strip-annotation-164
(car syntmp-x-567)
#f))
(set-cdr!
syntmp-new-569
(syntmp-strip-annotation-164
(cdr syntmp-x-567)
#f))
syntmp-new-569)))
((syntmp-annotation?-92 syntmp-x-567)
(or (annotation-stripped syntmp-x-567)
(syntmp-strip-annotation-164
(annotation-expression syntmp-x-567)
syntmp-x-567)))
((vector? syntmp-x-567)
(let ((syntmp-new-570
(make-vector (vector-length syntmp-x-567))))
(begin
(when syntmp-parent-568
(set-annotation-stripped!
syntmp-parent-568
syntmp-new-570))
(let syntmp-loop-571 ((syntmp-i-572
(- (vector-length syntmp-x-567)
1)))
(unless
(syntmp-fx<-91 syntmp-i-572 0)
(vector-set!
syntmp-new-570
syntmp-i-572
(syntmp-strip-annotation-164
(vector-ref syntmp-x-567 syntmp-i-572)
#f))
(syntmp-loop-571 (syntmp-fx--89 syntmp-i-572 1))))
syntmp-new-570)))
(else syntmp-x-567))))
(syntmp-ellipsis?-163
(lambda (syntmp-e-573 syntmp-r-574)
(and (syntmp-nonsymbol-id?-117 syntmp-e-573)
(let ((syntmp-id-575
(syntmp-make-syntax-object-103
'$sc-ellipsis
(syntmp-syntax-object-wrap-106 syntmp-e-573))))
(let ((syntmp-n-576
(syntmp-id-var-name-140
syntmp-id-575
'(()))))
(let ((syntmp-b-577
(syntmp-lookup-115 syntmp-n-576 syntmp-r-574)))
(if (eq? (syntmp-binding-type-110 syntmp-b-577)
'ellipsis)
(syntmp-bound-id=?-142
syntmp-e-573
(syntmp-binding-value-111 syntmp-b-577))
(syntmp-free-id=?-141
syntmp-e-573
'#(syntax-object
...
((top)
#(ribcage () () ())
#(ribcage () () ())
#(ribcage #(b) #((top)) #("i"))
#(ribcage () () ())
#(ribcage #(n) #((top)) #("i"))
#(ribcage () () ())
#(ribcage #(id) #((top)) #("i"))
#(ribcage () () ())
#(ribcage #(e r) #((top) (top)) #("i" "i"))
#(ribcage
(lambda-var-list
gen-var
strip
strip-annotation
ellipsis?
chi-void
eval-local-transformer
chi-local-syntax
chi-lambda-clause
chi-body
chi-macro
chi-application
chi-expr
chi
chi-top
syntax-type
chi-when-list
chi-install-global
chi-top-sequence
chi-sequence
source-wrap
wrap
bound-id-member?
distinct-bound-ids?
valid-bound-ids?
bound-id=?
free-id=?
id-var-name
same-marks?
join-marks
join-wraps
smart-append
make-binding-wrap
extend-ribcage!
make-empty-ribcage
new-mark
anti-mark
the-anti-mark
top-marked?
top-wrap
empty-wrap
set-ribcage-labels!
set-ribcage-marks!
set-ribcage-symnames!
ribcage-labels
ribcage-marks
ribcage-symnames
ribcage?
make-ribcage
gen-labels
gen-label
make-rename
rename-marks
rename-new
rename-old
subst-rename?
wrap-subst
wrap-marks
make-wrap
id-sym-name&marks
id-sym-name
id?
nonsymbol-id?
global-extend
lookup
macros-only-env
extend-var-env
extend-env
null-env
binding-value
binding-type
make-binding
arg-check
source-annotation
no-source
unannotate
set-syntax-object-wrap!
set-syntax-object-expression!
syntax-object-wrap
syntax-object-expression
syntax-object?
make-syntax-object
build-lexical-var
build-letrec
build-named-let
build-let
build-sequence
build-data
build-primref
build-lambda
build-global-definition
build-global-assignment
build-global-reference
build-lexical-assignment
build-lexical-reference
build-conditional
build-application
get-global-definition-hook
put-global-definition-hook
gensym-hook
error-hook
local-eval-hook
top-level-eval-hook
annotation?
fx<
fx=
fx-
fx+
noexpand)
((top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top))
("i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"))
#(ribcage
(define-structure)
((top))
("i"))))))))))))
(syntmp-chi-void-162
(lambda () (list (quote void))))
(syntmp-eval-local-transformer-161
(lambda (syntmp-expanded-578)
(let ((syntmp-p-579
(syntmp-local-eval-hook-94 syntmp-expanded-578)))
(if (procedure? syntmp-p-579)
syntmp-p-579
(syntax-error
syntmp-p-579
"nonprocedure transformer")))))
(syntmp-chi-local-syntax-160
(lambda (syntmp-rec?-580
syntmp-e-581
syntmp-r-582
syntmp-w-583
syntmp-s-584
syntmp-k-585)
((lambda (syntmp-tmp-586)
((lambda (syntmp-tmp-587)
(if syntmp-tmp-587
(apply (lambda (syntmp-_-588
syntmp-id-589
syntmp-val-590
syntmp-e1-591
syntmp-e2-592)
(let ((syntmp-ids-593 syntmp-id-589))
(if (not (syntmp-valid-bound-ids?-143
syntmp-ids-593))
(syntax-error
syntmp-e-581
"duplicate bound keyword in")
(let ((syntmp-labels-595
(syntmp-gen-labels-124
syntmp-ids-593)))
(let ((syntmp-new-w-596
(syntmp-make-binding-wrap-135
syntmp-ids-593
syntmp-labels-595
syntmp-w-583)))
(syntmp-k-585
(cons syntmp-e1-591 syntmp-e2-592)
(syntmp-extend-env-112
syntmp-labels-595
(let ((syntmp-w-598
(if syntmp-rec?-580
syntmp-new-w-596
syntmp-w-583))
(syntmp-trans-r-599
(syntmp-macros-only-env-114
syntmp-r-582)))
(map (lambda (syntmp-x-600)
(cons 'macro
(syntmp-eval-local-transformer-161
(syntmp-chi-154
syntmp-x-600
syntmp-trans-r-599
syntmp-w-598))))
syntmp-val-590))
syntmp-r-582)
syntmp-new-w-596
syntmp-s-584))))))
syntmp-tmp-587)
((lambda (syntmp-_-602)
(syntax-error
(syntmp-source-wrap-147
syntmp-e-581
syntmp-w-583
syntmp-s-584)))
syntmp-tmp-586)))
(syntax-dispatch
syntmp-tmp-586
'(any #(each (any any)) any . each-any))))
syntmp-e-581)))
(syntmp-chi-lambda-clause-159
(lambda (syntmp-e-603
syntmp-c-604
syntmp-r-605
syntmp-w-606
syntmp-k-607)
((lambda (syntmp-tmp-608)
((lambda (syntmp-tmp-609)
(if syntmp-tmp-609
(apply (lambda (syntmp-id-610 syntmp-e1-611 syntmp-e2-612)
(let ((syntmp-ids-613 syntmp-id-610))
(if (not (syntmp-valid-bound-ids?-143
syntmp-ids-613))
(syntax-error
syntmp-e-603
"invalid parameter list in")
(let ((syntmp-labels-615
(syntmp-gen-labels-124
syntmp-ids-613))
(syntmp-new-vars-616
(map syntmp-gen-var-166
syntmp-ids-613)))
(syntmp-k-607
syntmp-new-vars-616
(syntmp-chi-body-158
(cons syntmp-e1-611 syntmp-e2-612)
syntmp-e-603
(syntmp-extend-var-env-113
syntmp-labels-615
syntmp-new-vars-616
syntmp-r-605)
(syntmp-make-binding-wrap-135
syntmp-ids-613
syntmp-labels-615
syntmp-w-606)))))))
syntmp-tmp-609)
((lambda (syntmp-tmp-618)
(if syntmp-tmp-618
(apply (lambda (syntmp-ids-619
syntmp-e1-620
syntmp-e2-621)
(let ((syntmp-old-ids-622
(syntmp-lambda-var-list-167
syntmp-ids-619)))
(if (not (syntmp-valid-bound-ids?-143
syntmp-old-ids-622))
(syntax-error
syntmp-e-603
"invalid parameter list in")
(let ((syntmp-labels-623
(syntmp-gen-labels-124
syntmp-old-ids-622))
(syntmp-new-vars-624
(map syntmp-gen-var-166
syntmp-old-ids-622)))
(syntmp-k-607
(let syntmp-f-625 ((syntmp-ls1-626
(cdr syntmp-new-vars-624))
(syntmp-ls2-627
(car syntmp-new-vars-624)))
(if (null? syntmp-ls1-626)
syntmp-ls2-627
(syntmp-f-625
(cdr syntmp-ls1-626)
(cons (car syntmp-ls1-626)
syntmp-ls2-627))))
(syntmp-chi-body-158
(cons syntmp-e1-620 syntmp-e2-621)
syntmp-e-603
(syntmp-extend-var-env-113
syntmp-labels-623
syntmp-new-vars-624
syntmp-r-605)
(syntmp-make-binding-wrap-135
syntmp-old-ids-622
syntmp-labels-623
syntmp-w-606)))))))
syntmp-tmp-618)
((lambda (syntmp-_-629)
(syntax-error syntmp-e-603))
syntmp-tmp-608)))
(syntax-dispatch
syntmp-tmp-608
'(any any . each-any)))))
(syntax-dispatch
syntmp-tmp-608
'(each-any any . each-any))))
syntmp-c-604)))
(syntmp-chi-body-158
(lambda (syntmp-body-630
syntmp-outer-form-631
syntmp-r-632
syntmp-w-633)
(let ((syntmp-r-634
(cons '("placeholder" placeholder)
syntmp-r-632)))
(let ((syntmp-ribcage-635
(syntmp-make-ribcage-125
'()
'()
'())))
(let ((syntmp-w-636
(syntmp-make-wrap-120
(syntmp-wrap-marks-121 syntmp-w-633)
(cons syntmp-ribcage-635
(syntmp-wrap-subst-122 syntmp-w-633)))))
(let syntmp-parse-637 ((syntmp-body-638
(map (lambda (syntmp-x-644)
(cons syntmp-r-634
(syntmp-wrap-146
syntmp-x-644
syntmp-w-636)))
syntmp-body-630))
(syntmp-ids-639 (quote ()))
(syntmp-labels-640 (quote ()))
(syntmp-vars-641 (quote ()))
(syntmp-vals-642 (quote ()))
(syntmp-bindings-643 (quote ())))
(if (null? syntmp-body-638)
(syntax-error
syntmp-outer-form-631
"no expressions in body")
(let ((syntmp-e-645 (cdar syntmp-body-638))
(syntmp-er-646 (caar syntmp-body-638)))
(call-with-values
(lambda ()
(syntmp-syntax-type-152
syntmp-e-645
syntmp-er-646
'(())
#f
syntmp-ribcage-635))
(lambda (syntmp-type-647
syntmp-value-648
syntmp-e-649
syntmp-w-650
syntmp-s-651)
(let ((syntmp-t-652 syntmp-type-647))
(if (memv syntmp-t-652 (quote (define-form)))
(let ((syntmp-id-653
(syntmp-wrap-146
syntmp-value-648
syntmp-w-650))
(syntmp-label-654
(syntmp-gen-label-123)))
(let ((syntmp-var-655
(syntmp-gen-var-166 syntmp-id-653)))
(begin
(syntmp-extend-ribcage!-134
syntmp-ribcage-635
syntmp-id-653
syntmp-label-654)
(syntmp-parse-637
(cdr syntmp-body-638)
(cons syntmp-id-653 syntmp-ids-639)
(cons syntmp-label-654
syntmp-labels-640)
(cons syntmp-var-655 syntmp-vars-641)
(cons (cons syntmp-er-646
(syntmp-wrap-146
syntmp-e-649
syntmp-w-650))
syntmp-vals-642)
(cons (cons 'lexical
syntmp-var-655)
syntmp-bindings-643)))))
(if (memv syntmp-t-652
'(define-syntax-form))
(let ((syntmp-id-656
(syntmp-wrap-146
syntmp-value-648
syntmp-w-650))
(syntmp-label-657
(syntmp-gen-label-123)))
(begin
(syntmp-extend-ribcage!-134
syntmp-ribcage-635
syntmp-id-656
syntmp-label-657)
(syntmp-parse-637
(cdr syntmp-body-638)
(cons syntmp-id-656 syntmp-ids-639)
(cons syntmp-label-657
syntmp-labels-640)
syntmp-vars-641
syntmp-vals-642
(cons (cons 'macro
(cons syntmp-er-646
(syntmp-wrap-146
syntmp-e-649
syntmp-w-650)))
syntmp-bindings-643))))
(if (memv syntmp-t-652 (quote (begin-form)))
((lambda (syntmp-tmp-658)
((lambda (syntmp-tmp-659)
(if syntmp-tmp-659
(apply (lambda (syntmp-_-660
syntmp-e1-661)
(syntmp-parse-637
(let syntmp-f-662 ((syntmp-forms-663
syntmp-e1-661))
(if (null? syntmp-forms-663)
(cdr syntmp-body-638)
(cons (cons syntmp-er-646
(syntmp-wrap-146
(car syntmp-forms-663)
syntmp-w-650))
(syntmp-f-662
(cdr syntmp-forms-663)))))
syntmp-ids-639
syntmp-labels-640
syntmp-vars-641
syntmp-vals-642
syntmp-bindings-643))
syntmp-tmp-659)
(syntax-error syntmp-tmp-658)))
(syntax-dispatch
syntmp-tmp-658
'(any . each-any))))
syntmp-e-649)
(if (memv syntmp-t-652
'(local-syntax-form))
(syntmp-chi-local-syntax-160
syntmp-value-648
syntmp-e-649
syntmp-er-646
syntmp-w-650
syntmp-s-651
(lambda (syntmp-forms-665
syntmp-er-666
syntmp-w-667
syntmp-s-668)
(syntmp-parse-637
(let syntmp-f-669 ((syntmp-forms-670
syntmp-forms-665))
(if (null? syntmp-forms-670)
(cdr syntmp-body-638)
(cons (cons syntmp-er-666
(syntmp-wrap-146
(car syntmp-forms-670)
syntmp-w-667))
(syntmp-f-669
(cdr syntmp-forms-670)))))
syntmp-ids-639
syntmp-labels-640
syntmp-vars-641
syntmp-vals-642
syntmp-bindings-643)))
(if (null? syntmp-ids-639)
(syntmp-build-sequence-99
#f
(map (lambda (syntmp-x-671)
(syntmp-chi-154
(cdr syntmp-x-671)
(car syntmp-x-671)
'(())))
(cons (cons syntmp-er-646
(syntmp-source-wrap-147
syntmp-e-649
syntmp-w-650
syntmp-s-651))
(cdr syntmp-body-638))))
(begin
(if (not (syntmp-valid-bound-ids?-143
syntmp-ids-639))
(syntax-error
syntmp-outer-form-631
"invalid or duplicate identifier in definition"))
(let syntmp-loop-672 ((syntmp-bs-673
syntmp-bindings-643)
(syntmp-er-cache-674
#f)
(syntmp-r-cache-675
#f))
(if (not (null? syntmp-bs-673))
(let ((syntmp-b-676
(car syntmp-bs-673)))
(if (eq? (car syntmp-b-676)
'macro)
(let ((syntmp-er-677
(cadr syntmp-b-676)))
(let ((syntmp-r-cache-678
(if (eq? syntmp-er-677
syntmp-er-cache-674)
syntmp-r-cache-675
(syntmp-macros-only-env-114
syntmp-er-677))))
(begin
(set-cdr!
syntmp-b-676
(syntmp-eval-local-transformer-161
(syntmp-chi-154
(cddr syntmp-b-676)
syntmp-r-cache-678
'(()))))
(syntmp-loop-672
(cdr syntmp-bs-673)
syntmp-er-677
syntmp-r-cache-678))))
(syntmp-loop-672
(cdr syntmp-bs-673)
syntmp-er-cache-674
syntmp-r-cache-675)))))
(set-cdr!
syntmp-r-634
(syntmp-extend-env-112
syntmp-labels-640
syntmp-bindings-643
(cdr syntmp-r-634)))
(syntmp-build-letrec-102
#f
syntmp-vars-641
(map (lambda (syntmp-x-679)
(syntmp-chi-154
(cdr syntmp-x-679)
(car syntmp-x-679)
'(())))
syntmp-vals-642)
(syntmp-build-sequence-99
#f
(map (lambda (syntmp-x-680)
(syntmp-chi-154
(cdr syntmp-x-680)
(car syntmp-x-680)
'(())))
(cons (cons syntmp-er-646
(syntmp-source-wrap-147
syntmp-e-649
syntmp-w-650
syntmp-s-651))
(cdr syntmp-body-638))))))))))))))))))))))
(syntmp-chi-macro-157
(lambda (syntmp-p-681
syntmp-e-682
syntmp-r-683
syntmp-w-684
syntmp-rib-685)
(letrec ((syntmp-rebuild-macro-output-686
(lambda (syntmp-x-687 syntmp-m-688)
(cond ((pair? syntmp-x-687)
(cons (syntmp-rebuild-macro-output-686
(car syntmp-x-687)
syntmp-m-688)
(syntmp-rebuild-macro-output-686
(cdr syntmp-x-687)
syntmp-m-688)))
((syntmp-syntax-object?-104 syntmp-x-687)
(let ((syntmp-w-689
(syntmp-syntax-object-wrap-106
syntmp-x-687)))
(let ((syntmp-ms-690
(syntmp-wrap-marks-121
syntmp-w-689))
(syntmp-s-691
(syntmp-wrap-subst-122
syntmp-w-689)))
(syntmp-make-syntax-object-103
(syntmp-syntax-object-expression-105
syntmp-x-687)
(if (and (pair? syntmp-ms-690)
(eq? (car syntmp-ms-690) #f))
(syntmp-make-wrap-120
(cdr syntmp-ms-690)
(if syntmp-rib-685
(cons syntmp-rib-685
(cdr syntmp-s-691))
(cdr syntmp-s-691)))
(syntmp-make-wrap-120
(cons syntmp-m-688 syntmp-ms-690)
(if syntmp-rib-685
(cons syntmp-rib-685
(cons 'shift
syntmp-s-691))
(cons 'shift
syntmp-s-691))))))))
((vector? syntmp-x-687)
(let ((syntmp-n-692
(vector-length syntmp-x-687)))
(let ((syntmp-v-693
(make-vector syntmp-n-692)))
(let syntmp-doloop-694 ((syntmp-i-695 0))
(if (syntmp-fx=-90
syntmp-i-695
syntmp-n-692)
syntmp-v-693
(begin
(vector-set!
syntmp-v-693
syntmp-i-695
(syntmp-rebuild-macro-output-686
(vector-ref
syntmp-x-687
syntmp-i-695)
syntmp-m-688))
(syntmp-doloop-694
(syntmp-fx+-88
syntmp-i-695
1))))))))
((symbol? syntmp-x-687)
(syntax-error
syntmp-x-687
"encountered raw symbol in macro output"))
(else syntmp-x-687)))))
(syntmp-rebuild-macro-output-686
(syntmp-p-681
(syntmp-wrap-146
syntmp-e-682
(syntmp-anti-mark-133 syntmp-w-684)))
(string #\m)))))
(syntmp-chi-application-156
(lambda (syntmp-x-696
syntmp-e-697
syntmp-r-698
syntmp-w-699
syntmp-s-700)
((lambda (syntmp-tmp-701)
((lambda (syntmp-tmp-702)
(if syntmp-tmp-702
(apply (lambda (syntmp-e0-703 syntmp-e1-704)
(cons syntmp-x-696
(map (lambda (syntmp-e-705)
(syntmp-chi-154
syntmp-e-705
syntmp-r-698
syntmp-w-699))
syntmp-e1-704)))
syntmp-tmp-702)
(syntax-error syntmp-tmp-701)))
(syntax-dispatch
syntmp-tmp-701
'(any . each-any))))
syntmp-e-697)))
(syntmp-chi-expr-155
(lambda (syntmp-type-707
syntmp-value-708
syntmp-e-709
syntmp-r-710
syntmp-w-711
syntmp-s-712)
(let ((syntmp-t-713 syntmp-type-707))
(if (memv syntmp-t-713 (quote (lexical)))
syntmp-value-708
(if (memv syntmp-t-713 (quote (core external-macro)))
(syntmp-value-708
syntmp-e-709
syntmp-r-710
syntmp-w-711
syntmp-s-712)
(if (memv syntmp-t-713 (quote (lexical-call)))
(syntmp-chi-application-156
syntmp-value-708
syntmp-e-709
syntmp-r-710
syntmp-w-711
syntmp-s-712)
(if (memv syntmp-t-713 (quote (global-call)))
(syntmp-chi-application-156
syntmp-value-708
syntmp-e-709
syntmp-r-710
syntmp-w-711
syntmp-s-712)
(if (memv syntmp-t-713 (quote (constant)))
(syntmp-build-data-98
syntmp-s-712
(syntmp-strip-165
(syntmp-source-wrap-147
syntmp-e-709
syntmp-w-711
syntmp-s-712)
'(())))
(if (memv syntmp-t-713 (quote (global)))
syntmp-value-708
(if (memv syntmp-t-713 (quote (call)))
(syntmp-chi-application-156
(syntmp-chi-154
(car syntmp-e-709)
syntmp-r-710
syntmp-w-711)
syntmp-e-709
syntmp-r-710
syntmp-w-711
syntmp-s-712)
(if (memv syntmp-t-713 (quote (begin-form)))
((lambda (syntmp-tmp-714)
((lambda (syntmp-tmp-715)
(if syntmp-tmp-715
(apply (lambda (syntmp-_-716
syntmp-e1-717
syntmp-e2-718)
(syntmp-chi-sequence-148
(cons syntmp-e1-717
syntmp-e2-718)
syntmp-r-710
syntmp-w-711
syntmp-s-712))
syntmp-tmp-715)
(syntax-error syntmp-tmp-714)))
(syntax-dispatch
syntmp-tmp-714
'(any any . each-any))))
syntmp-e-709)
(if (memv syntmp-t-713
'(local-syntax-form))
(syntmp-chi-local-syntax-160
syntmp-value-708
syntmp-e-709
syntmp-r-710
syntmp-w-711
syntmp-s-712
syntmp-chi-sequence-148)
(if (memv syntmp-t-713
'(eval-when-form))
((lambda (syntmp-tmp-720)
((lambda (syntmp-tmp-721)
(if syntmp-tmp-721
(apply (lambda (syntmp-_-722
syntmp-x-723
syntmp-e1-724
syntmp-e2-725)
(let ((syntmp-when-list-726
(syntmp-chi-when-list-151
syntmp-e-709
syntmp-x-723
syntmp-w-711)))
(if (memq 'eval
syntmp-when-list-726)
(syntmp-chi-sequence-148
(cons syntmp-e1-724
syntmp-e2-725)
syntmp-r-710
syntmp-w-711
syntmp-s-712)
(syntmp-chi-void-162))))
syntmp-tmp-721)
(syntax-error syntmp-tmp-720)))
(syntax-dispatch
syntmp-tmp-720
'(any each-any any . each-any))))
syntmp-e-709)
(if (memv syntmp-t-713
'(define-form define-syntax-form))
(syntax-error
(syntmp-wrap-146
syntmp-value-708
syntmp-w-711)
"invalid context for definition of")
(if (memv syntmp-t-713 (quote (syntax)))
(syntax-error
(syntmp-source-wrap-147
syntmp-e-709
syntmp-w-711
syntmp-s-712)
"reference to pattern variable outside syntax form")
(if (memv syntmp-t-713
'(displaced-lexical))
(syntax-error
(syntmp-source-wrap-147
syntmp-e-709
syntmp-w-711
syntmp-s-712)
"reference to identifier outside its scope")
(syntax-error
(syntmp-source-wrap-147
syntmp-e-709
syntmp-w-711
syntmp-s-712))))))))))))))))))
(syntmp-chi-154
(lambda (syntmp-e-729 syntmp-r-730 syntmp-w-731)
(call-with-values
(lambda ()
(syntmp-syntax-type-152
syntmp-e-729
syntmp-r-730
syntmp-w-731
#f
#f))
(lambda (syntmp-type-732
syntmp-value-733
syntmp-e-734
syntmp-w-735
syntmp-s-736)
(syntmp-chi-expr-155
syntmp-type-732
syntmp-value-733
syntmp-e-734
syntmp-r-730
syntmp-w-735
syntmp-s-736)))))
(syntmp-chi-top-153
(lambda (syntmp-e-737
syntmp-r-738
syntmp-w-739
syntmp-m-740
syntmp-esew-741)
(call-with-values
(lambda ()
(syntmp-syntax-type-152
syntmp-e-737
syntmp-r-738
syntmp-w-739
#f
#f))
(lambda (syntmp-type-754
syntmp-value-755
syntmp-e-756
syntmp-w-757
syntmp-s-758)
(let ((syntmp-t-759 syntmp-type-754))
(if (memv syntmp-t-759 (quote (begin-form)))
((lambda (syntmp-tmp-760)
((lambda (syntmp-tmp-761)
(if syntmp-tmp-761
(apply (lambda (syntmp-_-762)
(syntmp-chi-void-162))
syntmp-tmp-761)
((lambda (syntmp-tmp-763)
(if syntmp-tmp-763
(apply (lambda (syntmp-_-764
syntmp-e1-765
syntmp-e2-766)
(syntmp-chi-top-sequence-149
(cons syntmp-e1-765 syntmp-e2-766)
syntmp-r-738
syntmp-w-757
syntmp-s-758
syntmp-m-740
syntmp-esew-741))
syntmp-tmp-763)
(syntax-error syntmp-tmp-760)))
(syntax-dispatch
syntmp-tmp-760
'(any any . each-any)))))
(syntax-dispatch syntmp-tmp-760 (quote (any)))))
syntmp-e-756)
(if (memv syntmp-t-759 (quote (local-syntax-form)))
(syntmp-chi-local-syntax-160
syntmp-value-755
syntmp-e-756
syntmp-r-738
syntmp-w-757
syntmp-s-758
(lambda (syntmp-body-768
syntmp-r-769
syntmp-w-770
syntmp-s-771)
(syntmp-chi-top-sequence-149
syntmp-body-768
syntmp-r-769
syntmp-w-770
syntmp-s-771
syntmp-m-740
syntmp-esew-741)))
(if (memv syntmp-t-759 (quote (eval-when-form)))
((lambda (syntmp-tmp-772)
((lambda (syntmp-tmp-773)
(if syntmp-tmp-773
(apply (lambda (syntmp-_-774
syntmp-x-775
syntmp-e1-776
syntmp-e2-777)
(let ((syntmp-when-list-778
(syntmp-chi-when-list-151
syntmp-e-756
syntmp-x-775
syntmp-w-757))
(syntmp-body-779
(cons syntmp-e1-776
syntmp-e2-777)))
(cond ((eq? syntmp-m-740 (quote e))
(if (memq 'eval
syntmp-when-list-778)
(syntmp-chi-top-sequence-149
syntmp-body-779
syntmp-r-738
syntmp-w-757
syntmp-s-758
'e
'(eval))
(syntmp-chi-void-162)))
((memq 'load
syntmp-when-list-778)
(if (or (memq 'compile
syntmp-when-list-778)
(and (eq? syntmp-m-740
'c&e)
(memq 'eval
syntmp-when-list-778)))
(syntmp-chi-top-sequence-149
syntmp-body-779
syntmp-r-738
syntmp-w-757
syntmp-s-758
'c&e
'(compile load))
(if (memq syntmp-m-740
'(c c&e))
(syntmp-chi-top-sequence-149
syntmp-body-779
syntmp-r-738
syntmp-w-757
syntmp-s-758
'c
'(load))
(syntmp-chi-void-162))))
((or (memq 'compile
syntmp-when-list-778)
(and (eq? syntmp-m-740
'c&e)
(memq 'eval
syntmp-when-list-778)))
(syntmp-top-level-eval-hook-93
(syntmp-chi-top-sequence-149
syntmp-body-779
syntmp-r-738
syntmp-w-757
syntmp-s-758
'e
'(eval)))
(syntmp-chi-void-162))
(else
(syntmp-chi-void-162)))))
syntmp-tmp-773)
(syntax-error syntmp-tmp-772)))
(syntax-dispatch
syntmp-tmp-772
'(any each-any any . each-any))))
syntmp-e-756)
(if (memv syntmp-t-759 (quote (define-syntax-form)))
(let ((syntmp-n-782
(syntmp-id-var-name-140
syntmp-value-755
syntmp-w-757))
(syntmp-r-783
(syntmp-macros-only-env-114 syntmp-r-738)))
(let ((syntmp-t-784 syntmp-m-740))
(if (memv syntmp-t-784 (quote (c)))
(if (memq (quote compile) syntmp-esew-741)
(let ((syntmp-e-785
(syntmp-chi-install-global-150
syntmp-n-782
(syntmp-chi-154
syntmp-e-756
syntmp-r-783
syntmp-w-757))))
(begin
(syntmp-top-level-eval-hook-93
syntmp-e-785)
(if (memq (quote load) syntmp-esew-741)
syntmp-e-785
(syntmp-chi-void-162))))
(if (memq (quote load) syntmp-esew-741)
(syntmp-chi-install-global-150
syntmp-n-782
(syntmp-chi-154
syntmp-e-756
syntmp-r-783
syntmp-w-757))
(syntmp-chi-void-162)))
(if (memv syntmp-t-784 (quote (c&e)))
(let ((syntmp-e-786
(syntmp-chi-install-global-150
syntmp-n-782
(syntmp-chi-154
syntmp-e-756
syntmp-r-783
syntmp-w-757))))
(begin
(syntmp-top-level-eval-hook-93
syntmp-e-786)
syntmp-e-786))
(begin
(if (memq (quote eval) syntmp-esew-741)
(syntmp-top-level-eval-hook-93
(syntmp-chi-install-global-150
syntmp-n-782
(syntmp-chi-154
syntmp-e-756
syntmp-r-783
syntmp-w-757))))
(syntmp-chi-void-162))))))
(if (memv syntmp-t-759 (quote (define-form)))
(let ((syntmp-n-787
(syntmp-id-var-name-140
syntmp-value-755
syntmp-w-757)))
(let ((syntmp-type-788
(syntmp-binding-type-110
(syntmp-lookup-115
syntmp-n-787
syntmp-r-738))))
(let ((syntmp-t-789 syntmp-type-788))
(if (memv syntmp-t-789 (quote (global)))
(let ((syntmp-x-790
(list 'define
syntmp-n-787
(syntmp-chi-154
syntmp-e-756
syntmp-r-738
syntmp-w-757))))
(begin
(if (eq? syntmp-m-740 (quote c&e))
(syntmp-top-level-eval-hook-93
syntmp-x-790))
syntmp-x-790))
(if (memv syntmp-t-789
'(displaced-lexical))
(syntax-error
(syntmp-wrap-146
syntmp-value-755
syntmp-w-757)
"identifier out of context")
(if (eq? syntmp-type-788
'external-macro)
(let ((syntmp-x-791
(list 'define
syntmp-n-787
(syntmp-chi-154
syntmp-e-756
syntmp-r-738
syntmp-w-757))))
(begin
(if (eq? syntmp-m-740 (quote c&e))
(syntmp-top-level-eval-hook-93
syntmp-x-791))
syntmp-x-791))
(syntax-error
(syntmp-wrap-146
syntmp-value-755
syntmp-w-757)
"cannot define keyword at top level")))))))
(let ((syntmp-x-792
(syntmp-chi-expr-155
syntmp-type-754
syntmp-value-755
syntmp-e-756
syntmp-r-738
syntmp-w-757
syntmp-s-758)))
(begin
(if (eq? syntmp-m-740 (quote c&e))
(syntmp-top-level-eval-hook-93
syntmp-x-792))
syntmp-x-792))))))))))))
(syntmp-syntax-type-152
(lambda (syntmp-e-793
syntmp-r-794
syntmp-w-795
syntmp-s-796
syntmp-rib-797)
(cond ((symbol? syntmp-e-793)
(let ((syntmp-n-798
(syntmp-id-var-name-140
syntmp-e-793
syntmp-w-795)))
(let ((syntmp-b-799
(syntmp-lookup-115 syntmp-n-798 syntmp-r-794)))
(let ((syntmp-type-800
(syntmp-binding-type-110 syntmp-b-799)))
(let ((syntmp-t-801 syntmp-type-800))
(if (memv syntmp-t-801 (quote (lexical)))
(values
syntmp-type-800
(syntmp-binding-value-111 syntmp-b-799)
syntmp-e-793
syntmp-w-795
syntmp-s-796)
(if (memv syntmp-t-801 (quote (global)))
(values
syntmp-type-800
syntmp-n-798
syntmp-e-793
syntmp-w-795
syntmp-s-796)
(if (memv syntmp-t-801 (quote (macro)))
(syntmp-syntax-type-152
(syntmp-chi-macro-157
(syntmp-binding-value-111 syntmp-b-799)
syntmp-e-793
syntmp-r-794
syntmp-w-795
syntmp-rib-797)
syntmp-r-794
'(())
syntmp-s-796
syntmp-rib-797)
(values
syntmp-type-800
(syntmp-binding-value-111 syntmp-b-799)
syntmp-e-793
syntmp-w-795
syntmp-s-796)))))))))
((pair? syntmp-e-793)
(let ((syntmp-first-802 (car syntmp-e-793)))
(if (syntmp-id?-118 syntmp-first-802)
(let ((syntmp-n-803
(syntmp-id-var-name-140
syntmp-first-802
syntmp-w-795)))
(let ((syntmp-b-804
(syntmp-lookup-115
syntmp-n-803
syntmp-r-794)))
(let ((syntmp-type-805
(syntmp-binding-type-110 syntmp-b-804)))
(let ((syntmp-t-806 syntmp-type-805))
(if (memv syntmp-t-806 (quote (lexical)))
(values
'lexical-call
(syntmp-binding-value-111 syntmp-b-804)
syntmp-e-793
syntmp-w-795
syntmp-s-796)
(if (memv syntmp-t-806 (quote (global)))
(values
'global-call
syntmp-n-803
syntmp-e-793
syntmp-w-795
syntmp-s-796)
(if (memv syntmp-t-806 (quote (macro)))
(syntmp-syntax-type-152
(syntmp-chi-macro-157
(syntmp-binding-value-111
syntmp-b-804)
syntmp-e-793
syntmp-r-794
syntmp-w-795
syntmp-rib-797)
syntmp-r-794
'(())
syntmp-s-796
syntmp-rib-797)
(if (memv syntmp-t-806
'(core external-macro))
(values
syntmp-type-805
(syntmp-binding-value-111
syntmp-b-804)
syntmp-e-793
syntmp-w-795
syntmp-s-796)
(if (memv syntmp-t-806
'(local-syntax))
(values
'local-syntax-form
(syntmp-binding-value-111
syntmp-b-804)
syntmp-e-793
syntmp-w-795
syntmp-s-796)
(if (memv syntmp-t-806
'(begin))
(values
'begin-form
#f
syntmp-e-793
syntmp-w-795
syntmp-s-796)
(if (memv syntmp-t-806
'(eval-when))
(values
'eval-when-form
#f
syntmp-e-793
syntmp-w-795
syntmp-s-796)
(if (memv syntmp-t-806
'(define))
((lambda (syntmp-tmp-807)
((lambda (syntmp-tmp-808)
(if (if syntmp-tmp-808
(apply (lambda (syntmp-_-809
syntmp-name-810
syntmp-val-811)
(syntmp-id?-118
syntmp-name-810))
syntmp-tmp-808)
#f)
(apply (lambda (syntmp-_-812
syntmp-name-813
syntmp-val-814)
(values
'define-form
syntmp-name-813
syntmp-val-814
syntmp-w-795
syntmp-s-796))
syntmp-tmp-808)
((lambda (syntmp-tmp-815)
(if (if syntmp-tmp-815
(apply (lambda (syntmp-_-816
syntmp-name-817
syntmp-args-818
syntmp-e1-819
syntmp-e2-820)
(and (syntmp-id?-118
syntmp-name-817)
(syntmp-valid-bound-ids?-143
(syntmp-lambda-var-list-167
syntmp-args-818))))
syntmp-tmp-815)
#f)
(apply (lambda (syntmp-_-821
syntmp-name-822
syntmp-args-823
syntmp-e1-824
syntmp-e2-825)
(values
'define-form
(syntmp-wrap-146
syntmp-name-822
syntmp-w-795)
(cons '#(syntax-object
lambda
((top)
#(ribcage
#(_
name
args
e1
e2)
#((top)
(top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(t)
#(("m"
top))
#("i"))
#(ribcage
()
()
())
#(ribcage
()
()
())
#(ribcage
()
()
())
#(ribcage
#(type)
#((top))
#("i"))
#(ribcage
()
()
())
#(ribcage
#(b)
#((top))
#("i"))
#(ribcage
()
()
())
#(ribcage
#(n)
#((top))
#("i"))
#(ribcage
()
()
())
#(ribcage
#(first)
#((top))
#("i"))
#(ribcage
()
()
())
#(ribcage
#(e
r
w
s
rib)
#((top)
(top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"
"i"))
#(ribcage
(lambda-var-list
gen-var
strip
strip-annotation
ellipsis?
chi-void
eval-local-transformer
chi-local-syntax
chi-lambda-clause
chi-body
chi-macro
chi-application
chi-expr
chi
chi-top
syntax-type
chi-when-list
chi-install-global
chi-top-sequence
chi-sequence
source-wrap
wrap
bound-id-member?
distinct-bound-ids?
valid-bound-ids?
bound-id=?
free-id=?
id-var-name
same-marks?
join-marks
join-wraps
smart-append
make-binding-wrap
extend-ribcage!
make-empty-ribcage
new-mark
anti-mark
the-anti-mark
top-marked?
top-wrap
empty-wrap
set-ribcage-labels!
set-ribcage-marks!
set-ribcage-symnames!
ribcage-labels
ribcage-marks
ribcage-symnames
ribcage?
make-ribcage
gen-labels
gen-label
make-rename
rename-marks
rename-new
rename-old
subst-rename?
wrap-subst
wrap-marks
make-wrap
id-sym-name&marks
id-sym-name
id?
nonsymbol-id?
global-extend
lookup
macros-only-env
extend-var-env
extend-env
null-env
binding-value
binding-type
make-binding
arg-check
source-annotation
no-source
unannotate
set-syntax-object-wrap!
set-syntax-object-expression!
syntax-object-wrap
syntax-object-expression
syntax-object?
make-syntax-object
build-lexical-var
build-letrec
build-named-let
build-let
build-sequence
build-data
build-primref
build-lambda
build-global-definition
build-global-assignment
build-global-reference
build-lexical-assignment
build-lexical-reference
build-conditional
build-application
get-global-definition-hook
put-global-definition-hook
gensym-hook
error-hook
local-eval-hook
top-level-eval-hook
annotation?
fx<
fx=
fx-
fx+
noexpand)
((top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top))
("i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"))
#(ribcage
(define-structure)
((top))
("i"))))
(syntmp-wrap-146
(cons syntmp-args-823
(cons syntmp-e1-824
syntmp-e2-825))
syntmp-w-795))
'(())
syntmp-s-796))
syntmp-tmp-815)
((lambda (syntmp-tmp-827)
(if (if syntmp-tmp-827
(apply (lambda (syntmp-_-828
syntmp-name-829)
(syntmp-id?-118
syntmp-name-829))
syntmp-tmp-827)
#f)
(apply (lambda (syntmp-_-830
syntmp-name-831)
(values
'define-form
(syntmp-wrap-146
syntmp-name-831
syntmp-w-795)
'(#(syntax-object
void
((top)
#(ribcage
#(_
name)
#((top)
(top))
#("i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(t)
#(("m"
top))
#("i"))
#(ribcage
()
()
())
#(ribcage
()
()
())
#(ribcage
()
()
())
#(ribcage
#(type)
#((top))
#("i"))
#(ribcage
()
()
())
#(ribcage
#(b)
#((top))
#("i"))
#(ribcage
()
()
())
#(ribcage
#(n)
#((top))
#("i"))
#(ribcage
()
()
())
#(ribcage
#(first)
#((top))
#("i"))
#(ribcage
()
()
())
#(ribcage
#(e
r
w
s
rib)
#((top)
(top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"
"i"))
#(ribcage
(lambda-var-list
gen-var
strip
strip-annotation
ellipsis?
chi-void
eval-local-transformer
chi-local-syntax
chi-lambda-clause
chi-body
chi-macro
chi-application
chi-expr
chi
chi-top
syntax-type
chi-when-list
chi-install-global
chi-top-sequence
chi-sequence
source-wrap
wrap
bound-id-member?
distinct-bound-ids?
valid-bound-ids?
bound-id=?
free-id=?
id-var-name
same-marks?
join-marks
join-wraps
smart-append
make-binding-wrap
extend-ribcage!
make-empty-ribcage
new-mark
anti-mark
the-anti-mark
top-marked?
top-wrap
empty-wrap
set-ribcage-labels!
set-ribcage-marks!
set-ribcage-symnames!
ribcage-labels
ribcage-marks
ribcage-symnames
ribcage?
make-ribcage
gen-labels
gen-label
make-rename
rename-marks
rename-new
rename-old
subst-rename?
wrap-subst
wrap-marks
make-wrap
id-sym-name&marks
id-sym-name
id?
nonsymbol-id?
global-extend
lookup
macros-only-env
extend-var-env
extend-env
null-env
binding-value
binding-type
make-binding
arg-check
source-annotation
no-source
unannotate
set-syntax-object-wrap!
set-syntax-object-expression!
syntax-object-wrap
syntax-object-expression
syntax-object?
make-syntax-object
build-lexical-var
build-letrec
build-named-let
build-let
build-sequence
build-data
build-primref
build-lambda
build-global-definition
build-global-assignment
build-global-reference
build-lexical-assignment
build-lexical-reference
build-conditional
build-application
get-global-definition-hook
put-global-definition-hook
gensym-hook
error-hook
local-eval-hook
top-level-eval-hook
annotation?
fx<
fx=
fx-
fx+
noexpand)
((top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top))
("i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"))
#(ribcage
(define-structure)
((top))
("i")))))
'(())
syntmp-s-796))
syntmp-tmp-827)
(syntax-error
syntmp-tmp-807)))
(syntax-dispatch
syntmp-tmp-807
'(any any)))))
(syntax-dispatch
syntmp-tmp-807
'(any (any . any)
any
.
each-any)))))
(syntax-dispatch
syntmp-tmp-807
'(any any any))))
syntmp-e-793)
(if (memv syntmp-t-806
'(define-syntax))
((lambda (syntmp-tmp-832)
((lambda (syntmp-tmp-833)
(if (if syntmp-tmp-833
(apply (lambda (syntmp-_-834
syntmp-name-835
syntmp-val-836)
(syntmp-id?-118
syntmp-name-835))
syntmp-tmp-833)
#f)
(apply (lambda (syntmp-_-837
syntmp-name-838
syntmp-val-839)
(values
'define-syntax-form
syntmp-name-838
syntmp-val-839
syntmp-w-795
syntmp-s-796))
syntmp-tmp-833)
(syntax-error
syntmp-tmp-832)))
(syntax-dispatch
syntmp-tmp-832
'(any any any))))
syntmp-e-793)
(if (memv syntmp-t-806
'(ellipsis))
(values
'ellipsis
(syntmp-make-syntax-object-103
(syntmp-syntax-object-expression-105
value)
(syntmp-anti-mark-133
(syntmp-syntax-object-wrap-106
value))))
(values
'call
#f
syntmp-e-793
syntmp-w-795
syntmp-s-796)))))))))))))))
(values
'call
#f
syntmp-e-793
syntmp-w-795
syntmp-s-796))))
((syntmp-syntax-object?-104 syntmp-e-793)
(syntmp-syntax-type-152
(syntmp-syntax-object-expression-105
syntmp-e-793)
syntmp-r-794
(syntmp-join-wraps-137
syntmp-w-795
(syntmp-syntax-object-wrap-106 syntmp-e-793))
#f
syntmp-rib-797))
((syntmp-annotation?-92 syntmp-e-793)
(syntmp-syntax-type-152
(annotation-expression syntmp-e-793)
syntmp-r-794
syntmp-w-795
(annotation-source syntmp-e-793)
syntmp-rib-797))
((self-evaluating? syntmp-e-793)
(values
'constant
#f
syntmp-e-793
syntmp-w-795
syntmp-s-796))
(else
(values
'other
#f
syntmp-e-793
syntmp-w-795
syntmp-s-796)))))
(syntmp-chi-when-list-151
(lambda (syntmp-e-840 syntmp-when-list-841 syntmp-w-842)
(let syntmp-f-843 ((syntmp-when-list-844 syntmp-when-list-841)
(syntmp-situations-845 (quote ())))
(if (null? syntmp-when-list-844)
syntmp-situations-845
(syntmp-f-843
(cdr syntmp-when-list-844)
(cons (let ((syntmp-x-846 (car syntmp-when-list-844)))
(cond ((syntmp-free-id=?-141
syntmp-x-846
'#(syntax-object
compile
((top)
#(ribcage () () ())
#(ribcage #(x) #((top)) #("i"))
#(ribcage () () ())
#(ribcage
#(f when-list situations)
#((top) (top) (top))
#("i" "i" "i"))
#(ribcage () () ())
#(ribcage
#(e when-list w)
#((top) (top) (top))
#("i" "i" "i"))
#(ribcage
(lambda-var-list
gen-var
strip
strip-annotation
ellipsis?
chi-void
eval-local-transformer
chi-local-syntax
chi-lambda-clause
chi-body
chi-macro
chi-application
chi-expr
chi
chi-top
syntax-type
chi-when-list
chi-install-global
chi-top-sequence
chi-sequence
source-wrap
wrap
bound-id-member?
distinct-bound-ids?
valid-bound-ids?
bound-id=?
free-id=?
id-var-name
same-marks?
join-marks
join-wraps
smart-append
make-binding-wrap
extend-ribcage!
make-empty-ribcage
new-mark
anti-mark
the-anti-mark
top-marked?
top-wrap
empty-wrap
set-ribcage-labels!
set-ribcage-marks!
set-ribcage-symnames!
ribcage-labels
ribcage-marks
ribcage-symnames
ribcage?
make-ribcage
gen-labels
gen-label
make-rename
rename-marks
rename-new
rename-old
subst-rename?
wrap-subst
wrap-marks
make-wrap
id-sym-name&marks
id-sym-name
id?
nonsymbol-id?
global-extend
lookup
macros-only-env
extend-var-env
extend-env
null-env
binding-value
binding-type
make-binding
arg-check
source-annotation
no-source
unannotate
set-syntax-object-wrap!
set-syntax-object-expression!
syntax-object-wrap
syntax-object-expression
syntax-object?
make-syntax-object
build-lexical-var
build-letrec
build-named-let
build-let
build-sequence
build-data
build-primref
build-lambda
build-global-definition
build-global-assignment
build-global-reference
build-lexical-assignment
build-lexical-reference
build-conditional
build-application
get-global-definition-hook
put-global-definition-hook
gensym-hook
error-hook
local-eval-hook
top-level-eval-hook
annotation?
fx<
fx=
fx-
fx+
noexpand)
((top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top))
("i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"))
#(ribcage
(define-structure)
((top))
("i")))))
'compile)
((syntmp-free-id=?-141
syntmp-x-846
'#(syntax-object
load
((top)
#(ribcage () () ())
#(ribcage #(x) #((top)) #("i"))
#(ribcage () () ())
#(ribcage
#(f when-list situations)
#((top) (top) (top))
#("i" "i" "i"))
#(ribcage () () ())
#(ribcage
#(e when-list w)
#((top) (top) (top))
#("i" "i" "i"))
#(ribcage
(lambda-var-list
gen-var
strip
strip-annotation
ellipsis?
chi-void
eval-local-transformer
chi-local-syntax
chi-lambda-clause
chi-body
chi-macro
chi-application
chi-expr
chi
chi-top
syntax-type
chi-when-list
chi-install-global
chi-top-sequence
chi-sequence
source-wrap
wrap
bound-id-member?
distinct-bound-ids?
valid-bound-ids?
bound-id=?
free-id=?
id-var-name
same-marks?
join-marks
join-wraps
smart-append
make-binding-wrap
extend-ribcage!
make-empty-ribcage
new-mark
anti-mark
the-anti-mark
top-marked?
top-wrap
empty-wrap
set-ribcage-labels!
set-ribcage-marks!
set-ribcage-symnames!
ribcage-labels
ribcage-marks
ribcage-symnames
ribcage?
make-ribcage
gen-labels
gen-label
make-rename
rename-marks
rename-new
rename-old
subst-rename?
wrap-subst
wrap-marks
make-wrap
id-sym-name&marks
id-sym-name
id?
nonsymbol-id?
global-extend
lookup
macros-only-env
extend-var-env
extend-env
null-env
binding-value
binding-type
make-binding
arg-check
source-annotation
no-source
unannotate
set-syntax-object-wrap!
set-syntax-object-expression!
syntax-object-wrap
syntax-object-expression
syntax-object?
make-syntax-object
build-lexical-var
build-letrec
build-named-let
build-let
build-sequence
build-data
build-primref
build-lambda
build-global-definition
build-global-assignment
build-global-reference
build-lexical-assignment
build-lexical-reference
build-conditional
build-application
get-global-definition-hook
put-global-definition-hook
gensym-hook
error-hook
local-eval-hook
top-level-eval-hook
annotation?
fx<
fx=
fx-
fx+
noexpand)
((top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top))
("i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"))
#(ribcage
(define-structure)
((top))
("i")))))
'load)
((syntmp-free-id=?-141
syntmp-x-846
'#(syntax-object
eval
((top)
#(ribcage () () ())
#(ribcage #(x) #((top)) #("i"))
#(ribcage () () ())
#(ribcage
#(f when-list situations)
#((top) (top) (top))
#("i" "i" "i"))
#(ribcage () () ())
#(ribcage
#(e when-list w)
#((top) (top) (top))
#("i" "i" "i"))
#(ribcage
(lambda-var-list
gen-var
strip
strip-annotation
ellipsis?
chi-void
eval-local-transformer
chi-local-syntax
chi-lambda-clause
chi-body
chi-macro
chi-application
chi-expr
chi
chi-top
syntax-type
chi-when-list
chi-install-global
chi-top-sequence
chi-sequence
source-wrap
wrap
bound-id-member?
distinct-bound-ids?
valid-bound-ids?
bound-id=?
free-id=?
id-var-name
same-marks?
join-marks
join-wraps
smart-append
make-binding-wrap
extend-ribcage!
make-empty-ribcage
new-mark
anti-mark
the-anti-mark
top-marked?
top-wrap
empty-wrap
set-ribcage-labels!
set-ribcage-marks!
set-ribcage-symnames!
ribcage-labels
ribcage-marks
ribcage-symnames
ribcage?
make-ribcage
gen-labels
gen-label
make-rename
rename-marks
rename-new
rename-old
subst-rename?
wrap-subst
wrap-marks
make-wrap
id-sym-name&marks
id-sym-name
id?
nonsymbol-id?
global-extend
lookup
macros-only-env
extend-var-env
extend-env
null-env
binding-value
binding-type
make-binding
arg-check
source-annotation
no-source
unannotate
set-syntax-object-wrap!
set-syntax-object-expression!
syntax-object-wrap
syntax-object-expression
syntax-object?
make-syntax-object
build-lexical-var
build-letrec
build-named-let
build-let
build-sequence
build-data
build-primref
build-lambda
build-global-definition
build-global-assignment
build-global-reference
build-lexical-assignment
build-lexical-reference
build-conditional
build-application
get-global-definition-hook
put-global-definition-hook
gensym-hook
error-hook
local-eval-hook
top-level-eval-hook
annotation?
fx<
fx=
fx-
fx+
noexpand)
((top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top))
("i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"))
#(ribcage
(define-structure)
((top))
("i")))))
'eval)
(else
(syntax-error
(syntmp-wrap-146 syntmp-x-846 syntmp-w-842)
"invalid eval-when situation"))))
syntmp-situations-845))))))
(syntmp-chi-install-global-150
(lambda (syntmp-name-847 syntmp-e-848)
(list 'install-global-transformer
(syntmp-build-data-98 #f syntmp-name-847)
syntmp-e-848)))
(syntmp-chi-top-sequence-149
(lambda (syntmp-body-849
syntmp-r-850
syntmp-w-851
syntmp-s-852
syntmp-m-853
syntmp-esew-854)
(syntmp-build-sequence-99
syntmp-s-852
(let syntmp-dobody-855 ((syntmp-body-856 syntmp-body-849)
(syntmp-r-857 syntmp-r-850)
(syntmp-w-858 syntmp-w-851)
(syntmp-m-859 syntmp-m-853)
(syntmp-esew-860 syntmp-esew-854))
(if (null? syntmp-body-856)
'()
(let ((syntmp-first-861
(syntmp-chi-top-153
(car syntmp-body-856)
syntmp-r-857
syntmp-w-858
syntmp-m-859
syntmp-esew-860)))
(cons syntmp-first-861
(syntmp-dobody-855
(cdr syntmp-body-856)
syntmp-r-857
syntmp-w-858
syntmp-m-859
syntmp-esew-860))))))))
(syntmp-chi-sequence-148
(lambda (syntmp-body-862
syntmp-r-863
syntmp-w-864
syntmp-s-865)
(syntmp-build-sequence-99
syntmp-s-865
(let syntmp-dobody-866 ((syntmp-body-867 syntmp-body-862)
(syntmp-r-868 syntmp-r-863)
(syntmp-w-869 syntmp-w-864))
(if (null? syntmp-body-867)
'()
(let ((syntmp-first-870
(syntmp-chi-154
(car syntmp-body-867)
syntmp-r-868
syntmp-w-869)))
(cons syntmp-first-870
(syntmp-dobody-866
(cdr syntmp-body-867)
syntmp-r-868
syntmp-w-869))))))))
(syntmp-source-wrap-147
(lambda (syntmp-x-871 syntmp-w-872 syntmp-s-873)
(syntmp-wrap-146
(if syntmp-s-873
(make-annotation syntmp-x-871 syntmp-s-873 #f)
syntmp-x-871)
syntmp-w-872)))
(syntmp-wrap-146
(lambda (syntmp-x-874 syntmp-w-875)
(cond ((and (null? (syntmp-wrap-marks-121 syntmp-w-875))
(null? (syntmp-wrap-subst-122 syntmp-w-875)))
syntmp-x-874)
((syntmp-syntax-object?-104 syntmp-x-874)
(syntmp-make-syntax-object-103
(syntmp-syntax-object-expression-105
syntmp-x-874)
(syntmp-join-wraps-137
syntmp-w-875
(syntmp-syntax-object-wrap-106 syntmp-x-874))))
((null? syntmp-x-874) syntmp-x-874)
(else
(syntmp-make-syntax-object-103
syntmp-x-874
syntmp-w-875)))))
(syntmp-bound-id-member?-145
(lambda (syntmp-x-876 syntmp-list-877)
(and (not (null? syntmp-list-877))
(or (syntmp-bound-id=?-142
syntmp-x-876
(car syntmp-list-877))
(syntmp-bound-id-member?-145
syntmp-x-876
(cdr syntmp-list-877))))))
(syntmp-distinct-bound-ids?-144
(lambda (syntmp-ids-878)
(let syntmp-distinct?-879 ((syntmp-ids-880 syntmp-ids-878))
(or (null? syntmp-ids-880)
(and (not (syntmp-bound-id-member?-145
(car syntmp-ids-880)
(cdr syntmp-ids-880)))
(syntmp-distinct?-879 (cdr syntmp-ids-880)))))))
(syntmp-valid-bound-ids?-143
(lambda (syntmp-ids-881)
(and (let syntmp-all-ids?-882 ((syntmp-ids-883 syntmp-ids-881))
(or (null? syntmp-ids-883)
(and (syntmp-id?-118 (car syntmp-ids-883))
(syntmp-all-ids?-882 (cdr syntmp-ids-883)))))
(syntmp-distinct-bound-ids?-144 syntmp-ids-881))))
(syntmp-bound-id=?-142
(lambda (syntmp-i-884 syntmp-j-885)
(if (and (syntmp-syntax-object?-104 syntmp-i-884)
(syntmp-syntax-object?-104 syntmp-j-885))
(and (eq? (let ((syntmp-e-886
(syntmp-syntax-object-expression-105
syntmp-i-884)))
(if (syntmp-annotation?-92 syntmp-e-886)
(annotation-expression syntmp-e-886)
syntmp-e-886))
(let ((syntmp-e-887
(syntmp-syntax-object-expression-105
syntmp-j-885)))
(if (syntmp-annotation?-92 syntmp-e-887)
(annotation-expression syntmp-e-887)
syntmp-e-887)))
(syntmp-same-marks?-139
(syntmp-wrap-marks-121
(syntmp-syntax-object-wrap-106 syntmp-i-884))
(syntmp-wrap-marks-121
(syntmp-syntax-object-wrap-106 syntmp-j-885))))
(eq? (let ((syntmp-e-888 syntmp-i-884))
(if (syntmp-annotation?-92 syntmp-e-888)
(annotation-expression syntmp-e-888)
syntmp-e-888))
(let ((syntmp-e-889 syntmp-j-885))
(if (syntmp-annotation?-92 syntmp-e-889)
(annotation-expression syntmp-e-889)
syntmp-e-889))))))
(syntmp-free-id=?-141
(lambda (syntmp-i-890 syntmp-j-891)
(and (eq? (let ((syntmp-x-892 syntmp-i-890))
(let ((syntmp-e-893
(if (syntmp-syntax-object?-104 syntmp-x-892)
(syntmp-syntax-object-expression-105
syntmp-x-892)
syntmp-x-892)))
(if (syntmp-annotation?-92 syntmp-e-893)
(annotation-expression syntmp-e-893)
syntmp-e-893)))
(let ((syntmp-x-894 syntmp-j-891))
(let ((syntmp-e-895
(if (syntmp-syntax-object?-104 syntmp-x-894)
(syntmp-syntax-object-expression-105
syntmp-x-894)
syntmp-x-894)))
(if (syntmp-annotation?-92 syntmp-e-895)
(annotation-expression syntmp-e-895)
syntmp-e-895))))
(eq? (syntmp-id-var-name-140
syntmp-i-890
'(()))
(syntmp-id-var-name-140
syntmp-j-891
'(()))))))
(syntmp-id-var-name-140
(lambda (syntmp-id-896 syntmp-w-897)
(letrec ((syntmp-search-vector-rib-900
(lambda (syntmp-sym-911
syntmp-subst-912
syntmp-marks-913
syntmp-symnames-914
syntmp-ribcage-915)
(let ((syntmp-n-916
(vector-length syntmp-symnames-914)))
(let syntmp-f-917 ((syntmp-i-918 0))
(cond ((syntmp-fx=-90 syntmp-i-918 syntmp-n-916)
(syntmp-search-898
syntmp-sym-911
(cdr syntmp-subst-912)
syntmp-marks-913))
((and (eq? (vector-ref
syntmp-symnames-914
syntmp-i-918)
syntmp-sym-911)
(syntmp-same-marks?-139
syntmp-marks-913
(vector-ref
(syntmp-ribcage-marks-128
syntmp-ribcage-915)
syntmp-i-918)))
(values
(vector-ref
(syntmp-ribcage-labels-129
syntmp-ribcage-915)
syntmp-i-918)
syntmp-marks-913))
(else
(syntmp-f-917
(syntmp-fx+-88 syntmp-i-918 1))))))))
(syntmp-search-list-rib-899
(lambda (syntmp-sym-919
syntmp-subst-920
syntmp-marks-921
syntmp-symnames-922
syntmp-ribcage-923)
(let syntmp-f-924 ((syntmp-symnames-925
syntmp-symnames-922)
(syntmp-i-926 0))
(cond ((null? syntmp-symnames-925)
(syntmp-search-898
syntmp-sym-919
(cdr syntmp-subst-920)
syntmp-marks-921))
((and (eq? (car syntmp-symnames-925)
syntmp-sym-919)
(syntmp-same-marks?-139
syntmp-marks-921
(list-ref
(syntmp-ribcage-marks-128
syntmp-ribcage-923)
syntmp-i-926)))
(values
(list-ref
(syntmp-ribcage-labels-129
syntmp-ribcage-923)
syntmp-i-926)
syntmp-marks-921))
(else
(syntmp-f-924
(cdr syntmp-symnames-925)
(syntmp-fx+-88 syntmp-i-926 1)))))))
(syntmp-search-898
(lambda (syntmp-sym-927
syntmp-subst-928
syntmp-marks-929)
(if (null? syntmp-subst-928)
(values #f syntmp-marks-929)
(let ((syntmp-fst-930 (car syntmp-subst-928)))
(if (eq? syntmp-fst-930 (quote shift))
(syntmp-search-898
syntmp-sym-927
(cdr syntmp-subst-928)
(cdr syntmp-marks-929))
(let ((syntmp-symnames-931
(syntmp-ribcage-symnames-127
syntmp-fst-930)))
(if (vector? syntmp-symnames-931)
(syntmp-search-vector-rib-900
syntmp-sym-927
syntmp-subst-928
syntmp-marks-929
syntmp-symnames-931
syntmp-fst-930)
(syntmp-search-list-rib-899
syntmp-sym-927
syntmp-subst-928
syntmp-marks-929
syntmp-symnames-931
syntmp-fst-930)))))))))
(cond ((symbol? syntmp-id-896)
(or (call-with-values
(lambda ()
(syntmp-search-898
syntmp-id-896
(syntmp-wrap-subst-122 syntmp-w-897)
(syntmp-wrap-marks-121 syntmp-w-897)))
(lambda (syntmp-x-933 . syntmp-ignore-932)
syntmp-x-933))
syntmp-id-896))
((syntmp-syntax-object?-104 syntmp-id-896)
(let ((syntmp-id-934
(let ((syntmp-e-936
(syntmp-syntax-object-expression-105
syntmp-id-896)))
(if (syntmp-annotation?-92 syntmp-e-936)
(annotation-expression syntmp-e-936)
syntmp-e-936)))
(syntmp-w1-935
(syntmp-syntax-object-wrap-106 syntmp-id-896)))
(let ((syntmp-marks-937
(syntmp-join-marks-138
(syntmp-wrap-marks-121 syntmp-w-897)
(syntmp-wrap-marks-121 syntmp-w1-935))))
(call-with-values
(lambda ()
(syntmp-search-898
syntmp-id-934
(syntmp-wrap-subst-122 syntmp-w-897)
syntmp-marks-937))
(lambda (syntmp-new-id-938 syntmp-marks-939)
(or syntmp-new-id-938
(call-with-values
(lambda ()
(syntmp-search-898
syntmp-id-934
(syntmp-wrap-subst-122 syntmp-w1-935)
syntmp-marks-939))
(lambda (syntmp-x-941 . syntmp-ignore-940)
syntmp-x-941))
syntmp-id-934))))))
((syntmp-annotation?-92 syntmp-id-896)
(let ((syntmp-id-942
(let ((syntmp-e-943 syntmp-id-896))
(if (syntmp-annotation?-92 syntmp-e-943)
(annotation-expression syntmp-e-943)
syntmp-e-943))))
(or (call-with-values
(lambda ()
(syntmp-search-898
syntmp-id-942
(syntmp-wrap-subst-122 syntmp-w-897)
(syntmp-wrap-marks-121 syntmp-w-897)))
(lambda (syntmp-x-945 . syntmp-ignore-944)
syntmp-x-945))
syntmp-id-942)))
(else
(syntmp-error-hook-95
'id-var-name
"invalid id"
syntmp-id-896))))))
(syntmp-same-marks?-139
(lambda (syntmp-x-946 syntmp-y-947)
(or (eq? syntmp-x-946 syntmp-y-947)
(and (not (null? syntmp-x-946))
(not (null? syntmp-y-947))
(eq? (car syntmp-x-946) (car syntmp-y-947))
(syntmp-same-marks?-139
(cdr syntmp-x-946)
(cdr syntmp-y-947))))))
(syntmp-join-marks-138
(lambda (syntmp-m1-948 syntmp-m2-949)
(syntmp-smart-append-136
syntmp-m1-948
syntmp-m2-949)))
(syntmp-join-wraps-137
(lambda (syntmp-w1-950 syntmp-w2-951)
(let ((syntmp-m1-952
(syntmp-wrap-marks-121 syntmp-w1-950))
(syntmp-s1-953
(syntmp-wrap-subst-122 syntmp-w1-950)))
(if (null? syntmp-m1-952)
(if (null? syntmp-s1-953)
syntmp-w2-951
(syntmp-make-wrap-120
(syntmp-wrap-marks-121 syntmp-w2-951)
(syntmp-smart-append-136
syntmp-s1-953
(syntmp-wrap-subst-122 syntmp-w2-951))))
(syntmp-make-wrap-120
(syntmp-smart-append-136
syntmp-m1-952
(syntmp-wrap-marks-121 syntmp-w2-951))
(syntmp-smart-append-136
syntmp-s1-953
(syntmp-wrap-subst-122 syntmp-w2-951)))))))
(syntmp-smart-append-136
(lambda (syntmp-m1-954 syntmp-m2-955)
(if (null? syntmp-m2-955)
syntmp-m1-954
(append syntmp-m1-954 syntmp-m2-955))))
(syntmp-make-binding-wrap-135
(lambda (syntmp-ids-956 syntmp-labels-957 syntmp-w-958)
(if (null? syntmp-ids-956)
syntmp-w-958
(syntmp-make-wrap-120
(syntmp-wrap-marks-121 syntmp-w-958)
(cons (let ((syntmp-labelvec-959
(list->vector syntmp-labels-957)))
(let ((syntmp-n-960
(vector-length syntmp-labelvec-959)))
(let ((syntmp-symnamevec-961
(make-vector syntmp-n-960))
(syntmp-marksvec-962
(make-vector syntmp-n-960)))
(begin
(let syntmp-f-963 ((syntmp-ids-964
syntmp-ids-956)
(syntmp-i-965 0))
(if (not (null? syntmp-ids-964))
(call-with-values
(lambda ()
(syntmp-id-sym-name&marks-119
(car syntmp-ids-964)
syntmp-w-958))
(lambda (syntmp-symname-966
syntmp-marks-967)
(begin
(vector-set!
syntmp-symnamevec-961
syntmp-i-965
syntmp-symname-966)
(vector-set!
syntmp-marksvec-962
syntmp-i-965
syntmp-marks-967)
(syntmp-f-963
(cdr syntmp-ids-964)
(syntmp-fx+-88 syntmp-i-965 1)))))))
(syntmp-make-ribcage-125
syntmp-symnamevec-961
syntmp-marksvec-962
syntmp-labelvec-959)))))
(syntmp-wrap-subst-122 syntmp-w-958))))))
(syntmp-extend-ribcage!-134
(lambda (syntmp-ribcage-968
syntmp-id-969
syntmp-label-970)
(begin
(syntmp-set-ribcage-symnames!-130
syntmp-ribcage-968
(cons (let ((syntmp-e-971
(syntmp-syntax-object-expression-105
syntmp-id-969)))
(if (syntmp-annotation?-92 syntmp-e-971)
(annotation-expression syntmp-e-971)
syntmp-e-971))
(syntmp-ribcage-symnames-127 syntmp-ribcage-968)))
(syntmp-set-ribcage-marks!-131
syntmp-ribcage-968
(cons (syntmp-wrap-marks-121
(syntmp-syntax-object-wrap-106 syntmp-id-969))
(syntmp-ribcage-marks-128 syntmp-ribcage-968)))
(syntmp-set-ribcage-labels!-132
syntmp-ribcage-968
(cons syntmp-label-970
(syntmp-ribcage-labels-129 syntmp-ribcage-968))))))
(syntmp-anti-mark-133
(lambda (syntmp-w-972)
(syntmp-make-wrap-120
(cons #f (syntmp-wrap-marks-121 syntmp-w-972))
(cons 'shift
(syntmp-wrap-subst-122 syntmp-w-972)))))
(syntmp-set-ribcage-labels!-132
(lambda (syntmp-x-973 syntmp-update-974)
(vector-set! syntmp-x-973 3 syntmp-update-974)))
(syntmp-set-ribcage-marks!-131
(lambda (syntmp-x-975 syntmp-update-976)
(vector-set! syntmp-x-975 2 syntmp-update-976)))
(syntmp-set-ribcage-symnames!-130
(lambda (syntmp-x-977 syntmp-update-978)
(vector-set! syntmp-x-977 1 syntmp-update-978)))
(syntmp-ribcage-labels-129
(lambda (syntmp-x-979)
(vector-ref syntmp-x-979 3)))
(syntmp-ribcage-marks-128
(lambda (syntmp-x-980)
(vector-ref syntmp-x-980 2)))
(syntmp-ribcage-symnames-127
(lambda (syntmp-x-981)
(vector-ref syntmp-x-981 1)))
(syntmp-ribcage?-126
(lambda (syntmp-x-982)
(and (vector? syntmp-x-982)
(= (vector-length syntmp-x-982) 4)
(eq? (vector-ref syntmp-x-982 0) (quote ribcage)))))
(syntmp-make-ribcage-125
(lambda (syntmp-symnames-983
syntmp-marks-984
syntmp-labels-985)
(vector
'ribcage
syntmp-symnames-983
syntmp-marks-984
syntmp-labels-985)))
(syntmp-gen-labels-124
(lambda (syntmp-ls-986)
(if (null? syntmp-ls-986)
'()
(cons (syntmp-gen-label-123)
(syntmp-gen-labels-124 (cdr syntmp-ls-986))))))
(syntmp-gen-label-123 (lambda () (string #\i)))
(syntmp-wrap-subst-122 cdr)
(syntmp-wrap-marks-121 car)
(syntmp-make-wrap-120 cons)
(syntmp-id-sym-name&marks-119
(lambda (syntmp-x-987 syntmp-w-988)
(if (syntmp-syntax-object?-104 syntmp-x-987)
(values
(let ((syntmp-e-989
(syntmp-syntax-object-expression-105
syntmp-x-987)))
(if (syntmp-annotation?-92 syntmp-e-989)
(annotation-expression syntmp-e-989)
syntmp-e-989))
(syntmp-join-marks-138
(syntmp-wrap-marks-121 syntmp-w-988)
(syntmp-wrap-marks-121
(syntmp-syntax-object-wrap-106 syntmp-x-987))))
(values
(let ((syntmp-e-990 syntmp-x-987))
(if (syntmp-annotation?-92 syntmp-e-990)
(annotation-expression syntmp-e-990)
syntmp-e-990))
(syntmp-wrap-marks-121 syntmp-w-988)))))
(syntmp-id?-118
(lambda (syntmp-x-991)
(cond ((symbol? syntmp-x-991) #t)
((syntmp-syntax-object?-104 syntmp-x-991)
(symbol?
(let ((syntmp-e-992
(syntmp-syntax-object-expression-105
syntmp-x-991)))
(if (syntmp-annotation?-92 syntmp-e-992)
(annotation-expression syntmp-e-992)
syntmp-e-992))))
((syntmp-annotation?-92 syntmp-x-991)
(symbol? (annotation-expression syntmp-x-991)))
(else #f))))
(syntmp-nonsymbol-id?-117
(lambda (syntmp-x-993)
(and (syntmp-syntax-object?-104 syntmp-x-993)
(symbol?
(let ((syntmp-e-994
(syntmp-syntax-object-expression-105
syntmp-x-993)))
(if (syntmp-annotation?-92 syntmp-e-994)
(annotation-expression syntmp-e-994)
syntmp-e-994))))))
(syntmp-global-extend-116
(lambda (syntmp-type-995 syntmp-sym-996 syntmp-val-997)
(syntmp-put-global-definition-hook-96
syntmp-sym-996
(cons syntmp-type-995 syntmp-val-997))))
(syntmp-lookup-115
(lambda (syntmp-x-998 syntmp-r-999)
(cond ((assq syntmp-x-998 syntmp-r-999) => cdr)
((symbol? syntmp-x-998)
(or (syntmp-get-global-definition-hook-97
syntmp-x-998)
'(global)))
(else (quote (displaced-lexical))))))
(syntmp-macros-only-env-114
(lambda (syntmp-r-1000)
(if (null? syntmp-r-1000)
'()
(let ((syntmp-a-1001 (car syntmp-r-1000)))
(if (memq (cadr syntmp-a-1001)
'(macro ellipsis))
(cons syntmp-a-1001
(syntmp-macros-only-env-114 (cdr syntmp-r-1000)))
(syntmp-macros-only-env-114 (cdr syntmp-r-1000)))))))
(syntmp-extend-var-env-113
(lambda (syntmp-labels-1002
syntmp-vars-1003
syntmp-r-1004)
(if (null? syntmp-labels-1002)
syntmp-r-1004
(syntmp-extend-var-env-113
(cdr syntmp-labels-1002)
(cdr syntmp-vars-1003)
(cons (cons (car syntmp-labels-1002)
(cons (quote lexical) (car syntmp-vars-1003)))
syntmp-r-1004)))))
(syntmp-extend-env-112
(lambda (syntmp-labels-1005
syntmp-bindings-1006
syntmp-r-1007)
(if (null? syntmp-labels-1005)
syntmp-r-1007
(syntmp-extend-env-112
(cdr syntmp-labels-1005)
(cdr syntmp-bindings-1006)
(cons (cons (car syntmp-labels-1005)
(car syntmp-bindings-1006))
syntmp-r-1007)))))
(syntmp-binding-value-111 cdr)
(syntmp-binding-type-110 car)
(syntmp-source-annotation-109
(lambda (syntmp-x-1008)
(cond ((syntmp-annotation?-92 syntmp-x-1008)
(annotation-source syntmp-x-1008))
((syntmp-syntax-object?-104 syntmp-x-1008)
(syntmp-source-annotation-109
(syntmp-syntax-object-expression-105
syntmp-x-1008)))
(else #f))))
(syntmp-set-syntax-object-wrap!-108
(lambda (syntmp-x-1009 syntmp-update-1010)
(vector-set! syntmp-x-1009 2 syntmp-update-1010)))
(syntmp-set-syntax-object-expression!-107
(lambda (syntmp-x-1011 syntmp-update-1012)
(vector-set! syntmp-x-1011 1 syntmp-update-1012)))
(syntmp-syntax-object-wrap-106
(lambda (syntmp-x-1013)
(vector-ref syntmp-x-1013 2)))
(syntmp-syntax-object-expression-105
(lambda (syntmp-x-1014)
(vector-ref syntmp-x-1014 1)))
(syntmp-syntax-object?-104
(lambda (syntmp-x-1015)
(and (vector? syntmp-x-1015)
(= (vector-length syntmp-x-1015) 3)
(eq? (vector-ref syntmp-x-1015 0)
'syntax-object))))
(syntmp-make-syntax-object-103
(lambda (syntmp-expression-1016 syntmp-wrap-1017)
(vector
'syntax-object
syntmp-expression-1016
syntmp-wrap-1017)))
(syntmp-build-letrec-102
(lambda (syntmp-src-1018
syntmp-vars-1019
syntmp-val-exps-1020
syntmp-body-exp-1021)
(if (null? syntmp-vars-1019)
syntmp-body-exp-1021
(list 'letrec
(map list syntmp-vars-1019 syntmp-val-exps-1020)
syntmp-body-exp-1021))))
(syntmp-build-named-let-101
(lambda (syntmp-src-1022
syntmp-vars-1023
syntmp-val-exps-1024
syntmp-body-exp-1025)
(if (null? syntmp-vars-1023)
syntmp-body-exp-1025
(list 'let
(car syntmp-vars-1023)
(map list
(cdr syntmp-vars-1023)
syntmp-val-exps-1024)
syntmp-body-exp-1025))))
(syntmp-build-let-100
(lambda (syntmp-src-1026
syntmp-vars-1027
syntmp-val-exps-1028
syntmp-body-exp-1029)
(if (null? syntmp-vars-1027)
syntmp-body-exp-1029
(list 'let
(map list syntmp-vars-1027 syntmp-val-exps-1028)
syntmp-body-exp-1029))))
(syntmp-build-sequence-99
(lambda (syntmp-src-1030 syntmp-exps-1031)
(if (null? (cdr syntmp-exps-1031))
(car syntmp-exps-1031)
(cons (quote begin) syntmp-exps-1031))))
(syntmp-build-data-98
(lambda (syntmp-src-1032 syntmp-exp-1033)
(if (and (self-evaluating? syntmp-exp-1033)
(not (vector? syntmp-exp-1033)))
syntmp-exp-1033
(list (quote quote) syntmp-exp-1033))))
(syntmp-get-global-definition-hook-97
(lambda (syntmp-symbol-1034)
(getprop
syntmp-symbol-1034
'*sc-expander*)))
(syntmp-put-global-definition-hook-96
(lambda (syntmp-symbol-1035 syntmp-binding-1036)
(putprop
syntmp-symbol-1035
'*sc-expander*
syntmp-binding-1036)))
(syntmp-error-hook-95
(lambda (syntmp-who-1037
syntmp-why-1038
syntmp-what-1039)
(error syntmp-who-1037
"~a ~s"
syntmp-why-1038
syntmp-what-1039)))
(syntmp-local-eval-hook-94
(lambda (syntmp-x-1040)
(eval (list syntmp-noexpand-87 syntmp-x-1040)
(interaction-environment))))
(syntmp-top-level-eval-hook-93
(lambda (syntmp-x-1041)
(eval (list syntmp-noexpand-87 syntmp-x-1041)
(interaction-environment))))
(syntmp-annotation?-92
(lambda (syntmp-x-1042) #f))
(syntmp-fx<-91 <)
(syntmp-fx=-90 =)
(syntmp-fx--89 -)
(syntmp-fx+-88 +)
(syntmp-noexpand-87 "noexpand"))
(begin
(syntmp-global-extend-116
'local-syntax
'letrec-syntax
#t)
(syntmp-global-extend-116
'local-syntax
'let-syntax
#f)
(syntmp-global-extend-116
'core
'fluid-let-syntax
(lambda (syntmp-e-1043
syntmp-r-1044
syntmp-w-1045
syntmp-s-1046)
((lambda (syntmp-tmp-1047)
((lambda (syntmp-tmp-1048)
(if (if syntmp-tmp-1048
(apply (lambda (syntmp-_-1049
syntmp-var-1050
syntmp-val-1051
syntmp-e1-1052
syntmp-e2-1053)
(syntmp-valid-bound-ids?-143 syntmp-var-1050))
syntmp-tmp-1048)
#f)
(apply (lambda (syntmp-_-1055
syntmp-var-1056
syntmp-val-1057
syntmp-e1-1058
syntmp-e2-1059)
(let ((syntmp-names-1060
(map (lambda (syntmp-x-1061)
(syntmp-id-var-name-140
syntmp-x-1061
syntmp-w-1045))
syntmp-var-1056)))
(begin
(for-each
(lambda (syntmp-id-1063 syntmp-n-1064)
(let ((syntmp-t-1065
(syntmp-binding-type-110
(syntmp-lookup-115
syntmp-n-1064
syntmp-r-1044))))
(if (memv syntmp-t-1065
'(displaced-lexical))
(syntax-error
(syntmp-source-wrap-147
syntmp-id-1063
syntmp-w-1045
syntmp-s-1046)
"identifier out of context"))))
syntmp-var-1056
syntmp-names-1060)
(syntmp-chi-body-158
(cons syntmp-e1-1058 syntmp-e2-1059)
(syntmp-source-wrap-147
syntmp-e-1043
syntmp-w-1045
syntmp-s-1046)
(syntmp-extend-env-112
syntmp-names-1060
(let ((syntmp-trans-r-1068
(syntmp-macros-only-env-114
syntmp-r-1044)))
(map (lambda (syntmp-x-1069)
(cons 'macro
(syntmp-eval-local-transformer-161
(syntmp-chi-154
syntmp-x-1069
syntmp-trans-r-1068
syntmp-w-1045))))
syntmp-val-1057))
syntmp-r-1044)
syntmp-w-1045))))
syntmp-tmp-1048)
((lambda (syntmp-_-1071)
(syntax-error
(syntmp-source-wrap-147
syntmp-e-1043
syntmp-w-1045
syntmp-s-1046)))
syntmp-tmp-1047)))
(syntax-dispatch
syntmp-tmp-1047
'(any #(each (any any)) any . each-any))))
syntmp-e-1043)))
(syntmp-global-extend-116
'core
'quote
(lambda (syntmp-e-1072
syntmp-r-1073
syntmp-w-1074
syntmp-s-1075)
((lambda (syntmp-tmp-1076)
((lambda (syntmp-tmp-1077)
(if syntmp-tmp-1077
(apply (lambda (syntmp-_-1078 syntmp-e-1079)
(syntmp-build-data-98
syntmp-s-1075
(syntmp-strip-165 syntmp-e-1079 syntmp-w-1074)))
syntmp-tmp-1077)
((lambda (syntmp-_-1080)
(syntax-error
(syntmp-source-wrap-147
syntmp-e-1072
syntmp-w-1074
syntmp-s-1075)))
syntmp-tmp-1076)))
(syntax-dispatch
syntmp-tmp-1076
'(any any))))
syntmp-e-1072)))
(syntmp-global-extend-116
'core
'syntax
(letrec ((syntmp-regen-1088
(lambda (syntmp-x-1089)
(let ((syntmp-t-1090 (car syntmp-x-1089)))
(if (memv syntmp-t-1090 (quote (ref)))
(cadr syntmp-x-1089)
(if (memv syntmp-t-1090 (quote (primitive)))
(cadr syntmp-x-1089)
(if (memv syntmp-t-1090 (quote (quote)))
(syntmp-build-data-98 #f (cadr syntmp-x-1089))
(if (memv syntmp-t-1090 (quote (lambda)))
(list 'lambda
(cadr syntmp-x-1089)
(syntmp-regen-1088 (caddr syntmp-x-1089)))
(if (memv syntmp-t-1090 (quote (map)))
(let ((syntmp-ls-1091
(map syntmp-regen-1088
(cdr syntmp-x-1089))))
(cons (if (syntmp-fx=-90
(length syntmp-ls-1091)
2)
'map
'map)
syntmp-ls-1091))
(cons (car syntmp-x-1089)
(map syntmp-regen-1088
(cdr syntmp-x-1089)))))))))))
(syntmp-gen-vector-1087
(lambda (syntmp-x-1092)
(cond ((eq? (car syntmp-x-1092) (quote list))
(cons (quote vector) (cdr syntmp-x-1092)))
((eq? (car syntmp-x-1092) (quote quote))
(list 'quote
(list->vector (cadr syntmp-x-1092))))
(else (list (quote list->vector) syntmp-x-1092)))))
(syntmp-gen-append-1086
(lambda (syntmp-x-1093 syntmp-y-1094)
(if (equal? syntmp-y-1094 (quote (quote ())))
syntmp-x-1093
(list (quote append) syntmp-x-1093 syntmp-y-1094))))
(syntmp-gen-cons-1085
(lambda (syntmp-x-1095 syntmp-y-1096)
(let ((syntmp-t-1097 (car syntmp-y-1096)))
(if (memv syntmp-t-1097 (quote (quote)))
(if (eq? (car syntmp-x-1095) (quote quote))
(list 'quote
(cons (cadr syntmp-x-1095)
(cadr syntmp-y-1096)))
(if (eq? (cadr syntmp-y-1096) (quote ()))
(list (quote list) syntmp-x-1095)
(list (quote cons) syntmp-x-1095 syntmp-y-1096)))
(if (memv syntmp-t-1097 (quote (list)))
(cons 'list
(cons syntmp-x-1095 (cdr syntmp-y-1096)))
(list (quote cons) syntmp-x-1095 syntmp-y-1096))))))
(syntmp-gen-map-1084
(lambda (syntmp-e-1098 syntmp-map-env-1099)
(let ((syntmp-formals-1100
(map cdr syntmp-map-env-1099))
(syntmp-actuals-1101
(map (lambda (syntmp-x-1102)
(list (quote ref) (car syntmp-x-1102)))
syntmp-map-env-1099)))
(cond ((eq? (car syntmp-e-1098) (quote ref))
(car syntmp-actuals-1101))
((andmap
(lambda (syntmp-x-1103)
(and (eq? (car syntmp-x-1103) (quote ref))
(memq (cadr syntmp-x-1103)
syntmp-formals-1100)))
(cdr syntmp-e-1098))
(cons 'map
(cons (list 'primitive
(car syntmp-e-1098))
(map (let ((syntmp-r-1104
(map cons
syntmp-formals-1100
syntmp-actuals-1101)))
(lambda (syntmp-x-1105)
(cdr (assq (cadr syntmp-x-1105)
syntmp-r-1104))))
(cdr syntmp-e-1098)))))
(else
(cons 'map
(cons (list 'lambda
syntmp-formals-1100
syntmp-e-1098)
syntmp-actuals-1101)))))))
(syntmp-gen-mappend-1083
(lambda (syntmp-e-1106 syntmp-map-env-1107)
(list 'apply
'(primitive append)
(syntmp-gen-map-1084
syntmp-e-1106
syntmp-map-env-1107))))
(syntmp-gen-ref-1082
(lambda (syntmp-src-1108
syntmp-var-1109
syntmp-level-1110
syntmp-maps-1111)
(if (syntmp-fx=-90 syntmp-level-1110 0)
(values syntmp-var-1109 syntmp-maps-1111)
(if (null? syntmp-maps-1111)
(syntax-error
syntmp-src-1108
"missing ellipsis in syntax form")
(call-with-values
(lambda ()
(syntmp-gen-ref-1082
syntmp-src-1108
syntmp-var-1109
(syntmp-fx--89 syntmp-level-1110 1)
(cdr syntmp-maps-1111)))
(lambda (syntmp-outer-var-1112 syntmp-outer-maps-1113)
(let ((syntmp-b-1114
(assq syntmp-outer-var-1112
(car syntmp-maps-1111))))
(if syntmp-b-1114
(values (cdr syntmp-b-1114) syntmp-maps-1111)
(let ((syntmp-inner-var-1115
(syntmp-gen-var-166 (quote tmp))))
(values
syntmp-inner-var-1115
(cons (cons (cons syntmp-outer-var-1112
syntmp-inner-var-1115)
(car syntmp-maps-1111))
syntmp-outer-maps-1113)))))))))))
(syntmp-gen-syntax-1081
(lambda (syntmp-src-1116
syntmp-e-1117
syntmp-r-1118
syntmp-maps-1119
syntmp-ellipsis?-1120)
(if (syntmp-id?-118 syntmp-e-1117)
(let ((syntmp-label-1121
(syntmp-id-var-name-140
syntmp-e-1117
'(()))))
(let ((syntmp-b-1122
(syntmp-lookup-115
syntmp-label-1121
syntmp-r-1118)))
(if (eq? (syntmp-binding-type-110 syntmp-b-1122)
'syntax)
(call-with-values
(lambda ()
(let ((syntmp-var.lev-1123
(syntmp-binding-value-111
syntmp-b-1122)))
(syntmp-gen-ref-1082
syntmp-src-1116
(car syntmp-var.lev-1123)
(cdr syntmp-var.lev-1123)
syntmp-maps-1119)))
(lambda (syntmp-var-1124 syntmp-maps-1125)
(values
(list (quote ref) syntmp-var-1124)
syntmp-maps-1125)))
(if (syntmp-ellipsis?-1120
syntmp-e-1117
syntmp-r-1118)
(syntax-error
syntmp-src-1116
"misplaced ellipsis in syntax form")
(values
(list (quote quote) syntmp-e-1117)
syntmp-maps-1119)))))
((lambda (syntmp-tmp-1126)
((lambda (syntmp-tmp-1127)
(if (if syntmp-tmp-1127
(apply (lambda (syntmp-dots-1128
syntmp-e-1129)
(syntmp-ellipsis?-1120
syntmp-dots-1128
syntmp-r-1118))
syntmp-tmp-1127)
#f)
(apply (lambda (syntmp-dots-1130 syntmp-e-1131)
(syntmp-gen-syntax-1081
syntmp-src-1116
syntmp-e-1131
syntmp-r-1118
syntmp-maps-1119
(lambda (syntmp-e-1132 syntmp-r-1133)
#f)))
syntmp-tmp-1127)
((lambda (syntmp-tmp-1134)
(if (if syntmp-tmp-1134
(apply (lambda (syntmp-x-1135
syntmp-dots-1136
syntmp-y-1137)
(syntmp-ellipsis?-1120
syntmp-dots-1136
syntmp-r-1118))
syntmp-tmp-1134)
#f)
(apply (lambda (syntmp-x-1138
syntmp-dots-1139
syntmp-y-1140)
(let syntmp-f-1141 ((syntmp-y-1142
syntmp-y-1140)
(syntmp-k-1143
(lambda (syntmp-maps-1144)
(call-with-values
(lambda ()
(syntmp-gen-syntax-1081
syntmp-src-1116
syntmp-x-1138
syntmp-r-1118
(cons '()
syntmp-maps-1144)
syntmp-ellipsis?-1120))
(lambda (syntmp-x-1145
syntmp-maps-1146)
(if (null? (car syntmp-maps-1146))
(syntax-error
syntmp-src-1116
"extra ellipsis in syntax form")
(values
(syntmp-gen-map-1084
syntmp-x-1145
(car syntmp-maps-1146))
(cdr syntmp-maps-1146))))))))
((lambda (syntmp-tmp-1147)
((lambda (syntmp-tmp-1148)
(if (if syntmp-tmp-1148
(apply (lambda (syntmp-dots-1149
syntmp-y-1150)
(syntmp-ellipsis?-1120
syntmp-dots-1149
syntmp-r-1118))
syntmp-tmp-1148)
#f)
(apply (lambda (syntmp-dots-1151
syntmp-y-1152)
(syntmp-f-1141
syntmp-y-1152
(lambda (syntmp-maps-1153)
(call-with-values
(lambda ()
(syntmp-k-1143
(cons '()
syntmp-maps-1153)))
(lambda (syntmp-x-1154
syntmp-maps-1155)
(if (null? (car syntmp-maps-1155))
(syntax-error
syntmp-src-1116
"extra ellipsis in syntax form")
(values
(syntmp-gen-mappend-1083
syntmp-x-1154
(car syntmp-maps-1155))
(cdr syntmp-maps-1155))))))))
syntmp-tmp-1148)
((lambda (syntmp-_-1156)
(call-with-values
(lambda ()
(syntmp-gen-syntax-1081
syntmp-src-1116
syntmp-y-1142
syntmp-r-1118
syntmp-maps-1119
syntmp-ellipsis?-1120))
(lambda (syntmp-y-1157
syntmp-maps-1158)
(call-with-values
(lambda ()
(syntmp-k-1143
syntmp-maps-1158))
(lambda (syntmp-x-1159
syntmp-maps-1160)
(values
(syntmp-gen-append-1086
syntmp-x-1159
syntmp-y-1157)
syntmp-maps-1160))))))
syntmp-tmp-1147)))
(syntax-dispatch
syntmp-tmp-1147
'(any . any))))
syntmp-y-1142)))
syntmp-tmp-1134)
((lambda (syntmp-tmp-1161)
(if syntmp-tmp-1161
(apply (lambda (syntmp-x-1162
syntmp-y-1163)
(call-with-values
(lambda ()
(syntmp-gen-syntax-1081
syntmp-src-1116
syntmp-x-1162
syntmp-r-1118
syntmp-maps-1119
syntmp-ellipsis?-1120))
(lambda (syntmp-x-1164
syntmp-maps-1165)
(call-with-values
(lambda ()
(syntmp-gen-syntax-1081
syntmp-src-1116
syntmp-y-1163
syntmp-r-1118
syntmp-maps-1165
syntmp-ellipsis?-1120))
(lambda (syntmp-y-1166
syntmp-maps-1167)
(values
(syntmp-gen-cons-1085
syntmp-x-1164
syntmp-y-1166)
syntmp-maps-1167))))))
syntmp-tmp-1161)
((lambda (syntmp-tmp-1168)
(if syntmp-tmp-1168
(apply (lambda (syntmp-e1-1169
syntmp-e2-1170)
(call-with-values
(lambda ()
(syntmp-gen-syntax-1081
syntmp-src-1116
(cons syntmp-e1-1169
syntmp-e2-1170)
syntmp-r-1118
syntmp-maps-1119
syntmp-ellipsis?-1120))
(lambda (syntmp-e-1172
syntmp-maps-1173)
(values
(syntmp-gen-vector-1087
syntmp-e-1172)
syntmp-maps-1173))))
syntmp-tmp-1168)
((lambda (syntmp-_-1174)
(values
(list 'quote
syntmp-e-1117)
syntmp-maps-1119))
syntmp-tmp-1126)))
(syntax-dispatch
syntmp-tmp-1126
'#(vector (any . each-any))))))
(syntax-dispatch
syntmp-tmp-1126
'(any . any)))))
(syntax-dispatch
syntmp-tmp-1126
'(any any . any)))))
(syntax-dispatch
syntmp-tmp-1126
'(any any))))
syntmp-e-1117)))))
(lambda (syntmp-e-1175
syntmp-r-1176
syntmp-w-1177
syntmp-s-1178)
(let ((syntmp-e-1179
(syntmp-source-wrap-147
syntmp-e-1175
syntmp-w-1177
syntmp-s-1178)))
((lambda (syntmp-tmp-1180)
((lambda (syntmp-tmp-1181)
(if syntmp-tmp-1181
(apply (lambda (syntmp-_-1182 syntmp-x-1183)
(call-with-values
(lambda ()
(syntmp-gen-syntax-1081
syntmp-e-1179
syntmp-x-1183
syntmp-r-1176
'()
syntmp-ellipsis?-163))
(lambda (syntmp-e-1184 syntmp-maps-1185)
(syntmp-regen-1088 syntmp-e-1184))))
syntmp-tmp-1181)
((lambda (syntmp-_-1186)
(syntax-error syntmp-e-1179))
syntmp-tmp-1180)))
(syntax-dispatch
syntmp-tmp-1180
'(any any))))
syntmp-e-1179)))))
(syntmp-global-extend-116
'core
'lambda
(lambda (syntmp-e-1187
syntmp-r-1188
syntmp-w-1189
syntmp-s-1190)
((lambda (syntmp-tmp-1191)
((lambda (syntmp-tmp-1192)
(if syntmp-tmp-1192
(apply (lambda (syntmp-_-1193 syntmp-c-1194)
(syntmp-chi-lambda-clause-159
(syntmp-source-wrap-147
syntmp-e-1187
syntmp-w-1189
syntmp-s-1190)
syntmp-c-1194
syntmp-r-1188
syntmp-w-1189
(lambda (syntmp-vars-1195 syntmp-body-1196)
(list 'lambda
syntmp-vars-1195
syntmp-body-1196))))
syntmp-tmp-1192)
(syntax-error syntmp-tmp-1191)))
(syntax-dispatch
syntmp-tmp-1191
'(any . any))))
syntmp-e-1187)))
(syntmp-global-extend-116
'core
'with-ellipsis
(lambda (syntmp-e-1197
syntmp-r-1198
syntmp-w-1199
syntmp-s-1200)
(let ((syntmp-tmp-1201 syntmp-e-1197))
(let ((syntmp-tmp-1202
(syntax-dispatch
syntmp-tmp-1201
'(_ any any . each-any))))
(if (and syntmp-tmp-1202
(apply (lambda (syntmp-dots-1203
syntmp-e1-1204
syntmp-e2-1205)
(syntmp-id?-118 syntmp-dots-1203))
syntmp-tmp-1202))
(apply (lambda (syntmp-dots-1206 syntmp-e1-1207 syntmp-e2-1208)
(let ((syntmp-id-1209
(if (symbol? syntmp-dots-1206)
'$sc-ellipsis
(syntmp-make-syntax-object-103
'$sc-ellipsis
(syntmp-syntax-object-wrap-106
syntmp-dots-1206)))))
(let ((syntmp-ids-1210 (list syntmp-id-1209))
(syntmp-labels-1211
(list (syntmp-gen-label-123)))
(syntmp-bindings-1212
(list (cons 'ellipsis
(syntmp-source-wrap-147
syntmp-dots-1206
syntmp-w-1199
syntmp-s-1200)))))
(let ((syntmp-nw-1213
(syntmp-make-binding-wrap-135
syntmp-ids-1210
syntmp-labels-1211
syntmp-w-1199))
(syntmp-nr-1214
(syntmp-extend-env-112
syntmp-labels-1211
syntmp-bindings-1212
syntmp-r-1198)))
(syntmp-chi-body-158
(cons syntmp-e1-1207 syntmp-e2-1208)
(syntmp-source-wrap-147
syntmp-e-1197
syntmp-nw-1213
syntmp-s-1200)
syntmp-nr-1214
syntmp-nw-1213)))))
syntmp-tmp-1202)
(syntax-error (quote with-ellipsis) "bad syntax"))))))
(syntmp-global-extend-116
'core
'let
(letrec ((syntmp-chi-let-1215
(lambda (syntmp-e-1216
syntmp-r-1217
syntmp-w-1218
syntmp-s-1219
syntmp-constructor-1220
syntmp-ids-1221
syntmp-vals-1222
syntmp-exps-1223)
(if (not (syntmp-valid-bound-ids?-143 syntmp-ids-1221))
(syntax-error
syntmp-e-1216
"duplicate bound variable in")
(let ((syntmp-labels-1224
(syntmp-gen-labels-124 syntmp-ids-1221))
(syntmp-new-vars-1225
(map syntmp-gen-var-166 syntmp-ids-1221)))
(let ((syntmp-nw-1226
(syntmp-make-binding-wrap-135
syntmp-ids-1221
syntmp-labels-1224
syntmp-w-1218))
(syntmp-nr-1227
(syntmp-extend-var-env-113
syntmp-labels-1224
syntmp-new-vars-1225
syntmp-r-1217)))
(syntmp-constructor-1220
syntmp-s-1219
syntmp-new-vars-1225
(map (lambda (syntmp-x-1228)
(syntmp-chi-154
syntmp-x-1228
syntmp-r-1217
syntmp-w-1218))
syntmp-vals-1222)
(syntmp-chi-body-158
syntmp-exps-1223
(syntmp-source-wrap-147
syntmp-e-1216
syntmp-nw-1226
syntmp-s-1219)
syntmp-nr-1227
syntmp-nw-1226))))))))
(lambda (syntmp-e-1229
syntmp-r-1230
syntmp-w-1231
syntmp-s-1232)
((lambda (syntmp-tmp-1233)
((lambda (syntmp-tmp-1234)
(if syntmp-tmp-1234
(apply (lambda (syntmp-_-1235
syntmp-id-1236
syntmp-val-1237
syntmp-e1-1238
syntmp-e2-1239)
(syntmp-chi-let-1215
syntmp-e-1229
syntmp-r-1230
syntmp-w-1231
syntmp-s-1232
syntmp-build-let-100
syntmp-id-1236
syntmp-val-1237
(cons syntmp-e1-1238 syntmp-e2-1239)))
syntmp-tmp-1234)
((lambda (syntmp-tmp-1243)
(if (if syntmp-tmp-1243
(apply (lambda (syntmp-_-1244
syntmp-f-1245
syntmp-id-1246
syntmp-val-1247
syntmp-e1-1248
syntmp-e2-1249)
(syntmp-id?-118 syntmp-f-1245))
syntmp-tmp-1243)
#f)
(apply (lambda (syntmp-_-1250
syntmp-f-1251
syntmp-id-1252
syntmp-val-1253
syntmp-e1-1254
syntmp-e2-1255)
(syntmp-chi-let-1215
syntmp-e-1229
syntmp-r-1230
syntmp-w-1231
syntmp-s-1232
syntmp-build-named-let-101
(cons syntmp-f-1251 syntmp-id-1252)
syntmp-val-1253
(cons syntmp-e1-1254 syntmp-e2-1255)))
syntmp-tmp-1243)
((lambda (syntmp-_-1259)
(syntax-error
(syntmp-source-wrap-147
syntmp-e-1229
syntmp-w-1231
syntmp-s-1232)))
syntmp-tmp-1233)))
(syntax-dispatch
syntmp-tmp-1233
'(any any #(each (any any)) any . each-any)))))
(syntax-dispatch
syntmp-tmp-1233
'(any #(each (any any)) any . each-any))))
syntmp-e-1229))))
(syntmp-global-extend-116
'core
'letrec
(lambda (syntmp-e-1260
syntmp-r-1261
syntmp-w-1262
syntmp-s-1263)
((lambda (syntmp-tmp-1264)
((lambda (syntmp-tmp-1265)
(if syntmp-tmp-1265
(apply (lambda (syntmp-_-1266
syntmp-id-1267
syntmp-val-1268
syntmp-e1-1269
syntmp-e2-1270)
(let ((syntmp-ids-1271 syntmp-id-1267))
(if (not (syntmp-valid-bound-ids?-143
syntmp-ids-1271))
(syntax-error
syntmp-e-1260
"duplicate bound variable in")
(let ((syntmp-labels-1273
(syntmp-gen-labels-124 syntmp-ids-1271))
(syntmp-new-vars-1274
(map syntmp-gen-var-166 syntmp-ids-1271)))
(let ((syntmp-w-1275
(syntmp-make-binding-wrap-135
syntmp-ids-1271
syntmp-labels-1273
syntmp-w-1262))
(syntmp-r-1276
(syntmp-extend-var-env-113
syntmp-labels-1273
syntmp-new-vars-1274
syntmp-r-1261)))
(syntmp-build-letrec-102
syntmp-s-1263
syntmp-new-vars-1274
(map (lambda (syntmp-x-1277)
(syntmp-chi-154
syntmp-x-1277
syntmp-r-1276
syntmp-w-1275))
syntmp-val-1268)
(syntmp-chi-body-158
(cons syntmp-e1-1269 syntmp-e2-1270)
(syntmp-source-wrap-147
syntmp-e-1260
syntmp-w-1275
syntmp-s-1263)
syntmp-r-1276
syntmp-w-1275)))))))
syntmp-tmp-1265)
((lambda (syntmp-_-1280)
(syntax-error
(syntmp-source-wrap-147
syntmp-e-1260
syntmp-w-1262
syntmp-s-1263)))
syntmp-tmp-1264)))
(syntax-dispatch
syntmp-tmp-1264
'(any #(each (any any)) any . each-any))))
syntmp-e-1260)))
(syntmp-global-extend-116
'core
'set!
(lambda (syntmp-e-1281
syntmp-r-1282
syntmp-w-1283
syntmp-s-1284)
((lambda (syntmp-tmp-1285)
((lambda (syntmp-tmp-1286)
(if (if syntmp-tmp-1286
(apply (lambda (syntmp-_-1287
syntmp-id-1288
syntmp-val-1289)
(syntmp-id?-118 syntmp-id-1288))
syntmp-tmp-1286)
#f)
(apply (lambda (syntmp-_-1290 syntmp-id-1291 syntmp-val-1292)
(let ((syntmp-val-1293
(syntmp-chi-154
syntmp-val-1292
syntmp-r-1282
syntmp-w-1283))
(syntmp-n-1294
(syntmp-id-var-name-140
syntmp-id-1291
syntmp-w-1283)))
(let ((syntmp-b-1295
(syntmp-lookup-115
syntmp-n-1294
syntmp-r-1282)))
(let ((syntmp-t-1296
(syntmp-binding-type-110 syntmp-b-1295)))
(if (memv syntmp-t-1296 (quote (lexical)))
(list 'set!
(syntmp-binding-value-111 syntmp-b-1295)
syntmp-val-1293)
(if (memv syntmp-t-1296 (quote (global)))
(list 'set!
syntmp-n-1294
syntmp-val-1293)
(if (memv syntmp-t-1296
'(displaced-lexical))
(syntax-error
(syntmp-wrap-146
syntmp-id-1291
syntmp-w-1283)
"identifier out of context")
(syntax-error
(syntmp-source-wrap-147
syntmp-e-1281
syntmp-w-1283
syntmp-s-1284)))))))))
syntmp-tmp-1286)
((lambda (syntmp-tmp-1297)
(if syntmp-tmp-1297
(apply (lambda (syntmp-_-1298
syntmp-getter-1299
syntmp-arg-1300
syntmp-val-1301)
(cons (syntmp-chi-154
(list '#(syntax-object
setter
((top)
#(ribcage
#(_ getter arg val)
#((top) (top) (top) (top))
#("i" "i" "i" "i"))
#(ribcage () () ())
#(ribcage
#(e r w s)
#((top) (top) (top) (top))
#("i" "i" "i" "i"))
#(ribcage
(lambda-var-list
gen-var
strip
strip-annotation
ellipsis?
chi-void
eval-local-transformer
chi-local-syntax
chi-lambda-clause
chi-body
chi-macro
chi-application
chi-expr
chi
chi-top
syntax-type
chi-when-list
chi-install-global
chi-top-sequence
chi-sequence
source-wrap
wrap
bound-id-member?
distinct-bound-ids?
valid-bound-ids?
bound-id=?
free-id=?
id-var-name
same-marks?
join-marks
join-wraps
smart-append
make-binding-wrap
extend-ribcage!
make-empty-ribcage
new-mark
anti-mark
the-anti-mark
top-marked?
top-wrap
empty-wrap
set-ribcage-labels!
set-ribcage-marks!
set-ribcage-symnames!
ribcage-labels
ribcage-marks
ribcage-symnames
ribcage?
make-ribcage
gen-labels
gen-label
make-rename
rename-marks
rename-new
rename-old
subst-rename?
wrap-subst
wrap-marks
make-wrap
id-sym-name&marks
id-sym-name
id?
nonsymbol-id?
global-extend
lookup
macros-only-env
extend-var-env
extend-env
null-env
binding-value
binding-type
make-binding
arg-check
source-annotation
no-source
unannotate
set-syntax-object-wrap!
set-syntax-object-expression!
syntax-object-wrap
syntax-object-expression
syntax-object?
make-syntax-object
build-lexical-var
build-letrec
build-named-let
build-let
build-sequence
build-data
build-primref
build-lambda
build-global-definition
build-global-assignment
build-global-reference
build-lexical-assignment
build-lexical-reference
build-conditional
build-application
get-global-definition-hook
put-global-definition-hook
gensym-hook
error-hook
local-eval-hook
top-level-eval-hook
annotation?
fx<
fx=
fx-
fx+
noexpand)
((top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top))
("i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"))
#(ribcage
(define-structure)
((top))
("i"))))
syntmp-getter-1299)
syntmp-r-1282
syntmp-w-1283)
(map (lambda (syntmp-e-1302)
(syntmp-chi-154
syntmp-e-1302
syntmp-r-1282
syntmp-w-1283))
(append
syntmp-arg-1300
(list syntmp-val-1301)))))
syntmp-tmp-1297)
((lambda (syntmp-_-1304)
(syntax-error
(syntmp-source-wrap-147
syntmp-e-1281
syntmp-w-1283
syntmp-s-1284)))
syntmp-tmp-1285)))
(syntax-dispatch
syntmp-tmp-1285
'(any (any . each-any) any)))))
(syntax-dispatch
syntmp-tmp-1285
'(any any any))))
syntmp-e-1281)))
(syntmp-global-extend-116
'begin
'begin
'())
(syntmp-global-extend-116
'define
'define
'())
(syntmp-global-extend-116
'define-syntax
'define-syntax
'())
(syntmp-global-extend-116
'eval-when
'eval-when
'())
(syntmp-global-extend-116
'core
'syntax-case
(letrec ((syntmp-gen-syntax-case-1308
(lambda (syntmp-x-1309
syntmp-keys-1310
syntmp-clauses-1311
syntmp-r-1312)
(if (null? syntmp-clauses-1311)
(list (quote syntax-error) syntmp-x-1309)
((lambda (syntmp-tmp-1313)
((lambda (syntmp-tmp-1314)
(if syntmp-tmp-1314
(apply (lambda (syntmp-pat-1315 syntmp-exp-1316)
(if (and (syntmp-id?-118 syntmp-pat-1315)
(andmap
(lambda (syntmp-x-1317)
(not (syntmp-free-id=?-141
syntmp-pat-1315
syntmp-x-1317)))
(cons '#(syntax-object
...
((top)
#(ribcage
#(pat exp)
#((top) (top))
#("i" "i"))
#(ribcage () () ())
#(ribcage
#(x
keys
clauses
r)
#((top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"))
#(ribcage
(gen-syntax-case
gen-clause
build-dispatch-call
convert-pattern)
((top)
(top)
(top)
(top))
("i" "i" "i" "i"))
#(ribcage
(lambda-var-list
gen-var
strip
strip-annotation
ellipsis?
chi-void
eval-local-transformer
chi-local-syntax
chi-lambda-clause
chi-body
chi-macro
chi-application
chi-expr
chi
chi-top
syntax-type
chi-when-list
chi-install-global
chi-top-sequence
chi-sequence
source-wrap
wrap
bound-id-member?
distinct-bound-ids?
valid-bound-ids?
bound-id=?
free-id=?
id-var-name
same-marks?
join-marks
join-wraps
smart-append
make-binding-wrap
extend-ribcage!
make-empty-ribcage
new-mark
anti-mark
the-anti-mark
top-marked?
top-wrap
empty-wrap
set-ribcage-labels!
set-ribcage-marks!
set-ribcage-symnames!
ribcage-labels
ribcage-marks
ribcage-symnames
ribcage?
make-ribcage
gen-labels
gen-label
make-rename
rename-marks
rename-new
rename-old
subst-rename?
wrap-subst
wrap-marks
make-wrap
id-sym-name&marks
id-sym-name
id?
nonsymbol-id?
global-extend
lookup
macros-only-env
extend-var-env
extend-env
null-env
binding-value
binding-type
make-binding
arg-check
source-annotation
no-source
unannotate
set-syntax-object-wrap!
set-syntax-object-expression!
syntax-object-wrap
syntax-object-expression
syntax-object?
make-syntax-object
build-lexical-var
build-letrec
build-named-let
build-let
build-sequence
build-data
build-primref
build-lambda
build-global-definition
build-global-assignment
build-global-reference
build-lexical-assignment
build-lexical-reference
build-conditional
build-application
get-global-definition-hook
put-global-definition-hook
gensym-hook
error-hook
local-eval-hook
top-level-eval-hook
annotation?
fx<
fx=
fx-
fx+
noexpand)
((top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top)
(top))
("i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"
"i"))
#(ribcage
(define-structure)
((top))
("i"))))
syntmp-keys-1310)))
(let ((syntmp-labels-1318
(list (syntmp-gen-label-123)))
(syntmp-var-1319
(syntmp-gen-var-166
syntmp-pat-1315)))
(list (list 'lambda
(list syntmp-var-1319)
(syntmp-chi-154
syntmp-exp-1316
(syntmp-extend-env-112
syntmp-labels-1318
(list (cons 'syntax
(cons syntmp-var-1319
0)))
syntmp-r-1312)
(syntmp-make-binding-wrap-135
(list syntmp-pat-1315)
syntmp-labels-1318
'(()))))
syntmp-x-1309))
(syntmp-gen-clause-1307
syntmp-x-1309
syntmp-keys-1310
(cdr syntmp-clauses-1311)
syntmp-r-1312
syntmp-pat-1315
#t
syntmp-exp-1316)))
syntmp-tmp-1314)
((lambda (syntmp-tmp-1320)
(if syntmp-tmp-1320
(apply (lambda (syntmp-pat-1321
syntmp-fender-1322
syntmp-exp-1323)
(syntmp-gen-clause-1307
syntmp-x-1309
syntmp-keys-1310
(cdr syntmp-clauses-1311)
syntmp-r-1312
syntmp-pat-1321
syntmp-fender-1322
syntmp-exp-1323))
syntmp-tmp-1320)
((lambda (syntmp-_-1324)
(syntax-error
(car syntmp-clauses-1311)
"invalid syntax-case clause"))
syntmp-tmp-1313)))
(syntax-dispatch
syntmp-tmp-1313
'(any any any)))))
(syntax-dispatch
syntmp-tmp-1313
'(any any))))
(car syntmp-clauses-1311)))))
(syntmp-gen-clause-1307
(lambda (syntmp-x-1325
syntmp-keys-1326
syntmp-clauses-1327
syntmp-r-1328
syntmp-pat-1329
syntmp-fender-1330
syntmp-exp-1331)
(call-with-values
(lambda ()
(syntmp-convert-pattern-1305
syntmp-pat-1329
syntmp-keys-1326
(lambda (syntmp-e-1332)
(syntmp-ellipsis?-163
syntmp-e-1332
syntmp-r-1328))))
(lambda (syntmp-p-1333 syntmp-pvars-1334)
(cond ((not (syntmp-distinct-bound-ids?-144
(map car syntmp-pvars-1334)))
(syntax-error
syntmp-pat-1329
"duplicate pattern variable in syntax-case pattern"))
((not (andmap
(lambda (syntmp-x-1335)
(not (syntmp-ellipsis?-163
(car syntmp-x-1335)
syntmp-r-1328)))
syntmp-pvars-1334))
(syntax-error
syntmp-pat-1329
"misplaced ellipsis in syntax-case pattern"))
(else
(let ((syntmp-y-1336
(syntmp-gen-var-166 (quote tmp))))
(list (list 'lambda
(list syntmp-y-1336)
(let ((syntmp-y-1337
syntmp-y-1336))
(list 'if
((lambda (syntmp-tmp-1338)
((lambda (syntmp-tmp-1339)
(if syntmp-tmp-1339
(apply (lambda ()
syntmp-y-1337)
syntmp-tmp-1339)
((lambda (syntmp-_-1340)
(list 'if
syntmp-y-1337
(syntmp-build-dispatch-call-1306
syntmp-pvars-1334
syntmp-fender-1330
syntmp-y-1337
syntmp-r-1328)
(syntmp-build-data-98
#f
#f)))
syntmp-tmp-1338)))
(syntax-dispatch
syntmp-tmp-1338
'#(atom #t))))
syntmp-fender-1330)
(syntmp-build-dispatch-call-1306
syntmp-pvars-1334
syntmp-exp-1331
syntmp-y-1337
syntmp-r-1328)
(syntmp-gen-syntax-case-1308
syntmp-x-1325
syntmp-keys-1326
syntmp-clauses-1327
syntmp-r-1328))))
(if (eq? syntmp-p-1333 (quote any))
(list (quote list) syntmp-x-1325)
(list 'syntax-dispatch
syntmp-x-1325
(syntmp-build-data-98
#f
syntmp-p-1333)))))))))))
(syntmp-build-dispatch-call-1306
(lambda (syntmp-pvars-1341
syntmp-exp-1342
syntmp-y-1343
syntmp-r-1344)
(let ((syntmp-ids-1345 (map car syntmp-pvars-1341))
(syntmp-levels-1346 (map cdr syntmp-pvars-1341)))
(let ((syntmp-labels-1347
(syntmp-gen-labels-124 syntmp-ids-1345))
(syntmp-new-vars-1348
(map syntmp-gen-var-166 syntmp-ids-1345)))
(list 'apply
(list 'lambda
syntmp-new-vars-1348
(syntmp-chi-154
syntmp-exp-1342
(syntmp-extend-env-112
syntmp-labels-1347
(map (lambda (syntmp-var-1349
syntmp-level-1350)
(cons 'syntax
(cons syntmp-var-1349
syntmp-level-1350)))
syntmp-new-vars-1348
(map cdr syntmp-pvars-1341))
syntmp-r-1344)
(syntmp-make-binding-wrap-135
syntmp-ids-1345
syntmp-labels-1347
'(()))))
syntmp-y-1343)))))
(syntmp-convert-pattern-1305
(lambda (syntmp-pattern-1351
syntmp-keys-1352
syntmp-ellipsis?-1353)
(let syntmp-cvt-1354 ((syntmp-p-1355 syntmp-pattern-1351)
(syntmp-n-1356 0)
(syntmp-ids-1357 (quote ())))
(if (syntmp-id?-118 syntmp-p-1355)
(if (syntmp-bound-id-member?-145
syntmp-p-1355
syntmp-keys-1352)
(values
(vector (quote free-id) syntmp-p-1355)
syntmp-ids-1357)
(values
'any
(cons (cons syntmp-p-1355 syntmp-n-1356)
syntmp-ids-1357)))
((lambda (syntmp-tmp-1358)
((lambda (syntmp-tmp-1359)
(if (if syntmp-tmp-1359
(apply (lambda (syntmp-x-1360
syntmp-dots-1361)
(syntmp-ellipsis?-1353
syntmp-dots-1361))
syntmp-tmp-1359)
#f)
(apply (lambda (syntmp-x-1362 syntmp-dots-1363)
(call-with-values
(lambda ()
(syntmp-cvt-1354
syntmp-x-1362
(syntmp-fx+-88 syntmp-n-1356 1)
syntmp-ids-1357))
(lambda (syntmp-p-1364
syntmp-ids-1365)
(values
(if (eq? syntmp-p-1364
'any)
'each-any
(vector
'each
syntmp-p-1364))
syntmp-ids-1365))))
syntmp-tmp-1359)
((lambda (syntmp-tmp-1366)
(if syntmp-tmp-1366
(apply (lambda (syntmp-x-1367
syntmp-y-1368)
(call-with-values
(lambda ()
(syntmp-cvt-1354
syntmp-y-1368
syntmp-n-1356
syntmp-ids-1357))
(lambda (syntmp-y-1369
syntmp-ids-1370)
(call-with-values
(lambda ()
(syntmp-cvt-1354
syntmp-x-1367
syntmp-n-1356
syntmp-ids-1370))
(lambda (syntmp-x-1371
syntmp-ids-1372)
(values
(cons syntmp-x-1371
syntmp-y-1369)
syntmp-ids-1372))))))
syntmp-tmp-1366)
((lambda (syntmp-tmp-1373)
(if syntmp-tmp-1373
(apply (lambda ()
(values
'()
syntmp-ids-1357))
syntmp-tmp-1373)
((lambda (syntmp-tmp-1374)
(if syntmp-tmp-1374
(apply (lambda (syntmp-x-1375)
(call-with-values
(lambda ()
(syntmp-cvt-1354
syntmp-x-1375
syntmp-n-1356
syntmp-ids-1357))
(lambda (syntmp-p-1377
syntmp-ids-1378)
(values
(vector
'vector
syntmp-p-1377)
syntmp-ids-1378))))
syntmp-tmp-1374)
((lambda (syntmp-x-1379)
(values
(vector
'atom
(syntmp-strip-165
syntmp-p-1355
'(())))
syntmp-ids-1357))
syntmp-tmp-1358)))
(syntax-dispatch
syntmp-tmp-1358
'#(vector each-any)))))
(syntax-dispatch
syntmp-tmp-1358
'()))))
(syntax-dispatch
syntmp-tmp-1358
'(any . any)))))
(syntax-dispatch
syntmp-tmp-1358
'(any any))))
syntmp-p-1355))))))
(lambda (syntmp-e-1380
syntmp-r-1381
syntmp-w-1382
syntmp-s-1383)
(let ((syntmp-e-1384
(syntmp-source-wrap-147
syntmp-e-1380
syntmp-w-1382
syntmp-s-1383)))
((lambda (syntmp-tmp-1385)
((lambda (syntmp-tmp-1386)
(if syntmp-tmp-1386
(apply (lambda (syntmp-_-1387
syntmp-val-1388
syntmp-key-1389
syntmp-m-1390)
(if (andmap
(lambda (syntmp-x-1391)
(and (syntmp-id?-118 syntmp-x-1391)
(not (syntmp-ellipsis?-163
syntmp-x-1391
syntmp-r-1381))))
syntmp-key-1389)
(let ((syntmp-x-1393
(syntmp-gen-var-166 (quote tmp))))
(list (list 'lambda
(list syntmp-x-1393)
(syntmp-gen-syntax-case-1308
syntmp-x-1393
syntmp-key-1389
syntmp-m-1390
syntmp-r-1381))
(syntmp-chi-154
syntmp-val-1388
syntmp-r-1381
'(()))))
(syntax-error
syntmp-e-1384
"invalid literals list in")))
syntmp-tmp-1386)
(syntax-error syntmp-tmp-1385)))
(syntax-dispatch
syntmp-tmp-1385
'(any any each-any . each-any))))
syntmp-e-1384)))))
(set! sc-expand
(let ((syntmp-m-1396 (quote e))
(syntmp-esew-1397 (quote (eval))))
(lambda (syntmp-x-1398)
(if (and (pair? syntmp-x-1398)
(equal? (car syntmp-x-1398) syntmp-noexpand-87))
(cadr syntmp-x-1398)
(syntmp-chi-top-153
syntmp-x-1398
'()
'((top))
syntmp-m-1396
syntmp-esew-1397)))))
(set! sc-expand3
(let ((syntmp-m-1399 (quote e))
(syntmp-esew-1400 (quote (eval))))
(lambda (syntmp-x-1402 . syntmp-rest-1401)
(if (and (pair? syntmp-x-1402)
(equal? (car syntmp-x-1402) syntmp-noexpand-87))
(cadr syntmp-x-1402)
(syntmp-chi-top-153
syntmp-x-1402
'()
'((top))
(if (null? syntmp-rest-1401)
syntmp-m-1399
(car syntmp-rest-1401))
(if (or (null? syntmp-rest-1401)
(null? (cdr syntmp-rest-1401)))
syntmp-esew-1400
(cadr syntmp-rest-1401)))))))
(set! identifier?
(lambda (syntmp-x-1403)
(syntmp-nonsymbol-id?-117 syntmp-x-1403)))
(set! datum->syntax-object
(lambda (syntmp-id-1404 syntmp-datum-1405)
(syntmp-make-syntax-object-103
syntmp-datum-1405
(syntmp-syntax-object-wrap-106 syntmp-id-1404))))
(set! syntax-object->datum
(lambda (syntmp-x-1406)
(syntmp-strip-165 syntmp-x-1406 (quote (())))))
(set! generate-temporaries
(lambda (syntmp-ls-1407)
(begin
(let ((syntmp-x-1408 syntmp-ls-1407))
(if (not (list? syntmp-x-1408))
(syntmp-error-hook-95
'generate-temporaries
"invalid argument"
syntmp-x-1408)))
(map (lambda (syntmp-x-1409)
(syntmp-wrap-146 (gensym) (quote ((top)))))
syntmp-ls-1407))))
(set! free-identifier=?
(lambda (syntmp-x-1410 syntmp-y-1411)
(begin
(let ((syntmp-x-1412 syntmp-x-1410))
(if (not (syntmp-nonsymbol-id?-117 syntmp-x-1412))
(syntmp-error-hook-95
'free-identifier=?
"invalid argument"
syntmp-x-1412)))
(let ((syntmp-x-1413 syntmp-y-1411))
(if (not (syntmp-nonsymbol-id?-117 syntmp-x-1413))
(syntmp-error-hook-95
'free-identifier=?
"invalid argument"
syntmp-x-1413)))
(syntmp-free-id=?-141
syntmp-x-1410
syntmp-y-1411))))
(set! bound-identifier=?
(lambda (syntmp-x-1414 syntmp-y-1415)
(begin
(let ((syntmp-x-1416 syntmp-x-1414))
(if (not (syntmp-nonsymbol-id?-117 syntmp-x-1416))
(syntmp-error-hook-95
'bound-identifier=?
"invalid argument"
syntmp-x-1416)))
(let ((syntmp-x-1417 syntmp-y-1415))
(if (not (syntmp-nonsymbol-id?-117 syntmp-x-1417))
(syntmp-error-hook-95
'bound-identifier=?
"invalid argument"
syntmp-x-1417)))
(syntmp-bound-id=?-142
syntmp-x-1414
syntmp-y-1415))))
(set! syntax-error
(lambda (syntmp-object-1419 . syntmp-messages-1418)
(begin
(for-each
(lambda (syntmp-x-1420)
(let ((syntmp-x-1421 syntmp-x-1420))
(if (not (string? syntmp-x-1421))
(syntmp-error-hook-95
'syntax-error
"invalid argument"
syntmp-x-1421))))
syntmp-messages-1418)
(let ((syntmp-message-1422
(if (null? syntmp-messages-1418)
"invalid syntax"
(apply string-append syntmp-messages-1418))))
(syntmp-error-hook-95
#f
syntmp-message-1422
(syntmp-strip-165
syntmp-object-1419
'(())))))))
(set! install-global-transformer
(lambda (syntmp-sym-1423 syntmp-v-1424)
(begin
(let ((syntmp-x-1425 syntmp-sym-1423))
(if (not (symbol? syntmp-x-1425))
(syntmp-error-hook-95
'define-syntax
"invalid argument"
syntmp-x-1425)))
(let ((syntmp-x-1426 syntmp-v-1424))
(if (not (procedure? syntmp-x-1426))
(syntmp-error-hook-95
'define-syntax
"invalid argument"
syntmp-x-1426)))
(syntmp-global-extend-116
'macro
syntmp-sym-1423
syntmp-v-1424))))
(letrec ((syntmp-match-1431
(lambda (syntmp-e-1432
syntmp-p-1433
syntmp-w-1434
syntmp-r-1435)
(cond ((not syntmp-r-1435) #f)
((eq? syntmp-p-1433 (quote _)) syntmp-r-1435)
((eq? syntmp-p-1433 (quote any))
(cons (syntmp-wrap-146 syntmp-e-1432 syntmp-w-1434)
syntmp-r-1435))
((syntmp-syntax-object?-104 syntmp-e-1432)
(syntmp-match*-1430
(let ((syntmp-e-1436
(syntmp-syntax-object-expression-105
syntmp-e-1432)))
(if (syntmp-annotation?-92 syntmp-e-1436)
(annotation-expression syntmp-e-1436)
syntmp-e-1436))
syntmp-p-1433
(syntmp-join-wraps-137
syntmp-w-1434
(syntmp-syntax-object-wrap-106 syntmp-e-1432))
syntmp-r-1435))
(else
(syntmp-match*-1430
(let ((syntmp-e-1437 syntmp-e-1432))
(if (syntmp-annotation?-92 syntmp-e-1437)
(annotation-expression syntmp-e-1437)
syntmp-e-1437))
syntmp-p-1433
syntmp-w-1434
syntmp-r-1435)))))
(syntmp-match*-1430
(lambda (syntmp-e-1438
syntmp-p-1439
syntmp-w-1440
syntmp-r-1441)
(cond ((null? syntmp-p-1439)
(and (null? syntmp-e-1438) syntmp-r-1441))
((pair? syntmp-p-1439)
(and (pair? syntmp-e-1438)
(syntmp-match-1431
(car syntmp-e-1438)
(car syntmp-p-1439)
syntmp-w-1440
(syntmp-match-1431
(cdr syntmp-e-1438)
(cdr syntmp-p-1439)
syntmp-w-1440
syntmp-r-1441))))
((eq? syntmp-p-1439 (quote each-any))
(let ((syntmp-l-1442
(syntmp-match-each-any-1428
syntmp-e-1438
syntmp-w-1440)))
(and syntmp-l-1442
(cons syntmp-l-1442 syntmp-r-1441))))
(else
(let ((syntmp-t-1443 (vector-ref syntmp-p-1439 0)))
(if (memv syntmp-t-1443 (quote (each)))
(if (null? syntmp-e-1438)
(syntmp-match-empty-1429
(vector-ref syntmp-p-1439 1)
syntmp-r-1441)
(let ((syntmp-l-1444
(syntmp-match-each-1427
syntmp-e-1438
(vector-ref syntmp-p-1439 1)
syntmp-w-1440)))
(and syntmp-l-1444
(let syntmp-collect-1445 ((syntmp-l-1446
syntmp-l-1444))
(if (null? (car syntmp-l-1446))
syntmp-r-1441
(cons (map car syntmp-l-1446)
(syntmp-collect-1445
(map cdr syntmp-l-1446))))))))
(if (memv syntmp-t-1443 (quote (free-id)))
(and (syntmp-id?-118 syntmp-e-1438)
(syntmp-free-id=?-141
(syntmp-wrap-146
syntmp-e-1438
syntmp-w-1440)
(vector-ref syntmp-p-1439 1))
syntmp-r-1441)
(if (memv syntmp-t-1443 (quote (atom)))
(and (equal?
(vector-ref syntmp-p-1439 1)
(syntmp-strip-165
syntmp-e-1438
syntmp-w-1440))
syntmp-r-1441)
(if (memv syntmp-t-1443 (quote (vector)))
(and (vector? syntmp-e-1438)
(syntmp-match-1431
(vector->list syntmp-e-1438)
(vector-ref syntmp-p-1439 1)
syntmp-w-1440
syntmp-r-1441)))))))))))
(syntmp-match-empty-1429
(lambda (syntmp-p-1447 syntmp-r-1448)
(cond ((null? syntmp-p-1447) syntmp-r-1448)
((eq? syntmp-p-1447 (quote _)) syntmp-r-1448)
((eq? syntmp-p-1447 (quote any))
(cons (quote ()) syntmp-r-1448))
((pair? syntmp-p-1447)
(syntmp-match-empty-1429
(car syntmp-p-1447)
(syntmp-match-empty-1429
(cdr syntmp-p-1447)
syntmp-r-1448)))
((eq? syntmp-p-1447 (quote each-any))
(cons (quote ()) syntmp-r-1448))
(else
(let ((syntmp-t-1449 (vector-ref syntmp-p-1447 0)))
(if (memv syntmp-t-1449 (quote (each)))
(syntmp-match-empty-1429
(vector-ref syntmp-p-1447 1)
syntmp-r-1448)
(if (memv syntmp-t-1449 (quote (free-id atom)))
syntmp-r-1448
(if (memv syntmp-t-1449 (quote (vector)))
(syntmp-match-empty-1429
(vector-ref syntmp-p-1447 1)
syntmp-r-1448)))))))))
(syntmp-match-each-any-1428
(lambda (syntmp-e-1450 syntmp-w-1451)
(cond ((syntmp-annotation?-92 syntmp-e-1450)
(syntmp-match-each-any-1428
(annotation-expression syntmp-e-1450)
syntmp-w-1451))
((pair? syntmp-e-1450)
(let ((syntmp-l-1452
(syntmp-match-each-any-1428
(cdr syntmp-e-1450)
syntmp-w-1451)))
(and syntmp-l-1452
(cons (syntmp-wrap-146
(car syntmp-e-1450)
syntmp-w-1451)
syntmp-l-1452))))
((null? syntmp-e-1450) (quote ()))
((syntmp-syntax-object?-104 syntmp-e-1450)
(syntmp-match-each-any-1428
(syntmp-syntax-object-expression-105
syntmp-e-1450)
(syntmp-join-wraps-137
syntmp-w-1451
(syntmp-syntax-object-wrap-106 syntmp-e-1450))))
(else #f))))
(syntmp-match-each-1427
(lambda (syntmp-e-1453 syntmp-p-1454 syntmp-w-1455)
(cond ((syntmp-annotation?-92 syntmp-e-1453)
(syntmp-match-each-1427
(annotation-expression syntmp-e-1453)
syntmp-p-1454
syntmp-w-1455))
((pair? syntmp-e-1453)
(let ((syntmp-first-1456
(syntmp-match-1431
(car syntmp-e-1453)
syntmp-p-1454
syntmp-w-1455
'())))
(and syntmp-first-1456
(let ((syntmp-rest-1457
(syntmp-match-each-1427
(cdr syntmp-e-1453)
syntmp-p-1454
syntmp-w-1455)))
(and syntmp-rest-1457
(cons syntmp-first-1456
syntmp-rest-1457))))))
((null? syntmp-e-1453) (quote ()))
((syntmp-syntax-object?-104 syntmp-e-1453)
(syntmp-match-each-1427
(syntmp-syntax-object-expression-105
syntmp-e-1453)
syntmp-p-1454
(syntmp-join-wraps-137
syntmp-w-1455
(syntmp-syntax-object-wrap-106 syntmp-e-1453))))
(else #f)))))
(begin
(set! syntax-dispatch
(lambda (syntmp-e-1458 syntmp-p-1459)
(cond ((eq? syntmp-p-1459 (quote any))
(list syntmp-e-1458))
((eq? syntmp-p-1459 (quote _)) (quote ()))
((syntmp-syntax-object?-104 syntmp-e-1458)
(syntmp-match*-1430
(let ((syntmp-e-1460
(syntmp-syntax-object-expression-105
syntmp-e-1458)))
(if (syntmp-annotation?-92 syntmp-e-1460)
(annotation-expression syntmp-e-1460)
syntmp-e-1460))
syntmp-p-1459
(syntmp-syntax-object-wrap-106 syntmp-e-1458)
'()))
(else
(syntmp-match*-1430
(let ((syntmp-e-1461 syntmp-e-1458))
(if (syntmp-annotation?-92 syntmp-e-1461)
(annotation-expression syntmp-e-1461)
syntmp-e-1461))
syntmp-p-1459
'(())
'())))))
(set! sc-chi syntmp-chi-154)))))
(install-global-transformer
'with-syntax
(lambda (syntmp-x-1462)
((lambda (syntmp-tmp-1463)
((lambda (syntmp-tmp-1464)
(if syntmp-tmp-1464
(apply (lambda (syntmp-_-1465 syntmp-e1-1466 syntmp-e2-1467)
(cons '#(syntax-object
let
((top)
#(ribcage
#(_ e1 e2)
#((top) (top) (top))
#("i" "i" "i"))
#(ribcage () () ())
#(ribcage #(x) #((top)) #("i"))))
(cons '()
(cons syntmp-e1-1466 syntmp-e2-1467))))
syntmp-tmp-1464)
((lambda (syntmp-tmp-1469)
(if syntmp-tmp-1469
(apply (lambda (syntmp-_-1470
syntmp-out-1471
syntmp-in-1472
syntmp-e1-1473
syntmp-e2-1474)
(list '#(syntax-object
syntax-case
((top)
#(ribcage
#(_ out in e1 e2)
#((top) (top) (top) (top) (top))
#("i" "i" "i" "i" "i"))
#(ribcage () () ())
#(ribcage #(x) #((top)) #("i"))))
syntmp-in-1472
'()
(list syntmp-out-1471
(cons '#(syntax-object
let
((top)
#(ribcage
#(_ out in e1 e2)
#((top)
(top)
(top)
(top)
(top))
#("i" "i" "i" "i" "i"))
#(ribcage () () ())
#(ribcage
#(x)
#((top))
#("i"))))
(cons '()
(cons syntmp-e1-1473
syntmp-e2-1474))))))
syntmp-tmp-1469)
((lambda (syntmp-tmp-1476)
(if syntmp-tmp-1476
(apply (lambda (syntmp-_-1477
syntmp-out-1478
syntmp-in-1479
syntmp-e1-1480
syntmp-e2-1481)
(list '#(syntax-object
syntax-case
((top)
#(ribcage
#(_ out in e1 e2)
#((top) (top) (top) (top) (top))
#("i" "i" "i" "i" "i"))
#(ribcage () () ())
#(ribcage #(x) #((top)) #("i"))))
(cons '#(syntax-object
list
((top)
#(ribcage
#(_ out in e1 e2)
#((top)
(top)
(top)
(top)
(top))
#("i" "i" "i" "i" "i"))
#(ribcage () () ())
#(ribcage
#(x)
#((top))
#("i"))))
syntmp-in-1479)
'()
(list syntmp-out-1478
(cons '#(syntax-object
let
((top)
#(ribcage
#(_ out in e1 e2)
#((top)
(top)
(top)
(top)
(top))
#("i" "i" "i" "i" "i"))
#(ribcage () () ())
#(ribcage
#(x)
#((top))
#("i"))))
(cons '()
(cons syntmp-e1-1480
syntmp-e2-1481))))))
syntmp-tmp-1476)
(syntax-error syntmp-tmp-1463)))
(syntax-dispatch
syntmp-tmp-1463
'(any #(each (any any)) any . each-any)))))
(syntax-dispatch
syntmp-tmp-1463
'(any ((any any)) any . each-any)))))
(syntax-dispatch
syntmp-tmp-1463
'(any () any . each-any))))
syntmp-x-1462)))
(install-global-transformer
'syntax-rules
(lambda (syntmp-xx-1503)
(letrec ((syntmp-expand-syntax-rules-1504
(lambda (syntmp-dots-1505
syntmp-keys-1506
syntmp-docstrings-1507
syntmp-clauses-1508)
((lambda (syntmp-tmp-1509)
((lambda (syntmp-tmp-1510)
(if syntmp-tmp-1510
(apply (lambda (syntmp-k-1511
syntmp-docstring-1512
syntmp-keyword-1513
syntmp-pattern-1514
syntmp-template-1515)
((lambda (syntmp-tmp-1516)
((lambda (syntmp-form-1517)
(if syntmp-dots-1505
((lambda (syntmp-tmp-1518)
((lambda (syntmp-dots-1519)
(list '#(syntax-object
with-ellipsis
((top)
#(ribcage () () ())
#(ribcage
#(dots)
#((top))
#("i"))
#(ribcage () () ())
#(ribcage
#(form)
#((top))
#("i"))
#(ribcage () () ())
#(ribcage () () ())
#(ribcage
#(k
docstring
keyword
pattern
template)
#((top)
(top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"
"i"))
#(ribcage () () ())
#(ribcage () () ())
#(ribcage
#(dots
keys
docstrings
clauses)
#((top)
(top)
(top)
(top))
#("i" "i" "i" "i"))
#(ribcage
(expand-syntax-rules)
((top))
("i"))
#(ribcage
#(xx)
#((top))
#("i"))))
syntmp-dots-1519
syntmp-form-1517))
syntmp-tmp-1518))
syntmp-dots-1505)
syntmp-form-1517))
syntmp-tmp-1516))
(cons '#(syntax-object
lambda
((top)
#(ribcage () () ())
#(ribcage () () ())
#(ribcage
#(k
docstring
keyword
pattern
template)
#((top) (top) (top) (top) (top))
#("i" "i" "i" "i" "i"))
#(ribcage () () ())
#(ribcage () () ())
#(ribcage
#(dots keys docstrings clauses)
#((top) (top) (top) (top))
#("i" "i" "i" "i"))
#(ribcage
(expand-syntax-rules)
((top))
("i"))
#(ribcage #(xx) #((top)) #("i"))))
(cons '(#(syntax-object
x
((top)
#(ribcage () () ())
#(ribcage () () ())
#(ribcage
#(k
docstring
keyword
pattern
template)
#((top)
(top)
(top)
(top)
(top))
#("i" "i" "i" "i" "i"))
#(ribcage () () ())
#(ribcage () () ())
#(ribcage
#(dots
keys
docstrings
clauses)
#((top)
(top)
(top)
(top))
#("i" "i" "i" "i"))
#(ribcage
(expand-syntax-rules)
((top))
("i"))
#(ribcage
#(xx)
#((top))
#("i")))))
(append
syntmp-docstring-1512
(list (cons '#(syntax-object
syntax-case
((top)
#(ribcage
()
()
())
#(ribcage
()
()
())
#(ribcage
#(k
docstring
keyword
pattern
template)
#((top)
(top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
()
()
())
#(ribcage
#(dots
keys
docstrings
clauses)
#((top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"))
#(ribcage
(expand-syntax-rules)
((top))
("i"))
#(ribcage
#(xx)
#((top))
#("i"))))
(cons '#(syntax-object
x
((top)
#(ribcage
()
()
())
#(ribcage
()
()
())
#(ribcage
#(k
docstring
keyword
pattern
template)
#((top)
(top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
()
()
())
#(ribcage
#(dots
keys
docstrings
clauses)
#((top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"))
#(ribcage
(expand-syntax-rules)
((top))
("i"))
#(ribcage
#(xx)
#((top))
#("i"))))
(cons syntmp-k-1511
(map (lambda (syntmp-tmp-1522
syntmp-tmp-1521)
(list (cons '#(syntax-object
dummy
((top)
#(ribcage
()
()
())
#(ribcage
()
()
())
#(ribcage
#(k
docstring
keyword
pattern
template)
#((top)
(top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
()
()
())
#(ribcage
#(dots
keys
docstrings
clauses)
#((top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"))
#(ribcage
(expand-syntax-rules)
((top))
("i"))
#(ribcage
#(xx)
#((top))
#("i"))))
syntmp-tmp-1521)
(list '#(syntax-object
syntax
((top)
#(ribcage
()
()
())
#(ribcage
()
()
())
#(ribcage
#(k
docstring
keyword
pattern
template)
#((top)
(top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
()
()
())
#(ribcage
#(dots
keys
docstrings
clauses)
#((top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"))
#(ribcage
(expand-syntax-rules)
((top))
("i"))
#(ribcage
#(xx)
#((top))
#("i"))))
syntmp-tmp-1522)))
syntmp-template-1515
syntmp-pattern-1514))))))))))
syntmp-tmp-1510)
(syntax-error syntmp-tmp-1509)))
(syntax-dispatch
syntmp-tmp-1509
'(each-any each-any #(each ((any . any) any))))))
(list syntmp-keys-1506
syntmp-docstrings-1507
syntmp-clauses-1508)))))
((lambda (syntmp-tmp-1524)
((lambda (syntmp-tmp-1525)
(if syntmp-tmp-1525
(apply (lambda (syntmp-_-1526
syntmp-k-1527
syntmp-keyword-1528
syntmp-pattern-1529
syntmp-template-1530)
(syntmp-expand-syntax-rules-1504
#f
syntmp-k-1527
'()
(map (lambda (syntmp-tmp-1534
syntmp-tmp-1533
syntmp-tmp-1532)
(list (cons syntmp-tmp-1532 syntmp-tmp-1533)
syntmp-tmp-1534))
syntmp-template-1530
syntmp-pattern-1529
syntmp-keyword-1528)))
syntmp-tmp-1525)
((lambda (syntmp-tmp-1535)
(if (if syntmp-tmp-1535
(apply (lambda (syntmp-_-1536
syntmp-k-1537
syntmp-docstring-1538
syntmp-keyword-1539
syntmp-pattern-1540
syntmp-template-1541)
(string?
(syntax-object->datum
syntmp-docstring-1538)))
syntmp-tmp-1535)
#f)
(apply (lambda (syntmp-_-1542
syntmp-k-1543
syntmp-docstring-1544
syntmp-keyword-1545
syntmp-pattern-1546
syntmp-template-1547)
(syntmp-expand-syntax-rules-1504
#f
syntmp-k-1543
(list syntmp-docstring-1544)
(map (lambda (syntmp-tmp-1551
syntmp-tmp-1550
syntmp-tmp-1549)
(list (cons syntmp-tmp-1549
syntmp-tmp-1550)
syntmp-tmp-1551))
syntmp-template-1547
syntmp-pattern-1546
syntmp-keyword-1545)))
syntmp-tmp-1535)
((lambda (syntmp-tmp-1552)
(if (if syntmp-tmp-1552
(apply (lambda (syntmp-_-1553
syntmp-dots-1554
syntmp-k-1555
syntmp-keyword-1556
syntmp-pattern-1557
syntmp-template-1558)
(identifier? syntmp-dots-1554))
syntmp-tmp-1552)
#f)
(apply (lambda (syntmp-_-1559
syntmp-dots-1560
syntmp-k-1561
syntmp-keyword-1562
syntmp-pattern-1563
syntmp-template-1564)
(syntmp-expand-syntax-rules-1504
syntmp-dots-1560
syntmp-k-1561
'()
(map (lambda (syntmp-tmp-1568
syntmp-tmp-1567
syntmp-tmp-1566)
(list (cons syntmp-tmp-1566
syntmp-tmp-1567)
syntmp-tmp-1568))
syntmp-template-1564
syntmp-pattern-1563
syntmp-keyword-1562)))
syntmp-tmp-1552)
((lambda (syntmp-tmp-1569)
(if (if syntmp-tmp-1569
(apply (lambda (syntmp-_-1570
syntmp-dots-1571
syntmp-k-1572
syntmp-docstring-1573
syntmp-keyword-1574
syntmp-pattern-1575
syntmp-template-1576)
(and (identifier? syntmp-dots-1571)
(string?
(syntax-object->datum
syntmp-docstring-1573))))
syntmp-tmp-1569)
#f)
(apply (lambda (syntmp-_-1577
syntmp-dots-1578
syntmp-k-1579
syntmp-docstring-1580
syntmp-keyword-1581
syntmp-pattern-1582
syntmp-template-1583)
(syntmp-expand-syntax-rules-1504
syntmp-dots-1578
syntmp-k-1579
(list syntmp-docstring-1580)
(map (lambda (syntmp-tmp-1587
syntmp-tmp-1586
syntmp-tmp-1585)
(list (cons syntmp-tmp-1585
syntmp-tmp-1586)
syntmp-tmp-1587))
syntmp-template-1583
syntmp-pattern-1582
syntmp-keyword-1581)))
syntmp-tmp-1569)
(syntax-error syntmp-tmp-1524)))
(syntax-dispatch
syntmp-tmp-1524
'(any any
each-any
any
.
#(each ((any . any) any)))))))
(syntax-dispatch
syntmp-tmp-1524
'(any any each-any . #(each ((any . any) any)))))))
(syntax-dispatch
syntmp-tmp-1524
'(any each-any any . #(each ((any . any) any)))))))
(syntax-dispatch
syntmp-tmp-1524
'(any each-any . #(each ((any . any) any))))))
syntmp-xx-1503))))
(install-global-transformer
'define-syntax-rule
(lambda (syntmp-x-1667)
((lambda (syntmp-tmp-1668)
((lambda (syntmp-tmp-1669)
(if syntmp-tmp-1669
(apply (lambda (syntmp-_-1670
syntmp-name-1671
syntmp-pattern-1672
syntmp-template-1673)
(list '#(syntax-object
define-syntax
((top)
#(ribcage
#(_ name pattern template)
#((top) (top) (top) (top))
#("i" "i" "i" "i"))
#(ribcage () () ())
#(ribcage #(x) #((top)) #("i"))))
syntmp-name-1671
(list '#(syntax-object
syntax-rules
((top)
#(ribcage
#(_ name pattern template)
#((top) (top) (top) (top))
#("i" "i" "i" "i"))
#(ribcage () () ())
#(ribcage #(x) #((top)) #("i"))))
'()
(list (cons syntmp-_-1670 syntmp-pattern-1672)
syntmp-template-1673))))
syntmp-tmp-1669)
((lambda (syntmp-tmp-1674)
(if (if syntmp-tmp-1674
(apply (lambda (syntmp-_-1675
syntmp-name-1676
syntmp-pattern-1677
syntmp-docstring-1678
syntmp-template-1679)
(string?
(syntax-object->datum syntmp-docstring-1678)))
syntmp-tmp-1674)
#f)
(apply (lambda (syntmp-_-1680
syntmp-name-1681
syntmp-pattern-1682
syntmp-docstring-1683
syntmp-template-1684)
(list '#(syntax-object
define-syntax
((top)
#(ribcage
#(_ name pattern docstring template)
#((top) (top) (top) (top) (top))
#("i" "i" "i" "i" "i"))
#(ribcage () () ())
#(ribcage #(x) #((top)) #("i"))))
syntmp-name-1681
(list '#(syntax-object
syntax-rules
((top)
#(ribcage
#(_
name
pattern
docstring
template)
#((top) (top) (top) (top) (top))
#("i" "i" "i" "i" "i"))
#(ribcage () () ())
#(ribcage #(x) #((top)) #("i"))))
'()
syntmp-docstring-1683
(list (cons syntmp-_-1680
syntmp-pattern-1682)
syntmp-template-1684))))
syntmp-tmp-1674)
(syntax-error syntmp-tmp-1668)))
(syntax-dispatch
syntmp-tmp-1668
'(any (any . any) any any)))))
(syntax-dispatch
syntmp-tmp-1668
'(any (any . any) any))))
syntmp-x-1667)))
(install-global-transformer
'let*
(lambda (syntmp-x-1703)
((lambda (syntmp-tmp-1704)
((lambda (syntmp-tmp-1705)
(if (if syntmp-tmp-1705
(apply (lambda (syntmp-let*-1706
syntmp-x-1707
syntmp-v-1708
syntmp-e1-1709
syntmp-e2-1710)
(andmap identifier? syntmp-x-1707))
syntmp-tmp-1705)
#f)
(apply (lambda (syntmp-let*-1712
syntmp-x-1713
syntmp-v-1714
syntmp-e1-1715
syntmp-e2-1716)
(let syntmp-f-1717 ((syntmp-bindings-1718
(map list
syntmp-x-1713
syntmp-v-1714)))
(if (null? syntmp-bindings-1718)
(cons '#(syntax-object
let
((top)
#(ribcage () () ())
#(ribcage
#(f bindings)
#((top) (top))
#("i" "i"))
#(ribcage
#(let* x v e1 e2)
#((top) (top) (top) (top) (top))
#("i" "i" "i" "i" "i"))
#(ribcage () () ())
#(ribcage #(x) #((top)) #("i"))))
(cons '()
(cons syntmp-e1-1715 syntmp-e2-1716)))
((lambda (syntmp-tmp-1722)
((lambda (syntmp-tmp-1723)
(if syntmp-tmp-1723
(apply (lambda (syntmp-body-1724
syntmp-binding-1725)
(list '#(syntax-object
let
((top)
#(ribcage () () ())
#(ribcage
#(body binding)
#((top) (top))
#("i" "i"))
#(ribcage () () ())
#(ribcage
#(f bindings)
#((top) (top))
#("i" "i"))
#(ribcage
#(let* x v e1 e2)
#((top)
(top)
(top)
(top)
(top))
#("i" "i" "i" "i" "i"))
#(ribcage () () ())
#(ribcage
#(x)
#((top))
#("i"))))
(list syntmp-binding-1725)
syntmp-body-1724))
syntmp-tmp-1723)
(syntax-error syntmp-tmp-1722)))
(syntax-dispatch
syntmp-tmp-1722
'(any any))))
(list (syntmp-f-1717 (cdr syntmp-bindings-1718))
(car syntmp-bindings-1718))))))
syntmp-tmp-1705)
(syntax-error syntmp-tmp-1704)))
(syntax-dispatch
syntmp-tmp-1704
'(any #(each (any any)) any . each-any))))
syntmp-x-1703)))
(install-global-transformer
'do
(lambda (syntmp-orig-x-1745)
((lambda (syntmp-tmp-1746)
((lambda (syntmp-tmp-1747)
(if syntmp-tmp-1747
(apply (lambda (syntmp-_-1748
syntmp-var-1749
syntmp-init-1750
syntmp-step-1751
syntmp-e0-1752
syntmp-e1-1753
syntmp-c-1754)
((lambda (syntmp-tmp-1755)
((lambda (syntmp-tmp-1756)
(if syntmp-tmp-1756
(apply (lambda (syntmp-step-1757)
((lambda (syntmp-tmp-1758)
((lambda (syntmp-tmp-1759)
(if syntmp-tmp-1759
(apply (lambda ()
(list '#(syntax-object
let
((top)
#(ribcage
()
()
())
#(ribcage
#(step)
#((top))
#("i"))
#(ribcage
#(_
var
init
step
e0
e1
c)
#((top)
(top)
(top)
(top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"
"i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(orig-x)
#((top))
#("i"))))
'#(syntax-object
doloop
((top)
#(ribcage
()
()
())
#(ribcage
#(step)
#((top))
#("i"))
#(ribcage
#(_
var
init
step
e0
e1
c)
#((top)
(top)
(top)
(top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"
"i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(orig-x)
#((top))
#("i"))))
(map list
syntmp-var-1749
syntmp-init-1750)
(list '#(syntax-object
if
((top)
#(ribcage
()
()
())
#(ribcage
#(step)
#((top))
#("i"))
#(ribcage
#(_
var
init
step
e0
e1
c)
#((top)
(top)
(top)
(top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"
"i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(orig-x)
#((top))
#("i"))))
(list '#(syntax-object
not
((top)
#(ribcage
()
()
())
#(ribcage
#(step)
#((top))
#("i"))
#(ribcage
#(_
var
init
step
e0
e1
c)
#((top)
(top)
(top)
(top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"
"i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(orig-x)
#((top))
#("i"))))
syntmp-e0-1752)
(cons '#(syntax-object
begin
((top)
#(ribcage
()
()
())
#(ribcage
#(step)
#((top))
#("i"))
#(ribcage
#(_
var
init
step
e0
e1
c)
#((top)
(top)
(top)
(top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"
"i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(orig-x)
#((top))
#("i"))))
(append
syntmp-c-1754
(list (cons '#(syntax-object
doloop
((top)
#(ribcage
()
()
())
#(ribcage
#(step)
#((top))
#("i"))
#(ribcage
#(_
var
init
step
e0
e1
c)
#((top)
(top)
(top)
(top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"
"i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(orig-x)
#((top))
#("i"))))
syntmp-step-1757)))))))
syntmp-tmp-1759)
((lambda (syntmp-tmp-1764)
(if syntmp-tmp-1764
(apply (lambda (syntmp-e1-1765
syntmp-e2-1766)
(list '#(syntax-object
let
((top)
#(ribcage
#(e1
e2)
#((top)
(top))
#("i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(step)
#((top))
#("i"))
#(ribcage
#(_
var
init
step
e0
e1
c)
#((top)
(top)
(top)
(top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"
"i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(orig-x)
#((top))
#("i"))))
'#(syntax-object
doloop
((top)
#(ribcage
#(e1
e2)
#((top)
(top))
#("i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(step)
#((top))
#("i"))
#(ribcage
#(_
var
init
step
e0
e1
c)
#((top)
(top)
(top)
(top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"
"i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(orig-x)
#((top))
#("i"))))
(map list
syntmp-var-1749
syntmp-init-1750)
(list '#(syntax-object
if
((top)
#(ribcage
#(e1
e2)
#((top)
(top))
#("i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(step)
#((top))
#("i"))
#(ribcage
#(_
var
init
step
e0
e1
c)
#((top)
(top)
(top)
(top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"
"i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(orig-x)
#((top))
#("i"))))
syntmp-e0-1752
(cons '#(syntax-object
begin
((top)
#(ribcage
#(e1
e2)
#((top)
(top))
#("i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(step)
#((top))
#("i"))
#(ribcage
#(_
var
init
step
e0
e1
c)
#((top)
(top)
(top)
(top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"
"i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(orig-x)
#((top))
#("i"))))
(cons syntmp-e1-1765
syntmp-e2-1766))
(cons '#(syntax-object
begin
((top)
#(ribcage
#(e1
e2)
#((top)
(top))
#("i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(step)
#((top))
#("i"))
#(ribcage
#(_
var
init
step
e0
e1
c)
#((top)
(top)
(top)
(top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"
"i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(orig-x)
#((top))
#("i"))))
(append
syntmp-c-1754
(list (cons '#(syntax-object
doloop
((top)
#(ribcage
#(e1
e2)
#((top)
(top))
#("i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(step)
#((top))
#("i"))
#(ribcage
#(_
var
init
step
e0
e1
c)
#((top)
(top)
(top)
(top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"
"i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(orig-x)
#((top))
#("i"))))
syntmp-step-1757)))))))
syntmp-tmp-1764)
(syntax-error
syntmp-tmp-1758)))
(syntax-dispatch
syntmp-tmp-1758
'(any . each-any)))))
(syntax-dispatch
syntmp-tmp-1758
'())))
syntmp-e1-1753))
syntmp-tmp-1756)
(syntax-error syntmp-tmp-1755)))
(syntax-dispatch
syntmp-tmp-1755
'each-any)))
(map (lambda (syntmp-v-1773 syntmp-s-1774)
((lambda (syntmp-tmp-1775)
((lambda (syntmp-tmp-1776)
(if syntmp-tmp-1776
(apply (lambda () syntmp-v-1773)
syntmp-tmp-1776)
((lambda (syntmp-tmp-1777)
(if syntmp-tmp-1777
(apply (lambda (syntmp-e-1778)
syntmp-e-1778)
syntmp-tmp-1777)
((lambda (syntmp-_-1779)
(syntax-error syntmp-orig-x-1745))
syntmp-tmp-1775)))
(syntax-dispatch
syntmp-tmp-1775
'(any)))))
(syntax-dispatch syntmp-tmp-1775 (quote ()))))
syntmp-s-1774))
syntmp-var-1749
syntmp-step-1751)))
syntmp-tmp-1747)
(syntax-error syntmp-tmp-1746)))
(syntax-dispatch
syntmp-tmp-1746
'(any #(each (any any . any))
(any . each-any)
.
each-any))))
syntmp-orig-x-1745)))
(install-global-transformer
'quasiquote
(letrec ((syntmp-quasicons-1807
(lambda (syntmp-x-1811 syntmp-y-1812)
((lambda (syntmp-tmp-1813)
((lambda (syntmp-tmp-1814)
(if syntmp-tmp-1814
(apply (lambda (syntmp-x-1815 syntmp-y-1816)
((lambda (syntmp-tmp-1817)
((lambda (syntmp-tmp-1818)
(if syntmp-tmp-1818
(apply (lambda (syntmp-dy-1819)
((lambda (syntmp-tmp-1820)
((lambda (syntmp-tmp-1821)
(if syntmp-tmp-1821
(apply (lambda (syntmp-dx-1822)
(list '#(syntax-object
quote
((top)
#(ribcage
#(dx)
#((top))
#("i"))
#(ribcage
#(dy)
#((top))
#("i"))
#(ribcage
()
()
())
#(ribcage
#(x
y)
#((top)
(top))
#("i"
"i"))
#(ribcage
()
()
())
#(ribcage
()
()
())
#(ribcage
#(x
y)
#((top)
(top))
#("i"
"i"))
#(ribcage
#(quasicons
quasiappend
quasivector
quasi)
#((top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"))))
(cons syntmp-dx-1822
syntmp-dy-1819)))
syntmp-tmp-1821)
((lambda (syntmp-_-1823)
(if (null? syntmp-dy-1819)
(list '#(syntax-object
list
((top)
#(ribcage
#(_)
#((top))
#("i"))
#(ribcage
#(dy)
#((top))
#("i"))
#(ribcage
()
()
())
#(ribcage
#(x
y)
#((top)
(top))
#("i"
"i"))
#(ribcage
()
()
())
#(ribcage
()
()
())
#(ribcage
#(x
y)
#((top)
(top))
#("i"
"i"))
#(ribcage
#(quasicons
quasiappend
quasivector
quasi)
#((top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"))))
syntmp-x-1815)
(list '#(syntax-object
cons
((top)
#(ribcage
#(_)
#((top))
#("i"))
#(ribcage
#(dy)
#((top))
#("i"))
#(ribcage
()
()
())
#(ribcage
#(x
y)
#((top)
(top))
#("i"
"i"))
#(ribcage
()
()
())
#(ribcage
()
()
())
#(ribcage
#(x
y)
#((top)
(top))
#("i"
"i"))
#(ribcage
#(quasicons
quasiappend
quasivector
quasi)
#((top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"))))
syntmp-x-1815
syntmp-y-1816)))
syntmp-tmp-1820)))
(syntax-dispatch
syntmp-tmp-1820
'(#(free-id
#(syntax-object
quote
((top)
#(ribcage
#(dy)
#((top))
#("i"))
#(ribcage
()
()
())
#(ribcage
#(x y)
#((top) (top))
#("i" "i"))
#(ribcage
()
()
())
#(ribcage
()
()
())
#(ribcage
#(x y)
#((top) (top))
#("i" "i"))
#(ribcage
#(quasicons
quasiappend
quasivector
quasi)
#((top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i")))))
any))))
syntmp-x-1815))
syntmp-tmp-1818)
((lambda (syntmp-tmp-1824)
(if syntmp-tmp-1824
(apply (lambda (syntmp-stuff-1825)
(cons '#(syntax-object
list
((top)
#(ribcage
#(stuff)
#((top))
#("i"))
#(ribcage
()
()
())
#(ribcage
#(x y)
#((top)
(top))
#("i" "i"))
#(ribcage
()
()
())
#(ribcage
()
()
())
#(ribcage
#(x y)
#((top)
(top))
#("i" "i"))
#(ribcage
#(quasicons
quasiappend
quasivector
quasi)
#((top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"))))
(cons syntmp-x-1815
syntmp-stuff-1825)))
syntmp-tmp-1824)
((lambda (syntmp-else-1826)
(list '#(syntax-object
cons
((top)
#(ribcage
#(else)
#((top))
#("i"))
#(ribcage () () ())
#(ribcage
#(x y)
#((top) (top))
#("i" "i"))
#(ribcage () () ())
#(ribcage () () ())
#(ribcage
#(x y)
#((top) (top))
#("i" "i"))
#(ribcage
#(quasicons
quasiappend
quasivector
quasi)
#((top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"))))
syntmp-x-1815
syntmp-y-1816))
syntmp-tmp-1817)))
(syntax-dispatch
syntmp-tmp-1817
'(#(free-id
#(syntax-object
list
((top)
#(ribcage () () ())
#(ribcage
#(x y)
#((top) (top))
#("i" "i"))
#(ribcage () () ())
#(ribcage () () ())
#(ribcage
#(x y)
#((top) (top))
#("i" "i"))
#(ribcage
#(quasicons
quasiappend
quasivector
quasi)
#((top) (top) (top) (top))
#("i" "i" "i" "i")))))
.
any)))))
(syntax-dispatch
syntmp-tmp-1817
'(#(free-id
#(syntax-object
quote
((top)
#(ribcage () () ())
#(ribcage
#(x y)
#((top) (top))
#("i" "i"))
#(ribcage () () ())
#(ribcage () () ())
#(ribcage
#(x y)
#((top) (top))
#("i" "i"))
#(ribcage
#(quasicons
quasiappend
quasivector
quasi)
#((top) (top) (top) (top))
#("i" "i" "i" "i")))))
any))))
syntmp-y-1816))
syntmp-tmp-1814)
(syntax-error syntmp-tmp-1813)))
(syntax-dispatch
syntmp-tmp-1813
'(any any))))
(list syntmp-x-1811 syntmp-y-1812))))
(syntmp-quasiappend-1808
(lambda (syntmp-x-1827 syntmp-y-1828)
((lambda (syntmp-tmp-1829)
((lambda (syntmp-tmp-1830)
(if syntmp-tmp-1830
(apply (lambda (syntmp-x-1831 syntmp-y-1832)
((lambda (syntmp-tmp-1833)
((lambda (syntmp-tmp-1834)
(if syntmp-tmp-1834
(apply (lambda () syntmp-x-1831)
syntmp-tmp-1834)
((lambda (syntmp-_-1835)
(list '#(syntax-object
append
((top)
#(ribcage
#(_)
#((top))
#("i"))
#(ribcage () () ())
#(ribcage
#(x y)
#((top) (top))
#("i" "i"))
#(ribcage () () ())
#(ribcage () () ())
#(ribcage
#(x y)
#((top) (top))
#("i" "i"))
#(ribcage
#(quasicons
quasiappend
quasivector
quasi)
#((top)
(top)
(top)
(top))
#("i" "i" "i" "i"))))
syntmp-x-1831
syntmp-y-1832))
syntmp-tmp-1833)))
(syntax-dispatch
syntmp-tmp-1833
'(#(free-id
#(syntax-object
quote
((top)
#(ribcage () () ())
#(ribcage
#(x y)
#((top) (top))
#("i" "i"))
#(ribcage () () ())
#(ribcage () () ())
#(ribcage
#(x y)
#((top) (top))
#("i" "i"))
#(ribcage
#(quasicons
quasiappend
quasivector
quasi)
#((top) (top) (top) (top))
#("i" "i" "i" "i")))))
()))))
syntmp-y-1832))
syntmp-tmp-1830)
(syntax-error syntmp-tmp-1829)))
(syntax-dispatch
syntmp-tmp-1829
'(any any))))
(list syntmp-x-1827 syntmp-y-1828))))
(syntmp-quasivector-1809
(lambda (syntmp-x-1836)
((lambda (syntmp-tmp-1837)
((lambda (syntmp-x-1838)
((lambda (syntmp-tmp-1839)
((lambda (syntmp-tmp-1840)
(if syntmp-tmp-1840
(apply (lambda (syntmp-x-1841)
(list '#(syntax-object
quote
((top)
#(ribcage #(x) #((top)) #("i"))
#(ribcage () () ())
#(ribcage #(x) #((top)) #("i"))
#(ribcage () () ())
#(ribcage () () ())
#(ribcage #(x) #((top)) #("i"))
#(ribcage
#(quasicons
quasiappend
quasivector
quasi)
#((top) (top) (top) (top))
#("i" "i" "i" "i"))))
(list->vector syntmp-x-1841)))
syntmp-tmp-1840)
((lambda (syntmp-tmp-1843)
(if syntmp-tmp-1843
(apply (lambda (syntmp-x-1844)
(cons '#(syntax-object
vector
((top)
#(ribcage
#(x)
#((top))
#("i"))
#(ribcage () () ())
#(ribcage
#(x)
#((top))
#("i"))
#(ribcage () () ())
#(ribcage () () ())
#(ribcage
#(x)
#((top))
#("i"))
#(ribcage
#(quasicons
quasiappend
quasivector
quasi)
#((top)
(top)
(top)
(top))
#("i" "i" "i" "i"))))
syntmp-x-1844))
syntmp-tmp-1843)
((lambda (syntmp-_-1846)
(list '#(syntax-object
list->vector
((top)
#(ribcage #(_) #((top)) #("i"))
#(ribcage () () ())
#(ribcage #(x) #((top)) #("i"))
#(ribcage () () ())
#(ribcage () () ())
#(ribcage #(x) #((top)) #("i"))
#(ribcage
#(quasicons
quasiappend
quasivector
quasi)
#((top) (top) (top) (top))
#("i" "i" "i" "i"))))
syntmp-x-1838))
syntmp-tmp-1839)))
(syntax-dispatch
syntmp-tmp-1839
'(#(free-id
#(syntax-object
list
((top)
#(ribcage () () ())
#(ribcage #(x) #((top)) #("i"))
#(ribcage () () ())
#(ribcage () () ())
#(ribcage #(x) #((top)) #("i"))
#(ribcage
#(quasicons
quasiappend
quasivector
quasi)
#((top) (top) (top) (top))
#("i" "i" "i" "i")))))
.
each-any)))))
(syntax-dispatch
syntmp-tmp-1839
'(#(free-id
#(syntax-object
quote
((top)
#(ribcage () () ())
#(ribcage #(x) #((top)) #("i"))
#(ribcage () () ())
#(ribcage () () ())
#(ribcage #(x) #((top)) #("i"))
#(ribcage
#(quasicons quasiappend quasivector quasi)
#((top) (top) (top) (top))
#("i" "i" "i" "i")))))
each-any))))
syntmp-x-1838))
syntmp-tmp-1837))
syntmp-x-1836)))
(syntmp-quasi-1810
(lambda (syntmp-p-1847 syntmp-lev-1848)
((lambda (syntmp-tmp-1849)
((lambda (syntmp-tmp-1850)
(if syntmp-tmp-1850
(apply (lambda (syntmp-p-1851)
(if (= syntmp-lev-1848 0)
syntmp-p-1851
(syntmp-quasicons-1807
'(#(syntax-object
quote
((top)
#(ribcage #(p) #((top)) #("i"))
#(ribcage () () ())
#(ribcage
#(p lev)
#((top) (top))
#("i" "i"))
#(ribcage
#(quasicons
quasiappend
quasivector
quasi)
#((top) (top) (top) (top))
#("i" "i" "i" "i"))))
#(syntax-object
unquote
((top)
#(ribcage #(p) #((top)) #("i"))
#(ribcage () () ())
#(ribcage
#(p lev)
#((top) (top))
#("i" "i"))
#(ribcage
#(quasicons
quasiappend
quasivector
quasi)
#((top) (top) (top) (top))
#("i" "i" "i" "i")))))
(syntmp-quasi-1810
(list syntmp-p-1851)
(- syntmp-lev-1848 1)))))
syntmp-tmp-1850)
((lambda (syntmp-tmp-1852)
(if syntmp-tmp-1852
(apply (lambda (syntmp-p-1853 syntmp-q-1854)
(if (= syntmp-lev-1848 0)
(syntmp-quasiappend-1808
syntmp-p-1853
(syntmp-quasi-1810
syntmp-q-1854
syntmp-lev-1848))
(syntmp-quasicons-1807
(syntmp-quasicons-1807
'(#(syntax-object
quote
((top)
#(ribcage
#(p q)
#((top) (top))
#("i" "i"))
#(ribcage () () ())
#(ribcage
#(p lev)
#((top) (top))
#("i" "i"))
#(ribcage
#(quasicons
quasiappend
quasivector
quasi)
#((top) (top) (top) (top))
#("i" "i" "i" "i"))))
#(syntax-object
unquote-splicing
((top)
#(ribcage
#(p q)
#((top) (top))
#("i" "i"))
#(ribcage () () ())
#(ribcage
#(p lev)
#((top) (top))
#("i" "i"))
#(ribcage
#(quasicons
quasiappend
quasivector
quasi)
#((top) (top) (top) (top))
#("i" "i" "i" "i")))))
(syntmp-quasi-1810
(list syntmp-p-1853)
(- syntmp-lev-1848 1)))
(syntmp-quasi-1810
syntmp-q-1854
syntmp-lev-1848))))
syntmp-tmp-1852)
((lambda (syntmp-tmp-1855)
(if syntmp-tmp-1855
(apply (lambda (syntmp-p-1856)
(syntmp-quasicons-1807
'(#(syntax-object
quote
((top)
#(ribcage
#(p)
#((top))
#("i"))
#(ribcage () () ())
#(ribcage
#(p lev)
#((top) (top))
#("i" "i"))
#(ribcage
#(quasicons
quasiappend
quasivector
quasi)
#((top) (top) (top) (top))
#("i" "i" "i" "i"))))
#(syntax-object
quasiquote
((top)
#(ribcage
#(p)
#((top))
#("i"))
#(ribcage () () ())
#(ribcage
#(p lev)
#((top) (top))
#("i" "i"))
#(ribcage
#(quasicons
quasiappend
quasivector
quasi)
#((top) (top) (top) (top))
#("i" "i" "i" "i")))))
(syntmp-quasi-1810
(list syntmp-p-1856)
(+ syntmp-lev-1848 1))))
syntmp-tmp-1855)
((lambda (syntmp-tmp-1857)
(if syntmp-tmp-1857
(apply (lambda (syntmp-p-1858
syntmp-q-1859)
(syntmp-quasicons-1807
(syntmp-quasi-1810
syntmp-p-1858
syntmp-lev-1848)
(syntmp-quasi-1810
syntmp-q-1859
syntmp-lev-1848)))
syntmp-tmp-1857)
((lambda (syntmp-tmp-1860)
(if syntmp-tmp-1860
(apply (lambda (syntmp-x-1861)
(syntmp-quasivector-1809
(syntmp-quasi-1810
syntmp-x-1861
syntmp-lev-1848)))
syntmp-tmp-1860)
((lambda (syntmp-p-1863)
(list '#(syntax-object
quote
((top)
#(ribcage
#(p)
#((top))
#("i"))
#(ribcage () () ())
#(ribcage
#(p lev)
#((top) (top))
#("i" "i"))
#(ribcage
#(quasicons
quasiappend
quasivector
quasi)
#((top)
(top)
(top)
(top))
#("i" "i" "i" "i"))))
syntmp-p-1863))
syntmp-tmp-1849)))
(syntax-dispatch
syntmp-tmp-1849
'#(vector each-any)))))
(syntax-dispatch
syntmp-tmp-1849
'(any . any)))))
(syntax-dispatch
syntmp-tmp-1849
'(#(free-id
#(syntax-object
quasiquote
((top)
#(ribcage () () ())
#(ribcage
#(p lev)
#((top) (top))
#("i" "i"))
#(ribcage
#(quasicons
quasiappend
quasivector
quasi)
#((top) (top) (top) (top))
#("i" "i" "i" "i")))))
any)))))
(syntax-dispatch
syntmp-tmp-1849
'((#(free-id
#(syntax-object
unquote-splicing
((top)
#(ribcage () () ())
#(ribcage #(p lev) #((top) (top)) #("i" "i"))
#(ribcage
#(quasicons quasiappend quasivector quasi)
#((top) (top) (top) (top))
#("i" "i" "i" "i")))))
any)
.
any)))))
(syntax-dispatch
syntmp-tmp-1849
'(#(free-id
#(syntax-object
unquote
((top)
#(ribcage () () ())
#(ribcage #(p lev) #((top) (top)) #("i" "i"))
#(ribcage
#(quasicons quasiappend quasivector quasi)
#((top) (top) (top) (top))
#("i" "i" "i" "i")))))
any))))
syntmp-p-1847))))
(lambda (syntmp-x-1864)
((lambda (syntmp-tmp-1865)
((lambda (syntmp-tmp-1866)
(if syntmp-tmp-1866
(apply (lambda (syntmp-_-1867 syntmp-e-1868)
(syntmp-quasi-1810 syntmp-e-1868 0))
syntmp-tmp-1866)
(syntax-error syntmp-tmp-1865)))
(syntax-dispatch
syntmp-tmp-1865
'(any any))))
syntmp-x-1864))))
(install-global-transformer
'include
(lambda (syntmp-x-1928)
(letrec ((syntmp-read-file-1929
(lambda (syntmp-fn-1930 syntmp-k-1931)
(let ((syntmp-p-1932 (open-input-file syntmp-fn-1930)))
(let syntmp-f-1933 ((syntmp-x-1934 (read syntmp-p-1932)))
(if (eof-object? syntmp-x-1934)
(begin
(close-input-port syntmp-p-1932)
'())
(cons (datum->syntax-object
syntmp-k-1931
syntmp-x-1934)
(syntmp-f-1933 (read syntmp-p-1932)))))))))
((lambda (syntmp-tmp-1935)
((lambda (syntmp-tmp-1936)
(if syntmp-tmp-1936
(apply (lambda (syntmp-k-1937 syntmp-filename-1938)
(let ((syntmp-fn-1939
(syntax-object->datum syntmp-filename-1938)))
((lambda (syntmp-tmp-1940)
((lambda (syntmp-tmp-1941)
(if syntmp-tmp-1941
(apply (lambda (syntmp-exp-1942)
(cons '#(syntax-object
begin
((top)
#(ribcage () () ())
#(ribcage
#(exp)
#((top))
#("i"))
#(ribcage () () ())
#(ribcage () () ())
#(ribcage
#(fn)
#((top))
#("i"))
#(ribcage
#(k filename)
#((top) (top))
#("i" "i"))
#(ribcage
(read-file)
((top))
("i"))
#(ribcage
#(x)
#((top))
#("i"))))
syntmp-exp-1942))
syntmp-tmp-1941)
(syntax-error syntmp-tmp-1940)))
(syntax-dispatch
syntmp-tmp-1940
'each-any)))
(syntmp-read-file-1929
syntmp-fn-1939
syntmp-k-1937))))
syntmp-tmp-1936)
(syntax-error syntmp-tmp-1935)))
(syntax-dispatch
syntmp-tmp-1935
'(any any))))
syntmp-x-1928))))
(install-global-transformer
'unquote
(lambda (syntmp-x-1959)
((lambda (syntmp-tmp-1960)
((lambda (syntmp-tmp-1961)
(if syntmp-tmp-1961
(apply (lambda (syntmp-_-1962 syntmp-e-1963)
(error 'unquote
"expression ,~s not valid outside of quasiquote"
(syntax-object->datum syntmp-e-1963)))
syntmp-tmp-1961)
(syntax-error syntmp-tmp-1960)))
(syntax-dispatch
syntmp-tmp-1960
'(any any))))
syntmp-x-1959)))
(install-global-transformer
'unquote-splicing
(lambda (syntmp-x-1969)
((lambda (syntmp-tmp-1970)
((lambda (syntmp-tmp-1971)
(if syntmp-tmp-1971
(apply (lambda (syntmp-_-1972 syntmp-e-1973)
(error 'unquote-splicing
"expression ,@~s not valid outside of quasiquote"
(syntax-object->datum syntmp-e-1973)))
syntmp-tmp-1971)
(syntax-error syntmp-tmp-1970)))
(syntax-dispatch
syntmp-tmp-1970
'(any any))))
syntmp-x-1969)))
(install-global-transformer
'case
(lambda (syntmp-x-1979)
((lambda (syntmp-tmp-1980)
((lambda (syntmp-tmp-1981)
(if syntmp-tmp-1981
(apply (lambda (syntmp-_-1982
syntmp-e-1983
syntmp-m1-1984
syntmp-m2-1985)
((lambda (syntmp-tmp-1986)
((lambda (syntmp-body-1987)
(list '#(syntax-object
let
((top)
#(ribcage () () ())
#(ribcage #(body) #((top)) #("i"))
#(ribcage
#(_ e m1 m2)
#((top) (top) (top) (top))
#("i" "i" "i" "i"))
#(ribcage () () ())
#(ribcage #(x) #((top)) #("i"))))
(list (list '#(syntax-object
t
((top)
#(ribcage () () ())
#(ribcage
#(body)
#((top))
#("i"))
#(ribcage
#(_ e m1 m2)
#((top) (top) (top) (top))
#("i" "i" "i" "i"))
#(ribcage () () ())
#(ribcage
#(x)
#((top))
#("i"))))
syntmp-e-1983))
syntmp-body-1987))
syntmp-tmp-1986))
(let syntmp-f-1988 ((syntmp-clause-1989 syntmp-m1-1984)
(syntmp-clauses-1990 syntmp-m2-1985))
(if (null? syntmp-clauses-1990)
((lambda (syntmp-tmp-1992)
((lambda (syntmp-tmp-1993)
(if syntmp-tmp-1993
(apply (lambda (syntmp-e1-1994
syntmp-e2-1995)
(cons '#(syntax-object
begin
((top)
#(ribcage
#(e1 e2)
#((top) (top))
#("i" "i"))
#(ribcage () () ())
#(ribcage
#(f clause clauses)
#((top) (top) (top))
#("i" "i" "i"))
#(ribcage
#(_ e m1 m2)
#((top)
(top)
(top)
(top))
#("i" "i" "i" "i"))
#(ribcage () () ())
#(ribcage
#(x)
#((top))
#("i"))))
(cons syntmp-e1-1994
syntmp-e2-1995)))
syntmp-tmp-1993)
((lambda (syntmp-tmp-1997)
(if syntmp-tmp-1997
(apply (lambda (syntmp-k-1998
syntmp-e1-1999
syntmp-e2-2000)
(list '#(syntax-object
if
((top)
#(ribcage
#(k e1 e2)
#((top)
(top)
(top))
#("i" "i" "i"))
#(ribcage () () ())
#(ribcage
#(f clause clauses)
#((top)
(top)
(top))
#("i" "i" "i"))
#(ribcage
#(_ e m1 m2)
#((top)
(top)
(top)
(top))
#("i" "i" "i" "i"))
#(ribcage () () ())
#(ribcage
#(x)
#((top))
#("i"))))
(list '#(syntax-object
memv
((top)
#(ribcage
#(k e1 e2)
#((top)
(top)
(top))
#("i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(f
clause
clauses)
#((top)
(top)
(top))
#("i"
"i"
"i"))
#(ribcage
#(_ e m1 m2)
#((top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(x)
#((top))
#("i"))))
'#(syntax-object
t
((top)
#(ribcage
#(k e1 e2)
#((top)
(top)
(top))
#("i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(f
clause
clauses)
#((top)
(top)
(top))
#("i"
"i"
"i"))
#(ribcage
#(_ e m1 m2)
#((top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(x)
#((top))
#("i"))))
(list '#(syntax-object
quote
((top)
#(ribcage
#(k
e1
e2)
#((top)
(top)
(top))
#("i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(f
clause
clauses)
#((top)
(top)
(top))
#("i"
"i"
"i"))
#(ribcage
#(_
e
m1
m2)
#((top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(x)
#((top))
#("i"))))
syntmp-k-1998))
(cons '#(syntax-object
begin
((top)
#(ribcage
#(k e1 e2)
#((top)
(top)
(top))
#("i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(f
clause
clauses)
#((top)
(top)
(top))
#("i"
"i"
"i"))
#(ribcage
#(_ e m1 m2)
#((top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(x)
#((top))
#("i"))))
(cons syntmp-e1-1999
syntmp-e2-2000))))
syntmp-tmp-1997)
((lambda (syntmp-_-2003)
(syntax-error syntmp-x-1979))
syntmp-tmp-1992)))
(syntax-dispatch
syntmp-tmp-1992
'(each-any any . each-any)))))
(syntax-dispatch
syntmp-tmp-1992
'(#(free-id
#(syntax-object
else
((top)
#(ribcage () () ())
#(ribcage
#(f clause clauses)
#((top) (top) (top))
#("i" "i" "i"))
#(ribcage
#(_ e m1 m2)
#((top) (top) (top) (top))
#("i" "i" "i" "i"))
#(ribcage () () ())
#(ribcage #(x) #((top)) #("i")))))
any
.
each-any))))
syntmp-clause-1989)
((lambda (syntmp-tmp-2004)
((lambda (syntmp-rest-2005)
((lambda (syntmp-tmp-2006)
((lambda (syntmp-tmp-2007)
(if syntmp-tmp-2007
(apply (lambda (syntmp-k-2008
syntmp-e1-2009
syntmp-e2-2010)
(list '#(syntax-object
if
((top)
#(ribcage
#(k e1 e2)
#((top)
(top)
(top))
#("i" "i" "i"))
#(ribcage () () ())
#(ribcage
#(rest)
#((top))
#("i"))
#(ribcage () () ())
#(ribcage
#(f
clause
clauses)
#((top)
(top)
(top))
#("i" "i" "i"))
#(ribcage
#(_ e m1 m2)
#((top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"))
#(ribcage () () ())
#(ribcage
#(x)
#((top))
#("i"))))
(list '#(syntax-object
memv
((top)
#(ribcage
#(k e1 e2)
#((top)
(top)
(top))
#("i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(rest)
#((top))
#("i"))
#(ribcage
()
()
())
#(ribcage
#(f
clause
clauses)
#((top)
(top)
(top))
#("i"
"i"
"i"))
#(ribcage
#(_ e m1 m2)
#((top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(x)
#((top))
#("i"))))
'#(syntax-object
t
((top)
#(ribcage
#(k e1 e2)
#((top)
(top)
(top))
#("i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(rest)
#((top))
#("i"))
#(ribcage
()
()
())
#(ribcage
#(f
clause
clauses)
#((top)
(top)
(top))
#("i"
"i"
"i"))
#(ribcage
#(_ e m1 m2)
#((top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(x)
#((top))
#("i"))))
(list '#(syntax-object
quote
((top)
#(ribcage
#(k
e1
e2)
#((top)
(top)
(top))
#("i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(rest)
#((top))
#("i"))
#(ribcage
()
()
())
#(ribcage
#(f
clause
clauses)
#((top)
(top)
(top))
#("i"
"i"
"i"))
#(ribcage
#(_
e
m1
m2)
#((top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(x)
#((top))
#("i"))))
syntmp-k-2008))
(cons '#(syntax-object
begin
((top)
#(ribcage
#(k e1 e2)
#((top)
(top)
(top))
#("i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(rest)
#((top))
#("i"))
#(ribcage
()
()
())
#(ribcage
#(f
clause
clauses)
#((top)
(top)
(top))
#("i"
"i"
"i"))
#(ribcage
#(_ e m1 m2)
#((top)
(top)
(top)
(top))
#("i"
"i"
"i"
"i"))
#(ribcage
()
()
())
#(ribcage
#(x)
#((top))
#("i"))))
(cons syntmp-e1-2009
syntmp-e2-2010))
syntmp-rest-2005))
syntmp-tmp-2007)
((lambda (syntmp-_-2013)
(syntax-error syntmp-x-1979))
syntmp-tmp-2006)))
(syntax-dispatch
syntmp-tmp-2006
'(each-any any . each-any))))
syntmp-clause-1989))
syntmp-tmp-2004))
(syntmp-f-1988
(car syntmp-clauses-1990)
(cdr syntmp-clauses-1990)))))))
syntmp-tmp-1981)
(syntax-error syntmp-tmp-1980)))
(syntax-dispatch
syntmp-tmp-1980
'(any any any . each-any))))
syntmp-x-1979)))
(install-global-transformer
'identifier-syntax
(lambda (syntmp-x-2043)
((lambda (syntmp-tmp-2044)
((lambda (syntmp-tmp-2045)
(if syntmp-tmp-2045
(apply (lambda (syntmp-_-2046 syntmp-e-2047)
(list '#(syntax-object
lambda
((top)
#(ribcage #(_ e) #((top) (top)) #("i" "i"))
#(ribcage () () ())
#(ribcage #(x) #((top)) #("i"))))
'(#(syntax-object
x
((top)
#(ribcage #(_ e) #((top) (top)) #("i" "i"))
#(ribcage () () ())
#(ribcage #(x) #((top)) #("i")))))
(list '#(syntax-object
syntax-case
((top)
#(ribcage
#(_ e)
#((top) (top))
#("i" "i"))
#(ribcage () () ())
#(ribcage #(x) #((top)) #("i"))))
'#(syntax-object
x
((top)
#(ribcage
#(_ e)
#((top) (top))
#("i" "i"))
#(ribcage () () ())
#(ribcage #(x) #((top)) #("i"))))
'()
(list '#(syntax-object
id
((top)
#(ribcage
#(_ e)
#((top) (top))
#("i" "i"))
#(ribcage () () ())
#(ribcage #(x) #((top)) #("i"))))
'(#(syntax-object
identifier?
((top)
#(ribcage
#(_ e)
#((top) (top))
#("i" "i"))
#(ribcage () () ())
#(ribcage #(x) #((top)) #("i"))))
(#(syntax-object
syntax
((top)
#(ribcage
#(_ e)
#((top) (top))
#("i" "i"))
#(ribcage () () ())
#(ribcage #(x) #((top)) #("i"))))
#(syntax-object
id
((top)
#(ribcage
#(_ e)
#((top) (top))
#("i" "i"))
#(ribcage () () ())
#(ribcage
#(x)
#((top))
#("i"))))))
(list '#(syntax-object
syntax
((top)
#(ribcage
#(_ e)
#((top) (top))
#("i" "i"))
#(ribcage () () ())
#(ribcage
#(x)
#((top))
#("i"))))
syntmp-e-2047))
(list (cons syntmp-_-2046
'(#(syntax-object
x
((top)
#(ribcage
#(_ e)
#((top) (top))
#("i" "i"))
#(ribcage () () ())
#(ribcage
#(x)
#((top))
#("i"))))
#(syntax-object
...
((top)
#(ribcage
#(_ e)
#((top) (top))
#("i" "i"))
#(ribcage () () ())
#(ribcage
#(x)
#((top))
#("i"))))))
(list '#(syntax-object
syntax
((top)
#(ribcage
#(_ e)
#((top) (top))
#("i" "i"))
#(ribcage () () ())
#(ribcage
#(x)
#((top))
#("i"))))
(cons syntmp-e-2047
'(#(syntax-object
x
((top)
#(ribcage
#(_ e)
#((top) (top))
#("i" "i"))
#(ribcage () () ())
#(ribcage
#(x)
#((top))
#("i"))))
#(syntax-object
...
((top)
#(ribcage
#(_ e)
#((top) (top))
#("i" "i"))
#(ribcage () () ())
#(ribcage
#(x)
#((top))
#("i")))))))))))
syntmp-tmp-2045)
(syntax-error syntmp-tmp-2044)))
(syntax-dispatch
syntmp-tmp-2044
'(any any))))
syntmp-x-2043)))
;;; -*-scheme-*- ;;; GNU Mes --- Maxwell Equations of Software ;;; Copyright (C) 2016, 2017, 2018 Free Software Foundation, Inc. ;;; ;;; This file is part of GNU Mes. ;;; ;;; GNU Mes is free software; you can redistribute it and/or modify it ;;; under the terms of the GNU General Public License as published by ;;; the Free Software Foundation; either version 3 of the License, or (at ;;; your option) any later version. ;;; ;;; GNU Mes is distributed in the hope that it will be useful, but ;;; WITHOUT ANY WARRANTY; without even the implied warranty of ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;;; GNU General Public License for more details. ;;; ;;; You should have received a copy of the GNU General Public License ;;; along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. ;;; Commentary: ;;; This file is generated from psyntax.ss. ;;; Code:
;;; -*-scheme-*-
;;; GNU Mes --- Maxwell Equations of Software
;;; Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
;;;
;;; This file is part of GNU Mes.
;;;
;;; GNU Mes is free software; you can redistribute it and/or modify it
;;; under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 3 of the License, or (at
;;; your option) any later version.
;;;
;;; GNU Mes is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;;; GNU General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;;; srfi-9.mes - records, based on struct.
(define-macro (define-record-type name constructor+params predicate . fields)
(let ((type (make-record-type name (map car fields))))
`(begin
(define ,name ,type)
(define ,(car constructor+params) ,(record-constructor type name (cdr constructor+params)))
(define ,predicate ,(record-predicate type))
(define-record-accessors ,type ,@fields))))
(define (make-record-type type fields . printer)
(let ((printer (if (pair? printer) (car printer))))
(make-struct '<record-type> (cons type (list fields)) printer)))
(define (record-type? o)
(eq? (struct-vtable o) '<record-type>))
(define (struct-vtable o)
(struct-ref o 0))
(define (record-type o)
(struct-ref o 2))
(define (record-predicate type)
(lambda (o)
(and (record? o)
(eq? (record-type o) (record-type type)))))
(define (record? o)
(and (struct? o)
(record-type? (struct-vtable o))))
(define (record-constructor type name params)
(let ((fields (record-fields type))
(record-type (record-type type)))
(lambda (. o)
(if (not (= (length o) (length params))) (error "wrong number of arguments for record-constructor")
(let ((rest (make-list (- (length fields) (length params)))))
(make-struct type (cons name (append o rest)) record-printer))))))
(define record-printer *unspecified*) ; TODO
(define (record-printer o)
(display "#<")
(display (record-type o))
(let* ((vtable (struct-vtable o))
(fields (record-fields vtable)))
(for-each (lambda (field)
(display " ")
(display field)
(display ": ")
(display ((record-getter vtable field) o)))
fields))
(display ">"))
(define (record-fields o)
(struct-ref o 3))
(define-macro (define-record-accessors type . fields)
`(begin
,@(map (lambda (field)
`(define-record-accessor ,type ,field))
fields)))
(define-macro (define-record-accessor type field)
`(begin
(define ,(cadr field) ,(record-getter type (car field)))
(if ,(pair? (cddr field))
(define ,(if (pair? (cddr field)) (caddr field)) ,(record-setter type (car field))))))
(define (record-getter type field)
(let ((i (record-field-index type field)))
(lambda (o . field?)
(if (not (eq? (record-type o) (record-type type))) (error "record getter: record expected" type o)
(if (pair? field?) field
(struct-ref o i))))))
(define (record-setter type field)
(let ((i (record-field-index type field)))
(lambda (o v)
(if (not (eq? (record-type o) (record-type type))) (error "record setter: record expected" type o)
(struct-set! o i v)))))
(define (record-field-index type field)
(+ 3 (or (lst-index (record-fields type) field)
(error "no such field" type field))))
(define (lst-index lst o)
(let loop ((lst lst) (i 0))
(and (pair? lst)
(if (eq? o (car lst)) i
(loop (cdr lst) (1+ i))))))
;; (define-record-type <employee>
;; (make-employee name age salary)
;; employee?
;; (name employe-name)
;; (age employee-age set-employee-age!)
;; (salary employee-salary))
;; (display <employee>)
;; (newline)
;; (display make-employee)
;; (newline)
;; (display "employee-age ")
;; (display employee-age)
;; (newline)
;; (display "set-employee-age! ")
;; (display set-employee-age!)
;; (newline)
;; (define janneke (make-employee "janneke" 49 42))
;; (display janneke)
;; (newline)
;; (display (employee-age janneke))
;; (newline)
;; (display (set-employee-age! janneke 33))
;; (newline)
;; (display (employee-age janneke))
;; (newline)
;;; -*-scheme-*-
;;; GNU Mes --- Maxwell Equations of Software
;;; Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
;;;
;;; This file is part of GNU Mes.
;;;
;;; GNU Mes is free software; you can redistribute it and/or modify it
;;; under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 3 of the License, or (at
;;; your option) any later version.
;;;
;;; GNU Mes is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;;; GNU General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;;; srfi-9.mes - GNU immutable records.
(define-macro (define-immutable-record-type type constructor+params predicate . fields)
`(define-record-type ,type ,constructor+params ,predicate ,@(map (lambda (f) (list-head f 2)) fields)))
(define-macro (set-field o getters value)
`(let ((getter ,(car getters)))
(let* ((type (struct-vtable ,o))
(name (record-type ,o))
(set (getter ,o #t)))
(define (field->value field)
(if (eq? set field) ,value
((record-getter type field) ,o)))
(let* ((fields (record-fields type))
(values (map field->value fields)))
(apply (record-constructor type name fields) values)))))
/*
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __M2_LIB_H
#define __M2_LIB_H
char **environ;
int __stdin;
int __stdout;
int __stderr;
int errno;
// CONSTANT EOF 0xffffffff
// CONSTANT __FILEDES_MAX 512
char* cast_intp_to_charp (int *i);
char* cast_long_to_charp (long i);
long cast_charp_to_long (char const *);
long cast_int_to_long (int i);
long cast_voidp_to_long (void const *);
char *itoa (int number);
char *ltoa (long number);
int __ungetc_p (int filedes);
int eputs (char *s);
int oputs (char *s);
int puts (char *s);
size_t strlen (char *s);
ssize_t _write ();
ssize_t write (int filedes, void *buffer, size_t size);
void __ungetc_clear (int filedes);
void __ungetc_init ();
void __ungetc_set (int filedes, int c);
struct timezone
{
int tz_minuteswest;
int tz_dsttime;
};
struct timespec
{
long tv_sec;
long tv_nsec;
};
struct timeval
{
long tv_sec;
long tv_usec;
};
#endif /* __M2_LIB_H */
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mes/lib-mini.h"
int __stdin;
int __stdout;
int __stderr;
char **environ;
int main (int argc, char **argv, char **envp);
/* FIXME: this is going to be called `FUNCTION__start' */
//#int
//#_start ()
//#{
//# ..
//#}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
void
_exit ()
{
asm ("mov____$i32,%eax SYS_exit");
asm ("mov____0x8(%ebp),%ebx !-4");
asm ("int____$0x80");
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
void
_write (int filedes, void *buffer, int size)
{
asm ("mov____$i32,%eax SYS_write");
asm ("mov____0x8(%ebp),%ebx !-4");
asm ("mov____0x8(%ebp),%ecx !-8");
asm ("mov____0x8(%ebp),%edx !-12");
asm ("int____$0x80");
}
/* * GNU Mes --- Maxwell Equations of Software * Copyright © 2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #include <mes/lib-mini.h> int errno; char **environ; int __stdin; int __stdout; int __stderr;
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
char*
cast_intp_to_charp (int const *i)
{
return i;
}
char*
cast_long_to_charp (long i)
{
return i;
}
long
cast_charp_to_long (char const *i)
{
return i;
}
long
cast_int_to_long (int i)
{
return i;
}
long
cast_voidp_to_long (void const *i)
{
return i;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
void _exit (int code);
void
exit (int code)
{
_exit (code);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
ssize_t
write (int filedes, void const *buffer, size_t size)
{
int r = _write (filedes, buffer, size);
if (r < 0)
{
errno = -r;
r = -1;
}
else
errno = 0;
return r;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include <linux/x86/syscall.h>
int errno;
int
__sys_call (int sys_call)
{
asm ("mov____0x8(%ebp),%eax !-4");
asm ("int____$0x80");
}
int
__sys_call1 (int sys_call, int one)
{
asm ("mov____0x8(%ebp),%eax !-4");
asm ("mov____0x8(%ebp),%ebx !-8");
asm ("int____$0x80");
}
int
__sys_call2 (int sys_call, int one, int two)
{
asm ("mov____0x8(%ebp),%eax !-4");
asm ("mov____0x8(%ebp),%ebx !-8");
asm ("mov____0x8(%ebp),%ecx !-12");
asm ("int____$0x80");
}
int
__sys_call3 (int sys_call, int one, int two, int three)
{
asm ("mov____0x8(%ebp),%eax !-4");
asm ("mov____0x8(%ebp),%ebx !-8");
asm ("mov____0x8(%ebp),%ecx !-12");
asm ("mov____0x8(%ebp),%edx !-16");
asm ("int____$0x80");
}
int
__sys_call4 (int sys_call, int one, int two, int three, int four)
{
asm ("mov____0x8(%ebp),%eax !-4");
asm ("mov____0x8(%ebp),%ebx !-8");
asm ("mov____0x8(%ebp),%ecx !-12");
asm ("mov____0x8(%ebp),%edx !-16");
asm ("mov____0x8(%ebp),%esi !-20");
asm ("int____$0x80");
}
int
_sys_call (int sys_call)
{
int r = __sys_call (sys_call);
if (r < 0)
{
errno = -r;
r = -1;
}
else
errno = 0;
return r;
}
int
_sys_call1 (int sys_call, int one)
{
int r = __sys_call1 (sys_call, one);
if (r < 0)
{
errno = -r;
r = -1;
}
else
errno = 0;
return r;
}
int
_sys_call2 (int sys_call, int one, int two)
{
int r = __sys_call2 (sys_call, one, two);
if (r < 0)
{
errno = -r;
r = -1;
}
else
errno = 0;
return r;
}
int
_sys_call3 (int sys_call, int one, int two, int three)
{
int r = __sys_call3 (sys_call, one, two, three);
if (r < 0)
{
errno = -r;
r = -1;
}
else
errno = 0;
return r;
}
int
_sys_call4 (int sys_call, int one, int two, int three, int four)
{
int r = __sys_call4 (sys_call, one, two, three, four);
if (r < 0)
{
errno = -r;
r = -1;
}
else
errno = 0;
return r;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
// CONSTANT SIGABRT 0
int
__raise (int signum)
{
return -1;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <linux/syscall.h>
#include <arch/syscall.h>
long
brk (void *addr)
{
long long_addr = cast_voidp_to_long (addr);
return _sys_call1 (SYS_brk, long_addr);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2021 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <string.h>
char *__brk = 0;
void *
malloc (size_t size)
{
if (!__brk)
__brk = cast_long_to_charp (brk (0));
if (brk (__brk + size) == -1)
return 0;
char *p = __brk;
__brk = __brk + size;
return p;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
char *
_memset (char *s, int c, size_t n)
{
char *p = s;
while (n != 0)
{
n = n - 1;
s[0] = c;
s = s + 1;
}
return p;
}
void *
memset (void *s, int c, size_t n)
{
return _memset (s, c, n);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <syscall.h>
#include <mes/lib.h>
#include <fcntl.h>
long
read (int filedes, void *buffer, long size)
{
long bytes = _sys_call3 (SYS_read, filedes, buffer, size);
return bytes;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <unistd.h>
int *__ungetc_buf;
int
__ungetc_p (int filedes)
{
if (__ungetc_buf == 0)
__ungetc_init ();
return __ungetc_buf[filedes] >= 0;
}
void
__ungetc_init ()
{
if (__ungetc_buf == 0)
{
int save_errno = errno;
__ungetc_buf = malloc ((__FILEDES_MAX + 1) * sizeof (int));
errno = save_errno;
memset (__ungetc_buf, -1, (__FILEDES_MAX + 1) * sizeof (int));
}
}
void
__ungetc_clear (int filedes)
{
if (__ungetc_buf == 0)
__ungetc_init ();
__ungetc_buf[filedes] = -1;
}
void
__ungetc_set (int filedes, int c)
{
if (__ungetc_buf == 0)
__ungetc_init ();
__ungetc_buf[filedes] = c;
}
int
fdgetc (int fd)
{
if (__ungetc_buf == 0)
__ungetc_init ();
char c;
int i = __ungetc_buf[fd];
if (i >= 0)
__ungetc_buf[fd] = -1;
else
{
int r = read (fd, &c, 1);
if (r < 1)
return -1;
i = c;
}
if (i < 0)
i = i + 256;
return i;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
int
getchar ()
{
return fdgetc (__stdin);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <stdio.h>
int
putchar (int c)
{
char *p = cast_intp_to_charp (&c);
write (__stdout, p, 1);
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <mes/lib.h>
#include <fcntl.h>
#include <stdarg.h>
int
open (char *file_name, int flags, int mask)
{
int r = _sys_call3 (SYS_open, file_name, flags, mask);
if (r > 2)
__ungetc_clear (r);
return r;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
int
mes_open (char *file_name, int flags, int mask)
{
int filedes = open (file_name, flags, mask);
if (filedes > 2)
__ungetc_clear (filedes);
return filedes;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
size_t
strlen (char const *s)
{
int i = 0;
while (s[i] != 0)
i = i + 1;
return i;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib-mini.h>
#include <string.h>
int
eputs (char const *s)
{
int i = strlen (s);
write (__stderr, s, i);
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
int
fdputc (int c, int fd)
{
char *p = cast_intp_to_charp (&c);
write (fd, p, 1);
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
int
eputc (int c)
{
return fdputc (c, __stderr);
}
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017,2022,2023 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __M2_TYPES_H #define __M2_TYPES_H 1 /* #ifndef __MES_CLOCK_T #define __MES_CLOCK_T #undef clock_t typedef long clock_t; #endif */ #ifndef __MES_DEV_T #define __MES_DEV_T #undef dev_t typedef long dev_t; #endif /* #if !defined (__MES_FILE_T) && ! defined (_FILE_T) #define __MES_FILE_T #define _FILE_T typedef long FILE; #endif */ #ifndef __MES_GID_T #define __MES_GID_T #undef gid_t typedef unsigned gid_t; #endif #ifndef __MES_INO_T #define __MES_INO_T #undef ino_t typedef unsigned ino_t; #endif #if __SIZEOF_LONG_LONG__ == 8 #ifndef __MES_INO64_T #define __MES_INO64_T #undef ino64_t typedef unsigned ino64_t; #endif #endif // __SIZEOF_LONG_LONG__ == 8 #if !defined (__MES_INTPTR_T) && !defined (__intptr_t_defined) #define __MES_INTPTR_T #define __intptr_t_defined #undef intptr_t typedef long intptr_t; #undef uintptr_t typedef unsigned uintptr_t; #endif #ifndef __MES_OFF_T #define __MES_OFF_T #undef off_t typedef long off_t; #endif #if __SIZEOF_LONG_LONG__ == 8 #ifndef __MES_OFF64_T #define __MES_OFF64_T #undef off64_t typedef unsigned off64_t; #endif #endif // __SIZEOF_LONG_LONG__ == 8 #ifndef __MES_PID_T #define __MES_PID_T #undef pid_t typedef int pid_t; #endif #ifndef __PTRDIFF_T #define __PTRDIFF_T #ifndef __MES_PTRDIFF_T #define __MES_PTRDIFF_T #undef ptrdiff_t typedef long ptrdiff_t; #endif #endif #ifndef __MES_SIGVAL_T #define __MES_SIGVAL_T #undef clock_t typedef long sigval_t; #endif #ifndef __SIZE_T #define __SIZE_T #ifndef __MES_SIZE_T #define __MES_SIZE_T typedef unsigned size_t; #endif #endif #ifndef __MES_SSIZE_T #define __MES_SSIZE_T #undef ssize_t typedef long ssize_t; #endif #ifndef __MES_UID_T #define __MES_UID_T #undef uid_t typedef unsigned uid_t; #endif #ifndef __WCHAR_T #define __WCHAR_T #ifndef __MES_WCHAR_T #define __MES_WCHAR_T #undef wchar_t typedef int wchar_t; #endif #endif #endif // __M2_TYPES_H
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2020,2021 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __MES_MES_H
#define __MES_MES_H
#include <sys/types.h>
#include "mes/cc.h"
struct scm
{
long type;
union
{
struct scm *car;
long car_value;
char *bytes;
long length;
struct scm *ref;
struct scm *variable;
struct scm *macro;
long port;
};
union
{
struct scm *cdr;
long cdr_value;
struct scm *closure;
struct scm *continuation;
char *name;
struct scm *string;
struct scm *structure;
long value;
FUNCTION function;
struct scm *vector;
};
};
/* mes */
extern char *g_datadir;
extern int g_debug;
extern char *g_buf;
extern int g_continuations;
extern struct scm *g_symbols;
extern struct scm *g_symbol_max;
extern int g_mini;
/* a/env */
extern struct scm *R0;
/* param 1 */
extern struct scm *R1;
/* save 2 */
extern struct scm *R2;
/* continuation */
extern struct scm *R3;
/* current-module */
extern struct scm *M0;
/* macro */
extern struct scm *g_macros;
extern struct scm *g_ports;
/* gc */
extern size_t ARENA_SIZE;
extern size_t MAX_ARENA_SIZE;
extern size_t STACK_SIZE;
extern size_t JAM_SIZE;
extern size_t GC_SAFETY;
extern size_t MAX_STRING;
extern char *g_arena;
extern struct scm *cell_arena;
extern struct scm *cell_zero;
extern struct scm *g_free;
extern struct scm *g_symbol;
extern struct scm **g_stack_array;
extern struct scm *g_cells;
extern struct scm *g_news;
extern long g_stack;
extern size_t gc_count;
extern struct timespec *gc_start_time;
extern struct timespec *gc_end_time;
extern size_t gc_time;
extern char **__execl_c_argv;
extern char *__open_boot_buf;
extern char *__open_boot_file_name;
extern char *__setenv_buf;
extern char *__reader_read_char_buf;
extern struct timespec *g_start_time;
extern struct timeval *__gettimeofday_time;
extern struct timespec *__get_internal_run_time_ts;
struct scm *cast_charp_to_scmp (char const *i);
struct scm **cast_charp_to_scmpp (char const *i);
char *cast_voidp_to_charp (void const *i);
long cast_scmp_to_long (struct scm *i);
char *cast_scmp_to_charp (struct scm *i);
struct scm *alloc (long n);
struct scm *apply (struct scm *f, struct scm *x, struct scm *a);
struct scm *apply_builtin (struct scm *fn, struct scm *x);
struct scm *apply_builtin0 (struct scm *fn);
struct scm *apply_builtin1 (struct scm *fn, struct scm *x);
struct scm *apply_builtin2 (struct scm *fn, struct scm *x, struct scm *y);
struct scm *apply_builtin3 (struct scm *fn, struct scm *x, struct scm *y, struct scm *z);
struct scm *builtin_name (struct scm *builtin);
struct scm *cstring_to_list (char const *s);
struct scm *cstring_to_symbol (char const *s);
struct scm *cell_ref (struct scm *cell, long index);
struct scm *fdisplay_ (struct scm *, int, int);
struct scm *init_symbols ();
struct scm *init_time (struct scm *a);
struct scm *make_builtin_type ();
struct scm *make_bytes (char const *s, size_t length);
struct scm *make_cell (long type, struct scm *car, struct scm *cdr);
struct scm *make_pointer_cell (long type, long car, void *cdr);
struct scm *make_value_cell (long type, long car, long cdr);
struct scm *make_char (int n);
struct scm *make_continuation (long n);
struct scm *make_hash_table_ (long size);
struct scm *make_hashq_type ();
struct scm *make_initial_module (struct scm *a);
struct scm *make_macro (struct scm *name, struct scm *x);
struct scm *make_number (long n);
struct scm *make_ref (struct scm *x);
struct scm *make_string (char const *s, size_t length);
struct scm *make_string0 (char const *s);
struct scm *make_string_port (struct scm *x);
struct scm *make_vector_ (long k, struct scm *e);
struct scm *mes_builtins (struct scm *a);
struct scm *push_cc (struct scm *p1, struct scm *p2, struct scm *a, struct scm *c);
struct scm *struct_ref_ (struct scm *x, long i);
struct scm *struct_set_x_ (struct scm *x, long i, struct scm *e);
struct scm *vector_ref_ (struct scm *x, long i);
struct scm *vector_set_x_ (struct scm *x, long i, struct scm *e);
FUNCTION builtin_function (struct scm *builtin);
char *cell_bytes (struct scm *x);
int peekchar ();
int readchar ();
int unreadchar ();
long gc_free ();
long length__ (struct scm *x);
size_t bytes_cells (size_t length);
void assert_max_string (size_t i, char const *msg, char const *string);
void assert_msg (int check, char *msg);
void assert_number (char const *name, struct scm *x);
void copy_cell (struct scm *to, struct scm *from);
void gc_ ();
void gc_dump_arena (struct scm *cells, long size);
void gc_init ();
void gc_peek_frame ();
void gc_pop_frame ();
void gc_push_frame ();
void gc_stats_ (char const* where);
void init_symbols_ ();
long seconds_and_nanoseconds_to_long (long s, long ns);
#include "mes/builtins.h"
#include "mes/constants.h"
#include "mes/symbols.h"
#endif /* __MES_MES_H */
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_BUILTINS_H #define __MES_BUILTINS_H /* src/builtins.c */ struct scm *make_builtin (struct scm *builtin_type, struct scm *name, struct scm *arity, struct scm *function); struct scm *builtin_name (struct scm *builtin); struct scm *builtin_arity (struct scm *builtin); struct scm *builtin_p (struct scm *x); struct scm *builtin_printer (struct scm *builtin); /* src/core.c */ struct scm *car (struct scm *x); struct scm *cdr (struct scm *x); struct scm *list (struct scm *x); struct scm *null_p (struct scm *x); struct scm *eq_p (struct scm *x, struct scm *y); struct scm *values (struct scm *x); struct scm *acons (struct scm *key, struct scm *value, struct scm *alist); struct scm *length (struct scm *x); struct scm *error (struct scm *key, struct scm *x); struct scm *append2 (struct scm *x, struct scm *y); struct scm *append_reverse (struct scm *x, struct scm *y); struct scm *reverse_x_ (struct scm *x, struct scm *t); struct scm *assq (struct scm *x, struct scm *a); struct scm *assoc (struct scm *x, struct scm *a); /* src/display.c */ struct scm *display_ (struct scm *x); struct scm *display_error_ (struct scm *x); struct scm *display_port_ (struct scm *x, struct scm *p); struct scm *write_ (struct scm *x); struct scm *write_error_ (struct scm *x); struct scm *write_port_ (struct scm *x, struct scm *p); /* src/eval-apply.c */ struct scm *pairlis (struct scm *x, struct scm *y, struct scm *a); struct scm *set_car_x (struct scm *x, struct scm *e); struct scm *set_cdr_x (struct scm *x, struct scm *e); struct scm *set_env_x (struct scm *x, struct scm *e, struct scm *a); struct scm *add_formals (struct scm *formals, struct scm *x); struct scm *eval_apply (); /* src/gc.c */ struct scm *gc_stats (); struct scm *cons (struct scm *x, struct scm *y); struct scm *gc_check (); struct scm *gc (); /* src/hash.c */ struct scm *hashq (struct scm *x, struct scm *size); struct scm *hash (struct scm *x, struct scm *size); struct scm *hashq_get_handle (struct scm *table, struct scm *key, struct scm *dflt); struct scm *hashq_ref (struct scm *table, struct scm *key, struct scm *dflt); struct scm *hash_ref (struct scm *table, struct scm *key, struct scm *dflt); struct scm *hashq_set_x (struct scm *table, struct scm *key, struct scm *value); struct scm *hash_set_x (struct scm *table, struct scm *key, struct scm *value); struct scm *hash_table_printer (struct scm *table); struct scm *make_hash_table (struct scm *x); /* src/lib.c */ struct scm *type_ (struct scm *x); struct scm *car_ (struct scm *x); struct scm *cdr_ (struct scm *x); struct scm *xassq (struct scm *x, struct scm *a); struct scm *memq (struct scm *x, struct scm *a); struct scm *equal2_p (struct scm *a, struct scm *b); struct scm *last_pair (struct scm *x); struct scm *pair_p (struct scm *x); struct scm *char_to_integer (struct scm *x); struct scm *integer_to_char (struct scm *x); /* src/math.c */ struct scm *greater_p (struct scm *x); struct scm *less_p (struct scm *x); struct scm *is_p (struct scm *x); struct scm *minus (struct scm *x); struct scm *plus (struct scm *x); struct scm *divide (struct scm *x); struct scm *modulo (struct scm *a, struct scm *b); struct scm *multiply (struct scm *x); struct scm *logand (struct scm *x); struct scm *logior (struct scm *x); struct scm *lognot (struct scm *x); struct scm *logxor (struct scm *x); struct scm *ash (struct scm *n, struct scm *count); /* src/module.c */ struct scm *make_module_type (); struct scm *module_printer (struct scm *module); struct scm *module_variable (struct scm *module, struct scm *name); struct scm *module_ref (struct scm *module, struct scm *name); struct scm *module_define_x (struct scm *module, struct scm *name, struct scm *value); /* src/posix.c */ struct scm *abort_ (); struct scm *exit_ (struct scm *x); struct scm *peek_byte (); struct scm *read_byte (); struct scm *unread_byte (struct scm *i); struct scm *peek_char (); struct scm *read_char (struct scm *port); struct scm *unread_char (struct scm *i); struct scm *write_char (struct scm *i); struct scm *write_byte (struct scm *x); struct scm *getenv_ (struct scm *s); struct scm *setenv_ (struct scm *s, struct scm *v); struct scm *access_p (struct scm *file_name, struct scm *mode); struct scm *current_input_port (); struct scm *open_input_file (struct scm *file_name); struct scm *open_input_string (struct scm *string); struct scm *set_current_input_port (struct scm *port); struct scm *current_output_port (); struct scm *current_error_port (); struct scm *open_output_file (struct scm *x); struct scm *set_current_output_port (struct scm *port); struct scm *set_current_error_port (struct scm *port); struct scm *chmod_ (struct scm *file_name, struct scm *mode); struct scm *isatty_p (struct scm *port); struct scm *primitive_fork (); struct scm *execl_ (struct scm *file_name, struct scm *args); struct scm *waitpid_ (struct scm *pid, struct scm *options); struct scm *current_time (); struct scm *gettimeofday_ (); struct scm *get_internal_run_time (); struct scm *getcwd_ (); struct scm *dup_ (struct scm *port); struct scm *dup2_ (struct scm *old, struct scm *new); struct scm *delete_file (struct scm *file_name); /* src/reader.c */ struct scm *read_input_file_env_ (struct scm *e, struct scm *a); struct scm *read_input_file_env (struct scm *a); struct scm *read_env (struct scm *a); struct scm *reader_read_sexp (struct scm *c, struct scm *s, struct scm *a); struct scm *reader_read_character (); struct scm *reader_read_binary (); struct scm *reader_read_octal (); struct scm *reader_read_hex (); struct scm *reader_read_string (); /* src/stack.c */ struct scm *frame_printer (struct scm *frame); struct scm *make_stack (struct scm *stack); struct scm *stack_length (struct scm *stack); struct scm *stack_ref (struct scm *stack, struct scm *index); /* src/string.c */ struct scm *string_equal_p (struct scm *a, struct scm *b); struct scm *symbol_to_string (struct scm *symbol); struct scm *symbol_to_keyword (struct scm *symbol); struct scm *keyword_to_string (struct scm *keyword); struct scm *string_to_symbol (struct scm *string); struct scm *make_symbol (struct scm *string); struct scm *string_to_list (struct scm *string); struct scm *list_to_string (struct scm *list); struct scm *read_string (struct scm *port); struct scm *string_append (struct scm *x); struct scm *string_length (struct scm *string); struct scm *string_ref (struct scm *str, struct scm *k); /* src/struct.c */ struct scm *make_struct (struct scm *type, struct scm *fields, struct scm *printer); struct scm *struct_length (struct scm *x); struct scm *struct_ref (struct scm *x, struct scm *i); struct scm *struct_set_x (struct scm *x, struct scm *i, struct scm *e); /* src/vector.c */ struct scm *make_vector (struct scm *x); struct scm *vector_length (struct scm *x); struct scm *vector_ref (struct scm *x, struct scm *i); struct scm *vector_entry (struct scm *x); struct scm *vector_set_x (struct scm *x, struct scm *i, struct scm *e); struct scm *list_to_vector (struct scm *x); struct scm *vector_to_list (struct scm *v); #endif /* __MES_BUILTINS_H */
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_CONSTANTS_H #define __MES_CONSTANTS_H /* Cell types */ // CONSTANT TCHAR 0 #define TCHAR 0 // CONSTANT TBYTES 1 #define TBYTES 1 // CONSTANT TCLOSURE 2 #define TCLOSURE 2 // CONSTANT TCONTINUATION 3 #define TCONTINUATION 3 // CONSTANT TKEYWORD 4 #define TKEYWORD 4 // CONSTANT TMACRO 5 #define TMACRO 5 // CONSTANT TNUMBER 6 #define TNUMBER 6 // CONSTANT TPAIR 7 #define TPAIR 7 // CONSTANT TPORT 8 #define TPORT 8 // CONSTANT TREF 9 #define TREF 9 // CONSTANT TSPECIAL 10 #define TSPECIAL 10 // CONSTANT TSTRING 11 #define TSTRING 11 // CONSTANT TSTRUCT 12 #define TSTRUCT 12 // CONSTANT TSYMBOL 13 #define TSYMBOL 13 // CONSTANT TVALUES 14 #define TVALUES 14 // CONSTANT TVARIABLE 15 #define TVARIABLE 15 // CONSTANT TVECTOR 16 #define TVECTOR 16 // CONSTANT TBROKEN_HEART 17 #define TBROKEN_HEART 17 /* Struct types */ // CONSTANT STRUCT_TYPE 0 #define STRUCT_TYPE 0 // CONSTANT STRUCT_PRINTER 1 #define STRUCT_PRINTER 1 // CONSTANT GC_FRAME_SIZE 5 #define GC_FRAME_SIZE 5 // CONSTANT GC_FRAME_PROCEDURE 4 #define GC_FRAME_PROCEDURE 4 // CONSTANT STDIN 0 // CONSTANT STDOUT 1 // CONSTANT STDERR 2 /* Unknown type 1 // CONSTANT EOF -1 */ // CONSTANT O_RDONLY 0 // CONSTANT O_WRONLY 1 // CONSTANT O_CREAT 0x40 // CONSTANT O_TRUNC 0x200 // CONSTANT PATH_MAX 1024 // CONSTANT __FILEDES_MAX 512 // CONSTANT S_IRUSR 00400 // CONSTANT S_IWUSR 00200 // CONSTANT CLOCK_PROCESS_CPUTIME_ID 2 #endif /* __MES_CONSTANTS_H */
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2016,2017,2018,2019,2021 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_SYMBOLS_H #define __MES_SYMBOLS_H extern struct scm *cell_nil; extern struct scm *cell_f; extern struct scm *cell_t; extern struct scm *cell_dot; extern struct scm *cell_arrow; extern struct scm *cell_undefined; extern struct scm *cell_unspecified; extern struct scm *cell_closure; extern struct scm *cell_circular; extern struct scm *cell_vm_apply; extern struct scm *cell_vm_apply2; extern struct scm *cell_vm_begin; extern struct scm *cell_vm_begin_eval; extern struct scm *cell_vm_begin_expand; extern struct scm *cell_vm_begin_expand_eval; extern struct scm *cell_vm_begin_expand_macro; extern struct scm *cell_vm_begin_expand_primitive_load; extern struct scm *cell_vm_begin_primitive_load; extern struct scm *cell_vm_begin_read_input_file; extern struct scm *cell_vm_call_with_current_continuation2; extern struct scm *cell_vm_call_with_values2; extern struct scm *cell_vm_eval; extern struct scm *cell_vm_eval2; extern struct scm *cell_vm_eval_check_func; extern struct scm *cell_vm_eval_define; extern struct scm *cell_vm_eval_macro_expand_eval; extern struct scm *cell_vm_eval_macro_expand_expand; extern struct scm *cell_vm_eval_pmatch_car; extern struct scm *cell_vm_eval_pmatch_cdr; extern struct scm *cell_vm_eval_set_x; extern struct scm *cell_vm_evlis; extern struct scm *cell_vm_evlis2; extern struct scm *cell_vm_evlis3; extern struct scm *cell_vm_if; extern struct scm *cell_vm_if_expr; extern struct scm *cell_vm_macro_expand; extern struct scm *cell_vm_macro_expand_car; extern struct scm *cell_vm_macro_expand_cdr; extern struct scm *cell_vm_macro_expand_define; extern struct scm *cell_vm_macro_expand_define_macro; extern struct scm *cell_vm_macro_expand_lambda; extern struct scm *cell_vm_macro_expand_set_x; extern struct scm *cell_vm_return; extern struct scm *cell_symbol_lambda; extern struct scm *cell_symbol_begin; extern struct scm *cell_symbol_if; extern struct scm *cell_symbol_quote; extern struct scm *cell_symbol_define; extern struct scm *cell_symbol_define_macro; extern struct scm *cell_symbol_quasiquote; extern struct scm *cell_symbol_unquote; extern struct scm *cell_symbol_unquote_splicing; extern struct scm *cell_symbol_syntax; extern struct scm *cell_symbol_quasisyntax; extern struct scm *cell_symbol_unsyntax; extern struct scm *cell_symbol_unsyntax_splicing; extern struct scm *cell_symbol_set_x; extern struct scm *cell_symbol_sc_expand; extern struct scm *cell_symbol_macro_expand; extern struct scm *cell_symbol_portable_macro_expand; extern struct scm *cell_symbol_sc_expander_alist; extern struct scm *cell_symbol_call_with_values; extern struct scm *cell_symbol_call_with_current_continuation; extern struct scm *cell_symbol_boot_module; extern struct scm *cell_symbol_current_module; extern struct scm *cell_symbol_primitive_load; extern struct scm *cell_symbol_car; extern struct scm *cell_symbol_cdr; extern struct scm *cell_symbol_not_a_number; extern struct scm *cell_symbol_not_a_pair; extern struct scm *cell_symbol_system_error; extern struct scm *cell_symbol_throw; extern struct scm *cell_symbol_unbound_variable; extern struct scm *cell_symbol_wrong_number_of_args; extern struct scm *cell_symbol_wrong_type_arg; extern struct scm *cell_symbol_buckets; extern struct scm *cell_symbol_builtin; extern struct scm *cell_symbol_frame; extern struct scm *cell_symbol_hashq_table; extern struct scm *cell_symbol_module; extern struct scm *cell_symbol_procedure; extern struct scm *cell_symbol_record_type; extern struct scm *cell_symbol_size; extern struct scm *cell_symbol_stack; extern struct scm *cell_symbol_argv; extern struct scm *cell_symbol_mes_datadir; extern struct scm *cell_symbol_mes_version; extern struct scm *cell_symbol_internal_time_units_per_second; extern struct scm *cell_symbol_compiler; extern struct scm *cell_symbol_arch; extern struct scm *cell_symbol_pmatch_car; extern struct scm *cell_symbol_pmatch_cdr; extern struct scm *cell_type_bytes; extern struct scm *cell_type_char; extern struct scm *cell_type_closure; extern struct scm *cell_type_continuation; extern struct scm *cell_type_function; extern struct scm *cell_type_keyword; extern struct scm *cell_type_macro; extern struct scm *cell_type_number; extern struct scm *cell_type_pair; extern struct scm *cell_type_port; extern struct scm *cell_type_ref; extern struct scm *cell_type_special; extern struct scm *cell_type_string; extern struct scm *cell_type_struct; extern struct scm *cell_type_symbol; extern struct scm *cell_type_values; extern struct scm *cell_type_variable; extern struct scm *cell_type_vector; extern struct scm *cell_type_broken_heart; extern struct scm *cell_symbol_program; extern struct scm *cell_symbol_test; // CONSTANT SYMBOL_MAX 114 #define SYMBOL_MAX 114 // CONSTANT CELL_UNSPECIFIED 7 #define CELL_UNSPECIFIED 7 // CONSTANT CELL_SYMBOL_RECORD_TYPE 82 #define CELL_SYMBOL_RECORD_TYPE 82 #endif /* __MES_SYMBOLS_H */
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <assert.h>
void
__assert_fail (char const *msg, char const *file, unsigned line,
char const *function)
{
if (file && file[0])
{
eputs (file);
eputs (":");
}
if (line)
{
eputs (itoa (line));
eputs (":");
}
if (function && function[0])
{
eputs (function);
eputs (":");
}
eputs ("assert fail: ");
eputs (msg);
eputs ("\n");
char *fail = 0;
fail[0] = 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2019 Jeremiah Orians <jeremiah@pdp10.guru>
* Copyright © 2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <assert.h>
#include <stdlib.h>
void
assert_msg (int bool, char *msg)
{
if (bool == 0)
__assert_fail (msg, 0, 0, 0);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
int
strncmp (char const *a, char const *b, size_t size)
{
if (size == 0)
return 0;
while (a[0] != 0 && b[0] != 0 && a[0] == b[0] && size > 1)
{
size = size - 1;
a = a + 1;
b = b + 1;
}
return a[0] - b[0];
}
/*
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <string.h>
#include <stdlib.h>
// CONSTANT M2_PTR_SIZE 4
#define M2_PTR_SIZE 1
char *
getenv (char const *s)
{
/* eputs ("\ngetenv s="); eputs (s); eputs ("\n"); */
char **p = environ;
char *q;
int length = strlen (s);
while (p[0] != 0)
{
/* eputs ("getenv p[0]="); eputs (p[0]); eputs ("\n"); */
if (strncmp (s, p[0], length) == 0)
{
/* eputs ("found!\n"); */
q = p[0] + length;
if (q[0] == '=')
return q + 1;
}
/* else */
/* eputs ("not found!\n"); */
p = p + M2_PTR_SIZE;
}
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <string.h>
int
fdputs (char const *s, int fd)
{
int i = strlen (s);
write (fd, s, i);
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2020,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#if __M2_PLANET__ || (!(__MESC__ && __arm__) && !(__TINYC__ && __arm__ && BOOTSTRAP))
size_t
__mesabi_uldiv (size_t a, size_t b, size_t *remainder)
{
remainder[0] = a % b;
return a / b;
}
#endif
char *__itoa_buf;
char *
ntoab (long x, unsigned base, int signed_p)
{
if (__itoa_buf == 0)
__itoa_buf = malloc (20);
char *p = __itoa_buf + 11;
p[0] = 0;
p = p - 1;
assert_msg (base > 0, "base > 0");
int sign_p = 0;
size_t i;
size_t u;
size_t b = base;
if (signed_p != 0 && x < 0)
{
sign_p = 1;
/* Avoid LONG_MIN */
u = (-(x + 1));
u = u + 1;
}
else
u = x;
do
{
u = __mesabi_uldiv (u, b, &i);
if (i > 9)
p[0] = 'a' + i - 10;
else
p[0] = '0' + i;
p = p - 1;
}
while (u != 0);
if (sign_p && p[1] != '0')
{
p[0] = '-';
p = p - 1;
}
return p + 1;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
* Copyright © 2023 Rick Masters <grick23@gmail.com>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
int
isdigit (int c)
{
return c >= '0' && c <= '9';
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <ctype.h>
int
isxdigit (int c)
{
return isdigit (c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
int
isspace (int c)
{
return (c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r' || c == ' ');
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ctype.h>
int
isnumber (int c, int base)
{
if (base == 2)
return (c >= '0') && (c <= '1');
if (base == 8)
return (c >= '0') && (c <= '7');
if (base == 10)
return isdigit (c);
if (base == 16)
return isxdigit (c);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
* Copyright © 2022,2023 Rick Masters <grick23@gmail.com>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <ctype.h>
long
abtol (char const **p, int base)
{
char const *s = p[0];
int i = 0;
int sign_p = 0;
int m = '0';
if (base == 0)
base = 10;
while (isspace (s[0]) != 0)
s = s + 1;
if (s[0] != 0 && s[0] == '+')
s = s + 1;
if (s[0] != 0 && s[0] == '-')
{
sign_p = 1;
s = s + 1;
}
while (isnumber (s[0], base) != 0)
{
i = i * base;
if (s[0] >= 'a')
m = 'a' - 10;
else
{
if (s[0] >= 'A')
m = 'A' - 10;
else
m = '0';
}
i = i + s[0] - m;
s = s + 1;
}
p[0] = s;
if (sign_p != 0)
return -i;
return i;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
int
atoi (char const *string)
{
char const *p = string;
return abtol (&p, 0);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <string.h>
char *
_memcpy (char *dest, char const *src, size_t n)
{
char *p = dest;
while (n != 0)
{
n = n - 1;
dest[0] = src[0];
dest = dest + 1;
src = src + 1;
}
return p;
}
void *
memcpy (void *dest, void const *src, size_t n)
{
return _memcpy (dest, src, n);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
void
free (void *ptr)
{
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <string.h>
void *
realloc (void *ptr, size_t size)
{
void *new = malloc (size);
if (ptr != 0 && new != 0)
{
memcpy (new, ptr, size);
free (ptr);
}
return new;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
char *
strcpy (char *dest, char const *src)
{
char *p = dest;
while (src[0] != 0)
{
p[0] = src[0];
p = p + 1;
src = src + 1;
}
p[0] = 0;
return dest;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
char *
itoa (int x)
{
return ntoab (x, 10, 1);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
char *
ltoa (long x)
{
return ntoab (x, 10, 1);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
int
fdungetc (int c, int fd)
{
if (c == -1)
return c;
else if (__ungetc_p (fd))
{
eputs (" ***MES C LIB*** fdungetc ungetc buffer overflow fd=");
eputs (itoa (fd));
eputs ("\n");
assert_msg (0, "0");
}
__ungetc_set (fd, c);
return c;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <string.h>
#include <stdlib.h>
// CONSTANT M2_PTR_SIZE 4
#define M2_PTR_SIZE 1
int
setenv (char const *s, char const *v, int overwrite_p)
{
char **p = environ;
int length = strlen (s);
char* q;
while (p[0] != 0)
{
if (strncmp (s, p[0], length) == 0)
{
q = p[0] + length;
if (q[0] == '=')
break;
}
p = p + M2_PTR_SIZE;
}
char *entry = malloc (length + strlen (v) + 2);
int end_p = p[0] == 0;
p[0] = entry;
strcpy (entry, s);
strcpy (entry + length, "=");
strcpy (entry + length + 1, v);
entry[length + strlen (v) + 2] = 0;
if (end_p != 0)
p[1] = 0;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <linux/syscall.h>
#include <arch/syscall.h>
int
access (char const *file_name, int how)
{
long long_file_name = cast_charp_to_long (file_name);
long long_how = cast_int_to_long (how);
return _sys_call2 (SYS_access, long_file_name, long_how);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __LINUX_M2_KERNEL_STAT_H
#define __LINUX_M2_KERNEL_STAT_H
/* https://github.com/torvalds/linux/blob/master/arch/x86/include/uapi/asm/stat.h */
/* *INDENT-OFF* */
struct stat
{
unsigned st_dev;
unsigned st_ino;
char st_mode[2];
char st_nlink[2];
char st_uid[2];
char st_gid[2];
unsigned st_rdev;
unsigned st_size;
unsigned st_blksize;
unsigned st_blocks;
unsigned st_atime;
unsigned st_atime_usec;
unsigned st_mtime;
unsigned st_mtime_usec;
unsigned st_ctime;
unsigned st_ctime_usec;
unsigned __pad0;
unsigned __pad1;
};
#endif /* __LINUX_M2_KERNEL_STAT_H */
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * Copyright © 2022 Dor Askayo <dor.askayo@gmail.com> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_SYS_STAT_H #define __MES_SYS_STAT_H 1 #if SYSTEM_LIBC #undef __MES_SYS_STAT_H #include_next <sys/stat.h> #else // ! SYSTEM_LIBC #include <time.h> #include <sys/types.h> #include <arch/kernel-stat.h> #ifndef __MES_MODE_T #define __MES_MODE_T typedef int mode_t; #endif int chmod (char const *file_name, mode_t mode); int fstat (int filedes, struct stat *buf); int mkdir (char const *file_name, mode_t mode); int mknod (char const *file_name, mode_t mode, dev_t dev); int chown (char const *file_name, uid_t owner, gid_t group); int rmdir (char const *file_name); int stat (char const *file_name, struct stat *buf); #define S_IFIFO 0010000 #define S_IFCHR 0020000 #define S_IFDIR 0040000 #define S_IFBLK 0060000 #define S_IFREG 0100000 #define S_IFLNK 0120000 #define S_IFMT 0170000 #define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) #define S_IRWXU 00700 #define S_IXUSR 00100 #define S_IWUSR 00200 #define S_IRUSR 00400 #define S_ISUID 04000 #define S_ISGID 02000 #define S_IXGRP 00010 #define S_IXOTH 00001 #define S_IRGRP 00040 #define S_IROTH 00004 #define S_IWGRP 00020 #define S_IWOTH 00002 #define S_IRWXG 00070 #define S_IRWXO 00007 #endif // ! SYSTEM_LIBC #endif // __MES_SYS_STAT_H
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <syscall.h>
#include <sys/stat.h>
int
chmod (char const *file_name, int mask)
{
long long_file_name = file_name;
long long_mask = mask;
return _sys_call2 (SYS_chmod, long_file_name, long_mask);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <mes/lib.h>
int
ioctl3 (int filedes, size_t command, long data)
{
long long_filedes = cast_int_to_long (filedes);
int r = _sys_call3 (SYS_ioctl, long_filedes, command, data);
return r;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <string.h>
#include <termio.h>
// CONSTANT TCGETS 0x5401
#define TCGETS 0x5401
struct ktermios
{
unsigned c_iflag;
unsigned c_oflag;
unsigned c_cflag;
unsigned c_lflag;
char c_line;
char c_cc[19];
unsigned c_ispeed;
unsigned c_ospeed;
};
struct ktermios *__isatty_kernel_termios;
int
isatty (int filedes)
{
if (__isatty_kernel_termios == 0)
__isatty_kernel_termios = malloc (sizeof (struct ktermios));
int r = ioctl3 (filedes, TCGETS, __isatty_kernel_termios);
return r == 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
int
fork ()
{
return _sys_call (SYS_fork);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
int
execve (char const *file_name, char **argv, char **env)
{
long long_file_name = file_name;
long long_argv = argv;
long long_env = env;
return _sys_call3 (SYS_execve, file_name, argv, env);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
int
execv (char *file_name, char **argv)
{
return execve (file_name, argv, environ);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2020,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <sys/types.h>
int
waitpid (int pid, int *status_ptr, int options)
{
long long_pid = pid;
long long_status_ptr = cast_voidp_to_long (status_ptr);
long long_options = options;
//##if __i386__
//# return _sys_call3 (SYS_waitpid, long_pid, long_status_ptr, long_options);
//##elif __x86_64__ || __arm__
return _sys_call4 (SYS_wait4, long_pid, long_status_ptr, long_options, 0);
//##else
//##error arch not supported
//##endif
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <sys/time.h>
int
gettimeofday (struct timeval *tv, struct timezone *tz)
{
long long_tv = cast_voidp_to_long (tv);
long long_tz = cast_voidp_to_long (tz);
return _sys_call2 (SYS_gettimeofday, long_tv, long_tz);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <syscall.h>
#include <time.h>
int
clock_gettime (long clk_id, struct timespec *tp)
{
long long_tp = tp;
return _sys_call2 (SYS_clock_gettime, clk_id, tp);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2022,2023 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <time.h>
char *__tv;
long
time (long* result)
{
int r;
if (__tv == 0)
__tv = malloc (sizeof (struct timeval));
struct timeval *tv = __tv;
r = _sys_call2 (SYS_gettimeofday, tv, 0);
if (r != 0)
return -1;
if (result != 0)
result[0] = tv->tv_sec;
return tv->tv_sec;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <linux/syscall.h>
#include <arch/syscall.h>
char *
_getcwd (char *buffer, size_t size)
{
long long_buffer = cast_charp_to_long (buffer);
int r = _sys_call2 (SYS_getcwd, long_buffer, size);
if (r >= 0)
return buffer;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <mes/mes.h>
#include <limits.h>
#include <stdlib.h>
#include <sys/types.h>
// CONSTANT PATH_MAX 1024
char *__getcwd_buf;
char *
getcwd (char *buffer, int size)
{
if (buffer == 0)
buffer = __getcwd_buf;
if (buffer == 0)
{
__getcwd_buf = malloc (PATH_MAX);
buffer = __getcwd_buf;
}
return _getcwd (buffer, size);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
int
dup (int old)
{
long long_old = old;
return _sys_call1 (SYS_dup, long_old);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
int
dup2 (int old, int new)
{
long long_old = old;
long long_new = new;
return _sys_call2 (SYS_dup2, long_old, long_new);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
int
strcmp (char const *a, char const *b)
{
while (a[0] != 0 && b[0] != 0 && a[0] == b[0])
{
a = a + 1;
b = b + 1;
}
return a[0] - b[0];
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
int
memcmp (void const *s1, void const *s2, size_t size)
{
if (size == 0)
return 0;
char const *a = s1;
char const *b = s2;
while (a[0] == b[0] && size > 1)
{
size = size - 1;
a = a + 1;
b = b + 1;
}
return a[0] - b[0];
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <linux/syscall.h>
#include <arch/syscall.h>
int
unlink (char const *file_name)
{
long long_file_name = cast_charp_to_long (file_name);
return _sys_call1 (SYS_unlink, long_file_name);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mes/lib.h"
#include "mes/mes.h"
struct scm *
make_builtin_type () /*:((internal)) */
{
struct scm *record_type = cell_symbol_record_type;
struct scm *fields = cell_nil;
fields = cons (cstring_to_symbol ("address"), fields);
fields = cons (cstring_to_symbol ("arity"), fields);
fields = cons (cstring_to_symbol ("name"), fields);
fields = cons (fields, cell_nil);
fields = cons (cell_symbol_builtin, fields);
return make_struct (record_type, fields, cell_unspecified);
}
struct scm *
make_builtin (struct scm *builtin_type, struct scm *name, struct scm *arity, struct scm *function)
{
struct scm *values = cell_nil;
values = cons (function, values);
values = cons (arity, values);
values = cons (name, values);
values = cons (cell_symbol_builtin, values);
return make_struct (builtin_type, values, cstring_to_symbol ("builtin-printer"));
}
struct scm *
builtin_name (struct scm *builtin)
{
return struct_ref_ (builtin, 3);
}
struct scm *
builtin_arity (struct scm *builtin)
{
return struct_ref_ (builtin, 4);
}
FUNCTION
builtin_function (struct scm *builtin)
{
struct scm *x = struct_ref_ (builtin, 5);
return x->function;
}
struct scm *
builtin_p (struct scm *x)
{
if (x->type == TSTRUCT)
if (struct_ref_ (x, 2) == cell_symbol_builtin)
return cell_t;
return cell_f;
}
struct scm *
builtin_printer (struct scm *builtin)
{
fdputs ("#<procedure ", __stdout);
display_ (builtin_name (builtin));
fdputc (' ', __stdout);
struct scm *x = builtin_arity (builtin);
int arity = x->value;
if (arity == -1)
fdputc ('_', __stdout);
else
{
fdputc ('(', __stdout);
int i;
for (i = 0; i < arity; i = i + 1)
{
if (i != 0)
fdputc (' ', __stdout);
fdputc ('_', __stdout);
}
}
fdputc ('>', __stdout);
}
struct scm *
init_builtin (struct scm *builtin_type, char const *name, int arity, void* function, struct scm *a)
{
struct scm *s = cstring_to_symbol (name);
long n = cast_voidp_to_long (function);
return acons (s,
make_builtin (builtin_type, symbol_to_string (s), make_number (arity),
make_number (n)), a);
}
struct scm *
mes_builtins (struct scm *a) /*:((internal)) */
{
struct scm *builtin_type = make_builtin_type ();
if (g_mini != 0)
{
a = init_builtin (builtin_type, "cons", 2, &cons, a);
a = init_builtin (builtin_type, "car", 1, &car, a);
a = init_builtin (builtin_type, "list", -1, &list, a);
a = init_builtin (builtin_type, "eq?", 2, &eq_p, a);
a = init_builtin (builtin_type, "-", -1, &minus, a);
a = init_builtin (builtin_type, "+", -1, &plus, a);
a = init_builtin (builtin_type, "core:display", 1, &display_, a);
a = init_builtin (builtin_type, "core:write", 1, &write_, a);
a = init_builtin (builtin_type, "core:display-error", 1, &display_error_, a);
a = init_builtin (builtin_type, "getenv", 1, &getenv_, a);
a = init_builtin (builtin_type, "gc", 0, &gc, a);
a = init_builtin (builtin_type, ">", -1, &greater_p, a);
a = init_builtin (builtin_type, "<", -1, &less_p, a);
a = init_builtin (builtin_type, "make-vector", -1, &make_vector, a);
return a;
}
/* src/builtins.c */
a = init_builtin (builtin_type, "make-builtin", 4, &make_builtin, a);
a = init_builtin (builtin_type, "builtin-name", 1, &builtin_name, a);
a = init_builtin (builtin_type, "builtin-arity", 1, &builtin_arity, a);
a = init_builtin (builtin_type, "builtin?", 1, &builtin_p, a);
a = init_builtin (builtin_type, "builtin-printer", 1, &builtin_printer, a);
/* src/core.c */
a = init_builtin (builtin_type, "car", 1, &car, a);
a = init_builtin (builtin_type, "cdr", 1, &cdr, a);
a = init_builtin (builtin_type, "list", -1, &list, a);
a = init_builtin (builtin_type, "null?", 1, &null_p, a);
a = init_builtin (builtin_type, "eq?", 2, &eq_p, a);
a = init_builtin (builtin_type, "values", -1, &values, a);
a = init_builtin (builtin_type, "acons", 3, &acons, a);
a = init_builtin (builtin_type, "length", 1, &length, a);
a = init_builtin (builtin_type, "error", 2, &error, a);
a = init_builtin (builtin_type, "append2", 2, &append2, a);
a = init_builtin (builtin_type, "append-reverse", 2, &append_reverse, a);
a = init_builtin (builtin_type, "core:reverse!", 2, &reverse_x_, a);
a = init_builtin (builtin_type, "assq", 2, &assq, a);
a = init_builtin (builtin_type, "assoc", 2, &assoc, a);
/* src/display.c */
a = init_builtin (builtin_type, "core:display", 1, &display_, a);
a = init_builtin (builtin_type, "core:display-error", 1, &display_error_, a);
a = init_builtin (builtin_type, "core:display-port", 2, &display_port_, a);
a = init_builtin (builtin_type, "core:write", 1, &write_, a);
a = init_builtin (builtin_type, "core:write-error", 1, &write_error_, a);
a = init_builtin (builtin_type, "core:write-port", 2, &write_port_, a);
/* src/eval-apply.c */
a = init_builtin (builtin_type, "pairlis", 3, &pairlis, a);
a = init_builtin (builtin_type, "set-car!", 2, &set_car_x, a);
a = init_builtin (builtin_type, "set-cdr!", 2, &set_cdr_x, a);
a = init_builtin (builtin_type, "set-env!", 3, &set_env_x, a);
a = init_builtin (builtin_type, "add-formals", 2, &add_formals, a);
a = init_builtin (builtin_type, "eval-apply", 0, &eval_apply, a);
/* src/gc.c */
a = init_builtin (builtin_type, "gc-stats", 0, &gc_stats, a);
a = init_builtin (builtin_type, "cons", 2, &cons, a);
a = init_builtin (builtin_type, "gc-check", 0, &gc_check, a);
a = init_builtin (builtin_type, "gc", 0, &gc, a);
/* src/hash.c */
a = init_builtin (builtin_type, "hashq", 2, &hashq, a);
a = init_builtin (builtin_type, "hash", 2, &hash, a);
a = init_builtin (builtin_type, "hashq-get-handle", 3, &hashq_get_handle, a);
a = init_builtin (builtin_type, "hashq-ref", 3, &hashq_ref, a);
a = init_builtin (builtin_type, "hash-ref", 3, &hash_ref, a);
a = init_builtin (builtin_type, "hashq-set!", 3, &hashq_set_x, a);
a = init_builtin (builtin_type, "hash-set!", 3, &hash_set_x, a);
a = init_builtin (builtin_type, "hash-table-printer", 1, &hash_table_printer, a);
a = init_builtin (builtin_type, "make-hash-table", 1, &make_hash_table, a);
/* src/lib.c */
a = init_builtin (builtin_type, "core:type", 1, &type_, a);
a = init_builtin (builtin_type, "core:car", 1, &car_, a);
a = init_builtin (builtin_type, "core:cdr", 1, &cdr_, a);
a = init_builtin (builtin_type, "xassq", 2, &xassq, a);
a = init_builtin (builtin_type, "memq", 2, &memq, a);
a = init_builtin (builtin_type, "equal2?", 2, &equal2_p, a);
a = init_builtin (builtin_type, "last-pair", 1, &last_pair, a);
a = init_builtin (builtin_type, "pair?", 1, &pair_p, a);
a = init_builtin (builtin_type, "char->integer", 1, &char_to_integer, a);
a = init_builtin (builtin_type, "integer->char", 1, &integer_to_char, a);
/* src/math.c */
a = init_builtin (builtin_type, ">", -1, &greater_p, a);
a = init_builtin (builtin_type, "<", -1, &less_p, a);
a = init_builtin (builtin_type, "=", -1, &is_p, a);
a = init_builtin (builtin_type, "-", -1, &minus, a);
a = init_builtin (builtin_type, "+", -1, &plus, a);
a = init_builtin (builtin_type, "/", -1, ÷, a);
a = init_builtin (builtin_type, "modulo", 2, &modulo, a);
a = init_builtin (builtin_type, "*", -1, &multiply, a);
a = init_builtin (builtin_type, "logand", -1, &logand, a);
a = init_builtin (builtin_type, "logior", -1, &logior, a);
a = init_builtin (builtin_type, "lognot", 1, &lognot, a);
a = init_builtin (builtin_type, "logxor", -1, &logxor, a);
a = init_builtin (builtin_type, "ash", 2, &ash, a);
/* src/module.c */
a = init_builtin (builtin_type, "make-module-type", 0, &make_module_type, a);
a = init_builtin (builtin_type, "module-printer", 1, &module_printer, a);
a = init_builtin (builtin_type, "module-variable", 2, &module_variable, a);
a = init_builtin (builtin_type, "module-ref", 2, &module_ref, a);
a = init_builtin (builtin_type, "module-define!", 3, &module_define_x, a);
/* src/posix.c */
a = init_builtin (builtin_type, "abort", 0, &abort_, a);
a = init_builtin (builtin_type, "exit", 1, &exit_, a);
a = init_builtin (builtin_type, "peek-byte", 0, &peek_byte, a);
a = init_builtin (builtin_type, "read-byte", 0, &read_byte, a);
a = init_builtin (builtin_type, "unread-byte", 1, &unread_byte, a);
a = init_builtin (builtin_type, "peek-char", 0, &peek_char, a);
a = init_builtin (builtin_type, "read-char", -1, &read_char, a);
a = init_builtin (builtin_type, "unread-char", 1, &unread_char, a);
a = init_builtin (builtin_type, "write-char", -1, &write_char, a);
a = init_builtin (builtin_type, "write-byte", -1, &write_byte, a);
a = init_builtin (builtin_type, "getenv", 1, &getenv_, a);
a = init_builtin (builtin_type, "setenv", 2, &setenv_, a);
a = init_builtin (builtin_type, "access?", 2, &access_p, a);
a = init_builtin (builtin_type, "current-input-port", 0, ¤t_input_port, a);
a = init_builtin (builtin_type, "open-input-file", 1, &open_input_file, a);
a = init_builtin (builtin_type, "open-input-string", 1, &open_input_string, a);
a = init_builtin (builtin_type, "set-current-input-port", 1, &set_current_input_port, a);
a = init_builtin (builtin_type, "current-output-port", 0, ¤t_output_port, a);
a = init_builtin (builtin_type, "current-error-port", 0, ¤t_error_port, a);
a = init_builtin (builtin_type, "open-output-file", -1, &open_output_file, a);
a = init_builtin (builtin_type, "set-current-output-port", 1, &set_current_output_port, a);
a = init_builtin (builtin_type, "set-current-error-port", 1, &set_current_error_port, a);
a = init_builtin (builtin_type, "chmod", 2, &chmod_, a);
a = init_builtin (builtin_type, "isatty?", 1, &isatty_p, a);
a = init_builtin (builtin_type, "primitive-fork", 0, &primitive_fork, a);
a = init_builtin (builtin_type, "execl", 2, &execl_, a);
a = init_builtin (builtin_type, "core:waitpid", 2, &waitpid_, a);
a = init_builtin (builtin_type, "current-time", 0, ¤t_time, a);
a = init_builtin (builtin_type, "gettimeofday", 0, &gettimeofday_, a);
a = init_builtin (builtin_type, "get-internal-run-time", 0, &get_internal_run_time, a);
a = init_builtin (builtin_type, "getcwd", 0, &getcwd_, a);
a = init_builtin (builtin_type, "dup", 1, &dup_, a);
a = init_builtin (builtin_type, "dup2", 2, &dup2_, a);
a = init_builtin (builtin_type, "delete-file", 1, &delete_file, a);
/* src/reader.c */
a = init_builtin (builtin_type, "core:read-input-file-env", 2, &read_input_file_env_, a);
a = init_builtin (builtin_type, "read-input-file-env", 1, &read_input_file_env, a);
a = init_builtin (builtin_type, "read-env", 1, &read_env, a);
a = init_builtin (builtin_type, "reader-read-sexp", 3, &reader_read_sexp, a);
a = init_builtin (builtin_type, "reader-read-character", 0, &reader_read_character, a);
a = init_builtin (builtin_type, "reader-read-binary", 0, &reader_read_binary, a);
a = init_builtin (builtin_type, "reader-read-octal", 0, &reader_read_octal, a);
a = init_builtin (builtin_type, "reader-read-hex", 0, &reader_read_hex, a);
a = init_builtin (builtin_type, "reader-read-string", 0, &reader_read_string, a);
/* src/stack.c */
a = init_builtin (builtin_type, "frame-printer", 1, &frame_printer, a);
a = init_builtin (builtin_type, "make-stack", -1, &make_stack, a);
a = init_builtin (builtin_type, "stack-length", 1, &stack_length, a);
a = init_builtin (builtin_type, "stack-ref", 2, &stack_ref, a);
/* src/string.c */
a = init_builtin (builtin_type, "string=?", 2, &string_equal_p, a);
a = init_builtin (builtin_type, "symbol->string", 1, &symbol_to_string, a);
a = init_builtin (builtin_type, "symbol->keyword", 1, &symbol_to_keyword, a);
a = init_builtin (builtin_type, "keyword->string", 1, &keyword_to_string, a);
a = init_builtin (builtin_type, "string->symbol", 1, &string_to_symbol, a);
a = init_builtin (builtin_type, "make-symbol", 1, &make_symbol, a);
a = init_builtin (builtin_type, "string->list", 1, &string_to_list, a);
a = init_builtin (builtin_type, "list->string", 1, &list_to_string, a);
a = init_builtin (builtin_type, "read-string", -1, &read_string, a);
a = init_builtin (builtin_type, "string-append", -1, &string_append, a);
a = init_builtin (builtin_type, "string-length", 1, &string_length, a);
a = init_builtin (builtin_type, "string-ref", 2, &string_ref, a);
/* src/struct.c */
a = init_builtin (builtin_type, "make-struct", 3, &make_struct, a);
a = init_builtin (builtin_type, "struct-length", 1, &struct_length, a);
a = init_builtin (builtin_type, "struct-ref", 2, &struct_ref, a);
a = init_builtin (builtin_type, "struct-set!", 3, &struct_set_x, a);
/* src/vector.c */
a = init_builtin (builtin_type, "make-vector", -1, &make_vector, a);
a = init_builtin (builtin_type, "vector-length", 1, &vector_length, a);
a = init_builtin (builtin_type, "vector-ref", 2, &vector_ref, a);
a = init_builtin (builtin_type, "vector-entry", 1, &vector_entry, a);
a = init_builtin (builtin_type, "vector-set!", 3, &vector_set_x, a);
a = init_builtin (builtin_type, "list->vector", 1, &list_to_vector, a);
a = init_builtin (builtin_type, "vector->list", 1, &vector_to_list, a);
return a;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
/** Commentary:
Essential functions, used by the eval/apply core.
*/
/** Code: */
#include "mes/lib.h"
#include "mes/mes.h"
#include <stdlib.h>
struct scm *
assoc_string (struct scm *x, struct scm *a) /*:((internal)) */
{
struct scm *b;
while (a != cell_nil)
{
b = a->car;
if (b->car->type == TSTRING)
if (string_equal_p (x, b->car) == cell_t)
return b;
a = a->cdr;
}
if (a != cell_nil)
return a->car;
return cell_f;
}
struct scm *
car (struct scm *x)
{
#if !__MESC_MES__
if (x->type != TPAIR)
error (cell_symbol_not_a_pair, cons (x, cell_symbol_car));
#endif
return x->car;
}
struct scm *
cdr (struct scm *x)
{
#if !__MESC_MES__
if (x->type != TPAIR)
error (cell_symbol_not_a_pair, cons (x, cell_symbol_cdr));
#endif
return x->cdr;
}
struct scm *
list (struct scm *x) /*:((arity . n)) */
{
return x;
}
struct scm *
null_p (struct scm *x)
{
if (x == cell_nil)
return cell_t;
return cell_f;
}
struct scm *
eq_p (struct scm *x, struct scm *y)
{
if (x == y)
return cell_t;
int t = x->type;
if (t == TKEYWORD)
{
if (y->type == TKEYWORD)
return string_equal_p (x, y);
return cell_f;
}
if (t == TCHAR)
{
if (y->type != TCHAR)
return cell_f;
if (x->value == y->value)
return cell_t;
return cell_f;
}
if (t == TNUMBER)
{
if (y->type != TNUMBER)
return cell_f;
if (x->value == y->value)
return cell_t;
return cell_f;
}
return cell_f;
}
struct scm *
values (struct scm *x) /*:((arity . n)) */
{
struct scm *v = cons (0, x);
v->type = TVALUES;
return v;
}
struct scm *
acons (struct scm *key, struct scm *value, struct scm *alist)
{
return cons (cons (key, value), alist);
}
long
length__ (struct scm *x) /*:((internal)) */
{
long n = 0;
while (x != cell_nil)
{
n = n + 1;
if (x->type != TPAIR)
return -1;
x = x->cdr;
}
return n;
}
struct scm *
length (struct scm *x)
{
return make_number (length__ (x));
}
struct scm *
error (struct scm *key, struct scm *x)
{
#if !__MESC_MES__ && !__M2_PLANET__
struct scm *throw = module_ref (R0, cell_symbol_throw);
if (throw != cell_undefined)
return apply (throw, cons (key, cons (x, cell_nil)), R0);
#endif
display_error_ (key);
eputs (": ");
write_error_ (x);
eputs ("\n");
assert_msg (0, "ERROR");
exit (1);
}
struct scm *
append2 (struct scm *x, struct scm *y)
{
if (x == cell_nil)
return y;
if (x->type != TPAIR)
error (cell_symbol_not_a_pair, cons (x, cstring_to_symbol ("append2")));
struct scm *r = cell_nil;
while (x != cell_nil)
{
r = cons (x->car, r);
x = x->cdr;
}
return reverse_x_ (r, y);
}
struct scm *
append_reverse (struct scm *x, struct scm *y)
{
if (x == cell_nil)
return y;
if (x->type != TPAIR)
error (cell_symbol_not_a_pair, cons (x, cstring_to_symbol ("append-reverse")));
while (x != cell_nil)
{
y = cons (x->car, y);
x = x->cdr;
}
return y;
}
struct scm *
reverse_x_ (struct scm *x, struct scm *t)
{
if (x != cell_nil && x->type != TPAIR)
error (cell_symbol_not_a_pair, cons (x, cstring_to_symbol ("core:reverse!")));
struct scm *r = t;
while (x != cell_nil)
{
t = x->cdr;
x->cdr = r;
r = x;
x = t;
}
return r;
}
struct scm *
assq (struct scm *x, struct scm *a)
{
if (a->type != TPAIR)
return cell_f;
int t = x->type;
if (t == TSYMBOL || t == TSPECIAL)
while (a != cell_nil)
{
if (x == a->car->car)
return a->car;
a = a->cdr;
}
else if (t == TCHAR || t == TNUMBER)
{
long v = x->value;
while (a != cell_nil)
{
if (v == a->car->car->value)
return a->car;
a = a->cdr;
}
}
else if (t == TKEYWORD)
{
while (a != cell_nil)
{
if (string_equal_p (x, a->car->car) == cell_t)
return a->car;
a = a->cdr;
}
}
else
/* pointer equality, e.g. on strings. */
while (a != cell_nil)
{
if (x == a->car->car)
return a->car;
a = a->cdr;
}
return cell_f;
}
struct scm *
assoc (struct scm *x, struct scm *a)
{
if (x->type == TSTRING)
return assoc_string (x, a);
while (a != cell_nil)
{
if (equal2_p (x, a->car->car) == cell_t)
return a->car;
a = a->cdr;
}
return cell_f;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mes/lib.h"
#include "mes/mes.h"
#include <stdlib.h>
int g_depth;
void
fdwrite_char (char v, int fd)
{
if (v == '\0')
fdputs ("\\nul", fd);
else if (v == '\a')
fdputs ("\\alarm", fd);
else if (v == '\b')
fdputs ("\\backspace", fd);
else if (v == '\t')
fdputs ("\\tab", fd);
else if (v == '\n')
fdputs ("\\newline", fd);
else if (v == '\v')
fdputs ("\\vtab", fd);
else if (v == '\f')
fdputs ("\\page", fd);
/* Nyacc bug
else if (v == '\r') fdputs ("return", fd);
*/
else if (v == 13)
fdputs ("\\return", fd);
else if (v == ' ')
fdputs ("\\space", fd);
else
{
if (v >= 32 && v <= 127)
fdputc ('\\', fd);
fdputc (v, fd);
}
}
void
fdwrite_string_char (char v, int fd)
{
if (v == '\0')
fdputs ("\\0", fd);
else if (v == '\a')
fdputs ("\\a", fd);
else if (v == '\b')
fdputs ("\\b", fd);
else if (v == '\t')
fdputs ("\\t", fd);
else if (v == '\v')
fdputs ("\\v", fd);
else if (v == '\n')
fdputs ("\\n", fd);
else if (v == '\f')
fdputs ("\\f", fd);
/* Nyacc bug
else if (v == '\r') fdputs ("\\r", fd);
else if (v == '\e') fdputs ("\\e", fd);
*/
else if (v == 13)
fdputs ("\\r", fd);
else if (v == 27)
fdputs ("\\e", fd);
else if (v == '\\')
fdputs ("\\\\", fd);
else if (v == '"')
fdputs ("\\\"", fd);
else
fdputc (v, fd);
}
void
fdwrite_string (char *s, int length, int fd)
{
int i;
for (i = 0; i < length; i = i + 1)
fdwrite_string_char (s[i], fd);
}
struct scm *display_helper (struct scm *x, int cont, char *sep, int fd, int write_p);
struct scm *
display_helper (struct scm *x, int cont, char *sep, int fd, int write_p)
{
fdputs (sep, fd);
if (g_depth == 0)
return cell_unspecified;
g_depth = g_depth - 1;
int t = x->type;
if (t == TCHAR)
{
if (write_p == 0)
fdputc (x->value, fd);
else
{
fdputs ("#", fd);
fdwrite_char (x->value, fd);
}
}
else if (t == TCLOSURE)
{
fdputs ("#<closure ", fd);
struct scm *circ = x->cdr->car;
struct scm *name = circ->cdr->car;
struct scm *args = x->cdr->cdr->car;
display_helper (name->car, 0, "", fd, 0);
fdputc (' ', fd);
display_helper (args, 0, "", fd, 0);
fdputs (">", fd);
}
else if (t == TMACRO)
{
fdputs ("#<macro ", fd);
display_helper (x->cdr, cont, "", fd, 0);
fdputs (">", fd);
}
else if (t == TVARIABLE)
{
fdputs ("#<variable ", fd);
display_helper (x->variable->car, cont, "", fd, 0);
fdputs (">", fd);
}
else if (t == TNUMBER)
{
fdputs (itoa (x->value), fd);
}
else if (t == TPAIR)
{
if (cont == 0)
fdputs ("(", fd);
if (x->car == cell_circular && x->cdr->car != cell_closure)
{
fdputs ("(*circ* . ", fd);
int i = 0;
x = x->cdr;
while (x != cell_nil && i < 10)
{
i = i + 1;
fdisplay_ (x->car->car, fd, write_p);
fdputs (" ", fd);
x = x->cdr;
}
fdputs (" ...)", fd);
}
else
{
if (x != 0 && x != cell_nil)
fdisplay_ (x->car, fd, write_p);
if (x->cdr != 0 && x->cdr->type == TPAIR)
display_helper (x->cdr, 1, " ", fd, write_p);
else if (x->cdr != 0 && x->cdr != cell_nil)
{
if (x->cdr->type != TPAIR)
fdputs (" . ", fd);
fdisplay_ (x->cdr, fd, write_p);
}
}
if (cont == 0)
fdputs (")", fd);
}
else if (t == TPORT)
{
fdputs ("#<port ", fd);
fdputs (itoa (x->port), fd);
fdputs (" ", fd);
x = x->string;
fdputc ('"', fd);
fdwrite_string (cell_bytes (x->string), x->length, fd);
fdputc ('"', fd);
fdputs (">", fd);
}
else if (t == TKEYWORD)
{
fdputs ("#:", fd);
fdwrite_string (cell_bytes (x->string), x->length, fd);
}
else if (t == TSTRING)
{
if (write_p == 1)
{
fdputc ('"', fd);
fdwrite_string (cell_bytes (x->string), x->length, fd);
fdputc ('"', fd);
}
else
fdputs (cell_bytes (x->string), fd);
}
else if (t == TSPECIAL || t == TSYMBOL)
fdwrite_string (cell_bytes (x->string), x->length, fd);
else if (t == TREF)
fdisplay_ (x->ref, fd, write_p);
else if (t == TSTRUCT)
{
struct scm *printer = struct_ref_ (x, STRUCT_PRINTER);
if (printer->type == TREF)
printer = printer->ref;
if (printer->type == TCLOSURE || builtin_p (printer) == cell_t)
apply (printer, cons (x, cell_nil), R0);
else
{
fdputs ("#<", fd);
fdisplay_ (x->structure, fd, write_p);
struct scm *t = x->car;
long size = x->length;
long i;
for (i = 2; i < size; i = i + 1)
{
fdputc (' ', fd);
fdisplay_ (cell_ref (x->structure, i), fd, write_p);
}
fdputc ('>', fd);
}
}
else if (t == TVECTOR)
{
fdputs ("#(", fd);
struct scm *t = x->car;
long i;
for (i = 0; i < x->length; i = i + 1)
{
if (i != 0)
fdputc (' ', fd);
fdisplay_ (cell_ref (x->vector, i), fd, write_p);
}
fdputc (')', fd);
}
else
{
fdputs ("<", fd);
fdputs (itoa (t), fd);
fdputs (":", fd);
fdputs (ltoa (cast_voidp_to_long (x)), fd);
fdputs (">", fd);
}
return cell_unspecified;
}
struct scm *
display_ (struct scm *x)
{
g_depth = 5;
return display_helper (x, 0, "", __stdout, 0);
}
struct scm *
display_error_ (struct scm *x)
{
g_depth = 5;
return display_helper (x, 0, "", __stderr, 0);
}
struct scm *
display_port_ (struct scm *x, struct scm *p)
{
assert_msg (p->type == TNUMBER, "p->type == TNUMBER");
return fdisplay_ (x, p->value, 0);
}
struct scm *
write_ (struct scm *x)
{
g_depth = 5;
return display_helper (x, 0, "", __stdout, 1);
}
struct scm *
write_error_ (struct scm *x)
{
g_depth = 5;
return display_helper (x, 0, "", __stderr, 1);
}
struct scm *
write_port_ (struct scm *x, struct scm *p)
{
assert_msg (p->type == TNUMBER, "p->type == TNUMBER");
return fdisplay_ (x, p->value, 1);
}
struct scm *
fdisplay_ (struct scm *x, int fd, int write_p) /*:((internal)) */
{
g_depth = 5;
return display_helper (x, 0, "", fd, write_p);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mes/lib.h"
#include "mes/mes.h"
#include <string.h>
struct scm *
assert_defined (struct scm *x, struct scm *e) /*:((internal)) */
{
if (e == cell_undefined)
return error (cell_symbol_unbound_variable, x);
return e;
}
struct scm *
check_formals (struct scm *f, struct scm *formals, struct scm *args) /*:((internal)) */
{
long flen;
if (formals->type == TNUMBER)
flen = formals->value;
else
flen = length__ (formals);
long alen = length__ (args);
if (alen != flen && alen != -1 && flen != -1)
{
char *s = "apply: wrong number of arguments; expected: ";
eputs (s);
eputs (itoa (flen));
eputs (", got: ");
eputs (itoa (alen));
eputs ("\n");
write_error_ (f);
struct scm *e = make_string0 (s);
return error (cell_symbol_wrong_number_of_args, cons (e, f));
}
return cell_unspecified;
}
struct scm *
check_apply (struct scm *f, struct scm *e) /*:((internal)) */
{
char *type = 0;
if (f == cell_f || f == cell_t)
type = "bool";
if (f == cell_nil)
type = "nil";
if (f == cell_unspecified)
type = "*unspecified*";
if (f == cell_undefined)
type = "*undefined*";
if (f->type == TCHAR)
type = "char";
if (f->type == TNUMBER)
type = "number";
if (f->type == TSTRING)
type = "string";
if (f->type == TSTRUCT && builtin_p (f) == cell_f)
type = "#<...>";
if (f->type == TBROKEN_HEART)
type = "<3";
if (type != 0)
{
char *s = "cannot apply: ";
eputs (s);
eputs (type);
eputs ("[");
write_error_ (e);
eputs ("]\n");
struct scm *e = make_string0 (s);
return error (cell_symbol_wrong_type_arg, cons (e, f));
}
return cell_unspecified;
}
struct scm *
pairlis (struct scm *x, struct scm *y, struct scm *a)
{
if (x == cell_nil)
return a;
if (x->type != TPAIR)
return cons (cons (x, y), a);
return cons (cons (car (x), car (y)), pairlis (cdr (x), cdr (y), a));
}
struct scm *
set_car_x (struct scm *x, struct scm *e)
{
if (x->type != TPAIR)
error (cell_symbol_not_a_pair, cons (x, cstring_to_symbol ("set-car!")));
x->car = e;
return cell_unspecified;
}
struct scm *
set_cdr_x (struct scm *x, struct scm *e)
{
if (x->type != TPAIR)
error (cell_symbol_not_a_pair, cons (x, cstring_to_symbol ("set-cdr!")));
x->cdr = e;
return cell_unspecified;
}
struct scm *
set_env_x (struct scm *x, struct scm *e, struct scm *a)
{
struct scm *p;
if (x->type == TVARIABLE)
p = x->variable;
else
p = assert_defined (x, module_variable (a, x));
if (p->type != TPAIR)
error (cell_symbol_not_a_pair, cons (p, x));
return set_cdr_x (p, e);
}
struct scm *
call_lambda (struct scm *e, struct scm *x, struct scm *aa, struct scm *a) /*:((internal)) */
{
struct scm *cl = cons (cons (cell_closure, x), x);
R1 = e;
R0 = cl;
return cell_unspecified;
}
struct scm *
make_closure_ (struct scm *args, struct scm *body, struct scm *a) /*:((internal)) */
{
return make_cell (TCLOSURE, cell_f, cons (cons (cell_circular, a), cons (args, body)));
}
struct scm *
make_variable_ (struct scm *var) /*:((internal)) */
{
return make_cell (TVARIABLE, var, 0);
}
struct scm *
macro_get_handle (struct scm *name) /*:((internal)) */
{
if (name->type == TSYMBOL)
return hashq_get_handle (g_macros, name, cell_nil);
return cell_f;
}
struct scm *
get_macro (struct scm *name) /*:((internal)) */
{
struct scm *m = macro_get_handle (name);
if (m != cell_f)
{
struct scm *d = m->cdr;
return d->macro;
}
return cell_f;
}
struct scm *
macro_set_x (struct scm *name, struct scm *value) /*:((internal)) */
{
return hashq_set_x (g_macros, name, value);
}
struct scm *
push_cc (struct scm *p1, struct scm *p2, struct scm *a, struct scm *c) /*:((internal)) */
{
struct scm *x = R3;
R3 = c;
R2 = p2;
gc_push_frame ();
R1 = p1;
R0 = a;
R3 = x;
return cell_unspecified;
}
struct scm *
add_formals (struct scm *formals, struct scm *x)
{
while (x->type == TPAIR)
{
formals = cons (x->car, formals);
x = x->cdr;
}
if (x->type == TSYMBOL)
formals = cons (x, formals);
return formals;
}
int
formal_p (struct scm *x, struct scm *formals) /*:((internal)) */
{
if (formals->type == TSYMBOL)
{
if (x == formals)
return 1;
else
return 0;
}
while (formals->type == TPAIR)
{
if (formals->car == x)
break;
formals = formals->cdr;
}
if (formals->type == TSYMBOL)
return formals == x;
return formals->type == TPAIR;
}
struct scm *
expand_variable_ (struct scm *x, struct scm *formals, int top_p) /*:((internal)) */
{
struct scm *a;
struct scm *f;
struct scm *v;
while (x->type == TPAIR)
{
a = x->car;
if (a->type == TPAIR)
{
if (a->car == cell_symbol_lambda)
{
f = a->cdr->car;
formals = add_formals (formals, f);
}
else if (a->car == cell_symbol_define || a->car == cell_symbol_define_macro)
{
f = a->cdr->car;
formals = add_formals (formals, f);
}
if (a->car != cell_symbol_quote)
expand_variable_ (a, formals, 0);
}
else
{
if (a == cell_symbol_lambda)
{
f = x->cdr->car;
formals = add_formals (formals, f);
x = x->cdr;
}
else if (a == cell_symbol_define || a == cell_symbol_define_macro)
{
f = x->cdr->car;
if (top_p != 0 && f->type == TPAIR)
f = f->cdr;
formals = add_formals (formals, f);
x = x->cdr;
}
else if (a == cell_symbol_quote)
return cell_unspecified;
else if (a->type == TSYMBOL
&& a != cell_symbol_boot_module
&& a != cell_symbol_current_module
&& a != cell_symbol_primitive_load
&& formal_p (x->car, formals) == 0)
{
v = module_variable (R0, a);
if (v != cell_f)
x->car = make_variable_ (v);
}
}
x = x->cdr;
top_p = 0;
}
return cell_unspecified;
}
struct scm *
expand_variable (struct scm *x, struct scm *formals) /*:((internal)) */
{
return expand_variable_ (x, formals, 1);
}
struct scm *
apply_builtin (struct scm *fn, struct scm *x) /*:((internal)) */
{
struct scm *a = builtin_arity (fn);
struct scm *d;
int arity = a->value;
if ((arity > 0 || arity == -1) && x != cell_nil)
{
a = x->car;
if (a->type == TVALUES)
x = cons (a->cdr->car, x->cdr);
}
if ((arity > 1 || arity == -1) && x != cell_nil)
{
a = x->car;
d = x->cdr;
if (d->type == TPAIR)
if (d->car->type == TVALUES)
x = cons (a, cons (d->car->cdr->car, d));
}
if (arity == 0)
return apply_builtin0 (fn);
if (arity == 1)
return apply_builtin1 (fn, x->car);
else if (arity == 2)
return apply_builtin2 (fn, x->car, x->cdr->car);
else if (arity == 3)
return apply_builtin3 (fn, x->car, x->cdr->car, x->cdr->cdr->car);
else if (arity == -1)
return apply_builtin1 (fn, x);
return cell_unspecified;
}
struct scm *
eval_apply ()
{
struct scm *aa;
struct scm *args;
struct scm *body;
struct scm *cl;
struct scm *entry;
struct scm *expanders;
struct scm *formals;
struct scm *input;
struct scm *name;
struct scm *macro;
struct scm *p;
struct scm *program;
struct scm *sc_expand;
struct scm *v;
struct scm *x;
int global_p;
int macro_p;
struct scm *a;
struct scm *c;
struct scm *d;
int t;
long i;
eval_apply:
if (R3 == cell_vm_evlis2)
goto evlis2;
else if (R3 == cell_vm_evlis3)
goto evlis3;
else if (R3 == cell_vm_eval_check_func)
goto eval_check_func;
else if (R3 == cell_vm_eval2)
goto eval2;
else if (R3 == cell_vm_apply2)
goto apply2;
else if (R3 == cell_vm_if_expr)
goto if_expr;
else if (R3 == cell_vm_begin_eval)
goto begin_eval;
else if (R3 == cell_vm_eval_set_x)
goto eval_set_x;
else if (R3 == cell_vm_macro_expand_car)
goto macro_expand_car;
else if (R3 == cell_vm_return)
goto vm_return;
else if (R3 == cell_vm_macro_expand_cdr)
goto macro_expand_cdr;
else if (R3 == cell_vm_eval_define)
goto eval_define;
else if (R3 == cell_vm_macro_expand)
goto macro_expand;
else if (R3 == cell_vm_macro_expand_lambda)
goto macro_expand_lambda;
else if (R3 == cell_vm_eval_pmatch_car)
goto eval_pmatch_car;
else if (R3 == cell_vm_begin_expand_macro)
goto begin_expand_macro;
else if (R3 == cell_vm_macro_expand_define)
goto macro_expand_define;
else if (R3 == cell_vm_begin_expand_eval)
goto begin_expand_eval;
else if (R3 == cell_vm_call_with_current_continuation2)
goto call_with_current_continuation2;
else if (R3 == cell_vm_macro_expand_set_x)
goto macro_expand_set_x;
else if (R3 == cell_vm_eval_pmatch_cdr)
goto eval_pmatch_cdr;
else if (R3 == cell_vm_macro_expand_define_macro)
goto macro_expand_define_macro;
else if (R3 == cell_vm_begin_primitive_load)
goto begin_primitive_load;
else if (R3 == cell_vm_evlis)
goto evlis;
else if (R3 == cell_vm_apply)
goto apply;
else if (R3 == cell_vm_eval)
goto eval;
else if (R3 == cell_vm_eval_macro_expand_eval)
goto eval_macro_expand_eval;
else if (R3 == cell_vm_eval_macro_expand_expand)
goto eval_macro_expand_expand;
else if (R3 == cell_vm_begin)
goto begin;
else if (R3 == cell_vm_begin_expand)
goto begin_expand;
else if (R3 == cell_vm_begin_expand_primitive_load)
goto begin_expand_primitive_load;
else if (R3 == cell_vm_if)
goto vm_if;
else if (R3 == cell_vm_call_with_values2)
goto call_with_values2;
else if (R3 == cell_unspecified)
return R1;
else
assert_msg (0, "eval/apply unknown continuation");
evlis:
if (R1 == cell_nil)
goto vm_return;
if (R1->type != TPAIR)
goto eval;
push_cc (R1->car, R1, R0, cell_vm_evlis2);
goto eval;
evlis2:
push_cc (R2->cdr, R1, R0, cell_vm_evlis3);
goto evlis;
evlis3:
R1 = cons (R2, R1);
goto vm_return;
apply:
g_stack_array[g_stack + GC_FRAME_PROCEDURE] = R1->car;
a = R1->car;
t = a->type;
if (t == TSTRUCT && builtin_p (R1->car) == cell_t)
{
check_formals (R1->car, builtin_arity (R1->car), R1->cdr);
R1 = apply_builtin (R1->car, R1->cdr);
goto vm_return;
}
else if (t == TCLOSURE)
{
cl = R1->car->closure;
body = cl->cdr->cdr;
formals = cl->cdr->car;
args = R1->cdr;
aa = cl->car->cdr;
aa = aa->cdr;
check_formals (R1->car, formals, R1->cdr);
p = pairlis (formals, args, aa);
call_lambda (body, p, aa, R0);
goto begin;
}
else if (t == TCONTINUATION)
{
a = R1->car;
v = a->continuation;
if (v->length != 0)
{
for (i = 0; i < v->length; i = i + 1)
g_stack_array[STACK_SIZE - v->length + i] = vector_ref_ (v, i);
g_stack = STACK_SIZE - v->length;
}
x = R1;
gc_pop_frame ();
R1 = x->cdr->car;
goto eval_apply;
}
else if (t == TSPECIAL)
{
c = R1->car;
if (c == cell_vm_apply)
{
push_cc (cons (R1->cdr->car, R1->cdr->cdr->car), R1, R0, cell_vm_return);
goto apply;
}
else if (c == cell_vm_eval)
{
push_cc (R1->cdr->car, R1, R1->cdr->cdr->car, cell_vm_return);
goto eval;
}
else if (c == cell_vm_begin_expand)
{
push_cc (cons (R1->cdr->car, cell_nil), R1, R1->cdr->cdr->car, cell_vm_return);
goto begin_expand;
}
else
check_apply (cell_f, R1->car);
}
else if (t == TSYMBOL)
{
c = R1->car;
if (c == cell_symbol_call_with_current_continuation)
{
R1 = R1->cdr;
goto call_with_current_continuation;
}
if (c == cell_symbol_call_with_values)
{
R1 = R1->cdr;
goto call_with_values;
}
if (c == cell_symbol_current_module)
{
R1 = R0;
goto vm_return;
}
if (c == cell_symbol_boot_module)
{
R1 = M0;
goto vm_return;
}
}
else if (t == TPAIR)
{
if (R1->car->car == cell_symbol_lambda)
{
formals = R1->car->cdr->car;
args = R1->cdr;
body = R1->car->cdr->cdr;
p = pairlis (formals, R1->cdr, R0);
check_formals (R1, formals, args);
call_lambda (body, p, p, R0);
goto begin;
}
}
push_cc (R1->car, R1, R0, cell_vm_apply2);
goto eval;
apply2:
check_apply (R1, R2->car);
R1 = cons (R1, R2->cdr);
goto apply;
eval:
t = R1->type;
if (t == TPAIR)
{
c = R1->car;
if (c == cell_symbol_pmatch_car)
{
push_cc (R1->cdr->car, R1, R0, cell_vm_eval_pmatch_car);
goto eval;
eval_pmatch_car:
x = R1;
gc_pop_frame ();
R1 = x->car;
goto eval_apply;
}
else if (c == cell_symbol_pmatch_cdr)
{
push_cc (R1->cdr->car, R1, R0, cell_vm_eval_pmatch_cdr);
goto eval;
eval_pmatch_cdr:
x = R1;
gc_pop_frame ();
R1 = x->cdr;
goto eval_apply;
}
else if (c == cell_symbol_quote)
{
x = R1;
gc_pop_frame ();
R1 = x->cdr->car;
goto eval_apply;
}
else if (c == cell_symbol_begin)
goto begin;
else if (c == cell_symbol_lambda)
{
R1 = make_closure_ (R1->cdr->car, R1->cdr->cdr, R0);
goto vm_return;
}
else if (c == cell_symbol_if)
{
R1 = R1->cdr;
goto vm_if;
}
else if (c == cell_symbol_set_x)
{
push_cc (R1->cdr->cdr->car, R1, R0, cell_vm_eval_set_x);
goto eval;
eval_set_x:
R1 = set_env_x (R2->cdr->car, R1, R0);
goto vm_return;
}
else if (c == cell_vm_macro_expand)
{
push_cc (R1->cdr->car, R1, R0, cell_vm_eval_macro_expand_eval);
goto eval;
eval_macro_expand_eval:
push_cc (R1, R2, R0, cell_vm_eval_macro_expand_expand);
goto macro_expand;
eval_macro_expand_expand:
goto vm_return;
}
else
{
if (R1->type == TPAIR)
if (R1->car == cell_symbol_define || R1->car == cell_symbol_define_macro)
{
global_p = 0;
if (R0->car->car != cell_closure)
global_p = 1;
macro_p = 0;
if (R1->car == cell_symbol_define_macro)
macro_p = 1;
if (global_p != 0)
{
name = R1->cdr->car;
aa = R1->cdr->car;
if (aa->type == TPAIR)
name = name->car;
if (macro_p != 0)
{
entry = assq (name, g_macros);
if (entry == cell_f)
macro_set_x (name, cell_f);
}
else
{
entry = module_variable (R0, name);
if (entry == cell_f)
module_define_x (M0, name, cell_f);
}
}
R2 = R1;
aa = R1->cdr->car;
if (aa->type != TPAIR)
{
push_cc (R1->cdr->cdr->car, R2, cons (cons (R1->cdr->car, R1->cdr->car), R0), cell_vm_eval_define);
goto eval;
}
else
{
p = pairlis (R1->cdr->car, R1->cdr->car, R0);
formals = R1->cdr->car->cdr;
body = R1->cdr->cdr;
if (macro_p != 0 || global_p != 0)
expand_variable (body, formals);
R1 = cons (cell_symbol_lambda, cons (formals, body));
push_cc (R1, R2, p, cell_vm_eval_define);
goto eval;
}
eval_define:
name = R2->cdr->car;
aa = R2->cdr->car;
if (aa->type == TPAIR)
name = name->car;
if (macro_p != 0)
{
entry = macro_get_handle (name);
R1 = make_macro (name, R1);
set_cdr_x (entry, R1);
}
else if (global_p != 0)
{
entry = module_variable (R0, name);
set_cdr_x (entry, R1);
}
else
{
entry = cons (name, R1);
aa = cons (entry, cell_nil);
set_cdr_x (aa, cdr (R0));
set_cdr_x (R0, aa);
cl = module_variable (R0, cell_closure);
set_cdr_x (cl, aa);
}
R1 = cell_unspecified;
goto vm_return;
}
push_cc (R1->car, R1, R0, cell_vm_eval_check_func);
gc_check ();
goto eval;
eval_check_func:
push_cc (R2->cdr, R2, R0, cell_vm_eval2);
goto evlis;
eval2:
R1 = cons (R2->car, R1);
goto apply;
}
}
else if (t == TSYMBOL)
{
if (R1 == cell_symbol_boot_module)
goto vm_return;
if (R1 == cell_symbol_current_module)
goto vm_return;
if (R1 == cell_symbol_begin)
goto vm_return;
if (R1 == cell_symbol_call_with_current_continuation)
goto vm_return;
R1 = assert_defined (R1, module_ref (R0, R1));
goto vm_return;
}
else if (t == TVARIABLE)
{
x = R1->variable;
R1 = x->cdr;
goto vm_return;
}
else if (t == TBROKEN_HEART)
error (cell_symbol_system_error, R1);
else
goto vm_return;
macro_expand:
if (R1->type != TPAIR || R1->car == cell_symbol_quote)
goto vm_return;
if (R1->car == cell_symbol_lambda)
{
push_cc (R1->cdr->cdr, R1, R0, cell_vm_macro_expand_lambda);
goto macro_expand;
macro_expand_lambda:
R2->cdr->cdr = R1;
R1 = R2;
goto vm_return;
}
if (R1->type == TPAIR)
{
macro = get_macro (R1->car);
if (macro != cell_f)
{
R1 = cons (macro, R1->cdr);
push_cc (R1, cell_nil, R0, cell_vm_macro_expand);
goto apply;
}
}
if (R1->car == cell_symbol_define || R1->car == cell_symbol_define_macro)
{
push_cc (R1->cdr->cdr, R1, R0, cell_vm_macro_expand_define);
goto macro_expand;
macro_expand_define:
R2->cdr->cdr = R1;
R1 = R2;
if (R1->car == cell_symbol_define_macro)
{
push_cc (R1, R1, R0, cell_vm_macro_expand_define_macro);
goto eval;
macro_expand_define_macro:
R1 = R2;
}
goto vm_return;
}
if (R1->car == cell_symbol_set_x)
{
push_cc (R1->cdr->cdr, R1, R0, cell_vm_macro_expand_set_x);
goto macro_expand;
macro_expand_set_x:
R2->cdr->cdr = R1;
R1 = R2;
goto vm_return;
}
if (R1->type == TPAIR)
{
a = R1->car;
if (a->type == TSYMBOL && a != cell_symbol_begin)
{
macro = macro_get_handle (cell_symbol_portable_macro_expand);
if (macro != cell_f)
{
expanders = module_ref (R0, cell_symbol_sc_expander_alist);
if (expanders != cell_undefined)
{
macro = assq (R1->car, expanders);
if (macro != cell_f)
{
sc_expand = module_ref (R0, cell_symbol_macro_expand);
R2 = R1;
if (sc_expand != cell_undefined && sc_expand != cell_f)
{
R1 = cons (sc_expand, cons (R1, cell_nil));
goto apply;
}
}
}
}
}
}
push_cc (R1->car, R1, R0, cell_vm_macro_expand_car);
goto macro_expand;
macro_expand_car:
R2->car = R1;
R1 = R2;
if (R1->cdr == cell_nil)
goto vm_return;
push_cc (R1->cdr, R1, R0, cell_vm_macro_expand_cdr);
goto macro_expand;
macro_expand_cdr:
R2->cdr = R1;
R1 = R2;
goto vm_return;
begin:
x = cell_unspecified;
while (R1 != cell_nil)
{
gc_check ();
if (R1->type == TPAIR)
{
if (R1->car->car == cell_symbol_primitive_load)
{
program = cons (R1->car, cell_nil);
push_cc (program, R1, R0, cell_vm_begin_primitive_load);
goto begin_expand;
begin_primitive_load:
R2->car = R1;
R1 = R2;
}
}
if (R1->type == TPAIR)
{
a = R1->car;
if (a->type == TPAIR)
{
if (a->car == cell_symbol_begin)
R1 = append2 (a->cdr, R1->cdr);
}
}
if (R1->cdr == cell_nil)
{
R1 = R1->car;
goto eval;
}
push_cc (R1->car, R1, R0, cell_vm_begin_eval);
goto eval;
begin_eval:
x = R1;
R1 = R2->cdr;
}
R1 = x;
goto vm_return;
begin_expand:
x = cell_unspecified;
while (R1 != cell_nil)
{
begin_expand_while:
gc_check ();
if (R1->type == TPAIR)
{
a = R1->car;
if (a->type == TPAIR)
if (R1->car->car == cell_symbol_begin)
R1 = append2 (R1->car->cdr, R1->cdr);
if (R1->car->car == cell_symbol_primitive_load)
{
push_cc (R1->car->cdr->car, R1, R0, cell_vm_begin_expand_primitive_load);
goto eval;
begin_expand_primitive_load:
if ((R1->type == TNUMBER) && R1->value == 0)
0;
else if (R1->type == TSTRING)
input = set_current_input_port (open_input_file (R1));
else if (R1->type == TPORT)
input = set_current_input_port (R1);
else
{
eputs ("begin_expand failed, R1=");
display_error_ (R1);
assert_msg (0, "begin-expand-boom 0");
}
push_cc (input, R2, R0, cell_vm_return);
x = read_input_file_env (R0);
if (g_debug > 5)
module_printer (M0);
gc_pop_frame ();
input = R1;
R1 = x;
set_current_input_port (input);
R1 = cons (cell_symbol_begin, R1);
R2->car = R1;
R1 = R2;
goto begin_expand_while;
continue; /* FIXME: M2-PLanet */
}
}
push_cc (R1->car, R1, R0, cell_vm_begin_expand_macro);
goto macro_expand;
begin_expand_macro:
if (R1 != R2->car)
{
R2->car = R1;
R1 = R2;
goto begin_expand_while;
continue; /* FIXME: M2-PLanet */
}
R1 = R2;
expand_variable (R1->car, cell_nil);
push_cc (R1->car, R1, R0, cell_vm_begin_expand_eval);
goto eval;
begin_expand_eval:
x = R1;
R1 = R2->cdr;
}
R1 = x;
goto vm_return;
vm_if:
push_cc (R1->car, R1, R0, cell_vm_if_expr);
goto eval;
if_expr:
x = R1;
R1 = R2;
if (x != cell_f)
{
R1 = R1->cdr->car;
goto eval;
}
if (R1->cdr->cdr != cell_nil)
{
R1 = R1->cdr->cdr->car;
goto eval;
}
R1 = cell_unspecified;
goto vm_return;
call_with_current_continuation:
gc_push_frame ();
x = make_continuation (g_continuations);
g_continuations = g_continuations + 1;
v = make_vector_ (STACK_SIZE - g_stack, cell_unspecified);
for (i = g_stack; i < STACK_SIZE; i = i + 1)
vector_set_x_ (v, i - g_stack, g_stack_array[i]);
x->continuation = v;
gc_pop_frame ();
push_cc (cons (R1->car, cons (x, cell_nil)), x, R0, cell_vm_call_with_current_continuation2);
goto apply;
call_with_current_continuation2:
v = make_vector_ (STACK_SIZE - g_stack, cell_unspecified);
for (i = g_stack; i < STACK_SIZE; i = i + 1)
vector_set_x_ (v, i - g_stack, g_stack_array[i]);
R2->continuation = v;
goto vm_return;
call_with_values:
push_cc (cons (R1->car, cell_nil), R1, R0, cell_vm_call_with_values2);
goto apply;
call_with_values2:
if (R1->type == TVALUES)
R1 = R1->cdr;
R1 = cons (R2->cdr->car, R1);
goto apply;
vm_return:
x = R1;
gc_pop_frame ();
R1 = x;
goto eval_apply;
}
struct scm *
apply (struct scm *f, struct scm *x, struct scm *a) /*:((internal)) */
{
push_cc (cons (f, x), cell_unspecified, R0, cell_unspecified);
R3 = cell_vm_apply;
return eval_apply ();
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2020,2021 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
* Copyright © 2022 Gabriel Wicki <gabriel@erlikon.ch>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mes/lib.h"
#include "mes/mes.h"
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <sys/time.h>
#include <time.h>
int g_dump_filedes;
#define M2_CELL_SIZE 1U
// CONSTANT M2_CELL_SIZE 12
char *
cell_bytes (struct scm *x)
{
char *p = cast_voidp_to_charp (x);
return p + (2 * sizeof (long));
}
#define U10 10U
// CONSTANT U10 10
#define U100 100U
// CONSTANT U100 100
void
gc_init ()
{
#if SYSTEM_LIBC
ARENA_SIZE = 100000000; /* 2.3GiB */
#elif ! __M2_PLANET__
ARENA_SIZE = 300000; /* 32b: 3MiB, 64b: 6 MiB */
#else
ARENA_SIZE = 20000000;
#endif
STACK_SIZE = 20000;
JAM_SIZE = 10;
MAX_ARENA_SIZE = 10000000;
GC_SAFETY = 2000;
MAX_STRING = 524288;
char *p;
p = getenv ("MES_MAX_ARENA");
if (p != 0)
MAX_ARENA_SIZE = atoi (p);
p = getenv ("MES_ARENA");
if (p != 0)
ARENA_SIZE = atoi (p);
JAM_SIZE = ARENA_SIZE / U10;
p = getenv ("MES_JAM");
if (p != 0)
JAM_SIZE = atoi (p);
GC_SAFETY = ARENA_SIZE / U100;
p = getenv ("MES_SAFETY");
if (p != 0)
GC_SAFETY = atoi (p);
p = getenv ("MES_STACK");
if (p != 0)
STACK_SIZE = atoi (p);
p = getenv ("MES_MAX_STRING");
if (p != 0)
MAX_STRING = atoi (p);
long arena_bytes = (ARENA_SIZE + JAM_SIZE) * sizeof (struct scm);
long alloc_bytes = arena_bytes + (STACK_SIZE * sizeof (struct scm));
g_arena = malloc (alloc_bytes);
g_cells = cast_charp_to_scmp (g_arena);
g_stack_array = cast_charp_to_scmpp (g_arena + arena_bytes);
/* The vector that holds the arenea. */
cell_arena = g_cells;
cell_zero = cell_arena + M2_CELL_SIZE;
g_cells = g_cells + M2_CELL_SIZE; /* Hmm? */
cell_arena->type = TVECTOR;
cell_arena->length = 1000;
cell_arena->vector = cell_zero;
cell_zero->type = TCHAR;
cell_zero->value = 'c';
g_free = g_cells + M2_CELL_SIZE;
/* FIXME: remove MES_MAX_STRING, grow dynamically. */
g_buf = malloc (MAX_STRING);
}
long
gc_free ()
{
return (g_free - g_cells) / M2_CELL_SIZE;
}
void
gc_stats_ (char const* where)
{
size_t i = g_free - g_cells;
i = i / M2_CELL_SIZE;
if (where)
{
eputs (where);
eputs (": ");
}
eputs ("[");
eputs (ltoa (i));
eputs ("]\n");
}
struct scm *
gc_stats ()
{
gc_stats_ (0);
size_t arena_used = g_free - g_cells;
arena_used = arena_used / M2_CELL_SIZE;
size_t arena_free = ARENA_SIZE - arena_used;
struct scm *r = cell_nil;
r = acons (cstring_to_symbol ("gc-count"), make_number (gc_count), r);
r = acons (cstring_to_symbol ("gc-time"), make_number (gc_time), r);
r = acons (cstring_to_symbol ("arena-free"), make_number (arena_free), r);
r = acons (cstring_to_symbol ("arena-size"), make_number (ARENA_SIZE), r);
return r;
}
struct scm *
alloc (long n)
{
struct scm *x = g_free;
g_free = g_free + (n * M2_CELL_SIZE);
long i = g_free - g_cells;
i = i / M2_CELL_SIZE;
if (i > ARENA_SIZE)
assert_msg (0, "alloc: out of memory");
return x;
}
struct scm *
make_cell (long type, struct scm *car, struct scm *cdr)
{
struct scm *x = g_free;
g_free = g_free + M2_CELL_SIZE;
long i = g_free - g_cells;
i = i / M2_CELL_SIZE;
if (i > ARENA_SIZE)
assert_msg (0, "make_cell: out of memory");
x->type = type;
x->car = car;
x->cdr = cdr;
return x;
}
struct scm *
make_pointer_cell (long type, long car, void *cdr)
{
struct scm *x = g_free;
g_free = g_free + M2_CELL_SIZE;
long i = g_free - g_cells;
i = i / M2_CELL_SIZE;
if (i > ARENA_SIZE)
assert_msg (0, "make_pointer_cell: out of memory");
x->type = type;
x->length = car;
x->cdr = cdr;
return x;
}
struct scm *
make_value_cell (long type, long car, long cdr)
{
struct scm *x = g_free;
g_free = g_free + M2_CELL_SIZE;
long i = g_free - g_cells;
i = i / M2_CELL_SIZE;
if (i > ARENA_SIZE)
assert_msg (0, "make_value_cell: out of memory");
x->type = type;
x->length = car;
x->value = cdr;
return x;
}
void
copy_cell (struct scm *to, struct scm *from)
{
to->type = from->type;
to->car = from->car;
to->cdr = from->cdr;
}
void
copy_stack (long index, struct scm *from)
{
g_stack_array[index] = from;
}
struct scm *
cell_ref (struct scm *cell, long index)
{
return cell + (index * M2_CELL_SIZE);
}
struct scm *
cons (struct scm *x, struct scm *y)
{
return make_cell (TPAIR, x, y);
}
size_t
bytes_cells (size_t length)
{
return (sizeof (long) + sizeof (long) + length - 1 + sizeof (struct scm *)) / sizeof (struct scm *);
}
struct scm *
make_bytes (char const *s, size_t length)
{
size_t size = bytes_cells (length);
struct scm *x = alloc (size);
x->type = TBYTES;
x->length = length;
char *p = cell_bytes (x);
if (length == 0)
p[0] = 0;
else
memcpy (p, s, length);
return x;
}
struct scm *
make_char (int n)
{
return make_value_cell (TCHAR, 0, n);
}
struct scm *
make_continuation (long n)
{
return make_value_cell (TCONTINUATION, n, g_stack);
}
struct scm *
make_macro (struct scm *name, struct scm *x) /*:((internal)) */
{
return make_cell (TMACRO, x, name->string);
}
struct scm *
make_number (long n)
{
return make_value_cell (TNUMBER, 0, n);
}
struct scm *
make_ref (struct scm *x) /*:((internal)) */
{
return make_cell (TREF, x, 0);
}
struct scm *
make_string (char const *s, size_t length)
{
if (length > MAX_STRING)
assert_max_string (length, "make_string", s);
struct scm *x = make_pointer_cell (TSTRING, length, 0);
struct scm *v = make_bytes (s, length + 1);
x->cdr = v;
return x;
}
struct scm *
make_string0 (char const *s)
{
return make_string (s, strlen (s));
}
struct scm *
make_string_port (struct scm *x) /*:((internal)) */
{
return make_pointer_cell (TPORT, -length__ (g_ports) - 2, x);
}
void
gc_init_news ()
{
g_news = g_free;
struct scm *ncell_arena = g_news;
struct scm *ncell_zero = ncell_arena + M2_CELL_SIZE;
g_news = g_news + M2_CELL_SIZE;
ncell_arena->type = TVECTOR;
ncell_arena->length = cell_arena->length;
ncell_arena->vector = g_news;
ncell_zero->type = TCHAR;
ncell_zero->value = 'n';
}
void
gc_up_arena ()
{
long old_arena_bytes = (ARENA_SIZE + JAM_SIZE) * sizeof (struct scm);
if (ARENA_SIZE / 2 < MAX_ARENA_SIZE / 4)
{
ARENA_SIZE = ARENA_SIZE * 2;
JAM_SIZE = JAM_SIZE * 2;
GC_SAFETY = GC_SAFETY * 2;
}
else
ARENA_SIZE = MAX_ARENA_SIZE - JAM_SIZE;
long arena_bytes = (ARENA_SIZE + JAM_SIZE) * sizeof (struct scm);
long stack_offset = (arena_bytes * 2);
long realloc_bytes = (arena_bytes * 2) + (STACK_SIZE * sizeof (struct scm));
void *p = realloc (g_cells - M2_CELL_SIZE, realloc_bytes);
if (p == 0)
{
eputs ("realloc failed, g_free=");
eputs (ltoa (cast_voidp_to_long (g_free)));
eputs (":");
long i = g_free - g_cells;
i = i / M2_CELL_SIZE;
eputs (ltoa (ARENA_SIZE - i));
eputs ("\n");
assert_msg (0, "0");
exit (1);
}
g_cells = p;
memcpy (p + stack_offset, p + old_arena_bytes, STACK_SIZE * sizeof (struct scm *));
g_cells = g_cells + M2_CELL_SIZE;
}
/* A pointer relocating memcpy for pointer cells to avoid using only
half of the allocated cells.
For number based cells a simply memcpy could be used, as number
references are relative.
A simple stop and copy (SICP 5.3) garbage collector allocates twice
the cell arena size only for the garbage collector. The garbage
collector switches back and forth between cells and news, thus
utilizing only half the allocated memory. */
void
gc_cellcpy (struct scm *dest, struct scm *src, size_t n)
{
void *p = src;
void *q = dest;
long dist = p - q;
long t;
long a;
long d;
int i;
int c;
while (n != 0)
{
t = src->type;
a = src->car_value;
d = src->cdr_value;
dest->type = t;
if (t == TBROKEN_HEART)
assert_msg (0, "gc_cellcpy: broken heart");
if (t == TMACRO
|| t == TPAIR
|| t == TREF
|| t == TVARIABLE)
dest->car_value = a - dist;
else
dest->car_value = a;
if (t == TBYTES
|| t == TCLOSURE
|| t == TCONTINUATION
|| t == TKEYWORD
|| t == TMACRO
|| t == TPAIR
|| t == TPORT
|| t == TSPECIAL
|| t == TSTRING
|| t == TSTRUCT
|| t == TSYMBOL
|| t == TVALUES
|| t == TVECTOR)
dest->cdr_value = d - dist;
else
dest->cdr_value = d;
if (t == TBYTES)
{
if (g_debug > 5)
{
eputs ("copying bytes[");
eputs (ntoab (cast_voidp_to_long (cell_bytes (src)), 16, 0));
eputs (", ");
eputs (ntoab (a, 10, 0));
eputs ("]: ");
eputs (cell_bytes (src));
eputs ("\n to [");
eputs (ntoab (cast_voidp_to_long (cell_bytes (dest)), 16, 0));
}
memcpy (cell_bytes (dest), cell_bytes (src), a);
if (g_debug > 5)
{
eputs ("]: ");
eputs (cell_bytes (dest));
eputs ("\n");
}
i = bytes_cells (a);
n = n - i;
c = i * M2_CELL_SIZE;
dest = dest + c;
src = src + c;
}
else
{
n = n - 1;
dest = dest + M2_CELL_SIZE;
src = src + M2_CELL_SIZE;
}
}
}
/* We do not actually flip cells and news, instead we move news back to
cells. */
void
gc_flip ()
{
if (g_free - g_news > JAM_SIZE)
JAM_SIZE = ((g_free - g_news) * 3) / 2;
cell_arena = g_cells - M2_CELL_SIZE; /* For debugging. */
gc_cellcpy (g_cells, g_news, (g_free - g_news) / M2_CELL_SIZE);
long dist = g_news - g_cells;
g_free = g_free - dist;
g_symbols = g_symbols - dist;
g_macros = g_macros - dist;
g_ports = g_ports - dist;
M0 = M0 - dist;
long i;
for (i = g_stack; i < STACK_SIZE; i = i + 1)
g_stack_array[i] = g_stack_array[i] - dist;
if (g_debug > 2)
gc_stats_ (";;; => jam");
}
struct scm *
gc_copy (struct scm *old) /*:((internal)) */
{
if (old->type == TBROKEN_HEART)
return old->car;
struct scm *new = g_free;
g_free = g_free + M2_CELL_SIZE;
copy_cell (new, old);
if (new->type == TSTRUCT || new->type == TVECTOR)
{
new->vector = g_free;
long i;
for (i = 0; i < old->length; i = i + 1)
{
copy_cell (g_free, cell_ref (old->vector, i));
g_free = g_free + M2_CELL_SIZE;
}
}
else if (new->type == TBYTES)
{
char const *src = cell_bytes (old);
char *dest = cell_bytes (new);
size_t length = new->length;
memcpy (dest, src, length);
g_free = g_free + ((bytes_cells (length) - 1) * M2_CELL_SIZE);
if (g_debug > 4)
{
eputs ("gc copy bytes: ");
eputs (src);
eputs ("\n");
eputs (" length: ");
eputs (ltoa (old->length));
eputs ("\n");
eputs (" nlength: ");
eputs (ltoa (new->length));
eputs ("\n");
eputs (" ==> ");
eputs (dest);
eputs ("\n");
}
}
old->type = TBROKEN_HEART;
old->car = new;
return new;
}
struct scm *
gc_relocate_car (struct scm *new, struct scm *car) /*:((internal)) */
{
new->car = car;
return cell_unspecified;
}
struct scm *
gc_relocate_cdr (struct scm *new, struct scm *cdr) /*:((internal)) */
{
new->cdr = cdr;
return cell_unspecified;
}
void
gc_loop (struct scm *scan)
{
struct scm *car;
struct scm *cdr;
long t;
while (scan < g_free)
{
t = scan->type;
if (t == TBROKEN_HEART)
assert_msg (0, "gc_loop: broken heart");
/* *INDENT-OFF* */
if (t == TMACRO
|| t == TPAIR
|| t == TREF
|| t == TVARIABLE)
/* *INDENT-ON* */
{
car = gc_copy (scan->car);
gc_relocate_car (scan, car);
}
/* *INDENT-OFF* */
if (t == TCLOSURE
|| t == TCONTINUATION
|| t == TKEYWORD
|| t == TMACRO
|| t == TPAIR
|| t == TPORT
|| t == TSPECIAL
|| t == TSTRING
/*|| t == TSTRUCT handled by gc_copy */
|| t == TSYMBOL
|| t == TVALUES
/*|| t == TVECTOR handled by gc_copy */
)
/* *INDENT-ON* */
{
cdr = gc_copy (scan->cdr);
gc_relocate_cdr (scan, cdr);
}
if (t == TBYTES)
scan = scan + (bytes_cells (scan->length) * M2_CELL_SIZE);
else
scan = scan + M2_CELL_SIZE;
}
gc_flip ();
}
struct scm *
gc_check ()
{
long used = ((g_free - g_cells) / M2_CELL_SIZE) + GC_SAFETY;
if (used >= ARENA_SIZE)
return gc ();
return cell_unspecified;
}
void
gc_ ()
{
gc_init_news ();
if (g_debug == 2)
eputs (".");
if (g_debug > 2)
{
gc_stats_ (";;; gc");
eputs (";;; free: [");
eputs (ltoa (ARENA_SIZE - gc_free ()));
eputs ("]...");
}
g_free = g_news + M2_CELL_SIZE;
if (ARENA_SIZE < MAX_ARENA_SIZE
&& cast_voidp_to_charp (g_cells) == g_arena + M2_CELL_SIZE)
{
if (g_debug == 2)
eputs ("+");
if (g_debug > 2)
{
eputs (" up[");
eputs (ltoa (cast_voidp_to_long (g_cells)));
eputs (",");
eputs (ltoa (cast_voidp_to_long (g_news)));
eputs (":");
eputs (ltoa (ARENA_SIZE));
eputs (",");
eputs (ltoa (MAX_ARENA_SIZE));
eputs ("]...");
}
gc_up_arena ();
}
struct scm *new_cell_nil = g_free;
struct scm *s;
for (s = cell_nil; s < g_symbol_max; s = s + M2_CELL_SIZE)
gc_copy (s);
g_symbols = gc_copy (g_symbols);
g_macros = gc_copy (g_macros);
g_ports = gc_copy (g_ports);
M0 = gc_copy (M0);
long i;
for (i = g_stack; i < STACK_SIZE; i = i + 1)
copy_stack (i, gc_copy (g_stack_array[i]));
gc_loop (new_cell_nil);
}
struct scm *
gc ()
{
if (getenv ("MES_DUMP") != 0)
gc_dump_arena (g_cells, gc_free ());
if (g_debug > 5)
{
eputs ("symbols: ");
write_error_ (g_symbols);
eputs ("\n");
eputs ("R0: ");
write_error_ (R0);
eputs ("\n");
}
clock_gettime (CLOCK_PROCESS_CPUTIME_ID, gc_start_time);
gc_push_frame ();
gc_ ();
gc_pop_frame ();
clock_gettime (CLOCK_PROCESS_CPUTIME_ID, gc_end_time);
long time = seconds_and_nanoseconds_to_long
(gc_end_time->tv_sec - gc_start_time->tv_sec,
gc_end_time->tv_nsec - gc_start_time->tv_nsec);
gc_time = gc_time + time;
gc_count = gc_count + 1;
if (g_debug > 5)
{
eputs ("symbols: ");
write_error_ (g_symbols);
eputs ("\n");
eputs ("R0: ");
write_error_ (R0);
eputs ("\n");
}
if (getenv ("MES_DUMP") != 0)
gc_dump_arena (g_cells, gc_free ());
return cell_unspecified;
}
void
gc_push_frame ()
{
if (g_stack < GC_FRAME_SIZE)
assert_msg (0, "STACK FULL");
g_stack_array[g_stack - 1] = cell_f;
g_stack_array[g_stack - 2] = R0;
g_stack_array[g_stack - 3] = R1;
g_stack_array[g_stack - 4] = R2;
g_stack_array[g_stack - 5] = R3;
g_stack = g_stack - GC_FRAME_SIZE;
}
void
gc_peek_frame ()
{
R3 = g_stack_array[g_stack];
R2 = g_stack_array[g_stack + 1];
R1 = g_stack_array[g_stack + 2];
R0 = g_stack_array[g_stack + 3];
g_stack_array[g_stack + GC_FRAME_PROCEDURE];
}
void
gc_pop_frame ()
{
gc_peek_frame ();
g_stack = g_stack + GC_FRAME_SIZE;
}
void
dumpc (char c)
{
fdputc (c, g_dump_filedes);
}
void
dumps (char const *s)
{
fdputs (s, g_dump_filedes);
}
void
gc_dump_register (char const* n, struct scm *r)
{
dumps (n); dumps (": ");
long i = cast_scmp_to_long (r);
long a = cast_charp_to_long (g_arena);
i = i - a;
i = i / M2_CELL_SIZE;
dumps (ltoa (i));
dumps ("\n");
}
void
gc_dump_state ()
{
gc_dump_register ("R0", R0);
gc_dump_register ("R1", R1);
gc_dump_register ("R2", R2);
gc_dump_register ("R3", R3);
gc_dump_register ("M0", M0);
gc_dump_register ("g_symbols", g_symbols);
gc_dump_register ("g_symbol_max", g_symbol_max);
gc_dump_register ("g_macros", g_macros);
gc_dump_register ("g_ports", g_ports);
gc_dump_register ("cell_zero", cell_zero);
gc_dump_register ("cell_nil", cell_nil);
}
void
gc_dump_stack ()
{
long i = g_stack;
while (i < STACK_SIZE)
{
gc_dump_register (itoa (i), g_stack_array[i]);
i = i + 1;
}
}
void
gc_dump_arena (struct scm *cells, long size)
{
struct scm *end = g_cells + (size * M2_CELL_SIZE);
struct scm *dist = cells;
int i;
long t;
long a;
long d;
int c;
char* p;
if (g_dump_filedes == 0)
g_dump_filedes = mes_open ("dump.mo", O_CREAT|O_WRONLY, 0644);
dumps ("stack="); dumps (ltoa (g_stack)); dumpc ('\n');
dumps ("size="); dumps (ltoa (size)); dumpc ('\n');
gc_dump_state ();
gc_dump_stack ();
while (end->type == 0 && end->car == 0 && end->cdr == 0)
{
end = end - M2_CELL_SIZE;
size = size - 1;
}
while (size > 0)
{
for (i=0; i < 16; i = i + 1)
{
t = cells->type;
a = cells->car_value;
d = cells->cdr_value;
if (size == 0)
dumps ("0 0 0");
else
{
dumps (ltoa (t));
dumpc (' ');
if (t == TMACRO
|| t == TPAIR
|| t == TREF
|| t == TVARIABLE)
{
dumps (ltoa ((cells->car - dist) / M2_CELL_SIZE));
/* dumps ("["); dumps (ltoa (a)); dumps ("]"); */
}
else
dumps (ltoa (a));
dumpc (' ');
if (t != TBYTES)
{
if (t == TCLOSURE
|| t == TCONTINUATION
|| t == TKEYWORD
|| t == TMACRO
|| t == TPAIR
|| t == TPORT
|| t == TSPECIAL
|| t == TSTRING
|| t == TSTRUCT
|| t == TSYMBOL
|| t == TVALUES
|| t == TVECTOR)
{
dumps (ltoa ((cells->cdr - dist) / M2_CELL_SIZE));
/* dumps ("["); dumps (ltoa (d)); dumps ("]"); */
}
else if (t == TNUMBER && d > 1000)
dumps (ltoa (1001));
else
dumps (ltoa (d));
}
if (t == TBYTES)
{
c = bytes_cells (a);
p = cell_bytes (cells);
size = size - c;
dumpc ('"');
while (a > 0)
{
if (p[0] != 0)
dumpc (p[0]);
p = p + 1;
a = a - 1;
}
dumpc ('"');
cells = cells + c * M2_CELL_SIZE;
size = size - c;
}
else
{
cells = cells + M2_CELL_SIZE;
size = size - 1;
}
}
if (i != 15)
dumps (" ");
else
dumpc ('\n');
}
dumpc ('\n');
}
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mes/lib.h"
#include "mes/mes.h"
#include <string.h>
int
hash_cstring (char const *s, long size)
{
int hash = s[0] * 37;
if (s[0] != 0)
if (s[1] != 0)
hash = hash + (s[1] * 43);
assert_msg (size != 0, "size");
hash = hash % size;
return hash;
}
int
hashq_ (struct scm *x, long size)
{
if (x->type == TSPECIAL || x->type == TSYMBOL)
return hash_cstring (cell_bytes (x->string), size); /* FIXME: hash x directly. */
error (cell_symbol_system_error, cons (make_string0 ("hashq_: not a symbol"), x));
}
int
hash_ (struct scm *x, long size)
{
if (x->type != TSTRING)
{
eputs ("hash_ failed, not a string:");
display_error_ (x);
assert_msg (0, "0");
}
return hash_cstring (cell_bytes (x->string), size);
}
struct scm *
hashq (struct scm *x, struct scm *size)
{
eputs ("hashq not supporteed\n");
assert_msg (0, "0");
}
struct scm *
hash (struct scm *x, struct scm *size)
{
eputs ("hash not supporteed\n");
assert_msg (0, "0");
}
struct scm *
hashq_get_handle (struct scm *table, struct scm *key, struct scm *dflt)
{
struct scm *s = struct_ref_ (table, 3);
long size = s->value;
unsigned hash = hashq_ (key, size);
struct scm *buckets = struct_ref_ (table, 4);
struct scm *bucket = vector_ref_ (buckets, hash);
struct scm *x = cell_f;
if (dflt->type == TPAIR)
x = dflt->car;
if (bucket->type == TPAIR)
x = assq (key, bucket);
return x;
}
struct scm *
hashq_ref (struct scm *table, struct scm *key, struct scm *dflt)
{
struct scm *x = hashq_get_handle (table, key, dflt);
if (x != cell_f)
x = x->cdr;
return x;
}
struct scm *
hash_ref (struct scm *table, struct scm *key, struct scm *dflt)
{
struct scm *s = struct_ref_ (table, 3);
long size = s->value;
unsigned hash = hash_ (key, size);
struct scm *buckets = struct_ref_ (table, 4);
struct scm *bucket = vector_ref_ (buckets, hash);
struct scm *x = cell_f;
if (dflt->type == TPAIR)
x = dflt->car;
if (bucket->type == TPAIR)
{
x = assoc (key, bucket);
if (x != cell_f)
x = x->cdr;
}
return x;
}
struct scm *
hash_set_x_ (struct scm *table, unsigned hash, struct scm *key, struct scm *value)
{
struct scm *buckets = struct_ref_ (table, 4);
struct scm *bucket = vector_ref_ (buckets, hash);
if (bucket->type != TPAIR)
bucket = cell_nil;
bucket = acons (key, value, bucket);
vector_set_x_ (buckets, hash, bucket);
return value;
}
struct scm *
hashq_set_x (struct scm *table, struct scm *key, struct scm *value)
{
struct scm *s = struct_ref_ (table, 3);
long size = s->value;
unsigned hash = hashq_ (key, size);
return hash_set_x_ (table, hash, key, value);
}
struct scm *
hash_set_x (struct scm *table, struct scm *key, struct scm *value)
{
struct scm *s = struct_ref_ (table, 3);
long size = s->value;
unsigned hash = hash_ (key, size);
return hash_set_x_ (table, hash, key, value);
}
struct scm *
hash_table_printer (struct scm *table)
{
fdputs ("#<", __stdout);
display_ (struct_ref_ (table, 2));
fdputc (' ', __stdout);
fdputs ("size: ", __stdout);
display_ (struct_ref_ (table, 3));
fdputc (' ', __stdout);
struct scm *buckets = struct_ref_ (table, 4);
fdputs ("buckets: ", __stdout);
int i;
struct scm *e;
for (i = 0; i < buckets->length; i = i + 1)
{
e = vector_ref_ (buckets, i);
if (e != cell_unspecified)
{
fdputc ('[', __stdout);
while (e->type == TPAIR)
{
write_ (e->car->car);
e = e->cdr;
if (e->type == TPAIR)
fdputc (' ', __stdout);
}
fdputs ("]\n ", __stdout);
}
}
fdputc ('>', __stdout);
}
struct scm *
make_hashq_type () /*:((internal)) */
{
struct scm *fields = cell_nil;
fields = cons (cell_symbol_buckets, fields);
fields = cons (cell_symbol_size, fields);
fields = cons (fields, cell_nil);
fields = cons (cell_symbol_hashq_table, fields);
return make_struct (cell_symbol_record_type, fields, cell_unspecified);
}
struct scm *
make_hash_table_ (long size)
{
if (size == 0)
size = 100;
struct scm *hashq_type = make_hashq_type ();
struct scm *buckets = make_vector_ (size, cell_unspecified);
struct scm *values = cell_nil;
values = cons (buckets, values);
values = cons (make_number (size), values);
values = cons (cell_symbol_hashq_table, values);
/*FIXME: symbol/printer
return make_struct (hashq_type, values, cstring_to_symbol ("hash-table-printer");*/
return make_struct (hashq_type, values, cell_unspecified);
}
struct scm *
make_hash_table (struct scm *x)
{
long size = 0;
if (x->type == TPAIR)
{
assert_msg (x->type == TNUMBER, "x->type == TNUMBER");
size = x->value;
}
return make_hash_table_ (size);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
/** Commentary:
Scheme library functions not used by the eval/apply core.
*/
/** Code: */
#include "mes/lib.h"
#include "mes/mes.h"
#include <stdlib.h>
struct scm *
type_ (struct scm *x)
{
return make_number (x->type);
}
struct scm *
car_ (struct scm *x)
{
struct scm *a = x->car;
if (x->type == TPAIR)
return a;
return make_number (cast_scmp_to_long (a));
}
struct scm *
cdr_ (struct scm *x)
{
struct scm *d = x->cdr;
if (x->type == TPAIR || x->type == TCLOSURE)
return d;
return make_number (cast_scmp_to_long (d));
}
struct scm *
xassq (struct scm *x, struct scm *a) /* For speed in core. */
{
while (a != cell_nil)
{
if (x == a->car->cdr)
return a->car;
a = a->cdr;
}
return cell_f;
}
struct scm *
memq (struct scm *x, struct scm *a)
{
int t = x->type;
if (t == TCHAR || t == TNUMBER)
{
long v = x->value;
while (a != cell_nil)
{
if (v == a->car->value)
return a;
a = a->cdr;
}
return cell_f;
}
if (t == TKEYWORD)
{
while (a != cell_nil)
{
if (a->car->type == TKEYWORD)
if (string_equal_p (x, a->car) == cell_t)
return a;
a = a->cdr;
}
return cell_f;
}
while (a != cell_nil)
{
if (x == a->car)
return a;
a = a->cdr;
}
return cell_f;
}
struct scm *
equal2_p (struct scm *a, struct scm *b)
{
long i;
struct scm *ai;
struct scm *bi;
equal2:
if (a == b)
return cell_t;
if (a->type == TPAIR && b->type == TPAIR)
{
if (equal2_p (a->car, b->car) == cell_t)
{
a = a->cdr;
b = b->cdr;
goto equal2;
}
return cell_f;
}
if (a->type == TSTRING && b->type == TSTRING)
return string_equal_p (a, b);
if (a->type == TVECTOR && b->type == TVECTOR)
{
if (a->length != b->length)
return cell_f;
for (i = 0; i < a->length; i = i + 1)
{
ai = cell_ref (a->vector, i);
bi = cell_ref (b->vector, i);
if (ai->type == TREF)
ai = ai->ref;
if (bi->type == TREF)
bi = bi->ref;
if (equal2_p (ai, bi) == cell_f)
return cell_f;
}
return cell_t;
}
return eq_p (a, b);
}
struct scm *
last_pair (struct scm *x)
{
while (x != cell_nil)
{
if (x->cdr == cell_nil)
return x;
x = x->cdr;
}
return x;
}
struct scm *
pair_p (struct scm *x)
{
if (x->type == TPAIR)
return cell_t;
return cell_f;
}
struct scm *
char_to_integer (struct scm *x)
{
return make_number (x->value);
}
struct scm *
integer_to_char (struct scm *x)
{
return make_char (x->value);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mes/lib.h"
#include "mes/mes.h"
struct scm *
apply_builtin0 (struct scm *fn)
{
FUNCTION fp = builtin_function (fn);
return fp ();
}
struct scm *
apply_builtin1 (struct scm *fn, struct scm *x)
{
FUNCTION fp = builtin_function (fn);
return fp (x);
}
struct scm *
apply_builtin2 (struct scm *fn, struct scm *x, struct scm *y)
{
FUNCTION fp = builtin_function (fn);
return fp (x, y);
}
struct scm *
apply_builtin3 (struct scm *fn, struct scm *x, struct scm *y, struct scm *z)
{
FUNCTION fp = builtin_function (fn);
return fp (x, y, z);
}
#undef cast_charp_to_scmp
#undef cast_charp_to_scmpp
#undef cast_voidp_to_charp
#undef cast_scmp_to_long
#undef cast_scmp_to_charp
struct scm *
cast_charp_to_scmp (char const *i)
{
return i;
}
struct scm **
cast_charp_to_scmpp (char const *i)
{
return i;
}
char*
cast_voidp_to_charp (void const *i)
{
return i;
}
long
cast_scmp_to_long (struct scm *i)
{
return i;
}
char*
cast_scmp_to_charp (struct scm *i)
{
return i;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2020,2021 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
* Copyright © 2021 W. J. van der Laan <laanwj@protonmail.com>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mes/lib.h"
#include "mes/mes.h"
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
void
assert_number (char const *name, struct scm *x)
{
if (x->type != TNUMBER)
{
eputs (name);
error (cell_symbol_not_a_number, x);
}
}
struct scm *
greater_p (struct scm *x) /*:((name . ">") (arity . n)) */
{
if (x == cell_nil)
return cell_t;
assert_number ("greater_p", x->car);
long n = x->car->value;
x = x->cdr;
struct scm *i;
long v;
while (x != cell_nil)
{
assert_number ("greater_p", x->car);
i = car (x);
v = i->value;
if (v >= n)
return cell_f;
n = v;
x = cdr (x);
}
return cell_t;
}
struct scm *
less_p (struct scm *x) /*:((name . "<") (arity . n)) */
{
if (x == cell_nil)
return cell_t;
assert_number ("less_p", x->car);
long n = x->car->value;
x = x->cdr;
struct scm *i;
long v;
while (x != cell_nil)
{
assert_number ("less_p", x->car);
i = car (x);
v = i->value;
if (v <= n)
return cell_f;
n = v;
x = cdr (x);
}
return cell_t;
}
struct scm *
is_p (struct scm *x) /*:((name . "=") (arity . n)) */
{
if (x == cell_nil)
return cell_t;
assert_number ("is_p", x->car);
long n = x->car->value;
x = cdr (x);
struct scm *i;
long v;
while (x != cell_nil)
{
i = car (x);
v = i->value;
if (v != n)
return cell_f;
x = cdr (x);
}
return cell_t;
}
struct scm *
minus (struct scm *x) /*:((name . "-") (arity . n)) */
{
assert_number ("minus", x->car);
long n = x->car->value;
x = cdr (x);
if (x == cell_nil)
n = -n;
struct scm *i;
long v;
while (x != cell_nil)
{
i = car (x);
assert_number ("minus", i);
v = i->value;
n = n - v;
x = cdr (x);
}
return make_number (n);
}
struct scm *
plus (struct scm *x) /*:((name . "+") (arity . n)) */
{
long n = 0;
struct scm *i;
long v;
while (x != cell_nil)
{
i = car (x);
assert_number ("plus", i);
v = i->value;
n = n + v;
x = cdr (x);
}
return make_number (n);
}
struct scm *
divide (struct scm *x) /*:((name . "/") (arity . n)) */
{
long n = 1;
struct scm *i;
long v;
if (x != cell_nil)
{
i = car (x);
assert_number ("divide", i);
v = i->value;
n = v;
x = cdr (x);
}
int sign_p = 0;
size_t u = n;
if (n < 0)
{
sign_p = 1;
u = -n;
}
size_t w;
while (x != cell_nil)
{
i = car (x);
assert_number ("divide", i);
v = i->value;
sign_p = sign_p && v > 0 || !sign_p && v < 0;
w = v;
if (v == 0)
error (cstring_to_symbol ("divide-by-zero"), x);
if (u == 0)
break;
if (w != 1)
u = u / w;
x = cdr (x);
}
n = u;
if (sign_p)
n = -n;
return make_number (n);
}
struct scm *
modulo (struct scm *a, struct scm *b)
{
assert_number ("modulo", a);
assert_number ("modulo", b);
long n = a->value;
long v = b->value;
if (v == 0)
error (cstring_to_symbol ("divide-by-zero"), a);
int sign_p = 0;
size_t w = v;
if (v < 0)
{
sign_p = 1;
w = -v;
}
while (n < 0)
n = n + w;
size_t u = n;
if (u != 0)
u = u % w;
n = u;
if (sign_p)
n = -n;
return make_number (n);
}
struct scm *
multiply (struct scm *x) /*:((name . "*") (arity . n)) */
{
long n = 1;
struct scm *i;
long v;
while (x != cell_nil)
{
i = car (x);
assert_number ("multiply", i);
v = i->value;
n = n * v;
x = cdr (x);
}
return make_number (n);
}
struct scm *
logand (struct scm *x) /*:((arity . n)) */
{
long n = -1;
struct scm *i;
long v;
while (x != cell_nil)
{
i = car (x);
assert_number ("multiply", i);
v = i->value;
n = n & v;
x = cdr (x);
}
return make_number (n);
}
struct scm *
logior (struct scm *x) /*:((arity . n)) */
{
long n = 0;
struct scm *i;
long v;
while (x != cell_nil)
{
i = car (x);
assert_number ("logior", i);
v = i->value;
n = n | v;
x = cdr (x);
}
return make_number (n);
}
struct scm *
lognot (struct scm *x)
{
assert_number ("lognot", x);
long n = ~x->value;
return make_number (n);
}
struct scm *
logxor (struct scm *x) /*:((arity . n)) */
{
long n = 0;
struct scm *i;
long v;
while (x != cell_nil)
{
i = car (x);
assert_number ("logxor", i);
v = i->value;
n = n ^ v;
x = cdr (x);
}
return make_number (n);
}
struct scm *
ash (struct scm *n, struct scm *count)
{
assert_number ("ash", n);
assert_number ("ash", count);
long cn = n->value;
long ccount = count->value;
long result;
if (ccount < 0)
result = cn >> -ccount;
else
result = cn << ccount;
return make_number (result);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2020,2021 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mes/lib.h"
#include "mes/mes.h"
#include <fcntl.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
struct scm *
mes_g_stack (struct scm *a) /*:((internal)) */
{
g_stack = STACK_SIZE;
R0 = a;
R1 = make_char (0);
R2 = make_char (0);
R3 = make_char (0);
return R0;
}
struct scm *
mes_environment (int argc, char **argv)
{
struct scm *a = init_symbols ();
char *compiler = "gnuc";
#if __MESC__
compiler = "mesc";
#elif __TINYC__
compiler = "tcc";
#endif
a = acons (cell_symbol_compiler, make_string0 (compiler), a);
char *arch;
#if __i386__
arch = "x86";
#elif __arm__
arch = "arm";
#elif __x86_64__
arch = "x86_64";
#else
#error arch not supported
#endif
a = acons (cell_symbol_arch, make_string0 (arch), a);
#if !MES_MINI
struct scm *lst = cell_nil;
int i;
for (i = argc - 1; i >= 0; i = i - 1)
lst = cons (make_string0 (argv[i]), lst);
a = acons (cell_symbol_argv, lst, a);
#endif
return mes_g_stack (a);
}
int
try_open_boot (char *file_name, char const *boot, char const *location)
{
strcpy (file_name + strlen (file_name), boot);
if (g_debug > 1)
{
eputs ("mes: reading boot-0 [");
eputs (location);
eputs ("]: ");
eputs (file_name);
eputs ("\n");
}
int fd = mes_open (file_name, O_RDONLY, 0);
if (g_debug != 0 && fd > 0)
{
eputs ("mes: read boot-0: ");
eputs (file_name);
eputs ("\n");
}
return fd;
}
void
open_boot ()
{
__stdin = -1;
char *boot = __open_boot_buf;
char *file_name = __open_boot_file_name;
strcpy (g_datadir, ".");
if (getenv ("MES_BOOT") != 0)
strcpy (boot, getenv ("MES_BOOT"));
else
strcpy (boot, "boot-0.scm");
if (getenv ("MES_PREFIX") != 0)
{
strcpy (g_datadir, getenv ("MES_PREFIX"));
strcpy (g_datadir + strlen (g_datadir), "/mes");
strcpy (file_name, g_datadir);
strcpy (file_name + strlen (file_name), "/module/mes/");
__stdin = try_open_boot (file_name, boot, "MES_PREFIX");
if (__stdin < 0)
{
strcpy (g_datadir, getenv ("MES_PREFIX"));
strcpy (g_datadir + strlen (g_datadir), "/share/mes");
strcpy (file_name, g_datadir);
strcpy (file_name + strlen (file_name), "/module/mes/");
__stdin = try_open_boot (file_name, boot, "MES_PREFIX/share/mes");
}
}
if (__stdin < 0)
{
g_datadir[0] = 0;
if (getenv ("srcdest") != 0)
strcpy (g_datadir, getenv ("srcdest"));
strcpy (g_datadir + strlen (g_datadir), "mes");
strcpy (file_name, g_datadir);
strcpy (file_name + strlen (file_name), "/module/mes/");
__stdin = try_open_boot (file_name, boot, "${srcdest}mes");
}
if (__stdin < 0)
{
file_name[0] = 0;
__stdin = try_open_boot (file_name, boot, "<boot>");
}
if (__stdin < 0)
{
eputs ("mes: boot failed: no such file: ");
eputs (boot);
eputs ("\n");
exit (1);
}
}
struct scm *
read_boot () /*:((internal)) */
{
R2 = read_input_file_env (R0);
__stdin = STDIN;
return R2;
}
void
init (char **envp)
{
environ = envp;
__execl_c_argv = malloc (1024 * sizeof (char *)); /* POSIX minimum: 4096 */
__gettimeofday_time = malloc (sizeof (struct timeval));
__get_internal_run_time_ts = malloc (sizeof (struct timespec));
__open_boot_buf = malloc (PATH_MAX);
__open_boot_file_name = malloc (PATH_MAX);
__reader_read_char_buf = malloc (10);
__setenv_buf = malloc (1024);
g_datadir = malloc (1024);
g_start_time = malloc (sizeof (struct timespec));
memset (g_start_time, 0, sizeof (struct timespec));
gc_start_time = malloc (sizeof (struct timespec));
gc_end_time = malloc (sizeof (struct timespec));
char *p = getenv ("MES_DEBUG");
if (p != 0)
g_debug = atoi (p);
g_mini = cast_charp_to_long (getenv ("MES_MINI"));
open_boot ();
gc_init ();
}
int
main (int argc, char **argv, char **envp)
{
init (envp);
struct scm *a = mes_environment (argc, argv);
a = mes_builtins (a);
a = init_time (a);
M0 = make_initial_module (a);
g_macros = make_hash_table_ (0);
if (g_debug > 5)
module_printer (M0);
struct scm *program = read_boot ();
R0 = acons (cell_symbol_program, program, R0);
push_cc (R2, cell_unspecified, R0, cell_unspecified);
if (g_debug > 2)
gc_stats_ ("\n gc boot");
if (g_debug > 3)
{
eputs ("program: ");
write_error_ (R1);
eputs ("\n");
}
R3 = cell_vm_begin_expand;
R1 = eval_apply ();
if (g_debug != 0)
{
write_error_ (R1);
eputs ("\n");
}
if (g_debug != 0)
{
if (g_debug > 5)
module_printer (M0);
if (g_debug < 3)
gc_stats_ ("\ngc run");
MAX_ARENA_SIZE = 0;
gc (g_stack);
if (g_debug < 3)
gc_stats_ (" => ");
if (g_debug > 5)
{
eputs ("\nports:");
write_error_ (g_ports);
eputs ("\n");
}
eputs ("\n");
}
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mes/lib.h"
#include "mes/mes.h"
struct scm *
make_module_type () /*:(internal)) */
{
struct scm *fields = cell_nil;
fields = cons (cstring_to_symbol ("globals"), fields);
fields = cons (cstring_to_symbol ("locals"), fields);
fields = cons (cstring_to_symbol ("name"), fields);
fields = cons (fields, cell_nil);
fields = cons (cell_symbol_module, fields);
return make_struct (cell_symbol_record_type, fields, cell_unspecified);
}
struct scm *
make_initial_module (struct scm *a) /*:((internal)) */
{
struct scm *module_type = make_module_type ();
a = acons (cell_symbol_module, module_type, a);
struct scm *hashq_type = make_hashq_type ();
a = acons (cell_symbol_hashq_table, hashq_type, a);
struct scm *name = cons (cstring_to_symbol ("boot"), cell_nil);
struct scm *globals = make_hash_table_ (0);
struct scm *locals = cell_nil;
struct scm *values = cell_nil;
values = cons (globals, values);
values = cons (locals, values);
values = cons (name, values);
values = cons (cell_symbol_module, values);
struct scm *module = make_struct (module_type, values, cstring_to_symbol ("module-printer"));
R0 = cell_nil;
R0 = cons (a->cdr->car, R0);
R0 = cons (a->car, R0);
M0 = module;
while (a->type == TPAIR)
{
module_define_x (module, a->car->car, a->car->cdr);
a = a->cdr;
}
return module;
}
struct scm *
module_printer (struct scm *module)
{
fdputs ("#<", __stdout);
display_ (struct_ref_ (module, 2));
fdputc (' ', __stdout);
fdputs ("name: ", __stdout);
display_ (struct_ref_ (module, 3));
fdputc (' ', __stdout);
fdputs ("locals: ", __stdout);
display_ (struct_ref_ (module, 4));
fdputc (' ', __stdout);
struct scm *table = struct_ref_ (module, 5);
fdputs ("globals:\n ", __stdout);
display_ (table);
fdputc ('>', __stdout);
}
struct scm *
module_variable (struct scm *module, struct scm *name)
{
/*struct scm *locals = struct_ref_ (module, 3);*/
struct scm *locals = module;
struct scm *x = assq (name, locals);
if (x == cell_f)
{
module = M0;
struct scm *globals = struct_ref_ (module, 5);
x = hashq_get_handle (globals, name, cell_f);
}
return x;
}
struct scm *
module_ref (struct scm *module, struct scm *name)
{
struct scm *x = module_variable (module, name);
if (x == cell_f)
return cell_undefined;
return x->cdr;
}
struct scm *
module_define_x (struct scm *module, struct scm *name, struct scm *value)
{
module = M0;
struct scm *globals = struct_ref_ (module, 5);
return hashq_set_x (globals, name, value);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2020,2021 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mes/lib.h"
#include "mes/mes.h"
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#if SYSTEM_LIBC
#define __raise(x) -1
#endif
struct scm *
abort_ () /*:((name . "abort")) */
{
if (g_debug > 0)
eputs ("abort!\n");
if (__raise (SIGABRT) < 0) /* could not raise SIGABRT */
{
/* Fail in any way possible */
char* x = 0;
x[0] = 2;
}
return cell_unspecified;
}
struct scm *
exit_ (struct scm *x) /*:((name . "exit")) */
{
assert_msg (x->type == TNUMBER, "x->type == TNUMBER");
exit (x->value);
}
int
peekchar ()
{
if (__stdin >= 0)
{
int c = readchar ();
unreadchar (c);
return c;
}
struct scm *port = current_input_port ();
struct scm *string = port->string;
size_t length = string->length;
if (length == 0)
return -1;
char const *p = cell_bytes (string->string);
return p[0];
}
int
readchar ()
{
if (__stdin >= 0)
return fdgetc (__stdin);
struct scm *port = current_input_port ();
struct scm *string = port->string;
size_t length = string->length;
if (length == 0)
return -1;
char const *p = cell_bytes (string->string);
int c = p[0];
p = p + 1;
port->string = make_string (p, length - 1);
return c;
}
int
unreadchar (int c)
{
if (__stdin >= 0)
return fdungetc (c, __stdin);
if (c == EOF) /* can't unread EOF */
return c;
struct scm *port = current_input_port ();
struct scm *string = port->string;
size_t length = string->length;
char *p = cell_bytes (string->string);
p = p - 1;
string = make_string (p, length + 1);
p = cell_bytes (string->string);
p[0] = c;
port->string = string;
return c;
}
struct scm *
peek_byte ()
{
return make_number (peekchar ());
}
struct scm *
read_byte ()
{
return make_number (readchar ());
}
struct scm *
unread_byte (struct scm *i)
{
unreadchar (i->value);
return i;
}
struct scm *
peek_char ()
{
return make_char (peekchar ());
}
struct scm *
read_char (struct scm *port) /*:((arity . n)) */
{
int fd = __stdin;
if (port->type == TPAIR)
if (port->car->type == TNUMBER)
__stdin = port->car->value;
struct scm *c = make_char (readchar ());
__stdin = fd;
return c;
}
struct scm *
unread_char (struct scm *i)
{
unreadchar (i->value);
return i;
}
struct scm *
write_char (struct scm *i) /*:((arity . n)) */
{
write_byte (i);
return i;
}
struct scm *
write_byte (struct scm *x) /*:((arity . n)) */
{
struct scm *c = car (x);
struct scm *p = cdr (x);
int fd = __stdout;
if (p->type == TPAIR)
{
struct scm *f = p->car;
if (f->type == TNUMBER)
{
long v = f->value;
if (v != 1)
fd = v;
if (v == 2)
fd = __stderr;
}
}
char cc = c->value;
write (fd, &cc, 1);
#if !__MESC__
assert_msg (c->type == TNUMBER || c->type == TCHAR, "c->type == TNUMBER || c->type == TCHAR");
#endif
return c;
}
struct scm *
getenv_ (struct scm *s) /*:((name . "getenv")) */
{
char *p;
p = getenv (cell_bytes (s->string));
if (p != 0)
return make_string0 (p);
return cell_f;
}
struct scm *
setenv_ (struct scm *s, struct scm *v) /*:((name . "setenv")) */
{
char *buf = __setenv_buf;
strcpy (buf, cell_bytes (s->string));
setenv (buf, cell_bytes (v->string), 1);
return cell_unspecified;
}
struct scm *
access_p (struct scm *file_name, struct scm *mode)
{
int result = access (cell_bytes (file_name->string), mode->value);
if (result == 0)
return cell_t;
return cell_f;
}
struct scm *
current_input_port ()
{
if (__stdin >= 0)
return make_number (__stdin);
struct scm *x = g_ports;
struct scm *a;
while (x != 0)
{
a = x->car;
if (a->port == __stdin)
return a;
x = x->cdr;
}
return x->car;
}
struct scm *
open_input_file (struct scm *file_name)
{
int filedes = mes_open (cell_bytes (file_name->string), O_RDONLY, 0);
if (filedes == -1)
error (cell_symbol_system_error, cons (make_string0 ("No such file or directory"), file_name));
return make_number (filedes);
}
struct scm *
open_input_string (struct scm *string)
{
struct scm *port = make_string_port (string);
g_ports = cons (port, g_ports);
return port;
}
struct scm *
set_current_input_port (struct scm *port)
{
struct scm *prev = current_input_port ();
if (port->type == TNUMBER)
{
int p = port->value;
if (p != 0)
__stdin = p;
else
__stdin = STDIN;
}
else if (port->type == TPORT)
__stdin = port->port;
return prev;
}
struct scm *
current_output_port ()
{
return make_number (__stdout);
}
struct scm *
current_error_port ()
{
return make_number (__stderr);
}
struct scm *
open_output_file (struct scm *x) /*:((arity . n)) */
{
struct scm *file_name = car (x);
x = cdr (x);
int mode = S_IRUSR | S_IWUSR;
if (x->type == TPAIR)
{
struct scm *i = car (x);
if (i->type == TNUMBER)
mode = i->value;
}
return make_number (mes_open (cell_bytes (file_name->string), O_WRONLY | O_CREAT | O_TRUNC, mode));
}
struct scm *
set_current_output_port (struct scm *port)
{
if (port->value != 0)
__stdout = port->value;
else
__stdout = STDOUT;
return current_output_port ();
}
struct scm *
set_current_error_port (struct scm *port)
{
if (port->value != 0)
__stderr = port->value;
else
__stderr = STDERR;
return current_error_port ();
}
struct scm *
chmod_ (struct scm *file_name, struct scm *mode) /*:((name . "chmod")) */
{
chmod (cell_bytes (file_name->string), mode->value);
return cell_unspecified;
}
struct scm *
isatty_p (struct scm *port)
{
if (isatty (port->value) != 0)
return cell_t;
return cell_f;
}
struct scm *
primitive_fork ()
{
return make_number (fork ());
}
struct scm *
execl_ (struct scm *file_name, struct scm *args) /*:((name . "execl")) */
{
char **c_argv = __execl_c_argv;
int i = 0;
if (length__ (args) > 1000)
error (cell_symbol_system_error,
cons (file_name, cons (make_string0 ("too many arguments"), cons (file_name, args))));
c_argv[i] = cell_bytes (file_name->string);
i = i + 1;
struct scm *arg;
while (args != cell_nil)
{
assert_msg (args->car->type == TSTRING, "args->car->type == TSTRING");
arg = args->car;
c_argv[i] = cell_bytes (arg->string);
i = i + 1;
args = args->cdr;
if (g_debug > 2)
{
eputs ("arg[");
eputs (itoa (i));
eputs ("]: ");
eputs (c_argv[i - 1]);
eputs ("\n");
}
}
c_argv[i] = 0;
return make_number (execv (c_argv[0], c_argv));
}
struct scm *
waitpid_ (struct scm *pid, struct scm *options)
{
int status;
int child = waitpid (pid->value, &status, options->value);
return cons (make_number (child), make_number (status));
}
#if __x86_64__
/* Nanoseconds on 64-bit systems with POSIX timers. */
// CONSTANT TIME_UNITS_PER_SECOND 1000000000
#define TIME_UNITS_PER_SECOND 1000000000U
#else
/* Milliseconds for everyone else. */
// CONSTANT TIME_UNITS_PER_SECOND 1000
#define TIME_UNITS_PER_SECOND 1000U
#endif
struct scm *
init_time (struct scm *a) /*:((internal)) */
{
clock_gettime (CLOCK_PROCESS_CPUTIME_ID, g_start_time);
a = acons (cell_symbol_internal_time_units_per_second, make_number (TIME_UNITS_PER_SECOND), a);
}
struct scm *
current_time ()
{
return make_number (time (0));
}
struct scm *
gettimeofday_ () /*:((name . "gettimeofday")) */
{
struct timeval *time = __gettimeofday_time;
gettimeofday (time, 0);
return cons (make_number (time->tv_sec), make_number (time->tv_usec));
}
#define UL1000000000 1000000000UL
// CONSTANT UL1000000000 1000000000
long
seconds_and_nanoseconds_to_long (long s, long ns)
{
size_t uns = ns;
if (ns < 0)
{
uns = - ns;
return s * TIME_UNITS_PER_SECOND - uns / (UL1000000000 / TIME_UNITS_PER_SECOND);
}
return s * TIME_UNITS_PER_SECOND + uns / (UL1000000000 / TIME_UNITS_PER_SECOND);
}
struct scm *
get_internal_run_time ()
{
struct timespec *ts = __get_internal_run_time_ts;
clock_gettime (CLOCK_PROCESS_CPUTIME_ID, ts);
long time = seconds_and_nanoseconds_to_long (ts->tv_sec - g_start_time->tv_sec,
ts->tv_nsec - g_start_time->tv_nsec);
return make_number (time);
}
struct scm *
getcwd_ () /*:((name . "getcwd")) */
{
return make_string0 (getcwd (0, PATH_MAX));
}
struct scm *
dup_ (struct scm *port) /*:((name . "dup")) */
{
return make_number (dup (port->value));
}
struct scm *
dup2_ (struct scm *old, struct scm *new) /*:((name . "dup2")) */
{
dup2 (old->value, new->value);
return cell_unspecified;
}
struct scm *
delete_file (struct scm *file_name)
{
unlink (cell_bytes (file_name->string));
return cell_unspecified;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
* Copyright © 2018 Jeremiah Orians <jeremiah@pdp10.guru>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mes/lib.h"
#include "mes/mes.h"
#include <ctype.h>
#include <stdio.h>
#include <string.h>
struct scm *
read_input_file_env_ (struct scm *e, struct scm *a)
{
if (e == cell_nil)
return e;
return cons (e, read_input_file_env_ (read_env (a), a));
}
struct scm *
read_input_file_env (struct scm *a)
{
return read_input_file_env_ (read_env (cell_nil), cell_nil);
}
int
reader_read_line_comment (int c)
{
while (c != EOF)
{
if (c == '\n')
return c;
c = readchar ();
}
error (cell_symbol_system_error, make_string0 ("reader_read_line_comment"));
}
struct scm *reader_read_block_comment (int s, int c);
struct scm *reader_read_hash (int c, struct scm *a);
struct scm *reader_read_list (int c, struct scm *a);
int
reader_identifier_p (int c)
{
return (c > ' ' && c <= '~' && c != '"' && c != ';' && c != '(' && c != ')' && c != EOF);
}
int
reader_end_of_word_p (int c)
{
return (c == '"' || c == ';' || c == '(' || c == ')' || isspace (c) || c == EOF);
}
struct scm *
reader_read_identifier_or_number (int c)
{
int i = 0;
long n = 0;
int negative_p = 0;
if (c == '+')
if (isdigit (peekchar ()) != 0)
c = readchar ();
if (c == '-')
if (isdigit (peekchar ()) != 0)
{
negative_p = 1;
c = readchar ();
}
while (isdigit (c) != 0)
{
g_buf[i] = c;
i = i + 1;
n = n * 10;
n = n + c - '0';
c = readchar ();
}
if (reader_end_of_word_p (c) != 0)
{
unreadchar (c);
if (negative_p != 0)
n = 0 - n;
return make_number (n);
}
/* Fallthrough: Note that `4a', `+1b' are identifiers */
while (reader_end_of_word_p (c) == 0)
{
g_buf[i] = c;
i = i + 1;
c = readchar ();
}
unreadchar (c);
g_buf[i] = 0;
return cstring_to_symbol (g_buf);
}
struct scm *
reader_read_sexp_ (int c, struct scm *a)
{
reset_reader:
if (c == EOF)
return cell_nil;
if (c == ';')
{
c = reader_read_line_comment (c);
goto reset_reader;
}
if ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\f'))
{
c = readchar ();
goto reset_reader;
}
if (c == '(')
return reader_read_list (readchar (), a);
if (c == ')')
return cell_nil;
if (c == '#')
return reader_read_hash (readchar (), a);
if (c == '`')
return cons (cell_symbol_quasiquote, cons (reader_read_sexp_ (readchar (), a), cell_nil));
if (c == ',')
{
if (peekchar () == '@')
{
readchar ();
return cons (cell_symbol_unquote_splicing, cons (reader_read_sexp_ (readchar (), a), cell_nil));
}
return cons (cell_symbol_unquote, cons (reader_read_sexp_ (readchar (), a), cell_nil));
}
if (c == '\'')
return cons (cell_symbol_quote, cons (reader_read_sexp_ (readchar (), a), cell_nil));
if (c == '"')
return reader_read_string ();
if (c == '.')
if (reader_identifier_p (peekchar ()) == 0)
return cell_dot;
return reader_read_identifier_or_number (c);
}
int
reader_eat_whitespace (int c)
{
while (isspace (c) != 0)
c = readchar ();
if (c == ';')
return reader_eat_whitespace (reader_read_line_comment (c));
if (c == '#')
{
int p = peekchar ();
if (p == '!' || p == '|')
{
c = readchar ();
reader_read_block_comment (c, readchar ());
return reader_eat_whitespace (readchar ());
}
}
return c;
}
struct scm *
reader_read_list (int c, struct scm *a)
{
c = reader_eat_whitespace (c);
if (c == ')')
return cell_nil;
if (c == EOF)
error (cell_symbol_not_a_pair, make_string0 ("EOF in list"));
struct scm *s = reader_read_sexp_ (c, a);
if (s == cell_dot)
{
s = reader_read_list (readchar (), a);
return s->car;
}
return cons (s, reader_read_list (readchar (), a));
}
struct scm *
read_env (struct scm *a)
{
return reader_read_sexp_ (readchar (), a);
}
struct scm *
reader_read_block_comment (int s, int c)
{
if (c == s)
if (peekchar () == '#')
{
readchar ();
return cell_unspecified;
}
return reader_read_block_comment (s, readchar ());
}
struct scm *
reader_read_hash (int c, struct scm *a)
{
if (c == '!')
{
reader_read_block_comment (c, readchar ());
return reader_read_sexp_ (readchar (), a);
}
if (c == '|')
{
reader_read_block_comment (c, readchar ());
return reader_read_sexp_ (readchar (), a);
}
if (c == 'f')
return cell_f;
if (c == 't')
return cell_t;
if (c == ',')
{
if (peekchar () == '@')
{
readchar ();
return cons (cell_symbol_unsyntax_splicing, cons (reader_read_sexp_ (readchar (), a), cell_nil));
}
return cons (cell_symbol_unsyntax, cons (reader_read_sexp_ (readchar (), a), cell_nil));
}
if (c == '\'')
return cons (cell_symbol_syntax, cons (reader_read_sexp_ (readchar (), a), cell_nil));
if (c == '`')
return cons (cell_symbol_quasisyntax, cons (reader_read_sexp_ (readchar (), a), cell_nil));
if (c == ':')
{
struct scm *x = reader_read_identifier_or_number (readchar ());
struct scm *msg = make_string0 ("keyword perifx ':' not followed by a symbol: ");
if (x->type == TNUMBER)
error (cell_symbol_system_error, cons (msg, x));
return symbol_to_keyword (x);
}
if (c == 'b')
return reader_read_binary ();
if (c == 'o')
return reader_read_octal ();
if (c == 'x')
return reader_read_hex ();
if (c == '\\')
return reader_read_character ();
if (c == '(')
return list_to_vector (reader_read_list (readchar (), a));
if (c == ';')
{
reader_read_sexp_ (readchar (), a);
return reader_read_sexp_ (readchar (), a);
}
return reader_read_sexp_ (readchar (), a);
}
struct scm *
reader_read_sexp (struct scm *c, struct scm *s, struct scm *a)
{
return reader_read_sexp_ (c->value, a);
}
struct scm *
reader_read_character ()
{
int c = readchar ();
int p = peekchar ();
int i = 0;
if (c >= '0' && c <= '7' && p >= '0' && p <= '7')
{
c = c - '0';
while (p >= '0' && p <= '7')
{
c = c << 3;
c = c + readchar () - '0';
p = peekchar ();
}
}
else if (c == 'x' && ((p >= '0' && p <= '9') || (p >= 'a' && p <= 'f') || (p >= 'F' && p <= 'F')))
{
struct scm *n = reader_read_hex ();
c = n->value;
eputs ("reading hex c=");
eputs (itoa (c));
eputs ("\n");
}
else if (((c >= 'a' && c <= 'z') || c == '*') && ((p >= 'a' && p <= 'z') || p == '*'))
{
char *buf = __reader_read_char_buf;
buf[i] = c;
i = i + 1;
while ((p >= 'a' && p <= 'z') || p == '*')
{
buf[i] = readchar ();
i = i + 1;
p = peekchar ();
}
buf[i] = 0;
if (strcmp (buf, "*eof*") == 0)
c = EOF;
else if (strcmp (buf, "nul") == 0)
c = '\0';
else if (strcmp (buf, "alarm") == 0)
c = '\a';
else if (strcmp (buf, "backspace") == 0)
c = '\b';
else if (strcmp (buf, "tab") == 0)
c = '\t';
else if (strcmp (buf, "linefeed") == 0)
c = '\n';
else if (strcmp (buf, "newline") == 0)
c = '\n';
else if (strcmp (buf, "vtab") == 0)
c = '\v';
else if (strcmp (buf, "page") == 0)
c = '\f';
else if (strcmp (buf, "return") == 0)
/* Nyacc bug
c = '\r'; */
c = 13;
else if (strcmp (buf, "esc") == 0)
c = 27;
else if (strcmp (buf, "space") == 0)
c = ' ';
/* Nyacc uses old abbrevs */
else if (strcmp (buf, "bel") == 0)
c = '\a';
else if (strcmp (buf, "bs") == 0)
c = '\b';
else if (strcmp (buf, "ht") == 0)
c = '\t';
else if (strcmp (buf, "vt") == 0)
c = '\v';
else if (strcmp (buf, "cr") == 0)
/* Nyacc bug
c = '\r'; */
c = 13;
else
{
eputs ("char not supported: ");
eputs (buf);
eputs ("\n");
error (cell_symbol_system_error, make_string0 ("char not supported"));
}
}
return make_char (c);
}
struct scm *
reader_read_binary ()
{
long n = 0;
int c = peekchar ();
int negative_p = 0;
if (c == '-')
{
negative_p = 1;
readchar ();
c = peekchar ();
}
while (c == '0' || c == '1')
{
n = n << 1;
n = n + c - '0';
readchar ();
c = peekchar ();
}
if (negative_p != 0)
n = 0 - n;
return make_number (n);
}
struct scm *
reader_read_octal ()
{
long n = 0;
int c = peekchar ();
int negative_p = 0;
if (c == '-')
{
negative_p = 1;
readchar ();
c = peekchar ();
}
while (c >= '0' && c <= '7')
{
n = n << 3;
n = n + c - '0';
readchar ();
c = peekchar ();
}
if (negative_p != 0)
n = 0 - n;
return make_number (n);
}
struct scm *
reader_read_hex ()
{
long n = 0;
int c = peekchar ();
int negative_p = 0;
if (c == '-')
{
negative_p = 1;
readchar ();
c = peekchar ();
}
while ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'))
{
n = n << 4;
if (c >= 'a')
n = n + c - 'a' + 10;
else if (c >= 'A')
n = n + c - 'A' + 10;
else
n = n + c - '0';
readchar ();
c = peekchar ();
}
if (negative_p != 0)
n = 0 - n;
return make_number (n);
}
struct scm *
reader_read_string ()
{
size_t i = 0;
int c;
struct scm *n;
do
{
if (i > MAX_STRING)
assert_max_string (i, "reader_read_string", g_buf);
c = readchar ();
if (c == '"')
break;
if (c == '\\')
{
c = readchar ();
if (c == '\\' || c == '"')
0;
else if (c == '0')
c = '\0';
else if (c == 'a')
c = '\a';
else if (c == 'b')
c = '\b';
else if (c == 't')
c = '\t';
else if (c == 'n')
c = '\n';
else if (c == 'v')
c = '\v';
else if (c == 'f')
c = '\f';
else if (c == 'r')
/* Nyacc bug
c = '\r'; */
c = 13;
else if (c == 'e')
/* Nyacc bug
c = '\e'; */
c = 27;
else if (c == 'x')
{
n = reader_read_hex ();
c = n->value;
}
}
g_buf[i] = c;
i = i + 1;
}
while (1);
g_buf[i] = 0;
return make_string (g_buf, i);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
* Copyright © 2021 W. J. van der Laan <laanwj@protonmail.com>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mes/lib.h"
#include "mes/mes.h"
#include <stdlib.h>
struct scm *
frame_printer (struct scm *frame)
{
fdputs ("#<", __stdout);
display_ (struct_ref_ (frame, 2));
fdputc (' ', __stdout);
fdputs ("procedure: ", __stdout);
display_ (struct_ref_ (frame, 3));
fdputc ('>', __stdout);
}
struct scm *
make_frame_type () /*:((internal)) */
{
struct scm *fields = cell_nil;
fields = cons (cell_symbol_procedure, fields);
fields = cons (fields, cell_nil);
fields = cons (cell_symbol_frame, fields);
return make_struct (cell_symbol_record_type, fields, cell_unspecified);
}
struct scm *
make_frame (struct scm *stack, long index)
{
struct scm *frame_type = make_frame_type ();
long array_index = 0;
struct scm *procedure = 0;
if (index != 0)
{
array_index = (STACK_SIZE - (index * GC_FRAME_SIZE));
procedure = g_stack_array[array_index + GC_FRAME_PROCEDURE];
}
if (procedure == 0)
procedure = cell_f;
struct scm *values = cell_nil;
values = cons (procedure, values);
values = cons (cell_symbol_frame, values);
return make_struct (frame_type, values, cstring_to_symbol ("frame-printer"));
}
struct scm *
make_stack_type () /*:((internal)) */
{
struct scm *fields = cell_nil;
fields = cons (cstring_to_symbol ("frames"), fields);
fields = cons (fields, cell_nil);
fields = cons (cell_symbol_stack, fields);
return make_struct (cell_symbol_record_type, fields, cell_unspecified);
}
struct scm *
make_stack (struct scm *stack) /*:((arity . n)) */
{
struct scm *stack_type = make_stack_type ();
long size = (STACK_SIZE - g_stack) / GC_FRAME_SIZE;
struct scm *frames = make_vector_ (size, cell_unspecified);
long i;
struct scm* frame;
for (i = 0; i < size; i = i + 1)
{
frame = make_frame (stack, i);
vector_set_x_ (frames, i, frame);
}
struct scm *values = cell_nil;
values = cons (frames, values);
values = cons (cell_symbol_stack, values);
return make_struct (stack_type, values, cell_unspecified);
}
struct scm *
stack_length (struct scm *stack)
{
struct scm *frames = struct_ref_ (stack, 3);
return vector_length (frames);
}
struct scm *
stack_ref (struct scm *stack, struct scm *index)
{
struct scm *frames = struct_ref_ (stack, 3);
return vector_ref (frames, index);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mes/lib.h"
#include "mes/mes.h"
#include <limits.h>
#include <string.h>
void
assert_max_string (size_t i, char const *msg, char const *string)
{
if (i > MAX_STRING)
{
eputs (msg);
eputs (":string too long[");
eputs (itoa (i));
eputs ("]:");
char *p = cast_voidp_to_charp (string);
p[MAX_STRING - 1] = 0;
eputs (p);
error (cell_symbol_system_error, cell_f);
}
}
char const *
list_to_cstring (struct scm *list, size_t *size)
{
size_t i = 0;
char *p = g_buf;
struct scm *x;
while (list != cell_nil)
{
if (i > MAX_STRING)
assert_max_string (i, "list_to_string", g_buf);
x = car (list);
g_buf[i] = x->value;
i = i + 1;
list = cdr (list);
}
g_buf[i] = 0;
size[0] = i;
return g_buf;
}
struct scm *
string_equal_p (struct scm *a, struct scm *b) /*:((name . "string=?")) */
{
if (!((a->type == TSTRING && b->type == TSTRING) || (a->type == TKEYWORD || b->type == TKEYWORD)))
{
eputs ("type a: ");
eputs (itoa (a->type));
eputs ("\n");
eputs ("type b: ");
eputs (itoa (b->type));
eputs ("\n");
eputs ("a= ");
write_error_ (a);
eputs ("\n");
eputs ("b= ");
write_error_ (b);
eputs ("\n");
assert_msg ((a->type == TSTRING && b->type == TSTRING) || (a->type == TKEYWORD || b->type == TKEYWORD), "(a->type == TSTRING && b->type == TSTRING) || (a->type == TKEYWORD || b->type == TKEYWORD)");
}
if (a == b)
return cell_t;
if (a->string == b->string)
return cell_t;
if (a->length == 0 && b->length == 0)
return cell_t;
if (a->length == b->length)
if (memcmp (cell_bytes (a->string), cell_bytes (b->string), a->length) == 0)
return cell_t;
return cell_f;
}
struct scm *
symbol_to_string (struct scm *symbol)
{
return make_cell (TSTRING, symbol->car, symbol->cdr);
}
struct scm *
symbol_to_keyword (struct scm *symbol)
{
return make_cell (TKEYWORD, symbol->car, symbol->cdr);
}
struct scm *
keyword_to_string (struct scm *keyword)
{
return make_cell (TSTRING, keyword->car, keyword->cdr);
}
struct scm *
string_to_symbol (struct scm *string)
{
struct scm *x = hash_ref (g_symbols, string, cell_f);
if (x == cell_f)
x = make_symbol (string);
return x;
}
struct scm *
make_symbol (struct scm *string)
{
struct scm *x = make_pointer_cell (TSYMBOL, string->length, string->string);
hash_set_x (g_symbols, string, x);
return x;
}
struct scm *
bytes_to_list (char const *s, size_t i)
{
struct scm *p = cell_nil;
int c;
while (i != 0)
{
i = i - 1;
c = (0x100 + s[i]) % 0x100;
p = cons (make_char (c), p);
}
return p;
}
struct scm *
cstring_to_list (char const *s)
{
return bytes_to_list (s, strlen (s));
}
struct scm *
cstring_to_symbol (char const *s)
{
struct scm *string = make_string0 (s);
return string_to_symbol (string);
}
struct scm *
string_to_list (struct scm *string)
{
return bytes_to_list (cell_bytes (string->string), string->length);
}
struct scm *
list_to_string (struct scm *list)
{
size_t size;
char const *s = list_to_cstring (list, &size);
return make_string (s, size);
}
struct scm *
read_string (struct scm *port) /*:((arity . n)) */
{
int fd = __stdin;
if (port->type == TPAIR)
{
struct scm *p = car (port);
if (p->type == TNUMBER)
__stdin = p->value;
}
int c = readchar ();
size_t i = 0;
while (c != -1)
{
if (i > MAX_STRING)
assert_max_string (i, "read_string", g_buf);
g_buf[i] = c;
i = i + 1;
c = readchar ();
}
g_buf[i] = 0;
__stdin = fd;
return make_string (g_buf, i);
}
struct scm *
string_append (struct scm *x) /*:((arity . n)) */
{
char *p = g_buf;
g_buf[0] = 0;
size_t size = 0;
struct scm *string;
while (x != cell_nil)
{
string = x->car;
assert_msg (string->type == TSTRING, "string->type == TSTRING");
memcpy (p, cell_bytes (string->string), string->length + 1);
p = p + string->length;
size = size + string->length;
if (size > MAX_STRING)
assert_max_string (size, "string_append", g_buf);
x = x->cdr;
}
return make_string (g_buf, size);
}
struct scm *
string_length (struct scm *string)
{
assert_msg (string->type == TSTRING, "string->type == TSTRING");
return make_number (string->length);
}
struct scm *
string_ref (struct scm *str, struct scm *k)
{
assert_msg (str->type == TSTRING, "str->type == TSTRING");
assert_msg (k->type == TNUMBER, "k->type == TNUMBER");
size_t size = str->length;
size_t i = k->value;
if (i > size)
error (cell_symbol_system_error, cons (make_string0 ("value out of range"), k));
char const *p = cell_bytes (str->string);
return make_char (p[i]);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mes/lib.h"
#include "mes/mes.h"
struct scm *
make_struct (struct scm *type, struct scm *fields, struct scm *printer)
{
long size = 2 + length__ (fields);
struct scm *x = alloc (1);
struct scm *v = alloc (size);
x->type = TSTRUCT;
x->length = size;
x->structure = v;
copy_cell (v, vector_entry (type));
copy_cell (cell_ref (v, 1), vector_entry (printer));
long i;
struct scm *e;
for (i = 2; i < size; i = i + 1)
{
e = cell_unspecified;
if (fields != cell_nil)
{
e = fields->car;
fields = fields->cdr;
}
copy_cell (cell_ref (v, i), vector_entry (e));
}
return x;
}
struct scm *
struct_length (struct scm *x)
{
assert_msg (x->type == TSTRUCT, "x->type == TSTRUCT");
return make_number (x->length);
}
struct scm *
struct_ref_ (struct scm *x, long i)
{
assert_msg (x->type == TSTRUCT, "x->type == TSTRUCT");
assert_msg (i < x->length, "i < x->length");
struct scm *e = cell_ref (x->structure, i);
if (e->type == TREF)
e = e->ref;
if (e->type == TCHAR)
e = make_char (e->value);
if (e->type == TNUMBER)
e = make_number (e->value);
return e;
}
struct scm *
struct_set_x_ (struct scm *x, long i, struct scm *e)
{
assert_msg (x->type == TSTRUCT, "x->type == TSTRUCT");
assert_msg (i < x->length, "i < x->length");
copy_cell (cell_ref (x->structure, i), vector_entry (e));
return cell_unspecified;
}
struct scm *
struct_ref (struct scm *x, struct scm *i)
{
return struct_ref_ (x, i->value);
}
struct scm *
struct_set_x (struct scm *x, struct scm *i, struct scm *e)
{
return struct_set_x_ (x, i->value, e);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2020,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mes/lib.h"
#include "mes/mes.h"
#include <string.h>
// char const *MES_VERSION = "0.24.2";
#if __M2_PLANET__
#define M2_CELL_SIZE 12
// CONSTANT M2_CELL_SIZE 12
#else
#define M2_CELL_SIZE 1
// CONSTANT M2_CELL_SIZE 12
#endif
struct scm *
init_symbol (struct scm *x, long type, char const *name)
{
x->type = type;
if (g_symbols == 0)
g_free = g_free + M2_CELL_SIZE;
else
{
int length = strlen (name);
struct scm *string = make_string (name, length);
x->car_value = length;
x->cdr = string->string;
hash_set_x (g_symbols, string, x);
}
g_symbol = g_symbol + M2_CELL_SIZE;
return x;
}
void
init_symbols_ () /*:((internal)) */
{
g_symbol = cell_nil;
cell_nil = init_symbol (g_symbol, TSPECIAL, "()");
cell_f = init_symbol (g_symbol, TSPECIAL, "#f");
cell_t = init_symbol (g_symbol, TSPECIAL, "#t");
cell_dot = init_symbol (g_symbol, TSPECIAL, ".");
cell_arrow = init_symbol (g_symbol, TSPECIAL, "=>");
cell_undefined = init_symbol (g_symbol, TSPECIAL, "*undefined*");
cell_unspecified = init_symbol (g_symbol, TSPECIAL, "*unspecified*");
cell_closure = init_symbol (g_symbol, TSPECIAL, "*closure*");
cell_circular = init_symbol (g_symbol, TSPECIAL, "*circular*");
cell_vm_apply = init_symbol (g_symbol, TSPECIAL, "core:apply");
cell_vm_apply2 = init_symbol (g_symbol, TSPECIAL, "*vm-apply2*");
cell_vm_begin = init_symbol (g_symbol, TSPECIAL, "*vm-begin*");
cell_vm_begin_eval = init_symbol (g_symbol, TSPECIAL, "*vm:begin-eval*");
cell_vm_begin_expand = init_symbol (g_symbol, TSPECIAL, "core:eval");
cell_vm_begin_expand_eval = init_symbol (g_symbol, TSPECIAL, "*vm:begin-expand-eval*");
cell_vm_begin_expand_macro = init_symbol (g_symbol, TSPECIAL, "*vm:begin-expand-macro*");
cell_vm_begin_expand_primitive_load = init_symbol (g_symbol, TSPECIAL, "*vm:core:begin-expand-primitive-load*");
cell_vm_begin_primitive_load = init_symbol (g_symbol, TSPECIAL, "*vm:core:begin-primitive-load*");
cell_vm_begin_read_input_file = init_symbol (g_symbol, TSPECIAL, "*vm-begin-read-input-file*");
cell_vm_call_with_current_continuation2 = init_symbol (g_symbol, TSPECIAL, "*vm-call-with-current-continuation2*");
cell_vm_call_with_values2 = init_symbol (g_symbol, TSPECIAL, "*vm-call-with-values2*");
cell_vm_eval = init_symbol (g_symbol, TSPECIAL, "core:eval-expanded");
cell_vm_eval2 = init_symbol (g_symbol, TSPECIAL, "*vm-eval2*");
cell_vm_eval_check_func = init_symbol (g_symbol, TSPECIAL, "*vm-eval-check-func*");
cell_vm_eval_define = init_symbol (g_symbol, TSPECIAL, "*vm-eval-define*");
cell_vm_eval_macro_expand_eval = init_symbol (g_symbol, TSPECIAL, "*vm:eval-macro-expand-eval*");
cell_vm_eval_macro_expand_expand = init_symbol (g_symbol, TSPECIAL, "*vm:eval-macro-expand-expand*");
cell_vm_eval_pmatch_car = init_symbol (g_symbol, TSPECIAL, "*vm-eval-pmatch-car*");
cell_vm_eval_pmatch_cdr = init_symbol (g_symbol, TSPECIAL, "*vm-eval-pmatch-cdr*");
cell_vm_eval_set_x = init_symbol (g_symbol, TSPECIAL, "*vm-eval-set!*");
cell_vm_evlis = init_symbol (g_symbol, TSPECIAL, "*vm-evlis*");
cell_vm_evlis2 = init_symbol (g_symbol, TSPECIAL, "*vm-evlis2*");
cell_vm_evlis3 = init_symbol (g_symbol, TSPECIAL, "*vm-evlis3*");
cell_vm_if = init_symbol (g_symbol, TSPECIAL, "*vm-if*");
cell_vm_if_expr = init_symbol (g_symbol, TSPECIAL, "*vm-if-expr*");
cell_vm_macro_expand = init_symbol (g_symbol, TSPECIAL, "core:macro-expand");
cell_vm_macro_expand_car = init_symbol (g_symbol, TSPECIAL, "*vm:core:macro-expand-car*");
cell_vm_macro_expand_cdr = init_symbol (g_symbol, TSPECIAL, "*vm:macro-expand-cdr*");
cell_vm_macro_expand_define = init_symbol (g_symbol, TSPECIAL, "*vm:core:macro-expand-define*");
cell_vm_macro_expand_define_macro = init_symbol (g_symbol, TSPECIAL, "*vm:core:macro-expand-define-macro*");
cell_vm_macro_expand_lambda = init_symbol (g_symbol, TSPECIAL, "*vm:core:macro-expand-lambda*");
cell_vm_macro_expand_set_x = init_symbol (g_symbol, TSPECIAL, "*vm:core:macro-expand-set!*");
cell_vm_return = init_symbol (g_symbol, TSPECIAL, "*vm-return*");
cell_symbol_lambda = init_symbol (g_symbol, TSYMBOL, "lambda");
cell_symbol_begin = init_symbol (g_symbol, TSYMBOL, "begin");
cell_symbol_if = init_symbol (g_symbol, TSYMBOL, "if");
cell_symbol_quote = init_symbol (g_symbol, TSYMBOL, "quote");
cell_symbol_define = init_symbol (g_symbol, TSYMBOL, "define");
cell_symbol_define_macro = init_symbol (g_symbol, TSYMBOL, "define-macro");
cell_symbol_quasiquote = init_symbol (g_symbol, TSYMBOL, "quasiquote");
cell_symbol_unquote = init_symbol (g_symbol, TSYMBOL, "unquote");
cell_symbol_unquote_splicing = init_symbol (g_symbol, TSYMBOL, "unquote-splicing");
cell_symbol_syntax = init_symbol (g_symbol, TSYMBOL, "syntax");
cell_symbol_quasisyntax = init_symbol (g_symbol, TSYMBOL, "quasisyntax");
cell_symbol_unsyntax = init_symbol (g_symbol, TSYMBOL, "unsyntax");
cell_symbol_unsyntax_splicing = init_symbol (g_symbol, TSYMBOL, "unsyntax-splicing");
cell_symbol_set_x = init_symbol (g_symbol, TSYMBOL, "set!");
cell_symbol_sc_expand = init_symbol (g_symbol, TSYMBOL, "sc-expand");
cell_symbol_macro_expand = init_symbol (g_symbol, TSYMBOL, "macro-expand");
cell_symbol_portable_macro_expand = init_symbol (g_symbol, TSYMBOL, "portable-macro-expand");
cell_symbol_sc_expander_alist = init_symbol (g_symbol, TSYMBOL, "*sc-expander-alist*");
cell_symbol_call_with_values = init_symbol (g_symbol, TSYMBOL, "call-with-values");
cell_symbol_call_with_current_continuation = init_symbol (g_symbol, TSYMBOL, "call-with-current-continuation");
cell_symbol_boot_module = init_symbol (g_symbol, TSYMBOL, "boot-module");
cell_symbol_current_module = init_symbol (g_symbol, TSYMBOL, "current-module");
cell_symbol_primitive_load = init_symbol (g_symbol, TSYMBOL, "primitive-load");
cell_symbol_car = init_symbol (g_symbol, TSYMBOL, "car");
cell_symbol_cdr = init_symbol (g_symbol, TSYMBOL, "cdr");
cell_symbol_not_a_number = init_symbol (g_symbol, TSYMBOL, "not-a-number");
cell_symbol_not_a_pair = init_symbol (g_symbol, TSYMBOL, "not-a-pair");
cell_symbol_system_error = init_symbol (g_symbol, TSYMBOL, "system-error");
cell_symbol_throw = init_symbol (g_symbol, TSYMBOL, "throw");
cell_symbol_unbound_variable = init_symbol (g_symbol, TSYMBOL, "unbound-variable");
cell_symbol_wrong_number_of_args = init_symbol (g_symbol, TSYMBOL, "wrong-number-of-args");
cell_symbol_wrong_type_arg = init_symbol (g_symbol, TSYMBOL, "wrong-type-arg");
cell_symbol_buckets = init_symbol (g_symbol, TSYMBOL, "buckets");
cell_symbol_builtin = init_symbol (g_symbol, TSYMBOL, "<builtin>");
cell_symbol_frame = init_symbol (g_symbol, TSYMBOL, "<frame>");
cell_symbol_hashq_table = init_symbol (g_symbol, TSYMBOL, "<hashq-table>");
cell_symbol_module = init_symbol (g_symbol, TSYMBOL, "<module>");
cell_symbol_procedure = init_symbol (g_symbol, TSYMBOL, "procedure");
cell_symbol_record_type = init_symbol (g_symbol, TSYMBOL, "<record-type>");
cell_symbol_size = init_symbol (g_symbol, TSYMBOL, "size");
cell_symbol_stack = init_symbol (g_symbol, TSYMBOL, "<stack>");
cell_symbol_argv = init_symbol (g_symbol, TSYMBOL, "%argv");
cell_symbol_mes_datadir = init_symbol (g_symbol, TSYMBOL, "%datadir");
cell_symbol_mes_version = init_symbol (g_symbol, TSYMBOL, "%version");
cell_symbol_internal_time_units_per_second = init_symbol (g_symbol, TSYMBOL, "internal-time-units-per-second");
cell_symbol_compiler = init_symbol (g_symbol, TSYMBOL, "%compiler");
cell_symbol_arch = init_symbol (g_symbol, TSYMBOL, "%arch");
cell_symbol_pmatch_car = init_symbol (g_symbol, TSYMBOL, "pmatch-car");
cell_symbol_pmatch_cdr = init_symbol (g_symbol, TSYMBOL, "pmatch-cdr");
cell_type_bytes = init_symbol (g_symbol, TSYMBOL, "<cell:bytes>");
cell_type_char = init_symbol (g_symbol, TSYMBOL, "<cell:char>");
cell_type_closure = init_symbol (g_symbol, TSYMBOL, "<cell:closure>");
cell_type_continuation = init_symbol (g_symbol, TSYMBOL, "<cell:continuation>");
cell_type_function = init_symbol (g_symbol, TSYMBOL, "<cell:function>");
cell_type_keyword = init_symbol (g_symbol, TSYMBOL, "<cell:keyword>");
cell_type_macro = init_symbol (g_symbol, TSYMBOL, "<cell:macro>");
cell_type_number = init_symbol (g_symbol, TSYMBOL, "<cell:number>");
cell_type_pair = init_symbol (g_symbol, TSYMBOL, "<cell:pair>");
cell_type_port = init_symbol (g_symbol, TSYMBOL, "<cell:port>");
cell_type_ref = init_symbol (g_symbol, TSYMBOL, "<cell:ref>");
cell_type_special = init_symbol (g_symbol, TSYMBOL, "<cell:special>");
cell_type_string = init_symbol (g_symbol, TSYMBOL, "<cell:string>");
cell_type_struct = init_symbol (g_symbol, TSYMBOL, "<cell:struct>");
cell_type_symbol = init_symbol (g_symbol, TSYMBOL, "<cell:symbol>");
cell_type_values = init_symbol (g_symbol, TSYMBOL, "<cell:values>");
cell_type_variable = init_symbol (g_symbol, TSYMBOL, "<cell:variable>");
cell_type_vector = init_symbol (g_symbol, TSYMBOL, "<cell:vector>");
cell_type_broken_heart = init_symbol (g_symbol, TSYMBOL, "<cell:broken-heart>");
cell_symbol_program = init_symbol (g_symbol, TSYMBOL, "%program");
cell_symbol_test = init_symbol (g_symbol, TSYMBOL, "%%test");
}
struct scm *
init_symbols () /*:((internal)) */
{
g_free = g_cells + M2_CELL_SIZE;
g_symbols = 0;
cell_nil = g_free;
init_symbols_ ();
g_symbol_max = g_symbol;
g_symbols = make_hash_table_ (500);
init_symbols_ ();
g_ports = cell_nil;
struct scm *a = cell_nil;
a = acons (cell_symbol_call_with_values, cell_symbol_call_with_values, a);
a = acons (cell_symbol_boot_module, cell_symbol_boot_module, a);
a = acons (cell_symbol_current_module, cell_symbol_current_module, a);
a = acons (cell_symbol_mes_version, make_string0 (MES_VERSION), a);
a = acons (cell_symbol_mes_datadir, make_string0 (g_datadir), a);
a = acons (cell_type_bytes, make_number (TBYTES), a);
a = acons (cell_type_char, make_number (TCHAR), a);
a = acons (cell_type_closure, make_number (TCLOSURE), a);
a = acons (cell_type_continuation, make_number (TCONTINUATION), a);
a = acons (cell_type_keyword, make_number (TKEYWORD), a);
a = acons (cell_type_macro, make_number (TMACRO), a);
a = acons (cell_type_number, make_number (TNUMBER), a);
a = acons (cell_type_pair, make_number (TPAIR), a);
a = acons (cell_type_port, make_number (TPORT), a);
a = acons (cell_type_ref, make_number (TREF), a);
a = acons (cell_type_special, make_number (TSPECIAL), a);
a = acons (cell_type_string, make_number (TSTRING), a);
a = acons (cell_type_struct, make_number (TSTRUCT), a);
a = acons (cell_type_symbol, make_number (TSYMBOL), a);
a = acons (cell_type_values, make_number (TVALUES), a);
a = acons (cell_type_variable, make_number (TVARIABLE), a);
a = acons (cell_type_vector, make_number (TVECTOR), a);
a = acons (cell_type_broken_heart, make_number (TBROKEN_HEART), a);
a = acons (cell_closure, a, a);
return a;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mes/lib.h"
#include "mes/mes.h"
#if __M2_PLANET__
#define M2_CELL_SIZE 12
// CONSTANT M2_CELL_SIZE 12
#else
#define M2_CELL_SIZE 1
// CONSTANT M2_CELL_SIZE 12
#endif
struct scm *
make_vector_ (long k, struct scm *e)
{
struct scm *x = alloc (1);
struct scm *v = alloc (k);
x->type = TVECTOR;
x->length = k;
x->vector = v;
long i;
for (i = 0; i < k; i = i + 1)
copy_cell (cell_ref (v, i), vector_entry (e));
return x;
}
struct scm *
make_vector (struct scm *x) /*:((arity . n)) */
{
struct scm *k = x->car;
assert_number ("make-vector", k);
long n = k->value;
struct scm *e = cell_unspecified;
if (x->cdr != cell_nil)
e = x->cdr->car;
return make_vector_ (n, e);
}
struct scm *
vector_length (struct scm *x)
{
assert_msg (x->type == TVECTOR, "x->type == TVECTOR");
return make_number (x->length);
}
struct scm *
vector_ref_ (struct scm *x, long i)
{
assert_msg (x->type == TVECTOR, "x->type == TVECTOR");
assert_msg (i < x->length, "i < x->length");
struct scm *e = cell_ref (x->vector, i);
if (e->type == TREF)
e = e->ref;
if (e->type == TCHAR)
e = make_char (e->value);
if (e->type == TNUMBER)
e = make_number (e->value);
return e;
}
struct scm *
vector_ref (struct scm *x, struct scm *i)
{
return vector_ref_ (x, i->value);
}
struct scm *
vector_entry (struct scm *x)
{
if (x->type != TCHAR && x->type != TNUMBER)
x = make_ref (x);
return x;
}
struct scm *
vector_set_x_ (struct scm *x, long i, struct scm *e)
{
assert_msg (x->type == TVECTOR, "x->type == TVECTOR");
assert_msg (i < x->length, "i < x->length");
copy_cell (cell_ref (x->vector, i), vector_entry (e));
return cell_unspecified;
}
struct scm *
vector_set_x (struct scm *x, struct scm *i, struct scm *e)
{
return vector_set_x_ (x, i->value, e);
}
struct scm *
list_to_vector (struct scm *x)
{
struct scm *v = make_vector_ (length__ (x), cell_unspecified);
struct scm *p = v->vector;
while (x != cell_nil)
{
copy_cell (p, vector_entry (car (x)));
p = p + M2_CELL_SIZE;
x = cdr (x);
}
return v;
}
struct scm *
vector_to_list (struct scm *v)
{
struct scm *x = cell_nil;
long i;
struct scm *e;
for (i = v->length; i; i = i - 1)
{
e = cell_ref (v->vector, i - 1);
if (e->type == TREF)
e = e->ref;
x = cons (e, x);
}
return x;
}
### GNU Mes --- Maxwell Equations of Software ### Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> ### ### This file is part of GNU Mes. ### ### Mes is free software# you can redistribute it and/or modify it ### under the terms of the GNU General Public License as published by ### the Free Software Foundation# either version 3 of the License, or (at ### your option) any later version. ### ### GNU Mes is distributed in the hope that it will be useful, but ### WITHOUT ANY WARRANTY# without even the implied warranty of ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ### GNU General Public License for more details. ### ### You should have received a copy of the GNU General Public License ### along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. # reduced instruction set: eax, ebx (some ecx for shift, edx for mul, div) # 193 instructions DEFINE add____$i32,%eax 05 DEFINE add____$i32,%ebx 81c3 DEFINE add____$i32,(%eax) 8100 DEFINE add____$i32,(%ebx) 8103 DEFINE add____$i32,0x32(%eax) 8180 DEFINE add____$i32,0x32(%ebp) 8185 DEFINE add____$i8,%eax 83c0 DEFINE add____$i8,%ebx 83c3 DEFINE add____$i8,%esp 83c4 DEFINE add____$i8,(%eax) 8300 DEFINE add____$i8,(%ebx) 8303 DEFINE add____$i8,0x32 8305 DEFINE add____$i8,0x32(%eax) 8380 DEFINE add____$i8,0x32(%ebp) 8385 DEFINE add____$i8,0x8(%eax) 8340 DEFINE add____$i8,0x8(%ebp) 8345 DEFINE add____%eax,%eax 01c0 DEFINE add____%ebp,%eax 01e8 DEFINE add____%ebx,%eax 01d8 DEFINE add____%ebx,%ebx 01db DEFINE addb___$i8,(%eax) 8000 DEFINE addb___$i8,(%ebx) 8003 DEFINE addw___$i8,(%eax) 668100 DEFINE addw___$i8,(%ebx) 668103 DEFINE and____$i32,%eax 25 DEFINE and____$i32,%ebx 81e3 DEFINE and____%ebx,%eax 21d8 DEFINE call32 e8 DEFINE call___*%eax ffd0 DEFINE call___*%ebx ffd3 DEFINE cltd 99 DEFINE cmp____$0x32,%eax 3d DEFINE cmp____$i32,%eax 3d DEFINE cmp____$i8,%eax 83f8 DEFINE cmp____$i8,%ebx 81fb DEFINE div___%ebx f7f3 DEFINE hlt f4 DEFINE idiv___%ebx f7fb DEFINE int cd DEFINE int____$0x80 cd80 DEFINE ja32 0f87 DEFINE jae32 0f83 DEFINE jb32 0f82 DEFINE jbe32 0f86 DEFINE je32 0f84 DEFINE je8 74 DEFINE jg32 0f8f DEFINE jge32 0f8d DEFINE jl32 0f8c DEFINE jle32 0f8e DEFINE jmp32 e9 DEFINE jmp____*%ebx ffe3 DEFINE jne32 0f85 DEFINE lahf 9f DEFINE lea____0x32(%ebp),%eax 8d85 DEFINE lea____0x8(%ebp),%eax 8d45 DEFINE leave c9 DEFINE mov____$i32,%eax b8 DEFINE mov____$i32,%ebx bb DEFINE mov____$i32,(%eax) c700 DEFINE mov____$i32,0x32 c705 DEFINE mov____$i32,0x8(%eax) c740 DEFINE mov____$i32,0x8(%ebp) c745 DEFINE mov____$i8,%al b0 DEFINE mov____$i8,%eax b0 DEFINE mov____%al,(%ebx) 8803 DEFINE mov____%al,0x8(%ebp) 8845 DEFINE mov____%ax,(%ebx) 668903 DEFINE mov____%ax,0x8(%ebp) 668945 DEFINE mov____%bx,%bx 6689db DEFINE mov____%cl,(%ebx) 8a08 DEFINE mov____%cx,(%ebx) 668b08 DEFINE mov____%dl,(%eax) 8810 DEFINE mov____%dl,0x8(%eax) 8850 DEFINE mov____%eax,%ebx 89c3 DEFINE mov____%eax,%ecx 89c1 DEFINE mov____%eax,(%ebx) 8903 DEFINE mov____%eax,0x32 a3 DEFINE mov____%eax,0x32(%ebp) 8985 DEFINE mov____%eax,0x8(%ebp) 8945 DEFINE mov____%ebp,%eax 89e8 DEFINE mov____%ebp,%ebx 89eb DEFINE mov____%ebx,%eax 89d8 DEFINE mov____%ebx,%ecx 89d9 DEFINE mov____%ebx,0x32 891d DEFINE mov____%ebx,0x32(%ebp) 899d DEFINE mov____%ebx,0x8(%ebp) 895d DEFINE mov____%ecx,(%ebx) 890b DEFINE mov____%edi,%ebp 89fd DEFINE mov____%edx,%eax 89d0 DEFINE mov____%edx,%ebx 89d3 DEFINE mov____%esp,%ebp 89e5 DEFINE mov____%esp,%edi 89e7 DEFINE mov____(%eax),%cl 8a08 DEFINE mov____(%eax),%cx 668b08 DEFINE mov____(%eax),%eax 8b00 DEFINE mov____(%eax),%ecx 8b08 DEFINE mov____(%ebx),%ebx 8b1b DEFINE mov____0x32(%eax),%eax 8b80 DEFINE mov____0x32(%eax),%ebx 8b98 DEFINE mov____0x32(%ebp),%eax 8b85 DEFINE mov____0x32(%ebp),%ebx 8b9d DEFINE mov____0x32,%eax a1 DEFINE mov____0x32,%ebx 8b1d DEFINE mov____0x8(%eax),%eax 8b40 DEFINE mov____0x8(%eax),%ebx 8b58 DEFINE mov____0x8(%ebp),%eax 8b45 DEFINE mov____0x8(%ebp),%ebp 8b6d DEFINE mov____0x8(%ebp),%ebx 8b5d DEFINE mov____0x8(%ebp),%ecx 8b4d DEFINE mov____0x8(%ebp),%edi 8b7d DEFINE mov____0x8(%ebp),%edx 8b55 DEFINE mov____0x8(%ebp),%esi 8b75 DEFINE mov____0x8(%ebp),%esp 8b65 DEFINE movb___%al,0x32 a2 DEFINE movb___%bl,0x32 881d DEFINE movsbl_%al,%eax 0fbec0 DEFINE movsbl_%bl,%ebx 0fbedb DEFINE movswl_%ax,%eax 0fbfc0 DEFINE movswl_%bx,%ebx 0fbfdb DEFINE movw___%ax,0x32 66a3 DEFINE movw___%bx,0x32 66891d DEFINE movzbl_%al,%eax 0fb6c0 DEFINE movzbl_%bl,%ebx 0fb6db DEFINE movzbl_(%eax),%eax 0fb600 DEFINE movzbl_(%ebx),%ebx 0fb61b DEFINE movzbl_0x32(%eax),%eax 0fb680 DEFINE movzbl_0x8(%eax),%eax 0fb640 DEFINE movzbl_0x8(%ebp),%eax 0fb645 DEFINE movzwl_%ax,%eax 0fb7c0 DEFINE movzwl_%bx,%ebx 0fb7db DEFINE movzwl_(%eax),%eax 0fb700 DEFINE movzwl_(%ebx),%ebx 0fb71b DEFINE movzwl_0x32(%eax),%eax 0fb780 DEFINE movzwl_0x32(%ebp),%eax 0fb785 DEFINE movzwl_0x8(%eax),%eax 0fb740 DEFINE mul____%ebx f7e3 DEFINE mul_____%ebx f7e3 DEFINE nop 90 DEFINE not____%eax f7d0 DEFINE not____%ebx f7d3 DEFINE or_____%ebx,%eax 09d8 DEFINE pop____%eax 58 DEFINE pop____%ebx 5b DEFINE pop____%edx 5a DEFINE push___$i32 68 DEFINE push___%eax 50 DEFINE push___%ebp 55 DEFINE push___%ebx 53 DEFINE push___%edx 52 DEFINE push___(%eax) ff30 DEFINE push___0x32(%ebp) ffb5 DEFINE push___0x8(%ebp) ff75 DEFINE ret c3 DEFINE sahf 9e DEFINE seta___%al 0f97c0 DEFINE seta___%bl 0f97c3 DEFINE setae__%al 0f93c0 DEFINE setae__%bl 0f93c3 DEFINE setb___%al 0f92c0 DEFINE setb___%bl 0f92c3 DEFINE setbe__%al 0f96c0 DEFINE setbe__%bl 0f96c3 DEFINE sete___%al 0f94c0 DEFINE sete___%bl 0f94c3 DEFINE setg___%al 0f9fc0 DEFINE setg___%bl 0f9fc3 DEFINE setge__%al 0f9dc0 DEFINE setge__%bl 0f9dc3 DEFINE setl___%al 0f9cc0 DEFINE setl___%bl 0f9cc3 DEFINE setle__%al 0f9ec0 DEFINE setle__%bl 0f9ec3 DEFINE setne__%al 0f95c0 DEFINE setne__%bl 0f95c3 DEFINE shl____$i8,%eax c1e0 DEFINE shl____$i8,%ebx c1e3 DEFINE shl____%cl,%eax d3e0 DEFINE shl____%cl,%ebx d3e3 DEFINE shr____%cl,%eax d3e8 DEFINE sub____$8,%esp 83ec DEFINE sub____$i32,%esp 81ec DEFINE sub____%al,%dl 28d0 DEFINE sub____%dl,%al 28c2 DEFINE sub____%ebx,%eax 29d8 DEFINE test___%al,%al 84c0 DEFINE test___%eax,%eax 85c0 DEFINE test___%ebx,%ebx 85db DEFINE xchg___%eax,%ebx 93 DEFINE xchg___%eax,(%esp) 870424 DEFINE xchg___%ebx,(%esp) 871c24 DEFINE xor____$i32,%eax 35 DEFINE xor____$i8,%ah 80f4 DEFINE xor____%eax,%eax 31c0 DEFINE xor____%ebx,%eax 31d8 DEFINE xor____%ebx,%ebx 31db DEFINE xor____%edx,%edx 31d2 # Enough for all of Mes + Mes C Libray when using all registers, i.e., # non-reduced instruction set #DEFINE add____$i32,%ecx 81c1 #DEFINE add____$i32,%edx 81c2 #DEFINE add____$i32,%esi 81c6 #DEFINE add____$i8,%ecx 83c1 #DEFINE add____$i8,%edx 83c2 #DEFINE add____$i8,%esi 83c6 #DEFINE add____$i8,(%ecx) 8301 #DEFINE add____$i8,(%edx) 8302 #DEFINE add____%ecx,%ebx 01cb #DEFINE add____%ecx,%ecx 01c9 #DEFINE add____%edx,%eax 01d0 #DEFINE add____%edx,%ecx 01d1 #DEFINE add____%edx,%edx 01d2 #DEFINE add____%esi,%edx 01f2 #DEFINE and____$i32,%ecx 81e1 #DEFINE and____$i32,%edx 81e2 #DEFINE and____%edx,%eax 21d0 #DEFINE and____(%edx),%eax 2302 #DEFINE cmp____%edx,%eax 39d0 #DEFINE idiv___%ecx f7f9 #DEFINE lea____0x32(%ebp),%edx 8d95 #DEFINE lea____0x8(%ebp),%edx 8d55 #DEFINE mov____$i32,%ecx b9 #DEFINE mov____$i32,%edx ba #DEFINE mov____$i32,%esi be #DEFINE mov____%al,(%edx) 8802 #DEFINE mov____%al,0x8(%edx) 8842 #DEFINE mov____%ax,(%edx) 668902 #DEFINE mov____%ax,0x32(%edx) 668982 #DEFINE mov____%ax,0x8(%edx) 668942 #DEFINE mov____%bl,(%ecx) 8819 #DEFINE mov____%eax,%edx 89c2 #DEFINE mov____%eax,%esi 89c6 #DEFINE mov____%eax,(%ecx) 8901 #DEFINE mov____%eax,(%edx) 8902 #DEFINE mov____%eax,0x32(%edx) 8982 #DEFINE mov____%eax,0x8(%edx) 8942 #DEFINE mov____%ebp,%ecx 89e9 #DEFINE mov____%ebp,%edx 89ea #DEFINE mov____%ebp,%esi 89ee #DEFINE mov____%ebx,(%ecx) 8919 #DEFINE mov____%ebx,0x8(%edx) 895a #DEFINE mov____%ecx,%eax 89c8 #DEFINE mov____%ecx,%ecx 89c9 #DEFINE mov____%ecx,%edx 89ca #DEFINE mov____%ecx,(%eax) 8908 #DEFINE mov____%ecx,(%edx) 890a #DEFINE mov____%ecx,0x32(%ebp) 898d #DEFINE mov____%ecx,0x8(%ebp) 894d #DEFINE mov____%edi,%ebx 89fb #DEFINE mov____%edx,%ecx 89d1 #DEFINE mov____%edx,(%eax) 8910 #DEFINE mov____%edx,0x32(%ebp) 8995 #DEFINE mov____%edx,0x8(%ebp) 8955 #DEFINE mov____%esi,%eax 89f0 #DEFINE mov____%esi,%ebx 89f3 #DEFINE mov____(%ecx),%ecx 8b09 #DEFINE mov____(%edx),%eax 8b02 #DEFINE mov____(%edx),%ecx 8b0a #DEFINE mov____(%edx),%edx 8b12 #DEFINE mov____0x32(%eax),%ecx 8b88 #DEFINE mov____0x32(%ebp),%ecx 8b8d #DEFINE mov____0x32(%ebp),%edx 8b95 #DEFINE mov____0x32,%ecx 8b0d #DEFINE mov____0x32,%edx 8b15 #DEFINE mov____0x8(%eax),%ecx 8b48 #DEFINE movsbl_%cl,%ecx 0fbec9 #DEFINE movsbl_%dl,%edx 0fbed2 #DEFINE movswl_%cx,%ecx 0fbfc9 #DEFINE movzbl_%cl,%ecx 0fb6c9 #DEFINE movzbl_%dl,%edx 0fb6d2 #DEFINE movzbl_(%eax),%edx 0fb610 #DEFINE movzbl_(%ecx),%ecx 0fb609 #DEFINE movzbl_(%edx),%edx 0fb612 #DEFINE movzwl_(%ecx),%ecx 0fb709 #DEFINE mul____%ecx f7e1 #DEFINE mul____%edi f7e7 #DEFINE mul____%edx f7e2 #DEFINE mul____%esi f7e6 #DEFINE or_____%ecx,%ebx 09cb #DEFINE or_____%edx,%eax 09d0 #DEFINE or_____(%edx),%eax 0b02 #DEFINE pop____%ecx 59 #DEFINE pop____%edi 5f #DEFINE push___%ecx 51 #DEFINE push___%edi 57 #DEFINE push___%esi 56 #DEFINE shl____$i8,%ecx c1e1 #DEFINE shl____$i8,%edx c1e2 #DEFINE shl____%cl,%ecx d3e1 #DEFINE sub____%eax,%edx 29c2 #DEFINE sub____%ecx,%ebx 29cb #DEFINE sub____%edx,%eax 29d0 #DEFINE sub____%edx,%ecx 29d1 #DEFINE xchg___%ebx,%ecx 87d9 #DEFINE xchg___%ecx,%edx 87ca #DEFINE xor____%ecx,%ecx 31c9 #DEFINE xor____%edx,%eax 31d0 # deprecated, remove after 0.18 DEFINE sub____%esp,$i32 81ec DEFINE sub____%esp,$i8 83ec DEFINE SYS_exit 01000000 DEFINE SYS_fork 02000000 DEFINE SYS_read 03000000 DEFINE SYS_rmdir 28000000 DEFINE SYS_write 04000000 DEFINE SYS_open 05000000 DEFINE SYS_close 06000000 DEFINE SYS_waitpid 07000000 DEFINE SYS_unlink 0a000000 DEFINE SYS_execve 0b000000 DEFINE SYS_chmod 0f000000 DEFINE SYS_lseek 13000000 DEFINE SYS_access 21000000 DEFINE SYS_brk 2d000000 DEFINE SYS_ioctl 36000000 DEFINE SYS_stat 6a000000 DEFINE SYS_fsync 76000000 DEFINE SYS_getcwd b7000000
### GNU Mes --- Maxwell Equations of Software
### Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
###
### This file is part of GNU Mes.
###
### GNU Mes is free software; you can redistribute it and/or modify it
### under the terms of the GNU General Public License as published by
### the Free Software Foundation; either version 3 of the License, or (at
### your option) any later version.
###
### GNU Mes is distributed in the hope that it will be useful, but
### WITHOUT ANY WARRANTY; without even the implied warranty of
### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
### GNU General Public License for more details.
###
### You should have received a copy of the GNU General Public License
### along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
:_start
push___%ebp
mov____%esp,%ebp
sub____$i32,%esp %0x1054
mov____$i8,%eax !0
mov____%eax,0x32 &GLOBAL___stdin
mov____$i8,%eax !1
mov____%eax,0x32 &GLOBAL___stdout
mov____$i8,%eax !2
mov____%eax,0x32 &GLOBAL___stderr
mov____%ebp,%eax
add____$i8,%eax !4
mov____(%eax),%eax
add____$i8,%eax !3
shl____$i8,%eax !0x02
add____%ebp,%eax
mov____%eax,0x32 &GLOBAL_environ
mov____%esp,%edi ; M2-Planet calling convention pushes forward
mov____%ebp,%eax ; argc
add____$i8,%eax !4
mov____(%eax),%eax
push___%eax
mov____%ebp,%eax ; argv
add____$i8,%eax !8
push___%eax
mov____0x32,%eax &GLOBAL_environ
push___%eax
mov____%edi,%ebp ; M2-Planet calling convention
call32 %FUNCTION_main
add____$i8,%esp !0x0
test___%eax,%eax
mov____%eax,%ebx
mov____$i32,%eax %1
int____$0x80
hlt
leave
ret
### Copyright (C) 2016 Jeremiah Orians ### Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org> ### This file is part of M2-Planet. ### ### M2-Planet is free software: you can redistribute it and/or modify ### it under the terms of the GNU General Public License as published by ### the Free Software Foundation, either version 3 of the License, or ### (at your option) any later version. ### ### M2-Planet is distributed in the hope that it will be useful, ### but WITHOUT ANY WARRANTY; without even the implied warranty of ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ### GNU General Public License for more details. ### ### You should have received a copy of the GNU General Public License ### along with M2-Planet. If not, see <http://www.gnu.org/licenses/>. ### stage0's hex2 format ### !<label> 1 byte relative ### $<label> 2 byte address ### @<label> 2 byte relative ### &<label> 4 byte address ### %<label> 4 byte relative ### if you wish to use this header, you need to add :ELF_end to the end of your ### M1 or hex2 files. ## ELF Header :ELF_base 7F 45 4C 46 # e_ident[EI_MAG0-3] ELF's magic number 01 # e_ident[EI_CLASS] Indicating 32 bit 01 # e_ident[EI_DATA] Indicating little endianness 01 # e_ident[EI_VERSION] Indicating original elf 03 # e_ident[EI_OSABI] Set at 3 because FreeBSD is strict 00 # e_ident[EI_ABIVERSION] See above 00 00 00 00 00 00 00 # e_ident[EI_PAD] 02 00 # e_type Indicating Executable 03 00 # e_machine Indicating 386 01 00 00 00 # e_version Indicating original elf &_start # e_entry Address of the entry point %ELF_program_headers>ELF_base # e_phoff Address of program header table %ELF_section_headers>ELF_base # e_shoff Address of section header table 00 00 00 00 # e_flags 34 00 # e_ehsize Indicating our 52 Byte header 20 00 # e_phentsize size of a program header table 01 00 # e_phnum number of entries in program table 28 00 # e_shentsize size of a section header table 05 00 # e_shnum number of entries in section table 02 00 # e_shstrndx index of the section names :ELF_program_headers :ELF_program_header__text 01 00 00 00 # ph_type: PT-LOAD = 1 00 00 00 00 # ph_offset &ELF_base # ph_vaddr &ELF_base # ph_physaddr %ELF_end>ELF_base # ph_filesz %ELF_end>ELF_base # ph_memsz 07 00 00 00 # ph_flags: PF-X|PF-W|PF-R = 7 01 00 00 00 # ph_alignment :ELF_text
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018,2021 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mes/lib-mini.h"
int main (int argc, char *argv[], char *envp[]);
int
_start ()
{
asm ("mov____$i8,%eax !0");
asm ("mov____%eax,0x32 &__stdin");
asm ("mov____$i8,%eax !1");
asm ("mov____%eax,0x32 &__stdout");
asm ("mov____$i8,%eax !2");
asm ("mov____%eax,0x32 &__stderr");
asm ("mov____%ebp,%eax");
asm ("add____$i8,%eax !4");
asm ("mov____(%eax),%eax");
asm ("add____$i8,%eax !3");
asm ("shl____$i8,%eax !0x02");
asm ("add____%ebp,%eax");
asm ("mov____%eax,0x32 &environ");
asm ("push___%eax");
asm ("mov____%ebp,%eax");
asm ("add____$i8,%eax !8");
asm ("push___%eax");
asm ("mov____%ebp,%eax");
asm ("add____$i8,%eax !4");
asm ("mov____(%eax),%eax");
asm ("push___%eax");
main ();
asm ("mov____%eax,%ebx");
asm ("mov____$i32,%eax %1");
asm ("int____$0x80");
asm ("hlt");
}
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2016,2017,2018,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_LIB_MINI_H #define __MES_LIB_MINI_H #if HAVE_CONFIG_H #include <mes/config.h> #endif // CONSTANT STDIN 0 #ifndef STDIN #define STDIN 0 #endif // CONSTANT STDOUT 1 #ifndef STDOUT #define STDOUT 1 #endif // CONSTANT STDERR 2 #ifndef STDERR #define STDERR 2 #endif extern char **environ; extern int __stdin; extern int __stdout; extern int __stderr; int eputs (char const *s); int puts (char const *s); int oputs (char const *s); #if SYSTEM_LIBC #include <sys/types.h> #include <unistd.h> #else //!SYSTEM_LIBC #ifndef _SIZE_T #define _SIZE_T #ifndef __SIZE_T #define __SIZE_T #ifndef __MES_SIZE_T #define __MES_SIZE_T #undef size_t typedef unsigned long size_t; #endif #endif #endif #ifndef _SSIZE_T #define _SSIZE_T #ifndef __SSIZE_T #define __SSIZE_T #ifndef __MES_SSIZE_T #define __MES_SSIZE_T #undef ssize_t #if __i386__ typedef int ssize_t; #else typedef long ssize_t; #endif #endif #endif #endif #ifndef __MES_ERRNO_T #define __MES_ERRNO_T 1 typedef int error_t; extern int errno; #endif // !__MES_ERRNO_T size_t strlen (char const *s); ssize_t _write (); ssize_t write (int filedes, void const *buffer, size_t size); #endif // !SYSTEM_LIBC #endif //__MES_LIB_MINI_H
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_STRING_H #define __MES_STRING_H 1 #if SYSTEM_LIBC #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #undef __MES_STRING_H #include_next <string.h> #else // ! SYSTEM_LIBC #include <sys/types.h> void *memchr (void const *block, int c, size_t size); void *memcpy (void *dest, void const *src, size_t n); void *memmove (void *dest, void const *src, size_t n); void *memset (void *s, int c, size_t n); void *memchr (void const *block, int c, size_t size); int memcmp (void const *s1, void const *s2, size_t n); void *memmem (void const *haystack, int haystack_len, void const *needle, int needle_len); char *strcat (char *dest, char const *src); char *strchr (char const *s, int c); int strcasecmp (char const *s1, char const *s2); int strcmp (char const *, char const *); char *strcpy (char *dest, char const *src); size_t strlen (char const *); char *strncpy (char *to, char const *from, size_t size); int strncmp (char const *, char const *, size_t); char *strrchr (char const *s, int c); char *strstr (char const *haystack, char const *needle); char *strlwr (char *string); char *strupr (char *string); char *strerror (int errnum); void perror (char const *message); #endif // ! SYSTEM_LIBC #endif // __MES_STRING_H
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib-mini.h>
#include <string.h>
int
oputs (char const *s)
{
int i = strlen (s);
write (__stdout, s, i);
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
void (*__call_at_exit) (void);
void
exit (int code)
{
if (__call_at_exit)
(*__call_at_exit) ();
_exit (code);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __MES_LIB_H
#define __MES_LIB_H
#include <mes/lib-mini.h>
char *cast_intp_to_charp (int const *i);
char *cast_long_to_charp (long i);
long cast_charp_to_long (char const *);
long cast_int_to_long (int i);
long cast_voidp_to_long (void const *);
int __mes_debug ();
void __ungetc_init ();
void __ungetc_clear (int filedes);
void __ungetc_set (int filedes, int c);
int __ungetc_p (int filedes);
double abtod (char const **p, int base);
long abtol (char const **p, int base);
char *dtoab (double number, int base, int signed_p);
char *itoa (int number);
char *ltoa (long number);
char *ltoab (long x, int base);
char *ntoab (long number, unsigned base, int signed_p);
char *ultoa (unsigned long number);
char *utoa (unsigned number);
int eputc (int c);
int fdgetc (int fd);
char * fdgets (char *s, int count, int fd);
int fdputc (int c, int fd);
int fdputs (char const *s, int fd);
int fdungetc (int c, int fd);
char *_getcwd (char *buffer, size_t size);
int ioctl3 (int filedes, size_t command, long data);
int isnumber (int c, int base);
int mes_open (char const *file_name, int flags, int mask);
int _open2 (char const *file_name, int flags);
int _open3 (char const *file_name, int flags, int mask);
int oputc (int c);
int oputs (char const *s);
char *search_path (char const *file_name);
ssize_t _read (int fd, void *buffer, size_t size);
void assert_msg (int check, char *msg);
extern char *__brk;
extern void (*__call_at_exit) (void);
#define __FILEDES_MAX 512
#if !SYSTEM_LIBC
void __assert_fail (char const *s, char const *file, unsigned line,
char const *function);
ssize_t __buffered_read (int filedes, void *buffer, size_t size);
size_t __buffered_read_clear (int filedes);
void _exit (int code);
long brk (void *addr);
#endif // !SYSTEM_LIBC
long __mesabi_imod (long a, long b);
long __mesabi_idiv (long a, long b);
unsigned long __mesabi_umod (unsigned long a, unsigned long b);
unsigned long __mesabi_udiv (unsigned long a, unsigned long b);
unsigned long __mesabi_uldiv (unsigned long a, unsigned long b,
unsigned long *remainder);
void *__memcpy (void *dest, void const *src, size_t n);
void *__memmove (void *dest, void const *src, size_t n);
void *__memset (void *s, int c, size_t n);
int __raise (int signal);
#endif //__MES_LIB_H
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
void
_exit ()
{
asm ("mov____$i32,%eax SYS_exit");
asm ("mov____0x8(%ebp),%ebx !8");
asm ("int____$0x80");
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
void
_write ()
{
asm ("mov____$i32,%eax SYS_write");
asm ("mov____0x8(%ebp),%ebx !8");
asm ("mov____0x8(%ebp),%ecx !12");
asm ("mov____0x8(%ebp),%edx !16");
asm ("int____$0x80");
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
int
puts (char const *s)
{
oputs (s);
return oputs ("\n");
}
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017,2018,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_ERRNO_H #define __MES_ERRNO_H 1 #if SYSTEM_LIBC #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #undef __MES_ERRNO_H #include_next <errno.h> #else // ! SYSTEM_LIBC #ifndef __MES_ERRNO_T #define __MES_ERRNO_T 1 typedef int error_t; #endif // !__MES_ERRNO_T extern int errno; #define ENOENT 2 #define EINTR 4 #define EIO 5 #define ENXIO 6 #define E2BIG 7 #define ENOEXEC 8 #define EBADF 9 #define ECHILD 10 #define EAGAIN 11 #define ENOMEM 12 #define EACCES 13 #define EEXIST 17 #define EXDEV 18 #define ENOTDIR 20 #define EISDIR 21 #define EINVAL 22 #define EMFILE 24 #define ENOSPC 28 #define ESPIPE 29 #define EPIPE 32 #define ERANGE 34 #define ENAMETOOLONG 36 #define ENOSYS 38 #define ELOOP 40 #if !__MESC__ //extern char const *const sys_errlist[]; extern char *sys_errlist[]; extern int sys_nerr; #endif // !__MESC__ #endif // ! SYSTEM_LIBC #endif // __MES_ERRNO_H
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/x86/syscall.h>
static long
__sys_call_internal (long sys_call)
{
asm ("mov____0x8(%ebp),%eax !8");
asm ("int____$0x80");
}
static long
__sys_call2_internal (long sys_call, long one, long two)
{
asm ("mov____0x8(%ebp),%eax !8");
asm ("mov____0x8(%ebp),%ebx !12");
asm ("mov____0x8(%ebp),%ecx !16");
asm ("int____$0x80");
}
/* Return < 0 on error (errno-like value from kernel), or 0 on success */
int
__raise (int signum)
{
int pid = (int) __sys_call_internal (SYS_getpid);
if (pid < 0)
return pid;
else
return (int) __sys_call2_internal (SYS_kill, pid, signum);
}
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_CTYPE_H #define __MES_CTYPE_H 1 #if SYSTEM_LIBC #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #undef __MES_CTYPE_H #include_next <ctype.h> #else // ! SYSTEM_LIBC #include <endian.h> int isalnum (int c); int isalpha (int c); int isascii (int c); int iscntrl (int c); int isdigit (int c); int isgraph (int c); int islower (int c); int isnumber (int c, int base); int isprint (int c); int ispunct (int c); int isspace (int c); int isupper (int c); int isxdigit (int c); int tolower (int c); int toupper (int c); #endif // ! SYSTEM_LIBC #endif // __MES_CTYPE_H
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#undef cast_intp_to_charp
#undef cast_long_to_charp
#undef cast_charp_to_long
#undef cast_int_to_long
#undef cast_voidp_to_long
char*
cast_intp_to_charp (int const *i)
{
return (char*)i;
}
char*
cast_long_to_charp (long i)
{
return (char*)i;
}
long
cast_charp_to_long (char const *i)
{
return (long)i;
}
long
cast_int_to_long (int i)
{
return (long)i;
}
long
cast_voidp_to_long (void const *i)
{
return (long)i;
}
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_LIMITS_H #define __MES_LIMITS_H 1 #if SYSTEM_LIBC #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #undef __MES_LIMITS_H #include_next <limits.h> #else // ! SYSTEM_LIBC #include <stdint.h> #define MB_CUR_MAX 1 #define NAME_MAX 255 #define PATH_MAX 512 #define _POSIX_OPEN_MAX 16 #endif // ! SYSTEM_LIBC #endif // __MES_LIMITS_H
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_STDLIB_H #define __MES_STDLIB_H 1 #ifndef __MES_COMPARISON_FN_T #define __MES_COMPARISON_FN_T typedef int (*comparison_fn_t) (void const *, void const *); #endif #if SYSTEM_LIBC #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #undef __MES_STDLIB_H #include_next <stdlib.h> #else // ! SYSTEM_LIBC #include <sys/types.h> #include <alloca.h> void abort (void); double atof (char const *s); int atoi (char const *s); int atexit (void (*function) (void)); void *calloc (size_t nmemb, size_t size); void _exit (int status); void exit (int status); void free (void *ptr); char *getenv (char const *s); int setenv (char const *s, char const *v, int overwrite_p); void unsetenv (char const *name); void *malloc (size_t); void qsort (void *base, size_t nmemb, size_t size, int (*compar) (void const *, void const *)); int rand (void); void *realloc (void *p, size_t size); double strtod (char const *string, char **tailptr); float strtof (char const *string, char **tailptr); long double strtold (char const *string, char **tailptr); long strtol (char const *string, char **tailptr, int base); long long strtoll (char const *string, char **tailptr, int base); unsigned long strtoul (char const *string, char **tailptr, int base); unsigned long long strtoull (char const *string, char **tailptr, int base); #define EXIT_FAILURE 1 #define EXIT_SUCCESS 0 void *bsearch (void const *key, void const *array, size_t count, size_t size, comparison_fn_t compare); #endif // ! SYSTEM_LIBC #endif // __MES_STDLIB_H
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __MES_SYS_RESOURCE_H
#define __MES_SYS_RESOURCE_H 1
#if SYSTEM_LIBC
#undef __MES_SYS_RESOURCE_H
#include_next <sys/resource.h>
#else // ! SYSTEM_LIBC
#include <sys/time.h>
struct rusage
{
struct timeval ru_utime;
struct timeval ru_stime;
long int ru_maxrss;
long int ru_ixrss;
long int ru_idrss;
long int ru_isrss;
long int ru_minflt;
long int ru_majflt;
long int ru_nswap;
long int ru_inblock;
long int ru_oublock;
long int ru_msgsnd;
long int ru_msgrcv;
long int ru_nsignals;
long int ru_nvcsw;
long int ru_nivcsw;
};
#define RUSAGE_SELF 0
#define RUSAGE_CHILDREN -1
#define RLIMIT_NOFILE 1024
#define OPEN_MAX RLIMIT_NOFILE
int getrusage (int processes, struct rusage *rusage);
#endif // ! SYSTEM_LIBC
#endif // __MES_SYS_RESOURCE_H
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_UNISTD_H #define __MES_UNISTD_H 1 #if SYSTEM_LIBC #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #undef __MES_UNISTD_H #include_next <unistd.h> #else // ! SYSTEM_LIBC #if defined (BOOTSTRAP_WITH_POSIX) #define _POSIX_VERSION 199009L #endif #include <sys/types.h> #ifndef NULL #define NULL 0 #endif #ifndef STDIN_FILENO #define STDIN_FILENO 0 #define STDOUT_FILENO 1 #define STDERR_FILENO 2 #endif // STDIN_FILENO #ifndef STDIN_FILE_NO #define STDIN_FILE_NO 0 #define STDOUT_FILE_NO 1 #define STDERR_FILE_NO 2 #endif // STDIN_FILE_NO #ifndef R_OK #define F_OK 0 #define X_OK 1 #define W_OK 2 #define R_OK 4 #endif int access (char const *s, int mode); unsigned int alarm (unsigned int seconds); int close (int fd); int execv (char const *file_name, char *const argv[]); int execl (char const *file_name, char const *arg, ...); int execlp (char const *file_name, char const *arg, ...); int execve (char const *file, char *const argv[], char *const env[]); int execvp (char const *file, char *const argv[]); int fork (void); int fsync (int filedes); char *getcwd (char *buf, size_t size); uid_t getuid (void); gid_t getgid (void); int setgid (gid_t newgid); int setuid (uid_t newuid); uid_t geteuid (void); gid_t getegid (void); pid_t getpgrp (void); pid_t getpid (void); pid_t getppid (void); int getpgid (pid_t pid); int isatty (int fd); int link (char const *old_name, char const *new_name); off_t lseek (int fd, off_t offset, int whence); ssize_t read (int fd, void *buffer, size_t size); ssize_t readlink (char const *file_name, char *buffer, size_t size); #if __SBRK_CHAR_PTRDIFF /* xmalloc in binutils <= 2.10.1 uses this old prototype */ char *sbrk (ptrdiff_t delta); #else void *sbrk (intptr_t delta); #endif int symlink (char const *old_name, char const *new_name); int unlink (char const *file_name); ssize_t write (int filedes, void const *buffer, size_t size); #endif // ! SYSTEM_LIBC #endif // __MES_UNISTD_H
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
char *
ltoab (long x, int base)
{
return ntoab (x, base, 1);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <stdlib.h>
#if SYSTEM_LIBC
#include <fcntl.h>
#include <stdarg.h>
// The Mes C Library defines and initializes these in crt1
int __stdin = STDIN;
int __stdout = STDOUT;
int __stderr = STDERR;
int
mes_open (char const *file_name, int flags, int mask)
{
int filedes = open (file_name, flags, mask);
if (filedes > 2)
__ungetc_clear (filedes);
return filedes;
}
#else // !SYSTEM_LIBC
int
mes_open (char const *file_name, int flags, int mask)
{
return _open3 (file_name, flags, mask);
}
#endif // !SYSTEM_LIBC
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __MES_ASSERT_H
#define __MES_ASSERT_H 1
#if SYSTEM_LIBC
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#undef __MES_ASSERT_H
#include_next <assert.h>
#else // ! SYSTEM_LIBC
#define assert(x) ((x) ? (void)0 : __assert_fail (#x, 0, 0, 0))
void __assert_fail (char const *s, char const *file, unsigned line,
char const *function);
#endif // ! SYSTEM_LIBC
#endif // __MES_ASSERT_H
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
int
oputc (int c)
{
return fdputc (c, __stdout);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
char *
ultoa (unsigned long x)
{
return ntoab (x, 10, 1);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
char *
utoa (unsigned x)
{
return ntoab (x, 10, 0);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
ssize_t
write (int filedes, void const *buffer, size_t size)
{
size_t skip = __buffered_read_clear (filedes);
if (skip)
lseek (filedes, -skip, SEEK_CUR);
int r = _write (filedes, buffer, size);
if (r < 0)
{
errno = -r;
r = -1;
}
else
errno = 0;
return r;
}
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2016,2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_STDIO_H #define __MES_STDIO_H 1 #include <mes/lib.h> #if SYSTEM_LIBC #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #undef __MES_STDIO_H #include_next <stdio.h> #else // ! SYSTEM_LIBC #ifndef _IOFBF #define _IOFBF 0 /* Fully buffered. */ #define _IOLBF 1 /* Line buffered. */ #define _IONBF 2 /* No buffering. */ #endif #ifndef BUFSIZ #define BUFSIZ 256 #endif #ifndef L_tmpnam #define L_tmpnam 100 #endif #include <sys/types.h> #define stdin (FILE*)0 #define stdout (FILE*)1 #define stderr (FILE*)2 #define SEEK_SET 0 #define SEEK_CUR 1 #define SEEK_END 2 FILE *fdopen (int fd, char const *mode); FILE *fopen (char const *file_name, char const *mode); int eputc (int c); int eputs (char const *s); int fclose (FILE * stream); int feof (FILE * stream); int ferror (FILE * stream); int fflush (FILE * stream); int fgetc (FILE * stream); char *fgets (char *s, int size, FILE * stream); int fprintf (FILE * stream, char const *format, ...); int fpurge (FILE * stream); int fputc (int c, FILE * stream); int fputs (char const *s, FILE * stream); int fscanf (FILE * stream, char const *template, ...); int fseek (FILE * stream, long offset, int whence); int getc (FILE * stream); int getchar (void); char *getlogin (void); int printf (char const *format, ...); int putc (int c, FILE * stream); int putchar (int c); int puts (char const *s); int remove (char const *file_name); int setvbuf (FILE * stream, char *buf, int mode, size_t size); int snprintf (char *str, size_t size, char const *format, ...); int sprintf (char *str, char const *format, ...); int sscanf (char const *str, char const *format, ...); int ungetc (int c, FILE * stream); long ftell (FILE * stream); size_t fread (void *ptr, size_t size, size_t count, FILE * stream); size_t freadahead (FILE * fp); size_t fwrite (void const *ptr, size_t size, size_t count, FILE * stream); #endif // ! SYSTEM_LIBC #endif // __MES_STDIO_H
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <stdio.h>
#include <sys/types.h>
#if !__MESC__ /* FIXME: We want bin/mes-mescc's x86-linux sha256sum to stay the same. */
off_t
_lseek (int filedes, off_t offset, int whence)
{
return _sys_call3 (SYS_lseek, (int) filedes, (long) offset, (int) whence);
}
#endif
off_t
lseek (int filedes, off_t offset, int whence)
{
#if !__MESC__ /* FIXME: We want bin/mes-mescc's x86-linux sha256sum to stay the same. */
if (_lseek (filedes, 0, SEEK_CUR) == -1)
return -1;
#endif
size_t skip = __buffered_read_clear (filedes);
if (whence == SEEK_CUR)
offset -= skip;
return _sys_call3 (SYS_lseek, (int) filedes, (long) offset, (int) whence);
}
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_SYSCALL_H #define __MES_SYSCALL_H long _sys_call (long sys_call); long _sys_call1 (long sys_call, long one); long _sys_call2 (long sys_call, long one, long two); long _sys_call3 (long sys_call, long one, long two, long three); long _sys_call4 (long sys_call, long one, long two, long three, long four); long _sys_call6 (long sys_call, long one, long two, long three, long four, long five, long six); #endif //__MES_SYSCALL_H
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_SYS_TYPES_H #define __MES_SYS_TYPES_H 1 #if SYSTEM_LIBC #undef __MES_SYS_TYPES_H #include_next <sys/types.h> #else // ! SYSTEM_LIBC #include <endian.h> #ifndef __MESCCLIB__ #define __MESCCLIB__ 15 #endif #ifndef EOF #define EOF -1 #endif #ifndef NULL #define NULL 0 #endif #ifndef __MES_CLOCK_T #define __MES_CLOCK_T #undef clock_t typedef long clock_t; #endif #ifndef __MES_DEV_T #define __MES_DEV_T #undef dev_t typedef long dev_t; #endif #if !defined (__MES_FILE_T) && ! defined (_FILE_T) #define __MES_FILE_T #define _FILE_T typedef long FILE; #endif #ifndef __MES_GID_T #define __MES_GID_T #undef gid_t typedef unsigned gid_t; #endif #ifndef __MES_INO_T #define __MES_INO_T #undef ino_t typedef unsigned long ino_t; #endif #if __SIZEOF_LONG_LONG__ == 8 #ifndef __MES_INO64_T #define __MES_INO64_T #undef ino64_t typedef unsigned long long ino64_t; #endif #endif // __SIZEOF_LONG_LONG__ == 8 #if !defined (__MES_INTPTR_T) && !defined (__intptr_t_defined) #define __MES_INTPTR_T #define __intptr_t_defined #undef intptr_t typedef long intptr_t; #undef uintptr_t typedef unsigned long uintptr_t; #endif #ifndef __MES_OFF_T #define __MES_OFF_T #undef off_t typedef long off_t; #endif #if __SIZEOF_LONG_LONG__ == 8 #ifndef __MES_OFF64_T #define __MES_OFF64_T #undef off64_t typedef unsigned long long off64_t; #endif #endif // __SIZEOF_LONG_LONG__ == 8 #ifndef __MES_PID_T #define __MES_PID_T #undef pid_t typedef int pid_t; #endif #ifndef __PTRDIFF_T #define __PTRDIFF_T #ifndef __MES_PTRDIFF_T #define __MES_PTRDIFF_T #undef ptrdiff_t typedef long ptrdiff_t; #endif #endif #ifndef __MES_SIGVAL_T #define __MES_SIGVAL_T #undef clock_t typedef long sigval_t; #endif #ifndef __SIZE_T #define __SIZE_T #ifndef __MES_SIZE_T #define __MES_SIZE_T #undef size_t typedef unsigned long size_t; #endif #endif #ifndef __MES_SSIZE_T #define __MES_SSIZE_T #undef ssize_t typedef long ssize_t; #endif #ifndef __MES_UID_T #define __MES_UID_T #undef uid_t typedef unsigned uid_t; #endif #ifndef __WCHAR_T #define __WCHAR_T #ifndef __MES_WCHAR_T #define __MES_WCHAR_T #undef wchar_t typedef int wchar_t; #endif #endif #endif // ! SYSTEM_LIBC #endif // __MES_SYS_TYPES_H
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <stdlib.h>
#include <string.h>
#if !__MESC__
#define __READ_BUFFER_MAX 128
int __read_buffer_max;
#else /* FIXME: We want bin/mes-mescc's x86-linux sha256sum to stay the same. */
#define __READ_BUFFER_MAX 100
#define __read_buffer_max 100
#endif
struct __read_buffer
{
ssize_t size;
char string[__READ_BUFFER_MAX];
};
struct __read_buffer *__read_cache = 0;
void
__buffered_read_init (int filedes)
{
if (!__read_cache)
{
__read_cache = (struct __read_buffer *) malloc (sizeof (struct __read_buffer) * __FILEDES_MAX);
#if !__MESC__
__read_buffer_max = __READ_BUFFER_MAX;
char *p = getenv ("MES_READ_BUFFER");
if (p)
{
__read_buffer_max = atoi (p);
if (__read_buffer_max < 0)
__read_buffer_max = 0;
if (__read_buffer_max > __READ_BUFFER_MAX)
__read_buffer_max = __READ_BUFFER_MAX;
}
#endif
}
}
size_t
__buffered_read_clear (int filedes)
{
__buffered_read_init (filedes);
size_t size = __read_cache[filedes].size;
__read_cache[filedes].size = 0;
return size;
}
ssize_t
__buffered_read (int filedes, void *buffer, size_t size)
{
size_t todo = size;
__buffered_read_init (filedes);
struct __read_buffer *cache = &__read_cache[filedes];
char *p = buffer;
if (!cache->size && size > __read_buffer_max)
return _read (filedes, buffer, size);
while (cache->size > 0 && todo)
{
todo--;
*p++ = cache->string[__read_buffer_max - cache->size--];
}
if (todo)
{
#if !__MESC__
if (todo > __read_buffer_max)
return size - todo + _read (filedes, p, todo);
if (__mes_debug () > 4)
{
eputs ("__buffered_read: ");
eputs (itoa (__read_buffer_max));
eputs ("\n");
}
#endif
ssize_t bytes = _read (filedes, cache->string, __read_buffer_max);
if (bytes < 0)
return -1;
if (bytes)
{
cache->size = bytes;
if (bytes < __read_buffer_max)
memmove (cache->string + __read_buffer_max - bytes, cache->string, bytes);
return size - todo + __buffered_read (filedes, p, todo);
}
}
return size - todo;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mes/lib.h"
#include <stdlib.h>
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
int
__mes_debug ()
{
static int __mes_debug = -1;
if (__mes_debug == -1)
{
char *p = getenv ("MES_DEBUG");
__mes_debug = p ? MAX (atoi (p), 1) : 0;
}
return __mes_debug;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <unistd.h>
int
execv (char const *file_name, char *const argv[])
{
return execve (file_name, argv, environ);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2020,2021 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <limits.h>
#include <sys/types.h>
#include <mes/lib.h>
char *__getcwd_buf;
char *
getcwd (char *buffer, size_t size)
{
static char buf[PATH_MAX];
if (buffer == 0)
buffer = buf;
return _getcwd (buffer, size);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <string.h>
#include <termio.h>
#if !SYSTEM_LIBC
typedef unsigned char cc_t;
typedef unsigned int speed_t;
typedef unsigned int tcflag_t;
// Use termio, termios2?
#define NCCS 19
struct termios
{
tcflag_t c_iflag;
tcflag_t c_oflag;
tcflag_t c_cflag;
tcflag_t c_lflag;
cc_t c_line;
cc_t c_cc[NCCS];
};
#endif
struct ktermios
{
tcflag_t c_iflag;
tcflag_t c_oflag;
tcflag_t c_cflag;
tcflag_t c_lflag;
cc_t c_line;
cc_t c_cc[NCCS];
speed_t c_ispeed;
speed_t c_ospeed;
};
int
__tcgetattr (int filedes, struct termios *termios_p)
{
struct ktermios kernel_termios;
int r = ioctl3 (filedes, TCGETS, (long) &kernel_termios);
termios_p->c_iflag = kernel_termios.c_iflag;
termios_p->c_oflag = kernel_termios.c_oflag;
termios_p->c_cflag = kernel_termios.c_cflag;
termios_p->c_lflag = kernel_termios.c_lflag;
termios_p->c_line = kernel_termios.c_line;
#if 0
termios_p->c_ispeed = kernel_termios.c_ispeed;
termios_p->c_ospeed = kernel_termios.c_ospeed;
#endif
memcpy (&termios_p->c_cc[0], &kernel_termios.c_cc[0], NCCS * sizeof (cc_t));
return r;
}
int
isatty (int filedes)
{
struct termios term;
return __tcgetattr (filedes, &term) == 0;
}
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_SYS_IOCTL_H #define __MES_SYS_IOCTL_H 1 #if SYSTEM_LIBC #undef __MES_SYS_IOCTL_H #include_next <sys/ioctl.h> #else // ! SYSTEM_LIBC #define TCGETS 0x5401 #define TCGETA 0x5405 int ioctl (int fd, unsigned long request, ...); int ioctl3 (int fd, unsigned long request, long data); #endif // ! SYSTEM_LIBC #endif // __MES_SYS_IOCTL_H
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __MES_TERMIO_H
#define __MES_TERMIO_H 1
#if SYSTEM_LIBC
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#undef __MES_TERMIO_H
#include_next <termio.h>
#else // ! SYSTEM_LIBC
#define TIOCGWINSZ 0x5413
#define TCGETA 0x5405
#define TCSETAW 0x5407
#define VTIME 5
#define VMIN 6
#define ISIG 0000001
#define ICANON 0000002
#define ECHO 0000010
#define ECHOK 0000040
#define ECHONL 0000100
#define ISTRIP 0000040
#define INLCR 0000100
#define ICRNL 0000400
#define CS8 0000060
#define PARENB 0000400
struct winsize
{
unsigned short ws_row;
unsigned short ws_col;
unsigned short ws_xpixel;
unsigned short ws_ypixel;
};
struct termio
{
unsigned short c_iflag;
unsigned short c_oflag;
unsigned short c_cflag;
unsigned short c_lflag;
unsigned char c_line;
unsigned char c_cc[8];
};
#endif // ! SYSTEM_LIBC
#endif // __MES_TERMIO_H
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <fcntl.h>
#include <stdarg.h>
int
_open2 (char const *file_name, int flags)
{
int mask = 0777;
return _open3 (file_name, flags, mask);
}
int
open (char const *file_name, int flags, ...)
{
if (flags & O_CREAT)
{
va_list ap;
va_start (ap, flags);
int mask = va_arg (ap, int);
int r = _open3 (file_name, flags, mask);
va_end (ap);
return r;
}
else
return _open2 (file_name, flags);
}
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_FCNTL_H #define __MES_FCNTL_H 1 #if SYSTEM_LIBC #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #undef __MES_FCNTL_H #include_next <fcntl.h> #else // ! SYSTEM_LIBC // *INDENT-OFF* #if __linux__ #define O_RDONLY 0 #define O_WRONLY 1 #define O_RDWR 2 #define O_CREAT 0x40 #define O_EXCL 0x80 #define O_TRUNC 0x200 #define O_APPEND 0x400 #ifdef __arm__ #define O_DIRECTORY 0x4000 /*#define O_DIRECT 0x10000*/ #else #define O_DIRECTORY 0x10000 #endif #elif __GNU__ #define O_RDONLY 1 #define O_WRONLY 2 #define O_RDWR 3 #define O_CREAT 0x10 #define O_APPEND 0x100 #define O_TRUNC 0x10000 #else #error platform not supported #endif // *INDENT-ON* #define FD_CLOEXEC 1 #define F_DUPFD 0 #define F_GETFD 1 #define F_SETFD 2 #define F_GETFL 3 #define F_SETFL 4 #define creat(file_name, mode) open (file_name, O_WRONLY | O_CREAT | O_TRUNC, mode) int dup (int old); int dup2 (int old, int new); int fcntl (int filedes, int command, ...); int open (char const *s, int flags, ...); #endif // ! SYSTEM_LIBC #endif // __MES_FCNTL_H
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_STDARG_H #define __MES_STDARG_H 1 #if SYSTEM_LIBC #undef __MES_STDARG_H #include_next <stdarg.h> #define va_arg8(ap, type) va_arg(ap, type) #else // ! SYSTEM_LIBC #include <sys/types.h> #if __GNUC__ && __x86_64__ #define __FOO_VARARGS 1 #endif typedef char *va_list; #define va_start(ap, last) (void)((ap) = (char*)(&(last) + 1)) #define va_arg(ap, type) (type)(((long*)((ap) = ((ap) + sizeof (void*))))[-1]) #define va_align(ap, alignment) ((char*)((((unsigned long) (ap)) + (alignment) - 1) &~ ((alignment) - 1))) #define va_arg8(ap, type) (type)(((double*)((ap) = (va_align((ap), 8) + sizeof(double))))[-1]) #define va_end(ap) (void)((ap) = 0) #define va_copy(dest, src) dest = src int vexec (char const *file_name, va_list ap); int vfprintf (FILE * stream, char const *template, va_list ap); int vfscanf (FILE * stream, char const *template, va_list ap); int vprintf (char const *format, va_list ap); int vsprintf (char *str, char const *format, va_list ap); int vsnprintf (char *str, size_t size, char const *format, va_list ap); int vsscanf (char const *s, char const *template, va_list ap); #endif // ! SYSTEM_LIBC #endif // __MES_STDARG_H
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <fcntl.h>
ssize_t
read (int filedes, void *buffer, size_t size)
{
ssize_t bytes = __buffered_read (filedes, buffer, size);
if (__mes_debug () > 4)
{
if (bytes == 1)
{
eputs ("read fd=");
eputs (itoa ((int) filedes));
eputs (" c=");
eputc (*(char *) buffer);
eputs ("\n");
}
else
{
eputs ("read fd=");
eputs (itoa ((int) filedes));
eputs (" bytes=");
eputs (itoa (bytes));
eputs ("\n");
}
}
return bytes;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sys/wait.h>
pid_t
wait (int *status_ptr)
{
return waitpid (-1, status_ptr, 0);
}
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_SYS_WAIT_H #define __MES_SYS_WAIT_H 1 #if SYSTEM_LIBC #undef __MES_SYS_WAIT_H #include_next <sys/wait.h> #else // ! SYSTEM_LIBC #ifndef __MES_PID_T #define __MES_PID_T typedef int pid_t; #endif #define WNOHANG 1 #define W_EXITCODE(status, signal) ((status) << 8 | (signal)) pid_t waitpid (pid_t pid, int *status_ptr, int options); pid_t wait (int *status_ptr); #endif // ! SYSTEM_LIBC #endif // __MES_SYS_WAIT_H
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
int
fgetc (FILE * stream)
{
return fdgetc ((long) stream);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
int
fputc (int c, FILE * stream)
{
return fdputc (c, (long) stream);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
int
fputs (char const *s, FILE * stream)
{
return fdputs (s, (long) stream);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
int
getc (FILE * stream)
{
return fdgetc ((long) stream);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
int
putc (int c, FILE * stream)
{
return fdputc (c, (long) stream);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
int
ungetc (int c, FILE * stream)
{
return fdungetc (c, (long) stream);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
void *
memchr (void const *block, int c, size_t size)
{
char const *p = block;
while (size != 0)
{
size = size - 1;
if (c == p[0])
return (void*) p;
p = p + 1;
}
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
void *
memmove (void *dest, void const *src, size_t n)
{
if (dest < src)
return memcpy (dest, src, n);
char *p = dest + n;
char const *q = src + n;
while (n--)
*--p = *--q;
return dest;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <signal.h>
#include <unistd.h>
int
raise (int signum)
{
kill (getpid (), signum);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __MES_SIGNAL_H
#define __MES_SIGNAL_H 1
#if SYSTEM_LIBC
#undef __MES_SIGNAL_H
#include_next <signal.h>
#else //! SYSTEM_LIBC
#define _NSIG 64
#define _SIGSET_NITEMS (_NSIG / (8 * sizeof(unsigned long)))
typedef struct {
unsigned long items[_SIGSET_NITEMS];
} sigset_t;
typedef long stack_t;
#include <sys/types.h>
// *INDENT-OFF*
#define NSIG 30
#define SIGHUP 1
#define SIGINT 2
#define SIGQUIT 3
#define SIGILL 4
#define SIGTRAP 5
#define SIGABRT 6
#define SIGIOT 6
#define SIGBUS 7
#define SIGFPE 8
#define SIGKILL 9
#define SIGUSR1 10
#define SIGSEGV 11
#define SIGUSR2 12
#define SIGPIPE 13
#define SIGALRM 14
#define SIGTERM 15
#define SIGSTKFLT 16
#define SIGCHLD 17
#define SIGCONT 18
#define SIGSTOP 19
#define SIGTSTP 20
#define SIGTTIN 21
#define SIGTTOU 22
#define SIGURG 23
#define SIGXCPU 24
#define SIGXFSZ 25
#define SIGVTALRM 26
#define SIGPROF 27
#define SIGWINCH 28
#define SIGIO 29
#define SIGPOLL SIGIO
#define FPE_INTDIV 1
#define FPE_INTOVF 2
#define FPE_FLTDIV 3
#define FPE_FLTOVF 4
#define FPE_FLTUND 5
#define FPE_FLTRES 6
#define FPE_FLTINV 7
#define FPE_FLTSUB 8
#define SA_NOCLDSTOP 0x00000001
#define SA_NOCLDWAIT 0x00000002
#define SA_SIGINFO 0x00000004
#define SA_RESTORER 0x04000000
#define SA_ONSTACK 0x08000000
#define SA_RESTART 0x10000000
#define SA_NODEFER 0x40000000
#define SA_RESETHAND 0x80000000
#define SA_NOMASK SA_NODEFER
#define SA_ONESHOT SA_RESETHAND
typedef struct siginfo_t
{
int si_signo;
int si_errno;
int si_code;
int si_trapno;
pid_t si_pid;
uid_t si_uid;
int si_status;
clock_t si_utime;
clock_t si_stime;
sigval_t si_value;
int si_int;
void *si_ptr;
int si_overrun;
int si_timerid;
void *si_addr;
long si_band;
int si_fd;
short si_addr_lsb;
void *si_lower;
void *si_upper;
int si_pkey;
void *si_call_addr;
int si_syscall;
unsigned int si_arch;
} siginfo_t;
// *INDENT-ON*
#if __MESC__
typedef long sighandler_t;
#else
typedef void (*sighandler_t) (int);
#endif
#if __i386__ || __x86_64__
struct sigaction
{
union
{
sighandler_t sa_handler;
void (*sa_sigaction) (int signum, siginfo_t *, void *);
};
unsigned long sa_flags;
#if __x86_64__
long _foo0;
#endif
sigset_t sa_mask;
#if __x86_64__
long _foo1[15];
#endif
//unsigned long sa_flags; // x86?
void (*sa_restorer) (void);
};
#else /* uapi */
struct sigaction
{
union
{
sighandler_t sa_handler;
void (*sa_sigaction) (int signum, siginfo_t *, void *);
};
unsigned long sa_flags;
void (*sa_restorer) (void);
sigset_t sa_mask;
};
#endif
#define SIG_DFL ((sighandler_t)0)
#define SIG_IGN ((sighandler_t)1)
#define SIG_ERR ((sighandler_t)-1)
#ifdef __i386__
#define EBX 0
#define ECX 1
#define EDX 2
#define ESI 3
#define EDI 4
#define EBP 5
#define EAX 6
#define DS 7
#define ES 8
#define FS 9
#define GS 10
#define ORIG_EAX 11
#define EIP 12
#define CS 13
#define EFL 14
#define UESP 15
#define SS 16
#define FRAME_SIZE 17
/* Type for general register. */
typedef int greg_t;
/* Number of general registers. */
#define NGREG 19
/* Container for all general registers. */
typedef greg_t gregset_t[NGREG];
/* Definitions taken from the kernel headers. */
struct _libc_fpreg
{
unsigned short int significand[4];
unsigned short int exponent;
};
struct _libc_fpstate
{
unsigned long int cw;
unsigned long int sw;
unsigned long int tag;
unsigned long int ipoff;
unsigned long int cssel;
unsigned long int dataoff;
unsigned long int datasel;
struct _libc_fpreg _st[8];
unsigned long int status;
};
/* Structure to describe FPU registers. */
typedef struct _libc_fpstate *fpregset_t;
typedef struct
{
gregset_t gregs;
/* Due to Linux's history we have to use a pointer here. The SysV/i386
ABI requires a struct with the values. */
fpregset_t fpregs;
unsigned long int oldmask;
unsigned long int cr2;
} mcontext_t;
/* Userlevel context. */
typedef struct ucontext
{
unsigned long int uc_flags;
struct ucontext *uc_link;
stack_t uc_stack;
mcontext_t uc_mcontext;
sigset_t uc_sigmask;
struct _libc_fpstate __fpregs_mem;
} ucontext_t;
#endif // !__i386__
int kill (pid_t pid, int signum);
int raise (int);
int sigaction (int signum, struct sigaction const *act, struct sigaction *oldact);
int sigaddset (sigset_t * set, int signum);
#if __MESC__
void *signal (int signum, void *action);
#else
sighandler_t signal (int signum, sighandler_t action);
#endif
int sigemptyset (sigset_t * set);
#ifndef SIG_BLOCK
#define SIG_BLOCK 0
#define SIG_UNBLOCK 1
#define SIG_SETMASK 2
#endif
int sigprocmask (int how, sigset_t const *set, sigset_t * oldset);
#endif //! SYSTEM_LIBC
#endif // __MES_SIGNAL_H
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <sys/stat.h>
int
chmod (char const *file_name, mode_t mask)
{
long long_file_name = cast_charp_to_long (file_name);
long long_mask = cast_int_to_long (mask);
return _sys_call2 (SYS_chmod, long_file_name, long_mask);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <time.h>
int
clock_gettime (clockid_t clk_id, struct timespec *tp)
{
long long_clk_id = cast_int_to_long (clk_id);
long long_tp = cast_voidp_to_long (tp);
return _sys_call2 (SYS_clock_gettime, long_clk_id, long_tp);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __MES_TIME_H
#define __MES_TIME_H 1
#if SYSTEM_LIBC
#undef __MES_TIME_H
#include_next <time.h>
#else // ! SYSTEM_LIBC
#ifndef __MES_TIME_T
#define __MES_TIME_T 1
typedef long int clockid_t;
typedef long int time_t;
#endif
struct tm
{
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
};
#ifndef __MES_STRUCT_TIMESPEC
#define __MES_STRUCT_TIMESPEC
struct timespec
{
long tv_sec;
long tv_nsec;
};
#endif // __MES_STRUCT_TIMESPEC
#define CLOCK_PROCESS_CPUTIME_ID 2
int clock_gettime (clockid_t clk_id, struct timespec *tp);
struct tm *localtime (time_t const *timep);
struct tm *gmtime (time_t const *time);
time_t mktime (struct tm *broken_time);
int nanosleep (struct timespec const *requested_time, struct timespec const *remaining);
time_t time (time_t * tloc);
#endif // ! SYSTEM_LIBC
#endif // __MES_TIME_H
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
int
execve (char const *file_name, char *const argv[], char *const env[])
{
return _sys_call3 (SYS_execve, (long) file_name, (long) argv, (long) env);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
int
fsync (int filedes)
{
return _sys_call1 (SYS_fsync, (int) filedes);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __MES_SYS_TIME_H
#define __MES_SYS_TIME_H 1
#if SYSTEM_LIBC
#undef __MES_SYS_TIME_H
#include_next <sys/time.h>
#else // ! SYSTEM_LIBC
struct timeval
{
long tv_sec;
long tv_usec;
};
struct timezone
{
int tz_minuteswest;
int tz_dsttime;
};
struct itimerval
{
struct timeval it_interval;
struct timeval it_value;
};
#define ITIMER_REAL 0
#define ITIMER_VIRTUAL 1
#define ITIMER_PROF 2
int gettimeofday (struct timeval *tv, struct timezone *tz);
int setitimer (int which, struct itimerval const *new, struct itimerval *old);
#endif // ! SYSTEM_LIBC
#endif // __MES_SYS_TIME_H
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2021 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
* Copyright © 2021 Danny Milosavljevic <dannym@scratchpost.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <string.h>
#include <stddef.h>
#include <stdint.h>
char *__brk = 0;
void *
malloc (size_t size)
{
if (!__brk)
__brk = cast_long_to_charp (brk (0));
/* align what we give back. */
__brk = (char*) (((uintptr_t) __brk
+ sizeof (max_align_t) - 1) & -sizeof (max_align_t));
if (brk (__brk + size) == -1)
return 0;
char *p = __brk;
__brk = __brk + size;
return p;
}
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * Copyright © 2021 Danny Milosavljevic <dannym@scratchpost.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_STDDEF_H #define __MES_STDDEF_H 1 #if SYSTEM_LIBC #undef __MES_STDDEF_H #include_next <stddef.h> #else // ! SYSTEM_LIBC #include <sys/types.h> #include <stdint.h> #include <unistd.h> #ifndef offsetof #if __MESC__ #define offsetof(type, field) (&((type *)0)->field) #else // !__MESC__ #define offsetof(type, field) ((size_t)&((type *)0)->field) #endif // !__MESC__ #endif // offsetof /* Note: on banana gcc, max_align_t is 16 Byte big instead! */ typedef double max_align_t; #endif // ! SYSTEM_LIBC #endif // __MES_STDDEF_H
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * Copyright © 2018 Peter De Wachter <pdewacht@gmail.com> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_STDINT_H #define __MES_STDINT_H 1 #if SYSTEM_LIBC #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #undef __MES_STDINT_H #include_next <stdint.h> #else // ! SYSTEM_LIBC #undef unsigned #undef uint8_t #undef int8_t #undef uint16_t #undef int16_t #undef uint32_t #undef int32_t #undef uint64_t #undef int64_t #undef uintptr_t #undef intmax_t #undef intptr_t #undef uintmax_t #undef ptrdiff_t typedef unsigned char uint8_t; typedef char int8_t; typedef unsigned short uint16_t; typedef short int16_t; typedef unsigned uint32_t; typedef int int32_t; #if __SIZEOF_LONG_LONG__ == 8 typedef unsigned long long uint64_t; typedef long long int64_t; #endif // __SIZEOF_LONG_LONG__ == 8 typedef int intmax_t; typedef unsigned uintmax_t; #include <sys/types.h> #define CHAR_BIT 8 #define CHAR_MAX 255 #define UCHAR_MAX 255 #define INT8_MAX 127 #define INT8_MIN (-INT8_MAX-1) #define UINT8_MAX 255 #define INT16_MAX 32767 #define INT16_MIN (-INT16_MAX-1) #define UINT16_MAX 65535 #define INT32_MAX 2147483647 #define INT32_MIN (-INT32_MAX-1) #define UINT32_MAX 4294967295U #define INT64_MAX 9223372036854775807LL #define INT64_MIN (-INT64_MAX-1) #define UINT64_MAX 18446744073709551615ULL #define INT_MIN -2147483648 #define INT_MAX 2147483647 #if __i386__ || __arm__ #define LONG_MIN INT_MIN #define LONG_MAX INT_MAX #define UINT_MAX UINT32_MAX #define ULONG_MAX UINT32_MAX #define LLONG_MIN INT64_MIN #define LLONG_MAX INT64_MAX #define SIZE_MAX UINT32_MAX #elif __x86_64__ #define LONG_MIN INT64_MIN #define LONG_MAX INT64_MAX #define UINT_MAX UINT32_MAX #define ULONG_MAX UINT64_MAX #define LLONG_MIN INT64_MIN #define LLONG_MAX INT64_MAX #define SIZE_MAX UINT64_MAX #endif #endif // ! SYSTEM_LIBC #endif // __MES_STDINT_H
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <mes/lib.h>
#include <fcntl.h>
int
_open3 (char const *file_name, int flags, int mask)
{
int r = _sys_call3 (SYS_open, (long) file_name, (int) flags, (int) mask);
if (r > 2)
{
__ungetc_clear (r);
__buffered_read_clear (r);
}
return r;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <mes/lib.h>
#include <fcntl.h>
ssize_t
_read (int filedes, void *buffer, size_t size)
{
return _sys_call3 (SYS_read, (int) filedes, (long) buffer, (long) size);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <time.h>
#include <sys/time.h>
#include <stdlib.h>
time_t
time (time_t * result)
{
struct timeval tv;
struct timezone tz;
if (gettimeofday (&tv, &tz) != 0)
return (time_t) - 1;
if (result)
*result = tv.tv_sec;
return tv.tv_sec;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <sys/types.h>
pid_t
waitpid (pid_t pid, int *status_ptr, int options)
{
#if __i386__
return _sys_call3 (SYS_waitpid, (long) pid, (long) status_ptr, (int) options);
#elif __x86_64__ || __arm__
return _sys_call4 (SYS_wait4, (long) pid, (long) status_ptr, (int) options, 0);
#else
#error arch not supported
#endif
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include <linux/x86/syscall.h>
long
__sys_call (long sys_call)
{
asm ("mov____0x8(%ebp),%eax !8");
asm ("int____$0x80");
}
long
__sys_call1 (long sys_call, long one)
{
asm ("mov____0x8(%ebp),%eax !8");
asm ("mov____0x8(%ebp),%ebx !12");
asm ("int____$0x80");
}
long
__sys_call2 (long sys_call, long one, long two)
{
asm ("mov____0x8(%ebp),%eax !8");
asm ("mov____0x8(%ebp),%ebx !12");
asm ("mov____0x8(%ebp),%ecx !16");
asm ("int____$0x80");
}
long
__sys_call3 (long sys_call, long one, long two, long three)
{
asm ("mov____0x8(%ebp),%eax !8");
asm ("mov____0x8(%ebp),%ebx !12");
asm ("mov____0x8(%ebp),%ecx !16");
asm ("mov____0x8(%ebp),%edx !20");
asm ("int____$0x80");
}
long
__sys_call4 (long sys_call, long one, long two, long three, long four)
{
asm ("mov____0x8(%ebp),%eax !8");
asm ("mov____0x8(%ebp),%ebx !12");
asm ("mov____0x8(%ebp),%ecx !16");
asm ("mov____0x8(%ebp),%edx !20");
asm ("mov____0x8(%ebp),%esi !24");
asm ("int____$0x80");
}
long
_sys_call (long sys_call)
{
long r = __sys_call (sys_call);
if (r < 0)
{
errno = -r;
r = -1;
}
else
errno = 0;
return r;
}
long
_sys_call1 (long sys_call, long one)
{
long r = __sys_call1 (sys_call, one);
if (r < 0)
{
errno = -r;
r = -1;
}
else
errno = 0;
return r;
}
long
_sys_call2 (long sys_call, long one, long two)
{
long r = __sys_call2 (sys_call, one, two);
if (r < 0)
{
errno = -r;
r = -1;
}
else
errno = 0;
return r;
}
long
_sys_call3 (long sys_call, long one, long two, long three)
{
long r = __sys_call3 (sys_call, one, two, three);
if (r < 0)
{
errno = -r;
r = -1;
}
else
errno = 0;
return r;
}
long
_sys_call4 (long sys_call, long one, long two, long three, long four)
{
long r = __sys_call4 (sys_call, one, two, three, four);
if (r < 0)
{
errno = -r;
r = -1;
}
else
errno = 0;
return r;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <unistd.h>
pid_t
getpid ()
{
return _sys_call (SYS_getpid);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <unistd.h>
int
kill (pid_t pid, int signum)
{
return _sys_call2 (SYS_kill, (long) pid, (long) signum);
}
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_CC_H #define __MES_CC_H typedef struct scm* SCM; #if __MESC__ typedef long FUNCTION; typedef long function0_t; typedef long function1_t; typedef long function2_t; typedef long function3_t; typedef long functionn_t; #else // !__MESC__ typedef SCM (*FUNCTION) (void); typedef SCM (*function0_t) (void); typedef SCM (*function1_t) (SCM); typedef SCM (*function2_t) (SCM, SCM); typedef SCM (*function3_t) (SCM, SCM, SCM); typedef SCM (*functionn_t) (SCM); #endif // !__MESC__ #endif //__MES_CC_H
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mes/lib.h"
#include "mes/mes.h"
struct scm *
apply_builtin0 (struct scm *fn)
{
struct scm *(*fp) (void) = (function0_t) builtin_function (fn);
return fp ();
}
struct scm *
apply_builtin1 (struct scm *fn, struct scm *x)
{
struct scm *(*fp) (struct scm *) = (function1_t) builtin_function (fn);
return fp (x);
}
struct scm *
apply_builtin2 (struct scm *fn, struct scm *x, struct scm *y)
{
struct scm *(*fp) (struct scm *, struct scm *) = (function2_t) builtin_function (fn);
return fp (x, y);
}
struct scm *
apply_builtin3 (struct scm *fn, struct scm *x, struct scm *y, struct scm *z)
{
struct scm *(*fp) (struct scm *, struct scm *, struct scm *) = (function3_t) builtin_function (fn);
return fp (x, y, z);
}
#undef cast_charp_to_scmp
#undef cast_charp_to_scmpp
#undef cast_voidp_to_charp
#undef cast_scmp_to_long
#undef cast_scmp_to_charp
struct scm *
cast_charp_to_scmp (char const *i)
{
return (struct scm *)i;
}
struct scm **
cast_charp_to_scmpp (char const *i)
{
return (struct scm **)i;
}
char*
cast_voidp_to_charp (void const *i)
{
return (char*)i;
}
long
cast_scmp_to_long (struct scm *i)
{
return (long)i;
}
char*
cast_scmp_to_charp (struct scm *i)
{
return (char*)i;
}
/* * GNU Mes --- Maxwell Equations of Software * Copyright © 2021 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #include <mes/lib-mini.h> #define extern #include <mes/symbols.h> #include <mes/mes.h>
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ctype.h>
int
islower (int c)
{
return c >= 'a' && c <= 'z';
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ctype.h>
int
isupper (int c)
{
return c >= 'A' && c <= 'Z';
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ctype.h>
int
tolower (int c)
{
if (isupper (c))
return c + ('a' - 'A');
return c;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ctype.h>
int
toupper (int c)
{
if (islower (c))
return c - ('a' - 'A');
return c;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
double
abtod (char const **p, int base)
{
char const *s = *p;
double d = 0;
int sign_p = 0;
if (!base)
base = 10;
double dbase = base;
long i = abtol (&s, base);
long f = 0;
long e = 0;
if (*s == '.')
{
s++;
f = abtol (&s, base);
}
if (*s == 'e')
{
s++;
e = abtol (&s, base);
}
d = i + f / dbase;
if (e < 0)
while (e++)
d = d / dbase;
while (e--)
d = d * dbase;
*p = s;
return sign_p ? -d : d;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <limits.h>
#include <string.h>
char *
dtoab (double d, int base, int signed_p)
{
static char dtoa_buf[40];
long i = (long) d;
char *p = ntoab (i, base, signed_p);
strcpy (dtoa_buf, p);
long f = (d - (double) i) * (double) 100000000;
if (f)
{
if (f < 0)
f = -f;
strcat (dtoa_buf, ".");
p = ntoab (f, base, 1);
strcat (dtoa_buf, p);
p = strchr (dtoa_buf, 0);
p--;
while (*p && *p == '0')
*p-- = 0;
}
return dtoa_buf;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
char *
search_path (char const *file_name)
{
static char buf[256];
char *path = getenv ("PATH");
if (__mes_debug ())
{
eputs ("\n search-path: ");
eputs (file_name);
eputs ("\n");
}
while (*path)
{
char *end = strchr (path, ':');
if (!end)
end = strchr (path, '\0');
strncpy (buf, path, end - path);
buf[end - path] = 0;
if (__mes_debug ())
{
eputs (" dir: ");
eputs (buf);
eputs ("\n");
}
if (buf[end - path] != '/')
strcat (buf, "/");
strcat (buf, file_name);
if (!access (buf, X_OK))
{
if (__mes_debug ())
{
eputs (" found: ");
eputs (buf);
eputs ("\n");
}
return buf;
}
path = end + 1;
}
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
int
execvp (char const *file_name, char *const argv[])
{
if (!strchr (file_name, '/'))
file_name = search_path (file_name);
if (!file_name)
{
errno = ENOENT;
return -1;
}
if (__mes_debug ())
{
eputs (" EXEC: ");
eputs (file_name);
eputs ("\n");
int i = 0;
while (argv[i])
{
eputs (" arg[");
eputs (itoa (i));
eputs ("]: ");
eputs (argv[i]);
eputs ("\n");
i++;
}
}
return execve (file_name, argv, environ);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <unistd.h>
int
fclose (FILE * stream)
{
int fd = (long) stream;
return close (fd);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
FILE *
fdopen (int fd, char const *mode)
{
return (FILE *) (long) fd;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
int
ferror (FILE * stream)
{
int fd = (long) stream;
if (fd == -1)
return -1;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
int
fflush (FILE * stream)
{
int filedes = (long) stream;
if (filedes < 3)
return 0;
return fsync (filedes);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
* Copyright © 2018 Jeremiah Orians <jeremiah@pdp10.guru>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
FILE *
fopen (char const *file_name, char const *opentype)
{
if (__mes_debug ())
{
eputs ("fopen ");
eputs (file_name);
eputs (" ");
eputs (opentype);
eputs ("\n");
}
int fd;
int mode = 0600;
if ((opentype[0] == 'a' || !strcmp (opentype, "r+")) && !access (file_name, O_RDONLY))
{
int flags = O_RDWR;
if (opentype[0] == 'a')
flags |= O_APPEND;
fd = _open3 (file_name, flags, mode);
}
else if (opentype[0] == 'w' || opentype[0] == 'a' || !strcmp (opentype, "r+"))
{
char *plus_p = strchr (opentype, '+');
int flags = plus_p ? O_RDWR | O_CREAT : O_WRONLY | O_CREAT | O_TRUNC;
fd = _open3 (file_name, flags, mode);
}
else
fd = _open3 (file_name, O_RDONLY, 0);
if (__mes_debug ())
{
eputs (" => fd=");
eputs (itoa (fd));
eputs ("\n");
}
if (!fd)
{
eputs (" ***MES LIB C*** fopen of stdin: signal me in band\n");
assert (0);
}
if (fd < 0)
fd = 0;
return (FILE *) (long) fd;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <stdarg.h>
#include <stdio.h>
int
fprintf (FILE * stream, char const *format, ...)
{
va_list ap;
va_start (ap, format);
int r = vfprintf (stream, format, ap);
va_end (ap);
return r;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int
__fungetc_p (FILE * stream)
{
return __ungetc_p ((int) (long) stream);
}
size_t
fread (void *data, size_t size, size_t count, FILE * stream)
{
if (!size || !count)
return 0;
size_t todo = size * count;
char *buf = (char *) data;
int bytes = 0;
while (__fungetc_p (stream) && todo-- && ++bytes)
*buf++ = fgetc (stream);
int filedes = (long) stream;
if (todo)
{
int r = read (filedes, buf, todo);
if (r < 0 && !bytes)
bytes = r;
else
bytes += r;
}
if (__mes_debug ())
{
static char debug_buf[4096];
eputs ("fread fd=");
eputs (itoa (filedes));
eputs (" bytes=");
eputs (itoa (bytes));
eputs ("\n");
if (bytes > 0 && bytes < sizeof (debug_buf))
{
strncpy (debug_buf, data, bytes);
buf[bytes] = 0;
eputs ("fread buf=");
eputs (debug_buf);
eputs ("\n");
}
}
if (bytes > 0)
return bytes / size;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <stdio.h>
#include <unistd.h>
int
fseek (FILE * stream, long offset, int whence)
{
int filedes = (long) stream;
off_t pos = lseek (filedes, offset, whence);
if (__mes_debug ())
{
eputs ("fread fd=");
eputs (itoa (filedes));
eputs (" =>");
eputs (itoa (pos));
eputs ("\n");
}
if (pos >= 0)
return 0;
return -1;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
long
ftell (FILE * stream)
{
return lseek ((int) (long) stream, 0, SEEK_CUR);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <stdio.h>
#include <unistd.h>
size_t
fwrite (void const *data, size_t size, size_t count, FILE * stream)
{
if (__mes_debug () > 1)
{
eputs ("fwrite ");
eputs (itoa ((int) (long) stream));
eputs (" ");
eputs (itoa (size));
eputs ("\n");
}
if (!size || !count)
return 0;
int filedes = (long) stream;
int bytes = write (filedes, data, size * count);
if (__mes_debug () > 2)
{
eputs (" => ");
eputs (itoa (bytes));
eputs ("\n");
}
if (bytes > 0)
return bytes / size;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <stdarg.h>
#include <stdio.h>
int
printf (char const *format, ...)
{
va_list ap;
int r;
#if __GNUC__ && __x86_64__ && !SYSTEM_LIBC
#define __FUNCTION_ARGS 1
ap += (__FOO_VARARGS + (__FUNCTION_ARGS << 1)) << 3;
#undef __FUNCTION_ARGS
#endif
va_start (ap, format);
r = vprintf (format, ap);
va_end (ap);
return r;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
int
remove (char const *file_name)
{
struct stat buf;
if (stat (file_name, &buf) < 0)
return -1;
if (S_ISDIR (buf.st_mode))
return rmdir (file_name);
return unlink (file_name);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <stdarg.h>
#include <stdio.h>
int
snprintf (char *str, size_t size, char const *format, ...)
{
va_list ap;
int r;
#if __GNUC__ && __x86_64__ && !SYSTEM_LIBC
#define __FUNCTION_ARGS 3
ap += (__FOO_VARARGS + (__FUNCTION_ARGS << 1)) << 3;
#undef __FUNCTION_ARGS
#endif
va_start (ap, format);
r = vsnprintf (str, size, format, ap);
va_end (ap);
return r;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <stdarg.h>
#include <stdio.h>
int
sprintf (char *str, char const *format, ...)
{
va_list ap;
int r;
#if __GNUC__ && __x86_64__ && !SYSTEM_LIBC
#define __FUNCTION_ARGS 2
ap += (__FOO_VARARGS + (__FUNCTION_ARGS << 1)) << 3;
#undef __FUNCTION_ARGS
#endif
va_start (ap, format);
r = vsprintf (str, format, ap);
va_end (ap);
return r;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <stdarg.h>
#include <stdio.h>
int
sscanf (char const *str, char const *template, ...)
{
va_list ap;
va_start (ap, template);
int r = vsscanf (str, template, ap);
va_end (ap);
return r;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
int
vfprintf (FILE * f, char const *format, va_list ap)
{
int fd = (long) f;
char const *p = format;
int count = 0;
while (*p)
if (*p != '%')
{
count++;
fputc (*p++, f);
}
else
{
p++;
char c = *p;
int left_p = 0;
int precision = -1;
int width = -1;
if (c == '-')
{
left_p = 1;
c = *++p;
}
char pad = ' ';
if (c == ' ')
{
pad = c;
c = *p++;
}
if (c == '0')
{
pad = c;
c = *p++;
}
if (c >= '0' && c <= '9')
{
width = abtol (&p, 10);
c = *p;
}
else if (c == '*')
{
width = va_arg (ap, int);
c = *++p;
}
if (c == '.')
{
c = *++p;
if (c >= '0' && c <= '9')
{
precision = abtol (&p, 10);
c = *p;
}
else if (c == '*')
{
precision = va_arg (ap, int);
c = *++p;
}
}
if (c == 'l')
c = *++p;
if (c == 'l')
{
eputs ("vfprintf: skipping second: l\n");
c = *++p;
}
switch (c)
{
case '%':
{
fputc (*p, f);
count++;
break;
}
case 'c':
{
char _c;
_c = va_arg (ap, long);
fputc (_c, f);
break;
}
case 'd':
case 'i':
case 'o':
case 'u':
case 'x':
case 'X':
{
long d = va_arg (ap, long);
int base = c == 'o' ? 8 : c == 'x' || c == 'X' ? 16 : 10;
char *s = ntoab (d, base, c != 'u' && c != 'x' && c != 'X');
if (c == 'X')
strupr (s);
int length = strlen (s);
if (precision == -1)
precision = length;
if (!left_p)
{
while (width-- > precision)
{
fputc (pad, f);
count++;
}
while (precision > length)
{
fputc ('0', f);
precision--;
width--;
count++;
}
}
while (*s)
{
if (precision-- <= 0)
break;
width--;
fputc (*s++, f);
count++;
}
while (width > 0)
{
width--;
fputc (pad, f);
count++;
}
break;
}
case 's':
{
char *s = va_arg (ap, char *);
int length = strlen (s);
if (precision == -1)
precision = length;
if (!left_p)
{
while (width-- > precision)
{
fputc (pad, f);
count++;
}
while (precision > length)
{
fputc (' ', f);
precision--;
width--;
count++;
}
}
while (*s)
{
if (precision-- <= 0)
break;
width--;
fputc (*s++, f);
count++;
}
while (width > 0)
{
width--;
fputc (pad, f);
count++;
}
break;
}
case 'f':
case 'e':
case 'E':
case 'g':
case 'G':
{
double d = va_arg8 (ap, double);
char *s = dtoab (d, 10, 1);
if (c == 'E' || c == 'G')
strupr (s);
int length = strlen (s);
if (precision == -1)
precision = length;
if (!left_p)
{
while (width-- > precision)
{
fputc (pad, f);
count++;
}
while (precision > length)
{
fputc (' ', f);
precision--;
width--;
count++;
}
}
while (*s)
{
if (precision-- <= 0)
break;
width--;
fputc (*s++, f);
count++;
}
while (width > 0)
{
width--;
fputc (pad, f);
count++;
}
break;
}
case 'n':
{
int *n = va_arg (ap, int *);
*n = count;
break;
}
default:
{
eputs ("vfprintf: not supported: %:");
eputc (c);
eputs ("\n");
p++;
}
}
p++;
}
va_end (ap);
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <stdarg.h>
#include <stdio.h>
int
vprintf (char const *format, va_list ap)
{
return vfprintf (stdout, format, ap);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <ctype.h>
#include <stdarg.h>
#include <string.h>
int
vsnprintf (char *str, size_t size, char const *format, va_list ap)
{
char const *p = format;
int count = 0;
char c;
while (*p)
if (*p != '%')
{
c = *p++;
if (count < size)
*str++ = c;
count++;
}
else
{
p++;
c = *p;
int left_p = 0;
int precision = -1;
int width = -1;
if (c == '-')
{
left_p = 1;
c = *++p;
}
char pad = ' ';
if (c == ' ')
{
pad = c;
c = *p++;
}
if (c == '0')
{
pad = c;
c = *p++;
}
if (c >= '0' && c <= '9')
{
width = abtol (&p, 10);
c = *p;
}
else if (c == '*')
{
width = va_arg (ap, long);
c = *++p;
}
if (c == '.')
{
c = *++p;
if (c >= '0' && c <= '9')
{
precision = abtol (&p, 10);
c = *p;
}
else if (c == '*')
{
precision = va_arg (ap, long);
c = *++p;
}
}
if (c == 'l')
c = *++p;
if (c == 'l')
c = *++p;
if (c == 'l')
{
eputs ("vsnprintf: skipping second: l\n");
c = *++p;
}
switch (c)
{
case '%':
{
if (count < size)
*str++ = *p;
count++;
break;
}
case 'c':
{
c = va_arg (ap, long);
if (count < size)
*str++ = c;
count++;
break;
}
case 'd':
case 'i':
case 'o':
case 'u':
case 'x':
case 'X':
{
long d = va_arg (ap, long);
int base = c == 'o' ? 8 : c == 'x' || c == 'X' ? 16 : 10;
char *s = ntoab (d, base, c != 'u' && c != 'x' && c != 'X');
if (c == 'X')
strupr (s);
int length = strlen (s);
if (precision == -1)
precision = length;
if (!left_p)
{
while (width-- > precision)
{
if (count < size)
*str++ = pad;
count++;
}
while (precision > length)
{
if (count < size)
*str++ = '0';
precision--;
width--;
count++;
}
}
while (*s)
{
if (precision-- <= 0)
break;
width--;
c = *s++;
if (count < size)
*str++ = c;
count++;
}
while (width > 0)
{
width--;
if (count < size)
*str++ = pad;
count++;
}
break;
}
case 's':
{
char *s = va_arg (ap, char *);
int length = s ? strlen (s) : 0;
if (precision == -1)
precision = length;
if (!left_p)
{
while (width-- > precision)
{
if (count < size)
*str++ = pad;
count++;
}
while (width > length)
{
if (count < size)
*str++ = ' ';
precision--;
width--;
count++;
}
}
while (s && *s)
{
if (precision-- <= 0)
break;
width--;
c = *s++;
if (count < size)
*str++ = c;
count++;
}
while (width > 0)
{
width--;
if (count < size)
*str++ = pad;
count++;
}
break;
}
case 'f':
case 'e':
case 'E':
case 'g':
case 'G':
{
double d = va_arg8 (ap, double);
char *s = dtoab (d, 10, 1);
if (c == 'E' || c == 'G')
strupr (s);
int length = strlen (s);
if (precision == -1)
precision = length;
if (!left_p)
{
while (width-- > precision)
{
if (count < size)
*str++ = pad;
count++;
}
while (precision > length)
{
if (count < size)
*str++ = ' ';
precision--;
width--;
count++;
}
}
while (*s)
{
if (precision-- <= 0)
break;
width--;
c = *s++;
if (count < size)
*str++ = c;
count++;
}
while (width > 0)
{
width--;
if (count < size)
*str++ = pad;
count++;
}
break;
}
case 'n':
{
int *n = va_arg (ap, int *);
*n = count;
break;
}
default:
{
eputs ("vsnprintf: not supported: %:");
eputc (c);
eputs ("\n");
p++;
}
}
p++;
}
va_end (ap);
if (count < size)
*str = 0;
return count;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <limits.h>
#include <stdarg.h>
int
vsprintf (char *str, char const *format, va_list ap)
{
return vsnprintf (str, LONG_MAX, format, ap);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
vsscanf (char const *s, char const *template, va_list ap)
{
char *p = (char *) s;
char const *t = template;
int count = 0;
while (*t && *p)
if (*t != '%')
{
t++;
p++;
}
else
{
t++;
char c = *t;
if (c == 'l')
c = *++t;
switch (c)
{
case '%':
{
p++;
break;
}
case 'c':
{
char *c = va_arg (ap, char *);
*c = *p++;
count++;
break;
}
case 'd':
case 'i':
case 'u':
{
int *d = va_arg (ap, int *);
*d = abtol ((char const **)&p, 10);
count++;
break;
}
case 'e':
case 'f':
case 'g':
case 'E':
case 'G':
{
float *f = va_arg (ap, float *);
*f = strtod (p, &p);
count++;
break;
}
default:
{
eputs ("vsscanf: not supported: %:");
eputc (c);
eputs ("\n");
t++;
p++;
}
}
t++;
}
va_end (ap);
return count;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <string.h>
void *
calloc (size_t nmemb, size_t size)
{
size_t count = nmemb * size;
void *p = malloc (count);
memset (p, 0, count);
return p;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
* Copyright © 2021 Paul Dersey <pdersey@gmail.com>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <string.h>
void
qswap (void *a, void *b, size_t size)
{
char *pa = a;
char *pb = b;
do
{
char tmp = *pa;
*pa++ = *pb;
*pb++ = tmp;
} while (--size > 0);
}
size_t
qpart (void *base, size_t count, size_t size, int (*compare) (void const *, void const *))
{
void *p = base + count * size;
size_t i = 0;
for (size_t j = 0; j < count; j++)
{
int c = compare (base + j * size, p);
if (c < 0)
{
#if 1 //__x86_64__
qswap (base + i * size, base + j * size, size);
#else
int p1 = base + i * size;
int p2 = base + j * size;
qswap (p1, p2, size);
#endif
i++;
}
else if (c == 0)
i++;
}
if (compare (base + count * size, base + i * size) < 0)
qswap (base + i * size, base + count * size, size);
return i;
}
void
qsort (void *base, size_t count, size_t size, int (*compare) (void const *, void const *))
{
if (count > 1)
{
int p = qpart (base, count - 1, size, compare);
qsort (base, p, size, compare);
#if 1 //__x86_64__
qsort (base + p * size, count - p, size, compare);
#else
int p1 = base + p * size;
int p2 = count - p;
qsort (p1, p2, size, compare);
#endif
}
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <stdlib.h>
#include <string.h>
double
strtod (char const *string, char **tailptr)
{
int base = 10;
if (!strncmp (string, "0x", 2))
{
string += 2;
base = 16;
}
if (tailptr)
{
*tailptr = (char *) string;
return abtod ((char const **) tailptr, base);
}
char **p = (char **) &string;
return abtod ((char const **) p, base);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
float
strtof (char const *string, char **tailptr)
{
return strtod (string, tailptr);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <stdlib.h>
#include <string.h>
long
strtol (char const *string, char **tailptr, int base)
{
if (!strncmp (string, "0x", 2))
{
string += 2;
base = 16;
}
if (tailptr)
{
*tailptr = (char *) string;
return abtol ((char const **) tailptr, base);
}
char **p = (char **) &string;
return abtol ((char const **) p, base);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
long double
strtold (char const *string, char **tailptr)
{
return strtod (string, tailptr);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
long long int
strtoll (char const *string, char **tailptr, int base)
{
return strtol (string, tailptr, base);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
unsigned long
strtoul (char const *string, char **tailptr, int base)
{
return strtol (string, tailptr, base);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
unsigned long long
strtoull (char const *string, char **tailptr, int base)
{
return strtol (string, tailptr, base);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 1997--2015,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
* Copyright (C) 1997--2015,2018 Han-Wen Nienhuys <hanwen@xs4all.nl>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
/** locate a substring. #memmem# finds the first occurrence of
#needle# in #haystack#. This is not ANSI-C.
The prototype is not in accordance with the Linux Programmer's
Manual v1.15, but it is with /usr/include/string.h */
unsigned char *
_memmem (unsigned char const *haystack, int haystack_len, unsigned char const *needle, int needle_len)
{
unsigned char const *end_haystack = haystack + haystack_len - needle_len + 1;
unsigned char const *end_needle = needle + needle_len;
/* Ahhh ... Some minimal lowlevel stuff. This *is* nice; Varation
is the spice of life */
while (haystack < end_haystack)
{
unsigned char const *subneedle = needle;
unsigned char const *subhaystack = haystack;
while (subneedle < end_needle)
if (*subneedle++ != *subhaystack++)
goto next;
/* Completed the needle. Gotcha. */
return (unsigned char *) haystack;
next:
haystack++;
}
return 0;
}
void *
memmem (void const *haystack, int haystack_len, void const *needle, int needle_len)
{
unsigned char const *haystack_byte_c = (unsigned char const *) haystack;
unsigned char const *needle_byte_c = (unsigned char const *) needle;
return _memmem (haystack_byte_c, haystack_len, needle_byte_c, needle_len);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
char *
strcat (char *to, char const *from)
{
char *p = strchr (to, '\0');
while (*from)
*p++ = *from++;
*p = 0;
return to;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
char *
strchr (char const *s, int c)
{
char const *p = s;
while (*p || !c)
{
if (c == *p)
return (char *) p;
p++;
}
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ctype.h>
#include <string.h>
char *
strlwr (char *string)
{
char *p = string;
while (*p)
{
*p = tolower (*p);
p++;
}
return string;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
char *
strncpy (char *to, char const *from, size_t size)
{
if (size == 0)
return to;
char *p = to;
while (*from && size--)
*p++ = *from++;
if (*from)
size++;
while (size--)
*p++ = 0;
return to;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
char *
strrchr (char const *s, int c)
{
int n = strlen (s);
if (!n)
return 0;
char const *p = s + n;
if (!*p && !c)
return (char *) p;
p--;
while (n-- && (*p || !c))
{
if (c == *p)
return (char *) p;
p--;
}
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
char *
strstr (char const *haystack, char const *needle)
{
return memmem (haystack, strlen (haystack), needle, strlen (needle));
}
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_SYS_MMAN_H #define __MES_SYS_MMAN_H 1 #if SYSTEM_LIBC #undef __MES_SYS_MMAN_H #include_next <sys/mman.h> #else // ! SYSTEM_LIBC #ifndef __MES_SIZE_T #define __MES_SIZE_T typedef unsigned long size_t; #endif #define PROT_NONE 0 #define PROT_READ 1 #define PROT_WRITE 2 #define PROT_EXEC 4 int mprotect (void *addr, size_t len, int prot); #endif // ! SYSTEM_LIBC #endif // __MES_SYS_MMAN_H
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ctype.h>
#include <string.h>
char *
strupr (char *string)
{
char *p = string;
while (*p)
{
*p = toupper (*p);
p++;
}
return string;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <signal.h>
int
sigaction (int signum, struct sigaction const *act, struct sigaction *oldact)
{
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <math.h>
double
ldexp (double value, int exponent)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("ldexp stub\n");
stub = 1;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_MATH_H #define __MES_MATH_H 1 #if SYSTEM_LIBC #undef __MES_MATH_H #include_next <math.h> #else // ! SYSTEM_LIBC double atan2 (double y, double x); double ceil (double x); double cos (double x); double exp (double x); double fabs (double number); double floor (double x); double ldexp (double value, int exponent); double log (double x); double modf (double value, double *integer_part); double pow (double base, double power); double sin (double x); double sqrt (double x); #endif // ! SYSTEM_LIBC #endif // __MES_MATH_H
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <string.h>
int
mprotect (void *addr, size_t len, int prot)
{
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>
struct tm *
localtime (time_t const *timep)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("localtime stub\n");
stub = 1;
errno = 0;
static struct tm zero = { 0 };
return &zero;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <signal.h>
int
sigemptyset (sigset_t * set)
{
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <setjmp.h>
#include <stdlib.h>
void
longjmp (jmp_buf env, int val)
{
val = val == 0 ? 1 : val;
///asm ("mov____0x8(%ebp),%eax !0x0c"); // val
asm ("mov____0x8(%ebp),%ebp !0x08"); // env*
asm ("mov____0x8(%ebp),%ebx !0x4"); // env.__pc
asm ("mov____0x8(%ebp),%esp !0x8"); // env.__sp
asm ("mov____0x8(%ebp),%ebp !0x0"); // env.__bp
asm ("jmp____*%ebx");
// not reached
exit (42);
}
int
setjmp (__jmp_buf * env)
{
long *p = (long *) &env;
env[0].__bp = p[-2];
env[0].__pc = p[-1];
env[0].__sp = (long) &env;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
* Copyright © 2020 Danny Milosavljevic <dannym@scratchpost.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __MES_SETJMP_H
#define __MES_SETJMP_H 1
#if SYSTEM_LIBC
#undef __MES_SETJMP_H
#include_next <setjmp.h>
#else // ! SYSTEM_LIBC
#if __arm__
#if __GNUC__ || __TINYC__
#warning "It is not supported to use mes' setjmp implementation together with GCC. Continuing with best-effort implementation."
typedef struct
{
long __sp;
long __lr;
long __registers[8]; /* Note: Keep in sync with lib/arm-mes-gcc/setjmp.c */
} __jmp_buf;
#else
typedef struct
{
long __fp;
long __lr;
long __sp;
} __jmp_buf;
#endif
#else
typedef struct
{
long __bp;
long __pc;
long __sp;
} __jmp_buf;
#endif
typedef __jmp_buf jmp_buf[1];
void longjmp (jmp_buf env, int val);
int setjmp (jmp_buf env);
#endif // ! SYSTEM_LIBC
#endif // __MES_SETJMP_H
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <mes/lib.h>
int
close (int filedes)
{
__ungetc_clear (filedes);
__buffered_read_clear (filedes);
return _sys_call1 (SYS_close, (int) filedes);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
int
rmdir (char const *file_name)
{
return _sys_call1 (SYS_rmdir, (long) file_name);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <sys/stat.h>
int
stat (char const *file_name, struct stat *statbuf)
{
return _sys_call2 (SYS_stat, (long) file_name, (long) statbuf);
}
### Copyright (C) 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> ### This file is part of stage0. ### ### stage0 is free software: you can redistribute it and/or modify ### it under the terms of the GNU General Public License as published by ### the Free Software Foundation, either version 3 of the License, or ### (at your option) any later version. ### ### stage0 is distributed in the hope that it will be useful, ### but WITHOUT ANY WARRANTY; without even the implied warranty of ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ### GNU General Public License for more details. ### ### You should have received a copy of the GNU General Public License ### along with stage0. If not, see <http://www.gnu.org/licenses/>. ### stage0's hex2 format for x86 ### !<label> 1 byte relative ### $<label> 2 byte address ### @<label> 2 byte relative ### &<label> 4 byte address ### %<label> 4 byte relative ### local_<label> function-local ### string_<index> string #<index> ### elf32-footer-single-main.hex2: 32 bit elf footer in hex2 for single main # @230 :ELF_str 00 # 0 :ELF_str__start 5f 73 74 61 72 74 00 # _start :ELF_str__main 6d 61 69 6e 00 # main 00 00 00 # @240 :ELF_sym 00 00 00 00 # st-name 00 00 00 00 # st-offset: &_start - BaseAddress 00 00 00 00 # st-len : &main - _start 00 # st-info = stt-func= 2 00 # st-other 01 00 # st-shndx: 1 # _start %ELF_str__start>ELF_str # st-name &_start 10 00 00 00 # st-len : &main - _start 02 # st-info = stt-func= 2 00 # st-other 01 00 # st-shndx: 1 # main %ELF_str__main>ELF_str # st-name &main 10 00 00 00 # st-len : &ELF_data - main 02 # st-info = stt-func= 2 00 # st-other 01 00 # st-shndx: 1 :ELF_end
### Copyright (C) 2016 Jeremiah Orians
### Copyright (C) 2017,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
###
### This file is part of GNU Mes.
###
### GNU Mes is free software; you can redistribute it and/or modify it
### under the terms of the GNU General Public License as published by
### the Free Software Foundation; either version 3 of the License, or (at
### your option) any later version.
###
### GNU Mes is distributed in the hope that it will be useful, but
### WITHOUT ANY WARRANTY; without even the implied warranty of
### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
### GNU General Public License for more details.
###
### You should have received a copy of the GNU General Public License
### along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
### Commentary:
# elf32-header.hex2: 32 bit elf header in hex2, with text segment, data
# segment and symbol tables.
# stage0's hex2 format for x86
# !<label> 1 byte relative
# $<label> 2 byte address
# @<label> 2 byte relative
# &<label> 4 byte address
# %<label> 4 byte relative
# local_<label> function-local
# string_<index> string #<index>
### Code:
:ELF_base
7F 45 4C 46 # e_ident[EI_MAG0-3] ELF's magic number
01 # e_ident[EI_CLASS] Indicating 32 bit
01 # e_ident[EI_DATA] Indicating little endianness
01 # e_ident[EI_VERSION] Indicating original elf
00 # e_ident[EI_OSABI] Set at 0 because none cares
00 # e_ident[EI_ABIVERSION] See above
00 00 00 00 00 00 00 # e_ident[EI_PAD]
02 00 # e_type Indicating Executable
03 00 # e_machine Indicating 32bit x86
01 00 00 00 # e_version Indicating original elf
&ELF_text # e_entry Address of the entry point
%ELF_program_headers>ELF_base # e_phoff Address of program header table
%ELF_section_headers>ELF_base # e_shoff Address of section header table
00 00 00 00 # e_flags
34 00 # e_ehsize Indicating our 52 Byte header
20 00 # e_phentsize size of a program header table
01 00 # e_phnum number of entries in program table
28 00 # e_shentsize size of a section header table
07 00 # e_shnum number of entries in section table
04 00 # e_shstrndx index of the section names
# @34
00 00 00 00
00 00 00 00
00 00 00 00
# @40
:ELF_program_headers
:ELF_program_header__text
01 00 00 00 # ph_type: PT-LOAD = 1
00 00 00 00 # ph_offset
&ELF_base # ph_vaddr
&ELF_base # ph_physaddr
%ELF_end>ELF_base # ph_filesz
%ELF_end>ELF_base # ph_memsz
07 00 00 00 # ph_flags: PF-X|PF-W|PF-R = 7
01 00 00 00 # ph_align
# @60
#:ELF_program_header__data # NOT USED
# FIXME: linux 4.17 does not allow this overlap
# Uhuuh, elf segment at 0000000001000000
# requested but the memory is mapped already
01 00 00 00 # ph_type: PT-LOAD = 1
00 00 00 00 # ph_offset
&ELF_base # ph_vaddr
&ELF_base # ph_physaddr
%ELF_end>ELF_base # ph_filesz
%ELF_end>ELF_base # ph_memsz
07 00 00 00 # ph_flags: PF-X|PF-W|PF-R = 7
01 00 00 00 # ph_align
# @80
:ELF_comment
4d 45 53 00 # MES
00 00 00 00 # align
00 00 00 00
00 00 00 00
4d 45 53 00 # MES
00 00 00 00 # align
00 00 00 00
00 00 00 00
# @a0
:ELF_shstr
00
:ELF_shstr__text
2e 74 65 78 74 00 # .text
:ELF_shstr__data
2e 64 61 74 61 00 # .data
:ELF_shstr__comment
2e 63 6f 6d 6d 65 6e 74 00 # .comment
:ELF_shstr__shstr
2e 73 68 73 74 72 74 61 62 00 # .shstrtab
:ELF_shstr__sym
2e 73 79 6d 74 61 62 00 # .symtab
:ELF_shstr__str
2e 73 74 72 74 61 62 00 # .strtab
# @d0
:ELF_section_headers
00 00 00 00 # sh_name
00 00 00 00 # sh_type
00 00 00 00 # sh_flags
00 00 00 00 # sh_addr
00 00 00 00 # sh_offset
00 00 00 00 # sh_length
00 00 00 00 # sh_link
00 00 00 00 # sh_info
01 00 00 00 # sh_1?
00 00 00 00 # sh_entsize
## FIXME: M0 for calculations?
:ELF_section_header_text
%ELF_shstr__text>ELF_shstr # sh_name
01 00 00 00 # sh_type = SHT_PROGBITS = 1
06 00 00 00 # sh_flags = SHF-ALLOC|SHF-EXEC =2 | 4 = 6
&ELF_text # sh_addr
%ELF_text>ELF_base # sh_offset
%ELF_data>ELF_text # sh_length
00 00 00 00 # sh_link
00 00 00 00 # sh_info
01 00 00 00 # sh_1?
00 00 00 00 # sh_entsize
:ELF_section_header_data
%ELF_shstr__data>ELF_shstr # sh_name
01 00 00 00 # sh_type = SHT_PROGBITS = 1
03 00 00 00 # sh_flags = SHF-WRITE|SHF-ALLOC = 1 | 2 = 3
&ELF_data # sh_addr
%ELF_data>ELF_base # sh_offset
%ELF_sym>ELF_data # sh_length
00 00 00 00 # sh_link
00 00 00 00 # sh_info
01 00 00 00 # sh_1?
00 00 00 00 # sh_entsize
:ELF_section_header_comment
%ELF_shstr__comment>ELF_shstr # sh_name
01 00 00 00 # sh_type = SHT_PROGBITS = 1
00 00 00 00 # sh_flags
&ELF_comment # sh_addr
%ELF_comment>ELF_base # sh_offset
%ELF_shstr>ELF_comment # sh_length
00 00 00 00 # sh_link
00 00 00 00 # sh_info
01 00 00 00 # sh_1?
00 00 00 00 # sh_entsize
:ELF_section_header_shstr
%ELF_shstr__shstr>ELF_shstr # sh_name
03 00 00 00 # sh_type: str-sht-strtab
00 00 00 00 # sh_flags
&ELF_shstr # sh_addr
%ELF_shstr>ELF_base # sh_offset
%ELF_section_headers>ELF_shstr # sh_length
00 00 00 00 # sh_link
00 00 00 00 # sh_info
01 00 00 00 # sh_1?
00 00 00 00 # sh_entsize
:ELF_section_header_sym
%ELF_shstr__sym>ELF_shstr # sh_name
02 00 00 00 # sh_type: str-sht-symtab
00 00 00 00 # sh_flags
&ELF_sym # sh_addr
%ELF_sym>ELF_base # sh_offset
%ELF_end>ELF_sym # sh_length
06 00 00 00 # sh_link:6
00 00 00 00 # sh_info
01 00 00 00 # sh_1?
10 00 00 00 # sh_entsize
:ELF_section_header_str
%ELF_shstr__str>ELF_shstr # sh_name
03 00 00 00 # sh_type: str-sht-strtab
00 00 00 00 # sh_flags
&ELF_str # sh_addr
%ELF_str>ELF_base # sh_offset
%ELF_sym>ELF_str # sh_length
00 00 00 00 # sh_link
00 00 00 00 # sh_info
01 00 00 00 # sh_1?
00 00 00 00 # sh_entsize
# @1e8
00 00 00 00 # align
00 00 00 00
# @1f0
00 00 00 00 # align
00 00 00 00
00 00 00 00
00 00 00 00
# @200
:ELF_text
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_ALLOCA_H #define __MES_ALLOCA_H 1 #if SYSTEM_LIBC #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #undef __MES_ALLOCA_H #include_next <alloca.h> #else // ! SYSTEM_LIBC #include <sys/types.h> #if _ALLOCA_UNSIGNED void *alloca (unsigned size); #elif _ALLOCA_CHAR char *alloca (int); #else void *alloca (size_t size); #endif #endif // ! SYSTEM_LIBC #endif // __MES_ALLOCA_H
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_ARGZ_H #define __MES_ARGZ_H 1 #if SYSTEM_LIBC #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #undef __MES_ARGZ_H #include_next <argz.h> #else // ! SYSTEM_LIBC #include <mes/lib-mini.h> size_t __argz_count (char const *argz, size_t len); void __argz_extract (char const *argz, size_t len, char **argv); size_t __argz_extract_count (char const *argz, size_t len, char **argv); #endif // ! SYSTEM_LIBC #endif // __MES_ARGZ_H
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright (C) 1996 Free Software Foundation, Inc.
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __MES_AR_H
#define __MES_AR_H 1
#if SYSTEM_LIBC
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#undef __MES_AR_H
#include_next <ar.h>
#else // ! SYSTEM_LIBC
// Taken from GNU C Library 2.2.5
/* Archive files start with the ARMAG identifying string. Then follows a
`struct ar_hdr', and as many bytes of member file data as its `ar_size'
member indicates, for each member file. */
#define ARMAG "!<arch>\n" /* String that begins an archive file. */
#define SARMAG 8 /* Size of that string. */
#define ARFMAG "`\n" /* String in ar_fmag at end of each header. */
struct ar_hdr
{
char ar_name[16]; /* Member file name, sometimes / terminated. */
char ar_date[12]; /* File date, decimal seconds since Epoch. */
char ar_uid[6], ar_gid[6]; /* User and group IDs, in ASCII decimal. */
char ar_mode[8]; /* File mode, in ASCII octal. */
char ar_size[10]; /* File size, in ASCII decimal. */
char ar_fmag[2]; /* Always contains ARFMAG. */
};
#endif // ! SYSTEM_LIBC
#endif // __MES_ARGZ_H
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright (C) 1991, 1992 Free Software Foundation, Inc.
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __MES_DIRENT_H
#define __MES_DIRENT_H 1
#if SYSTEM_LIBC
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#undef __MES_DIRENT_H
#include_next <dirent.h>
#else // ! SYSTEM_LIBC
#include <dirstream.h>
// Taken from GNU C Library 1.06.4, 2.2.5
/*
* POSIX Standard: 5.1.2 Directory Operations <dirent.h>
*/
#include <stddef.h>
int __getdirentries (int filedes, char *buffer, size_t nbytes, off_t * basep);
struct dirent
{
ino_t d_ino;
off_t d_off;
unsigned short int d_reclen;
#if 0
unsigned char d_type;
#endif
char d_name[256]; /* We must not include limits.h! */
};
/* Open a directory stream on NAME.
Return a DIR stream on the directory, or NULL if it could not be opened. */
DIR *opendir (char const *name);
/* Close the directory stream DIRP.
Return 0 if successful, -1 if not. */
int closedir (DIR * dirp);
/* Read a directory entry from DIRP.
Return a pointer to a `struct dirent' describing the entry,
or NULL for EOF or error. The storage returned may be overwritten
by a later readdir call on the same DIR stream. */
struct dirent *readdir (DIR * dirp);
/* Rewind DIRP to the beginning of the directory. */
extern void rewinddir (DIR * dirp);
#endif // ! SYSTEM_LIBC
#endif // __MES_DIRENT_H
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
*Copyright (C) 1993, 1995, 1996 Free Software Foundation, Inc.
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __MES_DIRSTREAM_H
#define __MES_DIRSTREAM_H 1
#if SYSTEM_LIBC && HAVE_DIRSTREAM_H
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#undef __MES_DIRSTREAM_H
#include_next <dirstream.h>
#else // ! SYSTEM_LIBC
#include <sys/types.h>
// Taken from GNU C Library 2.2.5
/* Directory stream type. */
struct __dirstream
{
int fd; /* File descriptor. */
char *data; /* Directory block. */
size_t allocation; /* Space allocated for the block. */
size_t size; /* Total valid data in the block. */
size_t offset; /* Current offset into the block. */
off_t filepos; /* Position of next entry to read. */
};
typedef struct __dirstream DIR;
#endif // ! SYSTEM_LIBC
#endif // __MES_DIRSTREAM_H
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_DLFCN_H #define __MES_DLFCN_H 1 #if SYSTEM_LIBC #undef __MES_DLFCN_H #include_next <dlfcn.h> #else // ! SYSTEM_LIBC #define RTLD_LAZY 0x00001 #define RTLD_NOW 0x00002 #define RTLD_BINDING_MASK 0x3 #define RTLD_NOLOAD 0x00004 #define RTLD_DEEPBIND 0x00008 #define RTLD_GLOBAL 0x00100 #define RTLD_LOCAL 0 #define RTLD_NODELETE 0x01000 #define RTLD_DEFAULT 0 void *dlopen (char const *filename, int flags); int dlclose (void *handle); #endif // ! SYSTEM_LIBC #endif // __MES_DLFCN_H
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_ENDIAN_H #define __MES_ENDIAN_H 1 #if SYSTEM_LIBC #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #undef __MES_ENDIAN_H #include_next <endian.h> #else // ! SYSTEM_LIBC #define __LITTLE_ENDIAN 1234 #define __BIG_ENDIAN 4321 #define __BYTE_ORDER __LITTLE_ENDIAN #endif // ! SYSTEM_LIBC #endif // __MES_ENDIAN_H
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_FEATURES_H #define __MES_FEATURES_H 1 #if SYSTEM_LIBC #undef __MES_FEATURES_H #include_next <features.h> #endif // (SYSTEM_LIBC) #endif // __MES_FEATURES_H
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_FLOAT_H #define __MES_FLOAT_H 1 #if SYSTEM_LIBC #undef __MES_FLOAT_H #include_next <float.h> #else // ! SYSTEM_LIBC #define MIN_EXP -1021 #define DBL_MIN_EXP -1021 #define LDBL_MIN_EXP -1021 #endif // ! SYSTEM_LIBC #endif // __MES_FLOAT_H
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __MES_GETOPT_H
#define __MES_GETOPT_H 1
#if SYSTEM_LIBC
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#undef __MES_GETOPT_H
#include_next <getopt.h>
#else // ! SYSTEM_LIBC
#include <endian.h>
int isdigit (int);
int isxdigit (int);
char *optarg;
int optind;
int opterr;
struct option
{
char const *name;
int has_arg;
int *flag;
int val;
};
enum _argtype
{
no_argument,
required_argument,
optional_argument
};
int getopt (int argc, char *const *argv, char const *options);
int getopt_long (int argc, char *const *argv, char const *options,
struct option const *long_options, int *opt_index);
int getopt_long_only (int argc, char *const *argv, char const *options,
struct option const *long_options, int *opt_index);
#endif // ! SYSTEM_LIBC
#endif // __MES_GETOPT_H
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __MES_GRP_H
#define __MES_GRP_H 1
#if SYSTEM_LIBC
#undef __MES_GRP_H
#include_next <grp.h>
#else // ! SYSTEM_LIBC
#include <sys/types.h>
struct group
{
char *gr_name;
gid_t gr_gid;
char **gr_mem;
};
struct group *getgrent (void);
void endgrent (void);
void setgrent (void);
struct group *getgrgid (gid_t gid);
struct group *getgrnam (char const *name);
#endif // ! SYSTEM_LIBC
#endif // __MES_GRP_H
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_INTTYPES_H #define __MES_INTTYPES_H 1 #if SYSTEM_LIBC #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #undef __MES_INTTYPES_H #include_next <inttypes.h> #else // ! SYSTEM_LIBC #include <stdint.h> #endif // ! SYSTEM_LIBC #endif // __MES_INTTYPES_H
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_LIBGEN_H #define __MES_LIBGEN_H 1 #if SYSTEM_LIBC #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #undef __MES_LIBGEN_H #include_next <libgen.h> #else // ! SYSTEM_LIBC char *dirname (char *); #endif // ! SYSTEM_LIBC #endif // __MES_LIBGEN_H
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_LOCALE_H #define __MES_LOCALE_H 1 #if SYSTEM_LIBC #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #undef __MES_LOCALE_H #include_next <locale.h> #else // ! SYSTEM_LIBC // *INDENT-OFF* #ifndef LC_ALL #define LC_CTYPE 0 #define LC_NUMERIC 1 #define LC_COLLATE 3 #define LC_ALL 6 #endif // *INDENT-ON* char *setlocale (int category, char const *locale); #endif // ! SYSTEM_LIBC #endif // __MES_LOCALE_H
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_MEMORY_H #define __MES_MEMORY_H 1 #if SYSTEM_LIBC #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #undef __MES_MEMORY_H #include_next <memory.h> #else // ! SYSTEM_LIBC #include <string.h> #endif // ! SYSTEM_LIBC #endif // __MES_MEMORY_H
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __MES_PWD_H
#define __MES_PWD_H 1
#if SYSTEM_LIBC
#undef __MES_PWD_H
#include_next <pwd.h>
#else // ! SYSTEM_LIBC
#include <sys/types.h>
struct passwd
{
char *pw_name;
char *pw_passwd;
uid_t pw_uid;
gid_t pw_gid;
char *pw_gecos;
char *pw_dir;
char *pw_shell;
};
struct passwd *getpwuid ();
#endif // ! SYSTEM_LIBC
#endif // __MES_PWD_H
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_STDBOOL_H #define __MES_STDBOOL_H 1 #if SYSTEM_LIBC #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #undef __MES_STDBOOL_H #include_next <stdbool.h> #else // ! SYSTEM_LIBC typedef int bool; #define false 0 #define true 1 #endif // ! SYSTEM_LIBC #endif // __MES_STDBOOL_H
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_STDNORETURN_H #define __MES_STDNORETURN_H 1 #if SYSTEM_LIBC #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #undef __MES_STDNORETURN_H #include_next <stdnoreturn.h> #else // ! SYSTEM_LIBC // whut? #endif // ! SYSTEM_LIBC #endif // __MES_STDNORETURN_H
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_STRINGS_H #define __MES_STRINGS_H 1 #if SYSTEM_LIBC #undef __MES_STRINGS_H #include_next <strings.h> #endif // (SYSTEM_LIBC) #endif // __MES_STRINGS_H
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_SYS_CDEFS_H #define __MES_SYS_CDEFS_H 1 #if SYSTEM_LIBC #undef __MES_SYS_CDEFS_H #include_next <sys/cdefs.h> #endif // (SYSTEM_LIBC) #endif // __MES_SYS_CDEFS_H
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_SYS_DIR_H #define __MES_SYS_DIR_H 1 #if SYSTEM_LIBC #undef __MES_SYS_DIR_H #include_next <sys/dir.h> #else // ! SYSTEM_LIBC #endif // ! SYSTEM_LIBC #endif // __MES_SYS_DIR_H
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_SYS_FILE_H #define __MES_SYS_FILE_H 1 #if SYSTEM_LIBC #undef __MES_SYS_FILE_H #include_next <sys/file.h> #else // ! SYSTEM_LIBC #endif // ! SYSTEM_LIBC #endif // __MES_SYS_FILE_H
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_SYS_PARAM_H #define __MES_SYS_PARAM_H 1 #if SYSTEM_LIBC #undef __MES_SYS_PARAM_H #include_next <sys/param.h> #else // ! SYSTEM_LIBC #endif // ! SYSTEM_LIBC #endif // __MES_SYS_PARAM_H
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_SYS_SELECT_H #define __MES_SYS_SELECT_H 1 #if SYSTEM_LIBC #undef __MES_SYS_SELECT_H #include_next <sys/select.h> #else //! SYSTEM_LIBC typedef int fd_set; #endif //! SYSTEM_LIBC #endif // __MES_SYS_SELECT_H
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_SYS_TIMEB_H #define __MES_SYS_TIMEB_H 1 #if SYSTEM_LIBC #undef __MES_SYS_TIMEB_H #include_next <sys/timeb.h> #endif // (SYSTEM_LIBC) #endif // __MES_SYS_TIMEB_H
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __MES_SYS_TIMES_H
#define __MES_SYS_TIMES_H 1
#if SYSTEM_LIBC
#undef __MES_SYS_TIMES_H
#include_next <sys/times.h>
#else // ! SYSTEM_LIBC
#ifndef __MES_CLOCK_T
#define __MES_CLOCK_T
#undef clock_t
typedef long clock_t;
#endif
#ifndef CLOCKS_PER_SEC
#define CLOCKS_PER_SEC 1000000
#endif
#ifndef HZ
#define HZ 100
#endif
struct tms
{
clock_t tms_utime;
clock_t tms_stime;
clock_t tms_cutime;
clock_t tms_cstime;
};
#endif // ! SYSTEM_LIBC
#endif // __MES_SYS_TIMES_H
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __MES_SYS_UCONTEXT_H #define __MES_SYS_UCONTEXT_H 1 #if SYSTEM_LIBC #undef __MES_SYS_UCONTEXT_H #include_next <sys/ucontext.h> #endif // (SYSTEM_LIBC) #endif // __MES_SYS_UCONTEXT_H
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __MES_SYS_USER_H
#define __MES_SYS_USER_H 1
#if SYSTEM_LIBC
#undef __MES_SYS_USER_H
#include_next <sys/user.h>
#else // ! SYSTEM_LIBC
/* These are the 32-bit x86 structures. */
struct user_fpregs_struct
{
long int cwd;
long int swd;
long int twd;
long int fip;
long int fcs;
long int foo;
long int fos;
long int st_space[20];
};
struct user_fpxregs_struct
{
unsigned short int cwd;
unsigned short int swd;
unsigned short int twd;
unsigned short int fop;
long int fip;
long int fcs;
long int foo;
long int fos;
long int mxcsr;
long int reserved;
long int st_space[32]; /* 8*16 bytes for each FP-reg = 128 bytes */
long int xmm_space[32]; /* 8*16 bytes for each XMM-reg = 128 bytes */
long int padding[56];
};
struct user_regs_struct
{
long int ebx;
long int ecx;
long int edx;
long int esi;
long int edi;
long int ebp;
long int eax;
long int xds;
long int xes;
long int xfs;
long int xgs;
long int orig_eax;
long int eip;
long int xcs;
long int eflags;
long int esp;
long int xss;
};
// *INDENT-OFF*
struct user
{
struct user_regs_struct regs;
int u_fpvalid;
struct user_fpregs_struct i387;
unsigned long int u_tsize;
unsigned long int u_dsize;
unsigned long int u_ssize;
unsigned long int start_code;
unsigned long int start_stack;
long int signal;
int reserved;
struct user_regs_struct *u_ar0;
struct user_fpregs_struct *u_fpstate;
unsigned long int magic;
char u_comm [32];
int u_debugreg [8];
};
#define PAGE_SHIFT 12
#define PAGE_SIZE (1UL << PAGE_SHIFT)
#define PAGE_MASK (~(PAGE_SIZE-1))
#define NBPG PAGE_SIZE
#define UPAGES 1
#define HOST_TEXT_START_ADDR (u.start_code)
#define HOST_STACK_END_ADDR (u.start_stack + u.u_ssize * NBPG)
// *INDENT-ON*
#endif // ! SYSTEM_LIBC
#endif // __MES_SYS_USER_H
b2a48b2724a7b791df66efc2384a405a91d26579459a6797514dec89c2e76658 /usr/bin/mes 148ce096422535832802494f1128c26b6580cf3b66635e56abb33f7b3fc26043 /usr/bin/mes-m2 17c86665c2f925d88e3c1f827f312bbc7575b9e6006ef57833e895b6e73b8087 /usr/bin/mescc.scm 4feafab424611c976dc4628aa862e37b4e92b5869aebbcf7655e684c3f589041 /usr/lib/x86-mes/crt1.s 69e9ec2ee2ebf065575cdaf878629178b85330257dde0da790a428c0280d1ecb /usr/lib/x86-mes/crt1.o c9944a799d584abfa76f385c14ac0caf6f46d03b34bf2712493602b12826c6b2 /usr/lib/x86-mes/x86.M1 d8646707db6aa2a76fdc5dbb3521376439e357f9f1de1d67f02a1afeefd342ac /usr/lib/x86-mes/libmescc.s 7ec49fbcbc70b49648150b021a2ff9ab2b27e89eb8815919a7ffabe375ab1edc /usr/lib/x86-mes/libc+tcc.s b935d4c30213b49d899ab8bccce33117057ea0f3a1a8d3afc5e07fb8ce4590db /usr/lib/x86-mes/libc.s 52f697278ccdff5e457f27e10f465a91ab9858f0c6cee0683831cadb3109bbb7 /usr/lib/x86-mes/libmescc.a 840ee884db456e1982f40bce82209515a8e0b09bc404c4bc2a26d237bfa4d9ca /usr/lib/x86-mes/libc+tcc.a 32be26479096c52dbffe9c8bf14765f753d6882c3ae43760eda03406c0cf2539 /usr/lib/x86-mes/libc.a b16ab368bc4c7b8bd896d03cba565a60e97760dea4da9f5c8a1a3d2902a79df6 /usr/lib/linux/x86-mes/elf32-header.hex2 f9873d9aab12e70f24d97f8319e17a1e698ca60779ae9a6ab3ede648cd60fc61 /usr/lib/linux/x86-mes/elf32-footer-single-main.hex2
https://lilypond.org/janneke/tcc/tcc-0.9.26-1136-g5bba73cc.tar.gz 23cacd448cff2baf6ed76c2d1e2d654ff4e557046e311dfb6be7e1c631014ef8 tcc-0.9.26.tar.gz
/* -------------------------------------------------------------- */
/*
* TCC - Tiny C Compiler
*
* tcctools.c - extra tools and and -m32/64 support
*
*/
/* -------------------------------------------------------------- */
/*
* This program is for making libtcc1.a without ar
* tiny_libmaker - tiny elf lib maker
* usage: tiny_libmaker [lib] files...
* Copyright (c) 2007 Timppa
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef _TCC_H
#include "tcc.h"
#endif
//#define ARMAG "!<arch>\n"
#define ARFMAG "`\n"
typedef struct {
char ar_name[16];
char ar_date[12];
char ar_uid[6];
char ar_gid[6];
char ar_mode[8];
char ar_size[10];
char ar_fmag[2];
} ArHdr;
static unsigned long le2belong(unsigned long ul) {
return ((ul & 0xFF0000)>>8)+((ul & 0xFF000000)>>24) +
((ul & 0xFF)<<24)+((ul & 0xFF00)<<8);
}
/* Returns 1 if s contains any of the chars of list, else 0 */
static int contains_any(const char *s, const char *list) {
const char *l;
for (; *s; s++) {
for (l = list; *l; l++) {
if (*s == *l)
return 1;
}
}
return 0;
}
static int ar_usage(int ret) {
fprintf(stderr, "usage: tcc -ar [rcsv] lib file...\n");
fprintf(stderr, "create library ([abdioptxN] not supported).\n");
return ret;
}
ST_FUNC int tcc_tool_ar(TCCState *s1, int argc, char **argv)
{
static ArHdr arhdr = {
"/ ",
" ",
"0 ",
"0 ",
"0 ",
" ",
ARFMAG
};
static ArHdr arhdro = {
" ",
" ",
"0 ",
"0 ",
"0 ",
" ",
ARFMAG
};
FILE *fi, *fh = NULL, *fo = NULL;
ElfW(Ehdr) *ehdr;
ElfW(Shdr) *shdr;
ElfW(Sym) *sym;
int i, fsize, i_lib, i_obj;
char *buf, *shstr, *symtab = NULL, *strtab = NULL;
int symtabsize = 0;//, strtabsize = 0;
char *anames = NULL;
int *afpos = NULL;
int istrlen, strpos = 0, fpos = 0, funccnt = 0, funcmax, hofs;
char tfile[260], stmp[20];
char *file, *name;
int ret = 2;
const char *ops_conflict = "habdioptxN"; // unsupported but destructive if ignored.
int verbose = 0;
i_lib = 0; i_obj = 0; // will hold the index of the lib and first obj
for (i = 1; i < argc; i++) {
const char *a = argv[i];
if (*a == '-' && strstr(a, "."))
ret = 1; // -x.y is always invalid (same as gnu ar)
if ((*a == '-') || (i == 1 && !strstr(a, "."))) { // options argument
if (contains_any(a, ops_conflict))
ret = 1;
if (strstr(a, "v"))
verbose = 1;
} else { // lib or obj files: don't abort - keep validating all args.
if (!i_lib) // first file is the lib
i_lib = i;
else if (!i_obj) // second file is the first obj
i_obj = i;
}
}
if (!i_obj) // i_obj implies also i_lib. we require both.
ret = 1;
if (ret == 1)
return ar_usage(ret);
if ((fh = fopen(argv[i_lib], "wb")) == NULL)
{
fprintf(stderr, "tcc: ar: can't open file %s \n", argv[i_lib]);
goto the_end;
}
sprintf(tfile, "%s.tmp", argv[i_lib]);
if ((fo = fopen(tfile, "wb+")) == NULL)
{
fprintf(stderr, "tcc: ar: can't create temporary file %s\n", tfile);
goto the_end;
}
funcmax = 250;
afpos = tcc_realloc(NULL, funcmax * sizeof *afpos); // 250 func
memcpy(&arhdro.ar_mode, "100666", 6);
// i_obj = first input object file
while (i_obj < argc)
{
if (*argv[i_obj] == '-') { // by now, all options start with '-'
i_obj++;
continue;
}
if ((fi = fopen(argv[i_obj], "rb")) == NULL) {
fprintf(stderr, "tcc: ar: can't open file %s \n", argv[i_obj]);
goto the_end;
}
if (verbose)
printf("a - %s\n", argv[i_obj]);
fseek(fi, 0, SEEK_END);
fsize = ftell(fi);
fseek(fi, 0, SEEK_SET);
buf = tcc_malloc(fsize + 1);
fread(buf, fsize, 1, fi);
fclose(fi);
// elf header
ehdr = (ElfW(Ehdr) *)buf;
if (ehdr->e_ident[4] != ELFCLASSW)
{
fprintf(stderr, "tcc: ar: Unsupported Elf Class: %s\n", argv[i_obj]);
goto the_end;
}
shdr = (ElfW(Shdr) *) (buf + ehdr->e_shoff + ehdr->e_shstrndx * ehdr->e_shentsize);
shstr = (char *)(buf + shdr->sh_offset);
for (i = 0; i < ehdr->e_shnum; i++)
{
shdr = (ElfW(Shdr) *) (buf + ehdr->e_shoff + i * ehdr->e_shentsize);
if (!shdr->sh_offset)
continue;
if (shdr->sh_type == SHT_SYMTAB)
{
symtab = (char *)(buf + shdr->sh_offset);
symtabsize = shdr->sh_size;
}
if (shdr->sh_type == SHT_STRTAB)
{
if (!strcmp(shstr + shdr->sh_name, ".strtab"))
{
strtab = (char *)(buf + shdr->sh_offset);
//strtabsize = shdr->sh_size;
}
}
}
if (symtab && symtabsize)
{
int nsym = symtabsize / sizeof(ElfW(Sym));
//printf("symtab: info size shndx name\n");
for (i = 1; i < nsym; i++)
{
sym = (ElfW(Sym) *) (symtab + i * sizeof(ElfW(Sym)));
if (sym->st_shndx &&
(sym->st_info == 0x10
|| sym->st_info == 0x11
|| sym->st_info == 0x12
)) {
//printf("symtab: %2Xh %4Xh %2Xh %s\n", sym->st_info, sym->st_size, sym->st_shndx, strtab + sym->st_name);
istrlen = strlen(strtab + sym->st_name)+1;
anames = tcc_realloc(anames, strpos+istrlen);
strcpy(anames + strpos, strtab + sym->st_name);
strpos += istrlen;
if (++funccnt >= funcmax) {
funcmax += 250;
afpos = tcc_realloc(afpos, funcmax * sizeof *afpos); // 250 func more
}
afpos[funccnt] = fpos;
}
}
}
file = argv[i_obj];
for (name = strchr(file, 0);
name > file && name[-1] != '/' && name[-1] != '\\';
--name);
istrlen = strlen(name);
if (istrlen >= sizeof(arhdro.ar_name))
istrlen = sizeof(arhdro.ar_name) - 1;
memset(arhdro.ar_name, ' ', sizeof(arhdro.ar_name));
memcpy(arhdro.ar_name, name, istrlen);
arhdro.ar_name[istrlen] = '/';
sprintf(stmp, "%-10d", fsize);
memcpy(&arhdro.ar_size, stmp, 10);
fwrite(&arhdro, sizeof(arhdro), 1, fo);
fwrite(buf, fsize, 1, fo);
tcc_free(buf);
i_obj++;
fpos += (fsize + sizeof(arhdro));
}
hofs = 8 + sizeof(arhdr) + strpos + (funccnt+1) * sizeof(int);
fpos = 0;
if ((hofs & 1)) // align
hofs++, fpos = 1;
// write header
fwrite("!<arch>\n", 8, 1, fh);
sprintf(stmp, "%-10d", (int)(strpos + (funccnt+1) * sizeof(int)));
memcpy(&arhdr.ar_size, stmp, 10);
fwrite(&arhdr, sizeof(arhdr), 1, fh);
afpos[0] = le2belong(funccnt);
for (i=1; i<=funccnt; i++)
afpos[i] = le2belong(afpos[i] + hofs);
fwrite(afpos, (funccnt+1) * sizeof(int), 1, fh);
fwrite(anames, strpos, 1, fh);
if (fpos)
fwrite("", 1, 1, fh);
// write objects
fseek(fo, 0, SEEK_END);
fsize = ftell(fo);
fseek(fo, 0, SEEK_SET);
buf = tcc_malloc(fsize + 1);
fread(buf, fsize, 1, fo);
fwrite(buf, fsize, 1, fh);
tcc_free(buf);
ret = 0;
the_end:
if (anames)
tcc_free(anames);
if (afpos)
tcc_free(afpos);
if (fh)
fclose(fh);
if (fo)
fclose(fo), remove(tfile);
return ret;
}
/* -------------------------------------------------------------- */
/*
* tiny_impdef creates an export definition file (.def) from a dll
* on MS-Windows. Usage: tiny_impdef library.dll [-o outputfile]"
*
* Copyright (c) 2005,2007 grischka
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifdef TCC_TARGET_PE
ST_FUNC int tcc_tool_impdef(TCCState *s1, int argc, char **argv)
{
int ret, v, i;
char infile[260];
char outfile[260];
const char *file;
char *p, *q;
FILE *fp, *op;
#ifdef _WIN32
char path[260];
#endif
infile[0] = outfile[0] = 0;
fp = op = NULL;
ret = 1;
p = NULL;
v = 0;
for (i = 1; i < argc; ++i) {
const char *a = argv[i];
if ('-' == a[0]) {
if (0 == strcmp(a, "-v")) {
v = 1;
} else if (0 == strcmp(a, "-o")) {
if (++i == argc)
goto usage;
strcpy(outfile, argv[i]);
} else
goto usage;
} else if (0 == infile[0])
strcpy(infile, a);
else
goto usage;
}
if (0 == infile[0]) {
usage:
fprintf(stderr,
"usage: tcc -impdef library.dll [-v] [-o outputfile]\n"
"create export definition file (.def) from dll\n"
);
goto the_end;
}
if (0 == outfile[0]) {
strcpy(outfile, tcc_basename(infile));
q = strrchr(outfile, '.');
if (NULL == q)
q = strchr(outfile, 0);
strcpy(q, ".def");
}
file = infile;
#ifdef _WIN32
if (SearchPath(NULL, file, ".dll", sizeof path, path, NULL))
file = path;
#endif
ret = tcc_get_dllexports(file, &p);
if (ret || !p) {
fprintf(stderr, "tcc: impdef: %s '%s'\n",
ret == 32 ? "can't read symbols from 32bit" :
ret == 64 ? "can't read symbols from 64bit" :
ret == -1 ? "can't find file" :
ret == 0 ? "no symbols found in" :
"unknown file type", file);
ret = 1;
goto the_end;
}
if (v)
printf("-> %s\n", file);
op = fopen(outfile, "w");
if (NULL == op) {
fprintf(stderr, "tcc: impdef: could not create output file: %s\n", outfile);
goto the_end;
}
fprintf(op, "LIBRARY %s\n\nEXPORTS\n", tcc_basename(file));
for (q = p, i = 0; *q; ++i) {
fprintf(op, "%s\n", q);
q += strlen(q) + 1;
}
if (v)
printf("<- %s (%d symbol%s)\n", outfile, i, &"s"[i<2]);
ret = 0;
the_end:
/* cannot free memory received from tcc_get_dllexports
if it came from a dll */
/* if (p)
tcc_free(p); */
if (fp)
fclose(fp);
if (op)
fclose(op);
return ret;
}
#endif /* TCC_TARGET_PE */
/* -------------------------------------------------------------- */
/*
* TCC - Tiny C Compiler
*
* Copyright (c) 2001-2004 Fabrice Bellard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* re-execute the i386/x86_64 cross-compilers with tcc -m32/-m64: */
#if !defined TCC_TARGET_I386 && !defined TCC_TARGET_X86_64
ST_FUNC void tcc_tool_cross(TCCState *s, char **argv, int option)
{
tcc_error("-m%d not implemented.", option);
}
#else
#ifdef _WIN32
#include <process.h>
static char *str_replace(const char *str, const char *p, const char *r)
{
const char *s, *s0;
char *d, *d0;
int sl, pl, rl;
sl = strlen(str);
pl = strlen(p);
rl = strlen(r);
for (d0 = NULL;; d0 = tcc_malloc(sl + 1)) {
for (d = d0, s = str; s0 = s, s = strstr(s, p), s; s += pl) {
if (d) {
memcpy(d, s0, sl = s - s0), d += sl;
memcpy(d, r, rl), d += rl;
} else
sl += rl - pl;
}
if (d) {
strcpy(d, s0);
return d0;
}
}
}
static int execvp_win32(const char *prog, char **argv)
{
int ret; char **p;
/* replace all " by \" */
for (p = argv; *p; ++p)
if (strchr(*p, '"'))
*p = str_replace(*p, "\"", "\\\"");
ret = _spawnvp(P_NOWAIT, prog, (const char *const*)argv);
if (-1 == ret)
return ret;
_cwait(&ret, ret, WAIT_CHILD);
exit(ret);
}
#define execvp execvp_win32
#endif /* _WIN32 */
ST_FUNC void tcc_tool_cross(TCCState *s, char **argv, int target)
{
char program[4096];
char *a0 = argv[0];
int prefix = tcc_basename(a0) - a0;
snprintf(program, sizeof program,
"%.*s%s"
#ifdef TCC_TARGET_PE
"-win32"
#endif
"-tcc"
#ifdef _WIN32
".exe"
#endif
, prefix, a0, target == 64 ? "x86_64" : "i386");
if (strcmp(a0, program))
execvp(argv[0] = program, argv);
tcc_error("could not run '%s'", program);
}
#endif /* TCC_TARGET_I386 && TCC_TARGET_X86_64 */
/* -------------------------------------------------------------- */
/* enable commandline wildcard expansion (tcc -o x.exe *.c) */
#ifdef _WIN32
int _CRT_glob = 1;
#ifndef _CRT_glob
int _dowildcard = 1;
#endif
#endif
/* -------------------------------------------------------------- */
/* generate xxx.d file */
ST_FUNC void gen_makedeps(TCCState *s, const char *target, const char *filename)
{
FILE *depout;
char buf[1024];
int i;
if (!filename) {
/* compute filename automatically: dir/file.o -> dir/file.d */
snprintf(buf, sizeof buf, "%.*s.d",
(int)(tcc_fileextension(target) - target), target);
filename = buf;
}
if (s->verbose)
printf("<- %s\n", filename);
/* XXX return err codes instead of error() ? */
depout = fopen(filename, "w");
if (!depout)
tcc_error("could not open '%s'", filename);
fprintf(depout, "%s: \\\n", target);
for (i=0; i<s->nb_target_deps; ++i)
fprintf(depout, " %s \\\n", s->target_deps[i]);
fprintf(depout, "\n");
fclose(depout);
}
/* -------------------------------------------------------------- */
if (ret == 1)
return ar_usage(ret);
if ((fh = fopen(argv[i_lib], "wb")) == NULL)
{
fprintf(stderr, "tcc: ar: can't open file %s \n", argv[i_lib]);
goto the_end;
}
if (ret == 1)
return ar_usage(ret);
// write header
if ((fh = fopen(argv[i_lib], "wb")) == NULL)
{
fprintf(stderr, "tcc: ar: can't open file %s \n", argv[i_lib]);
goto the_end;
}
// write header
/*
* TCC - Tiny C Compiler
*
* Copyright (c) 2001-2004 Fabrice Bellard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef ONE_SOURCE
#include "libtcc.c"
#else
#include "tcc.h"
#endif
#include "tcctools.c"
static const char help[] =
"Tiny C Compiler "TCC_VERSION" - Copyright (C) 2001-2006 Fabrice Bellard\n"
"Usage: tcc [options...] [-o outfile] [-c] infile(s)...\n"
" tcc [options...] -run infile [arguments...]\n"
"General options:\n"
" -c compile only - generate an object file\n"
" -o outfile set output filename\n"
" -run run compiled source\n"
" -fflag set or reset (with 'no-' prefix) 'flag' (see tcc -hh)\n"
" -Wwarning set or reset (with 'no-' prefix) 'warning' (see tcc -hh)\n"
" -w disable all warnings\n"
" -v -vv show version, show search paths or loaded files\n"
" -h -hh show this, show more help\n"
" -bench show compilation statistics\n"
" - use stdin pipe as infile\n"
" @listfile read arguments from listfile\n"
"Preprocessor options:\n"
" -Idir add include path 'dir'\n"
" -Dsym[=val] define 'sym' with value 'val'\n"
" -Usym undefine 'sym'\n"
" -E preprocess only\n"
"Linker options:\n"
" -Ldir add library path 'dir'\n"
" -llib link with dynamic or static library 'lib'\n"
" -r generate (relocatable) object file\n"
" -shared generate a shared library/dll\n"
" -rdynamic export all global symbols to dynamic linker\n"
" -soname set name for shared library to be used at runtime\n"
" -Wl,-opt[=val] set linker option (see tcc -hh)\n"
"Debugger options:\n"
" -g generate runtime debug info\n"
#ifdef CONFIG_TCC_BCHECK
" -b compile with built-in memory and bounds checker (implies -g)\n"
#endif
#ifdef CONFIG_TCC_BACKTRACE
" -bt N show N callers in stack traces\n"
#endif
"Misc. options:\n"
" -x[c|a|n] specify type of the next infile\n"
" -nostdinc do not use standard system include paths\n"
" -nostdlib do not link with standard crt and libraries\n"
" -Bdir set tcc's private include/library dir\n"
" -MD generate dependency file for make\n"
" -MF file specify dependency file name\n"
" -m32/64 defer to i386/x86_64 cross compiler\n"
"Tools:\n"
" create library : tcc -ar [rcsv] lib.a files\n"
#ifdef TCC_TARGET_PE
" create def file : tcc -impdef lib.dll [-v] [-o lib.def]\n"
#endif
;
static const char help2[] =
"Tiny C Compiler "TCC_VERSION" - More Options\n"
"Special options:\n"
" -P -P1 with -E: no/alternative #line output\n"
" -dD -dM with -E: output #define directives\n"
" -pthread same as -D_REENTRANT and -lpthread\n"
" -On same as -D__OPTIMIZE__ for n > 0\n"
" -Wp,-opt same as -opt\n"
" -include file include 'file' above each input file\n"
" -isystem dir add 'dir' to system include path\n"
" -iwithprefix dir set tcc's private include/library subdir\n"
" -static link to static libraries (not recommended)\n"
" -dumpversion print version\n"
" -print-search-dirs print search paths\n"
"Ignored options:\n"
" --param -pedantic -pipe -s -std -traditional\n"
"-W... warnings:\n"
" all turn on some (*) warnings\n"
" error stop after first warning\n"
" unsupported warn about ignored options, pragmas, etc.\n"
" write-strings strings are const\n"
" implicit-function-declaration warn for missing prototype (*)\n"
"-f[no-]... flags:\n"
" unsigned-char default char is unsigned\n"
" signed-char default char is signed\n"
" common use common section instead of bss\n"
" leading-underscore decorate extern symbols\n"
" ms-extensions allow anonymous struct in struct\n"
" dollars-in-identifiers allow '$' in C symbols\n"
"-m... target specific options:\n"
" ms-bitfields use MSVC bitfield layout\n"
#ifdef TCC_TARGET_ARM
" float-abi hard/softfp on arm\n"
#endif
#ifdef TCC_TARGET_X86_64
" no-sse disable floats on x86_64\n"
#endif
"-Wl,... linker options:\n"
" -nostdlib do not link with standard crt/libs\n"
" -[no-]whole-archive load lib(s) fully/only as needed\n"
" -export-all-symbols same as -rdynamic\n"
" -image-base= -Ttext= set base address of executable\n"
" -section-alignment= set section alignment in executable\n"
#ifdef TCC_TARGET_PE
" -file-alignment= set PE file alignment\n"
" -stack= set PE stack reserve\n"
" -large-address-aware set related PE option\n"
" -subsystem=[console/windows] set PE subsystem\n"
" -oformat=[pe-* binary] set executable output format\n"
"Predefined macros:\n"
" tcc -E -dM - < nul\n"
#else
" -rpath= set dynamic library search path\n"
" -enable-new-dtags set DT_RUNPATH instead of DT_RPATH\n"
" -soname= set DT_SONAME elf tag\n"
" -Bsymbolic set DT_SYMBOLIC elf tag\n"
" -oformat=[elf32/64-* binary] set executable output format\n"
" -init= -fini= -as-needed -O (ignored)\n"
"Predefined macros:\n"
" tcc -E -dM - < /dev/null\n"
#endif
"See also the manual for more details.\n"
;
static const char version[] =
"tcc version "TCC_VERSION" ("
#ifdef TCC_TARGET_I386
"i386"
#elif defined TCC_TARGET_X86_64
"x86_64"
#elif defined TCC_TARGET_C67
"C67"
#elif defined TCC_TARGET_ARM
"ARM"
#elif defined TCC_TARGET_ARM64
"AArch64"
#endif
#ifdef TCC_ARM_HARDFLOAT
" Hard Float"
#endif
#ifdef TCC_TARGET_PE
" Windows"
#elif defined(TCC_TARGET_MACHO)
" Darwin"
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
" FreeBSD"
#else
" Linux"
#endif
")\n"
;
static void print_dirs(const char *msg, char **paths, int nb_paths)
{
int i;
printf("%s:\n%s", msg, nb_paths ? "" : " -\n");
for(i = 0; i < nb_paths; i++)
printf(" %s\n", paths[i]);
}
static void print_search_dirs(TCCState *s)
{
printf("install: %s\n", s->tcc_lib_path);
/* print_dirs("programs", NULL, 0); */
print_dirs("include", s->sysinclude_paths, s->nb_sysinclude_paths);
print_dirs("libraries", s->library_paths, s->nb_library_paths);
#ifndef TCC_TARGET_PE
print_dirs("crt", s->crt_paths, s->nb_crt_paths);
printf("libtcc1:\n %s/"TCC_LIBTCC1"\n", s->tcc_lib_path);
printf("elfinterp:\n %s\n", DEFAULT_ELFINTERP(s));
#endif
}
static void set_environment(TCCState *s)
{
char * path;
path = getenv("C_INCLUDE_PATH");
if(path != NULL) {
tcc_add_include_path(s, path);
}
path = getenv("CPATH");
if(path != NULL) {
tcc_add_include_path(s, path);
}
path = getenv("LIBRARY_PATH");
if(path != NULL) {
tcc_add_library_path(s, path);
}
}
static char *default_outputfile(TCCState *s, const char *first_file)
{
char buf[1024];
char *ext;
const char *name = "a";
if (first_file && strcmp(first_file, "-"))
name = tcc_basename(first_file);
snprintf(buf, sizeof(buf), "%s", name);
ext = tcc_fileextension(buf);
#ifdef TCC_TARGET_PE
if (s->output_type == TCC_OUTPUT_DLL)
strcpy(ext, ".dll");
else
if (s->output_type == TCC_OUTPUT_EXE)
strcpy(ext, ".exe");
else
#endif
if (s->output_type == TCC_OUTPUT_OBJ && !s->option_r && *ext)
strcpy(ext, ".o");
else
strcpy(buf, "a.out");
return tcc_strdup(buf);
}
static unsigned getclock_ms(void)
{
#ifdef _WIN32
return GetTickCount();
#else
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec*1000 + (tv.tv_usec+500)/1000;
#endif
}
int main(int argc, char **argv)
{
TCCState *s;
int ret, opt, n = 0;
unsigned start_time = 0;
const char *first_file;
redo:
s = tcc_new();
opt = tcc_parse_args(s, &argc, &argv, 1);
if (n == 0) {
if (opt == OPT_HELP)
return printf(help), 1;
if (opt == OPT_HELP2)
return printf(help2), 1;
if (opt == OPT_M32 || opt == OPT_M64)
tcc_tool_cross(s, argv, opt); /* never returns */
if (s->verbose)
printf(version);
if (opt == OPT_AR)
return tcc_tool_ar(s, argc, argv);
#ifdef TCC_TARGET_PE
if (opt == OPT_IMPDEF)
return tcc_tool_impdef(s, argc, argv);
#endif
if (opt == OPT_V)
return 0;
if (opt == OPT_PRINT_DIRS) {
/* initialize search dirs */
tcc_set_output_type(s, TCC_OUTPUT_MEMORY);
print_search_dirs(s);
return 0;
}
n = s->nb_files;
if (n == 0)
tcc_error("no input files\n");
if (s->output_type == TCC_OUTPUT_PREPROCESS) {
if (!s->outfile) {
s->ppfp = stdout;
} else {
s->ppfp = fopen(s->outfile, "w");
if (!s->ppfp)
tcc_error("could not write '%s'", s->outfile);
}
} else if (s->output_type == TCC_OUTPUT_OBJ && !s->option_r) {
if (s->nb_libraries)
tcc_error("cannot specify libraries with -c");
if (n > 1 && s->outfile)
tcc_error("cannot specify output file with -c many files");
} else {
if (s->option_pthread)
tcc_set_options(s, "-lpthread");
}
if (s->do_bench)
start_time = getclock_ms();
}
set_environment(s);
if (s->output_type == 0)
s->output_type = TCC_OUTPUT_EXE;
tcc_set_output_type(s, s->output_type);
/* compile or add each files or library */
for (first_file = NULL, ret = 0;;) {
struct filespec *f = s->files[s->nb_files - n];
s->filetype = f->type;
s->alacarte_link = f->alacarte;
if (f->type == AFF_TYPE_LIB) {
if (tcc_add_library_err(s, f->name) < 0)
ret = 1;
} else {
if (1 == s->verbose)
printf("-> %s\n", f->name);
if (!first_file)
first_file = f->name;
if (tcc_add_file(s, f->name) < 0)
ret = 1;
}
s->filetype = 0;
s->alacarte_link = 1;
if (ret || --n == 0
|| (s->output_type == TCC_OUTPUT_OBJ && !s->option_r))
break;
}
if (s->output_type == TCC_OUTPUT_PREPROCESS) {
if (s->outfile)
fclose(s->ppfp);
} else if (0 == ret) {
if (s->output_type == TCC_OUTPUT_MEMORY) {
#ifdef TCC_IS_NATIVE
ret = tcc_run(s, argc, argv);
#endif
} else {
if (!s->outfile)
s->outfile = default_outputfile(s, first_file);
if (tcc_output_file(s, s->outfile))
ret = 1;
else if (s->gen_deps)
gen_makedeps(s, s->outfile, s->deps_outfile);
}
}
if (s->do_bench && ret == 0 && n == 0)
tcc_print_stats(s, getclock_ms() - start_time);
tcc_delete(s);
if (ret == 0 && n)
goto redo; /* compile more files with -c */
return ret;
}
/*
* TCC - Tiny C Compiler
*
* Copyright (c) 2001-2004 Fabrice Bellard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _TCC_H
#include "tcc.h"
#endif
/********************************************************/
/* global variables */
/* use GNU C extensions */
ST_DATA int gnu_ext = 1;
/* use TinyCC extensions */
ST_DATA int tcc_ext = 1;
/* XXX: get rid of this ASAP */
ST_DATA struct TCCState *tcc_state;
static int nb_states;
/********************************************************/
#ifdef ONE_SOURCE
#include "tccpp.c"
#include "tccgen.c"
#include "tccelf.c"
#include "tccrun.c"
#ifdef TCC_TARGET_I386
#include "i386-gen.c"
#include "i386-link.c"
#include "i386-asm.c"
#endif
#ifdef TCC_TARGET_ARM
#include "arm-gen.c"
#include "arm-link.c"
#include "arm-asm.c"
#endif
#ifdef TCC_TARGET_ARM64
#include "arm64-gen.c"
#include "arm64-link.c"
#endif
#ifdef TCC_TARGET_C67
#include "c67-gen.c"
#include "c67-link.c"
#endif
#ifdef TCC_TARGET_X86_64
#include "x86_64-gen.c"
#include "x86_64-link.c"
#include "i386-asm.c"
#endif
#ifdef CONFIG_TCC_ASM
#include "tccasm.c"
#endif
#ifdef TCC_TARGET_COFF
#include "tcccoff.c"
#endif
#ifdef TCC_TARGET_PE
#include "tccpe.c"
#endif
#endif /* ONE_SOURCE */
/********************************************************/
#ifndef CONFIG_TCC_ASM
ST_FUNC void asm_instr(void)
{
tcc_error("inline asm() not supported");
}
ST_FUNC void asm_global_instr(void)
{
tcc_error("inline asm() not supported");
}
#endif
/********************************************************/
#ifdef _WIN32
ST_FUNC char *normalize_slashes(char *path)
{
char *p;
for (p = path; *p; ++p)
if (*p == '\\')
*p = '/';
return path;
}
static HMODULE tcc_module;
/* on win32, we suppose the lib and includes are at the location of 'tcc.exe' */
static void tcc_set_lib_path_w32(TCCState *s)
{
char path[1024], *p;
GetModuleFileNameA(tcc_module, path, sizeof path);
p = tcc_basename(normalize_slashes(strlwr(path)));
if (p - 5 > path && 0 == strncmp(p - 5, "/bin/", 5))
p -= 5;
else if (p > path)
p--;
*p = 0;
tcc_set_lib_path(s, path);
}
#ifdef TCC_TARGET_PE
static void tcc_add_systemdir(TCCState *s)
{
char buf[1000];
GetSystemDirectory(buf, sizeof buf);
tcc_add_library_path(s, normalize_slashes(buf));
}
#endif
#ifdef LIBTCC_AS_DLL
BOOL WINAPI DllMain (HINSTANCE hDll, DWORD dwReason, LPVOID lpReserved)
{
if (DLL_PROCESS_ATTACH == dwReason)
tcc_module = hDll;
return TRUE;
}
#endif
#endif
/********************************************************/
/* copy a string and truncate it. */
ST_FUNC char *pstrcpy(char *buf, int buf_size, const char *s)
{
char *q, *q_end;
int c;
if (buf_size > 0) {
q = buf;
q_end = buf + buf_size - 1;
while (q < q_end) {
c = *s++;
if (c == '\0')
break;
*q++ = c;
}
*q = '\0';
}
return buf;
}
/* strcat and truncate. */
ST_FUNC char *pstrcat(char *buf, int buf_size, const char *s)
{
int len;
len = strlen(buf);
if (len < buf_size)
pstrcpy(buf + len, buf_size - len, s);
return buf;
}
ST_FUNC char *pstrncpy(char *out, const char *in, size_t num)
{
memcpy(out, in, num);
out[num] = '\0';
return out;
}
/* extract the basename of a file */
PUB_FUNC char *tcc_basename(const char *name)
{
char *p = strchr(name, 0);
while (p > name && !IS_DIRSEP(p[-1]))
--p;
return p;
}
/* extract extension part of a file
*
* (if no extension, return pointer to end-of-string)
*/
PUB_FUNC char *tcc_fileextension (const char *name)
{
char *b = tcc_basename(name);
char *e = strrchr(b, '.');
return e ? e : strchr(b, 0);
}
/********************************************************/
/* memory management */
#undef free
#undef malloc
#undef realloc
#ifndef MEM_DEBUG
PUB_FUNC void tcc_free(void *ptr)
{
free(ptr);
}
PUB_FUNC void *tcc_malloc(unsigned long size)
{
void *ptr;
ptr = malloc(size);
if (!ptr && size)
tcc_error("memory full (malloc)");
return ptr;
}
PUB_FUNC void *tcc_mallocz(unsigned long size)
{
void *ptr;
ptr = tcc_malloc(size);
memset(ptr, 0, size);
return ptr;
}
PUB_FUNC void *tcc_realloc(void *ptr, unsigned long size)
{
void *ptr1;
ptr1 = realloc(ptr, size);
if (!ptr1 && size)
tcc_error("memory full (realloc)");
return ptr1;
}
PUB_FUNC char *tcc_strdup(const char *str)
{
char *ptr;
ptr = tcc_malloc(strlen(str) + 1);
strcpy(ptr, str);
return ptr;
}
PUB_FUNC void tcc_memcheck(void)
{
}
#else
#define MEM_DEBUG_MAGIC1 0xFEEDDEB1
#define MEM_DEBUG_MAGIC2 0xFEEDDEB2
#define MEM_DEBUG_MAGIC3 0xFEEDDEB3
#define MEM_DEBUG_FILE_LEN 40
#define MEM_DEBUG_CHECK3(header) \
((mem_debug_header_t*)((char*)header + header->size))->magic3
#define MEM_USER_PTR(header) \
((char *)header + offsetof(mem_debug_header_t, magic3))
#define MEM_HEADER_PTR(ptr) \
(mem_debug_header_t *)((char*)ptr - offsetof(mem_debug_header_t, magic3))
struct mem_debug_header {
unsigned magic1;
unsigned size;
struct mem_debug_header *prev;
struct mem_debug_header *next;
int line_num;
char file_name[MEM_DEBUG_FILE_LEN + 1];
unsigned magic2;
__attribute__((aligned(16))) unsigned magic3;
};
typedef struct mem_debug_header mem_debug_header_t;
static mem_debug_header_t *mem_debug_chain;
static unsigned mem_cur_size;
static unsigned mem_max_size;
static mem_debug_header_t *malloc_check(void *ptr, const char *msg)
{
mem_debug_header_t * header = MEM_HEADER_PTR(ptr);
if (header->magic1 != MEM_DEBUG_MAGIC1 ||
header->magic2 != MEM_DEBUG_MAGIC2 ||
MEM_DEBUG_CHECK3(header) != MEM_DEBUG_MAGIC3 ||
header->size == (unsigned)-1) {
fprintf(stderr, "%s check failed\n", msg);
if (header->magic1 == MEM_DEBUG_MAGIC1)
fprintf(stderr, "%s:%u: block allocated here.\n",
header->file_name, header->line_num);
exit(1);
}
return header;
}
PUB_FUNC void *tcc_malloc_debug(unsigned long size, const char *file, int line)
{
int ofs;
mem_debug_header_t *header;
header = malloc(sizeof(mem_debug_header_t) + size);
if (!header)
tcc_error("memory full (malloc)");
header->magic1 = MEM_DEBUG_MAGIC1;
header->magic2 = MEM_DEBUG_MAGIC2;
header->size = size;
MEM_DEBUG_CHECK3(header) = MEM_DEBUG_MAGIC3;
header->line_num = line;
ofs = strlen(file) - MEM_DEBUG_FILE_LEN;
strncpy(header->file_name, file + (ofs > 0 ? ofs : 0), MEM_DEBUG_FILE_LEN);
header->file_name[MEM_DEBUG_FILE_LEN] = 0;
header->next = mem_debug_chain;
header->prev = NULL;
if (header->next)
header->next->prev = header;
mem_debug_chain = header;
mem_cur_size += size;
if (mem_cur_size > mem_max_size)
mem_max_size = mem_cur_size;
return MEM_USER_PTR(header);
}
PUB_FUNC void tcc_free_debug(void *ptr)
{
mem_debug_header_t *header;
if (!ptr)
return;
header = malloc_check(ptr, "tcc_free");
mem_cur_size -= header->size;
header->size = (unsigned)-1;
if (header->next)
header->next->prev = header->prev;
if (header->prev)
header->prev->next = header->next;
if (header == mem_debug_chain)
mem_debug_chain = header->next;
free(header);
}
PUB_FUNC void *tcc_mallocz_debug(unsigned long size, const char *file, int line)
{
void *ptr;
ptr = tcc_malloc_debug(size,file,line);
memset(ptr, 0, size);
return ptr;
}
PUB_FUNC void *tcc_realloc_debug(void *ptr, unsigned long size, const char *file, int line)
{
mem_debug_header_t *header;
int mem_debug_chain_update = 0;
if (!ptr)
return tcc_malloc_debug(size, file, line);
header = malloc_check(ptr, "tcc_realloc");
mem_cur_size -= header->size;
mem_debug_chain_update = (header == mem_debug_chain);
header = realloc(header, sizeof(mem_debug_header_t) + size);
if (!header)
tcc_error("memory full (realloc)");
header->size = size;
MEM_DEBUG_CHECK3(header) = MEM_DEBUG_MAGIC3;
if (header->next)
header->next->prev = header;
if (header->prev)
header->prev->next = header;
if (mem_debug_chain_update)
mem_debug_chain = header;
mem_cur_size += size;
if (mem_cur_size > mem_max_size)
mem_max_size = mem_cur_size;
return MEM_USER_PTR(header);
}
PUB_FUNC char *tcc_strdup_debug(const char *str, const char *file, int line)
{
char *ptr;
ptr = tcc_malloc_debug(strlen(str) + 1, file, line);
strcpy(ptr, str);
return ptr;
}
PUB_FUNC void tcc_memcheck(void)
{
if (mem_cur_size) {
mem_debug_header_t *header = mem_debug_chain;
fprintf(stderr, "MEM_DEBUG: mem_leak= %d bytes, mem_max_size= %d bytes\n",
mem_cur_size, mem_max_size);
while (header) {
fprintf(stderr, "%s:%u: error: %u bytes leaked\n",
header->file_name, header->line_num, header->size);
header = header->next;
}
#if MEM_DEBUG-0 == 2
exit(2);
#endif
}
}
#endif /* MEM_DEBUG */
#define free(p) use_tcc_free(p)
#define malloc(s) use_tcc_malloc(s)
#define realloc(p, s) use_tcc_realloc(p, s)
/********************************************************/
/* dynarrays */
ST_FUNC void dynarray_add(void *ptab, int *nb_ptr, void *data)
{
int nb, nb_alloc;
void **pp;
nb = *nb_ptr;
pp = *(void ***)ptab;
/* every power of two we double array size */
if ((nb & (nb - 1)) == 0) {
if (!nb)
nb_alloc = 1;
else
nb_alloc = nb * 2;
pp = tcc_realloc(pp, nb_alloc * sizeof(void *));
*(void***)ptab = pp;
}
pp[nb++] = data;
*nb_ptr = nb;
}
ST_FUNC void dynarray_reset(void *pp, int *n)
{
void **p;
for (p = *(void***)pp; *n; ++p, --*n)
if (*p)
tcc_free(*p);
tcc_free(*(void**)pp);
*(void**)pp = NULL;
}
static void tcc_split_path(TCCState *s, void *p_ary, int *p_nb_ary, const char *in)
{
const char *p;
do {
int c;
CString str;
cstr_new(&str);
for (p = in; c = *p, c != '\0' && c != PATHSEP; ++p) {
if (c == '{' && p[1] && p[2] == '}') {
c = p[1], p += 2;
if (c == 'B')
cstr_cat(&str, s->tcc_lib_path, -1);
} else {
cstr_ccat(&str, c);
}
}
if (str.size) {
cstr_ccat(&str, '\0');
dynarray_add(p_ary, p_nb_ary, tcc_strdup(str.data));
}
cstr_free(&str);
in = p+1;
} while (*p);
}
/********************************************************/
static void strcat_vprintf(char *buf, int buf_size, const char *fmt, va_list ap)
{
int len;
len = strlen(buf);
vsnprintf(buf + len, buf_size - len, fmt, ap);
}
static void strcat_printf(char *buf, int buf_size, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
strcat_vprintf(buf, buf_size, fmt, ap);
va_end(ap);
}
static void error1(TCCState *s1, int is_warning, const char *fmt, va_list ap)
{
char buf[2048];
BufferedFile **pf, *f;
buf[0] = '\0';
/* use upper file if inline ":asm:" or token ":paste:" */
for (f = file; f && f->filename[0] == ':'; f = f->prev)
;
if (f) {
for(pf = s1->include_stack; pf < s1->include_stack_ptr; pf++)
strcat_printf(buf, sizeof(buf), "In file included from %s:%d:\n",
(*pf)->filename, (*pf)->line_num);
if (f->line_num > 0) {
strcat_printf(buf, sizeof(buf), "%s:%d: ",
f->filename, f->line_num - !!(tok_flags & TOK_FLAG_BOL));
} else {
strcat_printf(buf, sizeof(buf), "%s: ",
f->filename);
}
} else {
strcat_printf(buf, sizeof(buf), "tcc: ");
}
if (is_warning)
strcat_printf(buf, sizeof(buf), "warning: ");
else
strcat_printf(buf, sizeof(buf), "error: ");
strcat_vprintf(buf, sizeof(buf), fmt, ap);
if (!s1->error_func) {
/* default case: stderr */
if (s1->ppfp) /* print a newline during tcc -E */
fprintf(s1->ppfp, "\n"), fflush(s1->ppfp);
fflush(stdout); /* flush -v output */
fprintf(stderr, "%s\n", buf);
fflush(stderr); /* print error/warning now (win32) */
} else {
s1->error_func(s1->error_opaque, buf);
}
if (!is_warning || s1->warn_error)
s1->nb_errors++;
}
LIBTCCAPI void tcc_set_error_func(TCCState *s, void *error_opaque,
void (*error_func)(void *opaque, const char *msg))
{
s->error_opaque = error_opaque;
s->error_func = error_func;
}
/* error without aborting current compilation */
PUB_FUNC void tcc_error_noabort(const char *fmt, ...)
{
TCCState *s1 = tcc_state;
va_list ap;
va_start(ap, fmt);
error1(s1, 0, fmt, ap);
va_end(ap);
}
PUB_FUNC void tcc_error(const char *fmt, ...)
{
TCCState *s1 = tcc_state;
va_list ap;
va_start(ap, fmt);
error1(s1, 0, fmt, ap);
va_end(ap);
/* better than nothing: in some cases, we accept to handle errors */
if (s1->error_set_jmp_enabled) {
longjmp(s1->error_jmp_buf, 1);
} else {
/* XXX: eliminate this someday */
exit(1);
}
}
PUB_FUNC void tcc_warning(const char *fmt, ...)
{
TCCState *s1 = tcc_state;
va_list ap;
if (s1->warn_none)
return;
va_start(ap, fmt);
error1(s1, 1, fmt, ap);
va_end(ap);
}
/********************************************************/
/* I/O layer */
ST_FUNC void tcc_open_bf(TCCState *s1, const char *filename, int initlen)
{
BufferedFile *bf;
int buflen = initlen ? initlen : IO_BUF_SIZE;
bf = tcc_mallocz(sizeof(BufferedFile) + buflen);
bf->buf_ptr = bf->buffer;
bf->buf_end = bf->buffer + initlen;
bf->buf_end[0] = CH_EOB; /* put eob symbol */
pstrcpy(bf->filename, sizeof(bf->filename), filename);
pstrcpy(bf->filename2, sizeof(bf->filename2), filename);
#ifdef _WIN32
normalize_slashes(bf->filename);
#endif
bf->line_num = 1;
bf->ifdef_stack_ptr = s1->ifdef_stack_ptr;
bf->fd = -1;
bf->prev = file;
file = bf;
}
ST_FUNC void tcc_close(void)
{
BufferedFile *bf = file;
if (bf->fd > 0) {
close(bf->fd);
total_lines += bf->line_num;
}
file = bf->prev;
tcc_free(bf);
}
ST_FUNC int tcc_open(TCCState *s1, const char *filename)
{
int fd;
if (strcmp(filename, "-") == 0)
fd = 0, filename = "<stdin>";
else
fd = open(filename, O_RDONLY | O_BINARY);
if ((s1->verbose == 2 && fd >= 0) || s1->verbose == 3)
printf("%s %*s%s\n", fd < 0 ? "nf":"->",
(int)(s1->include_stack_ptr - s1->include_stack), "", filename);
if (fd < 0)
return -1;
tcc_open_bf(s1, filename, 0);
file->fd = fd;
return fd;
}
/* compile the C file opened in 'file'. Return non zero if errors. */
static int tcc_compile(TCCState *s1)
{
Sym *define_start;
define_start = define_stack;
#if HAVE_SETJMP
if (setjmp(s1->error_jmp_buf) == 0) {
#else
if (1) {
#endif
s1->nb_errors = 0;
s1->error_set_jmp_enabled = 1;
preprocess_start(s1);
tccgen_start(s1);
#ifdef INC_DEBUG
printf("%s: **** new file\n", file->filename);
#endif
ch = file->buf_ptr[0];
tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
parse_flags = PARSE_FLAG_PREPROCESS | PARSE_FLAG_TOK_NUM | PARSE_FLAG_TOK_STR;
next();
decl(VT_CONST);
if (tok != TOK_EOF)
expect("declaration");
/* free defines here already on behalf of of M.M.'s possibly existing
experimental preprocessor implementation. The normal call below
is still there to free after error-longjmp */
free_defines(define_start);
tccgen_end(s1);
}
s1->error_set_jmp_enabled = 0;
free_inline_functions(s1);
/* reset define stack, but keep -D and built-ins */
free_defines(define_start);
sym_pop(&global_stack, NULL, 0);
sym_pop(&local_stack, NULL, 0);
return s1->nb_errors != 0 ? -1 : 0;
}
LIBTCCAPI int tcc_compile_string(TCCState *s, const char *str)
{
int len, ret;
len = strlen(str);
tcc_open_bf(s, "<string>", len);
memcpy(file->buffer, str, len);
ret = tcc_compile(s);
tcc_close();
return ret;
}
/* define a preprocessor symbol. A value can also be provided with the '=' operator */
LIBTCCAPI void tcc_define_symbol(TCCState *s1, const char *sym, const char *value)
{
int len1, len2;
/* default value */
if (!value)
value = "1";
len1 = strlen(sym);
len2 = strlen(value);
/* init file structure */
tcc_open_bf(s1, "<define>", len1 + len2 + 1);
memcpy(file->buffer, sym, len1);
file->buffer[len1] = ' ';
memcpy(file->buffer + len1 + 1, value, len2);
/* parse with define parser */
ch = file->buf_ptr[0];
next_nomacro();
parse_define();
tcc_close();
}
/* undefine a preprocessor symbol */
LIBTCCAPI void tcc_undefine_symbol(TCCState *s1, const char *sym)
{
TokenSym *ts;
Sym *s;
ts = tok_alloc(sym, strlen(sym));
s = define_find(ts->tok);
/* undefine symbol by putting an invalid name */
if (s)
define_undef(s);
}
/* cleanup all static data used during compilation */
static void tcc_cleanup(void)
{
if (NULL == tcc_state)
return;
tccpp_delete(tcc_state);
tcc_state = NULL;
/* free sym_pools */
dynarray_reset(&sym_pools, &nb_sym_pools);
/* reset symbol stack */
sym_free_first = NULL;
}
LIBTCCAPI TCCState *tcc_new(void)
{
TCCState *s;
tcc_cleanup();
s = tcc_mallocz(sizeof(TCCState));
if (!s)
return NULL;
tcc_state = s;
++nb_states;
#if BOOTSTRAP
s->static_link = 1;
#endif
s->alacarte_link = 1;
s->nocommon = 1;
s->warn_implicit_function_declaration = 1;
#ifdef CHAR_IS_UNSIGNED
s->char_is_unsigned = 1;
#endif
#ifdef TCC_TARGET_I386
s->seg_size = 32;
#endif
/* enable this if you want symbols with leading underscore on windows: */
#if 0 /* def TCC_TARGET_PE */
s->leading_underscore = 1;
#endif
#ifdef _WIN32
tcc_set_lib_path_w32(s);
#else
tcc_set_lib_path(s, CONFIG_TCCDIR);
#endif
tccelf_new(s);
tccpp_new(s);
/* we add dummy defines for some special macros to speed up tests
and to have working defined() */
define_push(TOK___LINE__, MACRO_OBJ, NULL, NULL);
define_push(TOK___FILE__, MACRO_OBJ, NULL, NULL);
define_push(TOK___DATE__, MACRO_OBJ, NULL, NULL);
define_push(TOK___TIME__, MACRO_OBJ, NULL, NULL);
{
/* define __TINYC__ 92X */
char buffer[32]; int a,b,c;
sscanf(TCC_VERSION, "%d.%d.%d", &a, &b, &c);
sprintf(buffer, "%d", a*10000 + b*100 + c);
tcc_define_symbol(s, "__TINYC__", buffer);
}
/* standard defines */
tcc_define_symbol(s, "__STDC__", NULL);
tcc_define_symbol(s, "__STDC_VERSION__", "199901L");
tcc_define_symbol(s, "__STDC_HOSTED__", NULL);
tcc_define_symbol(s, "__SIZEOF_LONG_LONG__", "8");
/* target defines */
#if defined(TCC_TARGET_I386)
tcc_define_symbol(s, "__i386__", NULL);
tcc_define_symbol(s, "__i386", NULL);
tcc_define_symbol(s, "i386", NULL);
#elif defined(TCC_TARGET_X86_64)
tcc_define_symbol(s, "__x86_64__", NULL);
#elif defined(TCC_TARGET_ARM)
tcc_define_symbol(s, "__ARM_ARCH_4__", NULL);
tcc_define_symbol(s, "__arm_elf__", NULL);
tcc_define_symbol(s, "__arm_elf", NULL);
tcc_define_symbol(s, "arm_elf", NULL);
tcc_define_symbol(s, "__arm__", NULL);
tcc_define_symbol(s, "__arm", NULL);
tcc_define_symbol(s, "arm", NULL);
tcc_define_symbol(s, "__APCS_32__", NULL);
tcc_define_symbol(s, "__ARMEL__", NULL);
#if defined(TCC_ARM_EABI)
tcc_define_symbol(s, "__ARM_EABI__", NULL);
#endif
#if defined(TCC_ARM_HARDFLOAT)
s->float_abi = ARM_HARD_FLOAT;
tcc_define_symbol(s, "__ARM_PCS_VFP", NULL);
#else
s->float_abi = ARM_SOFTFP_FLOAT;
#endif
#elif defined(TCC_TARGET_ARM64)
tcc_define_symbol(s, "__aarch64__", NULL);
#endif
#ifdef TCC_TARGET_PE
tcc_define_symbol(s, "_WIN32", NULL);
# ifdef TCC_TARGET_X86_64
tcc_define_symbol(s, "_WIN64", NULL);
# endif
#else
tcc_define_symbol(s, "__unix__", NULL);
tcc_define_symbol(s, "__unix", NULL);
tcc_define_symbol(s, "unix", NULL);
# if defined(__linux__)
tcc_define_symbol(s, "__linux__", NULL);
tcc_define_symbol(s, "__linux", NULL);
# endif
# if defined(__FreeBSD__)
tcc_define_symbol(s, "__FreeBSD__", "__FreeBSD__");
/* No 'Thread Storage Local' on FreeBSD with tcc */
tcc_define_symbol(s, "__NO_TLS", NULL);
# endif
# if defined(__FreeBSD_kernel__)
tcc_define_symbol(s, "__FreeBSD_kernel__", NULL);
# endif
#endif
# if defined(__NetBSD__)
tcc_define_symbol(s, "__NetBSD__", "__NetBSD__");
# endif
# if defined(__OpenBSD__)
tcc_define_symbol(s, "__OpenBSD__", "__OpenBSD__");
# endif
/* TinyCC & gcc defines */
#if defined(TCC_TARGET_PE) && PTR_SIZE == 8
/* 64bit Windows. */
tcc_define_symbol(s, "__SIZE_TYPE__", "unsigned long long");
tcc_define_symbol(s, "__PTRDIFF_TYPE__", "long long");
tcc_define_symbol(s, "__LLP64__", NULL);
#elif PTR_SIZE == 8
/* Other 64bit systems. */
tcc_define_symbol(s, "__SIZE_TYPE__", "unsigned long");
tcc_define_symbol(s, "__PTRDIFF_TYPE__", "long");
tcc_define_symbol(s, "__LP64__", NULL);
#else
/* Other 32bit systems. */
tcc_define_symbol(s, "__SIZE_TYPE__", "unsigned long");
tcc_define_symbol(s, "__PTRDIFF_TYPE__", "long");
tcc_define_symbol(s, "__ILP32__", NULL);
#endif
#if defined(TCC_MUSL)
tcc_define_symbol(s, "__builtin_va_list", "void *");
#endif /* TCC_MUSL */
#ifdef TCC_TARGET_PE
tcc_define_symbol(s, "__WCHAR_TYPE__", "unsigned short");
tcc_define_symbol(s, "__WINT_TYPE__", "unsigned short");
#else
tcc_define_symbol(s, "__WCHAR_TYPE__", "int");
/* wint_t is unsigned int by default, but (signed) int on BSDs
and unsigned short on windows. Other OSes might have still
other conventions, sigh. */
# if defined(__FreeBSD__) || defined (__FreeBSD_kernel__) \
|| defined(__NetBSD__) || defined(__OpenBSD__)
tcc_define_symbol(s, "__WINT_TYPE__", "int");
# ifdef __FreeBSD__
/* define __GNUC__ to have some useful stuff from sys/cdefs.h
that are unconditionally used in FreeBSDs other system headers :/ */
tcc_define_symbol(s, "__GNUC__", "2");
tcc_define_symbol(s, "__GNUC_MINOR__", "7");
tcc_define_symbol(s, "__builtin_alloca", "alloca");
# endif
# else
tcc_define_symbol(s, "__WINT_TYPE__", "unsigned int");
/* glibc defines */
tcc_define_symbol(s, "__REDIRECT(name, proto, alias)",
"name proto __asm__ (#alias)");
tcc_define_symbol(s, "__REDIRECT_NTH(name, proto, alias)",
"name proto __asm__ (#alias) __THROW");
# endif
/* Some GCC builtins that are simple to express as macros. */
tcc_define_symbol(s, "__builtin_extract_return_addr(x)", "x");
#endif /* ndef TCC_TARGET_PE */
return s;
}
LIBTCCAPI void tcc_delete(TCCState *s1)
{
tcc_cleanup();
/* free sections */
tccelf_delete(s1);
/* free library paths */
dynarray_reset(&s1->library_paths, &s1->nb_library_paths);
dynarray_reset(&s1->crt_paths, &s1->nb_crt_paths);
/* free include paths */
dynarray_reset(&s1->cached_includes, &s1->nb_cached_includes);
dynarray_reset(&s1->include_paths, &s1->nb_include_paths);
dynarray_reset(&s1->sysinclude_paths, &s1->nb_sysinclude_paths);
dynarray_reset(&s1->cmd_include_files, &s1->nb_cmd_include_files);
tcc_free(s1->tcc_lib_path);
tcc_free(s1->soname);
tcc_free(s1->rpath);
tcc_free(s1->init_symbol);
tcc_free(s1->fini_symbol);
tcc_free(s1->outfile);
tcc_free(s1->deps_outfile);
dynarray_reset(&s1->files, &s1->nb_files);
dynarray_reset(&s1->target_deps, &s1->nb_target_deps);
dynarray_reset(&s1->pragma_libs, &s1->nb_pragma_libs);
dynarray_reset(&s1->argv, &s1->argc);
#ifdef TCC_IS_NATIVE
/* free runtime memory */
tcc_run_free(s1);
#endif
tcc_free(s1);
if (0 == --nb_states)
tcc_memcheck();
}
LIBTCCAPI int tcc_set_output_type(TCCState *s, int output_type)
{
s->output_type = output_type;
/* always elf for objects */
if (output_type == TCC_OUTPUT_OBJ)
s->output_format = TCC_OUTPUT_FORMAT_ELF;
if (s->char_is_unsigned)
tcc_define_symbol(s, "__CHAR_UNSIGNED__", NULL);
if (!s->nostdinc) {
/* default include paths */
/* -isystem paths have already been handled */
tcc_add_sysinclude_path(s, CONFIG_TCC_SYSINCLUDEPATHS);
}
#ifdef CONFIG_TCC_BCHECK
if (s->do_bounds_check) {
/* if bound checking, then add corresponding sections */
tccelf_bounds_new(s);
/* define symbol */
tcc_define_symbol(s, "__BOUNDS_CHECKING_ON", NULL);
}
#endif
if (s->do_debug) {
/* add debug sections */
tccelf_stab_new(s);
}
tcc_add_library_path(s, CONFIG_TCC_LIBPATHS);
#ifdef TCC_TARGET_PE
# ifdef _WIN32
if (!s->nostdlib && output_type != TCC_OUTPUT_OBJ)
tcc_add_systemdir(s);
# endif
#else
/* paths for crt objects */
tcc_split_path(s, &s->crt_paths, &s->nb_crt_paths, CONFIG_TCC_CRTPREFIX);
/* add libc crt1/crti objects */
if ((output_type == TCC_OUTPUT_EXE || output_type == TCC_OUTPUT_DLL) &&
!s->nostdlib) {
if (output_type != TCC_OUTPUT_DLL)
tcc_add_crt(s, "crt1.o");
tcc_add_crt(s, "crti.o");
}
#endif
return 0;
}
LIBTCCAPI int tcc_add_include_path(TCCState *s, const char *pathname)
{
tcc_split_path(s, &s->include_paths, &s->nb_include_paths, pathname);
return 0;
}
LIBTCCAPI int tcc_add_sysinclude_path(TCCState *s, const char *pathname)
{
tcc_split_path(s, &s->sysinclude_paths, &s->nb_sysinclude_paths, pathname);
return 0;
}
ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags)
{
int ret, filetype;
filetype = flags & 0x0F;
if (filetype == 0) {
/* use a file extension to detect a filetype */
const char *ext = tcc_fileextension(filename);
if (ext[0]) {
ext++;
if (!strcmp(ext, "S"))
filetype = AFF_TYPE_ASMPP;
else if (!strcmp(ext, "s"))
filetype = AFF_TYPE_ASM;
else if (!PATHCMP(ext, "c") || !PATHCMP(ext, "i"))
filetype = AFF_TYPE_C;
else
filetype = AFF_TYPE_BIN;
} else {
filetype = AFF_TYPE_C;
}
}
/* open the file */
ret = tcc_open(s1, filename);
if (ret < 0) {
if (flags & AFF_PRINT_ERROR)
tcc_error_noabort("file '%s' not found", filename);
return ret;
}
/* update target deps */
dynarray_add(&s1->target_deps, &s1->nb_target_deps,
tcc_strdup(filename));
parse_flags = 0;
/* if .S file, define __ASSEMBLER__ like gcc does */
if (filetype == AFF_TYPE_ASM || filetype == AFF_TYPE_ASMPP) {
tcc_define_symbol(s1, "__ASSEMBLER__", NULL);
parse_flags = PARSE_FLAG_ASM_FILE;
}
if (flags & AFF_PREPROCESS) {
ret = tcc_preprocess(s1);
} else if (filetype == AFF_TYPE_C) {
ret = tcc_compile(s1);
#ifdef CONFIG_TCC_ASM
} else if (filetype == AFF_TYPE_ASMPP) {
/* non preprocessed assembler */
ret = tcc_assemble(s1, 1);
} else if (filetype == AFF_TYPE_ASM) {
/* preprocessed assembler */
ret = tcc_assemble(s1, 0);
#endif
} else {
ElfW(Ehdr) ehdr;
int fd, obj_type;
fd = file->fd;
obj_type = tcc_object_type(fd, &ehdr);
lseek(fd, 0, SEEK_SET);
/* do not display line number if error */
file->line_num = 0;
#ifdef TCC_TARGET_MACHO
if (0 == obj_type && 0 == strcmp(tcc_fileextension(filename), "dylib"))
obj_type = AFF_BINTYPE_DYN;
#endif
switch (obj_type) {
case AFF_BINTYPE_REL:
ret = tcc_load_object_file(s1, fd, 0);
break;
#ifndef TCC_TARGET_PE
case AFF_BINTYPE_DYN:
if (s1->output_type == TCC_OUTPUT_MEMORY) {
ret = 0;
#ifdef TCC_IS_NATIVE
if (NULL == dlopen(filename, RTLD_GLOBAL | RTLD_LAZY))
ret = -1;
#endif
} else {
ret = tcc_load_dll(s1, fd, filename,
(flags & AFF_REFERENCED_DLL) != 0);
}
break;
#endif
case AFF_BINTYPE_AR:
ret = tcc_load_archive(s1, fd);
break;
#ifdef TCC_TARGET_COFF
case AFF_BINTYPE_C67:
ret = tcc_load_coff(s1, fd);
break;
#endif
default:
#ifdef TCC_TARGET_PE
ret = pe_load_file(s1, filename, fd);
#else
/* as GNU ld, consider it is an ld script if not recognized */
ret = tcc_load_ldscript(s1);
#endif
if (ret < 0)
tcc_error_noabort("unrecognized file type");
break;
}
}
tcc_close();
return ret;
}
LIBTCCAPI int tcc_add_file(TCCState *s, const char *filename)
{
if (s->output_type == TCC_OUTPUT_PREPROCESS)
return tcc_add_file_internal(s, filename, AFF_PRINT_ERROR | AFF_PREPROCESS | s->filetype);
else
return tcc_add_file_internal(s, filename, AFF_PRINT_ERROR | s->filetype);
}
LIBTCCAPI int tcc_add_library_path(TCCState *s, const char *pathname)
{
tcc_split_path(s, &s->library_paths, &s->nb_library_paths, pathname);
return 0;
}
static int tcc_add_library_internal(TCCState *s, const char *fmt,
const char *filename, int flags, char **paths, int nb_paths)
{
char buf[1024];
int i;
for(i = 0; i < nb_paths; i++) {
snprintf(buf, sizeof(buf), fmt, paths[i], filename);
if (tcc_add_file_internal(s, buf, flags | AFF_TYPE_BIN) == 0)
return 0;
}
return -1;
}
/* find and load a dll. Return non zero if not found */
/* XXX: add '-rpath' option support ? */
ST_FUNC int tcc_add_dll(TCCState *s, const char *filename, int flags)
{
return tcc_add_library_internal(s, "%s/%s", filename, flags,
s->library_paths, s->nb_library_paths);
}
ST_FUNC int tcc_add_crt(TCCState *s, const char *filename)
{
if (-1 == tcc_add_library_internal(s, "%s/%s",
filename, 0, s->crt_paths, s->nb_crt_paths))
tcc_error_noabort("file '%s' not found", filename);
return 0;
}
/* the library name is the same as the argument of the '-l' option */
LIBTCCAPI int tcc_add_library(TCCState *s, const char *libraryname)
{
#if defined TCC_TARGET_PE
const char *libs[] = { "%s/%s.def", "%s/lib%s.def", "%s/%s.dll", "%s/lib%s.dll", "%s/lib%s.a", NULL };
const char **pp = s->static_link ? libs + 4 : libs;
#elif defined TCC_TARGET_MACHO
const char *libs[] = { "%s/lib%s.dylib", "%s/lib%s.a", NULL };
const char **pp = s->static_link ? libs + 1 : libs;
#else
const char *libs[] = { "%s/lib%s.so", "%s/lib%s.a", NULL };
const char **pp = s->static_link ? libs + 1 : libs;
#endif
while (*pp) {
if (0 == tcc_add_library_internal(s, *pp,
libraryname, 0, s->library_paths, s->nb_library_paths))
return 0;
++pp;
}
return -1;
}
PUB_FUNC int tcc_add_library_err(TCCState *s, const char *libname)
{
int ret = tcc_add_library(s, libname);
if (ret < 0)
tcc_error_noabort("library '%s' not found", libname);
return ret;
}
/* handle #pragma comment(lib,) */
ST_FUNC void tcc_add_pragma_libs(TCCState *s1)
{
int i;
for (i = 0; i < s1->nb_pragma_libs; i++)
tcc_add_library_err(s1, s1->pragma_libs[i]);
}
LIBTCCAPI int tcc_add_symbol(TCCState *s, const char *name, const void *val)
{
#ifdef TCC_TARGET_PE
/* On x86_64 'val' might not be reachable with a 32bit offset.
So it is handled here as if it were in a DLL. */
pe_putimport(s, 0, name, (uintptr_t)val);
#else
set_elf_sym(symtab_section, (uintptr_t)val, 0,
ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
SHN_ABS, name);
#endif
return 0;
}
LIBTCCAPI void tcc_set_lib_path(TCCState *s, const char *path)
{
tcc_free(s->tcc_lib_path);
s->tcc_lib_path = tcc_strdup(path);
}
#define WD_ALL 0x0001 /* warning is activated when using -Wall */
#define FD_INVERT 0x0002 /* invert value before storing */
typedef struct FlagDef {
uint16_t offset;
uint16_t flags;
const char *name;
} FlagDef;
static int no_flag(const char **pp)
{
const char *p = *pp;
if (*p != 'n' || *++p != 'o' || *++p != '-')
return 0;
*pp = p + 1;
return 1;
}
ST_FUNC int set_flag(TCCState *s, const FlagDef *flags, const char *name)
{
int value, ret;
const FlagDef *p;
const char *r;
value = 1;
r = name;
if (no_flag(&r))
value = 0;
for (ret = -1, p = flags; p->name; ++p) {
if (ret) {
if (strcmp(r, p->name))
continue;
} else {
if (0 == (p->flags & WD_ALL))
continue;
}
if (p->offset) {
*(int*)((char *)s + p->offset) =
p->flags & FD_INVERT ? !value : value;
if (ret)
return 0;
} else {
ret = 0;
}
}
return ret;
}
static int strstart(const char *val, const char **str)
{
const char *p, *q;
p = *str;
q = val;
while (*q) {
if (*p != *q)
return 0;
p++;
q++;
}
*str = p;
return 1;
}
/* Like strstart, but automatically takes into account that ld options can
*
* - start with double or single dash (e.g. '--soname' or '-soname')
* - arguments can be given as separate or after '=' (e.g. '-Wl,-soname,x.so'
* or '-Wl,-soname=x.so')
*
* you provide `val` always in 'option[=]' form (no leading -)
*/
static int link_option(const char *str, const char *val, const char **ptr)
{
const char *p, *q;
int ret;
/* there should be 1 or 2 dashes */
if (*str++ != '-')
return 0;
if (*str == '-')
str++;
/* then str & val should match (potentially up to '=') */
p = str;
q = val;
ret = 1;
if (q[0] == '?') {
++q;
if (no_flag(&p))
ret = -1;
}
while (*q != '\0' && *q != '=') {
if (*p != *q)
return 0;
p++;
q++;
}
/* '=' near eos means ',' or '=' is ok */
if (*q == '=') {
if (*p == 0)
*ptr = p;
if (*p != ',' && *p != '=')
return 0;
p++;
} else if (*p) {
return 0;
}
*ptr = p;
return ret;
}
static const char *skip_linker_arg(const char **str)
{
const char *s1 = *str;
const char *s2 = strchr(s1, ',');
*str = s2 ? s2++ : (s2 = s1 + strlen(s1));
return s2;
}
static void copy_linker_arg(char **pp, const char *s, int sep)
{
const char *q = s;
char *p = *pp;
int l = 0;
if (p && sep)
p[l = strlen(p)] = sep, ++l;
skip_linker_arg(&q);
pstrncpy(l + (*pp = tcc_realloc(p, q - s + l + 1)), s, q - s);
}
/* set linker options */
static int tcc_set_linker(TCCState *s, const char *option)
{
while (*option) {
const char *p = NULL;
char *end = NULL;
int ignoring = 0;
int ret;
if (link_option(option, "Bsymbolic", &p)) {
s->symbolic = 1;
} else if (link_option(option, "nostdlib", &p)) {
s->nostdlib = 1;
} else if (link_option(option, "fini=", &p)) {
copy_linker_arg(&s->fini_symbol, p, 0);
ignoring = 1;
} else if (link_option(option, "image-base=", &p)
|| link_option(option, "Ttext=", &p)) {
s->text_addr = strtoull(p, &end, 16);
s->has_text_addr = 1;
} else if (link_option(option, "init=", &p)) {
copy_linker_arg(&s->init_symbol, p, 0);
ignoring = 1;
} else if (link_option(option, "oformat=", &p)) {
#if defined(TCC_TARGET_PE)
if (strstart("pe-", &p)) {
#elif PTR_SIZE == 8
if (strstart("elf64-", &p)) {
#else
if (strstart("elf32-", &p)) {
#endif
s->output_format = TCC_OUTPUT_FORMAT_ELF;
} else if (!strcmp(p, "binary")) {
s->output_format = TCC_OUTPUT_FORMAT_BINARY;
#ifdef TCC_TARGET_COFF
} else if (!strcmp(p, "coff")) {
s->output_format = TCC_OUTPUT_FORMAT_COFF;
#endif
} else
goto err;
} else if (link_option(option, "as-needed", &p)) {
ignoring = 1;
} else if (link_option(option, "O", &p)) {
ignoring = 1;
} else if (link_option(option, "export-all-symbols", &p)) {
s->rdynamic = 1;
} else if (link_option(option, "rpath=", &p)) {
copy_linker_arg(&s->rpath, p, ':');
} else if (link_option(option, "enable-new-dtags", &p)) {
s->enable_new_dtags = 1;
} else if (link_option(option, "section-alignment=", &p)) {
s->section_align = strtoul(p, &end, 16);
} else if (link_option(option, "soname=", &p)) {
copy_linker_arg(&s->soname, p, 0);
#ifdef TCC_TARGET_PE
} else if (link_option(option, "large-address-aware", &p)) {
s->pe_characteristics |= 0x20;
} else if (link_option(option, "file-alignment=", &p)) {
s->pe_file_align = strtoul(p, &end, 16);
} else if (link_option(option, "stack=", &p)) {
s->pe_stack_size = strtoul(p, &end, 10);
} else if (link_option(option, "subsystem=", &p)) {
#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64)
if (!strcmp(p, "native")) {
s->pe_subsystem = 1;
} else if (!strcmp(p, "console")) {
s->pe_subsystem = 3;
} else if (!strcmp(p, "gui") || !strcmp(p, "windows")) {
s->pe_subsystem = 2;
} else if (!strcmp(p, "posix")) {
s->pe_subsystem = 7;
} else if (!strcmp(p, "efiapp")) {
s->pe_subsystem = 10;
} else if (!strcmp(p, "efiboot")) {
s->pe_subsystem = 11;
} else if (!strcmp(p, "efiruntime")) {
s->pe_subsystem = 12;
} else if (!strcmp(p, "efirom")) {
s->pe_subsystem = 13;
#elif defined(TCC_TARGET_ARM)
if (!strcmp(p, "wince")) {
s->pe_subsystem = 9;
#endif
} else
goto err;
#endif
} else if (ret = link_option(option, "?whole-archive", &p), ret) {
s->alacarte_link = ret < 0;
} else if (p) {
return 0;
} else {
err:
tcc_error("unsupported linker option '%s'", option);
}
if (ignoring && s->warn_unsupported)
tcc_warning("unsupported linker option '%s'", option);
option = skip_linker_arg(&p);
}
return 1;
}
typedef struct TCCOption {
const char *name;
uint16_t index;
uint16_t flags;
} TCCOption;
enum {
TCC_OPTION_HELP,
TCC_OPTION_HELP2,
TCC_OPTION_v,
TCC_OPTION_I,
TCC_OPTION_D,
TCC_OPTION_U,
TCC_OPTION_P,
TCC_OPTION_L,
TCC_OPTION_B,
TCC_OPTION_l,
TCC_OPTION_bench,
TCC_OPTION_bt,
TCC_OPTION_b,
TCC_OPTION_g,
TCC_OPTION_c,
TCC_OPTION_dumpversion,
TCC_OPTION_d,
TCC_OPTION_static,
TCC_OPTION_std,
TCC_OPTION_shared,
TCC_OPTION_soname,
TCC_OPTION_o,
TCC_OPTION_r,
TCC_OPTION_s,
TCC_OPTION_traditional,
TCC_OPTION_Wl,
TCC_OPTION_Wp,
TCC_OPTION_W,
TCC_OPTION_O,
TCC_OPTION_mfloat_abi,
TCC_OPTION_m,
TCC_OPTION_f,
TCC_OPTION_isystem,
TCC_OPTION_iwithprefix,
TCC_OPTION_include,
TCC_OPTION_nostdinc,
TCC_OPTION_nostdlib,
TCC_OPTION_print_search_dirs,
TCC_OPTION_rdynamic,
TCC_OPTION_param,
TCC_OPTION_pedantic,
TCC_OPTION_pthread,
TCC_OPTION_run,
TCC_OPTION_w,
TCC_OPTION_pipe,
TCC_OPTION_E,
TCC_OPTION_MD,
TCC_OPTION_MF,
TCC_OPTION_x,
TCC_OPTION_ar,
TCC_OPTION_impdef
};
#define TCC_OPTION_HAS_ARG 0x0001
#define TCC_OPTION_NOSEP 0x0002 /* cannot have space before option and arg */
static const TCCOption tcc_options[] = {
{ "h", TCC_OPTION_HELP, 0 },
{ "-help", TCC_OPTION_HELP, 0 },
{ "?", TCC_OPTION_HELP, 0 },
{ "hh", TCC_OPTION_HELP2, 0 },
{ "v", TCC_OPTION_v, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
{ "I", TCC_OPTION_I, TCC_OPTION_HAS_ARG },
{ "D", TCC_OPTION_D, TCC_OPTION_HAS_ARG },
{ "U", TCC_OPTION_U, TCC_OPTION_HAS_ARG },
{ "P", TCC_OPTION_P, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
{ "L", TCC_OPTION_L, TCC_OPTION_HAS_ARG },
{ "B", TCC_OPTION_B, TCC_OPTION_HAS_ARG },
{ "l", TCC_OPTION_l, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
{ "bench", TCC_OPTION_bench, 0 },
#ifdef CONFIG_TCC_BACKTRACE
{ "bt", TCC_OPTION_bt, TCC_OPTION_HAS_ARG },
#endif
#ifdef CONFIG_TCC_BCHECK
{ "b", TCC_OPTION_b, 0 },
#endif
{ "g", TCC_OPTION_g, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
{ "c", TCC_OPTION_c, 0 },
{ "dumpversion", TCC_OPTION_dumpversion, 0},
{ "d", TCC_OPTION_d, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
{ "static", TCC_OPTION_static, 0 },
{ "std", TCC_OPTION_std, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
{ "shared", TCC_OPTION_shared, 0 },
{ "soname", TCC_OPTION_soname, TCC_OPTION_HAS_ARG },
{ "o", TCC_OPTION_o, TCC_OPTION_HAS_ARG },
{ "-param", TCC_OPTION_param, TCC_OPTION_HAS_ARG },
{ "pedantic", TCC_OPTION_pedantic, 0},
{ "pthread", TCC_OPTION_pthread, 0},
{ "run", TCC_OPTION_run, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
{ "rdynamic", TCC_OPTION_rdynamic, 0 },
{ "r", TCC_OPTION_r, 0 },
{ "s", TCC_OPTION_s, 0 },
{ "traditional", TCC_OPTION_traditional, 0 },
{ "Wl,", TCC_OPTION_Wl, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
{ "Wp,", TCC_OPTION_Wp, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
{ "W", TCC_OPTION_W, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
{ "O", TCC_OPTION_O, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
#ifdef TCC_TARGET_ARM
{ "mfloat-abi", TCC_OPTION_mfloat_abi, TCC_OPTION_HAS_ARG },
#endif
{ "m", TCC_OPTION_m, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
{ "f", TCC_OPTION_f, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
{ "isystem", TCC_OPTION_isystem, TCC_OPTION_HAS_ARG },
{ "iwithprefix", TCC_OPTION_iwithprefix, TCC_OPTION_HAS_ARG },
{ "include", TCC_OPTION_include, TCC_OPTION_HAS_ARG },
{ "nostdinc", TCC_OPTION_nostdinc, 0 },
{ "nostdlib", TCC_OPTION_nostdlib, 0 },
{ "print-search-dirs", TCC_OPTION_print_search_dirs, 0 },
{ "w", TCC_OPTION_w, 0 },
{ "pipe", TCC_OPTION_pipe, 0},
{ "E", TCC_OPTION_E, 0},
{ "MD", TCC_OPTION_MD, 0},
{ "MF", TCC_OPTION_MF, TCC_OPTION_HAS_ARG },
{ "x", TCC_OPTION_x, TCC_OPTION_HAS_ARG },
{ "ar", TCC_OPTION_ar, 0},
#ifdef TCC_TARGET_PE
{ "impdef", TCC_OPTION_impdef, 0},
#endif
{ NULL, 0, 0 },
};
static const FlagDef options_W[] = {
{ 0, 0, "all" },
{ offsetof(TCCState, warn_unsupported), 0, "unsupported" },
{ offsetof(TCCState, warn_write_strings), 0, "write-strings" },
{ offsetof(TCCState, warn_error), 0, "error" },
{ offsetof(TCCState, warn_gcc_compat), 0, "gcc-compat" },
{ offsetof(TCCState, warn_implicit_function_declaration), WD_ALL,
"implicit-function-declaration" },
{ 0, 0, NULL }
};
static const FlagDef options_f[] = {
{ offsetof(TCCState, char_is_unsigned), 0, "unsigned-char" },
{ offsetof(TCCState, char_is_unsigned), FD_INVERT, "signed-char" },
{ offsetof(TCCState, nocommon), FD_INVERT, "common" },
{ offsetof(TCCState, leading_underscore), 0, "leading-underscore" },
{ offsetof(TCCState, ms_extensions), 0, "ms-extensions" },
{ offsetof(TCCState, dollars_in_identifiers), 0, "dollars-in-identifiers" },
{ 0, 0, NULL }
};
static const FlagDef options_m[] = {
{ offsetof(TCCState, ms_bitfields), 0, "ms-bitfields" },
#ifdef TCC_TARGET_X86_64
{ offsetof(TCCState, nosse), FD_INVERT, "sse" },
#endif
{ 0, 0, NULL }
};
static void parse_option_D(TCCState *s1, const char *optarg)
{
char *sym = tcc_strdup(optarg);
char *value = strchr(sym, '=');
if (value)
*value++ = '\0';
tcc_define_symbol(s1, sym, value);
tcc_free(sym);
}
static void args_parser_add_file(TCCState *s, const char* filename, int filetype)
{
struct filespec *f = tcc_malloc(sizeof *f + strlen(filename));
f->type = filetype;
f->alacarte = s->alacarte_link;
strcpy(f->name, filename);
dynarray_add(&s->files, &s->nb_files, f);
}
static int args_parser_make_argv(const char *r, int *argc, char ***argv)
{
int ret = 0, q, c;
CString str;
for(;;) {
while (c = (unsigned char)*r, c && c <= ' ')
++r;
if (c == 0)
break;
q = 0;
cstr_new(&str);
while (c = (unsigned char)*r, c) {
++r;
if (c == '\\' && (*r == '"' || *r == '\\')) {
c = *r++;
} else if (c == '"') {
q = !q;
continue;
} else if (q == 0 && c <= ' ') {
break;
}
cstr_ccat(&str, c);
}
cstr_ccat(&str, 0);
//printf("<%s>\n", str.data), fflush(stdout);
dynarray_add(argv, argc, tcc_strdup(str.data));
cstr_free(&str);
++ret;
}
return ret;
}
/* read list file */
static void args_parser_listfile(TCCState *s,
const char *filename, int optind, int *pargc, char ***pargv)
{
int fd, i;
size_t len;
char *p;
int argc = 0;
char **argv = NULL;
fd = open(filename, O_RDONLY | O_BINARY);
if (fd < 0)
tcc_error("listfile '%s' not found", filename);
len = lseek(fd, 0, SEEK_END);
p = tcc_malloc(len + 1), p[len] = 0;
lseek(fd, 0, SEEK_SET), read(fd, p, len), close(fd);
for (i = 0; i < *pargc; ++i)
if (i == optind)
args_parser_make_argv(p, &argc, &argv);
else
dynarray_add(&argv, &argc, tcc_strdup((*pargv)[i]));
tcc_free(p);
dynarray_reset(&s->argv, &s->argc);
*pargc = s->argc = argc, *pargv = s->argv = argv;
}
PUB_FUNC int tcc_parse_args(TCCState *s, int *pargc, char ***pargv, int optind)
{
const TCCOption *popt;
const char *optarg, *r;
const char *run = NULL;
int last_o = -1;
int x;
CString linker_arg; /* collect -Wl options */
char buf[1024];
int tool = 0, arg_start = 0, noaction = optind;
char **argv = *pargv;
int argc = *pargc;
cstr_new(&linker_arg);
while (optind < argc) {
r = argv[optind];
if (r[0] == '@' && r[1] != '\0') {
args_parser_listfile(s, r + 1, optind, &argc, &argv);
continue;
}
optind++;
if (tool) {
if (r[0] == '-' && r[1] == 'v' && r[2] == 0)
++s->verbose;
continue;
}
reparse:
if (r[0] != '-' || r[1] == '\0') {
if (r[0] != '@') /* allow "tcc file(s) -run @ args ..." */
args_parser_add_file(s, r, s->filetype);
if (run) {
tcc_set_options(s, run);
arg_start = optind - 1;
break;
}
continue;
}
/* find option in table */
for(popt = tcc_options; ; ++popt) {
const char *p1 = popt->name;
const char *r1 = r + 1;
if (p1 == NULL)
tcc_error("invalid option -- '%s'", r);
if (!strstart(p1, &r1))
continue;
optarg = r1;
if (popt->flags & TCC_OPTION_HAS_ARG) {
if (*r1 == '\0' && !(popt->flags & TCC_OPTION_NOSEP)) {
if (optind >= argc)
arg_err:
tcc_error("argument to '%s' is missing", r);
optarg = argv[optind++];
}
} else if (*r1 != '\0')
continue;
break;
}
switch(popt->index) {
case TCC_OPTION_HELP:
return OPT_HELP;
case TCC_OPTION_HELP2:
return OPT_HELP2;
case TCC_OPTION_I:
tcc_add_include_path(s, optarg);
break;
case TCC_OPTION_D:
parse_option_D(s, optarg);
break;
case TCC_OPTION_U:
tcc_undefine_symbol(s, optarg);
break;
case TCC_OPTION_L:
tcc_add_library_path(s, optarg);
break;
case TCC_OPTION_B:
/* set tcc utilities path (mainly for tcc development) */
tcc_set_lib_path(s, optarg);
break;
case TCC_OPTION_l:
args_parser_add_file(s, optarg, AFF_TYPE_LIB);
s->nb_libraries++;
break;
case TCC_OPTION_pthread:
parse_option_D(s, "_REENTRANT");
s->option_pthread = 1;
break;
case TCC_OPTION_bench:
s->do_bench = 1;
break;
#ifdef CONFIG_TCC_BACKTRACE
case TCC_OPTION_bt:
tcc_set_num_callers(atoi(optarg));
break;
#endif
#ifdef CONFIG_TCC_BCHECK
case TCC_OPTION_b:
s->do_bounds_check = 1;
s->do_debug = 1;
break;
#endif
case TCC_OPTION_g:
s->do_debug = 1;
break;
case TCC_OPTION_c:
x = TCC_OUTPUT_OBJ;
set_output_type:
if (s->output_type)
tcc_warning("-%s: overriding compiler action already specified", popt->name);
s->output_type = x;
break;
case TCC_OPTION_d:
if (*optarg == 'D')
s->dflag = 3;
else if (*optarg == 'M')
s->dflag = 7;
else
goto unsupported_option;
break;
case TCC_OPTION_static:
s->static_link = 1;
break;
case TCC_OPTION_std:
/* silently ignore, a current purpose:
allow to use a tcc as a reference compiler for "make test" */
break;
case TCC_OPTION_shared:
x = TCC_OUTPUT_DLL;
goto set_output_type;
case TCC_OPTION_soname:
s->soname = tcc_strdup(optarg);
break;
case TCC_OPTION_o:
if (s->outfile) {
tcc_warning("multiple -o option");
tcc_free(s->outfile);
}
s->outfile = tcc_strdup(optarg);
break;
case TCC_OPTION_r:
/* generate a .o merging several output files */
s->option_r = 1;
x = TCC_OUTPUT_OBJ;
goto set_output_type;
case TCC_OPTION_isystem:
tcc_add_sysinclude_path(s, optarg);
break;
case TCC_OPTION_iwithprefix:
snprintf(buf, sizeof buf, "{B}/%s", optarg);
tcc_add_sysinclude_path(s, buf);
break;
case TCC_OPTION_include:
dynarray_add(&s->cmd_include_files,
&s->nb_cmd_include_files, tcc_strdup(optarg));
break;
case TCC_OPTION_nostdinc:
s->nostdinc = 1;
break;
case TCC_OPTION_nostdlib:
s->nostdlib = 1;
break;
case TCC_OPTION_run:
#ifndef TCC_IS_NATIVE
tcc_error("-run is not available in a cross compiler");
#endif
run = optarg;
x = TCC_OUTPUT_MEMORY;
goto set_output_type;
case TCC_OPTION_v:
do ++s->verbose; while (*optarg++ == 'v');
++noaction;
break;
case TCC_OPTION_f:
if (set_flag(s, options_f, optarg) < 0)
goto unsupported_option;
break;
#ifdef TCC_TARGET_ARM
case TCC_OPTION_mfloat_abi:
/* tcc doesn't support soft float yet */
if (!strcmp(optarg, "softfp")) {
s->float_abi = ARM_SOFTFP_FLOAT;
tcc_undefine_symbol(s, "__ARM_PCS_VFP");
} else if (!strcmp(optarg, "hard"))
s->float_abi = ARM_HARD_FLOAT;
else
tcc_error("unsupported float abi '%s'", optarg);
break;
#endif
case TCC_OPTION_m:
if (set_flag(s, options_m, optarg) < 0) {
if (x = atoi(optarg), x != 32 && x != 64)
goto unsupported_option;
if (PTR_SIZE != x/8)
return x;
++noaction;
}
break;
case TCC_OPTION_W:
if (set_flag(s, options_W, optarg) < 0)
goto unsupported_option;
break;
case TCC_OPTION_w:
s->warn_none = 1;
break;
case TCC_OPTION_rdynamic:
s->rdynamic = 1;
break;
case TCC_OPTION_Wl:
if (linker_arg.size)
--linker_arg.size, cstr_ccat(&linker_arg, ',');
cstr_cat(&linker_arg, optarg, 0);
if (tcc_set_linker(s, linker_arg.data))
cstr_free(&linker_arg);
break;
case TCC_OPTION_Wp:
r = optarg;
goto reparse;
case TCC_OPTION_E:
x = TCC_OUTPUT_PREPROCESS;
goto set_output_type;
case TCC_OPTION_P:
s->Pflag = atoi(optarg) + 1;
break;
case TCC_OPTION_MD:
s->gen_deps = 1;
break;
case TCC_OPTION_MF:
s->deps_outfile = tcc_strdup(optarg);
break;
case TCC_OPTION_dumpversion:
printf ("%s\n", TCC_VERSION);
exit(0);
break;
case TCC_OPTION_x:
if (*optarg == 'c')
s->filetype = AFF_TYPE_C;
else if (*optarg == 'a')
s->filetype = AFF_TYPE_ASMPP;
else if (*optarg == 'n')
s->filetype = AFF_TYPE_NONE;
else
tcc_warning("unsupported language '%s'", optarg);
break;
case TCC_OPTION_O:
last_o = atoi(optarg);
break;
case TCC_OPTION_print_search_dirs:
x = OPT_PRINT_DIRS;
goto extra_action;
case TCC_OPTION_impdef:
x = OPT_IMPDEF;
goto extra_action;
case TCC_OPTION_ar:
x = OPT_AR;
extra_action:
arg_start = optind - 1;
if (arg_start != noaction)
tcc_error("cannot parse %s here", r);
tool = x;
break;
case TCC_OPTION_traditional:
case TCC_OPTION_pedantic:
case TCC_OPTION_pipe:
case TCC_OPTION_s:
/* ignored */
break;
default:
unsupported_option:
if (s->warn_unsupported)
tcc_warning("unsupported option '%s'", r);
break;
}
}
if (last_o > 0)
tcc_define_symbol(s, "__OPTIMIZE__", NULL);
if (linker_arg.size) {
r = linker_arg.data;
goto arg_err;
}
*pargc = argc - arg_start;
*pargv = argv + arg_start;
if (tool)
return tool;
if (optind != noaction)
return 0;
if (s->verbose == 2)
return OPT_PRINT_DIRS;
if (s->verbose)
return OPT_V;
return OPT_HELP;
}
LIBTCCAPI void tcc_set_options(TCCState *s, const char *r)
{
char **argv = NULL;
int argc = 0;
args_parser_make_argv(r, &argc, &argv);
tcc_parse_args(s, &argc, &argv, 0);
dynarray_reset(&argv, &argc);
}
PUB_FUNC void tcc_print_stats(TCCState *s, unsigned total_time)
{
if (total_time < 1)
total_time = 1;
if (total_bytes < 1)
total_bytes = 1;
fprintf(stderr, "* %d idents, %d lines, %d bytes\n"
"* %0.3f s, %u lines/s, %0.1f MB/s\n",
tok_ident - TOK_IDENT, total_lines, total_bytes,
(double)total_time/1000,
(unsigned)total_lines*1000/total_time,
(double)total_bytes/1000/total_time);
#ifdef MEM_DEBUG
fprintf(stderr, "* %d bytes memory used\n", mem_max_size);
#endif
}
/*
* TCC - Tiny C Compiler
*
* Copyright (c) 2001-2004 Fabrice Bellard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _TCC_H
#define _TCC_H
#define _GNU_SOURCE
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include <fcntl.h>
#include <setjmp.h>
#include <time.h>
#ifndef _WIN32
# include <unistd.h>
# include <sys/time.h>
# ifndef CONFIG_TCC_STATIC
# include <dlfcn.h>
# endif
/* XXX: need to define this to use them in non ISOC99 context */
extern float strtof (const char *__nptr, char **__endptr);
extern long double strtold (const char *__nptr, char **__endptr);
#else /* on _WIN32: */
# include <windows.h>
# include <io.h> /* open, close etc. */
# include <direct.h> /* getcwd */
# ifdef __GNUC__
# include <stdint.h>
# endif
# define inline __inline
# define inp next_inp /* inp is an intrinsic on msvc */
# define snprintf _snprintf
# define vsnprintf _vsnprintf
# ifndef __GNUC__
# define strtold (long double)strtod
# define strtof (float)strtod
# define strtoll _strtoi64
# define strtoull _strtoui64
# endif
# ifdef LIBTCC_AS_DLL
# define LIBTCCAPI __declspec(dllexport)
# define PUB_FUNC LIBTCCAPI
# endif
# ifdef _MSC_VER
# pragma warning (disable : 4244) // conversion from 'uint64_t' to 'int', possible loss of data
# pragma warning (disable : 4267) // conversion from 'size_t' to 'int', possible loss of data
# pragma warning (disable : 4996) // The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name
# pragma warning (disable : 4018) // signed/unsigned mismatch
# pragma warning (disable : 4146) // unary minus operator applied to unsigned type, result still unsigned
# define ssize_t intptr_t
# define __attribute__(x) __declspec x
# define aligned align
# else
# endif
# undef CONFIG_TCC_STATIC
#endif
#ifndef O_BINARY
# define O_BINARY 0
#endif
#ifdef __GNUC__
# define NORETURN __attribute__ ((noreturn))
#elif defined _MSC_VER
# define NORETURN __declspec(noreturn)
#else
# define NORETURN
#endif
#ifdef _WIN32
# define IS_DIRSEP(c) (c == '/' || c == '\\')
# define IS_ABSPATH(p) (IS_DIRSEP(p[0]) || (p[0] && p[1] == ':' && IS_DIRSEP(p[2])))
# define PATHCMP stricmp
#else
# define IS_DIRSEP(c) (c == '/')
# define IS_ABSPATH(p) IS_DIRSEP(p[0])
# define PATHCMP strcmp
#endif
#ifdef TCC_TARGET_PE
#define PATHSEP ';'
#else
#define PATHSEP ':'
#endif
/* -------------------------------------------- */
/* parser debug */
/* #define PARSE_DEBUG */
/* preprocessor debug */
/* #define PP_DEBUG */
/* include file debug */
/* #define INC_DEBUG */
/* memory leak debug */
/* #define MEM_DEBUG */
/* assembler debug */
/* #define ASM_DEBUG */
/* target selection */
/* #define TCC_TARGET_I386 *//* i386 code generator */
/* #define TCC_TARGET_X86_64 *//* x86-64 code generator */
/* #define TCC_TARGET_ARM *//* ARMv4 code generator */
/* #define TCC_TARGET_ARM64 *//* ARMv8 code generator */
/* #define TCC_TARGET_C67 *//* TMS320C67xx code generator */
/* default target is I386 */
#if !defined(TCC_TARGET_I386) && !defined(TCC_TARGET_ARM) && \
!defined(TCC_TARGET_ARM64) && !defined(TCC_TARGET_C67) && \
!defined(TCC_TARGET_X86_64)
#define TCC_TARGET_I386
#endif
/* object format selection */
#if defined(TCC_TARGET_C67)
#define TCC_TARGET_COFF
#endif
/* only native compiler supports -run */
#if defined _WIN32 == defined TCC_TARGET_PE
# if (defined __i386__ || defined _X86_) && defined TCC_TARGET_I386
# define TCC_IS_NATIVE
# elif (defined __x86_64__ || defined _AMD64_) && defined TCC_TARGET_X86_64
# define TCC_IS_NATIVE
# elif defined __arm__ && defined TCC_TARGET_ARM
# define TCC_IS_NATIVE
# elif defined __aarch64__ && defined TCC_TARGET_ARM64
# define TCC_IS_NATIVE
# endif
#endif
#if defined TCC_IS_NATIVE && !defined CONFIG_TCCBOOT
# define CONFIG_TCC_BACKTRACE
# if (defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64) \
&& !defined TCC_UCLIBC && !defined TCC_MUSL
# define CONFIG_TCC_BCHECK /* enable bound checking code */
# endif
#endif
/* ------------ path configuration ------------ */
#ifndef CONFIG_SYSROOT
# define CONFIG_SYSROOT ""
#endif
#ifndef CONFIG_TCCDIR
# define CONFIG_TCCDIR "."
#endif
#ifndef CONFIG_LDDIR
# define CONFIG_LDDIR "lib"
#endif
#ifdef CONFIG_TRIPLET
# define USE_TRIPLET(s) s "/" CONFIG_TRIPLET
# define ALSO_TRIPLET(s) USE_TRIPLET(s) ":" s
#else
# define USE_TRIPLET(s) s
# define ALSO_TRIPLET(s) s
#endif
/* path to find crt1.o, crti.o and crtn.o */
#ifndef CONFIG_TCC_CRTPREFIX
# define CONFIG_TCC_CRTPREFIX USE_TRIPLET(CONFIG_SYSROOT "/usr/" CONFIG_LDDIR)
#endif
/* Below: {B} is substituted by CONFIG_TCCDIR (rsp. -B option) */
/* system include paths */
#ifndef CONFIG_TCC_SYSINCLUDEPATHS
# ifdef TCC_TARGET_PE
# define CONFIG_TCC_SYSINCLUDEPATHS "{B}/include;{B}/include/winapi"
# else
# define CONFIG_TCC_SYSINCLUDEPATHS \
"{B}/include" \
":" ALSO_TRIPLET(CONFIG_SYSROOT "/usr/local/include") \
":" ALSO_TRIPLET(CONFIG_SYSROOT "/usr/include")
# endif
#endif
/* library search paths */
#ifndef CONFIG_TCC_LIBPATHS
# ifdef TCC_TARGET_PE
# define CONFIG_TCC_LIBPATHS "{B}/lib"
# else
# define CONFIG_TCC_LIBPATHS \
ALSO_TRIPLET(CONFIG_SYSROOT "/usr/" CONFIG_LDDIR) \
":" ALSO_TRIPLET(CONFIG_SYSROOT "/" CONFIG_LDDIR) \
":" ALSO_TRIPLET(CONFIG_SYSROOT "/usr/local/" CONFIG_LDDIR)
# endif
#endif
/* name of ELF interpreter */
#ifndef CONFIG_TCC_ELFINTERP
# if defined __FreeBSD__
# define CONFIG_TCC_ELFINTERP "/libexec/ld-elf.so.1"
# elif defined __FreeBSD_kernel__
# if defined(TCC_TARGET_X86_64)
# define CONFIG_TCC_ELFINTERP "/lib/ld-kfreebsd-x86-64.so.1"
# else
# define CONFIG_TCC_ELFINTERP "/lib/ld.so.1"
# endif
# elif defined __DragonFly__
# define CONFIG_TCC_ELFINTERP "/usr/libexec/ld-elf.so.2"
# elif defined __NetBSD__
# define CONFIG_TCC_ELFINTERP "/usr/libexec/ld.elf_so"
# elif defined __GNU__
# define CONFIG_TCC_ELFINTERP "/lib/ld.so"
# elif defined(TCC_TARGET_PE)
# define CONFIG_TCC_ELFINTERP "-"
# elif defined(TCC_UCLIBC)
# define CONFIG_TCC_ELFINTERP "/lib/ld-uClibc.so.0" /* is there a uClibc for x86_64 ? */
# elif defined TCC_TARGET_ARM64
# if defined(TCC_MUSL)
# define CONFIG_TCC_ELFINTERP "/lib/ld-musl-aarch64.so.1"
# else
# define CONFIG_TCC_ELFINTERP "/lib/ld-linux-aarch64.so.1"
# endif
# elif defined(TCC_TARGET_X86_64)
# if defined(TCC_MUSL)
# define CONFIG_TCC_ELFINTERP "/lib/ld-musl-x86_64.so.1"
# else
# define CONFIG_TCC_ELFINTERP "/lib64/ld-linux-x86-64.so.2"
# endif
# elif !defined(TCC_ARM_EABI)
# if defined(TCC_MUSL)
# define CONFIG_TCC_ELFINTERP "/lib/ld-musl-arm.so.1"
# else
# define CONFIG_TCC_ELFINTERP "/lib/ld-linux.so.2"
# endif
# endif
#endif
/* var elf_interp dans *-gen.c */
#ifdef CONFIG_TCC_ELFINTERP
# define DEFAULT_ELFINTERP(s) CONFIG_TCC_ELFINTERP
#else
# define DEFAULT_ELFINTERP(s) default_elfinterp(s)
#endif
/* (target specific) libtcc1.a */
#ifndef TCC_LIBTCC1
# define TCC_LIBTCC1 "libtcc1.a"
#endif
/* library to use with CONFIG_USE_LIBGCC instead of libtcc1.a */
#if defined CONFIG_USE_LIBGCC && !defined TCC_LIBGCC
#define TCC_LIBGCC USE_TRIPLET(CONFIG_SYSROOT "/" CONFIG_LDDIR) "/libgcc_s.so.1"
#endif
/* -------------------------------------------- */
#include "libtcc.h"
#include "elf.h"
#include "stab.h"
/* -------------------------------------------- */
#ifndef PUB_FUNC /* functions used by tcc.c but not in libtcc.h */
# define PUB_FUNC
#endif
#ifdef ONE_SOURCE
#define ST_INLN static inline
#define ST_FUNC static
#define ST_DATA static
#else
#define ST_INLN
#define ST_FUNC
#define ST_DATA extern
#endif
#ifdef TCC_PROFILE /* profile all functions */
# define static
#endif
/* -------------------------------------------- */
/* include the target specific definitions */
#define TARGET_DEFS_ONLY
#ifdef TCC_TARGET_I386
# include "i386-gen.c"
# include "i386-link.c"
#endif
#ifdef TCC_TARGET_X86_64
# include "x86_64-gen.c"
# include "x86_64-link.c"
#endif
#ifdef TCC_TARGET_ARM
# include "arm-gen.c"
# include "arm-link.c"
# include "arm-asm.c"
#endif
#ifdef TCC_TARGET_ARM64
# include "arm64-gen.c"
# include "arm64-link.c"
#endif
#ifdef TCC_TARGET_C67
# include "coff.h"
# include "c67-gen.c"
# include "c67-link.c"
#endif
#undef TARGET_DEFS_ONLY
/* -------------------------------------------- */
#if PTR_SIZE == 8
# define ELFCLASSW ELFCLASS64
# define ElfW(type) Elf##64##_##type
# define ELFW(type) ELF##64##_##type
# define ElfW_Rel ElfW(Rela)
# define SHT_RELX SHT_RELA
# define REL_SECTION_FMT ".rela%s"
#else
# define ELFCLASSW ELFCLASS32
# define ElfW(type) Elf##32##_##type
# define ELFW(type) ELF##32##_##type
# define ElfW_Rel ElfW(Rel)
# define SHT_RELX SHT_REL
# define REL_SECTION_FMT ".rel%s"
#endif
/* target address type */
#define addr_t ElfW(Addr)
/* -------------------------------------------- */
#define INCLUDE_STACK_SIZE 32
#define IFDEF_STACK_SIZE 64
#define VSTACK_SIZE 256
#define STRING_MAX_SIZE 1024
#define TOKSTR_MAX_SIZE 256
#define PACK_STACK_SIZE 8
#define TOK_HASH_SIZE 16384 /* must be a power of two */
#define TOK_ALLOC_INCR 512 /* must be a power of two */
#define TOK_MAX_SIZE 4 /* token max size in int unit when stored in string */
/* token symbol management */
typedef struct TokenSym {
struct TokenSym *hash_next;
struct Sym *sym_define; /* direct pointer to define */
struct Sym *sym_label; /* direct pointer to label */
struct Sym *sym_struct; /* direct pointer to structure */
struct Sym *sym_identifier; /* direct pointer to identifier */
int tok; /* token number */
int len;
char str[1];
} TokenSym;
#ifdef TCC_TARGET_PE
typedef unsigned short nwchar_t;
#else
typedef int nwchar_t;
#endif
typedef struct CString {
int size; /* size in bytes */
void *data; /* either 'char *' or 'nwchar_t *' */
int size_allocated;
} CString;
/* type definition */
typedef struct CType {
int t;
struct Sym *ref;
} CType;
/* constant value */
typedef union CValue {
long double ld;
double d;
float f;
#if HAVE_LONG_LONG_STUB || HAVE_LONG_LONG
uint64_t i;
#else
uint32_t i;
uint32_t i_padding;
#endif
struct {
int size;
const void *data;
} str;
#if BOOTSTRAP && __arm__
int tab[4];
#else
int tab[LDOUBLE_SIZE/4];
#endif
} CValue;
/* value on stack */
typedef struct SValue {
CType type; /* type */
unsigned short r; /* register + flags */
unsigned short r2; /* second register, used for 'long long'
type. If not used, set to VT_CONST */
CValue c; /* constant, if VT_CONST */
struct Sym *sym; /* symbol, if (VT_SYM | VT_CONST), or if
result of unary() for an identifier. */
} SValue;
struct Attribute {
#if HAVE_BITFIELD
unsigned
func_call : 3, /* calling convention (0..5), see below */
aligned : 5, /* alignment as log2+1 (0 == unspecified) */
packed : 1,
func_export : 1,
func_import : 1,
func_args : 5,
func_body : 1,
mode : 4,
weak : 1,
visibility : 2,
unsigned_enum : 1,
fill : 7; // 7 bits left to fit well in union below
#else // !HAVE_BITFIELD
struct {
unsigned func_call;
unsigned aligned;
unsigned packed;
unsigned func_export;
unsigned func_import;
unsigned func_args;
unsigned func_body;
unsigned mode;
unsigned weak;
unsigned visibility;
unsigned unsigned_enum;
unsigned fill;
};
#endif // !HAVE_BITFIELD
};
/* GNUC attribute definition */
typedef struct AttributeDef {
struct Attribute a;
struct Section *section;
int alias_target; /* token */
int asm_label; /* associated asm label */
} AttributeDef;
/* symbol management */
typedef struct Sym {
int v; /* symbol token */
union {
int asm_label; /* associated asm label */
int scope; /* scope level for locals */
};
union {
long r; /* associated register or VT_CONST/VT_LOCAL and LVAL type */
struct Attribute a;
};
union {
long c; /* associated number */
int *d; /* define token stream */
};
CType type; /* associated type */
union {
struct Sym *next; /* next related symbol (for fields and anoms) */
long jnext; /* next jump label */
};
struct Sym *prev; /* prev symbol in stack */
struct Sym *prev_tok; /* previous symbol for this token */
} Sym;
/* section definition */
/* XXX: use directly ELF structure for parameters ? */
/* special flag to indicate that the section should not be linked to
the other ones */
#define SHF_PRIVATE 0x80000000
/* special flag, too */
#define SECTION_ABS ((void *)1)
typedef struct Section {
unsigned long data_offset; /* current data offset */
unsigned char *data; /* section data */
unsigned long data_allocated; /* used for realloc() handling */
int sh_name; /* elf section name (only used during output) */
int sh_num; /* elf section number */
int sh_type; /* elf section type */
int sh_flags; /* elf section flags */
int sh_info; /* elf section info */
int sh_addralign; /* elf section alignment */
int sh_entsize; /* elf entry size */
unsigned long sh_size; /* section size (only used during output) */
addr_t sh_addr; /* address at which the section is relocated */
unsigned long sh_offset; /* file offset */
int nb_hashed_syms; /* used to resize the hash table */
struct Section *link; /* link to another section */
struct Section *reloc; /* corresponding section for relocation, if any */
struct Section *hash; /* hash table for symbols */
struct Section *prev; /* previous section on section stack */
char name[1]; /* section name */
} Section;
typedef struct DLLReference {
int level;
void *handle;
char name[1];
} DLLReference;
/* -------------------------------------------------- */
#define SYM_STRUCT 0x40000000 /* struct/union/enum symbol space */
#define SYM_FIELD 0x20000000 /* struct/union field symbol space */
#define SYM_FIRST_ANOM 0x10000000 /* first anonymous sym */
/* stored in 'Sym.c' field */
#define FUNC_NEW 1 /* ansi function prototype */
#define FUNC_OLD 2 /* old function prototype */
#define FUNC_ELLIPSIS 3 /* ansi function prototype with ... */
/* stored in 'Sym.r' field */
#define FUNC_CDECL 0 /* standard c call */
#define FUNC_STDCALL 1 /* pascal c call */
#define FUNC_FASTCALL1 2 /* first param in %eax */
#define FUNC_FASTCALL2 3 /* first parameters in %eax, %edx */
#define FUNC_FASTCALL3 4 /* first parameter in %eax, %edx, %ecx */
#define FUNC_FASTCALLW 5 /* first parameter in %ecx, %edx */
/* field 'Sym.t' for macros */
#define MACRO_OBJ 0 /* object like macro */
#define MACRO_FUNC 1 /* function like macro */
/* field 'Sym.r' for C labels */
#define LABEL_DEFINED 0 /* label is defined */
#define LABEL_FORWARD 1 /* label is forward defined */
#define LABEL_DECLARED 2 /* label is declared but never used */
/* type_decl() types */
#define TYPE_ABSTRACT 1 /* type without variable */
#define TYPE_DIRECT 2 /* type with variable */
#define IO_BUF_SIZE 8192
typedef struct BufferedFile {
uint8_t *buf_ptr;
uint8_t *buf_end;
int fd;
struct BufferedFile *prev;
int line_num; /* current line number - here to simplify code */
int line_ref; /* tcc -E: last printed line */
int ifndef_macro; /* #ifndef macro / #endif search */
int ifndef_macro_saved; /* saved ifndef_macro */
int *ifdef_stack_ptr; /* ifdef_stack value at the start of the file */
int include_next_index; /* next search path */
char filename[1024]; /* filename */
char filename2[1024]; /* filename not modified by # line directive */
unsigned char unget[4];
unsigned char buffer[1]; /* extra size for CH_EOB char */
} BufferedFile;
#define CH_EOB '\\' /* end of buffer or '\0' char in file */
#define CH_EOF (-1) /* end of file */
/* parsing state (used to save parser state to reparse part of the
source several times) */
typedef struct ParseState {
const int *macro_ptr;
int line_num;
int tok;
CValue tokc;
} ParseState;
/* used to record tokens */
typedef struct TokenString {
int *str;
int len;
int allocated_len;
int last_line_num;
/* used to chain token-strings with begin/end_macro() */
struct TokenString *prev;
const int *prev_ptr;
char alloc;
} TokenString;
/* inline functions */
typedef struct InlineFunc {
TokenString *func_str;
Sym *sym;
char filename[1];
} InlineFunc;
/* include file cache, used to find files faster and also to eliminate
inclusion if the include file is protected by #ifndef ... #endif */
typedef struct CachedInclude {
int ifndef_macro;
int once;
int hash_next; /* -1 if none */
char filename[1]; /* path specified in #include */
} CachedInclude;
#define CACHED_INCLUDES_HASH_SIZE 32
#ifdef CONFIG_TCC_ASM
typedef struct ExprValue {
#if HAVE_LONG_LONG_STUB || HAVE_LONG_LONG
uint64_t v;
#else
uint32_t v;
uint32_t v_padding;
#endif
Sym *sym;
int pcrel;
} ExprValue;
#define MAX_ASM_OPERANDS 30
typedef struct ASMOperand {
int id; /* GCC 3 optional identifier (0 if number only supported */
char *constraint;
char asm_str[16]; /* computed asm string for operand */
SValue *vt; /* C value of the expression */
int ref_index; /* if >= 0, gives reference to a output constraint */
int input_index; /* if >= 0, gives reference to an input constraint */
int priority; /* priority, used to assign registers */
int reg; /* if >= 0, register number used for this operand */
int is_llong; /* true if double register value */
int is_memory; /* true if memory operand */
int is_rw; /* for '+' modifier */
} ASMOperand;
#endif
/* extra symbol attributes (not in symbol table) */
struct sym_attr {
unsigned got_offset;
unsigned plt_offset;
int plt_sym;
int dyn_index;
#ifdef TCC_TARGET_ARM
unsigned char plt_thumb_stub:1;
#endif
};
struct TCCState {
int verbose; /* if true, display some information during compilation */
int nostdinc; /* if true, no standard headers are added */
int nostdlib; /* if true, no standard libraries are added */
int nocommon; /* if true, do not use common symbols for .bss data */
int static_link; /* if true, static linking is performed */
int rdynamic; /* if true, all symbols are exported */
int symbolic; /* if true, resolve symbols in the current module first */
int alacarte_link; /* if true, only link in referenced objects from archive */
char *tcc_lib_path; /* CONFIG_TCCDIR or -B option */
char *soname; /* as specified on the command line (-soname) */
char *rpath; /* as specified on the command line (-Wl,-rpath=) */
int enable_new_dtags; /* ditto, (-Wl,--enable-new-dtags) */
/* output type, see TCC_OUTPUT_XXX */
int output_type;
/* output format, see TCC_OUTPUT_FORMAT_xxx */
int output_format;
/* C language options */
int char_is_unsigned;
int leading_underscore;
int ms_extensions; /* allow nested named struct w/o identifier behave like unnamed */
int dollars_in_identifiers; /* allows '$' char in identifiers */
int ms_bitfields; /* if true, emulate MS algorithm for aligning bitfields */
/* warning switches */
int warn_write_strings;
int warn_unsupported;
int warn_error;
int warn_none;
int warn_implicit_function_declaration;
int warn_gcc_compat;
/* compile with debug symbol (and use them if error during execution) */
int do_debug;
#ifdef CONFIG_TCC_BCHECK
/* compile with built-in memory and bounds checker */
int do_bounds_check;
#endif
#ifdef TCC_TARGET_ARM
enum float_abi float_abi; /* float ABI of the generated code*/
#endif
addr_t text_addr; /* address of text section */
int has_text_addr;
unsigned section_align; /* section alignment */
char *init_symbol; /* symbols to call at load-time (not used currently) */
char *fini_symbol; /* symbols to call at unload-time (not used currently) */
#ifdef TCC_TARGET_I386
int seg_size; /* 32. Can be 16 with i386 assembler (.code16) */
#endif
#ifdef TCC_TARGET_X86_64
int nosse; /* For -mno-sse support. */
#endif
/* array of all loaded dlls (including those referenced by loaded dlls) */
DLLReference **loaded_dlls;
int nb_loaded_dlls;
/* include paths */
char **include_paths;
int nb_include_paths;
char **sysinclude_paths;
int nb_sysinclude_paths;
/* library paths */
char **library_paths;
int nb_library_paths;
/* crt?.o object path */
char **crt_paths;
int nb_crt_paths;
/* -include files */
char **cmd_include_files;
int nb_cmd_include_files;
/* error handling */
void *error_opaque;
void (*error_func)(void *opaque, const char *msg);
int error_set_jmp_enabled;
jmp_buf error_jmp_buf;
int nb_errors;
/* output file for preprocessing (-E) */
FILE *ppfp;
enum {
LINE_MACRO_OUTPUT_FORMAT_GCC,
LINE_MACRO_OUTPUT_FORMAT_NONE,
LINE_MACRO_OUTPUT_FORMAT_STD,
LINE_MACRO_OUTPUT_FORMAT_P10 = 11
} Pflag; /* -P switch */
char dflag; /* -dX value */
/* for -MD/-MF: collected dependencies for this compilation */
char **target_deps;
int nb_target_deps;
/* compilation */
BufferedFile *include_stack[INCLUDE_STACK_SIZE];
BufferedFile **include_stack_ptr;
int ifdef_stack[IFDEF_STACK_SIZE];
int *ifdef_stack_ptr;
/* included files enclosed with #ifndef MACRO */
int cached_includes_hash[CACHED_INCLUDES_HASH_SIZE];
CachedInclude **cached_includes;
int nb_cached_includes;
/* #pragma pack stack */
int pack_stack[PACK_STACK_SIZE];
int *pack_stack_ptr;
char **pragma_libs;
int nb_pragma_libs;
/* inline functions are stored as token lists and compiled last
only if referenced */
struct InlineFunc **inline_fns;
int nb_inline_fns;
/* sections */
Section **sections;
int nb_sections; /* number of sections, including first dummy section */
Section **priv_sections;
int nb_priv_sections; /* number of private sections */
/* got & plt handling */
Section *got;
Section *plt;
/* temporary dynamic symbol sections (for dll loading) */
Section *dynsymtab_section;
/* exported dynamic symbol section */
Section *dynsym;
/* copy of the global symtab_section variable */
Section *symtab;
/* extra attributes (eg. GOT/PLT value) for symtab symbols */
struct sym_attr *sym_attrs;
int nb_sym_attrs;
/* tiny assembler state */
Sym *asm_labels;
#ifdef TCC_TARGET_PE
/* PE info */
int pe_subsystem;
unsigned pe_characteristics;
unsigned pe_file_align;
unsigned pe_stack_size;
# ifdef TCC_TARGET_X86_64
Section *uw_pdata;
int uw_sym;
unsigned uw_offs;
# endif
#endif
#ifdef TCC_IS_NATIVE
const char *runtime_main;
void **runtime_mem;
int nb_runtime_mem;
#endif
/* used by main and tcc_parse_args only */
struct filespec **files; /* files seen on command line */
int nb_files; /* number thereof */
int nb_libraries; /* number of libs thereof */
int filetype;
char *outfile; /* output filename */
int option_r; /* option -r */
int do_bench; /* option -bench */
int gen_deps; /* option -MD */
char *deps_outfile; /* option -MF */
int option_pthread; /* -pthread option */
int argc;
char **argv;
};
struct filespec {
char type;
char alacarte;
char name[1];
};
/* The current value can be: */
#define VT_VALMASK 0x003f /* mask for value location, register or: */
#define VT_CONST 0x0030 /* constant in vc (must be first non register value) */
#define VT_LLOCAL 0x0031 /* lvalue, offset on stack */
#define VT_LOCAL 0x0032 /* offset on stack */
#define VT_CMP 0x0033 /* the value is stored in processor flags (in vc) */
#define VT_JMP 0x0034 /* value is the consequence of jmp true (even) */
#define VT_JMPI 0x0035 /* value is the consequence of jmp false (odd) */
#define VT_LVAL 0x0100 /* var is an lvalue */
#define VT_SYM 0x0200 /* a symbol value is added */
#define VT_MUSTCAST 0x0400 /* value must be casted to be correct (used for
char/short stored in integer registers) */
#define VT_MUSTBOUND 0x0800 /* bound checking must be done before
dereferencing value */
#define VT_BOUNDED 0x8000 /* value is bounded. The address of the
bounding function call point is in vc */
#define VT_LVAL_BYTE 0x1000 /* lvalue is a byte */
#define VT_LVAL_SHORT 0x2000 /* lvalue is a short */
#define VT_LVAL_UNSIGNED 0x4000 /* lvalue is unsigned */
#define VT_LVAL_TYPE (VT_LVAL_BYTE | VT_LVAL_SHORT | VT_LVAL_UNSIGNED)
/* types */
#define VT_BTYPE 0x000f /* mask for basic type */
#define VT_INT 0 /* integer type */
#define VT_BYTE 1 /* signed byte type */
#define VT_SHORT 2 /* short type */
#define VT_VOID 3 /* void type */
#define VT_PTR 4 /* pointer */
#define VT_ENUM 5 /* enum definition */
#define VT_FUNC 6 /* function type */
#define VT_STRUCT 7 /* struct/union definition */
#define VT_FLOAT 8 /* IEEE float */
#define VT_DOUBLE 9 /* IEEE double */
#define VT_LDOUBLE 10 /* IEEE long double */
#define VT_BOOL 11 /* ISOC99 boolean type */
#define VT_LLONG 12 /* 64 bit integer */
#define VT_LONG 13 /* long integer (NEVER USED as type, only
during parsing) */
#define VT_QLONG 14 /* 128-bit integer. Only used for x86-64 ABI */
#define VT_QFLOAT 15 /* 128-bit float. Only used for x86-64 ABI */
#define VT_UNSIGNED 0x0010 /* unsigned type */
#define VT_ARRAY 0x0020 /* array type (also has VT_PTR) */
#define VT_BITFIELD 0x0040 /* bitfield modifier */
#define VT_CONSTANT 0x0800 /* const modifier */
#define VT_VOLATILE 0x1000 /* volatile modifier */
#define VT_DEFSIGN 0x2000 /* signed type */
#define VT_VLA 0x00020000 /* VLA type (also has VT_PTR and VT_ARRAY) */
/* storage */
#define VT_EXTERN 0x00000080 /* extern definition */
#define VT_STATIC 0x00000100 /* static variable */
#define VT_TYPEDEF 0x00000200 /* typedef definition */
#define VT_INLINE 0x00000400 /* inline definition */
#define VT_IMPORT 0x00004000 /* win32: extern data imported from dll */
#define VT_EXPORT 0x00008000 /* win32: data exported from dll */
#define VT_WEAK 0x00010000 /* weak symbol */
#define VT_TLS 0x00040000 /* thread-local storage */
#define VT_VIS_SHIFT 19 /* shift for symbol visibility, overlapping
bitfield values, because bitfields never
have linkage and hence never have
visibility. */
#define VT_VIS_SIZE 2 /* We have four visibilities. */
#define VT_VIS_MASK (((1 << VT_VIS_SIZE)-1) << VT_VIS_SHIFT)
#define VT_STRUCT_SHIFT 19 /* shift for bitfield shift values (max: 32 - 2*6) */
/* type mask (except storage) */
#define VT_STORAGE (VT_EXTERN | VT_STATIC | VT_TYPEDEF | VT_INLINE | VT_IMPORT | VT_EXPORT | VT_WEAK | VT_VIS_MASK)
#define VT_TYPE (~(VT_STORAGE))
/* token values */
/* warning: the following compare tokens depend on i386 asm code */
#define TOK_ULT 0x92
#define TOK_UGE 0x93
#define TOK_EQ 0x94
#define TOK_NE 0x95
#define TOK_ULE 0x96
#define TOK_UGT 0x97
#define TOK_Nset 0x98
#define TOK_Nclear 0x99
#define TOK_LT 0x9c
#define TOK_GE 0x9d
#define TOK_LE 0x9e
#define TOK_GT 0x9f
#define TOK_LAND 0xa0
#define TOK_LOR 0xa1
#define TOK_DEC 0xa2
#define TOK_MID 0xa3 /* inc/dec, to void constant */
#define TOK_INC 0xa4
#define TOK_UDIV 0xb0 /* unsigned division */
#define TOK_UMOD 0xb1 /* unsigned modulo */
#define TOK_PDIV 0xb2 /* fast division with undefined rounding for pointers */
/* tokens that carry values (in additional token string space / tokc) --> */
#define TOK_CCHAR 0xb3 /* char constant in tokc */
#define TOK_LCHAR 0xb4
#define TOK_CINT 0xb5 /* number in tokc */
#define TOK_CUINT 0xb6 /* unsigned int constant */
#define TOK_CLLONG 0xb7 /* long long constant */
#define TOK_CULLONG 0xb8 /* unsigned long long constant */
#define TOK_STR 0xb9 /* pointer to string in tokc */
#define TOK_LSTR 0xba
#define TOK_CFLOAT 0xbb /* float constant */
#define TOK_CDOUBLE 0xbc /* double constant */
#define TOK_CLDOUBLE 0xbd /* long double constant */
#define TOK_PPNUM 0xbe /* preprocessor number */
#define TOK_PPSTR 0xbf /* preprocessor string */
#define TOK_LINENUM 0xc0 /* line number info */
/* <-- */
#define TOK_UMULL 0xc2 /* unsigned 32x32 -> 64 mul */
#define TOK_ADDC1 0xc3 /* add with carry generation */
#define TOK_ADDC2 0xc4 /* add with carry use */
#define TOK_SUBC1 0xc5 /* add with carry generation */
#define TOK_SUBC2 0xc6 /* add with carry use */
#define TOK_ARROW 0xc7
#define TOK_DOTS 0xc8 /* three dots */
#define TOK_SHR 0xc9 /* unsigned shift right */
#define TOK_TWOSHARPS 0xca /* ## preprocessing token */
#define TOK_PLCHLDR 0xcb /* placeholder token as defined in C99 */
#define TOK_NOSUBST 0xcc /* means following token has already been pp'd */
#define TOK_PPJOIN 0xce /* A '##' in the right position to mean pasting */
#define TOK_SHL 0x01 /* shift left */
#define TOK_SAR 0x02 /* signed shift right */
/* assignment operators : normal operator or 0x80 */
#define TOK_A_MOD 0xa5
#define TOK_A_AND 0xa6
#define TOK_A_MUL 0xaa
#define TOK_A_ADD 0xab
#define TOK_A_SUB 0xad
#define TOK_A_DIV 0xaf
#define TOK_A_XOR 0xde
#define TOK_A_OR 0xfc
#define TOK_A_SHL 0x81
#define TOK_A_SAR 0x82
#ifndef offsetof
#define offsetof(type, field) ((size_t) &((type *)0)->field)
#endif
#ifndef countof
#define countof(tab) (sizeof(tab) / sizeof((tab)[0]))
#endif
#define TOK_EOF (-1) /* end of file */
#define TOK_LINEFEED 10 /* line feed */
/* all identifiers and strings have token above that */
#define TOK_IDENT 256
#define DEF_ASM(x) DEF(TOK_ASM_ ## x, #x)
#define TOK_ASM_int TOK_INT
#define DEF_ASMDIR(x) DEF(TOK_ASMDIR_ ## x, "." #x)
#define TOK_ASMDIR_FIRST TOK_ASMDIR_byte
#define TOK_ASMDIR_LAST TOK_ASMDIR_section
#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
/* only used for i386 asm opcodes definitions */
#define DEF_BWL(x) \
DEF(TOK_ASM_ ## x ## b, #x "b") \
DEF(TOK_ASM_ ## x ## w, #x "w") \
DEF(TOK_ASM_ ## x ## l, #x "l") \
DEF(TOK_ASM_ ## x, #x)
#define DEF_WL(x) \
DEF(TOK_ASM_ ## x ## w, #x "w") \
DEF(TOK_ASM_ ## x ## l, #x "l") \
DEF(TOK_ASM_ ## x, #x)
#ifdef TCC_TARGET_X86_64
# define DEF_BWLQ(x) \
DEF(TOK_ASM_ ## x ## b, #x "b") \
DEF(TOK_ASM_ ## x ## w, #x "w") \
DEF(TOK_ASM_ ## x ## l, #x "l") \
DEF(TOK_ASM_ ## x ## q, #x "q") \
DEF(TOK_ASM_ ## x, #x)
# define DEF_WLQ(x) \
DEF(TOK_ASM_ ## x ## w, #x "w") \
DEF(TOK_ASM_ ## x ## l, #x "l") \
DEF(TOK_ASM_ ## x ## q, #x "q") \
DEF(TOK_ASM_ ## x, #x)
# define DEF_BWLX DEF_BWLQ
# define DEF_WLX DEF_WLQ
/* number of sizes + 1 */
# define NBWLX 5
#else
# define DEF_BWLX DEF_BWL
# define DEF_WLX DEF_WL
/* number of sizes + 1 */
# define NBWLX 4
#endif
#define DEF_FP1(x) \
DEF(TOK_ASM_ ## f ## x ## s, "f" #x "s") \
DEF(TOK_ASM_ ## fi ## x ## l, "fi" #x "l") \
DEF(TOK_ASM_ ## f ## x ## l, "f" #x "l") \
DEF(TOK_ASM_ ## fi ## x ## s, "fi" #x "s")
#define DEF_FP(x) \
DEF(TOK_ASM_ ## f ## x, "f" #x ) \
DEF(TOK_ASM_ ## f ## x ## p, "f" #x "p") \
DEF_FP1(x)
#define DEF_ASMTEST(x,suffix) \
DEF_ASM(x ## o ## suffix) \
DEF_ASM(x ## no ## suffix) \
DEF_ASM(x ## b ## suffix) \
DEF_ASM(x ## c ## suffix) \
DEF_ASM(x ## nae ## suffix) \
DEF_ASM(x ## nb ## suffix) \
DEF_ASM(x ## nc ## suffix) \
DEF_ASM(x ## ae ## suffix) \
DEF_ASM(x ## e ## suffix) \
DEF_ASM(x ## z ## suffix) \
DEF_ASM(x ## ne ## suffix) \
DEF_ASM(x ## nz ## suffix) \
DEF_ASM(x ## be ## suffix) \
DEF_ASM(x ## na ## suffix) \
DEF_ASM(x ## nbe ## suffix) \
DEF_ASM(x ## a ## suffix) \
DEF_ASM(x ## s ## suffix) \
DEF_ASM(x ## ns ## suffix) \
DEF_ASM(x ## p ## suffix) \
DEF_ASM(x ## pe ## suffix) \
DEF_ASM(x ## np ## suffix) \
DEF_ASM(x ## po ## suffix) \
DEF_ASM(x ## l ## suffix) \
DEF_ASM(x ## nge ## suffix) \
DEF_ASM(x ## nl ## suffix) \
DEF_ASM(x ## ge ## suffix) \
DEF_ASM(x ## le ## suffix) \
DEF_ASM(x ## ng ## suffix) \
DEF_ASM(x ## nle ## suffix) \
DEF_ASM(x ## g ## suffix)
#endif /* defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 */
enum tcc_token {
TOK_LAST = TOK_IDENT - 1
#define DEF(id, str) ,id
#include "tcctok.h"
#undef DEF
};
/* keywords: tok >= TOK_IDENT && tok < TOK_UIDENT */
#define TOK_UIDENT TOK_DEFINE
/* ------------ libtcc.c ------------ */
/* use GNU C extensions */
ST_DATA int gnu_ext;
/* use Tiny C extensions */
ST_DATA int tcc_ext;
/* XXX: get rid of this ASAP */
ST_DATA struct TCCState *tcc_state;
/* public functions currently used by the tcc main function */
ST_FUNC char *pstrcpy(char *buf, int buf_size, const char *s);
ST_FUNC char *pstrcat(char *buf, int buf_size, const char *s);
ST_FUNC char *pstrncpy(char *out, const char *in, size_t num);
PUB_FUNC char *tcc_basename(const char *name);
PUB_FUNC char *tcc_fileextension (const char *name);
#ifndef MEM_DEBUG
PUB_FUNC void tcc_free(void *ptr);
PUB_FUNC void *tcc_malloc(unsigned long size);
PUB_FUNC void *tcc_mallocz(unsigned long size);
PUB_FUNC void *tcc_realloc(void *ptr, unsigned long size);
PUB_FUNC char *tcc_strdup(const char *str);
#else
#define tcc_free(ptr) tcc_free_debug(ptr)
#define tcc_malloc(size) tcc_malloc_debug(size, __FILE__, __LINE__)
#define tcc_mallocz(size) tcc_mallocz_debug(size, __FILE__, __LINE__)
#define tcc_realloc(ptr,size) tcc_realloc_debug(ptr, size, __FILE__, __LINE__)
#define tcc_strdup(str) tcc_strdup_debug(str, __FILE__, __LINE__)
PUB_FUNC void tcc_free_debug(void *ptr);
PUB_FUNC void *tcc_malloc_debug(unsigned long size, const char *file, int line);
PUB_FUNC void *tcc_mallocz_debug(unsigned long size, const char *file, int line);
PUB_FUNC void *tcc_realloc_debug(void *ptr, unsigned long size, const char *file, int line);
PUB_FUNC char *tcc_strdup_debug(const char *str, const char *file, int line);
#endif
#define free(p) use_tcc_free(p)
#define malloc(s) use_tcc_malloc(s)
#define realloc(p, s) use_tcc_realloc(p, s)
#undef strdup
#define strdup(s) use_tcc_strdup(s)
PUB_FUNC void tcc_memcheck(void);
PUB_FUNC void tcc_error_noabort(const char *fmt, ...);
PUB_FUNC NORETURN void tcc_error(const char *fmt, ...);
PUB_FUNC void tcc_warning(const char *fmt, ...);
/* other utilities */
ST_FUNC void dynarray_add(void *ptab, int *nb_ptr, void *data);
ST_FUNC void dynarray_reset(void *pp, int *n);
ST_INLN void cstr_ccat(CString *cstr, int ch);
ST_FUNC void cstr_cat(CString *cstr, const char *str, int len);
ST_FUNC void cstr_wccat(CString *cstr, int ch);
ST_FUNC void cstr_new(CString *cstr);
ST_FUNC void cstr_free(CString *cstr);
ST_FUNC void cstr_reset(CString *cstr);
ST_INLN void sym_free(Sym *sym);
ST_FUNC Sym *sym_push2(Sym **ps, int v, int t, long c);
ST_FUNC Sym *sym_find2(Sym *s, int v);
ST_FUNC Sym *sym_push(int v, CType *type, int r, long c);
ST_FUNC void sym_pop(Sym **ptop, Sym *b, int keep);
ST_INLN Sym *struct_find(int v);
ST_INLN Sym *sym_find(int v);
ST_FUNC Sym *global_identifier_push(int v, int t, long c);
ST_FUNC void tcc_open_bf(TCCState *s1, const char *filename, int initlen);
ST_FUNC int tcc_open(TCCState *s1, const char *filename);
ST_FUNC void tcc_close(void);
ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags);
/* flags: */
#define AFF_PRINT_ERROR 0x10 /* print error if file not found */
#define AFF_REFERENCED_DLL 0x20 /* load a referenced dll from another dll */
#define AFF_PREPROCESS 0x40 /* preprocess file */
/* combined with: */
#define AFF_TYPE_NONE 0
#define AFF_TYPE_C 1
#define AFF_TYPE_ASM 2
#define AFF_TYPE_ASMPP 3
#define AFF_TYPE_BIN 4
#define AFF_TYPE_LIB 5
/* values from tcc_object_type(...) */
#define AFF_BINTYPE_REL 1
#define AFF_BINTYPE_DYN 2
#define AFF_BINTYPE_AR 3
#define AFF_BINTYPE_C67 4
ST_FUNC int tcc_add_crt(TCCState *s, const char *filename);
ST_FUNC int tcc_add_dll(TCCState *s, const char *filename, int flags);
ST_FUNC void tcc_add_pragma_libs(TCCState *s1);
PUB_FUNC int tcc_add_library_err(TCCState *s, const char *f);
PUB_FUNC void tcc_print_stats(TCCState *s, unsigned total_time);
PUB_FUNC int tcc_parse_args(TCCState *s, int *argc, char ***argv, int optind);
#ifdef _WIN32
ST_FUNC char *normalize_slashes(char *path);
#endif
/* tcc_parse_args return codes: */
#define OPT_HELP 1
#define OPT_HELP2 2
#define OPT_V 3
#define OPT_PRINT_DIRS 4
#define OPT_AR 5
#define OPT_IMPDEF 6
#define OPT_M32 32
#define OPT_M64 64
/* ------------ tccpp.c ------------ */
ST_DATA struct BufferedFile *file;
ST_DATA int ch, tok;
ST_DATA CValue tokc;
ST_DATA const int *macro_ptr;
ST_DATA int parse_flags;
ST_DATA int tok_flags;
ST_DATA CString tokcstr; /* current parsed string, if any */
/* display benchmark infos */
ST_DATA int total_lines;
ST_DATA int total_bytes;
ST_DATA int tok_ident;
ST_DATA TokenSym **table_ident;
#define TOK_FLAG_BOL 0x0001 /* beginning of line before */
#define TOK_FLAG_BOF 0x0002 /* beginning of file before */
#define TOK_FLAG_ENDIF 0x0004 /* a endif was found matching starting #ifdef */
#define TOK_FLAG_EOF 0x0008 /* end of file */
#define PARSE_FLAG_PREPROCESS 0x0001 /* activate preprocessing */
#define PARSE_FLAG_TOK_NUM 0x0002 /* return numbers instead of TOK_PPNUM */
#define PARSE_FLAG_LINEFEED 0x0004 /* line feed is returned as a
token. line feed is also
returned at eof */
#define PARSE_FLAG_ASM_FILE 0x0008 /* we processing an asm file: '#' can be used for line comment, etc. */
#define PARSE_FLAG_SPACES 0x0010 /* next() returns space tokens (for -E) */
#define PARSE_FLAG_ACCEPT_STRAYS 0x0020 /* next() returns '\\' token */
#define PARSE_FLAG_TOK_STR 0x0040 /* return parsed strings instead of TOK_PPSTR */
/* isidnum_table flags: */
#define IS_SPC 1
#define IS_ID 2
#define IS_NUM 4
ST_FUNC TokenSym *tok_alloc(const char *str, int len);
ST_FUNC const char *get_tok_str(int v, CValue *cv);
ST_FUNC void begin_macro(TokenString *str, int alloc);
ST_FUNC void end_macro(void);
ST_FUNC void set_idnum(int c, int val);
ST_FUNC void save_parse_state(ParseState *s);
ST_FUNC void restore_parse_state(ParseState *s);
ST_INLN void tok_str_new(TokenString *s);
ST_FUNC TokenString *tok_str_alloc(void);
ST_FUNC void tok_str_free(TokenString *s);
ST_FUNC void tok_str_free_str(int *str);
ST_FUNC void tok_str_add(TokenString *s, int t);
ST_FUNC void tok_str_add_tok(TokenString *s);
ST_INLN void define_push(int v, int macro_type, int *str, Sym *first_arg);
ST_FUNC void define_undef(Sym *s);
ST_INLN Sym *define_find(int v);
ST_FUNC void free_defines(Sym *b);
ST_FUNC Sym *label_find(int v);
ST_FUNC Sym *label_push(Sym **ptop, int v, int flags);
ST_FUNC void label_pop(Sym **ptop, Sym *slast);
ST_FUNC void parse_define(void);
ST_FUNC void preprocess(int is_bof);
ST_FUNC void next_nomacro(void);
ST_FUNC void next(void);
ST_INLN void unget_tok(int last_tok);
ST_FUNC void preprocess_start(TCCState *s1);
ST_FUNC void tccpp_new(TCCState *s);
ST_FUNC void tccpp_delete(TCCState *s);
ST_FUNC int tcc_preprocess(TCCState *s1);
ST_FUNC void skip(int c);
ST_FUNC NORETURN void expect(const char *msg);
/* space excluding newline */
static inline int is_space(int ch) {
return ch == ' ' || ch == '\t' || ch == '\v' || ch == '\f' || ch == '\r';
}
static inline int isid(int c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
}
static inline int isnum(int c) {
return c >= '0' && c <= '9';
}
static inline int isoct(int c) {
return c >= '0' && c <= '7';
}
static inline int toup(int c) {
return (c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c;
}
/* ------------ tccgen.c ------------ */
#define SYM_POOL_NB (8192 / sizeof(Sym))
ST_DATA Sym *sym_free_first;
ST_DATA void **sym_pools;
ST_DATA int nb_sym_pools;
ST_DATA Sym *global_stack;
ST_DATA Sym *local_stack;
ST_DATA Sym *local_label_stack;
ST_DATA Sym *global_label_stack;
ST_DATA Sym *define_stack;
ST_DATA CType char_pointer_type, func_old_type, int_type, size_type;
ST_DATA SValue __vstack[1+/*to make bcheck happy*/ VSTACK_SIZE], *vtop, *pvtop;
#define vstack (__vstack + 1)
ST_DATA int rsym, anon_sym, ind, loc;
ST_DATA int const_wanted; /* true if constant wanted */
ST_DATA int nocode_wanted; /* true if no code generation wanted for an expression */
ST_DATA int global_expr; /* true if compound literals must be allocated globally (used during initializers parsing */
ST_DATA CType func_vt; /* current function return type (used by return instruction) */
ST_DATA int func_var; /* true if current function is variadic */
ST_DATA int func_vc;
ST_DATA int last_line_num, last_ind, func_ind; /* debug last line number and pc */
ST_DATA const char *funcname;
ST_FUNC void tcc_debug_start(TCCState *s1);
ST_FUNC void tcc_debug_end(TCCState *s1);
ST_FUNC void tcc_debug_funcstart(TCCState *s1, Sym *sym);
ST_FUNC void tcc_debug_funcend(TCCState *s1, int size);
ST_FUNC void tcc_debug_line(TCCState *s1);
ST_FUNC void tccgen_start(TCCState *s1);
ST_FUNC void tccgen_end(TCCState *s1);
ST_FUNC void free_inline_functions(TCCState *s);
ST_FUNC void check_vstack(void);
ST_INLN int is_float(int t);
ST_FUNC int ieee_finite(double d);
ST_FUNC void test_lvalue(void);
ST_FUNC void vpushi(int v);
ST_FUNC Sym *external_global_sym(int v, CType *type, int r);
ST_FUNC void vset(CType *type, int r, long v);
ST_FUNC void vswap(void);
ST_FUNC void vpush_global_sym(CType *type, int v);
ST_FUNC void vrote(SValue *e, int n);
ST_FUNC void vrott(int n);
ST_FUNC void vrotb(int n);
#ifdef TCC_TARGET_ARM
ST_FUNC int get_reg_ex(int rc, int rc2);
ST_FUNC void lexpand_nr(void);
#endif
ST_FUNC void vpushv(SValue *v);
ST_FUNC void save_reg(int r);
ST_FUNC void save_reg_upstack(int r, int n);
ST_FUNC int get_reg(int rc);
ST_FUNC void save_regs(int n);
ST_FUNC void gaddrof(void);
ST_FUNC int gv(int rc);
ST_FUNC void gv2(int rc1, int rc2);
ST_FUNC void vpop(void);
ST_FUNC void gen_op(int op);
ST_FUNC int type_size(CType *type, int *a);
ST_FUNC void mk_pointer(CType *type);
ST_FUNC void vstore(void);
ST_FUNC void inc(int post, int c);
ST_FUNC void parse_mult_str (CString *astr, const char *msg);
ST_FUNC void parse_asm_str(CString *astr);
ST_FUNC int lvalue_type(int t);
ST_FUNC void indir(void);
ST_FUNC void unary(void);
ST_FUNC void expr_prod(void);
ST_FUNC void expr_sum(void);
ST_FUNC void gexpr(void);
ST_FUNC int expr_const(void);
ST_FUNC void decl(int l);
#if defined CONFIG_TCC_BCHECK || defined TCC_TARGET_C67
ST_FUNC Sym *get_sym_ref(CType *type, Section *sec, unsigned long offset, unsigned long size);
#endif
#if defined TCC_TARGET_X86_64 && !defined TCC_TARGET_PE
ST_FUNC int classify_x86_64_va_arg(CType *ty);
#endif
/* ------------ tccelf.c ------------ */
#define TCC_OUTPUT_FORMAT_ELF 0 /* default output format: ELF */
#define TCC_OUTPUT_FORMAT_BINARY 1 /* binary image output */
#define TCC_OUTPUT_FORMAT_COFF 2 /* COFF */
#define ARMAG "!<arch>\012" /* For COFF and a.out archives */
typedef struct {
unsigned int n_strx; /* index into string table of name */
unsigned char n_type; /* type of symbol */
unsigned char n_other; /* misc info (usually empty) */
unsigned short n_desc; /* description field */
unsigned int n_value; /* value of symbol */
} Stab_Sym;
ST_DATA Section *text_section, *data_section, *bss_section; /* predefined sections */
ST_DATA Section *common_section;
ST_DATA Section *cur_text_section; /* current section where function code is generated */
#ifdef CONFIG_TCC_ASM
ST_DATA Section *last_text_section; /* to handle .previous asm directive */
#endif
#ifdef CONFIG_TCC_BCHECK
/* bound check related sections */
ST_DATA Section *bounds_section; /* contains global data bound description */
ST_DATA Section *lbounds_section; /* contains local data bound description */
ST_FUNC void tccelf_bounds_new(TCCState *s);
#endif
/* symbol sections */
ST_DATA Section *symtab_section, *strtab_section;
/* debug sections */
ST_DATA Section *stab_section, *stabstr_section;
ST_FUNC void tccelf_new(TCCState *s);
ST_FUNC void tccelf_delete(TCCState *s);
ST_FUNC void tccelf_stab_new(TCCState *s);
ST_FUNC Section *new_section(TCCState *s1, const char *name, int sh_type, int sh_flags);
ST_FUNC void section_realloc(Section *sec, unsigned long new_size);
ST_FUNC size_t section_add(Section *sec, addr_t size, int align);
ST_FUNC void *section_ptr_add(Section *sec, addr_t size);
ST_FUNC void section_reserve(Section *sec, unsigned long size);
ST_FUNC Section *find_section(TCCState *s1, const char *name);
ST_FUNC Section *new_symtab(TCCState *s1, const char *symtab_name, int sh_type, int sh_flags, const char *strtab_name, const char *hash_name, int hash_sh_flags);
ST_FUNC void put_extern_sym2(Sym *sym, Section *section, addr_t value, unsigned long size, int can_add_underscore);
ST_FUNC void put_extern_sym(Sym *sym, Section *section, addr_t value, unsigned long size);
#if PTR_SIZE == 4
ST_FUNC void greloc(Section *s, Sym *sym, unsigned long offset, int type);
#endif
ST_FUNC void greloca(Section *s, Sym *sym, unsigned long offset, int type, addr_t addend);
ST_FUNC int put_elf_str(Section *s, const char *sym);
ST_FUNC int put_elf_sym(Section *s, addr_t value, unsigned long size, int info, int other, int shndx, const char *name);
ST_FUNC int set_elf_sym(Section *s, addr_t value, unsigned long size, int info, int other, int shndx, const char *name);
ST_FUNC int find_elf_sym(Section *s, const char *name);
ST_FUNC void put_elf_reloc(Section *symtab, Section *s, unsigned long offset, int type, int symbol);
ST_FUNC void put_elf_reloca(Section *symtab, Section *s, unsigned long offset, int type, int symbol, addr_t addend);
ST_FUNC void put_stabs(const char *str, int type, int other, int desc, unsigned long value);
ST_FUNC void put_stabs_r(const char *str, int type, int other, int desc, unsigned long value, Section *sec, int sym_index);
ST_FUNC void put_stabn(int type, int other, int desc, int value);
ST_FUNC void put_stabd(int type, int other, int desc);
ST_FUNC void relocate_common_syms(void);
ST_FUNC void relocate_syms(TCCState *s1, Section *symtab, int do_resolve);
ST_FUNC void relocate_section(TCCState *s1, Section *s);
ST_FUNC void tcc_add_linker_symbols(TCCState *s1);
ST_FUNC int tcc_object_type(int fd, ElfW(Ehdr) *h);
ST_FUNC int tcc_load_object_file(TCCState *s1, int fd, unsigned long file_offset);
ST_FUNC int tcc_load_archive(TCCState *s1, int fd);
ST_FUNC void tcc_add_bcheck(TCCState *s1);
ST_FUNC void tcc_add_runtime(TCCState *s1);
ST_FUNC void build_got_entries(TCCState *s1);
ST_FUNC struct sym_attr *get_sym_attr(TCCState *s1, int index, int alloc);
ST_FUNC void squeeze_multi_relocs(Section *sec, size_t oldrelocoffset);
ST_FUNC addr_t get_elf_sym_addr(TCCState *s, const char *name, int err);
#if defined TCC_IS_NATIVE || defined TCC_TARGET_PE
ST_FUNC void *tcc_get_symbol_err(TCCState *s, const char *name);
#endif
#ifndef TCC_TARGET_PE
ST_FUNC int tcc_load_dll(TCCState *s1, int fd, const char *filename, int level);
ST_FUNC int tcc_load_ldscript(TCCState *s1);
ST_FUNC uint8_t *parse_comment(uint8_t *p);
ST_FUNC void minp(void);
ST_INLN void inp(void);
ST_FUNC int handle_eob(void);
#endif
/* ------------ xxx-link.c ------------ */
/* Wether to generate a GOT/PLT entry and when. NO_GOTPLT_ENTRY is first so
that unknown relocation don't create a GOT or PLT entry */
enum gotplt_entry {
NO_GOTPLT_ENTRY, /* never generate (eg. GLOB_DAT & JMP_SLOT relocs) */
BUILD_GOT_ONLY, /* only build GOT (eg. TPOFF relocs) */
AUTO_GOTPLT_ENTRY, /* generate if sym is UNDEF */
ALWAYS_GOTPLT_ENTRY /* always generate (eg. PLTOFF relocs) */
};
ST_FUNC int code_reloc (int reloc_type);
ST_FUNC int gotplt_entry_type (int reloc_type);
ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_attr *attr);
ST_FUNC void relocate_init(Section *sr);
ST_FUNC void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, addr_t addr, addr_t val);
ST_FUNC void relocate_plt(TCCState *s1);
/* ------------ xxx-gen.c ------------ */
ST_DATA const int reg_classes[NB_REGS];
ST_FUNC void gsym_addr(int t, int a);
ST_FUNC void gsym(int t);
ST_FUNC void load(int r, SValue *sv);
ST_FUNC void store(int r, SValue *v);
ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *align, int *regsize);
ST_FUNC void gfunc_call(int nb_args);
ST_FUNC void gfunc_prolog(CType *func_type);
ST_FUNC void gfunc_epilog(void);
ST_FUNC int gjmp(int t);
ST_FUNC void gjmp_addr(int a);
ST_FUNC int gtst(int inv, int t);
#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
ST_FUNC void gtst_addr(int inv, int a);
#else
#define gtst_addr(inv, a) gsym_addr(gtst(inv, 0), a)
#endif
ST_FUNC void gen_opi(int op);
ST_FUNC void gen_opf(int op);
ST_FUNC void gen_cvt_ftoi(int t);
ST_FUNC void gen_cvt_ftof(int t);
ST_FUNC void ggoto(void);
#ifndef TCC_TARGET_C67
ST_FUNC void o(unsigned int c);
#endif
#ifndef TCC_TARGET_ARM
ST_FUNC void gen_cvt_itof(int t);
#endif
ST_FUNC void gen_vla_sp_save(int addr);
ST_FUNC void gen_vla_sp_restore(int addr);
ST_FUNC void gen_vla_alloc(CType *type, int align);
static inline uint16_t read16le(unsigned char *p) {
return p[0] | (uint16_t)p[1] << 8;
}
static inline void write16le(unsigned char *p, uint16_t x) {
p[0] = x & 255, p[1] = x >> 8 & 255;
}
static inline uint32_t read32le(unsigned char *p) {
return read16le(p) | (uint32_t)read16le(p + 2) << 16;
}
static inline void write32le(unsigned char *p, uint32_t x) {
write16le(p, x), write16le(p + 2, x >> 16);
}
static inline void add32le(unsigned char *p, int32_t x) {
write32le(p, read32le(p) + x);
}
#if HAVE_LONG_LONG
static inline uint64_t read64le(unsigned char *p) {
return read32le(p) | (uint64_t)read32le(p + 4) << 32;
}
static inline void write64le(unsigned char *p, uint64_t x) {
write32le(p, x), write32le(p + 4, x >> 32);
}
static inline void add64le(unsigned char *p, int64_t x) {
write64le(p, read64le(p) + x);
}
#endif
/* ------------ i386-gen.c ------------ */
#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
ST_FUNC void g(int c);
ST_FUNC void gen_le16(int c);
ST_FUNC void gen_le32(int c);
ST_FUNC void gen_addr32(int r, Sym *sym, long c);
ST_FUNC void gen_addrpc32(int r, Sym *sym, long c);
#endif
#ifdef CONFIG_TCC_BCHECK
ST_FUNC void gen_bounded_ptr_add(void);
ST_FUNC void gen_bounded_ptr_deref(void);
#endif
/* ------------ x86_64-gen.c ------------ */
#ifdef TCC_TARGET_X86_64
ST_FUNC void gen_addr64(int r, Sym *sym, int64_t c);
ST_FUNC void gen_opl(int op);
#endif
/* ------------ arm-gen.c ------------ */
#ifdef TCC_TARGET_ARM
#if defined(TCC_ARM_EABI) && !defined(CONFIG_TCC_ELFINTERP)
PUB_FUNC const char *default_elfinterp(struct TCCState *s);
#endif
ST_FUNC void arm_init(struct TCCState *s);
ST_FUNC void gen_cvt_itof1(int t);
#endif
/* ------------ arm64-gen.c ------------ */
#ifdef TCC_TARGET_ARM64
ST_FUNC void gen_cvt_sxtw(void);
ST_FUNC void gen_opl(int op);
ST_FUNC void gfunc_return(CType *func_type);
ST_FUNC void gen_va_start(void);
ST_FUNC void gen_va_arg(CType *t);
ST_FUNC void gen_clear_cache(void);
#endif
/* ------------ c67-gen.c ------------ */
#ifdef TCC_TARGET_C67
#endif
/* ------------ tcccoff.c ------------ */
#ifdef TCC_TARGET_COFF
ST_FUNC int tcc_output_coff(TCCState *s1, FILE *f);
ST_FUNC int tcc_load_coff(TCCState * s1, int fd);
#endif
/* ------------ tccasm.c ------------ */
ST_FUNC void asm_instr(void);
ST_FUNC void asm_global_instr(void);
#ifdef CONFIG_TCC_ASM
ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands, const char *name, const char **pp);
ST_FUNC Sym* get_asm_sym(int name, Sym *csym);
ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe);
ST_FUNC int asm_int_expr(TCCState *s1);
ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess);
/* ------------ i386-asm.c ------------ */
ST_FUNC void gen_expr32(ExprValue *pe);
#ifdef TCC_TARGET_X86_64
ST_FUNC void gen_expr64(ExprValue *pe);
#endif
ST_FUNC void asm_opcode(TCCState *s1, int opcode);
ST_FUNC int asm_parse_regvar(int t);
ST_FUNC void asm_compute_constraints(ASMOperand *operands, int nb_operands, int nb_outputs, const uint8_t *clobber_regs, int *pout_reg);
ST_FUNC void subst_asm_operand(CString *add_str, SValue *sv, int modifier);
ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands, int nb_outputs, int is_output, uint8_t *clobber_regs, int out_reg);
ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str);
#endif
/* ------------ tccpe.c -------------- */
#ifdef TCC_TARGET_PE
ST_FUNC int pe_load_file(struct TCCState *s1, const char *filename, int fd);
ST_FUNC int pe_output_file(TCCState * s1, const char *filename);
ST_FUNC int pe_putimport(TCCState *s1, int dllindex, const char *name, addr_t value);
#ifndef TCC_TARGET_ARM
ST_FUNC SValue *pe_getimport(SValue *sv, SValue *v2);
#endif
#ifdef TCC_TARGET_X86_64
ST_FUNC void pe_add_unwind_data(unsigned start, unsigned end, unsigned stack);
#endif
PUB_FUNC int tcc_get_dllexports(const char *filename, char **pp);
/* symbol properties stored in Elf32_Sym->st_other */
# define ST_PE_EXPORT 0x10
# define ST_PE_IMPORT 0x20
# define ST_PE_STDCALL 0x40
#endif
/* ------------ tccrun.c ----------------- */
#ifdef TCC_IS_NATIVE
#ifdef CONFIG_TCC_STATIC
#define RTLD_LAZY 0x001
#define RTLD_NOW 0x002
#define RTLD_GLOBAL 0x100
#define RTLD_DEFAULT NULL
/* dummy function for profiling */
ST_FUNC void *dlopen(const char *filename, int flag);
ST_FUNC void dlclose(void *p);
ST_FUNC const char *dlerror(void);
ST_FUNC void *dlsym(void *handle, const char *symbol);
#endif
#ifdef CONFIG_TCC_BACKTRACE
ST_DATA int rt_num_callers;
ST_DATA const char **rt_bound_error_msg;
ST_DATA void *rt_prog_main;
ST_FUNC void tcc_set_num_callers(int n);
#endif
ST_FUNC void tcc_run_free(TCCState *s1);
#endif
/* ------------ tcctools.c ----------------- */
#if 0 /* included in tcc.c */
ST_FUNC int tcc_tool_ar(TCCState *s, int argc, char **argv);
#ifdef TCC_TARGET_PE
ST_FUNC int tcc_tool_impdef(TCCState *s, int argc, char **argv);
#endif
ST_FUNC void tcc_tool_cross(TCCState *s, char **argv, int option);
ST_FUNC void gen_makedeps(TCCState *s, const char *target, const char *filename);
#endif
/********************************************************/
#undef ST_DATA
#ifdef ONE_SOURCE
#define ST_DATA static
#else
#define ST_DATA
#endif
/********************************************************/
#endif /* _TCC_H */
/*
* TCC - Tiny C Compiler
*
* Copyright (c) 2001-2004 Fabrice Bellard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _TCC_H
#include "tcc.h"
#endif
#if BOOTSTRAP && __arm__
void
tccpp_ok ()
{
}
#endif
/********************************************************/
/* global variables */
ST_DATA int tok_flags;
ST_DATA int parse_flags;
ST_DATA struct BufferedFile *file;
ST_DATA int ch, tok;
ST_DATA CValue tokc;
ST_DATA const int *macro_ptr;
ST_DATA CString tokcstr; /* current parsed string, if any */
/* display benchmark infos */
ST_DATA int total_lines;
ST_DATA int total_bytes;
ST_DATA int tok_ident;
ST_DATA TokenSym **table_ident;
/* ------------------------------------------------------------------------- */
static TokenSym *hash_ident[TOK_HASH_SIZE];
static char token_buf[STRING_MAX_SIZE + 1];
static CString cstr_buf;
static CString macro_equal_buf;
static TokenString tokstr_buf;
static unsigned char isidnum_table[256 - CH_EOF];
static int pp_debug_tok, pp_debug_symv;
static int pp_once;
static void tok_print(const char *msg, const int *str);
static struct TinyAlloc *toksym_alloc;
static struct TinyAlloc *tokstr_alloc;
static struct TinyAlloc *cstr_alloc;
static TokenString *macro_stack;
static const char tcc_keywords[] =
#define DEF(id, str) str "\0"
#include "tcctok.h"
#undef DEF
;
/* WARNING: the content of this string encodes token numbers */
static const unsigned char tok_two_chars[] =
/* outdated -- gr
"<=\236>=\235!=\225&&\240||\241++\244--\242==\224<<\1>>\2+=\253"
"-=\255*=\252/=\257%=\245&=\246^=\336|=\374->\313..\250##\266";
*/{
'<','=', TOK_LE,
'>','=', TOK_GE,
'!','=', TOK_NE,
'&','&', TOK_LAND,
'|','|', TOK_LOR,
'+','+', TOK_INC,
'-','-', TOK_DEC,
'=','=', TOK_EQ,
'<','<', TOK_SHL,
'>','>', TOK_SAR,
'+','=', TOK_A_ADD,
'-','=', TOK_A_SUB,
'*','=', TOK_A_MUL,
'/','=', TOK_A_DIV,
'%','=', TOK_A_MOD,
'&','=', TOK_A_AND,
'^','=', TOK_A_XOR,
'|','=', TOK_A_OR,
'-','>', TOK_ARROW,
'.','.', 0xa8, // C++ token ?
'#','#', TOK_TWOSHARPS,
0
};
static void next_nomacro_spc(void);
ST_FUNC void skip(int c)
{
if (tok != c)
tcc_error("'%c' expected (got \"%s\")", c, get_tok_str(tok, &tokc));
next();
}
ST_FUNC void expect(const char *msg)
{
tcc_error("%s expected", msg);
}
/* ------------------------------------------------------------------------- */
/* Custom allocator for tiny objects */
#define USE_TAL
#ifndef USE_TAL
#define tal_free(al, p) tcc_free(p)
#define tal_realloc(al, p, size) tcc_realloc(p, size)
#define tal_new(a,b,c)
#define tal_delete(a)
#else
#if !defined(MEM_DEBUG)
#define tal_free(al, p) tal_free_impl(al, p)
#define tal_realloc(al, p, size) tal_realloc_impl(&al, p, size)
#define TAL_DEBUG_PARAMS
#else
#define TAL_DEBUG 1
//#define TAL_INFO 1 /* collect and dump allocators stats */
#define tal_free(al, p) tal_free_impl(al, p, __FILE__, __LINE__)
#define tal_realloc(al, p, size) tal_realloc_impl(&al, p, size, __FILE__, __LINE__)
#define TAL_DEBUG_PARAMS , const char *file, int line
#define TAL_DEBUG_FILE_LEN 15
#endif
#define TOKSYM_TAL_SIZE (768 * 1024) /* allocator for tiny TokenSym in table_ident */
#define TOKSTR_TAL_SIZE (768 * 1024) /* allocator for tiny TokenString instances */
#define CSTR_TAL_SIZE (256 * 1024) /* allocator for tiny CString instances */
#define TOKSYM_TAL_LIMIT 256 /* prefer unique limits to distinguish allocators debug msgs */
#define TOKSTR_TAL_LIMIT 128 /* 32 * sizeof(int) */
#define CSTR_TAL_LIMIT 1024
typedef struct TinyAlloc {
unsigned limit;
unsigned size;
uint8_t *buffer;
uint8_t *p;
unsigned nb_allocs;
struct TinyAlloc *next, *top;
#ifdef TAL_INFO
unsigned nb_peak;
unsigned nb_total;
unsigned nb_missed;
uint8_t *peak_p;
#endif
} TinyAlloc;
typedef struct tal_header_t {
unsigned size;
#ifdef TAL_DEBUG
int line_num; /* negative line_num used for double free check */
char file_name[TAL_DEBUG_FILE_LEN + 1];
#endif
} tal_header_t;
/* ------------------------------------------------------------------------- */
static TinyAlloc *tal_new(TinyAlloc **pal, unsigned limit, unsigned size)
{
TinyAlloc *al = tcc_mallocz(sizeof(TinyAlloc));
al->p = al->buffer = tcc_malloc(size);
al->limit = limit;
al->size = size;
if (pal) *pal = al;
return al;
}
static void tal_delete(TinyAlloc *al)
{
TinyAlloc *next;
tail_call:
if (!al)
return;
#ifdef TAL_INFO
fprintf(stderr, "limit=%5d, size=%5g MB, nb_peak=%6d, nb_total=%8d, nb_missed=%6d, usage=%5.1f%%\n",
al->limit, al->size / 1024.0 / 1024.0, al->nb_peak, al->nb_total, al->nb_missed,
(al->peak_p - al->buffer) * 100.0 / al->size);
#endif
#ifdef TAL_DEBUG
if (al->nb_allocs > 0) {
uint8_t *p;
fprintf(stderr, "TAL_DEBUG: memory leak %d chunk(s) (limit= %d)\n",
al->nb_allocs, al->limit);
p = al->buffer;
while (p < al->p) {
tal_header_t *header = (tal_header_t *)p;
if (header->line_num > 0) {
fprintf(stderr, "%s:%d: chunk of %d bytes leaked\n",
header->file_name, header->line_num, header->size);
}
p += header->size + sizeof(tal_header_t);
}
#if MEM_DEBUG-0 == 2
exit(2);
#endif
}
#endif
next = al->next;
tcc_free(al->buffer);
tcc_free(al);
al = next;
goto tail_call;
}
static void tal_free_impl(TinyAlloc *al, void *p TAL_DEBUG_PARAMS)
{
if (!p)
return;
tail_call:
if (al->buffer <= (uint8_t *)p && (uint8_t *)p < al->buffer + al->size) {
#ifdef TAL_DEBUG
tal_header_t *header = (((tal_header_t *)p) - 1);
if (header->line_num < 0) {
fprintf(stderr, "%s:%d: TAL_DEBUG: double frees chunk from\n",
file, line);
fprintf(stderr, "%s:%d: %d bytes\n",
header->file_name, (int)-header->line_num, (int)header->size);
} else
header->line_num = -header->line_num;
#endif
al->nb_allocs--;
if (!al->nb_allocs)
al->p = al->buffer;
} else if (al->next) {
al = al->next;
goto tail_call;
}
else
tcc_free(p);
}
static void *tal_realloc_impl(TinyAlloc **pal, void *p, unsigned size TAL_DEBUG_PARAMS)
{
tal_header_t *header;
void *ret;
int is_own;
unsigned adj_size = (size + 3) & -4;
TinyAlloc *al = *pal;
tail_call:
is_own = (al->buffer <= (uint8_t *)p && (uint8_t *)p < al->buffer + al->size);
if ((!p || is_own) && size <= al->limit) {
if (al->p + adj_size + sizeof(tal_header_t) < al->buffer + al->size) {
header = (tal_header_t *)al->p;
header->size = adj_size;
#ifdef TAL_DEBUG
{ int ofs = strlen(file) - TAL_DEBUG_FILE_LEN;
strncpy(header->file_name, file + (ofs > 0 ? ofs : 0), TAL_DEBUG_FILE_LEN);
header->file_name[TAL_DEBUG_FILE_LEN] = 0;
header->line_num = line; }
#endif
ret = al->p + sizeof(tal_header_t);
al->p += adj_size + sizeof(tal_header_t);
if (is_own) {
header = (((tal_header_t *)p) - 1);
memcpy(ret, p, header->size);
#ifdef TAL_DEBUG
header->line_num = -header->line_num;
#endif
} else {
al->nb_allocs++;
}
#ifdef TAL_INFO
if (al->nb_peak < al->nb_allocs)
al->nb_peak = al->nb_allocs;
if (al->peak_p < al->p)
al->peak_p = al->p;
al->nb_total++;
#endif
return ret;
} else if (is_own) {
al->nb_allocs--;
ret = tal_realloc(*pal, 0, size);
header = (((tal_header_t *)p) - 1);
memcpy(ret, p, header->size);
#ifdef TAL_DEBUG
header->line_num = -header->line_num;
#endif
return ret;
}
if (al->next) {
al = al->next;
} else {
TinyAlloc *bottom = al, *next = al->top ? al->top : al;
al = tal_new(pal, next->limit, next->size * 2);
al->next = next;
bottom->top = al;
}
goto tail_call;
}
if (is_own) {
al->nb_allocs--;
ret = tcc_malloc(size);
header = (((tal_header_t *)p) - 1);
memcpy(ret, p, header->size);
#ifdef TAL_DEBUG
header->line_num = -header->line_num;
#endif
} else if (al->next) {
al = al->next;
goto tail_call;
} else
ret = tcc_realloc(p, size);
#ifdef TAL_INFO
al->nb_missed++;
#endif
return ret;
}
#endif /* USE_TAL */
/* ------------------------------------------------------------------------- */
/* CString handling */
static void cstr_realloc(CString *cstr, int new_size)
{
int size;
size = cstr->size_allocated;
if (size < 8)
size = 8; /* no need to allocate a too small first string */
while (size < new_size)
size = size * 2;
cstr->data = tal_realloc(cstr_alloc, cstr->data, size);
cstr->size_allocated = size;
}
/* add a byte */
ST_INLN void cstr_ccat(CString *cstr, int ch)
{
int size;
size = cstr->size + 1;
if (size > cstr->size_allocated)
cstr_realloc(cstr, size);
((unsigned char *)cstr->data)[size - 1] = ch;
cstr->size = size;
}
ST_FUNC void cstr_cat(CString *cstr, const char *str, int len)
{
int size;
if (len <= 0)
len = strlen(str) + 1 + len;
size = cstr->size + len;
if (size > cstr->size_allocated)
cstr_realloc(cstr, size);
memmove(((unsigned char *)cstr->data) + cstr->size, str, len);
cstr->size = size;
}
/* add a wide char */
ST_FUNC void cstr_wccat(CString *cstr, int ch)
{
int size;
size = cstr->size + sizeof(nwchar_t);
if (size > cstr->size_allocated)
cstr_realloc(cstr, size);
*(nwchar_t *)(((unsigned char *)cstr->data) + size - sizeof(nwchar_t)) = ch;
cstr->size = size;
}
ST_FUNC void cstr_new(CString *cstr)
{
memset(cstr, 0, sizeof(CString));
}
/* free string and reset it to NULL */
ST_FUNC void cstr_free(CString *cstr)
{
tal_free(cstr_alloc, cstr->data);
cstr_new(cstr);
}
/* reset string to empty */
ST_FUNC void cstr_reset(CString *cstr)
{
cstr->size = 0;
}
/* XXX: unicode ? */
static void add_char(CString *cstr, int c)
{
if (c == '\'' || c == '\"' || c == '\\') {
/* XXX: could be more precise if char or string */
cstr_ccat(cstr, '\\');
}
if (c >= 32 && c <= 126) {
cstr_ccat(cstr, c);
} else {
cstr_ccat(cstr, '\\');
if (c == '\n') {
cstr_ccat(cstr, 'n');
} else {
cstr_ccat(cstr, '0' + ((c >> 6) & 7));
cstr_ccat(cstr, '0' + ((c >> 3) & 7));
cstr_ccat(cstr, '0' + (c & 7));
}
}
}
/* ------------------------------------------------------------------------- */
/* allocate a new token */
static TokenSym *tok_alloc_new(TokenSym **pts, const char *str, int len)
{
TokenSym *ts, **ptable;
int i;
if (tok_ident >= SYM_FIRST_ANOM)
tcc_error("memory full (symbols)");
/* expand token table if needed */
i = tok_ident - TOK_IDENT;
if ((i % TOK_ALLOC_INCR) == 0) {
ptable = tcc_realloc(table_ident, (i + TOK_ALLOC_INCR) * sizeof(TokenSym *));
table_ident = ptable;
}
ts = tal_realloc(toksym_alloc, 0, sizeof(TokenSym) + len);
table_ident[i] = ts;
ts->tok = tok_ident++;
ts->sym_define = NULL;
ts->sym_label = NULL;
ts->sym_struct = NULL;
ts->sym_identifier = NULL;
ts->len = len;
ts->hash_next = NULL;
memcpy(ts->str, str, len);
ts->str[len] = '\0';
*pts = ts;
return ts;
}
#define TOK_HASH_INIT 1
#define TOK_HASH_FUNC(h, c) ((h) + ((h) << 5) + ((h) >> 27) + (c))
/* find a token and add it if not found */
ST_FUNC TokenSym *tok_alloc(const char *str, int len)
{
TokenSym *ts, **pts;
int i;
unsigned int h;
h = TOK_HASH_INIT;
for(i=0;i<len;i++)
h = TOK_HASH_FUNC(h, ((unsigned char *)str)[i]);
h &= (TOK_HASH_SIZE - 1);
pts = &hash_ident[h];
for(;;) {
ts = *pts;
if (!ts)
break;
if (ts->len == len && !memcmp(ts->str, str, len))
return ts;
pts = &(ts->hash_next);
}
return tok_alloc_new(pts, str, len);
}
/* XXX: buffer overflow */
/* XXX: float tokens */
ST_FUNC const char *get_tok_str(int v, CValue *cv)
{
char *p;
int i, len;
cstr_reset(&cstr_buf);
p = cstr_buf.data;
switch(v) {
case TOK_CINT:
case TOK_CUINT:
case TOK_CLLONG:
case TOK_CULLONG:
/* XXX: not quite exact, but only useful for testing */
#ifdef _WIN32
sprintf(p, "%u", (unsigned)cv->i);
#else
sprintf(p, "%llu", (unsigned long long)cv->i);
#endif
break;
case TOK_LCHAR:
cstr_ccat(&cstr_buf, 'L');
case TOK_CCHAR:
cstr_ccat(&cstr_buf, '\'');
add_char(&cstr_buf, cv->i);
cstr_ccat(&cstr_buf, '\'');
cstr_ccat(&cstr_buf, '\0');
break;
case TOK_PPNUM:
case TOK_PPSTR:
return (char*)cv->str.data;
case TOK_LSTR:
cstr_ccat(&cstr_buf, 'L');
case TOK_STR:
cstr_ccat(&cstr_buf, '\"');
if (v == TOK_STR) {
len = cv->str.size - 1;
for(i=0;i<len;i++)
add_char(&cstr_buf, ((unsigned char *)cv->str.data)[i]);
} else {
len = (cv->str.size / sizeof(nwchar_t)) - 1;
for(i=0;i<len;i++)
add_char(&cstr_buf, ((nwchar_t *)cv->str.data)[i]);
}
cstr_ccat(&cstr_buf, '\"');
cstr_ccat(&cstr_buf, '\0');
break;
case TOK_CFLOAT:
cstr_cat(&cstr_buf, "<float>", 0);
break;
case TOK_CDOUBLE:
cstr_cat(&cstr_buf, "<double>", 0);
break;
case TOK_CLDOUBLE:
cstr_cat(&cstr_buf, "<long double>", 0);
break;
case TOK_LINENUM:
cstr_cat(&cstr_buf, "<linenumber>", 0);
break;
/* above tokens have value, the ones below don't */
case TOK_LT:
v = '<';
goto addv;
case TOK_GT:
v = '>';
goto addv;
case TOK_DOTS:
return strcpy(p, "...");
case TOK_A_SHL:
return strcpy(p, "<<=");
case TOK_A_SAR:
return strcpy(p, ">>=");
default:
if (v < TOK_IDENT) {
/* search in two bytes table */
const unsigned char *q = tok_two_chars;
while (*q) {
if (q[2] == v) {
*p++ = q[0];
*p++ = q[1];
*p = '\0';
return cstr_buf.data;
}
q += 3;
}
if (v >= 127) {
sprintf(cstr_buf.data, "<%02x>", v);
return cstr_buf.data;
}
addv:
*p++ = v;
*p = '\0';
} else if (v < tok_ident) {
return table_ident[v - TOK_IDENT]->str;
} else if (v >= SYM_FIRST_ANOM) {
/* special name for anonymous symbol */
sprintf(p, "L.%u", v - SYM_FIRST_ANOM);
} else {
/* should never happen */
return NULL;
}
break;
}
return cstr_buf.data;
}
/* return the current character, handling end of block if necessary
(but not stray) */
ST_FUNC int handle_eob(void)
{
BufferedFile *bf = file;
int len;
/* only tries to read if really end of buffer */
if (bf->buf_ptr >= bf->buf_end) {
if (bf->fd != -1) {
#if defined(PARSE_DEBUG)
len = 1;
#else
len = IO_BUF_SIZE;
#endif
len = read(bf->fd, bf->buffer, len);
if (len < 0)
len = 0;
} else {
len = 0;
}
total_bytes += len;
bf->buf_ptr = bf->buffer;
bf->buf_end = bf->buffer + len;
*bf->buf_end = CH_EOB;
}
if (bf->buf_ptr < bf->buf_end) {
return bf->buf_ptr[0];
} else {
bf->buf_ptr = bf->buf_end;
return CH_EOF;
}
}
/* read next char from current input file and handle end of input buffer */
ST_INLN void inp(void)
{
ch = *(++(file->buf_ptr));
/* end of buffer/file handling */
if (ch == CH_EOB)
ch = handle_eob();
}
/* handle '\[\r]\n' */
static int handle_stray_noerror(void)
{
while (ch == '\\') {
inp();
if (ch == '\n') {
file->line_num++;
inp();
} else if (ch == '\r') {
inp();
if (ch != '\n')
goto fail;
file->line_num++;
inp();
} else {
fail:
return 1;
}
}
return 0;
}
static void handle_stray(void)
{
if (handle_stray_noerror())
tcc_error("stray '\\' in program");
}
/* skip the stray and handle the \\n case. Output an error if
incorrect char after the stray */
static int handle_stray1(uint8_t *p)
{
int c;
file->buf_ptr = p;
if (p >= file->buf_end) {
c = handle_eob();
if (c != '\\')
return c;
p = file->buf_ptr;
}
ch = *p;
if (handle_stray_noerror()) {
if (!(parse_flags & PARSE_FLAG_ACCEPT_STRAYS))
tcc_error("stray '\\' in program");
*--file->buf_ptr = '\\';
}
p = file->buf_ptr;
c = *p;
return c;
}
/* handle just the EOB case, but not stray */
#define PEEKC_EOB(c, p)\
{\
p++;\
c = *p;\
if (c == '\\') {\
file->buf_ptr = p;\
c = handle_eob();\
p = file->buf_ptr;\
}\
}
/* handle the complicated stray case */
#define PEEKC(c, p)\
{\
p++;\
c = *p;\
if (c == '\\') {\
c = handle_stray1(p);\
p = file->buf_ptr;\
}\
}
/* input with '\[\r]\n' handling. Note that this function cannot
handle other characters after '\', so you cannot call it inside
strings or comments */
ST_FUNC void minp(void)
{
inp();
if (ch == '\\')
handle_stray();
}
/* single line C++ comments */
static uint8_t *parse_line_comment(uint8_t *p)
{
int c;
p++;
for(;;) {
c = *p;
redo:
if (c == '\n' || c == CH_EOF) {
break;
} else if (c == '\\') {
file->buf_ptr = p;
c = handle_eob();
p = file->buf_ptr;
if (c == '\\') {
PEEKC_EOB(c, p);
if (c == '\n') {
file->line_num++;
PEEKC_EOB(c, p);
} else if (c == '\r') {
PEEKC_EOB(c, p);
if (c == '\n') {
file->line_num++;
PEEKC_EOB(c, p);
}
}
} else {
goto redo;
}
} else {
p++;
}
}
return p;
}
/* C comments */
ST_FUNC uint8_t *parse_comment(uint8_t *p)
{
int c;
p++;
for(;;) {
/* fast skip loop */
for(;;) {
c = *p;
if (c == '\n' || c == '*' || c == '\\')
break;
p++;
c = *p;
if (c == '\n' || c == '*' || c == '\\')
break;
p++;
}
/* now we can handle all the cases */
if (c == '\n') {
file->line_num++;
p++;
} else if (c == '*') {
p++;
for(;;) {
c = *p;
if (c == '*') {
p++;
} else if (c == '/') {
goto end_of_comment;
} else if (c == '\\') {
file->buf_ptr = p;
c = handle_eob();
p = file->buf_ptr;
if (c == CH_EOF)
tcc_error("unexpected end of file in comment");
if (c == '\\') {
/* skip '\[\r]\n', otherwise just skip the stray */
while (c == '\\') {
PEEKC_EOB(c, p);
if (c == '\n') {
file->line_num++;
PEEKC_EOB(c, p);
} else if (c == '\r') {
PEEKC_EOB(c, p);
if (c == '\n') {
file->line_num++;
PEEKC_EOB(c, p);
}
} else {
goto after_star;
}
}
}
} else {
break;
}
}
after_star: ;
} else {
/* stray, eob or eof */
file->buf_ptr = p;
c = handle_eob();
p = file->buf_ptr;
if (c == CH_EOF) {
tcc_error("unexpected end of file in comment");
} else if (c == '\\') {
p++;
}
}
}
end_of_comment:
p++;
return p;
}
ST_FUNC void set_idnum(int c, int val)
{
isidnum_table[c - CH_EOF] = val;
}
#define cinp minp
static inline void skip_spaces(void)
{
while (isidnum_table[ch - CH_EOF] & IS_SPC)
cinp();
}
static inline int check_space(int t, int *spc)
{
if (t < 256 && (isidnum_table[t - CH_EOF] & IS_SPC)) {
if (*spc)
return 1;
*spc = 1;
} else
*spc = 0;
return 0;
}
/* parse a string without interpreting escapes */
static uint8_t *parse_pp_string(uint8_t *p,
int sep, CString *str)
{
int c;
p++;
for(;;) {
c = *p;
if (c == sep) {
break;
} else if (c == '\\') {
file->buf_ptr = p;
c = handle_eob();
p = file->buf_ptr;
if (c == CH_EOF) {
unterminated_string:
/* XXX: indicate line number of start of string */
tcc_error("missing terminating %c character", sep);
} else if (c == '\\') {
/* escape : just skip \[\r]\n */
PEEKC_EOB(c, p);
if (c == '\n') {
file->line_num++;
p++;
} else if (c == '\r') {
PEEKC_EOB(c, p);
if (c != '\n')
expect("'\n' after '\r'");
file->line_num++;
p++;
} else if (c == CH_EOF) {
goto unterminated_string;
} else {
if (str) {
cstr_ccat(str, '\\');
cstr_ccat(str, c);
}
p++;
}
}
} else if (c == '\n') {
file->line_num++;
goto add_char;
} else if (c == '\r') {
PEEKC_EOB(c, p);
if (c != '\n') {
if (str)
cstr_ccat(str, '\r');
} else {
file->line_num++;
goto add_char;
}
} else {
add_char:
if (str)
cstr_ccat(str, c);
p++;
}
}
p++;
return p;
}
/* skip block of text until #else, #elif or #endif. skip also pairs of
#if/#endif */
static void preprocess_skip(void)
{
int a, start_of_line, c, in_warn_or_error;
uint8_t *p;
p = file->buf_ptr;
a = 0;
redo_start:
start_of_line = 1;
in_warn_or_error = 0;
for(;;) {
redo_no_start:
c = *p;
switch(c) {
case ' ':
case '\t':
case '\f':
case '\v':
case '\r':
p++;
goto redo_no_start;
case '\n':
file->line_num++;
p++;
goto redo_start;
case '\\':
file->buf_ptr = p;
c = handle_eob();
if (c == CH_EOF) {
expect("#endif");
} else if (c == '\\') {
ch = file->buf_ptr[0];
handle_stray_noerror();
}
p = file->buf_ptr;
goto redo_no_start;
/* skip strings */
case '\"':
case '\'':
if (in_warn_or_error)
goto _default;
p = parse_pp_string(p, c, NULL);
break;
/* skip comments */
case '/':
if (in_warn_or_error)
goto _default;
file->buf_ptr = p;
ch = *p;
minp();
p = file->buf_ptr;
if (ch == '*') {
p = parse_comment(p);
} else if (ch == '/') {
p = parse_line_comment(p);
}
break;
case '#':
p++;
if (start_of_line) {
file->buf_ptr = p;
next_nomacro();
p = file->buf_ptr;
if (a == 0 &&
(tok == TOK_ELSE || tok == TOK_ELIF || tok == TOK_ENDIF))
goto the_end;
if (tok == TOK_IF || tok == TOK_IFDEF || tok == TOK_IFNDEF)
a++;
else if (tok == TOK_ENDIF)
a--;
else if( tok == TOK_ERROR || tok == TOK_WARNING)
in_warn_or_error = 1;
else if (tok == TOK_LINEFEED)
goto redo_start;
else if (parse_flags & PARSE_FLAG_ASM_FILE)
p = parse_line_comment(p - 1);
} else if (parse_flags & PARSE_FLAG_ASM_FILE)
p = parse_line_comment(p - 1);
break;
_default:
default:
p++;
break;
}
start_of_line = 0;
}
the_end: ;
file->buf_ptr = p;
}
/* ParseState handling */
/* XXX: currently, no include file info is stored. Thus, we cannot display
accurate messages if the function or data definition spans multiple
files */
/* save current parse state in 's' */
ST_FUNC void save_parse_state(ParseState *s)
{
s->line_num = file->line_num;
s->macro_ptr = macro_ptr;
s->tok = tok;
s->tokc = tokc;
}
/* restore parse state from 's' */
ST_FUNC void restore_parse_state(ParseState *s)
{
file->line_num = s->line_num;
macro_ptr = s->macro_ptr;
tok = s->tok;
tokc = s->tokc;
}
#if 0
/* return the number of additional 'ints' necessary to store the
token */
static inline int tok_size(const int *p)
{
switch(*p) {
/* 4 bytes */
case TOK_CINT:
case TOK_CUINT:
case TOK_CCHAR:
case TOK_LCHAR:
case TOK_CFLOAT:
case TOK_LINENUM:
return 1 + 1;
case TOK_STR:
case TOK_LSTR:
case TOK_PPNUM:
case TOK_PPSTR:
return 1 + ((sizeof(CString) + ((CString *)(p+1))->size + 3) >> 2);
case TOK_CDOUBLE:
case TOK_CLLONG:
case TOK_CULLONG:
return 1 + 2;
case TOK_CLDOUBLE:
return 1 + LDOUBLE_SIZE / 4;
default:
return 1 + 0;
}
}
#endif
/* token string handling */
ST_INLN void tok_str_new(TokenString *s)
{
s->str = NULL;
s->len = 0;
s->allocated_len = 0;
s->last_line_num = -1;
}
ST_FUNC TokenString *tok_str_alloc(void)
{
TokenString *str = tal_realloc(tokstr_alloc, 0, sizeof *str);
tok_str_new(str);
return str;
}
ST_FUNC int *tok_str_dup(TokenString *s)
{
int *str;
str = tal_realloc(tokstr_alloc, 0, s->len * sizeof(int));
memcpy(str, s->str, s->len * sizeof(int));
return str;
}
ST_FUNC void tok_str_free_str(int *str)
{
tal_free(tokstr_alloc, str);
}
ST_FUNC void tok_str_free(TokenString *str)
{
tok_str_free_str(str->str);
tal_free(tokstr_alloc, str);
}
ST_FUNC int *tok_str_realloc(TokenString *s, int new_size)
{
int *str, size;
size = s->allocated_len;
if (size < 16)
size = 16;
while (size < new_size)
size = size * 2;
if (size > s->allocated_len) {
str = tal_realloc(tokstr_alloc, s->str, size * sizeof(int));
s->allocated_len = size;
s->str = str;
}
return s->str;
}
ST_FUNC void tok_str_add(TokenString *s, int t)
{
int len, *str;
len = s->len;
str = s->str;
if (len >= s->allocated_len)
str = tok_str_realloc(s, len + 1);
str[len++] = t;
s->len = len;
}
ST_FUNC void begin_macro(TokenString *str, int alloc)
{
str->alloc = alloc;
str->prev = macro_stack;
str->prev_ptr = macro_ptr;
macro_ptr = str->str;
macro_stack = str;
}
ST_FUNC void end_macro(void)
{
TokenString *str = macro_stack;
macro_stack = str->prev;
macro_ptr = str->prev_ptr;
if (str->alloc == 2) {
str->alloc = 3; /* just mark as finished */
} else {
tok_str_free(str);
}
}
static void tok_str_add2(TokenString *s, int t, CValue *cv)
{
int len, *str;
len = s->len;
str = s->str;
/* allocate space for worst case */
if (len + TOK_MAX_SIZE >= s->allocated_len)
str = tok_str_realloc(s, len + TOK_MAX_SIZE + 1);
str[len++] = t;
switch(t) {
case TOK_CINT:
case TOK_CUINT:
case TOK_CCHAR:
case TOK_LCHAR:
case TOK_CFLOAT:
case TOK_LINENUM:
str[len++] = cv->tab[0];
break;
case TOK_PPNUM:
case TOK_PPSTR:
case TOK_STR:
case TOK_LSTR:
{
/* Insert the string into the int array. */
size_t nb_words =
1 + (cv->str.size + sizeof(int) - 1) / sizeof(int);
if (len + nb_words >= s->allocated_len)
str = tok_str_realloc(s, len + nb_words + 1);
str[len] = cv->str.size;
memcpy(&str[len + 1], cv->str.data, cv->str.size);
len += nb_words;
}
break;
case TOK_CDOUBLE:
case TOK_CLLONG:
case TOK_CULLONG:
#if LDOUBLE_SIZE == 8
case TOK_CLDOUBLE:
#endif
str[len++] = cv->tab[0];
str[len++] = cv->tab[1];
break;
#if LDOUBLE_SIZE == 12
case TOK_CLDOUBLE:
str[len++] = cv->tab[0];
str[len++] = cv->tab[1];
str[len++] = cv->tab[2];
#elif LDOUBLE_SIZE == 16
case TOK_CLDOUBLE:
str[len++] = cv->tab[0];
str[len++] = cv->tab[1];
str[len++] = cv->tab[2];
str[len++] = cv->tab[3];
#elif LDOUBLE_SIZE != 8
#error add long double size support
#endif
break;
default:
break;
}
s->len = len;
}
/* add the current parse token in token string 's' */
ST_FUNC void tok_str_add_tok(TokenString *s)
{
CValue cval;
/* save line number info */
if (file->line_num != s->last_line_num) {
s->last_line_num = file->line_num;
cval.i = s->last_line_num;
tok_str_add2(s, TOK_LINENUM, &cval);
}
tok_str_add2(s, tok, &tokc);
}
/* get a token from an integer array and increment pointer
accordingly. we code it as a macro to avoid pointer aliasing. */
static inline void TOK_GET(int *t, const int **pp, CValue *cv)
{
const int *p = *pp;
int n, *tab;
tab = cv->tab;
switch(*t = *p++) {
case TOK_CINT:
case TOK_CUINT:
case TOK_CCHAR:
case TOK_LCHAR:
case TOK_LINENUM:
tab[0] = *p++;
cv->i = (*t == TOK_CUINT) ? (unsigned)cv->i : (int)cv->i;
break;
case TOK_CFLOAT:
tab[0] = *p++;
break;
case TOK_STR:
case TOK_LSTR:
case TOK_PPNUM:
case TOK_PPSTR:
cv->str.size = *p++;
cv->str.data = p;
p += (cv->str.size + sizeof(int) - 1) / sizeof(int);
break;
case TOK_CDOUBLE:
case TOK_CLLONG:
case TOK_CULLONG:
n = 2;
goto copy;
case TOK_CLDOUBLE:
#if LDOUBLE_SIZE == 16
n = 4;
#elif LDOUBLE_SIZE == 12
n = 3;
#elif LDOUBLE_SIZE == 8
n = 2;
#else
# error add long double size support
#endif
copy:
do
*tab++ = *p++;
while (--n);
break;
default:
break;
}
*pp = p;
}
/* Calling this function is expensive, but it is not possible
to read a token string backwards. */
static int tok_last(const int *str0, const int *str1)
{
const int *str = str0;
int tok = 0;
CValue cval;
while (str < str1)
TOK_GET(&tok, &str, &cval);
return tok;
}
static int macro_is_equal(const int *a, const int *b)
{
CValue cv;
int t;
if (!a || !b)
return 1;
while (*a && *b) {
/* first time preallocate macro_equal_buf, next time only reset position to start */
cstr_reset(¯o_equal_buf);
TOK_GET(&t, &a, &cv);
cstr_cat(¯o_equal_buf, get_tok_str(t, &cv), 0);
TOK_GET(&t, &b, &cv);
if (strcmp(macro_equal_buf.data, get_tok_str(t, &cv)))
return 0;
}
return !(*a || *b);
}
/* defines handling */
ST_INLN void define_push(int v, int macro_type, int *str, Sym *first_arg)
{
Sym *s, *o;
o = define_find(v);
s = sym_push2(&define_stack, v, macro_type, 0);
s->d = str;
s->next = first_arg;
table_ident[v - TOK_IDENT]->sym_define = s;
if (o && !macro_is_equal(o->d, s->d))
tcc_warning("%s redefined", get_tok_str(v, NULL));
}
/* undefined a define symbol. Its name is just set to zero */
ST_FUNC void define_undef(Sym *s)
{
int v = s->v;
if (v >= TOK_IDENT && v < tok_ident)
table_ident[v - TOK_IDENT]->sym_define = NULL;
}
ST_INLN Sym *define_find(int v)
{
v -= TOK_IDENT;
if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
return NULL;
return table_ident[v]->sym_define;
}
/* free define stack until top reaches 'b' */
ST_FUNC void free_defines(Sym *b)
{
while (define_stack != b) {
Sym *top = define_stack;
define_stack = top->prev;
tok_str_free_str(top->d);
define_undef(top);
sym_free(top);
}
/* restore remaining (-D or predefined) symbols if they were
#undef'd in the file */
while (b) {
int v = b->v;
if (v >= TOK_IDENT && v < tok_ident) {
Sym **d = &table_ident[v - TOK_IDENT]->sym_define;
if (!*d)
*d = b;
}
b = b->prev;
}
}
/* label lookup */
ST_FUNC Sym *label_find(int v)
{
v -= TOK_IDENT;
if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
return NULL;
return table_ident[v]->sym_label;
}
ST_FUNC Sym *label_push(Sym **ptop, int v, int flags)
{
Sym *s, **ps;
s = sym_push2(ptop, v, 0, 0);
s->r = flags;
ps = &table_ident[v - TOK_IDENT]->sym_label;
if (ptop == &global_label_stack) {
/* modify the top most local identifier, so that
sym_identifier will point to 's' when popped */
while (*ps != NULL)
ps = &(*ps)->prev_tok;
}
s->prev_tok = *ps;
*ps = s;
return s;
}
/* pop labels until element last is reached. Look if any labels are
undefined. Define symbols if '&&label' was used. */
ST_FUNC void label_pop(Sym **ptop, Sym *slast)
{
Sym *s, *s1;
for(s = *ptop; s != slast; s = s1) {
s1 = s->prev;
if (s->r == LABEL_DECLARED) {
tcc_warning("label '%s' declared but not used", get_tok_str(s->v, NULL));
} else if (s->r == LABEL_FORWARD) {
tcc_error("label '%s' used but not defined",
get_tok_str(s->v, NULL));
} else {
if (s->c) {
/* define corresponding symbol. A size of
1 is put. */
put_extern_sym(s, cur_text_section, s->jnext, 1);
}
}
/* remove label */
table_ident[s->v - TOK_IDENT]->sym_label = s->prev_tok;
sym_free(s);
}
*ptop = slast;
}
/* eval an expression for #if/#elif */
static int expr_preprocess(void)
{
int c, t;
TokenString *str;
str = tok_str_alloc();
while (tok != TOK_LINEFEED && tok != TOK_EOF) {
next(); /* do macro subst */
if (tok == TOK_DEFINED) {
next_nomacro();
t = tok;
if (t == '(')
next_nomacro();
c = define_find(tok) != 0;
if (t == '(')
next_nomacro();
tok = TOK_CINT;
tokc.i = c;
} else if (tok >= TOK_IDENT) {
/* if undefined macro */
tok = TOK_CINT;
tokc.i = 0;
}
tok_str_add_tok(str);
}
tok_str_add(str, -1); /* simulate end of file */
tok_str_add(str, 0);
/* now evaluate C constant expression */
begin_macro(str, 1);
next();
c = expr_const();
end_macro();
return c != 0;
}
/* parse after #define */
ST_FUNC void parse_define(void)
{
Sym *s, *first, **ps;
int v, t, varg, is_vaargs, spc;
int saved_parse_flags = parse_flags;
v = tok;
if (v < TOK_IDENT)
tcc_error("invalid macro name '%s'", get_tok_str(tok, &tokc));
/* XXX: should check if same macro (ANSI) */
first = NULL;
t = MACRO_OBJ;
/* We have to parse the whole define as if not in asm mode, in particular
no line comment with '#' must be ignored. Also for function
macros the argument list must be parsed without '.' being an ID
character. */
parse_flags = ((parse_flags & ~PARSE_FLAG_ASM_FILE) | PARSE_FLAG_SPACES);
/* '(' must be just after macro definition for MACRO_FUNC */
next_nomacro_spc();
if (tok == '(') {
set_idnum('.', 0);
next_nomacro();
ps = &first;
if (tok != ')') for (;;) {
varg = tok;
next_nomacro();
is_vaargs = 0;
if (varg == TOK_DOTS) {
varg = TOK___VA_ARGS__;
is_vaargs = 1;
} else if (tok == TOK_DOTS && gnu_ext) {
is_vaargs = 1;
next_nomacro();
}
if (varg < TOK_IDENT)
bad_list:
tcc_error("bad macro parameter list");
s = sym_push2(&define_stack, varg | SYM_FIELD, is_vaargs, 0);
*ps = s;
ps = &s->next;
if (tok == ')')
break;
if (tok != ',' || is_vaargs)
goto bad_list;
next_nomacro();
}
next_nomacro_spc();
t = MACRO_FUNC;
}
tokstr_buf.len = 0;
spc = 2;
parse_flags |= PARSE_FLAG_ACCEPT_STRAYS | PARSE_FLAG_SPACES | PARSE_FLAG_LINEFEED;
/* The body of a macro definition should be parsed such that identifiers
are parsed like the file mode determines (i.e. with '.' being an
ID character in asm mode). But '#' should be retained instead of
regarded as line comment leader, so still don't set ASM_FILE
in parse_flags. */
set_idnum('.', (saved_parse_flags & PARSE_FLAG_ASM_FILE) ? IS_ID : 0);
while (tok != TOK_LINEFEED && tok != TOK_EOF) {
/* remove spaces around ## and after '#' */
if (TOK_TWOSHARPS == tok) {
if (2 == spc)
goto bad_twosharp;
if (1 == spc)
--tokstr_buf.len;
spc = 3;
tok = TOK_PPJOIN;
} else if ('#' == tok) {
spc = 4;
} else if (check_space(tok, &spc)) {
goto skip;
}
tok_str_add2(&tokstr_buf, tok, &tokc);
skip:
next_nomacro_spc();
}
parse_flags = saved_parse_flags;
if (spc == 1)
--tokstr_buf.len; /* remove trailing space */
tok_str_add(&tokstr_buf, 0);
if (3 == spc)
bad_twosharp:
tcc_error("'##' cannot appear at either end of macro");
define_push(v, t, tok_str_dup(&tokstr_buf), first);
}
static CachedInclude *search_cached_include(TCCState *s1, const char *filename, int add)
{
const unsigned char *s;
unsigned int h;
CachedInclude *e;
int i;
h = TOK_HASH_INIT;
s = (unsigned char *) filename;
while (*s) {
#ifdef _WIN32
h = TOK_HASH_FUNC(h, toup(*s));
#else
h = TOK_HASH_FUNC(h, *s);
#endif
s++;
}
h &= (CACHED_INCLUDES_HASH_SIZE - 1);
i = s1->cached_includes_hash[h];
for(;;) {
if (i == 0)
break;
e = s1->cached_includes[i - 1];
if (0 == PATHCMP(e->filename, filename))
return e;
i = e->hash_next;
}
if (!add)
return NULL;
e = tcc_malloc(sizeof(CachedInclude) + strlen(filename));
strcpy(e->filename, filename);
e->ifndef_macro = e->once = 0;
dynarray_add(&s1->cached_includes, &s1->nb_cached_includes, e);
/* add in hash table */
e->hash_next = s1->cached_includes_hash[h];
s1->cached_includes_hash[h] = s1->nb_cached_includes;
#ifdef INC_DEBUG
printf("adding cached '%s'\n", filename);
#endif
return e;
}
static void pragma_parse(TCCState *s1)
{
next_nomacro();
if (tok == TOK_push_macro || tok == TOK_pop_macro) {
int t = tok, v;
Sym *s;
if (next(), tok != '(')
goto pragma_err;
if (next(), tok != TOK_STR)
goto pragma_err;
v = tok_alloc(tokc.str.data, tokc.str.size - 1)->tok;
if (next(), tok != ')')
goto pragma_err;
if (t == TOK_push_macro) {
while (NULL == (s = define_find(v)))
define_push(v, 0, NULL, NULL);
s->type.ref = s; /* set push boundary */
} else {
for (s = define_stack; s; s = s->prev)
if (s->v == v && s->type.ref == s) {
s->type.ref = NULL;
break;
}
}
if (s)
table_ident[v - TOK_IDENT]->sym_define = s->d ? s : NULL;
else
tcc_warning("unbalanced #pragma pop_macro");
pp_debug_tok = t, pp_debug_symv = v;
} else if (tok == TOK_once) {
search_cached_include(s1, file->filename, 1)->once = pp_once;
} else if (s1->ppfp) {
/* tcc -E: keep pragmas below unchanged */
unget_tok(' ');
unget_tok(TOK_PRAGMA);
unget_tok('#');
unget_tok(TOK_LINEFEED);
} else if (tok == TOK_pack) {
/* This may be:
#pragma pack(1) // set
#pragma pack() // reset to default
#pragma pack(push,1) // push & set
#pragma pack(pop) // restore previous */
next();
skip('(');
if (tok == TOK_ASM_pop) {
next();
if (s1->pack_stack_ptr <= s1->pack_stack) {
stk_error:
tcc_error("out of pack stack");
}
s1->pack_stack_ptr--;
} else {
int val = 0;
if (tok != ')') {
if (tok == TOK_ASM_push) {
next();
if (s1->pack_stack_ptr >= s1->pack_stack + PACK_STACK_SIZE - 1)
goto stk_error;
s1->pack_stack_ptr++;
skip(',');
}
if (tok != TOK_CINT)
goto pragma_err;
val = tokc.i;
if (val < 1 || val > 16 || (val & (val - 1)) != 0)
goto pragma_err;
next();
}
*s1->pack_stack_ptr = val;
}
if (tok != ')')
goto pragma_err;
} else if (tok == TOK_comment) {
char *file;
next();
skip('(');
if (tok != TOK_lib)
goto pragma_warn;
next();
skip(',');
if (tok != TOK_STR)
goto pragma_err;
file = tcc_strdup((char *)tokc.str.data);
dynarray_add(&s1->pragma_libs, &s1->nb_pragma_libs, file);
next();
if (tok != ')')
goto pragma_err;
} else {
pragma_warn:
if (s1->warn_unsupported)
tcc_warning("#pragma %s is ignored", get_tok_str(tok, &tokc));
}
return;
pragma_err:
tcc_error("malformed #pragma directive");
return;
}
/* is_bof is true if first non space token at beginning of file */
ST_FUNC void preprocess(int is_bof)
{
TCCState *s1 = tcc_state;
int i, c, n, saved_parse_flags;
char buf[1024], *q;
Sym *s;
saved_parse_flags = parse_flags;
parse_flags = PARSE_FLAG_PREPROCESS
| PARSE_FLAG_TOK_NUM
| PARSE_FLAG_TOK_STR
| PARSE_FLAG_LINEFEED
| (parse_flags & PARSE_FLAG_ASM_FILE)
;
next_nomacro();
redo:
switch(tok) {
case TOK_DEFINE:
pp_debug_tok = tok;
next_nomacro();
pp_debug_symv = tok;
parse_define();
break;
case TOK_UNDEF:
pp_debug_tok = tok;
next_nomacro();
pp_debug_symv = tok;
s = define_find(tok);
/* undefine symbol by putting an invalid name */
if (s)
define_undef(s);
break;
case TOK_INCLUDE:
case TOK_INCLUDE_NEXT:
ch = file->buf_ptr[0];
/* XXX: incorrect if comments : use next_nomacro with a special mode */
skip_spaces();
if (ch == '<') {
c = '>';
goto read_name;
} else if (ch == '\"') {
c = ch;
read_name:
inp();
q = buf;
while (ch != c && ch != '\n' && ch != CH_EOF) {
if ((q - buf) < sizeof(buf) - 1)
*q++ = ch;
if (ch == '\\') {
if (handle_stray_noerror() == 0)
--q;
} else
inp();
}
*q = '\0';
minp();
#if 0
/* eat all spaces and comments after include */
/* XXX: slightly incorrect */
while (ch1 != '\n' && ch1 != CH_EOF)
inp();
#endif
} else {
int len;
/* computed #include : concatenate everything up to linefeed,
the result must be one of the two accepted forms.
Don't convert pp-tokens to tokens here. */
parse_flags = (PARSE_FLAG_PREPROCESS
| PARSE_FLAG_LINEFEED
| (parse_flags & PARSE_FLAG_ASM_FILE));
next();
buf[0] = '\0';
while (tok != TOK_LINEFEED) {
pstrcat(buf, sizeof(buf), get_tok_str(tok, &tokc));
next();
}
len = strlen(buf);
/* check syntax and remove '<>|""' */
if ((len < 2 || ((buf[0] != '"' || buf[len-1] != '"') &&
(buf[0] != '<' || buf[len-1] != '>'))))
tcc_error("'#include' expects \"FILENAME\" or <FILENAME>");
c = buf[len-1];
memmove(buf, buf + 1, len - 2);
buf[len - 2] = '\0';
}
if (s1->include_stack_ptr >= s1->include_stack + INCLUDE_STACK_SIZE)
tcc_error("#include recursion too deep");
/* store current file in stack, but increment stack later below */
*s1->include_stack_ptr = file;
i = tok == TOK_INCLUDE_NEXT ? file->include_next_index : 0;
n = 2 + s1->nb_include_paths + s1->nb_sysinclude_paths;
for (; i < n; ++i) {
char buf1[sizeof file->filename];
CachedInclude *e;
const char *path;
if (i == 0) {
/* check absolute include path */
if (!IS_ABSPATH(buf))
continue;
buf1[0] = 0;
} else if (i == 1) {
/* search in file's dir if "header.h" */
if (c != '\"')
continue;
/* https://savannah.nongnu.org/bugs/index.php?50847 */
path = file->filename2;
pstrncpy(buf1, path, tcc_basename(path) - path);
} else {
/* search in all the include paths */
int j = i - 2, k = j - s1->nb_include_paths;
path = k < 0 ? s1->include_paths[j] : s1->sysinclude_paths[k];
pstrcpy(buf1, sizeof(buf1), path);
pstrcat(buf1, sizeof(buf1), "/");
}
pstrcat(buf1, sizeof(buf1), buf);
e = search_cached_include(s1, buf1, 0);
if (e && (define_find(e->ifndef_macro) || e->once == pp_once)) {
/* no need to parse the include because the 'ifndef macro'
is defined (or had #pragma once) */
#ifdef INC_DEBUG
printf("%s: skipping cached %s\n", file->filename, buf1);
#endif
goto include_done;
}
if (tcc_open(s1, buf1) < 0)
continue;
file->include_next_index = i + 1;
#ifdef INC_DEBUG
printf("%s: including %s\n", file->prev->filename, file->filename);
#endif
/* update target deps */
dynarray_add(&s1->target_deps, &s1->nb_target_deps,
tcc_strdup(buf1));
/* push current file in stack */
++s1->include_stack_ptr;
/* add include file debug info */
if (s1->do_debug)
put_stabs(file->filename, N_BINCL, 0, 0, 0);
tok_flags |= TOK_FLAG_BOF | TOK_FLAG_BOL;
ch = file->buf_ptr[0];
goto the_end;
}
tcc_error("include file '%s' not found", buf);
include_done:
break;
case TOK_IFNDEF:
c = 1;
goto do_ifdef;
case TOK_IF:
c = expr_preprocess();
goto do_if;
case TOK_IFDEF:
c = 0;
do_ifdef:
next_nomacro();
if (tok < TOK_IDENT)
tcc_error("invalid argument for '#if%sdef'", c ? "n" : "");
if (is_bof) {
if (c) {
#ifdef INC_DEBUG
printf("#ifndef %s\n", get_tok_str(tok, NULL));
#endif
file->ifndef_macro = tok;
}
}
c = (define_find(tok) != 0) ^ c;
do_if:
if (s1->ifdef_stack_ptr >= s1->ifdef_stack + IFDEF_STACK_SIZE)
tcc_error("memory full (ifdef)");
*s1->ifdef_stack_ptr++ = c;
goto test_skip;
case TOK_ELSE:
if (s1->ifdef_stack_ptr == s1->ifdef_stack)
tcc_error("#else without matching #if");
if (s1->ifdef_stack_ptr[-1] & 2)
tcc_error("#else after #else");
c = (s1->ifdef_stack_ptr[-1] ^= 3);
goto test_else;
case TOK_ELIF:
if (s1->ifdef_stack_ptr == s1->ifdef_stack)
tcc_error("#elif without matching #if");
c = s1->ifdef_stack_ptr[-1];
if (c > 1)
tcc_error("#elif after #else");
/* last #if/#elif expression was true: we skip */
if (c == 1) {
c = 0;
} else {
c = expr_preprocess();
s1->ifdef_stack_ptr[-1] = c;
}
test_else:
if (s1->ifdef_stack_ptr == file->ifdef_stack_ptr + 1)
file->ifndef_macro = 0;
test_skip:
if (!(c & 1)) {
preprocess_skip();
is_bof = 0;
goto redo;
}
break;
case TOK_ENDIF:
if (s1->ifdef_stack_ptr <= file->ifdef_stack_ptr)
tcc_error("#endif without matching #if");
s1->ifdef_stack_ptr--;
/* '#ifndef macro' was at the start of file. Now we check if
an '#endif' is exactly at the end of file */
if (file->ifndef_macro &&
s1->ifdef_stack_ptr == file->ifdef_stack_ptr) {
file->ifndef_macro_saved = file->ifndef_macro;
/* need to set to zero to avoid false matches if another
#ifndef at middle of file */
file->ifndef_macro = 0;
while (tok != TOK_LINEFEED)
next_nomacro();
tok_flags |= TOK_FLAG_ENDIF;
goto the_end;
}
break;
case TOK_PPNUM:
n = strtoul((char*)tokc.str.data, &q, 10);
goto _line_num;
case TOK_LINE:
next();
if (tok != TOK_CINT)
_line_err:
tcc_error("wrong #line format");
n = tokc.i;
_line_num:
next();
if (tok != TOK_LINEFEED) {
if (tok == TOK_STR)
pstrcpy(file->filename, sizeof(file->filename), (char *)tokc.str.data);
else if (parse_flags & PARSE_FLAG_ASM_FILE)
break;
else
goto _line_err;
--n;
}
if (file->fd > 0)
total_lines += file->line_num - n;
file->line_num = n;
if (s1->do_debug)
put_stabs(file->filename, N_BINCL, 0, 0, 0);
break;
case TOK_ERROR:
case TOK_WARNING:
c = tok;
ch = file->buf_ptr[0];
skip_spaces();
q = buf;
while (ch != '\n' && ch != CH_EOF) {
if ((q - buf) < sizeof(buf) - 1)
*q++ = ch;
if (ch == '\\') {
if (handle_stray_noerror() == 0)
--q;
} else
inp();
}
*q = '\0';
if (c == TOK_ERROR)
tcc_error("#error %s", buf);
else
tcc_warning("#warning %s", buf);
break;
case TOK_PRAGMA:
pragma_parse(s1);
break;
case TOK_LINEFEED:
goto the_end;
default:
/* ignore gas line comment in an 'S' file. */
if (saved_parse_flags & PARSE_FLAG_ASM_FILE)
goto ignore;
if (tok == '!' && is_bof)
/* '!' is ignored at beginning to allow C scripts. */
goto ignore;
tcc_warning("Ignoring unknown preprocessing directive #%s", get_tok_str(tok, &tokc));
ignore:
file->buf_ptr = parse_line_comment(file->buf_ptr - 1);
goto the_end;
}
/* ignore other preprocess commands or #! for C scripts */
while (tok != TOK_LINEFEED)
next_nomacro();
the_end:
parse_flags = saved_parse_flags;
}
/* evaluate escape codes in a string. */
static void parse_escape_string(CString *outstr, const uint8_t *buf, int is_long)
{
int c, n;
const uint8_t *p;
p = buf;
for(;;) {
c = *p;
if (c == '\0')
break;
if (c == '\\') {
p++;
/* escape */
c = *p;
switch(c) {
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
/* at most three octal digits */
n = c - '0';
p++;
c = *p;
if (isoct(c)) {
n = n * 8 + c - '0';
p++;
c = *p;
if (isoct(c)) {
n = n * 8 + c - '0';
p++;
}
}
c = n;
goto add_char_nonext;
case 'x':
case 'u':
case 'U':
p++;
n = 0;
for(;;) {
c = *p;
if (c >= 'a' && c <= 'f')
c = c - 'a' + 10;
else if (c >= 'A' && c <= 'F')
c = c - 'A' + 10;
else if (isnum(c))
c = c - '0';
else
break;
n = n * 16 + c;
p++;
}
c = n;
goto add_char_nonext;
case 'a':
c = '\a';
break;
case 'b':
c = '\b';
break;
case 'f':
c = '\f';
break;
case 'n':
c = '\n';
break;
case 'r':
c = '\r';
break;
case 't':
c = '\t';
break;
case 'v':
c = '\v';
break;
case 'e':
if (!gnu_ext)
goto invalid_escape;
c = 27;
break;
case '\'':
case '\"':
case '\\':
case '?':
break;
default:
invalid_escape:
if (c >= '!' && c <= '~')
tcc_warning("unknown escape sequence: \'\\%c\'", c);
else
tcc_warning("unknown escape sequence: \'\\x%x\'", c);
break;
}
}
p++;
add_char_nonext:
if (!is_long)
cstr_ccat(outstr, c);
else
cstr_wccat(outstr, c);
}
/* add a trailing '\0' */
if (!is_long)
cstr_ccat(outstr, '\0');
else
cstr_wccat(outstr, '\0');
}
static void parse_string(const char *s, int len)
{
uint8_t buf[1000], *p = buf;
int is_long, sep;
if ((is_long = *s == 'L'))
++s, --len;
sep = *s++;
len -= 2;
if (len >= sizeof buf)
p = tcc_malloc(len + 1);
memcpy(p, s, len);
p[len] = 0;
cstr_reset(&tokcstr);
parse_escape_string(&tokcstr, p, is_long);
if (p != buf)
tcc_free(p);
if (sep == '\'') {
int char_size;
/* XXX: make it portable */
if (!is_long)
char_size = 1;
else
char_size = sizeof(nwchar_t);
if (tokcstr.size <= char_size)
tcc_error("empty character constant");
if (tokcstr.size > 2 * char_size)
tcc_warning("multi-character character constant");
if (!is_long) {
tokc.i = *(int8_t *)tokcstr.data;
tok = TOK_CCHAR;
} else {
tokc.i = *(nwchar_t *)tokcstr.data;
tok = TOK_LCHAR;
}
} else {
tokc.str.size = tokcstr.size;
tokc.str.data = tokcstr.data;
if (!is_long)
tok = TOK_STR;
else
tok = TOK_LSTR;
}
}
/* we use 64 bit numbers */
#define BN_SIZE 2
/* bn = (bn << shift) | or_val */
static void bn_lshift(unsigned int *bn, int shift, int or_val)
{
int i;
unsigned int v;
for(i=0;i<BN_SIZE;i++) {
v = bn[i];
bn[i] = (v << shift) | or_val;
or_val = v >> (32 - shift);
}
}
static void bn_zero(unsigned int *bn)
{
int i;
for(i=0;i<BN_SIZE;i++) {
bn[i] = 0;
}
}
/* parse number in null terminated string 'p' and return it in the
current token */
static void parse_number(const char *p)
{
int b, t, shift, frac_bits, s, exp_val, ch;
char *q;
unsigned int bn[BN_SIZE];
double d;
/* number */
q = token_buf;
ch = *p++;
t = ch;
ch = *p++;
*q++ = t;
b = 10;
if (t == '.') {
goto float_frac_parse;
} else if (t == '0') {
if (ch == 'x' || ch == 'X') {
q--;
ch = *p++;
b = 16;
} else if (tcc_ext && (ch == 'b' || ch == 'B')) {
q--;
ch = *p++;
b = 2;
}
}
/* parse all digits. cannot check octal numbers at this stage
because of floating point constants */
while (1) {
if (ch >= 'a' && ch <= 'f')
t = ch - 'a' + 10;
else if (ch >= 'A' && ch <= 'F')
t = ch - 'A' + 10;
else if (isnum(ch))
t = ch - '0';
else
break;
if (t >= b)
break;
if (q >= token_buf + STRING_MAX_SIZE) {
num_too_long:
tcc_error("number too long");
}
*q++ = ch;
ch = *p++;
}
if (ch == '.' ||
((ch == 'e' || ch == 'E') && b == 10) ||
((ch == 'p' || ch == 'P') && (b == 16 || b == 2))) {
if (b != 10) {
/* NOTE: strtox should support that for hexa numbers, but
non ISOC99 libcs do not support it, so we prefer to do
it by hand */
/* hexadecimal or binary floats */
/* XXX: handle overflows */
*q = '\0';
if (b == 16)
shift = 4;
else
shift = 1;
bn_zero(bn);
q = token_buf;
while (1) {
t = *q++;
if (t == '\0') {
break;
} else if (t >= 'a') {
t = t - 'a' + 10;
} else if (t >= 'A') {
t = t - 'A' + 10;
} else {
t = t - '0';
}
bn_lshift(bn, shift, t);
}
frac_bits = 0;
if (ch == '.') {
ch = *p++;
while (1) {
t = ch;
if (t >= 'a' && t <= 'f') {
t = t - 'a' + 10;
} else if (t >= 'A' && t <= 'F') {
t = t - 'A' + 10;
} else if (t >= '0' && t <= '9') {
t = t - '0';
} else {
break;
}
if (t >= b)
tcc_error("invalid digit");
bn_lshift(bn, shift, t);
frac_bits += shift;
ch = *p++;
}
}
if (ch != 'p' && ch != 'P')
expect("exponent");
ch = *p++;
s = 1;
exp_val = 0;
if (ch == '+') {
ch = *p++;
} else if (ch == '-') {
s = -1;
ch = *p++;
}
if (ch < '0' || ch > '9')
expect("exponent digits");
while (ch >= '0' && ch <= '9') {
exp_val = exp_val * 10 + ch - '0';
ch = *p++;
}
exp_val = exp_val * s;
/* now we can generate the number */
/* XXX: should patch directly float number */
#if HAVE_FLOAT
d = (double)bn[1] * 4294967296.0 + (double)bn[0];
#endif
d = ldexp(d, exp_val - frac_bits);
t = toup(ch);
if (t == 'F') {
ch = *p++;
tok = TOK_CFLOAT;
/* float : should handle overflow */
tokc.f = (float)d;
} else if (t == 'L') {
ch = *p++;
#ifdef TCC_TARGET_PE
tok = TOK_CDOUBLE;
tokc.d = d;
#else
tok = TOK_CLDOUBLE;
/* XXX: not large enough */
tokc.ld = (long double)d;
#endif
} else {
tok = TOK_CDOUBLE;
tokc.d = d;
}
} else {
/* decimal floats */
if (ch == '.') {
if (q >= token_buf + STRING_MAX_SIZE)
goto num_too_long;
*q++ = ch;
ch = *p++;
float_frac_parse:
while (ch >= '0' && ch <= '9') {
if (q >= token_buf + STRING_MAX_SIZE)
goto num_too_long;
*q++ = ch;
ch = *p++;
}
}
if (ch == 'e' || ch == 'E') {
if (q >= token_buf + STRING_MAX_SIZE)
goto num_too_long;
*q++ = ch;
ch = *p++;
if (ch == '-' || ch == '+') {
if (q >= token_buf + STRING_MAX_SIZE)
goto num_too_long;
*q++ = ch;
ch = *p++;
}
if (ch < '0' || ch > '9')
expect("exponent digits");
while (ch >= '0' && ch <= '9') {
if (q >= token_buf + STRING_MAX_SIZE)
goto num_too_long;
*q++ = ch;
ch = *p++;
}
}
*q = '\0';
t = toup(ch);
errno = 0;
if (t == 'F') {
ch = *p++;
tok = TOK_CFLOAT;
tokc.f = strtof(token_buf, NULL);
} else if (t == 'L') {
ch = *p++;
#ifdef TCC_TARGET_PE
tok = TOK_CDOUBLE;
tokc.d = strtod(token_buf, NULL);
#else
tok = TOK_CLDOUBLE;
#if HAVE_FLOAT
tokc.ld = strtold(token_buf, NULL);
#endif
#endif
} else {
tok = TOK_CDOUBLE;
#if HAVE_FLOAT
tokc.d = strtod(token_buf, NULL);
#endif
}
}
} else {
unsigned long long n, n1;
int lcount, ucount, must_64bit;
const char *p1;
/* integer number */
*q = '\0';
q = token_buf;
if (b == 10 && *q == '0') {
b = 8;
q++;
}
n = 0;
while(1) {
t = *q++;
/* no need for checks except for base 10 / 8 errors */
if (t == '\0')
break;
else if (t >= 'a')
t = t - 'a' + 10;
else if (t >= 'A')
t = t - 'A' + 10;
else
t = t - '0';
if (t >= b)
tcc_error("invalid digit");
n1 = n;
n = n * b + t;
#if HAVE_LONG_LONG
/* detect overflow */
/* XXX: this test is not reliable */
if (n < n1)
tcc_error("integer constant overflow");
#endif
}
/* Determine the characteristics (unsigned and/or 64bit) the type of
the constant must have according to the constant suffix(es) */
lcount = ucount = must_64bit = 0;
p1 = p;
for(;;) {
t = toup(ch);
if (t == 'L') {
if (lcount >= 2)
tcc_error("three 'l's in integer constant");
if (lcount && *(p - 1) != ch)
tcc_error("incorrect integer suffix: %s", p1);
lcount++;
#if !defined TCC_TARGET_X86_64 || defined TCC_TARGET_PE
if (lcount == 2)
#endif
must_64bit = 1;
ch = *p++;
} else if (t == 'U') {
if (ucount >= 1)
tcc_error("two 'u's in integer constant");
ucount++;
ch = *p++;
} else {
break;
}
}
#if HAVE_LONG_LONG
/* Whether 64 bits are needed to hold the constant's value */
if (n & 0xffffffff00000000LL || must_64bit) {
tok = TOK_CLLONG;
n1 = n >> 32;
} else
#endif
{
tok = TOK_CINT;
n1 = n;
}
/* Whether type must be unsigned to hold the constant's value */
if (ucount || ((n1 >> 31) && (b != 10))) {
if (tok == TOK_CLLONG)
tok = TOK_CULLONG;
else
tok = TOK_CUINT;
/* If decimal and no unsigned suffix, bump to 64 bits or throw error */
} else if (n1 >> 31) {
if (tok == TOK_CINT)
tok = TOK_CLLONG;
else
tcc_error("integer constant overflow");
}
tokc.i = n;
}
if (ch)
tcc_error("invalid number\n");
}
#define PARSE2(c1, tok1, c2, tok2) \
case c1: \
PEEKC(c, p); \
if (c == c2) { \
p++; \
tok = tok2; \
} else { \
tok = tok1; \
} \
break;
/* return next token without macro substitution */
static inline void next_nomacro1(void)
{
int t, c, is_long, len;
TokenSym *ts;
uint8_t *p, *p1;
unsigned int h;
p = file->buf_ptr;
redo_no_start:
c = *p;
switch(c) {
case ' ':
case '\t':
tok = c;
p++;
if (parse_flags & PARSE_FLAG_SPACES)
goto keep_tok_flags;
while (isidnum_table[*p - CH_EOF] & IS_SPC)
++p;
goto redo_no_start;
case '\f':
case '\v':
case '\r':
p++;
goto redo_no_start;
case '\\':
/* first look if it is in fact an end of buffer */
c = handle_stray1(p);
p = file->buf_ptr;
if (c == '\\')
goto parse_simple;
if (c != CH_EOF)
goto redo_no_start;
{
TCCState *s1 = tcc_state;
if ((parse_flags & PARSE_FLAG_LINEFEED)
&& !(tok_flags & TOK_FLAG_EOF)) {
tok_flags |= TOK_FLAG_EOF;
tok = TOK_LINEFEED;
goto keep_tok_flags;
} else if (!(parse_flags & PARSE_FLAG_PREPROCESS)) {
tok = TOK_EOF;
} else if (s1->ifdef_stack_ptr != file->ifdef_stack_ptr) {
tcc_error("missing #endif");
} else if (s1->include_stack_ptr == s1->include_stack) {
/* no include left : end of file. */
tok = TOK_EOF;
} else {
tok_flags &= ~TOK_FLAG_EOF;
/* pop include file */
/* test if previous '#endif' was after a #ifdef at
start of file */
if (tok_flags & TOK_FLAG_ENDIF) {
#ifdef INC_DEBUG
printf("#endif %s\n", get_tok_str(file->ifndef_macro_saved, NULL));
#endif
search_cached_include(s1, file->filename, 1)
->ifndef_macro = file->ifndef_macro_saved;
tok_flags &= ~TOK_FLAG_ENDIF;
}
/* add end of include file debug info */
if (tcc_state->do_debug) {
put_stabd(N_EINCL, 0, 0);
}
/* pop include stack */
tcc_close();
s1->include_stack_ptr--;
p = file->buf_ptr;
goto redo_no_start;
}
}
break;
case '\n':
file->line_num++;
tok_flags |= TOK_FLAG_BOL;
p++;
maybe_newline:
if (0 == (parse_flags & PARSE_FLAG_LINEFEED))
goto redo_no_start;
tok = TOK_LINEFEED;
goto keep_tok_flags;
case '#':
/* XXX: simplify */
PEEKC(c, p);
if ((tok_flags & TOK_FLAG_BOL) &&
(parse_flags & PARSE_FLAG_PREPROCESS)) {
file->buf_ptr = p;
preprocess(tok_flags & TOK_FLAG_BOF);
p = file->buf_ptr;
goto maybe_newline;
} else {
if (c == '#') {
p++;
tok = TOK_TWOSHARPS;
} else {
if (parse_flags & PARSE_FLAG_ASM_FILE) {
p = parse_line_comment(p - 1);
goto redo_no_start;
} else {
tok = '#';
}
}
}
break;
/* dollar is allowed to start identifiers when not parsing asm */
case '$':
if (!(isidnum_table[c - CH_EOF] & IS_ID)
|| (parse_flags & PARSE_FLAG_ASM_FILE))
goto parse_simple;
case 'a': case 'b': case 'c': case 'd':
case 'e': case 'f': case 'g': case 'h':
case 'i': case 'j': case 'k': case 'l':
case 'm': case 'n': case 'o': case 'p':
case 'q': case 'r': case 's': case 't':
case 'u': case 'v': case 'w': case 'x':
case 'y': case 'z':
case 'A': case 'B': case 'C': case 'D':
case 'E': case 'F': case 'G': case 'H':
case 'I': case 'J': case 'K':
case 'M': case 'N': case 'O': case 'P':
case 'Q': case 'R': case 'S': case 'T':
case 'U': case 'V': case 'W': case 'X':
case 'Y': case 'Z':
case '_':
parse_ident_fast:
p1 = p;
h = TOK_HASH_INIT;
h = TOK_HASH_FUNC(h, c);
while (c = *++p, isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM))
h = TOK_HASH_FUNC(h, c);
len = p - p1;
if (c != '\\') {
TokenSym **pts;
/* fast case : no stray found, so we have the full token
and we have already hashed it */
h &= (TOK_HASH_SIZE - 1);
pts = &hash_ident[h];
for(;;) {
ts = *pts;
if (!ts)
break;
if (ts->len == len && !memcmp(ts->str, p1, len))
goto token_found;
pts = &(ts->hash_next);
}
ts = tok_alloc_new(pts, (char *) p1, len);
token_found: ;
} else {
/* slower case */
cstr_reset(&tokcstr);
cstr_cat(&tokcstr, (char *) p1, len);
p--;
PEEKC(c, p);
parse_ident_slow:
while (isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM))
{
cstr_ccat(&tokcstr, c);
PEEKC(c, p);
}
ts = tok_alloc(tokcstr.data, tokcstr.size);
}
tok = ts->tok;
break;
case 'L':
t = p[1];
if (t != '\\' && t != '\'' && t != '\"') {
/* fast case */
goto parse_ident_fast;
} else {
PEEKC(c, p);
if (c == '\'' || c == '\"') {
is_long = 1;
goto str_const;
} else {
cstr_reset(&tokcstr);
cstr_ccat(&tokcstr, 'L');
goto parse_ident_slow;
}
}
break;
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
case '8': case '9':
t = c;
PEEKC(c, p);
/* after the first digit, accept digits, alpha, '.' or sign if
prefixed by 'eEpP' */
parse_num:
cstr_reset(&tokcstr);
for(;;) {
cstr_ccat(&tokcstr, t);
if (!((isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM))
|| c == '.'
|| ((c == '+' || c == '-')
&& (((t == 'e' || t == 'E')
&& !(parse_flags & PARSE_FLAG_ASM_FILE
/* 0xe+1 is 3 tokens in asm */
&& ((char*)tokcstr.data)[0] == '0'
&& toup(((char*)tokcstr.data)[1]) == 'X'))
|| t == 'p' || t == 'P'))))
break;
t = c;
PEEKC(c, p);
}
/* We add a trailing '\0' to ease parsing */
cstr_ccat(&tokcstr, '\0');
tokc.str.size = tokcstr.size;
tokc.str.data = tokcstr.data;
tok = TOK_PPNUM;
break;
case '.':
/* special dot handling because it can also start a number */
PEEKC(c, p);
if (isnum(c)) {
t = '.';
goto parse_num;
} else if ((isidnum_table['.' - CH_EOF] & IS_ID)
&& (isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM))) {
*--p = c = '.';
goto parse_ident_fast;
} else if (c == '.') {
PEEKC(c, p);
if (c == '.') {
p++;
tok = TOK_DOTS;
} else {
*--p = '.'; /* may underflow into file->unget[] */
tok = '.';
}
} else {
tok = '.';
}
break;
case '\'':
case '\"':
is_long = 0;
str_const:
cstr_reset(&tokcstr);
if (is_long)
cstr_ccat(&tokcstr, 'L');
cstr_ccat(&tokcstr, c);
p = parse_pp_string(p, c, &tokcstr);
cstr_ccat(&tokcstr, c);
cstr_ccat(&tokcstr, '\0');
tokc.str.size = tokcstr.size;
tokc.str.data = tokcstr.data;
tok = TOK_PPSTR;
break;
case '<':
PEEKC(c, p);
if (c == '=') {
p++;
tok = TOK_LE;
} else if (c == '<') {
PEEKC(c, p);
if (c == '=') {
p++;
tok = TOK_A_SHL;
} else {
tok = TOK_SHL;
}
} else {
tok = TOK_LT;
}
break;
case '>':
PEEKC(c, p);
if (c == '=') {
p++;
tok = TOK_GE;
} else if (c == '>') {
PEEKC(c, p);
if (c == '=') {
p++;
tok = TOK_A_SAR;
} else {
tok = TOK_SAR;
}
} else {
tok = TOK_GT;
}
break;
case '&':
PEEKC(c, p);
if (c == '&') {
p++;
tok = TOK_LAND;
} else if (c == '=') {
p++;
tok = TOK_A_AND;
} else {
tok = '&';
}
break;
case '|':
PEEKC(c, p);
if (c == '|') {
p++;
tok = TOK_LOR;
} else if (c == '=') {
p++;
tok = TOK_A_OR;
} else {
tok = '|';
}
break;
case '+':
PEEKC(c, p);
if (c == '+') {
p++;
tok = TOK_INC;
} else if (c == '=') {
p++;
tok = TOK_A_ADD;
} else {
tok = '+';
}
break;
case '-':
PEEKC(c, p);
if (c == '-') {
p++;
tok = TOK_DEC;
} else if (c == '=') {
p++;
tok = TOK_A_SUB;
} else if (c == '>') {
p++;
tok = TOK_ARROW;
} else {
tok = '-';
}
break;
PARSE2('!', '!', '=', TOK_NE)
PARSE2('=', '=', '=', TOK_EQ)
PARSE2('*', '*', '=', TOK_A_MUL)
PARSE2('%', '%', '=', TOK_A_MOD)
PARSE2('^', '^', '=', TOK_A_XOR)
/* comments or operator */
case '/':
PEEKC(c, p);
if (c == '*') {
p = parse_comment(p);
/* comments replaced by a blank */
tok = ' ';
goto keep_tok_flags;
} else if (c == '/') {
p = parse_line_comment(p);
tok = ' ';
goto keep_tok_flags;
} else if (c == '=') {
p++;
tok = TOK_A_DIV;
} else {
tok = '/';
}
break;
/* simple tokens */
case '(':
case ')':
case '[':
case ']':
case '{':
case '}':
case ',':
case ';':
case ':':
case '?':
case '~':
case '@': /* only used in assembler */
parse_simple:
tok = c;
p++;
break;
default:
if (c >= 0x80 && c <= 0xFF) /* utf8 identifiers */
goto parse_ident_fast;
if (parse_flags & PARSE_FLAG_ASM_FILE)
goto parse_simple;
tcc_error("unrecognized character \\x%02x", c);
break;
}
tok_flags = 0;
keep_tok_flags:
file->buf_ptr = p;
#if defined(PARSE_DEBUG)
printf("token = %s\n", get_tok_str(tok, &tokc));
#endif
}
/* return next token without macro substitution. Can read input from
macro_ptr buffer */
static void next_nomacro_spc(void)
{
if (macro_ptr) {
redo:
tok = *macro_ptr;
if (tok) {
TOK_GET(&tok, ¯o_ptr, &tokc);
if (tok == TOK_LINENUM) {
file->line_num = tokc.i;
goto redo;
}
}
} else {
next_nomacro1();
}
}
ST_FUNC void next_nomacro(void)
{
do {
next_nomacro_spc();
} while (tok < 256 && (isidnum_table[tok - CH_EOF] & IS_SPC));
}
static void macro_subst(
TokenString *tok_str,
Sym **nested_list,
const int *macro_str,
int can_read_stream
);
/* substitute arguments in replacement lists in macro_str by the values in
args (field d) and return allocated string */
static int *macro_arg_subst(Sym **nested_list, const int *macro_str, Sym *args)
{
int t, t0, t1, spc;
const int *st;
Sym *s;
CValue cval;
TokenString str;
CString cstr;
tok_str_new(&str);
t0 = t1 = 0;
while(1) {
TOK_GET(&t, ¯o_str, &cval);
if (!t)
break;
if (t == '#') {
/* stringize */
TOK_GET(&t, ¯o_str, &cval);
if (!t)
goto bad_stringy;
s = sym_find2(args, t);
if (s) {
cstr_new(&cstr);
cstr_ccat(&cstr, '\"');
st = s->d;
spc = 0;
while (*st) {
TOK_GET(&t, &st, &cval);
if (t != TOK_PLCHLDR
&& t != TOK_NOSUBST
&& 0 == check_space(t, &spc)) {
const char *s = get_tok_str(t, &cval);
while (*s) {
if (t == TOK_PPSTR && *s != '\'')
add_char(&cstr, *s);
else
cstr_ccat(&cstr, *s);
++s;
}
}
}
cstr.size -= spc;
cstr_ccat(&cstr, '\"');
cstr_ccat(&cstr, '\0');
#ifdef PP_DEBUG
printf("\nstringize: <%s>\n", (char *)cstr.data);
#endif
/* add string */
cval.str.size = cstr.size;
cval.str.data = cstr.data;
tok_str_add2(&str, TOK_PPSTR, &cval);
cstr_free(&cstr);
} else {
bad_stringy:
expect("macro parameter after '#'");
}
} else if (t >= TOK_IDENT) {
s = sym_find2(args, t);
if (s) {
int l0 = str.len;
st = s->d;
/* if '##' is present before or after, no arg substitution */
if (*macro_str == TOK_PPJOIN || t1 == TOK_PPJOIN) {
/* special case for var arg macros : ## eats the ','
if empty VA_ARGS variable. */
if (t1 == TOK_PPJOIN && t0 == ',' && gnu_ext && s->type.t) {
if (*st == 0) {
/* suppress ',' '##' */
str.len -= 2;
} else {
/* suppress '##' and add variable */
str.len--;
goto add_var;
}
} else {
for(;;) {
int t1;
TOK_GET(&t1, &st, &cval);
if (!t1)
break;
tok_str_add2(&str, t1, &cval);
}
}
} else {
add_var:
/* NOTE: the stream cannot be read when macro
substituting an argument */
macro_subst(&str, nested_list, st, 0);
}
if (str.len == l0) /* expanded to empty string */
tok_str_add(&str, TOK_PLCHLDR);
} else {
tok_str_add(&str, t);
}
} else {
tok_str_add2(&str, t, &cval);
}
t0 = t1, t1 = t;
}
tok_str_add(&str, 0);
return str.str;
}
static char const ab_month_name[12][4] =
{
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
/* peek or read [ws_str == NULL] next token from function macro call,
walking up macro levels up to the file if necessary */
static int next_argstream(Sym **nested_list, int can_read_stream, TokenString *ws_str)
{
int t;
const int *p;
Sym *sa;
for (;;) {
if (macro_ptr) {
p = macro_ptr, t = *p;
if (ws_str) {
while (is_space(t) || TOK_LINEFEED == t || TOK_PLCHLDR == t)
tok_str_add(ws_str, t), t = *++p;
}
if (t == 0 && can_read_stream) {
end_macro();
/* also, end of scope for nested defined symbol */
sa = *nested_list;
while (sa && sa->v == 0)
sa = sa->prev;
if (sa)
sa->v = 0;
continue;
}
} else {
ch = handle_eob();
if (ws_str) {
while (is_space(ch) || ch == '\n' || ch == '/') {
if (ch == '/') {
int c;
uint8_t *p = file->buf_ptr;
PEEKC(c, p);
if (c == '*') {
p = parse_comment(p);
file->buf_ptr = p - 1;
} else if (c == '/') {
p = parse_line_comment(p);
file->buf_ptr = p - 1;
} else
break;
ch = ' ';
}
if (!(ch == '\f' || ch == '\v' || ch == '\r'))
tok_str_add(ws_str, ch);
cinp();
}
}
t = ch;
}
if (ws_str)
return t;
next_nomacro_spc();
return tok;
}
}
/* do macro substitution of current token with macro 's' and add
result to (tok_str,tok_len). 'nested_list' is the list of all
macros we got inside to avoid recursing. Return non zero if no
substitution needs to be done */
static int macro_subst_tok(
TokenString *tok_str,
Sym **nested_list,
Sym *s,
int can_read_stream)
{
Sym *args, *sa, *sa1;
int parlevel, *mstr, t, t1, spc;
TokenString str;
char *cstrval;
CValue cval;
CString cstr;
char buf[32];
/* if symbol is a macro, prepare substitution */
/* special macros */
if (tok == TOK___LINE__) {
snprintf(buf, sizeof(buf), "%d", file->line_num);
cstrval = buf;
t1 = TOK_PPNUM;
goto add_cstr1;
} else if (tok == TOK___FILE__) {
cstrval = file->filename;
goto add_cstr;
} else if (tok == TOK___DATE__ || tok == TOK___TIME__) {
time_t ti;
struct tm *tm;
time(&ti);
tm = localtime(&ti);
if (tok == TOK___DATE__) {
snprintf(buf, sizeof(buf), "%s %2d %d",
ab_month_name[tm->tm_mon], tm->tm_mday, tm->tm_year + 1900);
} else {
snprintf(buf, sizeof(buf), "%02d:%02d:%02d",
tm->tm_hour, tm->tm_min, tm->tm_sec);
}
cstrval = buf;
add_cstr:
t1 = TOK_STR;
add_cstr1:
cstr_new(&cstr);
cstr_cat(&cstr, cstrval, 0);
cval.str.size = cstr.size;
cval.str.data = cstr.data;
tok_str_add2(tok_str, t1, &cval);
cstr_free(&cstr);
} else {
int saved_parse_flags = parse_flags;
mstr = s->d;
if (s->type.t == MACRO_FUNC) {
/* whitespace between macro name and argument list */
TokenString ws_str;
tok_str_new(&ws_str);
spc = 0;
parse_flags |= PARSE_FLAG_SPACES | PARSE_FLAG_LINEFEED
| PARSE_FLAG_ACCEPT_STRAYS;
/* get next token from argument stream */
t = next_argstream(nested_list, can_read_stream, &ws_str);
if (t != '(') {
/* not a macro substitution after all, restore the
* macro token plus all whitespace we've read.
* whitespace is intentionally not merged to preserve
* newlines. */
parse_flags = saved_parse_flags;
tok_str_add(tok_str, tok);
if (parse_flags & PARSE_FLAG_SPACES) {
int i;
for (i = 0; i < ws_str.len; i++)
tok_str_add(tok_str, ws_str.str[i]);
}
tok_str_free_str(ws_str.str);
return 0;
} else {
tok_str_free_str(ws_str.str);
}
do {
next_nomacro(); /* eat '(' */
} while (tok == TOK_PLCHLDR);
/* argument macro */
args = NULL;
sa = s->next;
/* NOTE: empty args are allowed, except if no args */
for(;;) {
do {
next_argstream(nested_list, can_read_stream, NULL);
} while (is_space(tok) || TOK_LINEFEED == tok);
empty_arg:
/* handle '()' case */
if (!args && !sa && tok == ')')
break;
if (!sa)
tcc_error("macro '%s' used with too many args",
get_tok_str(s->v, 0));
tok_str_new(&str);
parlevel = spc = 0;
/* NOTE: non zero sa->t indicates VA_ARGS */
while ((parlevel > 0 ||
(tok != ')' &&
(tok != ',' || sa->type.t)))) {
if (tok == TOK_EOF || tok == 0)
break;
if (tok == '(')
parlevel++;
else if (tok == ')')
parlevel--;
if (tok == TOK_LINEFEED)
tok = ' ';
if (!check_space(tok, &spc))
tok_str_add2(&str, tok, &tokc);
next_argstream(nested_list, can_read_stream, NULL);
}
if (parlevel)
expect(")");
str.len -= spc;
tok_str_add(&str, 0);
sa1 = sym_push2(&args, sa->v & ~SYM_FIELD, sa->type.t, 0);
sa1->d = str.str;
sa = sa->next;
if (tok == ')') {
/* special case for gcc var args: add an empty
var arg argument if it is omitted */
if (sa && sa->type.t && gnu_ext)
goto empty_arg;
break;
}
if (tok != ',')
expect(",");
}
if (sa) {
tcc_error("macro '%s' used with too few args",
get_tok_str(s->v, 0));
}
parse_flags = saved_parse_flags;
/* now subst each arg */
mstr = macro_arg_subst(nested_list, mstr, args);
/* free memory */
sa = args;
while (sa) {
sa1 = sa->prev;
tok_str_free_str(sa->d);
sym_free(sa);
sa = sa1;
}
}
sym_push2(nested_list, s->v, 0, 0);
parse_flags = saved_parse_flags;
macro_subst(tok_str, nested_list, mstr, can_read_stream | 2);
/* pop nested defined symbol */
sa1 = *nested_list;
*nested_list = sa1->prev;
sym_free(sa1);
if (mstr != s->d)
tok_str_free_str(mstr);
}
return 0;
}
static int paste_tokens(int t1, CValue *v1, int t2, CValue *v2)
{
CString cstr;
int n, ret = 1;
cstr_new(&cstr);
if (t1 != TOK_PLCHLDR)
cstr_cat(&cstr, get_tok_str(t1, v1), -1);
n = cstr.size;
if (t2 != TOK_PLCHLDR)
cstr_cat(&cstr, get_tok_str(t2, v2), -1);
cstr_ccat(&cstr, '\0');
tcc_open_bf(tcc_state, ":paste:", cstr.size);
memcpy(file->buffer, cstr.data, cstr.size);
for (;;) {
next_nomacro1();
if (0 == *file->buf_ptr)
break;
if (is_space(tok))
continue;
tcc_warning("pasting \"%.*s\" and \"%s\" does not give a valid"
" preprocessing token", n, cstr.data, (char*)cstr.data + n);
ret = 0;
break;
}
tcc_close();
//printf("paste <%s>\n", (char*)cstr.data);
cstr_free(&cstr);
return ret;
}
/* handle the '##' operator. Return NULL if no '##' seen. Otherwise
return the resulting string (which must be freed). */
static inline int *macro_twosharps(const int *ptr0)
{
int t;
CValue cval;
TokenString macro_str1;
int start_of_nosubsts = -1;
const int *ptr;
/* we search the first '##' */
for (ptr = ptr0;;) {
TOK_GET(&t, &ptr, &cval);
if (t == TOK_PPJOIN)
break;
if (t == 0)
return NULL;
}
tok_str_new(¯o_str1);
//tok_print(" $$$", ptr0);
for (ptr = ptr0;;) {
TOK_GET(&t, &ptr, &cval);
if (t == 0)
break;
if (t == TOK_PPJOIN)
continue;
while (*ptr == TOK_PPJOIN) {
int t1; CValue cv1;
/* given 'a##b', remove nosubsts preceding 'a' */
if (start_of_nosubsts >= 0)
macro_str1.len = start_of_nosubsts;
/* given 'a##b', remove nosubsts preceding 'b' */
while ((t1 = *++ptr) == TOK_NOSUBST)
;
if (t1 && t1 != TOK_PPJOIN) {
TOK_GET(&t1, &ptr, &cv1);
if (t != TOK_PLCHLDR || t1 != TOK_PLCHLDR) {
if (paste_tokens(t, &cval, t1, &cv1)) {
t = tok, cval = tokc;
} else {
tok_str_add2(¯o_str1, t, &cval);
t = t1, cval = cv1;
}
}
}
}
if (t == TOK_NOSUBST) {
if (start_of_nosubsts < 0)
start_of_nosubsts = macro_str1.len;
} else {
start_of_nosubsts = -1;
}
tok_str_add2(¯o_str1, t, &cval);
}
tok_str_add(¯o_str1, 0);
//tok_print(" ###", macro_str1.str);
return macro_str1.str;
}
/* do macro substitution of macro_str and add result to
(tok_str,tok_len). 'nested_list' is the list of all macros we got
inside to avoid recursing. */
static void macro_subst(
TokenString *tok_str,
Sym **nested_list,
const int *macro_str,
int can_read_stream
)
{
Sym *s;
const int *ptr;
int t, spc, nosubst;
CValue cval;
int *macro_str1 = NULL;
/* first scan for '##' operator handling */
ptr = macro_str;
spc = nosubst = 0;
/* first scan for '##' operator handling */
if (can_read_stream) {
macro_str1 = macro_twosharps(ptr);
if (macro_str1)
ptr = macro_str1;
}
while (1) {
TOK_GET(&t, &ptr, &cval);
if (t == 0)
break;
if (t >= TOK_IDENT && 0 == nosubst) {
s = define_find(t);
if (s == NULL)
goto no_subst;
/* if nested substitution, do nothing */
if (sym_find2(*nested_list, t)) {
/* and mark it as TOK_NOSUBST, so it doesn't get subst'd again */
tok_str_add2(tok_str, TOK_NOSUBST, NULL);
goto no_subst;
}
{
TokenString str;
str.str = (int*)ptr;
begin_macro(&str, 2);
tok = t;
macro_subst_tok(tok_str, nested_list, s, can_read_stream);
if (str.alloc == 3) {
/* already finished by reading function macro arguments */
break;
}
ptr = macro_ptr;
end_macro ();
}
spc = (tok_str->len &&
is_space(tok_last(tok_str->str,
tok_str->str + tok_str->len)));
} else {
if (t == '\\' && !(parse_flags & PARSE_FLAG_ACCEPT_STRAYS))
tcc_error("stray '\\' in program");
no_subst:
if (!check_space(t, &spc))
tok_str_add2(tok_str, t, &cval);
nosubst = 0;
if (t == TOK_NOSUBST)
nosubst = 1;
}
}
if (macro_str1)
tok_str_free_str(macro_str1);
}
/* return next token with macro substitution */
ST_FUNC void next(void)
{
redo:
if (parse_flags & PARSE_FLAG_SPACES)
next_nomacro_spc();
else
next_nomacro();
if (macro_ptr) {
if (tok == TOK_NOSUBST || tok == TOK_PLCHLDR) {
/* discard preprocessor markers */
goto redo;
} else if (tok == 0) {
/* end of macro or unget token string */
end_macro();
goto redo;
}
} else if (tok >= TOK_IDENT && (parse_flags & PARSE_FLAG_PREPROCESS)) {
Sym *s;
/* if reading from file, try to substitute macros */
s = define_find(tok);
if (s) {
Sym *nested_list = NULL;
tokstr_buf.len = 0;
macro_subst_tok(&tokstr_buf, &nested_list, s, 1);
tok_str_add(&tokstr_buf, 0);
begin_macro(&tokstr_buf, 2);
goto redo;
}
}
/* convert preprocessor tokens into C tokens */
if (tok == TOK_PPNUM) {
if (parse_flags & PARSE_FLAG_TOK_NUM)
parse_number((char *)tokc.str.data);
} else if (tok == TOK_PPSTR) {
if (parse_flags & PARSE_FLAG_TOK_STR)
parse_string((char *)tokc.str.data, tokc.str.size - 1);
}
}
/* push back current token and set current token to 'last_tok'. Only
identifier case handled for labels. */
ST_INLN void unget_tok(int last_tok)
{
TokenString *str = tok_str_alloc();
tok_str_add2(str, tok, &tokc);
tok_str_add(str, 0);
begin_macro(str, 1);
tok = last_tok;
}
ST_FUNC void preprocess_start(TCCState *s1)
{
char *buf;
s1->include_stack_ptr = s1->include_stack;
s1->ifdef_stack_ptr = s1->ifdef_stack;
file->ifdef_stack_ptr = s1->ifdef_stack_ptr;
pp_once++;
pvtop = vtop = vstack - 1;
s1->pack_stack[0] = 0;
s1->pack_stack_ptr = s1->pack_stack;
set_idnum('$', s1->dollars_in_identifiers ? IS_ID : 0);
set_idnum('.', (parse_flags & PARSE_FLAG_ASM_FILE) ? IS_ID : 0);
buf = tcc_malloc(3 + strlen(file->filename));
sprintf(buf, "\"%s\"", file->filename);
tcc_define_symbol(s1, "__BASE_FILE__", buf);
tcc_free(buf);
if (s1->nb_cmd_include_files) {
CString cstr;
int i;
cstr_new(&cstr);
for (i = 0; i < s1->nb_cmd_include_files; i++) {
cstr_cat(&cstr, "#include \"", -1);
cstr_cat(&cstr, s1->cmd_include_files[i], -1);
cstr_cat(&cstr, "\"\n", -1);
}
*s1->include_stack_ptr++ = file;
tcc_open_bf(s1, "<command line>", cstr.size);
memcpy(file->buffer, cstr.data, cstr.size);
cstr_free(&cstr);
}
}
ST_FUNC void tccpp_new(TCCState *s)
{
int i, c;
const char *p, *r;
/* might be used in error() before preprocess_start() */
s->include_stack_ptr = s->include_stack;
/* init isid table */
for(i = CH_EOF; i<128; i++)
set_idnum(i,
is_space(i) ? IS_SPC
: isid(i) ? IS_ID
: isnum(i) ? IS_NUM
: 0);
for(i = 128; i<256; i++)
set_idnum(i, IS_ID);
/* init allocators */
tal_new(&toksym_alloc, TOKSYM_TAL_LIMIT, TOKSYM_TAL_SIZE);
tal_new(&tokstr_alloc, TOKSTR_TAL_LIMIT, TOKSTR_TAL_SIZE);
tal_new(&cstr_alloc, CSTR_TAL_LIMIT, CSTR_TAL_SIZE);
memset(hash_ident, 0, TOK_HASH_SIZE * sizeof(TokenSym *));
cstr_new(&cstr_buf);
cstr_realloc(&cstr_buf, STRING_MAX_SIZE);
tok_str_new(&tokstr_buf);
tok_str_realloc(&tokstr_buf, TOKSTR_MAX_SIZE);
tok_ident = TOK_IDENT;
p = tcc_keywords;
while (*p) {
r = p;
for(;;) {
c = *r++;
if (c == '\0')
break;
}
tok_alloc(p, r - p - 1);
p = r;
}
}
ST_FUNC void tccpp_delete(TCCState *s)
{
int i, n;
/* free -D and compiler defines */
free_defines(NULL);
/* cleanup from error/setjmp */
while (macro_stack)
end_macro();
macro_ptr = NULL;
while (file)
tcc_close();
/* free tokens */
n = tok_ident - TOK_IDENT;
for(i = 0; i < n; i++)
tal_free(toksym_alloc, table_ident[i]);
tcc_free(table_ident);
table_ident = NULL;
/* free static buffers */
cstr_free(&tokcstr);
cstr_free(&cstr_buf);
cstr_free(¯o_equal_buf);
tok_str_free_str(tokstr_buf.str);
/* free allocators */
tal_delete(toksym_alloc);
toksym_alloc = NULL;
tal_delete(tokstr_alloc);
tokstr_alloc = NULL;
tal_delete(cstr_alloc);
cstr_alloc = NULL;
}
/* ------------------------------------------------------------------------- */
/* tcc -E [-P[1]] [-dD} support */
static void tok_print(const char *msg, const int *str)
{
FILE *fp;
int t;
CValue cval;
fp = tcc_state->ppfp;
if (!fp || !tcc_state->dflag)
fp = stdout;
fprintf(fp, "%s ", msg);
while (str) {
TOK_GET(&t, &str, &cval);
if (!t)
break;
fprintf(fp,"%s", get_tok_str(t, &cval));
}
fprintf(fp, "\n");
}
static void pp_line(TCCState *s1, BufferedFile *f, int level)
{
int d = f->line_num - f->line_ref;
if (s1->dflag & 4)
return;
if (s1->Pflag == LINE_MACRO_OUTPUT_FORMAT_NONE) {
;
} else if (level == 0 && f->line_ref && d < 8) {
while (d > 0)
fputs("\n", s1->ppfp), --d;
} else if (s1->Pflag == LINE_MACRO_OUTPUT_FORMAT_STD) {
fprintf(s1->ppfp, "#line %d \"%s\"\n", f->line_num, f->filename);
} else {
fprintf(s1->ppfp, "# %d \"%s\"%s\n", f->line_num, f->filename,
level > 0 ? " 1" : level < 0 ? " 2" : "");
}
f->line_ref = f->line_num;
}
static void define_print(TCCState *s1, int v)
{
FILE *fp;
Sym *s;
s = define_find(v);
if (NULL == s || NULL == s->d)
return;
fp = s1->ppfp;
fprintf(fp, "#define %s", get_tok_str(v, NULL));
if (s->type.t == MACRO_FUNC) {
Sym *a = s->next;
fprintf(fp,"(");
if (a)
for (;;) {
fprintf(fp,"%s", get_tok_str(a->v & ~SYM_FIELD, NULL));
if (!(a = a->next))
break;
fprintf(fp,",");
}
fprintf(fp,")");
}
tok_print("", s->d);
}
static void pp_debug_defines(TCCState *s1)
{
int v, t;
const char *vs;
FILE *fp;
t = pp_debug_tok;
if (t == 0)
return;
file->line_num--;
pp_line(s1, file, 0);
file->line_ref = ++file->line_num;
fp = s1->ppfp;
v = pp_debug_symv;
vs = get_tok_str(v, NULL);
if (t == TOK_DEFINE) {
define_print(s1, v);
} else if (t == TOK_UNDEF) {
fprintf(fp, "#undef %s\n", vs);
} else if (t == TOK_push_macro) {
fprintf(fp, "#pragma push_macro(\"%s\")\n", vs);
} else if (t == TOK_pop_macro) {
fprintf(fp, "#pragma pop_macro(\"%s\")\n", vs);
}
pp_debug_tok = 0;
}
static void pp_debug_builtins(TCCState *s1)
{
int v;
for (v = TOK_IDENT; v < tok_ident; ++v)
define_print(s1, v);
}
/* Add a space between tokens a and b to avoid unwanted textual pasting */
static int pp_need_space(int a, int b)
{
return 'E' == a ? '+' == b || '-' == b
: '+' == a ? TOK_INC == b || '+' == b
: '-' == a ? TOK_DEC == b || '-' == b
: a >= TOK_IDENT ? b >= TOK_IDENT
: 0;
}
/* maybe hex like 0x1e */
static int pp_check_he0xE(int t, const char *p)
{
if (t == TOK_PPNUM && toup(strchr(p, 0)[-1]) == 'E')
return 'E';
return t;
}
/* Preprocess the current file */
ST_FUNC int tcc_preprocess(TCCState *s1)
{
BufferedFile **iptr;
int token_seen, spcs, level;
const char *p;
Sym *define_start;
define_start = define_stack;
preprocess_start(s1);
ch = file->buf_ptr[0];
tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
parse_flags = PARSE_FLAG_PREPROCESS
| (parse_flags & PARSE_FLAG_ASM_FILE)
| PARSE_FLAG_LINEFEED
| PARSE_FLAG_SPACES
| PARSE_FLAG_ACCEPT_STRAYS
;
/* Credits to Fabrice Bellard's initial revision to demonstrate its
capability to compile and run itself, provided all numbers are
given as decimals. tcc -E -P10 will do. */
if (s1->Pflag == LINE_MACRO_OUTPUT_FORMAT_P10)
parse_flags |= PARSE_FLAG_TOK_NUM, s1->Pflag = 1;
#ifdef PP_BENCH
/* for PP benchmarks */
do next(); while (tok != TOK_EOF);
free_defines(define_start);
return 0;
#endif
if (s1->dflag & 1) {
pp_debug_builtins(s1);
s1->dflag &= ~1;
}
token_seen = TOK_LINEFEED, spcs = 0;
pp_line(s1, file, 0);
for (;;) {
iptr = s1->include_stack_ptr;
next();
if (tok == TOK_EOF)
break;
level = s1->include_stack_ptr - iptr;
if (level) {
if (level > 0)
pp_line(s1, *iptr, 0);
pp_line(s1, file, level);
}
if (s1->dflag) {
pp_debug_defines(s1);
if (s1->dflag & 4)
continue;
}
if (token_seen == TOK_LINEFEED) {
if (tok == ' ') {
++spcs;
continue;
}
if (tok == TOK_LINEFEED) {
spcs = 0;
continue;
}
pp_line(s1, file, 0);
} else if (tok == TOK_LINEFEED) {
++file->line_ref;
} else {
spcs = pp_need_space(token_seen, tok);
}
while (spcs)
fputs(" ", s1->ppfp), --spcs;
fputs(p = get_tok_str(tok, &tokc), s1->ppfp);
token_seen = pp_check_he0xE(tok, p);;
}
/* reset define stack, but keep -D and built-ins */
free_defines(define_start);
return 0;
}
/* ------------------------------------------------------------------------- */
/*
* TCC - Tiny C Compiler
*
* Copyright (c) 2001-2004 Fabrice Bellard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _TCC_H
#include "tcc.h"
#endif
#if BOOTSTRAP && __arm__
void
tccgen_ok ()
{
}
#endif
/********************************************************/
/* global variables */
/* loc : local variable index
ind : output code index
rsym: return symbol
anon_sym: anonymous symbol index
*/
ST_DATA int rsym, anon_sym, ind, loc;
ST_DATA Sym *sym_free_first;
ST_DATA void **sym_pools;
ST_DATA int nb_sym_pools;
ST_DATA Sym *global_stack;
ST_DATA Sym *local_stack;
ST_DATA Sym *define_stack;
ST_DATA Sym *global_label_stack;
ST_DATA Sym *local_label_stack;
static int local_scope;
static int in_sizeof;
static int section_sym;
ST_DATA int vlas_in_scope; /* number of VLAs that are currently in scope */
ST_DATA int vla_sp_root_loc; /* vla_sp_loc for SP before any VLAs were pushed */
ST_DATA int vla_sp_loc; /* Pointer to variable holding location to store stack pointer on the stack when modifying stack pointer */
ST_DATA SValue __vstack[1+VSTACK_SIZE], *vtop, *pvtop;
ST_DATA int const_wanted; /* true if constant wanted */
ST_DATA int nocode_wanted; /* true if no code generation wanted for an expression */
ST_DATA int global_expr; /* true if compound literals must be allocated globally (used during initializers parsing */
ST_DATA CType func_vt; /* current function return type (used by return instruction) */
ST_DATA int func_var; /* true if current function is variadic (used by return instruction) */
ST_DATA int func_vc;
ST_DATA int last_line_num, last_ind, func_ind; /* debug last line number and pc */
ST_DATA const char *funcname;
ST_DATA CType char_pointer_type, func_old_type, int_type, size_type;
ST_DATA struct switch_t {
struct case_t {
#if HAVE_LONG_LONG_STUB || HAVE_LONG_LONG
int64_t v1, v2;
#else
int32_t v1, v1_padding, v2, v2_padding;
#endif
int sym;
} **p; int n; /* list of case ranges */
int def_sym; /* default symbol */
} *cur_switch; /* current switch */
/* ------------------------------------------------------------------------- */
static void gen_cast(CType *type);
static inline CType *pointed_type(CType *type);
static int is_compatible_types(CType *type1, CType *type2);
static int parse_btype(CType *type, AttributeDef *ad);
static CType *type_decl(CType *type, AttributeDef *ad, int *v, int td);
static void parse_expr_type(CType *type);
static void init_putv(CType *type, Section *sec, unsigned long c);
static void decl_initializer(CType *type, Section *sec, unsigned long c, int first, int size_only);
static void block(int *bsym, int *csym, int is_expr);
static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, int has_init, int v, int scope);
static int decl0(int l, int is_for_loop_init, Sym *);
static void expr_eq(void);
static void vla_runtime_type_size(CType *type, int *a);
static void vla_sp_restore(void);
static void vla_sp_restore_root(void);
static int is_compatible_parameter_types(CType *type1, CType *type2);
#if HAVE_LONG_LONG
static inline int64_t expr_const64(void);
ST_FUNC void vpush64(int ty, unsigned long long v);
#else
static inline int32_t expr_const32(void);
#endif
ST_FUNC void vpush(CType *type);
ST_FUNC int gvtst(int inv, int t);
ST_FUNC int is_btype_size(int bt);
static void gen_inline_functions(TCCState *s);
ST_INLN int is_float(int t)
{
int bt;
bt = t & VT_BTYPE;
return bt == VT_LDOUBLE || bt == VT_DOUBLE || bt == VT_FLOAT || bt == VT_QFLOAT;
}
/* we use our own 'finite' function to avoid potential problems with
non standard math libs */
/* XXX: endianness dependent */
ST_FUNC int ieee_finite(double d)
{
int p[4];
memcpy(p, &d, sizeof(double));
return ((unsigned)((p[1] | 0x800fffff) + 1)) >> 31;
}
ST_FUNC void test_lvalue(void)
{
if (!(vtop->r & VT_LVAL))
expect("lvalue");
}
ST_FUNC void check_vstack(void)
{
if (pvtop != vtop)
tcc_error("internal compiler error: vstack leak (%d)", vtop - pvtop);
}
/* ------------------------------------------------------------------------- */
/* vstack debugging aid */
#if 0
void pv (const char *lbl, int a, int b)
{
int i;
for (i = a; i < a + b; ++i) {
SValue *p = &vtop[-i];
printf("%s vtop[-%d] : type.t:%04x r:%04x r2:%04x c.i:%d\n",
lbl, i, p->type.t, p->r, p->r2, (int)p->c.i);
}
}
#endif
/* ------------------------------------------------------------------------- */
/* start of translation unit info */
ST_FUNC void tcc_debug_start(TCCState *s1)
{
if (s1->do_debug) {
char buf[512];
/* file info: full path + filename */
section_sym = put_elf_sym(symtab_section, 0, 0,
ELFW(ST_INFO)(STB_LOCAL, STT_SECTION), 0,
text_section->sh_num, NULL);
getcwd(buf, sizeof(buf));
#ifdef _WIN32
normalize_slashes(buf);
#endif
pstrcat(buf, sizeof(buf), "/");
put_stabs_r(buf, N_SO, 0, 0,
text_section->data_offset, text_section, section_sym);
put_stabs_r(file->filename, N_SO, 0, 0,
text_section->data_offset, text_section, section_sym);
last_ind = 0;
last_line_num = 0;
}
/* an elf symbol of type STT_FILE must be put so that STB_LOCAL
symbols can be safely used */
put_elf_sym(symtab_section, 0, 0,
ELFW(ST_INFO)(STB_LOCAL, STT_FILE), 0,
SHN_ABS, file->filename);
}
/* put end of translation unit info */
ST_FUNC void tcc_debug_end(TCCState *s1)
{
if (!s1->do_debug)
return;
put_stabs_r(NULL, N_SO, 0, 0,
text_section->data_offset, text_section, section_sym);
}
/* generate line number info */
ST_FUNC void tcc_debug_line(TCCState *s1)
{
if (!s1->do_debug)
return;
if ((last_line_num != file->line_num || last_ind != ind)) {
put_stabn(N_SLINE, 0, file->line_num, ind - func_ind);
last_ind = ind;
last_line_num = file->line_num;
}
}
/* put function symbol */
ST_FUNC void tcc_debug_funcstart(TCCState *s1, Sym *sym)
{
char buf[512];
if (!s1->do_debug)
return;
/* stabs info */
/* XXX: we put here a dummy type */
snprintf(buf, sizeof(buf), "%s:%c1",
funcname, sym->type.t & VT_STATIC ? 'f' : 'F');
put_stabs_r(buf, N_FUN, 0, file->line_num, 0,
cur_text_section, sym->c);
/* //gr gdb wants a line at the function */
put_stabn(N_SLINE, 0, file->line_num, 0);
last_ind = 0;
last_line_num = 0;
}
/* put function size */
ST_FUNC void tcc_debug_funcend(TCCState *s1, int size)
{
if (!s1->do_debug)
return;
put_stabn(N_FUN, 0, 0, size);
}
/* ------------------------------------------------------------------------- */
ST_FUNC void tccgen_start(TCCState *s1)
{
cur_text_section = NULL;
funcname = "";
anon_sym = SYM_FIRST_ANOM;
section_sym = 0;
const_wanted = 0;
nocode_wanted = 1;
/* define some often used types */
int_type.t = VT_INT;
char_pointer_type.t = VT_BYTE;
mk_pointer(&char_pointer_type);
#if PTR_SIZE == 4
size_type.t = VT_INT;
#else
size_type.t = VT_LLONG;
#endif
func_old_type.t = VT_FUNC;
func_old_type.ref = sym_push(SYM_FIELD, &int_type, FUNC_CDECL, FUNC_OLD);
tcc_debug_start(s1);
#ifdef TCC_TARGET_ARM
arm_init(s1);
#endif
}
ST_FUNC void tccgen_end(TCCState *s1)
{
gen_inline_functions(s1);
check_vstack();
/* end of translation unit info */
tcc_debug_end(s1);
}
/* ------------------------------------------------------------------------- */
/* apply storage attributes to Elf symbol */
static void update_storage(Sym *sym)
{
int t;
ElfW(Sym) *esym;
if (0 == sym->c)
return;
t = sym->type.t;
esym = &((ElfW(Sym) *)symtab_section->data)[sym->c];
#if !__MESC__ // Nyacc 0.80.40 cpp-bug
if (t & VT_VIS_MASK)
esym->st_other = (esym->st_other & ~ELFW(ST_VISIBILITY)(-1))
| ((t & VT_VIS_MASK) >> VT_VIS_SHIFT);
if (t & VT_WEAK)
esym->st_info = ELFW(ST_INFO)(STB_WEAK, ELFW(ST_TYPE)(esym->st_info));
#else //__MESC__
// #warning work around Nyacc 0.80.42 bug.
#endif // __MESC__
#ifdef TCC_TARGET_PE
if (t & VT_EXPORT)
esym->st_other |= ST_PE_EXPORT;
#endif
}
/* ------------------------------------------------------------------------- */
/* update sym->c so that it points to an external symbol in section
'section' with value 'value' */
ST_FUNC void put_extern_sym2(Sym *sym, Section *section,
addr_t value, unsigned long size,
int can_add_underscore)
{
int sym_type, sym_bind, sh_num, info, other, t;
ElfW(Sym) *esym;
const char *name;
char buf1[256];
#ifdef CONFIG_TCC_BCHECK
char buf[32];
#endif
if (section == NULL)
sh_num = SHN_UNDEF;
else if (section == SECTION_ABS)
sh_num = SHN_ABS;
else
sh_num = section->sh_num;
if (!sym->c) {
name = get_tok_str(sym->v, NULL);
#ifdef CONFIG_TCC_BCHECK
if (tcc_state->do_bounds_check) {
/* XXX: avoid doing that for statics ? */
/* if bound checking is activated, we change some function
names by adding the "__bound" prefix */
switch(sym->v) {
#ifdef TCC_TARGET_PE
/* XXX: we rely only on malloc hooks */
case TOK_malloc:
case TOK_free:
case TOK_realloc:
case TOK_memalign:
case TOK_calloc:
#endif
case TOK_memcpy:
case TOK_memmove:
case TOK_memset:
case TOK_strlen:
case TOK_strcpy:
case TOK_alloca:
strcpy(buf, "__bound_");
strcat(buf, name);
name = buf;
break;
}
}
#endif
t = sym->type.t;
if ((t & VT_BTYPE) == VT_FUNC) {
sym_type = STT_FUNC;
} else if ((t & VT_BTYPE) == VT_VOID) {
sym_type = STT_NOTYPE;
} else {
sym_type = STT_OBJECT;
}
if (t & VT_STATIC)
sym_bind = STB_LOCAL;
else
sym_bind = STB_GLOBAL;
other = 0;
#ifdef TCC_TARGET_PE
if (sym_type == STT_FUNC && sym->type.ref) {
Sym *ref = sym->type.ref;
if (ref->a.func_call == FUNC_STDCALL && can_add_underscore) {
sprintf(buf1, "_%s@%d", name, ref->a.func_args * PTR_SIZE);
name = buf1;
other |= ST_PE_STDCALL;
can_add_underscore = 0;
}
}
if (t & VT_IMPORT)
other |= ST_PE_IMPORT;
#endif
if (tcc_state->leading_underscore && can_add_underscore) {
buf1[0] = '_';
pstrcpy(buf1 + 1, sizeof(buf1) - 1, name);
name = buf1;
}
if (sym->asm_label)
name = get_tok_str(sym->asm_label, NULL);
info = ELFW(ST_INFO)(sym_bind, sym_type);
sym->c = set_elf_sym(symtab_section, value, size, info, other, sh_num, name);
} else {
esym = &((ElfW(Sym) *)symtab_section->data)[sym->c];
esym->st_value = value;
esym->st_size = size;
esym->st_shndx = sh_num;
}
update_storage(sym);
}
ST_FUNC void put_extern_sym(Sym *sym, Section *section,
addr_t value, unsigned long size)
{
put_extern_sym2(sym, section, value, size, 1);
}
/* add a new relocation entry to symbol 'sym' in section 's' */
ST_FUNC void greloca(Section *s, Sym *sym, unsigned long offset, int type,
addr_t addend)
{
int c = 0;
if (nocode_wanted && s == cur_text_section)
return;
if (sym) {
if (0 == sym->c)
put_extern_sym(sym, NULL, 0, 0);
c = sym->c;
}
/* now we can add ELF relocation info */
put_elf_reloca(symtab_section, s, offset, type, c, addend);
}
#if PTR_SIZE == 4
ST_FUNC void greloc(Section *s, Sym *sym, unsigned long offset, int type)
{
greloca(s, sym, offset, type, 0);
}
#endif
/* ------------------------------------------------------------------------- */
/* symbol allocator */
static Sym *__sym_malloc(void)
{
Sym *sym_pool, *sym, *last_sym;
int i;
sym_pool = tcc_malloc(SYM_POOL_NB * sizeof(Sym));
dynarray_add(&sym_pools, &nb_sym_pools, sym_pool);
last_sym = sym_free_first;
sym = sym_pool;
for(i = 0; i < SYM_POOL_NB; i++) {
sym->next = last_sym;
last_sym = sym;
sym++;
}
sym_free_first = last_sym;
return last_sym;
}
static inline Sym *sym_malloc(void)
{
Sym *sym;
#ifndef SYM_DEBUG
sym = sym_free_first;
if (!sym)
sym = __sym_malloc();
sym_free_first = sym->next;
return sym;
#else
sym = tcc_malloc(sizeof(Sym));
return sym;
#endif
}
ST_INLN void sym_free(Sym *sym)
{
#ifndef SYM_DEBUG
sym->next = sym_free_first;
sym_free_first = sym;
#else
tcc_free(sym);
#endif
}
/* push, without hashing */
ST_FUNC Sym *sym_push2(Sym **ps, int v, int t, long c)
{
Sym *s;
s = sym_malloc();
s->scope = 0;
s->v = v;
s->type.t = t;
s->type.ref = NULL;
#ifdef _WIN64
s->d = NULL;
#endif
s->c = c;
s->next = NULL;
/* add in stack */
s->prev = *ps;
*ps = s;
return s;
}
/* find a symbol and return its associated structure. 's' is the top
of the symbol stack */
ST_FUNC Sym *sym_find2(Sym *s, int v)
{
while (s) {
if (s->v == v)
return s;
else if (s->v == -1)
return NULL;
s = s->prev;
}
return NULL;
}
/* structure lookup */
ST_INLN Sym *struct_find(int v)
{
v -= TOK_IDENT;
if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
return NULL;
return table_ident[v]->sym_struct;
}
/* find an identifier */
ST_INLN Sym *sym_find(int v)
{
v -= TOK_IDENT;
if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
return NULL;
return table_ident[v]->sym_identifier;
}
/* push a given symbol on the symbol stack */
ST_FUNC Sym *sym_push(int v, CType *type, int r, long c)
{
Sym *s, **ps;
TokenSym *ts;
if (local_stack)
ps = &local_stack;
else
ps = &global_stack;
s = sym_push2(ps, v, type->t, c);
s->type.ref = type->ref;
s->r = r;
/* don't record fields or anonymous symbols */
/* XXX: simplify */
if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) < SYM_FIRST_ANOM) {
/* record symbol in token array */
ts = table_ident[(v & ~SYM_STRUCT) - TOK_IDENT];
if (v & SYM_STRUCT)
ps = &ts->sym_struct;
else
ps = &ts->sym_identifier;
s->prev_tok = *ps;
*ps = s;
s->scope = local_scope;
if (s->prev_tok && s->prev_tok->scope == s->scope)
tcc_error("redeclaration of '%s'",
get_tok_str(v & ~SYM_STRUCT, NULL));
}
return s;
}
/* push a global identifier */
ST_FUNC Sym *global_identifier_push(int v, int t, long c)
{
Sym *s, **ps;
s = sym_push2(&global_stack, v, t, c);
/* don't record anonymous symbol */
if (v < SYM_FIRST_ANOM) {
ps = &table_ident[v - TOK_IDENT]->sym_identifier;
/* modify the top most local identifier, so that
sym_identifier will point to 's' when popped */
while (*ps != NULL)
ps = &(*ps)->prev_tok;
s->prev_tok = NULL;
*ps = s;
}
return s;
}
/* pop symbols until top reaches 'b'. If KEEP is non-zero don't really
pop them yet from the list, but do remove them from the token array. */
ST_FUNC void sym_pop(Sym **ptop, Sym *b, int keep)
{
Sym *s, *ss, **ps;
TokenSym *ts;
int v;
s = *ptop;
while(s != b) {
ss = s->prev;
v = s->v;
/* remove symbol in token array */
/* XXX: simplify */
if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) < SYM_FIRST_ANOM) {
ts = table_ident[(v & ~SYM_STRUCT) - TOK_IDENT];
if (v & SYM_STRUCT)
ps = &ts->sym_struct;
else
ps = &ts->sym_identifier;
*ps = s->prev_tok;
}
if (!keep)
sym_free(s);
s = ss;
}
if (!keep)
*ptop = b;
}
/* ------------------------------------------------------------------------- */
static void vsetc(CType *type, int r, CValue *vc)
{
int v;
if (vtop >= vstack + (VSTACK_SIZE - 1))
tcc_error("memory full (vstack)");
/* cannot let cpu flags if other instruction are generated. Also
avoid leaving VT_JMP anywhere except on the top of the stack
because it would complicate the code generator.
Don't do this when nocode_wanted. vtop might come from
!nocode_wanted regions (see 88_codeopt.c) and transforming
it to a register without actually generating code is wrong
as their value might still be used for real. All values
we push under nocode_wanted will eventually be popped
again, so that the VT_CMP/VT_JMP value will be in vtop
when code is unsuppressed again.
Same logic below in vswap(); */
if (vtop >= vstack && !nocode_wanted) {
v = vtop->r & VT_VALMASK;
if (v == VT_CMP || (v & ~1) == VT_JMP)
gv(RC_INT);
}
vtop++;
vtop->type = *type;
vtop->r = r;
vtop->r2 = VT_CONST;
vtop->c = *vc;
vtop->sym = NULL;
}
ST_FUNC void vswap(void)
{
SValue tmp;
/* cannot vswap cpu flags. See comment at vsetc() above */
if (vtop >= vstack && !nocode_wanted) {
int v = vtop->r & VT_VALMASK;
if (v == VT_CMP || (v & ~1) == VT_JMP)
gv(RC_INT);
}
tmp = vtop[0];
vtop[0] = vtop[-1];
vtop[-1] = tmp;
}
/* pop stack value */
ST_FUNC void vpop(void)
{
int v;
v = vtop->r & VT_VALMASK;
#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64)
/* for x86, we need to pop the FP stack */
if (v == TREG_ST0) {
o(0xd8dd); /* fstp %st(0) */
} else
#endif
if (v == VT_JMP || v == VT_JMPI) {
/* need to put correct jump if && or || without test */
gsym(vtop->c.i);
}
vtop--;
}
/* push constant of type "type" with useless value */
ST_FUNC void vpush(CType *type)
{
CValue cval;
vsetc(type, VT_CONST, &cval);
}
/* push integer constant */
ST_FUNC void vpushi(int v)
{
CValue cval;
cval.i = v;
vsetc(&int_type, VT_CONST, &cval);
}
/* push a pointer sized constant */
static void vpushs(addr_t v)
{
CValue cval;
cval.i = v;
vsetc(&size_type, VT_CONST, &cval);
}
/* push arbitrary 64bit constant */
ST_FUNC void vpush64(int ty, unsigned long long v)
{
CValue cval;
CType ctype;
ctype.t = ty;
ctype.ref = NULL;
cval.i = v;
vsetc(&ctype, VT_CONST, &cval);
}
/* push long long constant */
static inline void vpushll(long long v)
{
vpush64(VT_LLONG, v);
}
ST_FUNC void vset(CType *type, int r, long v)
{
CValue cval;
cval.i = v;
vsetc(type, r, &cval);
}
static void vseti(int r, int v)
{
CType type;
type.t = VT_INT;
type.ref = 0;
vset(&type, r, v);
}
ST_FUNC void vpushv(SValue *v)
{
if (vtop >= vstack + (VSTACK_SIZE - 1))
tcc_error("memory full (vstack)");
vtop++;
*vtop = *v;
}
static void vdup(void)
{
vpushv(vtop);
}
/* rotate n first stack elements to the bottom
I1 ... In -> I2 ... In I1 [top is right]
*/
ST_FUNC void vrotb(int n)
{
int i;
SValue tmp;
tmp = vtop[-n + 1];
for(i=-n+1;i!=0;i++)
vtop[i] = vtop[i+1];
vtop[0] = tmp;
}
/* rotate the n elements before entry e towards the top
I1 ... In ... -> In I1 ... I(n-1) ... [top is right]
*/
ST_FUNC void vrote(SValue *e, int n)
{
int i;
SValue tmp;
tmp = *e;
for(i = 0;i < n - 1; i++)
e[-i] = e[-i - 1];
e[-n + 1] = tmp;
}
/* rotate n first stack elements to the top
I1 ... In -> In I1 ... I(n-1) [top is right]
*/
ST_FUNC void vrott(int n)
{
vrote(vtop, n);
}
/* push a symbol value of TYPE */
static inline void vpushsym(CType *type, Sym *sym)
{
CValue cval;
cval.i = 0;
vsetc(type, VT_CONST | VT_SYM, &cval);
vtop->sym = sym;
}
/* Return a static symbol pointing to a section */
ST_FUNC Sym *get_sym_ref(CType *type, Section *sec, unsigned long offset, unsigned long size)
{
int v;
Sym *sym;
v = anon_sym++;
sym = global_identifier_push(v, type->t | VT_STATIC, 0);
sym->type.ref = type->ref;
sym->r = VT_CONST | VT_SYM;
put_extern_sym(sym, sec, offset, size);
return sym;
}
/* push a reference to a section offset by adding a dummy symbol */
static void vpush_ref(CType *type, Section *sec, unsigned long offset, unsigned long size)
{
vpushsym(type, get_sym_ref(type, sec, offset, size));
}
/* define a new external reference to a symbol 'v' of type 'u' */
ST_FUNC Sym *external_global_sym(int v, CType *type, int r)
{
Sym *s;
s = sym_find(v);
if (!s) {
/* push forward reference */
s = global_identifier_push(v, type->t | VT_EXTERN, 0);
s->type.ref = type->ref;
s->r = r | VT_CONST | VT_SYM;
}
return s;
}
/* Merge some storage attributes. */
static void patch_storage(Sym *sym, CType *type)
{
int t;
if (!is_compatible_types(&sym->type, type))
tcc_error("incompatible types for redefinition of '%s'",
get_tok_str(sym->v, NULL));
t = type->t;
#ifdef TCC_TARGET_PE
if ((sym->type.t ^ t) & VT_IMPORT)
tcc_error("incompatible dll linkage for redefinition of '%s'",
get_tok_str(sym->v, NULL));
#endif
sym->type.t |= t & (VT_EXPORT|VT_WEAK);
if (t & VT_VIS_MASK) {
int vis = sym->type.t & VT_VIS_MASK;
int vis2 = t & VT_VIS_MASK;
if (vis == (STV_DEFAULT << VT_VIS_SHIFT))
vis = vis2;
else if (vis2 != (STV_DEFAULT << VT_VIS_SHIFT))
vis = (vis < vis2) ? vis : vis2;
sym->type.t = (sym->type.t & ~VT_VIS_MASK) | vis;
}
}
/* define a new external reference to a symbol 'v' */
static Sym *external_sym(int v, CType *type, int r)
{
Sym *s;
s = sym_find(v);
if (!s) {
/* push forward reference */
s = sym_push(v, type, r | VT_CONST | VT_SYM, 0);
s->type.t |= VT_EXTERN;
} else {
if (s->type.ref == func_old_type.ref) {
s->type.ref = type->ref;
s->r = r | VT_CONST | VT_SYM;
s->type.t |= VT_EXTERN;
}
patch_storage(s, type);
update_storage(s);
}
return s;
}
/* push a reference to global symbol v */
ST_FUNC void vpush_global_sym(CType *type, int v)
{
vpushsym(type, external_global_sym(v, type, 0));
}
/* save registers up to (vtop - n) stack entry */
ST_FUNC void save_regs(int n)
{
SValue *p, *p1;
for(p = vstack, p1 = vtop - n; p <= p1; p++)
save_reg(p->r);
}
/* save r to the memory stack, and mark it as being free */
ST_FUNC void save_reg(int r)
{
save_reg_upstack(r, 0);
}
/* save r to the memory stack, and mark it as being free,
if seen up to (vtop - n) stack entry */
ST_FUNC void save_reg_upstack(int r, int n)
{
int l, saved, size, align;
SValue *p, *p1, sv;
CType *type;
if ((r &= VT_VALMASK) >= VT_CONST)
return;
if (nocode_wanted)
return;
/* modify all stack values */
saved = 0;
l = 0;
for(p = vstack, p1 = vtop - n; p <= p1; p++) {
if ((p->r & VT_VALMASK) == r ||
((p->type.t & VT_BTYPE) == VT_LLONG && (p->r2 & VT_VALMASK) == r)) {
/* must save value on stack if not already done */
if (!saved) {
/* NOTE: must reload 'r' because r might be equal to r2 */
r = p->r & VT_VALMASK;
/* store register in the stack */
type = &p->type;
if ((p->r & VT_LVAL) ||
(!is_float(type->t) && (type->t & VT_BTYPE) != VT_LLONG))
#if PTR_SIZE == 8
type = &char_pointer_type;
#else
type = &int_type;
#endif
size = type_size(type, &align);
loc = (loc - size) & -align;
sv.type.t = type->t;
sv.r = VT_LOCAL | VT_LVAL;
sv.c.i = loc;
store(r, &sv);
#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64)
/* x86 specific: need to pop fp register ST0 if saved */
if (r == TREG_ST0) {
o(0xd8dd); /* fstp %st(0) */
}
#endif
#if PTR_SIZE == 4
/* special long long case */
if ((type->t & VT_BTYPE) == VT_LLONG) {
sv.c.i += 4;
store(p->r2, &sv);
}
#endif
l = loc;
saved = 1;
}
/* mark that stack entry as being saved on the stack */
if (p->r & VT_LVAL) {
/* also clear the bounded flag because the
relocation address of the function was stored in
p->c.i */
p->r = (p->r & ~(VT_VALMASK | VT_BOUNDED)) | VT_LLOCAL;
} else {
p->r = lvalue_type(p->type.t) | VT_LOCAL;
}
p->r2 = VT_CONST;
p->c.i = l;
}
}
}
#ifdef TCC_TARGET_ARM
/* find a register of class 'rc2' with at most one reference on stack.
* If none, call get_reg(rc) */
ST_FUNC int get_reg_ex(int rc, int rc2)
{
int r;
SValue *p;
for(r=0;r<NB_REGS;r++) {
if (reg_classes[r] & rc2) {
int n;
n=0;
for(p = vstack; p <= vtop; p++) {
if ((p->r & VT_VALMASK) == r ||
(p->r2 & VT_VALMASK) == r)
n++;
}
if (n <= 1)
return r;
}
}
return get_reg(rc);
}
#endif
/* find a free register of class 'rc'. If none, save one register */
ST_FUNC int get_reg(int rc)
{
int r;
SValue *p;
/* find a free register */
for(r=0;r<NB_REGS;r++) {
if (reg_classes[r] & rc) {
if (nocode_wanted)
return r;
for(p=vstack;p<=vtop;p++) {
if ((p->r & VT_VALMASK) == r ||
(p->r2 & VT_VALMASK) == r)
goto notfound;
}
return r;
}
notfound: ;
}
/* no register left : free the first one on the stack (VERY
IMPORTANT to start from the bottom to ensure that we don't
spill registers used in gen_opi()) */
for(p=vstack;p<=vtop;p++) {
/* look at second register (if long long) */
r = p->r2 & VT_VALMASK;
if (r < VT_CONST && (reg_classes[r] & rc))
goto save_found;
r = p->r & VT_VALMASK;
if (r < VT_CONST && (reg_classes[r] & rc)) {
save_found:
save_reg(r);
return r;
}
}
/* Should never comes here */
return -1;
}
/* move register 's' (of type 't') to 'r', and flush previous value of r to memory
if needed */
static void move_reg(int r, int s, int t)
{
SValue sv;
if (r != s) {
save_reg(r);
sv.type.t = t;
sv.type.ref = NULL;
sv.r = s;
sv.c.i = 0;
load(r, &sv);
}
}
/* get address of vtop (vtop MUST BE an lvalue) */
ST_FUNC void gaddrof(void)
{
vtop->r &= ~VT_LVAL;
/* tricky: if saved lvalue, then we can go back to lvalue */
if ((vtop->r & VT_VALMASK) == VT_LLOCAL)
vtop->r = (vtop->r & ~(VT_VALMASK | VT_LVAL_TYPE)) | VT_LOCAL | VT_LVAL;
}
#ifdef CONFIG_TCC_BCHECK
/* generate lvalue bound code */
static void gbound(void)
{
int lval_type;
CType type1;
vtop->r &= ~VT_MUSTBOUND;
/* if lvalue, then use checking code before dereferencing */
if (vtop->r & VT_LVAL) {
/* if not VT_BOUNDED value, then make one */
if (!(vtop->r & VT_BOUNDED)) {
lval_type = vtop->r & (VT_LVAL_TYPE | VT_LVAL);
/* must save type because we must set it to int to get pointer */
type1 = vtop->type;
vtop->type.t = VT_PTR;
gaddrof();
vpushi(0);
gen_bounded_ptr_add();
vtop->r |= lval_type;
vtop->type = type1;
}
/* then check for dereferencing */
gen_bounded_ptr_deref();
}
}
#endif
/* store vtop a register belonging to class 'rc'. lvalues are
converted to values. Cannot be used if cannot be converted to
register value (such as structures). */
ST_FUNC int gv(int rc)
{
int r, bit_pos, bit_size, size, align;
int rc2;
/* NOTE: get_reg can modify vstack[] */
if (vtop->type.t & VT_BITFIELD) {
CType type;
int bits = 32;
bit_pos = (vtop->type.t >> VT_STRUCT_SHIFT) & 0x3f;
bit_size = (vtop->type.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f;
/* remove bit field info to avoid loops */
vtop->type.t &= ~VT_BITFIELD & ((1 << VT_STRUCT_SHIFT) - 1);
/* cast to int to propagate signedness in following ops */
if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
type.t = VT_LLONG;
bits = 64;
} else
type.t = VT_INT;
if((vtop->type.t & VT_UNSIGNED) ||
(vtop->type.t & VT_BTYPE) == VT_BOOL ||
(((vtop->type.t & VT_BTYPE) == VT_ENUM) &&
vtop->type.ref->a.unsigned_enum))
type.t |= VT_UNSIGNED;
gen_cast(&type);
/* generate shifts */
vpushi(bits - (bit_pos + bit_size));
gen_op(TOK_SHL);
vpushi(bits - bit_size);
/* NOTE: transformed to SHR if unsigned */
gen_op(TOK_SAR);
r = gv(rc);
} else {
if (is_float(vtop->type.t) &&
(vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
unsigned long offset;
/* CPUs usually cannot use float constants, so we store them
generically in data segment */
size = type_size(&vtop->type, &align);
offset = section_add(data_section, size, align);
vpush_ref(&vtop->type, data_section, offset, size);
vswap();
init_putv(&vtop->type, data_section, offset);
vtop->r |= VT_LVAL;
}
#ifdef CONFIG_TCC_BCHECK
if (vtop->r & VT_MUSTBOUND)
gbound();
#endif
r = vtop->r & VT_VALMASK;
rc2 = (rc & RC_FLOAT) ? RC_FLOAT : RC_INT;
#ifndef TCC_TARGET_ARM64
if (rc == RC_IRET)
rc2 = RC_LRET;
#ifdef TCC_TARGET_X86_64
else if (rc == RC_FRET)
rc2 = RC_QRET;
#endif
#endif
/* need to reload if:
- constant
- lvalue (need to dereference pointer)
- already a register, but not in the right class */
if (r >= VT_CONST
|| (vtop->r & VT_LVAL)
|| !(reg_classes[r] & rc)
#if PTR_SIZE == 8
|| ((vtop->type.t & VT_BTYPE) == VT_QLONG && !(reg_classes[vtop->r2] & rc2))
|| ((vtop->type.t & VT_BTYPE) == VT_QFLOAT && !(reg_classes[vtop->r2] & rc2))
#else
|| ((vtop->type.t & VT_BTYPE) == VT_LLONG && !(reg_classes[vtop->r2] & rc2))
#endif
)
{
r = get_reg(rc);
#if PTR_SIZE == 8
if (((vtop->type.t & VT_BTYPE) == VT_QLONG) || ((vtop->type.t & VT_BTYPE) == VT_QFLOAT)) {
int addr_type = VT_LLONG, load_size = 8, load_type = ((vtop->type.t & VT_BTYPE) == VT_QLONG) ? VT_LLONG : VT_DOUBLE;
#else
if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
int addr_type = VT_INT, load_size = 4, load_type = VT_INT;
unsigned long long ll;
#endif
int r2, original_type;
original_type = vtop->type.t;
/* two register type load : expand to two words
temporarily */
#if PTR_SIZE == 4
if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
/* load constant */
ll = vtop->c.i;
vtop->c.i = ll; /* first word */
load(r, vtop);
vtop->r = r; /* save register value */
vpushi(ll >> 32); /* second word */
} else
#endif
if (vtop->r & VT_LVAL) {
/* We do not want to modifier the long long
pointer here, so the safest (and less
efficient) is to save all the other registers
in the stack. XXX: totally inefficient. */
#if 0
save_regs(1);
#else
/* lvalue_save: save only if used further down the stack */
save_reg_upstack(vtop->r, 1);
#endif
/* load from memory */
vtop->type.t = load_type;
load(r, vtop);
vdup();
vtop[-1].r = r; /* save register value */
/* increment pointer to get second word */
vtop->type.t = addr_type;
gaddrof();
vpushi(load_size);
gen_op('+');
vtop->r |= VT_LVAL;
vtop->type.t = load_type;
} else {
/* move registers */
load(r, vtop);
vdup();
vtop[-1].r = r; /* save register value */
vtop->r = vtop[-1].r2;
}
/* Allocate second register. Here we rely on the fact that
get_reg() tries first to free r2 of an SValue. */
r2 = get_reg(rc2);
load(r2, vtop);
vpop();
/* write second register */
vtop->r2 = r2;
vtop->type.t = original_type;
} else if ((vtop->r & VT_LVAL) && !is_float(vtop->type.t)) {
int t1, t;
/* lvalue of scalar type : need to use lvalue type
because of possible cast */
t = vtop->type.t;
t1 = t;
/* compute memory access type */
if (vtop->r & VT_LVAL_BYTE)
t = VT_BYTE;
else if (vtop->r & VT_LVAL_SHORT)
t = VT_SHORT;
if (vtop->r & VT_LVAL_UNSIGNED)
t |= VT_UNSIGNED;
vtop->type.t = t;
load(r, vtop);
/* restore wanted type */
vtop->type.t = t1;
} else {
/* one register type load */
load(r, vtop);
}
}
vtop->r = r;
#ifdef TCC_TARGET_C67
/* uses register pairs for doubles */
if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE)
vtop->r2 = r+1;
#endif
}
return r;
}
/* generate vtop[-1] and vtop[0] in resp. classes rc1 and rc2 */
ST_FUNC void gv2(int rc1, int rc2)
{
int v;
/* generate more generic register first. But VT_JMP or VT_CMP
values must be generated first in all cases to avoid possible
reload errors */
v = vtop[0].r & VT_VALMASK;
if (v != VT_CMP && (v & ~1) != VT_JMP && rc1 <= rc2) {
vswap();
gv(rc1);
vswap();
gv(rc2);
/* test if reload is needed for first register */
if ((vtop[-1].r & VT_VALMASK) >= VT_CONST) {
vswap();
gv(rc1);
vswap();
}
} else {
gv(rc2);
vswap();
gv(rc1);
vswap();
/* test if reload is needed for first register */
if ((vtop[0].r & VT_VALMASK) >= VT_CONST) {
gv(rc2);
}
}
}
#ifndef TCC_TARGET_ARM64
/* wrapper around RC_FRET to return a register by type */
static int rc_fret(int t)
{
#ifdef TCC_TARGET_X86_64
if (t == VT_LDOUBLE) {
return RC_ST0;
}
#endif
return RC_FRET;
}
#endif
/* wrapper around REG_FRET to return a register by type */
static int reg_fret(int t)
{
#ifdef TCC_TARGET_X86_64
if (t == VT_LDOUBLE) {
return TREG_ST0;
}
#endif
return REG_FRET;
}
#if PTR_SIZE == 4
/* expand 64bit on stack in two ints */
static void lexpand(void)
{
int u, v;
u = vtop->type.t & (VT_DEFSIGN | VT_UNSIGNED);
v = vtop->r & (VT_VALMASK | VT_LVAL);
if (v == VT_CONST) {
vdup();
#if HAVE_LONG_LONG_STUB || HAVE_LONG_LONG
vtop[0].c.i >>= 32;
#endif
} else if (v == (VT_LVAL|VT_CONST) || v == (VT_LVAL|VT_LOCAL)) {
vdup();
vtop[0].c.i += 4;
} else {
gv(RC_INT);
vdup();
vtop[0].r = vtop[-1].r2;
vtop[0].r2 = vtop[-1].r2 = VT_CONST;
}
vtop[0].type.t = vtop[-1].type.t = VT_INT | u;
}
#endif
#ifdef TCC_TARGET_ARM
/* expand long long on stack */
ST_FUNC void lexpand_nr(void)
{
int u,v;
u = vtop->type.t & (VT_DEFSIGN | VT_UNSIGNED);
vdup();
vtop->r2 = VT_CONST;
vtop->type.t = VT_INT | u;
v=vtop[-1].r & (VT_VALMASK | VT_LVAL);
if (v == VT_CONST) {
vtop[-1].c.i = vtop->c.i;
#if HAVE_LONG_LONG_STUB || HAVE_LONG_LONG
vtop->c.i = vtop->c.i >> 32;
#endif
vtop->r = VT_CONST;
} else if (v == (VT_LVAL|VT_CONST) || v == (VT_LVAL|VT_LOCAL)) {
vtop->c.i += 4;
vtop->r = vtop[-1].r;
} else if (v > VT_CONST) {
vtop--;
lexpand();
} else
vtop->r = vtop[-1].r2;
vtop[-1].r2 = VT_CONST;
vtop[-1].type.t = VT_INT | u;
}
#endif
#if PTR_SIZE == 4
/* build a long long from two ints */
static void lbuild(int t)
{
gv2(RC_INT, RC_INT);
vtop[-1].r2 = vtop[0].r;
vtop[-1].type.t = t;
vpop();
}
#endif
/* convert stack entry to register and duplicate its value in another
register */
static void gv_dup(void)
{
int rc, t, r, r1;
SValue sv;
t = vtop->type.t;
#if PTR_SIZE == 4
if ((t & VT_BTYPE) == VT_LLONG) {
lexpand();
gv_dup();
vswap();
vrotb(3);
gv_dup();
vrotb(4);
/* stack: H L L1 H1 */
lbuild(t);
vrotb(3);
vrotb(3);
vswap();
lbuild(t);
vswap();
} else
#endif
{
/* duplicate value */
rc = RC_INT;
sv.type.t = VT_INT;
if (is_float(t)) {
rc = RC_FLOAT;
#ifdef TCC_TARGET_X86_64
if ((t & VT_BTYPE) == VT_LDOUBLE) {
rc = RC_ST0;
}
#endif
sv.type.t = t;
}
r = gv(rc);
r1 = get_reg(rc);
sv.r = r;
sv.c.i = 0;
load(r1, &sv); /* move r to r1 */
vdup();
/* duplicates value */
if (r != r1)
vtop->r = r1;
}
}
/* Generate value test
*
* Generate a test for any value (jump, comparison and integers) */
ST_FUNC int gvtst(int inv, int t)
{
int v = vtop->r & VT_VALMASK;
if (v != VT_CMP && v != VT_JMP && v != VT_JMPI) {
vpushi(0);
gen_op(TOK_NE);
}
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
/* constant jmp optimization */
if ((vtop->c.i != 0) != inv)
t = gjmp(t);
vtop--;
return t;
}
return gtst(inv, t);
}
#if PTR_SIZE == 4
/* generate CPU independent (unsigned) long long operations */
static void gen_opl(int op)
{
int t, a, b, op1, c, i;
int func;
unsigned short reg_iret = REG_IRET;
unsigned short reg_lret = REG_LRET;
SValue tmp;
switch(op) {
case '/':
case TOK_PDIV:
func = TOK___divdi3;
goto gen_func;
case TOK_UDIV:
func = TOK___udivdi3;
goto gen_func;
case '%':
func = TOK___moddi3;
goto gen_mod_func;
case TOK_UMOD:
func = TOK___umoddi3;
gen_mod_func:
#ifdef TCC_ARM_EABI
reg_iret = TREG_R2;
reg_lret = TREG_R3;
#endif
gen_func:
/* call generic long long function */
vpush_global_sym(&func_old_type, func);
vrott(3);
gfunc_call(2);
vpushi(0);
vtop->r = reg_iret;
vtop->r2 = reg_lret;
break;
case '^':
case '&':
case '|':
case '*':
case '+':
case '-':
//pv("gen_opl A",0,2);
t = vtop->type.t;
vswap();
lexpand();
vrotb(3);
lexpand();
/* stack: L1 H1 L2 H2 */
tmp = vtop[0];
vtop[0] = vtop[-3];
vtop[-3] = tmp;
tmp = vtop[-2];
vtop[-2] = vtop[-3];
vtop[-3] = tmp;
vswap();
/* stack: H1 H2 L1 L2 */
//pv("gen_opl B",0,4);
if (op == '*') {
vpushv(vtop - 1);
vpushv(vtop - 1);
gen_op(TOK_UMULL);
lexpand();
/* stack: H1 H2 L1 L2 ML MH */
for(i=0;i<4;i++)
vrotb(6);
/* stack: ML MH H1 H2 L1 L2 */
tmp = vtop[0];
vtop[0] = vtop[-2];
vtop[-2] = tmp;
/* stack: ML MH H1 L2 H2 L1 */
gen_op('*');
vrotb(3);
vrotb(3);
gen_op('*');
/* stack: ML MH M1 M2 */
gen_op('+');
gen_op('+');
} else if (op == '+' || op == '-') {
/* XXX: add non carry method too (for MIPS or alpha) */
if (op == '+')
op1 = TOK_ADDC1;
else
op1 = TOK_SUBC1;
gen_op(op1);
/* stack: H1 H2 (L1 op L2) */
vrotb(3);
vrotb(3);
gen_op(op1 + 1); /* TOK_xxxC2 */
} else {
gen_op(op);
/* stack: H1 H2 (L1 op L2) */
vrotb(3);
vrotb(3);
/* stack: (L1 op L2) H1 H2 */
gen_op(op);
/* stack: (L1 op L2) (H1 op H2) */
}
/* stack: L H */
lbuild(t);
break;
case TOK_SAR:
case TOK_SHR:
case TOK_SHL:
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
t = vtop[-1].type.t;
vswap();
lexpand();
vrotb(3);
/* stack: L H shift */
c = (int)vtop->c.i;
/* constant: simpler */
/* NOTE: all comments are for SHL. the other cases are
done by swapping words */
vpop();
if (op != TOK_SHL)
vswap();
if (c >= 32) {
/* stack: L H */
vpop();
if (c > 32) {
vpushi(c - 32);
gen_op(op);
}
if (op != TOK_SAR) {
vpushi(0);
} else {
gv_dup();
vpushi(31);
gen_op(TOK_SAR);
}
vswap();
} else {
vswap();
gv_dup();
/* stack: H L L */
vpushi(c);
gen_op(op);
vswap();
vpushi(32 - c);
if (op == TOK_SHL)
gen_op(TOK_SHR);
else
gen_op(TOK_SHL);
vrotb(3);
/* stack: L L H */
vpushi(c);
if (op == TOK_SHL)
gen_op(TOK_SHL);
else
gen_op(TOK_SHR);
gen_op('|');
}
if (op != TOK_SHL)
vswap();
lbuild(t);
} else {
/* XXX: should provide a faster fallback on x86 ? */
switch(op) {
case TOK_SAR:
func = TOK___ashrdi3;
goto gen_func;
case TOK_SHR:
func = TOK___lshrdi3;
goto gen_func;
case TOK_SHL:
func = TOK___ashldi3;
goto gen_func;
}
}
break;
default:
/* compare operations */
t = vtop->type.t;
vswap();
lexpand();
vrotb(3);
lexpand();
/* stack: L1 H1 L2 H2 */
tmp = vtop[-1];
vtop[-1] = vtop[-2];
vtop[-2] = tmp;
/* stack: L1 L2 H1 H2 */
/* compare high */
op1 = op;
/* when values are equal, we need to compare low words. since
the jump is inverted, we invert the test too. */
if (op1 == TOK_LT)
op1 = TOK_LE;
else if (op1 == TOK_GT)
op1 = TOK_GE;
else if (op1 == TOK_ULT)
op1 = TOK_ULE;
else if (op1 == TOK_UGT)
op1 = TOK_UGE;
a = 0;
b = 0;
gen_op(op1);
if (op == TOK_NE) {
b = gvtst(0, 0);
} else {
a = gvtst(1, 0);
if (op != TOK_EQ) {
/* generate non equal test */
vpushi(TOK_NE);
vtop->r = VT_CMP;
b = gvtst(0, 0);
}
}
/* compare low. Always unsigned */
op1 = op;
if (op1 == TOK_LT)
op1 = TOK_ULT;
else if (op1 == TOK_LE)
op1 = TOK_ULE;
else if (op1 == TOK_GT)
op1 = TOK_UGT;
else if (op1 == TOK_GE)
op1 = TOK_UGE;
gen_op(op1);
a = gvtst(1, a);
gsym(b);
vseti(VT_JMPI, a);
break;
}
}
#endif
#if HAVE_LONG_LONG_STUB || HAVE_LONG_LONG
static uint64_t gen_opic_sdiv(uint64_t a, uint64_t b)
{
#if HAVE_LONG_LONG
uint64_t x = (a >> 63 ? -a : a) / (b >> 63 ? -b : b);
return (a ^ b) >> 63 ? -x : x;
#else
uint32_t x = (a >> 31 ? -a : a) / (b >> 31 ? -b : b);
return (a ^ b) >> 31 ? -x : x;
#endif
}
static int gen_opic_lt(uint64_t a, uint64_t b)
{
#if HAVE_LONG_LONG
return (a ^ (uint64_t)1 << 63) < (b ^ (uint64_t)1 << 63);
#else
return (a ^ (uint32_t)1 << 31) < (b ^ (uint32_t)1 << 31);
#endif
}
#else
static uint32_t gen_opic_sdiv(uint32_t a, uint32_t b)
{
uint32_t x = (a >> 31 ? -a : a) / (b >> 31 ? -b : b);
return (a ^ b) >> 31 ? -x : x;
}
static int gen_opic_lt(uint32_t a, uint32_t b)
{
return (a ^ (uint32_t)1 << 31) < (b ^ (uint32_t)1 << 31);
}
#endif
/* handle integer constant optimizations and various machine
independent opt */
static void gen_opic(int op)
{
SValue *v1 = vtop - 1;
SValue *v2 = vtop;
int t1 = v1->type.t & VT_BTYPE;
int t2 = v2->type.t & VT_BTYPE;
int c1 = (v1->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
int c2 = (v2->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
#if HAVE_LONG_LONG
uint64_t l1 = c1 ? v1->c.i : 0;
uint64_t l2 = c2 ? v2->c.i : 0;
int shm = (t1 == VT_LLONG) ? 63 : 31;
#else
uint32_t l1 = c1 ? v1->c.i : 0;
uint32_t l2 = c2 ? v2->c.i : 0;
int shm = 31;
#endif
if (t1 != VT_LLONG && (PTR_SIZE != 8 || t1 != VT_PTR))
l1 = ((uint32_t)l1 |
(v1->type.t & VT_UNSIGNED ? 0 : -(l1 & 0x80000000)));
if (t2 != VT_LLONG && (PTR_SIZE != 8 || t2 != VT_PTR))
l2 = ((uint32_t)l2 |
(v2->type.t & VT_UNSIGNED ? 0 : -(l2 & 0x80000000)));
if (c1 && c2) {
switch(op) {
case '+': l1 += l2; break;
case '-': l1 -= l2; break;
case '&': l1 &= l2; break;
case '^': l1 ^= l2; break;
case '|': l1 |= l2; break;
case '*': l1 *= l2; break;
case TOK_PDIV:
case '/':
case '%':
case TOK_UDIV:
case TOK_UMOD:
/* if division by zero, generate explicit division */
if (l2 == 0) {
if (const_wanted)
tcc_error("division by zero in constant");
goto general_case;
}
switch(op) {
default: l1 = gen_opic_sdiv(l1, l2); break;
case '%': l1 = l1 - l2 * gen_opic_sdiv(l1, l2); break;
case TOK_UDIV: l1 = l1 / l2; break;
case TOK_UMOD: l1 = l1 % l2; break;
}
break;
case TOK_SHL: l1 <<= (l2 & shm); break;
case TOK_SHR: l1 >>= (l2 & shm); break;
case TOK_SAR:
#if HAVE_LONG_LONG
l1 = (l1 >> 63) ? ~(~l1 >> (l2 & shm)) : l1 >> (l2 & shm);
#else
l1 = (l1 >> 31) ? ~(~l1 >> (l2 & shm)) : l1 >> (l2 & shm);
#endif
break;
/* tests */
case TOK_ULT: l1 = l1 < l2; break;
case TOK_UGE: l1 = l1 >= l2; break;
case TOK_EQ: l1 = l1 == l2; break;
case TOK_NE: l1 = l1 != l2; break;
case TOK_ULE: l1 = l1 <= l2; break;
case TOK_UGT: l1 = l1 > l2; break;
case TOK_LT: l1 = gen_opic_lt(l1, l2); break;
case TOK_GE: l1 = !gen_opic_lt(l1, l2); break;
case TOK_LE: l1 = !gen_opic_lt(l2, l1); break;
case TOK_GT: l1 = gen_opic_lt(l2, l1); break;
/* logical */
case TOK_LAND: l1 = l1 && l2; break;
case TOK_LOR: l1 = l1 || l2; break;
default:
goto general_case;
}
if (t1 != VT_LLONG && (PTR_SIZE != 8 || t1 != VT_PTR))
l1 = ((uint32_t)l1 |
(v1->type.t & VT_UNSIGNED ? 0 : -(l1 & 0x80000000)));
v1->c.i = l1;
vtop--;
} else {
/* if commutative ops, put c2 as constant */
if (c1 && (op == '+' || op == '&' || op == '^' ||
op == '|' || op == '*')) {
vswap();
c2 = c1; //c = c1, c1 = c2, c2 = c;
l2 = l1; //l = l1, l1 = l2, l2 = l;
}
if (!const_wanted &&
c1 && ((l1 == 0 &&
(op == TOK_SHL || op == TOK_SHR || op == TOK_SAR)) ||
(l1 == -1 && op == TOK_SAR))) {
/* treat (0 << x), (0 >> x) and (-1 >> x) as constant */
vtop--;
} else if (!const_wanted &&
c2 && ((l2 == 0 && (op == '&' || op == '*')) ||
(l2 == -1 && op == '|') ||
(l2 == 0xffffffff && t2 != VT_LLONG && op == '|') ||
(l2 == 1 && (op == '%' || op == TOK_UMOD)))) {
/* treat (x & 0), (x * 0), (x | -1) and (x % 1) as constant */
if (l2 == 1)
vtop->c.i = 0;
vswap();
vtop--;
} else if (c2 && (((op == '*' || op == '/' || op == TOK_UDIV ||
op == TOK_PDIV) &&
l2 == 1) ||
((op == '+' || op == '-' || op == '|' || op == '^' ||
op == TOK_SHL || op == TOK_SHR || op == TOK_SAR) &&
l2 == 0) ||
(op == '&' &&
l2 == -1))) {
/* filter out NOP operations like x*1, x-0, x&-1... */
vtop--;
} else if (c2 && (op == '*' || op == TOK_PDIV || op == TOK_UDIV)) {
/* try to use shifts instead of muls or divs */
if (l2 > 0 && (l2 & (l2 - 1)) == 0) {
int n = -1;
while (l2) {
l2 >>= 1;
n++;
}
vtop->c.i = n;
if (op == '*')
op = TOK_SHL;
else if (op == TOK_PDIV)
op = TOK_SAR;
else
op = TOK_SHR;
}
goto general_case;
} else if (c2 && (op == '+' || op == '-') &&
(((vtop[-1].r & (VT_VALMASK | VT_LVAL | VT_SYM)) == (VT_CONST | VT_SYM))
|| (vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_LOCAL)) {
/* symbol + constant case */
if (op == '-')
l2 = -l2;
l2 += vtop[-1].c.i;
/* The backends can't always deal with addends to symbols
larger than +-1<<31. Don't construct such. */
if ((int)l2 != l2)
goto general_case;
vtop--;
vtop->c.i = l2;
} else {
general_case:
/* call low level op generator */
if (t1 == VT_LLONG || t2 == VT_LLONG ||
(PTR_SIZE == 8 && (t1 == VT_PTR || t2 == VT_PTR)))
gen_opl(op);
else
gen_opi(op);
}
}
}
/* generate a floating point operation with constant propagation */
static void gen_opif(int op)
{
int c1, c2;
SValue *v1, *v2;
long double f1, f2;
v1 = vtop - 1;
v2 = vtop;
/* currently, we cannot do computations with forward symbols */
c1 = (v1->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
c2 = (v2->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
if (c1 && c2) {
if (v1->type.t == VT_FLOAT) {
f1 = v1->c.f;
f2 = v2->c.f;
} else if (v1->type.t == VT_DOUBLE) {
f1 = v1->c.d;
f2 = v2->c.d;
} else {
f1 = v1->c.ld;
f2 = v2->c.ld;
}
/* NOTE: we only do constant propagation if finite number (not
NaN or infinity) (ANSI spec) */
if (!ieee_finite(f1) || !ieee_finite(f2))
goto general_case;
switch(op) {
case '+': f1 += f2; break;
case '-': f1 -= f2; break;
case '*': f1 *= f2; break;
case '/':
#if HAVE_FLOAT
if (f2 == 0.0) {
if (const_wanted)
tcc_error("division by zero in constant");
goto general_case;
}
#endif
f1 /= f2;
break;
/* XXX: also handles tests ? */
default:
goto general_case;
}
/* XXX: overflow test ? */
if (v1->type.t == VT_FLOAT) {
v1->c.f = f1;
} else if (v1->type.t == VT_DOUBLE) {
v1->c.d = f1;
} else {
v1->c.ld = f1;
}
vtop--;
} else {
general_case:
gen_opf(op);
}
}
static int pointed_size(CType *type)
{
int align;
return type_size(pointed_type(type), &align);
}
static void vla_runtime_pointed_size(CType *type)
{
int align;
vla_runtime_type_size(pointed_type(type), &align);
}
static inline int is_null_pointer(SValue *p)
{
if ((p->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST)
return 0;
return ((p->type.t & VT_BTYPE) == VT_INT && (uint32_t)p->c.i == 0) ||
((p->type.t & VT_BTYPE) == VT_LLONG && p->c.i == 0) ||
((p->type.t & VT_BTYPE) == VT_PTR &&
(PTR_SIZE == 4 ? (uint32_t)p->c.i == 0 : p->c.i == 0));
}
static inline int is_integer_btype(int bt)
{
return (bt == VT_BYTE || bt == VT_SHORT ||
bt == VT_INT || bt == VT_LLONG);
}
/* check types for comparison or subtraction of pointers */
static void check_comparison_pointer_types(SValue *p1, SValue *p2, int op)
{
CType *type1, *type2, tmp_type1, tmp_type2;
int bt1, bt2;
/* null pointers are accepted for all comparisons as gcc */
if (is_null_pointer(p1) || is_null_pointer(p2))
return;
type1 = &p1->type;
type2 = &p2->type;
bt1 = type1->t & VT_BTYPE;
bt2 = type2->t & VT_BTYPE;
/* accept comparison between pointer and integer with a warning */
if ((is_integer_btype(bt1) || is_integer_btype(bt2)) && op != '-') {
if (op != TOK_LOR && op != TOK_LAND )
tcc_warning("comparison between pointer and integer");
return;
}
/* both must be pointers or implicit function pointers */
if (bt1 == VT_PTR) {
type1 = pointed_type(type1);
} else if (bt1 != VT_FUNC)
goto invalid_operands;
if (bt2 == VT_PTR) {
type2 = pointed_type(type2);
} else if (bt2 != VT_FUNC) {
invalid_operands:
tcc_error("invalid operands to binary %s", get_tok_str(op, NULL));
}
if ((type1->t & VT_BTYPE) == VT_VOID ||
(type2->t & VT_BTYPE) == VT_VOID)
return;
tmp_type1 = *type1;
tmp_type2 = *type2;
tmp_type1.t &= ~(VT_DEFSIGN | VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE);
tmp_type2.t &= ~(VT_DEFSIGN | VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE);
if (!is_compatible_types(&tmp_type1, &tmp_type2)) {
/* gcc-like error if '-' is used */
if (op == '-')
goto invalid_operands;
else
tcc_warning("comparison of distinct pointer types lacks a cast");
}
}
/* generic gen_op: handles types problems */
ST_FUNC void gen_op(int op)
{
int u, t1, t2, bt1, bt2, t;
CType type1;
redo:
t1 = vtop[-1].type.t;
t2 = vtop[0].type.t;
bt1 = t1 & VT_BTYPE;
bt2 = t2 & VT_BTYPE;
if (bt1 == VT_STRUCT || bt2 == VT_STRUCT) {
tcc_error("operation on a struct");
} else if (bt1 == VT_FUNC || bt2 == VT_FUNC) {
if (bt2 == VT_FUNC) {
mk_pointer(&vtop->type);
gaddrof();
}
if (bt1 == VT_FUNC) {
vswap();
mk_pointer(&vtop->type);
gaddrof();
vswap();
}
goto redo;
} else if (bt1 == VT_PTR || bt2 == VT_PTR) {
/* at least one operand is a pointer */
/* relational op: must be both pointers */
if (op >= TOK_ULT && op <= TOK_LOR) {
check_comparison_pointer_types(vtop - 1, vtop, op);
/* pointers are handled are unsigned */
#if PTR_SIZE == 8
t = VT_LLONG | VT_UNSIGNED;
#else
t = VT_INT | VT_UNSIGNED;
#endif
goto std_op;
}
/* if both pointers, then it must be the '-' op */
if (bt1 == VT_PTR && bt2 == VT_PTR) {
if (op != '-')
tcc_error("cannot use pointers here");
check_comparison_pointer_types(vtop - 1, vtop, op);
/* XXX: check that types are compatible */
if (vtop[-1].type.t & VT_VLA) {
vla_runtime_pointed_size(&vtop[-1].type);
} else {
vpushi(pointed_size(&vtop[-1].type));
}
vrott(3);
gen_opic(op);
/* set to integer type */
#if PTR_SIZE == 8
vtop->type.t = VT_LLONG;
#else
vtop->type.t = VT_INT;
#endif
vswap();
gen_op(TOK_PDIV);
} else {
/* exactly one pointer : must be '+' or '-'. */
if (op != '-' && op != '+')
tcc_error("cannot use pointers here");
/* Put pointer as first operand */
if (bt2 == VT_PTR) {
vswap();
t = t1, t1 = t2, t2 = t;
}
#if PTR_SIZE == 4
if ((vtop[0].type.t & VT_BTYPE) == VT_LLONG)
/* XXX: truncate here because gen_opl can't handle ptr + long long */
gen_cast(&int_type);
#endif
type1 = vtop[-1].type;
type1.t &= ~VT_ARRAY;
if (vtop[-1].type.t & VT_VLA)
vla_runtime_pointed_size(&vtop[-1].type);
else {
u = pointed_size(&vtop[-1].type);
if (u < 0)
tcc_error("unknown array element size");
#if PTR_SIZE == 8
vpushll(u);
#else
/* XXX: cast to int ? (long long case) */
vpushi(u);
#endif
}
gen_op('*');
#if 0
/* #ifdef CONFIG_TCC_BCHECK
The main reason to removing this code:
#include <stdio.h>
int main ()
{
int v[10];
int i = 10;
int j = 9;
fprintf(stderr, "v+i-j = %p\n", v+i-j);
fprintf(stderr, "v+(i-j) = %p\n", v+(i-j));
}
When this code is on. then the output looks like
v+i-j = 0xfffffffe
v+(i-j) = 0xbff84000
*/
/* if evaluating constant expression, no code should be
generated, so no bound check */
if (tcc_state->do_bounds_check && !const_wanted) {
/* if bounded pointers, we generate a special code to
test bounds */
if (op == '-') {
vpushi(0);
vswap();
gen_op('-');
}
gen_bounded_ptr_add();
} else
#endif
{
gen_opic(op);
}
/* put again type if gen_opic() swaped operands */
vtop->type = type1;
}
} else if (is_float(bt1) || is_float(bt2)) {
/* compute bigger type and do implicit casts */
if (bt1 == VT_LDOUBLE || bt2 == VT_LDOUBLE) {
t = VT_LDOUBLE;
} else if (bt1 == VT_DOUBLE || bt2 == VT_DOUBLE) {
t = VT_DOUBLE;
} else {
t = VT_FLOAT;
}
/* floats can only be used for a few operations */
if (op != '+' && op != '-' && op != '*' && op != '/' &&
(op < TOK_ULT || op > TOK_GT))
tcc_error("invalid operands for binary operation");
goto std_op;
} else if (op == TOK_SHR || op == TOK_SAR || op == TOK_SHL) {
t = bt1 == VT_LLONG ? VT_LLONG : VT_INT;
if ((t1 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (t | VT_UNSIGNED))
t |= VT_UNSIGNED;
goto std_op;
} else if (bt1 == VT_LLONG || bt2 == VT_LLONG) {
/* cast to biggest op */
t = VT_LLONG;
/* convert to unsigned if it does not fit in a long long */
if ((t1 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_LLONG | VT_UNSIGNED) ||
(t2 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_LLONG | VT_UNSIGNED))
t |= VT_UNSIGNED;
goto std_op;
} else {
/* integer operations */
t = VT_INT;
/* convert to unsigned if it does not fit in an integer */
if ((t1 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_INT | VT_UNSIGNED) ||
(t2 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_INT | VT_UNSIGNED))
t |= VT_UNSIGNED;
std_op:
/* XXX: currently, some unsigned operations are explicit, so
we modify them here */
if (t & VT_UNSIGNED) {
if (op == TOK_SAR)
op = TOK_SHR;
else if (op == '/')
op = TOK_UDIV;
else if (op == '%')
op = TOK_UMOD;
else if (op == TOK_LT)
op = TOK_ULT;
else if (op == TOK_GT)
op = TOK_UGT;
else if (op == TOK_LE)
op = TOK_ULE;
else if (op == TOK_GE)
op = TOK_UGE;
}
vswap();
type1.t = t;
gen_cast(&type1);
vswap();
/* special case for shifts and long long: we keep the shift as
an integer */
if (op == TOK_SHR || op == TOK_SAR || op == TOK_SHL)
type1.t = VT_INT;
gen_cast(&type1);
if (is_float(t))
gen_opif(op);
else
gen_opic(op);
if (op >= TOK_ULT && op <= TOK_GT) {
/* relational op: the result is an int */
vtop->type.t = VT_INT;
} else {
vtop->type.t = t;
}
}
// Make sure that we have converted to an rvalue:
if (vtop->r & VT_LVAL)
gv(is_float(vtop->type.t & VT_BTYPE) ? RC_FLOAT : RC_INT);
}
#ifndef TCC_TARGET_ARM
/* generic itof for unsigned long long case */
static void gen_cvt_itof1(int t)
{
#ifdef TCC_TARGET_ARM64
gen_cvt_itof(t);
#else
if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) ==
(VT_LLONG | VT_UNSIGNED)) {
if (t == VT_FLOAT)
vpush_global_sym(&func_old_type, TOK___floatundisf);
#if LDOUBLE_SIZE != 8
else if (t == VT_LDOUBLE)
vpush_global_sym(&func_old_type, TOK___floatundixf);
#endif
else
vpush_global_sym(&func_old_type, TOK___floatundidf);
vrott(2);
gfunc_call(1);
vpushi(0);
vtop->r = reg_fret(t);
} else {
gen_cvt_itof(t);
}
#endif
}
#endif
/* generic ftoi for unsigned long long case */
static void gen_cvt_ftoi1(int t)
{
#ifdef TCC_TARGET_ARM64
gen_cvt_ftoi(t);
#else
int st;
if (t == (VT_LLONG | VT_UNSIGNED)) {
/* not handled natively */
st = vtop->type.t & VT_BTYPE;
if (st == VT_FLOAT)
vpush_global_sym(&func_old_type, TOK___fixunssfdi);
#if LDOUBLE_SIZE != 8
else if (st == VT_LDOUBLE)
vpush_global_sym(&func_old_type, TOK___fixunsxfdi);
#endif
else
vpush_global_sym(&func_old_type, TOK___fixunsdfdi);
vrott(2);
gfunc_call(1);
vpushi(0);
vtop->r = REG_IRET;
vtop->r2 = REG_LRET;
} else {
gen_cvt_ftoi(t);
}
#endif
}
/* force char or short cast */
static void force_charshort_cast(int t)
{
int bits, dbt;
dbt = t & VT_BTYPE;
/* XXX: add optimization if lvalue : just change type and offset */
if (dbt == VT_BYTE)
bits = 8;
else
bits = 16;
if (t & VT_UNSIGNED) {
vpushi((1 << bits) - 1);
gen_op('&');
} else {
if ((vtop->type.t & VT_BTYPE) == VT_LLONG)
bits = 64 - bits;
else
bits = 32 - bits;
vpushi(bits);
gen_op(TOK_SHL);
/* result must be signed or the SAR is converted to an SHL
This was not the case when "t" was a signed short
and the last value on the stack was an unsigned int */
vtop->type.t &= ~VT_UNSIGNED;
vpushi(bits);
gen_op(TOK_SAR);
}
}
/* cast 'vtop' to 'type'. Casting to bitfields is forbidden. */
static void gen_cast(CType *type)
{
int sbt, dbt, sf, df, c, p;
/* special delayed cast for char/short */
/* XXX: in some cases (multiple cascaded casts), it may still
be incorrect */
if (vtop->r & VT_MUSTCAST) {
vtop->r &= ~VT_MUSTCAST;
force_charshort_cast(vtop->type.t);
}
/* bitfields first get cast to ints */
if (vtop->type.t & VT_BITFIELD) {
gv(RC_INT);
}
dbt = type->t & (VT_BTYPE | VT_UNSIGNED);
sbt = vtop->type.t & (VT_BTYPE | VT_UNSIGNED);
if (sbt != dbt) {
sf = is_float(sbt);
df = is_float(dbt);
c = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
p = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == (VT_CONST | VT_SYM);
if (c) {
/* constant case: we can do it now */
/* XXX: in ISOC, cannot do it if error in convert */
#if HAVE_FLOAT
if (sbt == VT_FLOAT)
vtop->c.ld = vtop->c.f;
else if (sbt == VT_DOUBLE)
vtop->c.ld = vtop->c.d;
if (df) {
if ((sbt & VT_BTYPE) == VT_LLONG) {
if ((sbt & VT_UNSIGNED) || !(vtop->c.i >> 63))
vtop->c.ld = vtop->c.i;
else
vtop->c.ld = -(long double)-vtop->c.i;
} else if(!sf) {
if ((sbt & VT_UNSIGNED) || !(vtop->c.i >> 31))
vtop->c.ld = (uint32_t)vtop->c.i;
else
vtop->c.ld = -(long double)-(uint32_t)vtop->c.i;
}
if (dbt == VT_FLOAT)
vtop->c.f = (float)vtop->c.ld;
else if (dbt == VT_DOUBLE)
vtop->c.d = (double)vtop->c.ld;
}
else if (sf && dbt == (VT_LLONG|VT_UNSIGNED)) {
vtop->c.i = vtop->c.ld;
} else if (sf && dbt == VT_BOOL) {
vtop->c.i = (vtop->c.ld != 0);
} else
#endif
{
#if HAVE_FLOAT
if(sf)
vtop->c.i = vtop->c.ld;
else
#endif
if (sbt == (VT_LLONG|VT_UNSIGNED))
;
else if (sbt & VT_UNSIGNED)
vtop->c.i = (uint32_t)vtop->c.i;
#if PTR_SIZE == 8
else if (sbt == VT_PTR)
;
#endif
else if (sbt != VT_LLONG)
vtop->c.i = ((uint32_t)vtop->c.i |
-(vtop->c.i & 0x80000000));
if (dbt == (VT_LLONG|VT_UNSIGNED))
;
else if (dbt == VT_BOOL)
vtop->c.i = (vtop->c.i != 0);
#if PTR_SIZE == 8
else if (dbt == VT_PTR)
;
#endif
else if (dbt != VT_LLONG) {
uint32_t m = ((dbt & VT_BTYPE) == VT_BYTE ? 0xff :
(dbt & VT_BTYPE) == VT_SHORT ? 0xffff :
0xffffffff);
vtop->c.i &= m;
if (!(dbt & VT_UNSIGNED))
vtop->c.i |= -(vtop->c.i & ((m >> 1) + 1));
}
}
} else if (p && dbt == VT_BOOL) {
vtop->r = VT_CONST;
vtop->c.i = 1;
} else {
/* non constant case: generate code */
if (sf && df) {
/* convert from fp to fp */
gen_cvt_ftof(dbt);
} else if (df) {
/* convert int to fp */
gen_cvt_itof1(dbt);
} else if (sf) {
/* convert fp to int */
if (dbt == VT_BOOL) {
vpushi(0);
gen_op(TOK_NE);
} else {
/* we handle char/short/etc... with generic code */
if (dbt != (VT_INT | VT_UNSIGNED) &&
dbt != (VT_LLONG | VT_UNSIGNED) &&
dbt != VT_LLONG)
dbt = VT_INT;
gen_cvt_ftoi1(dbt);
if (dbt == VT_INT && (type->t & (VT_BTYPE | VT_UNSIGNED)) != dbt) {
/* additional cast for char/short... */
vtop->type.t = dbt;
gen_cast(type);
}
}
#if PTR_SIZE == 4
} else if ((dbt & VT_BTYPE) == VT_LLONG) {
if ((sbt & VT_BTYPE) != VT_LLONG) {
/* scalar to long long */
/* machine independent conversion */
gv(RC_INT);
/* generate high word */
if (sbt == (VT_INT | VT_UNSIGNED)) {
vpushi(0);
gv(RC_INT);
} else {
if (sbt == VT_PTR) {
/* cast from pointer to int before we apply
shift operation, which pointers don't support*/
gen_cast(&int_type);
}
gv_dup();
vpushi(31);
gen_op(TOK_SAR);
}
/* patch second register */
vtop[-1].r2 = vtop->r;
vpop();
}
#else
} else if ((dbt & VT_BTYPE) == VT_LLONG ||
(dbt & VT_BTYPE) == VT_PTR ||
(dbt & VT_BTYPE) == VT_FUNC) {
if ((sbt & VT_BTYPE) != VT_LLONG &&
(sbt & VT_BTYPE) != VT_PTR &&
(sbt & VT_BTYPE) != VT_FUNC) {
/* need to convert from 32bit to 64bit */
gv(RC_INT);
if (sbt != (VT_INT | VT_UNSIGNED)) {
#if defined(TCC_TARGET_ARM64)
gen_cvt_sxtw();
#elif defined(TCC_TARGET_X86_64)
int r = gv(RC_INT);
/* x86_64 specific: movslq */
o(0x6348);
o(0xc0 + (REG_VALUE(r) << 3) + REG_VALUE(r));
#else
#error
#endif
}
}
#endif
} else if (dbt == VT_BOOL) {
/* scalar to bool */
vpushi(0);
gen_op(TOK_NE);
} else if ((dbt & VT_BTYPE) == VT_BYTE ||
(dbt & VT_BTYPE) == VT_SHORT) {
if (sbt == VT_PTR) {
vtop->type.t = VT_INT;
tcc_warning("nonportable conversion from pointer to char/short");
}
force_charshort_cast(dbt);
#if PTR_SIZE == 4
} else if ((dbt & VT_BTYPE) == VT_INT) {
/* scalar to int */
if ((sbt & VT_BTYPE) == VT_LLONG) {
/* from long long: just take low order word */
lexpand();
vpop();
}
/* if lvalue and single word type, nothing to do because
the lvalue already contains the real type size (see
VT_LVAL_xxx constants) */
#endif
}
}
} else if ((dbt & VT_BTYPE) == VT_PTR && !(vtop->r & VT_LVAL)) {
/* if we are casting between pointer types,
we must update the VT_LVAL_xxx size */
vtop->r = (vtop->r & ~VT_LVAL_TYPE)
| (lvalue_type(type->ref->type.t) & VT_LVAL_TYPE);
}
vtop->type = *type;
}
/* return type size as known at compile time. Put alignment at 'a' */
ST_FUNC int type_size(CType *type, int *a)
{
Sym *s;
int bt;
bt = type->t & VT_BTYPE;
if (bt == VT_STRUCT) {
/* struct/union */
s = type->ref;
*a = s->r;
return s->c;
} else if (bt == VT_PTR) {
if (type->t & VT_ARRAY) {
int ts;
s = type->ref;
ts = type_size(&s->type, a);
if (ts < 0 && s->c < 0)
ts = -ts;
return ts * s->c;
} else {
*a = PTR_SIZE;
return PTR_SIZE;
}
} else if (bt == VT_LDOUBLE) {
*a = LDOUBLE_ALIGN;
return LDOUBLE_SIZE;
} else if (bt == VT_DOUBLE || bt == VT_LLONG) {
#ifdef TCC_TARGET_I386
#ifdef TCC_TARGET_PE
*a = 8;
#else
*a = 4;
#endif
#elif defined(TCC_TARGET_ARM)
#ifdef TCC_ARM_EABI
*a = 8;
#else
*a = 4;
#endif
#else
*a = 8;
#endif
return 8;
} else if (bt == VT_INT || bt == VT_FLOAT) {
*a = 4;
return 4;
} else if (bt == VT_SHORT) {
*a = 2;
return 2;
} else if (bt == VT_QLONG || bt == VT_QFLOAT) {
*a = 8;
return 16;
} else if (bt == VT_ENUM) {
*a = 4;
/* Enums might be incomplete, so don't just return '4' here. */
return type->ref->c;
} else {
/* char, void, function, _Bool */
*a = 1;
return 1;
}
}
/* push type size as known at runtime time on top of value stack. Put
alignment at 'a' */
ST_FUNC void vla_runtime_type_size(CType *type, int *a)
{
if (type->t & VT_VLA) {
type_size(&type->ref->type, a);
vset(&int_type, VT_LOCAL|VT_LVAL, type->ref->c);
} else {
vpushi(type_size(type, a));
}
}
static void vla_sp_restore(void) {
if (vlas_in_scope) {
gen_vla_sp_restore(vla_sp_loc);
}
}
static void vla_sp_restore_root(void) {
if (vlas_in_scope) {
gen_vla_sp_restore(vla_sp_root_loc);
}
}
/* return the pointed type of t */
static inline CType *pointed_type(CType *type)
{
return &type->ref->type;
}
/* modify type so that its it is a pointer to type. */
ST_FUNC void mk_pointer(CType *type)
{
Sym *s;
s = sym_push(SYM_FIELD, type, 0, -1);
type->t = VT_PTR | (type->t & ~VT_TYPE);
type->ref = s;
}
/* compare function types. OLD functions match any new functions */
static int is_compatible_func(CType *type1, CType *type2)
{
Sym *s1, *s2;
s1 = type1->ref;
s2 = type2->ref;
if (!is_compatible_types(&s1->type, &s2->type))
return 0;
/* check func_call */
if (s1->a.func_call != s2->a.func_call)
return 0;
/* XXX: not complete */
if (s1->c == FUNC_OLD || s2->c == FUNC_OLD)
return 1;
if (s1->c != s2->c)
return 0;
while (s1 != NULL) {
if (s2 == NULL)
return 0;
if (!is_compatible_parameter_types(&s1->type, &s2->type))
return 0;
s1 = s1->next;
s2 = s2->next;
}
if (s2)
return 0;
return 1;
}
/* return true if type1 and type2 are the same. If unqualified is
true, qualifiers on the types are ignored.
- enums are not checked as gcc __builtin_types_compatible_p ()
*/
static int compare_types(CType *type1, CType *type2, int unqualified)
{
int bt1, t1, t2;
t1 = type1->t & VT_TYPE;
t2 = type2->t & VT_TYPE;
if (unqualified) {
/* strip qualifiers before comparing */
t1 &= ~(VT_CONSTANT | VT_VOLATILE);
t2 &= ~(VT_CONSTANT | VT_VOLATILE);
}
/* Default Vs explicit signedness only matters for char */
if ((t1 & VT_BTYPE) != VT_BYTE) {
t1 &= ~VT_DEFSIGN;
t2 &= ~VT_DEFSIGN;
}
/* An enum is compatible with (unsigned) int. Ideally we would
store the enums signedness in type->ref.a.<some_bit> and
only accept unsigned enums with unsigned int and vice versa.
But one of our callers (gen_assign_cast) always strips VT_UNSIGNED
from pointer target types, so we can't add it here either. */
if ((t1 & VT_BTYPE) == VT_ENUM) {
t1 = VT_INT;
if (type1->ref->a.unsigned_enum)
t1 |= VT_UNSIGNED;
}
if ((t2 & VT_BTYPE) == VT_ENUM) {
t2 = VT_INT;
if (type2->ref->a.unsigned_enum)
t2 |= VT_UNSIGNED;
}
/* XXX: bitfields ? */
if (t1 != t2)
return 0;
/* test more complicated cases */
bt1 = t1 & VT_BTYPE;
if (bt1 == VT_PTR) {
type1 = pointed_type(type1);
type2 = pointed_type(type2);
return is_compatible_types(type1, type2);
} else if (bt1 == VT_STRUCT) {
return (type1->ref == type2->ref);
} else if (bt1 == VT_FUNC) {
return is_compatible_func(type1, type2);
} else {
return 1;
}
}
/* return true if type1 and type2 are exactly the same (including
qualifiers).
*/
static int is_compatible_types(CType *type1, CType *type2)
{
return compare_types(type1,type2,0);
}
/* return true if type1 and type2 are the same (ignoring qualifiers).
*/
static int is_compatible_parameter_types(CType *type1, CType *type2)
{
return compare_types(type1,type2,1);
}
/* print a type. If 'varstr' is not NULL, then the variable is also
printed in the type */
/* XXX: union */
/* XXX: add array and function pointers */
static void type_to_str(char *buf, int buf_size,
CType *type, const char *varstr)
{
int bt, v, t;
Sym *s, *sa;
char buf1[256];
const char *tstr;
t = type->t;
bt = t & VT_BTYPE;
buf[0] = '\0';
if (t & VT_CONSTANT)
pstrcat(buf, buf_size, "const ");
if (t & VT_VOLATILE)
pstrcat(buf, buf_size, "volatile ");
if ((t & (VT_DEFSIGN | VT_UNSIGNED)) == (VT_DEFSIGN | VT_UNSIGNED))
pstrcat(buf, buf_size, "unsigned ");
else if (t & VT_DEFSIGN)
pstrcat(buf, buf_size, "signed ");
if (t & VT_EXTERN)
pstrcat(buf, buf_size, "extern ");
if (t & VT_STATIC)
pstrcat(buf, buf_size, "static ");
if (t & VT_TYPEDEF)
pstrcat(buf, buf_size, "typedef ");
if (t & VT_INLINE)
pstrcat(buf, buf_size, "inline ");
buf_size -= strlen(buf);
buf += strlen(buf);
switch(bt) {
case VT_VOID:
tstr = "void";
goto add_tstr;
case VT_BOOL:
tstr = "_Bool";
goto add_tstr;
case VT_BYTE:
tstr = "char";
goto add_tstr;
case VT_SHORT:
tstr = "short";
goto add_tstr;
case VT_INT:
tstr = "int";
goto add_tstr;
case VT_LONG:
tstr = "long";
goto add_tstr;
case VT_LLONG:
tstr = "long long";
goto add_tstr;
case VT_FLOAT:
tstr = "float";
goto add_tstr;
case VT_DOUBLE:
tstr = "double";
goto add_tstr;
case VT_LDOUBLE:
tstr = "long double";
add_tstr:
pstrcat(buf, buf_size, tstr);
break;
case VT_ENUM:
case VT_STRUCT:
if (bt == VT_STRUCT)
tstr = "struct ";
else
tstr = "enum ";
pstrcat(buf, buf_size, tstr);
v = type->ref->v & ~SYM_STRUCT;
if (v >= SYM_FIRST_ANOM)
pstrcat(buf, buf_size, "<anonymous>");
else
pstrcat(buf, buf_size, get_tok_str(v, NULL));
break;
case VT_FUNC:
s = type->ref;
type_to_str(buf, buf_size, &s->type, varstr);
pstrcat(buf, buf_size, "(");
sa = s->next;
while (sa != NULL) {
type_to_str(buf1, sizeof(buf1), &sa->type, NULL);
pstrcat(buf, buf_size, buf1);
sa = sa->next;
if (sa)
pstrcat(buf, buf_size, ", ");
}
pstrcat(buf, buf_size, ")");
goto no_var;
case VT_PTR:
s = type->ref;
if (t & VT_ARRAY) {
snprintf(buf1, sizeof(buf1), "%s[%ld]", varstr ? varstr : "", s->c);
type_to_str(buf, buf_size, &s->type, buf1);
goto no_var;
}
pstrcpy(buf1, sizeof(buf1), "*");
if (t & VT_CONSTANT)
pstrcat(buf1, buf_size, "const ");
if (t & VT_VOLATILE)
pstrcat(buf1, buf_size, "volatile ");
if (varstr)
pstrcat(buf1, sizeof(buf1), varstr);
type_to_str(buf, buf_size, &s->type, buf1);
goto no_var;
}
if (varstr) {
pstrcat(buf, buf_size, " ");
pstrcat(buf, buf_size, varstr);
}
no_var: ;
}
/* verify type compatibility to store vtop in 'dt' type, and generate
casts if needed. */
static void gen_assign_cast(CType *dt)
{
CType *st, *type1, *type2, tmp_type1, tmp_type2;
char buf1[256], buf2[256];
int dbt, sbt;
st = &vtop->type; /* source type */
dbt = dt->t & VT_BTYPE;
sbt = st->t & VT_BTYPE;
if (sbt == VT_VOID || dbt == VT_VOID) {
if (sbt == VT_VOID && dbt == VT_VOID)
; /*
It is Ok if both are void
A test program:
void func1() {}
void func2() {
return func1();
}
gcc accepts this program
*/
else
tcc_error("cannot cast from/to void");
}
if (dt->t & VT_CONSTANT)
tcc_warning("assignment of read-only location");
switch(dbt) {
case VT_PTR:
/* special cases for pointers */
/* '0' can also be a pointer */
if (is_null_pointer(vtop))
goto type_ok;
/* accept implicit pointer to integer cast with warning */
if (is_integer_btype(sbt)) {
tcc_warning("assignment makes pointer from integer without a cast");
goto type_ok;
}
type1 = pointed_type(dt);
/* a function is implicitly a function pointer */
if (sbt == VT_FUNC) {
if ((type1->t & VT_BTYPE) != VT_VOID &&
!is_compatible_types(pointed_type(dt), st))
tcc_warning("assignment from incompatible pointer type");
goto type_ok;
}
if (sbt != VT_PTR)
goto error;
type2 = pointed_type(st);
if ((type1->t & VT_BTYPE) == VT_VOID ||
(type2->t & VT_BTYPE) == VT_VOID) {
/* void * can match anything */
} else {
/* exact type match, except for qualifiers */
tmp_type1 = *type1;
tmp_type2 = *type2;
tmp_type1.t &= ~(VT_CONSTANT | VT_VOLATILE);
tmp_type2.t &= ~(VT_CONSTANT | VT_VOLATILE);
if (!is_compatible_types(&tmp_type1, &tmp_type2)) {
/* Like GCC don't warn by default for merely changes
in pointer target signedness. Do warn for different
base types, though, in particular for unsigned enums
and signed int targets. */
if ((tmp_type1.t & (VT_DEFSIGN | VT_UNSIGNED)) !=
(tmp_type2.t & (VT_DEFSIGN | VT_UNSIGNED)) &&
(tmp_type1.t & VT_BTYPE) == (tmp_type2.t & VT_BTYPE))
;
else
tcc_warning("assignment from incompatible pointer type");
}
}
/* check const and volatile */
if ((!(type1->t & VT_CONSTANT) && (type2->t & VT_CONSTANT)) ||
(!(type1->t & VT_VOLATILE) && (type2->t & VT_VOLATILE)))
tcc_warning("assignment discards qualifiers from pointer target type");
break;
case VT_BYTE:
case VT_SHORT:
case VT_INT:
case VT_LLONG:
if (sbt == VT_PTR || sbt == VT_FUNC) {
tcc_warning("assignment makes integer from pointer without a cast");
} else if (sbt == VT_STRUCT) {
goto case_VT_STRUCT;
}
/* XXX: more tests */
break;
case VT_STRUCT:
case_VT_STRUCT:
tmp_type1 = *dt;
tmp_type2 = *st;
tmp_type1.t &= ~(VT_CONSTANT | VT_VOLATILE);
tmp_type2.t &= ~(VT_CONSTANT | VT_VOLATILE);
if (!is_compatible_types(&tmp_type1, &tmp_type2)) {
error:
type_to_str(buf1, sizeof(buf1), st, NULL);
type_to_str(buf2, sizeof(buf2), dt, NULL);
tcc_error("cannot cast '%s' to '%s'", buf1, buf2);
}
break;
}
type_ok:
gen_cast(dt);
}
/* store vtop in lvalue pushed on stack */
ST_FUNC void vstore(void)
{
int sbt, dbt, ft, r, t, size, align, bit_size, bit_pos, rc, delayed_cast;
ft = vtop[-1].type.t;
sbt = vtop->type.t & VT_BTYPE;
dbt = ft & VT_BTYPE;
if ((((sbt == VT_INT || sbt == VT_SHORT) && dbt == VT_BYTE) ||
(sbt == VT_INT && dbt == VT_SHORT))
&& !(vtop->type.t & VT_BITFIELD)) {
/* optimize char/short casts */
delayed_cast = VT_MUSTCAST;
vtop->type.t = (ft & VT_TYPE & ~VT_BITFIELD &
((1 << VT_STRUCT_SHIFT) - 1));
/* XXX: factorize */
if (ft & VT_CONSTANT)
tcc_warning("assignment of read-only location");
} else {
delayed_cast = 0;
if (!(ft & VT_BITFIELD))
gen_assign_cast(&vtop[-1].type);
}
if (sbt == VT_STRUCT) {
/* if structure, only generate pointer */
/* structure assignment : generate memcpy */
/* XXX: optimize if small size */
size = type_size(&vtop->type, &align);
/* destination */
vswap();
vtop->type.t = VT_PTR;
gaddrof();
/* address of memcpy() */
#ifdef TCC_ARM_EABI
if(!(align & 7))
vpush_global_sym(&func_old_type, TOK_memcpy8);
else if(!(align & 3))
vpush_global_sym(&func_old_type, TOK_memcpy4);
else
#endif
/* Use memmove, rather than memcpy, as dest and src may be same: */
#if BOOTSTRAP && __arm__
vpush_global_sym(&func_old_type, TOK___memmove);
#else
vpush_global_sym(&func_old_type, TOK_memmove);
#endif
vswap();
/* source */
vpushv(vtop - 2);
vtop->type.t = VT_PTR;
gaddrof();
/* type size */
vpushi(size);
gfunc_call(3);
/* leave source on stack */
} else if (ft & VT_BITFIELD) {
/* bitfield store handling */
/* save lvalue as expression result (example: s.b = s.a = n;) */
vdup(), vtop[-1] = vtop[-2];
bit_pos = (ft >> VT_STRUCT_SHIFT) & 0x3f;
bit_size = (ft >> (VT_STRUCT_SHIFT + 6)) & 0x3f;
/* remove bit field info to avoid loops */
vtop[-1].type.t = ft & ~VT_BITFIELD & ((1 << VT_STRUCT_SHIFT) - 1);
if((ft & VT_BTYPE) == VT_BOOL) {
gen_cast(&vtop[-1].type);
vtop[-1].type.t = (vtop[-1].type.t & ~VT_BTYPE) | (VT_BYTE | VT_UNSIGNED);
}
/* duplicate destination */
vdup();
vtop[-1] = vtop[-2];
/* mask and shift source */
if((ft & VT_BTYPE) != VT_BOOL) {
if((ft & VT_BTYPE) == VT_LLONG) {
vpushll((1ULL << bit_size) - 1ULL);
} else {
vpushi((1 << bit_size) - 1);
}
gen_op('&');
}
vpushi(bit_pos);
gen_op(TOK_SHL);
/* load destination, mask and or with source */
vswap();
if((ft & VT_BTYPE) == VT_LLONG) {
vpushll(~(((1ULL << bit_size) - 1ULL) << bit_pos));
} else {
vpushi(~(((1 << bit_size) - 1) << bit_pos));
}
gen_op('&');
gen_op('|');
/* store result */
vstore();
/* ... and discard */
vpop();
} else {
#ifdef CONFIG_TCC_BCHECK
/* bound check case */
if (vtop[-1].r & VT_MUSTBOUND) {
vswap();
gbound();
vswap();
}
#endif
rc = RC_INT;
if (is_float(ft)) {
rc = RC_FLOAT;
#ifdef TCC_TARGET_X86_64
if ((ft & VT_BTYPE) == VT_LDOUBLE) {
rc = RC_ST0;
} else if ((ft & VT_BTYPE) == VT_QFLOAT) {
rc = RC_FRET;
}
#endif
}
r = gv(rc); /* generate value */
/* if lvalue was saved on stack, must read it */
if ((vtop[-1].r & VT_VALMASK) == VT_LLOCAL) {
SValue sv;
t = get_reg(RC_INT);
#if PTR_SIZE == 8
sv.type.t = VT_PTR;
#else
sv.type.t = VT_INT;
#endif
sv.r = VT_LOCAL | VT_LVAL;
sv.c.i = vtop[-1].c.i;
load(t, &sv);
vtop[-1].r = t | VT_LVAL;
}
/* two word case handling : store second register at word + 4 (or +8 for x86-64) */
#if PTR_SIZE == 8
if (((ft & VT_BTYPE) == VT_QLONG) || ((ft & VT_BTYPE) == VT_QFLOAT)) {
int addr_type = VT_LLONG, load_size = 8, load_type = ((vtop->type.t & VT_BTYPE) == VT_QLONG) ? VT_LLONG : VT_DOUBLE;
#else
if ((ft & VT_BTYPE) == VT_LLONG) {
int addr_type = VT_INT, load_size = 4, load_type = VT_INT;
#endif
vtop[-1].type.t = load_type;
store(r, vtop - 1);
vswap();
/* convert to int to increment easily */
vtop->type.t = addr_type;
gaddrof();
vpushi(load_size);
gen_op('+');
vtop->r |= VT_LVAL;
vswap();
vtop[-1].type.t = load_type;
/* XXX: it works because r2 is spilled last ! */
store(vtop->r2, vtop - 1);
} else {
store(r, vtop - 1);
}
vswap();
vtop--; /* NOT vpop() because on x86 it would flush the fp stack */
vtop->r |= delayed_cast;
}
}
/* post defines POST/PRE add. c is the token ++ or -- */
ST_FUNC void inc(int post, int c)
{
test_lvalue();
vdup(); /* save lvalue */
if (post) {
gv_dup(); /* duplicate value */
vrotb(3);
vrotb(3);
}
/* add constant */
vpushi(c - TOK_MID);
gen_op('+');
vstore(); /* store value */
if (post)
vpop(); /* if post op, return saved value */
}
ST_FUNC void parse_mult_str (CString *astr, const char *msg)
{
/* read the string */
if (tok != TOK_STR)
expect(msg);
cstr_new(astr);
while (tok == TOK_STR) {
/* XXX: add \0 handling too ? */
cstr_cat(astr, tokc.str.data, -1);
next();
}
cstr_ccat(astr, '\0');
}
/* If I is >= 1 and a power of two, returns log2(i)+1.
If I is 0 returns 0. */
static int exact_log2p1(int i)
{
int ret;
if (!i)
return 0;
for (ret = 1; i >= 1 << 8; ret += 8)
i >>= 8;
if (i >= 1 << 4)
ret += 4, i >>= 4;
if (i >= 1 << 2)
ret += 2, i >>= 2;
if (i >= 1 << 1)
ret++;
return ret;
}
/* Parse GNUC __attribute__ extension. Currently, the following
extensions are recognized:
- aligned(n) : set data/function alignment.
- packed : force data alignment to 1
- section(x) : generate data/code in this section.
- unused : currently ignored, but may be used someday.
- regparm(n) : pass function parameters in registers (i386 only)
*/
static void parse_attribute(AttributeDef *ad)
{
int t, n;
CString astr;
while (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2) {
next();
skip('(');
skip('(');
while (tok != ')') {
if (tok < TOK_IDENT)
expect("attribute name");
t = tok;
next();
switch(t) {
case TOK_SECTION1:
case TOK_SECTION2:
skip('(');
parse_mult_str(&astr, "section name");
ad->section = find_section(tcc_state, (char *)astr.data);
skip(')');
cstr_free(&astr);
break;
case TOK_ALIAS1:
case TOK_ALIAS2:
skip('(');
parse_mult_str(&astr, "alias(\"target\")");
ad->alias_target = /* save string as token, for later */
tok_alloc((char*)astr.data, astr.size-1)->tok;
skip(')');
cstr_free(&astr);
break;
case TOK_VISIBILITY1:
case TOK_VISIBILITY2:
skip('(');
parse_mult_str(&astr,
"visibility(\"default|hidden|internal|protected\")");
if (!strcmp (astr.data, "default"))
ad->a.visibility = STV_DEFAULT;
else if (!strcmp (astr.data, "hidden"))
ad->a.visibility = STV_HIDDEN;
else if (!strcmp (astr.data, "internal"))
ad->a.visibility = STV_INTERNAL;
else if (!strcmp (astr.data, "protected"))
ad->a.visibility = STV_PROTECTED;
else
expect("visibility(\"default|hidden|internal|protected\")");
skip(')');
cstr_free(&astr);
break;
case TOK_ALIGNED1:
case TOK_ALIGNED2:
if (tok == '(') {
next();
n = expr_const();
if (n <= 0 || (n & (n - 1)) != 0)
tcc_error("alignment must be a positive power of two");
skip(')');
} else {
n = MAX_ALIGN;
}
ad->a.aligned = exact_log2p1(n);
if (n != 1 << (ad->a.aligned - 1))
tcc_error("alignment of %d is larger than implemented", n);
break;
case TOK_PACKED1:
case TOK_PACKED2:
ad->a.packed = 1;
break;
case TOK_WEAK1:
case TOK_WEAK2:
ad->a.weak = 1;
break;
case TOK_UNUSED1:
case TOK_UNUSED2:
/* currently, no need to handle it because tcc does not
track unused objects */
break;
case TOK_NORETURN1:
case TOK_NORETURN2:
/* currently, no need to handle it because tcc does not
track unused objects */
break;
case TOK_CDECL1:
case TOK_CDECL2:
case TOK_CDECL3:
ad->a.func_call = FUNC_CDECL;
break;
case TOK_STDCALL1:
case TOK_STDCALL2:
case TOK_STDCALL3:
ad->a.func_call = FUNC_STDCALL;
break;
#ifdef TCC_TARGET_I386
case TOK_REGPARM1:
case TOK_REGPARM2:
skip('(');
n = expr_const();
if (n > 3)
n = 3;
else if (n < 0)
n = 0;
if (n > 0)
ad->a.func_call = FUNC_FASTCALL1 + n - 1;
skip(')');
break;
case TOK_FASTCALL1:
case TOK_FASTCALL2:
case TOK_FASTCALL3:
ad->a.func_call = FUNC_FASTCALLW;
break;
#endif
case TOK_MODE:
skip('(');
switch(tok) {
case TOK_MODE_DI:
ad->a.mode = VT_LLONG + 1;
break;
case TOK_MODE_QI:
ad->a.mode = VT_BYTE + 1;
break;
case TOK_MODE_HI:
ad->a.mode = VT_SHORT + 1;
break;
case TOK_MODE_SI:
case TOK_MODE_word:
ad->a.mode = VT_INT + 1;
break;
default:
tcc_warning("__mode__(%s) not supported\n", get_tok_str(tok, NULL));
break;
}
next();
skip(')');
break;
case TOK_DLLEXPORT:
ad->a.func_export = 1;
break;
case TOK_DLLIMPORT:
ad->a.func_import = 1;
break;
default:
if (tcc_state->warn_unsupported)
tcc_warning("'%s' attribute ignored", get_tok_str(t, NULL));
/* skip parameters */
if (tok == '(') {
int parenthesis = 0;
do {
if (tok == '(')
parenthesis++;
else if (tok == ')')
parenthesis--;
next();
} while (parenthesis && tok != -1);
}
break;
}
if (tok != ',')
break;
next();
}
skip(')');
skip(')');
}
}
static Sym * find_field (CType *type, int v)
{
Sym *s = type->ref;
v |= SYM_FIELD;
while ((s = s->next) != NULL) {
if ((s->v & SYM_FIELD) &&
(s->type.t & VT_BTYPE) == VT_STRUCT &&
(s->v & ~SYM_FIELD) >= SYM_FIRST_ANOM) {
Sym *ret = find_field (&s->type, v);
if (ret)
return ret;
}
if (s->v == v)
break;
}
return s;
}
static void struct_add_offset (Sym *s, int offset)
{
while ((s = s->next) != NULL) {
if ((s->v & SYM_FIELD) &&
(s->type.t & VT_BTYPE) == VT_STRUCT &&
(s->v & ~SYM_FIELD) >= SYM_FIRST_ANOM) {
struct_add_offset(s->type.ref, offset);
} else
s->c += offset;
}
}
static void struct_layout(CType *type, AttributeDef *ad)
{
int align, maxalign, offset, c, bit_pos, bt, prevbt, prev_bit_size;
int pcc = !tcc_state->ms_bitfields;
int packwarn = tcc_state->warn_gcc_compat;
int typealign, bit_size, size;
Sym *f;
if (ad->a.aligned)
maxalign = 1 << (ad->a.aligned - 1);
else
maxalign = 1;
offset = 0;
c = 0;
bit_pos = 0;
prevbt = VT_STRUCT; /* make it never match */
prev_bit_size = 0;
size = 0;
for (f = type->ref->next; f; f = f->next) {
size = type_size(&f->type, &typealign);
if (f->type.t & VT_BITFIELD)
bit_size = (f->type.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f;
else
bit_size = -1;
if (bit_size == 0 && pcc) {
/* Zero-width bit-fields in PCC mode aren't affected
by any packing (attribute or pragma). */
align = typealign;
} else if (f->r > 1) {
align = f->r;
} else if (ad->a.packed || f->r == 1) {
align = 1;
/* Packed fields or packed records don't let the base type
influence the records type alignment. */
typealign = 1;
} else {
align = typealign;
}
if (type->ref->type.t != TOK_STRUCT) {
if (pcc && bit_size >= 0)
size = (bit_size + 7) >> 3;
/* Bit position is already zero from our caller. */
offset = 0;
if (size > c)
c = size;
} else if (bit_size < 0) {
int addbytes = pcc ? (bit_pos + 7) >> 3 : 0;
prevbt = VT_STRUCT;
prev_bit_size = 0;
c = (c + addbytes + align - 1) & -align;
offset = c;
if (size > 0)
c += size;
bit_pos = 0;
} else {
/* A bit-field. Layout is more complicated. There are two
options TCC implements: PCC compatible and MS compatible
(PCC compatible is what GCC uses for almost all targets).
In PCC layout the overall size of the struct (in c) is
_excluding_ the current run of bit-fields (that is,
there's at least additional bit_pos bits after c). In
MS layout c does include the current run of bit-fields.
This matters for calculating the natural alignment buckets
in PCC mode. */
/* 'align' will be used to influence records alignment,
so it's the max of specified and type alignment, except
in certain cases that depend on the mode. */
if (align < typealign)
align = typealign;
if (pcc) {
/* In PCC layout a non-packed bit-field is placed adjacent
to the preceding bit-fields, except if it would overflow
its container (depending on base type) or it's a zero-width
bit-field. Packed non-zero-width bit-fields always are
placed adjacent. */
int ofs = (c * 8 + bit_pos) % (typealign * 8);
int ofs2 = ofs + bit_size + (typealign * 8) - 1;
if (bit_size == 0 ||
(typealign != 1 &&
(ofs2 / (typealign * 8)) > (size/typealign))) {
c = (c + ((bit_pos + 7) >> 3) + typealign - 1) & -typealign;
bit_pos = 0;
} else if (bit_pos + bit_size > size * 8) {
c += bit_pos >> 3;
bit_pos &= 7;
if (bit_pos + bit_size > size * 8) {
c += 1, bit_pos = 0;
if ((ad->a.packed || f->r) && packwarn) {
tcc_warning("struct layout not compatible with GCC (internal limitation)");
packwarn = 0;
}
}
}
offset = c;
/* In PCC layout named bit-fields influence the alignment
of the containing struct using the base types alignment,
except for packed fields (which here have correct
align/typealign). */
if ((f->v & SYM_FIRST_ANOM))
align = 1;
} else {
bt = f->type.t & VT_BTYPE;
if ((bit_pos + bit_size > size * 8) ||
(bit_size > 0) == (bt != prevbt)) {
c = (c + typealign - 1) & -typealign;
offset = c;
bit_pos = 0;
/* In MS bitfield mode a bit-field run always uses
at least as many bits as the underlying type.
To start a new run it's also required that this
or the last bit-field had non-zero width. */
if (bit_size || prev_bit_size)
c += size;
}
/* In MS layout the records alignment is normally
influenced by the field, except for a zero-width
field at the start of a run (but by further zero-width
fields it is again). */
if (bit_size == 0 && prevbt != bt)
align = 1;
prevbt = bt;
prev_bit_size = bit_size;
}
f->type.t = (f->type.t & ~(0x3f << VT_STRUCT_SHIFT))
| (bit_pos << VT_STRUCT_SHIFT);
bit_pos += bit_size;
if (pcc && bit_pos >= size * 8) {
c += size;
bit_pos -= size * 8;
}
}
if (align > maxalign)
maxalign = align;
#if 0
printf("set field %s offset=%d",
get_tok_str(f->v & ~SYM_FIELD, NULL), offset);
if (f->type.t & VT_BITFIELD) {
printf(" pos=%d size=%d",
(f->type.t >> VT_STRUCT_SHIFT) & 0x3f,
(f->type.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f);
}
printf("\n");
#endif
if (f->v & SYM_FIRST_ANOM && (f->type.t & VT_BTYPE) == VT_STRUCT) {
Sym *ass;
/* An anonymous struct/union. Adjust member offsets
to reflect the real offset of our containing struct.
Also set the offset of this anon member inside
the outer struct to be zero. Via this it
works when accessing the field offset directly
(from base object), as well as when recursing
members in initializer handling. */
int v2 = f->type.ref->v;
if (!(v2 & SYM_FIELD) &&
(v2 & ~SYM_STRUCT) < SYM_FIRST_ANOM) {
Sym **pps;
/* This happens only with MS extensions. The
anon member has a named struct type, so it
potentially is shared with other references.
We need to unshare members so we can modify
them. */
ass = f->type.ref;
f->type.ref = sym_push(anon_sym++ | SYM_FIELD,
&f->type.ref->type, 0,
f->type.ref->c);
pps = &f->type.ref->next;
while ((ass = ass->next) != NULL) {
*pps = sym_push(ass->v, &ass->type, 0, ass->c);
pps = &((*pps)->next);
}
*pps = NULL;
}
struct_add_offset(f->type.ref, offset);
f->c = 0;
} else {
f->c = offset;
}
f->r = 0;
}
/* store size and alignment */
type->ref->c = (c + (pcc ? (bit_pos + 7) >> 3 : 0)
+ maxalign - 1) & -maxalign;
type->ref->r = maxalign;
if (offset + size > type->ref->c)
tcc_warning("will touch memory past end of the struct (internal limitation)");
}
/* enum/struct/union declaration. u is either VT_ENUM or VT_STRUCT */
static void struct_decl(CType *type, AttributeDef *ad, int u)
{
int a, v, size, align, flexible, alignoverride;
long c;
int bit_size, bsize, bt;
Sym *s, *ss, **ps;
AttributeDef ad1;
CType type1, btype;
a = tok; /* save decl type */
next();
if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2)
parse_attribute(ad);
if (tok != '{') {
v = tok;
next();
/* struct already defined ? return it */
if (v < TOK_IDENT)
expect("struct/union/enum name");
s = struct_find(v);
if (s && (s->scope == local_scope || (tok != '{' && tok != ';'))) {
if (s->type.t != a)
tcc_error("redefinition of '%s'", get_tok_str(v, NULL));
goto do_decl;
}
} else {
v = anon_sym++;
}
/* Record the original enum/struct/union token. */
type1.t = a;
type1.ref = NULL;
/* we put an undefined size for struct/union */
s = sym_push(v | SYM_STRUCT, &type1, 0, -1);
s->r = 0; /* default alignment is zero as gcc */
/* put struct/union/enum name in type */
do_decl:
type->t = u;
type->ref = s;
if (tok == '{') {
next();
if (s->c != -1)
tcc_error("struct/union/enum already defined");
/* cannot be empty */
c = 0;
/* non empty enums are not allowed */
if (a == TOK_ENUM) {
int seen_neg = 0;
int seen_wide = 0;
for(;;) {
CType *t = &int_type;
v = tok;
if (v < TOK_UIDENT)
expect("identifier");
ss = sym_find(v);
if (ss && !local_stack)
tcc_error("redefinition of enumerator '%s'",
get_tok_str(v, NULL));
next();
if (tok == '=') {
next();
#if PTR_SIZE == 8
c = expr_const64();
#else
/* We really want to support long long enums
on i386 as well, but the Sym structure only
holds a 'long' for associated constants,
and enlarging it would bump its size (no
available padding). So punt for now. */
c = expr_const();
#endif
}
if (c < 0)
seen_neg = 1;
if (c != (int)c && (unsigned long)c != (unsigned int)c)
seen_wide = 1, t = &size_type;
/* enum symbols have static storage */
ss = sym_push(v, t, VT_CONST, c);
ss->type.t |= VT_STATIC;
if (tok != ',')
break;
next();
c++;
/* NOTE: we accept a trailing comma */
if (tok == '}')
break;
}
if (!seen_neg)
s->a.unsigned_enum = 1;
s->c = type_size(seen_wide ? &size_type : &int_type, &align);
skip('}');
} else {
ps = &s->next;
flexible = 0;
while (tok != '}') {
if (!parse_btype(&btype, &ad1)) {
skip(';');
continue;
}
while (1) {
if (flexible)
tcc_error("flexible array member '%s' not at the end of struct",
get_tok_str(v, NULL));
bit_size = -1;
v = 0;
type1 = btype;
if (tok != ':') {
if (tok != ';')
type_decl(&type1, &ad1, &v, TYPE_DIRECT);
if (v == 0) {
if ((type1.t & VT_BTYPE) != VT_STRUCT)
expect("identifier");
else {
int v = btype.ref->v;
if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) < SYM_FIRST_ANOM) {
if (tcc_state->ms_extensions == 0)
expect("identifier");
}
}
}
if (type_size(&type1, &align) < 0) {
if ((a == TOK_STRUCT) && (type1.t & VT_ARRAY) && c)
flexible = 1;
else
tcc_error("field '%s' has incomplete type",
get_tok_str(v, NULL));
}
if ((type1.t & VT_BTYPE) == VT_FUNC ||
(type1.t & VT_STORAGE))
tcc_error("invalid type for '%s'",
get_tok_str(v, NULL));
}
if (tok == ':') {
next();
bit_size = expr_const();
/* XXX: handle v = 0 case for messages */
if (bit_size < 0)
tcc_error("negative width in bit-field '%s'",
get_tok_str(v, NULL));
if (v && bit_size == 0)
tcc_error("zero width for bit-field '%s'",
get_tok_str(v, NULL));
if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2)
parse_attribute(&ad1);
}
size = type_size(&type1, &align);
/* Only remember non-default alignment. */
alignoverride = 0;
if (ad1.a.aligned) {
int speca = 1 << (ad1.a.aligned - 1);
alignoverride = speca;
} else if (ad1.a.packed || ad->a.packed) {
alignoverride = 1;
} else if (*tcc_state->pack_stack_ptr) {
if (align >= *tcc_state->pack_stack_ptr)
alignoverride = *tcc_state->pack_stack_ptr;
}
if (bit_size >= 0) {
bt = type1.t & VT_BTYPE;
if (bt != VT_INT &&
bt != VT_BYTE &&
bt != VT_SHORT &&
bt != VT_BOOL &&
bt != VT_ENUM &&
bt != VT_LLONG)
tcc_error("bitfields must have scalar type");
bsize = size * 8;
if (bit_size > bsize) {
tcc_error("width of '%s' exceeds its type",
get_tok_str(v, NULL));
} else if (bit_size == bsize) {
/* no need for bit fields */
;
} else {
type1.t |= VT_BITFIELD |
(0 << VT_STRUCT_SHIFT) |
(bit_size << (VT_STRUCT_SHIFT + 6));
}
}
if (v != 0 || (type1.t & VT_BTYPE) == VT_STRUCT) {
/* Remember we've seen a real field to check
for placement of flexible array member. */
c = 1;
}
/* If member is a struct or bit-field, enforce
placing into the struct (as anonymous). */
if (v == 0 &&
((type1.t & VT_BTYPE) == VT_STRUCT ||
bit_size >= 0)) {
v = anon_sym++;
}
if (v) {
ss = sym_push(v | SYM_FIELD, &type1, alignoverride, 0);
*ps = ss;
ps = &ss->next;
}
if (tok == ';' || tok == TOK_EOF)
break;
skip(',');
}
skip(';');
}
skip('}');
if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2)
parse_attribute(ad);
struct_layout(type, ad);
}
}
}
/* return 1 if basic type is a type size (short, long, long long) */
ST_FUNC int is_btype_size(int bt)
{
return bt == VT_SHORT || bt == VT_LONG || bt == VT_LLONG;
}
/* Add type qualifiers to a type. If the type is an array then the qualifiers
are added to the element type, copied because it could be a typedef. */
static void parse_btype_qualify(CType *type, int qualifiers)
{
while (type->t & VT_ARRAY) {
type->ref = sym_push(SYM_FIELD, &type->ref->type, 0, type->ref->c);
type = &type->ref->type;
}
type->t |= qualifiers;
}
/* return 0 if no type declaration. otherwise, return the basic type
and skip it.
*/
static int parse_btype(CType *type, AttributeDef *ad)
{
int t, u, bt_size, complete, type_found, typespec_found, g;
Sym *s;
CType type1;
memset(ad, 0, sizeof(AttributeDef));
complete = 0;
type_found = 0;
typespec_found = 0;
t = 0;
while(1) {
switch(tok) {
case TOK_EXTENSION:
/* currently, we really ignore extension */
next();
continue;
/* basic types */
case TOK_CHAR:
u = VT_BYTE;
basic_type:
next();
basic_type1:
if (complete)
tcc_error("too many basic types");
t |= u;
bt_size = is_btype_size (u & VT_BTYPE);
if (u == VT_INT || (!bt_size && !(t & VT_TYPEDEF)))
complete = 1;
typespec_found = 1;
break;
case TOK_VOID:
u = VT_VOID;
goto basic_type;
case TOK_SHORT:
u = VT_SHORT;
goto basic_type;
case TOK_INT:
u = VT_INT;
goto basic_type;
case TOK_LONG:
next();
if ((t & VT_BTYPE) == VT_DOUBLE) {
#ifndef TCC_TARGET_PE
t = (t & ~VT_BTYPE) | VT_LDOUBLE;
#endif
} else if ((t & VT_BTYPE) == VT_LONG) {
t = (t & ~VT_BTYPE) | VT_LLONG;
} else {
u = VT_LONG;
goto basic_type1;
}
break;
#ifdef TCC_TARGET_ARM64
case TOK_UINT128:
/* GCC's __uint128_t appears in some Linux header files. Make it a
synonym for long double to get the size and alignment right. */
u = VT_LDOUBLE;
goto basic_type;
#endif
case TOK_BOOL:
u = VT_BOOL;
goto basic_type;
case TOK_FLOAT:
u = VT_FLOAT;
goto basic_type;
case TOK_DOUBLE:
next();
if ((t & VT_BTYPE) == VT_LONG) {
#ifdef TCC_TARGET_PE
t = (t & ~VT_BTYPE) | VT_DOUBLE;
#else
t = (t & ~VT_BTYPE) | VT_LDOUBLE;
#endif
} else {
u = VT_DOUBLE;
goto basic_type1;
}
break;
case TOK_ENUM:
struct_decl(&type1, ad, VT_ENUM);
basic_type2:
u = type1.t;
type->ref = type1.ref;
goto basic_type1;
case TOK_STRUCT:
case TOK_UNION:
struct_decl(&type1, ad, VT_STRUCT);
goto basic_type2;
/* type modifiers */
case TOK_CONST1:
case TOK_CONST2:
case TOK_CONST3:
type->t = t;
parse_btype_qualify(type, VT_CONSTANT);
t = type->t;
next();
break;
case TOK_VOLATILE1:
case TOK_VOLATILE2:
case TOK_VOLATILE3:
type->t = t;
parse_btype_qualify(type, VT_VOLATILE);
t = type->t;
next();
break;
case TOK_SIGNED1:
case TOK_SIGNED2:
case TOK_SIGNED3:
if ((t & (VT_DEFSIGN|VT_UNSIGNED)) == (VT_DEFSIGN|VT_UNSIGNED))
tcc_error("signed and unsigned modifier");
typespec_found = 1;
t |= VT_DEFSIGN;
next();
break;
case TOK_REGISTER:
case TOK_AUTO:
case TOK_RESTRICT1:
case TOK_RESTRICT2:
case TOK_RESTRICT3:
next();
break;
case TOK_UNSIGNED:
if ((t & (VT_DEFSIGN|VT_UNSIGNED)) == VT_DEFSIGN)
tcc_error("signed and unsigned modifier");
t |= VT_DEFSIGN | VT_UNSIGNED;
next();
typespec_found = 1;
break;
/* storage */
case TOK_EXTERN:
g = VT_EXTERN;
goto storage;
case TOK_STATIC:
g = VT_STATIC;
goto storage;
case TOK_TYPEDEF:
g = VT_TYPEDEF;
goto storage;
storage:
if (t & (VT_EXTERN|VT_STATIC|VT_TYPEDEF) & ~g)
tcc_error("multiple storage classes");
t |= g;
next();
break;
case TOK_INLINE1:
case TOK_INLINE2:
case TOK_INLINE3:
t |= VT_INLINE;
next();
break;
/* GNUC attribute */
case TOK_ATTRIBUTE1:
case TOK_ATTRIBUTE2:
parse_attribute(ad);
if (ad->a.mode) {
u = ad->a.mode -1;
t = (t & ~VT_BTYPE) | u;
}
break;
/* GNUC typeof */
case TOK_TYPEOF1:
case TOK_TYPEOF2:
case TOK_TYPEOF3:
next();
parse_expr_type(&type1);
/* remove all storage modifiers except typedef */
type1.t &= ~(VT_STORAGE&~VT_TYPEDEF);
goto basic_type2;
default:
if (typespec_found)
goto the_end;
s = sym_find(tok);
if (!s || !(s->type.t & VT_TYPEDEF))
goto the_end;
type->t = ((s->type.t & ~VT_TYPEDEF) |
(t & ~(VT_CONSTANT | VT_VOLATILE)));
type->ref = s->type.ref;
if (t & (VT_CONSTANT | VT_VOLATILE))
parse_btype_qualify(type, t & (VT_CONSTANT | VT_VOLATILE));
t = type->t;
if (s->r) {
/* get attributes from typedef */
if (0 == ad->a.aligned)
ad->a.aligned = s->a.aligned;
if (0 == ad->a.func_call)
ad->a.func_call = s->a.func_call;
ad->a.packed |= s->a.packed;
}
next();
typespec_found = 1;
break;
}
type_found = 1;
}
the_end:
if (tcc_state->char_is_unsigned) {
if ((t & (VT_DEFSIGN|VT_BTYPE)) == VT_BYTE)
t |= VT_UNSIGNED;
}
/* long is never used as type */
if ((t & VT_BTYPE) == VT_LONG)
#if PTR_SIZE == 8 && !defined TCC_TARGET_PE
t = (t & ~VT_BTYPE) | VT_LLONG;
#else
t = (t & ~VT_BTYPE) | VT_INT;
#endif
type->t = t;
return type_found;
}
/* convert a function parameter type (array to pointer and function to
function pointer) */
static inline void convert_parameter_type(CType *pt)
{
/* remove const and volatile qualifiers (XXX: const could be used
to indicate a const function parameter */
pt->t &= ~(VT_CONSTANT | VT_VOLATILE);
/* array must be transformed to pointer according to ANSI C */
pt->t &= ~VT_ARRAY;
if ((pt->t & VT_BTYPE) == VT_FUNC) {
mk_pointer(pt);
}
}
ST_FUNC void parse_asm_str(CString *astr)
{
skip('(');
parse_mult_str(astr, "string constant");
}
/* Parse an asm label and return the token */
static int asm_label_instr(void)
{
int v;
CString astr;
next();
parse_asm_str(&astr);
skip(')');
#ifdef ASM_DEBUG
printf("asm_alias: \"%s\"\n", (char *)astr.data);
#endif
v = tok_alloc(astr.data, astr.size - 1)->tok;
cstr_free(&astr);
return v;
}
static int post_type(CType *type, AttributeDef *ad, int storage, int td)
{
int n, l, t1, arg_size, align;
Sym **plast, *s, *first;
AttributeDef ad1;
CType pt;
if (tok == '(') {
/* function type, or recursive declarator (return if so) */
next();
if (td && !(td & TYPE_ABSTRACT))
return 0;
if (tok == ')')
l = 0;
else if (parse_btype(&pt, &ad1))
l = FUNC_NEW;
else if (td)
return 0;
else
l = FUNC_OLD;
first = NULL;
plast = &first;
arg_size = 0;
if (l) {
for(;;) {
/* read param name and compute offset */
if (l != FUNC_OLD) {
if ((pt.t & VT_BTYPE) == VT_VOID && tok == ')')
break;
type_decl(&pt, &ad1, &n, TYPE_DIRECT | TYPE_ABSTRACT);
if ((pt.t & VT_BTYPE) == VT_VOID)
tcc_error("parameter declared as void");
arg_size += (type_size(&pt, &align) + PTR_SIZE - 1) / PTR_SIZE;
} else {
n = tok;
if (n < TOK_UIDENT)
expect("identifier");
pt.t = VT_VOID; /* invalid type */
next();
}
convert_parameter_type(&pt);
s = sym_push(n | SYM_FIELD, &pt, 0, 0);
*plast = s;
plast = &s->next;
if (tok == ')')
break;
skip(',');
if (l == FUNC_NEW && tok == TOK_DOTS) {
l = FUNC_ELLIPSIS;
next();
break;
}
if (l == FUNC_NEW && !parse_btype(&pt, &ad1))
tcc_error("invalid type");
}
} else
/* if no parameters, then old type prototype */
l = FUNC_OLD;
skip(')');
/* NOTE: const is ignored in returned type as it has a special
meaning in gcc / C++ */
type->t &= ~VT_CONSTANT;
/* some ancient pre-K&R C allows a function to return an array
and the array brackets to be put after the arguments, such
that "int c()[]" means something like "int[] c()" */
if (tok == '[') {
next();
skip(']'); /* only handle simple "[]" */
mk_pointer(type);
}
/* we push a anonymous symbol which will contain the function prototype */
ad->a.func_args = arg_size;
s = sym_push(SYM_FIELD, type, 0, l);
s->a = ad->a;
s->next = first;
type->t = VT_FUNC;
type->ref = s;
} else if (tok == '[') {
int saved_nocode_wanted = nocode_wanted;
/* array definition */
next();
if (tok == TOK_RESTRICT1)
next();
n = -1;
t1 = 0;
if (tok != ']') {
if (!local_stack || (storage & VT_STATIC))
vpushi(expr_const());
else {
/* VLAs (which can only happen with local_stack && !VT_STATIC)
length must always be evaluated, even under nocode_wanted,
so that its size slot is initialized (e.g. under sizeof
or typeof). */
nocode_wanted = 0;
gexpr();
}
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
n = vtop->c.i;
if (n < 0)
tcc_error("invalid array size");
} else {
if (!is_integer_btype(vtop->type.t & VT_BTYPE))
tcc_error("size of variable length array should be an integer");
t1 = VT_VLA;
}
}
skip(']');
/* parse next post type */
post_type(type, ad, storage, 0);
if (type->t == VT_FUNC)
tcc_error("declaration of an array of functions");
t1 |= type->t & VT_VLA;
if (t1 & VT_VLA) {
loc -= type_size(&int_type, &align);
loc &= -align;
n = loc;
vla_runtime_type_size(type, &align);
gen_op('*');
vset(&int_type, VT_LOCAL|VT_LVAL, n);
vswap();
vstore();
}
if (n != -1)
vpop();
nocode_wanted = saved_nocode_wanted;
/* we push an anonymous symbol which will contain the array
element type */
s = sym_push(SYM_FIELD, type, 0, n);
type->t = (t1 ? VT_VLA : VT_ARRAY) | VT_PTR;
type->ref = s;
}
return 1;
}
/* Parse a type declarator (except basic type), and return the type
in 'type'. 'td' is a bitmask indicating which kind of type decl is
expected. 'type' should contain the basic type. 'ad' is the
attribute definition of the basic type. It can be modified by
type_decl(). If this (possibly abstract) declarator is a pointer chain
it returns the innermost pointed to type (equals *type, but is a different
pointer), otherwise returns type itself, that's used for recursive calls. */
static CType *type_decl(CType *type, AttributeDef *ad, int *v, int td)
{
CType *post, *ret;
int qualifiers, storage;
/* recursive type, remove storage bits first, apply them later again */
storage = type->t & VT_STORAGE;
type->t &= ~VT_STORAGE;
post = ret = type;
while (tok == '*') {
qualifiers = 0;
redo:
next();
switch(tok) {
case TOK_CONST1:
case TOK_CONST2:
case TOK_CONST3:
qualifiers |= VT_CONSTANT;
goto redo;
case TOK_VOLATILE1:
case TOK_VOLATILE2:
case TOK_VOLATILE3:
qualifiers |= VT_VOLATILE;
goto redo;
case TOK_RESTRICT1:
case TOK_RESTRICT2:
case TOK_RESTRICT3:
goto redo;
/* XXX: clarify attribute handling */
case TOK_ATTRIBUTE1:
case TOK_ATTRIBUTE2:
parse_attribute(ad);
break;
}
mk_pointer(type);
type->t |= qualifiers;
if (ret == type)
/* innermost pointed to type is the one for the first derivation */
ret = pointed_type(type);
}
if (tok == '(') {
/* This is possibly a parameter type list for abstract declarators
('int ()'), use post_type for testing this. */
if (!post_type(type, ad, 0, td)) {
/* It's not, so it's a nested declarator, and the post operations
apply to the innermost pointed to type (if any). */
/* XXX: this is not correct to modify 'ad' at this point, but
the syntax is not clear */
if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2)
parse_attribute(ad);
post = type_decl(type, ad, v, td);
skip(')');
}
} else if (tok >= TOK_IDENT && (td & TYPE_DIRECT)) {
/* type identifier */
*v = tok;
next();
} else {
if (!(td & TYPE_ABSTRACT))
expect("identifier");
*v = 0;
}
post_type(post, ad, storage, 0);
if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2)
parse_attribute(ad);
type->t |= storage;
return ret;
}
/* compute the lvalue VT_LVAL_xxx needed to match type t. */
ST_FUNC int lvalue_type(int t)
{
int bt, r;
r = VT_LVAL;
bt = t & VT_BTYPE;
if (bt == VT_BYTE || bt == VT_BOOL)
r |= VT_LVAL_BYTE;
else if (bt == VT_SHORT)
r |= VT_LVAL_SHORT;
else
return r;
if (t & VT_UNSIGNED)
r |= VT_LVAL_UNSIGNED;
return r;
}
/* indirection with full error checking and bound check */
ST_FUNC void indir(void)
{
if ((vtop->type.t & VT_BTYPE) != VT_PTR) {
if ((vtop->type.t & VT_BTYPE) == VT_FUNC)
return;
expect("pointer");
}
if (vtop->r & VT_LVAL)
gv(RC_INT);
vtop->type = *pointed_type(&vtop->type);
/* Arrays and functions are never lvalues */
if (!(vtop->type.t & VT_ARRAY) && !(vtop->type.t & VT_VLA)
&& (vtop->type.t & VT_BTYPE) != VT_FUNC) {
vtop->r |= lvalue_type(vtop->type.t);
/* if bound checking, the referenced pointer must be checked */
#ifdef CONFIG_TCC_BCHECK
if (tcc_state->do_bounds_check)
vtop->r |= VT_MUSTBOUND;
#endif
}
}
/* pass a parameter to a function and do type checking and casting */
static void gfunc_param_typed(Sym *func, Sym *arg)
{
int func_type;
CType type;
func_type = func->c;
if (func_type == FUNC_OLD ||
(func_type == FUNC_ELLIPSIS && arg == NULL)) {
/* default casting : only need to convert float to double */
if ((vtop->type.t & VT_BTYPE) == VT_FLOAT) {
type.t = VT_DOUBLE;
gen_cast(&type);
} else if (vtop->type.t & VT_BITFIELD) {
type.t = vtop->type.t & (VT_BTYPE | VT_UNSIGNED);
type.ref = vtop->type.ref;
gen_cast(&type);
}
} else if (arg == NULL) {
tcc_error("too many arguments to function");
} else {
type = arg->type;
type.t &= ~VT_CONSTANT; /* need to do that to avoid false warning */
gen_assign_cast(&type);
}
}
/* parse an expression and return its type without any side effect.
If UNRY we parse an unary expression, otherwise a full one. */
static void expr_type(CType *type, int unry)
{
nocode_wanted++;
if (unry)
unary();
else
gexpr();
*type = vtop->type;
vpop();
nocode_wanted--;
}
/* parse an expression of the form '(type)' or '(expr)' and return its
type */
static void parse_expr_type(CType *type)
{
int n;
AttributeDef ad;
skip('(');
if (parse_btype(type, &ad)) {
type_decl(type, &ad, &n, TYPE_ABSTRACT);
} else {
expr_type(type, 0);
}
skip(')');
}
static void parse_type(CType *type)
{
AttributeDef ad;
int n;
if (!parse_btype(type, &ad)) {
expect("type");
}
type_decl(type, &ad, &n, TYPE_ABSTRACT);
}
static void parse_builtin_params(int nc, const char *args)
{
char c, sep = '(';
CType t;
if (nc)
nocode_wanted++;
next();
while ((c = *args++)) {
skip(sep);
sep = ',';
switch (c) {
case 'e': expr_eq(); continue;
case 't': parse_type(&t); vpush(&t); continue;
default: tcc_error("internal error"); break;
}
}
skip(')');
if (nc)
nocode_wanted--;
}
ST_FUNC void unary(void)
{
int n, t, align, size, r, sizeof_caller;
CType type;
Sym *s;
AttributeDef ad;
sizeof_caller = in_sizeof;
in_sizeof = 0;
/* XXX: GCC 2.95.3 does not generate a table although it should be
better here */
tok_next:
switch(tok) {
case TOK_EXTENSION:
next();
goto tok_next;
case TOK_CINT:
case TOK_CCHAR:
case TOK_LCHAR:
t = VT_INT;
push_tokc:
type.t = t;
type.ref = 0;
vsetc(&type, VT_CONST, &tokc);
next();
break;
case TOK_CUINT:
t = VT_INT | VT_UNSIGNED;
goto push_tokc;
case TOK_CLLONG:
t = VT_LLONG;
goto push_tokc;
case TOK_CULLONG:
t = VT_LLONG | VT_UNSIGNED;
goto push_tokc;
case TOK_CFLOAT:
t = VT_FLOAT;
goto push_tokc;
case TOK_CDOUBLE:
t = VT_DOUBLE;
goto push_tokc;
case TOK_CLDOUBLE:
t = VT_LDOUBLE;
goto push_tokc;
case TOK___FUNCTION__:
if (!gnu_ext)
goto tok_identifier;
/* fall thru */
case TOK___FUNC__:
{
void *ptr;
int len;
/* special function name identifier */
len = strlen(funcname) + 1;
/* generate char[len] type */
type.t = VT_BYTE;
mk_pointer(&type);
type.t |= VT_ARRAY;
type.ref->c = len;
vpush_ref(&type, data_section, data_section->data_offset, len);
ptr = section_ptr_add(data_section, len);
memcpy(ptr, funcname, len);
next();
}
break;
case TOK_LSTR:
#ifdef TCC_TARGET_PE
t = VT_SHORT | VT_UNSIGNED;
#else
t = VT_INT;
#endif
goto str_init;
case TOK_STR:
/* string parsing */
t = VT_BYTE;
str_init:
if (tcc_state->warn_write_strings)
t |= VT_CONSTANT;
type.t = t;
mk_pointer(&type);
type.t |= VT_ARRAY;
memset(&ad, 0, sizeof(AttributeDef));
decl_initializer_alloc(&type, &ad, VT_CONST, 2, 0, 0);
break;
case '(':
next();
/* cast ? */
if (parse_btype(&type, &ad)) {
type_decl(&type, &ad, &n, TYPE_ABSTRACT);
skip(')');
/* check ISOC99 compound literal */
if (tok == '{') {
/* data is allocated locally by default */
if (global_expr)
r = VT_CONST;
else
r = VT_LOCAL;
/* all except arrays are lvalues */
if (!(type.t & VT_ARRAY))
r |= lvalue_type(type.t);
memset(&ad, 0, sizeof(AttributeDef));
decl_initializer_alloc(&type, &ad, r, 1, 0, 0);
} else {
if (sizeof_caller) {
vpush(&type);
return;
}
unary();
gen_cast(&type);
}
} else if (tok == '{') {
int saved_nocode_wanted = nocode_wanted;
if (const_wanted)
tcc_error("expected constant");
/* save all registers */
save_regs(0);
/* statement expression : we do not accept break/continue
inside as GCC does. We do retain the nocode_wanted state,
as statement expressions can't ever be entered from the
outside, so any reactivation of code emission (from labels
or loop heads) can be disabled again after the end of it. */
block(NULL, NULL, 1);
nocode_wanted = saved_nocode_wanted;
skip(')');
} else {
gexpr();
skip(')');
}
break;
case '*':
next();
unary();
indir();
break;
case '&':
next();
unary();
/* functions names must be treated as function pointers,
except for unary '&' and sizeof. Since we consider that
functions are not lvalues, we only have to handle it
there and in function calls. */
/* arrays can also be used although they are not lvalues */
if ((vtop->type.t & VT_BTYPE) != VT_FUNC &&
!(vtop->type.t & VT_ARRAY))
test_lvalue();
mk_pointer(&vtop->type);
gaddrof();
break;
case '!':
next();
unary();
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
CType boolean;
boolean.t = VT_BOOL;
gen_cast(&boolean);
vtop->c.i = !vtop->c.i;
} else if ((vtop->r & VT_VALMASK) == VT_CMP)
vtop->c.i ^= 1;
else {
save_regs(1);
vseti(VT_JMP, gvtst(1, 0));
}
break;
case '~':
next();
unary();
vpushi(-1);
gen_op('^');
break;
case '+':
next();
unary();
if ((vtop->type.t & VT_BTYPE) == VT_PTR)
tcc_error("pointer not accepted for unary plus");
/* In order to force cast, we add zero, except for floating point
where we really need an noop (otherwise -0.0 will be transformed
into +0.0). */
if (!is_float(vtop->type.t)) {
vpushi(0);
gen_op('+');
}
break;
case TOK_SIZEOF:
case TOK_ALIGNOF1:
case TOK_ALIGNOF2:
t = tok;
next();
in_sizeof++;
expr_type(&type, 1); // Perform a in_sizeof = 0;
size = type_size(&type, &align);
if (t == TOK_SIZEOF) {
if (!(type.t & VT_VLA)) {
if (size < 0)
tcc_error("sizeof applied to an incomplete type");
vpushs(size);
} else {
vla_runtime_type_size(&type, &align);
}
} else {
vpushs(align);
}
vtop->type.t |= VT_UNSIGNED;
break;
case TOK_builtin_expect:
/* __builtin_expect is a no-op for now */
parse_builtin_params(0, "ee");
vpop();
break;
case TOK_builtin_types_compatible_p:
parse_builtin_params(0, "tt");
vtop[-1].type.t &= ~(VT_CONSTANT | VT_VOLATILE);
vtop[0].type.t &= ~(VT_CONSTANT | VT_VOLATILE);
n = is_compatible_types(&vtop[-1].type, &vtop[0].type);
vtop -= 2;
vpushi(n);
break;
case TOK_builtin_choose_expr:
{
#if HAVE_LONG_LONG_STUB || HAVE_LONG_LONG
int64_t c;
#else
int32_t c;
#endif
next();
skip('(');
#if HAVE_LONG_LONG
c = expr_const64();
#else
c = expr_const32();
#endif
skip(',');
if (!c) {
nocode_wanted++;
}
expr_eq();
if (!c) {
vpop();
nocode_wanted--;
}
skip(',');
if (c) {
nocode_wanted++;
}
expr_eq();
if (c) {
vpop();
nocode_wanted--;
}
skip(')');
}
break;
case TOK_builtin_constant_p:
parse_builtin_params(1, "e");
n = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
vtop--;
vpushi(n);
break;
case TOK_builtin_frame_address:
case TOK_builtin_return_address:
{
int tok1 = tok;
int level;
CType type;
next();
skip('(');
if (tok != TOK_CINT) {
tcc_error("%s only takes positive integers",
tok1 == TOK_builtin_return_address ?
"__builtin_return_address" :
"__builtin_frame_address");
}
level = (uint32_t)tokc.i;
next();
skip(')');
type.t = VT_VOID;
mk_pointer(&type);
vset(&type, VT_LOCAL, 0); /* local frame */
while (level--) {
mk_pointer(&vtop->type);
indir(); /* -> parent frame */
}
if (tok1 == TOK_builtin_return_address) {
// assume return address is just above frame pointer on stack
vpushi(PTR_SIZE);
gen_op('+');
mk_pointer(&vtop->type);
indir();
}
}
break;
#ifdef TCC_TARGET_X86_64
#ifdef TCC_TARGET_PE
case TOK_builtin_va_start:
parse_builtin_params(0, "ee");
r = vtop->r & VT_VALMASK;
if (r == VT_LLOCAL)
r = VT_LOCAL;
if (r != VT_LOCAL)
tcc_error("__builtin_va_start expects a local variable");
vtop->r = r;
vtop->type = char_pointer_type;
vtop->c.i += 8;
vstore();
break;
#else
case TOK_builtin_va_arg_types:
parse_builtin_params(0, "t");
vpushi(classify_x86_64_va_arg(&vtop->type));
vswap();
vpop();
break;
#endif
#endif
#ifdef TCC_TARGET_ARM64
case TOK___va_start: {
parse_builtin_params(0, "ee");
//xx check types
gen_va_start();
vpushi(0);
vtop->type.t = VT_VOID;
break;
}
case TOK___va_arg: {
CType type;
parse_builtin_params(0, "et");
type = vtop->type;
vpop();
//xx check types
gen_va_arg(&type);
vtop->type = type;
break;
}
case TOK___arm64_clear_cache: {
parse_builtin_params(0, "ee");
gen_clear_cache();
vpushi(0);
vtop->type.t = VT_VOID;
break;
}
#endif
/* pre operations */
case TOK_INC:
case TOK_DEC:
t = tok;
next();
unary();
inc(0, t);
break;
case '-':
next();
unary();
t = vtop->type.t & VT_BTYPE;
if (is_float(t)) {
/* In IEEE negate(x) isn't subtract(0,x), but rather
subtract(-0, x). */
vpush(&vtop->type);
#if HAVE_FLOAT
if (t == VT_FLOAT)
vtop->c.f = -1.0 * 0.0;
else if (t == VT_DOUBLE)
vtop->c.d = -1.0 * 0.0;
else
vtop->c.ld = -1.0 * 0.0;
#endif
} else
vpushi(0);
vswap();
gen_op('-');
break;
case TOK_LAND:
if (!gnu_ext)
goto tok_identifier;
next();
/* allow to take the address of a label */
if (tok < TOK_UIDENT)
expect("label identifier");
s = label_find(tok);
if (!s) {
s = label_push(&global_label_stack, tok, LABEL_FORWARD);
} else {
if (s->r == LABEL_DECLARED)
s->r = LABEL_FORWARD;
}
if (!s->type.t) {
s->type.t = VT_VOID;
mk_pointer(&s->type);
s->type.t |= VT_STATIC;
}
vpushsym(&s->type, s);
next();
break;
// special qnan , snan and infinity values
case TOK___NAN__:
#if HAVE_LONG_LONG
vpush64(VT_DOUBLE, 0x7ff8000000000000ULL);
#else
vpush64(VT_DOUBLE, 0x7ff80000);
vtop->c.i = vtop->c.i << 32;
#endif
next();
break;
case TOK___SNAN__:
#if HAVE_LONG_LONG
vpush64(VT_DOUBLE, 0x7ff0000000000001ULL);
#else
vpush64(VT_DOUBLE, 0x7ff00000);
vtop->c.i = vtop->c.i << 32;
vtop->c.i = vtop->c.i | 1;
#endif
next();
break;
case TOK___INF__:
#if HAVE_LONG_LONG
vpush64(VT_DOUBLE, 0x7ff0000000000000ULL);
#else
vpush64(VT_DOUBLE, 0x7ff00000);
vtop->c.i = vtop->c.i << 32;
#endif
next();
break;
default:
tok_identifier:
t = tok;
next();
if (t < TOK_UIDENT)
expect("identifier");
s = sym_find(t);
if (!s) {
const char *name = get_tok_str(t, NULL);
if (tok != '(')
tcc_error("'%s' undeclared", name);
/* for simple function calls, we tolerate undeclared
external reference to int() function */
if (tcc_state->warn_implicit_function_declaration
#ifdef TCC_TARGET_PE
/* people must be warned about using undeclared WINAPI functions
(which usually start with uppercase letter) */
|| (name[0] >= 'A' && name[0] <= 'Z')
#endif
)
tcc_warning("implicit declaration of function '%s'", name);
s = external_global_sym(t, &func_old_type, 0);
}
r = s->r;
/* A symbol that has a register is a local register variable,
which starts out as VT_LOCAL value. */
if ((r & VT_VALMASK) < VT_CONST)
r = (r & ~VT_VALMASK) | VT_LOCAL;
vset(&s->type, r, s->c);
/* Point to s as backpointer (even without r&VT_SYM).
Will be used by at least the x86 inline asm parser for
regvars. */
vtop->sym = s;
if (vtop->r & VT_SYM) {
vtop->c.i = 0;
}
break;
}
/* post operations */
while (1) {
if (tok == TOK_INC || tok == TOK_DEC) {
inc(1, tok);
next();
} else if (tok == '.' || tok == TOK_ARROW || tok == TOK_CDOUBLE) {
int qualifiers;
/* field */
if (tok == TOK_ARROW)
indir();
qualifiers = vtop->type.t & (VT_CONSTANT | VT_VOLATILE);
test_lvalue();
gaddrof();
/* expect pointer on structure */
if ((vtop->type.t & VT_BTYPE) != VT_STRUCT)
expect("struct or union");
if (tok == TOK_CDOUBLE)
expect("field name");
next();
if (tok == TOK_CINT || tok == TOK_CUINT)
expect("field name");
s = find_field(&vtop->type, tok);
if (!s)
tcc_error("field not found: %s", get_tok_str(tok & ~SYM_FIELD, &tokc));
/* add field offset to pointer */
vtop->type = char_pointer_type; /* change type to 'char *' */
vpushi(s->c);
gen_op('+');
/* change type to field type, and set to lvalue */
vtop->type = s->type;
vtop->type.t |= qualifiers;
/* an array is never an lvalue */
if (!(vtop->type.t & VT_ARRAY)) {
vtop->r |= lvalue_type(vtop->type.t);
#ifdef CONFIG_TCC_BCHECK
/* if bound checking, the referenced pointer must be checked */
if (tcc_state->do_bounds_check && (vtop->r & VT_VALMASK) != VT_LOCAL)
vtop->r |= VT_MUSTBOUND;
#endif
}
next();
} else if (tok == '[') {
next();
gexpr();
gen_op('+');
indir();
skip(']');
} else if (tok == '(') {
SValue ret;
Sym *sa;
int nb_args, ret_nregs, ret_align, regsize, variadic;
/* function call */
if ((vtop->type.t & VT_BTYPE) != VT_FUNC) {
/* pointer test (no array accepted) */
if ((vtop->type.t & (VT_BTYPE | VT_ARRAY)) == VT_PTR) {
vtop->type = *pointed_type(&vtop->type);
if ((vtop->type.t & VT_BTYPE) != VT_FUNC)
goto error_func;
} else {
error_func:
expect("function pointer");
}
} else {
vtop->r &= ~VT_LVAL; /* no lvalue */
}
/* get return type */
s = vtop->type.ref;
next();
sa = s->next; /* first parameter */
nb_args = regsize = 0;
ret.r2 = VT_CONST;
/* compute first implicit argument if a structure is returned */
if ((s->type.t & VT_BTYPE) == VT_STRUCT) {
variadic = (s->c == FUNC_ELLIPSIS);
ret_nregs = gfunc_sret(&s->type, variadic, &ret.type,
&ret_align, ®size);
if (!ret_nregs) {
/* get some space for the returned structure */
size = type_size(&s->type, &align);
#ifdef TCC_TARGET_ARM64
/* On arm64, a small struct is return in registers.
It is much easier to write it to memory if we know
that we are allowed to write some extra bytes, so
round the allocated space up to a power of 2: */
if (size < 16)
while (size & (size - 1))
size = (size | (size - 1)) + 1;
#endif
loc = (loc - size) & -align;
ret.type = s->type;
ret.r = VT_LOCAL | VT_LVAL;
/* pass it as 'int' to avoid structure arg passing
problems */
vseti(VT_LOCAL, loc);
ret.c = vtop->c;
nb_args++;
}
} else {
ret_nregs = 1;
ret.type = s->type;
}
if (ret_nregs) {
/* return in register */
if (is_float(ret.type.t)) {
ret.r = reg_fret(ret.type.t);
#ifdef TCC_TARGET_X86_64
if ((ret.type.t & VT_BTYPE) == VT_QFLOAT)
ret.r2 = REG_QRET;
#endif
} else {
#ifndef TCC_TARGET_ARM64
#ifdef TCC_TARGET_X86_64
if ((ret.type.t & VT_BTYPE) == VT_QLONG)
#else
if ((ret.type.t & VT_BTYPE) == VT_LLONG)
#endif
ret.r2 = REG_LRET;
#endif
ret.r = REG_IRET;
}
ret.c.i = 0;
}
if (tok != ')') {
for(;;) {
expr_eq();
gfunc_param_typed(s, sa);
nb_args++;
if (sa)
sa = sa->next;
if (tok == ')')
break;
skip(',');
}
}
if (sa)
tcc_error("too few arguments to function");
skip(')');
gfunc_call(nb_args);
/* return value */
for (r = ret.r + ret_nregs + !ret_nregs; r-- > ret.r;) {
vsetc(&ret.type, r, &ret.c);
vtop->r2 = ret.r2; /* Loop only happens when r2 is VT_CONST */
}
/* handle packed struct return */
if (((s->type.t & VT_BTYPE) == VT_STRUCT) && ret_nregs) {
int addr, offset;
size = type_size(&s->type, &align);
/* We're writing whole regs often, make sure there's enough
space. Assume register size is power of 2. */
if (regsize > align)
align = regsize;
loc = (loc - size) & -align;
addr = loc;
offset = 0;
for (;;) {
vset(&ret.type, VT_LOCAL | VT_LVAL, addr + offset);
vswap();
vstore();
vtop--;
if (--ret_nregs == 0)
break;
offset += regsize;
}
vset(&s->type, VT_LOCAL | VT_LVAL, addr);
}
} else {
break;
}
}
}
ST_FUNC void expr_prod(void)
{
int t;
unary();
while (tok == '*' || tok == '/' || tok == '%') {
t = tok;
next();
unary();
gen_op(t);
}
}
ST_FUNC void expr_sum(void)
{
int t;
expr_prod();
while (tok == '+' || tok == '-') {
t = tok;
next();
expr_prod();
gen_op(t);
}
}
static void expr_shift(void)
{
int t;
expr_sum();
while (tok == TOK_SHL || tok == TOK_SAR) {
t = tok;
next();
expr_sum();
gen_op(t);
}
}
static void expr_cmp(void)
{
int t;
expr_shift();
while ((tok >= TOK_ULE && tok <= TOK_GT) ||
tok == TOK_ULT || tok == TOK_UGE) {
t = tok;
next();
expr_shift();
gen_op(t);
}
}
static void expr_cmpeq(void)
{
int t;
expr_cmp();
while (tok == TOK_EQ || tok == TOK_NE) {
t = tok;
next();
expr_cmp();
gen_op(t);
}
}
static void expr_and(void)
{
expr_cmpeq();
while (tok == '&') {
next();
expr_cmpeq();
gen_op('&');
}
}
static void expr_xor(void)
{
expr_and();
while (tok == '^') {
next();
expr_and();
gen_op('^');
}
}
static void expr_or(void)
{
expr_xor();
while (tok == '|') {
next();
expr_xor();
gen_op('|');
}
}
static void expr_land(void)
{
expr_or();
if (tok == TOK_LAND) {
int t = 0;
for(;;) {
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
CType ctb;
ctb.t = VT_BOOL;
gen_cast(&ctb);
if (vtop->c.i) {
vpop();
} else {
nocode_wanted++;
while (tok == TOK_LAND) {
next();
expr_or();
vpop();
}
nocode_wanted--;
if (t)
gsym(t);
gen_cast(&int_type);
break;
}
} else {
if (!t)
save_regs(1);
t = gvtst(1, t);
}
if (tok != TOK_LAND) {
if (t)
vseti(VT_JMPI, t);
else
vpushi(1);
break;
}
next();
expr_or();
}
}
}
static void expr_lor(void)
{
expr_land();
if (tok == TOK_LOR) {
int t = 0;
for(;;) {
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
CType ctb;
ctb.t = VT_BOOL;
gen_cast(&ctb);
if (!vtop->c.i) {
vpop();
} else {
nocode_wanted++;
while (tok == TOK_LOR) {
next();
expr_land();
vpop();
}
nocode_wanted--;
if (t)
gsym(t);
gen_cast(&int_type);
break;
}
} else {
if (!t)
save_regs(1);
t = gvtst(0, t);
}
if (tok != TOK_LOR) {
if (t)
vseti(VT_JMP, t);
else
vpushi(0);
break;
}
next();
expr_land();
}
}
}
/* Assuming vtop is a value used in a conditional context
(i.e. compared with zero) return 0 if it's false, 1 if
true and -1 if it can't be statically determined. */
static int condition_3way(void)
{
int c = -1;
if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST &&
(!(vtop->r & VT_SYM) ||
!(vtop->sym->type.t & VT_WEAK))) {
CType boolean;
boolean.t = VT_BOOL;
vdup();
gen_cast(&boolean);
c = vtop->c.i;
vpop();
}
return c;
}
static void expr_cond(void)
{
int tt, u, r1, r2, rc, t1, t2, bt1, bt2, islv, c, g;
SValue sv;
CType type, type1, type2;
expr_lor();
if (tok == '?') {
next();
c = condition_3way();
g = (tok == ':' && gnu_ext);
if (c < 0) {
/* needed to avoid having different registers saved in
each branch */
if (is_float(vtop->type.t)) {
rc = RC_FLOAT;
#ifdef TCC_TARGET_X86_64
if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) {
rc = RC_ST0;
}
#endif
} else
rc = RC_INT;
gv(rc);
save_regs(1);
if (g)
gv_dup();
tt = gvtst(1, 0);
} else {
if (!g)
vpop();
tt = 0;
}
if (1) {
if (c == 0)
nocode_wanted++;
if (!g)
gexpr();
type1 = vtop->type;
sv = *vtop; /* save value to handle it later */
vtop--; /* no vpop so that FP stack is not flushed */
skip(':');
u = 0;
if (c < 0)
u = gjmp(0);
gsym(tt);
if (c == 0)
nocode_wanted--;
if (c == 1)
nocode_wanted++;
expr_cond();
if (c == 1)
nocode_wanted--;
type2 = vtop->type;
t1 = type1.t;
bt1 = t1 & VT_BTYPE;
t2 = type2.t;
bt2 = t2 & VT_BTYPE;
/* cast operands to correct type according to ISOC rules */
if (is_float(bt1) || is_float(bt2)) {
if (bt1 == VT_LDOUBLE || bt2 == VT_LDOUBLE) {
type.t = VT_LDOUBLE;
} else if (bt1 == VT_DOUBLE || bt2 == VT_DOUBLE) {
type.t = VT_DOUBLE;
} else {
type.t = VT_FLOAT;
}
} else if (bt1 == VT_LLONG || bt2 == VT_LLONG) {
/* cast to biggest op */
type.t = VT_LLONG;
/* convert to unsigned if it does not fit in a long long */
if ((t1 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_LLONG | VT_UNSIGNED) ||
(t2 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_LLONG | VT_UNSIGNED))
type.t |= VT_UNSIGNED;
} else if (bt1 == VT_PTR || bt2 == VT_PTR) {
/* If one is a null ptr constant the result type
is the other. */
if (is_null_pointer (vtop))
type = type1;
else if (is_null_pointer (&sv))
type = type2;
/* XXX: test pointer compatibility, C99 has more elaborate
rules here. */
else
type = type1;
} else if (bt1 == VT_FUNC || bt2 == VT_FUNC) {
/* XXX: test function pointer compatibility */
type = bt1 == VT_FUNC ? type1 : type2;
} else if (bt1 == VT_STRUCT || bt2 == VT_STRUCT) {
/* XXX: test structure compatibility */
type = bt1 == VT_STRUCT ? type1 : type2;
} else if (bt1 == VT_VOID || bt2 == VT_VOID) {
/* NOTE: as an extension, we accept void on only one side */
type.t = VT_VOID;
} else {
/* integer operations */
type.t = VT_INT;
/* convert to unsigned if it does not fit in an integer */
if ((t1 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_INT | VT_UNSIGNED) ||
(t2 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_INT | VT_UNSIGNED))
type.t |= VT_UNSIGNED;
}
/* keep structs lvalue by transforming `(expr ? a : b)` to `*(expr ? &a : &b)` so
that `(expr ? a : b).mem` does not error with "lvalue expected" */
islv = (vtop->r & VT_LVAL) && (sv.r & VT_LVAL) && VT_STRUCT == (type.t & VT_BTYPE);
islv &= c < 0;
/* now we convert second operand */
if (c != 1) {
gen_cast(&type);
if (islv) {
mk_pointer(&vtop->type);
gaddrof();
} else if (VT_STRUCT == (vtop->type.t & VT_BTYPE))
gaddrof();
}
rc = RC_INT;
if (is_float(type.t)) {
rc = RC_FLOAT;
#ifdef TCC_TARGET_X86_64
if ((type.t & VT_BTYPE) == VT_LDOUBLE) {
rc = RC_ST0;
}
#endif
} else if ((type.t & VT_BTYPE) == VT_LLONG) {
/* for long longs, we use fixed registers to avoid having
to handle a complicated move */
rc = RC_IRET;
}
tt = r2 = 0;
if (c < 0) {
r2 = gv(rc);
tt = gjmp(0);
}
gsym(u);
/* this is horrible, but we must also convert first
operand */
if (c != 0) {
*vtop = sv;
gen_cast(&type);
if (islv) {
mk_pointer(&vtop->type);
gaddrof();
} else if (VT_STRUCT == (vtop->type.t & VT_BTYPE))
gaddrof();
}
if (c < 0) {
r1 = gv(rc);
move_reg(r2, r1, type.t);
vtop->r = r2;
gsym(tt);
if (islv)
indir();
}
}
}
}
static void expr_eq(void)
{
int t;
expr_cond();
if (tok == '=' ||
(tok >= TOK_A_MOD && tok <= TOK_A_DIV) ||
tok == TOK_A_XOR || tok == TOK_A_OR ||
tok == TOK_A_SHL || tok == TOK_A_SAR) {
test_lvalue();
t = tok;
next();
if (t == '=') {
expr_eq();
} else {
vdup();
expr_eq();
gen_op(t & 0x7f);
}
vstore();
}
}
ST_FUNC void gexpr(void)
{
while (1) {
expr_eq();
if (tok != ',')
break;
vpop();
next();
}
}
/* parse a constant expression and return value in vtop. */
static void expr_const1(void)
{
const_wanted++;
expr_cond();
const_wanted--;
}
#if HAVE_LONG_LONG
/* parse an integer constant and return its value. */
static inline int64_t expr_const64(void)
{
int64_t c;
expr_const1();
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST)
expect("constant expression");
c = vtop->c.i;
vpop();
return c;
}
#else
/* parse an integer constant and return its value. */
static inline int32_t expr_const32(void)
{
int32_t c;
expr_const1();
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST)
expect("constant expression");
c = vtop->c.i;
vpop();
return c;
}
#endif
/* parse an integer constant and return its value.
Complain if it doesn't fit 32bit (signed or unsigned). */
ST_FUNC int expr_const(void)
{
int c;
#if HAVE_LONG_LONG
int64_t wc = expr_const64();
#else
int32_t wc = expr_const32();
#endif
c = wc;
if (c != wc && (unsigned)c != wc)
tcc_error("constant exceeds 32 bit");
return c;
}
/* return the label token if current token is a label, otherwise
return zero */
static int is_label(void)
{
int last_tok;
/* fast test first */
if (tok < TOK_UIDENT)
return 0;
/* no need to save tokc because tok is an identifier */
last_tok = tok;
next();
if (tok == ':') {
return last_tok;
} else {
unget_tok(last_tok);
return 0;
}
}
#ifndef TCC_TARGET_ARM64
static void gfunc_return(CType *func_type)
{
if ((func_type->t & VT_BTYPE) == VT_STRUCT) {
CType type, ret_type;
int ret_align, ret_nregs, regsize;
ret_nregs = gfunc_sret(func_type, func_var, &ret_type,
&ret_align, ®size);
if (0 == ret_nregs) {
/* if returning structure, must copy it to implicit
first pointer arg location */
type = *func_type;
mk_pointer(&type);
vset(&type, VT_LOCAL | VT_LVAL, func_vc);
indir();
vswap();
/* copy structure value to pointer */
vstore();
} else {
/* returning structure packed into registers */
int r, size, addr, align;
size = type_size(func_type,&align);
if ((vtop->r != (VT_LOCAL | VT_LVAL) ||
(vtop->c.i & (ret_align-1)))
&& (align & (ret_align-1))) {
loc = (loc - size) & -ret_align;
addr = loc;
type = *func_type;
vset(&type, VT_LOCAL | VT_LVAL, addr);
vswap();
vstore();
vpop();
vset(&ret_type, VT_LOCAL | VT_LVAL, addr);
}
vtop->type = ret_type;
if (is_float(ret_type.t))
r = rc_fret(ret_type.t);
else
r = RC_IRET;
if (ret_nregs == 1)
gv(r);
else {
for (;;) {
vdup();
gv(r);
vpop();
if (--ret_nregs == 0)
break;
/* We assume that when a structure is returned in multiple
registers, their classes are consecutive values of the
suite s(n) = 2^n */
r <<= 1;
vtop->c.i += regsize;
}
}
}
} else if (is_float(func_type->t)) {
gv(rc_fret(func_type->t));
} else {
gv(RC_IRET);
}
vtop--; /* NOT vpop() because on x86 it would flush the fp stack */
}
#endif
static int case_cmp(const void *pa, const void *pb)
{
#if HAVE_LONG_LONG
int64_t a = (*(struct case_t**) pa)->v1;
int64_t b = (*(struct case_t**) pb)->v1;
#else
int32_t a = (*(struct case_t**) pa)->v1;
int32_t b = (*(struct case_t**) pb)->v1;
#endif
return a < b ? -1 : a > b;
}
static void gcase(struct case_t **base, int len, int *bsym)
{
struct case_t *p;
int e;
int ll = (vtop->type.t & VT_BTYPE) == VT_LLONG;
gv(RC_INT);
while (len > 4) {
/* binary search */
p = base[len/2];
vdup();
if (ll)
vpushll(p->v2);
else
vpushi(p->v2);
gen_op(TOK_LE);
e = gtst(1, 0);
vdup();
if (ll)
vpushll(p->v1);
else
vpushi(p->v1);
gen_op(TOK_GE);
gtst_addr(0, p->sym); /* v1 <= x <= v2 */
/* x < v1 */
gcase(base, len/2, bsym);
if (cur_switch->def_sym)
gjmp_addr(cur_switch->def_sym);
else
*bsym = gjmp(*bsym);
/* x > v2 */
gsym(e);
e = len/2 + 1;
base += e; len -= e;
}
/* linear scan */
while (len--) {
p = *base++;
vdup();
if (ll)
vpushll(p->v2);
else
vpushi(p->v2);
if (p->v1 == p->v2) {
gen_op(TOK_EQ);
gtst_addr(0, p->sym);
} else {
gen_op(TOK_LE);
e = gtst(1, 0);
vdup();
if (ll)
vpushll(p->v1);
else
vpushi(p->v1);
gen_op(TOK_GE);
gtst_addr(0, p->sym);
gsym(e);
}
}
}
static void block(int *bsym, int *csym, int is_expr)
{
int a, b, c, d, cond;
Sym *s;
/* generate line number info */
if (tcc_state->do_debug)
tcc_debug_line(tcc_state);
if (is_expr) {
/* default return value is (void) */
vpushi(0);
vtop->type.t = VT_VOID;
}
if (tok == TOK_IF) {
/* if test */
int saved_nocode_wanted = nocode_wanted;
next();
skip('(');
gexpr();
skip(')');
cond = condition_3way();
if (cond == 1)
a = 0, vpop();
else
a = gvtst(1, 0);
if (cond == 0)
nocode_wanted |= 0x20000000;
block(bsym, csym, 0);
if (cond != 1)
nocode_wanted = saved_nocode_wanted;
c = tok;
if (c == TOK_ELSE) {
next();
d = gjmp(0);
gsym(a);
if (cond == 1)
nocode_wanted |= 0x20000000;
block(bsym, csym, 0);
gsym(d); /* patch else jmp */
if (cond != 0)
nocode_wanted = saved_nocode_wanted;
} else
gsym(a);
} else if (tok == TOK_WHILE) {
int saved_nocode_wanted;
nocode_wanted &= ~0x20000000;
next();
d = ind;
vla_sp_restore();
skip('(');
gexpr();
skip(')');
a = gvtst(1, 0);
b = 0;
++local_scope;
saved_nocode_wanted = nocode_wanted;
block(&a, &b, 0);
nocode_wanted = saved_nocode_wanted;
--local_scope;
gjmp_addr(d);
gsym(a);
gsym_addr(b, d);
} else if (tok == '{') {
Sym *llabel;
int block_vla_sp_loc = vla_sp_loc, saved_vlas_in_scope = vlas_in_scope;
next();
/* record local declaration stack position */
s = local_stack;
llabel = local_label_stack;
++local_scope;
/* handle local labels declarations */
if (tok == TOK_LABEL) {
next();
for(;;) {
if (tok < TOK_UIDENT)
expect("label identifier");
label_push(&local_label_stack, tok, LABEL_DECLARED);
next();
if (tok == ',') {
next();
} else {
skip(';');
break;
}
}
}
while (tok != '}') {
if ((a = is_label()))
unget_tok(a);
else
decl(VT_LOCAL);
if (tok != '}') {
if (is_expr)
vpop();
block(bsym, csym, is_expr);
}
}
/* pop locally defined labels */
label_pop(&local_label_stack, llabel);
/* pop locally defined symbols */
--local_scope;
/* In the is_expr case (a statement expression is finished here),
vtop might refer to symbols on the local_stack. Either via the
type or via vtop->sym. We can't pop those nor any that in turn
might be referred to. To make it easier we don't roll back
any symbols in that case; some upper level call to block() will
do that. We do have to remove such symbols from the lookup
tables, though. sym_pop will do that. */
sym_pop(&local_stack, s, is_expr);
/* Pop VLA frames and restore stack pointer if required */
if (vlas_in_scope > saved_vlas_in_scope) {
vla_sp_loc = saved_vlas_in_scope ? block_vla_sp_loc : vla_sp_root_loc;
vla_sp_restore();
}
vlas_in_scope = saved_vlas_in_scope;
next();
} else if (tok == TOK_RETURN) {
next();
if (tok != ';') {
gexpr();
gen_assign_cast(&func_vt);
gfunc_return(&func_vt);
}
skip(';');
/* jump unless last stmt in top-level block */
if (tok != '}' || local_scope != 1)
rsym = gjmp(rsym);
nocode_wanted |= 0x20000000;
} else if (tok == TOK_BREAK) {
/* compute jump */
if (!bsym)
tcc_error("cannot break");
*bsym = gjmp(*bsym);
next();
skip(';');
nocode_wanted |= 0x20000000;
} else if (tok == TOK_CONTINUE) {
/* compute jump */
if (!csym)
tcc_error("cannot continue");
vla_sp_restore_root();
*csym = gjmp(*csym);
next();
skip(';');
} else if (tok == TOK_FOR) {
int e;
int saved_nocode_wanted;
nocode_wanted &= ~0x20000000;
next();
skip('(');
s = local_stack;
++local_scope;
if (tok != ';') {
/* c99 for-loop init decl? */
if (!decl0(VT_LOCAL, 1, NULL)) {
/* no, regular for-loop init expr */
gexpr();
vpop();
}
}
skip(';');
d = ind;
c = ind;
vla_sp_restore();
a = 0;
b = 0;
if (tok != ';') {
gexpr();
a = gvtst(1, 0);
}
skip(';');
if (tok != ')') {
e = gjmp(0);
c = ind;
vla_sp_restore();
gexpr();
vpop();
gjmp_addr(d);
gsym(e);
}
skip(')');
saved_nocode_wanted = nocode_wanted;
block(&a, &b, 0);
nocode_wanted = saved_nocode_wanted;
gjmp_addr(c);
gsym(a);
gsym_addr(b, c);
--local_scope;
sym_pop(&local_stack, s, 0);
} else
if (tok == TOK_DO) {
int saved_nocode_wanted;
nocode_wanted &= ~0x20000000;
next();
a = 0;
b = 0;
d = ind;
vla_sp_restore();
saved_nocode_wanted = nocode_wanted;
block(&a, &b, 0);
skip(TOK_WHILE);
skip('(');
gsym(b);
gexpr();
c = gvtst(0, 0);
gsym_addr(c, d);
nocode_wanted = saved_nocode_wanted;
skip(')');
gsym(a);
skip(';');
} else
if (tok == TOK_SWITCH) {
struct switch_t *saved, sw;
int saved_nocode_wanted = nocode_wanted;
SValue switchval;
next();
skip('(');
gexpr();
skip(')');
switchval = *vtop--;
a = 0;
b = gjmp(0); /* jump to first case */
sw.p = NULL; sw.n = 0; sw.def_sym = 0;
saved = cur_switch;
cur_switch = &sw;
block(&a, csym, 0);
nocode_wanted = saved_nocode_wanted;
a = gjmp(a); /* add implicit break */
/* case lookup */
gsym(b);
qsort(sw.p, sw.n, sizeof(void*), case_cmp);
for (b = 1; b < sw.n; b++)
if (sw.p[b - 1]->v2 >= sw.p[b]->v1)
tcc_error("duplicate case value");
/* Our switch table sorting is signed, so the compared
value needs to be as well when it's 64bit. */
if ((switchval.type.t & VT_BTYPE) == VT_LLONG)
switchval.type.t &= ~VT_UNSIGNED;
vpushv(&switchval);
gcase(sw.p, sw.n, &a);
vpop();
if (sw.def_sym)
gjmp_addr(sw.def_sym);
dynarray_reset(&sw.p, &sw.n);
cur_switch = saved;
/* break label */
gsym(a);
} else
if (tok == TOK_CASE) {
struct case_t *cr = tcc_malloc(sizeof(struct case_t));
if (!cur_switch)
expect("switch");
nocode_wanted &= ~0x20000000;
next();
#if HAVE_LONG_LONG
cr->v1 = cr->v2 = expr_const64();
#else
cr->v1 = cr->v2 = expr_const32();
#endif
if (gnu_ext && tok == TOK_DOTS) {
next();
#if HAVE_LONG_LONG
cr->v2 = expr_const64();
#else
cr->v2 = expr_const32();
#endif
if (cr->v2 < cr->v1)
tcc_warning("empty case range");
}
cr->sym = ind;
dynarray_add(&cur_switch->p, &cur_switch->n, cr);
skip(':');
is_expr = 0;
goto block_after_label;
} else
if (tok == TOK_DEFAULT) {
next();
skip(':');
if (!cur_switch)
expect("switch");
if (cur_switch->def_sym)
tcc_error("too many 'default'");
cur_switch->def_sym = ind;
is_expr = 0;
goto block_after_label;
} else
if (tok == TOK_GOTO) {
next();
if (tok == '*' && gnu_ext) {
/* computed goto */
next();
gexpr();
if ((vtop->type.t & VT_BTYPE) != VT_PTR)
expect("pointer");
ggoto();
} else if (tok >= TOK_UIDENT) {
s = label_find(tok);
/* put forward definition if needed */
if (!s) {
s = label_push(&global_label_stack, tok, LABEL_FORWARD);
} else {
if (s->r == LABEL_DECLARED)
s->r = LABEL_FORWARD;
}
vla_sp_restore_root();
if (s->r & LABEL_FORWARD)
s->jnext = gjmp(s->jnext);
else
gjmp_addr(s->jnext);
next();
} else {
expect("label identifier");
}
skip(';');
} else if (tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3) {
asm_instr();
} else {
b = is_label();
if (b) {
/* label case */
next();
s = label_find(b);
if (s) {
if (s->r == LABEL_DEFINED)
tcc_error("duplicate label '%s'", get_tok_str(s->v, NULL));
gsym(s->jnext);
s->r = LABEL_DEFINED;
} else {
s = label_push(&global_label_stack, b, LABEL_DEFINED);
}
s->jnext = ind;
vla_sp_restore();
/* we accept this, but it is a mistake */
block_after_label:
nocode_wanted &= ~0x20000000;
if (tok == '}') {
tcc_warning("deprecated use of label at end of compound statement");
} else {
if (is_expr)
vpop();
block(bsym, csym, is_expr);
}
} else {
/* expression case */
if (tok != ';') {
if (is_expr) {
vpop();
gexpr();
} else {
gexpr();
vpop();
}
}
skip(';');
}
}
}
/* This skips over a stream of tokens containing balanced {} and ()
pairs, stopping at outer ',' ';' and '}'. If STR then allocates
and stores the skipped tokens in *STR. This doesn't check if
() and {} are nested correctly, i.e. "({)}" is accepted. */
static void skip_or_save_block(TokenString **str)
{
int level = 0;
if (str)
*str = tok_str_alloc();
while ((level > 0 || (tok != '}' && tok != ',' && tok != ';'))) {
int t;
if (tok == TOK_EOF) {
if (str || level > 0)
tcc_error("unexpected end of file");
else
break;
}
if (str)
tok_str_add_tok(*str);
t = tok;
next();
if (t == '{' || t == '(') {
level++;
} else if (t == '}' || t == ')') {
level--;
if (level == 0)
break;
}
}
if (str) {
tok_str_add(*str, -1);
tok_str_add(*str, 0);
}
}
#define EXPR_CONST 1
#define EXPR_ANY 2
static void parse_init_elem(int expr_type)
{
int saved_global_expr;
switch(expr_type) {
case EXPR_CONST:
/* compound literals must be allocated globally in this case */
saved_global_expr = global_expr;
global_expr = 1;
expr_const1();
global_expr = saved_global_expr;
/* NOTE: symbols are accepted, as well as lvalue for anon symbols
(compound literals). */
if (((vtop->r & (VT_VALMASK | VT_LVAL)) != VT_CONST
&& ((vtop->r & (VT_SYM|VT_LVAL)) != (VT_SYM|VT_LVAL)
|| vtop->sym->v < SYM_FIRST_ANOM))
#ifdef TCC_TARGET_PE
|| (vtop->type.t & VT_IMPORT)
#endif
)
tcc_error("initializer element is not constant");
break;
case EXPR_ANY:
expr_eq();
break;
}
}
/* put zeros for variable based init */
static void init_putz(Section *sec, unsigned long c, int size)
{
if (sec) {
/* nothing to do because globals are already set to zero */
} else {
#if BOOTSTRAP && __arm__
vpush_global_sym(&func_old_type, TOK___memset);
#else
vpush_global_sym(&func_old_type, TOK_memset);
#endif
vseti(VT_LOCAL, c);
#ifdef TCC_TARGET_ARM
vpushs(size);
vpushi(0);
#else
vpushi(0);
vpushs(size);
#endif
gfunc_call(3);
}
}
/* t is the array or struct type. c is the array or struct
address. cur_field is the pointer to the current
field, for arrays the 'c' member contains the current start
index. 'size_only' is true if only size info is needed (only used
in arrays). al contains the already initialized length of the
current container (starting at c). This returns the new length of that. */
static int decl_designator(CType *type, Section *sec, unsigned long c,
Sym **cur_field, int size_only, int al)
{
Sym *s, *f;
int index, index_last, align, l, nb_elems, elem_size;
unsigned long corig = c;
elem_size = 0;
nb_elems = 1;
if (gnu_ext && (l = is_label()) != 0)
goto struct_field;
/* NOTE: we only support ranges for last designator */
while (nb_elems == 1 && (tok == '[' || tok == '.')) {
if (tok == '[') {
if (!(type->t & VT_ARRAY))
expect("array type");
next();
index = index_last = expr_const();
if (tok == TOK_DOTS && gnu_ext) {
next();
index_last = expr_const();
}
skip(']');
s = type->ref;
if (index < 0 || (s->c >= 0 && index_last >= s->c) ||
index_last < index)
tcc_error("invalid index");
if (cur_field)
(*cur_field)->c = index_last;
type = pointed_type(type);
elem_size = type_size(type, &align);
c += index * elem_size;
nb_elems = index_last - index + 1;
} else {
next();
l = tok;
struct_field:
next();
if ((type->t & VT_BTYPE) != VT_STRUCT)
expect("struct/union type");
f = find_field(type, l);
if (!f)
expect("field");
if (cur_field)
*cur_field = f;
type = &f->type;
c += f->c;
}
cur_field = NULL;
}
if (!cur_field) {
if (tok == '=') {
next();
} else if (!gnu_ext) {
expect("=");
}
} else {
if (type->t & VT_ARRAY) {
index = (*cur_field)->c;
if (type->ref->c >= 0 && index >= type->ref->c)
tcc_error("index too large");
type = pointed_type(type);
c += index * type_size(type, &align);
} else {
f = *cur_field;
while (f && (f->v & SYM_FIRST_ANOM) && (f->type.t & VT_BITFIELD))
*cur_field = f = f->next;
if (!f)
tcc_error("too many field init");
type = &f->type;
c += f->c;
}
}
/* must put zero in holes (note that doing it that way
ensures that it even works with designators) */
if (!size_only && c - corig > al)
init_putz(sec, corig + al, c - corig - al);
decl_initializer(type, sec, c, 0, size_only);
/* XXX: make it more general */
if (!size_only && nb_elems > 1) {
unsigned long c_end;
uint8_t *src, *dst;
int i;
if (!sec) {
vset(type, VT_LOCAL|VT_LVAL, c);
for (i = 1; i < nb_elems; i++) {
vset(type, VT_LOCAL|VT_LVAL, c + elem_size * i);
vswap();
vstore();
}
vpop();
} else {
c_end = c + nb_elems * elem_size;
if (c_end > sec->data_allocated)
section_realloc(sec, c_end);
src = sec->data + c;
dst = src;
for(i = 1; i < nb_elems; i++) {
dst += elem_size;
memcpy(dst, src, elem_size);
}
}
}
c += nb_elems * type_size(type, &align);
if (c - corig > al)
al = c - corig;
return al;
}
#if BOOTSTRAP && HAVE_FLOAT
struct long_double
{
int one;
int two;
int three;
};
#endif
/* store a value or an expression directly in global data or in local array */
static void init_putv(CType *type, Section *sec, unsigned long c)
{
int bt, bit_pos, bit_size;
void *ptr;
unsigned long long bit_mask;
CType dtype;
dtype = *type;
dtype.t &= ~VT_CONSTANT; /* need to do that to avoid false warning */
if (sec) {
int size, align;
/* XXX: not portable */
/* XXX: generate error if incorrect relocation */
gen_assign_cast(&dtype);
bt = type->t & VT_BTYPE;
size = type_size(type, &align);
section_reserve(sec, c + size);
ptr = sec->data + c;
/* XXX: make code faster ? */
if (!(type->t & VT_BITFIELD)) {
bit_pos = 0;
bit_size = PTR_SIZE * 8;
bit_mask = -1LL;
} else {
bit_pos = (vtop->type.t >> VT_STRUCT_SHIFT) & 0x3f;
bit_size = (vtop->type.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f;
bit_mask = (1LL << bit_size) - 1;
}
if ((vtop->r & (VT_SYM|VT_CONST)) == (VT_SYM|VT_CONST) &&
vtop->sym->v >= SYM_FIRST_ANOM &&
/* XXX This rejects compound literals like
'(void *){ptr}'. The problem is that '&sym' is
represented the same way, which would be ruled out
by the SYM_FIRST_ANOM check above, but also '"string"'
in 'char *p = "string"' is represented the same
with the type being VT_PTR and the symbol being an
anonymous one. That is, there's no difference in vtop
between '(void *){x}' and '&(void *){x}'. Ignore
pointer typed entities here. Hopefully no real code
will every use compound literals with scalar type. */
(vtop->type.t & VT_BTYPE) != VT_PTR) {
/* These come from compound literals, memcpy stuff over. */
Section *ssec;
ElfW(Sym) *esym;
ElfW_Rel *rel;
esym = &((ElfW(Sym) *)symtab_section->data)[vtop->sym->c];
ssec = tcc_state->sections[esym->st_shndx];
memmove (ptr, ssec->data + esym->st_value, size);
if (ssec->reloc) {
/* We need to copy over all memory contents, and that
includes relocations. Use the fact that relocs are
created it order, so look from the end of relocs
until we hit one before the copied region. */
int num_relocs = ssec->reloc->data_offset / sizeof(*rel);
rel = (ElfW_Rel*)(ssec->reloc->data + ssec->reloc->data_offset);
while (num_relocs--) {
rel--;
if (rel->r_offset >= esym->st_value + size)
continue;
if (rel->r_offset < esym->st_value)
break;
/* Note: if the same fields are initialized multiple
times (possible with designators) then we possibly
add multiple relocations for the same offset here.
That would lead to wrong code, the last reloc needs
to win. We clean this up later after the whole
initializer is parsed. */
put_elf_reloca(symtab_section, sec,
c + rel->r_offset - esym->st_value,
ELFW(R_TYPE)(rel->r_info),
ELFW(R_SYM)(rel->r_info),
#if PTR_SIZE == 8
rel->r_addend
#else
0
#endif
);
}
}
} else {
if ((vtop->r & VT_SYM) &&
(bt == VT_BYTE ||
bt == VT_SHORT ||
bt == VT_DOUBLE ||
bt == VT_LDOUBLE ||
#if PTR_SIZE == 8
(bt == VT_LLONG && bit_size != 64) ||
bt == VT_INT
#else
bt == VT_LLONG ||
(bt == VT_INT && bit_size != 32)
#endif
))
tcc_error("initializer element is not computable at load time");
switch(bt) {
/* XXX: when cross-compiling we assume that each type has the
same representation on host and target, which is likely to
be wrong in the case of long double */
case VT_BOOL:
vtop->c.i = (vtop->c.i != 0);
case VT_BYTE:
*(char *)ptr |= (vtop->c.i & bit_mask) << bit_pos;
break;
case VT_SHORT:
*(short *)ptr |= (vtop->c.i & bit_mask) << bit_pos;
break;
case VT_FLOAT:
#if HAVE_FLOAT
#if !(BOOTSTRAP && __arm__)
*(float*)ptr = vtop->c.f;
#else
{
long *lptr = ptr;
*lptr = vtop->c.f;
}
#endif
#endif
break;
case VT_DOUBLE:
#if HAVE_FLOAT
#if !(BOOTSTRAP && __arm__)
*(double *)ptr = vtop->c.d;
#else
{
long long *llptr = ptr;
*llptr = vtop->c.d;
}
#endif
#endif
break;
case VT_LDOUBLE:
#if HAVE_FLOAT
if (sizeof(long double) == LDOUBLE_SIZE)
#if !(BOOTSTRAP && __arm__)
*(long double *)ptr = vtop->c.ld;
#else
{
// XXX TODO: breaks on mescc/mes-tcc based build
// maybe disable with HAVE_LONG_DOUBLE?
//struct long_double *ldptr = ptr;
//struct long_double tmp = (struct long_double)vtop->c.ld;
//*ldptr = (struct long_double)tmp;
}
#endif
else if (sizeof(double) == LDOUBLE_SIZE)
*(double *)ptr = (double)vtop->c.ld;
#if (defined __i386__ || defined __x86_64__) && (defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64)
else if (sizeof (long double) >= 10)
memcpy(memset(ptr, 0, LDOUBLE_SIZE), &vtop->c.ld, 10);
#if defined (__TINYC__)
else if (sizeof (long double) == sizeof (double))
__asm__("fldl %1\nfstpt %0\n" : "=m"
(memset(ptr, 0, LDOUBLE_SIZE), ptr) : "m" (vtop->c.ld));
#endif
#endif
else
tcc_error("can't cross compile long double constants");
#endif
break;
#if PTR_SIZE != 8
case VT_LLONG:
*(long long *)ptr |= (vtop->c.i & bit_mask) << bit_pos;
break;
#else
case VT_LLONG:
#endif
case VT_PTR:
{
addr_t val = (vtop->c.i & bit_mask) << bit_pos;
#if PTR_SIZE == 8
if (vtop->r & VT_SYM)
greloca(sec, vtop->sym, c, R_DATA_PTR, val);
else
*(addr_t *)ptr |= val;
#else
if (vtop->r & VT_SYM)
greloc(sec, vtop->sym, c, R_DATA_PTR);
*(addr_t *)ptr |= val;
#endif
break;
}
default:
{
int val = (vtop->c.i & bit_mask) << bit_pos;
#if PTR_SIZE == 8
if (vtop->r & VT_SYM)
greloca(sec, vtop->sym, c, R_DATA_PTR, val);
else
*(int *)ptr |= val;
#else
if (vtop->r & VT_SYM)
greloc(sec, vtop->sym, c, R_DATA_PTR);
*(int *)ptr |= val;
#endif
break;
}
}
}
vtop--;
} else {
vset(&dtype, VT_LOCAL|VT_LVAL, c);
vswap();
vstore();
vpop();
}
}
/* 't' contains the type and storage info. 'c' is the offset of the
object in section 'sec'. If 'sec' is NULL, it means stack based
allocation. 'first' is true if array '{' must be read (multi
dimension implicit array init handling). 'size_only' is true if
size only evaluation is wanted (only for arrays). */
static void decl_initializer(CType *type, Section *sec, unsigned long c,
int first, int size_only)
{
int len, n, no_oblock, nb, i;
int size1, align1;
int have_elem;
Sym *s, *f;
Sym indexsym;
CType *t1;
/* If we currently are at an '}' or ',' we have read an initializer
element in one of our callers, and not yet consumed it. */
have_elem = tok == '}' || tok == ',';
if (!have_elem && tok != '{' &&
/* In case of strings we have special handling for arrays, so
don't consume them as initializer value (which would commit them
to some anonymous symbol). */
tok != TOK_LSTR && tok != TOK_STR &&
!size_only) {
parse_init_elem(!sec ? EXPR_ANY : EXPR_CONST);
have_elem = 1;
}
if (have_elem &&
!(type->t & VT_ARRAY) &&
/* Use i_c_parameter_t, to strip toplevel qualifiers.
The source type might have VT_CONSTANT set, which is
of course assignable to non-const elements. */
is_compatible_parameter_types(type, &vtop->type)) {
init_putv(type, sec, c);
} else if (type->t & VT_ARRAY) {
s = type->ref;
n = s->c;
t1 = pointed_type(type);
size1 = type_size(t1, &align1);
no_oblock = 1;
if ((first && tok != TOK_LSTR && tok != TOK_STR) ||
tok == '{') {
if (tok != '{')
tcc_error("character array initializer must be a literal,"
" optionally enclosed in braces");
skip('{');
no_oblock = 0;
}
/* only parse strings here if correct type (otherwise: handle
them as ((w)char *) expressions */
if ((tok == TOK_LSTR &&
#ifdef TCC_TARGET_PE
(t1->t & VT_BTYPE) == VT_SHORT && (t1->t & VT_UNSIGNED)
#else
(t1->t & VT_BTYPE) == VT_INT
#endif
) || (tok == TOK_STR && (t1->t & VT_BTYPE) == VT_BYTE)) {
len = 0;
while (tok == TOK_STR || tok == TOK_LSTR) {
int cstr_len, ch;
/* compute maximum number of chars wanted */
if (tok == TOK_STR)
cstr_len = tokc.str.size;
else
cstr_len = tokc.str.size / sizeof(nwchar_t);
cstr_len--;
nb = cstr_len;
if (n >= 0 && nb > (n - len))
nb = n - len;
if (!size_only) {
if (cstr_len > nb)
tcc_warning("initializer-string for array is too long");
/* in order to go faster for common case (char
string in global variable, we handle it
specifically */
if (sec && tok == TOK_STR && size1 == 1) {
memcpy(sec->data + c + len, tokc.str.data, nb);
} else {
for(i=0;i<nb;i++) {
if (tok == TOK_STR)
ch = ((unsigned char *)tokc.str.data)[i];
else
ch = ((nwchar_t *)tokc.str.data)[i];
vpushi(ch);
init_putv(t1, sec, c + (len + i) * size1);
}
}
}
len += nb;
next();
}
/* only add trailing zero if enough storage (no
warning in this case since it is standard) */
if (n < 0 || len < n) {
if (!size_only) {
vpushi(0);
init_putv(t1, sec, c + (len * size1));
}
len++;
}
len *= size1;
} else {
indexsym.c = 0;
f = &indexsym;
do_init_list:
len = 0;
while (tok != '}' || have_elem) {
len = decl_designator(type, sec, c, &f, size_only, len);
have_elem = 0;
if (type->t & VT_ARRAY) {
++indexsym.c;
/* special test for multi dimensional arrays (may not
be strictly correct if designators are used at the
same time) */
if (no_oblock && len >= n*size1)
break;
} else {
if (s->type.t == TOK_UNION)
f = NULL;
else
f = f->next;
if (no_oblock && f == NULL)
break;
}
if (tok == '}')
break;
skip(',');
}
}
/* put zeros at the end */
if (!size_only && len < n*size1)
init_putz(sec, c + len, n*size1 - len);
if (!no_oblock)
skip('}');
/* patch type size if needed, which happens only for array types */
if (n < 0)
s->c = size1 == 1 ? len : ((len + size1 - 1)/size1);
} else if ((type->t & VT_BTYPE) == VT_STRUCT) {
size1 = 1;
no_oblock = 1;
if (first || tok == '{') {
skip('{');
no_oblock = 0;
}
s = type->ref;
f = s->next;
n = s->c;
goto do_init_list;
} else if (tok == '{') {
next();
decl_initializer(type, sec, c, first, size_only);
skip('}');
} else if (size_only) {
/* If we supported only ISO C we wouldn't have to accept calling
this on anything than an array size_only==1 (and even then
only on the outermost level, so no recursion would be needed),
because initializing a flex array member isn't supported.
But GNU C supports it, so we need to recurse even into
subfields of structs and arrays when size_only is set. */
/* just skip expression */
do {
skip_or_save_block(NULL);
} while (tok != '}' && tok != ',' && tok != -1);
} else {
if (!have_elem) {
/* This should happen only when we haven't parsed
the init element above for fear of committing a
string constant to memory too early. */
if (tok != TOK_STR && tok != TOK_LSTR)
expect("string constant");
parse_init_elem(!sec ? EXPR_ANY : EXPR_CONST);
}
init_putv(type, sec, c);
}
}
/* parse an initializer for type 't' if 'has_init' is non zero, and
allocate space in local or global data space ('r' is either
VT_LOCAL or VT_CONST). If 'v' is non zero, then an associated
variable 'v' of scope 'scope' is declared before initializers
are parsed. If 'v' is zero, then a reference to the new object
is put in the value stack. If 'has_init' is 2, a special parsing
is done to handle string constants. */
static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
int has_init, int v, int scope)
{
int size, align, addr;
ParseState saved_parse_state = {0};
TokenString *init_str = NULL;
Section *sec;
Sym *flexible_array;
flexible_array = NULL;
if ((type->t & VT_BTYPE) == VT_STRUCT) {
Sym *field = type->ref->next;
if (field) {
while (field->next)
field = field->next;
if (field->type.t & VT_ARRAY && field->type.ref->c < 0)
flexible_array = field;
}
}
size = type_size(type, &align);
/* If unknown size, we must evaluate it before
evaluating initializers because
initializers can generate global data too
(e.g. string pointers or ISOC99 compound
literals). It also simplifies local
initializers handling */
if (size < 0 || (flexible_array && has_init)) {
if (!has_init)
tcc_error("unknown type size");
/* get all init string */
if (has_init == 2) {
init_str = tok_str_alloc();
/* only get strings */
while (tok == TOK_STR || tok == TOK_LSTR) {
tok_str_add_tok(init_str);
next();
}
tok_str_add(init_str, -1);
tok_str_add(init_str, 0);
} else {
skip_or_save_block(&init_str);
}
/* compute size */
save_parse_state(&saved_parse_state);
begin_macro(init_str, 1);
next();
decl_initializer(type, NULL, 0, 1, 1);
/* prepare second initializer parsing */
macro_ptr = init_str->str;
next();
/* if still unknown size, error */
size = type_size(type, &align);
if (size < 0)
tcc_error("unknown type size");
}
/* If there's a flex member and it was used in the initializer
adjust size. */
if (flexible_array &&
flexible_array->type.ref->c > 0)
size += flexible_array->type.ref->c
* pointed_size(&flexible_array->type);
/* take into account specified alignment if bigger */
if (ad->a.aligned) {
int speca = 1 << (ad->a.aligned - 1);
if (speca > align)
align = speca;
} else if (ad->a.packed) {
align = 1;
}
if ((r & VT_VALMASK) == VT_LOCAL) {
sec = NULL;
#ifdef CONFIG_TCC_BCHECK
if (tcc_state->do_bounds_check && (type->t & VT_ARRAY)) {
loc--;
}
#endif
loc = (loc - size) & -align;
addr = loc;
#ifdef CONFIG_TCC_BCHECK
/* handles bounds */
/* XXX: currently, since we do only one pass, we cannot track
'&' operators, so we add only arrays */
if (tcc_state->do_bounds_check && (type->t & VT_ARRAY)) {
addr_t *bounds_ptr;
/* add padding between regions */
loc--;
/* then add local bound info */
bounds_ptr = section_ptr_add(lbounds_section, 2 * sizeof(addr_t));
bounds_ptr[0] = addr;
bounds_ptr[1] = size;
}
#endif
if (v) {
/* local variable */
#ifdef CONFIG_TCC_ASM
if (ad->asm_label) {
int reg = asm_parse_regvar(ad->asm_label);
if (reg >= 0)
r = (r & ~VT_VALMASK) | reg;
}
#endif
sym_push(v, type, r, addr);
} else {
/* push local reference */
vset(type, r, addr);
}
} else {
Sym *sym = NULL;
if (v && scope == VT_CONST) {
/* see if the symbol was already defined */
sym = sym_find(v);
if (sym) {
patch_storage(sym, type);
if (sym->type.t & VT_EXTERN) {
/* if the variable is extern, it was not allocated */
sym->type.t &= ~VT_EXTERN;
/* set array size if it was omitted in extern
declaration */
if ((sym->type.t & VT_ARRAY) &&
sym->type.ref->c < 0 &&
type->ref->c >= 0)
sym->type.ref->c = type->ref->c;
} else if (!has_init) {
/* we accept several definitions of the same
global variable. this is tricky, because we
must play with the SHN_COMMON type of the symbol */
/* no init data, we won't add more to the symbol */
update_storage(sym);
goto no_alloc;
} else if (sym->c) {
ElfW(Sym) *esym;
esym = &((ElfW(Sym) *)symtab_section->data)[sym->c];
if (esym->st_shndx == data_section->sh_num)
tcc_error("redefinition of '%s'", get_tok_str(v, NULL));
}
}
}
/* allocate symbol in corresponding section */
sec = ad->section;
if (!sec) {
if (has_init)
sec = data_section;
else if (tcc_state->nocommon)
sec = bss_section;
}
if (sec) {
addr = section_add(sec, size, align);
#ifdef CONFIG_TCC_BCHECK
/* add padding if bound check */
if (tcc_state->do_bounds_check)
section_add(sec, 1, 1);
#endif
} else {
addr = align; /* SHN_COMMON is special, symbol value is align */
sec = common_section;
}
if (v) {
if (!sym) {
sym = sym_push(v, type, r | VT_SYM, 0);
sym->asm_label = ad->asm_label;
}
/* update symbol definition */
put_extern_sym(sym, sec, addr, size);
} else {
/* push global reference */
sym = get_sym_ref(type, sec, addr, size);
vpushsym(type, sym);
vtop->r |= r;
}
#ifdef CONFIG_TCC_BCHECK
/* handles bounds now because the symbol must be defined
before for the relocation */
if (tcc_state->do_bounds_check) {
addr_t *bounds_ptr;
greloca(bounds_section, sym, bounds_section->data_offset, R_DATA_PTR, 0);
/* then add global bound info */
bounds_ptr = section_ptr_add(bounds_section, 2 * sizeof(addr_t));
bounds_ptr[0] = 0; /* relocated */
bounds_ptr[1] = size;
}
#endif
}
if (type->t & VT_VLA) {
int a;
/* save current stack pointer */
if (vlas_in_scope == 0) {
if (vla_sp_root_loc == -1)
vla_sp_root_loc = (loc -= PTR_SIZE);
gen_vla_sp_save(vla_sp_root_loc);
}
vla_runtime_type_size(type, &a);
gen_vla_alloc(type, a);
gen_vla_sp_save(addr);
vla_sp_loc = addr;
vlas_in_scope++;
} else if (has_init) {
size_t oldreloc_offset = 0;
if (sec && sec->reloc)
oldreloc_offset = sec->reloc->data_offset;
decl_initializer(type, sec, addr, 1, 0);
if (sec && sec->reloc)
squeeze_multi_relocs(sec, oldreloc_offset);
/* patch flexible array member size back to -1, */
/* for possible subsequent similar declarations */
if (flexible_array)
flexible_array->type.ref->c = -1;
}
no_alloc:
/* restore parse state if needed */
if (init_str) {
end_macro();
restore_parse_state(&saved_parse_state);
}
}
/* parse a function defined by symbol 'sym' and generate its code in
'cur_text_section' */
static void gen_function(Sym *sym)
{
nocode_wanted = 0;
ind = cur_text_section->data_offset;
/* NOTE: we patch the symbol size later */
put_extern_sym(sym, cur_text_section, ind, 0);
funcname = get_tok_str(sym->v, NULL);
func_ind = ind;
/* Initialize VLA state */
vla_sp_loc = -1;
vla_sp_root_loc = -1;
/* put debug symbol */
tcc_debug_funcstart(tcc_state, sym);
/* push a dummy symbol to enable local sym storage */
sym_push2(&local_stack, SYM_FIELD, 0, 0);
local_scope = 1; /* for function parameters */
gfunc_prolog(&sym->type);
local_scope = 0;
rsym = 0;
block(NULL, NULL, 0);
nocode_wanted = 0;
gsym(rsym);
gfunc_epilog();
cur_text_section->data_offset = ind;
label_pop(&global_label_stack, NULL);
/* reset local stack */
local_scope = 0;
sym_pop(&local_stack, NULL, 0);
/* end of function */
/* patch symbol size */
((ElfW(Sym) *)symtab_section->data)[sym->c].st_size =
ind - func_ind;
tcc_debug_funcend(tcc_state, ind - func_ind);
/* It's better to crash than to generate wrong code */
cur_text_section = NULL;
funcname = ""; /* for safety */
func_vt.t = VT_VOID; /* for safety */
func_var = 0; /* for safety */
ind = 0; /* for safety */
nocode_wanted = 1;
check_vstack();
}
static void gen_inline_functions(TCCState *s)
{
Sym *sym;
int inline_generated, i, ln;
struct InlineFunc *fn;
ln = file->line_num;
/* iterate while inline function are referenced */
for(;;) {
inline_generated = 0;
for (i = 0; i < s->nb_inline_fns; ++i) {
fn = s->inline_fns[i];
sym = fn->sym;
if (sym && sym->c) {
/* the function was used: generate its code and
convert it to a normal function */
fn->sym = NULL;
if (file)
pstrcpy(file->filename, sizeof file->filename, fn->filename);
sym->type.t &= ~VT_INLINE;
begin_macro(fn->func_str, 1);
next();
cur_text_section = text_section;
gen_function(sym);
end_macro();
inline_generated = 1;
}
}
if (!inline_generated)
break;
}
file->line_num = ln;
}
ST_FUNC void free_inline_functions(TCCState *s)
{
int i;
/* free tokens of unused inline functions */
for (i = 0; i < s->nb_inline_fns; ++i) {
struct InlineFunc *fn = s->inline_fns[i];
if (fn->sym)
tok_str_free(fn->func_str);
}
dynarray_reset(&s->inline_fns, &s->nb_inline_fns);
}
/* 'l' is VT_LOCAL or VT_CONST to define default storage type, or VT_CMP
if parsing old style parameter decl list (and FUNC_SYM is set then) */
static int decl0(int l, int is_for_loop_init, Sym *func_sym)
{
int v, has_init, r;
CType type, btype;
Sym *sym;
AttributeDef ad;
while (1) {
if (!parse_btype(&btype, &ad)) {
if (is_for_loop_init)
return 0;
/* skip redundant ';' if not in old parameter decl scope */
if (tok == ';' && l != VT_CMP) {
next();
continue;
}
if (l == VT_CONST &&
(tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3)) {
/* global asm block */
asm_global_instr();
continue;
}
/* special test for old K&R protos without explicit int
type. Only accepted when defining global data */
if (l != VT_CONST || tok < TOK_UIDENT)
break;
btype.t = VT_INT;
}
if (((btype.t & VT_BTYPE) == VT_ENUM ||
(btype.t & VT_BTYPE) == VT_STRUCT) &&
tok == ';') {
if ((btype.t & VT_BTYPE) == VT_STRUCT) {
int v = btype.ref->v;
if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) >= SYM_FIRST_ANOM)
tcc_warning("unnamed struct/union that defines no instances");
}
next();
continue;
}
while (1) { /* iterate thru each declaration */
type = btype;
/* If the base type itself was an array type of unspecified
size (like in 'typedef int arr[]; arr x = {1};') then
we will overwrite the unknown size by the real one for
this decl. We need to unshare the ref symbol holding
that size. */
if ((type.t & VT_ARRAY) && type.ref->c < 0) {
type.ref = sym_push(SYM_FIELD, &type.ref->type, 0, type.ref->c);
}
type_decl(&type, &ad, &v, TYPE_DIRECT);
#if 0
{
char buf[500];
type_to_str(buf, sizeof(buf), &type, get_tok_str(v, NULL));
printf("type = '%s'\n", buf);
}
#endif
if ((type.t & VT_BTYPE) == VT_FUNC) {
if ((type.t & VT_STATIC) && (l == VT_LOCAL)) {
tcc_error("function without file scope cannot be static");
}
/* if old style function prototype, we accept a
declaration list */
sym = type.ref;
if (sym->c == FUNC_OLD && l == VT_CONST)
decl0(VT_CMP, 0, sym);
}
if (gnu_ext && (tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3)) {
ad.asm_label = asm_label_instr();
/* parse one last attribute list, after asm label */
parse_attribute(&ad);
if (tok == '{')
expect(";");
}
if (ad.a.weak)
type.t |= VT_WEAK;
#ifdef TCC_TARGET_PE
if (ad.a.func_import || ad.a.func_export) {
if (type.t & (VT_STATIC|VT_TYPEDEF))
tcc_error("cannot have dll linkage with static or typedef");
if (ad.a.func_export)
type.t |= VT_EXPORT;
else if ((type.t & VT_BTYPE) != VT_FUNC)
type.t |= VT_IMPORT|VT_EXTERN;
}
#endif
type.t |= ad.a.visibility << VT_VIS_SHIFT;
if (tok == '{') {
if (l != VT_CONST)
tcc_error("cannot use local functions");
if ((type.t & VT_BTYPE) != VT_FUNC)
expect("function definition");
/* reject abstract declarators in function definition
make old style params without decl have int type */
sym = type.ref;
while ((sym = sym->next) != NULL) {
if (!(sym->v & ~SYM_FIELD))
expect("identifier");
if (sym->type.t == VT_VOID)
sym->type = int_type;
}
/* XXX: cannot do better now: convert extern line to static inline */
if ((type.t & (VT_EXTERN | VT_INLINE)) == (VT_EXTERN | VT_INLINE))
type.t = (type.t & ~VT_EXTERN) | VT_STATIC;
sym = sym_find(v);
if (sym) {
Sym *ref;
if ((sym->type.t & VT_BTYPE) != VT_FUNC)
goto func_error1;
ref = sym->type.ref;
/* use func_call from prototype if not defined */
if (ref->a.func_call != FUNC_CDECL
&& type.ref->a.func_call == FUNC_CDECL)
type.ref->a.func_call = ref->a.func_call;
/* use static from prototype */
if (sym->type.t & VT_STATIC)
type.t = (type.t & ~VT_EXTERN) | VT_STATIC;
/* If the definition has no visibility use the
one from prototype. */
if (! (type.t & VT_VIS_MASK))
type.t |= sym->type.t & VT_VIS_MASK;
/* apply other storage attributes from prototype */
type.t |= sym->type.t & (VT_EXPORT|VT_WEAK);
if (!is_compatible_types(&sym->type, &type)) {
func_error1:
tcc_error("incompatible types for redefinition of '%s'",
get_tok_str(v, NULL));
}
if (ref->a.func_body)
tcc_error("redefinition of '%s'", get_tok_str(v, NULL));
/* if symbol is already defined, then put complete type */
sym->type = type;
} else {
/* put function symbol */
sym = global_identifier_push(v, type.t, 0);
sym->type.ref = type.ref;
}
sym->type.ref->a.func_body = 1;
sym->r = VT_SYM | VT_CONST;
/* static inline functions are just recorded as a kind
of macro. Their code will be emitted at the end of
the compilation unit only if they are used */
if ((type.t & (VT_INLINE | VT_STATIC)) ==
(VT_INLINE | VT_STATIC)) {
struct InlineFunc *fn;
const char *filename;
filename = file ? file->filename : "";
fn = tcc_malloc(sizeof *fn + strlen(filename));
strcpy(fn->filename, filename);
fn->sym = sym;
skip_or_save_block(&fn->func_str);
dynarray_add(&tcc_state->inline_fns,
&tcc_state->nb_inline_fns, fn);
} else {
/* compute text section */
cur_text_section = ad.section;
if (!cur_text_section)
cur_text_section = text_section;
gen_function(sym);
}
break;
} else {
if (l == VT_CMP) {
/* find parameter in function parameter list */
for (sym = func_sym->next; sym; sym = sym->next)
if ((sym->v & ~SYM_FIELD) == v)
goto found;
tcc_error("declaration for parameter '%s' but no such parameter",
get_tok_str(v, NULL));
found:
if (type.t & VT_STORAGE) /* 'register' is okay */
tcc_error("storage class specified for '%s'",
get_tok_str(v, NULL));
if (sym->type.t != VT_VOID)
tcc_error("redefinition of parameter '%s'",
get_tok_str(v, NULL));
convert_parameter_type(&type);
sym->type = type;
} else if (type.t & VT_TYPEDEF) {
/* save typedefed type */
/* XXX: test storage specifiers ? */
sym = sym_find(v);
if (sym && sym->scope == local_scope) {
if (!is_compatible_types(&sym->type, &type)
|| !(sym->type.t & VT_TYPEDEF))
tcc_error("incompatible redefinition of '%s'",
get_tok_str(v, NULL));
sym->type = type;
} else {
sym = sym_push(v, &type, 0, 0);
}
sym->a = ad.a;
} else {
r = 0;
if ((type.t & VT_BTYPE) == VT_FUNC) {
/* external function definition */
/* specific case for func_call attribute */
type.ref->a = ad.a;
} else if (!(type.t & VT_ARRAY)) {
/* not lvalue if array */
r |= lvalue_type(type.t);
}
has_init = (tok == '=');
if (has_init && (type.t & VT_VLA))
tcc_error("variable length array cannot be initialized");
if (((type.t & VT_EXTERN) && (!has_init || l != VT_CONST)) ||
((type.t & VT_BTYPE) == VT_FUNC) ||
((type.t & VT_ARRAY) && (type.t & VT_STATIC) &&
!has_init && l == VT_CONST && type.ref->c < 0)) {
/* external variable or function */
/* NOTE: as GCC, uninitialized global static
arrays of null size are considered as
extern */
sym = external_sym(v, &type, r);
sym->asm_label = ad.asm_label;
if (ad.alias_target) {
Section tsec;
ElfW(Sym) *esym;
Sym *alias_target;
alias_target = sym_find(ad.alias_target);
if (!alias_target || !alias_target->c)
tcc_error("unsupported forward __alias__ attribute");
esym = &((ElfW(Sym) *)symtab_section->data)[alias_target->c];
tsec.sh_num = esym->st_shndx;
put_extern_sym2(sym, &tsec, esym->st_value, esym->st_size, 0);
}
} else {
if (type.t & VT_STATIC)
r |= VT_CONST;
else
r |= l;
if (has_init)
next();
decl_initializer_alloc(&type, &ad, r, has_init, v, l);
}
}
if (tok != ',') {
if (is_for_loop_init)
return 1;
skip(';');
break;
}
next();
}
ad.a.aligned = 0;
}
}
return 0;
}
ST_FUNC void decl(int l)
{
decl0(l, 0, NULL);
}
/* ------------------------------------------------------------------------- */
/*
* ELF file handling for TCC
*
* Copyright (c) 2001-2004 Fabrice Bellard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _TCC_H
#include "tcc.h"
#endif
/* Define this to get some debug output during relocation processing. */
#undef DEBUG_RELOC
/********************************************************/
/* global variables */
ST_DATA Section *text_section, *data_section, *bss_section; /* predefined sections */
ST_DATA Section *common_section;
ST_DATA Section *cur_text_section; /* current section where function code is generated */
#ifdef CONFIG_TCC_ASM
ST_DATA Section *last_text_section; /* to handle .previous asm directive */
#endif
#ifdef CONFIG_TCC_BCHECK
/* bound check related sections */
ST_DATA Section *bounds_section; /* contains global data bound description */
ST_DATA Section *lbounds_section; /* contains local data bound description */
#endif
/* symbol sections */
ST_DATA Section *symtab_section, *strtab_section;
/* debug sections */
ST_DATA Section *stab_section, *stabstr_section;
/* XXX: avoid static variable */
static int new_undef_sym = 0; /* Is there a new undefined sym since last new_undef_sym() */
/* ------------------------------------------------------------------------- */
ST_FUNC void tccelf_new(TCCState *s)
{
/* no section zero */
dynarray_add(&s->sections, &s->nb_sections, NULL);
/* create standard sections */
text_section = new_section(s, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR);
data_section = new_section(s, ".data", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
bss_section = new_section(s, ".bss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE);
common_section = new_section(s, ".common", SHT_NOBITS, SHF_PRIVATE);
common_section->sh_num = SHN_COMMON;
/* symbols are always generated for linking stage */
symtab_section = new_symtab(s, ".symtab", SHT_SYMTAB, 0,
".strtab",
".hashtab", SHF_PRIVATE);
strtab_section = symtab_section->link;
s->symtab = symtab_section;
/* private symbol table for dynamic symbols */
s->dynsymtab_section = new_symtab(s, ".dynsymtab", SHT_SYMTAB, SHF_PRIVATE,
".dynstrtab",
".dynhashtab", SHF_PRIVATE);
get_sym_attr(s, 0, 1);
}
#ifdef CONFIG_TCC_BCHECK
ST_FUNC void tccelf_bounds_new(TCCState *s)
{
/* create bounds sections */
bounds_section = new_section(s, ".bounds",
SHT_PROGBITS, SHF_ALLOC);
lbounds_section = new_section(s, ".lbounds",
SHT_PROGBITS, SHF_ALLOC);
}
#endif
ST_FUNC void tccelf_stab_new(TCCState *s)
{
stab_section = new_section(s, ".stab", SHT_PROGBITS, 0);
stab_section->sh_entsize = sizeof(Stab_Sym);
stabstr_section = new_section(s, ".stabstr", SHT_STRTAB, 0);
put_elf_str(stabstr_section, "");
stab_section->link = stabstr_section;
/* put first entry */
put_stabs("", 0, 0, 0, 0);
}
static void free_section(Section *s)
{
tcc_free(s->data);
}
ST_FUNC void tccelf_delete(TCCState *s1)
{
int i;
/* free all sections */
for(i = 1; i < s1->nb_sections; i++)
free_section(s1->sections[i]);
dynarray_reset(&s1->sections, &s1->nb_sections);
for(i = 0; i < s1->nb_priv_sections; i++)
free_section(s1->priv_sections[i]);
dynarray_reset(&s1->priv_sections, &s1->nb_priv_sections);
/* free any loaded DLLs */
#ifdef TCC_IS_NATIVE
for ( i = 0; i < s1->nb_loaded_dlls; i++) {
DLLReference *ref = s1->loaded_dlls[i];
if ( ref->handle )
# ifdef _WIN32
FreeLibrary((HMODULE)ref->handle);
# else
dlclose(ref->handle);
# endif
}
#endif
/* free loaded dlls array */
dynarray_reset(&s1->loaded_dlls, &s1->nb_loaded_dlls);
tcc_free(s1->sym_attrs);
}
ST_FUNC Section *new_section(TCCState *s1, const char *name, int sh_type, int sh_flags)
{
Section *sec;
sec = tcc_mallocz(sizeof(Section) + strlen(name));
strcpy(sec->name, name);
sec->sh_type = sh_type;
sec->sh_flags = sh_flags;
switch(sh_type) {
case SHT_HASH:
case SHT_REL:
case SHT_RELA:
case SHT_DYNSYM:
case SHT_SYMTAB:
case SHT_DYNAMIC:
sec->sh_addralign = 4;
break;
case SHT_STRTAB:
sec->sh_addralign = 1;
break;
default:
sec->sh_addralign = PTR_SIZE; /* gcc/pcc default alignment */
break;
}
if (sh_flags & SHF_PRIVATE) {
dynarray_add(&s1->priv_sections, &s1->nb_priv_sections, sec);
} else {
sec->sh_num = s1->nb_sections;
dynarray_add(&s1->sections, &s1->nb_sections, sec);
}
return sec;
}
ST_FUNC Section *new_symtab(TCCState *s1,
const char *symtab_name, int sh_type, int sh_flags,
const char *strtab_name,
const char *hash_name, int hash_sh_flags)
{
Section *symtab, *strtab, *hash;
int *ptr, nb_buckets;
symtab = new_section(s1, symtab_name, sh_type, sh_flags);
symtab->sh_entsize = sizeof(ElfW(Sym));
strtab = new_section(s1, strtab_name, SHT_STRTAB, sh_flags);
put_elf_str(strtab, "");
symtab->link = strtab;
put_elf_sym(symtab, 0, 0, 0, 0, 0, NULL);
nb_buckets = 1;
hash = new_section(s1, hash_name, SHT_HASH, hash_sh_flags);
hash->sh_entsize = sizeof(int);
symtab->hash = hash;
hash->link = symtab;
ptr = section_ptr_add(hash, (2 + nb_buckets + 1) * sizeof(int));
ptr[0] = nb_buckets;
ptr[1] = 1;
memset(ptr + 2, 0, (nb_buckets + 1) * sizeof(int));
return symtab;
}
/* realloc section and set its content to zero */
ST_FUNC void section_realloc(Section *sec, unsigned long new_size)
{
unsigned long size;
unsigned char *data;
size = sec->data_allocated;
if (size == 0)
size = 1;
while (size < new_size)
size = size * 2;
data = tcc_realloc(sec->data, size);
memset(data + sec->data_allocated, 0, size - sec->data_allocated);
sec->data = data;
sec->data_allocated = size;
}
/* reserve at least 'size' bytes aligned per 'align' in section
'sec' from current offset, and return the aligned offset */
ST_FUNC size_t section_add(Section *sec, addr_t size, int align)
{
size_t offset, offset1;
offset = (sec->data_offset + align - 1) & -align;
offset1 = offset + size;
if (sec->sh_type != SHT_NOBITS && offset1 > sec->data_allocated)
section_realloc(sec, offset1);
sec->data_offset = offset1;
if (align > sec->sh_addralign)
sec->sh_addralign = align;
return offset;
}
/* reserve at least 'size' bytes in section 'sec' from
sec->data_offset. */
ST_FUNC void *section_ptr_add(Section *sec, addr_t size)
{
size_t offset = section_add(sec, size, 1);
return sec->data + offset;
}
/* reserve at least 'size' bytes from section start */
ST_FUNC void section_reserve(Section *sec, unsigned long size)
{
if (size > sec->data_allocated)
section_realloc(sec, size);
if (size > sec->data_offset)
sec->data_offset = size;
}
/* return a reference to a section, and create it if it does not
exists */
ST_FUNC Section *find_section(TCCState *s1, const char *name)
{
Section *sec;
int i;
for(i = 1; i < s1->nb_sections; i++) {
sec = s1->sections[i];
if (!strcmp(name, sec->name))
return sec;
}
/* sections are created as PROGBITS */
return new_section(s1, name, SHT_PROGBITS, SHF_ALLOC);
}
/* ------------------------------------------------------------------------- */
ST_FUNC int put_elf_str(Section *s, const char *sym)
{
int offset, len;
char *ptr;
len = strlen(sym) + 1;
offset = s->data_offset;
ptr = section_ptr_add(s, len);
memcpy(ptr, sym, len);
return offset;
}
/* elf symbol hashing function */
static unsigned long elf_hash(const unsigned char *name)
{
unsigned long h = 0, g;
while (*name) {
h = (h << 4) + *name++;
g = h & 0xf0000000;
if (g)
h ^= g >> 24;
h &= ~g;
}
return h;
}
/* rebuild hash table of section s */
/* NOTE: we do factorize the hash table code to go faster */
static void rebuild_hash(Section *s, unsigned int nb_buckets)
{
ElfW(Sym) *sym;
int *ptr, *hash, nb_syms, sym_index, h;
unsigned char *strtab;
strtab = s->link->data;
nb_syms = s->data_offset / sizeof(ElfW(Sym));
s->hash->data_offset = 0;
ptr = section_ptr_add(s->hash, (2 + nb_buckets + nb_syms) * sizeof(int));
ptr[0] = nb_buckets;
ptr[1] = nb_syms;
ptr += 2;
hash = ptr;
memset(hash, 0, (nb_buckets + 1) * sizeof(int));
ptr += nb_buckets + 1;
sym = (ElfW(Sym) *)s->data + 1;
for(sym_index = 1; sym_index < nb_syms; sym_index++) {
if (ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) {
#if !BOOTSTRAP
h = elf_hash(strtab + sym->st_name) % nb_buckets;
#else
h = elf_hash(strtab + sym->st_name);
h = h % nb_buckets;
#endif
*ptr = hash[h];
hash[h] = sym_index;
} else {
*ptr = 0;
}
ptr++;
sym++;
}
}
/* return the symbol number */
ST_FUNC int put_elf_sym(Section *s, addr_t value, unsigned long size,
int info, int other, int shndx, const char *name)
{
int name_offset, sym_index;
int nbuckets, h;
ElfW(Sym) *sym;
Section *hs;
sym = section_ptr_add(s, sizeof(ElfW(Sym)));
if (name)
name_offset = put_elf_str(s->link, name);
else
name_offset = 0;
/* XXX: endianness */
sym->st_name = name_offset;
sym->st_value = value;
sym->st_size = size;
sym->st_info = info;
sym->st_other = other;
sym->st_shndx = shndx;
sym_index = sym - (ElfW(Sym) *)s->data;
hs = s->hash;
if (hs) {
int *ptr, *base;
ptr = section_ptr_add(hs, sizeof(int));
base = (int *)hs->data;
/* only add global or weak symbols */
if (ELFW(ST_BIND)(info) != STB_LOCAL) {
/* add another hashing entry */
nbuckets = base[0];
#if !BOOTSTRAP
h = elf_hash((unsigned char *) name) % nbuckets;
#else
h = elf_hash((unsigned char *) name);
h = h % nbuckets;
#endif
*ptr = base[2 + h];
base[2 + h] = sym_index;
base[1]++;
/* we resize the hash table */
hs->nb_hashed_syms++;
if (hs->nb_hashed_syms > 2 * nbuckets) {
rebuild_hash(s, 2 * nbuckets);
}
} else {
*ptr = 0;
base[1]++;
}
}
return sym_index;
}
/* find global ELF symbol 'name' and return its index. Return 0 if not
found. */
ST_FUNC int find_elf_sym(Section *s, const char *name)
{
ElfW(Sym) *sym;
Section *hs;
int nbuckets, sym_index, h;
const char *name1;
hs = s->hash;
if (!hs)
return 0;
nbuckets = ((int *)hs->data)[0];
#if !BOOTSTRAP
h = elf_hash((unsigned char *) name) % nbuckets;
#else
h = elf_hash((unsigned char *) name);
h = h % nbuckets;
#endif
sym_index = ((int *)hs->data)[2 + h];
while (sym_index != 0) {
sym = &((ElfW(Sym) *)s->data)[sym_index];
name1 = (char *) s->link->data + sym->st_name;
if (!strcmp(name, name1))
return sym_index;
sym_index = ((int *)hs->data)[2 + nbuckets + sym_index];
}
return 0;
}
/* return elf symbol value, signal error if 'err' is nonzero */
ST_FUNC addr_t get_elf_sym_addr(TCCState *s, const char *name, int err)
{
int sym_index;
ElfW(Sym) *sym;
sym_index = find_elf_sym(s->symtab, name);
sym = &((ElfW(Sym) *)s->symtab->data)[sym_index];
if (!sym_index || sym->st_shndx == SHN_UNDEF) {
if (err)
tcc_error("%s not defined", name);
return 0;
}
return sym->st_value;
}
#if __MESC_MES__ || !__MESC__
#if !defined (__intptr_t_defined) && !defined (__MES_INTPTR_T)
typedef unsigned long uintptr_t;
#endif
#else // guile
#undef uintptr_t
#endif
/* return elf symbol value */
LIBTCCAPI void *tcc_get_symbol(TCCState *s, const char *name)
{
return (void*)(uintptr_t)get_elf_sym_addr(s, name, 0);
}
#if defined TCC_IS_NATIVE || defined TCC_TARGET_PE
/* return elf symbol value or error */
ST_FUNC void* tcc_get_symbol_err(TCCState *s, const char *name)
{
return (void*)(uintptr_t)get_elf_sym_addr(s, name, 1);
}
#endif
/* add an elf symbol : check if it is already defined and patch
it. Return symbol index. NOTE that sh_num can be SHN_UNDEF. */
ST_FUNC int set_elf_sym(Section *s, addr_t value, unsigned long size,
int info, int other, int shndx, const char *name)
{
ElfW(Sym) *esym;
int sym_bind, sym_index, sym_type, esym_bind;
unsigned char sym_vis, esym_vis, new_vis;
sym_bind = ELFW(ST_BIND)(info);
sym_type = ELFW(ST_TYPE)(info);
sym_vis = ELFW(ST_VISIBILITY)(other);
sym_index = find_elf_sym(s, name);
esym = &((ElfW(Sym) *)s->data)[sym_index];
if (sym_index && esym->st_value == value && esym->st_size == size
&& esym->st_info == info && esym->st_other == other
&& esym->st_shndx == shndx)
return sym_index;
if (sym_bind != STB_LOCAL) {
/* we search global or weak symbols */
if (!sym_index)
goto do_def;
if (esym->st_shndx != SHN_UNDEF) {
esym_bind = ELFW(ST_BIND)(esym->st_info);
/* propagate the most constraining visibility */
/* STV_DEFAULT(0)<STV_PROTECTED(3)<STV_HIDDEN(2)<STV_INTERNAL(1) */
esym_vis = ELFW(ST_VISIBILITY)(esym->st_other);
if (esym_vis == STV_DEFAULT) {
new_vis = sym_vis;
} else if (sym_vis == STV_DEFAULT) {
new_vis = esym_vis;
} else {
new_vis = (esym_vis < sym_vis) ? esym_vis : sym_vis;
}
esym->st_other = (esym->st_other & ~ELFW(ST_VISIBILITY)(-1))
| new_vis;
other = esym->st_other; /* in case we have to patch esym */
if (shndx == SHN_UNDEF) {
/* ignore adding of undefined symbol if the
corresponding symbol is already defined */
} else if (sym_bind == STB_GLOBAL && esym_bind == STB_WEAK) {
/* global overrides weak, so patch */
goto do_patch;
} else if (sym_bind == STB_WEAK && esym_bind == STB_GLOBAL) {
/* weak is ignored if already global */
} else if (sym_bind == STB_WEAK && esym_bind == STB_WEAK) {
/* keep first-found weak definition, ignore subsequents */
} else if (sym_vis == STV_HIDDEN || sym_vis == STV_INTERNAL) {
/* ignore hidden symbols after */
} else if ((esym->st_shndx == SHN_COMMON
|| esym->st_shndx == bss_section->sh_num)
&& (shndx < SHN_LORESERVE
&& shndx != bss_section->sh_num)) {
/* data symbol gets precedence over common/bss */
goto do_patch;
} else if (shndx == SHN_COMMON || shndx == bss_section->sh_num) {
/* data symbol keeps precedence over common/bss */
} else if (s == tcc_state->dynsymtab_section) {
/* we accept that two DLL define the same symbol */
} else {
#if 0
printf("new_bind=%x new_shndx=%x new_vis=%x old_bind=%x old_shndx=%x old_vis=%x\n",
sym_bind, shndx, new_vis, esym_bind, esym->st_shndx, esym_vis);
#endif
tcc_error_noabort("'%s' defined twice", name);
}
} else {
do_patch:
esym->st_info = ELFW(ST_INFO)(sym_bind, sym_type);
esym->st_shndx = shndx;
new_undef_sym = 1;
esym->st_value = value;
esym->st_size = size;
esym->st_other = other;
}
} else {
do_def:
sym_index = put_elf_sym(s, value, size,
ELFW(ST_INFO)(sym_bind, sym_type), other,
shndx, name);
}
return sym_index;
}
/* put relocation */
ST_FUNC void put_elf_reloca(Section *symtab, Section *s, unsigned long offset,
int type, int symbol, addr_t addend)
{
char buf[256];
Section *sr;
ElfW_Rel *rel;
sr = s->reloc;
if (!sr) {
/* if no relocation section, create it */
snprintf(buf, sizeof(buf), REL_SECTION_FMT, s->name);
/* if the symtab is allocated, then we consider the relocation
are also */
sr = new_section(tcc_state, buf, SHT_RELX, symtab->sh_flags);
sr->sh_entsize = sizeof(ElfW_Rel);
sr->link = symtab;
sr->sh_info = s->sh_num;
s->reloc = sr;
}
rel = section_ptr_add(sr, sizeof(ElfW_Rel));
rel->r_offset = offset;
rel->r_info = ELFW(R_INFO)(symbol, type);
#if SHT_RELX == SHT_RELA
rel->r_addend = addend;
#else
if (addend)
tcc_error("non-zero addend on REL architecture");
#endif
}
ST_FUNC void put_elf_reloc(Section *symtab, Section *s, unsigned long offset,
int type, int symbol)
{
put_elf_reloca(symtab, s, offset, type, symbol, 0);
}
/* Remove relocations for section S->reloc starting at oldrelocoffset
that are to the same place, retaining the last of them. As side effect
the relocations are sorted. Possibly reduces the number of relocs. */
ST_FUNC void squeeze_multi_relocs(Section *s, size_t oldrelocoffset)
{
Section *sr = s->reloc;
ElfW_Rel *r, *dest;
ssize_t a;
ElfW(Addr) addr;
if (oldrelocoffset + sizeof(*r) >= sr->data_offset)
return;
/* The relocs we're dealing with are the result of initializer parsing.
So they will be mostly in order and there aren't many of them.
Secondly we need a stable sort (which qsort isn't). We use
a simple insertion sort. */
for (a = oldrelocoffset + sizeof(*r); a < sr->data_offset; a += sizeof(*r)) {
ssize_t i = a - sizeof(*r);
///addr = ((ElfW_Rel*)(sr->data + a))->r_offset;
ElfW_Rel* ea = (ElfW_Rel*)(sr->data + a);
ElfW_Rel* ei = (ElfW_Rel*)(sr->data + i);
addr = ea->r_offset;
for (; i >= (ssize_t)oldrelocoffset &&
ei->r_offset > addr; i -= sizeof(*r)) {
ei = (ElfW_Rel*)(sr->data + i);
ElfW_Rel tmp = *(ElfW_Rel*)(sr->data + a);
*(ElfW_Rel*)(sr->data + a) = *(ElfW_Rel*)(sr->data + i);
*(ElfW_Rel*)(sr->data + i) = tmp;
}
}
r = (ElfW_Rel*)(sr->data + oldrelocoffset);
dest = r;
for (; r < (ElfW_Rel*)(sr->data + sr->data_offset); r++) {
if (dest->r_offset != r->r_offset)
dest++;
*dest = *r;
}
sr->data_offset = (unsigned char*)dest - sr->data + sizeof(*r);
}
/* put stab debug information */
ST_FUNC void put_stabs(const char *str, int type, int other, int desc,
unsigned long value)
{
Stab_Sym *sym;
sym = section_ptr_add(stab_section, sizeof(Stab_Sym));
if (str) {
sym->n_strx = put_elf_str(stabstr_section, str);
} else {
sym->n_strx = 0;
}
sym->n_type = type;
sym->n_other = other;
sym->n_desc = desc;
sym->n_value = value;
}
ST_FUNC void put_stabs_r(const char *str, int type, int other, int desc,
unsigned long value, Section *sec, int sym_index)
{
put_stabs(str, type, other, desc, value);
put_elf_reloc(symtab_section, stab_section,
stab_section->data_offset - sizeof(unsigned int),
R_DATA_32, sym_index);
}
ST_FUNC void put_stabn(int type, int other, int desc, int value)
{
put_stabs(NULL, type, other, desc, value);
}
ST_FUNC void put_stabd(int type, int other, int desc)
{
put_stabs(NULL, type, other, desc, 0);
}
ST_FUNC struct sym_attr *get_sym_attr(TCCState *s1, int index, int alloc)
{
int n;
struct sym_attr *tab;
if (index >= s1->nb_sym_attrs) {
if (!alloc)
return s1->sym_attrs;
/* find immediately bigger power of 2 and reallocate array */
n = 1;
while (index >= n)
n *= 2;
tab = tcc_realloc(s1->sym_attrs, n * sizeof(*s1->sym_attrs));
s1->sym_attrs = tab;
memset(s1->sym_attrs + s1->nb_sym_attrs, 0,
(n - s1->nb_sym_attrs) * sizeof(*s1->sym_attrs));
s1->nb_sym_attrs = n;
}
return &s1->sym_attrs[index];
}
/* Browse each elem of type <type> in section <sec> starting at elem <startoff>
using variable <elem> */
#define for_each_elem(sec, startoff, elem, type) \
for (elem = (type *) sec->data + startoff; \
elem < (type *) (sec->data + sec->data_offset); elem++)
/* In an ELF file symbol table, the local symbols must appear below
the global and weak ones. Since TCC cannot sort it while generating
the code, we must do it after. All the relocation tables are also
modified to take into account the symbol table sorting */
static void sort_syms(TCCState *s1, Section *s)
{
int *old_to_new_syms;
ElfW(Sym) *new_syms;
int nb_syms, i;
ElfW(Sym) *p, *q;
ElfW_Rel *rel;
Section *sr;
int type, sym_index;
nb_syms = s->data_offset / sizeof(ElfW(Sym));
new_syms = tcc_malloc(nb_syms * sizeof(ElfW(Sym)));
old_to_new_syms = tcc_malloc(nb_syms * sizeof(int));
/* first pass for local symbols */
p = (ElfW(Sym) *)s->data;
q = new_syms;
for(i = 0; i < nb_syms; i++) {
if (ELFW(ST_BIND)(p->st_info) == STB_LOCAL) {
old_to_new_syms[i] = q - new_syms;
*q++ = *p;
}
p++;
}
/* save the number of local symbols in section header */
s->sh_info = q - new_syms;
/* then second pass for non local symbols */
p = (ElfW(Sym) *)s->data;
for(i = 0; i < nb_syms; i++) {
if (ELFW(ST_BIND)(p->st_info) != STB_LOCAL) {
old_to_new_syms[i] = q - new_syms;
*q++ = *p;
}
p++;
}
/* we copy the new symbols to the old */
memcpy(s->data, new_syms, nb_syms * sizeof(ElfW(Sym)));
tcc_free(new_syms);
/* now we modify all the relocations */
for(i = 1; i < s1->nb_sections; i++) {
sr = s1->sections[i];
if (sr->sh_type == SHT_RELX && sr->link == s) {
for_each_elem(sr, 0, rel, ElfW_Rel) {
sym_index = ELFW(R_SYM)(rel->r_info);
type = ELFW(R_TYPE)(rel->r_info);
sym_index = old_to_new_syms[sym_index];
rel->r_info = ELFW(R_INFO)(sym_index, type);
}
}
}
tcc_free(old_to_new_syms);
}
/* relocate common symbols in the .bss section */
ST_FUNC void relocate_common_syms(void)
{
ElfW(Sym) *sym;
for_each_elem(symtab_section, 1, sym, ElfW(Sym)) {
if (sym->st_shndx == SHN_COMMON) {
/* symbol alignment is in st_value for SHN_COMMONs */
sym->st_value = section_add(bss_section, sym->st_size,
sym->st_value);
sym->st_shndx = bss_section->sh_num;
}
}
}
/* relocate symbol table, resolve undefined symbols if do_resolve is
true and output error if undefined symbol. */
ST_FUNC void relocate_syms(TCCState *s1, Section *symtab, int do_resolve)
{
ElfW(Sym) *sym;
int sym_bind, sh_num;
const char *name;
for_each_elem(symtab, 1, sym, ElfW(Sym)) {
sh_num = sym->st_shndx;
if (sh_num == SHN_UNDEF) {
name = (char *) strtab_section->data + sym->st_name;
/* Use ld.so to resolve symbol for us (for tcc -run) */
if (do_resolve) {
#if defined TCC_IS_NATIVE && !defined TCC_TARGET_PE
void *addr = dlsym(RTLD_DEFAULT, name);
if (addr) {
sym->st_value = (addr_t) addr;
#ifdef DEBUG_RELOC
printf ("relocate_sym: %s -> 0x%lx\n", name, sym->st_value);
#endif
goto found;
}
#endif
/* if dynamic symbol exist, it will be used in relocate_section */
} else if (s1->dynsym && find_elf_sym(s1->dynsym, name))
goto found;
/* XXX: _fp_hw seems to be part of the ABI, so we ignore
it */
if (!strcmp(name, "_fp_hw"))
goto found;
/* only weak symbols are accepted to be undefined. Their
value is zero */
sym_bind = ELFW(ST_BIND)(sym->st_info);
if (sym_bind == STB_WEAK)
sym->st_value = 0;
else
tcc_error_noabort("undefined symbol '%s'", name);
} else if (sh_num < SHN_LORESERVE) {
/* add section base */
sym->st_value += s1->sections[sym->st_shndx]->sh_addr;
}
found: ;
}
}
/* relocate a given section (CPU dependent) by applying the relocations
in the associated relocation section */
ST_FUNC void relocate_section(TCCState *s1, Section *s)
{
Section *sr = s->reloc;
ElfW_Rel *rel;
ElfW(Sym) *sym;
int type, sym_index;
unsigned char *ptr;
addr_t tgt, addr;
relocate_init(sr);
for_each_elem(sr, 0, rel, ElfW_Rel) {
ptr = s->data + rel->r_offset;
sym_index = ELFW(R_SYM)(rel->r_info);
sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
type = ELFW(R_TYPE)(rel->r_info);
tgt = sym->st_value;
#if SHT_RELX == SHT_RELA
tgt += rel->r_addend;
#endif
addr = s->sh_addr + rel->r_offset;
relocate(s1, rel, type, ptr, addr, tgt);
}
/* if the relocation is allocated, we change its symbol table */
if (sr->sh_flags & SHF_ALLOC)
sr->link = s1->dynsym;
}
/* relocate relocation table in 'sr' */
static void relocate_rel(TCCState *s1, Section *sr)
{
Section *s;
ElfW_Rel *rel;
s = s1->sections[sr->sh_info];
for_each_elem(sr, 0, rel, ElfW_Rel)
rel->r_offset += s->sh_addr;
}
/* count the number of dynamic relocations so that we can reserve
their space */
static int prepare_dynamic_rel(TCCState *s1, Section *sr)
{
ElfW_Rel *rel;
int sym_index, type, count;
count = 0;
for_each_elem(sr, 0, rel, ElfW_Rel) {
sym_index = ELFW(R_SYM)(rel->r_info);
type = ELFW(R_TYPE)(rel->r_info);
switch(type) {
#if defined(TCC_TARGET_I386)
case R_386_32:
#elif defined(TCC_TARGET_X86_64)
case R_X86_64_32:
case R_X86_64_32S:
case R_X86_64_64:
#endif
count++;
break;
#if defined(TCC_TARGET_I386)
case R_386_PC32:
#elif defined(TCC_TARGET_X86_64)
case R_X86_64_PC32:
#endif
if (get_sym_attr(s1, sym_index, 0)->dyn_index)
count++;
break;
default:
break;
}
}
if (count) {
/* allocate the section */
sr->sh_flags |= SHF_ALLOC;
sr->sh_size = count * sizeof(ElfW_Rel);
}
return count;
}
static void build_got(TCCState *s1)
{
/* if no got, then create it */
s1->got = new_section(s1, ".got", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
s1->got->sh_entsize = 4;
set_elf_sym(symtab_section, 0, 4, ELFW(ST_INFO)(STB_GLOBAL, STT_OBJECT),
0, s1->got->sh_num, "_GLOBAL_OFFSET_TABLE_");
/* keep space for _DYNAMIC pointer and two dummy got entries */
section_ptr_add(s1->got, 3 * PTR_SIZE);
}
/* Create a GOT and (for function call) a PLT entry corresponding to a symbol
in s1->symtab. When creating the dynamic symbol table entry for the GOT
relocation, use 'size' and 'info' for the corresponding symbol metadata.
Returns the offset of the GOT or (if any) PLT entry. */
static struct sym_attr * put_got_entry(TCCState *s1, int dyn_reloc_type,
unsigned long size,
int info, int sym_index)
{
int need_plt_entry;
const char *name;
ElfW(Sym) *sym;
struct sym_attr *attr;
unsigned got_offset;
char plt_name[100];
int len;
need_plt_entry = (dyn_reloc_type == R_JMP_SLOT);
attr = get_sym_attr(s1, sym_index, 1);
/* In case a function is both called and its address taken 2 GOT entries
are created, one for taking the address (GOT) and the other for the PLT
entry (PLTGOT). */
if (need_plt_entry ? attr->plt_offset : attr->got_offset)
return attr;
/* create the GOT entry */
got_offset = s1->got->data_offset;
section_ptr_add(s1->got, PTR_SIZE);
/* Create the GOT relocation that will insert the address of the object or
function of interest in the GOT entry. This is a static relocation for
memory output (dlsym will give us the address of symbols) and dynamic
relocation otherwise (executable and DLLs). The relocation should be
done lazily for GOT entry with *_JUMP_SLOT relocation type (the one
associated to a PLT entry) but is currently done at load time for an
unknown reason. */
sym = &((ElfW(Sym) *) symtab_section->data)[sym_index];
name = (char *) symtab_section->link->data + sym->st_name;
if (s1->dynsym) {
if (ELFW(ST_BIND)(sym->st_info) == STB_LOCAL) {
/* Hack alarm. We don't want to emit dynamic symbols
and symbol based relocs for STB_LOCAL symbols, but rather
want to resolve them directly. At this point the symbol
values aren't final yet, so we must defer this. We will later
have to create a RELATIVE reloc anyway, so we misuse the
relocation slot to smuggle the symbol reference until
fill_local_got_entries. Not that the sym_index is
relative to symtab_section, not s1->dynsym! Nevertheless
we use s1->dyn_sym so that if this is the first call
that got->reloc is correctly created. Also note that
RELATIVE relocs are not normally created for the .got,
so the types serves as a marker for later (and is retained
also for the final output, which is okay because then the
got is just normal data). */
put_elf_reloc(s1->dynsym, s1->got, got_offset, R_RELATIVE,
sym_index);
} else {
if (0 == attr->dyn_index)
attr->dyn_index = set_elf_sym(s1->dynsym, sym->st_value, size,
info, 0, sym->st_shndx, name);
put_elf_reloc(s1->dynsym, s1->got, got_offset, dyn_reloc_type,
attr->dyn_index);
}
} else {
put_elf_reloc(symtab_section, s1->got, got_offset, dyn_reloc_type,
sym_index);
}
if (need_plt_entry) {
if (!s1->plt) {
s1->plt = new_section(s1, ".plt", SHT_PROGBITS,
SHF_ALLOC | SHF_EXECINSTR);
s1->plt->sh_entsize = 4;
}
attr->plt_offset = create_plt_entry(s1, got_offset, attr);
/* create a symbol 'sym@plt' for the PLT jump vector */
len = strlen(name);
if (len > sizeof plt_name - 5)
len = sizeof plt_name - 5;
memcpy(plt_name, name, len);
strcpy(plt_name + len, "@plt");
attr->plt_sym = put_elf_sym(s1->symtab, attr->plt_offset, sym->st_size,
ELFW(ST_INFO)(STB_GLOBAL, STT_FUNC), 0, s1->plt->sh_num, plt_name);
} else {
attr->got_offset = got_offset;
}
return attr;
}
/* build GOT and PLT entries */
ST_FUNC void build_got_entries(TCCState *s1)
{
Section *s;
ElfW_Rel *rel;
ElfW(Sym) *sym;
int i, type, gotplt_entry, reloc_type, sym_index;
struct sym_attr *attr;
for(i = 1; i < s1->nb_sections; i++) {
s = s1->sections[i];
if (s->sh_type != SHT_RELX)
continue;
/* no need to handle got relocations */
if (s->link != symtab_section)
continue;
for_each_elem(s, 0, rel, ElfW_Rel) {
type = ELFW(R_TYPE)(rel->r_info);
gotplt_entry = gotplt_entry_type(type);
sym_index = ELFW(R_SYM)(rel->r_info);
sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
if (gotplt_entry == NO_GOTPLT_ENTRY) {
continue;
}
/* Automatically create PLT/GOT [entry] if it is an undefined
reference (resolved at runtime), or the symbol is absolute,
probably created by tcc_add_symbol, and thus on 64-bit
targets might be too far from application code. */
if (gotplt_entry == AUTO_GOTPLT_ENTRY) {
if (sym->st_shndx == SHN_UNDEF) {
ElfW(Sym) *esym;
int dynindex;
if (s1->output_type == TCC_OUTPUT_DLL && ! PCRELATIVE_DLLPLT)
continue;
/* Relocations for UNDEF symbols would normally need
to be transferred into the executable or shared object.
If that were done AUTO_GOTPLT_ENTRY wouldn't exist.
But TCC doesn't do that (at least for exes), so we
need to resolve all such relocs locally. And that
means PLT slots for functions in DLLs and COPY relocs for
data symbols. COPY relocs were generated in
bind_exe_dynsyms (and the symbol adjusted to be defined),
and for functions we were generated a dynamic symbol
of function type. */
if (s1->dynsym) {
/* dynsym isn't set for -run :-/ */
dynindex = get_sym_attr(s1, sym_index, 0)->dyn_index;
esym = (ElfW(Sym) *)s1->dynsym->data + dynindex;
if (dynindex
&& (ELFW(ST_TYPE)(esym->st_info) == STT_FUNC
|| (ELFW(ST_TYPE)(esym->st_info) == STT_NOTYPE
&& ELFW(ST_TYPE)(sym->st_info) == STT_FUNC)))
goto jmp_slot;
}
} else if (!(sym->st_shndx == SHN_ABS
#ifndef TCC_TARGET_ARM
&& PTR_SIZE == 8
#endif
))
continue;
}
#ifdef TCC_TARGET_X86_64
if ((type == R_X86_64_PLT32 || type == R_X86_64_PC32) &&
(ELFW(ST_VISIBILITY)(sym->st_other) != STV_DEFAULT ||
ELFW(ST_BIND)(sym->st_info) == STB_LOCAL)) {
rel->r_info = ELFW(R_INFO)(sym_index, R_X86_64_PC32);
continue;
}
#endif
if (code_reloc(type)) {
jmp_slot:
reloc_type = R_JMP_SLOT;
} else
reloc_type = R_GLOB_DAT;
if (!s1->got)
build_got(s1);
if (gotplt_entry == BUILD_GOT_ONLY)
continue;
attr = put_got_entry(s1, reloc_type, sym->st_size, sym->st_info,
sym_index);
if (reloc_type == R_JMP_SLOT)
rel->r_info = ELFW(R_INFO)(attr->plt_sym, type);
}
}
}
/* put dynamic tag */
static void put_dt(Section *dynamic, int dt, addr_t val)
{
ElfW(Dyn) *dyn;
dyn = section_ptr_add(dynamic, sizeof(ElfW(Dyn)));
dyn->d_tag = dt;
dyn->d_un.d_val = val;
}
#ifndef TCC_TARGET_PE
static void add_init_array_defines(TCCState *s1, const char *section_name)
{
Section *s;
long end_offset;
char sym_start[1024];
char sym_end[1024];
snprintf(sym_start, sizeof(sym_start), "__%s_start", section_name + 1);
snprintf(sym_end, sizeof(sym_end), "__%s_end", section_name + 1);
s = find_section(s1, section_name);
if (!s) {
end_offset = 0;
s = data_section;
} else {
end_offset = s->data_offset;
}
set_elf_sym(symtab_section,
0, 0,
ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
s->sh_num, sym_start);
set_elf_sym(symtab_section,
end_offset, 0,
ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
s->sh_num, sym_end);
}
#endif
static int tcc_add_support(TCCState *s1, const char *filename)
{
char buf[1024];
snprintf(buf, sizeof(buf), "%s/%s", s1->tcc_lib_path, filename);
return tcc_add_file(s1, buf);
}
ST_FUNC void tcc_add_bcheck(TCCState *s1)
{
#ifdef CONFIG_TCC_BCHECK
addr_t *ptr;
int sym_index;
if (0 == s1->do_bounds_check)
return;
/* XXX: add an object file to do that */
ptr = section_ptr_add(bounds_section, sizeof(*ptr));
*ptr = 0;
set_elf_sym(symtab_section, 0, 0,
ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
bounds_section->sh_num, "__bounds_start");
/* pull bcheck.o from libtcc1.a */
sym_index = set_elf_sym(symtab_section, 0, 0,
ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
SHN_UNDEF, "__bound_init");
if (s1->output_type != TCC_OUTPUT_MEMORY) {
/* add 'call __bound_init()' in .init section */
Section *init_section = find_section(s1, ".init");
unsigned char *pinit = section_ptr_add(init_section, 5);
pinit[0] = 0xe8;
write32le(pinit + 1, -4);
put_elf_reloc(symtab_section, init_section,
init_section->data_offset - 4, R_386_PC32, sym_index);
/* R_386_PC32 = R_X86_64_PC32 = 2 */
}
#endif
}
/* add tcc runtime libraries */
ST_FUNC void tcc_add_runtime(TCCState *s1)
{
tcc_add_bcheck(s1);
tcc_add_pragma_libs(s1);
/* add libc */
if (!s1->nostdlib) {
tcc_add_library_err(s1, "c");
#ifdef TCC_LIBGCC
if (!s1->static_link) {
if (TCC_LIBGCC[0] == '/')
tcc_add_file(s1, TCC_LIBGCC);
else
tcc_add_dll(s1, TCC_LIBGCC, 0);
}
#endif
tcc_add_support(s1, TCC_LIBTCC1);
#if CONFIG_TCC_LIBTCC1_MES
tcc_add_support(s1, TCC_LIBTCC1_MES);
#endif
/* add crt end if not memory output */
if (s1->output_type != TCC_OUTPUT_MEMORY)
tcc_add_crt(s1, "crtn.o");
}
}
/* add various standard linker symbols (must be done after the
sections are filled (for example after allocating common
symbols)) */
ST_FUNC void tcc_add_linker_symbols(TCCState *s1)
{
char buf[1024];
int i;
Section *s;
set_elf_sym(symtab_section,
text_section->data_offset, 0,
ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
text_section->sh_num, "_etext");
set_elf_sym(symtab_section,
data_section->data_offset, 0,
ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
data_section->sh_num, "_edata");
set_elf_sym(symtab_section,
bss_section->data_offset, 0,
ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
bss_section->sh_num, "_end");
#ifndef TCC_TARGET_PE
/* horrible new standard ldscript defines */
add_init_array_defines(s1, ".preinit_array");
add_init_array_defines(s1, ".init_array");
add_init_array_defines(s1, ".fini_array");
#endif
/* add start and stop symbols for sections whose name can be
expressed in C */
for(i = 1; i < s1->nb_sections; i++) {
s = s1->sections[i];
if (s->sh_type == SHT_PROGBITS &&
(s->sh_flags & SHF_ALLOC)) {
const char *p;
int ch;
/* check if section name can be expressed in C */
p = s->name;
for(;;) {
ch = *p;
if (!ch)
break;
if (!isid(ch) && !isnum(ch))
goto next_sec;
p++;
}
snprintf(buf, sizeof(buf), "__start_%s", s->name);
set_elf_sym(symtab_section,
0, 0,
ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
s->sh_num, buf);
snprintf(buf, sizeof(buf), "__stop_%s", s->name);
set_elf_sym(symtab_section,
s->data_offset, 0,
ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
s->sh_num, buf);
}
next_sec: ;
}
}
static void tcc_output_binary(TCCState *s1, FILE *f,
const int *sec_order)
{
Section *s;
int i, offset, size;
offset = 0;
for(i=1;i<s1->nb_sections;i++) {
s = s1->sections[sec_order[i]];
if (s->sh_type != SHT_NOBITS &&
(s->sh_flags & SHF_ALLOC)) {
while (offset < s->sh_offset) {
fputc(0, f);
offset++;
}
size = s->sh_size;
fwrite(s->data, 1, size, f);
offset += size;
}
}
}
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#define HAVE_PHDR 1
#define EXTRA_RELITEMS 14
#else
#define HAVE_PHDR 1
#define EXTRA_RELITEMS 9
#endif
ST_FUNC void fill_got_entry(TCCState *s1, ElfW_Rel *rel)
{
int sym_index = ELFW(R_SYM) (rel->r_info);
ElfW(Sym) *sym = &((ElfW(Sym) *) symtab_section->data)[sym_index];
struct sym_attr *attr = get_sym_attr(s1, sym_index, 0);
unsigned offset = attr->got_offset;
if (0 == offset)
return;
section_reserve(s1->got, offset + PTR_SIZE);
#ifdef TCC_TARGET_X86_64
write64le(s1->got->data + offset, sym->st_value);
#else
write32le(s1->got->data + offset, sym->st_value);
#endif
}
/* Perform relocation to GOT or PLT entries */
ST_FUNC void fill_got(TCCState *s1)
{
Section *s;
ElfW_Rel *rel;
int i;
for(i = 1; i < s1->nb_sections; i++) {
s = s1->sections[i];
if (s->sh_type != SHT_RELX)
continue;
/* no need to handle got relocations */
if (s->link != symtab_section)
continue;
for_each_elem(s, 0, rel, ElfW_Rel) {
switch (ELFW(R_TYPE) (rel->r_info)) {
case R_X86_64_GOT32:
case R_X86_64_GOTPCREL:
case R_X86_64_GOTPCRELX:
case R_X86_64_REX_GOTPCRELX:
case R_X86_64_PLT32:
fill_got_entry(s1, rel);
break;
}
}
}
}
/* See put_got_entry for a description. This is the second stage
where GOT references to local defined symbols are rewritten. */
static void fill_local_got_entries(TCCState *s1)
{
ElfW_Rel *rel;
for_each_elem(s1->got->reloc, 0, rel, ElfW_Rel) {
if (ELFW(R_TYPE)(rel->r_info) == R_RELATIVE) {
int sym_index = ELFW(R_SYM) (rel->r_info);
ElfW(Sym) *sym = &((ElfW(Sym) *) symtab_section->data)[sym_index];
struct sym_attr *attr = get_sym_attr(s1, sym_index, 0);
unsigned offset = attr->got_offset;
if (offset != rel->r_offset - s1->got->sh_addr)
tcc_error_noabort("huh");
rel->r_info = ELFW(R_INFO)(0, R_RELATIVE);
#if SHT_RELX == SHT_RELA
rel->r_addend = sym->st_value;
#else
/* All our REL architectures also happen to be 32bit LE. */
write32le(s1->got->data + offset, sym->st_value);
#endif
}
}
}
/* Bind symbols of executable: resolve undefined symbols from exported symbols
in shared libraries and export non local defined symbols to shared libraries
if -rdynamic switch was given on command line */
static void bind_exe_dynsyms(TCCState *s1)
{
const char *name;
int sym_index, index;
ElfW(Sym) *sym, *esym;
int type;
/* Resolve undefined symbols from dynamic symbols. When there is a match:
- if STT_FUNC or STT_GNU_IFUNC symbol -> add it in PLT
- if STT_OBJECT symbol -> add it in .bss section with suitable reloc */
for_each_elem(symtab_section, 1, sym, ElfW(Sym)) {
if (sym->st_shndx == SHN_UNDEF) {
name = (char *) symtab_section->link->data + sym->st_name;
sym_index = find_elf_sym(s1->dynsymtab_section, name);
if (sym_index) {
esym = &((ElfW(Sym) *)s1->dynsymtab_section->data)[sym_index];
type = ELFW(ST_TYPE)(esym->st_info);
if ((type == STT_FUNC) || (type == STT_GNU_IFUNC)) {
/* Indirect functions shall have STT_FUNC type in executable
* dynsym section. Indeed, a dlsym call following a lazy
* resolution would pick the symbol value from the
* executable dynsym entry which would contain the address
* of the function wanted by the caller of dlsym instead of
* the address of the function that would return that
* address */
int dynindex
= put_elf_sym(s1->dynsym, 0, esym->st_size,
ELFW(ST_INFO)(STB_GLOBAL,STT_FUNC), 0, 0,
name);
int index = sym - (ElfW(Sym) *) symtab_section->data;
get_sym_attr(s1, index, 1)->dyn_index = dynindex;
} else if (type == STT_OBJECT) {
unsigned long offset;
ElfW(Sym) *dynsym;
offset = bss_section->data_offset;
/* XXX: which alignment ? */
offset = (offset + 16 - 1) & -16;
set_elf_sym (s1->symtab, offset, esym->st_size,
esym->st_info, 0, bss_section->sh_num, name);
index = put_elf_sym(s1->dynsym, offset, esym->st_size,
esym->st_info, 0, bss_section->sh_num,
name);
/* Ensure R_COPY works for weak symbol aliases */
if (ELFW(ST_BIND)(esym->st_info) == STB_WEAK) {
for_each_elem(s1->dynsymtab_section, 1, dynsym, ElfW(Sym)) {
if ((dynsym->st_value == esym->st_value)
&& (ELFW(ST_BIND)(dynsym->st_info) == STB_GLOBAL)) {
char *dynname = (char *) s1->dynsymtab_section->link->data
+ dynsym->st_name;
put_elf_sym(s1->dynsym, offset, dynsym->st_size,
dynsym->st_info, 0,
bss_section->sh_num, dynname);
break;
}
}
}
put_elf_reloc(s1->dynsym, bss_section,
offset, R_COPY, index);
offset += esym->st_size;
bss_section->data_offset = offset;
}
} else {
/* STB_WEAK undefined symbols are accepted */
/* XXX: _fp_hw seems to be part of the ABI, so we ignore it */
if (ELFW(ST_BIND)(sym->st_info) == STB_WEAK ||
!strcmp(name, "_fp_hw")) {
} else {
tcc_error_noabort("undefined symbol '%s'", name);
}
}
} else if (s1->rdynamic && ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) {
/* if -rdynamic option, then export all non local symbols */
name = (char *) symtab_section->link->data + sym->st_name;
set_elf_sym(s1->dynsym, sym->st_value, sym->st_size, sym->st_info,
0, sym->st_shndx, name);
}
}
}
/* Bind symbols of libraries: export all non local symbols of executable that
are referenced by shared libraries. The reason is that the dynamic loader
search symbol first in executable and then in libraries. Therefore a
reference to a symbol already defined by a library can still be resolved by
a symbol in the executable. */
static void bind_libs_dynsyms(TCCState *s1)
{
const char *name;
int sym_index;
ElfW(Sym) *sym, *esym;
for_each_elem(s1->dynsymtab_section, 1, esym, ElfW(Sym)) {
name = (char *) s1->dynsymtab_section->link->data + esym->st_name;
sym_index = find_elf_sym(symtab_section, name);
/* XXX: avoid adding a symbol if already present because of
-rdynamic ? */
sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
if (sym_index && sym->st_shndx != SHN_UNDEF)
set_elf_sym(s1->dynsym, sym->st_value, sym->st_size, sym->st_info,
0, sym->st_shndx, name);
else if (esym->st_shndx == SHN_UNDEF) {
/* weak symbols can stay undefined */
if (ELFW(ST_BIND)(esym->st_info) != STB_WEAK)
tcc_warning("undefined dynamic symbol '%s'", name);
}
}
}
/* Export all non local symbols. This is used by shared libraries so that the
non local symbols they define can resolve a reference in another shared
library or in the executable. Correspondingly, it allows undefined local
symbols to be resolved by other shared libraries or by the executable. */
static void export_global_syms(TCCState *s1)
{
int dynindex, index;
const char *name;
ElfW(Sym) *sym;
for_each_elem(symtab_section, 1, sym, ElfW(Sym)) {
if (ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) {
name = (char *) symtab_section->link->data + sym->st_name;
dynindex = put_elf_sym(s1->dynsym, sym->st_value, sym->st_size,
sym->st_info, 0, sym->st_shndx, name);
index = sym - (ElfW(Sym) *) symtab_section->data;
get_sym_attr(s1, index, 1)->dyn_index = dynindex;
}
}
}
/* Allocate strings for section names and decide if an unallocated section
should be output.
NOTE: the strsec section comes last, so its size is also correct ! */
static void alloc_sec_names(TCCState *s1, int file_type, Section *strsec)
{
int i;
Section *s;
/* Allocate strings for section names */
for(i = 1; i < s1->nb_sections; i++) {
s = s1->sections[i];
/* when generating a DLL, we include relocations but we may
patch them */
if (file_type == TCC_OUTPUT_DLL &&
s->sh_type == SHT_RELX &&
!(s->sh_flags & SHF_ALLOC)) {
/* gr: avoid bogus relocs for empty (debug) sections */
if (s1->sections[s->sh_info]->sh_flags & SHF_ALLOC)
prepare_dynamic_rel(s1, s);
else if (s1->do_debug)
s->sh_size = s->data_offset;
} else if (s1->do_debug ||
file_type == TCC_OUTPUT_OBJ ||
(s->sh_flags & SHF_ALLOC) ||
i == (s1->nb_sections - 1)) {
/* we output all sections if debug or object file */
s->sh_size = s->data_offset;
}
if (s->sh_size || (s->sh_flags & SHF_ALLOC))
s->sh_name = put_elf_str(strsec, s->name);
}
strsec->sh_size = strsec->data_offset;
}
/* Info to be copied in dynamic section */
struct dyn_inf {
Section *dynamic;
Section *dynstr;
unsigned long dyn_rel_off;
addr_t rel_addr;
addr_t rel_size;
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
addr_t bss_addr;
addr_t bss_size;
#endif
};
/* Assign sections to segments and decide how are sections laid out when loaded
in memory. This function also fills corresponding program headers. */
static int layout_sections(TCCState *s1, ElfW(Phdr) *phdr, int phnum,
Section *interp, Section* strsec,
struct dyn_inf *dyninf, int *sec_order)
{
int i, j, k, file_type, sh_order_index, file_offset;
unsigned long s_align;
long long tmp;
addr_t addr;
ElfW(Phdr) *ph;
Section *s;
file_type = s1->output_type;
sh_order_index = 1;
file_offset = 0;
if (s1->output_format == TCC_OUTPUT_FORMAT_ELF)
file_offset = sizeof(ElfW(Ehdr)) + phnum * sizeof(ElfW(Phdr));
s_align = ELF_PAGE_SIZE;
if (s1->section_align)
s_align = s1->section_align;
if (phnum > 0) {
if (s1->has_text_addr) {
int a_offset, p_offset;
addr = s1->text_addr;
/* we ensure that (addr % ELF_PAGE_SIZE) == file_offset %
ELF_PAGE_SIZE */
a_offset = (int) (addr & (s_align - 1));
p_offset = file_offset & (s_align - 1);
if (a_offset < p_offset)
a_offset += s_align;
file_offset += (a_offset - p_offset);
} else {
if (file_type == TCC_OUTPUT_DLL)
addr = 0;
else
addr = ELF_START_ADDR;
/* compute address after headers */
addr += (file_offset & (s_align - 1));
}
ph = &phdr[0];
/* Leave one program headers for the program interpreter and one for
the program header table itself if needed. These are done later as
they require section layout to be done first. */
if (interp)
ph += 1 + HAVE_PHDR;
/* dynamic relocation table information, for .dynamic section */
dyninf->rel_addr = dyninf->rel_size = 0;
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
dyninf->bss_addr = dyninf->bss_size = 0;
#endif
for(j = 0; j < 2; j++) {
ph->p_type = PT_LOAD;
if (j == 0)
ph->p_flags = PF_R | PF_X;
else
ph->p_flags = PF_R | PF_W;
ph->p_align = s_align;
/* Decide the layout of sections loaded in memory. This must
be done before program headers are filled since they contain
info about the layout. We do the following ordering: interp,
symbol tables, relocations, progbits, nobits */
/* XXX: do faster and simpler sorting */
for(k = 0; k < 5; k++) {
for(i = 1; i < s1->nb_sections; i++) {
s = s1->sections[i];
/* compute if section should be included */
if (j == 0) {
if ((s->sh_flags & (SHF_ALLOC | SHF_WRITE)) !=
SHF_ALLOC)
continue;
} else {
if ((s->sh_flags & (SHF_ALLOC | SHF_WRITE)) !=
(SHF_ALLOC | SHF_WRITE))
continue;
}
if (s == interp) {
if (k != 0)
continue;
} else if (s->sh_type == SHT_DYNSYM ||
s->sh_type == SHT_STRTAB ||
s->sh_type == SHT_HASH) {
if (k != 1)
continue;
} else if (s->sh_type == SHT_RELX) {
if (k != 2)
continue;
} else if (s->sh_type == SHT_NOBITS) {
if (k != 4)
continue;
} else {
if (k != 3)
continue;
}
sec_order[sh_order_index++] = i;
/* section matches: we align it and add its size */
tmp = addr;
addr = (addr + s->sh_addralign - 1) &
~(s->sh_addralign - 1);
file_offset += (int) ( addr - tmp );
s->sh_offset = file_offset;
s->sh_addr = addr;
/* update program header infos */
if (ph->p_offset == 0) {
ph->p_offset = file_offset;
ph->p_vaddr = addr;
ph->p_paddr = ph->p_vaddr;
}
/* update dynamic relocation infos */
if (s->sh_type == SHT_RELX) {
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
if (!strcmp(strsec->data + s->sh_name, ".rel.got")) {
dyninf->rel_addr = addr;
dyninf->rel_size += s->sh_size; /* XXX only first rel. */
}
if (!strcmp(strsec->data + s->sh_name, ".rel.bss")) {
dyninf->bss_addr = addr;
dyninf->bss_size = s->sh_size; /* XXX only first rel. */
}
#else
if (dyninf->rel_size == 0)
dyninf->rel_addr = addr;
dyninf->rel_size += s->sh_size;
#endif
}
addr += s->sh_size;
if (s->sh_type != SHT_NOBITS)
file_offset += s->sh_size;
}
}
if (j == 0) {
/* Make the first PT_LOAD segment include the program
headers itself (and the ELF header as well), it'll
come out with same memory use but will make various
tools like binutils strip work better. */
ph->p_offset &= ~(ph->p_align - 1);
ph->p_vaddr &= ~(ph->p_align - 1);
ph->p_paddr &= ~(ph->p_align - 1);
}
ph->p_filesz = file_offset - ph->p_offset;
ph->p_memsz = addr - ph->p_vaddr;
ph++;
if (j == 0) {
if (s1->output_format == TCC_OUTPUT_FORMAT_ELF) {
/* if in the middle of a page, we duplicate the page in
memory so that one copy is RX and the other is RW */
if ((addr & (s_align - 1)) != 0)
addr += s_align;
} else {
addr = (addr + s_align - 1) & ~(s_align - 1);
file_offset = (file_offset + s_align - 1) & ~(s_align - 1);
}
}
}
}
/* all other sections come after */
for(i = 1; i < s1->nb_sections; i++) {
s = s1->sections[i];
if (phnum > 0 && (s->sh_flags & SHF_ALLOC))
continue;
sec_order[sh_order_index++] = i;
file_offset = (file_offset + s->sh_addralign - 1) &
~(s->sh_addralign - 1);
s->sh_offset = file_offset;
if (s->sh_type != SHT_NOBITS)
file_offset += s->sh_size;
}
return file_offset;
}
static void fill_unloadable_phdr(ElfW(Phdr) *phdr, int phnum, Section *interp,
Section *dynamic)
{
ElfW(Phdr) *ph;
/* if interpreter, then add corresponding program header */
if (interp) {
ph = &phdr[0];
if (HAVE_PHDR)
{
int len = phnum * sizeof(ElfW(Phdr));
ph->p_type = PT_PHDR;
ph->p_offset = sizeof(ElfW(Ehdr));
ph->p_vaddr = interp->sh_addr - len;
ph->p_paddr = ph->p_vaddr;
ph->p_filesz = ph->p_memsz = len;
ph->p_flags = PF_R | PF_X;
ph->p_align = 4; /* interp->sh_addralign; */
ph++;
}
ph->p_type = PT_INTERP;
ph->p_offset = interp->sh_offset;
ph->p_vaddr = interp->sh_addr;
ph->p_paddr = ph->p_vaddr;
ph->p_filesz = interp->sh_size;
ph->p_memsz = interp->sh_size;
ph->p_flags = PF_R;
ph->p_align = interp->sh_addralign;
}
/* if dynamic section, then add corresponding program header */
if (dynamic) {
ph = &phdr[phnum - 1];
ph->p_type = PT_DYNAMIC;
ph->p_offset = dynamic->sh_offset;
ph->p_vaddr = dynamic->sh_addr;
ph->p_paddr = ph->p_vaddr;
ph->p_filesz = dynamic->sh_size;
ph->p_memsz = dynamic->sh_size;
ph->p_flags = PF_R | PF_W;
ph->p_align = dynamic->sh_addralign;
}
}
/* Fill the dynamic section with tags describing the address and size of
sections */
static void fill_dynamic(TCCState *s1, struct dyn_inf *dyninf)
{
Section *dynamic;
dynamic = dyninf->dynamic;
/* put dynamic section entries */
dynamic->data_offset = dyninf->dyn_rel_off;
put_dt(dynamic, DT_HASH, s1->dynsym->hash->sh_addr);
put_dt(dynamic, DT_STRTAB, dyninf->dynstr->sh_addr);
put_dt(dynamic, DT_SYMTAB, s1->dynsym->sh_addr);
put_dt(dynamic, DT_STRSZ, dyninf->dynstr->data_offset);
put_dt(dynamic, DT_SYMENT, sizeof(ElfW(Sym)));
#if PTR_SIZE == 8
put_dt(dynamic, DT_RELA, dyninf->rel_addr);
put_dt(dynamic, DT_RELASZ, dyninf->rel_size);
put_dt(dynamic, DT_RELAENT, sizeof(ElfW_Rel));
#else
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
put_dt(dynamic, DT_PLTGOT, s1->got->sh_addr);
put_dt(dynamic, DT_PLTRELSZ, dyninf->rel_size);
put_dt(dynamic, DT_JMPREL, dyninf->rel_addr);
put_dt(dynamic, DT_PLTREL, DT_REL);
put_dt(dynamic, DT_REL, dyninf->bss_addr);
put_dt(dynamic, DT_RELSZ, dyninf->bss_size);
#else
put_dt(dynamic, DT_REL, dyninf->rel_addr);
put_dt(dynamic, DT_RELSZ, dyninf->rel_size);
put_dt(dynamic, DT_RELENT, sizeof(ElfW_Rel));
#endif
#endif
if (s1->do_debug)
put_dt(dynamic, DT_DEBUG, 0);
put_dt(dynamic, DT_NULL, 0);
}
/* Relocate remaining sections and symbols (that is those not related to
dynamic linking) */
static int final_sections_reloc(TCCState *s1)
{
int i;
Section *s;
relocate_syms(s1, s1->symtab, 0);
if (s1->nb_errors != 0)
return -1;
/* relocate sections */
/* XXX: ignore sections with allocated relocations ? */
for(i = 1; i < s1->nb_sections; i++) {
s = s1->sections[i];
#if defined(TCC_TARGET_I386) || defined(TCC_MUSL)
if (s->reloc && s != s1->got && (s->sh_flags & SHF_ALLOC)) //gr
/* On X86 gdb 7.3 works in any case but gdb 6.6 will crash if SHF_ALLOC
checking is removed */
#else
if (s->reloc && s != s1->got)
/* On X86_64 gdb 7.3 will crash if SHF_ALLOC checking is present */
#endif
relocate_section(s1, s);
}
/* relocate relocation entries if the relocation tables are
allocated in the executable */
for(i = 1; i < s1->nb_sections; i++) {
s = s1->sections[i];
if ((s->sh_flags & SHF_ALLOC) &&
s->sh_type == SHT_RELX) {
relocate_rel(s1, s);
}
}
return 0;
}
/* Create an ELF file on disk.
This function handle ELF specific layout requirements */
static void tcc_output_elf(TCCState *s1, FILE *f, int phnum, ElfW(Phdr) *phdr,
int file_offset, int *sec_order)
{
int i, shnum, offset, size, file_type;
Section *s;
ElfW(Ehdr) ehdr;
ElfW(Shdr) shdr, *sh;
file_type = s1->output_type;
shnum = s1->nb_sections;
memset(&ehdr, 0, sizeof(ehdr));
if (phnum > 0) {
ehdr.e_phentsize = sizeof(ElfW(Phdr));
ehdr.e_phnum = phnum;
ehdr.e_phoff = sizeof(ElfW(Ehdr));
}
/* align to 4 */
file_offset = (file_offset + 3) & -4;
/* fill header */
ehdr.e_ident[0] = ELFMAG0;
ehdr.e_ident[1] = ELFMAG1;
ehdr.e_ident[2] = ELFMAG2;
ehdr.e_ident[3] = ELFMAG3;
ehdr.e_ident[4] = ELFCLASSW;
ehdr.e_ident[5] = ELFDATA2LSB;
ehdr.e_ident[6] = EV_CURRENT;
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
ehdr.e_ident[EI_OSABI] = ELFOSABI_FREEBSD;
#endif
#ifdef TCC_TARGET_ARM
#if defined (TCC_ARM_EABI) || BOOTSTRAP
ehdr.e_ident[EI_OSABI] = 0;
ehdr.e_flags = EF_ARM_EABI_VER4;
if (file_type == TCC_OUTPUT_EXE || file_type == TCC_OUTPUT_DLL)
ehdr.e_flags |= EF_ARM_HASENTRY;
if (s1->float_abi == ARM_HARD_FLOAT)
ehdr.e_flags |= EF_ARM_VFP_FLOAT;
else
ehdr.e_flags |= EF_ARM_SOFT_FLOAT;
#else
ehdr.e_ident[EI_OSABI] = ELFOSABI_ARM;
#endif
#endif
switch(file_type) {
default:
case TCC_OUTPUT_EXE:
ehdr.e_type = ET_EXEC;
ehdr.e_entry = get_elf_sym_addr(s1, "_start", 1);
break;
case TCC_OUTPUT_DLL:
ehdr.e_type = ET_DYN;
ehdr.e_entry = text_section->sh_addr; /* XXX: is it correct ? */
break;
case TCC_OUTPUT_OBJ:
ehdr.e_type = ET_REL;
break;
}
ehdr.e_machine = EM_TCC_TARGET;
ehdr.e_version = EV_CURRENT;
ehdr.e_shoff = file_offset;
ehdr.e_ehsize = sizeof(ElfW(Ehdr));
ehdr.e_shentsize = sizeof(ElfW(Shdr));
ehdr.e_shnum = shnum;
ehdr.e_shstrndx = shnum - 1;
fwrite(&ehdr, 1, sizeof(ElfW(Ehdr)), f);
fwrite(phdr, 1, phnum * sizeof(ElfW(Phdr)), f);
offset = sizeof(ElfW(Ehdr)) + phnum * sizeof(ElfW(Phdr));
sort_syms(s1, symtab_section);
for(i = 1; i < s1->nb_sections; i++) {
s = s1->sections[sec_order[i]];
if (s->sh_type != SHT_NOBITS) {
while (offset < s->sh_offset) {
fputc(0, f);
offset++;
}
size = s->sh_size;
if (size)
fwrite(s->data, 1, size, f);
offset += size;
}
}
/* output section headers */
while (offset < ehdr.e_shoff) {
fputc(0, f);
offset++;
}
for(i = 0; i < s1->nb_sections; i++) {
sh = &shdr;
memset(sh, 0, sizeof(ElfW(Shdr)));
s = s1->sections[i];
if (s) {
sh->sh_name = s->sh_name;
sh->sh_type = s->sh_type;
sh->sh_flags = s->sh_flags;
sh->sh_entsize = s->sh_entsize;
sh->sh_info = s->sh_info;
if (s->link)
sh->sh_link = s->link->sh_num;
sh->sh_addralign = s->sh_addralign;
sh->sh_addr = s->sh_addr;
sh->sh_offset = s->sh_offset;
sh->sh_size = s->sh_size;
}
fwrite(sh, 1, sizeof(ElfW(Shdr)), f);
}
}
/* Write an elf, coff or "binary" file */
static int tcc_write_elf_file(TCCState *s1, const char *filename, int phnum,
ElfW(Phdr) *phdr, int file_offset, int *sec_order)
{
int fd, mode, file_type;
FILE *f;
file_type = s1->output_type;
if (file_type == TCC_OUTPUT_OBJ)
mode = 0666;
else
mode = 0777;
unlink(filename);
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, mode);
if (fd < 0) {
tcc_error_noabort("could not write '%s'", filename);
return -1;
}
f = fdopen(fd, "wb");
if (s1->verbose)
printf("<- %s\n", filename);
#ifdef TCC_TARGET_COFF
if (s1->output_format == TCC_OUTPUT_FORMAT_COFF)
tcc_output_coff(s1, f);
else
#endif
if (s1->output_format == TCC_OUTPUT_FORMAT_ELF)
tcc_output_elf(s1, f, phnum, phdr, file_offset, sec_order);
else
tcc_output_binary(s1, f, sec_order);
fclose(f);
return 0;
}
/* Sort section headers by assigned sh_addr, remove sections
that we aren't going to output. */
static void tidy_section_headers(TCCState *s1, int *sec_order)
{
int i, nnew, l, *backmap;
Section **snew, *s;
ElfW(Sym) *sym;
snew = tcc_malloc(s1->nb_sections * sizeof(snew[0]));
backmap = tcc_malloc(s1->nb_sections * sizeof(backmap[0]));
for (i = 0, nnew = 0, l = s1->nb_sections; i < s1->nb_sections; i++) {
s = s1->sections[sec_order[i]];
if (!i || s->sh_name) {
backmap[sec_order[i]] = nnew;
snew[nnew] = s;
++nnew;
} else {
backmap[sec_order[i]] = 0;
snew[--l] = s;
}
}
for (i = 0; i < nnew; i++) {
s = snew[i];
if (s) {
s->sh_num = i;
if (s->sh_type == SHT_RELX)
s->sh_info = backmap[s->sh_info];
}
}
for_each_elem(symtab_section, 1, sym, ElfW(Sym))
if (sym->st_shndx != SHN_UNDEF && sym->st_shndx < SHN_LORESERVE)
sym->st_shndx = backmap[sym->st_shndx];
for_each_elem(s1->dynsym, 1, sym, ElfW(Sym))
if (sym->st_shndx != SHN_UNDEF && sym->st_shndx < SHN_LORESERVE)
sym->st_shndx = backmap[sym->st_shndx];
for (i = 0; i < s1->nb_sections; i++)
sec_order[i] = i;
tcc_free(s1->sections);
s1->sections = snew;
s1->nb_sections = nnew;
tcc_free(backmap);
}
/* Output an elf, coff or binary file */
/* XXX: suppress unneeded sections */
static int elf_output_file(TCCState *s1, const char *filename)
{
int i, ret, phnum, shnum, file_type, file_offset, *sec_order;
struct dyn_inf dyninf = {0};
ElfW(Phdr) *phdr;
ElfW(Sym) *sym;
Section *strsec, *interp, *dynamic, *dynstr;
file_type = s1->output_type;
s1->nb_errors = 0;
/* if linking, also link in runtime libraries (libc, libgcc, etc.) */
if (file_type != TCC_OUTPUT_OBJ) {
tcc_add_runtime(s1);
}
phdr = NULL;
sec_order = NULL;
interp = dynamic = dynstr = NULL; /* avoid warning */
if (file_type != TCC_OUTPUT_OBJ) {
relocate_common_syms();
tcc_add_linker_symbols(s1);
if (!s1->static_link) {
if (file_type == TCC_OUTPUT_EXE) {
char *ptr;
/* allow override the dynamic loader */
const char *elfint = getenv("LD_SO");
if (elfint == NULL)
elfint = DEFAULT_ELFINTERP(s1);
/* add interpreter section only if executable */
interp = new_section(s1, ".interp", SHT_PROGBITS, SHF_ALLOC);
interp->sh_addralign = 1;
ptr = section_ptr_add(interp, 1 + strlen(elfint));
strcpy(ptr, elfint);
}
/* add dynamic symbol table */
s1->dynsym = new_symtab(s1, ".dynsym", SHT_DYNSYM, SHF_ALLOC,
".dynstr",
".hash", SHF_ALLOC);
dynstr = s1->dynsym->link;
/* add dynamic section */
dynamic = new_section(s1, ".dynamic", SHT_DYNAMIC,
SHF_ALLOC | SHF_WRITE);
dynamic->link = dynstr;
dynamic->sh_entsize = sizeof(ElfW(Dyn));
build_got(s1);
if (file_type == TCC_OUTPUT_EXE) {
bind_exe_dynsyms(s1);
if (s1->nb_errors) {
ret = -1;
goto the_end;
}
bind_libs_dynsyms(s1);
} else /* shared library case: simply export all global symbols */
export_global_syms(s1);
build_got_entries(s1);
/* add a list of needed dlls */
for(i = 0; i < s1->nb_loaded_dlls; i++) {
DLLReference *dllref = s1->loaded_dlls[i];
if (dllref->level == 0)
put_dt(dynamic, DT_NEEDED, put_elf_str(dynstr, dllref->name));
}
if (s1->rpath)
put_dt(dynamic, s1->enable_new_dtags ? DT_RUNPATH : DT_RPATH,
put_elf_str(dynstr, s1->rpath));
/* XXX: currently, since we do not handle PIC code, we
must relocate the readonly segments */
if (file_type == TCC_OUTPUT_DLL) {
if (s1->soname)
put_dt(dynamic, DT_SONAME, put_elf_str(dynstr, s1->soname));
put_dt(dynamic, DT_TEXTREL, 0);
}
if (s1->symbolic)
put_dt(dynamic, DT_SYMBOLIC, 0);
/* add necessary space for other entries */
dyninf.dyn_rel_off = dynamic->data_offset;
dynamic->data_offset += sizeof(ElfW(Dyn)) * EXTRA_RELITEMS;
} else {
/* still need to build got entries in case of static link */
build_got_entries(s1);
}
}
/* we add a section for symbols */
strsec = new_section(s1, ".shstrtab", SHT_STRTAB, 0);
put_elf_str(strsec, "");
/* compute number of sections */
shnum = s1->nb_sections;
/* this array is used to reorder sections in the output file */
sec_order = tcc_malloc(sizeof(int) * shnum);
sec_order[0] = 0;
/* compute number of program headers */
switch(file_type) {
default:
case TCC_OUTPUT_OBJ:
phnum = 0;
break;
case TCC_OUTPUT_EXE:
if (!s1->static_link)
phnum = 4 + HAVE_PHDR;
else
phnum = 2;
break;
case TCC_OUTPUT_DLL:
phnum = 3;
break;
}
/* Allocate strings for section names */
alloc_sec_names(s1, file_type, strsec);
/* allocate program segment headers */
phdr = tcc_mallocz(phnum * sizeof(ElfW(Phdr)));
/* compute section to program header mapping */
file_offset = layout_sections(s1, phdr, phnum, interp, strsec, &dyninf,
sec_order);
/* Fill remaining program header and finalize relocation related to dynamic
linking. */
if (phnum > 0) {
fill_unloadable_phdr(phdr, phnum, interp, dynamic);
if (dynamic) {
dyninf.dynamic = dynamic;
dyninf.dynstr = dynstr;
fill_dynamic(s1, &dyninf);
/* put in GOT the dynamic section address and relocate PLT */
write32le(s1->got->data, dynamic->sh_addr);
if (file_type == TCC_OUTPUT_EXE
|| (RELOCATE_DLLPLT && file_type == TCC_OUTPUT_DLL))
relocate_plt(s1);
/* relocate symbols in .dynsym now that final addresses are known */
for_each_elem(s1->dynsym, 1, sym, ElfW(Sym)) {
if (sym->st_shndx != SHN_UNDEF && sym->st_shndx < SHN_LORESERVE) {
/* do symbol relocation */
sym->st_value += s1->sections[sym->st_shndx]->sh_addr;
}
}
}
}
/* if building executable or DLL, then relocate each section
except the GOT which is already relocated */
if (file_type != TCC_OUTPUT_OBJ) {
ret = final_sections_reloc(s1);
if (ret)
goto the_end;
#if !BOOTSTRAP
tidy_section_headers(s1, sec_order);
#endif
}
/* Perform relocation to GOT or PLT entries */
if (file_type == TCC_OUTPUT_EXE && s1->static_link)
fill_got(s1);
else if (s1->got)
fill_local_got_entries(s1);
/* Create the ELF file with name 'filename' */
ret = tcc_write_elf_file(s1, filename, phnum, phdr, file_offset, sec_order);
s1->nb_sections = shnum;
the_end:
tcc_free(sec_order);
tcc_free(phdr);
return ret;
}
LIBTCCAPI int tcc_output_file(TCCState *s, const char *filename)
{
int ret;
#ifdef TCC_TARGET_PE
if (s->output_type != TCC_OUTPUT_OBJ) {
ret = pe_output_file(s, filename);
} else
#endif
ret = elf_output_file(s, filename);
return ret;
}
static void *load_data(int fd, unsigned long file_offset, unsigned long size)
{
void *data;
data = tcc_malloc(size);
lseek(fd, file_offset, SEEK_SET);
read(fd, data, size);
return data;
}
typedef struct SectionMergeInfo {
Section *s; /* corresponding existing section */
unsigned long offset; /* offset of the new section in the existing section */
uint8_t new_section; /* true if section 's' was added */
uint8_t link_once; /* true if link once section */
} SectionMergeInfo;
ST_FUNC int tcc_object_type(int fd, ElfW(Ehdr) *h)
{
int size = read(fd, h, sizeof *h);
if (size == sizeof *h && 0 == memcmp(h, ELFMAG, 4)) {
if (h->e_type == ET_REL)
return AFF_BINTYPE_REL;
if (h->e_type == ET_DYN)
return AFF_BINTYPE_DYN;
} else if (size >= 8) {
if (0 == memcmp(h, ARMAG, 8))
return AFF_BINTYPE_AR;
#ifdef TCC_TARGET_COFF
if (((struct filehdr*)h)->f_magic == COFF_C67_MAGIC)
return AFF_BINTYPE_C67;
#endif
}
return 0;
}
/* load an object file and merge it with current files */
/* XXX: handle correctly stab (debug) info */
ST_FUNC int tcc_load_object_file(TCCState *s1,
int fd, unsigned long file_offset)
{
ElfW(Ehdr) ehdr;
ElfW(Shdr) *shdr, *sh;
int size, i, j, offset, offseti, nb_syms, sym_index, ret, seencompressed;
unsigned char *strsec, *strtab;
int *old_to_new_syms;
char *sh_name, *name;
SectionMergeInfo *sm_table, *sm;
ElfW(Sym) *sym, *symtab;
ElfW_Rel *rel;
Section *s;
int stab_index;
int stabstr_index;
stab_index = stabstr_index = 0;
lseek(fd, file_offset, SEEK_SET);
if (tcc_object_type(fd, &ehdr) != AFF_BINTYPE_REL)
goto fail1;
/* test CPU specific stuff */
if (ehdr.e_ident[5] != ELFDATA2LSB ||
ehdr.e_machine != EM_TCC_TARGET) {
fail1:
tcc_error_noabort("invalid object file");
return -1;
}
/* read sections */
shdr = load_data(fd, file_offset + ehdr.e_shoff,
sizeof(ElfW(Shdr)) * ehdr.e_shnum);
sm_table = tcc_mallocz(sizeof(SectionMergeInfo) * ehdr.e_shnum);
/* load section names */
sh = &shdr[ehdr.e_shstrndx];
strsec = load_data(fd, file_offset + sh->sh_offset, sh->sh_size);
/* load symtab and strtab */
old_to_new_syms = NULL;
symtab = NULL;
strtab = NULL;
nb_syms = 0;
seencompressed = 0;
for(i = 1; i < ehdr.e_shnum; i++) {
sh = &shdr[i];
if (sh->sh_type == SHT_SYMTAB) {
if (symtab) {
tcc_error_noabort("object must contain only one symtab");
fail:
ret = -1;
goto the_end;
}
nb_syms = sh->sh_size / sizeof(ElfW(Sym));
symtab = load_data(fd, file_offset + sh->sh_offset, sh->sh_size);
sm_table[i].s = symtab_section;
/* now load strtab */
sh = &shdr[sh->sh_link];
strtab = load_data(fd, file_offset + sh->sh_offset, sh->sh_size);
}
if (sh->sh_flags & SHF_COMPRESSED)
seencompressed = 1;
}
/* now examine each section and try to merge its content with the
ones in memory */
for(i = 1; i < ehdr.e_shnum; i++) {
/* no need to examine section name strtab */
if (i == ehdr.e_shstrndx)
continue;
sh = &shdr[i];
sh_name = (char *) strsec + sh->sh_name;
/* ignore sections types we do not handle */
if (sh->sh_type != SHT_PROGBITS &&
sh->sh_type != SHT_RELX &&
#ifdef TCC_ARM_EABI
sh->sh_type != SHT_ARM_EXIDX &&
#endif
sh->sh_type != SHT_NOBITS &&
sh->sh_type != SHT_PREINIT_ARRAY &&
sh->sh_type != SHT_INIT_ARRAY &&
sh->sh_type != SHT_FINI_ARRAY &&
strcmp(sh_name, ".stabstr")
)
continue;
if (seencompressed
&& (!strncmp(sh_name, ".debug_", sizeof(".debug_")-1)
|| (sh->sh_type == SHT_RELX
&& !strncmp((char*)strsec + shdr[sh->sh_info].sh_name,
".debug_", sizeof(".debug_")-1))))
continue;
if (sh->sh_addralign < 1)
sh->sh_addralign = 1;
/* find corresponding section, if any */
for(j = 1; j < s1->nb_sections;j++) {
s = s1->sections[j];
if (!strcmp(s->name, sh_name)) {
if (!strncmp(sh_name, ".gnu.linkonce",
sizeof(".gnu.linkonce") - 1)) {
/* if a 'linkonce' section is already present, we
do not add it again. It is a little tricky as
symbols can still be defined in
it. */
sm_table[i].link_once = 1;
goto next;
} else {
goto found;
}
}
}
/* not found: create new section */
s = new_section(s1, sh_name, sh->sh_type, sh->sh_flags & ~SHF_GROUP);
/* take as much info as possible from the section. sh_link and
sh_info will be updated later */
s->sh_addralign = sh->sh_addralign;
s->sh_entsize = sh->sh_entsize;
sm_table[i].new_section = 1;
found:
if (sh->sh_type != s->sh_type) {
tcc_error_noabort("invalid section type");
goto fail;
}
/* align start of section */
offset = s->data_offset;
if (0 == strcmp(sh_name, ".stab")) {
stab_index = i;
goto no_align;
}
if (0 == strcmp(sh_name, ".stabstr")) {
stabstr_index = i;
goto no_align;
}
size = sh->sh_addralign - 1;
offset = (offset + size) & ~size;
if (sh->sh_addralign > s->sh_addralign)
s->sh_addralign = sh->sh_addralign;
s->data_offset = offset;
no_align:
sm_table[i].offset = offset;
sm_table[i].s = s;
/* concatenate sections */
size = sh->sh_size;
if (sh->sh_type != SHT_NOBITS) {
unsigned char *ptr;
lseek(fd, file_offset + sh->sh_offset, SEEK_SET);
ptr = section_ptr_add(s, size);
read(fd, ptr, size);
} else {
s->data_offset += size;
}
next: ;
}
/* gr relocate stab strings */
if (stab_index && stabstr_index) {
Stab_Sym *a, *b;
unsigned o;
s = sm_table[stab_index].s;
a = (Stab_Sym *)(s->data + sm_table[stab_index].offset);
b = (Stab_Sym *)(s->data + s->data_offset);
o = sm_table[stabstr_index].offset;
while (a < b)
a->n_strx += o, a++;
}
/* second short pass to update sh_link and sh_info fields of new
sections */
for(i = 1; i < ehdr.e_shnum; i++) {
s = sm_table[i].s;
if (!s || !sm_table[i].new_section)
continue;
sh = &shdr[i];
if (sh->sh_link > 0)
s->link = sm_table[sh->sh_link].s;
if (sh->sh_type == SHT_RELX) {
s->sh_info = sm_table[sh->sh_info].s->sh_num;
/* update backward link */
s1->sections[s->sh_info]->reloc = s;
}
}
sm = sm_table;
/* resolve symbols */
old_to_new_syms = tcc_mallocz(nb_syms * sizeof(int));
sym = symtab + 1;
for(i = 1; i < nb_syms; i++, sym++) {
if (sym->st_shndx != SHN_UNDEF &&
sym->st_shndx < SHN_LORESERVE) {
sm = &sm_table[sym->st_shndx];
if (sm->link_once) {
/* if a symbol is in a link once section, we use the
already defined symbol. It is very important to get
correct relocations */
if (ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) {
name = (char *) strtab + sym->st_name;
sym_index = find_elf_sym(symtab_section, name);
if (sym_index)
old_to_new_syms[i] = sym_index;
}
continue;
}
/* if no corresponding section added, no need to add symbol */
if (!sm->s)
continue;
/* convert section number */
sym->st_shndx = sm->s->sh_num;
/* offset value */
sym->st_value += sm->offset;
}
/* add symbol */
name = (char *) strtab + sym->st_name;
sym_index = set_elf_sym(symtab_section, sym->st_value, sym->st_size,
sym->st_info, sym->st_other,
sym->st_shndx, name);
old_to_new_syms[i] = sym_index;
}
/* third pass to patch relocation entries */
for(i = 1; i < ehdr.e_shnum; i++) {
s = sm_table[i].s;
if (!s)
continue;
sh = &shdr[i];
offset = sm_table[i].offset;
switch(s->sh_type) {
case SHT_RELX:
/* take relocation offset information */
offseti = sm_table[sh->sh_info].offset;
for_each_elem(s, (offset / sizeof(*rel)), rel, ElfW_Rel) {
int type;
unsigned sym_index;
/* convert symbol index */
type = ELFW(R_TYPE)(rel->r_info);
sym_index = ELFW(R_SYM)(rel->r_info);
/* NOTE: only one symtab assumed */
if (sym_index >= nb_syms)
goto invalid_reloc;
sym_index = old_to_new_syms[sym_index];
/* ignore link_once in rel section. */
if (!sym_index && !sm->link_once
#ifdef TCC_TARGET_ARM
&& type != R_ARM_V4BX
#endif
) {
invalid_reloc:
tcc_error_noabort("Invalid relocation entry [%2d] '%s' @ %.8x",
i, strsec + sh->sh_name, rel->r_offset);
goto fail;
}
rel->r_info = ELFW(R_INFO)(sym_index, type);
/* offset the relocation offset */
rel->r_offset += offseti;
#ifdef TCC_TARGET_ARM
/* Jumps and branches from a Thumb code to a PLT entry need
special handling since PLT entries are ARM code.
Unconditional bl instructions referencing PLT entries are
handled by converting these instructions into blx
instructions. Other case of instructions referencing a PLT
entry require to add a Thumb stub before the PLT entry to
switch to ARM mode. We set bit plt_thumb_stub of the
attribute of a symbol to indicate such a case. */
if (type == R_ARM_THM_JUMP24)
get_sym_attr(s1, sym_index, 1)->plt_thumb_stub = 1;
#endif
}
break;
default:
break;
}
}
ret = 0;
the_end:
tcc_free(symtab);
tcc_free(strtab);
tcc_free(old_to_new_syms);
tcc_free(sm_table);
tcc_free(strsec);
tcc_free(shdr);
return ret;
}
typedef struct ArchiveHeader {
char ar_name[16]; /* name of this member */
char ar_date[12]; /* file mtime */
char ar_uid[6]; /* owner uid; printed as decimal */
char ar_gid[6]; /* owner gid; printed as decimal */
char ar_mode[8]; /* file mode, printed as octal */
char ar_size[10]; /* file size, printed as decimal */
char ar_fmag[2]; /* should contain ARFMAG */
} ArchiveHeader;
static int get_be32(const uint8_t *b)
{
return b[3] | (b[2] << 8) | (b[1] << 16) | (b[0] << 24);
}
static long get_be64(const uint8_t *b)
{
long long ret = get_be32(b);
ret = (ret << 32) | (unsigned)get_be32(b+4);
return (long)ret;
}
/* load only the objects which resolve undefined symbols */
static int tcc_load_alacarte(TCCState *s1, int fd, int size, int entrysize)
{
long i, bound, nsyms, sym_index, off, ret;
uint8_t *data;
const char *ar_names, *p;
const uint8_t *ar_index;
ElfW(Sym) *sym;
data = tcc_malloc(size);
if (read(fd, data, size) != size)
goto fail;
nsyms = entrysize == 4 ? get_be32(data) : get_be64(data);
ar_index = data + entrysize;
ar_names = (char *) ar_index + nsyms * entrysize;
do {
bound = 0;
for(p = ar_names, i = 0; i < nsyms; i++, p += strlen(p)+1) {
sym_index = find_elf_sym(symtab_section, p);
if(sym_index) {
sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
if(sym->st_shndx == SHN_UNDEF) {
off = (entrysize == 4
? get_be32(ar_index + i * 4)
: get_be64(ar_index + i * 8))
+ sizeof(ArchiveHeader);
++bound;
if(tcc_load_object_file(s1, fd, off) < 0) {
fail:
ret = -1;
goto the_end;
}
}
}
}
} while(bound);
ret = 0;
the_end:
tcc_free(data);
return ret;
}
/* load a '.a' file */
ST_FUNC int tcc_load_archive(TCCState *s1, int fd)
{
ArchiveHeader hdr;
char ar_size[11];
char ar_name[17];
char magic[8];
int size, len, i;
unsigned long file_offset;
/* skip magic which was already checked */
read(fd, magic, sizeof(magic));
for(;;) {
len = read(fd, &hdr, sizeof(hdr));
if (len == 0)
break;
if (len != sizeof(hdr)) {
tcc_error_noabort("invalid archive");
return -1;
}
memcpy(ar_size, hdr.ar_size, sizeof(hdr.ar_size));
ar_size[sizeof(hdr.ar_size)] = '\0';
size = strtol(ar_size, NULL, 0);
memcpy(ar_name, hdr.ar_name, sizeof(hdr.ar_name));
for(i = sizeof(hdr.ar_name) - 1; i >= 0; i--) {
if (ar_name[i] != ' ')
break;
}
ar_name[i + 1] = '\0';
file_offset = lseek(fd, 0, SEEK_CUR);
/* align to even */
size = (size + 1) & ~1;
if (!strcmp(ar_name, "/")) {
/* coff symbol table : we handle it */
if(s1->alacarte_link)
return tcc_load_alacarte(s1, fd, size, 4);
} else if (!strcmp(ar_name, "/SYM64/")) {
if(s1->alacarte_link)
return tcc_load_alacarte(s1, fd, size, 8);
} else {
ElfW(Ehdr) ehdr;
if (tcc_object_type(fd, &ehdr) == AFF_BINTYPE_REL) {
if (tcc_load_object_file(s1, fd, file_offset) < 0)
return -1;
}
}
lseek(fd, file_offset + size, SEEK_SET);
}
return 0;
}
#ifndef TCC_TARGET_PE
/* load a DLL and all referenced DLLs. 'level = 0' means that the DLL
is referenced by the user (so it should be added as DT_NEEDED in
the generated ELF file) */
ST_FUNC int tcc_load_dll(TCCState *s1, int fd, const char *filename, int level)
{
ElfW(Ehdr) ehdr;
ElfW(Shdr) *shdr, *sh, *sh1;
int i, j, nb_syms, nb_dts, sym_bind, ret;
ElfW(Sym) *sym, *dynsym;
ElfW(Dyn) *dt, *dynamic;
unsigned char *dynstr;
const char *name, *soname;
DLLReference *dllref;
read(fd, &ehdr, sizeof(ehdr));
/* test CPU specific stuff */
if (ehdr.e_ident[5] != ELFDATA2LSB ||
ehdr.e_machine != EM_TCC_TARGET) {
tcc_error_noabort("bad architecture");
return -1;
}
/* read sections */
shdr = load_data(fd, ehdr.e_shoff, sizeof(ElfW(Shdr)) * ehdr.e_shnum);
/* load dynamic section and dynamic symbols */
nb_syms = 0;
nb_dts = 0;
dynamic = NULL;
dynsym = NULL; /* avoid warning */
dynstr = NULL; /* avoid warning */
for(i = 0, sh = shdr; i < ehdr.e_shnum; i++, sh++) {
switch(sh->sh_type) {
case SHT_DYNAMIC:
nb_dts = sh->sh_size / sizeof(ElfW(Dyn));
dynamic = load_data(fd, sh->sh_offset, sh->sh_size);
break;
case SHT_DYNSYM:
nb_syms = sh->sh_size / sizeof(ElfW(Sym));
dynsym = load_data(fd, sh->sh_offset, sh->sh_size);
sh1 = &shdr[sh->sh_link];
dynstr = load_data(fd, sh1->sh_offset, sh1->sh_size);
break;
default:
break;
}
}
/* compute the real library name */
soname = tcc_basename(filename);
for(i = 0, dt = dynamic; i < nb_dts; i++, dt++) {
if (dt->d_tag == DT_SONAME) {
soname = (char *) dynstr + dt->d_un.d_val;
}
}
/* if the dll is already loaded, do not load it */
for(i = 0; i < s1->nb_loaded_dlls; i++) {
dllref = s1->loaded_dlls[i];
if (!strcmp(soname, dllref->name)) {
/* but update level if needed */
if (level < dllref->level)
dllref->level = level;
ret = 0;
goto the_end;
}
}
/* add the dll and its level */
dllref = tcc_mallocz(sizeof(DLLReference) + strlen(soname));
dllref->level = level;
strcpy(dllref->name, soname);
dynarray_add(&s1->loaded_dlls, &s1->nb_loaded_dlls, dllref);
/* add dynamic symbols in dynsym_section */
for(i = 1, sym = dynsym + 1; i < nb_syms; i++, sym++) {
sym_bind = ELFW(ST_BIND)(sym->st_info);
if (sym_bind == STB_LOCAL)
continue;
name = (char *) dynstr + sym->st_name;
set_elf_sym(s1->dynsymtab_section, sym->st_value, sym->st_size,
sym->st_info, sym->st_other, sym->st_shndx, name);
}
/* load all referenced DLLs */
for(i = 0, dt = dynamic; i < nb_dts; i++, dt++) {
switch(dt->d_tag) {
case DT_NEEDED:
name = (char *) dynstr + dt->d_un.d_val;
for(j = 0; j < s1->nb_loaded_dlls; j++) {
dllref = s1->loaded_dlls[j];
if (!strcmp(name, dllref->name))
goto already_loaded;
}
if (tcc_add_dll(s1, name, AFF_REFERENCED_DLL) < 0) {
tcc_error_noabort("referenced dll '%s' not found", name);
ret = -1;
goto the_end;
}
already_loaded:
break;
}
}
ret = 0;
the_end:
tcc_free(dynstr);
tcc_free(dynsym);
tcc_free(dynamic);
tcc_free(shdr);
return ret;
}
#define LD_TOK_NAME 256
#define LD_TOK_EOF (-1)
/* return next ld script token */
static int ld_next(TCCState *s1, char *name, int name_size)
{
int c;
char *q;
redo:
switch(ch) {
case ' ':
case '\t':
case '\f':
case '\v':
case '\r':
case '\n':
inp();
goto redo;
case '/':
minp();
if (ch == '*') {
file->buf_ptr = parse_comment(file->buf_ptr);
ch = file->buf_ptr[0];
goto redo;
} else {
q = name;
*q++ = '/';
goto parse_name;
}
break;
case '\\':
ch = handle_eob();
if (ch != '\\')
goto redo;
/* fall through */
/* case 'a' ... 'z': */
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
case 'g':
case 'h':
case 'i':
case 'j':
case 'k':
case 'l':
case 'm':
case 'n':
case 'o':
case 'p':
case 'q':
case 'r':
case 's':
case 't':
case 'u':
case 'v':
case 'w':
case 'x':
case 'y':
case 'z':
/* case 'A' ... 'z': */
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
case 'G':
case 'H':
case 'I':
case 'J':
case 'K':
case 'L':
case 'M':
case 'N':
case 'O':
case 'P':
case 'Q':
case 'R':
case 'S':
case 'T':
case 'U':
case 'V':
case 'W':
case 'X':
case 'Y':
case 'Z':
case '_':
case '.':
case '$':
case '~':
q = name;
parse_name:
for(;;) {
if (!((ch >= 'a' && ch <= 'z') ||
(ch >= 'A' && ch <= 'Z') ||
(ch >= '0' && ch <= '9') ||
strchr("/.-_+=$:\\,~", ch)))
break;
if ((q - name) < name_size - 1) {
*q++ = ch;
}
minp();
}
*q = '\0';
c = LD_TOK_NAME;
break;
case CH_EOF:
c = LD_TOK_EOF;
break;
default:
c = ch;
inp();
break;
}
return c;
}
static int ld_add_file(TCCState *s1, const char filename[])
{
if (filename[0] == '/') {
if (CONFIG_SYSROOT[0] == '\0'
&& tcc_add_file_internal(s1, filename, AFF_TYPE_BIN) == 0)
return 0;
filename = tcc_basename(filename);
}
return tcc_add_dll(s1, filename, 0);
}
static inline int new_undef_syms(void)
{
int ret = 0;
ret = new_undef_sym;
new_undef_sym = 0;
return ret;
}
static int ld_add_file_list(TCCState *s1, const char *cmd, int as_needed)
{
char filename[1024], libname[1024];
int t, group, nblibs = 0, ret = 0;
char **libs = NULL;
group = !strcmp(cmd, "GROUP");
if (!as_needed)
new_undef_syms();
t = ld_next(s1, filename, sizeof(filename));
if (t != '(')
expect("(");
t = ld_next(s1, filename, sizeof(filename));
for(;;) {
libname[0] = '\0';
if (t == LD_TOK_EOF) {
tcc_error_noabort("unexpected end of file");
ret = -1;
goto lib_parse_error;
} else if (t == ')') {
break;
} else if (t == '-') {
t = ld_next(s1, filename, sizeof(filename));
if ((t != LD_TOK_NAME) || (filename[0] != 'l')) {
tcc_error_noabort("library name expected");
ret = -1;
goto lib_parse_error;
}
pstrcpy(libname, sizeof libname, &filename[1]);
if (s1->static_link) {
snprintf(filename, sizeof filename, "lib%s.a", libname);
} else {
snprintf(filename, sizeof filename, "lib%s.so", libname);
}
} else if (t != LD_TOK_NAME) {
tcc_error_noabort("filename expected");
ret = -1;
goto lib_parse_error;
}
if (!strcmp(filename, "AS_NEEDED")) {
ret = ld_add_file_list(s1, cmd, 1);
if (ret)
goto lib_parse_error;
} else {
/* TODO: Implement AS_NEEDED support. Ignore it for now */
if (!as_needed) {
ret = ld_add_file(s1, filename);
if (ret)
goto lib_parse_error;
if (group) {
/* Add the filename *and* the libname to avoid future conversions */
dynarray_add(&libs, &nblibs, tcc_strdup(filename));
if (libname[0] != '\0')
dynarray_add(&libs, &nblibs, tcc_strdup(libname));
}
}
}
t = ld_next(s1, filename, sizeof(filename));
if (t == ',') {
t = ld_next(s1, filename, sizeof(filename));
}
}
if (group && !as_needed) {
while (new_undef_syms()) {
int i;
for (i = 0; i < nblibs; i ++)
ld_add_file(s1, libs[i]);
}
}
lib_parse_error:
dynarray_reset(&libs, &nblibs);
return ret;
}
/* interpret a subset of GNU ldscripts to handle the dummy libc.so
files */
ST_FUNC int tcc_load_ldscript(TCCState *s1)
{
char cmd[64];
char filename[1024];
int t, ret;
ch = handle_eob();
for(;;) {
t = ld_next(s1, cmd, sizeof(cmd));
if (t == LD_TOK_EOF)
return 0;
else if (t != LD_TOK_NAME)
return -1;
if (!strcmp(cmd, "INPUT") ||
!strcmp(cmd, "GROUP")) {
ret = ld_add_file_list(s1, cmd, 0);
if (ret)
return ret;
} else if (!strcmp(cmd, "OUTPUT_FORMAT") ||
!strcmp(cmd, "TARGET")) {
/* ignore some commands */
t = ld_next(s1, cmd, sizeof(cmd));
if (t != '(')
expect("(");
for(;;) {
t = ld_next(s1, filename, sizeof(filename));
if (t == LD_TOK_EOF) {
tcc_error_noabort("unexpected end of file");
return -1;
} else if (t == ')') {
break;
}
}
} else {
return -1;
}
}
return 0;
}
#endif /* !TCC_TARGET_PE */
/*
* TCC - Tiny C Compiler - Support for -run switch
*
* Copyright (c) 2001-2004 Fabrice Bellard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _TCC_H
#include "tcc.h"
#endif
/* only native compiler supports -run */
#ifdef TCC_IS_NATIVE
#ifndef _WIN32
# include <sys/mman.h>
#endif
#ifdef CONFIG_TCC_BACKTRACE
# ifndef _WIN32
# include <signal.h>
# ifndef __OpenBSD__
# include <sys/ucontext.h>
# endif
# else
# define ucontext_t CONTEXT
# endif
ST_DATA int rt_num_callers = 6;
ST_DATA const char **rt_bound_error_msg;
ST_DATA void *rt_prog_main;
static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level);
static void rt_error(ucontext_t *uc, const char *fmt, ...);
static void set_exception_handler(void);
#endif
static void set_pages_executable(void *ptr, unsigned long length);
static int tcc_relocate_ex(TCCState *s1, void *ptr);
#ifdef _WIN64
static void *win64_add_function_table(TCCState *s1);
static void win64_del_function_table(void *);
#endif
// #define HAVE_SELINUX
/* ------------------------------------------------------------- */
/* Do all relocations (needed before using tcc_get_symbol())
Returns -1 on error. */
LIBTCCAPI int tcc_relocate(TCCState *s1, void *ptr)
{
int size;
if (TCC_RELOCATE_AUTO != ptr)
return tcc_relocate_ex(s1, ptr);
size = tcc_relocate_ex(s1, NULL);
if (size < 0)
return -1;
#ifdef HAVE_SELINUX
/* Use mmap instead of malloc for Selinux. */
ptr = mmap (NULL, size, PROT_READ|PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (ptr == MAP_FAILED)
tcc_error("tccrun: could not map memory");
dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, (void*)(addr_t)size);
#else
ptr = tcc_malloc(size);
#endif
tcc_relocate_ex(s1, ptr); /* no more errors expected */
dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, ptr);
return 0;
}
ST_FUNC void tcc_run_free(TCCState *s1)
{
int i;
for (i = 0; i < s1->nb_runtime_mem; ++i) {
#ifdef HAVE_SELINUX
unsigned size = (unsigned)(addr_t)s1->runtime_mem[i++];
munmap(s1->runtime_mem[i], size);
#else
#ifdef _WIN64
win64_del_function_table(*(void**)s1->runtime_mem[i]);
#endif
tcc_free(s1->runtime_mem[i]);
#endif
}
tcc_free(s1->runtime_mem);
}
/* launch the compiled program with the given arguments */
LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv)
{
int (*prog_main)(int, char **);
s1->runtime_main = "main";
if (tcc_relocate(s1, TCC_RELOCATE_AUTO) < 0)
return -1;
prog_main = tcc_get_symbol_err(s1, s1->runtime_main);
#ifdef CONFIG_TCC_BACKTRACE
if (s1->do_debug) {
set_exception_handler();
rt_prog_main = prog_main;
}
#endif
errno = 0; /* clean errno value */
#ifdef CONFIG_TCC_BCHECK
if (s1->do_bounds_check) {
void (*bound_init)(void);
void (*bound_exit)(void);
void (*bound_new_region)(void *p, addr_t size);
int (*bound_delete_region)(void *p);
int i, ret;
/* set error function */
rt_bound_error_msg = tcc_get_symbol_err(s1, "__bound_error_msg");
/* XXX: use .init section so that it also work in binary ? */
bound_init = tcc_get_symbol_err(s1, "__bound_init");
bound_exit = tcc_get_symbol_err(s1, "__bound_exit");
bound_new_region = tcc_get_symbol_err(s1, "__bound_new_region");
bound_delete_region = tcc_get_symbol_err(s1, "__bound_delete_region");
bound_init();
/* mark argv area as valid */
bound_new_region(argv, argc*sizeof(argv[0]));
for (i=0; i<argc; ++i)
bound_new_region(argv[i], strlen(argv[i]) + 1);
ret = (*prog_main)(argc, argv);
/* unmark argv area */
for (i=0; i<argc; ++i)
bound_delete_region(argv[i]);
bound_delete_region(argv);
bound_exit();
return ret;
}
#endif
return (*prog_main)(argc, argv);
}
#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
#define RUN_SECTION_ALIGNMENT 63
#else
#define RUN_SECTION_ALIGNMENT 15
#endif
/* relocate code. Return -1 on error, required size if ptr is NULL,
otherwise copy code into buffer passed by the caller */
static int tcc_relocate_ex(TCCState *s1, void *ptr)
{
Section *s;
unsigned offset, length, fill, i, k;
addr_t mem;
if (NULL == ptr) {
s1->nb_errors = 0;
#ifdef TCC_TARGET_PE
pe_output_file(s1, NULL);
#else
tcc_add_runtime(s1);
relocate_common_syms();
tcc_add_linker_symbols(s1);
build_got_entries(s1);
#endif
if (s1->nb_errors)
return -1;
}
offset = 0, mem = (addr_t)ptr;
fill = -mem & RUN_SECTION_ALIGNMENT;
#ifdef _WIN64
offset += sizeof (void*);
#endif
for (k = 0; k < 2; ++k) {
for(i = 1; i < s1->nb_sections; i++) {
s = s1->sections[i];
if (0 == (s->sh_flags & SHF_ALLOC))
continue;
if (k != !(s->sh_flags & SHF_EXECINSTR))
continue;
offset += fill;
s->sh_addr = mem ? mem + offset : 0;
#if 0
if (mem)
printf("%-16s +%02lx %p %04x\n",
s->name, fill, (void*)s->sh_addr, (unsigned)s->data_offset);
#endif
offset += s->data_offset;
fill = -(mem + offset) & 15;
}
#if RUN_SECTION_ALIGNMENT > 15
/* To avoid that x86 processors would reload cached instructions each time
when data is written in the near, we need to make sure that code and data
do not share the same 64 byte unit */
fill = -(mem + offset) & RUN_SECTION_ALIGNMENT;
#endif
}
/* relocate symbols */
relocate_syms(s1, s1->symtab, 1);
if (s1->nb_errors)
return -1;
if (0 == mem)
return offset + RUN_SECTION_ALIGNMENT;
/* relocate each section */
for(i = 1; i < s1->nb_sections; i++) {
s = s1->sections[i];
if (s->reloc)
relocate_section(s1, s);
}
relocate_plt(s1);
#ifdef _WIN64
*(void**)ptr = win64_add_function_table(s1);
#endif
for(i = 1; i < s1->nb_sections; i++) {
s = s1->sections[i];
if (0 == (s->sh_flags & SHF_ALLOC))
continue;
length = s->data_offset;
ptr = (void*)s->sh_addr;
if (NULL == s->data || s->sh_type == SHT_NOBITS)
memset(ptr, 0, length);
else
memcpy(ptr, s->data, length);
/* mark executable sections as executable in memory */
if (s->sh_flags & SHF_EXECINSTR)
set_pages_executable(ptr, length);
}
return 0;
}
/* ------------------------------------------------------------- */
/* allow to run code in memory */
static void set_pages_executable(void *ptr, unsigned long length)
{
#ifdef _WIN32
unsigned long old_protect;
VirtualProtect(ptr, length, PAGE_EXECUTE_READWRITE, &old_protect);
#else
void __clear_cache(void *beginning, void *end);
addr_t start, end;
#ifndef PAGESIZE
# define PAGESIZE 4096
#endif
start = (addr_t)ptr & ~(PAGESIZE - 1);
end = (addr_t)ptr + length;
end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1);
if (mprotect((void *)start, end - start, PROT_READ | PROT_WRITE | PROT_EXEC))
tcc_error("mprotect failed: did you mean to configure --with-selinux?");
# if defined TCC_TARGET_ARM || defined TCC_TARGET_ARM64
#if !BOOTSTRAP
__clear_cache(ptr, (char *)ptr + length);
#endif
# endif
#endif
}
#ifdef _WIN64
static void *win64_add_function_table(TCCState *s1)
{
void *p = NULL;
if (s1->uw_pdata) {
p = (void*)s1->uw_pdata->sh_addr;
RtlAddFunctionTable(
(RUNTIME_FUNCTION*)p,
s1->uw_pdata->data_offset / sizeof (RUNTIME_FUNCTION),
text_section->sh_addr
);
s1->uw_pdata = NULL;
}
return p;;
}
static void win64_del_function_table(void *p)
{
if (p) {
RtlDeleteFunctionTable((RUNTIME_FUNCTION*)p);
}
}
#endif
/* ------------------------------------------------------------- */
#ifdef CONFIG_TCC_BACKTRACE
ST_FUNC void tcc_set_num_callers(int n)
{
rt_num_callers = n;
}
/* print the position in the source file of PC value 'pc' by reading
the stabs debug information */
static addr_t rt_printline(addr_t wanted_pc, const char *msg)
{
char func_name[128], last_func_name[128];
addr_t func_addr, last_pc, pc;
const char *incl_files[INCLUDE_STACK_SIZE];
int incl_index, len, last_line_num, i;
const char *str, *p;
Stab_Sym *stab_sym = NULL, *stab_sym_end, *sym;
int stab_len = 0;
char *stab_str = NULL;
if (stab_section) {
stab_len = stab_section->data_offset;
stab_sym = (Stab_Sym *)stab_section->data;
stab_str = (char *) stabstr_section->data;
}
func_name[0] = '\0';
func_addr = 0;
incl_index = 0;
last_func_name[0] = '\0';
last_pc = (addr_t)-1;
last_line_num = 1;
if (!stab_sym)
goto no_stabs;
stab_sym_end = (Stab_Sym*)((char*)stab_sym + stab_len);
for (sym = stab_sym + 1; sym < stab_sym_end; ++sym) {
switch(sym->n_type) {
/* function start or end */
case N_FUN:
if (sym->n_strx == 0) {
/* we test if between last line and end of function */
pc = sym->n_value + func_addr;
if (wanted_pc >= last_pc && wanted_pc < pc)
goto found;
func_name[0] = '\0';
func_addr = 0;
} else {
str = stab_str + sym->n_strx;
p = strchr(str, ':');
if (!p) {
pstrcpy(func_name, sizeof(func_name), str);
} else {
len = p - str;
if (len > sizeof(func_name) - 1)
len = sizeof(func_name) - 1;
memcpy(func_name, str, len);
func_name[len] = '\0';
}
func_addr = sym->n_value;
}
break;
/* line number info */
case N_SLINE:
pc = sym->n_value + func_addr;
if (wanted_pc >= last_pc && wanted_pc < pc)
goto found;
last_pc = pc;
last_line_num = sym->n_desc;
/* XXX: slow! */
strcpy(last_func_name, func_name);
break;
/* include files */
case N_BINCL:
str = stab_str + sym->n_strx;
add_incl:
if (incl_index < INCLUDE_STACK_SIZE) {
incl_files[incl_index++] = str;
}
break;
case N_EINCL:
if (incl_index > 1)
incl_index--;
break;
case N_SO:
if (sym->n_strx == 0) {
incl_index = 0; /* end of translation unit */
} else {
str = stab_str + sym->n_strx;
/* do not add path */
len = strlen(str);
if (len > 0 && str[len - 1] != '/')
goto add_incl;
}
break;
}
}
no_stabs:
/* second pass: we try symtab symbols (no line number info) */
incl_index = 0;
if (symtab_section)
{
ElfW(Sym) *sym, *sym_end;
int type;
sym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset);
for(sym = (ElfW(Sym) *)symtab_section->data + 1;
sym < sym_end;
sym++) {
type = ELFW(ST_TYPE)(sym->st_info);
if (type == STT_FUNC || type == STT_GNU_IFUNC) {
if (wanted_pc >= sym->st_value &&
wanted_pc < sym->st_value + sym->st_size) {
pstrcpy(last_func_name, sizeof(last_func_name),
(char *) strtab_section->data + sym->st_name);
func_addr = sym->st_value;
goto found;
}
}
}
}
/* did not find any info: */
fprintf(stderr, "%s %p ???\n", msg, (void*)wanted_pc);
fflush(stderr);
return 0;
found:
i = incl_index;
if (i > 0)
fprintf(stderr, "%s:%d: ", incl_files[--i], last_line_num);
fprintf(stderr, "%s %p", msg, (void*)wanted_pc);
if (last_func_name[0] != '\0')
fprintf(stderr, " %s()", last_func_name);
if (--i >= 0) {
fprintf(stderr, " (included from ");
for (;;) {
fprintf(stderr, "%s", incl_files[i]);
if (--i < 0)
break;
fprintf(stderr, ", ");
}
fprintf(stderr, ")");
}
fprintf(stderr, "\n");
fflush(stderr);
return func_addr;
}
/* emit a run time error at position 'pc' */
static void rt_error(ucontext_t *uc, const char *fmt, ...)
{
va_list ap;
addr_t pc;
int i;
fprintf(stderr, "Runtime error: ");
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fprintf(stderr, "\n");
for(i=0;i<rt_num_callers;i++) {
if (rt_get_caller_pc(&pc, uc, i) < 0)
break;
pc = rt_printline(pc, i ? "by" : "at");
if (pc == (addr_t)rt_prog_main && pc)
break;
}
}
/* ------------------------------------------------------------- */
#ifndef _WIN32
/* signal handler for fatal errors */
static void sig_error(int signum, siginfo_t *siginf, void *puc)
{
ucontext_t *uc = puc;
switch(signum) {
case SIGFPE:
switch(siginf->si_code) {
case FPE_INTDIV:
case FPE_FLTDIV:
rt_error(uc, "division by zero");
break;
default:
rt_error(uc, "floating point exception");
break;
}
break;
case SIGBUS:
case SIGSEGV:
if (rt_bound_error_msg && *rt_bound_error_msg)
rt_error(uc, *rt_bound_error_msg);
else
rt_error(uc, "dereferencing invalid pointer");
break;
case SIGILL:
rt_error(uc, "illegal instruction");
break;
case SIGABRT:
rt_error(uc, "abort() called");
break;
default:
rt_error(uc, "caught signal %d", signum);
break;
}
exit(255);
}
#ifndef SA_SIGINFO
# define SA_SIGINFO 0x00000004u
#endif
/* Generate a stack backtrace when a CPU exception occurs. */
static void set_exception_handler(void)
{
struct sigaction sigact;
/* install TCC signal handlers to print debug info on fatal
runtime errors */
sigact.sa_flags = SA_SIGINFO | SA_RESETHAND;
sigact.sa_sigaction = sig_error;
sigemptyset(&sigact.sa_mask);
sigaction(SIGFPE, &sigact, NULL);
sigaction(SIGILL, &sigact, NULL);
sigaction(SIGSEGV, &sigact, NULL);
sigaction(SIGBUS, &sigact, NULL);
sigaction(SIGABRT, &sigact, NULL);
}
/* ------------------------------------------------------------- */
#ifdef __i386__
/* fix for glibc 2.1 */
#ifndef REG_EIP
#define REG_EIP EIP
#define REG_EBP EBP
#endif
/* return the PC at frame level 'level'. Return negative if not found */
static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
{
addr_t fp;
int i;
if (level == 0) {
#if defined(__APPLE__)
*paddr = uc->uc_mcontext->__ss.__eip;
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
*paddr = uc->uc_mcontext.mc_eip;
#elif defined(__dietlibc__)
*paddr = uc->uc_mcontext.eip;
#elif defined(__NetBSD__)
*paddr = uc->uc_mcontext.__gregs[_REG_EIP];
#elif defined(__OpenBSD__)
*paddr = uc->sc_eip;
#else
*paddr = uc->uc_mcontext.gregs[REG_EIP];
#endif
return 0;
} else {
#if defined(__APPLE__)
fp = uc->uc_mcontext->__ss.__ebp;
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
fp = uc->uc_mcontext.mc_ebp;
#elif defined(__dietlibc__)
fp = uc->uc_mcontext.ebp;
#elif defined(__NetBSD__)
fp = uc->uc_mcontext.__gregs[_REG_EBP];
#elif defined(__OpenBSD__)
*paddr = uc->sc_ebp;
#else
fp = uc->uc_mcontext.gregs[REG_EBP];
#endif
for(i=1;i<level;i++) {
/* XXX: check address validity with program info */
if (fp <= 0x1000 || fp >= 0xc0000000)
return -1;
fp = ((addr_t *)fp)[0];
}
*paddr = ((addr_t *)fp)[1];
return 0;
}
}
/* ------------------------------------------------------------- */
#elif defined(__x86_64__)
/* return the PC at frame level 'level'. Return negative if not found */
static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
{
addr_t fp;
int i;
if (level == 0) {
/* XXX: only support linux */
#if defined(__APPLE__)
*paddr = uc->uc_mcontext->__ss.__rip;
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
*paddr = uc->uc_mcontext.mc_rip;
#elif defined(__NetBSD__)
*paddr = uc->uc_mcontext.__gregs[_REG_RIP];
#else
*paddr = uc->uc_mcontext.gregs[REG_RIP];
#endif
return 0;
} else {
#if defined(__APPLE__)
fp = uc->uc_mcontext->__ss.__rbp;
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
fp = uc->uc_mcontext.mc_rbp;
#elif defined(__NetBSD__)
fp = uc->uc_mcontext.__gregs[_REG_RBP];
#else
fp = uc->uc_mcontext.gregs[REG_RBP];
#endif
for(i=1;i<level;i++) {
/* XXX: check address validity with program info */
if (fp <= 0x1000)
return -1;
fp = ((addr_t *)fp)[0];
}
*paddr = ((addr_t *)fp)[1];
return 0;
}
}
/* ------------------------------------------------------------- */
#elif defined(__arm__)
/* return the PC at frame level 'level'. Return negative if not found */
static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
{
addr_t fp, sp;
int i;
if (level == 0) {
/* XXX: only supports linux */
#if defined(__linux__)
*paddr = uc->uc_mcontext.arm_pc;
#else
return -1;
#endif
return 0;
} else {
#if defined(__linux__)
fp = uc->uc_mcontext.arm_fp;
sp = uc->uc_mcontext.arm_sp;
if (sp < 0x1000)
sp = 0x1000;
#else
return -1;
#endif
/* XXX: specific to tinycc stack frames */
if (fp < sp + 12 || fp & 3)
return -1;
for(i = 1; i < level; i++) {
sp = ((addr_t *)fp)[-2];
if (sp < fp || sp - fp > 16 || sp & 3)
return -1;
fp = ((addr_t *)fp)[-3];
if (fp <= sp || fp - sp < 12 || fp & 3)
return -1;
}
/* XXX: check address validity with program info */
*paddr = ((addr_t *)fp)[-1];
return 0;
}
}
/* ------------------------------------------------------------- */
#elif defined(__aarch64__)
static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
{
if (level < 0)
return -1;
else if (level == 0) {
*paddr = uc->uc_mcontext.pc;
return 0;
}
else {
addr_t *fp = (addr_t *)uc->uc_mcontext.regs[29];
int i;
for (i = 1; i < level; i++)
fp = (addr_t *)fp[0];
*paddr = fp[1];
return 0;
}
}
/* ------------------------------------------------------------- */
#else
#warning add arch specific rt_get_caller_pc()
static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
{
return -1;
}
#endif /* !__i386__ */
/* ------------------------------------------------------------- */
#else /* WIN32 */
static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info)
{
EXCEPTION_RECORD *er = ex_info->ExceptionRecord;
CONTEXT *uc = ex_info->ContextRecord;
switch (er->ExceptionCode) {
case EXCEPTION_ACCESS_VIOLATION:
if (rt_bound_error_msg && *rt_bound_error_msg)
rt_error(uc, *rt_bound_error_msg);
else
rt_error(uc, "access violation");
break;
case EXCEPTION_STACK_OVERFLOW:
rt_error(uc, "stack overflow");
break;
case EXCEPTION_INT_DIVIDE_BY_ZERO:
rt_error(uc, "division by zero");
break;
default:
rt_error(uc, "exception caught");
break;
}
return EXCEPTION_EXECUTE_HANDLER;
}
/* Generate a stack backtrace when a CPU exception occurs. */
static void set_exception_handler(void)
{
SetUnhandledExceptionFilter(cpu_exception_handler);
}
/* return the PC at frame level 'level'. Return non zero if not found */
static int rt_get_caller_pc(addr_t *paddr, CONTEXT *uc, int level)
{
addr_t fp, pc;
int i;
#ifdef _WIN64
pc = uc->Rip;
fp = uc->Rbp;
#else
pc = uc->Eip;
fp = uc->Ebp;
#endif
if (level > 0) {
for(i=1;i<level;i++) {
/* XXX: check address validity with program info */
if (fp <= 0x1000 || fp >= 0xc0000000)
return -1;
fp = ((addr_t*)fp)[0];
}
pc = ((addr_t*)fp)[1];
}
*paddr = pc;
return 0;
}
#endif /* _WIN32 */
#endif /* CONFIG_TCC_BACKTRACE */
/* ------------------------------------------------------------- */
#ifdef CONFIG_TCC_STATIC
/* dummy function for profiling */
ST_FUNC void *dlopen(const char *filename, int flag)
{
return NULL;
}
ST_FUNC void dlclose(void *p)
{
}
ST_FUNC const char *dlerror(void)
{
return "error";
}
typedef struct TCCSyms {
char *str;
void *ptr;
} TCCSyms;
/* add the symbol you want here if no dynamic linking is done */
static TCCSyms tcc_syms[] = {
#if !defined(CONFIG_TCCBOOT)
#define TCCSYM(a) { #a, &a, },
TCCSYM(printf)
TCCSYM(fprintf)
TCCSYM(fopen)
TCCSYM(fclose)
#undef TCCSYM
#endif
{ NULL, NULL },
};
ST_FUNC void *dlsym(void *handle, const char *symbol)
{
TCCSyms *p;
p = tcc_syms;
while (p->str != NULL) {
if (!strcmp(p->str, symbol))
return p->ptr;
p++;
}
return NULL;
}
#endif /* CONFIG_TCC_STATIC */
#endif /* TCC_IS_NATIVE */
/* ------------------------------------------------------------- */
/*
* X86 code generator for TCC
*
* Copyright (c) 2001-2004 Fabrice Bellard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef TARGET_DEFS_ONLY
/* number of available registers */
#define NB_REGS 5
#define NB_ASM_REGS 8
#define CONFIG_TCC_ASM
/* a register can belong to several classes. The classes must be
sorted from more general to more precise (see gv2() code which does
assumptions on it). */
#define RC_INT 0x0001 /* generic integer register */
#define RC_FLOAT 0x0002 /* generic float register */
#define RC_EAX 0x0004
#define RC_ST0 0x0008
#define RC_ECX 0x0010
#define RC_EDX 0x0020
#define RC_EBX 0x0040
#define RC_IRET RC_EAX /* function return: integer register */
#define RC_LRET RC_EDX /* function return: second integer register */
#define RC_FRET RC_ST0 /* function return: float register */
/* pretty names for the registers */
enum {
TREG_EAX = 0,
TREG_ECX,
TREG_EDX,
TREG_EBX,
TREG_ST0,
TREG_ESP = 4
};
/* return registers for function */
#define REG_IRET TREG_EAX /* single word int return register */
#define REG_LRET TREG_EDX /* second word return register (for long long) */
#define REG_FRET TREG_ST0 /* float return register */
/* defined if function parameters must be evaluated in reverse order */
#define INVERT_FUNC_PARAMS
/* defined if structures are passed as pointers. Otherwise structures
are directly pushed on stack. */
/* #define FUNC_STRUCT_PARAM_AS_PTR */
/* pointer size, in bytes */
#define PTR_SIZE 4
/* long double size and alignment, in bytes */
#define LDOUBLE_SIZE 12
#define LDOUBLE_ALIGN 4
/* maximum alignment (for aligned attribute support) */
#define MAX_ALIGN 8
/******************************************************/
#else /* ! TARGET_DEFS_ONLY */
/******************************************************/
#ifndef _TCC_H
#include "tcc.h"
#endif
/* define to 1/0 to [not] have EBX as 4th register */
#define USE_EBX 0
ST_DATA const int reg_classes[NB_REGS] = {
/* eax */ RC_INT | RC_EAX,
/* ecx */ RC_INT | RC_ECX,
/* edx */ RC_INT | RC_EDX,
/* ebx */ (RC_INT | RC_EBX) * USE_EBX,
/* st0 */ RC_FLOAT | RC_ST0,
};
static unsigned long func_sub_sp_offset;
static int func_ret_sub;
#ifdef CONFIG_TCC_BCHECK
static addr_t func_bound_offset;
static unsigned long func_bound_ind;
#endif
/* XXX: make it faster ? */
ST_FUNC void g(int c)
{
int ind1;
if (nocode_wanted)
return;
ind1 = ind + 1;
if (ind1 > cur_text_section->data_allocated)
section_realloc(cur_text_section, ind1);
cur_text_section->data[ind] = c;
ind = ind1;
}
ST_FUNC void o(unsigned int c)
{
while (c) {
g(c);
c = c >> 8;
}
}
ST_FUNC void gen_le16(int v)
{
g(v);
g(v >> 8);
}
ST_FUNC void gen_le32(int c)
{
g(c);
g(c >> 8);
g(c >> 16);
g(c >> 24);
}
/* output a symbol and patch all calls to it */
ST_FUNC void gsym_addr(int t, int a)
{
while (t) {
unsigned char *ptr = cur_text_section->data + t;
uint32_t n = read32le(ptr); /* next value */
write32le(ptr, a - t - 4);
t = n;
}
}
ST_FUNC void gsym(int t)
{
gsym_addr(t, ind);
}
/* instruction + 4 bytes data. Return the address of the data */
static int oad(int c, int s)
{
int t;
if (nocode_wanted)
return s;
o(c);
t = ind;
gen_le32(s);
return t;
}
/* generate jmp to a label */
#define gjmp2(instr,lbl) oad(instr,lbl)
/* output constant with relocation if 'r & VT_SYM' is true */
ST_FUNC void gen_addr32(int r, Sym *sym, long c)
{
if (r & VT_SYM)
greloc(cur_text_section, sym, ind, R_386_32);
gen_le32(c);
}
ST_FUNC void gen_addrpc32(int r, Sym *sym, long c)
{
if (r & VT_SYM)
greloc(cur_text_section, sym, ind, R_386_PC32);
gen_le32(c - 4);
}
/* generate a modrm reference. 'op_reg' contains the addtional 3
opcode bits */
static void gen_modrm(int op_reg, int r, Sym *sym, int c)
{
op_reg = op_reg << 3;
if ((r & VT_VALMASK) == VT_CONST) {
/* constant memory reference */
o(0x05 | op_reg);
gen_addr32(r, sym, c);
} else if ((r & VT_VALMASK) == VT_LOCAL) {
/* currently, we use only ebp as base */
if (c == (char)c) {
/* short reference */
o(0x45 | op_reg);
g(c);
} else {
oad(0x85 | op_reg, c);
}
} else {
g(0x00 | op_reg | (r & VT_VALMASK));
}
}
/* load 'r' from value 'sv' */
ST_FUNC void load(int r, SValue *sv)
{
int v, t, ft, fc, fr;
SValue v1;
#ifdef TCC_TARGET_PE
SValue v2;
sv = pe_getimport(sv, &v2);
#endif
fr = sv->r;
ft = sv->type.t;
fc = sv->c.i;
ft &= ~(VT_VOLATILE | VT_CONSTANT);
v = fr & VT_VALMASK;
if (fr & VT_LVAL) {
if (v == VT_LLOCAL) {
v1.type.t = VT_INT;
v1.r = VT_LOCAL | VT_LVAL;
v1.c.i = fc;
fr = r;
if (!(reg_classes[fr] & RC_INT))
fr = get_reg(RC_INT);
load(fr, &v1);
}
if ((ft & VT_BTYPE) == VT_FLOAT) {
o(0xd9); /* flds */
r = 0;
} else if ((ft & VT_BTYPE) == VT_DOUBLE) {
o(0xdd); /* fldl */
r = 0;
} else if ((ft & VT_BTYPE) == VT_LDOUBLE) {
o(0xdb); /* fldt */
r = 5;
} else if ((ft & VT_TYPE) == VT_BYTE || (ft & VT_TYPE) == VT_BOOL) {
o(0xbe0f); /* movsbl */
} else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) {
o(0xb60f); /* movzbl */
} else if ((ft & VT_TYPE) == VT_SHORT) {
o(0xbf0f); /* movswl */
} else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) {
o(0xb70f); /* movzwl */
} else {
o(0x8b); /* movl */
}
gen_modrm(r, fr, sv->sym, fc);
} else {
if (v == VT_CONST) {
o(0xb8 + r); /* mov $xx, r */
gen_addr32(fr, sv->sym, fc);
} else if (v == VT_LOCAL) {
if (fc) {
o(0x8d); /* lea xxx(%ebp), r */
gen_modrm(r, VT_LOCAL, sv->sym, fc);
} else {
o(0x89);
o(0xe8 + r); /* mov %ebp, r */
}
} else if (v == VT_CMP) {
oad(0xb8 + r, 0); /* mov $0, r */
o(0x0f); /* setxx %br */
o(fc);
o(0xc0 + r);
} else if (v == VT_JMP || v == VT_JMPI) {
t = v & 1;
oad(0xb8 + r, t); /* mov $1, r */
o(0x05eb); /* jmp after */
gsym(fc);
oad(0xb8 + r, t ^ 1); /* mov $0, r */
} else if (v != r) {
o(0x89);
o(0xc0 + r + v * 8); /* mov v, r */
}
}
}
/* store register 'r' in lvalue 'v' */
ST_FUNC void store(int r, SValue *v)
{
int fr, bt, ft, fc;
#ifdef TCC_TARGET_PE
SValue v2;
v = pe_getimport(v, &v2);
#endif
ft = v->type.t;
fc = v->c.i;
fr = v->r & VT_VALMASK;
ft &= ~(VT_VOLATILE | VT_CONSTANT);
bt = ft & VT_BTYPE;
/* XXX: incorrect if float reg to reg */
if (bt == VT_FLOAT) {
o(0xd9); /* fsts */
r = 2;
} else if (bt == VT_DOUBLE) {
o(0xdd); /* fstpl */
r = 2;
} else if (bt == VT_LDOUBLE) {
o(0xc0d9); /* fld %st(0) */
o(0xdb); /* fstpt */
r = 7;
} else {
if (bt == VT_SHORT)
o(0x66);
if (bt == VT_BYTE || bt == VT_BOOL)
o(0x88);
else
o(0x89);
}
if (fr == VT_CONST ||
fr == VT_LOCAL ||
(v->r & VT_LVAL)) {
gen_modrm(r, v->r, v->sym, fc);
} else if (fr != r) {
o(0xc0 + fr + r * 8); /* mov r, fr */
}
}
static void gadd_sp(int val)
{
if (val == (char)val) {
o(0xc483);
g(val);
} else {
oad(0xc481, val); /* add $xxx, %esp */
}
}
#if defined CONFIG_TCC_BCHECK || defined TCC_TARGET_PE
static void gen_static_call(int v)
{
Sym *sym;
sym = external_global_sym(v, &func_old_type, 0);
oad(0xe8, -4);
greloc(cur_text_section, sym, ind-4, R_386_PC32);
}
#endif
/* 'is_jmp' is '1' if it is a jump */
static void gcall_or_jmp(int is_jmp)
{
int r;
if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
int rt;
/* constant case */
if (vtop->r & VT_SYM) {
/* relocation case */
greloc(cur_text_section, vtop->sym,
ind + 1, R_386_PC32);
} else {
/* put an empty PC32 relocation */
put_elf_reloc(symtab_section, cur_text_section,
ind + 1, R_386_PC32, 0);
}
oad(0xe8 + is_jmp, vtop->c.i - 4); /* call/jmp im */
/* extend the return value to the whole register if necessary
visual studio and gcc do not always set the whole eax register
when assigning the return value of a function */
rt = vtop->type.ref->type.t;
switch (rt & VT_BTYPE) {
case VT_BYTE:
if (rt & VT_UNSIGNED) {
o(0xc0b60f); /* movzx %al, %eax */
}
else {
o(0xc0be0f); /* movsx %al, %eax */
}
break;
case VT_SHORT:
if (rt & VT_UNSIGNED) {
o(0xc0b70f); /* movzx %ax, %eax */
}
else {
o(0xc0bf0f); /* movsx %ax, %eax */
}
break;
default:
break;
}
} else {
/* otherwise, indirect call */
r = gv(RC_INT);
o(0xff); /* call/jmp *r */
o(0xd0 + r + (is_jmp << 4));
}
}
static uint8_t fastcall_regs[3] = { TREG_EAX, TREG_EDX, TREG_ECX };
static uint8_t fastcallw_regs[2] = { TREG_ECX, TREG_EDX };
/* Return the number of registers needed to return the struct, or 0 if
returning via struct pointer. */
ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize)
{
#ifdef TCC_TARGET_PE
int size, align;
*ret_align = 1; // Never have to re-align return values for x86
*regsize = 4;
size = type_size(vt, &align);
if (size > 8 || (size & (size - 1)))
return 0;
if (size == 8)
ret->t = VT_LLONG;
else if (size == 4)
ret->t = VT_INT;
else if (size == 2)
ret->t = VT_SHORT;
else
ret->t = VT_BYTE;
ret->ref = NULL;
return 1;
#else
*ret_align = 1; // Never have to re-align return values for x86
return 0;
#endif
}
/* Generate function call. The function address is pushed first, then
all the parameters in call order. This functions pops all the
parameters and the function address. */
ST_FUNC void gfunc_call(int nb_args)
{
int size, align, r, args_size, i, func_call;
Sym *func_sym;
args_size = 0;
for(i = 0;i < nb_args; i++) {
if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) {
size = type_size(&vtop->type, &align);
/* align to stack align size */
size = (size + 3) & ~3;
/* allocate the necessary size on stack */
oad(0xec81, size); /* sub $xxx, %esp */
/* generate structure store */
r = get_reg(RC_INT);
o(0x89); /* mov %esp, r */
o(0xe0 + r);
vset(&vtop->type, r | VT_LVAL, 0);
vswap();
vstore();
args_size += size;
} else if (is_float(vtop->type.t)) {
gv(RC_FLOAT); /* only one float register */
if ((vtop->type.t & VT_BTYPE) == VT_FLOAT)
size = 4;
else if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE)
size = 8;
else
size = 12;
oad(0xec81, size); /* sub $xxx, %esp */
if (size == 12)
o(0x7cdb);
else
o(0x5cd9 + size - 4); /* fstp[s|l] 0(%esp) */
g(0x24);
g(0x00);
args_size += size;
} else {
/* simple type (currently always same size) */
/* XXX: implicit cast ? */
r = gv(RC_INT);
if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
size = 8;
o(0x50 + vtop->r2); /* push r */
} else {
size = 4;
}
o(0x50 + r); /* push r */
args_size += size;
}
vtop--;
}
save_regs(0); /* save used temporary registers */
func_sym = vtop->type.ref;
func_call = func_sym->a.func_call;
/* fast call case */
if ((func_call >= FUNC_FASTCALL1 && func_call <= FUNC_FASTCALL3) ||
func_call == FUNC_FASTCALLW) {
int fastcall_nb_regs;
uint8_t *fastcall_regs_ptr;
if (func_call == FUNC_FASTCALLW) {
fastcall_regs_ptr = fastcallw_regs;
fastcall_nb_regs = 2;
} else {
fastcall_regs_ptr = fastcall_regs;
fastcall_nb_regs = func_call - FUNC_FASTCALL1 + 1;
}
for(i = 0;i < fastcall_nb_regs; i++) {
if (args_size <= 0)
break;
o(0x58 + fastcall_regs_ptr[i]); /* pop r */
/* XXX: incorrect for struct/floats */
args_size -= 4;
}
}
#ifndef TCC_TARGET_PE
else if ((vtop->type.ref->type.t & VT_BTYPE) == VT_STRUCT)
args_size -= 4;
#endif
gcall_or_jmp(0);
if (args_size && func_call != FUNC_STDCALL)
gadd_sp(args_size);
vtop--;
}
#ifdef TCC_TARGET_PE
#define FUNC_PROLOG_SIZE (10 + USE_EBX)
#else
#define FUNC_PROLOG_SIZE (9 + USE_EBX)
#endif
/* generate function prolog of type 't' */
ST_FUNC void gfunc_prolog(CType *func_type)
{
int addr, align, size, func_call, fastcall_nb_regs;
int param_index, param_addr;
uint8_t *fastcall_regs_ptr;
Sym *sym;
CType *type;
sym = func_type->ref;
func_call = sym->a.func_call;
addr = 8;
loc = 0;
func_vc = 0;
if (func_call >= FUNC_FASTCALL1 && func_call <= FUNC_FASTCALL3) {
fastcall_nb_regs = func_call - FUNC_FASTCALL1 + 1;
fastcall_regs_ptr = fastcall_regs;
} else if (func_call == FUNC_FASTCALLW) {
fastcall_nb_regs = 2;
fastcall_regs_ptr = fastcallw_regs;
} else {
fastcall_nb_regs = 0;
fastcall_regs_ptr = NULL;
}
param_index = 0;
ind += FUNC_PROLOG_SIZE;
func_sub_sp_offset = ind;
/* if the function returns a structure, then add an
implicit pointer parameter */
func_vt = sym->type;
func_var = (sym->c == FUNC_ELLIPSIS);
#ifdef TCC_TARGET_PE
size = type_size(&func_vt,&align);
if (((func_vt.t & VT_BTYPE) == VT_STRUCT)
&& (size > 8 || (size & (size - 1)))) {
#else
if ((func_vt.t & VT_BTYPE) == VT_STRUCT) {
#endif
/* XXX: fastcall case ? */
func_vc = addr;
addr += 4;
param_index++;
}
/* define parameters */
while ((sym = sym->next) != NULL) {
type = &sym->type;
size = type_size(type, &align);
size = (size + 3) & ~3;
#ifdef FUNC_STRUCT_PARAM_AS_PTR
/* structs are passed as pointer */
if ((type->t & VT_BTYPE) == VT_STRUCT) {
size = 4;
}
#endif
if (param_index < fastcall_nb_regs) {
/* save FASTCALL register */
loc -= 4;
o(0x89); /* movl */
gen_modrm(fastcall_regs_ptr[param_index], VT_LOCAL, NULL, loc);
param_addr = loc;
} else {
param_addr = addr;
addr += size;
}
sym_push(sym->v & ~SYM_FIELD, type,
VT_LOCAL | lvalue_type(type->t), param_addr);
param_index++;
}
func_ret_sub = 0;
/* pascal type call ? */
if (func_call == FUNC_STDCALL)
func_ret_sub = addr - 8;
#ifndef TCC_TARGET_PE
else if (func_vc)
func_ret_sub = 4;
#endif
#ifdef CONFIG_TCC_BCHECK
/* leave some room for bound checking code */
if (tcc_state->do_bounds_check) {
func_bound_offset = lbounds_section->data_offset;
func_bound_ind = ind;
oad(0xb8, 0); /* lbound section pointer */
oad(0xb8, 0); /* call to function */
}
#endif
}
/* generate function epilog */
ST_FUNC void gfunc_epilog(void)
{
addr_t v, saved_ind;
#ifdef CONFIG_TCC_BCHECK
if (tcc_state->do_bounds_check
&& func_bound_offset != lbounds_section->data_offset) {
addr_t saved_ind;
addr_t *bounds_ptr;
Sym *sym_data;
/* add end of table info */
bounds_ptr = section_ptr_add(lbounds_section, sizeof(addr_t));
*bounds_ptr = 0;
/* generate bound local allocation */
saved_ind = ind;
ind = func_bound_ind;
sym_data = get_sym_ref(&char_pointer_type, lbounds_section,
func_bound_offset, lbounds_section->data_offset);
greloc(cur_text_section, sym_data,
ind + 1, R_386_32);
oad(0xb8, 0); /* mov %eax, xxx */
gen_static_call(TOK___bound_local_new);
ind = saved_ind;
/* generate bound check local freeing */
o(0x5250); /* save returned value, if any */
greloc(cur_text_section, sym_data, ind + 1, R_386_32);
oad(0xb8, 0); /* mov %eax, xxx */
gen_static_call(TOK___bound_local_delete);
o(0x585a); /* restore returned value, if any */
}
#endif
/* align local size to word & save local variables */
v = (-loc + 3) & -4;
#if USE_EBX
o(0x8b);
gen_modrm(TREG_EBX, VT_LOCAL, NULL, -(v+4));
#endif
o(0xc9); /* leave */
if (func_ret_sub == 0) {
o(0xc3); /* ret */
} else {
o(0xc2); /* ret n */
g(func_ret_sub);
g(func_ret_sub >> 8);
}
saved_ind = ind;
ind = func_sub_sp_offset - FUNC_PROLOG_SIZE;
#ifdef TCC_TARGET_PE
if (v >= 4096) {
oad(0xb8, v); /* mov stacksize, %eax */
gen_static_call(TOK___chkstk); /* call __chkstk, (does the stackframe too) */
} else
#endif
{
o(0xe58955); /* push %ebp, mov %esp, %ebp */
o(0xec81); /* sub esp, stacksize */
gen_le32(v);
#ifdef TCC_TARGET_PE
o(0x90); /* adjust to FUNC_PROLOG_SIZE */
#endif
}
o(0x53 * USE_EBX); /* push ebx */
ind = saved_ind;
}
/* generate a jump to a label */
ST_FUNC int gjmp(int t)
{
return gjmp2(0xe9, t);
}
/* generate a jump to a fixed address */
ST_FUNC void gjmp_addr(int a)
{
int r;
r = a - ind - 2;
if (r == (char)r) {
g(0xeb);
g(r);
} else {
oad(0xe9, a - ind - 5);
}
}
ST_FUNC void gtst_addr(int inv, int a)
{
int v = vtop->r & VT_VALMASK;
if (v == VT_CMP) {
inv ^= (vtop--)->c.i;
a -= ind + 2;
if (a == (char)a) {
g(inv - 32);
g(a);
} else {
g(0x0f);
oad(inv - 16, a - 4);
}
} else if ((v & ~1) == VT_JMP) {
if ((v & 1) != inv) {
gjmp_addr(a);
gsym(vtop->c.i);
} else {
gsym(vtop->c.i);
o(0x05eb);
gjmp_addr(a);
}
vtop--;
}
}
/* generate a test. set 'inv' to invert test. Stack entry is popped */
ST_FUNC int gtst(int inv, int t)
{
int v = vtop->r & VT_VALMASK;
if (nocode_wanted) {
;
} else if (v == VT_CMP) {
/* fast case : can jump directly since flags are set */
g(0x0f);
t = gjmp2((vtop->c.i - 16) ^ inv, t);
} else if (v == VT_JMP || v == VT_JMPI) {
/* && or || optimization */
if ((v & 1) == inv) {
/* insert vtop->c jump list in t */
uint32_t n1, n = vtop->c.i;
if (n) {
while ((n1 = read32le(cur_text_section->data + n)))
n = n1;
write32le(cur_text_section->data + n, t);
t = vtop->c.i;
}
} else {
t = gjmp(t);
gsym(vtop->c.i);
}
}
vtop--;
return t;
}
/* generate an integer binary operation */
ST_FUNC void gen_opi(int op)
{
int r, fr, opc, c;
switch(op) {
case '+':
case TOK_ADDC1: /* add with carry generation */
opc = 0;
gen_op8:
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
/* constant case */
vswap();
r = gv(RC_INT);
vswap();
c = vtop->c.i;
if (c == (char)c) {
/* generate inc and dec for smaller code */
if (c==1 && opc==0 && op != TOK_ADDC1) {
o (0x40 | r); // inc
} else if (c==1 && opc==5 && op != TOK_SUBC1) {
o (0x48 | r); // dec
} else {
o(0x83);
o(0xc0 | (opc << 3) | r);
g(c);
}
} else {
o(0x81);
oad(0xc0 | (opc << 3) | r, c);
}
} else {
gv2(RC_INT, RC_INT);
r = vtop[-1].r;
fr = vtop[0].r;
o((opc << 3) | 0x01);
o(0xc0 + r + fr * 8);
}
vtop--;
if (op >= TOK_ULT && op <= TOK_GT) {
vtop->r = VT_CMP;
vtop->c.i = op;
}
break;
case '-':
case TOK_SUBC1: /* sub with carry generation */
opc = 5;
goto gen_op8;
case TOK_ADDC2: /* add with carry use */
opc = 2;
goto gen_op8;
case TOK_SUBC2: /* sub with carry use */
opc = 3;
goto gen_op8;
case '&':
opc = 4;
goto gen_op8;
case '^':
opc = 6;
goto gen_op8;
case '|':
opc = 1;
goto gen_op8;
case '*':
gv2(RC_INT, RC_INT);
r = vtop[-1].r;
fr = vtop[0].r;
vtop--;
o(0xaf0f); /* imul fr, r */
o(0xc0 + fr + r * 8);
break;
case TOK_SHL:
opc = 4;
goto gen_shift;
case TOK_SHR:
opc = 5;
goto gen_shift;
case TOK_SAR:
opc = 7;
gen_shift:
opc = 0xc0 | (opc << 3);
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
/* constant case */
vswap();
r = gv(RC_INT);
vswap();
c = vtop->c.i & 0x1f;
o(0xc1); /* shl/shr/sar $xxx, r */
o(opc | r);
g(c);
} else {
/* we generate the shift in ecx */
gv2(RC_INT, RC_ECX);
r = vtop[-1].r;
o(0xd3); /* shl/shr/sar %cl, r */
o(opc | r);
}
vtop--;
break;
case '/':
case TOK_UDIV:
case TOK_PDIV:
case '%':
case TOK_UMOD:
case TOK_UMULL:
/* first operand must be in eax */
/* XXX: need better constraint for second operand */
gv2(RC_EAX, RC_ECX);
r = vtop[-1].r;
fr = vtop[0].r;
vtop--;
save_reg(TREG_EDX);
/* save EAX too if used otherwise */
save_reg_upstack(TREG_EAX, 1);
if (op == TOK_UMULL) {
o(0xf7); /* mul fr */
o(0xe0 + fr);
vtop->r2 = TREG_EDX;
r = TREG_EAX;
} else {
if (op == TOK_UDIV || op == TOK_UMOD) {
o(0xf7d231); /* xor %edx, %edx, div fr, %eax */
o(0xf0 + fr);
} else {
o(0xf799); /* cltd, idiv fr, %eax */
o(0xf8 + fr);
}
if (op == '%' || op == TOK_UMOD)
r = TREG_EDX;
else
r = TREG_EAX;
}
vtop->r = r;
break;
default:
opc = 7;
goto gen_op8;
}
}
/* generate a floating point operation 'v = t1 op t2' instruction. The
two operands are guaranteed to have the same floating point type */
/* XXX: need to use ST1 too */
ST_FUNC void gen_opf(int op)
{
int a, ft, fc, swapped, r;
/* convert constants to memory references */
if ((vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
vswap();
gv(RC_FLOAT);
vswap();
}
if ((vtop[0].r & (VT_VALMASK | VT_LVAL)) == VT_CONST)
gv(RC_FLOAT);
/* must put at least one value in the floating point register */
if ((vtop[-1].r & VT_LVAL) &&
(vtop[0].r & VT_LVAL)) {
vswap();
gv(RC_FLOAT);
vswap();
}
swapped = 0;
/* swap the stack if needed so that t1 is the register and t2 is
the memory reference */
if (vtop[-1].r & VT_LVAL) {
vswap();
swapped = 1;
}
if (op >= TOK_ULT && op <= TOK_GT) {
/* load on stack second operand */
load(TREG_ST0, vtop);
save_reg(TREG_EAX); /* eax is used by FP comparison code */
if (op == TOK_GE || op == TOK_GT)
swapped = !swapped;
else if (op == TOK_EQ || op == TOK_NE)
swapped = 0;
if (swapped)
o(0xc9d9); /* fxch %st(1) */
if (op == TOK_EQ || op == TOK_NE)
o(0xe9da); /* fucompp */
else
o(0xd9de); /* fcompp */
o(0xe0df); /* fnstsw %ax */
if (op == TOK_EQ) {
o(0x45e480); /* and $0x45, %ah */
o(0x40fC80); /* cmp $0x40, %ah */
} else if (op == TOK_NE) {
o(0x45e480); /* and $0x45, %ah */
o(0x40f480); /* xor $0x40, %ah */
op = TOK_NE;
} else if (op == TOK_GE || op == TOK_LE) {
o(0x05c4f6); /* test $0x05, %ah */
op = TOK_EQ;
} else {
o(0x45c4f6); /* test $0x45, %ah */
op = TOK_EQ;
}
vtop--;
vtop->r = VT_CMP;
vtop->c.i = op;
} else {
/* no memory reference possible for long double operations */
if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) {
load(TREG_ST0, vtop);
swapped = !swapped;
}
switch(op) {
default:
case '+':
a = 0;
break;
case '-':
a = 4;
if (swapped)
a++;
break;
case '*':
a = 1;
break;
case '/':
a = 6;
if (swapped)
a++;
break;
}
ft = vtop->type.t;
fc = vtop->c.i;
if ((ft & VT_BTYPE) == VT_LDOUBLE) {
o(0xde); /* fxxxp %st, %st(1) */
o(0xc1 + (a << 3));
} else {
/* if saved lvalue, then we must reload it */
r = vtop->r;
if ((r & VT_VALMASK) == VT_LLOCAL) {
SValue v1;
r = get_reg(RC_INT);
v1.type.t = VT_INT;
v1.r = VT_LOCAL | VT_LVAL;
v1.c.i = fc;
load(r, &v1);
fc = 0;
}
if ((ft & VT_BTYPE) == VT_DOUBLE)
o(0xdc);
else
o(0xd8);
gen_modrm(a, r, vtop->sym, fc);
}
vtop--;
}
}
/* convert integers to fp 't' type. Must handle 'int', 'unsigned int'
and 'long long' cases. */
ST_FUNC void gen_cvt_itof(int t)
{
save_reg(TREG_ST0);
gv(RC_INT);
if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
/* signed long long to float/double/long double (unsigned case
is handled generically) */
o(0x50 + vtop->r2); /* push r2 */
o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
o(0x242cdf); /* fildll (%esp) */
o(0x08c483); /* add $8, %esp */
} else if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) ==
(VT_INT | VT_UNSIGNED)) {
/* unsigned int to float/double/long double */
o(0x6a); /* push $0 */
g(0x00);
o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
o(0x242cdf); /* fildll (%esp) */
o(0x08c483); /* add $8, %esp */
} else {
/* int to float/double/long double */
o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
o(0x2404db); /* fildl (%esp) */
o(0x04c483); /* add $4, %esp */
}
vtop->r = TREG_ST0;
}
/* convert fp to int 't' type */
ST_FUNC void gen_cvt_ftoi(int t)
{
#if HAVE_FLOAT
int bt = vtop->type.t & VT_BTYPE;
if (bt == VT_FLOAT)
vpush_global_sym(&func_old_type, TOK___fixsfdi);
else if (bt == VT_LDOUBLE)
vpush_global_sym(&func_old_type, TOK___fixxfdi);
else
vpush_global_sym(&func_old_type, TOK___fixdfdi);
vswap();
gfunc_call(1);
vpushi(0);
vtop->r = REG_IRET;
vtop->r2 = REG_LRET;
#endif // HAVE_FLOAT
}
/* convert from one floating point type to another */
ST_FUNC void gen_cvt_ftof(int t)
{
/* all we have to do on i386 is to put the float in a register */
gv(RC_FLOAT);
}
/* computed goto support */
ST_FUNC void ggoto(void)
{
gcall_or_jmp(1);
vtop--;
}
/* bound check support functions */
#ifdef CONFIG_TCC_BCHECK
/* generate a bounded pointer addition */
ST_FUNC void gen_bounded_ptr_add(void)
{
/* prepare fast i386 function call (args in eax and edx) */
gv2(RC_EAX, RC_EDX);
/* save all temporary registers */
vtop -= 2;
save_regs(0);
/* do a fast function call */
gen_static_call(TOK___bound_ptr_add);
/* returned pointer is in eax */
vtop++;
vtop->r = TREG_EAX | VT_BOUNDED;
/* address of bounding function call point */
vtop->c.i = (cur_text_section->reloc->data_offset - sizeof(Elf32_Rel));
}
/* patch pointer addition in vtop so that pointer dereferencing is
also tested */
ST_FUNC void gen_bounded_ptr_deref(void)
{
addr_t func;
int size, align;
Elf32_Rel *rel;
Sym *sym;
size = 0;
/* XXX: put that code in generic part of tcc */
if (!is_float(vtop->type.t)) {
if (vtop->r & VT_LVAL_BYTE)
size = 1;
else if (vtop->r & VT_LVAL_SHORT)
size = 2;
}
if (!size)
size = type_size(&vtop->type, &align);
switch(size) {
case 1: func = TOK___bound_ptr_indir1; break;
case 2: func = TOK___bound_ptr_indir2; break;
case 4: func = TOK___bound_ptr_indir4; break;
case 8: func = TOK___bound_ptr_indir8; break;
case 12: func = TOK___bound_ptr_indir12; break;
case 16: func = TOK___bound_ptr_indir16; break;
default:
tcc_error("unhandled size when dereferencing bounded pointer");
func = 0;
break;
}
/* patch relocation */
/* XXX: find a better solution ? */
rel = (Elf32_Rel *)(cur_text_section->reloc->data + vtop->c.i);
sym = external_global_sym(func, &func_old_type, 0);
if (!sym->c)
put_extern_sym(sym, NULL, 0, 0);
rel->r_info = ELF32_R_INFO(sym->c, ELF32_R_TYPE(rel->r_info));
}
#endif
/* Save the stack pointer onto the stack */
ST_FUNC void gen_vla_sp_save(int addr) {
/* mov %esp,addr(%ebp)*/
o(0x89);
gen_modrm(TREG_ESP, VT_LOCAL, NULL, addr);
}
/* Restore the SP from a location on the stack */
ST_FUNC void gen_vla_sp_restore(int addr) {
o(0x8b);
gen_modrm(TREG_ESP, VT_LOCAL, NULL, addr);
}
/* Subtract from the stack pointer, and push the resulting value onto the stack */
ST_FUNC void gen_vla_alloc(CType *type, int align) {
#ifdef TCC_TARGET_PE
/* alloca does more than just adjust %rsp on Windows */
vpush_global_sym(&func_old_type, TOK_alloca);
vswap(); /* Move alloca ref past allocation size */
gfunc_call(1);
#else
int r;
r = gv(RC_INT); /* allocation size */
/* sub r,%rsp */
o(0x2b);
o(0xe0 | r);
/* We align to 16 bytes rather than align */
/* and ~15, %esp */
o(0xf0e483);
vpop();
#endif
}
/* end of X86 code generator */
/*************************************************************/
#endif
/*************************************************************/
#ifdef TARGET_DEFS_ONLY
#define EM_TCC_TARGET EM_386
/* relocation type for 32 bit data relocation */
#define R_DATA_32 R_386_32
#define R_DATA_PTR R_386_32
#define R_JMP_SLOT R_386_JMP_SLOT
#define R_GLOB_DAT R_386_GLOB_DAT
#define R_COPY R_386_COPY
#define R_RELATIVE R_386_RELATIVE
#define R_NUM R_386_NUM
#define ELF_START_ADDR 0x08048000
#define ELF_PAGE_SIZE 0x1000
#define PCRELATIVE_DLLPLT 0
#define RELOCATE_DLLPLT 0
#else /* !TARGET_DEFS_ONLY */
#ifndef _TCC_H
#include "tcc.h"
#endif
/* Returns 1 for a code relocation, 0 for a data relocation. For unknown
relocations, returns -1. */
int code_reloc (int reloc_type)
{
switch (reloc_type) {
case R_386_RELATIVE:
case R_386_16:
case R_386_32:
case R_386_GOTPC:
case R_386_GOTOFF:
case R_386_GOT32:
case R_386_GOT32X:
case R_386_GLOB_DAT:
case R_386_COPY:
return 0;
case R_386_PC16:
case R_386_PC32:
case R_386_PLT32:
case R_386_JMP_SLOT:
return 1;
}
tcc_error ("Unknown relocation type: %d", reloc_type);
return -1;
}
/* Returns an enumerator to describe whether and when the relocation needs a
GOT and/or PLT entry to be created. See tcc.h for a description of the
different values. */
int gotplt_entry_type (int reloc_type)
{
switch (reloc_type) {
case R_386_RELATIVE:
case R_386_16:
case R_386_GLOB_DAT:
case R_386_JMP_SLOT:
case R_386_COPY:
return NO_GOTPLT_ENTRY;
case R_386_32:
/* This relocations shouldn't normally need GOT or PLT
slots if it weren't for simplicity in the code generator.
See our caller for comments. */
return AUTO_GOTPLT_ENTRY;
case R_386_PC16:
case R_386_PC32:
return AUTO_GOTPLT_ENTRY;
case R_386_GOTPC:
case R_386_GOTOFF:
return BUILD_GOT_ONLY;
case R_386_GOT32:
case R_386_GOT32X:
case R_386_PLT32:
return ALWAYS_GOTPLT_ENTRY;
}
tcc_error ("Unknown relocation type: %d", reloc_type);
return -1;
}
ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_attr *attr)
{
Section *plt = s1->plt;
uint8_t *p;
int modrm;
unsigned plt_offset, relofs;
/* on i386 if we build a DLL, we add a %ebx offset */
if (s1->output_type == TCC_OUTPUT_DLL)
modrm = 0xa3;
else
modrm = 0x25;
/* empty PLT: create PLT0 entry that pushes the library identifier
(GOT + PTR_SIZE) and jumps to ld.so resolution routine
(GOT + 2 * PTR_SIZE) */
if (plt->data_offset == 0) {
p = section_ptr_add(plt, 16);
p[0] = 0xff; /* pushl got + PTR_SIZE */
p[1] = modrm + 0x10;
write32le(p + 2, PTR_SIZE);
p[6] = 0xff; /* jmp *(got + PTR_SIZE * 2) */
p[7] = modrm;
write32le(p + 8, PTR_SIZE * 2);
}
plt_offset = plt->data_offset;
/* The PLT slot refers to the relocation entry it needs via offset.
The reloc entry is created below, so its offset is the current
data_offset */
relofs = s1->got->reloc ? s1->got->reloc->data_offset : 0;
/* Jump to GOT entry where ld.so initially put the address of ip + 4 */
p = section_ptr_add(plt, 16);
p[0] = 0xff; /* jmp *(got + x) */
p[1] = modrm;
write32le(p + 2, got_offset);
p[6] = 0x68; /* push $xxx */
write32le(p + 7, relofs);
p[11] = 0xe9; /* jmp plt_start */
write32le(p + 12, -(plt->data_offset));
return plt_offset;
}
/* relocate the PLT: compute addresses and offsets in the PLT now that final
address for PLT and GOT are known (see fill_program_header) */
ST_FUNC void relocate_plt(TCCState *s1)
{
uint8_t *p, *p_end;
if (!s1->plt)
return;
p = s1->plt->data;
p_end = p + s1->plt->data_offset;
if (p < p_end) {
add32le(p + 2, s1->got->sh_addr);
add32le(p + 8, s1->got->sh_addr);
p += 16;
while (p < p_end) {
add32le(p + 2, s1->got->sh_addr);
p += 16;
}
}
}
static ElfW_Rel *qrel; /* ptr to next reloc entry reused */
void relocate_init(Section *sr)
{
qrel = (ElfW_Rel *) sr->data;
}
void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, addr_t addr, addr_t val)
{
int sym_index, esym_index;
sym_index = ELFW(R_SYM)(rel->r_info);
switch (type) {
case R_386_32:
if (s1->output_type == TCC_OUTPUT_DLL) {
esym_index = s1->sym_attrs[sym_index].dyn_index;
qrel->r_offset = rel->r_offset;
if (esym_index) {
qrel->r_info = ELFW(R_INFO)(esym_index, R_386_32);
qrel++;
return;
} else {
qrel->r_info = ELFW(R_INFO)(0, R_386_RELATIVE);
qrel++;
}
}
add32le(ptr, val);
return;
case R_386_PC32:
if (s1->output_type == TCC_OUTPUT_DLL) {
/* DLL relocation */
esym_index = s1->sym_attrs[sym_index].dyn_index;
if (esym_index) {
qrel->r_offset = rel->r_offset;
qrel->r_info = ELFW(R_INFO)(esym_index, R_386_PC32);
qrel++;
return;
}
}
add32le(ptr, val - addr);
return;
case R_386_PLT32:
add32le(ptr, val - addr);
return;
case R_386_GLOB_DAT:
case R_386_JMP_SLOT:
write32le(ptr, val);
return;
case R_386_GOTPC:
add32le(ptr, s1->got->sh_addr - addr);
return;
case R_386_GOTOFF:
add32le(ptr, val - s1->got->sh_addr);
return;
case R_386_GOT32:
case R_386_GOT32X:
/* we load the got offset */
add32le(ptr, s1->sym_attrs[sym_index].got_offset);
return;
case R_386_16:
if (s1->output_format != TCC_OUTPUT_FORMAT_BINARY) {
output_file:
tcc_error("can only produce 16-bit binary files");
}
write16le(ptr, read16le(ptr) + val);
return;
case R_386_PC16:
if (s1->output_format != TCC_OUTPUT_FORMAT_BINARY)
goto output_file;
write16le(ptr, read16le(ptr) + val - addr);
return;
case R_386_RELATIVE:
/* do nothing */
return;
case R_386_COPY:
/* This relocation must copy initialized data from the library
to the program .bss segment. Currently made like for ARM
(to remove noise of default case). Is this true?
*/
return;
default:
fprintf(stderr,"FIXME: handle reloc type %d at %x [%p] to %x\n",
type, (unsigned)addr, ptr, (unsigned)val);
return;
}
}
#endif /* !TARGET_DEFS_ONLY */
/*
* i386 specific functions for TCC assembler
*
* Copyright (c) 2001, 2002 Fabrice Bellard
* Copyright (c) 2009 Frédéric Feret (x86_64 support)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _TCC_H
#include "tcc.h"
#endif
#define MAX_OPERANDS 3
#define TOK_ASM_first TOK_ASM_clc
#define TOK_ASM_last TOK_ASM_emms
#define TOK_ASM_alllast TOK_ASM_subps
#define OPC_B 0x01 /* only used with OPC_WL */
#define OPC_WL 0x02 /* accepts w, l or no suffix */
#define OPC_BWL (OPC_B | OPC_WL) /* accepts b, w, l or no suffix */
#define OPC_REG 0x04 /* register is added to opcode */
#define OPC_MODRM 0x08 /* modrm encoding */
#define OPCT_MASK 0x70
#define OPC_FWAIT 0x10 /* add fwait opcode */
#define OPC_SHIFT 0x20 /* shift opcodes */
#define OPC_ARITH 0x30 /* arithmetic opcodes */
#define OPC_FARITH 0x40 /* FPU arithmetic opcodes */
#define OPC_TEST 0x50 /* test opcodes */
#define OPCT_IS(v,i) (((v) & OPCT_MASK) == (i))
#define OPC_0F 0x100 /* Is secondary map (0x0f prefix) */
#define OPC_48 0x200 /* Always has REX prefix */
#ifdef TCC_TARGET_X86_64
# define OPC_WLQ 0x1000 /* accepts w, l, q or no suffix */
# define OPC_BWLQ (OPC_B | OPC_WLQ) /* accepts b, w, l, q or no suffix */
# define OPC_WLX OPC_WLQ
# define OPC_BWLX OPC_BWLQ
#else
# define OPC_WLX OPC_WL
# define OPC_BWLX OPC_BWL
#endif
#define OPC_GROUP_SHIFT 13
/* in order to compress the operand type, we use specific operands and
we or only with EA */
enum {
OPT_REG8=0, /* warning: value is hardcoded from TOK_ASM_xxx */
OPT_REG16, /* warning: value is hardcoded from TOK_ASM_xxx */
OPT_REG32, /* warning: value is hardcoded from TOK_ASM_xxx */
#ifdef TCC_TARGET_X86_64
OPT_REG64, /* warning: value is hardcoded from TOK_ASM_xxx */
#endif
OPT_MMX, /* warning: value is hardcoded from TOK_ASM_xxx */
OPT_SSE, /* warning: value is hardcoded from TOK_ASM_xxx */
OPT_CR, /* warning: value is hardcoded from TOK_ASM_xxx */
OPT_TR, /* warning: value is hardcoded from TOK_ASM_xxx */
OPT_DB, /* warning: value is hardcoded from TOK_ASM_xxx */
OPT_SEG,
OPT_ST,
#ifdef TCC_TARGET_X86_64
OPT_REG8_LOW, /* %spl,%bpl,%sil,%dil, encoded like ah,ch,dh,bh, but
with REX prefix, not used in insn templates */
#endif
OPT_IM8,
OPT_IM8S,
OPT_IM16,
OPT_IM32,
#ifdef TCC_TARGET_X86_64
OPT_IM64,
#endif
OPT_EAX, /* %al, %ax, %eax or %rax register */
OPT_ST0, /* %st(0) register */
OPT_CL, /* %cl register */
OPT_DX, /* %dx register */
OPT_ADDR, /* OP_EA with only offset */
OPT_INDIR, /* *(expr) */
/* composite types */
OPT_COMPOSITE_FIRST,
OPT_IM, /* IM8 | IM16 | IM32 */
OPT_REG, /* REG8 | REG16 | REG32 | REG64 */
OPT_REGW, /* REG16 | REG32 | REG64 */
OPT_IMW, /* IM16 | IM32 */
OPT_MMXSSE, /* MMX | SSE */
OPT_DISP, /* Like OPT_ADDR, but emitted as displacement (for jumps) */
OPT_DISP8, /* Like OPT_ADDR, but only 8bit (short jumps) */
/* can be ored with any OPT_xxx */
OPT_EA = 0x80
};
#define OP_REG8 (1 << OPT_REG8)
#define OP_REG16 (1 << OPT_REG16)
#define OP_REG32 (1 << OPT_REG32)
#define OP_MMX (1 << OPT_MMX)
#define OP_SSE (1 << OPT_SSE)
#define OP_CR (1 << OPT_CR)
#define OP_TR (1 << OPT_TR)
#define OP_DB (1 << OPT_DB)
#define OP_SEG (1 << OPT_SEG)
#define OP_ST (1 << OPT_ST)
#define OP_IM8 (1 << OPT_IM8)
#define OP_IM8S (1 << OPT_IM8S)
#define OP_IM16 (1 << OPT_IM16)
#define OP_IM32 (1 << OPT_IM32)
#define OP_EAX (1 << OPT_EAX)
#define OP_ST0 (1 << OPT_ST0)
#define OP_CL (1 << OPT_CL)
#define OP_DX (1 << OPT_DX)
#define OP_ADDR (1 << OPT_ADDR)
#define OP_INDIR (1 << OPT_INDIR)
#ifdef TCC_TARGET_X86_64
# define OP_REG64 (1 << OPT_REG64)
# define OP_REG8_LOW (1 << OPT_REG8_LOW)
# define OP_IM64 (1 << OPT_IM64)
# define OP_EA32 (OP_EA << 1)
#else
# define OP_REG64 0
# define OP_REG8_LOW 0
# define OP_IM64 0
# define OP_EA32 0
#endif
#define OP_EA 0x40000000
#define OP_REG (OP_REG8 | OP_REG16 | OP_REG32 | OP_REG64)
#ifdef TCC_TARGET_X86_64
# define TREG_XAX TREG_RAX
# define TREG_XCX TREG_RCX
# define TREG_XDX TREG_RDX
#else
# define TREG_XAX TREG_EAX
# define TREG_XCX TREG_ECX
# define TREG_XDX TREG_EDX
#endif
typedef struct ASMInstr {
uint16_t sym;
uint16_t opcode;
uint16_t instr_type;
uint8_t nb_ops;
uint8_t op_type[MAX_OPERANDS]; /* see OP_xxx */
} ASMInstr;
typedef struct Operand {
uint32_t type;
int8_t reg; /* register, -1 if none */
int8_t reg2; /* second register, -1 if none */
uint8_t shift;
ExprValue e;
} Operand;
static const uint8_t reg_to_size[9] = {
/*
[OP_REG8] = 0,
[OP_REG16] = 1,
[OP_REG32] = 2,
#ifdef TCC_TARGET_X86_64
[OP_REG64] = 3,
#endif
*/
0, 0, 1, 0, 2, 0, 0, 0, 3
};
#define NB_TEST_OPCODES 30
static const uint8_t test_bits[NB_TEST_OPCODES] = {
0x00, /* o */
0x01, /* no */
0x02, /* b */
0x02, /* c */
0x02, /* nae */
0x03, /* nb */
0x03, /* nc */
0x03, /* ae */
0x04, /* e */
0x04, /* z */
0x05, /* ne */
0x05, /* nz */
0x06, /* be */
0x06, /* na */
0x07, /* nbe */
0x07, /* a */
0x08, /* s */
0x09, /* ns */
0x0a, /* p */
0x0a, /* pe */
0x0b, /* np */
0x0b, /* po */
0x0c, /* l */
0x0c, /* nge */
0x0d, /* nl */
0x0d, /* ge */
0x0e, /* le */
0x0e, /* ng */
0x0f, /* nle */
0x0f, /* g */
};
static const uint8_t segment_prefixes[] = {
0x26, /* es */
0x2e, /* cs */
0x36, /* ss */
0x3e, /* ds */
0x64, /* fs */
0x65 /* gs */
};
static const ASMInstr asm_instrs[] = {
#define ALT(x) x
/* This removes a 0x0f in the second byte */
#if HAVE_LONG_LONG
#define O(o) ((uint64_t) ((((o) & 0xff00) == 0x0f00) ? ((((o) >> 8) & ~0xff) | ((o) & 0xff)) : (o)))
#else
#define O(o) ((uint32_t) ((((o) & 0xff00) == 0x0f00) ? ((((o) >> 8) & ~0xff) | ((o) & 0xff)) : (o)))
#endif
/* This constructs instr_type from opcode, type and group. */
#define T(o,i,g) ((i) | ((g) << OPC_GROUP_SHIFT) | ((((o) & 0xff00) == 0x0f00) ? OPC_0F : 0))
#define DEF_ASM_OP0(name, opcode)
#define DEF_ASM_OP0L(name, opcode, group, instr_type) { TOK_ASM_ ## name, O(opcode), T(opcode, instr_type, group), 0, { 0 } },
#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) { TOK_ASM_ ## name, O(opcode), T(opcode, instr_type, group), 1, { op0 }},
#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) { TOK_ASM_ ## name, O(opcode), T(opcode, instr_type, group), 2, { op0, op1 }},
#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) { TOK_ASM_ ## name, O(opcode), T(opcode, instr_type, group), 3, { op0, op1, op2 }},
#ifdef TCC_TARGET_X86_64
# include "x86_64-asm.h"
#else
# include "i386-asm.h"
#endif
/* last operation */
{ 0, },
};
static const uint16_t op0_codes[] = {
#define ALT(x)
#define DEF_ASM_OP0(x, opcode) opcode,
#define DEF_ASM_OP0L(name, opcode, group, instr_type)
#define DEF_ASM_OP1(name, opcode, group, instr_type, op0)
#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1)
#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2)
#ifdef TCC_TARGET_X86_64
# include "x86_64-asm.h"
#else
# include "i386-asm.h"
#endif
};
static inline int get_reg_shift(TCCState *s1)
{
int shift, v;
v = asm_int_expr(s1);
switch(v) {
case 1:
shift = 0;
break;
case 2:
shift = 1;
break;
case 4:
shift = 2;
break;
case 8:
shift = 3;
break;
default:
expect("1, 2, 4 or 8 constant");
shift = 0;
break;
}
return shift;
}
#ifdef TCC_TARGET_X86_64
static int asm_parse_numeric_reg(int t, unsigned int *type)
{
int reg = -1;
if (t >= TOK_IDENT && t < tok_ident) {
const char *s = table_ident[t - TOK_IDENT]->str;
char c;
*type = OP_REG64;
if (*s == 'c') {
s++;
*type = OP_CR;
}
if (*s++ != 'r')
return -1;
/* Don't allow leading '0'. */
if ((c = *s++) >= '1' && c <= '9')
reg = c - '0';
else
return -1;
if ((c = *s) >= '0' && c <= '5')
s++, reg = reg * 10 + c - '0';
if (reg > 15)
return -1;
if ((c = *s) == 0)
;
else if (*type != OP_REG64)
return -1;
else if (c == 'b' && !s[1])
*type = OP_REG8;
else if (c == 'w' && !s[1])
*type = OP_REG16;
else if (c == 'd' && !s[1])
*type = OP_REG32;
else
return -1;
}
return reg;
}
#endif
static int asm_parse_reg(unsigned int *type)
{
int reg = 0;
*type = 0;
if (tok != '%')
goto error_32;
next();
if (tok >= TOK_ASM_eax && tok <= TOK_ASM_edi) {
reg = tok - TOK_ASM_eax;
*type = OP_REG32;
#ifdef TCC_TARGET_X86_64
} else if (tok >= TOK_ASM_rax && tok <= TOK_ASM_rdi) {
reg = tok - TOK_ASM_rax;
*type = OP_REG64;
} else if (tok == TOK_ASM_rip) {
reg = -2; /* Probably should use different escape code. */
*type = OP_REG64;
} else if ((reg = asm_parse_numeric_reg(tok, type)) >= 0
&& (*type == OP_REG32 || *type == OP_REG64)) {
;
#endif
} else {
error_32:
expect("register");
}
next();
return reg;
}
static void parse_operand(TCCState *s1, Operand *op)
{
ExprValue e;
int reg, indir;
const char *p;
indir = 0;
if (tok == '*') {
next();
indir = OP_INDIR;
}
if (tok == '%') {
next();
if (tok >= TOK_ASM_al && tok <= TOK_ASM_db7) {
reg = tok - TOK_ASM_al;
op->type = 1 << (reg >> 3); /* WARNING: do not change constant order */
op->reg = reg & 7;
if ((op->type & OP_REG) && op->reg == TREG_XAX)
op->type |= OP_EAX;
else if (op->type == OP_REG8 && op->reg == TREG_XCX)
op->type |= OP_CL;
else if (op->type == OP_REG16 && op->reg == TREG_XDX)
op->type |= OP_DX;
} else if (tok >= TOK_ASM_dr0 && tok <= TOK_ASM_dr7) {
op->type = OP_DB;
op->reg = tok - TOK_ASM_dr0;
} else if (tok >= TOK_ASM_es && tok <= TOK_ASM_gs) {
op->type = OP_SEG;
op->reg = tok - TOK_ASM_es;
} else if (tok == TOK_ASM_st) {
op->type = OP_ST;
op->reg = 0;
next();
if (tok == '(') {
next();
if (tok != TOK_PPNUM)
goto reg_error;
p = tokc.str.data;
reg = p[0] - '0';
if ((unsigned)reg >= 8 || p[1] != '\0')
goto reg_error;
op->reg = reg;
next();
skip(')');
}
if (op->reg == 0)
op->type |= OP_ST0;
goto no_skip;
#ifdef TCC_TARGET_X86_64
} else if (tok >= TOK_ASM_spl && tok <= TOK_ASM_dil) {
op->type = OP_REG8 | OP_REG8_LOW;
op->reg = 4 + tok - TOK_ASM_spl;
} else if ((op->reg = asm_parse_numeric_reg(tok, &op->type)) >= 0) {
;
#endif
} else {
reg_error:
tcc_error("unknown register %%%s", get_tok_str(tok, &tokc));
}
next();
no_skip: ;
} else if (tok == '$') {
/* constant value */
next();
asm_expr(s1, &e);
op->type = OP_IM32;
op->e = e;
if (!op->e.sym) {
if (op->e.v == (uint8_t)op->e.v)
op->type |= OP_IM8;
if (op->e.v == (int8_t)op->e.v)
op->type |= OP_IM8S;
if (op->e.v == (uint16_t)op->e.v)
op->type |= OP_IM16;
#ifdef TCC_TARGET_X86_64
if (op->e.v != (int32_t)op->e.v && op->e.v != (uint32_t)op->e.v)
op->type = OP_IM64;
#endif
}
} else {
/* address(reg,reg2,shift) with all variants */
op->type = OP_EA;
op->reg = -1;
op->reg2 = -1;
op->shift = 0;
if (tok != '(') {
asm_expr(s1, &e);
op->e = e;
} else {
next();
if (tok == '%') {
unget_tok('(');
op->e.v = 0;
op->e.sym = NULL;
} else {
/* bracketed offset expression */
asm_expr(s1, &e);
if (tok != ')')
expect(")");
next();
op->e.v = e.v;
op->e.sym = e.sym;
}
op->e.pcrel = 0;
}
if (tok == '(') {
unsigned int type = 0;
next();
if (tok != ',') {
op->reg = asm_parse_reg(&type);
}
if (tok == ',') {
next();
if (tok != ',') {
op->reg2 = asm_parse_reg(&type);
}
if (tok == ',') {
next();
op->shift = get_reg_shift(s1);
}
}
if (type & OP_REG32)
op->type |= OP_EA32;
skip(')');
}
if (op->reg == -1 && op->reg2 == -1)
op->type |= OP_ADDR;
}
op->type |= indir;
}
/* XXX: unify with C code output ? */
ST_FUNC void gen_expr32(ExprValue *pe)
{
if (pe->pcrel)
/* If PC-relative, always set VT_SYM, even without symbol,
so as to force a relocation to be emitted. */
gen_addrpc32(VT_SYM, pe->sym, pe->v);
else
gen_addr32(pe->sym ? VT_SYM : 0, pe->sym, pe->v);
}
#ifdef TCC_TARGET_X86_64
ST_FUNC void gen_expr64(ExprValue *pe)
{
gen_addr64(pe->sym ? VT_SYM : 0, pe->sym, pe->v);
}
#endif
/* XXX: unify with C code output ? */
static void gen_disp32(ExprValue *pe)
{
Sym *sym = pe->sym;
if (sym && sym->r == cur_text_section->sh_num) {
/* same section: we can output an absolute value. Note
that the TCC compiler behaves differently here because
it always outputs a relocation to ease (future) code
elimination in the linker */
gen_le32(pe->v + sym->jnext - ind - 4);
} else {
if (sym && sym->type.t == VT_VOID) {
sym->type.t = VT_FUNC;
sym->type.ref = NULL;
}
gen_addrpc32(VT_SYM, sym, pe->v);
}
}
/* generate the modrm operand */
static inline int asm_modrm(int reg, Operand *op)
{
int mod, reg1, reg2, sib_reg1;
if (op->type & (OP_REG | OP_MMX | OP_SSE)) {
g(0xc0 + (reg << 3) + op->reg);
} else if (op->reg == -1 && op->reg2 == -1) {
/* displacement only */
#ifdef TCC_TARGET_X86_64
g(0x04 + (reg << 3));
g(0x25);
#else
g(0x05 + (reg << 3));
#endif
gen_expr32(&op->e);
#ifdef TCC_TARGET_X86_64
} else if (op->reg == -2) {
ExprValue *pe = &op->e;
g(0x05 + (reg << 3));
gen_addrpc32(pe->sym ? VT_SYM : 0, pe->sym, pe->v);
return ind;
#endif
} else {
sib_reg1 = op->reg;
/* fist compute displacement encoding */
if (sib_reg1 == -1) {
sib_reg1 = 5;
mod = 0x00;
} else if (op->e.v == 0 && !op->e.sym && op->reg != 5) {
mod = 0x00;
} else if (op->e.v == (int8_t)op->e.v && !op->e.sym) {
mod = 0x40;
} else {
mod = 0x80;
}
/* compute if sib byte needed */
reg1 = op->reg;
if (op->reg2 != -1)
reg1 = 4;
g(mod + (reg << 3) + reg1);
if (reg1 == 4) {
/* add sib byte */
reg2 = op->reg2;
if (reg2 == -1)
reg2 = 4; /* indicate no index */
g((op->shift << 6) + (reg2 << 3) + sib_reg1);
}
/* add offset */
if (mod == 0x40) {
g(op->e.v);
} else if (mod == 0x80 || op->reg == -1) {
gen_expr32(&op->e);
}
}
return 0;
}
#ifdef TCC_TARGET_X86_64
#define REX_W 0x48
#define REX_R 0x44
#define REX_X 0x42
#define REX_B 0x41
static void asm_rex(int width64, Operand *ops, int nb_ops, int *op_type,
int regi, int rmi)
{
unsigned char rex = width64 ? 0x48 : 0;
int saw_high_8bit = 0;
int i;
if (rmi == -1) {
/* No mod/rm byte, but we might have a register op nevertheless
(we will add it to the opcode later). */
for(i = 0; i < nb_ops; i++) {
if (op_type[i] & (OP_REG | OP_ST)) {
if (ops[i].reg >= 8) {
rex |= REX_B;
ops[i].reg -= 8;
} else if (ops[i].type & OP_REG8_LOW)
rex |= 0x40;
else if (ops[i].type & OP_REG8 && ops[i].reg >= 4)
/* An 8 bit reg >= 4 without REG8 is ah/ch/dh/bh */
saw_high_8bit = ops[i].reg;
break;
}
}
} else {
if (regi != -1) {
if (ops[regi].reg >= 8) {
rex |= REX_R;
ops[regi].reg -= 8;
} else if (ops[regi].type & OP_REG8_LOW)
rex |= 0x40;
else if (ops[regi].type & OP_REG8 && ops[regi].reg >= 4)
/* An 8 bit reg >= 4 without REG8 is ah/ch/dh/bh */
saw_high_8bit = ops[regi].reg;
}
if (ops[rmi].type & (OP_REG | OP_MMX | OP_SSE | OP_CR | OP_EA)) {
if (ops[rmi].reg >= 8) {
rex |= REX_B;
ops[rmi].reg -= 8;
} else if (ops[rmi].type & OP_REG8_LOW)
rex |= 0x40;
else if (ops[rmi].type & OP_REG8 && ops[rmi].reg >= 4)
/* An 8 bit reg >= 4 without REG8 is ah/ch/dh/bh */
saw_high_8bit = ops[rmi].reg;
}
if (ops[rmi].type & OP_EA && ops[rmi].reg2 >= 8) {
rex |= REX_X;
ops[rmi].reg2 -= 8;
}
}
if (rex) {
if (saw_high_8bit)
tcc_error("can't encode register %%%ch when REX prefix is required",
"acdb"[saw_high_8bit-4]);
g(rex);
}
}
#endif
static void maybe_print_stats (void)
{
static int already = 1;
if (!already)
/* print stats about opcodes */
{
const struct ASMInstr *pa;
int freq[4];
int op_vals[500];
int nb_op_vals, i, j;
already = 1;
nb_op_vals = 0;
memset(freq, 0, sizeof(freq));
for(pa = asm_instrs; pa->sym != 0; pa++) {
freq[pa->nb_ops]++;
//for(i=0;i<pa->nb_ops;i++) {
for(j=0;j<nb_op_vals;j++) {
//if (pa->op_type[i] == op_vals[j])
if (pa->instr_type == op_vals[j])
goto found;
}
//op_vals[nb_op_vals++] = pa->op_type[i];
op_vals[nb_op_vals++] = pa->instr_type;
found: ;
//}
}
for(i=0;i<nb_op_vals;i++) {
int v = op_vals[i];
//if ((v & (v - 1)) != 0)
printf("%3d: %08x\n", i, v);
}
printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
(int)sizeof(asm_instrs),
(int)sizeof(asm_instrs) / (int)sizeof(ASMInstr),
freq[0], freq[1], freq[2], freq[3]);
}
}
ST_FUNC void asm_opcode(TCCState *s1, int opcode)
{
const ASMInstr *pa;
int i, modrm_index, modreg_index, reg, v, op1, seg_prefix, pc;
int nb_ops, s;
Operand ops[MAX_OPERANDS], *pop;
int op_type[3]; /* decoded op type */
int alltypes; /* OR of all operand types */
int autosize;
int p66;
#ifdef TCC_TARGET_X86_64
int rex64;
#endif
maybe_print_stats();
/* force synthetic ';' after prefix instruction, so we can handle */
/* one-line things like "rep stosb" instead of only "rep\nstosb" */
if (opcode >= TOK_ASM_wait && opcode <= TOK_ASM_repnz)
unget_tok(';');
/* get operands */
pop = ops;
nb_ops = 0;
seg_prefix = 0;
alltypes = 0;
for(;;) {
if (tok == ';' || tok == TOK_LINEFEED)
break;
if (nb_ops >= MAX_OPERANDS) {
tcc_error("incorrect number of operands");
}
parse_operand(s1, pop);
if (tok == ':') {
if (pop->type != OP_SEG || seg_prefix)
tcc_error("incorrect prefix");
seg_prefix = segment_prefixes[pop->reg];
next();
parse_operand(s1, pop);
if (!(pop->type & OP_EA)) {
tcc_error("segment prefix must be followed by memory reference");
}
}
pop++;
nb_ops++;
if (tok != ',')
break;
next();
}
s = 0; /* avoid warning */
/* optimize matching by using a lookup table (no hashing is needed
!) */
for(pa = asm_instrs; pa->sym != 0; pa++) {
int it = pa->instr_type & OPCT_MASK;
s = 0;
if (it == OPC_FARITH) {
v = opcode - pa->sym;
if (!((unsigned)v < 8 * 6 && (v % 6) == 0))
continue;
} else if (it == OPC_ARITH) {
if (!(opcode >= pa->sym && opcode < pa->sym + 8*NBWLX))
continue;
s = (opcode - pa->sym) % NBWLX;
if ((pa->instr_type & OPC_BWLX) == OPC_WLX)
{
/* We need to reject the xxxb opcodes that we accepted above.
Note that pa->sym for WLX opcodes is the 'w' token,
to get the 'b' token subtract one. */
if (((opcode - pa->sym + 1) % NBWLX) == 0)
continue;
s++;
}
} else if (it == OPC_SHIFT) {
if (!(opcode >= pa->sym && opcode < pa->sym + 7*NBWLX))
continue;
s = (opcode - pa->sym) % NBWLX;
} else if (it == OPC_TEST) {
if (!(opcode >= pa->sym && opcode < pa->sym + NB_TEST_OPCODES))
continue;
/* cmovxx is a test opcode but accepts multiple sizes.
TCC doesn't accept the suffixed mnemonic, instead we
simply force size autodetection always. */
if (pa->instr_type & OPC_WLX)
s = NBWLX - 1;
} else if (pa->instr_type & OPC_B) {
#ifdef TCC_TARGET_X86_64
/* Some instructions don't have the full size but only
bwl form. insb e.g. */
if ((pa->instr_type & OPC_WLQ) != OPC_WLQ
&& !(opcode >= pa->sym && opcode < pa->sym + NBWLX-1))
continue;
#endif
if (!(opcode >= pa->sym && opcode < pa->sym + NBWLX))
continue;
s = opcode - pa->sym;
} else if (pa->instr_type & OPC_WLX) {
if (!(opcode >= pa->sym && opcode < pa->sym + NBWLX-1))
continue;
s = opcode - pa->sym + 1;
} else {
if (pa->sym != opcode)
continue;
}
if (pa->nb_ops != nb_ops)
continue;
#ifdef TCC_TARGET_X86_64
/* Special case for moves. Selecting the IM64->REG64 form
should only be done if we really have an >32bit imm64, and that
is hardcoded. Ignore it here. */
if (pa->opcode == 0xb0 && ops[0].type != OP_IM64
&& (ops[1].type & OP_REG) == OP_REG64
&& !(pa->instr_type & OPC_0F))
continue;
#endif
/* now decode and check each operand */
alltypes = 0;
for(i = 0; i < nb_ops; i++) {
int op1, op2;
op1 = pa->op_type[i];
op2 = op1 & 0x1f;
switch(op2) {
case OPT_IM:
v = OP_IM8 | OP_IM16 | OP_IM32;
break;
case OPT_REG:
v = OP_REG8 | OP_REG16 | OP_REG32 | OP_REG64;
break;
case OPT_REGW:
v = OP_REG16 | OP_REG32 | OP_REG64;
break;
case OPT_IMW:
v = OP_IM16 | OP_IM32;
break;
case OPT_MMXSSE:
v = OP_MMX | OP_SSE;
break;
case OPT_DISP:
case OPT_DISP8:
v = OP_ADDR;
break;
default:
v = 1 << op2;
break;
}
if (op1 & OPT_EA)
v |= OP_EA;
op_type[i] = v;
if ((ops[i].type & v) == 0)
goto next;
alltypes |= ops[i].type;
}
/* all is matching ! */
break;
next: ;
}
if (pa->sym == 0) {
if (opcode >= TOK_ASM_first && opcode <= TOK_ASM_last) {
int b;
b = op0_codes[opcode - TOK_ASM_first];
if (b & 0xff00)
g(b >> 8);
g(b);
return;
} else if (opcode <= TOK_ASM_alllast) {
tcc_error("bad operand with opcode '%s'",
get_tok_str(opcode, NULL));
} else {
tcc_error("unknown opcode '%s'",
get_tok_str(opcode, NULL));
}
}
/* if the size is unknown, then evaluate it (OPC_B or OPC_WL case) */
autosize = NBWLX-1;
#ifdef TCC_TARGET_X86_64
/* XXX the autosize should rather be zero, to not have to adjust this
all the time. */
if ((pa->instr_type & OPC_BWLQ) == OPC_B)
autosize = NBWLX-2;
#endif
if (s == autosize) {
/* Check for register operands providing hints about the size.
Start from the end, i.e. destination operands. This matters
only for opcodes accepting different sized registers, lar and lsl
are such opcodes. */
for(i = nb_ops - 1; s == autosize && i >= 0; i--) {
if ((ops[i].type & OP_REG) && !(op_type[i] & (OP_CL | OP_DX)))
s = reg_to_size[ops[i].type & OP_REG];
}
if (s == autosize) {
if ((opcode == TOK_ASM_push || opcode == TOK_ASM_pop) &&
(ops[0].type & (OP_SEG | OP_IM8S | OP_IM32)))
s = 2;
else if ((opcode == TOK_ASM_push || opcode == TOK_ASM_pop) &&
(ops[0].type & OP_EA))
s = NBWLX - 2;
else
tcc_error("cannot infer opcode suffix");
}
}
#ifdef TCC_TARGET_X86_64
/* Generate addr32 prefix if needed */
for(i = 0; i < nb_ops; i++) {
if (ops[i].type & OP_EA32) {
g(0x67);
break;
}
}
#endif
/* generate data16 prefix if needed */
p66 = 0;
if (s == 1)
p66 = 1;
else {
/* accepting mmx+sse in all operands --> needs 0x66 to
switch to sse mode. Accepting only sse in an operand --> is
already SSE insn and needs 0x66/f2/f3 handling. */
for (i = 0; i < nb_ops; i++)
if ((op_type[i] & (OP_MMX | OP_SSE)) == (OP_MMX | OP_SSE)
&& ops[i].type & OP_SSE)
p66 = 1;
}
if (p66)
g(0x66);
#ifdef TCC_TARGET_X86_64
rex64 = 0;
if (pa->instr_type & OPC_48)
rex64 = 1;
else if (s == 3 || (alltypes & OP_REG64)) {
/* generate REX prefix */
int default64 = 0;
for(i = 0; i < nb_ops; i++) {
if (op_type[i] == OP_REG64 && pa->opcode != 0xb8) {
/* If only 64bit regs are accepted in one operand
this is a default64 instruction without need for
REX prefixes, except for movabs(0xb8). */
default64 = 1;
break;
}
}
/* XXX find better encoding for the default64 instructions. */
if (((opcode != TOK_ASM_push && opcode != TOK_ASM_pop
&& opcode != TOK_ASM_pushw && opcode != TOK_ASM_pushl
&& opcode != TOK_ASM_pushq && opcode != TOK_ASM_popw
&& opcode != TOK_ASM_popl && opcode != TOK_ASM_popq
&& opcode != TOK_ASM_call && opcode != TOK_ASM_jmp))
&& !default64)
rex64 = 1;
}
#endif
/* now generates the operation */
if (OPCT_IS(pa->instr_type, OPC_FWAIT))
g(0x9b);
if (seg_prefix)
g(seg_prefix);
v = pa->opcode;
if (pa->instr_type & OPC_0F)
v = ((v & ~0xff) << 8) | 0x0f00 | (v & 0xff);
if ((v == 0x69 || v == 0x6b) && nb_ops == 2) {
/* kludge for imul $im, %reg */
nb_ops = 3;
ops[2] = ops[1];
op_type[2] = op_type[1];
} else if (v == 0xcd && ops[0].e.v == 3 && !ops[0].e.sym) {
v--; /* int $3 case */
nb_ops = 0;
} else if ((v == 0x06 || v == 0x07)) {
if (ops[0].reg >= 4) {
/* push/pop %fs or %gs */
v = 0x0fa0 + (v - 0x06) + ((ops[0].reg - 4) << 3);
} else {
v += ops[0].reg << 3;
}
nb_ops = 0;
} else if (v <= 0x05) {
/* arith case */
v += ((opcode - TOK_ASM_addb) / NBWLX) << 3;
} else if ((pa->instr_type & (OPCT_MASK | OPC_MODRM)) == OPC_FARITH) {
/* fpu arith case */
v += ((opcode - pa->sym) / 6) << 3;
}
/* search which operand will be used for modrm */
modrm_index = -1;
modreg_index = -1;
if (pa->instr_type & OPC_MODRM) {
if (!nb_ops) {
/* A modrm opcode without operands is a special case (e.g. mfence).
It has a group and acts as if there's an register operand 0
(ax). */
i = 0;
ops[i].type = OP_REG;
ops[i].reg = 0;
goto modrm_found;
}
/* first look for an ea operand */
for(i = 0;i < nb_ops; i++) {
if (op_type[i] & OP_EA)
goto modrm_found;
}
/* then if not found, a register or indirection (shift instructions) */
for(i = 0;i < nb_ops; i++) {
if (op_type[i] & (OP_REG | OP_MMX | OP_SSE | OP_INDIR))
goto modrm_found;
}
#ifdef ASM_DEBUG
tcc_error("bad op table");
#endif
modrm_found:
modrm_index = i;
/* if a register is used in another operand then it is
used instead of group */
for(i = 0;i < nb_ops; i++) {
int t = op_type[i];
if (i != modrm_index &&
(t & (OP_REG | OP_MMX | OP_SSE | OP_CR | OP_TR | OP_DB | OP_SEG))) {
modreg_index = i;
break;
}
}
}
#ifdef TCC_TARGET_X86_64
asm_rex (rex64, ops, nb_ops, op_type, modreg_index, modrm_index);
#endif
if (pa->instr_type & OPC_REG) {
/* mov $im, %reg case */
if (v == 0xb0 && s >= 1)
v += 7;
for(i = 0; i < nb_ops; i++) {
if (op_type[i] & (OP_REG | OP_ST)) {
v += ops[i].reg;
break;
}
}
}
if (pa->instr_type & OPC_B)
v += s >= 1;
if (nb_ops == 1 && pa->op_type[0] == OPT_DISP8) {
Sym *sym;
int jmp_disp;
/* see if we can really generate the jump with a byte offset */
sym = ops[0].e.sym;
if (!sym)
goto no_short_jump;
if (sym->r != cur_text_section->sh_num)
goto no_short_jump;
jmp_disp = ops[0].e.v + sym->jnext - ind - 2 - (v >= 0xff);
if (jmp_disp == (int8_t)jmp_disp) {
/* OK to generate jump */
ops[0].e.sym = 0;
ops[0].e.v = jmp_disp;
op_type[0] = OP_IM8S;
} else {
no_short_jump:
/* long jump will be allowed. need to modify the
opcode slightly */
if (v == 0xeb) /* jmp */
v = 0xe9;
else if (v == 0x70) /* jcc */
v += 0x0f10;
else
tcc_error("invalid displacement");
}
}
if (OPCT_IS(pa->instr_type, OPC_TEST))
v += test_bits[opcode - pa->sym];
op1 = v >> 16;
if (op1)
g(op1);
op1 = (v >> 8) & 0xff;
if (op1)
g(op1);
g(v);
if (OPCT_IS(pa->instr_type, OPC_SHIFT)) {
reg = (opcode - pa->sym) / NBWLX;
if (reg == 6)
reg = 7;
} else if (OPCT_IS(pa->instr_type, OPC_ARITH)) {
reg = (opcode - pa->sym) / NBWLX;
} else if (OPCT_IS(pa->instr_type, OPC_FARITH)) {
reg = (opcode - pa->sym) / 6;
} else {
reg = (pa->instr_type >> OPC_GROUP_SHIFT) & 7;
}
pc = 0;
if (pa->instr_type & OPC_MODRM) {
/* if a register is used in another operand then it is
used instead of group */
if (modreg_index >= 0)
reg = ops[modreg_index].reg;
pc = asm_modrm(reg, &ops[modrm_index]);
}
/* emit constants */
#ifndef TCC_TARGET_X86_64
if (!(pa->instr_type & OPC_0F)
&& (pa->opcode == 0x9a || pa->opcode == 0xea)) {
/* ljmp or lcall kludge */
gen_expr32(&ops[1].e);
if (ops[0].e.sym)
tcc_error("cannot relocate");
gen_le16(ops[0].e.v);
return;
}
#endif
for(i = 0;i < nb_ops; i++) {
v = op_type[i];
if (v & (OP_IM8 | OP_IM16 | OP_IM32 | OP_IM64 | OP_IM8S | OP_ADDR)) {
/* if multiple sizes are given it means we must look
at the op size */
if ((v | OP_IM8 | OP_IM64) == (OP_IM8 | OP_IM16 | OP_IM32 | OP_IM64)) {
if (s == 0)
v = OP_IM8;
else if (s == 1)
v = OP_IM16;
else if (s == 2 || (v & OP_IM64) == 0)
v = OP_IM32;
else
v = OP_IM64;
}
if ((v & (OP_IM8 | OP_IM8S | OP_IM16)) && ops[i].e.sym)
tcc_error("cannot relocate");
if (v & (OP_IM8 | OP_IM8S)) {
g(ops[i].e.v);
} else if (v & OP_IM16) {
gen_le16(ops[i].e.v);
#ifdef TCC_TARGET_X86_64
} else if (v & OP_IM64) {
gen_expr64(&ops[i].e);
#endif
} else if (pa->op_type[i] == OPT_DISP || pa->op_type[i] == OPT_DISP8) {
gen_disp32(&ops[i].e);
} else {
gen_expr32(&ops[i].e);
}
}
}
/* after immediate operands, adjust pc-relative address */
if (pc)
add32le(cur_text_section->data + pc - 4, pc - ind);
}
/* return the constraint priority (we allocate first the lowest
numbered constraints) */
static inline int constraint_priority(const char *str)
{
int priority, c, pr;
/* we take the lowest priority */
priority = 0;
for(;;) {
c = *str;
if (c == '\0')
break;
str++;
switch(c) {
case 'A':
pr = 0;
break;
case 'a':
case 'b':
case 'c':
case 'd':
case 'S':
case 'D':
pr = 1;
break;
case 'q':
pr = 2;
break;
case 'r':
case 'R':
case 'p':
pr = 3;
break;
case 'N':
case 'M':
case 'I':
case 'e':
case 'i':
case 'm':
case 'g':
pr = 4;
break;
default:
tcc_error("unknown constraint '%c'", c);
pr = 0;
}
if (pr > priority)
priority = pr;
}
return priority;
}
static const char *skip_constraint_modifiers(const char *p)
{
while (*p == '=' || *p == '&' || *p == '+' || *p == '%')
p++;
return p;
}
/* If T (a token) is of the form "%reg" returns the register
number and type, otherwise return -1. */
ST_FUNC int asm_parse_regvar (int t)
{
const char *s;
Operand op;
if (t < TOK_IDENT)
return -1;
s = table_ident[t - TOK_IDENT]->str;
if (s[0] != '%')
return -1;
t = tok_alloc(s+1, strlen(s)-1)->tok;
unget_tok(t);
unget_tok('%');
parse_operand(tcc_state, &op);
/* Accept only integer regs for now. */
if (op.type & OP_REG)
return op.reg;
else
return -1;
}
#define REG_OUT_MASK 0x01
#define REG_IN_MASK 0x02
#define is_reg_allocated(reg) (regs_allocated[reg] & reg_mask)
ST_FUNC void asm_compute_constraints(ASMOperand *operands,
int nb_operands, int nb_outputs,
const uint8_t *clobber_regs,
int *pout_reg)
{
ASMOperand *op;
int sorted_op[MAX_ASM_OPERANDS];
int i, j, k, p1, p2, tmp, reg, c, reg_mask;
const char *str;
uint8_t regs_allocated[NB_ASM_REGS];
/* init fields */
for(i=0;i<nb_operands;i++) {
op = &operands[i];
op->input_index = -1;
op->ref_index = -1;
op->reg = -1;
op->is_memory = 0;
op->is_rw = 0;
}
/* compute constraint priority and evaluate references to output
constraints if input constraints */
for(i=0;i<nb_operands;i++) {
op = &operands[i];
str = op->constraint;
str = skip_constraint_modifiers(str);
if (isnum(*str) || *str == '[') {
/* this is a reference to another constraint */
k = find_constraint(operands, nb_operands, str, NULL);
if ((unsigned)k >= i || i < nb_outputs)
tcc_error("invalid reference in constraint %d ('%s')",
i, str);
op->ref_index = k;
if (operands[k].input_index >= 0)
tcc_error("cannot reference twice the same operand");
operands[k].input_index = i;
op->priority = 5;
} else if ((op->vt->r & VT_VALMASK) == VT_LOCAL
&& op->vt->sym
&& (reg = op->vt->sym->r & VT_VALMASK) < VT_CONST) {
op->priority = 1;
op->reg = reg;
} else {
op->priority = constraint_priority(str);
}
}
/* sort operands according to their priority */
for(i=0;i<nb_operands;i++)
sorted_op[i] = i;
for(i=0;i<nb_operands - 1;i++) {
for(j=i+1;j<nb_operands;j++) {
p1 = operands[sorted_op[i]].priority;
p2 = operands[sorted_op[j]].priority;
if (p2 < p1) {
tmp = sorted_op[i];
sorted_op[i] = sorted_op[j];
sorted_op[j] = tmp;
}
}
}
for(i = 0;i < NB_ASM_REGS; i++) {
if (clobber_regs[i])
regs_allocated[i] = REG_IN_MASK | REG_OUT_MASK;
else
regs_allocated[i] = 0;
}
/* esp cannot be used */
regs_allocated[4] = REG_IN_MASK | REG_OUT_MASK;
/* ebp cannot be used yet */
regs_allocated[5] = REG_IN_MASK | REG_OUT_MASK;
/* allocate registers and generate corresponding asm moves */
for(i=0;i<nb_operands;i++) {
j = sorted_op[i];
op = &operands[j];
str = op->constraint;
/* no need to allocate references */
if (op->ref_index >= 0)
continue;
/* select if register is used for output, input or both */
if (op->input_index >= 0) {
reg_mask = REG_IN_MASK | REG_OUT_MASK;
} else if (j < nb_outputs) {
reg_mask = REG_OUT_MASK;
} else {
reg_mask = REG_IN_MASK;
}
if (op->reg >= 0) {
if (is_reg_allocated(op->reg))
tcc_error("asm regvar requests register that's taken already");
reg = op->reg;
goto reg_found;
}
try_next:
c = *str++;
switch(c) {
case '=':
goto try_next;
case '+':
op->is_rw = 1;
/* FALL THRU */
case '&':
if (j >= nb_outputs)
tcc_error("'%c' modifier can only be applied to outputs", c);
reg_mask = REG_IN_MASK | REG_OUT_MASK;
goto try_next;
case 'A':
/* allocate both eax and edx */
if (is_reg_allocated(TREG_XAX) ||
is_reg_allocated(TREG_XDX))
goto try_next;
op->is_llong = 1;
op->reg = TREG_XAX;
regs_allocated[TREG_XAX] |= reg_mask;
regs_allocated[TREG_XDX] |= reg_mask;
break;
case 'a':
reg = TREG_XAX;
goto alloc_reg;
case 'b':
reg = 3;
goto alloc_reg;
case 'c':
reg = TREG_XCX;
goto alloc_reg;
case 'd':
reg = TREG_XDX;
goto alloc_reg;
case 'S':
reg = 6;
goto alloc_reg;
case 'D':
reg = 7;
alloc_reg:
if (is_reg_allocated(reg))
goto try_next;
goto reg_found;
case 'q':
/* eax, ebx, ecx or edx */
for(reg = 0; reg < 4; reg++) {
if (!is_reg_allocated(reg))
goto reg_found;
}
goto try_next;
case 'r':
case 'R':
case 'p': /* A general address, for x86(64) any register is acceptable*/
/* any general register */
for(reg = 0; reg < 8; reg++) {
if (!is_reg_allocated(reg))
goto reg_found;
}
goto try_next;
reg_found:
/* now we can reload in the register */
op->is_llong = 0;
op->reg = reg;
regs_allocated[reg] |= reg_mask;
break;
case 'e':
case 'i':
if (!((op->vt->r & (VT_VALMASK | VT_LVAL)) == VT_CONST))
goto try_next;
break;
case 'I':
case 'N':
case 'M':
if (!((op->vt->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST))
goto try_next;
break;
case 'm':
case 'g':
/* nothing special to do because the operand is already in
memory, except if the pointer itself is stored in a
memory variable (VT_LLOCAL case) */
/* XXX: fix constant case */
/* if it is a reference to a memory zone, it must lie
in a register, so we reserve the register in the
input registers and a load will be generated
later */
if (j < nb_outputs || c == 'm') {
if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) {
/* any general register */
for(reg = 0; reg < 8; reg++) {
if (!(regs_allocated[reg] & REG_IN_MASK))
goto reg_found1;
}
goto try_next;
reg_found1:
/* now we can reload in the register */
regs_allocated[reg] |= REG_IN_MASK;
op->reg = reg;
op->is_memory = 1;
}
}
break;
default:
tcc_error("asm constraint %d ('%s') could not be satisfied",
j, op->constraint);
break;
}
/* if a reference is present for that operand, we assign it too */
if (op->input_index >= 0) {
operands[op->input_index].reg = op->reg;
operands[op->input_index].is_llong = op->is_llong;
}
}
/* compute out_reg. It is used to store outputs registers to memory
locations references by pointers (VT_LLOCAL case) */
*pout_reg = -1;
for(i=0;i<nb_operands;i++) {
op = &operands[i];
if (op->reg >= 0 &&
(op->vt->r & VT_VALMASK) == VT_LLOCAL &&
!op->is_memory) {
for(reg = 0; reg < 8; reg++) {
if (!(regs_allocated[reg] & REG_OUT_MASK))
goto reg_found2;
}
tcc_error("could not find free output register for reloading");
reg_found2:
*pout_reg = reg;
break;
}
}
/* print sorted constraints */
#ifdef ASM_DEBUG
for(i=0;i<nb_operands;i++) {
j = sorted_op[i];
op = &operands[j];
printf("%%%d [%s]: \"%s\" r=0x%04x reg=%d\n",
j,
op->id ? get_tok_str(op->id, NULL) : "",
op->constraint,
op->vt->r,
op->reg);
}
if (*pout_reg >= 0)
printf("out_reg=%d\n", *pout_reg);
#endif
}
ST_FUNC void subst_asm_operand(CString *add_str,
SValue *sv, int modifier)
{
int r, reg, size, val;
char buf[64];
r = sv->r;
if ((r & VT_VALMASK) == VT_CONST) {
if (!(r & VT_LVAL) && modifier != 'c' && modifier != 'n' &&
modifier != 'P')
cstr_ccat(add_str, '$');
if (r & VT_SYM) {
const char *name = get_tok_str(sv->sym->v, NULL);
if (sv->sym->v >= SYM_FIRST_ANOM) {
/* In case of anonymous symbols ("L.42", used
for static data labels) we can't find them
in the C symbol table when later looking up
this name. So enter them now into the asm label
list when we still know the symbol. */
get_asm_sym(tok_alloc(name, strlen(name))->tok, sv->sym);
}
cstr_cat(add_str, name, -1);
if ((uint32_t)sv->c.i == 0)
goto no_offset;
cstr_ccat(add_str, '+');
}
val = sv->c.i;
if (modifier == 'n')
val = -val;
snprintf(buf, sizeof(buf), "%d", (int)sv->c.i);
cstr_cat(add_str, buf, -1);
no_offset:;
#ifdef TCC_TARGET_X86_64
if (r & VT_LVAL)
cstr_cat(add_str, "(%rip)", -1);
#endif
} else if ((r & VT_VALMASK) == VT_LOCAL) {
#ifdef TCC_TARGET_X86_64
snprintf(buf, sizeof(buf), "%d(%%rbp)", (int)sv->c.i);
#else
snprintf(buf, sizeof(buf), "%d(%%ebp)", (int)sv->c.i);
#endif
cstr_cat(add_str, buf, -1);
} else if (r & VT_LVAL) {
reg = r & VT_VALMASK;
if (reg >= VT_CONST)
tcc_error("internal compiler error");
snprintf(buf, sizeof(buf), "(%%%s)",
#ifdef TCC_TARGET_X86_64
get_tok_str(TOK_ASM_rax + reg, NULL)
#else
get_tok_str(TOK_ASM_eax + reg, NULL)
#endif
);
cstr_cat(add_str, buf, -1);
} else {
/* register case */
reg = r & VT_VALMASK;
if (reg >= VT_CONST)
tcc_error("internal compiler error");
/* choose register operand size */
if ((sv->type.t & VT_BTYPE) == VT_BYTE ||
(sv->type.t & VT_BTYPE) == VT_BOOL)
size = 1;
else if ((sv->type.t & VT_BTYPE) == VT_SHORT)
size = 2;
#ifdef TCC_TARGET_X86_64
else if ((sv->type.t & VT_BTYPE) == VT_LLONG ||
(sv->type.t & VT_BTYPE) == VT_PTR)
size = 8;
#endif
else
size = 4;
if (size == 1 && reg >= 4)
size = 4;
if (modifier == 'b') {
if (reg >= 4)
tcc_error("cannot use byte register");
size = 1;
} else if (modifier == 'h') {
if (reg >= 4)
tcc_error("cannot use byte register");
size = -1;
} else if (modifier == 'w') {
size = 2;
} else if (modifier == 'k') {
size = 4;
#ifdef TCC_TARGET_X86_64
} else if (modifier == 'q') {
size = 8;
#endif
}
switch(size) {
case -1:
reg = TOK_ASM_ah + reg;
break;
case 1:
reg = TOK_ASM_al + reg;
break;
case 2:
reg = TOK_ASM_ax + reg;
break;
default:
reg = TOK_ASM_eax + reg;
break;
#ifdef TCC_TARGET_X86_64
case 8:
reg = TOK_ASM_rax + reg;
break;
#endif
}
snprintf(buf, sizeof(buf), "%%%s", get_tok_str(reg, NULL));
cstr_cat(add_str, buf, -1);
}
}
/* generate prolog and epilog code for asm statement */
ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands,
int nb_outputs, int is_output,
uint8_t *clobber_regs,
int out_reg)
{
uint8_t regs_allocated[NB_ASM_REGS];
ASMOperand *op;
int i, reg;
/* Strictly speaking %Xbp and %Xsp should be included in the
call-preserved registers, but currently it doesn't matter. */
#ifdef TCC_TARGET_X86_64
#ifdef TCC_TARGET_PE
static uint8_t reg_saved[] = { 3, 6, 7, 12, 13, 14, 15 };
#else
static uint8_t reg_saved[] = { 3, 12, 13, 14, 15 };
#endif
#else
static uint8_t reg_saved[] = { 3, 6, 7 };
#endif
/* mark all used registers */
memcpy(regs_allocated, clobber_regs, sizeof(regs_allocated));
for(i = 0; i < nb_operands;i++) {
op = &operands[i];
if (op->reg >= 0)
regs_allocated[op->reg] = 1;
}
if (!is_output) {
/* generate reg save code */
for(i = 0; i < sizeof(reg_saved)/sizeof(reg_saved[0]); i++) {
reg = reg_saved[i];
if (regs_allocated[reg]) {
if (reg >= 8)
g(0x41), reg-=8;
g(0x50 + reg);
}
}
/* generate load code */
for(i = 0; i < nb_operands; i++) {
op = &operands[i];
if (op->reg >= 0) {
if ((op->vt->r & VT_VALMASK) == VT_LLOCAL &&
op->is_memory) {
/* memory reference case (for both input and
output cases) */
SValue sv;
sv = *op->vt;
sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL | VT_LVAL;
sv.type.t = VT_PTR;
load(op->reg, &sv);
} else if (i >= nb_outputs || op->is_rw) {
/* load value in register */
load(op->reg, op->vt);
if (op->is_llong) {
SValue sv;
sv = *op->vt;
sv.c.i += 4;
load(TREG_XDX, &sv);
}
}
}
}
} else {
/* generate save code */
for(i = 0 ; i < nb_outputs; i++) {
op = &operands[i];
if (op->reg >= 0) {
if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) {
if (!op->is_memory) {
SValue sv;
sv = *op->vt;
sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL;
sv.type.t = VT_PTR;
load(out_reg, &sv);
sv = *op->vt;
sv.r = (sv.r & ~VT_VALMASK) | out_reg;
store(op->reg, &sv);
}
} else {
store(op->reg, op->vt);
if (op->is_llong) {
SValue sv;
sv = *op->vt;
sv.c.i += 4;
store(TREG_XDX, &sv);
}
}
}
}
/* generate reg restore code */
for(i = sizeof(reg_saved)/sizeof(reg_saved[0]) - 1; i >= 0; i--) {
reg = reg_saved[i];
if (regs_allocated[reg]) {
if (reg >= 8)
g(0x41), reg-=8;
g(0x58 + reg);
}
}
}
}
ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str)
{
int reg;
TokenSym *ts;
#ifdef TCC_TARGET_X86_64
unsigned int type;
#endif
if (!strcmp(str, "memory") ||
!strcmp(str, "cc") ||
!strcmp(str, "flags"))
return;
ts = tok_alloc(str, strlen(str));
reg = ts->tok;
if (reg >= TOK_ASM_eax && reg <= TOK_ASM_edi) {
reg -= TOK_ASM_eax;
} else if (reg >= TOK_ASM_ax && reg <= TOK_ASM_di) {
reg -= TOK_ASM_ax;
#ifdef TCC_TARGET_X86_64
} else if (reg >= TOK_ASM_rax && reg <= TOK_ASM_rdi) {
reg -= TOK_ASM_rax;
} else if ((reg = asm_parse_numeric_reg(reg, &type)) >= 0) {
;
#endif
} else {
tcc_error("invalid clobber register '%s'", str);
}
clobber_regs[reg] = 1;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ctype.h>
int
isalnum (int c)
{
return isdigit (c) || isalpha (c);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ctype.h>
int
isalpha (int c)
{
return islower (c) || isupper (c);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ctype.h>
int
isascii (int c)
{
return c >= 0 && c <= 127;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ctype.h>
int
iscntrl (int c)
{
return c >= 0 && c < 32;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ctype.h>
int
isgraph (int c)
{
return c > 32 && c < 127;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ctype.h>
int
isprint (int c)
{
return c >= 32 && c < 127;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ctype.h>
int
ispunct (int c)
{
return isprint (c) && !isspace (c) && !isalnum (c);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright (C) 1991, 1993, 1995, 1996, 1998 Free Software Foundation, Inc.
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
// Taken from GNU C Library 2.2.5
#include <errno.h>
#include <stddef.h>
#include <stdlib.h>
#include <dirent.h>
#include <unistd.h>
#include <errno.h>
#include <stddef.h>
#include <stdlib.h>
#include <dirent.h>
#include <unistd.h>
#include <dirstream.h>
/* Close the directory stream DIRP.
Return 0 if successful, -1 if not. */
int
closedir (DIR * dirp)
{
int filedes;
if (dirp == NULL)
{
errno = EINVAL;
return -1;
}
filedes = dirp->fd;
free (dirp);
return close (filedes);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright (C) 1993 Free Software Foundation, Inc.
* Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
// Taken from GNU C Library 1.06.4
#include <mes/lib.h>
#include <dirent.h>
#include <stdio.h>
#include <unistd.h>
int
__getdirentries (int filedes, char *buffer, size_t nbytes, off_t * basep)
{
if (basep)
*basep = lseek (filedes, (off_t) 0, SEEK_CUR);
return read (filedes, buffer, nbytes);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright (C) 1991-1996,98,2000,2001 Free Software Foundation, Inc.
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
// Taken from GNU C Library 2.2.5
#include <mes/lib.h>
#include <errno.h>
#include <limits.h>
#include <stddef.h>
#include <stdlib.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <dirstream.h>
/* Open a directory stream on NAME. */
DIR *
opendir (char const *name)
{
DIR *dirp;
struct stat statbuf;
int fd;
size_t allocation;
int save_errno;
if (name[0] == '\0')
{
/* POSIX.1-1990 says an empty name gets ENOENT;
but `open' might like it fine. */
errno = ENOENT;
return 0;
}
fd = open (name, O_RDONLY | O_DIRECTORY);
if (fd < 0)
return 0;
if (fstat (fd, &statbuf) < 0)
goto lose;
if (fcntl (fd, F_SETFD, FD_CLOEXEC) < 0)
goto lose;
allocation = statbuf.st_blksize;
dirp = (DIR *) calloc (1, sizeof (DIR) + allocation);
if (!dirp)
lose:
{
save_errno = errno;
close (fd);
errno = save_errno;
return 0;
}
dirp->data = (char *) (dirp + 1);
dirp->allocation = allocation;
dirp->fd = fd;
return dirp;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright (C) 1991,92,93,94,95,96,97,99,2000 Free Software Foundation, Inc.
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
// Taken from GNU C Library 2.2.5
#include <errno.h>
#include <limits.h>
#include <stddef.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/types.h>
#include <assert.h>
#include <dirstream.h>
int getdents (int filedes, char *buffer, size_t nbytes);
/* Read a directory entry from DIRP. */
struct dirent *
readdir (DIR * dirp)
{
struct dirent *dp;
int saved_errno = errno;
do
{
size_t reclen;
if (dirp->offset >= dirp->size)
{
/* We've emptied out our buffer. Refill it. */
size_t maxread;
ssize_t bytes;
maxread = dirp->allocation;
#if 0
off_t base;
bytes = __getdirentries (dirp->fd, dirp->data, maxread, &base);
#else
bytes = getdents (dirp->fd, dirp->data, maxread);
#endif
if (bytes <= 0)
{
/* Don't modifiy errno when reaching EOF. */
if (bytes == 0)
errno = saved_errno;
dp = 0;
break;
}
dirp->size = (size_t) bytes;
/* Reset the offset into the buffer. */
dirp->offset = 0;
}
dp = (struct dirent *) &dirp->data[dirp->offset];
reclen = dp->d_reclen;
dirp->offset += reclen;
dirp->filepos = dp->d_off;
/* Skip deleted files. */
}
while (dp->d_ino == 0);
return dp;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
int
chdir (char const *file_name)
{
return _sys_call1 (SYS_chdir, (long) file_name);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <stdarg.h>
int
fcntl (int filedes, int command, ...)
{
va_list ap;
va_start (ap, command);
int data = va_arg (ap, int);
int r = _sys_call3 (SYS_fcntl, (int) filedes, (int) command, (int) data);
va_end (ap);
return r;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <sys/stat.h>
int
fstat (int filedes, struct stat *statbuf)
{
return _sys_call2 (SYS_fstat, (int) filedes, (long) statbuf);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <sys/types.h>
int
getdents (int filedes, char *buffer, size_t nbytes)
{
return _sys_call3 (SYS_getdents, (int) filedes, (long) buffer, (long) nbytes);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <unistd.h>
gid_t
getegid ()
{
return _sys_call (SYS_getegid);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <unistd.h>
uid_t
geteuid ()
{
return _sys_call (SYS_geteuid);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <unistd.h>
gid_t
getgid ()
{
return _sys_call (SYS_getgid);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <unistd.h>
pid_t
getppid ()
{
return _sys_call (SYS_getppid);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <sys/resource.h>
int
getrusage (int processes, struct rusage *rusage)
{
return _sys_call2 (SYS_getrusage, (int) processes, (long) rusage);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <unistd.h>
uid_t
getuid ()
{
return _sys_call (SYS_getuid);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <stdarg.h>
#include <sys/ioctl.h>
int
ioctl (int filedes, unsigned long command, ...)
{
va_list ap;
va_start (ap, command);
int data = va_arg (ap, int);
int r = _sys_call3 (SYS_ioctl, (int) filedes, (long) command, (int) data);
va_end (ap);
return r;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
int
link (char const *old_name, char const *new_name)
{
return _sys_call2 (SYS_link, (long) old_name, (long) new_name);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <sys/stat.h>
int
lstat (char const *file_name, struct stat *statbuf)
{
return _sys_call2 (SYS_lstat, (long) file_name, (long) statbuf);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <sys/stat.h>
int
mkdir (char const *file_name, mode_t mode)
{
return _sys_call2 (SYS_mkdir, (long) file_name, (long) mode);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <sys/stat.h>
int
mknod (char const *file_name, mode_t mode, dev_t dev)
{
return _sys_call3 (SYS_mknod, (long) file_name, (long) mode, (long) dev);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <time.h>
#include <sys/time.h>
int
nanosleep (struct timespec const *requested_time, struct timespec const *remaining)
{
return _sys_call2 (SYS_nanosleep, (long) requested_time, (long) remaining);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <unistd.h>
int
pipe (int filedes[2])
{
return _sys_call1 (SYS_pipe, (long) filedes);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <sys/stat.h>
ssize_t
readlink (char const *file_name, char *buffer, size_t size)
{
return _sys_call3 (SYS_readlink, (long) file_name, (long) buffer, (long) size);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <unistd.h>
int
rename (char const *old_name, char const *new_name)
{
return _sys_call2 (SYS_rename, (long) old_name, (long) new_name);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <unistd.h>
int
setgid (gid_t newgid)
{
return _sys_call1 (SYS_setgid, (long) newgid);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <sys/time.h>
#include <unistd.h>
int
setitimer (int which, struct itimerval const *new, struct itimerval *old)
{
return _sys_call3 (SYS_setitimer, (long) which, (long) new, (long) old);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <unistd.h>
int
setuid (uid_t newuid)
{
return _sys_call1 (SYS_setuid, (long) newuid);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2022,2023 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <unistd.h>
#include <signal.h>
#if __i386__
#else
void
_restorer_for_siginfo (void)
{
_sys_call (SYS_rt_sigreturn);
}
#endif
sighandler_t
signal (int signum, sighandler_t action)
{
#if __i386__
return (sighandler_t) _sys_call2 (SYS_signal, signum, (long) action);
#else
static struct sigaction setup_action = { 0 };
static struct sigaction old = { 0 };
unsigned short bitindex;
unsigned short itembitcount;
setup_action.sa_handler = action;
/* The sa_restorer is not used. Reason:
* The kernel uses its own restorer anyway by pretending we gave one on
our side (no, really).
glibc still has a custom restorer because they want to be able to
backtrace. gdb has a second part that detects a specific instruction
sequence and then fixes up the backtrace. Since we don't use that
specific instruction sequence and also the non-siginfo restorer
is complicated (pops foreign item off the stack--so needs inline
assembly) and we don't support great debuggability in other places,
I've decided to take the easy way out for now. */
/*setup_action.sa_restorer = _restorer_for_siginfo;*/
bitindex = signum - 1;
itembitcount = 8 * sizeof (setup_action.sa_mask.items[0]);
setup_action.sa_mask.items[bitindex / itembitcount] = 1UL << (bitindex % itembitcount);
old.sa_handler = SIG_DFL;
setup_action.sa_flags = SA_RESTART;
#if __x86_64__
/* Tell kernel that we use a restorer, on penalty of segfault. */
setup_action.sa_flags |= SA_RESTORER;
#endif
int r = _sys_call4 (SYS_rt_sigaction, signum, (long) &setup_action, (long) &old, sizeof (sigset_t));
if (r)
return 0;
return old.sa_handler;
#endif
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <signal.h>
#include <unistd.h>
int
sigprocmask (int how, sigset_t const *set, sigset_t * oldset)
{
#if __i386__
return _sys_call3 (SYS_sigprocmask, (long) how, (long) set, (long) oldset);
#else
return _sys_call3 (SYS_rt_sigprocmask, (long) how, (long) set, (long) oldset);
#endif
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <unistd.h>
int
symlink (char const *old_name, char const *new_name)
{
return _sys_call2 (SYS_symlink, (long) old_name, (long) new_name);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mes/lib-mini.h"
#define SYS_exit "0x01"
// *INDENT-OFF*
void
_exit (int code)
{
asm (
"mov $"SYS_exit",%%eax\n\t"
"mov %0,%%ebx\n\t"
"int $0x80\n\t"
: // no outputs "=" (r)
: "rm" (code)
: "eax", "ebx"
);
// not reached
// _exit (0); // tcc has a problem with this
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include <linux/x86/syscall.h>
// *INDENT-OFF*
long
__sys_call (long sys_call)
{
long r;
asm (
"mov %1,%%eax\n\t"
"int $0x80\n\t"
"mov %%eax,%0\n\t"
: "=r" (r)
: "rm" (sys_call)
: "eax"
);
return r;
}
long
__sys_call1 (long sys_call, long one)
{
long r;
asm (
"mov %1,%%eax\n\t"
"mov %2,%%ebx\n\t"
"int $0x80\n\t"
"mov %%eax,%0\n\t"
: "=r" (r)
: "rm" (sys_call), "rm" (one)
: "eax", "ebx"
);
return r;
}
long
__sys_call2 (long sys_call, long one, long two)
{
long r;
asm (
"mov %1,%%eax\n\t"
"mov %2,%%ebx\n\t"
"mov %3,%%ecx\n\t"
"int $0x80\n\t"
"mov %%eax,%0\n\t"
: "=r" (r)
: "rm" (sys_call), "rm" (one), "rm" (two)
: "eax", "ebx", "ecx"
);
return r;
}
long
__sys_call3 (long sys_call, long one, long two, long three)
{
long r;
asm (
"mov %2,%%ebx\n\t"
"mov %3,%%ecx\n\t"
"mov %4,%%edx\n\t"
"mov %1,%%eax\n\t"
"int $0x80\n\t"
"mov %%eax,%0\n\t"
: "=r" (r)
: "rm" (sys_call), "rm" (one), "rm" (two), "rm" (three)
: "eax", "ebx", "ecx", "edx"
);
return r;
}
long
__sys_call4 (long sys_call, long one, long two, long three, long four)
{
long r;
asm (
"mov %2,%%ebx\n\t"
"mov %3,%%ecx\n\t"
"mov %4,%%edx\n\t"
"mov %5,%%esi\n\t"
"mov %1,%%eax\n\t"
"int $0x80\n\t"
"mov %%eax,%0\n\t"
: "=r" (r)
: "rm" (sys_call), "rm" (one), "rm" (two), "rm" (three), "rm" (four)
: "eax", "ebx", "ecx", "edx", "esi"
);
return r;
}
// *INDENT-ON*
long
_sys_call (long sys_call)
{
long r = __sys_call (sys_call);
if (r < 0)
{
errno = -r;
r = -1;
}
else
errno = 0;
return r;
}
long
_sys_call1 (long sys_call, long one)
{
long r = __sys_call1 (sys_call, one);
if (r < 0)
{
errno = -r;
r = -1;
}
else
errno = 0;
return r;
}
long
_sys_call2 (long sys_call, long one, long two)
{
long r = __sys_call2 (sys_call, one, two);
if (r < 0)
{
errno = -r;
r = -1;
}
else
errno = 0;
return r;
}
long
_sys_call3 (long sys_call, long one, long two, long three)
{
long r = __sys_call3 (sys_call, one, two, three);
if (r < 0)
{
errno = -r;
r = -1;
}
else
errno = 0;
return r;
}
long
_sys_call4 (long sys_call, long one, long two, long three, long four)
{
long r = __sys_call4 (sys_call, one, two, three, four);
if (r < 0)
{
errno = -r;
r = -1;
}
else
errno = 0;
return r;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2016,2017,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mes/lib-mini.h"
#define SYS_write "0x04"
// *INDENT-OFF*
ssize_t
_write (int filedes, void const *buffer, size_t size)
{
long r;
asm (
"mov $"SYS_write",%%eax\n\t"
"mov %1,%%ebx\n\t"
"mov %2,%%ecx\n\t"
"mov %3,%%edx\n\t"
"int $0x80\n\t"
"mov %%eax,%0\n\t"
: "=r" (r)
: "rm" (filedes), "rm" (buffer), "rm" (size)
: "eax", "ebx", "ecx", "edx"
);
return r;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <math.h>
double
ceil (double number)
{
long i = number + 0.9999;
number = i;
return number;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <math.h>
double
fabs (double number)
{
if (number < 0)
return -number;
return number;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <math.h>
double
floor (double number)
{
long i = number;
number = i;
return number;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <stdio.h>
char *
fdgets (char *s, int count, int fd)
{
int c = 0;
char *p = s;
while (--count > 0 && c != '\n')
{
c = fdgetc (fd);
if (c == EOF)
break;
*p++ = c;
}
if (p == s && (c == EOF || count == -1))
return 0;
*p = 0;
return s;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <unistd.h>
#include <sys/time.h>
unsigned int
alarm (unsigned int seconds)
{
#if !__MESC__
struct itimerval old;
struct itimerval new;
new.it_interval.tv_usec = 0;
new.it_interval.tv_sec = 0;
new.it_value.tv_usec = 0;
new.it_value.tv_sec = (long int) seconds;
if (setitimer (ITIMER_REAL, &new, &old) < 0)
return 0;
return old.it_value.tv_sec;
#endif
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <stdarg.h>
#include <unistd.h>
int
vexec (char const *file_name, va_list ap)
{
char *arg = va_arg (ap, char *);
char *argv[1000]; // POSIX minimum 4096
int i = 0;
argv[i++] = (char *) file_name;
while (arg)
{
argv[i++] = arg;
arg = va_arg (ap, char *);
if (__mes_debug () > 2)
{
eputs ("arg[");
eputs (itoa (i));
eputs ("]: ");
eputs (argv[i - 1]);
eputs ("\n");
}
}
argv[i] = 0;
int r = execv (file_name, argv);
va_end (ap);
return r;
}
int
execl (char const *file_name, char const *arg, ...)
{
va_list ap;
int r;
va_start (ap, arg);
if (__mes_debug () > 2)
{
eputs ("execl ");
eputs (file_name);
eputs ("\n");
}
r = vexec (file_name, ap);
va_end (ap);
return r;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
#include <stdarg.h>
#include <string.h>
int
execlp (char const *file_name, char const *arg, ...)
{
va_list ap;
int r;
va_start (ap, arg);
if (!strchr (file_name, '/'))
file_name = search_path (file_name);
if (__mes_debug () > 2)
{
eputs ("execlp ");
eputs (file_name ? file_name : "0");
eputs ("\n");
}
if (!file_name)
{
errno = ENOENT;
return -1;
}
r = vexec (file_name, ap);
va_end (ap);
return r;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <unistd.h>
char *
mktemp (char *template)
{
char *p = strchr (template, '\0');
int q = (long) template;
*--p = ((unsigned char) (q >> 4)) % 26 + 'a';
*--p = ((unsigned char) (q >> 8)) % 26 + 'a';
*--p = ((unsigned char) (q >> 12)) % 26 + 'a';
*--p = ((unsigned char) (q >> 16)) % 26 + 'a';
*--p = ((unsigned char) (q >> 20)) % 26 + 'a';
*--p = ((unsigned char) (q >> 24)) % 26 + 'a';
return template;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <unistd.h>
#include <stdlib.h>
#if __SBRK_CHAR_PTRDIFF
char *
sbrk (ptrdiff_t)
#else
void *
sbrk (intptr_t delta)
#endif
{
if (delta >= 0)
return malloc (delta);
return __brk;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
unsigned int
sleep (unsigned int seconds)
{
struct timespec requested_time;
struct timespec remaining;
requested_time.tv_sec = seconds;
requested_time.tv_nsec = 0;
return nanosleep (&requested_time, &remaining);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <string.h>
#include <unistd.h>
void
unsetenv (char const *name)
{
int length = strlen (name);
char **p = environ;
while (*p)
{
if (!strncmp (name, *p, length) && *(*p + length) == '=')
{
char **q = p;
q[0] = q[1];
while (*q++)
q[0] = q[1];
}
p++;
}
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
#include <stdio.h>
void
clearerr (FILE * stream)
{
errno = 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
int
feof (FILE * stream)
{
int c = fgetc (stream);
if (c != EOF)
ungetc (c, stream);
return c == EOF;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <stdio.h>
char *
fgets (char *s, int count, FILE * stream)
{
return fdgets (s, count, (int) (long) stream);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
int
fileno (FILE * stream)
{
return (int) (long) stream;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
FILE *
freopen (char const *file_name, char const *opentype, FILE * stream)
{
fclose (stream);
return fopen (file_name, opentype);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <stdarg.h>
#include <stdio.h>
int
fscanf (FILE * stream, char const *template, ...)
{
va_list ap;
va_start (ap, template);
int r = vfscanf (stream, template, ap);
va_end (ap);
return r;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
void
perror (char const *message)
{
fprintf (stderr, "%s: %s\n", strerror (errno), message);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018,2019,2023 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
int
vfscanf (FILE * stream, char const *template, va_list ap)
{
char p = fgetc (stream);
char const *t = template;
int count = 0;
while (*t && p != EOF)
if (*t != '%')
{
t++;
p = fgetc (stream);
}
else
{
t++;
char c = *t;
if (c == 'l')
c = *++t;
switch (c)
{
case '%':
{
p = fgetc (stream);
break;
}
case 'c':
{
char *c = va_arg (ap, char *);
*c = p;
p = fgetc (stream);
count++;
break;
}
case 'd':
case 'i':
case 'u':
{
int *d = va_arg (ap, int *);
char buf[20];
char *q = buf;
if (p == '+' || p == '-')
{
*q++ = p;
p = fgetc (stream);
}
while (isdigit (p))
{
*q++ = p;
p = fgetc (stream);
}
ungetc (p, stream);
*q = 0;
q = buf;
*d = abtol ((char const**)&q, 10);
count++;
break;
}
case 'e':
case 'f':
case 'g':
case 'E':
case 'G':
{
float *f = va_arg (ap, float *);
char buf[20];
char *q = buf;
if (p == '+' || p == '-')
{
*q++ = p;
p = fgetc (stream);
}
while (isdigit (p))
{
*q++ = p;
p = fgetc (stream);
}
ungetc (p, stream);
*q = 0;
q = buf;
*f = strtod (q, &q);
count++;
break;
}
default:
{
eputs ("vsscanf: not supported: %:");
eputc (c);
eputs ("\n");
t++;
p = fgetc (stream);
}
}
t++;
}
va_end (ap);
return count;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <signal.h>
void
abort (void)
{
if (raise (SIGABRT) < 0) /* could not raise SIGABRT */
{
/* Fail in any way possible */
unsigned char* x = (unsigned char*) 0;
*x = 2;
}
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
int
abs (int x)
{
if (x < 0)
return -x;
return x;
}
/* alloca.c -- allocate automatically reclaimed memory
(Mostly) portable public-domain implementation -- D A Gwyn
Taken from GNU binutils 2.10.1.
Minor changes
Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
This implementation of the PWB library alloca function,
which is used to allocate space off the run-time stack so
that it is automatically reclaimed upon procedure exit,
was inspired by discussions with J. Q. Johnson of Cornell.
There are some preprocessor constants that can
be defined when compiling for your specific system, for
improved efficiency; however, the defaults should be okay.
The general concept of this implementation is to keep
track of all alloca-allocated blocks, and reclaim any
that are found to be deeper in the stack than the current
invocation. This heuristic does not reclaim storage as
soon as it becomes invalid, but it will do so eventually.
As a special case, alloca(0) reclaims storage without
allocating any. It is a good idea to use alloca(0) in
your main control loop, etc. to force garbage collection. */
#include <stdlib.h>
#include <unistd.h>
#define ALIGN_SIZE 4
#define ADDRESS_FUNCTION(arg) &(arg)
#define STACK_DIR -1
union alloca_header
{
char align[ALIGN_SIZE]; /* To force sizeof(union alloca_header). */
struct
{
union alloca_header *next; /* For chaining headers. */
char *deep; /* For stack depth measure. */
} h;
};
static union alloca_header *last_alloca_header = NULL; /* -> last alloca header. */
/* Return a void * to at least SIZE bytes of storage,
which will be automatically reclaimed upon exit from
the procedure that called alloca. Originally, this space
was supposed to be taken from the current stack frame of the
caller, but that method cannot be made to work for some
implementations of C, for example under Gould's UTX/32. */
void *
alloca (size_t size)
{
char probe; /* Probes stack depth: */
char *depth = ADDRESS_FUNCTION (probe);
/* Reclaim garbage, defined as all alloca'd storage that
was allocated from deeper in the stack than currently. */
{
union alloca_header *hp; /* Traverses linked list. */
for (hp = last_alloca_header; hp != NULL;)
if ((STACK_DIR > 0 && hp->h.deep > depth) || (STACK_DIR < 0 && hp->h.deep < depth))
{
union alloca_header *np = hp->h.next;
free ((void *) hp); /* Collect garbage. */
hp = np; /* -> next header. */
}
else
break; /* Rest are not deeper. */
last_alloca_header = hp; /* -> last valid storage. */
}
if (size == 0)
return NULL; /* No allocation required. */
/* Allocate combined header + user data storage. */
{
void *new = malloc (sizeof (union alloca_header) + size);
/* Address of header. */
if (new == 0)
abort ();
((union alloca_header *) new)->h.next = last_alloca_header;
((union alloca_header *) new)->h.deep = depth;
last_alloca_header = (union alloca_header *) new;
/* User storage begins just after header. */
return (void *) ((char *) new + sizeof (union alloca_header));
}
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <stdlib.h>
int
atexit (void (*function) (void))
{
__call_at_exit = function;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
double
atof (char const *string)
{
char const *p = string;
return abtod (&p, 0);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
int
atol (char const *s)
{
return atoi (s);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
int
__exit (int status)
{
exit (status);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2023 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
size_t
mbstowcs (wchar_t * wstring, char const *string, size_t size)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("mbstowcs stub\n");
stub = 1;
strcpy ((char*)wstring, string);
return strlen (string);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
int
bcmp (void const *s1, void const *s2, size_t size)
{
return memcmp (s1, s2, size);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
void
bcopy (void const *src, void *dest, size_t n)
{
memmove (dest, src, n);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <string.h>
#if BZERO_INT
int
#else
void
#endif
bzero (void *block, size_t size)
{
#if BZERO_INT
return (int) (long)
#endif
memset (block, 0, size);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#if INDEX_INT
int
#else
char *
#endif
index (char const *s, int c)
{
return strchr (s, c);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#if INDEX_INT
int
#else
char *
#endif
rindex (char const *s, int c)
{
return strrchr (s, c);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
size_t
strcspn (char const *string, char const *stopset)
{
char *p = (char *) string;
while (*p)
if (strchr (stopset, *p))
break;
else
p++;
return p - string;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <string.h>
char *
strdup (char const *s)
{
size_t length = strlen (s) + 1;
char *p = (char *) malloc (length);
if (p)
return memcpy (p, s, length);
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <string.h>
char *sys_errlist[] = {
"error 00",
"error 01",
"error 02",
"error 03",
"error 04",
"error 05",
"error 06",
"error 07",
"error 08",
"error 09",
"error 10",
"error 11",
"error 12",
"error 13",
"error 14",
"error 15",
"error 16",
"error 17",
"error 18",
"error 19",
"error 20",
"error 21",
"error 22",
"error 23",
"error 24",
"error 25",
"error 26",
"error 27",
"error 28",
"error 29",
"error 30",
"error 31",
"error 32",
"error 33",
"error 34",
"error 35",
"error 36",
"error 37",
"error 38",
"error 39",
};
int sys_nerr = 39;
char *
strerror (int errnum)
{
if (__mes_debug ())
{
eputs ("strerror errnum=");
eputs (itoa (errnum));
eputs ("\n");
}
if (errnum > 0 && errnum <= sys_nerr)
return sys_errlist[errnum];
return "sterror: unknown error";
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
char *
strncat (char *to, char const *from, size_t size)
{
if (size == 0)
return to;
char *p = strchr (to, '\0');
while (*from && size-- > 0)
*p++ = *from++;
*p = 0;
return to;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
char *
strpbrk (char const *string, char const *stopset)
{
char *p = (char *) string;
while (*p)
if (strchr (stopset, *p))
break;
else
p++;
return p;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
size_t
strspn (char const *string, char const *skipset)
{
char *p = (char *) string;
while (*p)
if (!strchr (skipset, *p))
break;
else
p++;
return p - string;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <math.h>
double
atan2 (double x, double y)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("atan2 stub\n");
stub = 1;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <stdlib.h>
void *
#if __MESC__
bsearch (void const *key, void const *array, size_t count, size_t size, void (*compare) ())
#else
bsearch (void const *key, void const *array, size_t count, size_t size, comparison_fn_t compare)
#endif
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("bsearch stub\n");
stub = 1;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
#include <unistd.h>
int
chown (char const *file_name, uid_t owner, gid_t group)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("chown stub\n");
stub = 1;
errno = 0;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
int
__cleanup ()
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("__cleanup stub\n");
stub = 1;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <math.h>
double
cos (double x)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("cos stub\n");
stub = 1;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
#include <time.h>
char *
ctime (time_t const *time)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("ctime stub\n");
stub = 1;
errno = 0;
return "now";
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <math.h>
double
exp (double x)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("exp stub\n");
stub = 1;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
#include <stdio.h>
int
fpurge (FILE * stream)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("fpurge stub\n");
stub = 1;
errno = 0;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
#include <stdio.h>
size_t
freadahead (FILE * fp)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("freadahead stub\n");
stub = 1;
errno = 0;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
double
frexp (int x)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("frexp stub\n");
stub = 1;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
#include <grp.h>
struct group *
getgrgid (gid_t gid)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("getgrid stub\n");
static char *groups[2] = { "root", 0 };
#if SYSTEM_LIBC
static struct group root = { "root", 0, 0 };
root.gr_mem = groups;
#else
static struct group root = { "root", 0, groups };
#endif
stub = 1;
errno = 0;
return &root;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
#include <grp.h>
struct group *
getgrnam (char const *name)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("getgrid stub\n");
static char *groups[2] = { "root", 0 };
#if SYSTEM_LIBC
static struct group root = { "root", 0, 0 };
root.gr_mem = groups;
#else
static struct group root = { "root", 0, groups };
#endif
stub = 1;
errno = 0;
return &root;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
#include <unistd.h>
char *
getlogin (void)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("getlogin stub\n");
stub = 1;
errno = 0;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
#include <unistd.h>
int
getpgid (pid_t pid)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("getpgid stub\n");
stub = 1;
errno = 0;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
#include <unistd.h>
pid_t
getpgrp (void)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("getpgrp stub\n");
stub = 1;
errno = 0;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
#include <pwd.h>
struct passwd *
getpwnam (const char *name)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("getpwnam stub\n");
stub = 1;
errno = 0;
static struct passwd root = { "root", "*", 0, 0, "", "/root", "/bin/sh" };
return &root;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
#include <pwd.h>
struct passwd *
getpwuid (uid_t uid)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("getpwuid stub\n");
stub = 1;
errno = 0;
static struct passwd root = { "root", "*", 0, 0, "", "/root", "/bin/sh" };
return &root;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>
struct tm *
gmtime (time_t const *time)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("gmtime stub\n");
stub = 1;
errno = 0;
return localtime (time);
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <math.h>
double
log (double x)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("log stub\n");
stub = 1;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
#include <time.h>
time_t
mktime (struct tm *broken_time)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("mktime stub\n");
stub = 1;
errno = 0;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <math.h>
double
modf (double value, double *integer_part)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("modf stub\n");
stub = 1;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
int
pclose (int x)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("pclose stub\n");
stub = 1;
errno = 0;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
int
popen (int x)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("popen stub\n");
stub = 1;
errno = 0;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <math.h>
double
pow (double base, double power)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("pow stub\n");
stub = 1;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
int
rand (void)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("rand stub\n");
stub = 1;
errno = 0;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
int
rewind (int x)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("rewind stub\n");
stub = 1;
errno = 0;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
int
setbuf (int x)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("setbuf stub\n");
stub = 1;
errno = 0;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
void
setgrent (void)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("setgrent stub\n");
stub = 1;
errno = 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <locale.h>
char *
setlocale (int category, char const *locale)
{
return "C";
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <stdio.h>
int
setvbuf (FILE * stream, char *buf, int mode, size_t size)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("setvbuf stub\n");
stub = 1;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <signal.h>
int
sigaddset (sigset_t * set, int signum)
{
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <signal.h>
int
sigblock (int mask)
{
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <signal.h>
int
sigdelset (sigset_t * set, int signum)
{
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
int
sigsetmask (int x)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("sigsetmask stub\n");
stub = 1;
errno = 0;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <math.h>
double
sin (double x)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("sin stub\n");
stub = 1;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
int
sys_siglist (int x)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("sys_siglist stub\n");
stub = 1;
errno = 0;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
int
system (int x)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("system stub\n");
stub = 1;
errno = 0;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <math.h>
double
sqrt (double x)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("sqrt stub\n");
stub = 1;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <time.h>
size_t
strftime (char *s, size_t size, char const *template, struct tm const *brokentime)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("strftime stub\n");
stub = 1;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <sys/times.h>
#include <sys/time.h>
clock_t
times (struct tms *buffer)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("times stub\n");
stub = 1;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
#include <unistd.h>
char *
ttyname (int filedes)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("ttyname stub\n");
stub = 1;
errno = 0;
if (isatty (filedes))
return "/dev/tty0";
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
int
umask (int x)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("umask stub\n");
stub = 1;
errno = 0;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#include <errno.h>
int
utime (int x)
{
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("utime stub\n");
errno = 0;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <setjmp.h>
#include <stdlib.h>
void
longjmp (jmp_buf env, int val)
{
val = val == 0 ? 1 : val;
// *INDENT-OFF*
asm (
"mov 0x8(%ebp),%ebp\n\t" // env*
"mov 0x4(%ebp),%ebx\n\t" // env->__pc
"mov 0x8(%ebp),%esp\n\t" // env->__sp
"mov 0x0(%ebp),%ebp\n\t" // env->__bp
"jmp *%ebx\n\t" // jmp *PC
);
// *INDENT-ON*
// not reached
exit (42);
}
int
setjmp (jmp_buf env)
{
long *p = (long *) &env;
env[0].__bp = p[-2];
env[0].__pc = p[-1];
env[0].__sp = (long) &env;
return 0;
}
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
* Copyright © 2021 Paul Dersey <pdersey@gmail.com>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib-mini.h>
//int main (int argc, char *argv[], char *envp[]);
// *INDENT-OFF*
void
_start ()
{
asm (
"mov $0,%%eax\n\t"
"mov %%eax,%0\n"
: "=r" (__stdin)
: //no inputs ""
);
asm (
"mov $1,%%eax\n\t"
"mov %%eax,%0\n"
: "=r" (__stdout)
: //no inputs ""
);
asm (
"mov $2,%%eax\n\t"
"mov %%eax,%0\n"
: "=r" (__stderr)
: //no inputs ""
);
asm (
"mov %%ebp,%%eax\n\t"
"add $4,%%eax\n\t"
"mov (%%eax),%%eax\n\t"
"add $3,%%eax\n\t"
"shl $2,%%eax\n\t"
"add %%ebp,%%eax\n\t"
"mov %%eax,%0\n\t"
"push %%eax\n\t"
: "=r" (environ)
: //no inputs ""
);
asm (
"mov %ebp,%eax\n\t"
"add $8,%eax\n\t"
"push %eax\n\t"
"mov %ebp,%eax\n\t"
"add $4,%eax\n\t"
"mov (%eax),%eax\n\t"
"push %eax\n\t"
"call main\n\t"
"mov %eax,%ebx\n\t"
"mov $1,%eax\n\t"
"int $0x80\n\t"
"hlt \n\t"
);
}
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */
/* -*-comment-start: "//";comment-end:""-*- * GNU Mes --- Maxwell Equations of Software * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> * * This file is part of GNU Mes. * * GNU Mes is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * GNU Mes is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Mes. If not, see <http://www.gnu.org/licenses/>. */
/* -*-comment-start: "//";comment-end:""-*-
* GNU Mes --- Maxwell Equations of Software
* Copyright © 2017,2018,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
* Copyright © 2021 Danny Milosavljevic <dannym@scratchpost.org>
*
* This file is part of GNU Mes.
*
* GNU Mes is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* GNU Mes is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Mes. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mes/lib.h>
#if HAVE_LONG_LONG
long long
__divdi3 (long long a, long long b)
#else
long
__divdi3 (long a, long b)
#endif
{
#if !__TINYC__
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("__divdi3 stub\n");
stub = 1;
#endif
// FIXME: Actually use long long
long ai = a;
long bi = b;
#if __arm__ && __TINYC__
return __mesabi_idiv (ai, bi);
#else
return ai / bi;
#endif
}
#if HAVE_LONG_LONG
long long
__moddi3 (long long a, long long b)
#else
long
__moddi3 (long a, long b)
#endif
{
#if !__TINYC__
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("__moddi3 stub\n");
stub = 1;
#endif
// FIXME: Actually use long long
long ai = a;
long bi = b;
#if __arm__ && __TINYC__
return __mesabi_imod (ai, bi);
#else
return ai % bi;
#endif
}
#if HAVE_LONG_LONG
unsigned long long
__udivdi3 (unsigned long long a, unsigned long long b)
#else
unsigned long
__udivdi3 (unsigned long a, long ah, unsigned long b)
#endif
{
#if !__TINYC__
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("__udivdi3 stub\n");
stub = 1;
#endif
// FIXME: Actually use long long
unsigned long ai = a;
unsigned long bi = b;
if (!b)
return 0;
#if __arm__ && __TINYC__
return __mesabi_udiv (ai, bi);
#else
return ai / bi;
#endif
}
#if HAVE_LONG_LONG
unsigned long long
__umoddi3 (unsigned long long a, unsigned long long b)
#else
unsigned long
__umoddi3 (unsigned long a, long ah, unsigned long b)
#endif
{
#if !__TINYC__
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("__umoddi3 stub\n");
stub = 1;
#endif
// FIXME: Actually use long long
unsigned long ai = a;
unsigned long bi = b;
#if __arm__ && __TINYC__
return __mesabi_umod (ai, bi);
#else
return ai % bi;
#endif
}
#if HAVE_LONG_LONG
unsigned long long
__lshrdi3 (unsigned long long a, long b)
#else
unsigned long
__lshrdi3 (unsigned long a, long ah, long b)
#endif
{
#if !__TINYC__
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("__lshrdi3 stub\n");
stub = 1;
return a >> b;
#else // __TINYC__
for (int i = 0; i < b; i++)
#if __arm__
a = __mesabi_udiv (a, 2); // I sure hope that doesn't endless recurse because of the optimizations in __mesabi_udiv that shift
#else // !__arm__
a /= 2;
#endif // !__arm__
return a;
#endif // __TINYC__
}
#if HAVE_LONG_LONG
long long
__ashldi3 (long long a, long b)
#else
long
__ashldi3 (long a, long ah, long b)
#endif
{
#if !__TINYC__
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("__ashldi3 stub\n");
stub = 1;
return a << b;
#else // __TINYC__
for (int i = 0; i < b; i++)
a += a;
return a;
#endif // __TINYC__
}
#if HAVE_LONG_LONG
long long
__ashrdi3 (long long a, long b)
#else
long
__ashrdi3 (long a, long ah, long b)
#endif
{
#if !__TINYC__
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("__ashrdi3 stub\n");
stub = 1;
return a >> b;
#else // __TINYC__
for (int i = 0; i < b; i++)
#if __arm__
a = __mesabi_idiv (a, 2); // I sure hope that doesn't endless recurse because of the optimizations in __mesabi_udiv that shift
#else // !__arm__
a /= 2;
#endif // !__arm__
return a;
#endif // __TINYC__
}
#if HAVE_FLOAT_STUB || HAVE_FLOAT
double
__attribute__((weak))
#if HAVE_LONG_LONG && HAVE_FLOAT
__floatundidf (unsigned long long a)
#else
__floatundidf (unsigned long a)
#endif
{
#if !__TINYC__
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("__floatundidf stub\n");
stub = 1;
#endif
return 0;
}
#if HAVE_LONG_LONG && HAVE_FLOAT
long double
__attribute__((weak))
__floatundixf (unsigned long long a)
#else
double
__attribute__((weak))
__floatundixf (unsigned long a)
#endif
{
#if !__TINYC__
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("__floatundixf stub\n");
stub = 1;
#endif
return 0;
}
#if HAVE_LONG_LONG
unsigned long long
#else
unsigned long
#endif
__attribute__((weak))
__fixunsxfdi (double a1)
{
#if !__TINYC__
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("__fixunsxfdi stub\n");
stub = 1;
#endif
return 0;
}
#if __TINYC__ == 9226
long
#elif __TINYC__
int
#else // !__TINYCC__
long long
#endif // !__TINYCC__
__attribute__((weak))
__fixdfdi (double a1)
{
#if !__TINYC__
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("__fixdfdi stub\n");
stub = 1;
#endif
return 0;
}
#if HAVE_LONG_LONG
unsigned long long
#else
unsigned long
#endif
__attribute__((weak))
__fixxfdi (double a1)
{
#if !__TINYC__
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("__fixxfdi stub\n");
stub = 1;
#endif
return 0;
}
#if HAVE_LONG_LONG
long long
#else
long
#endif
__attribute__((weak))
__fixsfdi (double a1)
{
#if !__TINYC__
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("__fixsfdi stub\n");
stub = 1;
#endif
return 0;
}
double
__attribute__((weak))
__fixunsdfdi (double num, double den)
{
#if !__TINYC__
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("__fixunsdfdi stub\n");
stub = 1;
#endif
return 0;
}
#endif // HAVE_FLOAT_STUB || HAVE_FLOAT
int
__attribute__((weak))
__fixunsdfsi (int a, int b)
{
#if !__TINYC__
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("__fixunsdfsi stub\n");
stub = 1;
#endif
return 0;
}
#if __arm__
int
__attribute__((weak))
__floatdisf (int a, int b)
{
#if !__TINYC__
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("__floatdisf stub\n");
stub = 1;
#endif
return 0;
}
int
__attribute__((weak))
__floatdidf (int a, int b)
{
#if !__TINYC__
static int stub = 0;
if (__mes_debug () && !stub)
eputs ("__floatdidf stub\n");
stub = 1;
#endif
return 0;
}
int
__divsi3 (int num, int den)
{
return __mesabi_idiv (num, den);
}
int
__modsi3 (int num, int den)
{
return __mesabi_imod (num, den);
}
int
__udivsi3 (int num, int den)
{
return __mesabi_udiv (num, den);
}
int
__umodsi3 (int num, int den)
{
return __mesabi_umod (num, den);
}
#endif //__arm__
/* Getopt for GNU.
Copyright (C) 1987, 88, 89, 90, 91, 1992 Free Software Foundation, Inc.
Copyright (C) 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; If not, see <http://www.gnu.org/licenses/>. */
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#if __MESC__
#define static
#endif
/* For communication from `getopt' to the caller.
When `getopt' finds an option that takes an argument,
the argument value is returned here. */
char *optarg = 0;
/* Index in ARGV of the next element to be scanned.
This is used for communication to and from the caller
and for communication between successive calls to `getopt'.
On entry to `getopt', zero means this is the first call; initialize.
When `getopt' returns EOF, this is the index of the first of the
non-option elements that the caller should itself scan.
Otherwise, `optind' communicates from one call to the next
how much of ARGV has been scanned so far. */
int optind = 0;
/* The next char to be scanned in the option-element
in which the last option character we returned was found.
This allows us to pick up the scan where we left off.
If this is zero, or a null string, it means resume the scan
by advancing to the next ARGV-element. */
static char *nextchar;
/* Callers store zero here to inhibit the error message
for unrecognized options. */
int opterr = 1;
/* Handle permutation of arguments. */
/* Describe the part of ARGV that contains non-options that have
been skipped. `first_nonopt' is the index in ARGV of the first of them;
`last_nonopt' is the index after the last of them. */
static int first_nonopt;
static int last_nonopt;
/* Scan elements of ARGV (whose length is ARGC) for option characters
given in OPTSTRING.
If an element of ARGV starts with '-', and is not exactly "-" or "--",
then it is an option element. The characters of this element
(aside from the initial '-') are option characters. If `getopt'
is called repeatedly, it returns successively each of the option characters
from each of the option elements.
If `getopt' finds another option character, it returns that character,
updating `optind' and `nextchar' so that the next call to `getopt' can
resume the scan with the following option character or ARGV-element.
If there are no more option characters, `getopt' returns `EOF'.
Then `optind' is the index in ARGV of the first ARGV-element
that is not an option. (The ARGV-elements have been permuted
so that those that are not options now come last.)
OPTSTRING is a string containing the legitimate option characters.
If an option character is seen that is not listed in OPTSTRING,
return '?' after printing an error message. If you set `opterr' to
zero, the error message is suppressed but we still return '?'.
If a char in OPTSTRING is followed by a colon, that means it wants an arg,
so the following text in the same ARGV-element, or the text of the following
ARGV-element, is returned in `optarg'. Two colons mean an option that
wants an optional arg; if there is text in the current ARGV-element,
it is returned in `optarg', otherwise `optarg' is set to zero.
If OPTSTRING starts with `-' or `+', it requests different methods of
handling the non-option ARGV-elements.
Long-named options begin with `--' instead of `-'.
Their names may be abbreviated as long as the abbreviation is unique
or is an exact match for some defined option. If they have an
argument, it follows the option name in the same ARGV-element, separated
from the option name by a `=', or else the in next ARGV-element.
When `getopt' finds a long-named option, it returns 0 if that option's
`flag' field is nonzero, the value of the option's `val' field
if the `flag' field is zero.
The elements of ARGV aren't really const, because we permute them.
But we pretend they're const in the prototype to be compatible
with other systems.
LONGOPTS is a vector of `struct option' terminated by an
element containing a name which is zero.
LONGIND returns the index in LONGOPT of the long-named option found.
It is only valid when a long-named option has been found by the most
recent call.
If LONG_ONLY is nonzero, '-' as well as '--' can introduce
long-named options. */
int
_getopt_internal (int argc, char *const
*argv, char const *optstring, struct option const *longopts, int *longind, int long_only)
{
int option_index;
optarg = 0;
/* Initialize the internal data when the first call is made.
Start processing options with ARGV-element 1 (since ARGV-element 0
is the program name); the sequence of previously skipped
non-option ARGV-elements is empty. */
if (optind == 0)
{
first_nonopt = last_nonopt = optind = 1;
nextchar = NULL;
}
if (nextchar == NULL || *nextchar == '\0')
{
/* If we have done all the ARGV-elements, stop the scan
and back over any non-options that we skipped and permuted. */
if (optind == argc)
{
/* Set the next-arg-index to point at the non-options
that we previously skipped, so the caller will digest them. */
if (first_nonopt != last_nonopt)
optind = first_nonopt;
return EOF;
}
/* If we have come to a non-option and did not permute it,
either stop the scan or describe it to the caller and pass it by. */
if ((argv[optind][0] != '-' || argv[optind][1] == '\0'))
return EOF;
/* We have found another option-ARGV-element.
Start decoding its characters. */
nextchar = (argv[optind] + 1 + (longopts != NULL && argv[optind][1] == '-'));
}
if (longopts != NULL && ((argv[optind][0] == '-' && (argv[optind][1] == '-' || long_only))))
{
const struct option *p;
char *s = nextchar;
int exact = 0;
int ambig = 0;
const struct option *pfound = NULL;
int indfound;
while (*s && *s != '=')
s++;
/* Test all options for either exact match or abbreviated matches. */
for (p = longopts, option_index = 0; p->name; p++, option_index++)
if (!strncmp (p->name, nextchar, s - nextchar))
{
if (s - nextchar == strlen (p->name))
{
/* Exact match found. */
pfound = p;
indfound = option_index;
exact = 1;
break;
}
else if (pfound == NULL)
{
/* First nonexact match found. */
pfound = p;
indfound = option_index;
}
else
/* Second nonexact match found. */
ambig = 1;
}
if (ambig && !exact)
{
if (opterr)
fprintf (stderr, "%s: option `%s' is ambiguous\n", argv[0], argv[optind]);
nextchar += strlen (nextchar);
optind++;
return '?';
}
if (pfound != NULL)
{
option_index = indfound;
optind++;
if (*s)
{
/* Don't test has_arg with >, because some C compilers don't
allow it to be used on enums. */
if (pfound->has_arg)
optarg = s + 1;
else
{
if (opterr)
{
if (argv[optind - 1][1] == '-')
/* --option */
fprintf (stderr,
"%s: option `--%s' doesn't allow an argument\n", argv[0], pfound->name);
else
/* +option or -option */
fprintf (stderr,
"%s: option `%c%s' doesn't allow an argument\n",
argv[0], argv[optind - 1][0], pfound->name);
}
nextchar += strlen (nextchar);
return '?';
}
}
else if (pfound->has_arg == 1)
{
if (optind < argc)
optarg = argv[optind++];
else
{
if (opterr)
fprintf (stderr, "%s: option `%s' requires an argument\n", argv[0], argv[optind - 1]);
nextchar += strlen (nextchar);
return '?';
}
}
nextchar += strlen (nextchar);
if (longind != NULL)
*longind = option_index;
if (pfound->flag)
{
*(pfound->flag) = pfound->val;
return 0;
}
return pfound->val;
}
/* Can't find it as a long option. If this is not getopt_long_only,
or the option starts with '--' or is not a valid short
option, then it's an error.
Otherwise interpret it as a short option. */
if (!long_only || argv[optind][1] == '-' || strchr (optstring, *nextchar) == NULL)
{
if (opterr)
{
if (argv[optind][1] == '-')
/* --option */
fprintf (stderr, "%s: unrecognized option `--%s'\n", argv[0], nextchar);
else
/* +option or -option */
fprintf (stderr, "%s: unrecognized option `%c%s'\n", argv[0], argv[optind][0], nextchar);
}
nextchar += strlen (nextchar);
optind++;
return '?';
}
}
/* Look at and handle the next option-character. */
{
char c = *nextchar++;
char *temp = strchr (optstring, c);
/* Increment `optind' when we start to process its last character. */
if (*nextchar == '\0')
optind++;
if (temp == NULL || c == ':')
{
if (opterr)
{
if (c < 040 || c >= 0177)
fprintf (stderr, "%s: unrecognized option, character code 0%o\n", argv[0], c);
else
fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c);
}
return '?';
}
if (temp[1] == ':')
{
if (temp[2] == ':')
{
/* This is an option that accepts an argument optionally. */
if (*nextchar != '\0')
{
optarg = nextchar;
optind++;
}
else
optarg = 0;
nextchar = NULL;
}
else
{
/* This is an option that requires an argument. */
if (*nextchar != 0)
{
optarg = nextchar;
/* If we end this ARGV-element by taking the rest as an arg,
we must advance to the next element now. */
optind++;
}
else if (optind == argc)
{
if (opterr)
fprintf (stderr, "%s: option `-%c' requires an argument\n", argv[0], c);
c = '?';
}
else
/* We already incremented `optind' once;
increment it again when taking next ARGV-elt as argument. */
optarg = argv[optind++];
nextchar = NULL;
}
}
return c;
}
}
int
getopt (int argc, char *const *argv, char const *options)
{
return _getopt_internal (argc, argv, options, (const struct option *) 0, (int *) 0, 0);
}
int
getopt_long (int argc, char *const *argv, char const *options,
struct option const *long_options, int *opt_index)
{
return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
}
int
getopt_long_only (int argc, char *const *argv, char const *options,
struct option const *long_options, int *opt_index)
{
return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
}
/* TCC runtime library.
Parts of this code are (c) 2002 Fabrice Bellard
Copyright (C) 1987, 1988, 1992, 1994, 1995 Free Software Foundation, Inc.
This file is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
In addition to the permissions in the GNU General Public License, the
Free Software Foundation gives you unlimited permission to link the
compiled version of this file into combinations with other programs,
and to distribute those combinations without any restriction coming
from the use of this file. (The General Public License restrictions
do apply in other respects; for example, they cover modification of
the file, and distribution when not linked into a combine
executable.)
This file is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
#define W_TYPE_SIZE 32
#define BITS_PER_UNIT 8
typedef int Wtype;
typedef unsigned int UWtype;
typedef unsigned int USItype;
typedef long long DWtype;
typedef unsigned long long UDWtype;
struct DWstruct {
Wtype low, high;
};
typedef union
{
struct DWstruct s;
DWtype ll;
} DWunion;
typedef long double XFtype;
#define WORD_SIZE (sizeof (Wtype) * BITS_PER_UNIT)
#define HIGH_WORD_COEFF (((UDWtype) 1) << WORD_SIZE)
/* the following deal with IEEE single-precision numbers */
#define EXCESS 126
#define SIGNBIT 0x80000000
#define HIDDEN (1 << 23)
#define SIGN(fp) ((fp) & SIGNBIT)
#define EXP(fp) (((fp) >> 23) & 0xFF)
#define MANT(fp) (((fp) & 0x7FFFFF) | HIDDEN)
#define PACK(s,e,m) ((s) | ((e) << 23) | (m))
/* the following deal with IEEE double-precision numbers */
#define EXCESSD 1022
#define HIDDEND (1 << 20)
#define EXPD(fp) (((fp.l.upper) >> 20) & 0x7FF)
#define SIGND(fp) ((fp.l.upper) & SIGNBIT)
#define MANTD(fp) (((((fp.l.upper) & 0xFFFFF) | HIDDEND) << 10) | \
(fp.l.lower >> 22))
#define HIDDEND_LL ((long long)1 << 52)
#define MANTD_LL(fp) ((fp.ll & (HIDDEND_LL-1)) | HIDDEND_LL)
#define PACKD_LL(s,e,m) (((long long)((s)+((e)<<20))<<32)|(m))
/* the following deal with x86 long double-precision numbers */
#define EXCESSLD 16382
#define EXPLD(fp) (fp.l.upper & 0x7fff)
#define SIGNLD(fp) ((fp.l.upper) & 0x8000)
/* only for x86 */
union ldouble_long {
long double ld;
struct {
unsigned long long lower;
unsigned short upper;
} l;
};
union double_long {
double d;
#if 1
struct {
unsigned int lower;
int upper;
} l;
#else
struct {
int upper;
unsigned int lower;
} l;
#endif
long long ll;
};
union float_long {
float f;
unsigned int l;
};
/* XXX: we don't support several builtin supports for now */
#if !defined(TCC_TARGET_X86_64) && !defined(TCC_TARGET_ARM)
/* XXX: use gcc/tcc intrinsic ? */
#if defined(TCC_TARGET_I386)
#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
__asm__ ("subl %5,%1\n\tsbbl %3,%0" \
: "=r" ((USItype) (sh)), \
"=&r" ((USItype) (sl)) \
: "0" ((USItype) (ah)), \
"g" ((USItype) (bh)), \
"1" ((USItype) (al)), \
"g" ((USItype) (bl)))
#define umul_ppmm(w1, w0, u, v) \
__asm__ ("mull %3" \
: "=a" ((USItype) (w0)), \
"=d" ((USItype) (w1)) \
: "%0" ((USItype) (u)), \
"rm" ((USItype) (v)))
#define udiv_qrnnd(q, r, n1, n0, dv) \
__asm__ ("divl %4" \
: "=a" ((USItype) (q)), \
"=d" ((USItype) (r)) \
: "0" ((USItype) (n0)), \
"1" ((USItype) (n1)), \
"rm" ((USItype) (dv)))
#define count_leading_zeros(count, x) \
do { \
USItype __cbtmp; \
__asm__ ("bsrl %1,%0" \
: "=r" (__cbtmp) : "rm" ((USItype) (x))); \
(count) = __cbtmp ^ 31; \
} while (0)
#else
#error unsupported CPU type
#endif
/* most of this code is taken from libgcc2.c from gcc */
static UDWtype __udivmoddi4 (UDWtype n, UDWtype d, UDWtype *rp)
{
DWunion ww;
DWunion nn, dd;
DWunion rr;
UWtype d0, d1, n0, n1, n2;
UWtype q0, q1;
UWtype b, bm;
nn.ll = n;
dd.ll = d;
d0 = dd.s.low;
d1 = dd.s.high;
n0 = nn.s.low;
n1 = nn.s.high;
#if !defined(UDIV_NEEDS_NORMALIZATION)
if (d1 == 0)
{
if (d0 > n1)
{
/* 0q = nn / 0D */
udiv_qrnnd (q0, n0, n1, n0, d0);
q1 = 0;
/* Remainder in n0. */
}
else
{
/* qq = NN / 0d */
if (d0 == 0)
d0 = 1 / d0; /* Divide intentionally by zero. */
udiv_qrnnd (q1, n1, 0, n1, d0);
udiv_qrnnd (q0, n0, n1, n0, d0);
/* Remainder in n0. */
}
if (rp != 0)
{
rr.s.low = n0;
rr.s.high = 0;
*rp = rr.ll;
}
}
#else /* UDIV_NEEDS_NORMALIZATION */
if (d1 == 0)
{
if (d0 > n1)
{
/* 0q = nn / 0D */
count_leading_zeros (bm, d0);
if (bm != 0)
{
/* Normalize, i.e. make the most significant bit of the
denominator set. */
d0 = d0 << bm;
n1 = (n1 << bm) | (n0 >> (W_TYPE_SIZE - bm));
n0 = n0 << bm;
}
udiv_qrnnd (q0, n0, n1, n0, d0);
q1 = 0;
/* Remainder in n0 >> bm. */
}
else
{
/* qq = NN / 0d */
if (d0 == 0)
d0 = 1 / d0; /* Divide intentionally by zero. */
count_leading_zeros (bm, d0);
if (bm == 0)
{
/* From (n1 >= d0) /\ (the most significant bit of d0 is set),
conclude (the most significant bit of n1 is set) /\ (the
leading quotient digit q1 = 1).
This special case is necessary, not an optimization.
(Shifts counts of W_TYPE_SIZE are undefined.) */
n1 -= d0;
q1 = 1;
}
else
{
/* Normalize. */
b = W_TYPE_SIZE - bm;
d0 = d0 << bm;
n2 = n1 >> b;
n1 = (n1 << bm) | (n0 >> b);
n0 = n0 << bm;
udiv_qrnnd (q1, n1, n2, n1, d0);
}
/* n1 != d0... */
udiv_qrnnd (q0, n0, n1, n0, d0);
/* Remainder in n0 >> bm. */
}
if (rp != 0)
{
rr.s.low = n0 >> bm;
rr.s.high = 0;
*rp = rr.ll;
}
}
#endif /* UDIV_NEEDS_NORMALIZATION */
else
{
if (d1 > n1)
{
/* 00 = nn / DD */
q0 = 0;
q1 = 0;
/* Remainder in n1n0. */
if (rp != 0)
{
rr.s.low = n0;
rr.s.high = n1;
*rp = rr.ll;
}
}
else
{
/* 0q = NN / dd */
count_leading_zeros (bm, d1);
if (bm == 0)
{
/* From (n1 >= d1) /\ (the most significant bit of d1 is set),
conclude (the most significant bit of n1 is set) /\ (the
quotient digit q0 = 0 or 1).
This special case is necessary, not an optimization. */
/* The condition on the next line takes advantage of that
n1 >= d1 (true due to program flow). */
if (n1 > d1 || n0 >= d0)
{
q0 = 1;
sub_ddmmss (n1, n0, n1, n0, d1, d0);
}
else
q0 = 0;
q1 = 0;
if (rp != 0)
{
rr.s.low = n0;
rr.s.high = n1;
*rp = rr.ll;
}
}
else
{
UWtype m1, m0;
/* Normalize. */
b = W_TYPE_SIZE - bm;
d1 = (d1 << bm) | (d0 >> b);
d0 = d0 << bm;
n2 = n1 >> b;
n1 = (n1 << bm) | (n0 >> b);
n0 = n0 << bm;
udiv_qrnnd (q0, n1, n2, n1, d1);
umul_ppmm (m1, m0, q0, d0);
if (m1 > n1 || (m1 == n1 && m0 > n0))
{
q0--;
sub_ddmmss (m1, m0, m1, m0, d1, d0);
}
q1 = 0;
/* Remainder in (n1n0 - m1m0) >> bm. */
if (rp != 0)
{
sub_ddmmss (n1, n0, n1, n0, m1, m0);
rr.s.low = (n1 << b) | (n0 >> bm);
rr.s.high = n1 >> bm;
*rp = rr.ll;
}
}
}
}
ww.s.low = q0;
ww.s.high = q1;
return ww.ll;
}
#define __negdi2(a) (-(a))
long long __divdi3(long long u, long long v)
{
int c = 0;
DWunion uu, vv;
DWtype w;
uu.ll = u;
vv.ll = v;
if (uu.s.high < 0) {
c = ~c;
uu.ll = __negdi2 (uu.ll);
}
if (vv.s.high < 0) {
c = ~c;
vv.ll = __negdi2 (vv.ll);
}
w = __udivmoddi4 (uu.ll, vv.ll, (UDWtype *) 0);
if (c)
w = __negdi2 (w);
return w;
}
long long __moddi3(long long u, long long v)
{
int c = 0;
DWunion uu, vv;
DWtype w;
uu.ll = u;
vv.ll = v;
if (uu.s.high < 0) {
c = ~c;
uu.ll = __negdi2 (uu.ll);
}
if (vv.s.high < 0)
vv.ll = __negdi2 (vv.ll);
__udivmoddi4 (uu.ll, vv.ll, (UDWtype *) &w);
if (c)
w = __negdi2 (w);
return w;
}
unsigned long long __udivdi3(unsigned long long u, unsigned long long v)
{
return __udivmoddi4 (u, v, (UDWtype *) 0);
}
unsigned long long __umoddi3(unsigned long long u, unsigned long long v)
{
UDWtype w;
__udivmoddi4 (u, v, &w);
return w;
}
/* XXX: fix tcc's code generator to do this instead */
long long __ashrdi3(long long a, int b)
{
#ifdef __TINYC__
DWunion u;
u.ll = a;
if (b >= 32) {
u.s.low = u.s.high >> (b - 32);
u.s.high = u.s.high >> 31;
} else if (b != 0) {
u.s.low = ((unsigned)u.s.low >> b) | (u.s.high << (32 - b));
u.s.high = u.s.high >> b;
}
return u.ll;
#else
return a >> b;
#endif
}
/* XXX: fix tcc's code generator to do this instead */
unsigned long long __lshrdi3(unsigned long long a, int b)
{
#ifdef __TINYC__
DWunion u;
u.ll = a;
if (b >= 32) {
u.s.low = (unsigned)u.s.high >> (b - 32);
u.s.high = 0;
} else if (b != 0) {
u.s.low = ((unsigned)u.s.low >> b) | (u.s.high << (32 - b));
u.s.high = (unsigned)u.s.high >> b;
}
return u.ll;
#else
return a >> b;
#endif
}
/* XXX: fix tcc's code generator to do this instead */
long long __ashldi3(long long a, int b)
{
#ifdef __TINYC__
DWunion u;
u.ll = a;
if (b >= 32) {
u.s.high = (unsigned)u.s.low << (b - 32);
u.s.low = 0;
} else if (b != 0) {
u.s.high = ((unsigned)u.s.high << b) | ((unsigned)u.s.low >> (32 - b));
u.s.low = (unsigned)u.s.low << b;
}
return u.ll;
#else
return a << b;
#endif
}
#endif /* !__x86_64__ */
/* XXX: fix tcc's code generator to do this instead */
float __floatundisf(unsigned long long a)
{
DWunion uu;
XFtype r;
uu.ll = a;
if (uu.s.high >= 0) {
return (float)uu.ll;
} else {
r = (XFtype)uu.ll;
r += 18446744073709551616.0;
return (float)r;
}
}
double __floatundidf(unsigned long long a)
{
DWunion uu;
XFtype r;
uu.ll = a;
if (uu.s.high >= 0) {
return (double)uu.ll;
} else {
r = (XFtype)uu.ll;
r += 18446744073709551616.0;
return (double)r;
}
}
long double __floatundixf(unsigned long long a)
{
DWunion uu;
XFtype r;
uu.ll = a;
if (uu.s.high >= 0) {
return (long double)uu.ll;
} else {
r = (XFtype)uu.ll;
r += 18446744073709551616.0;
return (long double)r;
}
}
unsigned long long __fixunssfdi (float a1)
{
register union float_long fl1;
register int exp;
register unsigned long l;
fl1.f = a1;
if (fl1.l == 0)
return (0);
exp = EXP (fl1.l) - EXCESS - 24;
l = MANT(fl1.l);
if (exp >= 41)
return (unsigned long long)-1;
else if (exp >= 0)
return (unsigned long long)l << exp;
else if (exp >= -23)
return l >> -exp;
else
return 0;
}
long long __fixsfdi (float a1)
{
long long ret; int s;
ret = __fixunssfdi((s = a1 >= 0) ? a1 : -a1);
return s ? ret : -ret;
}
unsigned long long __fixunsdfdi (double a1)
{
register union double_long dl1;
register int exp;
register unsigned long long l;
dl1.d = a1;
if (dl1.ll == 0)
return (0);
exp = EXPD (dl1) - EXCESSD - 53;
l = MANTD_LL(dl1);
if (exp >= 12)
return (unsigned long long)-1;
else if (exp >= 0)
return l << exp;
else if (exp >= -52)
return l >> -exp;
else
return 0;
}
long long __fixdfdi (double a1)
{
long long ret; int s;
ret = __fixunsdfdi((s = a1 >= 0) ? a1 : -a1);
return s ? ret : -ret;
}
#ifndef TCC_TARGET_ARM
unsigned long long __fixunsxfdi (long double a1)
{
register union ldouble_long dl1;
register int exp;
register unsigned long long l;
dl1.ld = a1;
if (dl1.l.lower == 0 && dl1.l.upper == 0)
return (0);
exp = EXPLD (dl1) - EXCESSLD - 64;
l = dl1.l.lower;
if (exp > 0)
return (unsigned long long)-1;
else if (exp >= -63)
return l >> -exp;
else
return 0;
}
long long __fixxfdi (long double a1)
{
long long ret; int s;
ret = __fixunsxfdi((s = a1 >= 0) ? a1 : -a1);
return s ? ret : -ret;
}
#endif /* !ARM */
7897c2ba182e92765856a9e3ddd2bdfcd69988b3ddc45b3ef932dc3ec041041a /usr/bin/mes-tcc 64bdb36f0048744957454b6ad186d37db4a7fcc392f67bf052b9bc7ec033b963 /usr/bin/boot0-tcc 18af79e0e3c4bfeaced2b0867561bc9fd370cbfcc78860de5a70926374ba7c53 /usr/bin/boot1-tcc d6fa0c2006f6c8fefb4f3750fade14aa884670637098857d06aef3c7a9bd02d8 /usr/bin/boot2-tcc 93b2c1c567f5938435fbe6c0115276f6073bd46a0a92791b164ca2752180caf5 /usr/bin/boot3-tcc a40305e6d2acb3dc64dbc80f64952431f4728eeff03f6e7c91c737cbf6e1bdda /usr/bin/boot4-tcc c56d99e284dfecae047e792b049c0cee022452f13262d6b3c869ec1c9935b276 /usr/bin/tcc 96f93f2d281ee6996767b25fbff4441e8335e7eeaeab060c1d4b698366b277c9 /usr/lib/mes/libc.a 12c07ae103e7e3b390150a79e5c600d88de14e9bb73a066f6342582729ef5a3f /usr/lib/mes/libgetopt.a d1168ee9b528e39f0b40e8d51fb7fa3619c4a5ee928137f7faf6d0879b0916b0 /usr/lib/mes/crt1.o 09d4f9821a2566f7e56381a19259c41bd97f3c5ed83f490705acbfd1139a7736 /usr/lib/mes/crti.o 461ca1494737fab86fe1c1d3addeaf9d0ece413e353abcdea8674db3f700cda3 /usr/lib/mes/crtn.o ac11f09698f092ed76ae40ebcd56cf3f2b903ea1333ef7537a00673dd6f73da7 /usr/lib/mes/tcc/libtcc1.a
https://download.savannah.gnu.org/releases/tinycc/tcc-0.9.27.tar.bz2 de23af78fca90ce32dff2dd45b3432b2334740bb9bb7b05bf60fdbfc396ceb9c
if (ret == 1)
return ar_usage(ret);
if ((fh = fopen(argv[i_lib], "wb")) == NULL)
{
fprintf(stderr, "tcc: ar: can't open file %s \n", argv[i_lib]);
goto the_end;
}
if (ret == 1)
return ar_usage(ret);
// write header
if ((fh = fopen(argv[i_lib], "wb")) == NULL)
{
fprintf(stderr, "tcc: ar: can't open file %s \n", argv[i_lib]);
goto the_end;
}
// write header
/*
* ELF file handling for TCC
*
* Copyright (c) 2001-2004 Fabrice Bellard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "tcc.h"
/* Define this to get some debug output during relocation processing. */
#undef DEBUG_RELOC
/********************************************************/
/* global variables */
ST_DATA Section *text_section, *data_section, *bss_section; /* predefined sections */
ST_DATA Section *common_section;
ST_DATA Section *cur_text_section; /* current section where function code is generated */
#ifdef CONFIG_TCC_ASM
ST_DATA Section *last_text_section; /* to handle .previous asm directive */
#endif
#ifdef CONFIG_TCC_BCHECK
/* bound check related sections */
ST_DATA Section *bounds_section; /* contains global data bound description */
ST_DATA Section *lbounds_section; /* contains local data bound description */
#endif
/* symbol sections */
ST_DATA Section *symtab_section;
/* debug sections */
ST_DATA Section *stab_section, *stabstr_section;
/* XXX: avoid static variable */
static int new_undef_sym = 0; /* Is there a new undefined sym since last new_undef_sym() */
/* special flag to indicate that the section should not be linked to the other ones */
#define SHF_PRIVATE 0x80000000
/* section is dynsymtab_section */
#define SHF_DYNSYM 0x40000000
/* ------------------------------------------------------------------------- */
ST_FUNC void tccelf_new(TCCState *s)
{
/* no section zero */
dynarray_add(&s->sections, &s->nb_sections, NULL);
/* create standard sections */
text_section = new_section(s, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR);
data_section = new_section(s, ".data", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
bss_section = new_section(s, ".bss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE);
common_section = new_section(s, ".common", SHT_NOBITS, SHF_PRIVATE);
common_section->sh_num = SHN_COMMON;
/* symbols are always generated for linking stage */
symtab_section = new_symtab(s, ".symtab", SHT_SYMTAB, 0,
".strtab",
".hashtab", SHF_PRIVATE);
s->symtab = symtab_section;
/* private symbol table for dynamic symbols */
s->dynsymtab_section = new_symtab(s, ".dynsymtab", SHT_SYMTAB, SHF_PRIVATE|SHF_DYNSYM,
".dynstrtab",
".dynhashtab", SHF_PRIVATE);
get_sym_attr(s, 0, 1);
}
#ifdef CONFIG_TCC_BCHECK
ST_FUNC void tccelf_bounds_new(TCCState *s)
{
/* create bounds sections */
bounds_section = new_section(s, ".bounds",
SHT_PROGBITS, SHF_ALLOC);
lbounds_section = new_section(s, ".lbounds",
SHT_PROGBITS, SHF_ALLOC);
}
#endif
ST_FUNC void tccelf_stab_new(TCCState *s)
{
stab_section = new_section(s, ".stab", SHT_PROGBITS, 0);
stab_section->sh_entsize = sizeof(Stab_Sym);
stabstr_section = new_section(s, ".stabstr", SHT_STRTAB, 0);
put_elf_str(stabstr_section, "");
stab_section->link = stabstr_section;
/* put first entry */
put_stabs("", 0, 0, 0, 0);
}
static void free_section(Section *s)
{
tcc_free(s->data);
}
ST_FUNC void tccelf_delete(TCCState *s1)
{
int i;
/* free all sections */
for(i = 1; i < s1->nb_sections; i++)
free_section(s1->sections[i]);
dynarray_reset(&s1->sections, &s1->nb_sections);
for(i = 0; i < s1->nb_priv_sections; i++)
free_section(s1->priv_sections[i]);
dynarray_reset(&s1->priv_sections, &s1->nb_priv_sections);
/* free any loaded DLLs */
#ifdef TCC_IS_NATIVE
for ( i = 0; i < s1->nb_loaded_dlls; i++) {
DLLReference *ref = s1->loaded_dlls[i];
if ( ref->handle )
# ifdef _WIN32
FreeLibrary((HMODULE)ref->handle);
# else
dlclose(ref->handle);
# endif
}
#endif
/* free loaded dlls array */
dynarray_reset(&s1->loaded_dlls, &s1->nb_loaded_dlls);
tcc_free(s1->sym_attrs);
symtab_section = NULL; /* for tccrun.c:rt_printline() */
}
/* save section data state */
ST_FUNC void tccelf_begin_file(TCCState *s1)
{
Section *s; int i;
for (i = 1; i < s1->nb_sections; i++) {
s = s1->sections[i];
s->sh_offset = s->data_offset;
}
/* disable symbol hashing during compilation */
s = s1->symtab, s->reloc = s->hash, s->hash = NULL;
#if defined TCC_TARGET_X86_64 && defined TCC_TARGET_PE
s1->uw_sym = 0;
#endif
}
/* At the end of compilation, convert any UNDEF syms to global, and merge
with previously existing symbols */
ST_FUNC void tccelf_end_file(TCCState *s1)
{
Section *s = s1->symtab;
int first_