1 /* File : misc.c 2 Author : Ozan Yigit 3 Updated: 26-Mar-1993 4 Purpose: Miscellaneous support code for PD M4. 5 */ 6 7 #include "mdef.h" 8 #include "extr.h" 9 #include "ourlims.h" 10 11 #ifdef DUFFCP 12 13 /* This version of the ANSI standard function memcpy() 14 uses Duff's Device (tm Tom Duff) to unroll the copying loop: 15 while (count-- > 0) *to++ = *from++; 16 */ 17 void memcpy(to, from, count) 18 register char *from, *to; 19 register int count; 20 { 21 if (count > 0) { 22 register int loops = (count+8-1) >> 3; /* div 8 round up */ 23 24 switch (count & (8-1)) { /* mod 8 */ 25 case 0: do { *to++ = *from++; 26 case 7: *to++ = *from++; 27 case 6: *to++ = *from++; 28 case 5: *to++ = *from++; 29 case 4: *to++ = *from++; 30 case 3: *to++ = *from++; 31 case 2: *to++ = *from++; 32 case 1: *to++ = *from++; 33 } while (--loops > 0); 34 } 35 } 36 } 37 38 #endif 39 40 41 /* strsave(s) 42 return a new malloc()ed copy of s -- same as V.3's strdup(). 43 */ 44 char *strsave(s) 45 char *s; 46 { 47 register int n = strlen(s)+1; 48 char *p = malloc(n); 49 50 if (p) memcpy(p, s, n); 51 return p; 52 } 53 54 55 /* indx(s1, s2) 56 if s1 can be decomposed as alpha || s2 || omega, return the length 57 of the shortest such alpha, otherwise return -1. 58 */ 59 int indx(s1, s2) 60 char *s1; 61 char *s2; 62 { 63 register char *t; 64 register char *m; 65 register char *p; 66 67 for (p = s1; *p; p++) { 68 for (t = p, m = s2; *m && *m == *t; m++, t++); 69 if (!*m) return p-s1; 70 } 71 return -1; 72 } 73 74 75 char pbmsg[] = "m4: too many characters pushed back"; 76 77 /* Xputback(c) 78 push character c back onto the input stream. 79 This is now macro putback() in misc.h 80 */ 81 void Xputback(c) 82 char c; 83 { 84 if (bp < endpbb) *bp++ = c; else error(pbmsg); 85 } 86 87 88 /* pbstr(s) 89 push string s back onto the input stream. 90 putback() has been unfolded here to improve performance. 91 Example: 92 s = <ABC> 93 bp = <more stuff> 94 After the call: 95 bp = <more stuffCBA> 96 It would be more efficient if we ran the pushback buffer in the 97 opposite direction 98 */ 99 void pbstr(s) 100 register char *s; 101 { 102 register char *es; 103 register char *zp; 104 105 zp = bp; 106 for (es = s; *es; ) es++; /* now es points to terminating NUL */ 107 bp += es-s; /* advance bp as far as it should go */ 108 if (bp >= endpbb) error("m4: too many characters to push back"); 109 while (es > s) *zp++ = *--es; 110 } 111 112 113 /* pbqtd(s) 114 pushes string s back "quoted", doing whatever has to be done to it to 115 make sure that the result will evaluate to the original value. As it 116 happens, we have only to add lquote and rquote. 117 */ 118 void pbqtd(s) 119 register char *s; 120 { 121 register char *es; 122 register char *zp; 123 124 zp = bp; 125 for (es = s; *es; ) es++; /* now es points to terminating NUL */ 126 bp += 2+es-s; /* advance bp as far as it should go */ 127 if (bp >= endpbb) error("m4: too many characters to push back"); 128 *zp++ = rquote; 129 while (es > s) *zp++ = *--es; 130 *zp++ = lquote; 131 } 132 133 134 /* pbnum(n) 135 convert a number to a (decimal) string and push it back. 136 The original definition did not work for MININT; this does. 137 */ 138 void pbnum(n) 139 int n; 140 { 141 register int num; 142 143 num = n > 0 ? -n : n; /* MININT <= num <= 0 */ 144 do { 145 putback('0' - (num % 10)); 146 } while ((num /= 10) < 0); 147 if (n < 0) putback('-'); 148 } 149 150 151 /* pbrad(n, r, m) 152 converts a number n to base r ([-36..-2] U [2..36]), with at least 153 m digits. If r == 10 and m == 1, this is exactly the same as pbnum. 154 However, this uses the function int2str() from R.A.O'Keefe's public 155 domain string library, and puts the results of that back. 156 The Unix System V Release 3 version of m4 accepts radix 1; 157 THIS VERSION OF M4 DOES NOT ACCEPT RADIX 1 OR -1, 158 nor do we accept radix < -36 or radix > 36. At the moment such bad 159 radices quietly produce nothing. The V.3 treatment of radix 1 is 160 push back abs(n) "1"s, then 161 if n < 0, push back one "-". 162 Until I come across something which uses it, I can't bring myself to 163 implement this. 164 165 I have, however, found a use for radix 0. Unsurprisingly, it is 166 related to radix 0 in Edinburgh Prolog. 167 eval('c1c2...cn', 0, m) 168 pushes back max(m-n,0) blanks and the characters c1...cn. This can 169 adjust to any byte size as long as UCHAR_MAX = (1 << CHAR_BIT) - 1. 170 In particular, eval(c, 0) where 0 < c <= UCHAR_MAX, pushes back the 171 character with code c. Note that this has to agree with eval(); so 172 both of them have to use the same byte ordering. 173 */ 174 void pbrad(n, r, m) 175 long int n; 176 int r, m; 177 { 178 char buffer[34]; 179 char *p; 180 int L; 181 182 if (r == 0) { 183 unsigned long int x = (unsigned long)n; 184 int n; 185 186 for (n = 0; x; x >>= CHAR_BIT, n++) buffer[n] = x & UCHAR_MAX; 187 for (L = n; --L >= 0; ) putback(buffer[L]); 188 for (L = m-n; --L >= 0; ) putback(' '); 189 return; 190 } 191 L = m - (int2str(p = buffer, -r, n)-buffer); 192 if (buffer[0] == '-') L++, p++; 193 if (L > 0) { 194 pbstr(p); 195 while (--L >= 0) putback('0'); 196 if (p != buffer) putback('-'); 197 } else { 198 pbstr(buffer); 199 } 200 } 201 202 203 char csmsg[] = "m4: string space overflow"; 204 205 /* chrsave(c) 206 put the character c in the string space. 207 */ 208 void Xchrsave(c) 209 char c; 210 { 211 #if 0 212 if (sp < 0) putc(c, active); else 213 #endif 214 if (ep < endest) *ep++ = c; else 215 error(csmsg); 216 } 217 218 219 /* getdiv(ind) 220 read in a diversion file and then delete it. 221 */ 222 void getdiv(ind) 223 int ind; 224 { 225 register int c; 226 register FILE *dfil; 227 register FILE *afil; 228 229 afil = active; 230 if (outfile[ind] == afil) 231 error("m4: undivert: diversion still active."); 232 (void) fclose(outfile[ind]); 233 outfile[ind] = NULL; 234 m4temp[UNIQUE] = '0' + ind; 235 if ((dfil = fopen(m4temp, "r")) == NULL) 236 error("m4: cannot undivert."); 237 while ((c = getc(dfil)) != EOF) putc(c, afil); 238 (void) fclose(dfil); 239 240 #if vms 241 if (remove(m4temp)) error("m4: cannot unlink."); 242 #else 243 if (unlink(m4temp) == -1) error("m4: cannot unlink."); 244 #endif 245 } 246 247 248 /* killdiv() 249 delete all the diversion files which have been created. 250 */ 251 void killdiv() 252 { 253 register int n; 254 255 for (n = 0; n < MAXOUT; n++) { 256 if (outfile[n] != NULL) { 257 (void) fclose(outfile[n]); 258 m4temp[UNIQUE] = '0' + n; 259 #if unix 260 (void) unlink(m4temp); 261 #else 262 (void) remove(m4temp); 263 #endif 264 } 265 } 266 } 267 268 269 /* error(s) 270 close all files, report a fatal error, and quit, letting the caller know. 271 */ 272 void error(s) 273 char *s; 274 { 275 killdiv(); 276 fprintf(stderr, "%s\n", s); 277 exit(1); 278 } 279 280 281 /* Interrupt handling 282 */ 283 static char *msg = "\ninterrupted."; 284 285 #ifdef __STDC__ 286 void onintr(int signo) 287 #else 288 onintr() 289 #endif 290 { 291 error(msg); 292 } 293 294 295 void usage() 296 { 297 fprintf(stderr, "Usage: m4 [-e] [-[BHST]int] [-Dname[=val]] [-Uname]\n"); 298 exit(1); 299 } 300 301 #ifdef GETOPT 302 /* Henry Spencer's getopt() - get option letter from argv */ 303 304 char *optarg; /* Global argument pointer. */ 305 int optind = 0; /* Global argv index. */ 306 307 static char *scan = NULL; /* Private scan pointer. */ 308 309 #ifndef __STDC__ 310 extern char *index(); 311 #define strchr index 312 #endif 313 314 int getopt(argc, argv, optstring) 315 int argc; 316 char **argv; 317 char *optstring; 318 { 319 register char c; 320 register char *place; 321 322 optarg = NULL; 323 324 if (scan == NULL || *scan == '\0') { 325 if (optind == 0) optind++; 326 if (optind >= argc 327 || argv[optind][0] != '-' 328 || argv[optind][1] == '\0') 329 return EOF; 330 if (strcmp(argv[optind], "--") == 0) { 331 optind++; 332 return EOF; 333 } 334 scan = argv[optind]+1; 335 optind++; 336 } 337 c = *scan++; 338 place = strchr(optstring, c); 339 340 if (place == NULL || c == ':') { 341 fprintf(stderr, "%s: unknown option -%c\n", argv[0], c); 342 return '?'; 343 } 344 place++; 345 if (*place == ':') { 346 if (*scan != '\0') { 347 optarg = scan; 348 scan = NULL; 349 } else { 350 optarg = argv[optind]; 351 optind++; 352 } 353 } 354 return c; 355 } 356 #endif 357 358