wip forking, added sash source

This commit is contained in:
tcsullivan 2018-10-18 18:24:43 -04:00
parent 6b5dfe3c09
commit f4149952ea
25 changed files with 11511 additions and 45 deletions

View File

@ -22,11 +22,6 @@
#define HEAP_ALIGN 4
typedef struct {
uint32_t size;
void *next;
} __attribute__ ((packed)) alloc_t;
static alloc_t *free_blocks;
static void *heap_end;

View File

@ -23,6 +23,11 @@
#include <stdint.h>
typedef struct {
uint32_t size;
void *next;
} __attribute__ ((packed)) alloc_t;
/**
* Initializes memory management of the given heap.
* No overflow stuff is done, so... be careful.

View File

@ -56,5 +56,5 @@ void init_idle(void)
task_start(user_main, 4096);
while (1)
delay(100);
delay(10);
}

View File

@ -26,6 +26,7 @@
extern void gpio_svc(uint32_t *);
extern void clock_svc(uint32_t *);
extern void task_svc(uint32_t *);
void svc_handler(uint32_t *args)
{
@ -41,6 +42,11 @@ void svc_handler(uint32_t *args)
break;
case 2:
clock_svc(args);
break;
case 3:
task_svc(args);
asm("mov r0, %0" :: "r" (args[0]));
break;
default:
break;
}

View File

@ -26,6 +26,13 @@
task_t *current, *prev;
static uint8_t task_disable = 0;
int task_fork(uint32_t sp);
void task_svc(uint32_t *args)
{
int result = task_fork(args[0]);
args[0] = result;
}
void task_hold(uint8_t hold)
{
if (hold != 0)
@ -119,11 +126,12 @@ void task_init(void (*init)(void), uint16_t stackSize)
// bit 0 - priv, bit 1 - psp/msp
asm("\
isb; \
cpsie i; \
mov r0, sp; \
msr psp, r0; \
mrs r0, control; \
orr r0, r0, #3; \
cpsie i; \
msr control, r0; \
");
@ -140,31 +148,41 @@ void task_start(void (*task)(void), uint16_t stackSize)
task_hold(0);
}
/*int fork_ret(void)
int task_fork_ret(void)
{
return 1;
return 0;
}
int fork(void)
// Return 0 for child, non-zero for parent
int task_fork(uint32_t sp)
{
void (*pc)(void) = (void (*)(void))((uint32_t)fork_ret & ~(3));
task_hold(1);
// duplicate task info
alloc_t *heapInfo = (alloc_t *)(current->stack - 2);
task_t *t = task_create(pc, heapInfo->size);
memcpy(t->stack, current->stack, heapInfo->size);
uint32_t *sp;
asm("mov %0, sp" : "=r" (sp));
t->sp = t->stack + (sp - current->stack);
// 1. Get a PC for the child
void (*pc)(void) = (void (*)(void))((uint32_t)task_fork_ret);
t->next = current->next;
current->next = t;
current = t;
// 2. Prepare child task
alloc_t *stackInfo = (alloc_t *)(((uint8_t *)current->stack)
- sizeof(alloc_t));
task_t *childTask = task_create(pc, stackInfo->size - 8);
for (uint32_t i = 0; i < (stackInfo->size - 8); i++)
childTask->stack[i] = current->stack[i];
//uint32_t *sp;
//asm("mov %0, sp" : "=r" (sp));
childTask->sp = (uint32_t *)((uint32_t)childTask->stack + (sp
- (uint32_t)current->stack));
// 3. Insert child into task chain
childTask->next = current->next;
current->next = childTask;
// 4. Re-enable scheduler, make change happen
task_hold(0);
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
return 0;
}*/
return 1;
}
__attribute__ ((naked))
void PendSV_Handler(void)

31
src/sash/CHANGES Normal file
View File

@ -0,0 +1,31 @@
These are the major changes from version 3.7 to version 3.8:
The Makefile has been updated for several distribution's standards.
The ext2_fs include file location has been changed.
Some compiler warnings were fixed.
The -ls command has the -n option to print numeric user and group ids.
The -n option might be needed in case the unsuppressable dynamic
linking used to lookup the names fails.
The -chroot, -pivot_root, and -losetup commands have been added.
The exit status for commands has been implemented (such as -exit).
Thanks to Tollef Fog Heen for the patches.
These are the major changes from version 3.6 to version 3.7:
A few bugs in the dd command have been fixed.
These are the major changes from version 3.5 to version 3.6:
The -mount and -umount commands have been modified to work for both
Linux and BSD systems. Thanks to Wilbern Cobb for the patches.
The -e and -s options for the -mount command have been added.
The -f option was added to the command line so that script files
using sash can be executed.

69
src/sash/Makefile Normal file
View File

@ -0,0 +1,69 @@
#
# Makefile for sash
#
# The HAVE_GZIP definition adds the -gzip and -gunzip commands.
# The HAVE_LINUX_ATTR definition adds the -chattr and -lsattr commands.
# The HAVE_LINUX_CHROOT definition adds the -chroot command.
# The HAVE_LINUX_PIVOT definition adds the -pivot_root command.
# The HAVE_LINUX_LOSETUP definition adds the -losetup command.
# The HAVE_LINUX_MOUNT definition makes -mount and -umount work on Linux.
# The HAVE_BSD_MOUNT definition makes -mount and -umount work on BSD.
# The MOUNT_TYPE definition sets the default file system type for -mount.
#
# Note that the linker may show warnings about 'statically linked
# programs' requiring getpwnam, getpwuid, getgrnam and getgrgid.
# This is unavoidable since those routines use dynamic libraries anyway.
# Sash will still run, but if there are shared library problems then
# the user might have to be be careful when using the -chown, -chgrp,
# and -ls commands.
#
CC=arm-none-eabi-gcc -mcpu=cortex-m4 -mthumb
#HAVE_GZIP = 1
#HAVE_LINUX_ATTR = 1
#HAVE_LINUX_CHROOT = 1
#HAVE_LINUX_LOSETUP = 1
#HAVE_LINUX_PIVOT = 1
#HAVE_LINUX_MOUNT = 1
#HAVE_BSD_MOUNT = 0
MOUNT_TYPE = '""'
OPT = -O3 -ffreestanding
CFLAGS = $(OPT) -Wall -Wmissing-prototypes \
-DMOUNT_TYPE=$(MOUNT_TYPE) #\
-DHAVE_GZIP=$(HAVE_GZIP) \
-DHAVE_LINUX_ATTR=$(HAVE_LINUX_ATTR) \
-DHAVE_LINUX_CHROOT=$(HAVE_LINUX_CHROOT) \
-DHAVE_LINUX_LOSETUP=$(HAVE_LINUX_LOSETUP) \
-DHAVE_LINUX_PIVOT=$(HAVE_LINUX_PIVOT) \
-DHAVE_LINUX_MOUNT=$(HAVE_LINUX_MOUNT) \
-DHAVE_BSD_MOUNT=$(HAVE_BSD_MOUNT) \
#LDFLAGS = -static
#LIBS = -lz
DESTDIR =
BINDIR = /bin
MANDIR = /usr/man
#OBJS = sash.o cmds.o cmd_dd.o cmd_ed.o cmd_grep.o cmd_ls.o cmd_tar.o \
cmd_gzip.o cmd_find.o cmd_file.o cmd_chattr.o cmd_ar.o utils.o
OBJS = sash.o cmds.o cmd_ls.o utils.o
sash: $(OBJS)
$(CC) $(LDFLAGS) -o sash $(OBJS) $(LIBS)
arm-none-eabi-strip sash
clean:
rm -f $(OBJS) sash
install: sash
cp sash $(DESTDIR)/$(BINDIR)/sash
cp sash.1 $(DESTDIR)/$(MANDIR)/man1/sash.1
$(OBJS): sash.h

17
src/sash/README Normal file
View File

@ -0,0 +1,17 @@
This is release 3.8 of sash, my stand-alone shell for Linux or other systems.
The purpose of this program is to make system recovery possible in
many cases where there are missing shared libraries or executables.
It does this by firstly being linked statically, and secondly by
including versions of many of the standard utilities within itself.
Read the sash.1 documentation for more details.
Type "make install" to build and install the program and man page.
Several options in the Makefile can be changed to build sash for
other UNIX-like systems. In particular, dependencies on Linux file
systems can be removed and the mount command can be configured.
David I. Bell
dbell@tip.net.au
8 March 2014

1019
src/sash/cmd_ar.c Normal file

File diff suppressed because it is too large Load Diff

265
src/sash/cmd_chattr.c Normal file
View File

@ -0,0 +1,265 @@
/*
* Copyright (c) 2014 by David I. Bell
* Permission is granted to use, distribute, or modify this source,
* provided that this copyright notice remains intact.
*
* The "chattr" and "lsattr" built-in commands.
* These commands are optionally built into sash.
* They manipulate the important ext2 file system file attribute flags.
*/
#if HAVE_LINUX_ATTR
#include <sys/ioctl.h>
#include <sys/types.h>
/*
* These were used for old linux versions.
* #include <linux/fs.h>
* #include <linux/ext2_fs.h>
*/
#include <ext2fs/ext2_fs.h>
#include "sash.h"
/*
* The chattr command.
* This can turn on or off the immutable and append-only ext2 flags.
*/
int
do_chattr(int argc, const char ** argv)
{
const char * fileName;
const char * options;
int * flagPointer;
int offFlags;
int onFlags;
int oldFlags;
int newFlags;
int fd;
int r;
r = 0;
argc--;
argv++;
/*
* Parse the options.
*/
onFlags = 0;
offFlags = 0;
while ((**argv == '-') || (**argv == '+'))
{
options = *argv++;
argc--;
/*
* Point at the proper flags to be modified.
*/
if (*options++ == '+')
flagPointer = &onFlags;
else
flagPointer = &offFlags;
/*
* Parse the flag characters.
*/
while (*options)
{
switch (*options++)
{
case 'i':
*flagPointer |= EXT2_IMMUTABLE_FL;
break;
case 'a':
*flagPointer |= EXT2_APPEND_FL;
break;
default:
fprintf(stderr, "Unknown flag '%c'\n",
options[-1]);
return 1;
}
}
}
/*
* Make sure that the attributes are reasonable.
*/
if ((onFlags == 0) && (offFlags == 0))
{
fprintf(stderr, "No attributes specified\n");
return 1;
}
if ((onFlags & offFlags) != 0)
{
fprintf(stderr, "Inconsistent attributes specified\n");
return 1;
}
/*
* Make sure there are some files to affect.
*/
if (argc <= 0)
{
fprintf(stderr, "No files specified for setting attributes\n");
return 1;
}
/*
* Iterate over all of the file names.
*/
while (argc-- > 0)
{
fileName = *argv++;
/*
* Open the file name.
*/
fd = open(fileName, O_RDONLY | O_NONBLOCK);
if (fd < 0)
{
perror(fileName);
r = 1;
continue;
}
/*
* Read the current ext2 flag bits.
*/
if (ioctl(fd, EXT2_IOC_GETFLAGS, &oldFlags) < 0)
{
perror(fileName);
r = 1;
(void) close(fd);
continue;
}
/*
* Modify the flag bits as specified.
*/
newFlags = oldFlags;
newFlags |= onFlags;
newFlags &= ~offFlags;
/*
* If the flags aren't being changed, then close this
* file and continue.
*/
if (newFlags == oldFlags)
{
(void) close(fd);
continue;
}
/*
* Set the new ext2 flag bits.
*/
if (ioctl(fd, EXT2_IOC_SETFLAGS, &newFlags) < 0)
{
perror(fileName);
r = 1;
(void) close(fd);
continue;
}
/*
* Close the file.
*/
(void) close(fd);
}
return r;
}
/*
* The lsattr command.
* This lists the immutable and append-only ext2 flags.
*/
int
do_lsattr(int argc, const char ** argv)
{
const char * fileName;
int r;
int fd;
int status;
int flags;
char string[4];
r = 0;
argc--;
argv++;
/*
* Iterate over all of the file names.
*/
while (argc-- > 0)
{
fileName = *argv++;
/*
* Open the file name.
*/
fd = open(fileName, O_RDONLY | O_NONBLOCK);
if (fd < 0)
{
perror(fileName);
r = 1;
continue;
}
/*
* Read the ext2 flag bits.
*/
status = ioctl(fd, EXT2_IOC_GETFLAGS, &flags);
/*
* Close the file and check the status.
*/
(void) close(fd);
if (status < 0)
{
perror(fileName);
r = 1;
continue;
}
/*
* Build up the string according to the flags.
* This is 'i' for immutable, and 'a' for append only.
*/
string[0] = ((flags & EXT2_IMMUTABLE_FL) ? 'i' : '-');
string[1] = ((flags & EXT2_APPEND_FL) ? 'a' : '-');
string[2] = '\0';
/*
* Print the flags and the file name.
*/
printf("%s %s\n", string, fileName);
}
return r;
}
#endif
/* END CODE */

372
src/sash/cmd_dd.c Normal file
View File

@ -0,0 +1,372 @@
/*
* Copyright (c) 2014 by David I. Bell
* Permission is granted to use, distribute, or modify this source,
* provided that this copyright notice remains intact.
*
* The "dd" built-in command.
*/
#include "sash.h"
#define PAR_NONE 0
#define PAR_IF 1
#define PAR_OF 2
#define PAR_BS 3
#define PAR_COUNT 4
#define PAR_SEEK 5
#define PAR_SKIP 6
typedef struct
{
const char * name;
int value;
} PARAM;
static const PARAM params[] =
{
{"if", PAR_IF},
{"of", PAR_OF},
{"bs", PAR_BS},
{"count", PAR_COUNT},
{"seek", PAR_SEEK},
{"skip", PAR_SKIP},
{NULL, PAR_NONE}
};
static long getNum(const char * cp);
int
do_dd(int argc, const char ** argv)
{
const char * str;
const PARAM * par;
const char * inFile;
const char * outFile;
char * cp;
int inFd;
int outFd;
int inCc;
int outCc;
int blockSize;
long count;
long seekVal;
long skipVal;
long inFull;
long inPartial;
long outFull;
long outPartial;
char * buf;
char localBuf[BUF_SIZE];
int r;
inFile = NULL;
outFile = NULL;
seekVal = 0;
skipVal = 0;
blockSize = 512;
count = -1;
r = 0;
while (--argc > 0)
{
str = *++argv;
cp = strchr(str, '=');
if (cp == NULL)
{
fprintf(stderr, "Bad dd argument\n");
return 1;
}
*cp++ = '\0';
for (par = params; par->name; par++)
{
if (strcmp(str, par->name) == 0)
break;
}
switch (par->value)
{
case PAR_IF:
if (inFile)
{
fprintf(stderr, "Multiple input files illegal\n");
return 1;
}
inFile = cp;
break;
case PAR_OF:
if (outFile)
{
fprintf(stderr, "Multiple output files illegal\n");
return 1;
}
outFile = cp;
break;
case PAR_BS:
blockSize = getNum(cp);
if (blockSize <= 0)
{
fprintf(stderr, "Bad block size value\n");
return 1;
}
break;
case PAR_COUNT:
count = getNum(cp);
if (count < 0)
{
fprintf(stderr, "Bad count value\n");
return 1;
}
break;
case PAR_SEEK:
seekVal = getNum(cp);
if (seekVal < 0)
{
fprintf(stderr, "Bad seek value\n");
return 1;
}
break;
case PAR_SKIP:
skipVal = getNum(cp);
if (skipVal < 0)
{
fprintf(stderr, "Bad skip value\n");
return 1;
}
break;
default:
fprintf(stderr, "Unknown dd parameter\n");
return 1;
}
}
if (inFile == NULL)
{
fprintf(stderr, "No input file specified\n");
return 1;
}
if (outFile == NULL)
{
fprintf(stderr, "No output file specified\n");
return 1;
}
buf = localBuf;
if (blockSize > sizeof(localBuf))
{
buf = malloc(blockSize);
if (buf == NULL)
{
fprintf(stderr, "Cannot allocate buffer\n");
return 1;
}
}
inFull = 0;
inPartial = 0;
outFull = 0;
outPartial = 0;
inFd = open(inFile, 0);
if (inFd < 0)
{
perror(inFile);
if (buf != localBuf)
free(buf);
return 1;
}
outFd = creat(outFile, 0666);
if (outFd < 0)
{
perror(outFile);
close(inFd);
if (buf != localBuf)
free(buf);
return 1;
}
if (skipVal)
{
if (lseek(inFd, skipVal * blockSize, 0) < 0)
{
while (skipVal-- > 0)
{
inCc = read(inFd, buf, blockSize);
if (inCc < 0)
{
perror(inFile);
r = 1;
goto cleanup;
}
if (inCc == 0)
{
fprintf(stderr, "End of file while skipping\n");
r = 1;
goto cleanup;
}
}
}
}
if (seekVal)
{
if (lseek(outFd, seekVal * blockSize, 0) < 0)
{
perror(outFile);
r = 1;
goto cleanup;
}
}
inCc = 0;
while (((count < 0) || (inFull + inPartial < count)) &&
(inCc = read(inFd, buf, blockSize)) > 0)
{
if (inCc < blockSize)
inPartial++;
else
inFull++;
cp = buf;
if (intFlag)
{
fprintf(stderr, "Interrupted\n");
r = 1;
goto cleanup;
}
while (inCc > 0)
{
outCc = write(outFd, cp, inCc);
if (outCc < 0)
{
perror(outFile);
r = 1;
goto cleanup;
}
if (outCc < blockSize)
outPartial++;
else
outFull++;
cp += outCc;
inCc -= outCc;
}
}
if (inCc < 0)
perror(inFile);
cleanup:
close(inFd);
if (close(outFd) < 0)
{
perror(outFile);
r = 1;
}
if (buf != localBuf)
free(buf);
printf("%ld+%ld records in\n", inFull, inPartial);
printf("%ld+%ld records out\n", outFull, outPartial);
return r;
}
/*
* Read a number with a possible multiplier.
* Returns -1 if the number format is illegal.
*/
static long
getNum(const char * cp)
{
long value;
if (!isDecimal(*cp))
return -1;
value = 0;
while (isDecimal(*cp))
value = value * 10 + *cp++ - '0';
switch (*cp++)
{
case 'k':
value *= 1024;
break;
case 'b':
value *= 512;
break;
case 'w':
value *= 2;
break;
case '\0':
return value;
default:
return -1;
}
if (*cp)
return -1;
return value;
}
/* END CODE */

1440
src/sash/cmd_ed.c Normal file

File diff suppressed because it is too large Load Diff

235
src/sash/cmd_file.c Normal file
View File

@ -0,0 +1,235 @@
/*
* Copyright (c) 2014 by David I. Bell
* Permission is granted to use, distribute, or modify this source,
* provided that this copyright notice remains intact.
*
* The "file" built-in command.
*/
#include <ctype.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "sash.h"
static const char * checkFile(const char * name);
int
do_file(int argc, const char ** argv)
{
const char * name;
const char * info;
argc--;
argv++;
while (argc-- > 0)
{
name = *argv++;
info = checkFile(name);
if (info == NULL)
info = "No information available";
printf("%s: %s\n", name, info);
}
return 0;
}
/*
* Examine the specified file and return a static string which
* describes the file type. Returns NULL on a failure.
*/
static const char *
checkFile(const char * name)
{
int mode;
int fd;
int cc;
int i;
int ch;
int badCount;
char * cp;
struct stat statBuf;
char data[8192];
static char info[1024];
cp = info;
*cp = '\0';
if (lstat(name, &statBuf) < 0)
{
if (errno == ENOENT)
return "non-existent";
sprintf(cp, "stat failed: %s", strerror(errno));
return info;
}
/*
* Check the file type.
*/
mode = statBuf.st_mode;
if (S_ISDIR(mode))
return "directory";
if (S_ISCHR(mode))
return "character device";
if (S_ISBLK(mode))
return "block device";
if (S_ISFIFO(mode))
return "named pipe";
#ifdef S_ISLNK
if (S_ISLNK(mode))
return "symbolic link";
#endif
#ifdef S_ISSOCK
if (S_ISSOCK(mode))
return "socket";
#endif
/*
* If the file is not a regular file mention that.
*/
if (!S_ISREG(mode))
{
sprintf(cp, "unknown mode 0x%x, \n", mode);
cp += strlen(cp);
}
/*
* Check for an executable file.
*/
if ((mode & (S_IEXEC | S_IXGRP | S_IXOTH)) != 0)
{
strcpy(cp, "executable, ");
cp += strlen(cp);
}
/*
* The file is a normal file.
* Open it if we can and read in the first block.
*/
fd = open(name, O_RDONLY);
if (fd < 0)
{
sprintf(cp, "unreadable: %s", strerror(errno));
return info;
}
cc = read(fd, data, sizeof(data));
if (cc < 0)
{
sprintf(cp, "read error: %s", strerror(errno));
(void) close(fd);
return info;
}
(void) close(fd);
/*
* Check for an empty file.
*/
if (cc == 0)
{
strcpy(cp, "empty file");
return info;
}
/*
* Check for a script file.
*/
if ((cc > 2) && (data[0] == '#') && (data[1] == '!'))
{
char * begin;
char * end;
data[sizeof(data) - 1] = '\0';
begin = &data[2];
while (*begin == ' ')
begin++;
end = begin;
while (*end && (*end != ' ') && (*end != '\n'))
end++;
*end = '\0';
sprintf(cp, "script for \"%s\"", begin);
return info;
}
/*
* Check for special binary data types.
*/
if ((data[0] == '\037') && (data[1] == '\235'))
return "compressed file";
if ((data[0] == '\037') && (data[1] == '\213'))
return "GZIP file";
if ((data[0] == '\177') && (memcmp(&data[1], "ELF", 3) == 0))
{
strcpy(cp, "ELF program");
return info;
}
/*
* Check for binary data.
*/
badCount = 0;
for (i = 0; i < cc; i++)
{
ch = data[i];
if ((ch == '\n') || (ch == '\t'))
continue;
if (isspace(ch) || isprint(ch))
continue;
badCount++;
}
if (badCount != 0)
{
strcpy(cp, "binary");
return info;
}
/*
* It is just a text file.
*/
strcpy(cp, "text file");
return info;
}
/* END CODE */

376
src/sash/cmd_find.c Normal file
View File

@ -0,0 +1,376 @@
/*
* Copyright (c) 2014 by David I. Bell
* Permission is granted to use, distribute, or modify this source,
* provided that this copyright notice remains intact.
*
* The "find" built-in command.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include "sash.h"
#ifdef S_ISLNK
#define LSTAT lstat
#else
#define LSTAT stat
#endif
#define MAX_NAME_SIZE (1024 * 10)
/*
* Items that can be specified to restrict the output.
*/
static BOOL xdevFlag;
static dev_t xdevDevice;
static long fileSize;
static const char * filePattern;
static const char * fileType;
/*
* Recursive routine to examine the files in a directory.
*/
static void examineDirectory(const char * path);
static BOOL testFile(const char * fullName, const struct stat * statBuf);
/*
* Find files from the specified directory path.
* This is limited to just printing their file names.
*/
int
do_find(int argc, const char ** argv)
{
const char * cp;
const char * path;
struct stat statBuf;
argc--;
argv++;
xdevFlag = FALSE;
fileType = NULL;
filePattern = NULL;
fileSize = 0;
if ((argc <= 0) || (**argv == '-'))
{
fprintf(stderr, "No path specified\n");
return 1;
}
path = *argv++;
argc--;
while (argc > 0)
{
argc--;
cp = *argv++;
if (strcmp(cp, "-xdev") == 0)
xdevFlag = TRUE;
else if (strcmp(cp, "-type") == 0)
{
if ((argc <= 0) || (**argv == '-'))
{
fprintf(stderr, "Missing type string\n");
return 1;
}
argc--;
fileType = *argv++;
}
else if (strcmp(cp, "-name") == 0)
{
if ((argc <= 0) || (**argv == '-'))
{
fprintf(stderr, "Missing file name\n");
return 1;
}
argc--;
filePattern = *argv++;
}
else if (strcmp(cp, "-size") == 0)
{
if ((argc <= 0) || (**argv == '-'))
{
fprintf(stderr, "Missing file size\n");
return 1;
}
argc--;
cp = *argv++;
fileSize = 0;
while (isDecimal(*cp))
fileSize = fileSize * 10 + (*cp++ - '0');
if (*cp || (fileSize < 0))
{
fprintf(stderr, "Bad file size specified\n");
return 1;
}
}
else
{
if (*cp != '-')
fprintf(stderr, "Missing dash in option\n");
else
fprintf(stderr, "Unknown option\n");
return 1;
}
}
/*
* Get information about the path and make sure that it
* is a directory.
*/
if (stat(path, &statBuf) < 0)
{
fprintf(stderr, "Cannot stat \"%s\": %s\n", path,
strerror(errno));
return 1;
}
if (!S_ISDIR(statBuf.st_mode))
{
fprintf(stderr, "Path \"%s\" is not a directory\n", path);
return 1;
}
/*
* Remember the device that this directory is on in case we need it.
*/
xdevDevice = statBuf.st_dev;
/*
* If the directory meets the specified criteria, then print it out.
*/
if (testFile(path, &statBuf))
printf("%s\n", path);
/*
* Now examine the files in the directory.
*/
examineDirectory(path);
return 0;
}
/*
* Recursive routine to examine the files in a directory.
*/
static void
examineDirectory(const char * path)
{
DIR * dir;
BOOL needSlash;
struct dirent * entry;
struct stat statBuf;
char fullName[MAX_NAME_SIZE];
/*
* Open the directory.
*/
dir = opendir(path);
if (dir == NULL)
{
fprintf(stderr, "Cannot read directory \"%s\": %s\n",
path, strerror(errno));
return;
}
/*
* See if a slash is needed.
*/
needSlash = (*path && (path[strlen(path) - 1] != '/'));
/*
* Read all of the directory entries and check them,
* except for the current and parent directory entries.
*/
while (!intFlag && ((entry = readdir(dir)) != NULL))
{
if ((strcmp(entry->d_name, ".") == 0) ||
(strcmp(entry->d_name, "..") == 0))
{
continue;
}
/*
* Build the full path name.
*/
strcpy(fullName, path);
if (needSlash)
strcat(fullName, "/");
strcat(fullName, entry->d_name);
/*
* Find out about this file.
*/
if (LSTAT(fullName, &statBuf) < 0)
{
fprintf(stderr, "Cannot stat \"%s\": %s\n",
fullName, strerror(errno));
continue;
}
/*
* If this file matches the criteria that was
* specified then print its name out.
*/
if (testFile(fullName, &statBuf))
printf("%s\n", fullName);
/*
* If this is a directory and we are allowed to cross
* mount points or the directory is still on the same
* device, then examine it's files too.
*/
if (S_ISDIR(statBuf.st_mode) &&
(!xdevFlag || (statBuf.st_dev == xdevDevice)))
{
examineDirectory(fullName);
}
}
closedir(dir);
}
/*
* Test a file name having the specified status to see if it should
* be acted on. Returns TRUE if the file name has been selected.
*/
static BOOL
testFile(const char * fullName, const struct stat * statBuf)
{
const char * cp;
const char * entryName;
BOOL wantType;
int mode;
mode = statBuf->st_mode;
/*
* Check the file type if it was specified.
*/
if (fileType != NULL)
{
wantType = FALSE;
for (cp = fileType; *cp; cp++)
{
switch (*cp)
{
case 'f':
if (S_ISREG(mode))
wantType = TRUE;
break;
case 'd':
if (S_ISDIR(mode))
wantType = TRUE;
break;
case 'p':
if (S_ISFIFO(mode))
wantType = TRUE;
break;
case 'c':
if (S_ISCHR(mode))
wantType = TRUE;
break;
case 'b':
if (S_ISBLK(mode))
wantType = TRUE;
break;
case 's':
if (S_ISSOCK(mode))
wantType = TRUE;
break;
#ifdef S_ISLNK
case 'l':
if (S_ISLNK(mode))
wantType = TRUE;
break;
#endif
default:
break;
}
}
if (!wantType)
return FALSE;
}
/*
* Check the file size if it was specified.
* This check only lets regular files and directories through.
*/
if (fileSize > 0)
{
if (!S_ISREG(mode) && !S_ISDIR(mode))
return FALSE;
if (statBuf->st_size < fileSize)
return FALSE;
}
/*
* Check the file name pattern if it was specified.
*/
if (filePattern != NULL)
{
entryName = strrchr(fullName, '/');
if (entryName)
entryName++;
else
entryName = fullName;
if (!match(entryName, filePattern))
return FALSE;
}
/*
* This file name is wanted.
*/
return TRUE;
}
/* END CODE */

197
src/sash/cmd_grep.c Normal file
View File

@ -0,0 +1,197 @@
/*
* Copyright (c) 2014 by David I. Bell
* Permission is granted to use, distribute, or modify this source,
* provided that this copyright notice remains intact.
*
* The "grep" built-in command.
*/
#include <ctype.h>
#include "sash.h"
static BOOL search
(const char * string, const char * word, BOOL ignoreCase);
int
do_grep(int argc, const char ** argv)
{
FILE * fp;
const char * word;
const char * name;
const char * cp;
BOOL tellName;
BOOL ignoreCase;
BOOL tellLine;
long line;
char buf[BUF_SIZE];
int r;
r = 1;
ignoreCase = FALSE;
tellLine = FALSE;
argc--;
argv++;
if (**argv == '-')
{
argc--;
cp = *argv++;
while (*++cp) switch (*cp)
{
case 'i':
ignoreCase = TRUE;
break;
case 'n':
tellLine = TRUE;
break;
default:
fprintf(stderr, "Unknown option\n");
return 1;
}
}
word = *argv++;
argc--;
tellName = (argc > 1);
while (argc-- > 0)
{
name = *argv++;
fp = fopen(name, "r");
if (fp == NULL)
{
perror(name);
r = 1;
continue;
}
line = 0;
while (fgets(buf, sizeof(buf), fp))
{
if (intFlag)
{
fclose(fp);
return 1;
}
line++;
cp = &buf[strlen(buf) - 1];
if (*cp != '\n')
fprintf(stderr, "%s: Line too long\n", name);
if (search(buf, word, ignoreCase))
{
r = 0;
if (tellName)
printf("%s: ", name);
if (tellLine)
printf("%ld: ", line);
fputs(buf, stdout);
}
}
if (ferror(fp))
perror(name);
fclose(fp);
}
return r;
}
/*
* See if the specified word is found in the specified string.
*/
static BOOL
search(const char * string, const char * word, BOOL ignoreCase)
{
const char * cp1;
const char * cp2;
int len;
int lowFirst;
int ch1;
int ch2;
len = strlen(word);
if (!ignoreCase)
{
while (TRUE)
{
string = strchr(string, word[0]);
if (string == NULL)
return FALSE;
if (memcmp(string, word, len) == 0)
return TRUE;
string++;
}
}
/*
* Here if we need to check case independence.
* Do the search by lower casing both strings.
*/
lowFirst = *word;
if (isupper(lowFirst))
lowFirst = tolower(lowFirst);
while (TRUE)
{
while (*string && (*string != lowFirst) &&
(!isupper(*string) || (tolower(*string) != lowFirst)))
{
string++;
}
if (*string == '\0')
return FALSE;
cp1 = string;
cp2 = word;
do
{
if (*cp2 == '\0')
return TRUE;
ch1 = *cp1++;
if (isupper(ch1))
ch1 = tolower(ch1);
ch2 = *cp2++;
if (isupper(ch2))
ch2 = tolower(ch2);
}
while (ch1 == ch2);
string++;
}
}
/* END CODE */

698
src/sash/cmd_gzip.c Normal file
View File

@ -0,0 +1,698 @@
/*
* Copyright (c) 2014 by David I. Bell
* Permission is granted to use, distribute, or modify this source,
* provided that this copyright notice remains intact.
*
* The "gzip" and "gunzip" built-in commands.
* These commands are optionally built into sash.
* This uses the zlib library by Jean-loup Gailly to compress and
* uncompress the files.
*/
#if HAVE_GZIP
#include <sys/types.h>
#include <sys/stat.h>
#include <zlib.h>
#include "sash.h"
#define GZ_EXT ".gz"
#define TGZ_EXT ".tgz"
#define Z_EXT ".Z"
#define TAR_EXT ".tar"
#define NO_EXT ""
/*
* Tables of conversions to make to file extensions.
*/
typedef struct
{
const char * input;
const char * output;
} CONVERT;
static const CONVERT gzipConvertTable[] =
{
{TAR_EXT, TGZ_EXT},
{NO_EXT, GZ_EXT}
};
static const CONVERT gunzipConvertTable[] =
{
{TGZ_EXT, TAR_EXT},
{GZ_EXT, NO_EXT},
{Z_EXT, NO_EXT},
{NO_EXT, NO_EXT}
};
/*
* Local routines to compress and uncompress files.
*/
static BOOL gzip(const char * inputFile, const char * outputFile);
static BOOL gunzip(const char * inputFile, const char * outputFile);
static const char * convertName
(const CONVERT * table, const char * inFile);
int
do_gzip(int argc, const char ** argv)
{
const char * outPath;
const char * inFile;
const char * outFile;
int i;
int r;
r = 0;
argc--;
argv++;
/*
* Look for the -o option if it is present.
* If present, it must be at the end of the command.
* Remember the output path and remove it if found.
*/
outPath = NULL;
if ((argc >= 2) && (strcmp(argv[argc - 2], "-o") == 0) &&
(argv[argc - 1][0] != '-'))
{
argc -= 2;
outPath = argv[argc + 1];
}
/*
* Now make sure that there are no more options.
*/
for (i = 0; i < argc; i++)
{
if (argv[i][0] == '-')
{
if (strcmp(argv[i], "-o") == 0)
fprintf(stderr, "Illegal use of -o\n");
else
fprintf(stderr, "Illegal option\n");
return 1;
}
}
/*
* If there is no output path specified, then compress each of
* the input files in place using their full paths. The input
* file names are then deleted.
*/
if (outPath == NULL)
{
while (!intFlag && (argc-- > 0))
{
inFile = *argv++;
outFile = convertName(gzipConvertTable, inFile);
/*
* Try to compress the file.
*/
if (!gzip(inFile, outFile))
{
r = 1;
continue;
}
/*
* This was successful.
* Try to delete the original file now.
*/
if (unlink(inFile) < 0)
{
fprintf(stderr, "%s: %s\n", inFile,
"Compressed ok but unlink failed");
r = 1;
}
}
return r;
}
/*
* There is an output path specified.
* If it is not a directory, then either compress the single
* specified input file to the exactly specified output path,
* or else complain.
*/
if (!isDirectory(outPath))
{
if (argc == 1)
r = !gzip(*argv, outPath);
else
{
fprintf(stderr, "Exactly one input file is required\n");
r = 1;
}
return r;
}
/*
* There was an output directory specified.
* Compress each of the input files into the specified
* output directory, converting their extensions if possible.
*/
while (!intFlag && (argc-- > 0))
{
inFile = *argv++;
/*
* Strip the path off of the input file name to make
* the beginnings of the output file name.
*/
outFile = strrchr(inFile, '/');
if (outFile)
outFile++;
else
outFile = inFile;
/*
* Convert the extension of the output file name if possible.
* If we can't, then that is ok.
*/
outFile = convertName(gzipConvertTable, outFile);
/*
* Now build the output path name by prefixing it with
* the output directory.
*/
outFile = buildName(outPath, outFile);
/*
* Compress the input file without deleting the input file.
*/
if (!gzip(inFile, outFile))
r = 1;
}
return r;
}
int
do_gunzip(int argc, const char ** argv)
{
const char * outPath;
const char * inFile;
const char * outFile;
int i;
int r;
r = 0;
argc--;
argv++;
/*
* Look for the -o option if it is present.
* If present, it must be at the end of the command.
* Remember the output path and remove it if found.
*/
outPath = NULL;
if ((argc >= 2) && (strcmp(argv[argc - 2], "-o") == 0) &&
(argv[argc - 1][0] != '-'))
{
argc -= 2;
outPath = argv[argc + 1];
}
/*
* Now make sure that there are no more options.
*/
for (i = 0; i < argc; i++)
{
if (argv[i][0] == '-')
{
if (strcmp(argv[i], "-o") == 0)
fprintf(stderr, "Illegal use of -o\n");
else
fprintf(stderr, "Illegal option\n");
return 1;
}
}
/*
* If there is no output path specified, then uncompress each of
* the input files in place using their full paths. They must
* have one of the proper compression extensions which is converted.
* The input file names are then deleted.
*/
if (outPath == NULL)
{
while (!intFlag && (argc-- > 0))
{
inFile = *argv++;
outFile = convertName(gunzipConvertTable, inFile);
if (inFile == outFile)
{
fprintf(stderr, "%s: %s\n", inFile,
"missing compression extension");
r = 1;
continue;
}
/*
* Try to uncompress the file.
*/
if (!gunzip(inFile, outFile))
{
r = 1;
continue;
}
/*
* This was successful.
* Try to delete the original file now.
*/
if (unlink(inFile) < 0)
{
fprintf(stderr, "%s: %s\n", inFile,
"Uncompressed ok but unlink failed");
r = 1;
}
}
return r;
}
/*
* There is an output path specified.
* If the output path is a device file then uncompress each of
* the input files to the device file.
*/
if (isDevice(outPath))
{
while (!intFlag && (argc-- > 0))
{
if (!gunzip(*argv++, outPath))
r = 1;
}
return r;
}
/*
* If the output path is not a directory then either uncompress the
* single specified input file to the exactly specified output path,
* or else complain.
*/
if (!isDirectory(outPath))
{
if (argc == 1)
return !gunzip(*argv, outPath);
else
fprintf(stderr, "Exactly one input file is required\n");
return 1;
}
/*
* There was an output directory specified.
* Uncompress each of the input files into the specified
* output directory, converting their extensions if possible.
*/
while (!intFlag && (argc-- > 0))
{
inFile = *argv++;
/*
* Strip the path off of the input file name to make
* the beginnings of the output file name.
*/
outFile = strrchr(inFile, '/');
if (outFile)
outFile++;
else
outFile = inFile;
/*
* Convert the extension of the output file name if possible.
* If we can't, then that is ok.
*/
outFile = convertName(gunzipConvertTable, outFile);
/*
* Now build the output path name by prefixing it with
* the output directory.
*/
outFile = buildName(outPath, outFile);
/*
* Uncompress the input file without deleting the input file.
*/
if (!gunzip(inFile, outFile))
r = 1;
}
return r;
}
/*
* Compress the specified input file to produce the output file.
* Returns TRUE if successful.
*/
static BOOL
gzip(const char * inputFileName, const char * outputFileName)
{
gzFile outGZ;
int inFD;
int len;
int err;
struct stat statBuf1;
struct stat statBuf2;
char buf[BUF_SIZE];
outGZ = NULL;
inFD = -1;
/*
* See if the output file is the same as the input file.
* If so, complain about it.
*/
if (stat(inputFileName, &statBuf1) < 0)
{
perror(inputFileName);
return FALSE;
}
if (stat(outputFileName, &statBuf2) < 0)
{
statBuf2.st_ino = -1;
statBuf2.st_dev = -1;
}
if ((statBuf1.st_dev == statBuf2.st_dev) &&
(statBuf1.st_ino == statBuf2.st_ino))
{
fprintf(stderr,
"Cannot compress file \"%s\" on top of itself\n",
inputFileName);
return FALSE;
}
/*
* Open the input file.
*/
inFD = open(inputFileName, O_RDONLY);
if (inFD < 0)
{
perror(inputFileName);
goto failed;
}
/*
* Ask the zlib library to open the output file.
*/
outGZ = gzopen(outputFileName, "wb9");
if (outGZ == NULL)
{
fprintf(stderr, "%s: gzopen failed\n", outputFileName);
goto failed;
}
/*
* Read the uncompressed data from the input file and write
* the compressed data to the output file.
*/
while ((len = read(inFD, buf, sizeof(buf))) > 0)
{
if (gzwrite(outGZ, buf, len) != len)
{
fprintf(stderr, "%s: %s\n", inputFileName,
gzerror(outGZ, &err));
goto failed;
}
if (intFlag)
goto failed;
}
if (len < 0)
{
perror(inputFileName);
goto failed;
}
/*
* All done, close the files.
*/
if (close(inFD))
{
perror(inputFileName);
goto failed;
}
inFD = -1;
if (gzclose(outGZ) != Z_OK)
{
fprintf(stderr, "%s: gzclose failed\n", outputFileName);
goto failed;
}
outGZ = NULL;
/*
* Success.
*/
return TRUE;
/*
* Here on an error, to clean up.
*/
failed:
if (inFD >= 0)
(void) close(inFD);
if (outGZ != NULL)
(void) gzclose(outGZ);
return FALSE;
}
/*
* Uncompress the input file to produce the output file.
* Returns TRUE if successful.
*/
static BOOL
gunzip(const char * inputFileName, const char * outputFileName)
{
gzFile inGZ;
int outFD;
int len;
int err;
struct stat statBuf1;
struct stat statBuf2;
char buf[BUF_SIZE];
inGZ = NULL;
outFD = -1;
/*
* See if the output file is the same as the input file.
* If so, complain about it.
*/
if (stat(inputFileName, &statBuf1) < 0)
{
perror(inputFileName);
return FALSE;
}
if (stat(outputFileName, &statBuf2) < 0)
{
statBuf2.st_ino = -1;
statBuf2.st_dev = -1;
}
if ((statBuf1.st_dev == statBuf2.st_dev) &&
(statBuf1.st_ino == statBuf2.st_ino))
{
fprintf(stderr,
"Cannot uncompress file \"%s\" on top of itself\n",
inputFileName);
return FALSE;
}
/*
* Ask the zlib library to open the input file.
*/
inGZ = gzopen(inputFileName, "rb");
if (inGZ == NULL)
{
fprintf(stderr, "%s: gzopen failed\n", inputFileName);
return FALSE;
}
/*
* Create the output file.
*/
if (isDevice(outputFileName))
outFD = open(outputFileName, O_WRONLY);
else
outFD = open(outputFileName, O_WRONLY|O_CREAT|O_TRUNC, 0666);
if (outFD < 0)
{
perror(outputFileName);
goto failed;
}
/*
* Read the compressed data from the input file and write
* the uncompressed data extracted from it to the output file.
*/
while ((len = gzread(inGZ, buf, sizeof(buf))) > 0)
{
if (fullWrite(outFD, buf, len) < 0)
{
perror(outputFileName);
goto failed;
}
if (intFlag)
goto failed;
}
if (len < 0)
{
fprintf(stderr, "%s: %s\n", inputFileName,
gzerror(inGZ, &err));
goto failed;
}
/*
* All done, close the files.
*/
if (close(outFD))
{
perror(outputFileName);
goto failed;
}
outFD = -1;
if (gzclose(inGZ) != Z_OK)
{
fprintf(stderr, "%s: gzclose failed\n", inputFileName);
goto failed;
}
inGZ = NULL;
/*
* Success.
*/
return TRUE;
/*
* Here on an error, to clean up.
*/
failed:
if (outFD >= 0)
(void) close(outFD);
if (inGZ != NULL)
(void) gzclose(inGZ);
return FALSE;
}
/*
* Convert an input file name to an output file name according to
* the specified convertion table. The returned name points into a
* static buffer which is overwritten on each call. The table must
* end in an entry which always specifies a successful conversion.
* If no conversion is done the original input file name pointer is
* returned.
*/
const char *
convertName(const CONVERT * table, const char * inputFile)
{
int inputLength;
int testLength;
static char buf[PATH_LEN];
inputLength = strlen(inputFile);
for (;;)
{
testLength = strlen(table->input);
if ((inputLength >= testLength) &&
(memcmp(table->input,
inputFile + inputLength - testLength,
testLength) == 0))
{
break;
}
table++;
}
/*
* If no conversion was done, return the original pointer.
*/
if ((testLength == 0) && (table->output[0] == '\0'))
return inputFile;
/*
* Build the new name and return it.
*/
memcpy(buf, inputFile, inputLength - testLength);
memcpy(buf + inputLength - testLength, table->output,
strlen(table->output) + 1);
return buf;
}
#endif
/* END CODE */

597
src/sash/cmd_ls.c Normal file
View File

@ -0,0 +1,597 @@
/*
* Copyright (c) 2014 by David I. Bell
* Permission is granted to use, distribute, or modify this source,
* provided that this copyright notice remains intact.
*
* The "ls" built-in command.
*/
#include "sash.h"
#include <sys/types.h>
#include <sys/stat.h>
#include "dirent.h"
#include <pwd.h>
#include <grp.h>
#define LISTSIZE 8192
#ifdef S_ISLNK
#define LSTAT lstat
#else
#define LSTAT stat
#endif
/*
* Flags for the LS command.
*/
#define LSF_LONG 0x01
#define LSF_DIR 0x02
#define LSF_INODE 0x04
#define LSF_MULT 0x08
#define LSF_FLAG 0x10
#define LSF_COLUMN 0x20
#define LSF_NUMERIC 0x40
/*
* Data holding list of files.
*/
static char ** list;
static int listSize;
static int listUsed;
/*
* Cached user and group name data.
*/
static char userName[12];
static int userId;
static BOOL userIdKnown;
static char groupName[12];
static int groupId;
static BOOL groupIdKnown;
/*
* Local procedures.
*/
static void listFile(
const char * name,
const struct stat * statBuf,
int flags,
int width
);
static BOOL addListName(const char * fileName);
static void listAllFiles(int flags, int displayWidth);
static void clearListNames(void);
int
do_ls(int argc, const char ** argv)
{
const char * cp;
const char * name;
int flags;
int i;
int displayWidth;
BOOL endSlash;
DIR * dirp;
struct dirent * dp;
char fullName[PATH_LEN];
struct stat statBuf;
int r;
static const char * def[] = {"."};
/*
* Reset for a new listing run.
*/
clearListNames();
userIdKnown = FALSE;
groupIdKnown = FALSE;
displayWidth = 0;
flags = 0;
/*
* Handle options.
*/
argc--;
argv++;
while ((argc > 0) && (**argv == '-'))
{
cp = *argv++ + 1;
argc--;
while (*cp) switch (*cp++)
{
case 'l': flags |= LSF_LONG; break;
case 'n': flags |= LSF_NUMERIC; break;
case 'd': flags |= LSF_DIR; break;
case 'i': flags |= LSF_INODE; break;
case 'F': flags |= LSF_FLAG; break;
case 'C': flags |= LSF_COLUMN; break;
default:
fprintf(stderr, "Unknown option -%c\n", cp[-1]);
return 1;
}
}
/*
* If long or numeric listing is specified then turn off column listing.
*/
if (flags & (LSF_LONG | LSF_NUMERIC))
flags &= ~LSF_COLUMN;
/*
* If column listing is specified then calculate the maximum
* width available for the columns of file names.
* This is settable using the COLS environment variable.
*/
if (flags & LSF_COLUMN)
{
name = getenv("COLS");
if (name)
displayWidth = atoi(name);
if (displayWidth <= 0)
displayWidth = 80;
}
/*
* If no arguments are given set up to show the current directory.
*/
if (argc <= 0)
{
argc = 1;
argv = def;
}
if (argc > 1)
flags |= LSF_MULT;
/*
* Make one pass over the file names to collect together
* all of the files which are not directories.
* We will process them all as one list.
*/
for (i = 0; i < argc; i++)
{
if ((flags & LSF_DIR) || !isDirectory(argv[i]))
{
if (!addListName(argv[i]))
return 1;
}
}
/*
* List those file names, and then clear the list.
*/
listAllFiles(flags, displayWidth);
clearListNames();
/*
* If directories were being listed as themselves, then we are done.
*/
if (flags & LSF_DIR)
return r;
/*
* Now iterate over the file names processing the directories.
*/
while (!intFlag && (argc-- > 0))
{
name = *argv++;
endSlash = (*name && (name[strlen(name) - 1] == '/'));
if (LSTAT(name, &statBuf) < 0)
{
perror(name);
r = 1;
continue;
}
/*
* If this file name is not a directory, then ignore it.
*/
if (!S_ISDIR(statBuf.st_mode))
continue;
/*
* Collect all the files in the directory.
*/
dirp = opendir(name);
if (dirp == NULL)
{
perror(name);
continue;
}
if (flags & LSF_MULT)
printf("\n%s:\n", name);
while (!intFlag && ((dp = readdir(dirp)) != NULL))
{
fullName[0] = '\0';
if ((*name != '.') || (name[1] != '\0'))
{
strcpy(fullName, name);
if (!endSlash)
strcat(fullName, "/");
}
strcat(fullName, dp->d_name);
/*
* Save the file name in the list.
*/
if (!addListName(fullName))
{
closedir(dirp);
return 1;
}
}
closedir(dirp);
/*
* List the files we collected in this directory,
* and then clear the list.
*/
listAllFiles(flags, displayWidth);
clearListNames();
}
return r;
}
/*
* List all of the files in the current list of files.
* The files are displayed according to the specified flags,
* in the specified display width.
*/
static void
listAllFiles(int flags, int displayWidth)
{
const char * name;
const char * cp;
int fileWidth;
int column;
int len;
int i;
struct stat statBuf;
/*
* Initialise width data until we need it.
*/
fileWidth = 0;
column = 0;
/*
* Sort the files in the list.
*/
qsort((void *) list, listUsed, sizeof(char *), nameSort);
/*
* If we are showing the files in columns then calculate the
* maximum width of all of the file names, taking into account
* various factors.
*/
if (flags & LSF_COLUMN)
{
for (i = 0; i < listUsed; i++)
{
len = strlen(list[i]);
if (fileWidth < len)
fileWidth = len;
}
if (flags & LSF_FLAG)
fileWidth++;
if (flags & LSF_INODE)
fileWidth += 8;
fileWidth += 2;
}
/*
* Now list the fileNames.
*/
for (i = 0; i < listUsed; i++)
{
name = list[i];
if (LSTAT(name, &statBuf) < 0)
{
perror(name);
continue;
}
cp = strrchr(name, '/');
if (cp)
cp++;
else
cp = name;
/*
* List the file in the next column or at the end
* of a line depending on the width left.
*/
if (column + fileWidth * 2 >= displayWidth)
{
listFile(cp, &statBuf, flags, 0);
column = 0;
}
else
{
listFile(cp, &statBuf, flags, fileWidth);
column += fileWidth;
}
}
/*
* Terminate the last file name if necessary.
*/
if (column > 0)
fputc('\n', stdout);
}
/*
* Do a listing of a particular file name according to the flags.
* The output is shown within the specified width if it is nonzero,
* or on its own line if the width is zero.
*/
static void
listFile(
const char * name,
const struct stat * statBuf,
int flags,
int width
)
{
char * cp;
struct passwd * pwd;
struct group * grp;
int len;
int mode;
int flagChar;
int usedWidth;
char buf[PATH_LEN];
mode = statBuf->st_mode;
/*
* Initialise buffers for use.
*/
cp = buf;
buf[0] = '\0';
flagChar = '\0';
/*
* Show the inode number if requested.
*/
if (flags & LSF_INODE)
{
sprintf(cp, "%7ld ", statBuf->st_ino);
cp += strlen(cp);
}
/*
* Create the long or numeric status line if requested.
*/
if (flags & (LSF_LONG | LSF_NUMERIC))
{
strcpy(cp, modeString(mode));
cp += strlen(cp);
sprintf(cp, "%3ld ", (long) statBuf->st_nlink);
cp += strlen(cp);
if (!userIdKnown || (statBuf->st_uid != userId))
{
if (flags & LSF_NUMERIC)
pwd = 0;
else
pwd = getpwuid(statBuf->st_uid);
if (pwd)
strcpy(userName, pwd->pw_name);
else
sprintf(userName, "%d", statBuf->st_uid);
userId = statBuf->st_uid;
userIdKnown = TRUE;
}
sprintf(cp, "%-8s ", userName);
cp += strlen(cp);
if (!groupIdKnown || (statBuf->st_gid != groupId))
{
if (flags & LSF_NUMERIC)
grp = 0;
else
grp = getgrgid(statBuf->st_gid);
if (grp)
strcpy(groupName, grp->gr_name);
else
sprintf(groupName, "%d", statBuf->st_gid);
groupId = statBuf->st_gid;
groupIdKnown = TRUE;
}
sprintf(cp, "%-8s ", groupName);
cp += strlen(cp);
if (S_ISBLK(mode) || S_ISCHR(mode))
{
sprintf(cp, "%3lu, %3lu ",
((unsigned long) statBuf->st_rdev) >> 8,
((unsigned long) statBuf->st_rdev) & 0xff);
}
else
sprintf(cp, "%8ld ", statBuf->st_size);
cp += strlen(cp);
sprintf(cp, " %-12s ", timeString(statBuf->st_mtime));
}
/*
* Set the special character if the file is a directory or
* symbolic link or executable and the display was requested.
*/
if (flags & LSF_FLAG)
{
if (S_ISDIR(mode))
flagChar = '/';
#ifdef S_ISLNK
else if (S_ISLNK(mode))
flagChar = '@';
#endif
else if ((mode & 0111) != 0)
flagChar = '*';
}
/*
* Print the status info followed by the file name.
*/
fputs(buf, stdout);
fputs(name, stdout);
if (flagChar)
fputc(flagChar, stdout);
/*
* Calculate the width used so far.
*/
usedWidth = strlen(buf) + strlen(name);
if (flagChar)
usedWidth++;
/*
* Show where a symbolic link points.
*/
#ifdef S_ISLNK
if ((flags & LSF_LONG) && S_ISLNK(mode))
{
len = readlink(name, buf, PATH_LEN - 1);
if (len >= 0)
{
buf[len] = '\0';
printf(" -> %s", buf);
}
usedWidth += strlen(buf) + 4;
}
#endif
/*
* If no width was given then just end the line with a newline.
*/
if (width == 0)
{
fputc('\n', stdout);
return;
}
/*
* There is a width given.
* Print as many spaces as it takes to reach that width.
*/
while (usedWidth++ < width)
fputc(' ', stdout);
}
/*
* Save a file name to the end of the static list, reallocating if necessary.
* The file name is copied into allocated memory owned by the list.
* Returns TRUE on success.
*/
static BOOL
addListName(const char * fileName)
{
char ** newList;
/*
* Reallocate the list if necessary.
*/
if (listUsed >= listSize)
{
newList = realloc(list,
((sizeof(char **)) * (listSize + LISTSIZE)));
if (newList == NULL)
{
fprintf(stderr, "No memory for file name buffer\n");
return FALSE;
}
list = newList;
listSize += LISTSIZE;
}
/*
* Copy the file name into the next entry.
*/
list[listUsed] = strdup(fileName);
if (list[listUsed] == NULL)
{
fprintf(stderr, "No memory for file name\n");
return FALSE;
}
/*
* Increment the amount of space used.
*/
listUsed++;
return TRUE;
}
/*
* Free all of the names from the list of file names.
*/
static void
clearListNames(void)
{
while (listUsed > 0)
{
listUsed--;
free(list[listUsed]);
}
}
/* END CODE */

1277
src/sash/cmd_tar.c Normal file

File diff suppressed because it is too large Load Diff

1562
src/sash/cmds.c Normal file

File diff suppressed because it is too large Load Diff

10
src/sash/dirent.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef DIRENT_H_
#define DIRENT_H_
typedef struct {} DIR;
struct dirent {
char *d_name;
};
#endif // DIRENT_H_

591
src/sash/sash.1 Normal file
View File

@ -0,0 +1,591 @@
.TH SASH 1
.SH NAME
sash \- stand-alone shell with built-in commands
.SH SYNOPSYS
.B sash [-c command] [-f fileName ] [-p prompt] [-q] [-a]
.SH DESCRIPTION
The
.B sash
program is a stand-alone shell which is useful for recovering from certain
types of system failures.
In particular, it was created in order to cope with the problem of
missing shared libraries or important executables.
.PP
.B Sash
can execute external programs, as in any shell. There are no restrictions
on these commands, as the standard shell is used to execute them if there
are any non-wildcard meta-characters in the command.
.PP
More importantly, however,
is that many of the standard system commands are built-in to
.BR sash .
These built-in commands are:
.PP
.nf
-ar, -chattr, -chgrp, -chmod, -chown, -chroot, -cmp,
-cp, -dd, -echo, -ed, -grep, -file, -find, -gunzip,
-gzip, -kill, -losetup, -ln, -ls, -lsattr, -mkdir,
-mknod, -more, -mount, -mv, -pivot_root, -printenv, -pwd,
-rm, -rmdir, -sum, -sync, -tar, -touch, -umount, -where
.fi
.PP
These commands are generally similar to the standard programs with similar
names. However, they are simpler and cruder than the external programs,
and so many of the options are not implemented. The restrictions for each
built-in command are described later.
.PP
The built-in commands which correspond to external programs begin with a
dash character in order to distinguish them from the external programs.
So typing "ls", for example, will attempt to run the real
.B ls
program.
If "-ls" is typed, then the built-in command which mimics
.B ls
is called.
.PP
For the built-in commands, file names are expanded so that asterisks,
question marks, and characters inside of square brackets are recognised
and are expanded.
Arguments can be quoted using single quotes, double quotes, or backslashes.
However, no other command line processing is performed.
This includes specifying of file redirection, and the specifying of a pipeline.
.PP
If an external program is non-existant or fails to run correctly, then
the "alias" built-in command may be used to redefine the standard command
so that it automatically runs the built-in command instead. For example,
the command "alias ls -ls" redefines "ls" to run the built-in command.
This saves you the pain of having to remember to type the leading dash
all of the time.
If many external programs will not run, then the "aliasall" command may
be useful to create multiple aliases.
.PP
The "help" command will list all of the built-in commands in
.B sash .
If an argument is given, it will list only those built-in commands
which contain the given argument as a sub-string.
Each built-in command is described below in more detail.
.PP
.TP
.B alias [name [command]]
If
.I name
and
.I command
are provided, this defines an alias for a command with the specified name
which executes the specified command with possible arguments.
Arguments containing wildcards can be quoted in order to defer their
expansion until the alias is invoked.
If just
.I name
is provided, then the definition
of the specified command alias is displayed. If nothing is provided,
then the definitions of all aliases are displayed.
.TP
.B aliasall
This defines aliases for all of the built-in commands that start with
dashes to the corresponding names without the dashes.
This may be useful when the system is so corrupted that no external
programs may be executed at all.
.TP
.B -ar [txp][v] arfile [filename]...
List or extract files from an ar archive.
The arfile argument specifies a file name which contains the archive.
If no additional filenames are specified, then all files in the archive are
operated on.
Otherwise, only those archive members which have the same name
as one of the additional filenames are operated on.
Filenames which do not appear in the archive are ignored.
Archives cannot be created or modified.
The archiver correctly handles 4.0BSD archives,
and understands both the SysV and 4.4BSD extensions for long file names.
The extended pseudo-BSD formats are not supported;
nor are the two antediluvian binary formats derived from V7 and earlier.
(The GNU archiver normally creates archives in the 4.0BSD format with
SysV extensions.)
.TP
.B cd [dirName]
If
.I dirName
is provided, then the current directory is changed to the
dirName. If
.I dirName
is absent, then the current directory is changed
to the user's home directory (value of the $HOME environment variable).
.TP
.B -chattr [+i] [-i] [+a] [-a] fileName ...
Change the attributes of the specified files on an ext2 or ext3 file system.
Using a plus sign adds the specified attribute for the files.
Using a minus sign removes the specified attributes for the files.
The 'i' attribute makes a file immutable so that it cannot be changed.
The 'a' attribute makes a file append-only.
This command is only available on Linux.
.TP
.B -chgrp gid fileName ...
Change the group id for the specified list of files. The
.I gid
can
either be a group name, or a decimal value.
.TP
.B -chmod mode fileName ...
Change the mode of the specified list of files. The
.I mode
argument
can only be an octal value.
.TP
.B -chown uid fileName ...
Change the owner id for the specified list of files. The
.I uid
can
either be a user name, or a decimal value.
.TP
.B -chroot path
Changes the root directory to that specified in
.I path.
This directory
will be used for path names beginning with /. The root directory is
inherited by all children of the current process.
.TP
.B -cmp fileName1 fileName2
Determines whether or not the specified file names have identical data.
This says that the files are links to each other, are different sizes,
differ at a particular byte number, or are identical.
.TP
.B -cp srcName ... destName
Copies one or more files from the
.I srcName
to the
.IR destName .
If more
than one srcName is given, or if destName is a directory, then all
the srcNames are copied into the destName directory with the same
names as the srcNames.
.TP
.B -dd if=name of=name [bs=n] [count=n] [skip=n] [seek=n]
Copy data from one file to another with the specified parameters.
The
.I if
and
.I of
arguments must be provided, so stdin and stdout cannot
be specified. The
.I bs
argument is the block size, and is a numeric
value (which defaults to 512 bytes).
.I Count
is the number of blocks
to be copied (which defaults to end of file for the input file).
.I Skip
is the number of blocks to ignore before copying (seek is used
if possible, and the default is 0).
.I Seek
is the number of blocks to
seek in the output file before writing (and defaults to 0). Any of
the numeric decimal values can have one or more trailing letters
from the set 'kbw', which multiplies the value by 1024, 512, and 2
respectively. The command reports the number of full blocks read
and written, and whether or not any partial block was read or written.
.TP
.B -echo [args] ...
Echo the arguments to the -echo command. Wildcards are expanded,
so this is a convenient way to get a quick list of file names in a directory.
The output is always terminated with a newline.
.TP
.B -ed [fileName]
Edit the specified file using line-mode commands. The following
.B ed
commands are provided: = c r w i a d p l s f k z and q.
Line numbers can be constants, ".", "$", "'x",
.RI / string /
and simple
arithmetic combinations of these. The substitute command and the
search expression can only use literal strings. There are some
small differences in the way that some commands behave.
.TP
.B exec fileName [args]
Execute the specified program with the specified arguments.
This replaces
.B sash
completely by the executed program.
.TP
.B exit
Quit from
.BR sash .
.TP
.B -file fileName ...
Examine the specified files and print out their file type.
This indicates whether the files are regular files or not,
whether they contain printable text or shell scripts,
are executables, or contain binary data.
.TP
.B -find dirName [-xdev] [-type chars] [-name pattern] [-size minSize]
Find all files contained within the specified directory
tree which meet all of the specified conditions.
The -xdev option prevents crossing of mount points.
The -name option specifies a wildcard pattern to match the last
component of the file names.
The -type option specifies that the files must have a type
matching the specified list from the set: f d c b p s l.
These represent regular files, directories, character devices,
block devices, named pipes, sockets, and symbolic links.
The -size option specifies that the files must be regular files or
directories which contain at least the specified number of bytes.
.TP
.B -grep [-in] word fileName ...
Display lines of the specified files which contain the given word.
If only one file name is given, then only the matching lines are
printed. If multiple file names are given, then the file names are
printed along with the matching lines.
.I Word
must be a single word,
(ie, not a regular expression). If -i is given, then case is
ignored when doing the search. If -n is given, then the line
numbers of the matching lines are also printed.
.TP
.B -gunzip inputFileName ... [-o outputPath]
Uncompress one or more files that had been compressed using the
.I gzip
or
.I compress
algorithms.
If the -o option is not given,
then each of the input file names must have one of the
extensions ".gz", ".tgz", or ".Z",
and those files will be replaced by the uncompressed versions of those files.
The original files will be deleted after the output files have been
successfully created.
The uncompressed versions of the files have the same names as the original
file names, except for a simple modification of their extensions.
If an extension is ".tgz", then the extension is replaced by ".tar".
Otherwise, the ".gz" or ".Z" extension is removed.
.sp
If the -o option is given, then the input files will not be deleted,
and the uncompressed versions of the files will be created as specified
by
.IR outputPath .
If the output path is a directory, then the uncompressed versions of the
input files will be placed in that directory with their file names
modified as described above, or with the same name if the input file name
does not have one of the special extensions.
If the output path is a regular file, then only one input file is allowed,
and the uncompressed version of that input file is created as the output
path exactly as specified.
If the output path is a block or character device, then the uncompressed
versions of the input files are concatenated to the device.
.sp
This command is only available if
.B sash
was compiled to use the gzip library.
.TP
.B -gzip inputFileName ... [-o outputPath]
Compresses one or more files using the
.I gzip
algorithm.
If the -o option is not given,
then each of the input file names will be replaced by the compressed
versions of those files,
The original files will be deleted after the output files have been
successfully created.
The compressed versions of the files have the same names as the original
file names, except for a simple modification of the extensions.
If an extension is ".tar", then the extension is replaced by ".tgz".
Otherwise, the ".gz" extension is added.
.sp
If the -o option is given, then the input files will not be deleted,
and the compressed versions of the files will be created as specified
by
.IR outputPath .
If the output path is a directory, then the compressed versions of the
input files will be placed in that directory with their file names
modified as described above.
If the output path is not a directory, then only one input file is allowed,
and the compressed version of that input file is created as the output
path exactly as specified.
.sp
This command is only available if
.B sash
was compiled to use the gzip library.
.TP
.B help [word]
Displays a list of built-in commands along with their usage strings.
If a word is given,
then just those commands whose name or usage contains the word is displayed.
If a word is specified which exactly matches a built-in command name,
then a short description of the command and its usage is given.
.TP
.B -kill [-signal] pid ...
Sends the specified signal to the specified list of processes.
.I Signal
is a numeric value, or one of the special values HUP, INT,
QUIT, KILL, TERM, STOP, CONT, USR1 or USR2.
If no signal is specified then SIGTERM is used.
.TP
.B -losetup [-d] loopDev [file]
Associates loopback devices with files on the system. If
.I -d
is not given,
the loopback device
.I loopDev
is associated with
.I file.
If
.I -d
is given,
.I loopDev
is unassociated with the file it's currently configured for.
.TP
.B -ln [-s] srcName ... destName
Links one or more files from the
.I srcName
to the specified
.IR destName .
If there are
multiple srcNames, or destName is a directory, then the link is
put in the destName directory with the same name as the source name.
The default links are hard links. Using -s makes symbolic links.
For symbolic links, only one srcName can be specified.
.TP
.B -ls [-lidFC] fileName ...
Display information about the specified list of file names.
The normal listing is simply a list of file names, one per line.
The options available are -l, -n, -i, -d, and -F.
The -l option produces a long listing giving the normal 'ls' information.
The -n option is like -l except that numeric user and group ids are shown.
The -i option displays the inode numbers of the files.
The -d option displays information about a directory, instead of the
files within it.
The -F option appends a slash or asterisk to the file name if the file
is a directory or is executable.
The -C option displays the file names in a multi-column format.
The width of the output is calculated using the COLS environment variable.
.TP
.B -lsattr fileName ...
Display attributes for the specified files on an ext2 or ext3 file system.
The letter 'i' indicates that the file is immutable and cannot change.
The letter 'a' indicates that the file is append-only.
Dashes are shown where the attributes are not set.
This command is only available on Linux.
.TP
.B -mkdir dirName ...
Creates the specified directories. They are created with the
default permissions.
.TP
.B -mknod fileName type major minor
Creates a special device node, either a character file or a block
file.
.I Filename
is the name of the node.
.I Type
is either 'c' or 'd'.
.I Major
is the major device number.
.I Minor
is the minor device number.
Both of these numbers are decimal.
.TP
.B -more fileName ...
Type out the contents of the specified file names, one page at a
time. For each page displayed, you can type 'n' and a return to go
to the next file, 'q' and a return to quit the command completely,
or just a return to go to the next page. The environment variables
LINES and COLS can be used to set the page size.
.TP
.B -mount [-t type] [-r] [-s] [-e] [-m] devName dirName
Mount a filesystem on a directory name.
The -t option specifies the type of filesystem being mounted,
and defaults to "ext3" for Linux and "ffs" for BSD.
The -r option indicates to mount the filesystem read-only.
The -s option indicates to mount the filesystem no-suid.
The -e option indicates to mount the filesystem no-exec.
The -m option indicates to remount an already mounted filesystem.
The -m option is only available on Linux.
.TP
.B -mv srcName ... destName
Moves one or more files from the
.I srcName
to the
.IR destName .
If multiple srcNames are given, or if destName is a directory, then
the srcNames are copied into the destination directory with the
same names as the srcNames. Renames are attempted first, but if
this fails because of the files being on different filesystems,
then copies and deletes are done instead.
.TP
.B -pivot_root newRoot putOld
Moves the root file system of the current process to the directory
.I putOld
and makes
.I newRoot
the new root file system of the current process.
.TP
.B -printenv [name]
If
.I name
is not given, this prints out the values of all the current
environment variables. If
.I name
is given, then only that environment variable value is printed.
.TP
.B prompt [word] ...
Sets the prompt string that is displayed before reading of a
command. A space is always added to the specified prompt.
.TP
.B -pwd
Prints the current working directory.
.TP
.B quit
Exits from
.BR sash .
.TP
.B -rm fileName ...
Removes one or more files.
.TP
.B -rmdir dirName ...
Removes one or more directories. The directories must be empty
for this to be successful.
.TP
.B setenv name value
Set the value of an environment variable.
.TP
.B source fileName
Execute commands which are contained in the specified file name.
.TP
.B -sum fileName ...
Calculates checksums for one or more files.
This is the 16 bit checksum compatible with the BSD sum program.
.TP
.B -sync
Do a "sync" system call to force dirty blocks out to the disk.
.TP
.B -tar [ctxv]f tarFileName [fileName] ...
Create, list or extract files from a tar archive.
The f option must be specified, and accepts a device or file name
argument which contains the tar archive.
When creating, at least one file name must be specified to be stored.
If a file name is a directory, then all the files and directories
within the directory are stored.
Linked files and other special file types are not handled properly.
When listing or extracting files, only those files starting with
the specified file names are processed.
If no file names are specified, then all files in the archive are processed.
Leading slashes in the tar archive file names are always removed so that you
might need to cd to "/" to restore files which had absolute paths.
.TP
.B -touch fileName ...
Updates the modify times of the specifed files. If a file does not
exist, then it will be created with the default protection.
.TP
.B umask [mask]
If
.I mask
is given, sets the "umask" value used for initializing the
permissions of newly created files. If
.I mask
is not given, then the
current umask value is printed. The mask is an octal value.
.TP
.B -umount [-f] fileName
Unmounts a file system. The file name can either be the device name
which is mounted, or else the directory name which the file system
is mounted onto.
The -f option unmounts the filesystem even if it is being used.
The -f option is only available on BSD.
.TP
.B unalias name
Remove the definition for the specified alias.
.TP
.B -where program
Prints out all of paths defined by the PATH environment variable where the
specified program exists. If the program exists but cannot be executed,
then the reason is also printed.
.SH OPTIONS
There are several command line options to
.BR sash .
.PP
The -c option executes the next argument as a command (including embedded
spaces to separate the arguments of the command), and then exits.
.PP
The -f option executes the commands contained in the file name specified
by the next argument, and then exits.
This feature can be used to create executable scripts for
.B sash
by starting the script file with a line similar to:
.nf
#! /bin/sash -f
.fi
.PP
The -p option takes the next argument as the prompt string to be used
when prompting for commands.
.PP
The -q option makes
.B sash
quiet, which simply means that it doesn't print its introduction line
when it starts.
This option is also implied if the -c or -f options are used.
.PP
The -a option creates aliases for the built-in commands so
that they replace the corresponding standard commands.
This is the same result as if the 'aliasall' command was used.
.SH SYSTEM RECOVERY
This section contains some useful information about using
.B sash
with
.B lilo
to perform system recovery in some situations.
Similar concepts should exist for other boot loaders and operating systems.
.PP
When important shared libraries are being upgraded,
it might be a good idea to have
.B sash
already running on a console by itself.
Then if there is a problem with the shared libraries
.B sash
will be unaffected and you may be able to use it to fix the problem.
.PP
If a problem with the system shows up at boot time so that you cannot
enter multi-user mode and log in,
then you can first try booting into single-user mode by adding the
.I single
keyword after your kernel image name at the
.B lilo
prompt.
If you manage to reach a shell prompt, then you can run
.B sash
from that shell (if necessary).
One reason for doing this is that you might need to use the
.B -mount
command with the -m option to remount the root file system
so that it can be modified.
.PP
If you cannot reach the shell in single-user mode,
then you can try running sash directly as a replacement for the init process.
This is done by adding the
.I init=/bin/sash
keyword after your kernel image name at the
.B lilo
prompt.
When this is done, then the use of the
.B aliasall
command might be useful to reduce attempts to access the root file system
when running commands.
.PP
If your root file system is so corrupted that you cannot get
.B sash
to run at all, then you will have to resort to a system recovery floppy.
.SH WARNINGS
.B Sash
should obviously be linked statically, otherwise its purpose is lost.
Note that even if the rest of the program is linked statically, the
password and group lookup routines in the C library can still be dynamic.
For that reason, if there are problems then it might be necessary to
only use numeric ids for the -chown and -chgrp commands and to use
the -n option instead of -l for the -ls command.
.PP
Several other system commands might be necessary for system recovery,
but aren't built-in to
.BR sash .
.SH AUTHOR
.nf
David I. Bell
dbell@tip.net.au
5 March 2014
.fi

1373
src/sash/sash.c Normal file

File diff suppressed because it is too large Load Diff

169
src/sash/sash.h Normal file
View File

@ -0,0 +1,169 @@
/*
* Copyright (c) 2014 by David I. Bell
* Permission is granted to use, distribute, or modify this source,
* provided that this copyright notice remains intact.
*
* Definitions for stand-alone shell for system maintainance for Linux.
*/
#ifndef SASH_H
#define SASH_H
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <memory.h>
#include <time.h>
#include <ctype.h>
#if __OpenBSD__
#include <sys/param.h>
#endif
#if __Linux__
#include <malloc.h>
#endif
#define PATH_LEN 1024
#define CMD_LEN 10240
#define ALIAS_ALLOC 20
#define EXPAND_ALLOC 1024
#define STDIN 0
#define STDOUT 1
#define MAX_SOURCE 10
#define BUF_SIZE 8192
#define isBlank(ch) (((ch) == ' ') || ((ch) == '\t'))
#define isDecimal(ch) (((ch) >= '0') && ((ch) <= '9'))
#define isOctal(ch) (((ch) >= '0') && ((ch) <= '7'))
#define isWildCard(ch) (((ch) == '*') || ((ch) == '?') || ((ch) == '['))
#ifndef MAX
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#endif
#ifndef MIN
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#endif
typedef int BOOL;
#define FALSE ((BOOL) 0)
#define TRUE ((BOOL) 1)
/*
* Built-in command functions.
*/
extern int do_alias(int argc, const char ** argv);
extern int do_aliasall(int argc, const char ** argv);
extern int do_cd(int argc, const char ** argv);
extern int do_exec(int argc, const char ** argv);
extern int do_exit(int argc, const char ** argv);
extern int do_prompt(int argc, const char ** argv);
extern int do_source(int argc, const char ** argv);
extern int do_umask(int argc, const char ** argv);
extern int do_unalias(int argc, const char ** argv);
extern int do_help(int argc, const char ** argv);
extern int do_ln(int argc, const char ** argv);
extern int do_cp(int argc, const char ** argv);
extern int do_mv(int argc, const char ** argv);
extern int do_rm(int argc, const char ** argv);
extern int do_chmod(int argc, const char ** argv);
extern int do_mkdir(int argc, const char ** argv);
extern int do_rmdir(int argc, const char ** argv);
extern int do_mknod(int argc, const char ** argv);
extern int do_chown(int argc, const char ** argv);
extern int do_chgrp(int argc, const char ** argv);
extern int do_sum(int argc, const char ** argv);
extern int do_sync(int argc, const char ** argv);
extern int do_printenv(int argc, const char ** argv);
extern int do_more(int argc, const char ** argv);
extern int do_cmp(int argc, const char ** argv);
extern int do_touch(int argc, const char ** argv);
extern int do_ls(int argc, const char ** argv);
extern int do_dd(int argc, const char ** argv);
extern int do_tar(int argc, const char ** argv);
extern int do_ar(int argc, const char ** argv);
extern int do_mount(int argc, const char ** argv);
extern int do_umount(int argc, const char ** argv);
extern int do_setenv(int argc, const char ** argv);
extern int do_pwd(int argc, const char ** argv);
extern int do_echo(int argc, const char ** argv);
extern int do_kill(int argc, const char ** argv);
extern int do_grep(int argc, const char ** argv);
extern int do_file(int argc, const char ** argv);
extern int do_find(int argc, const char ** argv);
extern int do_ed(int argc, const char ** argv);
extern int do_where(int argc, const char ** argv);
#if HAVE_GZIP
extern int do_gzip(int argc, const char ** argv);
extern int do_gunzip(int argc, const char ** argv);
#endif
#if HAVE_LINUX_ATTR
extern int do_lsattr(int argc, const char ** argv);
extern int do_chattr(int argc, const char ** argv);
#endif
#if HAVE_LINUX_CHROOT
extern int do_chroot(int argc, const char ** argv);
#endif
#if HAVE_LINUX_LOSETUP
extern int do_losetup(int argc, const char ** argv);
#endif
#if HAVE_LINUX_PIVOT
extern int do_pivot_root(int argc, const char ** argv);
extern int pivot_root(const char *new_root, const char *put_old);
#endif
/*
* Global utility routines.
*/
extern const char * modeString(int mode);
extern const char * timeString(time_t timeVal);
extern BOOL isDirectory(const char * name);
extern BOOL isDevice(const char * name);
extern int nameSort(const void * p1, const void * p2);
extern char * getChunk(int size);
extern char * chunkstrdup(const char *);
extern void freeChunks(void);
extern int trySystem(const char * cmd);
extern void tryWrite(int fd, const char * buf, int len);
extern int fullWrite(int fd, const char * buf, int len);
extern int fullRead(int fd, char * buf, int len);
extern void checkStatus(const char * name, int status);
extern BOOL match(const char * text, const char * pattern);
extern const char * buildName
(const char * dirName, const char * fileName);
extern BOOL makeArgs
(const char * cmd, int * argcPtr, const char *** argvPtr);
extern BOOL copyFile
(const char * srcName, const char * destName, BOOL setModes);
extern BOOL makeString
(int argc, const char ** argv, char * buf, int bufLen);
extern int expandWildCards
(const char * fileNamePattern, const char *** retFileTable);
/*
* Global variable to indicate that an SIGINT occurred.
* This is used to stop processing.
*/
extern BOOL intFlag;
#endif
/* END CODE */

1144
src/sash/utils.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2,9 +2,6 @@
#include "priv_gpio.h"
#include <kernel/task.h>
void task1(void);
void task2(void);
void user_delay(uint32_t ms)
{
register uint32_t r1 asm("r1") = ms;
@ -16,29 +13,32 @@ void user_delay(uint32_t ms)
" :: "r" (r1));
}
int fork(void)
{
int result;
asm("\
mov r0, sp; \
svc 3; \
mov %0, r0; \
" : "=r" (result));
return result;
}
void user_main(void)
{
gpio(GPIO_MODE, 5, OUTPUT);
task_start(task1, 512);
for (int i = 0; i < 8; i++) {
gpio(GPIO_OUT, 5, !(i & 1));
user_delay(200);
}
}
void task1(void)
{
user_delay(400);
task_start(task2, 1024);
}
void task2(void)
{
int state = 0;
user_delay(2500);
if (fork() == 0) {
while (1) {
gpio(GPIO_OUT, 5, state ^= 1);
gpio(GPIO_OUT, 5, 1);
user_delay(1000);
}
} else {
while (1) {
gpio(GPIO_OUT, 5, 0);
user_delay(500);
}
}
}