wip forking, added sash source
This commit is contained in:
parent
6b5dfe3c09
commit
f4149952ea
@ -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;
|
||||
|
||||
|
@ -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.
|
||||
|
@ -56,5 +56,5 @@ void init_idle(void)
|
||||
task_start(user_main, 4096);
|
||||
|
||||
while (1)
|
||||
delay(100);
|
||||
delay(10);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
31
src/sash/CHANGES
Normal 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
69
src/sash/Makefile
Normal 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
17
src/sash/README
Normal 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
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
265
src/sash/cmd_chattr.c
Normal 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
372
src/sash/cmd_dd.c
Normal 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
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
235
src/sash/cmd_file.c
Normal 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
376
src/sash/cmd_find.c
Normal 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
197
src/sash/cmd_grep.c
Normal 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
698
src/sash/cmd_gzip.c
Normal 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
597
src/sash/cmd_ls.c
Normal 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
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
1562
src/sash/cmds.c
Normal file
File diff suppressed because it is too large
Load Diff
10
src/sash/dirent.h
Normal file
10
src/sash/dirent.h
Normal 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
591
src/sash/sash.1
Normal 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
1373
src/sash/sash.c
Normal file
File diff suppressed because it is too large
Load Diff
169
src/sash/sash.h
Normal file
169
src/sash/sash.h
Normal 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
1144
src/sash/utils.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user