Browse Source

First commit

Phyks (Lucas Verney) 4 years ago
commit
72324c5fb8
8 changed files with 1205 additions and 0 deletions
  1. 1
    0
      .gitignore
  2. 3
    0
      .gitmodules
  3. 17
    0
      CMakeLists.txt
  4. 1
    0
      bliss
  5. 191
    0
      include/cmdline.h
  6. 9
    0
      src/args.ggo
  7. 641
    0
      src/cmdline.c
  8. 342
    0
      src/main.c

+ 1
- 0
.gitignore View File

@@ -0,0 +1 @@
1
+build/

+ 3
- 0
.gitmodules View File

@@ -0,0 +1,3 @@
1
+[submodule "bliss"]
2
+	path = bliss
3
+	url = https://github.com/Polochon-street/bliss

+ 17
- 0
CMakeLists.txt View File

@@ -0,0 +1,17 @@
1
+cmake_minimum_required(VERSION 2.8)
2
+
3
+project(MPDBliss C)
4
+
5
+add_subdirectory(bliss)
6
+
7
+file (GLOB MPDBLISS_SRC "src/*.c")
8
+include_directories("include/" "bliss/include")
9
+
10
+add_executable (mpdbliss
11
+    ${MPDBLISS_SRC})
12
+
13
+target_link_libraries (mpdbliss
14
+    m
15
+    sqlite3
16
+    mpdclient
17
+    bliss)

+ 1
- 0
bliss

@@ -0,0 +1 @@
1
+Subproject commit 75db3a83356615bcc5c00c269a5a0356b20fb676

+ 191
- 0
include/cmdline.h View File

@@ -0,0 +1,191 @@
1
+/** @file cmdline.h
2
+ *  @brief The header file for the command line option parser
3
+ *  generated by GNU Gengetopt version 2.22.6
4
+ *  http://www.gnu.org/software/gengetopt.
5
+ *  DO NOT modify this file, since it can be overwritten
6
+ *  @author GNU Gengetopt by Lorenzo Bettini */
7
+
8
+#ifndef CMDLINE_H
9
+#define CMDLINE_H
10
+
11
+/* If we use autoconf.  */
12
+#ifdef HAVE_CONFIG_H
13
+#include "config.h"
14
+#endif
15
+
16
+#include <stdio.h> /* for FILE */
17
+
18
+#ifdef __cplusplus
19
+extern "C" {
20
+#endif /* __cplusplus */
21
+
22
+#ifndef CMDLINE_PARSER_PACKAGE
23
+/** @brief the program name (used for printing errors) */
24
+#define CMDLINE_PARSER_PACKAGE "MPDBliss"
25
+#endif
26
+
27
+#ifndef CMDLINE_PARSER_PACKAGE_NAME
28
+/** @brief the complete program name (used for help and version) */
29
+#define CMDLINE_PARSER_PACKAGE_NAME "MPDBliss"
30
+#endif
31
+
32
+#ifndef CMDLINE_PARSER_VERSION
33
+/** @brief the program version */
34
+#define CMDLINE_PARSER_VERSION "VERSION"
35
+#endif
36
+
37
+/** @brief Where the command line options are stored */
38
+struct gengetopt_args_info
39
+{
40
+  const char *help_help; /**< @brief Print help and exit help description.  */
41
+  const char *version_help; /**< @brief Print version and exit help description.  */
42
+  int rescan_flag;	/**< @brief Rescan the whole MPD database. (default=off).  */
43
+  const char *rescan_help; /**< @brief Rescan the whole MPD database. help description.  */
44
+  int update_flag;	/**< @brief Trigger an update. (default=off).  */
45
+  const char *update_help; /**< @brief Trigger an update. help description.  */
46
+  char * mpd_root_arg;	/**< @brief MPD library base path..  */
47
+  char * mpd_root_orig;	/**< @brief MPD library base path. original value given at command line.  */
48
+  const char *mpd_root_help; /**< @brief MPD library base path. help description.  */
49
+  char * host_arg;	/**< @brief MPD host. (default='localhost').  */
50
+  char * host_orig;	/**< @brief MPD host. original value given at command line.  */
51
+  const char *host_help; /**< @brief MPD host. help description.  */
52
+  int port_arg;	/**< @brief MPD port. (default='6600').  */
53
+  char * port_orig;	/**< @brief MPD port. original value given at command line.  */
54
+  const char *port_help; /**< @brief MPD port. help description.  */
55
+  
56
+  unsigned int help_given ;	/**< @brief Whether help was given.  */
57
+  unsigned int version_given ;	/**< @brief Whether version was given.  */
58
+  unsigned int rescan_given ;	/**< @brief Whether rescan was given.  */
59
+  unsigned int update_given ;	/**< @brief Whether update was given.  */
60
+  unsigned int mpd_root_given ;	/**< @brief Whether mpd_root was given.  */
61
+  unsigned int host_given ;	/**< @brief Whether host was given.  */
62
+  unsigned int port_given ;	/**< @brief Whether port was given.  */
63
+
64
+} ;
65
+
66
+/** @brief The additional parameters to pass to parser functions */
67
+struct cmdline_parser_params
68
+{
69
+  int override; /**< @brief whether to override possibly already present options (default 0) */
70
+  int initialize; /**< @brief whether to initialize the option structure gengetopt_args_info (default 1) */
71
+  int check_required; /**< @brief whether to check that all required options were provided (default 1) */
72
+  int check_ambiguity; /**< @brief whether to check for options already specified in the option structure gengetopt_args_info (default 0) */
73
+  int print_errors; /**< @brief whether getopt_long should print an error message for a bad option (default 1) */
74
+} ;
75
+
76
+/** @brief the purpose string of the program */
77
+extern const char *gengetopt_args_info_purpose;
78
+/** @brief the usage string of the program */
79
+extern const char *gengetopt_args_info_usage;
80
+/** @brief the description string of the program */
81
+extern const char *gengetopt_args_info_description;
82
+/** @brief all the lines making the help output */
83
+extern const char *gengetopt_args_info_help[];
84
+
85
+/**
86
+ * The command line parser
87
+ * @param argc the number of command line options
88
+ * @param argv the command line options
89
+ * @param args_info the structure where option information will be stored
90
+ * @return 0 if everything went fine, NON 0 if an error took place
91
+ */
92
+int cmdline_parser (int argc, char **argv,
93
+  struct gengetopt_args_info *args_info);
94
+
95
+/**
96
+ * The command line parser (version with additional parameters - deprecated)
97
+ * @param argc the number of command line options
98
+ * @param argv the command line options
99
+ * @param args_info the structure where option information will be stored
100
+ * @param override whether to override possibly already present options
101
+ * @param initialize whether to initialize the option structure my_args_info
102
+ * @param check_required whether to check that all required options were provided
103
+ * @return 0 if everything went fine, NON 0 if an error took place
104
+ * @deprecated use cmdline_parser_ext() instead
105
+ */
106
+int cmdline_parser2 (int argc, char **argv,
107
+  struct gengetopt_args_info *args_info,
108
+  int override, int initialize, int check_required);
109
+
110
+/**
111
+ * The command line parser (version with additional parameters)
112
+ * @param argc the number of command line options
113
+ * @param argv the command line options
114
+ * @param args_info the structure where option information will be stored
115
+ * @param params additional parameters for the parser
116
+ * @return 0 if everything went fine, NON 0 if an error took place
117
+ */
118
+int cmdline_parser_ext (int argc, char **argv,
119
+  struct gengetopt_args_info *args_info,
120
+  struct cmdline_parser_params *params);
121
+
122
+/**
123
+ * Save the contents of the option struct into an already open FILE stream.
124
+ * @param outfile the stream where to dump options
125
+ * @param args_info the option struct to dump
126
+ * @return 0 if everything went fine, NON 0 if an error took place
127
+ */
128
+int cmdline_parser_dump(FILE *outfile,
129
+  struct gengetopt_args_info *args_info);
130
+
131
+/**
132
+ * Save the contents of the option struct into a (text) file.
133
+ * This file can be read by the config file parser (if generated by gengetopt)
134
+ * @param filename the file where to save
135
+ * @param args_info the option struct to save
136
+ * @return 0 if everything went fine, NON 0 if an error took place
137
+ */
138
+int cmdline_parser_file_save(const char *filename,
139
+  struct gengetopt_args_info *args_info);
140
+
141
+/**
142
+ * Print the help
143
+ */
144
+void cmdline_parser_print_help(void);
145
+/**
146
+ * Print the version
147
+ */
148
+void cmdline_parser_print_version(void);
149
+
150
+/**
151
+ * Initializes all the fields a cmdline_parser_params structure 
152
+ * to their default values
153
+ * @param params the structure to initialize
154
+ */
155
+void cmdline_parser_params_init(struct cmdline_parser_params *params);
156
+
157
+/**
158
+ * Allocates dynamically a cmdline_parser_params structure and initializes
159
+ * all its fields to their default values
160
+ * @return the created and initialized cmdline_parser_params structure
161
+ */
162
+struct cmdline_parser_params *cmdline_parser_params_create(void);
163
+
164
+/**
165
+ * Initializes the passed gengetopt_args_info structure's fields
166
+ * (also set default values for options that have a default)
167
+ * @param args_info the structure to initialize
168
+ */
169
+void cmdline_parser_init (struct gengetopt_args_info *args_info);
170
+/**
171
+ * Deallocates the string fields of the gengetopt_args_info structure
172
+ * (but does not deallocate the structure itself)
173
+ * @param args_info the structure to deallocate
174
+ */
175
+void cmdline_parser_free (struct gengetopt_args_info *args_info);
176
+
177
+/**
178
+ * Checks that all the required options were specified
179
+ * @param args_info the structure to check
180
+ * @param prog_name the name of the program that will be used to print
181
+ *   possible errors
182
+ * @return
183
+ */
184
+int cmdline_parser_required (struct gengetopt_args_info *args_info,
185
+  const char *prog_name);
186
+
187
+
188
+#ifdef __cplusplus
189
+}
190
+#endif /* __cplusplus */
191
+#endif /* CMDLINE_H */

+ 9
- 0
src/args.ggo View File

@@ -0,0 +1,9 @@
1
+package "MPDBliss"
2
+version "VERSION"
3
+purpose "Binding MPD and Bliss."
4
+
5
+option "rescan" r "Rescan the whole MPD database." flag off
6
+option "update" u "Trigger an update." flag off
7
+option "mpd_root" - "MPD library base path." string
8
+option "host" - "MPD host." string default="localhost" optional
9
+option "port" - "MPD port." int default="6600" optional

+ 641
- 0
src/cmdline.c View File

@@ -0,0 +1,641 @@
1
+/*
2
+  File autogenerated by gengetopt version 2.22.6
3
+  generated with the following command:
4
+  gengetopt 
5
+
6
+  The developers of gengetopt consider the fixed text that goes in all
7
+  gengetopt output files to be in the public domain:
8
+  we make no copyright claims on it.
9
+*/
10
+
11
+/* If we use autoconf.  */
12
+#ifdef HAVE_CONFIG_H
13
+#include "config.h"
14
+#endif
15
+
16
+#include <stdio.h>
17
+#include <stdlib.h>
18
+#include <string.h>
19
+
20
+#ifndef FIX_UNUSED
21
+#define FIX_UNUSED(X) (void) (X) /* avoid warnings for unused params */
22
+#endif
23
+
24
+#include <getopt.h>
25
+
26
+#include "cmdline.h"
27
+
28
+const char *gengetopt_args_info_purpose = "Binding MPD and Bliss.";
29
+
30
+const char *gengetopt_args_info_usage = "Usage: MPDBliss [OPTIONS]...";
31
+
32
+const char *gengetopt_args_info_versiontext = "";
33
+
34
+const char *gengetopt_args_info_description = "";
35
+
36
+const char *gengetopt_args_info_help[] = {
37
+  "  -h, --help             Print help and exit",
38
+  "  -V, --version          Print version and exit",
39
+  "  -r, --rescan           Rescan the whole MPD database.  (default=off)",
40
+  "  -u, --update           Trigger an update.  (default=off)",
41
+  "      --mpd_root=STRING  MPD library base path.",
42
+  "      --host=STRING      MPD host.  (default=`localhost')",
43
+  "      --port=INT         MPD port.  (default=`6600')",
44
+    0
45
+};
46
+
47
+typedef enum {ARG_NO
48
+  , ARG_FLAG
49
+  , ARG_STRING
50
+  , ARG_INT
51
+} cmdline_parser_arg_type;
52
+
53
+static
54
+void clear_given (struct gengetopt_args_info *args_info);
55
+static
56
+void clear_args (struct gengetopt_args_info *args_info);
57
+
58
+static int
59
+cmdline_parser_internal (int argc, char **argv, struct gengetopt_args_info *args_info,
60
+                        struct cmdline_parser_params *params, const char *additional_error);
61
+
62
+static int
63
+cmdline_parser_required2 (struct gengetopt_args_info *args_info, const char *prog_name, const char *additional_error);
64
+
65
+static char *
66
+gengetopt_strdup (const char *s);
67
+
68
+static
69
+void clear_given (struct gengetopt_args_info *args_info)
70
+{
71
+  args_info->help_given = 0 ;
72
+  args_info->version_given = 0 ;
73
+  args_info->rescan_given = 0 ;
74
+  args_info->update_given = 0 ;
75
+  args_info->mpd_root_given = 0 ;
76
+  args_info->host_given = 0 ;
77
+  args_info->port_given = 0 ;
78
+}
79
+
80
+static
81
+void clear_args (struct gengetopt_args_info *args_info)
82
+{
83
+  FIX_UNUSED (args_info);
84
+  args_info->rescan_flag = 0;
85
+  args_info->update_flag = 0;
86
+  args_info->mpd_root_arg = NULL;
87
+  args_info->mpd_root_orig = NULL;
88
+  args_info->host_arg = gengetopt_strdup ("localhost");
89
+  args_info->host_orig = NULL;
90
+  args_info->port_arg = 6600;
91
+  args_info->port_orig = NULL;
92
+  
93
+}
94
+
95
+static
96
+void init_args_info(struct gengetopt_args_info *args_info)
97
+{
98
+
99
+
100
+  args_info->help_help = gengetopt_args_info_help[0] ;
101
+  args_info->version_help = gengetopt_args_info_help[1] ;
102
+  args_info->rescan_help = gengetopt_args_info_help[2] ;
103
+  args_info->update_help = gengetopt_args_info_help[3] ;
104
+  args_info->mpd_root_help = gengetopt_args_info_help[4] ;
105
+  args_info->host_help = gengetopt_args_info_help[5] ;
106
+  args_info->port_help = gengetopt_args_info_help[6] ;
107
+  
108
+}
109
+
110
+void
111
+cmdline_parser_print_version (void)
112
+{
113
+  printf ("%s %s\n",
114
+     (strlen(CMDLINE_PARSER_PACKAGE_NAME) ? CMDLINE_PARSER_PACKAGE_NAME : CMDLINE_PARSER_PACKAGE),
115
+     CMDLINE_PARSER_VERSION);
116
+
117
+  if (strlen(gengetopt_args_info_versiontext) > 0)
118
+    printf("\n%s\n", gengetopt_args_info_versiontext);
119
+}
120
+
121
+static void print_help_common(void) {
122
+  cmdline_parser_print_version ();
123
+
124
+  if (strlen(gengetopt_args_info_purpose) > 0)
125
+    printf("\n%s\n", gengetopt_args_info_purpose);
126
+
127
+  if (strlen(gengetopt_args_info_usage) > 0)
128
+    printf("\n%s\n", gengetopt_args_info_usage);
129
+
130
+  printf("\n");
131
+
132
+  if (strlen(gengetopt_args_info_description) > 0)
133
+    printf("%s\n\n", gengetopt_args_info_description);
134
+}
135
+
136
+void
137
+cmdline_parser_print_help (void)
138
+{
139
+  int i = 0;
140
+  print_help_common();
141
+  while (gengetopt_args_info_help[i])
142
+    printf("%s\n", gengetopt_args_info_help[i++]);
143
+}
144
+
145
+void
146
+cmdline_parser_init (struct gengetopt_args_info *args_info)
147
+{
148
+  clear_given (args_info);
149
+  clear_args (args_info);
150
+  init_args_info (args_info);
151
+}
152
+
153
+void
154
+cmdline_parser_params_init(struct cmdline_parser_params *params)
155
+{
156
+  if (params)
157
+    { 
158
+      params->override = 0;
159
+      params->initialize = 1;
160
+      params->check_required = 1;
161
+      params->check_ambiguity = 0;
162
+      params->print_errors = 1;
163
+    }
164
+}
165
+
166
+struct cmdline_parser_params *
167
+cmdline_parser_params_create(void)
168
+{
169
+  struct cmdline_parser_params *params = 
170
+    (struct cmdline_parser_params *)malloc(sizeof(struct cmdline_parser_params));
171
+  cmdline_parser_params_init(params);  
172
+  return params;
173
+}
174
+
175
+static void
176
+free_string_field (char **s)
177
+{
178
+  if (*s)
179
+    {
180
+      free (*s);
181
+      *s = 0;
182
+    }
183
+}
184
+
185
+
186
+static void
187
+cmdline_parser_release (struct gengetopt_args_info *args_info)
188
+{
189
+
190
+  free_string_field (&(args_info->mpd_root_arg));
191
+  free_string_field (&(args_info->mpd_root_orig));
192
+  free_string_field (&(args_info->host_arg));
193
+  free_string_field (&(args_info->host_orig));
194
+  free_string_field (&(args_info->port_orig));
195
+  
196
+  
197
+
198
+  clear_given (args_info);
199
+}
200
+
201
+
202
+static void
203
+write_into_file(FILE *outfile, const char *opt, const char *arg, const char *values[])
204
+{
205
+  FIX_UNUSED (values);
206
+  if (arg) {
207
+    fprintf(outfile, "%s=\"%s\"\n", opt, arg);
208
+  } else {
209
+    fprintf(outfile, "%s\n", opt);
210
+  }
211
+}
212
+
213
+
214
+int
215
+cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info)
216
+{
217
+  int i = 0;
218
+
219
+  if (!outfile)
220
+    {
221
+      fprintf (stderr, "%s: cannot dump options to stream\n", CMDLINE_PARSER_PACKAGE);
222
+      return EXIT_FAILURE;
223
+    }
224
+
225
+  if (args_info->help_given)
226
+    write_into_file(outfile, "help", 0, 0 );
227
+  if (args_info->version_given)
228
+    write_into_file(outfile, "version", 0, 0 );
229
+  if (args_info->rescan_given)
230
+    write_into_file(outfile, "rescan", 0, 0 );
231
+  if (args_info->update_given)
232
+    write_into_file(outfile, "update", 0, 0 );
233
+  if (args_info->mpd_root_given)
234
+    write_into_file(outfile, "mpd_root", args_info->mpd_root_orig, 0);
235
+  if (args_info->host_given)
236
+    write_into_file(outfile, "host", args_info->host_orig, 0);
237
+  if (args_info->port_given)
238
+    write_into_file(outfile, "port", args_info->port_orig, 0);
239
+  
240
+
241
+  i = EXIT_SUCCESS;
242
+  return i;
243
+}
244
+
245
+int
246
+cmdline_parser_file_save(const char *filename, struct gengetopt_args_info *args_info)
247
+{
248
+  FILE *outfile;
249
+  int i = 0;
250
+
251
+  outfile = fopen(filename, "w");
252
+
253
+  if (!outfile)
254
+    {
255
+      fprintf (stderr, "%s: cannot open file for writing: %s\n", CMDLINE_PARSER_PACKAGE, filename);
256
+      return EXIT_FAILURE;
257
+    }
258
+
259
+  i = cmdline_parser_dump(outfile, args_info);
260
+  fclose (outfile);
261
+
262
+  return i;
263
+}
264
+
265
+void
266
+cmdline_parser_free (struct gengetopt_args_info *args_info)
267
+{
268
+  cmdline_parser_release (args_info);
269
+}
270
+
271
+/** @brief replacement of strdup, which is not standard */
272
+char *
273
+gengetopt_strdup (const char *s)
274
+{
275
+  char *result = 0;
276
+  if (!s)
277
+    return result;
278
+
279
+  result = (char*)malloc(strlen(s) + 1);
280
+  if (result == (char*)0)
281
+    return (char*)0;
282
+  strcpy(result, s);
283
+  return result;
284
+}
285
+
286
+int
287
+cmdline_parser (int argc, char **argv, struct gengetopt_args_info *args_info)
288
+{
289
+  return cmdline_parser2 (argc, argv, args_info, 0, 1, 1);
290
+}
291
+
292
+int
293
+cmdline_parser_ext (int argc, char **argv, struct gengetopt_args_info *args_info,
294
+                   struct cmdline_parser_params *params)
295
+{
296
+  int result;
297
+  result = cmdline_parser_internal (argc, argv, args_info, params, 0);
298
+
299
+  if (result == EXIT_FAILURE)
300
+    {
301
+      cmdline_parser_free (args_info);
302
+      exit (EXIT_FAILURE);
303
+    }
304
+  
305
+  return result;
306
+}
307
+
308
+int
309
+cmdline_parser2 (int argc, char **argv, struct gengetopt_args_info *args_info, int override, int initialize, int check_required)
310
+{
311
+  int result;
312
+  struct cmdline_parser_params params;
313
+  
314
+  params.override = override;
315
+  params.initialize = initialize;
316
+  params.check_required = check_required;
317
+  params.check_ambiguity = 0;
318
+  params.print_errors = 1;
319
+
320
+  result = cmdline_parser_internal (argc, argv, args_info, &params, 0);
321
+
322
+  if (result == EXIT_FAILURE)
323
+    {
324
+      cmdline_parser_free (args_info);
325
+      exit (EXIT_FAILURE);
326
+    }
327
+  
328
+  return result;
329
+}
330
+
331
+int
332
+cmdline_parser_required (struct gengetopt_args_info *args_info, const char *prog_name)
333
+{
334
+  int result = EXIT_SUCCESS;
335
+
336
+  if (cmdline_parser_required2(args_info, prog_name, 0) > 0)
337
+    result = EXIT_FAILURE;
338
+
339
+  if (result == EXIT_FAILURE)
340
+    {
341
+      cmdline_parser_free (args_info);
342
+      exit (EXIT_FAILURE);
343
+    }
344
+  
345
+  return result;
346
+}
347
+
348
+int
349
+cmdline_parser_required2 (struct gengetopt_args_info *args_info, const char *prog_name, const char *additional_error)
350
+{
351
+  int error_occurred = 0;
352
+  FIX_UNUSED (additional_error);
353
+
354
+  /* checks for required options */
355
+  if (! args_info->mpd_root_given)
356
+    {
357
+      fprintf (stderr, "%s: '--mpd_root' option required%s\n", prog_name, (additional_error ? additional_error : ""));
358
+      error_occurred = 1;
359
+    }
360
+  
361
+  
362
+  /* checks for dependences among options */
363
+
364
+  return error_occurred;
365
+}
366
+
367
+
368
+static char *package_name = 0;
369
+
370
+/**
371
+ * @brief updates an option
372
+ * @param field the generic pointer to the field to update
373
+ * @param orig_field the pointer to the orig field
374
+ * @param field_given the pointer to the number of occurrence of this option
375
+ * @param prev_given the pointer to the number of occurrence already seen
376
+ * @param value the argument for this option (if null no arg was specified)
377
+ * @param possible_values the possible values for this option (if specified)
378
+ * @param default_value the default value (in case the option only accepts fixed values)
379
+ * @param arg_type the type of this option
380
+ * @param check_ambiguity @see cmdline_parser_params.check_ambiguity
381
+ * @param override @see cmdline_parser_params.override
382
+ * @param no_free whether to free a possible previous value
383
+ * @param multiple_option whether this is a multiple option
384
+ * @param long_opt the corresponding long option
385
+ * @param short_opt the corresponding short option (or '-' if none)
386
+ * @param additional_error possible further error specification
387
+ */
388
+static
389
+int update_arg(void *field, char **orig_field,
390
+               unsigned int *field_given, unsigned int *prev_given, 
391
+               char *value, const char *possible_values[],
392
+               const char *default_value,
393
+               cmdline_parser_arg_type arg_type,
394
+               int check_ambiguity, int override,
395
+               int no_free, int multiple_option,
396
+               const char *long_opt, char short_opt,
397
+               const char *additional_error)
398
+{
399
+  char *stop_char = 0;
400
+  const char *val = value;
401
+  int found;
402
+  char **string_field;
403
+  FIX_UNUSED (field);
404
+
405
+  stop_char = 0;
406
+  found = 0;
407
+
408
+  if (!multiple_option && prev_given && (*prev_given || (check_ambiguity && *field_given)))
409
+    {
410
+      if (short_opt != '-')
411
+        fprintf (stderr, "%s: `--%s' (`-%c') option given more than once%s\n", 
412
+               package_name, long_opt, short_opt,
413
+               (additional_error ? additional_error : ""));
414
+      else
415
+        fprintf (stderr, "%s: `--%s' option given more than once%s\n", 
416
+               package_name, long_opt,
417
+               (additional_error ? additional_error : ""));
418
+      return 1; /* failure */
419
+    }
420
+
421
+  FIX_UNUSED (default_value);
422
+    
423
+  if (field_given && *field_given && ! override)
424
+    return 0;
425
+  if (prev_given)
426
+    (*prev_given)++;
427
+  if (field_given)
428
+    (*field_given)++;
429
+  if (possible_values)
430
+    val = possible_values[found];
431
+
432
+  switch(arg_type) {
433
+  case ARG_FLAG:
434
+    *((int *)field) = !*((int *)field);
435
+    break;
436
+  case ARG_INT:
437
+    if (val) *((int *)field) = strtol (val, &stop_char, 0);
438
+    break;
439
+  case ARG_STRING:
440
+    if (val) {
441
+      string_field = (char **)field;
442
+      if (!no_free && *string_field)
443
+        free (*string_field); /* free previous string */
444
+      *string_field = gengetopt_strdup (val);
445
+    }
446
+    break;
447
+  default:
448
+    break;
449
+  };
450
+
451
+  /* check numeric conversion */
452
+  switch(arg_type) {
453
+  case ARG_INT:
454
+    if (val && !(stop_char && *stop_char == '\0')) {
455
+      fprintf(stderr, "%s: invalid numeric value: %s\n", package_name, val);
456
+      return 1; /* failure */
457
+    }
458
+    break;
459
+  default:
460
+    ;
461
+  };
462
+
463
+  /* store the original value */
464
+  switch(arg_type) {
465
+  case ARG_NO:
466
+  case ARG_FLAG:
467
+    break;
468
+  default:
469
+    if (value && orig_field) {
470
+      if (no_free) {
471
+        *orig_field = value;
472
+      } else {
473
+        if (*orig_field)
474
+          free (*orig_field); /* free previous string */
475
+        *orig_field = gengetopt_strdup (value);
476
+      }
477
+    }
478
+  };
479
+
480
+  return 0; /* OK */
481
+}
482
+
483
+
484
+int
485
+cmdline_parser_internal (
486
+  int argc, char **argv, struct gengetopt_args_info *args_info,
487
+                        struct cmdline_parser_params *params, const char *additional_error)
488
+{
489
+  int c;	/* Character of the parsed option.  */
490
+
491
+  int error_occurred = 0;
492
+  struct gengetopt_args_info local_args_info;
493
+  
494
+  int override;
495
+  int initialize;
496
+  int check_required;
497
+  int check_ambiguity;
498
+  
499
+  package_name = argv[0];
500
+  
501
+  override = params->override;
502
+  initialize = params->initialize;
503
+  check_required = params->check_required;
504
+  check_ambiguity = params->check_ambiguity;
505
+
506
+  if (initialize)
507
+    cmdline_parser_init (args_info);
508
+
509
+  cmdline_parser_init (&local_args_info);
510
+
511
+  optarg = 0;
512
+  optind = 0;
513
+  opterr = params->print_errors;
514
+  optopt = '?';
515
+
516
+  while (1)
517
+    {
518
+      int option_index = 0;
519
+
520
+      static struct option long_options[] = {
521
+        { "help",	0, NULL, 'h' },
522
+        { "version",	0, NULL, 'V' },
523
+        { "rescan",	0, NULL, 'r' },
524
+        { "update",	0, NULL, 'u' },
525
+        { "mpd_root",	1, NULL, 0 },
526
+        { "host",	1, NULL, 0 },
527
+        { "port",	1, NULL, 0 },
528
+        { 0,  0, 0, 0 }
529
+      };
530
+
531
+      c = getopt_long (argc, argv, "hVru", long_options, &option_index);
532
+
533
+      if (c == -1) break;	/* Exit from `while (1)' loop.  */
534
+
535
+      switch (c)
536
+        {
537
+        case 'h':	/* Print help and exit.  */
538
+          cmdline_parser_print_help ();
539
+          cmdline_parser_free (&local_args_info);
540
+          exit (EXIT_SUCCESS);
541
+
542
+        case 'V':	/* Print version and exit.  */
543
+          cmdline_parser_print_version ();
544
+          cmdline_parser_free (&local_args_info);
545
+          exit (EXIT_SUCCESS);
546
+
547
+        case 'r':	/* Rescan the whole MPD database..  */
548
+        
549
+        
550
+          if (update_arg((void *)&(args_info->rescan_flag), 0, &(args_info->rescan_given),
551
+              &(local_args_info.rescan_given), optarg, 0, 0, ARG_FLAG,
552
+              check_ambiguity, override, 1, 0, "rescan", 'r',
553
+              additional_error))
554
+            goto failure;
555
+        
556
+          break;
557
+        case 'u':	/* Trigger an update..  */
558
+        
559
+        
560
+          if (update_arg((void *)&(args_info->update_flag), 0, &(args_info->update_given),
561
+              &(local_args_info.update_given), optarg, 0, 0, ARG_FLAG,
562
+              check_ambiguity, override, 1, 0, "update", 'u',
563
+              additional_error))
564
+            goto failure;
565
+        
566
+          break;
567
+
568
+        case 0:	/* Long option with no short option */
569
+          /* MPD library base path..  */
570
+          if (strcmp (long_options[option_index].name, "mpd_root") == 0)
571
+          {
572
+          
573
+          
574
+            if (update_arg( (void *)&(args_info->mpd_root_arg), 
575
+                 &(args_info->mpd_root_orig), &(args_info->mpd_root_given),
576
+                &(local_args_info.mpd_root_given), optarg, 0, 0, ARG_STRING,
577
+                check_ambiguity, override, 0, 0,
578
+                "mpd_root", '-',
579
+                additional_error))
580
+              goto failure;
581
+          
582
+          }
583
+          /* MPD host..  */
584
+          else if (strcmp (long_options[option_index].name, "host") == 0)
585
+          {
586
+          
587
+          
588
+            if (update_arg( (void *)&(args_info->host_arg), 
589
+                 &(args_info->host_orig), &(args_info->host_given),
590
+                &(local_args_info.host_given), optarg, 0, "localhost", ARG_STRING,
591
+                check_ambiguity, override, 0, 0,
592
+                "host", '-',
593
+                additional_error))
594
+              goto failure;
595
+          
596
+          }
597
+          /* MPD port..  */
598
+          else if (strcmp (long_options[option_index].name, "port") == 0)
599
+          {
600
+          
601
+          
602
+            if (update_arg( (void *)&(args_info->port_arg), 
603
+                 &(args_info->port_orig), &(args_info->port_given),
604
+                &(local_args_info.port_given), optarg, 0, "6600", ARG_INT,
605
+                check_ambiguity, override, 0, 0,
606
+                "port", '-',
607
+                additional_error))
608
+              goto failure;
609
+          
610
+          }
611
+          
612
+          break;
613
+        case '?':	/* Invalid option.  */
614
+          /* `getopt_long' already printed an error message.  */
615
+          goto failure;
616
+
617
+        default:	/* bug: option not considered.  */
618
+          fprintf (stderr, "%s: option unknown: %c%s\n", CMDLINE_PARSER_PACKAGE, c, (additional_error ? additional_error : ""));
619
+          abort ();
620
+        } /* switch */
621
+    } /* while */
622
+
623
+
624
+
625
+  if (check_required)
626
+    {
627
+      error_occurred += cmdline_parser_required2 (args_info, argv[0], additional_error);
628
+    }
629
+
630
+  cmdline_parser_release (&local_args_info);
631
+
632
+  if ( error_occurred )
633
+    return (EXIT_FAILURE);
634
+
635
+  return 0;
636
+
637
+failure:
638
+  
639
+  cmdline_parser_release (&local_args_info);
640
+  return (EXIT_FAILURE);
641
+}

+ 342
- 0
src/main.c View File

@@ -0,0 +1,342 @@
1
+#include <math.h>
2
+#include <signal.h>
3
+#include <stdio.h>
4
+#include <stdlib.h>
5
+#include <string.h>
6
+#include <sys/stat.h>
7
+#include <time.h>
8
+
9
+#include <mpd/client.h>
10
+#include <sqlite3.h>
11
+
12
+#include "bliss.h"
13
+#include "cmdline.h"
14
+
15
+// TODO: Handle deletions from db
16
+
17
+#define DEFAULT_STRING_LENGTH 255
18
+
19
+// Data file path to store latest seen mtimes and db
20
+char mpdbliss_data_file[DEFAULT_STRING_LENGTH] = "";
21
+char mpdbliss_data_db[DEFAULT_STRING_LENGTH] = "";
22
+// IDLE loop control variable
23
+volatile bool mpd_run_idle_loop = true;
24
+// MPD connection handler
25
+struct mpd_connection *conn;
26
+
27
+
28
+/**
29
+ * Handle interruption when waiting for MPD IDLE items.
30
+ */
31
+void sigint_catch_function(int signo)
32
+{
33
+    // TODO: Not working
34
+    // TODO: Should store latest seen mtime there
35
+    printf("Exiting...\n");
36
+
37
+    // Stop listening for MPD IDLE
38
+    mpd_run_noidle(conn);
39
+
40
+    // Stop main loop
41
+    mpd_run_idle_loop = false;
42
+}
43
+
44
+
45
+/**
46
+ * Strip the trailing slash from a string.
47
+ *
48
+ * @param[in] str   String to strip slash from.
49
+ * @param[out] str   Stripped string.
50
+ */
51
+void strip_trailing_slash(char* str)
52
+{
53
+    size_t length = strlen(str);
54
+    if ('/' == str[length - 1]) {
55
+        str[length - 1] = '\0';
56
+    }
57
+}
58
+
59
+
60
+/**
61
+ * Update the database.
62
+ *
63
+ * @param mpd_connection    MPD connection object to use.
64
+ * @param initial_mtime     Initial mtime to use.
65
+ * @param mpd_base_path     Root directory of the MPD library.
66
+ */
67
+void update_database(
68
+        struct mpd_connection *conn,
69
+        time_t initial_mtime,
70
+        char *mpd_base_path
71
+    )
72
+{
73
+    // Store latest mtime seen
74
+    time_t latest_mtime = initial_mtime;
75
+
76
+    // Get the list of all the files to process
77
+    if (!mpd_send_list_all_meta(conn, NULL)) {
78
+        fprintf(stderr, "Unable to get a full list of items in the db.\n");
79
+        return;
80
+    }
81
+
82
+    // Connect to SQLite db
83
+    sqlite3 *dbh;
84
+    if (0 != sqlite3_open(mpdbliss_data_db, &dbh)) {
85
+        fprintf(stderr, "Unable to open SQLite db.\n");
86
+        return;
87
+    }
88
+
89
+    // Process the received list
90
+    struct mpd_entity *entity;
91
+    while ((entity = mpd_recv_entity(conn)) != NULL) {
92
+        const struct mpd_song *song;
93
+
94
+        switch (mpd_entity_get_type(entity)) {
95
+            case MPD_ENTITY_TYPE_SONG:
96
+                song = mpd_entity_get_song(entity);
97
+                break;
98
+
99
+            case MPD_ENTITY_TYPE_UNKNOWN:
100
+            case MPD_ENTITY_TYPE_DIRECTORY:
101
+            case MPD_ENTITY_TYPE_PLAYLIST:
102
+                // Pass such types
103
+                continue;
104
+        }
105
+
106
+        // Pass song if already seen
107
+        time_t song_mtime = mpd_song_get_last_modified(song);
108
+        if (difftime(song_mtime, initial_mtime) <= 0) {
109
+            continue;
110
+        }
111
+
112
+        // Compute bl_analyze and store it
113
+        const char *song_uri = mpd_song_get_uri(song);
114
+        printf("\nAdding new song to db: %s\n", song_uri);
115
+        struct bl_song song_analysis;
116
+        char song_full_uri[DEFAULT_STRING_LENGTH] = "";
117
+        strncat(song_full_uri, mpd_base_path, DEFAULT_STRING_LENGTH);
118
+        strncat(song_full_uri, song_uri, DEFAULT_STRING_LENGTH);
119
+        if (BL_UNEXPECTED == bl_analyze(song_full_uri, &song_analysis)) {
120
+            fprintf(stderr, "Error while parsing song: %s.\n\n", song_uri);
121
+            continue;
122
+        }
123
+        // Insert into db
124
+        sqlite3_stmt *res;
125
+        // Begin transaction
126
+        int dberr = sqlite3_exec(dbh, "BEGIN TRANSACTION", NULL, NULL, NULL);
127
+        if (SQLITE_OK != dberr) {
128
+            fprintf(stderr, "Error while inserting data in db: %s\n\n", sqlite3_errmsg(dbh));
129
+            sqlite3_exec(dbh, "ROLLBACK", NULL, NULL, NULL);
130
+            continue;
131
+        }
132
+        // Insert song analysis in database
133
+        dberr = sqlite3_prepare_v2(dbh,
134
+                "INSERT INTO songs(tempo, amplitude, frequency, attack, filename) VALUES(?, ?, ?, ?, ?)",
135
+                -1, &res, 0);
136
+        if (SQLITE_OK != dberr) {
137
+            fprintf(stderr, "Error while inserting data in db: %s\n\n", sqlite3_errmsg(dbh));
138
+            sqlite3_exec(dbh, "ROLLBACK", NULL, NULL, NULL);
139
+            continue;
140
+        }
141
+        sqlite3_bind_double(res, 1, song_analysis.force_vector.tempo);
142
+        sqlite3_bind_double(res, 2, song_analysis.force_vector.amplitude);
143
+        sqlite3_bind_double(res, 3, song_analysis.force_vector.frequency);
144
+        sqlite3_bind_double(res, 4, song_analysis.force_vector.attack);
145
+        sqlite3_bind_text(res, 5, song_uri, strlen(song_uri), SQLITE_STATIC);
146
+        sqlite3_step(res);
147
+        sqlite3_finalize(res);
148
+        int last_id = sqlite3_last_insert_rowid(dbh);
149
+        // Insert updated distances
150
+        dberr = sqlite3_prepare_v2(dbh, "SELECT id, tempo, amplitude, frequency, attack FROM songs", -1, &res, 0);
151
+        if (SQLITE_OK != dberr) {
152
+            fprintf(stderr, "Error while inserting data in db: %s\n\n", sqlite3_errmsg(dbh));
153
+            sqlite3_exec(dbh, "ROLLBACK", NULL, NULL, NULL);
154
+            continue;
155
+        }
156
+        int dberr2 = SQLITE_OK;
157
+        while (sqlite3_step(res) == SQLITE_ROW) {
158
+            int id = sqlite3_column_int(res, 0);
159
+            if (id == last_id) {
160
+                // Skip last inserted item
161
+                continue;
162
+            }
163
+            struct force_vector_s song_db;
164
+            song_db.tempo = sqlite3_column_double(res, 1);
165
+            song_db.amplitude = sqlite3_column_double(res, 2);
166
+            song_db.frequency = sqlite3_column_double(res, 3);
167
+            song_db.attack = sqlite3_column_double(res, 4);
168
+            float distance = bl_distance(song_analysis.force_vector, song_db);
169
+
170
+            sqlite3_stmt *res2;
171
+            dberr2 = sqlite3_prepare_v2(dbh,
172
+                    "INSERT INTO distances(song1, song2, distance) VALUES(?, ?, ?)",
173
+                    -1, &res2, 0);
174
+            if (SQLITE_OK != dberr2) {
175
+                fprintf(stderr, "Error while inserting data in db: %s\n\n", sqlite3_errmsg(dbh));
176
+                break;
177
+            }
178
+            printf("%d %d %f\n", last_id, id, distance);
179
+            sqlite3_bind_int(res2, 1, last_id);
180
+            sqlite3_bind_int(res2, 2, id);
181
+            sqlite3_bind_double(res2, 3, distance);
182
+            sqlite3_step(res2);
183
+            sqlite3_finalize(res2);
184
+        }
185
+        if (SQLITE_OK != dberr2) {
186
+            sqlite3_exec(dbh, "ROLLBACK", NULL, NULL, NULL);
187
+            continue;
188
+        }
189
+        sqlite3_finalize(res);
190
+        // Commit transaction
191
+        dberr = sqlite3_exec(dbh, "COMMIT", NULL, NULL, NULL);
192
+        if (SQLITE_OK != dberr) {
193
+            fprintf(stderr, "Error while inserting data in db: %s\n\n", sqlite3_errmsg(dbh));
194
+            sqlite3_exec(dbh, "ROLLBACK", NULL, NULL, NULL);
195
+            continue;
196
+        }
197
+
198
+        // Update latest mtime
199
+        if (difftime(song_mtime, latest_mtime) >= 0) {
200
+            latest_mtime = song_mtime;
201
+        }
202
+
203
+        // Free the allocated entity
204
+        mpd_entity_free(entity);
205
+        printf("\n");
206
+    }
207
+
208
+    // Close SQLite connection
209
+    sqlite3_close(dbh);
210
+
211
+    // Update last_mtime
212
+    FILE *fp = fopen(mpdbliss_data_file, "w+");
213
+    if (NULL != fp) {
214
+        fprintf(fp, "%d\n", latest_mtime);
215
+        fclose(fp);
216
+    }
217
+    else {
218
+        fprintf(stderr, "Unable to store latest mtime seen.\n");
219
+        return;
220
+    }
221
+}
222
+
223
+
224
+int main(int argc, char** argv) {
225
+    struct gengetopt_args_info args_info;
226
+
227
+    // Scan arguments
228
+    if (0 != cmdline_parser(argc, argv, &args_info)) {
229
+        exit(EXIT_FAILURE) ;
230
+    }
231
+
232
+    // Create MPD connection
233
+    conn = mpd_connection_new(
234
+            args_info.host_arg,
235
+            args_info.port_arg,
236
+            30000);
237
+    if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS) {
238
+        fprintf(stderr, "Unable to connect to the MPD server.\n");
239
+        exit(EXIT_FAILURE);
240
+    }
241
+
242
+    char *mpd_base_path = args_info.mpd_root_arg;
243
+
244
+    // Get data directory
245
+    char *xdg_data_home_env = getenv("XDG_DATA_HOME");
246
+    if (NULL == xdg_data_home_env) {
247
+        strncat(mpdbliss_data_file, getenv("HOME"), DEFAULT_STRING_LENGTH);
248
+        strip_trailing_slash(mpdbliss_data_file);
249
+        strncat(mpdbliss_data_file, "/.local/share/mpdbliss", DEFAULT_STRING_LENGTH);
250
+    }
251
+    else {
252
+        strncpy(mpdbliss_data_file, xdg_data_home_env, DEFAULT_STRING_LENGTH);
253
+        strip_trailing_slash(mpdbliss_data_file);
254
+        strncat(mpdbliss_data_file, "/mpdbliss", DEFAULT_STRING_LENGTH);
255
+    }
256
+
257
+    // Ensure data folder exists
258
+    mkdir(mpdbliss_data_file, 0700);
259
+
260
+    // Set data file path
261
+    strncat(mpdbliss_data_db, mpdbliss_data_file, DEFAULT_STRING_LENGTH);
262
+    strncat(mpdbliss_data_db, "/db.sqlite3", DEFAULT_STRING_LENGTH);
263
+    strncat(mpdbliss_data_file, "/latest_mtime.txt", DEFAULT_STRING_LENGTH);
264
+
265
+    // Get latest mtime
266
+    time_t last_mtime = 0;  // Set it to epoch by default
267
+    FILE *fp = fopen(mpdbliss_data_file, "r");
268
+    if (NULL != fp) {
269
+        // Read it from file if applicable
270
+        fscanf(fp, "%d\n", &last_mtime);
271
+        fclose(fp);
272
+    }
273
+
274
+    // Initialize database table
275
+    sqlite3 *dbh;
276
+    if (0 != sqlite3_open_v2(mpdbliss_data_db, &dbh, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL)) {
277
+        fprintf(stderr, "Unable to open SQLite db.\n");
278
+        return EXIT_FAILURE;
279
+    }
280
+    int dberr = sqlite3_exec(dbh, "PRAGMA foreign_keys = ON", NULL, NULL, NULL);
281
+    if (SQLITE_OK != dberr) {
282
+        fprintf(stderr, "Error creating db: %s.\n", sqlite3_errmsg(dbh));
283
+        sqlite3_close(dbh);
284
+        return EXIT_FAILURE;
285
+    }
286
+    dberr = sqlite3_exec(dbh, "CREATE TABLE IF NOT EXISTS songs( \
287
+        id INTEGER PRIMARY KEY, \
288
+        tempo REAL, \
289
+        amplitude REAL, \
290
+        frequency REAL, \
291
+        attack REAL, \
292
+        filename TEXT)",
293
+        NULL, NULL, NULL);
294
+    if (SQLITE_OK != dberr) {
295
+        fprintf(stderr, "Error creating db: %s.\n", sqlite3_errmsg(dbh));
296
+        sqlite3_close(dbh);
297
+        return EXIT_FAILURE;
298
+    }
299
+    dberr = sqlite3_exec(dbh, "CREATE TABLE IF NOT EXISTS distances( \
300
+        song1 INTEGER, \
301
+        song2 INTEGER, \
302
+        distance REAL, \
303
+        FOREIGN KEY(song1) REFERENCES songs(id) ON DELETE CASCADE, \
304
+        FOREIGN KEY(song2) REFERENCES songs(id) ON DELETE CASCADE)",
305
+        NULL, NULL, NULL);
306
+    if (SQLITE_OK != dberr) {
307
+        fprintf(stderr, "Error creating db: %s.\n", sqlite3_errmsg(dbh));
308
+        sqlite3_close(dbh);
309
+        return EXIT_FAILURE;
310
+    }
311
+    sqlite3_close(dbh);
312
+
313
+    // Check if a full rescan is needed
314
+    if (1 == args_info.rescan_flag) {
315
+        update_database(conn, last_mtime, mpd_base_path);
316
+    }
317
+    // Else, if we requested an update of the db
318
+    else if (true == args_info.update_flag) {
319
+        // Rescan from last known mtime
320
+        update_database(conn, last_mtime, mpd_base_path);
321
+    }
322
+    else {
323
+        // Setting signal handler
324
+        if (signal(SIGINT, sigint_catch_function) == SIG_ERR) {
325
+            fprintf(stderr, "An error occurred while setting a signal handler.\n");
326
+            return EXIT_FAILURE;
327
+        }
328
+
329
+        while (mpd_run_idle_loop) {
330
+            // Else, start an MPD IDLE connection
331
+            mpd_run_idle_mask(conn, MPD_IDLE_DATABASE);
332
+
333
+            // Rescan from last known mtime
334
+            update_database(conn, last_mtime, mpd_base_path);
335
+
336
+            // Stop listening to MPD IDLE
337
+            mpd_run_noidle(conn);
338
+        }
339
+    }
340
+
341
+    return EXIT_SUCCESS;
342
+}