578 lines
13 KiB
C
578 lines
13 KiB
C
/*
|
|
ChibiOS - Copyright (C) 2006..2022 Giovanni Di Sirio
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <dirent.h>
|
|
|
|
#define NEWLINE_STR "\r\n"
|
|
#define TWIDTH 80
|
|
|
|
static char *dotp = ".";
|
|
static void *bufp = NULL;
|
|
|
|
static bool Rflg = false;
|
|
static bool aflg = false;
|
|
static bool dflg = false;
|
|
static bool fflg = false;
|
|
static bool lflg = false;
|
|
static bool qflg = false;
|
|
|
|
struct afile {
|
|
char ftype;
|
|
short fnlink;
|
|
mode_t fflags;
|
|
off_t fsize;
|
|
char *fname;
|
|
};
|
|
|
|
static void usage(void) {
|
|
fprintf(stderr, "Usage: ls [<opts>] [<file>]..." NEWLINE_STR);
|
|
fprintf(stderr, "Options:" NEWLINE_STR);
|
|
fprintf(stderr, " -R list subdirectories recursively" NEWLINE_STR);
|
|
fprintf(stderr, " -a do not ignore entries starting with ." NEWLINE_STR);
|
|
fprintf(stderr, " -d list directories themselves, not their contents" NEWLINE_STR);
|
|
fprintf(stderr, " -f do not sort, enable -a, disable -l" NEWLINE_STR);
|
|
fprintf(stderr, " -l use a long listing format" NEWLINE_STR);
|
|
fprintf(stderr, " -q print ? instead of nongraphic characters" NEWLINE_STR);
|
|
}
|
|
|
|
static void freeall(void) {
|
|
|
|
if (bufp != NULL) {
|
|
free(bufp);
|
|
}
|
|
}
|
|
|
|
static void error(const char *s) {
|
|
|
|
fprintf(stderr, "ls: %s" NEWLINE_STR, s);
|
|
exit(1);
|
|
}
|
|
|
|
static bool gstat(struct afile *fp, const char *file, bool flag) {
|
|
|
|
memset(fp, 0, sizeof (struct afile));
|
|
fp->ftype = '-';
|
|
|
|
if (flag || lflg) {
|
|
int ret;
|
|
struct stat stb;
|
|
|
|
ret = stat(file, &stb);
|
|
if (ret < 0) {
|
|
fprintf(stderr, "%s not found\n", file);
|
|
return true;
|
|
}
|
|
|
|
fp->fsize = stb.st_size;
|
|
fp->fflags = stb.st_mode & ~S_IFMT;
|
|
fp->fnlink = stb.st_nlink;
|
|
switch (stb.st_mode & S_IFMT) {
|
|
case S_IFDIR:
|
|
fp->ftype = 'd';
|
|
break;
|
|
case S_IFBLK:
|
|
fp->ftype = 'b';
|
|
fp->fsize = stb.st_rdev;
|
|
break;
|
|
case S_IFCHR:
|
|
fp->ftype = 'c';
|
|
fp->fsize = stb.st_rdev;
|
|
break;
|
|
case S_IFSOCK:
|
|
fp->ftype = 's';
|
|
fp->fsize = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static char *fmtmode(char *p, mode_t flags) {
|
|
|
|
if ((flags & S_IRUSR) != (mode_t)0) {
|
|
*p++ = 'r';
|
|
}
|
|
if ((flags & S_IWUSR) != (mode_t)0) {
|
|
*p++ = 'w';
|
|
}
|
|
if ((flags & S_IXUSR) != (mode_t)0) {
|
|
*p++ = ((flags & S_ISUID) == (mode_t)0) ? 'x' : 's';
|
|
}
|
|
if ((flags & S_IRGRP) != (mode_t)0) {
|
|
*p++ = 'r';
|
|
}
|
|
if ((flags & S_IWGRP) != (mode_t)0) {
|
|
*p++ = 'w';
|
|
}
|
|
if ((flags & S_IXGRP) != (mode_t)0) {
|
|
*p++ = ((flags & S_ISGID) == (mode_t)0) ? 'x' : 's';
|
|
}
|
|
if ((flags & S_IROTH) != (mode_t)0) {
|
|
*p++ = 'r';
|
|
}
|
|
if ((flags & S_IWOTH) != (mode_t)0) {
|
|
*p++ = 'w';
|
|
}
|
|
if ((flags & S_IXOTH) != (mode_t)0) {
|
|
*p++ = 'x';
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
static char *fmtlstuff(const struct afile *p) {
|
|
static char lstuffbuf[TWIDTH];
|
|
static char gname[32], uname[32], fsize[32], ftime[32];
|
|
char *lp = lstuffbuf;
|
|
int n;
|
|
|
|
n = TWIDTH;
|
|
|
|
/* type mode uname gname fsize ftime */
|
|
/* get uname */
|
|
{
|
|
char *cp = /*getname(p->fuid)*/"root";
|
|
if (cp)
|
|
n -= snprintf(uname, n, "%-9.9s", cp);
|
|
else
|
|
n -= snprintf(uname, n, "%-9d",/* p->fuid*/0);
|
|
}
|
|
|
|
/* get gname */
|
|
{
|
|
char *cp = /*getgroup(p->fgid)*/"root";
|
|
if (cp)
|
|
n -= snprintf(gname, n, "%-9.9s", cp);
|
|
else
|
|
n -= snprintf(gname, n, "%-9d", /*p->fgid*/0);
|
|
}
|
|
|
|
/* get fsize */
|
|
if (p->ftype == 'b' || p->ftype == 'c') {
|
|
n -= snprintf(fsize, n, "%3d,%4d", /*major(p->fsize)*/0, /*minor(p->fsize)*/0);
|
|
}
|
|
else if (p->ftype == 's') {
|
|
n -= snprintf(fsize, n, "%8d", 0);
|
|
}
|
|
else {
|
|
n -= snprintf(fsize, n, "%8d", (int)p->fsize);
|
|
}
|
|
|
|
/* get ftime */
|
|
{
|
|
#if 0
|
|
char *cp = ctime(&p->fmtime);
|
|
if ((p->fmtime < sixmonthsago) || (p->fmtime > now))
|
|
(void)sprintf(ftime, " %-7.7s %-4.4s ", cp + 4, cp + 20);
|
|
else
|
|
(void)sprintf(ftime, " %-12.12s ", cp + 4);
|
|
#endif
|
|
n += snprintf(ftime, n, " %-7.7s %-4.4s ", "Jan 1", "1990");
|
|
}
|
|
|
|
/* splat */
|
|
*lp++ = p->ftype;
|
|
lp = fmtmode(lp, p->fflags);
|
|
(void) snprintf(lp, n, "%3d %s%s%s%s", /*p->fnl*/0, uname, gname, fsize, ftime);
|
|
|
|
return lstuffbuf;
|
|
}
|
|
|
|
static char *fmtentry(const struct afile *fp) {
|
|
static char fmtres[TWIDTH];
|
|
register char *cp, *dp;
|
|
|
|
(void) snprintf(fmtres, TWIDTH, "%s%s%s",
|
|
/*iflg ? fmtinum(fp) :*/ "",
|
|
/*sflg ? fmtsize(fp) :*/ "",
|
|
lflg ? fmtlstuff(fp) : "");
|
|
|
|
dp = &fmtres[strlen(fmtres)];
|
|
for (cp = fp->fname; *cp; cp++) {
|
|
if (qflg && ((*cp < ' ') || (*cp >= 0x7F))) {
|
|
*dp++ = '?';
|
|
}
|
|
else {
|
|
*dp++ = *cp;
|
|
}
|
|
}
|
|
*dp++ = 0;
|
|
|
|
return fmtres;
|
|
}
|
|
|
|
static void formatf(const struct afile *fp0, const struct afile *fplast) {
|
|
int n;
|
|
|
|
n = (int)(fplast - fp0);
|
|
if (n > 0) {
|
|
const struct afile *fp;
|
|
int i, j, columns, lines, width;
|
|
|
|
/* Determining number and size of columns.*/
|
|
if (lflg) {
|
|
columns = 1;
|
|
}
|
|
else {
|
|
width = 0;
|
|
for (fp = fp0; fp < fplast; fp++) {
|
|
int len;
|
|
|
|
len = (int)strlen(fmtentry(fp));
|
|
if (len > width) {
|
|
width = len;
|
|
}
|
|
width += 2;
|
|
columns = TWIDTH / width;
|
|
if (columns == 0) {
|
|
columns = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
lines = (n + columns - 1) / columns;
|
|
for (i = 0; i < lines; i++) {
|
|
for (j = 0; j < columns; j++) {
|
|
int w;
|
|
char *cp;
|
|
|
|
fp = fp0 + j * lines + i;
|
|
cp = fmtentry(fp);
|
|
printf("%s", cp);
|
|
if (fp + lines >= fplast) {
|
|
printf(NEWLINE_STR);
|
|
break;
|
|
}
|
|
w = (int)strlen(cp);
|
|
while (w < width) {
|
|
w++;
|
|
putchar(' ');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
register struct afile *fp;
|
|
long width = 0, w, nentry = fplast - fp0;
|
|
int i, j, columns, lines;
|
|
char *cp;
|
|
|
|
if (fp0 == fplast)
|
|
return;
|
|
if (lflg || Cflg == 0)
|
|
columns = 1;
|
|
else {
|
|
for (fp = fp0; fp < fplast; fp++) {
|
|
long len = strlen(fmtentry(fp));
|
|
|
|
if (len > width)
|
|
width = len;
|
|
}
|
|
if (usetabs)
|
|
width = (width + 8) &~ 7;
|
|
else
|
|
width += 2;
|
|
columns = twidth / (int)width;
|
|
if (columns == 0)
|
|
columns = 1;
|
|
}
|
|
lines = ((int)nentry + columns - 1) / columns;
|
|
for (i = 0; i < lines; i++) {
|
|
for (j = 0; j < columns; j++) {
|
|
fp = fp0 + j * lines + i;
|
|
cp = fmtentry(fp);
|
|
printf("%s", cp);
|
|
if (fp + lines >= fplast) {
|
|
printf("\n");
|
|
break;
|
|
}
|
|
w = strlen(cp);
|
|
while (w < width)
|
|
if (usetabs) {
|
|
w = (w + 8) &~ 7;
|
|
putchar('\t');
|
|
} else {
|
|
w++;
|
|
putchar(' ');
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static int fcmp(const void *a, const void *b) {
|
|
const struct afile *f1 = a, *f2 = b;
|
|
|
|
/* Directories first then alphabetic order.*/
|
|
if ((f1->ftype == 'd') && (f2->ftype != 'd')) {
|
|
return -1;
|
|
}
|
|
if ((f1->ftype != 'd') && (f2->ftype == 'd')) {
|
|
return 1;
|
|
}
|
|
|
|
return strcmp(f1->fname, f2->fname);
|
|
}
|
|
|
|
static long getdir(char *dir, struct afile **pfp0, struct afile **pfplast) {
|
|
struct afile *fp;
|
|
DIR *dirp;
|
|
struct dirent *dp;
|
|
long nb, nent = 20;
|
|
|
|
dirp = opendir(dir);
|
|
if (dirp == NULL) {
|
|
*pfp0 = *pfplast = NULL;
|
|
printf("%s unreadable\n", dir); /* not stderr! */
|
|
return (0);
|
|
}
|
|
fp = *pfp0 = (struct afile*)calloc(nent, sizeof(struct afile));
|
|
if (fp == 0L) {
|
|
fprintf(stderr, "ls: out of memory\n");
|
|
exit(1);
|
|
}
|
|
*pfplast = *pfp0 + nent;
|
|
nb = 0;
|
|
while ((dp = readdir(dirp)) != NULL) {
|
|
if (dp->d_ino == 0)
|
|
continue;
|
|
if (((aflg == false) && (dp->d_name[0] == '.') && (/*(Aflg == 0)*/false)) ||
|
|
(dp->d_name[1] == 0) ||
|
|
((dp->d_name[1] == '.') && (dp->d_name[2] == 0)))
|
|
continue;
|
|
if (gstat(fp, cat(dir, dp->d_name), /*Fflg + */Rflg/*, &nb*/))
|
|
continue;
|
|
fp->fnum = dp->d_ino;
|
|
fp->fname = savestr(dp->d_name);
|
|
fp++;
|
|
if (fp == *pfplast) {
|
|
*pfp0 = (struct afile*)realloc((char*)*pfp0,
|
|
2 * nent * sizeof(struct afile));
|
|
if (*pfp0 == 0) {
|
|
fprintf(stderr, "ls: out of memory\n");
|
|
exit(1);
|
|
}
|
|
fp = *pfp0 + nent;
|
|
*pfplast = fp + nent;
|
|
nent *= 2;
|
|
}
|
|
}
|
|
closedir(dirp);
|
|
*pfplast = fp;
|
|
return kbytes(dbtob(nb));
|
|
}
|
|
|
|
static void formatd(char *name, int title) {
|
|
struct afile *fp;
|
|
struct subdirs *dp;
|
|
struct afile *dfp0, *dfplast;
|
|
long nkb;
|
|
|
|
nkb = getdir(name, &dfp0, &dfplast);
|
|
if (dfp0 == 0)
|
|
return;
|
|
if (fflg == 0)
|
|
qsort(dfp0, dfplast - dfp0, sizeof(struct afile), fcmp);
|
|
if (title)
|
|
printf("%s:\n", name);
|
|
if (lflg/* || sflg*/)
|
|
printf("total %ld\n", nkb);
|
|
formatf(dfp0, dfplast);
|
|
if (Rflg)
|
|
for (fp = dfplast - 1; fp >= dfp0; fp--) {
|
|
if (fp->ftype != 'd' || !strcmp(fp->fname, ".")
|
|
|| !strcmp(fp->fname, ".."))
|
|
continue;
|
|
dp = (struct subdirs*)malloc(sizeof(struct subdirs));
|
|
if (dp == 0L) { /*PATCH GIOV.*/
|
|
fprintf(stderr, "ls: out of memory\n");
|
|
exit(1);
|
|
}
|
|
dp->sd_name = savestr(cat(name, fp->fname));
|
|
dp->sd_next = subdirs;
|
|
subdirs = dp;
|
|
}
|
|
for (fp = dfp0; fp < dfplast; fp++) {
|
|
if ((fp->fflags & ISARG) == 0 && fp->fname)
|
|
free(fp->fname);
|
|
if (fp->flinkto)
|
|
free(fp->flinkto);
|
|
}
|
|
free((char*)dfp0);
|
|
}
|
|
|
|
/*
|
|
* Application entry point.
|
|
*/
|
|
int main(int argc, char *argv[], char *envp[]) {
|
|
int i;
|
|
struct afile *fp, *fp0, *fplast;
|
|
|
|
(void)envp;
|
|
|
|
#if 1
|
|
/* Enable for RAM debug.*/
|
|
asm volatile ("bkpt");
|
|
#endif
|
|
|
|
/* Parsing arguments.*/
|
|
argv++;
|
|
argc--;
|
|
while ((argc > 0) && (*argv[0] == '-')) {
|
|
argv[0]++;
|
|
while (*argv[0] != '\0') {
|
|
switch (*argv[0]) {
|
|
case 'R':
|
|
Rflg = true;
|
|
break;
|
|
case 'a':
|
|
aflg = true;
|
|
break;
|
|
case 'd':
|
|
dflg = true;
|
|
break;
|
|
case 'f':
|
|
fflg = true;
|
|
break;
|
|
case 'l':
|
|
lflg = true;
|
|
break;
|
|
case 'q':
|
|
qflg = true;
|
|
break;
|
|
default:
|
|
usage();
|
|
return 1;
|
|
}
|
|
}
|
|
argv[0]++;
|
|
}
|
|
|
|
/* The "f" flag has side effects.*/
|
|
if (fflg) {
|
|
aflg = true;
|
|
lflg = false;
|
|
/*sflg = false;*/
|
|
/*tflg = false*/;
|
|
}
|
|
|
|
/* Case where no paths are specified.*/
|
|
if (argc == 0) {
|
|
argc++;
|
|
argv = &dotp;
|
|
}
|
|
|
|
/* Allocating a single big buffer for all files.*/
|
|
bufp = calloc(argc, sizeof (struct afile));
|
|
fp0 = (struct afile *)bufp;
|
|
if (bufp == NULL) {
|
|
error("out of memory");
|
|
}
|
|
|
|
/* Scanning all arguments and populating the array.*/
|
|
fp = fp0;
|
|
for (i = 0; i < argc; i++) {
|
|
if (!gstat(fp, *argv, true)) {
|
|
fp->fname = *argv;
|
|
fp++;
|
|
}
|
|
argv++;
|
|
}
|
|
fplast = fp;
|
|
|
|
/* Sorting the array, if not disabled.*/
|
|
if (!fflg) {
|
|
qsort(fp0, fplast - fp0, sizeof (struct afile), fcmp);
|
|
}
|
|
|
|
if (dflg) {
|
|
/* Not entering directories.*/
|
|
formatf(fp0, fplast);
|
|
}
|
|
else {
|
|
/* Entering directories. TODO */
|
|
if (fflg) {
|
|
fp = fp0;
|
|
}
|
|
else {
|
|
/* Skipping directories.*/
|
|
for (fp = fp0; fp < fplast && fp->ftype != 'd'; fp++) {
|
|
}
|
|
formatf(fp0, fp);
|
|
}
|
|
|
|
if (fp < fplast) {
|
|
if (fp > fp0) {
|
|
printf("\n");
|
|
}
|
|
#if 0
|
|
for (;;) {
|
|
formatd(fp->fname, argc > 1);
|
|
while (subdirs) {
|
|
struct subdirs *t;
|
|
|
|
t = subdirs;
|
|
subdirs = t->sd_next;
|
|
printf("\n");
|
|
formatd(t->sd_name, 1);
|
|
free((void *)t->sd_name);
|
|
free((void *)t);
|
|
}
|
|
if (++fp == fplast)
|
|
break;
|
|
printf("\n");
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
freeall();
|
|
return 0;
|
|
#if 0
|
|
if (argc > 2) {
|
|
fprintf(stderr, "Usage: ls [<dirpath>]" NEWLINE_STR);
|
|
return 1;
|
|
}
|
|
|
|
if (argc == 1) {
|
|
path = ".";
|
|
}
|
|
else {
|
|
path = argv[1];
|
|
}
|
|
|
|
dirp = opendir(path);
|
|
if (dirp == NULL) {
|
|
fprintf(stderr, "ls: %s" NEWLINE_STR, strerror(errno));
|
|
return 1;
|
|
}
|
|
|
|
while ((dep = readdir(dirp)) != NULL) {
|
|
printf("%s" NEWLINE_STR, dep->d_name);
|
|
}
|
|
|
|
closedir(dirp);
|
|
#endif
|
|
}
|