📄 ls.asm
字号:
;; The contents of outbuf are written to stdout, and lsfile returns. stosb mov [DATAOFF(following)], al lea eax, [byte ebx + 3] pop ecx mov edx, edi sub edx, ecx int 0x80 ret;; errmsg displays a simple error message on stderr.;;;; input:;; eax = error number (positive value, to be returned upon exit);; esi = filename to use as error message;; edi = length of filename;;;; output:;; retval = error number;; carry flag is set (for the benefit of dolstat);; eax, ebx, ecx, and edx are alterederrmsg: mov [DATAOFF(retval)], eax call tobol mov dword [edi + esi], ' ??' | (10 << 24) lea eax, [byte ebx + 3] inc ebx mov ecx, esi lea edx, [byte edi + 4] int 0x80 stc ret;; dolstat executues the lstat system call, and displays an error;; message if the call was not successful.;;;; input:;; ebx = pathname of file;;;; output:;; sts = stat information for the file;; eax = lstat return value;; ecx = zero if the file is a directory;; carry flag is set if lstat returned an error;; zero flag is set if the file is a directorydolstat: push byte 107 pop eax lea ecx, [DATAOFF(sts)] int 0x80 neg eax jc errmsg mov ecx, [DATAOFF(sts.st_mode) - 2] shr ecx, 28 and ecx, byte ~4 ret;; Here is the epilog code for lsdir. When the directory contains;; nothing more, the program closes the file handle, and then examines;; the last return value from getdents. If it is non-zero, errmsg is;; called; otherwise the program falls through to tobol.getdentsend: push eax push byte 6 pop eax int 0x80 pop eaxbadfile: neg eax jc errmsg;; tobol forces the output to the start of a line.;;;; input:;; xline = current tabstop position of cursor;;;; output:;; xline = zero;; eax, ebx, ecx, and edx are alteredtobol: xor ebx, ebx inc ebx cmp bh, [DATAOFF(xline)] jz .quit mov [DATAOFF(xline)], bh lea eax, [byte ebx + 3] lea ecx, [DATAOFF(newline)] mov edx, ebx int 0x80.quit: ret;; lsdir is the continuation of ls in the case when the supplied;; filename is a directory needing to be expanded.lsdir:;; A read-only file handle for the directory is obtained and saved on;; the stack. mov al, 5 int 0x80 or eax, eax js badfile push eax;; If the directory headings flag is on, then the pathname is;; displayed on stdout followed by a colon. If this is not the first;; time the program has written to stdout, then a preceding newline is;; added. lea ebx, [byte ecx + 1] mov eax, [DATAOFF(dirheads)] or al, al jz .nodirhead mov [DATAOFF(following)], bl mov ecx, esi lea edx, [byte edi + 2] or ah, ah mov ah, 10 mov [esi + edi], eax jz .firstout dec ecx inc edx mov byte [ecx], 10.firstout: lea eax, [byte ebx + 3] int 0x80.nodirhead:;; A slash is added to the pathname, and the file handle is stored in;; ebx. mov byte [esi + edi], '/' inc edi pop ebx;; The program obtains the directory's contents in the outer loop,;; using the getdents system call. The loop is exited when getdents;; returns a non-positive value..getdentsloop: xor eax, eax mov al, 141 mov ecx, direntsbuf cdq mov dh, direntsbufsize >> 8 int 0x80 or eax, eax jle getdentsend;; The program examines each directory entry in the inner loop, during;; which the current state is saved on the stack. (ebx contains the;; file handle, ecx contains the current pointer into the dirents;; buffer, eax contains the size of the unread dirents, esi points to;; pathbuf, and edi contains the length of the pathname.).direntloop: pusha;; edi is set to the position after the final slash in the pathname,;; and esi is set to the filename of the current directory entry. Then;; the filename is appended to the directory, and the filename's;; length (plus one) is stashed in edx. cdq add edi, esi mov ebx, esi lea esi, [byte ecx + dirent.d_name] push esi.appendloop: lodsb stosb inc edx or al, al jnz .appendloop pop esi;; If the filename begins with a dot and -a was not specified, this;; file is passed over. Likewise if the -B option was specified and;; the filename ends in a tilde. cmp byte [esi], '.' jnz .visible cmp al, [DATAOFF(showdots)] jz .resume.visible: cmp byte [byte edi - 2], '~' jnz .notabackup cmp al, [DATAOFF(hidebkup)] jnz .resume.notabackup:;; lstat is called on the complete path. If it is also a directory,;; and if -R was specified, and if the entry is not named "." or "..",;; then the path is added to the directory queue. lea edi, [byte edx - 1] call dolstat jc .resume jnz .dofile cmp al, [DATAOFF(recursed)] jz .dofile push esi lodsb cmp al, '.' jnz .dirokay lodsb cmp al, '.' jnz .notdotdot lodsb.notdotdot: or al, al jz .dirloops.dirokay: mov esi, ebx push edi call dirpush pop edi.dirloops: pop esi;; lsfile displays the entry..dofile: call lsfile;; State is restored, and ecx is advanced to the next entry. If there;; are no entries left in the buffer, the program returns to the outer;; loop in order to get the next batch of entries..resume: popa movzx edx, word [byte ecx + dirent.d_reclen] add ecx, edx sub eax, edx jbe .getdentsloop jmp short .direntloop;; copyfilename copies a string, translating non-graphic characters;; according to the flags rawchars and octchars.;;;; input:;; esi = source string;; edi = destination buffer;; ecx = length of string;;;; output:;; esi = end of source string;; edi = end of copied string;; ecx = zero;; eax and edx are alteredcopyfilename:;; dl is non-zero if -N was specified, and dh is non-zero if -b was;; specified. mov edx, [DATAOFF(rawchars)];; The program retrieves the next character in the filename. If -N was;; specified, the character is copied as-is..charloop: lodsb or dl, dl jnz .graphic;; The backslash and double-quote characters are checked for;; separately, since they need to be escaped when -b is specified. All;; other characters between 32 and 126 inclusive are copied as-is. mov ah, '\' cmp al, ah jz .backslash cmp al, '"' jz .backslash cmp al, '~' ja .octal cmp al, ' ' jae .graphic;; If -b was not specified, non-graphic characters are replaced with a;; question mark. Otherwise, non-graphic characters are replaced by a;; backslash followed by three octal digits. In the case of a;; backslash or a double-quote, a backslash is inserted before the;; actual character..octal: or dh, dh jz .usehook ror eax, 8 stosb mov al, '0' >> 2 rol eax, 2 stosb shr eax, 26 aam 8 add ax, '00'.backslash: or dh, dh jz .graphic mov [edi], ah inc edi jmp short .graphic.usehook: mov al, '?'.graphic: stosb;; The program loops until all characters have been copied. dec ecx jnz .charloopquit2: ret;; lsrecurse calls ls repeatedly for each directory listed in;; the queue, recursing through any subdirectories depth-first.;;;; input:;; esi = where in the queue to begin;; dirqueuetop = where in the queue to end;;;; output:;; edx, esi = dirqueuetop;; all other registers (except ebp) are alteredlsrecurse:;; The program ensures that directory listings always begin and end on;; their own lines, and then sets edx to the current end of the queue;; (which is initially set to the start of the queue). call tobol mov edx, dirqueuedirqueuetop equ $ - 4;; The program begins at the requested position in the queue, and;; calls ls for each element. The program calls lsrecurse again;; directly, using the original end of the queue as the starting;; point, so that if the call to ls caused more items to be added to;; the queue, they will be displayed before the next item in the;; original queue. This recursion ensures a depth-first traversal of;; subdirectories (and causes the queue to act more like a stack of;; queues). After the recursive call has bottomed out and returns;; here, the end of the queue is reset. This prevents the new items;; from being displayed again later, and also keeps the queue from;; continually growing in size..dirloop: cmp esi, edx jnc quit2 push edx xor edx, edx call ls mov esi, [byte esp + 4] call lsrecurse pop esi pop edx mov [DATAOFF(dirqueuetop)], edx jmp short .dirloop;; The read-only data.suffixes: db '|*/**@=' ; file suffix array db 0, 24, 60, 60 ; size of time unitstimeunits:permissions equ $ - 1filetypes equ $ + 3options equ $ + 12optioncount equ 13 db 'xwtrpc-dTbs-1lFsSiRbNBadhms'; permission bits: xwtr - T s S; file type chars: pc d b - l s; cmdline options: 1lFs iRbNBad; time unit chars: dhms_ebp_pos_ equ origin + ($ - $$);; itoa converts a number to its ASCII decimal representation.;;;; input:;; eax = number to display;; edi = buffer to write to;; edx = number of bytes to write (padding with spaces on the left);; ;; output:;; eax = zero;; edi = the position immediately following the string;; ecx is altereditoa:;; edx is preserved on the stack, for the caller's convenience, and;; the complete buffer is filled with spaces. push edx push eax mov ecx, edx mov al, ' ' rep stosb pop eax;; The position after the number is preserved on the stack, and then;; the program moves backward through the buffer, dividing the number;; by ten and replacing a space with the remainder changed to an ASCII;; digit on each iteration through the loop.newline equ $ + 2 push edi mov cl, 10.decloop: cdq div ecx add dl, '0' dec edi mov [edi], dl or eax, eax jnz .decloop;; edi and edx are restored and the program returns to the caller. pop edi pop edx retfilesize equ $ - $$;; The zero-initialized data.ABSOLUTE origin + filesizeALIGNB 16;; buffer containing the stat structurests:.st_dev: resw 1 resw 1.st_ino: resd 1 ; inode number.st_mode: resw 1 ; mode bits.st_nlink: resw 1 ; number of hard links.st_uid: resw 1 ; owner user id.st_gid: resw 1 ; owner group id.st_rdev: resw 1 ; rdev numbers resw 1.st_size: resd 1 ; file size in bytes.st_blksize: resd 1.st_blocks: resd 1 ; file size in blocks.st_atime: resd 1 resd 1.st_mtime: resd 1 ; modification time resd 1.st_ctime: resd 1 resd 3;; The program's return value buffer.retval: resd 1;; The flags set by the command-line options.optionflags: resb 1 ; (invalid option)hidedirs: resb 1 ; -dshowdots: resb 1 ; -ahidebkup: resb 1 ; -Brawchars: resb 1 ; -Noctchars: resb 1 ; -brecursed: resb 1 ; -Rshowinod: resb 1 ; -i resb 1 ; (-S, invalid option)showblks: resb 1 ; -sshowtype: resb 1 ; -Flongdisp: resb 1 ; -loneperln: resb 1 ; -1;; The directory headings flag.dirheads: resb 1;; following is set to non-zero after writing to stdout.following: resb 1;; The current cursor position, as measured in tabstops.xline: resb 1;; The buffer for holding the current pathname (with an extra byte;; prepended for holding the occasional newline character). resd 1pathbuf: resb pathbufsize + 16;; The output buffer. (It needs to be large enough to hold two;; complete pathnames, both of which could be quadrupled in size due;; to substituting octal escape sequences for non-graphic characters.)outbuf: resb pathbufsize * 8 + 64;; The buffer used to hold the data returned from getdents.direntsbuf: resb direntsbufsize;; The initial size of the directory queue. This item needs to be last;; of all, since it is expanded by calling brk.dirqueue: resb pathbufsizememsize equ $ - origin
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -