RCS_ID_C "$Id: list.c,v 1.11 93/09/17 07:54:01 ppessi Exp $";
/*
 * list.c - build file list 
 *
 * Author: ppessi <Pekka.Pessi@hut.fi>
 *
 * Copyright (c) 1992, 1993 Pekka Pessi
 *
 * Last modified: Fri Sep 17 07:15:11 1993 ppessi
 *
 */

#include "ls.h"
#include <dos/dosasl.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>

static struct ExAllList *
balloc(struct ExAllList * list)
{
  struct ExAllList * car = malloc(EXLISTSIZE);
  if (car) {
    car -> El_Next = list;
  } 
#ifndef NOMYDEBUG 
  FPrintf(Stderr, "Balloc: %p -> %p\n", car, list);
#endif
  return car;
}

void
bfree(volatile struct ExAllList * list)
{
  while (list) {
    volatile struct ExAllList * car = list;
    list = car->El_Next;
    free((struct ExAllList *)car);
  }
}

/* Filter to exlude .#? and icons (but Emacs info files, too) */
static UBYTE filter_normal[] = 
{ P_NOT, P_ORSTART, P_ANY, '.', 'I', 'N', 'F', 'O', 
         P_ORNEXT,  '.', P_ANY, P_OREND, P_NOTEND, '\0' }; 
/* Filter to exclude icons */
static UBYTE filter_dotted[] = 
{ P_NOT, P_ORSTART, P_ANY, '.', 'I', 'N', 'F', 'O', P_OREND, P_NOTEND, '\0' }; 

/*
 * Fill in an emulated ExAllData structure 
 */
static struct ExAllData *
fill_ead(struct ExAllData *next, struct FileInfoBlock *fib, 
	UBYTE *name, UWORD namelen)
{
  next->ed_Next = NULL;
  next->ed_Name = (UBYTE *)(next+1);
  next->ed_Type = fib->fib_DirEntryType;
  next->ed_Size = fib->fib_Size;
  next->ed_Prot = fib->fib_Protection;
  next->ed_Days = fib->fib_Date.ds_Days;
  next->ed_Mins = fib->fib_Date.ds_Minute;
  next->ed_Ticks = fib->fib_Date.ds_Tick;
  next->ed_Comment = NULL;
#ifdef FIBB_OTR_READ /* DOSVERSION >= 39 */
#define FIB_OWNERUID(fib)  (fib->fib_OwnerUID)
#define FIB_OWNERGID(fib)  (fib->fib_OwnerGID)
#else
#define FIB_OWNERUID(fib) ((WORD *)(fib->fib_Reserved))[0]
#define FIB_OWNERGID(fib) ((WORD *)(fib->fib_Reserved))[1]
#endif
  next->ed_OwnerUID = FIB_OWNERUID(fib); /* V39 */
  next->ed_OwnerGID = FIB_OWNERGID(fib); /* V39 */
  /* NONSTANDARD */
  next->ed_Key = fib->fib_DiskKey; 
  next->ed_Blocks = fib->fib_NumBlocks; 

  strcpy(next->ed_Name, name);
  next = (struct ExAllData *)(next->ed_Name + namelen + 1);
  /* pad to next long word */
  return (struct ExAllData *)
    ((UBYTE *)next + ((4 - (3 & (unsigned int)next)) & 3));
}

LONG 
myExAll(BPTR lock, struct ExAllData *buffer, long size, long data,
	   struct ExAllControl *control)
{
  struct FileInfoBlock *fib;
  struct ExAllData *prev = NULL;

  control->eac_Entries = 0;

  if (data <= ED_COMMENT) 
    return ExAll(lock, buffer, size, data, control);
  
  if (!control->eac_LastKey) {
    fib = AllocDosObject(DOS_FIB, (struct TagItem *)NULL);
    control->eac_LastKey = (ULONG)fib;
    if (!Examine(lock, fib) || !ExNext(lock, fib)) {
      control->eac_LastKey = NULL;
      FreeDosObject(DOS_FIB, fib); /* does not touch IoErr */
      return DOSFALSE;
    }
  } else {
    fib = (struct FileInfoBlock *)control->eac_LastKey;
  }

  do {
    LONG nlen;
    if (control->eac_MatchString &&
	!MatchPatternNoCase(control->eac_MatchString, fib->fib_FileName)) {
      if (IoErr() == 0) continue;
    }
    nlen = strlen(fib->fib_FileName);
    if (nlen + sizeof(*buffer) > size) return DOSTRUE;
    if (prev)
      prev->ed_Next = buffer;
    control->eac_Entries++;
    buffer = fill_ead(prev = buffer, fib, fib->fib_FileName, nlen);
    size -= (UBYTE *)buffer - (UBYTE *)prev;
  } while (ExNext(lock, fib));

  control->eac_LastKey = NULL;
  FreeDosObject(DOS_FIB, fib);
  SetIoErr(ERROR_NO_MORE_ENTRIES);
  return DOSFALSE;
}

/*
 * listdir builds a ExAllList from directory name
 */
struct ExAllList *
listdir(char *name, struct options options)
{
  struct ExAllList *newbuffer, *buffers;
  struct ExAllControl *cntrl;
  BPTR mylock;
  int entries = 0;

  long data = 
    options.longformat ? ED_OWNER : 
      (options.inode || options.kilos ? ED_BLK : ED_DATE);

  newbuffer = buffers = balloc(NULL);
  if (!buffers)
    return NULL;

  cntrl = (struct ExAllControl *) 
    AllocDosObject(DOS_EXALLCONTROL, (struct TagItem *)NULL);
  if (!cntrl) {
    bfree(buffers);
    return NULL;
  }

  cntrl -> eac_LastKey = 0;
  cntrl -> eac_MatchString = 
    (options.all ? 
     NULL : (options.dotted ? 
	     filter_dotted : filter_normal));
  cntrl -> eac_MatchFunc = NULL;

  /* read whole directory */
  if (mylock = Lock((UBYTE *)name, SHARED_LOCK)) {
    LONG again;

    do {
      again = myExAll(mylock, &(newbuffer->El_Data), EXDATASIZE, data, cntrl);
      entries += cntrl->eac_Entries; 
      if (again && cntrl->eac_Entries > 0 && 
	  buffers && (buffers = balloc(newbuffer))) {
	newbuffer = buffers;
      } 
      /* We loop until myExAll tells to stop */
    } while (again);
    if (IoErr() != ERROR_NO_MORE_ENTRIES)
      buffers = NULL;
    UnLock(mylock);
  } else {
    LONG code = IoErr();
    if (code) PrintFault(code, name);
  }

  if (buffers) {
    buffers->El_Number = entries;
  } else {
    bfree(newbuffer);
  }

  FreeDosObject(DOS_EXALLCONTROL, cntrl);
  return buffers;
}

/*
 * listpatterns builds an ExAllList from all command line arguments
 */
struct ExAllList *
  listpatterns(int patc, char **patv, struct options options)
{
  struct ExAllList *fbuffers = NULL;
  struct ExAllData *prev, *next;
  long entries = 0, left = 0, code;
  UBYTE *pat;
  struct AnchorPath *anchor = malloc(sizeof(struct AnchorPath) + MAXPATHLEN);
  
  if (!anchor) exit(RETURN_FAIL);

  anchor->ap_BreakBits = NULL;
  anchor->ap_Flags = APF_DODOT;
  anchor->ap_Strlen = MAXPATHLEN;
  
  for (; patc > 0; patv++, patc--) {
    pat = *patv;
    if (*pat == '.' && (*++pat == '.' || *pat == '/')) ++pat;
    if (code = MatchFirst(pat, anchor)) {
      PrintFault(code, *patv);
      continue;
    } else do {
      int namelen = strlen(anchor->ap_Buf);
#ifndef NOMYDEBUG 
      FPrintf(Stderr, "Adding file %s at %p\n", anchor->ap_Buf, next);
#endif
      if (sizeof(struct ExAllData) + namelen > left) {
	/* Allocate new buffer for entry */
	if (!(fbuffers = balloc(fbuffers))) {
	  MatchEnd(anchor);
	  exit(RETURN_FAIL);
	}
	left = EXDATASIZE; 
	next = &(fbuffers->El_Data);
      } else {
	prev->ed_Next = next;	/* Initialized by the if clause! */
      }
      next = fill_ead(prev = next, &(anchor->ap_Info), anchor->ap_Buf, namelen);
      left -= (UBYTE *) next - (UBYTE *) prev;
      fbuffers->El_Number = ++entries;
#ifndef NOMYDEBUG 
      FPrintf(Stderr, "Next at %p\n", next);
#endif
    } while (!MatchNext(anchor));
    MatchEnd(anchor);
  }
  free(anchor);

  if (entries == 0) {
    bfree(fbuffers);
    fbuffers = NULL;
  }

  return fbuffers;
}
