/*
 * nasd_cheops_cl_test.c
 *
 * test module
 *
 * Authors: Khalil Amiri, CMU SCS/ECE, July 19997
 *          Sean Levy, CMU SCS, July 1999
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1996,1997,1998,1999.
 *
 * Permission to reproduce, use, and prepare derivative works of
 * this software for internal use is granted provided the copyright
 * and "No Warranty" statements are included with all reproductions
 * and derivative works. This software may also be redistributed
 * without charge provided that the copyright and "No Warranty"
 * statements are included in all redistributions.
 *
 * NO WARRANTY. THIS SOFTWARE IS FURNISHED ON AN "AS IS" BASIS.
 * CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED AS TO THE MATTER INCLUDING, BUT NOT LIMITED
 * TO: WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY
 * OF RESULTS OR RESULTS OBTAINED FROM USE OF THIS SOFTWARE. CARNEGIE
 * MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT
 * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
 */


#include <nasd/nasd_options.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <unistd.h>
#include <malloc.h>
#include <signal.h>
#include <nasd/nasd_getopt.h>

#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <time.h>

#include <nasd/nasd_types.h>
#include <nasd/nasd_cheops_types.h>
#include <nasd/nasd_cheops_ios.h>
#include <nasd/nasd_cheops_cache.h>
#include <nasd/nasd_cheops_common.h>
#include <nasd/nasd_cheops_raidmap.h>
#include <nasd/nasd_cheops_mgr.h>
#include <nasd/nasd_cheops_client.h>
#include <nasd/nasd_cheops_client_internal.h>
#include "nasd_cheops_cl_vnasd.h"
#include "nasd_cheops_time.h"

#define CHEOPS_YORN(_v_)	((_v_) ? "yes" : "no")

/* nasty globals */
_nasd_cheops_cache_t *bs_cachep;
extern _nasd_cheops_mgr_handles_t _nasd_cheops_mgr_handles;

nasd_identifier_t rw_bsid;

/* prototypes */
extern void _nasd_cheops_doio_thread(int myid);
extern void _nasd_cheops_print_asm (nasd_asm_t *a);
extern int  _nasd_cheops_map_access(NASD_layout_t *, nasd_offset_t,
                                    nasd_len_t, caddr_t, nasd_asm_t *);
extern void _nasd_cheops_cl_show_mgrs();
extern int  _nasd_cheops_cl_bscache_rd(_nasd_cheops_cache_key_t, caddr_t);
extern int  _nasd_cheops_cl_bscache_wb(_nasd_cheops_cache_ent_t *, int);
void _nasd_cheops_print_time(nasd_timespec_t ts);
void _nasd_cheops_print_attribute(nasd_attribute_t *attr);

_nasd_cheops_cache_key_t make_key(int);
void test_cache(int);
int test_mapping(int, int);
int test_readwrite(int, int);
int test_init_stuff();

/* debugging */
int cheops_test_cache = 0;
int cheops_test_mapping = 0;
int cheops_test_init = 1;
int cheops_test_readwrite = 1;

#define ITER 5 

void
usage(void)
{
  fprintf(stderr, "cheops_clerk: usage: cheops_clerk [options]\n");
  fprintf(stderr, "options are:\n");
  fprintf(stderr, "  -d             debug mode [autoflush stderr, stdout]\n");
  fprintf(stderr, "  -f filename    resource file\n");
  fprintf(stderr, "  -O             override defaults via command line\n");
  fprintf(stderr, "  -t nthreads    use nthreads I/O threads\n");
  fprintf(stderr, "  -c             TEST_CACHE (default: %s)\n",
	  CHEOPS_YORN(cheops_test_cache));
  fprintf(stderr, "  -i             TEST_INIT (default: %s)\n",
	  CHEOPS_YORN(cheops_test_init));
  fprintf(stderr, "  -m             TEST_MAPPING (default: %s)\n",
	  CHEOPS_YORN(cheops_test_mapping));
  fprintf(stderr, "    -o offset    offset (in bytes)\n");
  fprintf(stderr, "    -l len       length (in bytes)\n");
  fprintf(stderr, "  -r     TEST_READWRITE (default: %s)\n",
	  CHEOPS_YORN(cheops_test_readwrite));
  fprintf(stderr, "    -b bsize     buffersize (in KB)\n");
  fprintf(stderr, "    -n nwrites   number of writes\n");
}

int
main(
  int    argc,
  char **argv)
{    
	int rc, i, max;
	int offset = -1;
	int len = -1;
	int bsize = -1;
	int nwrites = -1;
	nasd_thread_t thread_desc[5];
	char c;
	char *resource_file = NULL;
  int override_defaults = 0;
  int quietude = 0;
  int temp;

	while(nasd_getopt(argc, argv, "db:cf:il:mn:o:rOqt:", &c)) {
	  switch(c) {
    case 'd':                           /* debug mode */
      setbuf(stderr, NULL);
      setbuf(stdout, NULL);
      fprintf(stderr, "[cheops test: debug mode on]\n");
      break;
	  case 'b':
	    if(!cheops_test_readwrite) {
	      fprintf(stderr,
                "ERROR: -b option only valid in TEST_READWRITE mode\n");
	      usage();
	      exit(1);
	    }
	    bsize = atoi(nasd_optarg);
	    break;
	  case 'c':
	    cheops_test_cache++;
	    break;
	  case 'f':
	    resource_file = nasd_optarg;
	    break;
	  case 'i':
	    cheops_test_init++;
	    break;
    case 'O':
      override_defaults++;
      break;
    case 'q':
      quietude++;
      break;
    case 't':
      if (sscanf(nasd_optarg, "%d", &temp) != 1) {
        fprintf(stderr, "ERROR: argument to -t does not scan as int (%s)\n",
                nasd_optarg);
        usage();
      } else if (temp < 1) {
        fprintf(stderr, "ERROR: %d I/O threads does not make sense\n");
        usage();
      }
      _nasd_cheops_doio_n_threads = temp;
      break;
	  case 'l':
	    if(!cheops_test_mapping) {
	      fprintf(stderr, "ERROR: -l option only valid in TEST_MAPPING mode\n");
	      usage();
	      exit(1);
	    }
	    len = atoi(nasd_optarg);
	    break;
	  case 'm':
	    cheops_test_mapping++;
	    break;
	  case 'n':
	    if(!cheops_test_readwrite) {
	      fprintf(stderr,
                "ERROR: -n option only valid in TEST_READWRITE mode\n");
	      usage();
	      exit(1);
	    }
	    nwrites = atoi(nasd_optarg);
	    break;
	  case 'o':
	    if(!cheops_test_mapping) {
	      fprintf(stderr, "ERROR: -o option only valid in TEST_MAPPING mode\n");
	      usage();
	      exit(1);
	    }
	    offset = atoi(nasd_optarg);
	    break;
	  case 'r':
	    cheops_test_readwrite++;
	    break;
	  case '?':
	  default:
	    usage();
	    exit(1);
	    break;
	  }
	}
	if (cheops_test_mapping && ((offset == -1) || (len == -1))) {
	  fprintf(stderr, "ERROR: must specify offset & len\n");
	  usage();
	  exit(1);
	}

	if(cheops_test_readwrite && ((bsize == -1) || (nwrites == -1))) {
	  fprintf(stderr, "ERROR: must specify bsize & nwrites\n");
	  usage();
	  exit(1);
	}

	rc = _nasd_cheops_cl_init(resource_file, NASD_BIND_DEFAULT,
                            override_defaults, quietude);
	if (rc)
	  fprintf(stderr, "CHEOPS: client init failed!\n");
	else 
	  _nasd_cheops_cl_show_mgrs();

	if (cheops_test_cache) {
#ifdef NOTYET
	  /* test the cache module */
	  rc = _nasd_cheops_cache_create(30, 64,
                                   _NASD_CHEOPS_CACHE_TYPE_SMAP, &bs_cachep);
	  if (rc)
	    fprintf(stderr, "CHEOPS: create cache failed! \n");
	  else
	    fprintf(stderr, "CHEOPS: create cache successful!\n");
	  bs_cachep->handle_read_fault = _nasd_cheops_cl_bscache_rd;
	  bs_cachep->writeback = _nasd_cheops_cl_bscache_wb;

	  for (i=1; i<10; i++)
	    if (rc = nasd_thread_create(&(thread_desc[i]),
                                  test_cache, (nasd_threadarg_t)i) )
	      fprintf(stderr, "CHEOPS: error spawning thread");
	  sleep(50);
#else /* NOTYET */
	  fprintf(stderr, "ERROR: cache not implemented.\n");
#endif /* NOTYET */
	}

	if (cheops_test_mapping)
	  test_mapping(offset, len);

	if (cheops_test_init)
	  test_init_stuff();

	if (cheops_test_readwrite) {
	  if (bsize<8*1024)
	    for (i=0; i<1;i++)
	      test_readwrite(bsize, nwrites);
	}
	exit(0);
}

void 
test_cache(
  int myid)
{
  int i, j;
  char ent[64];
	int found = 0;
	int rc, max;
	caddr_t bp;
	_nasd_cheops_cache_ent_t *ep;

	max = 20;
	for (j=0; j< max; j++) {
	  i = myid*100+j;
	  rc = _nasd_cheops_cache_read(bs_cachep, make_key(i), ent);
	  if (!rc)
	    fprintf(stderr, "CHEOPS: [%d] got back entry: %s\n", myid, ent);
	  else
	    fprintf(stderr, "CHEOPS: [%d] did not find entry: %d\n", myid, i);
	}

	fprintf(stderr, "CHEOPS: "
   "\n\n [%d] Phase 1: attempting to fix a lot of entries \n\n", myid);	
	for (j=0; j< max/2; j++) {
	  i = myid*100+j; 
	  sleep (1);
	  rc = _nasd_cheops_cache_hold_ent(bs_cachep, make_key(i), &ep);
	  if (rc) 
	    fprintf(stderr, "CHEOPS: [%d] can not fix ent %d...\n", myid, i);
	  else
	    fprintf(stderr, "CHEOPS: [%d] successfully fixed ent %d...\n", myid, i);
	  rc = _nasd_cheops_cache_release_ent(bs_cachep, ep);
	}

	fprintf(stderr, "CHEOPS: \n\n [%d] Phase 2: looking up entries \n\n", myid);	
	for (j=0; j< max; j++) {
	  i = myid*100+j;
	  rc = _nasd_cheops_cache_read(bs_cachep, make_key(i), ent);
	  if (!rc)
	    fprintf(stderr, "CHEOPS: [%d] got back entry: %s\n", myid, ent);
	  else
	    fprintf(stderr, "CHEOPS: "
       "[%d] did not find entry: %d (got back rc=%d)\n", myid, i, rc);
	  }
	fprintf(stderr, "CHEOPS: [%d] I am done %d...\n", myid, max);
}

int
test_mapping(int offset, int len)
{	
  int found = 0;
	int rc, i, max;
	nasd_asm_t *a;
	char *buf;
	NASD_layout_t Layout, *lp;

	fprintf(stderr, "CHEOPS: testing raid mapping code ...\n");	
	lp = &Layout;
	lp->stripeUnitSize = 32;
	lp->numCol = 2;
	lp->numDataCol = 2;
	lp->layoutType = RAID0;
	lp->dataStripeSize = lp->numDataCol*lp->stripeUnitSize;
	lp->map = _nasd_cheops_get_layout(RAID5);
	lp->identifier_list[0].nasd_identifier = 40;
  lp->identifier_list[0].disk_identifier = 0;
	lp->identifier_list[1].nasd_identifier = 40;
  lp->identifier_list[1].disk_identifier = 1;
	lp->identifier_list[2].nasd_identifier = 40;
  lp->identifier_list[2].disk_identifier = 2;
	lp->identifier_list[3].nasd_identifier = 40;
  lp->identifier_list[3].disk_identifier = 3;
	lp->identifier_list[4].nasd_identifier = 40;
  lp->identifier_list[4].disk_identifier = 4;
	
	NASD_Malloc(a, sizeof(nasd_asm_t), (nasd_asm_t *));
	bzero((char *)a, sizeof(nasd_asm_t));
	_nasd_cheops_map_access(lp, offset, len, buf, a);
	_nasd_cheops_print_asm(a);
	NASD_Free(a, sizeof(nasd_asm_t));
	return 0;
}

int
test_init_stuff()
{
  nasd_cheops_handle_t h;
  nasd_cheops_bs_handle_t bs_handle;
  nasd_cookie_t       in_cookie;
  nasd_attribute_t    in_attribute;
  nasd_fieldmask_t    in_fieldmask;
  nasd_partnum_t      in_partnum;
  nasd_identifier_t   out_identifier;
  nasd_attribute_t    out_attribute;
  nasd_status_t       nasd_status;
  nasd_rpc_status_t   status;
  nasd_identifier_t   *new_ids, in_bsid;
  nasd_cheops_bs_lu_args_t args;
  nasd_cheops_bs_lu_res_t res;
  int rc, i, j, k;

	for (i=0; i< _nasd_cheops_mgr_handles.num_managers; i++) {
	  fprintf(stderr, "CHEOPS: trying to contact storage manager ");
	  fprintf(stderr, "CHEOPS: %s", _nasd_cheops_mgr_handles.hl[i].mgr_name);
    nasd_cheops_client_null(_nasd_cheops_mgr_handles.hl[i].h, &nasd_status,
                            &status);
	  if (nasd_status != NASD_SUCCESS)
	    fprintf(stderr, "CHEOPS: null rpc failed\n");
	  else
	    fprintf(stderr, "CHEOPS: ...NULL rpc success\n");
	}

	NASD_Malloc(new_ids, sizeof(nasd_identifier_t)*1000, (nasd_identifier_t *));

	out_identifier=0;
	h = _nasd_cheops_mgr_handles.hl[0].h;
	for (j=0; j< ITER; j++) {
	  _nasd_create_cl(h, in_cookie, in_attribute, in_fieldmask, in_partnum,
                    &out_identifier, &out_attribute, &nasd_status, &status);
	  new_ids[j] = out_identifier;
	  if (status || (nasd_status!=NASD_SUCCESS))
	    fprintf(stderr, "CHEOPS: create object failed\n");
	  else
	    fprintf(stderr, "CHEOPS: "
       "create object successful, id = 0x%"NASD_ID_FMT"\n", out_identifier);
	}

	for (j=0; j< ITER; j++) {
	  in_bsid = new_ids[j];
    args.in_cookie = in_cookie;
    args.in_bsid = in_bsid;
	  nasd_cheops_client_bs_lookup(h, &args, &res, &status);
    bs_handle = res.out_bs_handle;
    nasd_status = res.out_nasd_status;
	  if (nasd_status || status) 
	    fprintf(stderr, "CHEOPS: lookup object failed\n");
	  else {
	    fprintf(stderr, "bsid: 0x%" NASD_ID_FMT ", SU: %d, G: %d, ",
              bs_handle.bsid, 
	     bs_handle.stripe_unit_size, bs_handle.num_data_col);
	    fprintf(stderr, "map(");
	    for (k=0; k<bs_handle.num_col; k++)
	      fprintf(stderr, "%d*0x%" NASD_ID_FMT ", ",
         bs_handle.identifier_list[k].disk_identifier,
         bs_handle.identifier_list[k].nasd_identifier);
	    fprintf(stderr, ")\n");
	  }
  }

  in_bsid = new_ids[0];
  _nasd_remove_cl(h, in_cookie,
                  in_bsid, &nasd_status, &status);
  args.in_cookie = in_cookie;
  args.in_bsid = in_bsid;
  nasd_cheops_client_bs_lookup(h, &args, &res, &status);
  bs_handle = res.out_bs_handle;
  nasd_status = res.out_nasd_status;
  if (nasd_status != NASD_SUCCESS) 
    fprintf(stderr, "CHEOPS: lookup object failed, %d\n", nasd_status);
  else {
    fprintf(stderr, "CHEOPS: 0x%" NASD_ID_FMT ", SU: %d, G: %d, ",
            bs_handle.bsid, 
     bs_handle.stripe_unit_size, bs_handle.num_data_col);
    fprintf(stderr, "map(");
    for (k=0; k<bs_handle.num_col; k++)
      fprintf(stderr, "%d*0x%" NASD_ID_FMT ", ",
       bs_handle.identifier_list[k].disk_identifier,
       bs_handle.identifier_list[k].nasd_identifier);
    fprintf(stderr, ")\n");
  }

  rw_bsid = new_ids[1];

	return 0;
	
}
	

int
test_doio()
{
#ifdef CHEOPS_TEST_DOIO     /* test the doio threads */

#endif /* CHEOPS_TEST_DOIO */
	return 0;
}

int
_nasd_cheops_test_drive()
{
  return 0;
}

_nasd_cheops_cache_key_t
make_key(int i) 
{
  _nasd_cheops_cache_key_t k;
	k.object_id = i;
	k.record_id = 0;
	return k;
}

int
test_readwrite(int bsize, int nwrites)
{

  nasd_cheops_handle_t h;
  int i, k;	
  nasd_offset_t in_offset=10;
  nasd_len_t in_len=10;
  nasd_byte_t *in_data;

  nasd_cheops_bs_handle_t bs_handle;
  nasd_attribute_t    in_attribute;
  nasd_attribute_t    out_attribute;
  nasd_fieldmask_t    in_fieldmask;
  nasd_cookie_t in_cookie;
  nasd_identifier_t oid;
  nasd_len_t out_len;
  nasd_cheops_qos_req_t qos_req;
  nasd_partnum_t in_partnum = 4;
  nasd_status_t nasd_status;
  nasd_rpc_status_t op_status;
  nasd_timespec_t now;
  nasd_cheops_bs_lu_args_t args;
  nasd_cheops_bs_lu_res_t res;
  CHEOPS_DECLARE_TIME;
  CHEOPS_INIT_TIME;

  qos_req.typ_access_size = 64;
  qos_req.reli_class = NASD_CHEOPS_QOS_LOW_RELI;
  qos_req.access_pattern = NASD_CHEOPS_QOS_MANY_SMALL_IOS;
  qos_req.parallelism = 2;
  qos_req.latency = 15;
#if 0
  qos_req.write_ratio = 0.2;
#else
  /* XXX doubles are not supported in RPC stuff */
  /* find some other representation */
  /*NASD_PANIC();*/
#endif
  qos_req.priority = 20;
  qos_req.min_su_size = 64;     

  h = _nasd_cheops_mgr_handles.hl[0].h;
  _nasd_create_cl(h, in_cookie, in_attribute, in_fieldmask, in_partnum,
                  &oid, &out_attribute, &nasd_status, &op_status);

  if (op_status || (nasd_status!=NASD_SUCCESS))
    fprintf(stderr, "CHEOPS: create object failed\n");
  else
    fprintf(stderr, "CHEOPS: create object successful, id = 0x%" NASD_ID_FMT "\n", oid);

  args.in_cookie = in_cookie;
  args.in_bsid = oid;
  nasd_cheops_client_bs_lookup(h, &args, &res, &op_status);
  bs_handle = res.out_bs_handle;
  nasd_status = res.out_nasd_status;

  if (nasd_status || op_status) 
    fprintf(stderr, "CHEOPS: lookup object failed\n");
  else {
    fprintf(stderr, "CHEOPS: "
     "bsid: 0x%" NASD_ID_FMT ", SU: %d, G: %d, RAID level %d\n",
     bs_handle.bsid, 
     bs_handle.stripe_unit_size, bs_handle.num_col, bs_handle.raid_level);
    fprintf(stderr, "map(");
    for (k=0; k<bs_handle.num_col; k++)
      fprintf(stderr, "%d*0x%" NASD_ID_FMT ", ",
       bs_handle.identifier_list[k].disk_identifier,
       bs_handle.identifier_list[k].nasd_identifier);
    fprintf(stderr, ")\n");
  }


  NASD_Malloc(in_data, bsize*1024, (nasd_byte_t *));
  bzero(in_data, bsize*1024);
  sprintf(in_data,
          "%s", "Cheops is kicking and screaming back to life! Yiii haaa!");

  in_offset = 0;
  in_len = bsize*1024; 
  for (in_offset=0; in_offset <nwrites*bsize*1024; in_offset+= bsize*1024) {
    fprintf(stderr, "CHEOPS: \n\nwriting at offset %" NASD_64u_FMT " \n",
            in_offset);
    CHEOPS_INIT_TIME;
    CHEOPS_BEGIN_TIME;
    _nasd_write_cl(h, in_cookie, oid, 
                   in_offset,  in_len,  in_partnum, &out_len,
                   in_data,  &nasd_status,  &op_status);
    CHEOPS_END_TIME;
    CHEOPS_REPORT_TIME("_nasd_write_cl");
    fprintf(stderr, "CHEOPS: %f MB write: ", in_len*1.0/(1024*1024) );
    CHEOPS_REPORT_BANDWIDTH( (in_len*1.0/(1024*1024)) );

  }
     
  bzero(in_data, bsize*1024);
  in_offset = 0;
  in_len = bsize*1024;
  for (in_offset=0; in_offset <nwrites*bsize*1024; in_offset+= bsize*1024) {
    CHEOPS_INIT_TIME;
    CHEOPS_BEGIN_TIME;
    _nasd_read_cl(h, in_cookie, oid, 
                  in_offset,  in_len,  in_partnum, &out_len,
                  in_data,  &nasd_status,  &op_status);
    CHEOPS_END_TIME;
    CHEOPS_REPORT_TIME("_nasd_read_cl");
    fprintf(stderr, "CHEOPS: %f MB read: ", in_len*1.0/(1024*1024) );
    CHEOPS_REPORT_BANDWIDTH( (in_len*1.0/(1024*1024)) );
  }

  in_data[200] = '\0';
  fprintf(stderr, "CHEOPS: buffer read=%s\n", in_data);
  bzero((char *)&out_attribute, sizeof(out_attribute));

  CHEOPS_INIT_TIME;
  CHEOPS_BEGIN_TIME;
  _nasd_getattr_cl(h, in_cookie, oid, in_partnum, &out_attribute,
                   &nasd_status, &op_status);
  CHEOPS_END_TIME;
  CHEOPS_REPORT_TIME("_nasd_getattr_cl");
  _nasd_cheops_print_attribute(&out_attribute);

  NASD_Free(in_data, bsize*1024);
  return 0;
}

void
_nasd_cheops_print_time(nasd_timespec_t ts)
{
  struct tm *gt;
  int hr;
  time_t t;

  t = ts.ts_sec;
  gt = gmtime(&t);
  hr = gt->tm_hour-4;
  if (hr<0) hr += 12;
  fprintf(stderr, "CHEOPS: Time is %d:%d:%d, ", hr, gt->tm_min, gt->tm_sec);
  fprintf(stderr, "CHEOPS: Date is %d/%d/%d \n", 1+gt->tm_mon, gt->tm_mday,
          gt->tm_year);
     
}

void 
_nasd_cheops_print_cheops_attribute(_nasd_cheops_attribute_t *attr)
{
  fprintf(stderr, "CHEOPS: Cheops Attributes: \n");
  fprintf(stderr, "CHEOPS: \tobject block size=%d\n",
   attr->block_size);
  fprintf(stderr, "CHEOPS: \tobject len = %" NASD_64u_FMT "\n",
   attr->object_len);
  fprintf(stderr, "CHEOPS: \tobject create time= ");
  _nasd_cheops_print_time (attr->object_create_time);
  fprintf(stderr, "CHEOPS: \tobject modify time = ");
  _nasd_cheops_print_time (attr->object_modify_time);
  fprintf(stderr, "CHEOPS: \tattr modify time= ");
  _nasd_cheops_print_time (attr->attr_modify_time);
}

void 
_nasd_cheops_print_attribute(nasd_attribute_t *attr)
{
  nasd_timespec_t oct;

  fprintf(stderr, "CHEOPS: Attributes:\n");
  fprintf(stderr, "CHEOPS: \tobject block size=%d\n", attr->block_size);
  fprintf(stderr, "CHEOPS: \tobject len = %" NASD_64u_FMT "\n",
          attr->object_len);
  fprintf(stderr, "CHEOPS: \tobject create time= ");
  _nasd_cheops_print_time (attr->object_create_time);
  fprintf(stderr, "CHEOPS: \tobject modify time = ");
  _nasd_cheops_print_time (attr->object_modify_time);
  fprintf(stderr, "CHEOPS: \tattr modify time= ");
  _nasd_cheops_print_time (attr->attr_modify_time);
  fprintf(stderr, "CHEOPS: \tfs_specific= %s\n", (char *)attr->fs_specific);
}

/* Local Variables:  */
/* indent-tabs-mode: nil */
/* tab-width: 2 */
/* End: */
