home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
oxcc1433.zip
/
SRC
/
CFLS.C
< prev
next >
Wrap
C/C++ Source or Header
|
1995-10-20
|
74KB
|
2,977 lines
/* `dir', `vdir' and `ls' directory listing programs for GNU.
Copyright (C) 1985, 1988, 1989, 1990 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* If the macro MULTI_COL is defined,
the multi-column format is the default regardless
of the type of output device.
This is for the `dir' program.
If the macro LONG_FORMAT is defined,
the long format is the default regardless of the
type of output device.
This is for the `vdir' program.
If neither is defined,
the output format depends on whether the output
device is a terminal.
This is for the `ls' program. */
/* Written by Richard Stallman and David MacKenzie. */
/* Modified for PCDOS and 32 Char filenames by Norman D. Culver */
/* Additonal mods for cff */
#include "cfls.h"
/* Return an int indicating the result of comparing two longs. */
#ifdef INT_16_BITS
#define longdiff(a, b) ((a) < (b) ? -1 : (a) > (b) ? 1 : 0)
#else
#define longdiff(a, b) ((a) - (b))
#endif
int glob_match ();
char *copystring ();
char *getgroup ();
char *getuser ();
char *make_link_path ();
char *xmalloc ();
char *xrealloc ();
int argmatch ();
int compare_atime ();
int rev_cmp_atime ();
int compare_ctime ();
int rev_cmp_ctime ();
int compare_mtime ();
int rev_cmp_mtime ();
int compare_size ();
int rev_cmp_size ();
int compare_name ();
int rev_cmp_name ();
int compare_extension ();
int rev_cmp_extension ();
int decode_switches ();
int file_interesting ();
long gobble_file ();
int is_not_dot_or_dotdot ();
int length_of_file_name_and_frills ();
void add_ignore_pattern ();
void add_list_pattern ();
void attach ();
void clear_files ();
void error ();
void extract_dirs_from_files ();
void get_link_name ();
void indent ();
void invalid_arg ();
void print_current_files ();
void print_dir ();
void print_file_name_and_frills ();
void print_horizontal ();
void print_long_format ();
void print_many_per_line ();
void print_name_with_quoting ();
void print_type_indicator ();
void print_with_commas ();
void queue_directory ();
void sort_files ();
void usage ();
enum filetype
{
symbolic_link,
directory,
arg_directory, /* Directory given as command line arg. */
normal /* All others. */
};
typedef struct _lstat {
unsigned long st_mode;
unsigned long st_atime;
unsigned long st_mtime;
unsigned long st_ctime;
unsigned long st_ino;
unsigned long st_size;
unsigned long st_alloc;
} LSTAT;
struct file
{
/* The file name. */
char *name;
int namlen;
LSTAT stats;
char *linkname;
unsigned long linkmode;
enum filetype filetype;
};
/* The table of files in the current directory:
`files' points to a vector of `struct file', one per file.
`nfiles' is the number of elements space has been allocated for.
`files_index' is the number actually in use. */
/* Address of block containing the files that are described. */
struct file *files;
/* Length of block that `files' points to, measured in files. */
int nfiles;
/* Index of first unused in `files'. */
int files_index;
/* Record of one pending directory waiting to be listed. */
struct pending
{
char *name;
/* If the directory is actually the file pointed to by a symbolic link we
were told to list, `realname' will contain the name of the symbolic
link, otherwise zero. */
char *realname;
struct ignore_pattern *list_patterns;
struct pending *next;
};
struct pending *pending_dirs;
/* Current time (seconds since 1970). When we are printing a file's time,
include the year if it is more than 6 months before this time. */
long current_time;
/* The number of digits to use for block sizes.
4, or more if needed for bigger numbers. */
int block_size_size;
/* The name the program was run with, stripped of any leading path. */
char *program_name;
/* Option flags */
/* long_format for lots of info, one per line.
one_per_line for just names, one per line.
many_per_line for just names, many per line, sorted vertically.
horizontal for just names, many per line, sorted horizontally.
with_commas for just names, many per line, separated by commas.
-l, -1, -C, -x and -m control this parameter. */
enum format
{
long_format, /* -l */
one_per_line, /* -1 */
many_per_line, /* -C */
horizontal, /* -x */
with_commas /* -m */
};
enum format format;
/* Type of time to print or sort by. Controlled by -c and -u. */
enum time_type
{
time_mtime, /* default */
time_ctime, /* -c */
time_atime /* -u */
};
enum time_type time_type;
/* The file characteristic to sort by. Controlled by -t, -S, -U, -X. */
enum sort_type
{
sort_none, /* -U */
sort_name, /* default */
sort_extension, /* -X */
sort_time, /* -t */
sort_size /* -S */
};
enum sort_type sort_type;
/* Direction of sort.
0 means highest first if numeric,
lowest first if alphabetic;
these are the defaults.
1 means the opposite order in each case. -r */
int sort_reverse;
/* Nonzero means print the user and group id's as numbers rather
than as names. -n */
int numeric_users;
/* Nonzero means mention the size in 512 byte blocks of each file. -s */
int print_block_size;
/* Nonzero means show file sizes in kilobytes instead of blocks
(the size of which is system-dependant). -k */
int kilobyte_blocks;
/* none means don't mention the type of files.
all means mention the types of all files.
not_programs means do so except for executables.
Controlled by -F and -p. */
enum indicator_style
{
none, /* default */
all, /* -F */
not_programs /* -p */
};
enum indicator_style indicator_style;
/* Nonzero means mention the inode number of each file. -i */
int print_inode;
/* Nonzero means when a symbolic link is found, display info on
the file linked to. -L */
int trace_links;
/* Nonzero means when a directory is found, display info on its
contents. -R */
int trace_dirs;
/* Nonzero means when an argument is a directory name, display info
on it itself. -d */
int immediate_dirs;
/* Nonzero means don't omit files whose names start with `.'. -A */
int all_files;
/* Nonzero means don't omit files `.' and `..'
This flag implies `all_files'. -a */
int really_all_files;
/* A linked list of shell-style globbing patterns. If a non-argument
file name matches any of these patterns, it is omitted, or listed.
List_patterns only occur if the shell fails to glob a pattern on
the command line, PCDOS is a good example. Ignore_patterns are
controlled by -I. Multiple -I options accumulate.
The -B option adds `*~' and `.*~' to this list. */
struct ignore_pattern
{
char *pattern;
struct ignore_pattern *next;
};
struct ignore_pattern *ignore_patterns;
/* Nonzero means quote nongraphic chars in file names. -b */
int quote_funny_chars;
/* Nonzero means output nongraphic chars in file names as `?'. -q */
int qmark_funny_chars;
/* Nonzero means output each file name using C syntax for a string.
Always accompanied by `quote_funny_chars'.
This mode, together with -x or -C or -m,
and without such frills as -F or -s,
is guaranteed to make it possible for a program receiving
the output to tell exactly what file names are present. -Q */
int quote_as_string;
/* The number of chars per hardware tab stop. -T */
int tabsize;
/* Nonzero means we are listing the working directory because no
non-option arguments were given. */
int dir_defaulted;
/* Nonzero means print each directory name before listing it. */
int print_dir_name;
/* The line length to use for breaking lines in many-per-line format.
Can be set with -w. */
int line_length;
/* If nonzero, the file listing format requires that stat be called on
each file. */
int format_needs_stat;
struct option long_options[] =
{
{"all", 0, NULL, 'a'},
{"escape", 0, NULL, 'b'},
{"directory", 0, NULL, 'd'},
{"inode", 0, NULL, 'i'},
{"kilobytes", 0, NULL, 'k'},
{"numeric-uid-gid", 0, NULL, 'n'},
{"hide-control-chars", 0, NULL, 'q'},
{"reverse", 0, NULL, 'r'},
{"size", 0, NULL, 's'},
{"width", 1, NULL, 'w'},
{"almost-all", 0, NULL, 'A'},
{"ignore-backups", 0, NULL, 'B'},
{"classify", 0, NULL, 'F'},
{"file-type", 0, NULL, 'F'},
{"ignore", 1, NULL, 'I'},
{"dereference", 0, NULL, 'L'},
{"literal", 0, NULL, 'N'},
{"quote-name", 0, NULL, 'Q'},
{"recursive", 0, NULL, 'R'},
{"format", 1, NULL, 12},
{"sort", 1, NULL, 10},
{"tabsize", 1, NULL, 'T'},
{"time", 1, NULL, 11},
{NULL, 0, NULL, 0}
};
char *format_args[] =
{
"verbose", "long", "commas", "horizontal", "across",
"vertical", "single-column", NULL
};
enum format formats[] =
{
long_format, long_format, with_commas, horizontal, horizontal,
many_per_line, one_per_line
};
char *sort_args[] =
{
"none", "time", "size", "extension", 0
};
enum sort_type sort_types[] =
{
sort_none, sort_time, sort_size, sort_extension
};
char *time_args[] =
{
"atime", "access", "use", "ctime", "status", 0
};
enum time_type time_types[] =
{
time_atime, time_atime, time_atime, time_ctime, time_ctime
};
static int
is_printable(const char *cp, int len)
{
register int i;
for(i = 0; i < len; ++i) {
if(!isgraph(cp[i])) {
if(cp[i] == '\0')
break;
return 0;
}
}
return (i > 0) ? 1 : 0;
}
/* Set all the option flags according to the switches specified.
Return the index of the first non-option argument. */
int
decode_switches (argc, argv)
int argc;
char **argv;
{
register char *p;
int c;
int longind;
qmark_funny_chars = 0;
quote_funny_chars = 0;
/* initialize all switches to default settings */
#ifdef MULTI_COL
#define PROGNAME "dir"
/* This is for the `dir' program. */
format = many_per_line;
quote_funny_chars = 1;
#else
#ifdef LONG_FORMAT
#define PROGNAME "vdir"
/* This is for the `vdir' program. */
format = long_format;
quote_funny_chars = 1;
#else
#define PROGNAME "cfls"
/* This is for the `ls' program. */
if (isatty (1))
{
format = many_per_line;
qmark_funny_chars = 1;
}
else
{
format = one_per_line;
qmark_funny_chars = 0;
}
#endif
#endif
time_type = time_mtime;
sort_type = sort_name;
sort_reverse = 0;
numeric_users = 0;
print_block_size = 0;
kilobyte_blocks = 0;
indicator_style = none;
print_inode = 0;
trace_links = 0;
trace_dirs = 0;
immediate_dirs = 0;
all_files = 0;
really_all_files = 0;
ignore_patterns = 0;
quote_as_string = 0;
p = getenv ("COLUMNS");
line_length = p ? atoi (p) : 80;
#ifdef TIOCGWINSZ
{
struct winsize ws;
if (ioctl (1, TIOCGWINSZ, &ws) != -1 && ws.ws_col != 0)
line_length = ws.ws_col;
}
#endif
p = getenv ("TABSIZE");
tabsize = p ? atoi (p) : 8;
while ((c = getopt_long (argc, argv, "abcdgiklmnpqrstuw:xABCFI:LNQRST:UX1",
long_options, &longind)) != EOF)
{
switch (c)
{
case 'a':
all_files = 1;
really_all_files = 1;
break;
case 'b':
quote_funny_chars = 1;
qmark_funny_chars = 0;
break;
case 'c':
time_type = time_ctime;
break;
case 'd':
immediate_dirs = 1;
break;
case 'g':
/* No effect. For BSD compatibility. */
break;
case 'i':
print_inode = 1;
break;
case 'k':
kilobyte_blocks = 1;
break;
case 'l':
format = long_format;
break;
case 'm':
format = with_commas;
break;
case 'n':
numeric_users = 1;
break;
case 'p':
indicator_style = not_programs;
break;
case 'q':
qmark_funny_chars = 1;
quote_funny_chars = 0;
break;
case 'r':
sort_reverse = 1;
break;
case 's':
print_block_size = 1;
break;
case 't':
sort_type = sort_time;
break;
case 'u':
time_type = time_atime;
break;
case 'w':
line_length = atoi (optarg);
if (line_length < 1)
error (1, 0, "invalid line width: %s", optarg);
break;
case 'x':
format = horizontal;
break;
case 'A':
all_files = 1;
break;
case 'B':
add_ignore_pattern ("*~");
add_ignore_pattern (".*~");
break;
case 'C':
format = many_per_line;
break;
case 'F':
indicator_style = all;
break;
case 'I':
add_ignore_pattern (optarg);
break;
case 'L':
trace_links = 1;
break;
case 'N':
quote_funny_chars = 0;
qmark_funny_chars = 0;
break;
case 'Q':
quote_as_string = 1;
quote_funny_chars = 1;
qmark_funny_chars = 0;
break;
case 'R':
trace_dirs = 1;
break;
case 'S':
sort_type = sort_size;
break;
case 'T':
tabsize = atoi (optarg);
if (tabsize < 1)
error (1, 0, "invalid tab size: %s", optarg);
break;
case 'U':
sort_type = sort_none;
break;
case 'X':
sort_type = sort_extension;
break;
case '1':
format = one_per_line;
break;
case 10: /* +sort */
longind = argmatch (optarg, sort_args);
if (longind < 0)
{
invalid_arg ("sort type", optarg, longind);
usage ();
}
sort_type = sort_types[longind];
break;
case 11: /* +time */
longind = argmatch (optarg, time_args);
if (longind < 0)
{
invalid_arg ("time type", optarg, longind);
usage ();
}
time_type = time_types[longind];
break;
case 12: /* +format */
longind = argmatch (optarg, format_args);
if (longind < 0)
{
invalid_arg ("format type", optarg, longind);
usage ();
}
format = formats[longind];
break;
default:
usage ();
}
}
return optind;
}
/* Request that the directory named `name' have its contents listed later.
If `realname' is nonzero, it will be used instead of `name' when the
directory name is printed. This allows symbolic links to directories
to be treated as regular directories but still be listed under their
real names. */
void
queue_directory (name, realname, thispend)
char *name;
char *realname;
struct pending *thispend;
{
struct pending *new;
new = (struct pending *) xmalloc (sizeof (struct pending));
new->next = pending_dirs;
pending_dirs = new;
new->name = copystring (name);
if (realname)
new->realname = copystring (realname);
else
new->realname = NULL;
if(thispend)
new->list_patterns = thispend->list_patterns;
else
new->list_patterns = NULL;
}
/* Read directory `name', and list the files in it.
If `realname' is nonzero, print its name instead of `name';
this is used for symbolic links to directories. */
void
print_dir (thispend)
struct pending *thispend;
{
register void *reading;
CFDIRENT *next;
register long total_blocks = 0;
char *name;
char *realname;
errno = 0;
name = thispend->name;
realname = thispend->realname;
reading = opendir (name);
if (!reading)
{
#ifdef PCDOS
errno = 2;/* it seems to have a small vocabulary, so ENOENT */
#endif
error (0, errno, "%s", name);
return;
}
/* Read the directory entries, and insert the subfiles into the `files'
table. */
clear_files ();
while (next = (void*)readdir (reading))
{
if (file_interesting (next, thispend))
total_blocks += gobble_file (next->d_name, 0, name, next);
}
closedir (reading);
/* Sort the directory contents. */
sort_files ();
/* If any member files are subdirectories, perhaps they should have their
contents listed rather than being mentioned here as files. */
if (trace_dirs)
extract_dirs_from_files (name, 1, thispend);
if (print_dir_name)
{
if (realname)
printf ("%s:\n", realname);
else
printf ("%s:\n", name);
}
#ifndef PCDOS
if (format == long_format || print_block_size)
printf ("total %u\n", total_blocks);
#endif
if (files_index)
print_current_files ();
#ifdef PCDOS
if(format == long_format || format == many_per_line || print_block_size)
{
printf ("\t%d file(s)\t", files_index);
if(print_block_size)
printf ("%lu blocks allocated\n", total_blocks);
else
printf ("%lu blocks used\n", total_blocks);
}
#endif
if (pending_dirs)
putchar ('\n');
}
/* Add `pattern' to the list of patterns for which files that match are
listed. */
void
add_list_pattern (pattern)
char *pattern;
{
register struct ignore_pattern *list;
list = (struct ignore_pattern *) xmalloc (sizeof (struct ignore_pattern));
list->pattern = pattern;
/* Add it to the head of the linked list. */
list->next = pending_dirs->list_patterns;
pending_dirs->list_patterns = list;
}
/* Add `pattern' to the list of patterns for which files that match are
not listed. */
void
add_ignore_pattern (pattern)
char *pattern;
{
register struct ignore_pattern *ignore;
ignore = (struct ignore_pattern *) xmalloc (sizeof (struct ignore_pattern));
ignore->pattern = pattern;
/* Add it to the head of the linked list. */
ignore->next = ignore_patterns;
ignore_patterns = ignore;
}
/* Return nonzero if the file in `next' should be listed. */
int
file_interesting (dirp, thispend)
register CFDIRENT *dirp;
register struct pending *thispend;
{
register struct ignore_pattern *ignore;
/* exclude files which match the ignore_pattern list */
for (ignore = ignore_patterns; ignore; ignore = ignore->next)
if (glob_match (ignore->pattern, dirp->d_name, 1))
return 0;
/* include files which match the list_pattern list */
for (ignore = thispend->list_patterns; ignore; ignore = ignore->next)
{
if (glob_match (ignore->pattern, dirp->d_name, 1))
goto file_maybe_ok;
}
if(thispend->list_patterns) return 0;
file_maybe_ok:
if (really_all_files
|| dirp->d_name[0] != '.'
|| (all_files
&& dirp->d_name[1] != '\0'
&& (dirp->d_name[1] != '.' || dirp->d_name[2] != '\0')))
return 1;
return 0;
}
/* Enter and remove entries in the table `files'. */
/* Empty the table of files. */
void
clear_files ()
{
register int i;
for (i = 0; i < files_index; i++)
{
free (files[i].name);
if (files[i].linkname)
free (files[i].linkname);
}
files_index = 0;
block_size_size = 3;
}
/* Add a file to the current table of files.
Verify that the file exists, and print an error message if it does not.
Return the number of blocks that the file occupies. */
long
gobble_file (name, explicit_arg, dirname, dirp)
char *name;
int explicit_arg;
char *dirname;
CFDIRENT *dirp;
{
long blocks;
register int val;
register char *path;
struct file *f;
int printable;
if (files_index == nfiles)
{
nfiles *= 2;
files = (struct file *) xrealloc (files, sizeof (struct file) * nfiles);
}
f = &files[files_index];
f->linkname = NULL;
f->linkmode = 0;
if(!dirp)
printable = 1;
else printable = is_printable(name, dirp->d_namlen);
if (explicit_arg && printable && if_pattern(name))
{/* The shell passed a pattern which needs globbing locally */
char ch, *pat;
if(path = rindex(name, '/'))
{
pat = path+1;
if(path == name || path[-1] == ':')
++path;
ch = *path;
*path = '\0';
if( ((pending_dirs) ? (strcmp(pending_dirs->name, name)) : 1))
queue_directory (name, NULL, NULL);
*path = ch;
add_list_pattern(pat);
return 0;
}
if(name[1] == ':')
{/* e.g. b:*.h */
ch = name[2];
name[2] = '\0';
if( ((pending_dirs) ? (strcmp(pending_dirs->name, name)) : 1))
queue_directory (name, NULL, NULL);
name[2] = ch;
add_list_pattern(&name[2]);
return 0;
}
else
{
if(!pending_dirs)
queue_directory ("./", NULL, NULL);
add_list_pattern(name);
return 0;
}
return 0;
}
blocks = 0;
if (explicit_arg || format_needs_stat)
{
LSTAT *sp = &f->stats;
if(dirp)
{
sp->st_mode = dirp->d_mode;
sp->st_size = dirp->d_bytesused;
sp->st_alloc = dirp->d_bytesalloc;
sp->st_mtime = dirp->d_mtime;
sp->st_ctime = dirp->d_ctime;
sp->st_atime = sp->st_mtime;
sp->st_ino = dirp->d_ino;
if(sp->st_mode & (M_HASHDIR|M_TREEDIR))
sp->st_mode |= S_IFDIR;
else sp->st_mode |= S_IFREG;
} else {
CFSTAT s;
cfstat(name, &s);
sp->st_mode = s.st_mode;
sp->st_size = s.st_size;
sp->st_alloc = s.st_size;
sp->st_mtime = s.st_mtime;
sp->st_ctime = s.st_ctime;
sp->st_atime = s.st_atime;
sp->st_ino = s.st_ino;
if(sp->st_mode & (M_HASHDIR|M_TREEDIR))
sp->st_mode |= S_IFDIR;
else sp->st_mode |= S_IFREG;
}
settype:
switch (sp->st_mode & S_IFMT)
{
case S_IFDIR:
if (explicit_arg && !immediate_dirs)
f->filetype = arg_directory;
else
f->filetype = directory;
break;
default:
f->filetype = normal;
break;
}
blocks = (long)convert_blocks(ST_NBLOCKS(sp), kilobyte_blocks);
if(blocks >= 1000 && block_size_size < 4)
block_size_size = 4;
if(blocks >= 10000 && block_size_size < 5)
block_size_size = 5;
if(blocks >= 100000 && block_size_size < 6)
block_size_size = 6;
if(blocks >= 1000000 && block_size_size < 7)
block_size_size = 7;
}
if(dirp)
{
if(is_printable(dirp->d_name, dirp->d_namlen))
{
f->name = xmalloc(dirp->d_namlen+1);
memcpy(f->name, dirp->d_name, dirp->d_namlen+1);
f->namlen = dirp->d_namlen;
} else {/* print a tidbit of the key, in hex */
f->name = xmalloc(16);
f->namlen = cfsprintf(f->name, "0x%x", *((long *)dirp->d_name));
}
} else {
f->namlen = strlen(name);
f->name = xmalloc(f->namlen+1);
strcpy(f->name, name);
}
files_index++;
return blocks;
}
/* Remove any entries from `files' that are for directories,
and queue them to be listed as directories instead.
`dirname' is the prefix to prepend to each dirname
to make it correct relative to ls's working dir.
`recursive' is nonzero if we should not treat `.' and `..' as dirs.
This is desirable when processing directories recursively. */
void
extract_dirs_from_files (dirname, recursive, thispend)
char *dirname;
int recursive;
struct pending *thispend;
{
register int i, j;
register char *path;
int dirlen;
dirlen = strlen (dirname) + 2;
/* Queue the directories last one first, because queueing reverses the
order. */
for (i = files_index - 1; i >= 0; i--)
if ((files[i].filetype == directory || files[i].filetype == arg_directory)
&& (!recursive || is_not_dot_or_dotdot (files[i].name)))
{
if (files[i].name[0] == '/' || dirname[0] == 0)
{
queue_directory (files[i].name, files[i].linkname, thispend);
}
else
{
path = (char *) xmalloc (strlen (files[i].name) + dirlen);
attach (path, dirname, files[i].name);
queue_directory (path, files[i].linkname, thispend);
free (path);
}
if ( files[i].filetype == arg_directory
|| (files[i].filetype == directory && thispend->list_patterns))
free (files[i].name);
}
/* Now delete the directories from the table, compacting all the remaining
entries. */
for (i = 0, j = 0; i < files_index; i++)
if ( !(files[i].filetype == arg_directory
|| (files[i].filetype == directory && thispend->list_patterns)))
files[j++] = files[i];
files_index = j;
}
/* Return non-zero if `name' doesn't end in `.' or `..'
This is so we don't try to recurse on `././././. ...' */
int
is_not_dot_or_dotdot (name)
char *name;
{
char *t;
t = rindex (name, '/');
if (t)
name = t + 1;
if (name[0] == '.'
&& (name[1] == '\0'
|| (name[1] == '.' && name[2] == '\0')))
return 0;
return 1;
}
/* Sort the files now in the table. */
void
sort_files ()
{
int (*func) ();
switch (sort_type)
{
case sort_none:
return;
case sort_time:
switch (time_type)
{
case time_ctime:
func = sort_reverse ? rev_cmp_ctime : compare_ctime;
break;
case time_mtime:
func = sort_reverse ? rev_cmp_mtime : compare_mtime;
break;
case time_atime:
func = sort_reverse ? rev_cmp_atime : compare_atime;
break;
}
break;
case sort_name:
func = sort_reverse ? rev_cmp_name : compare_name;
break;
case sort_extension:
func = sort_reverse ? rev_cmp_extension : compare_extension;
break;
case sort_size:
func = sort_reverse ? rev_cmp_size : compare_size;
break;
}
qsort (files, files_index, sizeof (struct file), func);
}
/* Comparison routines for sorting the files. */
int
compare_ctime (file1, file2)
struct file *file1, *file2;
{
return longdiff (file2->stats.st_ctime, file1->stats.st_ctime);
}
int
rev_cmp_ctime (file2, file1)
struct file *file1, *file2;
{
return longdiff (file2->stats.st_ctime, file1->stats.st_ctime);
}
int
compare_mtime (file1, file2)
struct file *file1, *file2;
{
return longdiff (file2->stats.st_mtime, file1->stats.st_mtime);
}
int
rev_cmp_mtime (file2, file1)
struct file *file1, *file2;
{
return longdiff (file2->stats.st_mtime, file1->stats.st_mtime);
}
int
compare_atime (file1, file2)
struct file *file1, *file2;
{
return longdiff (file2->stats.st_atime, file1->stats.st_atime);
}
int
rev_cmp_atime (file2, file1)
struct file *file1, *file2;
{
return longdiff (file2->stats.st_atime, file1->stats.st_atime);
}
int
compare_size (file1, file2)
struct file *file1, *file2;
{
return longdiff (file2->stats.st_size, file1->stats.st_size);
}
int
rev_cmp_size (file2, file1)
struct file *file1, *file2;
{
return longdiff (file2->stats.st_size, file1->stats.st_size);
}
int
compare_name (file1, file2)
struct file *file1, *file2;
{
return strcmp (file1->name, file2->name);
}
int
rev_cmp_name (file2, file1)
struct file *file1, *file2;
{
return strcmp (file1->name, file2->name);
}
/* Compare file extensions. Files with no extension are `smallest'.
If extensions are the same, compare by filenames instead. */
int
compare_extension (file1, file2)
struct file *file1, *file2;
{
register char *base1, *base2;
register int cmp;
base1 = rindex (file1->name, '.');
base2 = rindex (file2->name, '.');
if (base1 == NULL && base2 == NULL)
return strcmp (file1->name, file2->name);
if (base1 == NULL)
return -1;
if (base2 == NULL)
return 1;
cmp = strcmp (base1, base2);
if (cmp == 0)
return strcmp (file1->name, file2->name);
return cmp;
}
int
rev_cmp_extension (file2, file1)
struct file *file1, *file2;
{
register char *base1, *base2;
register int cmp;
base1 = rindex (file1->name, '.');
base2 = rindex (file2->name, '.');
if (base1 == NULL && base2 == NULL)
return strcmp (file1->name, file2->name);
if (base1 == NULL)
return -1;
if (base2 == NULL)
return 1;
cmp = strcmp (base1, base2);
if (cmp == 0)
return strcmp (file1->name, file2->name);
return cmp;
}
/* List all the files now in the table. */
void
print_current_files ()
{
register int i;
switch (format)
{
case one_per_line:
for (i = 0; i < files_index; i++)
{
print_file_name_and_frills (files + i);
putchar ('\n');
}
break;
case many_per_line:
print_many_per_line ();
break;
case horizontal:
print_horizontal ();
break;
case with_commas:
print_with_commas ();
break;
case long_format:
for (i = 0; i < files_index; i++)
{
print_long_format (files + i);
putchar ('\n');
}
break;
}
}
void
print_long_format (f)
struct file *f;
{
char modebuf[20];
char timebuf[40];
long when;
unsigned long mode = f->stats.st_mode;
if(mode & M_CHUNK)
strcpy(modebuf, "Chunk");
else if(mode & M_VALUE)
strcpy(modebuf, "Value");
else if(mode & (M_TREEDIR|M_HASHDIR))
strcpy(modebuf, "Node ");
else modebuf[0] = 0;
switch (time_type)
{
case time_ctime:
when = f->stats.st_ctime;
break;
case time_mtime:
when = f->stats.st_mtime;
break;
case time_atime:
when = f->stats.st_atime;
break;
}
if(when > 0)
{
strcpy (timebuf, (char *)ctime (&when));
if (current_time - when > 6L * 30L * 24L * 60L * 60L
|| current_time - when < 0L)
{
/* The file is fairly old or in the future.
POSIX says the cutoff is 6 months old;
approximate this by 6*30 days.
Show the year instead of the time of day. */
strcpy (timebuf + 11, timebuf + 19);
}
} else strcpy (timebuf, " No Date ");
timebuf[16] = 0;
if (print_inode)
#ifndef PCDOS
printf ("%6u ", f->stats.st_ino);
#else
printf ("%5u ", f->stats.st_ino);
#endif
if (print_block_size)
{
printf ("%*u ", block_size_size, convert_blocks (ST_NBLOCKS (&f->stats),
kilobyte_blocks));/* used blocks */
}
printf("%s ", modebuf);
printf ("%8lu ", f->stats.st_size);
printf ("%s ", timebuf + 4);
print_name_with_quoting (f->name);
if (f->filetype == symbolic_link)
{
if (f->linkname)
{
fputs (" -> ", stdout);
print_name_with_quoting (f->linkname);
if (indicator_style != none)
print_type_indicator (f->linkmode);
}
}
else if (indicator_style != none)
print_type_indicator (f->stats.st_mode);
}
void
print_name_with_quoting (p)
register char *p;
{
register unsigned char c;
if (quote_as_string)
putchar ('"');
while (c = *p++)
{
if (quote_funny_chars)
{
switch (c)
{
case '\\':
printf ("\\\\");
break;
case '\n':
printf ("\\n");
break;
case '\b':
printf ("\\b");
break;
case '\r':
printf ("\\r");
break;
case '\t':
printf ("\\t");
break;
case '\f':
printf ("\\f");
break;
case ' ':
printf ("\\ ");
break;
case '"':
printf ("\\\"");
break;
default:
if (c > 040 && c < 0177)
putchar (c);
else
printf ("\\%03o", (unsigned int) c);
}
}
else
{
if (c >= 040 && c < 0177)
putchar (c);
else if (!qmark_funny_chars)
putchar (c);
else
putchar ('?');
}
}
if (quote_as_string)
putchar ('"');
}
/* Print the file name of `f' with appropriate quoting.
Also print file size, inode number, and filetype indicator character,
as requested by switches. */
void
print_file_name_and_frills (f)
struct file *f;
{
if (print_inode)
#ifndef PCDOS
printf ("%6u ", f->stats.st_ino);
#else
printf ("%5u ", f->stats.st_ino);
#endif
if (print_block_size)
{
printf ("%*u ",block_size_size, convert_blocks (ST_NBLOCKS (&f->stats),
kilobyte_blocks));/* used blocks */
}
#ifdef PCDOS
if(format == many_per_line)
{
strupr(f->name);
if((f->stats.st_mode & S_IFMT) == S_IFDIR)
putchar('[');
}
#endif
print_name_with_quoting (f->name);
#ifdef PCDOS
if(format == many_per_line)
if((f->stats.st_mode & S_IFMT) == S_IFDIR)
putchar(']');
#endif
if (indicator_style != none)
print_type_indicator (f->stats.st_mode);
}
void
print_type_indicator (mode)
unsigned int mode;
{
switch (mode & S_IFMT)
{
case S_IFDIR:
#ifdef PCDOS
if(format != many_per_line)
#endif
putchar ('/');
break;
#ifdef S_IFLNK
case S_IFLNK:
putchar ('@');
break;
#endif
#ifdef S_IFIFO
case S_IFIFO:
putchar ('|');
break;
#endif
#ifdef S_IFSOCK
case S_IFSOCK:
putchar ('=');
break;
#endif
case S_IFREG:
if (indicator_style == all
&& (mode & (S_IEXEC | S_IEXEC >> 3 | S_IEXEC >> 6)))
putchar ('*');
break;
}
}
int
length_of_file_name_and_frills (f)
struct file *f;
{
register char *p = f->name;
register char c;
register int len = 0;
unsigned filetype = f->stats.st_mode & S_IFMT;
if (print_inode)
{
#ifndef PCDOS
len += 7;
#else
len += 6;
#endif
}
if (print_block_size)
len += 1 + block_size_size;
if (quote_as_string)
len += 2;
while (c = *p++)
{
if (quote_funny_chars)
{
switch (c)
{
case '\\':
case '\n':
case '\b':
case '\r':
case '\t':
case '\f':
case ' ':
len += 2;
break;
case '"':
if (quote_as_string)
len += 2;
else
len += 1;
break;
default:
if (c >= 040 && c < 0177)
len += 1;
else
len += 4;
}
}
else
len += 1;
}
if (indicator_style != none)
{
if (filetype == S_IFREG)
{
if (indicator_style == all
&& (f->stats.st_mode & (S_IEXEC | S_IEXEC >> 3 | S_IEXEC >> 6)))
len += 1;
}
else if (filetype & S_IFDIR
#ifdef S_IFLNK
|| filetype == S_IFLNK
#endif
#ifdef S_IFIFO
|| filetype == S_IFIFO
#endif
#ifdef S_IFSOCK
|| filetype == S_IFSOCK
#endif
) len += 1;
}
#ifdef PCDOS
if(format == many_per_line)
{
if(filetype == S_IFDIR)
{
len += 2;
if(indicator_style != none) --len;
}
}
#endif
return len;
}
void
print_many_per_line ()
{
int filesno; /* Index into files. */
int row; /* Current row. */
int max_name_length; /* Length of longest file name + frills. */
int name_length; /* Length of each file name + frills. */
int pos; /* Current character column. */
int cols; /* Number of files across. */
int rows; /* Maximum number of files down. */
/* Compute the maximum file name length. */
max_name_length = 0;
for (filesno = 0; filesno < files_index; filesno++)
{
name_length = length_of_file_name_and_frills (files + filesno);
if (name_length > max_name_length)
max_name_length = name_length;
}
/* Allow at least two spaces between names. */
max_name_length += 2;
/* Calculate the maximum number of columns that will fit. */
cols = line_length / max_name_length;
if (cols == 0)
cols = 1;
/* Calculate the number of rows that will be in each column except possibly
for a short column on the right. */
rows = files_index / cols + (files_index % cols != 0);
/* Recalculate columns based on rows. */
cols = files_index / rows + (files_index % rows != 0);
for (row = 0; row < rows; row++)
{
filesno = row;
pos = 0;
/* Print the next row. */
while (1)
{
print_file_name_and_frills (files + filesno);
name_length = length_of_file_name_and_frills (files + filesno);
filesno += rows;
if (filesno >= files_index)
break;
indent (pos + name_length, pos + max_name_length);
pos += max_name_length;
}
putchar ('\n');
}
}
void
print_horizontal ()
{
int filesno;
int max_name_length;
int name_length;
int cols;
int pos;
/* Compute the maximum file name length. */
max_name_length = 0;
for (filesno = 0; filesno < files_index; filesno++)
{
name_length = length_of_file_name_and_frills (files + filesno);
if (name_length > max_name_length)
max_name_length = name_length;
}
/* Allow two spaces between names. */
max_name_length += 2;
cols = line_length / max_name_length;
if (cols == 0)
cols = 1;
pos = 0;
name_length = 0;
for (filesno = 0; filesno < files_index; filesno++)
{
if (filesno != 0)
{
if (filesno % cols == 0)
{
putchar ('\n');
pos = 0;
}
else
{
indent (pos + name_length, pos + max_name_length);
pos += max_name_length;
}
}
print_file_name_and_frills (files + filesno);
name_length = length_of_file_name_and_frills (files + filesno);
}
putchar ('\n');
}
void
print_with_commas ()
{
int filesno;
int pos, old_pos;
pos = 0;
for (filesno = 0; filesno < files_index; filesno++)
{
old_pos = pos;
pos += length_of_file_name_and_frills (files + filesno);
if (filesno + 1 < files_index)
pos += 2; /* For the comma and space */
if (old_pos != 0 && pos >= line_length)
{
putchar ('\n');
pos -= old_pos;
}
print_file_name_and_frills (files + filesno);
if (filesno + 1 < files_index)
{
putchar (',');
putchar (' ');
}
}
putchar ('\n');
}
#ifndef PCDOS
struct userid
{
unsigned short uid;
char *name;
struct userid *next;
};
struct userid *user_alist;
/* Translate `uid' to a login name, with cache. */
char *
getuser (uid)
unsigned short uid;
{
register struct userid *tail;
struct passwd *pwent;
char usernum_string[20];
for (tail = user_alist; tail; tail = tail->next)
if (tail->uid == uid)
return tail->name;
pwent = getpwuid (uid);
tail = (struct userid *) xmalloc (sizeof (struct userid));
tail->uid = uid;
tail->next = user_alist;
if (pwent == NULL)
{
sprintf (usernum_string, "%u", (unsigned short) uid);
tail->name = copystring (usernum_string);
}
else
tail->name = copystring (pwent->pw_name);
user_alist = tail;
return tail->name;
}
/* We use the same struct as for userids. */
struct userid *group_alist;
/* Translate `gid' to a group name, with cache. */
char *
getgroup (gid)
unsigned short gid;
{
register struct userid *tail;
struct group *grent;
char groupnum_string[20];
for (tail = group_alist; tail; tail = tail->next)
if (tail->uid == gid)
return tail->name;
grent = getgrgid (gid);
tail = (struct userid *) xmalloc (sizeof (struct userid));
tail->uid = gid;
tail->next = group_alist;
if (grent == NULL)
{
sprintf (groupnum_string, "%u", (unsigned int) gid);
tail->name = copystring (groupnum_string);
}
else
tail->name = copystring (grent->gr_name);
group_alist = tail;
return tail->name;
}
#endif
/* Assuming cursor is at position `from', indent up to position `to'. */
void
indent (from, to)
int from, to;
{
while (from < to)
{
if (to / tabsize > from / tabsize)
{
putchar ('\t');
from += tabsize - from % tabsize;
}
else
{
putchar (' ');
from++;
}
}
}
/* Low level subroutines of general use,
not specifically related to the task of listing a directory. */
char *
xrealloc (obj, size)
void *obj;
int size;
{
char *val = realloc (obj, size);
if (!val)
error (1, 0, "virtual memory exhausted");
return val;
}
char *
xmalloc (size)
int size;
{
char *val = malloc (size);
if (!val)
error (1, 0, "virtual memory exhausted");
return val;
}
/* Return a newly allocated copy of `string'. */
char *
copystring (string)
char *string;
{
return strcpy ((char *) xmalloc (strlen (string) + 1), string);
}
/* Put `dirname/name' into `dest', handling `.' and `/' properly. */
void
attach (dest, dirname, name)
char *dest, *dirname, *name;
{
char *dirnamep = dirname;
/* Copy dirname if it is not ".". */
if (dirname[0] != '.' || dirname[1] != 0)
{
while (*dirnamep)
*dest++ = *dirnamep++;
/* Add '/' if `dirname' doesn't already end with it. */
if (dirnamep > dirname && (dirnamep[-1] != '/'
#ifdef PCDOS
&& dirnamep[-1] != ':'
#endif
))
*dest++ = '/';
}
while (*name)
*dest++ = *name++;
*dest = 0;
}
/* Check for a globbing pattern in an explicit argument, the shell may
have passed it along, always for PCDOS. */
int
if_pattern(name)
register char *name;
{
register char ch;
while (ch = *name++)
if (ch == '*' || ch == '?' || ch == '[')
return 1;
return 0;
}
#ifdef PCDOS
void lose_backslashes(char *path)
{
while(*path)
{
if(*path == '\\') *path = '/';
++path;
}
}
#endif
void
usage ()
{
fprintf (stdout, "\
Usage: ls [-abdgiklnpqrstxABCFNQRSUX1] [path...]\n");
fprintf (stdout, "\n\
Switch-------------------Meaning\n\
a, +all List all files.\n\
b, +escape Quote nongraphic characters.\n\
d, +directory List directory name, not contents.\n\
g, Ignored for Unix compatibility.\n\
i, +inode Print index number of each file.\n\
k, +kilobytes Print file sizes in kilobyte blocks.\n\
l, +format=long Print in long format.\n\
m, +format=commas List files horizontally, separated by commas.\n\
p Put '/' after each directory, if not multi-col.\n\
r, +reverse Sort in reverse order.\n\
s, +size Print the size of each file in blocks allocated.\n\
t, +sort=time Sort directory contents by timestamp.\n\
x, +format=across Multi-column, sorted horizontally.\n\
A, +almost-all List all files except for '.' and '..'.\n\
B, +ignore-backups Do not list files that end with '~'.\n\
C, +format=vertical Multi-column, sorted vertically.\n\
F, +classify Tag the file type of each file.\n\
N, +literal Do not quote file names.\n\
Q, +quote-name Enclose file names in double quotes.\n\
R, +recursive List the contents of directories recursively.\n\
S, +sort=size Sort directory contents by file size.\n\
U, +sort=none Do not sort directory contents.\n\
X, +sort=extension Sort directory contents by file extension.\n\
1, +format=single-column List one file per line.\n\
w, +width cols Assume screen width to be 'cols' wide.\n\
T. +tabsize cols Assume that each tabstop is 'cols' wide.\n\
I, +ignore pattern Do not list files that match 'pattern'.\n");
exit (1);
}
/* Getopt for GNU.
Copyright (C) 1987, 1989, 1990 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#ifdef __STDC__
#define CONST const
#else
#define CONST
#endif
/* This version of `getopt' appears to the caller like standard Unix `getopt'
but it behaves differently for the user, since it allows the user
to intersperse the options with the other arguments.
As `getopt' works, it permutes the elements of `argv' so that,
when it is done, all the options precede everything else. Thus
all application programs are extended to handle flexible argument order.
Setting the environment variable _POSIX_OPTION_ORDER disables permutation.
Then the behavior is completely standard.
GNU application programs can use a third alternative mode in which
they can distinguish the relative order of options and other arguments. */
/* For communication from `getopt' to the caller.
When `getopt' finds an option that takes an argument,
the argument value is returned here.
Also, when `ordering' is RETURN_IN_ORDER,
each non-option ARGV-element is returned here. */
char *optarg = 0;
/* Index in ARGV of the next element to be scanned.
This is used for communication to and from the caller
and for communication between successive calls to `getopt'.
On entry to `getopt', zero means this is the first call; initialize.
When `getopt' returns EOF, this is the index of the first of the
non-option elements that the caller should itself scan.
Otherwise, `optind' communicates from one call to the next
how much of ARGV has been scanned so far. */
int optind = 0;
/* The next char to be scanned in the option-element
in which the last option character we returned was found.
This allows us to pick up the scan where we left off.
If this is zero, or a null string, it means resume the scan
by advancing to the next ARGV-element. */
static char *nextchar;
/* Callers store zero here to inhibit the error message
for unrecognized options. */
int opterr = 1;
/* Describe how to deal with options that follow non-option ARGV-elements.
If the caller did not specify anything,
the default is REQUIRE_ORDER if the environment variable
_POSIX_OPTION_ORDER is defined, PERMUTE otherwise.
REQUIRE_ORDER means don't recognize them as options;
stop option processing when the first non-option is seen.
This is what Unix does.
This mode of operation is selected by either setting the environment
variable _POSIX_OPTION_ORDER, or using `+' as the first character
of the list of option characters.
PERMUTE is the default. We permute the contents of ARGV as we scan,
so that eventually all the non-options are at the end. This allows options
to be given in any order, even with programs that were not written to
expect this.
RETURN_IN_ORDER is an option available to programs that were written
to expect options and other ARGV-elements in any order and that care about
the ordering of the two. We describe each non-option ARGV-element
as if it were the argument of an option with character code 1.
Using `-' as the first character of the list of option characters
selects this mode of operation.
The special argument `--' forces an end of option-scanning regardless
of the value of `ordering'. In the case of RETURN_IN_ORDER, only
`--' can cause `getopt' to return EOF with `optind' != ARGC. */
static enum
{
REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
} ordering;
/* Describe the long-named options requested by the application.
_GETOPT_LONG_OPTIONS is a vector of `struct option' terminated by an
element containing a name which is zero.
The field `has_arg' is 1 if the option takes an argument,
2 if it takes an optional argument. */
CONST struct option *_getopt_long_options;
int _getopt_long_only = 0;
/* Index in _GETOPT_LONG_OPTIONS of the long-named option actually found.
Only valid when a long-named option was found. */
int option_index;
/* Handle permutation of arguments. */
/* Describe the part of ARGV that contains non-options that have
been skipped. `first_nonopt' is the index in ARGV of the first of them;
`last_nonopt' is the index after the last of them. */
static int first_nonopt;
static int last_nonopt;
/* Exchange two adjacent subsequences of ARGV.
One subsequence is elements [first_nonopt,last_nonopt)
which contains all the non-options that have been skipped so far.
The other is elements [last_nonopt,optind), which contains all
the options processed since those non-options were skipped.
`first_nonopt' and `last_nonopt' are relocated so that they describe
the new indices of the non-options in ARGV after they are moved. */
static void
exchange (argv)
char **argv;
{
int nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *);
char **temp = (char **) alloca (nonopts_size);
/* Interchange the two blocks of data in ARGV. */
bcopy (&argv[first_nonopt], temp, nonopts_size);
bcopy (&argv[last_nonopt], &argv[first_nonopt],
(optind - last_nonopt) * sizeof (char *));
bcopy (temp, &argv[first_nonopt + optind - last_nonopt], nonopts_size);
/* Update records for the slots the non-options now occupy. */
first_nonopt += (optind - last_nonopt);
last_nonopt = optind;
}
/* Scan elements of ARGV (whose length is ARGC) for option characters
given in OPTSTRING.
If an element of ARGV starts with '-', and is not exactly "-" or "--",
then it is an option element. The characters of this element
(aside from the initial '-') are option characters. If `getopt'
is called repeatedly, it returns successively each of the option characters
from each of the option elements.
If `getopt' finds another option character, it returns that character,
updating `optind' and `nextchar' so that the next call to `getopt' can
resume the scan with the following option character or ARGV-element.
If there are no more option characters, `getopt' returns `EOF'.
Then `optind' is the index in ARGV of the first ARGV-element
that is not an option. (The ARGV-elements have been permuted
so that those that are not options now come last.)
OPTSTRING is a string containing the legitimate option characters.
If an option character is seen that is not listed in OPTSTRING,
return '?' after printing an error message. If you set `opterr' to
zero, the error message is suppressed but we still return '?'.
If a char in OPTSTRING is followed by a colon, that means it wants an arg,
so the following text in the same ARGV-element, or the text of the following
ARGV-element, is returned in `optarg'. Two colons mean an option that
wants an optional arg; if there is text in the current ARGV-element,
it is returned in `optarg', otherwise `optarg' is set to zero.
If OPTSTRING starts with `-' or `+', it requests different methods of
handling the non-option ARGV-elements.
See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
Long-named options begin with `+' instead of `-'.
Their names may be abbreviated as long as the abbreviation is unique
or is an exact match for some defined option. If they have an
argument, it follows the option name in the same ARGV-element, separated
from the option name by a `=', or else the in next ARGV-element.
When `getopt' finds a long-named option, it returns 0 if that option's
`flag' field is nonzero, the value of the option's `val' field
otherwise. */
int
getopt (argc, argv, optstring)
int argc;
char **argv;
CONST char *optstring;
{
optarg = 0;
/* Initialize the internal data when the first call is made.
Start processing options with ARGV-element 1 (since ARGV-element 0
is the program name); the sequence of previously skipped
non-option ARGV-elements is empty. */
if (optind == 0)
{
first_nonopt = last_nonopt = optind = 1;
nextchar = 0;
/* Determine how to handle the ordering of options and nonoptions. */
if (optstring[0] == '-')
{
ordering = RETURN_IN_ORDER;
++optstring;
}
else if (optstring[0] == '+')
{
ordering = REQUIRE_ORDER;
++optstring;
}
else if (getenv ("_POSIX_OPTION_ORDER") != 0)
ordering = REQUIRE_ORDER;
else
ordering = PERMUTE;
}
if (nextchar == NULL || *nextchar == 0)
{
if (ordering == PERMUTE)
{
/* If we have just processed some options following some non-options,
exchange them so that the options come first. */
if (first_nonopt != last_nonopt && last_nonopt != optind)
exchange (argv);
else if (last_nonopt != optind)
first_nonopt = optind;
/* Now skip any additional non-options
and extend the range of non-options previously skipped. */
while (optind < argc
&& (argv[optind][0] != '-'
|| argv[optind][1] == 0)
&& (_getopt_long_options == 0
|| argv[optind][0] != '+'
|| argv[optind][1] == 0))
optind++;
last_nonopt = optind;
}
/* Special ARGV-element `--' means premature end of options.
Skip it like a null option,
then exchange with previous non-options as if it were an option,
then skip everything else like a non-option. */
if (optind != argc && !strcmp (argv[optind], "--"))
{
optind++;
if (first_nonopt != last_nonopt && last_nonopt != optind)
exchange (argv);
else if (first_nonopt == last_nonopt)
first_nonopt = optind;
last_nonopt = argc;
optind = argc;
}
/* If we have done all the ARGV-elements, stop the scan
and back over any non-options that we skipped and permuted. */
if (optind == argc)
{
/* Set the next-arg-index to point at the non-options
that we previously skipped, so the caller will digest them. */
if (first_nonopt != last_nonopt)
optind = first_nonopt;
return EOF;
}
/* If we have come to a non-option and did not permute it,
either stop the scan or describe it to the caller and pass it by. */
if ((argv[optind][0] != '-' || argv[optind][1] == 0)
&& (_getopt_long_options == 0
|| argv[optind][0] != '+' || argv[optind][1] == 0))
{
if (ordering == REQUIRE_ORDER)
return EOF;
optarg = argv[optind++];
return 1;
}
/* We have found another option-ARGV-element.
Start decoding its characters. */
nextchar = argv[optind] + 1;
}
if (_getopt_long_options != 0
&& (argv[optind][0] == '+'
|| (_getopt_long_only && argv[optind][0] == '-'))
)
{
CONST struct option *p;
char *s = nextchar;
int exact = 0;
int ambig = 0;
CONST struct option *pfound = NULL;
int indfound;
while (*s && *s != '=')
s++;
/* Test all options for either exact match or abbreviated matches. */
for (p = _getopt_long_options, option_index = 0; p->name;
p++, option_index++)
if (!strncmp (p->name, nextchar, s - nextchar))
{
if (s - nextchar == strlen (p->name))
{
/* Exact match found. */
pfound = p;
indfound = option_index;
exact = 1;
break;
}
else if (pfound == NULL)
{
/* First nonexact match found. */
pfound = p;
indfound = option_index;
}
else
/* Second nonexact match found. */
ambig = 1;
}
if (ambig && !exact)
{
fprintf (stderr, "%s: option `%s' is ambiguous\n",
argv[0], argv[optind]);
nextchar += strlen (nextchar);
optind++;
return '?';
}
if (pfound != NULL)
{
option_index = indfound;
optind++;
if (*s)
{
if (pfound->has_arg > 0)
optarg = s + 1;
else
{
fprintf (stderr,
"%s: option `%c%s' doesn't allow an argument\n",
argv[0], argv[optind - 1][0], pfound->name);
nextchar += strlen (nextchar);
return '?';
}
}
else if (pfound->has_arg == 1)
{
if (optind < argc)
optarg = argv[optind++];
else
{
fprintf (stderr, "%s: option `%s' requires an argument\n",
argv[0], argv[optind - 1]);
nextchar += strlen (nextchar);
return '?';
}
}
nextchar += strlen (nextchar);
if (pfound->flag)
{
*(pfound->flag) = pfound->val;
return 0;
}
return pfound->val;
}
/* Can't find it as a long option. If this is getopt_long_only,
and the option starts with '-' and is a valid short
option, then interpret it as a short option. Otherwise it's
an error. */
if (_getopt_long_only == 0 || argv[optind][0] == '+' ||
index (optstring, *nextchar) == NULL)
{
if (opterr != 0)
fprintf (stderr, "%s: unrecognized option `%c%s'\n",
argv[0], argv[optind][0], nextchar);
nextchar += strlen (nextchar);
optind++;
return '?';
}
}
/* Look at and handle the next option-character. */
{
char c = *nextchar++;
char *temp = index (optstring, c);
/* Increment `optind' when we start to process its last character. */
if (*nextchar == 0)
optind++;
if (temp == 0 || c == ':')
{
if (opterr != 0)
{
if (c < 040 || c >= 0177)
fprintf (stderr, "%s: unrecognized option, character code 0%o\n",
argv[0], c);
else
fprintf (stderr, "%s: unrecognized option `-%c'\n",
argv[0], c);
}
return '?';
}
if (temp[1] == ':')
{
if (temp[2] == ':')
{
/* This is an option that accepts an argument optionally. */
if (*nextchar != 0)
{
optarg = nextchar;
optind++;
}
else
optarg = NULL;
nextchar = NULL;
}
else
{
/* This is an option that requires an argument. */
if (*nextchar != 0)
{
optarg = nextchar;
/* If we end this ARGV-element by taking the rest as an arg,
we must advance to the next element now. */
optind++;
}
else if (optind == argc)
{
if (opterr != 0)
fprintf (stderr, "%s: option `-%c' requires an argument\n",
argv[0], c);
c = '?';
}
else
/* We already incremented `optind' once;
increment it again when taking next ARGV-elt as argument. */
optarg = argv[optind++];
nextchar = 0;
}
}
return c;
}
}
/* Getopt for GNU.
Copyright (C) 1987, 1989 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#ifdef __STDC__
#define CONST const
#else
#define CONST
#endif
#if !defined (NULL)
#define NULL ((void *)0)
#endif
int
getopt_long (argc, argv, options, long_options, opt_index)
int argc;
char **argv;
CONST char *options;
CONST struct option *long_options;
int *opt_index;
{
int val;
_getopt_long_options = long_options;
val = getopt (argc, argv, options);
if (val == 0 && opt_index != NULL)
*opt_index = option_index;
return val;
}
/* Like getopt_long, but '-' as well as '+' can indicate a long option.
If an option that starts with '-' doesn't match a long option,
but does match a short option, it is parsed as a short option
instead. */
int
getopt_long_only (argc, argv, options, long_options, opt_index)
int argc;
char **argv;
CONST char *options;
CONST struct option *long_options;
int *opt_index;
{
int val;
_getopt_long_options = long_options;
_getopt_long_only = 1;
val = getopt (argc, argv, options);
if (val == 0 && opt_index != NULL)
*opt_index = option_index;
return val;
}
/* argmatch.c -- find a match for a string in an array
Copyright (C) 1990 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* Written by David MacKenzie <djm@ai.mit.edu> */
extern char *program_name;
/* If ARG is an unambiguous match for an element of the
null-terminated array OPTLIST, return the index in OPTLIST
of the matched element, else -1 if it does not match any element
or -2 if it is ambiguous (is a prefix of more than one element). */
int
argmatch (arg, optlist)
char *arg;
char **optlist;
{
int i; /* Temporary index in OPTLIST. */
int arglen; /* Length of ARG. */
int matchind = -1; /* Index of first nonexact match. */
int ambiguous = 0; /* If nonzero, multiple nonexact match(es). */
arglen = strlen (arg);
/* Test all elements for either exact match or abbreviated matches. */
for (i = 0; optlist[i]; i++)
{
if (!strncmp (optlist[i], arg, arglen))
{
if (strlen (optlist[i]) == arglen)
/* Exact match found. */
return i;
else if (matchind == -1)
/* First nonexact match found. */
matchind = i;
else
/* Second nonexact match found. */
ambiguous = 1;
}
}
if (ambiguous)
return -2;
else
return matchind;
}
/* Error reporting for argmatch.
KIND is a description of the type of entity that was being matched.
VALUE is the invalid value that was given.
PROBLEM is the return value from argmatch. */
void
invalid_arg (kind, value, problem)
char *kind;
char *value;
int problem;
{
fprintf (stderr, "%s: ", program_name);
if (problem == -1)
fprintf (stderr, "invalid");
else /* Assume -2. */
fprintf (stderr, "ambiguous");
fprintf (stderr, " %s `%s'\n", kind, value);
}
/* File-name wildcard pattern matching for GNU.
Copyright (C) 1985, 1988, 1989, 1990 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* To whomever it may concern: I have never seen the code which most
Unix programs use to perform this function. I wrote this from scratch
based on specifications for the pattern matching. --RMS. */
static int glob_match_after_star ();
/* Return nonzero if PATTERN has any special globbing chars in it. */
int
glob_pattern_p (pattern)
char *pattern;
{
register char *p = pattern;
register char c;
while ((c = *p++) != '\0')
switch (c)
{
case '?':
case '[':
case '*':
return 1;
case '\\':
if (*p++ == '\0')
return 0;
}
return 0;
}
/* Match the pattern PATTERN against the string TEXT;
return 1 if it matches, 0 otherwise.
A match means the entire string TEXT is used up in matching.
In the pattern string, `*' matches any sequence of characters,
`?' matches any character, [SET] matches any character in the specified set,
[!SET] matches any character not in the specified set.
A set is composed of characters or ranges; a range looks like
character hyphen character (as in 0-9 or A-Z).
[0-9a-zA-Z_] is the set of characters allowed in C identifiers.
Any other character in the pattern must be matched exactly.
To suppress the special syntactic significance of any of `[]*?!-\',
and match the character exactly, precede it with a `\'.
If DOT_SPECIAL is nonzero,
`*' and `?' do not match `.' at the beginning of TEXT. */
int
glob_match (pattern, text, dot_special)
char *pattern, *text;
int dot_special;
{
register char *p = pattern, *t = text;
register char c;
while ((c = *p++) != '\0')
switch (c)
{
case '?':
if (*t == '\0' || (dot_special && t == text && *t == '.'))
return 0;
else
++t;
break;
case '\\':
if (*p++ != *t++)
return 0;
break;
case '*':
if (dot_special && t == text && *t == '.')
return 0;
return glob_match_after_star (p, t);
case '[':
{
register char c1 = *t++;
int invert;
if (c1 == '\0')
return 0;
invert = (*p == '!');
if (invert)
p++;
c = *p++;
while (1)
{
register char cstart = c, cend = c;
if (c == '\\')
{
cstart = *p++;
cend = cstart;
}
if (cstart == '\0')
return 0; /* Missing ']'. */
c = *p++;
if (c == '-')
{
cend = *p++;
if (cend == '\\')
cend = *p++;
if (cend == '\0')
return 0;
c = *p++;
}
if (c1 >= cstart && c1 <= cend)
goto match;
if (c == ']')
break;
}
if (!invert)
return 0;
break;
match:
/* Skip the rest of the [...] construct that already matched. */
while (c != ']')
{
if (c == '\0')
return 0;
c = *p++;
if (c == '\0')
return 0;
if (c == '\\')
p++;
}
if (invert)
return 0;
break;
}
default:
if (c != *t++)
return 0;
}
return *t == '\0';
}
/* Like glob_match, but match PATTERN against any final segment of TEXT. */
static int
glob_match_after_star (pattern, text)
char *pattern, *text;
{
register char *p = pattern, *t = text;
register char c, c1;
while ((c = *p++) == '?' || c == '*')
if (c == '?' && *t++ == '\0')
return 0;
if (c == '\0')
return 1;
if (c == '\\')
c1 = *p;
else
c1 = c;
--p;
while (1)
{
if ((c == '[' || *t == c1) && glob_match (p, t, 0))
return 1;
if (*t++ == '\0')
return 0;
}
}
/* error.c -- error handler for noninteractive utilities
Copyright (C) 1990 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* David MacKenzie */
/* MY GOD [NDC] */
#define va_alist a1, a2, a3, a4, a5, a6, a7, a8
#define va_dcl char *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8;
void exit ();
#if 0
static char *
strerror (errnum)
int errnum;
{
extern char *sys_errlist[];
extern int sys_nerr;
if (errnum > 0 && errnum < sys_nerr)
return sys_errlist[errnum];
return "Unknown system error";
}
#endif
/* Print the program name and error message MESSAGE, which is a printf-style
format string with optional args.
If ERRNUM is nonzero, print its corresponding system error message.
Exit with status STATUS if it is nonzero. */
/* VARARGS */
void
error (status, errnum, message, va_alist)
int status;
int errnum;
char *message;
va_dcl
{
extern char *program_name;
fprintf (stderr, "%s: ", program_name);
fprintf (stderr, message, a1, a2, a3, a4, a5, a6, a7, a8);
if (errnum)
fprintf (stderr, ": %s", strerror (errnum));
putc ('\n', stderr);
fflush (stderr);
if (status)
exit (status);
}
/* ========================== MAIN PROGRAM ============================== */
#if defined(SKELETON)
int cfls(int argc, char **argv)
#else
void main (int argc, char **argv)
#endif
{
register int i;
register struct pending *thispend;
#if !defined(SKELETON)
cfinit("cfls", 400, NULL);
#endif
dir_defaulted = 1;
print_dir_name = 1;
pending_dirs = NULL;
ignore_patterns = NULL;
current_time = time ((long *) 0);
program_name = argv[0];
i = decode_switches (argc, argv);
format_needs_stat =
sort_type == sort_time
|| sort_type == sort_size
|| format == long_format
|| trace_links
|| trace_dirs
|| indicator_style != none
#ifdef PCDOS
|| format == many_per_line
#endif
|| print_block_size || print_inode;
nfiles = 100;
files = (struct file *) xmalloc (sizeof (struct file) * nfiles);
files_index = 0;
clear_files ();
if (i < argc)
dir_defaulted = 0;
for (; i < argc; i++)
{
#ifdef PCDOS
lose_backslashes(argv[i]);
#endif
gobble_file (argv[i], 1, "", NULL);
}
if (dir_defaulted)
{
if (immediate_dirs)
gobble_file (".", 1, "", NULL);
else
queue_directory (".", NULL, NULL);
}
if (files_index)
{
sort_files ();
if (!immediate_dirs)
extract_dirs_from_files ("", 0, NULL);
/* `files_index' might be zero now. */
}
if (files_index)
{
print_current_files ();
if (pending_dirs)
putchar ('\n');
}
else if (!trace_dirs && pending_dirs && pending_dirs->next == NULL)
print_dir_name = 0;
while (pending_dirs)
{
thispend = pending_dirs;
pending_dirs = pending_dirs->next;
print_dir (thispend);
free (thispend->name);
if (thispend->realname)
free (thispend->realname);
free (thispend);
print_dir_name = 1;
}
#if !defined(SKELETON)
cfexit();
exit(0);
#else
return 0;
#endif
}