// // Copyright 2003 Perforce Software. All rights reserved. // // This file is part of Perforce - the FAST SCM System. // // p4wDiffPane: // Displays output of diff or diff2 #include <p4wp4.h> #include "p4wStrBuf.h" #include "p4wHtml.h" #include "p4wPane.h" #include "p4wDiffPane.h" #define RED 0xFF0000 #define GREEN 0x007F00 #define BLUE 0x0000FF p4wDiffPane::p4wDiffPane( p4wView & ParentView, p4wRequest & Request, StrPtr *path, StrPtr *path2 ) : p4wPane( ParentView, Request ), fSeenData(0), fState(0), fType(0), fPath(path), fPath2(path2), fSingletonCR(0), fGotLF(0) { } p4wDiffPane::~p4wDiffPane() { } // ------------------------------------- // Render functions. // void p4wDiffPane::Begin() { // // Begin the pane. if( !fSeenData ) { p4wHtml htm; htm.comment( "BEGIN DIFF PANE" ); fRequest << htm; } } void p4wDiffPane::RenderText( char *data ) { RenderBinary(data, strlen(data)); } void p4wDiffPane::RenderBinary( char *data, int len ) { if (fState) { if (fState == 1) fData.Append(data, len); else fFile.Append(data, len); return; } fDiffsFound = 1; if (isdigit(*data) && fRequest.GetDiffType()) fType = fRequest.GetDiffType(); p4wHtml htm( 1 ); if( !fSeenData ) { fSeenData = 1; if (!fType) { htm.beginTRow(); htm.beginCol(); p4wURL urlMaker; StrBuf clearIcon; urlMaker.ConstructURL( clearIcon, "/clearpixelIcon", AC_ICON, NULL ); htm.icon( clearIcon.Text(), "5", "0", "", 1 ); htm.endCol(); htm.endTRow(); htm.beginTRow(); htm.beginCol(); fRequest << htm; htm.Clear(); if (!fType) { const StrPtr *dw = fRequest.GetStateArg( "dw" ); if (dw && strchr(dw->Text(), 'c')) htm << "<center><div class=\"fSmaller\">Invalid number of context lines; reverting to old style context diff.</div></center>"; } htm.beginTable( "0", "100%", 0, "4", 0, "fSmall" ); htm.beginTRow(); htm.beginCol(); htm.beginTable( "0", "100%", "8", "1", "#7F7F7F" ); htm.beginTRow( NULL, NULL, "#FFFFFF" ); htm.beginCol(); htm.beginTable( "0", NULL , "2", "0", "#FFFFFF" ); htm.beginTRow(); htm.beginCol(); htm.beginUnformatted(); fRequest << htm; } } // // If page content limit in bytes has been exceeded, don't // generate this entry if( PageLimitExceeded() ) { char nam[4096]; sprintf(nam, "%d", ++chunkCtr); fRequest << "<a name=\"" << nam << "\"></a>"; sprintf(nam, "%d", totChunks); fRequest << "<a name=\"" << nam << "\"></a>"; return; } if (!fType) { fRequest << p4wStrBuf().EscapeHTML( StrRef(data, len), Unicode() ); return; } fState = 1; fData.Append(data, len); } void p4wDiffPane::Render2paneDiff( char * file ) { int lgth; p4wURL urlMaker; StrBuf clearIcon; urlMaker.ConstructURL( clearIcon, "/clearpixelIcon", AC_ICON, NULL ); in_ctr = in2ctr = bStar = mflag = nflag = last = bSep = difctr = ctrdif = bCommaComma = chunkCtr = totChunks = addChunks = delChunks = chgChunks= bEOF = lastWasStar = 0; bSync = 1; action = prevaction = '\0'; const StrPtr *diffliw = fRequest.GetStateArg( "dl" ); if (diffliw) { if (*diffliw == "n") nflag = 1; else if (*diffliw == "m") mflag = 1; } p4wHtml htm( 1 ); cflag = fType == -1 ? 0 : fType; if (!cflag && ((lgth = strlen(file)) > fRequest.PageContentLimit() * 100)) { cflag = 50; // big files get automatic context p4wURL urlMaker; StrBuf url; StrBuf newBase; htm.beginTable( 0, "100%", "4", 0, 0, "fSmall" ); htm.beginTRow("top"); htm.beginCol(0, "center"); htm << "<font color=\"#FF0000\">"; htm << "Large file needs "; fRequest.UseNewBase( newBase, NULL, "path", "/settings" ); urlMaker.ConstructURL( url, newBase.Text(), AC_HELP, NULL, fRequest.GetUnicode() ); url << "#pcl"; htm.beginLink( url.Text() ); htm << "page content limit"; htm.endLink(); htm << " of at least "; StrNum n; n.Set(lgth / 96); htm << n.Text(); htm << " KB; <nobr>displaying 50 line context.</nobr><p></font>" << crlf; } else { htm.beginTable( 0, "100%", "1" ); htm.beginTRow(); htm.beginCol(); htm.icon( clearIcon.Text(), "1", "1", "", 1 ); } htm.endCol(); htm.endTRow(); htm.endTable(); htm.beginTable( "0", "100%", "0", "1" ); htm.beginTRow(); htm.beginCol(); htm.beginTable( "0", "100%", "0", "0" ); htm.beginTRow(); htm.beginCol(0, 0, 0, 0, 0, "10"); htm.icon( clearIcon.Text(), "1", "10", "", 1 ); htm.endCol(); htm.beginCol(); htm.beginTable( "0", 0, "0", "0", 0, "fSmall" ); htm.beginTRow(); htm.beginCol(0,0,0,0,0,0,0,1); StrBuf url; url.Set( fRequest.IsHTTPS() ? "https://" : "http://" ); url << fRequest.GetHTTPPort() << fRequest.GetFullURL(); char nam[4096]; char js[80]; sprintf(nam, "%s#1", url.Text()); htm.beginLink( nam, 0,0,0,0,0, "onClick", "return showchunk(1);" ); htm << "<img src=\"/arrowdownIcon?ac=20\" border=\"0\" align=\"top\">"; htm << " First Diff"; htm.endLink(); // count the chunks char * diffp = fData.Text(); while (getNextLine(difbuf, &diffp)) { if (isdigit(difbuf[0])) { totChunks++; if (strchr(difbuf.Text(), 'c')) chgChunks++; else if (strchr(difbuf.Text(), 'a')) addChunks++; else if (strchr(difbuf.Text(), 'd')) delChunks++; } } sprintf(nam, " "); htm << nam; sprintf(nam, "%s#%d", url.Text(), totChunks); sprintf(js, "return showchunk(%d);", totChunks); htm.beginLink( nam, 0,0,0,0,0, "onClick", js ); htm << "<img src=\"/arrowdbldownIcon?ac=20\" border=\"0\" align=\"top\">"; htm << " Last Diff "; htm.endLink(); htm << "<script type=\"text/javascript\"><!--" << crlf; htm << "set_mDif(" << totChunks << ");" << crlf; htm << "document.write(\" \")" << crlf; htm << "document.write(\"<Input type='checkbox' name='nextprev' id='nextprev' value='1' checked='1'>\")" << crlf; htm << "document.write(\"<label for='nextprev'>\")" << crlf; htm << "document.write(\" Type <b>n</b> or <b>p</b> to move to the next or previous diff\")" << crlf; htm << "document.write(\"</label>\")" << crlf; htm << "//--></script>" << crlf; htm.endCol(); htm.endTRow(); htm.endTable(); htm.endCol(); htm.beginCol(0, "right"); htm.beginTable( "0", 0, 0, "0", 0, "fSmall" ); htm.beginTRow(); htm.beginCol(0,0,0,0,0,0,0,1); sprintf(nam, " <b>%d</b> Total Diffs: ", totChunks); htm << nam; htm.endCol(); htm.beginCol(0, 0, 0, 0, "#C0FFC0"); htm << "<nobr> " << addChunks << " Added </font></nobr>"; htm.endCol(); htm.beginCol(0, 0, 0, 0, "#FFD0D0"); htm << "<nobr> " << delChunks << " Deleted </font></nobr>"; htm.endCol(); htm.beginCol(0, 0, 0, 0, "#E0E0FF"); htm << "<nobr> " << chgChunks << " Changed </font></nobr>"; htm.endCol(); htm.endTRow(); htm.endTable(); htm.endCol(); htm.endTRow(); htm.endTable(); htm.beginCol(0, 0, 0, 0, 0, "10"); htm.icon( clearIcon.Text(), "1", "10", "", 1 ); htm.endCol(); htm.endTRow(); htm.endTable(); htm.beginTable( "0", "100%", 0, "2", 0, "fSmall" ); htm.beginTRow(); htm.beginCol(); htm.beginTRow(); htm.beginCol(0, 0, "2"); htm.beginTable( "0", "100%", 0, "1", "#7F7F7F" ); htm.beginTRow( NULL, NULL, "#FFFFFF" ); htm.beginCol(); htm.beginTable(0, "100%", 0, 0, 0, "f"); htm << crlf << "<tr><th> </th><th></th><th width=\"50%\" align=\"left\" class=\"diffpath\">"; htm << fPath->Text() << "</th>"; htm << "<th></th><th width=\"50%\" align=\"left\" class=\"diffpath\">"; if (fPath2) htm << fPath2->Text(); else htm << "Workspace version of " << fPath->Text(); htm << "</th><th> </th></tr>\n"; htm.beginTRow(0, 0, "#D0D0D0"); htm.beginCol(0, 0, "6", 0, 0, 0, "1"); htm.endCol(); htm.endTRow(); fRequest << htm; char * inp = file; diffp = fData.Text(); bEOF = 0; do { if (!getNextLine(difbuf, &diffp)) { // nodiffs(); return; } } while (!isdigit(difbuf[0])); nextdiff(); if (difctr == 1 && action == 'c') bSync = 0; else if (difctr == 0 && action == 'a') { bSync = 0; do { if (!getNextLine(difbuf, &diffp)) { difctr = 0x7FFFFFFF; break; } if (*(difbuf.Text()) != '>') break; writeleft(" ", GREEN, bStar); writeright(toHTML(difbuf.Text()+2), GREEN); } while (*(difbuf.Text()) == '>'); nextdiff(); } int n; int firsttime = 1; while (getNextLine(in_buf, &inp) || firsttime) { firsttime = 0; if (++in_ctr < difctr) { if (cflag) { if ((in_ctr + cflag < difctr + (action=='a')) && (in_ctr - cflag >= last + (prevaction=='a'))) { if (!bSep && (in_ctr - cflag > last)) { writesep(); bSep = 1; } else if (!bSync) { ctrdif = in2ctr - (in_ctr - 1); bSync = 1; } continue; } } writeleft(toHTML(in_buf.Text()), 0, 0); writeright(safe.Text(), 0); if( PageLimitExceeded() ) return; continue; } switch(action) { case 'a': if (difctr >= in_ctr) { writeleft(toHTML(in_buf.Text()), 0, 0); writeright(safe.Text(), 0); n = 0; } else n = 1; do { if (!getNextLine(difbuf, &diffp)) { difctr = 0x7FFFFFFF; break; } if (*(difbuf.Text()) != '>') break; writeleft(" ", GREEN, bStar); writeright(toHTML(difbuf.Text()+2), GREEN); } while (*(difbuf.Text()) == '>'); if (n) { writeleft(toHTML(in_buf.Text()), 0, 0); writeright(safe.Text(), 0); } break; case 'c': n = 0; do { int b = bEOF; if (!getNextLine(difbuf, &diffp)) { difbuf.Set(""); difctr = 0x7FFFFFFF; bEOF = b; // not used for diffs file break; } if (*(difbuf.Text()) == '<') { n++; continue; } if (*(difbuf.Text()) == '-') continue; if (*(difbuf.Text()) != '>') break; if (n) { writeleft(toHTML(in_buf.Text()), BLUE, bStar); getNextLine(in_buf, &inp); in_ctr++; n--; } else writeleft(" ", BLUE, bStar); writeright(toHTML(difbuf.Text()+2), BLUE); } while (*(difbuf.Text()) == '>' || *(difbuf.Text()) == '<' || *(difbuf.Text()) == '-'); while (n) { writeleft(toHTML(in_buf.Text()), BLUE, bStar); getNextLine(in_buf, &inp); writeright(" ", BLUE); in_ctr++; n--; } if (!bEOF) { writeleft(toHTML(in_buf.Text()), 0, 0); writeright(safe.Text(), 0); } break; case 'd': n = 0; do { if (!getNextLine(difbuf, &diffp)) { difctr = 0x7FFFFFFF; break; } if (*(difbuf.Text()) != '<') break; writeleft(toHTML(difbuf.Text()+2), RED, bStar); writeright(" ", RED); in_ctr++; n++; } while (*(difbuf.Text()) == '<'); while (n--) getNextLine(in_buf, &inp); writeleft(toHTML(in_buf.Text()), 0, 0); writeright(safe.Text(), 0); break; default: if (!fRequest.bQuiet()) printf("Unknown diff action: %s\n", difbuf.Text()); break; } nextdiff(); last = in_ctr; bSep = 0; } if (difctr != 0x7FFFFFFF) { switch(action) { case 'a': do { if (!getNextLine(difbuf, &diffp)) { difctr = 0x7FFFFFFF; break; } if (*(difbuf.Text()) != '>') break; writeleft(" ", GREEN, bStar); writeright(toHTML(difbuf.Text()+2), GREEN); } while (*(difbuf.Text()) == '>'); break; default: if (!fRequest.bQuiet()) printf("Unknown diff action: %s\n", difbuf.Text()); break; } } } char * p4wDiffPane::getNextLine(StrBuf &buf, char **ptr) { char *p = *ptr; char *q = p; if (!*q--) { buf.Set(""); bEOF = 1; return 0; } while(*++q && *q != '\n' && *q != '\r') ; if (!(q-p)) buf.Set("\0"); else { buf.Set(p, q-p); buf.Append("\0"); } if (*q == '\r') { if (*(q+1) == '\n') q++; else if (in_ctr) { fSingletonCR = in_ctr; } } if (*q) { if (*q == '\n' && (buf[0] == '>' || buf[0] == '<')) fGotLF = in_ctr; q++; } *ptr = q; return buf.Text(); } char *p4wDiffPane::toHTML(char *bgn) { safe.Alloc((strlen(bgn) * 6) + 200); char *q = safe.Text(); char *p; int i = 0; int inarow = 0; int nonWhtSp = 0; for (p = bgn + strlen(bgn); *--p == ' ' && p >= bgn; ) *p = '\0'; // first pass for unicode conversion and some HTML chars p4wStrBuf buf; buf.EscapeHTML( StrRef(bgn), Unicode() ); bgn = buf.Text(); for (p = bgn-1; *++p; ) { switch(*p) { case ' ': if (nflag) { strcpy(q, " "); q += strlen(q); } else { if ((nonWhtSp || (i > 40)) && *(q-1) != ' ') { *q++ = ' '; } else { strcpy(q, " "); q += strlen(q); } } i++; break; case ',': *q++ = *p; if (!nflag && (*(p+1) > ' ')) { strcpy(q, "<wbr>"); q += strlen(q); } i++; break; case '\t': { do { strcpy(q, (mflag && i && (*(q-1)!=' ')) ? " " : " "); q += strlen(q); } while (++i % 4); break; } default: if (!nflag && i && (*(q-1) == *p)) { if (++inarow > 8) { strcpy(q, "<wbr>"); q += strlen(q); inarow = 0; } } else inarow = 0; *q++ = *p; if (!nflag && i && (*(q-2) == '\\') && (*p == 'n')) { strcpy(q, "<wbr>"); q += strlen(q); } nonWhtSp = 1; i++; break; } } *q = '\0'; return safe.Text(); } void p4wDiffPane::nextdiff() { char * p; if (difctr == 0x7FFFFFFF) { action = '\0'; return; } difctr = atoi(difbuf.Text()); for (p = difbuf.Text(); *++p < 'A'; ) { if (*p < ' ') { difctr = 0x7FFFFFFF; return; } } prevaction = action; action = *p; bStar = 1; } void p4wDiffPane::writeleft(char *str, int color, int star) { p4wHtml htm( 1 ); char buf[16]; char nam[4096]; char js[80]; char clr[10]; if (strcmp(str, " ")) { sprintf(buf, "%d", in_ctr); switch(color) { case RED: clr[0] = 'r'; break; case GREEN: clr[0] = 'g'; break; case BLUE: clr[0] = 'b'; break; default: clr[0] = 'n'; break; } } else { buf[0] = '\0'; str = buf; clr[0] = 'n'; } clr[1] = '\0'; StrBuf id; if (star) { id << "an" << ++chunkCtr; htm << "<tbody id =\"" << id << "\">" << crlf; } else if (lastWasStar && !color) { lastWasStar = 0; htm << "</tbody>" << crlf; } htm.beginTRow(); char arrowclass[4]; if (color) { char arrowclass[4]; switch(color) { case RED: arrowclass[0] = 'r'; break; case GREEN: arrowclass[0] = 'g'; break; case BLUE: arrowclass[0] = 'b'; break; default: arrowclass[0] = 'n'; break; } strcpy(&arrowclass[1], "aro"); htm.beginCol("top", 0,0,0,0,0,0,0, arrowclass); } else htm.beginCol("top"); if (star) { lastWasStar = 1; sprintf(nam, "%d", chunkCtr); htm << "<a name=\"" << nam << "\"></a>"; htm.beginNobreak(); StrBuf url; url.Set( fRequest.IsHTTPS() ? "https://" : "http://" ); url << fRequest.GetHTTPPort() << fRequest.GetFullURL(); StrBuf icon; p4wURL urlMaker; if (chunkCtr == totChunks) { urlMaker.ConstructIcon( icon, "/clearpixelIcon", 10, 7, "", 1 ); htm << icon; } else { urlMaker.ConstructIcon( icon, "/arrowdownIcon", 10, 7, "Next Diff", 1 ); sprintf(nam, "%s#%d", url.Text(), chunkCtr+1); sprintf(js, "return showchunk(%d);", chunkCtr+1); htm.beginLink( nam, 0,0,0,0,0, "onClick", js ); htm << icon; htm.endLink(); } if (chunkCtr == 1) { urlMaker.ConstructIcon( icon, "/clearpixelIcon", 10, 7, "", 1 ); htm << icon; } else { urlMaker.ConstructIcon( icon, "/arrowupIcon", 10, 7, "Previous Diff", 1 ); sprintf(nam, "%s#%d", url.Text(), chunkCtr-1); sprintf(js, "return showchunk(%d);", chunkCtr-1); htm.beginLink( nam, 0,0,0,0,0, "onClick", js ); htm << icon; htm.endLink(); } htm.endNobreak(); bCommaComma = 1; } else if (bCommaComma) { bCommaComma = 0; htm << "<script language=javascript>" << crlf; htm << "</script>" << crlf; htm << "<noscript>" << crlf; htm << "<span class=\"cc\">,,</span>"; htm << "</noscript>" << crlf; } htm.endCol(); htm.beginCol(0, 0, 0, 0, 0, 0, 0, 0, clr); htm << " " << buf << " "; htm.endCol(); htm << crlf; if (color && clr[0] != 'n') { clr[1] = 'x'; clr[2] = '\0'; } htm.beginCol(0, 0, 0, 0, 0, 0, 0, 0, color ? clr : 0); htm << str; if( PageLimitExceeded() ) { sprintf(nam, "%d", ++chunkCtr); htm << "<a name=\"" << nam << "\"></a>"; if (chunkCtr != totChunks) { sprintf(nam, "%d", totChunks); htm << "<a name=\"" << nam << "\"></a>"; } } htm.endCol(); if (star) bStar = 0; fRequest << htm; } void p4wDiffPane::writeright(char *str, int color) { p4wHtml htm( 1 ); char buf[16]; char clr[10]; if (strcmp(str, " ")) { if (cflag && bSync) { bSync = 0; in2ctr = in_ctr + ctrdif - 1; } sprintf(buf, "%d", ++in2ctr); switch(color) { case RED: clr[0] = 'r'; break; case GREEN: clr[0] = 'g'; break; case BLUE: clr[0] = 'b'; break; default: clr[0] = 'n'; break; } } else { buf[0] = '\0'; str = buf; clr[0] = 'n'; } clr[1] = '\0'; htm.beginCol(0, 0, 0, 0, 0, 0, 0, 0, clr); htm << " " << buf << " "; htm.endCol(); htm << crlf; if (color && clr[0] != 'n') { clr[1] = 'x'; clr[2] = '\0'; } htm.beginCol(0, 0, 0, 0, 0, 0, 0, 0, color ? clr : 0); htm << str; if( PageLimitExceeded() ) { char nam[4096]; sprintf(nam, "%d", ++chunkCtr); htm << "<a name=\"" << nam << "\"> </a>"; if (chunkCtr != totChunks) { sprintf(nam, "%d", totChunks); htm << "<a name=\"" << nam << "\"></a>"; } } htm.endCol(); htm.endTRow(); fRequest << htm; } void p4wDiffPane::writesep() { p4wHtml htm( 1 ); htm.beginTRow(0, 0, "#666666"); htm.beginCol(0, 0, "6", 0, 0, 0, "1"); htm.endCol(); htm.endTRow(); fRequest << htm; } void p4wDiffPane::End() { p4wHtml htm; if (fState) { if (fState == 2) { // // Render 2 pane Diff Render2paneDiff( fFile.Text() ); if (fSingletonCR && fGotLF) // check for line ending problems { htm.beginTRow(); htm.beginCol(0,0, "2"); htm.endCol(); htm.beginCol(0,0, "4"); htm.beginSpan("fNormal"); htm.text( "<p>Warning: inconsistent line endings found in the left hand file around line ", 0, 0, "#FF0000" ); if (fSingletonCR < fGotLF) htm << fSingletonCR; else htm << fGotLF; htm.text( " - results questionable.", 0, 0, "#FF0000" ); htm.endSpan(); htm.endCol(); htm.endTRow(); } } else return; } // // Tell user if no diffs were found. if( !fPrintedError && !fDiffsFound ) { htm.beginTRow(); htm.beginCol(); htm.text( " No differences found." ); htm.endCol(); htm.endTRow(); } // // End the table only if we started one if( fSeenData) { if (!fType) { htm.endUnformatted(); htm.endCol(); htm.endTRow(); } htm.endTable(); htm.endCol(); htm.endTRow(); htm.endTable(); if (fType && fRequest.GetJavascriptMode() != 2 ) htm << "</center>" << crlf; } htm.comment( "END DIFF PANE" ); fRequest << htm; } void p4wDiffPane::RenderInfo( char *data, char level ) { // // Output any "files differ" message. // This results from diffing binary files. p4wHtml htm; if( strstr( data, "files differ" ) ) { fDiffsFound = 1; htm.beginTRow(); htm.beginCol(); htm.beginSpan("fNormal"); htm << " "; htm.text( data ); htm.endSpan(); htm.endCol(); htm.endTRow(); fRequest << htm; } else if ( !strncmp(data, "===", 3) ) { char *p = strchr(data, '<'); if (p) { if (!strncmp(p, "< none >", 8)) p = "Left hand file/revision does not exist."; else if (!strncmp(p, "<none>", 6)) p = "Right hand file/revision does not exist."; else p = NULL; fDiffsFound = 1; htm.beginTRow(); htm.beginCol(); htm.beginSpan("fNormal"); htm << " "; htm << p4wStrBuf().EscapeHTML(StrRef(data)); if (p) { htm.linebreak(); htm << " "; htm.text(p, 0, 0, "#FF0000"); } htm.endSpan(); htm.endCol(); htm.endTRow(); fRequest << htm; } } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 12234 | Matt Attaway |
Rejigger P4Web project in preparation for official sunsetting The bin directory contains the last official builds of P4Web from the Perforce download site. P4Web is soon to be completely sunsetted; these builds are here for folks who don't want to build their own. To better handle the archived builds the source code has been moved into a separate src directory. |
||
//guest/perforce_software/p4web/Panes/p4wDiffPane.cpp | |||||
#1 | 8914 | Matt Attaway | Initial add of the P4Web source code |