Parcours d’arborescence en TinyScheme pour Gimp

Menu principal

Dans le cadre d’une macro Gimp Script-Fu de traitement par lot, vous pourriez avoir besoin de parcourir toute une arborescence et pas seulement un répertoire à la recherche de fichier de types précis.

La fonctionfile-glob permet de le faire mais seulement sur un seul niveau. Voici la fonctionparcours-repertoires qui permet de combler ce manque.

Utilisation

Pour fonctionner, il vous faut les fonctions suivantes :

est-dans?
présentée dans le billet Fonction est-dans? en TinyScheme pour Gimp
sans-extension
présentée dans le billet Manipulation de nom de fichier en TinyScheme sous Gimp
extension
présentée dans le billet Manipulation de nom de fichier en TinyScheme sous Gimp
est-un-repertoire?
qui teste si un chemin pointe sur un répertoire
est-un-fichier?
qui teste si un chemin pointe sur un fichier standard
parcours-repertoires
qui est la fonction qui nous intéresse à proprement parler

Les fonctionsest-un-repertoire? etest-un-fichier? sont des fonctions créées pour simplifier l’écriture (la lecture ?) de la fonctionparcours-repertoires.

La fonctionparcours-repertoires s’utilise de la façon suivante :

scheme
(parcours-repertoires <répertoire> <profondeur max> <liste des extensions recherchées>)

Les paramètres sont les suivants :

répertoire
le répertoire de départ, sans slash ou anti-slash terminal (“/home/utilisateur” est valide mais pas “/home/utilisateur/”)
profondeur max
niveau de profondeur maximum de recherche dans l’arborescence, un niveau à 1 ne recherche que dans le répertoire donné et pas dans ses sous-répertoires, un niveau à 2 recherche dans le répertoire donné, dans ses sous-répertoires directs mais pas dans les sous-répertoires des sous-répertoires etc. Indiquer une grande valeur pour aller au bout de l’arborescence (16 est un bon candidat)
liste des extensions recherchées
une liste de chaînes de caractères contenant des extensions sous la forme “.png” “.jpeg” (le point est important et la fonction ne comprend pas les caractères génériques * ou ?) ; note : pensez à mettre les variantes avec des majuscules ex. : “.jpg” “.JPG” “.jpeg” “.JPEG” “.Jpeg” etc.

La fonctionparcours-repertoires retourne une liste de tous les fichiers répondant aux critères donnés dans les paramètres. Chaque chaîne contenue dans cette liste est un chemin vers le fichier incluant le répertoire (un chemin absolu si le répertoire fourni est lui-même absolu).

La fonction a été développée sous Linux mais elle devrait également fonctionner sous Windows (mais cela n’a pas été testé !).

Exemple d’appel :

scheme
(parcours-repertoires "/home/utilisateur" 16 (list ".png" ".jpg" ".gif"))

Code source

scheme
; Teste si une valeur se trouve dans une liste
(define (est-dans? valeur liste)
  (cond
    ; Si la liste est nulle, la valeur ne peut pas s’y trouver
    ((null? liste) #f)

    ; Si le premier élément de la liste est égale à la valeur on retourne vrai
    ((equal? valeur (car liste)) #t)

    ; Sinon on recherche la valeur dans le reste de la liste
    (else (est-dans? valeur (cdr liste)))
  )
)

; Retourne un nom de fichier sans son extension
(define (sans-extension nom-fichier)
  (let loop ((nom nom-fichier))
    (cond
      ; Si le nom est vide, il n’y a pas d’extension, on retourne le nom complet
      ((string=? nom "") nom-fichier)

      ; Si le dernier caractère est un séparateur de répertoire
      ((string=? (substring nom (- (string-length nom) 1) (string-length nom)) DIR-SEPARATOR)
        ; on retourne le nom complet
        nom-fichier
      )

      ; Si le dernier caractère est un point
      ((char=? (string-ref nom (- (string-length nom) 1)) #\.)
        ; on retourne le nom en lui enlevant le dernier caractère
        (substring nom 0 (- (string-length nom) 1))
      )

      ; Sinon on supprime le dernier caractère du nom et on boucle
      (else (loop (substring nom 0 (- (string-length nom) 1))))
    )
  )
)

; Retourne l’extension d’un nom de fichier
(define (extension nom-fichier)
  ; Extension=nom de fichier - nom de fichier sans extension
  (substring nom-fichier (string-length (sans-extension nom-fichier)))
)

; Vérifie si un chemin est un répertoire
(define (est-un-repertoire? repertoire entree)
  (=
    (file-type (string-append repertoire DIR-SEPARATOR entree))
    FILE-TYPE-DIR
  )
)

; Vérifie si un chemin est un fichier standard
(define (est-un-fichier? repertoire entree)
  (=
    (file-type (string-append repertoire DIR-SEPARATOR entree))
    FILE-TYPE-FILE
  )
)

; Retourne la liste de tous les fichiers des répertoires et sous-répertoires
(define (parcours-repertoires repertoire niveau extensions)
  (let*
    (
      ; Récupère un curseur sur le contenu du répertoire
      (stream (dir-open-stream repertoire))

      ; Récupère la liste des fichiers
      (retour
        ; Boucle sur la liste des fichiers du répertoire
        (let loop ((entree (dir-read-entry stream)) (entrees '()))
          (cond
            ; Si on est arrivé en fin de liste, on retourne la liste des
            ; fichiers trouvés
            ((or (eof-object? entree) (equal? entree #f)) entrees)

            ; Si l’entrée est un répertoire et que le niveau le permet,
            ; on récupère la liste de ses fichiers par récursivité et on
            ; l’ajoute à la liste en cours
            ((and (est-un-repertoire? repertoire entree)
                  (> niveau 1))
              (loop
                (dir-read-entry stream)
                (append
                  entrees
                  (parcours-repertoires
                    (string-append repertoire DIR-SEPARATOR entree)
                    (- niveau 1)
                    extensions
                  )
                )
              )
            )

            ; Si l’entrée est un fichier et que son extension est dans la liste
            ; des extensions autorisées, on ajoute l’entrée à la liste en cours
            ; et on passe à l’entrée suivante
            ((and (est-un-fichier? repertoire entree)
                  (est-dans? (extension entree) extensions))
              (loop
                (dir-read-entry stream)
                (cons (string-append repertoire DIR-SEPARATOR entree) entrees)
              )
            )

            ; Sinon on passe à l’entrée suivante
            (else (loop (dir-read-entry stream) entrees))
          )
        )
      )
    )

    ; Ferme le curseur sur le répertoire
    (dir-close-stream stream)

    ; Retourne la liste des fichiers trouvés
    retour
  )
)

Fonctionnement

La fonctionparcours-repertoires fait appel aux fonctionsdir-open-stream,dir-read-entry etdir-close-stream pour parcourir l’arborescence. Elle ne fait pas appel à la fonctionfile-glob. En plus du désavantage de ne pouvoir parcourir une arborescence en profondeur, elle n’est pas capable de rechercher plusieurs extensions à la fois.

Concernant l’utilisation de la fonctiondir-read-entry, j’ai ajouté le test(equal? entree #f) car il lui arrive de retourner#f en lieu et place de#<EOF>. Je ne sais pas pourquoi ! Est-ce une mauvaise utilisation de ma part ? Un bug dansdir-read-entry ? Ou un fonctionnement que je n’aurais pas saisi ? Si quelqu’un a la réponse, je suis preneur !