1 //
2 // aegis - project change supervisor
3 // Copyright (C) 1995, 1999, 2001-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/stdio.h>
22 #include <common/ac/stdlib.h>
23 #include <common/ac/string.h>
24 #include <common/ac/time.h>
25 #include <common/ac/sys/types.h>
26 #include <common/ac/sys/stat.h>
27 
28 #include <common/progname.h>
29 #include <common/quit.h>
30 #include <common/sizeof.h>
31 #include <common/trace.h>
32 #include <libaegis/ael/change/by_state.h>
33 #include <libaegis/arglex/change.h>
34 #include <libaegis/arglex/project.h>
35 #include <libaegis/arglex2.h>
36 #include <libaegis/change.h>
37 #include <libaegis/change/branch.h>
38 #include <libaegis/change/file.h>
39 #include <libaegis/change/identifier.h>
40 #include <libaegis/col.h>
41 #include <libaegis/commit.h>
42 #include <libaegis/dir.h>
43 #include <libaegis/gonzo.h>
44 #include <libaegis/help.h>
45 #include <libaegis/lock.h>
46 #include <libaegis/os.h>
47 #include <libaegis/project.h>
48 #include <libaegis/project/active.h>
49 #include <libaegis/project/history.h>
50 #include <libaegis/sub.h>
51 #include <libaegis/undo.h>
52 #include <libaegis/user.h>
53 
54 #include <aegis/aenbru.h>
55 
56 
57 static void
new_branch_undo_usage(void)58 new_branch_undo_usage(void)
59 {
60     const char      *progname;
61 
62     progname = progname_get();
63     fprintf
64     (
65         stderr,
66         "usage: %s -New_Branch_Undo <change_number> [ <option>... ]\n",
67         progname
68     );
69     fprintf
70     (
71         stderr,
72         "       %s -New_Branch_Undo -List [ <option>... ]\n",
73         progname
74     );
75     fprintf(stderr, "       %s -New_Branch_Undo -Help\n", progname);
76     quit(1);
77 }
78 
79 
80 static void
new_branch_undo_help(void)81 new_branch_undo_help(void)
82 {
83     help("aenbru", new_branch_undo_usage);
84 }
85 
86 
87 static void
new_branch_undo_list(void)88 new_branch_undo_list(void)
89 {
90     trace(("new_branch_undo_list()\n{\n"));
91     arglex();
92     change_identifier cid;
93     cid.command_line_parse_rest(new_branch_undo_usage);
94     list_changes_in_state_mask(cid, 1 << cstate_state_being_developed);
95     trace(("}\n"));
96 }
97 
98 
99 static string_ty *
branch_changes_path_get(change::pointer cp)100 branch_changes_path_get(change::pointer cp)
101 {
102     project         *pp;
103     string_ty       *result;
104 
105     assert(cp->is_a_branch());
106     pp = cp->pp->bind_branch(change_copy(cp));
107     result = str_copy(pp->changes_path_get());
108     project_free(pp);
109     return result;
110 }
111 
112 
113 static void
new_branch_undo_main(void)114 new_branch_undo_main(void)
115 {
116     string_ty       *project_name;
117     long            change_number;
118     project         *pp;
119     user_ty::pointer up;
120     change::pointer cp;
121     string_ty       *s;
122 
123     trace(("new_branch_undo_main()\n{\n"));
124     arglex();
125     project_name = 0;
126     change_number = 0;
127     while (arglex_token != arglex_token_eoln)
128     {
129         switch (arglex_token)
130         {
131         default:
132             generic_argument(new_branch_undo_usage);
133             continue;
134 
135         case arglex_token_keep:
136         case arglex_token_interactive:
137         case arglex_token_keep_not:
138             user_ty::delete_file_argument(new_branch_undo_usage);
139             break;
140 
141         case arglex_token_change:
142         case arglex_token_branch:
143             arglex();
144             // fall through...
145 
146         case arglex_token_number:
147             arglex_parse_branch
148             (
149                 &project_name,
150                 &change_number,
151                 new_branch_undo_usage
152             );
153             continue;
154 
155         case arglex_token_project:
156             arglex();
157             // fall through...
158 
159         case arglex_token_string:
160             arglex_parse_project(&project_name, new_branch_undo_usage);
161             continue;
162 
163         case arglex_token_wait:
164         case arglex_token_wait_not:
165             user_ty::lock_wait_argument(new_branch_undo_usage);
166             break;
167         }
168         arglex();
169     }
170 
171     //
172     // locate project data
173     //
174     if (!project_name)
175     {
176         nstring n = user_ty::create()->default_project();
177         project_name = n.get_ref_copy();
178     }
179     pp = project_alloc(project_name);
180     str_free(project_name);
181     pp->bind_existing();
182 
183     //
184     // locate user data
185     //
186     up = user_ty::create();
187     if (!project_administrator_query(pp, up->name()))
188         project_fatal(pp, 0, i18n("not an administrator"));
189 
190     //
191     // locate change data
192     //
193     if (!change_number)
194         change_number = up->default_change(pp);
195     cp = change_alloc(pp, change_number);
196     change_bind_existing(cp);
197 
198     //
199     // Make sure we are talking about a branch.
200     //
201     if (!cp->is_a_branch())
202         change_fatal(cp, 0, i18n("use aedbu instead"));
203 
204     //
205     // Make sure the branch is not active.
206     //      (project_active reports the error itself)
207     //
208     project_active_check_branch(cp, 0);
209 
210     //
211     // Take an advisory write lock on the appropriate row of the change
212     // table.  Take an advisory write lock on the appropriate row of the
213     // user table.  Block until can get both simultaneously.
214     //
215     gonzo_gstate_lock_prepare_new();
216     pp->pstate_lock_prepare();
217     change_cstate_lock_prepare(cp);
218     up->ustate_lock_prepare();
219     lock_take();
220 
221     //
222     // Race condition: check that the up is still a project
223     // administrator now that we have the project lock.
224     //
225     if (!project_administrator_query(pp, up->name()))
226         project_fatal(pp, 0, i18n("not an administrator"));
227 
228     //
229     // It is an error if the change is not in the being developed state.
230     // It is an error if the change is not assigned to the current user.
231     //
232     if (!cp->is_being_developed())
233         change_fatal(cp, 0, i18n("bad dbu state"));
234 
235     //
236     // Remove the change from the list of assigned changes in the user
237     // change table (in the user row).
238     //
239     up->own_remove(pp, change_number);
240 
241     //
242     // remove the development directory
243     //
244     nstring dd(change_top_path_get(cp, 1));
245     if (up->delete_file_query(dd, true, true))
246     {
247         change_verbose(cp, 0, i18n("remove development directory"));
248         user_ty::become scoped(pp->get_user());
249         commit_rmdir_tree_errok(dd);
250     }
251 
252     //
253     // tell the project to forget this change
254     //
255     project_change_delete(pp, change_number);
256 
257     //
258     // delete the change state file of the branch
259     // and the state files of all subordinate changes
260     //
261     project_become(pp);
262     commit_unlink_errok(cp->cstate_filename_get());
263     commit_unlink_errok(change_fstate_filename_get(cp));
264     commit_rmdir_tree_errok(branch_changes_path_get(cp));
265     project_become_undo(pp);
266 
267     //
268     // Remove aliases of this branch.
269     //      (Punctuation?)
270     //
271     s = str_format("%s.%ld", project_name_get(pp).c_str(), change_number);
272     gonzo_alias_delete(s);
273     str_free(s);
274 
275     //
276     // Update change table row (and change history table).
277     // Update user table row.
278     // Release advisory write locks.
279     //
280     pp->pstate_write();
281     gonzo_gstate_write();
282     commit();
283     lock_release();
284 
285     //
286     // verbose success message
287     //
288     change_verbose(cp, 0, i18n("new branch undo complete"));
289     change_free(cp);
290     project_free(pp);
291     trace(("}\n"));
292 }
293 
294 
295 void
new_branch_undo(void)296 new_branch_undo(void)
297 {
298     static arglex_dispatch_ty dispatch[] =
299     {
300         { arglex_token_help, new_branch_undo_help, 0 },
301         { arglex_token_list, new_branch_undo_list, 0 },
302     };
303 
304     trace(("new_branch_undo()\n{\n"));
305     arglex_dispatch(dispatch, SIZEOF(dispatch), new_branch_undo_main);
306     trace(("}\n"));
307 }
308 
309 
310 // vim: set ts=8 sw=4 et :
311