00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <errno.h>
00022 #include <string.h>
00023 #include <stdlib.h>
00024
00025 #include "htstring.h"
00026 #include "snprintf.h"
00027 #include "vfsview.h"
00028
00029 static vfs_extra *make_vfs_extra(const char *name, pstat_t s)
00030 {
00031 vfs_extra *e = (vfs_extra*)malloc(sizeof *e);
00032 e->stat = s;
00033 e->name = strdup(name);
00034 return e;
00035 }
00036
00037 static void free_vfs_extra(vfs_extra *e)
00038 {
00039 free(e->name);
00040 free(e);
00041 }
00042
00043 static Vfs *vfslistbox_vfs;
00044
00045 static int vfslistbox_fncmp(const char *a, const char *b)
00046 {
00047 if (strchr("/ *@=#+", *a) && (*b != *a)) {
00048 if (*a == '/') return -1;
00049 if (*b == '/') return 1;
00050 return *b-*a;
00051 }
00052 return vfslistbox_vfs->compareFilenames(a,b);
00053 }
00054
00055
00056
00057
00058
00059 #define VFSV_FORMAT_NAME 0
00060 #define VFSV_FORMAT_SIZE 1
00061 #define VFSV_FORMAT_BSIZE 2
00062 #define VFSV_FORMAT_TYPE 3
00063 #define VFSV_FORMAT_MTIME 4
00064 #define VFSV_FORMAT_ATIME 5
00065 #define VFSV_FORMAT_CTIME 6
00066 #define VFSV_FORMAT_PERM 7
00067 #define VFSV_FORMAT_MODE 8
00068 #define VFSV_FORMAT_NLINK 9
00069 #define VFSV_FORMAT_NGID 10
00070 #define VFSV_FORMAT_NUID 11
00071 #define VFSV_FORMAT_OWNER 12
00072 #define VFSV_FORMAT_GROUP 13
00073 #define VFSV_FORMAT_INODE 14
00074 #define VFSV_FORMAT_SPACE 15
00075 #define VFSV_FORMAT_MARK 16
00076 #define VFSV_FORMAT_SEPARATOR 17
00077 #define VFSV_FORMAT_DESC 18
00078 #define VFSV_FORMAT_RMTIME 19
00079 #define VFSV_FORMAT_RATIME 20
00080 #define VFSV_FORMAT_RCTIME 21
00081
00082 static char *format_property[VFSV_FORMAT_PROPERTIES]={
00083 "name", "size",
00084 "bsize", "type",
00085 "mtime", "atime",
00086 "ctime", "perm",
00087 "mode", "nlink",
00088 "ngid", "nuid",
00089 "owner", "group",
00090 "inode", "space",
00091 "mark", "|",
00092 "desc", "rmtime",
00093 "ratime", "rctime"
00094 };
00095
00096 #define MAKE_DISPLAY_FORMAT(type) MAKE_DISPLAY_FORMAT_MIN_WIDTH(type, 0)
00097 #define MAKE_DISPLAY_FORMAT_MIN_WIDTH(type, width) (((width) << 16) | (type))
00098 #define MAKE_DISPLAY_FORMAT_FIXED_WIDTH(type, width) ((((width) | 0x8000) << 16) | (type))
00099
00100 #define GET_DISPLAY_FORMAT_TYPE(dfmt) ((dfmt) & 0xffff)
00101 #define GET_DISPLAY_FORMAT_SIZE(dfmt) (((dfmt)>>16) & 0x7fff)
00102 #define GET_DISPLAY_FORMAT_IS_FIXED_SIZE(dfmt) ((dfmt) & 0x80000000)
00103 #define GET_DISPLAY_FORMAT_IS_MIN_SIZE(dfmt) (!GET_DISPLAY_FORMAT_IS_FIXED_SIZE(dfmt))
00104
00105 void VfsListbox::init(bounds *b, ht_list *vl, ht_text *sp)
00106 {
00107 cvfs = NULL;
00108 show_pos = sp;
00109 ht_itext_listbox::init(b);
00110 vfs_list = vl;
00111 cdir[0] = 0;
00112 cproto[0] = 0;
00113 config_changed();
00114 }
00115
00116 void VfsListbox::done()
00117 {
00118 ht_itext_listbox::done();
00119 }
00120
00121 int VfsListbox::changeDir(const char *dir)
00122 {
00123 char url[VFS_URL_MAX];
00124 ht_snprintf(url, sizeof url, "%s:%s", cproto, dir);
00125 return changeURL(url);
00126 }
00127
00128 int VfsListbox::changeURL(const char *url)
00129 {
00130 int c = vfs_list->count();
00131 const char *pend = strchr(url, ':');
00132 Vfs *newVfs = NULL;
00133 const char *pathptr = url;
00134 if (pend) {
00135
00136 char protoname[VFS_PROTO_MAX+1];
00137 if (pend-url > VFS_PROTO_MAX) return EINVAL;
00138 strncpy(protoname, url, pend-url);
00139 protoname[pend-url] = 0;
00140 for (int i=0; i<c; i++) {
00141 Vfs *v = (Vfs*)vfs_list->get(i);
00142 if (strcmp(protoname, v->getProtoName()) == 0) {
00143 newVfs = v;
00144 break;
00145 }
00146 }
00147 if (!newVfs) return EINVAL;
00148 pathptr = pend+1;
00149 } else {
00150
00151 if (!c) return EINVAL;
00152 newVfs = (Vfs*)vfs_list->get(0);
00153 }
00154 char path[VFS_DIR_MAX+1];
00155 if (sys_common_canonicalize(path, pathptr, NULL, newVfs->isPathDelim()) !=0) return EINVAL;
00156
00157
00158 int l = strlen(path)-1;
00159 if ((l==-1) || !newVfs->isPathDelim()(path[l])) {
00160 char delim = newVfs->isPathDelim()('/') ? '/' : '\\';
00161 if (l+2 >= (int)sizeof path) return EINVAL;
00162 path[l+1] = delim;
00163 path[l+2] = 0;
00164 }
00165
00166
00167 pstat_t s;
00168 int e;
00169 if ((e = newVfs->pstat(&s, path)) != 0) return e;
00170 if (!(s.caps & pstat_mode_type) || !(HT_S_ISDIR(s.mode))) return ENOTDIR;
00171
00172
00173 char spath[VFS_DIR_MAX+1];
00174 char spath2[VFS_DIR_MAX+1];
00175 strcpy(spath, cdir);
00176 char *p = strend(spath)-2;
00177 bool cdpp = false;
00178 while (p >= spath) {
00179 if (newVfs->isPathDelim()(*p)) {
00180 strcpy(spath2, p+1);
00181 *(strend(spath2)-1) = 0;
00182 *(p+1) = 0;
00183 if (newVfs->compareFilenames(path, spath) == 0) {
00184 cdpp = true;
00185 }
00186 break;
00187 }
00188 p--;
00189 }
00190
00191
00192 strncpy(cproto, newVfs->getProtoName(), sizeof cproto-1);
00193 strncpy(cdir, path, sizeof cdir-1);
00194 cvfs = newVfs;
00195
00196 reread();
00197
00198 update();
00199 if (dfmt_quickfind != -1) {
00200 ht_text_listbox_sort_order so[1];
00201 vfslistbox_vfs = cvfs;
00202 so[0].col = dfmt_quickfind;
00203 so[0].compare_func = vfslistbox_fncmp;
00204 sort(1, so);
00205 }
00206
00207 gotoItemByPosition(0);
00208
00209 if (cdpp) {
00210 ht_text_listbox_item *i = (ht_text_listbox_item*)getFirst();
00211 while (i) {
00212 vfs_extra *x = (vfs_extra*)i->extra_data;
00213 if (newVfs->compareFilenames(x->name, spath2) == 0) {
00214 gotoItemByEntry(i);
00215 break;
00216 }
00217 i = (ht_text_listbox_item*)getNext(i);
00218 }
00219 }
00220 rearrangeColumns();
00221
00222 return 0;
00223 }
00224
00225 void VfsListbox::config_changed()
00226 {
00227 ht_text_listbox::config_changed();
00228 char *dfmt = get_config_string("misc/vfs display format");
00229 setDisplayFormat(dfmt ? dfmt : (char*)"name");
00230 if (dfmt) free(dfmt);
00231 }
00232
00233 int VfsListbox::cursorAdjust()
00234 {
00235 return 1;
00236 }
00237
00238 void VfsListbox::freeExtraData(void *extra_data)
00239 {
00240 if (extra_data) {
00241 free_vfs_extra((vfs_extra*)extra_data);
00242 }
00243 }
00244
00245 const char *VfsListbox::getCurDir()
00246 {
00247 return cdir;
00248 }
00249
00250 const char *VfsListbox::getCurProto()
00251 {
00252 return cproto;
00253 }
00254
00255 Vfs *VfsListbox::getCurVfs()
00256 {
00257 return cvfs;
00258 }
00259
00260 void VfsListbox::handlemsg(htmsg *msg)
00261 {
00262 switch (msg->msg) {
00263 case msg_keypressed:
00264 switch (msg->data1.integer) {
00265 case K_Return: {
00266 if (count && selectEntry(e_cursor)) {
00267 clearmsg(msg);
00268 return;
00269 }
00270 break;
00271 }
00272 }
00273 break;
00274 }
00275 return ht_text_listbox::handlemsg(msg);
00276 }
00277
00278 void VfsListbox::setDisplayFormat(char *fmt)
00279 {
00280
00281
00282
00283
00284 int type;
00285 dfmt_cols = 0;
00286 dfmt_props = 0;
00287 dfmt_quickfind = -1;
00288 while ((fmt = translateProp(fmt, &type))) {
00289 if (type == VFSV_FORMAT_SEPARATOR) {
00290 if (++dfmt_cols == VFSV_FORMAT_MAX_COLS) break;
00291 } else {
00292 if (*fmt == ':') {
00293 fmt++;
00294 unsigned int width = strtoul(fmt, &fmt, 10);
00295 if (*fmt == '+') {
00296 fmt++;
00297 dfmt_prop[dfmt_props] = MAKE_DISPLAY_FORMAT_MIN_WIDTH(type, width);
00298 } else {
00299 dfmt_prop[dfmt_props] = MAKE_DISPLAY_FORMAT_FIXED_WIDTH(type, width);
00300 }
00301 } else {
00302 dfmt_prop[dfmt_props] = MAKE_DISPLAY_FORMAT(type);
00303 }
00304 dfmt_prop2colidx[dfmt_props] = dfmt_cols;
00305 if (++dfmt_props == VFSV_FORMAT_MAX_COLS) break;
00306 }
00307
00308 while ((*fmt == ',') || (*fmt == ' ')) fmt++;
00309
00310 if ((type == VFSV_FORMAT_NAME) && (dfmt_quickfind == -1)) {
00311 dfmt_quickfind = dfmt_cols;
00312 }
00313 }
00314 ++dfmt_cols;
00315 cols = dfmt_cols;
00316 if (dfmt_quickfind != -1) keycol = dfmt_quickfind;
00317 else keycol = 0;
00318 rearrangeColumns();
00319 }
00320
00321 bool VfsListbox::selectEntry(void *entry)
00322 {
00323 ht_text_listbox_item *i = (ht_text_listbox_item*)entry;
00324 if (i->extra_data) {
00325 vfs_extra *e = (vfs_extra*)i->extra_data;
00326 if (e->stat.caps & pstat_mode_type) {
00327 if (HT_S_ISDIR(e->stat.mode)) {
00328 char d[VFS_DIR_MAX];
00329 ht_snprintf(d, sizeof d, "%s%s", cdir, e->name);
00330 changeDir(d);
00331 update();
00332 return true;
00333 }
00334 }
00335 }
00336 return false;
00337 }
00338
00339 char *VfsListbox::translateProp(char *fmt, int *type)
00340 {
00341 for (int i=0; i<VFSV_FORMAT_PROPERTIES; i++) {
00342 int l = strlen(format_property[i]);
00343 if (strncmp(fmt, format_property[i], l)==0) {
00344 *type = i;
00345 return fmt+l;
00346 }
00347 }
00348 return 0;
00349 }
00350
00351 void VfsListbox::reread()
00352 {
00353 #define VFSV_FORMAT_MAX_LENGTH 256
00354 if (!cvfs) return;
00355 clearAll();
00356 char *strs[VFSV_FORMAT_MAX_COLS];
00357 for (int i=0; i<dfmt_cols; i++) {
00358 strs[i] = (char*)malloc(VFSV_FORMAT_MAX_LENGTH);
00359 }
00360 pfind_t f;
00361 int k = 0;
00362 if (cvfs->findFirst(cdir, &f)) {
00363 do {
00364 if (strcmp(f.name, ".") != 0) {
00365 for (int i=0; i<dfmt_cols; i++) {
00366 *(strs[i]) = 0;
00367 }
00368 for (int i=0; i<dfmt_props; i++) {
00369 int z = dfmt_prop2colidx[i];
00370 int l = strlen(strs[z]);
00371 renderEntry(strs[z]+l, VFSV_FORMAT_MAX_LENGTH-l, dfmt_prop[i], f.name, f.stat);
00372 }
00373 insert_str_extra(k++, make_vfs_extra(f.name, f.stat), strs);
00374 }
00375 } while (cvfs->findNext(&f));
00376 }
00377 cvfs->findClose(&f);
00378 for (int i=0; i<dfmt_cols; i++) {
00379 free(strs[i]);
00380 }
00381 }
00382
00383 void *VfsListbox::quickfind(char *s)
00384 {
00385 ht_text_listbox_item *item = (ht_text_listbox_item *)e_cursor;
00386 for (int j=0; j<2; j++) {
00387 int slen = strlen(s);
00388 char *i = NULL;
00389 if (item) {
00390 i = item->data[keycol];
00391 i = (i && *i) ? i+1 : i;
00392 }
00393 while (item && (compare_strn(i, s, slen)!=0)) {
00394 item = item->next;
00395 if (item) {
00396 i = item->data[keycol];
00397 i = (i && *i) ? i+1 : i;
00398 }
00399 }
00400 if (item) return item;
00401 item = first;
00402 }
00403 return NULL;
00404 }
00405
00406 char *VfsListbox::quickfindCompletition(char *s)
00407 {
00408 ht_text_listbox_item *item = first;
00409 char *res = NULL;
00410 int slen = strlen(s);
00411 char *i = NULL;
00412 if (item) {
00413 i = item->data[keycol];
00414 i = (i && *i) ? i+1 : i;
00415 }
00416 while (item) {
00417 if (compare_strn(i, s, slen)==0) {
00418 if (!res) {
00419 res = ht_strdup(item->data[keycol]+1);
00420 } else {
00421 int a = compare_ccomm(item->data[keycol]+1, res);
00422 res[a] = 0;
00423 }
00424 }
00425 item = item->next;
00426 if (item) {
00427 i = item->data[keycol];
00428 i = (i && *i) ? i+1 : i;
00429 }
00430 }
00431 return res;
00432 }
00433
00434 void VfsListbox::update()
00435 {
00436 if (show_pos) {
00437 char curl[VFS_URL_MAX];
00438 ht_snprintf(curl, sizeof curl, "%s:%s", cproto, cdir);
00439 show_pos->settext(curl);
00440 }
00441
00442 ht_text_listbox::update();
00443 }
00444
00445 void VfsListbox::renderEntry(char *buf, int bufsize, int dfmt, const char *filename, pstat_t stat)
00446 {
00447 buf[0] = 0;
00448 int timei = 0;
00449 switch (GET_DISPLAY_FORMAT_TYPE(dfmt)) {
00450 case VFSV_FORMAT_NAME: {
00451 ht_snprintf(buf, bufsize, "%s", filename);
00452 break;
00453 }
00454 case VFSV_FORMAT_SIZE:
00455 if (stat.caps & pstat_size) {
00456 ht_snprintf(buf, bufsize, "%d", stat.size);
00457 }
00458 break;
00459 case VFSV_FORMAT_BSIZE:
00460 if (HT_S_ISDIR(stat.mode)) {
00461 if (strcmp(filename, "..")==0) {
00462 ht_snprintf(buf, bufsize,"<UP-DIR>", stat.size);
00463 } else {
00464 ht_snprintf(buf, bufsize,"<SUB-DIR>", stat.size);
00465 }
00466 } else if (stat.caps & pstat_size) {
00467 ht_snprintf(buf, bufsize, "%u", stat.size);
00468 }
00469 break;
00470 case VFSV_FORMAT_TYPE:
00471 if (bufsize>1) {
00472 if (stat.caps & pstat_mode_type) {
00473 if (HT_S_ISDIR(stat.mode)) {
00474 buf[0] = '/';
00475 } else if (HT_S_ISBLK(stat.mode)) {
00476 buf[0] = '+';
00477 } else if (HT_S_ISCHR(stat.mode)) {
00478 buf[0] = '#';
00479 } else if (HT_S_ISFIFO(stat.mode)) {
00480 buf[0] = '|';
00481 } else if (HT_S_ISLNK(stat.mode)) {
00482 buf[0] = '@';
00483 } else if (HT_S_ISSOCK(stat.mode)) {
00484 buf[0] = '=';
00485 } else if (stat.mode & HT_S_IXUSR) {
00486 buf[0] = '*';
00487 } else {
00488 buf[0] = ' ';
00489 }
00490 buf[1] = 0;
00491 }
00492 }
00493 break;
00494 case VFSV_FORMAT_MTIME:timei++;
00495 case VFSV_FORMAT_ATIME:timei++;
00496 case VFSV_FORMAT_CTIME:timei++;
00497 case VFSV_FORMAT_RMTIME:timei++;
00498 case VFSV_FORMAT_RATIME:timei++;
00499 case VFSV_FORMAT_RCTIME: {
00500 time_t q;
00501 bool avail = false;
00502 bool reltime = false;
00503
00504 switch (timei) {
00505 case 2: reltime = true;
00506 case 5:
00507 if (stat.caps & pstat_mtime) {
00508 q = stat.mtime;
00509 avail = true;
00510 }
00511 break;
00512 case 1: reltime = true;
00513 case 4:
00514 if (stat.caps & pstat_atime) {
00515 q = stat.atime;
00516 avail = true;
00517 }
00518 break;
00519 case 0: reltime = true;
00520 case 3:
00521 if (stat.caps & pstat_ctime) {
00522 q = stat.ctime;
00523 avail = true;
00524 }
00525 break;
00526 }
00527 if (avail) {
00528 tm *pt = gmtime(&q);
00529 if (!pt) {
00530 q = 0;
00531 pt = gmtime(&q);
00532 }
00533 tm t = *pt;
00534
00535 time_t ct;
00536 time(&ct);
00537 tm *pc = gmtime(&ct);
00538 if (!pc) {
00539 ct = 0;
00540 pc = gmtime(&ct);
00541 }
00542 tm c = *pc;
00543 char *line = buf;
00544
00545 char *months[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
00546 if ((reltime) && ((UINT)ct-q<=60*60*24*28)) {
00547 if ((UINT)ct-q<=60*60*24) {
00548 line += ht_snprintf(line, bufsize-(line-buf), "now ");
00549 } else if ((UINT)ct-q<=60*60*24*2) {
00550 line += ht_snprintf(line, bufsize-(line-buf), "now ");
00551 } else {
00552 line += ht_snprintf(line, bufsize-(line-buf), "%s-%uday", ((UINT)(ct-q)/(60*60*24)>10) ? "" : " ", (UINT)(ct-q)/(60*60*24));
00553 }
00554 } else {
00555 line += ht_snprintf(line, bufsize-(line-buf), "%s %02d", months[t.tm_mon], t.tm_mday);
00556 }
00557 if (t.tm_year==c.tm_year) {
00558 if ((reltime) && ((UINT)ct-q<=60*60*24)) {
00559 if ((UINT)ct-q<=60*60) {
00560 line += ht_snprintf(line, bufsize-(line-buf), "-%umin", (UINT)(ct-q)/60);
00561 } else {
00562 line += ht_snprintf(line, bufsize-(line-buf), "-%um:%u", (UINT)(ct-q)/60/60, (UINT)(ct-q)/60%60);
00563 }
00564 } else {
00565 line += ht_snprintf(line, bufsize-(line-buf), " %02d:%02d:%02d", t.tm_hour, t.tm_min, t.tm_sec);
00566 }
00567 } else {
00568 line += ht_snprintf(line, bufsize-(line-buf), " %04d", t.tm_year+1900);
00569 line += ht_snprintf(line, bufsize-(line-buf), " %02d:%02d:%02d", t.tm_hour, t.tm_min, t.tm_sec);
00570 }
00571 }
00572 break;
00573 }
00574 case VFSV_FORMAT_PERM: {
00575 if (bufsize>3) {
00576 char *line = buf;
00577 if (stat.caps & pstat_mode_type) {
00578 *(line++)=HT_S_ISDIR(stat.mode) ? 'd' : '-';
00579 }
00580
00581 if (stat.caps & pstat_mode_oth) {
00582 *(line++)=(stat.mode & HT_S_IROTH) ? 'r' : '-';
00583 *(line++)=(stat.mode & HT_S_IWOTH) ? 'w' : '-';
00584 *(line++)=(stat.mode & HT_S_IXOTH) ? 'x' : '-';
00585 }
00586
00587 if (stat.caps & pstat_mode_grp) {
00588 *(line++)=(stat.mode & HT_S_IRGRP) ? 'r' : '-';
00589 *(line++)=(stat.mode & HT_S_IWGRP) ? 'w' : '-';
00590 *(line++)=(stat.mode & HT_S_IXGRP) ? 'x' : '-';
00591 }
00592
00593 if (stat.caps & pstat_mode_usr) {
00594 *(line++)=(stat.mode & HT_S_IRUSR) ? 'r' : '-';
00595 *(line++)=(stat.mode & HT_S_IWUSR) ? 'w' : '-';
00596 *(line++)=(stat.mode & HT_S_IXUSR) ? 'x' : '-';
00597 }
00598 *line = 0;
00599 }
00600 break;
00601 }
00602 case VFSV_FORMAT_MODE:
00603 if (stat.caps & pstat_mode_all) {
00604 ht_snprintf(buf, bufsize, "%o", stat.mode & ((1<<9)-1));
00605 }
00606 break;
00607 case VFSV_FORMAT_NLINK:
00608 break;
00609 case VFSV_FORMAT_NGID:
00610 if (stat.caps & pstat_gid) {
00611 ht_snprintf(buf, bufsize, "%u", stat.gid);
00612 }
00613 break;
00614 case VFSV_FORMAT_NUID:
00615 if (stat.caps & pstat_uid) {
00616 ht_snprintf(buf, bufsize, "%u", stat.uid);
00617 }
00618 break;
00619 case VFSV_FORMAT_OWNER:
00620 break;
00621 case VFSV_FORMAT_GROUP:
00622 break;
00623 case VFSV_FORMAT_INODE:
00624 if (stat.caps & pstat_inode) {
00625 ht_snprintf(buf, bufsize, "%u", stat.inode);
00626 }
00627 break;
00628 case VFSV_FORMAT_SPACE:
00629 if (bufsize>1) {
00630 buf[0] = ' ';
00631 buf[1] = 0;
00632 }
00633 break;
00634 case VFSV_FORMAT_MARK:
00635 break;
00636 case VFSV_FORMAT_DESC:
00637 if (stat.caps & pstat_desc) {
00638
00639
00640
00641 }
00642 break;
00643 }
00644 }
00645
00646
00647
00648
00649
00650 bool VfsListbox2::selectEntry(void *entry)
00651 {
00652 if (VfsListbox::selectEntry(entry)) return true;
00653 ht_text_listbox_item *i = (ht_text_listbox_item*)entry;
00654 if (i->extra_data) {
00655 vfs_extra *e = (vfs_extra*)i->extra_data;
00656 char path[VFS_URL_MAX];
00657 ht_snprintf(path, sizeof path, "%s%s", cdir, e->name);
00658 cvfs->open(path, 1);
00659 return true;
00660 }
00661 return false;
00662 }