source: trunk/lib/Trac.cc

Last change on this file was 1635, checked in by Peter Johansson, 2 months ago

updating copyright statements using 0.11pre (using character-based assessment rather than line-based).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 15.7 KB
Line 
1// $Id: Trac.cc 1635 2023-03-30 04:16:57Z peter $
2
3/*
4  Copyright (C) 2007, 2008 Jari Häkkinen, Peter Johansson
5  Copyright (C) 2009, 2023 Peter Johansson
6
7  This file is part of svndigest, http://dev.thep.lu.se/svndigest
8
9  svndigest is free software; you can redistribute it and/or modify it
10  under the terms of the GNU General Public License as published by
11  the Free Software Foundation; either version 3 of the License, or
12  (at your option) any later version.
13
14  svndigest is distributed in the hope that it will be useful, but
15  WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  General Public License for more details.
18
19  You should have received a copy of the GNU General Public License
20  along with svndigest. If not, see <http://www.gnu.org/licenses/>.
21*/
22
23#include <config.h>
24
25#include "Trac.h"
26
27#include "Configuration.h"
28#include "Functor.h"
29#include "HtmlStream.h"
30#include "html_utility.h"
31#include "utility.h"
32
33namespace theplu{
34namespace svndigest{
35
36  Trac::Trac(HtmlStream& hs)
37    : hs_(hs)
38  {}
39
40  std::string Trac::anchor_text(std::string::const_iterator first,
41                                std::string::const_iterator last,
42                                std::string::const_iterator last_trunc)
43  {
44    if (last>last_trunc) { // truncate
45      std::string str(first, last_trunc);
46      str+="...";
47      return str;
48    }
49    return std::string(first, last);
50  }
51
52
53  bool Trac::changeset(const std::string::const_iterator& first,
54                       std::string::const_iterator& iter,
55                       const std::string::const_iterator& last,
56                       const std::string::const_iterator& last_trunc)
57  {
58    if (changeset1(first, iter, last, last_trunc))
59      return true;
60    if (changeset2(iter, last, last_trunc))
61      return true;
62    if (changeset3(iter, last, last_trunc))
63      return true;
64    return false;
65  }
66
67
68  bool Trac::changeset1(const std::string::const_iterator& first,
69                        std::string::const_iterator& iter,
70                        const std::string::const_iterator& last,
71                        const std::string::const_iterator& last_trunc)
72  {
73    if (iter==last)
74      return false;
75    if (*iter != 'r')
76      return false;
77    if (iter!=first && isalnum(*(iter-1)))
78      return false;
79    const std::string::const_iterator iter_orig(iter);
80    ++iter;
81    std::string rev = match(iter, last, Digit());
82    if (rev.empty() || (iter!=last && (std::isalnum(*iter) || *iter==':')) ){
83      iter = iter_orig;
84      return false;
85    }
86    std::string href(Configuration::instance().trac_root()+"changeset/"+rev);
87    hs_.stream() << anchor(href, anchor_text(iter_orig, iter, last_trunc));
88    return true;
89  }
90
91
92  bool Trac::changeset2(std::string::const_iterator& first,
93                        const std::string::const_iterator& last,
94                        const std::string::const_iterator& last_trunc)
95  {
96    if (first==last)
97      return false;
98    if (*first != '[')
99      return false;
100    const std::string::const_iterator first_orig(first);
101    ++first;
102    std::string rev = match(first, last, Digit());
103    if (rev.empty() || first==last || *first!=']'){
104      first = first_orig;
105      return false;
106    }
107    ++first;
108    std::string href(Configuration::instance().trac_root()+"changeset/"+rev);
109    hs_.stream() << anchor(href, anchor_text(first_orig,first, last_trunc));
110    return true;
111  }
112
113
114  bool Trac::changeset3(std::string::const_iterator& first,
115                        const std::string::const_iterator& last,
116                        const std::string::const_iterator& last_trunc)
117  {
118    if (first==last)
119      return false;
120    const std::string::const_iterator first_orig(first);
121    if (match(first, last, std::string("changeset:")).empty()){
122      first = first_orig;
123      return false;
124    }
125    std::string rev = match(first, last, Digit());
126    if (rev.empty()){
127      first = first_orig;
128      return false;
129    }
130    std::string href(Configuration::instance().trac_root()+"changeset/"+rev);
131    hs_.stream() << anchor(href, anchor_text(first_orig,first, last_trunc));
132    return true;
133  }
134
135
136  bool Trac::comment(std::string::const_iterator& first,
137                     const std::string::const_iterator& last,
138                     const std::string::const_iterator& last_trunc)
139  {
140    if (first==last)
141      return false;
142
143    const std::string::const_iterator first_orig(first);
144    if (match(first, last, std::string("comment:ticket:")).empty()){
145      first = first_orig;
146      return false;
147    }
148
149    std::string ticket = match(first, last, AlNum());
150    if (ticket.empty() || first == last || *first != ':') {
151      first = first_orig;
152      return false;
153    }
154    ++first;
155    std::string comment = match(first, last, Digit());
156    if (comment.empty() ) {
157      first = first_orig;
158      return false;
159    }
160    std::string href(Configuration::instance().trac_root()+"ticket/"+ticket+
161                     "#comment:"+comment);
162    hs_.stream() << anchor(href, anchor_text(first_orig,first, last_trunc));
163    return true;
164  }
165
166
167  bool Trac::diff(std::string::const_iterator& first,
168                  const std::string::const_iterator& last,
169                  const std::string::const_iterator& last_trunc)
170  {
171    if (diff3(first, last, last_trunc))
172      return true;
173    if (diff1(first, last, last_trunc))
174      return true;
175    if (diff2(first, last, last_trunc))
176      return true;
177    return false;
178  }
179
180
181  bool Trac::diff1(std::string::const_iterator& first,
182                   const std::string::const_iterator& last,
183                   const std::string::const_iterator& last_trunc)
184  {
185    if (first==last)
186      return false;
187
188    const std::string::const_iterator first_orig(first);
189    if (match(first, last, std::string("diff:")).empty()){
190      first = first_orig;
191      return false;
192    }
193    std::string node = match(first, last, notChar('@'));
194    if (first==last){
195      first = first_orig;
196      return false;
197    }
198    ++first;
199    std::string old_rev = match(first, last, AlNum());
200    if (old_rev.empty() || first == last || *first != ':') {
201      first = first_orig;
202      return false;
203    }
204    ++first;
205    std::string new_rev = match(first, last, AlNum());
206    if (new_rev.empty() ) {
207      first = first_orig;
208      return false;
209    }
210    std::string href;
211    if (node.empty())
212      href = std::string(Configuration::instance().trac_root()+
213                         "changeset?new="+new_rev+"&amp;old="+old_rev);
214    else
215      href = std::string(Configuration::instance().trac_root()+
216                         "changeset?new="+new_rev+"&amp;new_path="+node+
217                         "&amp;old="+old_rev+"&amp;old_path="+node);
218    hs_.stream() << anchor(href, anchor_text(first_orig,first, last_trunc));
219    return true;
220  }
221
222
223  bool Trac::diff2(std::string::const_iterator& first,
224                   const std::string::const_iterator& last,
225                   const std::string::const_iterator& last_trunc)
226  {
227    if (first==last)
228      return false;
229
230    const std::string::const_iterator first_orig(first);
231    if (match(first, last, std::string("diff:")).empty()){
232      first = first_orig;
233      return false;
234    }
235    std::string old_path(match(first, last, not2Str("//", " ")));
236    std::string new_path;
237    if (first==last || *first==' ')
238      new_path = old_path;
239    else {
240      first += 2;
241      new_path = match(first, last, notChar(' '));
242    }
243    if (new_path.empty() || old_path.empty()){
244      first = first_orig;
245      return false;
246    }
247    std::string href(Configuration::instance().trac_root()+
248                     "changeset?new_path="+new_path+"&amp;old_path="+old_path);
249    hs_.stream() << anchor(href, anchor_text(first_orig,first, last_trunc));
250    return true;
251  }
252
253
254  bool Trac::diff3(std::string::const_iterator& first,
255                   const std::string::const_iterator& last,
256                   const std::string::const_iterator& last_trunc)
257  {
258    if (first==last)
259      return false;
260
261    const std::string::const_iterator first_orig(first);
262    if (match(first, last, std::string("diff:")).empty()){
263      first = first_orig;
264      return false;
265    }
266    std::string old_path(match(first, last, not2Char('@', ' ')));
267    if (*first!='@'){
268      first = first_orig;
269      return false;
270    }
271    ++first;
272    std::string old_rev(match(first, last, AlNum()));
273    if (match(first, last, std::string("//")).empty()) {
274      first = first_orig;
275      return false;
276    }
277    std::string new_path = match(first, last, not2Char('@', ' '));
278    if (*first!='@'){
279      first = first_orig;
280      return false;
281    }
282    ++first;
283    std::string new_rev(match(first, last, AlNum()));
284    if (new_rev.empty()){
285      first = first_orig;
286      return false;
287    }
288
289    std::string href(Configuration::instance().trac_root()+
290                     "changeset?new="+new_rev+"&amp;new_path="+new_path+
291                     "&amp;old="+old_rev+"&amp;old_path="+old_path);
292    hs_.stream() << anchor(href, anchor_text(first_orig,first, last_trunc));
293    return true;
294  }
295
296
297  bool Trac::log(const std::string::const_iterator& first,
298                 std::string::const_iterator& iter,
299                 const std::string::const_iterator& last,
300                 const std::string::const_iterator& last_trunc)
301  {
302    if (log1(first, iter, last, last_trunc))
303      return true;
304    if (log2(iter, last, last_trunc))
305      return true;
306    if (log3(iter, last, last_trunc))
307      return true;
308    return false;
309  }
310
311
312  bool Trac::log1(const std::string::const_iterator& first,
313                  std::string::const_iterator& iter,
314                  const std::string::const_iterator& last,
315                  const std::string::const_iterator& last_trunc)
316  {
317    if (iter==last)
318      return false;
319
320    const std::string::const_iterator iter_orig(iter);
321    if (*iter != 'r')
322      return false;
323    if (iter!=first && isalnum(*(iter-1)))
324      return false;
325    ++iter;
326
327    std::string stop_rev = match(iter, last, Digit());
328    if (stop_rev.empty() || iter == last || *iter != ':') {
329      iter = iter_orig;
330      return false;
331    }
332    ++iter;
333    std::string rev = match(iter, last, Digit());
334    if (rev.empty() || (iter!=last && std::isalnum(*iter) ) ){
335      iter = iter_orig;
336      return false;
337    }
338    std::string href(Configuration::instance().trac_root()+"log/?rev="+
339                     rev+"&amp;stop_rev="+stop_rev);
340    hs_.stream() << anchor(href, anchor_text(iter_orig,iter, last_trunc));
341    return true;
342  }
343
344
345  bool Trac::log2(std::string::const_iterator& first,
346                  const std::string::const_iterator& last,
347                  const std::string::const_iterator& last_trunc)
348  {
349    if (first==last)
350      return false;
351
352    const std::string::const_iterator first_orig(first);
353    if (*first != '[')
354      return false;
355    ++first;
356
357    std::string stop_rev = match(first, last, AlNum());
358    if (stop_rev.empty() || first == last || *first != ':') {
359      first = first_orig;
360      return false;
361    }
362    ++first;
363    std::string rev = match(first, last, AlNum());
364    if (rev.empty() || first == last || *first != ']') {
365      first = first_orig;
366      return false;
367    }
368    ++first; // eating ']'
369    std::string href(Configuration::instance().trac_root()+"log/?rev="+
370                     rev+"&amp;stop_rev="+stop_rev);
371    hs_.stream() << anchor(href, anchor_text(first_orig,first, last_trunc));
372    return true;
373  }
374
375
376  bool Trac::log3(std::string::const_iterator& first,
377                  const std::string::const_iterator& last,
378                  const std::string::const_iterator& last_trunc)
379  {
380    if (first==last)
381      return false;
382
383    const std::string::const_iterator first_orig(first);
384
385    const std::string log_str("log:");
386    if (!match_begin(first, last, log_str)) {
387      first = first_orig;
388      return false;
389    }
390    first += log_str.size();
391    std::string node = match(first, last, not2Char('#', '@'));
392    ++first;
393    std::string stop_rev = match(first, last, AlNum());
394    if (stop_rev.empty() || first == last || *first != ':') {
395      first = first_orig;
396      return false;
397    }
398    ++first;
399    std::string rev = match(first, last, AlNum());
400    if (rev.empty() ) {
401      first = first_orig;
402      return false;
403    }
404    std::string href;
405    if (!node.empty() && node[0]=='/')
406      href = std::string(Configuration::instance().trac_root()+"log"+node+
407                         "?rev="+rev+"&amp;stop_rev="+stop_rev);
408    else
409      href = std::string(Configuration::instance().trac_root()+"log/"+node+
410                         "?rev="+rev+"&amp;stop_rev="+stop_rev);
411
412    hs_.stream() << anchor(href, anchor_text(first_orig,first, last_trunc));
413    return true;
414  }
415
416
417  bool Trac::milestone(std::string::const_iterator& first,
418                       const std::string::const_iterator& last,
419                       const std::string::const_iterator& last_trunc)
420  {
421    if (first==last)
422      return false;
423
424    const std::string::const_iterator first_orig(first);
425
426    if (match(first, last, std::string("milestone:")).empty()){
427      first = first_orig;
428      return false;
429    }
430
431    const std::string::const_iterator milestone_begin(first);
432
433    // find the last alphanumerical char before next space (or last)
434    for (std::string::const_iterator i(first); i!=last && *i!=' '; ++i)
435      if (isalnum(*i))
436        first = i+1;
437
438    std::string milestone(milestone_begin, first);
439    if (milestone.empty()){
440      first = first_orig;
441      return false;
442    }
443
444    const Configuration& conf = Configuration::instance();
445    std::string href(conf.trac_root()+"milestone/"+milestone);
446    hs_.stream() << anchor(href, anchor_text(first_orig,first, last_trunc));
447    return true;
448  }
449
450
451  void Trac::print(std::string str, size_t width)
452  {
453    std::string::const_iterator first(str.begin());
454    std::string::const_iterator last_trunc(str.end());
455    if (width<=str.size()) // truncate
456      last_trunc = first+width;
457    while (first<last_trunc) {
458      if (log(str.begin(), first, str.end(), last_trunc))
459        continue;
460      if (comment(first, str.end(), last_trunc))
461        continue;
462      if (ticket(first, str.end(), last_trunc))
463        continue;
464      if (changeset(str.begin(), first, str.end(), last_trunc))
465        continue;
466      if (diff(first, str.end(), last_trunc))
467        continue;
468      if (milestone(first, str.end(), last_trunc))
469        continue;
470      if (source(first, str.end(), last_trunc))
471        continue;
472      hs_ << *first;
473      ++first;
474    }
475    if (last_trunc!=str.end())
476      hs_ << "...";
477  }
478
479
480  bool Trac::source(std::string::const_iterator& first,
481                    const std::string::const_iterator& last,
482                    const std::string::const_iterator& last_trunc)
483  {
484    if (first==last)
485      return false;
486
487    const std::string::const_iterator first_orig(first);
488
489    if (match(first, last, std::string("source:")).empty()){
490      first = first_orig;
491      return false;
492    }
493
494    std::string node;
495    std::string rev;
496    std::string line;
497    const std::string::const_iterator node_begin(first);
498
499    node = match(first, last, not2Char('@', ' '));
500    if (!node.empty() && node[0]=='/')
501      node = node.substr(1, node.size()-1);
502
503    if (*first == '@'){
504      ++first;
505      rev = match(first, last, AlNum());
506      if (*first == '#') {
507        ++first;
508        line = match(first, last, notChar(' '));
509      }
510    }
511
512    const Configuration& conf = Configuration::instance();
513    std::string href(conf.trac_root()+"browser/"+node);
514    if (!rev.empty()) {
515      href += "?rev="+rev;
516      if (!line.empty())
517        href += "#"+line;
518    }
519    hs_.stream() << anchor(href, anchor_text(first_orig,first, last_trunc));
520    return true;
521  }
522
523
524  bool Trac::ticket(std::string::const_iterator& first,
525                    const std::string::const_iterator& last,
526                    const std::string::const_iterator& last_trunc)
527  {
528    if (ticket1(first, last, last_trunc))
529      return true;
530    if (ticket2(first, last, last_trunc))
531      return true;
532    return false;
533  }
534
535
536  bool Trac::ticket1(std::string::const_iterator& first,
537                     const std::string::const_iterator& last,
538                     const std::string::const_iterator& last_trunc)
539  {
540    if (first==last)
541      return false;
542
543    const std::string::const_iterator first_orig(first);
544    if (*first != '#')
545      return false;
546    ++first;
547    std::string ticket = match(first, last, Digit());
548
549    if (ticket.empty()) {
550      first = first_orig;
551      return false;
552    }
553
554    const Configuration& conf = Configuration::instance();
555    hs_.stream() << anchor(conf.trac_root()+"ticket/"+ticket,
556                           anchor_text(first_orig,first, last_trunc));
557    return true;
558  }
559
560
561  bool Trac::ticket2(std::string::const_iterator& first,
562                     const std::string::const_iterator& last,
563                     const std::string::const_iterator& last_trunc)
564  {
565    if (first==last)
566      return false;
567
568    const std::string::const_iterator first_orig(first);
569
570    if (match(first, last, std::string("ticket:")).empty()){
571      first = first_orig;
572      return false;
573    }
574    std::string ticket = match(first, last, Digit());
575
576    const Configuration& conf = Configuration::instance();
577    std::string href(conf.trac_root()+"ticket/"+ticket);
578    hs_.stream() << anchor(href, anchor_text(first_orig,first, last_trunc));
579    return true;
580  }
581
582
583}} // end of namespace svndigest and namespace theplu
Note: See TracBrowser for help on using the repository browser.