1/* 2 * split-include.c 3 * 4 * Copyright abandoned, Michael Chastain, <mailto:mec@shout.net>. 5 * This is a C version of syncdep.pl by Werner Almesberger. 6 * 7 * This program takes autoconf.h as input and outputs a directory full 8 * of one-line include files, merging onto the old values. 9 * 10 * Think of the configuration options as key-value pairs. Then there 11 * are five cases: 12 * 13 * key old value new value action 14 * 15 * KEY-1 VALUE-1 VALUE-1 leave file alone 16 * KEY-2 VALUE-2A VALUE-2B write VALUE-2B into file 17 * KEY-3 - VALUE-3 write VALUE-3 into file 18 * KEY-4 VALUE-4 - write an empty file 19 * KEY-5 (empty) - leave old empty file alone 20 */ 21 22#include <sys/stat.h> 23#include <sys/types.h> 24 25#include <ctype.h> 26#include <errno.h> 27#include <fcntl.h> 28#include <stdio.h> 29#include <stdlib.h> 30#include <string.h> 31#include <unistd.h> 32 33#define ERROR_EXIT(strExit) \ 34 { \ 35 const int errnoSave = errno; \ 36 fprintf(stderr, "%s: ", str_my_name); \ 37 errno = errnoSave; \ 38 perror((strExit)); \ 39 exit(1); \ 40 } 41 42 43 44int main(int argc, const char * argv []) 45{ 46 const char * str_my_name; 47 const char * str_file_autoconf; 48 const char * str_dir_config; 49 50 FILE * fp_config; 51 FILE * fp_target; 52 FILE * fp_find; 53 54 int buffer_size; 55 56 char * line; 57 char * old_line; 58 char * list_target; 59 char * ptarget; 60 61 struct stat stat_buf; 62 63 /* Check arg count. */ 64 if (argc != 3) 65 { 66 fprintf(stderr, "%s: wrong number of arguments.\n", argv[0]); 67 exit(1); 68 } 69 70 str_my_name = argv[0]; 71 str_file_autoconf = argv[1]; 72 str_dir_config = argv[2]; 73 74 /* Find a buffer size. */ 75 if (stat(str_file_autoconf, &stat_buf) != 0) 76 ERROR_EXIT(str_file_autoconf); 77 buffer_size = 2 * stat_buf.st_size + 4096; 78 79 /* Allocate buffers. */ 80 if ( (line = malloc(buffer_size)) == NULL 81 || (old_line = malloc(buffer_size)) == NULL 82 || (list_target = malloc(buffer_size)) == NULL ) 83 ERROR_EXIT(str_file_autoconf); 84 85 /* Open autoconfig file. */ 86 if ((fp_config = fopen(str_file_autoconf, "r")) == NULL) 87 ERROR_EXIT(str_file_autoconf); 88 89 /* Make output directory if needed. */ 90 if (stat(str_dir_config, &stat_buf) != 0) 91 { 92 if (mkdir(str_dir_config, 0755) != 0) 93 ERROR_EXIT(str_dir_config); 94 } 95 96 /* Change to output directory. */ 97 if (chdir(str_dir_config) != 0) 98 ERROR_EXIT(str_dir_config); 99 100 /* Put initial separator into target list. */ 101 ptarget = list_target; 102 *ptarget++ = '\n'; 103 104 /* Read config lines. */ 105 while (fgets(line, buffer_size, fp_config)) 106 { 107 const char * str_config; 108 int is_same; 109 int itarget; 110 111 if (line[0] != '#') 112 continue; 113 if ((str_config = strstr(line, "CONFIG_")) == NULL) 114 continue; 115 116 /* Make the output file name. */ 117 str_config += sizeof("CONFIG_") - 1; 118 for (itarget = 0; !isspace((int)str_config[itarget]); itarget++) 119 { 120 char c = str_config[itarget]; 121 if (isupper((int)c)) c = tolower((int)c); 122 if (c == '_') c = '/'; 123 ptarget[itarget] = c; 124 } 125 ptarget[itarget++] = '.'; 126 ptarget[itarget++] = 'h'; 127 ptarget[itarget++] = '\0'; 128 129 /* Check for existing file. */ 130 is_same = 0; 131 if ((fp_target = fopen(ptarget, "r")) != NULL) 132 { 133 fgets(old_line, buffer_size, fp_target); 134 if (fclose(fp_target) != 0) 135 ERROR_EXIT(ptarget); 136 if (!strcmp(line, old_line)) 137 is_same = 1; 138 } 139 140 if (!is_same) 141 { 142 /* Auto-create directories. */ 143 int islash; 144 for (islash = 0; islash < itarget; islash++) 145 { 146 if (ptarget[islash] == '/') 147 { 148 ptarget[islash] = '\0'; 149 if (stat(ptarget, &stat_buf) != 0 150 && mkdir(ptarget, 0755) != 0) 151 ERROR_EXIT( ptarget ); 152 ptarget[islash] = '/'; 153 } 154 } 155 156 /* Write the file. */ 157 if ((fp_target = fopen(ptarget, "w" )) == NULL) 158 ERROR_EXIT(ptarget); 159 fputs(line, fp_target); 160 if (ferror(fp_target) || fclose(fp_target) != 0) 161 ERROR_EXIT(ptarget); 162 } 163 164 /* Update target list */ 165 ptarget += itarget; 166 *(ptarget-1) = '\n'; 167 } 168 169 /* 170 * Close autoconfig file. 171 * Terminate the target list. 172 */ 173 if (fclose(fp_config) != 0) 174 ERROR_EXIT(str_file_autoconf); 175 *ptarget = '\0'; 176 177 /* 178 * Fix up existing files which have no new value. 179 * This is Case 4 and Case 5. 180 * 181 * I re-read the tree and filter it against list_target. 182 * This is crude. But it avoids data copies. Also, list_target 183 * is compact and contiguous, so it easily fits into cache. 184 * 185 * Notice that list_target contains strings separated by \n, 186 * with a \n before the first string and after the last. 187 * fgets gives the incoming names a terminating \n. 188 * So by having an initial \n, strstr will find exact matches. 189 */ 190 191 fp_find = popen("find * -type f -name \"*.h\" -print", "r"); 192 if (fp_find == 0) 193 ERROR_EXIT( "find" ); 194 195 line[0] = '\n'; 196 while (fgets(line+1, buffer_size, fp_find)) 197 { 198 if (strstr(list_target, line) == NULL) 199 { 200 /* 201 * This is an old file with no CONFIG_* flag in autoconf.h. 202 */ 203 204 /* First strip the \n. */ 205 line[strlen(line)-1] = '\0'; 206 207 /* Grab size. */ 208 if (stat(line+1, &stat_buf) != 0) 209 ERROR_EXIT(line); 210 211 /* If file is not empty, make it empty and give it a fresh date. */ 212 if (stat_buf.st_size != 0) 213 { 214 if ((fp_target = fopen(line+1, "w")) == NULL) 215 ERROR_EXIT(line); 216 if (fclose(fp_target) != 0) 217 ERROR_EXIT(line); 218 } 219 } 220 } 221 222 if (pclose(fp_find) != 0) 223 ERROR_EXIT("find"); 224 225 return 0; 226} 227