1 /* 2 * Copyright (c) 1980, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * @(#) Copyright (c) 1980, 1993 The Regents of the University of California. All rights reserved. 30 * @(#)mkstr.c 8.1 (Berkeley) 6/6/93 31 * $FreeBSD: src/usr.bin/mkstr/mkstr.c,v 1.4.2.1 2002/11/16 01:07:42 tjr Exp $ 32 */ 33 34 #include <err.h> 35 #include <errno.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 40 #define ungetchar(c) ungetc(c, stdin) 41 42 /* 43 * mkstr - create a string error message file by massaging C source 44 * 45 * Bill Joy UCB August 1977 46 * 47 * Modified March 1978 to hash old messages to be able to recompile 48 * without addding messages to the message file (usually) 49 * 50 * Based on an earlier program conceived by Bill Joy and Chuck Haley 51 * 52 * Program to create a string error message file 53 * from a group of C programs. Arguments are the name 54 * of the file where the strings are to be placed, the 55 * prefix of the new files where the processed source text 56 * is to be placed, and the files to be processed. 57 * 58 * The program looks for 'error("' in the source stream. 59 * Whenever it finds this, the following characters from the '"' 60 * to a '"' are replaced by 'seekpt' where seekpt is a 61 * pointer into the error message file. 62 * If the '(' is not immediately followed by a '"' no change occurs. 63 * 64 * The optional '-' causes strings to be added at the end of the 65 * existing error message file for recompilation of single routines. 66 */ 67 68 static FILE *mesgread, *mesgwrite; 69 static char name[100], *np; 70 71 static void copystr(void); 72 static int fgetNUL(char *, int, FILE *); 73 static unsigned hashit(char *, int, unsigned); 74 static void inithash(void); 75 static int match(const char *); 76 static int octdigit(char); 77 static void process(void); 78 static void usage(void) __dead2; 79 80 int 81 main(int argc, char *argv[]) 82 { 83 char addon = 0; 84 size_t namelen; 85 86 argc--, argv++; 87 if (argc > 1 && argv[0][0] == '-') 88 addon++, argc--, argv++; 89 if (argc < 3) 90 usage(); 91 mesgwrite = fopen(argv[0], addon ? "a" : "w"); 92 if (mesgwrite == NULL) 93 err(1, "%s", argv[0]); 94 mesgread = fopen(argv[0], "r"); 95 if (mesgread == NULL) 96 err(1, "%s", argv[0]); 97 inithash(); 98 argc--, argv++; 99 namelen = strlcpy(name, argv[0], sizeof(name)); 100 if (namelen >= sizeof(name)) { 101 errno = ENAMETOOLONG; 102 err(1, "%s", argv[0]); 103 } 104 np = name + namelen; 105 argc--, argv++; 106 do { 107 if (strlcpy(np, argv[0], sizeof(name) - namelen) >= 108 sizeof(name) - namelen) { 109 errno = ENAMETOOLONG; 110 err(1, "%s%s", name, argv[0]); 111 } 112 if (freopen(name, "w", stdout) == NULL) 113 err(1, "%s", name); 114 if (freopen(argv[0], "r", stdin) == NULL) 115 err(1, "%s", argv[0]); 116 process(); 117 argc--, argv++; 118 } while (argc > 0); 119 exit(0); 120 } 121 122 static void 123 usage(void) 124 { 125 fprintf(stderr, "usage: mkstr [-] mesgfile prefix file ...\n"); 126 exit(1); 127 } 128 129 static void 130 process(void) 131 { 132 int c; 133 134 for (;;) { 135 c = getchar(); 136 if (c == EOF) 137 return; 138 if (c != 'e') { 139 putchar(c); 140 continue; 141 } 142 if (match("error(")) { 143 printf("error("); 144 c = getchar(); 145 if (c != '"') 146 putchar(c); 147 else 148 copystr(); 149 } 150 } 151 } 152 153 static int 154 match(const char *ocp) 155 { 156 const char *cp; 157 int c; 158 159 for (cp = ocp + 1; *cp; cp++) { 160 c = getchar(); 161 if (c != *cp) { 162 while (ocp < cp) 163 putchar(*ocp++); 164 ungetchar(c); 165 return (0); 166 } 167 } 168 return (1); 169 } 170 171 static void 172 copystr(void) 173 { 174 int c, ch; 175 char buf[512]; 176 char *cp = buf; 177 178 for (;;) { 179 if (cp == buf + sizeof(buf) - 2) 180 errx(1, "message too long"); 181 c = getchar(); 182 if (c == EOF) 183 break; 184 switch (c) { 185 186 case '"': 187 *cp++ = 0; 188 goto out; 189 case '\\': 190 c = getchar(); 191 switch (c) { 192 193 case 'b': 194 c = '\b'; 195 break; 196 case 't': 197 c = '\t'; 198 break; 199 case 'r': 200 c = '\r'; 201 break; 202 case 'n': 203 c = '\n'; 204 break; 205 case '\n': 206 continue; 207 case 'f': 208 c = '\f'; 209 break; 210 case '0': 211 c = 0; 212 break; 213 case '\\': 214 break; 215 default: 216 if (!octdigit(c)) 217 break; 218 c -= '0'; 219 ch = getchar(); 220 if (!octdigit(ch)) 221 break; 222 c <<= 7, c += ch - '0'; 223 ch = getchar(); 224 if (!octdigit(ch)) 225 break; 226 c <<= 3, c+= ch - '0', ch = -1; 227 break; 228 } 229 } 230 *cp++ = c; 231 } 232 out: 233 *cp = 0; 234 printf("%d", hashit(buf, 1, 0)); 235 } 236 237 static int 238 octdigit(char c) 239 { 240 241 return (c >= '0' && c <= '7'); 242 } 243 244 static void 245 inithash(void) 246 { 247 char buf[512]; 248 int mesgpt = 0; 249 250 rewind(mesgread); 251 while (fgetNUL(buf, sizeof buf, mesgread) != 0) { 252 hashit(buf, 0, mesgpt); 253 mesgpt += strlen(buf) + 2; 254 } 255 } 256 257 #define NBUCKETS 511 258 259 static struct hash { 260 long hval; 261 unsigned hpt; 262 struct hash *hnext; 263 } *bucket[NBUCKETS]; 264 265 static unsigned 266 hashit(char *str, int really, unsigned fakept) 267 { 268 int i; 269 struct hash *hp; 270 char buf[512]; 271 long hashval = 0; 272 char *cp; 273 274 if (really) 275 fflush(mesgwrite); 276 for (cp = str; *cp;) 277 hashval = (hashval << 1) + *cp++; 278 i = hashval % NBUCKETS; 279 if (i < 0) 280 i += NBUCKETS; 281 if (really != 0) 282 for (hp = bucket[i]; hp != NULL; hp = hp->hnext) 283 if (hp->hval == hashval) { 284 fseek(mesgread, (long) hp->hpt, 0); 285 fgetNUL(buf, sizeof buf, mesgread); 286 /* 287 fprintf(stderr, "Got (from %d) %s\n", hp->hpt, buf); 288 */ 289 if (strcmp(buf, str) == 0) 290 break; 291 } 292 if (!really || hp == NULL) { 293 hp = (struct hash *) calloc(1, sizeof *hp); 294 if (hp == NULL) 295 err(1, NULL); 296 hp->hnext = bucket[i]; 297 hp->hval = hashval; 298 hp->hpt = really ? ftell(mesgwrite) : fakept; 299 if (really) { 300 fwrite(str, sizeof (char), strlen(str) + 1, mesgwrite); 301 fwrite("\n", sizeof (char), 1, mesgwrite); 302 } 303 bucket[i] = hp; 304 } 305 /* 306 fprintf(stderr, "%s hashed to %ld at %d\n", str, hp->hval, hp->hpt); 307 */ 308 return (hp->hpt); 309 } 310 311 static int 312 fgetNUL(char *obuf, int rmdr, FILE *file) 313 { 314 int c; 315 char *buf = obuf; 316 317 while (--rmdr > 0 && (c = getc(file)) != 0 && c != EOF) 318 *buf++ = c; 319 *buf++ = 0; 320 getc(file); 321 return ((feof(file) || ferror(file)) ? 0 : 1); 322 } 323