1693 lines
36 KiB
C
1693 lines
36 KiB
C
/* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */
|
|
/* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */
|
|
/* All Rights Reserved */
|
|
|
|
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF */
|
|
/* UNIX System Laboratories, Inc. */
|
|
/* The copyright notice above does not evidence any */
|
|
/* actual or intended publication of such source code. */
|
|
|
|
#ident "$Revision: 1.66 $"
|
|
|
|
/* Copyright (c) 1987, 1988 Microsoft Corporation */
|
|
/* All Rights Reserved */
|
|
|
|
/* This Module contains Proprietary Information of Microsoft */
|
|
/* Corporation and should be treated as Confidential. */
|
|
/*
|
|
* Copyright (c) 1986, 1987, 1988, Sun Microsystems, Inc.
|
|
* All Rights Reserved.
|
|
*/
|
|
/*
|
|
* list file or directory
|
|
*/
|
|
/***************************************************************************
|
|
* Command: ls
|
|
* Inheritable Privileges: P_DACREAD,P_MACREAD
|
|
* Fixed Privileges: None
|
|
* Notes: lists contents of directories
|
|
*
|
|
***************************************************************************/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/acl.h>
|
|
#include <sys/euc.h>
|
|
#include <sys/mac_label.h>
|
|
#include <sys/mkdev.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/capability.h>
|
|
#include <sys/mac.h>
|
|
#include <ctype.h>
|
|
#include <curses.h>
|
|
#include <deflt.h>
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <getwidth.h>
|
|
#include <grp.h>
|
|
#include <invent.h>
|
|
#include <libgen.h>
|
|
#include <limits.h>
|
|
#include <locale.h>
|
|
#include <mls.h>
|
|
#include <paths.h>
|
|
#include <proj.h>
|
|
#include <pfmt.h>
|
|
#include <pwd.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <term.h>
|
|
#include <termios.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#define BFSIZE 16
|
|
|
|
/* Date and time formats */
|
|
|
|
#define FORMAT1 " %b %e %Y "
|
|
#define FORMAT1ID ":321"
|
|
#define FORMAT2 " %b %e %H:%M " /*
|
|
* b --- abbreviated month name
|
|
e --- day number
|
|
Y --- year in the form ccyy
|
|
H --- hour (24 hour version)
|
|
M --- minute */
|
|
#define FORMAT2ID ":322"
|
|
|
|
#define DEFFILE "mac" /* /etc/default file containing the default LTDB lvl */
|
|
#define LTDB_DEFAULT 2 /* default LID for non-readable LTDB */
|
|
|
|
typedef struct {
|
|
char *lnamep; /* file name */
|
|
char ltype; /* filetype */
|
|
char aclindicator; /* indication of ACL existence on file */
|
|
char lnum_len; /* length of lnum */
|
|
char lsize_len; /* length of lsize */
|
|
char lblocks_len; /* length of lblocks */
|
|
char lnl_len; /* length of lnl */
|
|
char lmaj_len; /* length of major dev number */
|
|
char lmin_len; /* length of minor dev number */
|
|
ino64_t lnum; /* inode number of file */
|
|
off64_t lsize; /* filesize or major/minor dev numbers */
|
|
blkcnt64_t lblocks; /* number of file blocks */
|
|
mode_t lflags; /* 0777 bits used as r,w,x permissions */
|
|
nlink_t lnl; /* number of links to file */
|
|
uid_t luid; /* userid */
|
|
gid_t lgid; /* groupid */
|
|
long lprojid; /* project id */
|
|
timespec_t lmtime; /* timestamp */
|
|
char *flinkto; /* symbolic link contents */
|
|
char *label; /* file label string */
|
|
char *cap; /* capability set */
|
|
char *acl; /* access control list */
|
|
char *dacl; /* directory default access control list */
|
|
} lbuf_t;
|
|
|
|
typedef struct dchain {
|
|
char *dc_name; /* path name */
|
|
struct dchain *dc_next; /* next directory in the chain */
|
|
} dchain_t;
|
|
|
|
static dchain_t *dfirst; /* start of the dir chain */
|
|
static dchain_t *dtemp; /* temporary - used for linking */
|
|
static char *curdir; /* the current directory */
|
|
|
|
static int nfiles = 0; /* number of flist entries in current use */
|
|
static int nargs = 0; /* number of flist entries used for arguments */
|
|
static int maxfils = 0; /* number of flist/lbuf entries allocated */
|
|
static int maxn = 0; /* number of flist entries with lbufs assigned */
|
|
#define QUANTN 128 /* allocation growth quantum */
|
|
static int firstIsDirFlag = 0;
|
|
|
|
static lbuf_t *nxtlbf; /* pointer to next lbuf to be assigned */
|
|
static lbuf_t **flist; /* pointer to list of lbuf pointers */
|
|
|
|
static int Aflg; /* 4.3BSD ls option: like -a but don't list . and .. */
|
|
static int Dflg; /* Access Control List(s) */
|
|
static int Hflg; /* -H option prevents link chasing with -F */
|
|
static int Mflg; /* Mandatory Access Control label */
|
|
static int Pflg; /* Capability (Privilege) set */
|
|
static int Sflg; /* show string-based special files as strings */
|
|
static int acl_enabled; /* Access Control Lists supported */
|
|
static int aflg, bflg, cflg, dflg, fflg, gflg, iflg, jflg, lflg, mflg;
|
|
static int nflg, oflg, pflg, qflg, sflg, tflg, uflg, xflg, zflg, hflg;
|
|
static int Cflg, Fflg, Rflg, Lflg, Zflg;
|
|
static int rflg = 1; /* initialized to 1 for special use in compare() */
|
|
static mode_t flags;
|
|
static int err = 0; /* Contains return code */
|
|
static int do_lnum_len, do_lsize_len, do_lblocks_len, do_lnl_len;
|
|
static int lnum_len, lsize_len, lblocks_len, lnl_len, lmaj_len, lmin_len;
|
|
|
|
#define DFL_NUM_LEN 5
|
|
#define DFL_SIZE_LEN 6
|
|
#define DFL_BLOCKS_LEN 4
|
|
#define DFL_NL_LEN 4
|
|
#define DFL_MAJOR_LEN 3
|
|
#define DFL_MINOR_LEN 3
|
|
|
|
/*
|
|
* print level information
|
|
* -1: don't print level
|
|
* LVL_ALIAS: print alias name
|
|
* LVL_FULL: print fully qualified level name
|
|
*/
|
|
static int lvlformat;
|
|
#define SHOWLVL() (lvlformat != -1)
|
|
|
|
static uid_t lastuid = (uid_t)-1;
|
|
static gid_t lastgid = (gid_t)-1;
|
|
static int lastprojid = -1;
|
|
|
|
static int statreq; /* is > 0 if any of sflg, (n)lflg, tflg are on */
|
|
|
|
#define LS_UID 0
|
|
#define LS_GID 1
|
|
#define LS_PRID 2
|
|
|
|
char option_str[] = "-RadC1xmnloghrtucpFbqisfLAHMDPSj";
|
|
|
|
static char dotp[] = ".";
|
|
/* assumed 15 = max. length of user/group name */
|
|
static char tbufu[BFSIZE], tbufg[BFSIZE], tbufp[BFSIZE];
|
|
|
|
/* total number of blocks of files in a directory */
|
|
static blkcnt64_t tblocks;
|
|
static timespec_t year;
|
|
static timespec_t now;
|
|
|
|
static int colwidth;
|
|
static int num_cols = 80;
|
|
static int filewidth;
|
|
static int fixedwidth;
|
|
static int lvlwidth;
|
|
static int normwidth;
|
|
static int extrawidth;
|
|
static int nomocore;
|
|
static int need_new_col; /* need a new column no matter what */
|
|
static int curcol;
|
|
|
|
static struct winsize win;
|
|
|
|
static char time_buf[50]; /* array to hold day and time */
|
|
|
|
static eucwidth_t wp;
|
|
static int difset1;
|
|
static int difset2;
|
|
static int difset3;
|
|
|
|
#define STRCHUNK (32 * 1024)
|
|
static char **strspace;
|
|
static int strspacelen;
|
|
static int strcurblock = -1;
|
|
static int strcuroffset = STRCHUNK;
|
|
|
|
static const char nomem[] = ":312:Out of memory: %s\n";
|
|
static const char usage[] = ":805:Usage: %s %s [files]\n";
|
|
|
|
extern int _pw_stayopen;
|
|
extern int _getpwent_no_shadow;
|
|
|
|
static int argcompare(const void *pp1, const void *pp2);
|
|
static void column(void);
|
|
static int compare(const void *pp1, const void *pp2);
|
|
static int getname(id_t uid, char *buf, int type);
|
|
static lbuf_t *gstat(char *file, int argfl);
|
|
static void ls_select(int *pairp);
|
|
static char *makename(char *dir, char *file);
|
|
static void morestrspace(void);
|
|
static void new_line(void);
|
|
static void pdirectory(char *name, int title, int lp);
|
|
static void pem(lbuf_t **slp, lbuf_t **lp, int tot_flag);
|
|
static void pentry(lbuf_t *ap);
|
|
static void pmode(mode_t aflag);
|
|
static void pprintf(char *s1, char *s2, char *s3);
|
|
static void rddir(char *dir);
|
|
static void resetstr(void);
|
|
static char *savestr(char *s);
|
|
static int setcount(char *s);
|
|
static int timespeccmp(timespec_t *t1, timespec_t *t2);
|
|
static int numlen(long long l, int minlen);
|
|
static int devlen(dev_t d, char *majorlen, char *minorlen);
|
|
|
|
|
|
/*
|
|
* Procedure: main
|
|
*
|
|
* Restrictions:
|
|
* fopen: P_MACREAD
|
|
* lvlout: P_MACREAD
|
|
*/
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
int amino;
|
|
char *bname = basename(argv[0]);
|
|
int c;
|
|
char *clptr;
|
|
lbuf_t *ep;
|
|
int i;
|
|
char *labelstate;
|
|
lbuf_t lb;
|
|
int opterr = 0;
|
|
struct timeval t;
|
|
int width;
|
|
cap_value_t caps[2] = {CAP_DAC_READ_SEARCH, CAP_MAC_READ};
|
|
|
|
/*
|
|
* Request both DAC and MAC read override capabilities.
|
|
* Don't sweat it if they're not available.
|
|
*/
|
|
(void)cap_acquire(2, caps);
|
|
|
|
(void)setlocale(LC_ALL, "");
|
|
(void)setcat("uxcore.abi");
|
|
getwidth(&wp);
|
|
difset1 = wp._eucw1 - wp._scrw1;
|
|
difset2 = wp._eucw2 - wp._scrw2;
|
|
difset3 = wp._eucw3 - wp._scrw3;
|
|
|
|
gettimeofday(&t, NULL);
|
|
lb.lmtime.tv_sec = t.tv_sec;
|
|
lb.lmtime.tv_nsec = t.tv_usec * 1000L;
|
|
year = lb.lmtime;
|
|
year.tv_sec -= 6L*30L*24L*60L*60L; /* 6 months ago */
|
|
now = lb.lmtime;
|
|
now.tv_sec += 60L;
|
|
if (!strcmp(bname, "lc")) {
|
|
(void)setlabel("UX:lc");
|
|
Cflg = 1;
|
|
mflg = 0;
|
|
} else
|
|
(void)setlabel("UX:ls");
|
|
|
|
if (isatty(1)) {
|
|
Cflg = 1;
|
|
mflg = 0;
|
|
}
|
|
|
|
while ((c=getopt(argc, argv, option_str)) != EOF) {
|
|
switch(c) {
|
|
case 'R':
|
|
Rflg++;
|
|
statreq++;
|
|
break;
|
|
case 'a':
|
|
aflg++;
|
|
break;
|
|
case 'A':
|
|
Aflg++;
|
|
break;
|
|
case 'd':
|
|
dflg++;
|
|
break;
|
|
case 'C':
|
|
Cflg = 1;
|
|
mflg = 0;
|
|
lflg = 0;
|
|
break;
|
|
case '1':
|
|
Cflg = 0;
|
|
break;
|
|
case 'x':
|
|
xflg = 1;
|
|
Cflg = 1;
|
|
mflg = 0;
|
|
lflg = 0;
|
|
break;
|
|
case 'm':
|
|
Cflg = 0;
|
|
lflg = 0;
|
|
mflg = 1;
|
|
break;
|
|
case 'n':
|
|
nflg++;
|
|
/* FALLTHROUGH */
|
|
case 'l':
|
|
Cflg = 0;
|
|
lflg++;
|
|
statreq++;
|
|
break;
|
|
case 'o':
|
|
oflg++;
|
|
lflg++;
|
|
statreq++;
|
|
break;
|
|
case 'g':
|
|
gflg++;
|
|
lflg++;
|
|
statreq++;
|
|
break;
|
|
case 'h':
|
|
hflg++;
|
|
break;
|
|
case 'r':
|
|
rflg = -1;
|
|
break;
|
|
case 't':
|
|
tflg++;
|
|
statreq++;
|
|
break;
|
|
case 'u':
|
|
uflg++;
|
|
break;
|
|
case 'c':
|
|
cflg++;
|
|
break;
|
|
case 'p':
|
|
pflg++;
|
|
statreq++;
|
|
break;
|
|
case 'F':
|
|
Fflg++;
|
|
statreq++;
|
|
break;
|
|
case 'b':
|
|
bflg = 1;
|
|
qflg = 0;
|
|
break;
|
|
case 'q':
|
|
qflg = 1;
|
|
bflg = 0;
|
|
break;
|
|
case 'i':
|
|
iflg++;
|
|
break;
|
|
case 'j':
|
|
jflg++;
|
|
lflg++;
|
|
statreq++;
|
|
break;
|
|
case 's':
|
|
sflg++;
|
|
statreq++;
|
|
break;
|
|
case 'f':
|
|
fflg++;
|
|
break;
|
|
case 'L':
|
|
Lflg++;
|
|
Hflg=0;
|
|
break;
|
|
case 'H':
|
|
Hflg++;
|
|
Lflg=0;
|
|
break;
|
|
case 'M':
|
|
Mflg++;
|
|
break;
|
|
case 'D':
|
|
Dflg++;
|
|
break;
|
|
case 'P':
|
|
Pflg++;
|
|
break;
|
|
case 'S':
|
|
Sflg++;
|
|
Lflg++;
|
|
break;
|
|
case 'z':
|
|
zflg++;
|
|
statreq++;
|
|
break;
|
|
case 'Z':
|
|
Zflg++;
|
|
statreq++;
|
|
break;
|
|
case '?':
|
|
opterr++;
|
|
break;
|
|
}
|
|
}
|
|
if (opterr) {
|
|
pfmt(stderr, MM_ACTION, usage, bname, option_str);
|
|
exit(2);
|
|
}
|
|
|
|
/*
|
|
* The z and Z options are mutually exclusive.
|
|
*/
|
|
if (zflg && Zflg) {
|
|
pfmt(stderr, MM_ERROR,
|
|
":806:-%c and -%c are mutually exclusive\n", 'z', 'Z');
|
|
pfmt(stderr, MM_ACTION, usage, bname, option_str);
|
|
exit(2);
|
|
}
|
|
|
|
acl_enabled = sysconf(_SC_ACL) > 0;
|
|
lvlformat = -1;
|
|
|
|
if (fflg) {
|
|
aflg++;
|
|
lflg = 0;
|
|
sflg = 0;
|
|
tflg = 0;
|
|
if (!(zflg || Zflg))
|
|
statreq = 0;
|
|
}
|
|
|
|
fixedwidth = 2;
|
|
if (pflg || Fflg)
|
|
fixedwidth++;
|
|
if (iflg)
|
|
fixedwidth += DFL_NUM_LEN + 1;
|
|
if (sflg)
|
|
fixedwidth += DFL_BLOCKS_LEN + 1;
|
|
|
|
if (lflg) {
|
|
if (!gflg && !oflg)
|
|
gflg = oflg = 1;
|
|
else if (gflg && oflg)
|
|
gflg = oflg = 0;
|
|
Cflg = mflg = 0;
|
|
_pw_stayopen = 1;
|
|
_getpwent_no_shadow = 1;
|
|
}
|
|
|
|
if (Cflg || mflg) {
|
|
if ((clptr = getenv("COLUMNS")) != NULL)
|
|
num_cols = atoi(clptr);
|
|
else if (ioctl(1, TIOCGWINSZ, &win) != -1)
|
|
num_cols = (win.ws_col == 0 ? 80 : win.ws_col);
|
|
if (num_cols < 20 || num_cols > 512)
|
|
/* assume it is an error */
|
|
num_cols = 80;
|
|
}
|
|
|
|
do_lnum_len = iflg && !mflg;
|
|
do_lblocks_len = sflg && !mflg;
|
|
do_lsize_len = do_lnl_len = lflg;
|
|
|
|
lnum_len = DFL_NUM_LEN;
|
|
lsize_len = DFL_SIZE_LEN;
|
|
lblocks_len = DFL_BLOCKS_LEN;
|
|
lnl_len = DFL_NL_LEN;
|
|
lmaj_len = DFL_MAJOR_LEN;
|
|
lmin_len = DFL_MINOR_LEN;
|
|
|
|
/* If env variable is on then show security labels */
|
|
|
|
labelstate = getenv("LABELFLAG");
|
|
if (labelstate && strcasecmp(labelstate, "on") == 0)
|
|
Mflg++;
|
|
|
|
lvlwidth = 0; /* initialize width of level */
|
|
|
|
if ((amino = (argc - optind)) == 0) {
|
|
/*
|
|
* case when no names are given in ls-command and current
|
|
* directory is to be used
|
|
*/
|
|
argv[optind] = dotp;
|
|
}
|
|
|
|
for (i = 0; i < (amino ? amino : 1); i++) {
|
|
if (SHOWLVL() || Cflg || mflg) {
|
|
width = strlen(argv[optind]);
|
|
width -= setcount(argv[optind]);
|
|
if (width > filewidth)
|
|
filewidth = width;
|
|
}
|
|
if ((ep = gstat((*argv[optind] ? argv[optind] : dotp), 1)) ==
|
|
NULL) {
|
|
if (nomocore)
|
|
exit(2);
|
|
err = 2;
|
|
optind++;
|
|
continue;
|
|
}
|
|
ep->lnamep = *argv[optind] ? argv[optind] : dotp;
|
|
optind++;
|
|
nargs++; /* count good arguments stored in flist */
|
|
|
|
/*
|
|
* if lvlout fails, lwidth is -1, less than lvlwidth
|
|
*
|
|
* if ltdb_lvl is > 0 then the LTDB is at a level which
|
|
* is unreadable by this process at its current level,
|
|
* change level to the level of the LTDB and issue lvlout
|
|
*/
|
|
if (Mflg && (Cflg || mflg) && ep->label) {
|
|
width += strlen(ep->label) + 3;
|
|
if (width > filewidth)
|
|
filewidth = width;
|
|
}
|
|
if (Pflg && (Cflg || mflg) && ep->cap) {
|
|
width += strlen(ep->cap) + 3;
|
|
if (width > filewidth)
|
|
filewidth = width;
|
|
}
|
|
if (Dflg && (Cflg || mflg)) {
|
|
if (ep->acl)
|
|
width += strlen(ep->acl) + 3;
|
|
if (ep->dacl)
|
|
width += strlen(ep->dacl) + 3;
|
|
if (ep->acl && ep->dacl)
|
|
width -= 2;
|
|
if (width > filewidth)
|
|
filewidth = width;
|
|
}
|
|
if (do_lnum_len && ep->lnum_len > lnum_len)
|
|
lnum_len = ep->lnum_len;
|
|
if (do_lsize_len) {
|
|
if (ep->lsize_len > lsize_len)
|
|
lsize_len = ep->lsize_len;
|
|
if (ep->lmaj_len > lmaj_len)
|
|
lmaj_len = ep->lmaj_len;
|
|
if (ep->lmin_len > lmin_len)
|
|
lmin_len = ep->lmin_len;
|
|
}
|
|
if (do_lblocks_len && ep->lblocks_len > lblocks_len)
|
|
lblocks_len = ep->lblocks_len;
|
|
if (do_lnl_len && ep->lnl_len > lnl_len)
|
|
lnl_len = ep->lnl_len;
|
|
}
|
|
extrawidth = (lnum_len - DFL_NUM_LEN) + (lsize_len - DFL_SIZE_LEN) +
|
|
(lblocks_len - DFL_BLOCKS_LEN) + (lnl_len - DFL_NL_LEN);
|
|
colwidth = fixedwidth + filewidth + extrawidth;
|
|
|
|
if (SHOWLVL()) {
|
|
/*
|
|
* An additional two spaces are taken into account to give at
|
|
* least two spaces between the level and the next file name.
|
|
* Originally, two spaces were kept between file names.
|
|
*/
|
|
normwidth = colwidth;
|
|
colwidth = normwidth + lvlwidth + 2;
|
|
}
|
|
qsort(flist, nargs, sizeof(lbuf_t *), argcompare);
|
|
for (i = 0; i < nargs; i++) {
|
|
if ((flist[i]->ltype == 'd' && dflg == 0) || fflg)
|
|
break;
|
|
}
|
|
pem(&flist[0], &flist[i], 0);
|
|
for (; i < nargs; i++) {
|
|
if (i == 0 && amino > 1)
|
|
firstIsDirFlag = 1;
|
|
pdirectory(flist[i]->lnamep, amino > 1, nargs);
|
|
if (nomocore)
|
|
exit(2);
|
|
/* -R: print subdirectories found */
|
|
while (dfirst) {
|
|
/* take off first dir on dfirst & print it */
|
|
dtemp = dfirst;
|
|
dfirst = dfirst->dc_next;
|
|
pdirectory(dtemp->dc_name, 1, nargs);
|
|
if (nomocore)
|
|
exit(2);
|
|
free(dtemp->dc_name);
|
|
free(dtemp);
|
|
}
|
|
}
|
|
exit(err);
|
|
/*NOTREACHED*/
|
|
}
|
|
|
|
|
|
/*
|
|
* Procedure: pdirectory
|
|
*
|
|
* Restrictions:
|
|
* gettxt: none
|
|
* pfmt: none
|
|
*
|
|
* Notes: print the directory name, labelling it if title is
|
|
* nonzero, using lp as the place to start reading in the dir.
|
|
*/
|
|
static void
|
|
pdirectory(char *name, int title, int lp)
|
|
{
|
|
lbuf_t *ap;
|
|
dchain_t *dp;
|
|
int j;
|
|
char *pname;
|
|
static char *sep;
|
|
static char *total;
|
|
|
|
filewidth = 0;
|
|
curdir = name;
|
|
if (title) {
|
|
if (firstIsDirFlag)
|
|
firstIsDirFlag = 0;
|
|
else
|
|
putchar('\n');
|
|
if (!sep)
|
|
sep = gettxt(":325", ":");
|
|
pprintf(name, "", sep);
|
|
new_line();
|
|
}
|
|
nfiles = lp;
|
|
rddir(name);
|
|
if (nomocore)
|
|
return;
|
|
if (fflg == 0)
|
|
qsort(&flist[lp], nfiles - lp, sizeof(lbuf_t *), compare);
|
|
if (Rflg) {
|
|
for (j = nfiles - 1; j >= lp; j--) {
|
|
ap = flist[j];
|
|
if (ap->ltype == 'd' &&
|
|
strcmp(ap->lnamep, ".") != 0 &&
|
|
strcmp(ap->lnamep, "..") != 0) {
|
|
dp = malloc(sizeof(*dp));
|
|
if (dp == NULL)
|
|
pfmt(stderr, MM_ERROR, nomem,
|
|
strerror(errno));
|
|
pname = makename(curdir, ap->lnamep);
|
|
dp->dc_name = strdup(pname);
|
|
if (dp->dc_name == NULL) {
|
|
pfmt(stderr, MM_ERROR, nomem,
|
|
strerror(errno));
|
|
free(dp);
|
|
} else {
|
|
dp->dc_next = dfirst;
|
|
dfirst = dp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (lflg || sflg) {
|
|
if (!total)
|
|
total = gettxt(":326", "total %llu");
|
|
curcol += printf(total, tblocks);
|
|
need_new_col++;
|
|
}
|
|
pem(&flist[lp], &flist[nfiles], 0);
|
|
resetstr();
|
|
}
|
|
|
|
/*
|
|
* pem: print 'em. Print a list of files (e.g. a directory) bounded
|
|
* by slp and lp.
|
|
*/
|
|
static void
|
|
pem(lbuf_t **slp, lbuf_t **lp, int tot_flag)
|
|
{
|
|
int col;
|
|
lbuf_t **ep;
|
|
int ncols;
|
|
int nrows;
|
|
int row;
|
|
|
|
if (Cflg || mflg)
|
|
ncols = colwidth > num_cols ? 1 : num_cols / colwidth;
|
|
if (ncols <= 1 || mflg || xflg || !Cflg) {
|
|
for (ep = slp; ep < lp; ep++)
|
|
pentry(*ep);
|
|
new_line();
|
|
return;
|
|
}
|
|
/* otherwise print -C columns */
|
|
if (tot_flag)
|
|
slp--;
|
|
nrows = (lp - slp - 1) / ncols + 1;
|
|
for (row = 0; row < nrows; row++) {
|
|
for (col = row == 0 && tot_flag; col < ncols; col++) {
|
|
ep = slp + (nrows * col) + row;
|
|
if (ep < lp)
|
|
pentry(*ep);
|
|
}
|
|
new_line();
|
|
}
|
|
}
|
|
|
|
/*
|
|
Return a number formated in human format. Stolen from top.
|
|
*/
|
|
static char *number(off64_t bytes)
|
|
{
|
|
#define NSTR 4
|
|
static char bufs[NSTR][16];
|
|
static int index = 0;
|
|
char *p;
|
|
char tag = 'b';
|
|
double amt;
|
|
off64_t kilo = 1024;
|
|
off64_t mega = (1024*kilo);
|
|
off64_t giga = (1024*mega);
|
|
off64_t tera = (1024*giga);
|
|
|
|
p = bufs[index];
|
|
index = (index + 1) % NSTR;
|
|
|
|
if (bytes < kilo) {
|
|
sprintf(p, "%lld%c", bytes, tag);
|
|
return (p);
|
|
}
|
|
|
|
amt = (double)bytes;
|
|
|
|
if (bytes >= tera) {
|
|
amt /= tera;
|
|
tag = 'T';
|
|
}
|
|
else if (bytes >= giga) {
|
|
amt /= giga;
|
|
tag = 'G';
|
|
}
|
|
else if (bytes >= mega) {
|
|
amt /= mega;
|
|
tag = 'M';
|
|
}
|
|
else {
|
|
amt /= kilo;
|
|
tag = 'K';
|
|
}
|
|
if (amt >= 10) {
|
|
sprintf(p, "%4.0f%c", amt, tag);
|
|
}
|
|
else {
|
|
sprintf(p, "%4.1f%c", amt, tag);
|
|
}
|
|
return (p);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Procedure: pentry
|
|
*
|
|
* Restrictions:
|
|
* cftime: none
|
|
* lvlout: P_MACREAD
|
|
* Notes:
|
|
* print one output entry;
|
|
* if uid/gid is not found in the appropriate
|
|
* file (passwd/group), then print uid/gid instead of
|
|
* user/group name;
|
|
*/
|
|
static void
|
|
pentry(lbuf_t *ap)
|
|
{
|
|
char buf[BUFSIZ];
|
|
char *dmark = ""; /* Used if -p or -F option active */
|
|
static char *id1;
|
|
static char *id2;
|
|
char *midstr;
|
|
lbuf_t *p;
|
|
int show_linkto; /* boolean: show link string */
|
|
|
|
p = ap;
|
|
column();
|
|
if (iflg) {
|
|
if (mflg)
|
|
curcol += printf("%llu ", p->lnum);
|
|
else
|
|
curcol += printf("%*llu ", lnum_len, p->lnum);
|
|
}
|
|
if (sflg) {
|
|
if (mflg)
|
|
curcol += printf("%lld ",
|
|
(p->ltype != 'b' && p->ltype != 'c') ?
|
|
p->lblocks : 0LL);
|
|
else
|
|
curcol += printf("%*lld ", lblocks_len,
|
|
(p->ltype != 'b' && p->ltype != 'c') ?
|
|
p->lblocks : 0LL);
|
|
}
|
|
if (lflg) {
|
|
putchar(p->ltype);
|
|
curcol++;
|
|
pmode(p->lflags);
|
|
curcol += printf("%c", p->aclindicator);
|
|
curcol += printf("%*d ", lnl_len, p->lnl);
|
|
if (oflg) {
|
|
if (!nflg && getname(p->luid, tbufu, LS_UID) == 0)
|
|
curcol += printf("%-8.8s ", tbufu);
|
|
else
|
|
curcol += printf("%-8u ", p->luid);
|
|
}
|
|
if (gflg) {
|
|
if (!nflg && getname(p->lgid, tbufg, LS_GID) == 0)
|
|
curcol += printf("%-9.9s", tbufg);
|
|
else
|
|
curcol += printf("%-9u", p->lgid);
|
|
}
|
|
if (jflg) {
|
|
if (!nflg && getname(p->lprojid, tbufp, LS_PRID) == 0)
|
|
curcol += printf("%-9.9s", tbufp);
|
|
else
|
|
curcol += printf("%-9d", p->lprojid);
|
|
}
|
|
if ((p->ltype == 'b' || p->ltype == 'c') &&
|
|
(!Sflg || (p->flinkto == NULL)))
|
|
curcol += printf(" %*d,%*d",
|
|
lmaj_len, major((dev_t)p->lsize),
|
|
lmin_len, minor((dev_t)p->lsize));
|
|
else{
|
|
if (hflg || getenv("HUMAN_BLOCKS")
|
|
&& !getenv("POSIXLY_CORRECT")) {
|
|
curcol += printf(" %7s", number(p->lsize));
|
|
} else {
|
|
curcol += printf(" %*lld", lsize_len, p->lsize);
|
|
}
|
|
}
|
|
if (timespeccmp(&p->lmtime, &year) < 0 ||
|
|
timespeccmp(&p->lmtime, &now) > 0) {
|
|
if (!id1)
|
|
id1 = gettxt(FORMAT1ID, FORMAT1);
|
|
cftime(time_buf, id1, &p->lmtime.tv_sec);
|
|
curcol += printf("%s", time_buf);
|
|
} else {
|
|
if (!id2)
|
|
id2 = gettxt(FORMAT2ID, FORMAT2);
|
|
cftime(time_buf, id2, &p->lmtime.tv_sec);
|
|
curcol += printf("%s", time_buf);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* prevent both "->" and trailing marks
|
|
* from appearing
|
|
*/
|
|
if (pflg && p->ltype == 'd')
|
|
dmark = "/";
|
|
|
|
show_linkto = (Sflg || lflg) && p->flinkto;
|
|
|
|
if (Fflg && !show_linkto) {
|
|
if (p->ltype == 'd')
|
|
dmark = "/";
|
|
else if (p->ltype == 'l')
|
|
dmark = "@";
|
|
else if (p->ltype == 'p')
|
|
dmark = "|";
|
|
else if (p->ltype == 'S')
|
|
dmark = "=";
|
|
else if (p->lflags & (S_IXUSR|S_IXGRP|S_IXOTH))
|
|
dmark = "*";
|
|
else
|
|
dmark = "";
|
|
}
|
|
|
|
midstr = "";
|
|
if (show_linkto) {
|
|
midstr = " -> ";
|
|
strcpy(buf, p->flinkto);
|
|
free(p->flinkto);
|
|
p->flinkto = NULL;
|
|
dmark = buf;
|
|
}
|
|
|
|
if (qflg || bflg)
|
|
pprintf(p->lnamep, midstr, dmark);
|
|
else
|
|
curcol += printf("%s%s%s", p->lnamep, midstr, dmark) -
|
|
setcount(p->lnamep);
|
|
|
|
if (Mflg) {
|
|
if (ap->label) {
|
|
curcol += printf(" [%s]", ap->label);
|
|
free (ap->label);
|
|
} else
|
|
curcol += printf(" []");
|
|
}
|
|
if (Pflg) {
|
|
if (ap->cap) {
|
|
curcol += printf(" [%s]", ap->cap);
|
|
free (ap->cap);
|
|
} else
|
|
curcol += printf(" []");
|
|
}
|
|
if (Dflg) {
|
|
curcol += printf(" [");
|
|
if (ap->acl) {
|
|
curcol += printf("%s", ap->acl);
|
|
free (ap->acl);
|
|
ap->acl = NULL;
|
|
}
|
|
if (ap->dacl) {
|
|
curcol += printf("/%s", ap->dacl);
|
|
free (ap->dacl);
|
|
ap->dacl = NULL;
|
|
}
|
|
curcol += printf("]");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* print various r,w,x permissions
|
|
*/
|
|
static void
|
|
pmode(mode_t aflag)
|
|
{
|
|
/* these arrays are declared static to allow initializations */
|
|
static int m0[] = { 1, S_IRUSR, 'r', '-' };
|
|
static int m1[] = { 1, S_IWUSR, 'w', '-' };
|
|
static int m2[] = { 3, S_ISUID|S_IXUSR, 's', S_IXUSR,
|
|
'x', S_ISUID, 'S', '-' };
|
|
static int m3[] = { 1, S_IRGRP, 'r', '-' };
|
|
static int m4[] = { 1, S_IWGRP, 'w', '-' };
|
|
static int m5[] = { 3, S_ISGID|S_IXGRP, 's', S_IXGRP,
|
|
'x', S_ISGID, 'L', '-'};
|
|
static int m6[] = { 1, S_IROTH, 'r', '-' };
|
|
static int m7[] = { 1, S_IWOTH, 'w', '-' };
|
|
static int m8[] = { 3, S_ISVTX|S_IXOTH, 't', S_IXOTH,
|
|
'x', S_ISVTX, 'T', '-'};
|
|
static int *m[] = { m0, m1, m2, m3, m4, m5, m6, m7, m8 };
|
|
int **mp;
|
|
|
|
flags = aflag;
|
|
for (mp = &m[0]; mp < &m[sizeof(m)/sizeof(m[0])]; mp++)
|
|
ls_select(*mp);
|
|
}
|
|
|
|
static void
|
|
ls_select(int *pairp)
|
|
{
|
|
int n;
|
|
|
|
n = *pairp++;
|
|
while (n-- > 0) {
|
|
if ((flags & *pairp) == *pairp) {
|
|
pairp++;
|
|
break;
|
|
} else
|
|
pairp += 2;
|
|
}
|
|
putchar(*pairp);
|
|
curcol++;
|
|
}
|
|
|
|
/*
|
|
* column: get to the beginning of the next column.
|
|
*/
|
|
static void
|
|
column(void)
|
|
{
|
|
if (curcol == 0)
|
|
return;
|
|
if (need_new_col) {
|
|
putchar('\n');
|
|
curcol = 0;
|
|
need_new_col = 0;
|
|
return;
|
|
}
|
|
if (mflg) {
|
|
putchar(',');
|
|
curcol++;
|
|
if (curcol + colwidth + 2 > num_cols) {
|
|
putchar('\n');
|
|
curcol = 0;
|
|
return;
|
|
}
|
|
putchar(' ');
|
|
curcol++;
|
|
return;
|
|
}
|
|
if (Cflg == 0) {
|
|
putchar('\n');
|
|
curcol = 0;
|
|
return;
|
|
}
|
|
if ((curcol / colwidth + 2) * colwidth > num_cols) {
|
|
putchar('\n');
|
|
curcol = 0;
|
|
return;
|
|
}
|
|
do {
|
|
putchar(' ');
|
|
curcol++;
|
|
} while (curcol % colwidth);
|
|
}
|
|
|
|
static void
|
|
new_line(void)
|
|
{
|
|
if (curcol) {
|
|
putchar('\n');
|
|
curcol = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Procedure: rddir
|
|
*
|
|
* Restrictions:
|
|
* opendir: none
|
|
* lvlout: P_MACREAD
|
|
* Notes:
|
|
* read each filename in directory dir and store its
|
|
* status in flist[nfiles]
|
|
* use makename() to form pathname dir/filename;
|
|
*
|
|
* If the LTDB is at a level which is unreadable by the current process
|
|
* (lttb_lvl > 0) ... attempt to set the P_SETPLEVEL privilege and
|
|
* change the process level to the level of the LTDB.
|
|
*/
|
|
static void
|
|
rddir(char *dir)
|
|
{
|
|
dirent64_t *dentry;
|
|
DIR *dirf;
|
|
lbuf_t *ep;
|
|
int width;
|
|
|
|
if ((dirf = opendir(dir)) == NULL) {
|
|
fflush(stdout);
|
|
pfmt(stderr, MM_ERROR, ":327:Cannot access directory %s: %s\n",
|
|
dir, strerror(errno));
|
|
err = 2;
|
|
return;
|
|
}
|
|
tblocks = 0;
|
|
lnum_len = DFL_NUM_LEN;
|
|
lsize_len = DFL_SIZE_LEN;
|
|
lblocks_len = DFL_BLOCKS_LEN;
|
|
lnl_len = DFL_NL_LEN;
|
|
lmaj_len = DFL_MAJOR_LEN;
|
|
lmin_len = DFL_MINOR_LEN;
|
|
while (dentry = readdir64(dirf)) {
|
|
/*
|
|
* check for directory items '.', '..',
|
|
* and items without valid inode-number;
|
|
*/
|
|
if (aflg == 0 && dentry->d_name[0] == '.' &&
|
|
(Aflg == 0 ||
|
|
(dentry->d_name[1] == '\0' ||
|
|
(dentry->d_name[1] == '.' && dentry->d_name[2] == '\0'))))
|
|
continue;
|
|
if (SHOWLVL() || Cflg || mflg) {
|
|
width = strlen(dentry->d_name);
|
|
width -= setcount(dentry->d_name);
|
|
if (width > filewidth)
|
|
filewidth = width;
|
|
}
|
|
ep = gstat(makename(dir, dentry->d_name), 0);
|
|
if (ep == NULL) {
|
|
if (nomocore)
|
|
return;
|
|
continue;
|
|
}
|
|
ep->lnum = dentry->d_ino;
|
|
if (do_lnum_len)
|
|
ep->lnum_len = numlen(ep->lnum, DFL_NUM_LEN);
|
|
ep->lnamep = savestr(dentry->d_name);
|
|
if (ep->lnamep == NULL) {
|
|
if (nomocore)
|
|
return;
|
|
continue;
|
|
}
|
|
if (Mflg && (Cflg || mflg) && ep->label) {
|
|
width += strlen(ep->label) + 3;
|
|
if (width > filewidth)
|
|
filewidth = width;
|
|
}
|
|
if (do_lnum_len && ep->lnum_len > lnum_len)
|
|
lnum_len = ep->lnum_len;
|
|
if (do_lsize_len) {
|
|
if (ep->lsize_len > lsize_len)
|
|
lsize_len = ep->lsize_len;
|
|
if (ep->lmaj_len > lmaj_len)
|
|
lmaj_len = ep->lmaj_len;
|
|
if (ep->lmin_len > lmin_len)
|
|
lmin_len = ep->lmin_len;
|
|
}
|
|
if (do_lblocks_len && ep->lblocks_len > lblocks_len)
|
|
lblocks_len = ep->lblocks_len;
|
|
if (do_lnl_len && ep->lnl_len > lnl_len)
|
|
lnl_len = ep->lnl_len;
|
|
}
|
|
(void)closedir(dirf);
|
|
extrawidth = (lnum_len - DFL_NUM_LEN) + (lsize_len - DFL_SIZE_LEN) +
|
|
(lblocks_len - DFL_BLOCKS_LEN) + (lnl_len - DFL_NL_LEN);
|
|
colwidth = fixedwidth + filewidth + extrawidth;
|
|
if (SHOWLVL()) {
|
|
/*
|
|
* An additional two spaces are taken into account
|
|
* to give at least two spaces between the level
|
|
* and the next file name.
|
|
* Originally, two spaces were kept between file names.
|
|
*/
|
|
normwidth = colwidth;
|
|
colwidth = normwidth + lvlwidth + 2;
|
|
}
|
|
}
|
|
|
|
void
|
|
get_mac(char *file, lbuf_t *rep)
|
|
{
|
|
mac_t label;
|
|
|
|
if (!Mflg)
|
|
return;
|
|
|
|
label = mac_get_file (file);
|
|
if (label == (mac_t) NULL)
|
|
rep->label = (char *) NULL;
|
|
else
|
|
rep->label = mac_to_text (label, (size_t *) NULL);
|
|
mac_free (label);
|
|
}
|
|
|
|
void
|
|
get_cap(char *file, lbuf_t *rep)
|
|
{
|
|
cap_t capset;
|
|
|
|
if (!Pflg)
|
|
return;
|
|
|
|
capset = cap_get_file (file);
|
|
if (capset == (cap_t) NULL)
|
|
rep->cap = (char *) NULL;
|
|
else
|
|
rep->cap = cap_to_text (capset, (size_t *) NULL);
|
|
cap_free (capset);
|
|
}
|
|
|
|
void
|
|
get_acl(char *file, lbuf_t *rep)
|
|
{
|
|
acl_t acl;
|
|
acl_t dacl;
|
|
char *buffer;
|
|
|
|
rep->aclindicator = ' ';
|
|
rep->acl = NULL;
|
|
rep->dacl = NULL;
|
|
|
|
if (!acl_enabled)
|
|
return;
|
|
|
|
if (!lflg && !Dflg)
|
|
return;
|
|
|
|
acl = acl_get_file (file, ACL_TYPE_ACCESS);
|
|
dacl = acl_get_file (file, ACL_TYPE_DEFAULT);
|
|
|
|
if (acl_valid (acl) == 0) {
|
|
rep->aclindicator = '+';
|
|
if (Dflg) {
|
|
buffer = acl_to_short_text (acl, (ssize_t *) NULL);
|
|
if (buffer) {
|
|
rep->acl = strdup(buffer);
|
|
acl_free(buffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (acl_valid (dacl) == 0) {
|
|
rep->aclindicator = '+';
|
|
if (Dflg) {
|
|
buffer = acl_to_short_text (dacl, (ssize_t *) NULL);
|
|
if (buffer) {
|
|
rep->dacl = strdup(buffer);
|
|
acl_free(buffer);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Procedure: gstat
|
|
*
|
|
* Restrictions:
|
|
* readlink(2): none
|
|
* stat(2): none
|
|
* acl(2): none
|
|
* Notes:
|
|
* get status of file and recomputes tblocks;
|
|
* argfl = 1 if file is a name in ls-command and = 0
|
|
* for filename in a directory whose name is an
|
|
* argument in the command;
|
|
* stores a pointer in flist[nfiles] and
|
|
* returns that pointer;
|
|
* returns NULL if failed;
|
|
*/
|
|
static lbuf_t *
|
|
gstat(char *file, int argfl)
|
|
{
|
|
char buf[BUFSIZ];
|
|
int bufsize = BUFSIZ;
|
|
int cc;
|
|
char *devname;
|
|
lbuf_t *rep;
|
|
struct stat64 statb;
|
|
struct stat64 statb1;
|
|
int (*statf)(const char *, struct stat64 *) = lstat64;
|
|
|
|
if (nomocore)
|
|
return NULL;
|
|
if (nfiles >= maxfils) {
|
|
/*
|
|
* all flist/lbuf pair assigned files
|
|
* time to get some more space
|
|
*/
|
|
maxfils += QUANTN;
|
|
if ((flist = realloc(flist,
|
|
maxfils * sizeof(lbuf_t *))) == NULL ||
|
|
(nxtlbf = malloc(QUANTN * sizeof(lbuf_t))) == NULL) {
|
|
pfmt(stderr, MM_ERROR, nomem, strerror(errno));
|
|
nomocore = 1;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* nfiles is reset to nargs for each directory
|
|
* that is given as an argument
|
|
* maxn is checked to prevent the assignment of an lbuf
|
|
* to a flist entry that already has one assigned.
|
|
*/
|
|
if (nfiles >= maxn) {
|
|
rep = nxtlbf++;
|
|
flist[nfiles++] = rep;
|
|
maxn = nfiles;
|
|
} else
|
|
rep = flist[nfiles++];
|
|
rep->lflags = (mode_t)0;
|
|
rep->flinkto = NULL;
|
|
|
|
get_mac(file, rep);
|
|
get_cap(file, rep);
|
|
get_acl(file, rep);
|
|
|
|
if (argfl || statreq) {
|
|
if ((*statf)(file, &statb) < 0) {
|
|
pfmt(stderr, MM_ERROR, ":5:Cannot access %s: %s\n",
|
|
file, strerror(errno));
|
|
nfiles--;
|
|
return NULL;
|
|
}
|
|
/*
|
|
* Need to make sure the stat() is necessary
|
|
* because the symlink may point to an automounted
|
|
* filesystem.
|
|
*/
|
|
if (!Hflg && (statb.st_mode & S_IFMT) == S_IFLNK &&
|
|
(Lflg || !lflg) && stat64(file, &statb1) == 0 &&
|
|
(Lflg || (!lflg && (statb1.st_mode & S_IFMT) == S_IFDIR)))
|
|
statb = statb1;
|
|
rep->lnum = statb.st_ino;
|
|
if (do_lnum_len)
|
|
rep->lnum_len = numlen(rep->lnum, DFL_NUM_LEN);
|
|
rep->lsize = statb.st_size;
|
|
if (do_lsize_len) {
|
|
rep->lsize_len = numlen(rep->lsize, DFL_SIZE_LEN);
|
|
rep->lmaj_len = rep->lmin_len = 0;
|
|
}
|
|
rep->lblocks = statb.st_blocks;
|
|
if (do_lblocks_len)
|
|
rep->lblocks_len = numlen(rep->lblocks, DFL_BLOCKS_LEN);
|
|
switch (statb.st_mode & S_IFMT) {
|
|
case S_IFDIR:
|
|
rep->ltype = 'd';
|
|
if (Sflg) {
|
|
devname = filename_to_devname(file, buf, &bufsize);
|
|
if (devname)
|
|
rep->flinkto = strdup(devname);
|
|
}
|
|
break;
|
|
|
|
case S_IFCHR:
|
|
case S_IFBLK:
|
|
if ((statb.st_mode & S_IFMT) == S_IFCHR)
|
|
rep->ltype = 'c';
|
|
else
|
|
rep->ltype = 'b';
|
|
|
|
if (Sflg) {
|
|
devname = filename_to_devname(file, buf, &bufsize);
|
|
if (devname)
|
|
rep->flinkto = strdup(devname);
|
|
}
|
|
if (!Sflg || !rep->flinkto) {
|
|
rep->lsize = (off_t)statb.st_rdev;
|
|
if (do_lsize_len)
|
|
rep->lsize_len =
|
|
devlen((dev_t)rep->lsize,
|
|
&rep->lmaj_len,
|
|
&rep->lmin_len);
|
|
}
|
|
break;
|
|
case S_IFIFO:
|
|
rep->ltype = 'p';
|
|
break;
|
|
case S_IFSOCK:
|
|
rep->ltype = 'S';
|
|
break;
|
|
case S_IFNAM:
|
|
/* 'st_rdev' field is really the subtype */
|
|
switch (statb.st_rdev) {
|
|
case S_INSEM:
|
|
rep->ltype = 's';
|
|
break;
|
|
case S_INSHD:
|
|
rep->ltype = 'm';
|
|
break;
|
|
default:
|
|
rep->ltype = '-';
|
|
break;
|
|
}
|
|
break;
|
|
case S_IFLNK:
|
|
rep->ltype = 'l';
|
|
if (lflg) {
|
|
cc = readlink(file, buf, BUFSIZ);
|
|
if (cc < 0)
|
|
break;
|
|
/*
|
|
* follow the symbolic link
|
|
* to generate the appropriate
|
|
* Fflg marker for the object
|
|
* eg, /bin -> /sym/bin/
|
|
*/
|
|
if (!Hflg && (Fflg || pflg) &&
|
|
stat64(file, &statb1) >= 0) {
|
|
switch (statb1.st_mode & S_IFMT) {
|
|
case S_IFDIR:
|
|
buf[cc++] = '/';
|
|
break;
|
|
default:
|
|
if ((statb1.st_mode & ~S_IFMT) &
|
|
(S_IXUSR|S_IXGRP|S_IXOTH))
|
|
buf[cc++] = '*';
|
|
break;
|
|
}
|
|
}
|
|
buf[cc] = '\0';
|
|
rep->flinkto = strdup(buf);
|
|
break;
|
|
}
|
|
/*
|
|
* ls /sym behaves differently from ls /sym/
|
|
* when /sym is a symbolic link. This is fixed
|
|
* when explicit arguments are specified.
|
|
*/
|
|
if (!argfl || stat64(file, &statb1) < 0)
|
|
break;
|
|
if ((statb1.st_mode & S_IFMT) == S_IFDIR) {
|
|
statb = statb1;
|
|
rep->ltype = 'd';
|
|
rep->lsize = statb1.st_size;
|
|
if (do_lsize_len)
|
|
rep->lsize_len = numlen(rep->lsize,
|
|
DFL_SIZE_LEN);
|
|
}
|
|
break;
|
|
default:
|
|
rep->ltype = '-';
|
|
break;
|
|
}
|
|
rep->lflags = statb.st_mode & ~S_IFMT;
|
|
|
|
/* mask ISARG and other file-type bits */
|
|
|
|
rep->luid = statb.st_uid;
|
|
rep->lgid = statb.st_gid;
|
|
rep->lprojid = statb.st_projid;
|
|
rep->lnl = statb.st_nlink;
|
|
if (do_lnl_len)
|
|
rep->lnl_len = numlen(rep->lnl, DFL_NL_LEN);
|
|
if (uflg)
|
|
rep->lmtime = statb.st_atim;
|
|
else if (cflg)
|
|
rep->lmtime = statb.st_ctim;
|
|
else
|
|
rep->lmtime = statb.st_mtim;
|
|
if (rep->ltype != 'b' && rep->ltype != 'c')
|
|
tblocks += rep->lblocks;
|
|
}
|
|
return rep;
|
|
}
|
|
|
|
/* returns pathname of the form dir/file;
|
|
* dir is a null-terminated string;
|
|
*/
|
|
static char *
|
|
makename(char *dir, char *file)
|
|
{
|
|
/* path + '/' + filename + '\0' */
|
|
static char dfile[PATH_MAX + MAXNAMLEN + 2];
|
|
char *dp;
|
|
char *fp;
|
|
|
|
dp = dfile;
|
|
fp = dir;
|
|
while (*fp)
|
|
*dp++ = *fp++;
|
|
if (dp > dfile && dp[-1] != '/')
|
|
*dp++ = '/';
|
|
fp = file;
|
|
while (*fp)
|
|
*dp++ = *fp++;
|
|
*dp = '\0';
|
|
return dfile;
|
|
}
|
|
|
|
/*
|
|
* get name from passwd/group file for a given uid/gid/projid
|
|
* and store it in buf; lastuid is set to uid;
|
|
* returns -1 if uid is not in file
|
|
*/
|
|
static int
|
|
getname(id_t id, char *buf, int type)
|
|
{
|
|
struct group *gp;
|
|
struct passwd *pp;
|
|
|
|
switch (type) {
|
|
case LS_UID:
|
|
if (id == lastuid)
|
|
return (0);
|
|
|
|
pp = getpwuid((uid_t)id);
|
|
if (!pp) {
|
|
lastuid = -1;
|
|
return -1;
|
|
}
|
|
strcpy(buf, pp->pw_name);
|
|
lastuid = id;
|
|
break;
|
|
|
|
case LS_GID:
|
|
if (id == lastgid)
|
|
return (0);
|
|
|
|
gp = getgrgid((gid_t)id);
|
|
if (!gp) {
|
|
lastgid = -1;
|
|
return -1;
|
|
}
|
|
strcpy(buf, gp->gr_name);
|
|
lastgid = id;
|
|
break;
|
|
|
|
case LS_PRID:
|
|
if (id == lastprojid)
|
|
return (0);
|
|
if (projname((prid_t) id, buf, BFSIZE) == 0)
|
|
return -1;
|
|
if (buf[0] == '\0')
|
|
return -1;
|
|
lastprojid = (int)id;
|
|
break;
|
|
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/* return >0 if item pointed by pp2 should appear first */
|
|
static int
|
|
argcompare(const void *pp1, const void *pp2)
|
|
{
|
|
lbuf_t *p1;
|
|
lbuf_t *p2;
|
|
int t;
|
|
|
|
p1 = *(lbuf_t **)pp1;
|
|
p2 = *(lbuf_t **)pp2;
|
|
if (dflg == 0) {
|
|
/*
|
|
* compare two names in ls-command one of which is file
|
|
* and the other is a directory;
|
|
* this portion is not used for comparing files within
|
|
* a directory name of ls-command.
|
|
*/
|
|
if (p1->ltype == 'd' && p2->ltype != 'd')
|
|
return 1;
|
|
if (p1->ltype != 'd' && p2->ltype == 'd')
|
|
return -1;
|
|
}
|
|
if (tflg && (t = timespeccmp(&p2->lmtime, &p1->lmtime)))
|
|
return rflg * t;
|
|
return rflg * strcmp(p1->lnamep, p2->lnamep);
|
|
}
|
|
|
|
/* return >0 if item pointed by pp2 should appear first */
|
|
static int
|
|
compare(const void *pp1, const void *pp2)
|
|
{
|
|
lbuf_t *p1;
|
|
lbuf_t *p2;
|
|
int t;
|
|
|
|
p1 = *(lbuf_t **)pp1;
|
|
p2 = *(lbuf_t **)pp2;
|
|
if (tflg && (t = timespeccmp(&p2->lmtime, &p1->lmtime)))
|
|
return rflg * t;
|
|
return rflg * strcmp(p1->lnamep, p2->lnamep);
|
|
}
|
|
|
|
/*
|
|
* This is a little weird, but don't do the printable character processing
|
|
* on the second string passed in. This allows us to print " -> " without
|
|
* converting the spaces to \040.
|
|
*/
|
|
static void
|
|
pprintf(char *s1, char *s2, char *s3)
|
|
{
|
|
int c;
|
|
int cc;
|
|
int i;
|
|
char *s;
|
|
|
|
for (s = s1, i = 0; i < 3; i++) {
|
|
while (c = (unsigned char)*s++) {
|
|
if ((i != 1) &&
|
|
(!ISPRINT(c, wp) || (bflg && (c == ' ')))) {
|
|
if (qflg)
|
|
c = '?';
|
|
else if (bflg) {
|
|
curcol += 3;
|
|
putchar('\\');
|
|
cc = '0' + ((c >> 6) & 07);
|
|
putchar(cc);
|
|
cc = '0' + ((c >> 3) & 07);
|
|
putchar(cc);
|
|
c = '0' + (c & 07);
|
|
}
|
|
}
|
|
curcol++;
|
|
putchar(c);
|
|
}
|
|
if (i == 0)
|
|
s = s2;
|
|
else
|
|
s = s3;
|
|
}
|
|
curcol -= setcount(s1);
|
|
}
|
|
|
|
static int
|
|
setcount(char *s)
|
|
{
|
|
int cnt = 0;
|
|
|
|
while (*s) {
|
|
if (NOTASCII(*s)) {
|
|
cnt += ISSET2(*s) ?
|
|
difset2 + 1 :
|
|
ISSET3(*s) ?
|
|
difset3 + 1 :
|
|
difset1;
|
|
if (ISSET2(*s))
|
|
s += wp._eucw2 + 1;
|
|
else if (ISSET3(*s))
|
|
s += wp._eucw3 + 1;
|
|
else
|
|
s += wp._eucw1;
|
|
} else
|
|
s++;
|
|
}
|
|
return cnt;
|
|
}
|
|
|
|
static void
|
|
morestrspace(void)
|
|
{
|
|
char *cp;
|
|
|
|
strspacelen++;
|
|
strspace = realloc(strspace, sizeof(*strspace) * strspacelen);
|
|
cp = malloc(STRCHUNK);
|
|
if (cp == NULL) {
|
|
pfmt(stderr, MM_ERROR, nomem, strerror(errno));
|
|
nomocore = 1;
|
|
err = 2;
|
|
strspacelen--;
|
|
return;
|
|
}
|
|
strspace[strspacelen - 1] = cp;
|
|
}
|
|
|
|
static char *
|
|
savestr(char *s)
|
|
{
|
|
int len = strlen(s) + 1;
|
|
char *p;
|
|
|
|
if (STRCHUNK - strcuroffset < len) {
|
|
if (strspacelen == strcurblock + 1) {
|
|
morestrspace();
|
|
if (nomocore)
|
|
return NULL;
|
|
}
|
|
strcurblock++;
|
|
strcuroffset = 0;
|
|
}
|
|
p = strspace[strcurblock] + strcuroffset;
|
|
strcpy(p, s);
|
|
strcuroffset += len;
|
|
return p;
|
|
}
|
|
|
|
static void
|
|
resetstr(void)
|
|
{
|
|
if (strspace == NULL)
|
|
return;
|
|
|
|
strcurblock = strcuroffset = 0;
|
|
}
|
|
|
|
static int
|
|
timespeccmp(timespec_t *t1, timespec_t *t2)
|
|
{
|
|
time_t s;
|
|
long n;
|
|
|
|
if (s = t1->tv_sec - t2->tv_sec)
|
|
return s > 0 ? 1 : -1;
|
|
if (n = t1->tv_nsec - t2->tv_nsec)
|
|
return n > 0 ? 1 : -1;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
numlen(long long l, int minlen)
|
|
{
|
|
int i;
|
|
static long long *v = NULL;
|
|
long long val;
|
|
|
|
if (!v) {
|
|
v = malloc(20 * sizeof(*v));
|
|
if (v == NULL)
|
|
pfmt(stderr, MM_ERROR, nomem, strerror(errno));
|
|
for (i = 0, val = 1; i < 19; i++) {
|
|
v[i] = val - 1LL;
|
|
val *= 10LL;
|
|
}
|
|
v[19] = LONGLONG_MAX;
|
|
}
|
|
if (l <= v[minlen])
|
|
return minlen;
|
|
for (i = minlen + 1; i < 20; i++) {
|
|
if (l <= v[i])
|
|
return i;
|
|
}
|
|
/* shouldn't get here */
|
|
return minlen;
|
|
}
|
|
|
|
static int
|
|
devlen(dev_t d, char *majorlen, char *minorlen)
|
|
{
|
|
*majorlen = numlen(major(d), DFL_MAJOR_LEN);
|
|
*minorlen = numlen(minor(d), DFL_MINOR_LEN);
|
|
return *majorlen + *minorlen + 1;
|
|
}
|