e487b77b1a 2008-02-04 drh: /* e487b77b1a 2008-02-04 drh: ** Copyright (c) 2008 D. Richard Hipp e487b77b1a 2008-02-04 drh: ** e487b77b1a 2008-02-04 drh: ** This program is free software; you can redistribute it and/or e487b77b1a 2008-02-04 drh: ** modify it under the terms of the GNU General Public e487b77b1a 2008-02-04 drh: ** License version 2 as published by the Free Software Foundation. e487b77b1a 2008-02-04 drh: ** e487b77b1a 2008-02-04 drh: ** This program is distributed in the hope that it will be useful, e487b77b1a 2008-02-04 drh: ** but WITHOUT ANY WARRANTY; without even the implied warranty of e487b77b1a 2008-02-04 drh: ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU e487b77b1a 2008-02-04 drh: ** General Public License for more details. e487b77b1a 2008-02-04 drh: ** e487b77b1a 2008-02-04 drh: ** You should have received a copy of the GNU General Public e487b77b1a 2008-02-04 drh: ** License along with this library; if not, write to the e487b77b1a 2008-02-04 drh: ** Free Software Foundation, Inc., 59 Temple Place - Suite 330, e487b77b1a 2008-02-04 drh: ** Boston, MA 02111-1307, USA. e487b77b1a 2008-02-04 drh: ** e487b77b1a 2008-02-04 drh: ** Author contact information: e487b77b1a 2008-02-04 drh: ** drh@hwaci.com e487b77b1a 2008-02-04 drh: ** http://www.hwaci.com/drh/ e487b77b1a 2008-02-04 drh: ** e487b77b1a 2008-02-04 drh: ******************************************************************************* e487b77b1a 2008-02-04 drh: ** e487b77b1a 2008-02-04 drh: ** This file contains code to implement the file browser web interface. e487b77b1a 2008-02-04 drh: */ e487b77b1a 2008-02-04 drh: #include "config.h" e487b77b1a 2008-02-04 drh: #include "browse.h" e487b77b1a 2008-02-04 drh: #include <assert.h> e487b77b1a 2008-02-04 drh: e487b77b1a 2008-02-04 drh: /* e487b77b1a 2008-02-04 drh: ** This is the implemention of the "pathelement(X,N)" SQL function. e487b77b1a 2008-02-04 drh: ** e487b77b1a 2008-02-04 drh: ** If X is a unix-like pathname (with "/" separators) and N is an e487b77b1a 2008-02-04 drh: ** integer, then skip the initial N characters of X and return the e487b77b1a 2008-02-04 drh: ** name of the path component that begins on the N+1th character e487b77b1a 2008-02-04 drh: ** (numbered from 0). If the path component is a directory (if e487b77b1a 2008-02-04 drh: ** it is followed by other path components) then prepend "/". e487b77b1a 2008-02-04 drh: ** e487b77b1a 2008-02-04 drh: ** Examples: e487b77b1a 2008-02-04 drh: ** e487b77b1a 2008-02-04 drh: ** pathelement('abc/pqr/xyz', 4) -> '/pqr' e487b77b1a 2008-02-04 drh: ** pathelement('abc/pqr', 4) -> 'pqr' e487b77b1a 2008-02-04 drh: ** pathelement('abc/pqr/xyz', 0) -> '/abc' e487b77b1a 2008-02-04 drh: */ e487b77b1a 2008-02-04 drh: static void pathelementFunc( e487b77b1a 2008-02-04 drh: sqlite3_context *context, e487b77b1a 2008-02-04 drh: int argc, e487b77b1a 2008-02-04 drh: sqlite3_value **argv e487b77b1a 2008-02-04 drh: ){ e487b77b1a 2008-02-04 drh: const unsigned char *z; e487b77b1a 2008-02-04 drh: int len, n, i; e487b77b1a 2008-02-04 drh: char *zOut; e487b77b1a 2008-02-04 drh: e487b77b1a 2008-02-04 drh: assert( argc==2 ); e487b77b1a 2008-02-04 drh: z = sqlite3_value_text(argv[0]); e487b77b1a 2008-02-04 drh: if( z==0 ) return; e487b77b1a 2008-02-04 drh: len = sqlite3_value_bytes(argv[0]); e487b77b1a 2008-02-04 drh: n = sqlite3_value_int(argv[1]); e487b77b1a 2008-02-04 drh: if( len<=n ) return; e487b77b1a 2008-02-04 drh: if( n>0 && z[n-1]!='/' ) return; e487b77b1a 2008-02-04 drh: for(i=n; i<len && z[i]!='/'; i++){} e487b77b1a 2008-02-04 drh: if( i==len ){ e487b77b1a 2008-02-04 drh: sqlite3_result_text(context, (char*)&z[n], len-n, SQLITE_TRANSIENT); e487b77b1a 2008-02-04 drh: }else{ e487b77b1a 2008-02-04 drh: zOut = sqlite3_mprintf("/%.*s", i-n, &z[n]); e487b77b1a 2008-02-04 drh: sqlite3_result_text(context, zOut, i-n+1, sqlite3_free); e487b77b1a 2008-02-04 drh: } e487b77b1a 2008-02-04 drh: } e487b77b1a 2008-02-04 drh: a20dcb5c26 2008-02-04 drh: /* a20dcb5c26 2008-02-04 drh: ** Given a pathname which is a relative path from the root of a20dcb5c26 2008-02-04 drh: ** the repository to a file or directory, compute a string which a20dcb5c26 2008-02-04 drh: ** is an HTML rendering of that path with hyperlinks on each a20dcb5c26 2008-02-04 drh: ** directory component of the path where the hyperlink redirects a20dcb5c26 2008-02-04 drh: ** to the "dir" page for the directory. a20dcb5c26 2008-02-04 drh: ** a20dcb5c26 2008-02-04 drh: ** There is no hyperlink on the file element of the path. a20dcb5c26 2008-02-04 drh: ** a20dcb5c26 2008-02-04 drh: ** The computed string is appended to the pOut blob. pOut should a20dcb5c26 2008-02-04 drh: ** have already been initialized. a20dcb5c26 2008-02-04 drh: */ a20dcb5c26 2008-02-04 drh: void hyperlinked_path(const char *zPath, Blob *pOut){ a20dcb5c26 2008-02-04 drh: int i, j; a20dcb5c26 2008-02-04 drh: char *zSep = ""; a20dcb5c26 2008-02-04 drh: a20dcb5c26 2008-02-04 drh: for(i=0; zPath[i]; i=j){ a20dcb5c26 2008-02-04 drh: for(j=i; zPath[j] && zPath[j]!='/'; j++){} d57de28756 2008-05-05 drh: if( zPath[j] && g.okHistory ){ a20dcb5c26 2008-02-04 drh: blob_appendf(pOut, "%s<a href=\"%s/dir?name=%#T\">%#h</a>", a20dcb5c26 2008-02-04 drh: zSep, g.zBaseURL, j, zPath, j-i, &zPath[i]); a20dcb5c26 2008-02-04 drh: }else{ d57de28756 2008-05-05 drh: blob_appendf(pOut, "%s%#h", zSep, j-i, &zPath[i]); a20dcb5c26 2008-02-04 drh: } a20dcb5c26 2008-02-04 drh: zSep = "/"; a20dcb5c26 2008-02-04 drh: while( zPath[j]=='/' ){ j++; } a20dcb5c26 2008-02-04 drh: } a20dcb5c26 2008-02-04 drh: } a20dcb5c26 2008-02-04 drh: e487b77b1a 2008-02-04 drh: e487b77b1a 2008-02-04 drh: /* e487b77b1a 2008-02-04 drh: ** WEBPAGE: dir e487b77b1a 2008-02-04 drh: ** e487b77b1a 2008-02-04 drh: ** Query parameters: e487b77b1a 2008-02-04 drh: ** e81cc91aa4 2008-02-04 drh: ** name=PATH Directory to display. Required. 923d644b89 2009-01-28 drh: ** ci=LABEL Show only files in this check-in. Optional. e487b77b1a 2008-02-04 drh: */ e487b77b1a 2008-02-04 drh: void page_dir(void){ e81cc91aa4 2008-02-04 drh: const char *zD = P("name"); e487b77b1a 2008-02-04 drh: int mxLen; e487b77b1a 2008-02-04 drh: int nCol, nRow; e487b77b1a 2008-02-04 drh: int cnt, i; e487b77b1a 2008-02-04 drh: char *zPrefix; e487b77b1a 2008-02-04 drh: Stmt q; 923d644b89 2009-01-28 drh: const char *zCI = P("ci"); 923d644b89 2009-01-28 drh: int rid = 0; 923d644b89 2009-01-28 drh: Blob content; 923d644b89 2009-01-28 drh: Manifest m; 923d644b89 2009-01-28 drh: const char *zSubdirLink; e487b77b1a 2008-02-04 drh: e487b77b1a 2008-02-04 drh: login_check_credentials(); e487b77b1a 2008-02-04 drh: if( !g.okHistory ){ login_needed(); return; } e487b77b1a 2008-02-04 drh: style_header("File List"); e487b77b1a 2008-02-04 drh: sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0, e487b77b1a 2008-02-04 drh: pathelementFunc, 0, 0); e487b77b1a 2008-02-04 drh: e81cc91aa4 2008-02-04 drh: /* If the name= parameter is an empty string, make it a NULL pointer */ e487b77b1a 2008-02-04 drh: if( zD && strlen(zD)==0 ){ zD = 0; } e81cc91aa4 2008-02-04 drh: 923d644b89 2009-01-28 drh: /* If a specific check-in is requested, fetch and parse it. */ 923d644b89 2009-01-28 drh: if( zCI && (rid = name_to_rid(zCI))!=0 && content_get(rid, &content) ){ 923d644b89 2009-01-28 drh: if( !manifest_parse(&m, &content) || m.type!=CFTYPE_MANIFEST ){ 923d644b89 2009-01-28 drh: zCI = 0; 923d644b89 2009-01-28 drh: } 923d644b89 2009-01-28 drh: } e487b77b1a 2008-02-04 drh: e487b77b1a 2008-02-04 drh: /* Compute the title of the page */ e487b77b1a 2008-02-04 drh: if( zD ){ e487b77b1a 2008-02-04 drh: Blob title; e487b77b1a 2008-02-04 drh: e487b77b1a 2008-02-04 drh: blob_zero(&title); a20dcb5c26 2008-02-04 drh: blob_appendf(&title, "Files in directory "); a20dcb5c26 2008-02-04 drh: hyperlinked_path(zD, &title); 923d644b89 2009-01-28 drh: @ <h2>%s(blob_str(&title)) a20dcb5c26 2008-02-04 drh: blob_reset(&title); a20dcb5c26 2008-02-04 drh: zPrefix = mprintf("%h/", zD); a20dcb5c26 2008-02-04 drh: }else{ 923d644b89 2009-01-28 drh: @ <h2>Files in the top-level directory a20dcb5c26 2008-02-04 drh: zPrefix = ""; 923d644b89 2009-01-28 drh: } 923d644b89 2009-01-28 drh: if( zCI ){ 923d644b89 2009-01-28 drh: char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); 923d644b89 2009-01-28 drh: char zShort[20]; 923d644b89 2009-01-28 drh: memcpy(zShort, zUuid, 10); 923d644b89 2009-01-28 drh: zShort[10] = 0; 923d644b89 2009-01-28 drh: @ of check-in [<a href="vinfo?name=%T(zUuid)">%s(zShort)</a>]</h2> 923d644b89 2009-01-28 drh: zSubdirLink = mprintf("%s/dir?ci=%s&name=%T", g.zBaseURL, zUuid, zPrefix); 923d644b89 2009-01-28 drh: if( zD ){ 923d644b89 2009-01-28 drh: style_submenu_element("Top", "Top", "%s/dir?ci=%s", g.zBaseURL, zUuid); e487b77b1a 2008-02-04 drh: } e487b77b1a 2008-02-04 drh: }else{ 923d644b89 2009-01-28 drh: @ </h2> 923d644b89 2009-01-28 drh: zSubdirLink = mprintf("%s/dir?name=%T", g.zBaseURL, zPrefix); e487b77b1a 2008-02-04 drh: } e487b77b1a 2008-02-04 drh: e487b77b1a 2008-02-04 drh: /* Compute the temporary table "localfiles" containing the names e487b77b1a 2008-02-04 drh: ** of all files and subdirectories in the zD[] directory. e487b77b1a 2008-02-04 drh: ** e487b77b1a 2008-02-04 drh: ** Subdirectory names begin with "/". This causes them to sort e487b77b1a 2008-02-04 drh: ** first and it also gives us an easy way to distinguish files e487b77b1a 2008-02-04 drh: ** from directories in the loop that follows. e487b77b1a 2008-02-04 drh: */ 923d644b89 2009-01-28 drh: db_multi_exec( 923d644b89 2009-01-28 drh: "CREATE TEMP TABLE localfiles(x UNIQUE NOT NULL, u);" 923d644b89 2009-01-28 drh: "CREATE TEMP TABLE allfiles(x UNIQUE NOT NULL, u);" 923d644b89 2009-01-28 drh: ); 923d644b89 2009-01-28 drh: if( zCI ){ 923d644b89 2009-01-28 drh: Stmt ins; 923d644b89 2009-01-28 drh: int i; 923d644b89 2009-01-28 drh: db_prepare(&ins, "INSERT INTO allfiles VALUES(:x, :u)"); 923d644b89 2009-01-28 drh: for(i=0; i<m.nFile; i++){ 923d644b89 2009-01-28 drh: db_bind_text(&ins, ":x", m.aFile[i].zName); 923d644b89 2009-01-28 drh: db_bind_text(&ins, ":u", m.aFile[i].zUuid); 923d644b89 2009-01-28 drh: db_step(&ins); 923d644b89 2009-01-28 drh: db_reset(&ins); 923d644b89 2009-01-28 drh: } 923d644b89 2009-01-28 drh: db_finalize(&ins); 923d644b89 2009-01-28 drh: }else{ 923d644b89 2009-01-28 drh: db_multi_exec( 923d644b89 2009-01-28 drh: "INSERT INTO allfiles SELECT name, NULL FROM filename" 923d644b89 2009-01-28 drh: ); 923d644b89 2009-01-28 drh: } e487b77b1a 2008-02-04 drh: if( zD ){ e487b77b1a 2008-02-04 drh: db_multi_exec( e487b77b1a 2008-02-04 drh: "INSERT OR IGNORE INTO localfiles " 923d644b89 2009-01-28 drh: " SELECT pathelement(x,%d), u FROM allfiles" 923d644b89 2009-01-28 drh: " WHERE x GLOB '%q/*'", e487b77b1a 2008-02-04 drh: strlen(zD)+1, zD e487b77b1a 2008-02-04 drh: ); e487b77b1a 2008-02-04 drh: }else{ e487b77b1a 2008-02-04 drh: db_multi_exec( e487b77b1a 2008-02-04 drh: "INSERT OR IGNORE INTO localfiles " 923d644b89 2009-01-28 drh: " SELECT pathelement(x,0), u FROM allfiles" e487b77b1a 2008-02-04 drh: ); e487b77b1a 2008-02-04 drh: } e487b77b1a 2008-02-04 drh: e487b77b1a 2008-02-04 drh: /* Generate a multi-column table listing the contents of zD[] e487b77b1a 2008-02-04 drh: ** directory. e487b77b1a 2008-02-04 drh: */ e487b77b1a 2008-02-04 drh: mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles"); e487b77b1a 2008-02-04 drh: cnt = db_int(0, "SELECT count(*) FROM localfiles"); e487b77b1a 2008-02-04 drh: nCol = 4; e487b77b1a 2008-02-04 drh: nRow = (cnt+nCol-1)/nCol; 923d644b89 2009-01-28 drh: db_prepare(&q, "SELECT x, u FROM localfiles ORDER BY x"); e487b77b1a 2008-02-04 drh: @ <table border="0" width="100%%"><tr><td valign="top" width="25%%"> e487b77b1a 2008-02-04 drh: i = 0; e487b77b1a 2008-02-04 drh: while( db_step(&q)==SQLITE_ROW ){ e487b77b1a 2008-02-04 drh: const char *zFName; e487b77b1a 2008-02-04 drh: if( i==nRow ){ e487b77b1a 2008-02-04 drh: @ </td><td valign="top" width="25%%"> e487b77b1a 2008-02-04 drh: i = 0; e487b77b1a 2008-02-04 drh: } e487b77b1a 2008-02-04 drh: i++; e487b77b1a 2008-02-04 drh: zFName = db_column_text(&q, 0); e487b77b1a 2008-02-04 drh: if( zFName[0]=='/' ){ e487b77b1a 2008-02-04 drh: zFName++; f871a3b578 2009-10-11 dmitry: @ <li> f871a3b578 2009-10-11 dmitry: @ <a href="%s(zSubdirLink)%T(zFName)">%h(zFName)/</a></li> 923d644b89 2009-01-28 drh: }else if( zCI ){ 923d644b89 2009-01-28 drh: const char *zUuid = db_column_text(&q, 1); 923d644b89 2009-01-28 drh: @ <li><a href="%s(g.zBaseURL)/artifact?name=%s(zUuid)">%h(zFName)</a> e487b77b1a 2008-02-04 drh: }else{ f871a3b578 2009-10-11 dmitry: @ <li> f871a3b578 2009-10-11 dmitry: @ <a href="%s(g.zBaseURL)/finfo?name=%T(zPrefix)%T(zFName)">%h(zFName)</a></li> e487b77b1a 2008-02-04 drh: } e487b77b1a 2008-02-04 drh: } e487b77b1a 2008-02-04 drh: db_finalize(&q); e487b77b1a 2008-02-04 drh: @ </td></tr></table> e487b77b1a 2008-02-04 drh: style_footer(); e487b77b1a 2008-02-04 drh: }