diff --git a/Makefile b/Makefile index d90d690..05c12dd 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,7 @@ SRC = \ chgrp.c \ chmod.c \ chown.c \ + chroot.c \ chvt.c \ cksum.c \ cmp.c \ @@ -32,6 +33,7 @@ SRC = \ date.c \ dirname.c \ echo.c \ + env.c \ false.c \ fold.c \ grep.c \ @@ -53,6 +55,7 @@ SRC = \ rmdir.c \ sleep.c \ sort.c \ + split.c \ sync.c \ tail.c \ tee.c \ @@ -65,6 +68,7 @@ SRC = \ unlink.c \ seq.c \ wc.c \ + who.c \ yes.c OBJ = $(SRC:.c=.o) $(LIB) diff --git a/TODO b/TODO index 217829f..00d3839 100644 --- a/TODO +++ b/TODO @@ -10,8 +10,6 @@ diff [-ru] file1 file2 du [-hdi] [path] -env [-u] [name=value...] [command] - expand [-i] [-t N] [file...] expr [expression] @@ -32,8 +30,6 @@ seq [-s string] [N [N]] N sha1sum [-c] [file...] -split [-a N] [-b N] [-l N] [input [prefix]] - test [expression...] tr string1 [string2] diff --git a/chroot.1 b/chroot.1 new file mode 100644 index 0000000..48cbe19 --- /dev/null +++ b/chroot.1 @@ -0,0 +1,26 @@ +.TH CHROOT 1 sbase\-VERSION +.SH NAME +chroot \- invoke a command with a different root directory +.SH SYNOPSIS +.B chroot +.IR dir +.RI [ command +.RI [ arg ...]] + +.SH DESCRIPTION +.B chroot +runs +.IR command +after changing the root directory to +.IR dir +with the +.B chroot +system call, and changing the working directory to the new root. + +If +.IR command +is not specified, an interactive shell is started in the new root. + +.SH SEE ALSO +.IR chroot (2) +.IR chdir (2) diff --git a/chroot.c b/chroot.c new file mode 100644 index 0000000..3abab0f --- /dev/null +++ b/chroot.c @@ -0,0 +1,38 @@ +#include +#include +#include "util.h" + +static void usage(void); + +int +main(int argc, char **argv) +{ + char *shell[] = {"/bin/sh", "-i", NULL}; + + if(getenv("SHELL")) + shell[0] = getenv("SHELL"); + + if(argc < 2) + usage(); + + if(chroot(argv[1]) == -1) + eprintf("chroot: '%s':", argv[1]); + + if(chdir("/") == -1) + eprintf("chroot:"); + + if(argc == 2) { + execvp(*shell, shell); + } else { + execvp(argv[2], argv+2); + } + + eprintf("chroot: '%s':", argv[2]); + return 1; +} + +void +usage(void) +{ + eprintf("usage: chroot dir [command [arg...]]\n"); +} diff --git a/env.1 b/env.1 new file mode 100644 index 0000000..b77fb2f --- /dev/null +++ b/env.1 @@ -0,0 +1,41 @@ +.TH ENV 1 sbase\-VERSION +.SH NAME +env \- modify the environment, then print it or run a command. +.SH SYNOPSIS +.B env +.RB [ \-i ] +.RB [ \-u +.IR name ]... +.RI [ name=value ]... +.RI [ cmd +.RI [ arg ...]] + +.SH DESCRIPTION +.B env +removes part of the environment according to the flags, then adds or +sets each variable specified by +.IR name +to equal +.IR value . + +If +.IR cmd +is given, it is executed in this new environment; otherwise, the +modified environment is printed to standard out. + +.SH OPTIONS +.TP +.B \-i +Comptetely ignore the existing environment; start fresh. + +.TP +.B \-u name +Unsets +.IR name +from the environment. + +.SH SEE ALSO +.IR printenv (1) +.IR putenv (3) +.IR environ (7) + diff --git a/env.c b/env.c new file mode 100644 index 0000000..718b13e --- /dev/null +++ b/env.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include +#include "util.h" + +extern char **environ; + +static void usage(void); + +int +main(int argc, char **argv) +{ + ARGBEGIN { + case 'i': + clearenv(); + break; + case 'u': + unsetenv(EARGF(usage())); + break; + default: + usage(); + } ARGEND; + + for(; *argv && strchr(*argv, '='); argv++) + putenv(*argv); + + if(*argv) { + execvp(*argv, argv); + enprintf(127-(errno!=EEXIST), "env: '%s':", *argv); + } + + while(environ && *environ) + printf("%s\n", *environ++); + + return 0; +} + +void +usage(void) +{ + eprintf("usage: env [-i] [-u name]... [name=value]... [cmd [arg...]]\n"); +} diff --git a/split.1 b/split.1 new file mode 100644 index 0000000..e615168 --- /dev/null +++ b/split.1 @@ -0,0 +1,58 @@ +.TH SPLIT 1 sbase\-VERSION +.SH NAME +split \- split up a file +.SH SYNOPSIS +.B split +.RB [ \-d ] +.RB [ \-a +.IR len ] +.RB [ \-b +.RI [ bytes [k|m|g]]] +.RB [ \-l +.RI [ lines ]] +.RI [ input +.RI [ prefix ]] + +.SH DESCRIPTION +.B split +Reads a file, splitting it into smaller files, every +.IR bytes +bytes +or +.IR lines +lines. If +.B split +runs out of filenames before all the data can be written, it stops at the +last valid filename, leaving all the written data on the disk. + +The +.IR b +and +.IR l +flags are mutually exclusive. Only the last one specified will be obeyed. + +.SH OPTIONS +.TP +.B \-d +Use decimal suffixes rather than alphabetical. + +.TP +.B \-a "len" +Set the suffix length to +.IR len +characters long. + +.TP +.B \-b [bytes[k|m|g]] +Start a new file every +.IR bytes +bytes. The units k, m, and g are case insensitive, and powers of 2, not 10. + +.TP +.B \-l [lines] +Start a new file every +.IR lines +lines. + +.SH SEE ALSO +.IR cat (1) diff --git a/split.c b/split.c new file mode 100644 index 0000000..87e1099 --- /dev/null +++ b/split.c @@ -0,0 +1,138 @@ +#include +#include +#include +#include +#include +#include +#include "util.h" + +static int itostr(char *, int, int); +static FILE *nextfile(FILE *, char *, int, int); +static void usage(void); + +static int base = 26, start = 'a'; + +int +main(int argc, char **argv) +{ + int plen, slen = 2; + int ch; + char name[NAME_MAX+1]; + char *prefix = "x"; + char *file = NULL; + char *tmp, *end; + uint64_t sizes['M'+1]; + uint64_t size = 1000, scale, n; + int always = 0; + FILE *in=stdin, *out=NULL; + + sizes['K'] = 1024; + sizes['M'] = 1024L*1024L; + sizes['G'] = 1024L*1024L*1024L; + + ARGBEGIN { + case 'b': + always = 1; + tmp = ARGF(); + if(tmp == NULL) + break; + + size = strtoull(tmp, &end, 10); + if(*end == '\0') + break; + if(strchr("KMG", toupper(*end)) == NULL || end[1] != '\0') + usage(); + scale = sizes[toupper(*end)]; + if(size > (UINT64_MAX/scale)) + eprintf("split: '%s': out of range\n", tmp); + size *= scale; + break; + case 'l': + always = 0; + tmp = ARGF(); + if(tmp) + size = estrtol(tmp, 10); + break; + case 'a': + slen = estrtol(EARGF(usage()), 10); + break; + case 'd': + base = 10; + start = '0'; + break; + default: + usage(); + } ARGEND; + + if(*argv) + file = *argv++; + if(*argv) + prefix = *argv++; + if(*argv) + usage(); + + plen = strlen(prefix); + if(plen+slen > NAME_MAX) + eprintf("split: names cannot exceed %d bytes", NAME_MAX); + strcpy(name, prefix); + + if(file && strcmp(file, "-") != 0) { + in = fopen(file, "r"); + if(!in) + eprintf("split: '%s':", file); + } + +Nextfile: + while((out = nextfile(out, name, plen, slen))) { + n = 0; + while((ch = getc(in)) != EOF) { + putc(ch, out); + n += (always || ch == '\n'); + if(n >= size) + goto Nextfile; + } + fclose(out); + break; + } + return 0; + +} + +void +usage(void) +{ + eprintf("usage: split [-d] [-a len] [-b [bytes[k|m|g]]] [-l [lines]] [input [prefix]]\n"); +} + + +int +itostr(char *str, int x, int n) +{ + str[n] = '\0'; + while(n-- > 0) { + str[n] = start + (x % base); + x /= base; + } + if(x) + return -1; + return 0; +} + +FILE * +nextfile(FILE *f, char *buf, int plen, int slen) +{ + static int n = 0; + int s; + + if(f) + fclose(f); + s = itostr(buf+plen, n++, slen); + if(s == -1) + return NULL; + + f = fopen(buf, "w"); + if(!f) + eprintf("split: '%s':", buf); + return f; +} + diff --git a/who.1 b/who.1 new file mode 100644 index 0000000..68ca7f1 --- /dev/null +++ b/who.1 @@ -0,0 +1,24 @@ +.TH WHO 1 sbase\-VERSION +.SH NAME +who \- print who has logged on +.SH SYNOPSIS +.B who + +.SH DESCRIPTION +.B who +prints a list of who has logged on, their controlling tty, and the +time at which they logged on. + +.SH BUGS +.B who +relies on the utmp file to be updated responsibly. This +doesn't always happen, which can cause who to print completely +bogus data. + +musl\-libc currently implements all utmpx functions as stubs Obviously, +this command cannot work under such conditions. + +.SH SEE ALSO +.IR getutxent (3) +.IR utmpx (5) + diff --git a/who.c b/who.c new file mode 100644 index 0000000..08c3c2b --- /dev/null +++ b/who.c @@ -0,0 +1,37 @@ +#include +#include +#include +#include +#include +#include +#include "util.h" + +static void usage(void); + +int +main(int argc, char **argv) +{ + struct utmpx *ut; + time_t t; + char timebuf[sizeof "yyyy-mm-dd hh:mm"]; + + if(argc!=1) + usage(); + + while((ut=getutxent())) { + if(ut->ut_type != USER_PROCESS) + continue; + t = ut->ut_tv.tv_sec; + strftime(timebuf, sizeof timebuf, "%Y-%m-%d %H:%M", localtime(&t)); + printf("%-8s %-12s %-16s\n", ut->ut_user, ut->ut_line, timebuf); + } + endutxent(); + return 0; +} + +void +usage(void) +{ + eprintf("usage: who\n"); +} +