%define SYS_open 0x2 %define SYS_close 0x3 %define SYS_write 0x1 %define SYS_read 0x0 %define SYS_lseek 0x8 %define SYS_exit 0x3c %define O_RDONLY 0x0 %define SEEK_SET 0x0 %define FD_stderr 2 %define FD_stdout 1 %define FD_stdin 0 %define BUFFER_SIZE 2048 section .text global _start _start: pop rax ; argc pop QWORD [exec_name] ; argv[0] check_arg_loop: mov rdi,[rsp] ; argv[i] ; check for null cmp rdi,0 je print_stdin_and_exit ; check for flags call check_flag cmp rax,0 je print_files ; no more flags pop rdi ; remove argv[i] (a flag) jmp check_arg_loop print_files: mov rdi,[rsp] ; argv[i] cmp rdi,0 je print_files_end ; check if argv[i] is '-' mov rsi,dash_str call streql cmp rax,0 jne print_files_stdin ; open file mov rdi,[rsp] ; argv[i] call open_file ; print file mov rdi,rax pop rsi ; argv[i] call print_file jmp print_files print_files_stdin: pop rdi ; argv[i] mov rdi,FD_stdin mov rsi,stdin_name call print_file ; rewind stdin mov rax,SYS_lseek mov rdi,FD_stdin mov rsi,0 mov rdx,SEEK_SET syscall jmp print_files print_files_end: xor rdi,rdi jmp exit open_file: ; int open_file(const char *path) push rdi ; path ; open file mov rax,SYS_open ; rdi already set mov rsi,O_RDONLY syscall ; check return cmp rax,0 jl open_error pop rdi ; path ret open_error: call start_error ; message mov rax,SYS_write mov rdi,FD_stderr mov rsi,open_error_text mov rdx,open_error_text_len syscall ; detail mov rdi,[rsp] ; path call strlen mov rdx,rax mov rax,SYS_write mov rdi,FD_stderr pop rsi ; path syscall call end_error mov rdi,1 jmp exit print_stdin_and_exit: mov rdi,FD_stdin mov rsi,stdin_name call print_file xor rdi,rdi jmp exit print_file: ; void print_file(int fd, const char *name) push rdi ; fd push rsi ; name print_file_loop: mov rax,SYS_read mov rdi,[rsp + 8] ; fd mov rsi,read_buff mov rdx,BUFFER_SIZE syscall ; check error cmp rax,0 jl read_file_error ; no more bytes je print_file_end ; print message mov rdx,rax mov rax,SYS_write mov rdi,FD_stdout mov rsi,read_buff syscall jmp print_file_loop print_file_end: pop rsi ; name pop rdi ; fd ret read_file_error: call start_error ; message mov rax,SYS_write mov rdi,FD_stderr mov rsi,read_error_text mov rdx,read_error_text_len syscall ; detail mov rdi,[rsp] ; name call strlen mov rdx,rax mov rax,SYS_write mov rdi,FD_stderr pop rsi ; name syscall call end_error mov rdi,1 jmp exit ;; check arg for -u, exit if unknown flag ;; return 1 if found, 0 otherwise check_flag: ; bool check_arg(const char *arg) xor rax,rax ; check if arg[0] == '-' cmp BYTE [rdi],'-' jne check_flag_end inc rdi ; rax still set check_flag_loop: ; check null byte mov cl,[rdi] cmp cl,0 je check_flag_end ; check for u cmp cl,'u' jne check_flag_error mov rax,1 inc rdi jmp check_flag_loop check_flag_error: push rdi ; arg + i call start_error ; message mov rax,SYS_write mov rdi,FD_stderr mov rsi,arg_error_text mov rdx,arg_error_text_len syscall ; character mov rax,SYS_write mov rdi,FD_stderr pop rsi ; arg + i mov rdx,1 syscall call end_error mov rdi,1 jmp exit check_flag_end: ret ;; print exec_name and a colon_space start_error: ; void start_error(void) mov rdi,[exec_name] call strlen mov rdx,rax mov rax,SYS_write mov rdi,FD_stderr mov rsi,[exec_name] syscall mov rax,SYS_write mov rdi,FD_stderr mov rsi,colon_space mov rdx,colon_space_len syscall ret ;; print newline end_error: ; void end_error(void) mov rax,SYS_write mov rdi,FD_stderr mov rsi,newline_ptr mov rdx,1 syscall ret streql: ; bool streql(const char *s1, const char *s2) ; check if both are zero mov cl,[rdi] ; s1[i] or cl,[rsi] jz streql_true ; check if equal mov cl,[rdi] xor cl,[rsi] jnz streql_false inc rdi inc rsi jmp streql streql_true: mov rax,1 ret streql_false: xor rax,rax ret exit: ; NORETURN void exit(int status) mov rax,SYS_exit syscall strlen: ; uint64 strlen(const char *str) mov rax,rdi strlen_loop: cmp BYTE [rax],0 je strlen_end inc rax jmp strlen_loop strlen_end: sub rax,rdi ret section .bss exec_name resq 1 read_buff resb BUFFER_SIZE section .data dash_str db "-",0x0 colon_space db ": " colon_space_len equ $ - colon_space newline_ptr db 0xA stdin_name db "",0x0 arg_error_text db "unknown flag: " arg_error_text_len equ $ - arg_error_text read_error_text db "read error: " read_error_text_len equ $ - read_error_text open_error_text db "could not open file for reading: " open_error_text_len equ $ - open_error_text