OSDN Git Service

Eliminate invalid comparisons of "this" with nullptr.
[mingw/mingw-get.git] / src / climain.cpp
1 /*
2  * climain.cpp
3  *
4  * $Id$
5  *
6  * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
7  * Copyright (C) 2009-2013, MinGW.org Project
8  *
9  *
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.
13  *
14  *
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.
20  *
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.
27  *
28  */
29 #include <stdio.h>
30 #include <libgen.h>
31 #include <string.h>
32 #include <fcntl.h>
33
34 #include "dmh.h"
35 #include "mkpath.h"
36
37 #include "pkgbase.h"
38 #include "pkgkeys.h"
39 #include "pkgopts.h"
40 #include "pkgtask.h"
41
42 EXTERN_C void cli_setopts( struct pkgopts *opts )
43 {
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.
46    */
47   (void) pkgOptions( OPTION_TABLE_ASSIGN, opts );
48 }
49
50 EXTERN_C pkgOpts *pkgOptions( int action, struct pkgopts *ref )
51 {
52   /* Global accessor for the program options data table.
53    */
54   static pkgOpts *table = NULL;
55   if( action == OPTION_TABLE_ASSIGN )
56     /*
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.
61      */
62     table = (pkgOpts *)(ref);
63
64   /* In all cases, we return the assigned data table location.
65    */
66   return table;
67 }
68
69 class pkgArchiveNameList
70 {
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.
75    */
76   private:
77     const char          *name;  // name of package tracked
78     pkgArchiveNameList  *next;  // pointer to next stack entry
79
80   public:
81     inline bool NotRecorded( const char *candidate )
82     {
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.
87        */
88       pkgArchiveNameList *check = this;
89       while( check != NULL )
90       {
91         /* We haven't walked off the bottom of the stack yet...
92          */
93         if( strcmp( check->name, candidate ) == 0 )
94         {
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.
98            */
99           free( (void *)(candidate) );
100
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.
104            */
105           return false;
106         }
107         /* No matching entry found yet; walk down to the next
108          * stack entry, if any, to continue the search.
109          */
110         check = check->next;
111       }
112       /* We walked off the bottom of the stack, without finding
113        * any match; return true to accept the candidate.
114        */
115       return true;
116     }
117     inline pkgArchiveNameList *Record( const char *candidate )
118     {
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.
124        */
125       pkgArchiveNameList *retptr = new pkgArchiveNameList();
126       retptr->name = candidate; retptr->next = this;
127       return retptr;
128     }
129     inline ~pkgArchiveNameList()
130     {
131       /* Completely clear the stack, releasing the heap memory
132        * allocated to record the stacked package names.
133        */
134       free( (void *)(name) );
135       delete next;
136     }
137 };
138
139 static pkgArchiveNameList *pkgProcessedArchives; // stack pointer
140
141 EXTERN_C int climain( int argc, char **argv )
142 {
143   try
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]...
147      */
148     char *dmh_progname;
149     char progname[ 1 + strlen( dmh_progname = strdup( basename( *argv++ ))) ];
150     dmh_init( DMH_SUBSYSTEM_TTY, strcpy( progname, dmh_progname ) );
151     free( dmh_progname );
152
153     /* Interpret the `action keyword', specifying the action to be
154      * performed on this invocation...
155      */
156     int action = action_code( *argv );
157     if( action < 0 )
158     {
159       /* No valid action keyword was found; force an abort
160        * through an appropriate DMH_FATAL notification...
161        */
162       if( *argv == NULL )
163         /*
164          * ...viz. the user didn't specify any argument, which
165          * could have been interpreted as an action keyword.
166          */
167         dmh_notify( DMH_FATAL, "no action specified\n" );
168
169       else
170         /* ...or, the specified action keyword was invalid.
171          */
172         dmh_notify( DMH_FATAL, "%s: unknown action keyword\n", *argv );
173     }
174
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.
178      */
179     const char *dfile;
180     if( access( dfile = xmlfile( profile_key ), R_OK ) != 0 )
181     {
182       /* The user hasn't provided a custom configuration profile...
183        */
184       dmh_notify( DMH_WARNING, "%s: user configuration file missing\n", dfile );
185
186       /* ...release the memory allocated by xmlfile(), to store its path name,
187        * then try the mingw-get distribution default profile instead.
188        */
189       free( (void *)(dfile) );
190       dmh_notify( DMH_INFO, "%s: trying system default configuration\n",
191           dfile = xmlfile( defaults_key ) );
192     }
193
194     pkgXmlDocument dbase( dfile );
195     if( dbase.IsOk() )
196     {
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.
202        */
203       free( (void *)(dfile) );
204
205       /* Merge all package lists, as specified in the "repository"
206        * section of the "profile", into the XML database tree...
207        */
208       if( dbase.BindRepositories( action == ACTION_UPDATE ) == NULL )
209         /*
210          * ...bailing out, on an invalid profile specification...
211          */
212         dmh_notify( DMH_FATAL, "%s: invalid application profile\n", dbase.Value() );
213
214       /* If the requested action was "update", then we've already done it,
215        * as a side effect of binding the cached repository catalogues...
216        */
217       if( action != ACTION_UPDATE )
218       {
219         /* ...otherwise, we need to load the system map...
220          */
221         dbase.LoadSystemMap();
222
223         /* ...initialise any preferences which the user may
224          * have specified within profile.xml...
225          */
226         dbase.EstablishPreferences( "cli" );
227
228         /* ...and invoke the appropriate action handler.
229          */
230         switch( action )
231         {
232           case ACTION_LIST:
233           case ACTION_SHOW:
234             /*
235              * "list" and "show" actions are synonymous;
236              * invoke the info-display handler.
237              */
238             dbase.DisplayPackageInfo( argc, argv );
239             break;
240
241           case ACTION_SOURCE:
242           case ACTION_LICENCE:
243             /*
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.
247              */
248             pkgProcessedArchives = NULL;
249             if( pkgOptions()->Test( OPTION_ALL_RELATED ) )
250             {
251               /* The "--all-related" option is in effect; ensure
252                * that all dependencies will be evaluated, as if to
253                * perform a recursive reinstall...
254                */
255               pkgOptions()->SetFlags( OPTION_ALL_DEPS );
256               /*
257                * ...then, for each package which is identified on
258                * the command line...
259                */
260               while( --argc )
261                 /*
262                  * ...schedule a request to install the package,
263                  * together with all of its runtime dependencies...
264                  */
265                 dbase.Schedule( ACTION_INSTALL, *++argv );
266
267               /* ...but DON'T proceed with installation; rather
268                * process the "source" or "licence" request for
269                * each scheduled package.
270                */
271               dbase.GetScheduledSourceArchives( (unsigned long)(action) );
272             }
273             else while( --argc )
274               /*
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.
278                */
279               dbase.GetSourceArchive( *++argv, (unsigned long)(action) );
280
281             /* Finally, clear the stack of processed package names.
282              */
283             delete pkgProcessedArchives;
284             break;
285
286           case ACTION_UPGRADE:
287             if( argc < 2 )
288               /*
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...
293                */
294               dbase.RescheduleInstalledPackages( ACTION_UPGRADE );
295
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).
300              */
301           default:
302             /* ...schedule the specified action for each additional command line
303              * argument, (each of which is assumed to represent a package name)...
304              */
305             while( --argc )
306               /*
307                * (Skipped if argv < 2 on entry).
308                */
309               dbase.Schedule( (unsigned long)(action), *++argv );
310
311             /* ...finally, execute all scheduled actions, and update the
312              * system map accordingly.
313              */
314             dbase.ExecuteActions();
315             dbase.UpdateSystemMap();
316         }
317       }
318       /* If we get this far, then all actions completed successfully;
319        * we are done.
320        */
321       return EXIT_SUCCESS;
322     }
323
324     /* If we get to here, then the package database load failed;
325      * once more, we force an abort through a DMH_FATAL notification...
326      *
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...
331      */
332     return dmh_notify( DMH_FATAL, "%s: cannot load configuration\n", dfile );
333   }
334
335   catch( dmh_exception &e )
336   {
337     /* An error occurred; it should already have been diagnosed,
338      * so simply bail out.
339      */
340     return EXIT_FAILURE;
341   }
342 }
343
344 #include "pkgproc.h"
345
346 void pkgActionItem::GetSourceArchive( pkgXmlNode *package, unsigned long category )
347 {
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.
350    */
351   const char *src = package->SourceArchiveName( category );
352   if( (src != NULL) && pkgProcessedArchives->NotRecorded( src ) )
353   {
354     if( pkgOptions()->Test( OPTION_PRINT_URIS ) == OPTION_PRINT_URIS )
355     {
356       /* The --print-uris option is in effect; this is all
357        * that we are expected to do.
358        */
359       PrintURI( src );
360     }
361
362     else
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...
366        */
367       const char *path_template; flags |= ACTION_DOWNLOAD;
368       DownloadSingleArchive( src, path_template = (category == ACTION_SOURCE)
369           ? pkgSourceArchivePath() : pkgArchivePath()
370         );
371
372       /* ...then, unless the --download-only option is in effect...
373        */
374       if( pkgOptions()->Test( OPTION_DOWNLOAD_ONLY ) != OPTION_DOWNLOAD_ONLY )
375       {
376         /* ...we establish the current working directory as the
377          * destination where it should be unpacked...
378          */
379         char source_archive[mkpath( NULL, path_template, src, NULL )];
380         mkpath( source_archive, path_template, src, NULL );
381
382         /* ...and extract the content from the source archive.
383          */
384         pkgTarArchiveExtractor unpack( source_archive, "." );
385       }
386       /* The path_template was allocated on the heap; we are
387        * done with it, so release the memory allocation...
388        */
389       free( (void *)(path_template) );
390     }
391
392     /* Record the current archive name as "processed", so we may
393      * avoid any inadvertent duplicate processing.
394      */
395     pkgProcessedArchives = pkgProcessedArchives->Record( src );
396   }
397 }
398
399 void pkgActionItem::GetScheduledSourceArchives( unsigned long category )
400 {
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.
405    */
406   pkgActionItem *scheduled = this;
407   while( scheduled->prev != NULL ) scheduled = scheduled->prev;
408
409   /* For each scheduled list entry...
410    */
411   while( scheduled != NULL )
412   {
413     /* ...process the "source" or "licence" request, as appropriate,
414      * in respect of the associated package...
415      */
416     scheduled->GetSourceArchive( scheduled->Selection(), category );
417     /*
418      * ...then move on to the next entry, if any.
419      */
420     scheduled = scheduled->next;
421   }
422 }
423
424 void pkgXmlDocument::GetSourceArchive( const char *name, unsigned long category )
425 {
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.
429    */ 
430   pkgXmlNode *pkg = FindPackageByName( name );
431   if( pkg->IsElementOfType( package_key ) )
432   {
433     /* We found a top-level specification for the required package...
434      */
435     pkgXmlNode *component = pkg->FindFirstAssociate( component_key );
436     if( component != NULL )
437       /*
438        * When this package is subdivided into components,
439        * then we derive the source reference from the first
440        * component defined.
441        */
442       pkg = component;
443   }
444
445   /* Now inspect the "release" specifications within the
446    * selected package/component definition...
447    */
448   if( (pkg = pkg->FindFirstAssociate( release_key )) != NULL )
449   {
450     /* ...creating a pkgActionItem...
451      */
452     pkgActionItem latest;
453     pkgXmlNode *selected = pkg;
454
455     /* ...and examining each release in turn...
456      */
457     while( pkg != NULL )
458     {
459       /* ...select the most recent release, and assign it
460        * to the pkgActionItem reference...
461        */
462       if( latest.SelectIfMostRecentFit( pkg ) == pkg )
463         latest.SelectPackage( selected = pkg );
464
465       /* ...continuing until we have examined all available
466        * release specifications.
467        */
468       pkg = pkg->FindNextAssociate( release_key );
469     }
470
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.
474      */
475     latest.GetSourceArchive( selected, category );
476   }
477 }
478
479 /* $RCSfile$: end of file */