1 //
2 // aegis - project change supervisor
3 // Copyright (C) 1991-1999, 2001-2009, 2011, 2012 Peter Miller
4 // Copyright (C) 2007-2011 Walter Franzini
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or (at
9 // your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program. If not, see <http://www.gnu.org/licenses/>.
18 //
19 
20 #include <common/ac/assert.h>
21 #include <common/ac/stdio.h>
22 #include <common/ac/stdlib.h>
23 #include <common/ac/unistd.h>
24 #include <common/ac/libintl.h>
25 
26 #include <common/gettime.h>
27 #include <common/now.h>
28 #include <common/progname.h>
29 #include <common/quit.h>
30 #include <common/sizeof.h>
31 #include <common/str_list.h>
32 #include <common/trace.h>
33 #include <libaegis/ael/project/files.h>
34 #include <libaegis/arglex2.h>
35 #include <libaegis/arglex/change.h>
36 #include <libaegis/arglex/project.h>
37 #include <libaegis/change/branch.h>
38 #include <libaegis/change/file.h>
39 #include <libaegis/change/identifier.h>
40 #include <libaegis/commit.h>
41 #include <libaegis/file/event.h>
42 #include <libaegis/file.h>
43 #include <libaegis/help.h>
44 #include <libaegis/lock.h>
45 #include <libaegis/log.h>
46 #include <libaegis/os.h>
47 #include <libaegis/project/file.h>
48 #include <libaegis/project/file/roll_forward.h>
49 #include <libaegis/project.h>
50 #include <libaegis/project/history.h>
51 #include <libaegis/search_path/base_get.h>
52 #include <libaegis/sub.h>
53 #include <libaegis/undo.h>
54 #include <libaegis/user.h>
55 
56 #include <aegis/aecp.h>
57 
58 
59 static void
copy_file_usage(void)60 copy_file_usage(void)
61 {
62     const char      *progname;
63 
64     progname = progname_get();
65     fprintf
66     (
67         stderr,
68         "usage: %s -CoPy_file [ <option>... ] <filename>...\n",
69         progname
70     );
71     fprintf
72     (
73         stderr,
74         "       %s -CoPy_file -INDependent [ <option>... ] <filename>...\n",
75         progname
76     );
77     fprintf(stderr, "       %s -CoPy_file -List [ <option>... ]\n", progname);
78     fprintf(stderr, "       %s -CoPy_file -Help\n", progname);
79     quit(1);
80 }
81 
82 
83 static void
copy_file_help(void)84 copy_file_help(void)
85 {
86     help("aecp", copy_file_usage);
87 }
88 
89 
90 static void
copy_file_list(void)91 copy_file_list(void)
92 {
93     trace(("copy_file_list()\n{\n"));
94     arglex();
95     change_identifier cid;
96     cid.command_line_parse_rest(copy_file_usage);
97     list_project_files(cid, 0);
98     trace(("}\n"));
99 }
100 
101 
102 #define NO_TIME_SET ((time_t)(-1))
103 
104 
105 static void
copy_file_independent(void)106 copy_file_independent(void)
107 {
108     trace(("copy_file_independent()\n{\n"));
109     arglex();
110     nstring_list filenames;
111     change_identifier cid;
112     nstring output_filename;
113     nstring output_directory;
114     while (arglex_token != arglex_token_eoln)
115     {
116         switch (arglex_token)
117         {
118         default:
119             generic_argument(copy_file_usage);
120             continue;
121 
122         case arglex_token_output_directory:
123             if (arglex() != arglex_token_string)
124             {
125                 option_needs_dir
126                 (
127                     arglex_token_output_directory,
128                     copy_file_usage
129                 );
130             }
131             if (!output_directory.empty())
132             {
133                 duplicate_option_by_name
134                 (
135                     arglex_token_output_directory,
136                     copy_file_usage
137                 );
138             }
139             output_directory = arglex_value.alv_string;
140             break;
141 
142         case arglex_token_directory:
143             if (arglex() != arglex_token_string)
144                 option_needs_dir(arglex_token_directory, copy_file_usage);
145             goto get_file_names;
146 
147         case arglex_token_file:
148             if (arglex() != arglex_token_string)
149                 option_needs_files(arglex_token_file, copy_file_usage);
150             // fall through...
151 
152         case arglex_token_string:
153             get_file_names:
154             {
155                 nstring s2(arglex_value.alv_string);
156                 filenames.push_back(s2);
157             }
158             break;
159 
160         case arglex_token_baseline:
161         case arglex_token_branch:
162         case arglex_token_change:
163         case arglex_token_delta:
164         case arglex_token_delta_date:
165         case arglex_token_delta_from_change:
166         case arglex_token_number:
167         case arglex_token_project:
168         case arglex_token_trunk:
169             cid.command_line_parse(copy_file_usage);
170             continue;
171 
172         case arglex_token_base_relative:
173         case arglex_token_current_relative:
174             user_ty::relative_filename_preference_argument(copy_file_usage);
175             break;
176 
177         case arglex_token_output:
178             if (!output_filename.empty())
179                 duplicate_option(copy_file_usage);
180             switch (arglex())
181             {
182             default:
183                 option_needs_file(arglex_token_output, copy_file_usage);
184 
185             case arglex_token_stdio:
186                 output_filename = "-";
187                 break;
188 
189             case arglex_token_string:
190                 output_filename = arglex_value.alv_string;
191                 break;
192             }
193             break;
194         }
195         arglex();
196     }
197     cid.command_line_check(copy_file_usage);
198     if (!output_directory.empty() && !output_filename.empty())
199     {
200         mutually_exclusive_options
201         (
202             arglex_token_output_directory,
203             arglex_token_output,
204             copy_file_usage
205         );
206     }
207     if (filenames.empty())
208     {
209         // FIXME: given the the most probable file list is "."
210         // should we accept no files to imply "."?
211         error_intl(0, i18n("no file names"));
212         copy_file_usage();
213     }
214 
215     //
216     // In order to behave as users expect, we need the original umask
217     // when Aegis was invoked.
218     //
219     int original_umask = 022;
220     os_become_orig_query(0, 0, &original_umask);
221 
222     //
223     // Take a read lock on the baseline, to ensure that it does
224     // not change (aeip) for the duration of the copy.
225     //
226     if (output_filename.empty())
227     {
228         project_baseline_read_lock_prepare(cid.get_pp());
229         lock_take();
230     }
231 
232     //
233     // All of the file validations are to be done from the historical
234     // perspective, not the branch head revision.
235     //
236     if (!cid.set() || cid.get_baseline())
237         cid.set_delta_from_baseline();
238     project_file_roll_forward *historian = cid.get_historian();
239 
240     //
241     // build the list of places to look
242     // when resolving the file name
243     //
244     // To cope with automounters, directories are stored as given,
245     // or are derived from the home directory in the passwd file.
246     // Within aegis, pathnames have their symbolic links resolved,
247     // and any comparison of paths is done on this "system idea"
248     // of the pathname.
249     //
250     if (output_directory.empty())
251     {
252         os_become_orig();
253         output_directory = nstring(os_curdir());
254         os_become_undo();
255     }
256     nstring_list search_path;
257     cid.get_pp()->search_path_get(search_path, true);
258     assert(!search_path.empty());
259 
260     //
261     // Find the base for relative filenames.
262     //
263     bool base_relative =
264         (
265             cid.get_up()->relative_filename_preference
266             (
267                 uconf_relative_filename_preference_base
268             )
269         ==
270             uconf_relative_filename_preference_base
271         );
272     nstring base = (base_relative ? search_path.back() : output_directory);
273     search_path.push_back(output_directory);
274 
275     //
276     // resolve the path of each file
277     // 1. the absolute path of the file name is obtained
278     // 2. if the file is inside the search list
279     // 3. if neither, error
280     //
281     unsigned number_of_errors = 0;
282     {
283         nstring_list names2;
284         for (size_t j = 0; j < filenames.size(); ++j)
285         {
286             nstring fn = filenames[j];
287             nstring afn = (fn[0] == '/' ? fn : os_path_cat(base, fn));
288             cid.get_up()->become_begin();
289             nstring s1 = os_pathname(afn, true);
290             cid.get_up()->become_end();
291 
292             nstring s3;
293             for (size_t k = 0; k < search_path.size(); ++k)
294             {
295                 s3 = os_below_dir(search_path[k], s1);
296                 if (!s3.empty())
297                     break;
298             }
299             if (s3.empty())
300             {
301                 sub_context_ty sc;
302                 sc.var_set_string("File_Name", s1);
303                 project_error(cid.get_pp(), &sc, i18n("$filename unrelated"));
304                 ++number_of_errors;
305                 continue;
306             }
307             nstring_list names3 = historian->get_last_dir(s3);
308             if (!names3.empty())
309                 names2.push_back_unique(names3);
310             else
311                 names2.push_back_unique(s3);
312         }
313         filenames = names2;
314     }
315 
316     //
317     // ensure that each file
318     // is in the historical baseline
319     //
320     for (size_t j = 0; j < filenames.size(); ++j)
321     {
322         nstring fn = filenames[j];
323         file_event *src_data = historian->get_last(fn);
324         if (!src_data)
325         {
326             nstring guess = historian->get_last_fuzzy(fn);
327             sub_context_ty sc;
328             sc.var_set_string("File_Name", fn);
329             if (!guess.empty())
330             {
331                 sc.var_set_string("Guess", guess);
332                 project_error
333                 (
334                     cid.get_pp(),
335                     &sc,
336                     i18n("no $filename, closest is $guess")
337                 );
338             }
339             else
340                 project_error(cid.get_pp(), &sc, i18n("no $filename"));
341             ++number_of_errors;
342         }
343     }
344     if (number_of_errors)
345     {
346         sub_context_ty sc;
347         sc.var_set_long("Number", number_of_errors);
348         sc.var_optional("Number");
349         project_fatal(cid.get_pp(), &sc, i18n("no files copied"));
350     }
351 
352     //
353     // make sure output is unambiguous
354     //
355     if (!output_filename.empty())
356     {
357         if (filenames.size() != 1)
358         {
359             sub_context_ty sc;
360             sc.var_set_long("Number", filenames.size());
361             sc.var_optional("Number");
362             sc.fatal_intl(i18n("single file with -Output"));
363             // NOTREACHED
364         }
365     }
366 
367     //
368     // Only create the output directory when we definiely know that we
369     // will produce some output.
370     //
371     if (!output_directory.empty())
372     {
373         os_become_orig();
374         os_mkdir(output_directory, 0777 & ~original_umask, true);
375         os_become_undo();
376     }
377 
378     //
379     // Copy each file into the destination directory.
380     // Create any necessary directories along the way.
381     //
382     for (size_t j = 0; j < filenames.size(); ++j)
383     {
384         nstring fn = filenames[j];
385         file_event *fep = historian->get_last(fn);
386         assert(fep);
387         if (!fep)
388             continue;
389 
390         fstate_src_ty *old_src = fep->get_src();
391         assert(old_src);
392         switch (old_src->action)
393         {
394         case file_action_remove:
395             //
396             // The file had been removed at this
397             // delta.  Omit it.
398             //
399             filenames.remove(fn);
400             --j;
401             continue;
402 
403         case file_action_create:
404             //
405             // The file has been renamed and we asked for the
406             // removed name.  Omit it.
407             //
408             if (fn != nstring(old_src->file_name))
409             {
410                 filenames.remove(fn);
411                 --j;
412                 continue;
413             }
414             // FALLTHROUGH
415 
416         case file_action_modify:
417         case file_action_insulate:
418         case file_action_transparent:
419             break;
420         }
421         int from_unlink = 0;
422         nstring from
423         (
424             project_file_version_path(cid.get_pp(), old_src, &from_unlink)
425         );
426 
427         //
428         // figure where to send it
429         //
430         nstring to =
431             (
432                 output_filename.empty()
433             ?
434                 os_path_cat(output_directory, fn)
435             :
436                 output_filename
437             );
438 
439         //
440         // copy the file
441         //
442         os_become_orig();
443         if (output_filename.empty())
444         {
445             os_mkdir_between(output_directory, fn, 0777 & ~original_umask);
446             if (os_exists(to))
447                 os_unlink(to);
448         }
449         if (to == "-")
450             to = "";
451         copy_whole_file(from, to, false);
452 
453         //
454         // set the file mode
455         //
456         if (output_filename.empty())
457         {
458             int mode = 0666;
459             if (old_src->executable)
460                 mode |= 0111;
461             mode &= ~original_umask;
462             os_chmod(to, mode);
463         }
464         os_become_undo();
465 
466         //
467         // clean up afterwards
468         //
469         if (from_unlink)
470         {
471             os_become_orig();
472             os_unlink_errok(from);
473             os_become_undo();
474         }
475     }
476 
477     //
478     // release the baseline lock
479     //
480     if (output_filename.empty())
481         lock_release();
482 
483     //
484     // verbose success message
485     //
486     for (size_t j = 0; j < filenames.size(); ++j)
487     {
488         sub_context_ty sc;
489         sc.var_set_string("File_Name", filenames[j]);
490         project_verbose(cid.get_pp(), &sc, i18n("copied $filename"));
491     }
492     trace(("}\n"));
493 }
494 
495 
496 static fstate_src_ty *
fake_removed_file(project * pp,string_ty * filename)497 fake_removed_file(project *pp, string_ty *filename)
498 {
499     fstate_src_ty   *p_src_data;
500     fstate_src_ty   *old_src;
501 
502     p_src_data = pp->file_find(filename, view_path_simple);
503     assert(p_src_data);
504     old_src = (fstate_src_ty *)fstate_src_type.alloc();
505     old_src->action = file_action_remove;
506     change_file_copy_basic_attributes(old_src, p_src_data);
507     if (p_src_data && p_src_data->edit)
508     {
509         assert(p_src_data->edit);
510         old_src->edit = history_version_copy(p_src_data->edit);
511     }
512     else
513     {
514         // Should never happen.  Yeah, right.
515         old_src->usage = file_usage_source;
516         old_src->edit = (history_version_ty *)history_version_type.alloc();
517         old_src->edit->revision = str_from_c("1.1");
518     }
519     return old_src;
520 }
521 
522 
523 static bool
delete_file_p(user_ty::pointer up,const nstring & filename)524 delete_file_p(user_ty::pointer up, const nstring &filename)
525 {
526     up->become_end();
527     bool result = up->delete_file_query(filename, false, true);
528     up->become_begin();
529     return result;
530 }
531 
532 
533 static void
copy_file_main(void)534 copy_file_main(void)
535 {
536     string_ty       *dd;
537     string_ty       *s1;
538     string_ty       *s2;
539     cstate_ty       *cstate_data;
540     size_t          j;
541     size_t          k;
542     string_ty       *project_name;
543     project      *pp;
544     project      *pp2;
545     long            change_number;
546     change::pointer cp;
547     log_style_ty    log_style;
548     user_ty::pointer up;
549     const char      *output_filename;
550     time_t          delta_date;
551     long            delta_number;
552     const char      *delta_name;
553     long            delta_from_change;
554     int             config_seen;
555     int             number_of_errors;
556     const char      *branch;
557     int             trunk;
558     int             read_only;
559 
560     trace(("copy_file_main()\n{\n"));
561     arglex();
562     string_list_ty wl;
563     bool overwriting = false;
564     project_name = 0;
565     change_number = 0;
566     log_style = log_style_append_default;
567     output_filename = 0;
568     delta_date = NO_TIME_SET;
569     delta_number = -1;
570     delta_name = 0;
571     delta_from_change = 0;
572     branch = 0;
573     trunk = 0;
574     read_only = 0;
575     bool rescind = false;
576     bool build_files_are_ok = false;
577     bool as_needed = false;
578     while (arglex_token != arglex_token_eoln)
579     {
580         switch (arglex_token)
581         {
582         default:
583             generic_argument(copy_file_usage);
584             continue;
585 
586         case arglex_token_overwriting:
587             if (overwriting)
588                 duplicate_option(copy_file_usage);
589             overwriting = true;
590             break;
591 
592         case arglex_token_directory:
593             if (arglex() != arglex_token_string)
594                 option_needs_dir(arglex_token_directory, copy_file_usage);
595             goto get_file_names;
596 
597         case arglex_token_file:
598             if (arglex() != arglex_token_string)
599                 option_needs_files(arglex_token_file, copy_file_usage);
600             // fall through...
601 
602         case arglex_token_string:
603           get_file_names:
604             s2 = str_from_c(arglex_value.alv_string);
605             wl.push_back(s2);
606             str_free(s2);
607             break;
608 
609         case arglex_token_change:
610             arglex();
611             // fall through...
612 
613         case arglex_token_number:
614             arglex_parse_change_with_branch
615             (
616                 &project_name,
617                 &change_number,
618                 &branch,
619                 copy_file_usage
620             );
621             continue;
622 
623         case arglex_token_project:
624             arglex();
625             arglex_parse_project(&project_name, copy_file_usage);
626             continue;
627 
628         case arglex_token_nolog:
629             if (log_style == log_style_none)
630                 duplicate_option(copy_file_usage);
631             log_style = log_style_none;
632             break;
633 
634         case arglex_token_delta:
635             if (delta_number >= 0 || delta_name)
636                 duplicate_option(copy_file_usage);
637             switch (arglex())
638             {
639             default:
640                 option_needs_number(arglex_token_delta, copy_file_usage);
641                 // NOTREACHED
642 
643             case arglex_token_number:
644                 delta_number = arglex_value.alv_number;
645                 if (delta_number < 0)
646                 {
647                     sub_context_ty sc;
648                     sc.var_set_long("Number", delta_number);
649                     sc.fatal_intl(i18n("delta $number out of range"));
650                     // NOTREACHED
651                 }
652                 break;
653 
654             case arglex_token_string:
655                 delta_name = arglex_value.alv_string;
656                 break;
657             }
658             break;
659 
660         case arglex_token_delta_date:
661             if (delta_date != NO_TIME_SET)
662                 duplicate_option(copy_file_usage);
663             if (arglex() != arglex_token_string)
664             {
665                 option_needs_string(arglex_token_delta_date, copy_file_usage);
666                 // NOTREACHED
667             }
668             delta_date = date_scan(arglex_value.alv_string);
669             if (delta_date == NO_TIME_SET)
670             {
671                 sub_context_ty sc;
672                 sc.var_set_charstar("Name", arglex_value.alv_string);
673                 sc.fatal_intl(i18n("date $name unknown"));
674                 // NOTREACHED
675             }
676             break;
677 
678         case arglex_token_delta_from_change:
679             if (arglex() != arglex_token_number)
680             {
681                 option_needs_number
682                 (
683                     arglex_token_delta_from_change,
684                     copy_file_usage
685                 );
686             }
687             if (delta_from_change)
688             {
689                 duplicate_option_by_name
690                 (
691                     arglex_token_delta_from_change,
692                     copy_file_usage
693                 );
694             }
695             delta_from_change = arglex_value.alv_number;
696             if (delta_from_change == 0)
697                 delta_from_change = MAGIC_ZERO;
698             else if (delta_from_change < 1)
699             {
700                 sub_context_ty sc;
701                 sc.var_set_long("Number", change_number);
702                 sc.fatal_intl(i18n("change $number out of range"));
703                 // NOTREACHED
704             }
705             break;
706 
707         case arglex_token_output:
708             if (output_filename)
709                 duplicate_option(copy_file_usage);
710             switch (arglex())
711             {
712             default:
713                 option_needs_file(arglex_token_output, copy_file_usage);
714 
715             case arglex_token_stdio:
716                 output_filename = "";
717                 break;
718 
719             case arglex_token_string:
720                 output_filename = arglex_value.alv_string;
721                 break;
722             }
723             break;
724 
725         case arglex_token_branch:
726             if (branch)
727                 duplicate_option(copy_file_usage);
728             switch (arglex())
729             {
730             default:
731                 option_needs_number(arglex_token_branch, copy_file_usage);
732 
733             case arglex_token_number:
734             case arglex_token_string:
735                 branch = arglex_value.alv_string;
736                 break;
737 
738             case arglex_token_stdio:
739                 branch = "";
740                 break;
741             }
742             break;
743 
744         case arglex_token_trunk:
745             if (trunk)
746                 duplicate_option(copy_file_usage);
747             ++trunk;
748             break;
749 
750         case arglex_token_read_only:
751             if (read_only)
752                 duplicate_option(copy_file_usage);
753             ++read_only;
754             break;
755 
756         case arglex_token_wait:
757         case arglex_token_wait_not:
758             user_ty::lock_wait_argument(copy_file_usage);
759             break;
760 
761         case arglex_token_base_relative:
762         case arglex_token_current_relative:
763             user_ty::relative_filename_preference_argument(copy_file_usage);
764             break;
765 
766         case arglex_token_rescind:
767             if (rescind)
768                 duplicate_option(copy_file_usage);
769             rescind = true;
770             break;
771 
772         case arglex_token_keep:
773         case arglex_token_interactive:
774         case arglex_token_keep_not:
775             user_ty::delete_file_argument(copy_file_usage);
776             break;
777 
778         case arglex_token_build:
779             // Undocumented, for use by aepatch
780             build_files_are_ok = true;
781             break;
782 
783         case arglex_token_as_needed:
784             if (as_needed)
785                 duplicate_option(copy_file_usage);
786             as_needed = true;
787             break;
788         }
789         arglex();
790     }
791     if (overwriting && as_needed)
792     {
793         mutually_exclusive_options
794         (
795             arglex_token_as_needed,
796             arglex_token_overwriting,
797             copy_file_usage
798         );
799     }
800     if (!wl.nstrings && !rescind)
801     {
802         error_intl(0, i18n("no file names"));
803         copy_file_usage();
804     }
805     if (trunk)
806     {
807         if (branch)
808         {
809             mutually_exclusive_options
810             (
811                 arglex_token_branch,
812                 arglex_token_trunk,
813                 copy_file_usage
814             );
815         }
816         branch = "";
817     }
818     if (rescind && output_filename && wl.nstrings != 1)
819     {
820         mutually_exclusive_options
821         (
822             arglex_token_rescind,
823             arglex_token_output,
824             copy_file_usage
825         );
826     }
827     if
828     (
829         (
830             (delta_name || delta_number >= 0)
831         +
832             !!delta_from_change
833         +
834             (delta_date != NO_TIME_SET)
835         )
836     >
837         1
838     )
839     {
840         mutually_exclusive_options3
841         (
842             arglex_token_delta,
843             arglex_token_delta_date,
844             arglex_token_delta_from_change,
845             copy_file_usage
846         );
847     }
848     if
849     (
850         rescind
851     &&
852         !delta_name
853     &&
854         delta_number < 0
855     &&
856         !delta_from_change
857     &&
858         delta_date == NO_TIME_SET
859     )
860     {
861         sub_context_ty sc;
862         sc.var_set_charstar("Name1", arglex_token_name(arglex_token_rescind));
863         sc.var_set_charstar("Name2", arglex_token_name(arglex_token_delta));
864         sc.fatal_intl(i18n("$name1 needs $name2"));
865         // NOTREACHED
866     }
867 
868     //
869     // make sure output is unambiguous
870     //
871     if (output_filename)
872     {
873         if (wl.nstrings != 1)
874         {
875             sub_context_ty sc;
876             sc.var_set_long("Number", (long)wl.nstrings);
877             sc.var_optional("Number");
878             sc.fatal_intl(i18n("single file with -Output"));
879             // NOTREACHED
880         }
881         overwriting = true;
882     }
883 
884     //
885     // locate project data
886     //
887     if (!project_name)
888     {
889         nstring n = user_ty::create()->default_project();
890         project_name = str_copy(n.get_ref());
891     }
892     pp = project_alloc(project_name);
893     str_free(project_name);
894     pp->bind_existing();
895 
896     //
897     // locate which branch
898     //
899     if (branch)
900         pp2 = pp->find_branch(branch);
901     else
902         pp2 = project_copy(pp);
903 
904     //
905     // locate user data
906     //
907     up = user_ty::create();
908 
909     //
910     // locate change data
911     //
912     if (!change_number)
913         change_number = up->default_change(pp);
914     cp = change_alloc(pp, change_number);
915     change_bind_existing(cp);
916 
917     //
918     // lock the change file
919     //
920     // Also take a read lock on the baseline, to ensure that it does
921     // not change (aeip) for the duration of the build.
922     //
923     if (!output_filename)
924     {
925         change_cstate_lock_prepare(cp);
926         project_baseline_read_lock_prepare(pp2);
927         lock_take();
928 
929         log_open(change_logfile_get(cp), up, log_style);
930 
931         if (cp->file_promote())
932         {
933             trace(("The change::file_promote found somthing to do.\n"));
934 
935             //
936             // Write out the file state, and then let go of the locks
937             // and take them again.  This ensures the data is consistent
938             // for the next stage of processing.
939             //
940             trace(("Write out what we've done so far.\n"));
941             cp->cstate_write();
942             commit();
943             lock_release();
944 
945             trace(("Take the locks again.\n"));
946             change_cstate_lock_prepare(cp);
947             project_baseline_read_lock_prepare(pp2);
948             lock_take();
949         }
950     }
951     cstate_data = cp->cstate_get();
952 
953     //
954     // When there is no explicit output file:
955     // It is an error if the change is not in the being_developed state.
956     // It is an error if the change is not assigned to the current user.
957     //
958     if (output_filename)
959     {
960         switch (cstate_data->state)
961         {
962         case cstate_state_awaiting_development:
963         case cstate_state_awaiting_integration:
964         case cstate_state_awaiting_review:
965         case cstate_state_being_developed:
966         case cstate_state_being_integrated:
967         case cstate_state_being_reviewed:
968         case cstate_state_completed:
969             break;
970 
971 #ifndef DEBUG
972         default:
973 #endif
974             wrong_state:
975             change_fatal(cp, 0, i18n("bad cp state"));
976         }
977     }
978     else
979     {
980         if (cstate_data->state != cstate_state_being_developed)
981             goto wrong_state;
982         if (cp->is_a_branch())
983             change_fatal(cp, 0, i18n("bad branch cp"));
984         if (nstring(cp->developer_name()) != up->name())
985             change_fatal(cp, 0, i18n("not developer"));
986     }
987 
988     //
989     // it is an error if the delta does not exist
990     //
991     if (delta_name)
992     {
993         s1 = str_from_c(delta_name);
994         delta_number = project_history_delta_by_name(pp2, s1, 0);
995         str_free(s1);
996     }
997     if (delta_date != NO_TIME_SET)
998     {
999         //
1000         // If the time is in the future, you could get a different
1001         // answer for the same input at some point in the future.
1002         //
1003         // This is the "time safe" quality first described by
1004         // Damon Poole <damon@ede.com>
1005         //
1006         if (delta_date > now())
1007             project_error(pp2, 0, i18n("date in the future"));
1008     }
1009     if (delta_from_change)
1010     {
1011         delta_number =
1012             project_change_number_to_delta_number(pp2, delta_from_change);
1013     }
1014     if (delta_number >= 0)
1015     {
1016         delta_date = project_history_delta_to_timestamp(pp2, delta_number);
1017         if (delta_date == NO_TIME_SET)
1018         {
1019             sub_context_ty sc;
1020             sc.var_set_long("Name", delta_number);
1021             change_fatal(cp, &sc, i18n("no delta $name"));
1022             // NOTREACHED
1023         }
1024         trace(("delta %ld -> delta date %ld\n", delta_number,
1025                (long)delta_date));
1026     }
1027 
1028     //
1029     // We may need to consult the project historian
1030     // for the historical list of files.
1031     //
1032     project_file_roll_forward historian;
1033     if (delta_date != NO_TIME_SET)
1034     {
1035         historian.set(pp2, delta_date, 0);
1036 
1037         if (wl.nstrings == 0)
1038         {
1039             assert(rescind);
1040             //
1041             // If no files are named in an aecp -rescind command, the
1042             // list of files is implied by the change being rescinded.
1043             //
1044             // However, files which are already in the change are to be
1045             // avoided unless -overwriting is specified.
1046             //
1047             size_t used = 0;
1048             size_t available = 0;
1049             change::pointer cp2 = historian.get_last_change();
1050             assert(cp2);
1051             for (size_t n = 0; ; ++n)
1052             {
1053                 fstate_src_ty *src = change_file_nth(cp2, n, view_path_first);
1054                 if (!src)
1055                     break;
1056                 if (overwriting || !cp->file_find(src, view_path_first))
1057                 {
1058                     wl.push_back(src->file_name);
1059                     ++used;
1060                 }
1061                 ++available;
1062             }
1063             if (!used)
1064             {
1065                 //
1066                 // FIXME: This isn't exactly the best error message,
1067                 // but it will do for now.
1068                 //
1069                 sub_context_ty sc;
1070                 sc.var_set_charstar("File_Name", ".");
1071                 sc.var_set_long("Number", (long)available);
1072                 sc.var_optional("Number");
1073                 change_fatal
1074                 (
1075                     cp,
1076                     &sc,
1077                     i18n("directory $filename contains no relevant files")
1078                 );
1079                 // NOTREACHED
1080             }
1081         }
1082     }
1083 
1084     //
1085     // build the list of places to look
1086     // when resolving the file name
1087     //
1088     // To cope with automounters, directories are stored as given,
1089     // or are derived from the home directory in the passwd file.
1090     // Within aegis, pathnames have their symbolic links resolved,
1091     // and any comparison of paths is done on this "system idea"
1092     // of the pathname.
1093     //
1094     string_list_ty search_path;
1095     cp->search_path_get(&search_path, true);
1096     assert(search_path.nstrings >= 1);
1097 
1098     //
1099     // Find the base for relative filenames.
1100     //
1101     bool base_relative =
1102         (
1103             up->relative_filename_preference
1104             (
1105                 uconf_relative_filename_preference_current
1106             )
1107         ==
1108             uconf_relative_filename_preference_base
1109         );
1110 
1111     os_become_orig();
1112     string_ty *curdir = os_curdir();
1113     os_become_undo();
1114 
1115     string_ty *base = str_copy(base_relative ? search_path.string[0] : curdir);
1116 
1117     //
1118     // resolve the path of each file
1119     // 1. the absolute path of the file name is obtained
1120     // 2. if the file is inside the search list
1121     // 3. if neither, error
1122     //
1123     config_seen = 0;
1124     string_list_ty wl2;
1125     number_of_errors = 0;
1126     for (j = 0; j < wl.nstrings; ++j)
1127     {
1128         s1 = wl.string[j];
1129         trace_string(s1->str_text);
1130         if (s1->str_text[0] == '/')
1131             s2 = str_copy(s1);
1132         else
1133             s2 = os_path_cat(base, s1);
1134         up->become_begin();
1135         s1 = os_pathname(s2, 1);
1136         up->become_end();
1137         str_free(s2);
1138         s2 = 0;
1139         for (k = 0; k < search_path.nstrings; ++k)
1140         {
1141             s2 = os_below_dir(search_path.string[k], s1);
1142             if (s2)
1143                 break;
1144         }
1145         str_free(s1);
1146         if (!s2)
1147         {
1148             sub_context_ty sc;
1149             sc.var_set_string("File_Name", wl.string[j]);
1150             change_error(cp, &sc, i18n("$filename unrelated"));
1151             ++number_of_errors;
1152             continue;
1153         }
1154         string_list_ty wl_out;
1155         string_list_ty wl_in;
1156         pp2->file_directory_query
1157         (
1158             s2,
1159             &wl_in,
1160             &wl_out,
1161             view_path_simple
1162         );
1163         if (delta_date != NO_TIME_SET)
1164             wl_in.push_back(wl_out);
1165         if (wl_in.nstrings)
1166         {
1167             int             used;
1168 
1169             //
1170             // if the user named a directory,
1171             // add all of the source files in that directory,
1172             // provided they are not already in the change.
1173             //
1174             if (output_filename)
1175             {
1176                 sub_context_ty sc;
1177                 sc.var_set_charstar
1178                 (
1179                     "Name",
1180                     arglex_token_name(arglex_token_output)
1181                 );
1182                 sc.error_intl(i18n("no dir with $name"));
1183                 ++number_of_errors;
1184             }
1185             used = 0;
1186             for (k = 0; k < wl_in.nstrings; ++k)
1187             {
1188                 string_ty       *s3;
1189 
1190                 s3 = wl_in.string[k];
1191                 trace_string(s3->str_text);
1192                 if (overwriting || !cp->file_find(nstring(s3), view_path_first))
1193                 {
1194                     if (wl2.member(s3))
1195                     {
1196                         sub_context_ty sc;
1197                         sc.var_set_string("File_Name", s3);
1198                         change_error(cp, &sc, i18n("too many $filename"));
1199                         ++number_of_errors;
1200                     }
1201                     else
1202                         wl2.push_back(s3);
1203                     if (cp->file_is_config(s3))
1204                         ++config_seen;
1205                     ++used;
1206                 }
1207             }
1208             if (!used)
1209             {
1210                 sub_context_ty sc;
1211                 if (s2->str_length)
1212                     sc.var_set_string("File_Name", s2);
1213                 else
1214                     sc.var_set_charstar("File_Name", ".");
1215                 sc.var_set_long("Number", (long)wl_in.nstrings);
1216                 sc.var_optional("Number");
1217                 change_error
1218                 (
1219                     cp,
1220                     &sc,
1221                     i18n("directory $filename contains no relevant files")
1222                 );
1223                 ++number_of_errors;
1224             }
1225         }
1226         else
1227         {
1228             if (wl2.member(s2))
1229             {
1230                 sub_context_ty sc;
1231                 sc.var_set_string("File_Name", s2);
1232                 change_error(cp, &sc, i18n("too many $filename"));
1233                 ++number_of_errors;
1234             }
1235             else
1236             {
1237                 //
1238                 // Ignore redundant requests.
1239                 //
1240                 bool copy_me = true;
1241                 if (as_needed)
1242                 {
1243                     fstate_src_ty *csrc =
1244                         cp->file_find(nstring(s2), view_path_first);
1245                     if (csrc && csrc->action == file_action_modify)
1246                         copy_me = false;
1247                 }
1248                 if (copy_me)
1249                     wl2.push_back(s2);
1250             }
1251             if (cp->file_is_config(s2))
1252                 ++config_seen;
1253         }
1254         str_free(s2);
1255     }
1256     wl = wl2;
1257 
1258     //
1259     // ensure that each file
1260     // 1. is not already part of the change
1261     // 2. is in the baseline
1262     //
1263     for (j = 0; j < wl.nstrings; ++j)
1264     {
1265         fstate_src_ty   *src_data;
1266 
1267         s1 = wl.string[j];
1268         if
1269         (
1270             cp->file_find(nstring(s1), view_path_first)
1271         &&
1272             !overwriting
1273         &&
1274             !output_filename
1275         )
1276         {
1277             sub_context_ty sc;
1278             sc.var_set_string("File_Name", s1);
1279             change_error(cp, &sc, i18n("bad cp, file $filename dup"));
1280             ++number_of_errors;
1281             continue;
1282         }
1283         if (output_filename)
1284         {
1285             fstate_src_ty   *c_src_data;
1286 
1287             //
1288             // OK to use a file that "almost" exists
1289             // in combination with the -Output option
1290             //
1291             c_src_data = cp->file_find(nstring(s1), view_path_first);
1292             if (c_src_data)
1293             {
1294                 switch (c_src_data->action)
1295                 {
1296                 case file_action_create:
1297                     continue;
1298 
1299                 case file_action_modify:
1300                 case file_action_remove:
1301                 case file_action_insulate:
1302                 case file_action_transparent:
1303                     break;
1304                 }
1305             }
1306         }
1307         src_data = pp2->file_find(s1, view_path_simple);
1308         if
1309         (
1310             !src_data
1311         ||
1312             (delta_date == NO_TIME_SET && src_data->deleted_by)
1313         )
1314         {
1315             sub_context_ty sc;
1316             src_data = pp2->file_find_fuzzy(s1, view_path_extreme);
1317             sc.var_set_string("File_Name", s1);
1318             if (src_data)
1319             {
1320                 sc.var_set_string("Guess", src_data->file_name);
1321                 project_error
1322                 (
1323                     pp2,
1324                     &sc,
1325                     i18n("no $filename, closest is $guess")
1326                 );
1327             }
1328             else
1329                 project_error(pp2, &sc, i18n("no $filename"));
1330             ++number_of_errors;
1331             continue;
1332         }
1333         if (src_data && !output_filename)
1334         {
1335             switch (src_data->usage)
1336             {
1337             case file_usage_source:
1338             case file_usage_config:
1339             case file_usage_test:
1340             case file_usage_manual_test:
1341                 break;
1342 
1343             case file_usage_build:
1344                 if (!build_files_are_ok)
1345                 {
1346                     sub_context_ty sc;
1347                     sc.var_set_string("File_Name", s1);
1348                     change_error(cp, &sc, i18n("$filename is built"));
1349                     ++number_of_errors;
1350                 }
1351                 break;
1352             }
1353         }
1354     }
1355     if (number_of_errors)
1356     {
1357         sub_context_ty sc;
1358         sc.var_set_long("Number", number_of_errors);
1359         sc.var_optional("Number");
1360         change_fatal(cp, &sc, i18n("no files copied"));
1361     }
1362 
1363     //
1364     // Copy each file into the development directory.
1365     // Create any necessary directories along the way.
1366     //
1367     // Add each file to the change file,
1368     // or update the edit number.
1369     //
1370     dd = change_development_directory_get(cp, 0);
1371     for (j = 0; j < wl.nstrings; ++j)
1372     {
1373         string_ty       *from = 0;
1374         string_ty       *to = 0;
1375         fstate_src_ty   *old_src = 0;
1376         fstate_src_ty   *older_src = 0;
1377         int             from_unlink = 0;
1378 
1379         s1 = wl.string[j];
1380         trace(("s1 = \"%s\";\n", s1->str_text));
1381         if (delta_date != NO_TIME_SET)
1382         {
1383             file_event   *fep;
1384 
1385             fep = historian.get_last(s1);
1386             if (!fep)
1387             {
1388                 //
1389                 // This file had not yet been created at
1390                 // the time of the delta.  Arrange for
1391                 // it to look like it's being removed.
1392                 //
1393                 // In the case of -rescind, it doesn't exist at the
1394                 // previous delta, either, so remove it in this case, too.
1395                 //
1396                 // This is a memory leak.
1397                 //
1398                 old_src = fake_removed_file(pp2, s1);
1399                 older_src = old_src;
1400             }
1401             else
1402             {
1403                 old_src = fep->get_src();
1404                 if (rescind)
1405                 {
1406                     fep = historian.get_older(s1);
1407                     trace(("fep = %lX\n", (long)fep));
1408                     if (fep)
1409                     {
1410                         older_src = fep->get_src();
1411                     }
1412                     else
1413                     {
1414                         // This is a memory leak.
1415                         older_src = fake_removed_file(pp2, s1);
1416                     }
1417                 }
1418                 else
1419                 {
1420                     //
1421                     // If the file has been renamed we asked for the
1422                     // old name of the file and get the new one.  So
1423                     // we remove the file name from the list.
1424                     //
1425                     if (!str_equal(s1, old_src->file_name))
1426                     {
1427                         wl.remove(s1);
1428                         --j;
1429                         continue;
1430                     }
1431                     older_src = old_src;
1432                 }
1433             }
1434             assert(old_src);
1435             trace(("old_src = %lX\n", (long)old_src));
1436             assert(older_src);
1437             trace(("older_src = %lX\n", (long)older_src));
1438             bool set_mode = true;
1439             switch (older_src->action)
1440             {
1441             case file_action_remove:
1442                 trace_string(s1->str_text);
1443                 change_file_whiteout_write(cp, s1, up);
1444                 goto done;
1445 
1446             case file_action_create:
1447             case file_action_modify:
1448             case file_action_insulate:
1449             case file_action_transparent:
1450 #ifndef DEBUG
1451             default:
1452 #endif
1453                 from = project_file_version_path(pp2, older_src, &from_unlink);
1454                 break;
1455             }
1456             trace(("from = \"%s\";\n", from->str_text));
1457 
1458             //
1459             // figure where to send it
1460             //
1461             if (output_filename)
1462                 to = str_from_c(output_filename);
1463             else
1464                 to = os_path_cat(dd, s1);
1465 
1466             //
1467             // copy the file
1468             //
1469             // But only if it doesn't exist,
1470             // or the user didn't say --keep.
1471             //
1472             up->become_begin();
1473             if (output_filename)
1474             {
1475                 copy_whole_file(from, to, 0);
1476             }
1477             else
1478             {
1479                 if (os_exists(to) && !os_symlink_query(to))
1480                 {
1481                     //
1482                     // File exists in development directory.
1483                     // Be careful replacing it.
1484                     //
1485                     if (overwriting || delete_file_p(up, nstring(s1)))
1486                     {
1487                         os_unlink(to);
1488                         copy_whole_file(from, to, 0);
1489                     }
1490                     else
1491                         set_mode = false;
1492                 }
1493                 else
1494                 {
1495                     //
1496                     // File does not exist in the development directory.
1497                     // (But a symlink may.)
1498                     //
1499                     os_mkdir_between(dd, s1, 02755);
1500                     os_unlink(to);
1501                     copy_whole_file(from, to, 0);
1502                 }
1503             }
1504 
1505             //
1506             // set the file mode
1507             //
1508             if (set_mode)
1509             {
1510                 int mode = 0444;
1511                 if (!read_only)
1512                     mode |= 0600;
1513                 if (older_src->executable)
1514                     mode |= 0111;
1515                 mode &= ~cp->umask_get();
1516                 os_chmod(to, mode);
1517             }
1518 
1519             //
1520             // clean up afterwards
1521             //
1522             if (from_unlink)
1523                 os_unlink_errok(from);
1524             up->become_end();
1525         done:
1526             str_free(from);
1527             str_free(to);
1528         }
1529         else
1530         {
1531             if (cstate_data->state == cstate_state_being_integrated)
1532             {
1533                 from =
1534                     os_path_cat(change_integration_directory_get(cp, 0), s1);
1535             }
1536             else
1537             {
1538                 from = project_file_path(pp2, s1);
1539             }
1540             if (output_filename)
1541                 to = str_from_c(output_filename);
1542             else
1543                 to = os_path_cat(dd, s1);
1544 
1545             //
1546             // We need the file information for the execuable bit.
1547             //
1548             old_src = pp2->file_find(s1, view_path_simple);
1549             assert(old_src);
1550             older_src = old_src;
1551 
1552             os_become_orig();
1553             int file_exists = os_exists(from);
1554             os_become_undo();
1555             if (!file_exists)
1556                 from =
1557                     project_file_version_path (pp2, old_src, &from_unlink);
1558 
1559             //
1560             // copy the file
1561             //
1562             bool set_mode = true;
1563             up->become_begin();
1564             if (output_filename)
1565             {
1566                 copy_whole_file(from, to, 0);
1567             }
1568             else
1569             {
1570                 if (os_exists(to) && !os_symlink_query(to))
1571                 {
1572                     //
1573                     // File exists in development directory.
1574                     // Be careful replacing it.
1575                     //
1576                     if (overwriting || delete_file_p(up, nstring(s1)))
1577                     {
1578                         os_unlink(to);
1579                         copy_whole_file(from, to, 0);
1580                     }
1581                     else
1582                         set_mode = false;
1583                 }
1584                 else
1585                 {
1586                     //
1587                     // File does not exist in the development directory.
1588                     // (But a symlink may.)
1589                     //
1590                     os_mkdir_between(dd, s1, 02755);
1591                     os_unlink(to);
1592                     copy_whole_file(from, to, 0);
1593                 }
1594             }
1595 
1596             //
1597             // set the file mode
1598             //
1599             if (set_mode)
1600             {
1601                 int mode = 0444;
1602                 if (!read_only)
1603                     mode |= 0600;
1604                 if (old_src->executable)
1605                     mode |= 0111;
1606                 mode &= ~cp->umask_get();
1607                 os_chmod(to, mode);
1608             }
1609             up->become_end();
1610 
1611             //
1612             // clean up afterwards
1613             //
1614             str_free(from);
1615             str_free(to);
1616         }
1617 
1618         if (!output_filename)
1619         {
1620             fstate_src_ty   *c_src_data;
1621             fstate_src_ty   *p_src_data;
1622 
1623             assert(!old_src == !older_src);
1624             p_src_data = older_src;
1625             if (!p_src_data)
1626                 p_src_data = pp2->file_find(s1, view_path_simple);
1627             assert(p_src_data);
1628             //
1629             // It's tempting to say:
1630             //
1631             // assert(p_src_data->edit);
1632             // assert(p_src_data->edit->revision);
1633             //
1634             // but it is not true for removed files.
1635             //
1636             // FIXME: shouldn't this be find-by-meta?
1637             c_src_data = cp->file_find(nstring(s1), view_path_first);
1638             if (!c_src_data)
1639             {
1640                 c_src_data = cp->file_new(s1);
1641                 // we copy the meta data soon,
1642                 // so don't bother with cp->file_new(p_src_data);
1643             }
1644             switch (p_src_data->action)
1645             {
1646             case file_action_remove:
1647                 c_src_data->action = file_action_remove;
1648                 break;
1649 
1650             case file_action_insulate:
1651             case file_action_transparent:
1652                 assert(0);
1653                 // fall through...
1654 
1655             case file_action_create:
1656             case file_action_modify:
1657 #ifndef DEBUG
1658             default:
1659 #endif
1660                 assert(p_src_data->edit);
1661                 assert(p_src_data->edit->revision);
1662                 c_src_data->action =
1663                     (read_only ? file_action_insulate : file_action_modify);
1664                 break;
1665             }
1666 
1667             //
1668             // Copy the file usage, attributes and uuid.
1669             //
1670             change_file_copy_basic_attributes(c_src_data, p_src_data);
1671 
1672             //
1673             // Watch out for test times.
1674             //
1675             if (!read_only)
1676             {
1677                 int             f_idx;
1678                 int             more_tests;
1679 
1680                 switch (c_src_data->usage)
1681                 {
1682                 case file_usage_test:
1683                 case file_usage_manual_test:
1684                     //
1685                     // The change now has at least one test, so cancel
1686                     // any testing exemption.
1687                     // (But test_baseline_exempt is still viable.)
1688                     //
1689                     change_rescind_test_exemption(cp);
1690 
1691                     //
1692                     // If there are no more tests, then the change
1693                     // must be made regression test exempt
1694                     //
1695                     more_tests = 0;
1696                     for (f_idx = 0; ; ++f_idx)
1697                     {
1698                         fstate_src_ty   *p_src;
1699 
1700                         p_src = pp2->file_nth(f_idx, view_path_extreme);
1701                         if (!p_src)
1702                             break;
1703                         switch (p_src->usage)
1704                         {
1705                         case file_usage_test:
1706                         case file_usage_manual_test:
1707                             if
1708                             (
1709                                 !cp->file_find
1710                                 (
1711                                     nstring(p_src->file_name),
1712                                     view_path_first
1713                                 )
1714                             )
1715                                 more_tests = 1;
1716                             break;
1717 
1718                         case file_usage_source:
1719                         case file_usage_config:
1720                         case file_usage_build:
1721                             continue;
1722                         }
1723                         break;
1724                     }
1725                     if (!more_tests)
1726                         change_force_regression_test_exemption(cp);
1727                     break;
1728 
1729                 case file_usage_source:
1730                 case file_usage_config:
1731                 case file_usage_build:
1732                     break;
1733                 }
1734             }
1735             if (old_src != older_src)
1736             {
1737                 //
1738                 // In the case of -rescind, crank forward to the following
1739                 // version.  That way we have copied the previous version,
1740                 // but claim the following version.  This will have the
1741                 // effect to backing out the delta specified.
1742                 //
1743                 assert(old_src);
1744                 p_src_data = old_src;
1745                 assert(p_src_data);
1746                 assert(p_src_data->edit);
1747                 assert(p_src_data->edit->revision);
1748             }
1749 
1750             //
1751             // p_src_data->edit
1752             //      The head revision of the branch.
1753             // p_src_data->edit_origin
1754             //      The version originally copied.
1755             //
1756             // c_src_data->edit
1757             //      Not meaningful until after integrate pass.
1758             // c_src_data->edit_origin
1759             //      The version originally copied.
1760             // c_src_data->edit_origin_new
1761             //      Updates branch edit_origin on
1762             //      integrate pass.
1763             //
1764             if (c_src_data->edit)
1765             {
1766                 assert(c_src_data->edit->revision);
1767                 history_version_type.free(c_src_data->edit);
1768                 c_src_data->edit = 0;
1769             }
1770             if (c_src_data->edit_origin)
1771             {
1772                 assert(c_src_data->edit_origin->revision);
1773                 history_version_type.free(c_src_data->edit_origin);
1774                 c_src_data->edit_origin = 0;
1775             }
1776             if (c_src_data->edit_origin_new)
1777             {
1778                 assert(c_src_data->edit_origin_new->revision);
1779                 history_version_type.free(c_src_data->edit_origin_new);
1780                 c_src_data->edit_origin_new = 0;
1781             }
1782             if (p_src_data->edit)
1783             {
1784                 assert(p_src_data->edit->revision);
1785                 c_src_data->edit_origin =
1786                     history_version_copy(p_src_data->edit);
1787             }
1788             else
1789             {
1790                 assert(p_src_data->edit_origin);
1791                 assert(p_src_data->edit_origin->revision);
1792                 assert(p_src_data->action == file_action_remove);
1793                 c_src_data->edit_origin =
1794                     history_version_copy(p_src_data->edit_origin);
1795             }
1796 
1797             //
1798             // Copying the config file into a change
1799             // invalidates all of the file fingerprints.
1800             // This is because the diff command,
1801             // test_command, build_command, etc, could be
1802             // changed when the config file is edited.
1803             //
1804             if (config_seen && c_src_data->file_fp)
1805             {
1806                 fingerprint_type.free(c_src_data->file_fp);
1807                 c_src_data->file_fp = 0;
1808             }
1809         }
1810 
1811         //
1812         // verbose progress message
1813         //
1814         sub_context_ty sc;
1815         sc.var_set_string("File_Name", s1);
1816         change_verbose(cp, &sc, i18n("copied $filename"));
1817     }
1818 
1819     bool recent_integration = false;
1820     if (!output_filename)
1821     {
1822         //
1823         // the number of files changed,
1824         // so stomp on the validation fields.
1825         //
1826         change_build_times_clear(cp);
1827 
1828         //
1829         // update the copyright years
1830         //
1831         change_copyright_years_now(cp);
1832 
1833         //
1834         // If the file manifest of the change is altered (e.g. by aenf, aenfu,
1835         // aecp, aecpu, etc), or the contents of any file is changed, the
1836         // UUID is cleared.  This is because it is no longer the same change
1837         // as was received by aedist or aepatch, and the UUID is invalidated.
1838         //
1839         change_uuid_clear(cp);
1840 
1841         // remember that we are about to
1842         recent_integration = cp->run_project_file_command_needed();
1843         if (recent_integration)
1844             cp->run_project_file_command_done();
1845 
1846         //
1847         // release the locks
1848         //
1849         cp->cstate_write();
1850         commit();
1851         lock_release();
1852     }
1853 
1854     //
1855     // verbose success message
1856     //
1857     sub_context_ty sc;
1858     sc.var_set_long("Number", (long)wl.nstrings);
1859     sc.var_optional("Number");
1860     change_verbose(cp, &sc, i18n("copy file complete"));
1861 
1862     if (!output_filename)
1863     {
1864         //
1865         // run the change file command
1866         // and the project file command if necessary
1867         //
1868         // The -output option means the change set's state is not
1869         // altered in any way, so no notification is required.
1870         //
1871         cp->run_copy_file_command(&wl, up);
1872     }
1873 
1874     if (recent_integration)
1875         cp->run_project_file_command(up);
1876 
1877     project_free(pp);
1878     change_free(cp);
1879     trace(("}\n"));
1880 }
1881 
1882 
1883 void
copy_file(void)1884 copy_file(void)
1885 {
1886     static arglex_dispatch_ty dispatch[] =
1887     {
1888         { arglex_token_help, copy_file_help, 0 },
1889         { arglex_token_list, copy_file_list, 0 },
1890         { arglex_token_independent, copy_file_independent, 0 },
1891     };
1892 
1893     trace(("copy_file()\n{\n"));
1894     arglex_dispatch(dispatch, SIZEOF(dispatch), copy_file_main);
1895     trace(("}\n"));
1896 }
1897 
1898 
1899 // vim: set ts=8 sw=4 et :
1900