MudOSa4DGD/
MudOSa4DGD/bin/
MudOSa4DGD/data/
MudOSa4DGD/doc/
MudOSa4DGD/doc/driver/
MudOSa4DGD/doc/efun/bitstrings/
MudOSa4DGD/doc/efun/command/
MudOSa4DGD/doc/efun/communication/
MudOSa4DGD/doc/efun/heart_beat/
MudOSa4DGD/doc/efun/interactive/
MudOSa4DGD/doc/efun/inventory/
MudOSa4DGD/doc/efun/living/
MudOSa4DGD/doc/efun/mappings/
MudOSa4DGD/doc/efun/strings/
MudOSa4DGD/doc/efun/uid/
MudOSa4DGD/doc/funs/
MudOSa4DGD/doc/language/
MudOSa4DGD/mudlib/dgd/doc/
MudOSa4DGD/mudlib/dgd/lib/include/dgd/
MudOSa4DGD/mudlib/dgd/lib/std/
MudOSa4DGD/mudlib/dgd/lib/sys/
MudOSa4DGD/mudlib/dgd/log/
MudOSa4DGD/mudlib/log/
MudOSa4DGD/mudlib/std/include/
MudOSa4DGD/mudlib/std/obj/
/*
 * file.c
 *
 * File thingy-bobedidabobobudidis
 *
 * (C) Frank Schmidt, Jesus@NorseMUD
 *
 */


/* check master for read/write permission */
#ifdef MUDOS_USER_ID
# define MASTER_READ(file) \
  call_other(master(), __VALID_READ_FUNC, file, geteuid(), previous_function() )
# define MASTER_WRITE(file) \
  call_other(master(), __VALID_WRITE_FUNC, file, geteuid(), previous_function() )
#else
# define MASTER_READ(file) \
  call_other(master(), __VALID_READ_FUNC, file, this_object(), previous_function() )
# define MASTER_WRITE(file) \
  call_other(master(), __VALID_WRITE_FUNC, file, this_object(), previous_function() )
#endif


/* get the size of a file */
static int file_size(string file) {
  int *sizes;
  if (!MASTER_READ(file)) return -1;
  sizes = ::get_dir(file)[GDIR_SIZES];
  if (::sizeof(sizes) != 1) return -1;
  return sizes[0];
}


/* return filenames found by <path> (with wildcards etc) */
static string *get_files(string path) {
  if (!MASTER_READ(path)) return ({ });
  return ::get_dir(path)[GDIR_NAMES];
}


#ifdef MUDOS_GET_DIR_BEHAVIOUR

/* get directory info (not very good simulated) */
static varargs mixed *get_dir(string file, int flag) {
  int len;
  if (!MASTER_READ(file)) return ({ });
  len = ::strlen(file);
  if (file == "" || file[len - 1] == '/')
    file += "*";
  else if (len > 1 && file[len - 2 ..] == "/.")
    file[len - 1] = '*';
  if (flag != -1)
    return ::get_dir(file)[GDIR_NAMES];
  else
    return ::get_dir(file);
}

#else

/* get directory info */
static mixed *get_dir(string file) {
  if (!MASTER_READ(file)) return ({ ({}), ({}), ({}) });
  return ::get_dir(file);
}

#endif


/* make a directory */
static int make_dir(string file) {
  if (!MASTER_WRITE(file)) return -1;
  return ::make_dir(file);
}


/* read bytes from a file */
static varargs string read_bytes(string file, int start, int size) {
  if (!MASTER_READ(file)) return 0;
  return ::read_file(file, start, size);
}


/* return a line range of a file */
private static string *read_lines(string file, int first, int num) {
  int i, offset, size;
  string buf, res, *result;

  /* bad numbers? */
  if (first < 0 || num <= 0) return 0;

  /* scan to beginning */
  size = file_size(file);
  while (first > 0 && size > 0) {
    int bytes;
    buf = ::read_file(file, offset, bytes=min(size, FILE_CHUNK));
    size -= bytes;
    offset += bytes;
    /* loop through carriage returns */
    while ((i=strsrch(buf, "\n")) != -1) {
      buf = buf[i+1..];
      if (--first <= 0)
	/* found beginning */
	break;
    }
  }

  /* read lines till end */
  result = ({ });
  res = "";
  while (num > 0 && size > 0) {
    int bytes;
    if (!buf) {
      buf = ::read_file(file, offset, bytes=min(size, FILE_CHUNK));
      size -= bytes;
      offset += bytes;
    }
    /* loop through carriage returns */
    while ((i=strsrch(buf, "\n")) != -1) {
      res += buf[..i-1];
      result += ({ res });
      res = "";
      buf = buf[i+1..];
      if (--num <= 0)
	/* found end */
	return result;
    }
    res += buf;
    /* read new chunk from file */
    buf = 0;
  }
#if 0
  /* reached EOF */
  if (num > 1)
    /* still got some lines left */
    return 0;
#endif
  /* got correct result */
  return result + ({ res });
}


/* read a file */
static varargs string read_file(string file, int first, int num) {
  string *lines;
  if (!MASTER_READ(file)) return 0;
  if (!first && !num)
    return ::read_file(file);
  if (!(lines = read_lines(file, first, num)))
    return 0;
  return implode(lines, "\n");
}


/* remove a file */
static int remove_file(string file) {
  if (!MASTER_WRITE(file)) return 0;
  return ::remove_file(file);
}


/* remove a directory */
static int remove_dir(string file) {
  if (!MASTER_WRITE(file)) return 0;
  return ::remove_dir(file);
}


/* rename a file */
static int rename_file(string from, string to) {
  if (!MASTER_WRITE(from)) return 0;
  if (!MASTER_WRITE(to)) return 0;
  ::remove_file(to); /* overwrite old file */
  return ::rename_file(from, to);
}


/* copy a file of any size */
static int copy_file(string from, string to) {
  int size, offset;
  mixed *sizes;
  string buf;
  if (!MASTER_READ(from)) return 0;
  if (!MASTER_WRITE(to)) return 0;

  /* get from filesize */
  sizes = ::get_dir(from)[GDIR_SIZES];
  if (::sizeof(sizes) != 1) return 0;
  if ((size=sizes[0]) < 0) return 0;

  ::remove_file(to); /* overwrite old file */
  if (size == 0) return ::write_file(to, "");
  for (; offset < size; offset+=COPY_FILE_CHUNK) {
    if ((buf=::read_file(from, offset, COPY_FILE_CHUNK))) {
      if (!::write_file(to, buf))
	return 0;
    }
    else
      return 0;
  }
  return 1;
}


/* restore an object */
#ifdef MUDOS_RESTORE_OBJECT_FLAG
static varargs int restore_object(string file, int dummy) {
#else
static int restore_object(string file) {
#endif
  if (!MASTER_READ(file)) return 0;
#ifdef DEFAULT_OBJ_EXTENSION
  {
    string s1, s2;
    if (sscanf(file, "%s.%s",s1,s2) != 2)
      /* add default extension */
      file += DEFAULT_OBJ_EXTENSION;
  }
#endif
  return ::restore_object(file);
}


/* save an object */
#ifdef MUDOS_SAVE_OBJECT_FLAG
static varargs int save_object(string file, int dummy) {
#else
static int save_object(string file) {
#endif
  if (!MASTER_WRITE(file)) return 0;
#ifdef DEFAULT_OBJ_EXTENSION
  {
    string s1, s2;
    if (sscanf(file, "%s.%s",s1,s2) != 2)
      /* add default extension */
      file += DEFAULT_OBJ_EXTENSION;
  }
#endif
  ::save_object(file);
  return 1;
}


/* write bytes to file */
#ifdef MUDOS_WRITE_BYTES_ARGS
static int write_bytes(string file, int start, string str) {
#else
static int write_bytes(string file, string str, int start) {
#endif
  int *sizes;
  if (!MASTER_WRITE(file)) return 0;
  if (start == 0)
    /* write from start in DGD */
    start = -file_size(file);
  return ::write_file(file, str, start);
}


/* write string to file */
static int write_file(string file, string str) {
  if (!MASTER_WRITE(file)) return 0;
  return ::write_file(file, str);
}



#ifdef MUDOS_EXTRA_FILEFUNCS


/* get file info (not very good simulated) */
static varargs mixed *stat(string file, int flag) {
  if (flag == -1) return get_dir(file);
  if (!MASTER_READ(file)) return ({ ({}), ({}), ({}) });
  return ::get_dir(file);
}


/* show a file to this_player() */
static varargs int cat(string file, int first, int num) {
  string *lines;
  if (num == 0)
    num = CAT_LINES;
  if (!(lines=read_lines(file, first, num)))
    return 0;
  if (::sizeof(lines) <= CAT_LINES)
    write(implode(lines, "\n"));
  else
    write(implode(lines[0 .. CAT_LINES - 1], "\n") +
	  "\n***TRUNCATED***\n");
  return 1;
}


/* show the tail of a file to this_player() */
static int tail(string file) {
  int size, *sizes;
  string str, *lines;
  if (!MASTER_READ(file)) return 0;
  sizes = ::get_dir(file)[GDIR_SIZES];
  if (::sizeof(sizes) == 1) {
    size = TAIL_CHUNK;
    while (1) {
      size = min(size, sizes[0]);
      str = ::read_file(file, -size, size);
      if (str == 0 || ::strlen(str) != size)
	return 0;
      lines = explode("\n" + str + "\n", "\n");
      
      if (::sizeof(lines) >= TAIL_LINES + 1 || size == sizes[0]) {
	if ((size=::sizeof(lines)) > TAIL_LINES + 1)
	  str = implode(lines[size - TAIL_LINES - 1 ..], "\n");
	else if (size == 1)
	  str = lines[0];
	write(str);
	return 1;
      }
      size += TAIL_CHUNK;
    }
  }
  return 0;
}


#endif /* MUDOS_EXTRA_FILEFUNCS */