wip forking, added sash source
This commit is contained in:
parent
6b5dfe3c09
commit
f4149952ea
@ -22,11 +22,6 @@
|
|||||||
|
|
||||||
#define HEAP_ALIGN 4
|
#define HEAP_ALIGN 4
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint32_t size;
|
|
||||||
void *next;
|
|
||||||
} __attribute__ ((packed)) alloc_t;
|
|
||||||
|
|
||||||
static alloc_t *free_blocks;
|
static alloc_t *free_blocks;
|
||||||
static void *heap_end;
|
static void *heap_end;
|
||||||
|
|
||||||
|
@ -23,6 +23,11 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t size;
|
||||||
|
void *next;
|
||||||
|
} __attribute__ ((packed)) alloc_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes memory management of the given heap.
|
* Initializes memory management of the given heap.
|
||||||
* No overflow stuff is done, so... be careful.
|
* No overflow stuff is done, so... be careful.
|
||||||
|
@ -56,5 +56,5 @@ void init_idle(void)
|
|||||||
task_start(user_main, 4096);
|
task_start(user_main, 4096);
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
delay(100);
|
delay(10);
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
|
|
||||||
extern void gpio_svc(uint32_t *);
|
extern void gpio_svc(uint32_t *);
|
||||||
extern void clock_svc(uint32_t *);
|
extern void clock_svc(uint32_t *);
|
||||||
|
extern void task_svc(uint32_t *);
|
||||||
|
|
||||||
void svc_handler(uint32_t *args)
|
void svc_handler(uint32_t *args)
|
||||||
{
|
{
|
||||||
@ -41,6 +42,11 @@ void svc_handler(uint32_t *args)
|
|||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
clock_svc(args);
|
clock_svc(args);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
task_svc(args);
|
||||||
|
asm("mov r0, %0" :: "r" (args[0]));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,13 @@
|
|||||||
task_t *current, *prev;
|
task_t *current, *prev;
|
||||||
static uint8_t task_disable = 0;
|
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)
|
void task_hold(uint8_t hold)
|
||||||
{
|
{
|
||||||
if (hold != 0)
|
if (hold != 0)
|
||||||
@ -119,11 +126,12 @@ void task_init(void (*init)(void), uint16_t stackSize)
|
|||||||
|
|
||||||
// bit 0 - priv, bit 1 - psp/msp
|
// bit 0 - priv, bit 1 - psp/msp
|
||||||
asm("\
|
asm("\
|
||||||
|
isb; \
|
||||||
|
cpsie i; \
|
||||||
mov r0, sp; \
|
mov r0, sp; \
|
||||||
msr psp, r0; \
|
msr psp, r0; \
|
||||||
mrs r0, control; \
|
mrs r0, control; \
|
||||||
orr r0, r0, #3; \
|
orr r0, r0, #3; \
|
||||||
cpsie i; \
|
|
||||||
msr control, r0; \
|
msr control, r0; \
|
||||||
");
|
");
|
||||||
|
|
||||||
@ -140,31 +148,41 @@ void task_start(void (*task)(void), uint16_t stackSize)
|
|||||||
task_hold(0);
|
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);
|
task_hold(1);
|
||||||
|
|
||||||
// duplicate task info
|
// 1. Get a PC for the child
|
||||||
alloc_t *heapInfo = (alloc_t *)(current->stack - 2);
|
void (*pc)(void) = (void (*)(void))((uint32_t)task_fork_ret);
|
||||||
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);
|
|
||||||
|
|
||||||
t->next = current->next;
|
// 2. Prepare child task
|
||||||
current->next = t;
|
alloc_t *stackInfo = (alloc_t *)(((uint8_t *)current->stack)
|
||||||
current = t;
|
- 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);
|
task_hold(0);
|
||||||
|
|
||||||
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
|
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
|
||||||
return 0;
|
return 1;
|
||||||
}*/
|
}
|
||||||
|
|
||||||
__attribute__ ((naked))
|
__attribute__ ((naked))
|
||||||
void PendSV_Handler(void)
|
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 "priv_gpio.h"
|
||||||
#include <kernel/task.h>
|
#include <kernel/task.h>
|
||||||
|
|
||||||
void task1(void);
|
|
||||||
void task2(void);
|
|
||||||
|
|
||||||
void user_delay(uint32_t ms)
|
void user_delay(uint32_t ms)
|
||||||
{
|
{
|
||||||
register uint32_t r1 asm("r1") = ms;
|
register uint32_t r1 asm("r1") = ms;
|
||||||
@ -16,29 +13,32 @@ void user_delay(uint32_t ms)
|
|||||||
" :: "r" (r1));
|
" :: "r" (r1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int fork(void)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
asm("\
|
||||||
|
mov r0, sp; \
|
||||||
|
svc 3; \
|
||||||
|
mov %0, r0; \
|
||||||
|
" : "=r" (result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void user_main(void)
|
void user_main(void)
|
||||||
{
|
{
|
||||||
gpio(GPIO_MODE, 5, OUTPUT);
|
gpio(GPIO_MODE, 5, OUTPUT);
|
||||||
task_start(task1, 512);
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++) {
|
if (fork() == 0) {
|
||||||
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);
|
|
||||||
while (1) {
|
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);
|
user_delay(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user