Fossil SCM
Add options to "fossil sys ls": -S, -m, and -d.
Commit
13de44edf7fcf8642cc8b59bea0078cacc5ea7f7b09d9d99f4642ba41e965196
Parent
ab03e5067e30748…
1 file changed
+93
-33
+93
-33
| --- src/xsystem.c | ||
| +++ src/xsystem.c | ||
| @@ -66,10 +66,21 @@ | ||
| 66 | 66 | } |
| 67 | 67 | } |
| 68 | 68 | } |
| 69 | 69 | } |
| 70 | 70 | |
| 71 | +/* | |
| 72 | +** Bit values for the mFlags paramater to "ls" | |
| 73 | +*/ | |
| 74 | +#define LS_LONG 0x001 /* -l Long format - one object per line */ | |
| 75 | +#define LS_REVERSE 0x002 /* -r Reverse the sort order */ | |
| 76 | +#define LS_MTIME 0x004 /* -t Sort by mtime, newest first */ | |
| 77 | +#define LS_SIZE 0x008 /* -S Sort by size, largest first */ | |
| 78 | +#define LS_COMMA 0x010 /* -m Comma-separated list */ | |
| 79 | +#define LS_DIRONLY 0x020 /* -d Show just directory name, not content */ | |
| 80 | +#define LS_ALL 0x040 /* -a Show all entries */ | |
| 81 | + | |
| 71 | 82 | /* Helper function for xsystem_ls(): Make entries in the LS table |
| 72 | 83 | ** for every file or directory zName. |
| 73 | 84 | ** |
| 74 | 85 | ** If zName is a directory, load all files contained within that directory. |
| 75 | 86 | ** If zName is just a file, load only that file. |
| @@ -84,14 +95,16 @@ | ||
| 84 | 95 | int nList; |
| 85 | 96 | int i; |
| 86 | 97 | const char *zPrefix; |
| 87 | 98 | switch( file_isdir(zName, ExtFILE) ){ |
| 88 | 99 | case 1: { /* A directory */ |
| 89 | - azList = 0; | |
| 90 | - nList = file_directory_list(zName, 0, (mFlags & 0x08)==0, 0, &azList); | |
| 91 | - zPrefix = fossil_strcmp(zName,".") ? zName : 0; | |
| 92 | - break; | |
| 100 | + if( (mFlags & LS_DIRONLY)==0 ){ | |
| 101 | + azList = 0; | |
| 102 | + nList = file_directory_list(zName, 0, (mFlags & LS_ALL)==0, 0, &azList); | |
| 103 | + zPrefix = fossil_strcmp(zName,".") ? zName : 0; | |
| 104 | + break; | |
| 105 | + } | |
| 93 | 106 | } |
| 94 | 107 | case 2: { /* A file */ |
| 95 | 108 | aList[0] = (char*)zName; |
| 96 | 109 | aList[1] = 0; |
| 97 | 110 | azList = aList; |
| @@ -110,38 +123,56 @@ | ||
| 110 | 123 | sqlite3_int64 mtime = file_mtime(zFile, ExtFILE); |
| 111 | 124 | sqlite3_bind_text(pStmt, 1, zFile, -1, SQLITE_TRANSIENT); |
| 112 | 125 | sqlite3_bind_int64(pStmt, 2, mtime); |
| 113 | 126 | sqlite3_bind_int64(pStmt, 3, sz); |
| 114 | 127 | sqlite3_bind_int(pStmt, 4, mode); |
| 128 | + sqlite3_bind_int64(pStmt, 5, strlen(zFile)); | |
| 129 | + /* TODO: wcwidth()------^^^^^^ */ | |
| 115 | 130 | sqlite3_step(pStmt); |
| 116 | 131 | sqlite3_reset(pStmt); |
| 117 | 132 | if( zPrefix ) fossil_free(zFile); |
| 118 | 133 | } |
| 119 | 134 | if( azList!=aList ){ |
| 120 | 135 | file_directory_list_free(azList); |
| 121 | 136 | } |
| 122 | 137 | } |
| 138 | + | |
| 139 | +/* | |
| 140 | +** Return arguments to ORDER BY that will correctly sort the entires. | |
| 141 | +*/ | |
| 142 | +static const char *xsystem_ls_orderby(int mFlags){ | |
| 143 | + static const char *zSortTypes[] = { | |
| 144 | + "fn COLLATE NOCASE", | |
| 145 | + "mtime DESC", | |
| 146 | + "size DESC", | |
| 147 | + "fn COLLATE NOCASE DESC", | |
| 148 | + "mtime", | |
| 149 | + "size" | |
| 150 | + }; | |
| 151 | + int i = 0; | |
| 152 | + if( mFlags & LS_MTIME ) i = 1; | |
| 153 | + if( mFlags & LS_SIZE ) i = 2; | |
| 154 | + if( mFlags & LS_REVERSE ) i += 3; | |
| 155 | + return zSortTypes[i]; | |
| 156 | +} | |
| 123 | 157 | |
| 124 | 158 | /* |
| 125 | 159 | ** Show ls output information for content in the LS table |
| 126 | 160 | */ |
| 127 | 161 | static void xsystem_ls_render( |
| 128 | 162 | sqlite3 *db, |
| 129 | 163 | int mFlags |
| 130 | 164 | ){ |
| 131 | 165 | sqlite3_stmt *pStmt; |
| 132 | - int bDesc = (mFlags & 0x02)!=0; | |
| 133 | - if( mFlags & 0x04 ) bDesc = !bDesc; | |
| 134 | - if( (mFlags & 0x01)!=0 ){ | |
| 166 | + if( (mFlags & LS_LONG)!=0 ){ | |
| 135 | 167 | /* Long mode */ |
| 136 | 168 | char *zSql; |
| 137 | 169 | zSql = mprintf( |
| 138 | 170 | "SELECT mode, size, strftime('%%Y-%%m-%%d %%H:%%M'," |
| 139 | 171 | "mtime,'unixepoch'), fn" |
| 140 | - " FROM ls ORDER BY %s %s", | |
| 141 | - (mFlags & 0x04)!=0 ? "mtime" : "fn", | |
| 142 | - bDesc ? "DESC" : "ASC"); | |
| 172 | + " FROM ls ORDER BY %s", | |
| 173 | + xsystem_ls_orderby(mFlags)); | |
| 143 | 174 | sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); |
| 144 | 175 | while( sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 145 | 176 | char zMode[12]; |
| 146 | 177 | const char *zName = (const char*)sqlite3_column_text(pStmt, 3); |
| 147 | 178 | int mode = sqlite3_column_int(pStmt, 0); |
| @@ -171,16 +202,40 @@ | ||
| 171 | 202 | sqlite3_column_int64(pStmt, 1), |
| 172 | 203 | sqlite3_column_text(pStmt, 2), |
| 173 | 204 | zName); |
| 174 | 205 | } |
| 175 | 206 | sqlite3_finalize(pStmt); |
| 207 | + }else if( (mFlags & LS_COMMA)!=0 ){ | |
| 208 | + /* Comma-separate list */ | |
| 209 | + int mx = terminal_get_width(80); | |
| 210 | + int sumW = 0; | |
| 211 | + char *zSql; | |
| 212 | + zSql = mprintf("SELECT fn, dlen FROM ls ORDER BY %s", | |
| 213 | + xsystem_ls_orderby(mFlags)); | |
| 214 | + sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); | |
| 215 | + while( sqlite3_step(pStmt)==SQLITE_ROW ){ | |
| 216 | + const char *z = (const char*)sqlite3_column_text(pStmt, 0); | |
| 217 | + int w = sqlite3_column_int(pStmt, 1); | |
| 218 | + if( sumW==0 ){ | |
| 219 | + fossil_print("%s", z); | |
| 220 | + sumW = w; | |
| 221 | + }else if( sumW + w + 2 >= mx ){ | |
| 222 | + fossil_print("\n%s", z); | |
| 223 | + sumW = w; | |
| 224 | + }else{ | |
| 225 | + fossil_print(", %s", z); | |
| 226 | + sumW += w+2; | |
| 227 | + } | |
| 228 | + } | |
| 229 | + fossil_free(zSql); | |
| 230 | + sqlite3_finalize(pStmt); | |
| 231 | + if( sumW>0 ) fossil_print("\n"); | |
| 176 | 232 | }else{ |
| 177 | 233 | /* Column mode with just filenames */ |
| 178 | 234 | int nCol, mxWidth, iRow, nSp, nRow; |
| 179 | 235 | char *zSql; |
| 180 | - char *zOrderBy; | |
| 181 | - sqlite3_prepare_v2(db, "SELECT max(length(fn)),count(*) FROM ls",-1, | |
| 236 | + sqlite3_prepare_v2(db, "SELECT max(dlen),count(*) FROM ls",-1, | |
| 182 | 237 | &pStmt,0); |
| 183 | 238 | if( sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 184 | 239 | mxWidth = sqlite3_column_int(pStmt,0); |
| 185 | 240 | nCol = (terminal_get_width(80)+1)/(mxWidth+2); |
| 186 | 241 | if( nCol<1 ) nCol = 1; |
| @@ -189,17 +244,14 @@ | ||
| 189 | 244 | nCol = 1; |
| 190 | 245 | mxWidth = 100; |
| 191 | 246 | nRow = 2000000; |
| 192 | 247 | } |
| 193 | 248 | sqlite3_finalize(pStmt); |
| 194 | - zOrderBy = mprintf("%s %s", (mFlags & 0x04)!=0 ? "mtime": "fn", | |
| 195 | - bDesc ? "DESC" : "ASC"); | |
| 196 | 249 | zSql = mprintf("WITH sfn(ii,fn,mtime) AS " |
| 197 | 250 | "(SELECT row_number()OVER(ORDER BY %s)-1,fn,mtime FROM ls)" |
| 198 | 251 | "SELECT ii/%d,ii%%%d, fn FROM sfn ORDER BY 2,1", |
| 199 | - zOrderBy, nRow, nRow); | |
| 200 | - fossil_free(zOrderBy); | |
| 252 | + xsystem_ls_orderby(mFlags), nRow, nRow); | |
| 201 | 253 | sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); |
| 202 | 254 | nSp = 0; |
| 203 | 255 | iRow = -1; |
| 204 | 256 | while( sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 205 | 257 | const char *zFN = (const char*)sqlite3_column_text(pStmt, 2); |
| @@ -221,13 +273,16 @@ | ||
| 221 | 273 | |
| 222 | 274 | /* List files "ls" |
| 223 | 275 | ** Options: |
| 224 | 276 | ** |
| 225 | 277 | ** -a Show files that begin with "." |
| 278 | +** -d Show just directory names, not content | |
| 226 | 279 | ** -l Long listing |
| 280 | +** -m Comma-separated list | |
| 227 | 281 | ** -r Reverse sort |
| 228 | -** -t Sort by mtime | |
| 282 | +** -S Sort by size, largest first | |
| 283 | +** -t Sort by mtime, newest first | |
| 229 | 284 | */ |
| 230 | 285 | void xsystem_ls(int argc, char **argv){ |
| 231 | 286 | int i, rc; |
| 232 | 287 | sqlite3 *db; |
| 233 | 288 | sqlite3_stmt *pStmt = 0; |
| @@ -237,35 +292,36 @@ | ||
| 237 | 292 | int needBlankLine = 0; |
| 238 | 293 | rc = sqlite3_open(":memory:", &db); |
| 239 | 294 | if( rc || db==0 ){ |
| 240 | 295 | fossil_fatal("Cannot open in-memory database"); |
| 241 | 296 | } |
| 242 | - sqlite3_exec(db, "CREATE TABLE ls(fn,mtime,size,mode);", 0,0,0); | |
| 243 | - rc = sqlite3_prepare_v2(db, "INSERT INTO ls VALUES(?1,?2,?3,?4)", | |
| 297 | + sqlite3_exec(db, "CREATE TABLE ls(fn,mtime,size,mode,dlen);", 0,0,0); | |
| 298 | + rc = sqlite3_prepare_v2(db, "INSERT INTO ls VALUES(?1,?2,?3,?4,?5)", | |
| 244 | 299 | -1, &pStmt, 0); |
| 245 | 300 | if( rc || db==0 ){ |
| 246 | 301 | fossil_fatal("Cannot prepare INSERT statement"); |
| 247 | 302 | } |
| 248 | 303 | for(i=1; i<argc; i++){ |
| 249 | 304 | const char *z = argv[i]; |
| 250 | 305 | if( z[0]=='-' ){ |
| 251 | 306 | int k; |
| 252 | 307 | for(k=1; z[k]; k++){ |
| 253 | - if( z[k]=='l' ){ | |
| 254 | - mFlags |= 0x01; | |
| 255 | - }else if( z[k]=='r' ){ | |
| 256 | - mFlags |= 0x02; | |
| 257 | - }else if( z[k]=='t' ){ | |
| 258 | - mFlags |= 0x04; | |
| 259 | - }else if( z[k]=='a' ){ | |
| 260 | - mFlags |= 0x08; | |
| 261 | - }else{ | |
| 262 | - fossil_fatal("unknown option: -%c", z[k]); | |
| 308 | + switch( z[k] ){ | |
| 309 | + case 'a': mFlags |= LS_ALL; break; | |
| 310 | + case 'd': mFlags |= LS_DIRONLY; break; | |
| 311 | + case 'l': mFlags |= LS_LONG; break; | |
| 312 | + case 'm': mFlags |= LS_COMMA; break; | |
| 313 | + case 'r': mFlags |= LS_REVERSE; break; | |
| 314 | + case 'S': mFlags |= LS_SIZE; break; | |
| 315 | + case 't': mFlags |= LS_MTIME; break; | |
| 316 | + default: { | |
| 317 | + fossil_fatal("unknown option: -%c", z[k]); | |
| 318 | + } | |
| 263 | 319 | } |
| 264 | 320 | } |
| 265 | 321 | }else{ |
| 266 | - if( file_isdir(z, ExtFILE)==1 ){ | |
| 322 | + if( (mFlags & LS_DIRONLY)==0 && file_isdir(z, ExtFILE)==1 ){ | |
| 267 | 323 | nDir++; |
| 268 | 324 | }else{ |
| 269 | 325 | nFile++; |
| 270 | 326 | xsystem_ls_insert(pStmt, z, mFlags); |
| 271 | 327 | } |
| @@ -310,13 +366,17 @@ | ||
| 310 | 366 | "Show the current system time and date\n" |
| 311 | 367 | }, |
| 312 | 368 | { "ls", xsystem_ls, |
| 313 | 369 | "[OPTIONS] [PATH] ...\n" |
| 314 | 370 | "Options:\n" |
| 315 | - " -l Long format\n" | |
| 316 | - " -r Reverse sort order\n" | |
| 317 | - " -t Sort by mtime\n" | |
| 371 | + " -a Show files that begin with '.'\n" | |
| 372 | + " -d Show just directory names, not content\n" | |
| 373 | + " -l Long listing\n" | |
| 374 | + " -m Comma-separated list\n" | |
| 375 | + " -r Reverse sort order\n" | |
| 376 | + " -S Sort by size, largest first\n" | |
| 377 | + " -t Sort by mtime, newest first\n" | |
| 318 | 378 | }, |
| 319 | 379 | { "pwd", xsystem_pwd, |
| 320 | 380 | "\n" |
| 321 | 381 | "Show the Present Working Directory name\n" |
| 322 | 382 | }, |
| 323 | 383 |
| --- src/xsystem.c | |
| +++ src/xsystem.c | |
| @@ -66,10 +66,21 @@ | |
| 66 | } |
| 67 | } |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | /* Helper function for xsystem_ls(): Make entries in the LS table |
| 72 | ** for every file or directory zName. |
| 73 | ** |
| 74 | ** If zName is a directory, load all files contained within that directory. |
| 75 | ** If zName is just a file, load only that file. |
| @@ -84,14 +95,16 @@ | |
| 84 | int nList; |
| 85 | int i; |
| 86 | const char *zPrefix; |
| 87 | switch( file_isdir(zName, ExtFILE) ){ |
| 88 | case 1: { /* A directory */ |
| 89 | azList = 0; |
| 90 | nList = file_directory_list(zName, 0, (mFlags & 0x08)==0, 0, &azList); |
| 91 | zPrefix = fossil_strcmp(zName,".") ? zName : 0; |
| 92 | break; |
| 93 | } |
| 94 | case 2: { /* A file */ |
| 95 | aList[0] = (char*)zName; |
| 96 | aList[1] = 0; |
| 97 | azList = aList; |
| @@ -110,38 +123,56 @@ | |
| 110 | sqlite3_int64 mtime = file_mtime(zFile, ExtFILE); |
| 111 | sqlite3_bind_text(pStmt, 1, zFile, -1, SQLITE_TRANSIENT); |
| 112 | sqlite3_bind_int64(pStmt, 2, mtime); |
| 113 | sqlite3_bind_int64(pStmt, 3, sz); |
| 114 | sqlite3_bind_int(pStmt, 4, mode); |
| 115 | sqlite3_step(pStmt); |
| 116 | sqlite3_reset(pStmt); |
| 117 | if( zPrefix ) fossil_free(zFile); |
| 118 | } |
| 119 | if( azList!=aList ){ |
| 120 | file_directory_list_free(azList); |
| 121 | } |
| 122 | } |
| 123 | |
| 124 | /* |
| 125 | ** Show ls output information for content in the LS table |
| 126 | */ |
| 127 | static void xsystem_ls_render( |
| 128 | sqlite3 *db, |
| 129 | int mFlags |
| 130 | ){ |
| 131 | sqlite3_stmt *pStmt; |
| 132 | int bDesc = (mFlags & 0x02)!=0; |
| 133 | if( mFlags & 0x04 ) bDesc = !bDesc; |
| 134 | if( (mFlags & 0x01)!=0 ){ |
| 135 | /* Long mode */ |
| 136 | char *zSql; |
| 137 | zSql = mprintf( |
| 138 | "SELECT mode, size, strftime('%%Y-%%m-%%d %%H:%%M'," |
| 139 | "mtime,'unixepoch'), fn" |
| 140 | " FROM ls ORDER BY %s %s", |
| 141 | (mFlags & 0x04)!=0 ? "mtime" : "fn", |
| 142 | bDesc ? "DESC" : "ASC"); |
| 143 | sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); |
| 144 | while( sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 145 | char zMode[12]; |
| 146 | const char *zName = (const char*)sqlite3_column_text(pStmt, 3); |
| 147 | int mode = sqlite3_column_int(pStmt, 0); |
| @@ -171,16 +202,40 @@ | |
| 171 | sqlite3_column_int64(pStmt, 1), |
| 172 | sqlite3_column_text(pStmt, 2), |
| 173 | zName); |
| 174 | } |
| 175 | sqlite3_finalize(pStmt); |
| 176 | }else{ |
| 177 | /* Column mode with just filenames */ |
| 178 | int nCol, mxWidth, iRow, nSp, nRow; |
| 179 | char *zSql; |
| 180 | char *zOrderBy; |
| 181 | sqlite3_prepare_v2(db, "SELECT max(length(fn)),count(*) FROM ls",-1, |
| 182 | &pStmt,0); |
| 183 | if( sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 184 | mxWidth = sqlite3_column_int(pStmt,0); |
| 185 | nCol = (terminal_get_width(80)+1)/(mxWidth+2); |
| 186 | if( nCol<1 ) nCol = 1; |
| @@ -189,17 +244,14 @@ | |
| 189 | nCol = 1; |
| 190 | mxWidth = 100; |
| 191 | nRow = 2000000; |
| 192 | } |
| 193 | sqlite3_finalize(pStmt); |
| 194 | zOrderBy = mprintf("%s %s", (mFlags & 0x04)!=0 ? "mtime": "fn", |
| 195 | bDesc ? "DESC" : "ASC"); |
| 196 | zSql = mprintf("WITH sfn(ii,fn,mtime) AS " |
| 197 | "(SELECT row_number()OVER(ORDER BY %s)-1,fn,mtime FROM ls)" |
| 198 | "SELECT ii/%d,ii%%%d, fn FROM sfn ORDER BY 2,1", |
| 199 | zOrderBy, nRow, nRow); |
| 200 | fossil_free(zOrderBy); |
| 201 | sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); |
| 202 | nSp = 0; |
| 203 | iRow = -1; |
| 204 | while( sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 205 | const char *zFN = (const char*)sqlite3_column_text(pStmt, 2); |
| @@ -221,13 +273,16 @@ | |
| 221 | |
| 222 | /* List files "ls" |
| 223 | ** Options: |
| 224 | ** |
| 225 | ** -a Show files that begin with "." |
| 226 | ** -l Long listing |
| 227 | ** -r Reverse sort |
| 228 | ** -t Sort by mtime |
| 229 | */ |
| 230 | void xsystem_ls(int argc, char **argv){ |
| 231 | int i, rc; |
| 232 | sqlite3 *db; |
| 233 | sqlite3_stmt *pStmt = 0; |
| @@ -237,35 +292,36 @@ | |
| 237 | int needBlankLine = 0; |
| 238 | rc = sqlite3_open(":memory:", &db); |
| 239 | if( rc || db==0 ){ |
| 240 | fossil_fatal("Cannot open in-memory database"); |
| 241 | } |
| 242 | sqlite3_exec(db, "CREATE TABLE ls(fn,mtime,size,mode);", 0,0,0); |
| 243 | rc = sqlite3_prepare_v2(db, "INSERT INTO ls VALUES(?1,?2,?3,?4)", |
| 244 | -1, &pStmt, 0); |
| 245 | if( rc || db==0 ){ |
| 246 | fossil_fatal("Cannot prepare INSERT statement"); |
| 247 | } |
| 248 | for(i=1; i<argc; i++){ |
| 249 | const char *z = argv[i]; |
| 250 | if( z[0]=='-' ){ |
| 251 | int k; |
| 252 | for(k=1; z[k]; k++){ |
| 253 | if( z[k]=='l' ){ |
| 254 | mFlags |= 0x01; |
| 255 | }else if( z[k]=='r' ){ |
| 256 | mFlags |= 0x02; |
| 257 | }else if( z[k]=='t' ){ |
| 258 | mFlags |= 0x04; |
| 259 | }else if( z[k]=='a' ){ |
| 260 | mFlags |= 0x08; |
| 261 | }else{ |
| 262 | fossil_fatal("unknown option: -%c", z[k]); |
| 263 | } |
| 264 | } |
| 265 | }else{ |
| 266 | if( file_isdir(z, ExtFILE)==1 ){ |
| 267 | nDir++; |
| 268 | }else{ |
| 269 | nFile++; |
| 270 | xsystem_ls_insert(pStmt, z, mFlags); |
| 271 | } |
| @@ -310,13 +366,17 @@ | |
| 310 | "Show the current system time and date\n" |
| 311 | }, |
| 312 | { "ls", xsystem_ls, |
| 313 | "[OPTIONS] [PATH] ...\n" |
| 314 | "Options:\n" |
| 315 | " -l Long format\n" |
| 316 | " -r Reverse sort order\n" |
| 317 | " -t Sort by mtime\n" |
| 318 | }, |
| 319 | { "pwd", xsystem_pwd, |
| 320 | "\n" |
| 321 | "Show the Present Working Directory name\n" |
| 322 | }, |
| 323 |
| --- src/xsystem.c | |
| +++ src/xsystem.c | |
| @@ -66,10 +66,21 @@ | |
| 66 | } |
| 67 | } |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | /* |
| 72 | ** Bit values for the mFlags paramater to "ls" |
| 73 | */ |
| 74 | #define LS_LONG 0x001 /* -l Long format - one object per line */ |
| 75 | #define LS_REVERSE 0x002 /* -r Reverse the sort order */ |
| 76 | #define LS_MTIME 0x004 /* -t Sort by mtime, newest first */ |
| 77 | #define LS_SIZE 0x008 /* -S Sort by size, largest first */ |
| 78 | #define LS_COMMA 0x010 /* -m Comma-separated list */ |
| 79 | #define LS_DIRONLY 0x020 /* -d Show just directory name, not content */ |
| 80 | #define LS_ALL 0x040 /* -a Show all entries */ |
| 81 | |
| 82 | /* Helper function for xsystem_ls(): Make entries in the LS table |
| 83 | ** for every file or directory zName. |
| 84 | ** |
| 85 | ** If zName is a directory, load all files contained within that directory. |
| 86 | ** If zName is just a file, load only that file. |
| @@ -84,14 +95,16 @@ | |
| 95 | int nList; |
| 96 | int i; |
| 97 | const char *zPrefix; |
| 98 | switch( file_isdir(zName, ExtFILE) ){ |
| 99 | case 1: { /* A directory */ |
| 100 | if( (mFlags & LS_DIRONLY)==0 ){ |
| 101 | azList = 0; |
| 102 | nList = file_directory_list(zName, 0, (mFlags & LS_ALL)==0, 0, &azList); |
| 103 | zPrefix = fossil_strcmp(zName,".") ? zName : 0; |
| 104 | break; |
| 105 | } |
| 106 | } |
| 107 | case 2: { /* A file */ |
| 108 | aList[0] = (char*)zName; |
| 109 | aList[1] = 0; |
| 110 | azList = aList; |
| @@ -110,38 +123,56 @@ | |
| 123 | sqlite3_int64 mtime = file_mtime(zFile, ExtFILE); |
| 124 | sqlite3_bind_text(pStmt, 1, zFile, -1, SQLITE_TRANSIENT); |
| 125 | sqlite3_bind_int64(pStmt, 2, mtime); |
| 126 | sqlite3_bind_int64(pStmt, 3, sz); |
| 127 | sqlite3_bind_int(pStmt, 4, mode); |
| 128 | sqlite3_bind_int64(pStmt, 5, strlen(zFile)); |
| 129 | /* TODO: wcwidth()------^^^^^^ */ |
| 130 | sqlite3_step(pStmt); |
| 131 | sqlite3_reset(pStmt); |
| 132 | if( zPrefix ) fossil_free(zFile); |
| 133 | } |
| 134 | if( azList!=aList ){ |
| 135 | file_directory_list_free(azList); |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | /* |
| 140 | ** Return arguments to ORDER BY that will correctly sort the entires. |
| 141 | */ |
| 142 | static const char *xsystem_ls_orderby(int mFlags){ |
| 143 | static const char *zSortTypes[] = { |
| 144 | "fn COLLATE NOCASE", |
| 145 | "mtime DESC", |
| 146 | "size DESC", |
| 147 | "fn COLLATE NOCASE DESC", |
| 148 | "mtime", |
| 149 | "size" |
| 150 | }; |
| 151 | int i = 0; |
| 152 | if( mFlags & LS_MTIME ) i = 1; |
| 153 | if( mFlags & LS_SIZE ) i = 2; |
| 154 | if( mFlags & LS_REVERSE ) i += 3; |
| 155 | return zSortTypes[i]; |
| 156 | } |
| 157 | |
| 158 | /* |
| 159 | ** Show ls output information for content in the LS table |
| 160 | */ |
| 161 | static void xsystem_ls_render( |
| 162 | sqlite3 *db, |
| 163 | int mFlags |
| 164 | ){ |
| 165 | sqlite3_stmt *pStmt; |
| 166 | if( (mFlags & LS_LONG)!=0 ){ |
| 167 | /* Long mode */ |
| 168 | char *zSql; |
| 169 | zSql = mprintf( |
| 170 | "SELECT mode, size, strftime('%%Y-%%m-%%d %%H:%%M'," |
| 171 | "mtime,'unixepoch'), fn" |
| 172 | " FROM ls ORDER BY %s", |
| 173 | xsystem_ls_orderby(mFlags)); |
| 174 | sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); |
| 175 | while( sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 176 | char zMode[12]; |
| 177 | const char *zName = (const char*)sqlite3_column_text(pStmt, 3); |
| 178 | int mode = sqlite3_column_int(pStmt, 0); |
| @@ -171,16 +202,40 @@ | |
| 202 | sqlite3_column_int64(pStmt, 1), |
| 203 | sqlite3_column_text(pStmt, 2), |
| 204 | zName); |
| 205 | } |
| 206 | sqlite3_finalize(pStmt); |
| 207 | }else if( (mFlags & LS_COMMA)!=0 ){ |
| 208 | /* Comma-separate list */ |
| 209 | int mx = terminal_get_width(80); |
| 210 | int sumW = 0; |
| 211 | char *zSql; |
| 212 | zSql = mprintf("SELECT fn, dlen FROM ls ORDER BY %s", |
| 213 | xsystem_ls_orderby(mFlags)); |
| 214 | sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); |
| 215 | while( sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 216 | const char *z = (const char*)sqlite3_column_text(pStmt, 0); |
| 217 | int w = sqlite3_column_int(pStmt, 1); |
| 218 | if( sumW==0 ){ |
| 219 | fossil_print("%s", z); |
| 220 | sumW = w; |
| 221 | }else if( sumW + w + 2 >= mx ){ |
| 222 | fossil_print("\n%s", z); |
| 223 | sumW = w; |
| 224 | }else{ |
| 225 | fossil_print(", %s", z); |
| 226 | sumW += w+2; |
| 227 | } |
| 228 | } |
| 229 | fossil_free(zSql); |
| 230 | sqlite3_finalize(pStmt); |
| 231 | if( sumW>0 ) fossil_print("\n"); |
| 232 | }else{ |
| 233 | /* Column mode with just filenames */ |
| 234 | int nCol, mxWidth, iRow, nSp, nRow; |
| 235 | char *zSql; |
| 236 | sqlite3_prepare_v2(db, "SELECT max(dlen),count(*) FROM ls",-1, |
| 237 | &pStmt,0); |
| 238 | if( sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 239 | mxWidth = sqlite3_column_int(pStmt,0); |
| 240 | nCol = (terminal_get_width(80)+1)/(mxWidth+2); |
| 241 | if( nCol<1 ) nCol = 1; |
| @@ -189,17 +244,14 @@ | |
| 244 | nCol = 1; |
| 245 | mxWidth = 100; |
| 246 | nRow = 2000000; |
| 247 | } |
| 248 | sqlite3_finalize(pStmt); |
| 249 | zSql = mprintf("WITH sfn(ii,fn,mtime) AS " |
| 250 | "(SELECT row_number()OVER(ORDER BY %s)-1,fn,mtime FROM ls)" |
| 251 | "SELECT ii/%d,ii%%%d, fn FROM sfn ORDER BY 2,1", |
| 252 | xsystem_ls_orderby(mFlags), nRow, nRow); |
| 253 | sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); |
| 254 | nSp = 0; |
| 255 | iRow = -1; |
| 256 | while( sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 257 | const char *zFN = (const char*)sqlite3_column_text(pStmt, 2); |
| @@ -221,13 +273,16 @@ | |
| 273 | |
| 274 | /* List files "ls" |
| 275 | ** Options: |
| 276 | ** |
| 277 | ** -a Show files that begin with "." |
| 278 | ** -d Show just directory names, not content |
| 279 | ** -l Long listing |
| 280 | ** -m Comma-separated list |
| 281 | ** -r Reverse sort |
| 282 | ** -S Sort by size, largest first |
| 283 | ** -t Sort by mtime, newest first |
| 284 | */ |
| 285 | void xsystem_ls(int argc, char **argv){ |
| 286 | int i, rc; |
| 287 | sqlite3 *db; |
| 288 | sqlite3_stmt *pStmt = 0; |
| @@ -237,35 +292,36 @@ | |
| 292 | int needBlankLine = 0; |
| 293 | rc = sqlite3_open(":memory:", &db); |
| 294 | if( rc || db==0 ){ |
| 295 | fossil_fatal("Cannot open in-memory database"); |
| 296 | } |
| 297 | sqlite3_exec(db, "CREATE TABLE ls(fn,mtime,size,mode,dlen);", 0,0,0); |
| 298 | rc = sqlite3_prepare_v2(db, "INSERT INTO ls VALUES(?1,?2,?3,?4,?5)", |
| 299 | -1, &pStmt, 0); |
| 300 | if( rc || db==0 ){ |
| 301 | fossil_fatal("Cannot prepare INSERT statement"); |
| 302 | } |
| 303 | for(i=1; i<argc; i++){ |
| 304 | const char *z = argv[i]; |
| 305 | if( z[0]=='-' ){ |
| 306 | int k; |
| 307 | for(k=1; z[k]; k++){ |
| 308 | switch( z[k] ){ |
| 309 | case 'a': mFlags |= LS_ALL; break; |
| 310 | case 'd': mFlags |= LS_DIRONLY; break; |
| 311 | case 'l': mFlags |= LS_LONG; break; |
| 312 | case 'm': mFlags |= LS_COMMA; break; |
| 313 | case 'r': mFlags |= LS_REVERSE; break; |
| 314 | case 'S': mFlags |= LS_SIZE; break; |
| 315 | case 't': mFlags |= LS_MTIME; break; |
| 316 | default: { |
| 317 | fossil_fatal("unknown option: -%c", z[k]); |
| 318 | } |
| 319 | } |
| 320 | } |
| 321 | }else{ |
| 322 | if( (mFlags & LS_DIRONLY)==0 && file_isdir(z, ExtFILE)==1 ){ |
| 323 | nDir++; |
| 324 | }else{ |
| 325 | nFile++; |
| 326 | xsystem_ls_insert(pStmt, z, mFlags); |
| 327 | } |
| @@ -310,13 +366,17 @@ | |
| 366 | "Show the current system time and date\n" |
| 367 | }, |
| 368 | { "ls", xsystem_ls, |
| 369 | "[OPTIONS] [PATH] ...\n" |
| 370 | "Options:\n" |
| 371 | " -a Show files that begin with '.'\n" |
| 372 | " -d Show just directory names, not content\n" |
| 373 | " -l Long listing\n" |
| 374 | " -m Comma-separated list\n" |
| 375 | " -r Reverse sort order\n" |
| 376 | " -S Sort by size, largest first\n" |
| 377 | " -t Sort by mtime, newest first\n" |
| 378 | }, |
| 379 | { "pwd", xsystem_pwd, |
| 380 | "\n" |
| 381 | "Show the Present Working Directory name\n" |
| 382 | }, |
| 383 |