//@copyright_begin // ================================================================ // Copyright Notice // Copyright (C) 1998-2004 by Joe Linoff // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // IN NO EVENT SHALL JOE LINOFF BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // // Comments and suggestions are always welcome. // Please report bugs to http://ccdoc.sourceforge.net/ccdoc // ================================================================ //@copyright_end #include "log.h" #include "phase1_scanner.h" #include // ================================================================ // This variable allows the header version // to be queried at runtime. // ================================================================ namespace { char ccdoc_rcsid[] = "$Id: phase1_scanner_doc.cc,v 1.5 2004/09/30 04:16:07 jlinoff Exp $"; } // ================================================================ // Constructor. // ================================================================ ccdoc::phase1::scanner_doc::scanner_doc(scanner& scan,switches& sw) : m_scanner(scan), m_mode(SHORT), m_sw(sw) { } // ================================================================ // Destructor. // ================================================================ ccdoc::phase1::scanner_doc::~scanner_doc() { } // ================================================================ // Empty? // ================================================================ bool ccdoc::phase1::scanner_doc::empty() const { return m_comment.empty(); } // ================================================================ // Format // ================================================================ const char* ccdoc::phase1::scanner_doc::format(char* token,int max) { char lineno[64]; sprintf(lineno,"%d",m_scanner.get_lineno() - 1); vector tokens; m_comment.add_file(m_scanner.get_file()); m_comment.add_lineno(lineno); m_comment.get(tokens); vector::reverse_iterator itr = tokens.rbegin(); for(;itr!=tokens.rend();++itr) { m_scanner.put_token(*itr); } m_scanner.get_token(); token[0] = '@'; token[1] = '{'; token[2] = 0; return token; } // ================================================================ // Scan a single argument. // ================================================================ bool ccdoc::phase1::scanner_doc::scan_1arg(const char* pline, string& arg, const char* directive, const char* argid, bool report_errors) { // Skip white space. for(;*pline!=0 && (*pline==' ' || *pline=='\t');++pline) ; if( 0 != *pline ) { arg = pline; return true; } if(report_errors) { s_log.warning() << "Ignored the " << directive << " directive because argument " << argid << " is missing at line " << m_scanner.get_lineno()-1 << " in " << m_scanner.get_file() << ".\n" << s_log.enable(); } return false; } // ================================================================ // Parse the ccdoc directive with 2 arguments, like @param // and @exception. // ================================================================ bool ccdoc::phase1::scanner_doc::scan_2args(char* pstr, string& arg1, string& arg2, const char* directive, const char* arg1id, const char* arg2id, bool arg2_required) { // Skip white space. for(;*pstr!=0 && (*pstr==' ' || *pstr=='\t');++pstr); if( 0 == *pstr ) { s_log.warning() << "Ignored the " << directive << " directive because argument " << arg1id << " is missing at line " << m_scanner.get_lineno()-1 << " in " << m_scanner.get_file() << ".\n\tThe correct specification is " << directive << " " << arg1id << " " << arg2id << ".\n" << s_log.enable(); return false; } char* arg1_beg = pstr; // Skip to next w/s unless the user specified an HTML // directive explicitly, this is determined by looking // at the first character. If it is a '<', pass everything // back as one argument. if( '<' == *pstr ) { for(;*pstr!=0;++pstr); } else { for(;*pstr!=0 && *pstr!=' ' && *pstr!='\t';++pstr); } if( 0 == *pstr ) { if( arg2_required ) { s_log.warning() << "Ignored the " << directive << " directive because argument " << arg2id << " is missing at line " << m_scanner.get_lineno()-1 << " in " << m_scanner.get_file() << ".\n\tThe correct specification is " << directive << " " << arg1id << " " << arg2id << ".\n" << s_log.enable(); return false; } arg1 = arg1_beg; arg2 = ""; return true; } char* arg1_end = pstr; *arg1_end = 0; // Skip white space. for(++pstr;*pstr!=0 && (*pstr==' ' || *pstr=='\t');++pstr); if( 0 == *pstr) { // Trailing w/s but no name. // Assume that there is no arg. arg1 = arg1_beg; arg2 = ""; return true; } // Set the end of the second argument. // Trim the trailing w/s. char* arg2_beg = pstr; char* arg2_end = arg2_beg; for(;*arg2_end;++arg2_end); for(--arg2_end;*arg2_end<=' ';--arg2_end); ++arg2_end; *arg2_end = 0; // Set the arguments. arg1 = arg1_beg; arg2 = arg2_beg; return true; } // ================================================================ // Is this a directive? // Derived from code submitted by bzoe 10/18/01. // ================================================================ bool ccdoc::phase1::scanner_doc::is_directive(const char* pstr1, const char* pstr2, bool null_term) const { for(;*pstr1;++pstr1,++pstr2) { if( *pstr1 != *pstr2 ) { return false; } } if( *pstr2 == ' ' || *pstr2 == '\t' ) return true; // Accept a terminating NULL? if( null_term && *pstr2 == 0 ) return true; return false; } // ================================================================ // Parse line. // ================================================================ void ccdoc::phase1::scanner_doc::parse_line(char* line) { // ================================================ // Check for ccdoc directives. // Skip the leading whitespace to look for a '@' // character. // ================================================ string pkgdoctid; // Used for the @pkgdoctid directive. char* pline = line; for(;*pline && (*pline==' ' || *pline=='\t');++pline) ; if('@' == pline[0]) { // ================================================ // ccdoc directive: '@@' // ================================================ if('@' == pline[1]) { // '@@' directive. // Convert < -> < // Convert > -> > // Convert & -> & const char* src = &pline[2]; static char line1[65536]; // maximum line length char* dst = line1; for(;*src;++src) { if( '<' == *src ) { *dst++ = '&'; *dst++ = 'l'; *dst++ = 't'; *dst++ = ';'; } else if( '>' == *src ) { *dst++ = '&'; *dst++ = 'g'; *dst++ = 't'; *dst++ = ';'; } else if( '&' == *src ) { *dst++ = '&'; *dst++ = 'a'; *dst++ = 'm'; *dst++ = 'p'; *dst++ = ';'; } else { *dst++ = *src; } } *dst = 0; add_line(line1); } // ================================================ // ccdoc directives: '@{' or '@}' // ================================================ else if('{' == pline[1] || '}' == pline[1]) { // Silently ignored, it is used the '//' processing. } // ================================================ // ccdoc directive: '@$' - same as @link // ================================================ else if('$' == pline[1] && ( ' ' == pline[2] || '\t' == pline[2] ) ) { pline += 2; string arg1; string arg2; if(scan_2args(pline,arg1,arg2,"@$","","",false)) { if( arg2.size() == 0 ) arg2 = arg1; add_line("@link"); add_line(arg1.c_str()); add_line(arg2.c_str()); } } // ================================================ // ccdoc directive: '@author' // ================================================ else if(is_directive("author",&pline[1])) { pline+=7; if( m_mode == SHORT ) m_mode = LONG; string arg; if( scan_1arg(pline,arg,"@author","") ) m_comment.add_author(arg); } // ================================================ // ccdoc directive: '@deprecated' // ================================================ else if(is_directive("deprecated",&pline[1],true)) { pline += 11; m_mode = DEPRECATED; string arg; if( scan_1arg(pline,arg,"@deprecated","",false) ) parse_comment_line(arg.c_str()); // Issue 0123 //m_comment.add_deprecated(arg); } // ================================================ // ccdoc directive: '@exception' // ================================================ else if(is_directive("exception",&pline[1])) { pline += 10; m_mode = EXCEPTION; string arg1; string arg2; if(scan_2args(pline,arg1,arg2,"@exception","","",false)) { if(arg2.size()) { m_comment.add_new_exception(arg1,arg2); } else { m_comment.add_new_exception(arg1); } } } // ================================================ // ccdoc directive: '@link' // ================================================ // This is now handled in parse_comment_line. else if(is_directive("link",&pline[1])) { pline += 5; string arg1; string arg2; if(scan_2args(pline,arg1,arg2,"@link","","",false)) { if( arg2.size() == 0 ) arg2 = arg1; // Search for an embedded @endlink directive. // If found, split it out. string arg3; if( arg2 == "@endlink" ) { // The simple case. arg2 = arg1; } else { string::size_type f = arg2.find("@endlink"); if( f && f != string::npos) { arg3 = arg2; arg2 = arg3.substr(0,f-1); size_t last = arg2.find_last_not_of(" \t\r\n"); if( string::npos != last ) { arg2 = arg2.substr(0,last+1); } } // Look for suffix characters. // @link a1 @endlink suffix characters. // ^ ^ // | +--- f+8 // +----------- f if( (f+8) < arg3.size() ) { string::size_type sz = arg3.size()-8; arg3 = arg3.substr(f+8,sz); } else { arg3 = ""; } } add_line("@link"); add_line(arg1.c_str()); add_line(arg2.c_str()); if( arg3.size() ) add_line(arg3.c_str()); } } // ================================================ // ccdoc directive: '@param' // ================================================ else if(is_directive("param",&pline[1])) { pline += 6; m_mode = PARAM; string arg1; string arg2; if(scan_2args(pline,arg1,arg2,"@param","","",false)) { if(arg2.size()) { m_comment.add_new_param(arg1,arg2); } else { m_comment.add_new_param(arg1); } } } // ================================================ // ccdoc directive: '@pkg' // ================================================ else if(is_directive("pkg",&pline[1])) { pline += 4; if( m_mode == SHORT ) m_mode = LONG; string arg; if( scan_1arg(pline,arg,"@pkg","") ) { // Break the argument down into '.' separated // package entries. if( m_comment.get_pkg().size() ) { report_multiply_defined_error("@pkg"); } else if( m_comment.get_pkgdoc().size() ) { // pkg and pkgdoc cannot simultaneously exist // in the same ccdoc comment. s_log.warning() << "Illegal declaration, @pkgdoc and @pkg are " << "mutually exclusive, @pkg will be ignored at line " << m_scanner.get_lineno()-1 << " in " << m_scanner.get_file() << ".\n" << s_log.enable(); } else { vector vec; parse_pkg_path(arg,"@pkg",vec); vector::iterator i = vec.begin(); for(;i!=vec.end();++i) { m_comment.add_pkg(*i); } } } } // ================================================ // ccdoc directive: '@pkgdoc' // ================================================ else if(is_directive("pkgdoc",&pline[1])) { pline += 7; if( m_mode == SHORT ) m_mode = LONG; string arg1; string arg2; if( scan_2args(pline,arg1,arg2,"@pkgdoc","","[]",false) ) { // Break the argument down into '.' separated // package entries. if( m_comment.get_pkgdoc().size() ) { report_multiply_defined_error("@pkgdoc"); } else if( m_comment.get_pkg().size() ) { // pkg and pkgdoc cannot simultaneously exist // in the same ccdoc comment. s_log.warning() << "Illegal declaration, @pkgdoc and @pkg are " << "mutually exclusive, @pkgdoc will be ignored at line " << m_scanner.get_lineno()-1 << " in " << m_scanner.get_file() << ".\n" << s_log.enable(); } else if( arg1.size() ) { // If the user specified a URL for the @pkgdoc directive, // insert it first so that it is easy to find. if( arg2.size() ) { // Issue 0025 // There is a special form of the @pkgdoc directive // that allows the user to specify their own URL // for the package page. This must be used very carefully // because the links to child entities may be lost. string urlid = "@url"; m_comment.add_pkgdoc( urlid ); m_comment.add_pkgdoc( arg2 ); } vector vec; parse_pkg_path(arg1,"@pkgdoc",vec); vector::iterator i = vec.begin(); for(;i!=vec.end();++i) { m_comment.add_pkgdoc(*i); } } } } // ================================================ // ccdoc directive: '@pkgdoctid' // This must appear after an @pkgdoc directive. // ================================================ else if(is_directive("pkgdoctid",&pline[1])) { pline += 10; if( m_mode == SHORT ) m_mode = LONG; string arg; if( scan_1arg(pline,arg,"@pkgdoctid","") ) { if( pkgdoctid.size() ) { report_multiply_defined_error("@pkgdoctid"); } else if( m_comment.get_pkgdoc().size() == 0 ) { // @pkgdoc must be specified first. s_log.warning() << "Illegal declaration, @pkgdoc must be specified before " << "@pkgdoctid at line " << m_scanner.get_lineno()-1 << " in " << m_scanner.get_file() << ".\n" << s_log.enable(); } else { pkgdoctid = arg; m_comment.add_pkgdoc_tid(arg); } } } // ================================================ // ccdoc directive: '@return' // ================================================ else if(is_directive("return",&pline[1],true)) { pline += 7; m_mode = RETURNS; string arg; if( scan_1arg(pline,arg,"@returns","",false) ) parse_comment_line(arg.c_str()); // Issue 0123 //m_comment.add_returns(arg); } // ================================================ // ccdoc directive: '@returns' // ================================================ else if(is_directive("returns",&pline[1],true)) { pline += 8; m_mode = RETURNS; string arg; if( scan_1arg(pline,arg,"@returns","",false) ) parse_comment_line(arg.c_str()); // Issue 0123 } // ================================================ // ccdoc directive: '@see' // ================================================ else if(is_directive("see",&pline[1])) { pline += 4; m_mode = SEE; string arg1; string arg2; if(scan_2args(pline,arg1,arg2,"@see","","[]",false)) { // Make sure that arg2 is numeric. if( arg2.size() ) { if( arg2 != "*" ) { // Make sure that the argument is numeric // unless the user specified the special // case of '*' which means all. const char* p = arg2.c_str(); for(;*p;++p) { if( *p < '0' || *p > '9' ) { s_log.warning() << "Invalid number " << arg2.c_str() << " in @see directive at line " << m_scanner.get_lineno()-1 << " in " << m_scanner.get_file() << " was ignored.\n" << s_log.enable(); arg2 = "0"; break; } } } } else { // The user did not specify an index, set the default // value of zero. arg2 = "0"; } m_comment.add_new_see(arg1,arg2); } } // ================================================ // ccdoc directive: '@source' // ================================================ else if(is_directive("source",&pline[1])) { pline += 7; if( m_mode == SHORT ) m_mode = LONG; string arg; if( scan_1arg(pline,arg,"@source","") ) { if( m_comment.get_source().size() ) { report_multiply_defined_error("@source"); } else { m_comment.add_source(arg); } } } // ================================================ // ccdoc directive: '@since' // ================================================ else if(is_directive("since",&pline[1])) { pline += 6; if( m_mode == SHORT ) m_mode = LONG; string arg; if( scan_1arg(pline,arg,"@since","") ) { if( m_comment.get_source().size() ) { report_multiply_defined_error("@since"); } else { m_comment.add_since(arg); } } } // ================================================ // ccdoc directive: '@suffix' // ================================================ else if(is_directive("suffix",&pline[1],true)) { pline += 7; m_comment.add_suffix(true); } // ================================================ // Issue 0084. // ccdoc directive: '@throws' // Derived from code submitted by bzoe 10/18/01. // ================================================ else if(is_directive("throws",&pline[1])) { pline += 7; m_mode = EXCEPTION; string arg1; string arg2; if(scan_2args(pline,arg1,arg2,"@throws","","",false)) { if(arg2.size()) { m_comment.add_new_exception(arg1,arg2); } else { m_comment.add_new_exception(arg1); } } } // ================================================ // ccdoc directive: '@todo' (Issue 0120) // ================================================ else if(is_directive("todo",&pline[1],true)) { pline += 5; m_mode = TODO; string arg; if( scan_1arg(pline,arg,"@todo","",false) ) parse_comment_line(arg.c_str()); // Issue 0123 //m_comment.add_returns(arg); } // ================================================ // ccdoc directive: '@version' // ================================================ else if(is_directive("version",&pline[1])) { pline += 8; if( m_mode == SHORT ) m_mode = LONG; string arg; if( scan_1arg(pline,arg,"@version","") ) { if( m_comment.get_version().size() ) { report_multiply_defined_error("@version"); } else { m_comment.add_version(arg); } } } else { // ================================================ // Although this line looks like a ccdoc directive, // it isn't. Add it the the current mode collection. // ================================================ add_line(line); } } else { parse_comment_line(line); } } // ================================================================ // Issue 0090: // This line is not ccdoc directive, add it the the current mode // collection unless it has embedded {@link ... } or @link/@endlink // directives. // ================================================================ void ccdoc::phase1::scanner_doc::parse_comment_line(const char* line) { vector new_lines; const char* ps = line; const char* pend = ps; // Now process the expected stuff. for(ps=line;*ps;++ps) { // "{@link ... }" if( '{' == ps[0] && '@' == ps[1] && 'l' == ps[2] && 'i' == ps[3] && 'n' == ps[4] && 'k' == ps[5] && ( ' ' == ps[6] || '\t' == ps[6] ) ) { // It looks we have an embedded link, now scan ahead to // the terminating right brace. unsigned num_ws = 1; const char* pbeg = &ps[1]; // point to '@link' const char* ps1 = pbeg; size_t len2 = 0; for(;*ps1 && *ps1 != '{' && *ps1 != '}' ;++ps1,++len2) { if( *ps1 == ' ' || *ps1 == '\t' ) ++num_ws; } if( *ps1 == '}' && num_ws>1 ) { // We definitely have an embedded link. // Create a new line from the end of the previous link // and the beginning of this one. string new_line; // Figure out the length of the prefix string. size_t len1 = 0; for(const char* ps2=pend;ps2!=ps;++ps2,++len1) ; if( len1 ) { // Special case handling to work around // paragraph recognition. if ( len1 == 1 && *pend == ' ' ) new_line = " "; // convert one space to two spaces else new_line.assign(pend,len1); new_lines.push_back(new_line); // prefix text } new_line.assign(pbeg,len2); // trailing w/s is handled later new_lines.push_back(new_line); // @link directive // Add in the new @link line. len1 = 0; ps = ps1; // The for() increment will fix this. pend = ps+1; // point just past the trailing right brace } } // "@link ... @endlink" else if( '{' != ps[0] && '@' == ps[1] && 'l' == ps[2] && 'i' == ps[3] && 'n' == ps[4] && 'k' == ps[5] && ( ' ' == ps[6] || '\t' == ps[6] ) ) { // It looks we have an embedded link, now scan ahead to // the terminating @endlink or new line. The new line // is for backward compatibility with ccdoc. The @endlink // is for forward compatibility with doxygen. unsigned num_ws = 1; const char* pbeg = &ps[1]; // point to '@link' const char* ps1 = pbeg; const char* pend1 = pbeg; size_t len2 = 0; for(;*ps1;++ps1,++len2,++pend1) { if( *ps1 == ' ' || *ps1 == '\t' ) ++num_ws; else if( '@' == ps1[0] && 'e' == ps1[1] && 'n' == ps1[2] && 'd' == ps1[3] && 'l' == ps1[4] && 'i' == ps1[5] && 'n' == ps1[6] && 'k' == ps1[7] && ( ' ' == ps1[8] || '\t' == ps1[8] || 0 == ps1[8] ) ) { pend1 = &ps1[7]; break; } } if( num_ws>1 ) { // We definitely have an embedded link. // Create a new line from the end of the previous link // and the beginning of this one. string new_line; // Figure out the length of the prefix string. size_t len1 = 0; for(const char* ps2=pend;ps2!=ps;++ps2,++len1) ; if( len1 ) { // Special case handling to work around // paragraph recognition. if ( len1 == 1 && *pend == ' ' ) new_line = " "; // convert one space to two spaces else new_line.assign(pend,len1); new_lines.push_back(new_line); // prefix text } new_line.assign(pbeg,len2); // trailing w/s is handled later new_lines.push_back(new_line); // @link directive // Add in the new @link line. len1 = 0; ps = pend1; // The for() increment will fix this. pend = ps+1; // point just past the trailing right brace } } } // Handle the suffix characters. if( new_lines.size() ) { // Get the trailing characters: // * Link {@link foo::bar xx} These are the suffix characters. // ^ if( pend != ps ) { string new_line; size_t len1 = 0; for(const char* ps2=pend;ps2!=ps;++ps2,++len1) ; if( len1 ) { // Special case handling to work around // paragraph recognition. if ( len1 == 1 && *pend == ' ' ) new_line = " "; else new_line.assign(pend,len1); new_lines.push_back(new_line); // suffix text } } static char s_line2[65536]; // maximum line length vector::iterator itr = new_lines.begin(); for(;itr!=new_lines.end();++itr) { strcpy(s_line2,itr->c_str()); parse_line(s_line2); } } else { add_line(line); } } // ================================================================ // Add line. // ================================================================ void ccdoc::phase1::scanner_doc::add_line(const char* line) { // If a line is NULL, it means that the user // wants a paragraph separator. It is stored // as a space. string desc; if( !line || *line == 0 ) { desc = " "; // This is where blank lines are defined. } else { desc = line; if( m_sw.jdsds() && m_mode == SHORT ) { // Issue 0089: Extract short comments the javadoc way. size_t sz = 0; for(const char* p=line;*p;++p,++sz) { if( *p == '.' ) { // In javadoc, a short description is terminated by // a period that is followed by a SPACE, TAB, NEWLINE. const char* pnext = p+1; // Issue 0167 if( *pnext == 0 || // same as new line *pnext == ' ' || *pnext == '\t' || *pnext == '\r' || *pnext == '\n' ) { ++sz; // include the period. string short_desc; short_desc = desc.substr(0,sz); m_comment.add_short_desc(short_desc); if( *pnext ) { // Handle this case: // Short description. Start of long description. size_t first = sz+1; for(sz=0;*pnext;++pnext) sz++; string long_desc; long_desc.assign(desc,first,sz); if( long_desc.size() ) { m_comment.add_long_desc(long_desc); } } m_mode = LONG; return; } } } } } switch(m_mode) { case DEPRECATED: m_comment.add_deprecated (desc); break; case EXCEPTION: m_comment.add_exception_desc (desc); break; case LONG: m_comment.add_long_desc (desc); break; case PARAM: m_comment.add_param_desc (desc); break; case RETURNS: m_comment.add_returns (desc); break; case SHORT: m_comment.add_short_desc (desc); break; case SEE: m_comment.add_see_desc (desc); break; case TODO: m_comment.add_todo (desc); break; } } // ================================================================ // Add line. // ================================================================ void ccdoc::phase1::scanner_doc::add_line(const char* line, vector& vec) { // This is where we handle embedded blank lines. if(*line) vec.push_back(line); else vec.push_back(" "); } // ================================================================ // Report multiply defined entities. // ================================================================ void ccdoc::phase1::scanner_doc::report_multiply_defined_error(const char* directive) const { s_log.warning() << "Directive " << directive << " is multiply defined at line " << m_scanner.get_lineno()-1 << " in " << m_scanner.get_file() << ", the first definition will be used.\n" << s_log.enable(); } // ================================================================ // Parse a pkg or pkgdoc path. // ================================================================ void ccdoc::phase1::scanner_doc::parse_pkg_path(string& arg, const char* directive, vector& vec) { // Look for '::' and '.' separators. // The following are legal: // A.B.C // A::B::C // A::B.C <-- not recommended static char token[65536]; ::strcpy(token,arg.c_str()); vec.clear(); char* b = token; char* e = b; for(;*e;++e) { if( ':' == *e ) { ++e; if( ':' == *e ) { --e; // back up to the first colon. *e = 0; // set the end of the previous token ++e; // point to the start of the next token if(!parse_pkg_path_entry(b,directive)) { vec.clear(); return; } vec.push_back(b); *e = '.'; b = e+1; } } else if( '.' == *e ) { *e = 0; if(!parse_pkg_path_entry(b,directive)) { vec.clear(); return; } vec.push_back(b); *e = '.'; b = e+1; } } if(!parse_pkg_path_entry(b,directive)) { vec.clear(); return; } vec.push_back(b); } // ================================================================ // Parse a pkg or pkgdoc path. // ================================================================ bool ccdoc::phase1::scanner_doc::parse_pkg_path_entry(char* token, const char* directive) { // Trim off trailing w/s. char* ptr = token; for(;*ptr;++ptr); // First get to the end of the string. for(ptr--;ptr>token;--ptr) { if( ' ' != *ptr && '\t' != *ptr && '\n' != *ptr ) { ptr++; *ptr = 0; break; } } if(*token == 0) { // We found a zero length entry. // Report it as a warning and ignore it. s_log.warning() << "Illegal zero length subpkg name found in the " << directive << " directive at line " << m_scanner.get_lineno()-1 << " in " << m_scanner.get_file() << ", the directive was ignored.\n" << s_log.enable(); return false; } return true; }