Logo Search packages:      
Sourcecode: hfsutils version File versions  Download package

hls.c

/*
 * hfsutils - tools for reading and writing Macintosh HFS volumes
 * Copyright (C) 1996-1998 Robert Leslie
 *
 * 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 2 of the License, 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.
 *
 * $Id: hls.c,v 1.8 1998/09/28 23:21:50 rob Exp $
 */

# ifdef HAVE_CONFIG_H
#  include "config.h"
# endif

# ifdef HAVE_UNISTD_H
#  include <unistd.h>
# endif

# ifdef HAVE_TERMIOS_H
#  include <termios.h>
# endif

# ifdef HAVE_SYS_IOCTL_H
#  include <sys/ioctl.h>
# else
int ioctl(int, int, ...);
# endif

# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <time.h>
# include <ctype.h>
# include <errno.h>

# include "hfs.h"
# include "hcwd.h"
# include "hfsutil.h"
# include "darray.h"
# include "dlist.h"
# include "dstring.h"
# include "hls.h"

# define HLS_ALL_FILES        0x0001
# define HLS_ESCAPE           0x0002
# define HLS_QUOTE            0x0004
# define HLS_QMARK_CTRL       0x0008
# define HLS_IMMEDIATE_DIRS   0x0010
# define HLS_CATIDS           0x0020
# define HLS_REVERSE          0x0040
# define HLS_SIZE       0x0080
# define HLS_INDICATOR        0x0100
# define HLS_RECURSIVE        0x0200
# define HLS_NAME       0x0400
# define HLS_SPACE            0x0800

# define F_MASK               0x0007
# define F_LONG               0x0000
# define F_ONE                0x0001
# define F_MANY               0x0002
# define F_HORIZ        0x0003
# define F_COMMAS       0x0004

# define T_MASK               0x0008
# define T_MOD                0x0000
# define T_CREATE       0x0008

# define S_MASK               0x0030
# define S_NAME               0x0000
# define S_TIME               0x0010
# define S_SIZE               0x0020

# define PATH(ent)      ((ent).path ? (ent).path : (ent).dirent.name)

typedef struct _queueent_ {
  char *path;
  hfsdirent dirent;
  void (*free)(struct _queueent_ *);
} queueent;

extern char *optarg;
extern int optind;

/*
 * NAME:    usage()
 * DESCRIPTION:   display usage message
 */
static
int usage(void)
{
  fprintf(stderr, "Usage: %s [options] [hfs-path ...]\n", argv0);

  return 1;
}

/*
 * NAME:    dpfree()
 * DESCRIPTION:   free a queue entry containing dynamically-allocated data
 */
static
void dpfree(queueent *ent)
{
  free(ent->path);
}

/*
 * NAME:    qnew()
 * DESCRIPTION:   create a new queue array
 */
static
darray *qnew(void)
{
  return darr_new(sizeof(queueent));
}

/*
 * NAME:    qfree()
 * DESCRIPTION:   free a queue array
 */
static
void qfree(darray *array)
{
  int i, sz;
  queueent *ents;

  sz   = darr_size(array);
  ents = darr_array(array);

  for (i = 0; i < sz; ++i)
    if (ents[i].free)
      ents[i].free(&ents[i]);

  darr_free(array);
}

static
int reverse;

/*
 * NAME:    compare_names()
 * DESCRIPTION:   lexicographically compare two filenames
 */
static
int compare_names(const queueent *ent1, const queueent *ent2)
{
  return reverse * strcasecmp(PATH(*ent1), PATH(*ent2));
}

/*
 * NAME:    compare_mtimes()
 * DESCRIPTION:   chronologically compare two modification dates
 */
static
int compare_mtimes(const queueent *ent1, const queueent *ent2)
{
  return reverse * (ent2->dirent.mddate - ent1->dirent.mddate);
}

/*
 * NAME:    compare_ctimes()
 * DESCRIPTION:   chronologically compare two creation dates
 */
static
int compare_ctimes(const queueent *ent1, const queueent *ent2)
{
  return reverse * (ent2->dirent.crdate - ent1->dirent.crdate);
}

/*
 * NAME:    compare_sizes()
 * DESCRIPTION:   compare two file sizes
 */
static
int compare_sizes(const queueent *ent1, const queueent *ent2)
{
  return reverse *
    ((ent2->dirent.u.file.dsize + ent2->dirent.u.file.rsize) -
     (ent1->dirent.u.file.dsize + ent1->dirent.u.file.rsize));
}

/*
 * NAME:    sortfiles()
 * DESCRIPTION:   arrange files in order according to sort selection
 */
static
void sortfiles(darray *files, int flags, int options)
{
  int (*compare)(const queueent *, const queueent *);

  switch (options & S_MASK)
    {
    case S_NAME:
      compare = compare_names;
      break;

    case S_TIME:
      switch (options & T_MASK)
      {
      case T_MOD:
        compare = compare_mtimes;
        break;

      case T_CREATE:
        compare = compare_ctimes;
        break;

      default:
        abort();
      }
      break;

    case S_SIZE:
      compare = compare_sizes;
      break;

    default:
      return;
    }

  reverse = (flags & HLS_REVERSE) ? -1 : 1;

  darr_sort(files, (int (*)(const void *, const void *)) compare);
}

/*
 * NAME:    outpath()
 * DESCRIPTION:   modulate an output string given current flags
 */
static
int outpath(dstring *str, queueent *ent, int flags)
{
  const char *path;

  path = PATH(*ent);

  dstr_shrink(str, 0);

  if ((flags & HLS_QUOTE) &&
      dstr_append(str, "\"", 1) == -1)
    return -1;

  if (flags & (HLS_ESCAPE | HLS_QUOTE | HLS_QMARK_CTRL))
    {
      const char *ptr;

      for (ptr = path; *ptr; ++ptr)
      {
        const char *add;
        char buf[5];

        if (flags & HLS_ESCAPE)
          {
            switch (*ptr)
            {
            case '\\':
              add = "\\\\";
              break;

            case '\n':
              add = "\\n";
              break;

            case '\b':
              add = "\\b";
              break;

            case '\r':
              add = "\\r";
              break;

            case '\t':
              add = "\\t";
              break;

            case '\f':
              add = "\\f";
              break;

            case ' ':
              add = "\\ ";
              break;

            case '\"':
              add = "\\\"";
              break;

            default:
              if (isgraph(*ptr))
                {
                  sprintf(buf, "%c", *ptr);
                  add = buf;
                }
              else
                {
                  sprintf(buf, "\\%03o", (unsigned char) *ptr);
                  add = buf;
                }
            }
          }
        else  /* ! (flags & HLS_ESCAPE) */
          {
            if (isprint(*ptr) || ! (flags & HLS_QMARK_CTRL))
            {
              sprintf(buf, "%c", *ptr);
              add = buf;
            }
            else
            {
              sprintf(buf, "?");
              add = buf;
            }
          }

        if (dstr_append(str, add, -1) == -1)
          return -1;
      }
    }
  else
    {
      if (dstr_append(str, path, -1) == -1)
      return -1;
    }

  if ((flags & HLS_QUOTE) &&
      dstr_append(str, "\"", 1) == -1)
    return -1;

  if (flags & HLS_INDICATOR)
    {
      char c = 0;

      if (ent->dirent.flags & HFS_ISDIR)
      c = ':';
      else if (strcmp(ent->dirent.u.file.type, "APPL") == 0)
      c = '*';

      if (c && dstr_append(str, &c, 1) == -1)
      return -1;
    }

  return 0;
}

/*
 * NAME:    misclen()
 * DESCRIPTION:   string length of miscellaneous section
 */
static
int misclen(int flags)
{
  return ((flags & HLS_CATIDS) ? 8 : 0) +
         ((flags & HLS_SIZE)   ? 5 : 0);
}

/*
 * NAME:    showmisc()
 * DESCRIPTION:   output miscellaneous numbers
 */
static
void showmisc(hfsdirent *ent, int flags)
{
  unsigned long size;

  size = ent->u.file.rsize + ent->u.file.dsize;

  if (flags & HLS_CATIDS)
    printf("%7lu ", ent->cnid);
  if (flags & HLS_SIZE)
    printf("%4lu ", size / 1024 + (size % 1024 != 0));
}

/*
 * NAME:    show_long()
 * DESCRIPTION:   output a list of files in long format
 */
static
void show_long(int sz, queueent *ents, char **strs,
             int flags, int options, int width)
{
  int i;
  time_t now;

  now = time(0);

  for (i = 0; i < sz; ++i)
    {
      hfsdirent *ent;
      time_t when;
      char timebuf[26];

      ent = &ents[i].dirent;

      switch (options & T_MASK)
      {
      case T_MOD:
        when = ent->mddate;
        break;

      case T_CREATE:
        when = ent->crdate;
        break;

      default:
        abort();
      }

      strcpy(timebuf, ctime(&when));

      if (now > when + 6L * 30L * 24L * 60L * 60L ||
        now < when - 60L * 60L)
      strcpy(timebuf + 11, timebuf + 19);

      timebuf[16] = 0;

      showmisc(ent, flags);

      if (ent->flags & HFS_ISDIR)
      printf("d%c %9u item%c               %s %s\n",
             ent->fdflags & HFS_FNDR_ISINVISIBLE ? 'i' : ' ',
             ent->u.dir.valence, ent->u.dir.valence == 1 ? ' ' : 's',
             timebuf + 4, strs[i]);
      else
      printf("%c%c %4s/%4s %9lu %9lu %s %s\n",
             ent->flags & HFS_ISLOCKED ? 'F' : 'f',
             ent->fdflags & HFS_FNDR_ISINVISIBLE ? 'i' : ' ',
             ent->u.file.type, ent->u.file.creator,
             ent->u.file.rsize, ent->u.file.dsize,
             timebuf + 4, strs[i]);
    }
}

/*
 * NAME:    show_one()
 * DESCRIPTION:   output a list of files in single-column format
 */
static
void show_one(int sz, queueent *ents, char **strs,
            int flags, int options, int width)
{
  int i;

  for (i = 0; i < sz; ++i)
    {
      showmisc(&ents[i].dirent, flags);
      printf("%s\n", strs[i]);
    }
}

/*
 * NAME:    show_many()
 * DESCRIPTION:   output a list of files in vertical-column format
 */
static
void show_many(int sz, queueent *ents, char **strs,
             int flags, int options, int width)
{
  int i, len, misc, maxlen = 0, rows, cols, row;

  misc = misclen(flags);

  for (i = 0; i < sz; ++i)
    {
      len = strlen(strs[i]) + misc;
      if (len > maxlen)
      maxlen = len;
    }

  maxlen += 2;

  cols = width / maxlen;
  if (cols == 0)
    cols = 1;

  rows = sz / cols + (sz % cols != 0);

  for (row = 0; row < rows; ++row)
    {
      i = row;

      while (1)
      {
        showmisc(&ents[i].dirent, flags);
        printf("%s", strs[i]);

        i += rows;
        if (i >= sz)
          break;

        for (len = strlen(strs[i - rows]) + misc;
             len < maxlen; ++len)
          putchar(' ');
      }

      putchar('\n');
    }
}

/*
 * NAME:    show_horiz()
 * DESCRIPTION:   output a list of files in horizontal-column format
 */
static
void show_horiz(int sz, queueent *ents, char **strs,
            int flags, int options, int width)
{
  int i, len, misc, maxlen = 0, cols;

  misc = misclen(flags);

  for (i = 0; i < sz; ++i)
    {
      len = strlen(strs[i]) + misc;
      if (len > maxlen)
      maxlen = len;
    }

  maxlen += 2;

  cols = width / maxlen;
  if (cols == 0)
    cols = 1;

  for (i = 0; i < sz; ++i)
    {
      if (i)
      {
        if (i % cols == 0)
          putchar('\n');
        else
          {
            for (len = strlen(strs[i - 1]) + misc;
               len < maxlen; ++len)
            putchar(' ');
          }
      }

      showmisc(&ents[i].dirent, flags);
      printf("%s", strs[i]);
    }

  if (i)
    putchar('\n');
}

/*
 * NAME:    show_commas()
 * DESCRIPTION:   output a list of files in comma-delimited format
 */
static
void show_commas(int sz, queueent *ents, char **strs,
             int flags, int options, int width)
{
  int i, pos = 0;

  for (i = 0; i < sz; ++i)
    {
      hfsdirent *ent;
      int len;

      ent = &ents[i].dirent;
      len = strlen(strs[i]) + misclen(flags) + ((i < sz - 1) ? 2 : 0);

      if (pos && pos + len >= width)
      {
        putchar('\n');
        pos = 0;
      }

      showmisc(ent, flags);
      printf("%s", strs[i]);

      if (i < sz - 1)
      {
        putchar(',');
        putchar(' ');
      }

      pos += len;
    }

  if (pos)
    putchar('\n');
}

/*
 * NAME:    showfiles()
 * DESCRIPTION:   display a set of files
 */
static
int showfiles(darray *files, int flags, int options, int width)
{
  dlist list;
  int i, sz, result = 0;
  queueent *ents;
  dstring str;
  char **strs;
  void (*show)(int, queueent *, char **, int, int, int);

  if (dl_init(&list) == -1)
    {
      fprintf(stderr, "%s: not enough memory\n", argv0);
      return -1;
    }

  sz   = darr_size(files);
  ents = darr_array(files);

  dstr_init(&str);

  for (i = 0; i < sz; ++i)
    {
      if (outpath(&str, &ents[i], flags) == -1 ||
        dl_append(&list, dstr_string(&str)) == -1)
      {
        result = -1;
        break;
      }
    }

  dstr_free(&str);

  strs = dl_array(&list);

  switch (options & F_MASK)
    {
    case F_LONG:
      show = show_long;
      break;

    case F_ONE:
      show = show_one;
      break;

    case F_MANY:
      show = show_many;
      break;

    case F_HORIZ:
      show = show_horiz;
      break;

    case F_COMMAS:
      show = show_commas;
      break;

    default:
      abort();
    }

  show(sz, ents, strs, flags, options, width);

  dl_free(&list);

  return result;
}

/*
 * NAME:    process()
 * DESCRIPTION:   sort and display results
 */
static
int process(hfsvol *vol, darray *dirs, darray *files,
          int flags, int options, int width)
{
  int i, dsz, fsz;
  queueent *ents;
  int result = 0;

  dsz = darr_size(dirs);
  fsz = darr_size(files);

  if (fsz)
    {
      sortfiles(files, flags, options);
      if (showfiles(files, flags, options, width) == -1)
      result = -1;

      flags |= HLS_NAME | HLS_SPACE;
    }
  else if (dsz > 1)
    flags |= HLS_NAME;

  ents = darr_array(dirs);

  for (i = 0; i < dsz; ++i)
    {
      const char *path;
      hfsdir *dir;
      queueent ent;

      darr_shrink(files, 0);

      path = PATH(ents[i]);
      dir  = hfs_opendir(vol, path);
      if (dir == 0)
      {
        hfsutil_perrorp(path);
        result = -1;
        continue;
      }

      while (hfs_readdir(dir, &ent.dirent) != -1)
      {
        if ((ent.dirent.fdflags & HFS_FNDR_ISINVISIBLE) &&
            ! (flags & HLS_ALL_FILES))
          continue;

        ent.path = 0;
        ent.free = 0;

        if (darr_append(files, &ent) == 0)
          {
            fprintf(stderr, "%s: not enough memory\n", argv0);
            result = -1;
            break;
          }

        if ((ent.dirent.flags & HFS_ISDIR) && (flags & HLS_RECURSIVE))
          {
            dstring str;

            dstr_init(&str);

            if (strchr(path, ':') == 0 &&
              dstr_append(&str, ":", 1) == -1)
            result = -1;

            if (dstr_append(&str, path, -1) == -1)
            result = -1;

            if (path[strlen(path) - 1] != ':' &&
              dstr_append(&str, ":", 1) == -1)
            result = -1;

            if (dstr_append(&str, ent.dirent.name, -1) == -1)
            result = -1;

            ent.path = strdup(dstr_string(&str));
            if (ent.path)
            ent.free = dpfree;
            else
            result = -1;

            dstr_free(&str);

            if (darr_append(dirs, &ent) == 0)
            {
              result = -1;
              if (ent.path)
                free(ent.path);
            }

            if (result)
            {
              fprintf(stderr, "%s: not enough memory\n", argv0);
              break;
            }

            dsz  = darr_size(dirs);
            ents = darr_array(dirs);
          }
      }

      hfs_closedir(dir);

      if (result)
      break;

      if (flags & HLS_SPACE)
      printf("\n");
      if (flags & HLS_NAME)
      printf("%s%s", path,
             path[strlen(path) - 1] == ':' ? "\n" : ":\n");

      sortfiles(files, flags, options);
      if (showfiles(files, flags, options, width) == -1)
      result = -1;

      flags |= HLS_NAME | HLS_SPACE;
    }

  return result;
}

/*
 * NAME:    queuepath()
 * DESCRIPTION:   append a file or directory to the list to process
 */
static
int queuepath(hfsvol *vol, char *path, darray *dirs, darray *files, int flags)
{
  queueent ent;
  darray *array;

  if (hfs_stat(vol, path, &ent.dirent) == -1)
    {
      hfsutil_perrorp(path);
      return (errno == ENOENT) ? 0 : -1;
    }

  ent.path = path;
  ent.free = 0;

  array = ((ent.dirent.flags & HFS_ISDIR) &&
         ! (flags & HLS_IMMEDIATE_DIRS)) ? dirs : files;

  if (darr_append(array, &ent) == 0)
    {
      fprintf(stderr, "%s: not enough memory\n", argv0);
      return -1;
    }

  return 0;
}

/*
 * NAME:    hls->main()
 * DESCRIPTION:   implement hls command
 */
int hls_main(int argc, char *argv[])
{
  hfsvol *vol;
  int fargc, i;
  char **fargv = 0;
  int result = 0;
  int flags, options, width;
  char *ptr;
  darray *dirs, *files;

  options = T_MOD | S_NAME;

  if (isatty(STDOUT_FILENO))
    {
      options |= F_MANY;
      flags    = HLS_QMARK_CTRL;
    }
  else
    {
      options |= F_ONE;
      flags    = 0;
    }

  if (strcmp(bargv0, "hdir") == 0)
    options = (options & ~F_MASK) | F_LONG;

  ptr   = getenv("COLUMNS");
  width = ptr ? atoi(ptr) : 80;

# ifdef TIOCGWINSZ
  {
    struct winsize ws;

    if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 &&
      ws.ws_col != 0)
      width = ws.ws_col;
  }
# endif

  while (1)
    {
      int opt;

      opt = getopt(argc, argv, "1abcdfilmqrstxw:CFNQRSU");
      if (opt == EOF)
      break;

      switch (opt)
      {
      case '?':
        return usage();

      case '1':
        options = (options & ~F_MASK) | F_ONE;
        break;

      case 'a':
        flags |= HLS_ALL_FILES;
        break;

      case 'b':
        flags |= HLS_ESCAPE;
        flags &= ~HLS_QMARK_CTRL;
        break;

      case 'c':
        options = (options & ~(T_MASK | S_MASK)) | T_CREATE | S_TIME;
        break;

      case 'd':
        flags |= HLS_IMMEDIATE_DIRS;
        break;

      case 'f':
        flags |= HLS_ALL_FILES;
        flags &= ~HLS_SIZE;
        options &= ~S_MASK;
        if ((options & F_MASK) == F_LONG)
          options = (options & ~F_MASK) |
            (isatty(STDOUT_FILENO) ? F_MANY : F_ONE);
        break;

      case 'i':
        flags |= HLS_CATIDS;
        break;

      case 'l':
        options = (options & ~F_MASK) | F_LONG;
        break;

      case 'm':
        options = (options & ~F_MASK) | F_COMMAS;
        break;

      case 'q':
        flags |= HLS_QMARK_CTRL;
        flags &= ~HLS_ESCAPE;
        break;

      case 'r':
        flags |= HLS_REVERSE;
        break;

      case 's':
        flags |= HLS_SIZE;
        break;

      case 't':
        options = (options & ~S_MASK) | S_TIME;
        break;

      case 'x':
        options = (options & ~F_MASK) | F_HORIZ;
        break;

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

      case 'C':
        options = (options & ~F_MASK) | F_MANY;
        break;

      case 'F':
        flags |= HLS_INDICATOR;
        break;

      case 'N':
        flags &= ~(HLS_ESCAPE | HLS_QMARK_CTRL);
        break;

      case 'Q':
        flags |= HLS_QUOTE | HLS_ESCAPE;
        flags &= ~HLS_QMARK_CTRL;
        break;

      case 'R':
        flags |= HLS_RECURSIVE;
        break;

      case 'S':
        options = (options & ~S_MASK) | S_SIZE;
        break;

      case 'U':
        options &= ~S_MASK;
        break;
      }
    }

  vol = hfsutil_remount(hcwd_getvol(-1), HFS_MODE_RDONLY);
  if (vol == 0)
    return 1;

  fargv = hfsutil_glob(vol, argc - optind, &argv[optind], &fargc, &result);

  dirs  = qnew();
  files = qnew();
  if (result == 0 && (dirs == 0 || files == 0))
    {
      fprintf(stderr, "%s: not enough memory\n", argv0);
      result = 1;
    }

  if (result == 0)
    {
      if (fargc == 0)
      {
        if (queuepath(vol, ":", dirs, files, flags) == -1)
          result = 1;
      }
      else
      {
        for (i = 0; i < fargc; ++i)
          {
            if (queuepath(vol, fargv[i], dirs, files, flags) == -1)
            {
              result = 1;
              break;
            }
          }
      }
    }

  if (result == 0 && process(vol, dirs, files, flags, options, width) == -1)
    result = 1;

  if (files)
    qfree(files);
  if (dirs)
    qfree(dirs);

  hfsutil_unmount(vol, &result);

  if (fargv)
    free(fargv);

  return result;
}

Generated by  Doxygen 1.6.0   Back to index