aa1b9764fa11e6a0f2db5306646fbc6b9c99f430
[uwplayer.git] / application / settings.c
1 /*
2  * Copyright 2022 Olaf Wintermann
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a 
5  * copy of this software and associated documentation files (the "Software"), 
6  * to deal in the Software without restriction, including without limitation 
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
8  * and/or sell copies of the Software, and to permit persons to whom the 
9  * Software is furnished to do so, subject to the following conditions:
10  * 
11  * The above copyright notice and this permission notice shall be included in 
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
17  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
20  * DEALINGS IN THE SOFTWARE.
21  */
22
23 #include "settings.h"
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <sys/stat.h>
32 #include <pthread.h>
33
34 #include "main.h"
35 #include "utils.h"
36 #include "json.h"
37
38 #include <ucx/map.h>
39 #include <ucx/properties.h>
40 #include <ucx/buffer.h>
41 #include <ucx/utils.h>
42
43 #define CONFIG_BASE_DIR ".config"
44 #define UWP_CONFIG_DIR  "uwplayer"
45 #define UWP_CONFIG_FILE "uwplayer.conf"
46
47 #define JS_READ_BUFSIZE 4096
48
49 static void* player_bin_search_thread(void *data);
50 static void conf_load_global_settings(void);
51
52 static char *uwp_config_dir;
53
54 /*
55  * root json config object
56  */
57 static JSONObject *uwp_config;
58
59 /*
60  * global settings from json config converted to key/value pairs
61  */
62 static UcxMap *uwp_settings; 
63
64 /*
65  * default settings
66  */
67 static UcxMap *uwp_default;
68
69 static int check_config_dir(void) {
70     char *home = getenv("HOME");
71     if(!home) {
72         return 1;
73     }
74     
75     char *cfg_dir = util_concat_path(home, CONFIG_BASE_DIR);
76     int ret = 0;
77     if(mkdir(cfg_dir, S_IRWXU)) {
78         if(errno != EEXIST) {
79             fprintf(stderr, "Error: Cannot access %s: %s\n", cfg_dir, strerror(errno));
80             ret = 1;
81         }
82     }
83     
84     if(!ret) {
85         uwp_config_dir = util_concat_path(cfg_dir, UWP_CONFIG_DIR);
86         if(mkdir(uwp_config_dir, S_IRWXU)) {
87             if(errno != EEXIST) {
88                 fprintf(stderr, "Error: Cannot access %s: %s\n", uwp_config_dir, strerror(errno));
89                 ret = 1;
90             }
91         }
92     }
93     
94     free(cfg_dir);
95     return ret;
96 }
97
98 int load_config(void) {
99     if(check_config_dir()) {
100         return 1;
101     }
102     
103     uwp_settings = ucx_map_new(16);
104     uwp_default = ucx_map_new(32);
105     
106     char *cfgfile_path = util_concat_path(uwp_config_dir, UWP_CONFIG_FILE);
107     FILE *cfgfile = fopen(cfgfile_path, "r");
108     free(cfgfile_path);
109     
110     int ret = 0;
111     if(cfgfile) {
112         JSONParser *parser = json_parser_new();
113         
114         JSONValue *value = NULL;
115         char buf[JS_READ_BUFSIZE];
116         size_t r;
117         
118         while((ret = json_read_value(parser, &value)) >= 0) {
119             if(ret == 0) {
120                 r = fread(buf, 1, JS_READ_BUFSIZE, cfgfile);
121                 if(r == 0) {
122                     break;
123                 }
124                 json_parser_fill(parser, buf, r);
125             } else {
126                 break;
127             }
128         }
129         
130         json_parser_free(parser);
131         
132         if(value) {
133             if(value->type == JSON_OBJECT) {
134                 ret = 0;
135                 uwp_config = &value->value.object;
136                 conf_load_global_settings();
137             } else {
138                 ret = 1;
139             }
140         } else {
141             ret = 1;
142         }
143         
144         
145         fclose(cfgfile);
146
147         if(ret) {
148             return ret;
149         }
150     }
151  
152     
153     // check if mpv or mplayer binaries are configured
154     char *player_bin = ucx_map_cstr_get(uwp_settings, UWP_PLAYER_BIN);
155     char *player_type = ucx_map_cstr_get(uwp_settings, UWP_PLAYER_TYPE);
156     
157     if(!player_bin) {
158         // try to find the mpv or mplayer binary path
159         pthread_t st;
160         pthread_create(&st, NULL, player_bin_search_thread, NULL);
161     } else if(!player_type) {
162         fprintf(stderr, "Warning: unknown player type (mplayer, mpv)\n");
163     }
164     
165     return 0;
166 }
167
168 static void conf_load_global_settings(void) {
169     JSONValue *settings = json_obj_get(uwp_config, "settings");
170     if(!settings) {
171         return;
172     }
173     
174     if(settings->type != JSON_OBJECT) {
175         fprintf(stderr, "Warning: 'settings' not an object\n");
176         return;
177     }
178     
179     JSONObject *s = &settings->value.object;
180     
181     for(size_t i=0;i<s->size;i++) {
182         JSONObjValue *gs = &s->values[i];
183         if(gs->value->type == JSON_STRING) {
184             ucx_map_cstr_put(uwp_settings, gs->name, gs->value->value.string.string);
185         }
186     }
187 }
188
189 static char* get_which_output(FILE *f, UcxBuffer *buf) {
190     buf->pos = 0;
191     buf->size = 0;
192     ucx_stream_copy(f, buf, (read_func)fread, (write_func)ucx_buffer_write);
193     if(!pclose(f)) {
194         ucx_buffer_putc(buf, 0);
195         size_t i;
196         for(i=0;i<buf->pos;i++) {
197             if(buf->space[i] == '\n') {
198                 buf->space[i] = 0;
199                 break;
200             }
201         }
202         return buf->space;
203     }
204     return NULL;
205 }
206
207 static Boolean finish_bin_search(XtPointer data) {
208     PlayerInfo *playerInfo = data;
209     ucx_map_cstr_put(uwp_settings, UWP_PLAYER_BIN, playerInfo->bin);
210     ucx_map_cstr_put(uwp_settings, UWP_PLAYER_TYPE, playerInfo->type);
211     free(playerInfo);
212     return 0;
213 }
214
215 static void* player_bin_search_thread(void *data) {
216     UcxBuffer *buf = ucx_buffer_new(NULL, 256, UCX_BUFFER_AUTOEXTEND);
217     
218     FILE *f = popen("which mpv", "r");
219     if(f) {
220         char *bin = get_which_output(f, buf);
221         if(bin) {
222             PlayerInfo *playerInfo = malloc(sizeof(PlayerInfo));
223             playerInfo->bin = strdup(bin);
224             playerInfo->type = strdup("mpv");
225             AppExecProc(finish_bin_search, playerInfo);
226             
227             ucx_buffer_free(buf);
228             return NULL;
229         }
230     }
231     
232     f = popen("which mplayer", "r");
233     if(f) {
234         char *bin = get_which_output(f, buf);
235         if(bin) {
236             PlayerInfo *playerInfo = malloc(sizeof(PlayerInfo));
237             playerInfo->bin = strdup(bin);
238             playerInfo->type = strdup("mplayer");
239             AppExecProc(finish_bin_search, playerInfo);
240         }
241     }
242     
243     ucx_buffer_free(buf);
244     return NULL;
245 }
246
247 char* SettingsGetPlayerBin(void) {
248     return ucx_map_cstr_get(uwp_settings, UWP_PLAYER_BIN);
249 }