1 //
2 // aegis - project change supervisor
3 // Copyright (C) 1991-1999, 2001-2010, 2012 Peter Miller
4 // Copyright (C) 2008 Walter Franzini
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or (at
9 // your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program. If not, see <http://www.gnu.org/licenses/>.
18 //
19
20 #include <common/ac/assert.h>
21 #include <common/ac/stdio.h>
22 #include <common/ac/stdlib.h>
23 #include <common/ac/string.h>
24 #include <common/ac/libintl.h>
25
26 #include <common/gmatch.h>
27 #include <common/language.h>
28 #include <common/nstring/list.h>
29 #include <common/progname.h>
30 #include <common/quit.h>
31 #include <common/sizeof.h>
32 #include <common/str_list.h>
33 #include <common/trace.h>
34 #include <common/uuidentifier.h>
35 #include <libaegis/arglex2.h>
36 #include <libaegis/arglex/change.h>
37 #include <libaegis/arglex/project.h>
38 #include <libaegis/attribute.h>
39 #include <libaegis/cattr.fmtgen.h>
40 #include <libaegis/change/attributes.h>
41 #include <libaegis/change/branch.h>
42 #include <libaegis/change/file.h>
43 #include <libaegis/change.h>
44 #include <libaegis/change/identifier.h>
45 #include <libaegis/commit.h>
46 #include <libaegis/file.h>
47 #include <libaegis/help.h>
48 #include <libaegis/io.h>
49 #include <libaegis/lock.h>
50 #include <libaegis/os.h>
51 #include <libaegis/project.h>
52 #include <libaegis/project/history.h>
53 #include <libaegis/sub.h>
54 #include <libaegis/uname.h>
55 #include <libaegis/undo.h>
56 #include <libaegis/user.h>
57 #include <aegis/aeca.h>
58
59
60 static void
change_attributes_usage(void)61 change_attributes_usage(void)
62 {
63 const char *progname;
64
65 progname = progname_get();
66 fprintf
67 (
68 stderr,
69 "usage: %s -Change_Attributes -File <attr-file> [ <option>... ]\n",
70 progname
71 );
72 fprintf
73 (
74 stderr,
75 " %s -Change_Attributes -Edit [ <option>... ]\n",
76 progname
77 );
78 fprintf
79 (
80 stderr,
81 " %s -Change_Attributes -List [ <option>... ]\n",
82 progname
83 );
84 fprintf(stderr, " %s -Change_Attributes -Help\n", progname);
85 quit(1);
86 }
87
88
89 static void
change_attributes_help(void)90 change_attributes_help(void)
91 {
92 help("aeca", change_attributes_usage);
93 }
94
95
96 static void
change_attributes_list(void)97 change_attributes_list(void)
98 {
99 cattr_ty *cattr_data;
100 cstate_ty *cstate_data;
101 int description_only;
102
103 trace(("change_attributes_list()\n{\n"));
104 arglex();
105 description_only = 0;
106 change_identifier cid;
107 while (arglex_token != arglex_token_eoln)
108 {
109 switch (arglex_token)
110 {
111 default:
112 generic_argument(change_attributes_usage);
113 continue;
114
115 case arglex_token_branch:
116 case arglex_token_change:
117 case arglex_token_delta:
118 case arglex_token_delta_date:
119 case arglex_token_delta_from_change:
120 case arglex_token_grandparent:
121 case arglex_token_number:
122 case arglex_token_project:
123 case arglex_token_trunk:
124 cid.command_line_parse(change_attributes_usage);
125 continue;
126
127 case arglex_token_description_only:
128 if (description_only)
129 duplicate_option(change_attributes_usage);
130 description_only = 1;
131 break;
132 }
133 arglex();
134 }
135 cid.command_line_check(change_attributes_usage);
136
137 //
138 // build the cattr data
139 //
140 cstate_data = cid.get_cp()->cstate_get();
141 cattr_data = (cattr_ty *)cattr_type.alloc();
142 change_attributes_copy(cattr_data, cstate_data);
143
144 //
145 // print the cattr data
146 //
147 language_human();
148 if (description_only)
149 {
150 string_ty *s;
151
152 s = cattr_data->description;
153 if (s && s->str_length)
154 {
155 printf
156 (
157 "%s%s",
158 s->str_text,
159 (s->str_text[s->str_length - 1] == '\n' ? "" : "\n")
160 );
161 }
162 }
163 else
164 cattr_write_file((string_ty *)0, cattr_data, 0);
165 language_C();
166 cattr_type.free(cattr_data);
167 trace(("}\n"));
168 }
169
170
171 static void
check_permissions(change_identifier & cid)172 check_permissions(change_identifier &cid)
173 {
174 if (project_administrator_query(cid.get_pp(), cid.get_up()->name()))
175 return;
176 if
177 (
178 cid.get_cp()->is_being_developed()
179 &&
180 nstring(cid.get_cp()->developer_name()) == cid.get_up()->name()
181 )
182 return;
183 if
184 (
185 project_developers_may_create_changes_get(cid.get_pp())
186 &&
187 cid.get_cp()->is_awaiting_development()
188 &&
189 project_developer_query(cid.get_pp(), cid.get_up()->name())
190 )
191 return;
192
193 change_fatal(cid.get_cp(), 0, i18n("bad ca, not auth"));
194 }
195
196
197 static cattr_ty *
cattr_fix_arch(change_identifier & cid)198 cattr_fix_arch(change_identifier &cid)
199 {
200 cstate_ty *cstate_data;
201 cattr_ty *cattr_data;
202 string_ty *un;
203 size_t j;
204 pconf_ty *pconf_data;
205
206 //
207 // Extract current change attributes.
208 //
209 cstate_data = cid.get_cp()->cstate_get();
210 cattr_data = (cattr_ty *)cattr_type.alloc();
211 change_attributes_copy(cattr_data, cstate_data);
212
213 //
214 // Toss out the architecture information.
215 //
216 if (cattr_data->architecture)
217 cattr_architecture_list_type.free(cattr_data->architecture);
218 cattr_data->architecture =
219 (cattr_architecture_list_ty *)cattr_architecture_list_type.alloc();
220
221 //
222 // For each of the project architectures, add all of the mandatory
223 // ones, and any of the optional ones that match, to the architecture
224 // list.
225 //
226 pconf_data = change_pconf_get(cid.get_cp(), 0);
227 un = uname_variant_get();
228 for (j = 0; j < pconf_data->architecture->length; ++j)
229 {
230 pconf_architecture_ty *pca = pconf_data->architecture->list[j];
231 if (!pca)
232 continue;
233 if (!pca->name)
234 continue;
235 if
236 (
237 pca->mode == pconf_architecture_mode_required
238 ||
239 (
240 pca->mode == pconf_architecture_mode_optional
241 &&
242 un
243 &&
244 gmatch(pca->pattern->str_text, un->str_text)
245 )
246 )
247 {
248 //
249 // We must be careful to suppress duplicates, otherwise the
250 // architecture prerequisites for state transitions are
251 // unsatifiable.
252 //
253 size_t k = 0;
254 for (k = 0; k < cattr_data->architecture->length; ++k)
255 if (str_equal(pca->name, cattr_data->architecture->list[k]))
256 break;
257 if (k < cattr_data->architecture->length)
258 {
259 meta_type *type_p = 0;
260 string_ty **sp =
261 (string_ty **)
262 cattr_architecture_list_type.list_parse
263 (
264 cattr_data->architecture,
265 &type_p
266 );
267 assert(type_p == &string_type);
268 *sp = str_copy(pca->name);
269 }
270
271 //
272 // Only the first match is used. This is consistent with
273 // how the change_architecture_name function works.
274 //
275 un = 0;
276 }
277 }
278
279 //
280 // If we didn't find any architectures, set the list to the
281 // "unspecified" architecture, which is the Aegis default.
282 //
283 if (cattr_data->architecture->length == 0)
284 {
285 meta_type *type_p = 0;
286 string_ty **sp =
287 (string_ty **)
288 cattr_architecture_list_type.list_parse
289 (
290 cattr_data->architecture,
291 &type_p
292 );
293 assert(type_p == &string_type);
294 *sp = str_from_c("unspecified");
295 }
296 return cattr_data;
297 }
298
299
300 static void
change_attributes_uuid(void)301 change_attributes_uuid(void)
302 {
303 cstate_ty *cstate_data;
304 string_ty *uuid;
305 size_t j;
306
307 trace(("change_attributes_main()\n{\n"));
308 arglex();
309 change_identifier cid;
310 uuid = 0;
311 while (arglex_token != arglex_token_eoln)
312 {
313 switch (arglex_token)
314 {
315 default:
316 generic_argument(change_attributes_usage);
317 continue;
318
319 case arglex_token_string:
320 if (uuid)
321 {
322 duplicate_option_by_name
323 (
324 arglex_token_uuid,
325 change_attributes_usage
326 );
327 }
328 uuid = str_from_c(arglex_value.alv_string);
329 if (!universal_unique_identifier_valid(uuid))
330 option_needs_uuid(arglex_token_uuid, change_attributes_usage);
331 break;
332
333 case arglex_token_branch:
334 case arglex_token_change:
335 case arglex_token_delta:
336 case arglex_token_delta_date:
337 case arglex_token_delta_from_change:
338 case arglex_token_grandparent:
339 case arglex_token_number:
340 case arglex_token_project:
341 case arglex_token_trunk:
342 cid.command_line_parse(change_attributes_usage);
343 continue;
344
345 case arglex_token_wait:
346 case arglex_token_wait_not:
347 user_ty::lock_wait_argument(change_attributes_usage);
348 break;
349 }
350 arglex();
351 }
352 cid.command_line_check(change_attributes_usage);
353
354 //
355 // As a special case, if no UUID string was given,
356 // make one up on the spot.
357 //
358 if (!uuid)
359 uuid = universal_unique_identifier();
360
361 //
362 // lock the change
363 //
364 change_cstate_lock_prepare(cid.get_cp());
365 lock_take();
366 cstate_data = cid.get_cp()->cstate_get();
367
368 //
369 // Unlike other change attributes, the UUID may *only* be edited by
370 // the developer when the change is in the "being developed" state.
371 // This is because it should only ever be done by aepatch or aedist,
372 // immediately after the files have been unpacked.
373 //
374 if
375 (
376 !cid.get_cp()->is_being_developed()
377 ||
378 nstring(cid.get_cp()->developer_name()) != cid.get_up()->name()
379 )
380 {
381 change_fatal(cid.get_cp(), 0, i18n("bad ca, not auth"));
382 }
383
384 //
385 // If the change already has a UUID, this command obviously isn't
386 // being used by aepatch or aedist, so tell the human to take a hike.
387 //
388 if (cstate_data->uuid)
389 {
390 change_fatal(cid.get_cp(), 0, i18n("bad ca, uuid already set"));
391 }
392
393 //
394 // Make sure the UUID has not been used before, anywhere in the
395 // projects. For a genuine UUID this is unlikely, but humans tend
396 // to do silly things at times, so this expensive check will be
397 // necessary.
398 //
399 change::pointer cp2 = project_uuid_find(cid.get_pp(), uuid);
400 if (cp2)
401 {
402 sub_context_ty *scp = sub_context_new();
403 sub_var_set_string(scp, "Other", cp2->version_get());
404 sub_var_optional(scp, "Other");
405 change_fatal(cid.get_cp(), scp, i18n("bad ca, uuid duplicate"));
406 // NOTREACHED
407 }
408
409 //
410 // If the file manifest of the change is altered (e.g. by aenf, aenfu,
411 // aecp, aecpu, etc), or the contents of any file is changed, the
412 // UUID is cleared. This is because it is no longer the same change
413 // as was received by aedist or aepatch, and the UUID is invalidated.
414 //
415 // At this point, we need to ensure that every change file has been
416 // fingerprinted, to be able to implement the above condition.
417 //
418 os_throttle();
419 for (j = 0; ; ++j)
420 {
421 fstate_src_ty *src;
422
423 src = change_file_nth(cid.get_cp(), j, view_path_first);
424 if (!src)
425 break;
426
427 //
428 // Do not check the fingerprint for derived files, to
429 // replicate exactly the aed behaviour: build files are not
430 // diffed so the fingerprint is not calculated.
431 //
432 switch (src->usage)
433 {
434 case file_usage_build:
435 continue;
436
437 case file_usage_config:
438 case file_usage_manual_test:
439 case file_usage_source:
440 case file_usage_test:
441 #ifndef DEBUG
442 default:
443 #endif
444 break;
445 }
446
447 change_file_fingerprint_check(cid.get_cp(), src);
448 }
449
450 //
451 // Assign the UUID. Can't do this before the has-it-been-used test,
452 // or the UUID would show as a duplicate. Can't do this before the
453 // file fingerprint test, or it will be nuked.
454 //
455 // Make sure it is in lower case.
456 //
457 cstate_data->uuid = str_downcase(uuid);
458 str_free(uuid);
459 uuid = 0;
460
461 //
462 // Write the cstate state back out.
463 //
464 cid.get_cp()->cstate_write();
465 commit();
466 lock_release();
467 change_verbose(cid.get_cp(), 0, i18n("attributes changed"));
468 trace(("}\n"));
469 }
470
471
472 static void
change_attributes_main(void)473 change_attributes_main(void)
474 {
475 sub_context_ty *scp;
476 cattr_ty *cattr_data;
477 cstate_ty *cstate_data;
478 pconf_ty *pconf_data;
479 edit_ty edit;
480 int description_only;
481 string_ty *input_file_name;
482 int fix_architecture;
483
484 trace(("change_attributes_main()\n{\n"));
485 arglex();
486 change_identifier cid;
487 edit = edit_not_set;
488 description_only = 0;
489 cattr_data = 0;
490 input_file_name = 0;
491 fix_architecture = 0;
492 nstring_list name_value_pairs;
493 while (arglex_token != arglex_token_eoln)
494 {
495 switch (arglex_token)
496 {
497 default:
498 generic_argument(change_attributes_usage);
499 continue;
500
501 case arglex_token_string:
502 if (strchr(arglex_value.alv_string, '='))
503 {
504 name_value_pairs.push_back(arglex_value.alv_string);
505 break;
506 }
507 scp = sub_context_new();
508 sub_var_set_charstar
509 (
510 scp,
511 "Name",
512 arglex_token_name(arglex_token_file)
513 );
514 error_intl(scp, i18n("warning: use $name option"));
515 sub_context_delete(scp);
516 if (input_file_name)
517 fatal_too_many_files();
518 goto read_attr_file;
519
520 case arglex_token_file:
521 if (input_file_name)
522 duplicate_option(change_attributes_usage);
523 switch (arglex())
524 {
525 default:
526 option_needs_file(arglex_token_file, change_attributes_usage);
527 // NOTREACHED
528
529 case arglex_token_string:
530 read_attr_file:
531 input_file_name = str_from_c(arglex_value.alv_string);
532 break;
533
534 case arglex_token_stdio:
535 input_file_name = str_from_c("");
536 break;
537 }
538 break;
539
540 case arglex_token_change:
541 case arglex_token_number:
542 case arglex_token_project:
543 cid.command_line_parse(change_attributes_usage);
544 continue;
545
546 case arglex_token_edit:
547 if (edit == edit_foreground)
548 duplicate_option(change_attributes_usage);
549 if (edit != edit_not_set)
550 {
551 too_many_edits:
552 mutually_exclusive_options
553 (
554 arglex_token_edit,
555 arglex_token_edit_bg,
556 change_attributes_usage
557 );
558 }
559 edit = edit_foreground;
560 break;
561
562 case arglex_token_edit_bg:
563 if (edit == edit_background)
564 duplicate_option(change_attributes_usage);
565 if (edit != edit_not_set)
566 goto too_many_edits;
567 edit = edit_background;
568 break;
569
570 case arglex_token_wait:
571 case arglex_token_wait_not:
572 user_ty::lock_wait_argument(change_attributes_usage);
573 break;
574
575 case arglex_token_description_only:
576 if (description_only)
577 duplicate_option(change_attributes_usage);
578 description_only = 1;
579 break;
580
581 case arglex_token_fix_architecture:
582 if (fix_architecture)
583 duplicate_option(change_attributes_usage);
584 fix_architecture = 1;
585 break;
586 }
587 arglex();
588 }
589 cid.command_line_check(change_attributes_usage);
590 if (fix_architecture)
591 {
592 if (edit == edit_foreground)
593 {
594 mutually_exclusive_options
595 (
596 arglex_token_edit,
597 arglex_token_fix_architecture,
598 change_attributes_usage
599 );
600 }
601 if (edit == edit_background)
602 {
603 mutually_exclusive_options
604 (
605 arglex_token_edit_bg,
606 arglex_token_fix_architecture,
607 change_attributes_usage
608 );
609 }
610 if (input_file_name)
611 {
612 mutually_exclusive_options
613 (
614 arglex_token_file,
615 arglex_token_fix_architecture,
616 change_attributes_usage
617 );
618 }
619 }
620 if (!name_value_pairs.empty())
621 {
622 if (fix_architecture || edit != edit_not_set || input_file_name)
623 change_attributes_usage();
624 }
625 if (input_file_name)
626 {
627 if (description_only)
628 {
629 cattr_data = (cattr_ty *)cattr_type.alloc();
630 os_become_orig();
631 nstring desc = read_whole_file(nstring(input_file_name));
632 if (desc.empty())
633 cattr_data->description = 0;
634 else
635 cattr_data->description = str_copy(desc.get_ref());
636 os_become_undo();
637 }
638 else
639 {
640 os_become_orig();
641 cattr_data = cattr_read_file(input_file_name);
642 os_become_undo();
643 }
644 assert(cattr_data);
645 change_attributes_fixup(cattr_data);
646 }
647 if
648 (
649 !cattr_data
650 &&
651 edit == edit_not_set
652 &&
653 !fix_architecture
654 &&
655 name_value_pairs.empty()
656 )
657 {
658 scp = sub_context_new();
659 sub_var_set_charstar
660 (
661 scp,
662 "Name1",
663 arglex_token_name(arglex_token_file)
664 );
665 sub_var_set_charstar
666 (
667 scp,
668 "Name2",
669 arglex_token_name(arglex_token_edit)
670 );
671 error_intl(scp, i18n("warning: no $name1, assuming $name2"));
672 sub_context_delete(scp);
673 edit = edit_foreground;
674 }
675 if (edit != edit_not_set && !cattr_data)
676 cattr_data = (cattr_ty *)cattr_type.alloc();
677
678 //
679 // edit the attributes
680 //
681 if (fix_architecture || !name_value_pairs.empty())
682 {
683 // Do nothing, yet
684 }
685 else if (edit != edit_not_set)
686 {
687 //
688 // make sure they are allowed to,
689 // to avoid a wasted edit
690 //
691 check_permissions(cid);
692
693 //
694 // fill in any other fields
695 //
696 cstate_data = cid.get_cp()->cstate_get();
697 change_attributes_copy(cattr_data, cstate_data);
698
699 //
700 // edit the attributes
701 //
702 if (description_only)
703 {
704 string_ty *s;
705
706 s = os_edit_string(cattr_data->description, edit);
707 assert(s);
708 if (cattr_data->description)
709 str_free(cattr_data->description);
710 cattr_data->description = s;
711 }
712 else
713 {
714 scp = sub_context_new();
715 sub_var_set_string(scp, "Name", project_name_get(cid.get_pp()));
716 sub_var_set_long(scp, "Number", cid.get_change_number());
717 io_comment_append(scp, i18n("Project $name, Change $number"));
718 sub_context_delete(scp);
719 change_attributes_edit(&cattr_data, edit);
720 }
721 }
722
723 //
724 // lock the change
725 //
726 change_cstate_lock_prepare(cid.get_cp());
727 lock_take();
728 cstate_data = cid.get_cp()->cstate_get();
729 pconf_data = change_pconf_get(cid.get_cp(), 0);
730
731 if (fix_architecture)
732 cattr_data = cattr_fix_arch(cid);
733
734 //
735 // Now apply the name=value pairs
736 //
737 if (!name_value_pairs.empty())
738 {
739 if (!cattr_data)
740 {
741 cattr_data = (cattr_ty *)cattr_type.alloc();
742 change_attributes_copy(cattr_data, cstate_data);
743 }
744 if (!cattr_data->attribute)
745 {
746 cattr_data->attribute =
747 (attributes_list_ty *)attributes_list_type.alloc();
748 }
749 for (size_t j = 0; j < name_value_pairs.size(); ++j)
750 {
751 nstring pair = name_value_pairs[j];
752 const char *eqp = strchr(pair.c_str(), '=');
753 assert(eqp);
754 if (!eqp)
755 continue;
756 bool append = (eqp > pair.c_str() && eqp[-1] == '+');
757 const char *name_end = eqp;
758 if (append)
759 --name_end;
760 nstring name(pair.c_str(), name_end - pair.c_str());
761 nstring value(eqp + 1);
762
763 if (append)
764 {
765 //
766 // This will add the attribute to the end of the
767 // list, unless this exact name and value are
768 // already present.
769 //
770 attributes_list_append_unique
771 (
772 cattr_data->attribute,
773 name.c_str(),
774 value.c_str()
775 );
776 }
777 else
778 {
779 //
780 // This will replace the first attribute with
781 // that name. If there is more than one of that name,
782 // the second and subsequent attributes are unchanged.
783 // If there is no attribute of that name, it will be
784 // appended.
785 //
786 attributes_list_insert
787 (
788 cattr_data->attribute,
789 name.c_str(),
790 value.c_str()
791 );
792 }
793 }
794 }
795
796 //
797 // make sure they are allowed to
798 // (even if edited, could have changed during edit)
799 //
800 check_permissions(cid);
801
802 //
803 // copy the attributes across
804 //
805 if (cattr_data->description)
806 {
807 if (cstate_data->description)
808 str_free(cstate_data->description);
809 cstate_data->description = str_copy(cattr_data->description);
810 }
811 if (cattr_data->brief_description)
812 {
813 if (cstate_data->brief_description)
814 str_free(cstate_data->brief_description);
815 cstate_data->brief_description =
816 str_copy(cattr_data->brief_description);
817 }
818 if (cattr_data->mask & cattr_cause_mask)
819 cstate_data->cause = cattr_data->cause;
820 if (project_administrator_query(cid.get_pp(), cid.get_up()->name()))
821 {
822 if (cattr_data->mask & cattr_test_exempt_mask)
823 {
824 cstate_data->test_exempt = cattr_data->test_exempt;
825 cstate_data->given_test_exemption = cattr_data->test_exempt;
826 }
827 if (cattr_data->mask & cattr_test_baseline_exempt_mask)
828 {
829 cstate_data->test_baseline_exempt =
830 cattr_data->test_baseline_exempt;
831 }
832 if (cattr_data->mask & cattr_regression_test_exempt_mask)
833 {
834 cstate_data->regression_test_exempt =
835 cattr_data->regression_test_exempt;
836 cstate_data->given_regression_test_exemption =
837 cattr_data->regression_test_exempt;
838 }
839 }
840 else
841 {
842 if
843 (
844 (
845 cattr_data->test_exempt
846 &&
847 !cstate_data->test_exempt
848 &&
849 !cstate_data->given_test_exemption
850 )
851 ||
852 (
853 cattr_data->test_baseline_exempt
854 &&
855 !cstate_data->test_baseline_exempt
856 )
857 ||
858 (
859 cattr_data->regression_test_exempt
860 &&
861 !cstate_data->regression_test_exempt
862 &&
863 !cstate_data->given_regression_test_exemption
864 )
865 )
866 {
867 change_fatal(cid.get_cp(), 0, i18n("bad ca, no test exempt"));
868 }
869 else
870 {
871 //
872 // developers may remove exemptions
873 //
874 if (cattr_data->mask & cattr_test_exempt_mask)
875 {
876 cstate_data->test_exempt = cattr_data->test_exempt;
877 }
878 if (cattr_data->mask & cattr_test_baseline_exempt_mask)
879 {
880 cstate_data->test_baseline_exempt =
881 cattr_data->test_baseline_exempt;
882 }
883 if (cattr_data->mask & cattr_regression_test_exempt_mask)
884 {
885 cstate_data->regression_test_exempt =
886 cattr_data->regression_test_exempt;
887 }
888 }
889 }
890
891 //
892 // copy the architecture across
893 //
894 if (cattr_data->architecture && cattr_data->architecture->length)
895 {
896 //
897 // make sure they did not name architectures
898 // we have never heard of
899 //
900 string_list_ty caarch;
901 for (size_t j = 0; j < cattr_data->architecture->length; ++j)
902 caarch.push_back_unique(cattr_data->architecture->list[j]);
903
904 string_list_ty pcarch;
905 assert(pconf_data->architecture);
906 assert(pconf_data->architecture->length);
907 for (size_t k = 0; k < pconf_data->architecture->length; ++k)
908 {
909 pcarch.push_back_unique(pconf_data->architecture->list[k]->name);
910 }
911
912 if (!caarch.subset(pcarch))
913 fatal_intl(0, i18n("bad ca, unknown architecture"));
914
915 //
916 // developers may remove architecture exemptions
917 // but may not grant them
918 //
919 if (!project_administrator_query(cid.get_pp(), cid.get_up()->name()))
920 {
921 string_list_ty csarch;
922 for (size_t m = 0; m < cstate_data->architecture->length; ++m)
923 {
924 csarch.push_back_unique(cstate_data->architecture->list[m]);
925 }
926
927 if (!csarch.subset(caarch))
928 fatal_intl(0, i18n("bad ca, no arch exempt"));
929 }
930
931 //
932 // copy the architecture names across
933 //
934 change_architecture_clear(cid.get_cp());
935 for (size_t m = 0; m < cattr_data->architecture->length; ++m)
936 {
937 change_architecture_add
938 (
939 cid.get_cp(),
940 cattr_data->architecture->list[m]
941 );
942 }
943 }
944
945 //
946 // copy the copyright years list across
947 //
948 if (cattr_data->copyright_years && cattr_data->copyright_years->length)
949 {
950 size_t j;
951
952 if (cstate_data->copyright_years)
953 cstate_copyright_years_list_type.free(cstate_data->copyright_years);
954 cstate_data->copyright_years =
955 (cstate_copyright_years_list_ty *)
956 cstate_copyright_years_list_type.alloc();
957 for (j = 0; j < cattr_data->copyright_years->length; ++j)
958 {
959 meta_type *type_p = 0;
960 long *int_p =
961 (long int *)
962 cstate_copyright_years_list_type.list_parse
963 (
964 cstate_data->copyright_years,
965 &type_p
966 );
967 assert(type_p==&integer_type);
968 *int_p = cattr_data->copyright_years->list[j];
969 }
970 }
971
972 //
973 // copy the previous version across
974 //
975 if (cattr_data->version_previous)
976 {
977 if (cstate_data->version_previous)
978 str_free(cstate_data->version_previous);
979 cstate_data->version_previous = str_copy(cattr_data->version_previous);
980 }
981
982 //
983 // Copy the user-defined attributes across.
984 //
985 if (cattr_data->attribute)
986 {
987 if (cstate_data->attribute)
988 {
989 attributes_list_type.free(cstate_data->attribute);
990 cstate_data->attribute = 0;
991 }
992 if (cattr_data->attribute->length)
993 {
994 cstate_data->attribute =
995 attributes_list_copy(cattr_data->attribute);
996 }
997 }
998
999 cattr_type.free(cattr_data);
1000 cid.get_cp()->cstate_write();
1001 commit();
1002 lock_release();
1003 change_verbose(cid.get_cp(), 0, i18n("attributes changed"));
1004 trace(("}\n"));
1005 }
1006
1007
1008 void
change_attributes(void)1009 change_attributes(void)
1010 {
1011 static arglex_dispatch_ty dispatch[] =
1012 {
1013 { arglex_token_help, change_attributes_help, 0 },
1014 { arglex_token_list, change_attributes_list, 0 },
1015 { arglex_token_uuid, change_attributes_uuid, 0 },
1016 };
1017
1018 trace(("change_attributes()\n{\n"));
1019 arglex_dispatch(dispatch, SIZEOF(dispatch), change_attributes_main);
1020 trace(("}\n"));
1021 }
1022
1023
1024 // vim: set ts=8 sw=4 et :
1025