1 /* OpenCP Module Player 2 * copyright (c) '94-'10 Niklas Beisert <nbeisert@physik.tu-muenchen.de> 3 * copyright (c) '04-'21 Stian Skjelstad <stian.skjelstad@gmail.com> 4 * 5 * the file-object code used by the File selector ][ 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 * 21 * revision history: (please note changes here) 22 * -ss040613 Stian Skjelstad <stian@nixia.no> 23 * -first release 24 * -ss040831 Stian Skjelstad <stian@nixia.no> 25 * -updated fs_8dot3_name to not crash anymore 26 * -removed modlist->pathtothis 27 */ 28 29 #include "config.h" 30 #include <assert.h> 31 #include <ctype.h> 32 #include <dirent.h> 33 #include <errno.h> 34 #include <fcntl.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <sys/types.h> 39 #include <sys/stat.h> 40 #include <unistd.h> 41 #include "types.h" 42 #include "dirdb.h" 43 #include "filesystem.h" 44 #include "filesystem-drive.h" 45 #include "mdb.h" 46 #include "modlist.h" 47 #include "stuff/compat.h" 48 #include "stuff/poutput.h" 49 #include "stuff/utf-8.h" 50 51 static void utf8_XdotY_name (const int X, const int Y, char *shortname, const char *source); 52 53 void modlist_free (struct modlist *modlist) 54 { 55 unsigned int i; 56 for (i=0;i<modlist->num;i++) 57 { 58 if (modlist->files[i].dir) 59 { 60 modlist->files[i].dir->unref (modlist->files[i].dir); 61 modlist->files[i].dir = 0; 62 } 63 if (modlist->files[i].file) 64 { 65 modlist->files[i].file->unref (modlist->files[i].file); 66 modlist->files[i].file = 0; 67 } 68 } 69 free (modlist->files); 70 free (modlist->sortindex); 71 free(modlist); 72 } 73 74 struct modlistentry *modlist_getcur (const struct modlist *modlist) /* does not Ref() */ 75 { 76 return modlist_get(modlist, modlist->pos); 77 } 78 79 struct modlistentry *modlist_get (const struct modlist *modlist, unsigned int index) /* does not Ref() */ 80 { 81 if (! modlist->num) 82 { 83 return NULL; /* should never happen */ 84 } 85 if (index >= modlist->num) 86 { 87 index = modlist->num - 1; 88 } 89 return &modlist->files[modlist->sortindex[index]]; 90 } 91 92 void modlist_append (struct modlist *modlist, struct modlistentry *entry) 93 { 94 if (!entry) 95 return; 96 if (modlist->num==modlist->max) 97 { 98 int *newindex; 99 struct modlistentry *newfiles; 100 101 newfiles = realloc(modlist->files, (modlist->max + 50) *sizeof(modlist->files[0])); 102 if (!newfiles) 103 { /* out of memory */ 104 fprintf (stderr, "modlist_append: out of memory\n"); 105 return; 106 } 107 modlist->files=newfiles; 108 109 newindex = realloc (modlist->sortindex, (modlist->max + 50) *sizeof(modlist->sortindex[0])); 110 if (!newindex) 111 { /* out of memory */ 112 fprintf (stderr, "modlist_append: out of memory\n"); 113 return; 114 } 115 modlist->sortindex = newindex; 116 117 modlist->max += 50; 118 } 119 modlist->files[modlist->num] = *entry; 120 modlist->sortindex[modlist->num] = modlist->num; 121 122 if (entry->file) 123 { 124 entry->file->ref (entry->file); 125 } 126 if (entry->dir) 127 { 128 entry->dir->ref (entry->dir); 129 } 130 modlist->num++; 131 } 132 133 void modlist_append_dir (struct modlist *modlist, struct ocpdir_t *dir) 134 { 135 struct modlistentry entry = {0}; 136 char *childpath = 0; 137 138 if (!dir) 139 { 140 return; 141 } 142 143 entry.dir = dir; /* modlist_append will do a ref */ 144 dirdbGetName_internalstr (dir->dirdb_ref, &childpath); 145 utf8_XdotY_name (8, 3, entry.utf8_8_dot_3, childpath); 146 utf8_XdotY_name (16, 3, entry.utf8_16_dot_3, childpath); 147 148 entry.mdb_ref = UINT32_MAX; 149 150 modlist_append (modlist, &entry); 151 } 152 153 void modlist_append_dotdot (struct modlist *modlist, struct ocpdir_t *dir) 154 { 155 struct modlistentry entry = {0}; 156 157 if (!dir) 158 { 159 return; 160 } 161 162 entry.dir = dir; /* modlist_append will do a ref */ 163 entry.flags = MODLIST_FLAG_DOTDOT; 164 strcpy (entry.utf8_8_dot_3, ".."); 165 strcpy (entry.utf8_16_dot_3, ".."); 166 167 entry.mdb_ref = UINT32_MAX; 168 169 modlist_append (modlist, &entry); 170 } 171 172 void modlist_append_drive (struct modlist *modlist, struct dmDrive *drive) 173 { 174 struct modlistentry entry = {0}; 175 char *childpath = 0; 176 177 if (!drive) 178 { 179 return; 180 } 181 182 entry.dir = drive->cwd; /* modlist_append will do a ref */ 183 entry.flags |= MODLIST_FLAG_DRV; 184 dirdbGetName_internalstr (drive->basedir->dirdb_ref, &childpath); 185 utf8_XdotY_name (8, 3, entry.utf8_8_dot_3, childpath); 186 utf8_XdotY_name (16, 3, entry.utf8_16_dot_3, childpath); 187 188 entry.mdb_ref = UINT32_MAX; 189 190 modlist_append (modlist, &entry); 191 } 192 193 void modlist_append_file (struct modlist *modlist, struct ocpfile_t *file) 194 { 195 struct modlistentry entry = {0}; 196 char *childpath = 0; 197 198 if (!file) 199 { 200 return; 201 } 202 203 entry.file = file; /* modlist_append will do a ref */ 204 dirdbGetName_internalstr (file->dirdb_ref, &childpath); 205 utf8_XdotY_name (8, 3, entry.utf8_8_dot_3, childpath); 206 utf8_XdotY_name (16, 3, entry.utf8_16_dot_3, childpath); 207 208 entry.mdb_ref = mdbGetModuleReference2 (file->dirdb_ref, file->filesize (file)); 209 210 modlist_append (modlist, &entry); 211 } 212 213 214 void modlist_remove_all_by_path(struct modlist *modlist, uint32_t ref) 215 { 216 unsigned int i; 217 for (i=0;i<modlist->num;) 218 { 219 if ( modlist->files[modlist->sortindex[i]].file && 220 (modlist->files[modlist->sortindex[i]].file->dirdb_ref == ref) ) 221 { 222 modlist_remove(modlist, i); 223 } else if ( modlist->files[modlist->sortindex[i]].dir && 224 (modlist->files[modlist->sortindex[i]].dir->dirdb_ref == ref) ) 225 { 226 modlist_remove(modlist, i); 227 } else { 228 i++; 229 } 230 } 231 } 232 233 void modlist_clear(struct modlist *modlist) 234 { 235 int i; 236 for (i=0;i<modlist->num;i++) 237 { 238 if (modlist->files[i].dir) 239 { 240 modlist->files[i].dir->unref (modlist->files[i].dir); 241 modlist->files[i].dir = 0; 242 } 243 if (modlist->files[i].file) 244 { 245 modlist->files[i].file->unref (modlist->files[i].file); 246 modlist->files[i].file = 0; 247 } 248 } 249 modlist->num = 0; 250 } 251 252 void modlist_remove(struct modlist *modlist, unsigned int index) /* by sortindex */ 253 { 254 unsigned int i; 255 assert (index < modlist->num); 256 unsigned int realindex = modlist->sortindex[index]; 257 258 if (modlist->files[realindex].file) 259 { 260 modlist->files[realindex].file->unref (modlist->files[realindex].file); 261 } 262 if (modlist->files[realindex].dir) 263 { 264 modlist->files[realindex].dir->unref (modlist->files[realindex].dir); 265 } 266 memmove(&modlist->files[realindex], &modlist->files[realindex+1], (modlist->num - realindex - 1) * sizeof(modlist->files[0])); 267 memmove(&modlist->sortindex[index], &modlist->sortindex[index+1], (modlist->num - index - 1) * sizeof (modlist->sortindex[0])); 268 modlist->num -= 1; 269 270 /* repair the sort-index */ 271 for (i = 0; i < modlist->num; i++) 272 { 273 if (modlist->sortindex[i] >= realindex) 274 { 275 modlist->sortindex[i]--; 276 } 277 } 278 279 #if 0 280 if ((modlist->max - modlist->num>75)) 281 { 282 modlist->max- = 50; 283 modlist->files = realloc (modlist->files, modlist->max * sizeof(modlist->files[0])); 284 modlist->sortindex = realloc (modlist->sortindex, modlist->max * sizeof (modlist->sortindex[0])); 285 } 286 #endif 287 if (!modlist->num) 288 modlist->pos = 0; 289 else if (modlist->pos >= modlist->num) 290 modlist->pos = modlist->num - 1; 291 } 292 293 int modlist_find(struct modlist *modlist, const uint32_t path) 294 { 295 unsigned int retval; 296 for (retval=0; retval < modlist->num; retval++) 297 { 298 int realindex = modlist->sortindex[retval]; 299 if ( modlist->files[realindex].file && 300 (modlist->files[realindex].file->dirdb_ref == path) ) 301 { 302 return retval; 303 } 304 if ( modlist->files[realindex].dir && 305 (modlist->files[realindex].dir->dirdb_ref == path) ) 306 { 307 return retval; 308 } 309 } 310 return -1; 311 } 312 313 void modlist_swap(struct modlist *modlist, unsigned int index1, unsigned int index2) 314 { 315 int entry; 316 entry = modlist->sortindex[index1]; 317 modlist->sortindex[index1] = modlist->sortindex[index2]; 318 modlist->sortindex[index2] = entry; 319 } 320 321 static const char *fuzzycmp(const char *dst, const char *src) 322 { 323 char DST, SRC; 324 while ((*dst)&&(*src)) 325 { 326 DST=toupper(*dst); 327 SRC=toupper(*src); 328 if (DST==SRC) 329 { 330 dst++; 331 src++; 332 } else 333 break; 334 } 335 return dst; 336 } 337 338 #warning input is CP437, search is done on UTF-8 339 int modlist_fuzzyfind(struct modlist *modlist, const char *filename) 340 { 341 unsigned int retval=0; 342 int hitscore=0; 343 unsigned int i; 344 unsigned int len = strlen(filename); 345 if (!len) 346 return 0; 347 for (i=0;i<modlist->num;i++) 348 { 349 char *temp = 0; 350 const char *diff; 351 int score; 352 int index = modlist->sortindex[i]; 353 struct modlistentry *m = &modlist->files[index]; 354 355 dirdbGetName_internalstr (m->file ? m->file->dirdb_ref : m->dir->dirdb_ref, &temp); 356 diff = fuzzycmp(temp, filename); 357 score = diff - temp; 358 359 if ((unsigned)score==len) 360 { 361 return i; 362 } else if (score>hitscore) 363 { 364 retval=i; 365 hitscore=score; 366 } 367 368 diff = fuzzycmp(m->utf8_16_dot_3, filename); 369 score = diff - m->utf8_16_dot_3; 370 if ((unsigned)score==len) 371 { 372 return i; 373 } else if (score>hitscore) 374 { 375 retval=i; 376 hitscore=score; 377 } 378 } 379 return retval; 380 } 381 382 static struct modlist *sorting; 383 static int mlecmp_score (const struct modlistentry *e1) 384 { 385 int i1; 386 387 if (e1->dir) 388 { 389 if (e1->flags & MODLIST_FLAG_DOTDOT) 390 { 391 i1 = 16; 392 } else if (e1->flags & MODLIST_FLAG_DRV) 393 { 394 i1 = 0; 395 } else { 396 i1 = 8; 397 398 if (e1->dir->is_archive) 399 { 400 i1 = 4; 401 } 402 if (e1->dir->is_playlist) 403 { 404 i1 = 2; 405 } 406 } 407 } else { 408 i1 = 1; 409 } 410 411 return i1; 412 } 413 static int mlecmp (const void *a, const void *b) 414 { 415 int _1 = *(int *)a; 416 int _2 = *(int *)b; 417 const struct modlistentry *e1 = &sorting->files[_1]; 418 const struct modlistentry *e2 = &sorting->files[_2]; 419 420 int i1 = mlecmp_score (e1); 421 int i2 = mlecmp_score (e2); 422 423 char *n1, *n2; 424 425 if (i1 != i2) 426 { 427 return i2 - i1; 428 } 429 430 dirdbGetName_internalstr (e1->file ? e1->file->dirdb_ref : e1->dir->dirdb_ref, &n1); 431 dirdbGetName_internalstr (e2->file ? e2->file->dirdb_ref : e2->dir->dirdb_ref, &n2); 432 433 return strcasecmp(n1, n2); 434 } 435 436 void modlist_sort (struct modlist *modlist) 437 { 438 sorting = modlist; /* dirty HACK that is not thread-safe / reentrant what so ever */ 439 qsort(modlist->sortindex, modlist->num, sizeof(modlist->sortindex[0]), mlecmp); 440 sorting = 0; 441 } 442 443 struct modlist *modlist_create (void) 444 { 445 /* TODO ARCS */ 446 /* 447 DIR *dir; 448 */ 449 struct modlist *retval=calloc(sizeof(struct modlist), 1); 450 451 return retval; 452 } 453 454 void modlist_append_modlist (struct modlist *target, struct modlist *source) 455 { 456 unsigned int i; 457 for (i=0;i<source->num;i++) 458 modlist_append(target, modlist_get(source, i)); 459 } 460 461 static size_t strlen_width (const char *source) 462 { 463 return measurestr_utf8 (source, strlen (source)); 464 } 465 466 static void strlcat_width (char *dst, char *src, int length) 467 { 468 while (*dst) 469 { 470 dst++; 471 } 472 while (length && *src) 473 { 474 int inc = 0; 475 int visuallen; 476 utf8_decode (src, strlen (src), &inc); 477 visuallen = measurestr_utf8 (src, inc); 478 if (visuallen > length) 479 { 480 break; 481 } 482 length -= visuallen; 483 memcpy (dst, src, inc); 484 dst += inc; 485 src += inc; 486 } 487 *dst = 0; 488 } 489 490 static void strlcpy_width (char *dst, char *src, int length) 491 { 492 while (length && *src) 493 { 494 int inc = 0; 495 int visuallen; 496 utf8_decode (src, strlen (src), &inc); 497 visuallen = measurestr_utf8 (src, inc); 498 if (visuallen > length) 499 { 500 break; 501 } 502 length -= visuallen; 503 memcpy (dst, src, inc); 504 dst += inc; 505 src += inc; 506 } 507 *dst = 0; 508 } 509 510 static void utf8_XdotY_name (const int X, const int Y, char *shortname, const char *source) 511 { 512 char *temppath; 513 char *lastdot; 514 int length=strlen(source); 515 516 temppath = strdup (source); 517 518 if ((lastdot = rindex(temppath + 1, '.'))) /* we allow files to start with . */ 519 { 520 *lastdot = 0; /* modify the source - most easy way around the problem */ 521 522 strlcpy_width (shortname, temppath, X); 523 length = strlen_width (shortname); 524 if (length < X) 525 { 526 char *target = shortname + strlen (shortname); 527 memset (target, ' ', X - length); 528 target [X - length] = 0; 529 } 530 531 strcat (shortname, "."); 532 533 strlcat_width (shortname, lastdot + 1, Y); 534 length = strlen_width (lastdot + 1); 535 if (length < Y) 536 { 537 char *target = shortname + strlen (shortname); 538 memset (target, ' ', Y - length); 539 target [Y - length] = 0; 540 } 541 } else { 542 strlcpy_width(shortname, temppath, X + Y + 1); 543 length = strlen_width (temppath); 544 if (length < (X + Y + 1)) 545 { 546 char *target = shortname + strlen (shortname); 547 memset (target, ' ', (X + Y + 1) - length); 548 target [(X + Y + 1) - length] = 0; 549 } 550 } 551 free (temppath); 552 } 553