LS technically fixed (still debugging pos interpreter errors). Sort class added, witht he ability to sort nodes by inode and the object through a common method. Added an enum SortBy that contains sorting types for the node. Backend for sorting implemented, modification of the ls command still required (will need long flag support for other sorting methods, and timestamp display).
This commit is contained in:
@@ -1 +1 @@
|
|||||||
32aac83d-ce2b-4def-977c-dcdcb6f514ee
|
32aac83d-ce2b-4def-977c-dcdcb6f514eg
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Bash, ExitCode, type Permission } from '../bash';
|
import { Bash, ExitCode, type Permission } from '../bash';
|
||||||
import { Type, type NodePerms, type TreeNode } from '../fs';
|
import { Type, type NodePerms, type TreeNode } from '../fs';
|
||||||
import { asciiByteQSort } from '../sort';
|
import { Sort } from '../sort';
|
||||||
import type { CommandArgs, ICommand, Result, resultData } from '../static';
|
import type { CommandArgs, ICommand, Result, resultData } from '../static';
|
||||||
|
|
||||||
type LsEntry = {
|
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 resultData: resultData = { cmd: 'ls', data: null, args: args };
|
||||||
const result: Result = { exitCode: ExitCode.ERROR, data: resultData };
|
const result: Result = { exitCode: ExitCode.ERROR, data: resultData };
|
||||||
const nodes: TreeNode[] = [];
|
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
|
//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));
|
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[] = [];
|
const rows: string[] = [];
|
||||||
|
|
||||||
if (!flagInfo.has('U') && !flagInfo.has('f'))
|
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) => (this.getFs().getNodeByINode(child).size));
|
||||||
const sizes = node.children.map((child) => (child.type === Type.Directory ? '4096' : '1'));
|
const maxSizeWidth = Math.max(...sizes.map((size) => size));
|
||||||
const maxSizeWidth = Math.max(...sizes.map((size) => size.length));
|
|
||||||
|
|
||||||
for (const inode of node.children) {
|
for (const inode of node.children) {
|
||||||
const child: TreeNode = this.getFs().getNodeByINode(inode);
|
const child: TreeNode = this.getFs().getNodeByINode(inode);
|
||||||
@@ -113,19 +112,22 @@ function result_ls(this: Bash, data: any, args: CommandArgs): HTMLElement {
|
|||||||
modt: formatModtime(node),
|
modt: formatModtime(node),
|
||||||
name: '.'
|
name: '.'
|
||||||
};
|
};
|
||||||
const parent: LsEntry = node.parent
|
let parent: LsEntry = {
|
||||||
? {
|
...current,
|
||||||
perms: formatPermission(node.parent),
|
name: '..'
|
||||||
children: formatChildren(node.parent),
|
}
|
||||||
owners: formatOwners.call(this, node.parent, flagInfo),
|
if(node.parent) {
|
||||||
size: formatSize.call(this, f_h, node.parent, maxSizeWidth),
|
const parentNode: TreeNode = this.getFs().getNodeByINode(node.parent);
|
||||||
modt: formatModtime(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: '..'
|
name: '..'
|
||||||
}
|
}
|
||||||
: {
|
|
||||||
...current,
|
}
|
||||||
name: '..'
|
|
||||||
};
|
|
||||||
|
|
||||||
if (flagInfo.has('r')) {
|
if (flagInfo.has('r')) {
|
||||||
rows.push(LsEntryUtils.toString(parent), LsEntryUtils.toString(current));
|
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 {
|
function formatOwners(this: Bash, node: TreeNode, flag: any): string {
|
||||||
const owner: string = node.owner;
|
const owner: string = this.getUserByUid(node.owner).username;
|
||||||
const group: string = node.group;
|
const group: string = this.getGroupByGid(node.group).groupname;
|
||||||
|
|
||||||
if (flag.has('G') || flag.has('o')) {
|
if (flag.has('G') || flag.has('o')) {
|
||||||
if (flag.has('n')) {
|
if (flag.has('n'))
|
||||||
const uid: number = this.getUserByName(owner).uid;
|
return `${node.owner}`;
|
||||||
return `${uid}`;
|
|
||||||
}
|
|
||||||
return `${owner}`;
|
return `${owner}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flag.has('g')) {
|
if (flag.has('g')) {
|
||||||
if (flag.has('n')) {
|
if (flag.has('n'))
|
||||||
const gid: number = this.getGroupByName(group).gid;
|
return `${node.group}`;
|
||||||
return `${gid}`;
|
|
||||||
}
|
|
||||||
return `${group}`;
|
return `${group}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flag.has('n')) {
|
if (flag.has('n'))
|
||||||
const uid: number = this.getUserByName(owner).uid;
|
return `${node.owner} ${node.group}`;
|
||||||
const gid: number = this.getGroupByName(group).gid;
|
|
||||||
|
|
||||||
return `${uid} ${gid}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${owner} ${group}`;
|
return `${owner} ${group}`;
|
||||||
}
|
}
|
||||||
@@ -220,16 +214,17 @@ function formatSize(this: Bash, human: boolean, node: TreeNode, max: number): st
|
|||||||
|
|
||||||
function formatModtime(node: TreeNode): string {
|
function formatModtime(node: TreeNode): string {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const hours: string = node.modtime.getHours().toString().padStart(2, '0');
|
//TODO: Change this to be dynamic based on the --time value passed
|
||||||
const minutes: string = node.modtime.getMinutes().toString().padStart(2, '0');
|
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 =
|
const time: string =
|
||||||
now.getFullYear() === node.modtime.getFullYear()
|
now.getFullYear() === node.timestamps.mTime.getFullYear()
|
||||||
? `${hours}:${minutes}`
|
? `${hours}:${minutes}`
|
||||||
: node.modtime.getFullYear().toString();
|
: node.timestamps.mTime.getFullYear().toString();
|
||||||
|
|
||||||
return [
|
return [
|
||||||
months[node.modtime.getMonth()],
|
months[node.timestamps.mTime.getMonth()],
|
||||||
node.modtime.getDate().toString().padStart(2, ' '),
|
node.timestamps.mTime.getDate().toString().padStart(2, ' '),
|
||||||
`${time}`
|
`${time}`
|
||||||
].join(' ');
|
].join(' ');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import type { readonly } from 'svelte/store';
|
import type { readonly } from 'svelte/store';
|
||||||
import type { Permission, TimeStamps, User } from './bash';
|
import type { Permission, TimeStamps, User } from './bash';
|
||||||
import { Stack } from '../stack';
|
import { Stack } from '../stack';
|
||||||
import { Pause } from '@lucide/svelte';
|
|
||||||
|
|
||||||
export enum Type {
|
export enum Type {
|
||||||
Directory = 16384,
|
Directory = 16384,
|
||||||
File = 32768,
|
File = 32768,
|
||||||
SymblicLink = 40960
|
SymbolicLink = 40960
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NodePerms = {
|
export type NodePerms = {
|
||||||
@@ -25,6 +24,7 @@ export type TreeNode = {
|
|||||||
parent?: number;
|
parent?: number;
|
||||||
name: string;
|
name: string;
|
||||||
type: Type;
|
type: Type;
|
||||||
|
size: number; //Size in Bytes
|
||||||
children: number[];
|
children: number[];
|
||||||
content: string; // GUID of the cache file that contains the file contents.
|
content: string; // GUID of the cache file that contains the file contents.
|
||||||
link: number; // Links
|
link: number; // Links
|
||||||
|
|||||||
@@ -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) {
|
export enum SortBy {
|
||||||
qSort(array, 0, array.length - 1, reverse);
|
NAME,
|
||||||
|
INODE,
|
||||||
|
SIZE,
|
||||||
|
EXTENSION,
|
||||||
|
TYPE,
|
||||||
|
MTIME = 'mTime',
|
||||||
|
ATIME = 'aTime',
|
||||||
|
CTIME = 'cTime',
|
||||||
}
|
}
|
||||||
|
|
||||||
function qSort(array: TreeNode[], start: number, end: number, reverse: boolean) {
|
export class Sort {
|
||||||
if (end <= start) return;
|
|
||||||
|
|
||||||
let pivot: number = partition(array, start, end, reverse);
|
public static nodeArraySort(this: Bash, nodes: TreeNode[] | number[], reverse: boolean = false, sortBy: SortBy = SortBy.NAME) {
|
||||||
qSort(array, start, pivot - 1, reverse);
|
if(nodes.length === 0) throw new Error('Tried to sort an empty node array!');
|
||||||
qSort(array, pivot + 1, end, reverse);
|
const parsedNodes: TreeNode[] = [];
|
||||||
}
|
|
||||||
|
|
||||||
function partition(part: TreeNode[], start: number, end: number, reverse: boolean): number {
|
if(typeof nodes[0] === 'number') {
|
||||||
let pivot: TreeNode = part[end];
|
for(const inode of nodes as number[]) {
|
||||||
let i: number = start - 1;
|
const node = this.getFs().getNodeByINode(inode);
|
||||||
|
parsedNodes.push(node);
|
||||||
for (let j = start; j <= end; j++) {
|
}
|
||||||
if (compareStrings(part[j].name, pivot.name, reverse) < 0) {
|
Sort.nodeQSort(parsedNodes, reverse, sortBy, 0, parsedNodes.length - 1);
|
||||||
i++;
|
} else Sort.nodeQSort(nodes as TreeNode[], reverse, sortBy, 0, nodes.length - 1);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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. - `);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ function jsonToNodeTable(data: any, parent?: number): Map<number, TreeNode> {
|
|||||||
inode: object.Inode,
|
inode: object.Inode,
|
||||||
name: object.Name,
|
name: object.Name,
|
||||||
type: object.Type,
|
type: object.Type,
|
||||||
|
size: object.Size,
|
||||||
interactible: object.Interactible,
|
interactible: object.Interactible,
|
||||||
func: object.Func,
|
func: object.Func,
|
||||||
children: object.Children,
|
children: object.Children,
|
||||||
|
|||||||
Reference in New Issue
Block a user