📄 ls.c
字号:
/* ls.c
*
* Copyright (c) 1992-2001 by Mike Gleason.
* All rights reserved.
*
*/
#include "syshdrs.h"
#include "util.h"
#include "ls.h"
#include "trace.h"
/* The program keeps a timestamp of 6 months ago and an hour from now, because
* the standard /bin/ls command will print the time (i.e. "Nov 8 09:20")
* instead of the year (i.e. "Oct 27 1996") if a file's timestamp is within
* this period.
*/
time_t gNowMinus6Mon, gNowPlus1Hr;
/* An array of month name abbreviations. This may not be in English. */
char gLsMon[13][4];
/* The program keeps its own cache of directory listings, so it doesn't
* need to re-request them from the server.
*/
LsCacheItem gLsCache[kLsCacheSize];
int gOldestLsCacheItem;
int gLsCacheItemLifetime = kLsCacheItemLifetime;
extern FTPConnectionInfo gConn;
extern char gRemoteCWD[512];
extern int gScreenColumns, gDebug;
void
InitLsCache(void)
{
(void) memset(gLsCache, 0, sizeof(gLsCache));
gOldestLsCacheItem = 0;
} /* InitLsCache */
/* Creates the ls monthname abbreviation array, so we don't have to
* re-calculate them each time.
*/
void InitLsMonths(void)
{
time_t now;
struct tm *ltp;
int i;
(void) time(&now);
ltp = localtime(&now); /* Fill up the structure. */
ltp->tm_mday = 15;
ltp->tm_hour = 12;
for (i=0; i<12; i++) {
ltp->tm_mon = i;
(void) strftime(gLsMon[i], sizeof(gLsMon[i]), "%b", ltp);
gLsMon[i][sizeof(gLsMon[i]) - 1] = '\0';
}
(void) strcpy(gLsMon[i], "BUG");
} /* InitLsMonths */
void InitLs(void)
{
InitLsCache();
InitLsMonths();
} /* InitLs */
/* Deletes an item from the ls cache. */
static void
FlushLsCacheItem(int i)
{
Trace(1, "flush ls cache item: %s\n", gLsCache[i].itempath);
if (gLsCache[i].itempath != NULL)
free(gLsCache[i].itempath);
gLsCache[i].itempath = NULL;
gLsCache[i].expiration = (time_t) 0;
DisposeFileInfoListContents(&gLsCache[i].fil);
} /* FlushLsCacheItem */
/* Clears all items from the ls cache. */
void
FlushLsCache(void)
{
int i;
for (i=0; i<kLsCacheSize; i++) {
if (gLsCache[i].expiration != (time_t) 0) {
FlushLsCacheItem(i);
}
}
} /* FlushLsCache */
/* Checks the cache for a directory listing for the given path. */
int
LsCacheLookup(const char *const itempath)
{
int i, j;
time_t now;
(void) time(&now);
for (i=0, j=gOldestLsCacheItem; i<kLsCacheSize; i++) {
if (--j < 0)
j = kLsCacheSize - 1;
if ((gLsCache[j].expiration != (time_t) 0) && (gLsCache[j].itempath != NULL)) {
if (strcmp(itempath, gLsCache[j].itempath) == 0) {
if (now > gLsCache[j].expiration) {
/* Found it, but it was expired. */
FlushLsCacheItem(j);
return (-1);
}
gLsCache[j].hits++;
return (j);
}
}
}
return (-1);
} /* LsCacheLookup */
/* Saves a directory listing from the given path into the cache. */
static void
LsCacheAdd(const char *const itempath, FileInfoListPtr files)
{
char *cp;
int j;
/* Never cache empty listings in case of errors */
if (files->nFileInfos == 0)
return;
j = LsCacheLookup(itempath);
if (j >= 0) {
/* Directory was already in there;
* Replace it with the new
* contents.
*/
FlushLsCacheItem(j);
}
cp = StrDup(itempath);
if (cp == NULL)
return;
j = gOldestLsCacheItem;
(void) memcpy(&gLsCache[j].fil, files, sizeof(FileInfoList));
(void) time(&gLsCache[j].expiration);
gLsCache[j].expiration += gLsCacheItemLifetime;
gLsCache[j].hits = 0;
gLsCache[j].itempath = cp;
Trace(1, "ls cache add: %s\n", itempath);
/* Increment the pointer. This is a circular array, so if it
* hits the end it wraps over to the other side.
*/
gOldestLsCacheItem++;
if (gOldestLsCacheItem >= kLsCacheSize)
gOldestLsCacheItem = 0;
} /* LsCacheAdd */
/* Does "ls -C", or the nice columnized /bin/ls-style format. */
static void
LsC(FileInfoListPtr dirp, int endChars, FILE *stream)
{
char buf[400];
char buf2[400];
int ncol, nrow;
int i, j, k, l;
int colw;
int n;
FileInfoVec itemv;
FileInfoPtr itemp;
char *cp1, *cp2, *lim;
int screenColumns;
screenColumns = gScreenColumns;
if (screenColumns > 400)
screenColumns = 400;
ncol = (screenColumns - 1) / ((int) dirp->maxFileLen + 2 + /*1or0*/ endChars);
if (ncol < 1)
ncol = 1;
colw = (screenColumns - 1) / ncol;
n = dirp->nFileInfos;
nrow = n / ncol;
if ((n % ncol) != 0)
nrow++;
for (i=0; i<(int) sizeof(buf2); i++)
buf2[i] = ' ';
itemv = dirp->vec;
for (j=0; j<nrow; j++) {
(void) memcpy(buf, buf2, sizeof(buf));
for (i=0, k=j, l=0; i<ncol; i++, k += nrow, l += colw) {
if (k >= n)
continue;
itemp = itemv[k];
cp1 = buf + l;
lim = cp1 + (int) (itemp->relnameLen);
cp2 = itemp->relname;
while (cp1 < lim)
*cp1++ = *cp2++;
if (endChars != 0) {
if (itemp->type == 'l') {
/* Regular ls always uses @
* for a symlink tail, even if
* the linked item is a directory.
*/
*cp1++ = '@';
} else if (itemp->type == 'd') {
*cp1++ = '/';
}
}
}
for (cp1 = buf + sizeof(buf); *--cp1 == ' '; ) {}
++cp1;
*cp1++ = '\n';
*cp1 = '\0';
(void) fprintf(stream, "%s", buf);
Trace(0, "%s", buf);
}
} /* LsC */
/* Converts a timestamp into a recent date string ("May 27 06:33"), or an
* old (or future) date string (i.e. "Oct 27 1996").
*/
void
LsDate(char *dstr, time_t ts)
{
struct tm *gtp;
if (ts == kModTimeUnknown) {
(void) strcpy(dstr, " ");
return;
}
gtp = localtime(&ts);
if (gtp == NULL) {
(void) strcpy(dstr, "Jan 0 1900");
return;
}
if ((ts > gNowPlus1Hr) || (ts < gNowMinus6Mon)) {
(void) sprintf(dstr, "%s %2d %4d",
gLsMon[gtp->tm_mon],
gtp->tm_mday,
gtp->tm_year + 1900
);
} else {
(void) sprintf(dstr, "%s %2d %02d:%02d",
gLsMon[gtp->tm_mon],
gtp->tm_mday,
gtp->tm_hour,
gtp->tm_min
);
}
} /* LsDate */
/* Does "ls -l", or the detailed /bin/ls-style, one file per line . */
void
LsL(FileInfoListPtr dirp, int endChars, int linkedTo, FILE *stream)
{
FileInfoPtr diritemp;
FileInfoVec diritemv;
int i;
char fTail[2];
int fType;
const char *l1, *l2;
char datestr[16];
char sizestr[32];
char plugspec[16];
char plugstr[64];
const char *expad;
fTail[0] = '\0';
fTail[1] = '\0';
(void) time(&gNowPlus1Hr);
gNowMinus6Mon = gNowPlus1Hr - 15552000;
gNowPlus1Hr += 3600;
diritemv = dirp->vec;
#ifdef HAVE_SNPRINTF
(void) snprintf(
plugspec,
sizeof(plugspec) - 1,
#else
(void) sprintf(
plugspec,
#endif
"%%-%ds",
(int) dirp->maxPlugLen
);
if (dirp->maxPlugLen < 29) {
/* We have some extra space to work with,
* so we can space out the columns a little.
*/
expad = " ";
} else {
expad = "";
}
for (i=0; ; i++) {
diritemp = diritemv[i];
if (diritemp == NULL)
break;
fType = (int) diritemp->type;
if (endChars != 0) {
if (fType == 'd')
fTail[0] = '/';
else
fTail[0] = '\0';
}
if (diritemp->rlinkto != NULL) {
if (linkedTo != 0) {
l1 = "";
l2 = "";
} else {
l1 = " -> ";
l2 = diritemp->rlinkto;
}
} else {
l1 = "";
l2 = "";
}
LsDate(datestr, diritemp->mdtm);
if (diritemp->size == kSizeUnknown) {
*sizestr = '\0';
} else {
#ifdef HAVE_SNPRINTF
(void) snprintf(
sizestr,
sizeof(sizestr) - 1,
#else
(void) sprintf(
sizestr,
#endif
#if defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG)
PRINTF_LONG_LONG,
#else
"%ld",
#endif
(longest_int) diritemp->size
);
}
#ifdef HAVE_SNPRINTF
(void) snprintf(
plugstr,
sizeof(plugstr) - 1,
#else
(void) sprintf(
plugstr,
#endif
plugspec,
diritemp->plug
);
(void) fprintf(stream, "%s %12s %s%s %s%s%s%s%s\n",
plugstr,
sizestr,
expad,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -