Darwin-Streaming-Server/PlaylistBroadcaster.tproj/PickerFromFile.cpp

484 lines
14 KiB
C++
Raw Normal View History

/*
*
* @APPLE_LICENSE_HEADER_START@
*
* Copyright (c) 1999-2008 Apple Inc. All Rights Reserved.
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*
*/
#include "PickerFromFile.h"
#include <stdlib.h>
#include "GetWord.h"
#include "Trim.h"
#include "MyAssert.h"
#include <sys/stat.h>
#ifndef __Win32__
#include <dirent.h>
#endif
#ifdef __hpux__
#include <sys/types.h>
#endif
#define kMaxPickerPath 512
#include <stdio.h>
static bool CompareNameToElement( PLDoubleLinkedListNode<LoopDetectionListElement>* node, void* name );
static void DisplayPickerErr( int pickErr, const char *message, const char*fname, int lineCount, const char*lineBuff );
static bool CompareNameToElement( PLDoubleLinkedListNode<LoopDetectionListElement>* node, void* name )
{
if ( !::strcmp( node->fElement->mPathName, (const char*)name ) )
return true;
return false;
}
static bool IsDir(char* path);
static bool IsDir(char* path)
{
struct stat data;
int err = stat(path, &data);
if (err == -1)
return false;
return ((data.st_mode & S_IFDIR) == S_IFDIR );
}
static void DisplayPickerErr( int pickErr, const char *message, const char*fname, int lineCount, const char*lineBuff )
{
char *errMessage;
qtss_printf( "- %s:\n", message );
if ( lineCount )
qtss_printf( " Playlist: %s, line# %i\n", fname, lineCount );
else
qtss_printf( " Playlist: %s\n", fname );
if ( lineBuff )
qtss_printf( " Playlist text: %s", lineBuff ); // lineBuff already includes a \n
switch ( pickErr )
{
case kPickerPopulateLoopDetected:
errMessage = "Include would create a loop.\n";
break;
case kPickerPopulateBadFormat:
errMessage = "Playlist file is missing *PLAY-LIST* identifier.\n";
break;
case kPickerPopulateFileError:
errMessage = "Playlist file could not be opened.\n";
break;
case kPickerPopulateNoMem:
default:
errMessage = "Internal error occurred.\n";
break;
}
qtss_printf( " Reason: %s\n", errMessage);
}
bool PathIsAbsolute(char *pathPtr)
{
bool result = false;
#ifdef __Win32__
if ( (pathPtr[1] == ':') && ( pathPtr[2] == kPathDelimiterChar ) )
result = true;
#else
if ( *pathPtr == kPathDelimiterChar )
result = true;
#endif
return result;
}
int PopulatePickerFromFile( PlaylistPicker* picker, char* fname, const char* basePath, LoopDetectionList *ldList )
{
Assert( picker );
Assert( fname );
FILE* weightings = NULL;
LoopDetectionListElement* ldElement = NULL;
LoopDetectionNode* ldNode = NULL;
int lineCount = 0;
int pickErr = kPickerPopulateNoErr;
char path[kMaxPickerPath];
#if kPartialPathBeginsWithDelimiter
if (PathIsAbsolute(fname))
{
if ( *basePath )
fname++;
#else
if ( !PathIsAbsolute(fname) )
{
#endif
// it's a partial path, expand it to include all
// previously traversed paths
::strncpy( path, basePath, kMaxPickerPath-1 );
::strncat( path, fname, kMaxPickerPath-1 );
}
else
{
// it's an absolute reference. use the path
// part of this for the new basePath
::strncpy( path, fname, kMaxPickerPath-1 );
}
// path is now either an absolute or working directory
// referenced partial path to the playlist file.
int len = strlen(path);
char lastChar = path[len-1];
if (lastChar == '\n' || lastChar == '\r' || lastChar == ' ')
path[len-1] = '\0';
// ldList is passed as NULL by the initial caller. recursive calls
// pass along the ldList we create hre
if ( ldList == NULL )
ldList = new LoopDetectionList;
Assert( ldList );
if ( !ldList )
pickErr = kPickerPopulateNoMem;
if ( !pickErr )
{
if ( ldList->ForEachUntil( CompareNameToElement, path ) )
{
// we're already in the include chain, this is a loop
// print a warning (error?) and continue past the loop.
//qtss_printf("- Playlists include loop at file: %s\n", path );
pickErr = kPickerPopulateLoopDetected;
}
}
if ( !pickErr )
{
ldElement = new LoopDetectionListElement( path );
Assert( ldElement );
if ( ldElement )
{ ldNode = new LoopDetectionNode( ldElement );
Assert( ldNode );
if ( !ldNode )
pickErr = kPickerPopulateNoMem;
}
else
pickErr = kPickerPopulateNoMem;
}
if (::IsDir(path))
return ::PopulatePickerFromDir(picker, path);
if ( !pickErr )
{
weightings = ::fopen( path, "r" );
if (!weightings)
{
qtss_printf("- Playlist picker failed opening list file %s\n", path);
pickErr = kPickerPopulateFileError;
}
}
if ( !pickErr )
{
SInt32 lineBuffSize = (kMaxPickerPath *2) - 1;
SInt32 wordBuffSize = kMaxPickerPath - 1;
char lineBuff[kMaxPickerPath * 2];
char wordBuff[kMaxPickerPath];
char* next;
char* pathEnd;
char* thisLine;
// add ourselves to the list
ldList->AddNode( ldNode );
// trim off the file name to get just the path part
pathEnd = ::strrchr( path, kPathDelimiterChar );
if ( pathEnd )
{
pathEnd++;
*pathEnd = 0;
}
else
*path = 0;
thisLine = lineBuff;
if ( ::fgets( lineBuff, lineBuffSize, weightings ) != NULL )
{
lineCount++;
thisLine = ::TrimLeft( lineBuff );
if ( 0 != ::strncmp(thisLine,"*PLAY-LIST*",11) )
{
//qtss_printf("- Playlist file missing *PLAY-LIST* identifier as first line:\n");
//qtss_printf(" %s%s\n", basePath, fname);
pickErr = kPickerPopulateBadFormat;
}
}
if ( !pickErr )
{
do
{
next = lineBuff;
if ( ::fgets( lineBuff, lineBuffSize, weightings ) == NULL )
break;
// qtss_printf("line = %s\n", lineBuff);
lineCount++;
next = ::TrimLeft( lineBuff );
if ( *next == '#' )
{
// it's a comment - just toss
//if ( *next )
// qtss_printf( "comment: %s" , &lineBuff[1] );
}
else if (*next == '+') // a list
{
next = ::TrimLeft( next+1 ); // skip past + include
if ( *next == '"' ) // get the name from the next part of the buff
next = ::GetQuotedWord( wordBuff, next, wordBuffSize );
else
next = ::GetWord( wordBuff, next, wordBuffSize );
// recusively populate from the include file.
pickErr = PopulatePickerFromFile( picker, wordBuff, path, ldList );
if ( pickErr )
{
DisplayPickerErr( pickErr, "Playlist Include failed", fname, lineCount, lineBuff );
pickErr = kPickerPopulateNoErr;
}
}
else if ( *next )
{
char numBuff[32];
char expandedFileName[kMaxPickerPath];
int weight = 10; // default weight is 10
// get the movie file name
if ( *next == '"' )
next = ::GetQuotedWord( wordBuff, next, wordBuffSize );
else
next = ::GetWord( wordBuff, next, wordBuffSize );
if (*wordBuff)
{
#if kPartialPathBeginsWithDelimiter
if ( PathIsAbsolute(wordBuff) )
{
char *wordStart = wordBuff;
if ( *path )
wordStart++;
// full or partial path to the movie
::strcpy( expandedFileName, path );
::strcat( expandedFileName, wordStart );
}
#else
if ( !PathIsAbsolute(wordBuff) )
{
// it's a partial path..
// cat the path and fname to form the
// full or partial path to the movie
::strcpy( expandedFileName, path );
::strcat( expandedFileName, wordBuff );
}
#endif
else
{ // it's an absolute path..
::strcpy( expandedFileName, wordBuff );
}
// then get the weighting ( if supplied )
next = ::GetWord( numBuff, next, 32 );
if ( *numBuff )
weight = ::atoi(numBuff);
// qtss_printf("expanded file name = %s\n", expandedFileName);
if (::IsDir(expandedFileName))
pickErr = ::PopulatePickerFromDir(picker, expandedFileName, weight);
else if ( !picker->AddToList( expandedFileName, weight ) )
pickErr = kPickerPopulateNoMem;
}
}
} while ( feof( weightings ) == 0 && pickErr == kPickerPopulateNoErr );
}
// remove ourselves from the list
ldList->RemoveNode( ldNode );
}
// only report unreported errors.
if ( ldList && ldList->GetNumNodes() == 0 && pickErr )
::DisplayPickerErr( pickErr, "Playlist error", fname, lineCount, NULL );
if ( ldNode )
delete ldNode; // node deletes element
else if ( ldElement )
delete ldElement;
if ( weightings )
(void)::fclose( weightings );
if ( ldList && ldList->GetNumNodes() == 0 )
{
// all done now!
delete ldList;
ldList = NULL;
}
return pickErr;
}
int PopulatePickerFromDir( PlaylistPicker* picker, char* dirPath, int weight )
{
static char expandedFileName[kMaxPickerPath]; // static so we don't build up the stack frame on recursion
int pickErr = 0;
if (dirPath != NULL)
strcpy(expandedFileName, dirPath);
#ifdef __Win32__
WIN32_FIND_DATA findData;
HANDLE findResultHandle;
Bool16 keepSearching = true;
int len = strlen(expandedFileName);
if (expandedFileName[len - 1] != kPathDelimiterChar)
{
expandedFileName[len] = kPathDelimiterChar;
expandedFileName[len+1] = 0;
len++;
}
strcat(expandedFileName, "*");
findResultHandle = ::FindFirstFile( expandedFileName, &findData);
if ( NULL == findResultHandle || INVALID_HANDLE_VALUE == findResultHandle )
{
//qtss_printf( "FindFirstFile( \"%s\" ): gle = %"_U32BITARG_"\n", searchPath, GetLastError() );
return 0;
}
while ( (pickErr == 0) && keepSearching )
{
expandedFileName[len] = 0; // retruncate name
if (findData.cFileName[0] != '.') // ignore anything beginning with a "."
{
strcat(expandedFileName, findData.cFileName);
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
pickErr = PopulatePickerFromDir(picker, NULL, weight);
else if ( !picker->AddToList( expandedFileName, weight ) )
pickErr = kPickerPopulateNoMem;
}
keepSearching = FindNextFile( findResultHandle, &findData );
}
#else
DIR* dir;
struct dirent* entry;
int len = strlen(expandedFileName);
if (expandedFileName[len - 1] != kPathDelimiterChar)
{
expandedFileName[len] = kPathDelimiterChar;
expandedFileName[len+1] = 0;
len++;
}
dir = opendir(expandedFileName);
if (dir == NULL)
return kPickerPopulateFileError;
do {
entry = readdir(dir);
if (entry == NULL) break;
if (entry->d_name[0] == '.') // ignore anything beginning with a "."
continue;
if (len + strlen(entry->d_name) < kMaxPickerPath)
{
strcat(expandedFileName, entry->d_name);
#if __solaris__ || __sgi__ || __osf__ || __hpux__
if (::IsDir(expandedFileName))
#else
if ((entry->d_type & DT_DIR) != 0)
#endif
pickErr = PopulatePickerFromDir(picker, NULL, weight);
else if ( !picker->AddToList( expandedFileName, weight ) )
pickErr = kPickerPopulateNoMem;
}
expandedFileName[len] = 0; // retruncate name
} while (pickErr == 0);
//close the directory back up
(void)::closedir(dir);
#endif
return pickErr;
}