aft: Avoid invalid read.
[paraslash.git] / afh_common.c
index cb773bd69bb2cc2706969e280685cadc160b2e01..a267f58b106b9f0df09d3a8dad99368c3c519401 100644 (file)
 #include "string.h"
 #include "afh.h"
 
-typedef void afh_init_func(struct audio_format_handler *);
-
-/*
- * Declaration of the audio format handler init functions.
- *
- * These symbols are referenced in the afl array below.
- *
- * Most audio format handlers depend on an external library and are not
- * compiled in if the library is not installed. Hence it is well possible that
- * not all of these functions are defined. It does not hurt to declare them
- * anyway, and this avoids another set of ifdefs.
- */
-extern afh_init_func mp3_afh_init, ogg_afh_init, aac_afh_init, wma_afh_init,
-       spx_afh_init, flac_afh_init, opus_afh_init;
-
 /** The list of all status items */
 const char *status_item_list[] = {STATUS_ITEMS};
 
 /**
- * The list of supported audio formats.
- *
- * We always define the full array of audio formats even if some audio formats
- * were not compiled in. This is because for each audio file the number of its
- * audio format is stored in the database. We don't want these numbers to become
- * stale just because the user installed a new version of paraslash that
- * supports a different set of audio formats.
- *
- * It can still be easily detected whether an audio format is compiled in by
- * checking if the init function pointer is not \p NULL.
+ * For each audio file the number of its audio format is stored in the
+ * database. Therefore this list, in particular its order, is part of the ABI.
+ * So it's only OK to append new audio formats. All audio formats are listed
+ * here, regardless of whether the audio format handler is compiled in.
  */
-static struct audio_format_handler afl[] = {
-       {
-               .name = "mp3",
-               .init = mp3_afh_init,
-       },
-       {
-               .name = "ogg",
-#if defined(HAVE_OGG) && defined(HAVE_VORBIS)
-               .init = ogg_afh_init,
-#endif
-       },
-       {
-               .name = "aac",
-#if defined(HAVE_FAAD)
-               .init = aac_afh_init,
-#endif
-       },
-       {
-               .name = "wma",
-               .init = wma_afh_init,
-       },
-       {
-               .name = "spx",
-#if defined(HAVE_OGG) && defined(HAVE_SPEEX)
-               .init = spx_afh_init,
-#endif
-       },
-       {
-               .name = "flac",
-#if defined(HAVE_OGG) && defined(HAVE_FLAC)
-               .init = flac_afh_init,
-#endif
-       },
-       {
-               .name = "opus",
-#if defined(HAVE_OGG) && defined(HAVE_OPUS)
-               .init = opus_afh_init,
-#endif
-       },
-       {
-               .name = NULL,
-       }
-};
+#define ALL_AUDIO_FORMATS \
+       AUDIO_FORMAT(mp3) \
+       AUDIO_FORMAT(ogg) \
+       AUDIO_FORMAT(aac) \
+       AUDIO_FORMAT(wma) \
+       AUDIO_FORMAT(spx) \
+       AUDIO_FORMAT(flac) \
+       AUDIO_FORMAT(opus) \
+
+/** \cond audio_format_handler */
+#define AUDIO_FORMAT(_fmt) #_fmt,
+static const char * const audio_format_names[] = {ALL_AUDIO_FORMATS};
+#undef AUDIO_FORMAT
+/* Weak declarations must be public. */
+#define AUDIO_FORMAT(_fmt) \
+       struct audio_format_handler _fmt ## _afh __attribute__ ((weak)) \
+       = {.get_file_info = NULL};
+ALL_AUDIO_FORMATS
+#undef AUDIO_FORMAT
+#define AUDIO_FORMAT(_fmt) & _fmt ## _afh,
+static struct audio_format_handler *afl[] = {ALL_AUDIO_FORMATS};
+#undef AUDIO_FORMAT
+#define NUM_AUDIO_FORMATS (ARRAY_SIZE(afl))
+/** \endcond audio_format_handler */
 
 /**
  * Get the name of the given audio format.
@@ -95,39 +55,25 @@ static struct audio_format_handler afl[] = {
  */
 const char *audio_format_name(int i)
 {
-       if (i < 0 || i >= ARRAY_SIZE(afl) - 1)
+       if (i < 0 || i >= NUM_AUDIO_FORMATS)
                return "???";
-       return afl[i].name;
+       return audio_format_names[i];
 }
 
 static inline int next_audio_format(int format)
 {
        for (;;) {
-               if (!afl[format].name)
-                       return format;
                format++;
-               if (afl[format].init)
+               if (format >= NUM_AUDIO_FORMATS)
+                       return format;
+               if (afl[format]->get_file_info)
                        return format;
        }
 }
 
 /** Iterate over each supported audio format. */
-#define FOR_EACH_AUDIO_FORMAT(i) for (i = 0; afl[i].name; i = next_audio_format(i))
-
-/**
- * Call the init function of each supported audio format handler.
- */
-void afh_init(void)
-{
-       int i;
-
-       PARA_NOTICE_LOG("supported audio formats: %s\n", AUDIO_FORMAT_HANDLERS);
-       FOR_EACH_AUDIO_FORMAT(i) {
-               PARA_INFO_LOG("initializing %s handler\n",
-                       audio_format_name(i));
-               afl[i].init(&afl[i]);
-       }
-}
+#define FOR_EACH_AUDIO_FORMAT(i) \
+       for (i = 0; i < NUM_AUDIO_FORMATS; i = next_audio_format(i))
 
 /**
  * Tell whether an audio format handler provides chunk tables.
@@ -142,7 +88,7 @@ void afh_init(void)
  */
 bool afh_supports_dynamic_chunks(int audio_format_id)
 {
-       return afl[audio_format_id].get_chunk;
+       return afl[audio_format_id]->get_chunk;
 }
 
 /**
@@ -159,8 +105,8 @@ int guess_audio_format(const char *name)
        int i,j, len = strlen(name);
 
        FOR_EACH_AUDIO_FORMAT(i) {
-               for (j = 0; afl[i].suffixes[j]; j++) {
-                       const char *p = afl[i].suffixes[j];
+               for (j = 0; afl[i]->suffixes[j]; j++) {
+                       const char *p = afl[i]->suffixes[j];
                        int plen = strlen(p);
                        if (len < plen + 1)
                                continue;
@@ -182,7 +128,7 @@ static int get_file_info(int format, const char *path, char *data,
        const char *fmt = audio_format_name(format);
 
        memset(afhi, 0, sizeof(*afhi));
-       ret = afl[format].get_file_info(data, size, fd, afhi);
+       ret = afl[format]->get_file_info(data, size, fd, afhi);
        if (ret < 0) {
                PARA_WARNING_LOG("%s: %s format not detected: %s\n",
                        path, fmt, para_strerror(-ret));
@@ -303,7 +249,7 @@ __must_check int afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi,
                uint8_t audio_format_id, const void *map, size_t mapsize,
                const char **buf, size_t *len, void **afh_context)
 {
-       struct audio_format_handler *afh = afl + audio_format_id;
+       struct audio_format_handler *afh = afl[audio_format_id];
 
        if (afh_supports_dynamic_chunks(audio_format_id)) {
                int ret;
@@ -340,7 +286,7 @@ __must_check int afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi,
  */
 void afh_close(void *afh_context, uint8_t audio_format_id)
 {
-       struct audio_format_handler *afh = afl + audio_format_id;
+       struct audio_format_handler *afh = afl[audio_format_id];
 
        if (!afh_supports_dynamic_chunks(audio_format_id))
                return;
@@ -403,7 +349,7 @@ int32_t afh_get_start_chunk(int32_t approx_chunk_num,
 void afh_get_header(struct afh_info *afhi, uint8_t audio_format_id,
                void *map, size_t mapsize, char **buf, size_t *len)
 {
-       struct audio_format_handler *afh = afl + audio_format_id;
+       struct audio_format_handler *afh = afl[audio_format_id];
 
        if (!map || !afhi || !afhi->header_len) {
                *buf = NULL;
@@ -426,7 +372,7 @@ void afh_get_header(struct afh_info *afhi, uint8_t audio_format_id,
  */
 void afh_free_header(char *header_buf, uint8_t audio_format_id)
 {
-       struct audio_format_handler *afh = afl + audio_format_id;
+       struct audio_format_handler *afh = afl[audio_format_id];
 
        if (afh->get_header)
                free(header_buf);
@@ -530,7 +476,7 @@ void set_max_chunk_size(struct afh_info *afhi)
 int afh_rewrite_tags(int audio_format_id, void *map, size_t mapsize,
                struct taginfo *tags, int output_fd, const char *filename)
 {
-       struct audio_format_handler *afh = afl + audio_format_id;
+       struct audio_format_handler *afh = afl[audio_format_id];
 
        if (!afh->rewrite_tags)
                return -ERRNO_TO_PARA_ERROR(ENOTSUP);