Comment obtenir le répertoire à partir duquel un programme s'exécute?


269

Existe-t-il une méthode indépendante de la plate-forme et indépendante du système de fichiers pour obtenir le chemin complet du répertoire à partir duquel un programme s'exécute à l'aide de C / C ++? À ne pas confondre avec le répertoire de travail actuel. (Veuillez ne pas suggérer de bibliothèques à moins qu'elles ne soient standard comme clib ou STL.)

(S'il n'y a pas de méthode indépendante de la plate-forme / du système de fichiers, les suggestions qui fonctionnent sous Windows et Linux pour des systèmes de fichiers spécifiques sont également les bienvenues.)


@chakrit: Ce serait formidable. (Bien que ce problème ne survienne généralement pas sous Windows.)
Ashwin Nanjappa

2
À moins que vous ne puissiez extraire le chemin de manière fiable argv[0], la technique dépendra beaucoup du système d'exploitation.
David R Tribble

1
Juste pour clarifier: le «répertoire courant» ou «le répertoire à partir duquel le programme s'exécute» (dans la terminologie de la question) est le répertoire où se trouve le fichier image du programme (fichier ~ .exe), et le « répertoire de travail actuel » est le répertoire, qui est rempli automatiquement si le programme utilise des chemins relatifs?
colemik

3
Lorsque vous #include <windows.h>, Windows place automatiquement un char*dans le chemin exécutable dans _pgmptr. Vous n'avez pas besoin d'appeler des fonctions supplémentaires ou d'assumer le courrier indésirable si vous travaillez uniquement sur Windows.
rsethc

1
Bien que le commentaire date d'il y a trois ans, j'aimerais développer le commentaire de rsethc sur _pgmptr. La documentation MSDN indique que les variables _pgmptret _wpgmptrsont obsolètes et que vous devez utiliser la fonction _get_pgmptr(char**)ou à la _get_wpgmptr(wchar_t**)place. MSDN
Hydranix

Réponses:


181

Voici le code pour obtenir le chemin d'accès complet à l'application en cours d'exécution:

Les fenêtres:

int bytes = GetModuleFileName(NULL, pBuf, len);
return bytes ? bytes : -1;

Linux:

int bytes = MIN(readlink("/proc/self/exe", pBuf, len), len - 1);
if(bytes >= 0)
    pBuf[bytes] = '\0';
return bytes;

3
Je pense que c'est la seule réponse ici qui répond à la question, et cela pour Windows et Linux. Bon travail.
Frank Szczerba

6
Boo pour / proc / pid / exe - Non pris en charge sur OS X pour une raison quelconque.
Chris Lutz

24
Quand je vois du code qui regarde une /procpartie de moi, il meurt un peu. Tout le monde n'est pas Linux, et même sur cette seule plate-forme /procdevrait être considérée sous réserve de changer de version en version, d'arc en arc, etc.
asveikau

4
s'ils se lancent à l'aide d'une commande aliasée sur Linux, l'argv [0] est-il le "nom de la commande" ou développé?
Andy Dent

20
Que diriez-vous d'ajouter char pBuf[256]; size_t len = sizeof(pBuf);pour laisser la solution plus clairement.
charles.cc.hsu

166

Si vous récupérez le répertoire courant lors du premier démarrage de votre programme, vous disposez effectivement du répertoire à partir duquel votre programme a été démarré. Stockez la valeur dans une variable et faites-y référence plus tard dans votre programme. Ceci est distinct du répertoire qui contient le fichier programme exécutable actuel . Ce n'est pas nécessairement le même répertoire; si quelqu'un exécute le programme à partir d'une invite de commandes, le programme est exécuté à partir du répertoire de travail actuel de l'invite de commandes, même si le fichier du programme réside ailleurs.

getcwd est une fonction POSIX et prise en charge prête à l'emploi par toutes les plates-formes compatibles POSIX. Vous n'auriez rien à faire de spécial (à part inclure les bons en-têtes unistd.h sous Unix et direct.h sous Windows).

Puisque vous créez un programme C, il sera lié à la bibliothèque d'exécution c par défaut qui est liée à TOUS les processus du système (les exceptions spécialement conçues sont évitées) et il inclura cette fonction par défaut. Le CRT n'est jamais considéré comme une bibliothèque externe car il fournit l'interface standard de base compatible avec le système d'exploitation.

Sous Windows, la fonction getcwd a été déconseillée au profit de _getcwd. Je pense que vous pouvez l'utiliser de cette façon.

#include <stdio.h>  /* defines FILENAME_MAX */
#ifdef WINDOWS
    #include <direct.h>
    #define GetCurrentDir _getcwd
#else
    #include <unistd.h>
    #define GetCurrentDir getcwd
 #endif

 char cCurrentPath[FILENAME_MAX];

 if (!GetCurrentDir(cCurrentPath, sizeof(cCurrentPath)))
     {
     return errno;
     }

cCurrentPath[sizeof(cCurrentPath) - 1] = '\0'; /* not really required */

printf ("The current working directory is %s", cCurrentPath);

44
Bonne réponse, mais je pensais que "le répertoire de travail actuel" n'était pas ce que l'on recherchait.
Michael Burr

4
vous devez ajouter que même si certaines documentations indiquent que cCurrentpath peut être nul et sera alloué par getcwd getcwd ne semble pas allouer quelque chose sur Mac OS et plante silencieusement votre programme
Janusz

4
Il y a une petite erreur, mais malheureusement je ne peux pas encore l'éditer .. ligne 10: cCurrentpath: devrait être cCurrentPath
Lipis

8
OMI sous Windows, les fonctions nommées POSIXy (dont certaines commençant par des traits de soulignement) doivent généralement être évitées. Ce ne sont pas les véritables API Windows mais plutôt le CRT. L'API Windows que vous souhaitez utiliser est GetCurrentDirectory (). msdn.microsoft.com/en-us/library/aa364934(VS.85).aspx
asveikau

6
La réponse de Mike est correcte. Le "répertoire courant" n'est pas toujours le même que le répertoire à partir duquel le binaire s'exécute. Par exemple, si une application s'exécute en tant que service sous Windows, le répertoire actuel sera probablement C: \ Windows \ System32, tandis que le répertoire binaire est différent.
Lucky Luke

42

C'est du forum cplusplus

Sur les fenêtres:

#include <string>
#include <windows.h>

std::string getexepath()
{
  char result[ MAX_PATH ];
  return std::string( result, GetModuleFileName( NULL, result, MAX_PATH ) );
}

Sous Linux:

#include <string>
#include <limits.h>
#include <unistd.h>

std::string getexepath()
{
  char result[ PATH_MAX ];
  ssize_t count = readlink( "/proc/self/exe", result, PATH_MAX );
  return std::string( result, (count > 0) ? count : 0 );
}

Sur HP-UX:

#include <string>
#include <limits.h>
#define _PSTAT64
#include <sys/pstat.h>
#include <sys/types.h>
#include <unistd.h>

std::string getexepath()
{
  char result[ PATH_MAX ];
  struct pst_status ps;

  if (pstat_getproc( &ps, sizeof( ps ), 0, getpid() ) < 0)
    return std::string();

  if (pstat_getpathname( result, PATH_MAX, &ps.pst_fid_text ) < 0)
    return std::string();

  return std::string( result );
}

1
Cette solution Windows ne gérera pas les caractères non ANSI dans le chemin. Vous devriez probablement utiliser GetModuleFileNameW et le convertir explicitement en UTF-8 (en faisant attention à le reconvertir chaque fois que vous devez émettre une commande de système de fichiers).
Adrian McCarthy

3
Pour la solution Windows, j'obtiens l'erreur error: cannot convert 'char*' to 'LPWCH {aka wchar_t*}' for argument '2' to 'DWORD GetModuleFileNameW(HMODULE, LPWCH, DWORD)'lors de la compilation avec MinGW.
HelloGoodbye

2
@Adrian, je ne suis généralement pas un programmeur Windows, mais n'y a-t-il pas un DEFINE ou d'une manière ou d'une autre pour dire à votre compilateur d'utiliser automatiquement la saveur _W () des fonctions?
Octopus

1
@Octopus: Pour utiliser les appels larges, vous devez utiliser WCHAR (au lieu de char) et std :: wstring (au lieu de std :: string).
Adrian McCarthy

29

Si vous voulez une méthode standard sans bibliothèques: Non. Le concept entier d'un répertoire n'est pas inclus dans la norme.

Si vous acceptez qu'une certaine dépendance (portable) à une bibliothèque proche du standard est acceptable: utilisez la bibliothèque de système de fichiers de Boost et demandez initial_path () .

IMHO qui est aussi proche que possible, avec un bon karma (Boost est un ensemble de bibliothèques de haute qualité bien établi)


8
À partir des documents Boost: template <class Path> const Path & initial_path (); Renvoie: current_path () au moment de l'entrée dans main (). Et current_path () est 'comme si par POSIX getcwd ()'. Ce n'est pas ce que l'interrogateur a demandé.
Jonathan Leffler


Comme indiqué, cela donne le chemin à partir duquel le binaire a été invoqué, pas le chemin vers le binaire ... car il pourrait être démarré à partir d'un dossier différent.
jpo38

21

Filesystem TS est maintenant un standard (et pris en charge par gcc 5.3+ et clang 3.9+), vous pouvez donc utiliser la current_path()fonction à partir de celui-ci:

std::string path = std::experimental::filesystem::current_path();

Dans gcc (5.3+) pour inclure le système de fichiers, vous devez utiliser:

#include <experimental/filesystem>

et liez votre code avec le -lstdc++fsdrapeau.

Si vous souhaitez utiliser le système de fichiers avec Microsoft Visual Studio, lisez ceci .


6
À partir du lien référencé, 1-2) Returns the absolute path of the current working directory, obtained as if by POSIX getcwd. (2) returns path() if error occurs. Downvoted, car l'OP demande spécifiquement le chemin actuel de l'exécutable plutôt que le répertoire de travail actuel.
S. Saad

20

Je sais qu'il est très tard dans la journée pour répondre à celle-ci, mais j'ai trouvé qu'aucune des réponses ne m'était aussi utile que ma propre solution. Un moyen très simple d'obtenir le chemin de votre CWD vers votre dossier bin est comme ceci:

int main(int argc, char* argv[])
{
    std::string argv_str(argv[0]);
    std::string base = argv_str.substr(0, argv_str.find_last_of("/"));
}

Vous pouvez maintenant simplement l'utiliser comme base pour votre chemin relatif. Ainsi, par exemple, j'ai cette structure de répertoires:

main
  ----> test
  ----> src
  ----> bin

et je veux compiler mon code source dans le bac et écrire un journal pour tester je peux simplement ajouter cette ligne à mon code.

std::string pathToWrite = base + "/../test/test.log";

J'ai essayé cette approche sur Linux en utilisant le chemin complet, l'alias, etc. et cela fonctionne très bien.

REMARQUE:

Si vous êtes sous Windows, vous devez utiliser un «\» comme séparateur de fichier et non «/». Vous devrez également y échapper par exemple:

std::string base = argv[0].substr(0, argv[0].find_last_of("\\"));

Je pense que cela devrait fonctionner mais ne l'ont pas testé, donc un commentaire serait apprécié s'il fonctionne ou un correctif sinon.


Oui, cela fonctionne également sur Windows. Je pense que c'est la meilleure solution. Autant que je sache, argv [0] garde toujours le chemin vers l'exécutable.
Wodzu

4
argv[0]est une très bonne idée, mais malheureusement ce que je reçois sur Linux est "./my_executable_name" ou "./make/my_executable_name". Fondamentalement, ce que j'obtiens dépend complètement de la façon dont je le lance
Xeverous

1
@Xeverous: alors quoi? Si j'ai des fichiers relatifs à mon exécutable qu'il doit ouvrir, à partir de "./" ou "./make/" dans vos cas devrait fonctionner. "." est le répertoire de travail actuel, et argv [0] vous indiquera le chemin relatif vers l'exécutable à partir de là, ce qui est exactement ce que l'OP devrait vouloir. C'est en tout cas exactement ce dont j'ai besoin.
nilo

9

Non, il n'y a pas de moyen standard. Je crois que les normes C / C ++ ne considèrent même pas l'existence de répertoires (ou d'autres organisations de système de fichiers).

Sous Windows, GetModuleFileName () renverra le chemin d'accès complet au fichier exécutable du processus en cours lorsque le paramètre hModule est défini sur NULL . Je ne peux pas m'empêcher avec Linux.

Vous devez également préciser si vous voulez le répertoire actuel ou le répertoire dans lequel réside l'image / l'exécutable du programme. En l'état, votre question est un peu ambiguë sur ce point.


9

Sous Windows, le moyen le plus simple consiste à utiliser la _get_pgmptrfonction in stdlib.hpour obtenir un pointeur sur une chaîne qui représente le chemin absolu vers l'exécutable, y compris le nom de l'exécutable.

char* path;
_get_pgmptr(&path);
printf(path); // Example output: C:/Projects/Hello/World.exe

8

Peut-être concaténer le répertoire de travail actuel avec argv [0]? Je ne sais pas si cela fonctionnerait sous Windows mais cela fonctionne sous Linux.

Par exemple:

#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char **argv) {
    char the_path[256];

    getcwd(the_path, 255);
    strcat(the_path, "/");
    strcat(the_path, argv[0]);

    printf("%s\n", the_path);

    return 0;
}

Lorsqu'il est exécuté, il génère:

jeremy @ jeremy-desktop: ~ / Desktop $ ./test
/home/jeremy/Desktop/./test


Vous aurez besoin d'une vérification pour voir si un chemin absolu est donné dans argv [0]. Mais plus important encore, que se passe-t-il si l'image est localisée via le CHEMIN? Est-ce que Linux remplit le chemin complet ou tout simplement ce qui est sur la ligne de commande?
Michael Burr

Comme l'a souligné Mike B, il s'agit d'une solution non générale; cela ne fonctionne que dans certaines circonstances très limitées. Fondamentalement, uniquement lorsque vous exécutez la commande par un chemin d'accès relatif - et ce n'est pas si élégant lorsque vous exécutez ../../../bin/progname au lieu de ./test
Jonathan Leffler

Si vous résolvez le chemin relatif possible d'argv [0] par rapport au répertoire actuel (car argv [0] pourrait être "../../myprogram.exe"), c'est probablement le moyen le plus sûr de répondre à la question. Il fonctionnera toujours et est portable (il fonctionne même sur Android!).
jpo38


6

Vous ne pouvez pas utiliser argv [0] à cet effet, il contient généralement le chemin d'accès complet à l'exécutable, mais pas nécessairement - le processus peut être créé avec une valeur arbitraire dans le champ.

Attention aussi, le répertoire courant et le répertoire avec l'exécutable sont deux choses différentes, donc getcwd () ne vous aidera pas non plus.

Sous Windows, utilisez GetModuleFileName (), sous Linux, lisez les fichiers / dev / proc / procID / ...


3

Juste pour empiler tardivement ici, ...

il n'y a pas de solution standard, car les langues sont indépendantes des systèmes de fichiers sous-jacents, de sorte que, comme d'autres l'ont dit, le concept d'un système de fichiers basé sur un répertoire est hors de la portée des langages c / c ++.

en plus de cela, vous ne voulez pas le répertoire de travail actuel, mais le répertoire dans lequel le programme s'exécute, qui doit tenir compte de la façon dont le programme est arrivé là où il se trouve - c'est-à-dire qu'il a été généré comme un nouveau processus via un fork, etc. Pour obtenir le répertoire dans lequel un programme s'exécute, comme les solutions l'ont démontré, vous devez obtenir ces informations des structures de contrôle de processus du système d'exploitation en question, qui est la seule autorité sur cette question. Ainsi, par définition, c'est une solution spécifique au système d'exploitation.


3

Pour le système Windows sur la console, vous pouvez utiliser la dircommande system ( ). Et la console vous donne des informations sur le répertoire et etc. Lisez la dircommande sur cmd. Mais pour les systèmes de type Unix, je ne sais pas ... Si cette commande est exécutée, lisez la commande bash. lsn'affiche pas le répertoire ...

Exemple:

int main()
{
    system("dir");
    system("pause"); //this wait for Enter-key-press;
    return 0;
}

2
#include <windows.h>
using namespace std;

// The directory path returned by native GetCurrentDirectory() no end backslash
string getCurrentDirectoryOnWindows()
{
    const unsigned long maxDir = 260;
    char currentDir[maxDir];
    GetCurrentDirectory(maxDir, currentDir);
    return string(currentDir);
}

1

Sur les plateformes POSIX, vous pouvez utiliser getcwd () .

Sous Windows, vous pouvez utiliser _getcwd () , comme l'utilisation de getcwd () est déconseillée.

Pour les bibliothèques standard, si Boost était suffisamment standard pour vous, j'aurais suggéré Boost :: filesystem, mais elles semblent avoir supprimé la normalisation des chemins de la proposition. Vous devrez peut-être attendre que TR2 devienne facilement disponible pour une solution entièrement standard.


10
getcwd () ne fait pas ce que le questionneur a demandé.
Jonathan Leffler

n'est-ce pas que la réponse acceptée utilise getcwd (), ou est-ce que je ne comprends pas seulement?
Sнаđошƒаӽ

J'ai voté positivement parce que c'est vous qui êtes venu avec ce qui est considéré comme la bonne réponse en premier.
Arnaud

Cette réponse n'essaie même pas de répondre à la question. Honte de l'écrire.
HelloWorld

1

Pour les chemins relatifs, voici ce que j'ai fait. Je connais l'âge de cette question, je veux simplement apporter une réponse plus simple qui fonctionne dans la majorité des cas:

Disons que vous avez un chemin comme celui-ci:

"path/to/file/folder"

Pour une raison quelconque, les exécutables Linux construits dans eclipse fonctionnent très bien avec cela. Cependant, Windows devient très confus si on lui donne un chemin comme celui-ci pour travailler!

Comme indiqué ci-dessus, il existe plusieurs façons d'obtenir le chemin actuel vers l'exécutable, mais la manière la plus simple que je trouve fonctionne dans la plupart des cas est d'ajouter ceci au FRONT de votre chemin:

"./path/to/file/folder"

L'ajout de "./" devrait vous permettre de trier! :) Ensuite, vous pouvez commencer à charger à partir du répertoire de votre choix, à condition que ce soit avec l'exécutable lui-même.

EDIT: Cela ne fonctionnera pas si vous essayez de lancer l'exécutable à partir de code :: blocks si c'est l'environnement de développement utilisé, car pour une raison quelconque, code :: blocks ne charge pas les choses correctement ...: D

EDIT2: J'ai découvert de nouvelles choses si vous spécifiez un chemin statique comme celui-ci dans votre code (en supposant que Example.data soit quelque chose que vous devez charger):

"resources/Example.data"

Si vous lancez ensuite votre application à partir du répertoire réel (ou dans Windows, vous créez un raccourci et définissez le répertoire de travail sur votre répertoire d'application), cela fonctionnera comme ça. Gardez cela à l'esprit lorsque vous déboguez des problèmes liés aux chemins de ressources / fichiers manquants. (Surtout dans les IDE qui définissent le mauvais répertoire de travail lors du lancement d'un exe de build à partir de l'IDE)


1

Une solution de bibliothèque (même si je sais que cela n'a pas été demandé). Si vous utilisez Qt: QCoreApplication::applicationDirPath()


1

Juste mes deux cents, mais le code suivant ne fonctionne-t-il pas de manière portable en C ++ 17?

#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;

int main(int argc, char* argv[])
{
    std::cout << "Path is " << fs::path(argv[0]).parent_path() << '\n';
}

Semble fonctionner pour moi sur Linux au moins.

Sur la base de l'idée précédente, j'ai maintenant:

std::filesystem::path prepend_exe_path(const std::string& filename, const std::string& exe_path = "");

Avec mise en œuvre:

fs::path prepend_exe_path(const std::string& filename, const std::string& exe_path)
{
    static auto exe_parent_path = fs::path(exe_path).parent_path();
    return exe_parent_path / filename;
}

Et astuce d'initialisation dans main():

(void) prepend_exe_path("", argv[0]);

Merci @Sam Redway pour l'idée argv [0]. Et bien sûr, je comprends que C ++ 17 n'était pas là depuis de nombreuses années lorsque l'OP a posé la question.


0

Boost Filesystem se initial_path()comporte comme POSIX getcwd(), et ne fait pas non plus ce que vous voulez, mais en ajoutantargv[0] à l'un d'eux devrait le faire.

Vous pouvez noter que le résultat n'est pas toujours joli - vous pouvez obtenir des choses comme /foo/bar/../../baz/a.outou/foo/bar//baz/a.out , mais je crois qu'il en résulte toujours un chemin valide qui nomme l'exécutable (notez que les barres obliques consécutives dans un chemin sont réduites à un).

J'ai déjà écrit une solution en utilisant envp(le troisième argument main()fonctionnait sous Linux mais ne semblait pas réalisable sous Windows, donc je recommande essentiellement la même solution que quelqu'un d'autre, mais avec l'explication supplémentaire de la raison pour laquelle elle est réellement correcte même si les résultats ne sont pas jolis.


0

Comme Minok l'a mentionné, aucune fonctionnalité de ce type n'est spécifiée dans la norme C ou la norme C ++. Ceci est considéré comme une fonctionnalité purement spécifique au système d'exploitation et il est spécifié dans la norme POSIX, par exemple.

Thorsten79 a donné une bonne suggestion, c'est la bibliothèque Boost.Filesystem. Cependant, cela peut être gênant si vous ne souhaitez pas avoir de dépendances de temps de liaison sous forme binaire pour votre programme.

Une bonne alternative que je recommanderais est la collection de bibliothèques 100% STLSoft C ++ uniquement pour les en-têtes Matthew Wilson (auteur de livres incontournables sur C ++). Il existe une façade portable PlatformSTL donne accès à une API spécifique au système: WinSTL pour Windows et UnixSTL sous Unix, il s'agit donc d'une solution portable. Tous les éléments spécifiques au système sont spécifiés avec l'utilisation de traits et de politiques, il s'agit donc d'un cadre extensible. Il y a bien sûr une bibliothèque de système de fichiers.


0

La commande linux bash qui progname rapportera un chemin vers le programme.

Même si l'on pouvait émettre la commande which depuis votre programme et diriger la sortie vers un fichier tmp et que le programme lit ensuite ce fichier tmp, il ne vous dira pas si ce programme est celui qui s'exécute. Il vous indique uniquement où se trouve un programme portant ce nom.

Ce qui est nécessaire est d'obtenir votre numéro d'identification de processus et d'analyser le chemin d'accès au nom

Dans mon programme, je veux savoir si le programme a été exécuté à partir du répertoire bin de l'utilisateur ou d'un autre dans le chemin ou à partir de / usr / bin. / usr / bin contiendrait la version prise en charge. Mon sentiment est que sous Linux, il existe une seule solution qui est portable.


0

Utilisez realpath()dans stdlib.hcomme ceci:

char *working_dir_path = realpath(".", NULL);

0

Fonctionne à partir de C ++ 11, en utilisant un système de fichiers expérimental, et C ++ 14-C ++ 17 ainsi qu'en utilisant un système de fichiers officiel.

application.h:

#pragma once

//
// https://en.cppreference.com/w/User:D41D8CD98F/feature_testing_macros
//
#ifdef __cpp_lib_filesystem
#include <filesystem>
#else
#include <experimental/filesystem>

namespace std {
    namespace filesystem = experimental::filesystem;
}
#endif

std::filesystem::path getexepath();

application.cpp:

#include "application.h"
#ifdef _WIN32
#include <windows.h>    //GetModuleFileNameW
#else
#include <limits.h>
#include <unistd.h>     //readlink
#endif

std::filesystem::path getexepath()
{
#ifdef _WIN32
    wchar_t path[MAX_PATH] = { 0 };
    GetModuleFileNameW(NULL, path, MAX_PATH);
    return path;
#else
    char result[PATH_MAX];
    ssize_t count = readlink("/proc/self/exe", result, PATH_MAX);
    return std::string(result, (count > 0) ? count : 0);
#endif
}

Bonne réponse, mais c'est un comportement indéfini d'ajouter des déclarations ou des définitions à l'espace de nomsstd . Pour éviter cela, vous pouvez ajouter à la fois les espaces de noms std::filesystemet std::experimental::filesystemun troisième espace de noms de votre choix, ou simplement utiliser using std::filesystem::path, si cela ne vous dérange pas d'ajouter la déclaration de pathà l'espace de noms global.
Cássio Renan

Je suppose qu'après que C ++ 14 experimental :: filesystem n'est plus utilisé, vous pouvez simplement oublier cela? (entre dans la première branche #if)
TarmoPikaro
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.