1/**************************************************************************** 2 * 3 * Program: dir.c 4 * Author: Marc van Kempen 5 * desc: Directory routines, sorting and reading 6 * 7 * Copyright (c) 1995, Marc van Kempen 8 * 9 * All rights reserved. 10 * 11 * This software may be used, modified, copied, distributed, and 12 * sold, in both source and binary form provided that the above 13 * copyright and these terms are retained, verbatim, as the first 14 * lines of this file. Under no circumstances is the author 15 * responsible for the proper functioning of this software, nor does 16 * the author assume any responsibility for damages incurred with 17 * its use. 18 * 19 ****************************************************************************/ 20 21#include <sys/types.h> 22#include <sys/stat.h> 23 24#include <unistd.h> /* XXX for _POSIX_VERSION ifdefs */ 25 26#if !defined sgi && !defined _POSIX_VERSION 27#include <sys/dir.h> 28#endif 29#if defined __sun__ 30#include <sys/dirent.h> 31#endif 32#if defined sgi || defined _POSIX_VERSION 33#include <dirent.h> 34#endif 35 36#include <stdlib.h> 37#include <stdio.h> 38#include <string.h> 39#include <fnmatch.h> 40#include <sys/param.h> 41#include "dir.h" 42 43/**************************************************************************** 44 * 45 * local prototypes 46 * 47 ****************************************************************************/ 48 49void toggle_dotfiles(void); 50int show_dotfiles(void); 51int dir_alphasort(const void *d1, const void *d2); 52int dir_sizesort(const void *d1, const void *d2); 53int dir_datesort(const void *d1, const void *d2); 54int dir_extsort(const void *d1, const void *d2); 55 56/**************************************************************************** 57 * 58 * global variables 59 * 60 ****************************************************************************/ 61 62 63/* This is user-selectable, I've set them fixed for now however */ 64 65void *_sort_func = dir_alphasort; 66static int _showdotfiles = TRUE; 67 68/**************************************************************************** 69 * 70 * Functions 71 * 72 ****************************************************************************/ 73 74int 75dir_select_nd( 76#if defined __linux__ 77 const struct dirent *d 78#else 79 struct dirent *d 80#endif 81) 82/* 83 * desc: allways include a directory entry <d>, except 84 * for the current directory and other dot-files 85 * keep '..' however. 86 * pre: <d> points to a dirent 87 * post: returns TRUE if d->d_name != "." else FALSE 88 */ 89{ 90 if (strcmp(d->d_name, ".")==0 || 91 (d->d_name[0] == '.' && strlen(d->d_name) > 1 && d->d_name[1] != '.')) { 92 return(FALSE); 93 } else { 94 return(TRUE); 95 } 96}/* dir_select_nd() */ 97 98 99int 100dir_select( 101#ifdef __linux__ 102 const struct dirent *d 103#else 104 struct dirent *d 105#endif 106) 107/* 108 * desc: allways include a directory entry <d>, except 109 * for the current directory 110 * pre: <d> points to a dirent 111 * post: returns TRUE if d->d_name != "." else FALSE 112 */ 113{ 114 if (strcmp(d->d_name, ".")==0) { /* don't include the current directory */ 115 return(FALSE); 116 } else { 117 return(TRUE); 118 } 119} /* dir_select() */ 120 121int 122dir_select_root_nd( 123#ifdef __linux__ 124 const struct dirent *d 125#else 126 struct dirent *d 127#endif 128) 129/* 130 * desc: allways include a directory entry <d>, except 131 * for the current directory and the parent directory. 132 * Also skip any other dot-files. 133 * pre: <d> points to a dirent 134 * post: returns TRUE if d->d_name[0] != "." else FALSE 135 */ 136{ 137 if (d->d_name[0] == '.') { /* don't include the current directory */ 138 return(FALSE); /* nor the parent directory */ 139 } else { 140 return(TRUE); 141 } 142} /* dir_select_root_nd() */ 143 144 145int 146dir_select_root( 147#ifdef __linux__ 148 const struct dirent *d 149#else 150 struct dirent *d 151#endif 152) 153/* 154 * desc: allways include a directory entry <d>, except 155 * for the current directory and the parent directory 156 * pre: <d> points to a dirent 157 * post: returns TRUE if d->d_name[0] != "." else FALSE 158 */ 159{ 160 if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) { 161 return(FALSE); 162 } else { 163 return(TRUE); 164 } 165}/* dir_select_root() */ 166 167 168#ifdef NO_ALPHA_SORT 169int 170alphasort(const void *d1, const void *d2) 171/* 172 * desc: a replacement for what should be in the library 173 */ 174{ 175 return(strcmp(((struct dirent *) d1)->d_name, 176 ((struct dirent *) d2)->d_name)); 177} /* alphasort() */ 178#endif 179 180int 181dir_alphasort(const void *d1, const void *d2) 182/* 183 * desc: compare d1 and d2, but put directories always first 184 * put '..' always on top 185 * 186 */ 187{ 188 DirList *f1 = ((DirList *) d1), 189 *f2 = ((DirList *) d2); 190 struct stat *s1 = &(f1->filestatus); 191 struct stat *s2 = &(f2->filestatus); 192 193 /* check for '..' */ 194 if (strcmp(((DirList *) d1)->filename, "..") == 0) { 195 return(-1); 196 } 197 if (strcmp(((DirList *) d2)->filename, "..") == 0) { 198 return(1); 199 } 200 201 /* put directories first */ 202 if ((s1->st_mode & S_IFDIR) && (s2->st_mode & S_IFDIR)) { 203 return(strcmp(f1->filename, f2->filename)); 204 }; 205 if (s1->st_mode & S_IFDIR) { 206 return(-1); 207 } 208 if (s2->st_mode & S_IFDIR) { 209 return(1); 210 } 211 return(strcmp(f1->filename, f2->filename)); 212 213} /* dir_alphasort() */ 214 215 216int 217dir_sizesort(const void *d1, const void *d2) 218/* 219 * desc: compare d1 and d2, but put directories always first 220 * 221 */ 222{ 223 DirList *f1 = ((DirList *) d1), 224 *f2 = ((DirList *) d2); 225 struct stat *s1 = &(f1->filestatus); 226 struct stat *s2 = &(f2->filestatus); 227 228 /* check for '..' */ 229 if (strcmp(((DirList *) d1)->filename, "..") == 0) { 230 return(-1); 231 } 232 if (strcmp(((DirList *) d2)->filename, "..") == 0) { 233 return(1); 234 } 235 236 /* put directories first */ 237 if ((s1->st_mode & S_IFDIR) && (s2->st_mode & S_IFDIR)) { 238 return(s1->st_size < s2->st_size ? 239 -1 240 : 241 s1->st_size >= s2->st_size); 242 }; 243 if (s1->st_mode & S_IFDIR) { 244 return(-1); 245 } 246 if (s2->st_mode & S_IFDIR) { 247 return(1); 248 } 249 return(s1->st_size < s2->st_size ? 250 -1 251 : 252 s1->st_size >= s2->st_size); 253 254} /* dir_sizesort() */ 255 256int 257dir_datesort(const void *d1, const void *d2) 258/* 259 * desc: compare d1 and d2 on date, but put directories always first 260 */ 261{ 262 DirList *f1 = ((DirList *) d1), 263 *f2 = ((DirList *) d2); 264 struct stat *s1 = &(f1->filestatus); 265 struct stat *s2 = &(f2->filestatus); 266 267 268 /* check for '..' */ 269 if (strcmp(((DirList *) d1)->filename, "..") == 0) { 270 return(-1); 271 } 272 if (strcmp(((DirList *) d2)->filename, "..") == 0) { 273 return(1); 274 } 275 276 /* put directories first */ 277 if ((s1->st_mode & S_IFDIR) && (s2->st_mode & S_IFDIR)) { 278 return(s1->st_mtime < s2->st_mtime ? 279 -1 280 : 281 s1->st_mtime >= s2->st_mtime); 282 }; 283 if (s1->st_mode & S_IFDIR) { 284 return(-1); 285 } 286 if (s2->st_mode & S_IFDIR) { 287 return(1); 288 } 289 return(s1->st_mtime < s2->st_mtime ? 290 -1 291 : 292 s1->st_mtime >= s2->st_mtime); 293 294} /* dir_datesort() */ 295 296 297int 298null_strcmp(char *s1, char *s2) 299/* 300 * desc: compare strings allowing NULL pointers 301 */ 302{ 303 if ((s1 == NULL) && (s2 == NULL)) { 304 return(0); 305 } 306 if (s1 == NULL) { 307 return(-1); 308 } 309 if (s2 == NULL) { 310 return(1); 311 } 312 return(strcmp(s1, s2)); 313} /* null_strcmp() */ 314 315 316int 317dir_extsort(const void *d1, const void *d2) 318/* 319 * desc: compare d1 and d2 on extension, but put directories always first 320 * extension = "the characters after the last dot in the filename" 321 * pre: d1 and d2 are pointers to DirList type records 322 * post: see code 323 */ 324{ 325 DirList *f1 = ((DirList *) d1), 326 *f2 = ((DirList *) d2); 327 struct stat *s1 = &(f1->filestatus); 328 struct stat *s2 = &(f2->filestatus); 329 char *ext1, *ext2; 330 int extf, ret; 331 332 333 /* check for '..' */ 334 if (strcmp(((DirList *) d1)->filename, "..") == 0) { 335 return(-1); 336 } 337 if (strcmp(((DirList *) d2)->filename, "..") == 0) { 338 return(1); 339 } 340 341 342 /* find the first extension */ 343 344 ext1 = f1->filename + strlen(f1->filename); 345 extf = FALSE; 346 while (!extf && (ext1 > f1->filename)) { 347 extf = (*--ext1 == '.'); 348 } 349 if (!extf) { 350 ext1 = NULL; 351 } else { 352 ext1++; 353 } 354 /* ext1 == NULL if there's no "extension" else ext1 points */ 355 /* to the first character of the extension string */ 356 357 /* find the second extension */ 358 359 ext2 = f2->filename + strlen(f2->filename); 360 extf = FALSE; 361 while (!extf && (ext2 > f2->filename)) { 362 extf = (*--ext2 == '.'); 363 } 364 if (!extf) { 365 ext2 = NULL; 366 } else { 367 ext2++; 368 } 369 /* idem as for ext1 */ 370 371 if ((s1->st_mode & S_IFDIR) && (s2->st_mode & S_IFDIR)) { 372 ret = null_strcmp(ext1, ext2); 373 if (ret == 0) { 374 return(strcmp(f1->filename, f2->filename)); 375 } else { 376 return(ret); 377 } 378 }; 379 if (s1->st_mode & S_IFDIR) { 380 return(-1); 381 } 382 if (s2->st_mode & S_IFDIR) { 383 return(1); 384 } 385 ret = null_strcmp(ext1, ext2); 386 if (ret == 0) { 387 return(strcmp(f1->filename, f2->filename)); 388 } else { 389 return(ret); 390 } 391 392} /* dir_extsort() */ 393 394 395void 396get_dir(char *dirname, char *fmask, DirList **dir, int *n) 397/* 398 * desc: get the files in the current directory 399 * pre: <dir> == NULL 400 * post: <dir> contains <n> dir-entries 401 */ 402{ 403 char cwd[MAXPATHLEN]; 404 char buf[256]; 405 struct dirent **dire; 406 struct stat status; 407 int i, j, nb; 408 long d; 409 410 411 getcwd(cwd, MAXPATHLEN); 412 if (strcmp(cwd, "/") == 0) { /* we are in the root directory */ 413 if (show_dotfiles()) { 414 *n = scandir(dirname, &dire, dir_select_root, alphasort); 415 } else { 416 *n = scandir(dirname, &dire, dir_select_root_nd, alphasort); 417 } 418 } else { 419 if (show_dotfiles()) { 420 *n = scandir(dirname, &dire, dir_select, alphasort); 421 } else { 422 *n = scandir(dirname, &dire, dir_select_nd, alphasort); 423 } 424 } 425 426 /* There is the possibility that we have entered a directory */ 427 /* which we are not allowed to read, scandir thus returning */ 428 /* -1 for *n. */ 429 /* Actually I should also check for lack of memory, but I'll */ 430 /* let my application happily crash if this is the case */ 431 /* Solution: */ 432 /* manually insert the parent directory as the only */ 433 /* directory entry, and return. */ 434 435 if (*n == -1) { 436 *n = 1; 437 *dir = (DirList *) malloc(sizeof(DirList)); 438 strcpy((*dir)[0].filename, ".."); 439 lstat("..", &status); 440 (*dir)[0].filestatus = status; 441 (*dir)[0].link = FALSE; 442 return; 443 } 444 445 *dir = (DirList *) malloc( *n * sizeof(DirList) ); 446 d = 0; 447 i = 0; 448 j = 0; 449 while (j<*n) { 450 lstat(dire[j]->d_name, &status); 451 /* check if this file is to be included */ 452 /* always include directories, the rest is subject to fmask */ 453 if (S_ISDIR(status.st_mode) 454 || fnmatch(fmask, dire[j]->d_name, FNM_NOESCAPE) != FNM_NOMATCH) { 455 strcpy((*dir)[i].filename, dire[j]->d_name); 456 (*dir)[i].filestatus = status; 457 if ((S_IFMT & status.st_mode) == S_IFLNK) { /* handle links */ 458 (*dir)[i].link = TRUE; 459 stat(dire[j]->d_name, &status); 460 nb = readlink(dire[j]->d_name, buf, sizeof(buf) - 1); 461 if (nb == -1) { 462 printf("get_dir(): Error reading link: %s\n", dire[j]->d_name); 463 exit(-1); 464 } else { 465 (*dir)[i].linkname = malloc(sizeof(char) * nb + 1); 466 strncpy((*dir)[i].linkname, buf, nb); 467 (*dir)[i].linkname[nb] = 0; 468 } 469 (*dir)[i].filestatus = status; 470 } else { 471 (*dir)[i].link = FALSE; 472 (*dir)[i].linkname = NULL; 473 } 474 i++; 475 } else { 476 /* skip this entry */ 477 } 478 j++; 479 } 480 *n = i; 481 482 /* sort the directory with the directory names on top */ 483 qsort((*dir), *n, sizeof(DirList), _sort_func); 484 485 /* Free the allocated memory */ 486 for (i=0; i<*n; i++) { 487 free(dire[i]); 488 } 489 free(dire); 490 491 return; 492}/* get_dir() */ 493 494 495void 496FreeDir(DirList *d, int n) 497/* 498 * desc: free the dirlist d 499 * pre: d != NULL 500 * post: memory allocated to d has been released 501 */ 502{ 503 int i; 504 505 if (d) { 506 for (i=0; i<n; i++) { 507 if (d[i].linkname) { 508 free(d[i].linkname); 509 } 510 } 511 free(d); 512 } else { 513 printf("dir.c:FreeDir(): d == NULL\n"); 514 exit(-1); 515 } 516 517 return; 518} /* FreeDir() */ 519 520void 521toggle_dotfiles(void) 522/* 523 * desc: toggle visibility of dot-files 524 */ 525{ 526 _showdotfiles = !_showdotfiles; 527 528 return; 529} /* toggle_dotfiles() */ 530 531int 532show_dotfiles(void) 533/* 534 * desc: return the value of _showdotfiles 535 */ 536{ 537 return(_showdotfiles); 538} /* show_dotfiles() */ 539 540void 541set_dotfiles(int b) 542/* 543 * desc: set the value of _showdotfiles 544 */ 545{ 546 _showdotfiles = b; 547 548 return; 549} /* set_dotfiles() */ 550