home *** CD-ROM | disk | FTP | other *** search
- /* `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
- }
-
-