1 //
2 // aegis - project change supervisor
3 // Copyright (C) 1999-2008, 2011, 2012 Peter Miller
4 // Copyright (C) 2005-2008, 2010 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/stddef.h>
22 #include <common/ac/stdlib.h>
23 #include <common/ac/string.h>
24 #include <common/ac/unistd.h>
25 
26 #include <common/error.h>
27 #include <common/nstring.h>
28 #include <common/str.h>
29 #include <common/str_list.h>
30 #include <common/symtab/template.h>
31 #include <common/trace.h>
32 #include <common/uuidentifier.h>
33 #include <common/version_stmp.h>
34 #include <libaegis/arglex/change.h>
35 #include <libaegis/arglex/project.h>
36 #include <libaegis/attribute.h>
37 #include <libaegis/cattr.fmtgen.h>
38 #include <libaegis/change/attributes.h>
39 #include <libaegis/change/branch.h>
40 #include <libaegis/change/file.h>
41 #include <libaegis/change.h>
42 #include <libaegis/fattr.fmtgen.h>
43 #include <libaegis/file.h>
44 #include <libaegis/help.h>
45 #include <libaegis/input/cpio.h>
46 #include <libaegis/meta_parse.h>
47 #include <libaegis/move_list.h>
48 #include <libaegis/os.h>
49 #include <libaegis/output/bit_bucket.h>
50 #include <libaegis/output/file.h>
51 #include <libaegis/patch/list.h>
52 #include <libaegis/project/file.h>
53 #include <libaegis/project/file/trojan.h>
54 #include <libaegis/project.h>
55 #include <libaegis/project/history.h>
56 #include <libaegis/project/invento_walk.h>
57 #include <libaegis/sub.h>
58 #include <libaegis/undo.h>
59 #include <libaegis/user.h>
60 
61 #include <aedist/usage.h>
62 #include <aedist/arglex3.h>
63 #include <aedist/change/functor/invent_build.h>
64 #include <aedist/open.h>
65 #include <aedist/receive.h>
66 
67 
68 static void
create_files(const nstring & cmd,cstate_src_list_ty * files,const nstring & dir,bool use_uuid)69 create_files(const nstring &cmd, cstate_src_list_ty *files, const nstring &dir,
70     bool use_uuid)
71 {
72     assert(files);
73 
74     for (size_t i = 0; i < files->length; ++i)
75     {
76         nstring tail;
77 
78         tail += " " + nstring(files->list[i]->file_name);
79         if (use_uuid && files->list[i]->uuid)
80             tail += " -uuid=" + nstring(files->list[i]->uuid);
81         else
82             tail += " -not-uuid";
83 
84         os_become_orig();
85         os_execute(cmd + tail, OS_EXEC_FLAG_INPUT, dir);
86         os_become_undo();
87     }
88 }
89 
90 static void
cstate_src_list_push_back(cstate_src_list_ty * l,cstate_src_ty * src_data)91 cstate_src_list_push_back(cstate_src_list_ty *l, cstate_src_ty *src_data)
92 {
93     meta_type *type_p = 0;
94     cstate_src_ty **dst_data_p;
95     cstate_src_ty *dst_data;
96 
97     assert(l);
98     assert(src_data);
99 
100     dst_data_p = (cstate_src_ty **)cstate_src_list_type.list_parse(l, &type_p);
101     assert(type_p == &cstate_src_type);
102     dst_data = (cstate_src_ty *)cstate_src_type.alloc();
103     *dst_data_p = dst_data;
104 
105     //
106     // Copy only file_name and UUID since it's all we need now.
107     //
108     dst_data->file_name = str_copy(src_data->file_name);
109     if (src_data->uuid)
110         dst_data->uuid = str_copy(src_data->uuid);
111 }
112 
113 static void
cstate_src_list_push_back_unique(cstate_src_list_ty * l,cstate_src_ty * src_data)114 cstate_src_list_push_back_unique(cstate_src_list_ty *l, cstate_src_ty *src_data)
115 {
116     assert(l);
117     assert(src_data);
118 
119     for (size_t i = 0; i < l->length; ++i)
120     {
121         if (str_equal(src_data->file_name, l->list[i]->file_name))
122             return;
123     }
124 
125     cstate_src_list_push_back(l, src_data);
126 }
127 
128 static void
move_xargs(const nstring & project_name,long change_number,const nstring_list & files,const nstring & dd,const nstring & trace_options)129 move_xargs(const nstring &project_name, long change_number,
130     const nstring_list &files, const nstring &dd, const nstring &trace_options)
131 {
132     if (0 != (files.size() % 2))
133         this_is_a_bug();
134 
135     nstring command =
136         nstring::format
137         (
138             "aegis --move-file --project=%s --change=%ld%s --verbose",
139             project_name.c_str(),
140             magic_zero_decode(change_number),
141             trace_options.c_str()
142         );
143 
144     //
145     // aemv needs an even number of file names, the os_xargs function
146     // run the commands with, at most, 50 args.  Thus each aemv
147     // invocation get an even number of file arguments.
148     //
149     os_xargs(command, files, dd);
150 }
151 
152 
153 static void
change_uuid_set(const nstring & project_name,long change_number,const nstring & uuid,const nstring & trace_options,const nstring & dd)154 change_uuid_set(const nstring &project_name, long change_number,
155     const nstring &uuid, const nstring &trace_options, const nstring &dd)
156 {
157     nstring s =
158         nstring::format
159         (
160             "aegis --change-attr --uuid %s -change=%ld --project=%s%s",
161             uuid.quote_shell().c_str(),
162             magic_zero_decode(change_number),
163             project_name.c_str(),
164             trace_options.c_str()
165         );
166     os_become_orig();
167     int err = os_execute_retcode(s, OS_EXEC_FLAG_INPUT, dd);
168     os_become_undo();
169 
170     //
171     // If we failed to set the change's UUID then we save its value
172     // using the user defined attribute original-UUID.  It's bad to
173     // throw away UUIDs.
174     //
175     if (err)
176     {
177         nstring at = nstring::format("%s+=%s", ORIGINAL_UUID, uuid.c_str());
178         nstring s2 =
179             nstring::format
180             (
181                 "aegis -change-attr %s -change=%ld -project=%s%s",
182                 at.quote_shell().c_str(),
183                 magic_zero_decode(change_number),
184                 project_name.c_str(),
185                 trace_options.c_str()
186             );
187         os_become_orig();
188         os_execute(s2, OS_EXEC_FLAG_INPUT | OS_EXEC_FLAG_ERROK, dd);
189         os_become_undo();
190     }
191 }
192 
193 
194 static long
number_of_files(string_ty * project_name,long change_number)195 number_of_files(string_ty *project_name, long change_number)
196 {
197     project      *pp;
198     change::pointer cp;
199     long            result;
200 
201     pp = project_alloc(project_name);
202     pp->bind_existing();
203     cp = change_alloc(pp, change_number);
204     change_bind_existing(cp);
205     result = change_file_count(cp);
206     change_free(cp);
207     project_free(pp);
208     return result;
209 }
210 
211 
212 static fstate_src_ty *
project_file_find_by_meta2(project * pp,cstate_src_ty * c_src_data,view_path_ty vp)213 project_file_find_by_meta2(project *pp, cstate_src_ty *c_src_data,
214     view_path_ty vp)
215 {
216     trace(("project_file_find_by_meta2(pp = %08lX, c_src = %08lX, vp = %s)\n"
217         "{\n", (long)pp, (long)c_src_data, view_path_ename(vp)));
218     trace
219     ((
220         "change: %s %s \"%s\" %s\n", file_usage_ename(c_src_data->usage),
221         file_action_ename(c_src_data->action),
222         c_src_data->file_name->str_text,
223         (c_src_data->edit_number ?
224             c_src_data->edit_number->str_text : "")
225     ));
226 #ifdef DEBUG
227     if (c_src_data->move)
228         trace(("  move \"%s\"\n", c_src_data->move->str_text));
229 #endif
230 
231     //
232     // Try to find the file by its UUID.
233     //
234     // For backwards compatibility reasons (UUIDs were introduced in
235     // 4.17) there could be a rename chain (pointing at UUID-less
236     // entries) to be taken into account.
237     //
238     if (c_src_data->uuid)
239     {
240         trace(("uuid = %s\n", c_src_data->uuid->str_text));
241         fstate_src_ty *p_src_data =
242             pp->file_find_by_uuid(c_src_data->uuid, vp);
243         while (p_src_data)
244         {
245             if (p_src_data->action != file_action_remove || !p_src_data->move)
246             {
247                 trace
248                 ((
249                     "project: %s %s \"%s\" %s %s\n",
250                     file_usage_ename(p_src_data->usage),
251                     file_action_ename(p_src_data->action),
252                     p_src_data->file_name->str_text,
253                     (p_src_data->edit_origin ?
254                         p_src_data->edit_origin->revision->str_text : ""),
255                     (p_src_data->edit ?
256                         p_src_data->edit->revision->str_text : "")
257                 ));
258                 trace(("return %08lX;\n", (long)p_src_data));
259                 trace(("}\n"));
260                 return p_src_data;
261             }
262             p_src_data = pp->file_find(p_src_data->move, vp);
263         }
264     }
265 
266     //
267     // Look for the file by name, tracking renames as they happen.
268     //
269     string_ty *name = c_src_data->file_name;
270     if (c_src_data->action == file_action_create && c_src_data->move)
271         name = c_src_data->move;
272     for (;;)
273     {
274         fstate_src_ty *p_src_data = pp->file_find(name, vp);
275         if (!p_src_data)
276         {
277             trace(("return NULL;\n"));
278             trace(("}\n"));
279             return 0;
280         }
281         if (p_src_data->action != file_action_remove || !p_src_data->move)
282         {
283             trace
284             ((
285                 "project: %s %s \"%s\" %s %s\n",
286                 file_usage_ename(p_src_data->usage),
287                 file_action_ename(p_src_data->action),
288                 p_src_data->file_name->str_text,
289                 (p_src_data->edit_origin ?
290                     p_src_data->edit_origin->revision->str_text : ""),
291                 (p_src_data->edit ?
292                     p_src_data->edit->revision->str_text : "")
293             ));
294             trace(("return %08lX;\n", (long)p_src_data));
295             trace(("}\n"));
296             return p_src_data;
297         }
298         name = p_src_data->move;
299     }
300 }
301 
302 
303 static cstate_src_ty *
find_src(cstate_ty * cs,string_ty * filename)304 find_src(cstate_ty *cs, string_ty *filename)
305 {
306     assert(cs);
307     assert(cs->src);
308     for (size_t j = 0; j < cs->src->length; ++j)
309     {
310         cstate_src_ty *src = cs->src->list[j];
311         if (str_equal(src->file_name, filename))
312             return src;
313     }
314     return 0;
315 }
316 
317 
318 static void
wrong_file(input & ifp,const nstring & expected)319 wrong_file(input &ifp, const nstring &expected)
320 {
321     nstring message =
322         nstring::format
323         (
324             "wrong file, expected \"%s\", "
325             "maybe the sender needs to use the --compatibility=%s option",
326             expected.c_str(),
327             version_stamp()
328         );
329     ifp->fatal_error(message.c_str());
330 }
331 
332 
333 static void
missing_file(input_cpio * ifp,const nstring & expected)334 missing_file(input_cpio *ifp, const nstring &expected)
335 {
336     nstring message =
337         nstring::format
338         (
339             "missing file, expected \"%s\", "
340             "maybe the sender needs to use the --compatibility=%s option",
341             expected.c_str(),
342             version_stamp()
343         );
344     ifp->fatal_error(message.c_str());
345 }
346 
347 
348 static void
build_uuid_list(cstate_ty * csp,nstring_list & uuids)349 build_uuid_list(cstate_ty *csp, nstring_list &uuids)
350 {
351     if (csp->uuid)
352         uuids.push_back(nstring(csp->uuid));
353     attributes_list_ty *alp = csp->attribute;
354     if (!alp)
355         return;
356     for (size_t j = 0; j < alp->length; ++j)
357     {
358         attributes_ty *ap = alp->list[j];
359         assert(ap);
360         if (!ap)
361             continue;
362         assert(ap->name);
363         if (!ap->name)
364             continue;
365         assert(ap->value);
366         if (!ap->value)
367             continue;
368         if (0 == strcasecmp(ORIGINAL_UUID, ap->name->str_text))
369             uuids.push_back(nstring(ap->value));
370     }
371 }
372 
373 
374 static bool
nothing_has_changed(change::pointer cp,user_ty::pointer up)375 nothing_has_changed(change::pointer cp, user_ty::pointer up)
376 {
377     //
378     // First we do a quick scan to see if the can avoid comparing any
379     // files at all.
380     //
381     for (size_t j = 0; ; ++j)
382     {
383         fstate_src_ty *src = change_file_nth(cp, j, view_path_first);
384         if (!src)
385             break;
386         if (src->action != file_action_modify)
387             return false;
388         if (src->usage == file_usage_build)
389             return false;
390     }
391 
392     //
393     // Compare each file in the change set with the same file in the
394     // baseline.
395     //
396     for (size_t k = 0; ; ++k)
397     {
398         fstate_src_ty *src = change_file_nth(cp, k, view_path_first);
399         if (!src)
400             break;
401 
402         nstring path1(cp->file_path(src));
403         assert(!path1.empty());
404         nstring path2(project_file_path(cp->pp, src));
405         assert(!path2.empty());
406 
407         up->become_begin();
408         bool different = files_are_different(path1, path2);
409         up->become_end();
410         if (different)
411             return false;
412     }
413 
414     //
415     // nothing has changed
416     //
417     return true;
418 }
419 
420 
421 void
receive_main(void)422 receive_main(void)
423 {
424     int             use_patch;
425     string_ty       *project_name;
426     long            change_number;
427     project      *pp;
428     change::pointer cp;
429     size_t          j;
430     cattr_ty        *cattr_data;
431     string_ty       *attribute_file_name;
432     move_list_ty    files_moved;
433     int             could_have_a_trojan;
434     int             config_seen;
435     int             uncopy;
436     int             trojan;
437     string_ty       *devdir;
438     int             exec_mode;
439     int             non_exec_mode;
440     int             ignore_uuid;
441 
442     project_name = 0;
443     change_number = 0;
444     trojan = -1;
445     nstring delta;
446     devdir = 0;
447     use_patch = -1;
448     ignore_uuid = -1;
449     nstring ifn;
450     nstring output_filename;
451     arglex();
452     while (arglex_token != arglex_token_eoln)
453     {
454         switch (arglex_token)
455         {
456         default:
457             generic_argument(usage);
458             continue;
459 
460         case arglex_token_change:
461             arglex();
462             arglex_parse_change(&project_name, &change_number, usage);
463             continue;
464 
465         case arglex_token_project:
466             arglex();
467             arglex_parse_project(&project_name, usage);
468             continue;
469 
470         case arglex_token_file:
471             if (!ifn.empty())
472                 duplicate_option(usage);
473             switch (arglex())
474             {
475             default:
476                 option_needs_file(arglex_token_file, usage);
477                 // NOTREACHED
478 
479             case arglex_token_string:
480                 ifn = arglex_value.alv_string;
481                 break;
482 
483             case arglex_token_stdio:
484                 ifn = "-";
485                 break;
486             }
487             break;
488 
489         case arglex_token_trojan:
490             if (trojan > 0)
491                 duplicate_option(usage);
492             if (trojan >= 0)
493             {
494                 too_many_trojans:
495                 mutually_exclusive_options
496                 (
497                     arglex_token_trojan,
498                     arglex_token_trojan_not,
499                     usage
500                 );
501             }
502             trojan = 1;
503             break;
504 
505         case arglex_token_trojan_not:
506             if (trojan == 0)
507                 duplicate_option(usage);
508             if (trojan >= 0)
509                 goto too_many_trojans;
510             trojan = 0;
511             break;
512 
513         case arglex_token_delta:
514             if (!delta.empty())
515                 duplicate_option(usage);
516             switch (arglex())
517             {
518             default:
519                 option_needs_number(arglex_token_delta, usage);
520                 // NOTREACHED
521 
522             case arglex_token_number:
523                 delta =
524                     nstring::format(" --delta=%ld", arglex_value.alv_number);
525                 break;
526 
527             case arglex_token_string:
528                 {
529                     if (arglex_value.alv_string[0] == 0)
530                         option_needs_number(arglex_token_delta, usage);
531                     nstring arg = arglex_value.alv_string;
532                     arg = arg.quote_shell();
533                     delta = nstring::format(" --delta=%s", arg.c_str());
534                 }
535                 break;
536             }
537             break;
538 
539         case arglex_token_directory:
540             if (devdir)
541             {
542                 duplicate_option(usage);
543                 // NOTREACHED
544             }
545             if (arglex() != arglex_token_string)
546             {
547                 option_needs_dir(arglex_token_directory, usage);
548                 // NOTREACHED
549             }
550             devdir = str_format(" --directory %s", arglex_value.alv_string);
551             break;
552 
553         case arglex_token_patch:
554             if (use_patch > 0)
555                 duplicate_option(usage);
556             if (use_patch >= 0)
557             {
558                 too_many_patchs:
559                 mutually_exclusive_options
560                 (
561                     arglex_token_patch,
562                     arglex_token_patch_not,
563                     usage
564                 );
565             }
566             use_patch = 1;
567             break;
568 
569         case arglex_token_patch_not:
570             if (use_patch == 0)
571                 duplicate_option(usage);
572             if (use_patch >= 0)
573                 goto too_many_patchs;
574             use_patch = 0;
575             break;
576 
577         case arglex_token_uuid_not:
578         case arglex_token_ignore_uuid:
579             if (ignore_uuid > 0)
580                 duplicate_option(usage);
581             if (ignore_uuid >= 0)
582             {
583                 too_many_ignore_uuid:
584                 mutually_exclusive_options
585                 (
586                     arglex_token_ignore_uuid,
587                     arglex_token_ignore_uuid_not,
588                     usage
589                 );
590             }
591             ignore_uuid = 1;
592             break;
593 
594         case arglex_token_uuid:
595         case arglex_token_ignore_uuid_not:
596             if (ignore_uuid == 0)
597                 duplicate_option(usage);
598             if (ignore_uuid >= 0)
599                 goto too_many_ignore_uuid;
600             ignore_uuid = 0;
601             break;
602 
603         case arglex_token_output:
604             if (!output_filename.empty())
605                 duplicate_option(usage);
606             if (arglex() != arglex_token_string)
607             {
608                 option_needs_file(arglex_token_output, usage);
609                 // NOTREACHED
610             }
611             output_filename = nstring(arglex_value.alv_string);
612             break;
613         }
614         arglex();
615     }
616     if (change_number && !output_filename.empty())
617     {
618         mutually_exclusive_options
619         (
620             arglex_token_change,
621             arglex_token_output,
622             usage
623         );
624     }
625 
626     //
627     // Open the input file and verify the format.
628     //
629     input_cpio *cpio_p = aedist_open(ifn);
630     assert(cpio_p);
631 
632     //
633     // Calculate the --reason option to use with the
634     // "aegis --new-change" command, if any.
635     //
636     nstring reason;
637     bool remote = false;
638     if (cpio_p->is_remote())
639     {
640         reason =
641             " --reason=" + ("Downloaded from " + cpio_p->name()).quote_shell();
642         remote = true;
643     }
644 
645     //
646     // read the project name from the archive,
647     // and use it to default the project if not given
648     //
649     os_become_orig();
650     nstring archive_name;
651     input ifp = cpio_p->child(archive_name);
652     if (!ifp.is_open())
653         missing_file(cpio_p, "etc/project-name");
654     assert(!archive_name.empty());
655     if (archive_name != "etc/project-name")
656         wrong_file(ifp, "etc/project-name");
657     {
658     nstring s;
659     if (!ifp->one_line(s) || s.empty())
660         ifp->fatal_error("short file");
661     if (!project_name)
662         project_name = s.get_ref_copy();
663     }
664     ifp.close();
665     os_become_undo();
666 
667     //
668     // locate project data
669     //      (Even of we don't use it, this confirms it is a valid
670     //      project name.)
671     //
672     pp = project_alloc(project_name);
673     pp->bind_existing();
674 
675     //
676     // Read the change number form the archive, if it's there.  Use that
677     // number (a) if the user didn't specify one on the command line,
678     // and (b) that number is available.
679     //
680     os_become_orig();
681     archive_name.clear();
682     ifp = cpio_p->child(archive_name);
683     if (!ifp.is_open())
684         missing_file(cpio_p, "etc/change-set");
685     assert(archive_name);
686     if (archive_name == "etc/change-number")
687     {
688         nstring s;
689         if (!ifp->one_line(s) || s.empty())
690             ifp->fatal_error("short file");
691         long proposed_change_number = s.to_long();
692         ifp.close();
693         os_become_undo();
694 
695         //
696         // Make sure the change number is available.
697         //
698         if
699         (
700             !change_number
701         &&
702             proposed_change_number > 0
703         &&
704             !project_change_number_in_use(pp, proposed_change_number)
705         )
706             change_number = proposed_change_number;
707 
708         //
709         // Start the next file, so we are in the same state as when
710         // there is no change number included.
711         //
712         archive_name.clear();
713         os_become_orig();
714         ifp = cpio_p->child(archive_name);
715         if (!ifp.is_open())
716             missing_file(cpio_p, "etc/change-set");
717     }
718     os_become_undo();
719 
720     //
721     // default the change number
722     //
723     if (!change_number)
724         change_number = project_next_change_number(pp, 1);
725 
726     //
727     // If the user asked for it, write the change number in the output
728     // file.
729     //
730     if (!output_filename.empty())
731     {
732         os_become_orig();
733         output::pointer ofp(output_file::open(output_filename));
734         os_become_undo();
735 
736         ofp->fprintf("%ld\n", change_number);
737     }
738 
739     //
740     // get the change details from the input
741     //
742     os_become_orig();
743     if (archive_name != "etc/change-set")
744         wrong_file(ifp, "etc/change-set");
745     cstate_ty *change_set = (cstate_ty *)parse_input(ifp, &cstate_type);
746     ifp.close();
747     os_become_undo();
748 
749     nstring_list incoming_uuids;
750     build_uuid_list(change_set, incoming_uuids);
751 
752     //
753     // Make sure we like the change set at a macro level.
754     //
755     if
756     (
757         !change_set->brief_description
758     ||
759         !change_set->description
760     ||
761         !change_set->src
762     ||
763         !change_set->src->length
764     )
765         cpio_p->fatal_error("bad change set");
766     for (j = 0; j < change_set->src->length; ++j)
767     {
768         cstate_src_ty   *src_data;
769 
770         src_data = change_set->src->list[j];
771         if
772         (
773             !src_data->file_name
774         ||
775             !src_data->file_name->str_length
776         ||
777             !(src_data->mask & cstate_src_action_mask)
778         ||
779             !(src_data->mask & cstate_src_usage_mask)
780         )
781             cpio_p->fatal_error("bad change info");
782     }
783 
784     //
785     // We refuse to receive a change we already have because that can
786     // undo changes.  If the local repository is the source of the
787     // change set, then the patch will fail to apply and the full
788     // source will be used, thus successive changes will be removed.
789     // The -ignore-uuid option is here to handle UUID clash, if any.
790     //
791     nstring branch;
792     nstring original_uuid;
793     time_t original_ipass_when = 0;
794     symtab<change> local_inventory;
795     bool include_branches = true;
796     bool all_changes = false;
797     bool ignore_original_uuid = true;
798     change_functor_inventory_builder cf(include_branches, all_changes,
799         ignore_original_uuid, pp, &local_inventory);
800     project_inventory_walk(pp, cf);
801     if (ignore_uuid <= 0)
802     {
803         if (change_set->uuid)
804         {
805             assert(universal_unique_identifier_valid(change_set->uuid));
806             change::pointer c = local_inventory.query(change_set->uuid);
807             if (c)
808             {
809                 assert(c->cstate_data->uuid);
810                 assert(universal_unique_identifier_valid(c->cstate_data->uuid));
811                 assert(str_equal(change_set->uuid, c->cstate_data->uuid));
812 
813                 //
814                 // run away, run away!
815                 //
816                 error_intl(0, i18n("change already present"));
817                 return;
818             }
819         }
820 
821         //
822         // We now add to the local inventory also the original-UUIDs.
823         //
824         ignore_original_uuid = false;
825         change_functor_inventory_builder cf2(include_branches, all_changes,
826             ignore_original_uuid, pp, &local_inventory);
827         project_inventory_walk(pp, cf2);
828 
829         //
830         // If the user has not specified a delta to copy file from, we
831         // try to guess using the original-UUID attribute of the
832         // change, if any.
833         //
834         if (delta.empty() && change_set->attribute)
835         {
836             //
837             // Using the 'original-UUID' of the received change we look
838             // for a completed change with a matching 'uuid'.
839             //
840             for (size_t i = 0; i < change_set->attribute->length; ++i)
841             {
842                 attributes_ty *current = change_set->attribute->list[i];
843                 if (!current || !current->name || !current->value)
844                     continue;
845                 if (0 != strcasecmp(current->name->str_text, ORIGINAL_UUID))
846                     continue;
847                 if (!universal_unique_identifier_valid(current->value))
848                     continue;
849                 change::pointer ancestor =
850                     local_inventory.query(current->value);
851                 if (!ancestor || !ancestor->is_completed())
852                     continue;
853                 trace_string(current->value->str_text);
854 
855                 //
856                 // We must keep track of the original-uuid and of the
857                 // timestamp of the change completion for later use in
858                 // the delta selection stage.
859                 //
860                 original_uuid = nstring(current->value);
861                 original_ipass_when =
862                     change_when_get
863                     (
864                         ancestor,
865                         cstate_history_what_integrate_pass
866                     );
867 
868                 //
869                 // This works even if the ancestor change is
870                 // in the trunk.
871                 //
872                 assert(ancestor->delta_number_get() > 0);
873                 delta =
874                     nstring::format
875                     (
876                         " --delta=%ld",
877                         ancestor->delta_number_get()
878                     );
879                 trace_string(delta.c_str());
880 
881                 if (ancestor->pp)
882                     branch = nstring(project_version_short_get(ancestor->pp));
883                 if (!branch.empty())
884                     branch = " --branch=" + branch;
885                 else
886                     branch = " --trunk";
887                 trace_nstring(branch);
888                 break;
889             }
890         }
891     }
892 
893     //
894     // We need to work out if we are a project administartor.  This lets
895     // us pass the testing atrributes through literally, rather than
896     // modulo the defaults.
897     //
898     user_ty::pointer up = user_ty::create();
899     bool am_admin = project_administrator_query(pp, up->name());
900 
901     //
902     // construct change attributes from the change_set
903     //
904     // Be careful when copying across the testing exemptions, to
905     // make sure we don't ask for an exemption we can't have.
906     //
907     os_become_orig();
908     attribute_file_name = os_edit_filename(0);
909     undo_unlink_errok(attribute_file_name);
910     os_become_undo();
911     cattr_data = (cattr_ty *)cattr_type.alloc();
912     cattr_data->brief_description = str_copy(change_set->brief_description);
913     cattr_data->description = str_copy(change_set->description);
914     cattr_data->cause = change_set->cause;
915     if (am_admin)
916     {
917         //
918         // Only project administrators can do this, otherwise we risk
919         // having the "aegis --new-change" command fail, which isn't nice.
920         //
921         cattr_data->test_exempt = change_set->test_exempt;
922         cattr_data->test_baseline_exempt = change_set->test_baseline_exempt;
923         cattr_data->regression_test_exempt = change_set->regression_test_exempt;
924     }
925     else
926     {
927         cattr_ty *dflt = (cattr_ty *)cattr_type.alloc();
928         dflt->cause = change_set->cause;
929         pconf_ty *pconf_data = project_pconf_get(pp);
930         change_attributes_default(dflt, pp, pconf_data);
931 
932         cattr_data->test_exempt =
933             (change_set->test_exempt && dflt->test_exempt);
934         cattr_data->test_baseline_exempt =
935             (change_set->test_baseline_exempt && dflt->test_baseline_exempt);
936         cattr_data->regression_test_exempt =
937             (
938                 change_set->regression_test_exempt
939             &&
940                 dflt->regression_test_exempt
941             );
942         cattr_type.free(dflt);
943     }
944     if (change_set->attribute)
945         cattr_data->attribute = attributes_list_copy(change_set->attribute);
946     if (remote)
947     {
948         if (!cattr_data->attribute)
949             cattr_data->attribute =
950                 (attributes_list_ty *)attributes_list_type.alloc();
951         attributes_list_insert
952         (
953             cattr_data->attribute,
954             "foreign-copyright",
955             "true"
956         );
957     }
958 
959     os_become_orig();
960     cattr_write_file(attribute_file_name, cattr_data, 0);
961     os_become_undo();
962     cattr_type.free(cattr_data);
963     cattr_data = 0;
964     project_free(pp);
965     pp = 0;
966 
967     //
968     // create the new change
969     //
970     nstring trace_options(trace_args());
971     os_become_orig();
972     nstring dot(os_curdir());
973     nstring new_change_command =
974         nstring::format
975         (
976             "aegis --new-change %ld --project=%s --file=%s --verbose%s%s",
977             magic_zero_decode(change_number),
978             project_name->str_text,
979             attribute_file_name->str_text,
980             reason.c_str(),
981             trace_options.c_str()
982         );
983     os_execute(new_change_command, OS_EXEC_FLAG_INPUT, dot);
984     os_unlink_errok(attribute_file_name);
985 
986     //
987     // Begin development of the new change.
988     //
989     nstring develop_begin_command =
990         nstring::format
991         (
992             "aegis --develop-begin %ld --project %s --verbose%s%s",
993             magic_zero_decode(change_number),
994             project_name->str_text,
995             (devdir ? devdir->str_text : ""),
996             trace_options.c_str()
997         );
998     os_execute(develop_begin_command, OS_EXEC_FLAG_INPUT, dot);
999     os_become_undo();
1000 
1001     //
1002     // Change to the development directory, so that we can use
1003     // relative filenames.  It makes things easier to read.
1004     //
1005     pp = project_alloc(project_name);
1006     pp->bind_existing();
1007     cp = change_alloc(pp, change_number);
1008     change_bind_existing(cp);
1009     nstring dd(change_development_directory_get(cp, 0));
1010     change_free(cp);
1011     cp = 0;
1012 
1013     os_chdir(dd);
1014 
1015     //
1016     // Adjust the file actions to ensure that renames are really
1017     // renames.  Historical versions of Aegis were not as fussy as now,
1018     // and you could get half a rename by aermu or aenfu of one half.
1019     //
1020     trace(("check that renames are complete\n"));
1021     for (j = 0; j < change_set->src->length; ++j)
1022     {
1023         cstate_src_ty *src1 = change_set->src->list[j];
1024         if (!src1->move)
1025             continue;
1026         trace(("%s \"%s\", move=\"%s\"\n", file_action_ename(src1->action),
1027                src1->file_name->str_text, src1->move->str_text));
1028         cstate_src_ty *src2 = find_src(change_set, src1->move);
1029         if (!src2)
1030         {
1031             // Only half a move.
1032             trace(("half a move\n"));
1033             str_free(src1->move);
1034             src1->move = 0;
1035             continue;
1036         }
1037 
1038         switch (src1->action)
1039         {
1040         case file_action_create:
1041             if (src2->action != file_action_remove)
1042             {
1043                 // Something really weird happened.
1044                 trace(("not a move\n"));
1045                 assert(0);
1046                 str_free(src1->move);
1047                 src1->move = 0;
1048             }
1049             break;
1050 
1051         case file_action_remove:
1052             if (src2->action != file_action_create)
1053             {
1054                 //
1055                 // Something happened to the file after the rename.
1056                 // This can occur when the change is a branch.
1057                 //
1058                 trace(("corresponds to %s \"%s\"\n",
1059                        file_action_ename(src2->action),
1060                        src2->file_name->str_text));
1061                 trace(("not a simple move\n"));
1062                 //
1063                 // If the target was later removed we short-circuit.
1064                 // This is needed because some versions of aedist -send
1065                 // erroneously represent (rename -> remove) by
1066                 // (rename <--> rename).
1067                 // This solution is wrong if a change_set can contain
1068                 // a chain, but the sender supposedly collapses chains.
1069                 //
1070                 if (src2->action == file_action_remove)
1071                 {
1072                   str_free(src1->move);
1073                   src1->move = 0;
1074                 }
1075             }
1076             break;
1077 
1078         case file_action_modify:
1079         case file_action_insulate:
1080         case file_action_transparent:
1081 #ifdef DEBUG
1082         default:
1083 #endif
1084             //
1085             // None of these actions are supposed to have the move field
1086             // set.
1087             //
1088             trace(("not a move\n"));
1089             assert(0);
1090             str_free(src1->move);
1091             src1->move = 0;
1092             break;
1093         }
1094     }
1095 
1096     //
1097     // Adjust the file actions to reflect the current state of
1098     // the project.
1099     //
1100     trace(("adjust file actions\n"));
1101     bool need_to_test = false;
1102     could_have_a_trojan = 0;
1103     symtab<nstring> old_name;
1104     for (j = 0; j < change_set->src->length; ++j)
1105     {
1106         cstate_src_ty   *src_data;
1107         fstate_src_ty   *p_src_data;
1108 
1109         src_data = change_set->src->list[j];
1110         assert(src_data);
1111         assert(src_data->file_name);
1112         trace(("%ld %s %s\n", (long) j, src_data->file_name->str_text,
1113             file_action_ename(src_data->action)));
1114         p_src_data =
1115             project_file_find_by_meta2(pp, src_data, view_path_extreme);
1116         switch (src_data->action)
1117         {
1118         case file_action_remove:
1119             //
1120             // Removing a removed file would be an
1121             // error, so take it out of the list completely.
1122             //
1123             trace(("remove\n"));
1124             if (!p_src_data)
1125             {
1126                 trace(("no matching project file\n"));
1127                 if (src_data->move)
1128                 {
1129                     //
1130                     // does the target file exist?
1131                     //
1132                     trace(("which makes rename difficult\n"));
1133                     p_src_data =
1134                         pp->file_find(src_data->move, view_path_extreme);
1135                     cstate_src_ty *src2 = find_src(change_set, src_data->move);
1136                     assert(src2); // we already verified this
1137                     if (p_src_data)
1138                     {
1139                         trace(("rewrite as modify\n"));
1140                         src2->action = file_action_modify;
1141                     }
1142                     else
1143                     {
1144                         trace(("rewrite as create\n"));
1145                         src2->action = file_action_create;
1146                     }
1147                     str_free(src2->move);
1148                     src2->move = 0;
1149 
1150                     //
1151                     // We also want to ignore the remove half of the
1152                     // rename, so fall through...
1153                     //
1154                 }
1155 
1156                 //
1157                 // It's tempting to fill the hole the removed element
1158                 // leaves with the last list element and in doing so,
1159                 // shortening the list.  But that would mean the order
1160                 // of the files in the change set differs from the order
1161                 // of the files in the cpio archive, but this would
1162                 // create problems later.  We have to do it the long way
1163                 // and shuffle everything down.
1164                 //
1165                 nuke_and_skip:
1166                 trace(("nuke and skip\n"));
1167                 for (size_t k = j + 1; k < change_set->src->length; ++k)
1168                     change_set->src->list[k - 1] = change_set->src->list[k];
1169                 cstate_src_type.free(src_data);
1170                 --j;
1171                 change_set->src->length--;
1172                 continue;
1173             }
1174 
1175             //
1176             // The view_path_extreme argument to project_find_file_by_
1177             // meta2 was supposed to take care of making removed files
1178             // disappear.
1179             //
1180             assert(p_src_data->action != file_action_remove);
1181 
1182             //
1183             // Take care of removing and renaming files which don't have
1184             // the same name in the project as in the incoming change set.
1185             //
1186             if (!str_equal(src_data->file_name, p_src_data->file_name))
1187             {
1188                 if (src_data->move)
1189                 {
1190                     if (str_equal(src_data->move, p_src_data->file_name))
1191                     {
1192                         //
1193                         // The file has already been renamed, so there
1194                         // is no need to remove it, but instead we turn
1195                         // the target half into a modify action if it is
1196                         // just a create action.
1197                         //
1198                         trace(("rename becomes modify\n"));
1199                         cstate_src_ty *src2 =
1200                             find_src(change_set, src_data->move);
1201                         if (src2 && (src2->action == file_action_create))
1202                         {
1203                             assert(src2->move);
1204                             assert(str_equal(src2->move,src_data->file_name));
1205                             str_free(src2->move);
1206                             src2->move = 0;
1207                             src2->action = file_action_modify;
1208                         }
1209                         goto nuke_and_skip;
1210                     }
1211 
1212                     //
1213                     // We are about to update the file name in the
1214                     // incoming change set to match the file name in the
1215                     // project.  But first we must update the create
1216                     // half of the rename as well.
1217                     //
1218                     trace(("fix rename To half\n"));
1219                     cstate_src_ty *src2 =
1220                         find_src(change_set, src_data->move);
1221                     assert(src2);
1222                     assert(src2->move);
1223                     assert(str_equal(src2->move, src_data->file_name));
1224 
1225                     str_free(src2->move);
1226                     src2->move = str_copy(p_src_data->file_name);
1227                 }
1228 
1229                 //
1230                 // Update the file name in the incoming change set to
1231                 // match the file name in the project.
1232                 // Save the old file name, we will use it to look into
1233                 // the archive.
1234                 //
1235                 trace(("file name changed\n"));
1236                 old_name.assign
1237                 (
1238                     nstring(p_src_data->file_name),
1239                     nstring(src_data->file_name)
1240                 );
1241                 str_free(src_data->file_name);
1242                 src_data->file_name = str_copy(p_src_data->file_name);
1243             }
1244             break;
1245 
1246         case file_action_transparent:
1247             trace(("transparent\n"));
1248             assert(!src_data->move);
1249             if (!p_src_data)
1250                 goto nuke_and_skip;
1251 
1252             //
1253             // Do we need to change the name of the file?
1254             //
1255             if (!str_equal(src_data->file_name, p_src_data->file_name))
1256             {
1257                 trace(("file name changed\n"));
1258                 //
1259                 // Save the old file name, we will use it to look into
1260                 // the archive.
1261                 //
1262                 old_name.assign
1263                 (
1264                     nstring(p_src_data->file_name),
1265                     nstring(src_data->file_name)
1266                 );
1267                 str_free(src_data->file_name);
1268                 src_data->file_name = str_copy(p_src_data->file_name);
1269             }
1270             // FIXME: do we need to check that making it transparent is
1271             // even possible in this branch?!?
1272             break;
1273 
1274         case file_action_create:
1275             trace(("create\n"));
1276             if (src_data->move)
1277             {
1278                 //
1279                 // Is the file missing from the repository?
1280                 // Has the rename already happened?
1281                 //
1282                 trace(("create half or rename\n"));
1283                 if
1284                 (
1285                     !p_src_data
1286                 ||
1287                     str_equal(src_data->file_name, p_src_data->file_name)
1288                 )
1289                 {
1290                     //
1291                     // Alter the file action.
1292                     //
1293                     if (p_src_data)
1294                     {
1295                         trace(("rename becomes modify\n"));
1296                         src_data->action = file_action_modify;
1297                     }
1298                     else
1299                     {
1300                         trace(("rename becomes create\n"));
1301                         src_data->action = file_action_create;
1302                     }
1303 
1304 
1305                     //
1306                     // Nuke the remove half of the rename.
1307                     //
1308                     // We can't just leave it for the remove half to
1309                     // clean up, because it may be after this point,
1310                     // and the clues are gone.  Plus, it would break
1311                     // the assertion that all renames have both halves
1312                     // present in the file list.
1313                     //
1314                     size_t m = 0;
1315                     for (m = 0; m < change_set->src->length; ++m)
1316                     {
1317                         cstate_src_ty *src2 = change_set->src->list[m];
1318                         if (str_equal(src_data->move, src2->file_name))
1319                         {
1320                             assert(src2->action == file_action_remove);
1321                             cstate_src_type.free(src2);
1322                             break;
1323                         }
1324                     }
1325                     assert(m < change_set->src->length);
1326                     for (size_t k = m + 1; k < change_set->src->length; ++k)
1327                         change_set->src->list[k - 1] = change_set->src->list[k];
1328                     str_free(src_data->move);
1329                     src_data->move = 0;
1330                     //
1331                     // We need to go back one step only if the remove
1332                     // half was before the current file.
1333                     //
1334                     if (m < j)
1335                         --j;
1336                     change_set->src->length--;
1337                 }
1338                 else
1339                 {
1340                     //
1341                     // The rename hasn't happened yet.
1342                     // Do not mess with the file names.
1343                     //
1344                     trace(("rename is clean\n"));
1345                 }
1346             }
1347             else
1348             {
1349                 //
1350                 // The file exists in the project.  Alter the incoming
1351                 // change set to modify the file.
1352                 //
1353                 if (p_src_data)
1354                 {
1355                     //
1356                     // FIXME: we need to look for tests with the same
1357                     // name but different UUIDs, and automagically give
1358                     // them a new name, rather than simply ignoring the
1359                     // UUID.
1360                     //
1361                     trace(("create becomes modify\n"));
1362                     src_data->action = file_action_modify;
1363 
1364                     //
1365                     // If the name is different (this only happens when
1366                     // the incoming file has a UUID) make it match the
1367                     // project.
1368                     //
1369                     if (!str_equal(src_data->file_name, p_src_data->file_name))
1370                     {
1371                         trace(("file name changed\n"));
1372                         //
1373                         // Save the old file name, we will use it to
1374                         // look into the archive.
1375                         //
1376                         old_name.assign
1377                         (
1378                             nstring(p_src_data->file_name),
1379                             nstring(src_data->file_name)
1380                         );
1381                         str_free(src_data->file_name);
1382                         src_data->file_name = str_copy(p_src_data->file_name);
1383                     }
1384                 }
1385             }
1386             break;
1387 
1388         case file_action_insulate:
1389             assert(0);
1390             // fall through...
1391 
1392         case file_action_modify:
1393             trace(("modify\n"));
1394             assert(!src_data->move);
1395             if (!p_src_data)
1396             {
1397                 //
1398                 // The named file doesn't exist in the project, to
1399                 // convert the action into a create instead.
1400                 //
1401                 trace(("modify becomes create\n"));
1402                 src_data->action = file_action_create;
1403             }
1404             else
1405             {
1406                 //
1407                 // Make sure the action is a modify (it is supposed to
1408                 // be impossible for an insulate action to get here, but
1409                 // you never know).
1410                 //
1411                 src_data->action = file_action_modify;
1412 
1413                 //
1414                 // If the name is different (this only happens when
1415                 // the incoming file has a UUID) make it match the
1416                 // project.
1417                 //
1418                 if (!str_equal(src_data->file_name, p_src_data->file_name))
1419                 {
1420                     trace(("file name changed\n"));
1421                     //
1422                     // Save the old file name, we will use it to look
1423                     // into the archive.
1424                     //
1425                     old_name.assign
1426                     (
1427                         nstring(p_src_data->file_name),
1428                         nstring(src_data->file_name)
1429                     );
1430                     str_free(src_data->file_name);
1431                     src_data->file_name = str_copy(p_src_data->file_name);
1432                 }
1433             }
1434             break;
1435         }
1436 
1437         //
1438         // Watch out for infection vectors.
1439         //
1440         trace(("check for trojans\n"));
1441         if (project_file_trojan_suspect(pp, src_data->file_name))
1442             could_have_a_trojan = 1;
1443     }
1444 
1445     //
1446     // add the removed files to the change
1447     //
1448     trace(("look for removed files\n"));
1449     move_list_constructor(&files_moved);
1450     nstring_list files_source;
1451 
1452     for (j = 0; j < change_set->src->length; ++j)
1453     {
1454         cstate_src_ty   *src_data;
1455         fstate_src_ty   *p_src_data;
1456 
1457         //
1458         // For now, we are only removing files.
1459         //
1460         src_data = change_set->src->list[j];
1461         assert(src_data);
1462         assert(src_data->file_name);
1463         p_src_data =
1464             project_file_find_by_meta2(pp, src_data, view_path_extreme);
1465         switch (src_data->action)
1466         {
1467         case file_action_remove:
1468             break;
1469 
1470         case file_action_modify:
1471             assert(p_src_data);
1472             if (p_src_data && p_src_data->usage != src_data->usage)
1473             {
1474                 //
1475                 // When files change type, it is necessary to remove
1476                 // them *and* then create them in the same change.
1477                 // Make sure the create loop also creates this file.
1478                 //
1479                 switch (p_src_data->usage)
1480                 {
1481                 case file_usage_config:
1482                     //
1483                     // We are receiving an archive that contains a file
1484                     // that our repository knows as file_usage_config,
1485                     // but deleting the last config file is forbidden
1486                     // and is wrong because we are receiving an old
1487                     // style archive.  Thus we skip the deletion.
1488                     //
1489                     continue;
1490 
1491                 case file_usage_source:
1492                 case file_usage_test:
1493                 case file_usage_manual_test:
1494                 case file_usage_build:
1495 #ifndef DEBUG
1496                 default:
1497 #endif
1498                     trace(("modify becomes create\n"));
1499                     src_data->action = file_action_create;
1500 
1501                     if (src_data->uuid)
1502                     {
1503                         //
1504                         // Remove the UUID so that we don't try to set
1505                         // it later (because we do this, later, for all
1506                         // created files with UUIDs).
1507                         //
1508                         str_free(src_data->uuid);
1509                         src_data->uuid = 0;
1510                     }
1511                     break;
1512                 }
1513                 break;
1514             }
1515             continue;
1516 
1517         case file_action_create:
1518         case file_action_insulate:
1519         case file_action_transparent:
1520 #ifndef DEBUG
1521         default:
1522 #endif
1523             continue;
1524         }
1525 
1526         //
1527         // add it to the list
1528         //
1529         if (src_data->move)
1530         {
1531             assert(src_data->action == file_action_remove);
1532             move_list_append_remove
1533             (
1534                 &files_moved,
1535                 src_data->file_name,
1536                 src_data->move
1537             );
1538         }
1539         else
1540             files_source.push_back_unique(nstring(src_data->file_name));
1541     }
1542     if (!files_source.empty())
1543     {
1544         nstring s =
1545             nstring::format
1546             (
1547                 "aegis --remove-file --project=%s --change=%ld%s --verbose",
1548                 project_name->str_text,
1549                 magic_zero_decode(change_number),
1550                 trace_options.c_str()
1551             );
1552         os_xargs(s, files_source, dd);
1553     }
1554 
1555     //
1556     // add the modified files to the change
1557     //
1558     trace(("look for modified files\n"));
1559     files_source.clear();
1560     symtab<nstring_list> files_source_by_origin;
1561     for (j = 0; j < change_set->src->length; ++j)
1562     {
1563         cstate_src_ty   *src_data;
1564 
1565         //
1566         // For now, we are only copying files.
1567         //
1568         src_data = change_set->src->list[j];
1569         assert(src_data->file_name);
1570         switch (src_data->action)
1571         {
1572         case file_action_modify:
1573             switch (src_data->usage)
1574             {
1575             case file_usage_build:
1576                 break;
1577 
1578             case file_usage_test:
1579             case file_usage_manual_test:
1580                 need_to_test = true;
1581                 // fall through...
1582 
1583             case file_usage_source:
1584             case file_usage_config:
1585                 //
1586                 // We must initialize the origin with the information
1587                 // from previous processing.
1588                 //
1589                 nstring origin = branch + delta;
1590 
1591                 //
1592                 // We have selected an origin delta using the
1593                 // original-UUID attribute contained in the archive.
1594                 // If we are receiving a branch that also contain the
1595                 // entire_source we must check that the change set
1596                 // that the UUID identify contains every single file.
1597                 //
1598                 if (!original_uuid.empty() && !branch.empty())
1599                 {
1600                     change::pointer ancestor =
1601                         local_inventory.query(original_uuid);
1602                     assert(ancestor);
1603                     fstate_src_ty *fsrc =
1604                         ancestor->file_find
1605                         (
1606                             nstring(src_data->file_name),
1607                             view_path_first
1608                         );
1609                     //
1610                     // We reset the origin if the ancestor change does
1611                     // not contain the file.
1612                     //
1613                     if (!fsrc)
1614                         origin = "";
1615                 }
1616 
1617                 if (src_data->attribute)
1618                 {
1619                     attributes_ty *edit_origin_UUID =
1620                         attributes_list_find
1621                         (
1622                             src_data->attribute,
1623                             EDIT_ORIGIN_UUID
1624                         );
1625                     if (edit_origin_UUID)
1626                     {
1627                         change::pointer ancestor =
1628                             local_inventory.query(edit_origin_UUID->value);
1629 
1630                         if (ancestor)
1631                         {
1632                             assert(ancestor->delta_number_get() > 0);
1633                             time_t ancestor_ipass_when =
1634                                 change_when_get
1635                                 (
1636                                     ancestor,
1637                                     cstate_history_what_integrate_pass
1638                                 );
1639 
1640                             //
1641                             // We select the delta to merge with using
1642                             // the most recently integrated change set.
1643                             //
1644                             if (ancestor_ipass_when > original_ipass_when)
1645                             {
1646                                 //
1647                                 // We cannot reuse existing delta and
1648                                 // branch variables, which used to
1649                                 // contains data from previous processing,
1650                                 // originl-UUID or options related,
1651                                 // because this will cause wrong origin
1652                                 // selection.
1653                                 //
1654                                 nstring ancestor_delta =
1655                                     nstring::format
1656                                     (
1657                                         " --delta=%ld",
1658                                         ancestor->delta_number_get()
1659                                     );
1660                                 trace_string(ancestor_delta.c_str());
1661 
1662                                 nstring ancestor_branch =
1663                                     nstring
1664                                     (
1665                                         project_version_short_get(ancestor->pp)
1666                                     );
1667                                 if (!ancestor_branch.empty())
1668                                     ancestor_branch =
1669                                         " --branch=" + ancestor_branch;
1670                                 else
1671                                     ancestor_branch = " --trunk";
1672                                 trace_nstring(ancestor_branch);
1673                                 origin = ancestor_branch + ancestor_delta;
1674                             }
1675                         }
1676                     }
1677                 }
1678 
1679                 //
1680                 // We create a hash table with origin as the key and
1681                 // with the list of files to be copied as the value.
1682                 //
1683                 nstring_list *file_list =
1684                     files_source_by_origin.query(origin);
1685                 if (!file_list)
1686                 {
1687                     file_list = new nstring_list;
1688                     files_source_by_origin.assign(origin, file_list);
1689                 }
1690                 file_list->push_back_unique(nstring(src_data->file_name));
1691                 break;
1692             }
1693             break;
1694 
1695         case file_action_create:
1696         case file_action_remove:
1697         case file_action_transparent:
1698             break;
1699 
1700         case file_action_insulate:
1701 #ifndef DEBUG
1702         default:
1703 #endif
1704             assert(0);
1705             break;
1706         }
1707 
1708         //
1709         // FIXME: we need to look for tests with the same name but
1710         // different UUIDs, and automagically give them a new name,
1711         // rather than simply ignoring the UUID.
1712         //
1713     }
1714     uncopy = 0;
1715 
1716     //
1717     // We copy modified files from the baseline grouping them using
1718     // the origin.
1719     //
1720     if (!files_source_by_origin.empty())
1721     {
1722         uncopy = 1;
1723         nstring_list origin;
1724 
1725         files_source_by_origin.keys(origin);
1726         assert(!origin.empty());
1727         for (size_t c = 0; c < origin.size(); ++c)
1728         {
1729             nstring_list *files_list =
1730                 files_source_by_origin.query(origin[c]);
1731             assert(!files_list->empty());
1732             nstring s =
1733                 nstring::format
1734                 (
1735                     "aegis --copy-file --project=%s --change=%ld%s "
1736                         "--verbose%s",
1737                     project_name->str_text,
1738                     magic_zero_decode(change_number),
1739                     trace_options.c_str(),
1740                     origin[c].c_str()
1741                 );
1742             os_xargs(s, *files_list, dd);
1743         }
1744     }
1745 
1746     //
1747     // add the new files to the change
1748     //
1749     trace(("look for created files\n"));
1750     files_source.clear();
1751     cstate_src_list_ty *files_source2 =
1752         (cstate_src_list_ty *)cstate_src_list_type.alloc();
1753     cstate_src_list_ty *files_config =
1754         (cstate_src_list_ty *)cstate_src_list_type.alloc();
1755     cstate_src_list_ty *files_build =
1756         (cstate_src_list_ty *)cstate_src_list_type.alloc();
1757     cstate_src_list_ty *files_test_auto =
1758         (cstate_src_list_ty *)cstate_src_list_type.alloc();
1759     cstate_src_list_ty *files_test_manual =
1760         (cstate_src_list_ty *)cstate_src_list_type.alloc();
1761     nstring the_config_file_old(THE_CONFIG_FILE_OLD);
1762     for (j = 0; j < change_set->src->length; ++j)
1763     {
1764         cstate_src_ty   *src_data;
1765 
1766         //
1767         // for now, we are only dealing with create
1768         //
1769         src_data = change_set->src->list[j];
1770         assert(src_data->file_name);
1771         switch (src_data->action)
1772         {
1773         case file_action_create:
1774             break;
1775 
1776         case file_action_modify:
1777         case file_action_remove:
1778         case file_action_transparent:
1779             continue;
1780 
1781         case file_action_insulate:
1782 #ifndef DEBUG
1783         default:
1784 #endif
1785             assert(0);
1786             continue;
1787         }
1788 
1789         //
1790         // add it to the list
1791         //
1792         if (src_data->move)
1793         {
1794             assert(src_data->action == file_action_create);
1795             move_list_append_create
1796             (
1797                 &files_moved,
1798                 src_data->move,
1799                 src_data->file_name
1800             );
1801             continue;
1802         }
1803         switch (src_data->usage)
1804         {
1805         case file_usage_source:
1806             if (the_config_file_old == nstring(src_data->file_name))
1807             {
1808                 cstate_src_list_push_back_unique(files_config, src_data);
1809                 continue;
1810             }
1811             cstate_src_list_push_back_unique(files_source2, src_data);
1812             break;
1813 
1814         case file_usage_config:
1815             cstate_src_list_push_back_unique(files_config, src_data);
1816             break;
1817 
1818         case file_usage_build:
1819             cstate_src_list_push_back_unique(files_build, src_data);
1820             break;
1821 
1822         case file_usage_test:
1823             cstate_src_list_push_back_unique(files_test_auto, src_data);
1824             need_to_test = true;
1825             break;
1826 
1827         case file_usage_manual_test:
1828             cstate_src_list_push_back_unique(files_test_manual, src_data);
1829             need_to_test = true;
1830             break;
1831         }
1832     }
1833 
1834     if (files_build->length > 0)
1835     {
1836         nstring s =
1837             nstring::format
1838             (
1839                 "aegis --new-file --build --project=%s --change=%ld%s "
1840                     "--verbose --no-template ",
1841                 project_name->str_text,
1842                 magic_zero_decode(change_number),
1843                 trace_options.c_str()
1844             );
1845         create_files(s, files_build, dd, ignore_uuid <= 0);
1846     }
1847     if (files_test_auto->length)
1848     {
1849         nstring s =
1850             nstring::format
1851             (
1852                 "aegis --new-test --automatic --project=%s --change=%ld%s "
1853                     "--verbose --no-template",
1854                 project_name->str_text,
1855                 magic_zero_decode(change_number),
1856                 trace_options.c_str()
1857             );
1858 
1859         create_files(s, files_test_auto, dd, ignore_uuid <= 0);
1860     }
1861     if (files_test_manual->length)
1862     {
1863         nstring s =
1864             nstring::format
1865             (
1866                 "aegis --new-test --manual --project=%s --change=%ld%s "
1867                     "--verbose --no-template",
1868                 project_name->str_text,
1869                 magic_zero_decode(change_number),
1870                 trace_options.c_str()
1871             );
1872         create_files(s, files_test_manual, dd, ignore_uuid <= 0);
1873     }
1874     if (files_source2->length)
1875     {
1876         nstring new_file_command =
1877             nstring::format
1878             (
1879                 "aegis --new-file --project=%s --change=%ld%s --verbose "
1880                     "--no-template",
1881                 project_name->str_text,
1882                 magic_zero_decode(change_number),
1883                 trace_options.c_str()
1884             );
1885         create_files(new_file_command, files_source2, dd, ignore_uuid <= 0);
1886     }
1887 
1888     //
1889     // NOTE: Create project configuration files one last, in case the
1890     // incoming change set includes the first instance of the project
1891     // configuration file.  We don't want errors or inconsistencies in
1892     // the project configuration files preventing us from sucessfully
1893     // unpacking the change set.
1894     //
1895     if (files_config->length)
1896     {
1897         nstring s =
1898             nstring::format
1899             (
1900                 "aegis --new-file --config --project=%s --change=%ld%s "
1901                     "--verbose --no-template",
1902                 project_name->str_text,
1903                 magic_zero_decode(change_number),
1904                 trace_options.c_str()
1905             );
1906         create_files(s, files_config, dd, ignore_uuid <= 0);
1907     }
1908 
1909     //
1910     // Now cope with files which moved.
1911     // They get a command each.
1912     //
1913     nstring_list batch_moved;
1914     for (j = 0; j < files_moved.length; ++j)
1915     {
1916         move_ty *mp = files_moved.item + j;
1917         trace(("from=\"%s\"\n", mp->from ? mp->from->str_text : ""));
1918         trace(("remove=%d\n", mp->remove));
1919         trace(("to=\"%s\"\n", mp->to ? mp->to->str_text : ""));
1920         trace(("create=%d\n", mp->create));
1921         assert(mp->create);
1922         assert(mp->remove);
1923         assert(mp->from);
1924         assert(mp->to);
1925         batch_moved.push_back(nstring(mp->from));
1926         batch_moved.push_back(nstring(mp->to));
1927     }
1928     move_xargs
1929     (
1930         nstring(project_name),
1931         change_number,
1932         batch_moved,
1933         dd,
1934         trace_options
1935     );
1936     move_list_destructor(&files_moved);
1937 
1938     //
1939     // now extract each file from the input
1940     //
1941     trace(("extract each file\n"));
1942     cp = change_alloc(pp, change_number);
1943     change_bind_existing(cp);
1944     os_become_orig();
1945     for (j = 0; j < change_set->src->length; ++j)
1946     {
1947         cstate_src_ty   *src_data;
1948         output::pointer ofp;
1949         int             need_whole_source;
1950 
1951         // verbose progress message here?
1952         src_data = change_set->src->list[j];
1953         trace_string(src_data->file_name->str_text);
1954         switch (src_data->action)
1955         {
1956         case file_action_insulate:
1957         case file_action_remove:
1958         case file_action_transparent:
1959 #ifndef DEBUG
1960         default:
1961 #endif
1962             continue;
1963 
1964         case file_action_create:
1965         case file_action_modify:
1966             break;
1967         }
1968         assert(src_data->file_name);
1969 
1970         //
1971         // If the file in the change set has been renamed to match the
1972         // local project we must look in the archive using the old
1973         // name, not the new one.
1974         //
1975         nstring *old = old_name.query(src_data->file_name);
1976 
1977         switch (src_data->usage)
1978         {
1979         case file_usage_build:
1980             continue;
1981 
1982         case file_usage_config:
1983         case file_usage_test:
1984         case file_usage_manual_test:
1985             could_have_a_trojan = 1;
1986             break;
1987 
1988         case file_usage_source:
1989             break;
1990         }
1991         archive_name.clear();
1992         ifp = cpio_p->child(archive_name);
1993         if (!ifp.is_open())
1994         {
1995             missing_file
1996             (
1997                 cpio_p,
1998                 nstring::format
1999                 (
2000                     "patch/%s",
2001                     old ? old->c_str() : src_data->file_name->str_text
2002                 )
2003             );
2004         }
2005         assert(archive_name);
2006         need_whole_source = 1;
2007 
2008         if (archive_name.starts_with("patch/"))
2009         {
2010             //
2011             // Check to see we got the file we expected
2012             //
2013             {
2014                 nstring s1 =
2015                     nstring::format("patch/%s", src_data->file_name->str_text);;
2016                 nstring s2;
2017                 if (old)
2018                     s2 = "patch/" + *old;
2019                 assert(archive_name == s1 || archive_name == s2);
2020             }
2021 
2022             bool patch_is_empty = (ifp->length() == 0);
2023 
2024             //
2025             // Put the patch into a file, so that we can invoke the
2026             // patch command.  It is safe to assume that the patch is in
2027             // the correct format to use the patch -p0 option.
2028             //
2029             nstring patch_file_name = nstring(os_edit_filename(0));
2030             if (!patch_is_empty)
2031             {
2032                 output::pointer os = output_file::binary_open(patch_file_name);
2033                 os << ifp;
2034             }
2035             ifp.close();
2036 
2037             //
2038             // We have a patch file, but we also know that a
2039             // complete source follows.  We can apply the patch
2040             // or discard it.  If we fail to apply it cleanly,
2041             // we can always use the complete source which follows.
2042             //
2043             switch (src_data->action)
2044             {
2045             case file_action_remove:
2046             case file_action_insulate:
2047             case file_action_transparent:
2048                 break;
2049 
2050             case file_action_create:
2051                 //
2052                 // We handle only the create half of the rename.
2053                 //
2054                 if (!src_data->move)
2055                     break;
2056                 // Fall through...
2057 
2058             case file_action_modify:
2059                 if (!use_patch)
2060                     break;
2061 
2062                 //
2063                 // Empty patches always apply cleanly.
2064                 //
2065                 if (patch_is_empty)
2066                 {
2067                     need_whole_source = 0;
2068                     break;
2069                 }
2070 
2071                 //
2072                 // Apply the patch.
2073                 //
2074                 nstring command =
2075                     nstring::format
2076                     (
2077                         CONF_PATCH " -p0"
2078 #ifdef HAVE_GNU_PATCH
2079                             " -f -s"
2080 #endif
2081                             " %s %s",
2082                         src_data->file_name->str_text,
2083                         patch_file_name.c_str()
2084                     );
2085                 int flags = OS_EXEC_FLAG_NO_INPUT; // + OS_EXEC_FLAG_SILENT;
2086                 int n = os_execute_retcode(command, flags, dd);
2087                 if (n == 0)
2088                 {
2089                     //
2090                     // The patch applied cleanly.
2091                     //
2092                     need_whole_source = 0;
2093                 }
2094                 else
2095                 {
2096                     sub_context_ty sc;
2097                     sc.var_set_string("File_Name", src_data->file_name);
2098                     sc.error_intl(i18n("warning: $filename patch not used"));
2099                 }
2100                 break;
2101             }
2102             if (!patch_is_empty)
2103                 os_unlink(patch_file_name);
2104 
2105             //
2106             // The src file should be next.
2107             //
2108             archive_name.clear();
2109             ifp = cpio_p->child(archive_name);
2110             if (!ifp.is_open())
2111             {
2112                 missing_file
2113                 (
2114                     cpio_p,
2115                     nstring::format
2116                     (
2117                         "src/%s",
2118                         old ? old->c_str() : src_data->file_name->str_text
2119                     )
2120                 );
2121             }
2122             assert(archive_name);
2123         }
2124 
2125         // make sure we have the file we expected
2126         {
2127             nstring s1 =
2128                 nstring::format("src/%s", src_data->file_name->str_text);
2129             nstring s2;
2130             if (old)
2131                 s2 = "src/" + *old;
2132             if (archive_name != s1 && (!s2 || archive_name != s2))
2133                 wrong_file(ifp, s1);
2134         }
2135 
2136         output::pointer os;
2137         if (need_whole_source)
2138             os = output_file::binary_open(src_data->file_name);
2139         else
2140             os = output_bit_bucket::create();
2141         os << ifp;
2142         ifp.close();
2143     }
2144 
2145     //
2146     // should be at end of input
2147     //
2148     archive_name.clear();
2149     ifp = cpio_p->child(archive_name);
2150     if (ifp.is_open())
2151         cpio_p->fatal_error("archive too long");
2152     delete cpio_p;
2153     cpio_p = 0;
2154     os_become_undo();
2155 
2156     //
2157     // Now chmod the executable files,
2158     // and set the file attributes.
2159     //
2160     exec_mode = 0755 & ~cp->umask_get();
2161     non_exec_mode = exec_mode & ~0111;
2162     os_become_orig();
2163     for (j = 0; j < change_set->src->length; ++j)
2164     {
2165         cstate_src_ty   *src_data;
2166 
2167         src_data = change_set->src->list[j];
2168         if (src_data->attribute)
2169         {
2170             fattr_ty        *fattr_data;
2171             string_ty       *s2;
2172 
2173             fattr_data = (fattr_ty *)fattr_type.alloc();
2174             fattr_data->attribute = attributes_list_copy(src_data->attribute);
2175             //
2176             // We remove the edit-origin-UUID from the file's
2177             // attributes list. It may be not correct for the local
2178             // repository.
2179             //
2180             attributes_list_remove(fattr_data->attribute, EDIT_ORIGIN_UUID);
2181             fattr_write_file(attribute_file_name, fattr_data, 0);
2182             fattr_type.free(fattr_data);
2183             s2 = str_quote_shell(src_data->file_name);
2184             nstring s =
2185                 nstring::format
2186                 (
2187                     "aegis --file-attributes --change=%ld --project=%s "
2188                         "--file=%s --verbose %s --base-rel%s",
2189                     magic_zero_decode(change_number),
2190                     project_name->str_text,
2191                     attribute_file_name->str_text,
2192                     s2->str_text,
2193                     trace_options.c_str()
2194                 );
2195             str_free(s2);
2196             os_execute(s, OS_EXEC_FLAG_INPUT, dd);
2197             os_unlink_errok(attribute_file_name);
2198         }
2199 
2200         switch (src_data->action)
2201         {
2202         case file_action_create:
2203         case file_action_modify:
2204             break;
2205 
2206         case file_action_remove:
2207         case file_action_insulate:
2208         case file_action_transparent:
2209 #ifndef DEBUG
2210         default:
2211 #endif
2212             continue;
2213         }
2214 
2215         switch (src_data->usage)
2216         {
2217         case file_usage_source:
2218         case file_usage_config:
2219         case file_usage_test:
2220         case file_usage_manual_test:
2221             break;
2222 
2223         case file_usage_build:
2224             continue;
2225         }
2226         assert(src_data->file_name);
2227 
2228         os_chmod
2229         (
2230             src_data->file_name,
2231             (src_data->executable ? exec_mode : non_exec_mode)
2232         );
2233     }
2234     os_become_undo();
2235     str_free(attribute_file_name);
2236 
2237     //
2238     // Now check to see if any of them were config files.  We couldn't do
2239     // it before now, in case we got an inconsistent config combination.
2240     //
2241     config_seen = 0;
2242     for (j = 0; j < change_set->src->length; ++j)
2243     {
2244         cstate_src_ty   *src_data;
2245 
2246         src_data = change_set->src->list[j];
2247         switch (src_data->action)
2248         {
2249         case file_action_create:
2250         case file_action_modify:
2251             break;
2252 
2253         case file_action_remove:
2254         case file_action_insulate:
2255         case file_action_transparent:
2256 #ifndef DEBUG
2257         default:
2258 #endif
2259             continue;
2260         }
2261         switch (src_data->usage)
2262         {
2263         case file_usage_build:
2264             continue;
2265 
2266         case file_usage_source:
2267             assert(src_data->file_name);
2268             if (cp->file_is_config(src_data->file_name))
2269             {
2270                 could_have_a_trojan = 1;
2271                 config_seen = 1;
2272             }
2273             break;
2274 
2275         case file_usage_config:
2276             could_have_a_trojan = 1;
2277             config_seen = 1;
2278             break;
2279 
2280         case file_usage_test:
2281         case file_usage_manual_test:
2282             could_have_a_trojan = 1;
2283             break;
2284         }
2285     }
2286     change_free(cp);
2287     cp = 0;
2288 
2289     //
2290     // If the change could have a trojan horse in it, stop here with
2291     // a warning.  The user needs to look at it and check.
2292     //
2293     if (trojan > 0)
2294         could_have_a_trojan = 1;
2295     else if (trojan == 0)
2296     {
2297         error_intl(0, i18n("warning: potential trojan, proceeding anyway"));
2298         could_have_a_trojan = 0;
2299         config_seen = 0;
2300     }
2301 
2302     //
2303     // If the change could have a trojan horse in the project config
2304     // file, stop here with a warning.  Don't even difference the
2305     // change, because the trojan could be embedded in the diff
2306     // command.  The user needs to look at it and check.
2307     //
2308     // FIX ME: what if the aecpu got rid of it?
2309     //
2310     if (config_seen)
2311     {
2312         //
2313         // before we bail, see if we can throw everything away.
2314         //
2315         if (ignore_uuid <= 0)
2316         {
2317             change::pointer other_cp = local_inventory.query(incoming_uuids);
2318             if (other_cp)
2319             {
2320                 cp = change_alloc(pp, change_number);
2321                 change_bind_existing(cp);
2322                 if (nothing_has_changed(cp, up))
2323                 {
2324                     nstring attr =
2325                         nstring::format
2326                         (
2327                             "%s+=%s",
2328                             ORIGINAL_UUID,
2329                             change_set->uuid->str_text
2330                         );
2331                     nstring s2 =
2332                         nstring::format
2333                         (
2334                             "aegis -change-attr %s -change=%ld -project=%s%s",
2335                             attr.quote_shell().c_str(),
2336                             magic_zero_decode(other_cp->number),
2337                             project_name_get(other_cp->pp).c_str(),
2338                             trace_options.c_str()
2339                         );
2340                     os_become_orig();
2341                     os_execute(s2, OS_EXEC_FLAG_INPUT | OS_EXEC_FLAG_ERROK, dd);
2342                     os_become_undo();
2343 
2344                     goto cancel_the_change;
2345                 }
2346                 change_free(cp);
2347                 cp = 0;
2348             }
2349         }
2350 
2351         error_intl
2352         (
2353             0,
2354          i18n("warning: potential trojan, review before completing development")
2355         );
2356 
2357         //
2358         // We set now the uuid of the change so it is preserved.
2359         //
2360         change_uuid_set
2361         (
2362             nstring(project_name),
2363             change_number,
2364             nstring(change_set->uuid),
2365             trace_options,
2366             dd
2367         );
2368 
2369         //
2370         // Make sure we are using an appropriate architecture.  This is
2371         // one of the commonest problems when seeding an empty repository.
2372         //
2373         nstring s =
2374             nstring::format
2375             (
2376                 "aegis --change-attr --fix-arch --change=%ld --project=%s%s",
2377                 magic_zero_decode(change_number),
2378                 project_name->str_text,
2379                 trace_options.c_str()
2380             );
2381         os_become_orig();
2382         os_execute(s, OS_EXEC_FLAG_INPUT, dd);
2383         os_become_undo();
2384         return;
2385     }
2386 
2387     //
2388     // now merge the change
2389     //
2390     {
2391         nstring merge_command =
2392             nstring::format
2393             (
2394                 "aegis --diff --only-merge --change=%ld --project=%s --verbose",
2395                 magic_zero_decode(change_number),
2396                 project_name->str_text
2397             );
2398         os_become_orig();
2399         os_execute(merge_command, OS_EXEC_FLAG_INPUT, dd);
2400         os_become_undo();
2401     }
2402 
2403     //
2404     // Now that all the files have been unpacked,
2405     // set the change's UUID.
2406     //
2407     // It is vaguely possible you have already downloaded this change
2408     // before, so we don't complain (OS_EXEC_FLAG_ERROK) if the command
2409     // fails.
2410     //
2411     // We intentionally set the UUID before the aecpu -unch.  The aecpu
2412     // -unch will clear the UUID if the change's file inventory changes,
2413     // because it is no longer the same change set.
2414     //
2415     if (change_set->uuid)
2416     {
2417         change_uuid_set
2418         (
2419             nstring(project_name),
2420             change_number,
2421             nstring(change_set->uuid),
2422             trace_options,
2423             dd
2424         );
2425     }
2426 
2427     //
2428     // If we are receiving a change set with an UUID, and the user has
2429     // not used the --ignore-uuid option, we avoid the uncopy files so
2430     // the UUID is preserved and we don't need a new one.  This should
2431     // reduce the UUID proliferation and some related side effects.
2432     //
2433     if (change_set->uuid && ignore_uuid <= 0)
2434         uncopy = 0;
2435 
2436     //
2437     // Un-copy any files which did not change.
2438     //
2439     // The idea is, if there are no files left, there is nothing
2440     // for this change to do, so cancel it.
2441     //
2442     // We intentionally set the UUID before the aecpu -unch.  The aecpu
2443     // -unch will clear the UUID if the change's file inventory changes,
2444     // because it is no longer the same change set.
2445     //
2446     if (uncopy)
2447     {
2448         {
2449             nstring copy_file_undo_cmd =
2450                 nstring::format
2451                 (
2452                     "aegis --copy-file-undo --unchanged --change=%ld "
2453                         "--project=%s --verbose%s",
2454                     magic_zero_decode(change_number),
2455                     project_name->str_text,
2456                     trace_options.c_str()
2457                 );
2458             os_become_orig();
2459             os_execute(copy_file_undo_cmd, OS_EXEC_FLAG_INPUT, dd);
2460             os_become_undo();
2461         }
2462 
2463         //
2464         // If there are no files left, we already have this change.
2465         //
2466         if (number_of_files(project_name, change_number) == 0)
2467         {
2468             cancel_the_change:
2469 
2470             //
2471             // get out of there
2472             //
2473             os_chdir(dot);
2474 
2475             //
2476             // stop developing the change
2477             // cancel the change
2478             //
2479             nstring s =
2480                 nstring::format
2481                 (
2482                     "aegis --develop-begin-undo --change=%ld --project=%s "
2483                         "--verbose%s --new-change-undo",
2484                     magic_zero_decode(change_number),
2485                     project_name->str_text,
2486                     trace_options.c_str()
2487                 );
2488             os_become_orig();
2489             os_execute(s, OS_EXEC_FLAG_INPUT, dot);
2490 
2491             //
2492             // run away, run away!
2493             //
2494             error_intl(0, i18n("change already present"));
2495             return;
2496         }
2497     }
2498     else if (ignore_uuid <= 0)
2499     {
2500         //
2501         // If this change set actually changes nothing (all the file
2502         // contents are already the same as in the project) and the
2503         // change set UUID is already in the project, then get rid of
2504         // this change set, and add the UUID to the existing change.
2505         //
2506         change::pointer other_cp = local_inventory.query(incoming_uuids);
2507         if (other_cp)
2508         {
2509             cp = change_alloc(pp, change_number);
2510             change_bind_existing(cp);
2511             if (nothing_has_changed(cp, up))
2512             {
2513                 nstring attr =
2514                     nstring::format
2515                     (
2516                         "%s+=%s",
2517                         ORIGINAL_UUID,
2518                         change_set->uuid->str_text
2519                     );
2520                 nstring s2 =
2521                     nstring::format
2522                     (
2523                         "aegis -change-attr %s -change=%ld -project=%s%s",
2524                         attr.quote_shell().c_str(),
2525                         magic_zero_decode(other_cp->number),
2526                         project_name_get(other_cp->pp).c_str(),
2527                         trace_options.c_str()
2528                     );
2529                 os_become_orig();
2530                 os_execute(s2, OS_EXEC_FLAG_INPUT | OS_EXEC_FLAG_ERROK, dd);
2531                 os_become_undo();
2532 
2533                 goto cancel_the_change;
2534             }
2535             change_free(cp);
2536             cp = 0;
2537         }
2538     }
2539 
2540     //
2541     // now diff the change
2542     //
2543     {
2544         nstring diff_command =
2545             nstring::format
2546             (
2547                 "aegis --diff --no-merge --change=%ld --project=%s --verbose%s",
2548                 magic_zero_decode(change_number),
2549                 project_name->str_text,
2550                 trace_options.c_str()
2551             );
2552         os_become_orig();
2553         os_execute(diff_command, OS_EXEC_FLAG_INPUT, dd);
2554         os_become_undo();
2555     }
2556 
2557     //
2558     // If the change could have a trojan horse in it, stop here with
2559     // a warning.  The user needs to look at it and check.
2560     //
2561     if (could_have_a_trojan)
2562     {
2563         error_intl
2564         (
2565             0,
2566          i18n("warning: potential trojan, review before completing development")
2567         );
2568         return;
2569     }
2570 
2571     //
2572     // Sleep for a second to make sure the derived files will have
2573     // mod-times strictly later than the source files, and that the aeb
2574     // timestamp will also be strictly later then the mod times for the
2575     // source files.
2576     //
2577     sleep(1);
2578 
2579     //
2580     // now build the change
2581     //
2582     {
2583         nstring build_command =
2584             nstring::format
2585             (
2586                 "aegis --build --change=%ld --project=%s --verbose%s",
2587                 magic_zero_decode(change_number),
2588                 project_name->str_text,
2589                 trace_options.c_str()
2590             );
2591         os_become_orig();
2592         os_execute(build_command, OS_EXEC_FLAG_INPUT, dd);
2593         os_become_undo();
2594     }
2595 
2596     //
2597     // Sleep for a second to make sure the aet timestamps will be
2598     // strictly later then the aeb timestamp.
2599     //
2600     sleep(1);
2601 
2602     //
2603     // re-read the change state data.
2604     //
2605     cp = change_alloc(pp, change_number);
2606     change_bind_existing(cp);
2607     cstate_ty *cstate_data = cp->cstate_get();
2608 
2609     //
2610     // now test the change
2611     //
2612     if (need_to_test && !cstate_data->test_exempt)
2613     {
2614         nstring s =
2615             nstring::format
2616             (
2617                 "aegis --test --change=%ld --project=%s --verbose%s",
2618                 magic_zero_decode(change_number),
2619                 project_name->str_text,
2620                 trace_options.c_str()
2621             );
2622         os_become_orig();
2623         os_execute(s, OS_EXEC_FLAG_INPUT, dd);
2624         os_become_undo();
2625     }
2626     if (need_to_test && !cstate_data->test_baseline_exempt)
2627     {
2628         nstring s =
2629             nstring::format
2630             (
2631                 "aegis --test --baseline --change=%ld --project=%s "
2632                     "--verbose%s",
2633                 magic_zero_decode(change_number),
2634                 project_name->str_text,
2635                 trace_options.c_str()
2636             );
2637         os_become_orig();
2638         os_execute(s, OS_EXEC_FLAG_INPUT, dd);
2639         os_become_undo();
2640     }
2641 
2642     // always to a regession test?
2643     if (!cstate_data->regression_test_exempt)
2644     {
2645         nstring s =
2646             nstring::format
2647             (
2648                 "aegis --test --regression --change=%ld --project=%s "
2649                     "--verbose%s",
2650                 magic_zero_decode(change_number),
2651                 project_name->str_text,
2652                 trace_options.c_str()
2653             );
2654         os_become_orig();
2655         os_execute(s, OS_EXEC_FLAG_INPUT, dd);
2656         os_become_undo();
2657     }
2658 
2659     change_free(cp);
2660     cp = 0;
2661     project_free(pp);
2662     pp = 0;
2663 
2664     //
2665     // end development (if we got this far!)
2666     //
2667     nstring s =
2668         nstring::format
2669         (
2670             "aegis --develop-end --change=%ld --project=%s --verbose%s",
2671             magic_zero_decode(change_number),
2672             project_name->str_text,
2673             trace_options.c_str()
2674         );
2675     os_become_orig();
2676     os_execute(s, OS_EXEC_FLAG_INPUT, dd);
2677     os_become_undo();
2678 
2679     // verbose success message here?
2680 }
2681 
2682 
2683 // vim: set ts=8 sw=4 et :
2684