EXTERN_C int climain( int argc, char **argv ) { /* Set up the diagnostic message handler, using the console's * `stderr' stream for notifications... */ dmh_init( DMH_SUBSYSTEM_TTY, *argv++ ); /* TODO: insert code here, to interpret any OPTIONS specified * on the command line. */ /* Interpret the `action keyword', specifying the action to be * performed on this invocation... */ int action = action_code( *argv ); if( action < 0 ) /* * The specified action keyword was invalid; * force an abort through a DMH_FATAL notification... */ dmh_notify( DMH_FATAL, "%s: unknown action keyword\n", *argv ); /* If we get to here, then the specified action identifies a * valid operation; load the package database, according to the * local `profile' configuration, and invoke the operation. */ const char *dfile; pkgXmlDocument dbase( dfile = xmlfile( "profile" ) ); if( dbase.IsOk() ) { /* We successfully loaded the basic settings... * The configuration file name was pushed on to the heap, * by xmlfile(); we don't need that any more, (because it * is reproduced within the database image itself), so * free the heap copy, to avoid memory leaks. */ free( (void *)(dfile) ); /* Merge all package lists, as specified in the "repository" * section of the "profile", into the XML database tree... */ if( dbase.BindRepositories( action == ACTION_UPDATE ) == NULL ) /* * ...bailing out, on an invalid profile specification... */ dmh_notify( DMH_FATAL, "%s: invalid application profile\n", dbase.Value() ); #if 0 /* If the requested action was "update", then we've already done it, * as a side effect of binding the cached repository catalogues... */ if( action != ACTION_UPDATE ) { /* ...otherwise, we still need to schedule and execute the action request... * * so, schedule the specified action for each additionally specified command * line argument, (each of which is assumed to represent a package name)... */ while( --argc ) dbase.Schedule( (unsigned long)(action), *++argv ); /* ...and finally, execute all scheduled actions... */ dbase.ExecuteActions(); } #endif /* If we get this far, then all actions completed successfully; * we are done... */ return EXIT_SUCCESS; } /* If we get to here, then the package database load failed; * once more, we force an abort through a DMH_FATAL notification... * * Note: although dmh_notify does not return, in the DMH_FATAL case, * GCC cannot know this, so we pretend that it gives us a return value, * to avoid a possible warning about reaching the end of a non-void * function without a return value assignment... */ return dmh_notify( DMH_FATAL, "%s: cannot load configuration\n", dfile ); }
static inline const char *pkgResolvedName( pkgXmlNode *rel, const char *tag, const char *ext ) { /* Local helper function to resolve the mapping from a released * package name, as identified from the XML release element "rel", * to its corresponding source or licence package name, according * to the selection of "source" or "licence" specified by "tag", * with "ext" passed a "src" or "lic" respectively. */ const char *refname; const char *retname = NULL; /* First, we retrieve the released package name... */ if( (refname = pkgArchiveName( rel, release_key, 1 )) != NULL ) { /* ...and if successful, look for an explicit reference to * the source or licence package, embedded within the release * specification itself. */ if( (retname = pkgArchiveName( rel, tag, 0 )) == NULL ) { /* When this fails to identify the required mapping, * then we look for a generic reference, defined for * the containing package. */ pkgXmlNode *enc = rel->GetParent(); /* A generic reference may be placed at any nesting * level, between the enclosing package element and * the release to which it relates; thus, starting * at the first enclosing level... */ rel = NULL; while( enc != NULL ) { /* ...enumerate reference specifications of the * appropriate type, examining all children of * the enclosing element. */ unsigned matched = 0; pkgXmlNode *child = enc->GetChildren(); while( child != NULL ) { /* We have a child, which we have not examined... */ if( child->IsElementOfType( tag ) ) { /* ...and it is of the required "tag" type. */ if( matched++ ) /* * We already had a candidate match, so we * diagnose but otherwise this duplicate... */ dmh_notify( DMH_WARNING, "redundant %s specification ignored\n", tag ); else /* This is the first candidate match found, * so we accept it. */ rel = child; } /* Continue examining child elements, until no more * are present at the current nesting level. */ child = child->GetNext(); } /* When we've completed the examination of all children * at a given nesting level, without finding a matching * specification, and that level is still within the * enclosing package element... */ if( (rel == NULL) && ! enc->IsElementOfType( package_key ) ) /* * ...then we extend the search to the next enclosing * level of nesting... */ enc = enc->GetParent(); else /* ...otherwise, we abandon the search. */ enc = NULL; } /* If we've searched all available nesting levels, * and failed to locate the requisite specification... */ if( rel == NULL ) { /* ...then we assume that the requisite tarname * is identical to the release tarname, with the * appropriate "ext" substitution for the package * class identification... */ pkgSpecs resolved( refname ); resolved.SetComponentClass( ext ); /* * ...so, having made the substitution, * we return the resultant tarname, noting * that this automatically allocates space * on the heap, for the returned string. */ return resolved.GetTarName(); } else /* We did find a mappingspecification, so we * extract a tarname template from it. */ retname = rel->GetPropVal( tarname_key, NULL ); } else if( strcmp( retname, value_none ) == 0 ) /* * The package is virtual, or an explicit mapping * specification indicates that there is no related * source or licence package; return NULL to advise * the caller of this. */ return NULL; /* If we get to here, we found a mapping specification; * it may be a template, so resolve any substitutions which * it must inherit from the released package tarname, again * noting that this allocates heap memory for the result. */ retname = pkgAssociateName( retname, refname ); } /* Finally, how ever we resolved the mapping, we return * the result. */ return retname; }
static const char *pkgArchiveName( pkgXmlNode *rel, const char *tag, unsigned opt ) { /* Local helper to establish actual release file names... * applicable only to XML "release" elements. */ if( ! rel->IsElementOfType( release_key ) ) { /* The XML element type name is not "release"; identify it... */ const char *reftype; if( (reftype = rel->GetName()) == NULL ) /* * ...or classify as "unknown", when given a NULL element. */ reftype = value_unknown; /* Complain that this XML element type is invalid, in this context... */ dmh_control( DMH_BEGIN_DIGEST ); dmh_notify( DMH_ERROR, "internal package specification error\n" ); dmh_notify( DMH_ERROR, "can't get 'tarname' for non-release element %s\n", reftype ); dmh_notify( DMH_ERROR, "please report this to the package maintainer\n" ); dmh_control( DMH_END_DIGEST ); /* ...and bail out, telling the caller that no archive name is available... */ return NULL; } /* Given a package release specification... * First check that it relates to a real package, rather than to * a virtual "meta-package"; such meta-packages exist solely as * containers for requirements specifications, and have no * associated archive. */ pkgXmlNode *pkg = rel->GetParent(); while( (pkg != NULL) && ! pkg->IsElementOfType( package_key ) ) pkg = pkg->GetParent(); /* FIXME: we should probably provide some error handling here, * to diagnose release elements without any package association; * (these would be identified by pkg == NULL). */ if( pkg != NULL ) { /* We found the package association... * Check its 'class' attribute, if any, and if classified as * 'virtual', return the archive association as "none". */ const char *package_class = pkg->GetPropVal( class_key, NULL ); if( (package_class != NULL) && (strcmp( package_class, value_virtual ) == 0) ) return value_none; } /* The given release specification relates to a real package... * Determine the archive name for the tarball to be processed; this * is retrieved from a child XML element with name specified by "tag"; * by default, if "opt" is non-zero, it is the canonical "tarname" * assigned to the release element itself, unless an alternative * specification is provided; if "opt" is zero, no default is * assumed, and the return value is NULL if no alternative * specification is provided. */ unsigned matched = 0; pkgXmlNode *dl = rel->GetChildren(); while( dl != NULL ) { /* Visit all children of the release specification element, * checking for the presence of an expected specification... */ if( dl->IsElementOfType( tag ) ) { /* Found one; ensure it is the only one... */ if( matched++ ) /* * ...else emit a warning, and ignore this one... */ dmh_notify( DMH_WARNING, "%s: archive name reassignment ignored\n", rel->GetPropVal( tarname_key, value_unknown ) ); else /* ...ok; this is the first "tag" specification, * accept it as the non-default source of the release's * "tarname" property. */ rel = dl; } /* Continue, until all children have been visited. */ dl = dl->GetNext(); } /* "rel" now points to the XML element having the appropriate * "tarname" specification; return a pointer to it's value. */ return (opt || matched) ? rel->GetPropVal( tarname_key, NULL ) : NULL; }
void pkgRepository::GetPackageList( const char *dname ) { /* Helper to retrieve and recursively process a named package list. * * FIXME: having made this recursively process multiple catalogues, * potentially from multiple independent repositories, we may have * introduced potential for catalogue name clashes; we need to add * name hashing in the local catalogue cache, to avoid conflicts. */ if( dname != NULL ) { const char *dfile; if( (dfile = xmlfile( dname )) != NULL ) { /* Check for a locally cached copy of the "package-list" file... */ if( force_update || (access( dfile, F_OK ) != 0) ) { /* When performing an "update", or if no local copy is available... * Force a "sync", to fetch a copy from the public host. */ dmh_printf( "Update catalogue: %s.xml\n", dname ); owner->SyncRepository( dname, repository ); } /* We SHOULD now have a locally cached copy of the package-list; * attempt to merge it into the active profile database... */ pkgXmlDocument merge( dfile ); if( merge.IsOk() ) { /* We successfully loaded the XML catalogue; refer to its * root element... */ if( pkgOptions()->Test( OPTION_VERBOSE ) > 1 ) dmh_printf( "Load catalogue: %s.xml\n", dname ); pkgXmlNode *catalogue, *pkglist; if( (catalogue = merge.GetRoot()) != NULL ) { /* ...read it, selecting each of the "package-collection" * records contained within it... */ pkglist = catalogue->FindFirstAssociate( package_collection_key ); while( pkglist != NULL ) { /* ...and append a copy of each to the active profile... */ dbase->LinkEndChild( pkglist->Clone() ); /* Move on to the next "package-collection" (if any) * within the current catalogue... */ pkglist = pkglist->FindNextAssociate( package_collection_key ); } /* Recursively incorporate any additional package lists, * which may be specified within the current catalogue... */ GetPackageList( catalogue->FindFirstAssociate( package_list_key ) ); } } else { /* The specified catalogue could not be successfully loaded; * emit a warning diagnostic message, and otherwise ignore it. */ dmh_notify( DMH_WARNING, "Load catalogue: FAILED: %s.xml\n", dname ); } /* However we handled it, the XML file's path name in "dfile" was * allocated on the heap; we lose its reference on termination of * this loop, so we must free it to avoid a memory leak. */ free( (void *)(dfile) ); } } }