DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T l

⟦f842a0ea7⟧ TextFile

    Length: 30892 (0x78ac)
    Types: TextFile
    Names: »ls.c«

Derivation

└─⟦a05ed705a⟧ Bits:30007078 DKUUG GNU 2/12/89
    └─⟦this⟧ »./ls.c« 

TextFile

/* `ls' directory listing program for GNU.
   Copyright (C) 1985, 1988 Free Software Foundation, Inc.

   Modified by Jay Fenlason, Jan 1988 so that ls -Ra doesn't
   loop on ././././. ...
   Modified by Roland McGrath, May 1988 so that -g does something.
   Also made it so that -B -A will not display .*~
   Also cleaned up massive ugliness.

		       NO WARRANTY

  BECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY
NO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW.  EXCEPT
WHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC,
RICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE THIS PROGRAM "AS IS"
WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY
AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE PROGRAM PROVE
DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
CORRECTION.

 IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M.
STALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY
WHO MAY MODIFY AND REDISTRIBUTE THIS PROGRAM AS PERMITTED BELOW, BE
LIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR
OTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR
DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR
A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) THIS
PROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY.

		GENERAL PUBLIC LICENSE TO COPY

  1. You may copy and distribute verbatim copies of this source file
as you receive it, in any medium, provided that you conspicuously
and appropriately publish on each copy a valid copyright notice
"Copyright (C) 1985 Free Software Foundation, Inc.", and include
following the copyright notice a verbatim copy of the above disclaimer
of warranty and of this License.

  2. You may modify your copy or copies of this source file or
any portion of it, and copy and distribute such modifications under
the terms of Paragraph 1 above, provided that you also do the following:

    a) cause the modified files to carry prominent notices stating
    that you changed the files and the date of any change; and

    b) cause the whole of any work that you distribute or publish,
    that in whole or in part contains or is a derivative of this
    program or any part thereof, to be licensed at no charge to all
    third parties on terms identical to those contained in this
    License Agreement (except that you may choose to grant more
    extensive warranty protection to third parties, at your option).

    c) You may charge a distribution fee for the physical act of
    transferring a copy, and you may at your option offer warranty
    protection in exchange for a fee.

  3. You may copy and distribute this program or any portion of it in
compiled, executable or object code form under the terms of Paragraphs
1 and 2 above provided that you do the following:

    a) cause each such copy to be accompanied by the
    corresponding machine-readable source code, which must
    be distributed under the terms of Paragraphs 1 and 2 above; or,

    b) cause each such copy to be accompanied by a
    written offer, with no time limit, to give any third party
    free (except for a nominal shipping charge) a machine readable
    copy of the corresponding source code, to be distributed
    under the terms of Paragraphs 1 and 2 above; or,

    c) in the case of a recipient of this program in compiled, executable
    or object code form (without the corresponding source code) you
    shall cause copies you distribute to be accompanied by a copy
    of the written offer of source code which you received along
    with the copy you received.

  4. You may not copy, sublicense, distribute or transfer this program
except as expressly provided under this License Agreement.  Any attempt
otherwise to copy, sublicense, distribute or transfer this program is void and
your rights to use the program under this License agreement shall be
automatically terminated.  However, parties who have received computer
software programs from you with this License Agreement will not have
their licenses terminated so long as such parties remain in full compliance.

  5. If you wish to incorporate parts of this program into other free
programs whose distribution conditions are different, write to the Free
Software Foundation at 675 Mass Ave, Cambridge, MA 02139.  We have not yet
worked out a simple rule that can be stated here, but we will often permit
this.  We will be guided by the two goals of preserving the free status of
all derivatives of our free software and of promoting the sharing and reuse of
software.

 In other words, you are welcome to use, share and improve this program.
 You are forbidden to forbid anyone else to use, share and improve
 what you give them.   Help stamp out software-hoarding!  */
\f


#include <sys/types.h>
#include <sys/stat.h>
#include <sys/dir.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sgtty.h>
#include <stdio.h>
#include <strings.h>
#include <grp.h>
#include <pwd.h>
#ifdef sparc
#include "alloca.h"
#endif
/* If the macro MULTI_COL is defined,
   then multi-column format is the default regardless
   of the type of output device,

   if the macro LONG_FORMAT is defined,
   then long format is the default regardless of the
   type of output device.

   These setting will be used for GNU's preferred
   directory listing programs, but not for GNU's ls,
   which will exist for compatibility.  */

char *copystring ();
int compare_atime (), compare_mtime (), compare_ctime (), compare_names ();
char *getuser (), *getgroup ();

extern int errno;
extern int sys_nerr;
extern char *sys_errlist[];
\f


/* 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.
*/

enum filetype
  {
    normal, directory, symbolic_link
  };

struct file
  {
    char *name;			/* The file name */
    struct stat stat;
    char *linkname;		/* name linked to, for symbolic link,
				   otherwise zero */
    enum filetype filetype;	/* directory, file or symlink */
  };

/* 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;
\f


/* Record of one pending directory waiting to be listed.  */

struct pending
  {
    char *name;
    struct pending *next;
  };

struct pending *pending_dirs;

/* Option flags */

/* 0 for lots of info, one per line.
   1 for just names, one per line.
   2 for just names, many per line.
   3 for many per line, horizontally.
   4 for many per line, separated by commas, no tabs.  */
/* -l, -1, -C, -x and -m control this parameter.  */

enum format
  {
    long_format,
    one_per_line,
    many_per_line,
    horizontal,
    with_commas,
  }
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 */
  }
time_type;

/* Current time (seconds since 1970).  When we are printing a file's time,
   include the year if it is more than 10 months before this time.  */

long current_time;

/* Nonzero means sort by time (-t).  Zero, sort by name.  */

int sort_by_time;

/* Direction of sort.
   0 means highest first if numeric,
   lowest first if alphabetic;
   these happen to be the defaults.
   1 means the opposite order in each case.  -r  */

int sort_reverse;

/* mention the group of each file.  -g  */

int print_groups;

/* mention size in blocks of each file.  -s  */

int print_block_size;

/* Number of digits to use for block sizes.
   4, or more if needed for bigger numbers.  */

int block_size_size;

/* 1 means mention type of file.  -F  */
/* Also -p sets this to -1, which means do so except for executables.  */

int print_filetype;

/* mention inode number of each file.  -i  */

int print_inode;

/* When a symbolic link is found, display info on the file linked to.  -L  */

int trace_links;

/* When a directory is found, display info on its contents.  -R  */

int trace_dirs;

/* When an argument is a directory name, display info on it itself.  -d  */

int immediate_dirs;

/* Don't omit files whose names suggest they are uninteresting.  -A  */

int all_files;

/* Don't omit files "." and ".." -a.  This flag implies all_files */

int really_all_files;

/* Make backup files appear uninteresting.  Better might be options to:
   1) list numbered backups in csh syntax "foo{.~12~,~13~}",  or
   2) ignoring all files matching a regexp that the user specifies. */
int ignore_backup_files;

#define BACKUP_SUFFIX '~'	/* used for detecting backup files */

/* Quote nongraphic chars in file names.  -b  */

int quote_funny_chars;

/* 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;

/* Nonzero means we are listing working directory because no args given */

int dir_defaulted;

/* Line length to use for breaking lines
   in many-per-line format.  Can be set with -w.  */

int line_length;

/* A version string, 'just 'cuz its a good idea */
/* JF:		made this version string */
/* 1.00:	added -v (version) and -h (help) options */
char *version_string = "GNU ls version 1.00\n";

/* A help msg.  Help is always a good idea (?) */
char *help_msg = "\
 This program is used to list the files and subdirectories contained\n\
 in a directory.  For more information, try the command 'info ls'.\n";

\f


int
main (argc, argv)
     int argc;
     char **argv;
{
  register int i;
  register char *thisdir;
  register struct pending *thispend;

  dir_defaulted = 1;
  pending_dirs = 0;
  current_time = time ((long *) 0);

  i = decode_switches (argc, argv);
  /* Note that decode_switches can set elements of argv to zero
     to prevent them from being taken as file names.  */

  nfiles = 100;
  files = (struct file *) xmalloc (sizeof (struct file) * nfiles);
  files_index = 0;

  clear_files ();

  for (; i < argc; i++)
    if (argv[i] != 0)
      {
	dir_defaulted = 0;
	gobble_file (argv[i], 1, "");
      }

  if (dir_defaulted)
    if (immediate_dirs)
      gobble_file (".", 0, "");
    else
      queue_directory (".");

  if (files_index)
    {
      sort_files ();
      if (!immediate_dirs)
	extract_dirs_from_files ("", 0);
      /* files_index may be zero now.  */
    }
  if (files_index)
    print_current_files ();

  while (pending_dirs)
    {
      thisdir = pending_dirs->name;
      thispend = pending_dirs;
      pending_dirs = pending_dirs->next;
      free (thispend);
      print_dir (thisdir);
    }

  return 0;
}
\f


/* Set all the option flags according to the switches specified.
   Also set to zero any elements of argv 
   that are switches or args to switches.

   Returns the index of the first non-option argument.  */

decode_switches (argc, argv)
     int argc;
     char **argv;
{
  extern char *getenv ();
  register int i;
  register char *p;
  struct sgttyb garbage;
  extern char *optarg;
  extern int optind;
  int c;

  /* initialize all switches to default settings */

#ifdef MULTI_COL
  format = many_per_line;
#else
#ifdef LONG_FORMAT
  format = long_format;
#else
  if (ioctl (1, TIOCGETP, &garbage) == 0)
    format = many_per_line;
  else
    format = one_per_line;
#endif
#endif

  time_type = time_mtime;
  sort_by_time = 0;
  sort_reverse = 0;
  print_groups = 0;
  print_block_size = 0;
  print_filetype = 0;
  print_inode = 0;
  trace_links = 0;
  trace_dirs = 0;
  immediate_dirs = 0;
  all_files = 0;
  really_all_files = 0;
  ignore_backup_files = 0;
  quote_funny_chars = 0;
  quote_as_string = 0;

  p = getenv ("COLUMNS");
  line_length = (p ? atoi (p) : 80) - 1;

  while ((c = getopt (argc, argv, "abcdhgilmprstuvw:xABCFLQR1")) != EOF)
    switch (c)
      {
      case 'a':
	all_files = 1;
	really_all_files = 1;
	break;

      case 'b':
	quote_funny_chars = 1;
	break;

      case 'c':
	time_type = time_ctime;
	break;

      case 'd':
	immediate_dirs = 1;
	break;

      case 'g':
	print_groups = 1;
	break;

      case 'h':
	fprintf(stderr,help_msg);
	exit(0);

      case 'i':
	print_inode = 1;
	break;

      case 'l':
	format = long_format;
	break;

      case 'm':
	format = with_commas;
	break;

      case 'p':
	print_filetype = -1;
	break;

      case 'r':
	sort_reverse = 1;
	break;

      case 's':
	print_block_size = 1;
	break;

      case 't':
	sort_by_time = 1;
	break;

      case 'u':
	time_type = time_atime;
	break;

      case 'v':
      	printf("%s",version_string);
	break;

      case 'w':
	line_length = atoi (optarg);
	break;

      case 'x':
	format = horizontal;
	break;

      case 'A':
	all_files = 1;
	break;

      case 'B':
	ignore_backup_files = 1;
	break;

      case 'C':
	format = many_per_line;
	break;

      case 'F':
	print_filetype = 1;
	break;

      case 'L':
	trace_links = 1;
	break;

      case 'Q':
	quote_as_string = 1;
	quote_funny_chars = 1;
	break;

      case 'R':
	trace_dirs = 1;
	break;

      case '1':
	format = one_per_line;
	break;
      }

  return optind;
}
\f


/* Request that the directory named `name' have its contents listed later.  */

queue_directory (name)
     char *name;
{
  struct pending *new = (struct pending *) xmalloc (sizeof (struct pending));
  new->next = pending_dirs;
  pending_dirs = new;
  new->name = copystring (name);
}

/* Read directory `name', and list the files in it.  */

print_dir (name)
     char *name;
{
  register DIR *reading = opendir (name);
  register struct direct *next;
  register int total_blocks = 0;
  register int blocks;

  if (!reading)
    {
      perror_with_name (name);
      return;
    }

  /* Read the directory entries, and insert the subfiles
     into the `files' table.  */

  clear_files ();

  while (next = readdir (reading))
    if (file_interesting_p (next))
      total_blocks += gobble_file (copystring (next->d_name), 0, name);

  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);

  /* If we queued any subdirectories to be listed,
     print each listed directory's name, including this one's.  */

  if (pending_dirs)
    dir_defaulted = 0;

  if (!dir_defaulted)
    printf ("%s:\n", name);
  if (format == long_format || print_block_size)
    printf ("total %d\n", total_blocks);

  if (files_index)
    print_current_files ();

  if (pending_dirs)
    putchar ('\n');

  free (name);
}

/* return nonzero if file should be listed. */
int
file_interesting_p (next)
     register struct direct *next;
{
  if (really_all_files)
    return 1;

  if (ignore_backup_files && next->d_name[next->d_namlen - 1] == BACKUP_SUFFIX)
    return 0;
  
  if (next->d_name[0] != '.'
      || (all_files
	  && next->d_name[1] != '\0'
	  && (next->d_name[1] != '.' || next->d_name[2] != '\0')))
    return 1;

  return 0;
}
\f


/* Enter and remove entries in the table `files'.  */

/* Empty the table of files */

clear_files ()
{
  register int i;

  for (i = 0; i < files_index; i++)
    {
      free (files[i].name);
      free (files[i].linkname);
    }

  files_index = 0;
  block_size_size = 4;
}

/* Add a file to the current table of files.
   check nonzero means verify that the file exists, and
   print an error message if it does not.
   Returns the number of blocks that the file occupies.  */

gobble_file (name, explicit_arg, dirname)
     char *name;
     int explicit_arg;
     char *dirname;
{
  register int val;
  register char *buf;
  register int bufsiz = 100;
  register int blocks;
  register char *concat;

  if (files_index == nfiles)
    files = (struct file *) xrealloc (files,
				      sizeof (struct file) * (nfiles *= 2));

  files[files_index].name = name;
  files[files_index].linkname = 0;

  if (explicit_arg || sort_by_time || format == long_format || trace_links
      || trace_dirs || print_filetype || print_block_size || print_inode)
    {
      /* `concat' gets the absolute pathname of this file.  */

      if (name[0] == '/' || dirname[0] == 0)
	concat = name;
      else
	{
	  concat = (char *) alloca (strlen (name) + strlen (dirname) + 2);
	  strcpy (concat, dirname);
	  strcat (concat, "/");
	  strcat (concat, name);
	}

      /* Read the file's properties.  */

      if (trace_links)
	val = stat (concat, &files[files_index].stat);
      else
	val = lstat (concat, &files[files_index].stat);
      if (val < 0)
	{
	  perror_with_name (concat);
	  return 0;
	}

      blocks = files[files_index].stat.st_blocks;
      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;

      /* Fill in auxiliary fields about the file.  */

      files[files_index].filetype = normal;
	      
      if ((files[files_index].stat.st_mode & S_IFMT) == S_IFLNK)
	files[files_index].filetype = symbolic_link;
      else if ((files[files_index].stat.st_mode & S_IFMT) == S_IFDIR)
	files[files_index].filetype = directory;

      if (format == long_format
	  && files[files_index].filetype == symbolic_link)
	{
	  while (1)
	    {
	      buf = (char *) xmalloc (bufsiz);
	      buf[bufsiz-1] = 0;
	      val = readlink (concat, buf, bufsiz);
	      if (val < 0 || buf[bufsiz-1] == 0)
		break;
	      free (buf);
	      bufsiz *= 2;
	    }
	  if (val < 0)
	    {
	      perror_with_name (concat);
	      files[files_index].linkname = 0;
	    }
	  else
	    {
	      buf[val] = 0;
	      files[files_index].linkname = buf;
	    }
	}
    }

  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 dirs recursively.  */

extract_dirs_from_files (dirname, recursive)
     char *dirname;
     int recursive;
{
  register int i, j = 0;
  register char *concat;

  /* Queue the directories last one first,
     because queueing reverses the order.  */
  for (i = files_index - 1; i >= 0; i--)
    if (files[i].filetype == directory
	&& (! recursive || is_not_dot_or_dotdot (files[i].name)))
      {
	if (files[i].name[0] == '/' || dirname[0] == 0)
	  concat = files[i].name;
	else
	  {
	    concat = (char *) alloca (strlen (files[i].name)
				      + strlen (dirname) + 2);
	    sprintf (concat, "%s/%s", dirname, files[i].name);
	  }
	queue_directory (concat);
	free (files[i].name);
      }

  /* Now delete the directories from the table,
     compacting all the remaining entries.  */

  for (i = 0; i < files_index; i++)
    if (files[i].filetype != directory)
      files[j++] = files[i];
  files_index = j;
}
\f


/* Returns non-zero if the filename doesn't end in . or .. */
/* This is so we don't try to recurse on ././././. ... */
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;
}
\f


/* Sort the files now in the table.  */

sort_files ()
{
  int (*func) ();

  if (sort_by_time)
    switch (time_type)
      {
      case time_ctime:
	func = compare_ctime;
	break;
      case time_mtime:
	func = compare_mtime;
	break;
      case time_atime:
	func = compare_atime;
	break;
      }
  else
    func = compare_names;
  
  qsort (files, files_index, sizeof (struct file), func);

  if (sort_reverse)
    reverse_files ();
}

/* Auxiliary routines for sorting the files */

compare_ctime (file1, file2)
     struct file *file1, *file2;
{
  return file2->stat.st_ctime - file1->stat.st_ctime;
}

compare_mtime (file1, file2)
     struct file *file1, *file2;
{
  return file2->stat.st_mtime - file1->stat.st_mtime;
}

compare_atime (file1, file2)
     struct file *file1, *file2;
{
  return file2->stat.st_atime - file1->stat.st_atime;
}

compare_names (file1, file2)
     struct file *file1, *file2;
{
  return strcmp (file1->name, file2->name);
}

reverse_files ()
{
  register int i, j;
  struct file temp;
  for (i = 0, j = files_index - 1; i < j; i++, j--)
    {
      temp = files[i];
      files[i] = files[j];
      files[j] = temp;
    }
}
\f


/* List all the files now in the table.  */

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;
    }
}

print_long_format (f)
     struct file *f;
{
  char mbuf[20];
  char tbuf[40];
  long time;

  filemodestring (&f->stat, mbuf);
  if (f->filetype == symbolic_link)
    mbuf[0] = 'l';
  mbuf[10] = 0;

  switch (time_type)
    {
    case time_ctime:
      time = f->stat.st_ctime;
      break;
    case time_mtime:
      time = f->stat.st_mtime;
      break;
    case time_atime:
      time = f->stat.st_atime;
      break;
    }

  strcpy (tbuf, ctime (&time));
  if (current_time - time > 300 * 24 * 60 * 60)
    {
      /* File is at least 300 days old.
	 Show its year instead of the time of day.  */
      strcpy (tbuf + 11, tbuf + 19);
    }
  tbuf[16] = 0;

  if (print_inode)
    printf ("%5d ", f->stat.st_ino);

  if (print_block_size)
    printf ("%*d ", block_size_size, f->stat.st_blocks);

  printf ("%s%3d %-9s %-9s", mbuf, f->stat.st_nlink,
	  getuser (f->stat.st_uid), getgroup (f->stat.st_gid));
  if ((f->stat.st_mode & S_IFMT) == S_IFCHR
      || (f->stat.st_mode & S_IFMT) == S_IFBLK)
    printf ("%3d, %3d %s ",
	    major (f->stat.st_rdev), minor (f->stat.st_rdev), tbuf + 4);
  else
    printf ("%8d %s ", f->stat.st_size, tbuf + 4);

  print_name_with_quoting (f->name);

  if (f->filetype == symbolic_link && f->linkname)
    {
      fputs (" -> ", stdout);
      print_name_with_quoting (f->linkname);
    }
}
\f


print_name_with_quoting (p)
     register char *p;
{
  register 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", c);
	    }
	}
      else
	{
	  if (c >= 040 && c < 0177)
	    putchar (c);
	  else
	    putchar ('?');
	}
    }

  if (quote_as_string)
    putchar ('"');
}
\f


/* Print a file name with appropriate quoting.
   Also print file size, inode number, and filetype indicator character,
   as requested by switches.  */

print_file_name_and_frills (f)
     struct file *f;
{
  if (print_inode)
    printf ("%5d ", f->stat.st_ino);

  if (print_block_size)
    printf ("%*d ", block_size_size, f->stat.st_blocks);

  print_name_with_quoting (f->name);

  if (print_filetype)
    switch (f->filetype)
      {
      case directory:
	putchar ('/');
	break;

      case symbolic_link:
	putchar ('@');
	break;

      default:
	if (print_filetype > 0
	    && (f->stat.st_mode & (S_IEXEC|S_IEXEC>>3|S_IEXEC>>6)))
	  putchar ('*');
/*	else		JF because it makes -Fm look funny 
	  putchar (' '); */
      }
}

length_of_file_name_and_frills (f)
     struct file *f;
{
  register char *p = f->name;
  register char c;
  register int len = 0;

  if (print_inode)
    len += 6;

  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 (print_filetype)
    {
      if (f->filetype!=normal)
        len += 1;
      else if (print_filetype > 0
	       && (f->stat.st_mode & (S_IEXEC|S_IEXEC>>3|S_IEXEC>>6)))
        len += 1;
    }

  return len;
}
\f


print_many_per_line ()
{
  int i;
  int i1;
  int max;
  int tem;
  int pos;
  int perline;
  int nlines;
  int longcols;
  int start_shortcol;

  /* Compute the maximum file name length.  */
  max = 0;
  for (i = 0; i < files_index; i++)
    {
      tem = length_of_file_name_and_frills (files + i);
      if (tem > max) max = tem;
    }

  /* Allow at least two spaces between names.  */
  max += 2;

  perline = line_length / max;
  nlines = (files_index + perline - 1) / perline;
  longcols = files_index % perline;
  if (!longcols) longcols = perline;
  start_shortcol = longcols * nlines;

  for (i1 = 0; i1 < nlines; i1++)
    {
      i = i1;
      pos = 0;
      /* Print the next line of output.  */
      while (1)
	{
	  print_file_name_and_frills (files + i);
	  tem = length_of_file_name_and_frills (files + i);

	  if (i >= start_shortcol)
	    i--;
	  i += nlines;
	  if (i >= files_index)
	    break;
	  if (i1 == nlines - 1 && i >= start_shortcol)
	    break;

	  indent (pos + tem, pos + max);
	  pos += max;
	}
      putchar ('\n');
    }
}
\f


print_horizontal ()
{
  int i;
  int max;
  int tem;
  int perline;
  int pos;

  /* Compute the maximum file name length.  */
  max = 0;
  for (i = 0; i < files_index; i++)
    {
      tem = length_of_file_name_and_frills (files + i);
      if (tem > max) max = tem;
    }

  /* Allow two spaces between names.  */
  max += 2;

  perline = line_length / max;

  pos = 0;
  tem = 0;

  for (i = 0; i < files_index; i++)
    {
      if (i != 0)
	{
	  if (i % perline == 0)
	    {
	      putchar ('\n');
	      pos = 0;
	    }
	  else
	    {
	      indent (pos + tem, pos + max);
	      pos += max;
	    }
	}

      print_file_name_and_frills (files + i);

      tem = length_of_file_name_and_frills (files + i);
    }
  putchar ('\n');
}
\f


print_with_commas ()
{
  int i;
  int col, ocol;

  col = 0;

  for (i = 0; i < files_index; i++)
    {
      ocol = col;

      col += length_of_file_name_and_frills (files + i);
      if (i + 1 < files_index)
	col += 2;			/* For the comma and space */
      
      if (ocol != 0 && col > line_length)
	{
	  putchar ('\n');
	  col -= ocol;
	}

      print_file_name_and_frills (files + i);
      if (i + 1 < files_index)
	{
	  putchar (',');
	  putchar (' ');
	}
    }
  putchar ('\n');
}
\f


/* Translate userid to login name, with cache.  */

struct userid { int uid; char *name; struct userid *next; };

struct userid *user_alist;

char *
getuser (uid)
     int 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 == 0)
    {
      sprintf (usernum_string, "%d", uid);
      tail->name = copystring (usernum_string);
    }
  else
    tail->name = copystring (pwent->pw_name);
  user_alist = tail;
  return tail->name;
}

/* Translate gid to group name, with cache.  */

/* We use the same struct as for userids.  */
struct userid *group_alist;

char *
getgroup (uid)
     int uid;
{
  register struct userid *tail;
  struct group *grent;
  char usernum_string[20];

  for (tail = group_alist; tail; tail = tail->next)
    if (tail->uid == uid)
      return tail->name;

  grent = getgrgid (uid);
  tail = (struct userid *) xmalloc (sizeof (struct userid));
  tail->uid = uid;
  tail->next = user_alist;
  if (grent == 0)
    {
      sprintf (usernum_string, "%d", uid);
      tail->name = copystring (usernum_string);
    }
  else
    tail->name = copystring (grent->gr_name);
  group_alist = tail;
  return tail->name;
}

/* Assuming cursor is at position FROM, indent up to position TO.  */

indent (from, to)
     int from, to;
{
  while (from < to)
    {
      if ((to / 8) > (from / 8))
	{
	  putchar ('\t');
	  from += 8 - from % 8;
	}
      else
	{
	  putchar (' ');
	  from++;
	}
    }
}

/* Low level subroutines of general use,
   not specifically related to the task of listing a directory.  */

xrealloc (obj, size)
     int obj, size;
{
  int val = realloc (obj, size);

  if (!val)
    fatal ("memory exhausted", 0, 0);

  return val;
}

xmalloc (size)
     int size;
{
  int val = malloc (size);

  if (!val)
    fatal ("memory exhausted", 0, 0);

  return val;
}

fatal (string, arg, arg2)
     char *string;
     int arg, arg2;
{
  error (string, arg, arg2);
  exit (1);
}

error (string, arg, arg2)
     char *string;
     int arg, arg2;
{
  fprintf (stderr, "ls: ");
  fprintf (stderr, string, arg, arg2);
  fprintf (stderr, "\n");
}


perror_with_name (name)
     char *name;
{
  int err = errno;

  if (err <= sys_nerr)
    error ("%s: %s", name, sys_errlist[err]);
  else
    error ("%s: %s", name, "unknown system error");
}

char *
copystring (string)
     char *string;
{
  int len = strlen (string);
  char *new = (char *) xmalloc (len + 1);

  strcpy (new, string);
  new[len] = 0;
  return new;
}

/* Emacs C-mode formatting variables:
   Local Variables:
   c-argdecl-indent: 5
   c-brace-imaginary-offset: 0
   c-brace-offset: 0
   c-continued-brace-offset: 0
   c-continued-statement-offset: 2
   c-indent-level: 2
   c-label-offset: -2
   End:
 */