/*
 * File rock.c - generate RRIP  records for iso9660 filesystems.

   Written by Eric Youngdale (1993).

   Copyright 1993 Yggdrasil Computing, Incorporated

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

#include <stdlib.h>

#ifndef AMIGA
#ifndef VMS
#ifndef __QNX__
#include <sys/sysmacros.h>
#endif
#include <unistd.h>
#endif
#endif

#include "mkisofs.h"
#include "iso9660.h"
#include <string.h>

#ifdef VMS
#define S_ISLNK(m)	(0)
#else
#ifndef S_ISLNK
#define S_ISLNK(m)	(((m) & S_IFMT) == S_IFLNK)
#endif
#endif

#define SU_VERSION 1

#define SL_ROOT    8
#define SL_PARENT  4
#define SL_CURRENT 2
#define SL_CONTINUE 1

/*
 * Buffer to build RR attributes
 */

static unsigned char Rock[16384];
static unsigned char symlink_buff[256];

#ifdef __STDC__
int generate_rock_ridge_attributes (char * whole_name, char * name,
				    struct directory_entry * s_entry,
				    struct stat * statbuf,
				    struct stat * lstatbuf,
				    int deep_opt)
#else
int generate_rock_ridge_attributes (whole_name, name,
				    s_entry,
				    statbuf,
				    lstatbuf,
				    deep_opt)
char * whole_name; char * name; struct directory_entry * s_entry;
struct stat * statbuf, *lstatbuf;
int deep_opt;
#endif
{
  int ipnt = 0;
  int flagpos, flagval;

  statbuf = statbuf;        /* this shuts up unreferenced compiler warnings */

  /* Identify that we are using the SUSP protocol */
  if(deep_opt & NEED_SP){
	  Rock[ipnt++] ='S';
	  Rock[ipnt++] ='P';
	  Rock[ipnt++] = 7;
	  Rock[ipnt++] = SU_VERSION;
	  Rock[ipnt++] = 0xbe;
	  Rock[ipnt++] = 0xef;
	  Rock[ipnt++] = 0;
  };

  /* First build the posix name field */
  Rock[ipnt++] ='R';
  Rock[ipnt++] ='R';
  Rock[ipnt++] = 5;
  Rock[ipnt++] = SU_VERSION;
  flagpos = ipnt;
  flagval = 0;
  Rock[ipnt++] = 0;   /* We go back and fix this later */

  if(strcmp(name,".")  && strcmp(name,"..")){
	  /* First build the posix name field */
	  Rock[ipnt++] ='N';
	  Rock[ipnt++] ='M';
	  Rock[ipnt++] = 5 + strlen(name);
	  Rock[ipnt++] = SU_VERSION;
	  Rock[ipnt++] = 0;
	  flagval |= (1<<3);
	  strcpy((char *)&Rock[ipnt], name);
	  ipnt += strlen(name);
  };

  /*
   * Add the posix modes 
   */
  Rock[ipnt++] ='P';
  Rock[ipnt++] ='X';
  Rock[ipnt++] = 36;
  Rock[ipnt++] = SU_VERSION;  
  flagval |= (1<<0);
  set_733((char*)Rock + ipnt, lstatbuf->st_mode);
  ipnt += 8;
  set_733((char*)Rock + ipnt, lstatbuf->st_nlink);
  ipnt += 8;
  set_733((char*)Rock + ipnt, lstatbuf->st_uid);
  ipnt += 8;
  set_733((char*)Rock + ipnt, lstatbuf->st_gid);
  ipnt += 8;

#ifndef AMIGA

  /*
   * Check for special devices
   */
  if (S_ISCHR(lstatbuf->st_mode) || S_ISBLK(lstatbuf->st_mode)) {
    Rock[ipnt++] ='P';
    Rock[ipnt++] ='N';
    Rock[ipnt++] = 20;
    Rock[ipnt++] = SU_VERSION;  
    flagval |= (1<<1);
    set_733((char*)Rock + ipnt, major(lstatbuf->st_rdev));
    ipnt += 8;
    set_733((char*)Rock + ipnt, minor(lstatbuf->st_rdev));
    ipnt += 8;
  };

  /*
   * Check for and symbolic links.  VMS does not have these.
   */
  if (S_ISLNK(lstatbuf->st_mode)){
    int lenpos, lenval, j0;
    int cflag, nchar;
    unsigned char * cpnt, *cpnt1;
    nchar = readlink(whole_name, symlink_buff, sizeof(symlink_buff));
    symlink_buff[nchar] = 0;
    set_733(s_entry->isorec.size, 0);
    Rock[ipnt++] ='S';
    Rock[ipnt++] ='L';
    lenpos = ipnt;
    Rock[ipnt++] = 20;
    Rock[ipnt++] = SU_VERSION;  
    Rock[ipnt++] = 0;
    flagval |= (1<<2);
    lenval = 5;
    cpnt = &symlink_buff[0];
    while(*cpnt){
      cpnt1 = (unsigned char *) strchr((char *) cpnt, '/');
      cflag = 0;
      if(cpnt1) {
	*cpnt1 = 0;
      };

      /* We treat certain components in a special way.  */
      if(cpnt[0] == '.' && cpnt[1] == '.' && cpnt[2] == 0){
	Rock[ipnt++] = SL_PARENT | cflag;
	Rock[ipnt++] = 0;  /* length is zero */
	lenval += 2;
      } else if(cpnt[0] == '.' && cpnt[1] == 0){
	Rock[ipnt++] = SL_CURRENT | cflag;
	Rock[ipnt++] = 0;  /* length is zero */
	lenval += 2;
      } else if(cpnt[0] == 0){
	Rock[ipnt++] = SL_ROOT | cflag;
	Rock[ipnt++] = 0;  /* length is zero */
	lenval += 2;
      } else {
	Rock[ipnt++] = cflag;
	j0 = strlen((char *) cpnt);
	Rock[ipnt++] = j0;
	strcpy((char *) Rock + ipnt, (char *) cpnt);
	ipnt += j0;
	lenval += j0 + 2;
      };
      if(cpnt1)
	cpnt = cpnt1 + 1;
      else
	break;
    };
    Rock[lenpos] = lenval;
  };

#endif

  /* 
   * Add in the Rock Ridge TF time field
   */
  Rock[ipnt++] ='T';
  Rock[ipnt++] ='F';
#ifdef __QNX__
  Rock[ipnt++] = 5 + 4 * 7;
  Rock[ipnt++] = SU_VERSION;
  Rock[ipnt++] = 0x0f;
#else
  Rock[ipnt++] = 5 + 3 * 7;
  Rock[ipnt++] = SU_VERSION;
  Rock[ipnt++] = 0x0e;
#endif
  flagval |= (1<<7);
#ifdef __QNX__
  iso9660_date((char *) &Rock[ipnt], lstatbuf->st_ftime);
  ipnt += 7;
#endif
  iso9660_date((char *) &Rock[ipnt], lstatbuf->st_mtime);
  ipnt += 7;
  iso9660_date((char *) &Rock[ipnt], lstatbuf->st_atime);
  ipnt += 7;
  iso9660_date((char *) &Rock[ipnt], lstatbuf->st_ctime);
  ipnt += 7;

  /* 
   * Add in the Rock Ridge RE time field
   */
  if(deep_opt & NEED_RE){
	  Rock[ipnt++] ='R';
	  Rock[ipnt++] ='E';
	  Rock[ipnt++] = 4;
	  Rock[ipnt++] = SU_VERSION;
	  flagval |= (1<<6);
  };
  /* 
   * Add in the Rock Ridge PL record, if required.
   */
  if(deep_opt & NEED_PL){
	  Rock[ipnt++] ='P';
	  Rock[ipnt++] ='L';
	  Rock[ipnt++] = 12;
	  Rock[ipnt++] = SU_VERSION;
	  set_733((char*)Rock + ipnt, 0);
	  ipnt += 8;
	  flagval |= (1<<5);
  };

  /* 
   * Add in the Rock Ridge CL field, if required.
   */
  if(deep_opt & NEED_CL){
	  Rock[ipnt++] ='C';
	  Rock[ipnt++] ='L';
	  Rock[ipnt++] = 12;
	  Rock[ipnt++] = SU_VERSION;
	  set_733((char*)Rock + ipnt, 0);
	  ipnt += 8;
	  flagval |= (1<<4);
  };

  /* 
   * Add in the Rock Ridge CE field, if required.  We use  this for the
   * extension record that is stored in the root directory.
   */
  if(deep_opt & NEED_CE){
	  Rock[ipnt++] ='C';
	  Rock[ipnt++] ='E';
	  Rock[ipnt++] = 28;
	  Rock[ipnt++] = SU_VERSION;
	  set_733((char*)Rock + ipnt, 0);
	  ipnt += 8;
	  set_733((char*)Rock + ipnt, 0);
	  ipnt += 8;
	  set_733((char*)Rock + ipnt, 0);
	  ipnt += 8;
  };

  /*
   * Done filling in all of the fields.  Now copy it back to a buffer for the
   * file in question.
   */

  /* Now copy this back to the buffer for the file */
  Rock[flagpos] = flagval;

  s_entry->rr_attributes = (char *) malloc(ipnt);
  s_entry->rr_attr_size = ipnt;
  memcpy(s_entry->rr_attributes, Rock, ipnt);
  return ipnt;
}

/* Guaranteed to  return a single sector with the relevant info */

char * FDECL4(generate_rr_extension_record, char *, id,  char  *, descriptor,
				    char *, source, int  *, size){
  int ipnt = 0;
  char * pnt;
  int len_id, len_des, len_src;

  len_id = strlen(id);
  len_des =  strlen(descriptor);
  len_src = strlen(source);
  Rock[ipnt++] ='E';
  Rock[ipnt++] ='R';
  Rock[ipnt++] = 8 + len_id + len_des + len_src;
  Rock[ipnt++] = 1;
  Rock[ipnt++] = len_id;
  Rock[ipnt++] = len_des;
  Rock[ipnt++] = len_src;
  Rock[ipnt++] = 1;

  memcpy(Rock  + ipnt, id, len_id);
  ipnt += len_id;

  memcpy(Rock  + ipnt, descriptor, len_des);
  ipnt += len_des;

  memcpy(Rock  + ipnt, source, len_src);
  ipnt += len_src;

  if(ipnt  > SECTOR_SIZE) {
	  fprintf(stderr,"Extension record too  long\n");
	  exit(1);
  };
  pnt = (char *) malloc(SECTOR_SIZE);
  memset(pnt, 0,  SECTOR_SIZE);
  memcpy(pnt, Rock, ipnt);
  *size = ipnt;
  return pnt;
}
