|
|
1.1 root 1: /*
2: * Detect typographical errors.
3: */
4:
5: #include <stdio.h>
6: #include <ctype.h>
7: #include <sys/stat.h>
8:
9: #define oc(c) ((c)+'`')
10: #define NSBRK 512 /* Amount to sbrk at once in bytes */
11: #define BADSBRK ((char *)-1) /* fail from sbrk() */
12: #define NWORD 200 /* Longest word (ridiculously big) */
13: #define NHASH 64 /* Number of hash buckets (power of 2) */
14:
15: char dictfile[] = "/usr/dict/dict";
16: char digfile[] = "/usr/dict/digrams";
17: char trifile[] = "/usr/dict/trigrams";
18: char tmp[] = "/tmp/typoXXXXXX";
19: char sort[] = "sort";
20: char *tmpfile;
21: char notrimem[] = "Out of memory for trigrams";
22: char nosort[] = "Cannot locate sort";
23: char word[NWORD];
24: FILE *ifp;
25: FILE *pfp;
26:
27: char ibuf[BUFSIZ];
28: char obuf[BUFSIZ];
29: char xbuf[BUFSIZ];
30:
31: unsigned hashval;
32:
33: int rflag; /* Raw (no removal of nroff things) input */
34: int nflag; /* No pre-defined statistics or exceptions */
35: int sflag; /* Write out statistics */
36:
37: typedef struct EXCEPT {
38: unsigned e_hval;
39: struct EXCEPT *e_next;
40: char e_word[];
41: } EXCEPT;
42: EXCEPT *except[NHASH];
43:
44: /*
45: * Table of digrams, indexed by first
46: * and then second character. [1-26]
47: * represent [a-z] inclusive and 0
48: * represents the beginning or ending of word.
49: */
50: unsigned digrams[27][27];
51:
52: /*
53: * Table of trigrams, indexed by first and
54: * second letter and allocated on the third letter.
55: * After all of the input data has been
56: * sampled, the t_freq entry changes from
57: * frequency to an index of peculiarity.
58: */
59: typedef struct TRIGRAMS {
60: struct TRIGRAMS *t_next;
61: char t_char;
62: unsigned char t_freq;
63: } TRIGRAMS;
64: TRIGRAMS *trigrams[27][27];
65:
66: /*
67: * Table of logarithms scaled by 1000.
68: * Table from 0-99 inclusive.
69: */
70: int log1[] = {
71: 0000, 0693, 1099, 1386, 1609, 1792, 1946, 2080, 2197, /* 1-9 */
72: 2303, 2398, 2485, 2565, 2639, 2708, 2772, 2833, 2890, /* 10-18 */
73: 2944, 2996, 3045, 3091, 3136, 3178, 3219, 3258, 3296, /* 19-27 */
74: 3332, 3367, 3401, 3434, 3466, 3497, 3526, 3555, 3584, /* 28-36 */
75: 3611, 3638, 3664, 3689, 3714, 3738, 3761, 3784, 3807, /* 37-45 */
76: 3829, 3850, 3871, 3892, 3912, 3932, 3951, 3970, 3989, /* 46-54 */
77: 4007, 4025, 4043, 4060, 4078, 4094, 4111, 4127, 4143, /* 55-63 */
78: 4159, 4174, 4190, 4205, 4220, 4234, 4249, 4263, 4277, /* 64-72 */
79: 4291, 4304, 4318, 4331, 4344, 4357, 4369, 4382, 4394, /* 73-81 */
80: 4407, 4419, 4431, 4443, 4454, 4466, 4477, 4489, 4500, /* 82-90 */
81: 4511, 4521, 4533, 4543, 4554, 4564, 4575, 4585, 4595, /* 91-99 */
82: };
83:
84: /*
85: * Table of logarithms for each multiple
86: * of ten from 100 - 990 inclusive
87: * (scaled as above).
88: */
89: int log2[] = {
90: 4605, 4701, 4788, 4868, 4942, 5011, 4075, 5136, 5193, /* 100-180 */
91: 5247, 5298, 5347, 5394, 5438, 5481, 5522, 5561, 5598, /* 190-270 */
92: 5635, 5670, 5704, 5737, 5768, 5799, 5829, 5858, 5886, /* 280-360 */
93: 5914, 5940, 5966, 5992, 6016, 6040, 6064, 6087, 6109, /* 370-450 */
94: 6131, 6153, 6174, 6194, 6215, 6234, 6254, 6273, 6292, /* 460-540 */
95: 6310, 6328, 6346, 6663, 6380, 6397, 6414, 6430, 6446, /* 550-630 */
96: 6462, 6477, 6492, 6507, 6522, 6537, 6551, 6565, 6579, /* 640-720 */
97: 6593, 6607, 6620, 6633, 6646, 6659, 6672, 6684, 6697, /* 730-810 */
98: 6709, 6721, 6733, 6745, 6757, 6769, 6780, 6791, 6802, /* 820-900 */
99: 6813, 6824, 6835, 6846, 6857, 6867, 6877, 6888, 6898, /* 910-990 */
100: };
101:
102: /*
103: * Table of logarithms of multiples
104: * of 100 from 1000-9900 inclusive
105: * (scaled as before).
106: */
107: int log3[] = {
108: 6908, 7003, 7090, 7170, 7244, 7313, 7378, 7438, 7496, /* 1000-1800 */
109: 7550, 7601, 7650, 7696, 7741, 7783, 7824, 7863, 7901, /* 1900-2700 */
110: 7937, 7973, 8006, 8039, 8071, 8102, 8132, 8161, 8189, /* 2800-3600 */
111: 8216, 8243, 8269, 8294, 8319, 8343, 8366, 8389, 8412, /* 3700-4500 */
112: 8434, 8455, 8476, 8497, 8517, 8537, 8556, 8576, 8594, /* 4600-5400 */
113: 8613, 8631, 8648, 8666, 8683, 8700, 8716, 8732, 8748, /* 5500-6300 */
114: 8764, 8780, 8795, 8810, 8825, 8839, 8854, 8868, 8882, /* 6400-7200 */
115: 8896, 8909, 8923, 8936, 8949, 8962, 8975, 8987, 9000, /* 7300-8100 */
116: 9012, 9024, 9036, 9048, 9060, 9071, 9083, 9094, 9105, /* 8200-9000 */
117: 9116, 9127, 9138, 9149, 9159, 9170, 9180, 9190, 9200, /* 9100-9900 */
118: };
119:
120: char *malloc();
121: char *mktemp();
122: char *sbrk();
123: char *getword();
124: EXCEPT *lookup();
125: FILE *pinit();
126:
127: main(argc, argv)
128: char *argv[];
129: {
130: register char *ap;
131: register int i;
132: FILE *fp;
133:
134: /*
135: * Because we have our own allocator.
136: */
137: setbuf(stdout, obuf);
138: setbuf(stderr, NULL);
139: while (argc>1 && *argv[1]=='-') {
140: for (ap = &argv[1][1]; *ap != '\0'; ap++)
141: switch (*ap) {
142: case 'n':
143: nflag = 1;
144: break;
145:
146: case 'r':
147: rflag = 1;
148: break;
149:
150: case 's':
151: sflag = 1;
152: break;
153:
154: default:
155: usage();
156: }
157: argc--;
158: argv++;
159: }
160: if (sflag == 0) {
161: readdicts();
162: pfp = pinit();
163: }
164: if (argc > 1)
165: for (i=1; i<argc; i++) {
166: if ((fp = fopen(argv[i], "r")) == NULL)
167: tyerr("Cannot open input `%s'", argv[i]);
168: typo(fp);
169: }
170: else
171: typo(stdin);
172: if (!sflag) {
173: pterm(pfp);
174: precompute();
175: /*
176: * Re-read the sorted word
177: * list.
178: */
179: reread();
180: } else
181: outstats();
182: rmexit(0);
183: }
184:
185: /*
186: * Called for each input word
187: * to set up file pointer, read
188: * of individual words and enter
189: * frequency statistics into the
190: * tables.
191: */
192: typo(fp)
193: register FILE *fp;
194: {
195: ifp = fp;
196: setbuf(fp, ibuf);
197: while (getword() != NULL) {
198: stats(word);
199: if (!sflag && lookup(word)==NULL)
200: fprintf(pfp, "%s\n", word);
201: }
202: if (fp != stdin)
203: fclose(fp);
204: }
205:
206: /*
207: * Initialise the trigram and digram
208: * tables if the user requests help.
209: * Read in the exception dictionary.
210: * Put each word in a hash table.
211: */
212: readdicts()
213: {
214: FILE *fp;
215:
216: if (nflag)
217: return;
218: if ((fp = fopen(dictfile, "r")) != NULL) {
219: register int l;
220: register char *cp;
221: register EXCEPT *ep;
222:
223: setbuf(fp, xbuf);
224: while (fgets(cp = word, sizeof word, fp) != NULL) {
225: l = strlen(cp);
226: cp[l-1] = '\0';
227: if ((ep = (EXCEPT *)malloc(l + sizeof *ep)) == NULL)
228: tyerr("Out of memory for dictionary");
229: hashval = 0;
230: while (*cp)
231: hashval += *cp++;
232: l = hashval%NHASH;
233: ep->e_next = except[l];
234: except[l] = ep;
235: ep->e_hval = hashval;
236: strcpy(ep->e_word, word);
237: }
238: fclose(fp);
239: }
240: if ((fp = fopen(digfile, "r")) != NULL) {
241: register int t1, t2;
242:
243: setbuf(fp, xbuf);
244: while (fgets(word, NWORD, fp) != NULL) {
245: t1 = cton(word[0]);
246: t2 = cton(word[1]);
247: digrams[t1][t2] = atoi(&word[2]);
248: }
249: fclose(fp);
250: }
251: if ((fp = fopen(trifile, "r")) != NULL) {
252: register TRIGRAMS *tp;
253: register int t1, t2, t3;
254:
255: setbuf(fp, xbuf);
256: while (fgets(word, NWORD, fp) != NULL) {
257: t1 = cton(word[0]);
258: t2 = cton(word[1]);
259: t3 = cton(word[2]);
260: for (tp = trigrams[t1][t2]; tp!=NULL; tp = tp->t_next) {
261: if (tp->t_char == t3) {
262: tp->t_freq += atoi(&word[3]);
263: break;
264: }
265: }
266: if (tp == NULL) {
267: if ((tp = (TRIGRAMS *)malloc(sizeof *tp))==NULL)
268: tyerr(notrimem);
269: tp->t_freq = atoi(&word[3]);
270: tp->t_char = t3;
271: tp->t_next = trigrams[t1][t2];
272: trigrams[t1][t2] = tp;
273: }
274: }
275: fclose(fp);
276: }
277: }
278:
279: /*
280: * Get a character from a digram or
281: * trigram file and check it.
282: * Convert character to index number
283: * for tables.
284: */
285: cton(c)
286: register unsigned c;
287: {
288: if ((c -= '`') >= 27)
289: tyerr("Invalid digram/trigram file format");
290: return (c);
291: }
292:
293: /*
294: * Get the next word from the input.
295: */
296: char *
297: getword()
298: {
299: register char *cp;
300: register int c;
301:
302: while (!isalpha(c = tgetc()))
303: if (c == EOF)
304: return (NULL);
305: cp = word;
306: for (;;) {
307: if (isupper(c))
308: c = tolower(c);
309: *cp++ = c;
310: if (!isalpha(c = tgetc()))
311: break;
312: }
313: *cp = '\0';
314: return (word);
315: }
316:
317: /*
318: * Get a character. This also checks
319: * for roff stuff (if -r is not specified)
320: * and hyphenated words.
321: */
322: tgetc()
323: {
324: static int nlflag = 1;
325: register int c;
326:
327: while (nlflag) {
328: if ((c = getc(ifp)) == '.')
329: while ((c = getc(ifp))!='\n' && c!=EOF)
330: ;
331: else {
332: ungetc(c, ifp);
333: nlflag = 0;
334: break;
335: }
336: }
337: again:
338: if ((c = getc(ifp)) == '\n')
339: nlflag = 1;
340: else if (c == '-') {
341: if ((c = getc(ifp)) == '\n')
342: goto again;
343: } else if (c == '\'')
344: goto again;
345: else if (c == '\\') { /* Fonts and sizes */
346: switch (c = getc(ifp)) {
347: case 'f':
348: c = getc(ifp);
349: goto again;
350:
351: case 's':
352: while ((c = getc(ifp))>='0' && c<='9')
353: ;
354: }
355: }
356: return (c);
357: }
358:
359: /*
360: * Lookup an input word
361: * in the exception list.
362: */
363: EXCEPT *
364: lookup(wp)
365: char *wp;
366: {
367: register char *cp = wp;
368: register EXCEPT *ep;
369:
370: hashval = 0;
371: while (*cp != '\0')
372: hashval += *cp++;
373: for (ep = except[hashval%NHASH]; ep != NULL; ep = ep->e_next)
374: if (ep->e_hval==hashval && strcmp(ep->e_word, word)==0)
375: return (ep);
376: return (NULL);
377: }
378:
379: /*
380: * Compute the trigram and digram statistics
381: * on this word.
382: */
383: stats(wp)
384: register char *wp;
385: {
386: register int t1, t2, t3;
387: register TRIGRAMS *tp;
388:
389: t1 = 0;
390: while (*wp != '\0') {
391: t2 = *wp++;
392: t3 = *wp;
393: if (t2>='a' && t2<='z')
394: t2 -= 'a'-1;
395: if (t3>='a' && t3<='z')
396: t3 -= 'a'-1;
397: digrams[t1][t2]++;
398: for (tp = trigrams[t1][t2]; tp != NULL; tp = tp->t_next)
399: if (tp->t_char == t3) {
400: tp->t_freq++;
401: break;
402: }
403: if (tp == NULL) {
404: if ((tp = (TRIGRAMS *)malloc(sizeof *tp)) != NULL) {
405: tp->t_freq = 1;
406: tp->t_char = t3;
407: tp->t_next = trigrams[t1][t2];
408: trigrams[t1][t2] = tp;
409: }
410: }
411: t1 = t2;
412: }
413: digrams[t2][t3]++;
414: }
415:
416: /*
417: * Output the digram and trigram statistics
418: * onto file `digram' and `trigram'.
419: */
420: outstats()
421: {
422: char x1buf[BUFSIZ];
423: register TRIGRAMS *tp;
424: register int t1, t2;
425: register int freq;
426: FILE *dfp, *tfp;
427:
428: if ((dfp = fopen("digrams", "w")) == NULL)
429: tyerr("Cannot create digrams file");
430: if ((tfp = fopen("trigrams", "w")) == NULL)
431: tyerr("Cannot create trigrams file");
432: setbuf(dfp, xbuf);
433: setbuf(tfp, x1buf);
434: for (t1 = 0; t1 < 27; t1++)
435: for (t2 = 0; t2 < 27; t2++) {
436: if ((freq = digrams[t1][t2]) != 0)
437: fprintf(dfp, "%c%c%d\n", oc(t1), oc(t2), freq);
438: for (tp = trigrams[t1][t2]; tp!=NULL; tp = tp->t_next)
439: fprintf(tfp, "%c%c%c%d\n", oc(t1), oc(t2),
440: oc(tp->t_char), tp->t_freq);
441: }
442: fclose(dfp);
443: fclose(tfp);
444: }
445:
446: /*
447: * Compute index of peculiarity on a
448: * word. This is the RMS mean of the
449: * index for each trigram (say XYZ).
450: * This index is 1/2(log (xy) + log(yz)) - log(xyz)
451: * If there is only one trigram, the RMS
452: * mean is taken to be the one sample.
453: */
454: pindex(wp)
455: register char *wp;
456: {
457: register int t1, t2, t3;
458: register TRIGRAMS *tp;
459: register long sumsq;
460: register long sum;
461: register unsigned count;
462: register int ntri;
463:
464: ntri = 0;
465: t1 = 0;
466: sum = sumsq = 0;
467: while (*wp != '\0') {
468: t2 = *wp++;
469: t3 = *wp;
470: if (t2>='a' && t2<='z')
471: t2 -= 'a'-1;
472: if (t3>='a' && t3<='z')
473: t3 -= 'a'-1;
474: for (tp = trigrams[t1][t2]; tp != NULL; tp = tp->t_next)
475: if (tp->t_char == t3) {
476: count = tp->t_freq;
477: break;
478: }
479: if (tp == NULL)
480: tyerr("Missing trigram");
481: sum += count;
482: sumsq += (long)count*count;
483: ntri++;
484: t1 = t2;
485: }
486: if (ntri > 1) {
487: sum *= sum;
488: sum /= ntri;
489: sum = sumsq-sum;
490: }
491: count = (qsqrt((int)sum) + 5) / 10;
492: return (count);
493: }
494:
495: /*
496: * Pre-compute the trigram statistics.
497: * This essentially transforms the
498: * trigram tables from a table
499: * of frequencies to a table of
500: * indices of peculiarity for that
501: * particular trigram.
502: */
503: precompute()
504: {
505: register int i, j;
506: register TRIGRAMS *tp;
507: register int logij;
508:
509: for (i=0; i<27; i++)
510: for (j=0; j<27; j++) {
511: logij = qlog(digrams[i][j]-1);
512: for (tp = trigrams[i][j]; tp != NULL; tp = tp->t_next) {
513: tp->t_freq = ((logij
514: + qlog(digrams[j][tp->t_char]-1))/2
515: - qlog(tp->t_freq-1)) / 100;
516: }
517: }
518: }
519:
520: /*
521: * Evaluate a natural logarithm
522: * quickly by table lookup.
523: * The resulting logarithm
524: * is multiplied by 1000.
525: * By definition, the log of 0 (or less) is
526: * -10.
527: */
528: qlog(n)
529: register unsigned n;
530: {
531: if (n <= 0)
532: return (-10*1000);
533: if (n < 100)
534: return (log1[n-1]);
535: if (n < 1000)
536: return (log2[(n-95)/10]);
537: if (n < 10000)
538: return (log3[(n-950)/100]);
539: return (10*1000);
540: }
541:
542: /*
543: * Quick version of sqare root.
544: * Uses Newton's method.
545: */
546: qsqrt(x)
547: register unsigned x;
548: {
549: register int maxterm = 50;
550: register unsigned old, new;
551:
552: if ((old = x/10) == 0) {
553: old = 1;
554: if (x == 0)
555: return (0);
556: }
557: do {
558: new = (x/old+old)/2;
559: if (old == new)
560: break;
561: old = new;
562: } while (--maxterm);
563: return (new);
564: }
565:
566: /*
567: * Build pipe to a sort routine.
568: */
569: FILE *
570: pinit()
571: {
572: int pfd[2];
573: register int pid;
574: register int fd;
575: FILE *fp;
576:
577: tmpfile = mktemp(tmp);
578: if (pipe(pfd) < 0)
579: tyerr("Cannot pipe to sort");
580: if ((pid = fork()) < 0)
581: tyerr("Cannot fork for sort");
582: if (pid) {
583: close(pfd[0]);
584: if ((fp = fdopen(pfd[1], "w")) != NULL)
585: setbuf(fp, xbuf);
586: return (fp);
587: } else {
588: dup2(pfd[0], 0);
589: close(pfd[0]);
590: close(pfd[1]);
591: if ((fd = creat(tmp, 0666)) < 0)
592: tyerr("Cannot create temporary file");
593: dup2(fd, 1);
594: close(fd);
595: execlp(sort, sort, "-u", NULL);
596: tyerr(nosort);
597: }
598: }
599:
600: /*
601: * Close off the pipe and wait
602: * for the sort command to
603: * complete.
604: */
605: pterm(fp)
606: FILE *fp;
607: {
608: int status;
609:
610: fclose(fp);
611: while (wait(&status) >= 0)
612: if (status)
613: tyerr("Sort failed");
614: }
615:
616: /*
617: * Re-read the sorted word-list
618: * from the temp file. Because
619: * sort -u is not implemented,
620: * we have to guarantee uniqueness
621: * ourselves.
622: * The output is piped into a
623: * `sort -nr' command.
624: */
625: reread()
626: {
627: FILE *fp;
628: register int ind;
629: int pid, status;
630: int pfd[2];
631:
632: if ((fp = fopen(tmpfile, "r")) == NULL)
633: tyerr("Cannot re-open temp-file");
634: setbuf(fp, xbuf);
635: if (pipe(pfd) < 0)
636: tyerr("Cannot create pipe to sort output");
637: if ((pid = fork()) < 0)
638: fprintf(stderr, "Cannot fork -- output is not sorted\n");
639: else if (pid) {
640: dup2(pfd[1], 1);
641: close(pfd[0]);
642: close(pfd[1]);
643: } else {
644: dup2(pfd[0], 0);
645: close(pfd[0]);
646: close(pfd[1]);
647: execlp(sort, sort, "-nr", NULL);
648: tyerr(nosort);
649: }
650: while (fgets(word, NWORD, fp) != NULL) {
651: word[strlen(word)-1] = '\0';
652: ind = pindex(word);
653: printf("%2d %s\n", ind, word);
654: }
655: fclose(fp);
656: fclose(stdout);
657: while (wait(&status) >= 0)
658: ;
659: }
660:
661: /*
662: * A simple malloc for which there
663: * is no free. It just uses sbrk and
664: * returns a value.
665: * `size' is in bytes.
666: */
667: char *
668: malloc(size)
669: register unsigned size;
670: {
671: static int *rp, *ep;
672: register int *cp;
673:
674: if ((size = (size+sizeof(int)-1)/sizeof(int)) == 0)
675: return (NULL);
676: if (rp==NULL || rp+size>=ep) {
677: if ((ep = (int *)sbrk(NSBRK)) == BADSBRK)
678: return (NULL);
679: if (rp == NULL)
680: rp = ep;
681: ep += NSBRK/sizeof(int);
682: }
683: cp = rp;
684: rp += size;
685: return (cp);
686: }
687:
688: /*
689: * Dummy free.
690: */
691: free(p)
692: char *p;
693: {
694: tyerr("Free not allowed");
695: }
696:
697: /*
698: * Dummy realloc
699: */
700: char *
701: realloc(p)
702: char *p;
703: {
704: tyerr("realloc not allowed");
705: }
706:
707: /*
708: * Exit, removing the temp-file
709: * if it is found.
710: */
711: rmexit(s)
712: {
713: if (tmpfile != NULL)
714: unlink(tmpfile);
715: exit(s);
716: }
717:
718: /*
719: * Error and usage messages
720: */
721: /* VARARGS */
722: tyerr(x)
723: {
724: fprintf(stderr, "typo: %r", &x);
725: putc('\n', stderr);
726: rmexit(1);
727: }
728:
729: usage()
730: {
731: fprintf(stderr, "Usage: typo [-nrs] file\n");
732: rmexit(1);
733: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.