//
// main.cpp
// DescriptionHere
//
// Created by John Ryland (jryland@xiaofrog.com) on 18/11/2017.
// Copyright 2017 InvertedLogic. All rights reserved.
//
#include "utils.h"
#include "filesystem.h"
#include "completions.h"
#include <thread>
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <csignal>
#include <cctype>
#include <vector>
#include <string>
#define hash(str) \
((str[0]<<24)|((str[0])?str[1]<<16:0)|((str[0]&&str[1])?str[2]<<8:0)|((str[0]&&str[1]&&str[2])?str[3]:0))
#define FOURCC(a,b,c,d) \
(a << 24 | b << 16 | c << 8 | d)
#define CASE_(str, a,b,c,d) \
}{ \
case FOURCC((a),(b),(c),(d)): if (strcmp(argv[0],str) == 0)
#define CASE(str) \
CASE_(str, str[0], (!str[0]) ? 0 : str[1], (!str[0] || !str[1]) ? 0 : str[2], (!str[0] || !str[1] || !str[2]) ? 0 : str[3])
typedef int (*command_t)(int argc, const char* argv[]);
class CommandLineWithCompletions : public FileTabCompletions
{
public:
CommandLineWithCompletions(FileSystem& fs)
: fileSystem(fs)
, FileTabCompletions(fs)
, inputProcessor(text, *this)
, input(kb, inputProcessor)
{
}
bool readLine(std::string& s)
{
showPrompt();
bool res = input.readLine(s);
// TODO: figure out where to show this - test 'cat' command with no arguments
//printf("\n");
return res;
}
void showPossibleCompletions(const std::vector<Completion>& completions)
{
// TODO: if first arg, also do tab completions from available commands
FileTabCompletions::showPossibleCompletions(completions);
showPrompt();
text.display();
fflush(stdout);
}
private:
void showPrompt()
{
printf("%s", (fileSystem.getCurrentDirectory() + "> ").c_str());
fflush(stdout);
}
FileSystem& fileSystem;
CommandLineText text;
RawKeyboard kb;
ReadLineWithCompletions inputProcessor;
public:
ReadLineKeyboard input;
};
class LineOutputInterface
{
public:
virtual ~LineOutputInterface() {}
virtual void putchar(int ch) = 0;
// int printf();
// int puts();
};
class Process
{
public:
LineInputInterface* input = nullptr;
LineOutputInterface* output = nullptr;
virtual void run()
{
}
};
class PipedInput : public LineInputInterface
{
public:
bool isOpen() { return true; }
bool readLine(std::string& str)
{
pipedSource->run();
return true;
}
Process* pipedSource;
};
struct Context
{
Context(int _argc, char** _argv, FileSystem& _fs, CommandLineWithCompletions& _c)
: argc(_argc), argv(_argv), fs(_fs), commandLine(_c)
{
}
// Need read / write to stdin/stdout here
// Process process;
// Environment -> map(string, string)
Context* parentCtx = nullptr;
int argc;
char** argv;
FileSystem& fs;
CommandLineWithCompletions& commandLine;
bool exiting = false;
int exitCode = 0;
};
int ls_cmd(Context& ctx, int argc, const char* argv[])
{
int argCount = (argc <= 1) ? 2 : argc;
for (int i = 1; i < argCount; i++)
{
std::string path = ".";
if (argc > 1)
path = argv[i];
if (argc > 2)
printf("%s%s:\n", (i!=1)?"\n":"", path.c_str());
// Doesn't do columns or colors
DirectoryEntrySet entries;
if (!ctx.fs.getDirectoryListing(entries, path))
printf("%s: %s: No such file or directory\n", ctx.fs.basename(argv[0]).c_str(), path.c_str());
for (int i = 0; i < entries.entries.size(); i++)
printf("%s\n", entries.entries[i].basename.c_str());
}
return 0;
}
int cat_cmd(Context& ctx, int argc, const char* argv[])
{
// Handles 0 args and reading from stdin. Also handles lists of files to cat
int argCount = (argc <= 1) ? 2 : argc;
for (int i = 1; i < argCount; i++)
{
LineBasedFile file((argc > 1) ? argv[i] : NULL);
LineInputInterface& inp = (argc > 1) ? (LineInputInterface&)file : ctx.commandLine.input;
if (!inp.isOpen()) {
printf("%s: %s: No such file or directory\n", ctx.fs.basename(argv[0]).c_str(), argv[i]);
continue;
}
std::string line;
while (inp.readLine(line))
{
printf("%s", line.c_str());
}
}
return 0;
}
int head_usage(const char* err, const char* in)
{
printf("head: %s -- %s\n", err, in);
printf("usage: head [-n lines | -c bytes] [file ...]\n");
return 1;
}
enum OpType
{
Flag,
Int,
Char,
Str,
List
};
struct CmdOption
{
bool optional;
bool mutuallyExclusiveWithLast;
char opCh;
const char* opStr;
const char* opError;
OpType opType;
const char* valueName;
void* defaultVal;
int minVal;
int maxVal;
void* result;
int resultLen;
const char* description;
bool wasSet;
};
int usage_from_opts(const char* argv0, int optC, CmdOption optV[])
{
printf("usage: %s ", argv0);
for (int x = 0; x < optC; x++)
{
bool lastOpt = (x+1 == optC);
bool mx = optV[x].mutuallyExclusiveWithLast;
bool nextMx = (lastOpt) ? false : optV[x + 1].mutuallyExclusiveWithLast;
if (mx) { printf("|%s", (optV[x].opCh != ' ') ? " " : ""); }
if (!mx && optV[x].optional) { printf("["); }
if (optV[x].opCh != ' ') { printf("-%c", optV[x].opCh); }
if (optV[x].opType != Flag) { printf("%s%s", (optV[x].opType != List && optV[x].valueName[0]) ? " " : "", optV[x].valueName); }
if (optV[x].opType == List) { printf(" ..."); }
if (!nextMx && optV[x].optional) { printf("]"); }
if (!lastOpt) { printf(" "); }
}
printf("\n");
return 1;
}
int parse_args(int optC, CmdOption optV[], int argc, const char* argv[])
{
int listIndex = -1;
for (int x = 0; x < optC; x++)
{
optV[x].result = optV[x].defaultVal;
optV[x].resultLen = 0;
optV[x].wasSet = false;
if (optV[x].opType == List)
{
if (listIndex != -1)
printf("warning, bad command opts specification\n");
listIndex = x;
}
}
for (int i = 1; i < argc; i++)
{
if (argv[i][0] == '-')
{
bool found = false;
char optCh = argv[i][1];
int j = 0;
int optX = 0;
if (optCh == '-' && argv[i][2])
{
for (int x = 0; x < optC; x++)
{
if (strcmp(&argv[i][2], optV[x].opStr) == 0)
{
optX = x;
found = true;
break;
}
}
}
else
{
for (int x = 0; x < optC; x++)
{
if (optCh == optV[x].opCh)
{
optX = x;
found = true;
if (argv[i][2] == 0)
i++;
else
j = 2;
break;
}
}
}
if (!found)
{
printf("%s: illegal option -- %s\n", argv[0], &argv[i][1]);
return usage_from_opts(argv[0], optC, optV);
}
else
{
if (i == argc) {
printf("%s: option requires an argument -- %s\n", argv[0], &argv[i-1][1]);
return usage_from_opts(argv[0], optC, optV);
}
/*
if (optV[optX].wasSet)
{
printf("%s: option already specified -- %s\n", argv[0], &argv[i-1][1]);
return usage_from_opts(argv[0], optC, optV);
}
*/
char* endp = nullptr;
int res = (int)strtol(&argv[i][j], &endp, 10);
optV[optX].result = reinterpret_cast<void*>(res);
optV[optX].wasSet = true;
if (endp[0] || res < optV[optX].minVal || res > optV[optX].maxVal) {
printf("%s: %s -- %s\n", argv[0], optV[optX].opError, &argv[i][j]);
return 1;
}
}
}
else
{
if (listIndex != -1)
{
optV[listIndex].result = &argv[i];
optV[listIndex].resultLen = argc - i;
break;
}
}
}
bool lastSet = false;
const char* lastSetOpt = "";
for (int x = 0; x < optC; x++)
{
if (!optV[x].mutuallyExclusiveWithLast)
lastSet = false;
if (optV[x].wasSet)
{
if (lastSet)
{
printf("%s: can't combine %s and %s options\n", argv[0], lastSetOpt, optV[x].valueName);
return usage_from_opts(argv[0], optC, optV);
}
lastSetOpt = optV[x].valueName;
lastSet = true;
}
}
return 0;
}
template <class T, std::size_t N>
constexpr std::size_t array_size(const T (&array)[N]) noexcept
{
return N;
}
int head_cmd(Context& ctx, int argc, const char* argv[])
{
CmdOption headOpts[] =
{
{ true, false, 'n', "", "illegal line count", Int, "lines", (void*)-1, 1, INT_MAX, nullptr, 0, "", false },
{ true, true, 'c', "", "illegal byte count", Int, "bytes", (void*)-1, 1, INT_MAX, nullptr, 0, "", false },
{ true, false, ' ', "", "", List, "file", nullptr, 0, 0, nullptr, 0, "", false }
};
if (parse_args(array_size(headOpts), headOpts, argc, argv) == 0)
{
int lineCount = (int)reinterpret_cast<size_t>(headOpts[0].result);
int byteCount = (int)reinterpret_cast<size_t>(headOpts[1].result);
char** files = (char**)headOpts[2].result;
int fileCount = headOpts[2].resultLen;
bool outputFileName = fileCount > 1;
// Default is a line count of 10 if nothing specified
if (byteCount == -1 && lineCount == -1)
{
lineCount = 10;
}
// Handles 0 args and reading from stdin. Also handles lists of files to cat
int argCount = (fileCount == 0) ? fileCount+1 : fileCount;
for (int i = 0; i < argCount; i++)
{
LineBasedFile file((fileCount) ? files[i] : NULL);
LineInputInterface& inp = (fileCount) ? (LineInputInterface&)file : ctx.commandLine.input;
if (!inp.isOpen()) {
printf("%s: %s: No such file or directory\n", ctx.fs.basename(argv[0]).c_str(), files[i]);
continue;
}
if (outputFileName)
{
printf("==> %s <==\n", files[i]);
}
std::string line;
int ln = 0;
int cc = 0;
while (inp.readLine(line))
{
cc += line.size();
if (byteCount != -1 && cc > byteCount) {
cc -= line.size();
std::string rem = line.substr(0, byteCount - cc);
printf("%s", rem.c_str());
break;
}
else
printf("%s", line.c_str());
ln++;
if (ln == lineCount)
break;
}
}
return 0;
}
return 1;
}
int cd_cmd(Context& ctx, int argc, const char* argv[])
{
if (argc > 1) {
if (strcmp(argv[1], "-") == 0) {
ctx.fs.popDirectory();
} else {
ctx.fs.changeDirectory(argv[1]);
}
} else {
ctx.fs.changeDirectory(getenv("HOME"));
}
return 0;
}
int exit_cmd(Context& ctx, int argc, const char* argv[])
{
int retCode = 0;
printf("\nExiting...\n");
if (argc > 1)
retCode = atoi(argv[1]);
ctx.exiting = true;
ctx.exitCode = retCode;
return retCode;
}
int basename_cmd(Context& ctx, int argc, const char* argv[])
{
if (argc < 2) {
// printf("usage: basename string [suffix]\n basename [-a] [-s suffix] string [...]\n");
printf("usage: basename string\n");
return 1;
}
printf("%s\n", ctx.fs.basename(argv[1]).c_str());
return 0;
}
int dirname_cmd(Context& ctx, int argc, const char* argv[])
{
if (argc < 2) {
printf("usage: dirname path\n");//, ctx.fs.basename(argv[0]).c_str());
return 1;
}
printf("%s\n", ctx.fs.dirname(argv[1]).c_str());
return 0;
}
int echo_cmd(Context& ctx, int argc, const char* argv[])
{
bool showNewLine = true;
if (argc > 1)
{
int firstArg = 1;
if (strcmp(argv[1], "-n") == 0) {
showNewLine = false;
firstArg = 2;
}
for (int i = firstArg; i < argc; i++)
{
if (i != firstArg)
{
printf(" ");
}
// TODO: expand variables
printf("%s", argv[i]);
}
}
if (showNewLine)
{
printf("\n");
}
return 0;
}
int true_cmd(Context& ctx, int argc, const char* argv[])
{
return 0;
}
int false_cmd(Context& ctx, int argc, const char* argv[])
{
return 1;
}
void process_date_format(time_t& tim, struct tm *dateTime, const char* format)
{
const char* daysOfWeek[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
const char* monthsOfYear[] = { "Janurary", "Feburary", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
while (*format)
{
if (*format == '%')
{
format++;
switch (*format)
{
case 0: break;
case 'n': printf("\n"); break; // newline
case 't': printf("\t"); break; // tab
case 'E': printf("\b"); break; // backspace
case 'O': printf("\b"); break; // backspace
case 'a': printf("%.3s", daysOfWeek[dateTime->tm_wday]); break; // short day of week
case 'w': printf("%i", dateTime->tm_wday); break; // day of week num
case 'd': printf("%02i", dateTime->tm_mday); break; // day of month
case 'e': printf("% 2i", dateTime->tm_mday); break; // day of month
case 'm': printf("%02i", dateTime->tm_mon + 1); break; // month
case 'b': printf("%.3s", monthsOfYear[dateTime->tm_mon]); break; // short month
case 'k': printf("%02i", dateTime->tm_hour); break; // 24 hour
case 'l': printf("%i", dateTime->tm_hour % 12); break; // 12 hour
case 'I': printf("%02i", dateTime->tm_hour % 12); break; // 12 hour
case 'p': printf("%s", (dateTime->tm_hour > 11) ? "pm" : "am"); break; // am/pm
case 'M': printf("%02i", dateTime->tm_min); break; // minute
case 'S': printf("%02i", dateTime->tm_sec); break; // second
case 's': printf("%llu", (unsigned long long)tim); break; // seconds from epoc
case 'A': printf("%s", daysOfWeek[dateTime->tm_wday]); break; // day of week
case 'B': printf("%s", monthsOfYear[dateTime->tm_mon]); break; // month
case 'Y': printf("%i", dateTime->tm_year + 1900); break; // year
case 'C': printf("%i", (dateTime->tm_year + 1900) / 100); break; // century
case 'y': printf("%i", (dateTime->tm_year + 1900) % 100); break; // short year
case 'i': printf("%i", dateTime->tm_yday + 1); break; // day from beginning of year (1 based)
case 'Z': printf("%s", dateTime->tm_zone); break; // timezone
// Compound formats
case 'r': process_date_format(tim, dateTime, "%l:%M:%S %p"); break;
case 'v': process_date_format(tim, dateTime, "%e-%b-%Y"); break;
case 'x': process_date_format(tim, dateTime, "%d/%m/%Y"); break;
case 'D': process_date_format(tim, dateTime, "%m/%d/%y"); break;
case 'F': process_date_format(tim, dateTime, "%Y-%m-%d"); break;
case 'R': process_date_format(tim, dateTime, "%k:%M"); break;
case 'T': process_date_format(tim, dateTime, "%k:%M:%S"); break;
case 'X': process_date_format(tim, dateTime, "%k:%M:%S"); break;
case 'c': process_date_format(tim, dateTime, "%a %e %b %k:%M:%S %Y"); break;
// TODO: check there might be some subtle difference to these:
case 'f': process_date_format(tim, dateTime, "%y"); break;
case 'g': process_date_format(tim, dateTime, "%y"); break;
case 'h': process_date_format(tim, dateTime, "%b"); break;
case 'j': process_date_format(tim, dateTime, "%i"); break;
case 'u': process_date_format(tim, dateTime, "%w"); break;
case 'G': process_date_format(tim, dateTime, "%Y"); break;
case 'H': process_date_format(tim, dateTime, "%k"); break;
case 'z': printf("+%i%02i", (int)dateTime->tm_gmtoff / 3600, ((int)dateTime->tm_gmtoff / 60) % 60); break; // tz offset
case 'U': printf("%i", (((7+dateTime->tm_yday) - dateTime->tm_wday)/7)); break; // week of year - Sunday start - starts with week 0
case 'W': printf("%i", (((7+dateTime->tm_yday) - ((dateTime->tm_wday+6)%7))/7)); break; // week of year - Monday start - starts with week 0
case 'V': printf("%i", (dateTime->tm_yday - (((dateTime->tm_wday+6)%7) + 1) + 10) / 7); break; // ISO week, TODO: incorrect around start/end of year
default: printf("%c", *format); break;
}
if (*format == 0)
break;
}
else
{
printf("%c", *format);
}
format++;
}
}
int date_cmd(Context& ctx, int argc, const char* argv[])
{
/*
date: illegal option -- -
usage: date [-jnRu] [-d dst] [-r seconds] [-t west] [-v[+|-]val[ymwdHMS]] ...
[-f fmt date | [[[mm]dd]HH]MM[[cc]yy][.ss]] [+format]
The options are as follows:
-d dst Set the kernel's value for daylight saving time. If dst is non-zero, future calls to gettimeofday(2) will return a non-zero for tz_dsttime.
-f Use input_fmt as the format string to parse the new_date provided rather than using the default [[[mm]dd]HH]MM[[cc]yy][.ss] format. Parsing is done using strptime(3).
-j Do not try to set the date. This allows you to use the -f flag in addition to the + option to convert one date format to another.
-n By default, if the timed(8) daemon is running, date sets the time on all of the machines in the local group. The -n option suppresses this behavior and causes the
time to be set only on the current machine.
-R Use RFC 2822 date and time output format. This is equivalent to use ``%a, %d %b %Y %T %z'' as output_fmt while LC_TIME is set to the ``C'' locale .
-r seconds
Print the date and time represented by seconds, where seconds is the number of seconds since the Epoch (00:00:00 UTC, January 1, 1970; see time(3)), and can be speci-
fied in decimal, octal, or hex.
-r filename
Print the date and time of the last modification of filename.
-t minutes_west
Set the system's value for minutes west of GMT. minutes_west specifies the number of minutes returned in tz_minuteswest by future calls to gettimeofday(2).
-u Display or set the date in UTC (Coordinated Universal) time.
-v Adjust (i.e., take the current date and display the result of the adjustment; not actually set the date) the second, minute, hour, month day, week day, month or year
according to val. If val is preceded with a plus or minus sign, the date is adjusted forwards or backwards according to the remaining string, otherwise the relevant
*/
CmdOption dateOpts[] =
{
{ true, false, 'j', "", "", Flag, "do not try to set the date", nullptr, 0, 0, nullptr, 0, "", false },
{ true, false, 'n', "", "", Flag, "set the date only on the current machine", nullptr, 0, 0, nullptr, 0, "", false },
{ true, false, 'R', "", "", Flag, "use RFC 2822 date and time output format", nullptr, 0, 0, nullptr, 0, "", false },
{ true, false, 'u', "", "", Flag, "display or set the date in UTC time", nullptr, 0, 0, nullptr, 0, "", false },
//{ true, false, 'd', "", "", Str, "dst", nullptr, 0, 0, nullptr, 0, "", false },
{ true, false, 'r', "", "", Int, "seconds", nullptr, 0, INT_MAX, nullptr, 0, "", false }, // if arg is int, seconds, if arg is file, modification time
//{ true, false, 't', "", "", Int, "west", nullptr, 0, INT_MAX, nullptr, 0, "", false },
{ true, false, 'v', "", "", Str, "[+|-]val[ymwdHMS]", nullptr, 0, 0, nullptr, 0, "", false }, // how much to adjust the time by
{ true, false, 'f', "", "", Str, "fmt date", nullptr, 0, 0, nullptr, 0, "", false },
{ true, true, ' ', "", "", Str, "[[[mm]dd]HH]MM[[cc]yy][.ss]", nullptr, 0, 0, nullptr, 0, "", false },
{ true, false, '+', "", "", List, "output_fmt", nullptr, 0, 0, nullptr, 0, "", false },
};
if (parse_args(array_size(dateOpts), dateOpts, argc, argv) == 0)
{
time_t tim = time(0);
//struct tm *dateTime = gmtime(&tim);
struct tm *dateTime = localtime(&tim);
const char* defaultFormatStr = "%a %e %b %Y %H:%M:%S %Z";
const char* format = defaultFormatStr;
// "-%a- -%b- -%c- -%d- -%e- -%g- -%h- -%j- -%k- -%l- -%m- -%n- -%p- -%r- -%s- -%t- -%u- -%v- -%w- -%x- -%y- -%z- -%A- -%B- -%C- -%D- -%E- -%F- -%G- -%H- -%I- -%J- -%K- -%L- -%M- -%N- -%O- -%P- -%Q- -%R- -%S- -%T- -%U- -%V- -%W- -%X- -%Y- -%Z-";
process_date_format(tim, dateTime, format);
printf("\n");
return 1;
}
return 0;
}
int sleep_cmd(Context& ctx, int argc, const char* argv[])
{
if (argc != 2) {
printf("usage: %s seconds\n", argv[0]);
return 1;
}
int secs = atoi(argv[1]);
std::this_thread::sleep_for(std::chrono::seconds(secs));
return 0;
}
int for_cmd(Context& ctx, int argc, const char* argv[])
{
printf("got for\n");
return 0;
}
int if_cmd(Context& ctx, int argc, const char* argv[])
{
printf("got if\n");
return 0;
}
int shell_cmd(Context& ctx, int argc, const char* argv[])
{
printf("got shell\n");
return 0;
}
int getopts_cmd(Context& ctx, int argc, const char* argv[])
{
/*
Johns-MacBook-Pro:mini-shell jryland$ OPTIND=0
Johns-MacBook-Pro:mini-shell jryland$ getopts S:T blah -S 123 -T ; echo $blah $OPTARG $OPTERR $OPTIND
S 123 0 3
Johns-MacBook-Pro:mini-shell jryland$ getopts S:T blah -S 123 -T ; echo $blah $OPTARG $OPTERR $OPTIND
T 0 4
Johns-MacBook-Pro:mini-shell jryland$ getopts S:T blah -S 123 -T ; echo $blah $OPTARG $OPTERR $OPTIND
? 0 4
*/
return 0;
}
int longcommand_cmd(Context& ctx, int argc, const char* argv[])
{
printf("got longcommand\n");
return 0;
}
// Built-in commands
#define COMMAND_LIST \
DO_COMMAND("ls", ls_cmd) \
DO_COMMAND("cat", cat_cmd) \
DO_COMMAND("cd", cd_cmd) \
DO_COMMAND("date", date_cmd) \
DO_COMMAND("echo", echo_cmd) \
DO_COMMAND("exit", exit_cmd) \
DO_COMMAND("head", head_cmd) \
DO_COMMAND("sleep", sleep_cmd) \
DO_COMMAND("true", true_cmd) \
DO_COMMAND("false", false_cmd) \
DO_COMMAND("help", help_cmd) \
DO_COMMAND("for", for_cmd) \
DO_COMMAND("if", if_cmd) \
DO_COMMAND("basename", basename_cmd) \
DO_COMMAND("dirname", dirname_cmd) \
DO_COMMAND("shell", shell_cmd) \
DO_COMMAND("longcommand", longcommand_cmd)
int help_cmd(Context& ctx, int argc, const char* argv[])
{
printf(" Command list:\n");
#define DO_COMMAND(str, cmd) \
printf(" %s\n", str);
COMMAND_LIST
#undef DO_COMMAND
return 0;
}
int exec_command(Context& ctx, int argc, const char* argv[])
{
int ret = 0;
if (argc > 0)
// TODO: make the command execution logic more extensible
switch (hash(argv[0]))
{{
#define DO_COMMAND(str, cmd) \
CASE(str) \
{ \
ret = cmd(ctx, argc, argv); \
break; \
}
COMMAND_LIST
#undef DO_COMMAND
default:
{
printf("-%s: %s: command not found\n", ctx.fs.basename(ctx.argv[0]).c_str(), argv[0]);
}
}}
return ret;
}
/*
TODO:
----
run processes in threads
handle Ctrl-C etc to terminate a process (key thread)
job control
pipe commands together, synchronization etc
Supported:
----------
dirname
echo
head
true
false
sleep
Partially Supported:
--------------------
basename
cat
cd
ls
date
Not-implemented:
----------------
easy:
unalias, alias, pwd, sort, split, join, fold, tr, paste, strings, uniq, wc, who, du, df, cut,
hard:
vi, awk, diff, patch, sed, lex, make, mailx, ed, crontab, nm, yacc
process control:
bg, fg, jobs, kill, nice, renice, nohup, ps, wait
file commands:
cp, find, ln, mkdir, mkfifo, mv, rm, rmdir, chmod, chgrp, chown, touch,
Other:
ar, asa, at, batch, bc, cal, cksum, cmp, comm, command, compress, csplit, dd, env, ex, expand, expr, fc, file,
gencat, getconf, getopts, grep, hash, iconv, id, ipcrm, icps, locale, localedef, logger, logname, lp, man, mesg,
more, newgrp, nl, od, pathchk, pax, pr, printf, read, sh, stty, tabs, tail, talk, tee, test, time, tput, tsort,
tty, type, ulimit, umask, uname, uncompress, unexpand, uudecode, uuencode, write, xargs, zcat
built-in commands
if, then, else, elif, fi
case, esac
select in
while in
until
for in
do
done
function id { commands ; }
continue
break
return
time
()
{}
;
|
||
&&
&
[
[[]]
var=...
alias x='...'
eval
print
command posix/unix command list:
common env variables:
HOME
IFS
PATH
USERNAME
PWD
EDITOR
PS1
SHELL
VISUAL
TERM
echo
*/
int main(int argc, char *argv[])
{
FileSystem fs;
CommandLineWithCompletions commandLine(fs);
Context ctx(argc, argv, fs, commandLine);
char IFS[64];
std::string rdLine;
while (commandLine.readLine(rdLine))
{
// Strip trailing newline
if (rdLine.size() && rdLine[rdLine.size()-1] == '\n')
rdLine = rdLine.substr(0, rdLine.size()-1);
std::vector<std::string> args = strSplit(rdLine.c_str(), ' ');
int argc = args.size();
std::vector<const char*> argvVec;
for (int i = 0; i < argc; i++)
argvVec.push_back(args[i].c_str());
const char** argv = &argvVec[0];
exec_command(ctx, argc, argv);
if (ctx.exiting)
return ctx.exitCode;
/*
printf("got: -%s-, split: ", line);
for (int i = 0; i < args.size(); i++)
printf(" %s, ", args[i].c_str());
printf("\n");
*/
}
}