6 * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
7 * Copyright (C) 2009-2013, MinGW.org Project
10 * Implementation of the main program function, which is invoked by
11 * the command line start-up stub when arguments are supplied; this
12 * causes the application to continue running as a CLI process.
15 * This is free software. Permission is granted to copy, modify and
16 * redistribute this software, under the provisions of the GNU General
17 * Public License, Version 3, (or, at your option, any later version),
18 * as published by the Free Software Foundation; see the file COPYING
19 * for licensing details.
21 * Note, in particular, that this software is provided "as is", in the
22 * hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
23 * even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
24 * PARTICULAR PURPOSE. Under no circumstances will the author, or the
25 * MinGW Project, accept liability for any damages, however caused,
26 * arising from the use of this software.
42 EXTERN_C void cli_setopts( struct pkgopts *opts )
44 /* Start-up hook used to make the table of command line options,
45 * as parsed by the CLI start-up module, available within the DLL.
47 (void) pkgOptions( OPTION_TABLE_ASSIGN, opts );
50 EXTERN_C pkgOpts *pkgOptions( int action, struct pkgopts *ref )
52 /* Global accessor for the program options data table.
54 static pkgOpts *table = NULL;
55 if( action == OPTION_TABLE_ASSIGN )
57 * This is a request to initialise the data table reference;
58 * it is typically called at program start-up, to record the
59 * location of the data table into which the CLI start-up
60 * module stores the result of its CLI options parse.
62 table = (pkgOpts *)(ref);
64 /* In all cases, we return the assigned data table location.
69 class pkgArchiveNameList
71 /* A locally implemented class, managing a LIFO stack of
72 * package names; this is used when processing the source
73 * and licence requests, to track the packages processed,
74 * so that we may avoid inadvertent duplicate processing.
77 const char *name; // name of package tracked
78 pkgArchiveNameList *next; // pointer to next stack entry
81 inline bool NotRecorded( const char *candidate )
83 /* Walk the stack of tracked package names, to determine
84 * if an entry matching "candidate" is already present;
85 * returns false if such an entry exists, otherwise true
86 * to indicate that it may be added as a unique entry.
88 pkgArchiveNameList *check = this;
89 while( check != NULL )
91 /* We haven't walked off the bottom of the stack yet...
93 if( strcmp( check->name, candidate ) == 0 )
95 /* ...and the current entry matches the candidate;
96 * thus the candidate will not be stacked, so we
97 * may discard it from the heap.
99 free( (void *)(candidate) );
101 /* We've found a match, so there is no point in
102 * continuing the search; simply return false to
103 * signal rejection of the candidate.
107 /* No matching entry found yet; walk down to the next
108 * stack entry, if any, to continue the search.
112 /* We walked off the bottom of the stack, without finding
113 * any match; return true to accept the candidate.
117 inline pkgArchiveNameList *Record( const char *candidate )
119 /* Add a new entry at the top of the stack, to record
120 * the processing of an archive named by "candidate";
121 * on entry "this" is the current stack pointer, and
122 * we return the new stack pointer, referring to the
123 * added entry which becomes the new top of stack.
125 pkgArchiveNameList *retptr = new pkgArchiveNameList();
126 retptr->name = candidate; retptr->next = this;
129 inline ~pkgArchiveNameList()
131 /* Completely clear the stack, releasing the heap memory
132 * allocated to record the stacked package names.
134 free( (void *)(name) );
139 static pkgArchiveNameList *pkgProcessedArchives; // stack pointer
141 EXTERN_C int climain( int argc, char **argv )
144 { /* Set up the diagnostic message handler, using the console's
145 * `stderr' stream for notifications, and tagging messages with
146 * the program basename derived from argv[0]...
149 char progname[ 1 + strlen( dmh_progname = strdup( basename( *argv++ ))) ];
150 dmh_init( DMH_SUBSYSTEM_TTY, strcpy( progname, dmh_progname ) );
151 free( dmh_progname );
153 /* Interpret the `action keyword', specifying the action to be
154 * performed on this invocation...
156 int action = action_code( *argv );
159 /* No valid action keyword was found; force an abort
160 * through an appropriate DMH_FATAL notification...
164 * ...viz. the user didn't specify any argument, which
165 * could have been interpreted as an action keyword.
167 dmh_notify( DMH_FATAL, "no action specified\n" );
170 /* ...or, the specified action keyword was invalid.
172 dmh_notify( DMH_FATAL, "%s: unknown action keyword\n", *argv );
175 /* If we get to here, then the specified action identifies a
176 * valid operation; load the package database, according to the
177 * local `profile' configuration, and invoke the operation.
180 if( access( dfile = xmlfile( profile_key ), R_OK ) != 0 )
182 /* The user hasn't provided a custom configuration profile...
184 dmh_notify( DMH_WARNING, "%s: user configuration file missing\n", dfile );
186 /* ...release the memory allocated by xmlfile(), to store its path name,
187 * then try the mingw-get distribution default profile instead.
189 free( (void *)(dfile) );
190 dmh_notify( DMH_INFO, "%s: trying system default configuration\n",
191 dfile = xmlfile( defaults_key ) );
194 pkgXmlDocument dbase( dfile );
197 /* We successfully loaded the basic settings...
198 * The configuration file name was pushed on to the heap,
199 * by xmlfile(); we don't need that any more, (because it
200 * is reproduced within the database image itself), so
201 * free the heap copy, to avoid memory leaks.
203 free( (void *)(dfile) );
205 /* Merge all package lists, as specified in the "repository"
206 * section of the "profile", into the XML database tree...
208 if( dbase.BindRepositories( action == ACTION_UPDATE ) == NULL )
210 * ...bailing out, on an invalid profile specification...
212 dmh_notify( DMH_FATAL, "%s: invalid application profile\n", dbase.Value() );
214 /* If the requested action was "update", then we've already done it,
215 * as a side effect of binding the cached repository catalogues...
217 if( action != ACTION_UPDATE )
219 /* ...otherwise, we need to load the system map...
221 dbase.LoadSystemMap();
223 /* ...initialise any preferences which the user may
224 * have specified within profile.xml...
226 dbase.EstablishPreferences( "cli" );
228 /* ...and invoke the appropriate action handler.
235 * "list" and "show" actions are synonymous;
236 * invoke the info-display handler.
238 dbase.DisplayPackageInfo( argc, argv );
244 * Process the "source" or "licence" request for one
245 * or more packages; begin with an empty stack of names,
246 * for tracking packages as processed.
248 pkgProcessedArchives = NULL;
249 if( pkgOptions()->Test( OPTION_ALL_RELATED ) )
251 /* The "--all-related" option is in effect; ensure
252 * that all dependencies will be evaluated, as if to
253 * perform a recursive reinstall...
255 pkgOptions()->SetFlags( OPTION_ALL_DEPS );
257 * ...then, for each package which is identified on
258 * the command line...
262 * ...schedule a request to install the package,
263 * together with all of its runtime dependencies...
265 dbase.Schedule( ACTION_INSTALL, *++argv );
267 /* ...but DON'T proceed with installation; rather
268 * process the "source" or "licence" request for
269 * each scheduled package.
271 dbase.GetScheduledSourceArchives( (unsigned long)(action) );
275 * The "--all-related" option is NOT in effect; simply
276 * process the "source" or "licence" request exclusively
277 * in respect of each package named on the command line.
279 dbase.GetSourceArchive( *++argv, (unsigned long)(action) );
281 /* Finally, clear the stack of processed package names.
283 delete pkgProcessedArchives;
289 * This is a special case of the upgrade request, for which
290 * no explicit package names have been specified; in this case
291 * we retrieve the list of all installed packages, scheduling
292 * each of them for upgrade...
294 dbase.RescheduleInstalledPackages( ACTION_UPGRADE );
296 /* ...subsequently falling through to complete the action,
297 * using the default processing mechanism; (note that in this
298 * case no further scheduling will be performed, because there
299 * are no additional package names specified in the argv list).
302 /* ...schedule the specified action for each additional command line
303 * argument, (each of which is assumed to represent a package name)...
307 * (Skipped if argv < 2 on entry).
309 dbase.Schedule( (unsigned long)(action), *++argv );
311 /* ...finally, execute all scheduled actions, and update the
312 * system map accordingly.
314 dbase.ExecuteActions();
315 dbase.UpdateSystemMap();
318 /* If we get this far, then all actions completed successfully;
324 /* If we get to here, then the package database load failed;
325 * once more, we force an abort through a DMH_FATAL notification...
327 * Note: although dmh_notify does not return, in the DMH_FATAL case,
328 * GCC cannot know this, so we pretend that it gives us a return value,
329 * to avoid a possible warning about reaching the end of a non-void
330 * function without a return value assignment...
332 return dmh_notify( DMH_FATAL, "%s: cannot load configuration\n", dfile );
335 catch( dmh_exception &e )
337 /* An error occurred; it should already have been diagnosed,
338 * so simply bail out.
346 void pkgActionItem::GetSourceArchive( pkgXmlNode *package, unsigned long category )
348 /* Handle a 'mingw-get source ...' or a 'mingw-get licence ...' request
349 * in respect of the source code or licence archive for a single package.
351 const char *src = package->SourceArchiveName( category );
352 if( (src != NULL) && pkgProcessedArchives->NotRecorded( src ) )
354 if( pkgOptions()->Test( OPTION_PRINT_URIS ) == OPTION_PRINT_URIS )
356 /* The --print-uris option is in effect; this is all
357 * that we are expected to do.
363 { /* The --print-uris option is not in effect; we must at
364 * least check that the source package is available in the
365 * source archive cache, and if not, download it...
367 const char *path_template; flags |= ACTION_DOWNLOAD;
368 DownloadSingleArchive( src, path_template = (category == ACTION_SOURCE)
369 ? pkgSourceArchivePath() : pkgArchivePath()
372 /* ...then, unless the --download-only option is in effect...
374 if( pkgOptions()->Test( OPTION_DOWNLOAD_ONLY ) != OPTION_DOWNLOAD_ONLY )
376 /* ...we establish the current working directory as the
377 * destination where it should be unpacked...
379 char source_archive[mkpath( NULL, path_template, src, NULL )];
380 mkpath( source_archive, path_template, src, NULL );
382 /* ...and extract the content from the source archive.
384 pkgTarArchiveExtractor unpack( source_archive, "." );
386 /* The path_template was allocated on the heap; we are
387 * done with it, so release the memory allocation...
389 free( (void *)(path_template) );
392 /* Record the current archive name as "processed", so we may
393 * avoid any inadvertent duplicate processing.
395 pkgProcessedArchives = pkgProcessedArchives->Record( src );
399 void pkgActionItem::GetScheduledSourceArchives( unsigned long category )
401 /* Process "source" or "licence" requests in respect of a list of
402 * packages, (scheduled as if for installation); this is the handler
403 * for the case when the "--all-related" option is in effect for a
404 * "source" or "licence" request.
406 pkgActionItem *scheduled = this;
407 while( scheduled->prev != NULL ) scheduled = scheduled->prev;
409 /* For each scheduled list entry...
411 while( scheduled != NULL )
413 /* ...process the "source" or "licence" request, as appropriate,
414 * in respect of the associated package...
416 scheduled->GetSourceArchive( scheduled->Selection(), category );
418 * ...then move on to the next entry, if any.
420 scheduled = scheduled->next;
424 void pkgXmlDocument::GetSourceArchive( const char *name, unsigned long category )
426 /* Look up a named package reference in the XML catalogue,
427 * then forward it as a pkgActionItem, for processing of an
428 * associated "source" or "licence" request.
430 pkgXmlNode *pkg = FindPackageByName( name );
431 if( pkg->IsElementOfType( package_key ) )
433 /* We found a top-level specification for the required package...
435 pkgXmlNode *component = pkg->FindFirstAssociate( component_key );
436 if( component != NULL )
438 * When this package is subdivided into components,
439 * then we derive the source reference from the first
445 /* Now inspect the "release" specifications within the
446 * selected package/component definition...
448 if( (pkg = pkg->FindFirstAssociate( release_key )) != NULL )
450 /* ...creating a pkgActionItem...
452 pkgActionItem latest;
453 pkgXmlNode *selected = pkg;
455 /* ...and examining each release in turn...
459 /* ...select the most recent release, and assign it
460 * to the pkgActionItem reference...
462 if( latest.SelectIfMostRecentFit( pkg ) == pkg )
463 latest.SelectPackage( selected = pkg );
465 /* ...continuing until we have examined all available
466 * release specifications.
468 pkg = pkg->FindNextAssociate( release_key );
471 /* Finally, hand off the "source" or "licence" processing
472 * request, based on the most recent release selection, to
473 * the pkgActionItem we've just instantiated.
475 latest.GetSourceArchive( selected, category );
479 /* $RCSfile$: end of file */