1 //
2 // aegis - project change supervisor
3 // Copyright (C) 2004-2009, 2011, 2012 Peter Miller
4 // Copyright (C) 2008 Walter Franzini
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or (at
9 // your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program. If not, see <http://www.gnu.org/licenses/>.
18 //
19
20 #include <common/ac/assert.h>
21 #include <common/ac/string.h>
22
23 #include <common/symtab.h>
24 #include <libaegis/change/branch.h>
25 #include <libaegis/change/file.h>
26 #include <libaegis/change.h>
27 #include <libaegis/change/lock_sync.h>
28 #include <libaegis/cstate.fmtgen.h>
29 #include <libaegis/fstate.fmtgen.h>
30 #include <libaegis/input/file.h>
31 #include <libaegis/os.h>
32 #include <libaegis/output/file.h>
33 #include <libaegis/project/file.h>
34 #include <libaegis/project.h>
35 #include <libaegis/user.h>
36
37 #include <aecvsserver/fake_version.h>
38 #include <aecvsserver/file_info.h>
39 #include <aecvsserver/module/change_bogus.h>
40 #include <aecvsserver/module/change.h>
41 #include <aecvsserver/module/project_bogu.h>
42 #include <aecvsserver/response/checked_in.h>
43 #include <aecvsserver/response/created.h>
44 #include <aecvsserver/response/new_entry.h>
45 #include <aecvsserver/response/removed.h>
46 #include <aecvsserver/response/remove_entry.h>
47 #include <aecvsserver/response/update_exist.h>
48 #include <aecvsserver/server.h>
49
50
~module_change()51 module_change::~module_change()
52 {
53 change_free(cp);
54 cp = 0;
55 project_free(pp);
56 pp = 0;
57 }
58
59
module_change(change::pointer arg)60 module_change::module_change(change::pointer arg) :
61 cp(arg),
62 pp(arg->pp),
63 up(user_ty::create())
64 {
65 }
66
67
68 void
modified(server_ty * sp,string_ty * file_name,file_info_ty * fip,input & contents)69 module_change::modified(server_ty *sp, string_ty *file_name, file_info_ty *fip,
70 input &contents)
71 {
72 //
73 // It is an error if the change is not in the "being developed" state.
74 //
75 if (!cp->is_being_developed())
76 {
77 server_error
78 (
79 sp,
80 "Modified: project \"%s\": change %ld: this change must be "
81 "in the \"being_developed\" state for this to work",
82 project_name_get(pp).c_str(),
83 cp->number
84 );
85 return;
86 }
87
88 //
89 // It is an error if the change is actually a branch.
90 //
91 if (cp->was_a_branch())
92 {
93 server_error
94 (
95 sp,
96 "Modified: project \"%s\": change %ld: is a branch; to modify "
97 "files you must use a change on the branch",
98 project_name_get(pp).c_str(),
99 cp->number
100 );
101 return;
102 }
103
104 //
105 // It is an error if the change is not assigned to the current user.
106 //
107 if (nstring(cp->developer_name()) != up->name())
108 {
109 server_error
110 (
111 sp,
112 "Modified: project \"%s\": change %ld: is owned by user \"%s\", "
113 "but it must be owned by user \"%s\" for this to work",
114 project_name_get(pp).c_str(),
115 cp->number,
116 cp->developer_name()->str_text,
117 up->name().c_str()
118 );
119 }
120
121 //
122 // If the file doesn't exist in the change, copy it or create it
123 // as appropriate.
124 //
125 fstate_src_ty *csrc = cp->file_find(nstring(file_name), view_path_first);
126 if (!csrc)
127 {
128 string_ty *qf;
129 const char *verb;
130 nstring the_command;
131 int ok;
132
133 nstring qp(project_name_get(pp).quote_shell());
134 qf = str_quote_shell(file_name);
135 if (!pp->file_find(file_name, view_path_extreme))
136 {
137 //
138 // FIXME: what if it's a new test?
139 // (aepatch has the same problem)
140 //
141 verb = "-new-file";
142 }
143 else
144 {
145 verb = "-copy-file";
146 }
147 the_command =
148 nstring::format
149 (
150 "aegis %s -project=%s -change=%ld -base-relative -v %s",
151 verb,
152 qp.c_str(),
153 cp->number,
154 qf->str_text
155 );
156 str_free(qf);
157
158 //
159 // Run the command.
160 // if there is a problem, it will also send the error to the client.
161 //
162 ok = server_execute(sp, the_command.get_ref());
163 if (!ok)
164 return;
165
166 //
167 // Change objects can be very long lived in the aecvsserver,
168 // so make sure that we re-read the meta data soon.
169 //
170 change_lock_sync_forced(cp);
171
172 //
173 // Now re-get the file information.
174 //
175 csrc = cp->file_find(nstring(file_name), view_path_first);
176 assert(csrc);
177 }
178 else if (csrc->action == file_action_remove)
179 {
180 server_error
181 (
182 sp,
183 "Modified: project \"%s\": change %ld: file \"%s\" is being "
184 "removed, it makes no sense to say it's been modified",
185 project_name_get(pp).c_str(),
186 cp->number,
187 file_name->str_text
188 );
189 return;
190 }
191
192 //
193 // Figure the path of the file to be written.
194 //
195 string_ty *dd = change_development_directory_get(cp, 0);
196 string_ty *abs_file_name = os_path_cat(dd, file_name);
197
198 //
199 // Normalize the mode (Aegis has its own idea about file modes).
200 //
201 if (fip->mode & 0222)
202 fip->mode |= 0222;
203 if (fip->mode & 0111)
204 fip->mode |= 0111;
205 fip->mode |= 0644;
206 fip->mode &= ~cp->umask_get();
207
208 //
209 // Copy the file contents to their destination.
210 //
211 os_become_orig();
212 output::pointer op = output_file::binary_open(abs_file_name);
213 op << contents;
214 op.reset();
215
216 //
217 // And make sure it is in the specified mode.
218 //
219 os_chmod(abs_file_name, fip->mode);
220 os_become_undo();
221 str_free(abs_file_name);
222 }
223
224
225 string_ty *
calculate_canonical_name() const226 module_change::calculate_canonical_name()
227 const
228 {
229 // FIXME: memory leak
230 return
231 str_format
232 (
233 "%s.C%3.3ld",
234 project_name_get(pp).c_str(),
235 cp->number
236 );
237 }
238
239
240 bool
update(server_ty * sp,string_ty *,string_ty * server_side_0,const options & opt)241 module_change::update(server_ty *sp, string_ty *, string_ty *server_side_0,
242 const options &opt)
243 {
244 size_t j;
245 static string_ty *minus_str;
246 static string_ty *zero;
247
248 //
249 // It is an error if the change is not in the "being developed" state.
250 //
251 // FIXME: actually, it's OK if the file is between being_developed
252 // and being_integrated, but only read-only for the later ones.
253 //
254 // FIXME: what about changes in the completed state, we will need
255 // to use project_file_roll_forward instead.
256 //
257 if (!cp->is_being_developed())
258 {
259 server_error
260 (
261 sp,
262 "project \"%s\": change %ld: this change must be in the "
263 "\"being_developed\" state for this to work",
264 project_name_get(pp).c_str(),
265 cp->number
266 );
267 return false;
268 }
269
270 //
271 // Form a list of files by unioning the source files of the change
272 // and all the ancestor branches together.
273 //
274 // For each of these files (that the client wants to know about)
275 // send information about their contents.
276 //
277 for (j = 0; ; ++j)
278 {
279 fstate_src_ty *src;
280 string_ty *client_side;
281 string_ty *server_side;
282 string_ty *path;
283 int need_to_unlink;
284 int mode;
285 string_ty *version;
286 int is_local;
287 file_info_ty *fip;
288
289 src = change_file_nth(cp, j, view_path_simple);
290 if (!src)
291 break;
292 switch (src->usage)
293 {
294 case file_usage_build:
295 continue;
296
297 case file_usage_source:
298 case file_usage_config:
299 case file_usage_test:
300 case file_usage_manual_test:
301 break;
302 }
303
304 //
305 // Make sure the client creates the directories for us.
306 //
307 server_side = os_path_cat(name(), src->file_name);
308 if (!is_update_prefix(server_side_0, server_side, opt.d))
309 {
310 //
311 // don't create files which are not under one of the
312 // Directories specified by the client.
313 //
314 str_free(server_side);
315 continue;
316 }
317 client_side = server_directory_calc_client_side(sp, server_side);
318 server_mkdir_above(sp, client_side, server_side);
319 server_updating_verbose(sp, client_side);
320
321 //
322 // Determine where to get the file from.
323 //
324 version = 0;
325 path = change_file_version_path(cp, src, &need_to_unlink);
326 is_local = !!cp->file_find(nstring(src->file_name), view_path_first);
327 os_become_orig();
328 input ip = input_file_open(path, need_to_unlink);
329 if (is_local && os_executable(path))
330 src->executable = true;
331 if (is_local)
332 version = fake_version(os_mtime_actual(path));
333 os_become_undo();
334 str_free(path);
335
336 //
337 // Determine the file mode.
338 //
339 mode = 0666;
340 if (src->executable)
341 mode |= 0111;
342 mode &= ~cp->umask_get();
343
344 //
345 // Determine the version string to send to the client.
346 // Special cases:
347 // "" - no user file,
348 // "0" - new user file,
349 // "-" - user file to be removed
350 //
351 if (!version)
352 {
353 switch (src->action)
354 {
355 case file_action_remove:
356 //
357 // What do do depends on whether the file exists on the
358 // client or not.
359 //
360 // the client has never heard of it
361 // do nothing
362 //
363 // The client has no Entry for it, but said Questionable
364 // do nothing
365 //
366 // the client Entry has a removed version ("-")
367 // do nothing.
368 //
369 // the client Entry has a new file version ("0")
370 // do nothing.
371 //
372 // the client says it exists
373 // if Is-modified
374 // send the new file ("0") Entry
375 // otherwise
376 // send the Removed response, which will remove
377 // the entry and the file from the client side.
378 //
379 fip = server_file_info_find(sp, server_side, 0);
380 if (!fip || !fip->version)
381 goto do_nothing;
382 if (!minus_str)
383 minus_str = str_from_c("-");
384 if (!zero)
385 zero = str_from_c("0");
386 if
387 (
388 str_equal(fip->version, minus_str)
389 ||
390 str_equal(fip->version, zero)
391 )
392 goto do_nothing;
393 if (fip->modified > 0)
394 {
395 version = str_copy(zero);
396 break;
397 }
398 server_response_queue
399 (
400 sp,
401 new response_removed(client_side, server_side)
402 );
403 goto do_nothing;
404
405 case file_action_transparent:
406 version = str_from_c("0");
407 break;
408
409 case file_action_create:
410 case file_action_modify:
411 case file_action_insulate:
412 if (src->edit && src->edit->revision)
413 version = str_copy(src->edit->revision);
414 else
415 version = str_from_c("0");
416 break;
417 }
418 if (!version)
419 version = str_from_c("");
420 }
421
422 fip = server_file_info_find(sp, server_side, 0);
423 if (!fip)
424 {
425 //
426 // Queue the response to be sent.
427 //
428 server_response_queue
429 (
430 sp,
431 new response_created
432 (
433 client_side,
434 server_side,
435 ip,
436 mode,
437 version
438 )
439 );
440 }
441 else if (opt.C)
442 {
443 //
444 // We have been told to over-write what they have on the
445 // client side.
446 //
447 if (fip->modified > 0)
448 {
449 server_response_queue
450 (
451 sp,
452 new response_update_existing
453 (
454 client_side,
455 server_side,
456 ip,
457 mode,
458 version
459 )
460 );
461 }
462 }
463 else if (str_equal(fip->version, version))
464 {
465 //
466 // What they copied the first time is still the current
467 // version on the server side.
468 //
469 if (fip->modified > 0)
470 {
471 string_ty *sub;
472
473 //
474 // Remind them they need to commit it.
475 //
476 sub = os_entryname(client_side);
477 server_m(sp, "M %s\n", sub->str_text);
478 str_free(sub);
479 }
480 }
481 else
482 {
483 //
484 // What they copied the first time is no longer the
485 // current version on the server side.
486 //
487 if (fip->modified)
488 {
489 //
490 // The server side change and the client side changed.
491 // The Modified request (received earlier) has copied
492 // the file into the change, and the over-written it.
493 //
494 // We have to send the file back to them,
495 // AFTER running a merge command.
496 //
497 // FIXME: run the merge command
498 //
499 server_response_queue
500 (
501 sp,
502 new response_update_existing
503 (
504 client_side,
505 server_side,
506 ip,
507 mode,
508 version
509 )
510 );
511 }
512 else
513 {
514 //
515 // The client side is out-of-date, resend the file and
516 // over-write the client-side contents.
517 //
518 server_response_queue
519 (
520 sp,
521 new response_update_existing
522 (
523 client_side,
524 server_side,
525 ip,
526 mode,
527 version
528 )
529 );
530 }
531 }
532 do_nothing:
533 str_free(client_side);
534 str_free(server_side);
535 str_free(version);
536
537 os_become_orig();
538 ip.close();
539 os_become_undo();
540 }
541 return true;
542 }
543
544
545 static bool
file_being_deleted(server_ty * sp,string_ty * server_side)546 file_being_deleted(server_ty *sp, string_ty *server_side)
547 {
548 file_info_ty *fip;
549 static string_ty *minus_str;
550
551 fip = server_file_info_find(sp, server_side, 0);
552 if (!fip)
553 return false;
554 if (!fip->version)
555 return false;
556 if (!minus_str)
557 minus_str = str_from_c("-");
558 return str_equal(fip->version, minus_str);
559 }
560
561
562 bool
checkin(server_ty * sp,string_ty * client_side,string_ty * server_side)563 module_change::checkin(server_ty *sp, string_ty *client_side,
564 string_ty *server_side)
565 {
566 fstate_src_ty *src;
567 int mode;
568 string_ty *version;
569 string_ty *filename;
570 const char *strp;
571
572 //
573 // It is an error if the change is not in the "being developed" state.
574 //
575 if (!cp->is_being_developed())
576 {
577 server_error
578 (
579 sp,
580 "ci: project \"%s\": change %ld: this change must be "
581 "in the \"being_developed\" state for this to work",
582 project_name_get(pp).c_str(),
583 cp->number
584 );
585 return false;
586 }
587
588 //
589 // Extract the baseline-relative name of the file.
590 //
591 // The server-side string sent by the client will be
592 // ROOT_PATH/project.Cnnn/filename so skip three slashes (ROOT_PATH
593 // starts with a slash) and use the rest. Except that the Directory
594 // request code strips off the ROOT_PATH/ part, so skip one slash
595 // instead.
596 //
597 strp = strchr(server_side->str_text, '/');
598 assert(strp);
599 filename = str_from_c(strp ? strp + 1 : ".");
600
601 src = cp->file_find(nstring(filename), view_path_first);
602 if (!src)
603 {
604 if (file_being_deleted(sp, server_side))
605 {
606 string_ty *qf;
607 string_ty *the_command;
608 int ok;
609
610 nstring qp(project_name_get(pp).quote_shell());
611 qf = str_quote_shell(filename);
612 the_command =
613 str_format
614 (
615 "aegis --remove-file --project=%s --change=%ld --base-relative -v %s",
616 qp.c_str(),
617 cp->number,
618 qf->str_text
619 );
620 str_free(qf);
621
622 //
623 // Run the command. If there is a problem, it will also
624 // send the error to the client.
625 //
626 ok = server_execute(sp, the_command);
627 str_free(the_command);
628 if (!ok)
629 return false;
630
631 //
632 // Change objects can be very long lived in the aecvsserver,
633 // so make sure that we re-read the meta data soon.
634 //
635 change_lock_sync_forced(cp);
636
637 //
638 // Let the client know the file is well and truly gone.
639 // (We dont use the Removed response because the file is
640 // already deleted from thwe client.)
641 //
642 server_response_queue
643 (
644 sp,
645 new response_remove_entry(client_side, server_side)
646 );
647
648 //
649 // Report success.
650 //
651 return true;
652 }
653
654 server_error
655 (
656 sp,
657 "ci: project \"%s\": change %ld: file \"%s\" unknown",
658 project_name_get(pp).c_str(),
659 cp->number,
660 filename->str_text
661 );
662 return false;
663 }
664
665 //
666 // For changes which are being developeed, like this one, we
667 // have to look at the file itself for the executable bit.
668 //
669 if (src->action != file_action_remove)
670 {
671 string_ty *path;
672
673 path = cp->file_path(filename);
674 os_become_orig();
675 if (os_executable(path))
676 src->executable = true;
677 os_become_undo();
678 str_free(path);
679 }
680
681 //
682 // Determine the file mode.
683 //
684 mode = 0666;
685 if (src->executable)
686 mode |= 0111;
687 mode &= ~cp->umask_get();
688
689 //
690 // Determine the version string to send to the client.
691 // Special cases:
692 // "" - no user file,
693 // "0" - new user file,
694 // "-" - user file to be removed
695 //
696 version = 0;
697 switch (src->action)
698 {
699 case file_action_remove:
700 version = str_from_c("-");
701 break;
702
703 case file_action_transparent:
704 version = str_from_c("0");
705 break;
706
707 case file_action_create:
708 case file_action_modify:
709 case file_action_insulate:
710 if (src->edit && src->edit->revision)
711 version = str_copy(src->edit->revision);
712 else
713 version = str_from_c("0");
714 break;
715 }
716 if (!version)
717 version = str_from_c("");
718
719 //
720 // Queue the response to be sent.
721 //
722 server_response_queue
723 (
724 sp,
725 new response_checked_in(client_side, server_side, mode, version)
726 );
727 str_free(version);
728
729 return true;
730 }
731
732
733 bool
add(server_ty * sp,string_ty * client_side,string_ty * server_side,const options &)734 module_change::add(server_ty *sp, string_ty *client_side,
735 string_ty *server_side, const options &)
736 {
737 fstate_src_ty *src;
738 int mode;
739 string_ty *version;
740 string_ty *filename;
741 const char *strp;
742
743 //
744 // It is an error if the change is not in the "being developed" state.
745 //
746 if (!cp->is_being_developed())
747 {
748 server_error
749 (
750 sp,
751 "add: project \"%s\": change %ld: this change must be "
752 "in the \"being_developed\" state for this to work",
753 project_name_get(pp).c_str(),
754 cp->number
755 );
756 return false;
757 }
758
759 //
760 // Extract the baseline-relative name of the file.
761 //
762 // The server-side string sent by the client will be
763 // ROOT_PATH/project.Cnnn/filename so skip three slashes and use
764 // the rest. Except that the Directory request code strips off
765 // the ROOT_PATH/ part, so skip one slash instead.
766 //
767 strp = strchr(server_side->str_text, '/');
768 assert(strp);
769 filename = str_from_c(strp ? strp + 1 : ".");
770
771 //
772 // The client sends an "Is-modified" request, so the file isn't
773 // necesarily created yet.
774 //
775 src = cp->file_find(nstring(filename), view_path_first);
776 if (src)
777 {
778 //
779 // The CVS documentation says that a "cvs add" of a removed file
780 // will re-instate it.
781 //
782 if (src->action == file_action_remove)
783 {
784 string_ty *qf;
785 string_ty *the_command;
786 int ok;
787
788 nstring qp(project_name_get(pp).quote_shell());
789 qf = str_quote_shell(filename);
790 the_command =
791 str_format
792 (
793 "aegis -rmu -project=%s -change=%ld -base-relative -v %s",
794 qp.c_str(),
795 cp->number,
796 qf->str_text
797 );
798
799 //
800 // Run the command.
801 // if there is a problem, it will also send the error to the client.
802 //
803 ok = server_execute(sp, the_command);
804 str_free(the_command);
805 if (!ok)
806 {
807 str_free(qf);
808 return false;
809 }
810
811 //
812 // Change objects can be very long lived in the aecvsserver,
813 // so make sure that we re-read the meta data soon.
814 //
815 change_lock_sync_forced(cp);
816
817 //
818 // Now re-get the file information.
819 //
820 src = pp->file_find(filename, view_path_extreme);
821 if (src)
822 {
823 the_command =
824 str_format
825 (
826 "aegis -copy-file -project=%s -change=%ld -base-relative -v %s",
827 qp.c_str(),
828 cp->number,
829 qf->str_text
830 );
831
832 //
833 // Run the command. if there is a problem, it will also
834 // send the error to the client.
835 //
836 ok = server_execute(sp, the_command);
837 str_free(the_command);
838 if (!ok)
839 {
840 str_free(qf);
841 return false;
842 }
843
844 //
845 // Change objects can be very long lived in the aecvsserver,
846 // so make sure that we re-read the meta data soon.
847 //
848 change_lock_sync_forced(cp);
849
850 //
851 // Now re-get the file information.
852 //
853 src = cp->file_find(nstring(filename), view_path_first);
854 assert(src);
855 }
856 str_free(qf);
857 }
858 }
859
860 //
861 // For changes which are being developeed, like this one, we
862 // have to look at the file itself for the executable bit.
863 //
864 if (src && src->action != file_action_remove)
865 {
866 string_ty *path;
867
868 path = cp->file_path(filename);
869 os_become_orig();
870 if (os_executable(path))
871 src->executable = true;
872 os_become_undo();
873 str_free(path);
874 }
875
876 //
877 // Determine the file mode.
878 //
879 mode = 0666;
880 if (src && src->executable)
881 mode |= 0111;
882 mode &= ~cp->umask_get();
883
884 if (!src)
885 {
886 version = str_from_c("0");
887 server_e
888 (
889 sp,
890 "scheduling file `%s' for addition",
891 client_side->str_text
892 );
893 server_response_queue
894 (
895 sp,
896 new response_new_entry(client_side, server_side, mode, version)
897 );
898 str_free(version);
899
900 return true;
901 }
902
903 //
904 // Determine the version string to send to the client.
905 // Special cases:
906 // "" - no user file,
907 // "0" - new user file,
908 // "-" - user file to be removed
909 //
910 version = 0;
911 switch (src->action)
912 {
913 case file_action_remove:
914 version = str_from_c("-");
915 break;
916
917 case file_action_transparent:
918 case file_action_create:
919 version = str_from_c("0");
920 break;
921
922 case file_action_modify:
923 case file_action_insulate:
924 if (src->edit && src->edit->revision)
925 version = str_copy(src->edit->revision);
926 else
927 version = str_from_c("0");
928 break;
929 }
930 if (!version)
931 version = str_from_c("");
932
933 //
934 // Queue the response to be sent.
935 //
936 server_e
937 (
938 sp,
939 "scheduling file `%s' for addition",
940 src->file_name->str_text
941 );
942 server_response_queue
943 (
944 sp,
945 new response_new_entry(client_side, server_side, mode, version)
946 );
947 str_free(version);
948
949 return true;
950 }
951
952
953 bool
remove(server_ty * sp,string_ty * client_side,string_ty * server_side,const options &)954 module_change::remove(server_ty *sp, string_ty *client_side,
955 string_ty *server_side, const options &)
956 {
957 fstate_src_ty *src;
958 int mode;
959 string_ty *version;
960 string_ty *filename;
961 const char *strp;
962
963 //
964 // It is an error if the change is not in the "being developed" state.
965 //
966 if (!cp->is_being_developed())
967 {
968 server_error
969 (
970 sp,
971 "remove: project \"%s\": change %ld: this change must be "
972 "in the \"being_developed\" state for this to work",
973 project_name_get(pp).c_str(),
974 cp->number
975 );
976 return false;
977 }
978
979 //
980 // Extract the baseline-relative name of the file.
981 //
982 // The server-side string sent by the client will be
983 // ROOT_PATH/project.Cnnn/filename so skip three slashes and use
984 // the rest. Except that the Directory request code strips off
985 // the ROOT_PATH/ part, so skip one slash instead.
986 //
987 strp = strchr(server_side->str_text, '/');
988 assert(strp);
989 filename = str_from_c(strp ? strp + 1 : ".");
990
991 //
992 // If the file already exists in the change,
993 // we may have to undo that.
994 //
995 src = cp->file_find(nstring(filename), view_path_first);
996 if (src)
997 {
998 string_ty *qf;
999 string_ty *the_command;
1000 int ok;
1001 const char *verb;
1002
1003 verb = "--copy-file-undo";
1004 switch (src->action)
1005 {
1006 case file_action_create:
1007 verb = "--new-file-undo";
1008 switch (src->usage)
1009 {
1010 case file_usage_source:
1011 case file_usage_config:
1012 case file_usage_build:
1013 break;
1014
1015 case file_usage_test:
1016 case file_usage_manual_test:
1017 verb = "--new-test-undo";
1018 break;
1019 }
1020 break;
1021
1022 case file_action_modify:
1023 case file_action_insulate:
1024 break;
1025
1026 case file_action_transparent:
1027 verb = "--make-transparent-undo";
1028 break;
1029
1030 case file_action_remove:
1031 goto already_being_removed;
1032 }
1033
1034 nstring qp = project_name_get(pp).quote_shell();
1035 qf = str_quote_shell(filename);
1036 the_command =
1037 str_format
1038 (
1039 "aegis %s -project=%s -change=%ld -base-relative -v %s",
1040 verb,
1041 qp.c_str(),
1042 cp->number,
1043 qf->str_text
1044 );
1045 str_free(qf);
1046
1047 //
1048 // Run the command.
1049 // if there is a problem, it will also send the error to the client.
1050 //
1051 ok = server_execute(sp, the_command);
1052 str_free(the_command);
1053 if (!ok)
1054 return false;
1055
1056 //
1057 // Change objects can be very long lived in the aecvsserver,
1058 // so make sure that we re-read the meta data soon.
1059 //
1060 change_lock_sync_forced(cp);
1061
1062 switch (src->action)
1063 {
1064 case file_action_create:
1065 server_response_queue
1066 (
1067 sp,
1068 new response_remove_entry(client_side, server_side)
1069 );
1070 return true;
1071
1072 case file_action_remove:
1073 case file_action_modify:
1074 case file_action_insulate:
1075 case file_action_transparent:
1076 break;
1077 }
1078 src = 0;
1079 }
1080
1081 src = pp->file_find(filename, view_path_extreme);
1082 if (!src)
1083 {
1084 server_error
1085 (
1086 sp,
1087 "project \"%s\": change %ld: file \"%s\" cannot be removed "
1088 "because it does not exist in the project",
1089 project_name_get(pp).c_str(),
1090 cp->number,
1091 filename->str_text
1092 );
1093 return false;
1094 }
1095
1096 //
1097 // Determine the version string to send to the client.
1098 // Special cases:
1099 // "" - no user file,
1100 // "0" - new user file,
1101 // "-" - user file to be removed
1102 //
1103 already_being_removed:
1104 version = str_from_c("-");;
1105 mode = 0644;
1106
1107 //
1108 // Queue the response to be sent.
1109 //
1110 server_e
1111 (
1112 sp,
1113 "scheduling file `%s' for removal",
1114 src->file_name->str_text
1115 );
1116 server_response_queue
1117 (
1118 sp,
1119 new response_new_entry(client_side, server_side, mode, version)
1120 );
1121 str_free(version);
1122
1123 return true;
1124 }
1125
1126
1127 module_ty *
module_change_new(string_ty * project_name,long change_number)1128 module_change_new(string_ty *project_name, long change_number)
1129 {
1130 //
1131 // Make sure the project makes sense.
1132 //
1133 project *pp = project_alloc(project_name);
1134 if (!pp->bind_existing_errok())
1135 {
1136 project_free(pp);
1137 return new module_project_bogus(project_name);
1138 }
1139
1140 //
1141 // Make sure the change makes sense.
1142 //
1143 change::pointer cp = change_alloc(pp, change_number);
1144 if (!change_bind_existing_errok(cp))
1145 {
1146 change_free(cp);
1147 project_free(pp);
1148 return new module_change_bogus(project_name, change_number);
1149 }
1150
1151 //
1152 // OK, looks like we have a viable change.
1153 //
1154 return new module_change(cp);
1155 }
1156
1157
1158 // vim: set ts=8 sw=4 et :
1159