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