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