source: branches/0.6-stable/lib/Trac.cc @ 761

Last change on this file since 761 was 761, checked in by Peter Johansson, 14 years ago

fixes #353

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