1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez.
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included
13 // in all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 // SOFTWARE.
22 //
23 // https://www.opensource.org/licenses/mit-license.php
24 //
25 ////////////////////////////////////////////////////////////////////////////////
26 
27 #include <cmake.h>
28 #include <iostream>
29 #include <vector>
30 #include <stdlib.h>
31 #include <shared.h>
32 #include <format.h>
33 #include <util.h>
34 #include <Command.h>
35 #include <main.h>
36 
37 #include <CmdAdd.h>
38 #include <CmdAliases.h>
39 #include <CmdAnnotate.h>
40 #include <CmdAppend.h>
41 #include <CmdAttributes.h>
42 #include <CmdBurndown.h>
43 #include <CmdCalc.h>
44 #include <CmdCalendar.h>
45 #include <CmdColor.h>
46 #include <CmdColumns.h>
47 #include <CmdCommands.h>
48 #include <CmdConfig.h>
49 #include <CmdContext.h>
50 #include <CmdCount.h>
51 #include <CmdCustom.h>
52 #include <CmdDelete.h>
53 #include <CmdDenotate.h>
54 #include <CmdDiagnostics.h>
55 #include <CmdDone.h>
56 #include <CmdDuplicate.h>
57 #include <CmdEdit.h>
58 #ifdef HAVE_EXECUTE
59 #include <CmdExec.h>
60 #endif
61 #include <CmdExport.h>
62 #include <CmdGet.h>
63 #include <CmdHelp.h>
64 #include <CmdHistory.h>
65 #include <CmdIDs.h>
66 #include <CmdImport.h>
67 #include <CmdInfo.h>
68 #include <CmdLog.h>
69 #include <CmdLogo.h>
70 #include <CmdModify.h>
71 #include <CmdNews.h>
72 #include <CmdPrepend.h>
73 #include <CmdProjects.h>
74 #include <CmdPurge.h>
75 #include <CmdReports.h>
76 #include <CmdShow.h>
77 #include <CmdStart.h>
78 #include <CmdStats.h>
79 #include <CmdStop.h>
80 #include <CmdSummary.h>
81 #include <CmdSync.h>
82 #include <CmdTags.h>
83 #include <CmdTimesheet.h>
84 #include <CmdUDAs.h>
85 #include <CmdUndo.h>
86 #include <CmdUnique.h>
87 #include <CmdUrgency.h>
88 #include <CmdVersion.h>
89 
90 #include <Context.h>
91 #include <ColProject.h>
92 #include <ColDue.h>
93 
94 ////////////////////////////////////////////////////////////////////////////////
factory(std::map<std::string,Command * > & all)95 void Command::factory (std::map <std::string, Command*>& all)
96 {
97   Command* c;
98 
99   c = new CmdAdd ();                all[c->keyword ()] = c;
100   c = new CmdAnnotate ();           all[c->keyword ()] = c;
101   c = new CmdAppend ();             all[c->keyword ()] = c;
102   c = new CmdBurndownDaily ();      all[c->keyword ()] = c;
103   c = new CmdBurndownMonthly ();    all[c->keyword ()] = c;
104   c = new CmdBurndownWeekly ();     all[c->keyword ()] = c;
105   c = new CmdCalc ();               all[c->keyword ()] = c;
106   c = new CmdCalendar ();           all[c->keyword ()] = c;
107   c = new CmdColor ();              all[c->keyword ()] = c;
108   c = new CmdColumns ();            all[c->keyword ()] = c;
109   c = new CmdCommands ();           all[c->keyword ()] = c;
110   c = new CmdCompletionAliases ();  all[c->keyword ()] = c;
111   c = new CmdCompletionColumns ();  all[c->keyword ()] = c;
112   c = new CmdCompletionCommands (); all[c->keyword ()] = c;
113   c = new CmdCompletionConfig ();   all[c->keyword ()] = c;
114   c = new CmdCompletionContext ();  all[c->keyword ()] = c;
115   c = new CmdCompletionIds ();      all[c->keyword ()] = c;
116   c = new CmdCompletionUDAs ();     all[c->keyword ()] = c;
117   c = new CmdCompletionUuids ();    all[c->keyword ()] = c;
118   c = new CmdCompletionProjects (); all[c->keyword ()] = c;
119   c = new CmdCompletionTags ();     all[c->keyword ()] = c;
120   c = new CmdCompletionVersion ();  all[c->keyword ()] = c;
121   c = new CmdConfig ();             all[c->keyword ()] = c;
122   c = new CmdContext ();            all[c->keyword ()] = c;
123   c = new CmdCount ();              all[c->keyword ()] = c;
124   c = new CmdDelete ();             all[c->keyword ()] = c;
125   c = new CmdDenotate ();           all[c->keyword ()] = c;
126   c = new CmdDiagnostics ();        all[c->keyword ()] = c;
127   c = new CmdDone ();               all[c->keyword ()] = c;
128   c = new CmdDuplicate ();          all[c->keyword ()] = c;
129   c = new CmdEdit ();               all[c->keyword ()] = c;
130 #ifdef HAVE_EXECUTE
131   c = new CmdExec ();               all[c->keyword ()] = c;
132 #endif
133   c = new CmdExport ();             all[c->keyword ()] = c;
134   c = new CmdGet ();                all[c->keyword ()] = c;
135   c = new CmdGHistoryDaily ();      all[c->keyword ()] = c;
136   c = new CmdGHistoryWeekly ();     all[c->keyword ()] = c;
137   c = new CmdGHistoryMonthly ();    all[c->keyword ()] = c;
138   c = new CmdGHistoryAnnual ();     all[c->keyword ()] = c;
139   c = new CmdHelp ();               all[c->keyword ()] = c;
140   c = new CmdHistoryDaily ();       all[c->keyword ()] = c;
141   c = new CmdHistoryWeekly ();      all[c->keyword ()] = c;
142   c = new CmdHistoryMonthly ();     all[c->keyword ()] = c;
143   c = new CmdHistoryAnnual ();      all[c->keyword ()] = c;
144   c = new CmdIDs ();                all[c->keyword ()] = c;
145   c = new CmdImport ();             all[c->keyword ()] = c;
146   c = new CmdInfo ();               all[c->keyword ()] = c;
147   c = new CmdLog ();                all[c->keyword ()] = c;
148   c = new CmdLogo ();               all[c->keyword ()] = c;
149   c = new CmdModify ();             all[c->keyword ()] = c;
150   c = new CmdNews ();               all[c->keyword ()] = c;
151   c = new CmdPrepend ();            all[c->keyword ()] = c;
152   c = new CmdProjects ();           all[c->keyword ()] = c;
153   c = new CmdPurge ();              all[c->keyword ()] = c;
154   c = new CmdReports ();            all[c->keyword ()] = c;
155   c = new CmdShow ();               all[c->keyword ()] = c;
156   c = new CmdShowRaw ();            all[c->keyword ()] = c;
157   c = new CmdStart ();              all[c->keyword ()] = c;
158   c = new CmdStats ();              all[c->keyword ()] = c;
159   c = new CmdStop ();               all[c->keyword ()] = c;
160   c = new CmdSummary ();            all[c->keyword ()] = c;
161   c = new CmdSync ();               all[c->keyword ()] = c;
162   c = new CmdTags ();               all[c->keyword ()] = c;
163   c = new CmdTimesheet ();          all[c->keyword ()] = c;
164   c = new CmdUDAs ();               all[c->keyword ()] = c;
165   c = new CmdUndo ();               all[c->keyword ()] = c;
166   c = new CmdUnique ();             all[c->keyword ()] = c;
167   c = new CmdUrgency ();            all[c->keyword ()] = c;
168   c = new CmdUUIDs ();              all[c->keyword ()] = c;
169   c = new CmdVersion ();            all[c->keyword ()] = c;
170   c = new CmdZshAttributes ();      all[c->keyword ()] = c;
171   c = new CmdZshCommands ();        all[c->keyword ()] = c;
172   c = new CmdZshCompletionIds ();   all[c->keyword ()] = c;
173   c = new CmdZshCompletionUuids (); all[c->keyword ()] = c;
174 
175   // Instantiate a command object for each custom report.
176   std::vector <std::string> reports;
177   for (auto &i : Context::getContext ().config)
178   {
179     if (i.first.substr (0, 7) == "report.")
180     {
181       std::string report = i.first.substr (7);
182       auto columns = report.find (".columns");
183       if (columns != std::string::npos)
184         reports.push_back (report.substr (0, columns));
185     }
186   }
187 
188   for (auto &report : reports)
189   {
190     // Make sure a custom report does not clash with a built-in command.
191     if (all.find (report) != all.end ())
192       throw format ("Custom report '{1}' conflicts with built-in task command.", report);
193 
194     c = new CmdCustom (
195               report,
196               "task <filter> " + report,
197               Context::getContext ().config.get ("report." + report + ".description"));
198 
199     all[c->keyword ()] = c;
200   }
201 }
202 
203 ////////////////////////////////////////////////////////////////////////////////
204 const std::map <Command::Category, std::string> Command::categoryNames =
205 {
206   // These strings are intentionally not l10n'd: they are used as identifiers.
207    {Command::Category::unassigned,   "unassigned"} // should never happen
208   ,{Command::Category::metadata,     "metadata"}
209   ,{Command::Category::report,       "report"}
210   ,{Command::Category::operation,    "operation"}
211   ,{Command::Category::context,      "context"}
212   ,{Command::Category::graphs,       "graphs"   }
213   ,{Command::Category::config,       "config"   }
214   ,{Command::Category::migration,    "migration"}
215   ,{Command::Category::misc,         "misc"     }
216   ,{Command::Category::internal,     "internal"}
217   ,{Command::Category::UNDOCUMENTED, "undocumented"}
218 };
219 
220 ////////////////////////////////////////////////////////////////////////////////
Command()221 Command::Command ()
222 : _keyword ("")
223 , _usage ("")
224 , _description ("")
225 , _read_only (true)
226 , _displays_id (true)
227 , _needs_confirm (false)
228 , _needs_gc (true)
229 , _uses_context (false)
230 , _accepts_filter (false)
231 , _accepts_modifications (false)
232 , _accepts_miscellaneous (false)
233 , _category(Category::unassigned)
234 , _permission_quit (false)
235 , _permission_all (false)
236 , _first_iteration (true)
237 {
238 }
239 
240 ////////////////////////////////////////////////////////////////////////////////
keyword() const241 std::string Command::keyword () const
242 {
243   return _keyword;
244 }
245 
246 ////////////////////////////////////////////////////////////////////////////////
usage() const247 std::string Command::usage () const
248 {
249   return _usage;
250 }
251 
252 ////////////////////////////////////////////////////////////////////////////////
description() const253 std::string Command::description () const
254 {
255   return _description;
256 }
257 
258 ////////////////////////////////////////////////////////////////////////////////
read_only() const259 bool Command::read_only () const
260 {
261   return _read_only;
262 }
263 
264 ////////////////////////////////////////////////////////////////////////////////
displays_id() const265 bool Command::displays_id () const
266 {
267   return _displays_id;
268 }
269 
270 ////////////////////////////////////////////////////////////////////////////////
needs_gc() const271 bool Command::needs_gc () const
272 {
273   return _needs_gc;
274 }
275 
276 ////////////////////////////////////////////////////////////////////////////////
uses_context() const277 bool Command::uses_context () const
278 {
279   return _uses_context;
280 }
281 
282 ////////////////////////////////////////////////////////////////////////////////
accepts_filter() const283 bool Command::accepts_filter () const
284 {
285   return _accepts_filter;
286 }
287 
288 ////////////////////////////////////////////////////////////////////////////////
accepts_modifications() const289 bool Command::accepts_modifications () const
290 {
291   return _accepts_modifications;
292 }
293 
294 ////////////////////////////////////////////////////////////////////////////////
accepts_miscellaneous() const295 bool Command::accepts_miscellaneous () const
296 {
297   return _accepts_miscellaneous;
298 }
299 
300 ////////////////////////////////////////////////////////////////////////////////
category() const301 Command::Category Command::category () const
302 {
303   return _category;
304 }
305 
306 ////////////////////////////////////////////////////////////////////////////////
307 // Returns true or false indicating whether to proceed with a write command, on
308 // a per-task basis, after (potentially) asking for permission.
309 //
310 // Factors:
311 //   filtered.size ()
312 //   rc.bulk
313 //   rc.confirmation
314 //   this->_read_only
permission(const std::string & question,unsigned int quantity)315 bool Command::permission (
316   const std::string& question,
317   unsigned int quantity)
318 {
319   // Read-only commands do not need to seek permission.  Write commands are
320   // granted permission automatically if the 'all' selection was made in an
321   // earlier call.  Or if the 'all' option has already been made.
322   if (_read_only ||
323       _permission_all)
324     return true;
325 
326   // If the 'quit' selection has already been made.
327   if (_permission_quit)
328     return false;
329 
330   // What remains are write commands that have not yet selected 'all' or 'quit'.
331   // Describe the task.
332   bool         confirmation = Context::getContext ().config.getBoolean ("confirmation");
333   unsigned int bulk         = Context::getContext ().config.getInteger ("bulk");
334 
335   // Quantity 1 modifications have optional confirmation, and only (y/n).
336   if (quantity == 1)
337   {
338     if (!_needs_confirm ||
339         !confirmation)
340       return true;
341 
342     bool answer = confirm (question);
343     return answer;
344   }
345 
346   // 1 < Quantity < bulk modifications have optional confirmation, in the (y/n/a/q)
347   // style. Bulk = 0 denotes infinite bulk.
348   if ((bulk == 0 || quantity < bulk) && (!_needs_confirm || !confirmation))
349     return true;
350 
351   if (Context::getContext ().verbose ("blank") && !_first_iteration)
352     std::cout << '\n';
353   int answer = confirm4 (question);
354   _first_iteration = false;
355   switch (answer)
356   {
357   case 1:                           return true;     // yes
358   case 2: _permission_all  = true;  return true;     // all
359   case 3: _permission_quit = true;  return false;    // quit
360   }
361 
362   return false;  // This line keeps the compiler happy.
363 }
364 
365 ////////////////////////////////////////////////////////////////////////////////
366