# ============================================================
# GUIDE COMPLET: MODULE OS
# Interface avec le système d'exploitation
# ============================================================

import os
import sys
import stat
import time
from pathlib import Path


# ============================================================
# 1. INFORMATIONS SYSTÈME
# ============================================================

# NOM DU SYSTÈME D'EXPLOITATION
system_name = os.name
# 'posix' (Linux, Mac), 'nt' (Windows), 'java' (Jython)

# PLATEFORME DÉTAILLÉE
platform = sys.platform
# 'linux', 'darwin' (Mac), 'win32' (Windows)

# SÉPARATEUR DE CHEMIN
sep = os.sep                    # '/' sur Unix, '\\' sur Windows
altsep = os.altsep              # None sur Unix, '/' sur Windows
pathsep = os.pathsep            # ':' sur Unix, ';' sur Windows
linesep = os.linesep            # '\n' sur Unix, '\r\n' sur Windows

# EXTENSION EXÉCUTABLE
extsep = os.extsep              # '.' (séparateur d'extension)

# NULL DEVICE
devnull = os.devnull            # '/dev/null' Unix, 'nul' Windows

# CPU COUNT
cpu_count = os.cpu_count()      # Nombre de CPUs ou None

# INFORMATIONS DÉTAILLÉES
def system_info():
    """Affiche informations système complètes"""
    print(f"OS: {os.name}")
    print(f"Platform: {sys.platform}")
    print(f"Path separator: {os.sep}")
    print(f"Line separator: {repr(os.linesep)}")
    print(f"CPUs: {os.cpu_count()}")


# ============================================================
# 2. RÉPERTOIRES ET CHEMINS
# ============================================================

# RÉPERTOIRE COURANT
current_dir = os.getcwd()                   # Obtenir répertoire courant
os.chdir('/path/to/dir')                    # Changer répertoire courant

# RÉPERTOIRE TEMPORAIRE (Unix)
if hasattr(os, 'getuid'):
    temp = os.getenv('TMPDIR', '/tmp')

# EXPANSION DE CHEMINS
# Tilde (~)
home_path = os.path.expanduser('~/documents')  # /home/user/documents

# Variables d'environnement
expanded = os.path.expandvars('$HOME/documents')  # Développe $HOME

# CHEMIN ABSOLU
abs_path = os.path.abspath('file.txt')      # Chemin absolu
abs_path = os.path.abspath('.')             # Répertoire courant absolu
abs_path = os.path.abspath('..')            # Parent absolu

# CHEMIN RÉEL (résout symlinks)
real_path = os.path.realpath('link')        # Suit les liens symboliques
real_path = os.path.realpath('.')           # Chemin canonique

# CHEMIN RELATIF
rel_path = os.path.relpath('/home/user/docs', '/home/user')  # 'docs'
rel_path = os.path.relpath('/a/b/c', '/a/d')  # '../b/c'

# NORMALISATION
normalized = os.path.normpath('a//b/../c')  # 'a/c'
normalized = os.path.normpath('/a/./b')     # '/a/b'

# JOINTURE DE CHEMINS
path = os.path.join('folder', 'subfolder', 'file.txt')
path = os.path.join('/home', 'user', 'docs')  # /home/user/docs

# SPLIT DE CHEMINS
# Séparer dossier et fichier
dirname, filename = os.path.split('/home/user/file.txt')
# dirname = '/home/user', filename = 'file.txt'

# Séparer extension
name, ext = os.path.splitext('file.txt')
# name = 'file', ext = '.txt'

name, ext = os.path.splitext('archive.tar.gz')
# name = 'archive.tar', ext = '.gz'

# Séparer drive (Windows)
drive, path = os.path.splitdrive('C:\\Users\\file.txt')
# drive = 'C:', path = '\\Users\\file.txt'

# BASENAME ET DIRNAME
basename = os.path.basename('/home/user/file.txt')  # 'file.txt'
dirname = os.path.dirname('/home/user/file.txt')    # '/home/user'

# COMMON PREFIX/PATH
common = os.path.commonprefix(['/home/user/a', '/home/user/b'])
# '/home/user/'

common = os.path.commonpath(['/home/user/a', '/home/user/b'])
# '/home/user' (Python 3.5+)


# ============================================================
# 3. TESTS SUR CHEMINS
# ============================================================

path = '/home/user/file.txt'

# EXISTENCE
exists = os.path.exists(path)               # True si existe
lexists = os.path.lexists(path)             # True même si symlink cassé

# TYPE
is_file = os.path.isfile(path)              # Fichier régulier?
is_dir = os.path.isdir(path)                # Répertoire?
is_link = os.path.islink(path)              # Lien symbolique?
is_mount = os.path.ismount(path)            # Point de montage?

# CHEMIN ABSOLU
is_abs = os.path.isabs(path)                # Commence par / ou C:\ ?

# ACCÈS (Unix)
if hasattr(os, 'access'):
    readable = os.access(path, os.R_OK)     # Lecture?
    writable = os.access(path, os.W_OK)     # Écriture?
    executable = os.access(path, os.X_OK)   # Exécution?
    exists = os.access(path, os.F_OK)       # Existe?

# MÊME FICHIER
if os.path.exists('file1') and os.path.exists('file2'):
    same = os.path.samefile('file1', 'file2')  # Même inode?

# MÊME STATS
stat1 = os.stat('file1')
stat2 = os.stat('file2')
same_stat = os.path.samestat(stat1, stat2)

# MÊME DEVICE (pour montages)
if os.path.exists('path1') and os.path.exists('path2'):
    same_dev = os.path.sameopenfile(fd1, fd2)  # Unix uniquement


# ============================================================
# 4. LISTING DE RÉPERTOIRES
# ============================================================

# LISTDIR: Liste contenu
entries = os.listdir('.')                   # Liste noms dans répertoire
entries = os.listdir('/home/user')          # Retourne list[str]

# Filtrer fichiers
files = [f for f in os.listdir('.') if os.path.isfile(f)]

# Filtrer répertoires
dirs = [d for d in os.listdir('.') if os.path.isdir(d)]

# SCANDIR: Plus efficace (Python 3.5+)
with os.scandir('.') as entries:
    for entry in entries:
        print(entry.name)                   # Nom
        print(entry.path)                   # Chemin complet
        print(entry.is_file())              # Fichier?
        print(entry.is_dir())               # Répertoire?
        print(entry.is_symlink())           # Lien?
        stat_info = entry.stat()            # Statistiques

# SCANDIR avec filtre
def list_python_files(directory):
    """Liste tous les fichiers .py"""
    with os.scandir(directory) as entries:
        for entry in entries:
            if entry.is_file() and entry.name.endswith('.py'):
                yield entry.path

# WALK: Parcours récursif
for root, dirs, files in os.walk('.'):
    print(f"Directory: {root}")
    print(f"Subdirectories: {dirs}")
    print(f"Files: {files}")
    
    # Traiter chaque fichier
    for file in files:
        full_path = os.path.join(root, file)
        print(full_path)

# WALK avec topdown=False (bottom-up)
for root, dirs, files in os.walk('.', topdown=False):
    # Traite d'abord les feuilles
    for file in files:
        process_file(os.path.join(root, file))

# WALK suivre symlinks
for root, dirs, files in os.walk('.', followlinks=True):
    # Attention: risque de boucles infinies
    pass

# WALK avec gestion d'erreurs
def handle_error(error):
    print(f"Error: {error}")

for root, dirs, files in os.walk('.', onerror=handle_error):
    pass

# FWALK: Walk avec file descriptors (Unix, Python 3.3+)
if hasattr(os, 'fwalk'):
    for root, dirs, files, rootfd in os.fwalk('.'):
        # rootfd peut être utilisé avec *at() functions
        for file in files:
            # Opérations relatives à rootfd
            stat_info = os.stat(file, dir_fd=rootfd)


# ============================================================
# 5. CRÉATION ET SUPPRESSION
# ============================================================

# CRÉER RÉPERTOIRE
os.mkdir('new_folder')                      # Créer (parent doit exister)
os.mkdir('folder', mode=0o755)              # Avec permissions (Unix)

# CRÉER RÉPERTOIRES RÉCURSIFS
os.makedirs('a/b/c/d')                      # Crée toute la hiérarchie
os.makedirs('folder', exist_ok=True)        # Pas d'erreur si existe

# SUPPRIMER FICHIER
os.remove('file.txt')                       # Supprimer fichier
os.unlink('file.txt')                       # Alias de remove

# SUPPRIMER RÉPERTOIRE VIDE
os.rmdir('empty_folder')                    # Doit être vide

# SUPPRIMER RÉPERTOIRES VIDES RÉCURSIFS
os.removedirs('a/b/c')                      # Supprime c, puis b, puis a si vides

# SUPPRIMER ARBRE COMPLET (utiliser shutil)
import shutil
shutil.rmtree('folder')                     # Supprime tout récursivement


# ============================================================
# 6. RENOMMER ET DÉPLACER
# ============================================================

# RENOMMER
os.rename('old_name.txt', 'new_name.txt')   # Renommer/déplacer
# Erreur si destination existe (Unix)

# REPLACE: Écrase si existe
os.replace('old.txt', 'new.txt')            # Remplace destination
# Atomique sur même filesystem

# RENAMES: Récursif
os.renames('a/b/c/file.txt', 'x/y/z/file.txt')
# Crée x/y/z si nécessaire, supprime a/b/c si vide


# ============================================================
# 7. LIENS (Unix)
# ============================================================

if os.name == 'posix':
    # LIEN SYMBOLIQUE
    os.symlink('target', 'link_name')       # Crée symlink
    os.symlink('target', 'link', target_is_directory=True)  # Windows
    
    # LIRE CIBLE DU LIEN
    target = os.readlink('link_name')       # Retourne cible
    
    # HARD LINK
    os.link('existing_file', 'hard_link')   # Crée hard link
    
    # LINKAT (avec dir_fd, Python 3.3+)
    os.linkat(src_fd, 'source', dst_fd, 'dest')


# ============================================================
# 8. PERMISSIONS ET PROPRIÉTÉS (Unix)
# ============================================================

if os.name == 'posix':
    # CHANGER PERMISSIONS
    os.chmod('file.txt', 0o644)             # rw-r--r--
    os.chmod('script.sh', 0o755)            # rwxr-xr-x
    
    # Avec constantes stat
    os.chmod('file.txt', stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP)
    
    # CHANGER PROPRIÉTAIRE
    os.chown('file.txt', uid=1000, gid=1000)  # Nécessite root
    
    # CHANGER TIMESTAMPS
    # atime (accès), mtime (modification)
    os.utime('file.txt', (atime, mtime))
    os.utime('file.txt', None)              # Temps actuel
    
    # UTIME avec nanosecondes (Python 3.3+)
    os.utime('file.txt', ns=(atime_ns, mtime_ns))
    
    # Suivre ou non les symlinks
    os.utime('link', (atime, mtime), follow_symlinks=False)


# ============================================================
# 9. STATISTIQUES DE FICHIERS
# ============================================================

# STAT: Informations complètes
stat_info = os.stat('file.txt')

# Taille
size = stat_info.st_size                    # Taille en octets

# Timestamps
mtime = stat_info.st_mtime                  # Dernière modification
atime = stat_info.st_atime                  # Dernier accès
ctime = stat_info.st_ctime                  # Création (Windows) / changement (Unix)

# Conversion en datetime
from datetime import datetime
mod_time = datetime.fromtimestamp(stat_info.st_mtime)

# Timestamps nanosecondes (Python 3.3+)
mtime_ns = stat_info.st_mtime_ns
atime_ns = stat_info.st_atime_ns
ctime_ns = stat_info.st_ctime_ns

# Unix spécifique
if os.name == 'posix':
    mode = stat_info.st_mode                # Permissions
    uid = stat_info.st_uid                  # User ID
    gid = stat_info.st_gid                  # Group ID
    nlink = stat_info.st_nlink              # Nombre hard links
    ino = stat_info.st_ino                  # Inode
    dev = stat_info.st_dev                  # Device ID
    
    # Blocs et taille blocs
    blocks = stat_info.st_blocks            # Nombre de blocs 512-byte
    blksize = stat_info.st_blksize          # Taille de bloc

# LSTAT: Stat sans suivre symlinks
lstat_info = os.lstat('link')               # Stats du lien lui-même

# FSTAT: Stat depuis file descriptor
fd = os.open('file.txt', os.O_RDONLY)
fstat_info = os.fstat(fd)
os.close(fd)

# STAT avec dir_fd (Python 3.3+)
if hasattr(os, 'stat'):
    dir_fd = os.open('.', os.O_RDONLY)
    stat_info = os.stat('file.txt', dir_fd=dir_fd)
    os.close(dir_fd)

# ANALYSE DES PERMISSIONS
def analyze_permissions(path):
    """Décode les permissions d'un fichier"""
    st = os.stat(path)
    mode = st.st_mode
    
    # Type de fichier
    if stat.S_ISREG(mode):
        file_type = "Fichier régulier"
    elif stat.S_ISDIR(mode):
        file_type = "Répertoire"
    elif stat.S_ISLNK(mode):
        file_type = "Lien symbolique"
    elif stat.S_ISFIFO(mode):
        file_type = "FIFO"
    elif stat.S_ISSOCK(mode):
        file_type = "Socket"
    else:
        file_type = "Autre"
    
    # Permissions
    perms = stat.filemode(mode)             # '-rwxr-xr-x'
    
    print(f"Type: {file_type}")
    print(f"Permissions: {perms}")
    print(f"Taille: {st.st_size} octets")


# ============================================================
# 10. FILE DESCRIPTORS
# ============================================================

# OPEN: Ouvre fichier au niveau OS
fd = os.open('file.txt', os.O_RDONLY)       # Lecture seule
fd = os.open('file.txt', os.O_WRONLY)       # Écriture seule
fd = os.open('file.txt', os.O_RDWR)         # Lecture/écriture
fd = os.open('file.txt', os.O_CREAT | os.O_WRONLY, 0o644)  # Créer

# Flags communs
# os.O_RDONLY      Lecture seule
# os.O_WRONLY      Écriture seule
# os.O_RDWR        Lecture/écriture
# os.O_CREAT       Créer si n'existe pas
# os.O_EXCL        Erreur si existe (avec O_CREAT)
# os.O_TRUNC       Tronquer à 0
# os.O_APPEND      Append mode
# os.O_NONBLOCK    Non-bloquant (Unix)
# os.O_SYNC        Écriture synchrone
# os.O_DIRECTORY   Doit être répertoire (Unix)

# CLOSE: Fermer file descriptor
os.close(fd)

# READ: Lire depuis fd
fd = os.open('file.txt', os.O_RDONLY)
data = os.read(fd, 1024)                    # Lit max 1024 bytes
os.close(fd)

# WRITE: Écrire vers fd
fd = os.open('file.txt', os.O_WRONLY | os.O_CREAT, 0o644)
bytes_written = os.write(fd, b'Hello World')
os.close(fd)

# LSEEK: Déplacer curseur
fd = os.open('file.txt', os.O_RDWR)
os.lseek(fd, 0, os.SEEK_SET)               # Début du fichier
os.lseek(fd, 10, os.SEEK_CUR)              # +10 depuis position actuelle
os.lseek(fd, -5, os.SEEK_END)              # 5 bytes avant la fin
position = os.lseek(fd, 0, os.SEEK_CUR)    # Position actuelle
os.close(fd)

# TRUNCATE: Tronquer fichier
fd = os.open('file.txt', os.O_RDWR)
os.truncate(fd, 100)                        # Tronque à 100 bytes
os.close(fd)

# Ou avec path
os.truncate('file.txt', 100)

# FSYNC: Flush sur disque
fd = os.open('file.txt', os.O_WRONLY)
os.write(fd, b'Important data')
os.fsync(fd)                                # Force écriture disque
os.close(fd)

# FDATASYNC: Flush données sans métadonnées (Unix)
if hasattr(os, 'fdatasync'):
    os.fdatasync(fd)

# DUP: Dupliquer file descriptor
fd1 = os.open('file.txt', os.O_RDONLY)
fd2 = os.dup(fd1)                           # Même fichier, fd différent
os.close(fd1)
os.close(fd2)

# DUP2: Dupliquer vers fd spécifique
os.dup2(fd1, fd2)                           # fd2 pointe maintenant sur fd1

# PIPE: Créer pipe (Unix)
if hasattr(os, 'pipe'):
    read_fd, write_fd = os.pipe()           # Retourne 2 fds
    os.write(write_fd, b'Hello')
    data = os.read(read_fd, 100)
    os.close(read_fd)
    os.close(write_fd)


# ============================================================
# 11. VARIABLES D'ENVIRONNEMENT
# ============================================================

# ENVIRON: Dictionnaire des variables
env_vars = os.environ                       # Mapping complet
env_vars = dict(os.environ)                 # Copie en dict

# GETENV: Lire variable
home = os.getenv('HOME')                    # Retourne None si absent
home = os.getenv('HOME', '/default')        # Avec valeur par défaut
path = os.getenv('PATH')

# GET: Alternative avec environ
home = os.environ.get('HOME')
home = os.environ.get('HOME', '/default')

# ACCÈS DIRECT (lève KeyError si absent)
home = os.environ['HOME']

# PUTENV: Modifier variable (pour sous-processus)
os.putenv('MY_VAR', 'value')
# Note: Préférer os.environ pour modifier

# SETENV avec environ (recommandé)
os.environ['MY_VAR'] = 'value'              # Modifie pour sous-processus

# UNSETENV: Supprimer variable
os.unsetenv('MY_VAR')
# Ou
del os.environ['MY_VAR']

# ITERATION
for key, value in os.environ.items():
    print(f"{key}={value}")

# VARIABLES COMMUNES
def print_common_vars():
    """Affiche variables d'environnement communes"""
    print(f"HOME: {os.getenv('HOME')}")
    print(f"USER: {os.getenv('USER')}")
    print(f"PATH: {os.getenv('PATH')}")
    print(f"SHELL: {os.getenv('SHELL')}")
    print(f"LANG: {os.getenv('LANG')}")
    print(f"PWD: {os.getenv('PWD')}")


# ============================================================
# 12. PROCESSUS
# ============================================================

# PID ACTUEL
pid = os.getpid()                           # Process ID

# PPID (Parent Process ID - Unix)
if hasattr(os, 'getppid'):
    ppid = os.getppid()

# UID/GID (Unix)
if hasattr(os, 'getuid'):
    uid = os.getuid()                       # User ID réel
    euid = os.geteuid()                     # User ID effectif
    gid = os.getgid()                       # Group ID réel
    egid = os.getegid()                     # Group ID effectif
    
    # Groupes supplémentaires
    groups = os.getgroups()                 # Liste des group IDs
    
    # SETUID/SETGID (nécessite privilèges)
    # os.setuid(1000)
    # os.setgid(1000)
    # os.setgroups([1000, 1001])

# PRIORITY (nice value - Unix)
if hasattr(os, 'nice'):
    current_nice = os.nice(0)               # Obtenir nice
    os.nice(5)                              # Augmenter nice (+5)

# SYSTEM: Exécuter commande shell
return_code = os.system('ls -la')           # Retourne exit code
# ATTENTION: Vulnérable à injection shell

# EXEC: Remplacer processus actuel (Unix)
if hasattr(os, 'execv'):
    # os.execv('/bin/ls', ['ls', '-la'])    # Ne retourne jamais
    # os.execvp('ls', ['ls', '-la'])        # Cherche dans PATH
    # os.execve('/bin/ls', ['ls'], env)     # Avec environnement
    pass

# FORK: Créer processus enfant (Unix)
if hasattr(os, 'fork'):
    pid = os.fork()
    if pid == 0:
        # Processus enfant
        print("Child process")
        os._exit(0)                         # _exit évite cleanup Python
    else:
        # Processus parent
        print(f"Parent, child PID: {pid}")
        os.wait()                           # Attend enfant

# WAIT: Attendre processus enfant (Unix)
if hasattr(os, 'wait'):
    pid, status = os.wait()                 # Attend n'importe quel enfant
    pid, status = os.waitpid(child_pid, 0)  # Attend PID spécifique
    
    # Options de wait
    # os.WNOHANG        Ne pas bloquer
    # os.WUNTRACED      Rapport stopped children
    
    # Analyser status
    if os.WIFEXITED(status):
        exit_code = os.WEXITSTATUS(status)
    if os.WIFSIGNALED(status):
        signal_num = os.WTERMSIG(status)

# SPAWN: Créer nouveau processus (cross-platform)
if hasattr(os, 'spawnv'):
    # os.P_WAIT         Attend complétion
    # os.P_NOWAIT       Retourne immédiatement
    # os.P_NOWAITO      Comme P_NOWAIT
    
    pid = os.spawnv(os.P_NOWAIT, '/bin/ls', ['ls', '-la'])
    pid = os.spawnvp(os.P_WAIT, 'ls', ['ls', '-la'])  # Cherche PATH

# KILL: Envoyer signal (Unix)
if hasattr(os, 'kill'):
    import signal
    os.kill(pid, signal.SIGTERM)            # Terminer processus
    os.kill(pid, signal.SIGKILL)            # Tuer immédiatement
    os.kill(pid, signal.SIGUSR1)            # Signal utilisateur

# KILLPG: Tuer process group (Unix)
if hasattr(os, 'killpg'):
    os.killpg(pgid, signal.SIGTERM)


# ============================================================
# 13. TUBES ET IPC (Unix)
# ============================================================

if hasattr(os, 'pipe'):
    # PIPE: Communication simple
    read_fd, write_fd = os.pipe()
    
    if os.fork() == 0:
        # Enfant: écriture
        os.close(read_fd)
        os.write(write_fd, b'Message from child')
        os.close(write_fd)
        os._exit(0)
    else:
        # Parent: lecture
        os.close(write_fd)
        message = os.read(read_fd, 1024)
        os.close(read_fd)
        os.wait()
        print(message.decode())

# PIPE2: Avec flags (Linux)
if hasattr(os, 'pipe2'):
    read_fd, write_fd = os.pipe2(os.O_NONBLOCK | os.O_CLOEXEC)

# MKFIFO: Named pipe (Unix)
if hasattr(os, 'mkfifo'):
    os.mkfifo('/tmp/myfifo', mode=0o600)
    
    # Écriture
    with open('/tmp/myfifo', 'w') as f:
        f.write('Hello')
    
    # Lecture (dans autre processus)
    with open('/tmp/myfifo', 'r') as f:
        data = f.read()


# ============================================================
# 14. TERMINAUX (Unix)
# ============================================================

if os.name == 'posix':
    # ISATTY: Est un terminal?
    if os.isatty(0):                        # stdin
        print("Running in terminal")
    
    if os.isatty(1):                        # stdout
        print("stdout is a terminal")
    
    # TTYNAME: Nom du terminal
    if hasattr(os, 'ttyname'):
        tty = os.ttyname(0)                 # '/dev/pts/0'
    
    # CTERMID: Nom terminal contrôlant
    if hasattr(os, 'ctermid'):
        controlling_tty = os.ctermid()
    
    # OPENPTY: Créer pseudo-terminal
    if hasattr(os, 'openpty'):
        master, slave = os.openpty()
        # master, slave sont des file descriptors
        os.close(master)
        os.close(slave)
    
    # TCGETPGRP/TCSETPGRP: Process group du terminal
    if hasattr(os, 'tcgetpgrp'):
        pgid = os.tcgetpgrp(0)              # Process group


# ============================================================
# 15. FILESYSTEM AVANCÉ (Unix)
# ============================================================

if os.name == 'posix':
    # STATVFS: Statistiques filesystem
    if hasattr(os, 'statvfs'):
        stat_vfs = os.statvfs('/')
        
        block_size = stat_vfs.f_bsize       # Taille de bloc
        total_blocks = stat_vfs.f_blocks    # Blocs totaux
        free_blocks = stat_vfs.f_bfree      # Blocs libres
        avail_blocks = stat_vfs.f_bavail    # Blocs disponibles (non-root)
        
        # Calculs
        total_size = block_size * total_blocks
        free_size = block_size * free_blocks
        used_size = total_size - free_size
        
        print(f"Total: {total_size / (1024**3):.2f} GB")
        print(f"Used: {used_size / (1024**3):.2f} GB")
        print(f"Free: {free_size / (1024**3):.2f} GB")
    
    # PATHCONF: Configuration du path
    if hasattr(os, 'pathconf'):
        # Nom max de fichier
        name_max = os.pathconf('/', 'PC_NAME_MAX')
        # Path max
        path_max = os.pathconf('/', 'PC_PATH_MAX')
    
    # CHROOT: Changer root (nécessite root)
    # os.chroot('/new/root')
    
    # CHDIR vers file descriptor
    if hasattr(os, 'fchdir'):
        fd = os.open('/tmp', os.O_RDONLY)
        os.fchdir(fd)
        os.close(fd)
    
    # SYNC: Flush tous les buffers
    if hasattr(os, 'sync'):
        os.sync()                           # Force écriture disque


# ============================================================
# 16. RANDOM
# ============================================================

# URANDOM: Bytes aléatoires cryptographiques
random_bytes = os.urandom(16)               # 16 bytes aléatoires
random_bytes = os.urandom(32)               # Pour clés crypto

# Conversion en hex
hex_string = random_bytes.hex()

# GETRANDOM (Linux 3.17+, Python 3.6+)
if hasattr(os, 'getrandom'):
    # Plus moderne que urandom
    random_data = os.getrandom(32, flags=0)
    # flags: os.GRND_RANDOM, os.GRND_NONBLOCK


# ============================================================
# 17. EXTENDED ATTRIBUTES (Unix)
# ============================================================

if hasattr(os, 'getxattr'):
    # GETXATTR: Lire attribut étendu
    try:
        value = os.getxattr('file.txt', 'user.comment')
    except OSError:
        pass
    
    # SETXATTR: Définir attribut étendu
    os.setxattr('file.txt', 'user.comment', b'My comment')
    
    # LISTXATTR: Liste attributs
    attrs = os.listxattr('file.txt')
    for attr in attrs:
        value = os.getxattr('file.txt', attr)
        print(f"{attr}: {value}")
    
    # REMOVEXATTR: Supprimer attribut
    os.removexattr('file.txt', 'user.comment')
    
    # Avec follow_symlinks
    os.setxattr('link', 'user.attr', b'value', follow_symlinks=False)


# ============================================================
# 18. SCHEDULING (Unix)
# ============================================================

if hasattr(os, 'sched_getaffinity'):
    # CPU AFFINITY: CPUs disponibles pour processus
    cpus = os.sched_getaffinity(0)          # 0 = processus actuel
    print(f"CPUs: {cpus}")                  # {0, 1, 2, 3}
    
    # Définir CPUs
    os.sched_setaffinity(0, {0, 1})         # Utiliser CPUs 0 et 1 seulement

# SCHEDULING PRIORITY
if hasattr(os, 'sched_get_priority_min'):
    # SCHED_FIFO, SCHED_RR, SCHED_OTHER
    min_prio = os.sched_get_priority_min(os.SCHED_FIFO)
    max_prio = os.sched_get_priority_max(os.SCHED_FIFO)
    
    # Obtenir paramètres scheduling
    param = os.sched_getparam(0)
    priority = os.sched_getscheduler(0)
    
    # Définir scheduling (nécessite privilèges)
    # os.sched_setscheduler(0, os.SCHED_FIFO, os.sched_param(10))


# ============================================================
# 19. MÉMOIRE ET RESSOURCES (Unix)
# ============================================================

# LOADAVG: Charge système (Unix)
if hasattr(os, 'getloadavg'):
    load1, load5, load15 = os.getloadavg()
    print(f"Load average: {load1:.2f} {load5:.2f} {load15:.2f}")

# RESSOURCES avec resource module
try:
    import resource
    
    # Limites ressources
    soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
    print(f"File descriptors: {soft} (soft) / {hard} (hard)")
    
    # Définir limites
    resource.setrlimit(resource.RLIMIT_NOFILE, (4096, 4096))
    
    # Usage ressources
    usage = resource.getrusage(resource.RUSAGE_SELF)
    print(f"User time: {usage.ru_utime}s")
    print(f"System time: {usage.ru_stime}s")
    print(f"Max RSS: {usage.ru_maxrss} KB")
except ImportError:
    pass


# ============================================================
# 20. CONTEXTES D'EXÉCUTION
# ============================================================

# CHDIR comme context manager (Python 3.11+)
if hasattr(os, 'chdir'):
    # Méthode manuelle
    old_cwd = os.getcwd()
    try:
        os.chdir('/tmp')
        # Travail dans /tmp
    finally:
        os.chdir(old_cwd)

# SCANDIR comme context manager
with os.scandir('.') as it:
    for entry in it:
        print(entry.name)
# Fermeture automatique

# FILE DESCRIPTOR avec context
class FileDescriptor:
    """Context manager pour file descriptors"""
    def __init__(self, path, flags):
        self.path = path
        self.flags = flags
        self.fd = None
    
    def __enter__(self):
        self.fd = os.open(self.path, self.flags)
        return self.fd
    
    def __exit__(self, *args):
        if self.fd is not None:
            os.close(self.fd)

# Usage
with FileDescriptor('file.txt', os.O_RDONLY) as fd:
    data = os.read(fd, 1024)


# ============================================================
# 21. CHEMINS SPÉCIAUX
# ============================================================

# SUPPORTS_* : Fonctionnalités supportées
supports_dir_fd = os.open in os.supports_dir_fd
supports_fd = os.stat in os.supports_fd
supports_follow_symlinks = os.stat in os.supports_follow_symlinks

# Vérifier support
def check_feature_support():
    """Vérifie quelles fonctionnalités sont supportées"""
    print(f"dir_fd: {os.open in os.supports_dir_fd}")
    print(f"fd: {os.stat in os.supports_fd}")
    print(f"follow_symlinks: {os.stat in os.supports_follow_symlinks}")


# ============================================================
# 22. DEVICE NUMBERS (Unix)
# ============================================================

if os.name == 'posix':
    # MAKEDEV: Créer device number
    if hasattr(os, 'makedev'):
        dev = os.makedev(major=8, minor=1)  # /dev/sda1
    
    # MAJOR/MINOR: Extraire composants
    if hasattr(os, 'major'):
        stat_info = os.stat('/dev/sda1')
        major = os.major(stat_info.st_dev)
        minor = os.minor(stat_info.st_dev)
        print(f"Device: major={major}, minor={minor}")


# ============================================================
# 23. COPY-ON-WRITE ET CLONAGE (Linux)
# ============================================================

# COPY_FILE_RANGE (Linux 4.5+, Python 3.8+)
if hasattr(os, 'copy_file_range'):
    src_fd = os.open('source.txt', os.O_RDONLY)
    dst_fd = os.open('dest.txt', os.O_WRONLY | os.O_CREAT)
    
    # Copie efficace kernel-space
    copied = os.copy_file_range(src_fd, dst_fd, 1024)
    
    os.close(src_fd)
    os.close(dst_fd)

# SENDFILE (Linux, Python 3.3+)
if hasattr(os, 'sendfile'):
    # Copie zero-copy très efficace
    src_fd = os.open('source.txt', os.O_RDONLY)
    dst_fd = os.open('dest.txt', os.O_WRONLY | os.O_CREAT)
    
    offset = 0
    size = os.fstat(src_fd).st_size
    os.sendfile(dst_fd, src_fd, offset, size)
    
    os.close(src_fd)
    os.close(dst_fd)


# ============================================================
# 24. LOCKING (Unix)
# ============================================================

if os.name == 'posix':
    import fcntl
    
    # FILE LOCKING
    fd = os.open('file.txt', os.O_RDWR)
    
    # Exclusive lock
    fcntl.flock(fd, fcntl.LOCK_EX)
    # Travail avec fichier...
    fcntl.flock(fd, fcntl.LOCK_UN)  # Unlock
    
    # Non-blocking lock
    try:
        fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
    except IOError:
        print("File is locked by another process")
    
    os.close(fd)
    
    # RECORD LOCKING (POSIX)
    fd = os.open('file.txt', os.O_RDWR)
    
    # Lock bytes 0-99
    fcntl.lockf(fd, fcntl.LOCK_EX, 100, 0, os.SEEK_SET)
    # Travail...
    fcntl.lockf(fd, fcntl.LOCK_UN, 100, 0, os.SEEK_SET)
    
    os.close(fd)


# ============================================================
# 25. POSIX SPAWN (Moderne)
# ============================================================

if hasattr(os, 'posix_spawn'):
    # POSIX_SPAWN: Alternative moderne à fork/exec
    pid = os.posix_spawn(
        '/bin/ls',
        ['ls', '-la'],
        os.environ
    )
    os.waitpid(pid, 0)
    
    # POSIX_SPAWNP: Cherche dans PATH
    pid = os.posix_spawnp(
        'ls',
        ['ls', '-la'],
        os.environ
    )


# ============================================================
# 26. SIGNAUX (Unix)
# ============================================================

if os.name == 'posix':
    import signal
    
    # KILL: Envoyer signal
    os.kill(pid, signal.SIGTERM)
    os.kill(pid, signal.SIGKILL)
    os.kill(pid, signal.SIGHUP)
    
    # KILLPG: À process group
    os.killpg(pgid, signal.SIGTERM)
    
    # GETPGID: Obtenir process group
    pgid = os.getpgid(pid)
    
    # SETPGID: Définir process group
    os.setpgid(0, 0)  # Nouveau process group
    
    # SESSION
    os.setsid()       # Nouvelle session (détache terminal)
    sid = os.getsid(0)  # Session ID


# ============================================================
# 27. UTILITAIRES OS.PATH
# ============================================================

# GETSIZE: Taille du fichier
size = os.path.getsize('file.txt')          # En octets

# GETMTIME/GETATIME/GETCTIME: Timestamps
mtime = os.path.getmtime('file.txt')        # Modification
atime = os.path.getatime('file.txt')        # Accès
ctime = os.path.getctime('file.txt')        # Création/changement

# Conversion en date lisible
from datetime import datetime
mod_date = datetime.fromtimestamp(mtime)
print(f"Modifié: {mod_date}")

# SUPPORTS_UNICODE_FILENAMES
unicode_support = os.path.supports_unicode_filenames
print(f"Unicode filenames: {unicode_support}")


# ============================================================
# 28. EXEMPLES PRATIQUES
# ============================================================

# TROUVER TOUS LES FICHIERS D'UN TYPE
def find_files(directory, extension):
    """Trouve tous les fichiers avec extension donnée"""
    results = []
    for root, dirs, files in os.walk(directory):
        for file in files:
            if file.endswith(extension):
                results.append(os.path.join(root, file))
    return results

# Usage
py_files = find_files('.', '.py')

# CALCULER TAILLE D'UN RÉPERTOIRE
def get_directory_size(path):
    """Calcule taille totale d'un répertoire"""
    total = 0
    for root, dirs, files in os.walk(path):
        for file in files:
            filepath = os.path.join(root, file)
            if os.path.isfile(filepath):
                total += os.path.getsize(filepath)
    return total

size = get_directory_size('.')
print(f"Size: {size / (1024**2):.2f} MB")

# COPIER FICHIER (bas niveau)
def copy_file(src, dst):
    """Copie fichier avec os.open/read/write"""
    src_fd = os.open(src, os.O_RDONLY)
    dst_fd = os.open(dst, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o644)
    
    try:
        while True:
            chunk = os.read(src_fd, 65536)  # 64KB chunks
            if not chunk:
                break
            os.write(dst_fd, chunk)
    finally:
        os.close(src_fd)
        os.close(dst_fd)

# CRÉER STRUCTURE DE RÉPERTOIRES
def create_project_structure(base_path):
    """Crée structure complète de projet"""
    structure = {
        'src': ['__init__.py', 'main.py'],
        'tests': ['__init__.py', 'test_main.py'],
        'docs': ['README.md'],
        'data': []
    }
    
    for folder, files in structure.items():
        folder_path = os.path.join(base_path, folder)
        os.makedirs(folder_path, exist_ok=True)
        
        for file in files:
            file_path = os.path.join(folder_path, file)
            # Créer fichier vide
            open(file_path, 'a').close()

# LISTER FICHIERS PAR DATE
def list_files_by_date(directory):
    """Liste fichiers triés par date de modification"""
    files = []
    for entry in os.scandir(directory):
        if entry.is_file():
            stat_info = entry.stat()
            files.append((entry.path, stat_info.st_mtime))
    
    # Trier par date (plus récent d'abord)
    files.sort(key=lambda x: x[1], reverse=True)
    
    for path, mtime in files:
        date = datetime.fromtimestamp(mtime)
        print(f"{date}: {path}")

# NETTOYER FICHIERS ANCIENS
def cleanup_old_files(directory, days=30):
    """Supprime fichiers modifiés il y a plus de N jours"""
    cutoff = time.time() - (days * 86400)
    deleted = 0
    
    for root, dirs, files in os.walk(directory):
        for file in files:
            filepath = os.path.join(root, file)
            if os.path.getmtime(filepath) < cutoff:
                os.remove(filepath)
                deleted += 1
    
    return deleted

# CHANGER PERMISSIONS RÉCURSIVEMENT
def chmod_recursive(path, file_mode=0o644, dir_mode=0o755):
    """Change permissions récursivement"""
    if os.name != 'posix':
        return
    
    for root, dirs, files in os.walk(path):
        # Changer permissions des répertoires
        for dir_name in dirs:
            dir_path = os.path.join(root, dir_name)
            os.chmod(dir_path, dir_mode)
        
        # Changer permissions des fichiers
        for file_name in files:
            file_path = os.path.join(root, file_name)
            os.chmod(file_path, file_mode)

# TROUVER FICHIERS DUPLIQUÉS
def find_duplicates(directory):
    """Trouve fichiers avec même taille et contenu"""
    import hashlib
    
    # Index par taille
    size_map = {}
    for root, dirs, files in os.walk(directory):
        for file in files:
            filepath = os.path.join(root, file)
            if os.path.isfile(filepath):
                size = os.path.getsize(filepath)
                size_map.setdefault(size, []).append(filepath)
    
    # Vérifier hash pour fichiers de même taille
    duplicates = []
    for size, files in size_map.items():
        if len(files) > 1:
            hash_map = {}
            for filepath in files:
                h = hashlib.md5()
                with open(filepath, 'rb') as f:
                    h.update(f.read())
                file_hash = h.hexdigest()
                hash_map.setdefault(file_hash, []).append(filepath)
            
            for file_hash, paths in hash_map.items():
                if len(paths) > 1:
                    duplicates.append(paths)
    
    return duplicates

# CRÉER BACKUP D'UN FICHIER
def backup_file(filepath):
    """Crée backup avec timestamp"""
    if not os.path.exists(filepath):
        return None
    
    timestamp = time.strftime('%Y%m%d_%H%M%S')
    backup_path = f"{filepath}.{timestamp}.bak"
    
    # Copier
    import shutil
    shutil.copy2(filepath, backup_path)
    return backup_path

# WATCHER DE RÉPERTOIRE (simple)
def watch_directory(directory, interval=1):
    """Surveille changements dans répertoire"""
    # État initial
    files = {}
    for entry in os.scandir(directory):
        if entry.is_file():
            files[entry.path] = entry.stat().st_mtime
    
    print(f"Watching {directory}...")
    
    while True:
        time.sleep(interval)
        
        current_files = {}
        for entry in os.scandir(directory):
            if entry.is_file():
                current_files[entry.path] = entry.stat().st_mtime
        
        # Nouveaux fichiers
        new_files = set(current_files) - set(files)
        for filepath in new_files:
            print(f"Created: {filepath}")
        
        # Fichiers supprimés
        deleted = set(files) - set(current_files)
        for filepath in deleted:
            print(f"Deleted: {filepath}")
        
        # Fichiers modifiés
        for filepath in set(files) & set(current_files):
            if files[filepath] != current_files[filepath]:
                print(f"Modified: {filepath}")
        
        files = current_files


# ============================================================
# 29. SÉCURITÉ
# ============================================================

# VÉRIFIER PERMISSIONS AVANT ACCÈS
def safe_read(filepath):
    """Vérifie permissions avant lecture"""
    if not os.path.exists(filepath):
        raise FileNotFoundError(f"{filepath} not found")
    
    if not os.access(filepath, os.R_OK):
        raise PermissionError(f"Cannot read {filepath}")
    
    with open(filepath, 'r') as f:
        return f.read()

# ÉVITER RACE CONDITIONS
def safe_create_file(filepath):
    """Crée fichier de manière atomique"""
    # O_EXCL assure création uniquement si n'existe pas
    fd = os.open(filepath, os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o600)
    try:
        os.write(fd, b'Initial content')
    finally:
        os.close(fd)

# VÉRIFIER CHEMINS (éviter directory traversal)
def safe_join(base, user_path):
    """Joint chemins en vérifiant qu'on reste dans base"""
    # Résoudre chemins absolus
    base = os.path.abspath(base)
    full_path = os.path.abspath(os.path.join(base, user_path))
    
    # Vérifier que full_path est sous base
    if not full_path.startswith(base + os.sep):
        raise ValueError("Path escape attempt detected")
    
    return full_path

# CRÉER FICHIER TEMPORAIRE SÉCURISÉ
import tempfile

def secure_temp_file():
    """Crée fichier temporaire sécurisé"""
    fd, path = tempfile.mkstemp(prefix='secure_', suffix='.tmp')
    try:
        os.write(fd, b'Sensitive data')
        os.fsync(fd)
    finally:
        os.close(fd)
    
    return path


# ============================================================
# 30. PERFORMANCE ET OPTIMISATION
# ============================================================

# SCANDIR vs LISTDIR
def compare_listing_methods(directory):
    """Compare performance scandir vs listdir"""
    import timeit
    
    def with_listdir():
        files = []
        for name in os.listdir(directory):
            path = os.path.join(directory, name)
            if os.path.isfile(path):
                files.append((path, os.path.getsize(path)))
        return files
    
    def with_scandir():
        files = []
        with os.scandir(directory) as entries:
            for entry in entries:
                if entry.is_file():
                    files.append((entry.path, entry.stat().st_size))
        return files
    
    time_listdir = timeit.timeit(with_listdir, number=100)
    time_scandir = timeit.timeit(with_scandir, number=100)
    
    print(f"listdir: {time_listdir:.3f}s")
    print(f"scandir: {time_scandir:.3f}s (faster)")

# BATCH OPERATIONS
def batch_chmod(files, mode):
    """Change permissions en batch"""
    if os.name != 'posix':
        return
    
    for filepath in files:
        try:
            os.chmod(filepath, mode)
        except OSError as e:
            print(f"Error on {filepath}: {e}")

# MÉMOIRE-EFFICIENT FILE READING
def process_large_file(filepath, chunk_size=65536):
    """Traite gros fichier par chunks"""
    fd = os.open(filepath, os.O_RDONLY)
    try:
        while True:
            chunk = os.read(fd, chunk_size)
            if not chunk:
                break
            # Traiter chunk
            process_chunk(chunk)
    finally:
        os.close(fd)


# ============================================================
# 31. COMPATIBILITÉ CROSS-PLATFORM
# ============================================================

# CHEMINS PORTABLES
def portable_path(*parts):
    """Crée chemin portable cross-platform"""
    return os.path.join(*parts)

# DÉTECTION PLATEFORME
def get_platform_info():
    """Informations plateforme"""
    info = {
        'os_name': os.name,
        'platform': sys.platform,
        'sep': os.sep,
        'is_windows': os.name == 'nt',
        'is_unix': os.name == 'posix',
        'is_mac': sys.platform == 'darwin',
        'is_linux': sys.platform.startswith('linux')
    }
    return info

# FONCTION CONDITIONNELLE
def platform_specific_operation(path):
    """Opération spécifique à la plateforme"""
    if os.name == 'nt':
        # Windows
        return windows_operation(path)
    elif os.name == 'posix':
        # Unix/Linux/Mac
        return unix_operation(path)
    else:
        raise NotImplementedError(f"Unsupported OS: {os.name}")

# NORMALISATION CHEMINS CROSS-PLATFORM
def normalize_path(path):
    """Normalise chemin pour plateforme actuelle"""
    # Convertit séparateurs
    if os.name == 'nt':
        path = path.replace('/', '\\')
    else:
        path = path.replace('\\', '/')
    
    return os.path.normpath(path)


# ============================================================
# 32. BONNES PRATIQUES
# ============================================================

"""
[OK] BONNES PRATIQUES:

1. PRÉFÉRER pathlib POUR NOUVEAU CODE
   - Plus moderne et orienté objet
   - Path('/path/to/file').read_text() vs open()

2. TOUJOURS FERMER FILE DESCRIPTORS
   - Utiliser try/finally ou context managers
   - Les FDs sont des ressources limitées

3. VÉRIFIER EXISTENCE AVANT OPÉRATIONS
   - os.path.exists() avant suppression
   - os.access() pour vérifier permissions

4. UTILISER CHEMINS ABSOLUS
   - os.path.abspath() pour éviter ambiguïtés
   - Surtout si os.chdir() est utilisé

5. GÉRER EXCEPTIONS
   - FileNotFoundError, PermissionError, etc.
   - Toujours prévoir cas d'erreur

6. SCANDIR > LISTDIR
   - Plus rapide pour grandes listes
   - Évite appels stat() supplémentaires

7. ÉVITER os.system()
   - Vulnérable aux injections shell
   - Préférer subprocess.run()

8. PERMISSIONS SÉCURISÉES
   - 0o600 pour fichiers sensibles (rw-------)
   - 0o644 pour fichiers publics (rw-r--r--)
   - 0o755 pour exécutables (rwxr-xr-x)

9. ATOMICITÉ
   - Utiliser O_EXCL pour création exclusive
   - rename() est atomique sur même filesystem

10. PORTABILITÉ
    - Utiliser os.path.join() au lieu de '/'
    - Tester sur toutes plateformes cibles
"""


# ============================================================
# 33. ERREURS COURANTES ET SOLUTIONS
# ============================================================

# [X] ERREUR: Oublier de fermer FD
def bad_fd_usage():
    fd = os.open('file.txt', os.O_RDONLY)
    data = os.read(fd, 1024)
    # [X] FD jamais fermé = fuite ressource

# [OK] SOLUTION: Toujours fermer
def good_fd_usage():
    fd = os.open('file.txt', os.O_RDONLY)
    try:
        data = os.read(fd, 1024)
    finally:
        os.close(fd)

# [X] ERREUR: Chemins codés en dur
def bad_path():
    return '/home/user/file.txt'  # [X] Non portable

# [OK] SOLUTION: Construction dynamique
def good_path():
    home = os.path.expanduser('~')
    return os.path.join(home, 'file.txt')

# [X] ERREUR: Ignorer exceptions
def bad_error_handling():
    os.remove('file.txt')  # [X] Plante si absent

# [OK] SOLUTION: Gérer exceptions
def good_error_handling():
    try:
        os.remove('file.txt')
    except FileNotFoundError:
        pass  # Fichier n'existe pas, OK
    except PermissionError:
        print("Permission denied")

# [X] ERREUR: Race condition
def bad_race_condition():
    if os.path.exists('file.txt'):
        # [X] Fichier peut être supprimé ici!
        os.remove('file.txt')

# [OK] SOLUTION: EAFP (Easier to Ask Forgiveness than Permission)
def good_race_avoidance():
    try:
        os.remove('file.txt')
    except FileNotFoundError:
        pass


# ============================================================
# 34. RESSOURCES ET DOCUMENTATION
# ============================================================

"""
DOCUMENTATION OFFICIELLE:
- https://docs.python.org/3/library/os.html
- https://docs.python.org/3/library/os.path.html

MODULES CONNEXES:
- pathlib: Interface moderne orientée objet
- shutil: Opérations haut niveau (copy, move, rmtree)
- glob: Pattern matching pour fichiers
- tempfile: Fichiers et répertoires temporaires
- stat: Constantes et fonctions pour st_mode
- fnmatch: Unix shell-style pattern matching

MIGRATION:
- os.path -> pathlib.Path (recommandé pour nouveau code)
- os.system() -> subprocess.run()
- os.popen() -> subprocess.Popen()

CONCEPTS CLÉS:
1. File Descriptors: Handles bas niveau
2. Permissions: Mode Unix (rwxrwxrwx)
3. Symlinks: Liens symboliques vs hard links
4. inodes: Identificateurs uniques fichiers
5. Atomicité: Opérations indivisibles
6. Race conditions: Problèmes concurrence
7. TOCTOU: Time-of-check to time-of-use

DIFFÉRENCES PLATEFORMES:
- Windows: Chemins avec \, drives (C:), case-insensitive
- Unix: Chemins avec /, case-sensitive, permissions
- Mac: Comme Unix mais filesystem parfois case-insensitive

SÉCURITÉ:
- Valider chemins utilisateur
- Utiliser permissions restrictives
- Éviter TOCTOU avec opérations atomiques
- Ne jamais faire confiance à os.system() avec input utilisateur
"""

# Helpers pour exemples
def fetch_data(i):
    """Placeholder"""
    return f"data_{i}"

def good_task():
    """Placeholder"""
    pass

def failing_task():
    """Placeholder"""
    raise ValueError("Error")

def slow_task():
    """Placeholder"""
    time.sleep(10)

def slow_operation():
    """Placeholder"""
    time.sleep(5)

def process_item(item):
    """Placeholder"""
    pass

def my_async_function():
    """Placeholder"""
    pass

def process_chunk(chunk):
    """Placeholder"""
    pass

def windows_operation(path):
    """Placeholder"""
    pass

def unix_operation(path):
    """Placeholder"""
    pass