diff --git a/src/lib/stores/bash/commands/ls.ts b/src/lib/stores/bash/commands/ls.ts index 723ccca..2662907 100755 --- a/src/lib/stores/bash/commands/ls.ts +++ b/src/lib/stores/bash/commands/ls.ts @@ -1,5 +1,5 @@ import { Bash, ExitCode, type Permission, type TimeStamps } from '../bash'; -import { Type, type NodePerms, type TreeNode } from '../fs'; +import { Type, VirtualFS, type NodePerms, type TreeNode } from '../fs'; import { Sort, SortNodeBy } from '../sort'; import type { CommandArgs, ICommand, Result, resultData } from '../static'; @@ -38,6 +38,7 @@ const months: readonly string[] = [ ]; export const cmd_ls = function (this: Bash, args: CommandArgs): Result { + const Fs = this.getFs(); const resultData: resultData = { cmd: 'ls', data: null, args: args }; const result: Result = { exitCode: ExitCode.ERROR, path: this.getCwd(), data: resultData }; const nodes: TreeNode[] = []; @@ -55,10 +56,10 @@ export const cmd_ls = function (this: Bash, args: CommandArgs): Result { this.throwError(result); //No such flag/s } - if (args.args.length === 0) nodes.push(this.getFs().getNodeByINode(this.getFs().cwd)); + if (args.args.length === 0) nodes.push(Fs.getNodeByINode(Fs.cwd)); for (let i = 0; i < args.args.length; i++) { - const node = this.getFs().resolvePath(args.args[i]); + const node = Fs.resolvePath(args.args[i]); if (node === null) this.throwError(result); //no such path (i think this will never occur as backed methods have error cases implemented - which is wrong) nodes.push(node); @@ -71,16 +72,18 @@ export const cmd_ls = function (this: Bash, args: CommandArgs): Result { function result_ls(this: Bash, data: any, args: CommandArgs): HTMLElement { const dummysonoerror: HTMLElement = document.createElement('div'); + const Fs: VirtualFS = this.getFs(); const flagInfo = checkFlags(args.flags); - const nodes: TreeNode[] = data; + let nodes: TreeNode[] = data; const f_a: boolean = flagInfo.has('a') || flagInfo.has('all'); const f_A: boolean = flagInfo.has('A') || flagInfo.has('almost-all'); const f_G: boolean = flagInfo.has('G') || flagInfo.has('no-group'); const f_h: boolean = flagInfo.has('h') || flagInfo.has('human-readable'); const f_r: boolean = flagInfo.has('r') || flagInfo.has('reverse'); + const f_R: boolean = flagInfo.has('R') || flagInfo.has('recursive'); const f_Q: boolean = flagInfo.has('Q') || flagInfo.has('quote-name'); const f_n: boolean = flagInfo.has('n') || flagInfo.has('numeric-uid-gid'); const f_N: boolean = flagInfo.has('N') || flagInfo.has('literal'); @@ -104,11 +107,29 @@ function result_ls(this: Bash, data: any, args: CommandArgs): HTMLElement { for (const node of nodes) { const elem: HTMLElement = document.createElement('div'); - const children: TreeNode[] = node.children.map((child) => this.getFs().getNodeByINode(child)); + const childrenMap: TreeNode[] = node.children.map((child) => Fs.getNodeByINode(child)); + + const children: TreeNode[] = (f_a || f_A) + ? childrenMap + : childrenMap.filter((child) => !child.name.startsWith('.')); + const timeArg = valuedArgs.find((flag) => flag.startsWith('time')); const shouldNamesShift: boolean = children.some((child) => child.name.match(/\s/) !== null); let timestamp: SortNodeBy.ATIME | SortNodeBy.CTIME | SortNodeBy.MTIME = SortNodeBy.MTIME; + if(f_a && !f_A) { + const current: TreeNode = node; + current.name = '.'; + + let parent: TreeNode = current; + if(node.parent) { + parent = Fs.getNodeByINode(node.parent); + parent.name = '..'; + } + + children.unshift(current, parent); + } + if(timeArg) { let value: string = timeArg.split('=')[1]; if (value && isValidNodeTimestamp(value)) timestamp = value; @@ -138,8 +159,6 @@ function result_ls(this: Bash, data: any, args: CommandArgs): HTMLElement { ...children.map((child) => child.size.toString().length)); for (const child of children) { - if (child.name.startsWith('.') && !(f_a || f_A)) continue; - const entry: LsEntry = { inode: null, perms: formatPermission(child), @@ -155,63 +174,19 @@ function result_ls(this: Bash, data: any, args: CommandArgs): HTMLElement { rows.push(LsEntryUtils.toString(entry)); } - if (f_a && !f_A) { - const current: LsEntry = { - inode: null, - perms: formatPermission(node), - children: formatChildren(node), - owners: formatOwners.call(this, node, flagInfo), - size: formatSize.call(this, f_h, node, maxSizeWidth, f_si), - modt: formatModtime(node, timestamp), - name: shouldNamesShift ? ' .' : '.' - }; - let parent: LsEntry = { - ...current, - name: shouldNamesShift ? ' ..' : '..' - }; - if (node.parent) { - const parentNode: TreeNode = this.getFs().getNodeByINode(node.parent); - parent = { - inode: null, - perms: formatPermission(parentNode), - children: formatChildren(parentNode), - owners: formatOwners.call(this, parentNode, flagInfo), - size: formatSize.call(this, f_h, parentNode, maxSizeWidth, f_si), - modt: formatModtime(parentNode, timestamp), - name: shouldNamesShift ? ' ..' : '..' - }; - } - - if (f_i) { - current.inode = node.inode; - parent.inode = node.parent ? node.parent : node.inode; - } - - if (f_r) { - rows.push(LsEntryUtils.toString(parent), LsEntryUtils.toString(current)); - } else { - rows.unshift(LsEntryUtils.toString(current), LsEntryUtils.toString(parent)); - } - } - //TODO: Calculate the total size of contents in the node rows.unshift('total ' + node.children.length.toString()); if (nodes.length > 1) { const nodePath: string = - node.name === '/' ? '/:' : `${this.getFs().getPathByInode(node.inode).slice(1)}:`; + node.name === '/' ? '/:' : `${Fs.getPathByInode(node.inode).slice(1)}:`; rows.unshift(nodePath); rows.push('\n'); } - - for (let i = 0; i < rows.length; i++) { const p: HTMLElement = document.createElement('p'); - - p.innerText = rows[i]; - elem.appendChild(p); } @@ -221,55 +196,61 @@ function result_ls(this: Bash, data: any, args: CommandArgs): HTMLElement { return w; } const maxWidth: number = Math.ceil((this.getTerminalWidth() / this.getTerminalFontSize()) * 0.8); - const nameLengths: number[] = children.map(node => node.name.length); let columns: string[][] = []; let colWidths: number[]; let lowBound: number = 1; - let highBound: number = Math.floor(maxWidth / 3); - + let highBound: number = children.length; while(lowBound + 1 < highBound) { - let c: number = Math.ceil((lowBound + highBound) / 2); + const c = lowBound + (highBound - lowBound) / 2; columns = []; colWidths = []; + let fileIndex = 0; for(let i = 0; i < c; i++) { + if(fileIndex >= children.length) break; columns[i] = []; - for(let j = 0; j < Math.ceil((children.length / c)); j++){ - const fileIndex: number = i * Math.floor((children.length / c)) + j; + for(let j = 0; j < Math.ceil(children.length / c); j++) { + if(fileIndex >= children.length) break; columns[i].push(children[fileIndex].name); + fileIndex++; } - colWidths.push(Math.max(...columns[i].map((string) => string.length))); + colWidths.push(Math.max(...columns[i].map((name) => name.length))); } - let calcWidth: number = - Math.ceil(colWidths.reduce((result, value) => result + value) + ((c-1) * 2)); + const calcWidth: number = colWidths.reduce((prev, curr) => prev + curr) + ((c-1) * 2); - if(calcWidth <= maxWidth) { - lowBound = c; - } - else highBound = c; + if(calcWidth < maxWidth) lowBound = c + 1; + else if(calcWidth > maxWidth) highBound = c -1 } - + const wrapper: HTMLElement = document.createElement('div'); wrapper.style.display = 'flex'; wrapper.style.columnGap = `${this.getTerminalFontSize() * 2}px`; - for(let i = 0; i < columns.length -1; i++) { + let fileIndex = 0; + + for(let i = 0; i < lowBound; i++) { + if(fileIndex >= children.length) break; const col: HTMLElement = document.createElement('div'); - for(let j = 0; j < columns[i].length -1; j++) { - const name: HTMLElement = document.createElement('p'); - name.innerText = columns[i][j]; - col.appendChild(name); - } + + for(let j = 0; j < Math.ceil(children.length / lowBound); j++) { + if(fileIndex >= children.length) break; + + const entry: HTMLElement = document.createElement('p'); + + entry.innerText = formatName(children[fileIndex], flagInfo, shouldNamesShift); + col.appendChild(entry); + fileIndex++; + } wrapper.appendChild(col); } if (nodes.length > 1) { const nodePath: HTMLElement = document.createElement('p'); - nodePath.innerText = node.name === '/' ? '/:' : `${this.getFs().getPathByInode(node.inode).slice(1)}:`; + nodePath.innerText = node.name === '/' ? '/:' : `${Fs.getPathByInode(node.inode).slice(1)}:`; w.appendChild(nodePath); } @@ -278,36 +259,6 @@ function result_ls(this: Bash, data: any, args: CommandArgs): HTMLElement { return w; } -function nodeBinarySearch( - n: number, - nameLengths: number[], - low: number, - high: number, - max: number): number { - - let cols: number = Math.floor((low + high) / 2); - - console.log("new bounds", low, high); - - if(low + 1 < high) { - - - const calcWidth: number = - Math.ceil(nameLengths.reduce((result, value) => result + value) + ((cols-1) * 2)); - - console.log(calcWidth, "calcWidth"); - console.log(cols); - - if (calcWidth <= max) { - low = cols; - } else high = cols; - - nodeBinarySearch(n, nameLengths, low, high, max); - } - console.log("out values", low, cols); - return low -} - function isValidNodeSortMethod(value: string): value is SortNodeBy { return Object.values(SortNodeBy).includes(value as SortNodeBy); } @@ -412,6 +363,25 @@ function formatName(node: TreeNode, flag: any, shouldShift: boolean) { return flag.has('p') && node.type === Type.Directory ? `${name}/` : name; } +/* function formatOutputShort(node: TreeNode, flag: any) { + let output: string = node.name; + const char: string = flag.has('Q') ? '"' : "'"; + + if (/\s/.test(node.name)) { + if(flag.has('i')) { + + } + else { + output = `${char}${node.name}${char}`; + } + + if(flag.has('i')) { + output = `${node.inode} ${output}` + } + + return flag.has('p') && node.type === Type.Directory ? ` ${output}/ ` : output; +} */ + const checkFlags = (pFlags: string[]) => { const flagSet = new Set(pFlags); @@ -436,6 +406,7 @@ export const ls: ICommand = { 't', 'S', 'r', + 'R', 'Q', 'p', 'o', @@ -452,6 +423,7 @@ export const ls: ICommand = { 'no-group', 'human-readable', 'reverse', + 'recursive', 'quote-name', 'indicator-style', 'literal',