diff --git a/src/constants/file.asm b/src/constants/file.asm index 0be0dce..9324f1d 100644 --- a/src/constants/file.asm +++ b/src/constants/file.asm @@ -75,3 +75,7 @@ section .rodata S_IFIFO equ 0010000q ; FIFO S_IFLNK equ 0120000q ; Symbolic link S_IFSOCK equ 0140000q ; Socket + ; Seek options + SEEK_SET equ 0 + SEEK_CUR equ 1 + SEEK_END equ 2 diff --git a/src/constants/syscalls.asm b/src/constants/syscalls.asm index f63212c..3f896be 100644 --- a/src/constants/syscalls.asm +++ b/src/constants/syscalls.asm @@ -6,6 +6,7 @@ section .rodata ;NR_stat equ 4 NR_fstat equ 5 NR_lstat equ 6 + NR_lseek equ 8 NR_exit equ 60 NR_creat equ 85 NR_umask equ 95 diff --git a/src/file.asm b/src/file.asm index 34a3cee..5190cad 100644 --- a/src/file.asm +++ b/src/file.asm @@ -14,6 +14,8 @@ section .text global fclose global fexist global fwrite + global fread + global fseek ;----- stat(*file[], *statBuffer[]) -----; ; Gets information from a file, stores it into statBuffer[] @@ -217,20 +219,19 @@ fexist: ;--- fwrite(*filePointer, *data[], ...) -----; ; Writes data to given file. Allows format specifiers and variable arguments (see `__INTERNAL_fmt` for supported specifiers) -; Return value: Length of given string(data[]), 0 if nothing was written (eg invalid file or file not writable, or data[] is empty) +; Return value: Length of given string(data[]), 0 if nothing was written or -errno if an error occured ; Used registers: ; rax* (ret) ; rdi (arg) Pointer to file ; rsi (arg) Data to write fwrite: - push rbp - mov rbp, rsp cmp rdi, 2 jg .cnt - xor rax, rax - leave + mov rax, -EBADF ret .cnt: + push rbp + mov rbp, rsp mov rax, rdi mov rdi, rsi mov rsi, rdx @@ -241,3 +242,58 @@ fwrite: call __INTERNAL_fmt leave ret + +;--- fread(*filePointer, *buff[], maxLength) ---; +; Reads data from given file. +; Return value: Amount of read characters or -errno if an error occured +; Used registers: +; rax* (ret) +; rdi* (arg) Pointer to file +; rsi (arg) Pointer to buff[] (where to store read data) +; rdx (arg) Size of buff[] +fread: + cmp rdi, 2 + jg .cnt + mov rax, -EBADF + ret + .cnt: + sub rsp, SIZE_QWORD + mov rax, NR_read + syscall + cmp rax, rdx + je .subOne + jmp .addAll + .subOne: + add rsi, rax + dec rsi + jmp .addEOS + .addAll: + add rsi, rax + .addEOS: + mov byte [rsi], EOS + .quit: + add rsp, SIZE_QWORD + ret + +;--- fseek(*filePointer, mode, offset) ---; +; Sets pointer in file. Mode is SEEK_SET, SEEK_CUR or SEEK_END. +; Return value: On succes returns the resulting offset location as measured in bytes from the beginning of the file. On error, -errno +; Used registers: +; rax* (ret) +; rdi (arg) Pointer to file +; rsi (arg) Seek mode +; rdx (arg) Pointer offset +fseek: + cmp rdi, 2 + jg .cnt + mov rax, -EBADF + ret + .cnt: + sub rsp, SIZE_QWORD + mov rax, rsi + mov rsi, rdx + mov rdx, rax + mov rax, NR_lseek + syscall + add rsp, SIZE_QWORD + ret diff --git a/src/tests.asm b/src/tests.asm index faff6a3..47c90e7 100644 --- a/src/tests.asm +++ b/src/tests.asm @@ -96,6 +96,8 @@ extern stat extern lstat extern fgettype extern fgetmod +extern fread +extern fseek section .rodata ;;; @@ -136,6 +138,8 @@ section .rodata ;file.asm TEST_fopen equ 1 ;Includes fclose TEST_fwrite equ 1 + TEST_fread equ 1 + TEST_fseek equ 1 TEST_stat equ 1 TEST_lstat equ 1 TEST_fgettype equ 1 @@ -349,6 +353,14 @@ section .rodata addTest(fwrite3, "fwrite(fp1, 'Howdy environment!') //mode 'w'") addTest(fwrite4, "fwrite(fp1, ''%c%c%c%c%c%c%c%c\n'', 'H', 'e', 'l', 'l', 'o', '!', '?', '!')") fwriteStr1 db "%c%c%c%c%c%c%c%c\n",EOS + ;fread() + addTestHeader(_fread, "fread") + addTest(fread1, "fread(fp1, strBuff1, 64)") + ;fseek() + addTestHeader(_fseek, "fseek") + addTest(fseek1, "fseek(fp1, SEEK_SET, 5)") + addTest(fseek2, "fseek(fp1, SEEK_CUR, -1)") + addTest(fseek3, "fseek(fp1, SEEK_SET, 0)") ;stat() addTestHeader(_stat, "stat") addTest(stat1, "stat('tests', statBuff1)") @@ -1288,6 +1300,91 @@ _start: call fclose %endif +;--- fread() +%if TEST_fread + printTestHeader(_fread) + mov rdi, [fp1] + call fclose + lea rdi, [rel file1] + mov rsi, 'r' + call fopen + assert_neq(0) + mov [fp1], rax + + printTest(fread1) + mov rdi, [fp1] + lea rsi, [rel strBuff1] + mov rdx, 64 + call fread + assert_more_eq(1) + lea rdi, [rel strBuff1] + call puts + + mov rdi, [fp1] + call fclose +%endif + +;--- fseek() +%if TEST_fseek + %macro fseek_relptr 1 + %endmacro + %macro fseek_readput 0 + mov rdi, [fp1] + lea rsi, [rel strBuff1] + mov rdx, 64 + call fread + lea rdi, [rel strBuff1] + call puts + %endmacro + printTestHeader(_fseek) + + lea rdi, [rel file1] + mov rsi, 'r' + call fopen + mov [fp1], rax + + ; TEST 1: fseek(fp1, SEEK_SET, 5) + printTest(fseek1) + mov rdi, [fp1] + mov rsi, SEEK_CUR + mov rdx, 5 + call fseek + assert_eq(5) + fseek_readput + + ; TEST 2: fseek(fp1, SEEK_CUR, -1) + printTest(fseek2) + mov rdi, [fp1] + mov rsi, SEEK_SET + mov rdx, 5 + call fseek + ;^ fseek_readput (fread) modified pointer, so restore it to result of test 1 + mov rdi, [fp1] + mov rsi, SEEK_CUR + mov rdx, -1 + call fseek + assert_eq(4) + fseek_readput + ; FAIL. Returned is 26 (which is length of the file) + ; My best guess is -1 is treated as unsigned. However, according to man 2 lseek: + ; The off_t data type is a signed integer data type + ; ( offset in rdx is supposed to be (off_t) offset ) + ; If it turns out unsigned after all, I'll have to manually get current location of pointer (call NR_lseek with SEEK_CUR and offset 0; returned value is current location) + ; ... and then substract given argument from that + + ; TEST 3: fseek(fp1, SEEK_SET, 0) + printTest(fseek3) + mov rdi, [fp1] + mov rsi, SEEK_SET + xor rdx, rdx + call fseek + assert_eq(0) + fseek_readput + + mov rdi, [fp1] + call fclose +%endif + ;--- stat() %if TEST_stat printTestHeader(_stat)