Coverage Report

Created: 2025-09-04 07:15

/src/mpv/osdep/path-unix.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * This file is part of mpv.
3
 *
4
 * mpv is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
6
 * License as published by the Free Software Foundation; either
7
 * version 2.1 of the License, or (at your option) any later version.
8
 *
9
 * mpv is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15
 * License along with mpv.  If not, see <http://www.gnu.org/licenses/>.
16
 */
17
18
#include <string.h>
19
20
#include "misc/bstr.h"
21
#include "options/path.h"
22
#include "osdep/threads.h"
23
#include "path.h"
24
25
#include "config.h"
26
27
static mp_once path_init_once = MP_STATIC_ONCE_INITIALIZER;
28
29
28
#define CONF_MAX 512
30
static char mpv_home[CONF_MAX];
31
static char old_home[CONF_MAX];
32
static char mpv_cache[CONF_MAX];
33
static char old_cache[CONF_MAX];
34
static char mpv_desktop[CONF_MAX];
35
static char mpv_state[CONF_MAX];
36
26
#define MKPATH(BUF, ...) (snprintf((BUF), CONF_MAX, __VA_ARGS__) >= CONF_MAX)
37
38
static void path_init(void)
39
2
{
40
2
    char *home = getenv("HOME");
41
2
    char *xdg_cache = getenv("XDG_CACHE_HOME");
42
2
    char *xdg_config = getenv("XDG_CONFIG_HOME");
43
2
    char *xdg_state = getenv("XDG_STATE_HOME");
44
45
2
    bool err = false;
46
2
    if (xdg_config && xdg_config[0]) {
47
0
        err = err || MKPATH(mpv_home, "%s/mpv", xdg_config);
48
2
    } else if (home && home[0]) {
49
2
        err = err || MKPATH(mpv_home, "%s/.config/mpv", home);
50
2
    }
51
52
    // Maintain compatibility with old ~/.mpv
53
2
    if (home && home[0]) {
54
2
        err = err || MKPATH(old_home, "%s/.mpv", home);
55
2
        err = err || MKPATH(old_cache, "%s/.mpv/cache", home);
56
2
    }
57
58
2
    if (xdg_cache && xdg_cache[0]) {
59
0
        err = err || MKPATH(mpv_cache, "%s/mpv", xdg_cache);
60
2
    } else if (home && home[0]) {
61
2
        err = err || MKPATH(mpv_cache, "%s/.cache/mpv", home);
62
2
    }
63
64
2
    if (xdg_state && xdg_state[0]) {
65
0
        err = err || MKPATH(mpv_state, "%s/mpv", xdg_state);
66
2
    } else if (home && home[0]) {
67
2
        err = err || MKPATH(mpv_state, "%s/.local/state/mpv", home);
68
2
    }
69
70
2
    char xdg_user_dirs[CONF_MAX];
71
2
    if (xdg_config && xdg_config[0]) {
72
0
        err = err || MKPATH(xdg_user_dirs, "%s/user-dirs.dirs", xdg_config);
73
2
    } else if (home && home[0]) {
74
2
        err = err || MKPATH(xdg_user_dirs, "%s/.config/user-dirs.dirs", home);
75
2
    }
76
77
    // Attempt to read user-dirs for XDG_DESKTOP_DIR
78
2
    mpv_desktop[0] = '\0';
79
2
    if (mp_path_exists(xdg_user_dirs)) {
80
0
        char line[4096];
81
0
        FILE *user_dirs = fopen(xdg_user_dirs, "r");
82
0
        while (fgets(line, sizeof(line), user_dirs)) {
83
0
            bstr data = bstr0(line);
84
0
            if (bstr_eatstart0(&data, "XDG_DESKTOP_DIR=")) {
85
0
                bstr value = bstr_strip_linebreaks(data);
86
0
                if (bstr_eatstart0(&value, "\"") && bstr_eatend0(&value, "\"")) {
87
0
                    bool home_prefix = bstr_eatstart0(&value, "$HOME/");
88
0
                    if (home_prefix) {
89
0
                        err = err || MKPATH(mpv_desktop, "%s/%.*s", home, BSTR_P(value));
90
0
                    } else {
91
0
                        err = err || MKPATH(mpv_desktop, "%.*s", BSTR_P(value));
92
0
                    }
93
0
                }
94
0
                break;
95
0
            }
96
0
        }
97
0
    }
98
99
2
    if (!mpv_desktop[0])
100
2
        err = err || MKPATH(mpv_desktop, "%s/%s", home, "Desktop");
101
102
    // If the old ~/.mpv exists, and the XDG config dir doesn't, use the old
103
    // config dir only. Also do not use any other XDG directories.
104
2
    if (mp_path_exists(old_home) && !mp_path_exists(mpv_home)) {
105
0
        err = err || MKPATH(mpv_home, "%s", old_home);
106
0
        err = err || MKPATH(mpv_cache, "%s", old_cache);
107
0
        err = err || MKPATH(mpv_state, "%s", old_home);
108
0
        old_home[0] = '\0';
109
0
        old_cache[0] = '\0';
110
0
    }
111
112
2
    if (err) {
113
0
        fprintf(stderr, "Config dir exceeds %d bytes\n", CONF_MAX);
114
0
        abort();
115
0
    }
116
2
}
117
118
const char *mp_get_platform_path_unix(void *talloc_ctx, const char *type)
119
37.1k
{
120
37.1k
    mp_exec_once(&path_init_once, path_init);
121
37.1k
    if (strcmp(type, "home") == 0)
122
7.95k
        return mpv_home;
123
29.1k
    if (strcmp(type, "old_home") == 0)
124
5.50k
        return old_home;
125
23.6k
    if (strcmp(type, "cache") == 0)
126
327
        return mpv_cache;
127
23.3k
    if (strcmp(type, "state") == 0)
128
7.65k
        return mpv_state;
129
15.7k
    if (strcmp(type, "global") == 0)
130
5.01k
        return MPV_CONFDIR;
131
10.6k
    if (strcmp(type, "desktop") == 0)
132
79
        return mpv_desktop;
133
10.6k
    return NULL;
134
10.6k
}