[d4be98]: src / pkgexec.cpp  Maximize  Restore  History

Download this file

968 lines (880 with data), 31.6 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
/*
* pkgexec.cpp
*
* $Id$
*
* Written by Keith Marshall <keithmarshall@users.sourceforge.net>
* Copyright (C) 2009-2013, MinGW.org Project
*
*
* Implementation of package management task scheduler and executive.
*
*
* This is free software. Permission is granted to copy, modify and
* redistribute this software, under the provisions of the GNU General
* Public License, Version 3, (or, at your option, any later version),
* as published by the Free Software Foundation; see the file COPYING
* for licensing details.
*
* Note, in particular, that this software is provided "as is", in the
* hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
* even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
* PARTICULAR PURPOSE. Under no circumstances will the author, or the
* MinGW Project, accept liability for any damages, however caused,
* arising from the use of this software.
*
*/
#include "dmh.h"
#include "mkpath.h"
#include "pkgbase.h"
#include "pkgkeys.h"
#include "pkginfo.h"
#include "pkgtask.h"
#include "pkgstat.h"
#include "pkgopts.h"
#include "pkgproc.h"
/* The following static member of the pkgSpinWait class provides
* the access hook through which the static core implementation of
* the base class methods may pass reports back to any derivative
* class object, while retaining the capability to issue reports
* even when no such object exists.
*/
pkgSpinWait *pkgSpinWait::referrer = NULL;
int pkgSpinWait::Report( const char *fmt, ... )
{
/* Also declared as static, this directs printf() style reports
* to any existing derivative class object, while behaving as a
* no-op in the absence of any such object.
*/
int count = 0;
if( referrer != NULL )
{
va_list argv;
va_start( argv, fmt );
count = referrer->DispatchReport( fmt, argv );
va_end( argv );
}
return count;
}
int pkgSpinWait::Indicator( void )
{
/* Once again, declared as static, this method provides a
* mechanism for spin-wait animation of any "%c" formatted
* field within a progress reporting message.
*/
static const char *marker = "|/-\\";
return marker[ referrer->UpdateIndex() ];
}
EXTERN_C const char *action_name( unsigned long index )
{
/* Define the keywords used on the mingw-get command line,
* to specify the package management actions to be performed,
* mapping each to a unique action code index.
*/
static const char* action_id[] =
{
"no change", /* unused; zero cannot test true in a bitwise test */
"remove", /* remove a previously installed package */
"install", /* install a new package */
"upgrade", /* upgrade previously installed packages */
"list", /* list packages and display related information */
"show", /* a synonym for "list" */
"update", /* update local copy of repository catalogues */
"licence", /* retrieve licence sources from repository */
"source" /* retrieve package sources from repository */
};
/* For specified "index", return a pointer to the associated keyword,
* or NULL, if "index" is outside the defined action code range.
*/
return ((index >= 0) && (index < end_of_actions))
? action_id[ index ]
: NULL;
}
EXTERN_C int action_code( const char* request )
{
/* Match an action keyword specified on the command line
* to an entry from the above list...
*/
if( request != NULL )
{
int lencode = strlen( request );
int index, retval, matched;
for( index = matched = 0; index < end_of_actions; index++ )
{
/* Try all defined keywords in turn, until we run out
* of definitions.
*/
if( (strncmp( request, action_name( index ), lencode ) == 0)
/*
* When we find a match, and it is the first...
*/
&& (++matched == 1) )
/*
* ...then we record as the probable index to return.
*/
retval = index;
}
if( matched > 1 )
/*
* We matched more than one valid keyword; reject them all.
*/
dmh_notify( DMH_ERROR, "%s: action keyword is ambiguous\n", request );
else if( matched == 1 )
/*
* We matched exactly one keyword; return its index value.
*/
return retval;
}
/* If we get to here, the specified keyword was not uniquely matched;
* signal this, by returning -1.
*/
return -1;
}
/* To circumvent MS-Windows restrictions on deletion and/or overwriting
* executable and shared object files, while their respective code is in
* use by a running application, and to facilitate upgrade of mingw-get
* itself, while it is the running application performing the upgrade,
* we introduce a "rites of passage" work around. The first phase of
* this is invoked immediately on process start up, but the second
* phase is deferred...
*/
#define IMPLEMENT_INITIATION_RITES PHASE_TWO_RITES
#include "rites.c"
/*
* ...until we know for sure that a self-upgrade has been scheduled...
*/
RITES_INLINE bool self_upgrade_rites( const char *name )
{
/* ...as determined by inspection of package names, and deferring
* the rite as "pending" until a request to process "mingw-get-bin"
* is actually received...
*/
pkgSpecs pkg( name );
bool pending = ((name = pkg.GetComponentClass()) == NULL)
|| (strcmp( name, "bin" ) != 0) || ((name = pkg.GetPackageName()) == NULL)
|| (strcmp( name, "mingw-get" ) != 0);
if( ! pending )
/*
* We've just identified a request to process "mingw-get-bin";
* thus the requirement to invoke the "self upgrade rites" has
* now become immediate, so do it...
*/
invoke_rites();
/* Finally, return the requirement state as it now is, whether it
* remains "pending" or not, so that the caller may avoid checking
* the requirement for invoking the "self upgrade rites" process,
* after it has already been requested.
*/
return pending;
}
pkgActionItem::pkgActionItem( pkgActionItem *after, pkgActionItem *before )
{
/* Construct an appropriately initialised non-specific pkgActionItem...
*/
flags = 0; /* no specific action yet assigned */
min_wanted = NULL; /* no minimum package version constraint... */
max_wanted = NULL; /* nor any maximum version */
/* Initialise package selection to NONE, for this action... */
selection[to_remove] = selection[to_install] = NULL;
/* Insert this item at a specified location in the actions list.
*/
prev = after;
next = before;
}
pkgActionItem*
pkgActionItem::Append( pkgActionItem *item )
{
/* Add an "item" to an ActionItems list, attaching it immediately
* after the item referenced by the "this" pointer; nominally "this"
* refers to the last entry in the list, resulting in a new item
* being appended to the list, but the implementation preserves
* integrity of any following list items, thus also fulfilling
* an "insert after this" function.
*/
if( this == NULL )
/*
* No list exists yet;
* return "item" as first and only entry in new list.
*/
return item;
/* Ensure "item" physically exists, or if not, create a generic
* placeholder in which to construct it...
*/
if( (item == NULL) && ((item = new pkgActionItem()) == NULL) )
/*
* ...bailing out if no such placeholder can be created.
*/
return NULL;
/* Maintain list integrity...
*/
if( (item->next = next) != NULL )
/*
* ...moving any existing items which already follow the insertion
* point in the list structure, to follow the newly added "item".
*/
next->prev = item;
/* Set the new item's own reference pointer, to establish its list
* attachment point...
*/
item->prev = this;
/* ...and attach it immediately after that point.
*/
return next = item;
}
pkgActionItem*
pkgActionItem::Insert( pkgActionItem *item )
{
/* Add an "item" to an ActionItems list, inserting it immediately
* before the item referenced by the "this" pointer.
*/
if( this == NULL )
/*
* No list exists yet;
* return "item" as first and only entry in new list.
*/
return item;
/* Ensure "item" physically exists, or if not, create a generic
* placeholder in which to construct it...
*/
if( (item == NULL) && ((item = new pkgActionItem()) == NULL) )
/*
* ...bailing out if no such placeholder can be created.
*/
return NULL;
/* Maintain list integrity...
*/
if( (item->prev = prev) != NULL )
/*
* ...moving any existing items which already precede the insertion
* point in the list structure, to precede the newly added "item".
*/
prev->next = item;
/* Set the new item's own reference pointer, to establish the item
* currently at the attachment point, as its immediate successor...
*/
item->next = this;
/* ...and attach it, immediately preceding that point.
*/
return prev = item;
}
pkgActionItem*
pkgActionItem::Schedule( unsigned long action, pkgActionItem& item )
{
/* Make a copy of an action item template (which may exist in
* a volatile scope) on the heap, assign the requested action,
* and return it for inclusion in the task schedule.
*/
pkgActionItem *rtn = new pkgActionItem(); *rtn = item;
if( pkgOptions()->Test( OPTION_REINSTALL ) == OPTION_REINSTALL )
/*
* When the user specified the "--reinstall" option, either
* explicitly, or implied by "--download-only", (or even as a
* side effect of "--print-uris"), we MUST enable a download
* action, in case it is required to complete the request.
*/
action |= ACTION_DOWNLOAD;
rtn->flags = action | (item.flags & ~ACTION_MASK);
/* The min_wanted and max_wanted properties, if defined, refer
* to dynamically allocated memory blocks, (on the heap); these
* must have only one action item owner; currently, the original
* item and the copy we've just made are both effective owners,
* and we want only the copy to retain this ownership, we must
* detach them from the original item.
*/
item.min_wanted = item.max_wanted = NULL;
/* Similarly, we must transfer any linkage into the schedule of
* actions from the original item to the copy.
*/
if( item.prev != NULL ) (item.prev)->next = rtn;
if( item.next != NULL ) (item.next)->prev = rtn;
item.prev = item.next = NULL;
/* Finally, we return the copy, leaving the ultimate disposal
* of the original to the caller's discretion.
*/
return rtn;
}
void pkgActionItem::Assert
( unsigned long set, unsigned long mask, pkgActionItem *schedule )
{
/* A method to manipulate the control, error trapping, and state
* flags for all items in the specified schedule of actions.
*
* Starting at the specified item, or the invoking class object
* item if no starting point is specified...
*/
if( (schedule != NULL) || ((schedule = this) != NULL) )
{
/* ...and provided this starting point is not NULL, walk back
* to the first item in the associated task schedule...
*/
while( schedule->prev != NULL ) schedule = schedule->prev;
while( schedule != NULL )
{
/* ...then, processing each scheduled task item in sequence,
* update the flags according to the specified mask and new
* bits to be set...
*/
schedule->flags = (schedule->flags & mask) | set;
/*
* ...before moving on to the next item in the sequence.
*/
schedule = schedule->next;
}
}
}
pkgActionItem*
pkgActionItem::GetReference( pkgActionItem& item )
{
/* Check for a prior reference, within the task schedule,
* for the package specified for processing by "item".
*/
pkgXmlNode* pkg;
if( (pkg = item.Selection()->GetParent()) != NULL )
{
/* We have a pointer to the XML database entry which identifies
* the package containing the release specified as the selection
* associated with "item"; walk the chain of prior entries in
* the schedule...
*/
for( pkgActionItem* item = this; item != NULL; item = item->prev )
{
/* ...and if we find another item holding an identical pointer,
* (i.e. to the same package), we return it...
*/
if( item->Selection()->GetParent() == pkg )
return item;
}
}
/* If we get to here, there is no prior action scheduled for the
* specified package, so we return a NULL pointer...
*/
return NULL;
}
pkgXmlNode *pkgActionItem::SelectIfMostRecentFit( pkgXmlNode *package )
{
/* Assign "package" as the "selection" for the referring action item,
* provided it matches the specified selection criteria and it represents
* a more recent release than any current selection.
*/
pkgSpecs test( package );
/* Establish the selection criteria...
*/
pkgSpecs min_fit( min_wanted );
pkgSpecs max_fit( max_wanted );
/* Choose one of the above, as a basis for identification of
* a correct package-component match...
*/
pkgSpecs& fit = min_wanted ? min_fit : max_fit;
/* Initially assuming that it may not...
*/
flags &= ~ACTION_MAY_SELECT;
/* ...verify that "package" fulfills the selection criteria...
*/
if( match_if_explicit( test.GetComponentClass(), fit.GetComponentClass() )
&& match_if_explicit( test.GetComponentVersion(), fit.GetComponentVersion() )
&& ((max_wanted == NULL) || ((flags & STRICTLY_LT) ? (test < max_fit) : (test <= max_fit)))
&& ((min_wanted == NULL) || ((flags & STRICTLY_GT) ? (test > min_fit) : (test >= min_fit))) )
{
/* We have the correct package component, and it fits within
* the allowed range of release versions...
*/
pkgSpecs last( Selection() );
if( test > last )
/*
* It is also more recent than the current selection,
* so we now replace that...
*/
selection[to_install] = package;
/* Regardless of whether we selected it, or not,
* mark "package" as a viable selection.
*/
flags |= ACTION_MAY_SELECT;
}
/* Whatever choice we make, we return the resultant selection...
*/
return Selection();
}
inline void pkgActionItem::SetPrimary( pkgActionItem* ref )
{
flags = ref->flags;
selection[ to_install ] = ref->selection[ to_install ];
selection[ to_remove ] = ref->selection[ to_remove ];
}
pkgActionItem* pkgXmlDocument::Schedule
( unsigned long action, pkgActionItem& item, pkgActionItem* rank )
{
/* Schedule an action item with a specified ranking order in
* the action list, (or at the end of the list if no ranking
* position is specified)...
*/
pkgActionItem *ref = rank ? rank : actions;
/* If we already have a prior matching item...
*/
pkgActionItem *prior;
if( (prior = actions->GetReference( item )) != NULL )
{
/* ...then, when the current request refers to a primary action,
* we update the already scheduled request to reflect this...
*/
if( (action & ACTION_PRIMARY) == ACTION_PRIMARY )
prior->SetPrimary( rank = ref->Schedule( action /* & ACTION_MASK */, item ) );
# if 0
dmh_printf( "Schedule(0x%08x):%s(prior)\n",
prior->HasAttribute((unsigned long)(-1)),
prior->Selection()->ArchiveName()
);
# endif
return prior;
}
/* ...otherwise, when this request produces a valid package reference,
* we raise a new scheduling request...
*/
else if( ((ref = ref->Schedule( action, item )) != NULL)
&& ((ref->Selection() != NULL) || (ref->Selection( to_remove ) != NULL)) )
{
# if 0
dmh_printf( "Schedule(0x%08x):%s(new)\n",
ref->HasAttribute((unsigned long)(-1)),
ref->Selection()->ArchiveName()
);
# endif
/* ...and, when successfully raised, add it to the task list...
*/
if( rank )
/*
* ...at the specified ranking position, if any...
*/
return rank->Insert( ref );
else
/* ...otherwise, at the end of the list.
*/
return actions = actions->Append( ref );
}
/* If we get to here, then no new action was scheduled; we simply
* return the current insertion point in the task list.
*/
return rank;
}
static __inline__ __attribute__((__always_inline__))
int reinstall_action_scheduled( pkgActionItem *package )
{
/* Helper function to identify scheduled actions which will
* result in reinstallation of the associated package.
*/
return
( pkgOptions()->Test( OPTION_REINSTALL )
&& (package->Selection() == package->Selection( to_remove ))
);
}
void pkgActionItem::Execute( bool with_download )
{
if( this != NULL )
{ pkgActionItem *current = this;
bool init_rites_pending = true;
while( current->prev != NULL ) current = current->prev;
/* Unless normal operations have been suppressed by the
* --print-uris option, (in order to obtain a list of all
* package URIs which the operation would access)...
*/
if( pkgOptions()->Test( OPTION_PRINT_URIS ) < OPTION_PRINT_URIS )
do {
/* ...we initiate any download requests which may
* be necessary to fetch all required archives into
* the local package cache.
*/
if( with_download )
DownloadArchiveFiles( current );
} while( SetAuthorities( current ) > 0 );
else while( current != NULL )
{
/* The --print-uris option is in effect: we simply loop
* over all packages with an assigned action, printing
* the associated download URI for each; (note that this
* will print the URI regardless of prior existence of
* the associated package in the local cache).
*/
current->PrintURI( current->Selection()->ArchiveName() );
current = current->next;
}
/* If the --download-only option is in effect, then we have
* nothing more to do...
*/
if( pkgOptions()->Test( OPTION_DOWNLOAD_ONLY ) != OPTION_DOWNLOAD_ONLY )
{
/* ...otherwise...
*/
while( current != NULL )
{
/* ...processing only those packages with assigned actions...
*/
if( (current->flags & ACTION_MASK) != 0 )
{
/* ...print a notification of the installation process to
* be performed, identifying the package to be processed.
*/
const char *tarname;
pkgXmlNode *ref = current->Selection();
if( (tarname = ref->GetPropVal( tarname_key, NULL )) == NULL )
{
ref = current->Selection( to_remove );
tarname = ref->GetPropVal( tarname_key, value_unknown );
}
dmh_printf( "%s: %s\n", reinstall_action_scheduled( current )
? "reinstall" : action_name( current->flags & ACTION_MASK ),
tarname
);
/* Package pre/post processing scripts may need to
* refer to the sysroot path for the package; place
* a copy in the environment, to facilitate this.
*/
pkgSpecs lookup( tarname );
ref = ref->GetSysRoot( lookup.GetSubSystemName() );
const char *path = ref->GetPropVal( pathname_key, NULL );
if( path != NULL )
{
/* Format the sysroot path into an environment variable
* assignment specification; note that the recorded path
* name is likely to include macros such as "%R", so we
* filter it through mkpath(), to expand them.
*/
const char *nothing = "";
char varspec_template[9 + strlen( path )];
sprintf( varspec_template, "SYSROOT=%s", path );
char varspec[mkpath( NULL, varspec_template, nothing, NULL )];
mkpath( varspec, varspec_template, nothing, NULL );
pkgPutEnv( PKG_PUTENV_DIRSEP_MSW, varspec );
}
/* Check for any outstanding requirement to invoke the
* "self upgrade rites" process, so that we may install an
* upgrade for mingw-get itself...
*/
if( init_rites_pending )
/*
* ...discontinuing the check once this has been completed,
* since it need not be performed more than once.
*/
init_rites_pending = self_upgrade_rites( tarname );
/* If we are performing an upgrade...
*/
if( ((current->flags & ACTION_MASK) == ACTION_UPGRADE)
/*
* ...and the latest version of the package is already installed...
*/
&& (current->Selection() == current->Selection( to_remove ))
/*
* ...and the `--reinstall' option hasn't been specified...
*/
&& (pkgOptions()->Test( OPTION_REINSTALL ) == 0) )
/*
* ...then simply report the up-to-date status...
*/
dmh_notify( DMH_INFO, "package %s is up to date\n", tarname );
else
{ /* ...otherwise, proceed to perform remove and install
* operations, as appropriate.
*/
if( reinstall_action_scheduled( current )
|| ((current->flags & ACTION_REMOVE) == ACTION_REMOVE) )
{
/* The selected package has been marked for removal, either
* explicitly, or as an implicit prerequisite for upgrade, or
* in preparation for reinstallation.
*/
pkgRemove( current );
}
if( (current->flags & ACTION_INSTALL) == ACTION_INSTALL )
{
/* The selected package has been marked for installation,
* either explicitly, or implicitly to complete a package upgrade.
*/
pkgXmlNode *tmp = current->Selection( to_remove );
if( reinstall_action_scheduled( current )
|| ((current->flags & ACTION_MASK) == ACTION_UPGRADE) )
current->selection[ to_remove ] = NULL;
pkgInstall( current );
current->selection[ to_remove ] = tmp;
}
}
}
/* Proceed to the next package, if any, with scheduled actions.
*/
pkgSpinWait::Report( "Processing... (%c)", pkgSpinWait::Indicator() );
current = current->next;
}
}
}
}
pkgActionItem *pkgActionItem::Clear( pkgActionItem *schedule, unsigned long mask )
{
/* Method to remove those action items which have no attribute flags in common
* with the specified mask, from the schedule; return the residual schedule of
* items, if any, which were not removed. (Note that specifying a mask with a
* value of 0UL, which is the default, results in removal of all items).
*/
pkgActionItem *residual = NULL;
/* Starting at the specified item, or the invoking class object item
* if no starting point is specified...
*/
if( (schedule != NULL) || ((schedule = this) != NULL) )
{
/* ...and provided this starting point is not NULL, walk back to
* the first item in the associated task schedule...
*/
while( schedule->prev != NULL ) schedule = schedule->prev;
while( schedule != NULL )
{
/* ...then, processing each scheduled task item in sequence, and
* keeping track of the next to be processed...
*/
pkgActionItem *nextptr = schedule->next;
if( (schedule->flags & mask) == 0 )
/*
* ...delete each which doesn't match any masked attribute...
*/
delete schedule;
else
/* ...otherwise add it to the residual schedule.
*/
residual = schedule;
/* In either event, move on to the next item in sequence, if any.
*/
schedule = nextptr;
}
}
/* Ultimately, return a pointer to the last item added to the residual
* schedule, or NULL if all items were deleted.
*/
return residual;
}
pkgActionItem::~pkgActionItem()
{
/* Destructor...
* The package version range selectors, "min_wanted" and "max_wanted",
* are always allocated storage space on the heap; we need to free that,
* before we destroy the reference pointers.
*/
if( (max_wanted != NULL) && (max_wanted != min_wanted) )
/*
* "max_wanted" is non-NULL, and is distinct, (i.e. it doesn't
* represent an equality constraint which shares a reference with
* "min_wanted"); we need to free it independently.
*/
free( (void *)(max_wanted) );
if( min_wanted != NULL )
/*
* "min_wanted" is non-NULL; we don't care if it is distinct,
* because if not, freeing it is required anyway, to also free
* the same memory referenced by "max_wanted".
*/
free( (void *)(min_wanted) );
/* Also ensure that we preserve the integrity of any linked list of
* action items in which this item participates, by detaching this
* item from the pointer chain.
*/
if( prev != NULL ) prev->next = next;
if( next != NULL ) next->prev = prev;
}
/*
****************
*
* Implementation of processing hooks, for handling pre/post-install
* and pre/post-remove scripts.
*
*/
#include "lua.hpp"
#include <process.h>
static const char *action_key = "action";
static const char *normal_key = "normal";
#define LUA_INLINE static inline __attribute__((__always_inline__))
LUA_INLINE bool init_lua_path()
# define LUA_LIBEXEC_PATH "\\libexec\\mingw-get\\?.lua"
{
/* A one time initialisation hook, to ensure that the built-in Lua script
* interpreter will load scripts from the libexec directory associated with
* the running mingw-get.exe instance.
*/
putenv( "LUA_PATH=!\\?.lua;!"LUA_LIBEXEC_PATH";!\\.."LUA_LIBEXEC_PATH );
return true;
}
LUA_INLINE bool lua_isstringarg( lua_State *interpreter, int arg_index )
{
/* Convenience function to check if a particular argument was passed
* from Lua, and if so, if it has a valid string representation.
*/
return lua_isnoneornil( interpreter, arg_index ) ? false
: lua_isstring( interpreter, arg_index );
}
static int lua_wsh_libexec_path( lua_State *interpreter )
{
/* Implementation for the Lua wsh.libexec_path function; it supports
* usage conforming to either of the function prototypes:
*
* wsh.libexec_path( script )
* wsh.libexec_path( script, subsystem )
*
* returning the absolute file system path to "script", within the
* libexec tree for the applicable subsystem, (or for the system in
* general, if no "subsystem" argument is specified).
*/
const char *approot = approot_path();
const char *script = lua_tostring( interpreter, 1 );
if( lua_isstringarg( interpreter, 2 ) )
{
/* This is the case where a "subsystem" is specified, so we encode
* the applicable subsystem inclusive path name...
*/
const char *path = "%slibexec\\%s\\%s";
const char *subsystem = lua_tostring( interpreter, 2 );
char ref[1 + snprintf( NULL, 0, path, approot, subsystem, script )];
snprintf( ref, sizeof( ref ), path, approot, subsystem, script );
/* ...which we then pass back to the Lua caller.
*/
lua_pushstring( interpreter, ref );
}
else
{ /* This is the case where no subsystem has been specified,
* so we encode the general system libexec path name...
*/
const char *path = "%slibexec\\%s";
char ref[1 + snprintf( NULL, 0, path, approot, script )];
snprintf( ref, sizeof( ref ), path, approot, script );
/* ...again passing it back to the Lua caller.
*/
lua_pushstring( interpreter, ref );
}
/* In either case, we have one result to pass back.
*/
return 1;
}
static int lua_wsh_execute( lua_State *interpreter )
{
/* Implementation for the Lua wsh.execute function; it conforms to
* an effective function prototype equivalent to:
*
* wsh.execute( command )
*
* delivering a capability similar to os.execute, but using wscript
* as the command interpreter, rather than the system shell.
*/
if( lua_isstringarg( interpreter, 1 ) )
{
/* If no "command" is specified, we silently process this as a no-op;
* when a command IS specified, we hand it off to the interpreter.
*/
const char *wsh = "wscript", *mode = "-nologo";
spawnlp( _P_WAIT, wsh, wsh, mode, lua_tostring( interpreter, 1 ), NULL );
}
/* Either way, we have nothing to return to the Lua caller.
*/
return 0;
}
static int luaload_wsh( lua_State *interpreter )
{
/* Declare the functions provided by our Windows Script Host
* interface, wrapping them into the Lua "wsh" module...
*/
static struct luaL_Reg wsh_function_registry[] =
{
/* Lua Name Handler Function */
/* -------------- -------------------- */
{ "execute", lua_wsh_execute },
{ "libexec_path", lua_wsh_libexec_path },
{ NULL, NULL }
};
/* ...and register the module within the active interpreter.
*/
luaL_newlib( interpreter, wsh_function_registry );
return 1;
}
int pkgXmlNode::DispatchScript
( int status, const char *context, const char *priority, pkgXmlNode *action )
{
/* Private method, called by InvokeScript(), to hand-off each script
* fragment from the requesting XML node, with class attribute matching
* the requested context and precedence matching the requested priority,
* for execution by the embedded lua interpreter.
*/
lua_State *interpreter = NULL;
static const char *priority_key = "precedence";
static bool lua_path_setup = false;
if( ! lua_path_setup )
/*
* The Lua script path hasn't been initialised yet; do it now!
*/
lua_path_setup = init_lua_path();
while( action != NULL )
{
/* We have at least one remaining script fragment, attached to the
* current XML node, which is a potential candidate for execution...
*/
if( (strcmp( context, action->GetPropVal( class_key, value_none )) == 0)
&& (strcmp( priority, action->GetPropVal( priority_key, normal_key )) == 0) )
{
/* ...and it does fit the current context and precedence; if we
* have not yet attached an interpreter to this node, then...
*/
if( (interpreter == NULL) && ((interpreter = luaL_newstate()) != NULL) )
{
/* ...start one now, initialise it by loading the standard
* lua libraries...
*/
luaL_openlibs( interpreter );
/* ...and register our Windows Script Host interface...
*/
luaL_requiref( interpreter, "wsh", luaload_wsh, 1 );
}
/* ...then hand off the current script fragment to this active
* lua interpreter...
*/
if( (status = luaL_dostring( interpreter, action->GetText() )) != 0 )
/*
* ...reporting any errors through mingw-get's standard
* diagnostic message handler.
*/
dmh_printf( "lua error in %s script:\n%s\n", context,
lua_tostring( interpreter, -1 )
);
}
/* Check for any further script fragments attached to the current node.
*/
action = action->FindNextAssociate( action_key );
}
/* Before leaving this node...
*/
if( interpreter != NULL )
/*
* ...close any active lua interpreter which we may have attached.
*/
lua_close( interpreter );
/* Finally, return the execution status reported by lua, from the last
* script fragment executed within the scope of the current node.
*/
return status;
}
int pkgXmlNode::InvokeScript( int status, const char *context )
{
/* Private component of the implementation for the public
* InvokeScript() method; it checks for the existence of at
* least one script attached to the invoking XML node, then
* hands off processing of the entire script collection...
*/
pkgXmlNode *action = FindFirstAssociate( action_key );
/* ...first processing any, in the requested context, which are
* designated as having "immediate" precedence...
*/
status = DispatchScript( status, context, "immediate", action );
/*
* ...then, traversing the XML hierarchy towards the root...
*/
if( this != GetDocumentRoot() )
/*
* ...processing any script fragments, in the requested context,
* which are attached to any container nodes...
*/
status = GetParent()->InvokeScript( status, context );
/* ...and finally, process any others attached to the current node,
* in the requested context, having "normal" precedence.
*/
return DispatchScript( status, context, normal_key, action );
}
/* $RCSfile$: end of file */

Get latest updates about Open Source Projects, Conferences and News.

Sign up for the SourceForge newsletter:

JavaScript is required for this form.





No, thanks