Beispiel #1
0
void FSE_GetResourceMetadata(void *fseObj, void *onQueryObj, /*INOUT*/ struct FSE_ResourceMetadata *res)
{
	struct FSE_State* fse = (struct FSE_State*) fseObj;
	struct FSE_QueryState *qs = (struct FSE_QueryState*) onQueryObj;
	int dotPos, slashPos;

	/*
	 *	Return the fullpath name and the file size.
	 */
	if (res != NULL)
	{
		res->LocalFilePathLen = (int) strlen(qs->ProcessPath);
		res->LocalFilePath = (char*) malloc (res->LocalFilePathLen + 1);
		memcpy(res->LocalFilePath, qs->ProcessPath, res->LocalFilePathLen + 1);
		res->FileLength = qs->ProcessFileSize;

		slashPos = ILibString_LastIndexOf(res->LocalFilePath, res->LocalFilePathLen, fse->DirDelimiter, (int) strlen(fse->DirDelimiter));
		dotPos = ILibString_LastIndexOf(res->LocalFilePath, res->LocalFilePathLen, ".", 1);

		if (qs->ProcessObjectType == FSE_OT_File)
		{
			res->Flags |= FSE_RMF_IsFile;
			if ((dotPos > 0) && (dotPos > slashPos))
			{
				res->FileExtensionLen = res->LocalFilePathLen - dotPos;
				res->FileExtension = (char*) malloc(res->FileExtensionLen + 1);
				memcpy(res->FileExtension, res->LocalFilePath + dotPos, res->FileExtensionLen + 1);
			}
		}
		else
		{
			res->Flags |= FSE_RMF_IsDirectory;
		}
	}
}
Beispiel #2
0
char* FilePathToFileExtension(char* file_path, int wide)
{
	int slashPos;
	int dotPos;
	char* extension = NULL;
	int filePathLen = 0;

	if(file_path == NULL) return NULL;

	filePathLen = (int) strlen(file_path);

	slashPos = ILibString_LastIndexOf(file_path, filePathLen, WIN32_DIR_DELIMITER, WIN32_DIR_DELIMITER_LEN);
	dotPos = ILibString_LastIndexOf(file_path, filePathLen, ".", 1);


	if ((dotPos > 0) && (dotPos > slashPos))
	{
		// this is a file
		int extensionLen = (int) strlen(file_path) - dotPos;
		extension = (char*) malloc(extensionLen + 1);
		memcpy(extension, file_path + dotPos, extensionLen + 1);
	
		return extension;
	}

	// this is a folder
	return NULL;
	
}
Beispiel #3
0
int FSE_HandleProcessObject(void *fseObj, struct MSA_CdsQuery *cdsQuery, void *onQueryObj, unsigned int filter, /*INOUT*/ struct CdsObject *cdsObj)
{
	struct FSE_State *fse = (struct FSE_State*) fseObj;
	struct FSE_QueryState *qs = (struct FSE_QueryState*) onQueryObj;
	struct FSE_PathInfo pi;
	struct FSE_ResourceMetadata* fseRes;
	int i;
	int expose = 0;
	int ignore = 0;
	char fn[MAX_FILENAME_SIZE];

	fseRes = (struct FSE_ResourceMetadata*) malloc(sizeof(struct FSE_ResourceMetadata));
	memset(fseRes, 0, sizeof(struct FSE_ResourceMetadata));

	memset(&pi, 0, sizeof(struct FSE_PathInfo));

	if (qs == NULL)
	{
		fprintf(stderr, "FSE_HandleProcessObject(): qs == NULL\r\n");
	}
	if (cdsObj == NULL)
	{
		fprintf(stderr, "FSE_HandleProcessObject(): cdsObj == NULL\r\n");
	}

	if ((qs->Flags & FSE_FLAG_Done) == 0)
	{
		/*
		 *	Do not process "." or ".."
		 */
		if (
			(strcmp(qs->ProcessPath, "")==0) ||
			(strcmp(qs->ProcessPath, ".")==0) ||
			(strcmp(qs->ProcessPath, "..")==0)
			)
		{
			ignore = 1;
		}

		/* get info about this path and determine if we're going to expose it */
		if (ignore == 0)
		{
			/*
			*	free temp storage
			*/
			if (pi.Ext != NULL) { free (pi.Ext); pi.Ext = NULL; pi.ExtLen = 0; }
			if (pi.FullPath != NULL) { free (pi.FullPath); pi.FullPath = NULL; pi.FullPathLen = 0; }
			if (pi.Name != NULL) { free (pi.Name); pi.Name = NULL; pi.NameLen = 0; }
			if (pi.NameNoExt != NULL) { free (pi.NameNoExt); pi.NameNoExt = NULL; pi.NameNoExtLen = 0; }
			if (pi.ParentPath != NULL) { free (pi.ParentPath); pi.ParentPath = NULL; pi.ParentPathLen = 0; }
			if (pi.RelativeFullPath != NULL) { free (pi.RelativeFullPath); pi.RelativeFullPath = NULL; pi.RelativeFullPathLen = 0; } 
			if (pi.RelativeParent != NULL) { free (pi.RelativeParent); pi.RelativeParent = NULL; pi.RelativeParentLen = 0; }


			memset(&pi, 0, sizeof(struct FSE_PathInfo));
			_FSE_ParsePathInfo(qs->ProcessPath, fse, qs, &pi);


			/* populate pi with data */
			expose = _FSE_ExposeEntry(&pi, fse, qs);
			if(cdsQuery->QueryType == MSA_Query_BrowseMetadata)
			{
				qs->Flags |= FSE_FLAG_Done;
			}
		}
		

		/*
		 * If this file is not supported, or it is a temp file where duplicated, we don't expose it,
		 * we have to find the next valid entry.
		 */
		if (ignore == 1 || expose == 0)
		{
			expose = _FSE_FindNextEntry(fse, qs, &pi);
		}

		/*
		*	SOLUTION_REFERENCE#3.6.3.2d
		*/


		if (expose != 0)
		{
			if(qs->CdsObjectType == FSE_CDS_PLAYSINGLE)
			{
				cdsObj->ID = (char*) malloc(pi.RelativeFullPathLen+CDS_STRING_PLAYSINGLE_ITEM_PREFIX_LEN+4);
			}
			else
			{
				cdsObj->ID = (char*) malloc(pi.RelativeFullPathLen+4);
			}
			cdsObj->ParentID = (char*) malloc(pi.RelativeParentLen+4);
			if (pi.RelativeFullPathLen > 0)
			{
				if(qs->CdsObjectType == FSE_CDS_PLAYSINGLE)
				{
					sprintf(cdsObj->ID, "%s0%s%s", CDS_STRING_PLAYSINGLE_ITEM_PREFIX, fse->DirDelimiter, pi.RelativeFullPath);
				}
				else
				{
					sprintf(cdsObj->ID, "0%s%s", fse->DirDelimiter, pi.RelativeFullPath);
				}
				if(ILibString_EndsWith(cdsObj->ID,(int)strlen(cdsObj->ID),EXTENSION_UPLOAD_TEMP,EXTENSION_UPLOAD_TEMP_LEN)!=0)
				{
					cdsObj->ID[(int)strlen(cdsObj->ID)-EXTENSION_UPLOAD_TEMP_LEN]=0;
				}

				if (pi.RelativeParentLen != 0)
				{
					sprintf(cdsObj->ParentID, "0%s%s", fse->DirDelimiter, pi.RelativeParent);
				}
				else
				{
					strcpy(cdsObj->ParentID, "0");
				}
			}
			else
			{
				sprintf(cdsObj->ID, "0");
				sprintf(cdsObj->ParentID, "-1");
			}

			if(qs->ProcessObjectType == FSE_OT_Directory && pi.NameLen > 0)
			{
				/* this is a directory, use the full directory name */
				cdsObj->Title = (char*) malloc(pi.NameLen+1);
				strcpy(cdsObj->Title, pi.Name);
			}
			else if (pi.NameNoExtLen > 0)
			{
				/* this is a file, use the name without extenstion for its title */
				cdsObj->Title = (char*) malloc(pi.NameNoExtLen+1);
				strcpy(cdsObj->Title, pi.NameNoExt);

				if(qs->CdsObjectType == FSE_CDS_TEMPOPRARY)
				{
					/* remove actual file extension in front of the tmp file extension */
					i = ILibString_LastIndexOf(cdsObj->Title, (int) strlen(cdsObj->Title),".",1);
					cdsObj->Title[i] = 0;				 
				}
			}
			else if (
				(pi.RelativeFullPathLen == 0) &&
				(pi.RelativeParentLen == pi.RelativeFullPathLen)
				)
			{
				/* this is the root directory */
				cdsObj->Title = (char*) malloc(5);
				strcpy(cdsObj->Title, "Root");
			}
			else
			{
				/* unknown item */
				cdsObj->Title = (char*) malloc(10);
				strcpy(cdsObj->Title, "<Unknown>");
			}

			cdsObj->Source = (char*)malloc(pi.FullPathLen+1);
			memcpy(cdsObj->Source,pi.FullPath,pi.FullPathLen);
			cdsObj->Source[pi.FullPathLen]=0;
			
			cdsObj->DeallocateThese =
				CDS_ALLOC_ID |
				CDS_ALLOC_ParentID |
				CDS_ALLOC_Title;

			/*
			*	Set the media class, the file system will consider the file type to be unknown if
			*   the file extension is something it isn't aware of, so if we are processing
			*   a temp file, then also need to find out its media class.
			*/
			if (qs->ProcessObjectType == FSE_OT_File)
			{
				if(strcmp(EXTENSION_UPLOAD_TEMP,pi.Ext)==0)
				{
					i = ILibString_LastIndexOf(pi.FullPath,pi.FullPathLen-EXTENSION_UPLOAD_TEMP_LEN,".",1);
					pi.FullPath[pi.FullPathLen-EXTENSION_UPLOAD_TEMP_LEN]=0;
					cdsObj->MediaClass = FileExtensionToClassCode(pi.FullPath+i, 0);
					pi.FullPath[pi.FullPathLen-EXTENSION_UPLOAD_TEMP_LEN]='.';
				}
				else
				{
					cdsObj->MediaClass = FileExtensionToClassCode(pi.Ext, 0);
				}
			}
			else if (qs->ProcessObjectType == FSE_OT_Directory)
			{
				cdsObj->MediaClass = CDS_MEDIACLASS_STORAGEFOLDER;
			}
			else
			{
				/* 
				 *	TODO: If you have other types of classes
				 *	you'll need to modify this section.
				 */
				cdsObj->MediaClass = CDS_MEDIACLASS_CONTAINER;
			}

			/*
			 * CUSTOMIZE: Additional meta data for ITEM
			 */
			if ((cdsObj->MediaClass & CDS_CLASS_MASK_OBJECT_TYPE) == CDS_CLASS_MASK_ITEM)
			{
				/*
				 *  All items supports OCM: detroy item
				 */
				cdsObj->DlnaManaged = CDS_DlnaManaged_DestroyItem;

				/* If this is image/video item, then get the last modified timestamp of the file
				 * and use it for the <dc:date> value
				 */
				switch (cdsObj->MediaClass & CDS_CLASS_MASK_MAJOR)
				{
					case CDS_CLASS_MASK_MAJOR_AUDIOITEM:
						cdsObj->TypeMajor.AudioItem.Date = (long) ILibFileDir_GetFileTimeStamp(cdsObj->Source);
						break;
					case CDS_CLASS_MASK_MAJOR_IMAGEITEM:
						cdsObj->TypeMajor.ImageItem.Date = (long) ILibFileDir_GetFileTimeStamp(cdsObj->Source);
						break;
					case CDS_CLASS_MASK_MAJOR_VIDEOITEM:
						cdsObj->TypeMajor.VideoItem.Date = (long) ILibFileDir_GetFileTimeStamp(cdsObj->Source);
						break;
				}

				#if defined (INCLUDE_FEATURE_PLAYSINGLE)
				/*
				*	SOLUTION_REFERENCE#3.6.3.11a
				*/

				/* If playsingle simulation is enabled, and if it is a audio/image/video item,
				 * then save the item's processpath, so that we'll use it to create a playsingle CdsObject
				 */
				if(
					(qs->CdsObjectType == FSE_CDS_NORMAL) && 
					(
					((cdsObj->MediaClass & CDS_CLASS_MASK_MAJOR) == CDS_CLASS_MASK_MAJOR_AUDIOITEM) ||
					((cdsObj->MediaClass & CDS_CLASS_MASK_MAJOR) == CDS_CLASS_MASK_MAJOR_IMAGEITEM) ||
					((cdsObj->MediaClass & CDS_CLASS_MASK_MAJOR) == CDS_CLASS_MASK_MAJOR_VIDEOITEM)
					)
				)
				{
					strcpy(qs->PlaySingleProcessPath, qs->ProcessPath);
				}
				#endif
			}
	
			/*
			 * CUSTOMIZE: Additional meta data for CONTAINER
			 */
			if ((cdsObj->MediaClass & CDS_CLASS_MASK_OBJECT_TYPE) == CDS_CLASS_MASK_CONTAINER)
			{
				/*
				 *  Containers are not searchable in this implementation.
				 *	CUSTOMIZE: Set this if container is searchable.
				 *
				 *	cdsObj->Flags |= CDS_OBJPROP_FLAGS_Searchable;
				 */

				/*
				 *  Containers supports OCM: upload content and OCM: create child container,
				 *  DlnaManaged OCM: detroy item bit does not apply to containers
				 */
				cdsObj->DlnaManaged =	CDS_DlnaManaged_UploadContent |
										CDS_DlnaManaged_CreateChildContainer;

				cdsObj->TypeObject.Container.ChildCount = 0;


				/*
				 *	Containers support OCM: content transfer must support <upnp:createClass> tag
				 *  This implemetation suppot audio, video and image items
				 */

				if(strncmp(cdsObj->ID, "0", strlen(cdsObj->ID))!=0)
				{
					cdsObj->TypeObject.Container.ChildCount = 3;

					/* set audioitem */
					cdsObj->TypeObject.Container.CreateClass = (struct CdsCreateClass*) malloc(sizeof(struct CdsCreateClass));
					memset(cdsObj->TypeObject.Container.CreateClass, 0, sizeof(struct CdsCreateClass));
					cdsObj->TypeObject.Container.CreateClass->IncludeDerived=1;
					cdsObj->TypeObject.Container.CreateClass->MediaClass = CDS_MEDIACLASS_AUDIOITEM;

					/* set iamgeitem */
					cdsObj->TypeObject.Container.CreateClass->Next = (struct CdsCreateClass*) malloc(sizeof(struct CdsCreateClass));
					memset(cdsObj->TypeObject.Container.CreateClass->Next, 0, sizeof(struct CdsCreateClass));
					cdsObj->TypeObject.Container.CreateClass->Next->IncludeDerived=1;
					cdsObj->TypeObject.Container.CreateClass->Next->MediaClass = CDS_MEDIACLASS_IMAGEITEM;

					/* set videoitem */
					cdsObj->TypeObject.Container.CreateClass->Next->Next = (struct CdsCreateClass*) malloc(sizeof(struct CdsCreateClass));
					memset(cdsObj->TypeObject.Container.CreateClass->Next->Next, 0, sizeof(struct CdsCreateClass));
					cdsObj->TypeObject.Container.CreateClass->Next->Next->IncludeDerived=1;
					cdsObj->TypeObject.Container.CreateClass->Next->Next->MediaClass = CDS_MEDIACLASS_VIDEOITEM;

					/* set container */
					cdsObj->TypeObject.Container.CreateClass->Next->Next->Next = (struct CdsCreateClass*) malloc(sizeof(struct CdsCreateClass));
					memset(cdsObj->TypeObject.Container.CreateClass->Next->Next->Next, 0, sizeof(struct CdsCreateClass));
					cdsObj->TypeObject.Container.CreateClass->Next->Next->Next->IncludeDerived=1;
					cdsObj->TypeObject.Container.CreateClass->Next->Next->Next->MediaClass = CDS_MEDIACLASS_CONTAINER;
				}
			}

			/*
			 *	This is a temp file, if the temp file was created from an upload request,
			 *	then the upload is incomplete, and we just expose the importUri value.
			 *  Since this is a file system, the only way to associate the importUri value
			 *  is faking it by regenerating the importUri value based on how the MSA object generated it
			 *  In a database backend, the application would call the MSA_ForCreateObjectResponse_AcceptUpload() method
			 *  which returns a request object, and you then call MSA_GetImportUri() to find out the importUri value and
			 *  store that in the database for the CDS item.
			 */

			//	/*
		}



		/*
		 *	MUSTDO: Track the latest date as a means
		 *	for deriving the updateID for this entry
		 */

		/*
		 *	Track the number of entries we've processed.
		 */
		qs->ProcessIndex++;
		qs->CdsObjectType = FSE_CDS_NORMAL;

		/*
		 *	free temp storage
		 */
		if (pi.Ext != NULL) { free (pi.Ext); pi.Ext = NULL; pi.ExtLen = 0; }
		if (pi.FullPath != NULL) { free (pi.FullPath); pi.FullPath = NULL; pi.FullPathLen = 0; }
		if (pi.Name != NULL) { free (pi.Name); pi.Name = NULL; pi.NameLen = 0; }
		if (pi.NameNoExt != NULL) { free (pi.NameNoExt); pi.NameNoExt = NULL; pi.NameNoExtLen = 0; }
		if (pi.ParentPath != NULL) { free (pi.ParentPath); pi.ParentPath = NULL; pi.ParentPathLen = 0; }
		if (pi.RelativeFullPath != NULL) { free (pi.RelativeFullPath); pi.RelativeFullPath = NULL; pi.RelativeFullPathLen = 0; } 
		if (pi.RelativeParent != NULL) { free (pi.RelativeParent); pi.RelativeParent = NULL; pi.RelativeParentLen = 0; }

	}

	if(fseRes->FileExtension!= NULL) {free(fseRes->FileExtension);}
	if(fseRes->LocalFilePath!= NULL) {free(fseRes->LocalFilePath);}
	free(fseRes);

	/*
	 *	finished populating the CDS Object for the file path, let's check if there's more files
	 *	in the directory to process, if so, update the QueringState object to prepare populating
	 *	the next CDS Object.
	 */
	if (qs->ProcessDirHandle != NULL)
	{
		while (ILibFileDir_GetDirNextFile(
						qs->ProcessDirHandle, 
						qs->FilePath, 
						fn,
						MAX_FILENAME_SIZE, 
						&(qs->ProcessFileSize)
						) != 0)
		{
			/*
			 *	A file was found. Let's see if it's something of interest.
			 */
			if (!((strcmp(fn, ".") == 0) || (strcmp(fn, "..") == 0)))
			{
				sprintf(qs->ProcessPath, "%s%s", qs->FilePath, fn);
				qs->ProcessObjectType = ILibFileDir_GetType(qs->ProcessPath);
				if(
					(qs->ProcessObjectType==ILibFileDir_Type_FILE) &&
					(ILibString_EndsWith(qs->ProcessPath, (int) strlen(qs->ProcessPath), EXTENSION_UPLOAD_TEMP, EXTENSION_UPLOAD_TEMP_LEN)!=0)
				)
				{
					/* treat incomplete upload files as CDS items. */
					qs->CdsObjectType = FSE_CDS_TEMPOPRARY;
				}

				/* return the expose value for the current populated CDS Object */
				return expose;
			}
		}

		#if defined (INCLUDE_FEATURE_PLAYSINGLE)
		/*
		*	SOLUTION_REFERENCE#3.6.3.11b
		*/

		/* If playsingle simulation is enabled, then it will use the last valid CDS item and create
		 * an additional CdsObject with a <res> element using the playsingle
		 */

		if(qs->CdsObjectType == FSE_CDS_NORMAL && qs->PlaySingleProcessPath[0] != '\0')
		{
			qs->CdsObjectType = FSE_CDS_PLAYSINGLE;
			/* replace the process path with the last valid CDS item process path. */
			{
				strcpy(qs->ProcessPath, qs->PlaySingleProcessPath);
				qs->ProcessObjectType = ILibFileDir_GetType(qs->ProcessPath);
				qs->PlaySingleProcessPath[0] = '\0';
			}
		}
		else
		{
			qs->CdsObjectType = FSE_CDS_NORMAL;
		#endif
			// no suitable entries were found, so mark it as being done
			qs->Flags |= FSE_FLAG_Done;
		#if defined (INCLUDE_FEATURE_PLAYSINGLE)
		}
		#endif
	}

	/* return the expose value for the current populated CDS Object */
	return expose;
}
Beispiel #4
0
void _FSE_ParsePathInfo(const char* fullpath, const struct FSE_State *fse, const struct FSE_QueryState *qs, /*INOUT*/ struct FSE_PathInfo *pi)
{
	int len;
	int ddpos=0;
	int dotpos=0;

	/* free memory */
	if (pi->Ext != NULL) { free (pi->Ext); pi->Ext = NULL; pi->ExtLen = 0; }

	if (pi->FullPath != NULL) { free (pi->FullPath); pi->FullPath = NULL; pi->FullPathLen = 0; }
	if (pi->Name != NULL) { free (pi->Name); pi->Name = NULL; pi->NameLen = 0; }
	if (pi->NameNoExt != NULL) { free (pi->NameNoExt); pi->NameNoExt = NULL; pi->NameNoExtLen = 0; }
	if (pi->ParentPath != NULL) { free (pi->ParentPath); pi->ParentPath = NULL; pi->ParentPathLen = 0; }
	if (pi->RelativeFullPath != NULL) { free (pi->RelativeFullPath); pi->RelativeFullPath = NULL; pi->RelativeFullPathLen = 0; } 
	if (pi->RelativeParent != NULL) { free (pi->RelativeParent); pi->RelativeParent = NULL; pi->RelativeParentLen = 0; }

	len = (int) strlen(fullpath);
	pi->FullPath = (char*) malloc(len+2);
	memcpy(pi->FullPath, fullpath, len);
	pi->FullPath[len] = '\0';
	pi->FullPathLen = len;

	/*
	 *	If it's a directory, make sure it ends
	 *	with a trailing directory delimiter.
	 *	We do this because fse->RootPath will have
	 *	a trailing delimiter.
	 */
	if (
		(qs->ProcessObjectType == FSE_OT_Directory) &&
		(pi->FullPath[pi->FullPathLen-1] != fse->DirDelimiterChr)
		)
	{
		pi->FullPath[pi->FullPathLen] = fse->DirDelimiterChr;
		pi->FullPathLen++;
		pi->FullPath[pi->FullPathLen] = '\0';
	}

	/*
	 *	Get the relative full path.
	 */
	pi->RelativeFullPathLen = pi->FullPathLen - fse->RootPathLen;
	pi->RelativeFullPath = (char*) malloc (pi->RelativeFullPathLen + 1);
	memcpy(pi->RelativeFullPath, pi->FullPath + fse->RootPathLen, pi->RelativeFullPathLen);
	pi->RelativeFullPath[pi->RelativeFullPathLen] = '\0';

	/*
	 *	Find last directory delimiter.
	 *	If the fullpath is a directory, temporarily
	 *	terminate w/o a trailing delimiter.
	 */

	if (qs->ProcessObjectType == FSE_OT_Directory)
	{
		pi->FullPathLen--;
		pi->FullPath[pi->FullPathLen] = '\0';
	}
	ddpos = ILibString_LastIndexOf(pi->FullPath, pi->FullPathLen, fse->DirDelimiter, (int) strlen(fse->DirDelimiter));
	if (qs->ProcessObjectType == FSE_OT_Directory)
	{
		pi->FullPath[pi->FullPathLen] = fse->DirDelimiterChr;
		pi->FullPathLen++;
	}

	/*
	 *	Get the name of the directory/file.
	 *	If no directory delimiter was found,
	 *	there is no name... which indicates
	 *	we should treat it as the root.
	 */
	if (ddpos >= 0)
	{
		pi->NameLen = pi->FullPathLen - ddpos - 1;
		pi->Name = (char*) malloc(pi->NameLen+1);
		memcpy(pi->Name, pi->FullPath + ddpos + 1, pi->NameLen+1);
	}
	else
	{
		pi->NameLen = 0;
		pi->Name = (char*) malloc(1);
		pi->Name[0] = '\0';
	}


	/*
	 *	Chop a trailing dir delimiter.
	 */
	if (
		(pi->NameLen > 0) && 
		(pi->Name[pi->NameLen-1] == fse->DirDelimiterChr)
		)
		{
			pi->NameLen--;
			pi->Name[pi->NameLen] = '\0';
		}

		dotpos = ILibString_LastIndexOf(pi->Name, pi->NameLen, ".", 1);
	if (dotpos >= 0)
	{
		/*
		 *	Get the name w/o the extension and save extension.
		 */
		pi->NameNoExtLen = dotpos;
		pi->NameNoExt = (char*) malloc(pi->NameNoExtLen + 1);
		memcpy(pi->NameNoExt, pi->Name, pi->NameNoExtLen);
		pi->NameNoExt[pi->NameNoExtLen] = '\0';

		pi->ExtLen = pi->NameLen - pi->NameNoExtLen;
		pi->Ext = (char*) malloc (pi->ExtLen + 1);
		memcpy(pi->Ext, pi->Name + dotpos, pi->ExtLen+1);
	}
	else
	{
		/*
		 *	Name w/o extension is the same as name.
		 *	Extension is blank.
		 */
		pi->NameNoExt = (char*) malloc(pi->NameLen+1);
		memcpy(pi->NameNoExt, pi->Name, pi->NameLen+1);
		pi->NameNoExtLen = pi->NameLen;

		pi->ExtLen = 0;
		pi->Ext = (char*) malloc(pi->ExtLen+1);
		pi->Ext[0] = '\0';
	}

	/*
	 *	Get the parent directory name
	 */
	if (ddpos >= 0)
	{
		pi->ParentPathLen = ddpos+1;
		pi->ParentPath = (char*) malloc(pi->ParentPathLen+1);
		memcpy(pi->ParentPath, pi->FullPath, pi->ParentPathLen);
		pi->ParentPath[pi->ParentPathLen] = '\0';
	}
	else
	{
		pi->ParentPathLen = 0;
		pi->ParentPath = (char*) malloc(1);
		pi->ParentPath[0] = '\0';
	}

	/* 
	 *	Get the relative parent dir.
	 *	rootlen includes a trailinger dir delimiter.
	 */
	if ((pi->ParentPathLen > 0) && (pi->ParentPathLen >= fse->RootPathLen))
	{
		pi->RelativeParentLen = pi->ParentPathLen - fse->RootPathLen;
		pi->RelativeParent = (char*) malloc(pi->RelativeParentLen + 1);
		memcpy(pi->RelativeParent, pi->ParentPath + fse->RootPathLen, pi->RelativeParentLen);
		pi->RelativeParent[pi->RelativeParentLen] = '\0';
	}
	else
	{
		pi->RelativeParentLen = 0;
		pi->RelativeParent = (char*) malloc(1);
		pi->RelativeParent[0] = '\0';
	}

}