00001
00002
00003
00004
00005
00006
00007
00008
00045 #include <apr_pools.h>
00046 #include <subversion-1/svn_ra.h>
00047 #include <subversion-1/svn_error.h>
00048 #include <subversion-1/svn_string.h>
00049 #include <subversion-1/svn_time.h>
00050
00051 #include <sys/types.h>
00052 #include <unistd.h>
00053 #include <ctype.h>
00054 #include <time.h>
00055 #include <fcntl.h>
00056
00057
00058 #include "global.h"
00059 #include "est_ops.h"
00060 #include "waa.h"
00061 #include "url.h"
00062 #include "options.h"
00063 #include "log.h"
00064 #include "update.h"
00065 #include "racallback.h"
00066 #include "helper.h"
00067
00068
00069 #define MAX_LOG_OUTPUT_LINE (1024)
00070
00071
00072
00073
00074 int log___divider(FILE *output, char *color_after)
00075 {
00076 return -1 == fprintf(output, "%s"
00077 "------------------------------------"
00078 "------------------------------------\n"
00079 "%s",
00080 (opt__get_int(OPT__LOG_OUTPUT) & LOG__OPT_COLOR) ? ANSI__BLUE : "",
00081 (opt__get_int(OPT__LOG_OUTPUT) & LOG__OPT_COLOR) ? color_after : "")
00082 ? errno : 0;
00083 }
00084
00085
00092 svn_error_t *log__receiver(void *baton,
00093 apr_hash_t *changed_paths,
00094 svn_revnum_t revision,
00095 const char *author,
00096 const char *date,
00097 const char *message,
00098 apr_pool_t *pool)
00099 {
00100 static const char indent[]=" ";
00101 int status;
00102 int lines, len, cur, sol, i;
00103 const char *ccp;
00104 char *auth, *dat, *mess;
00105 FILE *output=baton;
00106
00107 apr_hash_index_t *hi;
00108 void const *name;
00109 apr_ssize_t namelen;
00110 char *local_name;
00111 int fn_count;
00112 char **filenames;
00113
00114
00115 DEBUGP("got log for %llu", (t_ull)revision);
00116
00117
00118 if (!message) message="(No message.)";
00119
00120
00121 ccp=message;
00122 lines=1;
00123 while ( (ccp=strchr(ccp, '\n')) ) lines++, ccp++;
00124 len = ccp+strlen(message) - message;
00125
00126 DEBUGP("got %d lines", lines);
00127
00128
00129 STOPIF( hlp__utf82local(author, &auth, -1), NULL);
00130 STOPIF( hlp__utf82local(date, &dat, -1), NULL);
00131
00132
00133 STOPIF( log___divider(output, ANSI__GREEN), NULL);
00134
00135
00136 STOPIF_CODE_EPIPE( fprintf(output,
00137 "r%llu | %s | %s | %d line%s\n"
00138 "%s",
00139 (t_ull)revision, auth, dat, lines,
00140 lines == 1 ? "" : "s",
00141 (opt__get_int(OPT__LOG_OUTPUT) & LOG__OPT_COLOR) ? ANSI__NORMAL : ""),
00142 NULL);
00143
00144
00145 if (changed_paths)
00146 {
00147 STOPIF_CODE_EPIPE( fputs("Changed paths:\n", output), NULL);
00148
00149
00150 fn_count=apr_hash_count(changed_paths);
00151 STOPIF( hlp__alloc( &filenames, sizeof(*filenames)*fn_count), NULL);
00152
00153 i=0;
00154 hi=apr_hash_first(pool, changed_paths);
00155 while (hi)
00156 {
00157 apr_hash_this(hi, &name, &namelen, NULL);
00158
00159 STOPIF( hlp__utf82local( name, &local_name, namelen), NULL);
00160
00161 BUG_ON(i>=fn_count,
00162 "too many filenames in hash - count was %d", fn_count);
00163 STOPIF( hlp__strdup(filenames+i, local_name),NULL);
00164 i++;
00165
00166 hi = apr_hash_next(hi);
00167 }
00168
00169 BUG_ON(i!=fn_count,
00170 "Wrong number of filenames in hash - count was %d", fn_count);
00171
00172 qsort(filenames, fn_count, sizeof(*filenames),
00173 hlp__compare_string_pointers);
00174 for(i=0; i<fn_count; i++)
00175 STOPIF_CODE_EPIPE( fprintf(output, " %s\n", filenames[i]), NULL);
00176 }
00177
00178 STOPIF_CODE_EPIPE( fputs("\n", output), NULL);
00179
00180
00181
00182
00183 len=strlen(message);
00184 sol=1;
00185 while(len >0)
00186 {
00187 DEBUGP("todo %d bytes, \\x%02X; sol=%d", len, *message & 0xff, sol);
00188 if (sol && (opt__get_int(OPT__LOG_OUTPUT) & LOG__OPT_INDENT))
00189 STOPIF_CODE_ERR( fputs(indent, output)==EOF, errno, NULL);
00190
00191 cur= len <= MAX_LOG_OUTPUT_LINE ? len : MAX_LOG_OUTPUT_LINE;
00192 ccp=memchr(message, '\n', cur);
00193 if (ccp)
00194 cur=ccp-message+1;
00195 else if (cur == MAX_LOG_OUTPUT_LINE)
00196 {
00197
00198
00199
00200
00201
00202
00203
00204 for(i=8; i>=0 && cur > 0; i--)
00205 {
00206 cur--;
00207
00208 if ((message[cur] & 0x80) == 0 ||
00209
00210 (message[cur] & 0xc0) == 0xc0)
00211 break;
00212 }
00213 STOPIF_CODE_ERR(i < 0, EILSEQ,
00214 "Invalid UTF8-sequence in log message for revision %llu found",
00215 (t_ull) revision);
00216
00217
00218 }
00219 DEBUGP("log output: %d bytes", cur);
00220
00221 STOPIF( hlp__utf82local(message, &mess, cur), NULL);
00222 STOPIF_CODE_EPIPE( fputs(mess, output), NULL);
00223
00224 message+=cur;
00225 len-=cur;
00226
00227
00228
00229 sol= ccp!=NULL;
00230 }
00231
00232 STOPIF_CODE_EPIPE( putc('\n', output), NULL);
00233
00234 ex:
00235 RETURN_SVNERR(status);
00236 }
00237
00238
00239
00243 int log__work(struct estat *root, int argc, char *argv[])
00244 {
00245 struct estat *sts;
00246 int status;
00247 svn_error_t *status_svn;
00248 char *path;
00249 apr_array_header_t *paths;
00250 int limit;
00251 char **normalized;
00252 FILE *output=stdout;
00253
00254
00255 status_svn=NULL;
00256 STOPIF_CODE_ERR(argc>1, EINVAL,
00257 "!This command takes (currently) at most a single path.");
00258
00259
00260 if (!isatty(STDOUT_FILENO))
00261 opt__set_int( OPT__LOG_OUTPUT, PRIO_PRE_CMDLINE,
00262 opt__get_int( OPT__LOG_OUTPUT) & ~LOG__OPT_COLOR);
00263 DEBUGP("options bits are %d", opt__get_int(OPT__LOG_OUTPUT));
00264
00265 STOPIF( waa__find_common_base( argc, argv, &normalized), NULL);
00266 STOPIF( url__load_nonempty_list(NULL, 0), NULL);
00267 STOPIF( waa__input_tree(root, NULL, NULL), NULL);
00268
00269 if (argc)
00270 {
00271 STOPIF( ops__traverse(root, normalized[0], 0, 0, &sts),
00272 "!The entry \"%s\" cannot be found.", normalized[0]);
00273 }
00274 else
00275 {
00276 sts=root;
00277 argc=1;
00278 }
00279
00280 current_url=NULL;
00281 if (url__parm_list_used)
00282 {
00283 STOPIF_CODE_ERR(url__parm_list_used>1, EINVAL,
00284 "!Only a single URL can be given.");
00285 STOPIF( url__find_by_name(url__parm_list[0], ¤t_url),
00286 "!No URL with name \"%s\" found", url__parm_list[0]);
00287 }
00288 else
00289 {
00290 if (sts->url)
00291 current_url=sts->url;
00292 else
00293 {
00294 STOPIF_CODE_ERR(urllist_count>1, EINVAL,
00295 "!The given entry has no URL associated yet.");
00296 DEBUGP("Taking default URL.");
00297 }
00298 }
00299
00300 if (!current_url)
00301 current_url=urllist[0];
00302
00303
00304 DEBUGP("doing URL %s", current_url->url);
00305 STOPIF( url__open_session(NULL, NULL), NULL);
00306
00307
00308 if (argc)
00309 {
00310 paths=apr_array_make(global_pool, argc, sizeof(char*));
00311
00312 STOPIF( ops__build_path(&path, sts), NULL);
00313 *(char **)apr_array_push(paths) = path+2;
00314 }
00315 else
00316 paths=NULL;
00317
00318 DEBUGP("got %d: %s - %s",
00319 opt_target_revisions_given,
00320 hlp__rev_to_string(opt_target_revision),
00321 hlp__rev_to_string(opt_target_revision2));
00322
00323
00324
00325 STOPIF( url__canonical_rev(current_url, &opt_target_revision), NULL);
00326 STOPIF( url__canonical_rev(current_url, &opt_target_revision2), NULL);
00327
00328 switch (opt_target_revisions_given)
00329 {
00330 case 0:
00331 opt_target_revision=SVN_INVALID_REVNUM;
00332 opt_target_revision2=1;
00333
00334 STOPIF( url__canonical_rev(current_url, &opt_target_revision), NULL);
00335 opt__set_int(OPT__LOG_MAXREV, PRIO_DEFAULT, 100);
00336 break;
00337 case 1:
00338 opt_target_revision2 = 1;
00339 opt__set_int(OPT__LOG_MAXREV, PRIO_DEFAULT, 1);
00340 break;
00341 case 2:
00342 opt__set_int(OPT__LOG_MAXREV,
00343 PRIO_DEFAULT,
00344 abs(opt_target_revision-opt_target_revision2)+1);
00345 break;
00346 default:
00347 BUG("how many");
00348 }
00349 limit=opt__get_int(OPT__LOG_MAXREV);
00350
00351 DEBUGP("log limit at %d", limit);
00352
00353 status_svn=svn_ra_get_log(current_url->session, paths,
00354 opt_target_revision, opt_target_revision2,
00355 limit,
00356 opt__is_verbose() > 0,
00357 0,
00358 log__receiver, output, global_pool);
00359
00360 if (status_svn)
00361 {
00362 if (status_svn->apr_err == -EPIPE)
00363 goto ex;
00364 STOPIF_SVNERR( status_svn, );
00365 }
00366
00367
00368 STOPIF( log___divider(output, ANSI__NORMAL), NULL);
00369
00370 ex:
00371 STOP_HANDLE_SVNERR(status_svn);
00372 ex2:
00373 return status;
00374 }
00375