error: invalid initializer va_list ap2 = va_arg(ap, va_list);

213 views Asked by At

I'm currently in chroot building LFS. I was trying to compile a legacy project called install-log but I get this error:

(lfs chroot) root:/src/install-log# make install
cc  -MMD -c -o list.o list.c
In file included from list.c:22:
list.c: In function 'fprintf_node':
list.c:145:23: error: invalid initializer
  145 |         va_list ap2 = va_arg(ap, va_list);
      |                       ^~~~~~
make: *** [Makefile:55: list.o] Error 1

The source of the program is present on the WayBack Machine and this is the program's home page on SourceForge.

I have no idea what is wrong as I'm not very technical. If someone could help, it would be appreciated!

This is the code snippet:

/* Prints 'node', using 'ap' as a formatting guide. */
static void fprintf_node(list_t* node, va_list ap)
{
        FILE* file = va_arg(ap, FILE*);
        char* fmt = va_arg(ap, char*);
/*line 145-->*/  va_list ap2 = va_arg(ap, va_list);

        /* An individual %! token */
        int token_cap = 8;
        int token_len = 0;
        char* token = xmalloc(token_cap);
        /* The expanded version of one %! token */
        int exp_token_cap = 8;
        int exp_token_len = 0;
        char* exp_token = xmalloc(exp_token_cap);
        /* The format string with all %!'s expanded */
        char* fmt2 = strdup(fmt);
        int fmt2_len = strlen(fmt2);
        int fmt2_cap = strlen(fmt2);
        char* fmt2_p = fmt2;
1

There are 1 answers

0
Jonathan Leffler On

The code in the package tries to use a generic function, proc_list_va() (defined in list.c), to apply two different functions to each element of the list (once in list.c, once in database.c). In doing so, it contorts the code horribly. I assume it worked with the available compilers on the test platforms when the code was released in May 2003, but it doesn't compile happily now.

It seems to me that the best fix is to use separate apply functions for the two different cases; it simplifies the code dramatically. I renamed the driving functions to apply_print_node() in list.c and to apply_prev_file() in database.c. I removed the declaration of proc_list_va() from install-log.h.

With the changes, the code compiles 'cleanly' under the default options for GCC 11.2.0 on a MacBook Pro running macOS Big Sur 11.6.5. That is, the makefile uses only -MMD to generate dependency information:

cc  -MMD -c -o database.o database.c
cc  -MMD -c -o list.o list.c

There are no complaints from the compiler with these options. It would be worth adding -O or -O3 to the compiler command lines. I've not dared to try my normal set of compiler options (-O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes -fno-common).

I'm not sure what's the best way to get the information to you. There's a 'patch' format file (series of diff -u outputs) for the three changed files below. If you would like a tar-ball of the original and modified source files, contact me (see my SO profile, and include "SO 7182-0194" in the subject line). The original versions of the files were stashed in the Safe subdirectory and the names have a timestamp attached recording when I downloaded them. It kept the originals unsullied while I hacked on the code. The primary changes are in list.c, database.c and install-log.h but I also made sundry other modernization changes in other files.

--- Safe/install-log-20220410.145707.h  2022-04-10 14:57:07.000000000 -0600
+++ install-log.h   2022-04-10 16:24:03.000000000 -0600
@@ -22,6 +22,7 @@
 #ifndef SEEN_INSTALL_LOG_H
 #define SEEN_INSTALL_LOG_H
 
+#include <stdio.h>
 #include <time.h>
 #include <stdarg.h>
 #include "version.h"
@@ -76,14 +77,14 @@
 extern void touch_timestamp(void);
 
 /* list.c */
+extern bool list_has_string(const list_t* list, const char* string);
 extern void add_node(list_t** node, void* data);
 extern void add_string_node(list_t** node, const char* string);
+extern void clear_list(list_t* list);
+extern void fprintf_list(FILE* file, list_t* list, char* fmt, ...);
 extern void insert_string_node(list_t* list, const char* string);
 extern void make_string_list(list_t* list, const char** string);
 extern void proc_list(list_t* list, void (*func)(list_t*));
-extern void proc_list_va(list_t* list, void (*func)(list_t*, va_list), ...);
-extern void clear_list(list_t* list);
-extern bool list_has_string(const list_t* list, const char* string);
 
 /* util.c */
 extern char* safe_sprintf(char** to, int* to_cap, char* fmt, ...);
--- Safe/config-20220410.150210.c   2022-04-10 15:02:10.000000000 -0600
+++ config.c    2022-04-10 15:34:00.000000000 -0600
@@ -19,6 +19,7 @@
  * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
  * Place - Suite 330, Boston, MA  02111-1307, USA. */
 
+#include <ctype.h>
 #include <getopt.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -159,7 +160,7 @@
 {
    int i;
    int line_num = 0;
-   int buf_len = 256;
+   size_t buf_len = 256;
    char* buf;  /* The whole line */
    char* value;    /* After the = */
    FILE* file;
--- Safe/database-20220410.150111.c 2022-04-10 15:01:11.000000000 -0600
+++ database.c  2022-04-11 07:58:48.000000000 -0600
@@ -28,13 +28,26 @@
 #include <unistd.h>
 #include "install-log.h"
 
-static void process_prev_file(list_t* node, va_list ap);
+static void process_prev_file(list_t* node, char **bufptr, int *buflen);
+
+/* Calls 'func' on each node in 'list', passing a va_list to 'func'. */
+typedef void (*CallBack)(list_t *list, char **bufptr, int *buflen);
+
+static void apply_prev_file(list_t* list, CallBack func, char **bufptr, int *buflen)
+{
+   list = list->next;
+   while (list != NULL) {
+       list_t* next = list->next;
+       func(list, bufptr, buflen);
+       list = next;
+   }
+}
 
 /* Gets data from existing database. */
 void read_db(void)
 {
    list_t* node = &prev_files;
-   int buf_cap = 64;
+   size_t buf_cap = 64;
    struct stat statbuf;
    char* buf;
    FILE* db_file;
@@ -106,7 +119,7 @@
    clear_list(&old_files);
    clear_list(&del_files);
 
-   proc_list_va(&prev_files, process_prev_file, &fullname, &fullname_cap);
+   apply_prev_file(&prev_files, process_prev_file, &fullname, &fullname_cap);
 
    if (fullname != NULL) free(fullname);
 }
@@ -136,10 +149,8 @@
 }
 
 /* Helper for find_old_and_del_files. */
-static void process_prev_file(list_t* node, va_list ap)
+static void process_prev_file(list_t* node, char **fullname, int *fullname_cap)
 {
-   char** fullname = va_arg(ap, char**);
-   int* fullname_cap = va_arg(ap, int*);
    struct stat statbuf;
    safe_sprintf(fullname, fullname_cap, "%s/%s", root, node->data);
    
--- Safe/editor-20220410.150044.c   2022-04-10 15:00:44.000000000 -0600
+++ editor.c    2022-04-10 15:32:52.000000000 -0600
@@ -22,7 +22,7 @@
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <sys/types.h>
+#include <string.h>
 #include <sys/wait.h>
 #include <unistd.h>
 #include "install-log.h"
--- Safe/find-20220410.150021.c 2022-04-10 15:00:21.000000000 -0600
+++ find.c  2022-04-10 15:37:28.000000000 -0600
@@ -19,13 +19,13 @@
  * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
  * Place - Suite 330, Boston, MA  02111-1307, USA. */
 
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
 #include <dirent.h>
 #include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
 #include "install-log.h"
 
 typedef struct dirent dirent_t;
@@ -68,7 +68,7 @@
        return;
    }
 
-   while (d = readdir(dir))
+   while ((d = readdir(dir)) != 0)
    {
        /* Skip '.' and '..' */
        if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0)
--- Safe/list-20220410.144854.c 2022-04-10 14:48:54.000000000 -0600
+++ list.c  2022-04-11 07:57:32.000000000 -0600
@@ -27,7 +27,7 @@
 
 static list_t* make_node(void* data);
 static void free_node(list_t* node);
-static void fprintf_node(list_t* node, va_list ap);
+static void fprintf_node(list_t* node, FILE *fp, char *fmt, va_list ap);
 
 /* Creates a node containing 'data' and advance *'node' to point to it. */
 void add_node(list_t** node, void* data)
@@ -82,14 +82,16 @@
 }
 
 /* Calls 'func' on each node in 'list', passing a va_list to 'func'. */
-void proc_list_va(list_t* list, void (*func)(list_t*, va_list), ...)
+typedef void (*CallBack)(list_t *list, FILE *fp, char *fmt, va_list ap);
+
+static void apply_print_node(list_t* list, FILE *fp, CallBack func, char *fmt, ...)
 {
    va_list ap;
    list = list->next;
    while (list != NULL) {
        list_t* next = list->next;
-       va_start(ap, func);
-       func(list, ap);
+       va_start(ap, fmt);
+       func(list, fp, fmt, ap);
        va_end(ap);
        list = next;
    }
@@ -102,7 +104,7 @@
 {
    va_list ap;
    va_start(ap, fmt);
-   proc_list_va(list, fprintf_node, file, fmt, ap);
+   apply_print_node(list, file, fprintf_node, fmt, ap);
    va_end(ap);
 }
 
@@ -138,12 +140,8 @@
 }
 
 /* Prints 'node', using 'ap' as a formatting guide. */
-static void fprintf_node(list_t* node, va_list ap)
+static void fprintf_node(list_t* node, FILE *fp, char *fmt, va_list ap)
 {
-   FILE* file = va_arg(ap, FILE*);
-   char* fmt = va_arg(ap, char*);
-   va_list ap2 = va_arg(ap, va_list);
-   
    /* An individual %! token */
    int token_cap = 8;
    int token_len = 0;
@@ -190,7 +188,7 @@
    }
 
    /* Finally, print */
-   vfprintf(file, fmt2, ap2);
+   vfprintf(fp, fmt2, ap);
 
    free(token);
    free(exp_token);
--- Safe/timestamp-20220410.145816.c    2022-04-10 14:58:16.000000000 -0600
+++ timestamp.c 2022-04-10 15:32:38.000000000 -0600
@@ -19,14 +19,14 @@
  * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
  * Place - Suite 330, Boston, MA  02111-1307, USA. */
 
+#include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <sys/types.h> 
-#include <sys/stat.h> 
+#include <string.h>
+#include <sys/stat.h>
 #include <time.h>
-#include <utime.h>
 #include <unistd.h>
-#include <errno.h>
+#include <utime.h>
 #include "install-log.h"
 
 typedef struct stat stat_t;

Hmmm, it occurs to me that since the 'apply' functions are now each calling a single function, there's no need for the function pointer argument. They could simply call the appropriate function directly. That's a refinement you can make if you want.

I should state that I've only compiled the code; I've not attempted to run it.

I did not manage to download the README file from the WayBack Machine (Internet Archive) at http://web.archive.org/web/20051214084919/http://ioioio.net:80/devel/install-log/install-log/. The CVS directory was likewise not available for download.