diff --git a/src/lib/assets/fs/signature b/src/lib/assets/fs/signature index 11e1443..c86b6aa 100644 --- a/src/lib/assets/fs/signature +++ b/src/lib/assets/fs/signature @@ -1 +1 @@ -32aac83d-ce2b-4def-977c-dcdcb6f514ee \ No newline at end of file +32aac83d-ce2b-4def-977c-dcdcb6f514eg \ No newline at end of file diff --git a/src/lib/stores/bash/commands/ls.ts b/src/lib/stores/bash/commands/ls.ts index 6f51248..480fa76 100644 --- a/src/lib/stores/bash/commands/ls.ts +++ b/src/lib/stores/bash/commands/ls.ts @@ -1,6 +1,6 @@ import { Bash, ExitCode, type Permission } from '../bash'; import { Type, type NodePerms, type TreeNode } from '../fs'; -import { asciiByteQSort } from '../sort'; +import { Sort } from '../sort'; import type { CommandArgs, ICommand, Result, resultData } from '../static'; type LsEntry = { @@ -40,7 +40,6 @@ export const cmd_ls = function (this: Bash, args: CommandArgs): Result { const resultData: resultData = { cmd: 'ls', data: null, args: args }; const result: Result = { exitCode: ExitCode.ERROR, data: resultData }; const nodes: TreeNode[] = []; - const paths: string[][] = []; //Check if args contain any nonexistent flags, if so add it to an array and check its length. if 0 no bad flags const invalidItems = args.flags.filter((flag) => !ls.flags.includes(flag)); @@ -81,11 +80,11 @@ function result_ls(this: Bash, data: any, args: CommandArgs): HTMLElement { const rows: string[] = []; if (!flagInfo.has('U') && !flagInfo.has('f')) - asciiByteQSort(node.children, flagInfo.has('r')); + //TODO: Add sort by option later on + Sort.nodeArraySort.call(this, node.children, flagInfo.has('r')); - //TODO: Actually calculate sizes here instead of defining numbers for types of nodes - const sizes = node.children.map((child) => (child.type === Type.Directory ? '4096' : '1')); - const maxSizeWidth = Math.max(...sizes.map((size) => size.length)); + const sizes = node.children.map((child) => (this.getFs().getNodeByINode(child).size)); + const maxSizeWidth = Math.max(...sizes.map((size) => size)); for (const inode of node.children) { const child: TreeNode = this.getFs().getNodeByINode(inode); @@ -113,19 +112,22 @@ function result_ls(this: Bash, data: any, args: CommandArgs): HTMLElement { modt: formatModtime(node), name: '.' }; - const parent: LsEntry = node.parent - ? { - perms: formatPermission(node.parent), - children: formatChildren(node.parent), - owners: formatOwners.call(this, node.parent, flagInfo), - size: formatSize.call(this, f_h, node.parent, maxSizeWidth), - modt: formatModtime(node.parent), + let parent: LsEntry = { + ...current, + name: '..' + } + if(node.parent) { + const parentNode: TreeNode = this.getFs().getNodeByINode(node.parent); + parent = { + perms: formatPermission(parentNode), + children: formatChildren(parentNode), + owners: formatOwners.call(this, parentNode, flagInfo), + size: formatSize.call(this, f_h, parentNode, maxSizeWidth), + modt: formatModtime(parentNode), name: '..' - } - : { - ...current, - name: '..' - }; + } + + } if (flagInfo.has('r')) { rows.push(LsEntryUtils.toString(parent), LsEntryUtils.toString(current)); @@ -170,31 +172,23 @@ function parsePerms(perms: NodePerms): string { } function formatOwners(this: Bash, node: TreeNode, flag: any): string { - const owner: string = node.owner; - const group: string = node.group; + const owner: string = this.getUserByUid(node.owner).username; + const group: string = this.getGroupByGid(node.group).groupname; if (flag.has('G') || flag.has('o')) { - if (flag.has('n')) { - const uid: number = this.getUserByName(owner).uid; - return `${uid}`; - } + if (flag.has('n')) + return `${node.owner}`; return `${owner}`; } if (flag.has('g')) { - if (flag.has('n')) { - const gid: number = this.getGroupByName(group).gid; - return `${gid}`; - } + if (flag.has('n')) + return `${node.group}`; return `${group}`; } - if (flag.has('n')) { - const uid: number = this.getUserByName(owner).uid; - const gid: number = this.getGroupByName(group).gid; - - return `${uid} ${gid}`; - } + if (flag.has('n')) + return `${node.owner} ${node.group}`; return `${owner} ${group}`; } @@ -220,16 +214,17 @@ function formatSize(this: Bash, human: boolean, node: TreeNode, max: number): st function formatModtime(node: TreeNode): string { const now = new Date(); - const hours: string = node.modtime.getHours().toString().padStart(2, '0'); - const minutes: string = node.modtime.getMinutes().toString().padStart(2, '0'); + //TODO: Change this to be dynamic based on the --time value passed + const hours: string = node.timestamps.mTime.getHours().toString().padStart(2, '0'); + const minutes: string = node.timestamps.mTime.getMinutes().toString().padStart(2, '0'); const time: string = - now.getFullYear() === node.modtime.getFullYear() + now.getFullYear() === node.timestamps.mTime.getFullYear() ? `${hours}:${minutes}` - : node.modtime.getFullYear().toString(); + : node.timestamps.mTime.getFullYear().toString(); return [ - months[node.modtime.getMonth()], - node.modtime.getDate().toString().padStart(2, ' '), + months[node.timestamps.mTime.getMonth()], + node.timestamps.mTime.getDate().toString().padStart(2, ' '), `${time}` ].join(' '); } diff --git a/src/lib/stores/bash/fs.ts b/src/lib/stores/bash/fs.ts index 12671be..92aa794 100644 --- a/src/lib/stores/bash/fs.ts +++ b/src/lib/stores/bash/fs.ts @@ -1,12 +1,11 @@ import type { readonly } from 'svelte/store'; import type { Permission, TimeStamps, User } from './bash'; import { Stack } from '../stack'; -import { Pause } from '@lucide/svelte'; export enum Type { Directory = 16384, File = 32768, - SymblicLink = 40960 + SymbolicLink = 40960 } export type NodePerms = { @@ -25,6 +24,7 @@ export type TreeNode = { parent?: number; name: string; type: Type; + size: number; //Size in Bytes children: number[]; content: string; // GUID of the cache file that contains the file contents. link: number; // Links diff --git a/src/lib/stores/bash/sort.ts b/src/lib/stores/bash/sort.ts index 33dd745..b3c4eb0 100644 --- a/src/lib/stores/bash/sort.ts +++ b/src/lib/stores/bash/sort.ts @@ -1,48 +1,112 @@ -import type { TreeNode } from './fs'; +import type { TreeNode } from "./fs"; +import type { Bash } from "./bash"; -export function asciiByteQSort(array: TreeNode[], reverse: boolean) { - qSort(array, 0, array.length - 1, reverse); +export enum SortBy { + NAME, + INODE, + SIZE, + EXTENSION, + TYPE, + MTIME = 'mTime', + ATIME = 'aTime', + CTIME = 'cTime', } -function qSort(array: TreeNode[], start: number, end: number, reverse: boolean) { - if (end <= start) return; +export class Sort { - let pivot: number = partition(array, start, end, reverse); - qSort(array, start, pivot - 1, reverse); - qSort(array, pivot + 1, end, reverse); -} - -function partition(part: TreeNode[], start: number, end: number, reverse: boolean): number { - let pivot: TreeNode = part[end]; - let i: number = start - 1; - - for (let j = start; j <= end; j++) { - if (compareStrings(part[j].name, pivot.name, reverse) < 0) { - i++; - let temp = part[i]; - part[i] = part[j]; - part[j] = temp; - } - } - i++; - let temp = part[i]; - part[i] = part[end]; - part[end] = temp; - - return i; -} - -function compareStrings(a: string, b: string, reverse: boolean): number { - const minLength = Math.min(a.length, b.length); - - for (let i = 0; i < minLength; i++) { - const charCodeA = a.charCodeAt(i); - const charCodeB = b.charCodeAt(i); - - if (charCodeA !== charCodeB) { - return reverse ? charCodeB - charCodeA : charCodeA - charCodeB; - } + public static nodeArraySort(this: Bash, nodes: TreeNode[] | number[], reverse: boolean = false, sortBy: SortBy = SortBy.NAME) { + if(nodes.length === 0) throw new Error('Tried to sort an empty node array!'); + const parsedNodes: TreeNode[] = []; + + if(typeof nodes[0] === 'number') { + for(const inode of nodes as number[]) { + const node = this.getFs().getNodeByINode(inode); + parsedNodes.push(node); + } + Sort.nodeQSort(parsedNodes, reverse, sortBy, 0, parsedNodes.length - 1); + } else Sort.nodeQSort(nodes as TreeNode[], reverse, sortBy, 0, nodes.length - 1); } - return reverse ? b.length - a.length : a.length - b.length; -} + private static nodeQSort(array: TreeNode[], reverse: boolean, sortBy: SortBy, start: number, end: number) { + if(end <= start) return; + + let pivot: number = this.nodePartition(array, reverse, sortBy, start, end); + this.nodeQSort(array, reverse, sortBy, start, pivot - 1); + this.nodeQSort(array, reverse, sortBy, pivot + 1, end); + + } + + private static nodePartition(part: TreeNode[], reverse: boolean, sortBy: SortBy, start: number, end: number): number { + let pivot: TreeNode = part[end]; + let i: number = start - 1; + + for (let j = start; j <= end; j++) { + if(this.nodeCompareElements(part[j], pivot, sortBy, reverse) < 0) { + i++; + let temp = part[i]; + part[i] = part[j]; + part[j] = temp; + } + } + i++; + let temp = part[i]; + part[i] = part[end]; + part[end] = temp; + + return i; + } + + private static nodeCompareElements(a: TreeNode, b: TreeNode, sortBy: SortBy, reverse: boolean): number { + switch(sortBy) { + case SortBy.NAME: { + const minLength = Math.min(a.name.length, b.name.length); + + for(let i = 0; i < minLength; i++) { + const charCodeA = a.name.charCodeAt(i); + const charCodeB = b.name.charCodeAt(i); + + if (charCodeA !== charCodeB) { + return reverse ? charCodeB - charCodeA : charCodeA - charCodeB; + } + } + return reverse ? b.name.length - a.name.length : a.name.length - b.name.length; + } + case SortBy.MTIME: + case SortBy.ATIME: + case SortBy.CTIME: { + // The sortBy serves as the lookup key in the timestamps object. + // It works because the times in SortBy enum have assigned values matching the names of the keys in the TreeNode object + const timeA: number = a.timestamps[sortBy].getTime(); + const timeB: number = b.timestamps[sortBy].getTime(); + + return reverse ? timeA - timeB : timeB - timeA; + } + case SortBy.SIZE: { + return reverse ? a.size - b.size : b.size - a.size; + } + case SortBy.EXTENSION: { + const extA: string = a.name.split('.').pop() ?? ''; + const extB: string = b.name.split('.').pop() ?? ''; + const minLength = Math.min(extA.length, extB.length); + + for(let i = 0; i < minLength; i++) { + const charCodeA = extA.charCodeAt(i); + const charCodeB = extB.charCodeAt(i); + + if (charCodeA !== charCodeB) { + return reverse ? charCodeB - charCodeA : charCodeA - charCodeB; + } + } + return reverse ? extB.length - extA.length : extA.length - extB.length; + } + case SortBy.INODE: { + return reverse ? b.inode - a.inode : a.inode - b.inode; + } + case SortBy.TYPE: { + return reverse ? b.type - a.type : a.type - b.type; + } + default: + throw new Error(`Sorting basis outside of the declared scope. - `); + } + } +} diff --git a/src/lib/stores/terminal/init.svelte.ts b/src/lib/stores/terminal/init.svelte.ts index 74e0cad..aae4677 100644 --- a/src/lib/stores/terminal/init.svelte.ts +++ b/src/lib/stores/terminal/init.svelte.ts @@ -18,6 +18,7 @@ function jsonToNodeTable(data: any, parent?: number): Map { inode: object.Inode, name: object.Name, type: object.Type, + size: object.Size, interactible: object.Interactible, func: object.Func, children: object.Children,