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

wrudf.c

/*    wrudf.c
 *
 *    Maintains a UDF filing system
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/resource.h>

#include "wrudf.h"

#define WRUDF_VERSION   "0.0.5"

char  *devicename;                  /* "/dev/cdrom" or disk image filename */
int   device;                       /* the file descriptor */
int   devicetype;
enum MEDIUM medium;
int   ignoreReadError;        /* used while reading VRS which may be absent on open CDR */

#ifdef _GNU_SOURCE
char  *line;
#define     GETLINE(prompt) readLine(prompt);
#else
char  line[256];
#define GETLINE(prompt) printf(prompt);   fgets(line, 256, stdin); *strchr(line, '\n') = 0;
#endif


int   cmndc;
int   cmndvSize;
char**      cmndv;

#define FOUND_BEA01     1
#define FOUND_NSR02     1<<1
#define FOUND_TEA01     1<<2
#define FOUND_PVD 1<<8
#define FOUND_LVD 1<<9
#define FOUND_PD  1<<10
#define FOUND_USD 1<<11 
uint32_t    found;

uint32_t    options;


Directory   *rootDir, *curDir;
timestamp   timeStamp;
regid       entityWRUDF = { 0, "-wrudf (" WRUDF_VERSION ")", "\x04\x05"};

extent_ad   extentMainVolDescSeq;
extent_ad   extentRsrvVolDescSeq;
extent_ad   extentNextVolDescSeq;
extent_ad   extentLogVolIntegritySeq;
uint32_t    integrityDescBlocknumber;           /* where to write LVID when finished */
extent_ad   extentLogVolIntegrityDesc;          /* (continuation) extent for current LVIDs */

extern      uint64_t  CDRuniqueID;                    /* in wrudf-cdr.c ex VAT FE */


struct partitionDesc          *pd;              /* for the writeable partition */
uint16_t                virtualPartitionNum  = 0xFFFF;
uint32_t                *vat;
uint32_t                newVATindex;
uint32_t                maxVATindex;
uint32_t                prevVATlbn;
struct logicalVolDesc         *lvd;
struct unallocSpaceDesc       *usd;
struct spaceBitmapDesc        *spaceMap;
struct logicalVolIntegrityDesc      *lvid;
struct fileSetDesc            *fsd;
int                           usedSparingEntries;
struct sparingTable           *st;

int   spaceMapDirty, usdDirty, sparingTableDirty;


char* readLine(char* prompt) {
    if( line ) {
      free(line);
    }
    return line = readline(prompt);
}


void 
initialise(char *devicename) 
{
    uint32_t                  i, len, blkno, lastblk, size;
    long_ad             *fsdAd;
    short_ad                  *adSpaceMap;
    struct sparablePartitionMap *spm;
    char                zeroes[5];
    char                        fsdOut[91];
    int                         fsdLen;
    struct generic_desc       *p;
    struct volStructDesc      *vsd;

    initIO(devicename);
    memset(zeroes, 0, 5);

    /* read Volume Recognition Sequence */
    ignoreReadError = 1;
    for( blkno = 16; blkno < 256; blkno++ ) {
      vsd = readSingleBlock(blkno);

      if( vsd == NULL || memcmp(vsd->stdIdent, zeroes, 5) == 0 )
          break;

      if( strncmp(vsd->stdIdent, "BEA01", VSD_STD_ID_LEN) == 0 ) {
          found |= FOUND_BEA01;
          continue;
      }
      if( strncmp(vsd->stdIdent, "NSR02", VSD_STD_ID_LEN) == 0 ) {
          found |= FOUND_NSR02;
          continue;
      }
      if( strncmp(vsd->stdIdent, "TEA01", 5) == 0 ) {
          found |= FOUND_TEA01;
          continue;
      }
    }
    ignoreReadError = 0;

    if( !(found & FOUND_BEA01) || !(found & FOUND_NSR02) || !(found & FOUND_TEA01) )
      printf("No UDF VRS\n");

    p = NULL;

    if( medium == CDR ) {
      p = readSingleBlock(512);
      if( p == NULL || p->descTag.tagIdent != TAG_IDENT_AVDP )
          fail("No AVDP at block 512 on CDR disc\n");
    }

    if( !p ) { 
      p = readTaggedBlock(256, ABSOLUTE);
      if( p->descTag.tagIdent != TAG_IDENT_AVDP ) {
          p = readTaggedBlock(trackSize - 1, ABSOLUTE);
          if( p->descTag.tagIdent != TAG_IDENT_AVDP ) {
            p = readTaggedBlock(trackSize - 256, ABSOLUTE);
            if( p->descTag.tagIdent != TAG_IDENT_AVDP )
                fail("No AVDP at block 256, N-256 or N-1 \n");
          } 
      }
    }

    extentMainVolDescSeq = ((struct anchorVolDescPtr*)p)->mainVolDescSeqExt;
    extentRsrvVolDescSeq = ((struct anchorVolDescPtr*)p)->reserveVolDescSeqExt;

    /* read Volume Descriptor Sequence */
    blkno = extentMainVolDescSeq.extLocation;
    len = extentMainVolDescSeq.extLength;

    for( i = 0; i < len; blkno++, i += 2048 ) {
      int   inMainSeq = 1;

      if( (p = readTaggedBlock(blkno, ABSOLUTE)) == NULL ) {
          if( !inMainSeq ) 
            fail("Volume Descriptor Sequences read failure\n");
          blkno = extentRsrvVolDescSeq.extLocation;
          len = extentRsrvVolDescSeq.extLength;
          inMainSeq = 0;
          i = 0;
      }
      switch( p->descTag.tagIdent ) {
      case TAG_IDENT_PVD:
          found |= FOUND_PVD;
          break;
      case TAG_IDENT_VDP:
          blkno = ((struct volDescPtr*)p)->nextVolDescSeqExt.extLocation - 1;
          len   = ((struct volDescPtr*)p)->nextVolDescSeqExt.extLength;
          i = (uint32_t) -2048;
          break;
      case TAG_IDENT_IUVD:
          break;
      case TAG_IDENT_PD:
          /* must have one (re)writeable partition */
          /* may have at most one RDONLY partition */
          switch( ((struct partitionDesc *)p)->accessType ) {
          case PD_ACCESS_TYPE_READ_ONLY:
            break;
          case PD_ACCESS_TYPE_REWRITABLE:
          case PD_ACCESS_TYPE_WRITE_ONCE:
            found |= FOUND_PD;
            if( !pd )
                pd  = (struct partitionDesc*) calloc(512, 1);
            if( p->volDescSeqNum > pd->volDescSeqNum )
                memcpy(pd, p, 512);
            break;
          default:
            printf("What to do with an accesstype %d partition?\n", 
                ((struct partitionDesc*)p)->accessType);
            break;
          }
          break;
      case TAG_IDENT_LVD:
          found |= FOUND_LVD;
          if( !lvd )
            lvd  = (struct logicalVolDesc*) calloc(512, 1);
          if( p->volDescSeqNum > lvd->volDescSeqNum )
            memcpy(lvd, p, 512);
          break;
      case TAG_IDENT_USD:
          found |= FOUND_USD;
          if( !usd )
            usd = (struct unallocSpaceDesc*) calloc(512, 1);
          if( p->volDescSeqNum > usd->volDescSeqNum )
            memcpy(usd, p, 512);
          break;
      case TAG_IDENT_TD:
          i = len;
          break;
      default:
          printf("Unexpected tag ID %04X\n in Volume Descriptor Sequence block %d", p->descTag.tagIdent, blkno);
      }
    }

    if( (found & FOUND_LVD) == 0 )
      fail("No LVD found \n");

    if( lvd->logicalBlockSize != 2048 )
      fail("Blocksize not 2048\n");

    spm  = (struct sparablePartitionMap*)lvd->partitionMaps;
    for( i = 0; i < lvd->numPartitionMaps; i++ ) {
      if( spm->partitionMapType == 2 ) {

          if( strncmp( spm->partIdent.ident, UDF_ID_SPARABLE, strlen(UDF_ID_SPARABLE)) == 0 ) {
            int   j;

            if( spm->sizeSparingTable > 2048 )
                fail("Cannot handle SparingTable > 2048 bytes");
            st = (struct sparingTable*)malloc(2048);
            p = readTaggedBlock(spm->locSparingTable[0], ABSOLUTE);
            memcpy(st, p, spm->sizeSparingTable);

            /* if #1 fails, try #2; should pick copy with highest sequenceNum */

            for( j = usedSparingEntries = 0; j < (spm->sizeSparingTable >> 3); j++ ) {
                 if( st->mapEntry[j].origLocation < 0xFFFFFFF0 )
                   usedSparingEntries++;
            }
          } else if( strncmp( spm->partIdent.ident, UDF_ID_VIRTUAL, strlen(UDF_ID_VIRTUAL)) == 0 )
            virtualPartitionNum = i;
      }
      (char*)spm += spm->partitionMapLength;
    }

    if( medium == CDR ) {
      if( virtualPartitionNum != 0xFFFF )
          readVATtable();
      else 
          fail("No Virtual Partition Map on CDR\n");
    }

    if( medium != CDR && (found & FOUND_USD) == 0 )
      fail("Did not find Unallocated Space Descriptor\n");

    /* Read Fileset Descriptor Sequence */
    fsdAd = (long_ad*) lvd->logicalVolContentsUse;
    for( i = 0; i < (fsdAd->extLength >> 11); i++ ) {
      blkno = pd->partitionStartingLocation + fsdAd->extLocation.logicalBlockNum + i;
      if( !(p = readTaggedBlock(blkno, ABSOLUTE)) ) 
          exit(1);
      switch( p->descTag.tagIdent ) {
      case TAG_IDENT_TD:
          i = fsdAd->extLength;
          break;
      case TAG_IDENT_FSD:
          if( fsd && (lvd->volDescSeqNum > p->volDescSeqNum) )
            break;
          fsd = (struct fileSetDesc*)malloc(512);
          memcpy(fsd, p, 512);
          break;
      default:
          printf("Unxpected tag id %d, where File Set Desc(256) expected\n", p->descTag.tagIdent);
      }
    }

    if( !fsd )
      fail("No File Set Descriptor\n");

    /* load Spacemap extent */
    adSpaceMap = (short_ad*) &((struct partitionHeaderDesc*)pd->partitionContentsUse)->unallocSpaceBitmap;

    if( adSpaceMap->extLength != 0 ) {
      blkno = adSpaceMap->extPosition;
      len = adSpaceMap->extLength;
      spaceMap = (struct spaceBitmapDesc*) malloc((len + 2047) & ~2047);

      for( i = 0; i < len; i += 2048 ) {
          p = readBlock(blkno, 0);
          memcpy( (uint8_t*)spaceMap + i, (uint8_t*) p, 2048);
          freeBlock(blkno++, 0);
      }

      if( spaceMap->descTag.tagIdent != TAG_IDENT_SBD )
          fail("SpaceBitmap not found\n");
    }

    if (fsdLen = decode_utf8(fsd->fileSetIdent, fsdOut, fsd->fileSetIdent[31]))
        fsdOut[fsdLen] = '\0';

    printf("You are going to update fileset '%s'\nProceed (y/N) : ", &fsdOut[1]);
    readLine(NULL);

    if( (line[0] | ' ') != 'y' )
      fail("wrudf terminated\n");

    /* Read Logical Volume Integrity sequence */
    blkno = lvd->integritySeqExt.extLocation;
    lastblk = blkno + (lvd->integritySeqExt.extLength >> 11);
    extentLogVolIntegrityDesc = lvd->integritySeqExt;

    for(   ; blkno < lastblk; blkno++ ) {
      if( !(p = readTaggedBlock(blkno, ABSOLUTE))  ) 
          fail("Read failure in Integrity Sequence, blk %d\n", blkno);

      switch( p->descTag.tagIdent ) {
      case TAG_IDENT_TD:
          blkno = lastblk;
          break;
      case TAG_IDENT_LVID:
          size = sizeof(struct logicalVolIntegrityDesc) + sizeof(struct logicalVolIntegrityDescImpUse)
            + 2 * sizeof(uint32_t) * ((struct logicalVolIntegrityDesc*)p)->numOfPartitions;
          if( !lvid )
            lvid = (struct logicalVolIntegrityDesc*) malloc(size);
          integrityDescBlocknumber = blkno;
          memcpy(lvid, p, size);

          if( lvid->nextIntegrityExt.extLocation ) {
            extentLogVolIntegrityDesc = lvid->nextIntegrityExt;
            blkno = lvid->nextIntegrityExt.extLocation;
            lastblk = blkno + (lvid->nextIntegrityExt.extLength >> 11);
            blkno--;                            /* incremented again in for statement */
          }
          break;
      default:
          printf("Unxpected tag %X in Integrity Sequence; blk %d\n", p->descTag.tagIdent, blkno);
          blkno = lastblk;
          break;
      }
    }

    if( !lvid || lvid->descTag.tagIdent != TAG_IDENT_LVID  )
      fail("No Logical Volume Integrity Descriptor\n");

    if( medium == CDR && lvid->integrityType == LVID_INTEGRITY_TYPE_CLOSE )
      fail("CDR volume has been closed\n");

    if( medium == CDR )
      // take from VAT FileEntry
      ((struct logicalVolHeaderDesc*)lvid->logicalVolContentsUse)->uniqueID = CDRuniqueID;
    
    curDir = rootDir = (Directory*)malloc(sizeof(Directory));
    memset(rootDir, 0, sizeof(Directory));
    rootDir->dataSize = 4096;
    rootDir->data = (uint8_t*)malloc(4096);
    rootDir->name = "";
    readDirectory( NULL, &fsd->rootDirectoryICB, "");

    if( medium == CDR )
      return;

    if( lvid->integrityType == LVID_INTEGRITY_TYPE_OPEN ) {
      GETLINE("** Volume was not closed; do you wish to proceed (y/N) : ");
      if( (line[0] | 0x20) != 'y' )
          exit(0);
    }

    /*  If prevailing LVID is in one before last block of extent
     *      then next LVID will be in last and must contain continuation extent */

    if( integrityDescBlocknumber + 2 ==    
      extentLogVolIntegrityDesc.extLocation + (extentLogVolIntegrityDesc.extLength >> 11) ) 
    {
      getUnallocSpaceExtent( 32 * 2048, extentLogVolIntegrityDesc.extLocation, &lvid->nextIntegrityExt);
      if( lvid->nextIntegrityExt.extLength == 0 ) {
          printf("No more unallocated space for Integrity Sequence\n");
      }
    } else {
      lvid->nextIntegrityExt.extLength = 0;
      lvid->nextIntegrityExt.extLocation = 0;
    }

    integrityDescBlocknumber++;

    updateTimestamp(0,0);
    memcpy(lvid->impUse + 2 * sizeof(uint32_t) * lvid->numOfPartitions, &entityWRUDF, sizeof(regid));
    lvid->recordingDateAndTime = timeStamp;
    lvid->integrityType = LVID_INTEGRITY_TYPE_OPEN;
    lvid->descTag.tagLocation = integrityDescBlocknumber;

    size = sizeof(struct logicalVolIntegrityDesc) + sizeof(struct logicalVolIntegrityDescImpUse)
      + 2 * sizeof(uint32_t) * lvid->numOfPartitions;
    
    lvid->descTag.descCRCLength = size - sizeof(tag);
    setChecksum(lvid);

    p = readTaggedBlock(integrityDescBlocknumber, ABSOLUTE);
    memcpy( p, lvid, size);
    dirtyBlock(integrityDescBlocknumber, ABSOLUTE);   /* not written because of buffering : Force unit access? */

}


int 
finalise(void) 
{
    int           i, lbn, len, size, blkno ;
    struct generic_desc       *p;
    short_ad      *adSpaceMap;

    updateDirectory(rootDir);                   /* and any dirty children */

    if( medium == CDR ) {
      writeVATtable();
    } else {
      /* rewrite Space Bitmap */
      if( spaceMapDirty) {
          adSpaceMap = (short_ad*) &((struct partitionHeaderDesc*)pd->partitionContentsUse)->unallocSpaceBitmap;
          lbn = adSpaceMap->extPosition;
          len = adSpaceMap->extLength;

          for( i = 0; i < len; i += 2048 ) {
            p = readBlock(lbn, 0);
            memcpy( p, (uint8_t*)spaceMap + i, 2048);
            dirtyBlock(lbn, 0);
            freeBlock(lbn++, 0);
          }
      }

      if( sparingTableDirty )
          updateSparingTable();

      /* write closed Logical Volume Integrity Descriptor */
      lvid->integrityType = LVID_INTEGRITY_TYPE_CLOSE;
      updateTimestamp(0,0);
      lvid->recordingDateAndTime = timeStamp;
      lvid->descTag.tagLocation = integrityDescBlocknumber;
      size = sizeof(struct logicalVolIntegrityDesc) + sizeof(struct logicalVolIntegrityDescImpUse) 
          + 2 * sizeof(uint32_t) * lvid->numOfPartitions;
      lvid->descTag.descCRCLength = size - sizeof(tag);
      setChecksum(lvid);
      p = readBlock(integrityDescBlocknumber, ABSOLUTE);
      memcpy(p, lvid, size);
      dirtyBlock(integrityDescBlocknumber, ABSOLUTE);
      freeBlock(integrityDescBlocknumber, ABSOLUTE);

      /* terminating descriptor */
      blkno = lvid->nextIntegrityExt.extLocation;
      if( !blkno )
          blkno = integrityDescBlocknumber + 1;

      p = readBlock(blkno, ABSOLUTE);
      memset(p, 0, 2048);
      p->descTag.tagLocation = blkno;
      p->descTag.tagSerialNum = lvid->descTag.tagSerialNum;
      p->descTag.tagIdent = TAG_IDENT_TD;
      p->descTag.descCRCLength = 512 - sizeof(tag);
      setChecksum(p);
      dirtyBlock(blkno, ABSOLUTE);
      freeBlock(blkno, ABSOLUTE);

      if( usdDirty ) {
          size = sizeof(struct unallocSpaceDesc) + usd->numAllocDescs * sizeof(extent_ad);
          usd->descTag.descCRCLength = size - sizeof(tag);
          setChecksum(usd);
          p = readBlock(usd->descTag.tagLocation, ABSOLUTE);
          memcpy(p, usd, size);
          dirtyBlock(usd->descTag.tagLocation, ABSOLUTE);
          freeBlock(usd->descTag.tagLocation, ABSOLUTE);

          /* now rewrite in Reserve Sequence which must be exact copy of Main Sequence */
          usd->descTag.tagLocation +=
            extentRsrvVolDescSeq.extLocation - extentMainVolDescSeq.extLocation;
          setChecksum(usd);
          p = readBlock(usd->descTag.tagLocation, ABSOLUTE);
          memcpy(p, usd, size);
          dirtyBlock(usd->descTag.tagLocation, ABSOLUTE);
          freeBlock(usd->descTag.tagLocation, ABSOLUTE);
      }
    } // end not CDR                      

    closeIO();                                  /* clears packet buffers; closes device */

    /* free allocated space */
    if(pd)  free(pd);
    if(lvd) free(lvd);
    if(fsd) free(fsd);
    if(usd) free (usd);
    if(spaceMap) free(spaceMap);
    if(lvid) free(lvid);
    if(st)  free(st);
    if(vat) free(vat);
    return 0;
}

int
parseCmnd(char* line) 
{
    char    *p, *q, next, term;
    int           i, j;
    int           cmnd;

    if( !cmndvSize ) {
      cmndvSize = 64;
      cmndv = malloc(cmndvSize * sizeof(char*));
    }

    if( line[0] == 0 )
      return CMND_FAILED;

    cmndc = 0;
    for( p = line; *p == ' '; p++ )   ;
    for( q = p; *q && (*q != ' '); q++ )   ;
    next = *q;
    *q = 0;

    if( !strcmp(p, "cd") || !strcmp(p, "ls") ) {
      printf("Specify cdh/lsh or cdc/lsc for Harddisk or CompactDisc\n");
      return CMND_FAILED;
    }

    if(      !strcmp(p, "cp") )    cmnd = CMND_CP;
    else if( !strcmp(p, "rm") )    cmnd = CMND_RM;
    else if( !strcmp(p, "mkdir") ) cmnd = CMND_MKDIR;
    else if( !strcmp(p, "rmdir") ) cmnd = CMND_RMDIR;
    else if( !strcmp(p, "lsc") )   cmnd = CMND_LSC;
    else if( !strcmp(p, "lsh") )   cmnd = CMND_LSH;
    else if( !strcmp(p, "cdc") )   cmnd = CMND_CDC;
    else if( !strcmp(p, "cdh") )   cmnd = CMND_CDH;
    else if( !strcmp(p, "quit") )  cmnd = CMND_QUIT;
    else if( !strcmp(p, "exit") )  cmnd = CMND_QUIT;
    else {
      printf("Invalid command\n");
      return CMND_FAILED;
    }

    while( next != 0 ) {
      if( cmndc == cmndvSize ) {
          cmndvSize += 32;
          cmndv = realloc(cmndv, cmndvSize);
          if( !cmndv )
            fail("cmndv reallocation failed\n");
      }
      p = q + 1;
      while( *p == ' ') p++;
      if( *p == 0 ) 
          break;
      if( *p == '"' ) {
          term = '"'; q = p+1;
      } else {
          term = ' '; q = p;
      }
      while( *q && *q != term ) q++;

      next = *q;
      if( *p == '"' ) {
          if( *q == 0 )
            return CMND_ARG_INVALID;
          cmndv[cmndc++] = p+1;
          *(q-1) = 0;
      } else {
          cmndv[cmndc++] = p;
          *q = 0;
      }
    }

    options = 0;
    for( i = 0; i < cmndc; i ++ ) {
      if( cmndv[i][0] == '-' ) {
          for( j = 1; cmndv[i][j]; j++ ) {
            switch( cmndv[i][j] ) {
            case 'f':
                options |= OPT_FORCE;
                break;
            case 'r':
                options |= OPT_RECURSIVE;
                break;
            default:
                return CMND_ARG_INVALID;
            }
          }
          cmndv[i] = NULL;
      }
    }

    for( i = j = 0; i < cmndc; i++ ) {
      if( i != j )
          cmndv[j] = cmndv[i];
      if( cmndv[j] != NULL )
          j++;
    }

    cmndc = j;

    /* could check whether options valid for command */

    return cmnd;
}

int
main(int argc, char** argv) 
{ 
    int           rv;
    int           cmnd;
    char    prompt[256];
    Directory     *d;

    printf("wrudf " WRUDF_VERSION "\n");
    devicename= "/dev/cdrom";

    if(argc > 1 )
      devicename = argv[1];               /* can specify disk image filename */

    if( setpriority(PRIO_PROCESS, 0, -10) ) {
      printf("setpriority(): %m\n");
      exit(1);
    }

    hdWorkingDir = getcwd(NULL, 0);
    initialise(devicename);

    for(;;) {
      d = rootDir;
      prompt[0] = 0;
      while( curDir != d ) { 
          strcat(prompt, d->name);
          strcat(prompt, "/");
          d = d->child;
      }
      if( d->name[0] == 0 )
          strcat(prompt, "/");
      else
          strcat(prompt, d->name); 

      strcat(prompt, " > ");
      
      GETLINE(prompt);

      cmnd = parseCmnd(line);

      if( cmnd == CMND_FAILED )
          continue;

      if( cmnd == CMND_QUIT )
          break;

      switch( cmnd ) {
      case CMND_CP:
          rv = cpCommand();
          break;
      case CMND_RM:
          rv = rmCommand();
          break;
      case CMND_MKDIR:
          rv = mkdirCommand();
          break;
      case CMND_RMDIR:
          rv = rmdirCommand();
          break;
      case CMND_LSC:
          rv = lscCommand();
          break;
      case CMND_LSH:
          rv = lshCommand();
          break;
      case CMND_CDC:
          rv = cdcCommand();
          break;
      case CMND_CDH:
          rv = cdhCommand();
          break;
      }

      switch( rv ) {
      case CMND_OK:
          break;
      case CMND_FAILED:
          printf("Failed\n");
          break;
      case WRONG_NO_ARGS:
          printf("Incorrect number of arguments\n");
          break;
      case CMND_ARG_INVALID:
          printf("Invalid argument\n");
          break;
      case NOT_IN_RW_PARTITION:
          printf("Not in RW partition\n");
          break;
      case DIR_INVALID:
          printf("Invalid directory\n");
          break;
      case DIR_NOT_EMPTY:
          printf("Directory not empty\n");
          break;
      case EXISTING_DIR:
          printf("Dir already exists\n");
          break;
      case DOES_NOT_EXIST:
          printf("File/directory does not exist\n");
          break;
      case PERMISSION_DENIED:
          printf("Permission denied\n");
          break;
      case IS_DIRECTORY:
          printf("Is a directory\n");
          break;
      default:
          printf("Unknown return value %d\n", rv);
      }
    }
    free(hdWorkingDir);
    return finalise();
}

Generated by  Doxygen 1.6.0   Back to index