Skip to content
This repository has been archived by the owner on Apr 13, 2024. It is now read-only.

Implement a which built-in #68

Merged
merged 4 commits into from
Mar 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/ansi-shell/pipeline/Pipeline.js
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@ export class Pipeline {
let cmdOut = pipe.in;
cmdOut = new ByteWriter({ delegate: cmdOut });
cmdCtx.externs.out = cmdOut;
cmdCtx.externs.commandProvider = ctx.externs.commandProvider;
nextIn = pipe.out;

// TODO: need to consider redirect from out to err
Expand Down
2 changes: 2 additions & 0 deletions src/puter-shell/coreutils/__exports__.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import module_true from './true.js'
import module_txt2img from './txt2img.js'
import module_usages from './usages.js'
import module_wc from './wc.js'
import module_which from './which.js'

export default {
"ai": module_ai,
Expand Down Expand Up @@ -95,4 +96,5 @@ export default {
"txt2img": module_txt2img,
"usages": module_usages,
"wc": module_wc,
"which": module_which,
};
75 changes: 75 additions & 0 deletions src/puter-shell/coreutils/which.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright (C) 2024 Puter Technologies Inc.
*
* This file is part of Phoenix Shell.
*
* Phoenix Shell is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Exit } from './coreutil_lib/exit.js';

export default {
name: 'which',
usage: 'which COMMAND...',
description: 'Look up each COMMAND, and return the path name of its executable.\n\n' +
'Returns 1 if any COMMAND is not found, otherwise returns 0.',
args: {
$: 'simple-parser',
allowPositionals: true,
options: {
'all': {
description: 'Return all matching path names of each COMMAND, not just the first',
type: 'boolean',
short: 'a',
},
},
},
execute: async ctx => {
const { out, err, commandProvider } = ctx.externs;
const { positionals, values } = ctx.locals;

let anyCommandsNotFound = false;

const printPath = async ( commandName, command ) => {
if (command.path) {
await out.write(`${command.path}\n`);
} else {
await out.write(`${commandName}: shell built-in command\n`);
}
};

for ( const commandName of positionals ) {
const result = values.all
? await commandProvider.lookupAll(commandName, { ctx })
: await commandProvider.lookup(commandName, { ctx });

if ( ! result ) {
anyCommandsNotFound = true;
await err.write(`${commandName} not found\n`);
continue;
}

if ( values.all ) {
for ( const command of result ) {
await printPath(commandName, command);
}
} else {
await printPath(commandName, result);
}
}

if ( anyCommandsNotFound ) {
throw new Exit(1);
}
}
};
9 changes: 9 additions & 0 deletions src/puter-shell/providers/BuiltinCommandProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,13 @@ export class BuiltinCommandProvider {
async lookup (id) {
return builtins[id];
}

// Only a single builtin can match a given name
async lookupAll (...a) {
const result = await this.lookup(...a);
if ( result ) {
return [ result ];
}
return undefined;
}
}
13 changes: 13 additions & 0 deletions src/puter-shell/providers/CompositeCommandProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,17 @@ export class CompositeCommandProvider {
}
}
}

async lookupAll (...a) {
const results = [];
for (const provider of this.providers) {
const commands = await provider.lookupAll(...a);
if ( commands ) {
results.push(...commands);
}
}

if ( results.length === 0 ) return undefined;
return results;
}
}
18 changes: 0 additions & 18 deletions src/puter-shell/providers/HostedCommandProvider.js

This file was deleted.

38 changes: 27 additions & 11 deletions src/puter-shell/providers/ScriptCommandProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,30 +22,46 @@ import { resolveRelativePath } from '../../util/path.js';

export class ScriptCommandProvider {
async lookup (id, { ctx }) {
const { filesystem } = ctx.platform;

const is_path = id.match(/^[.\/]/);
if ( ! is_path ) return undefined;

const absPath = resolveRelativePath(ctx.vars, id);

const { filesystem } = ctx.platform;
try {
await filesystem.stat(absPath);
// TODO: More rigorous check that it's an executable text file
} catch (e) {
return undefined;
}

const script_blob = await filesystem.read(absPath);
const script_text = await script_blob.text();
return {
path: id,
async execute (ctx) {
const script_blob = await filesystem.read(absPath);
const script_text = await script_blob.text();

console.log('result though?', script_text);
console.log('result though?', script_text);

// note: it's still called `parseLineForProcessing` but
// it has since been extended to parse the entire file
const ast = ctx.externs.parser.parseScript(script_text);
const statements = ast[0].statements;
// note: it's still called `parseLineForProcessing` but
// it has since been extended to parse the entire file
const ast = ctx.externs.parser.parseScript(script_text);
const statements = ast[0].statements;

return {
async execute (ctx) {
for (const stmt of statements) {
const pipeline = await Pipeline.createFromAST(ctx, stmt);
await pipeline.execute(ctx);
}
}
};
}

// Only a single script can match a given path
async lookupAll (...a) {
const result = await this.lookup(...a);
if ( result ) {
return [ result ];
}
return undefined;
}
}