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