--- cvs_direct.c.orig Wed May 25 20:39:40 2005 +++ cvs_direct.c Wed Nov 1 19:05:43 2017 @@ -3,6 +3,7 @@ * See COPYING file for license information */ +#include #include #include #include @@ -126,7 +127,7 @@ CvsServerCtx * open_cvs_server(char * p_root, int comp send_string(ctx, "Root %s\n", ctx->root); /* this is taken from 1.11.1p1 trace - but with Mbinary removed. we can't handle it (yet!) */ - send_string(ctx, "Valid-responses ok error Valid-requests Checked-in New-entry Checksum Copy-file Updated Created Update-existing Merged Patched Rcs-diff Mode Mod-time Removed Remove-entry Set-static-directory Clear-static-directory Set-sticky Clear-sticky Template Set-checkin-prog Set-update-prog Notified Module-expansion Wrapper-rcsOption M E F\n", ctx->root); + send_string(ctx, "Valid-responses ok error Valid-requests Checked-in New-entry Checksum Copy-file Updated Created Update-existing Merged Patched Rcs-diff Mode Mod-time Removed Remove-entry Set-static-directory Clear-static-directory Set-sticky Clear-sticky Template Set-checkin-prog Set-update-prog Notified Module-expansion Wrapper-rcsOption M E F LOGM\n", ctx->root); send_string(ctx, "valid-requests\n"); @@ -853,8 +854,19 @@ void cvs_diff(CvsServerCtx * ctx, * the compression state, and there was no way to resynchronize that state with * the parent process. We could use threads... */ -FILE * cvs_rlog_open(CvsServerCtx * ctx, const char * rep, const char * date_str) +CvsRlog * cvs_rlog_open(CvsServerCtx * ctx, const char * rep, const char * date_str) { + CvsRlog * cvsrlog; + + cvsrlog = calloc(1, sizeof(*cvsrlog)); + if (!cvsrlog) + { + debug(DEBUG_SYSERROR, "cvs_rlog_open: calloc failed"); + exit(1); + } + cvsrlog->csctx = ctx; + cvsrlog->flags = CRLOGF_CVSDIRECT; + /* note: use of the date_str is handled in a non-standard, cvsps specific way */ if (date_str && date_str[0]) { @@ -871,22 +883,39 @@ FILE * cvs_rlog_open(CvsServerCtx * ctx, const char * * FIXME: is it possible to create a 'fake' FILE * whose 'refill' * function is below? */ - return (FILE*)ctx; + return cvsrlog; } -char * cvs_rlog_fgets(char * buff, int buflen, CvsServerCtx * ctx) +char * cvs_rlog_fgets(char * buff, int buflen, CvsRlog * cvsrlog) { char lbuff[BUFSIZ]; - int len; + int n, len; - len = read_line(ctx, lbuff); + assert(cvsrlog->flags & CRLOGF_CVSDIRECT); + + len = read_line(cvsrlog->csctx, lbuff); debug(DEBUG_TCP, "cvs_direct: rlog: read %s", lbuff); - if (memcmp(lbuff, "M ", 2) == 0) + if (memcmp(lbuff, "M ", 2) == 0 || memcmp(lbuff, "LOGM ", 5) == 0) { - memcpy(buff, lbuff + 2, len - 2); - buff[len - 2 ] = '\n'; - buff[len - 1 ] = 0; + if ('L' == lbuff[0]) + { + n = 5; + CRLOG_SET_LOGM(cvsrlog); + } + else + { + n = 2; + CRLOG_CLR_LOGM(cvsrlog); + } + if (buflen < len - n) { + fprintf(stderr, "****WARNING**** rlog buffer len(=%d) > " + "buflen(=%d)\n", len - n, buflen); + len = buflen; + } + memcpy(buff, lbuff + n, len - n); + buff[len - n ] = '\n'; + buff[len - n + 1 ] = 0; } else if (memcmp(lbuff, "E ", 2) == 0) { @@ -901,8 +930,10 @@ char * cvs_rlog_fgets(char * buff, int buflen, CvsServ return buff; } -void cvs_rlog_close(CvsServerCtx * ctx) +void cvs_rlog_close(CvsRlog * cvsrlog) { + assert(cvsrlog->flags & CRLOGF_CVSDIRECT); + free(cvsrlog); } void cvs_version(CvsServerCtx * ctx, char * client_version, char * server_version) --- cvs_direct.h.orig Wed May 25 20:39:40 2005 +++ cvs_direct.h Wed Nov 1 13:42:27 2017 @@ -11,14 +11,37 @@ typedef struct _CvsServerCtx CvsServerCtx; #endif +struct _CvsRlog +{ + unsigned int flags; + #define CRLOGF_CAP_LOGM 0x01U /* set on first LOGM read */ + #define CRLOGF_READ_LOGM 0x02U /* flags current read as LOGM */ + #define CRLOGF_CVSDIRECT 0x04U + + /* if CVSDIRECT */ + CvsServerCtx * csctx; + /* else application FILE used and application managed */ + FILE * fp; +}; +/* private */ +#define CRLOG_SET_LOGM(x) ((x)->flags |= CRLOGF_CAP_LOGM|CRLOGF_READ_LOGM) +#define CRLOG_CLR_LOGM(x) ((x)->flags &= ~CRLOGF_READ_LOGM) +/* public */ +#define CRLOG_HAS_LOGM(x) ((x)->flags & CRLOGF_CAP_LOGM) +#define CRLOG_IS_LOGM(x) ((x)->flags & CRLOGF_READ_LOGM) + +typedef struct _CvsRlog CvsRlog; + CvsServerCtx * open_cvs_server(char * root, int); void close_cvs_server(CvsServerCtx*); void cvs_rdiff(CvsServerCtx *, const char *, const char *, const char *, const char *); void cvs_rupdate(CvsServerCtx *, const char *, const char *, const char *, int, const char *); void cvs_diff(CvsServerCtx *, const char *, const char *, const char *, const char *, const char *); -FILE * cvs_rlog_open(CvsServerCtx *, const char *, const char *); -char * cvs_rlog_fgets(char *, int, CvsServerCtx *); -void cvs_rlog_close(CvsServerCtx *); +/* Must call cvs_rlog_close to free CvsRlog object */ +CvsRlog * cvs_rlog_open(CvsServerCtx *, const char *, const char *); +char * cvs_rlog_fgets(char *, int, CvsRlog *); +/* frees CvsRlog object */ +void cvs_rlog_close(CvsRlog *); void cvs_version(CvsServerCtx *, char *, char *); #endif /* CVS_DIRECT_H */ --- cvsps.c.orig Wed May 25 20:39:40 2005 +++ cvsps.c Wed Nov 1 19:03:44 2017 @@ -45,7 +45,8 @@ enum NEED_START_LOG, NEED_REVISION, NEED_DATE_AUTHOR_STATE, - NEED_EOM + NEED_EOM, + NEED_LOGM }; /* true globals */ @@ -145,6 +146,9 @@ static CvsFileRevision * rev_follow_branch(CvsFileRevi static int before_tag(CvsFileRevision * rev, const char * tag); static void determine_branch_ancestor(PatchSet * ps, PatchSet * head_ps); static void handle_collisions(); +static int count_loglines(PatchSet *); +static size_t append_logbuff(char * _logbuff, size_t _logbuffmax, + size_t _loglen, char * _buff); int main(int argc, char *argv[]) { @@ -256,22 +260,13 @@ int main(int argc, char *argv[]) exit(0); } -static void load_from_cvs() +static CvsRlog * cvsrlog_open(void) { - FILE * cvsfp; - char buff[BUFSIZ]; - int state = NEED_FILE; - CvsFile * file = NULL; - PatchSetMember * psm = NULL; - char datebuff[20]; - char authbuff[AUTH_STR_MAX]; - char logbuff[LOG_STR_MAX + 1]; - int loglen = 0; - int have_log = 0; - char cmd[BUFSIZ]; + char * ltype; char date_str[64]; + char cmd[BUFSIZ]; char use_rep_buff[PATH_MAX]; - char * ltype; + CvsRlog * cvsrlog; if (!no_rlog && !test_log_file && cvs_check_cap(CAP_HAVE_RLOG)) { @@ -305,36 +300,85 @@ static void load_from_cvs() date_str[0] = 0; snprintf(cmd, BUFSIZ, "cvs %s %s %s %s", compress_arg, norc, ltype, use_rep_buff); } - + debug(DEBUG_STATUS, "******* USING CMD %s", cmd); cache_date = time(NULL); + if (cvs_direct_ctx) + return cvs_rlog_open(cvs_direct_ctx, repository_path, date_str); + + cvsrlog = calloc(1, sizeof(*cvsrlog)); + if (!cvsrlog) + { + debug(DEBUG_SYSERROR, "calloc failed"); + exit(1); + } + /* FIXME: this is ugly, need to virtualize the accesses away from here */ if (test_log_file) - cvsfp = fopen(test_log_file, "r"); - else if (cvs_direct_ctx) - cvsfp = cvs_rlog_open(cvs_direct_ctx, repository_path, date_str); + { + cvsrlog->fp = fopen(test_log_file, "r"); + if (!cvsrlog->fp) + debug(DEBUG_SYSERROR, "can't open log-file: %s", test_log_file); + exit(1); + } else - cvsfp = popen(cmd, "r"); - - if (!cvsfp) { - debug(DEBUG_SYSERROR, "can't open cvs pipe using command %s", cmd); + cvsrlog->fp = popen(cmd, "r"); + if (!cvsrlog->fp) + debug(DEBUG_SYSERROR, "can't open cvs pipe using command %s", cmd); exit(1); } - for (;;) + return cvsrlog; +} + +static void cvsrlog_close(CvsRlog *cvsrlog) +{ + if (cvsrlog->flags & CRLOGF_CVSDIRECT) + return cvs_rlog_close(cvsrlog); + + if (test_log_file) + fclose(cvsrlog->fp); + else { - char * tst; - if (cvs_direct_ctx) - tst = cvs_rlog_fgets(buff, BUFSIZ, cvs_direct_ctx); - else - tst = fgets(buff, BUFSIZ, cvsfp); + if (pclose(cvsrlog->fp) < 0) + { + debug(DEBUG_APPERROR, "cvs rlog command exited with error. aborting"); + exit(1); + } + } + free(cvsrlog); +} - if (!tst) - break; +static char * cvsrlog_fgets(CvsRlog *, char * _buff, int _buflen); +static char * cvsrlog_fgets(CvsRlog * cvsrlog, char * buff, int buflen) +{ + if (cvsrlog->flags & CRLOGF_CVSDIRECT) + return cvs_rlog_fgets(buff, buflen, cvsrlog); + else + return fgets(buff, buflen, cvsrlog->fp); +} +static void load_from_cvs() +{ + char buff[BUFSIZ]; + int state = NEED_FILE; + CvsFile * file = NULL; + PatchSetMember * psm = NULL; + char datebuff[20]; + char authbuff[AUTH_STR_MAX]; + char logbuff[LOG_STR_MAX + 1]; + int loglen = 0; + int have_log = 0; + int haslogm = 0, logmstarted = 0; + CvsRlog *cvsrlog; + + cvsrlog = cvsrlog_open(); + + while (cvsrlog_fgets(cvsrlog, buff, sizeof(buff))) + { debug(DEBUG_STATUS, "state: %d read line:%s", state, buff); switch(state) @@ -403,7 +447,7 @@ static void load_from_cvs() * of the info (revs and logs) until we hit the next file */ psm = NULL; - state = NEED_EOM; + state = haslogm ? NEED_LOGM : NEED_EOM; } } break; @@ -440,10 +484,18 @@ static void load_from_cvs() psm->post_rev->dead = 1; } - state = NEED_EOM; + state = haslogm ? NEED_LOGM : NEED_EOM; } break; case NEED_EOM: + if (!haslogm && CRLOG_HAS_LOGM(cvsrlog)) + { + /* First encounter of LOGM, switch processing. */ + haslogm = 1; + state = NEED_LOGM; + goto dologm; + } + dobounds: if (strcmp(buff, CVS_LOG_BOUNDARY) == 0) { if (psm) @@ -477,42 +529,44 @@ static void load_from_cvs() { /* other "blahblah: information;" messages can * follow the stuff we pay attention to - */ - if (have_log || !is_revision_metadata(buff)) + * + * If server supports LOGM, do not append to logbuff here. + */ + if (!haslogm && (have_log || !is_revision_metadata(buff))) { - /* if the log buffer is full, that's it. - * - * Also, read lines (fgets) always have \n in them - * which we count on. So if truncation happens, - * be careful to put a \n on. - * - * Buffer has LOG_STR_MAX + 1 for room for \0 if - * necessary - */ - if (loglen < LOG_STR_MAX) - { - int len = strlen(buff); - - if (len >= LOG_STR_MAX - loglen) - { - debug(DEBUG_APPMSG1, "WARNING: maximum log length exceeded, truncating log"); - len = LOG_STR_MAX - loglen; - buff[len - 1] = '\n'; - } - - debug(DEBUG_STATUS, "appending %s to log", buff); - memcpy(logbuff + loglen, buff, len); - loglen += len; - logbuff[loglen] = 0; - have_log = 1; - } + loglen = append_logbuff(logbuff, LOG_STR_MAX, loglen, buff); + have_log = 1; } else { debug(DEBUG_STATUS, "ignoring unhandled info %s", buff); } } + break; + case NEED_LOGM: + dologm: + if (!logmstarted) + { + if (!CRLOG_IS_LOGM(cvsrlog)) + break; + logmstarted = 1; + logbuff[0] = 0; + loglen = 0; + } + else if (!CRLOG_IS_LOGM(cvsrlog)) + { + /* + * Just passed the last LOGM line. continue at EOM for + * revision/file boundary parsing. + */ + logmstarted = 0; + state = NEED_EOM; + goto dobounds; + break; + } + + loglen = append_logbuff(logbuff, LOG_STR_MAX, loglen, buff); break; } } @@ -529,23 +583,42 @@ static void load_from_cvs() debug(DEBUG_APPERROR, "Error: Log file parsing error. (%d) Use -v to debug", state); exit(1); } - - if (test_log_file) + + cvsrlog_close(cvsrlog); +} + +static size_t append_logbuff(char * logbuff, size_t logbuffmax, size_t loglen, + char * buff) +{ + size_t len; + + /* if the log buffer is full, that's it. + * + * Also, read lines (fgets) always have \n in them + * which we count on. So if truncation happens, + * be careful to put a \n on. + * + * Buffer has LOG_STR_MAX + 1 for room for \0 if + * necessary + */ + if (loglen >= logbuffmax) + return; + + len = strlen(buff); + + if (len >= logbuffmax - loglen) { - fclose(cvsfp); + debug(DEBUG_APPMSG1, "WARNING: maximum log length exceeded, truncating log"); + len = logbuffmax - loglen; + buff[len - 1] = '\n'; } - else if (cvs_direct_ctx) - { - cvs_rlog_close(cvs_direct_ctx); - } - else - { - if (pclose(cvsfp) < 0) - { - debug(DEBUG_APPERROR, "cvs rlog command exited with error. aborting"); - exit(1); - } - } + + debug(DEBUG_STATUS, "appending %s to log", buff); + memcpy(logbuff + loglen, buff, len); + loglen += len; + logbuff[loglen] = 0; + + return loglen; } static int usage(const char * str1, const char * str2) @@ -1433,6 +1506,24 @@ static void check_print_patch_set(PatchSet * ps) fflush(stdout); } +static int count_loglines(PatchSet * ps) +{ + char *p; + int loglines; + + if (!ps->descr) + return 0; + + loglines = 0; + p = ps->descr; + while (*p) + { + if (*p++ == '\n') + loglines++; + } + return loglines; +} + static void print_patch_set(PatchSet * ps) { struct tm *tm; @@ -1455,7 +1546,7 @@ static void print_patch_set(PatchSet * ps) if (ps->ancestor_branch) printf("Ancestor branch: %s\n", ps->ancestor_branch); printf("Tag: %s %s\n", ps->tag ? ps->tag : "(none)", tag_flag_descr[ps->tag_flags]); - printf("Log:\n%s\n", ps->descr); + printf("Log: %d\n%s\n", count_loglines(ps), ps->descr); printf("Members: \n"); while (next != &ps->members) @@ -2550,9 +2641,9 @@ static void determine_branch_ancestor(PatchSet * ps, P for (next = ps->members.next; next != &ps->members; next = next->next) { + int d1, d2; PatchSetMember * psm = list_entry(next, PatchSetMember, link); rev = psm->pre_rev; - int d1, d2; /* the reason this is at all complicated has to do with a * branch off of a branch. it is possible (and indeed