#!/usr/local/bin/perl -w ## set up some globals $BASE = '/base/of-the/image/path'; ## base image directory $LINK = "$BASE/desktop.jpg"; ## the target image $GO_DOWN = 0.70; ## dig deeper 70% of the time $TRY_LIMIT = 1000; ## stop after 1000 tries, max $current = "$BASE"; ## set up a path heirarchy @P = ($current); ## set up a loop counter $N = 0; while (($N++ < $TRY_LIMIT) && (-d $current)) { ## read the directory opendir (DIR, $current) or die qq(can't read "$current": $!); @D = grep { (!/^\./) && ## exclude hidden files (!/^#/) ## exclude user-selected files } readdir DIR; closedir DIR; ## get a list of subirs $d = []; @$d = grep { -d "$current/$_" } @D; ## get a list of '*.jpg' files $j = []; @$j = grep { /.jpg$/i } @D; ## assemble a list of weights.. # # the basic idea is to give some items a higher chance # of being chosen than others. if a filename begins # with a number N, the program's decision table will # contain N copies of that filename. # # we start by building a table with N-1 entries for # each item that does start with a number, then finish # off by adding the whole file list to the table. # @w = (); for $f (@$d) { if ($f =~ /^(\d+)/) { push @w, ($f) x ($1 - 1); } } push @$d, @w; ## decide whether to choose a file from this directory, ## or to pick a random directory and drill down again. # # note that we may end up choosing an empty list.. # therefore we check the list we choose to make sure # it does contain something, and if it doesn't, we # deal with it. # # the first stage of dealing with it is to fall back # to the other option.. if we chose to drill deeper and # in a directory with no subdirs, we fall back to # selecting a random image. if we chose to select an # image in a directory that only contains subdirs, we # fall back to drilling deeper. # # the second stage of dealing with it.. which occurs # if we hit an empty directory.. is to back out one # level and try again. # # in the pathological case, where you run this loop # on the base of a file tree that contains no .jpg # files at all, the loop counter will force termination # after $TRY_LIMIT tries. # ($l, $r) = (rand() < $GO_DOWN) ? ($d, $j) : ($j, $d); $list = (@$l) ? $l : $r; if (@$list) { push @P, $list->[ rand (@$list) ]; } else { pop @P; } $current = join ('/', @P); } unlink $LINK; link $current, $LINK if (-f $current);